LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/gmt - ogrgmtlayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 415 489 84.9 %
Date: 2024-11-21 22:18:42 Functions: 13 14 92.9 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  OpenGIS Simple Features Reference Implementation
       4             :  * Purpose:  Implements OGRGmtLayer class.
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2007, Frank Warmerdam <warmerdam@pobox.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "ogr_gmt.h"
      14             : #include "cpl_conv.h"
      15             : #include "ogr_p.h"
      16             : 
      17             : #include <algorithm>
      18             : 
      19             : /************************************************************************/
      20             : /*                            OGRGmtLayer()                             */
      21             : /************************************************************************/
      22             : 
      23         107 : OGRGmtLayer::OGRGmtLayer(GDALDataset *poDS, const char *pszFilename,
      24             :                          VSILFILE *fp, const OGRSpatialReference *poSRS,
      25         107 :                          int bUpdateIn)
      26             :     : m_poDS(poDS), poFeatureDefn(nullptr), iNextFID(0),
      27         107 :       bUpdate(CPL_TO_BOOL(bUpdateIn)),
      28             :       // Assume header complete in readonly mode.
      29         107 :       bHeaderComplete(CPL_TO_BOOL(!bUpdate)), bRegionComplete(false),
      30             :       nRegionOffset(0),
      31         107 :       m_fp(fp ? fp : VSIFOpenL(pszFilename, (bUpdateIn ? "r+" : "r"))),
      32         214 :       papszKeyedValues(nullptr), bValidFile(false)
      33             : {
      34         107 :     if (m_fp == nullptr)
      35          35 :         return;
      36             : 
      37             :     /* -------------------------------------------------------------------- */
      38             :     /*      Create the feature definition                                   */
      39             :     /* -------------------------------------------------------------------- */
      40          72 :     poFeatureDefn = new OGRFeatureDefn(CPLGetBasename(pszFilename));
      41          72 :     SetDescription(poFeatureDefn->GetName());
      42          72 :     poFeatureDefn->Reference();
      43             : 
      44             :     /* -------------------------------------------------------------------- */
      45             :     /*      Read the header.                                                */
      46             :     /* -------------------------------------------------------------------- */
      47          72 :     if (!STARTS_WITH(pszFilename, "/vsistdout"))
      48             :     {
      49         142 :         CPLString osFieldNames;
      50         142 :         CPLString osFieldTypes;
      51         142 :         CPLString osGeometryType;
      52         142 :         CPLString osRegion;
      53         142 :         CPLString osWKT;
      54         142 :         CPLString osProj4;
      55         142 :         CPLString osEPSG;
      56          71 :         vsi_l_offset nStartOfLine = 0;
      57             : 
      58          71 :         VSIFSeekL(m_fp, 0, SEEK_SET);
      59             : 
      60         258 :         while (ReadLine() && osLine[0] == '#')
      61             :         {
      62         207 :             if (strstr(osLine, "FEATURE_DATA"))
      63             :             {
      64          20 :                 bHeaderComplete = true;
      65          20 :                 ReadLine();
      66          20 :                 break;
      67             :             }
      68             : 
      69         187 :             if (STARTS_WITH_CI(osLine, "# REGION_STUB "))
      70          34 :                 nRegionOffset = nStartOfLine;
      71             : 
      72         390 :             for (int iKey = 0; papszKeyedValues != nullptr &&
      73         356 :                                papszKeyedValues[iKey] != nullptr;
      74             :                  iKey++)
      75             :             {
      76         203 :                 if (papszKeyedValues[iKey][0] == 'N')
      77          20 :                     osFieldNames = papszKeyedValues[iKey] + 1;
      78         203 :                 if (papszKeyedValues[iKey][0] == 'T')
      79          20 :                     osFieldTypes = papszKeyedValues[iKey] + 1;
      80         203 :                 if (papszKeyedValues[iKey][0] == 'G')
      81          52 :                     osGeometryType = papszKeyedValues[iKey] + 1;
      82         203 :                 if (papszKeyedValues[iKey][0] == 'R')
      83          35 :                     osRegion = papszKeyedValues[iKey] + 1;
      84         203 :                 if (papszKeyedValues[iKey][0] == 'J' &&
      85           6 :                     papszKeyedValues[iKey][1] != 0 &&
      86           6 :                     papszKeyedValues[iKey][2] != 0)
      87             :                 {
      88          12 :                     std::string osArg = papszKeyedValues[iKey] + 2;
      89          10 :                     if (osArg[0] == '"' && osArg.size() >= 2 &&
      90           4 :                         osArg.back() == '"')
      91             :                     {
      92           4 :                         osArg = osArg.substr(1, osArg.length() - 2);
      93           4 :                         char *pszArg = CPLUnescapeString(
      94             :                             osArg.c_str(), nullptr, CPLES_BackslashQuotable);
      95           4 :                         osArg = pszArg;
      96           4 :                         CPLFree(pszArg);
      97             :                     }
      98             : 
      99           6 :                     if (papszKeyedValues[iKey][1] == 'e')
     100           2 :                         osEPSG = std::move(osArg);
     101           6 :                     if (papszKeyedValues[iKey][1] == 'p')
     102           2 :                         osProj4 = std::move(osArg);
     103           6 :                     if (papszKeyedValues[iKey][1] == 'w')
     104           2 :                         osWKT = std::move(osArg);
     105             :                 }
     106             :             }
     107             : 
     108         187 :             nStartOfLine = VSIFTellL(m_fp);
     109             :         }
     110             : 
     111             :         /* --------------------------------------------------------------------
     112             :          */
     113             :         /*      Handle coordinate system. */
     114             :         /* --------------------------------------------------------------------
     115             :          */
     116          71 :         if (osWKT.length())
     117             :         {
     118           2 :             m_poSRS = new OGRSpatialReference();
     119           2 :             m_poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     120           2 :             if (m_poSRS->importFromWkt(osWKT.c_str()) != OGRERR_NONE)
     121             :             {
     122           0 :                 delete m_poSRS;
     123           0 :                 m_poSRS = nullptr;
     124             :             }
     125             :         }
     126          69 :         else if (osEPSG.length())
     127             :         {
     128           0 :             m_poSRS = new OGRSpatialReference();
     129           0 :             m_poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     130           0 :             if (m_poSRS->importFromEPSG(atoi(osEPSG)) != OGRERR_NONE)
     131             :             {
     132           0 :                 delete m_poSRS;
     133           0 :                 m_poSRS = nullptr;
     134             :             }
     135             :         }
     136          69 :         else if (osProj4.length())
     137             :         {
     138           0 :             m_poSRS = new OGRSpatialReference();
     139           0 :             m_poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     140           0 :             if (m_poSRS->importFromProj4(osProj4) != OGRERR_NONE)
     141             :             {
     142           0 :                 delete m_poSRS;
     143           0 :                 m_poSRS = nullptr;
     144             :             }
     145             :         }
     146             : 
     147          71 :         if (osGeometryType == "POINT")
     148           8 :             poFeatureDefn->SetGeomType(wkbPoint);
     149          63 :         else if (osGeometryType == "MULTIPOINT")
     150           8 :             poFeatureDefn->SetGeomType(wkbMultiPoint);
     151          55 :         else if (osGeometryType == "LINESTRING")
     152           8 :             poFeatureDefn->SetGeomType(wkbLineString);
     153          47 :         else if (osGeometryType == "MULTILINESTRING")
     154           9 :             poFeatureDefn->SetGeomType(wkbMultiLineString);
     155          38 :         else if (osGeometryType == "POLYGON")
     156          10 :             poFeatureDefn->SetGeomType(wkbPolygon);
     157          28 :         else if (osGeometryType == "MULTIPOLYGON")
     158           9 :             poFeatureDefn->SetGeomType(wkbMultiPolygon);
     159             : 
     160             :         /* --------------------------------------------------------------------
     161             :          */
     162             :         /*      Process a region line. */
     163             :         /* --------------------------------------------------------------------
     164             :          */
     165          71 :         if (osRegion.length() > 0)
     166             :         {
     167             :             char **papszTokens =
     168          35 :                 CSLTokenizeStringComplex(osRegion.c_str(), "/", FALSE, FALSE);
     169             : 
     170          35 :             if (CSLCount(papszTokens) == 4)
     171             :             {
     172          35 :                 sRegion.MinX = CPLAtofM(papszTokens[0]);
     173          35 :                 sRegion.MaxX = CPLAtofM(papszTokens[1]);
     174          35 :                 sRegion.MinY = CPLAtofM(papszTokens[2]);
     175          35 :                 sRegion.MaxY = CPLAtofM(papszTokens[3]);
     176             :             }
     177             : 
     178          35 :             bRegionComplete = true;
     179             : 
     180          35 :             CSLDestroy(papszTokens);
     181             :         }
     182             : 
     183             :         /* --------------------------------------------------------------------
     184             :          */
     185             :         /*      Process fields. */
     186             :         /* --------------------------------------------------------------------
     187             :          */
     188          71 :         if (osFieldNames.length() || osFieldTypes.length())
     189             :         {
     190             :             char **papszFN =
     191          20 :                 CSLTokenizeStringComplex(osFieldNames, "|", TRUE, TRUE);
     192             :             char **papszFT =
     193          20 :                 CSLTokenizeStringComplex(osFieldTypes, "|", TRUE, TRUE);
     194          20 :             const int nFNCount = CSLCount(papszFN);
     195          20 :             const int nFTCount = CSLCount(papszFT);
     196          20 :             const int nFieldCount = std::max(nFNCount, nFTCount);
     197             : 
     198         110 :             for (int iField = 0; iField < nFieldCount; iField++)
     199             :             {
     200         180 :                 OGRFieldDefn oField("", OFTString);
     201             : 
     202          90 :                 if (iField < nFNCount)
     203          90 :                     oField.SetName(papszFN[iField]);
     204             :                 else
     205           0 :                     oField.SetName(CPLString().Printf("Field_%d", iField + 1));
     206             : 
     207          90 :                 if (iField < nFTCount)
     208             :                 {
     209          90 :                     if (EQUAL(papszFT[iField], "integer"))
     210          19 :                         oField.SetType(OFTInteger);
     211          71 :                     else if (EQUAL(papszFT[iField], "double"))
     212          19 :                         oField.SetType(OFTReal);
     213          52 :                     else if (EQUAL(papszFT[iField], "datetime"))
     214          17 :                         oField.SetType(OFTDateTime);
     215             :                 }
     216             : 
     217          90 :                 poFeatureDefn->AddFieldDefn(&oField);
     218             :             }
     219             : 
     220          20 :             CSLDestroy(papszFN);
     221          20 :             CSLDestroy(papszFT);
     222             :         }
     223             :     }
     224             :     else
     225             :     {
     226           1 :         if (poSRS)
     227             :         {
     228           1 :             m_poSRS = poSRS->Clone();
     229           1 :             m_poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     230             :         }
     231             :     }
     232             : 
     233          72 :     poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(m_poSRS);
     234             : 
     235          72 :     bValidFile = true;
     236             : }
     237             : 
     238             : /************************************************************************/
     239             : /*                           ~OGRGmtLayer()                           */
     240             : /************************************************************************/
     241             : 
     242         214 : OGRGmtLayer::~OGRGmtLayer()
     243             : 
     244             : {
     245         107 :     if (m_nFeaturesRead > 0 && poFeatureDefn != nullptr)
     246             :     {
     247          10 :         CPLDebug("Gmt", "%d features read on layer '%s'.",
     248           5 :                  static_cast<int>(m_nFeaturesRead), poFeatureDefn->GetName());
     249             :     }
     250             : 
     251             :     /* -------------------------------------------------------------------- */
     252             :     /*      Write out the region bounds if we know where they go, and we    */
     253             :     /*      are in update mode.                                             */
     254             :     /* -------------------------------------------------------------------- */
     255         107 :     if (nRegionOffset != 0 && bUpdate)
     256             :     {
     257          34 :         VSIFSeekL(m_fp, nRegionOffset, SEEK_SET);
     258          34 :         VSIFPrintfL(m_fp, "# @R%.12g/%.12g/%.12g/%.12g", sRegion.MinX,
     259             :                     sRegion.MaxX, sRegion.MinY, sRegion.MaxY);
     260             :     }
     261             : 
     262             :     /* -------------------------------------------------------------------- */
     263             :     /*      Clean up.                                                       */
     264             :     /* -------------------------------------------------------------------- */
     265         107 :     CSLDestroy(papszKeyedValues);
     266             : 
     267         107 :     if (poFeatureDefn)
     268          72 :         poFeatureDefn->Release();
     269             : 
     270         107 :     if (m_poSRS)
     271           3 :         m_poSRS->Release();
     272             : 
     273         107 :     if (m_fp != nullptr)
     274          72 :         VSIFCloseL(m_fp);
     275         214 : }
     276             : 
     277             : /************************************************************************/
     278             : /*                              ReadLine()                              */
     279             : /*                                                                      */
     280             : /*      Read a line into osLine.  If it is a comment line with @        */
     281             : /*      keyed values, parse out the keyed values into                   */
     282             : /*      papszKeyedValues.                                               */
     283             : /************************************************************************/
     284             : 
     285        1244 : bool OGRGmtLayer::ReadLine()
     286             : 
     287             : {
     288             :     /* -------------------------------------------------------------------- */
     289             :     /*      Clear last line.                                                */
     290             :     /* -------------------------------------------------------------------- */
     291        1244 :     osLine.erase();
     292        1244 :     if (papszKeyedValues)
     293             :     {
     294         286 :         CSLDestroy(papszKeyedValues);
     295         286 :         papszKeyedValues = nullptr;
     296             :     }
     297             : 
     298             :     /* -------------------------------------------------------------------- */
     299             :     /*      Read newline.                                                   */
     300             :     /* -------------------------------------------------------------------- */
     301        1244 :     const char *pszLine = CPLReadLineL(m_fp);
     302        1244 :     if (pszLine == nullptr)
     303          56 :         return false;  // end of file.
     304             : 
     305        1188 :     osLine = pszLine;
     306             : 
     307             :     /* -------------------------------------------------------------------- */
     308             :     /*      If this is a comment line with keyed values, parse them.        */
     309             :     /* -------------------------------------------------------------------- */
     310             : 
     311        1188 :     if (osLine[0] != '#' || osLine.find_first_of('@') == std::string::npos)
     312         898 :         return true;
     313             : 
     314         290 :     CPLStringList aosKeyedValues;
     315        3163 :     for (size_t i = 0; i < osLine.length(); i++)
     316             :     {
     317        2873 :         if (osLine[i] == '@' && i + 2 <= osLine.size())
     318             :         {
     319         341 :             bool bInQuotes = false;
     320             : 
     321         341 :             size_t iValEnd = i + 2;  // Used after for.
     322        5660 :             for (; iValEnd < osLine.length(); iValEnd++)
     323             :             {
     324        5406 :                 if (!bInQuotes && isspace((unsigned char)osLine[iValEnd]))
     325          87 :                     break;
     326             : 
     327        6511 :                 if (bInQuotes && iValEnd < osLine.length() - 1 &&
     328        1192 :                     osLine[iValEnd] == '\\')
     329             :                 {
     330          88 :                     iValEnd++;
     331             :                 }
     332        5231 :                 else if (osLine[iValEnd] == '"')
     333          26 :                     bInQuotes = !bInQuotes;
     334             :             }
     335             : 
     336         682 :             const CPLString osValue = osLine.substr(i + 2, iValEnd - i - 2);
     337             : 
     338             :             // Unecape contents
     339             :             char *pszUEValue =
     340         341 :                 CPLUnescapeString(osValue, nullptr, CPLES_BackslashQuotable);
     341             : 
     342         341 :             CPLString osKeyValue = osLine.substr(i + 1, 1);
     343         341 :             osKeyValue += pszUEValue;
     344         341 :             CPLFree(pszUEValue);
     345         341 :             aosKeyedValues.AddString(osKeyValue);
     346             : 
     347         341 :             i = iValEnd;
     348             :         }
     349             :     }
     350         290 :     papszKeyedValues = aosKeyedValues.StealList();
     351             : 
     352         290 :     return true;
     353             : }
     354             : 
     355             : /************************************************************************/
     356             : /*                            ResetReading()                            */
     357             : /************************************************************************/
     358             : 
     359          20 : void OGRGmtLayer::ResetReading()
     360             : 
     361             : {
     362          20 :     if (iNextFID == 0)
     363          18 :         return;
     364             : 
     365           2 :     iNextFID = 0;
     366           2 :     VSIFSeekL(m_fp, 0, SEEK_SET);
     367           2 :     ReadLine();
     368             : }
     369             : 
     370             : /************************************************************************/
     371             : /*                          ScanAheadForHole()                          */
     372             : /*                                                                      */
     373             : /*      Scan ahead to see if the next geometry is a hole.  If so        */
     374             : /*      return true, otherwise seek back to where we were and return    */
     375             : /*      false.                                                          */
     376             : /************************************************************************/
     377             : 
     378          30 : bool OGRGmtLayer::ScanAheadForHole()
     379             : 
     380             : {
     381          60 :     const CPLString osSavedLine = osLine;
     382          30 :     const vsi_l_offset nSavedLocation = VSIFTellL(m_fp);
     383             : 
     384          87 :     while (ReadLine() && osLine[0] == '#')
     385             :     {
     386          58 :         if (papszKeyedValues != nullptr && papszKeyedValues[0][0] == 'H')
     387           1 :             return true;
     388             :     }
     389             : 
     390          29 :     VSIFSeekL(m_fp, nSavedLocation, SEEK_SET);
     391          29 :     osLine = osSavedLine;
     392             : 
     393             :     // We do not actually restore papszKeyedValues, but we
     394             :     // assume it does not matter since this method is only called
     395             :     // when processing the '>' line.
     396             : 
     397          29 :     return false;
     398             : }
     399             : 
     400             : /************************************************************************/
     401             : /*                           NextIsFeature()                            */
     402             : /*                                                                      */
     403             : /*      Returns true if the next line is a feature attribute line.      */
     404             : /*      This generally indicates the end of a multilinestring or        */
     405             : /*      multipolygon feature.                                           */
     406             : /************************************************************************/
     407             : 
     408           5 : bool OGRGmtLayer::NextIsFeature()
     409             : 
     410             : {
     411           5 :     const CPLString osSavedLine = osLine;
     412           5 :     const vsi_l_offset nSavedLocation = VSIFTellL(m_fp);
     413           5 :     bool bReturn = false;
     414             : 
     415           5 :     ReadLine();
     416             : 
     417           5 :     if (osLine[0] == '#' && strstr(osLine, "@D") != nullptr)
     418           2 :         bReturn = true;
     419             : 
     420           5 :     VSIFSeekL(m_fp, nSavedLocation, SEEK_SET);
     421           5 :     osLine = osSavedLine;
     422             : 
     423             :     // We do not actually restore papszKeyedValues, but we
     424             :     // assume it does not matter since this method is only called
     425             :     // when processing the '>' line.
     426             : 
     427          10 :     return bReturn;
     428             : }
     429             : 
     430             : /************************************************************************/
     431             : /*                         GetNextRawFeature()                          */
     432             : /************************************************************************/
     433             : 
     434          55 : OGRFeature *OGRGmtLayer::GetNextRawFeature()
     435             : 
     436             : {
     437             : #if 0
     438             :     bool bMultiVertex =
     439             :         poFeatureDefn->GetGeomType() != wkbPoint
     440             :         && poFeatureDefn->GetGeomType() != wkbUnknown;
     441             : #endif
     442         110 :     CPLString osFieldData;
     443          55 :     OGRGeometry *poGeom = nullptr;
     444             : 
     445             :     /* -------------------------------------------------------------------- */
     446             :     /*      Read lines associated with this feature.                        */
     447             :     /* -------------------------------------------------------------------- */
     448         871 :     for (; true; ReadLine())
     449             :     {
     450         926 :         if (osLine.length() == 0)
     451          25 :             break;
     452             : 
     453         901 :         if (osLine[0] == '>')
     454             :         {
     455          67 :             OGRwkbGeometryType eType = wkbUnknown;
     456          67 :             if (poGeom)
     457          33 :                 eType = wkbFlatten(poGeom->getGeometryType());
     458          67 :             if (eType == wkbMultiPolygon)
     459             :             {
     460           3 :                 OGRMultiPolygon *poMP = poGeom->toMultiPolygon();
     461           3 :                 if (ScanAheadForHole())
     462             :                 {
     463             :                     // Add a hole to the current polygon.
     464           1 :                     poMP->getGeometryRef(poMP->getNumGeometries() - 1)
     465           1 :                         ->addRingDirectly(new OGRLinearRing());
     466             :                 }
     467           2 :                 else if (!NextIsFeature())
     468             :                 {
     469           1 :                     OGRPolygon *poPoly = new OGRPolygon();
     470             : 
     471           1 :                     poPoly->addRingDirectly(new OGRLinearRing());
     472             : 
     473           1 :                     poMP->addGeometryDirectly(poPoly);
     474             :                 }
     475             :                 else
     476           1 :                     break; /* done geometry */
     477             :             }
     478          64 :             else if (eType == wkbPolygon)
     479             :             {
     480          27 :                 if (ScanAheadForHole())
     481           0 :                     poGeom->toPolygon()->addRingDirectly(new OGRLinearRing());
     482             :                 else
     483          27 :                     break; /* done geometry */
     484             :             }
     485          37 :             else if (eType == wkbMultiLineString && !NextIsFeature())
     486             :             {
     487           4 :                 poGeom->toMultiLineString()->addGeometryDirectly(
     488           2 :                     new OGRLineString());
     489             :             }
     490          35 :             else if (poGeom != nullptr)
     491             :             {
     492           1 :                 break;
     493             :             }
     494          34 :             else if (poFeatureDefn->GetGeomType() == wkbUnknown)
     495             :             {
     496           0 :                 poFeatureDefn->SetGeomType(wkbLineString);
     497             :                 // bMultiVertex = true;
     498             :             }
     499             :         }
     500         834 :         else if (osLine[0] == '#')
     501             :         {
     502         145 :             for (int i = 0;
     503         145 :                  papszKeyedValues != nullptr && papszKeyedValues[i] != nullptr;
     504             :                  i++)
     505             :             {
     506          72 :                 if (papszKeyedValues[i][0] == 'D')
     507          34 :                     osFieldData = papszKeyedValues[i] + 1;
     508             :             }
     509             :         }
     510             :         else
     511             :         {
     512             :             // Parse point line.
     513         761 :             double dfX = 0.0;
     514         761 :             double dfY = 0.0;
     515         761 :             double dfZ = 0.0;
     516         761 :             const int nDim = CPLsscanf(osLine, "%lf %lf %lf", &dfX, &dfY, &dfZ);
     517             : 
     518         761 :             if (nDim >= 2)
     519             :             {
     520         761 :                 if (poGeom == nullptr)
     521             :                 {
     522          35 :                     switch (poFeatureDefn->GetGeomType())
     523             :                     {
     524           0 :                         case wkbLineString:
     525           0 :                             poGeom = new OGRLineString();
     526           0 :                             break;
     527             : 
     528          30 :                         case wkbPolygon:
     529             :                         {
     530          30 :                             OGRPolygon *poPoly = new OGRPolygon();
     531          30 :                             poGeom = poPoly;
     532          30 :                             poPoly->addRingDirectly(new OGRLinearRing());
     533          30 :                             break;
     534             :                         }
     535             : 
     536           2 :                         case wkbMultiPolygon:
     537             :                         {
     538           2 :                             OGRPolygon *poPoly = new OGRPolygon();
     539           2 :                             poPoly->addRingDirectly(new OGRLinearRing());
     540             : 
     541           2 :                             OGRMultiPolygon *poMP = new OGRMultiPolygon();
     542           2 :                             poGeom = poMP;
     543           2 :                             poMP->addGeometryDirectly(poPoly);
     544             :                         }
     545           2 :                         break;
     546             : 
     547           0 :                         case wkbMultiPoint:
     548           0 :                             poGeom = new OGRMultiPoint();
     549           0 :                             break;
     550             : 
     551           2 :                         case wkbMultiLineString:
     552             :                         {
     553             :                             OGRMultiLineString *poMLS =
     554           2 :                                 new OGRMultiLineString();
     555           2 :                             poGeom = poMLS;
     556           2 :                             poMLS->addGeometryDirectly(new OGRLineString());
     557           2 :                             break;
     558             :                         }
     559             : 
     560           1 :                         case wkbPoint:
     561             :                         case wkbUnknown:
     562             :                         default:
     563           1 :                             poGeom = new OGRPoint();
     564           1 :                             break;
     565             :                     }
     566             :                 }
     567             : 
     568         761 :                 CPLAssert(poGeom != nullptr);
     569             :                 // cppcheck-suppress nullPointerRedundantCheck
     570         761 :                 switch (wkbFlatten(poGeom->getGeometryType()))
     571             :                 {
     572           1 :                     case wkbPoint:
     573             :                     {
     574           1 :                         OGRPoint *poPoint = poGeom->toPoint();
     575           1 :                         poPoint->setX(dfX);
     576           1 :                         poPoint->setY(dfY);
     577           1 :                         if (nDim == 3)
     578           1 :                             poPoint->setZ(dfZ);
     579           1 :                         break;
     580             :                     }
     581             : 
     582           0 :                     case wkbLineString:
     583             :                     {
     584           0 :                         OGRLineString *poLS = poGeom->toLineString();
     585           0 :                         if (nDim == 3)
     586           0 :                             poLS->addPoint(dfX, dfY, dfZ);
     587             :                         else
     588           0 :                             poLS->addPoint(dfX, dfY);
     589           0 :                         break;
     590             :                     }
     591             : 
     592         752 :                     case wkbPolygon:
     593             :                     case wkbMultiPolygon:
     594             :                     {
     595         752 :                         OGRPolygon *poPoly = nullptr;
     596             : 
     597         752 :                         if (wkbFlatten(poGeom->getGeometryType()) ==
     598             :                             wkbMultiPolygon)
     599             :                         {
     600          17 :                             OGRMultiPolygon *poMP = poGeom->toMultiPolygon();
     601          17 :                             poPoly = poMP->getGeometryRef(
     602          17 :                                 poMP->getNumGeometries() - 1);
     603             :                         }
     604             :                         else
     605         735 :                             poPoly = poGeom->toPolygon();
     606             : 
     607         752 :                         OGRLinearRing *poRing = nullptr;
     608         752 :                         if (poPoly->getNumInteriorRings() == 0)
     609         748 :                             poRing = poPoly->getExteriorRing();
     610             :                         else
     611           4 :                             poRing = poPoly->getInteriorRing(
     612           4 :                                 poPoly->getNumInteriorRings() - 1);
     613             : 
     614         752 :                         if (nDim == 3)
     615           0 :                             poRing->addPoint(dfX, dfY, dfZ);
     616             :                         else
     617         752 :                             poRing->addPoint(dfX, dfY);
     618             :                     }
     619         752 :                     break;
     620             : 
     621           8 :                     case wkbMultiLineString:
     622             :                     {
     623           8 :                         OGRMultiLineString *poML = poGeom->toMultiLineString();
     624             :                         OGRLineString *poLine =
     625           8 :                             poML->getGeometryRef(poML->getNumGeometries() - 1);
     626             : 
     627           8 :                         if (nDim == 3)
     628           0 :                             poLine->addPoint(dfX, dfY, dfZ);
     629             :                         else
     630           8 :                             poLine->addPoint(dfX, dfY);
     631             :                     }
     632           8 :                     break;
     633             : 
     634           0 :                     default:
     635           0 :                         CPLAssert(false);
     636             :                 }
     637             :             }
     638             :         }
     639             : 
     640         872 :         if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
     641             :         {
     642           1 :             ReadLine();
     643           1 :             break;
     644             :         }
     645         871 :     }
     646             : 
     647          55 :     if (poGeom == nullptr)
     648          20 :         return nullptr;
     649             : 
     650             :     /* -------------------------------------------------------------------- */
     651             :     /*      Create feature.                                                 */
     652             :     /* -------------------------------------------------------------------- */
     653          35 :     OGRFeature *poFeature = new OGRFeature(poFeatureDefn);
     654          35 :     poGeom->assignSpatialReference(m_poSRS);
     655          35 :     poFeature->SetGeometryDirectly(poGeom);
     656          35 :     poFeature->SetFID(iNextFID++);
     657             : 
     658             :     /* -------------------------------------------------------------------- */
     659             :     /*      Process field values.                                           */
     660             :     /* -------------------------------------------------------------------- */
     661          35 :     char **papszFD = CSLTokenizeStringComplex(osFieldData, "|", TRUE, TRUE);
     662             : 
     663         133 :     for (int iField = 0; papszFD != nullptr && papszFD[iField] != nullptr;
     664             :          iField++)
     665             :     {
     666          98 :         if (iField >= poFeatureDefn->GetFieldCount())
     667           0 :             break;
     668             : 
     669          98 :         poFeature->SetField(iField, papszFD[iField]);
     670             :     }
     671             : 
     672          35 :     CSLDestroy(papszFD);
     673             : 
     674          35 :     m_nFeaturesRead++;
     675             : 
     676          35 :     return poFeature;
     677             : }
     678             : 
     679             : /************************************************************************/
     680             : /*                           CompleteHeader()                           */
     681             : /*                                                                      */
     682             : /*      Finish writing out the header with field definitions and the    */
     683             : /*      layer geometry type.                                            */
     684             : /************************************************************************/
     685             : 
     686          19 : OGRErr OGRGmtLayer::CompleteHeader(OGRGeometry *poThisGeom)
     687             : 
     688             : {
     689             :     /* -------------------------------------------------------------------- */
     690             :     /*      If we do not already have a geometry type, try to work one      */
     691             :     /*      out and write it now.                                           */
     692             :     /* -------------------------------------------------------------------- */
     693          19 :     if (poFeatureDefn->GetGeomType() == wkbUnknown && poThisGeom != nullptr)
     694             :     {
     695           2 :         poFeatureDefn->SetGeomType(wkbFlatten(poThisGeom->getGeometryType()));
     696             : 
     697           2 :         const char *pszGeom = nullptr;
     698           2 :         switch (wkbFlatten(poFeatureDefn->GetGeomType()))
     699             :         {
     700           0 :             case wkbPoint:
     701           0 :                 pszGeom = " @GPOINT";
     702           0 :                 break;
     703           0 :             case wkbLineString:
     704           0 :                 pszGeom = " @GLINESTRING";
     705           0 :                 break;
     706           1 :             case wkbPolygon:
     707           1 :                 pszGeom = " @GPOLYGON";
     708           1 :                 break;
     709           0 :             case wkbMultiPoint:
     710           0 :                 pszGeom = " @GMULTIPOINT";
     711           0 :                 break;
     712           0 :             case wkbMultiLineString:
     713           0 :                 pszGeom = " @GMULTILINESTRING";
     714           0 :                 break;
     715           1 :             case wkbMultiPolygon:
     716           1 :                 pszGeom = " @GMULTIPOLYGON";
     717           1 :                 break;
     718           0 :             default:
     719           0 :                 pszGeom = "";
     720           0 :                 break;
     721             :         }
     722             : 
     723           2 :         VSIFPrintfL(m_fp, "#%s\n", pszGeom);
     724             :     }
     725             : 
     726             :     /* -------------------------------------------------------------------- */
     727             :     /*      Prepare and write the field names and types.                    */
     728             :     /* -------------------------------------------------------------------- */
     729          38 :     CPLString osFieldNames;
     730          19 :     CPLString osFieldTypes;
     731             : 
     732         106 :     for (int iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++)
     733             :     {
     734          87 :         if (iField > 0)
     735             :         {
     736          68 :             osFieldNames += "|";
     737          68 :             osFieldTypes += "|";
     738             :         }
     739             : 
     740          87 :         osFieldNames += poFeatureDefn->GetFieldDefn(iField)->GetNameRef();
     741          87 :         switch (poFeatureDefn->GetFieldDefn(iField)->GetType())
     742             :         {
     743          18 :             case OFTInteger:
     744          18 :                 osFieldTypes += "integer";
     745          18 :                 break;
     746             : 
     747          19 :             case OFTReal:
     748          19 :                 osFieldTypes += "double";
     749          19 :                 break;
     750             : 
     751          16 :             case OFTDateTime:
     752          16 :                 osFieldTypes += "datetime";
     753          16 :                 break;
     754             : 
     755          34 :             default:
     756          34 :                 osFieldTypes += "string";
     757          34 :                 break;
     758             :         }
     759             :     }
     760             : 
     761          19 :     if (poFeatureDefn->GetFieldCount() > 0)
     762             :     {
     763          19 :         VSIFPrintfL(m_fp, "# @N%s\n", osFieldNames.c_str());
     764          19 :         VSIFPrintfL(m_fp, "# @T%s\n", osFieldTypes.c_str());
     765             :     }
     766             : 
     767             :     /* -------------------------------------------------------------------- */
     768             :     /*      Mark the end of the header, and start of feature data.          */
     769             :     /* -------------------------------------------------------------------- */
     770          19 :     VSIFPrintfL(m_fp, "# FEATURE_DATA\n");
     771             : 
     772          19 :     bHeaderComplete = true;
     773          19 :     bRegionComplete = true;  // no feature written, so we know them all!
     774             : 
     775          38 :     return OGRERR_NONE;
     776             : }
     777             : 
     778             : /************************************************************************/
     779             : /*                           ICreateFeature()                            */
     780             : /************************************************************************/
     781             : 
     782          86 : OGRErr OGRGmtLayer::ICreateFeature(OGRFeature *poFeature)
     783             : 
     784             : {
     785          86 :     if (!bUpdate)
     786             :     {
     787           0 :         CPLError(CE_Failure, CPLE_NoWriteAccess,
     788             :                  "Cannot create features on read-only dataset.");
     789           0 :         return OGRERR_FAILURE;
     790             :     }
     791             : 
     792             :     /* -------------------------------------------------------------------- */
     793             :     /*      Do we need to write the header describing the fields?           */
     794             :     /* -------------------------------------------------------------------- */
     795          86 :     if (!bHeaderComplete)
     796             :     {
     797          19 :         OGRErr eErr = CompleteHeader(poFeature->GetGeometryRef());
     798             : 
     799          19 :         if (eErr != OGRERR_NONE)
     800           0 :             return eErr;
     801             :     }
     802             : 
     803             :     /* -------------------------------------------------------------------- */
     804             :     /*      Write out the feature                                           */
     805             :     /* -------------------------------------------------------------------- */
     806          86 :     OGRGeometry *poGeom = poFeature->GetGeometryRef();
     807             : 
     808          86 :     if (poGeom == nullptr)
     809             :     {
     810          33 :         CPLError(CE_Failure, CPLE_AppDefined,
     811             :                  "Features without geometry not supported by GMT writer.");
     812          33 :         return OGRERR_FAILURE;
     813             :     }
     814             : 
     815          53 :     if (poFeatureDefn->GetGeomType() == wkbUnknown)
     816           4 :         poFeatureDefn->SetGeomType(wkbFlatten(poGeom->getGeometryType()));
     817             : 
     818             :     // Do we need a vertex collection marker grouping vertices.
     819          53 :     if (poFeatureDefn->GetGeomType() != wkbPoint)
     820          47 :         VSIFPrintfL(m_fp, ">\n");
     821             : 
     822             :     /* -------------------------------------------------------------------- */
     823             :     /*      Write feature properties()                                      */
     824             :     /* -------------------------------------------------------------------- */
     825          53 :     if (poFeatureDefn->GetFieldCount() > 0)
     826             :     {
     827         106 :         CPLString osFieldData;
     828             : 
     829         270 :         for (int iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++)
     830             :         {
     831             :             OGRFieldType eFType =
     832         217 :                 poFeatureDefn->GetFieldDefn(iField)->GetType();
     833         217 :             const char *pszRawValue = poFeature->GetFieldAsString(iField);
     834             : 
     835         217 :             if (iField > 0)
     836         164 :                 osFieldData += "|";
     837             : 
     838             :             // We do not want prefix spaces for numeric values.
     839         217 :             if (eFType == OFTInteger || eFType == OFTReal)
     840         104 :                 while (*pszRawValue == ' ')
     841           0 :                     pszRawValue++;
     842             : 
     843         217 :             if (strchr(pszRawValue, ' ') || strchr(pszRawValue, '|') ||
     844         186 :                 strchr(pszRawValue, '\t') || strchr(pszRawValue, '\n'))
     845             :             {
     846          31 :                 osFieldData += "\"";
     847             : 
     848             :                 char *pszEscapedVal =
     849          31 :                     CPLEscapeString(pszRawValue, -1, CPLES_BackslashQuotable);
     850          31 :                 osFieldData += pszEscapedVal;
     851          31 :                 CPLFree(pszEscapedVal);
     852             : 
     853          31 :                 osFieldData += "\"";
     854             :             }
     855             :             else
     856         186 :                 osFieldData += pszRawValue;
     857             :         }
     858             : 
     859          53 :         VSIFPrintfL(m_fp, "# @D%s\n", osFieldData.c_str());
     860             :     }
     861             : 
     862             :     /* -------------------------------------------------------------------- */
     863             :     /*      Write Geometry                                                  */
     864             :     /* -------------------------------------------------------------------- */
     865          53 :     return WriteGeometry(OGRGeometry::ToHandle(poGeom), true);
     866             : }
     867             : 
     868             : /************************************************************************/
     869             : /*                           WriteGeometry()                            */
     870             : /*                                                                      */
     871             : /*      Write a geometry to the file.  If bHaveAngle is true it         */
     872             : /*      means the angle bracket preceding the point stream has          */
     873             : /*      already been written out.                                       */
     874             : /*                                                                      */
     875             : /*      We use the C API for geometry access because of its            */
     876             : /*      simplified access to vertices and children geometries.          */
     877             : /************************************************************************/
     878             : 
     879         116 : OGRErr OGRGmtLayer::WriteGeometry(OGRGeometryH hGeom, bool bHaveAngle)
     880             : 
     881             : {
     882             :     /* -------------------------------------------------------------------- */
     883             :     /*      This is a geometry with sub-geometries.                         */
     884             :     /* -------------------------------------------------------------------- */
     885         116 :     if (OGR_G_GetGeometryCount(hGeom) > 0)
     886             :     {
     887          53 :         OGRErr eErr = OGRERR_NONE;
     888             : 
     889          53 :         for (int iGeom = 0;
     890         116 :              iGeom < OGR_G_GetGeometryCount(hGeom) && eErr == OGRERR_NONE;
     891             :              iGeom++)
     892             :         {
     893             :             // We need to emit polygon @P and @H items while we still
     894             :             // know this is a polygon and which is the outer and inner
     895             :             // ring.
     896          63 :             if (wkbFlatten(OGR_G_GetGeometryType(hGeom)) == wkbPolygon)
     897             :             {
     898          36 :                 if (!bHaveAngle)
     899             :                 {
     900           6 :                     VSIFPrintfL(m_fp, ">\n");
     901           6 :                     bHaveAngle = true;
     902             :                 }
     903          36 :                 if (iGeom == 0)
     904          35 :                     VSIFPrintfL(m_fp, "# @P\n");
     905             :                 else
     906           1 :                     VSIFPrintfL(m_fp, "# @H\n");
     907             :             }
     908             : 
     909             :             eErr =
     910          63 :                 WriteGeometry(OGR_G_GetGeometryRef(hGeom, iGeom), bHaveAngle);
     911          63 :             bHaveAngle = false;
     912             :         }
     913          53 :         return eErr;
     914             :     }
     915             : 
     916             :     /* -------------------------------------------------------------------- */
     917             :     /*      If this is not a point we need to have an angle bracket to      */
     918             :     /*      mark the vertex list.                                           */
     919             :     /* -------------------------------------------------------------------- */
     920          63 :     if (wkbFlatten(OGR_G_GetGeometryType(hGeom)) != wkbPoint && !bHaveAngle)
     921           4 :         VSIFPrintfL(m_fp, ">\n");
     922             : 
     923             :     /* -------------------------------------------------------------------- */
     924             :     /*      Dump vertices.                                                  */
     925             :     /* -------------------------------------------------------------------- */
     926          63 :     const int nPointCount = OGR_G_GetPointCount(hGeom);
     927          63 :     const int nDim = OGR_G_GetCoordinateDimension(hGeom);
     928             :     // For testing only. Ticket #6453
     929             :     const bool bUseTab =
     930          63 :         CPLTestBool(CPLGetConfigOption("GMT_USE_TAB", "FALSE"));
     931             : 
     932         671 :     for (int iPoint = 0; iPoint < nPointCount; iPoint++)
     933             :     {
     934         608 :         const double dfX = OGR_G_GetX(hGeom, iPoint);
     935         608 :         const double dfY = OGR_G_GetY(hGeom, iPoint);
     936         608 :         const double dfZ = OGR_G_GetZ(hGeom, iPoint);
     937             : 
     938         608 :         sRegion.Merge(dfX, dfY);
     939             :         char szLine[128];
     940         608 :         OGRMakeWktCoordinate(szLine, dfX, dfY, dfZ, nDim);
     941         608 :         if (bUseTab)
     942             :         {
     943          60 :             for (char *szPtr = szLine; *szPtr != '\0'; ++szPtr)
     944             :             {
     945          47 :                 if (*szPtr == ' ')
     946          13 :                     *szPtr = '\t';
     947             :             }
     948             :         }
     949         608 :         if (VSIFPrintfL(m_fp, "%s\n", szLine) < 1)
     950             :         {
     951           0 :             CPLError(CE_Failure, CPLE_FileIO, "Gmt write failure: %s",
     952           0 :                      VSIStrerror(errno));
     953           0 :             return OGRERR_FAILURE;
     954             :         }
     955             :     }
     956             : 
     957          63 :     return OGRERR_NONE;
     958             : }
     959             : 
     960             : /************************************************************************/
     961             : /*                             GetExtent()                              */
     962             : /*                                                                      */
     963             : /*      Fetch extent of the data currently stored in the dataset.       */
     964             : /*      The bForce flag has no effect on SHO files since that value     */
     965             : /*      is always in the header.                                        */
     966             : /*                                                                      */
     967             : /*      Returns OGRERR_NONE/OGRRERR_FAILURE.                            */
     968             : /************************************************************************/
     969             : 
     970           0 : OGRErr OGRGmtLayer::GetExtent(OGREnvelope *psExtent, int bForce)
     971             : 
     972             : {
     973           0 :     if (bRegionComplete && sRegion.IsInit())
     974             :     {
     975           0 :         *psExtent = sRegion;
     976           0 :         return OGRERR_NONE;
     977             :     }
     978             : 
     979           0 :     return OGRLayer::GetExtent(psExtent, bForce);
     980             : }
     981             : 
     982             : /************************************************************************/
     983             : /*                           TestCapability()                           */
     984             : /************************************************************************/
     985             : 
     986          72 : int OGRGmtLayer::TestCapability(const char *pszCap)
     987             : 
     988             : {
     989          72 :     if (EQUAL(pszCap, OLCRandomRead))
     990           0 :         return FALSE;
     991             : 
     992          72 :     if (EQUAL(pszCap, OLCSequentialWrite))
     993          16 :         return TRUE;
     994             : 
     995          56 :     if (EQUAL(pszCap, OLCFastSpatialFilter))
     996           0 :         return FALSE;
     997             : 
     998          56 :     if (EQUAL(pszCap, OLCFastGetExtent))
     999           0 :         return bRegionComplete;
    1000             : 
    1001          56 :     if (EQUAL(pszCap, OLCCreateField))
    1002          16 :         return TRUE;
    1003             : 
    1004          40 :     if (EQUAL(pszCap, OLCZGeometries))
    1005           0 :         return TRUE;
    1006             : 
    1007          40 :     return FALSE;
    1008             : }
    1009             : 
    1010             : /************************************************************************/
    1011             : /*                            CreateField()                             */
    1012             : /************************************************************************/
    1013             : 
    1014          87 : OGRErr OGRGmtLayer::CreateField(const OGRFieldDefn *poField, int bApproxOK)
    1015             : 
    1016             : {
    1017          87 :     if (!bUpdate)
    1018             :     {
    1019           0 :         CPLError(CE_Failure, CPLE_NoWriteAccess,
    1020             :                  "Cannot create fields on read-only dataset.");
    1021           0 :         return OGRERR_FAILURE;
    1022             :     }
    1023             : 
    1024          87 :     if (bHeaderComplete)
    1025             :     {
    1026           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1027             :                  "Unable to create fields after features have been created.");
    1028           0 :         return OGRERR_FAILURE;
    1029             :     }
    1030             : 
    1031          87 :     switch (poField->GetType())
    1032             :     {
    1033          71 :         case OFTInteger:
    1034             :         case OFTReal:
    1035             :         case OFTString:
    1036             :         case OFTDateTime:
    1037          71 :             poFeatureDefn->AddFieldDefn(poField);
    1038          71 :             return OGRERR_NONE;
    1039             :             break;
    1040             : 
    1041             :             break;
    1042             : 
    1043          16 :         default:
    1044          16 :             if (!bApproxOK)
    1045             :             {
    1046           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1047             :                          "Field %s is of unsupported type %s.",
    1048             :                          poField->GetNameRef(),
    1049             :                          poField->GetFieldTypeName(poField->GetType()));
    1050           0 :                 return OGRERR_FAILURE;
    1051             :             }
    1052          16 :             else if (poField->GetType() == OFTDate ||
    1053           0 :                      poField->GetType() == OFTTime)
    1054             :             {
    1055          16 :                 OGRFieldDefn oModDef(poField);
    1056          16 :                 oModDef.SetType(OFTDateTime);
    1057          16 :                 poFeatureDefn->AddFieldDefn(poField);
    1058          16 :                 return OGRERR_NONE;
    1059             :             }
    1060             :             else
    1061             :             {
    1062           0 :                 OGRFieldDefn oModDef(poField);
    1063           0 :                 oModDef.SetType(OFTString);
    1064           0 :                 poFeatureDefn->AddFieldDefn(poField);
    1065           0 :                 return OGRERR_NONE;
    1066             :             }
    1067             :     }
    1068             : }

Generated by: LCOV version 1.14