diff --git a/src/lib/marble/MarbleGlobal.h b/src/lib/marble/MarbleGlobal.h index 6ecd4e92b..463688657 100644 --- a/src/lib/marble/MarbleGlobal.h +++ b/src/lib/marble/MarbleGlobal.h @@ -1,310 +1,313 @@ // // 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 2007-2009 Torsten Rahn // Copyright 2007 Inge Wallin // #ifndef MARBLE_GLOBAL_H #define MARBLE_GLOBAL_H #include #include #include "marble_export.h" #include "MarbleColors.h" // #define QT_STRICT_ITERATORS namespace Marble { enum TessellationFlag { NoTessellation = 0x0, Tessellate = 0x1, RespectLatitudeCircle = 0x2, FollowGround = 0x4, PreventNodeFiltering = 0x8 }; Q_DECLARE_FLAGS(TessellationFlags, TessellationFlag) /** * @brief This enum is used to choose the projection shown in the view. */ enum Projection { Spherical, ///< Spherical projection ("Orthographic") Equirectangular, ///< Flat projection ("plate carree") Mercator, ///< Mercator projection Gnomonic, ///< Gnomonic projection Stereographic, ///< Stereographic projection LambertAzimuthal, ///< Lambert Azimuthal Equal-Area projection AzimuthalEquidistant, ///< Azimuthal Equidistant projection VerticalPerspective ///< Vertical perspective projection // NOTE: MarbleWidget::setProjection(int) relies on VerticalPerspective being the last // value above. Adjust that method if you do changes here }; /** * @brief This enum is used to choose the dimension. */ enum Dimension { Latitude, ///< Latitude Longitude ///< Longitude }; Q_DECLARE_FLAGS(Dimensions, Dimension) /** * @brief This enum is used to choose the unit chosen to measure angles. */ enum AngleUnit { DMSDegree, ///< Degrees in DMS notation DecimalDegree, ///< Degrees in decimal notation UTM ///< UTM }; /** * @brief This enum is used to choose context in which map quality gets used. */ enum ViewContext { Still, ///< still image Animation ///< animated view (e.g. while rotating the globe) }; /** * @brief This enum is used to choose the map quality shown in the view. */ enum MapQuality { OutlineQuality, ///< Only a wire representation is drawn LowQuality, ///< Low resolution (e.g. interlaced) NormalQuality, ///< Normal quality HighQuality, ///< High quality (e.g. antialiasing for lines) PrintQuality ///< Print quality }; /** * @brief This enum is used to specify the proxy that is used. */ enum ProxyType { HttpProxy, ///< Uses an Http proxy Socks5Proxy ///< Uses a Socks5Proxy }; /** * @brief This enum is used to choose the localization of the labels. */ enum LabelPositionFlag { NoLabel = 0x0, LineStart = 0x1, LineCenter = 0x2, LineEnd = 0x4, IgnoreXMargin = 0x8, IgnoreYMargin = 0x10, FollowLine = 0x20 }; Q_DECLARE_FLAGS(LabelPositionFlags, LabelPositionFlag) /** * @brief This enum is used to choose the localization of the labels. */ enum LabelLocalization { CustomAndNative, ///< Custom and native labels Custom, ///< Shows the name in the user's language Native ///< Display the name in the official language and /// glyphs of the labeled place. }; /** * @brief This enum is used to choose how the globe behaves while dragging. */ enum DragLocation { KeepAxisVertically, ///< Keep planet axis vertically FollowMousePointer ///< Follow mouse pointer exactly }; /** * @brief This enum is used to choose how the globe behaves while dragging. */ enum OnStartup { ShowHomeLocation, ///< Show home location on startup LastLocationVisited ///< Show last location visited on quit }; enum AltitudeMode { ClampToGround, ///< Altitude always sticks to ground level RelativeToGround, ///< Altitude is always given relative to ground level Absolute, ///< Altitude is given relative to the sealevel RelativeToSeaFloor, ///< Altitude is given relative to the sea floor ClampToSeaFloor ///< Altitude always sticks to sea floor }; enum Pole { AnyPole, ///< Any pole NorthPole, ///< Only North Pole SouthPole ///< Only South Pole }; /** * @brief This enum is used to describe the type of download */ enum DownloadUsage { DownloadBulk, ///< Bulk download, for example "File/Download region" DownloadBrowse ///< Browsing mode, normal operation of Marble, like a web browser }; /** * @brief Describes possible flight mode (interpolation between source * and target camera positions) */ enum FlyToMode { Automatic, ///< A sane value is chosen automatically depending on animation settings and the action Instant, ///< Change camera position immediately (no interpolation) Linear, ///< Linear interpolation of lon, lat and distance to ground Jump ///< Linear interpolation of lon and lat, distance increases towards the middle point, then decreases }; /** * @brief Search mode: Global (worldwide) versus area (local, regional) search */ enum SearchMode { GlobalSearch, ///< Search a whole planet AreaSearch ///< Search a certain region of a planet (e.g. visible region) }; /** * @brief */ enum RenderStatus { Complete, ///< All data is there and up to date WaitingForUpdate, ///< Rendering is based on complete, but outdated data, data update was requested WaitingForData, ///< Rendering is based on no or partial data, more data was requested (e.g. pending network queries) Incomplete ///< Data is missing and some error occurred when trying to retrieve it (e.g. network failure) }; const int defaultLevelZeroColumns = 2; const int defaultLevelZeroRows = 1; // Conversion Metric / Imperial System: km vs. miles const qreal MI2KM = 1.609344; const qreal KM2MI = 1.0 / MI2KM; // Conversion Nautical / Imperial System: nm vs. km const qreal NM2KM = 1.852; const qreal KM2NM = 1.0 / NM2KM; const qreal NM2FT = 6080; // nm feet // Conversion Metric / Imperial System: meter vs. feet const qreal M2FT = 3.2808; const qreal FT2M = 1.0 / M2FT; // Conversion Metric / Imperial System: meter vs inch const qreal M2IN = 39.3701; const qreal IN2M = 1.0 / M2IN; +// Interconversion between Imperial System: feet vs inch +const qreal FT2IN = 12.0; + // Conversion Metric / Imperial System: meter vs yard const qreal M2YD = 1.09361; const qreal YD2M = 1.0 / M2YD; // Conversion meter vs millimeter const qreal M2MM = 1000.0; const qreal MM2M = 1.0 / M2MM; // Conversion meter vs centimeter const qreal M2CM = 100.0; const qreal CM2M = 1.0 / M2CM; // Conversion degree vs. radians const qreal DEG2RAD = M_PI / 180.0; const qreal RAD2DEG = 180.0 / M_PI; // Conversion meter vs kilometer const qreal KM2METER = 1000.0; const qreal METER2KM = 1.0 / KM2METER; //Conversion hour vs minute const qreal HOUR2MIN = 60.0; const qreal MIN2HOUR = 1.0 / HOUR2MIN; //Conversion (time) minute vs second const qreal MIN2SEC = 60.0; const qreal SEC2MIN = 1.0 / MIN2SEC; //Conversion hour vs second const qreal HOUR2SEC = 3600.0; const qreal SEC2HOUR = 1.0 / HOUR2SEC; // Version definitions to use with an external application (as digiKam) // String for about dialog and http user agent // FIXME: check if blanks are allowed in user agent version numbers const QString MARBLE_VERSION_STRING = QString::fromLatin1( "0.27.20 (0.28 development version)" ); // API Version id: // form : 0xMMmmpp // MM = major revision. // mm = minor revision. // pp = patch revision. #define MARBLE_VERSION 0x001b14 static const char NOT_AVAILABLE[] = QT_TRANSLATE_NOOP("Marble", "not available"); const int tileDigits = 6; // Average earth radius in m // Deprecated: Please use model()->planetRadius() instead. const qreal EARTH_RADIUS = 6378137.0; // Maximum level of base tiles const int maxBaseTileLevel = 4; // Default size (width and height) of tiles const unsigned int c_defaultTileSize = 675; class MarbleGlobalPrivate; class MarbleLocale; class MARBLE_EXPORT MarbleGlobal { public: static MarbleGlobal * getInstance(); ~MarbleGlobal(); MarbleLocale * locale() const; enum Profile { Default = 0x0, SmallScreen = 0x1, HighResolution = 0x2 }; Q_DECLARE_FLAGS( Profiles, Profile ) Profiles profiles() const; void setProfiles( Profiles profiles ); /** @deprecated Profiles are detected automatically now. This only returns profiles() anymore */ MARBLE_DEPRECATED static Profiles detectProfiles(); private: MarbleGlobal(); Q_DISABLE_COPY( MarbleGlobal ) MarbleGlobalPrivate * const d; }; } Q_DECLARE_OPERATORS_FOR_FLAGS( Marble::TessellationFlags ) Q_DECLARE_OPERATORS_FOR_FLAGS( Marble::LabelPositionFlags ) Q_DECLARE_OPERATORS_FOR_FLAGS( Marble::MarbleGlobal::Profiles ) #endif diff --git a/src/lib/marble/geodata/CMakeLists.txt b/src/lib/marble/geodata/CMakeLists.txt index 2105d0571..fa11284b7 100644 --- a/src/lib/marble/geodata/CMakeLists.txt +++ b/src/lib/marble/geodata/CMakeLists.txt @@ -1,500 +1,500 @@ SET ( geodata_data_SRCS geodata/data/LonLatParser.cpp geodata/data/GeoDataRegion.cpp geodata/data/GeoDataUpdate.cpp geodata/data/GeoDataViewVolume.cpp geodata/data/GeoDataHotSpot.cpp geodata/data/GeoDataAlias.cpp geodata/data/GeoDataImagePyramid.cpp geodata/data/GeoDataGeometry.cpp geodata/data/GeoDataPoint.cpp geodata/data/GeoDataPhotoOverlay.cpp geodata/data/GeoDataTimePrimitive.cpp geodata/data/GeoDataVec2.cpp geodata/data/GeoDataBalloonStyle.cpp geodata/data/GeoDataNetworkLink.cpp geodata/data/GeoDataLineStyle.cpp geodata/data/GeoDataScreenOverlay.cpp geodata/data/GeoDataContainer.cpp geodata/data/GeoDataItemIcon.cpp geodata/data/GeoDataTour.cpp geodata/data/GeoDataTourPrimitive.cpp geodata/data/GeoDataOverlay.cpp geodata/data/GeoDataListStyle.cpp geodata/data/GeoDataFlyTo.cpp geodata/data/GeoDataMultiTrack.cpp geodata/data/GeoDataSnippet.cpp geodata/data/GeoDataStyle.cpp geodata/data/GeoDataLinearRing.cpp geodata/data/GeoDataFolder.cpp geodata/data/GeoDataDocument.cpp geodata/data/GeoDataLatLonAltBox.cpp geodata/data/GeoDataStyleSelector.cpp geodata/data/GeoDataLod.cpp geodata/data/GeoDataColorStyle.cpp geodata/data/GeoDataData.cpp geodata/data/GeoDataLocation.cpp geodata/data/GeoDataPolygon.cpp geodata/data/GeoDataLineString.cpp geodata/data/GeoDataOrientation.cpp geodata/data/GeoDataLookAt.cpp geodata/data/GeoDataPlacemark.cpp geodata/data/GeoDataPlaylist.cpp geodata/data/GeoDataPolyStyle.cpp geodata/data/GeoDataModel.cpp geodata/data/GeoDataLink.cpp geodata/data/GeoDataIconStyle.cpp geodata/data/GeoDataAbstractView.cpp geodata/data/GeoDataStyleMap.cpp geodata/data/GeoDataExtendedData.cpp geodata/data/GeoDataLabelStyle.cpp geodata/data/GeoDataTimeSpan.cpp geodata/data/GeoDataMultiGeometry.cpp geodata/data/GeoDataRelation.cpp geodata/data/GeoDataSimpleArrayData.cpp geodata/data/GeoDataObject.cpp geodata/data/GeoDataLatLonQuad.cpp geodata/data/GeoDataCoordinates.cpp geodata/data/GeoDataTrack.cpp geodata/data/GeoDataNetworkLinkControl.cpp geodata/data/GeoDataFeature.cpp geodata/data/GeoDataCamera.cpp geodata/data/GeoDataTimeStamp.cpp geodata/data/GeoDataGroundOverlay.cpp geodata/data/GeoDataLatLonBox.cpp geodata/data/GeoDataScale.cpp geodata/data/GeoDataResourceMap.cpp geodata/data/GeoDataTourControl.cpp geodata/data/GeoDataAccuracy.cpp geodata/data/GeoDataWait.cpp geodata/data/GeoDataSoundCue.cpp geodata/data/GeoDataAnimatedUpdate.cpp geodata/data/GeoDataSchema.cpp geodata/data/GeoDataSimpleField.cpp geodata/data/GeoDataChange.cpp geodata/data/GeoDataCreate.cpp geodata/data/GeoDataDelete.cpp geodata/data/GeoDataSchemaData.cpp geodata/data/GeoDataSimpleData.cpp geodata/data/GeoDataBuilding.cpp ) SET ( geodata_scene_SRCS geodata/scene/GeoSceneAbstractTileProjection.cpp geodata/scene/GeoSceneMercatorTileProjection.cpp geodata/scene/GeoSceneEquirectTileProjection.cpp geodata/scene/GeoSceneIcon.cpp geodata/scene/GeoSceneTileDataset.cpp geodata/scene/GeoSceneVectorTileDataset.cpp geodata/scene/GeoSceneGeodata.cpp geodata/scene/GeoSceneGroup.cpp geodata/scene/GeoSceneZoom.cpp geodata/scene/GeoSceneLegend.cpp geodata/scene/GeoSceneTextureTileDataset.cpp geodata/scene/GeoSceneAbstractDataset.cpp geodata/scene/GeoSceneItem.cpp geodata/scene/GeoSceneLicense.cpp geodata/scene/GeoSceneSection.cpp geodata/scene/GeoSceneFilter.cpp geodata/scene/GeoSceneHead.cpp geodata/scene/GeoSceneVector.cpp geodata/scene/GeoSceneSettings.cpp geodata/scene/GeoSceneDocument.cpp geodata/scene/GeoSceneMap.cpp geodata/scene/GeoSceneProperty.cpp geodata/scene/GeoSceneLayer.cpp geodata/scene/GeoScenePalette.cpp ) # handlers and writers sources SET ( geodata_handlers_dgml_SRCS geodata/handlers/dgml/DgmlFilterTagHandler.h geodata/handlers/dgml/DgmlHeadingTagHandler.h geodata/handlers/dgml/DgmlDocumentTagHandler.cpp geodata/handlers/dgml/DgmlMinimumTagHandler.cpp geodata/handlers/dgml/DgmlHeadTagHandler.h geodata/handlers/dgml/DgmlLayerTagHandler.cpp geodata/handlers/dgml/DgmlMinimumTagHandler.h geodata/handlers/dgml/DgmlTargetTagHandler.cpp geodata/handlers/dgml/DgmlSourceFileTagHandler.h geodata/handlers/dgml/DgmlSourceFileTagHandler.cpp geodata/handlers/dgml/DgmlGeodataTagHandler.h geodata/handlers/dgml/DgmlDownloadPolicyTagHandler.h geodata/handlers/dgml/DgmlProjectionTagHandler.cpp geodata/handlers/dgml/DgmlLegendTagHandler.cpp geodata/handlers/dgml/DgmlTargetTagHandler.h geodata/handlers/dgml/DgmlAuxillaryDictionary.cpp geodata/handlers/dgml/DgmlGeodataTagHandler.cpp geodata/handlers/dgml/DgmlBlendingTagHandler.h geodata/handlers/dgml/DgmlSourceDirTagHandler.cpp geodata/handlers/dgml/DgmlValueTagHandler.h geodata/handlers/dgml/DgmlMapTagHandler.h geodata/handlers/dgml/DgmlPropertyTagHandler.h geodata/handlers/dgml/DgmlAttributeDictionary.h geodata/handlers/dgml/DgmlThemeTagHandler.cpp geodata/handlers/dgml/DgmlInstallMapTagHandler.h geodata/handlers/dgml/DgmlPropertyTagHandler.cpp geodata/handlers/dgml/DgmlAvailableTagHandler.h geodata/handlers/dgml/DgmlMaximumTagHandler.h geodata/handlers/dgml/DgmlHeadTagHandler.cpp geodata/handlers/dgml/DgmlLegendTagHandler.h geodata/handlers/dgml/DgmlProjectionTagHandler.h geodata/handlers/dgml/DgmlAttributeDictionary.cpp geodata/handlers/dgml/DgmlInstallMapTagHandler.cpp geodata/handlers/dgml/DgmlVectorTagHandler.h geodata/handlers/dgml/DgmlDiscreteTagHandler.cpp geodata/handlers/dgml/DgmlNameTagHandler.cpp geodata/handlers/dgml/DgmlLicenseTagHandler.h geodata/handlers/dgml/DgmlAvailableTagHandler.cpp geodata/handlers/dgml/DgmlStorageLayoutTagHandler.h geodata/handlers/dgml/DgmlThemeTagHandler.h geodata/handlers/dgml/DgmlSettingsTagHandler.h geodata/handlers/dgml/DgmlDescriptionTagHandler.h geodata/handlers/dgml/DgmlBrushTagHandler.cpp geodata/handlers/dgml/DgmlSectionTagHandler.h geodata/handlers/dgml/DgmlTextureTagHandler.h geodata/handlers/dgml/DgmlDownloadUrlTagHandler.cpp geodata/handlers/dgml/DgmlBlendingTagHandler.cpp geodata/handlers/dgml/DgmlBrushTagHandler.h geodata/handlers/dgml/DgmlZoomTagHandler.h geodata/handlers/dgml/DgmlVectorTagHandler.cpp geodata/handlers/dgml/DgmlDiscreteTagHandler.h geodata/handlers/dgml/DgmlMapTagHandler.cpp geodata/handlers/dgml/DgmlVectortileTagHandler.cpp geodata/handlers/dgml/DgmlVisibleTagHandler.cpp geodata/handlers/dgml/DgmlHeadingTagHandler.cpp geodata/handlers/dgml/DgmlTileSizeTagHandler.h geodata/handlers/dgml/DgmlPenTagHandler.h geodata/handlers/dgml/DgmlZoomTagHandler.cpp geodata/handlers/dgml/DgmlTileSizeTagHandler.cpp geodata/handlers/dgml/DgmlTextureTagHandler.cpp geodata/handlers/dgml/DgmlPenTagHandler.cpp geodata/handlers/dgml/DgmlDescriptionTagHandler.cpp geodata/handlers/dgml/DgmlElementDictionary.h geodata/handlers/dgml/DgmlIconTagHandler.cpp geodata/handlers/dgml/DgmlValueTagHandler.cpp geodata/handlers/dgml/DgmlLicenseTagHandler.cpp geodata/handlers/dgml/DgmlTextTagHandler.cpp geodata/handlers/dgml/DgmlMaximumTagHandler.cpp geodata/handlers/dgml/DgmlPaletteTagHandler.cpp geodata/handlers/dgml/DgmlGroupTagHandler.cpp geodata/handlers/dgml/DgmlDownloadUrlTagHandler.h geodata/handlers/dgml/DgmlItemTagHandler.h geodata/handlers/dgml/DgmlSourceDirTagHandler.h geodata/handlers/dgml/DgmlGroupTagHandler.h geodata/handlers/dgml/DgmlStorageLayoutTagHandler.cpp geodata/handlers/dgml/DgmlTextTagHandler.h geodata/handlers/dgml/DgmlFilterTagHandler.cpp geodata/handlers/dgml/DgmlVisibleTagHandler.h geodata/handlers/dgml/DgmlSectionTagHandler.cpp geodata/handlers/dgml/DgmlDownloadPolicyTagHandler.cpp geodata/handlers/dgml/DgmlIconTagHandler.h geodata/handlers/dgml/DgmlNameTagHandler.h geodata/handlers/dgml/DgmlDocumentTagHandler.h geodata/handlers/dgml/DgmlPaletteTagHandler.h geodata/handlers/dgml/DgmlVectortileTagHandler.h geodata/handlers/dgml/DgmlElementDictionary.cpp geodata/handlers/dgml/DgmlItemTagHandler.cpp geodata/handlers/dgml/DgmlSettingsTagHandler.cpp geodata/handlers/dgml/DgmlAuxillaryDictionary.h geodata/handlers/dgml/DgmlLayerTagHandler.h geodata/handlers/dgml/DgmlRenderOrderTagHandler.cpp geodata/handlers/dgml/DgmlRenderOrderTagHandler.h ) SET ( geodata_writers_dgml_SRCS geodata/writers/dgml/DgmlSectionTagWriter.h geodata/writers/dgml/DgmlSettingsTagWriter.cpp geodata/writers/dgml/DgmlTagWriter.h geodata/writers/dgml/DgmlItemTagWriter.h geodata/writers/dgml/DgmlSettingsTagWriter.h geodata/writers/dgml/DgmlMapTagWriter.cpp geodata/writers/dgml/DgmlSectionTagWriter.cpp geodata/writers/dgml/DgmlGeodataTagWriter.h geodata/writers/dgml/DgmlVectorTagWriter.h geodata/writers/dgml/DgmlGeodataTagWriter.cpp geodata/writers/dgml/DgmlLayerTagWriter.cpp geodata/writers/dgml/DgmlDocumentTagWriter.cpp geodata/writers/dgml/DgmlTextureTagWriter.h geodata/writers/dgml/DgmlMapTagWriter.h geodata/writers/dgml/DgmlVectorTagWriter.cpp geodata/writers/dgml/DgmlDocumentTagWriter.h geodata/writers/dgml/DgmlHeadTagWriter.cpp geodata/writers/dgml/DgmlLegendTagWriter.cpp geodata/writers/dgml/DgmlLegendTagWriter.h geodata/writers/dgml/DgmlHeadTagWriter.h geodata/writers/dgml/DgmlTextureTagWriter.cpp geodata/writers/dgml/DgmlTagWriter.cpp geodata/writers/dgml/DgmlLayerTagWriter.h geodata/writers/dgml/DgmlItemTagWriter.cpp ) SET( geodata_graphicsitem_SRCS geodata/graphicsitem/GeoLineStringGraphicsItem.cpp geodata/graphicsitem/GeoPhotoGraphicsItem.cpp geodata/graphicsitem/GeoPolygonGraphicsItem.cpp geodata/graphicsitem/AbstractGeoPolygonGraphicsItem.cpp - geodata/graphicsitem/BuildingGeoPolygonGraphicsItem.cpp + geodata/graphicsitem/BuildingGraphicsItem.cpp geodata/graphicsitem/GeoTrackGraphicsItem.cpp geodata/graphicsitem/ScreenOverlayGraphicsItem.cpp ) SET ( geodata_handlers_kml_SRCS geodata/handlers/kml/KmlAddressTagHandler.cpp geodata/handlers/kml/KmlAltitudeModeTagHandler.cpp geodata/handlers/kml/KmlAltitudeTagHandler.cpp geodata/handlers/kml/KmlAnimatedUpdateTagHandler.cpp geodata/handlers/kml/KmlAreaTagHandler.cpp geodata/handlers/kml/KmlBalloonStyleTagHandler.cpp geodata/handlers/kml/KmlBeginTagHandler.cpp geodata/handlers/kml/KmlBgColorTagHandler.cpp geodata/handlers/kml/KmlCameraTagHandler.cpp geodata/handlers/kml/KmlChangeTagHandler.cpp geodata/handlers/kml/KmlColorModeTagHandler.cpp geodata/handlers/kml/KmlColorTagHandler.cpp geodata/handlers/kml/KmlCoordinatesTagHandler.cpp geodata/handlers/kml/KmlCountrycodeTagHandler.cpp geodata/handlers/kml/KmlCountryNameCodeTagHandler.cpp geodata/handlers/kml/KmlDataTagHandler.cpp geodata/handlers/kml/KmlDescriptionTagHandler.cpp geodata/handlers/kml/KmlDisplayModeTagHandler.cpp geodata/handlers/kml/KmlDisplayNameTagHandler.cpp geodata/handlers/kml/KmlDocumentTagHandler.cpp geodata/handlers/kml/KmlDurationTagHandler.cpp geodata/handlers/kml/KmldrawOrderTagHandler.cpp geodata/handlers/kml/KmlEastTagHandler.cpp geodata/handlers/kml/KmlElementDictionary.cpp geodata/handlers/kml/KmlEndTagHandler.cpp geodata/handlers/kml/KmlExtendedDataTagHandler.cpp geodata/handlers/kml/KmlExtrudeTagHandler.cpp geodata/handlers/kml/KmlFillTagHandler.cpp geodata/handlers/kml/KmlFlyToModeTagHandler.cpp geodata/handlers/kml/KmlFlyToTagHandler.cpp geodata/handlers/kml/KmlFlyToViewTagHandler.cpp geodata/handlers/kml/KmlFolderTagHandler.cpp geodata/handlers/kml/KmlGroundOverlayTagHandler.cpp geodata/handlers/kml/KmlGxAltitudeModeTagHandler.cpp geodata/handlers/kml/KmlGxTimeSpanTagHandler.cpp geodata/handlers/kml/KmlGxTimeStampTagHandler.cpp geodata/handlers/kml/KmlHeadingTagHandler.cpp geodata/handlers/kml/KmlHotSpotTagHandler.cpp geodata/handlers/kml/KmlHrefTagHandler.cpp geodata/handlers/kml/KmlHttpQueryTagHandler.cpp geodata/handlers/kml/KmlIconStyleTagHandler.cpp geodata/handlers/kml/KmlIconTagHandler.cpp geodata/handlers/kml/KmlInnerBoundaryIsTagHandler.cpp geodata/handlers/kml/KmlItemIconTagHandler.cpp geodata/handlers/kml/KmlKeyTagHandler.cpp geodata/handlers/kml/KmlLabelStyleTagHandler.cpp geodata/handlers/kml/KmlLatitudeTagHandler.cpp geodata/handlers/kml/KmlLatLonAltBoxTagHandler.cpp geodata/handlers/kml/KmlLatLonBoxTagHandler.cpp geodata/handlers/kml/KmlLatLonQuadTagHandler.cpp geodata/handlers/kml/KmlLinearRingTagHandler.cpp geodata/handlers/kml/KmlLineStringTagHandler.cpp geodata/handlers/kml/KmlLineStyleTagHandler.cpp geodata/handlers/kml/KmlLinkTagHandler.cpp geodata/handlers/kml/KmlListItemTypeTagHandler.cpp geodata/handlers/kml/KmlListStyleTagHandler.cpp geodata/handlers/kml/KmlLodTagHandler.cpp geodata/handlers/kml/KmlLongitudeTagHandler.cpp geodata/handlers/kml/KmlLookAtTagHandler.cpp geodata/handlers/kml/KmlMarblePlacemarkTagHandler.cpp geodata/handlers/kml/KmlMaxAltitudeTagHandler.cpp geodata/handlers/kml/KmlMaxFadeExtentTagHandler.cpp geodata/handlers/kml/KmlMaxLodPixelsTagHandler.cpp geodata/handlers/kml/KmlMinAltitudeTagHandler.cpp geodata/handlers/kml/KmlMinFadeExtentTagHandler.cpp geodata/handlers/kml/KmlMinLodPixelsTagHandler.cpp geodata/handlers/kml/KmlModelTagHandler.cpp geodata/handlers/kml/KmlMultiGeometryTagHandler.cpp geodata/handlers/kml/KmlMultiTrackTagHandler.cpp geodata/handlers/kml/KmlMxSizeTagHandler.cpp geodata/handlers/kml/KmlNameTagHandler.cpp geodata/handlers/kml/KmlNetworkLinkTagHandler.cpp geodata/handlers/kml/KmlNorthTagHandler.cpp geodata/handlers/kml/KmlObjectTagHandler.cpp geodata/handlers/kml/KmlOpenTagHandler.cpp geodata/handlers/kml/KmlOuterBoundaryIsTagHandler.cpp geodata/handlers/kml/KmlOutlineTagHandler.cpp geodata/handlers/kml/KmlOverlayXYTagHandler.cpp geodata/handlers/kml/KmlPairTagHandler.cpp geodata/handlers/kml/KmlPhotoOverlayTagHandler.cpp geodata/handlers/kml/KmlPlacemarkTagHandler.cpp geodata/handlers/kml/KmlPlaylistTagHandler.cpp geodata/handlers/kml/KmlPointTagHandler.cpp geodata/handlers/kml/KmlPolygonTagHandler.cpp geodata/handlers/kml/KmlPolyStyleTagHandler.cpp geodata/handlers/kml/KmlPopTagHandler.cpp geodata/handlers/kml/KmlRangeTagHandler.cpp geodata/handlers/kml/KmlRefreshIntervalTagHandler.cpp geodata/handlers/kml/KmlRefreshModeTagHandler.cpp geodata/handlers/kml/KmlRefreshVisibilityTagHandler.cpp geodata/handlers/kml/KmlRegionTagHandler.cpp geodata/handlers/kml/KmlRoleTagHandler.cpp geodata/handlers/kml/KmlRollTagHandler.cpp geodata/handlers/kml/KmlRotationTagHandler.cpp geodata/handlers/kml/KmlRotationXYTagHandler.cpp geodata/handlers/kml/Kml_scaleTagHandler.cpp geodata/handlers/kml/KmlSchemaDataTagHandler.cpp geodata/handlers/kml/KmlSchemaTagHandler.cpp geodata/handlers/kml/KmlScreenOverlayTagHandler.cpp geodata/handlers/kml/KmlScreenXYTagHandler.cpp geodata/handlers/kml/KmlSimpleArrayDataTagHandler.cpp geodata/handlers/kml/KmlSimpleDataTagHandler.cpp geodata/handlers/kml/KmlSimpleFieldTagHandler.cpp geodata/handlers/kml/KmlSizeTagHandler.cpp geodata/handlers/kml/KmlSouthTagHandler.cpp geodata/handlers/kml/KmlStateTagHandler.cpp geodata/handlers/kml/KmlStyleMapTagHandler.cpp geodata/handlers/kml/KmlStyleTagHandler.cpp geodata/handlers/kml/KmlStyleUrlTagHandler.cpp geodata/handlers/kml/KmlTessellateTagHandler.cpp geodata/handlers/kml/KmlTextColorTagHandler.cpp geodata/handlers/kml/KmlTextTagHandler.cpp geodata/handlers/kml/KmlTiltTagHandler.cpp geodata/handlers/kml/KmlTimeSpanTagHandler.cpp geodata/handlers/kml/KmlTimeStampTagHandler.cpp geodata/handlers/kml/KmlTourTagHandler.cpp geodata/handlers/kml/KmlTourControlTagHandler.cpp geodata/handlers/kml/KmlTrackTagHandler.cpp geodata/handlers/kml/KmlValueTagHandler.cpp geodata/handlers/kml/KmlViewBoundScaleTagHandler.cpp geodata/handlers/kml/KmlVisibilityTagHandler.cpp geodata/handlers/kml/KmlWaitTagHandler.cpp geodata/handlers/kml/KmlWestTagHandler.cpp geodata/handlers/kml/KmlWhenTagHandler.cpp geodata/handlers/kml/KmlWidthTagHandler.cpp geodata/handlers/kml/KmlViewFormatTagHandler.cpp geodata/handlers/kml/KmlViewRefreshModeTagHandler.cpp geodata/handlers/kml/KmlViewRefreshTimeTagHandler.cpp geodata/handlers/kml/KmlViewVolumeTagHandler.cpp geodata/handlers/kml/KmlLeftFovTagHandler.cpp geodata/handlers/kml/KmlRightFovTagHandler.cpp geodata/handlers/kml/KmlBottomFovTagHandler.cpp geodata/handlers/kml/KmlTopFovTagHandler.cpp geodata/handlers/kml/KmlNearTagHandler.cpp geodata/handlers/kml/KmlImagePyramidTagHandler.cpp geodata/handlers/kml/KmlTileSizeTagHandler.cpp geodata/handlers/kml/KmlMaxHeightTagHandler.cpp geodata/handlers/kml/KmlMaxWidthTagHandler.cpp geodata/handlers/kml/KmlGridOriginTagHandler.cpp geodata/handlers/kml/KmlShapeTagHandler.cpp geodata/handlers/kml/KmlMinRefreshPeriodTagHandler.cpp geodata/handlers/kml/KmlMaxSessionLengthTagHandler.cpp geodata/handlers/kml/KmlCookieTagHandler.cpp geodata/handlers/kml/KmlMessageTagHandler.cpp geodata/handlers/kml/KmlLinkNameTagHandler.cpp geodata/handlers/kml/KmlLinkDescriptionTagHandler.cpp geodata/handlers/kml/KmlLinkSnippetTagHandler.cpp geodata/handlers/kml/KmlSnippetTagHandler.cpp geodata/handlers/kml/KmlExpiresTagHandler.cpp geodata/handlers/kml/KmlUpdateTagHandler.cpp geodata/handlers/kml/KmlNetworkLinkControlTagHandler.cpp geodata/handlers/kml/KmlplayModeTagHandler.cpp geodata/handlers/kml/KmlOrientationTagHandler.cpp geodata/handlers/kml/KmlScaleTagHandler.cpp geodata/handlers/kml/KmlXTagHandler.cpp geodata/handlers/kml/KmlYTagHandler.cpp geodata/handlers/kml/KmlZTagHandler.cpp geodata/handlers/kml/KmlLocationTagHandler.cpp geodata/handlers/kml/KmlResourceMapTagHandler.cpp geodata/handlers/kml/KmlAliasTagHandler.cpp geodata/handlers/kml/KmlSourceHrefTagHandler.cpp geodata/handlers/kml/KmlTargetHrefTagHandler.cpp geodata/handlers/kml/KmlSoundCueTagHandler.cpp geodata/handlers/kml/KmldelayedStartTagHandler.cpp geodata/handlers/kml/KmlBalloonVisibilityTagHandler.cpp geodata/handlers/kml/KmlCreateTagHandler.cpp geodata/handlers/kml/KmlDeleteTagHandler.cpp geodata/handlers/kml/KmlOsmPlacemarkDataTagHandler.cpp geodata/handlers/kml/KmlTagTagHandler.cpp geodata/handlers/kml/KmlMemberTagHandler.cpp geodata/handlers/kml/KmlNdTagHandler.cpp ) SET ( geodata_writers_kml_SRCS geodata/writers/kml/KmlAnimatedUpdateTagWriter.cpp geodata/writers/kml/KmlBalloonStyleTagWriter.cpp geodata/writers/kml/KmlCameraTagWriter.cpp geodata/writers/kml/KmlColorStyleTagWriter.cpp geodata/writers/kml/KmlDataTagWriter.cpp geodata/writers/kml/KmlDocumentTagWriter.cpp geodata/writers/kml/KmlExtendedDataTagWriter.cpp geodata/writers/kml/KmlFeatureTagWriter.cpp geodata/writers/kml/KmlFolderTagWriter.cpp geodata/writers/kml/KmlFlyToTagWriter.cpp geodata/writers/kml/KmlGroundOverlayWriter.cpp geodata/writers/kml/KmlIconStyleTagWriter.cpp geodata/writers/kml/KmlLatLonAltBoxWriter.cpp geodata/writers/kml/KmlLatLonBoxWriter.cpp geodata/writers/kml/KmlLatLonQuadWriter.cpp geodata/writers/kml/KmlLabelStyleTagWriter.cpp geodata/writers/kml/KmlLinearRingTagWriter.cpp geodata/writers/kml/KmlLineStringTagWriter.cpp geodata/writers/kml/KmlLineStyleTagWriter.cpp geodata/writers/kml/KmlLinkTagWriter.cpp geodata/writers/kml/KmlListStyleTagWriter.cpp geodata/writers/kml/KmlLodTagWriter.cpp geodata/writers/kml/KmlLookAtTagWriter.cpp geodata/writers/kml/KmlModelTagWriter.cpp geodata/writers/kml/KmlMultiGeometryTagWriter.cpp geodata/writers/kml/KmlMultiTrackTagWriter.cpp geodata/writers/kml/KmlNetworkLinkTagWriter.cpp geodata/writers/kml/KmlNetworkLinkControlTagWriter.cpp geodata/writers/kml/KmlObjectTagWriter.cpp geodata/writers/kml/KmlOverlayTagWriter.cpp geodata/writers/kml/KmlPhotoOverlayWriter.cpp geodata/writers/kml/KmlPlacemarkTagWriter.cpp geodata/writers/kml/KmlPlaylistTagWriter.cpp geodata/writers/kml/KmlPointTagWriter.cpp geodata/writers/kml/KmlPolygonTagWriter.cpp geodata/writers/kml/KmlPolyStyleTagWriter.cpp geodata/writers/kml/KmlRegionTagWriter.cpp geodata/writers/kml/KmlSchemaTagWriter.cpp geodata/writers/kml/KmlSchemaDataTagWriter.cpp geodata/writers/kml/KmlSimpleDataTagWriter.cpp geodata/writers/kml/KmlSimpleFieldTagWriter.cpp geodata/writers/kml/KmlScreenOverlayWriter.cpp geodata/writers/kml/KmlStyleMapTagWriter.cpp geodata/writers/kml/KmlStyleTagWriter.cpp geodata/writers/kml/KmlTagWriter.cpp geodata/writers/kml/KmlTimeSpanWriter.cpp geodata/writers/kml/KmlTimeStampTagWriter.cpp geodata/writers/kml/KmlTourTagWriter.cpp geodata/writers/kml/KmlTrackWriter.cpp geodata/writers/kml/KmlUpdateTagWriter.cpp geodata/writers/kml/KmlOsmPlacemarkDataTagWriter.cpp ) # writer and the parser sources SET ( geodata_parser_SRCS geodata/parser/GeoDataParser.cpp geodata/parser/GeoDataTypes.cpp geodata/parser/GeoDocument.cpp geodata/parser/GeoParser.cpp geodata/parser/GeoSceneParser.cpp geodata/parser/GeoSceneTypes.cpp geodata/parser/GeoTagHandler.cpp ) SET( geodata_writer_SRCS geodata/writer/GeoTagWriter.cpp geodata/writer/GeoWriter.cpp geodata/writer/GeoWriterBackend.cpp geodata/writer/GeoDataDocumentWriter.cpp ) SET( geodata_SRCS ${geodata_data_SRCS} ${geodata_graphicsitem_SRCS} ${geodata_scene_SRCS} ${geodata_parser_SRCS} ${geodata_writer_SRCS} ${geodata_handlers_kml_SRCS} ${geodata_handlers_dgml_SRCS} ${geodata_writers_kml_SRCS} ${geodata_writers_dgml_SRCS} ) #add_subdirectory(geodata/data/tests) diff --git a/src/lib/marble/geodata/data/GeoDataBuilding.cpp b/src/lib/marble/geodata/data/GeoDataBuilding.cpp index 5bdc0bebe..368f89c83 100644 --- a/src/lib/marble/geodata/data/GeoDataBuilding.cpp +++ b/src/lib/marble/geodata/data/GeoDataBuilding.cpp @@ -1,107 +1,191 @@ // // 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 2017 Mohammed Nafees // #include "GeoDataBuilding.h" #include "GeoDataBuilding_p.h" #include "GeoDataTypes.h" namespace Marble { GeoDataBuilding::GeoDataBuilding() : GeoDataGeometry(new GeoDataBuildingPrivate), d(new GeoDataBuildingPrivate) { } GeoDataBuilding::GeoDataBuilding(const GeoDataGeometry &other) : GeoDataGeometry(other), d(new GeoDataBuildingPrivate) { } GeoDataBuilding::GeoDataBuilding(const GeoDataBuilding &other) : GeoDataGeometry(other), d(new GeoDataBuildingPrivate(*other.d)) { } GeoDataBuilding::~GeoDataBuilding() { delete d; } GeoDataBuilding& GeoDataBuilding::operator=(const GeoDataBuilding& other) { GeoDataGeometry::operator=(other); *d = *other.d; return *this; } const char *GeoDataBuilding::nodeType() const { return GeoDataTypes::GeoDataBuildingType; } EnumGeometryId GeoDataBuilding::geometryId() const { return GeoDataBuildingId; } GeoDataGeometry *GeoDataBuilding::copy() const { return new GeoDataBuilding(*this); } double GeoDataBuilding::height() const { return d->m_height; } void GeoDataBuilding::setHeight(double height) { d->m_height = height; } int GeoDataBuilding::minLevel() const { return d->m_minLevel; } void GeoDataBuilding::setMinLevel(int minLevel) { d->m_minLevel = minLevel; } int GeoDataBuilding::maxLevel() const { return d->m_maxLevel; } void GeoDataBuilding::setMaxLevel(int maxLevel) { d->m_maxLevel = maxLevel; } QVector GeoDataBuilding::nonExistentLevels() const { return d->m_nonExistentLevels; } void GeoDataBuilding::setNonExistentLevels(const QVector &nonExistentLevels) { d->m_nonExistentLevels = nonExistentLevels; } GeoDataMultiGeometry* GeoDataBuilding::multiGeometry() const { return &d->m_multiGeometry; } +const GeoDataLatLonAltBox &GeoDataBuilding::latLonAltBox() const +{ + // @TODO: This is temporary, for only when we have just one child + Q_ASSERT(d->m_multiGeometry.size() == 1); + return d->m_multiGeometry.at(0).latLonAltBox(); +} + +QString GeoDataBuilding::name() const +{ + return d->m_name; +} + +void GeoDataBuilding::setName(const QString& name) +{ + d->m_name = name; +} + +QVector GeoDataBuilding::entries() const +{ + return d->m_entries; +} + +void GeoDataBuilding::setEntries(const QVector &entries) +{ + d->m_entries = entries; +} + +double GeoDataBuilding::parseBuildingHeight(const QString& buildingHeight) +{ + double height = 8.0; + + // check first for unitless value + bool converted; + double extractedHeight = buildingHeight.toDouble(&converted); + if (converted) { + return extractedHeight; + } + + if (buildingHeight.endsWith(QChar('m')) || + buildingHeight.endsWith(QLatin1String("meter")) || + buildingHeight.endsWith(QLatin1String("meters")) || + buildingHeight.endsWith(QLatin1String("metre")) || + buildingHeight.endsWith(QLatin1String("metres"))) { + QString const heightValue = QString(buildingHeight).remove(QStringLiteral("meters")) + .remove(QStringLiteral("meter")).remove(QStringLiteral("metres")) + .remove(QStringLiteral("metre")).remove(QChar('m')).trimmed(); + bool extracted; + double extractedHeight = heightValue.toDouble(&extracted); + if (extracted) { + height = extractedHeight; + } + } else { // feet and inches + double extractedHeight = 0.0; // in inches, converted to meters in the end + if (buildingHeight.contains(QChar('\''))) { + double heightInches = 0.0; + QStringList const feetInches = buildingHeight.split(QChar('\'')); + bool okFeet; + double feet = feetInches[0].trimmed().toDouble(&okFeet); + if (okFeet) { + heightInches = feet * FT2IN; + } + if (!feetInches[1].isEmpty()) { // has inches as unit as well + bool okInches; + double inches = QString(feetInches[1]).remove(QChar('\"')).trimmed().toDouble(&okInches); + if (okInches) { + heightInches += inches; + } + } + extractedHeight = heightInches; + } else if (buildingHeight.endsWith(QLatin1String("feet"))) { + bool ok; + double feet = QString(buildingHeight).remove(QStringLiteral("feet")).trimmed().toDouble(&ok); + if (ok) { + extractedHeight = feet * FT2IN; + } + } + if (extractedHeight > 0.0) { + height = extractedHeight * IN2M; // convert inches to meters + } + } + + return height; +} + } diff --git a/src/lib/marble/geodata/data/GeoDataBuilding.h b/src/lib/marble/geodata/data/GeoDataBuilding.h index 1fd01bf5a..d3f8fb741 100644 --- a/src/lib/marble/geodata/data/GeoDataBuilding.h +++ b/src/lib/marble/geodata/data/GeoDataBuilding.h @@ -1,116 +1,147 @@ // // 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 2017 Mohammed Nafees // #ifndef MARBLE_GEODATABUILDING_H #define MARBLE_GEODATABUILDING_H #include #include "GeoDataGeometry.h" +#include "GeoDataCoordinates.h" #include "geodata_export.h" namespace Marble { class GeoDataBuildingPrivate; /*! \class GeoDataBuilding \brief Contains important information about a building and its floors (levels) GeoDataBuilding holds information such as minimum floor, maximum floor, floor data and their respective MultiGeometry and other possible metadata such as the total height of the building, type etc. */ class GEODATA_EXPORT GeoDataBuilding : public GeoDataGeometry { public: explicit GeoDataBuilding(); explicit GeoDataBuilding(const GeoDataGeometry &other); explicit GeoDataBuilding(const GeoDataBuilding &other); + struct NamedEntry { + GeoDataCoordinates point; + QString label; + }; + GeoDataBuilding& operator=(const GeoDataBuilding &other); const char *nodeType() const override; EnumGeometryId geometryId() const override; GeoDataGeometry *copy() const override; + static double parseBuildingHeight(const QString& buildingHeight); + /*! Destroys the GeoDataBuilding */ ~GeoDataBuilding() override; /*! @return the height of the building */ double height() const; /*! Sets the height of the building @param height */ void setHeight(double height); /*! @return the minimum level */ int minLevel() const; /*! Sets the minimum level of the building @param minLevel */ void setMinLevel(int minLevel); /*! @return the maximum level of the building */ int maxLevel() const; /*! Sets the maximum level of the building @param maxLevel */ void setMaxLevel(int maxLevel); /*! @return the non existent levels in the building */ QVector nonExistentLevels() const; /*! Sets the non existent levels of the building @param nonExistentLevels */ void setNonExistentLevels(const QVector& nonExistentLevels); /*! - * @return the multigeometry associated with the building + @return the multigeometry associated with the building */ GeoDataMultiGeometry* multiGeometry() const; + +/*! + @return the latlonaltbox for the contained multigeometry + */ + const GeoDataLatLonAltBox& latLonAltBox() const override; + + +/*! + @return the name of the building + */ + QString name() const; + + +/*! + Sets the name of the building + @param name + */ + void setName(const QString& name); + + QVector entries() const; + + void setEntries(const QVector& entries); + private: GeoDataBuildingPrivate* const d; }; } #endif diff --git a/src/lib/marble/geodata/data/GeoDataBuilding_p.h b/src/lib/marble/geodata/data/GeoDataBuilding_p.h index 0df2b46a2..7e452b8b4 100644 --- a/src/lib/marble/geodata/data/GeoDataBuilding_p.h +++ b/src/lib/marble/geodata/data/GeoDataBuilding_p.h @@ -1,45 +1,47 @@ // // 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 2017 Mohammed Nafees // #ifndef MARBLE_GEODATABUILDING_P_H #define MARBLE_GEODATABUILDING_P_H #include "GeoDataGeometry_p.h" #include "GeoDataMultiGeometry.h" namespace Marble { class GeoDataBuildingPrivate : public GeoDataGeometryPrivate { public: GeoDataBuildingPrivate() : m_height(0.0), m_minLevel(0), m_maxLevel(0) { } GeoDataGeometryPrivate *copy() const override { GeoDataBuildingPrivate* copy = new GeoDataBuildingPrivate; *copy = *this; return copy; } double m_height; int m_minLevel; int m_maxLevel; QVector m_nonExistentLevels; GeoDataMultiGeometry m_multiGeometry; + QString m_name; + QVector m_entries; }; } #endif diff --git a/src/lib/marble/geodata/graphicsitem/AbstractGeoPolygonGraphicsItem.cpp b/src/lib/marble/geodata/graphicsitem/AbstractGeoPolygonGraphicsItem.cpp index caba2228e..7e5aba664 100644 --- a/src/lib/marble/geodata/graphicsitem/AbstractGeoPolygonGraphicsItem.cpp +++ b/src/lib/marble/geodata/graphicsitem/AbstractGeoPolygonGraphicsItem.cpp @@ -1,213 +1,240 @@ // // 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 = 0; AbstractGeoPolygonGraphicsItem::AbstractGeoPolygonGraphicsItem(const GeoDataPlacemark *placemark, const GeoDataPolygon *polygon) : GeoGraphicsItem(placemark), m_polygon(polygon), - m_ring(0) + m_ring(0), + m_building(0) { } AbstractGeoPolygonGraphicsItem::AbstractGeoPolygonGraphicsItem(const GeoDataPlacemark *placemark, const GeoDataLinearRing *ring) : GeoGraphicsItem(placemark), m_polygon(0), - m_ring(ring) + m_ring(ring), + m_building(0) +{ +} + +AbstractGeoPolygonGraphicsItem::AbstractGeoPolygonGraphicsItem(const GeoDataPlacemark *placemark, const GeoDataBuilding *building) : + GeoGraphicsItem(placemark), + m_polygon(0), + m_ring(0), + m_building(building) { } AbstractGeoPolygonGraphicsItem::~AbstractGeoPolygonGraphicsItem() { } const GeoDataLatLonAltBox& AbstractGeoPolygonGraphicsItem::latLonAltBox() const { - if( m_polygon ) { + if(m_polygon) { return m_polygon->latLonAltBox(); + } else if (m_ring) { + return m_ring->latLonAltBox(); } - 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) { 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) { 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/AbstractGeoPolygonGraphicsItem.h b/src/lib/marble/geodata/graphicsitem/AbstractGeoPolygonGraphicsItem.h index 0e5f6d1bc..4ac53eab6 100644 --- a/src/lib/marble/geodata/graphicsitem/AbstractGeoPolygonGraphicsItem.h +++ b/src/lib/marble/geodata/graphicsitem/AbstractGeoPolygonGraphicsItem.h @@ -1,59 +1,67 @@ // // 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_ABSTRACTGEOPOLYGONGRAPHICSITEM_H #define MARBLE_ABSTRACTGEOPOLYGONGRAPHICSITEM_H #include "GeoGraphicsItem.h" #include "marble_export.h" #include #include namespace Marble { class GeoDataLinearRing; class GeoDataPlacemark; class GeoDataPolygon; +class GeoDataBuilding; class MARBLE_EXPORT AbstractGeoPolygonGraphicsItem : public GeoGraphicsItem { protected: - explicit AbstractGeoPolygonGraphicsItem(const GeoDataPlacemark *placemark, const GeoDataPolygon *polygon); - explicit AbstractGeoPolygonGraphicsItem(const GeoDataPlacemark *placemark, const GeoDataLinearRing *ring); + AbstractGeoPolygonGraphicsItem(const GeoDataPlacemark *placemark, const GeoDataPolygon *polygon); + AbstractGeoPolygonGraphicsItem(const GeoDataPlacemark *placemark, const GeoDataLinearRing *ring); + AbstractGeoPolygonGraphicsItem(const GeoDataPlacemark *placemark, const GeoDataBuilding *building); ~AbstractGeoPolygonGraphicsItem() override; public: const GeoDataLatLonAltBox& latLonAltBox() const override; void paint(GeoPainter* painter, const ViewportParams *viewport, const QString &layer, int tileZoomLevel) override; bool contains(const QPoint &screenPosition, const ViewportParams *viewport) const override; + void setLinearRing(GeoDataLinearRing* ring); + void setPolygon(GeoDataPolygon* polygon); + static const void *s_previousStyle; protected: bool configurePainter(GeoPainter* painter, const ViewportParams *viewport); inline const GeoDataPolygon *polygon() const { return m_polygon; } inline const GeoDataLinearRing *ring() const { return m_ring; } + inline + const GeoDataBuilding *building() const { return m_building; } static int extractElevation(const GeoDataPlacemark &placemark); private: QPixmap texture(const QString &path, const QColor &color); - const GeoDataPolygon *const m_polygon; - const GeoDataLinearRing *const m_ring; + const GeoDataPolygon * m_polygon; + const GeoDataLinearRing * m_ring; + const GeoDataBuilding *const m_building; }; } #endif diff --git a/src/lib/marble/geodata/graphicsitem/BuildingGeoPolygonGraphicsItem.cpp b/src/lib/marble/geodata/graphicsitem/BuildingGraphicsItem.cpp similarity index 74% rename from src/lib/marble/geodata/graphicsitem/BuildingGeoPolygonGraphicsItem.cpp rename to src/lib/marble/geodata/graphicsitem/BuildingGraphicsItem.cpp index 5ce758157..a8379b780 100644 --- a/src/lib/marble/geodata/graphicsitem/BuildingGeoPolygonGraphicsItem.cpp +++ b/src/lib/marble/geodata/graphicsitem/BuildingGraphicsItem.cpp @@ -1,576 +1,503 @@ // // 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 "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 { -BuildingGeoPolygonGraphicsItem::BuildingGeoPolygonGraphicsItem(const GeoDataPlacemark *placemark, - const GeoDataPolygon *polygon) - : AbstractGeoPolygonGraphicsItem(placemark, polygon) - , m_buildingHeight(extractBuildingHeight(*placemark)) - , m_buildingText(extractBuildingLabel(*placemark)) - , m_entries(extractNamedEntries(*placemark)) - , m_hasInnerBoundaries(false) +BuildingGraphicsItem::BuildingGraphicsItem(const GeoDataPlacemark *placemark, const GeoDataBuilding *building) + : AbstractGeoPolygonGraphicsItem(placemark, building) { - setZValue(m_buildingHeight); - Q_ASSERT(m_buildingHeight > 0.0); - - QStringList paintLayers; - paintLayers << QStringLiteral("Polygon/Building/frame") - << QStringLiteral("Polygon/Building/roof"); - setPaintLayers(paintLayers); -} + 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); + } -BuildingGeoPolygonGraphicsItem::BuildingGeoPolygonGraphicsItem(const GeoDataPlacemark *placemark, - const GeoDataLinearRing* ring) - : AbstractGeoPolygonGraphicsItem(placemark, ring) - , m_buildingHeight(extractBuildingHeight(*placemark)) - , m_buildingText(extractBuildingLabel(*placemark)) - , m_entries(extractNamedEntries(*placemark)) -{ - setZValue(m_buildingHeight); - Q_ASSERT(m_buildingHeight > 0.0); + setZValue(building->height()); + Q_ASSERT(building->height() > 0.0); QStringList paintLayers; paintLayers << QStringLiteral("Polygon/Building/frame") << QStringLiteral("Polygon/Building/roof"); setPaintLayers(paintLayers); } -BuildingGeoPolygonGraphicsItem::~BuildingGeoPolygonGraphicsItem() +BuildingGraphicsItem::~BuildingGraphicsItem() { qDeleteAll(m_cachedOuterPolygons); qDeleteAll(m_cachedInnerPolygons); qDeleteAll(m_cachedOuterRoofPolygons); qDeleteAll(m_cachedInnerRoofPolygons); } -void BuildingGeoPolygonGraphicsItem::initializeBuildingPainting(const GeoPainter* painter, const ViewportParams *viewport, - bool &drawAccurate3D, bool &isCameraAboveBuilding ) const +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 BuildingGeoPolygonGraphicsItem::updatePolygons( const ViewportParams *viewport, - QVector& outerPolygons, - QVector& innerPolygons, - bool &hasInnerBoundaries ) +void BuildingGraphicsItem::updatePolygons(const ViewportParams *viewport, + QVector& outerPolygons, + QVector& innerPolygons, + bool &hasInnerBoundaries ) { // 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 BuildingGeoPolygonGraphicsItem::centroid(const QPolygonF &polygon, double &area) +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(); i 0.0); - qreal const buildingFactor = m_buildingHeight / EARTH_RADIUS; + Q_ASSERT(building()->height() > 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); } -double BuildingGeoPolygonGraphicsItem::extractBuildingHeight(const GeoDataPlacemark &placemark) -{ - double height = 8.0; - - const OsmPlacemarkData &osmData = placemark.osmData(); - - 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); -} - -QString BuildingGeoPolygonGraphicsItem::extractBuildingLabel(const GeoDataPlacemark &placemark) -{ - 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, int tileZoomLevel) +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(m_buildingHeight); + 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 BuildingGeoPolygonGraphicsItem::paintRoof(GeoPainter* painter, const ViewportParams* viewport) +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 (!m_buildingText.isEmpty() || !m_entries.isEmpty()) { + 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 && !m_buildingText.isEmpty() && !roofCenter.isNull()) { - double const w2 = 0.5 * painter->fontMetrics().width(m_buildingText); + 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(), m_buildingText, + painter->drawTextFragment(roofCenter.toPoint(), building()->name(), painter->font().pointSize(), painter->brush().color()); } } } // Render additional housenumbers at building entries - if (!m_entries.isEmpty() && maxArea > 1600 * m_entries.size()) { - for(const auto &entry: m_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(), - m_buildingText, painter->font().pointSize(), painter->brush().color(), + building()->name(), painter->font().pointSize(), painter->brush().color(), GeoPainter::RoundFrame); } } } -void BuildingGeoPolygonGraphicsItem::paintFrame(GeoPainter *painter, const ViewportParams *viewport) +void BuildingGraphicsItem::paintFrame(GeoPainter *painter, const ViewportParams *viewport) { // TODO: how does this match the Q_ASSERT in the constructor? - if (m_buildingHeight == 0.0) { + 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 BuildingGeoPolygonGraphicsItem::screenPolygons(const ViewportParams *viewport, const GeoDataPolygon *polygon, +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 BuildingGeoPolygonGraphicsItem::contains(const QPoint &screenPosition, const ViewportParams *viewport) const +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 BuildingGeoPolygonGraphicsItem::configurePainterForFrame(GeoPainter *painter) const +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/lib/marble/geodata/graphicsitem/BuildingGeoPolygonGraphicsItem.h b/src/lib/marble/geodata/graphicsitem/BuildingGraphicsItem.h similarity index 72% rename from src/lib/marble/geodata/graphicsitem/BuildingGeoPolygonGraphicsItem.h rename to src/lib/marble/geodata/graphicsitem/BuildingGraphicsItem.h index 4132eaa48..9658153ba 100644 --- a/src/lib/marble/geodata/graphicsitem/BuildingGeoPolygonGraphicsItem.h +++ b/src/lib/marble/geodata/graphicsitem/BuildingGraphicsItem.h @@ -1,77 +1,64 @@ // // 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 +class MARBLE_EXPORT BuildingGraphicsItem : public AbstractGeoPolygonGraphicsItem { public: - explicit BuildingGeoPolygonGraphicsItem(const GeoDataPlacemark *placemark, const GeoDataPolygon *polygon); - explicit BuildingGeoPolygonGraphicsItem(const GeoDataPlacemark *placemark, const GeoDataLinearRing *ring); - ~BuildingGeoPolygonGraphicsItem() override; + BuildingGraphicsItem(const GeoDataPlacemark *placemark, const GeoDataBuilding *building); + ~BuildingGraphicsItem() override; public: void paint(GeoPainter* painter, const ViewportParams *viewport, const QString &layer, int tileZoomLevel) override; private: - struct NamedEntry { - GeoDataCoordinates point; - QString label; - }; - void paintFrame(GeoPainter* painter, const ViewportParams *viewport); void paintRoof(GeoPainter* painter, const ViewportParams *viewport); bool configurePainterForFrame(GeoPainter *painter) const; void initializeBuildingPainting(const GeoPainter* painter, const ViewportParams *viewport, bool &drawAccurate3D, bool &isCameraAboveBuilding) const; void updatePolygons( const ViewportParams *viewport, QVector& outlinePolygons, QVector& innerPolygons, bool &hasInnerBoundaries); QPointF buildingOffset(const QPointF &point, const ViewportParams *viewport, bool* isCameraAboveBuilding = nullptr) const; static QPointF centroid(const QPolygonF &polygon, double &area); void screenPolygons(const ViewportParams *viewport, const GeoDataPolygon* polygon, QVector &polygons, QVector &outlines ); bool contains(const QPoint &screenPosition, const ViewportParams *viewport) const override; - static double extractBuildingHeight(const GeoDataPlacemark &placemark); - static QString extractBuildingLabel(const GeoDataPlacemark &placemark); - static QVector extractNamedEntries(const GeoDataPlacemark &placemark); - private: - const double m_buildingHeight; - const QString m_buildingText; - const QVector m_entries; QVector m_cachedOuterPolygons; QVector m_cachedInnerPolygons; QVector m_cachedOuterRoofPolygons; QVector m_cachedInnerRoofPolygons; bool m_hasInnerBoundaries; }; } #endif diff --git a/src/lib/marble/geodata/graphicsitem/GeoPolygonGraphicsItem.cpp b/src/lib/marble/geodata/graphicsitem/GeoPolygonGraphicsItem.cpp index 8cefb5153..edf76f76f 100644 --- a/src/lib/marble/geodata/graphicsitem/GeoPolygonGraphicsItem.cpp +++ b/src/lib/marble/geodata/graphicsitem/GeoPolygonGraphicsItem.cpp @@ -1,59 +1,57 @@ // // 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 "GeoPolygonGraphicsItem.h" -#include "BuildingGeoPolygonGraphicsItem.h" +#include "BuildingGraphicsItem.h" #include "GeoDataPlacemark.h" #include "StyleBuilder.h" namespace Marble { AbstractGeoPolygonGraphicsItem *GeoPolygonGraphicsItem::createGraphicsItem(const GeoDataPlacemark *placemark, const GeoDataPolygon *polygon) { - if (placemark->visualCategory() == GeoDataPlacemark::Building) { - return new BuildingGeoPolygonGraphicsItem(placemark, polygon); - } return new GeoPolygonGraphicsItem(placemark, polygon); } AbstractGeoPolygonGraphicsItem *GeoPolygonGraphicsItem::createGraphicsItem(const GeoDataPlacemark *placemark, const GeoDataLinearRing *ring) { - if (placemark->visualCategory() == GeoDataPlacemark::Building) { - return new BuildingGeoPolygonGraphicsItem(placemark, ring); - } return new GeoPolygonGraphicsItem(placemark, ring); } +AbstractGeoPolygonGraphicsItem *GeoPolygonGraphicsItem::createGraphicsItem(const GeoDataPlacemark *placemark, const GeoDataBuilding *building) +{ + return new BuildingGraphicsItem(placemark, building); +} GeoPolygonGraphicsItem::GeoPolygonGraphicsItem(const GeoDataPlacemark *placemark, const GeoDataPolygon *polygon) : AbstractGeoPolygonGraphicsItem(placemark, polygon) { const int elevation = extractElevation(*placemark); setZValue(zValue() + elevation); const GeoDataPlacemark::GeoDataVisualCategory visualCategory = placemark->visualCategory(); const QString paintLayer = QLatin1String("Polygon/") + StyleBuilder::visualCategoryName(visualCategory); setPaintLayers(QStringList(paintLayer)); } GeoPolygonGraphicsItem::GeoPolygonGraphicsItem(const GeoDataPlacemark *placemark, const GeoDataLinearRing *ring) : AbstractGeoPolygonGraphicsItem(placemark, ring) { const int elevation = extractElevation(*placemark); setZValue(zValue() + elevation); const GeoDataPlacemark::GeoDataVisualCategory visualCategory = placemark->visualCategory(); const QString paintLayer = QLatin1String("Polygon/") + StyleBuilder::visualCategoryName(visualCategory); setPaintLayers(QStringList(paintLayer)); } } diff --git a/src/lib/marble/geodata/graphicsitem/GeoPolygonGraphicsItem.h b/src/lib/marble/geodata/graphicsitem/GeoPolygonGraphicsItem.h index bc93c545c..6ca4a1e55 100644 --- a/src/lib/marble/geodata/graphicsitem/GeoPolygonGraphicsItem.h +++ b/src/lib/marble/geodata/graphicsitem/GeoPolygonGraphicsItem.h @@ -1,34 +1,36 @@ // // 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_GEOPOLYGONGRAPHICSITEM_H #define MARBLE_GEOPOLYGONGRAPHICSITEM_H #include "AbstractGeoPolygonGraphicsItem.h" namespace Marble { class GeoDataLinearRing; class GeoDataPolygon; +class GeoDataBuilding; class MARBLE_EXPORT GeoPolygonGraphicsItem : public AbstractGeoPolygonGraphicsItem { public: static AbstractGeoPolygonGraphicsItem *createGraphicsItem(const GeoDataPlacemark *placemark, const GeoDataPolygon *polygon); static AbstractGeoPolygonGraphicsItem *createGraphicsItem(const GeoDataPlacemark *placemark, const GeoDataLinearRing *ring); + static AbstractGeoPolygonGraphicsItem *createGraphicsItem(const GeoDataPlacemark *placemark, const GeoDataBuilding *building); explicit GeoPolygonGraphicsItem(const GeoDataPlacemark *placemark, const GeoDataPolygon *polygon); explicit GeoPolygonGraphicsItem(const GeoDataPlacemark *placemark, const GeoDataLinearRing *ring); }; } #endif diff --git a/src/lib/marble/layers/GeometryLayer.cpp b/src/lib/marble/layers/GeometryLayer.cpp index c6fb027c7..33ae3ae5c 100644 --- a/src/lib/marble/layers/GeometryLayer.cpp +++ b/src/lib/marble/layers/GeometryLayer.cpp @@ -1,669 +1,672 @@ // // 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 2008-2009 Patrick Spendrin // Copyright 2010 Thibaut Gridel // Copyright 2011-2012 Bernhard Beschow // Copyright 2014 Gábor Péterffy // #include "GeometryLayer.h" // Marble #include "GeoDataLatLonAltBox.h" #include "GeoDataDocument.h" #include "GeoDataFolder.h" #include "GeoDataLineStyle.h" #include "GeoDataMultiTrack.h" #include "GeoDataObject.h" #include "GeoDataPlacemark.h" #include "GeoDataLinearRing.h" #include "GeoDataMultiGeometry.h" #include "GeoDataPolygon.h" +#include "GeoDataBuilding.h" #include "GeoDataPolyStyle.h" #include "GeoDataStyle.h" #include "GeoDataIconStyle.h" #include "GeoDataStyleMap.h" #include "GeoDataTrack.h" #include "GeoDataFeature.h" #include "MarbleDebug.h" #include "GeoPainter.h" #include "ViewportParams.h" #include "RenderState.h" #include "GeoGraphicsScene.h" #include "GeoGraphicsItem.h" #include "GeoLineStringGraphicsItem.h" #include "GeoPolygonGraphicsItem.h" #include "GeoTrackGraphicsItem.h" #include "GeoDataPhotoOverlay.h" #include "GeoDataScreenOverlay.h" #include "GeoPhotoGraphicsItem.h" #include "ScreenOverlayGraphicsItem.h" #include "TileId.h" #include "MarbleGraphicsItem.h" #include "MarblePlacemarkModel.h" #include "GeoDataTreeModel.h" #include #include "StyleBuilder.h" #include "AbstractGeoPolygonGraphicsItem.h" #include "GeoLineStringGraphicsItem.h" #include "GeoDataRelation.h" // Qt #include #include #include namespace Marble { class GeometryLayerPrivate { public: typedef QVector OsmLineStringItems; typedef QSet Relations; typedef QHash FeatureRelationHash; typedef QVector GeoGraphicItems; struct PaintFragments { // Three lists for different z values // A z value of 0 is default and used by the majority of items, so sorting // can be avoided for it QVector negative; // subways QVector null; // areas and roads QVector positive; // buildings }; explicit GeometryLayerPrivate(const QAbstractItemModel *model, const StyleBuilder *styleBuilder); void createGraphicsItems(const GeoDataObject *object); void createGraphicsItems(const GeoDataObject *object, FeatureRelationHash &relations); void createGraphicsItemFromGeometry(const GeoDataGeometry *object, const GeoDataPlacemark *placemark, const Relations &relations); void createGraphicsItemFromOverlay(const GeoDataOverlay *overlay); void removeGraphicsItems(const GeoDataFeature *feature); void updateTiledLineStrings(const GeoDataPlacemark *placemark, GeoLineStringGraphicsItem* lineStringItem); void updateTiledLineStrings(OsmLineStringItems &lineStringItems); void clearCache(); bool showRelation(const GeoDataRelation* relation) const; void updateRelationVisibility(); const QAbstractItemModel *const m_model; const StyleBuilder *const m_styleBuilder; GeoGraphicsScene m_scene; QString m_runtimeTrace; QList m_screenOverlays; QHash m_osmLineStringItems; int m_tileLevel; GeoGraphicsItem* m_lastFeatureAt; bool m_dirty; int m_cachedItemCount; QHash m_cachedPaintFragments; typedef QPair LayerItem; QList m_cachedDefaultLayer; QDateTime m_cachedDateTime; GeoDataLatLonBox m_cachedLatLonBox; QSet m_highlightedRouteRelations; GeoDataRelation::RelationTypes m_visibleRelationTypes; }; GeometryLayerPrivate::GeometryLayerPrivate(const QAbstractItemModel *model, const StyleBuilder *styleBuilder) : m_model(model), m_styleBuilder(styleBuilder), m_tileLevel(0), m_lastFeatureAt(nullptr), m_dirty(true), m_cachedItemCount(0), m_visibleRelationTypes(GeoDataRelation::RouteFerry) { } void GeometryLayerPrivate::createGraphicsItems(const GeoDataObject *object) { FeatureRelationHash noRelations; createGraphicsItems(object, noRelations); } GeometryLayer::GeometryLayer(const QAbstractItemModel *model, const StyleBuilder *styleBuilder) : d(new GeometryLayerPrivate(model, styleBuilder)) { const GeoDataObject *object = static_cast(d->m_model->index(0, 0, QModelIndex()).internalPointer()); if (object && object->parent()) { d->createGraphicsItems(object->parent()); } connect(model, SIGNAL(dataChanged(QModelIndex, QModelIndex)), this, SLOT(resetCacheData())); connect(model, SIGNAL(rowsInserted(QModelIndex, int, int)), this, SLOT(addPlacemarks(QModelIndex, int, int))); connect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex, int, int)), this, SLOT(removePlacemarks(QModelIndex, int, int))); connect(model, SIGNAL(modelReset()), this, SLOT(resetCacheData())); connect(this, SIGNAL(highlightedPlacemarksChanged(QVector)), &d->m_scene, SLOT(applyHighlight(QVector))); connect(&d->m_scene, SIGNAL(repaintNeeded()), this, SIGNAL(repaintNeeded())); } GeometryLayer::~GeometryLayer() { delete d; } QStringList GeometryLayer::renderPosition() const { return QStringList(QStringLiteral("HOVERS_ABOVE_SURFACE")); } bool GeometryLayer::render(GeoPainter *painter, ViewportParams *viewport, const QString& renderPos, GeoSceneLayer * layer) { Q_UNUSED(renderPos) Q_UNUSED(layer) painter->save(); auto const & box = viewport->viewLatLonAltBox(); bool isEqual = GeoDataLatLonBox::fuzzyCompare(d->m_cachedLatLonBox, box, 0.05); if (d->m_cachedLatLonBox.isEmpty() || !isEqual) { d->m_dirty = true; } // update the items cache at least every second since the last request auto const now = QDateTime::currentDateTime(); if (!d->m_cachedDateTime.isValid() || d->m_cachedDateTime.msecsTo(now) > 1000) { d->m_dirty = true; } if (d->m_dirty) { d->m_dirty = false; const int maxZoomLevel = qMin(d->m_tileLevel, d->m_styleBuilder->maximumZoomLevel()); auto const items = d->m_scene.items(box, maxZoomLevel);; d->m_cachedLatLonBox = box; d->m_cachedDateTime = now; d->m_cachedItemCount = items.size(); d->m_cachedDefaultLayer.clear(); d->m_cachedPaintFragments.clear(); QHash paintFragments; QSet const knownLayers = QSet::fromList(d->m_styleBuilder->renderOrder()); for (GeoGraphicsItem* item: items) { QStringList paintLayers = item->paintLayers(); if (paintLayers.isEmpty()) { mDebug() << item << " provides no paint layers, so I force one onto it."; paintLayers << QString(); } for (const auto &layer: paintLayers) { if (knownLayers.contains(layer)) { GeometryLayerPrivate::PaintFragments &fragments = paintFragments[layer]; double const zValue = item->zValue(); // assign subway stations if (zValue == 0.0) { fragments.null << item; // assign areas and streets } else if (zValue < 0.0) { fragments.negative << item; // assign buildings } else { fragments.positive << item; } } else { // assign symbols d->m_cachedDefaultLayer << GeometryLayerPrivate::LayerItem(layer, item); static QSet missingLayers; if (!missingLayers.contains(layer)) { mDebug() << "Missing layer " << layer << ", in render order, will render it on top"; missingLayers << layer; } } } } // Sort each fragment by z-level for (const QString &layer: d->m_styleBuilder->renderOrder()) { GeometryLayerPrivate::PaintFragments & layerItems = paintFragments[layer]; std::sort(layerItems.negative.begin(), layerItems.negative.end(), GeoGraphicsItem::zValueLessThan); // The idea here is that layerItems.null has most items and does not need to be sorted by z-value // since they are all equal (=0). We do sort them by style pointer though for batch rendering std::sort(layerItems.null.begin(), layerItems.null.end(), GeoGraphicsItem::styleLessThan); std::sort(layerItems.positive.begin(), layerItems.positive.end(), GeoGraphicsItem::zValueAndStyleLessThan); auto const count = layerItems.negative.size() + layerItems.null.size() + layerItems.positive.size(); d->m_cachedPaintFragments[layer].reserve(count); d->m_cachedPaintFragments[layer] << layerItems.negative; d->m_cachedPaintFragments[layer] << layerItems.null; d->m_cachedPaintFragments[layer] << layerItems.positive; } } for (const QString &layer: d->m_styleBuilder->renderOrder()) { auto & layerItems = d->m_cachedPaintFragments[layer]; AbstractGeoPolygonGraphicsItem::s_previousStyle = 0; GeoLineStringGraphicsItem::s_previousStyle = 0; for (auto item: layerItems) { item->paint(painter, viewport, layer, d->m_tileLevel); } } for (const auto & item: d->m_cachedDefaultLayer) { item.second->paint(painter, viewport, item.first, d->m_tileLevel); } for (ScreenOverlayGraphicsItem* item: d->m_screenOverlays) { item->paintEvent(painter, viewport); } painter->restore(); d->m_runtimeTrace = QStringLiteral("Geometries: %1 Zoom: %2") .arg(d->m_cachedItemCount) .arg(d->m_tileLevel); return true; } RenderState GeometryLayer::renderState() const { return RenderState(QStringLiteral("GeoGraphicsScene")); } QString GeometryLayer::runtimeTrace() const { return d->m_runtimeTrace; } bool GeometryLayer::hasFeatureAt(const QPoint &curpos, const ViewportParams *viewport) { if (d->m_lastFeatureAt && d->m_lastFeatureAt->contains(curpos, viewport)) { return true; } auto const renderOrder = d->m_styleBuilder->renderOrder(); for (int i = renderOrder.size() - 1; i >= 0; --i) { auto & layerItems = d->m_cachedPaintFragments[renderOrder[i]]; for (auto item : layerItems) { if (item->contains(curpos, viewport)) { d->m_lastFeatureAt = item; return true; } } } return false; } void GeometryLayerPrivate::createGraphicsItems(const GeoDataObject *object, FeatureRelationHash &relations) { clearCache(); if (auto document = geodata_cast(object)) { for (auto feature: document->featureList()) { if (auto relation = geodata_cast(feature)) { relation->setVisible(showRelation(relation)); for (auto member: relation->members()) { relations[member] << relation; } } } } if (auto placemark = geodata_cast(object)) { createGraphicsItemFromGeometry(placemark->geometry(), placemark, relations.value(placemark)); } else if (const GeoDataOverlay* overlay = dynamic_cast(object)) { createGraphicsItemFromOverlay(overlay); } // parse all child objects of the container if (const GeoDataContainer *container = dynamic_cast(object)) { int rowCount = container->size(); for (int row = 0; row < rowCount; ++row) { createGraphicsItems(container->child(row), relations); } } } void GeometryLayerPrivate::updateTiledLineStrings(const GeoDataPlacemark* placemark, GeoLineStringGraphicsItem* lineStringItem) { if (!placemark->hasOsmData()) { return; } qint64 const osmId = placemark->osmData().oid(); if (osmId <= 0) { return; } auto & lineStringItems = m_osmLineStringItems[osmId]; lineStringItems << lineStringItem; updateTiledLineStrings(lineStringItems); } void GeometryLayerPrivate::updateTiledLineStrings(OsmLineStringItems &lineStringItems) { GeoDataLineString merged; if (lineStringItems.size() > 1) { QVector lineStrings; for (auto item : lineStringItems) { lineStrings << item->lineString(); } merged = GeoLineStringGraphicsItem::merge(lineStrings); } // If merging failed, reset all. Otherwise only the first one // gets the merge result and becomes visible. bool visible = true; for (auto item : lineStringItems) { item->setVisible(visible); if (visible) { item->setMergedLineString(merged); visible = merged.isEmpty(); } } } void GeometryLayerPrivate::clearCache() { m_lastFeatureAt = nullptr; m_dirty = true; m_cachedDateTime = QDateTime(); m_cachedItemCount = 0; m_cachedPaintFragments.clear(); m_cachedDefaultLayer.clear(); m_cachedLatLonBox = GeoDataLatLonBox(); } inline bool GeometryLayerPrivate::showRelation(const GeoDataRelation *relation) const { return (m_visibleRelationTypes.testFlag(relation->relationType()) || m_highlightedRouteRelations.contains(relation->osmData().oid())); } void GeometryLayerPrivate::updateRelationVisibility() { for (int i = 0; i < m_model->rowCount(); ++i) { QVariant const data = m_model->data(m_model->index(i, 0), MarblePlacemarkModel::ObjectPointerRole); GeoDataObject *object = qvariant_cast (data); if (auto doc = geodata_cast(object)) { for (auto feature: doc->featureList()) { if (auto relation = geodata_cast(feature)) { relation->setVisible(showRelation(relation)); } } } } m_scene.resetStyle(); } void GeometryLayerPrivate::createGraphicsItemFromGeometry(const GeoDataGeometry* object, const GeoDataPlacemark *placemark, const Relations &relations) { if (!placemark->isGloballyVisible()) { return; // Reconsider this when visibility can be changed dynamically } GeoGraphicsItem *item = 0; if (const auto line = geodata_cast(object)) { auto lineStringItem = new GeoLineStringGraphicsItem(placemark, line); item = lineStringItem; updateTiledLineStrings(placemark, lineStringItem); } else if (const auto ring = geodata_cast(object)) { item = GeoPolygonGraphicsItem::createGraphicsItem(placemark, ring); } else if (const auto poly = geodata_cast(object)) { item = GeoPolygonGraphicsItem::createGraphicsItem(placemark, poly); if (item->zValue() == 0) { item->setZValue(poly->renderOrder()); } + } else if (const auto building = geodata_cast(object)) { + item = GeoPolygonGraphicsItem::createGraphicsItem(placemark, building); } else if (const auto multigeo = geodata_cast(object)) { int rowCount = multigeo->size(); for (int row = 0; row < rowCount; ++row) { createGraphicsItemFromGeometry(multigeo->child(row), placemark, relations); } } else if (const auto multitrack = geodata_cast(object)) { int rowCount = multitrack->size(); for (int row = 0; row < rowCount; ++row) { createGraphicsItemFromGeometry(multitrack->child(row), placemark, relations); } } else if (const auto track = geodata_cast(object)) { item = new GeoTrackGraphicsItem(placemark, track); } if (!item) { return; } item->setRelations(relations); item->setStyleBuilder(m_styleBuilder); item->setVisible(item->visible() && placemark->isGloballyVisible()); item->setMinZoomLevel(m_styleBuilder->minimumZoomLevel(*placemark)); m_scene.addItem(item); } void GeometryLayerPrivate::createGraphicsItemFromOverlay(const GeoDataOverlay *overlay) { if (!overlay->isGloballyVisible()) { return; // Reconsider this when visibility can be changed dynamically } GeoGraphicsItem* item = 0; if (const auto photoOverlay = geodata_cast(overlay)) { GeoPhotoGraphicsItem *photoItem = new GeoPhotoGraphicsItem(overlay); photoItem->setPoint(photoOverlay->point()); item = photoItem; } else if (const auto screenOverlay = geodata_cast(overlay)) { ScreenOverlayGraphicsItem *screenItem = new ScreenOverlayGraphicsItem(screenOverlay); m_screenOverlays.push_back(screenItem); } if (item) { item->setStyleBuilder(m_styleBuilder); item->setVisible(overlay->isGloballyVisible()); m_scene.addItem(item); } } void GeometryLayerPrivate::removeGraphicsItems(const GeoDataFeature *feature) { clearCache(); if (const auto placemark = geodata_cast(feature)) { if (placemark->isGloballyVisible() && geodata_cast(placemark->geometry()) && placemark->hasOsmData() && placemark->osmData().oid() > 0) { auto & items = m_osmLineStringItems[placemark->osmData().oid()]; bool removed = false; for (auto item : items) { if (item->feature() == feature) { items.removeOne(item); removed = true; break; } } Q_ASSERT(removed); updateTiledLineStrings(items); } m_scene.removeItem(feature); } else if (const auto container = dynamic_cast(feature)) { for (const GeoDataFeature *child: container->featureList()) { removeGraphicsItems(child); } } else if (geodata_cast(feature)) { for (ScreenOverlayGraphicsItem *item: m_screenOverlays) { if (item->screenOverlay() == feature) { m_screenOverlays.removeAll(item); } } } } void GeometryLayer::addPlacemarks(const QModelIndex& parent, int first, int last) { Q_ASSERT(first < d->m_model->rowCount(parent)); Q_ASSERT(last < d->m_model->rowCount(parent)); for (int i = first; i <= last; ++i) { QModelIndex index = d->m_model->index(i, 0, parent); Q_ASSERT(index.isValid()); const GeoDataObject *object = qvariant_cast(index.data(MarblePlacemarkModel::ObjectPointerRole)); Q_ASSERT(object); d->createGraphicsItems(object); } emit repaintNeeded(); } void GeometryLayer::removePlacemarks(const QModelIndex& parent, int first, int last) { Q_ASSERT(last < d->m_model->rowCount(parent)); bool isRepaintNeeded = false; for (int i = first; i <= last; ++i) { QModelIndex index = d->m_model->index(i, 0, parent); Q_ASSERT(index.isValid()); const GeoDataObject *object = qvariant_cast(index.data(MarblePlacemarkModel::ObjectPointerRole)); const GeoDataFeature *feature = dynamic_cast(object); if (feature != 0) { d->removeGraphicsItems(feature); isRepaintNeeded = true; } } if (isRepaintNeeded) { emit repaintNeeded(); } } void GeometryLayer::resetCacheData() { d->clearCache(); d->m_scene.clear(); qDeleteAll(d->m_screenOverlays); d->m_screenOverlays.clear(); d->m_osmLineStringItems.clear(); const GeoDataObject *object = static_cast(d->m_model->index(0, 0, QModelIndex()).internalPointer()); if (object && object->parent()) { d->createGraphicsItems(object->parent()); } emit repaintNeeded(); } void GeometryLayer::setTileLevel(int tileLevel) { d->m_tileLevel = tileLevel; } QVector GeometryLayer::whichFeatureAt(const QPoint &curpos, const ViewportParams *viewport) { QVector result; auto const renderOrder = d->m_styleBuilder->renderOrder(); QString const label = QStringLiteral("/label"); QSet checked; for (int i = renderOrder.size()-1; i >= 0; --i) { if (renderOrder[i].endsWith(label)) { continue; } auto & layerItems = d->m_cachedPaintFragments[renderOrder[i]]; for (auto j = layerItems.size()-1; j >= 0; --j) { auto const & layerItem = layerItems[j]; if (!checked.contains(layerItem)) { if (layerItem->contains(curpos, viewport)) { result << layerItem->feature(); } checked << layerItem; } } } return result; } void GeometryLayer::highlightRouteRelation(qint64 osmId, bool enabled) { if (enabled) { d->m_highlightedRouteRelations << osmId; } else { d->m_highlightedRouteRelations.remove(osmId); } d->updateRelationVisibility(); } void GeometryLayer::setVisibleRelationTypes(GeoDataRelation::RelationTypes relationTypes) { if (relationTypes != d->m_visibleRelationTypes) { d->m_visibleRelationTypes = relationTypes; d->updateRelationVisibility(); } } void GeometryLayer::handleHighlight(qreal lon, qreal lat, GeoDataCoordinates::Unit unit) { GeoDataCoordinates clickedPoint(lon, lat, 0, unit); QVector selectedPlacemarks; for (int i = 0; i < d->m_model->rowCount(); ++i) { QVariant const data = d->m_model->data(d->m_model->index(i, 0), MarblePlacemarkModel::ObjectPointerRole); GeoDataObject *object = qvariant_cast (data); Q_ASSERT(object); if (const auto doc = geodata_cast(object)) { bool isHighlight = false; for (const GeoDataStyleMap &styleMap: doc->styleMaps()) { if (styleMap.contains(QStringLiteral("highlight"))) { isHighlight = true; break; } } /* * If a document doesn't specify any highlight * styleId in its style maps then there is no need * to further check that document for placemarks * which have been clicked because we won't * highlight them. */ if (isHighlight) { QVector::Iterator iter = doc->begin(); QVector::Iterator const end = doc->end(); for (; iter != end; ++iter) { if (auto placemark = geodata_cast(*iter)) { GeoDataPolygon *polygon = dynamic_cast(placemark->geometry()); if (polygon && polygon->contains(clickedPoint)) { selectedPlacemarks.push_back(placemark); } if (auto linearRing = geodata_cast(placemark->geometry())) { if (linearRing->contains(clickedPoint)) { selectedPlacemarks.push_back(placemark); } } if (auto multiGeometry = geodata_cast(placemark->geometry())) { QVector::Iterator multiIter = multiGeometry->begin(); QVector::Iterator const multiEnd = multiGeometry->end(); for (; multiIter != multiEnd; ++multiIter) { GeoDataPolygon *poly = dynamic_cast(*multiIter); if (poly && poly->contains(clickedPoint)) { selectedPlacemarks.push_back(placemark); break; } if (auto linearRing = geodata_cast(*multiIter)) { if (linearRing->contains(clickedPoint)) { selectedPlacemarks.push_back(placemark); break; } } } } } } } } } emit highlightedPlacemarksChanged(selectedPlacemarks); } } #include "moc_GeometryLayer.cpp" diff --git a/src/lib/marble/osm/OsmObjectManager.cpp b/src/lib/marble/osm/OsmObjectManager.cpp index be71b3b7b..f87c4e466 100644 --- a/src/lib/marble/osm/OsmObjectManager.cpp +++ b/src/lib/marble/osm/OsmObjectManager.cpp @@ -1,108 +1,124 @@ // // 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 Stanciu Marius-Valeriu // // Self #include "OsmObjectManager.h" // Marble #include "GeoDataPlacemark.h" #include "GeoDataLinearRing.h" #include "GeoDataPolygon.h" +#include "GeoDataBuilding.h" +#include "GeoDataMultiGeometry.h" #include "osm/OsmPlacemarkData.h" namespace Marble { qint64 OsmObjectManager::m_minId = -1; void OsmObjectManager::initializeOsmData( GeoDataPlacemark* placemark ) { OsmPlacemarkData &osmData = placemark->osmData(); bool isNull = osmData.isNull(); if ( isNull ) { // The "--m_minId" assignments mean: assigning an id lower( by 1 ) than the current lowest, // and updating the current lowest id. osmData.setId( --m_minId ); } // Assigning osmData to each of the line's nodes ( if they don't already have data ) if (const auto lineString = geodata_cast(placemark->geometry())) { QVector::const_iterator it = lineString->constBegin(); QVector::ConstIterator const end = lineString->constEnd(); for ( ; it != end; ++it ) { if (osmData.nodeReference(*it).isNull()) { osmData.nodeReference(*it).setId(--m_minId); } } } + const auto building = geodata_cast(placemark->geometry()); + + GeoDataLinearRing* lineString; + if (building) { + lineString = geodata_cast(&building->multiGeometry()->at(0)); + } else { + lineString = geodata_cast(placemark->geometry()); + } // Assigning osmData to each of the line's nodes ( if they don't already have data ) - if (const auto lineString = geodata_cast(placemark->geometry())) { + if (lineString) { for (auto it =lineString->constBegin(), end = lineString->constEnd(); it != end; ++it ) { if (osmData.nodeReference(*it).isNull()) { osmData.nodeReference(*it).setId(--m_minId); } } } + GeoDataPolygon* polygon; + if (building) { + polygon = geodata_cast(&building->multiGeometry()->at(0)); + } else { + polygon = geodata_cast(placemark->geometry()); + } // Assigning osmData to each of the polygons boundaries, and to each of the // nodes that are part of those boundaries ( if they don't already have data ) - if (const auto polygon = geodata_cast(placemark->geometry())) { + if (polygon) { const GeoDataLinearRing &outerBoundary = polygon->outerBoundary(); int index = -1; if ( isNull ) { osmData.addTag(QStringLiteral("type"), QStringLiteral("multipolygon")); } // Outer boundary OsmPlacemarkData &outerBoundaryData = osmData.memberReference( index ); if (outerBoundaryData.isNull()) { outerBoundaryData.setId(--m_minId); } // Outer boundary nodes QVector::const_iterator it = outerBoundary.constBegin(); QVector::ConstIterator const end = outerBoundary.constEnd(); for ( ; it != end; ++it ) { if (outerBoundaryData.nodeReference(*it).isNull()) { outerBoundaryData.nodeReference(*it).setId(--m_minId); } } // Each inner boundary for( const GeoDataLinearRing &innerRing: polygon->innerBoundaries() ) { ++index; OsmPlacemarkData &innerRingData = osmData.memberReference( index ); if (innerRingData.isNull()) { innerRingData.setId(--m_minId); } // Inner boundary nodes QVector::const_iterator it = innerRing.constBegin(); QVector::ConstIterator const end = innerRing.constEnd(); for ( ; it != end; ++it ) { if (innerRingData.nodeReference(*it).isNull()) { innerRingData.nodeReference(*it).setId(--m_minId); } } } } } void OsmObjectManager::registerId( qint64 id ) { m_minId = qMin( id, m_minId ); } } diff --git a/src/plugins/runner/osm/OsmWay.cpp b/src/plugins/runner/osm/OsmWay.cpp index 89a29824c..41d927b44 100644 --- a/src/plugins/runner/osm/OsmWay.cpp +++ b/src/plugins/runner/osm/OsmWay.cpp @@ -1,192 +1,277 @@ // // 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 +#include namespace Marble { QSet OsmWay::s_areaTags; +QSet OsmWay::s_buildingTags; GeoDataPlacemark *OsmWay::create(const OsmNodes &nodes, QSet &usedNodes) const { OsmPlacemarkData osmData = m_osmData; GeoDataGeometry *geometry = 0; if (isArea()) { GeoDataLinearRing linearRing; linearRing.reserve(m_references.size()); bool const stripLastNode = m_references.first() == m_references.last(); for (int i=0, n=m_references.size() - (stripLastNode ? 1 : 0); iappend(new GeoDataLinearRing(linearRing.optimized())); + + geometry = new GeoDataBuilding(building); + } else { + geometry = new GeoDataLinearRing(linearRing.optimized()); + } } else { GeoDataLineString lineString; lineString.reserve(m_references.size()); for(auto nodeId: m_references) { auto const nodeIter = nodes.constFind(nodeId); if (nodeIter == nodes.constEnd()) { return nullptr; } OsmNode const & node = nodeIter.value(); osmData.addNodeReference(node.coordinates(), node.osmData()); lineString.append(node.coordinates()); usedNodes << nodeId; } geometry = new GeoDataLineString(lineString.optimized()); } Q_ASSERT(geometry != nullptr); 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"))); } placemark->setOsmData(osmData); placemark->setZoomLevel(StyleBuilder::minimumZoomLevel(placemark->visualCategory())); placemark->setPopularity(StyleBuilder::popularity(placemark)); placemark->setVisible(placemark->visualCategory() != GeoDataPlacemark::None); return 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 if (m_osmData.containsTag(QStringLiteral("area"), QStringLiteral("yes"))) { return true; } 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"))); for (auto const & 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); } +bool OsmWay::isBuilding() const +{ + for (auto iter = m_osmData.tagsBegin(), end=m_osmData.tagsEnd(); iter != end; ++iter) { + const auto tag = StyleBuilder::OsmTag(iter.key(), iter.value()); + if (isBuildingTag(tag)) { + return true; + } + } + + return false; +} + +bool OsmWay::isBuildingTag(const StyleBuilder::OsmTag &keyValue) +{ + if (s_buildingTags.isEmpty()) { + for (auto const & tag: StyleBuilder::buildingTags()) { + s_buildingTags.insert(tag); + } + } + + return s_buildingTags.contains(keyValue); +} + +QString OsmWay::extractBuildingName() const +{ + auto tagIter = m_osmData.findTag(QStringLiteral("addr:housename")); + if (tagIter != m_osmData.tagsEnd()) { + return tagIter.value(); + } + + tagIter = m_osmData.findTag(QStringLiteral("addr:housenumber")); + if (tagIter != m_osmData.tagsEnd()) { + return tagIter.value(); + } + + return QString(); +} + +double OsmWay::extractBuildingHeight() const +{ + double height = 8.0; + + QHash::const_iterator tagIter; + if ((tagIter = m_osmData.findTag(QStringLiteral("height"))) != m_osmData.tagsEnd()) { + height = GeoDataBuilding::parseBuildingHeight(tagIter.value()); + } else if ((tagIter = m_osmData.findTag(QStringLiteral("building:levels"))) != m_osmData.tagsEnd()) { + int const levels = tagIter.value().toInt(); + int const skipLevels = m_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); +} + +QVector OsmWay::extractNamedEntries() const +{ + QVector entries; + + const auto end = m_osmData.nodeReferencesEnd(); + for (auto iter = m_osmData.nodeReferencesBegin(); iter != end; ++iter) { + const auto tagIter = iter.value().findTag(QStringLiteral("addr:housenumber")); + if (tagIter != iter.value().tagsEnd()) { + GeoDataBuilding::NamedEntry entry; + entry.point = iter.key(); + entry.label = tagIter.value(); + entries.push_back(entry); + } + } + + return entries; +} + } diff --git a/src/plugins/runner/osm/OsmWay.h b/src/plugins/runner/osm/OsmWay.h index 1fa73dd1f..6234920ef 100644 --- a/src/plugins/runner/osm/OsmWay.h +++ b/src/plugins/runner/osm/OsmWay.h @@ -1,51 +1,61 @@ // // 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 // #ifndef MARBLE_OSMWAY #define MARBLE_OSMWAY #include "OsmNode.h" +#include "GeoDataBuilding.h" #include #include #include #include namespace Marble { class GeoDataDocument; class OsmWay { public: OsmPlacemarkData & osmData(); void addReference(qint64 id); const OsmPlacemarkData & osmData() const; const QVector &references() const; GeoDataPlacemark* create(const OsmNodes &nodes, QSet &usedNodes) const; private: bool isArea() const; static bool isAreaTag(const StyleBuilder::OsmTag &keyValue); + bool isBuilding() const; + + static bool isBuildingTag(const StyleBuilder::OsmTag &keyValue); + OsmPlacemarkData m_osmData; QVector m_references; static QSet s_areaTags; + static QSet s_buildingTags; + + QString extractBuildingName() const; + double extractBuildingHeight() const; + QVector extractNamedEntries() const; }; typedef QHash OsmWays; } #endif diff --git a/src/plugins/runner/osm/translators/O5mWriter.cpp b/src/plugins/runner/osm/translators/O5mWriter.cpp index 45a66be34..5e3c9218f 100644 --- a/src/plugins/runner/osm/translators/O5mWriter.cpp +++ b/src/plugins/runner/osm/translators/O5mWriter.cpp @@ -1,341 +1,349 @@ // // 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 2016 Dennis Nienhüser // #include "O5mWriter.h" #include "GeoDataDocument.h" #include "GeoDataLineString.h" #include "GeoDataLinearRing.h" #include "GeoDataPlacemark.h" #include "GeoDataRelation.h" #include "GeoDataPolygon.h" +#include "GeoDataBuilding.h" +#include "GeoDataMultiGeometry.h" #include "GeoWriter.h" #include "osm/OsmPlacemarkData.h" #include #include namespace Marble { QSet O5mWriter::m_blacklistedTags; bool O5mWriter::write(QIODevice *device, const GeoDataDocument &document) { if (!device || !device->isWritable()) { return false; } OsmConverter converter; converter.read(&document); QDataStream stream(device); writeHeader(stream); writeNodes(converter.nodes(), stream); writeWays(converter.ways(), stream); writeRelations(converter.relations(), stream); writeTrailer(stream); return true; } void O5mWriter::writeHeader(QDataStream &stream) const { stream << qint8(0xff); // o5m file start indicator stream << qint8(0xe0); // o5m header block indicator stream << qint8(0x04) << qint8(0x6f) << qint8(0x35) << qint8(0x6d) << qint8(0x32); // o5m header } void O5mWriter::writeNodes(const OsmConverter::Nodes &nodes, QDataStream &stream) const { if (nodes.empty()) { return; } stream << qint8(0xff); // reset delta encoding counters StringTable stringTable; qint64 lastId = 0; double lastLon = 0.0; double lastLat = 0.0; for(auto const & node: nodes) { if (node.second.id() == lastId) { continue; } stream << qint8(0x10); // node section start indicator QBuffer buffer; buffer.open(QIODevice::WriteOnly); QDataStream bufferStream(&buffer); OsmPlacemarkData const & osmData = node.second; qint64 idDiff = osmData.id() - lastId; writeSigned(idDiff, bufferStream); writeVersion(osmData, bufferStream); GeoDataCoordinates const & coordinates = node.first; double const lon = coordinates.longitude(GeoDataCoordinates::Degree); double const lat = coordinates.latitude(GeoDataCoordinates::Degree); writeSigned(deltaTo(lon, lastLon), bufferStream); writeSigned(deltaTo(lat, lastLat), bufferStream); writeTags(osmData, stringTable, bufferStream); writeUnsigned(buffer.size(), stream); stream.writeRawData(buffer.data().constData(), buffer.size()); lastId = osmData.id(); lastLon = lon; lastLat = lat; } } void O5mWriter::writeWays(const OsmConverter::Ways &ways, QDataStream &stream) const { if (ways.empty()) { return; } stream << qint8(0xff); // reset delta encoding counters StringTable stringTable; qint64 lastId = 0; qint64 lastReferenceId = 0; for (auto const & way: ways) { Q_ASSERT(way.first); if (way.second.id() == lastId) { continue; } stream << qint8(0x11); // way start indicator OsmPlacemarkData const & osmData = way.second; QBuffer buffer; buffer.open(QIODevice::WriteOnly); QDataStream bufferStream(&buffer); qint64 idDiff = osmData.id() - lastId; writeSigned(idDiff, bufferStream); lastId = osmData.id(); writeVersion(osmData, bufferStream); QBuffer referencesBuffer; referencesBuffer.open(QIODevice::WriteOnly); QDataStream referencesStream(&referencesBuffer); writeReferences(*way.first, lastReferenceId, osmData, referencesStream); writeUnsigned(referencesBuffer.size(), bufferStream); bufferStream.writeRawData(referencesBuffer.data().constData(), referencesBuffer.size()); writeTags(osmData, stringTable, bufferStream); writeUnsigned(buffer.size(), stream); stream.writeRawData(buffer.data().constData(), buffer.size()); } } void O5mWriter::writeRelations(const OsmConverter::Relations &relations, QDataStream &stream) const { if (relations.empty()) { return; } stream << qint8(0xff); // reset delta encoding counters StringTable stringTable; qint64 lastId = 0; qint64 lastReferenceId = 0; for (auto const & relation: relations) { if (relation.second.id() == lastId) { continue; } stream << qint8(0x12); // relation start indicator OsmPlacemarkData const & osmData = relation.second; QBuffer buffer; buffer.open(QIODevice::WriteOnly); QDataStream bufferStream(&buffer); qint64 idDiff = osmData.id() - lastId; writeSigned(idDiff, bufferStream); lastId = osmData.id(); writeVersion(osmData, bufferStream); QBuffer referencesBuffer; referencesBuffer.open(QIODevice::WriteOnly); QDataStream referencesStream(&referencesBuffer); if (const auto placemark = geodata_cast(relation.first)) { - auto polygon = geodata_cast(placemark->geometry()); - Q_ASSERT(polygon); - writeMultipolygonMembers(*polygon, lastReferenceId, osmData, stringTable, referencesStream); + if (const auto building = geodata_cast(placemark->geometry())) { + auto polygon = geodata_cast(&building->multiGeometry()->at(0)); + Q_ASSERT(polygon); + writeMultipolygonMembers(*polygon, lastReferenceId, osmData, stringTable, referencesStream); + } else { + auto polygon = geodata_cast(placemark->geometry()); + Q_ASSERT(polygon); + writeMultipolygonMembers(*polygon, lastReferenceId, osmData, stringTable, referencesStream); + } } else if (const auto placemark = geodata_cast(relation.first)) { writeRelationMembers(placemark, lastReferenceId, osmData, stringTable, referencesStream); } else { Q_ASSERT(false); } writeUnsigned(referencesBuffer.size(), bufferStream); bufferStream.writeRawData(referencesBuffer.data().constData(), referencesBuffer.size()); writeTags(osmData, stringTable, bufferStream); writeUnsigned(buffer.size(), stream); stream.writeRawData(buffer.data().constData(), buffer.size()); } } void O5mWriter::writeTrailer(QDataStream &stream) const { stream << qint8(0xfe); // o5m file end indicator } void O5mWriter::writeMultipolygonMembers(const GeoDataPolygon &polygon, qint64 &lastId, const OsmPlacemarkData &osmData, StringTable &stringTable, QDataStream &stream) const { qint64 id = osmData.memberReference(-1).id(); qint64 idDiff = id - lastId; writeSigned(idDiff, stream); lastId = id; writeStringPair(StringPair("1outer", QString()), stringTable, stream); // type=way, role=outer for (int index = 0; index < polygon.innerBoundaries().size(); ++index) { id = osmData.memberReference( index ).id(); qint64 idDiff = id - lastId; writeSigned(idDiff, stream); writeStringPair(StringPair("1inner", QString()), stringTable, stream); // type=way, role=inner lastId = id; } } void O5mWriter::writeRelationMembers(const GeoDataRelation *relation, qint64 &lastId, const OsmPlacemarkData &osmData, O5mWriter::StringTable &stringTable, QDataStream &stream) const { Q_UNUSED(relation); for (auto iter = osmData.relationReferencesBegin(), end = osmData.relationReferencesEnd(); iter != end; ++iter) { qint64 id = iter.key(); qint64 idDiff = id - lastId; writeSigned(idDiff, stream); auto const key = QString("1%1").arg(iter.value()); writeStringPair(StringPair(key, QString()), stringTable, stream); // type=way, role=... lastId = id; } } void O5mWriter::writeReferences(const GeoDataLineString &lineString, qint64 &lastId, const OsmPlacemarkData &osmData, QDataStream &stream) const { QVector::const_iterator it = lineString.constBegin(); QVector::ConstIterator const end = lineString.constEnd(); for ( ; it != end; ++it ) { qint64 id = osmData.nodeReference( *it ).id(); qint64 idDiff = id - lastId; writeSigned(idDiff, stream); lastId = id; } if (!lineString.isEmpty() && lineString.isClosed()) { auto const startId = osmData.nodeReference(lineString.first()).id(); auto const endId = osmData.nodeReference(lineString.last()).id(); if (startId != endId) { qint64 idDiff = startId - lastId; writeSigned(idDiff, stream); lastId = startId; } } } void O5mWriter::writeVersion(const OsmPlacemarkData &, QDataStream &stream) const { stream << qint8(0x00); // no version information /** @todo implement */ } void O5mWriter::writeTags(const OsmPlacemarkData &osmData, StringTable &stringTable, QDataStream &stream) const { if (m_blacklistedTags.isEmpty()) { m_blacklistedTags << QStringLiteral("mx:version"); m_blacklistedTags << QStringLiteral("mx:changeset"); m_blacklistedTags << QStringLiteral("mx:uid"); m_blacklistedTags << QStringLiteral("mx:visible"); m_blacklistedTags << QStringLiteral("mx:user"); m_blacklistedTags << QStringLiteral("mx:timestamp"); m_blacklistedTags << QStringLiteral("mx:action"); } for (auto iter=osmData.tagsBegin(), end = osmData.tagsEnd(); iter != end; ++iter) { if (!m_blacklistedTags.contains(iter.key())) { writeStringPair(StringPair(iter.key(), iter.value()), stringTable, stream); } } } void O5mWriter::writeStringPair(const StringPair &pair, StringTable &stringTable, QDataStream &stream) const { Q_ASSERT(stringTable.size() <= 15000); auto const iter = stringTable.constFind(pair); if (iter == stringTable.cend()) { QByteArray data; data.push_back(char(0x00)); data.push_back(pair.first.toUtf8()); if (!pair.second.isEmpty()) { data.push_back(char(0x00)); data.push_back(pair.second.toUtf8()); } data.push_back(char(0x00)); stream.writeRawData(data.constData(), data.size()); bool const tooLong = pair.first.size() + pair.second.size() > 250; bool const tableFull = stringTable.size() > 15000; if (!tooLong && !tableFull) { /* When the table is full, old values could be re-used. * See o5m spec. This is only relevant for large files and would * need some kind of string popularity to be effective though. */ stringTable.insert(pair, stringTable.size()); } } else { auto const reference = stringTable.size() - iter.value(); Q_ASSERT(reference >= 0); writeUnsigned(reference, stream); } } void O5mWriter::writeSigned(qint64 value, QDataStream &stream) const { bool const negative = value < 0; if (negative) { value = -value - 1; } quint8 word = (value >> 6) > 0 ? (1<<7) : 0; word |= ( (value << 1) & 0x7e); if (negative) { word |= 0x01; } value >>= 6; stream << word; while (value > 0) { word = ((value >> 7) > 0 ? 0x80 : 0x00) | (value & 0x7f); stream << word; value >>= 7; } } void O5mWriter::writeUnsigned(quint32 value, QDataStream &stream) const { do { quint8 word = ((value >> 7) > 0 ? 0x80 : 0x00) | (value & 0x7f); stream << word; value >>= 7; } while (value > 0); } qint32 O5mWriter::deltaTo(double value, double previous) const { double const diff = value - previous; return qRound(diff * 1e7); } MARBLE_ADD_WRITER(O5mWriter, "o5m") } diff --git a/src/plugins/runner/osm/translators/OsmConverter.cpp b/src/plugins/runner/osm/translators/OsmConverter.cpp index 8131ef0ea..a5e045de5 100644 --- a/src/plugins/runner/osm/translators/OsmConverter.cpp +++ b/src/plugins/runner/osm/translators/OsmConverter.cpp @@ -1,109 +1,130 @@ // // 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 2016 Dennis Nienhüser // //Self #include "OsmDocumentTagTranslator.h" //Marble #include "OsmNodeTagWriter.h" #include "OsmWayTagWriter.h" #include "OsmElementDictionary.h" #include "GeoDataDocument.h" #include "GeoWriter.h" #include "GeoDataPlacemark.h" #include "GeoDataGeometry.h" #include "GeoDataPoint.h" #include "GeoDataPolygon.h" #include "GeoDataRelation.h" #include "GeoDataLinearRing.h" +#include "GeoDataBuilding.h" +#include "GeoDataMultiGeometry.h" #include "osm/OsmPlacemarkData.h" #include "osm/OsmObjectManager.h" #include "OsmRelationTagWriter.h" #include namespace Marble { void OsmConverter::read(const GeoDataDocument *document) { m_nodes.clear(); m_ways.clear(); m_relations.clear(); // Writing all the component nodes ( points, nodes of polylines, nodes of polygons ) for (auto feature: document->featureList()) { if (auto placemark = geodata_cast(feature)) { // If the placemark's osmData is not complete, it is initialized by the OsmObjectManager OsmObjectManager::initializeOsmData( placemark ); const OsmPlacemarkData & osmData = placemark->osmData(); if (geodata_cast(placemark->geometry())) { m_nodes << OsmConverter::Node(placemark->coordinate(), osmData); } else if (const auto lineString = geodata_cast(placemark->geometry())) { for (auto const &coordinates: *lineString) { m_nodes << OsmConverter::Node(coordinates, osmData.nodeReference(coordinates)); } m_ways << OsmConverter::Way(lineString, osmData); } else if (const auto linearRing = geodata_cast(placemark->geometry())) { - for (auto const &coordinates: *linearRing) { - m_nodes << OsmConverter::Node(coordinates, osmData.nodeReference(coordinates)); - } - m_ways << OsmConverter::Way(linearRing, osmData); + processLinearRing(linearRing, osmData); } else if (const auto polygon = geodata_cast(placemark->geometry())) { - int index = -1; - - // Writing all the outerRing's nodes - const GeoDataLinearRing &outerRing = polygon->outerBoundary(); - const OsmPlacemarkData outerRingOsmData = osmData.memberReference( index ); - for (auto const &coordinates: outerRing) { - m_nodes << OsmConverter::Node(coordinates, outerRingOsmData.nodeReference(coordinates)); - } - m_ways << OsmConverter::Way(&outerRing, outerRingOsmData); - - // Writing all nodes for each innerRing - for (auto const &innerRing: polygon->innerBoundaries() ) { - ++index; - const OsmPlacemarkData innerRingOsmData = osmData.memberReference( index ); - for (auto const &coordinates: innerRing) { - m_nodes << OsmConverter::Node(coordinates, innerRingOsmData.nodeReference(coordinates)); - } - m_ways << OsmConverter::Way(&innerRing, innerRingOsmData); + processPolygon(polygon, osmData, placemark); + } else if (const auto building = geodata_cast(placemark->geometry())) { + if (const auto linearRing = geodata_cast(&building->multiGeometry()->at(0))) { + processLinearRing(linearRing, osmData); + } else if (const auto polygon = geodata_cast(&building->multiGeometry()->at(0))) { + processPolygon(polygon, osmData, placemark); } - m_relations.append(OsmConverter::Relation(placemark, osmData)); } } else if (const auto placemark = geodata_cast(feature)) { m_relations.append(OsmConverter::Relation(placemark, placemark->osmData())); } } // Sort by id ascending since some external tools rely on that std::sort(m_nodes.begin(), m_nodes.end(), [] (const Node &a, const Node &b) { return a.second.id() < b.second.id(); }); std::sort(m_ways.begin(), m_ways.end(), [] (const Way &a, const Way &b) { return a.second.id() < b.second.id(); }); std::sort(m_relations.begin(), m_relations.end(), [] (const Relation &a, const Relation &b) { return a.second.id() < b.second.id(); }); } const OsmConverter::Nodes &OsmConverter::nodes() const { return m_nodes; } const OsmConverter::Ways &OsmConverter::ways() const { return m_ways; } const OsmConverter::Relations &OsmConverter::relations() const { return m_relations; } +void OsmConverter::processLinearRing(GeoDataLinearRing *linearRing, + const OsmPlacemarkData& osmData) +{ + for (auto const &coordinates: *linearRing) { + m_nodes << OsmConverter::Node(coordinates, osmData.nodeReference(coordinates)); + } + m_ways << OsmConverter::Way(linearRing, osmData); +} + +void OsmConverter::processPolygon(GeoDataPolygon *polygon, + const OsmPlacemarkData& osmData, + GeoDataPlacemark* placemark) +{ + int index = -1; + + // Writing all the outerRing's nodes + const GeoDataLinearRing &outerRing = polygon->outerBoundary(); + const OsmPlacemarkData outerRingOsmData = osmData.memberReference( index ); + for (auto const &coordinates: outerRing) { + m_nodes << OsmConverter::Node(coordinates, outerRingOsmData.nodeReference(coordinates)); + } + m_ways << OsmConverter::Way(&outerRing, outerRingOsmData); + + // Writing all nodes for each innerRing + for (auto const &innerRing: polygon->innerBoundaries() ) { + ++index; + const OsmPlacemarkData innerRingOsmData = osmData.memberReference( index ); + for (auto const &coordinates: innerRing) { + m_nodes << OsmConverter::Node(coordinates, innerRingOsmData.nodeReference(coordinates)); + } + m_ways << OsmConverter::Way(&innerRing, innerRingOsmData); + } + m_relations.append(OsmConverter::Relation(placemark, osmData)); +} + } diff --git a/src/plugins/runner/osm/translators/OsmConverter.h b/src/plugins/runner/osm/translators/OsmConverter.h index 4f9d28e57..585a92122 100644 --- a/src/plugins/runner/osm/translators/OsmConverter.h +++ b/src/plugins/runner/osm/translators/OsmConverter.h @@ -1,53 +1,61 @@ // // 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 2016 Dennis Nienhüser // #ifndef MARBLE_OSMCONVERTER_H #define MARBLE_OSMCONVERTER_H #include namespace Marble { class GeoDataLineString; class GeoDataDocument; +class GeoDataLinearRing; class GeoDataPolygon; +class GeoDataPlacemark; class GeoDataFeature; class OsmPlacemarkData; class OsmConverter { public: typedef QPair Tag; typedef QPair Node; typedef QPair Way; typedef QPair Relation; typedef QVector Nodes; typedef QVector Tags; typedef QVector Ways; typedef QVector Relations; void read(const GeoDataDocument* document); const Nodes & nodes() const; const Ways & ways() const; const Relations &relations() const; private: Nodes m_nodes; Ways m_ways; Relations m_relations; + + void processLinearRing(GeoDataLinearRing *linearRing, + const OsmPlacemarkData& osmData); + void processPolygon(GeoDataPolygon *polygon, + const OsmPlacemarkData& osmData, + GeoDataPlacemark* placemark); }; } #endif diff --git a/src/plugins/runner/osm/translators/OsmDocumentTagTranslator.cpp b/src/plugins/runner/osm/translators/OsmDocumentTagTranslator.cpp index 376044633..1ec8750a8 100644 --- a/src/plugins/runner/osm/translators/OsmDocumentTagTranslator.cpp +++ b/src/plugins/runner/osm/translators/OsmDocumentTagTranslator.cpp @@ -1,69 +1,77 @@ // // 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 Stanciu Marius-Valeriu // //Self #include "OsmDocumentTagTranslator.h" //Marble #include "OsmNodeTagWriter.h" #include "OsmWayTagWriter.h" #include "OsmElementDictionary.h" #include "GeoDataDocument.h" #include "GeoWriter.h" #include "GeoDataGeometry.h" #include "GeoDataPoint.h" #include "GeoDataPolygon.h" +#include "GeoDataBuilding.h" +#include "GeoDataMultiGeometry.h" #include "GeoDataPlacemark.h" #include "GeoDataLinearRing.h" #include "GeoDataTypes.h" #include "osm/OsmPlacemarkData.h" #include "osm/OsmObjectManager.h" #include "OsmRelationTagWriter.h" #include "OsmConverter.h" #include namespace Marble { static GeoTagWriterRegistrar s_writerDocument( GeoTagWriter::QualifiedName( GeoDataTypes::GeoDataDocumentType, osm::osmTag_version06 ), new OsmDocumentTagTranslator() ); bool OsmDocumentTagTranslator::write( const GeoNode *node, GeoWriter& writer ) const { const GeoDataDocument *document = static_cast(node); OsmConverter converter; converter.read(document); OsmNodeTagWriter::writeAllNodes(converter.nodes(), writer); qint64 lastId = 0; for (auto const &way: converter.ways()) { if (way.second.id() != lastId) { OsmWayTagWriter::writeWay(*way.first, way.second, writer); lastId = way.second.id(); } } for (auto const & relation: converter.relations()) { if (auto placemark = geodata_cast(relation.first)) { - auto polygon = geodata_cast(placemark->geometry()); - Q_ASSERT(polygon); - OsmRelationTagWriter::writeMultipolygon(*polygon, relation.second, writer ); + if (const auto building = geodata_cast(placemark->geometry())) { + auto polygon = geodata_cast(&building->multiGeometry()->at(0)); + Q_ASSERT(polygon); + OsmRelationTagWriter::writeMultipolygon(*polygon, relation.second, writer ); + } else { + auto polygon = geodata_cast(placemark->geometry()); + Q_ASSERT(polygon); + OsmRelationTagWriter::writeMultipolygon(*polygon, relation.second, writer ); + } } } return true; } } diff --git a/tests/TestGeoDataBuilding.cpp b/tests/TestGeoDataBuilding.cpp index 2906dbcf0..62fd6c0d3 100644 --- a/tests/TestGeoDataBuilding.cpp +++ b/tests/TestGeoDataBuilding.cpp @@ -1,69 +1,99 @@ // // 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 2017 Mohammed Nafees // #include -#include +#include "TestUtils.h" #include "GeoDataBuilding.h" #include "GeoDataMultiGeometry.h" #include "GeoDataLinearRing.h" namespace Marble { class TestGeoDataBuilding : public QObject { Q_OBJECT private Q_SLOTS: void defaultConstructor(); + void testHeightExtraction(); }; void TestGeoDataBuilding::defaultConstructor() { GeoDataBuilding building; QCOMPARE(building.height(), 0.0); QCOMPARE(building.minLevel(), 0); QCOMPARE(building.maxLevel(), 0); building.setHeight(24.5); building.setMinLevel(-2); building.setMaxLevel(10); QCOMPARE(building.height(), 24.5); QCOMPARE(building.minLevel(), -2); QCOMPARE(building.maxLevel(), 10); QVERIFY(building.nonExistentLevels().isEmpty()); QVector nonExistentLevels; nonExistentLevels << 4 << 13; building.setNonExistentLevels(nonExistentLevels); QVERIFY(!building.nonExistentLevels().isEmpty()); QVERIFY(building.multiGeometry()->size() == 0); building.multiGeometry()->append(new GeoDataLinearRing); QVERIFY(building.multiGeometry()->size() > 0); GeoDataBuilding building2(building); QCOMPARE(building2.height(), 24.5); QCOMPARE(building2.minLevel(), -2); QCOMPARE(building2.maxLevel(), 10); QVERIFY(!building2.nonExistentLevels().isEmpty()); QVERIFY(building2.multiGeometry()->size() > 0); } +void TestGeoDataBuilding::testHeightExtraction() +{ + QString const meters1 = "12 m"; + QString const meters2 = "12.8 meters"; + QString const meters3 = "12.56 meter"; + QString const meters4 = "14.44 metres"; + QString const meters5 = "23.43 metre"; + + QFUZZYCOMPARE(GeoDataBuilding::parseBuildingHeight(meters1), 12.0, 0.0001); + QFUZZYCOMPARE(GeoDataBuilding::parseBuildingHeight(meters2), 12.8, 0.0001); + QFUZZYCOMPARE(GeoDataBuilding::parseBuildingHeight(meters3), 12.56, 0.0001); + QFUZZYCOMPARE(GeoDataBuilding::parseBuildingHeight(meters4), 14.44, 0.0001); + QFUZZYCOMPARE(GeoDataBuilding::parseBuildingHeight(meters5), 23.43, 0.0001); + + QString const feet1 = "55'4\""; // 664 inches + QString const feet2 = "60.56 feet"; // 726.72 inches + QString const feet3 = "300\'"; // 3600 inches + + QFUZZYCOMPARE(GeoDataBuilding::parseBuildingHeight(feet1), 16.8656, 0.0001); + QFUZZYCOMPARE(GeoDataBuilding::parseBuildingHeight(feet2), 18.4587, 0.0001); + QFUZZYCOMPARE(GeoDataBuilding::parseBuildingHeight(feet3), 91.44, 0.0001); + + QString const unitless1 = "0.8"; // default in meters + QString const unitless2 = "12"; // default in meters + + QFUZZYCOMPARE(GeoDataBuilding::parseBuildingHeight(unitless1), 0.8, 0.0001); + QFUZZYCOMPARE(GeoDataBuilding::parseBuildingHeight(unitless2), 12.0, 0.0001); +} + } QTEST_MAIN(Marble::TestGeoDataBuilding) #include "TestGeoDataBuilding.moc" diff --git a/tools/vectorosm-tilecreator/NodeReducer.cpp b/tools/vectorosm-tilecreator/NodeReducer.cpp index 579049e24..54afeaa82 100644 --- a/tools/vectorosm-tilecreator/NodeReducer.cpp +++ b/tools/vectorosm-tilecreator/NodeReducer.cpp @@ -1,190 +1,220 @@ // // 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 2016 Akshat Tandon // #include "GeoDataPlacemark.h" #include "GeoDataLineString.h" #include "GeoDataPolygon.h" +#include "GeoDataBuilding.h" +#include "GeoDataMultiGeometry.h" #include "GeoDataCoordinates.h" #include "MarbleMath.h" #include "NodeReducer.h" #include "OsmPlacemarkData.h" #include #include namespace Marble { NodeReducer::NodeReducer(GeoDataDocument* document, const TileId &tileId) : m_removedNodes(0), m_remainingNodes(0), m_zoomLevel(tileId.zoomLevel()) { const GeoSceneMercatorTileProjection tileProjection; GeoDataLatLonBox tileBoundary = tileProjection.geoCoordinates(m_zoomLevel, tileId.x(), tileId.y()); tileBoundary.scale(1.0-1e-4, 1.0-1e-4); tileBoundary.boundaries(m_tileBoundary[North], m_tileBoundary[South], m_tileBoundary[East], m_tileBoundary[West]); for (GeoDataPlacemark* placemark: document->placemarkList()) { GeoDataGeometry const * const geometry = placemark->geometry(); auto const visualCategory = placemark->visualCategory(); if (const auto prevLine = geodata_cast(geometry)) { GeoDataLineString* reducedLine = new GeoDataLineString; reduce(*prevLine, placemark->osmData(), visualCategory, reducedLine); placemark->setGeometry(reducedLine); } else if (m_zoomLevel < 17) { if (const auto prevRing = geodata_cast(geometry)) { - GeoDataLinearRing* reducedRing = new GeoDataLinearRing; - reduce(*prevRing, placemark->osmData(), visualCategory, reducedRing); - placemark->setGeometry(reducedRing); + placemark->setGeometry(reducedRing(*prevRing, placemark, visualCategory)); } else if (const auto prevPolygon = geodata_cast(geometry)) { - GeoDataPolygon* reducedPolygon = new GeoDataPolygon; - GeoDataLinearRing const * prevRing = &(prevPolygon->outerBoundary()); - GeoDataLinearRing reducedRing; - reduce(*prevRing, placemark->osmData().memberReference(-1), visualCategory, &reducedRing); - reducedPolygon->setOuterBoundary(reducedRing); - QVector const & innerBoundaries = prevPolygon->innerBoundaries(); - for(int i = 0; i < innerBoundaries.size(); i++) { - prevRing = &innerBoundaries[i]; - GeoDataLinearRing reducedInnerRing; - reduce(*prevRing, placemark->osmData().memberReference(i), visualCategory, &reducedInnerRing); - reducedPolygon->appendInnerBoundary(reducedInnerRing); + placemark->setGeometry(reducedPolygon(*prevPolygon, placemark, visualCategory)); + } else if (const auto building = geodata_cast(geometry)) { + if (const auto prevRing = geodata_cast(&building->multiGeometry()->at(0))) { + GeoDataLinearRing* ring = reducedRing(*prevRing, placemark, visualCategory); + GeoDataBuilding* newBuilding = new GeoDataBuilding(*building); + newBuilding->multiGeometry()->clear(); + newBuilding->multiGeometry()->append(ring); + placemark->setGeometry(newBuilding); + } else if (const auto prevPolygon = geodata_cast(&building->multiGeometry()->at(0))) { + GeoDataPolygon* poly = reducedPolygon(*prevPolygon, placemark, visualCategory); + GeoDataBuilding* newBuilding = new GeoDataBuilding(*building); + newBuilding->multiGeometry()->clear(); + newBuilding->multiGeometry()->append(poly); + placemark->setGeometry(newBuilding); } - placemark->setGeometry(reducedPolygon); } } } } qint64 NodeReducer::remainingNodes() const { return m_remainingNodes; } qreal NodeReducer::epsilonFor(qreal multiplier) const { if (m_zoomLevel >= 17) { return 0.25; } else if (m_zoomLevel >= 10) { int const factor = 1 << (qAbs(m_zoomLevel-12)); return multiplier / factor; } else { int const factor = 1 << (qAbs(m_zoomLevel-10)); return multiplier * factor; } } qreal NodeReducer::perpendicularDistance(const GeoDataCoordinates &a, const GeoDataCoordinates &b, const GeoDataCoordinates &c) const { qreal ret; qreal const y0 = a.latitude(); qreal const x0 = a.longitude(); qreal const y1 = b.latitude(); qreal const x1 = b.longitude(); qreal const y2 = c.latitude(); qreal const x2 = c.longitude(); qreal const y01 = x0 - x1; qreal const x01 = y0 - y1; qreal const y10 = x1 - x0; qreal const x10 = y1 - y0; qreal const y21 = x2 - x1; qreal const x21 = y2 - y1; qreal const len = (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2); qreal const t = len == 0.0 ? -1.0 : (x01 * x21 + y01 * y21) / len; if ( t < 0.0 ) { ret = EARTH_RADIUS * distanceSphere(a, b); } else if ( t > 1.0 ) { ret = EARTH_RADIUS * distanceSphere(a, c); } else { qreal const nom = qAbs( x21 * y10 - x10 * y21 ); qreal const den = sqrt( x21 * x21 + y21 * y21 ); ret = EARTH_RADIUS * nom / den; } return ret; } bool NodeReducer::touchesTileBorder(const GeoDataCoordinates &coordinates) const { return coordinates.latitude() >= m_tileBoundary[North] || coordinates.latitude() <= m_tileBoundary[South] || coordinates.longitude() <= m_tileBoundary[West] || coordinates.longitude() >= m_tileBoundary[East]; } qint64 NodeReducer::removedNodes() const { return m_removedNodes; } void NodeReducer::setBorderPoints(OsmPlacemarkData &osmData, const QVector &borderPoints, int length) const { int const n = borderPoints.size(); if (n == 0) { return; } if (n > length) { qDebug() << "Invalid border points for length" << length << ":" << borderPoints; return; } typedef QPair Segment; typedef QVector Segments; Segments segments; Segment currentSegment; currentSegment.first = borderPoints.first(); currentSegment.second = currentSegment.first; for (int i=1; i 1 && segments.last().second+1 == length && segments.first().first == 0) { segments.last().second = segments.first().second; segments.pop_front(); } int wraps = 0; for (auto const &segment: segments) { if (segment.first >= segment.second) { ++wraps; } if (segment.first < 0 || segment.second < 0 || segment.first+1 > length || segment.second+1 > length) { qDebug() << "Wrong border points sequence for length " << length << ":" << borderPoints << ", intermediate " << segments; return; } } if (wraps > 1) { //qDebug() << "Wrong border points sequence:" << borderPoints; return; } QString value; value.reserve(segments.size() * (2 + QString::number(length).size())); for (auto const &segment: segments) { int diff = segment.second - segment.first; diff = diff > 0 ? diff : length + diff; value = value % QStringLiteral(";") % QString::number(segment.first) % QStringLiteral("+") % QString::number(diff); } osmData.addTag(QStringLiteral("mx:bp"), value.mid(1)); } +GeoDataLinearRing *NodeReducer::reducedRing(const GeoDataLinearRing& prevRing, + GeoDataPlacemark* placemark, + const GeoDataPlacemark::GeoDataVisualCategory& visualCategory) +{ + GeoDataLinearRing* reducedRing = new GeoDataLinearRing; + reduce(prevRing, placemark->osmData(), visualCategory, reducedRing); + return reducedRing; +} + +GeoDataPolygon *NodeReducer::reducedPolygon(const GeoDataPolygon& prevPolygon, + GeoDataPlacemark* placemark, + const GeoDataPlacemark::GeoDataVisualCategory& visualCategory) +{ + GeoDataPolygon* reducedPolygon = new GeoDataPolygon; + GeoDataLinearRing const * prevRing = &(prevPolygon.outerBoundary()); + GeoDataLinearRing reducedRing; + reduce(*prevRing, placemark->osmData().memberReference(-1), visualCategory, &reducedRing); + reducedPolygon->setOuterBoundary(reducedRing); + QVector const & innerBoundaries = prevPolygon.innerBoundaries(); + for(int i = 0; i < innerBoundaries.size(); i++) { + prevRing = &innerBoundaries[i]; + GeoDataLinearRing reducedInnerRing; + reduce(*prevRing, placemark->osmData().memberReference(i), visualCategory, &reducedInnerRing); + reducedPolygon->appendInnerBoundary(reducedInnerRing); + } + return reducedPolygon; +} + } diff --git a/tools/vectorosm-tilecreator/NodeReducer.h b/tools/vectorosm-tilecreator/NodeReducer.h index 3c257c3af..cd9b2fafa 100644 --- a/tools/vectorosm-tilecreator/NodeReducer.h +++ b/tools/vectorosm-tilecreator/NodeReducer.h @@ -1,127 +1,134 @@ // // 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 2016 Akshat Tandon // #ifndef MARBLE_NODEREDUCER_H #define MARBLE_NODEREDUCER_H #include "MarbleMath.h" #include "OsmPlacemarkData.h" #include "VectorClipper.h" namespace Marble { class NodeReducer { public: NodeReducer(GeoDataDocument* document, const TileId &tileId); qint64 removedNodes() const; qint64 remainingNodes() const; private: qreal epsilonFor(qreal multiplier) const; qreal perpendicularDistance(const GeoDataCoordinates &a, const GeoDataCoordinates &b, const GeoDataCoordinates &c) const; bool touchesTileBorder(const GeoDataCoordinates &coordinates) const; void setBorderPoints(OsmPlacemarkData &osmData, const QVector &borderPoints, int length) const; + GeoDataLinearRing* reducedRing(const GeoDataLinearRing& prevRing, + GeoDataPlacemark* placemark, + const GeoDataPlacemark::GeoDataVisualCategory& visualCategory); + GeoDataPolygon* reducedPolygon(const GeoDataPolygon& prevPolygon, + GeoDataPlacemark* placemark, + const GeoDataPlacemark::GeoDataVisualCategory& visualCategory); + template void reduce(T const & lineString, OsmPlacemarkData& osmData, GeoDataPlacemark::GeoDataVisualCategory visualCategory, T* reducedLine) { bool const isArea = lineString.isClosed() && VectorClipper::canBeArea(visualCategory); qreal const epsilon = epsilonFor(isArea ? 45.0 : 30.0); *reducedLine = douglasPeucker(lineString, osmData, epsilon); qint64 prevSize = lineString.size(); qint64 reducedSize = reducedLine->size(); m_removedNodes += (prevSize - reducedSize); m_remainingNodes += reducedSize; QVector borderPoints; int index = 0; for (auto const &coordinate: *reducedLine) { if (touchesTileBorder(coordinate)) { borderPoints << index; } ++index; } setBorderPoints(osmData, borderPoints, reducedLine->size()); } template T extract(T const & lineString, int start, int end) const { T result; for (int i=start; i<=end; ++i) { result << lineString[i]; } return result; } template T merge(T const & a, T const &b) const { T result = a; int const index = a.size(); result << b; result.remove(index); return result; } template T douglasPeucker(T const & lineString, const OsmPlacemarkData &osmData, qreal epsilon) const { if (lineString.size() < 3) { return lineString; } double maxDistance = 0.0; int index = 1; int const end = lineString.size()-1; for (int i = 1; i maxDistance) { index = i; maxDistance = distance; } } if (maxDistance >= epsilon) { T const left = douglasPeucker(extract(lineString, 0, index), osmData, epsilon); T const right = douglasPeucker(extract(lineString, index, end), osmData, epsilon); return merge(left, right); } T result; result << lineString[0]; for (int i=1; i // Copyright 2016 David Kolozsvari // #include "VectorClipper.h" #include "TileId.h" #include "GeoDataLatLonAltBox.h" #include "GeoDataPolygon.h" #include "GeoDataPlacemark.h" #include "GeoDataRelation.h" #include "OsmPlacemarkData.h" #include "OsmObjectManager.h" #include "TileCoordsPyramid.h" #include "clipper/clipper.hpp" #include #include #include #include namespace Marble { VectorClipper::VectorClipper(GeoDataDocument* document, int maxZoomLevel) : m_maxZoomLevel(maxZoomLevel) { for (auto feature: document->featureList()) { if (const auto placemark = geodata_cast(feature)) { // Select zoom level such that the placemark fits in a single tile int zoomLevel; qreal north, south, east, west; placemark->geometry()->latLonAltBox().boundaries(north, south, east, west); for (zoomLevel = maxZoomLevel; zoomLevel >= 0; --zoomLevel) { if (TileId::fromCoordinates(GeoDataCoordinates(west, north), zoomLevel) == TileId::fromCoordinates(GeoDataCoordinates(east, south), zoomLevel)) { break; } } TileId const key = TileId::fromCoordinates(GeoDataCoordinates(west, north), zoomLevel); m_items[key] << placemark; } else if (GeoDataRelation *relation = geodata_cast(feature)) { m_relations << relation; } else { Q_ASSERT(false && "only placemark variants are supported so far"); } } } GeoDataDocument *VectorClipper::clipTo(const GeoDataLatLonBox &tileBoundary, int zoomLevel) { bool const filterSmallAreas = zoomLevel > 10 && zoomLevel < 17; GeoDataDocument* tile = new GeoDataDocument(); auto const clip = clipPath(tileBoundary, zoomLevel); GeoDataLinearRing ring; ring << GeoDataCoordinates(tileBoundary.west(), tileBoundary.north()); ring << GeoDataCoordinates(tileBoundary.east(), tileBoundary.north()); ring << GeoDataCoordinates(tileBoundary.east(), tileBoundary.south()); ring << GeoDataCoordinates(tileBoundary.west(), tileBoundary.south()); qreal const minArea = filterSmallAreas ? 0.01 * area(ring) : 0.0; QSet osmIds; for (GeoDataPlacemark const * placemark: potentialIntersections(tileBoundary)) { GeoDataGeometry const * const geometry = placemark ? placemark->geometry() : nullptr; if (geometry && tileBoundary.intersects(geometry->latLonAltBox())) { if (geodata_cast(geometry)) { clipPolygon(placemark, clip, minArea, tile, osmIds); } else if (geodata_cast(geometry)) { clipString(placemark, clip, minArea, tile, osmIds); } else if (geodata_cast(geometry)) { clipString(placemark, clip, minArea, tile, osmIds); + } else if (const auto building = geodata_cast(geometry)) { + if (geodata_cast(&building->multiGeometry()->at(0))) { + clipPolygon(placemark, clip, minArea, tile, osmIds); + } else if (geodata_cast(&building->multiGeometry()->at(0))) { + clipString(placemark, clip, minArea, tile, osmIds); + } } else { tile->append(placemark->clone()); osmIds << placemark->osmData().id(); } } } for (auto relation: m_relations) { if (relation->containsAnyOf(osmIds)) { GeoDataRelation* multi = new GeoDataRelation; multi->osmData() = relation->osmData(); tile->append(multi); } } return tile; } QVector VectorClipper::potentialIntersections(const GeoDataLatLonBox &box) const { qreal north, south, east, west; box.boundaries(north, south, east, west); TileId const topLeft = TileId::fromCoordinates(GeoDataCoordinates(west, north), m_maxZoomLevel); TileId const bottomRight = TileId::fromCoordinates(GeoDataCoordinates(east, south), m_maxZoomLevel); QRect rect; rect.setCoords(topLeft.x(), topLeft.y(), bottomRight.x(), bottomRight.y()); TileCoordsPyramid pyramid(0, m_maxZoomLevel); pyramid.setBottomLevelCoords(rect); QVector result; for (int level = pyramid.topLevel(), maxLevel = pyramid.bottomLevel(); level <= maxLevel; ++level) { int x1, y1, x2, y2; pyramid.coords(level).getCoords(&x1, &y1, &x2, &y2); for (int x = x1; x <= x2; ++x) { for (int y = y1; y <= y2; ++y) { result << m_items.value(TileId(0, level, x, y)); } } } return result; } GeoDataDocument *VectorClipper::clipTo(unsigned int zoomLevel, unsigned int tileX, unsigned int tileY) { const GeoDataLatLonBox tileBoundary = m_tileProjection.geoCoordinates(zoomLevel, tileX, tileY); GeoDataDocument *tile = clipTo(tileBoundary, zoomLevel); QString tileName = QString("%1/%2/%3").arg(zoomLevel).arg(tileX).arg(tileY); tile->setName(tileName); return tile; } ClipperLib::Path VectorClipper::clipPath(const GeoDataLatLonBox &box, int zoomLevel) const { using namespace ClipperLib; Path path; int const steps = qMax(1, 22 - 2 * zoomLevel); qreal const scale = IntPoint::scale; double x = box.west() * scale; double const horizontalStep = (box.east() * scale - x) / steps; double y = box.north() * scale; double const verticalStep = (box.south() * scale - y) / steps; for (int i=0; i= GeoDataPlacemark::HighwaySteps && visualCategory <= GeoDataPlacemark::HighwayMotorway) { return false; } if (visualCategory >= GeoDataPlacemark::RailwayRail && visualCategory <= GeoDataPlacemark::RailwayFunicular) { return false; } if (visualCategory >= GeoDataPlacemark::AdminLevel1 && visualCategory <= GeoDataPlacemark::AdminLevel11) { return false; } if (visualCategory == GeoDataPlacemark::BoundaryMaritime || visualCategory == GeoDataPlacemark::InternationalDateLine) { return false; } return true; } qreal VectorClipper::area(const GeoDataLinearRing &ring) { auto const iter = m_areas.find(&ring); if (iter != m_areas.end()) { return *iter; } int const n = ring.size(); qreal area = 0; if (n<3) { return area; } for (int i = 1; i < n; ++i ){ area += (ring[i].longitude() - ring[i-1].longitude() ) * ( ring[i].latitude() + ring[i-1].latitude()); } area += (ring[0].longitude() - ring[n-1].longitude() ) * (ring[0].latitude() + ring[n-1].latitude()); qreal const result = EARTH_RADIUS * EARTH_RADIUS * qAbs(area * 0.5); m_areas.insert(&ring, result); return result; } void VectorClipper::getBounds(const ClipperLib::Path &path, ClipperLib::cInt &minX, ClipperLib::cInt &maxX, ClipperLib::cInt &minY, ClipperLib::cInt &maxY) const { Q_ASSERT(!path.empty()); minX = path[0].X; maxX = minX; minY = path[0].Y; maxY = minY; for (auto const & point: path) { if (point.X < minX) { minX = point.X; } else if (point.X > maxX) { maxX = point.X; } if (point.Y < minY) { minY = point.Y; } else if (point.Y > maxY) { maxY = point.Y; } } } void VectorClipper::clipPolygon(const GeoDataPlacemark *placemark, const ClipperLib::Path &tileBoundary, qreal minArea, GeoDataDocument *document, QSet &osmIds) { - const GeoDataPolygon* polygon = static_cast(placemark->geometry()); + bool isBuilding = false; + GeoDataPolygon* polygon; + if (const auto building = geodata_cast(placemark->geometry())) { + polygon = geodata_cast(&building->multiGeometry()->at(0)); + isBuilding = true; + } else { + GeoDataPlacemark* copyPlacemark = new GeoDataPlacemark(*placemark); + polygon = geodata_cast(copyPlacemark->geometry()); + } + if (minArea > 0.0 && area(polygon->outerBoundary()) < minArea) { return; } using namespace ClipperLib; Path path; for(auto const & node: polygon->outerBoundary()) { path << IntPoint(&node); } cInt minX, maxX, minY, maxY; getBounds(tileBoundary, minX, maxX, minY, maxY); Clipper clipper; clipper.PreserveCollinear(true); clipper.AddPath(tileBoundary, ptClip, true); clipper.AddPath(path, ptSubject, true); Paths paths; clipper.Execute(ctIntersection, paths); for(const auto &path: paths) { GeoDataPlacemark* newPlacemark = new GeoDataPlacemark; newPlacemark->setVisible(placemark->isVisible()); newPlacemark->setVisualCategory(placemark->visualCategory()); GeoDataLinearRing outerRing; OsmPlacemarkData const & placemarkOsmData = placemark->osmData(); OsmPlacemarkData & newPlacemarkOsmData = newPlacemark->osmData(); int index = -1; OsmPlacemarkData const & outerRingOsmData = placemarkOsmData.memberReference(index); OsmPlacemarkData & newOuterRingOsmData = newPlacemarkOsmData.memberReference(index); int nodeIndex = 0; for(const auto &point: path) { GeoDataCoordinates const coordinates = point.coordinates(); outerRing << coordinates; auto const originalOsmData = outerRingOsmData.nodeReference(coordinates); if (originalOsmData.id() > 0) { newOuterRingOsmData.addNodeReference(coordinates, originalOsmData); } ++nodeIndex; } GeoDataPolygon* newPolygon = new GeoDataPolygon; newPolygon->setOuterBoundary(outerRing); - newPlacemark->setGeometry(newPolygon); + if (isBuilding) { + const auto building = geodata_cast(placemark->geometry()); + GeoDataBuilding* newBuilding = new GeoDataBuilding(*building); + newBuilding->multiGeometry()->clear(); + newBuilding->multiGeometry()->append(newPolygon); + newPlacemark->setGeometry(newBuilding); + } else { + newPlacemark->setGeometry(newPolygon); + } if (placemarkOsmData.id() > 0) { newPlacemarkOsmData.addTag(QStringLiteral("mx:oid"), QString::number(placemarkOsmData.id())); } copyTags(placemarkOsmData, newPlacemarkOsmData); copyTags(outerRingOsmData, newOuterRingOsmData); if (outerRingOsmData.id() > 0) { newOuterRingOsmData.addTag(QStringLiteral("mx:oid"), QString::number(outerRingOsmData.id())); } auto const & innerBoundaries = polygon->innerBoundaries(); for (index = 0; index < innerBoundaries.size(); ++index) { auto const & innerBoundary = innerBoundaries.at(index); if (minArea > 0.0 && area(innerBoundary) < minArea) { continue; } auto const & innerRingOsmData = placemarkOsmData.memberReference(index); clipper.Clear(); clipper.AddPath(path, ptClip, true); Path innerPath; for(auto const & node: innerBoundary) { innerPath << IntPoint(&node); } clipper.AddPath(innerPath, ptSubject, true); Paths innerPaths; clipper.Execute(ctIntersection, innerPaths); for(auto const &innerPath: innerPaths) { int const newIndex = newPolygon->innerBoundaries().size(); auto & newInnerRingOsmData = newPlacemarkOsmData.memberReference(newIndex); GeoDataLinearRing innerRing; nodeIndex = 0; for(const auto &point: innerPath) { GeoDataCoordinates const coordinates = point.coordinates(); innerRing << coordinates; auto const originalOsmData = innerRingOsmData.nodeReference(coordinates); if (originalOsmData.id() > 0) { newInnerRingOsmData.addNodeReference(coordinates, originalOsmData); } ++nodeIndex; } newPolygon->appendInnerBoundary(innerRing); if (innerRingOsmData.id() > 0) { newInnerRingOsmData.addTag(QStringLiteral("mx:oid"), QString::number(innerRingOsmData.id())); } copyTags(innerRingOsmData, newInnerRingOsmData); } } OsmObjectManager::initializeOsmData(newPlacemark); document->append(newPlacemark); osmIds << placemark->osmData().id(); } } void VectorClipper::copyTags(const GeoDataPlacemark &source, GeoDataPlacemark &target) const { copyTags(source.osmData(), target.osmData()); } void VectorClipper::copyTags(const OsmPlacemarkData &originalPlacemarkData, OsmPlacemarkData &targetOsmData) const { for (auto iter=originalPlacemarkData.tagsBegin(), end=originalPlacemarkData.tagsEnd(); iter != end; ++iter) { targetOsmData.addTag(iter.key(), iter.value()); } } } diff --git a/tools/vectorosm-tilecreator/VectorClipper.h b/tools/vectorosm-tilecreator/VectorClipper.h index 87bed4b8b..42457e6cd 100644 --- a/tools/vectorosm-tilecreator/VectorClipper.h +++ b/tools/vectorosm-tilecreator/VectorClipper.h @@ -1,121 +1,139 @@ // // 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 2016 Dennis Nienhüser // Copyright 2016 David Kolozsvari // #ifndef TINYPLANETPROCESSOR_H #define TINYPLANETPROCESSOR_H #include "OsmPlacemarkData.h" #include #include "GeoDataPlacemark.h" #include "GeoDataLinearRing.h" +#include "GeoDataBuilding.h" +#include "GeoDataMultiGeometry.h" #include #include #include #include "GeoDataDocument.h" #include "clipper/clipper.hpp" #include #include namespace Marble { class GeoDataLinearRing; class GeoDataRelation; class VectorClipper { public: VectorClipper(GeoDataDocument* document, int maxZoomLevel); GeoDataDocument* clipTo(unsigned int zoomLevel, unsigned int tileX, unsigned int tileY); static bool canBeArea(GeoDataPlacemark::GeoDataVisualCategory visualCategory); private: GeoDataDocument* clipTo(const GeoDataLatLonBox &box, int zoomLevel); QVector potentialIntersections(const GeoDataLatLonBox &box) const; ClipperLib::Path clipPath(const GeoDataLatLonBox &box, int zoomLevel) const; qreal area(const GeoDataLinearRing &ring); void getBounds(const ClipperLib::Path &path, ClipperLib::cInt &minX, ClipperLib::cInt &maxX, ClipperLib::cInt &minY, ClipperLib::cInt &maxY) const; template void clipString(const GeoDataPlacemark *placemark, const ClipperLib::Path &tileBoundary, qreal minArea, GeoDataDocument* document, QSet &osmIds) { - const T* ring = static_cast(placemark->geometry()); + bool isBuilding = false; + T* ring; + if (const auto building = geodata_cast(placemark->geometry())) { + ring = geodata_cast(&building->multiGeometry()->at(0)); + isBuilding = true; + } else { + GeoDataPlacemark* copyPlacemark = new GeoDataPlacemark(*placemark); + ring = geodata_cast(copyPlacemark->geometry()); + } bool const isClosed = ring->isClosed() && canBeArea(placemark->visualCategory()); if (isClosed && minArea > 0.0 && area(*static_cast(ring)) < minArea) { return; } auto const & osmData = placemark->osmData(); using namespace ClipperLib; Path subject; for(auto const & node: *ring) { subject << IntPoint(&node); } cInt minX, maxX, minY, maxY; getBounds(tileBoundary, minX, maxX, minY, maxY); Clipper clipper; clipper.PreserveCollinear(true); clipper.AddPath(tileBoundary, ptClip, true); clipper.AddPath(subject, ptSubject, isClosed); PolyTree tree; clipper.Execute(ctIntersection, tree); Paths paths; if (isClosed) { ClosedPathsFromPolyTree(tree, paths); } else { OpenPathsFromPolyTree(tree, paths); } for(const auto &path: paths) { GeoDataPlacemark* newPlacemark = new GeoDataPlacemark; newPlacemark->setVisible(placemark->isVisible()); newPlacemark->setVisualCategory(placemark->visualCategory()); T* newRing = new T; int index = 0; for(const auto &point: path) { GeoDataCoordinates const coordinates = point.coordinates(); *newRing << coordinates; auto const originalOsmData = osmData.nodeReference(coordinates); if (originalOsmData.id() > 0) { newPlacemark->osmData().addNodeReference(coordinates, originalOsmData); } ++index; } - newPlacemark->setGeometry(newRing); + if (isBuilding) { + const auto building = geodata_cast(placemark->geometry()); + GeoDataBuilding* newBuilding = new GeoDataBuilding(*building); + newBuilding->multiGeometry()->clear(); + newBuilding->multiGeometry()->append(newRing); + newPlacemark->setGeometry(newBuilding); + } else { + newPlacemark->setGeometry(newRing); + } if (placemark->osmData().id() > 0) { newPlacemark->osmData().addTag(QStringLiteral("mx:oid"), QString::number(placemark->osmData().id())); } copyTags(*placemark, *newPlacemark); OsmObjectManager::initializeOsmData(newPlacemark); document->append(newPlacemark); osmIds << placemark->osmData().id(); } } void clipPolygon(const GeoDataPlacemark *placemark, const ClipperLib::Path &tileBoundary, qreal minArea, GeoDataDocument* document, QSet &osmIds); void copyTags(const GeoDataPlacemark &source, GeoDataPlacemark &target) const; void copyTags(const OsmPlacemarkData &originalPlacemarkData, OsmPlacemarkData& targetOsmData) const; QMap > m_items; int m_maxZoomLevel; GeoSceneMercatorTileProjection m_tileProjection; QHash m_areas; QSet m_relations; }; } #endif // TINYPLANETPROCESSOR_H