#include "gdal_algorithms.hpp"
#include "gdal_common.hpp"
#include "gdal_dataset.hpp"
#include "gdal_layer.hpp"
#include "gdal_rasterband.hpp"
#include "utils/number_list.hpp"
namespace node_gdal {
void Algorithms::Initialize(Local<Object> target) {
Nan::SetMethod(target, "fillNodata", fillNodata);
Nan::SetMethod(target, "contourGenerate", contourGenerate);
Nan::SetMethod(target, "sieveFilter", sieveFilter);
Nan::SetMethod(target, "checksumImage", checksumImage);
Nan::SetMethod(target, "polygonize", polygonize);
}
/**
* Fill raster regions by interpolation from edges.
*
* @throws Error
* @method fillNodata
* @static
* @for gdal
* @param {Object} options
* @param {gdal.RasterBand} options.src This band to be updated in-place.
* @param {gdal.RasterBand} [options.mask] Mask band
* @param {Number} options.searchDist The maximum distance (in pixels) that the
* algorithm will search out for values to interpolate.
* @param {integer} [options.smoothingIterations=0] The number of 3x3 average
* filter smoothing iterations to run after the interpolation to dampen
* artifacts.
*/
NAN_METHOD(Algorithms::fillNodata) {
Nan::HandleScope scope;
Local<Object> obj;
RasterBand *src;
RasterBand *mask = NULL;
double search_dist;
int smooth_iterations = 0;
NODE_ARG_OBJECT(0, "options", obj);
NODE_WRAPPED_FROM_OBJ(obj, "src", RasterBand, src);
NODE_WRAPPED_FROM_OBJ_OPT(obj, "mask", RasterBand, mask);
NODE_DOUBLE_FROM_OBJ(obj, "searchDist", search_dist);
NODE_INT_FROM_OBJ_OPT(obj, "smoothIterations", smooth_iterations)
CPLErr err =
GDALFillNodata(src->get(), mask ? mask->get() : NULL, search_dist, 0, smooth_iterations, NULL, NULL, NULL);
if (err) {
NODE_THROW_CPLERR(err);
return;
}
return;
}
/**
* Create vector contours from raster DEM.
*
* This algorithm will generate contour vectors for the input raster band on the
* requested set of contour levels. The vector contours are written to the
* passed in vector layer. Also, a NODATA value may be specified to identify
* pixels that should not be considered in contour line generation.
*
* @throws Error
* @method contourGenerate
* @static
* @for gdal
* @param {Object} options
* @param {gdal.RasterBand} options.src
* @param {gdal.Layer} options.dst
* @param {Number} [options.offset=0] The "offset" relative to which contour
* intervals are applied. This is normally zero, but could be different. To
* generate 10m contours at 5, 15, 25, ... the offset would be 5.
* @param {Number} [options.interval=100] The elevation interval between
* contours generated.
* @param {Number[]} [options.fixedLevels] A list of fixed contour levels at
* which contours should be generated. Overrides interval/base options if set.
* @param {Number} [options.nodata] The value to use as a "nodata" value. That
* is, a pixel value which should be ignored in generating contours as if the
* value of the pixel were not known.
* @param {integer} [options.idField] A field index to indicate where a unique
* id should be written for each feature (contour) written.
* @param {integer} [options.elevField] A field index to indicate where the
* elevation value of the contour should be written.
*/
NAN_METHOD(Algorithms::contourGenerate) {
Nan::HandleScope scope;
Local<Object> obj;
Local<Value> prop;
RasterBand *src;
Layer *dst;
double interval = 100, base = 0;
double *fixed_levels = NULL;
DoubleList fixed_level_array;
int n_fixed_levels = 0;
int use_nodata = 0;
double nodata = 0;
int id_field = -1, elev_field = -1;
NODE_ARG_OBJECT(0, "options", obj);
NODE_WRAPPED_FROM_OBJ(obj, "src", RasterBand, src);
NODE_WRAPPED_FROM_OBJ(obj, "dst", Layer, dst);
NODE_INT_FROM_OBJ_OPT(obj, "idField", id_field);
NODE_INT_FROM_OBJ_OPT(obj, "elevField", elev_field);
NODE_DOUBLE_FROM_OBJ_OPT(obj, "interval", interval);
NODE_DOUBLE_FROM_OBJ_OPT(obj, "offset", base);
if (Nan::HasOwnProperty(obj, Nan::New("fixedLevels").ToLocalChecked()).FromMaybe(false)) {
if (fixed_level_array.parse(Nan::Get(obj, Nan::New("fixedLevels").ToLocalChecked()).ToLocalChecked())) {
return; // error parsing double list
} else {
fixed_levels = fixed_level_array.get();
n_fixed_levels = fixed_level_array.length();
}
}
if (Nan::HasOwnProperty(obj, Nan::New("nodata").ToLocalChecked()).FromMaybe(false)) {
prop = Nan::Get(obj, Nan::New("nodata").ToLocalChecked()).ToLocalChecked();
if (prop->IsNumber()) {
use_nodata = 1;
nodata = Nan::To<double>(prop).ToChecked();
} else if (!prop->IsNull() && !prop->IsUndefined()) {
Nan::ThrowTypeError("nodata property must be a number");
}
}
CPLErr err = GDALContourGenerate(
src->get(),
interval,
base,
n_fixed_levels,
fixed_levels,
use_nodata,
nodata,
dst->get(),
id_field,
elev_field,
NULL,
NULL);
if (err) {
NODE_THROW_CPLERR(err);
return;
}
return;
}
/**
* Removes small raster polygons.
*
* @throws Error
* @method sieveFilter
* @static
* @for gdal
* @param {Object} options
* @param {gdal.RasterBand} options.src
* @param {gdal.RasterBand} options.dst Output raster band. It may be the same
* as src band to update the source in place.
* @param {gdal.RasterBand} [options.mask] All pixels in the mask band with a
* value other than zero will be considered suitable for inclusion in polygons.
* @param {Number} options.threshold Raster polygons with sizes smaller than
* this will be merged into their largest neighbour.
* @param {integer} [options.connectedness=4] Either 4 indicating that diagonal
* pixels are not considered directly adjacent for polygon membership purposes
* or 8 indicating they are.
*/
NAN_METHOD(Algorithms::sieveFilter) {
Nan::HandleScope scope;
Local<Object> obj;
RasterBand *src;
RasterBand *dst;
RasterBand *mask = NULL;
int threshold;
int connectedness = 4;
NODE_ARG_OBJECT(0, "options", obj);
NODE_WRAPPED_FROM_OBJ(obj, "src", RasterBand, src);
NODE_WRAPPED_FROM_OBJ(obj, "dst", RasterBand, dst);
NODE_WRAPPED_FROM_OBJ_OPT(obj, "mask", RasterBand, mask);
NODE_INT_FROM_OBJ(obj, "threshold", threshold);
NODE_INT_FROM_OBJ_OPT(obj, "connectedness", connectedness);
if (connectedness != 4 && connectedness != 8) {
Nan::ThrowError("connectedness option must be 4 or 8");
return;
}
CPLErr err =
GDALSieveFilter(src->get(), mask ? mask->get() : NULL, dst->get(), threshold, connectedness, NULL, NULL, NULL);
if (err) {
NODE_THROW_CPLERR(err);
return;
}
return;
}
/**
* Compute checksum for image region.
*
* @throws Error
* @method checksumImage
* @static
* @for gdal
* @param {gdal.RasterBand} src
* @param {integer} [x=0]
* @param {integer} [y=0]
* @param {integer} [w=src.width]
* @param {integer} [h=src.height]
* @return integer
*/
NAN_METHOD(Algorithms::checksumImage) {
Nan::HandleScope scope;
RasterBand *src;
int x = 0, y = 0, w, h, bandw, bandh;
NODE_ARG_WRAPPED(0, "src", RasterBand, src);
w = bandw = src->get()->GetXSize();
h = bandh = src->get()->GetYSize();
NODE_ARG_INT_OPT(1, "x", x);
NODE_ARG_INT_OPT(2, "y", y);
NODE_ARG_INT_OPT(3, "xSize", w);
NODE_ARG_INT_OPT(4, "ySize", h);
if (x < 0 || y < 0 || x >= bandw || y >= bandh) {
Nan::ThrowRangeError("offset invalid for given band");
return;
}
if (w < 0 || h < 0 || w > bandw || h > bandh) {
Nan::ThrowRangeError("x and y size must be smaller than band dimensions and greater than 0");
return;
}
if (x + w - 1 >= bandw || y + h - 1 >= bandh) {
Nan::ThrowRangeError("given range is outside bounds of given band");
return;
}
int checksum = GDALChecksumImage(src->get(), x, y, w, h);
info.GetReturnValue().Set(Nan::New<Integer>(checksum));
}
/**
* Creates vector polygons for all connected regions of pixels in the raster
* sharing a common pixel value. Each polygon is created with an attribute
* indicating the pixel value of that polygon. A raster mask may also be
* provided to determine which pixels are eligible for processing.
*
* @throws Error
* @method polygonize
* @static
* @for gdal
* @param {Object} options
* @param {gdal.RasterBand} options.src
* @param {gdal.Layer} options.dst
* @param {gdal.RasterBand} [options.mask]
* @param {integer} options.pixValField The attribute field index indicating the
* feature attribute into which the pixel value of the polygon should be
* written.
* @param {integer} [options.connectedness=4] Either 4 indicating that diagonal
* pixels are not considered directly adjacent for polygon membership purposes
* or 8 indicating they are.
* @param {Boolean} [options.useFloats=false] Use floating point buffers instead
* of int buffers.
*/
NAN_METHOD(Algorithms::polygonize) {
Nan::HandleScope scope;
Local<Object> obj;
RasterBand *src;
RasterBand *mask = NULL;
Layer *dst;
int connectedness = 4;
int pix_val_field = 0;
char **papszOptions = NULL;
NODE_ARG_OBJECT(0, "options", obj);
NODE_WRAPPED_FROM_OBJ(obj, "src", RasterBand, src);
NODE_WRAPPED_FROM_OBJ(obj, "dst", Layer, dst);
NODE_WRAPPED_FROM_OBJ_OPT(obj, "mask", RasterBand, mask);
NODE_INT_FROM_OBJ_OPT(obj, "connectedness", connectedness)
NODE_INT_FROM_OBJ(obj, "pixValField", pix_val_field);
if (connectedness == 8) {
papszOptions = CSLSetNameValue(papszOptions, "8CONNECTED", "8");
} else if (connectedness != 4) {
Nan::ThrowError("connectedness must be 4 or 8");
return;
}
CPLErr err;
if (
Nan::HasOwnProperty(obj, Nan::New("useFloats").ToLocalChecked()).FromMaybe(false) &&
Nan::To<bool>(Nan::Get(obj, Nan::New("useFloats").ToLocalChecked()).ToLocalChecked()).ToChecked()) {
err = GDALFPolygonize(
src->get(),
mask ? mask->get() : NULL,
reinterpret_cast<OGRLayerH>(dst->get()),
pix_val_field,
papszOptions,
NULL,
NULL);
} else {
err = GDALPolygonize(
src->get(),
mask ? mask->get() : NULL,
reinterpret_cast<OGRLayerH>(dst->get()),
pix_val_field,
papszOptions,
NULL,
NULL);
}
if (papszOptions) CSLDestroy(papszOptions);
if (err) {
NODE_THROW_CPLERR(err);
return;
}
return;
}
} // namespace node_gdal