LCOV - code coverage report
Current view: top level - apps - ogrlineref.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 594 879 67.6 %
Date: 2026-06-23 16:35:19 Functions: 13 14 92.9 %

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

Generated by: LCOV version 1.14