1 /******************************************************************************
2  *
3  * Project:  OpenGIS Simple Features Reference Implementation
4  * Purpose:  Program to generate a UMN MapServer compatible tile index for a
5  *           set of OGR data sources.
6  * Author:   Frank Warmerdam, warmerdam@pobox.com
7  *
8  ******************************************************************************
9  * Copyright (c) 2002, Frank Warmerdam
10  * Copyright (c) 2007-2010, Even Rouault <even dot rouault at spatialys.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 "cpl_port.h"
32 
33 #include <cassert>
34 #include <vector>
35 
36 #include "cpl_conv.h"
37 #include "cpl_string.h"
38 #include "gdal_version.h"
39 #include "ogr_api.h"
40 #include "ogrsf_frmts.h"
41 #include "commonutils.h"
42 
43 CPL_CVSID("$Id: ogrtindex.cpp b55a33407a80673ec314b165c82f47dd02e9dc9c 2020-04-27 20:37:55 +0200 Even Rouault $")
44 
45 typedef enum
46 {
47     FORMAT_AUTO,
48     FORMAT_WKT,
49     FORMAT_EPSG,
50     FORMAT_PROJ
51 } SrcSRSFormat;
52 
53 /************************************************************************/
54 /*                               Usage()                                */
55 /************************************************************************/
56 
Usage()57 static void Usage()
58 
59 {
60     printf("Usage: ogrtindex [-lnum n]... [-lname name]... [-f output_format]\n"
61            "                 [-write_absolute_path] [-skip_different_projection]\n"
62            "                 [-t_srs target_srs]\n"
63            "                 [-src_srs_name field_name] [-src_srs_format [AUTO|WKT|EPSG|PROJ]\n"
64            "                 [-accept_different_schemas]\n"
65            "                 output_dataset src_dataset...\n");
66     printf("\n");
67     printf("  -lnum n: Add layer number 'n' from each source file\n"
68            "           in the tile index.\n");
69     printf("  -lname name: Add the layer named 'name' from each source file\n"
70            "               in the tile index.\n");
71     printf("  -f output_format: Select an output format name.\n");
72     printf("  -tileindex field_name: The name to use for the dataset name.\n"
73            "                         Defaults to LOCATION.\n");
74     printf("  -write_absolute_path: Filenames are written with absolute paths.\n");
75     printf("  -skip_different_projection: Only layers with same projection ref \n"
76            "        as layers already inserted in the tileindex will be inserted.\n");
77     printf("  -accept_different_schemas: by default ogrtindex checks that all layers inserted\n"
78            "                             into the index have the same attribute schemas. If you\n"
79            "                             specify this option, this test will be disabled. Be aware that\n"
80            "                             resulting index may be incompatible with MapServer!\n");
81     printf(
82            "  - If -t_srs is specified, geometries of input files will be transformed to the desired\n"
83            "    target coordinate reference system.\n"
84            "    Note that using this option generates files that are NOT compatible with MapServer < 7.2.\n"
85            "  - Simple rectangular polygons are generated in the same coordinate reference system\n"
86            "    as the vectors, or in target reference system if the -t_srs option is used.\n");
87     printf("\n");
88     printf("If no -lnum or -lname arguments are given it is assumed that\n"
89            "all layers in source datasets should be added to the tile index\n"
90            "as independent records.\n");
91     exit(1);
92 }
93 
94 /************************************************************************/
95 /*                                main()                                */
96 /************************************************************************/
97 
MAIN_START(nArgc,papszArgv)98 MAIN_START(nArgc, papszArgv)
99 
100 {
101     // Check strict compilation and runtime library version as we use C++ API.
102     if( !GDAL_CHECK_VERSION(papszArgv[0]) )
103         exit(1);
104 /* -------------------------------------------------------------------- */
105 /*      Register format(s).                                             */
106 /* -------------------------------------------------------------------- */
107     OGRRegisterAll();
108 
109 /* -------------------------------------------------------------------- */
110 /*      Processing command line arguments.                              */
111 /* -------------------------------------------------------------------- */
112     int nFirstSourceDataset = -1;
113     bool bLayersWildcarded = true;
114     const char *pszFormat = nullptr;
115     const char *pszTileIndexField = "LOCATION";
116     const char *pszOutputName = nullptr;
117     bool write_absolute_path = false;
118     bool skip_different_projection = false;
119     char* current_path = nullptr;
120     bool accept_different_schemas = false;
121     bool bFirstWarningForNonMatchingAttributes = true;
122     const char *pszTargetSRS = "";
123     bool bSetTargetSRS = false;
124     const char* pszSrcSRSName = nullptr;
125     int i_SrcSRSName = -1;
126     bool bSrcSRSFormatSpecified = false;
127     SrcSRSFormat eSrcSRSFormat = FORMAT_AUTO;
128     size_t nMaxFieldSize = 254;
129 
130     for( int iArg = 1; iArg < nArgc; iArg++ )
131     {
132         if( EQUAL(papszArgv[iArg], "--utility_version") )
133         {
134             printf("%s was compiled against GDAL %s and "
135                    "is running against GDAL %s\n",
136                    papszArgv[0], GDAL_RELEASE_NAME,
137                    GDALVersionInfo("RELEASE_NAME"));
138             return 0;
139         }
140         else if( iArg < nArgc-1 &&
141                  (EQUAL(papszArgv[iArg],"-f") || EQUAL(papszArgv[iArg],"-of")) )
142         {
143             pszFormat = papszArgv[++iArg];
144         }
145         else if( EQUAL(papszArgv[iArg],"-write_absolute_path"))
146         {
147             write_absolute_path = true;
148         }
149         else if( EQUAL(papszArgv[iArg],"-skip_different_projection"))
150         {
151             skip_different_projection = true;
152         }
153         else if( EQUAL(papszArgv[iArg],"-accept_different_schemas"))
154         {
155             accept_different_schemas = true;
156         }
157         else if( iArg < nArgc-1 && EQUAL(papszArgv[iArg],"-tileindex") )
158         {
159             pszTileIndexField = papszArgv[++iArg];
160         }
161         else if( EQUAL(papszArgv[iArg],"-lnum")
162                  || EQUAL(papszArgv[iArg],"-lname") )
163         {
164             iArg++;
165             bLayersWildcarded = false;
166         }
167         else if( iArg < nArgc-1 && strcmp(papszArgv[iArg],"-t_srs") == 0 )
168         {
169             pszTargetSRS = papszArgv[++iArg];
170             bSetTargetSRS = true;
171         }
172         else if( iArg < nArgc-1 &&
173                  strcmp(papszArgv[iArg], "-src_srs_name") == 0 )
174         {
175             pszSrcSRSName = papszArgv[++iArg];
176         }
177         else if( iArg < nArgc-1 &&
178                  strcmp(papszArgv[iArg], "-src_srs_format") == 0 )
179         {
180             bSrcSRSFormatSpecified = true;
181             const char* pszSRSFormat = papszArgv[++iArg];
182             if( EQUAL(pszSRSFormat, "AUTO") )
183                 eSrcSRSFormat = FORMAT_AUTO;
184             else if( EQUAL(pszSRSFormat, "WKT") )
185                 eSrcSRSFormat = FORMAT_WKT;
186             else if( EQUAL(pszSRSFormat, "EPSG") )
187                 eSrcSRSFormat = FORMAT_EPSG;
188             else if( EQUAL(pszSRSFormat, "PROJ") )
189                 eSrcSRSFormat = FORMAT_PROJ;
190         }
191         else if( papszArgv[iArg][0] == '-' )
192         {
193             Usage();
194         }
195         else if( pszOutputName == nullptr )
196         {
197             pszOutputName = papszArgv[iArg];
198         }
199         else if( nFirstSourceDataset == -1 )
200         {
201             nFirstSourceDataset = iArg;
202         }
203     }
204 
205     if( pszOutputName == nullptr || nFirstSourceDataset == -1 )
206         Usage();
207 
208     if( bSrcSRSFormatSpecified && pszSrcSRSName == nullptr )
209     {
210         fprintf(stderr,
211                 "-src_srs_name must be specified when -src_srs_format is "
212                 "specified.\n");
213         Usage();
214     }
215 
216 /* -------------------------------------------------------------------- */
217 /*      Create and validate target SRS if given.                        */
218 /* -------------------------------------------------------------------- */
219     OGRSpatialReference* poTargetSRS = nullptr;
220     if( bSetTargetSRS )
221     {
222         if( skip_different_projection )
223         {
224             fprintf(stderr,
225                     "Warning : -skip_different_projection does not apply "
226                     "when -t_srs is requested.\n");
227         }
228         poTargetSRS = new OGRSpatialReference();
229         poTargetSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
230         // coverity[tainted_data]
231         if( poTargetSRS->SetFromUserInput( pszTargetSRS ) != CE_None )
232         {
233             delete poTargetSRS;
234             fprintf(stderr, "Invalid target SRS `%s'.\n",
235                     pszTargetSRS);
236             exit(1);
237         }
238     }
239 
240 /* -------------------------------------------------------------------- */
241 /*      Try to open as an existing dataset for update access.           */
242 /* -------------------------------------------------------------------- */
243     GDALDataset *poDstDS = reinterpret_cast<GDALDataset*>(
244         OGROpen(pszOutputName, TRUE, nullptr));
245 
246 /* -------------------------------------------------------------------- */
247 /*      If that failed, find the driver so we can create the tile index.*/
248 /* -------------------------------------------------------------------- */
249     OGRLayer *poDstLayer = nullptr;
250 
251     if( poDstDS == nullptr )
252     {
253         CPLString osFormat;
254         if( pszFormat == nullptr )
255         {
256             const std::vector<CPLString> aoDrivers =
257                 GetOutputDriversFor(pszOutputName, GDAL_OF_VECTOR);
258             if( aoDrivers.empty() )
259             {
260                 CPLError(CE_Failure, CPLE_AppDefined,
261                          "Cannot guess driver for %s", pszOutputName);
262                 exit(10);
263             }
264             else
265             {
266                 if( aoDrivers.size() > 1 )
267                 {
268                     CPLError(
269                         CE_Warning, CPLE_AppDefined,
270                         "Several drivers matching %s extension. Using %s",
271                         CPLGetExtension(pszOutputName), aoDrivers[0].c_str());
272                 }
273                 osFormat = aoDrivers[0];
274             }
275         }
276         else
277         {
278             osFormat = pszFormat;
279         }
280         if( !EQUAL(osFormat, "ESRI Shapefile") )
281             nMaxFieldSize = 0;
282 
283         GDALDriverH hDriver = GDALGetDriverByName(osFormat.c_str());
284         if( hDriver == nullptr )
285         {
286             GDALDriverManager *poDM = GetGDALDriverManager();
287             for( int iDriver = 0; iDriver < poDM->GetDriverCount(); iDriver++ )
288             {
289                 fprintf(stderr, "Unable to find driver `%s'.\n",
290                         osFormat.c_str());
291                 fprintf(stderr, "The following drivers are available:\n");
292 
293                 GDALDriver* poIter = poDM->GetDriver(iDriver);
294                 char** papszDriverMD = poIter->GetMetadata();
295                 if( CPLTestBool(CSLFetchNameValueDef(
296                         papszDriverMD, GDAL_DCAP_VECTOR, "FALSE")) &&
297                     CPLTestBool(CSLFetchNameValueDef(
298                         papszDriverMD, GDAL_DCAP_CREATE, "FALSE")) )
299                 {
300                     fprintf(stderr, "  -> `%s'\n", poIter->GetDescription());
301                 }
302             }
303             exit(1);
304         }
305 
306         if( !CPLTestBool(CSLFetchNameValueDef(GDALGetMetadata(hDriver, nullptr),
307                                               GDAL_DCAP_CREATE, "FALSE")) )
308         {
309             fprintf(stderr,
310                     "%s driver does not support data source creation.\n",
311                     osFormat.c_str());
312             exit(1);
313         }
314 
315 /* -------------------------------------------------------------------- */
316 /*      Now create it.                                                  */
317 /* -------------------------------------------------------------------- */
318 
319         poDstDS = reinterpret_cast<GDALDataset*>(
320             GDALCreate(hDriver, pszOutputName, 0, 0, 0, GDT_Unknown, nullptr));
321         if( poDstDS == nullptr )
322         {
323             fprintf(stderr, "%s driver failed to create %s\n",
324                     osFormat.c_str(), pszOutputName);
325             exit(1);
326         }
327 
328         if( poDstDS->GetLayerCount() == 0 )
329         {
330             if( nFirstSourceDataset < nArgc &&
331                 papszArgv[nFirstSourceDataset][0] == '-' )
332             {
333                 nFirstSourceDataset++;
334             }
335 
336             OGRSpatialReference* poSrcSpatialRef = nullptr;
337             if( bSetTargetSRS )
338             {
339                 // Fetches the SRS from target SRS (if set), or from the SRS of
340                 // the first layer and use it when creating the
341                 // tileindex layer.
342                 poSrcSpatialRef = poTargetSRS->Clone();
343             }
344             else if( nFirstSourceDataset < nArgc )
345             {
346                 GDALDataset* poDS = reinterpret_cast<GDALDataset*>(
347                     OGROpen(papszArgv[nFirstSourceDataset], FALSE, nullptr));
348                 if( poDS != nullptr )
349                 {
350                     for( int iLayer = 0;
351                          iLayer < poDS->GetLayerCount();
352                          iLayer++ )
353                     {
354                         bool bRequested = bLayersWildcarded;
355                         OGRLayer *poLayer = poDS->GetLayer(iLayer);
356 
357                         for( int iArg = 1; iArg < nArgc && !bRequested; iArg++ )
358                         {
359                             if( EQUAL(papszArgv[iArg], "-lnum")
360                                 && atoi(papszArgv[iArg+1]) == iLayer )
361                                 bRequested = true;
362                             else if( EQUAL(papszArgv[iArg], "-lname") &&
363                                      EQUAL(papszArgv[iArg+1],
364                                            poLayer->GetLayerDefn()->GetName()) )
365                                 bRequested = true;
366                         }
367 
368                         if( !bRequested )
369                             continue;
370 
371                         if( poLayer->GetSpatialRef() )
372                             poSrcSpatialRef = poLayer->GetSpatialRef()->Clone();
373                         break;
374                     }
375                 }
376 
377                 GDALClose(poDS);
378             }
379 
380             poDstLayer = poDstDS->CreateLayer("tileindex", poSrcSpatialRef);
381 
382             OGRFieldDefn oLocation(pszTileIndexField, OFTString);
383             oLocation.SetWidth(200);
384             poDstLayer->CreateField(&oLocation);
385 
386             if( pszSrcSRSName != nullptr )
387             {
388                 OGRFieldDefn oSrcSRSNameField(pszSrcSRSName, OFTString);
389                 poDstLayer->CreateField(&oSrcSRSNameField);
390             }
391 
392             if( poSrcSpatialRef )
393                 poSrcSpatialRef->Release();
394         }
395     }
396 
397 /* -------------------------------------------------------------------- */
398 /*      Identify target layer and field.                                */
399 /* -------------------------------------------------------------------- */
400 
401     poDstLayer = poDstDS->GetLayer(0);
402     if( poDstLayer == nullptr )
403     {
404         fprintf(stderr, "Can't find any layer in output tileindex!\n");
405         exit(1);
406     }
407 
408     const int iTileIndexField =
409         poDstLayer->GetLayerDefn()->GetFieldIndex(pszTileIndexField);
410     if( iTileIndexField == -1 )
411     {
412         fprintf(stderr, "Can't find %s field in tile index dataset.\n",
413                 pszTileIndexField);
414         exit(1);
415     }
416 
417     if( pszSrcSRSName != nullptr )
418         i_SrcSRSName = poDstLayer->GetLayerDefn()->GetFieldIndex(pszSrcSRSName);
419 
420     OGRFeatureDefn* poFeatureDefn = nullptr;
421 
422     // Load in memory existing file names in SHP.
423     char **existingLayersTab = nullptr;
424     OGRSpatialReference* alreadyExistingSpatialRef = nullptr;
425     bool alreadyExistingSpatialRefValid = false;
426     const int nExistingLayers = static_cast<int>(poDstLayer->GetFeatureCount());
427     if( nExistingLayers )
428     {
429         existingLayersTab = static_cast<char **>(
430             CPLMalloc(nExistingLayers * sizeof(char*)));
431         for( int i = 0; i < nExistingLayers; i++ )
432         {
433             OGRFeature* feature = poDstLayer->GetNextFeature();
434             existingLayersTab[i] =
435                 CPLStrdup(feature->GetFieldAsString( iTileIndexField));
436             if( i == 0 )
437             {
438                 char* filename = CPLStrdup(existingLayersTab[i]);
439                 // j used after for.
440                 int j = static_cast<int>(strlen(filename)) - 1;
441                 for( ; j >= 0; j-- )
442                 {
443                     if( filename[j] == ',' )
444                         break;
445                 }
446                 GDALDataset *poDS = nullptr;
447                 if( j >= 0 )
448                 {
449                     const int iLayer = atoi(filename + j + 1);
450                     filename[j] = 0;
451                     poDS = reinterpret_cast<GDALDataset *>(
452                         OGROpen(filename, FALSE, nullptr));
453                     if( poDS != nullptr )
454                     {
455                         OGRLayer *poLayer = poDS->GetLayer(iLayer);
456                         if( poLayer )
457                         {
458                             alreadyExistingSpatialRefValid = true;
459                             alreadyExistingSpatialRef =
460                                 poLayer->GetSpatialRef() ?
461                                 poLayer->GetSpatialRef()->Clone() : nullptr;
462 
463                             if( poFeatureDefn == nullptr )
464                                 poFeatureDefn =
465                                     poLayer->GetLayerDefn()->Clone();
466                         }
467                         GDALClose(poDS);
468                     }
469                 }
470             }
471         }
472     }
473 
474     if( write_absolute_path )
475     {
476         current_path = CPLGetCurrentDir();
477         if( current_path == nullptr )
478         {
479             fprintf(stderr,
480                     "This system does not support the CPLGetCurrentDir call. "
481                     "The option -write_absolute_path will have no effect\n");
482             write_absolute_path = false;
483         }
484     }
485 /* ==================================================================== */
486 /*      Process each input datasource in turn.                          */
487 /* ==================================================================== */
488     for( ; nFirstSourceDataset < nArgc; nFirstSourceDataset++ )
489     {
490         if( papszArgv[nFirstSourceDataset][0] == '-' )
491         {
492             nFirstSourceDataset++;
493             continue;
494         }
495 
496         char* fileNameToWrite = nullptr;
497         VSIStatBuf sStatBuf;
498 
499         if( write_absolute_path &&
500             CPLIsFilenameRelative( papszArgv[nFirstSourceDataset] ) &&
501             VSIStat( papszArgv[nFirstSourceDataset], &sStatBuf ) == 0 )
502         {
503             fileNameToWrite =
504                 CPLStrdup(CPLProjectRelativeFilename(
505                     current_path, papszArgv[nFirstSourceDataset]));
506         }
507         else
508         {
509             fileNameToWrite = CPLStrdup(papszArgv[nFirstSourceDataset]);
510         }
511 
512         GDALDataset *poDS = reinterpret_cast<GDALDataset*>(
513             OGROpen(papszArgv[nFirstSourceDataset], FALSE, nullptr ));
514 
515         if( poDS == nullptr )
516         {
517             fprintf(stderr, "Failed to open dataset %s, skipping.\n",
518                     papszArgv[nFirstSourceDataset]);
519             CPLFree(fileNameToWrite);
520             continue;
521         }
522 
523 /* -------------------------------------------------------------------- */
524 /*      Check all layers, and see if they match requests.               */
525 /* -------------------------------------------------------------------- */
526         for( int iLayer = 0; iLayer < poDS->GetLayerCount(); iLayer++ )
527         {
528             bool bRequested = bLayersWildcarded;
529             OGRLayer *poLayer = poDS->GetLayer(iLayer);
530 
531             for( int iArg = 1; iArg < nArgc && !bRequested; iArg++ )
532             {
533                 if( EQUAL(papszArgv[iArg], "-lnum")
534                     && atoi(papszArgv[iArg+1]) == iLayer )
535                     bRequested = true;
536                 else if( EQUAL(papszArgv[iArg], "-lname" )
537                          && EQUAL(papszArgv[iArg+1],
538                                   poLayer->GetLayerDefn()->GetName()) )
539                     bRequested = true;
540             }
541 
542             if( !bRequested )
543                 continue;
544 
545             // Checks that the layer is not already in tileindex.
546             int i = 0;  // Used after for.
547             for( ; i < nExistingLayers; i++ )
548             {
549                 // TODO(schwehr): Move this off of the stack.
550                 char szLocation[5000] = {};
551                 snprintf(szLocation, sizeof(szLocation), "%s,%d",
552                          fileNameToWrite, iLayer);
553                 if( EQUAL(szLocation, existingLayersTab[i]) )
554                 {
555                     fprintf(stderr, "Layer %d of %s is already in tileindex. "
556                             "Skipping it.\n",
557                             iLayer, papszArgv[nFirstSourceDataset]);
558                     break;
559                 }
560             }
561             if( i != nExistingLayers )
562             {
563                 continue;
564             }
565 
566             OGRSpatialReference* spatialRef = poLayer->GetSpatialRef();
567             // If not set target srs, test that the current file uses same
568             // projection as others.
569             if( !bSetTargetSRS )
570             {
571                 if( alreadyExistingSpatialRefValid )
572                 {
573                     if( (spatialRef != nullptr &&
574                          alreadyExistingSpatialRef != nullptr &&
575                         spatialRef->IsSame(alreadyExistingSpatialRef) ==
576                           FALSE) ||
577                         ((spatialRef != nullptr) !=
578                         (alreadyExistingSpatialRef != nullptr)) )
579                     {
580                         fprintf(
581                             stderr,
582                             "Warning : layer %d of %s is not using the same "
583                             "projection system as other files in the "
584                             "tileindex. This may cause problems when using it "
585                             "in MapServer for example.%s\n",
586                             iLayer, papszArgv[nFirstSourceDataset],
587                             skip_different_projection ? " Skipping it" : "");
588                         if( skip_different_projection )
589                         {
590                             continue;
591                         }
592                     }
593                 }
594                 else
595                 {
596                     alreadyExistingSpatialRefValid = true;
597                     alreadyExistingSpatialRef =
598                         spatialRef ? spatialRef->Clone() : nullptr;
599                 }
600             }
601 
602 /* -------------------------------------------------------------------- */
603 /*      Check if all layers in dataset have the same attributes schema. */
604 /* -------------------------------------------------------------------- */
605             if( poFeatureDefn == nullptr )
606             {
607                 poFeatureDefn = poLayer->GetLayerDefn()->Clone();
608             }
609             else if( !accept_different_schemas )
610             {
611                 OGRFeatureDefn* poFeatureDefnCur = poLayer->GetLayerDefn();
612                 assert(nullptr != poFeatureDefnCur);
613 
614                 const int fieldCount = poFeatureDefnCur->GetFieldCount();
615 
616                 if( fieldCount != poFeatureDefn->GetFieldCount())
617                 {
618                     fprintf( stderr, "Number of attributes of layer %s of %s "
619                              "does not match ... skipping it.\n",
620                              poLayer->GetLayerDefn()->GetName(),
621                              papszArgv[nFirstSourceDataset]);
622                     if( bFirstWarningForNonMatchingAttributes )
623                     {
624                         fprintf(
625                             stderr, "Note : you can override this "
626                             "behavior with -accept_different_schemas option\n"
627                             "but this may result in a tileindex incompatible "
628                             "with MapServer\n");
629                         bFirstWarningForNonMatchingAttributes = false;
630                     }
631                     continue;
632                 }
633 
634                 bool bSkip = false;
635                 for( int fn = 0; fn < poFeatureDefnCur->GetFieldCount(); fn++ )
636                 {
637                     OGRFieldDefn* poField = poFeatureDefn->GetFieldDefn(fn);
638                     OGRFieldDefn* poFieldCur =
639                         poFeatureDefnCur->GetFieldDefn(fn);
640 
641                     // XXX - Should those pointers be checked against NULL?
642                     assert(nullptr != poField);
643                     assert(nullptr != poFieldCur);
644 
645                     if( poField->GetType() != poFieldCur->GetType()
646                         || poField->GetWidth() != poFieldCur->GetWidth()
647                         || poField->GetPrecision() != poFieldCur->GetPrecision()
648                         || !EQUAL( poField->GetNameRef(),
649                                    poFieldCur->GetNameRef() ) )
650                     {
651                         fprintf(
652                             stderr, "Schema of attributes of layer %s of %s "
653                             "does not match. Skipping it.\n",
654                             poLayer->GetLayerDefn()->GetName(),
655                             papszArgv[nFirstSourceDataset]);
656                         if( bFirstWarningForNonMatchingAttributes )
657                         {
658                             fprintf(
659                                 stderr, "Note : you can override this "
660                                 "behavior with -accept_different_schemas "
661                                 "option,\nbut this may result in a tileindex "
662                                 "incompatible with MapServer\n");
663                             bFirstWarningForNonMatchingAttributes = false;
664                         }
665                         bSkip = true;
666                         break;
667                     }
668                 }
669 
670                 if( bSkip )
671                     continue;
672             }
673 
674 /* -------------------------------------------------------------------- */
675 /*      Get layer extents, and create a corresponding polygon           */
676 /*      geometry.                                                       */
677 /* -------------------------------------------------------------------- */
678             OGREnvelope sExtents;
679 
680             if( poLayer->GetExtent( &sExtents, TRUE ) != OGRERR_NONE )
681             {
682                 fprintf(stderr,
683                         "GetExtent() failed on layer %s of %s, skipping.\n",
684                         poLayer->GetLayerDefn()->GetName(),
685                         papszArgv[nFirstSourceDataset]);
686                 continue;
687             }
688 
689             OGRLinearRing oRing;
690             oRing.addPoint(sExtents.MinX, sExtents.MinY);
691             oRing.addPoint(sExtents.MinX, sExtents.MaxY);
692             oRing.addPoint(sExtents.MaxX, sExtents.MaxY);
693             oRing.addPoint(sExtents.MaxX, sExtents.MinY);
694             oRing.addPoint(sExtents.MinX, sExtents.MinY);
695 
696             OGRPolygon oRegion;
697             oRegion.addRing(&oRing);
698 
699             // If set target srs, do the forward transformation of all points.
700             if( bSetTargetSRS && spatialRef != nullptr )
701             {
702                 OGRCoordinateTransformation* poCT = nullptr;
703                 if( !spatialRef->IsSame(poTargetSRS) )
704                 {
705                     poCT = OGRCreateCoordinateTransformation(spatialRef,
706                                                              poTargetSRS);
707                     if( poCT == nullptr ||
708                         oRegion.transform(poCT) == OGRERR_FAILURE )
709                     {
710                         char* pszSourceWKT = nullptr;
711                         spatialRef->exportToWkt(&pszSourceWKT);
712                         fprintf(
713                             stderr,
714                             "Warning : unable to transform points from source "
715                             "SRS `%s' to target SRS `%s'\n"
716                             "for file `%s' - file skipped\n",
717                             pszSourceWKT, pszTargetSRS,
718                             papszArgv[nFirstSourceDataset]);
719                         CPLFree(pszSourceWKT);
720                         delete poCT;
721                         continue;
722                     }
723                     delete poCT;
724                 }
725             }
726 
727 /* -------------------------------------------------------------------- */
728 /*      Add layer to tileindex.                                         */
729 /* -------------------------------------------------------------------- */
730             OGRFeature oTileFeat(poDstLayer->GetLayerDefn());
731 
732             // TODO(schwehr): Move this off of the stack.
733             char szLocation[5000] = {};
734             snprintf(szLocation, sizeof(szLocation), "%s,%d",
735                      fileNameToWrite, iLayer);
736             oTileFeat.SetGeometry(&oRegion);
737             oTileFeat.SetField(iTileIndexField, szLocation);
738 
739             if( i_SrcSRSName >= 0 && spatialRef != nullptr )
740             {
741                 const char* pszAuthorityCode =
742                     spatialRef->GetAuthorityCode(nullptr);
743                 const char* pszAuthorityName =
744                     spatialRef->GetAuthorityName(nullptr);
745                 char* pszWKT = nullptr;
746                 spatialRef->exportToWkt(&pszWKT);
747                 if( eSrcSRSFormat == FORMAT_AUTO )
748                 {
749                     if( pszAuthorityName != nullptr &&
750                         pszAuthorityCode != nullptr )
751                     {
752                         oTileFeat.SetField(i_SrcSRSName,
753                             CPLSPrintf("%s:%s",
754                                        pszAuthorityName, pszAuthorityCode));
755                     }
756                     else if( nMaxFieldSize == 0 ||
757                             strlen(pszWKT) <= nMaxFieldSize )
758                     {
759                         oTileFeat.SetField(i_SrcSRSName, pszWKT);
760                     }
761                     else
762                     {
763                         char* pszProj4 = nullptr;
764                         if( spatialRef->exportToProj4(&pszProj4) ==
765                               OGRERR_NONE )
766                         {
767                             oTileFeat.SetField(i_SrcSRSName, pszProj4);
768                             CPLFree(pszProj4);
769                         }
770                         else
771                         {
772                             oTileFeat.SetField(i_SrcSRSName, pszWKT);
773                         }
774                     }
775                 }
776                 else if( eSrcSRSFormat == FORMAT_WKT )
777                 {
778                     if( nMaxFieldSize == 0 ||
779                         strlen(pszWKT) <= nMaxFieldSize )
780                     {
781                         oTileFeat.SetField(i_SrcSRSName, pszWKT);
782                     }
783                     else
784                     {
785                         fprintf(
786                             stderr,
787                             "Cannot write WKT for file %s as it is too long!\n",
788                             fileNameToWrite);
789                     }
790                 }
791                 else if( eSrcSRSFormat == FORMAT_PROJ )
792                 {
793                     char* pszProj4 = nullptr;
794                     if( spatialRef->exportToProj4(&pszProj4) == OGRERR_NONE )
795                     {
796                         oTileFeat.SetField(i_SrcSRSName, pszProj4);
797                         CPLFree(pszProj4);
798                     }
799                 }
800                 else if( eSrcSRSFormat == FORMAT_EPSG )
801                 {
802                     if( pszAuthorityName != nullptr &&
803                         pszAuthorityCode != nullptr )
804                         oTileFeat.SetField(i_SrcSRSName,
805                             CPLSPrintf("%s:%s",
806                                        pszAuthorityName, pszAuthorityCode));
807                 }
808                 CPLFree(pszWKT);
809             }
810             if( poDstLayer->CreateFeature(&oTileFeat) != OGRERR_NONE )
811             {
812                 fprintf(stderr,
813                         "Failed to create feature on tile index. "
814                         "Terminating.");
815                 GDALClose(poDstDS);
816                 exit(1);
817             }
818         }
819 
820 /* -------------------------------------------------------------------- */
821 /*      Cleanup this data source.                                       */
822 /* -------------------------------------------------------------------- */
823         CPLFree(fileNameToWrite);
824         GDALClose(poDS);
825     }
826 
827 /* -------------------------------------------------------------------- */
828 /*      Close tile index and clear buffers.                             */
829 /* -------------------------------------------------------------------- */
830     GDALClose(poDstDS);
831     OGRFeatureDefn::DestroyFeatureDefn(poFeatureDefn);
832 
833     if( alreadyExistingSpatialRef != nullptr )
834         alreadyExistingSpatialRef->Release();
835     delete poTargetSRS;
836 
837     CPLFree(current_path);
838 
839     if( nExistingLayers )
840     {
841         for( int i = 0; i < nExistingLayers; i++ )
842         {
843             CPLFree(existingLayersTab[i]);
844         }
845         CPLFree(existingLayersTab);
846     }
847 
848     OGRCleanupAll();
849 
850     return 0;
851 }
852 MAIN_END
853