847 lines
32 KiB
C++
847 lines
32 KiB
C++
/******************************************************************************
|
|
*
|
|
* Project: OpenGIS Simple Features Reference Implementation
|
|
* Purpose: Program to generate a UMN MapServer compatible tile index for a
|
|
* set of OGR data sources.
|
|
* Author: Frank Warmerdam, warmerdam@pobox.com
|
|
*
|
|
******************************************************************************
|
|
* Copyright (c) 2002, Frank Warmerdam
|
|
* Copyright (c) 2007-2010, Even Rouault <even dot rouault at spatialys.com>
|
|
*
|
|
* SPDX-License-Identifier: MIT
|
|
****************************************************************************/
|
|
|
|
#include "cpl_port.h"
|
|
|
|
#include <cassert>
|
|
#include <vector>
|
|
|
|
#include "cpl_conv.h"
|
|
#include "cpl_string.h"
|
|
#include "gdal_version.h"
|
|
#include "gdalargumentparser.h"
|
|
#include "ogr_api.h"
|
|
#include "ogrsf_frmts.h"
|
|
#include "commonutils.h"
|
|
|
|
typedef enum
|
|
{
|
|
FORMAT_AUTO,
|
|
FORMAT_WKT,
|
|
FORMAT_EPSG,
|
|
FORMAT_PROJ
|
|
} SrcSRSFormat;
|
|
|
|
/**
|
|
* @brief Makes sure the GDAL library is properly cleaned up before exiting.
|
|
* @param nCode exit code
|
|
* @todo Move to API
|
|
*/
|
|
static void GDALExit(int nCode)
|
|
{
|
|
GDALDestroy();
|
|
exit(nCode);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* main() */
|
|
/************************************************************************/
|
|
|
|
MAIN_START(nArgc, papszArgv)
|
|
|
|
{
|
|
|
|
// Check strict compilation and runtime library version as we use C++ API.
|
|
if (!GDAL_CHECK_VERSION(papszArgv[0]))
|
|
GDALExit(1);
|
|
|
|
EarlySetConfigOptions(nArgc, papszArgv);
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Processing command line arguments. */
|
|
/* -------------------------------------------------------------------- */
|
|
bool bLayersWildcarded = true;
|
|
std::string osOutputFormat;
|
|
std::string osTileIndexField;
|
|
std::string osOutputName;
|
|
bool bWriteAbsolutePath{false};
|
|
bool bSkipDifferentProjection{false};
|
|
char *pszCurrentPath = nullptr;
|
|
bool bAcceptDifferentSchemas{false};
|
|
bool bFirstWarningForNonMatchingAttributes = true;
|
|
std::string osTargetSRS;
|
|
bool bSetTargetSRS = false;
|
|
std::string osSrcSRSName;
|
|
int i_SrcSRSName = -1;
|
|
SrcSRSFormat eSrcSRSFormat = FORMAT_AUTO;
|
|
size_t nMaxFieldSize = 254;
|
|
std::vector<std::string> aosSrcDatasets;
|
|
std::vector<std::string> aosLayerNames;
|
|
std::vector<int> anLayerNumbers;
|
|
|
|
GDALArgumentParser argParser{"ogrtindex", true};
|
|
|
|
argParser.add_description(
|
|
_("Program to generate a UMN MapServer compatible "
|
|
"tile index for a set of OGR data sources."));
|
|
|
|
argParser.add_epilog(
|
|
_("For more details, see the full documentation for ogrtindex "
|
|
"at\nhttps://gdal.org/programs/ogrtindex.html"));
|
|
|
|
argParser.add_argument("-lnum")
|
|
.metavar("<n>")
|
|
.append()
|
|
.scan<'i', int>()
|
|
.store_into(anLayerNumbers)
|
|
.help(
|
|
_("Add layer number <n> from each source file in the tile index."));
|
|
|
|
argParser.add_argument("-lname")
|
|
.metavar("<name>")
|
|
.append()
|
|
.store_into(aosLayerNames)
|
|
.help(_(
|
|
"Add layer named <name> from each source file in the tile index."));
|
|
|
|
argParser.add_output_format_argument(osOutputFormat);
|
|
|
|
argParser.add_argument("-tileindex")
|
|
.metavar("<tileindex>")
|
|
.default_value("LOCATION")
|
|
.nargs(1)
|
|
.store_into(osTileIndexField)
|
|
.help(_("Name to use for the dataset name."));
|
|
|
|
argParser.add_argument("-write_absolute_path")
|
|
.flag()
|
|
.store_into(bWriteAbsolutePath)
|
|
.help(_("Write absolute path of the source file in the tile index."));
|
|
|
|
argParser.add_argument("-skip_different_projection")
|
|
.flag()
|
|
.store_into(bSkipDifferentProjection)
|
|
.help(_("Skip layers that are not in the same projection as the first "
|
|
"layer."));
|
|
|
|
argParser.add_argument("-t_srs")
|
|
.metavar("<srs_def>")
|
|
.store_into(osTargetSRS)
|
|
.help(
|
|
_("Extent of input files will be transformed to the desired target "
|
|
"coordinate reference system."));
|
|
|
|
argParser.add_argument("-src_srs_name")
|
|
.metavar("<field_name>")
|
|
.store_into(osSrcSRSName)
|
|
.help(_("Name of the field to store the SRS of each tile."));
|
|
|
|
argParser.add_argument("-src_srs_format")
|
|
.metavar("{AUTO|WKT|EPSG|PROJ}")
|
|
.choices("AUTO", "WKT", "EPSG", "PROJ")
|
|
.action(
|
|
[&eSrcSRSFormat](const auto &f)
|
|
{
|
|
if (f == "WKT")
|
|
eSrcSRSFormat = FORMAT_WKT;
|
|
else if (f == "EPSG")
|
|
eSrcSRSFormat = FORMAT_EPSG;
|
|
else if (f == "PROJ")
|
|
eSrcSRSFormat = FORMAT_PROJ;
|
|
else
|
|
eSrcSRSFormat = FORMAT_AUTO;
|
|
})
|
|
.help(_("Format of the source SRS to store in the tile index file."));
|
|
|
|
argParser.add_argument("-accept_different_schemas")
|
|
.flag()
|
|
.store_into(bAcceptDifferentSchemas)
|
|
.help(_(
|
|
"Disable check for identical schemas for layers in input files."));
|
|
|
|
argParser.add_argument("output_dataset")
|
|
.metavar("<output_dataset>")
|
|
.store_into(osOutputName)
|
|
.help(_("Name of the output dataset."));
|
|
|
|
argParser.add_argument("src_dataset")
|
|
.metavar("<src_dataset>")
|
|
.nargs(nargs_pattern::at_least_one)
|
|
.store_into(aosSrcDatasets)
|
|
.help(_("Name of the source dataset(s)."));
|
|
|
|
CPLStringList aosArgv;
|
|
|
|
for (int i = 0; i < nArgc; i++)
|
|
{
|
|
aosArgv.AddString(papszArgv[i]);
|
|
}
|
|
|
|
try
|
|
{
|
|
argParser.parse_args(aosArgv);
|
|
}
|
|
catch (const std::exception &e)
|
|
{
|
|
argParser.display_error_and_usage(e);
|
|
GDALExit(1);
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Validate input */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
//srs_name must be specified when srs_format is specified.
|
|
if (argParser.is_used("-src_srs_format") &&
|
|
!argParser.is_used("-src_srs_name"))
|
|
{
|
|
fprintf(stderr, "-src_srs_name must be specified when -src_srs_format "
|
|
"is specified.\n");
|
|
GDALExit(1);
|
|
}
|
|
|
|
bLayersWildcarded = aosLayerNames.empty() && anLayerNumbers.empty();
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Register format(s). */
|
|
/* -------------------------------------------------------------------- */
|
|
OGRRegisterAll();
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Create and validate target SRS if given. */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
OGRSpatialReference *poTargetSRS = nullptr;
|
|
|
|
if (!osTargetSRS.empty())
|
|
{
|
|
if (bSkipDifferentProjection)
|
|
{
|
|
fprintf(stderr,
|
|
"Warning : -skip_different_projection does not apply "
|
|
"when -t_srs is requested.\n");
|
|
}
|
|
poTargetSRS = new OGRSpatialReference();
|
|
poTargetSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
|
|
// coverity[tainted_data]
|
|
if (poTargetSRS->SetFromUserInput(osTargetSRS.c_str()) != CE_None)
|
|
{
|
|
delete poTargetSRS;
|
|
fprintf(stderr, "Invalid target SRS `%s'.\n", osTargetSRS.c_str());
|
|
GDALExit(1);
|
|
}
|
|
bSetTargetSRS = true;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Try to open as an existing dataset for update access. */
|
|
/* -------------------------------------------------------------------- */
|
|
GDALDataset *poDstDS =
|
|
GDALDataset::FromHandle(OGROpen(osOutputName.c_str(), TRUE, nullptr));
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* If that failed, find the driver so we can create the tile index.*/
|
|
/* -------------------------------------------------------------------- */
|
|
OGRLayer *poDstLayer = nullptr;
|
|
|
|
if (poDstDS == nullptr)
|
|
{
|
|
CPLString osFormat;
|
|
if (osOutputFormat.empty())
|
|
{
|
|
const auto aoDrivers =
|
|
GetOutputDriversFor(osOutputName.c_str(), GDAL_OF_VECTOR);
|
|
if (aoDrivers.empty())
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"Cannot guess driver for %s", osOutputName.c_str());
|
|
GDALExit(10);
|
|
}
|
|
else
|
|
{
|
|
if (aoDrivers.size() > 1)
|
|
{
|
|
CPLError(CE_Warning, CPLE_AppDefined,
|
|
"Several drivers matching %s extension. Using %s",
|
|
CPLGetExtension(osOutputName.c_str()),
|
|
aoDrivers[0].c_str());
|
|
}
|
|
osFormat = aoDrivers[0];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
osFormat = osOutputFormat;
|
|
}
|
|
|
|
if (!EQUAL(osFormat, "ESRI Shapefile"))
|
|
nMaxFieldSize = 0;
|
|
|
|
GDALDriverH hDriver = GDALGetDriverByName(osFormat.c_str());
|
|
if (hDriver == nullptr)
|
|
{
|
|
GDALDriverManager *poDM = GetGDALDriverManager();
|
|
for (int iDriver = 0; iDriver < poDM->GetDriverCount(); iDriver++)
|
|
{
|
|
fprintf(stderr, "Unable to find driver `%s'.\n",
|
|
osFormat.c_str());
|
|
fprintf(stderr, "The following drivers are available:\n");
|
|
|
|
GDALDriver *poIter = poDM->GetDriver(iDriver);
|
|
char **papszDriverMD = poIter->GetMetadata();
|
|
if (CPLTestBool(CSLFetchNameValueDef(
|
|
papszDriverMD, GDAL_DCAP_VECTOR, "FALSE")) &&
|
|
CPLTestBool(CSLFetchNameValueDef(
|
|
papszDriverMD, GDAL_DCAP_CREATE, "FALSE")))
|
|
{
|
|
fprintf(stderr, " -> `%s'\n", poIter->GetDescription());
|
|
}
|
|
}
|
|
GDALExit(1);
|
|
}
|
|
|
|
if (!CPLTestBool(CSLFetchNameValueDef(GDALGetMetadata(hDriver, nullptr),
|
|
GDAL_DCAP_CREATE, "FALSE")))
|
|
{
|
|
fprintf(stderr,
|
|
"%s driver does not support data source creation.\n",
|
|
osFormat.c_str());
|
|
GDALExit(1);
|
|
}
|
|
|
|
/* --------------------------------------------------------------------
|
|
*/
|
|
/* Now create it. */
|
|
/* --------------------------------------------------------------------
|
|
*/
|
|
|
|
poDstDS = GDALDataset::FromHandle(GDALCreate(
|
|
hDriver, osOutputName.c_str(), 0, 0, 0, GDT_Unknown, nullptr));
|
|
if (poDstDS == nullptr)
|
|
{
|
|
fprintf(stderr, "%s driver failed to create %s\n", osFormat.c_str(),
|
|
osOutputName.c_str());
|
|
GDALExit(1);
|
|
}
|
|
|
|
if (poDstDS->GetLayerCount() == 0)
|
|
{
|
|
|
|
OGRSpatialReference *poSrcSpatialRef = nullptr;
|
|
if (bSetTargetSRS)
|
|
{
|
|
// Fetches the SRS from target SRS (if set), or from the SRS of
|
|
// the first layer and use it when creating the
|
|
// tileindex layer.
|
|
poSrcSpatialRef = poTargetSRS->Clone();
|
|
}
|
|
else if (!aosSrcDatasets.empty())
|
|
{
|
|
GDALDataset *poDS = GDALDataset::FromHandle(
|
|
OGROpen(aosSrcDatasets.front().c_str(), FALSE, nullptr));
|
|
if (poDS != nullptr)
|
|
{
|
|
for (int iLayer = 0; iLayer < poDS->GetLayerCount();
|
|
iLayer++)
|
|
{
|
|
bool bRequested = bLayersWildcarded;
|
|
OGRLayer *poLayer = poDS->GetLayer(iLayer);
|
|
|
|
if (!bRequested)
|
|
{
|
|
if (std::find(anLayerNumbers.cbegin(),
|
|
anLayerNumbers.cend(),
|
|
iLayer) != anLayerNumbers.cend())
|
|
bRequested = true;
|
|
else if (std::find(
|
|
aosLayerNames.cbegin(),
|
|
aosLayerNames.cend(),
|
|
poLayer->GetLayerDefn()->GetName()) !=
|
|
aosLayerNames.cend())
|
|
bRequested = true;
|
|
}
|
|
|
|
if (!bRequested)
|
|
continue;
|
|
|
|
if (poLayer->GetSpatialRef())
|
|
poSrcSpatialRef = poLayer->GetSpatialRef()->Clone();
|
|
break;
|
|
}
|
|
}
|
|
|
|
GDALClose(poDS);
|
|
}
|
|
|
|
poDstLayer = poDstDS->CreateLayer("tileindex", poSrcSpatialRef);
|
|
|
|
OGRFieldDefn oLocation(osTileIndexField.c_str(), OFTString);
|
|
oLocation.SetWidth(200);
|
|
poDstLayer->CreateField(&oLocation);
|
|
|
|
if (!osSrcSRSName.empty())
|
|
{
|
|
OGRFieldDefn oSrcSRSNameField(osSrcSRSName.c_str(), OFTString);
|
|
poDstLayer->CreateField(&oSrcSRSNameField);
|
|
}
|
|
|
|
if (poSrcSpatialRef)
|
|
poSrcSpatialRef->Release();
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Identify target layer and field. */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
poDstLayer = poDstDS->GetLayer(0);
|
|
if (poDstLayer == nullptr)
|
|
{
|
|
fprintf(stderr, "Can't find any layer in output tileindex!\n");
|
|
GDALExit(1);
|
|
}
|
|
|
|
const int iTileIndexField =
|
|
poDstLayer->GetLayerDefn()->GetFieldIndex(osTileIndexField.c_str());
|
|
if (iTileIndexField == -1)
|
|
{
|
|
fprintf(stderr, "Can't find %s field in tile index dataset.\n",
|
|
osTileIndexField.c_str());
|
|
GDALExit(1);
|
|
}
|
|
|
|
if (!osSrcSRSName.empty())
|
|
i_SrcSRSName =
|
|
poDstLayer->GetLayerDefn()->GetFieldIndex(osSrcSRSName.c_str());
|
|
|
|
OGRFeatureDefn *poFeatureDefn = nullptr;
|
|
|
|
// Load in memory existing file names in SHP.
|
|
char **existingLayersTab = nullptr;
|
|
OGRSpatialReference *alreadyExistingSpatialRef = nullptr;
|
|
bool alreadyExistingSpatialRefValid = false;
|
|
const int nExistingLayers = static_cast<int>(poDstLayer->GetFeatureCount());
|
|
if (nExistingLayers)
|
|
{
|
|
existingLayersTab =
|
|
static_cast<char **>(CPLMalloc(nExistingLayers * sizeof(char *)));
|
|
for (int i = 0; i < nExistingLayers; i++)
|
|
{
|
|
OGRFeature *feature = poDstLayer->GetNextFeature();
|
|
existingLayersTab[i] =
|
|
CPLStrdup(feature->GetFieldAsString(iTileIndexField));
|
|
if (i == 0)
|
|
{
|
|
char *filename = CPLStrdup(existingLayersTab[i]);
|
|
// j used after for.
|
|
int j = static_cast<int>(strlen(filename)) - 1;
|
|
for (; j >= 0; j--)
|
|
{
|
|
if (filename[j] == ',')
|
|
break;
|
|
}
|
|
GDALDataset *poDS = nullptr;
|
|
if (j >= 0)
|
|
{
|
|
const int iLayer = atoi(filename + j + 1);
|
|
filename[j] = 0;
|
|
poDS = GDALDataset::FromHandle(
|
|
OGROpen(filename, FALSE, nullptr));
|
|
if (poDS != nullptr)
|
|
{
|
|
OGRLayer *poLayer = poDS->GetLayer(iLayer);
|
|
if (poLayer)
|
|
{
|
|
alreadyExistingSpatialRefValid = true;
|
|
alreadyExistingSpatialRef =
|
|
poLayer->GetSpatialRef()
|
|
? poLayer->GetSpatialRef()->Clone()
|
|
: nullptr;
|
|
|
|
if (poFeatureDefn == nullptr)
|
|
poFeatureDefn =
|
|
poLayer->GetLayerDefn()->Clone();
|
|
}
|
|
GDALClose(poDS);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bWriteAbsolutePath)
|
|
{
|
|
pszCurrentPath = CPLGetCurrentDir();
|
|
if (pszCurrentPath == nullptr)
|
|
{
|
|
fprintf(stderr,
|
|
"This system does not support the CPLGetCurrentDir call. "
|
|
"The option -write_absolute_path will have no effect\n");
|
|
bWriteAbsolutePath = false;
|
|
}
|
|
}
|
|
/* ==================================================================== */
|
|
/* Process each input datasource in turn. */
|
|
/* ==================================================================== */
|
|
for (const auto &srcDataSet : aosSrcDatasets)
|
|
{
|
|
|
|
char *fileNameToWrite = nullptr;
|
|
VSIStatBuf sStatBuf;
|
|
|
|
if (bWriteAbsolutePath && CPLIsFilenameRelative(srcDataSet.c_str()) &&
|
|
VSIStat(srcDataSet.c_str(), &sStatBuf) == 0)
|
|
{
|
|
fileNameToWrite = CPLStrdup(
|
|
CPLProjectRelativeFilename(pszCurrentPath, srcDataSet.c_str()));
|
|
}
|
|
else
|
|
{
|
|
fileNameToWrite = CPLStrdup(srcDataSet.c_str());
|
|
}
|
|
|
|
GDALDataset *poDS = GDALDataset::FromHandle(
|
|
OGROpen(srcDataSet.c_str(), FALSE, nullptr));
|
|
|
|
if (poDS == nullptr)
|
|
{
|
|
fprintf(stderr, "Failed to open dataset %s, skipping.\n",
|
|
srcDataSet.c_str());
|
|
CPLFree(fileNameToWrite);
|
|
continue;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------- */
|
|
/* Check all layers, and see if they match requests. */
|
|
/* ----------------------------------------------------------------- */
|
|
|
|
for (int iLayer = 0; iLayer < poDS->GetLayerCount(); iLayer++)
|
|
{
|
|
bool bRequested = bLayersWildcarded;
|
|
OGRLayer *poLayer = poDS->GetLayer(iLayer);
|
|
|
|
if (!bRequested)
|
|
{
|
|
if (std::find(anLayerNumbers.cbegin(), anLayerNumbers.cend(),
|
|
iLayer) != anLayerNumbers.cend())
|
|
bRequested = true;
|
|
else if (std::find(aosLayerNames.cbegin(), aosLayerNames.cend(),
|
|
poLayer->GetLayerDefn()->GetName()) !=
|
|
aosLayerNames.cend())
|
|
bRequested = true;
|
|
}
|
|
|
|
if (!bRequested)
|
|
continue;
|
|
|
|
// Checks that the layer is not already in tileindex.
|
|
int i = 0; // Used after for.
|
|
for (; i < nExistingLayers; i++)
|
|
{
|
|
// TODO(schwehr): Move this off of the stack.
|
|
char szLocation[5000] = {};
|
|
snprintf(szLocation, sizeof(szLocation), "%s,%d",
|
|
fileNameToWrite, iLayer);
|
|
if (EQUAL(szLocation, existingLayersTab[i]))
|
|
{
|
|
fprintf(stderr,
|
|
"Layer %d of %s is already in tileindex. "
|
|
"Skipping it.\n",
|
|
iLayer, srcDataSet.c_str());
|
|
break;
|
|
}
|
|
}
|
|
if (i != nExistingLayers)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
OGRSpatialReference *spatialRef = poLayer->GetSpatialRef();
|
|
// If not set target srs, test that the current file uses same
|
|
// projection as others.
|
|
if (!bSetTargetSRS)
|
|
{
|
|
if (alreadyExistingSpatialRefValid)
|
|
{
|
|
if ((spatialRef != nullptr &&
|
|
alreadyExistingSpatialRef != nullptr &&
|
|
spatialRef->IsSame(alreadyExistingSpatialRef) ==
|
|
FALSE) ||
|
|
((spatialRef != nullptr) !=
|
|
(alreadyExistingSpatialRef != nullptr)))
|
|
{
|
|
fprintf(
|
|
stderr,
|
|
"Warning : layer %d of %s is not using the same "
|
|
"projection system as other files in the "
|
|
"tileindex. This may cause problems when using it "
|
|
"in MapServer for example.%s\n",
|
|
iLayer, srcDataSet.c_str(),
|
|
bSkipDifferentProjection ? " Skipping it" : "");
|
|
if (bSkipDifferentProjection)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
alreadyExistingSpatialRefValid = true;
|
|
alreadyExistingSpatialRef =
|
|
spatialRef ? spatialRef->Clone() : nullptr;
|
|
}
|
|
}
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* Check if all layers in dataset have the same attributes schema. */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
if (poFeatureDefn == nullptr)
|
|
{
|
|
poFeatureDefn = poLayer->GetLayerDefn()->Clone();
|
|
}
|
|
else if (!bAcceptDifferentSchemas)
|
|
{
|
|
OGRFeatureDefn *poFeatureDefnCur = poLayer->GetLayerDefn();
|
|
assert(nullptr != poFeatureDefnCur);
|
|
|
|
const int fieldCount = poFeatureDefnCur->GetFieldCount();
|
|
|
|
if (fieldCount != poFeatureDefn->GetFieldCount())
|
|
{
|
|
fprintf(stderr,
|
|
"Number of attributes of layer %s of %s "
|
|
"does not match ... skipping it.\n",
|
|
poLayer->GetLayerDefn()->GetName(),
|
|
srcDataSet.c_str());
|
|
if (bFirstWarningForNonMatchingAttributes)
|
|
{
|
|
fprintf(
|
|
stderr,
|
|
"Note : you can override this "
|
|
"behavior with -accept_different_schemas option\n"
|
|
"but this may result in a tileindex incompatible "
|
|
"with MapServer\n");
|
|
bFirstWarningForNonMatchingAttributes = false;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
bool bSkip = false;
|
|
for (int fn = 0; fn < poFeatureDefnCur->GetFieldCount(); fn++)
|
|
{
|
|
OGRFieldDefn *poField = poFeatureDefn->GetFieldDefn(fn);
|
|
OGRFieldDefn *poFieldCur =
|
|
poFeatureDefnCur->GetFieldDefn(fn);
|
|
|
|
// XXX - Should those pointers be checked against NULL?
|
|
assert(nullptr != poField);
|
|
assert(nullptr != poFieldCur);
|
|
|
|
if (poField->GetType() != poFieldCur->GetType() ||
|
|
poField->GetWidth() != poFieldCur->GetWidth() ||
|
|
poField->GetPrecision() != poFieldCur->GetPrecision() ||
|
|
!EQUAL(poField->GetNameRef(), poFieldCur->GetNameRef()))
|
|
{
|
|
fprintf(stderr,
|
|
"Schema of attributes of layer %s of %s "
|
|
"does not match. Skipping it.\n",
|
|
poLayer->GetLayerDefn()->GetName(),
|
|
srcDataSet.c_str());
|
|
if (bFirstWarningForNonMatchingAttributes)
|
|
{
|
|
fprintf(
|
|
stderr,
|
|
"Note : you can override this "
|
|
"behavior with -accept_different_schemas "
|
|
"option,\nbut this may result in a tileindex "
|
|
"incompatible with MapServer\n");
|
|
bFirstWarningForNonMatchingAttributes = false;
|
|
}
|
|
bSkip = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (bSkip)
|
|
continue;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------- */
|
|
/* Get layer extents, and create a corresponding polygon */
|
|
/* geometry. */
|
|
/* ----------------------------------------------------------------- */
|
|
OGREnvelope sExtents;
|
|
|
|
if (poLayer->GetExtent(&sExtents, TRUE) != OGRERR_NONE)
|
|
{
|
|
fprintf(stderr,
|
|
"GetExtent() failed on layer %s of %s, skipping.\n",
|
|
poLayer->GetLayerDefn()->GetName(), srcDataSet.c_str());
|
|
continue;
|
|
}
|
|
|
|
OGRLinearRing oRing;
|
|
oRing.addPoint(sExtents.MinX, sExtents.MinY);
|
|
oRing.addPoint(sExtents.MinX, sExtents.MaxY);
|
|
oRing.addPoint(sExtents.MaxX, sExtents.MaxY);
|
|
oRing.addPoint(sExtents.MaxX, sExtents.MinY);
|
|
oRing.addPoint(sExtents.MinX, sExtents.MinY);
|
|
|
|
OGRPolygon oRegion;
|
|
oRegion.addRing(&oRing);
|
|
|
|
// If set target srs, do the forward transformation of all points.
|
|
if (bSetTargetSRS && spatialRef != nullptr)
|
|
{
|
|
if (!spatialRef->IsSame(poTargetSRS))
|
|
{
|
|
auto poCT = std::unique_ptr<OGRCoordinateTransformation>(
|
|
OGRCreateCoordinateTransformation(spatialRef,
|
|
poTargetSRS));
|
|
if (poCT == nullptr ||
|
|
oRegion.transform(poCT.get()) == OGRERR_FAILURE)
|
|
{
|
|
char *pszSourceWKT = nullptr;
|
|
spatialRef->exportToWkt(&pszSourceWKT);
|
|
fprintf(
|
|
stderr,
|
|
"Warning : unable to transform points from source "
|
|
"SRS `%s' to target SRS `%s'\n"
|
|
"for file `%s' - file skipped\n",
|
|
pszSourceWKT, osTargetSRS.c_str(),
|
|
srcDataSet.c_str());
|
|
CPLFree(pszSourceWKT);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ---------------------------------------------------------------- */
|
|
/* Add layer to tileindex. */
|
|
/* ---------------------------------------------------------------- */
|
|
OGRFeature oTileFeat(poDstLayer->GetLayerDefn());
|
|
|
|
// TODO(schwehr): Move this off of the stack.
|
|
char szLocation[5000] = {};
|
|
snprintf(szLocation, sizeof(szLocation), "%s,%d", fileNameToWrite,
|
|
iLayer);
|
|
oTileFeat.SetGeometry(&oRegion);
|
|
oTileFeat.SetField(iTileIndexField, szLocation);
|
|
|
|
if (i_SrcSRSName >= 0 && spatialRef != nullptr)
|
|
{
|
|
const char *pszAuthorityCode =
|
|
spatialRef->GetAuthorityCode(nullptr);
|
|
const char *pszAuthorityName =
|
|
spatialRef->GetAuthorityName(nullptr);
|
|
char *pszWKT = nullptr;
|
|
spatialRef->exportToWkt(&pszWKT);
|
|
if (eSrcSRSFormat == FORMAT_AUTO)
|
|
{
|
|
if (pszAuthorityName != nullptr &&
|
|
pszAuthorityCode != nullptr)
|
|
{
|
|
oTileFeat.SetField(i_SrcSRSName,
|
|
CPLSPrintf("%s:%s", pszAuthorityName,
|
|
pszAuthorityCode));
|
|
}
|
|
else if (nMaxFieldSize == 0 ||
|
|
strlen(pszWKT) <= nMaxFieldSize)
|
|
{
|
|
oTileFeat.SetField(i_SrcSRSName, pszWKT);
|
|
}
|
|
else
|
|
{
|
|
char *pszProj4 = nullptr;
|
|
if (spatialRef->exportToProj4(&pszProj4) == OGRERR_NONE)
|
|
{
|
|
oTileFeat.SetField(i_SrcSRSName, pszProj4);
|
|
CPLFree(pszProj4);
|
|
}
|
|
else
|
|
{
|
|
oTileFeat.SetField(i_SrcSRSName, pszWKT);
|
|
}
|
|
}
|
|
}
|
|
else if (eSrcSRSFormat == FORMAT_WKT)
|
|
{
|
|
if (nMaxFieldSize == 0 || strlen(pszWKT) <= nMaxFieldSize)
|
|
{
|
|
oTileFeat.SetField(i_SrcSRSName, pszWKT);
|
|
}
|
|
else
|
|
{
|
|
fprintf(
|
|
stderr,
|
|
"Cannot write WKT for file %s as it is too long!\n",
|
|
fileNameToWrite);
|
|
}
|
|
}
|
|
else if (eSrcSRSFormat == FORMAT_PROJ)
|
|
{
|
|
char *pszProj4 = nullptr;
|
|
if (spatialRef->exportToProj4(&pszProj4) == OGRERR_NONE)
|
|
{
|
|
oTileFeat.SetField(i_SrcSRSName, pszProj4);
|
|
CPLFree(pszProj4);
|
|
}
|
|
}
|
|
else if (eSrcSRSFormat == FORMAT_EPSG)
|
|
{
|
|
if (pszAuthorityName != nullptr &&
|
|
pszAuthorityCode != nullptr)
|
|
oTileFeat.SetField(i_SrcSRSName,
|
|
CPLSPrintf("%s:%s", pszAuthorityName,
|
|
pszAuthorityCode));
|
|
}
|
|
CPLFree(pszWKT);
|
|
}
|
|
if (poDstLayer->CreateFeature(&oTileFeat) != OGRERR_NONE)
|
|
{
|
|
fprintf(stderr, "Failed to create feature on tile index. "
|
|
"Terminating.");
|
|
GDALClose(poDstDS);
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
/* ---------------------------------------------------------------- */
|
|
/* Cleanup this data source. */
|
|
/* ---------------------------------------------------------------- */
|
|
CPLFree(fileNameToWrite);
|
|
GDALClose(poDS);
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Close tile index and clear buffers. */
|
|
/* -------------------------------------------------------------------- */
|
|
int nRetCode = 0;
|
|
if (GDALClose(poDstDS) != CE_None)
|
|
nRetCode = 1;
|
|
OGRFeatureDefn::DestroyFeatureDefn(poFeatureDefn);
|
|
|
|
if (alreadyExistingSpatialRef != nullptr)
|
|
alreadyExistingSpatialRef->Release();
|
|
delete poTargetSRS;
|
|
|
|
CPLFree(pszCurrentPath);
|
|
|
|
if (nExistingLayers)
|
|
{
|
|
for (int i = 0; i < nExistingLayers; i++)
|
|
{
|
|
CPLFree(existingLayersTab[i]);
|
|
}
|
|
CPLFree(existingLayersTab);
|
|
}
|
|
|
|
GDALDestroy();
|
|
|
|
return nRetCode;
|
|
}
|
|
|
|
MAIN_END
|