diff --git a/src/apps/marble-maps/MainScreen.qml b/src/apps/marble-maps/MainScreen.qml index 4453b47fa..acba10c41 100644 --- a/src/apps/marble-maps/MainScreen.qml +++ b/src/apps/marble-maps/MainScreen.qml @@ -1,359 +1,367 @@ // // 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 1.3 import QtQuick.Window 2.2 import org.kde.marble 0.20 ApplicationWindow { id: root title: qsTr("Marble Maps") visible: true width: 600 height: 400 color: palette.window SystemPalette{ id: palette colorGroup: SystemPalette.Active } Item { id: mapItem anchors { top: parent.top left: parent.left right: parent.right bottom: dialogContainer.top } 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(root.width, root.height) anchors.fill: parent visible: true // Theme settings. - projection: MarbleItem.Mercator + projection: smallZoom ? MarbleItem.Spherical : MarbleItem.Mercator mapThemeId: "earth/vectorosm/vectorosm.dgml" // Visibility of layers/plugins. showFrameRate: false - showAtmosphere: false + showAtmosphere: smallZoom showCompass: false showClouds: false showCrosshairs: false - showGrid: false + showGrid: smallZoom showOverviewMap: false showOtherPlaces: false showScaleBar: false - showBackground: false + showBackground: smallZoom positionProvider: suspended ? "" : currentPositionProvider keepScreenOn: !suspended && navigationManager.visible showPositionMarker: false placemarkDelegate: Image { property int xPos: 0 property int yPos: 0 property var placemark: null x: xPos - 0.5 * width y: yPos - height width: Screen.pixelDensity*6 height: width source: "qrc:///ic_place.png" onPlacemarkChanged: { placemarkDialog.placemark = placemark } } onPositionAvailableChanged: { updateIndicator(); } onPositionVisibleChanged: { updateIndicator(); } onVisibleLatLonAltBoxChanged: { updateIndicator(); } onCurrentPositionChanged: { updateIndicator(); } - Component.onCompleted: marbleMaps.loadSettings() + 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: routing anchors.fill: parent marbleItem: marbleMaps routingProfile: routeEditor.routingProfile // visible: hasRoute // TODO: make this work } PositionMarker { id: positionMarker posX: navigationManager.snappedPositionMarkerScreenPosition.x posY: navigationManager.snappedPositionMarkerScreenPosition.y angle: marbleMaps.angle visible: marbleMaps.positionAvailable && marbleMaps.positionVisible radius: navigationManager.screenAccuracy showAccuracy: navigationManager.deviated } MouseArea { anchors.fill: parent propagateComposedEvents: true onPressed: { marbleMaps.focus = true; mouse.accepted = false; } } Search { id: search anchors.fill: parent marbleQuickItem: marbleMaps routingManager: routing visible: !navigationManager.visible } } NavigationManager { id: navigationManager width: parent.width height: parent.height visible: false marbleItem: marbleMaps } } 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 } 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 anchors { bottom: parent.bottom horizontalCenter: zoomToPositionButton.horizontalCenter bottomMargin: Screen.pixelDensity * 4 } onClicked: { if (dialogContainer.currentIndex === dialogContainer.routing) { dialogContainer.currentIndex = dialogContainer.none navigationManager.visible = true } else if (dialogContainer.currentIndex === dialogContainer.place) { dialogContainer.currentIndex = dialogContainer.routing placemarkDialog.addToRoute() } else { dialogContainer.currentIndex = dialogContainer.routing navigationManager.visible = false } } iconSource: "qrc:///material/directions.svg"; states: [ State { name: "" PropertyChanges { target: routeEditorButton; iconSource: "qrc:///material/directions.svg"; } }, State { name: "routingAction" when: dialogContainer.currentIndex === dialogContainer.routing PropertyChanges { target: routeEditorButton; iconSource: "qrc:///material/navigation.svg"; } }, State { name: "placeAction" when: dialogContainer.currentIndex === dialogContainer.place PropertyChanges { target: routeEditorButton; iconSource: placemarkDialog.actionIconSource } } ] } } Item { id: dialogContainer anchors { left: parent.left right: parent.right bottom: parent.bottom } visible: currentIndex >= 0 property var contentItem: routeEditor height: visible ? contentItem.height : 0 readonly property int none: -1 readonly property int routing: 0 readonly property int place: 1 readonly property int about: 2 readonly property int settings: 3 readonly property int developer: 4 property int currentIndex: none onCurrentIndexChanged: { switch (currentIndex) { case none: case routing: contentItem = routeEditor; break; case place: contentItem = placemarkDialog; break; case about: contentItem = aboutDialog; break; case settings: contentItem = settingsDialog; break; case developer: contentItem = developerDialog; break; } } RouteEditor { id: routeEditor visible: dialogContainer.currentIndex === dialogContainer.routing anchors.left: parent.left anchors.right: parent.right anchors.bottom: parent.bottom } PlacemarkDialog { id: placemarkDialog visible: dialogContainer.currentIndex === dialogContainer.place anchors.left: parent.left anchors.right: parent.right anchors.bottom: parent.bottom map: marbleMaps } AboutDialog { id: aboutDialog visible: dialogContainer.currentIndex === dialogContainer.about anchors.left: parent.left anchors.right: parent.right anchors.bottom: parent.bottom } SettingsDialog { id: settingsDialog visible: dialogContainer.currentIndex === dialogContainer.settings anchors.left: parent.left anchors.right: parent.right anchors.bottom: parent.bottom } DeveloperDialog { id: developerDialog visible: dialogContainer.currentIndex === dialogContainer.developer anchors.left: parent.left anchors.right: parent.right anchors.bottom: parent.bottom } } BorderImage { visible: dialogContainer.visible anchors.fill: dialogContainer anchors.margins: -14 border { top: 14; left: 14; right: 14; bottom: 14 } source: "qrc:///border_shadow.png" } 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: { root.aboutToQuit = false quitHelper.visible = false } } } property bool aboutToQuit: false onClosing: { if (root.aboutToQuit === true) { close.accepted = true // we will quit return } else if (navigationManager.visible) { navigationManager.visible = false } else if (dialogContainer.visible) { dialogContainer.currentIndex = dialogContainer.none } else { root.aboutToQuit = true quitHelper.visible = true } close.accepted = false } } diff --git a/src/apps/marble-maps/create-apk.py b/src/apps/marble-maps/create-apk.py index 6866b65ac..21244bb0c 100755 --- a/src/apps/marble-maps/create-apk.py +++ b/src/apps/marble-maps/create-apk.py @@ -1,139 +1,140 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- # # This file is part of the Marble Virtual Globe. # # This program is free software licensed under the GNU LGPL. You can # find a copy of this license in LICENSE.txt in the top directory of # the source code. # # Copyright 2015 Dennis Nienhüser # import os import sys import argparse from subprocess import call import tempfile import shutil class Filter(object): ''' Decides which files to include in the APK ''' def __init__(self, base): self.base = base.rstrip(os.sep) def shouldPackage(self, dir, files): # Change absolute directory name to one relative to the installation directory dir = dir.replace(self.base, '', 1) if dir == '': # Would not end up in the package, but is slightly faster return ['include'] if dir == '/assets/data': # Currently not used - return ['mwdbii', 'weather', 'naturalearth', 'stars', 'flags'] + return ['mwdbii', 'weather', 'naturalearth', 'flags'] elif dir == '/assets/data/maps': # Other planets are not used return ['moon'] elif dir == '/assets/data/maps/earth': # Unused map themes return ['srtm', 'bluemarble', 'precip-dec', 'citylights', 'plain', 'schagen1689', 'political'] elif dir == '/assets/data/maps/earth/openstreetmap': # Large images from example KML tour return [item for item in files if item.endswith('.png') or item.endswith('.jpg')] elif dir == '/assets/data/placemarks': # Only include very basic placemarks return ['moonlandingsites.cache', 'moonterrain.cache', 'elevplacemarks.cache', 'otherplacemarks.cache', 'cityplacemarks.cache'] elif dir == '/assets/data/placemarks': # Large images. worldmap.svg is used by the overviewmap, bring back if that plugin should be enabled return ['application-x-marble.svg', 'marsmap.svg', 'marble-logo.svg', 'lunarmap.svg', 'worldmap.svg'] elif dir == '/assets/plugins': # Whitelisted plugins, all others are ignored search = ['LatLonPlugin', 'NominatimSearchPlugin', 'LocalDatabasePlugin', 'LocalOsmSearchPlugin'] routing = ['CycleStreetsPlugin', 'OSRMPlugin', 'YoursPlugin', 'NominatimReverseGeocodingPlugin'] fileFormats = ['CachePlugin', 'GpxPlugin', 'KmlPlugin', 'OsmPlugin'] floatItems = ['License'] positioning = ['QtPositioningPositionProviderPlugin'] - plugins = search + routing + fileFormats + floatItems + positioning + render = ['StarsPlugin', 'GraticulePlugin'] + plugins = search + routing + fileFormats + floatItems + positioning + render whitelist = set(['lib{}.so'.format(plugin) for plugin in plugins]) masked = [item for item in files if item not in whitelist] if len(files) - len(masked) != len(whitelist): print ('Warning: At least one white-listed plugin is not installed') return masked elif dir.startswith('/libs/'): # other android app binary return ['libMarbleBehaim.so'] return [] qtDir = os.environ.get('Qt5_android') if qtDir is None: print ('Please setup the Qt5_android environment variable point to your Qt installation') sys.exit(1) parser = argparse.ArgumentParser(description='Create an Android application package (APK) for Marble Maps') parser.add_argument('--target', help='Target filename (or directory) for the .apk package') parser.add_argument('--keystore', help='Keystore file for signing the package') parser.add_argument('--storepass', help='Keystore password for signing the package') parser.add_argument('--release', action='store_true', help='Build a release package') parser.add_argument('--install', action='store_true', help='Install the package to a connected device (uninstalling it before, if needed)') parser.add_argument('--reinstall', action='store_true', help='Install the package to a connected device (keeping previous data intact, if any)') parser.add_argument('directory', help='The directory where the Android build is installed to') args = parser.parse_args() # Sanity check for given options if not args.install and not args.reinstall and args.target is None: print('Please pass one of --install, --reinstall or --target to either install the package directly or store it locally.') sys.exit(1) jsonFile = os.path.join(args.directory, 'share', 'deploy-marble-maps.json') if not os.path.isfile(jsonFile): print('Cannot find {}. Is {} really a Marble Android installation?'.format(jsonFile, args.directory)) sys.exit(1) # Gather needed tools deployExe = os.path.join(qtDir, 'bin', 'androiddeployqt') antExe = os.environ.get('ANT', '/usr/bin/ant') with tempfile.TemporaryDirectory() as tempDir: os.rmdir(tempDir) # shutil.copytree does not like directories to exist filter = Filter(args.directory) shutil.copytree(args.directory, tempDir, ignore=filter.shouldPackage) deployOptions = ['--verbose', '--output', tempDir, '--input', jsonFile, '--ant', antExe] # Debug vs. release type packages, signing options if args.release or args.keystore is not None: deployOptions.append('--release') if args.keystore is not None: deployOptions.append('--sign') deployOptions.append(args.keystore) deployOptions.append('Marble') deployOptions.append('--tsa') deployOptions.append('http://timestamp.digicert.com') if args.storepass is not None: deployOptions.append('--storepass') deployOptions.append(args.storepass) # Options for installing the created package to a connected device if args.install: deployOptions.append('--install') if args.reinstall: deployOptions.append('--reinstall') call([deployExe] + deployOptions) if args.target is not None: packageType = 'debug' if args.keystore is not None: packageType = 'release-signed' elif args.release: packageType = 'release-unsigned' targetFile = os.path.join(tempDir, 'bin', 'QtApp-{}.apk'.format(packageType)) if os.path.isfile(targetFile): shutil.copy(targetFile, args.target) print ('Created APK at ' + args.target) else: sys.exit(1)