diff --git a/data/android/drawable-xxxhdpi/ic_settings_black_48dp.png b/data/android/drawable-xxxhdpi/ic_settings_black_48dp.png new file mode 100644 index 000000000..e4d24ea6f Binary files /dev/null and b/data/android/drawable-xxxhdpi/ic_settings_black_48dp.png differ diff --git a/src/apps/marble-maps/MainScreen.qml b/src/apps/marble-maps/MainScreen.qml index ea4572eb2..a5b980efe 100644 --- a/src/apps/marble-maps/MainScreen.qml +++ b/src/apps/marble-maps/MainScreen.qml @@ -1,493 +1,493 @@ // // 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 Gábor Péterffy // Copyright 2015 Dennis Nienhüser // Copyright 2015 Mikhail Ivchenko // import QtQuick 2.3 import QtQuick.Controls 2.0 import QtQuick.Window 2.2 import org.kde.marble 0.20 ApplicationWindow { id: app title: qsTr("Marble Maps") visible: true width: 600 height: 400 color: "#f9f9f9" // Keep the background white while no dialog is loaded property alias state: stateTracker.state property var selectedPlacemark property bool showOsmTags: false property int currentWaypointIndex: 0 property real animatedMargin: app.state === "none" ? 0 : -dialogLoader.height property bool dialogExpanded: animatedMargin === -dialogLoader.height property real mapOffset: !dialogExpanded ? animatedMargin / 2 : 0 Behavior on animatedMargin { NumberAnimation { id: dialogAnimation duration: 200 easing.type: Easing.OutQuart } } onSelectedPlacemarkChanged: { if (!selectedPlacemark) { app.state = "none" } } SystemPalette{ id: palette colorGroup: SystemPalette.Active } Settings { id: settings } Item { id: mapItem anchors { top: parent.top topMargin: mapOffset left: parent.left right: parent.right } height: !dialogExpanded ? parent.height : parent.height + animatedMargin PinchArea { anchors.fill: parent enabled: true onPinchStarted: marbleMaps.handlePinchStarted(pinch.center) onPinchFinished: marbleMaps.handlePinchFinished(pinch.center) onPinchUpdated: marbleMaps.handlePinchUpdated(pinch.center, pinch.scale); MarbleMaps { id: marbleMaps property string currentPositionProvider: "QtPositioning" property bool wlanOnly: false property bool smallZoom : radius < 2 * Math.max(app.width, app.height) anchors.fill: parent visible: true // Theme settings. projection: smallZoom ? MarbleItem.Spherical : MarbleItem.Mercator mapThemeId: settings.value("MarbleMaps", "mapThemeId", "earth/vectorosm/vectorosm.dgml") // Visibility of layers/plugins. showFrameRate: false showAtmosphere: smallZoom showCompass: false showClouds: false showCrosshairs: false showGrid: smallZoom showOverviewMap: false showOtherPlaces: false showScaleBar: false showBackground: smallZoom showPublicTransport: settings.value("MarbleMaps", "showPublicTransport", "false") === "true" positionProvider: suspended ? "" : currentPositionProvider keepScreenOn: !suspended && navigationManager.guidanceModeEnabled showPositionMarker: false animationViewContext: dialogAnimation.running placemarkDelegate: Image { id: balloon property int xPos: 0 property int yPos: 0 property real animationOffset: 0 property var placemark: null x: xPos - 0.5 * width y: yPos - height - 30 * Screen.pixelDensity * animationOffset opacity: 1.0 - animationOffset Connections { target: app onSelectedPlacemarkChanged: balloonAnimation.restart() } NumberAnimation { id: balloonAnimation target: balloon property: "animationOffset" from: 1 to: 0 duration: 1000 easing.type: Easing.OutBounce } width: Screen.pixelDensity*6 height: width source: "qrc:///ic_place.png" onPlacemarkChanged: { app.selectedPlacemark = placemark if (placemark) { app.state = "place" } else { app.state = "none" } } } onPositionAvailableChanged: { updateIndicator(); } onPositionVisibleChanged: { updateIndicator(); } onVisibleLatLonAltBoxChanged: { !panningDetectionTimer.restart(); updateIndicator(); } onCurrentPositionChanged: { updateIndicator(); } onZoomChanged: { zoomDetectionTimer.restart() } Component.onCompleted: { setPluginSetting("coordinate-grid", "gridColor", "#999999"); setPluginSetting("coordinate-grid", "tropicsColor", "#888888"); setPluginSetting("coordinate-grid", "equatorColor", "#777777"); setPluginSetting("coordinate-grid", "primaryLabels", "false"); setPluginSetting("coordinate-grid", "secondaryLabels", "false"); marbleMaps.loadSettings() } Component.onDestruction: marbleMaps.writeSettings() function updateIndicator() { if ( !positionVisible && positionAvailable ) { zoomToPositionButton.updateIndicator(); } } RoutingManager { id: routingManager anchors.fill: parent marbleItem: marbleMaps visible: hasRoute function addToRoute() { ensureRouteHasDeparture() routingManager.addViaByPlacemarkAtIndex(routingManager.waypointCount(), selectedPlacemark) routingManager.clearSearchResultPlacemarks() selectedPlacemark = null app.state = "route" } function ensureRouteHasDeparture() { if (routingManager.routeRequestModel.count === 0) { if (marbleMaps.positionAvailable) { routingManager.addViaByPlacemark(marbleMaps.currentPosition) } } } } Timer { id: zoomDetectionTimer interval: 1000 } Timer { id: panningDetectionTimer interval: 1000 } PositionMarker { id: positionMarker x: navigationManager.snappedPositionMarkerScreenPosition.x - positionMarker.width / 2 y: navigationManager.snappedPositionMarkerScreenPosition.y - positionMarker.height / 2 angle: marbleMaps.angle visible: marbleMaps.positionAvailable && marbleMaps.positionVisible radius: navigationManager.screenAccuracy / 2 showAccuracy: navigationManager.deviated allowRadiusAnimation: !zoomDetectionTimer.running allowPositionAnimation: !panningDetectionTimer.running speed: marbleMaps.speed MouseArea { anchors.fill: parent onPressed: app.state = "position" } } MouseArea { anchors.fill: parent propagateComposedEvents: true onPressed: { marbleMaps.focus = true; mouse.accepted = false; } } } NavigationManager { id: navigationManager width: parent.width height: parent.height visible: false marbleItem: marbleMaps hasRoute: routingManager.hasRoute } } BoxedText { id: distanceIndicator text: qsTr("%1 km").arg(zoomToPositionButton.distance < 10 ? zoomToPositionButton.distance.toFixed(1) : zoomToPositionButton.distance.toFixed(0)) anchors { bottom: zoomToPositionButton.top horizontalCenter: zoomToPositionButton.horizontalCenter } visible: marbleMaps.positionAvailable && !marbleMaps.positionVisible } PositionButton { id: zoomToPositionButton anchors { right: parent.right rightMargin: Screen.pixelDensity * 1 bottom: routeEditorButton.top bottomMargin: 10 } enabled: marbleMaps.positionAvailable iconSource: marbleMaps.positionAvailable ? "qrc:///gps_fixed.png" : "qrc:///gps_not_fixed.png" onClicked: marbleMaps.centerOnCurrentPosition() property real distance: 0 function updateIndicator() { var point = marbleMaps.mapFromItem(zoomToPositionButton, diameter * 0.5, diameter * 0.5); distance = 0.001 * marbleMaps.distanceFromPointToCurrentLocation(point); angle = marbleMaps.angleFromPointToCurrentLocation(point); } showDirection: marbleMaps.positionAvailable && !marbleMaps.positionVisible } CircularButton { id: routeEditorButton property string currentProfileIcon: "qrc:///material/directions-car.svg" anchors { bottom: parent.bottom bottomMargin: Screen.pixelDensity * 4 - mapOffset horizontalCenter: zoomToPositionButton.horizontalCenter } enabled: app.state !== "route" || routingManager.hasRoute onClicked: { if (app.state === "route") { app.state = "none" navigationManager.visible = true } else if (app.state === "place") { app.state = "route" routingManager.addToRoute() } else { app.state = "route" navigationManager.visible = false } } iconSource: "qrc:///material/directions.svg"; states: [ State { name: "" PropertyChanges { target: routeEditorButton; iconSource: "qrc:///material/directions.svg"; } }, State { name: "routingAction" when: app.state === "route" PropertyChanges { target: routeEditorButton; iconSource: "qrc:///material/navigation.svg"; } }, State { name: "placeAction" when: app.state === "place" PropertyChanges { target: routeEditorButton; iconSource: currentProfileIcon } } ] } } Search { id: search anchors.fill: parent marbleQuickItem: marbleMaps visible: !navigationManager.visible onItemSelected: { if (routingManager) { routingManager.addSearchResultAsPlacemark(suggestedPlacemark); } app.selectedPlacemark = suggestedPlacemark; app.state = "place" } onMenuButtonClicked: drawer.open() } Loader { id: dialogLoader focus: true anchors { left: parent.left right: parent.right top: parent.bottom topMargin: app.animatedMargin } onLoaded: { if (app.state === "place") { dialogLoader.item.map = marbleMaps dialogLoader.item.placemark = app.selectedPlacemark dialogLoader.item.showOsmTags = app.showOsmTags dialogLoader.item.showAccessibility = drawer.showAccessibility } else if (app.state === "route") { item.routingManager = routingManager item.routingProfile = routingManager.routingProfile item.currentIndex = Qt.binding(function() { return app.currentWaypointIndex }) } else if (app.state == "position") { dialogLoader.item.map = marbleMaps dialogLoader.item.navigationManager = navigationManager } } Connections { target: dialogLoader.item onCurrentProfileIconChanged: routeEditorButton.currentProfileIcon = dialogLoader.item.currentProfileIcon ignoreUnknownSignals: true } } BorderImage { visible: app.state != "none" anchors.fill: dialogLoader anchors.margins: -14 border { top: 14; left: 14; right: 14; bottom: 14 } source: "qrc:///border_shadow.png" } SidePanel { id: drawer - width: 0.7 * app.width + width: 0.67 * app.width height: app.height marbleMaps: marbleMaps onAboutActionTriggered: { app.state = "about" dialogLoader.focus = true } } BoxedText { id: quitHelper visible: false text: qsTr("Press again to close.") anchors.bottom: parent.bottom anchors.bottomMargin: Screen.pixelDensity * 5 anchors.horizontalCenter: parent.horizontalCenter onVisibleChanged: { if (visible) { quitTimer.restart() } } Timer { id: quitTimer interval: 3000; running: false; repeat: false onTriggered: { app.aboutToQuit = false quitHelper.visible = false } } } property bool aboutToQuit: false onClosing: { if (app.aboutToQuit === true) { close.accepted = true // we will quit return } else if (navigationManager.visible) { navigationManager.visible = false } else if (app.state !== "none") { app.state = "none" } else { app.aboutToQuit = true quitHelper.visible = true } close.accepted = false } Item { id: stateTracker state: "none" states: [ State { name: "none" PropertyChanges { target: dialogLoader; source: "" } }, State { name: "position" PropertyChanges { target: dialogLoader; source: "CurrentPosition.qml" } }, State { name: "route" PropertyChanges { target: dialogLoader; source: "RouteEditor.qml" } }, State { name: "place" PropertyChanges { target: dialogLoader; source: "PlacemarkDialog.qml" } }, State { name: "about" PropertyChanges { target: dialogLoader; source: "AboutDialog.qml" } }, State { name: "settings" PropertyChanges { target: dialogLoader; source: "SettingsDialog.qml" } }, State { name: "developer" PropertyChanges { target: dialogLoader; source: "DeveloperDialog.qml" } } ] } } diff --git a/src/apps/marble-maps/MarbleMaps.qrc b/src/apps/marble-maps/MarbleMaps.qrc index 454c99897..755ec42d0 100644 --- a/src/apps/marble-maps/MarbleMaps.qrc +++ b/src/apps/marble-maps/MarbleMaps.qrc @@ -1,80 +1,87 @@ AboutDialog.qml FlatButton.qml Completion.qml MainScreen.qml SearchResults.qml SearchField.qml Search.qml CircularButton.qml IconText.qml PositionButton.qml BoxedText.qml ImageButton.qml WaypointImage.qml RoutingManager.qml PlacemarkDialog.qml SettingsDialog.qml DeveloperDialog.qml ProfileSelectorMenu.qml FloatingMenuButton.qml RouteEditor.qml RoutesItem.qml MenuIcon.qml NavigationInfoBar.qml NavigationManager.qml PositionMarker.qml Waypoint.qml CurrentPosition.qml SidePanel.qml + PublicTransport.qml drawer.svg ../../../data/android/drawable-xxxhdpi/search.png ../../../data/android/drawable-xxxhdpi/ic_menu_black_48dp.png ../../../data/android/drawable-xxxhdpi/gps_fixed.png ../../../data/android/drawable-xxxhdpi/gps_not_fixed.png ../../../data/android/drawable-xxxhdpi/backdrop.png ../../../data/android/drawable-xxxhdpi/border_shadow.png ../../../data/android/drawable-xxxhdpi/map.png ../../../data/android/drawable-xxxhdpi/waypoint.png ../../../data/android/drawable-xxxhdpi/up.png ../../../data/android/drawable-xxxhdpi/down.png ../../../data/android/drawable-xxxhdpi/delete.png ../../../data/android/drawable-xxxhdpi/delete_white.png ../../../data/android/drawable-xxxhdpi/ic_close_black_18dp.png ../../../data/android/drawable-xxxhdpi/busy_indicator.png ../../../data/android/drawable-xxxhdpi/navigation_blue.png ../../../data/svg/navigation_blue.svg ../../../data/android/drawable-xxxhdpi/circular_menu_backdrop.png ../../../data/android/drawable-xxxhdpi/ic_place.png ../../../data/android/drawable-xxxhdpi/ic_place_arrival.png ../../../data/android/drawable-xxxhdpi/ic_place_departure.png ../../../data/android/drawable-xxxhdpi/ic_place_unknown.png ../../../data/android/drawable-xxxhdpi/ic_place_via.png ../../../data/android/drawable-xxxhdpi/ic_add_black_48dp.png + ../../../data/android/drawable-xxxhdpi/ic_settings_black_48dp.png material-icons/ic_local_gas_station_48px.svg material-icons/ic_open_in_browser_48px.svg material-icons/ic_volume_off_48px.svg material-icons/ic_volume_up_48px.svg material-icons/ic_directions_48px.svg - material-icons/ic_directions_bike_48px.svg - material-icons/ic_directions_car_48px.svg - material-icons/ic_directions_walk_48px.svg + ../../../data/svg/material/maps/ic_directions_bike_48px.svg + ../../../data/svg/material/maps/ic_directions_boat_48px.svg + ../../../data/svg/material/maps/ic_directions_bus_48px.svg + ../../../data/svg/material/maps/ic_directions_car_48px.svg + ../../../data/svg/material/maps/ic_directions_railway_48px.svg + ../../../data/svg/material/maps/ic_directions_run_48px.svg + ../../../data/svg/material/maps/ic_directions_subway_48px.svg + ../../../data/svg/material/maps/ic_directions_walk_48px.svg + ../../../data/svg/material/maps/ic_tram_48px.svg material-icons/ic_navigation_48px.svg material-icons/ic_access_time_48px.svg material-icons/ic_accessible_black_48px.svg material-icons/ic_network_wifi_black_48px.svg material-icons/ic_place_black_48px.svg material-icons/ic_star_24px.svg material-icons/ic_star_border_24px.svg material-icons/ic_label_48px.svg - material-icons/ic_directions_bus_48px.svg konqi/konqi-app-dev.png konqi/konqi-dev-qt.png konqi/konqi-group.png konqi/konqi-globe.png konqi/konqi-books.png RouteProfileRadioButton.qml MarbleScrollBar.qml diff --git a/src/apps/marble-maps/MenuIcon.qml b/src/apps/marble-maps/MenuIcon.qml index 84ac4aa92..041fdb80e 100644 --- a/src/apps/marble-maps/MenuIcon.qml +++ b/src/apps/marble-maps/MenuIcon.qml @@ -1,72 +1,93 @@ // // 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 Dennis Nienhüser // import QtQuick 2.3 import QtQuick.Window 2.2 +import QtQuick.Layouts 1.3 Item { id: root anchors.left: parent.left anchors.right: parent.right - height: childrenRect.height + height: container.height + 2 * Screen.pixelDensity property bool checkable: false property bool checked: false + property bool hasSettings: false property alias text: text.text property alias icon: image.source signal triggered() + signal settingsTriggered() Rectangle { anchors.fill: parent visible: root.checkable && root.checked color: palette.highlight radius: 3 } Item { id: container property real padding: Screen.pixelDensity * 1 anchors.top: parent.top anchors.left: parent.left anchors.right: parent.right - height: childrenRect.height + padding + height: row.height anchors.margins: padding - Row { - anchors.leftMargin: Screen.pixelDensity * 4 - spacing: Screen.pixelDensity * 2 + RowLayout { id: row + anchors.left: parent.left + anchors.right: settingsButton.left + spacing: Screen.pixelDensity * 2 Image { id: image sourceSize.height: text.height fillMode: Image.PreserveAspectFit - anchors.verticalCenter: text.verticalCenter + anchors.verticalCenter: parent.verticalCenter } Text { id: text + Layout.fillWidth: true font.pointSize: 18 color: root.checkable && root.checked ? palette.highlightedText : palette.text + elide: Text.ElideRight } } - } - MouseArea { - anchors.fill: parent - onClicked: { - if (root.checkable) { - root.checked = !root.checked + MouseArea { + anchors.fill: parent + onClicked: { + if (root.checkable) { + root.checked = !root.checked + } + root.triggered() + } + } + + Image { + id: settingsButton + visible: root.hasSettings + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + sourceSize.height: 0.67 * text.height + fillMode: Image.PreserveAspectFit + + source: "qrc:/settings.png" + MouseArea { + anchors.fill: parent + onClicked: root.settingsTriggered() } - root.triggered() } } } diff --git a/src/apps/marble-maps/PublicTransport.qml b/src/apps/marble-maps/PublicTransport.qml new file mode 100644 index 000000000..1337e3cd5 --- /dev/null +++ b/src/apps/marble-maps/PublicTransport.qml @@ -0,0 +1,98 @@ +// +// 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 Dennis Nienhüser +// + +import QtQuick 2.6 +import QtQuick.Window 2.2 +import QtQuick.Controls 2.0 +import QtQuick.Layouts 1.3 + + +Item { + id: root + + implicitHeight: Math.min(0.75 * Screen.height, listView.contentHeight) + property var marbleMaps + + ListModel { + id: transportModel + ListElement { + name: qsTr("Train") + icon: "material/directions-railway.svg" + key: "train" + } + ListElement { + name: qsTr("Subway") + icon: "material/directions-subway.svg" + key: "subway" + } + ListElement { + name: qsTr("Tram") + icon: "material/directions-tram.svg" + key: "tram" + } + ListElement { + name: qsTr("Bus") + icon: "material/directions-bus.svg" + key: "bus" + } + ListElement { + name: qsTr("Trolley Bus") + icon: "material/directions-bus.svg" + key: "trolley-bus" + } + } + + ListView { + id: listView + anchors.fill: parent + contentWidth: width + + model: transportModel + clip: true + spacing: Screen.pixelDensity * 2 + + delegate: Row { + CheckBox { + id: control + text: name + + checked: root.marbleMaps.isRelationTypeVisible(key) + + contentItem: Row { + Item { + height: parent.height + width: control.indicator.width + control.spacing + } + + Image { + source: icon + height: parent.height + sourceSize.height: height + fillMode: Image.PreserveAspectFit + } + + Text { + height: parent.height + text: control.text + font: control.font + opacity: enabled ? 1.0 : 0.3 + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + } + + onCheckedChanged: { + root.marbleMaps.setRelationTypeVisible(key, checked) + } + } + } + ScrollBar.vertical: ScrollBar {} + } +} diff --git a/src/apps/marble-maps/SidePanel.qml b/src/apps/marble-maps/SidePanel.qml index 73437bfe8..b485f1ce5 100644 --- a/src/apps/marble-maps/SidePanel.qml +++ b/src/apps/marble-maps/SidePanel.qml @@ -1,89 +1,107 @@ // // 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 Dennis Nienhüser // import QtQuick 2.3 import QtQuick.Controls 2.0 import QtQuick.Window 2.2 +import QtQuick.Dialogs 1.2 import org.kde.marble 0.20 Drawer { id: root property var marbleMaps property alias showAccessibility: accessibilityAction.checked signal aboutActionTriggered() Settings { id: settings property bool showUpdateInfo: Number(value("MarbleMaps", "updateInfoVersion", "0")) < 1 Component.onDestruction: { settings.setValue("MarbleMaps", "showAccessibility", accessibilityAction.checked ? "true" : "false") - settings.setValue("MarbleMaps", "showPublicTransport", publicTransportAction.checked ? "true" : "false") } } Column { id: drawerContent anchors.fill: parent spacing: Screen.pixelDensity * 2 Image { source: "drawer.svg" width: parent.width sourceSize.width: width fillMode: Image.PreserveAspectFit } MenuIcon { id: publicTransportAction anchors.leftMargin: Screen.pixelDensity * 2 anchors.rightMargin: anchors.leftMargin checkable: true - checked: settings.value("MarbleMaps", "showPublicTransport", "false") === "true" + checked: marbleMaps.showPublicTransport + hasSettings: true text: qsTr("Public Transport") - icon: "qrc:/material/bus.svg" + icon: "qrc:/material/directions-bus.svg" onTriggered: { + root.close() root.marbleMaps.showPublicTransport = checked + } + onSettingsTriggered: { root.close() + publicTransportLoader.source = "PublicTransport.qml" + publicTransportDialog.open() } } MenuIcon { id: accessibilityAction anchors.leftMargin: Screen.pixelDensity * 2 anchors.rightMargin: anchors.leftMargin checkable: true checked: settings.value("MarbleMaps", "showAccessibility", "false") === "true" text: qsTr("Accessibility") icon: "qrc:/material/wheelchair.svg" onTriggered: root.close() } Rectangle { width: parent.width height: 1 color: "gray" } MenuIcon { text: qsTr("About Marble Maps…") anchors.leftMargin: Screen.pixelDensity * 2 anchors.rightMargin: anchors.leftMargin onTriggered: { root.close() root.aboutActionTriggered() } } } + Dialog { + id: publicTransportDialog + title: qsTr("Public Transport") + + Loader { + id: publicTransportLoader + onLoaded: { + item.implicitWidth = parent.width + item.marbleMaps = root.marbleMaps + } + } + } } diff --git a/src/lib/marble/MarbleMap.cpp b/src/lib/marble/MarbleMap.cpp index 866768e8a..5f5191ce4 100644 --- a/src/lib/marble/MarbleMap.cpp +++ b/src/lib/marble/MarbleMap.cpp @@ -1,1449 +1,1449 @@ // // 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 2006-2009 Torsten Rahn // Copyright 2007 Inge Wallin // Copyright 2008 Carlos Licea // Copyright 2009 Jens-Michael Hoffmann // Copyright 2010-2012 Bernhard Beschow // // Own #include "MarbleMap.h" // Posix #include // Qt #include #include // Marble #include "layers/FloatItemsLayer.h" #include "layers/FogLayer.h" #include "layers/FpsLayer.h" #include "layers/GeometryLayer.h" #include "layers/GroundLayer.h" #include "layers/MarbleSplashLayer.h" #include "layers/PlacemarkLayer.h" #include "layers/TextureLayer.h" #include "layers/VectorTileLayer.h" #include "AbstractFloatItem.h" #include "DgmlAuxillaryDictionary.h" #include "FileManager.h" #include "GeoDataTreeModel.h" #include "GeoPainter.h" #include "GeoSceneDocument.h" #include "GeoSceneFilter.h" #include "GeoSceneGeodata.h" #include "GeoSceneHead.h" #include "GeoSceneLayer.h" #include "GeoSceneMap.h" #include "GeoScenePalette.h" #include "GeoSceneSettings.h" #include "GeoSceneVector.h" #include "GeoSceneVectorTileDataset.h" #include "GeoSceneTextureTileDataset.h" #include "GeoSceneZoom.h" #include "GeoDataDocument.h" #include "GeoDataFeature.h" #include "GeoDataStyle.h" #include "GeoDataStyleMap.h" #include "LayerManager.h" #include "MapThemeManager.h" #include "MarbleDebug.h" #include "MarbleDirs.h" #include "MarbleModel.h" #include "PluginManager.h" #include "RenderPlugin.h" #include "StyleBuilder.h" #include "SunLocator.h" #include "TileId.h" #include "TileCoordsPyramid.h" #include "TileCreator.h" #include "TileCreatorDialog.h" #include "TileLoader.h" #include "ViewParams.h" #include "ViewportParams.h" #include "RenderState.h" #include "BookmarkManager.h" namespace Marble { class MarbleMap::CustomPaintLayer : public LayerInterface { public: explicit CustomPaintLayer( MarbleMap *map ) : m_map( map ) { } QStringList renderPosition() const override { return QStringList() << "USER_TOOLS"; } bool render( GeoPainter *painter, ViewportParams *viewport, const QString &renderPos, GeoSceneLayer *layer ) override { Q_UNUSED( viewport ); Q_UNUSED( renderPos ); Q_UNUSED( layer ); m_map->customPaint( painter ); return true; } qreal zValue() const override { return 1.0e6; } RenderState renderState() const override { return RenderState(QStringLiteral("Custom Map Paint")); } QString runtimeTrace() const override { return QStringLiteral("CustomPaint"); } private: MarbleMap *const m_map; }; class MarbleMapPrivate { friend class MarbleWidget; public: explicit MarbleMapPrivate( MarbleMap *parent, MarbleModel *model ); void updateMapTheme(); void updateProperty( const QString &, bool ); void setDocument( QString key ); void updateTileLevel(); void addPlugins(); MarbleMap *const q; // The model we are showing. MarbleModel *const m_model; bool m_modelIsOwned; // Parameters for the maps appearance. ViewParams m_viewParams; ViewportParams m_viewport; bool m_showFrameRate; bool m_showDebugPolygons; bool m_showDebugBatchRender; - bool m_showPublicTransport; + GeoDataRelation::RelationTypes m_visibleRelationTypes; StyleBuilder m_styleBuilder; QList m_renderPlugins; LayerManager m_layerManager; MarbleSplashLayer m_marbleSplashLayer; MarbleMap::CustomPaintLayer m_customPaintLayer; GeometryLayer m_geometryLayer; FloatItemsLayer m_floatItemsLayer; FogLayer m_fogLayer; GroundLayer m_groundLayer; TextureLayer m_textureLayer; PlacemarkLayer m_placemarkLayer; VectorTileLayer m_vectorTileLayer; bool m_isLockedToSubSolarPoint; bool m_isSubSolarPointIconVisible; RenderState m_renderState; }; MarbleMapPrivate::MarbleMapPrivate( MarbleMap *parent, MarbleModel *model ) : q( parent ), m_model( model ), m_viewParams(), m_showFrameRate( false ), m_showDebugPolygons( false ), m_showDebugBatchRender( false ), - m_showPublicTransport(false), + m_visibleRelationTypes(GeoDataRelation::RouteFerry), m_styleBuilder(), m_layerManager( parent ), m_customPaintLayer( parent ), m_geometryLayer(model->treeModel(), &m_styleBuilder), m_floatItemsLayer(parent), m_textureLayer( model->downloadManager(), model->pluginManager(), model->sunLocator(), model->groundOverlayModel() ), m_placemarkLayer( model->placemarkModel(), model->placemarkSelectionModel(), model->clock(), &m_styleBuilder ), m_vectorTileLayer( model->downloadManager(), model->pluginManager(), model->treeModel() ), m_isLockedToSubSolarPoint( false ), m_isSubSolarPointIconVisible( false ) { m_layerManager.addLayer(&m_floatItemsLayer); m_layerManager.addLayer( &m_fogLayer ); m_layerManager.addLayer( &m_groundLayer ); m_layerManager.addLayer( &m_geometryLayer ); m_layerManager.addLayer( &m_placemarkLayer ); m_layerManager.addLayer( &m_customPaintLayer ); m_model->bookmarkManager()->setStyleBuilder(&m_styleBuilder); QObject::connect( m_model, SIGNAL(themeChanged(QString)), parent, SLOT(updateMapTheme()) ); QObject::connect( m_model->fileManager(), SIGNAL(fileAdded(QString)), parent, SLOT(setDocument(QString)) ); QObject::connect( &m_placemarkLayer, SIGNAL(repaintNeeded()), parent, SIGNAL(repaintNeeded())); QObject::connect ( &m_layerManager, SIGNAL(pluginSettingsChanged()), parent, SIGNAL(pluginSettingsChanged()) ); QObject::connect ( &m_layerManager, SIGNAL(repaintNeeded(QRegion)), parent, SIGNAL(repaintNeeded(QRegion)) ); QObject::connect ( &m_layerManager, SIGNAL(renderPluginInitialized(RenderPlugin*)), parent, SIGNAL(renderPluginInitialized(RenderPlugin*)) ); QObject::connect ( &m_layerManager, SIGNAL(visibilityChanged(QString,bool)), parent, SLOT(setPropertyValue(QString,bool)) ); QObject::connect( &m_geometryLayer, SIGNAL(repaintNeeded()), parent, SIGNAL(repaintNeeded())); QObject::connect(&m_vectorTileLayer, SIGNAL(tileLevelChanged(int)), &m_geometryLayer, SLOT(setTileLevel(int))); QObject::connect(&m_vectorTileLayer, SIGNAL(tileLevelChanged(int)), &m_placemarkLayer, SLOT(setTileLevel(int))); QObject::connect(&m_textureLayer, SIGNAL(tileLevelChanged(int)), &m_placemarkLayer, SLOT(setTileLevel(int))); /* * Slot handleHighlight finds all placemarks * that contain the clicked point. * The placemarks under the clicked position may * have their styleUrl set to a style map which * doesn't specify any highlight styleId. Such * placemarks will be fletered out in GeoGraphicsScene * and will not be highlighted. */ QObject::connect( parent, SIGNAL(highlightedPlacemarksChanged(qreal,qreal,GeoDataCoordinates::Unit)), &m_geometryLayer, SLOT(handleHighlight(qreal,qreal,GeoDataCoordinates::Unit)) ); QObject::connect(&m_floatItemsLayer, SIGNAL(repaintNeeded(QRegion)), parent, SIGNAL(repaintNeeded(QRegion))); QObject::connect(&m_floatItemsLayer, SIGNAL(renderPluginInitialized(RenderPlugin*)), parent, SIGNAL(renderPluginInitialized(RenderPlugin*))); QObject::connect(&m_floatItemsLayer, SIGNAL(visibilityChanged(QString,bool)), parent, SLOT(setPropertyValue(QString,bool))); QObject::connect(&m_floatItemsLayer, SIGNAL(pluginSettingsChanged()), parent, SIGNAL(pluginSettingsChanged())); QObject::connect( &m_textureLayer, SIGNAL(tileLevelChanged(int)), parent, SLOT(updateTileLevel()) ); QObject::connect( &m_vectorTileLayer, SIGNAL(tileLevelChanged(int)), parent, SLOT(updateTileLevel()) ); QObject::connect( &m_textureLayer, SIGNAL(repaintNeeded()), parent, SIGNAL(repaintNeeded()) ); QObject::connect( parent, SIGNAL(visibleLatLonAltBoxChanged(GeoDataLatLonAltBox)), parent, SIGNAL(repaintNeeded()) ); addPlugins(); QObject::connect(model->pluginManager(), SIGNAL(renderPluginsChanged()), parent, SLOT(addPlugins())); } void MarbleMapPrivate::updateProperty( const QString &name, bool show ) { // earth if (name == QLatin1String("places")) { m_placemarkLayer.setShowPlaces( show ); } else if (name == QLatin1String("cities")) { m_placemarkLayer.setShowCities( show ); } else if (name == QLatin1String("terrain")) { m_placemarkLayer.setShowTerrain( show ); } else if (name == QLatin1String("otherplaces")) { m_placemarkLayer.setShowOtherPlaces( show ); } // other planets else if (name == QLatin1String("landingsites")) { m_placemarkLayer.setShowLandingSites( show ); } else if (name == QLatin1String("craters")) { m_placemarkLayer.setShowCraters( show ); } else if (name == QLatin1String("maria")) { m_placemarkLayer.setShowMaria( show ); } else if (name == QLatin1String("relief")) { m_textureLayer.setShowRelief( show ); } for(RenderPlugin *renderPlugin: m_renderPlugins) { if ( name == renderPlugin->nameId() ) { if ( renderPlugin->visible() == show ) { break; } renderPlugin->setVisible( show ); break; } } } void MarbleMapPrivate::addPlugins() { for (const RenderPlugin *factory: m_model->pluginManager()->renderPlugins()) { bool alreadyCreated = false; for(const RenderPlugin *existing: m_renderPlugins) { if (existing->nameId() == factory->nameId()) { alreadyCreated = true; break; } } if (alreadyCreated) { continue; } RenderPlugin *const renderPlugin = factory->newInstance(m_model); Q_ASSERT(renderPlugin && "Plugin must not return null when requesting a new instance."); m_renderPlugins << renderPlugin; if (AbstractFloatItem *const floatItem = qobject_cast(renderPlugin)) { m_floatItemsLayer.addFloatItem(floatItem); } else { m_layerManager.addRenderPlugin(renderPlugin); } } } // ---------------------------------------------------------------- MarbleMap::MarbleMap() : d( new MarbleMapPrivate( this, new MarbleModel( this ) ) ) { // nothing to do } MarbleMap::MarbleMap(MarbleModel *model) : d( new MarbleMapPrivate( this, model ) ) { d->m_modelIsOwned = false; } MarbleMap::~MarbleMap() { MarbleModel *model = d->m_modelIsOwned ? d->m_model : 0; d->m_layerManager.removeLayer( &d->m_customPaintLayer ); d->m_layerManager.removeLayer( &d->m_geometryLayer ); d->m_layerManager.removeLayer(&d->m_floatItemsLayer); d->m_layerManager.removeLayer( &d->m_fogLayer ); d->m_layerManager.removeLayer( &d->m_placemarkLayer ); d->m_layerManager.removeLayer( &d->m_textureLayer ); d->m_layerManager.removeLayer( &d->m_groundLayer ); qDeleteAll(d->m_renderPlugins); delete d; delete model; // delete the model after private data } MarbleModel *MarbleMap::model() const { return d->m_model; } ViewportParams *MarbleMap::viewport() { return &d->m_viewport; } const ViewportParams *MarbleMap::viewport() const { return &d->m_viewport; } void MarbleMap::setMapQualityForViewContext( MapQuality quality, ViewContext viewContext ) { d->m_viewParams.setMapQualityForViewContext( quality, viewContext ); // Update texture map during the repaint that follows: d->m_textureLayer.setNeedsUpdate(); } MapQuality MarbleMap::mapQuality( ViewContext viewContext ) const { return d->m_viewParams.mapQuality( viewContext ); } MapQuality MarbleMap::mapQuality() const { return d->m_viewParams.mapQuality(); } void MarbleMap::setViewContext( ViewContext viewContext ) { if ( d->m_viewParams.viewContext() == viewContext ) { return; } const MapQuality oldQuality = d->m_viewParams.mapQuality(); d->m_viewParams.setViewContext( viewContext ); emit viewContextChanged( viewContext ); if ( d->m_viewParams.mapQuality() != oldQuality ) { // Update texture map during the repaint that follows: d->m_textureLayer.setNeedsUpdate(); emit repaintNeeded(); } } ViewContext MarbleMap::viewContext() const { return d->m_viewParams.viewContext(); } void MarbleMap::setSize( int width, int height ) { setSize( QSize( width, height ) ); } void MarbleMap::setSize( const QSize& size ) { d->m_viewport.setSize( size ); emit visibleLatLonAltBoxChanged( d->m_viewport.viewLatLonAltBox() ); } QSize MarbleMap::size() const { return QSize( d->m_viewport.width(), d->m_viewport.height() ); } int MarbleMap::width() const { return d->m_viewport.width(); } int MarbleMap::height() const { return d->m_viewport.height(); } int MarbleMap::radius() const { return d->m_viewport.radius(); } void MarbleMap::setRadius( int radius ) { const int oldRadius = d->m_viewport.radius(); d->m_viewport.setRadius( radius ); if ( oldRadius != d->m_viewport.radius() ) { emit radiusChanged( radius ); emit visibleLatLonAltBoxChanged( d->m_viewport.viewLatLonAltBox() ); } } int MarbleMap::preferredRadiusCeil( int radius ) { return d->m_textureLayer.preferredRadiusCeil( radius ); } int MarbleMap::preferredRadiusFloor( int radius ) { return d->m_textureLayer.preferredRadiusFloor( radius ); } int MarbleMap::tileZoomLevel() const { return qMax(d->m_textureLayer.tileZoomLevel(), d->m_vectorTileLayer.tileZoomLevel()); } qreal MarbleMap::centerLatitude() const { // Calculate translation of center point const qreal centerLat = d->m_viewport.centerLatitude(); return centerLat * RAD2DEG; } bool MarbleMap::hasFeatureAt(const QPoint &position) const { return d->m_placemarkLayer.hasPlacemarkAt(position) || d->m_geometryLayer.hasFeatureAt(position, viewport()); } qreal MarbleMap::centerLongitude() const { // Calculate translation of center point const qreal centerLon = d->m_viewport.centerLongitude(); return centerLon * RAD2DEG; } int MarbleMap::minimumZoom() const { if ( d->m_model->mapTheme() ) return d->m_model->mapTheme()->head()->zoom()->minimum(); return 950; } int MarbleMap::maximumZoom() const { if ( d->m_model->mapTheme() ) return d->m_model->mapTheme()->head()->zoom()->maximum(); return 2100; } bool MarbleMap::discreteZoom() const { if ( d->m_model->mapTheme() ) return d->m_model->mapTheme()->head()->zoom()->discrete(); return false; } QVector MarbleMap::whichFeatureAt( const QPoint& curpos ) const { return d->m_placemarkLayer.whichPlacemarkAt( curpos ) + d->m_geometryLayer.whichFeatureAt( curpos, viewport() ); } void MarbleMap::reload() { d->m_textureLayer.reload(); d->m_vectorTileLayer.reload(); } void MarbleMap::downloadRegion( QVector const & pyramid ) { Q_ASSERT( textureLayer() ); Q_ASSERT( !pyramid.isEmpty() ); QTime t; t.start(); // When downloading a region (the author of these lines thinks) most users probably expect // the download to begin with the low resolution tiles and then procede level-wise to // higher resolution tiles. In order to achieve this, we start requesting downloads of // high resolution tiles and request the low resolution tiles at the end because // DownloadQueueSet (silly name) is implemented as stack. int const first = 0; int tilesCount = 0; for ( int level = pyramid[first].bottomLevel(); level >= pyramid[first].topLevel(); --level ) { QSet tileIdSet; for( int i = 0; i < pyramid.size(); ++i ) { QRect const coords = pyramid[i].coords( level ); mDebug() << "MarbleMap::downloadRegion level:" << level << "tile coords:" << coords; int x1, y1, x2, y2; coords.getCoords( &x1, &y1, &x2, &y2 ); for ( int x = x1; x <= x2; ++x ) { for ( int y = y1; y <= y2; ++y ) { TileId const stackedTileId( 0, level, x, y ); tileIdSet.insert( stackedTileId ); // FIXME: use lazy evaluation to not generate up to 100k tiles in one go // this can take considerable time even on very fast systems // in contrast generating the TileIds on the fly when they are needed // does not seem to affect download speed. } } } QSetIterator i( tileIdSet ); while( i.hasNext() ) { TileId const tileId = i.next(); d->m_textureLayer.downloadStackedTile( tileId ); } tilesCount += tileIdSet.count(); } // Needed for downloading unique tiles only. Much faster than if tiles for each level is downloaded separately int const elapsedMs = t.elapsed(); mDebug() << "MarbleMap::downloadRegion:" << tilesCount << "tiles, " << elapsedMs << "ms"; } void MarbleMap::highlightRouteRelation(qint64 osmId, bool enabled) { d->m_geometryLayer.highlightRouteRelation(osmId, enabled); } bool MarbleMap::propertyValue( const QString& name ) const { bool value; if ( d->m_model->mapTheme() ) { d->m_model->mapTheme()->settings()->propertyValue( name, value ); } else { value = false; mDebug() << "WARNING: Failed to access a map theme! Property: " << name; } return value; } bool MarbleMap::showOverviewMap() const { return propertyValue(QStringLiteral("overviewmap")); } bool MarbleMap::showScaleBar() const { return propertyValue(QStringLiteral("scalebar")); } bool MarbleMap::showCompass() const { return propertyValue(QStringLiteral("compass")); } bool MarbleMap::showGrid() const { return propertyValue(QStringLiteral("coordinate-grid")); } bool MarbleMap::showClouds() const { return d->m_viewParams.showClouds(); } bool MarbleMap::showSunShading() const { return d->m_textureLayer.showSunShading(); } bool MarbleMap::showCityLights() const { return d->m_textureLayer.showCityLights(); } bool MarbleMap::isLockedToSubSolarPoint() const { return d->m_isLockedToSubSolarPoint; } bool MarbleMap::isSubSolarPointIconVisible() const { return d->m_isSubSolarPointIconVisible; } bool MarbleMap::showAtmosphere() const { return d->m_viewParams.showAtmosphere(); } bool MarbleMap::showCrosshairs() const { bool visible = false; QList pluginList = renderPlugins(); QList::const_iterator i = pluginList.constBegin(); QList::const_iterator const end = pluginList.constEnd(); for (; i != end; ++i ) { if ((*i)->nameId() == QLatin1String("crosshairs")) { visible = (*i)->visible(); } } return visible; } bool MarbleMap::showPlaces() const { return propertyValue(QStringLiteral("places")); } bool MarbleMap::showCities() const { return propertyValue(QStringLiteral("cities")); } bool MarbleMap::showTerrain() const { return propertyValue(QStringLiteral("terrain")); } bool MarbleMap::showOtherPlaces() const { return propertyValue(QStringLiteral("otherplaces")); } bool MarbleMap::showRelief() const { return propertyValue(QStringLiteral("relief")); } bool MarbleMap::showIceLayer() const { return propertyValue(QStringLiteral("ice")); } bool MarbleMap::showBorders() const { return propertyValue(QStringLiteral("borders")); } bool MarbleMap::showRivers() const { return propertyValue(QStringLiteral("rivers")); } bool MarbleMap::showLakes() const { return propertyValue(QStringLiteral("lakes")); } bool MarbleMap::showFrameRate() const { return d->m_showFrameRate; } bool MarbleMap::showBackground() const { return d->m_layerManager.showBackground(); } -bool MarbleMap::showPublicTransport() const +GeoDataRelation::RelationTypes MarbleMap::visibleRelationTypes() const { - return d->m_showPublicTransport; + return d->m_visibleRelationTypes; } quint64 MarbleMap::volatileTileCacheLimit() const { return d->m_textureLayer.volatileCacheLimit(); } void MarbleMap::rotateBy(qreal deltaLon, qreal deltaLat) { centerOn( d->m_viewport.centerLongitude() * RAD2DEG + deltaLon, d->m_viewport.centerLatitude() * RAD2DEG + deltaLat ); } void MarbleMap::centerOn( const qreal lon, const qreal lat ) { d->m_viewport.centerOn( lon * DEG2RAD, lat * DEG2RAD ); emit visibleLatLonAltBoxChanged( d->m_viewport.viewLatLonAltBox() ); } void MarbleMap::setCenterLatitude( qreal lat ) { centerOn( centerLongitude(), lat ); } void MarbleMap::setCenterLongitude( qreal lon ) { centerOn( lon, centerLatitude() ); } Projection MarbleMap::projection() const { return d->m_viewport.projection(); } void MarbleMap::setProjection( Projection projection ) { if ( d->m_viewport.projection() == projection ) return; emit projectionChanged( projection ); d->m_viewport.setProjection( projection ); d->m_textureLayer.setProjection( projection ); emit visibleLatLonAltBoxChanged( d->m_viewport.viewLatLonAltBox() ); } bool MarbleMap::screenCoordinates( qreal lon, qreal lat, qreal& x, qreal& y ) const { return d->m_viewport.screenCoordinates( lon * DEG2RAD, lat * DEG2RAD, x, y ); } bool MarbleMap::geoCoordinates( int x, int y, qreal& lon, qreal& lat, GeoDataCoordinates::Unit unit ) const { return d->m_viewport.geoCoordinates( x, y, lon, lat, unit ); } void MarbleMapPrivate::setDocument( QString key ) { if ( !m_model->mapTheme() ) { // Happens if no valid map theme is set or at application startup // if a file is passed via command line parameters and the last // map theme has not been loaded yet /** * @todo Do we need to queue the document and process it once a map * theme becomes available? */ return; } GeoDataDocument* doc = m_model->fileManager()->at( key ); for ( const GeoSceneLayer *layer: m_model->mapTheme()->map()->layers() ) { if ( layer->backend() != dgml::dgmlValue_geodata && layer->backend() != dgml::dgmlValue_vector ) continue; // look for documents for ( const GeoSceneAbstractDataset *dataset: layer->datasets() ) { const GeoSceneGeodata *data = static_cast( dataset ); QString containername = data->sourceFile(); QString colorize = data->colorize(); if( key == containername ) { if (colorize == QLatin1String("land")) { m_textureLayer.addLandDocument( doc ); } if (colorize == QLatin1String("sea")) { m_textureLayer.addSeaDocument( doc ); } // set visibility according to theme property if( !data->property().isEmpty() ) { bool value; m_model->mapTheme()->settings()->propertyValue( data->property(), value ); doc->setVisible( value ); m_model->treeModel()->updateFeature( doc ); } } } } } void MarbleMapPrivate::updateTileLevel() { emit q->tileLevelChanged(q->tileZoomLevel()); } // Used to be paintEvent() void MarbleMap::paint( GeoPainter &painter, const QRect &dirtyRect ) { Q_UNUSED( dirtyRect ); if (d->m_showDebugPolygons ) { if (viewContext() == Animation) { painter.setDebugPolygonsLevel(1); } else { painter.setDebugPolygonsLevel(2); } } painter.setDebugBatchRender(d->m_showDebugBatchRender); if ( !d->m_model->mapTheme() ) { mDebug() << "No theme yet!"; d->m_marbleSplashLayer.render( &painter, &d->m_viewport ); return; } QTime t; t.start(); RenderStatus const oldRenderStatus = d->m_renderState.status(); d->m_layerManager.renderLayers( &painter, &d->m_viewport ); d->m_renderState = d->m_layerManager.renderState(); bool const parsing = d->m_model->fileManager()->pendingFiles() > 0; d->m_renderState.addChild(RenderState(QStringLiteral("Files"), parsing ? WaitingForData : Complete)); RenderStatus const newRenderStatus = d->m_renderState.status(); if ( oldRenderStatus != newRenderStatus ) { emit renderStatusChanged( newRenderStatus ); } emit renderStateChanged( d->m_renderState ); if ( d->m_showFrameRate ) { FpsLayer fpsPainter( &t ); fpsPainter.paint( &painter ); } const qreal fps = 1000.0 / (qreal)( t.elapsed() ); emit framesPerSecond( fps ); } void MarbleMap::customPaint( GeoPainter *painter ) { Q_UNUSED( painter ); } QString MarbleMap::mapThemeId() const { return d->m_model->mapThemeId(); } void MarbleMap::setMapThemeId( const QString& mapThemeId ) { d->m_model->setMapThemeId( mapThemeId ); } void MarbleMapPrivate::updateMapTheme() { m_layerManager.removeLayer( &m_textureLayer ); // FIXME Find a better way to do this reset. Maybe connect to themeChanged SIGNAL? m_vectorTileLayer.reset(); m_layerManager.removeLayer( &m_vectorTileLayer ); m_layerManager.removeLayer( &m_groundLayer ); QObject::connect( m_model->mapTheme()->settings(), SIGNAL(valueChanged(QString,bool)), q, SLOT(updateProperty(QString,bool)) ); QObject::connect( m_model->mapTheme()->settings(), SIGNAL(valueChanged(QString,bool)), m_model, SLOT(updateProperty(QString,bool)) ); q->setPropertyValue(QStringLiteral("clouds_data"), m_viewParams.showClouds()); m_groundLayer.setColor( m_model->mapTheme()->map()->backgroundColor() ); // Check whether there is a texture layer and vectortile layer available: if ( m_model->mapTheme()->map()->hasTextureLayers() ) { const GeoSceneSettings *const settings = m_model->mapTheme()->settings(); const GeoSceneGroup *const textureLayerSettings = settings ? settings->group( "Texture Layers" ) : 0; const GeoSceneGroup *const vectorTileLayerSettings = settings ? settings->group( "VectorTile Layers" ) : 0; bool textureLayersOk = true; bool vectorTileLayersOk = true; // textures will contain texture layers and // vectorTiles vectortile layers QVector textures; QVector vectorTiles; for( GeoSceneLayer* layer: m_model->mapTheme()->map()->layers() ){ if ( layer->backend() == dgml::dgmlValue_texture ){ for ( const GeoSceneAbstractDataset *pos: layer->datasets() ) { const GeoSceneTextureTileDataset *const texture = dynamic_cast( pos ); if ( !texture ) continue; const QString sourceDir = texture->sourceDir(); const QString installMap = texture->installMap(); const QString role = layer->role(); // If the tiles aren't already there, put up a progress dialog // while creating them. if ( !TileLoader::baseTilesAvailable( *texture ) && !installMap.isEmpty() ) { mDebug() << "Base tiles not available. Creating Tiles ... \n" << "SourceDir: " << sourceDir << "InstallMap:" << installMap; TileCreator *tileCreator = new TileCreator( sourceDir, installMap, (role == QLatin1String("dem")) ? "true" : "false" ); tileCreator->setTileFormat( texture->fileFormat().toLower() ); QPointer tileCreatorDlg = new TileCreatorDialog( tileCreator, 0 ); tileCreatorDlg->setSummary( m_model->mapTheme()->head()->name(), m_model->mapTheme()->head()->description() ); tileCreatorDlg->exec(); if ( TileLoader::baseTilesAvailable( *texture ) ) { mDebug() << "Base tiles for" << sourceDir << "successfully created."; } else { qWarning() << "Some or all base tiles for" << sourceDir << "could not be created."; } delete tileCreatorDlg; } if ( TileLoader::baseTilesAvailable( *texture ) ) { textures.append( texture ); } else { qWarning() << "Base tiles for" << sourceDir << "not available. Skipping all texture layers."; textureLayersOk = false; } } } else if ( layer->backend() == dgml::dgmlValue_vectortile ){ for ( const GeoSceneAbstractDataset *pos: layer->datasets() ) { const GeoSceneVectorTileDataset *const vectorTile = dynamic_cast( pos ); if ( !vectorTile ) continue; const QString sourceDir = vectorTile->sourceDir(); const QString installMap = vectorTile->installMap(); const QString role = layer->role(); // If the tiles aren't already there, put up a progress dialog // while creating them. if ( !TileLoader::baseTilesAvailable( *vectorTile ) && !installMap.isEmpty() ) { mDebug() << "Base tiles not available. Creating Tiles ... \n" << "SourceDir: " << sourceDir << "InstallMap:" << installMap; TileCreator *tileCreator = new TileCreator( sourceDir, installMap, (role == QLatin1String("dem")) ? "true" : "false" ); tileCreator->setTileFormat( vectorTile->fileFormat().toLower() ); QPointer tileCreatorDlg = new TileCreatorDialog( tileCreator, 0 ); tileCreatorDlg->setSummary( m_model->mapTheme()->head()->name(), m_model->mapTheme()->head()->description() ); tileCreatorDlg->exec(); if ( TileLoader::baseTilesAvailable( *vectorTile ) ) { qDebug() << "Base tiles for" << sourceDir << "successfully created."; } else { qDebug() << "Some or all base tiles for" << sourceDir << "could not be created."; } delete tileCreatorDlg; } if ( TileLoader::baseTilesAvailable( *vectorTile ) ) { vectorTiles.append( vectorTile ); } else { qWarning() << "Base tiles for" << sourceDir << "not available. Skipping all texture layers."; vectorTileLayersOk = false; } } } } QString seafile, landfile; if( !m_model->mapTheme()->map()->filters().isEmpty() ) { const GeoSceneFilter *filter= m_model->mapTheme()->map()->filters().first(); if (filter->type() == QLatin1String("colorize")) { //no need to look up with MarbleDirs twice so they are left null for now QList palette = filter->palette(); for (const GeoScenePalette *curPalette: palette ) { if (curPalette->type() == QLatin1String("sea")) { seafile = MarbleDirs::path( curPalette->file() ); } else if (curPalette->type() == QLatin1String("land")) { landfile = MarbleDirs::path( curPalette->file() ); } } //look up locations if they are empty if( seafile.isEmpty() ) seafile = MarbleDirs::path(QStringLiteral("seacolors.leg")); if( landfile.isEmpty() ) landfile = MarbleDirs::path(QStringLiteral("landcolors.leg")); } } m_textureLayer.setMapTheme( textures, textureLayerSettings, seafile, landfile ); m_textureLayer.setProjection( m_viewport.projection() ); m_textureLayer.setShowRelief( q->showRelief() ); m_vectorTileLayer.setMapTheme( vectorTiles, vectorTileLayerSettings ); if (m_textureLayer.textureLayerCount() == 0) { m_layerManager.addLayer( &m_groundLayer ); } if ( textureLayersOk ) m_layerManager.addLayer( &m_textureLayer ); if ( vectorTileLayersOk && !vectorTiles.isEmpty() ) m_layerManager.addLayer( &m_vectorTileLayer ); } else { m_layerManager.addLayer( &m_groundLayer ); m_textureLayer.setMapTheme( QVector(), 0, "", "" ); m_vectorTileLayer.setMapTheme( QVector(), 0 ); } // earth m_placemarkLayer.setShowPlaces( q->showPlaces() ); m_placemarkLayer.setShowCities( q->showCities() ); m_placemarkLayer.setShowTerrain( q->showTerrain() ); m_placemarkLayer.setShowOtherPlaces( q->showOtherPlaces() ); m_placemarkLayer.setShowLandingSites(q->propertyValue(QStringLiteral("landingsites"))); m_placemarkLayer.setShowCraters(q->propertyValue(QStringLiteral("craters"))); m_placemarkLayer.setShowMaria(q->propertyValue(QStringLiteral("maria"))); m_styleBuilder.setDefaultLabelColor(m_model->mapTheme()->map()->labelColor()); m_placemarkLayer.requestStyleReset(); for (RenderPlugin *renderPlugin: m_renderPlugins) { bool propertyAvailable = false; m_model->mapTheme()->settings()->propertyAvailable( renderPlugin->nameId(), propertyAvailable ); bool propertyValue = false; m_model->mapTheme()->settings()->propertyValue( renderPlugin->nameId(), propertyValue ); if ( propertyAvailable ) { renderPlugin->setVisible( propertyValue ); } } emit q->themeChanged( m_model->mapTheme()->head()->mapThemeId() ); } void MarbleMap::setPropertyValue( const QString& name, bool value ) { mDebug() << "In MarbleMap the property " << name << "was set to " << value; if ( d->m_model->mapTheme() ) { d->m_model->mapTheme()->settings()->setPropertyValue( name, value ); d->m_textureLayer.setNeedsUpdate(); } else { mDebug() << "WARNING: Failed to access a map theme! Property: " << name; } if (d->m_textureLayer.textureLayerCount() == 0) { d->m_layerManager.addLayer( &d->m_groundLayer ); } else { d->m_layerManager.removeLayer( &d->m_groundLayer ); } } void MarbleMap::setShowOverviewMap( bool visible ) { setPropertyValue(QStringLiteral("overviewmap"), visible); } void MarbleMap::setShowScaleBar( bool visible ) { setPropertyValue(QStringLiteral("scalebar"), visible); } void MarbleMap::setShowCompass( bool visible ) { setPropertyValue(QStringLiteral("compass"), visible); } void MarbleMap::setShowAtmosphere( bool visible ) { for ( RenderPlugin *plugin: renderPlugins() ) { if (plugin->nameId() == QLatin1String("atmosphere")) { plugin->setVisible( visible ); } } d->m_viewParams.setShowAtmosphere( visible ); } void MarbleMap::setShowCrosshairs( bool visible ) { QList pluginList = renderPlugins(); QList::const_iterator i = pluginList.constBegin(); QList::const_iterator const end = pluginList.constEnd(); for (; i != end; ++i ) { if ((*i)->nameId() == QLatin1String("crosshairs")) { (*i)->setVisible( visible ); } } } void MarbleMap::setShowClouds( bool visible ) { d->m_viewParams.setShowClouds( visible ); setPropertyValue(QStringLiteral("clouds_data"), visible); } void MarbleMap::setShowSunShading( bool visible ) { d->m_textureLayer.setShowSunShading( visible ); } void MarbleMap::setShowCityLights( bool visible ) { d->m_textureLayer.setShowCityLights( visible ); setPropertyValue(QStringLiteral("citylights"), visible); } void MarbleMap::setLockToSubSolarPoint( bool visible ) { disconnect( d->m_model->sunLocator(), SIGNAL(positionChanged(qreal,qreal)), this, SLOT(centerOn(qreal,qreal)) ); if( isLockedToSubSolarPoint() != visible ) { d->m_isLockedToSubSolarPoint = visible; } if ( isLockedToSubSolarPoint() ) { connect( d->m_model->sunLocator(), SIGNAL(positionChanged(qreal,qreal)), this, SLOT(centerOn(qreal,qreal)) ); centerOn( d->m_model->sunLocator()->getLon(), d->m_model->sunLocator()->getLat() ); } else if ( visible ) { mDebug() << "Ignoring centering on sun, since the sun plugin is not loaded."; } } void MarbleMap::setSubSolarPointIconVisible( bool visible ) { if ( isSubSolarPointIconVisible() != visible ) { d->m_isSubSolarPointIconVisible = visible; } } void MarbleMap::setShowTileId( bool visible ) { d->m_textureLayer.setShowTileId( visible ); } void MarbleMap::setShowGrid( bool visible ) { setPropertyValue(QStringLiteral("coordinate-grid"), visible); } void MarbleMap::setShowPlaces( bool visible ) { setPropertyValue(QStringLiteral("places"), visible); } void MarbleMap::setShowCities( bool visible ) { setPropertyValue(QStringLiteral("cities"), visible); } void MarbleMap::setShowTerrain( bool visible ) { setPropertyValue(QStringLiteral("terrain"), visible); } void MarbleMap::setShowOtherPlaces( bool visible ) { setPropertyValue(QStringLiteral("otherplaces"), visible); } void MarbleMap::setShowRelief( bool visible ) { setPropertyValue(QStringLiteral("relief"), visible); } void MarbleMap::setShowIceLayer( bool visible ) { setPropertyValue(QStringLiteral("ice"), visible); } void MarbleMap::setShowBorders( bool visible ) { setPropertyValue(QStringLiteral("borders"), visible); } void MarbleMap::setShowRivers( bool visible ) { setPropertyValue(QStringLiteral("rivers"), visible); } void MarbleMap::setShowLakes( bool visible ) { setPropertyValue(QStringLiteral("lakes"), visible); } void MarbleMap::setShowFrameRate( bool visible ) { d->m_showFrameRate = visible; } void MarbleMap::setShowRuntimeTrace( bool visible ) { if (visible != d->m_layerManager.showRuntimeTrace()) { d->m_layerManager.setShowRuntimeTrace(visible); emit repaintNeeded(); } } bool MarbleMap::showRuntimeTrace() const { return d->m_layerManager.showRuntimeTrace(); } void MarbleMap::setShowDebugPolygons( bool visible) { if (visible != d->m_showDebugPolygons) { d->m_showDebugPolygons = visible; emit repaintNeeded(); } } bool MarbleMap::showDebugPolygons() const { return d->m_showDebugPolygons; } void MarbleMap::setShowDebugBatchRender( bool visible) { qDebug() << Q_FUNC_INFO << visible; if (visible != d->m_showDebugBatchRender) { d->m_showDebugBatchRender = visible; emit repaintNeeded(); } } bool MarbleMap::showDebugBatchRender() const { return d->m_showDebugBatchRender; } void MarbleMap::setShowDebugPlacemarks( bool visible) { if (visible != d->m_placemarkLayer.isDebugModeEnabled()) { d->m_placemarkLayer.setDebugModeEnabled(visible); emit repaintNeeded(); } } bool MarbleMap::showDebugPlacemarks() const { return d->m_placemarkLayer.isDebugModeEnabled(); } void MarbleMap::setShowBackground( bool visible ) { d->m_layerManager.setShowBackground( visible ); } -void MarbleMap::setShowPublicTransport(bool showPublicTransport) +void MarbleMap::setVisibleRelationTypes(GeoDataRelation::RelationTypes relationTypes) { - if (d->m_showPublicTransport != showPublicTransport) { - d->m_showPublicTransport = showPublicTransport; - d->m_geometryLayer.setShowPublicTransport(showPublicTransport); - emit showPublicTransportChanged(showPublicTransport); + if (d->m_visibleRelationTypes != relationTypes) { + d->m_visibleRelationTypes = relationTypes; + d->m_geometryLayer.setVisibleRelationTypes(relationTypes); + emit visibleRelationTypesChanged(d->m_visibleRelationTypes); } } void MarbleMap::notifyMouseClick( int x, int y ) { qreal lon = 0; qreal lat = 0; const bool valid = geoCoordinates( x, y, lon, lat, GeoDataCoordinates::Radian ); if ( valid ) { emit mouseClickGeoPosition( lon, lat, GeoDataCoordinates::Radian ); } } void MarbleMap::clearVolatileTileCache() { d->m_vectorTileLayer.reset(); d->m_textureLayer.reset(); mDebug() << "Cleared Volatile Cache!"; } void MarbleMap::setVolatileTileCacheLimit( quint64 kilobytes ) { mDebug() << "kiloBytes" << kilobytes; d->m_textureLayer.setVolatileCacheLimit( kilobytes ); } AngleUnit MarbleMap::defaultAngleUnit() const { if ( GeoDataCoordinates::defaultNotation() == GeoDataCoordinates::Decimal ) { return DecimalDegree; } else if ( GeoDataCoordinates::defaultNotation() == GeoDataCoordinates::UTM ) { return UTM; } return DMSDegree; } void MarbleMap::setDefaultAngleUnit( AngleUnit angleUnit ) { if ( angleUnit == DecimalDegree ) { GeoDataCoordinates::setDefaultNotation( GeoDataCoordinates::Decimal ); return; } else if ( angleUnit == UTM ) { GeoDataCoordinates::setDefaultNotation( GeoDataCoordinates::UTM ); return; } GeoDataCoordinates::setDefaultNotation( GeoDataCoordinates::DMS ); } QFont MarbleMap::defaultFont() const { return d->m_styleBuilder.defaultFont(); } void MarbleMap::setDefaultFont( const QFont& font ) { d->m_styleBuilder.setDefaultFont(font); d->m_placemarkLayer.requestStyleReset(); } QList MarbleMap::renderPlugins() const { return d->m_renderPlugins; } QList MarbleMap::floatItems() const { return d->m_floatItemsLayer.floatItems(); } AbstractFloatItem * MarbleMap::floatItem( const QString &nameId ) const { for ( AbstractFloatItem * floatItem: floatItems() ) { if ( floatItem && floatItem->nameId() == nameId ) { return floatItem; } } return 0; // No item found } QList MarbleMap::dataPlugins() const { return d->m_layerManager.dataPlugins(); } QList MarbleMap::whichItemAt( const QPoint& curpos ) const { return d->m_layerManager.whichItemAt( curpos ); } void MarbleMap::addLayer( LayerInterface *layer ) { d->m_layerManager.addLayer(layer); } void MarbleMap::removeLayer( LayerInterface *layer ) { d->m_layerManager.removeLayer(layer); } RenderStatus MarbleMap::renderStatus() const { return d->m_layerManager.renderState().status(); } RenderState MarbleMap::renderState() const { return d->m_layerManager.renderState(); } QString MarbleMap::addTextureLayer(GeoSceneTextureTileDataset *texture) { return textureLayer()->addTextureLayer(texture); } void MarbleMap::removeTextureLayer(const QString &key) { textureLayer()->removeTextureLayer(key); } // this method will only temporarily "pollute" the MarbleModel class TextureLayer *MarbleMap::textureLayer() const { return &d->m_textureLayer; } const StyleBuilder* MarbleMap::styleBuilder() const { return &d->m_styleBuilder; } } #include "moc_MarbleMap.cpp" diff --git a/src/lib/marble/MarbleMap.h b/src/lib/marble/MarbleMap.h index 1ae271d0d..3e1636e0a 100644 --- a/src/lib/marble/MarbleMap.h +++ b/src/lib/marble/MarbleMap.h @@ -1,789 +1,790 @@ // // 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 2006-2008 Torsten Rahn // Copyright 2007 Inge Wallin // Copyright 2009 Jens-Michael Hoffmann // #ifndef MARBLE_MARBLEMAP_H #define MARBLE_MARBLEMAP_H /** @file * This file contains the headers for MarbleMap. * * @author Torsten Rahn * @author Inge Wallin */ #include "marble_export.h" #include "GeoDataCoordinates.h" // In geodata/data/ +#include "GeoDataRelation.h" // Qt #include #include class QFont; class QString; namespace Marble { // MarbleMap class MarbleMapPrivate; // Marble class GeoDataLatLonAltBox; class GeoDataFeature; class MarbleModel; class ViewportParams; class GeoPainter; class LayerInterface; class RenderPlugin; class RenderState; class AbstractDataPlugin; class AbstractDataPluginItem; class AbstractFloatItem; class TextureLayer; class TileCoordsPyramid; class GeoSceneTextureTileDataset; class StyleBuilder; /** * @short A class that can paint a view of the earth. * * FIXME: Change this description when we are done. * * This class can paint a view of the earth or any other globe, * depending on which dataset is used. It can be used to show the * globe in a widget like MarbleWidget does, or on any other * QPaintDevice. * * The projection and other view parameters that control how MarbleMap * paints the map is given through the class ViewParams. If the * programmer wants to allow the user to control the map, he/she has * to provide a way for the user to interact with it. An example of * this can be seen in the class MarbleWidgetInputHandler, that lets * the user control a MarbleWidget that uses MarbleMap internally. * * The MarbleMap needs to be provided with a data model to * work. This model is contained in the MarbleModel class. The widget * can also construct its own model if none is given to the * constructor. This data model contains 3 separate datatypes: * tiles which provide the background, vectors which * provide things like country borders and coastlines and * placemarks which can show points of interest, such as * cities, mountain tops or the poles. * * @see MarbleWidget * @see MarbleModel */ class MARBLE_EXPORT MarbleMap : public QObject { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.kde.MarbleMap") public: friend class MarbleWidget; /** * @brief Construct a new MarbleMap. * * This constructor should be used when you will only use one * MarbleMap. The widget will create its own MarbleModel when * created. */ MarbleMap(); /** * @brief Construct a new MarbleMap. * @param model the data model for the widget. * * This constructor should be used when you plan to use more than * one MarbleMap for the same MarbleModel (not yet supported, * but will be soon). */ explicit MarbleMap( MarbleModel *model ); ~MarbleMap() override; /** * @brief Return the model that this view shows. */ MarbleModel *model() const; // Accessors to internal objects; ViewportParams *viewport(); const ViewportParams *viewport() const; /** * @brief Get the Projection used for the map * @return @c Spherical a Globe * @return @c Equirectangular a flat map * @return @c Mercator another flat map */ Projection projection() const; /** * @brief Get the ID of the current map theme * To ensure that a unique identifier is being used the theme does NOT * get represented by its name but the by relative location of the file * that specifies the theme: * * Example: * maptheme = "earth/bluemarble/bluemarble.dgml" */ QString mapThemeId() const; void setMapQualityForViewContext( MapQuality qualityForViewContext, ViewContext viewContext ); MapQuality mapQuality( ViewContext viewContext ) const; /** * @brief Return the current map quality. */ MapQuality mapQuality() const; void setViewContext( ViewContext viewContext ); ViewContext viewContext() const; void setSize( int width, int height ); void setSize( const QSize& size ); QSize size() const; int width() const; int height() const; /** * @brief Return the radius of the globe in pixels. */ int radius() const; int preferredRadiusCeil( int radius ); int preferredRadiusFloor( int radius ); int tileZoomLevel() const; /** * @brief return the minimum zoom value for the current map theme. */ int minimumZoom() const; /** * @brief return the minimum zoom value for the current map theme. */ int maximumZoom() const; bool discreteZoom() const; /** * @brief Get the screen coordinates corresponding to geographical coordinates in the map. * @param lon the lon coordinate of the requested pixel position * @param lat the lat coordinate of the requested pixel position * @param x the x coordinate of the pixel is returned through this parameter * @param y the y coordinate of the pixel is returned through this parameter * @return @c true if the geographical coordinates are visible on the screen * @c false if the geographical coordinates are not visible on the screen */ bool screenCoordinates( qreal lon, qreal lat, qreal& x, qreal& y ) const; /** * @brief Get the earth coordinates corresponding to a pixel in the map. * @param x the x coordinate of the pixel * @param y the y coordinate of the pixel * @param lon the longitude angle is returned through this parameter * @param lat the latitude angle is returned through this parameter * @return @c true if the pixel (x, y) is within the globe * @c false if the pixel (x, y) is outside the globe, i.e. in space. */ bool geoCoordinates( int x, int y, qreal& lon, qreal& lat, GeoDataCoordinates::Unit = GeoDataCoordinates::Degree ) const; /** * @brief Return the longitude of the center point. * @return The longitude of the center point in degree. */ qreal centerLongitude() const; /** * @brief Return the latitude of the center point. * @return The latitude of the center point in degree. */ qreal centerLatitude() const; /** * @since 0.26.0 */ bool hasFeatureAt(const QPoint&) const; QVector whichFeatureAt( const QPoint& ) const; /** * @brief Return the property value by name. * @return The property value (usually: visibility). */ bool propertyValue( const QString& name ) const; /** * @brief Return whether the overview map is visible. * @return The overview map visibility. */ bool showOverviewMap() const; /** * @brief Return whether the scale bar is visible. * @return The scale bar visibility. */ bool showScaleBar() const; /** * @brief Return whether the compass bar is visible. * @return The compass visibility. */ bool showCompass() const; /** * @brief Return whether the cloud cover is visible. * @return The cloud cover visibility. */ bool showClouds() const; /** * @brief Return whether the night shadow is visible. * @return visibility of night shadow */ bool showSunShading() const; /** * @brief Return whether the city lights are shown instead of the night shadow. * @return visibility of city lights */ bool showCityLights() const; /** * @brief Return whether the globe is locked to the sub solar point * @return if globe is locked to sub solar point */ bool isLockedToSubSolarPoint() const; /** * @brief Return whether the sun icon is shown in the sub solar point. * @return visibility of the sun icon in the sub solar point */ bool isSubSolarPointIconVisible() const; /** * @brief Return whether the atmospheric glow is visible. * @return The cloud cover visibility. */ bool showAtmosphere() const; /** * @brief Return whether the crosshairs are visible. * @return The crosshairs' visibility. */ bool showCrosshairs() const; /** * @brief Return whether the coordinate grid is visible. * @return The coordinate grid visibility. */ bool showGrid() const; /** * @brief Return whether the place marks are visible. * @return The place mark visibility. */ bool showPlaces() const; /** * @brief Return whether the city place marks are visible. * @return The city place mark visibility. */ bool showCities() const; /** * @brief Return whether the terrain place marks are visible. * @return The terrain place mark visibility. */ bool showTerrain() const; /** * @brief Return whether other places are visible. * @return The visibility of other places. */ bool showOtherPlaces() const; /** * @brief Return whether the relief is visible. * @return The relief visibility. */ bool showRelief() const; /** * @brief Return whether the ice layer is visible. * @return The ice layer visibility. */ bool showIceLayer() const; /** * @brief Return whether the borders are visible. * @return The border visibility. */ bool showBorders() const; /** * @brief Return whether the rivers are visible. * @return The rivers' visibility. */ bool showRivers() const; /** * @brief Return whether the lakes are visible. * @return The lakes' visibility. */ bool showLakes() const; /** * @brief Return whether the frame rate gets displayed. * @return the frame rates visibility */ bool showFrameRate() const; bool showBackground() const; - bool showPublicTransport() const; + GeoDataRelation::RelationTypes visibleRelationTypes() const; /** * @brief Returns the limit in kilobytes of the volatile (in RAM) tile cache. * @return the limit of volatile tile cache in kilobytes. */ quint64 volatileTileCacheLimit() const; /** * @brief Returns a list of all RenderPlugins in the model, this includes float items * @return the list of RenderPlugins */ QList renderPlugins() const; QList floatItems() const; /** * @brief Returns a list of all FloatItems in the model * @return the list of the floatItems */ AbstractFloatItem * floatItem( const QString &nameId ) const; /** * @brief Returns a list of all DataPlugins on the layer * @return the list of DataPlugins */ QList dataPlugins() const; /** * @brief Returns all widgets of dataPlugins on the position curpos */ QList whichItemAt( const QPoint& curpos ) const; AngleUnit defaultAngleUnit() const; QFont defaultFont() const; TextureLayer *textureLayer() const; /** * @brief Add a layer to be included in rendering. */ void addLayer( LayerInterface *layer ); /** * @brief Adds a texture sublayer * @return Returns a key that identifies the texture sublayer */ QString addTextureLayer(GeoSceneTextureTileDataset *texture); /** * @brief Removes a texture sublayer * @param Key that was returned from corresponding addTextureLayer */ void removeTextureLayer(const QString &key); /** * @brief Remove a layer from being included in rendering. */ void removeLayer( LayerInterface *layer ); RenderStatus renderStatus() const; RenderState renderState() const; /** * @since 0.26.0 */ const StyleBuilder* styleBuilder() const; public Q_SLOTS: /** * @brief Paint the map using a give painter. * @param painter The painter to use. * @param dirtyRect the rectangle that actually needs repainting. */ void paint( GeoPainter &painter, const QRect &dirtyRect ); /** * @brief Set the radius of the globe in pixels. * @param radius The new globe radius value in pixels. */ void setRadius( int radius ); /** * @brief Rotate the view by the two angles phi and theta. * @param deltaLon an angle that specifies the change in terms of longitude * @param deltaLat an angle that specifies the change in terms of latitude * * This function rotates the view by two angles, * deltaLon ("theta") and deltaLat ("phi"). * If we start at (0, 0), the result will be the exact equivalent * of (lon, lat), otherwise the resulting angle will be the sum of * the previous position and the two offsets. */ void rotateBy(qreal deltaLon, qreal deltaLat); /** * @brief Center the view on a geographical point * @param lat an angle parallel to the latitude lines * +90(N) - -90(S) * @param lon an angle parallel to the longitude lines * +180(W) - -180(E) */ void centerOn( const qreal lon, const qreal lat ); /** * @brief Set the latitude for the center point * @param lat the new value for the latitude in degree */ void setCenterLatitude( qreal lat ); /** * @brief Set the longitude for the center point * @param lon the new value for the longitude in degree */ void setCenterLongitude( qreal lon ); /** * @brief Set the Projection used for the map * @param projection projection type (e.g. Spherical, Equirectangular, Mercator) */ void setProjection( Projection projection ); /** * @brief Set a new map theme * @param maptheme The ID of the new maptheme. To ensure that a unique * identifier is being used the theme does NOT get represented by its * name but the by relative location of the file that specifies the theme: * * Example: * maptheme = "earth/bluemarble/bluemarble.dgml" */ void setMapThemeId( const QString& maptheme ); /** * @brief Sets the value of a map theme property * @param value value of the property (usually: visibility) * * Later on we might add a "setPropertyType and a QVariant * if needed. */ void setPropertyValue( const QString& name, bool value ); /** * @brief Set whether the overview map overlay is visible * @param visible visibility of the overview map */ void setShowOverviewMap( bool visible ); /** * @brief Set whether the scale bar overlay is visible * @param visible visibility of the scale bar */ void setShowScaleBar( bool visible ); /** * @brief Set whether the compass overlay is visible * @param visible visibility of the compass */ void setShowCompass( bool visible ); /** * @brief Set whether the cloud cover is visible * @param visible visibility of the cloud cover */ void setShowClouds( bool visible ); /** * @brief Set whether the night shadow is visible. * @param visibile visibility of shadow */ void setShowSunShading( bool visible ); /** * @brief Set whether city lights instead of night shadow are visible. * @param visible visibility of city lights */ void setShowCityLights( bool visible ); /** * @brief Set the globe locked to the sub solar point * @param vsible if globe is locked to the sub solar point */ void setLockToSubSolarPoint( bool visible ); /** * @brief Set whether the sun icon is shown in the sub solar point * @param visible if the sun icon is shown in the sub solar point */ void setSubSolarPointIconVisible( bool visible ); /** * @brief Set whether the is tile is visible * NOTE: This is part of the transitional debug API * and might be subject to changes until Marble 0.8 * @param visible visibility of the tile */ void setShowTileId( bool visible ); /** * @brief Set whether the atmospheric glow is visible * @param visible visibility of the atmospheric glow */ void setShowAtmosphere( bool visible ); /** * @brief Set whether the crosshairs are visible * @param visible visibility of the crosshairs */ void setShowCrosshairs( bool visible ); /** * @brief Set whether the coordinate grid overlay is visible * @param visible visibility of the coordinate grid */ void setShowGrid( bool visible ); /** * @brief Set whether the place mark overlay is visible * @param visible visibility of the place marks */ void setShowPlaces( bool visible ); /** * @brief Set whether the city place mark overlay is visible * @param visible visibility of the city place marks */ void setShowCities( bool visible ); /** * @brief Set whether the terrain place mark overlay is visible * @param visible visibility of the terrain place marks */ void setShowTerrain( bool visible ); /** * @brief Set whether the other places overlay is visible * @param visible visibility of other places */ void setShowOtherPlaces( bool visible ); /** * @brief Set whether the relief is visible * @param visible visibility of the relief */ void setShowRelief( bool visible ); /** * @brief Set whether the ice layer is visible * @param visible visibility of the ice layer */ void setShowIceLayer( bool visible ); /** * @brief Set whether the borders visible * @param visible visibility of the borders */ void setShowBorders( bool visible ); /** * @brief Set whether the rivers are visible * @param visible visibility of the rivers */ void setShowRivers( bool visible ); /** * @brief Set whether the lakes are visible * @param visible visibility of the lakes */ void setShowLakes( bool visible ); /** * @brief Set whether the frame rate gets shown * @param visible visibility of the frame rate */ void setShowFrameRate( bool visible ); void setShowRuntimeTrace( bool visible ); bool showRuntimeTrace() const; /** * @brief Set whether to enter the debug mode for * polygon node drawing * @param visible visibility of the node debug mode */ void setShowDebugPolygons( bool visible); bool showDebugPolygons() const; /** * @brief Set whether to enter the debug mode for * visualizing batch rendering * @param visible visibility of the batch rendering */ void setShowDebugBatchRender( bool visible); bool showDebugBatchRender() const; /** * @brief Set whether to enter the debug mode for * placemark drawing * @param visible visibility of the node debug mode */ void setShowDebugPlacemarks(bool visible); bool showDebugPlacemarks() const; void setShowBackground( bool visible ); - void setShowPublicTransport(bool show); + void setVisibleRelationTypes(GeoDataRelation::RelationTypes relationTypes); /** * @brief used to notify about the position of the mouse click */ void notifyMouseClick( int x, int y ); void clearVolatileTileCache(); /** * @brief Set the limit of the volatile (in RAM) tile cache. * @param bytes The limit in kilobytes. */ void setVolatileTileCacheLimit( quint64 kiloBytes ); void setDefaultAngleUnit( AngleUnit angleUnit ); void setDefaultFont( const QFont& font ); /** * @brief Reload the currently displayed map by reloading texture tiles * from the Internet. In the future this should be extended to all * kinds of data which is used in the map. */ void reload(); void downloadRegion( QVector const & ); void highlightRouteRelation(qint64 osmId, bool enabled); Q_SIGNALS: void tileLevelChanged( int level ); /** * @brief Signal that the theme has changed * @param theme Name of the new theme. */ void themeChanged( const QString& theme ); void projectionChanged( Projection ); void radiusChanged( int radius ); void mouseMoveGeoPosition( const QString& ); void mouseClickGeoPosition( qreal lon, qreal lat, GeoDataCoordinates::Unit ); void framesPerSecond( qreal fps ); /** * This signal is emitted when the repaint of the view was requested. * If available with the @p dirtyRegion which is the region the view will change in. * If dirtyRegion.isEmpty() returns true, the whole viewport has to be repainted. */ void repaintNeeded( const QRegion& dirtyRegion = QRegion() ); /** * This signal is emitted when the visible region of the map changes. This typically happens * when the user moves the map around or zooms. */ void visibleLatLonAltBoxChanged( const GeoDataLatLonAltBox& visibleLatLonAltBox ); /** * @brief This signal is emit when the settings of a plugin changed. */ void pluginSettingsChanged(); /** * @brief Signal that a render item has been initialized */ void renderPluginInitialized( RenderPlugin *renderPlugin ); /** * @brief Emitted when the layer rendering status has changed * @param status New render status */ void renderStatusChanged( RenderStatus status ); void renderStateChanged( const RenderState &state ); void highlightedPlacemarksChanged( qreal, qreal, GeoDataCoordinates::Unit ); void viewContextChanged(ViewContext viewContext); - void showPublicTransportChanged(bool showPublicTransport); + void visibleRelationTypesChanged(GeoDataRelation::RelationTypes relationTypes); protected: /** * @brief Enables custom drawing onto the MarbleMap straight after * @brief the globe and before all other layers have been rendered. * @param painter * * @deprecated implement LayerInterface and add it using @p addLayer() */ virtual void customPaint( GeoPainter *painter ); private: Q_PRIVATE_SLOT( d, void updateMapTheme() ) Q_PRIVATE_SLOT( d, void updateProperty( const QString &, bool ) ) Q_PRIVATE_SLOT( d, void setDocument(QString) ) Q_PRIVATE_SLOT( d, void updateTileLevel() ) Q_PRIVATE_SLOT(d, void addPlugins()) private: Q_DISABLE_COPY( MarbleMap ) MarbleMapPrivate * const d; friend class MarbleMapPrivate; class CustomPaintLayer; friend class CustomPaintLayer; }; } #endif diff --git a/src/lib/marble/declarative/MarbleQuickItem.cpp b/src/lib/marble/declarative/MarbleQuickItem.cpp index dfcf38a0b..7369a9e68 100644 --- a/src/lib/marble/declarative/MarbleQuickItem.cpp +++ b/src/lib/marble/declarative/MarbleQuickItem.cpp @@ -1,1012 +1,1096 @@ // // This file is part of the Marble Virtual Globe. // // This program is free software licensed under the GNU LGPL. You can // find a copy of this license in LICENSE.txt in the top directory of // the source code. // // Copyright 2014 Adam Dabrowski // #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "GeoDataRelation.h" #include "osm/OsmPlacemarkData.h" #include "GeoDataDocument.h" namespace Marble { //TODO - move to separate files class QuickItemSelectionRubber : public AbstractSelectionRubber { //TODO: support rubber selection in MarbleQuickItem public: QuickItemSelectionRubber(); void show() override { m_visible = true; } void hide() override { m_visible = false; } bool isVisible() const override { return m_visible; } const QRect &geometry() const override { return m_geometry; } void setGeometry(const QRect &/*geometry*/) override {} private: QRect m_geometry; bool m_visible; }; //TODO - implement missing functionalities class MarbleQuickInputHandler : public MarbleDefaultInputHandler { public: MarbleQuickInputHandler(MarbleAbstractPresenter *marblePresenter, MarbleQuickItem *marbleQuick) : MarbleDefaultInputHandler(marblePresenter) ,m_marbleQuick(marbleQuick) { setInertialEarthRotationEnabled(false); //Disabled by default, it's buggy. TODO - fix } bool acceptMouse() override { return true; } void pinch(QPointF center, qreal scale, Qt::GestureState state) { //TODO - this whole thing should be moved to MarbleAbstractPresenter (void)handlePinch(center, scale, state); } void handleMouseButtonPressAndHold(const QPoint &position) override { m_marbleQuick->reverseGeocoding(position); } private Q_SLOTS: void showLmbMenu(int x, int y) override { m_marbleQuick->selectPlacemarkAt(x, y); } void showRmbMenu(int, int) override {} void openItemToolTip() override {} void setCursor(const QCursor &cursor) override { m_marbleQuick->setCursor(cursor); } private Q_SLOTS: void installPluginEventFilter(RenderPlugin *) override {} private: bool layersEventFilter(QObject *o, QEvent *e) override { return m_marbleQuick->layersEventFilter(o, e); } //empty - don't check. It would be invalid with quick items void checkReleasedMove(QMouseEvent *) override {} bool handleTouch(QTouchEvent *event) override { if (event->touchPoints().count() > 1) { //not handling multi-touch at all, let PinchArea or MultiPointTouchArea take care of it return false; } if (event->touchPoints().count() == 1) { //handle - but do not accept. I.e. pinchArea still needs to get this QTouchEvent::TouchPoint p = event->touchPoints().at(0); if (event->type() == QEvent::TouchBegin) { QMouseEvent press(QMouseEvent::MouseButtonPress, p.pos(), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); handleMouseEvent(&press); } else if (event->type() == QEvent::TouchUpdate) { QMouseEvent move(QMouseEvent::MouseMove, p.pos(), Qt::NoButton, Qt::LeftButton, Qt::NoModifier); handleMouseEvent(&move); } else if (event->type() == QEvent::TouchEnd) { QMouseEvent release(QMouseEvent::MouseButtonRelease, p.pos(), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); handleMouseEvent(&release); } } return false; } AbstractSelectionRubber *selectionRubber() override { return &m_selectionRubber; } MarbleQuickItem *m_marbleQuick; QuickItemSelectionRubber m_selectionRubber; bool m_usePinchArea; }; class MarbleQuickItemPrivate { public: explicit MarbleQuickItemPrivate(MarbleQuickItem *marble) : m_marble(marble), m_model(), m_map(&m_model), m_presenter(&m_map), m_positionVisible(false), m_currentPosition(marble), m_inputHandler(&m_presenter, marble), m_placemarkDelegate(nullptr), m_placemarkItem(nullptr), m_placemark(nullptr), m_reverseGeocoding(&m_model), - m_showScaleBar(false) + m_showScaleBar(false), + m_enabledRelationTypes(GeoDataRelation::RouteFerry | + GeoDataRelation::RouteTrain | + GeoDataRelation::RouteSubway | + GeoDataRelation::RouteTram | + GeoDataRelation::RouteBus | + GeoDataRelation::RouteTrolleyBus), + m_showPublicTransport(false) { m_currentPosition.setName(QObject::tr("Current Location")); + m_relationTypeConverter["road"] = GeoDataRelation::RouteRoad; + m_relationTypeConverter["detour"] = GeoDataRelation::RouteDetour; + m_relationTypeConverter["ferry"] = GeoDataRelation::RouteFerry; + m_relationTypeConverter["train"] = GeoDataRelation::RouteTrain; + m_relationTypeConverter["subway"] = GeoDataRelation::RouteSubway; + m_relationTypeConverter["tram"] = GeoDataRelation::RouteTram; + m_relationTypeConverter["bus"] = GeoDataRelation::RouteBus; + m_relationTypeConverter["trolley-bus"] = GeoDataRelation::RouteTrolleyBus; + m_relationTypeConverter["bicycle"] = GeoDataRelation::RouteBicycle; + m_relationTypeConverter["mountainbike"] = GeoDataRelation::RouteMountainbike; + m_relationTypeConverter["foot"] = GeoDataRelation::RouteFoot; + m_relationTypeConverter["hiking"] = GeoDataRelation::RouteHiking; + m_relationTypeConverter["horse"] = GeoDataRelation::RouteHorse; + m_relationTypeConverter["inline-skates"] = GeoDataRelation::RouteInlineSkates; + m_relationTypeConverter["downhill"] = GeoDataRelation::RouteSkiDownhill; + m_relationTypeConverter["ski-nordic"] = GeoDataRelation::RouteSkiNordic; + m_relationTypeConverter["skitour"] = GeoDataRelation::RouteSkitour; + m_relationTypeConverter["sled"] = GeoDataRelation::RouteSled; } + void updateVisibleRoutes(); + private: MarbleQuickItem *m_marble; friend class MarbleQuickItem; MarbleModel m_model; MarbleMap m_map; MarbleAbstractPresenter m_presenter; bool m_positionVisible; Placemark m_currentPosition; MarbleQuickInputHandler m_inputHandler; QQmlComponent* m_placemarkDelegate; QQuickItem* m_placemarkItem; Placemark* m_placemark; ReverseGeocodingRunnerManager m_reverseGeocoding; bool m_showScaleBar; + QMap m_relationTypeConverter; + GeoDataRelation::RelationTypes m_enabledRelationTypes; + bool m_showPublicTransport; }; MarbleQuickItem::MarbleQuickItem(QQuickItem *parent) : QQuickPaintedItem(parent) ,d(new MarbleQuickItemPrivate(this)) { setRenderTarget(QQuickPaintedItem::FramebufferObject); setOpaquePainting(true); qRegisterMetaType("Placemark*"); for (AbstractFloatItem *item: d->m_map.floatItems()) { if (item->nameId() == QLatin1String("license")) { item->setPosition(QPointF(5.0, -10.0)); } else { item->hide(); } } d->m_model.positionTracking()->setTrackVisible(false); connect(&d->m_map, SIGNAL(repaintNeeded(QRegion)), this, SLOT(update())); connect(this, &MarbleQuickItem::widthChanged, this, &MarbleQuickItem::resizeMap); connect(this, &MarbleQuickItem::heightChanged, this, &MarbleQuickItem::resizeMap); connect(&d->m_map, &MarbleMap::visibleLatLonAltBoxChanged, this, &MarbleQuickItem::updatePositionVisibility); connect(&d->m_map, &MarbleMap::visibleLatLonAltBoxChanged, this, &MarbleQuickItem::visibleLatLonAltBoxChanged); connect(&d->m_map, &MarbleMap::radiusChanged, this, &MarbleQuickItem::radiusChanged); connect(&d->m_map, &MarbleMap::radiusChanged, this, &MarbleQuickItem::zoomChanged); - connect(&d->m_map, &MarbleMap::showPublicTransportChanged, this, &MarbleQuickItem::showPublicTransportChanged); connect(&d->m_reverseGeocoding, SIGNAL(reverseGeocodingFinished(GeoDataCoordinates,GeoDataPlacemark)), this, SLOT(handleReverseGeocoding(GeoDataCoordinates,GeoDataPlacemark))); setAcceptedMouseButtons(Qt::AllButtons); installEventFilter(&d->m_inputHandler); } void MarbleQuickItem::resizeMap() { d->m_map.setSize(qMax(100, int(width())), qMax(100, int(height()))); update(); updatePositionVisibility(); } void MarbleQuickItem::positionDataStatusChanged(PositionProviderStatus status) { bool const positionAvailable = status == PositionProviderStatusAvailable; emit positionAvailableChanged(positionAvailable); updatePositionVisibility(); } void MarbleQuickItem::positionChanged(const GeoDataCoordinates &, GeoDataAccuracy) { updatePositionVisibility(); } void MarbleQuickItem::updatePositionVisibility() { updatePlacemarks(); bool isVisible = false; if ( positionAvailable() ) { qreal x, y; bool globeHidesPoint; bool const valid = d->m_map.viewport()->screenCoordinates(d->m_model.positionTracking()->currentLocation(), x, y, globeHidesPoint); isVisible = valid && !globeHidesPoint; } if ( isVisible != d->m_positionVisible ) { d->m_positionVisible = isVisible; emit positionVisibleChanged( isVisible ); } } void MarbleQuickItem::updateCurrentPosition(const GeoDataCoordinates &coordinates) { d->m_currentPosition.placemark().setCoordinate(coordinates); emit currentPositionChanged(&d->m_currentPosition); } void MarbleQuickItem::updatePlacemarks() { if (!d->m_placemarkDelegate || !d->m_placemark) { return; } if (!d->m_placemarkItem) { QQmlContext * context = new QQmlContext(qmlContext(d->m_placemarkDelegate)); QObject * component = d->m_placemarkDelegate->create(context); d->m_placemarkItem = qobject_cast( component ); if (d->m_placemarkItem) { d->m_placemarkItem->setParentItem( this ); d->m_placemarkItem->setProperty("placemark", QVariant::fromValue(d->m_placemark)); } else { delete component; return; } } qreal x = 0; qreal y = 0; const bool visible = d->m_map.viewport()->screenCoordinates(d->m_placemark->placemark().coordinate(), x, y); d->m_placemarkItem->setVisible(visible); if (visible) { d->m_placemarkItem->setProperty("xPos", QVariant(x)); d->m_placemarkItem->setProperty("yPos", QVariant(y)); } } void MarbleQuickItem::handleReverseGeocoding(const GeoDataCoordinates &coordinates, const GeoDataPlacemark &placemark) { if (d->m_placemark && d->m_placemark->placemark().coordinate() == coordinates) { d->m_placemark->setGeoDataPlacemark(placemark); updatePlacemarks(); } } void MarbleQuickItem::paint(QPainter *painter) { //TODO - much to be done here still, i.e paint !enabled version QPaintDevice *paintDevice = painter->device(); QRect rect = contentsBoundingRect().toRect(); painter->end(); { GeoPainter geoPainter(paintDevice, d->m_map.viewport(), d->m_map.mapQuality()); d->m_map.paint(geoPainter, rect); } painter->begin(paintDevice); } void MarbleQuickItem::classBegin() { } void MarbleQuickItem::componentComplete() { } int MarbleQuickItem::mapWidth() const { return d->m_map.width(); } int MarbleQuickItem::mapHeight() const { return d->m_map.height(); } bool MarbleQuickItem::showFrameRate() const { return d->m_map.showFrameRate(); } MarbleQuickItem::Projection MarbleQuickItem::projection() const { return Projection(d->m_map.projection()); } QString MarbleQuickItem::mapThemeId() const { return d->m_map.mapThemeId(); } bool MarbleQuickItem::showAtmosphere() const { return d->m_map.showAtmosphere(); } bool MarbleQuickItem::showCompass() const { return d->m_map.showCompass(); } bool MarbleQuickItem::showClouds() const { return d->m_map.showClouds(); } bool MarbleQuickItem::showCrosshairs() const { return d->m_map.showCrosshairs(); } bool MarbleQuickItem::showGrid() const { return d->m_map.showGrid(); } bool MarbleQuickItem::showOverviewMap() const { return d->m_map.showOverviewMap(); } bool MarbleQuickItem::showOtherPlaces() const { return d->m_map.showOtherPlaces(); } bool MarbleQuickItem::showScaleBar() const { return d->m_showScaleBar; } bool MarbleQuickItem::showBackground() const { return d->m_map.showBackground(); } bool MarbleQuickItem::showPositionMarker() const { QList plugins = d->m_map.renderPlugins(); for (const RenderPlugin * plugin: plugins) { if (plugin->nameId() == QLatin1String("positionMarker")) { return plugin->visible(); } } return false; } bool MarbleQuickItem::showPublicTransport() const { - return d->m_map.showPublicTransport(); + return d->m_showPublicTransport; } QString MarbleQuickItem::positionProvider() const { if ( d->m_model.positionTracking()->positionProviderPlugin() ) { return d->m_model.positionTracking()->positionProviderPlugin()->nameId(); } return QString(); } MarbleModel* MarbleQuickItem::model() { return &d->m_model; } const MarbleModel* MarbleQuickItem::model() const { return &d->m_model; } MarbleMap* MarbleQuickItem::map() { return &d->m_map; } const MarbleMap* MarbleQuickItem::map() const { return &d->m_map; } bool MarbleQuickItem::inertialGlobeRotation() const { return d->m_inputHandler.inertialEarthRotationEnabled(); } bool MarbleQuickItem::animationViewContext() const { return d->m_map.viewContext() == Animation; } QQmlComponent *MarbleQuickItem::placemarkDelegate() const { return d->m_placemarkDelegate; } void MarbleQuickItem::reverseGeocoding(const QPoint &point) { qreal lon, lat; d->m_map.viewport()->geoCoordinates(point.x(), point.y(), lon, lat); auto const coordinates = GeoDataCoordinates(lon, lat, 0.0, GeoDataCoordinates::Degree); delete d->m_placemarkItem; d->m_placemarkItem = nullptr; delete d->m_placemark; d->m_placemark = new Placemark(this); d->m_placemark->placemark().setCoordinate(coordinates); d->m_reverseGeocoding.reverseGeocoding(coordinates); } qreal MarbleQuickItem::speed() const { return d->m_model.positionTracking()->speed(); } qreal MarbleQuickItem::angle() const { bool routeExists = d->m_model.routingManager()->routingModel()->route().distance() != 0.0; bool onRoute = !d->m_model.routingManager()->routingModel()->deviatedFromRoute(); if ( routeExists && onRoute) { GeoDataCoordinates curPoint = d->m_model.positionTracking()->positionProviderPlugin()->position(); return d->m_model.routingManager()->routingModel()->route().currentSegment().projectedDirection(curPoint); } else { return d->m_model.positionTracking()->direction(); } } bool MarbleQuickItem::positionAvailable() const { return d->m_model.positionTracking()->status() == PositionProviderStatusAvailable; } bool MarbleQuickItem::positionVisible() { return d->m_positionVisible; } qreal MarbleQuickItem::distanceFromPointToCurrentLocation(const QPoint & position) const { if ( positionAvailable() ) { qreal lon1; qreal lat1; d->m_map.viewport()->geoCoordinates(position.x(), position.y(), lon1, lat1, GeoDataCoordinates::Radian ); GeoDataCoordinates currentCoordinates = d->m_model.positionTracking()->currentLocation(); qreal lon2 = currentCoordinates.longitude(); qreal lat2 = currentCoordinates.latitude(); return distanceSphere(lon1, lat1, lon2, lat2) * d->m_model.planetRadius(); } return 0; } qreal MarbleQuickItem::angleFromPointToCurrentLocation( const QPoint & position ) const { if ( positionAvailable() ) { qreal x, y; PositionTracking const * positionTracking = d->m_model.positionTracking(); map()->viewport()->screenCoordinates( positionTracking->currentLocation(), x, y ); return atan2( y-position.y(), x-position.x() ) * RAD2DEG; } return 0; } Placemark * MarbleQuickItem::currentPosition() const { return &d->m_currentPosition; } QPointF MarbleQuickItem::screenCoordinatesFromCoordinate(Coordinate * coordinate) const { qreal x; qreal y; d->m_map.viewport()->screenCoordinates(coordinate->coordinates(), x, y); return QPointF(x, y); } void MarbleQuickItem::setRadius(int radius) { d->m_map.setRadius(radius); } void MarbleQuickItem::setZoom(int newZoom, FlyToMode mode) { d->m_presenter.setZoom(newZoom, mode); } void MarbleQuickItem::setZoomToMaximumLevel() { d->m_presenter.setZoom(d->m_map.maximumZoom()); } void MarbleQuickItem::centerOn(const GeoDataPlacemark& placemark, bool animated) { d->m_presenter.centerOn(placemark, animated); } void MarbleQuickItem::centerOn(const GeoDataLatLonBox& box, bool animated) { d->m_presenter.centerOn(box, animated); } void MarbleQuickItem::centerOn(const GeoDataCoordinates &coordinate) { GeoDataLookAt target = d->m_presenter.lookAt(); target.setCoordinates(coordinate); d->m_presenter.flyTo(target, Automatic); } void MarbleQuickItem::centerOn(qreal longitude, qreal latitude) { d->m_presenter.centerOn(longitude, latitude); } void MarbleQuickItem::centerOnCoordinates(qreal longitude, qreal latitude) { centerOn(longitude, latitude); } void MarbleQuickItem::centerOnCurrentPosition() { GeoDataCoordinates coordinates = d->m_model.positionTracking()->currentLocation(); if ( coordinates == GeoDataCoordinates() ) { return; } d->m_presenter.centerOn(coordinates, true); if (d->m_presenter.zoom() < 3000) { d->m_presenter.setZoom(3500); } } void MarbleQuickItem::selectPlacemarkAt(int x, int y) { auto features = d->m_map.whichFeatureAt(QPoint(x, y)); QVector placemarks; for(auto feature: features) { if (const auto placemark = geodata_cast(feature)) { placemarks << placemark; } } for(auto placemark: placemarks) { if (d->m_placemark && placemark->coordinate() == d->m_placemark->placemark().coordinate()) { d->m_placemark->deleteLater(); d->m_placemark = nullptr; } else { if (d->m_placemark) { d->m_placemark->deleteLater(); } d->m_placemark = new Placemark(this); d->m_placemark->setGeoDataPlacemark(*placemark); } delete d->m_placemarkItem; d->m_placemarkItem = nullptr; updatePlacemarks(); return; } if (d->m_placemark) { d->m_placemark->deleteLater(); d->m_placemark = nullptr; delete d->m_placemarkItem; d->m_placemarkItem = nullptr; updatePlacemarks(); } } void MarbleQuickItem::goHome() { d->m_presenter.goHome(); } void MarbleQuickItem::zoomIn(FlyToMode mode) { d->m_presenter.zoomIn(mode); } void MarbleQuickItem::zoomOut(FlyToMode mode) { d->m_presenter.zoomOut(mode); } void MarbleQuickItem::handlePinchStarted(const QPointF &point) { pinch(point, 1, Qt::GestureStarted); } void MarbleQuickItem::handlePinchFinished(const QPointF &point) { pinch(point, 1, Qt::GestureFinished); } void MarbleQuickItem::handlePinchUpdated(const QPointF &point, qreal scale) { scale = sqrt(sqrt(scale)); scale = qBound(static_cast(0.5), scale, static_cast(2.0)); pinch(point, scale, Qt::GestureUpdated); } void MarbleQuickItem::setMapWidth(int mapWidth) { if (d->m_map.width() == mapWidth) { return; } d->m_map.setSize(mapWidth, mapHeight()); emit mapWidthChanged(mapWidth); } void MarbleQuickItem::setMapHeight(int mapHeight) { if (this->mapHeight() == mapHeight) { return; } d->m_map.setSize(mapWidth(), mapHeight); emit mapHeightChanged(mapHeight); } void MarbleQuickItem::setShowFrameRate(bool showFrameRate) { if (this->showFrameRate() == showFrameRate) { return; } d->m_map.setShowFrameRate(showFrameRate); emit showFrameRateChanged(showFrameRate); } void MarbleQuickItem::setProjection(Projection projection) { if (this->projection() == projection) { return; } d->m_map.setProjection(Marble::Projection(projection)); emit projectionChanged(projection); } void MarbleQuickItem::setMapThemeId(const QString& mapThemeId) { if (this->mapThemeId() == mapThemeId) { return; } bool const showCompass = d->m_map.showCompass(); bool const showOverviewMap = d->m_map.showOverviewMap(); bool const showOtherPlaces = d->m_map.showOtherPlaces(); bool const showGrid = d->m_map.showGrid(); d->m_map.setMapThemeId(mapThemeId); // Map themes are allowed to change properties. Enforce ours. d->m_map.setShowCompass(showCompass); d->m_map.setShowOverviewMap(showOverviewMap); d->m_map.setShowOtherPlaces(showOtherPlaces); d->m_map.setShowGrid(showGrid); d->m_map.setShowScaleBar(d->m_showScaleBar); emit mapThemeIdChanged(mapThemeId); } void MarbleQuickItem::setShowAtmosphere(bool showAtmosphere) { if (this->showAtmosphere() == showAtmosphere) { return; } d->m_map.setShowAtmosphere(showAtmosphere); emit showAtmosphereChanged(showAtmosphere); } void MarbleQuickItem::setShowCompass(bool showCompass) { if (this->showCompass() == showCompass) { return; } d->m_map.setShowCompass(showCompass); emit showCompassChanged(showCompass); } void MarbleQuickItem::setShowClouds(bool showClouds) { if (this->showClouds() == showClouds) { return; } d->m_map.setShowClouds(showClouds); emit showCloudsChanged(showClouds); } void MarbleQuickItem::setShowCrosshairs(bool showCrosshairs) { if (this->showCrosshairs() == showCrosshairs) { return; } d->m_map.setShowCrosshairs(showCrosshairs); emit showCrosshairsChanged(showCrosshairs); } void MarbleQuickItem::setShowGrid(bool showGrid) { if (this->showGrid() == showGrid) { return; } d->m_map.setShowGrid(showGrid); emit showGridChanged(showGrid); } void MarbleQuickItem::setShowOverviewMap(bool showOverviewMap) { if (this->showOverviewMap() == showOverviewMap) { return; } d->m_map.setShowOverviewMap(showOverviewMap); emit showOverviewMapChanged(showOverviewMap); } void MarbleQuickItem::setShowOtherPlaces(bool showOtherPlaces) { if (this->showOtherPlaces() == showOtherPlaces) { return; } d->m_map.setShowOtherPlaces(showOtherPlaces); emit showOtherPlacesChanged(showOtherPlaces); } void MarbleQuickItem::setShowScaleBar(bool showScaleBar) { if (d->m_showScaleBar == showScaleBar) { return; } d->m_showScaleBar = showScaleBar; d->m_map.setShowScaleBar(d->m_showScaleBar); emit showScaleBarChanged(showScaleBar); } void MarbleQuickItem::setShowBackground(bool showBackground) { if (this->showBackground() == showBackground) { return; } d->m_map.setShowBackground(showBackground); emit showBackgroundChanged(showBackground); } void MarbleQuickItem::setShowPositionMarker(bool showPositionMarker) { if (this->showPositionMarker() == showPositionMarker) { return; } QList plugins = d->m_map.renderPlugins(); for ( RenderPlugin * plugin: plugins ) { if (plugin->nameId() == QLatin1String("positionMarker")) { plugin->setVisible(showPositionMarker); break; } } emit showPositionMarkerChanged(showPositionMarker); } void MarbleQuickItem::setShowPublicTransport(bool enabled) { - d->m_map.setShowPublicTransport(enabled); + if (d->m_showPublicTransport != enabled) { + d->m_showPublicTransport = enabled; + d->updateVisibleRoutes(); + emit showPublicTransportChanged(enabled); + } } void MarbleQuickItem::setPositionProvider(const QString &positionProvider) { QString name; if ( d->m_model.positionTracking()->positionProviderPlugin() ) { name = d->m_model.positionTracking()->positionProviderPlugin()->nameId(); if ( name == positionProvider ) { return; } } if ( positionProvider.isEmpty() ) { d->m_model.positionTracking()->setPositionProviderPlugin( nullptr ); return; } QList plugins = d->m_model.pluginManager()->positionProviderPlugins(); for (const PositionProviderPlugin* plugin: plugins) { if ( plugin->nameId() == positionProvider) { PositionProviderPlugin * newPlugin = plugin->newInstance(); d->m_model.positionTracking()->setPositionProviderPlugin(newPlugin); connect(newPlugin, SIGNAL(statusChanged(PositionProviderStatus)), this, SLOT(positionDataStatusChanged(PositionProviderStatus))); connect(newPlugin, SIGNAL(positionChanged(GeoDataCoordinates,GeoDataAccuracy)), this, SLOT(updateCurrentPosition(GeoDataCoordinates))); connect(newPlugin, SIGNAL(positionChanged(GeoDataCoordinates,GeoDataAccuracy)), this, SIGNAL(speedChanged())); connect(newPlugin, SIGNAL(positionChanged(GeoDataCoordinates,GeoDataAccuracy)), this, SIGNAL(angleChanged())); emit positionProviderChanged(positionProvider); break; } } } void MarbleQuickItem::setInertialGlobeRotation(bool inertialGlobeRotation) { if (inertialGlobeRotation == d->m_inputHandler.inertialEarthRotationEnabled()) { return; } d->m_inputHandler.setInertialEarthRotationEnabled(inertialGlobeRotation); emit inertialGlobeRotationChanged(inertialGlobeRotation); } void MarbleQuickItem::setAnimationViewContext(bool animationViewContext) { d->m_map.setViewContext(animationViewContext ? Animation : Still ); emit inertialGlobeRotationChanged(animationViewContext); } void MarbleQuickItem::setPluginSetting(const QString &pluginId, const QString &key, const QString &value) { for (RenderPlugin* plugin: d->m_map.renderPlugins()) { if (plugin->nameId() == pluginId) { plugin->setSetting(key, value); } } } void MarbleQuickItem::setPropertyEnabled(const QString &property, bool enabled) { d->m_map.setPropertyValue(property, enabled); } bool MarbleQuickItem::isPropertyEnabled(const QString &property) const { return d->m_map.propertyValue(property); } void MarbleQuickItem::setShowRuntimeTrace(bool showRuntimeTrace) { d->m_map.setShowRuntimeTrace(showRuntimeTrace); update(); } void MarbleQuickItem::setShowDebugPolygons(bool showDebugPolygons) { d->m_map.setShowDebugPolygons(showDebugPolygons); update(); } void MarbleQuickItem::setShowDebugPlacemarks(bool showDebugPlacemarks) { d->m_map.setShowDebugPlacemarks(showDebugPlacemarks); update(); } void MarbleQuickItem::setShowDebugBatches(bool showDebugBatches) { d->m_map.setShowDebugBatchRender(showDebugBatches); update(); } void MarbleQuickItem::setPlacemarkDelegate(QQmlComponent *placemarkDelegate) { if (d->m_placemarkDelegate == placemarkDelegate) { return; } delete d->m_placemarkItem; d->m_placemarkItem = nullptr; d->m_placemarkDelegate = placemarkDelegate; emit placemarkDelegateChanged(placemarkDelegate); } void MarbleQuickItem::loadSettings() { QSettings settings; settings.beginGroup(QStringLiteral("MarbleQuickItem")); double lon = settings.value(QStringLiteral("centerLon"), QVariant(0.0)).toDouble(); double lat = settings.value(QStringLiteral("centerLat"), QVariant(0.0)).toDouble(); if (lat == 0.0 && lon == 0.0) { centerOnCurrentPosition(); } else { centerOn(lon, lat); } int const zoom = settings.value(QStringLiteral("zoom"), QVariant(0)).toInt(); if (zoom > 0) { setZoom(zoom); } + auto const defaultRelationTypes = QStringList() << "ferry" << "train" << "subway" << "tram" << "bus" << "trolley-bus"; + auto const visibleRelationTypes = settings.value(QStringLiteral("visibleRelationTypes"), defaultRelationTypes).toStringList(); + d->m_enabledRelationTypes = GeoDataRelation::UnknownType; + for (auto const &route: visibleRelationTypes) { + d->m_enabledRelationTypes |= d->m_relationTypeConverter.value(route, GeoDataRelation::UnknownType); + } + setShowPublicTransport(settings.value(QStringLiteral("showPublicTransport"), false).toBool()); settings.endGroup(); d->m_model.routingManager()->readSettings(); d->m_model.bookmarkManager()->loadFile(QStringLiteral("bookmarks/bookmarks.kml")); d->m_model.bookmarkManager()->setShowBookmarks(true); + d->updateVisibleRoutes(); } void MarbleQuickItem::writeSettings() { QSettings settings; settings.beginGroup(QStringLiteral("MarbleQuickItem")); settings.setValue(QStringLiteral("centerLon"), QVariant(d->m_map.centerLongitude())); settings.setValue(QStringLiteral("centerLat"), QVariant(d->m_map.centerLatitude())); settings.setValue(QStringLiteral("zoom"), QVariant(zoom())); + QStringList enabledRoutes; + QMap relationConverter; + for (auto iter = d->m_relationTypeConverter.cbegin(), end = d->m_relationTypeConverter.cend(); iter != end; ++iter) { + relationConverter[iter.value()] = iter.key(); + } + for (auto iter = relationConverter.cbegin(), end = relationConverter.cend(); iter != end; ++iter) { + if (d->m_enabledRelationTypes & iter.key()) { + enabledRoutes << iter.value(); + } + } + settings.setValue(QStringLiteral("visibleRelationTypes"), enabledRoutes); + settings.setValue(QStringLiteral("showPublicTransport"), d->m_showPublicTransport); + settings.endGroup(); d->m_model.routingManager()->writeSettings(); } void MarbleQuickItem::reloadTiles() { d->m_map.reload(); } void MarbleQuickItem::highlightRouteRelation(qint64 osmId, bool enabled) { d->m_map.highlightRouteRelation(osmId, enabled); } + void MarbleQuickItem::setRelationTypeVisible(const QString &relationType, bool visible) + { + auto const relation = d->m_relationTypeConverter.value(relationType, GeoDataRelation::UnknownType); + if (visible) { + d->m_enabledRelationTypes |= relation; + } else { + d->m_enabledRelationTypes &= ~relation; + } + d->updateVisibleRoutes(); + } + + bool MarbleQuickItem::isRelationTypeVisible(const QString &relationType) const + { + auto const relation = d->m_relationTypeConverter.value(relationType, GeoDataRelation::UnknownType); + return d->m_enabledRelationTypes & relation; + } + QObject *MarbleQuickItem::getEventFilter() const { //We would want to install the same event filter for abstract layer QuickItems such as PinchArea return &d->m_inputHandler; } void MarbleQuickItem::pinch(const QPointF& center, qreal scale, Qt::GestureState state) { d->m_inputHandler.pinch(center, scale, state); } MarbleInputHandler *MarbleQuickItem::inputHandler() { return &d->m_inputHandler; } int MarbleQuickItem::radius() const { return d->m_map.radius(); } int MarbleQuickItem::zoom() const { return d->m_presenter.logzoom(); } bool MarbleQuickItem::layersEventFilter(QObject *, QEvent *) { //Does nothing, but can be reimplemented in a subclass return false; } QuickItemSelectionRubber::QuickItemSelectionRubber() : m_visible(false) { // nothing to do } + void MarbleQuickItemPrivate::updateVisibleRoutes() + { + GeoDataRelation::RelationTypes relationTypes = m_enabledRelationTypes; + if (!m_showPublicTransport) { + relationTypes &= ~GeoDataRelation::RouteTrain; + relationTypes &= ~GeoDataRelation::RouteSubway; + relationTypes &= ~GeoDataRelation::RouteTram; + relationTypes &= ~GeoDataRelation::RouteBus; + relationTypes &= ~GeoDataRelation::RouteTrolleyBus; + } + m_map.setVisibleRelationTypes(relationTypes); + } + } diff --git a/src/lib/marble/declarative/MarbleQuickItem.h b/src/lib/marble/declarative/MarbleQuickItem.h index 98fab1713..1deb79050 100644 --- a/src/lib/marble/declarative/MarbleQuickItem.h +++ b/src/lib/marble/declarative/MarbleQuickItem.h @@ -1,245 +1,247 @@ // // This file is part of the Marble Virtual Globe. // // This program is free software licensed under the GNU LGPL. You can // find a copy of this license in LICENSE.txt in the top directory of // the source code. // // Copyright 2014 Adam Dabrowski // #ifndef MARBLEQUICKITEM_H #define MARBLEQUICKITEM_H #include "marble_declarative_export.h" #include #include #include "GeoDataAccuracy.h" #include "MarbleGlobal.h" #include "PositionProviderPluginInterface.h" #include "MarbleMap.h" #include "Placemark.h" #include "Coordinate.h" namespace Marble { class GeoDataLatLonBox; class GeoDataPlacemark; class MarbleModel; class MarbleInputHandler; class MarbleQuickItemPrivate; //Class is still being developed class MARBLE_DECLARATIVE_EXPORT MarbleQuickItem : public QQuickPaintedItem { Q_OBJECT Q_ENUMS(Projection) Q_PROPERTY(int mapWidth READ mapWidth WRITE setMapWidth NOTIFY mapWidthChanged) Q_PROPERTY(int mapHeight READ mapHeight WRITE setMapHeight NOTIFY mapHeightChanged) Q_PROPERTY(int zoom READ zoom WRITE setZoom NOTIFY zoomChanged) Q_PROPERTY(int radius READ radius WRITE setRadius NOTIFY radiusChanged) Q_PROPERTY(bool showFrameRate READ showFrameRate WRITE setShowFrameRate NOTIFY showFrameRateChanged) Q_PROPERTY(Projection projection READ projection WRITE setProjection NOTIFY projectionChanged) Q_PROPERTY(QString mapThemeId READ mapThemeId WRITE setMapThemeId NOTIFY mapThemeIdChanged) Q_PROPERTY(bool showAtmosphere READ showAtmosphere WRITE setShowAtmosphere NOTIFY showAtmosphereChanged) Q_PROPERTY(bool showCompass READ showCompass WRITE setShowCompass NOTIFY showCompassChanged) Q_PROPERTY(bool showClouds READ showClouds WRITE setShowClouds NOTIFY showCloudsChanged) Q_PROPERTY(bool showCrosshairs READ showCrosshairs WRITE setShowCrosshairs NOTIFY showCrosshairsChanged) Q_PROPERTY(bool showGrid READ showGrid WRITE setShowGrid NOTIFY showGridChanged) Q_PROPERTY(bool showOverviewMap READ showOverviewMap WRITE setShowOverviewMap NOTIFY showOverviewMapChanged) Q_PROPERTY(bool showOtherPlaces READ showOtherPlaces WRITE setShowOtherPlaces NOTIFY showOtherPlacesChanged) Q_PROPERTY(bool showScaleBar READ showScaleBar WRITE setShowScaleBar NOTIFY showScaleBarChanged) Q_PROPERTY(bool showBackground READ showBackground WRITE setShowBackground NOTIFY showBackgroundChanged) Q_PROPERTY(bool showPositionMarker READ showPositionMarker WRITE setShowPositionMarker NOTIFY showPositionMarkerChanged) Q_PROPERTY(bool showPublicTransport READ showPublicTransport WRITE setShowPublicTransport NOTIFY showPublicTransportChanged) Q_PROPERTY(QString positionProvider READ positionProvider WRITE setPositionProvider NOTIFY positionProviderChanged) Q_PROPERTY(bool positionAvailable READ positionAvailable NOTIFY positionAvailableChanged) Q_PROPERTY(bool positionVisible READ positionVisible NOTIFY positionVisibleChanged) Q_PROPERTY(MarbleMap* marbleMap READ map NOTIFY marbleMapChanged) Q_PROPERTY(Placemark* currentPosition READ currentPosition NOTIFY currentPositionChanged) Q_PROPERTY(qreal speed READ speed NOTIFY speedChanged) Q_PROPERTY(qreal angle READ angle NOTIFY angleChanged) Q_PROPERTY(bool inertialGlobeRotation READ inertialGlobeRotation WRITE setInertialGlobeRotation NOTIFY inertialGlobeRotationChanged) Q_PROPERTY(bool animationViewContext READ animationViewContext WRITE setAnimationViewContext NOTIFY animationViewContextChanged) Q_PROPERTY(QQmlComponent* placemarkDelegate READ placemarkDelegate WRITE setPlacemarkDelegate NOTIFY placemarkDelegateChanged) public: explicit MarbleQuickItem(QQuickItem *parent = 0); enum Projection{ Spherical = Marble::Spherical, Equirectangular = Marble::Equirectangular, Mercator = Marble::Mercator, Gnomonic = Marble::Gnomonic, Stereographic = Marble::Stereographic, LambertAzimuthal = Marble::LambertAzimuthal, AzimuthalEquidistant = Marble::AzimuthalEquidistant, VerticalPerspective = Marble::VerticalPerspective }; MarbleInputHandler *inputHandler(); int zoom() const; int radius() const; public Q_SLOTS: void goHome(); void setZoom(int zoom, FlyToMode mode = Instant); Q_INVOKABLE void setZoomToMaximumLevel(); void setRadius(int radius); void centerOn(const GeoDataPlacemark& placemark, bool animated = false); void centerOn(const GeoDataLatLonBox& box, bool animated = false); void centerOn(const GeoDataCoordinates& coordinate); void centerOn(qreal longitude, qreal latitude); Q_INVOKABLE void centerOnCoordinates(qreal longitude, qreal latitude); Q_INVOKABLE void centerOnCurrentPosition(); Q_INVOKABLE void selectPlacemarkAt(int x, int y); void zoomIn(FlyToMode mode = Automatic); void zoomOut(FlyToMode mode = Automatic); Q_INVOKABLE void handlePinchStarted(const QPointF &point); Q_INVOKABLE void handlePinchFinished(const QPointF &point); Q_INVOKABLE void handlePinchUpdated(const QPointF &point, qreal scale); void setMapWidth(int mapWidth); void setMapHeight(int mapHeight); void setShowFrameRate(bool showFrameRate); void setProjection(Projection projection); void setMapThemeId(const QString& mapThemeId); void setShowAtmosphere(bool showAtmosphere); void setShowCompass(bool showCompass); void setShowClouds(bool showClouds); void setShowCrosshairs(bool showCrosshairs); void setShowGrid(bool showGrid); void setShowOverviewMap(bool showOverviewMap); void setShowOtherPlaces(bool showOtherPlaces); void setShowScaleBar(bool showScaleBar); void setShowBackground(bool showBackground); void setShowPositionMarker(bool showPositionMarker); void setShowPublicTransport(bool showPublicTransport); void setPositionProvider(const QString & positionProvider); void setInertialGlobeRotation(bool inertialGlobeRotation); void setAnimationViewContext(bool animationViewContext); void setPluginSetting(const QString &plugin, const QString &key, const QString &value); void setPropertyEnabled(const QString &property, bool enabled); bool isPropertyEnabled(const QString &property) const; Q_INVOKABLE void setShowRuntimeTrace(bool showRuntimeTrace); Q_INVOKABLE void setShowDebugPolygons(bool showDebugPolygons); Q_INVOKABLE void setShowDebugPlacemarks(bool showDebugPlacemarks); Q_INVOKABLE void setShowDebugBatches(bool showDebugBatches); void setPlacemarkDelegate(QQmlComponent* placemarkDelegate); Q_INVOKABLE void loadSettings(); Q_INVOKABLE void writeSettings(); Q_INVOKABLE void reloadTiles(); Q_INVOKABLE void highlightRouteRelation(qint64 osmId, bool enabled); + Q_INVOKABLE void setRelationTypeVisible(const QString &relationType, bool visible); + Q_INVOKABLE bool isRelationTypeVisible(const QString &relationType) const; public: void paint(QPainter *painter) override; // QQmlParserStatus interface public: void classBegin() override; void componentComplete() override; public: virtual bool layersEventFilter(QObject *o, QEvent *e); int mapWidth() const; int mapHeight() const; bool showFrameRate() const; Projection projection() const; QString mapThemeId() const; bool showAtmosphere() const; bool showCompass() const; bool showClouds() const; bool showCrosshairs() const; bool showGrid() const; bool showOverviewMap() const; bool showOtherPlaces() const; bool showScaleBar() const; bool showBackground() const; bool showPositionMarker() const; bool showPublicTransport() const; QString positionProvider() const; bool positionAvailable() const; bool positionVisible(); Q_INVOKABLE qreal distanceFromPointToCurrentLocation(const QPoint & position) const; Q_INVOKABLE qreal angleFromPointToCurrentLocation(const QPoint & position) const; Placemark* currentPosition() const; Q_INVOKABLE QPointF screenCoordinatesFromCoordinate(Coordinate * coordinate) const; qreal speed() const; qreal angle() const; MarbleModel* model(); const MarbleModel* model() const; MarbleMap* map(); const MarbleMap* map() const; bool inertialGlobeRotation() const; bool animationViewContext() const; QQmlComponent* placemarkDelegate() const; void reverseGeocoding(const QPoint &point); Q_SIGNALS: void mapWidthChanged(int mapWidth); void mapHeightChanged(int mapHeight); void showFrameRateChanged(bool showFrameRate); void projectionChanged(Projection projection); void mapThemeIdChanged(const QString& mapThemeId); void showAtmosphereChanged(bool showAtmosphere); void showCompassChanged(bool showCompass); void showCloudsChanged(bool showClouds); void showCrosshairsChanged(bool showCrosshairs); void showGridChanged(bool showGrid); void showOverviewMapChanged(bool showOverviewMap); void showOtherPlacesChanged(bool showOtherPlaces); void showScaleBarChanged(bool showScaleBar); void showBackgroundChanged(bool showBackground); void showPositionMarkerChanged(bool showPositionMarker); void showPublicTransportChanged(bool showPublicTransport); void positionProviderChanged(const QString & positionProvider); void positionAvailableChanged(bool positionAvailable); void positionVisibleChanged(bool positionVisible); void marbleMapChanged(); void visibleLatLonAltBoxChanged(); void currentPositionChanged(Placemark* currentPosition); void angleChanged(); void speedChanged(); void zoomChanged(); void radiusChanged(int radius); void inertialGlobeRotationChanged(bool inertialGlobeRotation); void animationViewContextChanged(bool animationViewContext); void placemarkDelegateChanged(QQmlComponent* placemarkDelegate); protected: QObject *getEventFilter() const; void pinch(const QPointF& center, qreal scale, Qt::GestureState state); private Q_SLOTS: void resizeMap(); void positionDataStatusChanged(PositionProviderStatus status); void positionChanged(const GeoDataCoordinates &, GeoDataAccuracy); void updatePositionVisibility(); void updateCurrentPosition(const GeoDataCoordinates & coordinates); void updatePlacemarks(); void handleReverseGeocoding(const GeoDataCoordinates &coordinates, const GeoDataPlacemark &placemark); private: typedef QSharedPointer MarbleQuickItemPrivatePtr; MarbleQuickItemPrivatePtr d; friend class MarbleQuickItemPrivate; }; } #endif // MARBLEQUICKITEM_H diff --git a/src/lib/marble/declarative/RouteRelationModel.cpp b/src/lib/marble/declarative/RouteRelationModel.cpp index 542b540d8..9047f7ba6 100644 --- a/src/lib/marble/declarative/RouteRelationModel.cpp +++ b/src/lib/marble/declarative/RouteRelationModel.cpp @@ -1,173 +1,173 @@ // // 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 Sergey Popov // #include "RouteRelationModel.h" #include "MarbleDirs.h" #include "osm/OsmPlacemarkData.h" #include "GeoDataColorStyle.h" namespace Marble { RouteRelationModel::RouteRelationModel(QObject *parent) : QAbstractListModel(parent) { m_networks[QStringLiteral("iwn")] = tr("International walking route"); m_networks[QStringLiteral("nwn")] = tr("National walking route"); m_networks[QStringLiteral("rwn")] = tr("Regional walking route"); m_networks[QStringLiteral("lwn")] = tr("Local walking route"); m_networks[QStringLiteral("icn")] = tr("International cycling route"); m_networks[QStringLiteral("ncn")] = tr("National cycling route"); m_networks[QStringLiteral("rcn")] = tr("Regional cycling route"); m_networks[QStringLiteral("lcn")] = tr("Local cycling route"); m_networks[QStringLiteral("US:TX:FM")] = tr("Farm to Market Road", "State or county road in Texas, USA"); m_networks[QStringLiteral("regional")] = tr("Regional route"); m_networks[QStringLiteral("national")] = tr("National route"); m_networks[QStringLiteral("municipal")] = tr("Municipal route"); m_networks[QStringLiteral("territorial")] = tr("Territorial route"); m_networks[QStringLiteral("local")] = tr("Local route"); m_networks[QStringLiteral("prefectural")] = tr("Prefectural route"); m_networks[QStringLiteral("US")] = tr("United States route"); } void RouteRelationModel::setRelations(const QSet &relations) { if (!m_relations.isEmpty()) { beginRemoveRows(QModelIndex(), 0, m_relations.count() - 1); m_relations.clear(); endRemoveRows(); } if (!relations.isEmpty()) { beginInsertRows(QModelIndex(), 0, relations.count() - 1); m_relations.reserve(relations.size()); for (auto relation: relations) { if (relation->relationType() >= GeoDataRelation::RouteRoad && relation->relationType() <= GeoDataRelation::RouteSled) { m_relations << new GeoDataRelation(*relation); } } std::sort(m_relations.begin(), m_relations.end(), [](const GeoDataRelation * a, const GeoDataRelation * b) { return *a < *b; }); endInsertRows(); } } int RouteRelationModel::rowCount(const QModelIndex & parent) const { return parent.isValid() ? 0 : m_relations.count(); } QVariant RouteRelationModel::data(const QModelIndex & index, int role) const { if (!index.isValid() || index.row() < 0 || index.row() >= m_relations.count()) { return QVariant(); } if (role == Qt::DisplayRole) { return m_relations.at(index.row())->name(); } else if (role == IconSource) { switch (m_relations.at(index.row())->relationType()) { - case GeoDataRelation::RouteRoad: return svgFile("material/maps/ic_directions_car_48px.svg"); - case GeoDataRelation::RouteDetour: return svgFile("material/maps/ic_directions_car_48px.svg"); - case GeoDataRelation::RouteFerry: return svgFile("material/maps/ic_directions_boat_48px.svg"); - case GeoDataRelation::RouteTrain: return svgFile("material/maps/ic_directions_railway_48px.svg"); - case GeoDataRelation::RouteSubway: return svgFile("material/maps/ic_directions_subway_48px.svg"); - case GeoDataRelation::RouteTram: return svgFile("material/maps/ic_tram_48px.svg"); - case GeoDataRelation::RouteBus: return svgFile("material/maps/ic_directions_bus_48px.svg"); - case GeoDataRelation::RouteTrolleyBus: return svgFile("material/maps/ic_directions_bus_48px.svg"); - case GeoDataRelation::RouteBicycle: return svgFile("material/maps/ic_directions_bike_48px.svg"); - case GeoDataRelation::RouteMountainbike: return svgFile("material/maps/ic_directions_bike_48px.svg"); - case GeoDataRelation::RouteFoot: return svgFile("material/maps/ic_directions_walk_48px.svg"); - case GeoDataRelation::RouteHiking: return svgFile("thenounproject/204712-hiker.svg"); - case GeoDataRelation::RouteHorse: return svgFile("thenounproject/78374-horse-riding.svg"); - case GeoDataRelation::RouteInlineSkates: return svgFile("thenounproject/101965-inline-skater.svg"); - case GeoDataRelation::RouteSkiDownhill: return svgFile("thenounproject/2412-skiing-downhill.svg"); - case GeoDataRelation::RouteSkiNordic: return svgFile("thenounproject/30231-skiing-cross-country.svg"); - case GeoDataRelation::RouteSkitour: return svgFile("thenounproject/29366-skitour.svg"); - case GeoDataRelation::RouteSled: return svgFile("thenounproject/365217-sled.svg"); + case GeoDataRelation::RouteRoad: return QStringLiteral("material/directions-car.svg"); + case GeoDataRelation::RouteDetour: return QStringLiteral("material/directions-car.svg"); + case GeoDataRelation::RouteFerry: return QStringLiteral("material/directions-boat.svg"); + case GeoDataRelation::RouteTrain: return QStringLiteral("material/directions-railway.svg"); + case GeoDataRelation::RouteSubway: return QStringLiteral("material/directions-subway.svg"); + case GeoDataRelation::RouteTram: return QStringLiteral("material/directions-tram.svg"); + case GeoDataRelation::RouteBus: return QStringLiteral("material/directions-bus.svg"); + case GeoDataRelation::RouteTrolleyBus: return QStringLiteral("material/directions-bus.svg"); + case GeoDataRelation::RouteBicycle: return QStringLiteral("material/directions-bike.svg"); + case GeoDataRelation::RouteMountainbike: return QStringLiteral("material/directions-bike.svg"); + case GeoDataRelation::RouteFoot: return QStringLiteral("material/directions-walk.svg"); + case GeoDataRelation::RouteHiking: return QStringLiteral("thenounproject/204712-hiker.svg"); + case GeoDataRelation::RouteHorse: return QStringLiteral("thenounproject/78374-horse-riding.svg"); + case GeoDataRelation::RouteInlineSkates: return QStringLiteral("thenounproject/101965-inline-skater.svg"); + case GeoDataRelation::RouteSkiDownhill: return QStringLiteral("thenounproject/2412-skiing-downhill.svg"); + case GeoDataRelation::RouteSkiNordic: return QStringLiteral("thenounproject/30231-skiing-cross-country.svg"); + case GeoDataRelation::RouteSkitour: return QStringLiteral("thenounproject/29366-skitour.svg"); + case GeoDataRelation::RouteSled: return QStringLiteral("thenounproject/365217-sled.svg"); case GeoDataRelation::UnknownType: return QVariant(QString()); } } else if (role == Description) { return m_relations.at(index.row())->osmData().tagValue(QStringLiteral("description")); } else if (role == Network) { auto const network = m_relations.at(index.row())->osmData().tagValue(QStringLiteral("network")); auto iter = m_networks.find(network); if (iter != m_networks.end()) { return *iter; } auto const fields = network.split(':', QString::SkipEmptyParts); for (auto const &field: fields) { auto iter = m_networks.find(field); if (iter != m_networks.end()) { return *iter; } } return network; } else if (role == RouteColor) { auto const color = m_relations.at(index.row())->osmData().tagValue(QStringLiteral("colour")); return color.isEmpty() ? QStringLiteral("white") : color; } else if (role == TextColor) { auto const colorValue = m_relations.at(index.row())->osmData().tagValue(QStringLiteral("colour")); auto const color = QColor(colorValue.isEmpty() ? QStringLiteral("white") : colorValue); return GeoDataColorStyle::contrastColor(color); } else if (role == RouteFrom) { return m_relations.at(index.row())->osmData().tagValue(QStringLiteral("from")); } else if (role == RouteTo) { return m_relations.at(index.row())->osmData().tagValue(QStringLiteral("to")); } else if (role == RouteRef) { auto const ref = m_relations.at(index.row())->osmData().tagValue(QStringLiteral("ref")); return ref.isEmpty() ? m_relations.at(index.row())->name() : ref; } else if (role == RouteVia) { auto const viaValue = m_relations.at(index.row())->osmData().tagValue(QStringLiteral("via")); auto viaList = viaValue.split(';', QString::SkipEmptyParts); for (auto &via: viaList) { via = via.trimmed(); } return viaList; } else if (role == OsmId) { return m_relations.at(index.row())->osmData().oid(); } else if (role == RouteVisible) { return m_relations.at(index.row())->isVisible(); } return QVariant(); } QHash RouteRelationModel::roleNames() const { QHash roles; roles[Qt::DisplayRole] = "display"; roles[IconSource] = "iconSource"; roles[Description] = "description"; roles[Network] = "network"; roles[RouteColor] = "routeColor"; roles[TextColor] = "textColor"; roles[RouteFrom] = "routeFrom"; roles[RouteTo] = "routeTo"; roles[RouteRef] = "routeRef"; roles[RouteVia] = "routeVia"; roles[OsmId] = "oid"; roles[RouteVisible] = "routeVisible"; return roles; } QString RouteRelationModel::svgFile(const QString &path) const { #ifdef Q_OS_ANDROID return MarbleDirs::path(QStringLiteral("svg/%1").arg(path)); #else return QStringLiteral("file:///") + MarbleDirs::path(QStringLiteral("svg/%1").arg(path)); #endif } } diff --git a/src/lib/marble/geodata/data/GeoDataRelation.cpp b/src/lib/marble/geodata/data/GeoDataRelation.cpp index a17d30dcc..93c17111f 100644 --- a/src/lib/marble/geodata/data/GeoDataRelation.cpp +++ b/src/lib/marble/geodata/data/GeoDataRelation.cpp @@ -1,185 +1,187 @@ // // 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 Dennis Nienhüser #include "GeoDataRelation.h" #include "GeoDataTypes.h" #include "OsmPlacemarkData.h" #include namespace Marble { class GeoDataRelationPrivate { public: QSet m_features; OsmPlacemarkData m_osmData; QSet m_memberIds; mutable GeoDataRelation::RelationType m_relationType = GeoDataRelation::UnknownType; mutable bool m_relationTypeDirty = true; static QHash s_relationTypes; }; QHash GeoDataRelationPrivate::s_relationTypes; GeoDataRelation::GeoDataRelation() : GeoDataFeature(), d_ptr(new GeoDataRelationPrivate) { // nothing to do } GeoDataRelation::~GeoDataRelation() { delete d_ptr; } GeoDataRelation::GeoDataRelation(const GeoDataRelation &other) : GeoDataFeature(other), d_ptr(new GeoDataRelationPrivate) { Q_D(GeoDataRelation); d->m_features = other.d_func()->m_features; d->m_osmData = other.d_func()->m_osmData; d->m_memberIds = other.d_func()->m_memberIds; d->m_relationType = other.d_func()->m_relationType; d->m_relationTypeDirty = other.d_func()->m_relationTypeDirty; } GeoDataRelation &GeoDataRelation::operator=(GeoDataRelation other) // passed by value { GeoDataFeature::operator=(other); std::swap(*this->d_ptr, *other.d_ptr); return *this; } bool GeoDataRelation::operator<(const GeoDataRelation &other) const { if (relationType() == other.relationType()) { Q_D(const GeoDataRelation); auto const refA = d->m_osmData.tagValue(QStringLiteral("ref")); auto const refB = other.osmData().tagValue(QStringLiteral("ref")); if (refA == refB) { return name() < other.name(); } return refA < refB; } return relationType() < other.relationType(); } const char *GeoDataRelation::nodeType() const { return GeoDataTypes::GeoDataRelationType; } GeoDataFeature *GeoDataRelation::clone() const { return new GeoDataRelation(*this); } void GeoDataRelation::addMember(const GeoDataFeature *feature, qint64 id, const QString &role) { Q_D(GeoDataRelation); d->m_features << feature; d->m_osmData.addRelation(id, role); d->m_memberIds << id; } QSet GeoDataRelation::members() const { Q_D(const GeoDataRelation); return d->m_features; } OsmPlacemarkData &GeoDataRelation::osmData() { Q_D(GeoDataRelation); d->m_relationTypeDirty = true; return d->m_osmData; } const OsmPlacemarkData &GeoDataRelation::osmData() const { Q_D(const GeoDataRelation); return d->m_osmData; } GeoDataRelation::RelationType GeoDataRelation::relationType() const { Q_D(const GeoDataRelation); if (!d->m_relationTypeDirty) { return d->m_relationType; } if (GeoDataRelationPrivate::s_relationTypes.isEmpty()) { auto &map = GeoDataRelationPrivate::s_relationTypes; map["road"] = RouteRoad; map["detour"] = RouteDetour; map["ferry"] = RouteFerry; map["train"] = RouteTrain; map["subway"] = RouteSubway; map["tram"] = RouteTram; map["bus"] = RouteBus; map["trolleybus"] = RouteTrolleyBus; map["bicycle"] = RouteBicycle; map["mtb"] = RouteMountainbike; map["foot"] = RouteFoot; map["hiking"] = GeoDataRelation::RouteHiking; map["horse"] = RouteHorse; map["inline_skates"] = RouteInlineSkates; } d->m_relationType = GeoDataRelation::UnknownType; d->m_relationTypeDirty = false; if (d->m_osmData.containsTag(QStringLiteral("type"), QStringLiteral("route"))) { auto const route = d->m_osmData.tagValue(QStringLiteral("route")); if (route == QStringLiteral("piste")) { auto const piste = d->m_osmData.tagValue(QStringLiteral("piste:type")); if (piste == QStringLiteral("downhill")) { d->m_relationType = RouteSkiDownhill; } else if (piste == QStringLiteral("nordic")) { d->m_relationType = RouteSkiNordic; } else if (piste == QStringLiteral("skitour")) { d->m_relationType = RouteSkitour; } else if (piste == QStringLiteral("sled")) { d->m_relationType = RouteSled; } } else { d->m_relationType = GeoDataRelationPrivate::s_relationTypes.value(route, UnknownType); } } return d->m_relationType; } QSet GeoDataRelation::memberIds() const { Q_D(const GeoDataRelation); return d->m_memberIds; } bool GeoDataRelation::containsAnyOf(const QSet &memberIds) const { Q_D(const GeoDataRelation); #if QT_VERSION >= 0x050600 // intersects was introduced in Qt 5.6. return d->m_memberIds.intersects(memberIds); #else for (auto memberId: memberIds) { if (d->m_memberIds.contains(memberId)) { return true; } } return false; #endif } +Q_DECLARE_OPERATORS_FOR_FLAGS(GeoDataRelation::RelationTypes) + } diff --git a/src/lib/marble/geodata/data/GeoDataRelation.h b/src/lib/marble/geodata/data/GeoDataRelation.h index 2d05ec432..642dbee16 100644 --- a/src/lib/marble/geodata/data/GeoDataRelation.h +++ b/src/lib/marble/geodata/data/GeoDataRelation.h @@ -1,74 +1,76 @@ // // 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 Dennis Nienhüser #ifndef MARBLE_GEODATARELATION_H #define MARBLE_GEODATARELATION_H #include "GeoDataCoordinates.h" #include "GeoDataPlacemark.h" #include "geodata_export.h" namespace Marble { class GeoDataRelationPrivate; class GEODATA_EXPORT GeoDataRelation: public GeoDataFeature { public: enum RelationType { - UnknownType, - RouteRoad, - RouteDetour, - RouteFerry, - RouteTrain, - RouteSubway, - RouteTram, - RouteBus, - RouteTrolleyBus, - RouteBicycle, - RouteMountainbike, - RouteFoot, - RouteHiking, - RouteHorse, - RouteInlineSkates, - RouteSkiDownhill, - RouteSkiNordic, - RouteSkitour, - RouteSled, + UnknownType = 0, + RouteRoad = 1 << 1, + RouteDetour = 1 << 2, + RouteFerry = 1 << 3, + RouteTrain = 1 << 4, + RouteSubway = 1 << 5, + RouteTram = 1 << 6, + RouteBus = 1 << 7, + RouteTrolleyBus = 1 << 8, + RouteBicycle = 1 << 9, + RouteMountainbike = 1 << 10, + RouteFoot = 1 << 11, + RouteHiking = 1 << 12, + RouteHorse = 1 << 13, + RouteInlineSkates = 1 << 14, + RouteSkiDownhill = 1 << 15, + RouteSkiNordic = 1 << 16, + RouteSkitour = 1 << 17, + RouteSled = 1 << 18 }; + Q_DECLARE_FLAGS(RelationTypes, RelationType) + GeoDataRelation(); ~GeoDataRelation() override; GeoDataRelation(const GeoDataRelation &other); GeoDataRelation & operator=(GeoDataRelation other); bool operator<(const GeoDataRelation &other) const; const char* nodeType() const override; GeoDataFeature * clone() const override; void addMember(const GeoDataFeature* feature, qint64 id, const QString &role); QSet members() const; OsmPlacemarkData &osmData(); const OsmPlacemarkData &osmData() const; RelationType relationType() const; QSet memberIds() const; bool containsAnyOf(const QSet &memberIds) const; private: GeoDataRelationPrivate* d_ptr; Q_DECLARE_PRIVATE(GeoDataRelation) }; } #endif diff --git a/src/lib/marble/layers/GeometryLayer.cpp b/src/lib/marble/layers/GeometryLayer.cpp index 78084a912..c6fb027c7 100644 --- a/src/lib/marble/layers/GeometryLayer.cpp +++ b/src/lib/marble/layers/GeometryLayer.cpp @@ -1,673 +1,669 @@ // // 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 "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; - bool m_showPublicTransport; + 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_showPublicTransport(false) + 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_showPublicTransport - && relation->relationType() >= GeoDataRelation::RouteTrain - && relation->relationType() <= GeoDataRelation::RouteTrolleyBus) - || m_highlightedRouteRelations.contains(relation->osmData().oid()); + 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 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::setShowPublicTransport(bool showPublicTransport) +void GeometryLayer::setVisibleRelationTypes(GeoDataRelation::RelationTypes relationTypes) { - if (showPublicTransport == d->m_showPublicTransport) { - return; + if (relationTypes != d->m_visibleRelationTypes) { + d->m_visibleRelationTypes = relationTypes; + d->updateRelationVisibility(); } - - d->m_showPublicTransport = showPublicTransport; - 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/layers/GeometryLayer.h b/src/lib/marble/layers/GeometryLayer.h index 46acd1326..849ae8d48 100644 --- a/src/lib/marble/layers/GeometryLayer.h +++ b/src/lib/marble/layers/GeometryLayer.h @@ -1,94 +1,95 @@ // // 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 Patrick Spendrin // Copyright 2010 Thibaut Gridel // Copyright 2011-2012 Bernhard Beschow // #ifndef MARBLE_GEOMETRYLAYER_H #define MARBLE_GEOMETRYLAYER_H #include #include "LayerInterface.h" #include "GeoDataCoordinates.h" +#include "GeoDataRelation.h" class QAbstractItemModel; class QModelIndex; class QPoint; namespace Marble { class GeoPainter; class GeoDataFeature; class GeoDataPlacemark; class GeoDataRelation; class StyleBuilder; class ViewportParams; class GeometryLayerPrivate; class GeometryLayer : public QObject, public LayerInterface { Q_OBJECT public: explicit GeometryLayer(const QAbstractItemModel *model, const StyleBuilder *styleBuilder); ~GeometryLayer() override; QStringList renderPosition() const override; bool render( GeoPainter *painter, ViewportParams *viewport, const QString& renderPos = QLatin1String("NONE"), GeoSceneLayer * layer = 0 ) override; RenderState renderState() const override; QString runtimeTrace() const override; bool hasFeatureAt(const QPoint& curpos, const ViewportParams * viewport); QVector whichFeatureAt( const QPoint& curpos, const ViewportParams * viewport ); void highlightRouteRelation(qint64 osmId, bool enabled); - void setShowPublicTransport(bool showPublicTransport); + void setVisibleRelationTypes(GeoDataRelation::RelationTypes relationTypes); public Q_SLOTS: void addPlacemarks( const QModelIndex& index, int first, int last ); void removePlacemarks( const QModelIndex& index, int first, int last ); void resetCacheData(); void setTileLevel(int tileLevel); /** * Finds all placemarks that contain the clicked point. * * The placemarks under the clicked position may * have their styleUrl set to a style map which * doesn't specify any highlight styleId. Such * placemarks will be fletered out in GeoGraphicsScene * and will not be highlighted. */ void handleHighlight( qreal lon, qreal lat, GeoDataCoordinates::Unit unit ); Q_SIGNALS: void repaintNeeded(); /** * @p selectedPlacemarks may contain placemarks which don't have * their styleUrl set to id of the style map which specifies * a highlight styleId. Such placemarks will be filtered out * in GeoGraphicsScene which will query for placemark->styleUrl() * to decide whether the placemark should be highlighted ot not. */ void highlightedPlacemarksChanged( const QVector& clickedPlacemarks ); private: GeometryLayerPrivate *d; }; } // namespace Marble #endif // MARBLE_GEOMETRYLAYER_H