diff --git a/src/lib/marble/geodata/data/GeoDataPlacemark.cpp b/src/lib/marble/geodata/data/GeoDataPlacemark.cpp index bc658de56..056df6536 100644 --- a/src/lib/marble/geodata/data/GeoDataPlacemark.cpp +++ b/src/lib/marble/geodata/data/GeoDataPlacemark.cpp @@ -1,818 +1,822 @@ // // 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 2004-2007 Torsten Rahn // Copyright 2007 Inge Wallin // Copyright 2008-2009 Patrick Spendrin // // Own #include "GeoDataPlacemark.h" // Private #include "GeoDataPlacemark_p.h" #include "GeoDataMultiGeometry.h" #include "GeoDataCoordinates.h" #include "GeoDataLatLonAltBox.h" #include "GeoDataData.h" #include "osm/OsmPlacemarkData.h" // Qt #include #include "MarbleDebug.h" #include "GeoDataTrack.h" #include "GeoDataModel.h" #include #include namespace Marble { const OsmPlacemarkData GeoDataPlacemarkPrivate::s_nullOsmPlacemarkData = OsmPlacemarkData(); const GeoDataPlacemarkExtendedData GeoDataPlacemarkPrivate::s_nullPlacemarkExtendedData = GeoDataPlacemarkExtendedData(); GeoDataPlacemark::GeoDataPlacemark() : GeoDataFeature( new GeoDataPlacemarkPrivate ) { p()->m_geometry->setParent( this ); } GeoDataPlacemark::GeoDataPlacemark( const GeoDataPlacemark& other ) : GeoDataFeature( other ) { // FIXME: temporary (until detach() is called) violates following invariant // which could lead to crashes // Q_ASSERT( this == p()->m_geometry->parent() ); // FIXME: fails as well when "other" is a copy where detach wasn't called // Q_ASSERT( other.p()->m_geometry == 0 || &other == other.p()->m_geometry->parent() ); } GeoDataPlacemark::GeoDataPlacemark( const QString& name ) : GeoDataFeature( new GeoDataPlacemarkPrivate ) { d->m_name = name; p()->m_geometry->setParent( this ); } GeoDataPlacemark::~GeoDataPlacemark() { // nothing to do } GeoDataPlacemark &GeoDataPlacemark::operator=( const GeoDataPlacemark &other ) { GeoDataFeature::operator=( other ); return *this; } bool GeoDataPlacemark::operator==( const GeoDataPlacemark& other ) const { if (!equals(other) || p()->m_population != other.p()->m_population) { return false; } if ((p()->m_placemarkExtendedData && !other.p()->m_placemarkExtendedData) || (!p()->m_placemarkExtendedData && other.p()->m_placemarkExtendedData)) { return false; } if (p()->m_placemarkExtendedData && other.p()->m_placemarkExtendedData && !(*p()->m_placemarkExtendedData == *other.p()->m_placemarkExtendedData)) { return false; } if ( !p()->m_geometry && !other.p()->m_geometry ) { return true; } else if ( (!p()->m_geometry && other.p()->m_geometry) || (p()->m_geometry && !other.p()->m_geometry) ) { return false; } if ( p()->m_geometry->nodeType() != other.p()->m_geometry->nodeType() ) { return false; } if ( p()->m_geometry->nodeType() == GeoDataTypes::GeoDataPolygonType ) { GeoDataPolygon *thisPoly = dynamic_cast( p()->m_geometry ); GeoDataPolygon *otherPoly = dynamic_cast( other.p()->m_geometry ); Q_ASSERT( thisPoly && otherPoly ); if ( *thisPoly != *otherPoly ) { return false; } } else if ( p()->m_geometry->nodeType() == GeoDataTypes::GeoDataLineStringType ) { GeoDataLineString *thisLine = dynamic_cast( p()->m_geometry ); GeoDataLineString *otherLine = dynamic_cast( other.p()->m_geometry ); Q_ASSERT( thisLine && otherLine ); if ( *thisLine != *otherLine ) { return false; } } else if ( p()->m_geometry->nodeType() == GeoDataTypes::GeoDataModelType ) { GeoDataModel *thisModel = dynamic_cast( p()->m_geometry ); GeoDataModel *otherModel = dynamic_cast( other.p()->m_geometry ); Q_ASSERT( thisModel && otherModel ); if ( *thisModel != *otherModel ) { return false; } /*} else if ( p()->m_geometry->nodeType() == GeoDataTypes::GeoDataMultiGeometryType ) { GeoDataMultiGeometry *thisMG = dynamic_cast( p()->m_geometry ); GeoDataMultiGeometry *otherMG = dynamic_cast( other.p()->m_geometry ); Q_ASSERT( thisMG && otherMG ); if ( *thisMG != *otherMG ) { return false; } */ // Does not have equality operators. I guess they need to be implemented soon. } else if ( p()->m_geometry->nodeType() == GeoDataTypes::GeoDataTrackType ) { GeoDataTrack *thisTrack = dynamic_cast( p()->m_geometry ); GeoDataTrack *otherTrack = dynamic_cast( other.p()->m_geometry ); Q_ASSERT( thisTrack && otherTrack ); if ( *thisTrack != *otherTrack ) { return false; } } else if ( p()->m_geometry->nodeType() == GeoDataTypes::GeoDataMultiTrackType ) { GeoDataMultiTrack *thisMT = dynamic_cast( p()->m_geometry ); GeoDataMultiTrack *otherMT = dynamic_cast( other.p()->m_geometry ); Q_ASSERT( thisMT && otherMT ); if ( *thisMT != *otherMT ) { return false; } } else if ( p()->m_geometry->nodeType() == GeoDataTypes::GeoDataPointType ) { GeoDataPoint *thisPoint = dynamic_cast( p()->m_geometry ); GeoDataPoint *otherPoint = dynamic_cast( other.p()->m_geometry ); Q_ASSERT( thisPoint && otherPoint ); if ( *thisPoint != *otherPoint ) { return false; } } return true; } bool GeoDataPlacemark::operator!=( const GeoDataPlacemark& other ) const { return !this->operator==( other ); } GeoDataPlacemark::GeoDataVisualCategory GeoDataPlacemark::visualCategory() const { return p()->m_visualCategory; } void GeoDataPlacemark::setVisualCategory(GeoDataPlacemark::GeoDataVisualCategory index) { detach(); p()->m_visualCategory = index; } GeoDataPlacemarkPrivate* GeoDataPlacemark::p() { return static_cast(d); } const GeoDataPlacemarkPrivate* GeoDataPlacemark::p() const { return static_cast(d); } GeoDataGeometry* GeoDataPlacemark::geometry() { detach(); p()->m_geometry->setParent( this ); return p()->m_geometry; } const GeoDataGeometry* GeoDataPlacemark::geometry() const { return p()->m_geometry; } const OsmPlacemarkData& GeoDataPlacemark::osmData() const { QVariant &placemarkVariantData = extendedData().valueRef( OsmPlacemarkData::osmHashKey() ).valueRef(); if ( !placemarkVariantData.canConvert() ) { return p()->s_nullOsmPlacemarkData; } OsmPlacemarkData &osmData = *reinterpret_cast( placemarkVariantData.data() ); return osmData; } void GeoDataPlacemark::setOsmData( const OsmPlacemarkData &osmData ) { extendedData().addValue( GeoDataData( OsmPlacemarkData::osmHashKey(), QVariant::fromValue( osmData ) ) ); } OsmPlacemarkData& GeoDataPlacemark::osmData() { QVariant &placemarkVariantData = extendedData().valueRef( OsmPlacemarkData::osmHashKey() ).valueRef(); if ( !placemarkVariantData.canConvert() ) { extendedData().addValue( GeoDataData( OsmPlacemarkData::osmHashKey(), QVariant::fromValue( OsmPlacemarkData() ) ) ); placemarkVariantData = extendedData().valueRef( OsmPlacemarkData::osmHashKey() ).valueRef(); } OsmPlacemarkData &osmData = *reinterpret_cast( placemarkVariantData.data() ); return osmData; } bool GeoDataPlacemark::hasOsmData() const { QVariant &placemarkVariantData = extendedData().valueRef( OsmPlacemarkData::osmHashKey() ).valueRef(); return placemarkVariantData.canConvert(); } void GeoDataPlacemark::clearOsmData() { detach(); extendedData().removeKey(OsmPlacemarkData::osmHashKey()); } const GeoDataLookAt *GeoDataPlacemark::lookAt() const { return dynamic_cast( abstractView() ); } GeoDataLookAt *GeoDataPlacemark::lookAt() { return dynamic_cast( abstractView() ); } bool GeoDataPlacemark::placemarkLayoutOrderCompare(const GeoDataPlacemark *left, const GeoDataPlacemark *right) { if (left->d->m_zoomLevel != right->d->m_zoomLevel) { return (left->d->m_zoomLevel < right->d->m_zoomLevel); // lower zoom level comes first } if (left->d->m_popularity != right->d->m_popularity) { return left->d->m_popularity > right->d->m_popularity; // higher popularity comes first } return left < right; // lower pointer value comes first } GeoDataCoordinates GeoDataPlacemark::coordinate( const QDateTime &dateTime, bool *iconAtCoordinates ) const { bool hasIcon = false; GeoDataCoordinates coord; if( p()->m_geometry ) { // Beware: comparison between pointers, not strings. - if ( p()->m_geometry->nodeType() == GeoDataTypes::GeoDataPointType ) { + if (p()->m_geometry->nodeType() == GeoDataTypes::GeoDataPointType + || p()->m_geometry->nodeType() == GeoDataTypes::GeoDataPolygonType + || p()->m_geometry->nodeType() == GeoDataTypes::GeoDataLinearRingType) { hasIcon = true; - coord = static_cast( p()->m_geometry )->coordinates(); + coord = p()->m_geometry->latLonAltBox().center(); } else if ( p()->m_geometry->nodeType() == GeoDataTypes::GeoDataMultiGeometryType ) { const GeoDataMultiGeometry *multiGeometry = static_cast( p()->m_geometry ); QVector::ConstIterator it = multiGeometry->constBegin(); QVector::ConstIterator end = multiGeometry->constEnd(); for ( ; it != end; ++it ) { - if ( (*it)->nodeType() == GeoDataTypes::GeoDataPointType ) { + if ((*it)->nodeType() == GeoDataTypes::GeoDataPointType + || (*it)->nodeType() == GeoDataTypes::GeoDataPolygonType + || (*it)->nodeType() == GeoDataTypes::GeoDataLinearRingType) { hasIcon = true; break; } } coord = p()->m_geometry->latLonAltBox().center(); } else if ( p()->m_geometry->nodeType() == GeoDataTypes::GeoDataTrackType ) { const GeoDataTrack *track = static_cast( p()->m_geometry ); hasIcon = track->size() != 0 && track->firstWhen() <= dateTime; coord = track->coordinatesAt( dateTime ); } else { coord = p()->m_geometry->latLonAltBox().center(); } } if ( iconAtCoordinates != 0 ) { *iconAtCoordinates = hasIcon; } return coord; } void GeoDataPlacemark::coordinate( qreal& lon, qreal& lat, qreal& alt ) const { coordinate().geoCoordinates( lon, lat, alt ); } void GeoDataPlacemark::setCoordinate( qreal lon, qreal lat, qreal alt, GeoDataPoint::Unit _unit) { setGeometry( new GeoDataPoint(lon, lat, alt, _unit ) ); } void GeoDataPlacemark::setCoordinate( const GeoDataCoordinates &point ) { setGeometry ( new GeoDataPoint( point ) ); } void GeoDataPlacemark::setGeometry( GeoDataGeometry *entry ) { detach(); delete p()->m_geometry; p()->m_geometry = entry; p()->m_geometry->setParent( this ); } QString GeoDataPlacemark::displayName() const { if (hasOsmData()) { OsmPlacemarkData const &data = osmData(); QStringList const uiLanguages = QLocale::system().uiLanguages(); foreach (const QString &uiLanguage, uiLanguages) { for (auto tagIter = data.tagsBegin(), end = data.tagsEnd(); tagIter != end; ++tagIter) { if (tagIter.key().startsWith(QLatin1String("name:"))) { QStringRef const tagLanguage = tagIter.key().midRef(5); if (tagLanguage == uiLanguage) { return tagIter.value(); } } } } } return name(); } QString GeoDataPlacemark::categoryName() const { switch (p()->m_visualCategory) { case Valley: return GeoDataPlacemarkPrivate::tr("Valley"); case OtherTerrain: return GeoDataPlacemarkPrivate::tr("Terrain"); case Crater: return GeoDataPlacemarkPrivate::tr("Crater"); case Mare: return GeoDataPlacemarkPrivate::tr("Sea"); case MannedLandingSite: return GeoDataPlacemarkPrivate::tr("Manned Landing Site"); case RoboticRover: return GeoDataPlacemarkPrivate::tr("Robotic Rover"); case UnmannedSoftLandingSite: return GeoDataPlacemarkPrivate::tr("Unmanned Soft Landing Site"); case UnmannedHardLandingSite: return GeoDataPlacemarkPrivate::tr("Unmanned Hard Landing Site"); case Mons: return GeoDataPlacemarkPrivate::tr("Mountain"); case SmallCity: return GeoDataPlacemarkPrivate::tr("City"); case SmallCountyCapital: return GeoDataPlacemarkPrivate::tr("County Capital"); case SmallStateCapital: return GeoDataPlacemarkPrivate::tr("State Capital"); case SmallNationCapital: return GeoDataPlacemarkPrivate::tr("Nation Capital"); case MediumCity: return GeoDataPlacemarkPrivate::tr("City"); case MediumCountyCapital: return GeoDataPlacemarkPrivate::tr("County Capital"); case MediumStateCapital: return GeoDataPlacemarkPrivate::tr("State Capital"); case MediumNationCapital: return GeoDataPlacemarkPrivate::tr("Nation Capital"); case BigCity: return GeoDataPlacemarkPrivate::tr("City"); case BigCountyCapital: return GeoDataPlacemarkPrivate::tr("County Capital"); case BigStateCapital: return GeoDataPlacemarkPrivate::tr("State Capital"); case BigNationCapital: return GeoDataPlacemarkPrivate::tr("Nation Capital"); case LargeCity: return GeoDataPlacemarkPrivate::tr("City"); case LargeCountyCapital: return GeoDataPlacemarkPrivate::tr("County Capital"); case LargeStateCapital: return GeoDataPlacemarkPrivate::tr("State Capital"); case LargeNationCapital: return GeoDataPlacemarkPrivate::tr("Nation Capital"); case Nation: return GeoDataPlacemarkPrivate::tr("Nation"); case Mountain: return GeoDataPlacemarkPrivate::tr("Mountain"); case Volcano: return GeoDataPlacemarkPrivate::tr("Volcano"); case Continent: return GeoDataPlacemarkPrivate::tr("Continent"); case Ocean: return GeoDataPlacemarkPrivate::tr("Ocean"); case GeographicPole: return GeoDataPlacemarkPrivate::tr("Geographic Pole"); case MagneticPole: return GeoDataPlacemarkPrivate::tr("Magnetic Pole"); case ShipWreck: return GeoDataPlacemarkPrivate::tr("Ship Wreck"); case AirPort: return GeoDataPlacemarkPrivate::tr("Air Port"); case Observatory: return GeoDataPlacemarkPrivate::tr("Observatory"); case MilitaryDangerArea: return GeoDataPlacemarkPrivate::tr("Military Danger Area"); case OsmSite: return GeoDataPlacemarkPrivate::tr("OSM Site"); case Coordinate: return GeoDataPlacemarkPrivate::tr("Coordinate"); case Bookmark: return GeoDataPlacemarkPrivate::tr("Bookmark"); case Satellite: return GeoDataPlacemarkPrivate::tr("Satellite"); // OpenStreetMap categories case PlaceCity: return GeoDataPlacemarkPrivate::tr("City"); case PlaceCityCapital: return GeoDataPlacemarkPrivate::tr("City Capital"); case PlaceSuburb: return GeoDataPlacemarkPrivate::tr("Suburb"); case PlaceHamlet: return GeoDataPlacemarkPrivate::tr("Hamlet"); case PlaceLocality: return GeoDataPlacemarkPrivate::tr("Locality"); case PlaceTown: return GeoDataPlacemarkPrivate::tr("Town"); case PlaceTownCapital: return GeoDataPlacemarkPrivate::tr("Town Capital"); case PlaceVillage: return GeoDataPlacemarkPrivate::tr("Village"); case PlaceVillageCapital: return GeoDataPlacemarkPrivate::tr("Village Capital"); case NaturalWater: return GeoDataPlacemarkPrivate::tr("Water"); case NaturalReef: return GeoDataPlacemarkPrivate::tr("Reef"); case NaturalWood: return GeoDataPlacemarkPrivate::tr("Wood"); case NaturalBeach: return GeoDataPlacemarkPrivate::tr("Beach"); case NaturalWetland: return GeoDataPlacemarkPrivate::tr("Wetland"); case NaturalGlacier: return GeoDataPlacemarkPrivate::tr("Glacier"); case NaturalIceShelf: return GeoDataPlacemarkPrivate::tr("Ice Shelf"); case NaturalScrub: return GeoDataPlacemarkPrivate::tr("Scrub"); case NaturalCliff: return GeoDataPlacemarkPrivate::tr("Cliff"); case NaturalHeath: return GeoDataPlacemarkPrivate::tr("Heath"); case HighwayTrafficSignals: return GeoDataPlacemarkPrivate::tr("Traffic Signals"); case HighwaySteps: return GeoDataPlacemarkPrivate::tr("Steps"); case HighwayUnknown: return GeoDataPlacemarkPrivate::tr("Unknown Road"); case HighwayPath: return GeoDataPlacemarkPrivate::tr("Path"); case HighwayFootway: return GeoDataPlacemarkPrivate::tr("Footway"); case HighwayTrack: return GeoDataPlacemarkPrivate::tr("Track"); case HighwayPedestrian: return GeoDataPlacemarkPrivate::tr("Footway"); case HighwayCycleway: return GeoDataPlacemarkPrivate::tr("Cycleway"); case HighwayService: return GeoDataPlacemarkPrivate::tr("Service Road"); case HighwayRoad: return GeoDataPlacemarkPrivate::tr("Road"); case HighwayResidential: return GeoDataPlacemarkPrivate::tr("Residential Road"); case HighwayLivingStreet: return GeoDataPlacemarkPrivate::tr("Living Street"); case HighwayUnclassified: return GeoDataPlacemarkPrivate::tr("Unclassified Road"); case HighwayTertiaryLink: return GeoDataPlacemarkPrivate::tr("Tertiary Link Road"); case HighwayTertiary: return GeoDataPlacemarkPrivate::tr("Tertiary Road"); case HighwaySecondaryLink: return GeoDataPlacemarkPrivate::tr("Secondary Link Road"); case HighwaySecondary: return GeoDataPlacemarkPrivate::tr("Secondary Road"); case HighwayPrimaryLink: return GeoDataPlacemarkPrivate::tr("Primary Link Road"); case HighwayPrimary: return GeoDataPlacemarkPrivate::tr("Primary Road"); case HighwayTrunkLink: return GeoDataPlacemarkPrivate::tr("Trunk Link Road"); case HighwayTrunk: return GeoDataPlacemarkPrivate::tr("Trunk Road"); case HighwayMotorwayLink: return GeoDataPlacemarkPrivate::tr("Motorway Link Road"); case HighwayMotorway: return GeoDataPlacemarkPrivate::tr("Motorway"); case Building: return GeoDataPlacemarkPrivate::tr("Building"); case AccomodationCamping: return GeoDataPlacemarkPrivate::tr("Camping"); case AccomodationHostel: return GeoDataPlacemarkPrivate::tr("Hostel"); case AccomodationHotel: return GeoDataPlacemarkPrivate::tr("Hotel"); case AccomodationMotel: return GeoDataPlacemarkPrivate::tr("Motel"); case AccomodationYouthHostel: return GeoDataPlacemarkPrivate::tr("Youth Hostel"); case AccomodationGuestHouse: return GeoDataPlacemarkPrivate::tr("Guest House"); case AmenityLibrary: return GeoDataPlacemarkPrivate::tr("Library"); case AmenityKindergarten: return GeoDataPlacemarkPrivate::tr("Kindergarten"); case EducationCollege: return GeoDataPlacemarkPrivate::tr("College"); case EducationSchool: return GeoDataPlacemarkPrivate::tr("School"); case EducationUniversity: return GeoDataPlacemarkPrivate::tr("University"); case FoodBar: return GeoDataPlacemarkPrivate::tr("Bar"); case FoodBiergarten: return GeoDataPlacemarkPrivate::tr("Biergarten"); case FoodCafe: return GeoDataPlacemarkPrivate::tr("Cafe"); case FoodFastFood: return GeoDataPlacemarkPrivate::tr("Fast Food"); case FoodPub: return GeoDataPlacemarkPrivate::tr("Pub"); case FoodRestaurant: return GeoDataPlacemarkPrivate::tr("Restaurant"); case HealthDentist: return GeoDataPlacemarkPrivate::tr("Dentist"); case HealthDoctors: return GeoDataPlacemarkPrivate::tr("Doctors"); case HealthHospital: return GeoDataPlacemarkPrivate::tr("Hospital"); case HealthPharmacy: return GeoDataPlacemarkPrivate::tr("Pharmacy"); case HealthVeterinary: return GeoDataPlacemarkPrivate::tr("Veterinary"); case MoneyAtm: return GeoDataPlacemarkPrivate::tr("ATM"); case MoneyBank: return GeoDataPlacemarkPrivate::tr("Bank"); case AmenityArchaeologicalSite: return GeoDataPlacemarkPrivate::tr("Archaeological Site"); case AmenityEmbassy: return GeoDataPlacemarkPrivate::tr("Embassy"); case AmenityEmergencyPhone: return GeoDataPlacemarkPrivate::tr("Emergency Phone"); case AmenityWaterPark: return GeoDataPlacemarkPrivate::tr("Water Park"); case AmenityCommunityCentre: return GeoDataPlacemarkPrivate::tr("Community Centre"); case AmenityFountain: return GeoDataPlacemarkPrivate::tr("Fountain"); case AmenityNightClub: return GeoDataPlacemarkPrivate::tr("Night Club"); case AmenityBench: return GeoDataPlacemarkPrivate::tr("Bench"); case AmenityCourtHouse: return GeoDataPlacemarkPrivate::tr("Court House"); case AmenityFireStation: return GeoDataPlacemarkPrivate::tr("Fire Station"); case AmenityHuntingStand: return GeoDataPlacemarkPrivate::tr("Hunting Stand"); case AmenityPolice: return GeoDataPlacemarkPrivate::tr("Police"); case AmenityPostBox: return GeoDataPlacemarkPrivate::tr("Post Box"); case AmenityPostOffice: return GeoDataPlacemarkPrivate::tr("Post Office"); case AmenityPrison: return GeoDataPlacemarkPrivate::tr("Prison"); case AmenityRecycling: return GeoDataPlacemarkPrivate::tr("Recycling"); case AmenityShelter: return GeoDataPlacemarkPrivate::tr("Shelter"); case AmenityTelephone: return GeoDataPlacemarkPrivate::tr("Telephone"); case AmenityToilets: return GeoDataPlacemarkPrivate::tr("Toilets"); case AmenityTownHall: return GeoDataPlacemarkPrivate::tr("Town Hall"); case AmenityWasteBasket: return GeoDataPlacemarkPrivate::tr("Waste Basket"); case AmenityDrinkingWater: return GeoDataPlacemarkPrivate::tr("Drinking Water"); case AmenityGraveyard: return GeoDataPlacemarkPrivate::tr("Graveyard"); case BarrierCityWall: return GeoDataPlacemarkPrivate::tr("City Wall"); case BarrierGate: return GeoDataPlacemarkPrivate::tr("Gate"); case BarrierLiftGate: return GeoDataPlacemarkPrivate::tr("Lift Gate"); case BarrierWall: return GeoDataPlacemarkPrivate::tr("Wall"); case NaturalPeak: return GeoDataPlacemarkPrivate::tr("Peak"); case NaturalTree: return GeoDataPlacemarkPrivate::tr("Tree"); case ShopBeverages: return GeoDataPlacemarkPrivate::tr("Beverages"); case ShopHifi: return GeoDataPlacemarkPrivate::tr("Hifi"); case ShopSupermarket: return GeoDataPlacemarkPrivate::tr("Supermarket"); case ShopAlcohol: return GeoDataPlacemarkPrivate::tr("Alcohol"); case ShopBakery: return GeoDataPlacemarkPrivate::tr("Bakery"); case ShopButcher: return GeoDataPlacemarkPrivate::tr("Butcher"); case ShopConfectionery: return GeoDataPlacemarkPrivate::tr("Confectionery"); case ShopConvenience: return GeoDataPlacemarkPrivate::tr("Convenience Shop"); case ShopGreengrocer: return GeoDataPlacemarkPrivate::tr("Greengrocer"); case ShopSeafood: return GeoDataPlacemarkPrivate::tr("Seafood"); case ShopDepartmentStore: return GeoDataPlacemarkPrivate::tr("Department Store"); case ShopKiosk: return GeoDataPlacemarkPrivate::tr("Kiosk"); case ShopBag: return GeoDataPlacemarkPrivate::tr("Bag"); case ShopClothes: return GeoDataPlacemarkPrivate::tr("Clothes"); case ShopFashion: return GeoDataPlacemarkPrivate::tr("Fashion"); case ShopJewelry: return GeoDataPlacemarkPrivate::tr("Jewelry"); case ShopShoes: return GeoDataPlacemarkPrivate::tr("Shoes"); case ShopVarietyStore: return GeoDataPlacemarkPrivate::tr("Variety Store"); case ShopBeauty: return GeoDataPlacemarkPrivate::tr("Beauty"); case ShopChemist: return GeoDataPlacemarkPrivate::tr("Chemist"); case ShopCosmetics: return GeoDataPlacemarkPrivate::tr("Cosmetics"); case ShopHairdresser: return GeoDataPlacemarkPrivate::tr("Hairdresser"); case ShopOptician: return GeoDataPlacemarkPrivate::tr("Optician"); case ShopPerfumery: return GeoDataPlacemarkPrivate::tr("Perfumery"); case ShopDoitYourself: return GeoDataPlacemarkPrivate::tr("Doit Yourself"); case ShopFlorist: return GeoDataPlacemarkPrivate::tr("Florist"); case ShopHardware: return GeoDataPlacemarkPrivate::tr("Hardware"); case ShopFurniture: return GeoDataPlacemarkPrivate::tr("Furniture"); case ShopElectronics: return GeoDataPlacemarkPrivate::tr("Electronics"); case ShopMobilePhone: return GeoDataPlacemarkPrivate::tr("Mobile Phone"); case ShopBicycle: return GeoDataPlacemarkPrivate::tr("Bicycle"); case ShopCar: return GeoDataPlacemarkPrivate::tr("Car"); case ShopCarRepair: return GeoDataPlacemarkPrivate::tr("Car Repair"); case ShopCarParts: return GeoDataPlacemarkPrivate::tr("Car Parts"); case ShopMotorcycle: return GeoDataPlacemarkPrivate::tr("Motorcycle"); case ShopOutdoor: return GeoDataPlacemarkPrivate::tr("Outdoor"); case ShopMusicalInstrument: return GeoDataPlacemarkPrivate::tr("Musical Instrument"); case ShopPhoto: return GeoDataPlacemarkPrivate::tr("Photo"); case ShopBook: return GeoDataPlacemarkPrivate::tr("Book"); case ShopGift: return GeoDataPlacemarkPrivate::tr("Gift"); case ShopStationery: return GeoDataPlacemarkPrivate::tr("Stationery"); case ShopLaundry: return GeoDataPlacemarkPrivate::tr("Laundry"); case ShopPet: return GeoDataPlacemarkPrivate::tr("Pet"); case ShopToys: return GeoDataPlacemarkPrivate::tr("Toys"); case ShopTravelAgency: return GeoDataPlacemarkPrivate::tr("Travel Agency"); case Shop: return GeoDataPlacemarkPrivate::tr("Shop"); case ManmadeBridge: return GeoDataPlacemarkPrivate::tr("Bridge"); case ManmadeLighthouse: return GeoDataPlacemarkPrivate::tr("Lighthouse"); case ManmadePier: return GeoDataPlacemarkPrivate::tr("Pier"); case ManmadeWaterTower: return GeoDataPlacemarkPrivate::tr("Water Tower"); case ManmadeWindMill: return GeoDataPlacemarkPrivate::tr("Wind Mill"); case TouristAttraction: return GeoDataPlacemarkPrivate::tr("Tourist Attraction"); case TouristCastle: return GeoDataPlacemarkPrivate::tr("Castle"); case TouristCinema: return GeoDataPlacemarkPrivate::tr("Cinema"); case TouristInformation: return GeoDataPlacemarkPrivate::tr("Information"); case TouristMonument: return GeoDataPlacemarkPrivate::tr("Monument"); case TouristMuseum: return GeoDataPlacemarkPrivate::tr("Museum"); case TouristRuin: return GeoDataPlacemarkPrivate::tr("Ruin"); case TouristTheatre: return GeoDataPlacemarkPrivate::tr("Theatre"); case TouristThemePark: return GeoDataPlacemarkPrivate::tr("Theme Park"); case TouristViewPoint: return GeoDataPlacemarkPrivate::tr("View Point"); case TouristZoo: return GeoDataPlacemarkPrivate::tr("Zoo"); case TouristAlpineHut: return GeoDataPlacemarkPrivate::tr("Alpine Hut"); case TransportAerodrome: return GeoDataPlacemarkPrivate::tr("Aerodrome"); case TransportHelipad: return GeoDataPlacemarkPrivate::tr("Helipad"); case TransportAirportGate: return GeoDataPlacemarkPrivate::tr("Airport Gate"); case TransportAirportRunway: return GeoDataPlacemarkPrivate::tr("Airport Runway"); case TransportAirportApron: return GeoDataPlacemarkPrivate::tr("Airport Apron"); case TransportAirportTaxiway: return GeoDataPlacemarkPrivate::tr("Airport Taxiway"); case TransportAirportTerminal: return GeoDataPlacemarkPrivate::tr("Airport Terminal"); case TransportBusStation: return GeoDataPlacemarkPrivate::tr("Bus Station"); case TransportBusStop: return GeoDataPlacemarkPrivate::tr("Bus Stop"); case TransportCarShare: return GeoDataPlacemarkPrivate::tr("Car Sharing"); case TransportFuel: return GeoDataPlacemarkPrivate::tr("Gas Station"); case TransportParking: return GeoDataPlacemarkPrivate::tr("Parking"); case TransportParkingSpace: return GeoDataPlacemarkPrivate::tr("Parking Space"); case TransportPlatform: return GeoDataPlacemarkPrivate::tr("Platform"); case TransportRentalBicycle: return GeoDataPlacemarkPrivate::tr("Rental Bicycle"); case TransportRentalCar: return GeoDataPlacemarkPrivate::tr("Rental Car"); case TransportTaxiRank: return GeoDataPlacemarkPrivate::tr("Taxi Rank"); case TransportTrainStation: return GeoDataPlacemarkPrivate::tr("Train Station"); case TransportTramStop: return GeoDataPlacemarkPrivate::tr("Tram Stop"); case TransportBicycleParking: return GeoDataPlacemarkPrivate::tr("Bicycle Parking"); case TransportMotorcycleParking: return GeoDataPlacemarkPrivate::tr("Motorcycle Parking"); case TransportSubwayEntrance: return GeoDataPlacemarkPrivate::tr("Subway Entrance"); case ReligionPlaceOfWorship: return GeoDataPlacemarkPrivate::tr("Place Of Worship"); case ReligionBahai: return GeoDataPlacemarkPrivate::tr("Bahai"); case ReligionBuddhist: return GeoDataPlacemarkPrivate::tr("Buddhist"); case ReligionChristian: return GeoDataPlacemarkPrivate::tr("Christian"); case ReligionMuslim: return GeoDataPlacemarkPrivate::tr("Muslim"); case ReligionHindu: return GeoDataPlacemarkPrivate::tr("Hindu"); case ReligionJain: return GeoDataPlacemarkPrivate::tr("Jain"); case ReligionJewish: return GeoDataPlacemarkPrivate::tr("Jewish"); case ReligionShinto: return GeoDataPlacemarkPrivate::tr("Shinto"); case ReligionSikh: return GeoDataPlacemarkPrivate::tr("Sikh"); case LeisureGolfCourse: return GeoDataPlacemarkPrivate::tr("Golf Course"); case LeisureMarina: return GeoDataPlacemarkPrivate::tr("Marina"); case LeisurePark: return GeoDataPlacemarkPrivate::tr("Park"); case LeisurePlayground: return GeoDataPlacemarkPrivate::tr("Playground"); case LeisurePitch: return GeoDataPlacemarkPrivate::tr("Pitch"); case LeisureSportsCentre: return GeoDataPlacemarkPrivate::tr("Sports Centre"); case LeisureStadium: return GeoDataPlacemarkPrivate::tr("Stadium"); case LeisureTrack: return GeoDataPlacemarkPrivate::tr("Track"); case LeisureSwimmingPool: return GeoDataPlacemarkPrivate::tr("Swimming Pool"); case LanduseAllotments: return GeoDataPlacemarkPrivate::tr("Allotments"); case LanduseBasin: return GeoDataPlacemarkPrivate::tr("Basin"); case LanduseCemetery: return GeoDataPlacemarkPrivate::tr("Cemetery"); case LanduseCommercial: return GeoDataPlacemarkPrivate::tr("Commercial"); case LanduseConstruction: return GeoDataPlacemarkPrivate::tr("Construction"); case LanduseFarmland: return GeoDataPlacemarkPrivate::tr("Farmland"); case LanduseFarmyard: return GeoDataPlacemarkPrivate::tr("Farmyard"); case LanduseGarages: return GeoDataPlacemarkPrivate::tr("Garages"); case LanduseGrass: return GeoDataPlacemarkPrivate::tr("Grass"); case LanduseIndustrial: return GeoDataPlacemarkPrivate::tr("Industrial"); case LanduseLandfill: return GeoDataPlacemarkPrivate::tr("Landfill"); case LanduseMeadow: return GeoDataPlacemarkPrivate::tr("Meadow"); case LanduseMilitary: return GeoDataPlacemarkPrivate::tr("Military"); case LanduseQuarry: return GeoDataPlacemarkPrivate::tr("Quarry"); case LanduseRailway: return GeoDataPlacemarkPrivate::tr("Railway"); case LanduseReservoir: return GeoDataPlacemarkPrivate::tr("Reservoir"); case LanduseResidential: return GeoDataPlacemarkPrivate::tr("Residential"); case LanduseRetail: return GeoDataPlacemarkPrivate::tr("Retail"); case LanduseOrchard: return GeoDataPlacemarkPrivate::tr("Orchard"); case LanduseVineyard: return GeoDataPlacemarkPrivate::tr("Vineyard"); case RailwayRail: return GeoDataPlacemarkPrivate::tr("Rail"); case RailwayNarrowGauge: return GeoDataPlacemarkPrivate::tr("Narrow Gauge"); case RailwayTram: return GeoDataPlacemarkPrivate::tr("Tram"); case RailwayLightRail: return GeoDataPlacemarkPrivate::tr("Light Rail"); case RailwayAbandoned: return GeoDataPlacemarkPrivate::tr("Abandoned Railway"); case RailwaySubway: return GeoDataPlacemarkPrivate::tr("Subway"); case RailwayPreserved: return GeoDataPlacemarkPrivate::tr("Preserved Railway"); case RailwayMiniature: return GeoDataPlacemarkPrivate::tr("Miniature Railway"); case RailwayConstruction: return GeoDataPlacemarkPrivate::tr("Railway Construction"); case RailwayMonorail: return GeoDataPlacemarkPrivate::tr("Monorail"); case RailwayFunicular: return GeoDataPlacemarkPrivate::tr("Funicular Railway"); case PowerTower: return GeoDataPlacemarkPrivate::tr("Power Tower"); case AdminLevel1: return GeoDataPlacemarkPrivate::tr("Admin Boundary (Level 1)"); case AdminLevel2: return GeoDataPlacemarkPrivate::tr("Admin Boundary (Level 2)"); case AdminLevel3: return GeoDataPlacemarkPrivate::tr("Admin Boundary (Level 3)"); case AdminLevel4: return GeoDataPlacemarkPrivate::tr("Admin Boundary (Level 4)"); case AdminLevel5: return GeoDataPlacemarkPrivate::tr("Admin Boundary (Level 5)"); case AdminLevel6: return GeoDataPlacemarkPrivate::tr("Admin Boundary (Level 6)"); case AdminLevel7: return GeoDataPlacemarkPrivate::tr("Admin Boundary (Level 7)"); case AdminLevel8: return GeoDataPlacemarkPrivate::tr("Admin Boundary (Level 8)"); case AdminLevel9: return GeoDataPlacemarkPrivate::tr("Admin Boundary (Level 9)"); case AdminLevel10: return GeoDataPlacemarkPrivate::tr("Admin Boundary (Level 10)"); case AdminLevel11: return GeoDataPlacemarkPrivate::tr("Admin Boundary (Level 11)"); case BoundaryMaritime: return GeoDataPlacemarkPrivate::tr("Boundary (Maritime)"); case Landmass: return GeoDataPlacemarkPrivate::tr("Land Mass"); case UrbanArea: return GeoDataPlacemarkPrivate::tr("Urban Area"); case InternationalDateLine: return GeoDataPlacemarkPrivate::tr("International Date Line"); case Bathymetry: return GeoDataPlacemarkPrivate::tr("Bathymetry"); case Default: case Unknown: case None: case LastIndex: return QString(); } return QString(); } qreal GeoDataPlacemark::area() const { return p()->m_placemarkExtendedData ? p()->m_placemarkExtendedData->m_area : -1.0; } void GeoDataPlacemark::setArea( qreal area ) { if (area == -1.0 && !p()->m_placemarkExtendedData) { return; // nothing to do } detach(); p()->m_geometry->setParent( this ); p()->placemarkExtendedData().m_area = area; } qint64 GeoDataPlacemark::population() const { return p()->m_population; } void GeoDataPlacemark::setPopulation( qint64 population ) { detach(); p()->m_geometry->setParent( this ); p()->m_population = population; } const QString GeoDataPlacemark::state() const { return p()->m_placemarkExtendedData ? p()->m_placemarkExtendedData->m_state : QString(); } void GeoDataPlacemark::setState( const QString &state ) { if (state.isEmpty() && !p()->m_placemarkExtendedData) { return; // nothing to do } detach(); p()->m_geometry->setParent( this ); p()->placemarkExtendedData().m_state = state; } const QString GeoDataPlacemark::countryCode() const { return p()->m_placemarkExtendedData ? p()->m_placemarkExtendedData->m_countrycode : QString(); } void GeoDataPlacemark::setCountryCode( const QString &countrycode ) { if (countrycode.isEmpty() && !p()->m_placemarkExtendedData) { return; // nothing to do } detach(); p()->m_geometry->setParent( this ); p()->placemarkExtendedData().m_countrycode = countrycode; } bool GeoDataPlacemark::isBalloonVisible() const { return p()->m_placemarkExtendedData ? p()->m_placemarkExtendedData->m_isBalloonVisible : false; } void GeoDataPlacemark::setBalloonVisible( bool visible ) { if (!visible && !p()->m_placemarkExtendedData) { return; // nothing to do } detach(); p()->m_geometry->setParent( this ); p()->placemarkExtendedData().m_isBalloonVisible = visible; } void GeoDataPlacemark::pack( QDataStream& stream ) const { GeoDataFeature::pack( stream ); stream << p()->placemarkExtendedData().m_countrycode; stream << p()->placemarkExtendedData().m_area; stream << p()->m_population; if ( p()->m_geometry ) { stream << p()->m_geometry->geometryId(); p()->m_geometry->pack( stream ); } else { stream << InvalidGeometryId; } } QXmlStreamWriter& GeoDataPlacemark::pack( QXmlStreamWriter& stream ) const { stream.writeStartElement( "placemark" ); stream.writeEndElement(); return stream; } QXmlStreamWriter& GeoDataPlacemark::operator <<( QXmlStreamWriter& stream ) const { pack( stream ); return stream; } void GeoDataPlacemark::unpack( QDataStream& stream ) { detach(); p()->m_geometry->setParent( this ); GeoDataFeature::unpack( stream ); stream >> p()->placemarkExtendedData().m_countrycode; stream >> p()->placemarkExtendedData().m_area; stream >> p()->m_population; int geometryId; stream >> geometryId; switch( geometryId ) { case InvalidGeometryId: break; case GeoDataPointId: { GeoDataPoint* point = new GeoDataPoint; point->unpack( stream ); delete p()->m_geometry; p()->m_geometry = point; } break; case GeoDataLineStringId: { GeoDataLineString* lineString = new GeoDataLineString; lineString->unpack( stream ); delete p()->m_geometry; p()->m_geometry = lineString; } break; case GeoDataLinearRingId: { GeoDataLinearRing* linearRing = new GeoDataLinearRing; linearRing->unpack( stream ); delete p()->m_geometry; p()->m_geometry = linearRing; } break; case GeoDataPolygonId: { GeoDataPolygon* polygon = new GeoDataPolygon; polygon->unpack( stream ); delete p()->m_geometry; p()->m_geometry = polygon; } break; case GeoDataMultiGeometryId: { GeoDataMultiGeometry* multiGeometry = new GeoDataMultiGeometry; multiGeometry->unpack( stream ); delete p()->m_geometry; p()->m_geometry = multiGeometry; } break; case GeoDataModelId: break; default: break; }; } } diff --git a/src/lib/marble/geodata/graphicsitem/BuildingGeoPolygonGraphicsItem.cpp b/src/lib/marble/geodata/graphicsitem/BuildingGeoPolygonGraphicsItem.cpp index e9f73d37c..a44fa2aad 100644 --- a/src/lib/marble/geodata/graphicsitem/BuildingGeoPolygonGraphicsItem.cpp +++ b/src/lib/marble/geodata/graphicsitem/BuildingGeoPolygonGraphicsItem.cpp @@ -1,440 +1,397 @@ // // 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 "BuildingGeoPolygonGraphicsItem.h" #include "MarbleDebug.h" #include "ViewportParams.h" #include "GeoDataTypes.h" #include "GeoDataPlacemark.h" #include "GeoDataLatLonAltBox.h" #include "GeoDataLinearRing.h" #include "GeoDataPolygon.h" #include "GeoDataIconStyle.h" #include "GeoDataPolyStyle.h" #include "OsmPlacemarkData.h" #include "GeoPainter.h" namespace Marble { BuildingGeoPolygonGraphicsItem::BuildingGeoPolygonGraphicsItem(const GeoDataPlacemark *placemark, const GeoDataPolygon *polygon) : AbstractGeoPolygonGraphicsItem(placemark, polygon) , m_buildingHeight(polygon->latLonAltBox().maxAltitude() - polygon->latLonAltBox().minAltitude()) - , m_buildingLabel(extractBuildingLabel(*placemark)) , m_entries(extractNamedEntries(*placemark)) { setZValue(this->zValue() + m_buildingHeight); Q_ASSERT(m_buildingHeight > 0.0); QStringList paintLayers; paintLayers << QStringLiteral("Polygon/Building/frame") << QStringLiteral("Polygon/Building/roof"); setPaintLayers(paintLayers); } BuildingGeoPolygonGraphicsItem::BuildingGeoPolygonGraphicsItem(const GeoDataPlacemark *placemark, const GeoDataLinearRing* ring) : AbstractGeoPolygonGraphicsItem(placemark, ring) , m_buildingHeight(ring->latLonAltBox().maxAltitude() - ring->latLonAltBox().minAltitude()) - , m_buildingLabel(extractBuildingLabel(*placemark)) , m_entries(extractNamedEntries(*placemark)) { setZValue(this->zValue() + m_buildingHeight); Q_ASSERT(m_buildingHeight > 0.0); QStringList paintLayers; paintLayers << QStringLiteral("Polygon/Building/frame") << QStringLiteral("Polygon/Building/roof"); setPaintLayers(paintLayers); } void BuildingGeoPolygonGraphicsItem::initializeBuildingPainting(const GeoPainter* painter, const ViewportParams *viewport, bool &drawAccurate3D, bool &isCameraAboveBuilding, bool &hasInnerBoundaries, QVector& outlinePolygons, QVector& innerPolygons) const { drawAccurate3D = false; isCameraAboveBuilding = false; QPointF offsetAtCorner = buildingOffset(QPointF(0, 0), viewport, &isCameraAboveBuilding); qreal maxOffset = qMax( qAbs( offsetAtCorner.x() ), qAbs( offsetAtCorner.y() ) ); drawAccurate3D = painter->mapQuality() == HighQuality ? maxOffset > 5.0 : maxOffset > 8.0; // 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, outlinePolygons); } viewport->screenCoordinates(polygon()->outerBoundary(), outlinePolygons); } else if (ring()) { viewport->screenCoordinates(*ring(), outlinePolygons); } } QPointF BuildingGeoPolygonGraphicsItem::centroid(const QPolygonF &polygon, double &area) { auto centroid = QPointF(0.0, 0.0); area = 0.0; for (auto i=0, n=polygon.size(); i 0.0); qreal const buildingFactor = m_buildingHeight / 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); } -QString BuildingGeoPolygonGraphicsItem::extractBuildingLabel(const GeoDataPlacemark &placemark) -{ - if (!placemark.name().isEmpty()) { - return placemark.name(); - } - - const OsmPlacemarkData &osmData = placemark.osmData(); - - auto tagIter = osmData.findTag(QStringLiteral("addr:housename")); - if (tagIter != osmData.tagsEnd()) { - return tagIter.value(); - } - - tagIter = osmData.findTag(QStringLiteral("addr:housenumber")); - if (tagIter != osmData.tagsEnd()) { - return tagIter.value(); - } - - return QString(); -} - QVector BuildingGeoPolygonGraphicsItem::extractNamedEntries(const GeoDataPlacemark &placemark) { QVector entries; const auto end = placemark.osmData().nodeReferencesEnd(); for (auto iter = placemark.osmData().nodeReferencesBegin(); iter != end; ++iter) { const auto tagIter = iter.value().findTag(QStringLiteral("addr:housenumber")); if (tagIter != iter.value().tagsEnd()) { NamedEntry entry; entry.point = iter.key(); entry.label = tagIter.value(); entries.push_back(entry); } } return entries; } void BuildingGeoPolygonGraphicsItem::paint(GeoPainter* painter, const ViewportParams* viewport, const QString &layer) { if (layer.endsWith(QLatin1String("/frame"))) { paintFrame(painter, viewport); } else if (layer.endsWith(QLatin1String("/roof"))) { paintRoof(painter, viewport); } else { mDebug() << "Didn't expect to have to paint layer " << layer << ", ignoring it."; } } void BuildingGeoPolygonGraphicsItem::paintRoof(GeoPainter* painter, const ViewportParams* viewport) { bool drawAccurate3D; bool isCameraAboveBuilding; bool hasInnerBoundaries; QVector outlinePolygons; QVector innerPolygons; initializeBuildingPainting(painter, viewport, drawAccurate3D, isCameraAboveBuilding, hasInnerBoundaries, outlinePolygons, innerPolygons); if (!isCameraAboveBuilding) { return; // do not render roof if we look inside the building } painter->save(); QPen const currentPen = configurePainter(painter, viewport); - bool const hasIcon = !style()->iconStyle().iconPath().isEmpty(); qreal maxSize(0.0); QPointF roofCenter; if (hasInnerBoundaries) { painter->setPen(Qt::NoPen); } // first paint the area and icon (and the outline if there are no inner boundaries) double maxArea = 0.0; foreach(QPolygonF* outlinePolygon, outlinePolygons) { QRectF const boundingRect = outlinePolygon->boundingRect(); QPolygonF buildingRoof; - if (hasIcon || !m_buildingLabel.isEmpty() || !m_entries.isEmpty()) { + if (!m_entries.isEmpty()) { QSizeF const polygonSize = boundingRect.size(); qreal size = polygonSize.width() * polygonSize.height(); if (size > maxSize) { maxSize = size; double area; roofCenter = centroid(*outlinePolygon, area); maxArea = qMax(area, maxArea); roofCenter += buildingOffset(roofCenter, viewport); } } if ( drawAccurate3D) { buildingRoof.reserve(outlinePolygon->size()); foreach(const QPointF &point, *outlinePolygon) { buildingRoof << point + buildingOffset(point, viewport); } if (hasInnerBoundaries) { QRegion clip(buildingRoof.toPolygon()); foreach(QPolygonF* innerPolygon, innerPolygons) { QPolygonF buildingInner; buildingInner.reserve(innerPolygon->size()); foreach(const QPointF &point, *innerPolygon) { buildingInner << point + buildingOffset(point, viewport); } clip-=QRegion(buildingInner.toPolygon()); } painter->setClipRegion(clip); } painter->drawPolygon(buildingRoof); } else { QPointF const offset = buildingOffset(boundingRect.center(), viewport); painter->translate(offset); if (hasInnerBoundaries) { QRegion clip(outlinePolygon->toPolygon()); foreach(QPolygonF* clipPolygon, innerPolygons) { clip-=QRegion(clipPolygon->toPolygon()); } painter->setClipRegion(clip); } painter->drawPolygon(*outlinePolygon); painter->translate(-offset); } - - if (hasIcon && !roofCenter.isNull()) { - QImage const icon = style()->iconStyle().scaledIcon(); - QPointF const iconCenter(icon.size().width()/2.0, icon.size().height()/2.0); - painter->drawImage(roofCenter-iconCenter, icon); - } else if (drawAccurate3D && !m_buildingLabel.isEmpty() && !roofCenter.isNull()) { - double const w2 = 0.5 * painter->fontMetrics().width(m_buildingLabel); - 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 (buildingRoof.containsPoint(textPosition + QPointF(-2, -ascent), Qt::OddEvenFill) - && buildingRoof.containsPoint(textPosition + QPointF(-2, descent), Qt::OddEvenFill) - && buildingRoof.containsPoint(textPosition + QPointF(2+2*w2, descent), Qt::OddEvenFill) - && buildingRoof.containsPoint(textPosition + QPointF(2+2*w2, -ascent), Qt::OddEvenFill) - ) { - painter->drawText(textPosition, m_buildingLabel); - } - } } // Render additional housenumbers at building entries if (!m_entries.isEmpty() && maxArea > 1600 * m_entries.size()) { QBrush brush = painter->brush(); QColor const brushColor = brush.color(); QColor lighterColor = brushColor.lighter(110); lighterColor.setAlphaF(0.9); brush.setColor(lighterColor); painter->setBrush(brush); foreach(const auto &entry, m_entries) { qreal x, y; viewport->screenCoordinates(entry.point, x, y); QPointF point(x, y); point += buildingOffset(point, viewport); auto const width = painter->fontMetrics().width(entry.label); auto const height = painter->fontMetrics().height(); QRectF rectangle(point, QSizeF(qMax(1.2*width, 1.1*height), 1.2*height)); rectangle.moveCenter(point); painter->drawRoundedRect(rectangle, 3, 3); painter->drawText(rectangle, Qt::AlignCenter, entry.label); } brush.setColor(brushColor); painter->setBrush(brush); } // then paint the outlines if there are inner boundaries if (hasInnerBoundaries) { painter->setPen(currentPen); foreach(QPolygonF * polygon, outlinePolygons) { QRectF const boundingRect = polygon->boundingRect(); if ( drawAccurate3D) { QPolygonF buildingRoof; buildingRoof.reserve(polygon->size()); foreach(const QPointF &point, *polygon) { buildingRoof << point + buildingOffset(point, viewport); } painter->drawPolyline(buildingRoof); } else { QPointF const offset = buildingOffset(boundingRect.center(), viewport); painter->translate(offset); painter->drawPolyline(*polygon); painter->translate(-offset); } } } qDeleteAll(outlinePolygons); painter->restore(); } void BuildingGeoPolygonGraphicsItem::paintFrame(GeoPainter *painter, const ViewportParams *viewport) { // TODO: how does this match the Q_ASSERT in the constructor? if (m_buildingHeight == 0.0) { return; } if ((polygon() && !viewport->resolves(polygon()->outerBoundary().latLonAltBox(), 4)) || (ring() && !viewport->resolves(ring()->latLonAltBox(), 4))) { return; } painter->save(); bool drawAccurate3D; bool isCameraAboveBuilding; bool hasInnerBoundaries; QVector outlinePolygons; QVector innerPolygons; initializeBuildingPainting(painter, viewport, drawAccurate3D, isCameraAboveBuilding, hasInnerBoundaries, outlinePolygons, innerPolygons); configureFramePainter(painter); foreach(QPolygonF* outlinePolygon, outlinePolygons) { if (outlinePolygon->isEmpty()) { continue; } if ( drawAccurate3D && isCameraAboveBuilding ) { // draw the building sides int const size = outlinePolygon->size(); QPointF & a = (*outlinePolygon)[0]; QPointF shiftA = a + buildingOffset(a, viewport); for (int i=1; isetPen(QPen(painter->brush().color(), 1.5)); } painter->drawPolygon(buildingSide); a = b; shiftA = shiftB; } } else { // don't draw the building sides - just draw the base frame instead if (hasInnerBoundaries) { QRegion clip(outlinePolygon->toPolygon()); foreach(QPolygonF* clipPolygon, innerPolygons) { clip-=QRegion(clipPolygon->toPolygon()); } painter->setClipRegion(clip); } painter->drawPolygon(*outlinePolygon); } } qDeleteAll(outlinePolygons); painter->restore(); } void BuildingGeoPolygonGraphicsItem::screenPolygons(const ViewportParams *viewport, const GeoDataPolygon *polygon, QVector &innerPolygons, QVector &outlines) { Q_ASSERT(polygon); QVector outerPolygons; viewport->screenCoordinates( polygon->outerBoundary(), outerPolygons ); outlines << outerPolygons; QVector innerBoundaries = polygon->innerBoundaries(); foreach (const GeoDataLinearRing &innerBoundary, innerBoundaries) { QVector innerPolygonsPerBoundary; viewport->screenCoordinates(innerBoundary, innerPolygonsPerBoundary); outlines << innerPolygonsPerBoundary; innerPolygons.reserve(innerPolygons.size() + innerPolygonsPerBoundary.size()); foreach( QPolygonF* innerPolygonPerBoundary, innerPolygonsPerBoundary ) { innerPolygons << innerPolygonPerBoundary; } } } void BuildingGeoPolygonGraphicsItem::configureFramePainter(GeoPainter *painter) const { GeoDataStyle::ConstPtr style = this->style(); if (!style) { painter->setPen( QPen() ); } else { const GeoDataPolyStyle& polyStyle = style->polyStyle(); const QColor transparentColor(Qt::transparent); QPen currentPen = painter->pen(); currentPen.setColor(transparentColor); painter->setPen(currentPen); if (!polyStyle.fill()) { painter->setBrush(transparentColor); } else { const QColor paintedColor = polyStyle.paintedColor(); painter->setBrush(paintedColor.darker(150)); } } } } diff --git a/src/lib/marble/geodata/graphicsitem/BuildingGeoPolygonGraphicsItem.h b/src/lib/marble/geodata/graphicsitem/BuildingGeoPolygonGraphicsItem.h index bb7a662ad..64f7d6e55 100644 --- a/src/lib/marble/geodata/graphicsitem/BuildingGeoPolygonGraphicsItem.h +++ b/src/lib/marble/geodata/graphicsitem/BuildingGeoPolygonGraphicsItem.h @@ -1,60 +1,58 @@ // // 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 // #ifndef MARBLE_BUILDINGGEOPOLYGONGRAPHICSITEM_H #define MARBLE_BUILDINGGEOPOLYGONGRAPHICSITEM_H #include "AbstractGeoPolygonGraphicsItem.h" #include "GeoDataCoordinates.h" class QPointF; namespace Marble { class MARBLE_EXPORT BuildingGeoPolygonGraphicsItem : public AbstractGeoPolygonGraphicsItem { public: explicit BuildingGeoPolygonGraphicsItem(const GeoDataPlacemark *placemark, const GeoDataPolygon *polygon); explicit BuildingGeoPolygonGraphicsItem(const GeoDataPlacemark *placemark, const GeoDataLinearRing *ring); public: virtual void paint(GeoPainter* painter, const ViewportParams *viewport, const QString &layer); private: struct NamedEntry { GeoDataCoordinates point; QString label; }; void paintFrame(GeoPainter* painter, const ViewportParams *viewport); void paintRoof(GeoPainter* painter, const ViewportParams *viewport); void configureFramePainter(GeoPainter *painter) const; void initializeBuildingPainting(const GeoPainter* painter, const ViewportParams *viewport, bool &drawAccurate3D, bool &isCameraAboveBuilding, bool &hasInnerBoundaries, QVector& outlinePolygons, QVector& innerPolygons) const; QPointF buildingOffset(const QPointF &point, const ViewportParams *viewport, bool* isCameraAboveBuilding = nullptr) const; static QPointF centroid(const QPolygonF &polygon, double &area); static void screenPolygons(const ViewportParams *viewport, const GeoDataPolygon* polygon, QVector &polygons, QVector &outlines); - static QString extractBuildingLabel(const GeoDataPlacemark &placemark); static QVector extractNamedEntries(const GeoDataPlacemark &placemark); private: const double m_buildingHeight; - const QString m_buildingLabel; const QVector m_entries; }; } #endif diff --git a/src/plugins/runner/osm/OsmWay.cpp b/src/plugins/runner/osm/OsmWay.cpp index 1b2a02b9f..f36979018 100644 --- a/src/plugins/runner/osm/OsmWay.cpp +++ b/src/plugins/runner/osm/OsmWay.cpp @@ -1,221 +1,227 @@ // // 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 2015 Dennis Nienhüser // #include #include #include #include #include #include #include #include #include #include namespace Marble { QSet OsmWay::s_areaTags; void OsmWay::create(GeoDataDocument *document, const OsmNodes &nodes, QSet &usedNodes) const { const double height = extractBuildingHeight(m_osmData); OsmPlacemarkData osmData = m_osmData; GeoDataGeometry *geometry = 0; if (isArea()) { GeoDataLinearRing linearRing; foreach(qint64 nodeId, m_references) { auto const nodeIter = nodes.constFind(nodeId); if (nodeIter == nodes.constEnd()) { return; } OsmNode const & node = nodeIter.value(); osmData.addNodeReference(node.coordinates(), node.osmData()); linearRing.append(node.coordinates()); usedNodes << nodeId; } linearRing = GeoDataLinearRing(linearRing.optimized()); if (!linearRing.isEmpty() && height != 0) { linearRing.first().setAltitude(height); } geometry = new GeoDataLinearRing(linearRing); } else { GeoDataLineString lineString; foreach(qint64 nodeId, m_references) { auto const nodeIter = nodes.constFind(nodeId); if (nodeIter == nodes.constEnd()) { return; } OsmNode const & node = nodeIter.value(); osmData.addNodeReference(node.coordinates(), node.osmData()); lineString.append(node.coordinates()); usedNodes << nodeId; } lineString = lineString.optimized(); if (!lineString.isEmpty() && height != 0) { lineString.first().setAltitude(height); } geometry = new GeoDataLineString(lineString); } Q_ASSERT(geometry != nullptr); if (height != 0) { geometry->setAltitudeMode(RelativeToGround); } OsmObjectManager::registerId(m_osmData.id()); GeoDataPlacemark *placemark = new GeoDataPlacemark; placemark->setGeometry(geometry); placemark->setVisualCategory(StyleBuilder::determineVisualCategory(m_osmData)); placemark->setName(m_osmData.tagValue(QStringLiteral("name"))); if (placemark->name().isEmpty()) { placemark->setName(m_osmData.tagValue(QStringLiteral("ref"))); } + if (placemark->name().isEmpty()) { + placemark->setName(m_osmData.tagValue(QStringLiteral("addr:housename"))); + } + if (placemark->name().isEmpty()) { + placemark->setName(m_osmData.tagValue(QStringLiteral("addr:housenumber"))); + } placemark->setOsmData(osmData); placemark->setVisible(placemark->visualCategory() != GeoDataPlacemark::None); document->append(placemark); } const QVector &OsmWay::references() const { return m_references; } OsmPlacemarkData &OsmWay::osmData() { return m_osmData; } const OsmPlacemarkData &OsmWay::osmData() const { return m_osmData; } void OsmWay::addReference(qint64 id) { m_references << id; } bool OsmWay::isArea() const { // @TODO A single OSM way can be both closed and non-closed, e.g. landuse=grass with barrier=fence. // We need to create two separate ways in cases like that to support this. // See also https://wiki.openstreetmap.org/wiki/Key:area bool const isLinearFeature = m_osmData.containsTag(QStringLiteral("area"), QStringLiteral("no")) || m_osmData.containsTagKey(QStringLiteral("highway")) || m_osmData.containsTagKey(QStringLiteral("barrier")); if (isLinearFeature) { return false; } bool const isAreaFeature = m_osmData.containsTagKey(QStringLiteral("landuse")); if (isAreaFeature) { return true; } for (auto iter = m_osmData.tagsBegin(), end=m_osmData.tagsEnd(); iter != end; ++iter) { const auto tag = StyleBuilder::OsmTag(iter.key(), iter.value()); if (isAreaTag(tag)) { return true; } } bool const isImplicitlyClosed = m_references.size() > 1 && m_references.front() == m_references.last(); return isImplicitlyClosed; } bool OsmWay::isAreaTag(const StyleBuilder::OsmTag &keyValue) { if (s_areaTags.isEmpty()) { // All these tags can be found updated at // http://wiki.openstreetmap.org/wiki/Map_Features#Landuse s_areaTags.insert(StyleBuilder::OsmTag(QStringLiteral("natural"), QStringLiteral("water"))); s_areaTags.insert(StyleBuilder::OsmTag(QStringLiteral("natural"), QStringLiteral("wood"))); s_areaTags.insert(StyleBuilder::OsmTag(QStringLiteral("natural"), QStringLiteral("beach"))); s_areaTags.insert(StyleBuilder::OsmTag(QStringLiteral("natural"), QStringLiteral("wetland"))); s_areaTags.insert(StyleBuilder::OsmTag(QStringLiteral("natural"), QStringLiteral("glacier"))); s_areaTags.insert(StyleBuilder::OsmTag(QStringLiteral("natural"), QStringLiteral("scrub"))); s_areaTags.insert(StyleBuilder::OsmTag(QStringLiteral("natural"), QStringLiteral("cliff"))); s_areaTags.insert(StyleBuilder::OsmTag(QStringLiteral("area"), QStringLiteral("yes"))); s_areaTags.insert(StyleBuilder::OsmTag(QStringLiteral("waterway"), QStringLiteral("riverbank"))); foreach (const auto tag, StyleBuilder::buildingTags()) { s_areaTags.insert(tag); } s_areaTags.insert(StyleBuilder::OsmTag(QStringLiteral("man_made"), QStringLiteral("bridge"))); s_areaTags.insert(StyleBuilder::OsmTag(QStringLiteral("amenity"), QStringLiteral("graveyard"))); s_areaTags.insert(StyleBuilder::OsmTag(QStringLiteral("amenity"), QStringLiteral("parking"))); s_areaTags.insert(StyleBuilder::OsmTag(QStringLiteral("amenity"), QStringLiteral("parking_space"))); s_areaTags.insert(StyleBuilder::OsmTag(QStringLiteral("amenity"), QStringLiteral("bicycle_parking"))); s_areaTags.insert(StyleBuilder::OsmTag(QStringLiteral("amenity"), QStringLiteral("college"))); s_areaTags.insert(StyleBuilder::OsmTag(QStringLiteral("amenity"), QStringLiteral("hospital"))); s_areaTags.insert(StyleBuilder::OsmTag(QStringLiteral("amenity"), QStringLiteral("kindergarten"))); s_areaTags.insert(StyleBuilder::OsmTag(QStringLiteral("amenity"), QStringLiteral("school"))); s_areaTags.insert(StyleBuilder::OsmTag(QStringLiteral("amenity"), QStringLiteral("university"))); s_areaTags.insert(StyleBuilder::OsmTag(QStringLiteral("leisure"), QStringLiteral("common"))); s_areaTags.insert(StyleBuilder::OsmTag(QStringLiteral("leisure"), QStringLiteral("garden"))); s_areaTags.insert(StyleBuilder::OsmTag(QStringLiteral("leisure"), QStringLiteral("golf_course"))); s_areaTags.insert(StyleBuilder::OsmTag(QStringLiteral("leisure"), QStringLiteral("marina"))); s_areaTags.insert(StyleBuilder::OsmTag(QStringLiteral("leisure"), QStringLiteral("playground"))); s_areaTags.insert(StyleBuilder::OsmTag(QStringLiteral("leisure"), QStringLiteral("pitch"))); s_areaTags.insert(StyleBuilder::OsmTag(QStringLiteral("leisure"), QStringLiteral("park"))); s_areaTags.insert(StyleBuilder::OsmTag(QStringLiteral("leisure"), QStringLiteral("sports_centre"))); s_areaTags.insert(StyleBuilder::OsmTag(QStringLiteral("leisure"), QStringLiteral("stadium"))); s_areaTags.insert(StyleBuilder::OsmTag(QStringLiteral("leisure"), QStringLiteral("swimming_pool"))); s_areaTags.insert(StyleBuilder::OsmTag(QStringLiteral("leisure"), QStringLiteral("track"))); s_areaTags.insert(StyleBuilder::OsmTag(QStringLiteral("military"), QStringLiteral("danger_area"))); s_areaTags.insert(StyleBuilder::OsmTag(QStringLiteral("marble_land"), QStringLiteral("landmass"))); s_areaTags.insert(StyleBuilder::OsmTag(QStringLiteral("settlement"), QStringLiteral("yes"))); } return s_areaTags.contains(keyValue); } double OsmWay::extractBuildingHeight(const OsmPlacemarkData &osmData) { double height = 8.0; QHash::const_iterator tagIter; if ((tagIter = osmData.findTag(QStringLiteral("height"))) != osmData.tagsEnd()) { /** @todo Also parse non-SI units, see https://wiki.openstreetmap.org/wiki/Key:height#Height_of_buildings */ QString const heightValue = QString(tagIter.value()).remove(QStringLiteral(" meters")).remove(QStringLiteral(" m")); bool extracted = false; double extractedHeight = heightValue.toDouble(&extracted); if (extracted) { height = extractedHeight; } } else if ((tagIter = osmData.findTag(QStringLiteral("building:levels"))) != osmData.tagsEnd()) { int const levels = tagIter.value().toInt(); int const skipLevels = osmData.tagValue(QStringLiteral("building:min_level")).toInt(); /** @todo Is 35 as an upper bound for the number of levels sane? */ height = 3.0 * qBound(1, 1+levels-skipLevels, 35); } return qBound(1.0, height, 1000.0); } }