Warning, file /education/marble/tools/vectorosm-tilecreator/NodeReducer.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 // SPDX-License-Identifier: LGPL-2.1-or-later 0002 // 0003 // SPDX-FileCopyrightText: 2016 Akshat Tandon <akshat.tandon@research.iiit.ac.in> 0004 // 0005 0006 #include "NodeReducer.h" 0007 #include "GeoDataPlacemark.h" 0008 #include "GeoDataLineString.h" 0009 #include "GeoDataPolygon.h" 0010 #include "GeoDataBuilding.h" 0011 #include "GeoDataMultiGeometry.h" 0012 #include "GeoDataCoordinates.h" 0013 #include "MarbleMath.h" 0014 0015 #include <QDebug> 0016 #include <QVector> 0017 0018 namespace Marble { 0019 0020 NodeReducer::NodeReducer(GeoDataDocument* document, const TileId &tileId) : 0021 m_removedNodes(0), 0022 m_remainingNodes(0), 0023 m_zoomLevel(tileId.zoomLevel()) 0024 { 0025 const GeoSceneMercatorTileProjection tileProjection; 0026 GeoDataLatLonBox tileBoundary = tileProjection.geoCoordinates(m_zoomLevel, tileId.x(), tileId.y()); 0027 tileBoundary.scale(1.0-1e-4, 1.0-1e-4); 0028 tileBoundary.boundaries(m_tileBoundary[North], m_tileBoundary[South], m_tileBoundary[East], m_tileBoundary[West]); 0029 0030 for (GeoDataPlacemark* placemark: document->placemarkList()) { 0031 GeoDataGeometry const * const geometry = placemark->geometry(); 0032 auto const visualCategory = placemark->visualCategory(); 0033 if (const auto prevLine = geodata_cast<GeoDataLineString>(geometry)) { 0034 GeoDataLineString* reducedLine = new GeoDataLineString; 0035 reduce(*prevLine, placemark->osmData(), visualCategory, reducedLine); 0036 placemark->setGeometry(reducedLine); 0037 } else if (m_zoomLevel < 17) { 0038 if (const auto prevRing = geodata_cast<GeoDataLinearRing>(geometry)) { 0039 placemark->setGeometry(reducedRing(*prevRing, placemark, visualCategory)); 0040 } else if (const auto prevPolygon = geodata_cast<GeoDataPolygon>(geometry)) { 0041 placemark->setGeometry(reducedPolygon(*prevPolygon, placemark, visualCategory)); 0042 } else if (const auto building = geodata_cast<GeoDataBuilding>(geometry)) { 0043 if (const auto prevRing = geodata_cast<GeoDataLinearRing>(&building->multiGeometry()->at(0))) { 0044 GeoDataLinearRing* ring = reducedRing(*prevRing, placemark, visualCategory); 0045 GeoDataBuilding* newBuilding = new GeoDataBuilding(*building); 0046 newBuilding->multiGeometry()->clear(); 0047 newBuilding->multiGeometry()->append(ring); 0048 placemark->setGeometry(newBuilding); 0049 } else if (const auto prevPolygon = geodata_cast<GeoDataPolygon>(&building->multiGeometry()->at(0))) { 0050 GeoDataPolygon* poly = reducedPolygon(*prevPolygon, placemark, visualCategory); 0051 GeoDataBuilding* newBuilding = new GeoDataBuilding(*building); 0052 newBuilding->multiGeometry()->clear(); 0053 newBuilding->multiGeometry()->append(poly); 0054 placemark->setGeometry(newBuilding); 0055 } 0056 } 0057 } 0058 } 0059 } 0060 0061 qint64 NodeReducer::remainingNodes() const 0062 { 0063 return m_remainingNodes; 0064 } 0065 0066 qreal NodeReducer::epsilonFor(qreal multiplier) const 0067 { 0068 if (m_zoomLevel >= 17) { 0069 return 0.25; 0070 } else if (m_zoomLevel >= 10) { 0071 int const factor = 1 << (qAbs(m_zoomLevel-12)); 0072 return multiplier / factor; 0073 } else { 0074 int const factor = 1 << (qAbs(m_zoomLevel-10)); 0075 return multiplier * factor; 0076 } 0077 } 0078 0079 qreal NodeReducer::perpendicularDistance(const GeoDataCoordinates &a, const GeoDataCoordinates &b, const GeoDataCoordinates &c) const 0080 { 0081 qreal ret; 0082 qreal const y0 = a.latitude(); 0083 qreal const x0 = a.longitude(); 0084 qreal const y1 = b.latitude(); 0085 qreal const x1 = b.longitude(); 0086 qreal const y2 = c.latitude(); 0087 qreal const x2 = c.longitude(); 0088 qreal const y01 = x0 - x1; 0089 qreal const x01 = y0 - y1; 0090 qreal const y10 = x1 - x0; 0091 qreal const x10 = y1 - y0; 0092 qreal const y21 = x2 - x1; 0093 qreal const x21 = y2 - y1; 0094 qreal const len = (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2); 0095 qreal const t = len == 0.0 ? -1.0 : (x01 * x21 + y01 * y21) / len; 0096 if ( t < 0.0 ) { 0097 ret = EARTH_RADIUS * a.sphericalDistanceTo(b); 0098 } else if ( t > 1.0 ) { 0099 ret = EARTH_RADIUS * a.sphericalDistanceTo(c); 0100 } else { 0101 qreal const nom = qAbs( x21 * y10 - x10 * y21 ); 0102 qreal const den = sqrt( x21 * x21 + y21 * y21 ); 0103 ret = EARTH_RADIUS * nom / den; 0104 } 0105 0106 return ret; 0107 } 0108 0109 bool NodeReducer::touchesTileBorder(const GeoDataCoordinates &coordinates) const 0110 { 0111 return coordinates.latitude() >= m_tileBoundary[North] || 0112 coordinates.latitude() <= m_tileBoundary[South] || 0113 coordinates.longitude() <= m_tileBoundary[West] || 0114 coordinates.longitude() >= m_tileBoundary[East]; 0115 } 0116 0117 qint64 NodeReducer::removedNodes() const 0118 { 0119 return m_removedNodes; 0120 } 0121 0122 void NodeReducer::setBorderPoints(OsmPlacemarkData &osmData, const QVector<int> &borderPoints, int length) const 0123 { 0124 int const n = borderPoints.size(); 0125 if (n == 0) { 0126 return; 0127 } 0128 0129 if (n > length) { 0130 qDebug() << "Invalid border points for length" << length << ":" << borderPoints; 0131 return; 0132 } 0133 0134 typedef QPair<int, int> Segment; 0135 using Segments = QVector<Segment>; 0136 Segments segments; 0137 Segment currentSegment; 0138 currentSegment.first = borderPoints.first(); 0139 currentSegment.second = currentSegment.first; 0140 for (int i=1; i<n; ++i) { 0141 if (currentSegment.second+1 == borderPoints[i]) { 0142 // compress and continue segment 0143 ++currentSegment.second; 0144 } else { 0145 segments << currentSegment; 0146 currentSegment.first = borderPoints[i]; 0147 currentSegment.second = currentSegment.first; 0148 } 0149 } 0150 0151 if (segments.isEmpty() || currentSegment != segments.last()) { 0152 segments << currentSegment; 0153 } 0154 0155 if (segments.size() > 1 && segments.last().second+1 == length && segments.first().first == 0) { 0156 segments.last().second = segments.first().second; 0157 segments.pop_front(); 0158 } 0159 0160 int wraps = 0; 0161 for (auto const &segment: segments) { 0162 if (segment.first >= segment.second) { 0163 ++wraps; 0164 } 0165 if (segment.first < 0 || segment.second < 0 || segment.first+1 > length || segment.second+1 > length) { 0166 qDebug() << "Wrong border points sequence for length " << length << ":" << borderPoints << ", intermediate " << segments; 0167 return; 0168 } 0169 } 0170 0171 if (wraps > 1) { 0172 //qDebug() << "Wrong border points sequence:" << borderPoints; 0173 return; 0174 } 0175 0176 QString value; 0177 value.reserve(segments.size() * (2 + QString::number(length).size())); 0178 for (auto const &segment: segments) { 0179 int diff = segment.second - segment.first; 0180 diff = diff > 0 ? diff : length + diff; 0181 value = value % QStringLiteral(";") % QString::number(segment.first) % QStringLiteral("+") % QString::number(diff); 0182 } 0183 osmData.addTag(QStringLiteral("mx:bp"), value.mid(1)); 0184 } 0185 0186 GeoDataLinearRing *NodeReducer::reducedRing(const GeoDataLinearRing& prevRing, 0187 GeoDataPlacemark* placemark, 0188 const GeoDataPlacemark::GeoDataVisualCategory& visualCategory) 0189 { 0190 GeoDataLinearRing* reducedRing = new GeoDataLinearRing; 0191 reduce(prevRing, placemark->osmData(), visualCategory, reducedRing); 0192 return reducedRing; 0193 } 0194 0195 GeoDataPolygon *NodeReducer::reducedPolygon(const GeoDataPolygon& prevPolygon, 0196 GeoDataPlacemark* placemark, 0197 const GeoDataPlacemark::GeoDataVisualCategory& visualCategory) 0198 { 0199 GeoDataPolygon* reducedPolygon = new GeoDataPolygon; 0200 GeoDataLinearRing const * prevRing = &(prevPolygon.outerBoundary()); 0201 GeoDataLinearRing reducedRing; 0202 reduce(*prevRing, placemark->osmData().memberReference(-1), visualCategory, &reducedRing); 0203 reducedPolygon->setOuterBoundary(reducedRing); 0204 QVector<GeoDataLinearRing> const & innerBoundaries = prevPolygon.innerBoundaries(); 0205 for(int i = 0; i < innerBoundaries.size(); i++) { 0206 prevRing = &innerBoundaries[i]; 0207 GeoDataLinearRing reducedInnerRing; 0208 reduce(*prevRing, placemark->osmData().memberReference(i), visualCategory, &reducedInnerRing); 0209 reducedPolygon->appendInnerBoundary(reducedInnerRing); 0210 } 0211 return reducedPolygon; 0212 } 0213 0214 }