File indexing completed on 2024-12-08 09:34:33
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 }