File indexing completed on 2025-03-09 03:37:14
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 (const auto prevRing = geodata_cast<GeoDataLinearRing>(geometry)) { 0038 placemark->setGeometry(reducedRing(*prevRing, placemark, visualCategory)); 0039 } else if (const auto prevPolygon = geodata_cast<GeoDataPolygon>(geometry)) { 0040 placemark->setGeometry(reducedPolygon(*prevPolygon, placemark, visualCategory)); 0041 } else if (const auto building = geodata_cast<GeoDataBuilding>(geometry)) { 0042 if (const auto prevRing = geodata_cast<GeoDataLinearRing>(&building->multiGeometry()->at(0))) { 0043 GeoDataLinearRing* ring = reducedRing(*prevRing, placemark, visualCategory); 0044 GeoDataBuilding* newBuilding = new GeoDataBuilding(*building); 0045 newBuilding->multiGeometry()->clear(); 0046 newBuilding->multiGeometry()->append(ring); 0047 placemark->setGeometry(newBuilding); 0048 } else if (const auto prevPolygon = geodata_cast<GeoDataPolygon>(&building->multiGeometry()->at(0))) { 0049 GeoDataPolygon* poly = reducedPolygon(*prevPolygon, placemark, visualCategory); 0050 GeoDataBuilding* newBuilding = new GeoDataBuilding(*building); 0051 newBuilding->multiGeometry()->clear(); 0052 newBuilding->multiGeometry()->append(poly); 0053 placemark->setGeometry(newBuilding); 0054 } 0055 } 0056 } 0057 } 0058 0059 qint64 NodeReducer::remainingNodes() const 0060 { 0061 return m_remainingNodes; 0062 } 0063 0064 qreal NodeReducer::epsilonFor(qreal multiplier) const 0065 { 0066 if (m_zoomLevel >= 17) { 0067 return 0.25; 0068 } else if (m_zoomLevel >= 10) { 0069 int const factor = 1 << (qAbs(m_zoomLevel-12)); 0070 return multiplier / factor; 0071 } else { 0072 int const factor = 1 << (qAbs(m_zoomLevel-10)); 0073 return multiplier * factor; 0074 } 0075 } 0076 0077 qreal NodeReducer::perpendicularDistance(const GeoDataCoordinates &a, const GeoDataCoordinates &b, const GeoDataCoordinates &c) const 0078 { 0079 qreal ret; 0080 qreal const y0 = a.latitude(); 0081 qreal const x0 = a.longitude(); 0082 qreal const y1 = b.latitude(); 0083 qreal const x1 = b.longitude(); 0084 qreal const y2 = c.latitude(); 0085 qreal const x2 = c.longitude(); 0086 qreal const y01 = x0 - x1; 0087 qreal const x01 = y0 - y1; 0088 qreal const y10 = x1 - x0; 0089 qreal const x10 = y1 - y0; 0090 qreal const y21 = x2 - x1; 0091 qreal const x21 = y2 - y1; 0092 qreal const len = (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2); 0093 qreal const t = len == 0.0 ? -1.0 : (x01 * x21 + y01 * y21) / len; 0094 if ( t < 0.0 ) { 0095 ret = EARTH_RADIUS * a.sphericalDistanceTo(b); 0096 } else if ( t > 1.0 ) { 0097 ret = EARTH_RADIUS * a.sphericalDistanceTo(c); 0098 } else { 0099 qreal const nom = qAbs( x21 * y10 - x10 * y21 ); 0100 qreal const den = sqrt( x21 * x21 + y21 * y21 ); 0101 ret = EARTH_RADIUS * nom / den; 0102 } 0103 0104 return ret; 0105 } 0106 0107 bool NodeReducer::touchesTileBorder(const GeoDataCoordinates &coordinates) const 0108 { 0109 return coordinates.latitude() >= m_tileBoundary[North] || 0110 coordinates.latitude() <= m_tileBoundary[South] || 0111 coordinates.longitude() <= m_tileBoundary[West] || 0112 coordinates.longitude() >= m_tileBoundary[East]; 0113 } 0114 0115 qint64 NodeReducer::removedNodes() const 0116 { 0117 return m_removedNodes; 0118 } 0119 0120 GeoDataLinearRing *NodeReducer::reducedRing(const GeoDataLinearRing& prevRing, 0121 GeoDataPlacemark* placemark, 0122 const GeoDataPlacemark::GeoDataVisualCategory& visualCategory) 0123 { 0124 GeoDataLinearRing* reducedRing = new GeoDataLinearRing; 0125 reduce(prevRing, placemark->osmData(), visualCategory, reducedRing); 0126 return reducedRing; 0127 } 0128 0129 GeoDataPolygon *NodeReducer::reducedPolygon(const GeoDataPolygon& prevPolygon, 0130 GeoDataPlacemark* placemark, 0131 const GeoDataPlacemark::GeoDataVisualCategory& visualCategory) 0132 { 0133 GeoDataPolygon* reducedPolygon = new GeoDataPolygon; 0134 GeoDataLinearRing const * prevRing = &(prevPolygon.outerBoundary()); 0135 GeoDataLinearRing reducedRing; 0136 reduce(*prevRing, placemark->osmData().memberReference(-1), visualCategory, &reducedRing); 0137 reducedPolygon->setOuterBoundary(reducedRing); 0138 QVector<GeoDataLinearRing> const & innerBoundaries = prevPolygon.innerBoundaries(); 0139 for(int i = 0; i < innerBoundaries.size(); i++) { 0140 prevRing = &innerBoundaries[i]; 0141 GeoDataLinearRing reducedInnerRing; 0142 reduce(*prevRing, placemark->osmData().memberReference(i), visualCategory, &reducedInnerRing); 0143 reducedPolygon->appendInnerBoundary(reducedInnerRing); 0144 } 0145 return reducedPolygon; 0146 } 0147 0148 }