diff --git a/examples/cpp/marble-game/CountryByFlag.cpp b/examples/cpp/marble-game/CountryByFlag.cpp index 6afa1ab40..aa5b5f5c7 100644 --- a/examples/cpp/marble-game/CountryByFlag.cpp +++ b/examples/cpp/marble-game/CountryByFlag.cpp @@ -1,173 +1,172 @@ // // This file is part of the Marble Virtual Globe. // // This program is free software licensed under the GNU LGPL. You can // find a copy of this license in LICENSE.txt in the top directory of // the source code. // // Copyright 2014 Abhinav Gangwar // // Self #include "CountryByFlag.h" // Qt #include #include #include #include #include #include // Marble #include #include #include #include #include #include #include #include -#include namespace Marble { class CountryByFlagPrivate { public: CountryByFlagPrivate( MarbleWidget *marbleWidget ) : m_marbleWidget( marbleWidget ), m_parent( nullptr ), m_countryNames( nullptr ) { m_continentsAndOceans << QStringLiteral("Asia") << QStringLiteral("Africa") << QStringLiteral("North America") << QStringLiteral("South America") << QStringLiteral("Antarctica") << QStringLiteral("Europe") << QStringLiteral("Australia") << QStringLiteral("Arctic Ocean") << QStringLiteral("Indian Ocean") << QStringLiteral("North Atlantic Ocean") << QStringLiteral("North Pacific Ocean") << QStringLiteral("South Pacific Ocean") << QStringLiteral("South Atlantic Ocean") << QStringLiteral("Southern Ocean"); } MarbleWidget *m_marbleWidget; CountryByFlag *m_parent; /** * Document to store point placemarks which * have country names ( from file "boundaryplacemarks.cache" ) */ GeoDataDocument *m_countryNames; /* * When I select a random placemark form boundaryplacemarks.cache * it may represent a continent. Since there is no flag * for a continent, we will not use this placemark to post question. * This list will help checking whether the placemark chosen to * post question is a continent/ocean . */ QStringList m_continentsAndOceans; }; CountryByFlag::CountryByFlag( MarbleWidget *marbleWidget ) : QObject(), d ( new CountryByFlagPrivate(marbleWidget) ) { d->m_parent = this; } CountryByFlag::~CountryByFlag() { delete d->m_countryNames; delete d; } void CountryByFlag::initiateGame() { /** * First remove the GeoDataDocument, which displays * country names, from map. */ if ( !d->m_countryNames ) { const GeoDataTreeModel *const treeModel = d->m_marbleWidget->model()->treeModel(); for ( int i = 0; i < treeModel->rowCount(); ++i ) { QVariant const data = treeModel->data ( treeModel->index ( i, 0 ), MarblePlacemarkModel::ObjectPointerRole ); GeoDataObject *object = qvariant_cast( data ); Q_ASSERT_X( object, "CountryByFlag::initiateGame", "failed to get valid data from treeModel for GeoDataObject" ); if (auto doc = geodata_cast(object)) { QFileInfo fileInfo( doc->fileName() ); if (fileInfo.fileName() == QLatin1String("boundaryplacemarks.cache")) { d->m_countryNames = doc; break; } } } } if ( d->m_countryNames ) { d->m_countryNames->setVisible( false ); d->m_marbleWidget->model()->treeModel()->updateFeature( d->m_countryNames ); d->m_marbleWidget->centerOn( 23.0, 42.0 ); d->m_marbleWidget->setDistance( 7500 ); d->m_marbleWidget->setHighlightEnabled( false ); emit gameInitialized(); } } void CountryByFlag::postQuestion( QObject *gameObject ) { /** * Find a random placemark */ Q_ASSERT_X( d->m_countryNames, "CountryByFlag::postQuestion", "CountryByFlagPrivate::m_countryNames is NULL" ); QVector countryPlacemarks = d->m_countryNames->placemarkList(); uint randomSeed = uint(QTime::currentTime().msec()); qsrand( randomSeed ); bool found = false; GeoDataPlacemark *placemark = nullptr; QVariantList answerOptions; QString flagPath; while ( !found ) { int randomIndex = qrand()%countryPlacemarks.size(); placemark = countryPlacemarks[randomIndex]; if ( !d->m_continentsAndOceans.contains(placemark->name(), Qt::CaseSensitive) ) { const QString countryCode = placemark->countryCode().toLower(); flagPath = MarbleDirs::path(QLatin1String("flags/flag_") + countryCode + QLatin1String(".svg")); QImage flag = QFile::exists( flagPath ) ? QImage( flagPath ) : QImage(); if ( !flag.isNull() ) { flagPath = QLatin1String("../../../data/flags/flag_") + countryCode + QLatin1String(".svg"); found = true; } } } answerOptions << placemark->name() << countryPlacemarks[qrand()%countryPlacemarks.size()]->name() << countryPlacemarks[qrand()%countryPlacemarks.size()]->name() << countryPlacemarks[qrand()%countryPlacemarks.size()]->name(); // Randomize the options in the list answerOptions for ( int i = 0; i < answerOptions.size(); ++i ) { QVariant option = answerOptions.takeAt( qrand()%answerOptions.size() ); answerOptions.append( option ); } if ( gameObject ) { QMetaObject::invokeMethod( gameObject, "countryByFlagQuestion", Q_ARG(QVariant, QVariant(answerOptions)), Q_ARG(QVariant, QVariant(flagPath)), Q_ARG(QVariant, QVariant(placemark->name())) ); } } } // namespace Marble #include "moc_CountryByFlag.cpp" diff --git a/src/lib/marble/declarative/Placemark.cpp b/src/lib/marble/declarative/Placemark.cpp index c0ca6d5d4..20252ce6f 100644 --- a/src/lib/marble/declarative/Placemark.cpp +++ b/src/lib/marble/declarative/Placemark.cpp @@ -1,748 +1,747 @@ // // This file is part of the Marble Virtual Globe. // // This program is free software licensed under the GNU LGPL. You can // find a copy of this license in LICENSE.txt in the top directory of // the source code. // // Copyright 2011 Dennis Nienhüser // #include "Placemark.h" #ifdef HAVE_QT5_POSITIONING #include #include #endif // HAVE_QT5_POSITIONING #include #include "GeoDataStyle.h" #include "GeoDataIconStyle.h" -#include "GeoDataTypes.h" #include "GeoDataDocument.h" namespace Marble { Placemark::Placemark(QObject *parent ) : QObject( parent ) { // nothing to do } void Placemark::setGeoDataPlacemark( const Marble::GeoDataPlacemark &placemark ) { m_placemark = placemark; m_address = QString(); m_description = QString(); m_website = QString(); m_wikipedia = QString(); m_openingHours = QString(); m_wheelchairInfo = QString(); m_wifiAvailable = QString(); m_phone = QString(); updateTags(); updateRelations(placemark); emit coordinatesChanged(); emit nameChanged(); emit descriptionChanged(); emit addressChanged(); emit websiteChanged(); emit wikipediaChanged(); emit openingHoursChanged(); emit wheelchairInfoChanged(); emit wifiAvailabilityChanged(); emit phoneChanged(); emit tagsChanged(); } Marble::GeoDataPlacemark & Placemark::placemark() { return m_placemark; } const GeoDataPlacemark &Placemark::placemark() const { return m_placemark; } QString Placemark::name() const { return m_placemark.displayName(); } QString Placemark::description() const { if (m_description.isEmpty()) { auto const category = m_placemark.visualCategory(); m_description = m_placemark.categoryName(); if (category == GeoDataPlacemark::AccomodationHotel || category == GeoDataPlacemark::FoodRestaurant) { QString const stars = m_placemark.osmData().tagValue(QStringLiteral("stars")); if (!stars.isEmpty()) { bool hasStars; int const numStars = stars.mid(0, 1).toInt(&hasStars); if (hasStars) { m_description += QString(' ') + QString("★").repeated(numStars) + stars.mid(1); } else { addTagValue(m_description, QStringLiteral("stars")); } } addFirstTagValueOf(m_description, QStringList() << "brand" << "operator"); } if ((category >= GeoDataPlacemark::AccomodationHostel && category <= GeoDataPlacemark::AccomodationGuestHouse) || category == GeoDataPlacemark::HealthHospital) { int const rooms = m_placemark.osmData().tagValue(QStringLiteral("rooms")).toInt(); if (rooms > 0) { //~ singular %n room //~ plural %n rooms addTagValue(m_description, QStringLiteral("rooms"), tr("%n rooms", nullptr, rooms)); } int const beds = m_placemark.osmData().tagValue(QStringLiteral("beds")).toInt(); if (beds > 0) { //~ singular %n bed //~ plural %n beds addTagValue(m_description, QStringLiteral("beds"), tr("%n beds", nullptr, beds)); } } if (category == GeoDataPlacemark::TransportParking || category == GeoDataPlacemark::TransportBicycleParking || category == GeoDataPlacemark::TransportMotorcycleParking) { addTagValue(m_description, QStringLiteral("capacity"), tr("%1 parking spaces")); addTagValue(m_description, QStringLiteral("maxstay"), tr("Maximum parking time %1")); } if (category >= GeoDataPlacemark::FoodBar && category <= GeoDataPlacemark::FoodRestaurant) { if (category != GeoDataPlacemark::FoodRestaurant) { addFirstTagValueOf(m_description, QStringList() << "brand" << "operator"); } else { // Do nothing, already added in stars section above } addTagValue(m_description, "cuisine"); addTagValue(m_description, "brewery"); addTagDescription(m_description, "self_service", "yes", "Self Service"); addTagDescription(m_description, "takeaway", "yes", "Take Away"); addTagDescription(m_description, "outdoor_seating", "yes", "Outdoor Seating"); addTagDescription(m_description, "ice_cream", "yes", "Ice Cream"); addTagDescription(m_description, "smoking", "dedicated", "Smoking (dedicated)"); addTagDescription(m_description, "smoking", "yes", "Smoking allowed"); addTagDescription(m_description, "smoking", "separated", "Smoking (separated)"); addTagDescription(m_description, "smoking", "isolated", "Smoking (isolated)"); addTagDescription(m_description, "smoking", "no", "No smoking"); addTagDescription(m_description, "smoking", "outside", "Smoking (outside)"); addTagDescription(m_description, "smoking:outside", "yes", "Smoking (outside)"); addTagDescription(m_description, "smoking:outside", "separated", "Smoking (outside separated)"); addTagDescription(m_description, "smoking:outside", "no", "No smoking outside"); } else if (category >= GeoDataPlacemark::ShopBeverages && category <= GeoDataPlacemark::Shop) { addFirstTagValueOf(m_description, QStringList() << "brand" << "operator"); addTagValue(m_description, "clothes"); addTagValue(m_description, "designation"); if (category == GeoDataPlacemark::ShopButcher) { addTagValue(m_description, "butcher"); } else if (category == GeoDataPlacemark::ShopCopy) { addTagDescription(m_description, QStringLiteral("service:computer"), QStringLiteral("yes"), tr("Computers available", "A copy shop provides computers for customer use")); addTagDescription(m_description, QStringLiteral("service:computer"), QStringLiteral("no"), tr("No computers available", "A copy shop does not provide computers for customer use")); addTagDescription(m_description, QStringLiteral("service:copy"), QStringLiteral("yes"), tr("Photocopying service", "A copy shop provides photocopying service")); addTagDescription(m_description, QStringLiteral("service:copy"), QStringLiteral("no"), tr("No photocopying service", "A copy shop does not provide photocopying service")); addTagDescription(m_description, QStringLiteral("service:scan"), QStringLiteral("yes"), tr("Digital scanning", "A copy shop provides a service for scanning documents into digital files")); 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")); addTagDescription(m_description, QStringLiteral("service:fax"), QStringLiteral("yes"), tr("Fax service", "A copy shop provides a service to send documents through fax")); 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")); addTagDescription(m_description, QStringLiteral("service:phone"), QStringLiteral("yes"), tr("Phone service", "A copy shop provides a paid service to make phone calls")); 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")); addTagDescription(m_description, QStringLiteral("service:print"), QStringLiteral("yes"), tr("Digital printing", "A copy shop provides services to print paper documents from digital files")); 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")); 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")); 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")); addTagDescription(m_description, QStringLiteral("service:prepress"), QStringLiteral("yes"), tr("Press printing assistance", "A copy shop provides help with preparing special printing techniques")); 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")); addTagDescription(m_description, QStringLiteral("service:self"), QStringLiteral("yes"), tr("Self service", "A copy shop provides individual copy machines for self-service")); addTagDescription(m_description, QStringLiteral("service:self"), QStringLiteral("no"), tr(" No self service", "A copy shop does not provide individual machines for self-service")); } else if (category == GeoDataPlacemark::ShopDeli) { addTagDescription(m_description, QStringLiteral("organic"), QStringLiteral("yes"), tr("Sells organic food", "A deli that sells organic food")); addTagDescription(m_description, QStringLiteral("organic"), QStringLiteral("no"), tr("Does not sell organic food", "A deli that does not sell organic food")); addTagDescription(m_description, QStringLiteral("organic"), QStringLiteral("only"), tr("Only sells organic food", "A deli that only sells organic food")); addTagDescription(m_description, QStringLiteral("diet:gluten_free"), QStringLiteral("yes"), tr("Sells gluten free food", "A deli that sells gluten free food")); 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")); addTagDescription(m_description, QStringLiteral("diet:gluten_free"), QStringLiteral("only"), tr("Only sells gluten free food", "A deli that only sells gluten free food")); addTagDescription(m_description, QStringLiteral("diet:lactose_free"), QStringLiteral("yes"), tr("Sells lactose free food", "A deli that sells lactose free food")); 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")); addTagDescription(m_description, QStringLiteral("diet:lactose_free"), QStringLiteral("only"), tr("Only sells lactose free food", "A deli that only sells lactose free food")); } else if (category == GeoDataPlacemark::ShopTobacco) { addTagDescription(m_description, QStringLiteral("lottery"), QStringLiteral("yes"), tr("Sells lottery tickets", "A tobacco shop that also sells lottery tickets")); addTagDescription(m_description, QStringLiteral("stamps"), QStringLiteral("yes"), tr("Sells revenue stamps", "A tobacco shop that also sells revenue stamps")); addTagDescription(m_description, QStringLiteral("salt"), QStringLiteral("yes"), tr("Sells salt", "A tobacco shop that also sells salt")); } } else if (category == GeoDataPlacemark::TransportBusStop) { addTagValue(m_description, "network"); addTagValue(m_description, "operator"); addTagValue(m_description, "ref"); } else if (category == GeoDataPlacemark::TransportCarShare) { addTagValue(m_description, "network"); addTagValue(m_description, "operator"); } else if (category == GeoDataPlacemark::TransportRentalBicycle || category == GeoDataPlacemark::TransportRentalCar || category == GeoDataPlacemark::TransportRentalSki) { addFirstTagValueOf(m_description, QStringList() << "brand" << "operator"); } else if (category == GeoDataPlacemark::TransportFuel) { addFirstTagValueOf(m_description, QStringList() << "brand" << "operator"); addTagDescription(m_description, "fuel:diesel", "yes", tr("Diesel")); addTagDescription(m_description, "fuel:biodiesel", "yes", tr("Biodiesel")); addTagDescription(m_description, "fuel:octane_91", "yes", tr("Octane 91")); addTagDescription(m_description, "fuel:octane_95", "yes", tr("Octane 95")); addTagDescription(m_description, "fuel:octane_98", "yes", tr("Octane 98")); addTagDescription(m_description, "fuel:octane_100", "yes", tr("Octane 100")); addTagDescription(m_description, "fuel:e10", "yes", tr("E10")); addTagDescription(m_description, "fuel:lpg", "yes", tr("LPG")); } else if (category == GeoDataPlacemark::NaturalTree) { addTagValue(m_description, "species:en"); addTagValue(m_description, "genus:en"); addTagValue(m_description, "leaf_type"); } else if (category == GeoDataPlacemark::NaturalCave){ addTagValue(m_description, "cave:ref"); } else if (category == GeoDataPlacemark::AmenityRecycling) { addTagDescription(m_description, QStringLiteral("recycling:batteries"), "yes", tr("Batteries")); addTagDescription(m_description, QStringLiteral("recycling:clothes"), "yes", tr("Clothes")); addTagDescription(m_description, QStringLiteral("recycling:glass"), "yes", tr("Glass")); addTagDescription(m_description, QStringLiteral("recycling:glass_bottles"), "yes", tr("Glass bottles")); addTagDescription(m_description, QStringLiteral("recycling:green_waste"), "yes", tr("Green waste")); addTagDescription(m_description, QStringLiteral("recycling:garden_waste"), "yes", tr("Garden waste")); addTagDescription(m_description, QStringLiteral("recycling:electrical_items"), "yes", tr("Electrical items")); addTagDescription(m_description, QStringLiteral("recycling:metal"), "yes", tr("Metal")); addTagDescription(m_description, QStringLiteral("recycling:mobile_phones"), "yes", tr("Mobile phones")); addTagDescription(m_description, QStringLiteral("recycling:newspaper"), "yes", tr("Newspaper")); addTagDescription(m_description, QStringLiteral("recycling:paint"), "yes", tr("Paint")); addTagDescription(m_description, QStringLiteral("recycling:paper"), "yes", tr("Paper")); addTagDescription(m_description, QStringLiteral("recycling:paper_packaging"), "yes", tr("Paper packaging")); addTagDescription(m_description, QStringLiteral("recycling:PET"), "yes", tr("PET")); addTagDescription(m_description, QStringLiteral("recycling:plastic"), "yes", tr("Plastic")); addTagDescription(m_description, QStringLiteral("recycling:plastic_bags"), "yes", tr("Plastic bags")); addTagDescription(m_description, QStringLiteral("recycling:plastic_bottles"), "yes", tr("Plastic bottles")); addTagDescription(m_description, QStringLiteral("recycling:plastic_packaging"), "yes", tr("Plastic packaging")); addTagDescription(m_description, QStringLiteral("recycling:polyester"), "yes", tr("Polyester")); addTagDescription(m_description, QStringLiteral("recycling:tyres"), "yes", tr("Tyres")); addTagDescription(m_description, QStringLiteral("recycling:waste"), "yes", tr("Waste")); addTagDescription(m_description, QStringLiteral("recycling:white_goods"), "yes", tr("White goods")); addTagDescription(m_description, QStringLiteral("recycling:wood"), "yes", tr("Wood")); } else if (category == GeoDataPlacemark::NaturalVolcano) { addTagDescription(m_description, QStringLiteral("volcano:status"), QStringLiteral("active"), tr("Active", "An active volcano")); addTagDescription(m_description, QStringLiteral("volcano:status"), QStringLiteral("dormant"), tr("Dormant", "A dormant volcano that will erupt at some point in the future.")); 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.")); addTagDescription(m_description, QStringLiteral("volcano:type"), QStringLiteral("stratovolcano"), tr("Stratovolcano")); addTagDescription(m_description, QStringLiteral("volcano:type"), QStringLiteral("shield"), tr("Shield volcano")); addTagDescription(m_description, QStringLiteral("volcano:type"), QStringLiteral("scoria"), tr("Scoria cone", "A scoria cone volcano.")); } else if (category == GeoDataPlacemark::HealthDoctors) { addTagDescription(m_description, QStringLiteral("healthcare"), QStringLiteral("alternative"), tr("Alternative medicine")); addTagDescription(m_description, QStringLiteral("healthcare"), QStringLiteral("audiologist"), tr("Audiologist")); addTagDescription(m_description, QStringLiteral("healthcare"), QStringLiteral("blood_bank"), tr("Blood bank")); addTagDescription(m_description, QStringLiteral("healthcare"), QStringLiteral("blood_donation"), tr("Blood donation")); addTagDescription(m_description, QStringLiteral("healthcare"), QStringLiteral("centre"), tr("Medical center")); addTagDescription(m_description, QStringLiteral("healthcare"), QStringLiteral("clinic"), tr("Clinic")); addTagDescription(m_description, QStringLiteral("healthcare"), QStringLiteral("dentist"), tr("Dentist")); addTagDescription(m_description, QStringLiteral("healthcare"), QStringLiteral("doctor"), tr("Medical practitioner")); addTagDescription(m_description, QStringLiteral("healthcare"), QStringLiteral("hospital"), tr("Hospital")); addTagDescription(m_description, QStringLiteral("healthcare"), QStringLiteral("midwife"), tr("Midwife")); addTagDescription(m_description, QStringLiteral("healthcare"), QStringLiteral("optometrist"), tr("Optometrist")); addTagDescription(m_description, QStringLiteral("healthcare"), QStringLiteral("physiotherapist"), tr("Physiotherapist")); addTagDescription(m_description, QStringLiteral("healthcare"), QStringLiteral("podiatrist"), tr("Podiatrist")); addTagDescription(m_description, QStringLiteral("healthcare"), QStringLiteral("psychotherapist"), tr("Psychotherapist")); addTagDescription(m_description, QStringLiteral("healthcare"), QStringLiteral("rehabilitation"), tr("Rehabilitation")); addTagDescription(m_description, QStringLiteral("healthcare"), QStringLiteral("speech_therapist"), tr("Speech therapist")); addTagValue(m_description, QStringLiteral("healthcare:speciality")); } else if (category == GeoDataPlacemark::AmenityBench) { int const seats = m_placemark.osmData().tagValue(QStringLiteral("seats")).toInt(); if (seats > 0) { //~ singular %n seat //~ plural %n seats addTagValue(m_description, QStringLiteral("seats"), tr("%n seats", "number of seats a bench provides", seats)); } addTagValue(m_description, QStringLiteral("material")); addTagDescription(m_description, QStringLiteral("backrest"), QStringLiteral("yes"), tr("Has backrest", "A bench provides a backrest to lean against")); addTagDescription(m_description, QStringLiteral("backrest"), QStringLiteral("no"), tr("No backrest", "A bench provides no backrest to lean against")); } else if (category == GeoDataPlacemark::AmenityWasteBasket) { addTagValue(m_description, QStringLiteral("waste")); } else if (category == GeoDataPlacemark::TransportSpeedCamera) { addTagValue(m_description, QStringLiteral("maxspeed"), tr("%1 km/h")); addTagValue(m_description, "ref"); } else if (category == GeoDataPlacemark::TransportParking) { addTagDescription(m_description, QStringLiteral("supervised"), QStringLiteral("yes"), tr("Is supervised", "Parking spaces are supervised by guards")); addTagDescription(m_description, QStringLiteral("supervised"), QStringLiteral("no"), tr("Not supervised", "Parking spaces are not supervised by guards")); int const disabledSpaces = m_placemark.osmData().tagValue(QStringLiteral("capacity:disabled")).toInt(); if (disabledSpaces > 0) { addTagValue(m_description, QStringLiteral("capacity:disabled"), tr("%1 disabled spaces", "Parking spaces")); } int const womenSpaces = m_placemark.osmData().tagValue(QStringLiteral("capacity:women")).toInt(); if (womenSpaces > 0) { addTagValue(m_description, QStringLiteral("capacity:women"), tr("%1 women spaces", "Parking spaces")); } int const parentSpaces = m_placemark.osmData().tagValue(QStringLiteral("capacity:parent")).toInt(); if (parentSpaces > 0) { addTagValue(m_description, QStringLiteral("capacity:parent"), tr("%1 parent and child spaces", "Parking spaces")); } int const electricChargers = m_placemark.osmData().tagValue(QStringLiteral("capacity:charging")).toInt(); if (electricChargers > 0) { addTagValue(m_description, QStringLiteral("capacity:charging"), tr("%1 spaces with electric chargers", "Parking spaces")); } } else if (category == GeoDataPlacemark::TransportBicycleParking) { addTagDescription(m_description, QStringLiteral("surveillance"), QStringLiteral("outdoor"), tr("Has outdoor surveillance", "A parking space has outdoor surveillance")); addTagDescription(m_description, QStringLiteral("surveillance"), QStringLiteral("indoor"), tr("Has indoor surveillance", "A parking space has indoor surveillance")); addTagDescription(m_description, QStringLiteral("surveillance"), QStringLiteral("public"), tr("Has public surveillance", "A parking space has public surveillance")); } else if (category == GeoDataPlacemark::TourismWildernessHut) { addTagDescription(m_description, QStringLiteral("shower"), QStringLiteral("yes"), tr("Has shower", "A hut provides showers inside or aside")); addTagDescription(m_description, QStringLiteral("shower"), QStringLiteral("no"), tr("Has no shower", "A hut does not provide showers inside or aside")); addTagDescription(m_description, QStringLiteral("mattress"), QStringLiteral("yes"), tr("Has mattress", "A hut provides mattress")); addTagDescription(m_description, QStringLiteral("mattress"), QStringLiteral("no"), tr("Has no mattress", "A hut does not provide mattress")); addTagDescription(m_description, QStringLiteral("drinking_water"), QStringLiteral("yes"), tr("Has water", "Water is available inside or aside")); addTagDescription(m_description, QStringLiteral("drinking_water"), QStringLiteral("no"), tr("Has no water", "Water is not available inside nor aside")); addTagDescription(m_description, QStringLiteral("toilets"), QStringLiteral("yes"), tr("Has toilets", "A hut provides toilets")); addTagDescription(m_description, QStringLiteral("toilets"), QStringLiteral("no"), tr("Has no toilets", "A hut does not provide toilets")); addTagDescription(m_description, QStringLiteral("reservation"), QStringLiteral("yes"), tr("Reservation is possible")); addTagDescription(m_description, QStringLiteral("reservation"), QStringLiteral("no"), tr("No reservation possible")); addTagDescription(m_description, QStringLiteral("reservation"), QStringLiteral("required"), tr("Reservation is required")); addTagDescription(m_description, QStringLiteral("reservation"), QStringLiteral("recommended"), tr("Reservation is recommended", "You should make reservation")); addTagDescription(m_description, QStringLiteral("reservation"), QStringLiteral("members_only"), tr("Only for members", "Reservation is only possible for members of the organisation running the hut")); } else if (category == GeoDataPlacemark::TourismArtwork) { addTagValue(m_description, QStringLiteral("artist_name"), tr("By %1")); } else if (category == GeoDataPlacemark::AmenityChargingStation) { addTagValue(m_description, QStringLiteral("capacity"), tr("%1 vehicles")); addTagValue(m_description, QStringLiteral("amperage"), tr("%1 ampere")); addTagValue(m_description, QStringLiteral("voltage"), tr("%1 volt")); addTagDescription(m_description, QStringLiteral("socket"), QStringLiteral("cee_blue"), tr("%1 blue CEE sockets")); addTagDescription(m_description, QStringLiteral("socket"), QStringLiteral("cee_red_16a"), tr("%1 red CEE sockets (16 A)")); addTagDescription(m_description, QStringLiteral("socket"), QStringLiteral("cee_red_32a"), tr("%1 red CEE sockets (32 A)")); addTagDescription(m_description, QStringLiteral("socket"), QStringLiteral("cee_red_64a"), tr("%1 red CEE sockets (64 A)")); addTagDescription(m_description, QStringLiteral("socket"), QStringLiteral("cee_red_125a"), tr("%1 red CEE sockets (125 A)")); addTagDescription(m_description, QStringLiteral("socket"), QStringLiteral("nema_5_15"), tr("%1 NEMA-5-15P plugs")); addTagDescription(m_description, QStringLiteral("socket"), QStringLiteral("typeb"), tr("%1 NEMA-5-15P plugs")); addTagDescription(m_description, QStringLiteral("socket"), QStringLiteral("nema_5_20"), tr("%1 NEMA-5-20P plugs")); addTagDescription(m_description, QStringLiteral("socket"), QStringLiteral("nema_14_30"), tr("%1 NEMA 14-30 sockets")); addTagDescription(m_description, QStringLiteral("socket"), QStringLiteral("nema_14_50"), tr("%1 NEMA 14-50 sockets")); addTagDescription(m_description, QStringLiteral("socket"), QStringLiteral("schuko"), tr("%1 Schuko sockets")); addTagDescription(m_description, QStringLiteral("socket"), QStringLiteral("bs1363"), tr("%1 BS 1363 sockets")); addTagDescription(m_description, QStringLiteral("socket"), QStringLiteral("type1"), tr("%1 Type 1 plugs")); addTagDescription(m_description, QStringLiteral("socket"), QStringLiteral("type1_combo"), tr("%1 Type 1 combo plugs")); addTagDescription(m_description, QStringLiteral("socket"), QStringLiteral("type2"), tr("%1 Type 2 sockets")); addTagDescription(m_description, QStringLiteral("socket"), QStringLiteral("type2_combo"), tr("%1 Type 2 combo sockets")); addTagDescription(m_description, QStringLiteral("socket"), QStringLiteral("type3"), tr("%1 Type 3 sockets")); addTagDescription(m_description, QStringLiteral("socket"), QStringLiteral("chademo"), tr("%1 CHAdeMO plugs")); addTagDescription(m_description, QStringLiteral("socket"), QStringLiteral("magne_charge"), tr("%1 Magne Charge plugs")); addTagDescription(m_description, QStringLiteral("socket"), QStringLiteral("tesla_standard"), tr("%1 Tesla standard plugs")); addTagDescription(m_description, QStringLiteral("socket"), QStringLiteral("tesla_supercharger"), tr("%1 Tesla standard plugs (Supercharger)")); addTagDescription(m_description, QStringLiteral("socket"), QStringLiteral("tesla_roadster"), tr("%1 Tesla roadster plugs")); } else if (category == GeoDataPlacemark::AmenityCarWash) { addTagValue(m_description, QStringLiteral("maxwidth"), tr("Maximum vehicle width: %1")); addTagValue(m_description, QStringLiteral("maxheight"), tr("Maximum vehicle height: %1")); addTagDescription(m_description, QStringLiteral("self_service"), QStringLiteral("yes"), tr("Self-service")); addTagDescription(m_description, QStringLiteral("self_service"), QStringLiteral("no"), tr("No self-service")); addTagDescription(m_description, QStringLiteral("automated"), QStringLiteral("yes"), tr("Automated")); addTagDescription(m_description, QStringLiteral("automated"), QStringLiteral("no"), tr("Manual")); } else if (category == GeoDataPlacemark::AmenitySocialFacility) { addTagDescription(m_description, QStringLiteral("social_facility"), QStringLiteral("group_home"), tr("Group home")); addTagDescription(m_description, QStringLiteral("social_facility"), QStringLiteral("nursing_home"), tr("Nursing home")); 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")); addTagDescription(m_description, QStringLiteral("social_facility"), QStringLiteral("day_care"), tr("Nursing services on a daily basis")); addTagDescription(m_description, QStringLiteral("social_facility"), QStringLiteral("shelter"), tr("Shelter")); addTagDescription(m_description, QStringLiteral("social_facility"), QStringLiteral("ambulatory_care"), tr("Ambulatory care")); addTagDescription(m_description, QStringLiteral("social_facility"), QStringLiteral("outreach"), tr("Social welfare services")); addTagDescription(m_description, QStringLiteral("social_facility"), QStringLiteral("workshop"), tr("Employment and workshops for offenders and people with disabilities")); addTagDescription(m_description, QStringLiteral("social_facility"), QStringLiteral("food_bank"), tr("Pre-packaged food for free or below market price")); addTagDescription(m_description, QStringLiteral("social_facility"), QStringLiteral("soup_kitchen"), tr("Prepared meals for free or below market price")); addTagDescription(m_description, QStringLiteral("social_facility"), QStringLiteral("dairy_kitchen"), tr("Free dairy food (subject to local regulations)")); addTagDescription(m_description, QStringLiteral("social_facility:for"), QStringLiteral("abused"), tr("For abused")); addTagDescription(m_description, QStringLiteral("social_facility:for"), QStringLiteral("child"), tr("For children")); addTagDescription(m_description, QStringLiteral("social_facility:for"), QStringLiteral("disabled"), tr("For people with physical disabilities")); addTagDescription(m_description, QStringLiteral("social_facility:for"), QStringLiteral("diseased"), tr("For those who suffer of a disease")); addTagDescription(m_description, QStringLiteral("social_facility:for"), QStringLiteral("drug_addicted"), tr("For drug-addicted")); addTagDescription(m_description, QStringLiteral("social_facility:for"), QStringLiteral("homeless"), tr("For homeless")); addTagDescription(m_description, QStringLiteral("social_facility:for"), QStringLiteral("juvenile"), tr("For juvenile")); addTagDescription(m_description, QStringLiteral("social_facility:for"), QStringLiteral("mental_health"), tr("For those with mental/psychological problems")); addTagDescription(m_description, QStringLiteral("social_facility:for"), QStringLiteral("migrant"), tr("For migrants")); addTagDescription(m_description, QStringLiteral("social_facility:for"), QStringLiteral("orphan"), tr("For orphans")); addTagDescription(m_description, QStringLiteral("social_facility:for"), QStringLiteral("senior"), tr("For elder people")); addTagDescription(m_description, QStringLiteral("social_facility:for"), QStringLiteral("underprivileged"), tr("For poor or disadvantaged people")); addTagDescription(m_description, QStringLiteral("social_facility:for"), QStringLiteral("unemployed"), tr("For unemployed")); addTagDescription(m_description, QStringLiteral("social_facility:for"), QStringLiteral("victim"), tr("For victims of crimes")); } else if (category == GeoDataPlacemark::HistoricMemorial) { addTagValue(m_description, QStringLiteral("inscription"), tr("Inscription: %1")); } else if (category >= GeoDataPlacemark::AerialwayCableCar && category <= GeoDataPlacemark::AerialwayGoods) { addTagValue(m_description, QStringLiteral("occupancy"), tr("%1 places per carriage")); addTagValue(m_description, QStringLiteral("capacity"), tr("%1 people per hour")); addTagValue(m_description, QStringLiteral("duration"), tr("%1 minutes")); addTagDescription(m_description, QStringLiteral("bubble"), QStringLiteral("yes"), tr("Has weather protection", "A carriage is protected from the weather")); addTagDescription(m_description, QStringLiteral("bubble"), QStringLiteral("no"), tr("No weather protection", "A carriage is not protected from the weather")); addTagDescription(m_description, QStringLiteral("heating"), QStringLiteral("yes"), tr("Is heated", "A carriage is heated")); addTagDescription(m_description, QStringLiteral("heating"), QStringLiteral("no"), tr("Not heated", "A carriage is not heated")); addTagDescription(m_description, QStringLiteral("bicycle"), QStringLiteral("yes"), tr("Bicycle transportation possible", "Bicycles can be transported")); addTagDescription(m_description, QStringLiteral("bicycle"), QStringLiteral("summer"), tr("Bicycle transportation only in summer", "Bicycles can only be transported in summer")); addTagDescription(m_description, QStringLiteral("bicycle"), QStringLiteral("no"), tr("Bicycle transportation impossible", "Bicyles cannot be transported")); } else if (category >= GeoDataPlacemark::PisteDownhill && category <= GeoDataPlacemark::PisteSkiJump) { addTagDescription(m_description, QStringLiteral("lit"), QStringLiteral("yes"), tr("Lit at night")); addTagDescription(m_description, QStringLiteral("piste:lit"), QStringLiteral("yes"), tr("Lit in winter")); addTagDescription(m_description, QStringLiteral("gladed"), QStringLiteral("yes"), tr("Contains trees", "A ski piste with trees (gladed)")); addTagDescription(m_description, QStringLiteral("patrolled"), QStringLiteral("no"), tr("Not patrolled")); addTagDescription(m_description, QStringLiteral("piste:grooming"), QStringLiteral("classic"), tr("Groomed for classic style nordic or downhill skiing")); addTagDescription(m_description, QStringLiteral("piste:grooming"), QStringLiteral("mogul"), tr("Mogul piste")); addTagDescription(m_description, QStringLiteral("piste:grooming"), QStringLiteral("skating"), tr("Groomed for free style or skating")); addTagDescription(m_description, QStringLiteral("piste:grooming"), QStringLiteral("classic;skating"), tr("Groomed for classic and free style skiing")); addTagDescription(m_description, QStringLiteral("piste:grooming"), QStringLiteral("classic+skating"), tr("Groomed for classic and free style skiing")); addTagDescription(m_description, QStringLiteral("piste:grooming"), QStringLiteral("scooter"), tr("Groomed by a smaller snowmobile")); addTagDescription(m_description, QStringLiteral("piste:grooming"), QStringLiteral("backcountry"), tr("Unmarked piste (backcountry touring)")); } if (category == GeoDataPlacemark::TransportBicycleParking || category == GeoDataPlacemark::TransportMotorcycleParking) { addTagDescription(m_description, QStringLiteral("covered"), QStringLiteral("yes"), tr("Is covered", "A parking space is covered")); addTagDescription(m_description, QStringLiteral("covered"), QStringLiteral("no"), tr("Not covered", "A parking space is not covered")); } if (category == GeoDataPlacemark::AmenityRecycling || category == GeoDataPlacemark::AmenityPostBox) { addTagValue(m_description, QStringLiteral("collection_times"), tr("Collection times %1"), QStringLiteral(", ")); } if (category == GeoDataPlacemark::AerialwayStation) { addTagDescription(m_description, "aerialway:access", "entry", tr("Entry", "Entry station of an aerialway")); addTagDescription(m_description, "aerialway:access", "exit", tr("Exit", "Exit station of an aerialway")); addTagDescription(m_description, "aerialway:access", "both", tr("Entry and exit", "Entry and exit station of an aerialway")); addTagDescription(m_description, "aerialway:access", "no", tr("No entry or exit", "Transit only station of an aerialway")); addTagDescription(m_description, "aerialway:summer:access", "entry", tr("Entry during summer", "Entry station of an aerialway during summer")); addTagDescription(m_description, "aerialway:summer:access", "exit", tr("Exit during summer", "Exit station of an aerialway during summer")); addTagDescription(m_description, "aerialway:summer:access", "both", tr("Entry and exit during summer", "Entry and exit station of an aerialway during summer")); addTagDescription(m_description, "aerialway:summer:access", "no", tr("No entry or exit during summer", "Transit only station of an aerialway during summer")); } if (category != GeoDataPlacemark::AerialwayStation) { addTagValue(m_description, QStringLiteral("ele"), tr("Elevation: %1 m")); } addTagDescription(m_description, "access", "customers", tr("Customers only")); 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")); addTagDescription(m_description, QStringLiteral("access"), QStringLiteral("private"), tr("Private", "Only with permission of the owner on an individual basis.")); addTagDescription(m_description, QStringLiteral("access"), QStringLiteral("permissive"), tr("Open to general traffic", "Open to general traffic but permission can be revoked by the owner")); addTagDescription(m_description, QStringLiteral("access"), QStringLiteral("no"), tr("No access", "No access for the general public")); addTagDescription(m_description, QStringLiteral("fee"), QStringLiteral("no"), tr("no fee")); addTagValue(m_description, QStringLiteral("description")); addTagValue(m_description, QStringLiteral("old_name"), tr("formerly %1")); const int level = m_placemark.osmData().tagValue(QStringLiteral("level")).toInt(); if (level > 2) { addTagValue(m_description, QStringLiteral("level"), tr("Floor %1", "Positive floor level")); } else if (level < -2) { addTagValue(m_description, QStringLiteral("level"), tr("Basement %1", "Negative floor level")); } else { addTagDescription(m_description, QStringLiteral("level"), QStringLiteral("2"), tr("Floor 2", "Floor level 2, two levels above ground level")); addTagDescription(m_description, QStringLiteral("level"), QStringLiteral("1"), tr("Floor 1", "Floor level 1, one level above ground level")); addTagDescription(m_description, QStringLiteral("level"), QStringLiteral("0"), tr("Ground floor", "Floor level 0")); addTagDescription(m_description, QStringLiteral("level"), QStringLiteral("-1"), tr("Basement 1", "Floor level -1, one level below ground level")); addTagDescription(m_description, QStringLiteral("level"), QStringLiteral("-2"), tr("Basement 2", "Floor level -2, two levels below ground level")); } } return m_description; } QString Placemark::address() const { if (m_address.isEmpty()) { m_address = addressFromOsmData(); } return m_address; } QString Placemark::website() const { if (!m_website.isEmpty()) { return m_website; } auto const tags = QStringList() << "website" << "contact:website" << "facebook" << "contact:facebook" << "url"; for(const QString &tag: tags) { QString const value = m_placemark.osmData().tagValue(tag); if (!value.isEmpty()) { QUrl url = QUrl(value); if (url.isValid()) { if (url.scheme().isEmpty()) { m_website = QStringLiteral("http://%1").arg(value); } else { m_website = value; } if (!m_website.isEmpty()) { return m_website; } } } } return m_website; } QString Placemark::wikipedia() const { if (!m_wikipedia.isEmpty()) { return m_wikipedia; } // TODO: also support "wikipedia:lang=page title" tags const QString wikipedia = m_placemark.osmData().tagValue("wikipedia"); if (!wikipedia.isEmpty()) { // full URL? if (wikipedia.startsWith(QLatin1String("http://")) || wikipedia.startsWith(QLatin1String("https://"))) { m_wikipedia = wikipedia; } else { // match "(lang:)human readable title" QRegularExpression re("^(?:([a-z]{2,}):)?(.*)$"); QRegularExpressionMatch match = re.match(wikipedia); QString lang = match.captured(1); if (lang.isEmpty()) { lang = QStringLiteral("en"); } const QString title = QString::fromLatin1(QUrl::toPercentEncoding(match.captured(2))); m_wikipedia = QLatin1String("https://") + lang + QLatin1String(".wikipedia.org/wiki/") + title; } } return m_wikipedia; } QString Placemark::openingHours() const { if (!m_openingHours.isEmpty()) { return m_openingHours; } addTagValue(m_openingHours, "opening_hours"); return m_openingHours; } QString Placemark::coordinates() const { return m_placemark.coordinate().toString(GeoDataCoordinates::Decimal).trimmed(); } QString Placemark::wheelchairInfo() const { if (!m_wheelchairInfo.isEmpty()) return m_wheelchairInfo; addTagDescription(m_wheelchairInfo, QStringLiteral("wheelchair"), QStringLiteral("yes"), tr("Wheelchair accessible")); addTagDescription(m_wheelchairInfo, QStringLiteral("wheelchair"), QStringLiteral("no"), tr("Wheelchair inaccessible")); addTagDescription(m_wheelchairInfo, QStringLiteral("wheelchair"), QStringLiteral("limited"), tr("Limited wheelchair accessibility")); addTagDescription(m_wheelchairInfo, QStringLiteral("wheelchair"), QStringLiteral("designated"), tr("Designated wheelchair access")); // Check if there is localized description auto const & osmData = m_placemark.osmData(); QStringList const uiLanguages = QLocale::system().uiLanguages(); const QString tag = QLatin1String("wheelchair:description:"); for (const QString &uiLanguage: uiLanguages) { for (auto tagIter = osmData.tagsBegin(), end = osmData.tagsEnd(); tagIter != end; ++tagIter) { if (tagIter.key().startsWith(tag)) { QStringRef const tagLanguage = tagIter.key().midRef(tag.length()); if (tagLanguage == uiLanguage) { append(m_wheelchairInfo, tagIter.value()); return m_wheelchairInfo; } } } } addTagValue(m_wheelchairInfo, "wheelchair:description"); return m_wheelchairInfo; } QString Placemark::wifiAvailable() const { if (!m_wifiAvailable.isEmpty()) { return m_wifiAvailable; } const auto& osmData = m_placemark.osmData(); addTagDescription(m_wifiAvailable, QStringLiteral("internet_access"), QStringLiteral("no"), tr("No public Internet access", "This location does not provide public Internet access")); addTagDescription(m_wifiAvailable, QStringLiteral("internet_access"), QStringLiteral("yes"), tr("Public Internet access available", "This location provides an unknown type of public Internet access.")); if (osmData.containsTag(QStringLiteral("internet_access:fee"), QStringLiteral("yes"))) { addTagDescription(m_wifiAvailable, QStringLiteral("internet_access"), QStringLiteral("wlan"), tr("Charged public wifi available", "Public wireless Internet access is available here for a fee.")); } else if (osmData.containsTag(QStringLiteral("internet_access:fee"), QStringLiteral("no"))) { addTagDescription(m_wifiAvailable, QStringLiteral("internet_access"), QStringLiteral("wlan"), tr("Free public wifi available", "Public wireless Internet access is available here for no cost.")); } else { addTagDescription(m_wifiAvailable, QStringLiteral("internet_access"), QStringLiteral("wlan"), tr("Public wifi available", "Public wireless Internet access is available here.")); } if (m_wifiAvailable.isEmpty()) { addTagDescription(m_wifiAvailable, QStringLiteral("wifi"), QStringLiteral("no"), tr("No public wifi", "Public wifi is not available here.")); addTagDescription(m_wifiAvailable, QStringLiteral("wifi"), QStringLiteral("yes"), tr("Public wifi available", "Public wireless Internet is available here.")); addTagDescription(m_wifiAvailable, QStringLiteral("wifi"), QStringLiteral("free"), tr("Free public wifi available", "Public wireless Internet is available here for no cost.")); } return m_wifiAvailable; } QString Placemark::phone() const { if (!m_phone.isEmpty()) { return m_phone; } addTagValue(m_phone, "phone"); return m_phone; } void Placemark::setName(const QString & name) { if (m_placemark.displayName() == name) { return; } m_placemark.setName(name); emit nameChanged(); } RouteRelationModel* Placemark::routeRelationModel() { return &m_relationModel; } double Placemark::longitude() const { return m_placemark.coordinate().longitude(GeoDataCoordinates::Degree); } double Placemark::latitude() const { return m_placemark.coordinate().latitude(GeoDataCoordinates::Degree); } const QStringList &Placemark::tags() const { return m_tags; } bool Placemark::addTagValue(QString &target, const QString &key, const QString &format, const QString& separator) const { auto const & osmData = m_placemark.osmData(); QString const value = osmData.tagValue(key); if (!value.isEmpty()) { QString description = format.isEmpty() ? value : format.arg(value); description.replace(QLatin1Char(';'), separator); append(target, description); return true; } return false; } void Placemark::addFirstTagValueOf(QString &target, const QStringList &keys) const { for (auto const &key: keys) { if (addTagValue(target, key)) { return; } } } void Placemark::addTagDescription(QString &target, const QString &key, const QString &value, const QString &description) const { auto const & osmData = m_placemark.osmData(); if (osmData.containsTag(key, value)) { append(target, description); } } void Placemark::append(QString &target, const QString &value) { if (!target.isEmpty()) { target += QStringLiteral(" · "); // non-latin1 } target += value; } QString Placemark::addressFromOsmData() const { #ifdef HAVE_QT5_POSITIONING QGeoAddress address; OsmPlacemarkData const data = m_placemark.osmData(); address.setCountry(data.tagValue("addr:country")); address.setState(data.tagValue("addr:state")); address.setCity(data.tagValue("addr:city")); address.setDistrict(data.tagValue("district")); address.setPostalCode(data.tagValue("addr:postcode")); QString const street = data.tagValue("addr:street"); QString const houseNumber = data.tagValue("addr:housenumber"); address.setStreet(formatStreet(street, houseNumber)); return address.text().replace("
", ", "); #else return QString(); #endif } QString Placemark::formatStreet(const QString &street, const QString &houseNumber) { return houseNumber.isEmpty() ? street : tr("%1 %2", "House number (first argument) and street name (second argument) in an address").arg(houseNumber).arg(street).trimmed(); } void Placemark::updateTags() { m_tags.clear(); QString const tag = QStringLiteral("%1 = %2"); for (auto iter = m_placemark.osmData().tagsBegin(), end = m_placemark.osmData().tagsEnd(); iter != end; ++iter) { m_tags << tag.arg(iter.key()).arg(iter.value()); } } void Placemark::updateRelations(const Marble::GeoDataPlacemark &placemark) { if (const auto document = (placemark.parent() ? geodata_cast(placemark.parent()) : nullptr)) { QVector allRelations; QSet relevantRelations; QSet placemarkIds; auto const & osmData = placemark.osmData(); placemarkIds << osmData.oid(); bool searchRelations = true; for (auto feature: document->featureList()) { if (const auto relation = geodata_cast(feature)) { allRelations << relation; if (relation->memberIds().contains(osmData.oid())) { relevantRelations << relation; auto const isRoute = relation->osmData().tagValue(QStringLiteral("type")) == QStringLiteral("route"); searchRelations &= !isRoute; } } } if (searchRelations) { for (auto feature: document->featureList()) { if (const auto relation = geodata_cast(feature)) { if (relevantRelations.contains(relation) && relation->osmData().containsTag(QStringLiteral("type"), QStringLiteral("public_transport")) && relation->osmData().containsTag(QStringLiteral("public_transport"), QStringLiteral("stop_area"))) { for (auto iter = relation->osmData().relationReferencesBegin(), end = relation->osmData().relationReferencesEnd(); iter != end; ++iter) { if (iter.value() == QStringLiteral("stop") || iter.value() == QStringLiteral("platform")) { placemarkIds << iter.key(); } } } } } } for (auto relation: allRelations) { if (relation->containsAnyOf(placemarkIds)) { relevantRelations << relation; } } m_relationModel.setRelations(relevantRelations); } } } #include "moc_Placemark.cpp" diff --git a/src/lib/marble/geodata/graphicsitem/AbstractGeoPolygonGraphicsItem.cpp b/src/lib/marble/geodata/graphicsitem/AbstractGeoPolygonGraphicsItem.cpp index 58789ec08..7db64391d 100644 --- a/src/lib/marble/geodata/graphicsitem/AbstractGeoPolygonGraphicsItem.cpp +++ b/src/lib/marble/geodata/graphicsitem/AbstractGeoPolygonGraphicsItem.cpp @@ -1,240 +1,239 @@ // // This file is part of the Marble Virtual Globe. // // This program is free software licensed under the GNU LGPL. You can // find a copy of this license in LICENSE.txt in the top directory of // the source code. // // Copyright 2011 Konstantin Oblaukhov // #include "AbstractGeoPolygonGraphicsItem.h" #include "GeoDataLinearRing.h" #include "GeoDataPolygon.h" #include "GeoDataBuilding.h" #include "GeoPainter.h" #include "GeoDataLatLonAltBox.h" #include "GeoDataStyle.h" #include "GeoDataIconStyle.h" #include "GeoDataLineStyle.h" #include "GeoDataPlacemark.h" #include "GeoDataPolyStyle.h" -#include "GeoDataTypes.h" #include "OsmPlacemarkData.h" #include "MarbleDebug.h" #include "ViewportParams.h" #include #include #include namespace Marble { const void *AbstractGeoPolygonGraphicsItem::s_previousStyle = nullptr; AbstractGeoPolygonGraphicsItem::AbstractGeoPolygonGraphicsItem(const GeoDataPlacemark *placemark, const GeoDataPolygon *polygon) : GeoGraphicsItem(placemark), m_polygon(polygon), m_ring(nullptr), m_building(nullptr) { } AbstractGeoPolygonGraphicsItem::AbstractGeoPolygonGraphicsItem(const GeoDataPlacemark *placemark, const GeoDataLinearRing *ring) : GeoGraphicsItem(placemark), m_polygon(nullptr), m_ring(ring), m_building(nullptr) { } AbstractGeoPolygonGraphicsItem::AbstractGeoPolygonGraphicsItem(const GeoDataPlacemark *placemark, const GeoDataBuilding *building) : GeoGraphicsItem(placemark), m_polygon(nullptr), m_ring(nullptr), m_building(building) { } AbstractGeoPolygonGraphicsItem::~AbstractGeoPolygonGraphicsItem() { } const GeoDataLatLonAltBox& AbstractGeoPolygonGraphicsItem::latLonAltBox() const { if(m_polygon) { return m_polygon->latLonAltBox(); } else if (m_ring) { return m_ring->latLonAltBox(); } return m_building->latLonAltBox(); } void AbstractGeoPolygonGraphicsItem::paint( GeoPainter* painter, const ViewportParams* viewport, const QString &layer, int tileZoomLevel) { Q_UNUSED(layer); Q_UNUSED(tileZoomLevel); bool isValid = true; if (s_previousStyle != style().data()) { isValid = configurePainter(painter, *viewport); } s_previousStyle = style().data(); if (!isValid) return; if ( m_polygon ) { bool innerResolved = false; for(auto const & ring : m_polygon->innerBoundaries()) { if (viewport->resolves(ring.latLonAltBox(), 4)) { innerResolved = true; break; } } if (innerResolved) { painter->drawPolygon(*m_polygon); } else { painter->drawPolygon(m_polygon->outerBoundary()); } } else if ( m_ring ) { painter->drawPolygon( *m_ring ); } } bool AbstractGeoPolygonGraphicsItem::contains(const QPoint &screenPosition, const ViewportParams *viewport) const { auto const visualCategory = static_cast(feature())->visualCategory(); if (visualCategory == GeoDataPlacemark::Landmass || visualCategory == GeoDataPlacemark::UrbanArea || (visualCategory >= GeoDataPlacemark::LanduseAllotments && visualCategory <= GeoDataPlacemark::LanduseVineyard)) { return false; } double lon, lat; viewport->geoCoordinates(screenPosition.x(), screenPosition.y(), lon, lat, GeoDataCoordinates::Radian); auto const coordinates = GeoDataCoordinates(lon, lat); if (m_polygon) { return m_polygon->contains(coordinates); } else if (m_ring) { return m_ring->contains(coordinates); } return false; } bool AbstractGeoPolygonGraphicsItem::configurePainter(GeoPainter *painter, const ViewportParams &viewport) const { QPen currentPen = painter->pen(); GeoDataStyle::ConstPtr style = this->style(); if (!style) { painter->setPen( QPen() ); // "style-less" polygons: a 1px black solid line } else { const GeoDataPolyStyle& polyStyle = style->polyStyle(); if (polyStyle.outline()) { const GeoDataLineStyle& lineStyle = style->lineStyle(); // To save performance we avoid making changes to the painter's pen. // So we first take a copy of the actual painter pen, make changes to it // and only if the resulting pen is different from the actual pen // we replace the painter's pen with our new pen. // We want to avoid the mandatory detach in QPen::setColor(), // so we carefully check whether applying the setter is needed currentPen.setColor(lineStyle.paintedColor()); currentPen.setWidthF(lineStyle.width()); currentPen.setCapStyle(lineStyle.capStyle()); currentPen.setStyle(lineStyle.penStyle()); if (painter->pen().color() != currentPen.color()) { painter->setPen(currentPen); } } else { // polygons without outline: Qt::NoPen (not drawn) if (currentPen.style() != Qt::NoPen) { painter->setPen(Qt::NoPen); } } if (!polyStyle.fill()) { painter->setBrush(Qt::transparent); } else { const QColor paintedColor = polyStyle.paintedColor(); if (painter->brush().color() != paintedColor || painter->brush().style() != polyStyle.brushStyle()) { if (!polyStyle.texturePath().isEmpty() || !polyStyle.textureImage().isNull()) { GeoDataCoordinates coords = latLonAltBox().center(); qreal x, y; viewport.screenCoordinates(coords, x, y); QBrush brush(texture(polyStyle.texturePath(), paintedColor)); painter->setBrush(brush); painter->setBrushOrigin(QPoint(x,y)); } else { painter->setBrush(QBrush(paintedColor, polyStyle.brushStyle())); } } } } return true; } int AbstractGeoPolygonGraphicsItem::extractElevation(const GeoDataPlacemark &placemark) { int elevation = 0; const OsmPlacemarkData &osmData = placemark.osmData(); const auto tagIter = osmData.findTag(QStringLiteral("ele")); if (tagIter != osmData.tagsEnd()) { elevation = tagIter.value().toInt(); } return elevation; } QPixmap AbstractGeoPolygonGraphicsItem::texture(const QString &texturePath, const QColor &color) const { QString const key = QString::number(color.rgba()) + '/' + texturePath; QPixmap texture; if (!QPixmapCache::find(key, texture)) { QImageReader imageReader(style()->polyStyle().resolvePath(texturePath)); texture = QPixmap::fromImageReader(&imageReader); if (texture.hasAlphaChannel()) { QPixmap pixmap (texture.size()); pixmap.fill(color); QPainter imagePainter(&pixmap); imagePainter.drawPixmap(0, 0, texture); imagePainter.end(); texture = pixmap; } QPixmapCache::insert(key, texture); } return texture; } void AbstractGeoPolygonGraphicsItem::setLinearRing(GeoDataLinearRing *ring) { Q_ASSERT(m_building); Q_ASSERT(!m_polygon); m_ring = ring; } void AbstractGeoPolygonGraphicsItem::setPolygon(GeoDataPolygon *polygon) { Q_ASSERT(m_building); Q_ASSERT(!m_ring); m_polygon = polygon; } } diff --git a/src/lib/marble/geodata/graphicsitem/BuildingGraphicsItem.cpp b/src/lib/marble/geodata/graphicsitem/BuildingGraphicsItem.cpp index 2f9364d75..f9625d3ff 100644 --- a/src/lib/marble/geodata/graphicsitem/BuildingGraphicsItem.cpp +++ b/src/lib/marble/geodata/graphicsitem/BuildingGraphicsItem.cpp @@ -1,503 +1,502 @@ // // This file is part of the Marble Virtual Globe. // // This program is free software licensed under the GNU LGPL. You can // find a copy of this license in LICENSE.txt in the top directory of // the source code. // // Copyright 2011 Konstantin Oblaukhov // #include "BuildingGraphicsItem.h" #include "MarbleDebug.h" #include "ViewportParams.h" -#include "GeoDataTypes.h" #include "GeoDataPlacemark.h" #include "GeoDataLinearRing.h" #include "GeoDataPolygon.h" #include "GeoDataBuilding.h" #include "GeoDataMultiGeometry.h" #include "GeoDataPolyStyle.h" #include "OsmPlacemarkData.h" #include "GeoPainter.h" #include #include namespace Marble { BuildingGraphicsItem::BuildingGraphicsItem(const GeoDataPlacemark *placemark, const GeoDataBuilding *building) : AbstractGeoPolygonGraphicsItem(placemark, building) { if (const auto ring = geodata_cast(&building->multiGeometry()->at(0))) { setLinearRing(ring); } else if (const auto poly = geodata_cast(&building->multiGeometry()->at(0))) { setPolygon(poly); } setZValue(building->height()); Q_ASSERT(building->height() > 0.0); QStringList paintLayers; paintLayers << QStringLiteral("Polygon/Building/frame") << QStringLiteral("Polygon/Building/roof"); setPaintLayers(paintLayers); } BuildingGraphicsItem::~BuildingGraphicsItem() { qDeleteAll(m_cachedOuterPolygons); qDeleteAll(m_cachedInnerPolygons); qDeleteAll(m_cachedOuterRoofPolygons); qDeleteAll(m_cachedInnerRoofPolygons); } void BuildingGraphicsItem::initializeBuildingPainting(const GeoPainter* painter, const ViewportParams *viewport, bool &drawAccurate3D, bool &isCameraAboveBuilding ) const { drawAccurate3D = false; isCameraAboveBuilding = false; auto const screen = QApplication::screens().first(); double const physicalSize = 1.0; // mm int const pixelSize = qRound(physicalSize * screen->physicalDotsPerInch() / (IN2M * M2MM)); QPointF offsetAtCorner = buildingOffset(QPointF(0, 0), viewport, &isCameraAboveBuilding); qreal maxOffset = qMax( qAbs( offsetAtCorner.x() ), qAbs( offsetAtCorner.y() ) ); drawAccurate3D = painter->mapQuality() == HighQuality ? maxOffset > pixelSize : maxOffset > 1.5 * pixelSize; } void BuildingGraphicsItem::updatePolygons(const ViewportParams &viewport, QVector& outerPolygons, QVector& innerPolygons, bool &hasInnerBoundaries) const { // Since subtracting one fully contained polygon from another results in a single // polygon with a "connecting line" between the inner and outer part we need // to first paint the inner area with no pen and then the outlines with the correct pen. hasInnerBoundaries = polygon() ? !polygon()->innerBoundaries().isEmpty() : false; if (polygon()) { if (hasInnerBoundaries) { screenPolygons(viewport, polygon(), innerPolygons, outerPolygons); } else { viewport.screenCoordinates(polygon()->outerBoundary(), outerPolygons); } } else if (ring()) { viewport.screenCoordinates(*ring(), outerPolygons); } } QPointF BuildingGraphicsItem::centroid(const QPolygonF &polygon, double &area) { auto centroid = QPointF(0.0, 0.0); area = 0.0; for (auto i=0, n=polygon.size(); iheight() > 0.0); qreal const buildingFactor = building()->height() / EARTH_RADIUS; qreal const cameraHeightPixel = viewport->width() * cameraFactor; qreal buildingHeightPixel = viewport->radius() * buildingFactor; qreal const cameraDistance = cameraHeightPixel-buildingHeightPixel; if (isCameraAboveBuilding) { *isCameraAboveBuilding = cameraDistance > 0; } qreal const cc = cameraDistance * cameraHeightPixel; qreal const cb = cameraDistance * buildingHeightPixel; // The following lines calculate the same result, but are potentially slower due // to using more trigonometric method calls // qreal const alpha1 = atan2(offsetX, cameraHeightPixel); // qreal const alpha2 = atan2(offsetX, cameraHeightPixel-buildingHeightPixel); // qreal const shiftX = 2 * (cameraHeightPixel-buildingHeightPixel) * sin(0.5*(alpha2-alpha1)); qreal const offsetX = point.x() - viewport->width() / 2.0; qreal const offsetY = point.y() - viewport->height() / 2.0; qreal const shiftX = offsetX * cb / (cc + offsetX); qreal const shiftY = offsetY * cb / (cc + offsetY); return QPointF(shiftX, shiftY); } void BuildingGraphicsItem::paint(GeoPainter* painter, const ViewportParams* viewport, const QString &layer, int tileZoomLevel) { // Just display flat buildings for tile level 17 if (tileZoomLevel == 17) { setZValue(0.0); if (layer.endsWith(QLatin1String("/roof"))) { AbstractGeoPolygonGraphicsItem::paint(painter, viewport, layer, tileZoomLevel ); } return; } setZValue(building()->height()); // For level 18, 19 .. render 3D buildings in perspective if (layer.endsWith(QLatin1String("/frame"))) { qDeleteAll(m_cachedOuterPolygons); qDeleteAll(m_cachedInnerPolygons); qDeleteAll(m_cachedOuterRoofPolygons); qDeleteAll(m_cachedInnerRoofPolygons); m_cachedOuterPolygons.clear(); m_cachedInnerPolygons.clear(); m_cachedOuterRoofPolygons.clear(); m_cachedInnerRoofPolygons.clear(); updatePolygons(*viewport, m_cachedOuterPolygons, m_cachedInnerPolygons, m_hasInnerBoundaries); if (m_cachedOuterPolygons.isEmpty()) { return; } paintFrame(painter, viewport); } else if (layer.endsWith(QLatin1String("/roof"))) { if (m_cachedOuterPolygons.isEmpty()) { return; } paintRoof(painter, viewport); } else { mDebug() << "Didn't expect to have to paint layer " << layer << ", ignoring it."; } } void BuildingGraphicsItem::paintRoof(GeoPainter* painter, const ViewportParams* viewport) { bool drawAccurate3D; bool isCameraAboveBuilding; initializeBuildingPainting(painter, viewport, drawAccurate3D, isCameraAboveBuilding); if (!isCameraAboveBuilding) { return; // do not render roof if we look inside the building } bool isValid = true; if (s_previousStyle != style().data()) { isValid = configurePainter(painter, *viewport); QFont font = painter->font(); // TODO: better font configuration if (font.pointSize() != 10) { font.setPointSize( 10 ); painter->setFont(font); } } s_previousStyle = style().data(); if (!isValid) return; // first paint the area (and the outline if there are no inner boundaries) if ( drawAccurate3D) { if (m_hasInnerBoundaries) { QPen const currentPen = painter->pen(); painter->setPen(Qt::NoPen); QVector fillPolygons = painter->createFillPolygons( m_cachedOuterRoofPolygons, m_cachedInnerRoofPolygons ); for( const QPolygonF* fillPolygon: fillPolygons ) { painter->drawPolygon(*fillPolygon); } painter->setPen(currentPen); for( const QPolygonF* outerRoof: m_cachedOuterRoofPolygons ) { painter->drawPolyline( *outerRoof ); } for( const QPolygonF* innerRoof: m_cachedInnerRoofPolygons ) { painter->drawPolyline( *innerRoof ); } qDeleteAll(fillPolygons); } else { for( const QPolygonF* outerRoof: m_cachedOuterRoofPolygons ) { painter->drawPolygon( *outerRoof ); } } } else { QPointF const offset = buildingOffset(m_cachedOuterPolygons[0]->boundingRect().center(), viewport); painter->translate(offset); if (m_hasInnerBoundaries) { QPen const currentPen = painter->pen(); painter->setPen(Qt::NoPen); QVector fillPolygons = painter->createFillPolygons( m_cachedOuterPolygons, m_cachedInnerPolygons ); for( const QPolygonF* fillPolygon: fillPolygons ) { painter->drawPolygon(*fillPolygon); } painter->setPen(currentPen); for( const QPolygonF* outerPolygon: m_cachedOuterPolygons ) { painter->drawPolyline( *outerPolygon ); } for( const QPolygonF* innerPolygon: m_cachedInnerPolygons ) { painter->drawPolyline( *innerPolygon ); } qDeleteAll(fillPolygons); } else { for( const QPolygonF* outerPolygon: m_cachedOuterPolygons ) { painter->drawPolygon( *outerPolygon ); } } painter->translate(-offset); } qreal maxSize(0.0); double maxArea = 0.0; for (int i = 0; i < m_cachedOuterRoofPolygons.size(); ++i) { const QPolygonF *outerRoof = m_cachedOuterRoofPolygons[i]; QPointF roofCenter; // Label position calculation if (!building()->name().isEmpty() || !building()->entries().isEmpty()) { QSizeF const polygonSize = outerRoof->boundingRect().size(); qreal size = polygonSize.width() * polygonSize.height(); if (size > maxSize) { maxSize = size; double area; roofCenter = centroid(*outerRoof, area); maxArea = qMax(area, maxArea); } } // Draw the housenumber labels if (drawAccurate3D && !building()->name().isEmpty() && !roofCenter.isNull()) { double const w2 = 0.5 * painter->fontMetrics().width(building()->name()); double const ascent = painter->fontMetrics().ascent(); double const descent = painter->fontMetrics().descent(); double const a2 = 0.5 * painter->fontMetrics().ascent(); QPointF const textPosition = roofCenter - QPointF(w2, -a2); if (outerRoof->containsPoint(textPosition + QPointF(-2, -ascent), Qt::OddEvenFill) && outerRoof->containsPoint(textPosition + QPointF(-2, descent), Qt::OddEvenFill) && outerRoof->containsPoint(textPosition + QPointF(2+2*w2, descent), Qt::OddEvenFill) && outerRoof->containsPoint(textPosition + QPointF(2+2*w2, -ascent), Qt::OddEvenFill) ) { painter->drawTextFragment(roofCenter.toPoint(), building()->name(), painter->font().pointSize(), painter->brush().color()); } } } // Render additional housenumbers at building entries if (!building()->entries().isEmpty() && maxArea > 1600 * building()->entries().size()) { for(const auto &entry: building()->entries()) { qreal x, y; viewport->screenCoordinates(entry.point, x, y); QPointF point(x, y); point += buildingOffset(point, viewport); painter->drawTextFragment(point.toPoint(), building()->name(), painter->font().pointSize(), painter->brush().color(), GeoPainter::RoundFrame); } } } void BuildingGraphicsItem::paintFrame(GeoPainter *painter, const ViewportParams *viewport) { // TODO: how does this match the Q_ASSERT in the constructor? if (building()->height() == 0.0) { return; } if ((polygon() && !viewport->resolves(polygon()->outerBoundary().latLonAltBox(), 4)) || (ring() && !viewport->resolves(ring()->latLonAltBox(), 4))) { return; } bool drawAccurate3D; bool isCameraAboveBuilding; initializeBuildingPainting(painter, viewport, drawAccurate3D, isCameraAboveBuilding); bool isValid = true; if (s_previousStyle != style().data()) { isValid = configurePainterForFrame(painter); } s_previousStyle = style().data(); if (!isValid) return; if ( drawAccurate3D && isCameraAboveBuilding ) { for (const QPolygonF *outline: m_cachedOuterPolygons) { if (outline->isEmpty()) { continue; } // draw the building sides int const size = outline->size(); QPolygonF * outerRoof = new QPolygonF; outerRoof->reserve(outline->size()); QPointF a = (*outline)[0]; QPointF shiftA = a + buildingOffset(a, viewport); outerRoof->append(shiftA); for (int i=1; i= 0; if (!backface) { QPolygonF buildingSide; buildingSide.reserve(4); buildingSide << a << shiftA << shiftB << b; painter->drawPolygon(buildingSide); } a = b; shiftA = shiftB; outerRoof->append(shiftA); } m_cachedOuterRoofPolygons.append(outerRoof); } for (const QPolygonF *outline: m_cachedInnerPolygons) { if (outline->isEmpty()) { continue; } // draw the building sides int const size = outline->size(); QPolygonF * innerRoof = new QPolygonF; innerRoof->reserve(outline->size()); QPointF a = (*outline)[0]; QPointF shiftA = a + buildingOffset(a, viewport); innerRoof->append(shiftA); for (int i=1; i= 0; if (backface) { QPolygonF buildingSide; buildingSide.reserve(4); buildingSide << a << shiftA << shiftB << b; painter->drawPolygon(buildingSide); } a = b; shiftA = shiftB; innerRoof->append(shiftA); } m_cachedInnerRoofPolygons.append(innerRoof); } } else { // don't draw the building sides - just draw the base frame instead QVector fillPolygons = painter->createFillPolygons( m_cachedOuterPolygons, m_cachedInnerPolygons ); for( QPolygonF* fillPolygon: fillPolygons ) { painter->drawPolygon(*fillPolygon); } qDeleteAll(fillPolygons); } } void BuildingGraphicsItem::screenPolygons(const ViewportParams &viewport, const GeoDataPolygon *polygon, QVector &innerPolygons, QVector &outerPolygons ) { Q_ASSERT(polygon); viewport.screenCoordinates(polygon->outerBoundary(), outerPolygons); QVector const & innerBoundaries = polygon->innerBoundaries(); for (const GeoDataLinearRing &innerBoundary: innerBoundaries) { QVector innerPolygonsPerBoundary; viewport.screenCoordinates(innerBoundary, innerPolygonsPerBoundary); innerPolygons.reserve(innerPolygons.size() + innerPolygonsPerBoundary.size()); for( QPolygonF* innerPolygonPerBoundary: innerPolygonsPerBoundary ) { innerPolygons << innerPolygonPerBoundary; } } } bool BuildingGraphicsItem::contains(const QPoint &screenPosition, const ViewportParams *viewport) const { if (m_cachedOuterPolygons.isEmpty()) { // Level 17 return AbstractGeoPolygonGraphicsItem::contains(screenPosition, viewport); } QPointF const point = screenPosition; for (auto polygon: m_cachedOuterRoofPolygons) { if (polygon->containsPoint(point, Qt::OddEvenFill)) { for (auto polygon: m_cachedInnerRoofPolygons) { if (polygon->containsPoint(point, Qt::OddEvenFill)) { return false; } } return true; } } for (auto polygon: m_cachedOuterPolygons) { if (polygon->containsPoint(point, Qt::OddEvenFill)) { for (auto polygon: m_cachedInnerPolygons) { if (polygon->containsPoint(point, Qt::OddEvenFill)) { return false; } } return true; } } return false; } bool BuildingGraphicsItem::configurePainterForFrame(GeoPainter *painter) const { QPen currentPen = painter->pen(); GeoDataStyle::ConstPtr style = this->style(); if (!style) { painter->setPen( QPen() ); } else { const GeoDataPolyStyle& polyStyle = style->polyStyle(); if (currentPen.style() != Qt::NoPen) { painter->setPen(Qt::NoPen); } if (!polyStyle.fill()) { return false; } else { const QColor paintedColor = polyStyle.paintedColor().darker(150); if (painter->brush().color() != paintedColor) { painter->setBrush(paintedColor); } } } return true; } } diff --git a/src/plugins/runner/osm/OsmParser.cpp b/src/plugins/runner/osm/OsmParser.cpp index 7bf5f9106..f972b8ad8 100644 --- a/src/plugins/runner/osm/OsmParser.cpp +++ b/src/plugins/runner/osm/OsmParser.cpp @@ -1,254 +1,253 @@ // // This file is part of the Marble Virtual Globe. // // This program is free software licensed under the GNU LGPL. You can // find a copy of this license in LICENSE.txt in the top directory of // the source code. // // Copyright 2011 Thibaut Gridel // Copyright 2011 Konstantin Oblaukhov // Copyright 2014 Bernhard Beschow // Copyright 2015 Dennis Nienhüser // #include "OsmParser.h" #include "OsmElementDictionary.h" #include "osm/OsmObjectManager.h" #include "GeoDataDocument.h" #include "GeoDataPoint.h" -#include "GeoDataTypes.h" #include "GeoDataStyle.h" #include "GeoDataPolyStyle.h" #include #include "o5mreader.h" #include #include #include #include #include namespace Marble { GeoDataDocument *OsmParser::parse(const QString &filename, QString &error) { QFileInfo const fileInfo(filename); if (!fileInfo.exists() || !fileInfo.isReadable()) { error = QString("Cannot read file %1").arg(filename); return nullptr; } if (fileInfo.completeSuffix() == QLatin1String("o5m")) { return parseO5m(filename, error); } else { return parseXml(filename, error); } } GeoDataDocument* OsmParser::parseO5m(const QString &filename, QString &error) { O5mreader* reader; O5mreaderDataset data; O5mreaderIterateRet outerState, innerState; char *key, *value; // share string data on the heap at least for this file QSet stringPool; OsmNodes nodes; OsmWays ways; OsmRelations relations; QHash relationTypes; relationTypes[O5MREADER_DS_NODE] = QStringLiteral("node"); relationTypes[O5MREADER_DS_WAY] = QStringLiteral("way"); relationTypes[O5MREADER_DS_REL] = QStringLiteral("relation"); auto file = fopen(filename.toStdString().c_str(), "rb"); o5mreader_open(&reader, file); while( (outerState = o5mreader_iterateDataSet(reader, &data)) == O5MREADER_ITERATE_RET_NEXT) { switch (data.type) { case O5MREADER_DS_NODE: { OsmNode& node = nodes[data.id]; node.osmData().setId(data.id); node.setCoordinates(GeoDataCoordinates(data.lon*1.0e-7, data.lat*1.0e-7, 0.0, GeoDataCoordinates::Degree)); while ((innerState = o5mreader_iterateTags(reader, &key, &value)) == O5MREADER_ITERATE_RET_NEXT) { const QString keyString = *stringPool.insert(QString::fromUtf8(key)); const QString valueString = *stringPool.insert(QString::fromUtf8(value)); node.osmData().addTag(keyString, valueString); } } break; case O5MREADER_DS_WAY: { OsmWay &way = ways[data.id]; way.osmData().setId(data.id); uint64_t nodeId; while ((innerState = o5mreader_iterateNds(reader, &nodeId)) == O5MREADER_ITERATE_RET_NEXT) { way.addReference(nodeId); } while ((innerState = o5mreader_iterateTags(reader, &key, &value)) == O5MREADER_ITERATE_RET_NEXT) { const QString keyString = *stringPool.insert(QString::fromUtf8(key)); const QString valueString = *stringPool.insert(QString::fromUtf8(value)); way.osmData().addTag(keyString, valueString); } } break; case O5MREADER_DS_REL: { OsmRelation &relation = relations[data.id]; relation.osmData().setId(data.id); char *role; uint8_t type; uint64_t refId; while ((innerState = o5mreader_iterateRefs(reader, &refId, &type, &role)) == O5MREADER_ITERATE_RET_NEXT) { const QString roleString = *stringPool.insert(QString::fromUtf8(role)); relation.addMember(refId, roleString, relationTypes[type]); } while ((innerState = o5mreader_iterateTags(reader, &key, &value)) == O5MREADER_ITERATE_RET_NEXT) { const QString keyString = *stringPool.insert(QString::fromUtf8(key)); const QString valueString = *stringPool.insert(QString::fromUtf8(value)); relation.osmData().addTag(keyString, valueString); } } break; } } fclose(file); error = reader->errMsg; o5mreader_close(reader); return createDocument(nodes, ways, relations); } GeoDataDocument* OsmParser::parseXml(const QString &filename, QString &error) { QXmlStreamReader parser; QFile file; QBuffer buffer; QFileInfo fileInfo(filename); if (fileInfo.completeSuffix() == QLatin1String("osm.zip")) { MarbleZipReader zipReader(filename); if (zipReader.fileInfoList().size() != 1) { int const fileNumber = zipReader.fileInfoList().size(); error = QStringLiteral("Unexpected number of files (%1) in %2").arg(fileNumber).arg(filename); return nullptr; } QByteArray const data = zipReader.fileData(zipReader.fileInfoList().first().filePath); buffer.setData(data); buffer.open(QBuffer::ReadOnly); parser.setDevice(&buffer); } else { file.setFileName(filename); if (!file.open(QFile::ReadOnly)) { error = QStringLiteral("Cannot open file %1").arg(filename); return nullptr; } parser.setDevice(&file); } OsmPlacemarkData* osmData(nullptr); QString parentTag; qint64 parentId(0); // share string data on the heap at least for this file QSet stringPool; OsmNodes m_nodes; OsmWays m_ways; OsmRelations m_relations; while (!parser.atEnd()) { parser.readNext(); if (!parser.isStartElement()) { continue; } QStringRef const tagName = parser.name(); if (tagName == osm::osmTag_node || tagName == osm::osmTag_way || tagName == osm::osmTag_relation) { parentTag = parser.name().toString(); parentId = parser.attributes().value(QLatin1String("id")).toLongLong(); if (tagName == osm::osmTag_node) { m_nodes[parentId].osmData() = OsmPlacemarkData::fromParserAttributes(parser.attributes()); m_nodes[parentId].parseCoordinates(parser.attributes()); osmData = &m_nodes[parentId].osmData(); } else if (tagName == osm::osmTag_way) { m_ways[parentId].osmData() = OsmPlacemarkData::fromParserAttributes(parser.attributes()); osmData = &m_ways[parentId].osmData(); } else { Q_ASSERT(tagName == osm::osmTag_relation); m_relations[parentId].osmData() = OsmPlacemarkData::fromParserAttributes(parser.attributes()); osmData = &m_relations[parentId].osmData(); } } else if (osmData && tagName == osm::osmTag_tag) { const QXmlStreamAttributes &attributes = parser.attributes(); const QString keyString = *stringPool.insert(attributes.value(QLatin1String("k")).toString()); const QString valueString = *stringPool.insert(attributes.value(QLatin1String("v")).toString()); osmData->addTag(keyString, valueString); } else if (tagName == osm::osmTag_nd && parentTag == osm::osmTag_way) { m_ways[parentId].addReference(parser.attributes().value(QLatin1String("ref")).toLongLong()); } else if (tagName == osm::osmTag_member && parentTag == osm::osmTag_relation) { m_relations[parentId].parseMember(parser.attributes()); } // other tags like osm, bounds ignored } if (parser.hasError()) { error = parser.errorString(); return nullptr; } return createDocument(m_nodes, m_ways, m_relations); } GeoDataDocument *OsmParser::createDocument(OsmNodes &nodes, OsmWays &ways, OsmRelations &relations) { GeoDataDocument* document = new GeoDataDocument; GeoDataPolyStyle backgroundPolyStyle; backgroundPolyStyle.setFill( true ); backgroundPolyStyle.setOutline( false ); backgroundPolyStyle.setColor(QStringLiteral("#f1eee8")); GeoDataStyle::Ptr backgroundStyle(new GeoDataStyle); backgroundStyle->setPolyStyle( backgroundPolyStyle ); backgroundStyle->setId(QStringLiteral("background")); document->addStyle( backgroundStyle ); QSet usedNodes, usedWays; for(auto const &relation: relations) { relation.createMultipolygon(document, ways, nodes, usedNodes, usedWays); } for(auto id: usedWays) { ways.remove(id); } QHash placemarks; for (auto iter=ways.constBegin(), end=ways.constEnd(); iter != end; ++iter) { auto placemark = iter.value().create(nodes, usedNodes); if (placemark) { document->append(placemark); placemarks[placemark->osmData().oid()] = placemark; } } for(auto id: usedNodes) { if (nodes[id].osmData().isEmpty()) { nodes.remove(id); } } for(auto const &node: nodes) { auto placemark = node.create(); if (placemark) { document->append(placemark); placemarks[placemark->osmData().oid()] = placemark; } } for(auto const &relation: relations) { relation.createRelation(document, placemarks); } return document; } }