1556 lines
56 KiB
C++
1556 lines
56 KiB
C++
/******************************************************************************
|
|
*
|
|
* Project: GDAL Utilities
|
|
* Purpose: Rasterize OGR shapes into a GDAL raster.
|
|
* Author: Frank Warmerdam <warmerdam@pobox.com>
|
|
*
|
|
******************************************************************************
|
|
* Copyright (c) 2005, Frank Warmerdam <warmerdam@pobox.com>
|
|
* Copyright (c) 2008-2015, Even Rouault <even dot rouault at spatialys dot com>
|
|
*
|
|
* SPDX-License-Identifier: MIT
|
|
****************************************************************************/
|
|
|
|
#include "cpl_port.h"
|
|
#include "gdal_utils.h"
|
|
#include "gdal_utils_priv.h"
|
|
|
|
#include <cmath>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <algorithm>
|
|
#include <limits>
|
|
#include <vector>
|
|
|
|
#include "commonutils.h"
|
|
#include "cpl_conv.h"
|
|
#include "cpl_error.h"
|
|
#include "cpl_progress.h"
|
|
#include "cpl_string.h"
|
|
#include "gdal.h"
|
|
#include "gdal_alg.h"
|
|
#include "gdal_priv.h"
|
|
#include "ogr_api.h"
|
|
#include "ogr_core.h"
|
|
#include "ogr_srs_api.h"
|
|
#include "gdalargumentparser.h"
|
|
|
|
/************************************************************************/
|
|
/* GDALRasterizeOptions() */
|
|
/************************************************************************/
|
|
|
|
struct GDALRasterizeOptions
|
|
{
|
|
std::vector<int> anBandList{};
|
|
std::vector<double> adfBurnValues{};
|
|
bool bInverse = false;
|
|
std::string osFormat{};
|
|
bool b3D = false;
|
|
GDALProgressFunc pfnProgress = GDALDummyProgress;
|
|
void *pProgressData = nullptr;
|
|
std::vector<std::string> aosLayers{};
|
|
std::string osSQL{};
|
|
std::string osDialect{};
|
|
std::string osBurnAttribute{};
|
|
std::string osWHERE{};
|
|
CPLStringList aosRasterizeOptions{};
|
|
CPLStringList aosTO{};
|
|
double dfXRes = 0;
|
|
double dfYRes = 0;
|
|
CPLStringList aosCreationOptions{};
|
|
GDALDataType eOutputType = GDT_Unknown;
|
|
std::vector<double> adfInitVals{};
|
|
std::string osNoData{};
|
|
OGREnvelope sEnvelop{};
|
|
int nXSize = 0;
|
|
int nYSize = 0;
|
|
OGRSpatialReference oOutputSRS{};
|
|
|
|
bool bTargetAlignedPixels = false;
|
|
bool bCreateOutput = false;
|
|
};
|
|
|
|
/************************************************************************/
|
|
/* GDALRasterizeOptionsGetParser() */
|
|
/************************************************************************/
|
|
|
|
static std::unique_ptr<GDALArgumentParser>
|
|
GDALRasterizeOptionsGetParser(GDALRasterizeOptions *psOptions,
|
|
GDALRasterizeOptionsForBinary *psOptionsForBinary)
|
|
{
|
|
auto argParser = std::make_unique<GDALArgumentParser>(
|
|
"gdal_rasterize", /* bForBinary=*/psOptionsForBinary != nullptr);
|
|
|
|
argParser->add_description(_("Burns vector geometries into a raster."));
|
|
|
|
argParser->add_epilog(
|
|
_("This program burns vector geometries (points, lines, and polygons) "
|
|
"into the raster band(s) of a raster image."));
|
|
|
|
// Dealt manually as argparse::nargs_pattern::at_least_one is problematic
|
|
argParser->add_argument("-b")
|
|
.metavar("<band>")
|
|
.append()
|
|
.scan<'i', int>()
|
|
//.nargs(argparse::nargs_pattern::at_least_one)
|
|
.help(_("The band(s) to burn values into."));
|
|
|
|
argParser->add_argument("-i")
|
|
.flag()
|
|
.store_into(psOptions->bInverse)
|
|
.help(_("Invert rasterization."));
|
|
|
|
argParser->add_argument("-at")
|
|
.flag()
|
|
.action(
|
|
[psOptions](const std::string &) {
|
|
psOptions->aosRasterizeOptions.SetNameValue("ALL_TOUCHED",
|
|
"TRUE");
|
|
})
|
|
.help(_("Enables the ALL_TOUCHED rasterization option."));
|
|
|
|
// Mutually exclusive options: -burn, -3d, -a
|
|
{
|
|
// Required if options for binary
|
|
auto &group = argParser->add_mutually_exclusive_group(
|
|
psOptionsForBinary != nullptr);
|
|
|
|
// Dealt manually as argparse::nargs_pattern::at_least_one is problematic
|
|
group.add_argument("-burn")
|
|
.metavar("<value>")
|
|
.scan<'g', double>()
|
|
.append()
|
|
//.nargs(argparse::nargs_pattern::at_least_one)
|
|
.help(_("A fixed value to burn into the raster band(s)."));
|
|
|
|
group.add_argument("-a")
|
|
.metavar("<attribute_name>")
|
|
.store_into(psOptions->osBurnAttribute)
|
|
.help(_("Name of the field in the input layer to get the burn "
|
|
"values from."));
|
|
|
|
group.add_argument("-3d")
|
|
.flag()
|
|
.store_into(psOptions->b3D)
|
|
.action(
|
|
[psOptions](const std::string &) {
|
|
psOptions->aosRasterizeOptions.SetNameValue(
|
|
"BURN_VALUE_FROM", "Z");
|
|
})
|
|
.help(_("Indicates that a burn value should be extracted from the "
|
|
"\"Z\" values of the feature."));
|
|
}
|
|
|
|
argParser->add_argument("-add")
|
|
.flag()
|
|
.action(
|
|
[psOptions](const std::string &) {
|
|
psOptions->aosRasterizeOptions.SetNameValue("MERGE_ALG", "ADD");
|
|
})
|
|
.help(_("Instead of burning a new value, this adds the new value to "
|
|
"the existing raster."));
|
|
|
|
// Undocumented
|
|
argParser->add_argument("-chunkysize")
|
|
.flag()
|
|
.hidden()
|
|
.action(
|
|
[psOptions](const std::string &s) {
|
|
psOptions->aosRasterizeOptions.SetNameValue("CHUNKYSIZE",
|
|
s.c_str());
|
|
});
|
|
|
|
// Mutually exclusive -l, -sql
|
|
{
|
|
auto &group = argParser->add_mutually_exclusive_group(false);
|
|
|
|
group.add_argument("-l")
|
|
.metavar("<layer_name>")
|
|
.append()
|
|
.store_into(psOptions->aosLayers)
|
|
.help(_("Name of the layer(s) to process."));
|
|
|
|
group.add_argument("-sql")
|
|
.metavar("<sql_statement>")
|
|
.store_into(psOptions->osSQL)
|
|
.action(
|
|
[psOptions](const std::string &sql)
|
|
{
|
|
GByte *pabyRet = nullptr;
|
|
if (!sql.empty() && sql.at(0) == '@' &&
|
|
VSIIngestFile(nullptr, sql.substr(1).c_str(), &pabyRet,
|
|
nullptr, 1024 * 1024))
|
|
{
|
|
GDALRemoveBOM(pabyRet);
|
|
char *pszSQLStatement =
|
|
reinterpret_cast<char *>(pabyRet);
|
|
psOptions->osSQL = CPLStrdup(
|
|
CPLRemoveSQLComments(pszSQLStatement).c_str());
|
|
VSIFree(pszSQLStatement);
|
|
}
|
|
})
|
|
.help(
|
|
_("An SQL statement to be evaluated against the datasource to "
|
|
"produce a virtual layer of features to be burned in."));
|
|
}
|
|
|
|
argParser->add_argument("-where")
|
|
.metavar("<expression>")
|
|
.store_into(psOptions->osWHERE)
|
|
.help(_("An optional SQL WHERE style query expression to be applied to "
|
|
"select features "
|
|
"to burn in from the input layer(s)."));
|
|
|
|
argParser->add_argument("-dialect")
|
|
.metavar("<sql_dialect>")
|
|
.store_into(psOptions->osDialect)
|
|
.help(_("The SQL dialect to use for the SQL expression."));
|
|
|
|
// Store later
|
|
argParser->add_argument("-a_nodata")
|
|
.metavar("<value>")
|
|
.help(_("Assign a specified nodata value to output bands."));
|
|
|
|
// Dealt manually as argparse::nargs_pattern::at_least_one is problematic
|
|
argParser->add_argument("-init")
|
|
.metavar("<value>")
|
|
.append()
|
|
//.nargs(argparse::nargs_pattern::at_least_one)
|
|
.scan<'g', double>()
|
|
.help(_("Initialize the output bands to the specified value."));
|
|
|
|
argParser->add_argument("-a_srs")
|
|
.metavar("<srs_def>")
|
|
.action(
|
|
[psOptions](const std::string &osOutputSRSDef)
|
|
{
|
|
if (psOptions->oOutputSRS.SetFromUserInput(
|
|
osOutputSRSDef.c_str()) != OGRERR_NONE)
|
|
{
|
|
throw std::invalid_argument(
|
|
std::string("Failed to process SRS definition: ")
|
|
.append(osOutputSRSDef));
|
|
}
|
|
psOptions->bCreateOutput = true;
|
|
})
|
|
.help(_("The spatial reference system to use for the output raster."));
|
|
|
|
argParser->add_argument("-to")
|
|
.metavar("<NAME>=<VALUE>")
|
|
.append()
|
|
.action([psOptions](const std::string &s)
|
|
{ psOptions->aosTO.AddString(s.c_str()); })
|
|
.help(_("Set a transformer option."));
|
|
|
|
// Store later
|
|
argParser->add_argument("-te")
|
|
.metavar("<xmin> <ymin> <xmax> <ymax>")
|
|
.nargs(4)
|
|
.scan<'g', double>()
|
|
.help(_("Set georeferenced extents of output file to be created."));
|
|
|
|
// Mutex with tr
|
|
{
|
|
auto &group = argParser->add_mutually_exclusive_group(false);
|
|
|
|
// Store later
|
|
group.add_argument("-tr")
|
|
.metavar("<xres> <yres>")
|
|
.nargs(2)
|
|
.scan<'g', double>()
|
|
.help(
|
|
_("Set output file resolution in target georeferenced units."));
|
|
|
|
// Store later
|
|
auto &arg = group.add_argument("-ts")
|
|
.metavar("<width> <height>")
|
|
.nargs(2)
|
|
.scan<'i', int>()
|
|
.help(_("Set output file size in pixels and lines."));
|
|
|
|
argParser->add_hidden_alias_for(arg, "-outsize");
|
|
}
|
|
|
|
argParser->add_argument("-tap")
|
|
.flag()
|
|
.store_into(psOptions->bTargetAlignedPixels)
|
|
.action([psOptions](const std::string &)
|
|
{ psOptions->bCreateOutput = true; })
|
|
.help(_("Align the coordinates of the extent to the values of the "
|
|
"output raster."));
|
|
|
|
argParser->add_argument("-optim")
|
|
.metavar("AUTO|VECTOR|RASTER")
|
|
.action(
|
|
[psOptions](const std::string &s) {
|
|
psOptions->aosRasterizeOptions.SetNameValue("OPTIM", s.c_str());
|
|
})
|
|
.help(_("Force the algorithm used."));
|
|
|
|
argParser->add_creation_options_argument(psOptions->aosCreationOptions)
|
|
.action([psOptions](const std::string &)
|
|
{ psOptions->bCreateOutput = true; });
|
|
|
|
argParser->add_output_type_argument(psOptions->eOutputType)
|
|
.action([psOptions](const std::string &)
|
|
{ psOptions->bCreateOutput = true; });
|
|
|
|
argParser->add_output_format_argument(psOptions->osFormat)
|
|
.action([psOptions](const std::string &)
|
|
{ psOptions->bCreateOutput = true; });
|
|
|
|
// Written that way so that in library mode, users can still use the -q
|
|
// switch, even if it has no effect
|
|
argParser->add_quiet_argument(
|
|
psOptionsForBinary ? &(psOptionsForBinary->bQuiet) : nullptr);
|
|
|
|
if (psOptionsForBinary)
|
|
{
|
|
|
|
argParser->add_open_options_argument(
|
|
psOptionsForBinary->aosOpenOptions);
|
|
|
|
argParser->add_argument("src_datasource")
|
|
.metavar("<src_datasource>")
|
|
.store_into(psOptionsForBinary->osSource)
|
|
.help(_("Any vector supported readable datasource."));
|
|
|
|
argParser->add_argument("dst_filename")
|
|
.metavar("<dst_filename>")
|
|
.store_into(psOptionsForBinary->osDest)
|
|
.help(_("The GDAL raster supported output file."));
|
|
}
|
|
|
|
return argParser;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALRasterizeAppGetParserUsage() */
|
|
/************************************************************************/
|
|
|
|
std::string GDALRasterizeAppGetParserUsage()
|
|
{
|
|
try
|
|
{
|
|
GDALRasterizeOptions sOptions;
|
|
GDALRasterizeOptionsForBinary sOptionsForBinary;
|
|
auto argParser =
|
|
GDALRasterizeOptionsGetParser(&sOptions, &sOptionsForBinary);
|
|
return argParser->usage();
|
|
}
|
|
catch (const std::exception &err)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined, "Unexpected exception: %s",
|
|
err.what());
|
|
return std::string();
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* InvertGeometries() */
|
|
/************************************************************************/
|
|
|
|
static void InvertGeometries(GDALDatasetH hDstDS,
|
|
std::vector<OGRGeometryH> &ahGeometries)
|
|
|
|
{
|
|
OGRMultiPolygon *poInvertMP = new OGRMultiPolygon();
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Create a ring that is a bit outside the raster dataset. */
|
|
/* -------------------------------------------------------------------- */
|
|
const int brx = GDALGetRasterXSize(hDstDS) + 2;
|
|
const int bry = GDALGetRasterYSize(hDstDS) + 2;
|
|
|
|
double adfGeoTransform[6] = {};
|
|
GDALGetGeoTransform(hDstDS, adfGeoTransform);
|
|
|
|
auto poUniverseRing = std::make_unique<OGRLinearRing>();
|
|
|
|
poUniverseRing->addPoint(
|
|
adfGeoTransform[0] + -2 * adfGeoTransform[1] + -2 * adfGeoTransform[2],
|
|
adfGeoTransform[3] + -2 * adfGeoTransform[4] + -2 * adfGeoTransform[5]);
|
|
|
|
poUniverseRing->addPoint(adfGeoTransform[0] + brx * adfGeoTransform[1] +
|
|
-2 * adfGeoTransform[2],
|
|
adfGeoTransform[3] + brx * adfGeoTransform[4] +
|
|
-2 * adfGeoTransform[5]);
|
|
|
|
poUniverseRing->addPoint(adfGeoTransform[0] + brx * adfGeoTransform[1] +
|
|
bry * adfGeoTransform[2],
|
|
adfGeoTransform[3] + brx * adfGeoTransform[4] +
|
|
bry * adfGeoTransform[5]);
|
|
|
|
poUniverseRing->addPoint(adfGeoTransform[0] + -2 * adfGeoTransform[1] +
|
|
bry * adfGeoTransform[2],
|
|
adfGeoTransform[3] + -2 * adfGeoTransform[4] +
|
|
bry * adfGeoTransform[5]);
|
|
|
|
poUniverseRing->addPoint(
|
|
adfGeoTransform[0] + -2 * adfGeoTransform[1] + -2 * adfGeoTransform[2],
|
|
adfGeoTransform[3] + -2 * adfGeoTransform[4] + -2 * adfGeoTransform[5]);
|
|
|
|
auto poUniversePoly = std::make_unique<OGRPolygon>();
|
|
poUniversePoly->addRing(std::move(poUniverseRing));
|
|
poInvertMP->addGeometry(std::move(poUniversePoly));
|
|
|
|
bool bFoundNonPoly = false;
|
|
// If we have GEOS, use it to "subtract" each polygon from the universe
|
|
// multipolygon
|
|
if (OGRGeometryFactory::haveGEOS())
|
|
{
|
|
OGRGeometry *poInvertMPAsGeom = poInvertMP;
|
|
poInvertMP = nullptr;
|
|
CPL_IGNORE_RET_VAL(poInvertMP);
|
|
for (unsigned int iGeom = 0; iGeom < ahGeometries.size(); iGeom++)
|
|
{
|
|
auto poGeom = OGRGeometry::FromHandle(ahGeometries[iGeom]);
|
|
const auto eGType = OGR_GT_Flatten(poGeom->getGeometryType());
|
|
if (eGType != wkbPolygon && eGType != wkbMultiPolygon)
|
|
{
|
|
if (!bFoundNonPoly)
|
|
{
|
|
bFoundNonPoly = true;
|
|
CPLError(CE_Warning, CPLE_AppDefined,
|
|
"Ignoring non-polygon geometries in -i mode");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
auto poNewGeom = poInvertMPAsGeom->Difference(poGeom);
|
|
if (poNewGeom)
|
|
{
|
|
delete poInvertMPAsGeom;
|
|
poInvertMPAsGeom = poNewGeom;
|
|
}
|
|
}
|
|
|
|
delete poGeom;
|
|
}
|
|
|
|
ahGeometries.resize(1);
|
|
ahGeometries[0] = OGRGeometry::ToHandle(poInvertMPAsGeom);
|
|
return;
|
|
}
|
|
|
|
OGRPolygon &hUniversePoly =
|
|
*poInvertMP->getGeometryRef(poInvertMP->getNumGeometries() - 1);
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* If we don't have GEOS, add outer rings of polygons as inner */
|
|
/* rings of poUniversePoly and inner rings as sub-polygons. Note */
|
|
/* that this only works properly if the polygons are disjoint, in */
|
|
/* the sense that the outer ring of any polygon is not inside the */
|
|
/* outer ring of another one. So the scenario of */
|
|
/* https://github.com/OSGeo/gdal/issues/8689 with an "island" in */
|
|
/* the middle of a hole will not work properly. */
|
|
/* -------------------------------------------------------------------- */
|
|
for (unsigned int iGeom = 0; iGeom < ahGeometries.size(); iGeom++)
|
|
{
|
|
const auto eGType =
|
|
OGR_GT_Flatten(OGR_G_GetGeometryType(ahGeometries[iGeom]));
|
|
if (eGType != wkbPolygon && eGType != wkbMultiPolygon)
|
|
{
|
|
if (!bFoundNonPoly)
|
|
{
|
|
bFoundNonPoly = true;
|
|
CPLError(CE_Warning, CPLE_AppDefined,
|
|
"Ignoring non-polygon geometries in -i mode");
|
|
}
|
|
OGR_G_DestroyGeometry(ahGeometries[iGeom]);
|
|
continue;
|
|
}
|
|
|
|
const auto ProcessPoly =
|
|
[&hUniversePoly, poInvertMP](OGRPolygon *poPoly)
|
|
{
|
|
for (int i = poPoly->getNumInteriorRings() - 1; i >= 0; --i)
|
|
{
|
|
auto poNewPoly = std::make_unique<OGRPolygon>();
|
|
std::unique_ptr<OGRLinearRing> poRing(
|
|
poPoly->stealInteriorRing(i));
|
|
poNewPoly->addRing(std::move(poRing));
|
|
poInvertMP->addGeometry(std::move(poNewPoly));
|
|
}
|
|
std::unique_ptr<OGRLinearRing> poShell(poPoly->stealExteriorRing());
|
|
hUniversePoly.addRing(std::move(poShell));
|
|
};
|
|
|
|
if (eGType == wkbPolygon)
|
|
{
|
|
auto poPoly =
|
|
OGRGeometry::FromHandle(ahGeometries[iGeom])->toPolygon();
|
|
ProcessPoly(poPoly);
|
|
delete poPoly;
|
|
}
|
|
else
|
|
{
|
|
auto poMulti =
|
|
OGRGeometry::FromHandle(ahGeometries[iGeom])->toMultiPolygon();
|
|
for (auto *poPoly : *poMulti)
|
|
{
|
|
ProcessPoly(poPoly);
|
|
}
|
|
delete poMulti;
|
|
}
|
|
}
|
|
|
|
ahGeometries.resize(1);
|
|
ahGeometries[0] = OGRGeometry::ToHandle(poInvertMP);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* ProcessLayer() */
|
|
/* */
|
|
/* Process all the features in a layer selection, collecting */
|
|
/* geometries and burn values. */
|
|
/************************************************************************/
|
|
|
|
static CPLErr ProcessLayer(OGRLayerH hSrcLayer, bool bSRSIsSet,
|
|
GDALDatasetH hDstDS,
|
|
const std::vector<int> &anBandList,
|
|
const std::vector<double> &adfBurnValues, bool b3D,
|
|
bool bInverse, const std::string &osBurnAttribute,
|
|
CSLConstList papszRasterizeOptions,
|
|
CSLConstList papszTO, GDALProgressFunc pfnProgress,
|
|
void *pProgressData)
|
|
|
|
{
|
|
/* -------------------------------------------------------------------- */
|
|
/* Checkout that SRS are the same. */
|
|
/* If -a_srs is specified, skip the test */
|
|
/* -------------------------------------------------------------------- */
|
|
OGRCoordinateTransformationH hCT = nullptr;
|
|
if (!bSRSIsSet)
|
|
{
|
|
OGRSpatialReferenceH hDstSRS = GDALGetSpatialRef(hDstDS);
|
|
|
|
if (hDstSRS)
|
|
hDstSRS = OSRClone(hDstSRS);
|
|
else if (GDALGetMetadata(hDstDS, "RPC") != nullptr)
|
|
{
|
|
hDstSRS = OSRNewSpatialReference(nullptr);
|
|
CPL_IGNORE_RET_VAL(
|
|
OSRSetFromUserInput(hDstSRS, SRS_WKT_WGS84_LAT_LONG));
|
|
OSRSetAxisMappingStrategy(hDstSRS, OAMS_TRADITIONAL_GIS_ORDER);
|
|
}
|
|
|
|
OGRSpatialReferenceH hSrcSRS = OGR_L_GetSpatialRef(hSrcLayer);
|
|
if (hDstSRS != nullptr && hSrcSRS != nullptr)
|
|
{
|
|
if (OSRIsSame(hSrcSRS, hDstSRS) == FALSE)
|
|
{
|
|
hCT = OCTNewCoordinateTransformation(hSrcSRS, hDstSRS);
|
|
if (hCT == nullptr)
|
|
{
|
|
CPLError(CE_Warning, CPLE_AppDefined,
|
|
"The output raster dataset and the input vector "
|
|
"layer do not have the same SRS.\n"
|
|
"And reprojection of input data did not work. "
|
|
"Results might be incorrect.");
|
|
}
|
|
}
|
|
}
|
|
else if (hDstSRS != nullptr && hSrcSRS == nullptr)
|
|
{
|
|
CPLError(CE_Warning, CPLE_AppDefined,
|
|
"The output raster dataset has a SRS, but the input "
|
|
"vector layer SRS is unknown.\n"
|
|
"Ensure input vector has the same SRS, otherwise results "
|
|
"might be incorrect.");
|
|
}
|
|
else if (hDstSRS == nullptr && hSrcSRS != nullptr)
|
|
{
|
|
CPLError(CE_Warning, CPLE_AppDefined,
|
|
"The input vector layer has a SRS, but the output raster "
|
|
"dataset SRS is unknown.\n"
|
|
"Ensure output raster dataset has the same SRS, otherwise "
|
|
"results might be incorrect.");
|
|
}
|
|
|
|
if (hDstSRS != nullptr)
|
|
{
|
|
OSRDestroySpatialReference(hDstSRS);
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Get field index, and check. */
|
|
/* -------------------------------------------------------------------- */
|
|
int iBurnField = -1;
|
|
bool bUseInt64 = false;
|
|
if (!osBurnAttribute.empty())
|
|
{
|
|
OGRFeatureDefnH hLayerDefn = OGR_L_GetLayerDefn(hSrcLayer);
|
|
iBurnField = OGR_FD_GetFieldIndex(hLayerDefn, osBurnAttribute.c_str());
|
|
if (iBurnField == -1)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"Failed to find field %s on layer %s.",
|
|
osBurnAttribute.c_str(),
|
|
OGR_FD_GetName(OGR_L_GetLayerDefn(hSrcLayer)));
|
|
if (hCT != nullptr)
|
|
OCTDestroyCoordinateTransformation(hCT);
|
|
return CE_Failure;
|
|
}
|
|
if (OGR_Fld_GetType(OGR_FD_GetFieldDefn(hLayerDefn, iBurnField)) ==
|
|
OFTInteger64)
|
|
{
|
|
GDALRasterBandH hBand = GDALGetRasterBand(hDstDS, anBandList[0]);
|
|
if (hBand && GDALGetRasterDataType(hBand) == GDT_Int64)
|
|
{
|
|
bUseInt64 = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Collect the geometries from this layer, and build list of */
|
|
/* burn values. */
|
|
/* -------------------------------------------------------------------- */
|
|
OGRFeatureH hFeat = nullptr;
|
|
std::vector<OGRGeometryH> ahGeometries;
|
|
std::vector<double> adfFullBurnValues;
|
|
std::vector<int64_t> anFullBurnValues;
|
|
|
|
OGR_L_ResetReading(hSrcLayer);
|
|
|
|
while ((hFeat = OGR_L_GetNextFeature(hSrcLayer)) != nullptr)
|
|
{
|
|
OGRGeometryH hGeom = OGR_F_StealGeometry(hFeat);
|
|
if (hGeom == nullptr)
|
|
{
|
|
OGR_F_Destroy(hFeat);
|
|
continue;
|
|
}
|
|
|
|
if (hCT != nullptr)
|
|
{
|
|
if (OGR_G_Transform(hGeom, hCT) != OGRERR_NONE)
|
|
{
|
|
OGR_F_Destroy(hFeat);
|
|
OGR_G_DestroyGeometry(hGeom);
|
|
continue;
|
|
}
|
|
}
|
|
ahGeometries.push_back(hGeom);
|
|
|
|
for (unsigned int iBand = 0; iBand < anBandList.size(); iBand++)
|
|
{
|
|
if (!adfBurnValues.empty())
|
|
adfFullBurnValues.push_back(adfBurnValues[std::min(
|
|
iBand,
|
|
static_cast<unsigned int>(adfBurnValues.size()) - 1)]);
|
|
else if (!osBurnAttribute.empty())
|
|
{
|
|
if (bUseInt64)
|
|
anFullBurnValues.push_back(
|
|
OGR_F_GetFieldAsInteger64(hFeat, iBurnField));
|
|
else
|
|
adfFullBurnValues.push_back(
|
|
OGR_F_GetFieldAsDouble(hFeat, iBurnField));
|
|
}
|
|
else if (b3D)
|
|
{
|
|
/* Points and Lines will have their "z" values collected at the
|
|
point and line levels respectively. Not implemented for
|
|
polygons */
|
|
adfFullBurnValues.push_back(0.0);
|
|
}
|
|
}
|
|
|
|
OGR_F_Destroy(hFeat);
|
|
}
|
|
|
|
if (hCT != nullptr)
|
|
OCTDestroyCoordinateTransformation(hCT);
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* If we are in inverse mode, we add one extra ring around the */
|
|
/* whole dataset to invert the concept of insideness and then */
|
|
/* merge everything into one geometry collection. */
|
|
/* -------------------------------------------------------------------- */
|
|
if (bInverse)
|
|
{
|
|
if (ahGeometries.empty())
|
|
{
|
|
for (unsigned int iBand = 0; iBand < anBandList.size(); iBand++)
|
|
{
|
|
if (!adfBurnValues.empty())
|
|
adfFullBurnValues.push_back(adfBurnValues[std::min(
|
|
iBand,
|
|
static_cast<unsigned int>(adfBurnValues.size()) - 1)]);
|
|
else /* FIXME? Not sure what to do exactly in the else case, but
|
|
we must insert a value */
|
|
{
|
|
adfFullBurnValues.push_back(0.0);
|
|
anFullBurnValues.push_back(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
InvertGeometries(hDstDS, ahGeometries);
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* If we have transformer options, create the transformer here */
|
|
/* Coordinate transformation to the target SRS has already been */
|
|
/* done, so we just need to convert to target raster space. */
|
|
/* Note: this is somewhat identical to what is done in */
|
|
/* GDALRasterizeGeometries() itself, except we can pass transformer*/
|
|
/* options. */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
void *pTransformArg = nullptr;
|
|
GDALTransformerFunc pfnTransformer = nullptr;
|
|
CPLErr eErr = CE_None;
|
|
if (papszTO != nullptr)
|
|
{
|
|
GDALDataset *poDS = GDALDataset::FromHandle(hDstDS);
|
|
char **papszTransformerOptions = CSLDuplicate(papszTO);
|
|
double adfGeoTransform[6] = {0.0};
|
|
if (poDS->GetGeoTransform(adfGeoTransform) != CE_None &&
|
|
poDS->GetGCPCount() == 0 && poDS->GetMetadata("RPC") == nullptr)
|
|
{
|
|
papszTransformerOptions = CSLSetNameValue(
|
|
papszTransformerOptions, "DST_METHOD", "NO_GEOTRANSFORM");
|
|
}
|
|
|
|
pTransformArg = GDALCreateGenImgProjTransformer2(
|
|
nullptr, hDstDS, papszTransformerOptions);
|
|
CSLDestroy(papszTransformerOptions);
|
|
|
|
pfnTransformer = GDALGenImgProjTransform;
|
|
if (pTransformArg == nullptr)
|
|
{
|
|
eErr = CE_Failure;
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Perform the burn. */
|
|
/* -------------------------------------------------------------------- */
|
|
if (eErr == CE_None)
|
|
{
|
|
if (bUseInt64)
|
|
{
|
|
eErr = GDALRasterizeGeometriesInt64(
|
|
hDstDS, static_cast<int>(anBandList.size()), anBandList.data(),
|
|
static_cast<int>(ahGeometries.size()), ahGeometries.data(),
|
|
pfnTransformer, pTransformArg, anFullBurnValues.data(),
|
|
papszRasterizeOptions, pfnProgress, pProgressData);
|
|
}
|
|
else
|
|
{
|
|
eErr = GDALRasterizeGeometries(
|
|
hDstDS, static_cast<int>(anBandList.size()), anBandList.data(),
|
|
static_cast<int>(ahGeometries.size()), ahGeometries.data(),
|
|
pfnTransformer, pTransformArg, adfFullBurnValues.data(),
|
|
papszRasterizeOptions, pfnProgress, pProgressData);
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Cleanup */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
if (pTransformArg)
|
|
GDALDestroyTransformer(pTransformArg);
|
|
|
|
for (int iGeom = static_cast<int>(ahGeometries.size()) - 1; iGeom >= 0;
|
|
iGeom--)
|
|
OGR_G_DestroyGeometry(ahGeometries[iGeom]);
|
|
|
|
return eErr;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CreateOutputDataset() */
|
|
/************************************************************************/
|
|
|
|
static GDALDatasetH CreateOutputDataset(
|
|
const std::vector<OGRLayerH> &ahLayers, OGRSpatialReferenceH hSRS,
|
|
OGREnvelope sEnvelop, GDALDriverH hDriver, const char *pszDest, int nXSize,
|
|
int nYSize, double dfXRes, double dfYRes, bool bTargetAlignedPixels,
|
|
int nBandCount, GDALDataType eOutputType, CSLConstList papszCreationOptions,
|
|
const std::vector<double> &adfInitVals, const char *pszNoData)
|
|
{
|
|
bool bFirstLayer = true;
|
|
char *pszWKT = nullptr;
|
|
const bool bBoundsSpecifiedByUser = sEnvelop.IsInit();
|
|
|
|
for (unsigned int i = 0; i < ahLayers.size(); i++)
|
|
{
|
|
OGRLayerH hLayer = ahLayers[i];
|
|
|
|
if (!bBoundsSpecifiedByUser)
|
|
{
|
|
OGREnvelope sLayerEnvelop;
|
|
|
|
if (OGR_L_GetExtent(hLayer, &sLayerEnvelop, TRUE) != OGRERR_NONE)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"Cannot get layer extent");
|
|
return nullptr;
|
|
}
|
|
|
|
/* Voluntarily increase the extent by a half-pixel size to avoid */
|
|
/* missing points on the border */
|
|
if (!bTargetAlignedPixels && dfXRes != 0 && dfYRes != 0)
|
|
{
|
|
sLayerEnvelop.MinX -= dfXRes / 2;
|
|
sLayerEnvelop.MaxX += dfXRes / 2;
|
|
sLayerEnvelop.MinY -= dfYRes / 2;
|
|
sLayerEnvelop.MaxY += dfYRes / 2;
|
|
}
|
|
|
|
sEnvelop.Merge(sLayerEnvelop);
|
|
}
|
|
|
|
if (bFirstLayer)
|
|
{
|
|
if (hSRS == nullptr)
|
|
hSRS = OGR_L_GetSpatialRef(hLayer);
|
|
|
|
bFirstLayer = false;
|
|
}
|
|
}
|
|
|
|
if (!sEnvelop.IsInit())
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined, "Could not determine bounds");
|
|
return nullptr;
|
|
}
|
|
|
|
if (dfXRes == 0 && dfYRes == 0)
|
|
{
|
|
if (nXSize == 0 || nYSize == 0)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"Size and resolutions are missing");
|
|
return nullptr;
|
|
}
|
|
dfXRes = (sEnvelop.MaxX - sEnvelop.MinX) / nXSize;
|
|
dfYRes = (sEnvelop.MaxY - sEnvelop.MinY) / nYSize;
|
|
}
|
|
else if (bTargetAlignedPixels && dfXRes != 0 && dfYRes != 0)
|
|
{
|
|
sEnvelop.MinX = floor(sEnvelop.MinX / dfXRes) * dfXRes;
|
|
sEnvelop.MaxX = ceil(sEnvelop.MaxX / dfXRes) * dfXRes;
|
|
sEnvelop.MinY = floor(sEnvelop.MinY / dfYRes) * dfYRes;
|
|
sEnvelop.MaxY = ceil(sEnvelop.MaxY / dfYRes) * dfYRes;
|
|
}
|
|
|
|
double adfProjection[6] = {sEnvelop.MinX, dfXRes, 0.0,
|
|
sEnvelop.MaxY, 0.0, -dfYRes};
|
|
|
|
if (nXSize == 0 && nYSize == 0)
|
|
{
|
|
const double dfXSize = 0.5 + (sEnvelop.MaxX - sEnvelop.MinX) / dfXRes;
|
|
const double dfYSize = 0.5 + (sEnvelop.MaxY - sEnvelop.MinY) / dfYRes;
|
|
if (dfXSize > std::numeric_limits<int>::max() ||
|
|
dfXSize < std::numeric_limits<int>::min() ||
|
|
dfYSize > std::numeric_limits<int>::max() ||
|
|
dfYSize < std::numeric_limits<int>::min())
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"Invalid computed output raster size: %f x %f", dfXSize,
|
|
dfYSize);
|
|
return nullptr;
|
|
}
|
|
nXSize = static_cast<int>(dfXSize);
|
|
nYSize = static_cast<int>(dfYSize);
|
|
}
|
|
|
|
GDALDatasetH hDstDS =
|
|
GDALCreate(hDriver, pszDest, nXSize, nYSize, nBandCount, eOutputType,
|
|
papszCreationOptions);
|
|
if (hDstDS == nullptr)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined, "Cannot create %s", pszDest);
|
|
return nullptr;
|
|
}
|
|
|
|
GDALSetGeoTransform(hDstDS, adfProjection);
|
|
|
|
if (hSRS)
|
|
OSRExportToWkt(hSRS, &pszWKT);
|
|
if (pszWKT)
|
|
GDALSetProjection(hDstDS, pszWKT);
|
|
CPLFree(pszWKT);
|
|
|
|
/*if( nBandCount == 3 || nBandCount == 4 )
|
|
{
|
|
for( int iBand = 0; iBand < nBandCount; iBand++ )
|
|
{
|
|
GDALRasterBandH hBand = GDALGetRasterBand(hDstDS, iBand + 1);
|
|
GDALSetRasterColorInterpretation(hBand,
|
|
(GDALColorInterp)(GCI_RedBand + iBand));
|
|
}
|
|
}*/
|
|
|
|
if (pszNoData)
|
|
{
|
|
for (int iBand = 0; iBand < nBandCount; iBand++)
|
|
{
|
|
GDALRasterBandH hBand = GDALGetRasterBand(hDstDS, iBand + 1);
|
|
if (GDALGetRasterDataType(hBand) == GDT_Int64)
|
|
GDALSetRasterNoDataValueAsInt64(hBand,
|
|
CPLAtoGIntBig(pszNoData));
|
|
else
|
|
GDALSetRasterNoDataValue(hBand, CPLAtof(pszNoData));
|
|
}
|
|
}
|
|
|
|
if (!adfInitVals.empty())
|
|
{
|
|
for (int iBand = 0;
|
|
iBand < std::min(nBandCount, static_cast<int>(adfInitVals.size()));
|
|
iBand++)
|
|
{
|
|
GDALRasterBandH hBand = GDALGetRasterBand(hDstDS, iBand + 1);
|
|
GDALFillRaster(hBand, adfInitVals[iBand], 0);
|
|
}
|
|
}
|
|
|
|
return hDstDS;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALRasterize() */
|
|
/************************************************************************/
|
|
|
|
/* clang-format off */
|
|
/**
|
|
* Burns vector geometries into a raster
|
|
*
|
|
* This is the equivalent of the
|
|
* <a href="/programs/gdal_rasterize.html">gdal_rasterize</a> utility.
|
|
*
|
|
* GDALRasterizeOptions* must be allocated and freed with
|
|
* GDALRasterizeOptionsNew() and GDALRasterizeOptionsFree() respectively.
|
|
* pszDest and hDstDS cannot be used at the same time.
|
|
*
|
|
* @param pszDest the destination dataset path or NULL.
|
|
* @param hDstDS the destination dataset or NULL.
|
|
* @param hSrcDataset the source dataset handle.
|
|
* @param psOptionsIn the options struct returned by GDALRasterizeOptionsNew()
|
|
* or NULL.
|
|
* @param pbUsageError pointer to a integer output variable to store if any
|
|
* usage error has occurred or NULL.
|
|
* @return the output dataset (new dataset that must be closed using
|
|
* GDALClose(), or hDstDS is not NULL) or NULL in case of error.
|
|
*
|
|
* @since GDAL 2.1
|
|
*/
|
|
/* clang-format on */
|
|
|
|
GDALDatasetH GDALRasterize(const char *pszDest, GDALDatasetH hDstDS,
|
|
GDALDatasetH hSrcDataset,
|
|
const GDALRasterizeOptions *psOptionsIn,
|
|
int *pbUsageError)
|
|
{
|
|
if (pszDest == nullptr && hDstDS == nullptr)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"pszDest == NULL && hDstDS == NULL");
|
|
|
|
if (pbUsageError)
|
|
*pbUsageError = TRUE;
|
|
return nullptr;
|
|
}
|
|
if (hSrcDataset == nullptr)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined, "hSrcDataset== NULL");
|
|
|
|
if (pbUsageError)
|
|
*pbUsageError = TRUE;
|
|
return nullptr;
|
|
}
|
|
if (hDstDS != nullptr && psOptionsIn && psOptionsIn->bCreateOutput)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"hDstDS != NULL but options that imply creating a new dataset "
|
|
"have been set.");
|
|
|
|
if (pbUsageError)
|
|
*pbUsageError = TRUE;
|
|
return nullptr;
|
|
}
|
|
|
|
GDALRasterizeOptions *psOptionsToFree = nullptr;
|
|
const GDALRasterizeOptions *psOptions = psOptionsIn;
|
|
if (psOptions == nullptr)
|
|
{
|
|
psOptionsToFree = GDALRasterizeOptionsNew(nullptr, nullptr);
|
|
psOptions = psOptionsToFree;
|
|
}
|
|
|
|
const bool bCloseOutDSOnError = hDstDS == nullptr;
|
|
if (pszDest == nullptr)
|
|
pszDest = GDALGetDescription(hDstDS);
|
|
|
|
if (psOptions->osSQL.empty() && psOptions->aosLayers.empty() &&
|
|
GDALDatasetGetLayerCount(hSrcDataset) != 1)
|
|
{
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"Neither -sql nor -l are specified, but the source dataset "
|
|
"has not one single layer.");
|
|
if (pbUsageError)
|
|
*pbUsageError = TRUE;
|
|
GDALRasterizeOptionsFree(psOptionsToFree);
|
|
return nullptr;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Open target raster file. Eventually we will add optional */
|
|
/* creation. */
|
|
/* -------------------------------------------------------------------- */
|
|
const bool bCreateOutput = psOptions->bCreateOutput || hDstDS == nullptr;
|
|
|
|
GDALDriverH hDriver = nullptr;
|
|
if (bCreateOutput)
|
|
{
|
|
CPLString osFormat;
|
|
if (psOptions->osFormat.empty())
|
|
{
|
|
osFormat = GetOutputDriverForRaster(pszDest);
|
|
if (osFormat.empty())
|
|
{
|
|
GDALRasterizeOptionsFree(psOptionsToFree);
|
|
return nullptr;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
osFormat = psOptions->osFormat;
|
|
}
|
|
|
|
/* --------------------------------------------------------------------
|
|
*/
|
|
/* Find the output driver. */
|
|
/* --------------------------------------------------------------------
|
|
*/
|
|
hDriver = GDALGetDriverByName(osFormat);
|
|
char **papszDriverMD =
|
|
hDriver ? GDALGetMetadata(hDriver, nullptr) : nullptr;
|
|
if (hDriver == nullptr ||
|
|
!CPLTestBool(CSLFetchNameValueDef(papszDriverMD, GDAL_DCAP_RASTER,
|
|
"FALSE")) ||
|
|
!CPLTestBool(
|
|
CSLFetchNameValueDef(papszDriverMD, GDAL_DCAP_CREATE, "FALSE")))
|
|
{
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"Output driver `%s' not recognised or does not support "
|
|
"direct output file creation.",
|
|
osFormat.c_str());
|
|
GDALRasterizeOptionsFree(psOptionsToFree);
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
const auto GetOutputDataType = [&](OGRLayerH hLayer)
|
|
{
|
|
CPLAssert(bCreateOutput);
|
|
CPLAssert(hDriver);
|
|
GDALDataType eOutputType = psOptions->eOutputType;
|
|
if (eOutputType == GDT_Unknown && !psOptions->osBurnAttribute.empty())
|
|
{
|
|
OGRFeatureDefnH hLayerDefn = OGR_L_GetLayerDefn(hLayer);
|
|
const int iBurnField = OGR_FD_GetFieldIndex(
|
|
hLayerDefn, psOptions->osBurnAttribute.c_str());
|
|
if (iBurnField >= 0 && OGR_Fld_GetType(OGR_FD_GetFieldDefn(
|
|
hLayerDefn, iBurnField)) == OFTInteger64)
|
|
{
|
|
const char *pszMD = GDALGetMetadataItem(
|
|
hDriver, GDAL_DMD_CREATIONDATATYPES, nullptr);
|
|
if (pszMD && CPLStringList(CSLTokenizeString2(pszMD, " ", 0))
|
|
.FindString("Int64") >= 0)
|
|
{
|
|
eOutputType = GDT_Int64;
|
|
}
|
|
}
|
|
}
|
|
if (eOutputType == GDT_Unknown)
|
|
{
|
|
eOutputType = GDT_Float64;
|
|
}
|
|
return eOutputType;
|
|
};
|
|
|
|
// Store SRS handle
|
|
OGRSpatialReferenceH hSRS =
|
|
psOptions->oOutputSRS.IsEmpty()
|
|
? nullptr
|
|
: OGRSpatialReference::ToHandle(
|
|
const_cast<OGRSpatialReference *>(&psOptions->oOutputSRS));
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Process SQL request. */
|
|
/* -------------------------------------------------------------------- */
|
|
CPLErr eErr = CE_Failure;
|
|
|
|
if (!psOptions->osSQL.empty())
|
|
{
|
|
OGRLayerH hLayer =
|
|
GDALDatasetExecuteSQL(hSrcDataset, psOptions->osSQL.c_str(),
|
|
nullptr, psOptions->osDialect.c_str());
|
|
if (hLayer != nullptr)
|
|
{
|
|
if (bCreateOutput)
|
|
{
|
|
std::vector<OGRLayerH> ahLayers;
|
|
ahLayers.push_back(hLayer);
|
|
|
|
const GDALDataType eOutputType = GetOutputDataType(hLayer);
|
|
hDstDS = CreateOutputDataset(
|
|
ahLayers, hSRS, psOptions->sEnvelop, hDriver, pszDest,
|
|
psOptions->nXSize, psOptions->nYSize, psOptions->dfXRes,
|
|
psOptions->dfYRes, psOptions->bTargetAlignedPixels,
|
|
static_cast<int>(psOptions->anBandList.size()), eOutputType,
|
|
psOptions->aosCreationOptions, psOptions->adfInitVals,
|
|
psOptions->osNoData.c_str());
|
|
if (hDstDS == nullptr)
|
|
{
|
|
GDALDatasetReleaseResultSet(hSrcDataset, hLayer);
|
|
GDALRasterizeOptionsFree(psOptionsToFree);
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
eErr = ProcessLayer(
|
|
hLayer, hSRS != nullptr, hDstDS, psOptions->anBandList,
|
|
psOptions->adfBurnValues, psOptions->b3D, psOptions->bInverse,
|
|
psOptions->osBurnAttribute.c_str(),
|
|
psOptions->aosRasterizeOptions, psOptions->aosTO,
|
|
psOptions->pfnProgress, psOptions->pProgressData);
|
|
|
|
GDALDatasetReleaseResultSet(hSrcDataset, hLayer);
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Create output file if necessary. */
|
|
/* -------------------------------------------------------------------- */
|
|
const int nLayerCount =
|
|
(psOptions->osSQL.empty() && psOptions->aosLayers.empty())
|
|
? 1
|
|
: static_cast<int>(psOptions->aosLayers.size());
|
|
|
|
if (bCreateOutput && hDstDS == nullptr)
|
|
{
|
|
std::vector<OGRLayerH> ahLayers;
|
|
|
|
GDALDataType eOutputType = psOptions->eOutputType;
|
|
|
|
for (int i = 0; i < nLayerCount; i++)
|
|
{
|
|
OGRLayerH hLayer;
|
|
if (psOptions->aosLayers.size() > static_cast<size_t>(i))
|
|
hLayer = GDALDatasetGetLayerByName(
|
|
hSrcDataset, psOptions->aosLayers[i].c_str());
|
|
else
|
|
hLayer = GDALDatasetGetLayer(hSrcDataset, 0);
|
|
if (hLayer == nullptr)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"Unable to find layer \"%s\".",
|
|
psOptions->aosLayers.size() > static_cast<size_t>(i)
|
|
? psOptions->aosLayers[i].c_str()
|
|
: "0");
|
|
GDALRasterizeOptionsFree(psOptionsToFree);
|
|
return nullptr;
|
|
}
|
|
if (eOutputType == GDT_Unknown)
|
|
{
|
|
if (GetOutputDataType(hLayer) == GDT_Int64)
|
|
eOutputType = GDT_Int64;
|
|
}
|
|
|
|
ahLayers.push_back(hLayer);
|
|
}
|
|
|
|
if (eOutputType == GDT_Unknown)
|
|
{
|
|
eOutputType = GDT_Float64;
|
|
}
|
|
|
|
hDstDS = CreateOutputDataset(
|
|
ahLayers, hSRS, psOptions->sEnvelop, hDriver, pszDest,
|
|
psOptions->nXSize, psOptions->nYSize, psOptions->dfXRes,
|
|
psOptions->dfYRes, psOptions->bTargetAlignedPixels,
|
|
static_cast<int>(psOptions->anBandList.size()), eOutputType,
|
|
psOptions->aosCreationOptions, psOptions->adfInitVals,
|
|
psOptions->osNoData.c_str());
|
|
if (hDstDS == nullptr)
|
|
{
|
|
GDALRasterizeOptionsFree(psOptionsToFree);
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Process each layer. */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
for (int i = 0; i < nLayerCount; i++)
|
|
{
|
|
OGRLayerH hLayer;
|
|
if (psOptions->aosLayers.size() > static_cast<size_t>(i))
|
|
hLayer = GDALDatasetGetLayerByName(hSrcDataset,
|
|
psOptions->aosLayers[i].c_str());
|
|
else
|
|
hLayer = GDALDatasetGetLayer(hSrcDataset, 0);
|
|
if (hLayer == nullptr)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"Unable to find layer \"%s\".",
|
|
psOptions->aosLayers.size() > static_cast<size_t>(i)
|
|
? psOptions->aosLayers[i].c_str()
|
|
: "0");
|
|
eErr = CE_Failure;
|
|
break;
|
|
}
|
|
|
|
if (!psOptions->osWHERE.empty())
|
|
{
|
|
if (OGR_L_SetAttributeFilter(hLayer, psOptions->osWHERE.c_str()) !=
|
|
OGRERR_NONE)
|
|
{
|
|
eErr = CE_Failure;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void *pScaledProgress = GDALCreateScaledProgress(
|
|
0.0, 1.0 * (i + 1) / nLayerCount, psOptions->pfnProgress,
|
|
psOptions->pProgressData);
|
|
|
|
eErr = ProcessLayer(hLayer, !psOptions->oOutputSRS.IsEmpty(), hDstDS,
|
|
psOptions->anBandList, psOptions->adfBurnValues,
|
|
psOptions->b3D, psOptions->bInverse,
|
|
psOptions->osBurnAttribute.c_str(),
|
|
psOptions->aosRasterizeOptions, psOptions->aosTO,
|
|
GDALScaledProgress, pScaledProgress);
|
|
|
|
GDALDestroyScaledProgress(pScaledProgress);
|
|
if (eErr != CE_None)
|
|
break;
|
|
}
|
|
|
|
GDALRasterizeOptionsFree(psOptionsToFree);
|
|
|
|
if (eErr != CE_None)
|
|
{
|
|
if (bCloseOutDSOnError)
|
|
GDALClose(hDstDS);
|
|
return nullptr;
|
|
}
|
|
|
|
return hDstDS;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* ArgIsNumericRasterize() */
|
|
/************************************************************************/
|
|
|
|
static bool ArgIsNumericRasterize(const char *pszArg)
|
|
|
|
{
|
|
char *pszEnd = nullptr;
|
|
CPLStrtod(pszArg, &pszEnd);
|
|
return pszEnd != nullptr && pszEnd[0] == '\0';
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALRasterizeOptionsNew() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* Allocates a GDALRasterizeOptions struct.
|
|
*
|
|
* @param papszArgv NULL terminated list of options (potentially including
|
|
* filename and open options too), or NULL. The accepted options are the ones of
|
|
* the <a href="/programs/gdal_rasterize.html">gdal_rasterize</a> utility.
|
|
* @param psOptionsForBinary (output) may be NULL (and should generally be
|
|
* NULL), otherwise (gdal_translate_bin.cpp use case) must be allocated with
|
|
* GDALRasterizeOptionsForBinaryNew() prior to this
|
|
* function. Will be filled with potentially present filename, open options,...
|
|
* @return pointer to the allocated GDALRasterizeOptions struct. Must be freed
|
|
* with GDALRasterizeOptionsFree().
|
|
*
|
|
* @since GDAL 2.1
|
|
*/
|
|
|
|
GDALRasterizeOptions *
|
|
GDALRasterizeOptionsNew(char **papszArgv,
|
|
GDALRasterizeOptionsForBinary *psOptionsForBinary)
|
|
{
|
|
|
|
auto psOptions = std::make_unique<GDALRasterizeOptions>();
|
|
|
|
/*-------------------------------------------------------------------- */
|
|
/* Parse arguments. */
|
|
/*-------------------------------------------------------------------- */
|
|
|
|
CPLStringList aosArgv;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Pre-processing for custom syntax that ArgumentParser does not */
|
|
/* support. */
|
|
/* -------------------------------------------------------------------- */
|
|
const int argc = CSLCount(papszArgv);
|
|
for (int i = 0; i < argc && papszArgv != nullptr && papszArgv[i] != nullptr;
|
|
i++)
|
|
{
|
|
// argparser will be confused if the value of a string argument
|
|
// starts with a negative sign.
|
|
if (EQUAL(papszArgv[i], "-a_nodata") && papszArgv[i + 1])
|
|
{
|
|
++i;
|
|
const std::string s = papszArgv[i];
|
|
psOptions->osNoData = s;
|
|
psOptions->bCreateOutput = true;
|
|
}
|
|
|
|
// argparser is confused by arguments that have at_least_one
|
|
// cardinality, if they immediately precede positional arguments.
|
|
else if (EQUAL(papszArgv[i], "-burn") && papszArgv[i + 1])
|
|
{
|
|
if (strchr(papszArgv[i + 1], ' '))
|
|
{
|
|
const CPLStringList aosTokens(
|
|
CSLTokenizeString(papszArgv[i + 1]));
|
|
for (const char *pszToken : aosTokens)
|
|
{
|
|
psOptions->adfBurnValues.push_back(CPLAtof(pszToken));
|
|
}
|
|
i += 1;
|
|
}
|
|
else
|
|
{
|
|
while (i < argc - 1 && ArgIsNumericRasterize(papszArgv[i + 1]))
|
|
{
|
|
psOptions->adfBurnValues.push_back(
|
|
CPLAtof(papszArgv[i + 1]));
|
|
i += 1;
|
|
}
|
|
}
|
|
|
|
// Dummy value to make argparse happy, as at least one of
|
|
// -burn, -a or -3d is required
|
|
aosArgv.AddString("-burn");
|
|
aosArgv.AddString("0");
|
|
}
|
|
else if (EQUAL(papszArgv[i], "-init") && papszArgv[i + 1])
|
|
{
|
|
if (strchr(papszArgv[i + 1], ' '))
|
|
{
|
|
const CPLStringList aosTokens(
|
|
CSLTokenizeString(papszArgv[i + 1]));
|
|
for (const char *pszToken : aosTokens)
|
|
{
|
|
psOptions->adfInitVals.push_back(CPLAtof(pszToken));
|
|
}
|
|
i += 1;
|
|
}
|
|
else
|
|
{
|
|
while (i < argc - 1 && ArgIsNumericRasterize(papszArgv[i + 1]))
|
|
{
|
|
psOptions->adfInitVals.push_back(CPLAtof(papszArgv[i + 1]));
|
|
i += 1;
|
|
}
|
|
}
|
|
psOptions->bCreateOutput = true;
|
|
}
|
|
else if (EQUAL(papszArgv[i], "-b") && papszArgv[i + 1])
|
|
{
|
|
if (strchr(papszArgv[i + 1], ' '))
|
|
{
|
|
const CPLStringList aosTokens(
|
|
CSLTokenizeString(papszArgv[i + 1]));
|
|
for (const char *pszToken : aosTokens)
|
|
{
|
|
psOptions->anBandList.push_back(atoi(pszToken));
|
|
}
|
|
i += 1;
|
|
}
|
|
else
|
|
{
|
|
while (i < argc - 1 && ArgIsNumericRasterize(papszArgv[i + 1]))
|
|
{
|
|
psOptions->anBandList.push_back(atoi(papszArgv[i + 1]));
|
|
i += 1;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
aosArgv.AddString(papszArgv[i]);
|
|
}
|
|
}
|
|
|
|
try
|
|
{
|
|
auto argParser =
|
|
GDALRasterizeOptionsGetParser(psOptions.get(), psOptionsForBinary);
|
|
argParser->parse_args_without_binary_name(aosArgv.List());
|
|
|
|
// Check all no store_into args
|
|
if (auto oTe = argParser->present<std::vector<double>>("-te"))
|
|
{
|
|
psOptions->sEnvelop.MinX = oTe.value()[0];
|
|
psOptions->sEnvelop.MinY = oTe.value()[1];
|
|
psOptions->sEnvelop.MaxX = oTe.value()[2];
|
|
psOptions->sEnvelop.MaxY = oTe.value()[3];
|
|
psOptions->bCreateOutput = true;
|
|
}
|
|
|
|
if (auto oTr = argParser->present<std::vector<double>>("-tr"))
|
|
{
|
|
psOptions->dfXRes = oTr.value()[0];
|
|
psOptions->dfYRes = oTr.value()[1];
|
|
|
|
if (psOptions->dfXRes <= 0 || psOptions->dfYRes <= 0)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"Wrong value for -tr parameter.");
|
|
return nullptr;
|
|
}
|
|
|
|
psOptions->bCreateOutput = true;
|
|
}
|
|
|
|
if (auto oTs = argParser->present<std::vector<int>>("-ts"))
|
|
{
|
|
psOptions->nXSize = oTs.value()[0];
|
|
psOptions->nYSize = oTs.value()[1];
|
|
|
|
if (psOptions->nXSize <= 0 || psOptions->nYSize <= 0)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"Wrong value for -ts parameter.");
|
|
return nullptr;
|
|
}
|
|
|
|
psOptions->bCreateOutput = true;
|
|
}
|
|
|
|
if (psOptions->bCreateOutput)
|
|
{
|
|
if (psOptions->dfXRes == 0 && psOptions->dfYRes == 0 &&
|
|
psOptions->nXSize == 0 && psOptions->nYSize == 0)
|
|
{
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"'-tr xres yres' or '-ts xsize ysize' is required.");
|
|
return nullptr;
|
|
}
|
|
|
|
if (psOptions->bTargetAlignedPixels && psOptions->dfXRes == 0 &&
|
|
psOptions->dfYRes == 0)
|
|
{
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"-tap option cannot be used without using -tr.");
|
|
return nullptr;
|
|
}
|
|
|
|
if (!psOptions->anBandList.empty())
|
|
{
|
|
CPLError(
|
|
CE_Failure, CPLE_NotSupported,
|
|
"-b option cannot be used when creating a GDAL dataset.");
|
|
return nullptr;
|
|
}
|
|
|
|
int nBandCount = 1;
|
|
|
|
if (!psOptions->adfBurnValues.empty())
|
|
nBandCount = static_cast<int>(psOptions->adfBurnValues.size());
|
|
|
|
if (static_cast<int>(psOptions->adfInitVals.size()) > nBandCount)
|
|
nBandCount = static_cast<int>(psOptions->adfInitVals.size());
|
|
|
|
if (psOptions->adfInitVals.size() == 1)
|
|
{
|
|
for (int i = 1; i <= nBandCount - 1; i++)
|
|
psOptions->adfInitVals.push_back(psOptions->adfInitVals[0]);
|
|
}
|
|
|
|
for (int i = 1; i <= nBandCount; i++)
|
|
psOptions->anBandList.push_back(i);
|
|
}
|
|
else
|
|
{
|
|
if (psOptions->anBandList.empty())
|
|
psOptions->anBandList.push_back(1);
|
|
}
|
|
|
|
if (!psOptions->osDialect.empty() && !psOptions->osWHERE.empty() &&
|
|
!psOptions->osSQL.empty())
|
|
{
|
|
CPLError(CE_Warning, CPLE_AppDefined,
|
|
"-dialect is ignored with -where. Use -sql instead");
|
|
}
|
|
|
|
if (psOptionsForBinary)
|
|
{
|
|
psOptionsForBinary->bCreateOutput = psOptions->bCreateOutput;
|
|
if (!psOptions->osFormat.empty())
|
|
psOptionsForBinary->osFormat = psOptions->osFormat;
|
|
}
|
|
else if (psOptions->adfBurnValues.empty() &&
|
|
psOptions->osBurnAttribute.empty() && !psOptions->b3D)
|
|
{
|
|
psOptions->adfBurnValues.push_back(255);
|
|
}
|
|
}
|
|
catch (const std::exception &e)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
|
|
return nullptr;
|
|
}
|
|
|
|
return psOptions.release();
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALRasterizeOptionsFree() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* Frees the GDALRasterizeOptions struct.
|
|
*
|
|
* @param psOptions the options struct for GDALRasterize().
|
|
*
|
|
* @since GDAL 2.1
|
|
*/
|
|
|
|
void GDALRasterizeOptionsFree(GDALRasterizeOptions *psOptions)
|
|
{
|
|
delete psOptions;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALRasterizeOptionsSetProgress() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* Set a progress function.
|
|
*
|
|
* @param psOptions the options struct for GDALRasterize().
|
|
* @param pfnProgress the progress callback.
|
|
* @param pProgressData the user data for the progress callback.
|
|
*
|
|
* @since GDAL 2.1
|
|
*/
|
|
|
|
void GDALRasterizeOptionsSetProgress(GDALRasterizeOptions *psOptions,
|
|
GDALProgressFunc pfnProgress,
|
|
void *pProgressData)
|
|
{
|
|
psOptions->pfnProgress = pfnProgress ? pfnProgress : GDALDummyProgress;
|
|
psOptions->pProgressData = pProgressData;
|
|
}
|