File indexing completed on 2024-05-05 03:51:05

0001 // SPDX-License-Identifier: LGPL-2.1-or-later
0002 //
0003 // SPDX-FileCopyrightText: 2011 Thibaut Gridel <tgridel@free.fr>
0004 // SPDX-FileCopyrightText: 2012 Bernhard Beschow <bbeschow@cs.tu-berlin.de>
0005 
0006 #include "PntRunner.h"
0007 
0008 #include "GeoDataDocument.h"
0009 #include "GeoDataLineString.h"
0010 #include "GeoDataLinearRing.h"
0011 #include "GeoDataPlacemark.h"
0012 #include "MarbleDebug.h"
0013 #include "MarbleGlobal.h"
0014 
0015 #include <qmath.h>
0016 #include <QFile>
0017 #include <QFileInfo>
0018 #include <QDataStream>
0019 
0020 namespace Marble
0021 {
0022 
0023 // distance of 180deg in arcminutes
0024 const qreal INT2RAD = M_PI / 10800.0;
0025 
0026 PntRunner::PntRunner(QObject *parent) :
0027     ParsingRunner(parent)
0028 {
0029 }
0030 
0031 PntRunner::~PntRunner()
0032 {
0033 }
0034 
0035 GeoDataDocument *PntRunner::parseFile(const QString &fileName, DocumentRole role, QString &errorString)
0036 {
0037     QFileInfo fileinfo( fileName );
0038     if (fileinfo.suffix().compare(QLatin1String("pnt"), Qt::CaseInsensitive) != 0) {
0039         errorString = QStringLiteral("File %1 does not have a pnt suffix").arg(fileName);
0040         mDebug() << errorString;
0041         return nullptr;
0042     }
0043 
0044     QFile  file( fileName );
0045     if ( !file.exists() ) {
0046         errorString = QStringLiteral("File %1 does not exist").arg(fileName);
0047         mDebug() << errorString;
0048         return nullptr;
0049     }
0050 
0051     file.open( QIODevice::ReadOnly );
0052     QDataStream stream( &file );  // read the data serialized from the file
0053     stream.setByteOrder( QDataStream::LittleEndian );
0054 
0055     GeoDataDocument *document = new GeoDataDocument();
0056     document->setDocumentRole( role );
0057     GeoDataPlacemark  *placemark = nullptr;
0058 
0059     int count = 0;
0060     bool error = false;
0061     while( !stream.atEnd() || error ){
0062         short  header = -1;
0063         short  iLat = -5400 - 1;
0064         short  iLon = -10800 - 1;
0065 
0066         stream >> header >> iLat >> iLon;
0067 
0068         // make sure iLat is within valid range
0069         if ( !( -5400 <= iLat && iLat <= 5400 ) ) {
0070             mDebug() << "invalid iLat =" << iLat << "(" << ( iLat * INT2RAD ) * RAD2DEG << ") in dataset" << count << "of file" << fileName;
0071             error = true;
0072         }
0073 
0074         // make sure iLon is within valid range
0075         if ( !( -10800 <= iLon && iLon <= 10800 ) ) {
0076             mDebug() << "invalid iLon =" << iLon << "(" << ( iLon * INT2RAD ) * RAD2DEG << ") in dataset" << count << "of file" << fileName;
0077             error = true;
0078         }
0079 
0080         if (header >= 1000 && !document->isEmpty()) {
0081             GeoDataLineString *const polyline = static_cast<GeoDataLineString*>( placemark->geometry() );
0082             if ( polyline->size() == 1 ) {
0083                 mDebug() << fileName << "contains single-point polygon at" << count << ". Aborting.";
0084                 error = true;
0085                 break;
0086             }
0087         }
0088 
0089         if ( header < 1 ) {
0090             /* invalid header */
0091             mDebug() << "invalid header:" << header << "in" << fileName << "at" << count;
0092             error = true;
0093             break;
0094         }
0095         else if ( header <= 5 ) {
0096             /* header represents level of detail */
0097             /* nothing to do */
0098         }
0099         else if ( header < 1000 ) {
0100             /* invalid header */
0101             mDebug() << "invalid header:" << header << "in" << fileName << "at" << count;
0102             error = true;
0103             break;
0104         }
0105         else if ( header < 2000 ) {
0106             /* header represents start of coastline */
0107             placemark = new GeoDataPlacemark;
0108             document->append( placemark );
0109             placemark->setGeometry( new GeoDataLinearRing );
0110         }
0111         else if ( header < 4000 ) {
0112             /* header represents start of country border */
0113             placemark = new GeoDataPlacemark;
0114             document->append( placemark );
0115             placemark->setGeometry( new GeoDataLineString );
0116         }
0117         else if ( header < 5000 ) {
0118             /* header represents start of internal political border */
0119             placemark = new GeoDataPlacemark;
0120             document->append( placemark );
0121             placemark->setGeometry( new GeoDataLineString );
0122         }
0123         else if ( header < 6000 ) {
0124             /* header represents start of island */
0125             placemark = new GeoDataPlacemark;
0126             document->append( placemark );
0127             placemark->setGeometry( new GeoDataLinearRing );
0128         }
0129         else if ( header < 7000 ) {
0130             /* header represents start of lake */
0131             placemark = new GeoDataPlacemark;
0132             document->append( placemark );
0133             placemark->setGeometry( new GeoDataLinearRing );
0134         }
0135         else if ( header < 8000 ) {
0136             /* header represents start of river */
0137             placemark = new GeoDataPlacemark;
0138             document->append( placemark );
0139             placemark->setGeometry( new GeoDataLineString );
0140         }
0141         else if ( header < 9000 ) {
0142             /* custom header represents start of glaciers, lakes or islands */
0143             placemark = new GeoDataPlacemark;
0144             document->append( placemark );
0145             placemark->setGeometry( new GeoDataLinearRing );
0146         }
0147         else if ( header < 10000 ) {
0148             /* custom header represents start of political borders */
0149             placemark = new GeoDataPlacemark;
0150             document->append( placemark );
0151             placemark->setGeometry( new GeoDataLineString );
0152         }
0153         else if ( header < 14000 ) {
0154             /* invalid header */
0155             mDebug() << "invalid header:" << header << "in" << fileName << "at" << count;
0156             error = true;
0157             break;
0158         }
0159         else if ( header < 15000 ) {
0160             /* custom header represents start of political borders */
0161             placemark = new GeoDataPlacemark;
0162             document->append( placemark );
0163             placemark->setGeometry( new GeoDataLineString );
0164         }
0165         else if ( header < 19000 ) {
0166             /* invalid header */
0167             mDebug() << "invalid header:" << header << "in" << fileName << "at" << count;
0168             error = true;
0169             break;
0170         }
0171         else if ( header < 20000 ) {
0172             /* custom header represents start of dateline */
0173             placemark = new GeoDataPlacemark;
0174             document->append( placemark );
0175             placemark->setGeometry( new GeoDataLineString );
0176         }
0177         else {
0178             /* invalid header */
0179             mDebug() << "invalid header:" << header << "in" << fileName << "at" << count;
0180             error = true;
0181             break;
0182         }
0183 
0184         GeoDataLineString *polyline = static_cast<GeoDataLineString*>( placemark->geometry() );
0185 
0186         // Transforming Range of Coordinates to iLat [0,ARCMINUTE] , iLon [0,2 * ARCMINUTE]
0187         polyline->append( GeoDataCoordinates( (qreal)(iLon) * INT2RAD, (qreal)(iLat) * INT2RAD,
0188                                               0.0, GeoDataCoordinates::Radian,
0189                                               5 - qMin( 5, (int)header  ) ) ); // if 1 <= header <= 5, header contains level of detail
0190                                                                            // else pick most sparse level of detail, which equals 0
0191 
0192         ++count;
0193     }
0194 
0195     file.close();
0196     if (document->isEmpty() || error) {
0197         delete document;
0198         document = nullptr;
0199         return nullptr;
0200     }
0201     document->setFileName( fileName );
0202     return document;
0203 }
0204 
0205 }
0206 
0207 #include "moc_PntRunner.cpp"