File indexing completed on 2025-01-19 06:43:39
0001 // SPDX-License-Identifier: LGPL-2.1-or-later 0002 // 0003 // SPDX-FileCopyrightText: 2016 Dennis Nienhüser <nienhueser@kde.org> 0004 // SPDX-FileCopyrightText: 2016 David Kolozsvari <freedawson@gmail.com> 0005 // 0006 0007 #ifndef VECTORCLIPPER_H 0008 #define VECTORCLIPPER_H 0009 0010 #include "OsmPlacemarkData.h" 0011 0012 #include <GeoDataLatLonBox.h> 0013 #include "GeoDataPlacemark.h" 0014 #include "GeoDataLinearRing.h" 0015 #include "GeoDataBuilding.h" 0016 #include "GeoDataMultiGeometry.h" 0017 #include <TileId.h> 0018 #include <GeoSceneMercatorTileProjection.h> 0019 #include <OsmObjectManager.h> 0020 #include "GeoDataDocument.h" 0021 0022 #include "clipper2/clipper.h" 0023 #include <QMap> 0024 #include <QSet> 0025 0026 #include <memory> 0027 0028 namespace Marble { 0029 0030 class GeoDataLinearRing; 0031 class GeoDataRelation; 0032 0033 class VectorClipper 0034 { 0035 public: 0036 VectorClipper(GeoDataDocument* document, int maxZoomLevel); 0037 0038 GeoDataDocument* clipTo(unsigned int zoomLevel, unsigned int tileX, unsigned int tileY); 0039 static bool canBeArea(GeoDataPlacemark::GeoDataVisualCategory visualCategory); 0040 0041 private: 0042 GeoDataDocument* clipTo(const GeoDataLatLonBox &box, int zoomLevel); 0043 QVector<GeoDataPlacemark*> potentialIntersections(const GeoDataLatLonBox &box) const; 0044 static Clipper2Lib::Rect64 clipRect(const GeoDataLatLonBox &box); 0045 qreal area(const GeoDataLinearRing &ring); 0046 0047 // convert radian-based coordinates to 10^-7 degree (100 nanodegree) integer coordinates used by the clipper library 0048 constexpr static qint64 const s_pointScale = 10000000 / M_PI * 180; 0049 static inline Clipper2Lib::Point64 coordinateToPoint(const GeoDataCoordinates &c) 0050 { 0051 return Clipper2Lib::Point64(qRound64(c.longitude() * s_pointScale), qRound64(c.latitude() * s_pointScale), reinterpret_cast<int64_t>(&c)); 0052 } 0053 static inline GeoDataCoordinates pointToCoordinate(Clipper2Lib::Point64 p) 0054 { 0055 return GeoDataCoordinates((double)p.x / s_pointScale, (double)p.y / s_pointScale); 0056 } 0057 0058 template<class T> 0059 static void pathToRing(const Clipper2Lib::Path64 &path, T *ring, const OsmPlacemarkData &originalOsmData, OsmPlacemarkData &newOsmData) 0060 { 0061 for(const auto &point: path) { 0062 if (point.z) { 0063 const auto *node = reinterpret_cast<const GeoDataCoordinates*>(point.z); 0064 *ring << *node; 0065 auto const data = originalOsmData.nodeReference(*node); 0066 if (data.id() > 0) { 0067 newOsmData.addNodeReference(*node, data); 0068 } 0069 } else { 0070 *ring << pointToCoordinate(point); 0071 } 0072 } 0073 } 0074 0075 template<class T> 0076 void clipString(const GeoDataPlacemark *placemark, const Clipper2Lib::Rect64 &tileBoundary, qreal minArea, 0077 GeoDataDocument* document, QSet<qint64> &osmIds) 0078 { 0079 if (osmIds.contains(placemark->osmData().id())) { 0080 return; 0081 } 0082 bool isBuilding = false; 0083 const T* ring; 0084 std::unique_ptr<GeoDataPlacemark> copyPlacemark; 0085 if (const auto building = geodata_cast<GeoDataBuilding>(placemark->geometry())) { 0086 ring = geodata_cast<T>(&static_cast<const GeoDataMultiGeometry*>(building->multiGeometry())->at(0)); 0087 isBuilding = true; 0088 } else { 0089 copyPlacemark.reset(new GeoDataPlacemark(*placemark)); 0090 ring = geodata_cast<T>(copyPlacemark->geometry()); 0091 } 0092 auto const & osmData = placemark->osmData(); 0093 bool const isClosed = ring->isClosed() && (canBeArea(placemark->visualCategory()) || osmData.tagValue(QStringLiteral("area")) == QLatin1String("yes")); 0094 if (isClosed && minArea > 0.0 && area(*static_cast<const GeoDataLinearRing*>(ring)) < minArea) { 0095 return; 0096 } 0097 using namespace Clipper2Lib; 0098 Path64 subject; 0099 subject.reserve(ring->size()); 0100 for(auto const & node: *ring) { 0101 subject.push_back(coordinateToPoint(node)); 0102 } 0103 0104 Paths64 paths; 0105 if (isClosed) { 0106 paths = Clipper2Lib::RectClip(tileBoundary, subject); 0107 } else { 0108 paths = Clipper2Lib::RectClipLines(tileBoundary, {subject}); 0109 } 0110 for(const auto &path: paths) { 0111 GeoDataPlacemark* newPlacemark = new GeoDataPlacemark; 0112 newPlacemark->setVisible(placemark->isVisible()); 0113 newPlacemark->setVisualCategory(placemark->visualCategory()); 0114 T* newRing = new T; 0115 pathToRing(path, newRing, osmData, newPlacemark->osmData()); 0116 0117 if (isBuilding) { 0118 const auto building = geodata_cast<GeoDataBuilding>(placemark->geometry()); 0119 GeoDataBuilding* newBuilding = new GeoDataBuilding(*building); 0120 newBuilding->multiGeometry()->clear(); 0121 newBuilding->multiGeometry()->append(newRing); 0122 newPlacemark->setGeometry(newBuilding); 0123 } else { 0124 newPlacemark->setGeometry(newRing); 0125 } 0126 if (placemark->osmData().id() > 0) { 0127 newPlacemark->osmData().addTag(QStringLiteral("mx:oid"), QString::number(placemark->osmData().id())); 0128 } 0129 copyTags(*placemark, *newPlacemark); 0130 OsmObjectManager::initializeOsmData(newPlacemark); 0131 document->append(newPlacemark); 0132 osmIds << placemark->osmData().id(); 0133 } 0134 } 0135 0136 void clipPolygon(const GeoDataPlacemark *placemark, const GeoDataLatLonBox &tileBoundary, 0137 const Clipper2Lib::Rect64 &clip, qreal minArea, 0138 GeoDataDocument* document, QSet<qint64> &osmIds); 0139 0140 void copyTags(const GeoDataPlacemark &source, GeoDataPlacemark &target) const; 0141 void copyTags(const OsmPlacemarkData &originalPlacemarkData, OsmPlacemarkData& targetOsmData) const; 0142 0143 QMap<TileId, QVector<GeoDataPlacemark*> > m_items; 0144 int m_maxZoomLevel; 0145 GeoSceneMercatorTileProjection m_tileProjection; 0146 QSet<GeoDataRelation*> m_relations; 0147 }; 0148 0149 } 0150 0151 #endif // VECTORCLIPPER_H