File indexing completed on 2024-03-24 15:23:44

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 }