File indexing completed on 2024-12-01 06:41:17

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 "WayConcatenator.h"
0007 
0008 #include <QList>
0009 #include <QHash>
0010 #include <QSet>
0011 
0012 #include "GeoDataPlacemark.h"
0013 #include "GeoDataDocument.h"
0014 #include "GeoDataObject.h"
0015 #include "GeoDataLineString.h"
0016 #include "OsmPlacemarkData.h"
0017 #include "StyleBuilder.h"
0018 #include "OsmObjectManager.h"
0019 
0020 
0021 namespace Marble {
0022 
0023 WayConcatenator::WayConcatenator(GeoDataDocument *document) :
0024     m_originalWays(0),
0025     m_mergedWays(0)
0026 {
0027     using PlacemarkPtr = QSharedPointer<GeoDataPlacemark>;
0028     for (GeoDataFeature *feature: document->featureList()) {
0029         if (const auto original = geodata_cast<GeoDataPlacemark>(feature)) {
0030             bool isWay = false;
0031             if (geodata_cast<GeoDataLineString>(original->geometry())) {
0032                 PlacemarkPtr placemark = PlacemarkPtr(new GeoDataPlacemark(*original));
0033                 OsmObjectManager::initializeOsmData(placemark.data());
0034                 OsmPlacemarkData const & osmData = placemark->osmData();
0035                 isWay = osmData.containsTagKey("highway") ||
0036                         osmData.containsTagKey("railway") ||
0037                         osmData.containsTagKey("waterway");
0038                 if (isWay) {
0039                     GeoDataLineString *line = static_cast<GeoDataLineString*>(placemark->geometry());
0040                     qint64 firstId = osmData.nodeReference(line->first()).oid();
0041                     qint64 lastId = osmData.nodeReference(line->last()).oid();
0042                     if (firstId > 0 && lastId > 0) {
0043                         ++m_originalWays;
0044                         bool containsFirst = m_hash.contains(firstId);
0045                         bool containsLast = m_hash.contains(lastId);
0046 
0047                         if (!containsFirst && !containsLast) {
0048                             createWayChunk(placemark, firstId, lastId);
0049                         } else if (containsFirst && !containsLast) {
0050                             auto chunk = wayChunk(*placemark, firstId);
0051                             if (chunk != nullptr) {
0052                                 concatFirst(placemark, chunk);
0053                             } else {
0054                                 createWayChunk(placemark, firstId, lastId);
0055                             }
0056                         } else if (!containsFirst && containsLast) {
0057                             auto chunk = wayChunk(*placemark, lastId);
0058                             if (chunk != nullptr) {
0059                                 concatLast(placemark, chunk);
0060                             } else {
0061                                 createWayChunk(placemark, firstId, lastId);
0062                             }
0063                         } else if (containsFirst && containsLast) {
0064                             auto chunk = wayChunk(*placemark, firstId);
0065                             auto otherChunk = wayChunk(*placemark, lastId);
0066 
0067                             if (chunk != nullptr && otherChunk != nullptr) {
0068                                 if(chunk == otherChunk) {
0069                                     m_wayPlacemarks.append(placemark);
0070                                 } else {
0071                                     concatBoth(placemark, chunk, otherChunk);
0072                                 }
0073                             } else if(chunk != nullptr && otherChunk == nullptr) {
0074                                 concatFirst(placemark, chunk);
0075                             } else if(chunk == nullptr && otherChunk != nullptr) {
0076                                 concatLast(placemark, otherChunk);
0077                             } else {
0078                                 createWayChunk(placemark, firstId, lastId);
0079                             }
0080                         }
0081                     } else {
0082                         isWay = false;
0083                     }
0084                 }
0085             }
0086 
0087             if (!isWay) {
0088                 m_otherPlacemarks << feature->clone();
0089             }
0090         } else {
0091             m_otherPlacemarks << feature->clone();
0092         }
0093     }
0094 
0095     document->clear();
0096     for (auto placemark: m_otherPlacemarks) {
0097         document->append(placemark);
0098     }
0099     addWayChunks(document);
0100 }
0101 
0102 int WayConcatenator::originalWays() const
0103 {
0104     return m_originalWays;
0105 }
0106 
0107 int WayConcatenator::mergedWays() const
0108 {
0109     return m_mergedWays;
0110 }
0111 
0112 void WayConcatenator::addWayChunks(GeoDataDocument *document)
0113 {
0114     for (auto const &placemark: m_wayPlacemarks) {
0115         document->append(placemark->clone());
0116     }
0117 
0118     QSet<WayChunk::Ptr> chunkSet;
0119     auto itr = m_chunks.begin();
0120     for (; itr != m_chunks.end(); ++itr) {
0121         if (!chunkSet.contains(*itr)) {
0122             ++m_mergedWays;
0123             chunkSet.insert(*itr);
0124             PlacemarkPtr placemark = (*itr)->merge();
0125             if (placemark) {
0126                 document->append(placemark->clone());
0127             }
0128         }
0129     }
0130 
0131     m_chunks.clear();
0132 }
0133 
0134 void WayConcatenator::createWayChunk(const PlacemarkPtr &placemark, qint64 firstId, qint64 lastId)
0135 {
0136     WayChunk::Ptr chunk = WayChunk::Ptr(new WayChunk(placemark, firstId, lastId));
0137     m_hash.insert(firstId, chunk);
0138     if (firstId != lastId) {
0139         m_hash.insert(lastId, chunk);
0140     }
0141     m_chunks.append(chunk);
0142 }
0143 
0144 WayChunk::Ptr WayConcatenator::wayChunk(const GeoDataPlacemark &placemark, qint64 matchId) const
0145 {
0146     QHash<qint64, WayChunk::Ptr>::ConstIterator matchItr = m_hash.find(matchId);
0147     while (matchItr != m_hash.end() && matchItr.key() == matchId) {
0148         auto const & chunk = matchItr.value();
0149         if (chunk->concatPossible(placemark)) {
0150             return chunk;
0151         }
0152         ++matchItr;
0153     }
0154     return WayChunk::Ptr();
0155 }
0156 
0157 void WayConcatenator::concatFirst(const PlacemarkPtr &placemark, const WayChunk::Ptr &chunk)
0158 {
0159     GeoDataLineString *line = static_cast<GeoDataLineString*>(placemark->geometry());
0160     qint64 firstId = placemark->osmData().nodeReference(line->first()).oid();
0161     qint64 lastId = placemark->osmData().nodeReference(line->last()).oid();
0162 
0163     if (chunk->first() != chunk->last()) {
0164         int chunksRemoved = m_hash.remove(firstId, chunk);
0165         Q_ASSERT(chunksRemoved == 1);
0166     }
0167     m_hash.insert(lastId, chunk);
0168 
0169     if (firstId == chunk->last()) {
0170         //First node matches with an existing last node
0171         chunk->append(placemark, lastId);
0172     } else {
0173         //First node matches with an existing first node
0174         //Reverse the GeoDataLineString of the placemark
0175         line->reverse();
0176         chunk->prepend(placemark, lastId);
0177     }
0178 }
0179 
0180 void WayConcatenator::concatLast(const PlacemarkPtr &placemark, const WayChunk::Ptr &chunk)
0181 {
0182     GeoDataLineString *line = static_cast<GeoDataLineString*>(placemark->geometry());
0183     qint64 firstId = placemark->osmData().nodeReference(line->first()).oid();
0184     qint64 lastId = placemark->osmData().nodeReference(line->last()).oid();
0185 
0186     if (chunk->first() != chunk->last()) {
0187         int chunksRemoved = m_hash.remove(lastId, chunk);
0188         Q_ASSERT(chunksRemoved == 1);
0189     }
0190     m_hash.insert(firstId, chunk);
0191 
0192     if (lastId == chunk->first()) {
0193         chunk->prepend(placemark, firstId);
0194     } else {
0195         line->reverse();
0196         chunk->append(placemark, firstId);
0197     }
0198 }
0199 
0200 void WayConcatenator::concatBoth(const PlacemarkPtr &placemark, const WayChunk::Ptr &chunk, const WayChunk::Ptr &otherChunk)
0201 {
0202     GeoDataLineString *line = static_cast<GeoDataLineString*>(placemark->geometry());
0203     qint64 firstId = placemark->osmData().nodeReference(line->first()).oid();
0204     qint64 lastId = placemark->osmData().nodeReference(line->last()).oid();
0205 
0206     int chunksRemoved;
0207     if (chunk->first() != chunk->last()) {
0208         chunksRemoved = m_hash.remove(firstId, chunk);
0209         Q_ASSERT(chunksRemoved == 1);
0210     }
0211 
0212     if (firstId == chunk->first()) {
0213         chunk->reverse();
0214     }
0215 
0216     chunk->append(placemark, lastId);
0217 
0218     if (lastId == otherChunk->last()) {
0219         otherChunk->reverse();
0220     }
0221     chunk->append(otherChunk);
0222 
0223     chunksRemoved = m_hash.remove(otherChunk->first(), otherChunk);
0224     Q_ASSERT(chunksRemoved == 1);
0225 
0226     if (otherChunk->first() != otherChunk->last()) {
0227         chunksRemoved = m_hash.remove(otherChunk->last(), otherChunk);
0228         Q_ASSERT(chunksRemoved == 1);
0229     }
0230 
0231     m_hash.insert(otherChunk->last(), chunk);
0232 
0233     m_chunks.removeOne(otherChunk);
0234 }
0235 
0236 }