diff --git a/src/lib/marble/geodata/data/GeoDataContainer.cpp b/src/lib/marble/geodata/data/GeoDataContainer.cpp index 528eb6a9b..ce7bde496 100644 --- a/src/lib/marble/geodata/data/GeoDataContainer.cpp +++ b/src/lib/marble/geodata/data/GeoDataContainer.cpp @@ -1,421 +1,426 @@ // // 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 Murad Tagirov // Copyright 2009 Patrick Spendrin // // Own #include "GeoDataContainer.h" #include "GeoDataContainer_p.h" // Marble #include "MarbleDebug.h" #include "GeoDataFeature.h" #include "GeoDataFolder.h" #include "GeoDataPlacemark.h" #include "GeoDataDocument.h" #include "GeoDataLatLonAltBox.h" #include "GeoDataGeometry.h" #include "GeoDataNetworkLinkControl.h" #include "GeoDataNetworkLink.h" #include "GeoDataGroundOverlay.h" #include "GeoDataPhotoOverlay.h" #include "GeoDataScreenOverlay.h" #include "GeoDataTour.h" #include namespace Marble { GeoDataContainer::GeoDataContainer() : GeoDataFeature( new GeoDataContainerPrivate ) { } GeoDataContainer::GeoDataContainer(GeoDataContainerPrivate *priv) : GeoDataFeature(priv) { Q_D(GeoDataContainer); d->setParent(this); } GeoDataContainer::GeoDataContainer(const GeoDataContainer& other, GeoDataContainerPrivate *priv) : GeoDataFeature(other, priv) { Q_D(GeoDataContainer); d->setParent(this); } GeoDataContainer::GeoDataContainer( const GeoDataContainer& other ) : GeoDataFeature(other, new GeoDataContainerPrivate(*other.d_func())) { Q_D(GeoDataContainer); d->setParent(this); } GeoDataContainer::~GeoDataContainer() { } GeoDataContainer& GeoDataContainer::operator=(const GeoDataContainer& other) { if (this != &other) { Q_D(GeoDataContainer); *d = *other.d_func(); } return *this; } bool GeoDataContainer::equals( const GeoDataContainer &other ) const { if ( !GeoDataFeature::equals(other) ) { return false; } Q_D(const GeoDataContainer); const GeoDataContainerPrivate* const other_d = other.d_func(); QVector::const_iterator thisBegin = d->m_vector.constBegin(); QVector::const_iterator thisEnd = d->m_vector.constEnd(); QVector::const_iterator otherBegin = other_d->m_vector.constBegin(); QVector::const_iterator otherEnd = other_d->m_vector.constEnd(); for (; thisBegin != thisEnd && otherBegin != otherEnd; ++thisBegin, ++otherBegin) { if (**thisBegin != **otherBegin) { return false; } } return thisBegin == thisEnd && otherBegin == otherEnd; } GeoDataLatLonAltBox GeoDataContainer::latLonAltBox() const { Q_D(const GeoDataContainer); GeoDataLatLonAltBox result; QVector::const_iterator it = d->m_vector.constBegin(); QVector::const_iterator end = d->m_vector.constEnd(); for (; it != end; ++it) { // Get all the placemarks from GeoDataContainer if (const GeoDataPlacemark *placemark = geodata_cast(*it)) { // Only use visible placemarks for extracting their latLonAltBox and // making an union with the global latLonAltBox Marble will fit its // zoom to if (placemark->isVisible()) { if (result.isEmpty()) { result = placemark->geometry()->latLonAltBox(); } else { result |= placemark->geometry()->latLonAltBox(); } } } else if (const GeoDataContainer *container = dynamic_cast(*it)) { if (result.isEmpty()) { result = container->latLonAltBox(); } else { result |= container->latLonAltBox(); } } } return result; } QVector GeoDataContainer::folderList() const { Q_D(const GeoDataContainer); QVector results; QVector::const_iterator it = d->m_vector.constBegin(); QVector::const_iterator end = d->m_vector.constEnd(); for (; it != end; ++it) { GeoDataFolder *folder = dynamic_cast(*it); if ( folder ) { results.append( folder ); } } return results; } QVector GeoDataContainer::placemarkList() const { Q_D(const GeoDataContainer); QVector results; for (auto it = d->m_vector.constBegin(), end = d->m_vector.constEnd(); it != end; ++it) { if (GeoDataPlacemark *placemark = geodata_cast(*it)) { results.append(placemark); } } return results; } QVector GeoDataContainer::featureList() const { Q_D(const GeoDataContainer); return d->m_vector; } /** * @brief returns the requested child item */ GeoDataFeature* GeoDataContainer::child( int i ) { Q_D(GeoDataContainer); return d->m_vector.at(i); } const GeoDataFeature* GeoDataContainer::child( int i ) const { Q_D(const GeoDataContainer); return d->m_vector.at(i); } /** * @brief returns the position of an item in the list */ int GeoDataContainer::childPosition( const GeoDataFeature* object ) const { Q_D(const GeoDataContainer); for (int i = 0; i < d->m_vector.size(); ++i) { if (d->m_vector.at(i) == object) { return i; } } return -1; } void GeoDataContainer::insert( GeoDataFeature *other, int index ) { insert( index, other ); } void GeoDataContainer::insert( int index, GeoDataFeature *feature ) { Q_D(GeoDataContainer); feature->setParent(this); d->m_vector.insert( index, feature ); } void GeoDataContainer::append( GeoDataFeature *other ) { Q_D(GeoDataContainer); other->setParent(this); d->m_vector.append( other ); } void GeoDataContainer::remove( int index ) { Q_D(GeoDataContainer); d->m_vector.remove( index ); } void GeoDataContainer::remove(int index, int count) { Q_D(GeoDataContainer); d->m_vector.remove( index, count ); } int GeoDataContainer::removeAll(GeoDataFeature *feature) { Q_D(GeoDataContainer); #if QT_VERSION >= 0x050400 return d->m_vector.removeAll(feature); #else int count = 0; QVector &vector = d->m_vector; QVector::iterator it = vector.begin(); while(it != vector.end()) { if (*it == feature) { it = vector.erase(it); ++count; } else { ++it; } } return count; #endif } void GeoDataContainer::removeAt(int index) { Q_D(GeoDataContainer); d->m_vector.removeAt( index ); } void GeoDataContainer::removeFirst() { Q_D(GeoDataContainer); d->m_vector.removeFirst(); } void GeoDataContainer::removeLast() { Q_D(GeoDataContainer); d->m_vector.removeLast(); } bool GeoDataContainer::removeOne( GeoDataFeature *feature ) { Q_D(GeoDataContainer); #if QT_VERSION >= 0x050400 return d->m_vector.removeOne( feature ); #else QVector &vector = d->m_vector; const int i = vector.indexOf(feature); if (i < 0) { return false; } vector.remove(i); return true; #endif } int GeoDataContainer::size() const { Q_D(const GeoDataContainer); return d->m_vector.size(); } +bool GeoDataContainer::isEmpty() const +{ + return size() == 0; +} + GeoDataFeature& GeoDataContainer::at( int pos ) { Q_D(GeoDataContainer); return *(d->m_vector[pos]); } const GeoDataFeature& GeoDataContainer::at( int pos ) const { Q_D(const GeoDataContainer); return *(d->m_vector.at(pos)); } GeoDataFeature& GeoDataContainer::last() { Q_D(GeoDataContainer); return *(d->m_vector.last()); } const GeoDataFeature& GeoDataContainer::last() const { Q_D(const GeoDataContainer); return *(d->m_vector.last()); } GeoDataFeature& GeoDataContainer::first() { Q_D(GeoDataContainer); return *(d->m_vector.first()); } const GeoDataFeature& GeoDataContainer::first() const { Q_D(const GeoDataContainer); return *(d->m_vector.first()); } void GeoDataContainer::clear() { Q_D(GeoDataContainer); qDeleteAll(d->m_vector); d->m_vector.clear(); } QVector::Iterator GeoDataContainer::begin() { Q_D(GeoDataContainer); return d->m_vector.begin(); } QVector::Iterator GeoDataContainer::end() { Q_D(GeoDataContainer); return d->m_vector.end(); } QVector::ConstIterator GeoDataContainer::constBegin() const { Q_D(const GeoDataContainer); return d->m_vector.constBegin(); } QVector::ConstIterator GeoDataContainer::constEnd() const { Q_D(const GeoDataContainer); return d->m_vector.constEnd(); } void GeoDataContainer::pack( QDataStream& stream ) const { Q_D(const GeoDataContainer); GeoDataFeature::pack( stream ); stream << d->m_vector.count(); for (QVector::const_iterator iterator = d->m_vector.constBegin(); iterator != d->m_vector.constEnd(); ++iterator ) { const GeoDataFeature *feature = *iterator; stream << feature->featureId(); feature->pack( stream ); } } void GeoDataContainer::unpack( QDataStream& stream ) { Q_D(GeoDataContainer); GeoDataFeature::unpack( stream ); int count; stream >> count; for ( int i = 0; i < count; ++i ) { int featureId; stream >> featureId; switch( featureId ) { case GeoDataDocumentId: /* not usable!!!! */ break; case GeoDataFolderId: { GeoDataFolder *folder = new GeoDataFolder; folder->unpack( stream ); d->m_vector.append( folder ); } break; case GeoDataPlacemarkId: { GeoDataPlacemark *placemark = new GeoDataPlacemark; placemark->unpack( stream ); d->m_vector.append( placemark ); } break; case GeoDataNetworkLinkId: break; case GeoDataScreenOverlayId: break; case GeoDataGroundOverlayId: break; default: break; }; } } } diff --git a/src/lib/marble/geodata/data/GeoDataContainer.h b/src/lib/marble/geodata/data/GeoDataContainer.h index b991e419f..edea0fef8 100644 --- a/src/lib/marble/geodata/data/GeoDataContainer.h +++ b/src/lib/marble/geodata/data/GeoDataContainer.h @@ -1,184 +1,189 @@ // // 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 Murad Tagirov // Copyright 2007 Inge Wallin // Copyright 2009 Patrick Spendrin // #ifndef MARBLE_GEODATACONTAINER_H #define MARBLE_GEODATACONTAINER_H #include #include "geodata_export.h" #include "GeoDataFeature.h" namespace Marble { class GeoDataContainerPrivate; class GeoDataFolder; class GeoDataPlacemark; class GeoDataLatLonAltBox; /** * @short A base class that can hold GeoDataFeatures * * GeoDataContainer is the base class for the GeoData container * classes GeoDataFolder and GeoDataDocument. It is never * instantiated by itself, but is always used as part of a derived * class. * * It is based on GeoDataFeature, and it only adds a * QVector to it, making it a Feature that can hold * other Features. * * @see GeoDataFolder * @see GeoDataDocument */ class GEODATA_EXPORT GeoDataContainer : public GeoDataFeature { public: /// Default constructor GeoDataContainer(); GeoDataContainer( const GeoDataContainer& other ); /// Destruct the GeoDataContainer ~GeoDataContainer() override; GeoDataContainer& operator=(const GeoDataContainer& other); /** * @brief A convenience function that returns the LatLonAltBox of all * placemarks in this container. * @return The GeoDataLatLonAltBox * * @see GeoDataLatLonAltBox */ GeoDataLatLonAltBox latLonAltBox() const; /** * @brief A convenience function that returns all folders in this container. * @return A QVector of GeoDataFolder * * @see GeoDataFolder */ QVector folderList() const; /** * @brief A convenience function that returns all features in this container. * @return A QVector of GeoDataFeature * * @see GeoDataFeature */ QVector featureList() const; /** * @brief A convenience function that returns all placemarks in this container. * @return A QVector of GeoDataPlacemark * * @see GeoDataPlacemark */ QVector placemarkList() const; /** * @brief returns the requested child item */ GeoDataFeature* child( int ); /** * @brief returns the requested child item */ const GeoDataFeature* child( int ) const; /** * @brief returns the position of an item in the list */ int childPosition( const GeoDataFeature *child) const; /** * @brief inserts @p feature at position @p index in the container */ void insert( int index, GeoDataFeature *feature ); GEODATA_DEPRECATED void insert(GeoDataFeature *other, int index); /** * @brief add an element */ void append( GeoDataFeature *other ); void remove( int index ); void remove(int index, int count); int removeAll(GeoDataFeature* feature); void removeAt(int index); void removeFirst(); void removeLast(); bool removeOne( GeoDataFeature *feature ); /** * @brief size of the container */ int size() const; + /** + * @brief Returns true if the container has size 0; otherwise returns false. + */ + bool isEmpty() const; + /** * @brief return the reference of the element at a specific position */ GeoDataFeature& at( int pos ); const GeoDataFeature& at( int pos ) const; /** * @brief return the reference of the last element for convenience */ GeoDataFeature& last(); const GeoDataFeature& last() const; /** * @brief return the reference of the last element for convenience */ GeoDataFeature& first(); const GeoDataFeature& first() const; QVector::Iterator begin(); QVector::Iterator end(); QVector::ConstIterator constBegin() const; QVector::ConstIterator constEnd() const; void clear(); /** * @brief Serialize the container to a stream. * @param stream the stream */ void pack( QDataStream& stream ) const override; /** * @brief Unserialize the container from a stream * @param stream the stream */ void unpack( QDataStream& stream ) override; protected: explicit GeoDataContainer(GeoDataContainerPrivate *priv); GeoDataContainer(const GeoDataContainer& other, GeoDataContainerPrivate *priv); bool equals( const GeoDataContainer &other ) const; using GeoDataFeature::equals; private: Q_DECLARE_PRIVATE(GeoDataContainer) }; } #endif diff --git a/src/lib/marble/routing/RoutingManager.cpp b/src/lib/marble/routing/RoutingManager.cpp index b89712f16..a67ac0266 100644 --- a/src/lib/marble/routing/RoutingManager.cpp +++ b/src/lib/marble/routing/RoutingManager.cpp @@ -1,646 +1,646 @@ // // 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 2010 Dennis Nienhüser // #include "RoutingManager.h" #include "AlternativeRoutesModel.h" #include "MarbleModel.h" #include "RouteRequest.h" #include "RoutingModel.h" #include "RoutingProfilesModel.h" #include "RoutingRunnerPlugin.h" #include "GeoWriter.h" #include "GeoDataDocument.h" #include "GeoDataExtendedData.h" #include "GeoDataData.h" #include "GeoDataFolder.h" #include "GeoDataParser.h" #include "GeoDataPlacemark.h" #include "GeoDataTreeModel.h" #include "MarbleDirs.h" #include "MarbleDebug.h" #include "PositionTracking.h" #include "PluginManager.h" #include "PositionProviderPlugin.h" #include "Route.h" #include "RoutingRunnerManager.h" #include #include #include #include #include namespace Marble { class RoutingManagerPrivate { public: RoutingManager* q; RouteRequest m_routeRequest; RoutingModel m_routingModel; RoutingProfilesModel m_profilesModel; RoutingManager::State m_state; const PluginManager *const m_pluginManager; GeoDataTreeModel *const m_treeModel; PositionTracking *const m_positionTracking; AlternativeRoutesModel m_alternativeRoutesModel; RoutingRunnerManager m_runnerManager; bool m_haveRoute; bool m_guidanceModeEnabled; QMutex m_fileMutex; bool m_shutdownPositionTracking; bool m_guidanceModeWarning; QString m_lastOpenPath; QString m_lastSavePath; QColor m_routeColorStandard; QColor m_routeColorHighlighted; QColor m_routeColorAlternative; RoutingManagerPrivate( MarbleModel *marbleModel, RoutingManager* manager, QObject *parent ); GeoDataFolder* routeRequest() const; static QString stateFile( const QString &name = QString( "route.kml" ) ); void saveRoute( const QString &filename ); void loadRoute( const QString &filename ); void addRoute( GeoDataDocument* route ); void routingFinished(); void setCurrentRoute( GeoDataDocument *route ); void recalculateRoute( bool deviated ); static void importPlacemark( RouteSegment &outline, QVector &segments, const GeoDataPlacemark *placemark ); }; RoutingManagerPrivate::RoutingManagerPrivate( MarbleModel *model, RoutingManager* manager, QObject *parent ) : q( manager ), m_routeRequest( manager ), m_routingModel( &m_routeRequest, model, manager ), m_profilesModel( model->pluginManager() ), m_state( RoutingManager::Retrieved ), m_pluginManager( model->pluginManager() ), m_treeModel( model->treeModel() ), m_positionTracking( model->positionTracking() ), m_alternativeRoutesModel( parent ), m_runnerManager( model, q ), m_haveRoute( false ), m_guidanceModeEnabled( false ), m_shutdownPositionTracking( false ), m_guidanceModeWarning( true ), m_routeColorStandard( Oxygen::skyBlue4 ), m_routeColorHighlighted( Oxygen::skyBlue1 ), m_routeColorAlternative( Oxygen::aluminumGray4 ) { m_routeColorStandard.setAlpha( 200 ); m_routeColorHighlighted.setAlpha( 200 ); m_routeColorAlternative.setAlpha( 200 ); } GeoDataFolder* RoutingManagerPrivate::routeRequest() const { GeoDataFolder* result = new GeoDataFolder; result->setName(QStringLiteral("Route Request")); for ( int i=0; iappend( placemark ); } return result; } QString RoutingManagerPrivate::stateFile( const QString &name) { QString const subdir = "routing"; QDir dir( MarbleDirs::localPath() ); if ( !dir.exists( subdir ) ) { if ( !dir.mkdir( subdir ) ) { mDebug() << "Unable to create dir " << dir.absoluteFilePath( subdir ); return dir.absolutePath(); } } if ( !dir.cd( subdir ) ) { mDebug() << "Cannot change into " << dir.absoluteFilePath( subdir ); } return dir.absoluteFilePath( name ); } void RoutingManagerPrivate::saveRoute(const QString &filename) { GeoWriter writer; writer.setDocumentType( kml::kmlTag_nameSpaceOgc22 ); QMutexLocker locker( &m_fileMutex ); QFile file( filename ); if ( !file.open( QIODevice::WriteOnly | QIODevice::Truncate ) ) { mDebug() << "Cannot write to " << file.fileName(); return; } GeoDataDocument container; container.setName(QStringLiteral("Route")); GeoDataFolder* request = routeRequest(); if ( request ) { container.append( request ); } GeoDataDocument *route = m_alternativeRoutesModel.currentRoute(); if ( route ) { container.append( new GeoDataDocument( *route ) ); } if ( !writer.write( &file, &container ) ) { mDebug() << "Can not write route state to " << file.fileName(); } file.close(); } void RoutingManagerPrivate::loadRoute(const QString &filename) { QFile file( filename ); if ( !file.open( QIODevice::ReadOnly ) ) { mDebug() << "Can not read route from " << file.fileName(); return; } GeoDataParser parser( GeoData_KML ); if ( !parser.read( &file ) ) { mDebug() << "Could not parse file: " << parser.errorString(); return; } GeoDocument *doc = parser.releaseDocument(); file.close(); bool loaded = false; GeoDataDocument* container = dynamic_cast( doc ); - if ( container && container->size() > 0 ) { + if (container && !container->isEmpty()) { GeoDataFolder* viaPoints = dynamic_cast( &container->first() ); if ( viaPoints ) { loaded = true; QVector placemarks = viaPoints->placemarkList(); for( int i=0; i viaPoints_needed; --i ) { m_routeRequest.remove( viaPoints_needed ); } } else { mDebug() << "Expected a GeoDataDocument with at least one child, didn't get one though"; } } if ( container && container->size() == 2 ) { GeoDataDocument* route = dynamic_cast(&container->last()); if ( route ) { loaded = true; m_alternativeRoutesModel.clear(); m_alternativeRoutesModel.addRoute( new GeoDataDocument(*route), AlternativeRoutesModel::Instant ); m_alternativeRoutesModel.setCurrentRoute( 0 ); m_state = RoutingManager::Retrieved; emit q->stateChanged( m_state ); emit q->routeRetrieved( route ); } else { mDebug() << "Expected a GeoDataDocument child, didn't get one though"; } } if (loaded) { delete doc; // == container } else { mDebug() << "File " << filename << " is not a valid Marble route .kml file"; if ( container ) { m_treeModel->addDocument( container ); } } } RoutingManager::RoutingManager( MarbleModel *marbleModel, QObject *parent ) : QObject( parent ), d( new RoutingManagerPrivate( marbleModel, this, this ) ) { connect( &d->m_runnerManager, SIGNAL(routeRetrieved(GeoDataDocument*)), this, SLOT(addRoute(GeoDataDocument*)) ); connect( &d->m_runnerManager, SIGNAL(routingFinished()), this, SLOT(routingFinished()) ); connect( &d->m_alternativeRoutesModel, SIGNAL(currentRouteChanged(GeoDataDocument*)), this, SLOT(setCurrentRoute(GeoDataDocument*)) ); connect( &d->m_routingModel, SIGNAL(deviatedFromRoute(bool)), this, SLOT(recalculateRoute(bool)) ); } RoutingManager::~RoutingManager() { delete d; } RoutingProfilesModel *RoutingManager::profilesModel() { return &d->m_profilesModel; } RoutingModel *RoutingManager::routingModel() { return &d->m_routingModel; } const RoutingModel *RoutingManager::routingModel() const { return &d->m_routingModel; } RouteRequest* RoutingManager::routeRequest() { return &d->m_routeRequest; } RoutingManager::State RoutingManager::state() const { return d->m_state; } void RoutingManager::retrieveRoute() { d->m_haveRoute = false; int realSize = 0; for ( int i = 0; i < d->m_routeRequest.size(); ++i ) { // Sort out dummy targets if ( d->m_routeRequest.at( i ).isValid() ) { ++realSize; } } d->m_alternativeRoutesModel.newRequest( &d->m_routeRequest ); if ( realSize > 1 ) { d->m_state = RoutingManager::Downloading; d->m_runnerManager.retrieveRoute( &d->m_routeRequest ); } else { d->m_routingModel.clear(); d->m_state = RoutingManager::Retrieved; } emit stateChanged( d->m_state ); } void RoutingManagerPrivate::addRoute( GeoDataDocument* route ) { if ( route ) { m_alternativeRoutesModel.addRoute( route ); } if ( !m_haveRoute ) { m_haveRoute = route != 0; } emit q->routeRetrieved( route ); } void RoutingManagerPrivate::routingFinished() { m_state = RoutingManager::Retrieved; emit q->stateChanged( m_state ); } void RoutingManagerPrivate::setCurrentRoute( GeoDataDocument *document ) { Route route; QVector segments; RouteSegment outline; QVector folders = document->folderList(); for( const GeoDataFolder *folder: folders ) { for( const GeoDataPlacemark *placemark: folder->placemarkList() ) { importPlacemark( outline, segments, placemark ); } } for( const GeoDataPlacemark *placemark: document->placemarkList() ) { importPlacemark( outline, segments, placemark ); } if ( segments.isEmpty() ) { segments << outline; } // Map via points onto segments if ( m_routeRequest.size() > 1 && segments.size() > 1 ) { int index = 0; for ( int j = 0; j < m_routeRequest.size(); ++j ) { QPair minimum( -1, -1.0 ); int viaIndex = -1; for ( int i = index; i < segments.size(); ++i ) { const RouteSegment &segment = segments[i]; GeoDataCoordinates closest; const qreal distance = segment.distanceTo( m_routeRequest.at( j ), closest, closest ); if ( minimum.first < 0 || distance < minimum.second ) { minimum.first = i; minimum.second = distance; viaIndex = j; } } if ( minimum.first >= 0 ) { index = minimum.first; Maneuver viaPoint = segments[ minimum.first ].maneuver(); viaPoint.setWaypoint( m_routeRequest.at( viaIndex ), viaIndex ); segments[ minimum.first ].setManeuver( viaPoint ); } } } if ( segments.size() > 0 ) { for( const RouteSegment &segment: segments ) { route.addRouteSegment( segment ); } } m_routingModel.setRoute( route ); } void RoutingManagerPrivate::importPlacemark( RouteSegment &outline, QVector &segments, const GeoDataPlacemark *placemark ) { const GeoDataGeometry* geometry = placemark->geometry(); const GeoDataLineString* lineString = dynamic_cast( geometry ); QStringList blacklist = QStringList() << "" << "Route" << "Tessellated"; RouteSegment segment; bool isOutline = true; if ( !blacklist.contains( placemark->name() ) ) { if( lineString ) { Maneuver maneuver; maneuver.setInstructionText( placemark->name() ); maneuver.setPosition( lineString->at( 0 ) ); if (placemark->extendedData().contains(QStringLiteral("turnType"))) { QVariant turnType = placemark->extendedData().value(QStringLiteral("turnType")).value(); // The enum value is converted to/from an int in the QVariant // because only a limited set of data types can be serialized with QVariant's // toString() method (which is used to serialize / values) maneuver.setDirection( Maneuver::Direction( turnType.toInt() ) ); } if (placemark->extendedData().contains(QStringLiteral("roadName"))) { QVariant roadName = placemark->extendedData().value(QStringLiteral("roadName")).value(); maneuver.setRoadName( roadName.toString() ); } segment.setManeuver( maneuver ); isOutline = false; } } if ( lineString ) { segment.setPath( *lineString ); if ( isOutline ) { outline = segment; } else { segments.push_back( segment ); } } } AlternativeRoutesModel* RoutingManager::alternativeRoutesModel() { return &d->m_alternativeRoutesModel; } void RoutingManager::writeSettings() const { d->saveRoute( d->stateFile() ); } void RoutingManager::saveRoute( const QString &filename ) const { d->saveRoute( filename ); } void RoutingManager::loadRoute( const QString &filename ) { d->loadRoute( filename ); } RoutingProfile RoutingManager::defaultProfile( RoutingProfile::TransportType transportType ) const { RoutingProfile profile; RoutingProfilesModel::ProfileTemplate tpl = RoutingProfilesModel::CarFastestTemplate; switch ( transportType ) { case RoutingProfile::Motorcar: tpl = RoutingProfilesModel::CarFastestTemplate; profile.setName(QStringLiteral("Motorcar")); profile.setTransportType( RoutingProfile::Motorcar ); break; case RoutingProfile::Bicycle: tpl = RoutingProfilesModel::BicycleTemplate; profile.setName(QStringLiteral("Bicycle")); profile.setTransportType( RoutingProfile::Bicycle ); break; case RoutingProfile::Pedestrian: tpl = RoutingProfilesModel::PedestrianTemplate; profile.setName(QStringLiteral("Pedestrian")); profile.setTransportType( RoutingProfile::Pedestrian ); break; } for( RoutingRunnerPlugin* plugin: d->m_pluginManager->routingRunnerPlugins() ) { if ( plugin->supportsTemplate( tpl ) ) { profile.pluginSettings()[plugin->nameId()] = plugin->templateSettings( tpl ); } } return profile; } void RoutingManager::readSettings() { d->loadRoute( d->stateFile() ); } void RoutingManager::setGuidanceModeEnabled( bool enabled ) { if ( d->m_guidanceModeEnabled == enabled ) { return; } d->m_guidanceModeEnabled = enabled; if ( enabled ) { d->saveRoute( d->stateFile( "guidance.kml" ) ); if ( d->m_guidanceModeWarning ) { QString text = QLatin1String("

") + tr("Caution: Driving instructions may be incomplete or wrong.") + QLatin1Char(' ') + tr("Road construction, weather and other unforeseen variables can result in the suggested route not to be the most expedient or safest route to your destination.") + QLatin1Char(' ') + tr("Please use common sense while navigating.") + QLatin1String("

") + QLatin1String("

") + tr("The Marble development team wishes you a pleasant and safe journey.") + QLatin1String("

"); QPointer messageBox = new QMessageBox(QMessageBox::Information, tr("Guidance Mode"), text, QMessageBox::Ok); QCheckBox *showAgain = new QCheckBox( tr( "Show again" ) ); showAgain->setChecked( true ); showAgain->blockSignals( true ); // otherwise it'd close the dialog messageBox->addButton( showAgain, QMessageBox::ActionRole ); const bool smallScreen = MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen; messageBox->resize( 380, smallScreen ? 400 : 240 ); messageBox->exec(); if ( !messageBox.isNull() ) { d->m_guidanceModeWarning = showAgain->isChecked(); } delete messageBox; } } else { d->loadRoute( d->stateFile( "guidance.kml" ) ); } PositionProviderPlugin* positionProvider = d->m_positionTracking->positionProviderPlugin(); if ( !positionProvider && enabled ) { QList plugins = d->m_pluginManager->positionProviderPlugins(); if ( plugins.size() > 0 ) { positionProvider = plugins.first()->newInstance(); } d->m_positionTracking->setPositionProviderPlugin( positionProvider ); d->m_shutdownPositionTracking = true; } else if ( positionProvider && !enabled && d->m_shutdownPositionTracking ) { d->m_shutdownPositionTracking = false; d->m_positionTracking->setPositionProviderPlugin( 0 ); } emit guidanceModeEnabledChanged( d->m_guidanceModeEnabled ); } void RoutingManagerPrivate::recalculateRoute( bool deviated ) { if ( m_guidanceModeEnabled && deviated ) { for ( int i=m_routeRequest.size()-3; i>=0; --i ) { if ( m_routeRequest.visited( i ) ) { m_routeRequest.remove( i ); } } if ( m_routeRequest.size() == 2 && m_routeRequest.visited( 0 ) && !m_routeRequest.visited( 1 ) ) { m_routeRequest.setPosition( 0, m_positionTracking->currentLocation(), QObject::tr( "Current Location" ) ); q->retrieveRoute(); } else if ( m_routeRequest.size() != 0 && !m_routeRequest.visited( m_routeRequest.size()-1 ) ) { m_routeRequest.insert( 0, m_positionTracking->currentLocation(), QObject::tr( "Current Location" ) ); q->retrieveRoute(); } } } void RoutingManager::reverseRoute() { d->m_routeRequest.reverse(); retrieveRoute(); } void RoutingManager::clearRoute() { d->m_routeRequest.clear(); retrieveRoute(); } void RoutingManager::setShowGuidanceModeStartupWarning( bool show ) { d->m_guidanceModeWarning = show; } bool RoutingManager::showGuidanceModeStartupWarning() const { return d->m_guidanceModeWarning; } void RoutingManager::setLastOpenPath( const QString &path ) { d->m_lastOpenPath = path; } QString RoutingManager::lastOpenPath() const { return d->m_lastOpenPath; } void RoutingManager::setLastSavePath( const QString &path ) { d->m_lastSavePath = path; } QString RoutingManager::lastSavePath() const { return d->m_lastSavePath; } void RoutingManager::setRouteColorStandard( const QColor& color ) { d->m_routeColorStandard = color; } QColor RoutingManager::routeColorStandard() const { return d->m_routeColorStandard; } void RoutingManager::setRouteColorHighlighted( const QColor& color ) { d->m_routeColorHighlighted = color; } QColor RoutingManager::routeColorHighlighted() const { return d->m_routeColorHighlighted; } void RoutingManager::setRouteColorAlternative( const QColor& color ) { d->m_routeColorAlternative = color; } QColor RoutingManager::routeColorAlternative() const { return d->m_routeColorAlternative; } bool RoutingManager::guidanceModeEnabled() const { return d->m_guidanceModeEnabled; } } // namespace Marble #include "moc_RoutingManager.cpp" diff --git a/src/plugins/render/elevationprofilemarker/ElevationProfileMarker.cpp b/src/plugins/render/elevationprofilemarker/ElevationProfileMarker.cpp index 5b08a8bbe..160449803 100644 --- a/src/plugins/render/elevationprofilemarker/ElevationProfileMarker.cpp +++ b/src/plugins/render/elevationprofilemarker/ElevationProfileMarker.cpp @@ -1,230 +1,230 @@ // // 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 2012 Bernhard Beschow // Copyright 2011-2012 Florian Eßer // #include "ElevationProfileMarker.h" #include "MarbleLocale.h" #include "MarbleModel.h" #include "GeoDataDocument.h" #include "GeoDataPlacemark.h" #include "GeoPainter.h" #include "GeoDataTreeModel.h" #include "ViewportParams.h" #include "MarbleGraphicsGridLayout.h" #include #include namespace Marble { ElevationProfileMarker::ElevationProfileMarker( const MarbleModel *marbleModel ) : RenderPlugin( marbleModel ), m_markerPlacemark( 0 ), m_markerItem(), m_markerIcon( &m_markerItem ), m_markerText( &m_markerItem ) { if ( !marbleModel ) { return; } setVisible( false ); m_markerItem.setCacheMode( MarbleGraphicsItem::ItemCoordinateCache ); connect( const_cast( marbleModel )->treeModel(), SIGNAL(added(GeoDataObject*)), this, SLOT(onGeoObjectAdded(GeoDataObject*)) ); connect( const_cast( marbleModel )->treeModel(), SIGNAL(removed(GeoDataObject*)), this, SLOT(onGeoObjectRemoved(GeoDataObject*)) ); } ElevationProfileMarker::~ElevationProfileMarker() { } QStringList ElevationProfileMarker::backendTypes() const { return QStringList(QStringLiteral("elevationprofilemarker")); } QString ElevationProfileMarker::renderPolicy() const { return QStringLiteral("ALWAYS"); } QStringList ElevationProfileMarker::renderPosition() const { return QStringList(QStringLiteral("HOVERS_ABOVE_SURFACE")); } qreal ElevationProfileMarker::zValue() const { return 3.0; } QString ElevationProfileMarker::name() const { return tr( "Elevation Profile Marker" ); } QString ElevationProfileMarker::guiString() const { return tr( "&Elevation Profile Marker" ); } QString ElevationProfileMarker::nameId() const { return QStringLiteral("elevationprofilemarker"); } QString ElevationProfileMarker::version() const { return QStringLiteral("1.0"); } QString ElevationProfileMarker::description() const { return tr( "Marks the current elevation of the elevation profile on the map." ); } QString ElevationProfileMarker::copyrightYears() const { return QStringLiteral("2011, 2012"); } QVector ElevationProfileMarker::pluginAuthors() const { return QVector() << PluginAuthor(QStringLiteral("Bernhard Beschow"), QStringLiteral("bbeschow@cs.tu-berlin.de")) << PluginAuthor(QStringLiteral("Florian Eßer"), QStringLiteral("f.esser@rwth-aachen.de")); } QIcon ElevationProfileMarker::icon() const { return QIcon(QStringLiteral(":/icons/elevationprofile.png")); } void ElevationProfileMarker::initialize() { m_markerIcon.setImage(QImage(QStringLiteral(":/flag-red-mirrored.png"))); MarbleGraphicsGridLayout *topLayout = new MarbleGraphicsGridLayout( 1, 2 ); m_markerItem.setLayout( topLayout ); topLayout->addItem( &m_markerIcon, 0, 0 ); m_markerText.setFrame( LabelGraphicsItem::RoundedRectFrame ); m_markerText.setPadding( 1 ); topLayout->addItem( &m_markerText, 0, 1 ); } bool ElevationProfileMarker::isInitialized() const { return !m_markerIcon.image().isNull(); } bool ElevationProfileMarker::render( GeoPainter* painter, ViewportParams* viewport, const QString& renderPos, GeoSceneLayer* layer ) { Q_UNUSED( renderPos ) Q_UNUSED( layer ) if ( !m_markerPlacemark ) return true; if ( m_currentPosition != m_markerPlacemark->coordinate() ) { m_currentPosition = m_markerPlacemark->coordinate(); if ( m_currentPosition.isValid() ) { QString unitString = tr( "m" ); int displayScale = 1.0; const MarbleLocale::MeasurementSystem measurementSystem = MarbleGlobal::getInstance()->locale()->measurementSystem(); switch ( measurementSystem ) { case MarbleLocale::MetricSystem: /* nothing to do */ break; case MarbleLocale::ImperialSystem: unitString = tr( "ft" ); displayScale = M2FT; break; case MarbleLocale::NauticalSystem: unitString = tr("ft"); displayScale = 0.001 * KM2NM * NM2FT; } QString intervalStr; intervalStr.setNum( m_currentPosition.altitude() * displayScale, 'f', 1 ); intervalStr += QLatin1Char(' ') + unitString; m_markerText.setText( intervalStr ); } } if ( m_currentPosition.isValid() ) { qreal x; qreal y; qreal lon; qreal lat; // move the icon by some pixels, so that the pole of the flag sits at the exact point int dx = +3 + m_markerItem.size().width() / 2 - m_markerIcon.contentRect().right();//-4; int dy = -6; viewport->screenCoordinates( m_currentPosition.longitude( GeoDataCoordinates::Radian ), m_currentPosition.latitude ( GeoDataCoordinates::Radian ), x, y ); viewport->geoCoordinates( x + dx, y + dy, lon, lat, GeoDataCoordinates::Radian ); m_markerItem.setCoordinate( GeoDataCoordinates( lon, lat, m_currentPosition.altitude(), GeoDataCoordinates::Radian ) ); painter->save(); m_markerItem.paintEvent( painter, viewport ); painter->restore(); } return true; } void ElevationProfileMarker::onGeoObjectAdded( GeoDataObject *object ) { if ( m_markerPlacemark ) return; GeoDataDocument *document = dynamic_cast( object ); if ( !document ) return; if (document->name() != QLatin1String("Elevation Profile")) return; - if ( document->size() < 1 ) + if (document->isEmpty()) return; m_markerPlacemark = dynamic_cast( document->child( 0 ) ); setVisible( m_markerPlacemark != 0 ); } void ElevationProfileMarker::onGeoObjectRemoved( GeoDataObject *object ) { GeoDataDocument *const document = dynamic_cast( object ); if ( !document ) return; if (document->name() != QLatin1String("Elevation Profile")) return; m_markerPlacemark = 0; emit repaintNeeded(); } } #include "moc_ElevationProfileMarker.cpp" diff --git a/src/plugins/runner/pnt/PntRunner.cpp b/src/plugins/runner/pnt/PntRunner.cpp index 07f31d2d8..5b489d4ac 100644 --- a/src/plugins/runner/pnt/PntRunner.cpp +++ b/src/plugins/runner/pnt/PntRunner.cpp @@ -1,212 +1,212 @@ // // This file is part of the Marble Virtual Globe. // // This program is free software licensed under the GNU LGPL. You can // find a copy of this license in LICENSE.txt in the top directory of // the source code. // // Copyright 2011 Thibaut Gridel // Copyright 2012 Bernhard Beschow #include "PntRunner.h" #include "GeoDataDocument.h" #include "GeoDataLineString.h" #include "GeoDataLinearRing.h" #include "GeoDataPlacemark.h" #include "MarbleDebug.h" #include "MarbleGlobal.h" #include #include #include #include namespace Marble { // distance of 180deg in arcminutes const qreal INT2RAD = M_PI / 10800.0; PntRunner::PntRunner(QObject *parent) : ParsingRunner(parent) { } PntRunner::~PntRunner() { } GeoDataDocument *PntRunner::parseFile(const QString &fileName, DocumentRole role, QString &errorString) { QFileInfo fileinfo( fileName ); if (fileinfo.suffix().compare(QLatin1String("pnt"), Qt::CaseInsensitive) != 0) { errorString = QStringLiteral("File %1 does not have a pnt suffix").arg(fileName); mDebug() << errorString; return nullptr; } QFile file( fileName ); if ( !file.exists() ) { errorString = QStringLiteral("File %1 does not exist").arg(fileName); mDebug() << errorString; return nullptr; } file.open( QIODevice::ReadOnly ); QDataStream stream( &file ); // read the data serialized from the file stream.setByteOrder( QDataStream::LittleEndian ); GeoDataDocument *document = new GeoDataDocument(); document->setDocumentRole( role ); GeoDataPlacemark *placemark = 0; int count = 0; bool error = false; while( !stream.atEnd() || error ){ short header = -1; short iLat = -5400 - 1; short iLon = -10800 - 1; stream >> header >> iLat >> iLon; // make sure iLat is within valid range if ( !( -5400 <= iLat && iLat <= 5400 ) ) { mDebug() << Q_FUNC_INFO << "invalid iLat =" << iLat << "(" << ( iLat * INT2RAD ) * RAD2DEG << ") in dataset" << count << "of file" << fileName; error = true; } // make sure iLon is within valid range if ( !( -10800 <= iLon && iLon <= 10800 ) ) { mDebug() << Q_FUNC_INFO << "invalid iLon =" << iLon << "(" << ( iLon * INT2RAD ) * RAD2DEG << ") in dataset" << count << "of file" << fileName; error = true; } - if ( header >= 1000 && document->size() > 0 ) { + if (header >= 1000 && !document->isEmpty()) { GeoDataLineString *const polyline = static_cast( placemark->geometry() ); if ( polyline->size() == 1 ) { mDebug() << Q_FUNC_INFO << fileName << "contains single-point polygon at" << count << ". Aborting."; error = true; break; } } if ( header < 1 ) { /* invalid header */ mDebug() << Q_FUNC_INFO << "invalid header:" << header << "in" << fileName << "at" << count; error = true; break; } else if ( header <= 5 ) { /* header represents level of detail */ /* nothing to do */ } else if ( header < 1000 ) { /* invalid header */ mDebug() << Q_FUNC_INFO << "invalid header:" << header << "in" << fileName << "at" << count; error = true; break; } else if ( header < 2000 ) { /* header represents start of coastline */ placemark = new GeoDataPlacemark; document->append( placemark ); placemark->setGeometry( new GeoDataLinearRing ); } else if ( header < 4000 ) { /* header represents start of country border */ placemark = new GeoDataPlacemark; document->append( placemark ); placemark->setGeometry( new GeoDataLineString ); } else if ( header < 5000 ) { /* header represents start of internal political border */ placemark = new GeoDataPlacemark; document->append( placemark ); placemark->setGeometry( new GeoDataLineString ); } else if ( header < 6000 ) { /* header represents start of island */ placemark = new GeoDataPlacemark; document->append( placemark ); placemark->setGeometry( new GeoDataLinearRing ); } else if ( header < 7000 ) { /* header represents start of lake */ placemark = new GeoDataPlacemark; document->append( placemark ); placemark->setGeometry( new GeoDataLinearRing ); } else if ( header < 8000 ) { /* header represents start of river */ placemark = new GeoDataPlacemark; document->append( placemark ); placemark->setGeometry( new GeoDataLineString ); } else if ( header < 9000 ) { /* custom header represents start of glaciers, lakes or islands */ placemark = new GeoDataPlacemark; document->append( placemark ); placemark->setGeometry( new GeoDataLinearRing ); } else if ( header < 10000 ) { /* custom header represents start of political borders */ placemark = new GeoDataPlacemark; document->append( placemark ); placemark->setGeometry( new GeoDataLineString ); } else if ( header < 14000 ) { /* invalid header */ mDebug() << Q_FUNC_INFO << "invalid header:" << header << "in" << fileName << "at" << count; error = true; break; } else if ( header < 15000 ) { /* custom header represents start of political borders */ placemark = new GeoDataPlacemark; document->append( placemark ); placemark->setGeometry( new GeoDataLineString ); } else if ( header < 19000 ) { /* invalid header */ mDebug() << Q_FUNC_INFO << "invalid header:" << header << "in" << fileName << "at" << count; error = true; break; } else if ( header < 20000 ) { /* custom header represents start of dateline */ placemark = new GeoDataPlacemark; document->append( placemark ); placemark->setGeometry( new GeoDataLineString ); } else { /* invalid header */ mDebug() << Q_FUNC_INFO << "invalid header:" << header << "in" << fileName << "at" << count; error = true; break; } GeoDataLineString *polyline = static_cast( placemark->geometry() ); // Transforming Range of Coordinates to iLat [0,ARCMINUTE] , iLon [0,2 * ARCMINUTE] polyline->append( GeoDataCoordinates( (qreal)(iLon) * INT2RAD, (qreal)(iLat) * INT2RAD, 0.0, GeoDataCoordinates::Radian, 5 - qMin( 5, (int)header ) ) ); // if 1 <= header <= 5, header contains level of detail // else pick most sparse level of detail, which equals 0 ++count; } file.close(); - if ( document->size() == 0 || error ) { + if (document->isEmpty() || error) { delete document; document = 0; return nullptr; } document->setFileName( fileName ); return document; } } #include "moc_PntRunner.cpp" diff --git a/src/plugins/runner/shp/ShpRunner.cpp b/src/plugins/runner/shp/ShpRunner.cpp index 17d039f52..e24720633 100644 --- a/src/plugins/runner/shp/ShpRunner.cpp +++ b/src/plugins/runner/shp/ShpRunner.cpp @@ -1,232 +1,232 @@ // // This file is part of the Marble Virtual Globe. // // This program is free software licensed under the GNU LGPL. You can // find a copy of this license in LICENSE.txt in the top directory of // the source code. // // Copyright 2011 Thibaut Gridel #include "ShpRunner.h" #include "GeoDataDocument.h" #include "GeoDataPlacemark.h" #include "GeoDataPolygon.h" #include "GeoDataLinearRing.h" #include "GeoDataPoint.h" #include "GeoDataMultiGeometry.h" #include "GeoDataSchema.h" #include "GeoDataSimpleField.h" #include "GeoDataStyle.h" #include "GeoDataPolyStyle.h" #include "MarbleDebug.h" #include #include namespace Marble { ShpRunner::ShpRunner(QObject *parent) : ParsingRunner(parent) { } ShpRunner::~ShpRunner() { } GeoDataDocument *ShpRunner::parseFile(const QString &fileName, DocumentRole role, QString &error) { QFileInfo fileinfo( fileName ); if (fileinfo.suffix().compare(QLatin1String("shp"), Qt::CaseInsensitive) != 0) { error = QStringLiteral("File %1 does not have a shp suffix").arg(fileName); mDebug() << error; return nullptr; } SHPHandle handle = SHPOpen( fileName.toStdString().c_str(), "rb" ); if ( !handle ) { error = QStringLiteral("Failed to read %1").arg(fileName); mDebug() << error; return nullptr; } int entities; int shapeType; SHPGetInfo( handle, &entities, &shapeType, NULL, NULL ); mDebug() << " SHP info " << entities << " Entities " << shapeType << " Shape Type "; DBFHandle dbfhandle; dbfhandle = DBFOpen( fileName.toStdString().c_str(), "rb"); int nameField = DBFGetFieldIndex( dbfhandle, "Name" ); int noteField = DBFGetFieldIndex( dbfhandle, "Note" ); int mapColorField = DBFGetFieldIndex( dbfhandle, "mapcolor13" ); GeoDataDocument *document = new GeoDataDocument; document->setDocumentRole( role ); if ( mapColorField != -1 ) { GeoDataSchema schema; schema.setId(QStringLiteral("default")); GeoDataSimpleField simpleField; simpleField.setName(QStringLiteral("mapcolor13")); simpleField.setType( GeoDataSimpleField::Double ); schema.addSimpleField( simpleField ); document->addSchema( schema ); } for ( int i=0; i< entities; ++i ) { GeoDataPlacemark *placemark = 0; placemark = new GeoDataPlacemark; document->append( placemark ); SHPObject *shape = SHPReadObject( handle, i ); if (nameField != -1) { const char* info = DBFReadStringAttribute( dbfhandle, i, nameField ); // TODO: defaults to utf-8 encoding, but could be also something else, optionally noted in a .cpg file placemark->setName( info ); mDebug() << "name " << placemark->name(); } if (noteField != -1) { const char* note = DBFReadStringAttribute( dbfhandle, i, noteField ); // TODO: defaults to utf-8 encoding, see comment for name placemark->setDescription( note ); mDebug() << "desc " << placemark->description(); } double mapColor = DBFReadDoubleAttribute( dbfhandle, i, mapColorField ); if ( mapColor ) { GeoDataStyle::Ptr style(new GeoDataStyle); if ( mapColor >= 0 && mapColor <=255 ) { quint8 colorIndex = quint8( mapColor ); style->polyStyle().setColorIndex( colorIndex ); } else { quint8 colorIndex = 0; // mapColor is undefined in this case style->polyStyle().setColorIndex( colorIndex ); } placemark->setStyle( style ); } switch ( shapeType ) { case SHPT_POINT: { GeoDataPoint *point = new GeoDataPoint( *shape->padfX, *shape->padfY, 0, GeoDataCoordinates::Degree ); placemark->setGeometry( point ); mDebug() << "point " << placemark->name(); break; } case SHPT_MULTIPOINT: { GeoDataMultiGeometry *geom = new GeoDataMultiGeometry; for( int j=0; jnVertices; ++j ) { geom->append( new GeoDataPoint( GeoDataCoordinates( shape->padfX[j], shape->padfY[j], 0, GeoDataCoordinates::Degree ) ) ); } placemark->setGeometry( geom ); mDebug() << "multipoint " << placemark->name(); break; } case SHPT_ARC: { if ( shape->nParts != 1 ) { GeoDataMultiGeometry *geom = new GeoDataMultiGeometry; for( int j=0; jnParts; ++j ) { GeoDataLineString *line = new GeoDataLineString; int itEnd = (j + 1 < shape->nParts) ? shape->panPartStart[j+1] : shape->nVertices; for( int k=shape->panPartStart[j]; kappend( GeoDataCoordinates( shape->padfX[k], shape->padfY[k], 0, GeoDataCoordinates::Degree ) ); } geom->append( line ); } placemark->setGeometry( geom ); mDebug() << "arc " << placemark->name() << " " << shape->nParts; } else { GeoDataLineString *line = new GeoDataLineString; for( int j=0; jnVertices; ++j ) { line->append( GeoDataCoordinates( shape->padfX[j], shape->padfY[j], 0, GeoDataCoordinates::Degree ) ); } placemark->setGeometry( line ); mDebug() << "arc " << placemark->name() << " " << shape->nParts; } break; } case SHPT_POLYGON: { if ( shape->nParts != 1 ) { bool isRingClockwise = false; GeoDataMultiGeometry *multigeom = new GeoDataMultiGeometry; GeoDataPolygon *poly = 0; int polygonCount = 0; for( int j=0; jnParts; ++j ) { GeoDataLinearRing ring; int itStart = shape->panPartStart[j]; int itEnd = (j + 1 < shape->nParts) ? shape->panPartStart[j+1] : shape->nVertices; for( int k = itStart; kpadfX[k], shape->padfY[k], 0, GeoDataCoordinates::Degree ) ); } isRingClockwise = ring.isClockwise(); if ( j == 0 || isRingClockwise ) { poly = new GeoDataPolygon; ++polygonCount; poly->setOuterBoundary( ring ); if ( polygonCount > 1 ) { multigeom->append( poly ); } } else { poly->appendInnerBoundary( ring ); } } if ( polygonCount > 1 ) { placemark->setGeometry( multigeom ); } else { placemark->setGeometry( poly ); delete multigeom; multigeom = 0; } mDebug() << "donut " << placemark->name() << " " << shape->nParts; } else { GeoDataPolygon *poly = new GeoDataPolygon; GeoDataLinearRing ring; for( int j=0; jnVertices; ++j ) { ring.append( GeoDataCoordinates( shape->padfX[j], shape->padfY[j], 0, GeoDataCoordinates::Degree ) ); } poly->setOuterBoundary( ring ); placemark->setGeometry( poly ); mDebug() << "poly " << placemark->name() << " " << shape->nParts; } break; } } } SHPClose( handle ); DBFClose( dbfhandle ); - if ( document->size() ) { + if (!document->isEmpty()) { document->setFileName( fileName ); return document; } else { delete document; return nullptr; } } } #include "moc_ShpRunner.cpp" diff --git a/tools/vectorosm-tilecreator/vectorosm-tilecreator.cpp b/tools/vectorosm-tilecreator/vectorosm-tilecreator.cpp index 4d790ada8..751596827 100644 --- a/tools/vectorosm-tilecreator/vectorosm-tilecreator.cpp +++ b/tools/vectorosm-tilecreator/vectorosm-tilecreator.cpp @@ -1,348 +1,348 @@ // // This file is part of the Marble Virtual Globe. // // This program is free software licensed under the GNU LGPL. You can // find a copy of this license in LICENSE.txt in the top directory of // the source code. // // Copyright 2016 David Kolozsvari // Copyright 2016 Dennis Nienhüser // #include "GeoDataDocumentWriter.h" #include "MarbleModel.h" #include "ParsingRunnerManager.h" #include "GeoDataGeometry.h" #include "GeoDataPlacemark.h" #include "GeoDataPolygon.h" #include "GeoDataLatLonAltBox.h" #include "TileId.h" #include "MarbleDirs.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "VectorClipper.h" #include "NodeReducer.h" #include "WayConcatenator.h" #include "TileIterator.h" #include "TileDirectory.h" #include "MbTileWriter.h" #include "SpellChecker.h" #include using namespace Marble; GeoDataDocument* mergeDocuments(GeoDataDocument* map1, GeoDataDocument* map2) { GeoDataDocument* mergedMap = new GeoDataDocument(*map1); OsmPlacemarkData marbleLand; marbleLand.addTag("marble_land","landmass"); for (auto placemark: map2->placemarkList()) { GeoDataPlacemark* land = new GeoDataPlacemark(*placemark); if (geodata_cast(land->geometry())) { land->setOsmData(marbleLand); } mergedMap->append(land); } return mergedMap; } QString tileFileName(const QCommandLineParser &parser, int x, int y, int zoomLevel) { QString const extension = parser.value("extension"); QString const output = parser.isSet("development") ? QString("%1/maps/earth/vectorosm-dev").arg(MarbleDirs::localPath()) : parser.value("output"); QString const outputDir = QString("%1/%2/%3").arg(output).arg(zoomLevel).arg(x); QString const outputFile = QString("%1/%2.%3").arg(outputDir).arg(y).arg(extension); return outputFile; } void writeBoundaryTile(GeoDataDocument* tile, const QString ®ion, const QCommandLineParser &parser, int x, int y, int zoomLevel) { QString const extension = parser.value("extension"); QString const outputDir = QString("%1/boundaries/%2/%3/%4").arg(parser.value("cache-directory")).arg(region).arg(zoomLevel).arg(x); QString const outputFile = QString("%1/%2.%3").arg(outputDir).arg(y).arg(extension); QDir().mkpath(outputDir); GeoDataDocumentWriter::write(outputFile, *tile); } QSharedPointer mergeBoundaryTiles(const QSharedPointer &background, ParsingRunnerManager &manager, const QCommandLineParser &parser, int x, int y, int zoomLevel) { GeoDataDocument* mergedMap = new GeoDataDocument; OsmPlacemarkData marbleLand; marbleLand.addTag("marble_land","landmass"); for (auto placemark: background->placemarkList()) { GeoDataPlacemark* land = new GeoDataPlacemark(*placemark); if (geodata_cast(land->geometry())) { land->setOsmData(marbleLand); } mergedMap->append(land); } QString const extension = parser.value("extension"); QString const boundaryDir = QString("%1/boundaries").arg(parser.value("cache-directory")); for(auto const &dir: QDir(boundaryDir).entryList(QDir::Dirs | QDir::NoDotAndDotDot)) { QString const file = QString("%1/%2/%3/%4/%5.%6").arg(boundaryDir).arg(dir).arg(zoomLevel).arg(x).arg(y).arg(extension); if (QFileInfo(file).exists()) { auto tile = TileDirectory::open(file, manager); if (tile) { for (auto placemark: tile->placemarkList()) { mergedMap->append(placemark->clone()); } } } } return QSharedPointer(mergedMap); } bool writeTile(GeoDataDocument* tile, const QString &outputFile) { QDir().mkpath(QFileInfo(outputFile).path()); if (!GeoDataDocumentWriter::write(outputFile, *tile)) { qWarning() << "Could not write the file " << outputFile; return false; } return true; } int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); QCoreApplication::setApplicationName("marble-vectorosm-tilecreator"); QCoreApplication::setApplicationVersion("0.1"); QCommandLineParser parser; parser.setApplicationDescription("A tool for Marble, which is used to reduce the details of osm maps."); parser.addHelpOption(); parser.addVersionOption(); parser.addPositionalArgument("input", "The input .osm or .shp file."); parser.addOptions({ {{"t", "osmconvert"}, "Tile data using osmconvert."}, {"conflict-resolution", "How to deal with existing tiles: overwrite, skip or merge", "mode", "overwrite"}, {{"c", "cache-directory"}, "Directory for temporary data.", "cache", "cache"}, {{"m", "mbtile"}, "Store tiles at level 15 onwards in a mbtile database.", "mbtile"}, {{"s", "spellcheck"}, "Use this geonames.org cities file for spell-checking city names", "spellcheck"}, {"verbose", "Increase amount of shell output information"}, {"boundaries", "Write boundary tiles (implied by conflict-resolution=merge)"}, {{"d", "development"}, "Use local development vector osm map theme as output storage"}, {{"z", "zoom-level"}, "Zoom level according to which OSM information has to be processed.", "levels", "11,13,15,17"}, {{"o", "output"}, "Output file or directory", "output", QString("%1/maps/earth/vectorosm").arg(MarbleDirs::localPath())}, {{"e", "extension"}, "Output file type: o5m (default), osm or kml", "file extension", "o5m"} }); // Process the actual command line arguments given by the user parser.process(app); const QStringList args = parser.positionalArguments(); if (args.isEmpty()) { parser.showHelp(); return 0; } // input is args.at(0), output is args.at(1) QString const extension = parser.value("extension"); QString inputFileName = args.at(0); auto const levels = parser.value("zoom-level").split(','); QVector zoomLevels; int maxZoomLevel = 0; for(auto const &level: levels) { int const zoomLevel = level.toInt(); maxZoomLevel = qMax(zoomLevel, maxZoomLevel); zoomLevels << zoomLevel; } if (zoomLevels.isEmpty()) { parser.showHelp(1); return 1; } bool const overwriteTiles = parser.value("conflict-resolution") == "overwrite"; bool const mergeTiles = parser.value("conflict-resolution") == "merge"; bool const writeBoundaries = mergeTiles || parser.isSet("boundaries"); QSharedPointer mbtileWriter; if (parser.isSet("mbtile")) { QString const mbtile = parser.value("mbtile"); mbtileWriter = QSharedPointer(new MbTileWriter(mbtile, extension)); mbtileWriter->setReportProgress(false); mbtileWriter->setCommitInterval(500); } MarbleModel model; ParsingRunnerManager manager(model.pluginManager()); QString const cacheDirectory = parser.value("cache-directory"); QDir().mkpath(cacheDirectory); if (!QFileInfo(cacheDirectory).isWritable()) { qWarning() << "Cannot write to cache directory" << cacheDirectory; parser.showHelp(1); } if (*zoomLevels.cbegin() <= 9) { auto map = TileDirectory::open(inputFileName, manager); VectorClipper processor(map.data(), maxZoomLevel); GeoDataLatLonBox world(85.0, -85.0, 180.0, -180.0, GeoDataCoordinates::Degree); if (parser.isSet("spellcheck")) { SpellChecker spellChecker(parser.value("spellcheck")); spellChecker.setVerbose(parser.isSet("verbose")); spellChecker.correctPlaceLabels(map.data()->placemarkList()); } for(auto zoomLevel: zoomLevels) { TileIterator iter(world, zoomLevel); qint64 count = 0; qint64 const total = iter.total(); for(auto const &tileId: iter) { ++count; QString const filename = tileFileName(parser, tileId.x(), tileId.y(), zoomLevel); if (!overwriteTiles && QFileInfo(filename).exists()) { continue; } GeoDataDocument* tile = processor.clipTo(zoomLevel, tileId.x(), tileId.y()); - if (tile->size() > 0) { + if (!tile->isEmpty()) { NodeReducer nodeReducer(tile, TileId(0, zoomLevel, tileId.x(), tileId.y())); if (!writeTile(tile, filename)) { return 4; } TileDirectory::printProgress(count / double(total)); std::cout << " Tile " << count << "/" << total << " (" << tile->name().toStdString() << ") done."; double const reduction = nodeReducer.removedNodes() / qMax(1.0, double(nodeReducer.remainingNodes() + nodeReducer.removedNodes())); std::cout << " Node reduction: " << qRound(reduction * 100.0) << "%"; } else { TileDirectory::printProgress(count / double(total)); std::cout << " Skipping empty tile " << count << "/" << total << " (" << tile->name().toStdString() << ")."; } std::cout << std::string(20, ' ') << '\r'; std::cout.flush(); delete tile; } } } else { QString const region = QFileInfo(inputFileName).fileName(); QString const regionDir = QString("%1/%2").arg(cacheDirectory).arg(QFileInfo(inputFileName).baseName()); TileDirectory mapTiles(TileDirectory::OpenStreetMap, regionDir, manager, extension, maxZoomLevel); mapTiles.setInputFile(inputFileName); mapTiles.createTiles(); auto const boundingBox = mapTiles.boundingBox(); TileDirectory loader(TileDirectory::Landmass, cacheDirectory, manager, extension, maxZoomLevel); loader.setBoundingBox(boundingBox); loader.createTiles(); typedef QMap > Tiles; Tiles tiles; qint64 total = 0; QSet boundaryTiles; for(auto zoomLevel: zoomLevels) { TileIterator iter(mapTiles.boundingBox(), zoomLevel); total += iter.total(); for(auto const &tileId: iter) { auto const tile = TileId(0, zoomLevel, tileId.x(), tileId.y()); int const innerNodes = mapTiles.innerNodes(tile); if (innerNodes > 0) { auto const mapTile = mapTiles.tileFor(zoomLevel, tileId.x(), tileId.y()); auto const name = QString("%1/%2/%3").arg(mapTile.zoomLevel()).arg(mapTile.x()).arg(mapTile.y()); tiles[name] << tile; if (innerNodes < 4) { boundaryTiles << name; } } else { --total; } } } qint64 count = 0; for (auto iter = tiles.begin(), end = tiles.end(); iter != end; ++iter) { for(auto const &tileId: iter.value()) { ++count; int const zoomLevel = tileId.zoomLevel(); QString const filename = tileFileName(parser, tileId.x(), tileId.y(), zoomLevel); if (!overwriteTiles) { if (zoomLevel > 13 && mbtileWriter && mbtileWriter->hasTile(tileId.x(), tileId.y(), zoomLevel)) { continue; } else if (QFileInfo(filename).exists()) { continue; } } typedef QSharedPointer GeoDocPtr; GeoDocPtr tile2 = GeoDocPtr(loader.clip(zoomLevel, tileId.x(), tileId.y())); - if (tile2->size() > 0) { + if (!tile2->isEmpty()) { GeoDocPtr tile1 = GeoDocPtr(mapTiles.clip(zoomLevel, tileId.x(), tileId.y())); TagsFilter::removeAnnotationTags(tile1.data()); int originalWays = 0; int mergedWays = 0; if (zoomLevel < 17) { WayConcatenator concatenator(tile1.data()); originalWays = concatenator.originalWays(); mergedWays = concatenator.mergedWays(); } NodeReducer nodeReducer(tile1.data(), tileId); - if (tile1->size() > 0 && tile2->size() > 0) { + if (!tile1->isEmpty() && !tile2->isEmpty()) { GeoDocPtr combined = GeoDocPtr(mergeDocuments(tile1.data(), tile2.data())); if (writeBoundaries && boundaryTiles.contains(iter.key())) { writeBoundaryTile(tile1.data(), region, parser, tileId.x(), tileId.y(), zoomLevel); if (mergeTiles) { combined = mergeBoundaryTiles(tile2, manager, parser, tileId.x(), tileId.y(), zoomLevel); } } if (zoomLevel > 13 && mbtileWriter) { QBuffer buffer; buffer.open(QBuffer::ReadWrite); if (GeoDataDocumentWriter::write(&buffer, *combined, extension)) { buffer.seek(0); mbtileWriter->addTile(&buffer, tileId.x(), tileId.y(), zoomLevel); } else { qWarning() << "Could not write the tile " << combined->name(); } } else { if (!writeTile(combined.data(), filename)) { return 4; } } TileDirectory::printProgress(count / double(total)); std::cout << " Tile " << count << "/" << total << " ("; std::cout << combined->name().toStdString() << ")."; double const reduction = nodeReducer.removedNodes() / qMax(1.0, double(nodeReducer.remainingNodes() + nodeReducer.removedNodes())); std::cout << " Node reduction: " << qRound(reduction * 100.0) << "%"; if (originalWays > 0) { std::cout << " , " << originalWays << " ways merged to " << mergedWays; } } else { TileDirectory::printProgress(count / double(total)); std::cout << " Skipping empty tile " << count << "/" << total << " (" << tile1->name().toStdString() << ")."; } } else { TileDirectory::printProgress(count / double(total)); std::cout << " Skipping sea tile " << count << "/" << total << " (" << tile2->name().toStdString() << ")."; } std::cout << std::string(20, ' ') << '\r'; std::cout.flush(); } } TileDirectory::printProgress(1.0); std::cout << " Vector OSM tiles complete." << std::string(30, ' ') << std::endl; } return 0; }