LCOV - code coverage report
Current view: top level - apps - ogrlineref.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 584 868 67.3 %
Date: 2025-01-18 12:42:00 Functions: 12 13 92.3 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  * Project:  ogr linear referencing utility
       3             :  * Purpose:  main source file
       4             :  * Author:   Dmitry Baryshnikov (aka Bishop), polimax@mail.ru
       5             :  *
       6             :  ******************************************************************************
       7             :  * Copyright (C) 2014 NextGIS
       8             :  *
       9             :  * SPDX-License-Identifier: MIT
      10             :  ****************************************************************************/
      11             : 
      12             : #include "cpl_port.h"
      13             : 
      14             : #include "commonutils.h"
      15             : #include "cpl_conv.h"
      16             : #include "cpl_error.h"
      17             : #include "cpl_string.h"
      18             : #include "gdal_version.h"
      19             : #include "gdal.h"
      20             : #include "gdal_alg.h"
      21             : #include "ogr_api.h"
      22             : #include "ogr_p.h"
      23             : #include "ogrsf_frmts.h"
      24             : #include "gdalargumentparser.h"
      25             : 
      26             : #include <limits>
      27             : #include <map>
      28             : #include <set>
      29             : #include <vector>
      30             : 
      31             : #define FIELD_START "beg"
      32             : #define FIELD_FINISH "end"
      33             : #define FIELD_SCALE_FACTOR "scale"
      34             : constexpr double DELTA = 0.00000001;  // - delta
      35             : #ifdef HAVE_GEOS
      36             : constexpr double TOLERANCE_DEGREE = 0.00008983153;
      37             : constexpr double TOLERANCE_METER = 10.0;
      38             : #endif
      39             : 
      40             : enum operation
      41             : {
      42             :     op_unknown = 0,
      43             :     op_create,
      44             :     op_get_pos,
      45             :     op_get_coord,
      46             :     op_get_subline
      47             : };
      48             : 
      49             : typedef struct _curve_data
      50             : {
      51             :     OGRLineString *pPart;
      52             :     double dfBeg, dfEnd, dfFactor;
      53             : 
      54          70 :     bool IsInside(const double &dfDist) const
      55             :     {
      56          70 :         return (dfDist + DELTA >= dfBeg) && (dfDist - DELTA <= dfEnd);
      57             :     }
      58             : } CURVE_DATA;
      59             : 
      60             : /************************************************************************/
      61             : /*                         SetupTargetLayer()                           */
      62             : /************************************************************************/
      63             : 
      64           3 : static OGRLayer *SetupTargetLayer(OGRLayer *poSrcLayer, GDALDataset *poDstDS,
      65             :                                   char **papszLCO, const char *pszNewLayerName,
      66             :                                   const char *pszOutputSepFieldName = nullptr)
      67             : {
      68             :     const CPLString szLayerName =
      69             :         pszNewLayerName == nullptr
      70           6 :             ? CPLGetBasenameSafe(poDstDS->GetDescription())
      71           6 :             : pszNewLayerName;
      72             : 
      73             :     /* -------------------------------------------------------------------- */
      74             :     /*      Get other info.                                                 */
      75             :     /* -------------------------------------------------------------------- */
      76           3 :     OGRFeatureDefn *poSrcFDefn = poSrcLayer->GetLayerDefn();
      77             : 
      78             :     /* -------------------------------------------------------------------- */
      79             :     /*      Find requested geometry fields.                                 */
      80             :     /* -------------------------------------------------------------------- */
      81             : 
      82           3 :     OGRSpatialReference *poOutputSRS = poSrcLayer->GetSpatialRef();
      83             : 
      84             :     /* -------------------------------------------------------------------- */
      85             :     /*      Find the layer.                                                 */
      86             :     /* -------------------------------------------------------------------- */
      87             : 
      88             :     // GetLayerByName() can instantiate layers that would have been
      89             :     // 'hidden' otherwise, for example, non-spatial tables in a
      90             :     // PostGIS-enabled database, so this apparently useless command is
      91             :     // not useless... (#4012)
      92           3 :     CPLPushErrorHandler(CPLQuietErrorHandler);
      93           3 :     OGRLayer *poDstLayer = poDstDS->GetLayerByName(szLayerName);
      94           3 :     CPLPopErrorHandler();
      95           3 :     CPLErrorReset();
      96             : 
      97           3 :     if (poDstLayer != nullptr)
      98             :     {
      99           0 :         const int nLayerCount = poDstDS->GetLayerCount();
     100           0 :         int iLayer = -1;  // Used after for.
     101           0 :         for (iLayer = 0; iLayer < nLayerCount; iLayer++)
     102             :         {
     103           0 :             OGRLayer *poLayer = poDstDS->GetLayer(iLayer);
     104           0 :             if (poLayer == poDstLayer)
     105           0 :                 break;
     106             :         }
     107             : 
     108           0 :         if (iLayer == nLayerCount)
     109             :             // Should not happen with an ideal driver.
     110           0 :             poDstLayer = nullptr;
     111             :     }
     112             : 
     113             :     /* -------------------------------------------------------------------- */
     114             :     /*      If the layer does not exist, then create it.                    */
     115             :     /* -------------------------------------------------------------------- */
     116           3 :     if (poDstLayer == nullptr)
     117             :     {
     118           3 :         if (!poDstDS->TestCapability(ODsCCreateLayer))
     119             :         {
     120           0 :             fprintf(stderr,
     121             :                     _("Layer %s not found, and "
     122             :                       "CreateLayer not supported by driver.\n"),
     123             :                     szLayerName.c_str());
     124           0 :             return nullptr;
     125             :         }
     126             : 
     127           3 :         OGRwkbGeometryType eGType = wkbLineString;
     128             : 
     129           3 :         CPLErrorReset();
     130             : 
     131           3 :         if (poDstDS->TestCapability(ODsCCreateGeomFieldAfterCreateLayer))
     132             :         {
     133           0 :             eGType = wkbNone;
     134             :         }
     135             : 
     136           3 :         poDstLayer = poDstDS->CreateLayer(
     137             :             szLayerName, poOutputSRS, static_cast<OGRwkbGeometryType>(eGType),
     138             :             papszLCO);
     139             : 
     140           3 :         if (poDstLayer == nullptr)
     141           0 :             return nullptr;
     142             : 
     143           3 :         if (poDstDS->TestCapability(ODsCCreateGeomFieldAfterCreateLayer))
     144             :         {
     145           0 :             OGRGeomFieldDefn oGFldDefn(poSrcFDefn->GetGeomFieldDefn(0));
     146           0 :             if (poOutputSRS != nullptr)
     147           0 :                 oGFldDefn.SetSpatialRef(poOutputSRS);
     148           0 :             oGFldDefn.SetType(wkbLineString);
     149           0 :             poDstLayer->CreateGeomField(&oGFldDefn);
     150             :         }
     151             :     }
     152             : 
     153             :     /* -------------------------------------------------------------------- */
     154             :     /*      Otherwise we will append to it, if append was requested.        */
     155             :     /* -------------------------------------------------------------------- */
     156             :     else
     157             :     {
     158           0 :         fprintf(stderr, _("FAILED: Layer %s already exists.\n"),
     159             :                 szLayerName.c_str());
     160           0 :         return nullptr;
     161             :     }
     162             : 
     163             :     // Create beg, end, scale factor fields.
     164           6 :     OGRFieldDefn oFieldDefn_Beg(FIELD_START, OFTReal);
     165           3 :     if (poDstLayer->CreateField(&oFieldDefn_Beg) != OGRERR_NONE)
     166             :     {
     167           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Create %s field failed!",
     168             :                  oFieldDefn_Beg.GetNameRef());
     169           0 :         return nullptr;
     170             :     }
     171             : 
     172           6 :     OGRFieldDefn oFieldDefn_End(FIELD_FINISH, OFTReal);
     173           3 :     if (poDstLayer->CreateField(&oFieldDefn_End) != OGRERR_NONE)
     174             :     {
     175           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Create %s field failed!",
     176             :                  oFieldDefn_End.GetNameRef());
     177           0 :         return nullptr;
     178             :     }
     179             : 
     180           6 :     OGRFieldDefn oFieldDefn_SF(FIELD_SCALE_FACTOR, OFTReal);
     181           3 :     if (poDstLayer->CreateField(&oFieldDefn_SF) != OGRERR_NONE)
     182             :     {
     183           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Create %s field failed!",
     184             :                  oFieldDefn_SF.GetNameRef());
     185           0 :         return nullptr;
     186             :     }
     187             : 
     188           3 :     if (pszOutputSepFieldName != nullptr)
     189             :     {
     190           0 :         OGRFieldDefn oSepField(pszOutputSepFieldName, OFTString);
     191           0 :         oSepField.SetWidth(254);
     192           0 :         if (poDstLayer->CreateField(&oSepField) != OGRERR_NONE)
     193             :         {
     194           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Create %s field failed!",
     195             :                      oSepField.GetNameRef());
     196           0 :             return nullptr;
     197             :         }
     198             :     }
     199             : 
     200             :     // Now that we've created a field, GetLayerDefn() won't return NULL.
     201           3 :     OGRFeatureDefn *poDstFDefn = poDstLayer->GetLayerDefn();
     202             : 
     203             :     // Sanity check: if it fails, the driver is buggy.
     204           3 :     if (poDstFDefn != nullptr && poDstFDefn->GetFieldCount() != 3)
     205             :     {
     206           1 :         CPLError(CE_Warning, CPLE_AppDefined,
     207             :                  "The output driver has claimed to have added the %s field, "
     208             :                  "but it did not!",
     209             :                  oFieldDefn_Beg.GetNameRef());
     210             :     }
     211             : 
     212           3 :     return poDstLayer;
     213             : }
     214             : 
     215             : /* -------------------------------------------------------------------- */
     216             : /*                  CheckDestDataSourceNameConsistency()                */
     217             : /* -------------------------------------------------------------------- */
     218             : 
     219           3 : static void CheckDestDataSourceNameConsistency(const char *pszDestFilename,
     220             :                                                const char *pszDriverName)
     221             : {
     222             :     char *pszDestExtension =
     223           3 :         CPLStrdup(CPLGetExtensionSafe(pszDestFilename).c_str());
     224             : 
     225             :     // TODO: Would be good to have driver metadata like for GDAL drivers.
     226             :     static const char *apszExtensions[][2] = {{"shp", "ESRI Shapefile"},
     227             :                                               {"dbf", "ESRI Shapefile"},
     228             :                                               {"sqlite", "SQLite"},
     229             :                                               {"db", "SQLite"},
     230             :                                               {"mif", "MapInfo File"},
     231             :                                               {"tab", "MapInfo File"},
     232             :                                               {"s57", "S57"},
     233             :                                               {"bna", "BNA"},
     234             :                                               {"csv", "CSV"},
     235             :                                               {"gml", "GML"},
     236             :                                               {"kml", "KML"},
     237             :                                               {"kmz", "LIBKML"},
     238             :                                               {"json", "GeoJSON"},
     239             :                                               {"geojson", "GeoJSON"},
     240             :                                               {"dxf", "DXF"},
     241             :                                               {"gdb", "FileGDB"},
     242             :                                               {"pix", "PCIDSK"},
     243             :                                               {"sql", "PGDump"},
     244             :                                               {"gtm", "GPSTrackMaker"},
     245             :                                               {"gmt", "GMT"},
     246             :                                               {"pdf", "PDF"},
     247             :                                               {nullptr, nullptr}};
     248             :     static const char *apszBeginName[][2] = {{"PG:", "PG"},
     249             :                                              {"MySQL:", "MySQL"},
     250             :                                              {"CouchDB:", "CouchDB"},
     251             :                                              {"GFT:", "GFT"},
     252             :                                              {"MSSQL:", "MSSQLSpatial"},
     253             :                                              {"ODBC:", "ODBC"},
     254             :                                              {"OCI:", "OCI"},
     255             :                                              {"SDE:", "SDE"},
     256             :                                              {"WFS:", "WFS"},
     257             :                                              {nullptr, nullptr}};
     258             : 
     259          66 :     for (int i = 0; apszExtensions[i][0] != nullptr; i++)
     260             :     {
     261          63 :         if (EQUAL(pszDestExtension, apszExtensions[i][0]) &&
     262           3 :             !EQUAL(pszDriverName, apszExtensions[i][1]))
     263             :         {
     264           0 :             fprintf(stderr,
     265             :                     _("Warning: The target file has a '%s' extension, "
     266             :                       "which is normally used by the %s driver,\n"
     267             :                       "but the requested output driver is %s. "
     268             :                       "Is it really what you want ?\n"),
     269             :                     pszDestExtension, apszExtensions[i][1], pszDriverName);
     270           0 :             break;
     271             :         }
     272             :     }
     273             : 
     274          30 :     for (int i = 0; apszBeginName[i][0] != nullptr; i++)
     275             :     {
     276          27 :         if (EQUALN(pszDestFilename, apszBeginName[i][0],
     277           0 :                    strlen(apszBeginName[i][0])) &&
     278           0 :             !EQUAL(pszDriverName, apszBeginName[i][1]))
     279             :         {
     280           0 :             fprintf(stderr,
     281             :                     _("Warning: The target file has a name which is normally "
     282             :                       "recognized by the %s driver,\n"
     283             :                       "but the requested output driver is %s. "
     284             :                       "Is it really what you want ?\n"),
     285             :                     apszBeginName[i][1], pszDriverName);
     286           0 :             break;
     287             :         }
     288             :     }
     289             : 
     290           3 :     CPLFree(pszDestExtension);
     291           3 : }
     292             : 
     293             : //------------------------------------------------------------------------
     294             : // AddFeature
     295             : //------------------------------------------------------------------------
     296             : 
     297          44 : static OGRErr AddFeature(OGRLayer *const poOutLayer, OGRLineString *pPart,
     298             :                          double dfFrom, double dfTo, double dfScaleFactor,
     299             :                          bool bQuiet,
     300             :                          const char *pszOutputSepFieldName = nullptr,
     301             :                          const char *pszOutputSepFieldValue = nullptr)
     302             : {
     303             :     OGRFeature *poFeature =
     304          44 :         OGRFeature::CreateFeature(poOutLayer->GetLayerDefn());
     305             : 
     306          44 :     poFeature->SetField(FIELD_START, dfFrom);
     307          44 :     poFeature->SetField(FIELD_FINISH, dfTo);
     308          44 :     poFeature->SetField(FIELD_SCALE_FACTOR, dfScaleFactor);
     309             : 
     310          44 :     if (pszOutputSepFieldName != nullptr)
     311             :     {
     312           0 :         poFeature->SetField(pszOutputSepFieldName, pszOutputSepFieldValue);
     313             :     }
     314             : 
     315          44 :     poFeature->SetGeometryDirectly(pPart);
     316             : 
     317          44 :     if (poOutLayer->CreateFeature(poFeature) != OGRERR_NONE)
     318             :     {
     319           0 :         if (!bQuiet)
     320           0 :             printf("Failed to create feature in shapefile.\n");
     321           0 :         return OGRERR_FAILURE;
     322             :     }
     323             : 
     324          44 :     OGRFeature::DestroyFeature(poFeature);
     325             : 
     326          44 :     return OGRERR_NONE;
     327             : }
     328             : 
     329             : //------------------------------------------------------------------------
     330             : // CreateSubline
     331             : //------------------------------------------------------------------------
     332           1 : static OGRErr CreateSubline(OGRLayer *const poPkLayer, double dfPosBeg,
     333             :                             double dfPosEnd, OGRLayer *const poOutLayer,
     334             :                             CPL_UNUSED int bDisplayProgress, bool bQuiet)
     335             : {
     336             :     // Get step
     337           1 :     poPkLayer->ResetReading();
     338           1 :     OGRFeature *pFeature = poPkLayer->GetNextFeature();
     339           1 :     if (nullptr != pFeature)
     340             :     {
     341             :         // FIXME: Clang Static Analyzer rightly found that the following
     342             :         // code is dead
     343             :         // dfBeg = pFeature->GetFieldAsDouble(FIELD_START);
     344             :         // dfEnd = pFeature->GetFieldAsDouble(FIELD_FINISH);
     345           1 :         OGRFeature::DestroyFeature(pFeature);
     346             :     }
     347             :     else
     348             :     {
     349           0 :         fprintf(stderr, _("Get step for positions %f - %f failed\n"), dfPosBeg,
     350             :                 dfPosEnd);
     351           0 :         return OGRERR_FAILURE;
     352             :     }
     353             :     // Get second part.
     354           1 :     double dfBeg = 0.0;
     355           1 :     double dfEnd = 0.0;
     356           1 :     pFeature = poPkLayer->GetNextFeature();
     357           1 :     if (nullptr != pFeature)
     358             :     {
     359           1 :         dfBeg = pFeature->GetFieldAsDouble(FIELD_START);
     360           1 :         dfEnd = pFeature->GetFieldAsDouble(FIELD_FINISH);
     361           1 :         OGRFeature::DestroyFeature(pFeature);
     362             :     }
     363             :     else
     364             :     {
     365           0 :         fprintf(stderr, _("Get step for positions %f - %f failed\n"), dfPosBeg,
     366             :                 dfPosEnd);
     367           0 :         return OGRERR_FAILURE;
     368             :     }
     369           1 :     const double dfStep = dfEnd - dfBeg;
     370             : 
     371             :     // Round input to step
     372           1 :     const double dfPosBegLow = floor(dfPosBeg / dfStep) * dfStep;
     373           1 :     const double dfPosEndHigh = ceil(dfPosEnd / dfStep) * dfStep;
     374             : 
     375           2 :     CPLString szAttributeFilter;
     376             :     szAttributeFilter.Printf("%s >= %f AND %s <= %f", FIELD_START, dfPosBegLow,
     377           1 :                              FIELD_FINISH, dfPosEndHigh);
     378             :     // TODO: ExecuteSQL should be faster.
     379           1 :     poPkLayer->SetAttributeFilter(szAttributeFilter);
     380           1 :     poPkLayer->ResetReading();
     381             : 
     382           2 :     std::map<double, OGRFeature *> moParts;
     383             : 
     384           6 :     while ((pFeature = poPkLayer->GetNextFeature()) != nullptr)
     385             :     {
     386           5 :         double dfStart = pFeature->GetFieldAsDouble(FIELD_START);
     387           5 :         moParts[dfStart] = pFeature;
     388             :     }
     389             : 
     390           1 :     if (moParts.empty())
     391             :     {
     392           0 :         fprintf(stderr, _("Get parts for positions %f - %f failed\n"), dfPosBeg,
     393             :                 dfPosEnd);
     394           0 :         return OGRERR_FAILURE;
     395             :     }
     396             : 
     397           2 :     OGRLineString SubLine;
     398           1 :     if (moParts.size() == 1)
     399             :     {
     400           0 :         std::map<double, OGRFeature *>::iterator IT = moParts.begin();
     401           0 :         const double dfStart = IT->first;
     402           0 :         double dfPosBegCorr = dfPosBeg - dfStart;
     403           0 :         const double dfSF = IT->second->GetFieldAsDouble(FIELD_SCALE_FACTOR);
     404           0 :         dfPosBegCorr *= dfSF;
     405             : 
     406           0 :         const double dfPosEndCorr = (dfPosEnd - dfStart) * dfSF;
     407             : 
     408           0 :         OGRLineString *pLine = IT->second->GetGeometryRef()->toLineString();
     409             : 
     410             :         OGRLineString *pSubLine =
     411           0 :             pLine->getSubLine(dfPosBegCorr, dfPosEndCorr, FALSE);
     412             : 
     413           0 :         OGRFeature::DestroyFeature(IT->second);
     414             :         // Store.
     415           0 :         return AddFeature(poOutLayer, pSubLine, dfPosBeg, dfPosEnd, 1.0,
     416           0 :                           bQuiet);
     417             :     }
     418             :     else
     419             :     {
     420           1 :         int nCounter = static_cast<int>(moParts.size());
     421           1 :         std::map<double, OGRFeature *>::iterator IT = moParts.begin();
     422           1 :         OGRLineString *pOutLine = new OGRLineString();
     423             :         // Get first part.
     424           1 :         const double dfStart = IT->first;
     425           1 :         double dfPosBegCorr = dfPosBeg - dfStart;
     426           1 :         double dfSF = IT->second->GetFieldAsDouble(FIELD_SCALE_FACTOR);
     427           1 :         dfPosBegCorr *= dfSF;
     428             : 
     429           1 :         OGRLineString *pLine = IT->second->GetGeometryRef()->toLineString();
     430             : 
     431             :         OGRLineString *pSubLine =
     432           1 :             pLine->getSubLine(dfPosBegCorr, pLine->get_Length(), FALSE);
     433             : 
     434           1 :         pOutLine->addSubLineString(pSubLine);
     435           1 :         delete pSubLine;
     436           1 :         OGRFeature::DestroyFeature(IT->second);
     437             : 
     438           1 :         ++IT;
     439           1 :         nCounter--;
     440             : 
     441           4 :         while (nCounter > 1)
     442             :         {
     443           3 :             pLine = IT->second->GetGeometryRef()->toLineString();
     444           3 :             pOutLine->addSubLineString(pLine);
     445           3 :             OGRFeature::DestroyFeature(IT->second);
     446           3 :             ++IT;
     447           3 :             nCounter--;
     448             :         }
     449             : 
     450             :         // Get last part
     451           1 :         double dfPosEndCorr = dfPosEnd - IT->first;
     452           1 :         dfSF = IT->second->GetFieldAsDouble(FIELD_SCALE_FACTOR);
     453           1 :         dfPosEndCorr *= dfSF;
     454             : 
     455           1 :         pLine = IT->second->GetGeometryRef()->toLineString();
     456             : 
     457           1 :         pSubLine = pLine->getSubLine(0, dfPosEndCorr, FALSE);
     458             : 
     459           1 :         pOutLine->addSubLineString(pSubLine);
     460           1 :         delete pSubLine;
     461             : 
     462           1 :         OGRFeature::DestroyFeature(IT->second);
     463             :         // Store
     464           1 :         return AddFeature(poOutLayer, pOutLine, dfPosBeg, dfPosEnd, 1.0,
     465           1 :                           bQuiet);
     466             :     }
     467             : }
     468             : 
     469             : //------------------------------------------------------------------------
     470             : // Project
     471             : //------------------------------------------------------------------------
     472             : #ifdef HAVE_GEOS
     473          66 : static double Project(OGRLineString *pLine, OGRPoint *pPoint)
     474             : {
     475          66 :     if (nullptr == pLine || nullptr == pPoint)
     476           0 :         return -1;
     477         132 :     OGRPoint TestPoint;
     478          66 :     pLine->StartPoint(&TestPoint);
     479          66 :     if (TestPoint.Equals(pPoint))
     480           2 :         return 0.0;
     481          64 :     pLine->EndPoint(&TestPoint);
     482          64 :     if (TestPoint.Equals(pPoint))
     483           2 :         return pLine->get_Length();
     484             : 
     485          62 :     return pLine->Project(pPoint);
     486             : }
     487             : #endif
     488             : 
     489             : //------------------------------------------------------------------------
     490             : // CreatePartsFromLineString
     491             : //------------------------------------------------------------------------
     492             : #ifdef HAVE_GEOS
     493           2 : static OGRErr CreatePartsFromLineString(
     494             :     OGRLineString *pPathGeom, OGRLayer *const poPkLayer, int nMValField,
     495             :     double dfStep, OGRLayer *const poOutLayer, int bDisplayProgress,
     496             :     bool bQuiet, const char *pszOutputSepFieldName = nullptr,
     497             :     const char *pszOutputSepFieldValue = nullptr)
     498             : {
     499             :     // Check repers/milestones/reference points type
     500           2 :     OGRwkbGeometryType eGeomType = poPkLayer->GetGeomType();
     501           2 :     if (wkbFlatten(eGeomType) != wkbPoint)
     502             :     {
     503           0 :         fprintf(stderr, _("Unsupported geometry type %s for path\n"),
     504             :                 OGRGeometryTypeToName(eGeomType));
     505           0 :         return OGRERR_FAILURE;
     506             :     }
     507             : 
     508           2 :     double dfTolerance = 1.0;
     509           2 :     const OGRSpatialReference *pSpaRef = pPathGeom->getSpatialReference();
     510           2 :     if (pSpaRef->IsGeographic())
     511             :     {
     512           2 :         dfTolerance = TOLERANCE_DEGREE;
     513             :     }
     514             :     else
     515             :     {
     516           0 :         dfTolerance = TOLERANCE_METER;
     517             :     }
     518             : 
     519             :     // Create sorted list of repers.
     520           4 :     std::map<double, OGRPoint *> moRepers;
     521           2 :     poPkLayer->ResetReading();
     522           2 :     OGRFeature *pReperFeature = nullptr;
     523          16 :     while ((pReperFeature = poPkLayer->GetNextFeature()) != nullptr)
     524             :     {
     525          14 :         const double dfReperPos = pReperFeature->GetFieldAsDouble(nMValField);
     526          14 :         OGRGeometry *pGeom = pReperFeature->GetGeometryRef();
     527          14 :         if (nullptr != pGeom)
     528             :         {
     529          14 :             OGRPoint *pPt = pGeom->clone()->toPoint();
     530          14 :             if (!bQuiet)
     531             :             {
     532          14 :                 if (moRepers.find(dfReperPos) != moRepers.end())
     533             :                 {
     534           0 :                     CPLError(
     535             :                         CE_Warning, CPLE_AppDefined,
     536             :                         "The distance %f is already present in repers file!",
     537             :                         dfReperPos);
     538             :                 }
     539             :             }
     540             :             // Check if reper is inside the path
     541          14 :             const double dfTestDistance = Project(pPathGeom, pPt);
     542          14 :             if (dfTestDistance < 0)
     543             :             {
     544           0 :                 if (!bQuiet)
     545             :                 {
     546           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
     547             :                              "The distance %f is out of path!", dfReperPos);
     548             :                 }
     549           0 :                 delete pPt;
     550             :             }
     551             :             else
     552             :             {
     553          14 :                 const double dfDist = pPathGeom->Distance(pPt);
     554          14 :                 if (dfDist < dfTolerance)
     555           6 :                     moRepers[dfReperPos] = pPt;
     556             :                 else
     557           8 :                     delete pPt;
     558             :             }
     559             :         }
     560          14 :         OGRFeature::DestroyFeature(pReperFeature);
     561             :     }
     562             : 
     563           2 :     if (moRepers.size() < 2)
     564             :     {
     565           0 :         fprintf(stderr, _("Not enough repers to proceed.\n"));
     566           0 :         return OGRERR_FAILURE;
     567             :     }
     568             : 
     569             :     // Check direction.
     570           2 :     if (!bQuiet)
     571             :     {
     572           2 :         fprintf(stdout, "Check path direction.\n");
     573             :     }
     574             : 
     575             :     // Get distance along path from pt1 and pt2.
     576             :     // If pt1 distance > pt2 distance, reverse path
     577           2 :     std::map<double, OGRPoint *>::const_iterator IT;
     578           2 :     IT = moRepers.begin();
     579           2 :     double dfPosition = IT->first;
     580           2 :     double dfBeginPosition = IT->first;
     581           2 :     OGRPoint *pt1 = IT->second;
     582           2 :     ++IT;
     583           2 :     OGRPoint *pt2 = IT->second;
     584             : 
     585           2 :     double dfDistance1 = Project(pPathGeom, pt1);
     586           2 :     double dfDistance2 = Project(pPathGeom, pt2);
     587             : 
     588           2 :     if (dfDistance1 > dfDistance2)
     589             :     {
     590           0 :         if (!bQuiet)
     591             :         {
     592           0 :             fprintf(stderr,
     593             :                     _("Warning: The path is opposite the repers direction. "
     594             :                       "Let's reverse path.\n"));
     595             :         }
     596           0 :         pPathGeom->reversePoints();
     597             : 
     598           0 :         dfDistance1 = Project(pPathGeom, pt1);
     599           0 :         dfDistance2 = Project(pPathGeom, pt2);
     600             :     }
     601             : 
     602           2 :     OGRLineString *pPart = nullptr;
     603             : 
     604           2 :     std::vector<CURVE_DATA> astSubLines;
     605             : 
     606           2 :     if (!bQuiet)
     607             :     {
     608           2 :         fprintf(stdout, "Create parts\n");
     609             :     }
     610             : 
     611             :     // Get first part
     612             :     // If first point is not at the beginning of the path
     613             :     // The first part should be from the beginning of the path
     614             :     // to the first point. length == part.getLength
     615           2 :     OGRPoint *pPtBeg = nullptr;
     616           2 :     OGRPoint *pPtEnd = nullptr;
     617           2 :     double dfPtBegPosition = 0.0;
     618           2 :     double dfPtEndPosition = 0.0;
     619             : 
     620           2 :     if (dfDistance1 > DELTA)
     621             :     {
     622           2 :         pPart = pPathGeom->getSubLine(0, dfDistance1, FALSE);
     623           2 :         if (nullptr != pPart)
     624             :         {
     625           2 :             double dfLen = pPart->get_Length();
     626           2 :             if (pSpaRef->IsGeographic())
     627             :             {
     628             :                 // convert to UTM/WGS84
     629           4 :                 OGRPoint pt;
     630           2 :                 pPart->Value(dfLen / 2, &pt);
     631             :                 const int nZoneEnv =
     632           2 :                     static_cast<int>(30 + (pt.getX() + 3.0) / 6.0 + 0.5);
     633             :                 int nEPSG;
     634           2 :                 if (pt.getY() > 0)
     635             :                 {
     636           2 :                     nEPSG = 32600 + nZoneEnv;
     637             :                 }
     638             :                 else
     639             :                 {
     640           0 :                     nEPSG = 32700 + nZoneEnv;
     641             :                 }
     642           4 :                 OGRSpatialReference SpatRef;
     643           2 :                 SpatRef.importFromEPSG(nEPSG);
     644           2 :                 OGRGeometry *pTransformPart = pPart->clone();
     645           2 :                 if (pTransformPart->transformTo(&SpatRef) == OGRERR_NONE)
     646             :                 {
     647             :                     OGRLineString *pTransformPartLS =
     648           2 :                         pTransformPart->toLineString();
     649           2 :                     dfLen = pTransformPartLS->get_Length();
     650             :                 }
     651             : 
     652           2 :                 CURVE_DATA data = {pPart, dfPosition - dfLen, dfPosition,
     653           2 :                                    pPart->get_Length() / dfLen};
     654           2 :                 astSubLines.push_back(data);
     655             : 
     656           2 :                 pPtBeg = new OGRPoint();
     657           2 :                 pPart->getPoint(0, pPtBeg);
     658           2 :                 dfPtBegPosition = dfPosition - dfLen;
     659             : 
     660             :                 // AddFeature(poOutLayer, pPart, dfPosition - dfLen, dfPosition,
     661             :                 //            pPart->get_Length() / dfLen, bQuiet);
     662           2 :                 delete pTransformPart;
     663             :             }
     664             :             else
     665             :             {
     666           0 :                 CURVE_DATA data = {pPart, dfPosition - dfLen, dfPosition, 1.0};
     667           0 :                 astSubLines.push_back(data);
     668             :                 // AddFeature(poOutLayer, pPart, dfPosition - dfLen, dfPosition,
     669             :                 //            1.0, bQuiet);
     670           0 :                 pPtBeg = new OGRPoint();
     671           0 :                 pPart->getPoint(0, pPtBeg);
     672           0 :                 dfPtBegPosition = dfPosition - dfLen;
     673             :             }
     674             :         }
     675             :     }
     676             : 
     677           2 :     if (dfDistance2 - dfDistance1 > DELTA)
     678             :     {
     679           2 :         pPart = pPathGeom->getSubLine(dfDistance1, dfDistance2, FALSE);
     680           2 :         if (nullptr != pPart)
     681             :         {
     682           4 :             CURVE_DATA data = {pPart, dfPosition, IT->first,
     683           2 :                                pPart->get_Length() / (IT->first - dfPosition)};
     684           2 :             astSubLines.push_back(data);
     685             :             // AddFeature(poOutLayer, pPart, dfPosition, IT->first,
     686             :             //            pPart->get_Length() / (IT->first - dfPosition),
     687             :             //            bQuiet);
     688             :         }
     689             :     }
     690             : 
     691           2 :     GDALProgressFunc pfnProgress = nullptr;
     692           2 :     void *pProgressArg = nullptr;
     693             : 
     694           2 :     double dfFactor = 1.0 / moRepers.size();
     695           2 :     if (bDisplayProgress)
     696             :     {
     697           0 :         pfnProgress = GDALScaledProgress;
     698             :         pProgressArg =
     699           0 :             GDALCreateScaledProgress(0.0, 1.0, GDALTermProgress, nullptr);
     700             :     }
     701             : 
     702           2 :     int nCount = 2;
     703           2 :     dfDistance1 = dfDistance2;
     704           2 :     dfPosition = IT->first;
     705           2 :     ++IT;  // Get third point
     706             : 
     707           2 :     double dfEndPosition = 0.0;
     708           4 :     while (IT != moRepers.end())
     709             :     {
     710           2 :         if (bDisplayProgress)
     711             :         {
     712           0 :             pfnProgress(nCount * dfFactor, "", pProgressArg);
     713           0 :             nCount++;
     714             :         }
     715             : 
     716           2 :         dfEndPosition = IT->first;
     717             : 
     718           2 :         dfDistance2 = Project(pPathGeom, IT->second);
     719             : 
     720           2 :         if (dfDistance2 - dfDistance1 > DELTA)
     721             :         {
     722           2 :             pPart = pPathGeom->getSubLine(dfDistance1, dfDistance2, FALSE);
     723           2 :             if (nullptr != pPart)
     724             :             {
     725           4 :                 CURVE_DATA data = {pPart, dfPosition, IT->first,
     726           6 :                                    pPart->get_Length() /
     727           2 :                                        (IT->first - dfPosition)};
     728           2 :                 astSubLines.push_back(data);
     729             :                 // AddFeature(poOutLayer, pPart, dfPosition, IT->first,
     730             :                 //            pPart->get_Length() / (IT->first - dfPosition),
     731             :                 //            bQuiet);
     732           2 :                 dfDistance1 = dfDistance2;
     733           2 :                 dfPosition = IT->first;
     734             :             }
     735             :         }
     736             : 
     737           2 :         ++IT;
     738             :     }
     739             : 
     740             :     // Get last part
     741           2 :     if (pPathGeom->get_Length() - dfDistance1 > DELTA)
     742             :     {
     743             :         pPart =
     744           2 :             pPathGeom->getSubLine(dfDistance1, pPathGeom->get_Length(), FALSE);
     745           2 :         if (nullptr != pPart)
     746             :         {
     747           2 :             double dfLen = pPart->get_Length();
     748           2 :             if (pSpaRef->IsGeographic())
     749             :             {
     750             :                 // convert to UTM/WGS84
     751           4 :                 OGRPoint pt;
     752           2 :                 pPart->Value(dfLen / 2, &pt);
     753             :                 const int nZoneEnv =
     754           2 :                     static_cast<int>(30 + (pt.getX() + 3.0) / 6.0 + 0.5);
     755             :                 int nEPSG;
     756           2 :                 if (pt.getY() > 0)
     757             :                 {
     758           2 :                     nEPSG = 32600 + nZoneEnv;
     759             :                 }
     760             :                 else
     761             :                 {
     762           0 :                     nEPSG = 32700 + nZoneEnv;
     763             :                 }
     764           4 :                 OGRSpatialReference SpatRef;
     765           2 :                 SpatRef.importFromEPSG(nEPSG);
     766           2 :                 OGRGeometry *pTransformPart = pPart->clone();
     767           2 :                 if (pTransformPart->transformTo(&SpatRef) == OGRERR_NONE)
     768             :                 {
     769             :                     OGRLineString *pTransformPartLS =
     770           2 :                         pTransformPart->toLineString();
     771           2 :                     dfLen = pTransformPartLS->get_Length();
     772             :                 }
     773           2 :                 CURVE_DATA data = {pPart, dfPosition, dfPosition + dfLen,
     774           2 :                                    pPart->get_Length() / dfLen};
     775           2 :                 astSubLines.push_back(data);
     776             :                 // AddFeature(poOutLayer, pPart, dfPosition, dfPosition + dfLen,
     777             :                 //            pPart->get_Length() / dfLen, bQuiet);
     778             : 
     779           2 :                 pPtEnd = new OGRPoint();
     780           2 :                 pPart->getPoint(pPart->getNumPoints() - 1, pPtEnd);
     781           2 :                 dfPtEndPosition = dfPosition + dfLen;
     782             : 
     783           2 :                 delete pTransformPart;
     784             :             }
     785             :             else
     786             :             {
     787           0 :                 CURVE_DATA data = {pPart, dfPosition, dfPosition + dfLen, 1.0};
     788           0 :                 astSubLines.push_back(data);
     789             :                 // AddFeature(poOutLayer, pPart, dfPosition - dfLen, dfPosition,
     790             :                 //            1.0, bQuiet);
     791           0 :                 pPtEnd = new OGRPoint();
     792           0 :                 pPart->getPoint(pPart->getNumPoints() - 1, pPtEnd);
     793           0 :                 dfPtEndPosition = dfPosition + dfLen;
     794             :             }
     795             :         }
     796             :     }
     797             : 
     798             :     // Create pickets
     799           2 :     if (!bQuiet)
     800             :     {
     801           2 :         fprintf(stdout, "\nCreate pickets.\n");
     802             :     }
     803             : 
     804           2 :     const double dfRoundBeg = pPtBeg != nullptr
     805           2 :                                   ? ceil(dfPtBegPosition / dfStep) * dfStep
     806           0 :                                   : ceil(dfBeginPosition / dfStep) * dfStep;
     807             : 
     808           2 :     if (pPtEnd != nullptr)
     809           2 :         dfEndPosition = dfPtEndPosition;
     810             : 
     811           2 :     dfFactor = dfStep / (dfEndPosition - dfRoundBeg);
     812           2 :     nCount = 0;
     813           8 :     for (std::map<double, OGRPoint *>::iterator oIter = moRepers.begin();
     814          14 :          oIter != moRepers.end(); ++oIter)
     815             :     {
     816           6 :         delete oIter->second;
     817             :     }
     818             : 
     819           2 :     moRepers.clear();
     820             : 
     821           2 :     if (pPtBeg != nullptr)
     822           2 :         moRepers[dfPtBegPosition] = pPtBeg;
     823           2 :     if (pPtEnd != nullptr)
     824           2 :         moRepers[dfPtEndPosition] = pPtEnd;
     825             : 
     826          43 :     for (double dfDist = dfRoundBeg; dfDist <= dfEndPosition; dfDist += dfStep)
     827             :     {
     828          41 :         if (bDisplayProgress)
     829             :         {
     830           0 :             pfnProgress(nCount * dfFactor, "", pProgressArg);
     831           0 :             nCount++;
     832             :         }
     833             : 
     834          70 :         for (int j = 0; j < static_cast<int>(astSubLines.size()); j++)
     835             :         {
     836          70 :             if (astSubLines[j].IsInside(dfDist))
     837             :             {
     838             :                 const double dfRealDist =
     839          41 :                     (dfDist - astSubLines[j].dfBeg) * astSubLines[j].dfFactor;
     840          41 :                 OGRPoint *pReperPoint = new OGRPoint();
     841          41 :                 astSubLines[j].pPart->Value(dfRealDist, pReperPoint);
     842             : 
     843          41 :                 moRepers[dfDist] = pReperPoint;
     844          41 :                 break;
     845             :             }
     846             :         }
     847             :     }
     848             : 
     849          10 :     for (int i = 0; i < static_cast<int>(astSubLines.size()); i++)
     850             :     {
     851           8 :         delete astSubLines[i].pPart;
     852             :     }
     853           2 :     astSubLines.clear();
     854             : 
     855           2 :     if (!bQuiet)
     856             :     {
     857           2 :         fprintf(stdout, "\nCreate sublines.\n");
     858             :     }
     859             : 
     860           2 :     IT = moRepers.begin();
     861           2 :     dfFactor = 1.0 / moRepers.size();
     862           2 :     nCount = 0;
     863           2 :     dfDistance1 = 0;
     864           2 :     dfPosition = IT->first;
     865             : 
     866          47 :     while (IT != moRepers.end())
     867             :     {
     868          45 :         if (bDisplayProgress)
     869             :         {
     870           0 :             pfnProgress(nCount * dfFactor, "", pProgressArg);
     871           0 :             nCount++;
     872             :         }
     873             : 
     874          45 :         dfDistance2 = Project(pPathGeom, IT->second);
     875             : 
     876          45 :         if (dfDistance2 - dfDistance1 > DELTA)
     877             :         {
     878          43 :             pPart = pPathGeom->getSubLine(dfDistance1, dfDistance2, FALSE);
     879          43 :             if (nullptr != pPart)
     880             :             {
     881          43 :                 AddFeature(poOutLayer, pPart, dfPosition, IT->first,
     882          43 :                            pPart->get_Length() / (IT->first - dfPosition),
     883             :                            bQuiet, pszOutputSepFieldName,
     884             :                            pszOutputSepFieldValue);
     885          43 :                 dfDistance1 = dfDistance2;
     886          43 :                 dfPosition = IT->first;
     887             :             }
     888             :         }
     889             : 
     890          45 :         ++IT;
     891             :     }
     892             : 
     893          47 :     for (std::map<double, OGRPoint *>::iterator oIter = moRepers.begin();
     894          92 :          oIter != moRepers.end(); ++oIter)
     895             :     {
     896          45 :         delete oIter->second;
     897             :     }
     898             : 
     899           2 :     if (!bQuiet)
     900             :     {
     901           2 :         fprintf(stdout, "\nSuccess!\n\n");
     902             :     }
     903             : 
     904           2 :     if (nullptr != pProgressArg)
     905             :     {
     906           0 :         GDALDestroyScaledProgress(pProgressArg);
     907             :     }
     908             : 
     909           2 :     return OGRERR_NONE;
     910             : }
     911             : #endif
     912             : 
     913             : //------------------------------------------------------------------------
     914             : // CreateParts
     915             : //------------------------------------------------------------------------
     916             : #ifdef HAVE_GEOS
     917           2 : static OGRErr CreateParts(OGRLayer *const poLnLayer, OGRLayer *const poPkLayer,
     918             :                           int nMValField, double dfStep,
     919             :                           OGRLayer *const poOutLayer, int bDisplayProgress,
     920             :                           bool bQuiet,
     921             :                           const char *pszOutputSepFieldName = nullptr,
     922             :                           const char *pszOutputSepFieldValue = nullptr)
     923             : {
     924           2 :     OGRErr eRetCode = OGRERR_FAILURE;
     925             : 
     926             :     // check path and get first line
     927           2 :     OGRwkbGeometryType eGeomType = poLnLayer->GetGeomType();
     928           2 :     if (wkbFlatten(eGeomType) != wkbLineString &&
     929           0 :         wkbFlatten(eGeomType) != wkbMultiLineString)
     930             :     {
     931           0 :         fprintf(stderr, _("Unsupported geometry type %s for path.\n"),
     932             :                 OGRGeometryTypeToName(eGeomType));
     933           0 :         return eRetCode;
     934             :     }
     935             : 
     936           2 :     poLnLayer->ResetReading();
     937             :     // Get first geometry
     938             :     // TODO: Attribute filter for path geometry.
     939           2 :     OGRFeature *pPathFeature = poLnLayer->GetNextFeature();
     940           2 :     if (nullptr != pPathFeature)
     941             :     {
     942           2 :         OGRGeometry *pGeom = pPathFeature->GetGeometryRef();
     943             : 
     944           4 :         if (pGeom != nullptr &&
     945           2 :             wkbFlatten(pGeom->getGeometryType()) == wkbMultiLineString)
     946             :         {
     947           0 :             if (!bQuiet)
     948             :             {
     949           0 :                 fprintf(stdout,
     950             :                         _("\nThe geometry " CPL_FRMT_GIB
     951             :                           " is wkbMultiLineString type.\n"),
     952             :                         pPathFeature->GetFID());
     953             :             }
     954             : 
     955           0 :             OGRGeometryCollection *pGeomColl = pGeom->toGeometryCollection();
     956           0 :             for (int i = 0; i < pGeomColl->getNumGeometries(); ++i)
     957             :             {
     958             :                 OGRLineString *pPath =
     959           0 :                     pGeomColl->getGeometryRef(i)->clone()->toLineString();
     960           0 :                 pPath->assignSpatialReference(pGeomColl->getSpatialReference());
     961           0 :                 eRetCode = CreatePartsFromLineString(
     962             :                     pPath, poPkLayer, nMValField, dfStep, poOutLayer,
     963             :                     bDisplayProgress, bQuiet, pszOutputSepFieldName,
     964             :                     pszOutputSepFieldValue);
     965             : 
     966           0 :                 if (eRetCode != OGRERR_NONE)
     967             :                 {
     968           0 :                     OGRFeature::DestroyFeature(pPathFeature);
     969           0 :                     return eRetCode;
     970             :                 }
     971             :             }
     972             :         }
     973           4 :         else if (pGeom != nullptr &&
     974           2 :                  wkbFlatten(pGeom->getGeometryType()) == wkbLineString)
     975             :         {
     976           2 :             OGRLineString *pGeomClone = pGeom->clone()->toLineString();
     977           2 :             eRetCode = CreatePartsFromLineString(
     978             :                 pGeomClone, poPkLayer, nMValField, dfStep, poOutLayer,
     979             :                 bDisplayProgress, bQuiet, pszOutputSepFieldName,
     980             :                 pszOutputSepFieldValue);
     981           2 :             delete pGeomClone;
     982             :         }
     983             : 
     984           2 :         OGRFeature::DestroyFeature(pPathFeature);
     985             :     }
     986             : 
     987             :     // Should never reach
     988             : 
     989           2 :     return eRetCode;
     990             : }
     991             : #endif
     992             : 
     993             : //------------------------------------------------------------------------
     994             : // CreatePartsMultiple
     995             : //------------------------------------------------------------------------
     996             : #ifdef HAVE_GEOS
     997           0 : static OGRErr CreatePartsMultiple(
     998             :     OGRLayer *const poLnLayer, const char *pszLineSepFieldName,
     999             :     OGRLayer *const poPkLayer, const char *pszPicketsSepFieldName,
    1000             :     int nMValField, double dfStep, OGRLayer *const poOutLayer,
    1001             :     const char *pszOutputSepFieldName, int bDisplayProgress, bool bQuiet)
    1002             : {
    1003             :     // Read all separate field values into array
    1004           0 :     OGRFeatureDefn *pDefn = poLnLayer->GetLayerDefn();
    1005           0 :     const int nLineSepFieldInd = pDefn->GetFieldIndex(pszLineSepFieldName);
    1006           0 :     if (nLineSepFieldInd == -1)
    1007             :     {
    1008           0 :         fprintf(stderr, _("The field was %s not found.\n"),
    1009             :                 pszLineSepFieldName);
    1010           0 :         return OGRERR_FAILURE;
    1011             :     }
    1012             : 
    1013           0 :     poLnLayer->ResetReading();
    1014             : 
    1015           0 :     std::set<CPLString> asIDs;
    1016           0 :     OGRFeature *pFeature = nullptr;
    1017           0 :     while ((pFeature = poLnLayer->GetNextFeature()) != nullptr)
    1018             :     {
    1019           0 :         CPLString sID = pFeature->GetFieldAsString(nLineSepFieldInd);
    1020           0 :         asIDs.insert(sID);
    1021             : 
    1022           0 :         OGRFeature::DestroyFeature(pFeature);
    1023             :     }
    1024             : 
    1025           0 :     for (std::set<CPLString>::const_iterator it = asIDs.begin();
    1026           0 :          it != asIDs.end(); ++it)
    1027             :     {
    1028             :         // Create select clause
    1029           0 :         CPLString sLineWhere;
    1030           0 :         sLineWhere.Printf("%s = '%s'", pszLineSepFieldName, it->c_str());
    1031           0 :         poLnLayer->SetAttributeFilter(sLineWhere);
    1032             : 
    1033           0 :         CPLString sPkWhere;
    1034           0 :         sPkWhere.Printf("%s = '%s'", pszPicketsSepFieldName, it->c_str());
    1035           0 :         poPkLayer->SetAttributeFilter(sPkWhere);
    1036             : 
    1037           0 :         if (!bQuiet)
    1038             :         {
    1039           0 :             fprintf(stdout, "The %s %s\n", pszPicketsSepFieldName, it->c_str());
    1040             :         }
    1041             : 
    1042             :         // Don't check success as we want to try all paths
    1043           0 :         CreateParts(poLnLayer, poPkLayer, nMValField, dfStep, poOutLayer,
    1044           0 :                     bDisplayProgress, bQuiet, pszOutputSepFieldName, *it);
    1045             :     }
    1046             : 
    1047           0 :     return OGRERR_NONE;
    1048             : }
    1049             : #endif
    1050             : 
    1051             : //------------------------------------------------------------------------
    1052             : // GetPosition
    1053             : //------------------------------------------------------------------------
    1054             : #ifdef HAVE_GEOS
    1055           1 : static OGRErr GetPosition(OGRLayer *const poPkLayer, double dfX, double dfY,
    1056             :                           int /* bDisplayProgress */, int bQuiet)
    1057             : {
    1058             :     // Create point
    1059           2 :     OGRPoint pt;
    1060           1 :     pt.setX(dfX);
    1061           1 :     pt.setY(dfY);
    1062           1 :     pt.assignSpatialReference(poPkLayer->GetSpatialRef());
    1063             : 
    1064           1 :     poPkLayer->ResetReading();
    1065           1 :     OGRLineString *pCloserPart = nullptr;
    1066           1 :     double dfBeg = 0.0;
    1067           1 :     double dfScale = 0.0;
    1068           1 :     double dfMinDistance = std::numeric_limits<double>::max();
    1069           1 :     OGRFeature *pFeature = nullptr;
    1070          10 :     while ((pFeature = poPkLayer->GetNextFeature()) != nullptr)
    1071             :     {
    1072           9 :         OGRGeometry *pCurrentGeom = pFeature->GetGeometryRef();
    1073           9 :         if (pCurrentGeom != nullptr)
    1074             :         {
    1075           9 :             double dfCurrentDistance = pCurrentGeom->Distance(&pt);
    1076           9 :             if (dfCurrentDistance < dfMinDistance)
    1077             :             {
    1078           5 :                 dfMinDistance = dfCurrentDistance;
    1079           5 :                 if (pCloserPart != nullptr)
    1080           4 :                     delete pCloserPart;
    1081           5 :                 pCloserPart = pFeature->StealGeometry()->toLineString();
    1082           5 :                 dfBeg = pFeature->GetFieldAsDouble(FIELD_START);
    1083           5 :                 dfScale = pFeature->GetFieldAsDouble(FIELD_SCALE_FACTOR);
    1084             :             }
    1085             :         }
    1086           9 :         OGRFeature::DestroyFeature(pFeature);
    1087             :     }
    1088             : 
    1089           1 :     if (nullptr == pCloserPart)
    1090             :     {
    1091           0 :         fprintf(stderr, _("Failed to find closest part.\n"));
    1092           0 :         return OGRERR_FAILURE;
    1093             :     }
    1094             :     // Now we have closest part
    1095             :     // Get real distance
    1096           1 :     const double dfRealDist = Project(pCloserPart, &pt);
    1097           1 :     delete pCloserPart;
    1098           1 :     if (dfScale == 0)
    1099             :     {
    1100           0 :         fprintf(stderr, _("dfScale == 0.\n"));
    1101           0 :         return OGRERR_FAILURE;
    1102             :     }
    1103             :     // Compute reference distance
    1104             :     // coverity[divide_by_zero]
    1105           1 :     const double dfRefDist = dfBeg + dfRealDist / dfScale;
    1106           1 :     if (bQuiet)
    1107             :     {
    1108           1 :         fprintf(stdout, "%s", CPLSPrintf("%f\n", dfRefDist));
    1109             :     }
    1110             :     else
    1111             :     {
    1112           0 :         fprintf(stdout, "%s",
    1113             :                 CPLSPrintf(
    1114             :                     _("The position for coordinates lat:%f, long:%f is %f\n"),
    1115             :                     dfY, dfX, dfRefDist));
    1116             :     }
    1117             : 
    1118           1 :     return OGRERR_NONE;
    1119             : }
    1120             : #endif
    1121             : 
    1122             : //------------------------------------------------------------------------
    1123             : // GetCoordinates
    1124             : //------------------------------------------------------------------------
    1125           1 : static OGRErr GetCoordinates(OGRLayer *const poPkLayer, double dfPos,
    1126             :                              /* CPL_UNUSED */ int /* bDisplayProgress */,
    1127             :                              bool bQuiet)
    1128             : {
    1129           2 :     CPLString szAttributeFilter;
    1130             :     szAttributeFilter.Printf("%s < %f AND %s > %f", FIELD_START, dfPos,
    1131           1 :                              FIELD_FINISH, dfPos);
    1132             :     // TODO: ExecuteSQL should be faster.
    1133           1 :     poPkLayer->SetAttributeFilter(szAttributeFilter);
    1134           1 :     poPkLayer->ResetReading();
    1135             : 
    1136           1 :     bool bHaveCoords = false;
    1137           2 :     for (auto &pFeature : poPkLayer)
    1138             :     {
    1139           1 :         bHaveCoords = true;
    1140           1 :         const double dfStart = pFeature->GetFieldAsDouble(FIELD_START);
    1141           1 :         double dfPosCorr = dfPos - dfStart;
    1142           1 :         const double dfSF = pFeature->GetFieldAsDouble(FIELD_SCALE_FACTOR);
    1143           1 :         dfPosCorr *= dfSF;
    1144           1 :         OGRLineString *pLine = pFeature->GetGeometryRef()->toLineString();
    1145             : 
    1146           2 :         OGRPoint pt;
    1147           1 :         pLine->Value(dfPosCorr, &pt);
    1148             : 
    1149           1 :         if (bQuiet)
    1150             :         {
    1151           1 :             fprintf(stdout, "%s",
    1152             :                     CPLSPrintf("%f,%f,%f\n", pt.getX(), pt.getY(), pt.getZ()));
    1153             :         }
    1154             :         else
    1155             :         {
    1156           0 :             fprintf(stdout, "%s",
    1157             :                     CPLSPrintf(_("The position for distance %f is lat:%f, "
    1158             :                                  "long:%f, height:%f\n"),
    1159             :                                dfPos, pt.getY(), pt.getX(), pt.getZ()));
    1160             :         }
    1161             :     }
    1162             : 
    1163           1 :     if (bHaveCoords)
    1164             :     {
    1165           1 :         return OGRERR_NONE;
    1166             :     }
    1167             :     else
    1168             :     {
    1169           0 :         fprintf(stderr, _("Get coordinates for position %f failed.\n"), dfPos);
    1170           0 :         return OGRERR_FAILURE;
    1171             :     }
    1172             : }
    1173             : 
    1174             : /************************************************************************/
    1175             : /*                           OGRLineRefOptions                          */
    1176             : /************************************************************************/
    1177             : 
    1178             : struct OGRLineRefOptions
    1179             : {
    1180             :     bool bQuiet = false;
    1181             :     bool bDisplayProgress = false;
    1182             :     std::string osFormat = "ESRI Shapefile";
    1183             : 
    1184             :     std::string osSrcLineDataSourceName;
    1185             :     std::string osSrcLineLayerName;
    1186             : #ifdef HAVE_GEOS
    1187             :     std::string osSrcLineSepFieldName;
    1188             : #endif
    1189             : 
    1190             :     std::string osSrcPicketsDataSourceName;
    1191             : #ifdef HAVE_GEOS
    1192             :     std::string osSrcPicketsLayerName;
    1193             :     std::string osSrcPicketsSepFieldName;
    1194             :     std::string osSrcPicketsMFieldName;
    1195             : #endif
    1196             : 
    1197             :     std::string osSrcPartsDataSourceName;
    1198             :     std::string osSrcPartsLayerName;
    1199             : 
    1200             : #ifdef HAVE_GEOS
    1201             :     std::string osOutputSepFieldName = "uniq_uid";
    1202             : #endif
    1203             :     std::string osOutputDataSourceName;
    1204             :     std::string osOutputLayerName;
    1205             : 
    1206             :     CPLStringList aosDSCO;
    1207             :     CPLStringList aosLCO;
    1208             : 
    1209             :     // Operations
    1210             :     bool bCreate = false;
    1211             :     bool bGetPos = false;
    1212             :     bool bGetSubLine = false;
    1213             :     bool bGetCoord = false;
    1214             : 
    1215             : #ifdef HAVE_GEOS
    1216             :     double dfXPos = std::numeric_limits<double>::quiet_NaN();
    1217             :     double dfYPos = std::numeric_limits<double>::quiet_NaN();
    1218             :     double dfStep = std::numeric_limits<double>::quiet_NaN();
    1219             : #endif
    1220             :     double dfPosBeg = std::numeric_limits<double>::quiet_NaN();
    1221             :     double dfPosEnd = std::numeric_limits<double>::quiet_NaN();
    1222             :     double dfPos = std::numeric_limits<double>::quiet_NaN();
    1223             : };
    1224             : 
    1225             : /************************************************************************/
    1226             : /*                           OGRLineRefGetParser                        */
    1227             : /************************************************************************/
    1228             : 
    1229             : static std::unique_ptr<GDALArgumentParser>
    1230           6 : OGRLineRefAppOptionsGetParser(OGRLineRefOptions *psOptions)
    1231             : {
    1232             :     auto argParser = std::make_unique<GDALArgumentParser>(
    1233           6 :         "ogrlineref", /* bForBinary */ true);
    1234             : 
    1235           6 :     argParser->add_description(
    1236           6 :         _("Create linear reference and provide some calculations using it."));
    1237             : 
    1238           6 :     argParser->add_epilog(_("For more details, consult the full documentation "
    1239             :                             "for the ogrlineref utility "
    1240           6 :                             "https://gdal.org/programs/ogrlineref.html"));
    1241             : 
    1242           6 :     auto &quietArg{argParser->add_quiet_argument(&psOptions->bQuiet)};
    1243           6 :     argParser->add_hidden_alias_for(quietArg, "-quiet");
    1244             : 
    1245           6 :     argParser->add_argument("-progress")
    1246           6 :         .flag()
    1247           6 :         .store_into(psOptions->bDisplayProgress)
    1248           6 :         .help(_("Display progress."));
    1249             : 
    1250           6 :     argParser->add_output_format_argument(psOptions->osFormat);
    1251             : 
    1252           6 :     argParser->add_dataset_creation_options_argument(psOptions->aosDSCO);
    1253             : 
    1254           6 :     argParser->add_layer_creation_options_argument(psOptions->aosLCO);
    1255             : 
    1256             : #ifdef HAVE_GEOS
    1257           6 :     argParser->add_argument("-create")
    1258           6 :         .flag()
    1259           6 :         .store_into(psOptions->bCreate)
    1260           6 :         .help(_("Create the linear reference file (linestring of parts)."));
    1261             : #endif
    1262             : 
    1263           6 :     argParser->add_argument("-l")
    1264          12 :         .metavar("<src_line_datasource_name>")
    1265           6 :         .store_into(psOptions->osSrcLineDataSourceName)
    1266           6 :         .help(_("Name of the line path datasource."));
    1267             : 
    1268           6 :     argParser->add_argument("-ln")
    1269          12 :         .metavar("<layer_name>")
    1270           6 :         .store_into(psOptions->osSrcLineLayerName)
    1271           6 :         .help(_("Layer name in the line path datasource."));
    1272             : 
    1273             : #ifdef HAVE_GEOS
    1274             : 
    1275           6 :     argParser->add_argument("-lf")
    1276          12 :         .metavar("<field_name>")
    1277           6 :         .store_into(psOptions->osSrcLineSepFieldName)
    1278           6 :         .help(_("Field name for unique paths in layer."));
    1279             : #endif
    1280             : 
    1281           6 :     argParser->add_argument("-p")
    1282          12 :         .metavar("<src_repers_datasource_name>")
    1283           6 :         .store_into(psOptions->osSrcPicketsDataSourceName)
    1284           6 :         .help(_("Datasource of repers name."));
    1285             : 
    1286             : #ifdef HAVE_GEOS
    1287           6 :     argParser->add_argument("-pn")
    1288          12 :         .metavar("<layer_name>")
    1289           6 :         .store_into(psOptions->osSrcPicketsLayerName)
    1290           6 :         .help(_("Layer name in repers datasource."));
    1291             : 
    1292           6 :     argParser->add_argument("-pm")
    1293          12 :         .metavar("<pos_field_name>")
    1294           6 :         .store_into(psOptions->osSrcPicketsMFieldName)
    1295           6 :         .help(_("Line position field name."));
    1296             : 
    1297           6 :     argParser->add_argument("-pf")
    1298          12 :         .metavar("<field_name>")
    1299           6 :         .store_into(psOptions->osSrcPicketsSepFieldName)
    1300             :         .help(_("Field name of unique values to map input reference points "
    1301           6 :                 "to lines."));
    1302             : #endif
    1303             : 
    1304           6 :     argParser->add_argument("-r")
    1305          12 :         .metavar("<src_parts_datasource_name>")
    1306           6 :         .store_into(psOptions->osSrcPartsDataSourceName)
    1307           6 :         .help(_("Path to linear reference file."));
    1308             : 
    1309           6 :     argParser->add_argument("-rn")
    1310          12 :         .metavar("<layer_name>")
    1311           6 :         .store_into(psOptions->osSrcPartsLayerName)
    1312           6 :         .help(_("Name of the layer in the input linear reference datasource."));
    1313             : 
    1314           6 :     argParser->add_argument("-o")
    1315          12 :         .metavar("<dst_datasource_name>")
    1316           6 :         .store_into(psOptions->osOutputDataSourceName)
    1317             :         .help(_("Path to output linear reference file (linestring "
    1318           6 :                 "datasource)."));
    1319             : 
    1320           6 :     argParser->add_argument("-on")
    1321          12 :         .metavar("<layer_name>")
    1322           6 :         .store_into(psOptions->osOutputLayerName)
    1323             :         .help(_("Name of the layer in the output linear reference "
    1324           6 :                 "datasource."));
    1325             : 
    1326             : #ifdef HAVE_GEOS
    1327           6 :     argParser->add_argument("-of")
    1328          12 :         .metavar("<field_name>")
    1329           6 :         .store_into(psOptions->osOutputSepFieldName)
    1330             :         .help(_(
    1331           6 :             "Name of the field for storing the unique values of input lines."));
    1332             : 
    1333           6 :     argParser->add_argument("-s")
    1334          12 :         .metavar("<step>")
    1335           6 :         .scan<'g', double>()
    1336           6 :         .store_into(psOptions->dfStep)
    1337           6 :         .help(_("Part size in linear units."));
    1338             : 
    1339           6 :     argParser->add_argument("-get_pos")
    1340           6 :         .flag()
    1341           6 :         .store_into(psOptions->bGetPos)
    1342           6 :         .help(_("Get the position for the given coordinates."));
    1343             : 
    1344           6 :     argParser->add_argument("-x")
    1345          12 :         .metavar("<x>")
    1346           6 :         .scan<'g', double>()
    1347           6 :         .store_into(psOptions->dfXPos)
    1348           6 :         .help(_("X coordinate."));
    1349             : 
    1350           6 :     argParser->add_argument("-y")
    1351          12 :         .metavar("<y>")
    1352           6 :         .scan<'g', double>()
    1353           6 :         .store_into(psOptions->dfYPos)
    1354           6 :         .help(_("Y coordinate."));
    1355             : #endif
    1356             : 
    1357           6 :     argParser->add_argument("-get_coord")
    1358           6 :         .flag()
    1359           6 :         .store_into(psOptions->bGetCoord)
    1360           6 :         .help(_("Return point on path for input linear distance."));
    1361             : 
    1362           6 :     argParser->add_argument("-m")
    1363          12 :         .metavar("<position>")
    1364           6 :         .scan<'g', double>()
    1365           6 :         .store_into(psOptions->dfPos)
    1366           6 :         .help(_("Input linear distance."));
    1367             : 
    1368           6 :     argParser->add_argument("-get_subline")
    1369           6 :         .flag()
    1370           6 :         .store_into(psOptions->bGetSubLine)
    1371             :         .help(_("Return the portion of the input path from and to input linear "
    1372           6 :                 "positions."));
    1373             : 
    1374           6 :     argParser->add_argument("-mb")
    1375          12 :         .metavar("<position>")
    1376           6 :         .scan<'g', double>()
    1377           6 :         .store_into(psOptions->dfPosBeg)
    1378           6 :         .help(_("Input linear distance begin."));
    1379             : 
    1380           6 :     argParser->add_argument("-me")
    1381          12 :         .metavar("<position>")
    1382           6 :         .scan<'g', double>()
    1383           6 :         .store_into(psOptions->dfPosEnd)
    1384           6 :         .help(_("Input linear distance end."));
    1385             : 
    1386           6 :     return argParser;
    1387             : }
    1388             : 
    1389             : /************************************************************************/
    1390             : /*                                main()                                */
    1391             : /************************************************************************/
    1392             : 
    1393           6 : MAIN_START(argc, argv)
    1394             : 
    1395             : {
    1396           6 :     OGRErr eErr = OGRERR_NONE;
    1397             : 
    1398           6 :     operation stOper = op_unknown;
    1399             : 
    1400           6 :     EarlySetConfigOptions(argc, argv);
    1401             : 
    1402           6 :     argc = GDALGeneralCmdLineProcessor(argc, &argv, 0);
    1403             : 
    1404           6 :     if (argc < 1)
    1405             :     {
    1406             :         try
    1407             :         {
    1408           2 :             OGRLineRefOptions sOptions;
    1409           1 :             auto argParser = OGRLineRefAppOptionsGetParser(&sOptions);
    1410           1 :             fprintf(stderr, "%s\n", argParser->usage().c_str());
    1411             :         }
    1412           0 :         catch (const std::exception &err)
    1413             :         {
    1414           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Unexpected exception: %s",
    1415           0 :                      err.what());
    1416             :         }
    1417           1 :         exit(1);
    1418             :     }
    1419             : 
    1420           5 :     OGRRegisterAll();
    1421             : 
    1422          10 :     OGRLineRefOptions psOptions;
    1423           5 :     auto argParser = OGRLineRefAppOptionsGetParser(&psOptions);
    1424             : 
    1425             :     try
    1426             :     {
    1427           5 :         argParser->parse_args_without_binary_name(argv + 1);
    1428           5 :         CSLDestroy(argv);
    1429             :     }
    1430           0 :     catch (const std::exception &error)
    1431             :     {
    1432           0 :         argParser->display_error_and_usage(error);
    1433           0 :         CSLDestroy(argv);
    1434           0 :         exit(1);
    1435             :     }
    1436             : 
    1437             :     // Select operation mode
    1438           5 :     if (psOptions.bCreate)
    1439           2 :         stOper = op_create;
    1440             : 
    1441           5 :     if (psOptions.bGetPos)
    1442             :     {
    1443           1 :         if (stOper != op_unknown)
    1444             :         {
    1445           0 :             fprintf(stderr, _("Only one operation can be specified\n"));
    1446           0 :             argParser->usage();
    1447           0 :             exit(1);
    1448             :         }
    1449           1 :         stOper = op_get_pos;
    1450             :     }
    1451             : 
    1452           5 :     if (psOptions.bGetCoord)
    1453             :     {
    1454           1 :         if (stOper != op_unknown)
    1455             :         {
    1456           0 :             fprintf(stderr, _("Only one operation can be specified\n"));
    1457           0 :             argParser->usage();
    1458           0 :             exit(1);
    1459             :         }
    1460           1 :         stOper = op_get_coord;
    1461             :     }
    1462             : 
    1463           5 :     if (psOptions.bGetSubLine)
    1464             :     {
    1465           1 :         if (stOper != op_unknown)
    1466             :         {
    1467           0 :             fprintf(stderr, _("Only one operation can be specified\n"));
    1468           0 :             argParser->usage();
    1469           0 :             exit(1);
    1470             :         }
    1471           1 :         stOper = op_get_subline;
    1472             :     }
    1473             : 
    1474           5 :     if (stOper == op_unknown)
    1475             :     {
    1476           0 :         fprintf(stderr, _("No operation specified\n"));
    1477           0 :         argParser->usage();
    1478           0 :         exit(1);
    1479             :     }
    1480             : 
    1481             :     /* -------------------------------------------------------------------- */
    1482             :     /*                     Create linear reference                          */
    1483             :     /* -------------------------------------------------------------------- */
    1484             : 
    1485           5 :     switch (stOper)
    1486             :     {
    1487           2 :         case op_create:
    1488             :         {
    1489             : #ifdef HAVE_GEOS
    1490           2 :             if (psOptions.osOutputDataSourceName.empty())
    1491             :             {
    1492           0 :                 fprintf(stderr, _("No output datasource provided.\n"));
    1493           0 :                 argParser->usage();
    1494           0 :                 exit(1);
    1495             :             }
    1496           2 :             if (psOptions.osSrcLineDataSourceName.empty())
    1497             :             {
    1498           0 :                 fprintf(stderr, _("No path datasource provided.\n"));
    1499           0 :                 argParser->usage();
    1500           0 :                 exit(1);
    1501             :             }
    1502           2 :             if (psOptions.osSrcPicketsMFieldName.empty())
    1503             :             {
    1504           0 :                 fprintf(stderr, _("No repers position field provided.\n"));
    1505           0 :                 argParser->usage();
    1506           0 :                 exit(1);
    1507             :             }
    1508           2 :             if (psOptions.osSrcPicketsDataSourceName.empty())
    1509             :             {
    1510           0 :                 fprintf(stderr, _("No repers datasource provided.\n"));
    1511           0 :                 argParser->usage();
    1512           0 :                 exit(1);
    1513             :             }
    1514           2 :             if (psOptions.dfStep == std::numeric_limits<double>::quiet_NaN())
    1515             :             {
    1516           0 :                 fprintf(stderr, _("No step provided.\n"));
    1517           0 :                 argParser->usage();
    1518           0 :                 exit(1);
    1519             :             }
    1520             : 
    1521             :             /* ------------------------------------------------------------- */
    1522             :             /*      Open data source.                                        */
    1523             :             /* ------------------------------------------------------------- */
    1524             : 
    1525           2 :             GDALDataset *poLnDS = GDALDataset::FromHandle(OGROpen(
    1526             :                 psOptions.osSrcLineDataSourceName.c_str(), FALSE, nullptr));
    1527             : 
    1528             :             /* ------------------------------------------------------------- */
    1529             :             /*      Report failure                                           */
    1530             :             /* ------------------------------------------------------------- */
    1531           2 :             if (poLnDS == nullptr)
    1532             :             {
    1533             :                 OGRSFDriverRegistrar *poR =
    1534           0 :                     OGRSFDriverRegistrar::GetRegistrar();
    1535             : 
    1536           0 :                 fprintf(stderr,
    1537             :                         _("FAILURE:\n"
    1538             :                           "Unable to open path datasource `%s' with "
    1539             :                           "the following drivers.\n"),
    1540             :                         psOptions.osSrcLineDataSourceName.c_str());
    1541             : 
    1542           0 :                 for (int iDriver = 0; iDriver < poR->GetDriverCount();
    1543             :                      iDriver++)
    1544             :                 {
    1545           0 :                     fprintf(stderr, "  -> %s\n",
    1546           0 :                             poR->GetDriver(iDriver)->GetDescription());
    1547             :                 }
    1548             : 
    1549           0 :                 exit(1);
    1550             :             }
    1551             : 
    1552           2 :             GDALDataset *poPkDS = GDALDataset::FromHandle(OGROpen(
    1553             :                 psOptions.osSrcPicketsDataSourceName.c_str(), FALSE, nullptr));
    1554             : 
    1555             :             /* --------------------------------------------------------------- */
    1556             :             /*      Report failure                                             */
    1557             :             /* --------------------------------------------------------------- */
    1558             : 
    1559           2 :             if (poPkDS == nullptr)
    1560             :             {
    1561             :                 OGRSFDriverRegistrar *poR =
    1562           0 :                     OGRSFDriverRegistrar::GetRegistrar();
    1563             : 
    1564           0 :                 fprintf(stderr,
    1565             :                         _("FAILURE:\n"
    1566             :                           "Unable to open repers datasource `%s' "
    1567             :                           "with the following drivers.\n"),
    1568             :                         psOptions.osSrcPicketsDataSourceName.c_str());
    1569             : 
    1570           0 :                 for (int iDriver = 0; iDriver < poR->GetDriverCount();
    1571             :                      iDriver++)
    1572             :                 {
    1573           0 :                     fprintf(stderr, "  -> %s\n",
    1574           0 :                             poR->GetDriver(iDriver)->GetDescription());
    1575             :                 }
    1576             : 
    1577           0 :                 exit(1);
    1578             :             }
    1579             : 
    1580             :             /* ----------------------------------------------------------------- */
    1581             :             /*      Find the output driver.                                      */
    1582             :             /* ----------------------------------------------------------------- */
    1583             : 
    1584           2 :             if (!psOptions.bQuiet)
    1585           2 :                 CheckDestDataSourceNameConsistency(
    1586             :                     psOptions.osOutputDataSourceName.c_str(),
    1587             :                     psOptions.osFormat.c_str());
    1588             : 
    1589           2 :             OGRSFDriverRegistrar *poR = OGRSFDriverRegistrar::GetRegistrar();
    1590             : 
    1591             :             GDALDriver *poDriver =
    1592           2 :                 poR->GetDriverByName(psOptions.osFormat.c_str());
    1593           2 :             if (poDriver == nullptr)
    1594             :             {
    1595           0 :                 fprintf(stderr, _("Unable to find driver `%s'.\n"),
    1596             :                         psOptions.osFormat.c_str());
    1597           0 :                 fprintf(stderr, _("The following drivers are available:\n"));
    1598             : 
    1599           0 :                 for (int iDriver = 0; iDriver < poR->GetDriverCount();
    1600             :                      iDriver++)
    1601             :                 {
    1602           0 :                     fprintf(stderr, "  -> `%s'\n",
    1603           0 :                             poR->GetDriver(iDriver)->GetDescription());
    1604             :                 }
    1605           0 :                 exit(1);
    1606             :             }
    1607             : 
    1608           2 :             if (!CPLTestBool(CSLFetchNameValueDef(poDriver->GetMetadata(),
    1609             :                                                   GDAL_DCAP_CREATE, "FALSE")))
    1610             :             {
    1611           0 :                 fprintf(stderr,
    1612             :                         _("%s driver does not support data source creation.\n"),
    1613             :                         psOptions.osSrcPicketsDataSourceName.c_str());
    1614           0 :                 exit(1);
    1615             :             }
    1616             : 
    1617             :             /* ---------------------------------------------------------------- */
    1618             :             /*      Create the output data source.                              */
    1619             :             /* ---------------------------------------------------------------- */
    1620             :             GDALDataset *poODS =
    1621           2 :                 poDriver->Create(psOptions.osOutputDataSourceName.c_str(), 0, 0,
    1622           2 :                                  0, GDT_Unknown, psOptions.aosDSCO);
    1623           2 :             if (poODS == nullptr)
    1624             :             {
    1625           0 :                 fprintf(stderr, _("%s driver failed to create %s.\n"),
    1626             :                         psOptions.osFormat.c_str(),
    1627             :                         psOptions.osOutputDataSourceName.c_str());
    1628           0 :                 exit(1);
    1629             :             }
    1630             : 
    1631             :             OGRLayer *poLnLayer =
    1632           2 :                 psOptions.osSrcLineLayerName.empty()
    1633           2 :                     ? poLnDS->GetLayer(0)
    1634           0 :                     : poLnDS->GetLayerByName(
    1635           0 :                           psOptions.osSrcLineLayerName.c_str());
    1636             : 
    1637           2 :             if (poLnLayer == nullptr)
    1638             :             {
    1639           0 :                 fprintf(stderr, _("Get path layer failed.\n"));
    1640           0 :                 exit(1);
    1641             :             }
    1642             : 
    1643             :             OGRLayer *poPkLayer =
    1644           2 :                 psOptions.osSrcPicketsLayerName.empty()
    1645           2 :                     ? poPkDS->GetLayer(0)
    1646           0 :                     : poPkDS->GetLayerByName(
    1647           0 :                           psOptions.osSrcPicketsLayerName.c_str());
    1648             : 
    1649           2 :             if (poPkLayer == nullptr)
    1650             :             {
    1651           0 :                 fprintf(stderr, _("Get repers layer failed.\n"));
    1652           0 :                 exit(1);
    1653             :             }
    1654             : 
    1655           2 :             OGRFeatureDefn *poPkFDefn = poPkLayer->GetLayerDefn();
    1656           2 :             int nMValField = poPkFDefn->GetFieldIndex(
    1657           2 :                 psOptions.osSrcPicketsMFieldName.c_str());
    1658             : 
    1659           2 :             OGRLayer *poOutLayer = nullptr;
    1660           2 :             if (!psOptions.osSrcLineSepFieldName.empty() &&
    1661           0 :                 !psOptions.osSrcPicketsSepFieldName.empty())
    1662             :             {
    1663             :                 poOutLayer =
    1664           0 :                     SetupTargetLayer(poLnLayer, poODS, psOptions.aosLCO,
    1665             :                                      psOptions.osOutputLayerName.c_str(),
    1666             :                                      psOptions.osOutputSepFieldName.c_str());
    1667           0 :                 if (poOutLayer == nullptr)
    1668             :                 {
    1669           0 :                     fprintf(stderr, _("Create output layer failed.\n"));
    1670           0 :                     exit(1);
    1671             :                 }
    1672             : 
    1673             :                 // Do the work
    1674           0 :                 eErr = CreatePartsMultiple(
    1675             :                     poLnLayer, psOptions.osSrcLineSepFieldName.c_str(),
    1676             :                     poPkLayer, psOptions.osSrcPicketsSepFieldName.c_str(),
    1677             :                     nMValField, psOptions.dfStep, poOutLayer,
    1678             :                     psOptions.osOutputSepFieldName.c_str(),
    1679           0 :                     psOptions.bDisplayProgress, psOptions.bQuiet);
    1680             :             }
    1681             :             else
    1682             :             {
    1683             :                 poOutLayer =
    1684           2 :                     SetupTargetLayer(poLnLayer, poODS, psOptions.aosLCO,
    1685             :                                      psOptions.osOutputLayerName.c_str());
    1686           2 :                 if (poOutLayer == nullptr)
    1687             :                 {
    1688           0 :                     fprintf(stderr, _("Create output layer failed.\n"));
    1689           0 :                     exit(1);
    1690             :                 }
    1691             : 
    1692             :                 // Do the work
    1693           2 :                 eErr = CreateParts(
    1694             :                     poLnLayer, poPkLayer, nMValField, psOptions.dfStep,
    1695           2 :                     poOutLayer, psOptions.bDisplayProgress, psOptions.bQuiet);
    1696             :             }
    1697             : 
    1698           2 :             GDALClose(poLnDS);
    1699           2 :             GDALClose(poPkDS);
    1700           2 :             if (GDALClose(poODS) != CE_None)
    1701           0 :                 eErr = CE_Failure;
    1702             : 
    1703             : #else   // HAVE_GEOS
    1704             :             fprintf(stderr,
    1705             :                     _("GEOS support not enabled or incompatible version.\n"));
    1706             :             exit(1);
    1707             : #endif  // HAVE_GEOS
    1708           2 :             break;
    1709             :         }
    1710           1 :         case op_get_pos:
    1711             :         {
    1712             : #ifdef HAVE_GEOS
    1713             : 
    1714           2 :             if (psOptions.dfXPos == std::numeric_limits<double>::quiet_NaN() ||
    1715           1 :                 psOptions.dfYPos == std::numeric_limits<double>::quiet_NaN())
    1716             :             {
    1717           0 :                 fprintf(stderr, _("no coordinates provided\n"));
    1718           0 :                 argParser->usage();
    1719           0 :                 exit(1);
    1720             :             }
    1721           1 :             if (psOptions.osSrcPartsDataSourceName.empty())
    1722             :             {
    1723           0 :                 fprintf(stderr, _("no parts datasource provided\n"));
    1724           0 :                 argParser->usage();
    1725           0 :                 exit(1);
    1726             :             }
    1727             : 
    1728           1 :             GDALDataset *poPartsDS = GDALDataset::FromHandle(OGROpen(
    1729             :                 psOptions.osSrcPartsDataSourceName.c_str(), FALSE, nullptr));
    1730             :             /* ------------------------------------------------------------------ */
    1731             :             /*      Report failure                                                */
    1732             :             /* ------------------------------------------------------------------ */
    1733           1 :             if (poPartsDS == nullptr)
    1734             :             {
    1735             :                 OGRSFDriverRegistrar *poR =
    1736           0 :                     OGRSFDriverRegistrar::GetRegistrar();
    1737             : 
    1738           0 :                 fprintf(stderr,
    1739             :                         _("FAILURE:\n"
    1740             :                           "Unable to open parts datasource `%s' with "
    1741             :                           "the following drivers.\n"),
    1742             :                         psOptions.osSrcPicketsDataSourceName.c_str());
    1743             : 
    1744           0 :                 for (int iDriver = 0; iDriver < poR->GetDriverCount();
    1745             :                      iDriver++)
    1746             :                 {
    1747           0 :                     fprintf(stderr, "  -> %s\n",
    1748           0 :                             poR->GetDriver(iDriver)->GetDescription());
    1749             :                 }
    1750             : 
    1751           0 :                 exit(1);
    1752             :             }
    1753             : 
    1754             :             OGRLayer *poPartsLayer =
    1755           1 :                 psOptions.osSrcPartsLayerName.empty()
    1756           1 :                     ? poPartsDS->GetLayer(0)
    1757           0 :                     : poPartsDS->GetLayerByName(
    1758           0 :                           psOptions.osSrcPartsLayerName.c_str());
    1759             : 
    1760           1 :             if (poPartsLayer == nullptr)
    1761             :             {
    1762           0 :                 fprintf(stderr, _("Get parts layer failed.\n"));
    1763           0 :                 exit(1);
    1764             :             }
    1765             : 
    1766             :             // Do the work
    1767           2 :             eErr = GetPosition(poPartsLayer, psOptions.dfXPos, psOptions.dfYPos,
    1768           1 :                                psOptions.bDisplayProgress, psOptions.bQuiet);
    1769             : 
    1770           1 :             GDALClose(poPartsDS);
    1771             : 
    1772             : #else   // HAVE_GEOS
    1773             :             fprintf(stderr,
    1774             :                     "GEOS support not enabled or incompatible version.\n");
    1775             :             exit(1);
    1776             : #endif  // HAVE_GEOS
    1777           1 :             break;
    1778             :         }
    1779           1 :         case op_get_coord:
    1780             :         {
    1781           1 :             if (psOptions.osSrcPartsDataSourceName.empty())
    1782             :             {
    1783           0 :                 fprintf(stderr, _("No parts datasource provided.\n"));
    1784           0 :                 argParser->usage();
    1785           0 :                 exit(1);
    1786             :             }
    1787           1 :             if (psOptions.dfPos == std::numeric_limits<double>::quiet_NaN())
    1788             :             {
    1789           0 :                 fprintf(stderr, _("No position provided.\n"));
    1790           0 :                 argParser->usage();
    1791           0 :                 exit(1);
    1792             :             }
    1793             : 
    1794           1 :             GDALDataset *poPartsDS = GDALDataset::FromHandle(OGROpen(
    1795             :                 psOptions.osSrcPartsDataSourceName.c_str(), FALSE, nullptr));
    1796             :             /* ----------------------------------------------------------------- */
    1797             :             /*      Report failure                                               */
    1798             :             /* ----------------------------------------------------------------- */
    1799           1 :             if (poPartsDS == nullptr)
    1800             :             {
    1801             :                 OGRSFDriverRegistrar *poR =
    1802           0 :                     OGRSFDriverRegistrar::GetRegistrar();
    1803             : 
    1804           0 :                 fprintf(stderr,
    1805             :                         _("FAILURE:\n"
    1806             :                           "Unable to open parts datasource `%s' with "
    1807             :                           "the following drivers.\n"),
    1808             :                         psOptions.osSrcPicketsDataSourceName.c_str());
    1809             : 
    1810           0 :                 for (int iDriver = 0; iDriver < poR->GetDriverCount();
    1811             :                      iDriver++)
    1812             :                 {
    1813           0 :                     fprintf(stderr, "  -> %s\n",
    1814           0 :                             poR->GetDriver(iDriver)->GetDescription());
    1815             :                 }
    1816             : 
    1817           0 :                 exit(1);
    1818             :             }
    1819             : 
    1820             :             OGRLayer *poPartsLayer =
    1821           1 :                 psOptions.osSrcPartsLayerName.empty()
    1822           1 :                     ? poPartsDS->GetLayer(0)
    1823           0 :                     : poPartsDS->GetLayerByName(
    1824           0 :                           psOptions.osSrcPartsLayerName.c_str());
    1825             : 
    1826           1 :             if (poPartsLayer == nullptr)
    1827             :             {
    1828           0 :                 fprintf(stderr, _("Get parts layer failed.\n"));
    1829           0 :                 exit(1);
    1830             :             }
    1831             :             // Do the work
    1832           2 :             eErr = GetCoordinates(poPartsLayer, psOptions.dfPos,
    1833           1 :                                   psOptions.bDisplayProgress, psOptions.bQuiet);
    1834             : 
    1835           1 :             GDALClose(poPartsDS);
    1836             : 
    1837           1 :             break;
    1838             :         }
    1839           1 :         case op_get_subline:
    1840             :         {
    1841           1 :             if (psOptions.dfPosBeg == std::numeric_limits<double>::quiet_NaN())
    1842             :             {
    1843           0 :                 fprintf(stderr, _("No begin position provided.\n"));
    1844           0 :                 argParser->usage();
    1845           0 :                 exit(1);
    1846             :             }
    1847           1 :             if (psOptions.dfPosEnd == std::numeric_limits<double>::quiet_NaN())
    1848             :             {
    1849           0 :                 fprintf(stderr, _("No end position provided.\n"));
    1850           0 :                 argParser->usage();
    1851           0 :                 exit(1);
    1852             :             }
    1853           1 :             if (psOptions.osSrcPartsDataSourceName.empty())
    1854             :             {
    1855           0 :                 fprintf(stderr, _("No parts datasource provided.\n"));
    1856           0 :                 argParser->usage();
    1857           0 :                 exit(1);
    1858             :             }
    1859             : 
    1860           1 :             GDALDataset *poPartsDS = GDALDataset::FromHandle(OGROpen(
    1861             :                 psOptions.osSrcPartsDataSourceName.c_str(), FALSE, nullptr));
    1862             : 
    1863             :             // Report failure.
    1864           1 :             if (poPartsDS == nullptr)
    1865             :             {
    1866             :                 OGRSFDriverRegistrar *poR =
    1867           0 :                     OGRSFDriverRegistrar::GetRegistrar();
    1868             : 
    1869           0 :                 fprintf(stderr,
    1870             :                         _("FAILURE:\n"
    1871             :                           "Unable to open parts datasource `%s' with "
    1872             :                           "the following drivers.\n"),
    1873             :                         psOptions.osSrcPicketsDataSourceName.c_str());
    1874             : 
    1875           0 :                 for (int iDriver = 0; iDriver < poR->GetDriverCount();
    1876             :                      iDriver++)
    1877             :                 {
    1878           0 :                     fprintf(stderr, "  -> %s\n",
    1879           0 :                             poR->GetDriver(iDriver)->GetDescription());
    1880             :                 }
    1881             : 
    1882           0 :                 exit(1);
    1883             :             }
    1884             : 
    1885             :             // Find the output driver.
    1886           1 :             if (!psOptions.bQuiet)
    1887           1 :                 CheckDestDataSourceNameConsistency(
    1888             :                     psOptions.osOutputDataSourceName.c_str(),
    1889             :                     psOptions.osFormat.c_str());
    1890             : 
    1891           1 :             OGRSFDriverRegistrar *poR = OGRSFDriverRegistrar::GetRegistrar();
    1892             : 
    1893             :             GDALDriver *poDriver =
    1894           1 :                 poR->GetDriverByName(psOptions.osFormat.c_str());
    1895           1 :             if (poDriver == nullptr)
    1896             :             {
    1897           0 :                 fprintf(stderr, _("Unable to find driver `%s'.\n"),
    1898             :                         psOptions.osFormat.c_str());
    1899           0 :                 fprintf(stderr, _("The following drivers are available:\n"));
    1900             : 
    1901           0 :                 for (int iDriver = 0; iDriver < poR->GetDriverCount();
    1902             :                      iDriver++)
    1903             :                 {
    1904           0 :                     fprintf(stderr, "  -> `%s'\n",
    1905           0 :                             poR->GetDriver(iDriver)->GetDescription());
    1906             :                 }
    1907           0 :                 exit(1);
    1908             :             }
    1909             : 
    1910           1 :             if (!CPLTestBool(CSLFetchNameValueDef(poDriver->GetMetadata(),
    1911             :                                                   GDAL_DCAP_CREATE, "FALSE")))
    1912             :             {
    1913           0 :                 fprintf(stderr,
    1914             :                         _("%s driver does not support data source creation.\n"),
    1915             :                         psOptions.osFormat.c_str());
    1916           0 :                 exit(1);
    1917             :             }
    1918             : 
    1919             :             // Create the output data source.
    1920             : 
    1921             :             GDALDataset *poODS =
    1922           1 :                 poDriver->Create(psOptions.osOutputDataSourceName.c_str(), 0, 0,
    1923           1 :                                  0, GDT_Unknown, psOptions.aosDSCO);
    1924           1 :             if (poODS == nullptr)
    1925             :             {
    1926           0 :                 fprintf(stderr, _("%s driver failed to create %s\n"),
    1927             :                         psOptions.osFormat.c_str(),
    1928             :                         psOptions.osOutputDataSourceName.c_str());
    1929           0 :                 exit(1);
    1930             :             }
    1931             : 
    1932             :             OGRLayer *poPartsLayer =
    1933           1 :                 psOptions.osSrcLineLayerName.empty()
    1934           1 :                     ? poPartsDS->GetLayer(0)
    1935           0 :                     : poPartsDS->GetLayerByName(
    1936           0 :                           psOptions.osSrcLineLayerName.c_str());
    1937             : 
    1938           1 :             if (poPartsLayer == nullptr)
    1939             :             {
    1940           0 :                 fprintf(stderr, _("Get parts layer failed.\n"));
    1941           0 :                 exit(1);
    1942             :             }
    1943             : 
    1944             :             OGRLayer *poOutLayer =
    1945           1 :                 SetupTargetLayer(poPartsLayer, poODS, psOptions.aosLCO,
    1946             :                                  psOptions.osOutputLayerName.c_str());
    1947             : 
    1948           1 :             if (poOutLayer == nullptr)
    1949             :             {
    1950           0 :                 fprintf(stderr, _("Create output layer failed.\n"));
    1951           0 :                 exit(1);
    1952             :             }
    1953             : 
    1954             :             // Do the work
    1955             : 
    1956           2 :             eErr = CreateSubline(poPartsLayer, psOptions.dfPosBeg,
    1957             :                                  psOptions.dfPosEnd, poOutLayer,
    1958           1 :                                  psOptions.bDisplayProgress, psOptions.bQuiet);
    1959             : 
    1960           1 :             GDALClose(poPartsDS);
    1961           1 :             if (GDALClose(poODS) != CE_None)
    1962           0 :                 eErr = CE_Failure;
    1963             : 
    1964           1 :             break;
    1965             :         }
    1966           0 :         default:
    1967           0 :             fprintf(stderr, _("Unknown operation.\n"));
    1968           0 :             argParser->usage();
    1969           0 :             exit(1);
    1970             :     }
    1971             : 
    1972           5 :     OGRCleanupAll();
    1973             : 
    1974             : #ifdef DBMALLOC
    1975             :     malloc_dump(1);
    1976             : #endif
    1977             : 
    1978           5 :     return eErr == OGRERR_NONE ? 0 : 1;
    1979             : }
    1980             : 
    1981           0 : MAIN_END

Generated by: LCOV version 1.14