File indexing completed on 2025-01-19 06:43:38
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 GeoDataLinearRing *NodeReducer::reducedRing(const GeoDataLinearRing& prevRing, 0123 GeoDataPlacemark* placemark, 0124 const GeoDataPlacemark::GeoDataVisualCategory& visualCategory) 0125 { 0126 GeoDataLinearRing* reducedRing = new GeoDataLinearRing; 0127 reduce(prevRing, placemark->osmData(), visualCategory, reducedRing); 0128 return reducedRing; 0129 } 0130 0131 GeoDataPolygon *NodeReducer::reducedPolygon(const GeoDataPolygon& prevPolygon, 0132 GeoDataPlacemark* placemark, 0133 const GeoDataPlacemark::GeoDataVisualCategory& visualCategory) 0134 { 0135 GeoDataPolygon* reducedPolygon = new GeoDataPolygon; 0136 GeoDataLinearRing const * prevRing = &(prevPolygon.outerBoundary()); 0137 GeoDataLinearRing reducedRing; 0138 reduce(*prevRing, placemark->osmData().memberReference(-1), visualCategory, &reducedRing); 0139 reducedPolygon->setOuterBoundary(reducedRing); 0140 QVector<GeoDataLinearRing> const & innerBoundaries = prevPolygon.innerBoundaries(); 0141 for(int i = 0; i < innerBoundaries.size(); i++) { 0142 prevRing = &innerBoundaries[i]; 0143 GeoDataLinearRing reducedInnerRing; 0144 reduce(*prevRing, placemark->osmData().memberReference(i), visualCategory, &reducedInnerRing); 0145 reducedPolygon->appendInnerBoundary(reducedInnerRing); 0146 } 0147 return reducedPolygon; 0148 } 0149 0150 }