From 7cad40784374dc7b0a6b4a0fdf2e20cad5aad720 Mon Sep 17 00:00:00 2001 From: James Turner Date: Wed, 6 Mar 2013 17:22:34 +0000 Subject: [PATCH] Core data class for PolyLine handling. This is all unused yet, but defines the simple model for polyLines. --- src/Navaids/CMakeLists.txt | 2 + src/Navaids/PolyLine.cxx | 161 +++++++++++++++++++++++++++++++ src/Navaids/PolyLine.hxx | 115 ++++++++++++++++++++++ src/Navaids/PositionedOctree.cxx | 34 ++++++- src/Navaids/PositionedOctree.hxx | 23 ++++- 5 files changed, 333 insertions(+), 2 deletions(-) create mode 100644 src/Navaids/PolyLine.cxx create mode 100644 src/Navaids/PolyLine.hxx diff --git a/src/Navaids/CMakeLists.txt b/src/Navaids/CMakeLists.txt index d611e1c16..485a7ac62 100644 --- a/src/Navaids/CMakeLists.txt +++ b/src/Navaids/CMakeLists.txt @@ -17,6 +17,7 @@ set(SOURCES FlightPlan.cxx NavDataCache.cxx PositionedOctree.cxx + PolyLine.cxx ) set(HEADERS @@ -36,6 +37,7 @@ set(HEADERS FlightPlan.hxx NavDataCache.hxx PositionedOctree.hxx + PolyLine.hxx ) if (NOT SYSTEM_SQLITE) diff --git a/src/Navaids/PolyLine.cxx b/src/Navaids/PolyLine.cxx new file mode 100644 index 000000000..f7164ea33 --- /dev/null +++ b/src/Navaids/PolyLine.cxx @@ -0,0 +1,161 @@ +/** + * Polyline - store geographic line-segments */ + +// Written by James Turner, started 2013. +// +// Copyright (C) 2013 James Turner +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#include "PolyLine.hxx" + +#include +#include + +#include + +#include + +using namespace flightgear; + +PolyLine::PolyLine(Type aTy, const SGGeodVec& aPoints) : + m_type(aTy), + m_data(aPoints) +{ + assert(!aPoints.empty()); +} + +PolyLine::~PolyLine() +{ + +} + +unsigned int PolyLine::numPoints() const +{ + return m_data.size(); +} + +SGGeod PolyLine::point(unsigned int aIndex) const +{ + assert(aIndex <= m_data.size()); + return m_data[aIndex]; +} + +PolyLineList PolyLine::createChunked(Type aTy, const SGGeodVec& aRawPoints) +{ + PolyLineList result; + if (aRawPoints.size() < 2) { + return result; + } + + const double maxDistanceSquaredM = 40000 * 40000; // 40km to start with + + SGVec3d chunkStartCart = SGVec3d::fromGeod(aRawPoints.front()); + SGGeodVec chunk; + SGGeodVec::const_iterator it = aRawPoints.begin(); + + while (it != aRawPoints.end()) { + SGVec3d ptCart = SGVec3d::fromGeod(*it); + double d2 = distSqr(chunkStartCart, ptCart); + + // distance check, but also ensure we generate actual valid line segments. + if ((chunk.size() >= 2) && (d2 > maxDistanceSquaredM)) { + chunk.push_back(*it); // close the segment + result.push_back(new PolyLine(aTy, chunk)); + chunkStartCart = ptCart; + chunk.clear(); + } + + chunk.push_back(*it++); // add to open chunk + } + + // if we have a single trailing point, we already added it as the last + // point of the previous chunk, so we're ok. Otherwise, create the + // final chunk's polyline + if (chunk.size() > 1) { + result.push_back(new PolyLine(aTy, chunk)); + } + + return result; +} + +void PolyLine::addToSpatialIndex() const +{ + std::set seen; + + BOOST_FOREACH(const SGGeod& g, m_data) { + SGVec3d cart(SGVec3d::fromGeod(g)); + Octree::Leaf* lf = Octree::global_spatialOctree->findLeafForPos(cart); + if (seen.find(lf) != seen.end()) { + continue; // don't insert multiple times + } + + lf->addPolyLine(const_cast(this)); + } // of data points iteration +} + +class SingleTypeFilter : public PolyLine::TypeFilter +{ +public: + SingleTypeFilter(PolyLine::Type aTy) : + m_type(aTy) + { } + + virtual bool pass(PolyLine::Type aTy) const + { return (aTy == m_type); } +private: + PolyLine::Type m_type; +}; + +PolyLineList PolyLine::linesNearPos(const SGGeod& aPos, double aRangeNm, Type aTy) +{ + return linesNearPos(aPos, aRangeNm, SingleTypeFilter(aTy)); +} + + +PolyLineList PolyLine::linesNearPos(const SGGeod& aPos, double aRangeNm, const TypeFilter& aFilter) +{ + std::set resultSet; + + SGVec3d cart = SGVec3d::fromGeod(aPos); + double cutoffM = aRangeNm * SG_NM_TO_METER; + Octree::FindLinesDeque deque; + deque.push_back(Octree::global_spatialOctree); + + while (!deque.empty()) { + Octree::Node* nd = deque.front(); + deque.pop_front(); + + PolyLineList lines; + nd->visitForLines(cart, cutoffM, lines, deque); + + // merge into result set, filtering as we go. + BOOST_FOREACH(PolyLineRef ref, lines) { + if (aFilter.pass(ref->type())) { + resultSet.insert(ref); + } + } + } // of deque iteration + + PolyLineList result; + result.insert(result.end(), resultSet.begin(), resultSet.end()); + return result; +} + + diff --git a/src/Navaids/PolyLine.hxx b/src/Navaids/PolyLine.hxx new file mode 100644 index 000000000..f6db3813b --- /dev/null +++ b/src/Navaids/PolyLine.hxx @@ -0,0 +1,115 @@ +/** + * Polyline - store geographic line-segments */ + +// Written by James Turner, started 2013. +// +// Copyright (C) 2013 James Turner +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef FG_POLY_LINE_HXX +#define FG_POLY_LINE_HXX + +#include + +#include +#include +#include + +namespace flightgear +{ + +typedef std::vector SGGeodVec; + +class PolyLine; + +typedef SGSharedPtr PolyLineRef; + +typedef std::vector PolyLineList; + +/** + * @class Store geographical linear data, with a type code. + * + * This is a basic in-memory model of GIS line data, without support for + * many features; especially there is no support for per-node attributes. + * + * PolyLines are added to the spatial index and can be queried by passing + * a search centre and cutoff distance. + */ +class PolyLine : public SGReferenced +{ +public: + virtual ~PolyLine(); + + enum Type + { + INVALID = 0, + COASTLINE, + NATIONAL_BOUNDARY, /// aka a border + REGIONAL_BOUNDARY, /// state / province / country / department + RIVER, + // airspace types in the future + LAST_TYPE + }; + + Type type() const + { return m_type; } + + /** + * number of points in this line - at least two. + */ + unsigned int numPoints() const; + + SGGeod point(unsigned int aIndex) const; + + const SGGeodVec& points() const + { return m_data; } + + /** + * create poly line objects from raw input points and a type. + * input points will be subdivided so the bounding area of each + * polyline stays within some threshold. + * + */ + static PolyLineList createChunked(Type aTy, const SGGeodVec& aRawPoints); + + /** + * retrieve all the lines within a range of a search point. + * lines are returned if any point is near the search location. + */ + static PolyLineList linesNearPos(const SGGeod& aPos, double aRangeNm, Type aTy); + + class TypeFilter + { + public: + virtual bool pass(Type aTy) const = 0; + }; + + static PolyLineList linesNearPos(const SGGeod& aPos, double aRangeNm, const TypeFilter& aFilter); +private: + void addToSpatialIndex() const; + + PolyLine(Type aTy, const SGGeodVec& aPoints); + + Type m_type; + SGGeodVec m_data; + // cache the bounding box? +}; + + + +} // of namespace flightgear + +#endif \ No newline at end of file diff --git a/src/Navaids/PositionedOctree.cxx b/src/Navaids/PositionedOctree.cxx index 781fee8f2..2dbae4089 100644 --- a/src/Navaids/PositionedOctree.cxx +++ b/src/Navaids/PositionedOctree.cxx @@ -113,7 +113,21 @@ void Leaf::loadChildren() childrenLoaded = true; } - + +void Leaf::addPolyLine(PolyLineRef aLine) +{ + lines.push_back(aLine); +} + +void Leaf::visitForLines(const SGVec3d& aPos, double aCutoff, + PolyLineList& aLines, + FindLinesDeque& aQ) const +{ + aLines.insert(aLines.end(), lines.begin(), lines.end()); +} + +/////////////////////////////////////////////////////////////////////////////// + Branch::Branch(const SGBoxd& aBox, int64_t aIdent) : Node(aBox, aIdent), childrenLoaded(false) @@ -139,6 +153,24 @@ void Branch::visit(const SGVec3d& aPos, double aCutoff, aQ.push(Ordered(children[i], d)); } // of child iteration } + +void Branch::visitForLines(const SGVec3d& aPos, double aCutoff, + PolyLineList& aLines, + FindLinesDeque& aQ) const +{ + for (unsigned int i=0; i<8; ++i) { + if (!children[i]) { + continue; + } + + double d = children[i]->distToNearest(aPos); + if (d > aCutoff) { + continue; // exceeded cutoff + } + + aQ.push_back(children[i]); + } // of child iteration +} Node* Branch::childForPos(const SGVec3d& aCart) const { diff --git a/src/Navaids/PositionedOctree.hxx b/src/Navaids/PositionedOctree.hxx index 3a830349c..b2100718b 100644 --- a/src/Navaids/PositionedOctree.hxx +++ b/src/Navaids/PositionedOctree.hxx @@ -37,6 +37,7 @@ #include #include +#include namespace flightgear { @@ -112,6 +113,10 @@ namespace Octree typedef Ordered OrderedPositioned; typedef std::vector FindNearestResults; + // for extracting lines, we don't care about distance ordering, since + // we're always grabbing all the lines in an area + typedef std::deque FindLinesDeque; + extern Node* global_spatialOctree; class Leaf; @@ -144,6 +149,10 @@ namespace Octree FindNearestResults& aResults, FindNearestPQueue&) = 0; virtual Leaf* findLeafForPos(const SGVec3d& aPos) const = 0; + + virtual void visitForLines(const SGVec3d& aPos, double aCutoff, + PolyLineList& aLines, + FindLinesDeque& aQ) const = 0; protected: Node(const SGBoxd &aBox, int64_t aIdent) : _ident(aIdent), @@ -170,12 +179,20 @@ namespace Octree } void insertChild(FGPositioned::Type ty, PositionedID id); + + void addPolyLine(PolyLineRef); + + virtual void visitForLines(const SGVec3d& aPos, double aCutoff, + PolyLineList& aLines, + FindLinesDeque& aQ) const; private: bool childrenLoaded; typedef std::multimap ChildMap; ChildMap children; - + + PolyLineList lines; + void loadChildren(); }; @@ -195,6 +212,10 @@ namespace Octree } int childMask() const; + + virtual void visitForLines(const SGVec3d& aPos, double aCutoff, + PolyLineList& aLines, + FindLinesDeque& aQ) const; private: Node* childForPos(const SGVec3d& aCart) const; Node* childAtIndex(int childIndex) const;