1 /******************************************************************************
2  * Project:  geography network utility
3  * Purpose:  Analyse GNM networks
4  * Authors:  Mikhail Gusev, gusevmihs at gmail dot com
5  *           Dmitry Baryshnikov, polimax@mail.ru
6  *
7  ******************************************************************************
8  * Copyright (C) 2014 Mikhail Gusev
9  * Copyright (c) 2014-2015, NextGIS <info@nextgis.com>
10  *
11  * Permission is hereby granted, free of charge, to any person obtaining a
12  * copy of this software and associated documentation files (the "Software"),
13  * to deal in the Software without restriction, including without limitation
14  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15  * and/or sell copies of the Software, and to permit persons to whom the
16  * Software is furnished to do so, subject to the following conditions:
17  *
18  * The above copyright notice and this permission notice shall be included
19  * in all copies or substantial portions of the Software.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27  * DEALINGS IN THE SOFTWARE.
28  ****************************************************************************/
29 
30 #include "commonutils.h"
31 #include "gdal_version.h"
32 #include "gnm.h"
33 #include "gnm_priv.h"
34 #include "ogr_p.h"
35 
36 CPL_CVSID("$Id: gnmanalyse.cpp 2dd182b498f0381aa3bc200928681dfbcf2ecfa6 2020-02-04 10:22:31 +0100 Enrico Weigelt, metux IT consult $")
37 
38 enum operation
39 {
40     op_unknown = 0, /** no operation */
41     op_dijkstra,    /** create shortest path using Dijkstra algorithm */
42     op_kpaths,      /** create k shortest paths using Yens algorithm */
43     op_resource    /** create resource distribution layer */
44 };
45 
46 /************************************************************************/
47 /*                               Usage()                                */
48 /************************************************************************/
Usage(const char * pszAdditionalMsg,int bShort=TRUE)49 static void Usage(const char* pszAdditionalMsg, int bShort = TRUE)
50 {
51     printf("Usage: gnmanalyse [--help][-q][-quiet][--long-usage]\n"
52            "                  [dijkstra start_gfid end_gfid [[-alo NAME=VALUE] ...]]\n"
53            "                  [kpaths start_gfid end_gfid k [[-alo NAME=VALUE] ...]]\n"
54            "                  [resource [[-alo NAME=VALUE] ...]]\n"
55            "                  [-ds ds_name][-f ds_format][-l layer_name]\n"
56            "                  [[-dsco NAME=VALUE] ...][-lco NAME=VALUE]\n"
57            "                  gnm_name\n");
58 
59     if (bShort)
60     {
61         printf("\nNote: gnmanalyse --long-usage for full help.\n");
62         if (pszAdditionalMsg)
63             fprintf(stderr, "\nFAILURE: %s\n", pszAdditionalMsg);
64         exit(1);
65     }
66 
67     printf("\n   dijkstra start_gfid end_gfid: calculates the best path between two points using Dijkstra algorithm from start_gfid point to end_gfid point\n"
68            "   kpaths start_gfid end_gfid k: calculates k (up to 10) best paths between two points using Yen\'s algorithm (which internally uses Dijkstra algorithm for single path calculating) from start_gfid point to end_gfid point\n"
69            "   resource: calculates the \"resource distribution\". The connected components search is performed using breadth-first search and starting from that features which are marked by rules as \'EMITTERS\'\n"
70            "   -ds ds_name: the name&path of the dataset to save the layer with resulting paths. Not need to be existed dataset\n"
71            "   -f ds_format: define this to set the format of newly created dataset\n"
72            "   -l layer_name: the name of the resulting layer. If the layer exists already - it will be rewritten. For K shortest paths several layers are created in format layer_nameN, where N - is number of the path (0 - is the most shortest one)\n"
73            "   -dsco NAME=VALUE: Dataset creation option (format specific)\n"
74            "   -lco  NAME=VALUE: Layer creation option (format specific)\n"
75            "   -alo  NAME=VALUE: Algorithm option (format specific)\n"
76            "   gnm_name: the network to work with (path and name)\n"
77            );
78 
79     if (pszAdditionalMsg)
80         fprintf(stderr, "\nFAILURE: %s\n", pszAdditionalMsg);
81 
82     exit(1);
83 }
84 
Usage(int bShort=TRUE)85 static void Usage(int bShort = TRUE)
86 {
87     Usage(nullptr, bShort);
88 }
89 
90 /************************************************************************/
91 /*                   GetLayerAndOverwriteIfNecessary()                  */
92 /************************************************************************/
93 
GetLayerAndOverwriteIfNecessary(GDALDataset * poDstDS,const char * pszNewLayerName,int bOverwrite,int * pbErrorOccurred)94 static OGRLayer* GetLayerAndOverwriteIfNecessary(GDALDataset *poDstDS,
95                                                  const char* pszNewLayerName,
96                                                  int bOverwrite,
97                                                  int* pbErrorOccurred)
98 {
99     if( pbErrorOccurred )
100         *pbErrorOccurred = FALSE;
101 
102     /* GetLayerByName() can instantiate layers that would have been */
103     /* 'hidden' otherwise, for example, non-spatial tables in a */
104     /* PostGIS-enabled database, so this apparently useless command is */
105     /* not useless... (#4012) */
106     CPLPushErrorHandler(CPLQuietErrorHandler);
107     OGRLayer* poDstLayer = poDstDS->GetLayerByName(pszNewLayerName);
108     CPLPopErrorHandler();
109     CPLErrorReset();
110 
111     int iLayer = -1;
112     if (poDstLayer != nullptr)
113     {
114         int nLayerCount = poDstDS->GetLayerCount();
115         for( iLayer = 0; iLayer < nLayerCount; iLayer++ )
116         {
117             OGRLayer        *poLayer = poDstDS->GetLayer(iLayer);
118             if (poLayer == poDstLayer)
119                 break;
120         }
121 
122         if (iLayer == nLayerCount)
123             /* should not happen with an ideal driver */
124             poDstLayer = nullptr;
125     }
126 
127 /* -------------------------------------------------------------------- */
128 /*      If the user requested overwrite, and we have the layer in       */
129 /*      question we need to delete it now so it will get recreated      */
130 /*      (overwritten).                                                  */
131 /* -------------------------------------------------------------------- */
132     if( poDstLayer != nullptr && bOverwrite )
133     {
134         if( poDstDS->DeleteLayer( iLayer ) != OGRERR_NONE )
135         {
136             fprintf( stderr,
137                      "DeleteLayer() failed when overwrite requested.\n" );
138             if( pbErrorOccurred )
139                 *pbErrorOccurred = TRUE;
140         }
141         poDstLayer = nullptr;
142     }
143 
144     return poDstLayer;
145 }
146 
147 /************************************************************************/
148 /*                     CreateAndFillOutputDataset                       */
149 /************************************************************************/
CreateAndFillOutputDataset(OGRLayer * poSrcLayer,const char * pszDestDataSource,const char * pszFormat,const char * pszLayer,char ** papszDSCO,char ** papszLCO,int bQuiet)150 static OGRErr CreateAndFillOutputDataset(OGRLayer* poSrcLayer,
151                                          const char* pszDestDataSource,
152                                          const char* pszFormat,
153                                          const char* pszLayer,
154                                          char** papszDSCO,
155                                          char** papszLCO,
156                                          int bQuiet)
157 {
158     GDALDriver *poDriver = GetGDALDriverManager()->GetDriverByName(pszFormat);
159     if( poDriver == nullptr )
160     {
161         fprintf( stderr, "%s driver not available\n", pszFormat );
162         return OGRERR_FAILURE;
163     }
164 
165     if( !CPLTestBool(
166             CSLFetchNameValueDef(poDriver->GetMetadata(), GDAL_DCAP_CREATE,
167                                  "FALSE") ) )
168     {
169         fprintf( stderr,  "%s driver does not support data source creation.\n",
170                 pszFormat );
171         return OGRERR_FAILURE;
172     }
173 
174     GDALDataset* poODS = poDriver->Create( pszDestDataSource, 0, 0, 0,
175                                          GDT_Unknown, papszDSCO );
176     if( poODS == nullptr )
177     {
178         fprintf( stderr,  "%s driver failed to create %s\n",
179                 pszFormat, pszDestDataSource );
180         return OGRERR_FAILURE;
181     }
182 
183     if(nullptr == pszLayer)
184         pszLayer = poSrcLayer->GetName();
185     int nError;
186     GetLayerAndOverwriteIfNecessary(poODS, pszLayer, TRUE, &nError);
187     if(nError == TRUE)
188     {
189         return OGRERR_FAILURE;
190     }
191 
192     // create layer
193     OGRLayer * poLayer = poODS->CopyLayer(poSrcLayer, pszLayer, papszLCO);
194     if (nullptr == poLayer)
195     {
196         fprintf(stderr, "\nFAILURE: Can not copy path to %s\n",
197                 pszDestDataSource);
198         GDALClose(poODS);
199 
200         return OGRERR_FAILURE;
201     }
202 
203     if (bQuiet == FALSE)
204     {
205         printf("\nPath successfully copied and added to the network at %s\n",
206             pszDestDataSource);
207     }
208 
209     GDALClose(poODS);
210 
211     return OGRERR_NONE;
212 }
213 
214 /************************************************************************/
215 /*                           ReportOnLayer()                            */
216 /************************************************************************/
217 
ReportOnLayer(OGRLayer * poLayer,int bVerbose)218 static void ReportOnLayer( OGRLayer * poLayer, int bVerbose )
219 
220 {
221     OGRFeatureDefn      *poDefn = poLayer->GetLayerDefn();
222 
223 /* -------------------------------------------------------------------- */
224 /*      Report various overall information.                             */
225 /* -------------------------------------------------------------------- */
226     printf( "\n" );
227 
228     printf( "Layer name: %s\n", poLayer->GetName() );
229 
230     if( bVerbose )
231     {
232         int nGeomFieldCount =
233             poLayer->GetLayerDefn()->GetGeomFieldCount();
234         if( nGeomFieldCount > 1 )
235         {
236             for(int iGeom = 0;iGeom < nGeomFieldCount; iGeom ++ )
237             {
238                 OGRGeomFieldDefn* poGFldDefn =
239                     poLayer->GetLayerDefn()->GetGeomFieldDefn(iGeom);
240                 printf( "Geometry (%s): %s\n", poGFldDefn->GetNameRef(),
241                     OGRGeometryTypeToName( poGFldDefn->GetType() ) );
242             }
243         }
244         else
245         {
246             printf( "Geometry: %s\n",
247                     OGRGeometryTypeToName( poLayer->GetGeomType() ) );
248         }
249 
250         printf( "Feature Count: " CPL_FRMT_GIB "\n", poLayer->GetFeatureCount() );
251 
252         OGREnvelope oExt;
253         if( nGeomFieldCount > 1 )
254         {
255             for(int iGeom = 0;iGeom < nGeomFieldCount; iGeom ++ )
256             {
257                 if (poLayer->GetExtent(iGeom, &oExt, TRUE) == OGRERR_NONE)
258                 {
259                     OGRGeomFieldDefn* poGFldDefn =
260                         poLayer->GetLayerDefn()->GetGeomFieldDefn(iGeom);
261                     CPLprintf("Extent (%s): (%f, %f) - (%f, %f)\n",
262                            poGFldDefn->GetNameRef(),
263                            oExt.MinX, oExt.MinY, oExt.MaxX, oExt.MaxY);
264                 }
265             }
266         }
267         else if ( poLayer->GetExtent(&oExt, TRUE) == OGRERR_NONE)
268         {
269             CPLprintf("Extent: (%f, %f) - (%f, %f)\n",
270                    oExt.MinX, oExt.MinY, oExt.MaxX, oExt.MaxY);
271         }
272 
273         char    *pszWKT;
274 
275         if( nGeomFieldCount > 1 )
276         {
277             for(int iGeom = 0;iGeom < nGeomFieldCount; iGeom ++ )
278             {
279                 OGRGeomFieldDefn* poGFldDefn =
280                     poLayer->GetLayerDefn()->GetGeomFieldDefn(iGeom);
281                 OGRSpatialReference* poSRS = poGFldDefn->GetSpatialRef();
282                 if( poSRS == nullptr )
283                     pszWKT = CPLStrdup( "(unknown)" );
284                 else
285                 {
286                     poSRS->exportToPrettyWkt( &pszWKT );
287                 }
288 
289                 printf( "SRS WKT (%s):\n%s\n",
290                         poGFldDefn->GetNameRef(), pszWKT );
291                 CPLFree( pszWKT );
292             }
293         }
294         else
295         {
296             if( poLayer->GetSpatialRef() == nullptr )
297                 pszWKT = CPLStrdup( "(unknown)" );
298             else
299             {
300                 poLayer->GetSpatialRef()->exportToPrettyWkt( &pszWKT );
301             }
302 
303             printf( "Layer SRS WKT:\n%s\n", pszWKT );
304             CPLFree( pszWKT );
305         }
306 
307         if( strlen(poLayer->GetFIDColumn()) > 0 )
308             printf( "FID Column = %s\n",
309                     poLayer->GetFIDColumn() );
310 
311         for(int iGeom = 0;iGeom < nGeomFieldCount; iGeom ++ )
312         {
313             OGRGeomFieldDefn* poGFldDefn =
314                 poLayer->GetLayerDefn()->GetGeomFieldDefn(iGeom);
315             if( nGeomFieldCount == 1 &&
316                 EQUAL(poGFldDefn->GetNameRef(), "")  && poGFldDefn->IsNullable() )
317                 break;
318             printf( "Geometry Column ");
319             if( nGeomFieldCount > 1 )
320                 printf("%d ", iGeom + 1);
321             if( !poGFldDefn->IsNullable() )
322                 printf("NOT NULL ");
323             printf("= %s\n", poGFldDefn->GetNameRef() );
324         }
325 
326         for( int iAttr = 0; iAttr < poDefn->GetFieldCount(); iAttr++ )
327         {
328             OGRFieldDefn    *poField = poDefn->GetFieldDefn( iAttr );
329             const char* pszType = (poField->GetSubType() != OFSTNone) ?
330                 CPLSPrintf("%s(%s)",
331                            poField->GetFieldTypeName( poField->GetType() ),
332                            poField->GetFieldSubTypeName(poField->GetSubType())) :
333                 poField->GetFieldTypeName( poField->GetType() );
334             printf( "%s: %s (%d.%d)",
335                     poField->GetNameRef(),
336                     pszType,
337                     poField->GetWidth(),
338                     poField->GetPrecision() );
339             if( !poField->IsNullable() )
340                 printf(" NOT NULL");
341             if( poField->GetDefault() != nullptr )
342                 printf(" DEFAULT %s", poField->GetDefault() );
343             printf( "\n" );
344         }
345     }
346 
347 /* -------------------------------------------------------------------- */
348 /*      Read, and dump features.                                        */
349 /* -------------------------------------------------------------------- */
350     OGRFeature  *poFeature = nullptr;
351     while( (poFeature = poLayer->GetNextFeature()) != nullptr )
352     {
353         poFeature->DumpReadable( nullptr );
354         OGRFeature::DestroyFeature( poFeature );
355     }
356 }
357 
358 /************************************************************************/
359 /*                                main()                                */
360 /************************************************************************/
361 
362 #define CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(nExtraArg) \
363     do { if (iArg + nExtraArg >= nArgc) \
364         Usage(CPLSPrintf("%s option requires %d argument(s)", papszArgv[iArg], \
365                 nExtraArg)); \
366         } while( false )
367 
MAIN_START(nArgc,papszArgv)368 MAIN_START(nArgc, papszArgv)
369 
370 {
371     int bQuiet = FALSE;
372 
373     const char *pszDataSource = nullptr;
374 
375     GNMGFID nFromFID = -1;
376     GNMGFID nToFID = -1;
377     int nK = 1;
378     const char *pszDataset = nullptr;
379     const char *pszFormat = "ESRI Shapefile";
380     const char *pszLayer = nullptr;
381     GNMNetwork *poDS = nullptr;
382     OGRLayer* poResultLayer = nullptr;
383     char  **papszDSCO = nullptr, **papszLCO = nullptr, **papszALO = nullptr;
384 
385     operation stOper = op_unknown;
386 
387     int          nRet = 0;
388 
389     // Check strict compilation and runtime library version as we use C++ API
390     if (! GDAL_CHECK_VERSION(papszArgv[0]))
391         exit(1);
392 
393     EarlySetConfigOptions(nArgc, papszArgv);
394 
395 /* -------------------------------------------------------------------- */
396 /*      Register format(s).                                             */
397 /* -------------------------------------------------------------------- */
398     GDALAllRegister();
399 
400 /* -------------------------------------------------------------------- */
401 /*      Processing command line arguments.                              */
402 /* -------------------------------------------------------------------- */
403     nArgc = GDALGeneralCmdLineProcessor( nArgc, &papszArgv, GDAL_OF_GNM );
404 
405     if( nArgc < 1 )
406     {
407         exit( -nArgc );
408     }
409 
410     for( int iArg = 1; iArg < nArgc; iArg++ )
411     {
412         if( EQUAL(papszArgv[1], "--utility_version") )
413         {
414             printf("%s was compiled against GDAL %s and is running against GDAL %s\n",
415                     papszArgv[0], GDAL_RELEASE_NAME, GDALVersionInfo("RELEASE_NAME"));
416             CSLDestroy( papszArgv );
417             return 0;
418         }
419 
420         else if( EQUAL(papszArgv[iArg],"--help") )
421         {
422             Usage();
423         }
424 
425         else if ( EQUAL(papszArgv[iArg], "--long-usage") )
426         {
427             Usage(FALSE);
428         }
429 
430         else if( EQUAL(papszArgv[iArg],"-q") || EQUAL(papszArgv[iArg],"-quiet") )
431         {
432             bQuiet = TRUE;
433         }
434 
435         else if( EQUAL(papszArgv[iArg],"dijkstra") )
436         {
437             CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(2);
438             stOper = op_dijkstra;
439             nFromFID = atoi(papszArgv[++iArg]);
440             nToFID = atoi(papszArgv[++iArg]);
441         }
442 
443         else if( EQUAL(papszArgv[iArg],"kpaths") )
444         {
445             CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(3);
446             stOper = op_kpaths;
447             nFromFID = atoi(papszArgv[++iArg]);
448             nToFID = atoi(papszArgv[++iArg]);
449             nK = atoi(papszArgv[++iArg]);
450         }
451 
452         else if( EQUAL(papszArgv[iArg],"resource") )
453         {
454             stOper = op_resource;
455         }
456 
457         else if( EQUAL(papszArgv[iArg],"-ds") )
458         {
459             CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(1);
460             pszDataset = papszArgv[++iArg];
461         }
462 
463         else if( (EQUAL(papszArgv[iArg],"-f") || EQUAL(papszArgv[iArg],"-of")) )
464         {
465             CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(1);
466             pszFormat = papszArgv[++iArg];
467         }
468 
469         else if( EQUAL(papszArgv[iArg],"-l") )
470         {
471             CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(1);
472             pszLayer = papszArgv[++iArg];
473         }
474         else if( EQUAL(papszArgv[iArg],"-dsco") )
475         {
476             CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(1);
477             papszDSCO = CSLAddString(papszDSCO, papszArgv[++iArg] );
478         }
479         else if( EQUAL(papszArgv[iArg],"-lco") )
480         {
481             CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(1);
482             papszLCO = CSLAddString(papszLCO, papszArgv[++iArg] );
483         }
484         else if( EQUAL(papszArgv[iArg],"-alo") )
485         {
486             CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(1);
487             papszALO = CSLAddString(papszALO, papszArgv[++iArg] );
488         }
489         else if( papszArgv[iArg][0] == '-' )
490         {
491             Usage(CPLSPrintf("Unknown option name '%s'", papszArgv[iArg]));
492         }
493 
494         else if( pszDataSource == nullptr )
495             pszDataSource = papszArgv[iArg];
496     }
497 
498 // do the work ////////////////////////////////////////////////////////////////
499 
500     if(stOper == op_dijkstra)
501     {
502         if(pszDataSource == nullptr)
503             Usage("No network dataset provided");
504 
505         if(nFromFID == -1 || nToFID == -1)
506             Usage("Invalid input from or to identificators");
507 
508         // open
509         poDS = cpl::down_cast<GNMNetwork*>(static_cast<GDALDataset*>(GDALOpenEx( pszDataSource,
510                              GDAL_OF_UPDATE | GDAL_OF_GNM, nullptr, nullptr, nullptr )));
511         if(nullptr == poDS)
512         {
513             fprintf( stderr, "\nFailed to open network at %s\n", pszDataSource);
514             nRet = 1;
515             goto exit;
516         }
517 
518         poResultLayer = poDS->GetPath(nFromFID, nToFID, GATDijkstraShortestPath,
519                                       papszALO);
520         if(nullptr == pszDataset)
521         {
522             ReportOnLayer(poResultLayer, bQuiet == FALSE);
523         }
524         else
525         {
526             if(CreateAndFillOutputDataset(poResultLayer, pszDataset, pszFormat,
527                                           pszLayer, papszDSCO, papszLCO, bQuiet)
528                     != OGRERR_NONE)
529             {
530                 nRet = 1;
531                 goto exit;
532             }
533         }
534     }
535     else if(stOper == op_kpaths)
536     {
537         if(pszDataSource == nullptr)
538             Usage("No network dataset provided");
539 
540         if(nFromFID == -1 || nToFID == -1)
541             Usage("Invalid input from or to identificators");
542 
543         // open
544         poDS = cpl::down_cast<GNMNetwork*>(static_cast<GDALDataset*>(GDALOpenEx( pszDataSource,
545                              GDAL_OF_UPDATE | GDAL_OF_GNM, nullptr, nullptr, nullptr )));
546         if(nullptr == poDS)
547         {
548             fprintf( stderr, "\nFailed to open network at %s\n", pszDataSource);
549             nRet = 1;
550             goto exit;
551         }
552 
553         if(CSLFindName(papszALO, GNM_MD_NUM_PATHS) == -1)
554         {
555             CPLDebug("GNM", "No K in options, add %d value", nK);
556             papszALO = CSLAddNameValue(papszALO, GNM_MD_NUM_PATHS,
557                                        CPLSPrintf("%d", nK));
558         }
559 
560         poResultLayer = poDS->GetPath(nFromFID, nToFID, GATKShortestPath,
561                                       papszALO);
562 
563         if(nullptr == pszDataset)
564         {
565             ReportOnLayer(poResultLayer, bQuiet == FALSE);
566         }
567         else
568         {
569             if(CreateAndFillOutputDataset(poResultLayer, pszDataset, pszFormat,
570                                           pszLayer, papszDSCO, papszLCO, bQuiet)
571                     != OGRERR_NONE)
572             {
573                 nRet = 1;
574                 goto exit;
575             }
576         }
577     }
578     else if(stOper == op_resource)
579     {
580         if(pszDataSource == nullptr)
581             Usage("No network dataset provided");
582 
583         // open
584         poDS = cpl::down_cast<GNMNetwork*>(static_cast<GDALDataset*>(GDALOpenEx( pszDataSource,
585                              GDAL_OF_UPDATE | GDAL_OF_GNM, nullptr, nullptr, nullptr )));
586         if(nullptr == poDS)
587         {
588             fprintf( stderr, "\nFailed to open network at %s\n", pszDataSource);
589             nRet = 1;
590             goto exit;
591         }
592 
593         poResultLayer = poDS->GetPath(nFromFID, nToFID, GATConnectedComponents,
594                                       papszALO);
595 
596         if(nullptr == pszDataset)
597         {
598             ReportOnLayer(poResultLayer, bQuiet == FALSE);
599         }
600         else
601         {
602             if(CreateAndFillOutputDataset(poResultLayer, pszDataset, pszFormat,
603                                           pszLayer, papszDSCO, papszLCO, bQuiet)
604                     != OGRERR_NONE)
605             {
606                 nRet = 1;
607                 goto exit;
608             }
609         }
610     }
611     else
612     {
613         printf("\nNeed an operation. See help what you can do with gnmanalyse:\n");
614         Usage();
615     }
616 
617  exit:
618     CSLDestroy(papszDSCO);
619     CSLDestroy(papszLCO);
620     CSLDestroy(papszALO);
621     CSLDestroy( papszArgv );
622 
623     if(poResultLayer != nullptr)
624         poDS->ReleaseResultSet(poResultLayer);
625 
626     if( poDS != nullptr )
627         GDALClose(poDS);
628 
629     GDALDestroyDriverManager();
630 
631     return nRet;
632 }
633 MAIN_END
634