File indexing completed on 2024-07-14 14:25:06

0001 // SPDX-License-Identifier: LGPL-2.1-or-later
0002 //
0003 // SPDX-FileCopyrightText: 2010 Dennis Nienhüser <nienhueser@kde.org>
0004 //
0005 
0006 #include "MonavMap.h"
0007 #include "MarbleDebug.h"
0008 
0009 #include "GeoDataParser.h"
0010 #include "GeoDataMultiGeometry.h"
0011 #include "GeoDataPlacemark.h"
0012 #include "GeoDataDocument.h"
0013 #include "GeoDataExtendedData.h"
0014 #include "GeoDataData.h"
0015 #include "GeoDataLatLonAltBox.h"
0016 
0017 namespace Marble
0018 {
0019 
0020 void MonavMap::setDirectory( const QDir &dir )
0021 {
0022     m_directory = dir;
0023     QFileInfo boundingBox( dir, "marble.kml" );
0024     if ( boundingBox.exists() ) {
0025         parseBoundingBox( boundingBox );
0026     } else {
0027         mDebug() << "No monav bounding box given for " << boundingBox.absoluteFilePath();
0028     }
0029 }
0030 
0031 void MonavMap::parseBoundingBox( const QFileInfo &file )
0032 {
0033     GeoDataLineString points;
0034     bool tooLarge = false;
0035     QFile input( file.absoluteFilePath() );
0036     if ( input.open( QFile::ReadOnly ) ) {
0037         GeoDataParser parser( GeoData_KML );
0038         if ( !parser.read( &input ) ) {
0039             mDebug() << "Could not parse file: " << parser.errorString();
0040             return;
0041         }
0042 
0043         GeoDocument *doc = parser.releaseDocument();
0044         input.close();
0045         GeoDataDocument *document = dynamic_cast<GeoDataDocument*>( doc );
0046         QVector<GeoDataPlacemark*> placemarks = document->placemarkList();
0047         if ( placemarks.size() == 1 ) {
0048             GeoDataPlacemark* placemark = placemarks.first();
0049             m_name = placemark->name();
0050             m_version = placemark->extendedData().value( "version" ).value().toString();
0051             m_date = placemark->extendedData().value( "date" ).value().toString();
0052             m_transport = placemark->extendedData().value( "transport" ).value().toString();
0053             m_payload = placemark->extendedData().value( "payload" ).value().toString();
0054             const GeoDataMultiGeometry* geometry = dynamic_cast<const GeoDataMultiGeometry*>( placemark->geometry() );
0055             if ( geometry->size() > 1500 ) {
0056                 tooLarge = true;
0057             }
0058             for ( int i = 0; geometry && i < geometry->size(); ++i ) {
0059                 const GeoDataLinearRing* poly = dynamic_cast<const GeoDataLinearRing*>( geometry->child( i ) );
0060                 if ( poly ) {
0061                     for ( int j = 0; j < poly->size(); ++j ) {
0062                         points << poly->at( j );
0063                     }
0064                     m_tiles.push_back( *poly );
0065 
0066                     if ( poly->size() > 1500 ) {
0067                         tooLarge = true;
0068                     }
0069                 }
0070             }
0071         } else {
0072             mDebug() << "File " << file.absoluteFilePath() << " does not contain one placemark, but " << placemarks.size();
0073         }
0074 
0075         delete doc;
0076     }
0077     m_boundingBox = points.latLonAltBox();
0078 
0079     if ( tooLarge ) {
0080         // The bounding box polygon is rather complicated, therefore not allowing a quick check
0081         // and also occupying memory. Discard the polygon and only store the rectangular bounding
0082         // box. Only happens for non-simplified bounding box polygons.
0083         mDebug() << "Discarding too large bounding box polygon for " << file.absoluteFilePath() << ". Please check for a map update.";
0084         m_tiles.clear();
0085     }
0086 }
0087 
0088 bool MonavMap::containsPoint( const GeoDataCoordinates &point ) const
0089 {
0090     // If we do not have a bounding box at all, we err on the safe side
0091     if ( m_boundingBox.isEmpty() ) {
0092         return true;
0093     }
0094 
0095     // Quick check for performance reasons
0096     if ( !m_boundingBox.contains( point ) ) {
0097         return false;
0098     }
0099 
0100     if ( m_tiles.isEmpty() ) {
0101         return true; // Tiles discarded for performance reason
0102     }
0103 
0104     // GeoDataLinearRing does a 3D check, but we only have 2D data for
0105     // the map bounding box. Therefore the 3D info of e.g. the GPS position
0106     // must be ignored.
0107     GeoDataCoordinates flatPosition = point;
0108     flatPosition.setAltitude( 0.0 );
0109     for( const GeoDataLinearRing & box: m_tiles ) {
0110         if ( box.contains( flatPosition ) ) {
0111             return true;
0112         }
0113     }
0114 
0115     return false;
0116 }
0117 
0118 qint64 MonavMap::size() const
0119 {
0120     qint64 result = 0;
0121     for( const QFileInfo & file: files() ) {
0122         result += file.size();
0123     }
0124 
0125     return result;
0126 }
0127 
0128 QList<QFileInfo> MonavMap::files() const
0129 {
0130     QList<QFileInfo> files;
0131     QStringList fileNames = QStringList() << "config" << "edges" << "names" << "paths" << "types";
0132     for( const QString & file: fileNames ) {
0133         files << QFileInfo(m_directory, QLatin1String("Contraction Hierarchies_") + file);
0134     }
0135 
0136     fileNames = QStringList() << "config" << "grid" << "index_1" << "index_2" << "index_3";
0137     for( const QString & file: fileNames ) {
0138         files << QFileInfo(m_directory, QLatin1String("GPSGrid_") + file);
0139     }
0140 
0141     files << QFileInfo( m_directory, "plugins.ini" );
0142     QFileInfo moduleDotIni( m_directory, "Module.ini" );
0143     if ( moduleDotIni.exists() ) {
0144         files << moduleDotIni;
0145     }
0146     files << QFileInfo( m_directory, "marble.kml" );
0147     return files;
0148 }
0149 
0150 void MonavMap::remove() const
0151 {
0152     for( const QFileInfo & file: files() ) {
0153         QFile ( file.absoluteFilePath() ).remove();
0154     }
0155 }
0156 
0157 bool MonavMap::areaLessThan( const MonavMap &first, const MonavMap &second )
0158 {
0159     if ( !first.m_tiles.isEmpty() && second.m_tiles.isEmpty() ) {
0160         return true;
0161     }
0162 
0163     if ( first.m_tiles.isEmpty() && !second.m_tiles.isEmpty() ) {
0164         return false;
0165     }
0166 
0167     qreal const areaOne = first.m_boundingBox.width() * first.m_boundingBox.height();
0168     qreal const areaTwo = second.m_boundingBox.width() * second.m_boundingBox.height();
0169     return areaOne < areaTwo;
0170 }
0171 
0172 bool MonavMap::nameLessThan( const MonavMap &first, const MonavMap &second )
0173 {
0174     return first.name() < second.name();
0175 }
0176 
0177 QDir MonavMap::directory() const
0178 {
0179     return m_directory;
0180 }
0181 
0182 QString MonavMap::transport() const
0183 {
0184     return m_transport;
0185 }
0186 
0187 QString MonavMap::name() const
0188 {
0189     return m_name;
0190 }
0191 
0192 QString MonavMap::version() const
0193 {
0194     return m_version;
0195 }
0196 
0197 QString MonavMap::date() const
0198 {
0199     return m_date;
0200 }
0201 
0202 QString MonavMap::payload() const
0203 {
0204     return m_payload;
0205 }
0206 
0207 }