File indexing completed on 2024-12-01 09:46:08
0001 // SPDX-License-Identifier: LGPL-2.1-or-later 0002 // 0003 // SPDX-FileCopyrightText: 2011 Thibaut Gridel <tgridel@free.fr> 0004 0005 #include "ShpRunner.h" 0006 0007 #include "GeoDataDocument.h" 0008 #include "GeoDataPlacemark.h" 0009 #include "GeoDataPolygon.h" 0010 #include "GeoDataLinearRing.h" 0011 #include "GeoDataPoint.h" 0012 #include "GeoDataMultiGeometry.h" 0013 #include "GeoDataSchema.h" 0014 #include "GeoDataSimpleField.h" 0015 #include "GeoDataStyle.h" 0016 #include "GeoDataPolyStyle.h" 0017 #include "MarbleDebug.h" 0018 0019 #include <QFileInfo> 0020 0021 #include <shapefil.h> 0022 0023 #include <memory> 0024 0025 namespace Marble 0026 { 0027 0028 ShpRunner::ShpRunner(QObject *parent) : 0029 ParsingRunner(parent) 0030 { 0031 } 0032 0033 ShpRunner::~ShpRunner() 0034 { 0035 } 0036 0037 GeoDataDocument *ShpRunner::parseFile(const QString &fileName, DocumentRole role, QString &error) 0038 { 0039 QFileInfo fileinfo( fileName ); 0040 if (fileinfo.suffix().compare(QLatin1String("shp"), Qt::CaseInsensitive) != 0) { 0041 error = QStringLiteral("File %1 does not have a shp suffix").arg(fileName); 0042 mDebug() << error; 0043 return nullptr; 0044 } 0045 0046 SHPHandle handle = SHPOpen( fileName.toStdString().c_str(), "rb" ); 0047 if ( !handle ) { 0048 error = QStringLiteral("Failed to read %1").arg(fileName); 0049 mDebug() << error; 0050 return nullptr; 0051 } 0052 int entities; 0053 int shapeType; 0054 SHPGetInfo( handle, &entities, &shapeType, nullptr, nullptr ); 0055 mDebug() << " SHP info " << entities << " Entities " 0056 << shapeType << " Shape Type "; 0057 0058 DBFHandle dbfhandle; 0059 dbfhandle = DBFOpen( fileName.toStdString().c_str(), "rb"); 0060 int nameField = DBFGetFieldIndex( dbfhandle, "Name" ); 0061 int noteField = DBFGetFieldIndex( dbfhandle, "Note" ); 0062 int mapColorField = DBFGetFieldIndex( dbfhandle, "mapcolor13" ); 0063 0064 GeoDataDocument *document = new GeoDataDocument; 0065 document->setDocumentRole( role ); 0066 0067 if ( mapColorField != -1 ) { 0068 GeoDataSchema schema; 0069 schema.setId(QStringLiteral("default")); 0070 GeoDataSimpleField simpleField; 0071 simpleField.setName(QStringLiteral("mapcolor13")); 0072 simpleField.setType( GeoDataSimpleField::Double ); 0073 schema.addSimpleField( simpleField ); 0074 document->addSchema( schema ); 0075 } 0076 0077 for ( int i=0; i< entities; ++i ) { 0078 GeoDataPlacemark *placemark = nullptr; 0079 placemark = new GeoDataPlacemark; 0080 document->append( placemark ); 0081 0082 std::unique_ptr<SHPObject, decltype(&SHPDestroyObject)> shape(SHPReadObject( handle, i ), &SHPDestroyObject); 0083 if (nameField != -1) { 0084 const char* info = DBFReadStringAttribute( dbfhandle, i, nameField ); 0085 // TODO: defaults to utf-8 encoding, but could be also something else, optionally noted in a .cpg file 0086 placemark->setName( info ); 0087 mDebug() << "name " << placemark->name(); 0088 } 0089 if (noteField != -1) { 0090 const char* note = DBFReadStringAttribute( dbfhandle, i, noteField ); 0091 // TODO: defaults to utf-8 encoding, see comment for name 0092 placemark->setDescription( note ); 0093 mDebug() << "desc " << placemark->description(); 0094 } 0095 0096 double mapColor = DBFReadDoubleAttribute( dbfhandle, i, mapColorField ); 0097 if ( mapColor ) { 0098 GeoDataStyle::Ptr style(new GeoDataStyle); 0099 if ( mapColor >= 0 && mapColor <=255 ) { 0100 quint8 colorIndex = quint8( mapColor ); 0101 style->polyStyle().setColorIndex( colorIndex ); 0102 } 0103 else { 0104 quint8 colorIndex = 0; // mapColor is undefined in this case 0105 style->polyStyle().setColorIndex( colorIndex ); 0106 } 0107 placemark->setStyle( style ); 0108 } 0109 0110 switch ( shapeType ) { 0111 case SHPT_POINT: { 0112 GeoDataPoint *point = new GeoDataPoint( *shape->padfX, *shape->padfY, 0, GeoDataCoordinates::Degree ); 0113 placemark->setGeometry( point ); 0114 mDebug() << "point " << placemark->name(); 0115 break; 0116 } 0117 0118 case SHPT_MULTIPOINT: { 0119 GeoDataMultiGeometry *geom = new GeoDataMultiGeometry; 0120 for( int j=0; j<shape->nVertices; ++j ) { 0121 geom->append( new GeoDataPoint( GeoDataCoordinates( 0122 shape->padfX[j], shape->padfY[j], 0123 0, GeoDataCoordinates::Degree ) ) ); 0124 } 0125 placemark->setGeometry( geom ); 0126 mDebug() << "multipoint " << placemark->name(); 0127 break; 0128 } 0129 0130 case SHPT_ARC: { 0131 if ( shape->nParts != 1 ) { 0132 GeoDataMultiGeometry *geom = new GeoDataMultiGeometry; 0133 for( int j=0; j<shape->nParts; ++j ) { 0134 GeoDataLineString *line = new GeoDataLineString; 0135 int itEnd = (j + 1 < shape->nParts) ? shape->panPartStart[j+1] : shape->nVertices; 0136 for( int k=shape->panPartStart[j]; k<itEnd; ++k ) { 0137 line->append( GeoDataCoordinates( 0138 shape->padfX[k], shape->padfY[k], 0139 0, GeoDataCoordinates::Degree ) ); 0140 } 0141 geom->append( line ); 0142 } 0143 placemark->setGeometry( geom ); 0144 mDebug() << "arc " << placemark->name() << " " << shape->nParts; 0145 0146 } else { 0147 GeoDataLineString *line = new GeoDataLineString; 0148 for( int j=0; j<shape->nVertices; ++j ) { 0149 line->append( GeoDataCoordinates( 0150 shape->padfX[j], shape->padfY[j], 0151 0, GeoDataCoordinates::Degree ) ); 0152 } 0153 placemark->setGeometry( line ); 0154 mDebug() << "arc " << placemark->name() << " " << shape->nParts; 0155 } 0156 break; 0157 } 0158 0159 case SHPT_POLYGON: { 0160 if ( shape->nParts != 1 ) { 0161 bool isRingClockwise = false; 0162 GeoDataMultiGeometry *multigeom = new GeoDataMultiGeometry; 0163 GeoDataPolygon *poly = nullptr; 0164 int polygonCount = 0; 0165 for( int j=0; j<shape->nParts; ++j ) { 0166 GeoDataLinearRing ring; 0167 int itStart = shape->panPartStart[j]; 0168 int itEnd = (j + 1 < shape->nParts) ? shape->panPartStart[j+1] : shape->nVertices; 0169 for( int k = itStart; k<itEnd; ++k ) { 0170 ring.append( GeoDataCoordinates( 0171 shape->padfX[k], shape->padfY[k], 0172 0, GeoDataCoordinates::Degree ) ); 0173 } 0174 isRingClockwise = ring.isClockwise(); 0175 if ( j == 0 || isRingClockwise ) { 0176 poly = new GeoDataPolygon; 0177 ++polygonCount; 0178 poly->setOuterBoundary( ring ); 0179 if ( polygonCount > 1 ) { 0180 multigeom->append( poly ); 0181 } 0182 } 0183 else { 0184 poly->appendInnerBoundary( ring ); 0185 } 0186 } 0187 if ( polygonCount > 1 ) { 0188 placemark->setGeometry( multigeom ); 0189 } 0190 else { 0191 placemark->setGeometry( poly ); 0192 delete multigeom; 0193 multigeom = nullptr; 0194 } 0195 mDebug() << "donut " << placemark->name() << " " << shape->nParts; 0196 0197 } else { 0198 GeoDataPolygon *poly = new GeoDataPolygon; 0199 GeoDataLinearRing ring; 0200 for( int j=0; j<shape->nVertices; ++j ) { 0201 ring.append( GeoDataCoordinates( 0202 shape->padfX[j], shape->padfY[j], 0203 0, GeoDataCoordinates::Degree ) ); 0204 } 0205 poly->setOuterBoundary( ring ); 0206 placemark->setGeometry( poly ); 0207 mDebug() << "poly " << placemark->name() << " " << shape->nParts; 0208 } 0209 break; 0210 } 0211 } 0212 } 0213 0214 SHPClose( handle ); 0215 0216 DBFClose( dbfhandle ); 0217 0218 if (!document->isEmpty()) { 0219 document->setFileName( fileName ); 0220 return document; 0221 } else { 0222 delete document; 0223 return nullptr; 0224 } 0225 } 0226 0227 } 0228 0229 #include "moc_ShpRunner.cpp"