LCOV - code coverage report
Current view: top level - gnm - gnmgenericnetwork.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 405 742 54.6 %
Date: 2025-07-03 09:54:33 Functions: 34 57 59.6 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL/OGR Geography Network support (Geographic Network Model)
       4             :  * Purpose:  GNM network class.
       5             :  * Authors:  Mikhail Gusev (gusevmihs at gmail dot com)
       6             :  *           Dmitry Baryshnikov, polimax@mail.ru
       7             :  *
       8             :  ******************************************************************************
       9             :  * Copyright (c) 2014, Mikhail Gusev
      10             :  * Copyright (c) 2014-2015, NextGIS <info@nextgis.com>
      11             :  *
      12             :  * Permission is hereby granted, free of charge, to any person obtaining a
      13             :  * copy of this software and associated documentation files (the "Software"),
      14             :  * to deal in the Software without restriction, including without limitation
      15             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      16             :  * and/or sell copies of the Software, and to permit persons to whom the
      17             :  * Software is furnished to do so, subject to the following conditions:
      18             :  *
      19             :  * The above copyright notice and this permission notice shall be included
      20             :  * in all copies or substantial portions of the Software.
      21             :  *
      22             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      23             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      24             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      25             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      26             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      27             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      28             :  * DEALINGS IN THE SOFTWARE.
      29             :  ****************************************************************************/
      30             : 
      31             : #include "gnm_api.h"
      32             : #include "gnm_priv.h"
      33             : #include "ogrsf_frmts.h"
      34             : 
      35             : #include <set>
      36             : 
      37             : //! @cond Doxygen_Suppress
      38          16 : GNMGenericNetwork::GNMGenericNetwork() : m_asRules()
      39             : {
      40          16 : }
      41             : 
      42          16 : GNMGenericNetwork::~GNMGenericNetwork()
      43             : {
      44          37 :     for (size_t i = 0; i < m_apoLayers.size(); i++)
      45          21 :         delete m_apoLayers[i];
      46          16 : }
      47             : 
      48          43 : int GNMGenericNetwork::GetLayerCount()
      49             : {
      50          43 :     return static_cast<int>(m_apoLayers.size());
      51             : }
      52             : 
      53          37 : OGRLayer *GNMGenericNetwork::GetLayer(int nIndex)
      54             : {
      55          37 :     if (nIndex < 0 || nIndex >= static_cast<int>(m_apoLayers.size()))
      56           0 :         return nullptr;
      57          37 :     return m_apoLayers[nIndex];
      58             : }
      59             : 
      60           4 : OGRErr GNMGenericNetwork::DeleteLayer(int nIndex)
      61             : {
      62           4 :     if (nIndex < 0 || nIndex >= static_cast<int>(m_apoLayers.size()))
      63           0 :         return OGRERR_FAILURE;
      64             : 
      65           4 :     const char *pszLayerName = m_apoLayers[nIndex]->GetName();
      66             :     OGRFeature *poFeature;
      67             : 
      68           4 :     std::set<GNMGFID> anGFIDs;
      69           4 :     std::set<GNMGFID>::iterator it;
      70             :     // remove layer GFID's from Features layer
      71             : 
      72           4 :     m_poFeaturesLayer->ResetReading();
      73         194 :     while ((poFeature = m_poFeaturesLayer->GetNextFeature()) != nullptr)
      74             :     {
      75             :         const char *pFeatureClass =
      76         190 :             poFeature->GetFieldAsString(GNM_SYSFIELD_LAYERNAME);
      77             : 
      78         190 :         if (EQUAL(pFeatureClass, pszLayerName))
      79             :         {
      80         128 :             anGFIDs.insert(poFeature->GetFieldAsGNMGFID(GNM_SYSFIELD_GFID));
      81         128 :             CPL_IGNORE_RET_VAL(
      82         128 :                 m_poFeaturesLayer->DeleteFeature(poFeature->GetFID()));
      83             :         }
      84         190 :         OGRFeature::DestroyFeature(poFeature);
      85             :     }
      86             : 
      87             :     // remove GFID's from graph layer
      88             : 
      89           4 :     m_poGraphLayer->ResetReading();
      90          70 :     while ((poFeature = m_poGraphLayer->GetNextFeature()) != nullptr)
      91             :     {
      92          66 :         GNMGFID nGFID = poFeature->GetFieldAsGNMGFID(GNM_SYSFIELD_SOURCE);
      93          66 :         it = anGFIDs.find(nGFID);
      94          66 :         if (it != anGFIDs.end())
      95             :         {
      96           0 :             CPL_IGNORE_RET_VAL(
      97           0 :                 m_poGraphLayer->DeleteFeature(poFeature->GetFID()));
      98           0 :             OGRFeature::DestroyFeature(poFeature);
      99          66 :             continue;
     100             :         }
     101             : 
     102          66 :         nGFID = poFeature->GetFieldAsGNMGFID(GNM_SYSFIELD_TARGET);
     103          66 :         it = anGFIDs.find(nGFID);
     104          66 :         if (it != anGFIDs.end())
     105             :         {
     106           0 :             CPL_IGNORE_RET_VAL(
     107           0 :                 m_poGraphLayer->DeleteFeature(poFeature->GetFID()));
     108           0 :             OGRFeature::DestroyFeature(poFeature);
     109           0 :             continue;
     110             :         }
     111             : 
     112          66 :         nGFID = poFeature->GetFieldAsGNMGFID(GNM_SYSFIELD_CONNECTOR);
     113          66 :         it = anGFIDs.find(nGFID);
     114          66 :         if (it != anGFIDs.end())
     115             :         {
     116          66 :             CPL_IGNORE_RET_VAL(
     117          66 :                 m_poGraphLayer->DeleteFeature(poFeature->GetFID()));
     118          66 :             OGRFeature::DestroyFeature(poFeature);
     119          66 :             continue;
     120             :         }
     121             : 
     122           0 :         OGRFeature::DestroyFeature(poFeature);
     123             :     }
     124             : 
     125             :     // remove connected rules
     126           8 :     for (size_t i = m_asRules.size(); i > 0; --i)
     127             :     {
     128           4 :         if (EQUAL(m_asRules[i - 1].GetSourceLayerName(), pszLayerName))
     129             :         {
     130           0 :             m_asRules.erase(m_asRules.begin() + i - 1);
     131           0 :             m_bIsRulesChanged = true;
     132             :         }
     133           4 :         else if (EQUAL(m_asRules[i - 1].GetTargetLayerName(), pszLayerName))
     134             :         {
     135           0 :             m_asRules.erase(m_asRules.begin() + i - 1);
     136           0 :             m_bIsRulesChanged = true;
     137             :         }
     138           4 :         else if (EQUAL(m_asRules[i - 1].GetConnectorLayerName(), pszLayerName))
     139             :         {
     140           0 :             m_asRules.erase(m_asRules.begin() + i - 1);
     141           0 :             m_bIsRulesChanged = true;
     142             :         }
     143             :     }
     144             : 
     145           4 :     delete m_apoLayers[nIndex];
     146             :     // remove from array
     147           4 :     m_apoLayers.erase(m_apoLayers.begin() + nIndex);
     148           4 :     return OGRERR_NONE;
     149             : }
     150             : 
     151           2 : CPLErr GNMGenericNetwork::Delete()
     152             : {
     153           2 :     CPLErr eResult = DeleteNetworkLayers();
     154           2 :     if (eResult != CE_None)
     155           0 :         return eResult;
     156           2 :     eResult = DeleteMetadataLayer();
     157           2 :     if (eResult != CE_None)
     158           0 :         return eResult;
     159           2 :     eResult = DeleteGraphLayer();
     160           2 :     if (eResult != CE_None)
     161           0 :         return eResult;
     162             : 
     163           2 :     return DeleteFeaturesLayer();
     164             : }
     165             : 
     166           4 : int GNMGenericNetwork::GetVersion() const
     167             : {
     168           4 :     return m_nVersion;
     169             : }
     170             : 
     171         128 : GIntBig GNMGenericNetwork::GetNewGlobalFID()
     172             : {
     173         128 :     return m_nGID++;
     174             : }
     175             : 
     176           5 : CPLString GNMGenericNetwork::GetAlgorithmName(GNMDirection eAlgorithm,
     177             :                                               bool bShortName)
     178             : {
     179           5 :     switch (eAlgorithm)
     180             :     {
     181           2 :         case GATDijkstraShortestPath:
     182           2 :             if (bShortName)
     183           2 :                 return CPLString("Dijkstra");
     184             :             else
     185           0 :                 return CPLString("Dijkstra shortest path");
     186           2 :         case GATKShortestPath:
     187           2 :             if (bShortName)
     188           2 :                 return CPLString("Yens");
     189             :             else
     190           0 :                 return CPLString("Yens shortest paths");
     191           1 :         case GATConnectedComponents:
     192           1 :             if (bShortName)
     193           1 :                 return CPLString("Connected");
     194             :             else
     195           0 :                 return CPLString("Connected components");
     196             :     }
     197             : 
     198           0 :     return CPLString("Invalid");
     199             : }
     200             : 
     201         128 : CPLErr GNMGenericNetwork::AddFeatureGlobalFID(GNMGFID nFID,
     202             :                                               const char *pszLayerName)
     203             : {
     204             :     OGRFeature *poFeature =
     205         128 :         OGRFeature::CreateFeature(m_poFeaturesLayer->GetLayerDefn());
     206         128 :     poFeature->SetField(GNM_SYSFIELD_GFID, nFID);
     207         128 :     poFeature->SetField(GNM_SYSFIELD_LAYERNAME, pszLayerName);
     208             : 
     209         128 :     if (m_poFeaturesLayer->CreateFeature(poFeature) != OGRERR_NONE)
     210             :     {
     211           0 :         OGRFeature::DestroyFeature(poFeature);
     212           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Failed to create feature.");
     213           0 :         return CE_Failure;
     214             :     }
     215             : 
     216         128 :     OGRFeature::DestroyFeature(poFeature);
     217             : 
     218         128 :     return CE_None;
     219             : }
     220             : 
     221          66 : CPLErr GNMGenericNetwork::ConnectFeatures(GNMGFID nSrcGFID, GNMGFID nTgtGFID,
     222             :                                           GNMGFID nConGFID, double dfCost,
     223             :                                           double dfInvCost, GNMDirection eDir)
     224             : {
     225          66 :     if (!m_bIsGraphLoaded && LoadGraph() != CE_None)
     226             :     {
     227           0 :         return CE_Failure;
     228             :     }
     229             : 
     230          66 :     OGRFeature *poFeature = FindConnection(nSrcGFID, nTgtGFID, nConGFID);
     231          66 :     if (poFeature != nullptr)
     232             :     {
     233           0 :         OGRFeature::DestroyFeature(poFeature);
     234           0 :         CPLError(CE_Failure, CPLE_AppDefined, "The connection already created");
     235           0 :         return CE_Failure;
     236             :     }
     237             : 
     238          66 :     if (m_asRules.empty())
     239             :     {
     240           0 :         CPLError(CE_Failure, CPLE_AppDefined, "The connection forbidden");
     241           0 :         return CE_Failure;
     242             :     }
     243             :     else
     244             :     {
     245          66 :         CPLString soSrcLayerName = m_moFeatureFIDMap[nSrcGFID];
     246          66 :         CPLString soTgtLayerName = m_moFeatureFIDMap[nTgtGFID];
     247          66 :         CPLString soConnLayerName = m_moFeatureFIDMap[nConGFID];
     248         132 :         for (size_t i = 0; i < m_asRules.size(); ++i)
     249             :         {
     250         132 :             if (!m_asRules[i].CanConnect(soSrcLayerName, soTgtLayerName,
     251          66 :                                          soConnLayerName))
     252             :             {
     253           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     254             :                          "The connection forbidden");
     255           0 :                 return CE_Failure;
     256             :             }
     257             :         }
     258             :     }
     259             : 
     260             :     // we support both vertices and edge to be virtual
     261          66 :     if (nConGFID == -1)
     262           0 :         nConGFID = GetNewVirtualFID();
     263          66 :     if (nSrcGFID == -1)
     264           0 :         nSrcGFID = GetNewVirtualFID();
     265          66 :     if (nTgtGFID == -1)
     266           0 :         nTgtGFID = GetNewVirtualFID();
     267             : 
     268          66 :     poFeature = OGRFeature::CreateFeature(m_poGraphLayer->GetLayerDefn());
     269          66 :     poFeature->SetField(GNM_SYSFIELD_SOURCE, nSrcGFID);
     270          66 :     poFeature->SetField(GNM_SYSFIELD_TARGET, nTgtGFID);
     271          66 :     poFeature->SetField(GNM_SYSFIELD_CONNECTOR, nConGFID);
     272          66 :     poFeature->SetField(GNM_SYSFIELD_COST, dfCost);
     273          66 :     poFeature->SetField(GNM_SYSFIELD_INVCOST, dfInvCost);
     274          66 :     poFeature->SetField(GNM_SYSFIELD_DIRECTION, eDir);
     275          66 :     poFeature->SetField(GNM_SYSFIELD_BLOCKED, GNM_BLOCK_NONE);
     276             : 
     277          66 :     if (m_poGraphLayer->CreateFeature(poFeature) != OGRERR_NONE)
     278             :     {
     279           0 :         OGRFeature::DestroyFeature(poFeature);
     280           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Failed to create feature.");
     281           0 :         return CE_Failure;
     282             :     }
     283             : 
     284          66 :     OGRFeature::DestroyFeature(poFeature);
     285             : 
     286             :     // update graph
     287             : 
     288          66 :     m_oGraph.AddEdge(nConGFID, nSrcGFID, nTgtGFID, eDir == GNM_EDGE_DIR_BOTH,
     289             :                      dfCost, dfInvCost);
     290             : 
     291          66 :     return CE_None;
     292             : }
     293             : 
     294           0 : CPLErr GNMGenericNetwork::DisconnectFeatures(GNMGFID nSrcGFID, GNMGFID nTgtGFID,
     295             :                                              GNMGFID nConGFID)
     296             : {
     297           0 :     if (!m_bIsGraphLoaded && LoadGraph() != CE_None)
     298             :     {
     299           0 :         return CE_Failure;
     300             :     }
     301             : 
     302           0 :     OGRFeature *poFeature = FindConnection(nSrcGFID, nTgtGFID, nConGFID);
     303           0 :     if (poFeature == nullptr)
     304             :     {
     305           0 :         CPLError(CE_Failure, CPLE_AppDefined, "The connection not exist");
     306           0 :         return CE_Failure;
     307             :     }
     308             : 
     309           0 :     if (m_poGraphLayer->DeleteFeature(poFeature->GetFID()) != OGRERR_NONE)
     310             :     {
     311           0 :         OGRFeature::DestroyFeature(poFeature);
     312           0 :         return CE_Failure;
     313             :     }
     314             : 
     315           0 :     OGRFeature::DestroyFeature(poFeature);
     316             : 
     317             :     // update graph
     318             : 
     319           0 :     m_oGraph.DeleteEdge(nConGFID);
     320             : 
     321           0 :     return CE_None;
     322             : }
     323             : 
     324           0 : CPLErr GNMGenericNetwork::DisconnectFeaturesWithId(GNMGFID nFID)
     325             : {
     326           0 :     if (!m_bIsGraphLoaded && LoadGraph() != CE_None)
     327             :     {
     328           0 :         return CE_Failure;
     329             :     }
     330             : 
     331           0 :     CPLString soFilter;
     332             :     soFilter.Printf("%s = " GNMGFIDFormat " or %s = " GNMGFIDFormat
     333             :                     " or %s = " GNMGFIDFormat,
     334             :                     GNM_SYSFIELD_SOURCE, nFID, GNM_SYSFIELD_TARGET, nFID,
     335           0 :                     GNM_SYSFIELD_CONNECTOR, nFID);
     336             : 
     337           0 :     CPLDebug("GNM", "Set attribute filter: %s", soFilter.c_str());
     338             : 
     339           0 :     m_poGraphLayer->SetAttributeFilter(soFilter);
     340           0 :     m_poGraphLayer->ResetReading();
     341             :     OGRFeature *poFeature;
     342           0 :     while ((poFeature = m_poGraphLayer->GetNextFeature()) != nullptr)
     343             :     {
     344           0 :         if (m_poGraphLayer->DeleteFeature(poFeature->GetFID()) != CE_None)
     345             :         {
     346           0 :             OGRFeature::DestroyFeature(poFeature);
     347           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     348             :                      "Failed to remove feature connection.");
     349           0 :             return CE_Failure;
     350             :         }
     351           0 :         OGRFeature::DestroyFeature(poFeature);
     352             :     }
     353             : 
     354           0 :     m_poGraphLayer->SetAttributeFilter(nullptr);
     355             : 
     356           0 :     m_oGraph.DeleteEdge(nFID);
     357           0 :     m_oGraph.DeleteVertex(nFID);
     358             : 
     359           0 :     return CE_None;
     360             : }
     361             : 
     362           0 : CPLErr GNMGenericNetwork::ReconnectFeatures(GNMGFID nSrcGFID, GNMGFID nTgtGFID,
     363             :                                             GNMGFID nConGFID, double dfCost,
     364             :                                             double dfInvCost, GNMDirection eDir)
     365             : {
     366           0 :     if (!m_bIsGraphLoaded && LoadGraph() != CE_None)
     367             :     {
     368           0 :         return CE_Failure;
     369             :     }
     370             : 
     371           0 :     OGRFeature *poFeature = FindConnection(nSrcGFID, nTgtGFID, nConGFID);
     372           0 :     if (poFeature == nullptr)
     373             :     {
     374           0 :         CPLError(CE_Failure, CPLE_AppDefined, "The connection not exist");
     375           0 :         return CE_Failure;
     376             :     }
     377             : 
     378           0 :     poFeature->SetField(GNM_SYSFIELD_COST, dfCost);
     379           0 :     poFeature->SetField(GNM_SYSFIELD_INVCOST, dfInvCost);
     380           0 :     poFeature->SetField(GNM_SYSFIELD_DIRECTION, eDir);
     381             : 
     382           0 :     if (m_poGraphLayer->SetFeature(poFeature) != OGRERR_NONE)
     383             :     {
     384           0 :         OGRFeature::DestroyFeature(poFeature);
     385           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Failed to update feature.");
     386           0 :         return CE_Failure;
     387             :     }
     388             : 
     389           0 :     OGRFeature::DestroyFeature(poFeature);
     390             : 
     391             :     // update graph
     392             : 
     393           0 :     m_oGraph.ChangeEdge(nConGFID, dfCost, dfInvCost);
     394             : 
     395           0 :     return CE_None;
     396             : }
     397             : 
     398           0 : CPLErr GNMGenericNetwork::DisconnectAll()
     399             : {
     400           0 :     if (!m_bIsGraphLoaded && LoadGraph() != CE_None)
     401             :     {
     402           0 :         return CE_Failure;
     403             :     }
     404             :     // delete everything from m_pGraphLayer
     405             : 
     406             :     OGRFeature *poFeature;
     407           0 :     m_poGraphLayer->ResetReading();
     408           0 :     while ((poFeature = m_poGraphLayer->GetNextFeature()) != nullptr)
     409             :     {
     410           0 :         CPL_IGNORE_RET_VAL(m_poGraphLayer->DeleteFeature(poFeature->GetFID()));
     411           0 :         OGRFeature::DestroyFeature(poFeature);
     412             :     }
     413             : 
     414           0 :     m_oGraph.Clear();
     415             : 
     416           0 :     return CE_None;
     417             : }
     418             : 
     419         300 : OGRFeature *GNMGenericNetwork::GetFeatureByGlobalFID(GNMGFID nFID)
     420             : {
     421         600 :     CPLString soLayerName = m_moFeatureFIDMap[nFID];
     422         466 :     for (size_t i = 0; i < m_apoLayers.size(); ++i)
     423             :     {
     424         458 :         if (EQUAL(soLayerName, m_apoLayers[i]->GetName()))
     425         292 :             return m_apoLayers[i]->GetFeature(nFID);
     426             :     }
     427           8 :     return nullptr;
     428             : }
     429             : 
     430           2 : CPLErr GNMGenericNetwork::CreateRule(const char *pszRuleStr)
     431             : {
     432           2 :     CPLDebug("GNM", "Try to create rule '%s'", pszRuleStr);
     433           4 :     GNMRule oRule(pszRuleStr);
     434           2 :     if (!oRule.IsValid())
     435             :     {
     436           0 :         return CE_Failure;
     437             :     }
     438             : 
     439           2 :     if (!oRule.IsAcceptAny())
     440             :     {
     441           0 :         bool bSrcExist = false;
     442           0 :         bool bTgtExist = false;
     443           0 :         bool bConnExist = false;
     444             :         // check layers exist
     445           0 :         for (size_t i = 0; i < m_apoLayers.size(); ++i)
     446             :         {
     447           0 :             if (EQUAL(oRule.GetSourceLayerName(), m_apoLayers[i]->GetName()))
     448             :             {
     449           0 :                 bSrcExist = true;
     450             :             }
     451           0 :             else if (EQUAL(oRule.GetTargetLayerName(),
     452             :                            m_apoLayers[i]->GetName()))
     453             :             {
     454           0 :                 bTgtExist = true;
     455             :             }
     456           0 :             else if (EQUAL(oRule.GetConnectorLayerName(),
     457             :                            m_apoLayers[i]->GetName()))
     458             :             {
     459           0 :                 bConnExist = true;
     460             :             }
     461             :         }
     462             : 
     463           0 :         if (!bSrcExist || !bTgtExist)
     464             :         {
     465           0 :             CPLError(CE_Failure, CPLE_IllegalArg,
     466             :                      "Layers '%s' or '%s' not exist",
     467           0 :                      oRule.GetSourceLayerName().c_str(),
     468           0 :                      oRule.GetTargetLayerName().c_str());
     469           0 :             return CE_Failure;
     470             :         }
     471             : 
     472           0 :         if (!bConnExist && !oRule.GetConnectorLayerName().empty())
     473             :         {
     474           0 :             CPLError(CE_Failure, CPLE_IllegalArg,
     475             :                      "Connector layer '%s' not exist",
     476           0 :                      oRule.GetConnectorLayerName().c_str());
     477           0 :             return CE_Failure;
     478             :         }
     479             :     }
     480             : 
     481           2 :     m_asRules.push_back(std::move(oRule));
     482           2 :     m_bIsRulesChanged = true;
     483             : 
     484           2 :     return CE_None;
     485             : }
     486             : 
     487           2 : CPLErr GNMGenericNetwork::DeleteAllRules()
     488             : {
     489           4 :     CPLString soFilter;
     490           2 :     soFilter.Printf("%s LIKE '%s%%'", GNM_SYSFIELD_PARAMNAME, GNM_MD_RULE);
     491           2 :     m_poMetadataLayer->SetAttributeFilter(soFilter);
     492             : 
     493           2 :     m_poMetadataLayer->ResetReading();
     494             :     OGRFeature *poFeature;
     495           2 :     std::vector<GIntBig> aFIDs;
     496           2 :     while ((poFeature = m_poMetadataLayer->GetNextFeature()) != nullptr)
     497             :     {
     498           0 :         aFIDs.push_back(poFeature->GetFID());
     499           0 :         OGRFeature::DestroyFeature(poFeature);
     500             :     }
     501             : 
     502           2 :     m_poMetadataLayer->SetAttributeFilter(nullptr);
     503           2 :     for (size_t i = 0; i < aFIDs.size(); ++i)
     504             :     {
     505           0 :         CPL_IGNORE_RET_VAL(m_poMetadataLayer->DeleteFeature(aFIDs[i]));
     506             :     }
     507             : 
     508           4 :     return CE_None;
     509             : }
     510             : 
     511           0 : CPLErr GNMGenericNetwork::DeleteRule(const char *pszRuleStr)
     512             : {
     513           0 :     for (size_t i = 0; i < m_asRules.size(); ++i)
     514             :     {
     515           0 :         if (EQUAL(pszRuleStr, m_asRules[i]))
     516             :         {
     517           0 :             m_asRules.erase(m_asRules.begin() + i);
     518           0 :             m_bIsRulesChanged = true;
     519           0 :             return CE_None;
     520             :         }
     521             :     }
     522             : 
     523           0 :     return CE_Failure;
     524             : }
     525             : 
     526           1 : char **GNMGenericNetwork::GetRules() const
     527             : {
     528           1 :     char **papszRules = nullptr;
     529           2 :     for (size_t i = 0; i < m_asRules.size(); ++i)
     530             :     {
     531           1 :         papszRules = CSLAddString(papszRules, m_asRules[i]);
     532             :     }
     533           1 :     return papszRules;
     534             : }
     535             : 
     536           2 : CPLErr GNMGenericNetwork::ConnectPointsByLines(char **papszLayerList,
     537             :                                                double dfTolerance,
     538             :                                                double dfCost, double dfInvCost,
     539             :                                                GNMDirection eDir)
     540             : {
     541           2 :     if (CSLCount(papszLayerList) < 2)
     542             :     {
     543           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
     544             :                  "Minimum 2 layers needed to connect");
     545           0 :         return CE_Failure;
     546             :     }
     547             : 
     548           4 :     std::vector<OGRLayer *> paLineLayers;
     549           4 :     std::vector<OGRLayer *> paPointLayers;
     550             :     int eType;
     551             :     int iLayer;
     552             :     OGRLayer *poLayer;
     553             : 
     554           6 :     for (iLayer = 0; papszLayerList[iLayer] != nullptr; ++iLayer)
     555             :     {
     556           4 :         poLayer = GetLayerByName(papszLayerList[iLayer]);
     557           4 :         if (nullptr == poLayer)
     558           0 :             continue;
     559             : 
     560           4 :         eType = wkbFlatten(poLayer->GetGeomType());
     561           4 :         if (eType == wkbLineString || eType == wkbMultiLineString)
     562             :         {
     563           2 :             paLineLayers.push_back(poLayer);
     564             :         }
     565           2 :         else if (eType == wkbPoint)
     566             :         {
     567           2 :             paPointLayers.push_back(poLayer);
     568             :         }
     569             :     }
     570             : 
     571           2 :     if (paLineLayers.empty() || paPointLayers.empty())
     572             :     {
     573           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
     574             :                  "Need at least one line (or "
     575             :                  "multiline) layer and one point layer to connect");
     576           0 :         return CE_Failure;
     577             :     }
     578             : 
     579             :     // now walk through all lines and find nearest points for line start and end
     580           4 :     for (size_t i = 0; i < paLineLayers.size(); ++i)
     581             :     {
     582           2 :         poLayer = paLineLayers[i];
     583             : 
     584           2 :         poLayer->ResetReading();
     585          68 :         for (auto &&poFeature : poLayer)
     586             :         {
     587          66 :             const OGRGeometry *poGeom = poFeature->GetGeometryRef();
     588          66 :             if (nullptr != poGeom)
     589             :             {
     590          66 :                 eType = wkbFlatten(poGeom->getGeometryType());
     591          66 :                 if (eType == wkbLineString)
     592             :                 {
     593          66 :                     const auto poLineString = poGeom->toLineString();
     594          66 :                     ConnectPointsByLine(poFeature->GetFID(), poLineString,
     595             :                                         paPointLayers, dfTolerance, dfCost,
     596          66 :                                         dfInvCost, eDir);
     597             :                 }
     598           0 :                 else if (eType == wkbMultiLineString)
     599             :                 {
     600           0 :                     const auto poMultiLineString = poGeom->toMultiLineString();
     601           0 :                     ConnectPointsByMultiline(
     602             :                         poFeature->GetFID(), poMultiLineString, paPointLayers,
     603           0 :                         dfTolerance, dfCost, dfInvCost, eDir);
     604             :                 }
     605             :             }
     606             :         }
     607             :     }
     608             : 
     609           2 :     return CE_None;
     610             : }
     611             : 
     612           0 : CPLErr GNMGenericNetwork::ChangeBlockState(GNMGFID nFID, bool bIsBlock)
     613             : {
     614           0 :     if (!m_bIsGraphLoaded && LoadGraph() != CE_None)
     615             :     {
     616           0 :         return CE_Failure;
     617             :     }
     618             : 
     619             :     // change block state in layer
     620           0 :     OGRLayer *poLayer = GetLayerByName(m_moFeatureFIDMap[nFID]);
     621           0 :     if (nullptr == poLayer)
     622             :     {
     623           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Failed to get layer '%s'.",
     624           0 :                  m_moFeatureFIDMap[nFID].c_str());
     625           0 :         return CE_Failure;
     626             :     }
     627             : 
     628           0 :     OGRFeature *poFeature = poLayer->GetFeature(nFID);
     629           0 :     if (nullptr == poFeature)
     630             :     {
     631           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     632             :                  "Failed to get feature '" GNMGFIDFormat "'.", nFID);
     633           0 :         return CE_Failure;
     634             :     }
     635             : 
     636           0 :     if (bIsBlock)
     637             :     {
     638           0 :         poFeature->SetField(GNM_SYSFIELD_BLOCKED, GNM_BLOCK_ALL);
     639             :     }
     640             :     else
     641             :     {
     642           0 :         poFeature->SetField(GNM_SYSFIELD_BLOCKED, GNM_BLOCK_NONE);
     643             :     }
     644             : 
     645           0 :     if (poLayer->SetFeature(poFeature) != OGRERR_NONE)
     646             :     {
     647           0 :         OGRFeature::DestroyFeature(poFeature);
     648           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Failed to update feature.");
     649           0 :         return CE_Failure;
     650             :     }
     651             : 
     652           0 :     OGRFeature::DestroyFeature(poFeature);
     653             : 
     654             :     GNMGFID nSrcFID, nTgtFID, nConFID;
     655             : 
     656             :     // change block state in graph layer
     657           0 :     m_poGraphLayer->ResetReading();
     658           0 :     while ((poFeature = m_poGraphLayer->GetNextFeature()) != nullptr)
     659             :     {
     660           0 :         nSrcFID = poFeature->GetFieldAsGNMGFID(GNM_SYSFIELD_SOURCE);
     661           0 :         nTgtFID = poFeature->GetFieldAsGNMGFID(GNM_SYSFIELD_TARGET);
     662           0 :         nConFID = poFeature->GetFieldAsGNMGFID(GNM_SYSFIELD_CONNECTOR);
     663           0 :         int nBlockState = poFeature->GetFieldAsInteger(GNM_SYSFIELD_BLOCKED);
     664             : 
     665           0 :         if (bIsBlock)
     666             :         {
     667           0 :             if (nSrcFID == nFID)
     668           0 :                 nBlockState |= GNM_BLOCK_SRC;
     669           0 :             else if (nTgtFID == nFID)
     670           0 :                 nBlockState |= GNM_BLOCK_TGT;
     671           0 :             else if (nConFID == nFID)
     672           0 :                 nBlockState |= GNM_BLOCK_CONN;
     673             : 
     674           0 :             poFeature->SetField(GNM_SYSFIELD_BLOCKED, nBlockState);
     675             :         }
     676             :         else
     677             :         {
     678           0 :             if (nSrcFID == nFID)
     679           0 :                 nBlockState &= ~GNM_BLOCK_SRC;
     680           0 :             else if (nTgtFID == nFID)
     681           0 :                 nBlockState &= ~GNM_BLOCK_TGT;
     682           0 :             else if (nConFID == nFID)
     683           0 :                 nBlockState &= ~GNM_BLOCK_CONN;
     684             : 
     685           0 :             poFeature->SetField(GNM_SYSFIELD_BLOCKED, nBlockState);
     686             :         }
     687             : 
     688           0 :         if (m_poGraphLayer->SetFeature(poFeature) != OGRERR_NONE)
     689             :         {
     690           0 :             OGRFeature::DestroyFeature(poFeature);
     691           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Failed to update feature.");
     692           0 :             return CE_Failure;
     693             :         }
     694             : 
     695           0 :         OGRFeature::DestroyFeature(poFeature);
     696             :     }
     697             : 
     698             :     // change block state in graph
     699           0 :     m_oGraph.ChangeBlockState(nFID, bIsBlock);
     700             : 
     701           0 :     return CE_None;
     702             : }
     703             : 
     704           0 : CPLErr GNMGenericNetwork::ChangeAllBlockState(bool bIsBlock)
     705             : {
     706           0 :     if (!m_bIsGraphLoaded && LoadGraph() != CE_None)
     707             :     {
     708           0 :         return CE_Failure;
     709             :     }
     710             : 
     711             :     OGRFeature *poFeature;
     712           0 :     m_poGraphLayer->ResetReading();
     713           0 :     while ((poFeature = m_poGraphLayer->GetNextFeature()) != nullptr)
     714             :     {
     715           0 :         if (bIsBlock)
     716             :         {
     717           0 :             poFeature->SetField(GNM_SYSFIELD_BLOCKED, GNM_BLOCK_ALL);
     718             :         }
     719             :         else
     720             :         {
     721           0 :             poFeature->SetField(GNM_SYSFIELD_BLOCKED, GNM_BLOCK_NONE);
     722             :         }
     723             : 
     724           0 :         if (m_poGraphLayer->SetFeature(poFeature) != OGRERR_NONE)
     725             :         {
     726           0 :             OGRFeature::DestroyFeature(poFeature);
     727           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Failed to update feature.");
     728           0 :             return CE_Failure;
     729             :         }
     730             : 
     731           0 :         OGRFeature::DestroyFeature(poFeature);
     732             :     }
     733             : 
     734             :     // change all network layers
     735             : 
     736           0 :     for (size_t i = 0; i < m_apoLayers.size(); ++i)
     737             :     {
     738           0 :         OGRLayer *poLayer = m_apoLayers[i];
     739           0 :         if (nullptr == poLayer)
     740           0 :             continue;
     741           0 :         while ((poFeature = poLayer->GetNextFeature()) != nullptr)
     742             :         {
     743           0 :             if (bIsBlock)
     744             :             {
     745           0 :                 poFeature->SetField(GNM_SYSFIELD_BLOCKED, GNM_BLOCK_ALL);
     746             :             }
     747             :             else
     748             :             {
     749           0 :                 poFeature->SetField(GNM_SYSFIELD_BLOCKED, GNM_BLOCK_NONE);
     750             :             }
     751             : 
     752           0 :             if (poLayer->SetFeature(poFeature) != OGRERR_NONE)
     753             :             {
     754           0 :                 OGRFeature::DestroyFeature(poFeature);
     755           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     756             :                          "Failed to update feature.");
     757           0 :                 return CE_Failure;
     758             :             }
     759             : 
     760           0 :             OGRFeature::DestroyFeature(poFeature);
     761             :         }
     762             :     }
     763             : 
     764           0 :     m_oGraph.ChangeAllBlockState(bIsBlock);
     765             : 
     766           0 :     return CE_None;
     767             : }
     768             : 
     769           5 : OGRLayer *GNMGenericNetwork::GetPath(GNMGFID nStartFID, GNMGFID nEndFID,
     770             :                                      GNMGraphAlgorithmType eAlgorithm,
     771             :                                      char **papszOptions)
     772             : {
     773             : 
     774           5 :     if (!m_bIsGraphLoaded && LoadGraph() != CE_None)
     775             :     {
     776           0 :         return nullptr;
     777             :     }
     778             : 
     779             :     GDALDriver *poMEMDrv =
     780           5 :         OGRSFDriverRegistrar::GetRegistrar()->GetDriverByName("Memory");
     781           5 :     if (poMEMDrv == nullptr)
     782             :     {
     783           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot load 'Memory' driver");
     784           0 :         return nullptr;
     785             :     }
     786             : 
     787             :     GDALDataset *poMEMDS =
     788           5 :         poMEMDrv->Create("dummy_name", 0, 0, 0, GDT_Unknown, nullptr);
     789           5 :     OGRSpatialReference oDstSpaRef(GetProjectionRef());
     790             :     OGRLayer *poMEMLayer =
     791           5 :         poMEMDS->CreateLayer(GetAlgorithmName(eAlgorithm, true), &oDstSpaRef,
     792             :                              wkbGeometryCollection, nullptr);
     793             : 
     794             :     OGRGNMWrappedResultLayer *poResLayer =
     795           5 :         new OGRGNMWrappedResultLayer(poMEMDS, poMEMLayer);
     796             : 
     797             :     const bool bReturnEdges =
     798           5 :         CPLFetchBool(papszOptions, GNM_MD_FETCHEDGES, true);
     799             :     const bool bReturnVertices =
     800           5 :         CPLFetchBool(papszOptions, GNM_MD_FETCHVERTEX, true);
     801             : 
     802           5 :     switch (eAlgorithm)
     803             :     {
     804           2 :         case GATDijkstraShortestPath:
     805             :         {
     806           4 :             GNMPATH path = m_oGraph.DijkstraShortestPath(nStartFID, nEndFID);
     807             : 
     808             :             // fill features in result layer
     809           2 :             FillResultLayer(poResLayer, path, 1, bReturnVertices, bReturnEdges);
     810             :         }
     811           2 :         break;
     812           2 :         case GATKShortestPath:
     813             :         {
     814             :             int nK =
     815           2 :                 atoi(CSLFetchNameValueDef(papszOptions, GNM_MD_NUM_PATHS, "1"));
     816             : 
     817           2 :             CPLDebug("GNM", "Search %d path(s)", nK);
     818             : 
     819             :             std::vector<GNMPATH> paths =
     820           4 :                 m_oGraph.KShortestPaths(nStartFID, nEndFID, nK);
     821             : 
     822             :             // fill features in result layer
     823           8 :             for (size_t i = 0; i < paths.size(); ++i)
     824             :             {
     825           6 :                 FillResultLayer(poResLayer, paths[i], static_cast<int>(i + 1),
     826           6 :                                 bReturnVertices, bReturnEdges);
     827             :             }
     828             :         }
     829           2 :         break;
     830           1 :         case GATConnectedComponents:
     831             :         {
     832           2 :             GNMVECTOR anEmitters;
     833           1 :             if (nullptr != papszOptions)
     834             :             {
     835             :                 char **papszEmitter =
     836           0 :                     CSLFetchNameValueMultiple(papszOptions, GNM_MD_EMITTER);
     837           0 :                 for (int i = 0; papszEmitter[i] != nullptr; ++i)
     838             :                 {
     839           0 :                     GNMGFID nEmitter = atol(papszEmitter[i]);
     840           0 :                     anEmitters.push_back(nEmitter);
     841             :                 }
     842           0 :                 CSLDestroy(papszEmitter);
     843             :             }
     844             : 
     845           1 :             if (nStartFID != -1)
     846             :             {
     847           1 :                 anEmitters.push_back(nStartFID);
     848             :             }
     849             : 
     850           1 :             if (nStartFID != -1)
     851             :             {
     852           1 :                 anEmitters.push_back(nEndFID);
     853             :             }
     854             : 
     855           2 :             GNMPATH path = m_oGraph.ConnectedComponents(anEmitters);
     856             : 
     857             :             // fill features in result layer
     858           1 :             FillResultLayer(poResLayer, path, 1, bReturnVertices, bReturnEdges);
     859             :         }
     860           1 :         break;
     861             :     }
     862             : 
     863           5 :     return poResLayer;
     864             : }
     865             : 
     866           0 : void GNMGenericNetwork::ConnectPointsByMultiline(
     867             :     GIntBig nFID, const OGRMultiLineString *poMultiLineString,
     868             :     const std::vector<OGRLayer *> &paPointLayers, double dfTolerance,
     869             :     double dfCost, double dfInvCost, GNMDirection eDir)
     870             : {
     871           0 :     VALIDATE_POINTER0(poMultiLineString,
     872             :                       "GNMGenericNetwork::ConnectPointsByMultiline");
     873           0 :     for (auto &&poLineString : poMultiLineString)
     874             :     {
     875           0 :         ConnectPointsByLine(nFID, poLineString, paPointLayers, dfTolerance,
     876           0 :                             dfCost, dfInvCost, eDir);
     877             :     }
     878             : }
     879             : 
     880          66 : void GNMGenericNetwork::ConnectPointsByLine(
     881             :     GIntBig nFID, const OGRLineString *poLineString,
     882             :     const std::vector<OGRLayer *> &paPointLayers, double dfTolerance,
     883             :     double dfCost, double dfInvCost, GNMDirection eDir)
     884             : {
     885          66 :     VALIDATE_POINTER0(poLineString, "GNMGenericNetwork::ConnectPointsByLine");
     886          66 :     OGRPoint oStartPoint, oEndPoint;
     887          66 :     poLineString->StartPoint(&oStartPoint);
     888          66 :     poLineString->EndPoint(&oEndPoint);
     889          66 :     double dfHalfTolerance = dfTolerance / 2;
     890             : 
     891             :     GNMGFID nSrcFID =
     892          66 :         FindNearestPoint(&oStartPoint, paPointLayers, dfHalfTolerance);
     893             :     GNMGFID nTgtFID =
     894          66 :         FindNearestPoint(&oEndPoint, paPointLayers, dfHalfTolerance);
     895             : 
     896          66 :     if (nSrcFID == -1 || nTgtFID == -1)
     897           0 :         return;
     898             : 
     899             :     // connect nSrcFID with nTgtFID via nFID
     900          66 :     ConnectFeatures(nSrcFID, nTgtFID, static_cast<GNMGFID>(nFID), dfCost,
     901          66 :                     dfInvCost, eDir);
     902             : }
     903             : 
     904         132 : GNMGFID GNMGenericNetwork::FindNearestPoint(
     905             :     const OGRPoint *poPoint, const std::vector<OGRLayer *> &paPointLayers,
     906             :     double dfTolerance)
     907             : {
     908         132 :     VALIDATE_POINTER1(poPoint, "GNMGenericNetwork::FindNearestPoint", -1);
     909         132 :     double dfMinX = poPoint->getX() - dfTolerance;
     910         132 :     double dfMinY = poPoint->getY() - dfTolerance;
     911         132 :     double dfMaxX = poPoint->getX() + dfTolerance;
     912         132 :     double dfMaxY = poPoint->getY() + dfTolerance;
     913             : 
     914             :     OGRFeature *poFeature;
     915             : 
     916         132 :     for (size_t i = 0; i < paPointLayers.size(); ++i)
     917             :     {
     918         132 :         OGRLayer *poLayer = paPointLayers[i];
     919             : 
     920         132 :         poLayer->SetSpatialFilterRect(dfMinX, dfMinY, dfMaxX, dfMaxY);
     921         132 :         poLayer->ResetReading();
     922         132 :         while ((poFeature = poLayer->GetNextFeature()) != nullptr)
     923             :         {
     924         132 :             GNMGFID nRetFID = poFeature->GetFieldAsGNMGFID(GNM_SYSFIELD_GFID);
     925         132 :             OGRFeature::DestroyFeature(poFeature);
     926         132 :             return nRetFID;
     927             :         }
     928             :     }
     929             : 
     930           0 :     return -1;
     931             : }
     932             : 
     933          66 : OGRFeature *GNMGenericNetwork::FindConnection(GNMGFID nSrcFID, GNMGFID nTgtFID,
     934             :                                               GNMGFID nConFID)
     935             : {
     936             : 
     937          66 :     CPLString soFilter;
     938             :     soFilter.Printf("%s = " GNMGFIDFormat " and %s = " GNMGFIDFormat
     939             :                     " and %s = " GNMGFIDFormat,
     940             :                     GNM_SYSFIELD_SOURCE, nSrcFID, GNM_SYSFIELD_TARGET, nTgtFID,
     941          66 :                     GNM_SYSFIELD_CONNECTOR, nConFID);
     942             : 
     943          66 :     CPLDebug("GNM", "Set attribute filter: %s", soFilter.c_str());
     944             : 
     945          66 :     m_poGraphLayer->SetAttributeFilter(soFilter);
     946          66 :     m_poGraphLayer->ResetReading();
     947          66 :     OGRFeature *f = m_poGraphLayer->GetNextFeature();
     948          66 :     m_poGraphLayer->SetAttributeFilter(nullptr);
     949         132 :     return f;
     950             : }
     951             : 
     952          16 : bool GNMGenericNetwork::SaveRules()
     953             : {
     954          16 :     if (!m_bIsRulesChanged)
     955          14 :         return true;
     956             : 
     957           2 :     if (DeleteAllRules() != CE_None)
     958           0 :         return false;
     959             : 
     960           2 :     bool bOK = true;
     961           4 :     for (int i = 0; i < static_cast<int>(m_asRules.size()); ++i)
     962             :     {
     963             :         auto poFeature =
     964           4 :             std::make_unique<OGRFeature>(m_poMetadataLayer->GetLayerDefn());
     965           2 :         poFeature->SetField(GNM_SYSFIELD_PARAMNAME,
     966             :                             CPLSPrintf("%s%d", GNM_MD_RULE, i + 1));
     967           2 :         poFeature->SetField(GNM_SYSFIELD_PARAMVALUE, m_asRules[i]);
     968           2 :         if (m_poMetadataLayer->CreateFeature(poFeature.get()) != OGRERR_NONE)
     969             :         {
     970           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Write rule '%s' failed",
     971           0 :                      m_asRules[i].c_str());
     972           0 :             bOK = false;
     973             :             // TODO: do we need interrupt here?
     974             :             // return CE_Failure;
     975             :         }
     976             :     }
     977           2 :     return bOK;
     978             : }
     979             : 
     980           0 : GNMGFID GNMGenericNetwork::GetNewVirtualFID()
     981             : {
     982           0 :     return --m_nVirtualConnectionGID;
     983             : }
     984             : 
     985           9 : void GNMGenericNetwork::FillResultLayer(OGRGNMWrappedResultLayer *poResLayer,
     986             :                                         const GNMPATH &path, int nNoOfPath,
     987             :                                         bool bReturnVertices, bool bReturnEdges)
     988             : {
     989         159 :     for (size_t i = 0; i < path.size(); ++i)
     990             :     {
     991         150 :         if (bReturnVertices)
     992             :         {
     993         150 :             GNMGFID nGFID = path[i].first;
     994             : 
     995             :             // TODO: create feature for virtual vertex
     996             :             // if(nGFID < -1) {...}
     997             : 
     998         300 :             CPLString soLayerName = m_moFeatureFIDMap[nGFID];
     999         150 :             OGRFeature *poFeature = GetFeatureByGlobalFID(nGFID);
    1000         150 :             if (nullptr != poFeature)
    1001             :             {
    1002         150 :                 poResLayer->InsertFeature(poFeature, soLayerName, nNoOfPath,
    1003         150 :                                           false);
    1004             : 
    1005         150 :                 OGRFeature::DestroyFeature(poFeature);
    1006             :             }
    1007             :         }
    1008             : 
    1009         150 :         if (bReturnEdges)
    1010             :         {
    1011         150 :             GNMGFID nGFID = path[i].second;
    1012             : 
    1013             :             // TODO: create feature for virtual edge
    1014             :             // if(nGFID < -1) {...}
    1015             : 
    1016         300 :             CPLString soLayerName = m_moFeatureFIDMap[nGFID];
    1017         150 :             OGRFeature *poFeature = GetFeatureByGlobalFID(nGFID);
    1018         150 :             if (nullptr != poFeature)
    1019             :             {
    1020         142 :                 poResLayer->InsertFeature(poFeature, soLayerName, nNoOfPath,
    1021         142 :                                           true);
    1022         142 :                 OGRFeature::DestroyFeature(poFeature);
    1023             :             }
    1024             :         }
    1025             :     }
    1026           9 : }
    1027             : 
    1028           6 : CPLErr GNMGenericNetwork::CheckLayerDriver(const char *pszDefaultDriverName,
    1029             :                                            char **papszOptions)
    1030             : {
    1031           6 :     if (nullptr == m_poLayerDriver)
    1032             :     {
    1033           2 :         const char *pszDriverName = CSLFetchNameValueDef(
    1034             :             papszOptions, GNM_MD_FORMAT, pszDefaultDriverName);
    1035             : 
    1036           2 :         if (!CheckStorageDriverSupport(pszDriverName))
    1037             :         {
    1038           0 :             CPLError(CE_Failure, CPLE_IllegalArg,
    1039             :                      "%s driver not supported as network storage",
    1040             :                      pszDriverName);
    1041           0 :             return CE_Failure;
    1042             :         }
    1043             : 
    1044           2 :         m_poLayerDriver =
    1045           2 :             GetGDALDriverManager()->GetDriverByName(pszDriverName);
    1046           2 :         if (nullptr == m_poLayerDriver)
    1047             :         {
    1048           0 :             CPLError(CE_Failure, CPLE_IllegalArg, "%s driver not available",
    1049             :                      pszDriverName);
    1050           0 :             return CE_Failure;
    1051             :         }
    1052             :     }
    1053           6 :     return CE_None;
    1054             : }
    1055             : 
    1056           2 : CPLErr GNMGenericNetwork::CreateMetadataLayer(GDALDataset *const pDS,
    1057             :                                               int nVersion, size_t nFieldSize)
    1058             : {
    1059             :     OGRLayer *pMetadataLayer =
    1060           2 :         pDS->CreateLayer(GNM_SYSLAYER_META, nullptr, wkbNone, nullptr);
    1061           2 :     if (nullptr == pMetadataLayer)
    1062             :     {
    1063           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Creation of '%s' layer failed",
    1064             :                  GNM_SYSLAYER_META);
    1065           0 :         return CE_Failure;
    1066             :     }
    1067             : 
    1068           4 :     OGRFieldDefn oFieldKey(GNM_SYSFIELD_PARAMNAME, OFTString);
    1069           2 :     oFieldKey.SetWidth(static_cast<int>(nFieldSize));
    1070           4 :     OGRFieldDefn oFieldValue(GNM_SYSFIELD_PARAMVALUE, OFTString);
    1071           2 :     oFieldValue.SetWidth(static_cast<int>(nFieldSize));
    1072             : 
    1073           4 :     if (pMetadataLayer->CreateField(&oFieldKey) != OGRERR_NONE ||
    1074           2 :         pMetadataLayer->CreateField(&oFieldValue) != OGRERR_NONE)
    1075             :     {
    1076           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1077             :                  "Creation of layer '%s' fields failed", GNM_SYSLAYER_META);
    1078           0 :         return CE_Failure;
    1079             :     }
    1080             : 
    1081             :     OGRFeature *poFeature;
    1082             : 
    1083             :     // write name
    1084           2 :     poFeature = OGRFeature::CreateFeature(pMetadataLayer->GetLayerDefn());
    1085           2 :     poFeature->SetField(GNM_SYSFIELD_PARAMNAME, GNM_MD_NAME);
    1086           2 :     poFeature->SetField(GNM_SYSFIELD_PARAMVALUE, m_soName);
    1087           2 :     if (pMetadataLayer->CreateFeature(poFeature) != OGRERR_NONE)
    1088             :     {
    1089           0 :         OGRFeature::DestroyFeature(poFeature);
    1090           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Write GNM name failed");
    1091           0 :         return CE_Failure;
    1092             :     }
    1093           2 :     OGRFeature::DestroyFeature(poFeature);
    1094             : 
    1095             :     // write version
    1096           2 :     poFeature = OGRFeature::CreateFeature(pMetadataLayer->GetLayerDefn());
    1097           2 :     poFeature->SetField(GNM_SYSFIELD_PARAMNAME, GNM_MD_VERSION);
    1098           2 :     poFeature->SetField(GNM_SYSFIELD_PARAMVALUE, CPLSPrintf("%d", nVersion));
    1099           2 :     if (pMetadataLayer->CreateFeature(poFeature) != OGRERR_NONE)
    1100             :     {
    1101           0 :         OGRFeature::DestroyFeature(poFeature);
    1102           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Write GNM version failed");
    1103           0 :         return CE_Failure;
    1104             :     }
    1105           2 :     OGRFeature::DestroyFeature(poFeature);
    1106             : 
    1107             :     // write description
    1108           2 :     if (!sDescription.empty())
    1109             :     {
    1110           2 :         poFeature = OGRFeature::CreateFeature(pMetadataLayer->GetLayerDefn());
    1111           2 :         poFeature->SetField(GNM_SYSFIELD_PARAMNAME, GNM_MD_DESCR);
    1112           2 :         poFeature->SetField(GNM_SYSFIELD_PARAMVALUE, sDescription);
    1113           2 :         if (pMetadataLayer->CreateFeature(poFeature) != OGRERR_NONE)
    1114             :         {
    1115           0 :             OGRFeature::DestroyFeature(poFeature);
    1116           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1117             :                      "Write GNM description failed");
    1118           0 :             return CE_Failure;
    1119             :         }
    1120           2 :         OGRFeature::DestroyFeature(poFeature);
    1121             :     }
    1122             : 
    1123             :     // write srs if < 254 or create file
    1124           2 :     if (!m_oSRS.IsEmpty())
    1125             :     {
    1126           2 :         char *pszWKT = nullptr;
    1127           2 :         m_oSRS.exportToWkt(&pszWKT);
    1128           2 :         const std::string soSRS = pszWKT ? pszWKT : "";
    1129           2 :         CPLFree(pszWKT);
    1130           2 :         if (soSRS.size() >= nFieldSize)
    1131             :         {
    1132             :             // cppcheck-suppress knownConditionTrueFalse
    1133           2 :             if (StoreNetworkSrs() != CE_None)
    1134           0 :                 return CE_Failure;
    1135             :         }
    1136             :         else
    1137             :         {
    1138             :             poFeature =
    1139           0 :                 OGRFeature::CreateFeature(pMetadataLayer->GetLayerDefn());
    1140           0 :             poFeature->SetField(GNM_SYSFIELD_PARAMNAME, GNM_MD_SRS);
    1141           0 :             poFeature->SetField(GNM_SYSFIELD_PARAMVALUE, soSRS.c_str());
    1142           0 :             if (pMetadataLayer->CreateFeature(poFeature) != OGRERR_NONE)
    1143             :             {
    1144           0 :                 OGRFeature::DestroyFeature(poFeature);
    1145           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "Write GNM SRS failed");
    1146           0 :                 return CE_Failure;
    1147             :             }
    1148           0 :             OGRFeature::DestroyFeature(poFeature);
    1149             :         }
    1150             :     }
    1151             : 
    1152           2 :     m_poMetadataLayer = pMetadataLayer;
    1153             : 
    1154           2 :     m_nVersion = nVersion;
    1155             : 
    1156             :     // create default rule
    1157           2 :     return CreateRule("ALLOW CONNECTS ANY");
    1158             : }
    1159             : 
    1160           0 : CPLErr GNMGenericNetwork::StoreNetworkSrs()
    1161             : {
    1162           0 :     return CE_Failure;
    1163             : }
    1164             : 
    1165           0 : CPLErr GNMGenericNetwork::LoadNetworkSrs()
    1166             : {
    1167           0 :     return CE_Failure;
    1168             : }
    1169             : 
    1170           2 : CPLErr GNMGenericNetwork::CreateGraphLayer(GDALDataset *const pDS)
    1171             : {
    1172           2 :     m_poGraphLayer =
    1173           2 :         pDS->CreateLayer(GNM_SYSLAYER_GRAPH, nullptr, wkbNone, nullptr);
    1174           2 :     if (nullptr == m_poGraphLayer)
    1175             :     {
    1176           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Creation of '%s' layer failed",
    1177             :                  GNM_SYSLAYER_GRAPH);
    1178           0 :         return CE_Failure;
    1179             :     }
    1180             : 
    1181           4 :     OGRFieldDefn oFieldSrc(GNM_SYSFIELD_SOURCE, GNMGFIDInt);
    1182           4 :     OGRFieldDefn oFieldDst(GNM_SYSFIELD_TARGET, GNMGFIDInt);
    1183           4 :     OGRFieldDefn oFieldConnector(GNM_SYSFIELD_CONNECTOR, GNMGFIDInt);
    1184           4 :     OGRFieldDefn oFieldCost(GNM_SYSFIELD_COST, OFTReal);
    1185           4 :     OGRFieldDefn oFieldInvCost(GNM_SYSFIELD_INVCOST, OFTReal);
    1186           4 :     OGRFieldDefn oFieldDir(GNM_SYSFIELD_DIRECTION, OFTInteger);
    1187           4 :     OGRFieldDefn oFieldBlock(GNM_SYSFIELD_BLOCKED, OFTInteger);
    1188             : 
    1189           2 :     if (m_poGraphLayer->CreateField(&oFieldSrc) != OGRERR_NONE ||
    1190           2 :         m_poGraphLayer->CreateField(&oFieldDst) != OGRERR_NONE ||
    1191           2 :         m_poGraphLayer->CreateField(&oFieldConnector) != OGRERR_NONE ||
    1192           2 :         m_poGraphLayer->CreateField(&oFieldCost) != OGRERR_NONE ||
    1193           2 :         m_poGraphLayer->CreateField(&oFieldInvCost) != OGRERR_NONE ||
    1194           6 :         m_poGraphLayer->CreateField(&oFieldDir) != OGRERR_NONE ||
    1195           2 :         m_poGraphLayer->CreateField(&oFieldBlock) != OGRERR_NONE)
    1196             :     {
    1197           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1198             :                  "Creation of layer '%s' fields failed", GNM_SYSLAYER_GRAPH);
    1199           0 :         return CE_Failure;
    1200             :     }
    1201             : 
    1202           2 :     return CE_None;
    1203             : }
    1204             : 
    1205           2 : CPLErr GNMGenericNetwork::CreateFeaturesLayer(GDALDataset *const pDS)
    1206             : {
    1207           2 :     m_poFeaturesLayer =
    1208           2 :         pDS->CreateLayer(GNM_SYSLAYER_FEATURES, nullptr, wkbNone, nullptr);
    1209           2 :     if (nullptr == m_poFeaturesLayer)
    1210             :     {
    1211           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Creation of '%s' layer failed",
    1212             :                  GNM_SYSLAYER_FEATURES);
    1213           0 :         return CE_Failure;
    1214             :     }
    1215             : 
    1216           4 :     OGRFieldDefn oFieldGID(GNM_SYSFIELD_GFID, GNMGFIDInt);
    1217           4 :     OGRFieldDefn oFieldLayerName(GNM_SYSFIELD_LAYERNAME, OFTString);
    1218           2 :     oFieldLayerName.SetWidth(254);
    1219             : 
    1220           4 :     if (m_poFeaturesLayer->CreateField(&oFieldGID) != OGRERR_NONE ||
    1221           2 :         m_poFeaturesLayer->CreateField(&oFieldLayerName) != OGRERR_NONE)
    1222             :     {
    1223           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1224             :                  "Creation of layer '%s' fields failed", GNM_SYSLAYER_FEATURES);
    1225           0 :         return CE_Failure;
    1226             :     }
    1227             : 
    1228           2 :     return CE_None;
    1229             : }
    1230             : 
    1231          14 : CPLErr GNMGenericNetwork::LoadMetadataLayer(GDALDataset *const pDS)
    1232             : {
    1233             :     // read version, description, SRS, classes, rules
    1234          14 :     m_poMetadataLayer = pDS->GetLayerByName(GNM_SYSLAYER_META);
    1235          14 :     if (nullptr == m_poMetadataLayer)
    1236             :     {
    1237           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Loading of '%s' layer failed",
    1238             :                  GNM_SYSLAYER_META);
    1239           0 :         return CE_Failure;
    1240             :     }
    1241             : 
    1242          28 :     std::map<int, GNMRule> moRules;
    1243          14 :     int nRulePrefixLen = static_cast<int>(CPLStrnlen(GNM_MD_RULE, 255));
    1244             :     OGRFeature *poFeature;
    1245          14 :     m_poMetadataLayer->ResetReading();
    1246          70 :     while ((poFeature = m_poMetadataLayer->GetNextFeature()) != nullptr)
    1247             :     {
    1248          56 :         const char *pKey = poFeature->GetFieldAsString(GNM_SYSFIELD_PARAMNAME);
    1249             :         const char *pValue =
    1250          56 :             poFeature->GetFieldAsString(GNM_SYSFIELD_PARAMVALUE);
    1251             : 
    1252          56 :         CPLDebug("GNM", "Load metadata. Key: %s, value %s", pKey, pValue);
    1253             : 
    1254          56 :         if (EQUAL(pKey, GNM_MD_NAME))
    1255             :         {
    1256          14 :             m_soName = pValue;
    1257             :         }
    1258          42 :         else if (EQUAL(pKey, GNM_MD_DESCR))
    1259             :         {
    1260          14 :             sDescription = pValue;
    1261             :         }
    1262          28 :         else if (EQUAL(pKey, GNM_MD_SRS))
    1263             :         {
    1264           0 :             m_oSRS.importFromWkt(pValue);
    1265             :         }
    1266          28 :         else if (EQUAL(pKey, GNM_MD_VERSION))
    1267             :         {
    1268          14 :             m_nVersion = atoi(pValue);
    1269             :         }
    1270          14 :         else if (EQUALN(pKey, GNM_MD_RULE, nRulePrefixLen))
    1271             :         {
    1272          14 :             moRules[atoi(pKey + nRulePrefixLen)] = GNMRule(pValue);
    1273             :         }
    1274             : 
    1275          56 :         OGRFeature::DestroyFeature(poFeature);
    1276             :     }
    1277             : 
    1278          28 :     for (std::map<int, GNMRule>::iterator it = moRules.begin();
    1279          42 :          it != moRules.end(); ++it)
    1280             :     {
    1281          14 :         if (it->second.IsValid())
    1282          14 :             m_asRules.push_back(it->second);
    1283             :     }
    1284             : 
    1285          14 :     if (!m_oSRS.IsEmpty())
    1286             :     {
    1287             :         // cppcheck-suppress knownConditionTrueFalse
    1288           0 :         if (LoadNetworkSrs() != CE_None)
    1289           0 :             return CE_Failure;
    1290             :     }
    1291             : 
    1292          14 :     return CE_None;
    1293             : }
    1294             : 
    1295          14 : CPLErr GNMGenericNetwork::LoadGraphLayer(GDALDataset *const pDS)
    1296             : {
    1297          14 :     m_poGraphLayer = pDS->GetLayerByName(GNM_SYSLAYER_GRAPH);
    1298          14 :     if (nullptr == m_poGraphLayer)
    1299             :     {
    1300           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Loading of '%s' layer failed",
    1301             :                  GNM_SYSLAYER_GRAPH);
    1302           0 :         return CE_Failure;
    1303             :     }
    1304             : 
    1305          14 :     return CE_None;
    1306             : }
    1307             : 
    1308           7 : CPLErr GNMGenericNetwork::LoadGraph()
    1309             : {
    1310           7 :     if (m_bIsGraphLoaded)
    1311           0 :         return CE_None;
    1312             : 
    1313           7 :     if (nullptr == m_poGraphLayer)
    1314             :     {
    1315           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Loading of graph data failed");
    1316           0 :         return CE_Failure;
    1317             :     }
    1318             : 
    1319             :     OGRFeature *poFeature;
    1320           7 :     m_poGraphLayer->ResetReading();
    1321             :     GNMGFID nSrcFID, nTgtFID, nConFID;
    1322             :     double dfCost, dfInvCost;
    1323         172 :     while ((poFeature = m_poGraphLayer->GetNextFeature()) != nullptr)
    1324             :     {
    1325         165 :         nSrcFID = poFeature->GetFieldAsGNMGFID(GNM_SYSFIELD_SOURCE);
    1326         165 :         nTgtFID = poFeature->GetFieldAsGNMGFID(GNM_SYSFIELD_TARGET);
    1327         165 :         nConFID = poFeature->GetFieldAsGNMGFID(GNM_SYSFIELD_CONNECTOR);
    1328         165 :         dfCost = poFeature->GetFieldAsDouble(GNM_SYSFIELD_COST);
    1329         165 :         dfInvCost = poFeature->GetFieldAsDouble(GNM_SYSFIELD_INVCOST);
    1330             :         GNMDirection eDir =
    1331         165 :             poFeature->GetFieldAsInteger(GNM_SYSFIELD_DIRECTION);
    1332             : 
    1333         165 :         int nBlockState = poFeature->GetFieldAsInteger(GNM_SYSFIELD_BLOCKED);
    1334             : 
    1335         165 :         bool bIsBlock = GNM_BLOCK_NONE != nBlockState;
    1336             : 
    1337         165 :         m_oGraph.AddEdge(nConFID, nSrcFID, nTgtFID, eDir == GNM_EDGE_DIR_BOTH,
    1338             :                          dfCost, dfInvCost);
    1339             : 
    1340         165 :         if (bIsBlock)
    1341             :         {
    1342           0 :             if (nBlockState & GNM_BLOCK_SRC)
    1343           0 :                 m_oGraph.ChangeBlockState(nSrcFID, bIsBlock);
    1344           0 :             if (nBlockState & GNM_BLOCK_TGT)
    1345           0 :                 m_oGraph.ChangeBlockState(nTgtFID, bIsBlock);
    1346           0 :             if (nBlockState & GNM_BLOCK_CONN)
    1347           0 :                 m_oGraph.ChangeBlockState(nConFID, bIsBlock);
    1348             :         }
    1349             : 
    1350         165 :         if (nConFID < m_nVirtualConnectionGID)
    1351           0 :             m_nVirtualConnectionGID = nConFID;
    1352             : 
    1353         165 :         OGRFeature::DestroyFeature(poFeature);
    1354             :     }
    1355             : 
    1356           7 :     m_bIsGraphLoaded = true;
    1357           7 :     return CE_None;
    1358             : }
    1359             : 
    1360          14 : CPLErr GNMGenericNetwork::LoadFeaturesLayer(GDALDataset *const pDS)
    1361             : {
    1362          14 :     m_poFeaturesLayer = pDS->GetLayerByName(GNM_SYSLAYER_FEATURES);
    1363          14 :     if (nullptr == m_poFeaturesLayer)
    1364             :     {
    1365           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Loading of '%s' layer failed",
    1366             :                  GNM_SYSLAYER_FEATURES);
    1367           0 :         return CE_Failure;
    1368             :     }
    1369             : 
    1370             :     OGRFeature *poFeature;
    1371          14 :     m_poFeaturesLayer->ResetReading();
    1372         687 :     while ((poFeature = m_poFeaturesLayer->GetNextFeature()) != nullptr)
    1373             :     {
    1374         673 :         GNMGFID nFID = poFeature->GetFieldAsGNMGFID(GNM_SYSFIELD_GFID);
    1375             :         const char *pFeatureClass =
    1376         673 :             poFeature->GetFieldAsString(GNM_SYSFIELD_LAYERNAME);
    1377             : 
    1378         673 :         if (nFID >= m_nGID)
    1379         673 :             m_nGID = nFID + 1;
    1380             : 
    1381         673 :         m_moFeatureFIDMap[nFID] = pFeatureClass;
    1382             : 
    1383             :         // Load network layer. No error handling as we want to load whole
    1384             :         // network
    1385         673 :         LoadNetworkLayer(pFeatureClass);
    1386             : 
    1387         673 :         OGRFeature::DestroyFeature(poFeature);
    1388             :     }
    1389          14 :     return CE_None;
    1390             : }
    1391             : 
    1392           4 : int GNMGenericNetwork::TestCapability(const char *pszCap)
    1393             : 
    1394             : {
    1395           4 :     if (EQUAL(pszCap, ODsCCreateLayer))
    1396           4 :         return TRUE;
    1397           0 :     else if (EQUAL(pszCap, ODsCDeleteLayer))
    1398           0 :         return TRUE;
    1399             :     else
    1400           0 :         return FALSE;
    1401             : }
    1402             : 
    1403           4 : OGRLayer *GNMGenericNetwork::CopyLayer(OGRLayer *poSrcLayer,
    1404             :                                        const char *pszNewName,
    1405             :                                        char **papszOptions)
    1406             : {
    1407           8 :     CPLStringList aosOptions(CSLDuplicate(papszOptions));
    1408           4 :     aosOptions.SetNameValue("DST_SRSWKT", GetProjectionRef());
    1409           8 :     return GDALDataset::CopyLayer(poSrcLayer, pszNewName, aosOptions.List());
    1410             : }
    1411             : 
    1412           0 : int GNMGenericNetwork::CloseDependentDatasets()
    1413             : {
    1414           0 :     size_t nCount = m_apoLayers.size();
    1415           0 :     for (size_t i = 0; i < nCount; ++i)
    1416             :     {
    1417           0 :         delete m_apoLayers[i];
    1418             :     }
    1419           0 :     m_apoLayers.clear();
    1420             : 
    1421           0 :     GNMNetwork::CloseDependentDatasets();
    1422             : 
    1423           0 :     return nCount > 0 ? TRUE : FALSE;
    1424             : }
    1425             : 
    1426          16 : CPLErr GNMGenericNetwork::FlushCache(bool bAtClosing)
    1427             : {
    1428          16 :     CPLErr eErr = CE_None;
    1429          16 :     if (!SaveRules())
    1430           0 :         eErr = CE_Failure;
    1431             : 
    1432          16 :     if (GNMNetwork::FlushCache(bAtClosing) != CE_None)
    1433           0 :         eErr = CE_Failure;
    1434          16 :     return eErr;
    1435             : }
    1436             : 
    1437             : //--- C API --------------------------------------------------------------------
    1438             : 
    1439           0 : CPLErr CPL_STDCALL GNMConnectFeatures(GNMGenericNetworkH hNet, GNMGFID nSrcFID,
    1440             :                                       GNMGFID nTgtFID, GNMGFID nConFID,
    1441             :                                       double dfCost, double dfInvCost,
    1442             :                                       GNMDirection eDir)
    1443             : {
    1444           0 :     VALIDATE_POINTER1(hNet, "GNMConnectFeatures", CE_Failure);
    1445             : 
    1446           0 :     return GNMGenericNetwork::FromHandle(hNet)->ConnectFeatures(
    1447           0 :         nSrcFID, nTgtFID, nConFID, dfCost, dfInvCost, eDir);
    1448             : }
    1449             : 
    1450           0 : CPLErr CPL_STDCALL GNMDisconnectFeatures(GNMGenericNetworkH hNet,
    1451             :                                          GNMGFID nSrcFID, GNMGFID nTgtFID,
    1452             :                                          GNMGFID nConFID)
    1453             : {
    1454           0 :     VALIDATE_POINTER1(hNet, "GNMDisconnectFeatures", CE_Failure);
    1455             : 
    1456           0 :     return GNMGenericNetwork::FromHandle(hNet)->DisconnectFeatures(
    1457           0 :         nSrcFID, nTgtFID, nConFID);
    1458             : }
    1459             : 
    1460           0 : CPLErr CPL_STDCALL GNMDisconnectFeaturesWithId(GNMGenericNetworkH hNet,
    1461             :                                                GNMGFID nFID)
    1462             : {
    1463           0 :     VALIDATE_POINTER1(hNet, "GNMDisconnectFeaturesWithId", CE_Failure);
    1464             : 
    1465           0 :     return GNMGenericNetwork::FromHandle(hNet)->DisconnectFeaturesWithId(nFID);
    1466             : }
    1467             : 
    1468           0 : CPLErr CPL_STDCALL GNMReconnectFeatures(GNMGenericNetworkH hNet,
    1469             :                                         GNMGFID nSrcFID, GNMGFID nTgtFID,
    1470             :                                         GNMGFID nConFID, double dfCost,
    1471             :                                         double dfInvCost, GNMDirection eDir)
    1472             : {
    1473           0 :     VALIDATE_POINTER1(hNet, "GNMReconnectFeatures", CE_Failure);
    1474             : 
    1475           0 :     return GNMGenericNetwork::FromHandle(hNet)->ReconnectFeatures(
    1476           0 :         nSrcFID, nTgtFID, nConFID, dfCost, dfInvCost, eDir);
    1477             : }
    1478             : 
    1479           0 : CPLErr CPL_STDCALL GNMCreateRule(GNMGenericNetworkH hNet,
    1480             :                                  const char *pszRuleStr)
    1481             : {
    1482           0 :     VALIDATE_POINTER1(hNet, "GNMCreateRule", CE_Failure);
    1483             : 
    1484           0 :     return GNMGenericNetwork::FromHandle(hNet)->CreateRule(pszRuleStr);
    1485             : }
    1486             : 
    1487           0 : CPLErr CPL_STDCALL GNMDeleteAllRules(GNMGenericNetworkH hNet)
    1488             : {
    1489           0 :     VALIDATE_POINTER1(hNet, "GNMDeleteAllRules", CE_Failure);
    1490             : 
    1491           0 :     return GNMGenericNetwork::FromHandle(hNet)->DeleteAllRules();
    1492             : }
    1493             : 
    1494           0 : CPLErr CPL_STDCALL GNMDeleteRule(GNMGenericNetworkH hNet,
    1495             :                                  const char *pszRuleStr)
    1496             : {
    1497           0 :     VALIDATE_POINTER1(hNet, "GNMDeleteRule", CE_Failure);
    1498             : 
    1499           0 :     return GNMGenericNetwork::FromHandle(hNet)->DeleteRule(pszRuleStr);
    1500             : }
    1501             : 
    1502           0 : char **CPL_STDCALL GNMGetRules(GNMGenericNetworkH hNet)
    1503             : {
    1504           0 :     VALIDATE_POINTER1(hNet, "GNMDeleteRule", nullptr);
    1505             : 
    1506           0 :     return GNMGenericNetwork::FromHandle(hNet)->GetRules();
    1507             : }
    1508             : 
    1509           1 : CPLErr CPL_STDCALL GNMConnectPointsByLines(GNMGenericNetworkH hNet,
    1510             :                                            char **papszLayerList,
    1511             :                                            double dfTolerance, double dfCost,
    1512             :                                            double dfInvCost, GNMDirection eDir)
    1513             : {
    1514           1 :     VALIDATE_POINTER1(hNet, "GNMConnectPointsByLines", CE_Failure);
    1515             : 
    1516           2 :     return GNMGenericNetwork::FromHandle(hNet)->ConnectPointsByLines(
    1517           1 :         papszLayerList, dfTolerance, dfCost, dfInvCost, eDir);
    1518             : }
    1519             : 
    1520           0 : CPLErr CPL_STDCALL GNMChangeBlockState(GNMGenericNetworkH hNet, GNMGFID nFID,
    1521             :                                        bool bIsBlock)
    1522             : {
    1523           0 :     VALIDATE_POINTER1(hNet, "GNMChangeBlockState", CE_Failure);
    1524             : 
    1525           0 :     return GNMGenericNetwork::FromHandle(hNet)->ChangeBlockState(nFID,
    1526           0 :                                                                  bIsBlock);
    1527             : }
    1528             : 
    1529           0 : CPLErr CPL_STDCALL GNMChangeAllBlockState(GNMGenericNetworkH hNet, int bIsBlock)
    1530             : {
    1531           0 :     VALIDATE_POINTER1(hNet, "GNMChangeAllBlockState", CE_Failure);
    1532             : 
    1533           0 :     return GNMGenericNetwork::FromHandle(hNet)->ChangeAllBlockState(bIsBlock ==
    1534           0 :                                                                     TRUE);
    1535             : }
    1536             : 
    1537             : //! @endcond

Generated by: LCOV version 1.14