22#include <simgear/math/SGGeod.hxx>
23#include <simgear/math/SGBox.hxx>
24#include <simgear/math/SGRect.hxx>
25#include <simgear/structure/SGSharedPtr.hxx>
26#include <simgear/debug/logstream.hxx>
27#include <simgear/io/iostreams/sgstream.hxx>
28#include <simgear/structure/exception.hxx>
29#include <simgear/timing/sg_time.hxx>
49template <
class T,
typename GetBox,
typename Equal>
52 const std::size_t MAX_DEPTH = 8;
53 const std::size_t SPLIT_THRESHOLD = 10;
54 const std::size_t depth;
56 std::array<std::unique_ptr<quadtree::Node<T,GetBox,Equal>>, 4> children;
57 std::vector<SGSharedPtr<T>> data;
61 Node(std::size_t depth,
int fQuadtrant): depth(depth), data(), quadrant(fQuadtrant) {
65 if (children[0] ==
nullptr) {
69 for (
auto& n : children) {
70 childrenSize += n->size();
76 bool isLeaf() {
return children[0] ==
nullptr;};
78 void resize(
const SGRectd& bounds ) {
80 SG_LOG(SG_ATC, SG_ALERT,
"Resizing Quadtree with data not supported");
82 this->bounds = bounds;
83 SG_LOG(SG_ATC, SG_BULK ,
"Resizing Quadtree " << quadrant <<
" to " << bounds.x() <<
"\t" << bounds.y() <<
"\t Width : " << bounds.width() <<
"\t Height : " << bounds.height());
86 bool add(
const SGRectd& pos, SGSharedPtr<T> value,
const Equal& equalFkt,
const GetBox& getBoxFunction)
88 if (equalFkt==
nullptr || getBoxFunction==
nullptr) {
89 SG_LOG(SG_ATC, SG_BULK ,
"equalFkt " << equalFkt <<
" getBoxFunction " << getBoxFunction);
93 if (!bounds.contains(pos.x(), pos.y())) {
94 SG_LOG(SG_ATC, SG_ALERT ,
"Not in node Quadrant " << quadrant <<
" to bounds " << bounds.x() <<
"\t" << bounds.y() <<
"\t" << (bounds.x()+bounds.width()) <<
"\t" << (bounds.y()+bounds.height()) <<
"\t Pos : \t" << pos.x() <<
"\t" << pos.y() );
97 if (depth >= MAX_DEPTH || data.size() < SPLIT_THRESHOLD) {
99 if (depth >= MAX_DEPTH) {
100 SG_LOG(SG_ATC, SG_BULK ,
"Max Depth reached");
103 auto it = std::find_if(std::begin(data), std::end(data),
104 [equalFkt, value](
auto rhs){
return equalFkt(value, rhs); });
105 if (it == std::end(data)) {
106 data.push_back(value);
107 SG_LOG(SG_ATC, SG_DEBUG ,
"Added " << value <<
" to level " << depth <<
" Size : " << data.size());
110 int sizeBefore = data.size();
112 int sizeBefore2 = data.size();
113 data.push_back(value);
114 int sizeAfter = data.size();
115 SG_LOG(SG_ATC, SG_DEBUG ,
"Not readded " << value <<
" to level " << depth <<
" " << sizeBefore<<
" " << sizeBefore2 <<
" " << sizeAfter);
121 auto i =
split(pos, equalFkt, getBoxFunction);
123 return children[
static_cast<std::size_t
>(
i)].get()->add(pos, value, equalFkt, getBoxFunction);
134 return children[
static_cast<std::size_t
>(
i)].get()->add(pos, value, equalFkt, getBoxFunction);
142 bool move(
const SGRectd& newPos,
const SGRectd& oldPos, SGSharedPtr<T> value,
const Equal& equalFkt,
const GetBox& getBoxFunction)
144 SGRectd realPos = getBoxFunction(value);
145 double dist = SGGeodesy::distanceM(SGGeod::fromDegM(oldPos.y(), oldPos.x(), 0), SGGeod::fromDegM(realPos.y(), realPos.x(), 0) );
146 SG_LOG(SG_ATC, SG_BULK,
147 "Moving " << oldPos.x() <<
":" << oldPos.y() <<
" to " << newPos.x() <<
":" << newPos.y() << (
isLeaf()?
" leaf ":
" ") << dist );
153 auto it = std::find_if(std::begin(data), std::end(data),
154 [equalFkt, value](
auto rhs){
return equalFkt(value, rhs); });
155 if (it == std::end(data)) {
156 SG_LOG(SG_ATC, SG_DEBUG ,
"Trying to move non existant data " << value <<
" " << data.size() );
168 if (oldQuadrant != newQuadrant) {
169 SG_LOG(SG_ATC, SG_BULK,
170 "Moving from quadrant " << oldQuadrant <<
" to quadrant " << newQuadrant <<
" Level " << depth );
171 bool removed = children[
static_cast<std::size_t
>(oldQuadrant)].get()->remove(oldPos, value, equalFkt);
172 bool added = children[
static_cast<std::size_t
>(newQuadrant)].get()->add(newPos, value, equalFkt, getBoxFunction);
173 if (!removed || !added)
175 SG_LOG(SG_ATC, SG_ALERT,
176 "Error moving " << (removed?
" true ":
" false ") << (added?
" true ":
" false "));
178 return removed && added;
181 return children[
static_cast<std::size_t
>(oldQuadrant)].get()->move(newPos, oldPos, value, equalFkt, getBoxFunction);
184 SG_LOG(SG_ATC, SG_WARN,
185 "Failed Moving from quadrant " << oldQuadrant <<
" to quadrant " << newQuadrant <<
" Level " << depth );
193 auto it = std::find_if(std::begin(data), std::end(data),
194 [equalFkt, value](
auto rhs){
return equalFkt(value, rhs); });
195 if (it == std::end(data)) {
196 SG_LOG(SG_ATC, SG_DEV_ALERT ,
"Trying to remove non existant data " << data.size());
204 bool remove(
const SGRectd& pos, SGSharedPtr<T> value,
const Equal& equalFunction) {
210 SG_LOG(SG_ATC, SG_BULK ,
"Remove from quadrant " <<
i <<
" Depth " << depth);
212 if (children[
static_cast<std::size_t
>(
i)].get()->
remove(
computeBox(pos,
i), value, equalFunction)) {
215 bool found = children[
static_cast<std::size_t
>(
i)].get()->findFullScan(value, equalFunction,
"Error /");
216 SG_LOG(SG_ATC, SG_DEBUG ,
"Trying to find misplaced data " << found);
220 SG_LOG(SG_ATC, SG_ALERT ,
"Trying to remove from UNKNOWN non leaf " );
231 bool findFullScan(SGSharedPtr<T> value,
const Equal& equalFkt,
const std::string& path) {
233 auto it = std::find_if(std::begin(data), std::end(data),
234 [equalFkt, value](
auto rhs){
return equalFkt(value, rhs); });
235 if (it == std::end(data)) {
239 SG_LOG(SG_ATC, SG_DEBUG ,
"Found in path node " << path <<
" " );
243 for (
size_t i = 0;
i < 4;
i++) {
244 std::string subpath = path + std::to_string(
i) +
"/";
245 bool found = children[
static_cast<std::size_t
>(
i)].get()->findFullScan(value, equalFkt, subpath);
259 bool removeFullScan(SGSharedPtr<T> value,
const Equal& equalFkt,
const std::string& path) {
261 auto it = std::find_if(std::begin(data), std::end(data),
262 [equalFkt, value](
auto rhs){
return equalFkt(value, rhs); });
263 if (it == std::end(data)) {
267 SG_LOG(SG_ATC, SG_DEBUG ,
"Found in path node " << path <<
" removing " );
272 for (
size_t i = 0;
i < 4;
i++) {
273 std::string subpath = path + std::to_string(
i) +
"/";
274 bool found = children[
static_cast<std::size_t
>(
i)].get()->removeFullScan(value, equalFkt, subpath);
284 bool printPath(
const SGRectd& pos, SGSharedPtr<T> value,
const Equal& equalFkt,
const std::string& path) {
286 SG_LOG(SG_ATC, SG_BULK , path );
287 auto it = std::find_if(std::begin(data), std::end(data),
288 [equalFkt, value](
auto rhs){
return equalFkt(value, rhs); });
289 if (it == std::end(data)) {
290 SG_LOG(SG_ATC, SG_DEBUG ,
"Not found when printing path " << value);
298 std::string subpath = path + std::to_string(
i) +
"/";
299 return children[
static_cast<std::size_t
>(
i)].get()->printPath(
computeBox(pos,
i), value, equalFkt, subpath);
301 SG_LOG(SG_ATC, SG_ALERT ,
"Unkown quadrant " );
307 bool printPath(
const SGRectd& pos,
const std::string& path) {
309 SG_LOG(SG_ATC, SG_DEBUG , path );
314 std::string subpath = path + std::to_string(
i) +
"/";
315 return children[
static_cast<std::size_t
>(
i)].get()->printPath(
computeBox(pos,
i), subpath);
317 SG_LOG(SG_ATC, SG_ALERT ,
"Unkown quadrant " );
324 auto nbValues =
size();
325 for (
const auto& child : children)
327 SG_LOG(SG_ATC, SG_DEBUG ,
"Leaf " << child.get()->isLeaf());
332 nbValues += child.get()->size();
334 SG_LOG(SG_ATC, SG_DEBUG ,
"Trying to merge Quadtree " << nbValues);
339 int split(
const SGRectd& pos,
const Equal& equalFkt,
const GetBox& getBoxFunction) {
341 SG_LOG(SG_ATC, SG_BULK,
"Splitting Quadtree Size : " << data.size() <<
" Depth : " << depth);
343 for (
size_t i = 0;
i < 4;
i++) {
344 children[
i] = std::make_unique<Node>(depth+1,
i);
348 auto newValues = std::vector<SGSharedPtr<T>>();
349 for (
auto value : data)
353 children[
static_cast<std::size_t
>(
i)].get()->add(getBoxFunction(value), value, equalFkt, getBoxFunction);
356 newValues.push_back(value);
359 data = std::move(newValues);
365 auto origin = box.getMin();
366 auto childSize = box.size();
371 return SGRectd(SGVec2d(origin.x(), origin.y()), SGVec2d(origin.x() + childSize.x(), origin.y() + childSize.y()));
373 return SGRectd(SGVec2d(origin.x(), origin.y() + childSize.y()), SGVec2d(origin.x() + childSize.x(), origin.y() + 2*childSize.y()));
375 return SGRectd(SGVec2d(origin.x() + childSize.x(), origin.y()), SGVec2d(origin.x() + 2*childSize.x(), origin.y() + childSize.y()));
377 return SGRectd(SGVec2d(origin.x() + childSize.x(), origin.y() + childSize.y()), SGVec2d(origin.x() + 2*childSize.x(), origin.y() + 2*childSize.y()));
379 assert(
false &&
"Invalid quadrant index");
386 auto origin = box.getMin();
387 auto childSize = box.size();
392 return SGRectd(SGVec2d(origin.x() + childSize.x(), origin.y() + childSize.y()), SGVec2d(origin.x() + childSize.x(), origin.y() + childSize.y()));
394 return SGRectd(SGVec2d(origin.x() + childSize.x(), origin.y() + 3*childSize.y()), SGVec2d(origin.x() + childSize.x(), origin.y() + 3*childSize.y()));
396 return SGRectd(SGVec2d(origin.x() + 3*childSize.x(), origin.y() + childSize.y()), SGVec2d(origin.x() + 3*childSize.x(), origin.y() + childSize.y()));
398 return SGRectd(SGVec2d(origin.x() + 3*childSize.x(), origin.y() + 3*childSize.y()), SGVec2d(origin.x() + 3*childSize.x(), origin.y() + 3*childSize.y()));
400 assert(
false &&
"Invalid quadrant index");
405 void query(
const SGRectd& queryBox,
const GetBox& getBoxFunction, std::vector<SGSharedPtr<T>>& values)
407 SG_LOG(SG_ATC, SG_BULK,
"Query Quadtree " << queryBox.getMin().x() <<
"\t" << queryBox.getMin().y() <<
"\t"
408 << queryBox.getMax().x() <<
"\t" << queryBox.getMax().y()
409 <<
" depth " << depth <<
" Leaf : " << (
isLeaf()?
"true":
"false"));
410 for (
auto value : data)
412 auto pos = getBoxFunction(value);
413 SG_LOG(SG_ATC, SG_BULK,
"Query Quadtree " << pos.x() <<
"\t" << pos.y());
414 if (queryBox.contains(pos.x(), pos.y())) {
415 values.push_back(value);
420 if (children.size()!=4) {
421 SG_LOG(SG_ATC, SG_ALERT,
"Wrong Box Size" );
423 for (
auto i = std::size_t(0);
i < children.size();
i++)
425 auto childBox =
computeBox(bounds,
static_cast<int>(
i));
426 SG_LOG(SG_ATC, SG_BULK,
"Query Quadtree center " <<
i <<
"\t" << childBox.x() <<
"\t" << childBox.y() <<
"\t" << childBox.width() <<
"\t" << childBox.height() );
428 children[
i].get()->query(queryBox, getBoxFunction, values);
430 SG_LOG(SG_ATC, SG_BULK,
"Query Quadtree center " <<
i <<
" not found " );
431 SG_LOG(SG_ATC, SG_BULK,
"QueryBox " << queryBox.getMin().x() <<
"," << queryBox.getMin().y() <<
"\t" << queryBox.getMax().x() <<
"," << queryBox.getMax().y() );
432 SG_LOG(SG_ATC, SG_BULK,
"ChildBox " << childBox.getMin().x() <<
"," << childBox.getMin().y() <<
"\t" << childBox.getMax().x() <<
"," << childBox.getMax().y() );
433 SG_LOG(SG_ATC, SG_BULK,
"MinMax " << ((childBox.getMax().y() > queryBox.getMin().y())?
"true":
"false"));
442 auto center = nodeBox.getMin();
444 if (valueBox.x() < nodeBox.x() + nodeBox.width()/2)
446 if (valueBox.y() < nodeBox.y() + nodeBox.height()/2)
448 else if (valueBox.y() + valueBox.height() >= nodeBox.y() + nodeBox.height()/2)
455 else if (valueBox.x() >= nodeBox.x() + nodeBox.width()/2)
457 if (valueBox.y() < nodeBox.y() + nodeBox.height()/2)
459 else if (valueBox.y() + valueBox.height() >= nodeBox.y() + nodeBox.height()/2)
475 return firstBox.getMax().x() > secondBox.getMin().x() &&
476 firstBox.getMin().x() < secondBox.getMax().x() &&
477 firstBox.getMax().y() > secondBox.getMin().y() &&
478 firstBox.getMin().y() < secondBox.getMax().y();
481 void dumpGeoJson(
const std::unique_ptr<sg_ofstream>& o,
const SGRectd& box) {
482 (*o) <<
",{ \"type\": \"Feature\"," << std::endl;
483 (*o) <<
"\"properties\": {}," << std::endl;
484 (*o) <<
" \"geometry\": { \"type\": \"Polygon\"," << std::endl;
485 (*o) <<
"\"coordinates\": [ [" << std::endl;
486 (*o) <<
"[" << box.getMin().y() <<
"," << box.getMin().x() <<
"]," << std::endl;
487 (*o) <<
"[" << box.getMax().y() <<
"," << box.getMin().x() <<
"]," << std::endl;
488 (*o) <<
"[" << box.getMax().y() <<
"," << box.getMax().x() <<
"]," << std::endl;
489 (*o) <<
"[" << box.getMin().y() <<
"," << box.getMax().x() <<
"]," << std::endl;
490 (*o) <<
"[" << box.getMin().y() <<
"," << box.getMin().x() <<
"]]]" << std::endl;
491 (*o) <<
"}}" << std::endl;
494 void dumpGeoJson(
const std::unique_ptr<sg_ofstream>& o,
const GetBox& getBoxFunction) {
495 (*o) <<
"{ \"type\": \"Feature\"," << std::endl;
496 (*o) <<
"\"properties\": {}," << std::endl;
497 (*o) <<
" \"geometry\": { \"type\": \"Polygon\"," << std::endl;
498 (*o) <<
"\"coordinates\": [ [" << std::endl;
499 (*o) <<
"[" << bounds.getMin().y() <<
"," << bounds.getMin().x() <<
"]," << std::endl;
500 (*o) <<
"[" << bounds.getMax().y() <<
"," << bounds.getMin().x() <<
"]," << std::endl;
501 (*o) <<
"[" << bounds.getMax().y() <<
"," << bounds.getMax().x() <<
"]," << std::endl;
502 (*o) <<
"[" << bounds.getMin().y() <<
"," << bounds.getMax().x() <<
"]," << std::endl;
503 (*o) <<
"[" << bounds.getMin().y() <<
"," << bounds.getMin().x() <<
"]]]" << std::endl;
504 (*o) <<
"}}" << std::endl;
506 for (
const auto& node : data)
509 (*o) <<
"{ \"type\": \"Feature\","<< std::endl;
510 (*o) <<
"\"properties\": { \"id\": \"" << node <<
"\"}," << std::endl;
511 (*o) <<
" \"geometry\": { \"type\": \"Point\"," << std::endl;
512 (*o) <<
"\"coordinates\": " << std::endl;
513 auto coords = getBoxFunction(node);
514 (*o) <<
"[" << coords.getMin().y() <<
"," << coords.getMin().x() <<
"]" << std::endl;
515 (*o) <<
"}}" << std::endl;
519 for (
const auto& child : children)
521 (*o) <<
"," << std::endl;
522 child.get()->dumpGeoJson(o, getBoxFunction);
528template <
class T,
typename GetBox,
typename Equal>
531 std::unique_ptr<quadtree::Node<T, GetBox, Equal>> rootNode;
533 GetBox getBoxFunction;
535 std::unique_ptr<sg_ofstream> geoJsonFile;
539 ): rootNode(std::make_unique<
quadtree::
Node<T,GetBox, Equal>>(0,
UNKNOWN)), getBoxFunction(getBox), equalFunction(equal),
540 geoJsonFile{std::make_unique<sg_ofstream>()}
546 snprintf(fname,
sizeof(fname),
"%ld_%f.json", t,
globals->get_sim_time_sec());
547 SG_LOG(SG_ATC, SG_ALERT ,
"Exported " << fname );
549 SGPath
p =
globals->get_download_dir() / fname;
550 geoJsonFile->open(
p);
551 (*geoJsonFile) <<
"{ \"type\": \"FeatureCollection\", \"features\": [";
552 rootNode.get()->dumpGeoJson(geoJsonFile, getBoxFunction);
553 rootNode.get()->dumpGeoJson(geoJsonFile, bounds);
554 (*geoJsonFile) <<
"]}";
555 geoJsonFile->close();
559 rootNode.get()->resize(bounds);
562 bool add(SGSharedPtr<T> value)
564 if (rootNode.get()!=
nullptr) {
565 SGRectd pos = getBoxFunction(value);
566 SGRectd bounds = rootNode.get()->getBounds();
567 if (!bounds.contains(pos.x(), pos.y())) {
568 SG_LOG(SG_ATC, SG_DEV_ALERT ,
"Not in index Bounds : " << bounds.x() <<
"x" << bounds.y() <<
"\t" << (bounds.x()+bounds.width()) <<
"x" << (bounds.y()+bounds.height()) << value <<
" Pos : " << pos );
571 bool ret = rootNode.get()->add(getBoxFunction(value), value, equalFunction, getBoxFunction);
575 SG_LOG(SG_ATC, SG_ALERT ,
"Not printed " << value );
578 SG_LOG(SG_ATC, SG_ALERT ,
"Not added " << value );
586 bool move(
const SGRectd& newPos, SGSharedPtr<T> value)
594 bool moved = rootNode.get()->move(newPos, getBoxFunction(value), value, equalFunction, getBoxFunction);
596 bool found = rootNode.get()->printPath(getBoxFunction(value), value, equalFunction,
"Start/");
599 rootNode.get()->printPath(getBoxFunction(value),
"Error/");
600 rootNode.get()->findFullScan(value, equalFunction,
"Error/");
602 bool removed = rootNode.get()->removeFullScan(value, equalFunction,
"Error/");
604 SG_LOG(SG_ATC, SG_ALERT ,
"Not removed " << value);
605 rootNode.get()->findFullScan(value, equalFunction,
"Error/");
607 return rootNode.get()->add(getBoxFunction(value), value, equalFunction, getBoxFunction);
618 if (value==
nullptr) {
621 return rootNode.get()->remove(getBoxFunction(value), value, equalFunction);
626 return rootNode.get()->printPath(getBoxFunction(value), value, equalFunction,
"/");
629 void query(SGSharedPtr<T> value, std::vector<SGSharedPtr<T>>& values)
631 return rootNode.get()->query(getBoxFunction(value), getBoxFunction, values);
634 void query(
const SGRectd& queryBox, std::vector<SGSharedPtr<T>>& values)
636 return rootNode.get()->query(queryBox, getBoxFunction, values);
639 size_t size()
const {
return rootNode.get()->size();}
QuadTree(const GetBox &getBox, const Equal &equal)
bool add(const SGRectd &pos, SGSharedPtr< T > value, const Equal &equalFkt, const GetBox &getBoxFunction)
bool printPath(const SGRectd &pos, const std::string &path)
Quadrant getQuadrant(const SGRectd &nodeBox, const SGRectd &valueBox) const
bool remove(const SGRectd &pos, SGSharedPtr< T > value, const Equal &equalFunction)
void dumpGeoJson(const std::unique_ptr< sg_ofstream > &o, const GetBox &getBoxFunction)
SGRectd computeBoxCenter(const SGRectd &box, int i) const
int split(const SGRectd &pos, const Equal &equalFkt, const GetBox &getBoxFunction)
bool removeValue(SGSharedPtr< T > value, const Equal &equalFkt)
Node(std::size_t depth, int fQuadtrant)
void dumpGeoJson(const std::unique_ptr< sg_ofstream > &o, const SGRectd &box)
bool intersection(const SGRectd &firstBox, const SGRectd &secondBox)
bool findFullScan(SGSharedPtr< T > value, const Equal &equalFkt, const std::string &path)
For debugging.
bool printPath(const SGRectd &pos, SGSharedPtr< T > value, const Equal &equalFkt, const std::string &path)
bool removeFullScan(SGSharedPtr< T > value, const Equal &equalFkt, const std::string &path)
For debugging.
SGRectd computeBox(const SGRectd &box, int i) const
bool move(const SGRectd &newPos, const SGRectd &oldPos, SGSharedPtr< T > value, const Equal &equalFkt, const GetBox &getBoxFunction)
void resize(const SGRectd &bounds)
void query(const SGRectd &queryBox, const GetBox &getBoxFunction, std::vector< SGSharedPtr< T > > &values)
void exportJson(const SGRectd &bounds)
void query(const SGRectd &queryBox, std::vector< SGSharedPtr< T > > &values)
bool move(const SGRectd &newPos, SGSharedPtr< T > value)
bool printPath(SGSharedPtr< T > value)
void resize(const SGRectd &bounds)
bool remove(SGSharedPtr< T > value)
QuadTree(const GetBox &getBox, const Equal &equal)
void query(SGSharedPtr< T > value, std::vector< SGSharedPtr< T > > &values)
bool add(SGSharedPtr< T > value)