File indexing completed on 2025-01-05 03:59:30

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