diff --git a/src/apps/marble-kde/kdemain.cpp b/src/apps/marble-kde/kdemain.cpp index 33cd9508e..4cd2c11e4 100644 --- a/src/apps/marble-kde/kdemain.cpp +++ b/src/apps/marble-kde/kdemain.cpp @@ -1,444 +1,450 @@ // // This file is part of the Marble Virtual Globe. // // This program is free software licensed under the GNU LGPL. You can // find a copy of this license in LICENSE.txt in the top directory of // the source code. // // Copyright 2007 Torsten Rahn // Copyright 2007 Inge Wallin // Copyright 2009 Jens-Michael Hoffmann // #include #include #include #include #include #include #include #include #include #include #include #include #include "ControlView.h" #include "KdeMainWindow.h" #include "GeoUriParser.h" #include "MarbleDebug.h" #include "MapThemeManager.h" #include "MarbleTest.h" #ifdef STATIC_BUILD #include Q_IMPORT_PLUGIN(qjpeg) Q_IMPORT_PLUGIN(qsvg) #endif using namespace Marble; static bool loadTranslation(const QString &localeDirName, QApplication &app) { const QString subPath = QLatin1String("locale/") + localeDirName + QLatin1String("/LC_MESSAGES/marble_qt.qm"); const QString fullPath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, subPath); if (fullPath.isEmpty()) { return false; } QTranslator* translator = new QTranslator(&app); if (!translator->load(fullPath)) { delete translator; return false; } app.installTranslator(translator); return true; } static void loadLibAndPluginTranslations(QApplication &app) { // Quote from ecm_create_qm_loader created code: // The way Qt translation system handles plural forms makes it necessary to // have a translation file which contains only plural forms for `en`. // That's why we load the `en` translation unconditionally, then load the // translation for the current locale to overload it. const QString en(QStringLiteral("en")); loadTranslation(en, app); QLocale locale = QLocale::system(); if (locale.name() != en) { if (!loadTranslation(locale.name(), app)) { loadTranslation(locale.bcp47Name(), app); } } } int main ( int argc, char *argv[] ) { QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps, true); QApplication app( argc, argv ); // Load Qt translation system catalog for the plugins and libmarblewidget loadLibAndPluginTranslations(app); // Init KF5 translation system KLocalizedString::setApplicationDomain("marble"); KAboutData aboutData( "marble", i18n( "Marble Virtual Globe" ), ControlView::applicationVersion(), i18n( "A World Atlas." ), KAboutLicense::LGPL_V2, i18n( "(c) 2007-%1", QLatin1String("2016") ), QString(), "http://marble.kde.org/" ); // Active Development Team of Marble aboutData.addAuthor( i18n( "Torsten Rahn" ), i18n( "Developer and Original Author" ), "rahn@kde.org" ); aboutData.addAuthor( i18n( "Bernhard Beschow" ), i18n( "WMS Support, Mobile, Performance" ), "bbeschow@cs.tu-berlin.de" ); aboutData.addAuthor( i18n( "Thibaut Gridel" ), i18n( "Geodata" ), "tgridel@free.fr" ); aboutData.addAuthor( i18n( "Jens-Michael Hoffmann" ), i18n( "OpenStreetMap Integration, OSM Namefinder, Download Management" ), "jmho@c-xx.com", "http://www.c-xx.com" ); aboutData.addAuthor( i18n( "Florian Eßer" ), i18n( "Elevation Profile" ), "f.esser@rwth-aachen.de" ); aboutData.addAuthor( i18n( "Wes Hardaker" ), i18n( "APRS Plugin" ), "marble@hardakers.net" ); aboutData.addAuthor( i18n( "Bastian Holst" ), i18n( "Online Services support" ), "bastianholst@gmx.de" ); aboutData.addAuthor( i18n( "Guillaume Martres" ), i18n( "Satellites" ), "smarter@ubuntu.com" ); aboutData.addAuthor( i18n( "Rene Kuettner" ), i18n( "Satellites, Eclipses" ), "rene@bitkanal.net" ); aboutData.addAuthor( i18n( "Friedrich W. H. Kossebau" ), i18n( "Plasma Integration, Bugfixes" ), "kossebau@kde.org" ); aboutData.addAuthor( i18n( "Dennis Nienhüser" ), i18n( "Routing, Navigation, Mobile" ), "nienhueser@kde.org" ); aboutData.addAuthor( i18n( "Niko Sams" ), i18n( "Routing, Elevation Profile" ), "niko.sams@gmail.com" ); aboutData.addAuthor( i18n( "Patrick Spendrin" ), i18n( "Core Developer: KML and Windows support" ), "pspendrin@gmail.com" ); aboutData.addAuthor( i18n( "Eckhart Wörner" ), i18n( "Bugfixes" ), "kde@ewsoftware.de" ); // Developers: aboutData.addAuthor( i18n( "Inge Wallin" ), i18n( "Core Developer and Co-Maintainer" ), "inge@lysator.liu.se" ); aboutData.addAuthor( i18n( "Henry de Valence" ), i18n( "Core Developer: Marble Runners, World-Clock Plasmoid" ), "hdevalence@gmail.com" ); aboutData.addAuthor( i18n( "Pino Toscano" ), i18n( "Network plugins" ), "pino@kde.org" ); aboutData.addAuthor( i18n( "Harshit Jain" ), i18n( "Planet filter" ), "sonu.itbhu@googlemail.com" ); aboutData.addAuthor( i18n( "Simon Edwards" ), i18n( "Marble Python Bindings" ), "simon@simonzone.com" ); aboutData.addAuthor( i18n( "Magnus Valle" ), i18n( "Historical Maps" ), "" ); aboutData.addAuthor( i18n( "Médéric Boquien" ), i18n( "Astronomical Observatories" ), "mboquien@free.fr" ); // ESA Summer of Code in Space aboutData.addAuthor( i18n( "Rene Kuettner" ), i18n( "ESA Summer of Code in Space 2012 Project:" " Visualization of planetary satellites" ), "rene@bitkanal.net" ); aboutData.addAuthor( i18n( "Guillaume Martres" ), i18n( "ESA Summer of Code in Space 2011 Project:" " Visualization of Satellite Orbits" ), "smarter@ubuntu.com" ); // Google Summer of Code aboutData.addAuthor( i18n( "Konstantin Oblaukhov" ), i18n( "Google Summer of Code 2011 Project:" " OpenStreetMap Vector Rendering" ), "oblaukhov.konstantin@gmail.com" ); aboutData.addAuthor( i18n( "Daniel Marth" ), i18n( "Google Summer of Code 2011 Project:" " Marble Touch on MeeGo" ), "danielmarth@gmx.at" ); aboutData.addAuthor( i18n( "Gaurav Gupta" ), i18n( "Google Summer of Code 2010 Project:" " Bookmarks" ), "1989.gaurav@gmail.com" ); aboutData.addAuthor( i18n( "Harshit Jain " ), i18n( "Google Summer of Code 2010 Project:" " Time Support" ), "hjain.itbhu@gmail.com" ); aboutData.addAuthor( i18n( "Siddharth Srivastava" ), i18n( "Google Summer of Code 2010 Project:" " Turn-by-turn Navigation" ), "akssps011@gmail.com" ); aboutData.addAuthor( i18n( "Andrew Manson" ), i18n( "Google Summer of Code 2009 Project:" " OSM Annotation" ), "g.real.ate@gmail.com" ); aboutData.addAuthor( i18n( "Bastian Holst" ), i18n( "Google Summer of Code 2009 Project:" " Online Services" ), "bastianholst@gmx.de" ); aboutData.addAuthor( i18n( "Patrick Spendrin" ), i18n( "Google Summer of Code 2008 Project:" " Vector Tiles for Marble" ), "pspendrin@gmail.com" ); aboutData.addAuthor( i18n( "Shashank Singh" ), i18n( "Google Summer of Code 2008 Project:" " Panoramio / Wikipedia -photo support for Marble" ), "shashank.personal@gmail.com" ); aboutData.addAuthor( i18n( "Carlos Licea" ), i18n( "Google Summer of Code 2007 Project:" " Equirectangular Projection (\"Flat Map\")" ), "carlos.licea@kdemail.net" ); aboutData.addAuthor( i18n( "Andrew Manson" ), i18n( "Google Summer of Code 2007 Project:" " GPS Support for Marble" ), "g.real.ate@gmail.com" ); aboutData.addAuthor( i18n( "Murad Tagirov" ), i18n( "Google Summer of Code 2007 Project:" " KML Support for Marble" ), "tmurad@gmail.com" ); // Developers aboutData.addAuthor( i18n( "Simon Schmeisser" ), i18n( "Development & Patches" )); aboutData.addAuthor( i18n( "Claudiu Covaci" ), i18n( "Development & Patches" )); aboutData.addAuthor( i18n( "David Roberts" ), i18n( "Development & Patches" )); aboutData.addAuthor( i18n( "Nikolas Zimmermann" ), i18n( "Development & Patches" )); aboutData.addAuthor( i18n( "Jan Becker" ), i18n( "Development & Patches" )); aboutData.addAuthor( i18n( "Stefan Asserhäll" ), i18n( "Development & Patches" )); aboutData.addAuthor( i18n( "Laurent Montel" ), i18n( "Development & Patches" )); aboutData.addAuthor( i18n( "Mayank Madan" ), i18n( "Development & Patches" )); aboutData.addAuthor( i18n( "Prashanth Udupa" ), i18n( "Development & Patches" )); aboutData.addAuthor( i18n( "Anne-Marie Mahfouf" ), i18n( "Development & Patches" )); aboutData.addAuthor( i18n( "Josef Spillner" ), i18n( "Development & Patches" )); aboutData.addAuthor( i18n( "Frerich Raabe" ), i18n( "Development & Patches" )); aboutData.addAuthor( i18n( "Frederik Gladhorn" ), i18n( "Development & Patches" )); aboutData.addAuthor( i18n( "Fredrik Höglund" ), i18n( "Development & Patches" )); aboutData.addAuthor( i18n( "Albert Astals Cid" ), i18n( "Development & Patches" )); aboutData.addAuthor( i18n( "Thomas Zander" ), i18n( "Development & Patches" )); aboutData.addAuthor( i18n( "Joseph Wenninger" ), i18n( "Development & Patches" )); aboutData.addAuthor( i18n( "Kris Thomsen" ), i18n( "Development & Patches" )); aboutData.addAuthor( i18n( "Daniel Molkentin" ), i18n( "Development & Patches" )); aboutData.addAuthor( i18n( "Christophe Leske" ), i18n( "Platforms & Distributions" )); aboutData.addAuthor( i18n( "Sebastian Wiedenroth" ), i18n( "Platforms & Distributions" )); aboutData.addAuthor( i18n( "Tim Sutton" ), i18n( "Platforms & Distributions" )); aboutData.addAuthor( i18n( "Christian Ehrlicher" ), i18n( "Platforms & Distributions" )); aboutData.addAuthor( i18n( "Ralf Habacker" ), i18n( "Platforms & Distributions" )); aboutData.addAuthor( i18n( "Steffen Joeris" ), i18n( "Platforms & Distributions" )); aboutData.addAuthor( i18n( "Marcus Czeslinski" ), i18n( "Platforms & Distributions" )); aboutData.addAuthor( i18n( "Marcus D. Hanwell" ), i18n( "Platforms & Distributions" )); aboutData.addAuthor( i18n( "Chitlesh Goorah" ), i18n( "Platforms & Distributions" )); aboutData.addAuthor( i18n( "Nuno Pinheiro" ), i18n( "Artwork" )); aboutData.addAuthor( i18n( "Torsten Rahn" ), i18n( "Artwork" )); // Credits aboutData.addCredit( i18n( "Luis Silva" ), i18n( "Various Suggestions & Testing" )); aboutData.addCredit( i18n( "Stefan Jordan" ), i18n( "Various Suggestions & Testing" )); aboutData.addCredit( i18n( "Robert Scott" ), i18n( "Various Suggestions & Testing" )); aboutData.addCredit( i18n( "Lubos Petrovic" ), i18n( "Various Suggestions & Testing" )); aboutData.addCredit( i18n( "Benoit Sigoure" ), i18n( "Various Suggestions & Testing" )); aboutData.addCredit( i18n( "Martin Konold" ), i18n( "Various Suggestions & Testing" )); aboutData.addCredit( i18n( "Matthias Welwarsky" ), i18n( "Various Suggestions & Testing" )); aboutData.addCredit( i18n( "Rainer Endres" ), i18n( "Various Suggestions & Testing" )); aboutData.addCredit( i18n( "Ralf Gesellensetter" ), i18n( "Various Suggestions & Testing" )); aboutData.addCredit( i18n( "Tim Alder" ), i18n( "Various Suggestions & Testing" )); aboutData.addCredit( i18n( "John Layt" ), i18n( "Special thanks for providing an" " important source of inspiration by creating" " Marble's predecessor \"Kartographer\"." )); KCrash::setCrashHandler(KCrash::defaultCrashHandler); KCrash::setDrKonqiEnabled(true); KAboutData::setApplicationData(aboutData); QApplication::setWindowIcon(QIcon::fromTheme(QStringLiteral("marble"))); QCommandLineParser parser; parser.addHelpOption(); parser.addVersionOption(); aboutData.setupCommandLine(&parser); // Autodetect profiles MarbleGlobal::Profiles profiles = MarbleGlobal::getInstance()->profiles(); QCommandLineOption debugOption( "debug-info", i18n( "Enable debug output" ) ); parser.addOption( debugOption ); + QCommandLineOption levelOption( "debug-levels", i18n( "Display OSM placemarks according to the level selected" ) ); + parser.addOption( levelOption ); QCommandLineOption timeOption( "timedemo", i18n( "Make a time measurement to check performance" ) ); parser.addOption( timeOption ); QCommandLineOption fpsOption( "fps", i18n( "Show frame rate" ) ); parser.addOption( fpsOption ); QCommandLineOption tileOption( "tile-id", i18n( "Show tile IDs" ) ); parser.addOption( tileOption ); QCommandLineOption traceOption( "runtimeTrace", i18n( "Show time spent in each layer" ) ); parser.addOption( traceOption ); QCommandLineOption dataPathOption("marbledatapath", i18n("Use a different directory which contains map data."), "directory"); parser.addOption( dataPathOption ); QCommandLineOption noSmallScreenOption( "nosmallscreen", i18n( "Do not use the interface optimized for small screens" ) ); QCommandLineOption smallScreenOption( "smallscreen", i18n( "Use the interface optimized for small screens" ) ); parser.addOption( (profiles & MarbleGlobal::SmallScreen) ? noSmallScreenOption : smallScreenOption ); QCommandLineOption noHighResOption( "nohighresolution", i18n( "Do not use the interface optimized for high resolutions" ) ); QCommandLineOption highResOption( "highresolution", i18n( "Use the interface optimized for high resolutions" ) ); parser.addOption( (profiles & MarbleGlobal::HighResolution) ? noHighResOption : highResOption ); QCommandLineOption coordinatesOption("latlon", i18n("Show map at given lat lon "), "coordinates"); parser.addOption( coordinatesOption ); QCommandLineOption geoUriOption("geo-uri", i18n("Show map at given geo "), "uri"); parser.addOption( geoUriOption ); QCommandLineOption distanceOption("distance", i18n("Set the distance of the observer to the globe (in km)"), "distance"); parser.addOption( distanceOption ); QCommandLineOption mapIdOption("map", i18n("Use map (e.g. \"earth/openstreetmap/openstreetmap.dgml\")"), "id"); parser.addOption( mapIdOption ); parser.addPositionalArgument("file", i18n( "One or more placemark files to be opened") ); parser.process( app ); aboutData.processCommandLine(&parser); // use ecm_create_qm_loader(marblewidget_SRCS marble_qt) // in the library src/lib/marble/CMakeList.txt to load the second catalog MarbleDebug::setEnabled( parser.isSet( debugOption ) ); if ( parser.isSet( smallScreenOption ) ) { profiles |= MarbleGlobal::SmallScreen; } else { profiles &= ~MarbleGlobal::SmallScreen; } if ( parser.isSet( highResOption ) ) { profiles |= MarbleGlobal::HighResolution; } else { profiles &= ~MarbleGlobal::HighResolution; } MarbleGlobal::getInstance()->setProfiles( profiles ); QString marbleDataPath = parser.value( dataPathOption ); MainWindow *window = new MainWindow( marbleDataPath ); window->setAttribute( Qt::WA_DeleteOnClose, true ); window->show(); if ( parser.isSet( timeOption ) ) { window->resize(900, 640); MarbleTest test( window->marbleWidget() ); test.timeDemo(); return 0; } if ( parser.isSet( fpsOption ) ) { window->marbleControl()->marbleWidget()->setShowFrameRate( true ); } + if (parser.isSet(levelOption)) { + window->marbleWidget()->setDebugLevelTags(true); + } + if ( parser.isSet( tileOption ) ) { window->marbleControl()->marbleWidget()->setShowTileId( true ); } const QString map = parser.value( mapIdOption ); if ( !map.isEmpty() ) { window->marbleWidget()->setMapThemeId(map); } const QString coordinatesString = parser.value( coordinatesOption ); if ( !coordinatesString.isEmpty() ) { bool success = false; const GeoDataCoordinates coordinates = GeoDataCoordinates::fromString(coordinatesString, success); if ( success ) { const qreal longitude = coordinates.longitude(GeoDataCoordinates::Degree); const qreal latitude = coordinates.latitude(GeoDataCoordinates::Degree); window->marbleWidget()->centerOn(longitude, latitude); } } const QString geoUriString = parser.value( geoUriOption ); if ( !geoUriString.isEmpty() ) { window->marbleControl()->openGeoUri( geoUriString ); } const QString distance = parser.value( distanceOption ); if ( !distance.isEmpty() ) { bool success = false; const qreal distanceValue = distance.toDouble(&success); if ( success ) window->marbleWidget()->setDistance(distanceValue); } // Read the files that are given on the command line. for( const QString &file: parser.positionalArguments() ) { // FIXME: Use openUrl( args->url(i) ) instead? if ( QFile::exists( file ) ) { window->marbleControl()->addGeoDataFile( file ); } } return app.exec(); } diff --git a/src/apps/marble-qt/qtmain.cpp b/src/apps/marble-qt/qtmain.cpp index 7d5575732..72c3ab597 100644 --- a/src/apps/marble-qt/qtmain.cpp +++ b/src/apps/marble-qt/qtmain.cpp @@ -1,302 +1,309 @@ // // 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-2007 Torsten Rahn // Copyright 2007 Inge Wallin // #include #include #include #include #include #include #include "QtMainWindow.h" #include "MapThemeManager.h" #include "MarbleWidgetInputHandler.h" #include "MarbleDirs.h" #include "MarbleDebug.h" #include "MarbleTest.h" #include "MarbleLocale.h" #include "GeoUriParser.h" #ifdef STATIC_BUILD #include Q_IMPORT_PLUGIN(qjpeg) Q_IMPORT_PLUGIN(qsvg) #endif #ifdef Q_OS_MACX //for getting app bundle path #include #endif using namespace Marble; // load translation file from normal "KDE Applications" packaging installation static bool loadTranslation(const QString &localeDirName, QApplication &app) { const QString subPath = QLatin1String("locale/") + localeDirName + QLatin1String("/LC_MESSAGES/marble_qt.qm"); const QString fullPath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, subPath); if (fullPath.isEmpty()) { return false; } QTranslator* translator = new QTranslator(&app); if (!translator->load(fullPath)) { delete translator; return false; } app.installTranslator(translator); return true; } // load KDE translators system based translations // TODO: document other possible supported translation systems, if any, and where their catalog files are static void loadTranslations(QApplication &app) { // Quote from ecm_create_qm_loader created code: // The way Qt translation system handles plural forms makes it necessary to // have a translation file which contains only plural forms for `en`. // That's why we load the `en` translation unconditionally, then load the // translation for the current locale to overload it. const QString en(QStringLiteral("en")); loadTranslation(en, app); QLocale locale = QLocale::system(); if (locale.name() != en) { if (!loadTranslation(locale.name(), app)) { loadTranslation(locale.bcp47Name(), app); } } } int main(int argc, char *argv[]) { QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps, true); QApplication app(argc, argv); app.setApplicationName( "Marble Virtual Globe" ); app.setOrganizationName( "KDE" ); app.setOrganizationDomain( "kde.org" ); #if QT_VERSION >= 0x050700 app.setDesktopFileName(QStringLiteral("org.kde.marble-qt")); #endif // Load Qt translation system catalog for libmarblewidget, the plugins and this app loadTranslations(app); app.setApplicationDisplayName(MainWindow::tr("Marble - Virtual Globe")); // For non static builds on mac and win // we need to be sure we can find the qt image // plugins. In mac be sure to look in the // application bundle... #ifdef Q_WS_WIN QApplication::addLibraryPath( QApplication::applicationDirPath() + QDir::separator() + QLatin1String("plugins")); #endif #ifdef Q_OS_MACX QApplication::instance()->setAttribute(Qt::AA_DontShowIconsInMenus); qDebug("Adding qt image plugins to plugin search path..."); CFURLRef myBundleRef = CFBundleCopyBundleURL(CFBundleGetMainBundle()); CFStringRef myMacPath = CFURLCopyFileSystemPath(myBundleRef, kCFURLPOSIXPathStyle); const char *mypPathPtr = CFStringGetCStringPtr(myMacPath,CFStringGetSystemEncoding()); CFRelease(myBundleRef); CFRelease(myMacPath); QString myPath(mypPathPtr); // if we are not in a bundle assume that the app is built // as a non bundle app and that image plugins will be // in system Qt frameworks. If the app is a bundle // lets try to set the qt plugin search path... if (myPath.contains(".app")) { myPath += QLatin1String("/Contents/plugins"); QApplication::addLibraryPath( myPath ); qDebug( "Added %s to plugin search path", qPrintable( myPath ) ); } #endif QString marbleDataPath; int dataPathIndex=0; QString mapThemeId; QString tour; QString coordinatesString; QString distanceString; QString geoUriString; MarbleGlobal::Profiles profiles = MarbleGlobal::getInstance()->profiles(); QStringList args = QApplication::arguments(); if ( args.contains( "-h" ) || args.contains( "--help" ) ) { qWarning() << "Usage: marble [options] [files]"; qWarning(); qWarning() << "[files] can be zero, one or more .kml and/or .gpx files to load and show."; qWarning(); qWarning() << "general options:"; qWarning() << " --marbledatapath= .... Overwrite the compile-time path to map themes and other data"; qWarning() << " --geo-uri= ............ Show map at given geo uri"; qWarning() << " --latlon= ..... Show map at given lat lon coordinates"; qWarning() << " --distance= ......... Set the distance of the observer to the globe (in km)"; qWarning() << " --map= ................. Use map id (e.g. \"earth/openstreetmap/openstreetmap.dgml\")"; qWarning() << " --tour= .............. Load a KML tour from the given file and play it"; qWarning(); qWarning() << "debug options:"; qWarning() << " --debug-info ............... write (more) debugging information to the console"; qWarning() << " --fps ...................... Show the paint performance (paint rate) in the top left corner"; qWarning() << " --runtimeTrace.............. Show the time spent and other debug info of each layer"; qWarning() << " --tile-id................... Write the identifier of texture tiles on top of them"; qWarning() << " --timedemo ................. Measure the paint performance while moving the map and quit"; - qWarning() << " --debug-polygons .............Display the polygon nodes and their index for debugging"; + qWarning() << " --debug-polygons ........... Display the polygon nodes and their index for debugging"; + qWarning() << " --debug-levels ............. Display OSM placemarks according to the level selected"; qWarning(); qWarning() << "profile options (note that marble should automatically detect which profile to use. Override that with the options below):"; qWarning() << " --highresolution ........... Enforce the profile for devices with high resolution (e.g. desktop computers)"; qWarning() << " --nohighresolution ......... Deactivate the profile for devices with high resolution (e.g. desktop computers)"; return 0; } for ( int i = 1; i < args.count(); ++i ) { const QString arg = args.at(i); if ( arg == QLatin1String( "--debug-info" ) ) { MarbleDebug::setEnabled( true ); } else if ( arg.startsWith( QLatin1String( "--marbledatapath=" ), Qt::CaseInsensitive ) ) { marbleDataPath = args.at(i).mid(17); } else if ( arg.compare( QLatin1String( "--marbledatapath" ), Qt::CaseInsensitive ) == 0 && i+1 < args.size() ) { dataPathIndex = i + 1; marbleDataPath = args.value( dataPathIndex ); ++i; } else if ( arg == QLatin1String( "--highresolution" ) ) { profiles |= MarbleGlobal::HighResolution; } else if ( arg == QLatin1String( "--nohighresolution" ) ) { profiles &= ~MarbleGlobal::HighResolution; } else if ( arg.startsWith( QLatin1String( "--latlon=" ), Qt::CaseInsensitive ) ) { coordinatesString = arg.mid(9); } else if ( arg.compare( QLatin1String( "--latlon" ), Qt::CaseInsensitive ) == 0 && i+1 < args.size() ) { ++i; coordinatesString = args.value( i ); } else if ( arg.compare( QLatin1String( "--geo-uri=" ), Qt::CaseInsensitive ) == 0 ) { geoUriString = arg.mid(10); } else if ( arg.compare( QLatin1String( "--geo-uri" ), Qt::CaseInsensitive ) == 0 && i+1 < args.size() ) { ++i; geoUriString = args.value(i); } else if ( arg.startsWith( QLatin1String( "--distance=" ), Qt::CaseInsensitive ) ) { distanceString = arg.mid(11); } else if ( arg.compare( QLatin1String( "--distance" ), Qt::CaseInsensitive ) == 0 && i+1 < args.size() ) { ++i; distanceString = args.value( i ); } else if ( arg.startsWith( QLatin1String( "--map=" ), Qt::CaseInsensitive ) ) { mapThemeId = arg.mid(6); } else if ( arg.compare( QLatin1String( "--map" ), Qt::CaseInsensitive ) == 0 && i+1 < args.size() ) { ++i; mapThemeId = args.value( i ); } else if ( arg.startsWith( QLatin1String( "--tour=" ), Qt::CaseInsensitive ) ) { tour = arg.mid(7); } else if ( arg.compare( QLatin1String( "--tour" ), Qt::CaseInsensitive ) == 0 && i+1 < args.size() ) { ++i; tour = args.value( i ); } } MarbleGlobal::getInstance()->setProfiles( profiles ); MarbleLocale::MeasurementSystem const measurement = (MarbleLocale::MeasurementSystem)QLocale::system().measurementSystem(); MarbleGlobal::getInstance()->locale()->setMeasurementSystem( measurement ); QVariantMap cmdLineSettings; if ( !mapThemeId.isEmpty() ) { cmdLineSettings.insert( QLatin1String("mapTheme"), QVariant(mapThemeId) ); } if ( !coordinatesString.isEmpty() ) { bool success = false; const GeoDataCoordinates coordinates = GeoDataCoordinates::fromString(coordinatesString, success); if ( success ) { QVariantList lonLat; lonLat << QVariant( coordinates.longitude(GeoDataCoordinates::Degree) ) << QVariant( coordinates.latitude(GeoDataCoordinates::Degree) ); cmdLineSettings.insert( QLatin1String("lonlat"), QVariant(lonLat) ); } } if ( !distanceString.isEmpty() ) { bool success = false; const qreal distance = distanceString.toDouble(&success); if ( success ) { cmdLineSettings.insert( QLatin1String("distance"), QVariant(distance) ); } } if ( !tour.isEmpty() ) { cmdLineSettings.insert( QLatin1String("tour"), QVariant(tour) ); } cmdLineSettings.insert( QLatin1String("geo-uri"), QVariant(geoUriString) ); MainWindow *window = new MainWindow( marbleDataPath, cmdLineSettings ); window->setAttribute( Qt::WA_DeleteOnClose, true ); // window->marbleWidget()->rotateTo( 0, 0, -90 ); // window->show(); for ( int i = 1; i < args.count(); ++i ) { const QString arg = args.at(i); if (arg == QLatin1String("--timedemo")) { window->resize(900, 640); MarbleTest marbleTest( window->marbleWidget() ); marbleTest.timeDemo(); return 0; } - else if (arg == QLatin1String("--fps")) { + + if (arg == QLatin1String("--fps")) { window->marbleControl()->marbleWidget()->setShowFrameRate( true ); } else if (arg == QLatin1String("--tile-id")) { window->marbleControl()->marbleWidget()->setShowTileId(true); } else if (arg == QLatin1String("--runtimeTrace")) { window->marbleControl()->marbleWidget()->setShowRuntimeTrace( true ); } else if (arg == QLatin1String("--debug-polygons")) { window->marbleControl()->marbleWidget()->setShowDebugPolygons( true ); } - else if ( i != dataPathIndex && QFile::exists( arg ) ) - window->addGeoDataFile( arg ); + else if ( i != dataPathIndex && QFile::exists( arg ) ) { + window->addGeoDataFile(arg); + } + else if (arg == QLatin1String("--debug-levels")) { + window->marbleWidget()->setDebugLevelTags(true); + } } auto const marbleWidget = window->marbleControl()->marbleWidget(); - bool const debugModeEnabled = marbleWidget->showRuntimeTrace() || marbleWidget->showDebugPolygons() || MarbleDebug::isEnabled(); + bool const debugModeEnabled = marbleWidget->showRuntimeTrace() || marbleWidget->showDebugPolygons() || + marbleWidget->debugLevelTags() || MarbleDebug::isEnabled(); marbleWidget->inputHandler()->setDebugModeEnabled(debugModeEnabled); return app.exec(); } diff --git a/src/lib/marble/MarbleInputHandler.cpp b/src/lib/marble/MarbleInputHandler.cpp index 63e874d5a..7999fc11c 100644 --- a/src/lib/marble/MarbleInputHandler.cpp +++ b/src/lib/marble/MarbleInputHandler.cpp @@ -1,990 +1,994 @@ // // 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-2007 Torsten Rahn // Copyright 2007 Inge Wallin // Copyright 2014 Adam Dabrowski // #include "MarbleInputHandler.h" #include #include #include #include #include #include #include #include #include "kineticmodel.h" #include "MarbleGlobal.h" #include "MarbleDebug.h" #include "MarbleMap.h" #include "GeoDataCoordinates.h" #include "MarbleAbstractPresenter.h" #include "ViewportParams.h" #include "AbstractFloatItem.h" #include "AbstractDataPluginItem.h" #include "RenderPlugin.h" namespace Marble { const int TOOLTIP_START_INTERVAL = 1000; class Q_DECL_HIDDEN MarbleInputHandler::Protected { public: Protected(MarbleAbstractPresenter *marblePresenter); MarbleAbstractPresenter *const m_marblePresenter; bool m_positionSignalConnected; QTimer *m_mouseWheelTimer; Qt::MouseButtons m_disabledMouseButtons; qreal m_wheelZoomTargetDistance; bool m_panViaArrowsEnabled; bool m_inertialEarthRotation; int m_steps; const int m_discreteZoomSteps = 120; }; MarbleInputHandler::Protected::Protected(MarbleAbstractPresenter *marblePresenter) : m_marblePresenter( marblePresenter ), m_positionSignalConnected( false ), m_mouseWheelTimer( 0 ), m_disabledMouseButtons( Qt::NoButton ), m_wheelZoomTargetDistance( 0.0 ), m_panViaArrowsEnabled( true ), m_inertialEarthRotation( true ), m_steps(0) { } MarbleInputHandler::MarbleInputHandler(MarbleAbstractPresenter *marblePresenter) : d(new Protected(marblePresenter)) { d->m_mouseWheelTimer = new QTimer( this ); connect(d->m_mouseWheelTimer, SIGNAL(timeout()), this, SLOT(restoreViewContext())); connect(d->m_marblePresenter->map(), SIGNAL(renderPluginInitialized(RenderPlugin*)), this, SLOT(installPluginEventFilter(RenderPlugin*))); } MarbleInputHandler::~MarbleInputHandler() { delete d->m_mouseWheelTimer; delete d; } void MarbleInputHandler::setPositionSignalConnected(bool connected) { d->m_positionSignalConnected = connected; } bool MarbleInputHandler::isPositionSignalConnected() const { return d->m_positionSignalConnected; } void MarbleInputHandler::setMouseButtonPopupEnabled(Qt::MouseButton mouseButton, bool enabled) { if (enabled) { d->m_disabledMouseButtons &= ~Qt::MouseButtons(mouseButton); } else { d->m_disabledMouseButtons |= mouseButton; } } bool MarbleInputHandler::isMouseButtonPopupEnabled(Qt::MouseButton mouseButton) const { return !(d->m_disabledMouseButtons & mouseButton); } void MarbleInputHandler::setPanViaArrowsEnabled(bool enabled) { d->m_panViaArrowsEnabled = enabled; } bool MarbleInputHandler::panViaArrowsEnabled() const { return d->m_panViaArrowsEnabled; } void MarbleInputHandler::setInertialEarthRotationEnabled(bool enabled) { d->m_inertialEarthRotation = enabled; } bool MarbleInputHandler::inertialEarthRotationEnabled() const { return d->m_inertialEarthRotation; } class Q_DECL_HIDDEN MarbleDefaultInputHandler::Private { public: Private(); ~Private(); QPixmap m_curpmtl; QPixmap m_curpmtc; QPixmap m_curpmtr; QPixmap m_curpmcr; QPixmap m_curpmcl; QPixmap m_curpmbl; QPixmap m_curpmbc; QPixmap m_curpmbr; QCursor m_arrowCur[3][3]; // Indicates if the left mouse button has been pressed already. bool m_leftPressed; // Indicates if the middle mouse button has been pressed already. bool m_midPressed; // The mouse pointer x position when the left mouse button has been pressed. int m_leftPressedX; // The mouse pointer y position when the left mouse button has been pressed. int m_leftPressedY; // The mouse pointer y position when the middle mouse button has been pressed. int m_midPressedY; int m_startingRadius; // Indicates if the right mouse button has been pressed already. bool m_rightPressed; // Point where the right mouse button has been pressed on. QPoint m_rightOrigin; // Position to calculate the heading. // Indicates previous position since mouse has been moved. QPoint m_rightPosition; // Indicates the heading when the right mouse button has been pressed // and mouse is moving. qreal m_heading; // The center longitude in radian when the left mouse button has been pressed. qreal m_leftPressedLon; // The center latitude in radian when the left mouse button has been pressed. qreal m_leftPressedLat; int m_dragThreshold; QTimer m_lmbTimer; QTimer m_pressAndHoldTimer; // Models to handle the kinetic spinning. KineticModel m_kineticSpinning; QPoint m_selectionOrigin; QPointer m_lastToolTipItem; QTimer m_toolTipTimer; QPoint m_toolTipPosition; }; MarbleDefaultInputHandler::Private::Private() : m_leftPressed(false), m_midPressed(false), m_rightPressed(false), m_heading(0), m_dragThreshold(MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen ? 15 : 3) { m_curpmtl.load(QStringLiteral(":/marble/cursor/tl.png")); m_curpmtc.load(QStringLiteral(":/marble/cursor/tc.png")); m_curpmtr.load(QStringLiteral(":/marble/cursor/tr.png")); m_curpmcr.load(QStringLiteral(":/marble/cursor/cr.png")); m_curpmcl.load(QStringLiteral(":/marble/cursor/cl.png")); m_curpmbl.load(QStringLiteral(":/marble/cursor/bl.png")); m_curpmbc.load(QStringLiteral(":/marble/cursor/bc.png")); m_curpmbr.load(QStringLiteral(":/marble/cursor/br.png")); m_arrowCur[0][0] = QCursor( m_curpmtl, 2, 2 ); m_arrowCur[1][0] = QCursor( m_curpmtc, 10, 3 ); m_arrowCur[2][0] = QCursor( m_curpmtr, 19, 2 ); m_arrowCur[0][1] = QCursor( m_curpmcl, 3, 10 ); m_arrowCur[1][1] = QCursor( Qt::OpenHandCursor ); m_arrowCur[2][1] = QCursor( m_curpmcr, 18, 10 ); m_arrowCur[0][2] = QCursor( m_curpmbl, 2, 19 ); m_arrowCur[1][2] = QCursor( m_curpmbc, 11, 18 ); m_arrowCur[2][2] = QCursor( m_curpmbr, 19, 19 ); } MarbleDefaultInputHandler::Private::~Private() { } MarbleDefaultInputHandler::MarbleDefaultInputHandler(MarbleAbstractPresenter *marblePresenter) : MarbleInputHandler(marblePresenter), d(new Private()) { d->m_toolTipTimer.setSingleShot(true); d->m_toolTipTimer.setInterval(TOOLTIP_START_INTERVAL); connect(&d->m_toolTipTimer, SIGNAL(timeout()), this, SLOT(openItemToolTip())); d->m_lmbTimer.setSingleShot(true); connect(&d->m_lmbTimer, SIGNAL(timeout()), this, SLOT(lmbTimeout())); d->m_kineticSpinning.setUpdateInterval(35); connect(&d->m_kineticSpinning, SIGNAL(positionChanged(qreal,qreal)), MarbleInputHandler::d->m_marblePresenter, SLOT(centerOn(qreal,qreal))); connect(&d->m_kineticSpinning, SIGNAL(headingChanged(qreal)), MarbleInputHandler::d->m_marblePresenter, SLOT(headingOn(qreal))); connect(&d->m_kineticSpinning, SIGNAL(finished()), SLOT(restoreViewContext())); // Left and right mouse button signals. connect(this, SIGNAL(rmbRequest(int,int)), this, SLOT(showRmbMenu(int,int))); connect(this, SIGNAL(lmbRequest(int,int)), this, SLOT(showLmbMenu(int,int))); d->m_pressAndHoldTimer.setInterval(800); d->m_pressAndHoldTimer.setSingleShot(true); connect(&d->m_pressAndHoldTimer, SIGNAL(timeout()), this, SLOT(handlePressAndHold())); } MarbleDefaultInputHandler::~MarbleDefaultInputHandler() { delete d; } void MarbleDefaultInputHandler::lmbTimeout() { if (!selectionRubber()->isVisible()) { qreal clickedLon = 0; qreal clickedLat = 0; bool isPointOnGlobe = MarbleInputHandler::d->m_marblePresenter->map()->geoCoordinates( d->m_leftPressedX, d->m_leftPressedY, clickedLon, clickedLat, GeoDataCoordinates::Degree ); emit lmbRequest(d->m_leftPressedX, d->m_leftPressedY); /** * emit mouse click only when the clicked * position is within the globe. */ if ( isPointOnGlobe ) { emit mouseClickGeoPosition( clickedLon, clickedLat, GeoDataCoordinates::Degree ); } } } void MarbleInputHandler::restoreViewContext() { // Needs to stop the timer since it repeats otherwise. d->m_mouseWheelTimer->stop(); // Redraw the map with the quality set for Still (if necessary). d->m_marblePresenter->setViewContext(Still); d->m_marblePresenter->map()->viewport()->resetFocusPoint(); d->m_wheelZoomTargetDistance = 0.0; } void MarbleDefaultInputHandler::hideSelectionIfCtrlReleased(QEvent *e) { if (selectionRubber()->isVisible() && e->type() == QEvent::MouseMove) { QMouseEvent *event = static_cast(e); if (!(event->modifiers() & Qt::ControlModifier)) { selectionRubber()->hide(); } } } bool MarbleDefaultInputHandler::handleDoubleClick(QMouseEvent *event) { qreal mouseLon; qreal mouseLat; const bool isMouseAboveMap = MarbleInputHandler::d->m_marblePresenter->map()->geoCoordinates(event->x(), event->y(), mouseLon, mouseLat, GeoDataCoordinates::Radian); if(isMouseAboveMap) { d->m_pressAndHoldTimer.stop(); d->m_lmbTimer.stop(); MarbleInputHandler::d->m_marblePresenter->moveTo(event->pos(), 0.67); } return acceptMouse(); } bool MarbleDefaultInputHandler::handleWheel(QWheelEvent *wheelevt) { MarbleAbstractPresenter *marblePresenter = MarbleInputHandler::d->m_marblePresenter; marblePresenter->setViewContext(Animation); if( (MarbleInputHandler::d->m_steps > 0 && wheelevt->delta() < 0) || (MarbleInputHandler::d->m_steps < 0 && wheelevt->delta() > 0) ) { MarbleInputHandler::d->m_steps = wheelevt->delta(); } else { MarbleInputHandler::d->m_steps += wheelevt->delta(); } if (marblePresenter->map()->discreteZoom()) { if(qAbs(MarbleInputHandler::d->m_steps) >= MarbleInputHandler::d->m_discreteZoomSteps) { marblePresenter->zoomAtBy(wheelevt->pos(), MarbleInputHandler::d->m_steps); MarbleInputHandler::d->m_steps = 0; } } else { qreal zoom = marblePresenter->zoom(); qreal target = MarbleInputHandler::d->m_wheelZoomTargetDistance; if (marblePresenter->animationsEnabled() && target > 0.0) { // Do not use intermediate (interpolated) distance values caused by animations zoom = marblePresenter->zoomFromDistance(target); } qreal newDistance = marblePresenter->distanceFromZoom(zoom + MarbleInputHandler::d->m_steps); MarbleInputHandler::d->m_wheelZoomTargetDistance = newDistance; marblePresenter->zoomAt(wheelevt->pos(), newDistance); if (MarbleInputHandler::d->m_inertialEarthRotation) { d->m_kineticSpinning.jumpToPosition(MarbleInputHandler::d->m_marblePresenter->centerLongitude(), MarbleInputHandler::d->m_marblePresenter->centerLatitude()); } MarbleInputHandler::d->m_steps = 0; } MarbleInputHandler::d->m_mouseWheelTimer->start(400); return true; } bool MarbleDefaultInputHandler::handlePinch(const QPointF ¢er, qreal scaleFactor, Qt::GestureState state) { qreal destLat; qreal destLon; MarbleAbstractPresenter *marblePresenter = MarbleInputHandler::d->m_marblePresenter; bool isValid = marblePresenter->map()->geoCoordinates(center.x(), center.y(), destLon, destLat, GeoDataCoordinates::Radian ); if (isValid) { marblePresenter->map()->viewport()->setFocusPoint(GeoDataCoordinates(destLon, destLat)); } qreal zoom, target, newDistance; qreal zoomDelta = scaleFactor > 1.0 ? scaleFactor : -1.0/scaleFactor; switch (state) { case Qt::NoGesture: break; case Qt::GestureStarted: marblePresenter->setViewContext(Animation); d->m_pressAndHoldTimer.stop(); d->m_lmbTimer.stop(); d->m_midPressed = false; d->m_leftPressed = false; break; case Qt::GestureUpdated: zoom = marblePresenter->zoom(); target = MarbleInputHandler::d->m_wheelZoomTargetDistance; if (marblePresenter->animationsEnabled() && target > 0.0) { // Do not use intermediate (interpolated) distance values caused by animations zoom = marblePresenter->zoomFromDistance(target); } newDistance = marblePresenter->distanceFromZoom(zoom + 20 * zoomDelta); MarbleInputHandler::d->m_wheelZoomTargetDistance = newDistance; marblePresenter->zoomAt(center.toPoint(), newDistance); break; case Qt::GestureFinished: marblePresenter->map()->viewport()->resetFocusPoint(); marblePresenter->setViewContext(Still); break; case Qt::GestureCanceled: marblePresenter->map()->viewport()->resetFocusPoint(); marblePresenter->setViewContext(Still); break; } return true; } bool MarbleDefaultInputHandler::handleGesture(QGestureEvent *ge) { QPinchGesture *pinch = static_cast(ge->gesture(Qt::PinchGesture)); if (!pinch) { return false; } qreal scaleFactor = pinch->scaleFactor(); QPointF center = pinch->centerPoint(); return handlePinch(center, scaleFactor, pinch->state()); } void MarbleDefaultInputHandler::checkReleasedMove(QMouseEvent *event) { // To prevent error from lost MouseButtonRelease events if (event->type() == QEvent::MouseMove && !(event->buttons() & Qt::LeftButton)) { if (d->m_leftPressed) { d->m_leftPressed = false; if (MarbleInputHandler::d->m_inertialEarthRotation) { d->m_kineticSpinning.start(); } else { MarbleInputHandler::d->m_marblePresenter->setViewContext(Still); } } } if (event->type() == QEvent::MouseMove && !(event->buttons() & Qt::MidButton)) { d->m_midPressed = false; } } void MarbleDefaultInputHandler::handleMouseButtonPress(QMouseEvent *event) { if (event->button() == Qt::LeftButton ) { d->m_pressAndHoldTimer.start(); handleLeftMouseButtonPress(event); } if ( event->button() == Qt::MidButton ) { handleMiddleMouseButtonPress(event); } if ( event->button() == Qt::RightButton ) { handleRightMouseButtonPress(event); } } void MarbleDefaultInputHandler::handleLeftMouseButtonPress(QMouseEvent *event) { // silently enable the animation context without triggering a repaint MarbleInputHandler::d->m_marblePresenter->map()->blockSignals(true); MarbleInputHandler::d->m_marblePresenter->setViewContext(Animation); MarbleInputHandler::d->m_marblePresenter->map()->blockSignals(false); if (isMouseButtonPopupEnabled(Qt::LeftButton)) { d->m_lmbTimer.start(400); } d->m_leftPressed = true; d->m_midPressed = false; selectionRubber()->hide(); // On the single event of a mouse button press these // values get stored, to enable us to e.g. calculate the // distance of a mouse drag while the mouse button is // still down. d->m_leftPressedX = event->x(); d->m_leftPressedY = event->y(); // Calculate translation of center point d->m_leftPressedLon = MarbleInputHandler::d->m_marblePresenter->centerLongitude(); d->m_leftPressedLat = MarbleInputHandler::d->m_marblePresenter->centerLatitude(); if (MarbleInputHandler::d->m_inertialEarthRotation) { d->m_kineticSpinning.stop(); d->m_kineticSpinning.setPosition(d->m_leftPressedLon, d->m_leftPressedLat); } if (event->modifiers() & Qt::ControlModifier) { mDebug() << Q_FUNC_INFO << "Starting selection"; d->m_pressAndHoldTimer.stop(); d->m_lmbTimer.stop(); d->m_selectionOrigin = event->pos(); selectionRubber()->setGeometry(QRect(d->m_selectionOrigin, QSize())); selectionRubber()->show(); } } void MarbleDefaultInputHandler::handleMiddleMouseButtonPress(QMouseEvent *event) { d->m_midPressed = true; d->m_leftPressed = false; d->m_startingRadius = MarbleInputHandler::d->m_marblePresenter->radius(); d->m_midPressedY = event->y(); if (MarbleInputHandler::d->m_inertialEarthRotation) { d->m_kineticSpinning.start(); } selectionRubber()->hide(); MarbleInputHandler::d->m_marblePresenter->setViewContext(Animation); } void MarbleDefaultInputHandler::handleRightMouseButtonPress(QMouseEvent *event) { d->m_rightPressed = true; d->m_rightOrigin = event->pos(); d->m_rightPosition = event->pos(); d->m_heading = MarbleInputHandler::d->m_marblePresenter->map()->heading(); if (MarbleInputHandler::d->m_inertialEarthRotation) { d->m_kineticSpinning.stop(); d->m_kineticSpinning.setHeading(d->m_heading); } } void MarbleDefaultInputHandler::handleMouseButtonRelease(QMouseEvent *event) { if (event->button() == Qt::LeftButton) { d->m_pressAndHoldTimer.stop(); //emit current coordinates to be interpreted //as requested emit mouseClickScreenPosition(d->m_leftPressedX, d->m_leftPressedY); d->m_leftPressed = false; if (MarbleInputHandler::d->m_inertialEarthRotation) { d->m_kineticSpinning.start(); } else { MarbleInputHandler::d->m_marblePresenter->setViewContext(Still); } } if (event->button() == Qt::MidButton) { d->m_midPressed = false; MarbleInputHandler::d->m_marblePresenter->setViewContext(Still); } if (event->type() == QEvent::MouseButtonRelease && event->button() == Qt::RightButton) { if (d->m_rightOrigin == event->pos()) { emit rmbRequest(event->x(), event->y()); } d->m_rightPressed = false; if (MarbleInputHandler::d->m_inertialEarthRotation) { d->m_kineticSpinning.start(); } else { MarbleInputHandler::d->m_marblePresenter->setViewContext(Still); } } if (event->type() == QEvent::MouseButtonRelease && event->button() == Qt::LeftButton && selectionRubber()->isVisible()) { mDebug() << Q_FUNC_INFO << "Leaving selection"; MarbleInputHandler::d->m_marblePresenter->setSelection(selectionRubber()->geometry()); selectionRubber()->hide(); } } void MarbleDefaultInputHandler::notifyPosition(bool isMouseAboveMap, qreal mouseLon, qreal mouseLat) { // emit the position string only if the signal got attached if (MarbleInputHandler::d->m_positionSignalConnected) { if (!isMouseAboveMap) { emit mouseMoveGeoPosition(QCoreApplication::translate( "Marble", NOT_AVAILABLE)); } else { QString position = GeoDataCoordinates(mouseLon, mouseLat).toString(); emit mouseMoveGeoPosition(position); } } } void MarbleDefaultInputHandler::adjustCursorShape(const QPoint &mousePosition, const QPoint &mouseDirection) { // Find out if there are data items and if one has defined an action QList dataItems = MarbleInputHandler::d->m_marblePresenter->map()->whichItemAt(mousePosition); bool dataAction = false; QPointer toolTipItem; QList::iterator it = dataItems.begin(); QList::iterator const end = dataItems.end(); for (; it != end && dataAction == false && toolTipItem.isNull(); ++it) { if ((*it)->action()) { dataAction = true; } if (!(*it)->toolTip().isNull() && toolTipItem.isNull()) { toolTipItem = (*it); } } if (toolTipItem.isNull()) { d->m_toolTipTimer.stop(); } else if (!( d->m_lastToolTipItem.data() == toolTipItem.data())) { d->m_toolTipTimer.start(); d->m_lastToolTipItem = toolTipItem; d->m_toolTipPosition = mousePosition; } else { if (!d->m_toolTipTimer.isActive()) { d->m_toolTipTimer.start(); } d->m_toolTipPosition = mousePosition; } if (!dataAction && !MarbleInputHandler::d->m_marblePresenter->map()->hasFeatureAt(mousePosition)) { if (!d->m_leftPressed) { d->m_arrowCur [1][1] = QCursor(Qt::OpenHandCursor); } else { d->m_arrowCur [1][1] = QCursor(Qt::ClosedHandCursor); } } else { if (!d->m_leftPressed) { d->m_arrowCur [1][1] = QCursor(Qt::PointingHandCursor); } } if (panViaArrowsEnabled()) { setCursor(d->m_arrowCur[mouseDirection.x()+1][mouseDirection.y()+1]); } else { setCursor(d->m_arrowCur[1][1]); } } QPoint MarbleDefaultInputHandler::mouseMovedOutside(QMouseEvent *event) { //Returns a 2d vector representing the direction in which the mouse left int dirX = 0; int dirY = 0; int polarity = MarbleInputHandler::d->m_marblePresenter->viewport()->polarity(); if (d->m_leftPressed) { d->m_leftPressed = false; if (MarbleInputHandler::d->m_inertialEarthRotation) { d->m_kineticSpinning.start(); } } QRect boundingRect = MarbleInputHandler::d->m_marblePresenter->viewport()->mapRegion().boundingRect(); if (boundingRect.width() != 0) { dirX = (int)( 3 * (event->x() - boundingRect.left()) / boundingRect.width()) - 1; } if (dirX > 1) { dirX = 1; } if (dirX < -1) { dirX = -1; } if (boundingRect.height() != 0) { dirY = (int)(3 * (event->y() - boundingRect.top()) / boundingRect.height()) - 1; } if (dirY > 1) { dirY = 1; } if (dirY < -1) { dirY = -1; } if (event->button() == Qt::LeftButton && event->type() == QEvent::MouseButtonPress && panViaArrowsEnabled() && !d->m_kineticSpinning.hasVelocity()) { d->m_pressAndHoldTimer.stop(); d->m_lmbTimer.stop(); qreal moveStep = MarbleInputHandler::d->m_marblePresenter->moveStep(); if (polarity < 0) { MarbleInputHandler::d->m_marblePresenter->rotateBy(-moveStep * (qreal)(+dirX), moveStep * (qreal)(+dirY)); } else { MarbleInputHandler::d->m_marblePresenter->rotateBy(-moveStep * (qreal)(-dirX), moveStep * (qreal)(+dirY)); } } if (!MarbleInputHandler::d->m_inertialEarthRotation) { MarbleInputHandler::d->m_marblePresenter->setViewContext(Still); } return QPoint(dirX, dirY); } bool MarbleDefaultInputHandler::handleMouseEvent(QMouseEvent *event) { QPoint direction; checkReleasedMove(event); // Do not handle (and therefore eat) mouse press and release events // that occur above visible float items. Mouse motion events are still // handled, however. if (event->type() != QEvent::MouseMove && !selectionRubber()->isVisible()) { auto const floatItems = MarbleInputHandler::d->m_marblePresenter->map()->floatItems(); for (AbstractFloatItem *floatItem: floatItems) { if ( floatItem->enabled() && floatItem->visible() && floatItem->contains( event->pos() ) ) { d->m_pressAndHoldTimer.stop(); d->m_lmbTimer.stop(); return false; } } } qreal mouseLon; qreal mouseLat; const bool isMouseAboveMap = MarbleInputHandler::d->m_marblePresenter->map()->geoCoordinates(event->x(), event->y(), mouseLon, mouseLat, GeoDataCoordinates::Radian); notifyPosition(isMouseAboveMap, mouseLon, mouseLat); QPoint mousePosition(event->x(), event->y()); if (isMouseAboveMap || selectionRubber()->isVisible() || MarbleInputHandler::d->m_marblePresenter->map()->hasFeatureAt(mousePosition)) { if (event->type() == QEvent::MouseButtonPress) { handleMouseButtonPress(event); } if (event->type() == QEvent::MouseButtonRelease) { handleMouseButtonRelease(event); } // Regarding all kinds of mouse moves: if (d->m_leftPressed && !selectionRubber()->isVisible()) { qreal radius = (qreal)(MarbleInputHandler::d->m_marblePresenter->radius()); qreal deltax = event->x() - d->m_leftPressedX; qreal deltay = event->y() - d->m_leftPressedY; if (qAbs(deltax) > d->m_dragThreshold || qAbs(deltay) > d->m_dragThreshold || !d->m_lmbTimer.isActive()) { MarbleInputHandler::d->m_marblePresenter->setViewContext(Animation); d->m_pressAndHoldTimer.stop(); d->m_lmbTimer.stop(); const Quaternion rotation = Quaternion::fromEuler( 0, 0, MarbleInputHandler::d->m_marblePresenter->map()->heading() * DEG2RAD ); Quaternion quat = Quaternion::fromSpherical( - M_PI/2 * deltax / radius, + M_PI/2 * deltay / radius ); quat.rotateAroundAxis( rotation ); qreal lon, lat; quat.getSpherical( lon, lat ); const qreal posLon = d->m_leftPressedLon + RAD2DEG * lon; const qreal posLat = d->m_leftPressedLat + RAD2DEG * lat; MarbleInputHandler::d->m_marblePresenter->centerOn(posLon, posLat); if (MarbleInputHandler::d->m_inertialEarthRotation) { d->m_kineticSpinning.setPosition(posLon, posLat); } } } if (d->m_midPressed) { int eventy = event->y(); int dy = d->m_midPressedY - eventy; MarbleInputHandler::d->m_marblePresenter->setRadius(d->m_startingRadius * pow(1.005, dy)); } if (d->m_rightPressed) { qreal centerX, centerY; MarbleInputHandler::d->m_marblePresenter->map()->screenCoordinates( MarbleInputHandler::d->m_marblePresenter->centerLongitude(), MarbleInputHandler::d->m_marblePresenter->centerLatitude(), centerX, centerY); // Deltas from previous position. int dx = event->x() - d->m_rightPosition.x(); int dy = event->y() - d->m_rightPosition.y(); d->m_rightPosition = event->pos(); // Moving on the bottom should be opposite direction. int sign = event->y() > centerY ? -1 : 1; // Left top and right bottom sides for y axis should be opposite direction. if ((event->x() < centerX && event->y() < centerY) || (event->x() > centerX && event->y() > centerY)) { dy *= -1; } const qreal speedFactor = 0.3; d->m_heading += (dx + dy) * sign * speedFactor; MarbleInputHandler::d->m_marblePresenter->map()->setHeading(d->m_heading); if (MarbleInputHandler::d->m_inertialEarthRotation) { d->m_kineticSpinning.setHeading(d->m_heading); } } if (selectionRubber()->isVisible()) { // We change selection. selectionRubber()->setGeometry(QRect(d->m_selectionOrigin, event->pos()).normalized()); } } else { direction = mouseMovedOutside(event); } if (MarbleInputHandler::d->m_marblePresenter->viewContext() != Animation) { adjustCursorShape(mousePosition, direction); } return acceptMouse(); } bool MarbleDefaultInputHandler::acceptMouse() { // let others, especially float items, still process the event // Note: This caused a bug in combination with oxygen, see https://bugs.kde.org/show_bug.cgi?id=242414 // and changing it a related regression, see https://bugs.kde.org/show_bug.cgi?id=324862 return false; } bool MarbleDefaultInputHandler::eventFilter(QObject* o, QEvent* e) { Q_UNUSED(o); if (layersEventFilter(o, e)) { return true; } hideSelectionIfCtrlReleased(e); switch (e->type()) { case QEvent::TouchBegin: case QEvent::TouchUpdate: case QEvent::TouchEnd: return handleTouch(static_cast(e)); case QEvent::KeyPress: return handleKeyPress(static_cast(e)); case QEvent::Gesture: return handleGesture(static_cast(e)); case QEvent::Wheel: return handleWheel(static_cast(e)); case QEvent::MouseButtonDblClick: return handleDoubleClick(static_cast(e)); case QEvent::MouseButtonPress: case QEvent::MouseButtonRelease: case QEvent::MouseMove: return handleMouseEvent(static_cast(e)); default: return false; } } bool MarbleDefaultInputHandler::handleTouch(QTouchEvent*) { return false; //reimplement to handle in cases of QML and PinchArea element } bool MarbleDefaultInputHandler::handleKeyPress(QKeyEvent* event) { if ( event->type() == QEvent::KeyPress ) { MarbleAbstractPresenter *marblePresenter = MarbleInputHandler::d->m_marblePresenter; bool handled = true; switch ( event->key() ) { case Qt::Key_Left: marblePresenter->moveByStep(-1, 0); break; case Qt::Key_Right: marblePresenter->moveByStep(1, 0); break; case Qt::Key_Up: marblePresenter->moveByStep(0, -1); break; case Qt::Key_Down: marblePresenter->moveByStep(0, 1); break; case Qt::Key_Plus: - marblePresenter->zoomIn(); + if (event->modifiers() != Qt::ControlModifier) { + marblePresenter->zoomIn(); + } break; case Qt::Key_Minus: - marblePresenter->zoomOut(); + if (event->modifiers() != Qt::ControlModifier) { + marblePresenter->zoomOut(); + } break; case Qt::Key_Home: marblePresenter->goHome(); break; default: handled = false; break; } return handled; } return false; } void MarbleDefaultInputHandler::handleMouseButtonPressAndHold(const QPoint &) { // Default implementation does nothing } void MarbleDefaultInputHandler::handlePressAndHold() { handleMouseButtonPressAndHold(QPoint(d->m_leftPressedX, d->m_leftPressedY)); } QPointer MarbleDefaultInputHandler::lastToolTipItem() { return d->m_lastToolTipItem; } QTimer* MarbleDefaultInputHandler::toolTipTimer() { return &d->m_toolTipTimer; } QPoint MarbleDefaultInputHandler::toolTipPosition() { return d->m_toolTipPosition; } } #include "moc_MarbleInputHandler.cpp" diff --git a/src/lib/marble/MarbleMap.cpp b/src/lib/marble/MarbleMap.cpp index d62642a8a..6d990651d 100644 --- a/src/lib/marble/MarbleMap.cpp +++ b/src/lib/marble/MarbleMap.cpp @@ -1,1466 +1,1489 @@ // // 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 #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( const 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; 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_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())); /* * 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( parent, SIGNAL(radiusChanged(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 { auto const tileZoomLevel = qMax(d->m_textureLayer.tileZoomLevel(), d->m_vectorTileLayer.tileZoomLevel()); return tileZoomLevel >= 0 ? tileZoomLevel : qMin(qMax(qLn(d->m_viewport.radius()*4/256)/qLn(2.0), 1), d->m_styleBuilder.maximumZoomLevel()); } 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(); } GeoDataRelation::RelationTypes MarbleMap::visibleRelationTypes() const { 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( const 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() { auto const tileZoomLevel = q->tileZoomLevel(); m_geometryLayer.setTileLevel(tileZoomLevel); m_placemarkLayer.setTileLevel(tileZoomLevel); emit q->tileLevelChanged(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::setLevelTagDebugModeEnabled(bool visible) +{ + if (visible != d->m_geometryLayer.levelTagDebugModeEnabled()) { + d->m_geometryLayer.setLevelTagDebugModeEnabled(visible); + emit repaintNeeded(); + } +} + +bool MarbleMap::levelTagDebugModeEnabled() const +{ + return d->m_geometryLayer.levelTagDebugModeEnabled(); +} + +void MarbleMap::setDebugLevelTag(int level) +{ + d->m_geometryLayer.setDebugLevelTag(level); +} + +int MarbleMap::debugLevelTag() const +{ + return d->m_geometryLayer.debugLevelTag(); +} + void MarbleMap::setShowBackground( bool visible ) { d->m_layerManager.setShowBackground( visible ); } void MarbleMap::setVisibleRelationTypes(GeoDataRelation::RelationTypes relationTypes) { 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; } qreal MarbleMap::heading() const { return d->m_viewport.heading() * RAD2DEG; } void MarbleMap::setHeading( qreal heading ) { d->m_viewport.setHeading( heading * DEG2RAD ); d->m_textureLayer.setNeedsUpdate(); emit visibleLatLonAltBoxChanged( d->m_viewport.viewLatLonAltBox() ); } } #include "moc_MarbleMap.cpp" diff --git a/src/lib/marble/MarbleMap.h b/src/lib/marble/MarbleMap.h index 2ffec6941..19ae1be78 100644 --- a/src/lib/marble/MarbleMap.h +++ b/src/lib/marble/MarbleMap.h @@ -1,794 +1,809 @@ // // 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; qreal heading() 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; 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 ); void setHeading( qreal heading ); /** * @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; + + /** + * @brief Set whether to enter the debug mode for + * level tags + * @param visible visibility according to OSM level tags + */ + void setLevelTagDebugModeEnabled(bool visible); + + bool levelTagDebugModeEnabled() const; + + void setDebugLevelTag(int level); + + int debugLevelTag() const; + + void setShowBackground( bool visible ); 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 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/MarbleWidget.cpp b/src/lib/marble/MarbleWidget.cpp index 3c2f31967..3a656f77a 100644 --- a/src/lib/marble/MarbleWidget.cpp +++ b/src/lib/marble/MarbleWidget.cpp @@ -1,1232 +1,1252 @@ // // 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-2007 Torsten Rahn // Copyright 2007 Inge Wallin // Copyright 2010-2012 Bernhard Beschow // Copyright 2012 Mohammed Nafees // #include "MarbleWidget.h" #include #include #include #include #include #include #include #include #include #include "DataMigration.h" #include "FpsLayer.h" #include "FileManager.h" #include "GeoDataLatLonAltBox.h" #include "GeoDataPlacemark.h" #include "GeoDataLookAt.h" #include "GeoPainter.h" #include "MarbleClock.h" #include "MarbleDebug.h" #include "MarbleDirs.h" #include "MarbleLocale.h" #include "MarbleMap.h" #include "MarbleModel.h" #include "MarbleWidgetInputHandler.h" #include "MarbleWidgetPopupMenu.h" #include "Planet.h" #include "PopupLayer.h" #include "RenderState.h" #include "RenderPlugin.h" #include "SunLocator.h" #include "TileCreatorDialog.h" #include "ViewportParams.h" #include "routing/RoutingLayer.h" #include "MarbleAbstractPresenter.h" #include "StyleBuilder.h" namespace Marble { class MarbleWidget::CustomPaintLayer : public LayerInterface { public: explicit CustomPaintLayer( MarbleWidget *widget ) : m_widget( widget ) { } 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 ); painter->setPen( Qt::black ); m_widget->customPaint( painter ); return true; } qreal zValue() const override { return 1.0e7; } RenderState renderState() const override { return RenderState(QStringLiteral("Custom Widget Paint")); } QString runtimeTrace() const override { return QStringLiteral("MarbleWidget::CustomPaintLayer"); } private: MarbleWidget *const m_widget; }; class MarbleWidgetPrivate { public: explicit MarbleWidgetPrivate( MarbleWidget *parent ) : m_widget( parent ), m_model(), m_map( &m_model ), m_presenter( &m_map ), m_inputhandler( 0 ), m_routingLayer( 0 ), m_mapInfoDialog( 0 ), m_customPaintLayer( parent ), m_popupmenu( 0 ), m_showFrameRate( false ) { } ~MarbleWidgetPrivate() { m_map.removeLayer( &m_customPaintLayer ); m_map.removeLayer( m_mapInfoDialog ); delete m_mapInfoDialog; delete m_popupmenu; } void construct(); void updateMapTheme(); void setInputHandler(); void setInputHandler( MarbleWidgetInputHandler *handler ); /** * @brief Update widget flags and cause a full repaint * * The background of the widget only needs to be redrawn in certain cases. This * method sets the widget flags accordingly and triggers a repaint. */ void updateSystemBackgroundAttribute(); MarbleWidget *const m_widget; MarbleModel m_model; MarbleMap m_map; MarbleAbstractPresenter m_presenter; MarbleWidgetInputHandler *m_inputhandler; RoutingLayer *m_routingLayer; PopupLayer *m_mapInfoDialog; MarbleWidget::CustomPaintLayer m_customPaintLayer; MarbleWidgetPopupMenu *m_popupmenu; bool m_showFrameRate; }; MarbleWidget::MarbleWidget(QWidget *parent) : QWidget( parent ), d( new MarbleWidgetPrivate( this ) ) { // setAttribute( Qt::WA_PaintOnScreen, true ); d->construct(); } MarbleWidget::~MarbleWidget() { // Remove and delete an existing InputHandler // initialized in d->construct() setInputHandler( 0 ); delete d; } void MarbleWidgetPrivate::construct() { QPointer dataMigration = new DataMigration( m_widget ); dataMigration->exec(); delete dataMigration; // Widget settings m_widget->setMinimumSize( 200, 300 ); m_widget->setFocusPolicy( Qt::WheelFocus ); m_widget->setFocus( Qt::OtherFocusReason ); // Set background: black. m_widget->setPalette( QPalette ( Qt::black ) ); // Set whether the black space gets displayed or the earth gets simply // displayed on the widget background. m_widget->setAutoFillBackground( true ); // Initialize the map and forward some signals. m_map.setSize( m_widget->width(), m_widget->height() ); m_map.setShowFrameRate( false ); // never let the map draw the frame rate, // we do this differently here in the widget m_widget->connect( &m_presenter, SIGNAL(regionSelected(QList)), m_widget, SIGNAL(regionSelected(QList)) ); m_widget->connect( &m_presenter, SIGNAL(zoomChanged(int)), m_widget, SIGNAL(zoomChanged(int)) ); m_widget->connect( &m_presenter, SIGNAL(distanceChanged(QString)), m_widget, SIGNAL(distanceChanged(QString)) ); // forward some signals of m_map m_widget->connect( &m_map, SIGNAL(visibleLatLonAltBoxChanged(GeoDataLatLonAltBox)), m_widget, SIGNAL(visibleLatLonAltBoxChanged(GeoDataLatLonAltBox)) ); m_widget->connect( &m_map, SIGNAL(projectionChanged(Projection)), m_widget, SIGNAL(projectionChanged(Projection)) ); m_widget->connect( &m_map, SIGNAL(tileLevelChanged(int)), m_widget, SIGNAL(tileLevelChanged(int)) ); m_widget->connect( &m_map, SIGNAL(framesPerSecond(qreal)), m_widget, SIGNAL(framesPerSecond(qreal)) ); m_widget->connect( &m_map, SIGNAL(viewContextChanged(ViewContext)), m_widget, SLOT(setViewContext(ViewContext)) ); m_widget->connect( &m_map, SIGNAL(pluginSettingsChanged()), m_widget, SIGNAL(pluginSettingsChanged()) ); m_widget->connect( &m_map, SIGNAL(renderPluginInitialized(RenderPlugin*)), m_widget, SIGNAL(renderPluginInitialized(RenderPlugin*)) ); // react to some signals of m_map m_widget->connect( &m_map, SIGNAL(themeChanged(QString)), m_widget, SLOT(updateMapTheme()) ); m_widget->connect( &m_map, SIGNAL(viewContextChanged(ViewContext)), m_widget, SIGNAL(viewContextChanged(ViewContext)) ); m_widget->connect( &m_map, SIGNAL(repaintNeeded(QRegion)), m_widget, SLOT(update()) ); m_widget->connect( &m_map, SIGNAL(visibleLatLonAltBoxChanged(GeoDataLatLonAltBox)), m_widget, SLOT(updateSystemBackgroundAttribute()) ); m_widget->connect( &m_map, SIGNAL(renderStatusChanged(RenderStatus)), m_widget, SIGNAL(renderStatusChanged(RenderStatus)) ); m_widget->connect( &m_map, SIGNAL(renderStateChanged(RenderState)), m_widget, SIGNAL(renderStateChanged(RenderState)) ); m_widget->connect( m_model.fileManager(), SIGNAL(centeredDocument(GeoDataLatLonBox)), m_widget, SLOT(centerOn(GeoDataLatLonBox)) ); // Show a progress dialog when the model calculates new map tiles. m_widget->connect( &m_model, SIGNAL( creatingTilesStart( TileCreator*, const QString&, const QString& ) ), m_widget, SLOT( creatingTilesStart( TileCreator*, const QString&, const QString& ) ) ); m_popupmenu = new MarbleWidgetPopupMenu( m_widget, &m_model ); m_routingLayer = new RoutingLayer( m_widget, m_widget ); m_routingLayer->setPlacemarkModel( 0 ); QObject::connect( m_routingLayer, SIGNAL(repaintNeeded(QRect)), m_widget, SLOT(update()) ); m_mapInfoDialog = new PopupLayer( m_widget, m_widget ); m_mapInfoDialog->setVisible( false ); m_widget->connect( m_mapInfoDialog, SIGNAL(repaintNeeded()), m_widget, SLOT(update()) ); m_map.addLayer( m_mapInfoDialog ); setInputHandler(); m_widget->setMouseTracking( true ); m_map.addLayer( &m_customPaintLayer ); m_widget->connect( m_inputhandler, SIGNAL(mouseClickGeoPosition(qreal,qreal,GeoDataCoordinates::Unit)), m_widget, SIGNAL(highlightedPlacemarksChanged(qreal,qreal,GeoDataCoordinates::Unit)) ); m_widget->setHighlightEnabled( true ); } void MarbleWidgetPrivate::setInputHandler() { setInputHandler( new MarbleWidgetInputHandler( &m_presenter, m_widget ) ); } void MarbleWidgetPrivate::setInputHandler( MarbleWidgetInputHandler *handler ) { delete m_inputhandler; m_inputhandler = handler; if ( m_inputhandler ) { m_widget->installEventFilter( m_inputhandler ); QObject::connect( m_inputhandler, SIGNAL(mouseClickScreenPosition(int,int)), m_widget, SLOT(notifyMouseClick(int,int)) ); QObject::connect( m_inputhandler, SIGNAL(mouseMoveGeoPosition(QString)), m_widget, SIGNAL(mouseMoveGeoPosition(QString)) ); } } void MarbleWidgetPrivate::updateSystemBackgroundAttribute() { // We only have to repaint the background every time if the earth // doesn't cover the whole image. const bool isOn = m_map.viewport()->mapCoversViewport() && !m_model.mapThemeId().isEmpty(); m_widget->setAttribute( Qt::WA_NoSystemBackground, isOn ); } // ---------------------------------------------------------------- MarbleModel *MarbleWidget::model() { return &d->m_model; } const MarbleModel *MarbleWidget::model() const { return &d->m_model; } ViewportParams* MarbleWidget::viewport() { return d->m_map.viewport(); } const ViewportParams* MarbleWidget::viewport() const { return d->m_map.viewport(); } MarbleWidgetPopupMenu *MarbleWidget::popupMenu() { return d->m_popupmenu; } void MarbleWidget::setInputHandler( MarbleWidgetInputHandler *handler ) { d->setInputHandler(handler); } MarbleWidgetInputHandler *MarbleWidget::inputHandler() const { return d->m_inputhandler; } int MarbleWidget::radius() const { return d->m_map.radius(); } void MarbleWidget::setRadius( int radius ) { d->m_map.setRadius( radius ); } qreal MarbleWidget::moveStep() const { return d->m_presenter.moveStep(); } int MarbleWidget::zoom() const { return d->m_presenter.logzoom(); } int MarbleWidget::tileZoomLevel() const { return d->m_map.tileZoomLevel(); } int MarbleWidget::minimumZoom() const { return d->m_map.minimumZoom(); } int MarbleWidget::maximumZoom() const { return d->m_map.maximumZoom(); } QVector MarbleWidget::whichFeatureAt( const QPoint &curpos ) const { return d->m_map.whichFeatureAt( curpos ); } QList MarbleWidget::whichItemAt( const QPoint &curpos ) const { return d->m_map.whichItemAt( curpos ); } void MarbleWidget::addLayer( LayerInterface *layer ) { d->m_map.addLayer( layer ); } void MarbleWidget::removeLayer( LayerInterface *layer ) { d->m_map.removeLayer( layer ); } Marble::TextureLayer* MarbleWidget::textureLayer() const { return d->m_map.textureLayer(); } QPixmap MarbleWidget::mapScreenShot() { return QPixmap::grabWidget( this ); } RenderStatus MarbleWidget::renderStatus() const { return d->m_map.renderStatus(); } RenderState MarbleWidget::renderState() const { return d->m_map.renderState(); } void MarbleWidget::setHighlightEnabled(bool enabled) { if ( enabled ) { connect( this, SIGNAL(highlightedPlacemarksChanged(qreal,qreal,GeoDataCoordinates::Unit)), &d->m_map, SIGNAL(highlightedPlacemarksChanged(qreal,qreal,GeoDataCoordinates::Unit)), Qt::UniqueConnection ); } else { disconnect( this, SIGNAL(highlightedPlacemarksChanged(qreal,qreal,GeoDataCoordinates::Unit)), &d->m_map, SIGNAL(highlightedPlacemarksChanged(qreal,qreal,GeoDataCoordinates::Unit)) ); } } bool MarbleWidget::showOverviewMap() const { return d->m_map.showOverviewMap(); } bool MarbleWidget::showScaleBar() const { return d->m_map.showScaleBar(); } bool MarbleWidget::showCompass() const { return d->m_map.showCompass(); } bool MarbleWidget::showClouds() const { return d->m_map.showClouds(); } bool MarbleWidget::showSunShading() const { return d->m_map.showSunShading(); } bool MarbleWidget::showCityLights() const { return d->m_map.showCityLights(); } bool MarbleWidget::isLockedToSubSolarPoint() const { return d->m_map.isLockedToSubSolarPoint(); } bool MarbleWidget::isSubSolarPointIconVisible() const { return d->m_map.isSubSolarPointIconVisible(); } bool MarbleWidget::showAtmosphere() const { return d->m_map.showAtmosphere(); } bool MarbleWidget::showCrosshairs() const { return d->m_map.showCrosshairs(); } bool MarbleWidget::showGrid() const { return d->m_map.showGrid(); } bool MarbleWidget::showPlaces() const { return d->m_map.showPlaces(); } bool MarbleWidget::showCities() const { return d->m_map.showCities(); } bool MarbleWidget::showTerrain() const { return d->m_map.showTerrain(); } bool MarbleWidget::showOtherPlaces() const { return d->m_map.showOtherPlaces(); } bool MarbleWidget::showRelief() const { return d->m_map.showRelief(); } bool MarbleWidget::showIceLayer() const { return d->m_map.showIceLayer(); } bool MarbleWidget::showBorders() const { return d->m_map.showBorders(); } bool MarbleWidget::showRivers() const { return d->m_map.showRivers(); } bool MarbleWidget::showLakes() const { return d->m_map.showLakes(); } bool MarbleWidget::showFrameRate() const { return d->m_showFrameRate; } bool MarbleWidget::showBackground() const { return d->m_map.showBackground(); } quint64 MarbleWidget::volatileTileCacheLimit() const { return d->m_map.volatileTileCacheLimit(); } void MarbleWidget::setZoom( int newZoom, FlyToMode mode ) { d->m_presenter.setZoom( newZoom, mode ); } void MarbleWidget::zoomView( int zoom, FlyToMode mode ) { d->m_presenter.zoomView( zoom, mode ); } void MarbleWidget::zoomViewBy( int zoomStep, FlyToMode mode ) { d->m_presenter.zoomViewBy( zoomStep, mode ); } void MarbleWidget::zoomIn( FlyToMode mode ) { d->m_presenter.zoomIn( mode ); } void MarbleWidget::zoomOut( FlyToMode mode ) { d->m_presenter.zoomOut( mode ); } void MarbleWidget::rotateBy( const qreal deltaLon, const qreal deltaLat, FlyToMode mode ) { d->m_presenter.rotateBy( deltaLon, deltaLat, mode ); } void MarbleWidget::centerOn( const qreal lon, const qreal lat, bool animated ) { d->m_presenter.centerOn( lon, lat, animated ); } void MarbleWidget::centerOn( const GeoDataCoordinates &position, bool animated ) { d->m_presenter.centerOn( position, animated ); } void MarbleWidget::centerOn( const GeoDataLatLonBox &box, bool animated ) { d->m_presenter.centerOn( box, animated ); } void MarbleWidget::centerOn( const GeoDataPlacemark& placemark, bool animated ) { d->m_presenter.centerOn( placemark, animated ); } void MarbleWidget::setCenterLatitude( qreal lat, FlyToMode mode ) { d->m_presenter.setCenterLatitude( lat, mode ); } void MarbleWidget::setCenterLongitude( qreal lon, FlyToMode mode ) { d->m_presenter.setCenterLongitude( lon, mode ); } Projection MarbleWidget::projection() const { return d->m_map.projection(); } void MarbleWidget::setProjection( Projection projection ) { d->m_map.setProjection( projection ); } void MarbleWidget::setProjection( int projection ) { setProjection( Projection( qAbs( projection ) % (Mercator+1) ) ); } void MarbleWidget::moveLeft( FlyToMode mode ) { d->m_presenter.moveByStep( -1, 0, mode ); } void MarbleWidget::moveRight( FlyToMode mode ) { d->m_presenter.moveByStep( 1, 0, mode ); } void MarbleWidget::moveUp( FlyToMode mode ) { d->m_presenter.moveByStep( 0, -1, mode ); } void MarbleWidget::moveDown( FlyToMode mode ) { d->m_presenter.moveByStep( 0, 1, mode ); } void MarbleWidget::leaveEvent( QEvent* ) { emit mouseMoveGeoPosition( QCoreApplication::translate( "Marble", NOT_AVAILABLE ) ); } void MarbleWidget::resizeEvent( QResizeEvent *event ) { setUpdatesEnabled( false ); d->m_map.setSize( event->size() ); setUpdatesEnabled( true ); QWidget::resizeEvent( event ); } void MarbleWidget::connectNotify( const QMetaMethod &signal ) { if ( d->m_inputhandler && signal == QMetaMethod::fromSignal( &MarbleWidget::mouseMoveGeoPosition ) ) { d->m_inputhandler->setPositionSignalConnected( true ); } } void MarbleWidget::disconnectNotify( const QMetaMethod &signal ) { if ( d->m_inputhandler && signal == QMetaMethod::fromSignal( &MarbleWidget::mouseMoveGeoPosition ) ) { d->m_inputhandler->setPositionSignalConnected( false ); } } bool MarbleWidget::screenCoordinates( qreal lon, qreal lat, qreal& x, qreal& y ) const { return d->m_map.screenCoordinates( lon, lat, x, y ); } bool MarbleWidget::geoCoordinates( int x, int y, qreal& lon, qreal& lat, GeoDataCoordinates::Unit unit ) const { return d->m_map.geoCoordinates( x, y, lon, lat, unit ); } qreal MarbleWidget::centerLatitude() const { return d->m_map.centerLatitude(); } qreal MarbleWidget::centerLongitude() const { return d->m_map.centerLongitude(); } QRegion MarbleWidget::mapRegion() const { return viewport()->mapRegion(); } void MarbleWidget::paintEvent( QPaintEvent *evt ) { QTime t; t.start(); QPaintDevice *paintDevice = this; QImage image; if (!isEnabled()) { // If the globe covers fully the screen then we can use the faster // RGB32 as there are no translucent areas involved. QImage::Format imageFormat = ( d->m_map.viewport()->mapCoversViewport() ) ? QImage::Format_RGB32 : QImage::Format_ARGB32_Premultiplied; // Paint to an intermediate image image = QImage( rect().size(), imageFormat ); image.fill( Qt::transparent ); paintDevice = ℑ } { // FIXME: Better way to get the GeoPainter // Create a painter that will do the painting. GeoPainter geoPainter( paintDevice, d->m_map.viewport(), d->m_map.mapQuality() ); d->m_map.paint( geoPainter, evt->rect() ); } if ( !isEnabled() ) { // Draw a grayscale version of the intermediate image QRgb* pixel = reinterpret_cast( image.scanLine( 0 )); for (int i=0; im_showFrameRate ) { QPainter painter( this ); FpsLayer fpsPainter( &t ); fpsPainter.paint( &painter ); const qreal fps = 1000.0 / (qreal)( t.elapsed() + 1 ); emit framesPerSecond( fps ); } } void MarbleWidget::customPaint( GeoPainter *painter ) { Q_UNUSED( painter ); /* This is a NOOP in the base class*/ } void MarbleWidget::goHome( FlyToMode mode ) { d->m_presenter.goHome( mode ); } QString MarbleWidget::mapThemeId() const { return d->m_model.mapThemeId(); } void MarbleWidget::setMapThemeId( const QString& mapThemeId ) { d->m_map.setMapThemeId( mapThemeId ); } void MarbleWidgetPrivate::updateMapTheme() { m_map.removeLayer( m_routingLayer ); m_widget->setRadius( m_widget->radius() ); // Corrects zoom range, if needed if (m_model.planetId() == QLatin1String("earth")) { m_map.addLayer( m_routingLayer ); } emit m_widget->themeChanged( m_map.mapThemeId() ); // Now we want a full repaint as the atmosphere might differ m_widget->setAttribute( Qt::WA_NoSystemBackground, false ); m_widget->update(); } GeoSceneDocument *MarbleWidget::mapTheme() const { return d->m_model.mapTheme(); } void MarbleWidget::setPropertyValue( const QString& name, bool value ) { mDebug() << "In MarbleWidget the property " << name << "was set to " << value; d->m_map.setPropertyValue( name, value ); } void MarbleWidget::setShowOverviewMap( bool visible ) { d->m_map.setShowOverviewMap( visible ); } void MarbleWidget::setShowScaleBar( bool visible ) { d->m_map.setShowScaleBar( visible ); } void MarbleWidget::setShowCompass( bool visible ) { d->m_map.setShowCompass( visible ); } void MarbleWidget::setShowClouds( bool visible ) { d->m_map.setShowClouds( visible ); } void MarbleWidget::setShowSunShading( bool visible ) { d->m_map.setShowSunShading( visible ); } void MarbleWidget::setShowCityLights( bool visible ) { d->m_map.setShowCityLights( visible ); } void MarbleWidget::setLockToSubSolarPoint( bool visible ) { if ( d->m_map.isLockedToSubSolarPoint() != visible ) { // Toggling input modifies event filters, so avoid that if not needed d->m_map.setLockToSubSolarPoint( visible ); setInputEnabled( !d->m_map.isLockedToSubSolarPoint() ); } } void MarbleWidget::setSubSolarPointIconVisible( bool visible ) { if ( d->m_map.isSubSolarPointIconVisible() != visible ) { d->m_map.setSubSolarPointIconVisible( 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("sun")) { (*i)->setVisible( visible ); } } } void MarbleWidget::setShowAtmosphere( bool visible ) { d->m_map.setShowAtmosphere( visible ); } void MarbleWidget::setShowCrosshairs( bool visible ) { d->m_map.setShowCrosshairs( visible ); } void MarbleWidget::setShowGrid( bool visible ) { d->m_map.setShowGrid( visible ); } void MarbleWidget::setShowPlaces( bool visible ) { d->m_map.setShowPlaces( visible ); } void MarbleWidget::setShowCities( bool visible ) { d->m_map.setShowCities( visible ); } void MarbleWidget::setShowTerrain( bool visible ) { d->m_map.setShowTerrain( visible ); } void MarbleWidget::setShowOtherPlaces( bool visible ) { d->m_map.setShowOtherPlaces( visible ); } void MarbleWidget::setShowRelief( bool visible ) { d->m_map.setShowRelief( visible ); } void MarbleWidget::setShowIceLayer( bool visible ) { d->m_map.setShowIceLayer( visible ); } void MarbleWidget::setShowBorders( bool visible ) { d->m_map.setShowBorders( visible ); } void MarbleWidget::setShowRivers( bool visible ) { d->m_map.setShowRivers( visible ); } void MarbleWidget::setShowLakes( bool visible ) { d->m_map.setShowLakes( visible ); } void MarbleWidget::setShowFrameRate( bool visible ) { d->m_showFrameRate = visible; update(); } void MarbleWidget::setShowBackground( bool visible ) { d->m_map.setShowBackground( visible ); } void MarbleWidget::setShowRuntimeTrace( bool visible ) { d->m_map.setShowRuntimeTrace( visible ); } bool MarbleWidget::showRuntimeTrace() const { return d->m_map.showRuntimeTrace(); } void MarbleWidget::setShowDebugPolygons( bool visible) { d->m_map.setShowDebugPolygons( visible ); } bool MarbleWidget::showDebugPolygons() const { return d->m_map.showDebugPolygons(); } void MarbleWidget::setShowDebugBatchRender( bool visible) { d->m_map.setShowDebugBatchRender( visible ); } bool MarbleWidget::showDebugBatchRender() const { return d->m_map.showDebugBatchRender(); } void MarbleWidget::setShowDebugPlacemarks( bool visible) { d->m_map.setShowDebugPlacemarks( visible ); } bool MarbleWidget::showDebugPlacemarks() const { return d->m_map.showDebugPlacemarks(); } +void MarbleWidget::setDebugLevelTags(bool visible) +{ + d->m_map.setLevelTagDebugModeEnabled(visible); +} + +bool MarbleWidget::debugLevelTags() const +{ + return d->m_map.levelTagDebugModeEnabled(); +} + void MarbleWidget::setShowTileId( bool visible ) { d->m_map.setShowTileId( visible ); } void MarbleWidget::notifyMouseClick( int x, int y) { qreal lon = 0; qreal lat = 0; bool const valid = geoCoordinates( x, y, lon, lat, GeoDataCoordinates::Radian ); if ( valid ) { emit mouseClickGeoPosition( lon, lat, GeoDataCoordinates::Radian ); } } void MarbleWidget::clearVolatileTileCache() { mDebug() << "About to clear VolatileTileCache"; d->m_map.clearVolatileTileCache(); } void MarbleWidget::setVolatileTileCacheLimit( quint64 kiloBytes ) { d->m_map.setVolatileTileCacheLimit( kiloBytes ); } // This slot will called when the Globe starts to create the tiles. void MarbleWidget::creatingTilesStart( TileCreator *creator, const QString &name, const QString &description ) { QPointer dialog = new TileCreatorDialog( creator, this ); dialog->setSummary( name, description ); dialog->exec(); delete dialog; } MapQuality MarbleWidget::mapQuality( ViewContext viewContext ) const { return d->m_map.mapQuality( viewContext ); } void MarbleWidget::setMapQualityForViewContext( MapQuality quality, ViewContext viewContext ) { d->m_map.setMapQualityForViewContext( quality, viewContext ); } ViewContext MarbleWidget::viewContext() const { return d->m_map.viewContext(); } void MarbleWidget::setViewContext( ViewContext viewContext ) { // Inform routing layer about view context change. If not done, // the routing layer causes severe performance problems when dragging the // map. So either do not remove this line, or keep a similar call in place // when you refactor it and test your changes wrt drag performance at // high zoom level with long routes! d->m_routingLayer->setViewContext( viewContext ); d->m_map.setViewContext( viewContext ); } bool MarbleWidget::animationsEnabled() const { return d->m_presenter.animationsEnabled(); } void MarbleWidget::setAnimationsEnabled( bool enabled ) { d->m_presenter.setAnimationsEnabled( enabled ); } AngleUnit MarbleWidget::defaultAngleUnit() const { return d->m_map.defaultAngleUnit(); } void MarbleWidget::setDefaultAngleUnit( AngleUnit angleUnit ) { d->m_map.setDefaultAngleUnit( angleUnit ); } QFont MarbleWidget::defaultFont() const { return d->m_map.defaultFont(); } void MarbleWidget::setDefaultFont( const QFont& font ) { d->m_map.setDefaultFont( font ); } void MarbleWidget::setSelection( const QRect& region ) { d->m_presenter.setSelection( region ); } qreal MarbleWidget::distance() const { return d->m_presenter.distance(); } void MarbleWidget::setDistance( qreal newDistance ) { d->m_presenter.setDistance( newDistance ); } QString MarbleWidget::distanceString() const { return d->m_presenter.distanceString(); } void MarbleWidget::setInputEnabled( bool enabled ) { //if input is set as enabled if ( enabled ) { if ( !d->m_inputhandler ) { d->setInputHandler(); } else { installEventFilter( d->m_inputhandler ); } } else // input is disabled { mDebug() << "MarbleWidget::disableInput"; removeEventFilter( d->m_inputhandler ); setCursor( Qt::ArrowCursor ); } } QList MarbleWidget::renderPlugins() const { return d->m_map.renderPlugins(); } void MarbleWidget::readPluginSettings( QSettings& settings ) { for( RenderPlugin *plugin: renderPlugins() ) { settings.beginGroup(QLatin1String("plugin_") + plugin->nameId()); QHash hash; for ( const QString& key: settings.childKeys() ) { hash.insert( key, settings.value( key ) ); } plugin->setSettings( hash ); settings.endGroup(); } } void MarbleWidget::writePluginSettings( QSettings& settings ) const { for( RenderPlugin *plugin: renderPlugins() ) { settings.beginGroup(QLatin1String("plugin_") + plugin->nameId()); QHash hash = plugin->settings(); QHash::iterator it = hash.begin(); while( it != hash.end() ) { settings.setValue( it.key(), it.value() ); ++it; } settings.endGroup(); } } QList MarbleWidget::floatItems() const { return d->m_map.floatItems(); } AbstractFloatItem * MarbleWidget::floatItem( const QString &nameId ) const { return d->m_map.floatItem( nameId ); } void MarbleWidget::changeEvent( QEvent * event ) { if ( event->type() == QEvent::EnabledChange ) { setInputEnabled(isEnabled()); } QWidget::changeEvent(event); } void MarbleWidget::flyTo( const GeoDataLookAt &newLookAt, FlyToMode mode ) { d->m_presenter.flyTo( newLookAt, mode ); } void MarbleWidget::reloadMap() { d->m_map.reload(); } void MarbleWidget::downloadRegion( QVector const & pyramid ) { d->m_map.downloadRegion( pyramid ); } GeoDataLookAt MarbleWidget::lookAt() const { return d->m_presenter.lookAt(); } GeoDataCoordinates MarbleWidget::focusPoint() const { return d->m_map.viewport()->focusPoint(); } void MarbleWidget::setFocusPoint( const GeoDataCoordinates &focusPoint ) { d->m_map.viewport()->setFocusPoint( focusPoint ); } void MarbleWidget::resetFocusPoint() { d->m_map.viewport()->resetFocusPoint(); } qreal MarbleWidget::radiusFromDistance( qreal distance ) const { return d->m_presenter.radiusFromDistance( distance ); } qreal MarbleWidget::distanceFromRadius( qreal radius ) const { return d->m_presenter.distanceFromRadius( radius ); } qreal MarbleWidget::zoomFromDistance( qreal distance ) const { return d->m_presenter.zoomFromDistance( distance ); } qreal MarbleWidget::distanceFromZoom( qreal zoom ) const { return d->m_presenter.distanceFromZoom( zoom ); } RoutingLayer* MarbleWidget::routingLayer() { return d->m_routingLayer; } PopupLayer *MarbleWidget::popupLayer() { return d->m_mapInfoDialog; } const StyleBuilder* MarbleWidget::styleBuilder() const { return d->m_map.styleBuilder(); } void MarbleWidget::setHeading( qreal heading ) { d->m_map.setHeading( heading ); } qreal MarbleWidget::heading() const { return d->m_map.heading(); } +void MarbleWidget::setLevelToDebug(int level) +{ + d->m_map.setDebugLevelTag(level); +} + +int MarbleWidget::levelToDebug() const +{ + return d->m_map.debugLevelTag(); +} + } #include "moc_MarbleWidget.cpp" diff --git a/src/lib/marble/MarbleWidget.h b/src/lib/marble/MarbleWidget.h index 4fdc4981d..2044ddcce 100644 --- a/src/lib/marble/MarbleWidget.h +++ b/src/lib/marble/MarbleWidget.h @@ -1,1157 +1,1173 @@ // // 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 // #ifndef MARBLE_MARBLEWIDGET_H #define MARBLE_MARBLEWIDGET_H /** @file * This file contains the headers for MarbleWidget. * * @author Torsten Rahn * @author Inge Wallin */ #include #include "GeoDataCoordinates.h" #include "MarbleGlobal.h" // types needed in all of marble. #include "marble_export.h" // Qt class QSettings; class QPixmap; namespace Marble { class AbstractDataPluginItem; class AbstractFloatItem; class GeoDataLatLonAltBox; class GeoDataLatLonBox; class GeoDataFeature; class GeoDataPlacemark; class GeoDataLookAt; class GeoPainter; class GeoSceneDocument; class LayerInterface; class MarbleModel; class MarbleWidgetPopupMenu; class MarbleWidgetInputHandler; class MarbleWidgetPrivate; class RenderPlugin; class RenderState; class RoutingLayer; class TextureLayer; class TileCoordsPyramid; class TileCreator; class ViewportParams; class PopupLayer; class StyleBuilder; /** * @short A widget class that displays a view of the earth. * * This widget displays a view of the earth or any other globe, * depending on which dataset is used. The user can navigate the * globe using either a control widget, e.g. the MarbleNavigator, or * the mouse. The mouse and keyboard control is done through a * MarbleWidgetInputHandler. Only some aspects of the widget can be * controlled by the mouse and/or keyboard. * * By clicking on the globe and moving the mouse, the position can be * changed. The user can also zoom by using the scroll wheel of the * mouse in the widget. The zoom value is not tied to any units, but * is an abstract value without any physical meaning. A value around * 1000 shows the full globe in a normal-sized window. Higher zoom * values give a more zoomed-in view. * * The MarbleWidget owns a data model to work. This model is contained * in the MarbleModel class, and it is painted by using a MarbleMap. * The widget takes care of creating the map and model. A MarbleModel * contains several datatypes, among them 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. * * In addition to navigating with the mouse, you can also use it to * get information about items on the map. You can either click on a * placemark with the left mouse button or with the right mouse button * anywhere on the map. * * The left mouse button opens up a menu with all the placemarks * within a certain distance from the mouse pointer. When you choose * one item from the menu, Marble will open up a dialog window with * some information about the placemark and also try to connect to * Wikipedia to retrieve an article about it. If there is such an * article, you will get a mini-browser window with the article in a tab. * * @see MarbleNavigator * @see MarbleMap * @see MarbleModel */ class MARBLE_EXPORT MarbleWidget : public QWidget { Q_OBJECT #ifdef MARBLE_DBUS Q_CLASSINFO("D-Bus Interface", "org.kde.MarbleWidget") #endif Q_PROPERTY(int zoom READ zoom WRITE setZoom) Q_PROPERTY(QString mapThemeId READ mapThemeId WRITE setMapThemeId) Q_PROPERTY(int projection READ projection WRITE setProjection) Q_PROPERTY(qreal longitude READ centerLongitude WRITE setCenterLongitude) Q_PROPERTY(qreal latitude READ centerLatitude WRITE setCenterLatitude) Q_PROPERTY(bool showOverviewMap READ showOverviewMap WRITE setShowOverviewMap) Q_PROPERTY(bool showScaleBar READ showScaleBar WRITE setShowScaleBar) Q_PROPERTY(bool showCompass READ showCompass WRITE setShowCompass) Q_PROPERTY(bool showGrid READ showGrid WRITE setShowGrid) Q_PROPERTY(bool showClouds READ showClouds WRITE setShowClouds) Q_PROPERTY(bool showSunShading READ showSunShading WRITE setShowSunShading) Q_PROPERTY(bool showCityLights READ showCityLights WRITE setShowCityLights) Q_PROPERTY(bool isLockedToSubSolarPoint READ isLockedToSubSolarPoint WRITE setLockToSubSolarPoint) Q_PROPERTY(bool isSubSolarPointIconVisible READ isSubSolarPointIconVisible WRITE setSubSolarPointIconVisible) Q_PROPERTY(bool showAtmosphere READ showAtmosphere WRITE setShowAtmosphere) Q_PROPERTY(bool showCrosshairs READ showCrosshairs WRITE setShowCrosshairs) Q_PROPERTY(bool showPlaces READ showPlaces WRITE setShowPlaces) Q_PROPERTY(bool showCities READ showCities WRITE setShowCities) Q_PROPERTY(bool showTerrain READ showTerrain WRITE setShowTerrain) Q_PROPERTY(bool showOtherPlaces READ showOtherPlaces WRITE setShowOtherPlaces) Q_PROPERTY(bool showRelief READ showRelief WRITE setShowRelief) Q_PROPERTY(bool showIceLayer READ showIceLayer WRITE setShowIceLayer) Q_PROPERTY(bool showBorders READ showBorders WRITE setShowBorders) Q_PROPERTY(bool showRivers READ showRivers WRITE setShowRivers) Q_PROPERTY(bool showLakes READ showLakes WRITE setShowLakes) Q_PROPERTY(ViewContext viewContext READ viewContext WRITE setViewContext NOTIFY viewContextChanged) Q_PROPERTY( RenderStatus renderStatus READ renderStatus NOTIFY renderStatusChanged ) Q_PROPERTY(quint64 volatileTileCacheLimit READ volatileTileCacheLimit WRITE setVolatileTileCacheLimit) public: /** * @brief Construct a new MarbleWidget. * @param parent the parent widget * * This constructor should be used when you will only use one * MarbleWidget. The widget will create its own MarbleModel when * created. */ explicit MarbleWidget( QWidget *parent = 0 ); ~MarbleWidget() override; /// @name Access to helper objects //@{ /** * @brief Return the model that this view shows. */ MarbleModel *model(); const MarbleModel *model() const; ViewportParams *viewport(); const ViewportParams *viewport() const; MarbleWidgetPopupMenu *popupMenu(); /** * Returns the current input handler */ MarbleWidgetInputHandler *inputHandler() const; /** * @brief Set the input handler */ void setInputHandler( MarbleWidgetInputHandler *handler ); /** * @brief Returns a list of all RenderPlugins on the widget, this includes float items * @return the list of RenderPlugins */ QList renderPlugins() const; /** * @brief Returns a list of all FloatItems on the widget * @return the list of the floatItems */ QList floatItems() const; /** * @brief Returns the FloatItem with the given id * @return The pointer to the requested floatItem, * * If no item is found the null pointer is returned. */ AbstractFloatItem * floatItem( const QString &nameId ) const; /** * Reads the plugin settings from the passed QSettings. * You shouldn't use this in a KDE application as these use KConfig. Here you could * use MarblePart which is handling this automatically. * @param settings The QSettings object to be used. */ void readPluginSettings( QSettings& settings ); /** * Writes the plugin settings in the passed QSettings. * You shouldn't use this in a KDE application as these use KConfig. Here you could * use MarblePart which is handling this automatically. * @param settings The QSettings object to be used. */ void writePluginSettings( QSettings& settings ) const; /** * @brief Retrieve the view context (i.e. still or animated map) */ ViewContext viewContext() const; /** * @brief Get the GeoSceneDocument object of the current map theme */ GeoSceneDocument * mapTheme() const; /** * @brief Returns all widgets of dataPlugins on the position curpos */ QList whichItemAt( const QPoint& curpos ) const; /** * @brief Add a layer to be included in rendering. */ void addLayer( LayerInterface *layer ); /** * @brief Remove a layer from being included in rendering. */ void removeLayer( LayerInterface *layer ); RoutingLayer* routingLayer(); PopupLayer* popupLayer(); /** * @since 0.26.0 */ const StyleBuilder* styleBuilder() 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; // int projection() const; //@} /// @name Visible map area //@{ /** * @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: * mapThemeId = "earth/bluemarble/bluemarble.dgml" */ QString mapThemeId() const; /** * @brief Return the projected region which describes the (shape of the) projected surface. */ QRegion mapRegion() const; /** * @brief Return the radius of the globe in pixels. */ int radius() const; /** * @brief Return the current zoom amount. */ int zoom() const; int tileZoomLevel() const; /** * @brief Return the current distance. */ qreal distance() const; /** * @brief Return the current distance string. */ QString distanceString() 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; //@} /// @name Position management //@{ /** * @brief Get the screen coordinates corresponding to geographical coordinates in the widget. * @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 widget. * @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; qreal heading() const; /** * @brief Return how much the map will move if one of the move slots are called. * @return The move step. */ qreal moveStep() const; /** * @brief Return the lookAt */ GeoDataLookAt lookAt() const; /** * @return The current point of focus, e.g. the point that is not moved * when changing the zoom level. If not set, it defaults to the * center point. * @see centerLongitude centerLatitude setFocusPoint resetFocusPoint */ GeoDataCoordinates focusPoint() const; /** * @brief Change the point of focus, overridding any previously set focus point. * @param focusPoint New focus point * @see focusPoint resetFocusPoint */ void setFocusPoint( const GeoDataCoordinates &focusPoint ); /** * @brief Invalidate any focus point set with @ref setFocusPoint. * @see focusPoint setFocusPoint */ void resetFocusPoint(); /** * @brief Return the globe radius (pixel) for the given distance (km) */ qreal radiusFromDistance( qreal distance ) const; /** * @brief Return the distance (km) at the given globe radius (pixel) */ qreal distanceFromRadius( qreal radius ) const; /** * Returns the zoom value (no unit) corresponding to the given camera distance (km) */ qreal zoomFromDistance( qreal distance ) const; /** * Returns the distance (km) corresponding to the given zoom value */ qreal distanceFromZoom( qreal zoom ) const; //@} /// @name Placemark management //@{ QVector whichFeatureAt( const QPoint& ) const; //@} /// @name Float items and map appearance //@{ /** * @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; /** * @brief Retrieve the map quality depending on the view context */ MapQuality mapQuality( ViewContext = Still ) const; /** * @brief Retrieve whether travels to a point should get animated */ bool animationsEnabled() const; AngleUnit defaultAngleUnit() const; void setDefaultAngleUnit( AngleUnit angleUnit ); QFont defaultFont() const; void setDefaultFont( const QFont& font ); //@} /// @name Tile management //@{ /** * @brief Returns the limit in kilobytes of the volatile (in RAM) tile cache. * @return the limit of volatile tile cache */ quint64 volatileTileCacheLimit() const; //@} /// @name Miscellaneous //@{ /** * @brief Return a QPixmap with the current contents of the widget. */ QPixmap mapScreenShot(); //@} /// @todo Enable this instead of the zoomView slot below for proper deprecation warnings /// around Marble 1.8 // @deprecated Please use setZoom //MARBLE_DEPRECATED( void zoomView( int zoom, FlyToMode mode = Instant ) ); /** * Summarized render status of the current map view * @see renderState */ RenderStatus renderStatus() const; /** * Detailed render status of the current map view */ RenderState renderState() const; /** * Toggle whether regions are highlighted when user selects them */ void setHighlightEnabled( bool enabled ); public Q_SLOTS: /// @name Position management slots //@{ /** * @brief Set the radius of the globe in pixels. * @param radius The new globe radius value in pixels. */ void setRadius( int radius ); /** * @brief Zoom the view to a certain zoomlevel * @param zoom the new zoom level. * * The zoom level is an abstract value without physical * interpretation. A zoom value around 1000 lets the viewer see * all of the earth in the default window. */ void setZoom( int zoom, FlyToMode mode = Instant ); /** * @deprecated To be removed soon. Please use setZoom instead. Same parameters. */ void zoomView( int zoom, FlyToMode mode = Instant ); /** * @brief Zoom the view by a certain step * @param zoomStep the difference between the old zoom and the new */ void zoomViewBy( int zoomStep, FlyToMode mode = Instant ); /** * @brief Zoom in by the amount zoomStep. */ void zoomIn( FlyToMode mode = Automatic ); /** * @brief Zoom out by the amount zoomStep. */ void zoomOut( FlyToMode mode = Automatic ); /** * @brief Set the distance of the observer to the globe in km. * @param distance The new distance in km. */ void setDistance( qreal distance ); /** * @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( const qreal deltaLon, const qreal deltaLat, FlyToMode mode = Instant ); /** * @brief Center the view on a geographical point * @param lat an angle in degrees parallel to the latitude lines * +90(N) - -90(S) * @param lon an angle in degrees parallel to the longitude lines * +180(W) - -180(E) */ void centerOn( const qreal lon, const qreal lat, bool animated = false ); /** * @brief Center the view on a point * This method centers the Marble map on the point described by the latitude * and longitude in the GeoDataCoordinate parameter @c point. It also zooms * the map to be at the elevation described by the altitude. If this is * not the desired functionality or you do not have an accurate altitude * then use @see centerOn(qreal, qreal, bool) * @param point the point in 3 dimensions above the globe to move the view * to. It will always be looking vertically down. */ void centerOn( const GeoDataCoordinates &point, bool animated = false ); /** * @brief Center the view on a bounding box so that it completely fills the viewport * This method not only centers on the center of the GeoDataLatLon box but it also * adjusts the zoom of the marble widget so that the LatLon box provided fills * the viewport. * @param box The GeoDataLatLonBox to zoom and move the MarbleWidget to. */ void centerOn( const GeoDataLatLonBox& box, bool animated = false ); /** * @brief Center the view on a placemark according to the following logic: * - if the placemark has a lookAt, zoom and center on that lookAt * - otherwise use the placemark geometry's latLonAltBox * @param box The GeoDataPlacemark to zoom and move the MarbleWidget to. */ void centerOn( const GeoDataPlacemark& placemark, bool animated = false ); /** * @brief Set the latitude for the center point * @param lat the new value for the latitude in degree. * @param mode the FlyToMode that will be used. */ void setCenterLatitude( qreal lat, FlyToMode mode = Instant ); /** * @brief Set the longitude for the center point * @param lon the new value for the longitude in degree. * @param mode the FlyToMode that will be used. */ void setCenterLongitude( qreal lon, FlyToMode mode = Instant ); void setHeading( qreal heading ); /** * @brief Move left by the moveStep. */ void moveLeft( FlyToMode mode = Automatic ); /** * @brief Move right by the moveStep. */ void moveRight( FlyToMode mode = Automatic ); /** * @brief Move up by the moveStep. */ void moveUp( FlyToMode mode = Automatic ); /** * @brief Move down by the moveStep. */ void moveDown( FlyToMode mode = Automatic ); /** * @brief Center the view on the default start point with the default zoom. */ void goHome( FlyToMode mode = Automatic ); /** * @brief Change the camera position to the given position. * @param lookAt New camera position. Changing the camera position means * that both the current center position as well as the zoom value may change * @param mode Interpolation type for intermediate camera positions. Automatic * (default) chooses a suitable interpolation among Instant, Lenar and Jump. * Instant will directly set the new zoom and position values, while * Linear results in a linear interpolation of intermediate center coordinates * along the sphere and a linear interpolation of changes in the camera distance * to the ground. Finally, Jump will behave the same as Linear with regard to * the center position interpolation, but use a parabolic height increase * towards the middle point of the intermediate positions. This appears * like a jump of the camera. */ void flyTo( const GeoDataLookAt &lookAt, FlyToMode mode = Automatic ); //@} /// @name Float items and map appearance slots //@{ /** * @brief Set the Projection used for the map * @param projection projection type (e.g. Spherical, Equirectangular, Mercator) */ void setProjection( int projection ); 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 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 setShowBackground( 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 runtime tracing for layers gets shown * @param visible visibility of the runtime tracing */ 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 * 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; /** + * @brief Set whether to render according to OSM indoor level tags + * @param visible visibility of entities (placemarks, buildings etc.) level-wise + */ + void setDebugLevelTags(bool visible); + + bool debugLevelTags() const; + + /** + * @brief Set the level to debug + * @param level the level to debug + */ + void setLevelToDebug(int level); + + int levelToDebug() const; + + /** * @brief Set the map quality for the specified view context. * * @param quality map quality for the specified view context * @param viewContext view context whose map quality should be set */ void setMapQualityForViewContext( MapQuality quality, ViewContext viewContext ); /** * @brief Set the view context (i.e. still or animated map) */ void setViewContext( ViewContext viewContext ); /** * @brief Set whether travels to a point should get animated */ void setAnimationsEnabled( bool enabled ); //@} /// @name Tile management slots //@{ void clearVolatileTileCache(); /** * @brief Set the limit of the volatile (in RAM) tile cache. * @param kilobytes The limit in kilobytes. */ void setVolatileTileCacheLimit( quint64 kiloBytes ); /** * @brief A slot that is called when the model starts to create new tiles. * @param creator the tile creator object. * @param name the name of the created theme. * @param description a descriptive text that can be shown in a dialog. * @see creatingTilesProgress * * This function is connected to the models signal with the same * name. When the model needs to create a cache of tiles in * several different resolutions, it will emit creatingTilesStart * once with a name of the theme and a descriptive text. The * widget can then pop up a dialog to explain why there is a * delay. The model will then call creatingTilesProgress several * times until the parameter reaches 100 (100%), after which the * creation process is finished. After this there will be no more * calls to creatingTilesProgress, and the poup dialog can then be * closed. */ void creatingTilesStart( TileCreator *creator, const QString& name, const QString& description ); /** * @brief Re-download all visible tiles. */ void reloadMap(); void downloadRegion( QVector const & ); //@} /// @name Miscellaneous slots //@{ /** * @brief Used to notify about the position of the mouse click */ void notifyMouseClick( int x, int y ); void setSelection( const QRect& region ); void setInputEnabled( bool ); TextureLayer *textureLayer() const; //@} Q_SIGNALS: /** * @brief Signal that the zoom has changed, and to what. * @param zoom The new zoom value. * @see setZoom() */ void zoomChanged( int zoom ); void distanceChanged( const QString& distanceString ); void tileLevelChanged( int level ); void viewContextChanged(ViewContext newViewContext); /** * @brief Signal that the theme has changed * @param theme Name of the new theme. */ void themeChanged( const QString& theme ); void projectionChanged( Projection ); void mouseMoveGeoPosition( const QString& ); void mouseClickGeoPosition( qreal lon, qreal lat, GeoDataCoordinates::Unit ); void framesPerSecond( qreal fps ); /** This signal is emit when a new rectangle region is selected over the map * The list of double values include coordinates in degrees using this order: * lon1, lat1, lon2, lat2 (or West, North, East, South) as left/top, right/bottom rectangle. */ void regionSelected( const QList& ); /** * 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 ); /** * 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 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 lon, qreal lat, GeoDataCoordinates::Unit unit ); protected: /** * @brief Reimplementation of the leaveEvent() function in QWidget. */ void leaveEvent( QEvent *event ) override; /** * @brief Reimplementation of the paintEvent() function in QWidget. */ void paintEvent( QPaintEvent *event ) override; /** * @brief Reimplementation of the resizeEvent() function in QWidget. */ void resizeEvent( QResizeEvent *event ) override; void connectNotify(const QMetaMethod &signal) override; void disconnectNotify(const QMetaMethod &signal) override; /** * @brief Reimplementation of the changeEvent() function in QWidget to * react to changes of the enabled state */ void changeEvent( QEvent * event ) override; /** * @brief Enables custom drawing onto the MarbleWidget straight after * @brief the globe and before all other layers has 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 updateSystemBackgroundAttribute() ) private: Q_DISABLE_COPY( MarbleWidget ) MarbleWidgetPrivate * const d; friend class MarbleWidgetPrivate; class CustomPaintLayer; friend class CustomPaintLayer; friend class MarbleWidgetDefaultInputHandler; -}; + }; } #endif diff --git a/src/lib/marble/MarbleWidgetInputHandler.cpp b/src/lib/marble/MarbleWidgetInputHandler.cpp index 6837a81ab..efd4726b9 100644 --- a/src/lib/marble/MarbleWidgetInputHandler.cpp +++ b/src/lib/marble/MarbleWidgetInputHandler.cpp @@ -1,187 +1,231 @@ // 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-2007 Torsten Rahn // Copyright 2007 Inge Wallin // Copyright 2014 Adam Dabrowski // #include "MarbleWidgetInputHandler.h" #include #include #include #include #include "MarbleGlobal.h" #include "MarbleDebug.h" #include "MarbleWidget.h" #include "AbstractDataPluginItem.h" #include "MarbleWidgetPopupMenu.h" #include "PopupLayer.h" #include "RenderPlugin.h" #include "RoutingLayer.h" namespace Marble { class MarbleWidgetInputHandlerPrivate { class MarbleWidgetSelectionRubber : public AbstractSelectionRubber { public: explicit MarbleWidgetSelectionRubber(MarbleWidget *widget) : m_rubberBand(QRubberBand::Rectangle, widget) { m_rubberBand.hide(); } void show() override { m_rubberBand.show(); } void hide() override { m_rubberBand.hide(); } bool isVisible() const override { return m_rubberBand.isVisible(); } const QRect &geometry() const override { return m_rubberBand.geometry(); } void setGeometry(const QRect &geometry) override { m_rubberBand.setGeometry(geometry); } private: QRubberBand m_rubberBand; }; public: MarbleWidgetInputHandlerPrivate(MarbleWidgetInputHandler *handler, MarbleWidget *widget) : m_inputHandler(handler) ,m_marbleWidget(widget) ,m_selectionRubber(widget) ,m_debugModeEnabled(false) { for(RenderPlugin *renderPlugin: widget->renderPlugins()) { if(renderPlugin->isInitialized()) { installPluginEventFilter(renderPlugin); } } m_marbleWidget->grabGesture(Qt::PinchGesture); } void setCursor(const QCursor &cursor) { m_marbleWidget->setCursor(cursor); } bool layersEventFilter(QObject *o, QEvent *e) { //FIXME - this should go up in hierarchy to MarbleInputHandler if (m_marbleWidget->popupLayer()->eventFilter(o, e)) { return true; } if (m_marbleWidget->routingLayer()->eventFilter(o, e)) { return true; } return false; } void installPluginEventFilter(RenderPlugin *renderPlugin) { m_marbleWidget->installEventFilter(renderPlugin); } MarbleWidgetInputHandler *m_inputHandler; MarbleWidget *m_marbleWidget; MarbleWidgetSelectionRubber m_selectionRubber; bool m_debugModeEnabled; }; void MarbleWidgetInputHandler::setCursor(const QCursor &cursor) { d->setCursor(cursor); } bool MarbleWidgetInputHandler::handleKeyPress(QKeyEvent *event) { if (d->m_debugModeEnabled) { - switch(event->key()) { - case Qt::Key_I: - MarbleDebug::setEnabled(!MarbleDebug::isEnabled()); - break; - case Qt::Key_R: - d->m_marbleWidget->setShowRuntimeTrace(!d->m_marbleWidget->showRuntimeTrace()); - break; - case Qt::Key_O: - d->m_marbleWidget->setShowDebugPlacemarks(!d->m_marbleWidget->showDebugPlacemarks()); - break; - case Qt::Key_P: - d->m_marbleWidget->setShowDebugPolygons(!d->m_marbleWidget->showDebugPolygons()); - break; - case Qt::Key_B: - d->m_marbleWidget->setShowDebugBatchRender(!d->m_marbleWidget->showDebugBatchRender()); - break; + if (event->modifiers() == Qt::ControlModifier && d->m_marbleWidget->debugLevelTags()) { + switch(event->key()) { + case Qt::Key_0: + d->m_marbleWidget->setLevelToDebug(0); + break; + case Qt::Key_1: + d->m_marbleWidget->setLevelToDebug(1); + break; + case Qt::Key_2: + d->m_marbleWidget->setLevelToDebug(2); + break; + case Qt::Key_3: + d->m_marbleWidget->setLevelToDebug(3); + break; + case Qt::Key_4: + d->m_marbleWidget->setLevelToDebug(4); + break; + case Qt::Key_5: + d->m_marbleWidget->setLevelToDebug(5); + break; + case Qt::Key_6: + d->m_marbleWidget->setLevelToDebug(6); + break; + case Qt::Key_7: + d->m_marbleWidget->setLevelToDebug(7); + break; + case Qt::Key_8: + d->m_marbleWidget->setLevelToDebug(8); + break; + case Qt::Key_9: + d->m_marbleWidget->setLevelToDebug(9); + break; + case Qt::Key_Plus: + d->m_marbleWidget->setLevelToDebug(d->m_marbleWidget->levelToDebug() + 1); + break; + case Qt::Key_Minus: + d->m_marbleWidget->setLevelToDebug(d->m_marbleWidget->levelToDebug() - 1); + break; + } + } else { + switch(event->key()) { + case Qt::Key_I: + MarbleDebug::setEnabled(!MarbleDebug::isEnabled()); + break; + case Qt::Key_R: + d->m_marbleWidget->setShowRuntimeTrace(!d->m_marbleWidget->showRuntimeTrace()); + break; + case Qt::Key_O: + d->m_marbleWidget->setShowDebugPlacemarks(!d->m_marbleWidget->showDebugPlacemarks()); + break; + case Qt::Key_P: + d->m_marbleWidget->setShowDebugPolygons(!d->m_marbleWidget->showDebugPolygons()); + break; + case Qt::Key_B: + d->m_marbleWidget->setShowDebugBatchRender(!d->m_marbleWidget->showDebugBatchRender()); + break; + case Qt::Key_L: + d->m_marbleWidget->setDebugLevelTags(!d->m_marbleWidget->debugLevelTags()); + break; + } } } return MarbleDefaultInputHandler::handleKeyPress(event); } AbstractSelectionRubber *MarbleWidgetInputHandler::selectionRubber() { return &d->m_selectionRubber; } bool MarbleWidgetInputHandler::layersEventFilter(QObject *o, QEvent *e) { return d->layersEventFilter(o, e); } void MarbleWidgetInputHandler::installPluginEventFilter(RenderPlugin *renderPlugin) { d->installPluginEventFilter(renderPlugin); } MarbleWidgetInputHandler::MarbleWidgetInputHandler(MarbleAbstractPresenter *marblePresenter, MarbleWidget *widget) : MarbleDefaultInputHandler(marblePresenter) ,d(new MarbleWidgetInputHandlerPrivate(this, widget)) { } void MarbleWidgetInputHandler::setDebugModeEnabled(bool enabled) { d->m_debugModeEnabled = enabled; } //FIXME - these should be moved to superclass and popupMenu should be abstracted in MarbleAbstractPresenter void MarbleWidgetInputHandler::showLmbMenu(int x, int y) { if (isMouseButtonPopupEnabled(Qt::LeftButton)) { d->m_marbleWidget->popupMenu()->showLmbMenu(x, y); toolTipTimer()->stop(); } } void MarbleWidgetInputHandler::showRmbMenu(int x, int y) { if (isMouseButtonPopupEnabled(Qt::RightButton)) { d->m_marbleWidget->popupMenu()->showRmbMenu(x, y); } } void MarbleWidgetInputHandler::openItemToolTip() { if (!lastToolTipItem().isNull()) { QToolTip::showText(d->m_marbleWidget->mapToGlobal(toolTipPosition()), lastToolTipItem()->toolTip(), d->m_marbleWidget, lastToolTipItem()->containsRect(toolTipPosition()).toRect()); } } } #include "moc_MarbleWidgetInputHandler.cpp" diff --git a/src/lib/marble/layers/GeometryLayer.cpp b/src/lib/marble/layers/GeometryLayer.cpp index 33ae3ae5c..74b8904ac 100644 --- a/src/lib/marble/layers/GeometryLayer.cpp +++ b/src/lib/marble/layers/GeometryLayer.cpp @@ -1,672 +1,717 @@ // // This file is part of the Marble Virtual Globe. // // This program is free software licensed under the GNU LGPL. You can // find a copy of this license in LICENSE.txt in the top directory of // the source code. // // Copyright 2008-2009 Patrick Spendrin // Copyright 2010 Thibaut Gridel // Copyright 2011-2012 Bernhard Beschow // Copyright 2014 Gábor Péterffy // #include "GeometryLayer.h" // Marble #include "GeoDataLatLonAltBox.h" #include "GeoDataDocument.h" #include "GeoDataFolder.h" #include "GeoDataLineStyle.h" #include "GeoDataMultiTrack.h" #include "GeoDataObject.h" #include "GeoDataPlacemark.h" #include "GeoDataLinearRing.h" #include "GeoDataMultiGeometry.h" #include "GeoDataPolygon.h" #include "GeoDataBuilding.h" #include "GeoDataPolyStyle.h" #include "GeoDataStyle.h" #include "GeoDataIconStyle.h" #include "GeoDataStyleMap.h" #include "GeoDataTrack.h" #include "GeoDataFeature.h" #include "MarbleDebug.h" #include "GeoPainter.h" #include "ViewportParams.h" #include "RenderState.h" #include "GeoGraphicsScene.h" #include "GeoGraphicsItem.h" #include "GeoLineStringGraphicsItem.h" #include "GeoPolygonGraphicsItem.h" #include "GeoTrackGraphicsItem.h" #include "GeoDataPhotoOverlay.h" #include "GeoDataScreenOverlay.h" #include "GeoPhotoGraphicsItem.h" #include "ScreenOverlayGraphicsItem.h" #include "TileId.h" #include "MarbleGraphicsItem.h" #include "MarblePlacemarkModel.h" #include "GeoDataTreeModel.h" #include #include "StyleBuilder.h" #include "AbstractGeoPolygonGraphicsItem.h" #include "GeoLineStringGraphicsItem.h" #include "GeoDataRelation.h" // Qt #include #include #include namespace Marble { class GeometryLayerPrivate { public: typedef QVector OsmLineStringItems; typedef QSet Relations; typedef QHash FeatureRelationHash; typedef QVector GeoGraphicItems; struct PaintFragments { // Three lists for different z values // A z value of 0 is default and used by the majority of items, so sorting // can be avoided for it QVector negative; // subways QVector null; // areas and roads QVector positive; // buildings }; explicit GeometryLayerPrivate(const QAbstractItemModel *model, const StyleBuilder *styleBuilder); void createGraphicsItems(const GeoDataObject *object); void createGraphicsItems(const GeoDataObject *object, FeatureRelationHash &relations); void createGraphicsItemFromGeometry(const GeoDataGeometry *object, const GeoDataPlacemark *placemark, const Relations &relations); void createGraphicsItemFromOverlay(const GeoDataOverlay *overlay); void removeGraphicsItems(const GeoDataFeature *feature); void updateTiledLineStrings(const GeoDataPlacemark *placemark, GeoLineStringGraphicsItem* lineStringItem); void updateTiledLineStrings(OsmLineStringItems &lineStringItems); void clearCache(); bool showRelation(const GeoDataRelation* relation) const; void updateRelationVisibility(); const QAbstractItemModel *const m_model; const StyleBuilder *const m_styleBuilder; GeoGraphicsScene m_scene; QString m_runtimeTrace; QList m_screenOverlays; QHash m_osmLineStringItems; int m_tileLevel; GeoGraphicsItem* m_lastFeatureAt; bool m_dirty; int m_cachedItemCount; QHash m_cachedPaintFragments; typedef QPair LayerItem; QList m_cachedDefaultLayer; QDateTime m_cachedDateTime; GeoDataLatLonBox m_cachedLatLonBox; QSet m_highlightedRouteRelations; GeoDataRelation::RelationTypes m_visibleRelationTypes; + bool m_levelTagDebugModeEnabled; + int m_levelToDebug; }; GeometryLayerPrivate::GeometryLayerPrivate(const QAbstractItemModel *model, const StyleBuilder *styleBuilder) : m_model(model), m_styleBuilder(styleBuilder), m_tileLevel(0), m_lastFeatureAt(nullptr), m_dirty(true), m_cachedItemCount(0), - m_visibleRelationTypes(GeoDataRelation::RouteFerry) + m_visibleRelationTypes(GeoDataRelation::RouteFerry), + m_levelTagDebugModeEnabled(false), + m_levelToDebug(0) { } 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) { + if (d->m_levelTagDebugModeEnabled) { + if (const auto placemark = geodata_cast(item->feature())) { + if (!placemark->hasOsmData()) { + continue; + } + QHash::const_iterator tagIter = placemark->osmData().findTag(QStringLiteral("level")); + if (tagIter == placemark->osmData().tagsEnd()) { + continue; + } + const int val = tagIter.value().toInt(); + if (val != d->m_levelToDebug) { + continue; + } + } + } item->paint(painter, viewport, layer, d->m_tileLevel); } } for (const auto & item: d->m_cachedDefaultLayer) { item.second->paint(painter, viewport, item.first, d->m_tileLevel); } for (ScreenOverlayGraphicsItem* item: d->m_screenOverlays) { item->paintEvent(painter, viewport); } painter->restore(); d->m_runtimeTrace = QStringLiteral("Geometries: %1 Zoom: %2") .arg(d->m_cachedItemCount) .arg(d->m_tileLevel); return true; } RenderState GeometryLayer::renderState() const { return RenderState(QStringLiteral("GeoGraphicsScene")); } QString GeometryLayer::runtimeTrace() const { return d->m_runtimeTrace; } bool GeometryLayer::hasFeatureAt(const QPoint &curpos, const ViewportParams *viewport) { if (d->m_lastFeatureAt && d->m_lastFeatureAt->contains(curpos, viewport)) { return true; } auto const renderOrder = d->m_styleBuilder->renderOrder(); for (int i = renderOrder.size() - 1; i >= 0; --i) { auto & layerItems = d->m_cachedPaintFragments[renderOrder[i]]; for (auto item : layerItems) { if (item->contains(curpos, viewport)) { d->m_lastFeatureAt = item; return true; } } } return false; } void GeometryLayerPrivate::createGraphicsItems(const GeoDataObject *object, FeatureRelationHash &relations) { clearCache(); if (auto document = geodata_cast(object)) { for (auto feature: document->featureList()) { if (auto relation = geodata_cast(feature)) { relation->setVisible(showRelation(relation)); for (auto member: relation->members()) { relations[member] << relation; } } } } if (auto placemark = geodata_cast(object)) { createGraphicsItemFromGeometry(placemark->geometry(), placemark, relations.value(placemark)); } else if (const GeoDataOverlay* overlay = dynamic_cast(object)) { createGraphicsItemFromOverlay(overlay); } // parse all child objects of the container if (const GeoDataContainer *container = dynamic_cast(object)) { int rowCount = container->size(); for (int row = 0; row < rowCount; ++row) { createGraphicsItems(container->child(row), relations); } } } void GeometryLayerPrivate::updateTiledLineStrings(const GeoDataPlacemark* placemark, GeoLineStringGraphicsItem* lineStringItem) { if (!placemark->hasOsmData()) { return; } qint64 const osmId = placemark->osmData().oid(); if (osmId <= 0) { return; } auto & lineStringItems = m_osmLineStringItems[osmId]; lineStringItems << lineStringItem; updateTiledLineStrings(lineStringItems); } void GeometryLayerPrivate::updateTiledLineStrings(OsmLineStringItems &lineStringItems) { GeoDataLineString merged; if (lineStringItems.size() > 1) { QVector lineStrings; for (auto item : lineStringItems) { lineStrings << item->lineString(); } merged = GeoLineStringGraphicsItem::merge(lineStrings); } // If merging failed, reset all. Otherwise only the first one // gets the merge result and becomes visible. bool visible = true; for (auto item : lineStringItems) { item->setVisible(visible); if (visible) { item->setMergedLineString(merged); visible = merged.isEmpty(); } } } void GeometryLayerPrivate::clearCache() { m_lastFeatureAt = nullptr; m_dirty = true; m_cachedDateTime = QDateTime(); m_cachedItemCount = 0; m_cachedPaintFragments.clear(); m_cachedDefaultLayer.clear(); m_cachedLatLonBox = GeoDataLatLonBox(); } inline bool GeometryLayerPrivate::showRelation(const GeoDataRelation *relation) const { return (m_visibleRelationTypes.testFlag(relation->relationType()) || m_highlightedRouteRelations.contains(relation->osmData().oid())); } void GeometryLayerPrivate::updateRelationVisibility() { for (int i = 0; i < m_model->rowCount(); ++i) { QVariant const data = m_model->data(m_model->index(i, 0), MarblePlacemarkModel::ObjectPointerRole); GeoDataObject *object = qvariant_cast (data); if (auto doc = geodata_cast(object)) { for (auto feature: doc->featureList()) { if (auto relation = geodata_cast(feature)) { relation->setVisible(showRelation(relation)); } } } } m_scene.resetStyle(); } void GeometryLayerPrivate::createGraphicsItemFromGeometry(const GeoDataGeometry* object, const GeoDataPlacemark *placemark, const Relations &relations) { if (!placemark->isGloballyVisible()) { return; // Reconsider this when visibility can be changed dynamically } GeoGraphicsItem *item = 0; if (const auto line = geodata_cast(object)) { auto lineStringItem = new GeoLineStringGraphicsItem(placemark, line); item = lineStringItem; updateTiledLineStrings(placemark, lineStringItem); } else if (const auto ring = geodata_cast(object)) { item = GeoPolygonGraphicsItem::createGraphicsItem(placemark, ring); } else if (const auto poly = geodata_cast(object)) { item = GeoPolygonGraphicsItem::createGraphicsItem(placemark, poly); if (item->zValue() == 0) { item->setZValue(poly->renderOrder()); } } else if (const auto building = geodata_cast(object)) { item = GeoPolygonGraphicsItem::createGraphicsItem(placemark, building); } else if (const auto multigeo = geodata_cast(object)) { int rowCount = multigeo->size(); for (int row = 0; row < rowCount; ++row) { createGraphicsItemFromGeometry(multigeo->child(row), placemark, relations); } } else if (const auto multitrack = geodata_cast(object)) { int rowCount = multitrack->size(); for (int row = 0; row < rowCount; ++row) { createGraphicsItemFromGeometry(multitrack->child(row), placemark, relations); } } else if (const auto track = geodata_cast(object)) { item = new GeoTrackGraphicsItem(placemark, track); } if (!item) { return; } item->setRelations(relations); item->setStyleBuilder(m_styleBuilder); item->setVisible(item->visible() && placemark->isGloballyVisible()); item->setMinZoomLevel(m_styleBuilder->minimumZoomLevel(*placemark)); m_scene.addItem(item); } void GeometryLayerPrivate::createGraphicsItemFromOverlay(const GeoDataOverlay *overlay) { if (!overlay->isGloballyVisible()) { return; // Reconsider this when visibility can be changed dynamically } GeoGraphicsItem* item = 0; if (const auto photoOverlay = geodata_cast(overlay)) { GeoPhotoGraphicsItem *photoItem = new GeoPhotoGraphicsItem(overlay); photoItem->setPoint(photoOverlay->point()); item = photoItem; } else if (const auto screenOverlay = geodata_cast(overlay)) { ScreenOverlayGraphicsItem *screenItem = new ScreenOverlayGraphicsItem(screenOverlay); m_screenOverlays.push_back(screenItem); } if (item) { item->setStyleBuilder(m_styleBuilder); item->setVisible(overlay->isGloballyVisible()); m_scene.addItem(item); } } void GeometryLayerPrivate::removeGraphicsItems(const GeoDataFeature *feature) { clearCache(); if (const auto placemark = geodata_cast(feature)) { if (placemark->isGloballyVisible() && geodata_cast(placemark->geometry()) && placemark->hasOsmData() && placemark->osmData().oid() > 0) { auto & items = m_osmLineStringItems[placemark->osmData().oid()]; bool removed = false; for (auto item : items) { if (item->feature() == feature) { items.removeOne(item); removed = true; break; } } Q_ASSERT(removed); updateTiledLineStrings(items); } m_scene.removeItem(feature); } else if (const auto container = dynamic_cast(feature)) { for (const GeoDataFeature *child: container->featureList()) { removeGraphicsItems(child); } } else if (geodata_cast(feature)) { for (ScreenOverlayGraphicsItem *item: m_screenOverlays) { if (item->screenOverlay() == feature) { m_screenOverlays.removeAll(item); } } } } void GeometryLayer::addPlacemarks(const QModelIndex& parent, int first, int last) { Q_ASSERT(first < d->m_model->rowCount(parent)); Q_ASSERT(last < d->m_model->rowCount(parent)); for (int i = first; i <= last; ++i) { QModelIndex index = d->m_model->index(i, 0, parent); Q_ASSERT(index.isValid()); const GeoDataObject *object = qvariant_cast(index.data(MarblePlacemarkModel::ObjectPointerRole)); Q_ASSERT(object); d->createGraphicsItems(object); } emit repaintNeeded(); } void GeometryLayer::removePlacemarks(const QModelIndex& parent, int first, int last) { Q_ASSERT(last < d->m_model->rowCount(parent)); bool isRepaintNeeded = false; for (int i = first; i <= last; ++i) { QModelIndex index = d->m_model->index(i, 0, parent); Q_ASSERT(index.isValid()); const GeoDataObject *object = qvariant_cast(index.data(MarblePlacemarkModel::ObjectPointerRole)); const GeoDataFeature *feature = dynamic_cast(object); if (feature != 0) { d->removeGraphicsItems(feature); isRepaintNeeded = true; } } if (isRepaintNeeded) { emit repaintNeeded(); } } void GeometryLayer::resetCacheData() { d->clearCache(); d->m_scene.clear(); qDeleteAll(d->m_screenOverlays); d->m_screenOverlays.clear(); d->m_osmLineStringItems.clear(); const GeoDataObject *object = static_cast(d->m_model->index(0, 0, QModelIndex()).internalPointer()); if (object && object->parent()) { d->createGraphicsItems(object->parent()); } emit repaintNeeded(); } void GeometryLayer::setTileLevel(int tileLevel) { d->m_tileLevel = tileLevel; } QVector GeometryLayer::whichFeatureAt(const QPoint &curpos, const ViewportParams *viewport) { QVector result; auto const renderOrder = d->m_styleBuilder->renderOrder(); QString const label = QStringLiteral("/label"); QSet checked; for (int i = renderOrder.size()-1; i >= 0; --i) { if (renderOrder[i].endsWith(label)) { continue; } auto & layerItems = d->m_cachedPaintFragments[renderOrder[i]]; for (auto j = layerItems.size()-1; j >= 0; --j) { auto const & layerItem = layerItems[j]; if (!checked.contains(layerItem)) { if (layerItem->contains(curpos, viewport)) { result << layerItem->feature(); } checked << layerItem; } } } return result; } void GeometryLayer::highlightRouteRelation(qint64 osmId, bool enabled) { if (enabled) { d->m_highlightedRouteRelations << osmId; } else { d->m_highlightedRouteRelations.remove(osmId); } d->updateRelationVisibility(); } void GeometryLayer::setVisibleRelationTypes(GeoDataRelation::RelationTypes relationTypes) { if (relationTypes != d->m_visibleRelationTypes) { d->m_visibleRelationTypes = relationTypes; d->updateRelationVisibility(); } } void GeometryLayer::handleHighlight(qreal lon, qreal lat, GeoDataCoordinates::Unit unit) { GeoDataCoordinates clickedPoint(lon, lat, 0, unit); QVector selectedPlacemarks; for (int i = 0; i < d->m_model->rowCount(); ++i) { QVariant const data = d->m_model->data(d->m_model->index(i, 0), MarblePlacemarkModel::ObjectPointerRole); GeoDataObject *object = qvariant_cast (data); Q_ASSERT(object); if (const auto doc = geodata_cast(object)) { bool isHighlight = false; for (const GeoDataStyleMap &styleMap: doc->styleMaps()) { if (styleMap.contains(QStringLiteral("highlight"))) { isHighlight = true; break; } } /* * If a document doesn't specify any highlight * styleId in its style maps then there is no need * to further check that document for placemarks * which have been clicked because we won't * highlight them. */ if (isHighlight) { QVector::Iterator iter = doc->begin(); QVector::Iterator const end = doc->end(); for (; iter != end; ++iter) { if (auto placemark = geodata_cast(*iter)) { GeoDataPolygon *polygon = dynamic_cast(placemark->geometry()); if (polygon && polygon->contains(clickedPoint)) { selectedPlacemarks.push_back(placemark); } if (auto linearRing = geodata_cast(placemark->geometry())) { if (linearRing->contains(clickedPoint)) { selectedPlacemarks.push_back(placemark); } } if (auto multiGeometry = geodata_cast(placemark->geometry())) { QVector::Iterator multiIter = multiGeometry->begin(); QVector::Iterator const multiEnd = multiGeometry->end(); for (; multiIter != multiEnd; ++multiIter) { GeoDataPolygon *poly = dynamic_cast(*multiIter); if (poly && poly->contains(clickedPoint)) { selectedPlacemarks.push_back(placemark); break; } if (auto linearRing = geodata_cast(*multiIter)) { if (linearRing->contains(clickedPoint)) { selectedPlacemarks.push_back(placemark); break; } } } } } } } } } emit highlightedPlacemarksChanged(selectedPlacemarks); } +void GeometryLayer::setLevelTagDebugModeEnabled(bool enabled) +{ + if (d->m_levelTagDebugModeEnabled != enabled) { + d->m_levelTagDebugModeEnabled = enabled; + emit repaintNeeded(); + } +} + +bool GeometryLayer::levelTagDebugModeEnabled() const +{ + return d->m_levelTagDebugModeEnabled; +} + +void GeometryLayer::setDebugLevelTag(int level) +{ + if (d->m_levelToDebug != level) { + d->m_levelToDebug = level; + emit repaintNeeded(); + } +} + +int GeometryLayer::debugLevelTag() const +{ + return d->m_levelToDebug; +} + } #include "moc_GeometryLayer.cpp" diff --git a/src/lib/marble/layers/GeometryLayer.h b/src/lib/marble/layers/GeometryLayer.h index 849ae8d48..0f2055e66 100644 --- a/src/lib/marble/layers/GeometryLayer.h +++ b/src/lib/marble/layers/GeometryLayer.h @@ -1,95 +1,103 @@ // // 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 setVisibleRelationTypes(GeoDataRelation::RelationTypes relationTypes); + void setLevelTagDebugModeEnabled(bool enabled); + + bool levelTagDebugModeEnabled() const; + + void setDebugLevelTag(int level); + + int debugLevelTag() const; + 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