File indexing completed on 2024-04-21 07:37:29

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 }