Warning, file /education/marble/src/plugins/runner/pn2/Pn2Runner.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 // SPDX-License-Identifier: LGPL-2.1-or-later 0002 // 0003 // SPDX-FileCopyrightText: 2012 Torsten Rahn <rahn@kde.org> 0004 // SPDX-FileCopyrightText: 2012 Cezar Mocan <mocancezar@gmail.com> 0005 // SPDX-FileCopyrightText: 2014 Abhinav Gangwar <abhgang@gmail.com> 0006 // 0007 // For the Natural Earth Layer providing the Default data set at 0.5 arcminute resolution should be enough. 0008 // This fileformat allows for even better packed data than the PNT format. For detailed polygons at arcminute 0009 // scale on average it should use only 33% of the amount used by PNT. 0010 // 0011 // Description of the file format 0012 // 0013 // In the fileformat initially a file header is provided that provides the file format version and the number 0014 // of polygons stored inside the file. A Polygon starts with the Polygon Header which provides the feature id 0015 // and the number of so called "absolute nodes" that are about to follow. Absolute nodes always contain 0016 // absolute geodetic coordinates. The Polygon Header also provides a flag that allows to specify whether the 0017 // polygon is supposed to represent a line string ("0") or a linear ring ("1"). Each absolute node can be followed 0018 // by relative nodes: These relative nodes are always nodes that follow in correct order inside the polygon after 0019 // "their" absolute node. Each absolute node specifies the number of relative nodes which contain relative 0020 // coordinates in reference to their absolute node. So an absolute node provides the absolute reference for 0021 // relative nodes across a theoretical area of 2x2 squaredegree-area (which in practice frequently might rather 0022 // amount to 1x1 square degrees). 0023 // 0024 // So much of the compression works by just referencing lat/lon diffs to special "absolute nodes". Hence the 0025 // compression will especially work well for polygons with many nodes with a high node density. 0026 // 0027 // The parser has to convert these relative coordinates to absolute coordinates. 0028 // 0029 0030 #include "Pn2Runner.h" 0031 0032 #include "GeoDataDocument.h" 0033 #include "GeoDataPlacemark.h" 0034 #include "GeoDataStyle.h" 0035 #include "GeoDataPolyStyle.h" 0036 #include "GeoDataLinearRing.h" 0037 #include "GeoDataPolygon.h" 0038 #include "GeoDataMultiGeometry.h" 0039 #include "MarbleDebug.h" 0040 0041 #include <QFile> 0042 #include <QFileInfo> 0043 0044 namespace Marble 0045 { 0046 // Polygon header flags, representing the type of polygon 0047 enum polygonFlagType { LINESTRING = 0, LINEARRING = 1, OUTERBOUNDARY = 2, INNERBOUNDARY = 3, MULTIGEOMETRY = 4 }; 0048 0049 0050 Pn2Runner::Pn2Runner(QObject *parent) : 0051 ParsingRunner(parent) 0052 { 0053 } 0054 0055 Pn2Runner::~Pn2Runner() 0056 { 0057 } 0058 0059 bool Pn2Runner::errorCheckLat( qint16 lat ) 0060 { 0061 return !(lat >= -10800 && lat <= +10800); 0062 } 0063 0064 bool Pn2Runner::errorCheckLon( qint16 lon ) 0065 { 0066 return !(lon >= -21600 && lon <= +21600); 0067 } 0068 0069 bool Pn2Runner::importPolygon( QDataStream &stream, GeoDataLineString* linestring, quint32 nrAbsoluteNodes ) 0070 { 0071 qint16 lat, lon, nrRelativeNodes; 0072 qint8 relativeLat, relativeLon; 0073 bool error = false; 0074 0075 0076 for ( quint32 absoluteNode = 1; absoluteNode <= nrAbsoluteNodes; absoluteNode++ ) { 0077 stream >> lat >> lon >> nrRelativeNodes; 0078 0079 error = error | errorCheckLat( lat ) | errorCheckLon( lon ); 0080 0081 qreal degLat = ( 1.0 * lat / 120.0 ); 0082 qreal degLon = ( 1.0 * lon / 120.0 ); 0083 0084 GeoDataCoordinates coord( degLon / 180 * M_PI, degLat / 180 * M_PI ); 0085 linestring->append( coord ); 0086 0087 for ( qint16 relativeNode = 1; relativeNode <= nrRelativeNodes; ++relativeNode ) { 0088 stream >> relativeLat >> relativeLon; 0089 0090 qint16 currLat = relativeLat + lat; 0091 qint16 currLon = relativeLon + lon; 0092 0093 0094 error = error | errorCheckLat( currLat ) | errorCheckLon( currLon ); 0095 0096 qreal currDegLat = ( 1.0 * currLat / 120.0 ); 0097 qreal currDegLon = ( 1.0 * currLon / 120.0 ); 0098 0099 0100 GeoDataCoordinates currCoord( currDegLon / 180 * M_PI, currDegLat / 180 * M_PI ); 0101 linestring->append( currCoord ); 0102 } 0103 } 0104 0105 *linestring = linestring->optimized(); 0106 0107 return error; 0108 } 0109 0110 GeoDataDocument *Pn2Runner::parseFile(const QString &fileName, DocumentRole role, QString &error) 0111 { 0112 QFileInfo fileinfo( fileName ); 0113 if (fileinfo.suffix().compare(QLatin1String("pn2"), Qt::CaseInsensitive) != 0) { 0114 error = QStringLiteral("File %1 does not have a pn2 suffix").arg(fileName); 0115 mDebug() << error; 0116 return nullptr; 0117 } 0118 0119 QFile file( fileName ); 0120 if ( !file.exists() ) { 0121 error = QStringLiteral("File %1 does not exist").arg(fileName); 0122 mDebug() << error; 0123 return nullptr; 0124 } 0125 0126 file.open( QIODevice::ReadOnly ); 0127 m_stream.setDevice( &file ); // read the data serialized from the file 0128 0129 m_stream >> m_fileHeaderVersion >> m_fileHeaderPolygons >> m_isMapColorField; 0130 0131 switch( m_fileHeaderVersion ) { 0132 case 1: return parseForVersion1( fileName, role ); 0133 case 2: return parseForVersion2( fileName, role ); 0134 default: qDebug() << "File can't be parsed. We don't have parser for file header version:" << m_fileHeaderVersion; 0135 break; 0136 } 0137 0138 return nullptr; 0139 } 0140 0141 GeoDataDocument* Pn2Runner::parseForVersion1(const QString& fileName, DocumentRole role) 0142 { 0143 GeoDataDocument *document = new GeoDataDocument(); 0144 document->setDocumentRole( role ); 0145 0146 bool error = false; 0147 0148 quint32 ID, nrAbsoluteNodes; 0149 quint8 flag, prevFlag = -1; 0150 0151 GeoDataStyle::Ptr style; 0152 GeoDataPolygon *polygon = new GeoDataPolygon; 0153 0154 for ( quint32 currentPoly = 1; ( currentPoly <= m_fileHeaderPolygons ) && ( !error ) && ( !m_stream.atEnd() ); currentPoly++ ) { 0155 0156 m_stream >> ID >> nrAbsoluteNodes >> flag; 0157 0158 if ( flag != INNERBOUNDARY && ( prevFlag == INNERBOUNDARY || prevFlag == OUTERBOUNDARY ) ) { 0159 0160 GeoDataPlacemark *placemark = new GeoDataPlacemark; 0161 placemark->setGeometry( polygon ); 0162 if ( m_isMapColorField ) { 0163 if ( style ) { 0164 placemark->setStyle( style ); 0165 } 0166 } 0167 document->append( placemark ); 0168 } 0169 0170 if ( flag == LINESTRING ) { 0171 GeoDataLineString *linestring = new GeoDataLineString; 0172 error = error | importPolygon( m_stream, linestring, nrAbsoluteNodes ); 0173 0174 GeoDataPlacemark *placemark = new GeoDataPlacemark; 0175 placemark->setGeometry( linestring ); 0176 document->append( placemark ); 0177 } 0178 0179 if ( ( flag == LINEARRING ) || ( flag == OUTERBOUNDARY ) || ( flag == INNERBOUNDARY ) ) { 0180 if ( flag == OUTERBOUNDARY && m_isMapColorField ) { 0181 quint8 colorIndex; 0182 m_stream >> colorIndex; 0183 style = GeoDataStyle::Ptr(new GeoDataStyle); 0184 GeoDataPolyStyle polyStyle; 0185 polyStyle.setColorIndex( colorIndex ); 0186 style->setPolyStyle( polyStyle ); 0187 } 0188 0189 GeoDataLinearRing* linearring = new GeoDataLinearRing; 0190 error = error | importPolygon( m_stream, linearring, nrAbsoluteNodes ); 0191 0192 if ( flag == LINEARRING ) { 0193 GeoDataPlacemark *placemark = new GeoDataPlacemark; 0194 placemark->setGeometry( linearring ); 0195 document->append( placemark ); 0196 } 0197 0198 if ( flag == OUTERBOUNDARY ) { 0199 polygon = new GeoDataPolygon; 0200 polygon->setOuterBoundary( *linearring ); 0201 } 0202 0203 if ( flag == INNERBOUNDARY ) { 0204 polygon->appendInnerBoundary( *linearring ); 0205 } 0206 } 0207 0208 if ( flag == MULTIGEOMETRY ) { 0209 // not implemented yet, for now elements inside a multigeometry are separated as individual geometries 0210 } 0211 0212 prevFlag = flag; 0213 } 0214 0215 if ( prevFlag == INNERBOUNDARY || prevFlag == OUTERBOUNDARY ) { 0216 GeoDataPlacemark *placemark = new GeoDataPlacemark; 0217 if ( m_isMapColorField ) { 0218 if ( style ) { 0219 placemark->setStyle( style ); 0220 } 0221 } 0222 placemark->setGeometry( polygon ); 0223 document->append( placemark ); 0224 } 0225 0226 if ( error ) { 0227 delete document; 0228 document = nullptr; 0229 return nullptr; 0230 } 0231 document->setFileName( fileName ); 0232 return document; 0233 } 0234 0235 GeoDataDocument* Pn2Runner::parseForVersion2( const QString &fileName, DocumentRole role ) 0236 { 0237 GeoDataDocument *document = new GeoDataDocument(); 0238 document->setDocumentRole( role ); 0239 0240 bool error = false; 0241 0242 quint32 nrAbsoluteNodes; 0243 quint32 placemarkCurrentID = 1; 0244 quint32 placemarkPrevID = 0; 0245 quint8 flag, prevFlag = -1; 0246 0247 GeoDataPolygon *polygon = new GeoDataPolygon; 0248 GeoDataStyle::Ptr style; 0249 GeoDataPlacemark *placemark =nullptr; // new GeoDataPlacemark; 0250 0251 quint32 currentPoly; 0252 for ( currentPoly = 1; ( currentPoly <= m_fileHeaderPolygons ) && ( !error ) && ( !m_stream.atEnd() ); currentPoly++ ) { 0253 m_stream >> flag >> placemarkCurrentID; 0254 0255 if ( flag == MULTIGEOMETRY && ( prevFlag == INNERBOUNDARY || prevFlag == OUTERBOUNDARY ) ) { 0256 if ( placemark ) { 0257 placemark->setGeometry( polygon ); 0258 } 0259 } 0260 0261 if ( flag != MULTIGEOMETRY && flag != INNERBOUNDARY && ( prevFlag == INNERBOUNDARY || prevFlag == OUTERBOUNDARY ) ) { 0262 if ( placemark ) { 0263 placemark->setGeometry( polygon ); 0264 } 0265 } 0266 /** 0267 * If the parsed placemark id @p placemarkCurrentID is different 0268 * from the id of previous placemark @p placemarkPrevID, it means 0269 * we have encountered a new placemark. So, prepare a style @p style 0270 * if file has color indices 0271 */ 0272 if ( placemarkCurrentID != placemarkPrevID ) { 0273 placemark = new GeoDataPlacemark; 0274 0275 // Handle the color index 0276 if( m_isMapColorField ) { 0277 quint8 colorIndex; 0278 m_stream >> colorIndex; 0279 style = GeoDataStyle::Ptr(new GeoDataStyle); 0280 GeoDataPolyStyle polyStyle; 0281 polyStyle.setColorIndex( colorIndex ); 0282 polyStyle.setFill( true ); 0283 style->setPolyStyle( polyStyle ); 0284 placemark->setStyle( style ); 0285 } 0286 0287 document->append( placemark ); 0288 } 0289 0290 placemarkPrevID = placemarkCurrentID; 0291 0292 if ( flag != MULTIGEOMETRY ) { 0293 m_stream >> nrAbsoluteNodes; 0294 0295 if ( flag == LINESTRING ) { 0296 GeoDataLineString *linestring = new GeoDataLineString; 0297 error = error | importPolygon( m_stream, linestring, nrAbsoluteNodes ); 0298 if ( placemark ) { 0299 placemark->setGeometry( linestring ); 0300 } 0301 } 0302 0303 if ( ( flag == LINEARRING ) || ( flag == OUTERBOUNDARY ) || ( flag == INNERBOUNDARY ) ) { 0304 GeoDataLinearRing* linearring = new GeoDataLinearRing; 0305 error = error || importPolygon( m_stream, linearring, nrAbsoluteNodes ); 0306 0307 if ( flag == LINEARRING ) { 0308 if ( placemark ) { 0309 placemark->setGeometry( linearring ); 0310 } 0311 } else { 0312 if ( flag == OUTERBOUNDARY ) { 0313 polygon = new GeoDataPolygon; 0314 polygon->setOuterBoundary( *linearring ); 0315 } 0316 0317 if ( flag == INNERBOUNDARY ) { 0318 polygon->appendInnerBoundary( *linearring ); 0319 } 0320 0321 delete linearring; 0322 } 0323 } 0324 prevFlag = flag; 0325 } 0326 0327 else { 0328 quint32 placemarkCurrentIDInMulti; 0329 quint8 flagInMulti; 0330 quint8 prevFlagInMulti = -1; 0331 quint8 multiSize = 0; 0332 0333 m_stream >> multiSize; 0334 0335 GeoDataMultiGeometry *multigeom = new GeoDataMultiGeometry; 0336 0337 /** 0338 * Read @p multiSize GeoDataGeometry objects 0339 */ 0340 for ( int iter = 0; iter < multiSize; ++iter ) { 0341 m_stream >> flagInMulti >> placemarkCurrentIDInMulti >> nrAbsoluteNodes; 0342 if ( flagInMulti != INNERBOUNDARY && ( prevFlagInMulti == INNERBOUNDARY || prevFlagInMulti == OUTERBOUNDARY ) ) { 0343 multigeom->append( polygon ); 0344 } 0345 0346 if ( flagInMulti == LINESTRING ) { 0347 GeoDataLineString *linestring = new GeoDataLineString; 0348 error = error || importPolygon( m_stream, linestring, nrAbsoluteNodes ); 0349 multigeom->append( linestring ); 0350 } 0351 0352 if ( ( flagInMulti == LINEARRING ) || ( flagInMulti == OUTERBOUNDARY ) || ( flagInMulti == INNERBOUNDARY ) ) { 0353 GeoDataLinearRing* linearring = new GeoDataLinearRing; 0354 error = error | importPolygon( m_stream, linearring, nrAbsoluteNodes ); 0355 0356 if ( flagInMulti == LINEARRING ) { 0357 multigeom->append( linearring ); 0358 } else { 0359 if ( flagInMulti == OUTERBOUNDARY ) { 0360 polygon = new GeoDataPolygon; 0361 polygon->setOuterBoundary( *linearring ); 0362 } 0363 0364 if ( flagInMulti == INNERBOUNDARY ) { 0365 polygon->appendInnerBoundary( *linearring ); 0366 } 0367 0368 delete linearring; 0369 } 0370 } 0371 prevFlagInMulti = flagInMulti; 0372 } 0373 0374 if ( prevFlagInMulti == INNERBOUNDARY || prevFlagInMulti == OUTERBOUNDARY ) { 0375 multigeom->append( polygon ); 0376 } 0377 if ( placemark ) { 0378 placemark->setGeometry( multigeom ); 0379 } 0380 prevFlag = MULTIGEOMETRY; 0381 } 0382 } 0383 0384 if ( (prevFlag == INNERBOUNDARY || prevFlag == OUTERBOUNDARY) && prevFlag != MULTIGEOMETRY ) { 0385 placemark->setGeometry( polygon ); 0386 } 0387 0388 if ( error ) { 0389 delete document; 0390 document = nullptr; 0391 return nullptr; 0392 } 0393 document->setFileName( fileName ); 0394 return document; 0395 } 0396 0397 } 0398 0399 0400 #include "moc_Pn2Runner.cpp"