#include "gdal_warper.hpp"
#include "gdal_common.hpp"
#include "gdal_dataset.hpp"
#include "gdal_spatial_reference.hpp"
#include "utils/warp_options.hpp"
namespace node_gdal {
void Warper::Initialize(Local<Object> target) {
Nan::SetMethod(target, "reprojectImage", reprojectImage);
Nan::SetMethod(target, "suggestedWarpOutput", suggestedWarpOutput);
}
/**
* GDALReprojectImage() method with a ChunkAndWarpImage replaced with
* ChunkAndWarpMulti
*/
CPLErr GDALReprojectImageMulti(
GDALDatasetH hSrcDS,
const char *pszSrcWKT,
GDALDatasetH hDstDS,
const char *pszDstWKT,
GDALResampleAlg eResampleAlg,
double dfWarpMemoryLimit,
double dfMaxError,
GDALProgressFunc pfnProgress,
void *pProgressArg,
GDALWarpOptions *psOptions)
{
GDALWarpOptions *psWOptions;
/* -------------------------------------------------------------------- */
/* Setup a reprojection based transformer. */
/* -------------------------------------------------------------------- */
void *hTransformArg;
hTransformArg = GDALCreateGenImgProjTransformer(hSrcDS, pszSrcWKT, hDstDS, pszDstWKT, TRUE, 1000.0, 0);
if (hTransformArg == NULL) return CE_Failure;
/* -------------------------------------------------------------------- */
/* Create a copy of the user provided options, or a defaulted */
/* options structure. */
/* -------------------------------------------------------------------- */
if (psOptions == NULL)
psWOptions = GDALCreateWarpOptions();
else
psWOptions = GDALCloneWarpOptions(psOptions);
psWOptions->eResampleAlg = eResampleAlg;
/* -------------------------------------------------------------------- */
/* Set transform. */
/* -------------------------------------------------------------------- */
if (dfMaxError > 0.0) {
psWOptions->pTransformerArg = GDALCreateApproxTransformer(GDALGenImgProjTransform, hTransformArg, dfMaxError);
psWOptions->pfnTransformer = GDALApproxTransform;
} else {
psWOptions->pfnTransformer = GDALGenImgProjTransform;
psWOptions->pTransformerArg = hTransformArg;
}
/* -------------------------------------------------------------------- */
/* Set file and band mapping. */
/* -------------------------------------------------------------------- */
int iBand;
psWOptions->hSrcDS = hSrcDS;
psWOptions->hDstDS = hDstDS;
if (psWOptions->nBandCount == 0) {
psWOptions->nBandCount = MIN(GDALGetRasterCount(hSrcDS), GDALGetRasterCount(hDstDS));
psWOptions->panSrcBands = (int *)CPLMalloc(sizeof(int) * psWOptions->nBandCount);
psWOptions->panDstBands = (int *)CPLMalloc(sizeof(int) * psWOptions->nBandCount);
for (iBand = 0; iBand < psWOptions->nBandCount; iBand++) {
psWOptions->panSrcBands[iBand] = iBand + 1;
psWOptions->panDstBands[iBand] = iBand + 1;
}
}
/* -------------------------------------------------------------------- */
/* Set source nodata values if the source dataset seems to have */
/* any. Same for target nodata values */
/* -------------------------------------------------------------------- */
for (iBand = 0; iBand < psWOptions->nBandCount; iBand++) {
GDALRasterBandH hBand = GDALGetRasterBand(hSrcDS, iBand + 1);
int bGotNoData = FALSE;
double dfNoDataValue;
if (GDALGetRasterColorInterpretation(hBand) == GCI_AlphaBand) { psWOptions->nSrcAlphaBand = iBand + 1; }
dfNoDataValue = GDALGetRasterNoDataValue(hBand, &bGotNoData);
if (bGotNoData) {
if (psWOptions->padfSrcNoDataReal == NULL) {
int ii;
psWOptions->padfSrcNoDataReal = (double *)CPLMalloc(sizeof(double) * psWOptions->nBandCount);
psWOptions->padfSrcNoDataImag = (double *)CPLMalloc(sizeof(double) * psWOptions->nBandCount);
for (ii = 0; ii < psWOptions->nBandCount; ii++) {
psWOptions->padfSrcNoDataReal[ii] = -1.1e20;
psWOptions->padfSrcNoDataImag[ii] = 0.0;
}
}
psWOptions->padfSrcNoDataReal[iBand] = dfNoDataValue;
}
// Deal with target band
hBand = GDALGetRasterBand(hDstDS, iBand + 1);
if (hBand && GDALGetRasterColorInterpretation(hBand) == GCI_AlphaBand) { psWOptions->nDstAlphaBand = iBand + 1; }
dfNoDataValue = GDALGetRasterNoDataValue(hBand, &bGotNoData);
if (bGotNoData) {
if (psWOptions->padfDstNoDataReal == NULL) {
int ii;
psWOptions->padfDstNoDataReal = (double *)CPLMalloc(sizeof(double) * psWOptions->nBandCount);
psWOptions->padfDstNoDataImag = (double *)CPLMalloc(sizeof(double) * psWOptions->nBandCount);
for (ii = 0; ii < psWOptions->nBandCount; ii++) {
psWOptions->padfDstNoDataReal[ii] = -1.1e20;
psWOptions->padfDstNoDataImag[ii] = 0.0;
}
}
psWOptions->padfDstNoDataReal[iBand] = dfNoDataValue;
}
}
/* -------------------------------------------------------------------- */
/* Set the progress function. */
/* -------------------------------------------------------------------- */
if (pfnProgress != NULL) {
psWOptions->pfnProgress = pfnProgress;
psWOptions->pProgressArg = pProgressArg;
}
/* -------------------------------------------------------------------- */
/* Create a warp options based on the options. */
/* -------------------------------------------------------------------- */
GDALWarpOperation oWarper;
CPLErr eErr;
eErr = oWarper.Initialize(psWOptions);
if (eErr == CE_None) eErr = oWarper.ChunkAndWarpMulti(0, 0, GDALGetRasterXSize(hDstDS), GDALGetRasterYSize(hDstDS));
/* -------------------------------------------------------------------- */
/* Cleanup. */
/* -------------------------------------------------------------------- */
GDALDestroyGenImgProjTransformer(hTransformArg);
if (dfMaxError > 0.0) GDALDestroyApproxTransformer(psWOptions->pTransformerArg);
GDALDestroyWarpOptions(psWOptions);
return eErr;
}
/**
* Reprojects a dataset.
*
* @throws Error
* @method reprojectImage
* @static
* @for gdal
* @param {object} options
* @param {gdal.Dataset} options.src
* @param {gdal.Dataset} options.dst
* @param {gdal.SpatialReference} options.s_srs
* @param {gdal.SpatialReference} options.t_srs
* @param {String} [options.resampling] Resampling algorithm ({{#crossLink
* "Constants (GRA)"}}available options{{/crossLink}})
* @param {gdal.Geometry} [options.cutline] Must be in src dataset pixel
* coordinates. Use CoordinateTransformation to convert between georeferenced
* coordinates and pixel coordinates
* @param {Integer[]} [options.srcBands]
* @param {Integer[]} [options.dstBands]
* @param {Integer} [options.srcAlphaBand]
* @param {Integer} [options.dstAlphaBand]
* @param {Number} [options.srcNodata]
* @param {Number} [options.dstNodata]
* @param {Integer} [options.memoryLimit]
* @param {Number} [options.maxError]
* @param {Boolean} [options.multi]
* @param {string[]|object} [options.options] Warp options (see:
* [reference](http://www.gdal.org/structGDALWarpOptions.html#a0ed77f9917bb96c7a9aabd73d4d06e08))
*/
NAN_METHOD(Warper::reprojectImage) {
Nan::HandleScope scope;
Local<Object> obj;
Local<Value> prop;
WarpOptions options;
GDALWarpOptions *opts;
std::string s_srs_str;
std::string t_srs_str;
SpatialReference *s_srs;
SpatialReference *t_srs;
double maxError = 0;
NODE_ARG_OBJECT(0, "Warp options", obj);
if (options.parse(obj)) {
return; // error parsing options object
} else {
opts = options.get();
}
if (!opts->hDstDS) {
Nan::ThrowTypeError("dst Dataset must be provided");
return;
}
NODE_WRAPPED_FROM_OBJ(obj, "s_srs", SpatialReference, s_srs);
NODE_WRAPPED_FROM_OBJ(obj, "t_srs", SpatialReference, t_srs);
NODE_DOUBLE_FROM_OBJ_OPT(obj, "maxError", maxError);
char *s_srs_wkt, *t_srs_wkt;
if (s_srs->get()->exportToWkt(&s_srs_wkt)) {
Nan::ThrowError("Error converting s_srs to WKT");
return;
}
if (t_srs->get()->exportToWkt(&t_srs_wkt)) {
CPLFree(s_srs_wkt);
Nan::ThrowError("Error converting t_srs to WKT");
return;
}
CPLErr err;
if (options.useMultithreading()) {
err = GDALReprojectImageMulti(
opts->hSrcDS,
s_srs_wkt,
opts->hDstDS,
t_srs_wkt,
opts->eResampleAlg,
opts->dfWarpMemoryLimit,
maxError,
NULL,
NULL,
opts);
} else {
err = GDALReprojectImage(
opts->hSrcDS,
s_srs_wkt,
opts->hDstDS,
t_srs_wkt,
opts->eResampleAlg,
opts->dfWarpMemoryLimit,
maxError,
NULL,
NULL,
opts);
}
CPLFree(s_srs_wkt);
CPLFree(t_srs_wkt);
if (err) {
NODE_THROW_CPLERR(err);
return;
}
return;
}
/**
* Used to determine the bounds and resolution of the output virtual file which
* should be large enough to include all the input image.
*
* @throws Error
* @method suggestedWarpOutput
* @static
* @for gdal
* @param {object} options Warp options
* @param {gdal.Dataset} options.src
* @param {gdal.SpatialReference} options.s_srs
* @param {gdal.SpatialReference} options.t_srs
* @param {Number} [options.maxError=0]
* @return {Object} An object containing `"rasterSize"` and `"geoTransform"`
* properties.
*/
NAN_METHOD(Warper::suggestedWarpOutput) {
Nan::HandleScope scope;
Local<Object> obj;
Local<Value> prop;
Dataset *ds;
SpatialReference *s_srs;
SpatialReference *t_srs;
double maxError = 0;
double geotransform[6];
int w = 0, h = 0;
NODE_ARG_OBJECT(0, "Warp options", obj);
if (Nan::HasOwnProperty(obj, Nan::New("src").ToLocalChecked()).FromMaybe(false)) {
prop = Nan::Get(obj, Nan::New("src").ToLocalChecked()).ToLocalChecked();
if (prop->IsObject() && !prop->IsNull() && Nan::New(Dataset::constructor)->HasInstance(prop)) {
ds = Nan::ObjectWrap::Unwrap<Dataset>(prop.As<Object>());
if (!ds->getDataset()) {
#if GDAL_VERSION_MAJOR < 2
if (ds->getDatasource()) {
Nan::ThrowError("src dataset must be a raster dataset");
return;
}
#endif
Nan::ThrowError("src dataset already closed");
return;
}
} else {
Nan::ThrowTypeError("src property must be a Dataset object");
return;
}
} else {
Nan::ThrowError("src dataset must be provided");
return;
}
NODE_WRAPPED_FROM_OBJ(obj, "s_srs", SpatialReference, s_srs);
NODE_WRAPPED_FROM_OBJ(obj, "t_srs", SpatialReference, t_srs);
NODE_DOUBLE_FROM_OBJ_OPT(obj, "maxError", maxError);
char *s_srs_wkt, *t_srs_wkt;
if (s_srs->get()->exportToWkt(&s_srs_wkt)) {
Nan::ThrowError("Error converting s_srs to WKT");
return;
}
if (t_srs->get()->exportToWkt(&t_srs_wkt)) {
CPLFree(s_srs_wkt);
Nan::ThrowError("Error converting t_srs to WKT");
return;
}
void *hTransformArg;
void *hGenTransformArg =
GDALCreateGenImgProjTransformer(ds->getDataset(), s_srs_wkt, NULL, t_srs_wkt, TRUE, 1000.0, 0);
if (!hGenTransformArg) {
CPLFree(s_srs_wkt);
CPLFree(t_srs_wkt);
Nan::ThrowError(CPLGetLastErrorMsg());
return;
}
GDALTransformerFunc pfnTransformer;
if (maxError > 0.0) {
hTransformArg = GDALCreateApproxTransformer(GDALGenImgProjTransform, hGenTransformArg, maxError);
pfnTransformer = GDALApproxTransform;
if (!hTransformArg) {
CPLFree(s_srs_wkt);
CPLFree(t_srs_wkt);
GDALDestroyGenImgProjTransformer(hGenTransformArg);
Nan::ThrowError(CPLGetLastErrorMsg());
return;
}
} else {
hTransformArg = hGenTransformArg;
pfnTransformer = GDALGenImgProjTransform;
}
CPLErr err = GDALSuggestedWarpOutput(ds->getDataset(), pfnTransformer, hTransformArg, geotransform, &w, &h);
CPLFree(s_srs_wkt);
CPLFree(t_srs_wkt);
GDALDestroyGenImgProjTransformer(hGenTransformArg);
if (maxError > 0.0) { GDALDestroyApproxTransformer(hTransformArg); }
if (err) {
NODE_THROW_CPLERR(err);
return;
}
Local<Array> result_geotransform = Nan::New<Array>();
Nan::Set(result_geotransform, 0, Nan::New<Number>(geotransform[0]));
Nan::Set(result_geotransform, 1, Nan::New<Number>(geotransform[1]));
Nan::Set(result_geotransform, 2, Nan::New<Number>(geotransform[2]));
Nan::Set(result_geotransform, 3, Nan::New<Number>(geotransform[3]));
Nan::Set(result_geotransform, 4, Nan::New<Number>(geotransform[4]));
Nan::Set(result_geotransform, 5, Nan::New<Number>(geotransform[5]));
Local<Object> result_size = Nan::New<Object>();
Nan::Set(result_size, Nan::New("x").ToLocalChecked(), Nan::New<Integer>(w));
Nan::Set(result_size, Nan::New("y").ToLocalChecked(), Nan::New<Integer>(h));
Local<Object> result = Nan::New<Object>();
Nan::Set(result, Nan::New("rasterSize").ToLocalChecked(), result_size);
Nan::Set(result, Nan::New("geoTransform").ToLocalChecked(), result_geotransform);
info.GetReturnValue().Set(result);
}
} // namespace node_gdal