File indexing completed on 2024-05-05 03:49:19

0001 // SPDX-License-Identifier: LGPL-2.1-or-later
0002 //
0003 // SPDX-FileCopyrightText: 2011 Dennis Nienhüser <nienhueser@kde.org>
0004 //
0005 
0006 #include "Placemark.h"
0007 
0008 #ifdef HAVE_QT5_POSITIONING
0009 #include <GeoDataExtendedData.h>
0010 #include <QGeoAddress>
0011 #endif // HAVE_QT5_POSITIONING
0012 
0013 #include <osm/OsmPlacemarkData.h>
0014 #include "GeoDataStyle.h"
0015 #include "GeoDataIconStyle.h"
0016 #include "GeoDataDocument.h"
0017 
0018 namespace Marble {
0019 
0020 Placemark::Placemark(QObject *parent ) :
0021     QObject( parent )
0022 {
0023     // nothing to do
0024 }
0025 
0026 void Placemark::setGeoDataPlacemark( const Marble::GeoDataPlacemark &placemark )
0027 {
0028     m_placemark = placemark;
0029     m_address = QString();
0030     m_description = QString();
0031     m_website = QString();
0032     m_wikipedia = QString();
0033     m_openingHours = QString();
0034     m_wheelchairInfo = QString();
0035     m_wifiAvailable = QString();
0036     m_phone = QString();
0037     updateTags();
0038     updateRelations(placemark);
0039     emit coordinatesChanged();
0040     emit nameChanged();
0041     emit descriptionChanged();
0042     emit addressChanged();
0043     emit websiteChanged();
0044     emit wikipediaChanged();
0045     emit openingHoursChanged();
0046     emit wheelchairInfoChanged();
0047     emit wifiAvailabilityChanged();
0048     emit phoneChanged();
0049     emit tagsChanged();
0050 }
0051 
0052 Marble::GeoDataPlacemark & Placemark::placemark()
0053 {
0054     return m_placemark;
0055 }
0056 
0057 const GeoDataPlacemark &Placemark::placemark() const
0058 {
0059     return m_placemark;
0060 }
0061 
0062 QString Placemark::name() const
0063 {
0064     return m_placemark.displayName();
0065 }
0066 
0067 QString Placemark::description() const
0068 {
0069     if (m_description.isEmpty()) {
0070         auto const category = m_placemark.visualCategory();
0071         m_description = m_placemark.categoryName();
0072 
0073         if (category == GeoDataPlacemark::AccomodationHotel || category == GeoDataPlacemark::FoodRestaurant) {
0074             QString const stars = m_placemark.osmData().tagValue(QStringLiteral("stars"));
0075             if (!stars.isEmpty()) {
0076                 bool hasStars;
0077                 int const numStars = stars.mid(0, 1).toInt(&hasStars);
0078                 if (hasStars) {
0079                     m_description += QString(' ') + QString("★").repeated(numStars) + stars.mid(1);
0080                 } else {
0081                     addTagValue(m_description, QStringLiteral("stars"));
0082                 }
0083             }
0084             addFirstTagValueOf(m_description, QStringList() << "brand" << "operator");
0085         }
0086 
0087         if ((category >= GeoDataPlacemark::AccomodationHostel &&
0088                 category <= GeoDataPlacemark::AccomodationGuestHouse) ||
0089                 category == GeoDataPlacemark::HealthHospital) {
0090             int const rooms = m_placemark.osmData().tagValue(QStringLiteral("rooms")).toInt();
0091             if (rooms > 0) {
0092                 //~ singular %n room
0093                 //~ plural %n rooms
0094                 addTagValue(m_description, QStringLiteral("rooms"), tr("%n rooms", nullptr, rooms));
0095             }
0096             int const beds = m_placemark.osmData().tagValue(QStringLiteral("beds")).toInt();
0097             if (beds > 0) {
0098                 //~ singular %n bed
0099                 //~ plural %n beds
0100                 addTagValue(m_description, QStringLiteral("beds"), tr("%n beds", nullptr, beds));
0101             }
0102         }
0103 
0104         if (category == GeoDataPlacemark::TransportParking || category == GeoDataPlacemark::TransportBicycleParking || category == GeoDataPlacemark::TransportMotorcycleParking) {
0105             addTagValue(m_description, QStringLiteral("capacity"), tr("%1 parking spaces"));
0106             addTagValue(m_description, QStringLiteral("maxstay"), tr("Maximum parking time %1"));
0107         }
0108 
0109         if (category >= GeoDataPlacemark::FoodBar && category <= GeoDataPlacemark::FoodRestaurant) {
0110             if (category != GeoDataPlacemark::FoodRestaurant) {
0111                 addFirstTagValueOf(m_description, QStringList() << "brand" << "operator");
0112             } else {
0113                 // Do nothing, already added in stars section above
0114             }
0115             addTagValue(m_description, "cuisine");
0116             addTagValue(m_description, "brewery");
0117             addTagDescription(m_description, "self_service", "yes", "Self Service");
0118             addTagDescription(m_description, "takeaway", "yes", "Take Away");
0119             addTagDescription(m_description, "outdoor_seating", "yes", "Outdoor Seating");
0120             addTagDescription(m_description, "ice_cream", "yes", "Ice Cream");
0121             addTagDescription(m_description, "smoking", "dedicated", "Smoking (dedicated)");
0122             addTagDescription(m_description, "smoking", "yes", "Smoking allowed");
0123             addTagDescription(m_description, "smoking", "separated", "Smoking (separated)");
0124             addTagDescription(m_description, "smoking", "isolated", "Smoking (isolated)");
0125             addTagDescription(m_description, "smoking", "no", "No smoking");
0126             addTagDescription(m_description, "smoking", "outside", "Smoking (outside)");
0127             addTagDescription(m_description, "smoking:outside", "yes", "Smoking (outside)");
0128             addTagDescription(m_description, "smoking:outside", "separated", "Smoking (outside separated)");
0129             addTagDescription(m_description, "smoking:outside", "no", "No smoking outside");
0130         } else if (category >= GeoDataPlacemark::ShopBeverages && category <= GeoDataPlacemark::Shop) {
0131             addFirstTagValueOf(m_description, QStringList() << "brand" << "operator");
0132             addTagValue(m_description, "clothes");
0133             addTagValue(m_description, "designation");
0134             if (category == GeoDataPlacemark::ShopButcher) {
0135                 addTagValue(m_description, "butcher");
0136             } else if (category == GeoDataPlacemark::ShopCopy) {
0137                 addTagDescription(m_description, QStringLiteral("service:computer"), QStringLiteral("yes"), tr("Computers available", "A copy shop provides computers for customer use"));
0138                 addTagDescription(m_description, QStringLiteral("service:computer"), QStringLiteral("no"), tr("No computers available", "A copy shop does not provide computers for customer use"));
0139                 addTagDescription(m_description, QStringLiteral("service:copy"), QStringLiteral("yes"), tr("Photocopying service", "A copy shop provides photocopying service"));
0140                 addTagDescription(m_description, QStringLiteral("service:copy"), QStringLiteral("no"), tr("No photocopying service", "A copy shop does not provide photocopying service"));
0141                 addTagDescription(m_description, QStringLiteral("service:scan"), QStringLiteral("yes"), tr("Digital scanning", "A copy shop provides a service for scanning documents into digital files"));
0142                 addTagDescription(m_description, QStringLiteral("service:scan"), QStringLiteral("no"), tr("No digital scanning", "A copy shop does not provide a service for scanning documents into digital files"));
0143                 addTagDescription(m_description, QStringLiteral("service:fax"), QStringLiteral("yes"), tr("Fax service", "A copy shop provides a service to send documents through fax"));
0144                 addTagDescription(m_description, QStringLiteral("service:fax"), QStringLiteral("no"), tr("No fax service", "A copy shop does not provide a service to send documents through fax"));
0145                 addTagDescription(m_description, QStringLiteral("service:phone"), QStringLiteral("yes"), tr("Phone service", "A copy shop provides a paid service to make phone calls"));
0146                 addTagDescription(m_description, QStringLiteral("service:phone"), QStringLiteral("no"), tr("No phone service", "A copy shop does not provide a paid service to make phone calls"));
0147                 addTagDescription(m_description, QStringLiteral("service:print"), QStringLiteral("yes"), tr("Digital printing", "A copy shop provides services to print paper documents from digital files"));
0148                 addTagDescription(m_description, QStringLiteral("service:print"), QStringLiteral("no"), tr("No digital printing", "A copy shop does not provide services to print paper documents from digital files"));
0149                 addTagDescription(m_description, QStringLiteral("service:press"), QStringLiteral("yes"), tr("Press printing service", "A copy shop provides a professional service to print a large number of copies of a document"));
0150                 addTagDescription(m_description, QStringLiteral("service:press"), QStringLiteral("no"), tr("No press printing service", "A copy shop does not provide a professional service to print a large number of copies of a document"));
0151                 addTagDescription(m_description, QStringLiteral("service:prepress"), QStringLiteral("yes"), tr("Press printing assistance", "A copy shop provides help with preparing special printing techniques"));
0152                 addTagDescription(m_description, QStringLiteral("service:prepress"), QStringLiteral("no"), tr("No press printing assistance", "A copy shop does not provide help with preparing special printing techniques"));
0153                 addTagDescription(m_description, QStringLiteral("service:self"), QStringLiteral("yes"), tr("Self service", "A copy shop provides individual copy machines for self-service"));
0154                 addTagDescription(m_description, QStringLiteral("service:self"), QStringLiteral("no"), tr(" No self service", "A copy shop does not provide individual machines for self-service"));
0155             } else if (category == GeoDataPlacemark::ShopDeli) {
0156                 addTagDescription(m_description, QStringLiteral("organic"), QStringLiteral("yes"), tr("Sells organic food", "A deli that sells organic food"));
0157                 addTagDescription(m_description, QStringLiteral("organic"), QStringLiteral("no"), tr("Does not sell organic food", "A deli that does not sell organic food"));
0158                 addTagDescription(m_description, QStringLiteral("organic"), QStringLiteral("only"), tr("Only sells organic food", "A deli that only sells organic food"));
0159 
0160                 addTagDescription(m_description, QStringLiteral("diet:gluten_free"), QStringLiteral("yes"), tr("Sells gluten free food", "A deli that sells gluten free food"));
0161                 addTagDescription(m_description, QStringLiteral("diet:gluten_free"), QStringLiteral("no"), tr("Does not sell gluten free food", "A deli that does not sell gluten free food"));
0162                 addTagDescription(m_description, QStringLiteral("diet:gluten_free"), QStringLiteral("only"), tr("Only sells gluten free food", "A deli that only sells gluten free food"));
0163 
0164                 addTagDescription(m_description, QStringLiteral("diet:lactose_free"), QStringLiteral("yes"), tr("Sells lactose free food", "A deli that sells lactose free food"));
0165                 addTagDescription(m_description, QStringLiteral("diet:lactose_free"), QStringLiteral("no"), tr("Does not sell lactose free food", "A deli that does not sell lactose free food"));
0166                 addTagDescription(m_description, QStringLiteral("diet:lactose_free"), QStringLiteral("only"), tr("Only sells lactose free food", "A deli that only sells lactose free food"));
0167             } else if (category == GeoDataPlacemark::ShopTobacco) {
0168                 addTagDescription(m_description, QStringLiteral("lottery"), QStringLiteral("yes"), tr("Sells lottery tickets", "A tobacco shop that also sells lottery tickets"));
0169                 addTagDescription(m_description, QStringLiteral("stamps"), QStringLiteral("yes"), tr("Sells revenue stamps", "A tobacco shop that also sells revenue stamps"));
0170                 addTagDescription(m_description, QStringLiteral("salt"), QStringLiteral("yes"), tr("Sells salt", "A tobacco shop that also sells salt"));
0171             }
0172         } else if (category == GeoDataPlacemark::TransportBusStop) {
0173             addTagValue(m_description, "network");
0174             addTagValue(m_description, "operator");
0175             addTagValue(m_description, "ref");
0176         } else if (category == GeoDataPlacemark::TransportCarShare) {
0177             addTagValue(m_description, "network");
0178             addTagValue(m_description, "operator");
0179         } else if (category == GeoDataPlacemark::TransportRentalBicycle ||
0180                    category == GeoDataPlacemark::TransportRentalCar ||
0181                    category == GeoDataPlacemark::TransportRentalSki) {
0182             addFirstTagValueOf(m_description, QStringList() << "brand" << "operator");
0183 
0184         } else if (category == GeoDataPlacemark::TransportFuel) {
0185             addFirstTagValueOf(m_description, QStringList() << "brand" << "operator");
0186             addTagDescription(m_description, "fuel:diesel", "yes", tr("Diesel"));
0187             addTagDescription(m_description, "fuel:biodiesel", "yes", tr("Biodiesel"));
0188             addTagDescription(m_description, "fuel:octane_91", "yes", tr("Octane 91"));
0189             addTagDescription(m_description, "fuel:octane_95", "yes", tr("Octane 95"));
0190             addTagDescription(m_description, "fuel:octane_98", "yes", tr("Octane 98"));
0191             addTagDescription(m_description, "fuel:octane_100", "yes", tr("Octane 100"));
0192             addTagDescription(m_description, "fuel:e10", "yes", tr("E10"));
0193             addTagDescription(m_description, "fuel:lpg", "yes", tr("LPG"));
0194         } else if (category == GeoDataPlacemark::NaturalTree) {
0195             addTagValue(m_description, "species:en");
0196             addTagValue(m_description, "genus:en");
0197             addTagValue(m_description, "leaf_type");
0198         } else if (category == GeoDataPlacemark::NaturalCave){
0199             addTagValue(m_description, "cave:ref");
0200         } else if (category == GeoDataPlacemark::AmenityRecycling) {
0201             addTagDescription(m_description, QStringLiteral("recycling:batteries"), "yes", tr("Batteries"));
0202             addTagDescription(m_description, QStringLiteral("recycling:clothes"), "yes", tr("Clothes"));
0203             addTagDescription(m_description, QStringLiteral("recycling:glass"), "yes", tr("Glass"));
0204             addTagDescription(m_description, QStringLiteral("recycling:glass_bottles"), "yes", tr("Glass bottles"));
0205             addTagDescription(m_description, QStringLiteral("recycling:green_waste"), "yes", tr("Green waste"));
0206             addTagDescription(m_description, QStringLiteral("recycling:garden_waste"), "yes", tr("Garden waste"));
0207             addTagDescription(m_description, QStringLiteral("recycling:electrical_items"), "yes", tr("Electrical items"));
0208             addTagDescription(m_description, QStringLiteral("recycling:metal"), "yes", tr("Metal"));
0209             addTagDescription(m_description, QStringLiteral("recycling:mobile_phones"), "yes", tr("Mobile phones"));
0210             addTagDescription(m_description, QStringLiteral("recycling:newspaper"), "yes", tr("Newspaper"));
0211             addTagDescription(m_description, QStringLiteral("recycling:paint"), "yes", tr("Paint"));
0212             addTagDescription(m_description, QStringLiteral("recycling:paper"), "yes", tr("Paper"));
0213             addTagDescription(m_description, QStringLiteral("recycling:paper_packaging"), "yes", tr("Paper packaging"));
0214             addTagDescription(m_description, QStringLiteral("recycling:PET"), "yes", tr("PET"));
0215             addTagDescription(m_description, QStringLiteral("recycling:plastic"), "yes", tr("Plastic"));
0216             addTagDescription(m_description, QStringLiteral("recycling:plastic_bags"), "yes", tr("Plastic bags"));
0217             addTagDescription(m_description, QStringLiteral("recycling:plastic_bottles"), "yes", tr("Plastic bottles"));
0218             addTagDescription(m_description, QStringLiteral("recycling:plastic_packaging"), "yes", tr("Plastic packaging"));
0219             addTagDescription(m_description, QStringLiteral("recycling:polyester"), "yes", tr("Polyester"));
0220             addTagDescription(m_description, QStringLiteral("recycling:tyres"), "yes", tr("Tyres"));
0221             addTagDescription(m_description, QStringLiteral("recycling:waste"), "yes", tr("Waste"));
0222             addTagDescription(m_description, QStringLiteral("recycling:white_goods"), "yes", tr("White goods"));
0223             addTagDescription(m_description, QStringLiteral("recycling:wood"), "yes", tr("Wood"));
0224         } else if (category == GeoDataPlacemark::NaturalVolcano) {
0225             addTagDescription(m_description, QStringLiteral("volcano:status"), QStringLiteral("active"), tr("Active", "An active volcano"));
0226             addTagDescription(m_description, QStringLiteral("volcano:status"), QStringLiteral("dormant"), tr("Dormant", "A dormant volcano that will erupt at some point in the future."));
0227             addTagDescription(m_description, QStringLiteral("volcano:status"), QStringLiteral("extinct"), tr("Extinct", "A volcano considered extinct, it has not erupted within the last 10000 years and likely never will again."));
0228             addTagDescription(m_description, QStringLiteral("volcano:type"), QStringLiteral("stratovolcano"), tr("Stratovolcano"));
0229             addTagDescription(m_description, QStringLiteral("volcano:type"), QStringLiteral("shield"), tr("Shield volcano"));
0230             addTagDescription(m_description, QStringLiteral("volcano:type"), QStringLiteral("scoria"), tr("Scoria cone", "A scoria cone volcano."));
0231         } else if (category == GeoDataPlacemark::HealthDoctors) {
0232             addTagDescription(m_description, QStringLiteral("healthcare"), QStringLiteral("alternative"), tr("Alternative medicine"));
0233             addTagDescription(m_description, QStringLiteral("healthcare"), QStringLiteral("audiologist"), tr("Audiologist"));
0234             addTagDescription(m_description, QStringLiteral("healthcare"), QStringLiteral("blood_bank"), tr("Blood bank"));
0235             addTagDescription(m_description, QStringLiteral("healthcare"), QStringLiteral("blood_donation"), tr("Blood donation"));
0236             addTagDescription(m_description, QStringLiteral("healthcare"), QStringLiteral("centre"), tr("Medical center"));
0237             addTagDescription(m_description, QStringLiteral("healthcare"), QStringLiteral("clinic"), tr("Clinic"));
0238             addTagDescription(m_description, QStringLiteral("healthcare"), QStringLiteral("dentist"), tr("Dentist"));
0239             addTagDescription(m_description, QStringLiteral("healthcare"), QStringLiteral("doctor"), tr("Medical practitioner"));
0240             addTagDescription(m_description, QStringLiteral("healthcare"), QStringLiteral("hospital"), tr("Hospital"));
0241             addTagDescription(m_description, QStringLiteral("healthcare"), QStringLiteral("midwife"), tr("Midwife"));
0242             addTagDescription(m_description, QStringLiteral("healthcare"), QStringLiteral("optometrist"), tr("Optometrist"));
0243             addTagDescription(m_description, QStringLiteral("healthcare"), QStringLiteral("physiotherapist"), tr("Physiotherapist"));
0244             addTagDescription(m_description, QStringLiteral("healthcare"), QStringLiteral("podiatrist"), tr("Podiatrist"));
0245             addTagDescription(m_description, QStringLiteral("healthcare"), QStringLiteral("psychotherapist"), tr("Psychotherapist"));
0246             addTagDescription(m_description, QStringLiteral("healthcare"), QStringLiteral("rehabilitation"), tr("Rehabilitation"));
0247             addTagDescription(m_description, QStringLiteral("healthcare"), QStringLiteral("speech_therapist"), tr("Speech therapist"));
0248             addTagValue(m_description, QStringLiteral("healthcare:speciality"));
0249         } else if (category == GeoDataPlacemark::AmenityBench) {
0250             int const seats = m_placemark.osmData().tagValue(QStringLiteral("seats")).toInt();
0251             if (seats > 0) {
0252                 //~ singular %n seat
0253                 //~ plural %n seats
0254                 addTagValue(m_description, QStringLiteral("seats"), tr("%n seats", "number of seats a bench provides", seats));
0255             }
0256             addTagValue(m_description, QStringLiteral("material"));
0257             addTagDescription(m_description, QStringLiteral("backrest"), QStringLiteral("yes"), tr("Has backrest", "A bench provides a backrest to lean against"));
0258             addTagDescription(m_description, QStringLiteral("backrest"), QStringLiteral("no"), tr("No backrest", "A bench provides no backrest to lean against"));
0259         } else if (category == GeoDataPlacemark::AmenityWasteBasket) {
0260             addTagValue(m_description, QStringLiteral("waste"));
0261         } else if (category == GeoDataPlacemark::TransportSpeedCamera) {
0262             addTagValue(m_description, QStringLiteral("maxspeed"), tr("%1 km/h"));
0263             addTagValue(m_description, "ref");
0264         } else if (category == GeoDataPlacemark::TransportParking) {
0265             addTagDescription(m_description, QStringLiteral("supervised"), QStringLiteral("yes"), tr("Is supervised", "Parking spaces are supervised by guards"));
0266             addTagDescription(m_description, QStringLiteral("supervised"), QStringLiteral("no"), tr("Not supervised", "Parking spaces are not supervised by guards"));
0267 
0268             int const disabledSpaces = m_placemark.osmData().tagValue(QStringLiteral("capacity:disabled")).toInt();
0269             if (disabledSpaces > 0) {
0270                 addTagValue(m_description, QStringLiteral("capacity:disabled"), tr("%1 disabled spaces", "Parking spaces"));
0271             }
0272             int const womenSpaces = m_placemark.osmData().tagValue(QStringLiteral("capacity:women")).toInt();
0273             if (womenSpaces > 0) {
0274                 addTagValue(m_description, QStringLiteral("capacity:women"), tr("%1 women spaces", "Parking spaces"));
0275             }
0276             int const parentSpaces = m_placemark.osmData().tagValue(QStringLiteral("capacity:parent")).toInt();
0277             if (parentSpaces > 0) {
0278                 addTagValue(m_description, QStringLiteral("capacity:parent"), tr("%1 parent and child spaces", "Parking spaces"));
0279             }
0280             int const electricChargers = m_placemark.osmData().tagValue(QStringLiteral("capacity:charging")).toInt();
0281             if (electricChargers > 0) {
0282                 addTagValue(m_description, QStringLiteral("capacity:charging"), tr("%1 spaces with electric chargers", "Parking spaces"));
0283             }
0284         } else if (category == GeoDataPlacemark::TransportBicycleParking) {
0285             addTagDescription(m_description, QStringLiteral("surveillance"), QStringLiteral("outdoor"), tr("Has outdoor surveillance", "A parking space has outdoor surveillance"));
0286             addTagDescription(m_description, QStringLiteral("surveillance"), QStringLiteral("indoor"), tr("Has indoor surveillance", "A parking space has indoor surveillance"));
0287             addTagDescription(m_description, QStringLiteral("surveillance"), QStringLiteral("public"), tr("Has public surveillance", "A parking space has public surveillance"));
0288         } else if (category == GeoDataPlacemark::TourismWildernessHut) {
0289             addTagDescription(m_description, QStringLiteral("shower"), QStringLiteral("yes"), tr("Has shower", "A hut provides showers inside or aside"));
0290             addTagDescription(m_description, QStringLiteral("shower"), QStringLiteral("no"), tr("Has no shower", "A hut does not provide showers inside or aside"));
0291             addTagDescription(m_description, QStringLiteral("mattress"), QStringLiteral("yes"), tr("Has mattress", "A hut provides mattress"));
0292             addTagDescription(m_description, QStringLiteral("mattress"), QStringLiteral("no"), tr("Has no mattress", "A hut does not provide mattress"));
0293             addTagDescription(m_description, QStringLiteral("drinking_water"), QStringLiteral("yes"), tr("Has water", "Water is available inside or aside"));
0294             addTagDescription(m_description, QStringLiteral("drinking_water"), QStringLiteral("no"), tr("Has no water", "Water is not available inside nor aside"));
0295             addTagDescription(m_description, QStringLiteral("toilets"), QStringLiteral("yes"), tr("Has toilets", "A hut provides toilets"));
0296             addTagDescription(m_description, QStringLiteral("toilets"), QStringLiteral("no"), tr("Has no toilets", "A hut does not provide toilets"));
0297             addTagDescription(m_description, QStringLiteral("reservation"), QStringLiteral("yes"), tr("Reservation is possible"));
0298             addTagDescription(m_description, QStringLiteral("reservation"), QStringLiteral("no"), tr("No reservation possible"));
0299             addTagDescription(m_description, QStringLiteral("reservation"), QStringLiteral("required"), tr("Reservation is required"));
0300             addTagDescription(m_description, QStringLiteral("reservation"), QStringLiteral("recommended"), tr("Reservation is recommended", "You should make reservation"));
0301             addTagDescription(m_description, QStringLiteral("reservation"), QStringLiteral("members_only"), tr("Only for members", "Reservation is only possible for members of the organisation running the hut"));
0302         } else if (category == GeoDataPlacemark::TourismArtwork) {
0303             addTagValue(m_description, QStringLiteral("artist_name"), tr("By %1"));
0304         } else if (category == GeoDataPlacemark::AmenityChargingStation) {
0305             addTagValue(m_description, QStringLiteral("capacity"), tr("%1 vehicles"));
0306             addTagValue(m_description, QStringLiteral("amperage"), tr("%1 ampere"));
0307             addTagValue(m_description, QStringLiteral("voltage"), tr("%1 volt"));
0308 
0309             addTagDescription(m_description, QStringLiteral("socket"), QStringLiteral("cee_blue"), tr("%1 blue CEE sockets"));
0310             addTagDescription(m_description, QStringLiteral("socket"), QStringLiteral("cee_red_16a"), tr("%1 red CEE sockets (16 A)"));
0311             addTagDescription(m_description, QStringLiteral("socket"), QStringLiteral("cee_red_32a"), tr("%1 red CEE sockets (32 A)"));
0312             addTagDescription(m_description, QStringLiteral("socket"), QStringLiteral("cee_red_64a"), tr("%1 red CEE sockets (64 A)"));
0313             addTagDescription(m_description, QStringLiteral("socket"), QStringLiteral("cee_red_125a"), tr("%1 red CEE sockets (125 A)"));
0314             addTagDescription(m_description, QStringLiteral("socket"), QStringLiteral("nema_5_15"), tr("%1 NEMA-5-15P plugs"));
0315             addTagDescription(m_description, QStringLiteral("socket"), QStringLiteral("typeb"), tr("%1 NEMA-5-15P plugs"));
0316             addTagDescription(m_description, QStringLiteral("socket"), QStringLiteral("nema_5_20"), tr("%1 NEMA-5-20P plugs"));
0317             addTagDescription(m_description, QStringLiteral("socket"), QStringLiteral("nema_14_30"), tr("%1 NEMA 14-30 sockets"));
0318             addTagDescription(m_description, QStringLiteral("socket"), QStringLiteral("nema_14_50"), tr("%1 NEMA 14-50 sockets"));
0319             addTagDescription(m_description, QStringLiteral("socket"), QStringLiteral("schuko"), tr("%1 Schuko sockets"));
0320             addTagDescription(m_description, QStringLiteral("socket"), QStringLiteral("bs1363"), tr("%1 BS 1363 sockets"));
0321             addTagDescription(m_description, QStringLiteral("socket"), QStringLiteral("type1"), tr("%1 Type 1 plugs"));
0322             addTagDescription(m_description, QStringLiteral("socket"), QStringLiteral("type1_combo"), tr("%1 Type 1 combo plugs"));
0323             addTagDescription(m_description, QStringLiteral("socket"), QStringLiteral("type2"), tr("%1 Type 2 sockets"));
0324             addTagDescription(m_description, QStringLiteral("socket"), QStringLiteral("type2_combo"), tr("%1 Type 2 combo sockets"));
0325             addTagDescription(m_description, QStringLiteral("socket"), QStringLiteral("type3"), tr("%1 Type 3 sockets"));
0326             addTagDescription(m_description, QStringLiteral("socket"), QStringLiteral("chademo"), tr("%1 CHAdeMO plugs"));
0327             addTagDescription(m_description, QStringLiteral("socket"), QStringLiteral("magne_charge"), tr("%1 Magne Charge plugs"));
0328             addTagDescription(m_description, QStringLiteral("socket"), QStringLiteral("tesla_standard"), tr("%1 Tesla standard plugs"));
0329             addTagDescription(m_description, QStringLiteral("socket"), QStringLiteral("tesla_supercharger"), tr("%1 Tesla standard plugs (Supercharger)"));
0330             addTagDescription(m_description, QStringLiteral("socket"), QStringLiteral("tesla_roadster"), tr("%1 Tesla roadster plugs"));
0331         } else if (category == GeoDataPlacemark::AmenityCarWash) {
0332             addTagValue(m_description, QStringLiteral("maxwidth"), tr("Maximum vehicle width: %1"));
0333             addTagValue(m_description, QStringLiteral("maxheight"), tr("Maximum vehicle height: %1"));
0334 
0335             addTagDescription(m_description, QStringLiteral("self_service"), QStringLiteral("yes"), tr("Self-service"));
0336             addTagDescription(m_description, QStringLiteral("self_service"), QStringLiteral("no"), tr("No self-service"));
0337             addTagDescription(m_description, QStringLiteral("automated"), QStringLiteral("yes"), tr("Automated"));
0338             addTagDescription(m_description, QStringLiteral("automated"), QStringLiteral("no"), tr("Manual"));
0339         } else if (category == GeoDataPlacemark::AmenitySocialFacility) {
0340             addTagDescription(m_description, QStringLiteral("social_facility"), QStringLiteral("group_home"), tr("Group home"));
0341             addTagDescription(m_description, QStringLiteral("social_facility"), QStringLiteral("nursing_home"), tr("Nursing home"));
0342             addTagDescription(m_description, QStringLiteral("social_facility"), QStringLiteral("assisted_living"), tr("Assisted living", "Like group home but for more independent people, e.g. who have flats"));
0343             addTagDescription(m_description, QStringLiteral("social_facility"), QStringLiteral("day_care"), tr("Nursing services on a daily basis"));
0344             addTagDescription(m_description, QStringLiteral("social_facility"), QStringLiteral("shelter"), tr("Shelter"));
0345             addTagDescription(m_description, QStringLiteral("social_facility"), QStringLiteral("ambulatory_care"), tr("Ambulatory care"));
0346             addTagDescription(m_description, QStringLiteral("social_facility"), QStringLiteral("outreach"), tr("Social welfare services"));
0347             addTagDescription(m_description, QStringLiteral("social_facility"), QStringLiteral("workshop"), tr("Employment and workshops for offenders and people with disabilities"));
0348             addTagDescription(m_description, QStringLiteral("social_facility"), QStringLiteral("food_bank"), tr("Pre-packaged food for free or below market price"));
0349             addTagDescription(m_description, QStringLiteral("social_facility"), QStringLiteral("soup_kitchen"), tr("Prepared meals for free or below market price"));
0350             addTagDescription(m_description, QStringLiteral("social_facility"), QStringLiteral("dairy_kitchen"), tr("Free dairy food (subject to local regulations)"));
0351 
0352             addTagDescription(m_description, QStringLiteral("social_facility:for"), QStringLiteral("abused"), tr("For abused"));
0353             addTagDescription(m_description, QStringLiteral("social_facility:for"), QStringLiteral("child"), tr("For children"));
0354             addTagDescription(m_description, QStringLiteral("social_facility:for"), QStringLiteral("disabled"), tr("For people with physical disabilities"));
0355             addTagDescription(m_description, QStringLiteral("social_facility:for"), QStringLiteral("diseased"), tr("For those who suffer of a disease"));
0356             addTagDescription(m_description, QStringLiteral("social_facility:for"), QStringLiteral("drug_addicted"), tr("For drug-addicted"));
0357             addTagDescription(m_description, QStringLiteral("social_facility:for"), QStringLiteral("homeless"), tr("For homeless"));
0358             addTagDescription(m_description, QStringLiteral("social_facility:for"), QStringLiteral("juvenile"), tr("For juvenile"));
0359             addTagDescription(m_description, QStringLiteral("social_facility:for"), QStringLiteral("mental_health"), tr("For those with mental/psychological problems"));
0360             addTagDescription(m_description, QStringLiteral("social_facility:for"), QStringLiteral("migrant"), tr("For migrants"));
0361             addTagDescription(m_description, QStringLiteral("social_facility:for"), QStringLiteral("orphan"), tr("For orphans"));
0362             addTagDescription(m_description, QStringLiteral("social_facility:for"), QStringLiteral("senior"), tr("For elder people"));
0363             addTagDescription(m_description, QStringLiteral("social_facility:for"), QStringLiteral("underprivileged"), tr("For poor or disadvantaged people"));
0364             addTagDescription(m_description, QStringLiteral("social_facility:for"), QStringLiteral("unemployed"), tr("For unemployed"));
0365             addTagDescription(m_description, QStringLiteral("social_facility:for"), QStringLiteral("victim"), tr("For victims of crimes"));
0366         } else if (category == GeoDataPlacemark::HistoricMemorial) {
0367             addTagValue(m_description, QStringLiteral("inscription"), tr("Inscription: %1"));
0368         } else if (category >= GeoDataPlacemark::AerialwayCableCar && category <= GeoDataPlacemark::AerialwayGoods) {
0369             addTagValue(m_description, QStringLiteral("occupancy"), tr("%1 places per carriage"));
0370             addTagValue(m_description, QStringLiteral("capacity"), tr("%1 people per hour"));
0371             addTagValue(m_description, QStringLiteral("duration"), tr("%1 minutes"));
0372 
0373             addTagDescription(m_description, QStringLiteral("bubble"), QStringLiteral("yes"), tr("Has weather protection", "A carriage is protected from the weather"));
0374             addTagDescription(m_description, QStringLiteral("bubble"), QStringLiteral("no"), tr("No weather protection", "A carriage is not protected from the weather"));
0375             addTagDescription(m_description, QStringLiteral("heating"), QStringLiteral("yes"), tr("Is heated", "A carriage is heated"));
0376             addTagDescription(m_description, QStringLiteral("heating"), QStringLiteral("no"), tr("Not heated", "A carriage is not heated"));
0377             addTagDescription(m_description, QStringLiteral("bicycle"), QStringLiteral("yes"), tr("Bicycle transportation possible", "Bicycles can be transported"));
0378             addTagDescription(m_description, QStringLiteral("bicycle"), QStringLiteral("summer"), tr("Bicycle transportation only in summer", "Bicycles can only be transported in summer"));
0379             addTagDescription(m_description, QStringLiteral("bicycle"), QStringLiteral("no"), tr("Bicycle transportation impossible", "Bicyles cannot be transported"));
0380         } else if (category >= GeoDataPlacemark::PisteDownhill && category <= GeoDataPlacemark::PisteSkiJump) {
0381             addTagDescription(m_description, QStringLiteral("lit"), QStringLiteral("yes"), tr("Lit at night"));
0382             addTagDescription(m_description, QStringLiteral("piste:lit"), QStringLiteral("yes"), tr("Lit in winter"));
0383             addTagDescription(m_description, QStringLiteral("gladed"), QStringLiteral("yes"), tr("Contains trees", "A ski piste with trees (gladed)"));
0384             addTagDescription(m_description, QStringLiteral("patrolled"), QStringLiteral("no"), tr("Not patrolled"));
0385 
0386             addTagDescription(m_description, QStringLiteral("piste:grooming"), QStringLiteral("classic"), tr("Groomed for classic style nordic or downhill skiing"));
0387             addTagDescription(m_description, QStringLiteral("piste:grooming"), QStringLiteral("mogul"), tr("Mogul piste"));
0388             addTagDescription(m_description, QStringLiteral("piste:grooming"), QStringLiteral("skating"), tr("Groomed for free style or skating"));
0389             addTagDescription(m_description, QStringLiteral("piste:grooming"), QStringLiteral("classic;skating"), tr("Groomed for classic and free style skiing"));
0390             addTagDescription(m_description, QStringLiteral("piste:grooming"), QStringLiteral("classic+skating"), tr("Groomed for classic and free style skiing"));
0391             addTagDescription(m_description, QStringLiteral("piste:grooming"), QStringLiteral("scooter"), tr("Groomed by a smaller snowmobile"));
0392             addTagDescription(m_description, QStringLiteral("piste:grooming"), QStringLiteral("backcountry"), tr("Unmarked piste (backcountry touring)"));
0393         }
0394 
0395         if (category == GeoDataPlacemark::TransportBicycleParking || category == GeoDataPlacemark::TransportMotorcycleParking) {
0396             addTagDescription(m_description, QStringLiteral("covered"), QStringLiteral("yes"), tr("Is covered", "A parking space is covered"));
0397             addTagDescription(m_description, QStringLiteral("covered"), QStringLiteral("no"), tr("Not covered", "A parking space is not covered"));
0398         }
0399 
0400         if (category == GeoDataPlacemark::AmenityRecycling || category == GeoDataPlacemark::AmenityPostBox) {
0401             addTagValue(m_description, QStringLiteral("collection_times"), tr("Collection times %1"), QStringLiteral(", "));
0402         }
0403 
0404         if (category == GeoDataPlacemark::AerialwayStation) {
0405             addTagDescription(m_description, "aerialway:access", "entry", tr("Entry", "Entry station of an aerialway"));
0406             addTagDescription(m_description, "aerialway:access", "exit", tr("Exit", "Exit station of an aerialway"));
0407             addTagDescription(m_description, "aerialway:access", "both", tr("Entry and exit", "Entry and exit station of an aerialway"));
0408             addTagDescription(m_description, "aerialway:access", "no", tr("No entry or exit", "Transit only station of an aerialway"));
0409             addTagDescription(m_description, "aerialway:summer:access", "entry", tr("Entry during summer", "Entry station of an aerialway during summer"));
0410             addTagDescription(m_description, "aerialway:summer:access", "exit", tr("Exit during summer", "Exit station of an aerialway during summer"));
0411             addTagDescription(m_description, "aerialway:summer:access", "both", tr("Entry and exit during summer", "Entry and exit station of an aerialway during summer"));
0412             addTagDescription(m_description, "aerialway:summer:access", "no", tr("No entry or exit during summer", "Transit only station of an aerialway during summer"));
0413         }
0414 
0415         if (category != GeoDataPlacemark::AerialwayStation) {
0416             addTagValue(m_description, QStringLiteral("ele"), tr("Elevation: %1 m"));
0417         }
0418 
0419         addTagDescription(m_description, "access", "customers", tr("Customers only"));
0420         addTagDescription(m_description, QStringLiteral("access"), QStringLiteral("yes"), tr("Accessible by anyone", "The public has an official, legally-enshrined right of access; i.e., it's a right of way"));
0421         addTagDescription(m_description, QStringLiteral("access"), QStringLiteral("private"), tr("Private", "Only with permission of the owner on an individual basis."));
0422         addTagDescription(m_description, QStringLiteral("access"), QStringLiteral("permissive"), tr("Open to general traffic", "Open to general traffic but permission can be revoked by the owner"));
0423         addTagDescription(m_description, QStringLiteral("access"), QStringLiteral("no"), tr("No access", "No access for the general public"));
0424 
0425 
0426         addTagDescription(m_description, QStringLiteral("fee"), QStringLiteral("no"), tr("no fee"));
0427         addTagValue(m_description, QStringLiteral("description"));
0428         addTagValue(m_description, QStringLiteral("old_name"), tr("formerly <i>%1</i>"));
0429 
0430         const int level = m_placemark.osmData().tagValue(QStringLiteral("level")).toInt();
0431         if (level > 2) {
0432             addTagValue(m_description, QStringLiteral("level"), tr("Floor %1", "Positive floor level"));
0433         } else if (level < -2) {
0434             addTagValue(m_description, QStringLiteral("level"), tr("Basement %1", "Negative floor level"));
0435         } else {
0436             addTagDescription(m_description, QStringLiteral("level"), QStringLiteral("2"), tr("Floor 2", "Floor level 2, two levels above ground level"));
0437             addTagDescription(m_description, QStringLiteral("level"), QStringLiteral("1"), tr("Floor 1", "Floor level 1, one level above ground level"));
0438             addTagDescription(m_description, QStringLiteral("level"), QStringLiteral("0"), tr("Ground floor", "Floor level 0"));
0439             addTagDescription(m_description, QStringLiteral("level"), QStringLiteral("-1"), tr("Basement 1", "Floor level -1, one level below ground level"));
0440             addTagDescription(m_description, QStringLiteral("level"), QStringLiteral("-2"), tr("Basement 2", "Floor level -2, two levels below ground level"));
0441         }
0442     }
0443 
0444     return m_description;
0445 }
0446 
0447 QString Placemark::address() const
0448 {
0449     if (m_address.isEmpty()) {
0450         m_address = addressFromOsmData();
0451     }
0452     return m_address;
0453 }
0454 
0455 QString Placemark::website() const
0456 {
0457     if (!m_website.isEmpty()) {
0458         return m_website;
0459     }
0460     auto const tags = QStringList() << "website" << "contact:website" << "facebook" << "contact:facebook" << "url";
0461     for(const QString &tag: tags) {
0462         QString const value = m_placemark.osmData().tagValue(tag);
0463         if (!value.isEmpty()) {
0464             QUrl url = QUrl(value);
0465             if (url.isValid()) {
0466                 if (url.scheme().isEmpty()) {
0467                     m_website = QStringLiteral("http://%1").arg(value);
0468                 } else {
0469                     m_website = value;
0470                 }
0471                 if (!m_website.isEmpty()) {
0472                     return m_website;
0473                 }
0474             }
0475         }
0476     }
0477 
0478     return m_website;
0479 }
0480 
0481 QString Placemark::wikipedia() const
0482 {
0483     if (!m_wikipedia.isEmpty()) {
0484         return m_wikipedia;
0485     }
0486 
0487     // TODO: also support "wikipedia:lang=page title" tags
0488     const QString wikipedia = m_placemark.osmData().tagValue("wikipedia");
0489     if (!wikipedia.isEmpty()) {
0490         // full URL?
0491         if (wikipedia.startsWith(QLatin1String("http://")) ||
0492             wikipedia.startsWith(QLatin1String("https://"))) {
0493             m_wikipedia = wikipedia;
0494         } else {
0495             // match "(lang:)human readable title"
0496             QRegularExpression re("^(?:([a-z]{2,}):)?(.*)$");
0497             QRegularExpressionMatch match = re.match(wikipedia);
0498             QString lang = match.captured(1);
0499             if (lang.isEmpty()) {
0500                 lang = QStringLiteral("en");
0501             }
0502             const QString title = QString::fromLatin1(QUrl::toPercentEncoding(match.captured(2)));
0503 
0504             m_wikipedia = QLatin1String("https://") + lang + QLatin1String(".wikipedia.org/wiki/") + title;
0505         }
0506     }
0507 
0508     return m_wikipedia;
0509 }
0510 
0511 QString Placemark::openingHours() const
0512 {
0513     if (!m_openingHours.isEmpty()) {
0514         return m_openingHours;
0515     }
0516 
0517     addTagValue(m_openingHours, "opening_hours");
0518     return m_openingHours;
0519 }
0520 
0521 QString Placemark::coordinates() const
0522 {
0523     return m_placemark.coordinate().toString(GeoDataCoordinates::Decimal).trimmed();
0524 }
0525 
0526 QString Placemark::wheelchairInfo() const
0527 {
0528     if (!m_wheelchairInfo.isEmpty())
0529         return m_wheelchairInfo;
0530 
0531     addTagDescription(m_wheelchairInfo, QStringLiteral("wheelchair"), QStringLiteral("yes"), tr("Wheelchair accessible"));
0532     addTagDescription(m_wheelchairInfo, QStringLiteral("wheelchair"), QStringLiteral("no"), tr("Wheelchair inaccessible"));
0533     addTagDescription(m_wheelchairInfo, QStringLiteral("wheelchair"), QStringLiteral("limited"), tr("Limited wheelchair accessibility"));
0534     addTagDescription(m_wheelchairInfo, QStringLiteral("wheelchair"), QStringLiteral("designated"), tr("Designated wheelchair access"));
0535 
0536     // Check if there is localized description
0537     auto const & osmData = m_placemark.osmData();
0538     QStringList const uiLanguages = QLocale::system().uiLanguages();
0539     const QString tag = QLatin1String("wheelchair:description:");
0540     for (const QString &uiLanguage: uiLanguages) {
0541         for (auto tagIter = osmData.tagsBegin(), end = osmData.tagsEnd(); tagIter != end; ++tagIter) {
0542             if (tagIter.key().startsWith(tag)) {
0543                 QStringRef const tagLanguage = tagIter.key().midRef(tag.length());
0544                 if (tagLanguage == uiLanguage) {
0545                     append(m_wheelchairInfo, tagIter.value());
0546                     return m_wheelchairInfo;
0547                 }
0548             }
0549         }
0550     }
0551 
0552     addTagValue(m_wheelchairInfo, "wheelchair:description");
0553 
0554     return m_wheelchairInfo;
0555 }
0556 
0557 QString Placemark::wifiAvailable() const
0558 {
0559     if (!m_wifiAvailable.isEmpty()) {
0560         return m_wifiAvailable;
0561     }
0562 
0563     const auto& osmData = m_placemark.osmData();
0564     addTagDescription(m_wifiAvailable, QStringLiteral("internet_access"), QStringLiteral("no"), tr("No public Internet access", "This location does not provide public Internet access"));
0565     addTagDescription(m_wifiAvailable, QStringLiteral("internet_access"), QStringLiteral("yes"), tr("Public Internet access available", "This location provides an unknown type of public Internet access."));
0566 
0567     if (osmData.containsTag(QStringLiteral("internet_access:fee"), QStringLiteral("yes"))) {
0568         addTagDescription(m_wifiAvailable, QStringLiteral("internet_access"), QStringLiteral("wlan"), tr("Charged public wifi available", "Public wireless Internet access is available here for a fee."));
0569     } else if (osmData.containsTag(QStringLiteral("internet_access:fee"), QStringLiteral("no"))) {
0570         addTagDescription(m_wifiAvailable, QStringLiteral("internet_access"), QStringLiteral("wlan"), tr("Free public wifi available", "Public wireless Internet access is available here for no cost."));
0571     } else {
0572         addTagDescription(m_wifiAvailable, QStringLiteral("internet_access"), QStringLiteral("wlan"), tr("Public wifi available", "Public wireless Internet access is available here."));
0573     }
0574 
0575     if (m_wifiAvailable.isEmpty()) {
0576         addTagDescription(m_wifiAvailable, QStringLiteral("wifi"), QStringLiteral("no"), tr("No public wifi", "Public wifi is not available here."));
0577         addTagDescription(m_wifiAvailable, QStringLiteral("wifi"), QStringLiteral("yes"), tr("Public wifi available", "Public wireless Internet is available here."));
0578         addTagDescription(m_wifiAvailable, QStringLiteral("wifi"), QStringLiteral("free"), tr("Free public wifi available", "Public wireless Internet is available here for no cost."));
0579     }
0580 
0581     return m_wifiAvailable;
0582 }
0583 
0584 QString Placemark::phone() const
0585 {
0586     if (!m_phone.isEmpty()) {
0587         return m_phone;
0588     }
0589 
0590     addTagValue(m_phone, "phone");
0591     return m_phone;
0592 }
0593 
0594 void Placemark::setName(const QString & name)
0595 {
0596     if (m_placemark.displayName() == name) {
0597         return;
0598     }
0599 
0600     m_placemark.setName(name);
0601     emit nameChanged();
0602 }
0603 
0604 RouteRelationModel* Placemark::routeRelationModel()
0605 {
0606     return &m_relationModel;
0607 }
0608 
0609 double Placemark::longitude() const
0610 {
0611     return m_placemark.coordinate().longitude(GeoDataCoordinates::Degree);
0612 }
0613 
0614 double Placemark::latitude() const
0615 {
0616     return m_placemark.coordinate().latitude(GeoDataCoordinates::Degree);
0617 }
0618 
0619 const QStringList &Placemark::tags() const
0620 {
0621     return m_tags;
0622 }
0623 
0624 bool Placemark::addTagValue(QString &target, const QString &key, const QString &format, const QString& separator) const
0625 {
0626     auto const & osmData = m_placemark.osmData();
0627     QString const value = osmData.tagValue(key);
0628     if (!value.isEmpty()) {
0629         QString description = format.isEmpty() ? value : format.arg(value);
0630         description.replace(QLatin1Char(';'), separator);
0631         append(target, description);
0632         return true;
0633     }
0634     return false;
0635 }
0636 
0637 void Placemark::addFirstTagValueOf(QString &target, const QStringList &keys) const
0638 {
0639     for (auto const &key: keys) {
0640         if (addTagValue(target, key)) {
0641             return;
0642         }
0643     }
0644 }
0645 
0646 void Placemark::addTagDescription(QString &target, const QString &key, const QString &value, const QString &description) const
0647 {
0648     auto const & osmData = m_placemark.osmData();
0649     if (osmData.containsTag(key, value)) {
0650         append(target, description);
0651     }
0652 }
0653 
0654 void Placemark::append(QString &target, const QString &value)
0655 {
0656     if (!target.isEmpty()) {
0657         target += QStringLiteral(" · "); // non-latin1
0658     }
0659     target += value;
0660 }
0661 
0662 QString Placemark::addressFromOsmData() const
0663 {
0664 #ifdef HAVE_QT5_POSITIONING
0665     QGeoAddress address;
0666     OsmPlacemarkData const data = m_placemark.osmData();
0667     address.setCountry(data.tagValue("addr:country"));
0668     address.setState(data.tagValue("addr:state"));
0669     address.setCity(data.tagValue("addr:city"));
0670     address.setDistrict(data.tagValue("district"));
0671     address.setPostalCode(data.tagValue("addr:postcode"));
0672     QString const street = data.tagValue("addr:street");
0673     QString const houseNumber = data.tagValue("addr:housenumber");
0674     address.setStreet(formatStreet(street, houseNumber));
0675     return address.text().replace("<br/>", ", ");
0676 #else
0677     return QString();
0678 #endif
0679 }
0680 
0681 QString Placemark::formatStreet(const QString &street, const QString &houseNumber)
0682 {
0683     return houseNumber.isEmpty() ? street : tr("%1 %2",
0684                                                "House number (first argument) and street name (second argument) in an address").arg(houseNumber).arg(street).trimmed();
0685 }
0686 
0687 void Placemark::updateTags()
0688 {
0689     m_tags.clear();
0690     QString const tag = QStringLiteral("%1 = %2");
0691     for (auto iter = m_placemark.osmData().tagsBegin(), end = m_placemark.osmData().tagsEnd(); iter != end; ++iter) {
0692         m_tags << tag.arg(iter.key()).arg(iter.value());
0693     }
0694 }
0695 
0696 void Placemark::updateRelations(const Marble::GeoDataPlacemark &placemark)
0697 {
0698     if (const auto document = (placemark.parent() ? geodata_cast<GeoDataDocument>(placemark.parent()) : nullptr)) {
0699         QVector<const GeoDataRelation*> allRelations;
0700         QSet<const GeoDataRelation*> relevantRelations;
0701         QSet<qint64> placemarkIds;
0702         auto const & osmData = placemark.osmData();
0703         placemarkIds << osmData.oid();
0704         bool searchRelations = true;
0705         for (auto feature: document->featureList()) {
0706             if (const auto relation = geodata_cast<GeoDataRelation>(feature)) {
0707                 allRelations << relation;
0708                 if (relation->memberIds().contains(osmData.oid())) {
0709                     relevantRelations << relation;
0710                     auto const isRoute = relation->osmData().tagValue(QStringLiteral("type")) == QStringLiteral("route");
0711                     searchRelations &= !isRoute;
0712                 }
0713             }
0714         }
0715         if (searchRelations) {
0716             for (auto feature: document->featureList()) {
0717                 if (const auto relation = geodata_cast<GeoDataRelation>(feature)) {
0718                     if (relevantRelations.contains(relation) &&
0719                             relation->osmData().containsTag(QStringLiteral("type"), QStringLiteral("public_transport")) &&
0720                             relation->osmData().containsTag(QStringLiteral("public_transport"), QStringLiteral("stop_area"))) {
0721                         for (auto iter = relation->osmData().relationReferencesBegin(), end = relation->osmData().relationReferencesEnd();
0722                              iter != end; ++iter) {
0723                             if (iter.value() == QStringLiteral("stop") || iter.value() == QStringLiteral("platform")) {
0724                                 placemarkIds << iter.key().id;
0725                             }
0726                         }
0727                     }
0728                 }
0729             }
0730         }
0731         for (auto relation: allRelations) {
0732             if (relation->containsAnyOf(placemarkIds)) {
0733                 relevantRelations << relation;
0734             }
0735         }
0736         m_relationModel.setRelations(relevantRelations);
0737     }
0738 }
0739 
0740 }
0741 
0742 #include "moc_Placemark.cpp"