1 /******************************************************************************
2  * $Id: ogrtindex.java b55a33407a80673ec314b165c82f47dd02e9dc9c 2020-04-27 20:37:55 +0200 Even Rouault $
3  *
4  * Project:  OpenGIS Simple Features Reference Implementation
5  * Purpose:  Program to generate a UMN MapServer compatible tile index for a
6  *           set of OGR data sources.
7  * Author:   Frank Warmerdam, warmerdam@pobox.com
8  *
9  ******************************************************************************
10  * Copyright (c) 2002, Frank Warmerdam
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 import org.gdal.gdal.gdal;
32 import org.gdal.ogr.DataSource;
33 import org.gdal.ogr.Driver;
34 import org.gdal.ogr.Feature;
35 import org.gdal.ogr.FeatureDefn;
36 import org.gdal.ogr.FieldDefn;
37 import org.gdal.ogr.Geometry;
38 import org.gdal.ogr.Layer;
39 import org.gdal.ogr.ogr;
40 import org.gdal.osr.SpatialReference;
41 
42 /* Note : this is the most direct port of ogrtindex.cpp possible */
43 /* It could be made much more java'ish ! */
44 
45 public class ogrtindex {
46 
main(String[] args)47    public static void main(String[] args) {
48 
49       boolean bLayersWildcarded = true;
50       int nFirstSourceDataset = -1;
51       String pszFormat = "ESRI Shapefile";
52       String pszTileIndexField = "LOCATION";
53       String pszOutputName = null;
54       boolean write_absolute_path = false;
55       boolean skip_different_projection = false;
56       String current_path = null;
57       boolean accept_different_schemas = false;
58       boolean bFirstWarningForNonMatchingAttributes = true;
59 
60       ogr.DontUseExceptions();
61 
62       /* -------------------------------------------------------------------- */
63       /*      Register format(s).                                             */
64       /* -------------------------------------------------------------------- */
65       // fixed: http://osgeo-org.1803224.n2.nabble.com/GDAL-Java-Binding-meomory-problem-under-intensive-method-calls-td7155011.html#a7157916
66       if( ogr.GetDriverCount() == 0 )
67          ogr.RegisterAll();
68 
69       /* -------------------------------------------------------------------- */
70       /*      Processing command line arguments.                              */
71       /* -------------------------------------------------------------------- */
72       args = ogr.GeneralCmdLineProcessor( args );
73 
74       if( args.length < 2 )
75       {
76          Usage();
77          return;
78       }
79 
80       for( int iArg = 0; iArg < args.length; iArg++ )
81       {
82          if( args[iArg].equalsIgnoreCase("-f") && iArg < args.length-1 )
83          {
84             pszFormat = args[++iArg];
85          }
86          else if( args[iArg].equalsIgnoreCase("-write_absolute_path") )
87          {
88             write_absolute_path = true;
89          }
90          else if( args[iArg].equalsIgnoreCase("-skip_different_projection") )
91          {
92             skip_different_projection = true;
93          }
94          else if( args[iArg].equalsIgnoreCase("-accept_different_schemas") )
95          {
96             accept_different_schemas = true;
97          }
98          else if( args[iArg].equalsIgnoreCase("-tileindex") && iArg < args.length-1 )
99          {
100             pszTileIndexField = args[++iArg];
101          }
102          else if( args[iArg].equalsIgnoreCase("-lnum")
103                || args[iArg].equalsIgnoreCase("-lname") )
104          {
105             iArg++;
106             bLayersWildcarded = false;
107          }
108          else if( args[iArg].charAt(0) == '-' )
109             Usage();
110          else if( pszOutputName == null )
111             pszOutputName = args[iArg];
112          else if( nFirstSourceDataset == -1 )
113             nFirstSourceDataset = iArg;
114       }
115 
116       if( pszOutputName == null || nFirstSourceDataset == -1 )
117          Usage();
118 
119       /* -------------------------------------------------------------------- */
120       /*      Try to open as an existing dataset for update access.           */
121       /* -------------------------------------------------------------------- */
122       DataSource poDstDS;
123       Layer poDstLayer = null;
124 
125       poDstDS = ogr.Open( pszOutputName, true );
126 
127       /* -------------------------------------------------------------------- */
128       /*      If that failed, find the driver so we can create the tile index.*/
129       /* -------------------------------------------------------------------- */
130       if( poDstDS == null )
131       {
132          Driver poDriver = null;
133 
134          for( int iDriver = 0; iDriver < ogr.GetDriverCount() && poDriver == null; iDriver++ )
135          {
136             poDriver = ogr.GetDriverByName(pszFormat);
137          }
138 
139          if( poDriver == null )
140          {
141             System.err.print("Unable to find driver '"+pszFormat+"'.\n");
142             System.err.print("The following drivers are available:\n" );
143 
144             for( int iDriver = 0; iDriver < ogr.GetDriverCount(); iDriver++ )
145             {
146                System.err.print("  . '"+ogr.GetDriver(iDriver).GetName()+"'\n");
147             }
148             return;
149          }
150 
151          if( !poDriver.TestCapability( ogr.ODrCCreateDataSource ) )
152          {
153             System.err.print(pszFormat + " driver does not support data source creation.\n");
154             return;
155          }
156 
157          /* -------------------------------------------------------------------- */
158          /*      Now create it.                                                  */
159          /* -------------------------------------------------------------------- */
160 
161          poDstDS = poDriver.CreateDataSource( pszOutputName );
162          if( poDstDS == null )
163          {
164             System.err.print(pszFormat + " driver failed to create "+pszOutputName+"\n");
165             return;
166          }
167 
168          if ( poDstDS.GetLayerCount() == 0 )
169          {
170             FieldDefn oLocation = new FieldDefn( pszTileIndexField, ogr.OFTString );
171 
172             oLocation.SetWidth( 200 );
173 
174             if( nFirstSourceDataset < args.length-2 && args[nFirstSourceDataset].charAt(0) == '-' )
175             {
176                nFirstSourceDataset++;
177             }
178 
179             SpatialReference poSrcSpatialRef = null;
180 
181             /* Fetches the SRS of the first layer and use it when creating the tileindex layer */
182             if (nFirstSourceDataset < args.length)
183             {
184                DataSource poDS = ogr.Open( args[nFirstSourceDataset],false );
185 
186                if (poDS!=null)
187                {
188                   for(int iLayer = 0; iLayer < poDS.GetLayerCount(); iLayer++ )
189                   {
190                      boolean bRequested = bLayersWildcarded;
191                      Layer poLayer = poDS.GetLayer(iLayer);
192 
193                      for(int iArg = 0; iArg < args.length && !bRequested; iArg++ )
194                      {
195                         if( args[iArg].equalsIgnoreCase("-lnum")
196                               && Integer.parseInt(args[iArg+1]) == iLayer )
197                            bRequested = true;
198                         else if( args[iArg].equalsIgnoreCase("-lname")
199                               && args[iArg+1].equalsIgnoreCase(poLayer.GetLayerDefn().GetName()) )
200                            bRequested = true;
201                      }
202 
203                      if( !bRequested )
204                         continue;
205 
206                      if ( poLayer.GetSpatialRef() != null)
207                         poSrcSpatialRef = poLayer.GetSpatialRef().Clone();
208                      break;
209                   }
210                }
211 
212                poDS.delete();
213             }
214 
215             poDstLayer = poDstDS.CreateLayer( "tileindex", poSrcSpatialRef );
216             poDstLayer.CreateField( oLocation, ogr.OFTString );
217 
218             /* with the OGR Java bindings, avoid using the delete() methods,
219              * except on the datasource objects, where it is necessary to close properly the
220              * native file handles.
221              */
222             // poSrcSpatialRef.delete();
223          }
224       }
225 
226       /* -------------------------------------------------------------------- */
227       /*      Identify target layer and field.                                */
228       /* -------------------------------------------------------------------- */
229       int   iTileIndexField;
230 
231       poDstLayer = poDstDS.GetLayer(0);
232       if( poDstLayer == null )
233       {
234          System.err.print("Can't find any layer in output tileindex!\n" );
235          return;
236       }
237 
238       iTileIndexField =
239          poDstLayer.GetLayerDefn().GetFieldIndex( pszTileIndexField );
240       if( iTileIndexField == -1 )
241       {
242          System.err.print("Can't find "+pszTileIndexField+" field in tile index dataset.\n");
243          return;
244       }
245 
246       FeatureDefn poFeatureDefn = null;
247 
248       /* Load in memory existing file names in SHP */
249       int nExistingLayers = 0;
250       String[] existingLayersTab = null;
251       SpatialReference alreadyExistingSpatialRef = null;
252       boolean alreadyExistingSpatialRefValid = false;
253       nExistingLayers = (int)poDstLayer.GetFeatureCount();
254       if (nExistingLayers > 0)
255       {
256          existingLayersTab = new String[nExistingLayers];
257          for(int i=0;i<nExistingLayers;i++)
258          {
259             Feature feature = poDstLayer.GetNextFeature();
260             existingLayersTab[i] = feature.GetFieldAsString( iTileIndexField);
261             if (i == 0)
262             {
263                DataSource       poDS;
264                String filename = existingLayersTab[i];
265                int j;
266                for(j=filename.length()-1;j>=0;j--)
267                {
268                   if (filename.charAt(j) == ',')
269                      break;
270                }
271                if (j >= 0)
272                {
273                   int iLayer = Integer.parseInt(filename.substring(j + 1));
274                   filename = filename.substring(0, j);
275                   poDS = ogr.Open(filename,false );
276                   if (poDS!=null)
277                   {
278                      Layer poLayer = poDS.GetLayer(iLayer);
279                      if (poLayer!=null)
280                      {
281                         alreadyExistingSpatialRefValid = true;
282                         alreadyExistingSpatialRef =
283                            (poLayer.GetSpatialRef()!=null) ? poLayer.GetSpatialRef().Clone() : null;
284 
285                            if (poFeatureDefn == null) {
286                               poFeatureDefn = CloneFeatureDefn(poLayer.GetLayerDefn()); // XXX: no Clone supported in java binding!!
287                            }
288                      }
289                      poDS.delete();
290                   }
291                }
292             }
293          }
294       }
295 
296       /* ignore check */
297       //if (write_absolute_path)
298       //{
299       //   current_path = CPLGetCurrentDir();
300       //   if (current_path == null)
301       //   {
302       //      fprintf( stderr, "This system does not support the CPLGetCurrentDir call. "
303       //      "The option -write_absolute_path will have no effect\n");
304       //      write_absolute_path = false;
305       //   }
306       //}
307       /* ==================================================================== */
308       /*      Process each input datasource in turn.                          */
309       /* ==================================================================== */
310 
311       for(; nFirstSourceDataset < args.length; nFirstSourceDataset++ )
312       {
313          DataSource       poDS;
314 
315          if( args[nFirstSourceDataset].charAt(0) == '-' )
316          {
317             nFirstSourceDataset++;
318             continue;
319          }
320 
321          String fileNameToWrite;
322 
323          //VSIStatBuf sStatBuf;
324          // FIXME: handle absolute path check
325          //if (write_absolute_path && CPLIsFilenameRelative( args[nFirstSourceDataset] ) &&
326          //      VSIStat( args[nFirstSourceDataset], &sStatBuf ) == 0)
327          //{
328          //   fileNameToWrite = CPLStrdup(CPLProjectRelativeFilename(current_path,args[nFirstSourceDataset]));
329          //}
330          //else
331          //{
332          //   fileNameToWrite = args[nFirstSourceDataset];
333          //}
334          fileNameToWrite = args[nFirstSourceDataset];
335 
336          poDS = ogr.Open( args[nFirstSourceDataset], false );
337 
338          if( poDS == null )
339          {
340             System.err.print("Failed to open dataset "+args[nFirstSourceDataset]+", skipping.\n");
341             continue;
342          }
343 
344          /* -------------------------------------------------------------------- */
345          /*      Check all layers, and see if they match requests.               */
346          /* -------------------------------------------------------------------- */
347          for(int iLayer = 0; iLayer < poDS.GetLayerCount(); iLayer++ )
348          {
349             boolean bRequested = bLayersWildcarded;
350             Layer poLayer = poDS.GetLayer(iLayer);
351 
352             for(int iArg = 0; iArg < args.length && !bRequested; iArg++ )
353             {
354                if( args[iArg].equalsIgnoreCase("-lnum")
355                      && Integer.parseInt(args[iArg+1]) == iLayer )
356                   bRequested = true;
357                else if( args[iArg].equalsIgnoreCase("-lname")
358                      && args[iArg+1].equalsIgnoreCase(poLayer.GetLayerDefn().GetName()) )
359                   bRequested = true;
360             }
361 
362             if( !bRequested )
363                continue;
364 
365             /* Checks that the layer is not already in tileindex */
366             int i;
367             for(i=0;i<nExistingLayers;i++)
368             {
369                String szLocation = fileNameToWrite+","+iLayer;
370                if (szLocation.equalsIgnoreCase(existingLayersTab[i]))
371                {
372                   System.err.println("Layer "+iLayer+" of "+args[nFirstSourceDataset]+" is already in tileindex. Skipping it.\n");
373                   break;
374                }
375             }
376             if (i != nExistingLayers)
377             {
378                continue;
379             }
380 
381             SpatialReference spatialRef = poLayer.GetSpatialRef();
382             if (alreadyExistingSpatialRefValid)
383             {
384                if ((spatialRef != null && alreadyExistingSpatialRef != null &&
385                      spatialRef.IsSame(alreadyExistingSpatialRef) == 0) ||
386                      ((spatialRef != null) != (alreadyExistingSpatialRef != null)))
387                {
388                   System.err.print("Warning : layer "+iLayer+" of "+args[nFirstSourceDataset]+" is not using the same projection system as "
389                         + "other files in the tileindex. This may cause problems when "
390                         + "using it in MapServer for example."+((skip_different_projection) ? " Skipping it" : "")+"\n");
391                   ;
392                   if (skip_different_projection)
393                   {
394                      continue;
395                   }
396                }
397             }
398             else
399             {
400                alreadyExistingSpatialRefValid = true;
401                alreadyExistingSpatialRef = (spatialRef!=null) ? spatialRef.Clone() : null;
402             }
403 
404             /* -------------------------------------------------------------------- */
405             /*    Check if all layers in dataset have the same attributes  schema. */
406             /* -------------------------------------------------------------------- */
407             if( poFeatureDefn == null )
408             {
409                poFeatureDefn = CloneFeatureDefn(poLayer.GetLayerDefn()); // XXX: no Clone supported in java binding!!
410             }
411             else if ( !accept_different_schemas )
412             {
413                FeatureDefn poFeatureDefnCur = poLayer.GetLayerDefn();
414                assert(null != poFeatureDefnCur);
415 
416                int fieldCount = poFeatureDefnCur.GetFieldCount();
417 
418                if( fieldCount != poFeatureDefn.GetFieldCount())
419                {
420                   System.err.print("Number of attributes of layer "+poLayer.GetLayerDefn().GetName()+" of "+args[nFirstSourceDataset]+" does not match ... skipping it.\n");
421 
422                   if (bFirstWarningForNonMatchingAttributes)
423                   {
424                      System.err.print("Note : you can override this behavior with -accept_different_schemas option\n"
425                            + "but this may result in a tileindex incompatible with MapServer\n");
426                      bFirstWarningForNonMatchingAttributes = false;
427                   }
428                   continue;
429                }
430 
431                boolean bSkip = false;
432                for( int fn = 0; fn < poFeatureDefnCur.GetFieldCount(); fn++ )
433                {
434                   FieldDefn poField = poFeatureDefn.GetFieldDefn(fn);
435                   FieldDefn poFieldCur = poFeatureDefnCur.GetFieldDefn(fn);
436 
437                   /* XXX - Should those pointers be checked against null? */
438                   assert(null != poField);
439                   assert(null != poFieldCur);
440 
441                   if( !poField.GetTypeName().equalsIgnoreCase(poFieldCur.GetTypeName())
442                         || poField.GetWidth() != poFieldCur.GetWidth()
443                         || poField.GetPrecision() != poFieldCur.GetPrecision()
444                         || !poField.GetNameRef().equalsIgnoreCase(poFieldCur.GetNameRef()) )
445                   {
446                      System.err.print("Schema of attributes of layer "+poLayer.GetLayerDefn().GetName()+" of "+args[nFirstSourceDataset]+" does not match ... skipping it.\n");
447 
448                      if (bFirstWarningForNonMatchingAttributes)
449                      {
450                         System.err.print("Note : you can override this behavior with -accept_different_schemas option\n"
451                               + "but this may result in a tileindex incompatible with MapServer\n");
452                         bFirstWarningForNonMatchingAttributes = false;
453                      }
454                      bSkip = true;
455                      break;
456                   }
457                }
458 
459                if (bSkip)
460                   continue;
461             }
462 
463 
464             /* -------------------------------------------------------------------- */
465             /*      Get layer extents, and create a corresponding polygon           */
466             /*      geometry.                                                       */
467             /* -------------------------------------------------------------------- */
468             double sExtents[] = poLayer.GetExtent(true);
469             Geometry/*Polygon*/ oRegion = new Geometry(ogr.wkbPolygon);
470             Geometry/*LinearRing*/ oRing = new Geometry(ogr.wkbLinearRing);
471 
472             if (sExtents == null) {
473                System.err.print("GetExtent() failed on layer "+poLayer.GetLayerDefn().GetName()+" of "+args[nFirstSourceDataset]+", skipping.\n");
474                continue;
475             }
476 
477             // XXX: sExtents [minX, maxX, minY, maxY]
478             //oRing.addPoint( sExtents.MinX, sExtents.MinY );
479             //oRing.addPoint( sExtents.MinX, sExtents.MaxY );
480             //oRing.addPoint( sExtents.MaxX, sExtents.MaxY );
481             //oRing.addPoint( sExtents.MaxX, sExtents.MinY );
482             //oRing.addPoint( sExtents.MinX, sExtents.MinY );
483             oRing.AddPoint_2D( sExtents[0], sExtents[2] );
484             oRing.AddPoint_2D( sExtents[0], sExtents[3] );
485             oRing.AddPoint_2D( sExtents[1], sExtents[3] );
486             oRing.AddPoint_2D( sExtents[1], sExtents[2] );
487             oRing.AddPoint_2D( sExtents[0], sExtents[2] );
488 
489             oRegion.AddGeometry( oRing );
490 
491             /* -------------------------------------------------------------------- */
492             /*      Add layer to tileindex.                                         */
493             /* -------------------------------------------------------------------- */
494             String        szLocation = fileNameToWrite+","+iLayer;
495             Feature  oTileFeat = new Feature( poDstLayer.GetLayerDefn() );
496 
497             oTileFeat.SetGeometry( oRegion );
498             oTileFeat.SetField( iTileIndexField, szLocation );
499 
500             if( poDstLayer.CreateFeature( oTileFeat ) != ogr.OGRERR_NONE )
501             {
502                System.err.print("Failed to create feature on tile index ... terminating." );
503                poDS.delete();
504                poDstDS.delete();
505                return;
506             }
507          }
508 
509          /* -------------------------------------------------------------------- */
510          /*      Cleanup this data source.                                       */
511          /* -------------------------------------------------------------------- */
512          poDS.delete();
513       }
514 
515       /* -------------------------------------------------------------------- */
516       /*      Close tile index and clear buffers.                             */
517       /* -------------------------------------------------------------------- */
518       poDstDS.delete();
519       //OGRFeatureDefn::DestroyFeatureDefn( poFeatureDefn );
520 
521       //if (alreadyExistingSpatialRef != null)
522       //   alreadyExistingSpatialRef.delete();
523 
524 
525    }
526 
527 
528 
529    /************************************************************************/
530    /*                               Usage()                                */
531    /************************************************************************/
532 
Usage()533    static void Usage()
534 
535    {
536       System.out.print(
537             "Usage: ogrtindex [-lnum n]... [-lname name]... [-f output_format]\n"
538             + "                 [-write_absolute_path] [-skip_different_projection]\n"
539             + "                 [-accept_different_schemas]\n"
540             + "                 output_dataset src_dataset...\n" );
541       System.out.print( "\n" );
542       System.out.print(
543             "  -lnum n: Add layer number 'n' from each source file\n"
544             + "           in the tile index.\n" );
545       System.out.print(
546             "  -lname name: Add the layer named 'name' from each source file\n"
547             + "               in the tile index.\n" );
548       System.out.print(
549             "  -f output_format: Select an output format name.  The default\n"
550             + "                    is to create a shapefile.\n" );
551       System.out.print(
552             "  -tileindex field_name: The name to use for the dataset name.\n"
553             + "                         Defaults to LOCATION.\n" );
554       System.out.print( "  -write_absolute_path: Filenames are written with absolute paths.\n" );
555       System.out.print(
556             "  -skip_different_projection: Only layers with same projection ref \n"
557             + "        as layers already inserted in the tileindex will be inserted.\n" );
558       System.out.print(
559             "  -accept_different_schemas: by default ogrtindex checks that all layers inserted\n"
560             + "                             into the index have the same attribute schemas. If you\n"
561             + "                             specify this option, this test will be disabled. Be aware that\n"
562             + "                             resulting index may be incompatible with MapServer!\n" );
563       System.out.print( "\n" );
564       System.out.print(
565             "If no -lnum or -lname arguments are given it is assumed that\n"
566             + "all layers in source datasets should be added to the tile index\n"
567             + "as independent records.\n" );
568    }
569 
570     /* Adhoc method to workaround the lack of a FeatureDefn.Clone() method */
CloneFeatureDefn(FeatureDefn poSrcFeatureDefn)571     static FeatureDefn CloneFeatureDefn(FeatureDefn poSrcFeatureDefn)
572     {
573         FeatureDefn poFeatureDefn = new FeatureDefn(poSrcFeatureDefn.GetName());
574         poFeatureDefn.SetGeomType(poSrcFeatureDefn.GetGeomType());
575         for(int fi = 0; fi < poSrcFeatureDefn.GetFieldCount(); fi++)
576             poFeatureDefn.AddFieldDefn(poSrcFeatureDefn.GetFieldDefn(fi));
577         return poFeatureDefn;
578     }
579 }
580