File indexing completed on 2024-05-12 15:29:53
0001 // SPDX-License-Identifier: LGPL-2.1-or-later 0002 // 0003 // SPDX-FileCopyrightText: 2015 Stanciu Marius-Valeriu <stanciumarius94@gmail.com> 0004 // 0005 0006 // Self 0007 #include "OsmTagEditorWidget_p.h" 0008 #include "OsmTagEditorWidget.h" 0009 0010 // Marble 0011 #include "GeoDataLineString.h" 0012 #include "GeoDataPolygon.h" 0013 #include "GeoDataPlacemark.h" 0014 #include "GeoDataStyle.h" 0015 #include "GeoDataIconStyle.h" 0016 #include "OsmPlacemarkData.h" 0017 #include "GeoDataExtendedData.h" 0018 #include "GeoDataData.h" 0019 #include "GeoDataGeometry.h" 0020 #include "GeoDataPoint.h" 0021 #include "StyleBuilder.h" 0022 0023 // Qt 0024 #include <QTreeWidget> 0025 #include <QObject> 0026 0027 namespace Marble 0028 { 0029 0030 const QString OsmTagEditorWidgetPrivate::m_customTagAdderText = QObject::tr( "Add custom tag..." ); 0031 OsmTagEditorWidgetPrivate::OsmTagEditorWidgetPrivate() 0032 { 0033 // nothing to do 0034 } 0035 0036 OsmTagEditorWidgetPrivate::~OsmTagEditorWidgetPrivate() 0037 { 0038 // nothing to do 0039 } 0040 0041 void OsmTagEditorWidgetPrivate::populateCurrentTagsList() 0042 { 0043 // Name tag 0044 if ( !m_placemark->name().isEmpty() ) { 0045 QStringList itemText; 0046 0047 // "name" is a standard OSM tag, don't translate 0048 itemText<< "name" << m_placemark->name(); 0049 QTreeWidgetItem *nameTag = new QTreeWidgetItem( itemText ); 0050 nameTag->setDisabled( true ); 0051 m_currentTagsList->addTopLevelItem( nameTag ); 0052 } 0053 0054 // Multipolygon type tag 0055 if (geodata_cast<GeoDataPolygon>(m_placemark->geometry())) { 0056 QStringList itemText; 0057 // "type" is a standard OSM tag, don't translate 0058 itemText<< "type" << "multipolygon"; 0059 QTreeWidgetItem *typeTag = new QTreeWidgetItem( itemText ); 0060 typeTag->setDisabled( true ); 0061 m_currentTagsList->addTopLevelItem( typeTag ); 0062 } 0063 0064 // Other tags 0065 if( m_placemark->hasOsmData() ) { 0066 const OsmPlacemarkData& osmData = m_placemark->osmData(); 0067 QHash< QString, QString>::const_iterator it = osmData.tagsBegin(); 0068 QHash< QString, QString>::const_iterator end = osmData.tagsEnd(); 0069 for ( ; it != end; ++it ) { 0070 QTreeWidgetItem *tagItem = tagWidgetItem(OsmTag(it.key(), it.value())); 0071 m_currentTagsList->addTopLevelItem( tagItem ); 0072 } 0073 } 0074 0075 // Custom tag adder item 0076 QTreeWidgetItem *adderItem = new QTreeWidgetItem(); 0077 adderItem->setText( 0, m_customTagAdderText ); 0078 adderItem->setForeground( 0, Qt::gray ); 0079 adderItem->setIcon(0, QIcon(QStringLiteral(":marble/list-add.png"))); 0080 adderItem->setFlags( adderItem->flags() | Qt::ItemIsEditable ); 0081 m_currentTagsList->addTopLevelItem( adderItem ); 0082 m_currentTagsList->resizeColumnToContents( 0 ); 0083 m_currentTagsList->resizeColumnToContents( 1 ); 0084 0085 0086 } 0087 0088 void OsmTagEditorWidgetPrivate::populatePresetTagsList() 0089 { 0090 QList<OsmTag> tags = recommendedTags(); 0091 for (const OsmTag &tag: tags) { 0092 QTreeWidgetItem *tagItem = tagWidgetItem( tag ); 0093 m_recommendedTagsList->addTopLevelItem( tagItem ); 0094 } 0095 } 0096 0097 QTreeWidgetItem *OsmTagEditorWidgetPrivate::tagWidgetItem(const OsmTag &tag) 0098 { 0099 QStringList itemText; 0100 0101 itemText << tag.first; 0102 itemText << (tag.second.isEmpty() ? QLatin1Char('<') + QObject::tr("value") + QLatin1Char('>') : tag.second); 0103 0104 QTreeWidgetItem *tagItem = new QTreeWidgetItem( itemText ); 0105 0106 return tagItem; 0107 } 0108 0109 QList<OsmTagEditorWidgetPrivate::OsmTag> OsmTagEditorWidgetPrivate::recommendedTags() const 0110 { 0111 static const QVector<OsmTag> additionalOsmTags = createAdditionalOsmTags(); 0112 0113 QList<OsmTag> recommendedTags; 0114 0115 QStringList filter = generateTagFilter(); 0116 0117 auto const osmTagMapping = StyleBuilder::osmTagMapping(); 0118 for (auto iter=osmTagMapping.begin(), end=osmTagMapping.end() ; iter != end; ++iter) { 0119 if ( filter.contains( iter.key().first ) ) { 0120 recommendedTags += iter.key(); 0121 } 0122 } 0123 0124 for (const auto& additionalOsmTag: additionalOsmTags) { 0125 if (filter.contains(additionalOsmTag.first)) { 0126 recommendedTags += additionalOsmTag; 0127 } 0128 } 0129 0130 return recommendedTags; 0131 } 0132 0133 0134 QStringList OsmTagEditorWidgetPrivate::generateTagFilter() const 0135 { 0136 // TO DO: implement more dynamic criteria for the filter 0137 // based on https://taginfo.openstreetmap.org/ and https://wiki.openstreetmap.org/wiki/ 0138 0139 // Contains all keys that should pass through the filter ( eg. { "amenity", "landuse", etc.. } ) 0140 QStringList filter; 0141 0142 QStringList tags, tagsAux; 0143 OsmPlacemarkData osmData; 0144 if ( m_placemark->hasOsmData() ) { 0145 osmData = m_placemark->osmData(); 0146 } 0147 else { 0148 osmData = OsmPlacemarkData(); 0149 } 0150 0151 // Patterns in order of usefulness 0152 0153 0154 // If the placemark is a node, and it doesn't already have any node-specific tags, recommend all node-specific tags 0155 tags = QStringList() << "amenity=*" << "shop=*" << "transport=*" << "tourism=*" << "historic=*" << "power=*" << "barrier=*"; 0156 if (geodata_cast<GeoDataPoint>(m_placemark->geometry()) && !containsAny(osmData, tags)) { 0157 addPattern( filter, osmData, tags ); 0158 } 0159 0160 0161 0162 // If the placemark is a way, and it doesn't already have any way-specific tags, recommend all way-specific tags 0163 tags = QStringList() << "highway=*" << "waterway=*" << "railway=*"; 0164 if (geodata_cast<GeoDataLineString>(m_placemark->geometry()) && !containsAny(osmData, tags)) { 0165 addPattern( filter, osmData, tags ); 0166 } 0167 0168 0169 0170 // If the placemark is a polygon, and it doesn't already have any polygon-specific tags, recommend all polygon-specific tags 0171 tags = QStringList() << "landuse=*" << "leisure=*"; 0172 if (geodata_cast<GeoDataPolygon>(m_placemark->geometry()) && !containsAny(osmData, tags)) { 0173 addPattern( filter, osmData, tags ); 0174 } 0175 0176 0177 0178 // If the placemark is a relation, recommend type=* 0179 tags = QStringList() << "type=*"; 0180 if (m_placemark->extendedData().value(QStringLiteral("osmRelation")).value().toString() == QLatin1String("yes")) { 0181 addPattern( filter, osmData, tags ); 0182 } 0183 0184 0185 0186 // If the placemark has type=route, recommend route=*, network=*, ref=*, operator=* 0187 tags = QStringList() << "type=route"; 0188 tagsAux = QStringList() << "route=*" << "network=*" << "ref=*" << "operator=*"; 0189 if (containsAny(osmData, tags)) { 0190 addPattern( filter, osmData, tagsAux ); 0191 } 0192 0193 0194 0195 // If the placemark has type=route_master, recommend route_master=*, 0196 tags = QStringList() << "type=route_master"; 0197 tagsAux = QStringList() << "route_master=*"; 0198 if (containsAny(osmData, tags)) { 0199 addPattern( filter, osmData, tagsAux ); 0200 } 0201 0202 0203 0204 // If the placemark has type=public_transport, recommend public_transport=*, 0205 tags = QStringList() << "type=public_transport"; 0206 tagsAux = QStringList() << "public_transport=*"; 0207 if (containsAny(osmData, tags)) { 0208 addPattern( filter, osmData, tagsAux ); 0209 } 0210 0211 0212 0213 // If the placemark has type=waterway, recommend waterway=*, 0214 tags = QStringList() << "type=waterway"; 0215 tagsAux = QStringList() << "waterway=*"; 0216 if (containsAny(osmData, tags)) { 0217 addPattern( filter, osmData, tagsAux ); 0218 } 0219 0220 0221 0222 // If the placemark has type=enforcement, recommend enforcement=*, 0223 tags = QStringList() << "type=enforcement"; 0224 tagsAux = QStringList() << "enforcement=*"; 0225 if (containsAny(osmData, tags)) { 0226 addPattern( filter, osmData, tagsAux ); 0227 } 0228 0229 0230 0231 // If the placemark has amenity=place_of_worship, recommend religion=* 0232 tags = QStringList() << "amenity=place_of_worship"; 0233 tagsAux = QStringList() << "religion=*"; 0234 if (containsAny(osmData, tags)) { 0235 addPattern( filter, osmData, tagsAux ); 0236 } 0237 0238 0239 0240 // If the placemark has amenity=toilets, recommend drinking_water=*, indoor=* 0241 tags = QStringList() << "amenity=toilets"; 0242 tagsAux = QStringList() << "drinking_water=*" << "indoor=*"; 0243 if (containsAny(osmData, tags)) { 0244 addPattern( filter, osmData, tagsAux ); 0245 } 0246 0247 0248 0249 // If the placemark has tourism=hostel, tourism=hotel or tourism=motel, recommend rooms=*, beds=*, wheelchair=* 0250 tags = QStringList() << "tourism=hotel" << "tourism=hostel" << "tourism=motel"; 0251 tagsAux = QStringList() << "rooms=*" << "beds=*" << "wheelchair=*"; 0252 if (containsAny(osmData, tags)) { 0253 addPattern( filter, osmData, tagsAux ); 0254 } 0255 0256 0257 0258 // If the placemark has tourism=*, shop=*, amenity=*, recommend website=*, email=*, fee=* 0259 tags = QStringList() << "tourism=*" << "shop=*" << "amenity=*"; 0260 tagsAux = QStringList() << "website=*" << "email=*" << "fee=*"; 0261 if (containsAny(osmData, tags)) { 0262 addPattern( filter, osmData, tagsAux ); 0263 } 0264 0265 0266 0267 // If the placemark has amenity=* shop=*, recommend building=* 0268 tags = QStringList() << "amenity=*" << "shop=*"; 0269 tagsAux = QStringList() << "building=*"; 0270 if (containsAny(osmData, tags)) { 0271 addPattern( filter, osmData, tagsAux ); 0272 } 0273 0274 0275 0276 // If the placemark has highway=*, recommend "lanes=*", "maxspeed=*", "oneway=*", "service=*", "bridge=*", "tunnel=*" 0277 tags = QStringList() << "highway=*"; 0278 tagsAux = QStringList() << "lanes=*" << "maxspeed=*" << "maxheight=*" << "maxweight=*" << "abutters=*" << "oneway=*" << "service=*" << "bridge=*" << "tunnel=*"; 0279 if (geodata_cast<GeoDataLineString>(m_placemark->geometry()) && containsAny(osmData, tags)) { 0280 addPattern( filter, osmData, tagsAux ); 0281 } 0282 0283 0284 0285 // If the placemark is a polygon, recommend "surface=*" 0286 tags = QStringList() << "surface=*"; 0287 if (geodata_cast<GeoDataPolygon>(m_placemark->geometry())) { 0288 addPattern( filter, osmData, tags ); 0289 } 0290 0291 0292 0293 // Always recommend these: 0294 tags = QStringList() << "addr:street=*" << "addr:housenumber=*" << "addr:postcode=*" << "addr:country=*" << "access=*"; 0295 addPattern( filter, osmData, tags ); 0296 0297 0298 return filter; 0299 } 0300 0301 bool OsmTagEditorWidgetPrivate::containsAny(const OsmPlacemarkData &osmData, const QStringList &tags) 0302 { 0303 for ( const QString &tag: tags ) { 0304 const QStringList tagSplit = tag.split(QLatin1Char('=')); 0305 0306 // Only "key=value" mappings should be checked 0307 Q_ASSERT( tagSplit.size() == 2 ); 0308 0309 QString key = tagSplit.at( 0 ); 0310 QString value = tagSplit.at( 1 ); 0311 0312 if (value == QLatin1String("*") && osmData.containsTagKey(key)) { 0313 return true; 0314 } 0315 else if (value != QLatin1String("*") && osmData.containsTag(key, value)) { 0316 return true; 0317 } 0318 } 0319 return false; 0320 } 0321 0322 void OsmTagEditorWidgetPrivate::addPattern(QStringList &filter, const OsmPlacemarkData &osmData, const QStringList &tags) 0323 { 0324 for ( const QString &tag: tags ) { 0325 const QStringList tagSplit = tag.split(QLatin1Char('=')); 0326 QString key = tagSplit.at( 0 ); 0327 if ( !osmData.containsTagKey( key ) ) { 0328 filter << key; 0329 } 0330 } 0331 } 0332 0333 QVector<OsmTagEditorWidgetPrivate::OsmTag> OsmTagEditorWidgetPrivate::createAdditionalOsmTags() 0334 { 0335 const QVector<OsmTag> additionalOsmTags = QVector<OsmTag>() 0336 0337 // Recommended for nodes 0338 << OsmTag("power", "pole") 0339 << OsmTag("power", "generator") 0340 << OsmTag("barrier", "fence") 0341 << OsmTag("barrier", "wall") 0342 << OsmTag("barrier", "gate") 0343 0344 // Recommended for ways 0345 << OsmTag("lanes", "") 0346 << OsmTag("maxspeed", "") 0347 << OsmTag("maxheight", "") 0348 << OsmTag("maxweight", "") 0349 << OsmTag("oneway", "yes") 0350 << OsmTag("service", "driveway") 0351 << OsmTag("service", "parking_aisle") 0352 << OsmTag("service", "alley") 0353 << OsmTag("tunnel", "yes") 0354 << OsmTag("abutters", "commercial") 0355 << OsmTag("abutters", "industrial") 0356 << OsmTag("abutters", "mixed") 0357 << OsmTag("abutters", "residential") 0358 0359 // Recommended for areas 0360 << OsmTag("surface", "unpaved") 0361 << OsmTag("surface", "paved") 0362 << OsmTag("surface", "gravel") 0363 << OsmTag("surface", "dirt") 0364 << OsmTag("surface", "grass") 0365 0366 // Relations 0367 << OsmTag("type", "route") 0368 << OsmTag("type", "route_master") 0369 << OsmTag("type", "public_transport") 0370 << OsmTag("type", "destination_sign") 0371 << OsmTag("type", "waterway") 0372 << OsmTag("type", "enforcement") 0373 0374 // Relations: route 0375 << OsmTag("route", "road") 0376 << OsmTag("route", "bicycle") 0377 << OsmTag("route", "foot") 0378 << OsmTag("route", "hiking") 0379 << OsmTag("route", "bus") 0380 << OsmTag("route", "trolleybus") 0381 << OsmTag("route", "ferry") 0382 << OsmTag("route", "detour") 0383 << OsmTag("route", "train") 0384 << OsmTag("route", "tram") 0385 << OsmTag("route", "mtb") 0386 << OsmTag("route", "horse") 0387 << OsmTag("route", "ski") 0388 << OsmTag("roundtrip", "yes") 0389 << OsmTag("network", "") 0390 << OsmTag("ref", "") 0391 << OsmTag("operator", "") 0392 0393 // Relations: route_master 0394 << OsmTag("route_master", "train") 0395 << OsmTag("route_master", "subway") 0396 << OsmTag("route_master", "monorail") 0397 << OsmTag("route_master", "tram") 0398 << OsmTag("route_master", "bus") 0399 << OsmTag("route_master", "trolleybus") 0400 << OsmTag("route_master", "ferry") 0401 << OsmTag("route_master", "bicycle") 0402 0403 // Relations: public_transport 0404 << OsmTag("public_transport", "stop_area") 0405 << OsmTag("public_transport", "stop_area_group") 0406 0407 // Relations: waterway 0408 << OsmTag("waterway", "river") 0409 << OsmTag("waterway", "stream") 0410 << OsmTag("waterway", "canal") 0411 << OsmTag("waterway", "drain") 0412 << OsmTag("waterway", "ditch") 0413 0414 // Relations: enforcement 0415 << OsmTag("enforcement", "maxheight") 0416 << OsmTag("enforcement", "maxweight") 0417 << OsmTag("enforcement", "maxspeed") 0418 << OsmTag("enforcement", "mindistance") 0419 << OsmTag("enforcement", "traffic_signals") 0420 << OsmTag("enforcement", "check") 0421 << OsmTag("enforcement", "access") 0422 << OsmTag("enforcement", "toll") 0423 0424 // Others 0425 << OsmTag("height", "") 0426 << OsmTag("rooms", "") 0427 << OsmTag("beds", "") 0428 << OsmTag("wheelchair", "") 0429 << OsmTag("website", "") 0430 << OsmTag("email", "") 0431 << OsmTag("fee", "") 0432 << OsmTag("destination", "") 0433 << OsmTag("indoor", "yes") 0434 0435 // Recommended for all 0436 << OsmTag("addr:street", "") 0437 << OsmTag("addr:housenumber", "") 0438 << OsmTag("addr:postcode", "") 0439 << OsmTag("addr:country", "") 0440 << OsmTag("access", "private") 0441 << OsmTag("access", "permissive"); 0442 0443 return additionalOsmTags; 0444 } 0445 0446 }