File indexing completed on 2023-09-24 07:56: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 #ifndef MARBLE_NODEREDUCER_H 0007 #define MARBLE_NODEREDUCER_H 0008 0009 #include "OsmPlacemarkData.h" 0010 #include "VectorClipper.h" 0011 0012 namespace Marble { 0013 0014 class NodeReducer { 0015 public: 0016 NodeReducer(GeoDataDocument* document, const TileId &tileId); 0017 0018 qint64 removedNodes() const; 0019 qint64 remainingNodes() const; 0020 0021 private: 0022 qreal epsilonFor(qreal multiplier) const; 0023 qreal perpendicularDistance(const GeoDataCoordinates &a, const GeoDataCoordinates &b, const GeoDataCoordinates &c) const; 0024 bool touchesTileBorder(const GeoDataCoordinates &coordinates) const; 0025 void setBorderPoints(OsmPlacemarkData &osmData, const QVector<int> &borderPoints, int length) const; 0026 0027 GeoDataLinearRing* reducedRing(const GeoDataLinearRing& prevRing, 0028 GeoDataPlacemark* placemark, 0029 const GeoDataPlacemark::GeoDataVisualCategory& visualCategory); 0030 GeoDataPolygon* reducedPolygon(const GeoDataPolygon& prevPolygon, 0031 GeoDataPlacemark* placemark, 0032 const GeoDataPlacemark::GeoDataVisualCategory& visualCategory); 0033 0034 template<class T> 0035 void reduce(T const & lineString, OsmPlacemarkData& osmData, GeoDataPlacemark::GeoDataVisualCategory visualCategory, T* reducedLine) 0036 { 0037 bool const isArea = lineString.isClosed() && VectorClipper::canBeArea(visualCategory); 0038 qreal const epsilon = epsilonFor(isArea ? 45.0 : 30.0); 0039 *reducedLine = douglasPeucker(lineString, osmData, epsilon); 0040 0041 qint64 prevSize = lineString.size(); 0042 qint64 reducedSize = reducedLine->size(); 0043 m_removedNodes += (prevSize - reducedSize); 0044 m_remainingNodes += reducedSize; 0045 0046 QVector<int> borderPoints; 0047 int index = 0; 0048 for (auto const &coordinate: *reducedLine) { 0049 if (touchesTileBorder(coordinate)) { 0050 borderPoints << index; 0051 } 0052 ++index; 0053 } 0054 setBorderPoints(osmData, borderPoints, reducedLine->size()); 0055 } 0056 0057 template<class T> 0058 T extract(T const & lineString, int start, int end) const 0059 { 0060 T result; 0061 for (int i=start; i<=end; ++i) { 0062 result << lineString[i]; 0063 } 0064 return result; 0065 } 0066 0067 template<class T> 0068 T merge(T const & a, T const &b) const 0069 { 0070 T result = a; 0071 int const index = a.size(); 0072 result << b; 0073 result.remove(index); 0074 return result; 0075 } 0076 0077 template<class T> 0078 T douglasPeucker(T const & lineString, const OsmPlacemarkData &osmData, qreal epsilon) const 0079 { 0080 if (lineString.size() < 3) { 0081 return lineString; 0082 } 0083 0084 double maxDistance = 0.0; 0085 int index = 1; 0086 int const end = lineString.size()-1; 0087 for (int i = 1; i<end; ++i) { 0088 double const distance = perpendicularDistance(lineString[i], lineString[0], lineString[end]); 0089 if (distance > maxDistance) { 0090 index = i; 0091 maxDistance = distance; 0092 } 0093 } 0094 0095 if (maxDistance >= epsilon) { 0096 T const left = douglasPeucker(extract(lineString, 0, index), osmData, epsilon); 0097 T const right = douglasPeucker(extract(lineString, index, end), osmData, epsilon); 0098 return merge(left, right); 0099 } 0100 0101 T result; 0102 result << lineString[0]; 0103 for (int i=1; i<end; ++i) { 0104 // even if under the Douglas Peucker threshold, we keep the following nodes: 0105 // - nodes at a tile border 0106 // - nodes containing any OSM tag themselves 0107 // - nodes adjacent to synthetic nodes introduced by clipping. This is to avoid 0108 // synthetic only line segments that geometry reassembly on the client might 0109 // remove entirely and thus distort the original geometry. 0110 bool const keepNode = touchesTileBorder(lineString[i]) || !osmData.nodeReference(lineString[i]).isEmpty() 0111 || osmData.nodeReference(lineString[i-1]).id() < 0 || ((i + 1) < end && osmData.nodeReference(lineString[i+1]).id() < 0); 0112 if (keepNode) { 0113 result << lineString[i]; 0114 } 0115 } 0116 result << lineString[end]; 0117 return result; 0118 } 0119 0120 qint64 m_removedNodes; 0121 qint64 m_remainingNodes; 0122 0123 int m_zoomLevel; 0124 double m_tileBoundary[4]; 0125 enum Boundary { 0126 West = 0, 0127 North = 1, 0128 East = 2, 0129 South = 3 0130 }; 0131 }; 0132 0133 } 0134 0135 #endif