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 }