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