2 * Polyline - store geographic line-segments */
4 // Written by James Turner, started 2013.
6 // Copyright (C) 2013 James Turner <zakalawe@mac.com>
8 // This program is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU General Public License as
10 // published by the Free Software Foundation; either version 2 of the
11 // License, or (at your option) any later version.
13 // This program is distributed in the hope that it will be useful, but
14 // WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 // General Public License for more details.
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the Free Software
20 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26 #include "PolyLine.hxx"
29 #include <boost/foreach.hpp>
31 #include <simgear/math/sg_geodesy.hxx>
33 #include <Navaids/PositionedOctree.hxx>
35 using namespace flightgear;
37 PolyLine::PolyLine(Type aTy, const SGGeodVec& aPoints) :
41 assert(!aPoints.empty());
49 unsigned int PolyLine::numPoints() const
54 SGGeod PolyLine::point(unsigned int aIndex) const
56 assert(aIndex <= m_data.size());
57 return m_data[aIndex];
60 PolyLineList PolyLine::createChunked(Type aTy, const SGGeodVec& aRawPoints)
63 if (aRawPoints.size() < 2) {
67 const double maxDistanceSquaredM = 40000 * 40000; // 40km to start with
69 SGVec3d chunkStartCart = SGVec3d::fromGeod(aRawPoints.front());
71 SGGeodVec::const_iterator it = aRawPoints.begin();
73 while (it != aRawPoints.end()) {
74 SGVec3d ptCart = SGVec3d::fromGeod(*it);
75 double d2 = distSqr(chunkStartCart, ptCart);
77 // distance check, but also ensure we generate actual valid line segments.
78 if ((chunk.size() >= 2) && (d2 > maxDistanceSquaredM)) {
79 chunk.push_back(*it); // close the segment
80 result.push_back(new PolyLine(aTy, chunk));
81 chunkStartCart = ptCart;
85 chunk.push_back(*it++); // add to open chunk
88 // if we have a single trailing point, we already added it as the last
89 // point of the previous chunk, so we're ok. Otherwise, create the
90 // final chunk's polyline
91 if (chunk.size() > 1) {
92 result.push_back(new PolyLine(aTy, chunk));
98 PolyLineRef PolyLine::create(PolyLine::Type aTy, const SGGeodVec &aRawPoints)
100 return new PolyLine(aTy, aRawPoints);
103 void PolyLine::addToSpatialIndex() const
105 Octree::Node* node = Octree::global_spatialOctree->findNodeForBox(cartesianBox());
106 node->addPolyLine(const_cast<PolyLine*>(this));
109 SGBoxd PolyLine::cartesianBox() const
112 SGGeodVec::const_iterator it;
113 for (it = m_data.begin(); it != m_data.end(); ++it) {
114 SGVec3d cart = SGVec3d::fromGeod(*it);
115 result.expandBy(cart);
121 class SingleTypeFilter : public PolyLine::TypeFilter
124 SingleTypeFilter(PolyLine::Type aTy) :
128 virtual bool pass(PolyLine::Type aTy) const
129 { return (aTy == m_type); }
131 PolyLine::Type m_type;
134 PolyLineList PolyLine::linesNearPos(const SGGeod& aPos, double aRangeNm, Type aTy)
136 return linesNearPos(aPos, aRangeNm, SingleTypeFilter(aTy));
140 PolyLineList PolyLine::linesNearPos(const SGGeod& aPos, double aRangeNm, const TypeFilter& aFilter)
142 std::set<PolyLineRef> resultSet;
144 SGVec3d cart = SGVec3d::fromGeod(aPos);
145 double cutoffM = aRangeNm * SG_NM_TO_METER;
146 Octree::FindLinesDeque deque;
147 deque.push_back(Octree::global_spatialOctree);
149 while (!deque.empty()) {
150 Octree::Node* nd = deque.front();
154 nd->visitForLines(cart, cutoffM, lines, deque);
156 // merge into result set, filtering as we go.
157 BOOST_FOREACH(PolyLineRef ref, lines) {
158 if (aFilter.pass(ref->type())) {
159 resultSet.insert(ref);
162 } // of deque iteration
165 result.insert(result.end(), resultSet.begin(), resultSet.end());