diff --git a/src/apps/CMakeLists.txt b/src/apps/CMakeLists.txt --- a/src/apps/CMakeLists.txt +++ b/src/apps/CMakeLists.txt @@ -9,5 +9,6 @@ endif() if(QT5BUILD) + add_subdirectory(behaim) add_subdirectory(marble-maps) endif() diff --git a/src/apps/behaim/CMakeLists.txt b/src/apps/behaim/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/src/apps/behaim/CMakeLists.txt @@ -0,0 +1,61 @@ +set(marble_SRCS main.cpp) +qt5_add_resources(marble_QRCS MarbleBehaim.qrc) + +include_directories(${CMAKE_CURRENT_BINARY_DIR}) + +if (CMAKE_SYSTEM_NAME STREQUAL Android) + set(TARGET MarbleBehaim) + + find_package(Qt5AndroidExtras) + include_directories(${Qt5AndroidExtras_INCLUDE_DIRS}) + + add_library (${TARGET} SHARED ${marble_SRCS} ${marble_QRCS}) +else() + set(TARGET marble-behaim) + add_executable (${TARGET} ${marble_SRCS} ${marble_QRCS}) +endif() + +target_link_libraries ( + ${TARGET} + ${Qt5Core_LIBRARIES} + ${Qt5Xml_LIBRARIES} + ${Qt5Widgets_LIBRARIES} + ${Qt5PrintSupport_LIBRARIES} + ${Qt5Network_LIBRARIES} + ${Qt5WebKitWidgets_LIBRARIES} + ${Qt5WebKit_LIBRARIES} + ${Qt5Sql_LIBRARIES} + ${Qt5Location_LIBRARIES} + ${Qt5Positioning_LIBRARIES} + ${Qt5AndroidExtras_LIBRARIES} + ${Qt5Multimedia_LIBRARIES} + astro + ${MARBLEWIDGET} + marbledeclarative) + +FILE(GLOB QML_FILES *.qml) +add_custom_target(marble-behaim_resources ALL SOURCES ${QML_FILES} package/AndroidManifest.xml) + +if (CMAKE_SYSTEM_NAME STREQUAL Android) + install(DIRECTORY "../../../data/android/" DESTINATION "${CMAKE_INSTALL_PREFIX}/res") + set(ABSOLUTE_INSTALL_PATH "${CMAKE_INSTALL_PREFIX}") + if(NOT IS_ABSOLUTE "${ABSOLUTE_INSTALL_PATH}") + set(ABSOLUTE_INSTALL_PATH "${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_PREFIX}") + endif() + get_filename_component(ABSOLUTE_INSTALL_PATH "${ABSOLUTE_INSTALL_PATH}" ABSOLUTE) + get_filename_component(QT_ANDROID_QT_ROOT "${Qt5Core_DIR}/../../.." ABSOLUTE) + set(QT_ANDROID_SDK_ROOT $ENV{ANDROID_SDK}) + set(QT_ANDROID_NDK_ROOT $ENV{ANDROID_NDK}) + set(QT_ANDROID_APP_PATH "${ABSOLUTE_INSTALL_PATH}/libs/${ANDROID_ABI}/libMarbleBehaim.so") + set(QT_ANDROID_APP_EXTRA_LIBS "${ABSOLUTE_INSTALL_PATH}/lib/libastro.so,${ABSOLUTE_INSTALL_PATH}/lib/libmarblewidget-qt5.so,${ABSOLUTE_INSTALL_PATH}/lib/libmarbledeclarative.so") + set(QT_ANDROID_APP_PACKAGE_SOURCE_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/package") + set(QT_ANDROID_APP_PACKAGE_NAME "org.kde.marble.behaim") + set(QT_ANDROID_APP_NAME "Marble Globe: Behaim's Erdapfel") + configure_file(package/deploy-behaim.json.in ${CMAKE_CURRENT_BINARY_DIR}/deploy-behaim.json @ONLY) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/deploy-behaim.json DESTINATION "${CMAKE_INSTALL_PREFIX}/share") + install(TARGETS ${TARGET} LIBRARY DESTINATION libs/${ANDROID_ABI}) +else() + if(CMAKE_SYSTEM_NAME STREQUAL Android OR MARBLE_INSTALL_ANDROID_APPS) + install(TARGETS ${TARGET} RUNTIME DESTINATION bin) + endif() +endif() diff --git a/src/apps/behaim/Legend.qml b/src/apps/behaim/Legend.qml new file mode 100644 --- /dev/null +++ b/src/apps/behaim/Legend.qml @@ -0,0 +1,216 @@ +// +// 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 QtQuick 2.3 +import QtQuick.Controls 1.3 +import QtQuick.Window 2.2 +import QtQuick.Layouts 1.1 +import QtGraphicalEffects 1.0 + +Item { + id: root + + Rectangle { + id: background + anchors.fill: parent + color: palette.base + } + + DropShadow { + anchors.fill: root + horizontalOffset: landscape ? 4 : 0 + verticalOffset: landscape ? 0 : -4 + radius: 4.0 + samples: 16 + color: "#666" + cached: true + fast: true + source: background + transparentBorder: true + } + + TabView { + id: tabView + anchors.fill: parent + tabPosition: Qt.BottomEdge + + Tab { + id: infoTab + anchors.margins: Screen.pixelDensity * 2 + title: "Info" + + Flickable { + anchors.fill: parent + contentWidth: infoText.width + contentHeight: infoText.height + flickableDirection: Flickable.VerticalFlick + clip: true + + Text { + id: infoText + text: "

Martin Behaim's Erdapfel

\ +

The oldest existant globe of the Earth.
\ + Martin Behaim and collaborators created the globe at the time of Columbus' first sea travel to the west.\ + Hence the American continent is missing on this globe.\ + Also note the detailed inscriptions in early modern German.

" + width: infoTab.width + wrapMode: Text.WordWrap + onLinkActivated: Qt.openUrlExternally(link) + } + } + + } + Tab { + id: variantsTab + anchors.margins: Screen.pixelDensity * 2 + title: "Variants" + + Flickable { + anchors.fill: parent + contentWidth: themeColumn.width + contentHeight: themeColumn.height + flickableDirection: Flickable.VerticalFlick + clip: true + + Column { + id: themeColumn + width: variantsTab.width + anchors { + margins: Screen.pixelDensity * 2 + topMargin: Screen.pixelDensity * 14 + } + + Text { + wrapMode: Text.Wrap + text: "

Globe Variant

" + } + + ExclusiveGroup { + id: layerGroup + onCurrentChanged: current.apply() + } + RadioButton { + text: "Original (1492)" + checked: true + exclusiveGroup: layerGroup + property string description: "Digital imagery taken directly from the original Behaim globe." + function apply() { + marbleMaps.setPropertyEnabled("ravenstein", false) + marbleMaps.setPropertyEnabled("ghillany", false) + } + } + + RadioButton { + text: "Ghillany (1853)" + property string description: "A (rough) facsimile created by Friedrich Wilhelm Ghillany in 1853." + exclusiveGroup: layerGroup + + function apply() { + marbleMaps.setPropertyEnabled("ravenstein", false) + marbleMaps.setPropertyEnabled("ghillany", true) + } + } + + RadioButton { + text: "Ravenstein (1908)" + property string description: "A (rough) facsimile created by Ernest George Ravenstein in 1908." + exclusiveGroup: layerGroup + function apply() { + marbleMaps.setPropertyEnabled("ghillany", false) + marbleMaps.setPropertyEnabled("ravenstein", true) + } + } + + Item { width: 1; height: Screen.pixelDensity * 2; } + + Text { + text: layerGroup.current.description + width: parent.width + wrapMode: Text.Wrap + } + } + } + } + + Tab { + anchors.margins: Screen.pixelDensity * 2 + title: "Settings" + + Flickable { + anchors.fill: parent + contentWidth: settingsColumn.width + contentHeight: settingsColumn.height + flickableDirection: Flickable.VerticalFlick + clip: true + + Column { + id: settingsColumn + anchors { + margins: Screen.pixelDensity * 2 + topMargin: Screen.pixelDensity * 14 + } + + Text { + wrapMode: Text.Wrap + text: "

Globe Settings

" + } + + CheckBox { + text: "Show Behaim places" + onCheckedChanged: marbleMaps.setPropertyEnabled("cities", checked) + } + CheckBox { + text: "Show texts and illustrations" + onCheckedChanged: marbleMaps.setPropertyEnabled("otherplaces", checked) + + } + CheckBox { + text: "Show the accurate coastline" + onCheckedChanged: marbleMaps.setPropertyEnabled("coastlines", checked) + } + } + } + } + + Tab { + id: aboutTab + anchors.margins: Screen.pixelDensity * 2 + title: "About" + + Flickable { + anchors.fill: parent + contentWidth: aboutText.width + contentHeight: aboutText.height + flickableDirection: Flickable.VerticalFlick + clip: true + + Text { + id: aboutText + anchors { + margins: Screen.pixelDensity * 2 + topMargin: Screen.pixelDensity * 14 + } + + text: "

Germanisches Nationalmuseum

\ +

The original Behaim globe can be visited in the + Germanisches Nationalmuseum in Nuremberg, Germany.\ + It was digitized in an elaborate process over several years and made available to the public under the CC-BY-SA license.

\ +

KDE Marble

\ +

This app is part of the Marble Project.\ + The Marble community works on maps and virtual globes with the goal to produce visually appealing, easy-to-use Free Software.

" + width: aboutTab.width + wrapMode: Text.WordWrap + onLinkActivated: Qt.openUrlExternally(link) + } + } + } + } +} diff --git a/src/apps/behaim/MainScreen.qml b/src/apps/behaim/MainScreen.qml new file mode 100644 --- /dev/null +++ b/src/apps/behaim/MainScreen.qml @@ -0,0 +1,117 @@ +// +// 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 QtQuick 2.3 +import QtQuick.Controls 1.3 +import QtQuick.Window 2.2 +import QtQuick.Layouts 1.1 + +import org.kde.edu.marble 0.20 + +ApplicationWindow { + id: root + title: qsTr("Behaim Globe") + visible: true + + width: 600 + height: 400 + + property bool landscape: root.width > root.height + + SystemPalette { + id: palette + colorGroup: SystemPalette.Active + } + + Rectangle { + id: background + anchors.fill: parent + color: palette.window + } + + GridLayout { + id: mainLayout + columns: root.landscape ? 2 : 1 + columnSpacing: 0 + rows: root.landscape ? 1 : 2 + rowSpacing: 0 + layoutDirection: root.landscape ? Qt.RightToLeft : Qt.LeftToRight + + Item { + id: mapItem + + implicitWidth: root.landscape ? root.width - infoItem.width : root.width + implicitHeight: root.landscape ? root.height : root.height - infoItem.height + + Rectangle { + color: "black" + anchors.fill: parent + } + + PinchArea { + anchors.fill: parent + enabled: true + + onPinchStarted: marbleMaps.handlePinchStarted(pinch.center) + onPinchFinished: marbleMaps.handlePinchFinished(pinch.center) + onPinchUpdated: marbleMaps.handlePinchUpdated(pinch.center, pinch.scale); + + MarbleItem { + id: marbleMaps + anchors.fill: parent + + focus: true + zoom: 1150 + inertialGlobeRotation: true + + // Theme settings. + projection: MarbleItem.Spherical + mapThemeId: "earth/behaim1492/behaim1492.dgml" + + // Visibility of layers/plugins. + showFrameRate: false + showAtmosphere: true + showCompass: false + showClouds: false + showCrosshairs: false + showGrid: false + showOverviewMap: false + showOtherPlaces: false + showScaleBar: false + showBackground: true + showPositionMarker: false + + Component.onCompleted: { + setPluginSetting("stars", "renderConstellationLines", "false"); + setPluginSetting("stars", "renderConstellationLabels", "false"); + setPluginSetting("stars", "renderDsoLabels", "false"); + setPluginSetting("stars", "viewSolarSystemLabel", "false"); + setPluginSetting("stars", "zoomSunMoon", "false"); + setPluginSetting("stars", "renderEcliptic", "false"); + setPluginSetting("stars", "renderCelestialEquator", "false"); + setPluginSetting("stars", "renderCelestialPole", "false"); + } + } + } + } + + Item { + id: infoItem + implicitWidth: root.landscape ? root.width / 2.5 : root.width + implicitHeight: root.landscape ? root.height : root.height / 2.5 + + Legend { + id: legend + anchors.fill: parent + } + } + } +} diff --git a/src/apps/behaim/MarbleBehaim.qrc b/src/apps/behaim/MarbleBehaim.qrc new file mode 100644 --- /dev/null +++ b/src/apps/behaim/MarbleBehaim.qrc @@ -0,0 +1,6 @@ + + + MainScreen.qml + Legend.qml + + diff --git a/src/apps/behaim/create-apk.py b/src/apps/behaim/create-apk.py new file mode 100755 --- /dev/null +++ b/src/apps/behaim/create-apk.py @@ -0,0 +1,136 @@ +#!/usr/bin/env python +# -*- 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 + + 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', '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': + # Moon placemarks + return ['moonlandingsites.cache', 'moonterrain.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', 'LocalDatabasePlugin'] + fileFormats = ['CachePlugin', 'KmlPlugin', 'Pn2Plugin'] + floatItems = ['License'] + positioning = ['QtPositioningPositionProviderPlugin'] + background = ['StarsPlugin', 'AtmospherePlugin'] + plugins = search + fileFormats + floatItems + positioning + background + 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 + + 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-behaim.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) diff --git a/src/apps/behaim/main.cpp b/src/apps/behaim/main.cpp new file mode 100644 --- /dev/null +++ b/src/apps/behaim/main.cpp @@ -0,0 +1,37 @@ +// +// This file is part of the Marble Virtual Globe. +// +// This program is free software licensed under the GNU LGPL. You can +// find a copy of this license in LICENSE.txt in the top directory of +// the source code. +// +// Copyright 2015 Dennis Nienhüser +// + +#include +#include +#include + +#include "declarative/MarbleDeclarativePlugin.h" +#include + +using namespace Marble; + +int main(int argc, char ** argv) +{ + QApplication app(argc, argv); + +#ifdef Q_OS_ANDROID + MarbleGlobal::Profiles profiles = MarbleGlobal::SmallScreen | MarbleGlobal::HighResolution; + MarbleGlobal::getInstance()->setProfiles( profiles ); +#endif + + MarbleDeclarativePlugin declarativePlugin; + const char * uri = "org.kde.edu.marble"; + declarativePlugin.registerTypes(uri); + + QQmlApplicationEngine engine; + engine.load(QUrl("qrc:/MainScreen.qml")); + + return app.exec(); +} diff --git a/src/apps/behaim/package/AndroidManifest.xml b/src/apps/behaim/package/AndroidManifest.xml new file mode 100644 --- /dev/null +++ b/src/apps/behaim/package/AndroidManifest.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/apps/behaim/package/deploy-behaim.json.in b/src/apps/behaim/package/deploy-behaim.json.in new file mode 100644 --- /dev/null +++ b/src/apps/behaim/package/deploy-behaim.json.in @@ -0,0 +1,16 @@ +{ + "description": "This file is to be read by androiddeployqt", + "qt": "@QT_ANDROID_QT_ROOT@", + "sdk": "@QT_ANDROID_SDK_ROOT@", + "ndk": "@QT_ANDROID_NDK_ROOT@", + "toolchain-prefix": "@ANDROID_TOOLCHAIN@", + "tool-prefix": "@ANDROID_TOOLCHAIN@", + "toolchain-version": "@ANDROID_GCC_VERSION@", + "ndk-host": "@_HOST@", + "target-architecture": "@ANDROID_ABI@", + "application-binary": "@QT_ANDROID_APP_PATH@", + "android-package": "@QT_ANDROID_APP_PACKAGE_NAME@", + "android-app-name": "@QT_ANDROID_APP_NAME@", + "android-extra-libs": "@QT_ANDROID_APP_EXTRA_LIBS@", + "android-package-source-directory": "@QT_ANDROID_APP_PACKAGE_SOURCE_ROOT@" +}