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 }