#include "gdal_common.hpp"
#include "collections/rasterband_overviews.hpp"
#include "collections/rasterband_pixels.hpp"
#include "gdal_dataset.hpp"
#include "gdal_majorobject.hpp"
#include "gdal_rasterband.hpp"
#include <cpl_port.h>
#include <limits>
namespace node_gdal {
Nan::Persistent<FunctionTemplate> RasterBand::constructor;
ObjectCache<GDALRasterBand, RasterBand> RasterBand::cache;
void RasterBand::Initialize(Local<Object> target) {
Nan::HandleScope scope;
Local<FunctionTemplate> lcons = Nan::New<FunctionTemplate>(RasterBand::New);
lcons->InstanceTemplate()->SetInternalFieldCount(1);
lcons->SetClassName(Nan::New("RasterBand").ToLocalChecked());
Nan::SetPrototypeMethod(lcons, "toString", toString);
Nan::SetPrototypeMethod(lcons, "flush", flush);
Nan::SetPrototypeMethod(lcons, "fill", fill);
Nan::SetPrototypeMethod(lcons, "getStatistics", getStatistics);
Nan::SetPrototypeMethod(lcons, "setStatistics", setStatistics);
Nan::SetPrototypeMethod(lcons, "computeStatistics", computeStatistics);
Nan::SetPrototypeMethod(lcons, "getMaskBand", getMaskBand);
Nan::SetPrototypeMethod(lcons, "getMaskFlags", getMaskFlags);
Nan::SetPrototypeMethod(lcons, "createMaskBand", createMaskBand);
Nan::SetPrototypeMethod(lcons, "getMetadata", getMetadata);
// unimplemented methods
// Nan::SetPrototypeMethod(lcons, "buildOverviews", buildOverviews);
// Nan::SetPrototypeMethod(lcons, "rasterIO", rasterIO);
// Nan::SetPrototypeMethod(lcons, "getColorTable", getColorTable);
// Nan::SetPrototypeMethod(lcons, "setColorTable", setColorTable);
// Nan::SetPrototypeMethod(lcons, "getHistogram", getHistogram);
// Nan::SetPrototypeMethod(lcons, "getDefaultHistogram", getDefaultHistogram);
// Nan::SetPrototypeMethod(lcons, "setDefaultHistogram", setDefaultHistogram);
ATTR_DONT_ENUM(lcons, "ds", dsGetter, READ_ONLY_SETTER);
ATTR_DONT_ENUM(lcons, "_uid", uidGetter, READ_ONLY_SETTER);
ATTR(lcons, "id", idGetter, READ_ONLY_SETTER);
ATTR(lcons, "description", descriptionGetter, READ_ONLY_SETTER);
ATTR(lcons, "size", sizeGetter, READ_ONLY_SETTER);
ATTR(lcons, "overviews", overviewsGetter, READ_ONLY_SETTER);
ATTR(lcons, "pixels", pixelsGetter, READ_ONLY_SETTER);
ATTR(lcons, "blockSize", blockSizeGetter, READ_ONLY_SETTER);
ATTR(lcons, "minimum", minimumGetter, READ_ONLY_SETTER);
ATTR(lcons, "maximum", maximumGetter, READ_ONLY_SETTER);
ATTR(lcons, "readOnly", readOnlyGetter, READ_ONLY_SETTER);
ATTR(lcons, "dataType", dataTypeGetter, READ_ONLY_SETTER);
ATTR(lcons, "hasArbitraryOverviews", hasArbitraryOverviewsGetter, READ_ONLY_SETTER);
ATTR(lcons, "unitType", unitTypeGetter, unitTypeSetter);
ATTR(lcons, "scale", scaleGetter, scaleSetter);
ATTR(lcons, "offset", offsetGetter, offsetSetter);
ATTR(lcons, "noDataValue", noDataValueGetter, noDataValueSetter);
ATTR(lcons, "categoryNames", categoryNamesGetter, categoryNamesSetter);
ATTR(lcons, "colorInterpretation", colorInterpretationGetter, colorInterpretationSetter);
Nan::Set(target, Nan::New("RasterBand").ToLocalChecked(), Nan::GetFunction(lcons).ToLocalChecked());
constructor.Reset(lcons);
}
RasterBand::RasterBand(GDALRasterBand *band) : Nan::ObjectWrap(), uid(0), this_(band), parent_ds(0) {
LOG("Created band [%p] (dataset = %p)", band, band->GetDataset());
}
RasterBand::RasterBand() : Nan::ObjectWrap(), uid(0), this_(0), parent_ds(0) {
}
RasterBand::~RasterBand() {
dispose();
}
void RasterBand::dispose() {
if (this_) {
LOG("Disposing band [%p]", this_);
ptr_manager.dispose(uid);
LOG("Disposed band [%p]", this_);
this_ = NULL;
}
}
/**
* A single raster band (or channel).
*
* @class gdal.RasterBand
*/
NAN_METHOD(RasterBand::New) {
Nan::HandleScope scope;
if (!info.IsConstructCall()) {
Nan::ThrowError("Cannot call constructor as function, you need to use 'new' keyword");
return;
}
if (info[0]->IsExternal()) {
Local<External> ext = info[0].As<External>();
void *ptr = ext->Value();
RasterBand *f = static_cast<RasterBand *>(ptr);
f->Wrap(info.This());
Local<Value> overviews = RasterBandOverviews::New(info.This());
Nan::SetPrivate(info.This(), Nan::New("overviews_").ToLocalChecked(), overviews);
Local<Value> pixels = RasterBandPixels::New(info.This());
Nan::SetPrivate(info.This(), Nan::New("pixels_").ToLocalChecked(), pixels);
info.GetReturnValue().Set(info.This());
return;
} else {
Nan::ThrowError("Cannot create band directly create with dataset instead");
return;
}
}
Local<Value> RasterBand::New(GDALRasterBand *raw, GDALDataset *raw_parent) {
Nan::EscapableHandleScope scope;
if (!raw) { return scope.Escape(Nan::Null()); }
if (cache.has(raw)) { return scope.Escape(cache.get(raw)); }
RasterBand *wrapped = new RasterBand(raw);
Local<Value> ext = Nan::New<External>(wrapped);
Local<Object> obj =
Nan::NewInstance(Nan::GetFunction(Nan::New(RasterBand::constructor)).ToLocalChecked(), 1, &ext).ToLocalChecked();
LOG("Adding band to cache[%p] (parent=%p)", raw, raw_parent);
cache.add(raw, obj);
// add reference to dataset so dataset doesnt get GC'ed while band is alive
// DONT USE GDALRasterBand.GetDataset() ... it will return a "fake" dataset
// for overview bands
// https://github.com/naturalatlas/node-gdal/blob/master/deps/libgdal/gdal/frmts/gtiff/geotiff.cpp#L84
Local<Object> ds;
uv_mutex_t *async_lock;
if (!Dataset::dataset_cache.has(raw_parent)) {
LOG("Band's parent dataset disappeared from cache (band = %p, dataset = %p)", raw, raw_parent);
Nan::ThrowError("Band's parent dataset disappeared from cache");
return scope.Escape(Nan::Undefined());
// ds = Dataset::New(raw_parent); //this should never happen
}
ds = Dataset::dataset_cache.get(raw_parent);
Dataset *parent = Nan::ObjectWrap::Unwrap<Dataset>(ds);
async_lock = parent->async_lock;
long parent_uid = parent->uid;
wrapped->uid = ptr_manager.add(raw, parent_uid);
wrapped->parent_ds = raw_parent;
wrapped->async_lock = async_lock;
Nan::SetPrivate(obj, Nan::New("ds_").ToLocalChecked(), ds);
return scope.Escape(obj);
}
NAN_METHOD(RasterBand::toString) {
Nan::HandleScope scope;
info.GetReturnValue().Set(Nan::New("RasterBand").ToLocalChecked());
}
/**
* Saves changes to disk.
*
* @method flush
*/
NODE_WRAPPED_METHOD(RasterBand, flush, FlushCache);
/**
* Return the status flags of the mask band associated with the band.
*
* The result will be a bitwise OR-ed set of status flags with the following
* available definitions that may be extended in the future:
*
* - `GMF_ALL_VALID` (`0x01`): There are no invalid pixels, all mask values
* will be 255. When used this will normally be the only flag set.
* - `GMF_PER_DATASET` (`0x02`): The mask band is shared between all bands on
* the dataset.
* - `GMF_ALPHA` (`0x04`): The mask band is actually an alpha band and may
* have values other than 0 and 255.
* - `GMF_NODATA` (`0x08`): Indicates the mask is actually being generated
* from nodata values. (mutually exclusive of `GMF_ALPHA`)
*
* @method getMaskFlags
* @return {Integer} Mask flags
*/
NODE_WRAPPED_METHOD_WITH_RESULT(RasterBand, getMaskFlags, Integer, GetMaskFlags);
// TODO: expose GMF constants in API
// ({{#crossLink "Constants (GMF)"}}see flags{{/crossLink}})
/**
* Adds a mask band to the current band.
*
* @throws Error
* @method createMaskBand
* @param {Integer} flags Mask flags
*/
NODE_WRAPPED_METHOD_WITH_CPLERR_RESULT_1_INTEGER_PARAM(RasterBand, createMaskBand, CreateMaskBand, "mask flags");
// TODO: expose GMF constants in API
// ({{#crossLink "Constants (GMF)"}}see flags{{/crossLink}})
/**
* Return the mask band associated with the band.
*
* @method getMaskBand
* @return {gdal.RasterBand}
*/
NAN_METHOD(RasterBand::getMaskBand) {
Nan::HandleScope scope;
RasterBand *band = Nan::ObjectWrap::Unwrap<RasterBand>(info.This());
if (!band->isAlive()) {
Nan::ThrowError("RasterBand object has already been destroyed");
return;
}
uv_mutex_lock(band->async_lock);
GDALRasterBand *mask_band = band->this_->GetMaskBand();
uv_mutex_unlock(band->async_lock);
if (!mask_band) {
info.GetReturnValue().Set(Nan::Null());
return;
}
info.GetReturnValue().Set(RasterBand::New(mask_band, band->getParent()));
}
/**
* Fill this band with a constant value.
*
* @throws Error
* @method fill
* @param {Number} real_value
* @param {Number} [imaginary_value]
*/
NAN_METHOD(RasterBand::fill) {
Nan::HandleScope scope;
double real, imaginary = 0;
NODE_ARG_DOUBLE(0, "real value", real);
NODE_ARG_DOUBLE_OPT(1, "imaginary value", real);
RasterBand *band = Nan::ObjectWrap::Unwrap<RasterBand>(info.This());
if (!band->isAlive()) {
Nan::ThrowError("RasterBand object has already been destroyed");
return;
}
uv_mutex_lock(band->async_lock);
int err = band->this_->Fill(real, imaginary);
uv_mutex_unlock(band->async_lock);
if (err) {
NODE_THROW_CPLERR(err);
return;
}
return;
}
// --- Custom error handling to handle VRT errors ---
// see: https://github.com/mapbox/mapnik-omnivore/issues/10
std::string stats_file_err = "";
CPLErrorHandler last_err_handler;
void CPL_STDCALL statisticsErrorHandler(CPLErr eErrClass, int err_no, const char *msg) {
if (err_no == CPLE_OpenFailed) { stats_file_err = msg; }
if (last_err_handler) { last_err_handler(eErrClass, err_no, msg); }
}
void pushStatsErrorHandler() {
last_err_handler = CPLSetErrorHandler(statisticsErrorHandler);
}
void popStatsErrorHandler() {
if (!last_err_handler) return;
CPLSetErrorHandler(last_err_handler);
}
/**
* Fetch image statistics.
*
* Returns the minimum, maximum, mean and standard deviation of all pixel values
* in this band. If approximate statistics are sufficient, the
* `allow_approximation` argument can be set to `true` in which case overviews,
* or a subset of image tiles may be used in computing the statistics.
*
* @throws Error
* @method getStatistics
* @param {Boolean} allow_approximation If `true` statistics may be computed
* based on overviews or a subset of all tiles.
* @param {Boolean} force If `false` statistics will only be returned if it can
* be done without rescanning the image.
* @return {Object} Statistics containing `"min"`, `"max"`, `"mean"`,
* `"std_dev"` properties.
*/
NAN_METHOD(RasterBand::getStatistics) {
Nan::HandleScope scope;
double min, max, mean, std_dev;
int approx, force;
NODE_ARG_BOOL(0, "allow approximation", approx);
NODE_ARG_BOOL(1, "force", force);
RasterBand *band = Nan::ObjectWrap::Unwrap<RasterBand>(info.This());
if (!band->isAlive()) {
Nan::ThrowError("RasterBand object has already been destroyed");
return;
}
uv_mutex_lock(band->async_lock);
pushStatsErrorHandler();
CPLErr err = band->this_->GetStatistics(approx, force, &min, &max, &mean, &std_dev);
popStatsErrorHandler();
uv_mutex_unlock(band->async_lock);
if (!stats_file_err.empty()) {
Nan::ThrowError(stats_file_err.c_str());
} else if (err) {
if (!force && err == CE_Warning) {
Nan::ThrowError("Statistics cannot be efficiently computed without scanning raster");
return;
}
NODE_THROW_CPLERR(err);
return;
}
Local<Object> result = Nan::New<Object>();
Nan::Set(result, Nan::New("min").ToLocalChecked(), Nan::New<Number>(min));
Nan::Set(result, Nan::New("max").ToLocalChecked(), Nan::New<Number>(max));
Nan::Set(result, Nan::New("mean").ToLocalChecked(), Nan::New<Number>(mean));
Nan::Set(result, Nan::New("std_dev").ToLocalChecked(), Nan::New<Number>(std_dev));
info.GetReturnValue().Set(result);
}
/**
* Computes image statistics.
*
* Returns the minimum, maximum, mean and standard deviation of all pixel values
* in this band. If approximate statistics are sufficient, the
* `allow_approximation` argument can be set to `true` in which case overviews,
* or a subset of image tiles may be used in computing the statistics.
*
* @throws Error
* @method computeStatistics
* @param {Boolean} allow_approximation If `true` statistics may be computed
* based on overviews or a subset of all tiles.
* @return {Object} Statistics containing `"min"`, `"max"`, `"mean"`,
* `"std_dev"` properties.
*/
NAN_METHOD(RasterBand::computeStatistics) {
Nan::HandleScope scope;
double min, max, mean, std_dev;
int approx;
NODE_ARG_BOOL(0, "allow approximation", approx);
RasterBand *band = Nan::ObjectWrap::Unwrap<RasterBand>(info.This());
if (!band->isAlive()) {
Nan::ThrowError("RasterBand object has already been destroyed");
return;
}
uv_mutex_lock(band->async_lock);
pushStatsErrorHandler();
CPLErr err = band->this_->ComputeStatistics(approx, &min, &max, &mean, &std_dev, NULL, NULL);
popStatsErrorHandler();
uv_mutex_unlock(band->async_lock);
if (!stats_file_err.empty()) {
Nan::ThrowError(stats_file_err.c_str());
} else if (err) {
NODE_THROW_CPLERR(err);
return;
}
Local<Object> result = Nan::New<Object>();
Nan::Set(result, Nan::New("min").ToLocalChecked(), Nan::New<Number>(min));
Nan::Set(result, Nan::New("max").ToLocalChecked(), Nan::New<Number>(max));
Nan::Set(result, Nan::New("mean").ToLocalChecked(), Nan::New<Number>(mean));
Nan::Set(result, Nan::New("std_dev").ToLocalChecked(), Nan::New<Number>(std_dev));
info.GetReturnValue().Set(result);
}
/**
* Set statistics on the band. This method can be used to store
* min/max/mean/standard deviation statistics.
*
* @throws Error
* @method setStatistics
* @param {Number} min
* @param {Number} max
* @param {Number} mean
* @param {Number} std_dev
*/
NAN_METHOD(RasterBand::setStatistics) {
Nan::HandleScope scope;
double min, max, mean, std_dev;
NODE_ARG_DOUBLE(0, "min", min);
NODE_ARG_DOUBLE(1, "max", max);
NODE_ARG_DOUBLE(2, "mean", mean);
NODE_ARG_DOUBLE(3, "standard deviation", std_dev);
RasterBand *band = Nan::ObjectWrap::Unwrap<RasterBand>(info.This());
if (!band->isAlive()) {
Nan::ThrowError("RasterBand object has already been destroyed");
return;
}
uv_mutex_lock(band->async_lock);
CPLErr err = band->this_->SetStatistics(min, max, mean, std_dev);
uv_mutex_unlock(band->async_lock);
if (err) {
NODE_THROW_CPLERR(err);
return;
}
return;
}
/**
* Returns band metadata
*
* @method getMetadata
* @param {string} [domain]
*/
NAN_METHOD(RasterBand::getMetadata) {
Nan::HandleScope scope;
std::string domain("");
NODE_ARG_OPT_STR(0, "domain", domain);
RasterBand *band = Nan::ObjectWrap::Unwrap<RasterBand>(info.This());
if (!band->isAlive()) {
Nan::ThrowError("RasterBand object has already been destroyed");
return;
}
uv_mutex_lock(band->async_lock);
const Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE &meta =
MajorObject::getMetadata(band->this_, domain.empty() ? NULL : domain.c_str());
uv_mutex_unlock(band->async_lock);
info.GetReturnValue().Set(meta);
}
/**
* @readOnly
* @attribute ds
* @type {gdal.Dataset}
*/
NAN_GETTER(RasterBand::dsGetter) {
Nan::HandleScope scope;
info.GetReturnValue().Set(Nan::GetPrivate(info.This(), Nan::New("ds_").ToLocalChecked()).ToLocalChecked());
}
/**
* @readOnly
* @attribute overviews
* @type {gdal.RasterBandOverviews}
*/
NAN_GETTER(RasterBand::overviewsGetter) {
Nan::HandleScope scope;
info.GetReturnValue().Set(Nan::GetPrivate(info.This(), Nan::New("overviews_").ToLocalChecked()).ToLocalChecked());
}
/**
* @readOnly
* @attribute pixels
* @type {gdal.RasterBandPixels}
*/
NAN_GETTER(RasterBand::pixelsGetter) {
Nan::HandleScope scope;
info.GetReturnValue().Set(Nan::GetPrivate(info.This(), Nan::New("pixels_").ToLocalChecked()).ToLocalChecked());
}
/**
* @readOnly
* @attribute id
* @type {Integer|null}
*/
NAN_GETTER(RasterBand::idGetter) {
Nan::HandleScope scope;
RasterBand *band = Nan::ObjectWrap::Unwrap<RasterBand>(info.This());
if (!band->isAlive()) {
Nan::ThrowError("RasterBand object has already been destroyed");
return;
}
uv_mutex_lock(band->async_lock);
int id = band->this_->GetBand();
uv_mutex_unlock(band->async_lock);
if (id == 0) {
info.GetReturnValue().Set(Nan::Null());
return;
} else {
info.GetReturnValue().Set(Nan::New<Integer>(id));
return;
}
}
/**
* Name of of band.
*
* @readOnly
* @attribute description
* @type {string}
*/
NAN_GETTER(RasterBand::descriptionGetter) {
Nan::HandleScope scope;
RasterBand *band = Nan::ObjectWrap::Unwrap<RasterBand>(info.This());
if (!band->isAlive()) {
Nan::ThrowError("RasterBand object has already been destroyed");
return;
}
uv_mutex_lock(band->async_lock);
const char *desc = band->this_->GetDescription();
uv_mutex_unlock(band->async_lock);
info.GetReturnValue().Set(SafeString::New(desc));
}
/**
* Size object containing `"x"` and `"y"` properties.
*
* @readOnly
* @attribute size
* @type {Object}
*/
NAN_GETTER(RasterBand::sizeGetter) {
Nan::HandleScope scope;
RasterBand *band = Nan::ObjectWrap::Unwrap<RasterBand>(info.This());
if (!band->isAlive()) {
Nan::ThrowError("RasterBand object has already been destroyed");
return;
}
Local<Object> result = Nan::New<Object>();
uv_mutex_lock(band->async_lock);
int x = band->this_->GetXSize();
int y = band->this_->GetYSize();
uv_mutex_unlock(band->async_lock);
Nan::Set(result, Nan::New("x").ToLocalChecked(), Nan::New<Integer>(x));
Nan::Set(result, Nan::New("y").ToLocalChecked(), Nan::New<Integer>(y));
info.GetReturnValue().Set(result);
}
/**
* Size object containing `"x"` and `"y"` properties.
*
* @readOnly
* @attribute blockSize
* @type {Object}
*/
NAN_GETTER(RasterBand::blockSizeGetter) {
Nan::HandleScope scope;
RasterBand *band = Nan::ObjectWrap::Unwrap<RasterBand>(info.This());
if (!band->isAlive()) {
Nan::ThrowError("RasterBand object has already been destroyed");
return;
}
int x, y;
uv_mutex_lock(band->async_lock);
band->this_->GetBlockSize(&x, &y);
uv_mutex_unlock(band->async_lock);
Local<Object> result = Nan::New<Object>();
Nan::Set(result, Nan::New("x").ToLocalChecked(), Nan::New<Integer>(x));
Nan::Set(result, Nan::New("y").ToLocalChecked(), Nan::New<Integer>(y));
info.GetReturnValue().Set(result);
}
/**
* Minimum value for this band.
*
* @readOnly
* @attribute minimum
* @type {Number}
*/
NAN_GETTER(RasterBand::minimumGetter) {
Nan::HandleScope scope;
RasterBand *band = Nan::ObjectWrap::Unwrap<RasterBand>(info.This());
if (!band->isAlive()) {
Nan::ThrowError("RasterBand object has already been destroyed");
return;
}
int success = 0;
uv_mutex_lock(band->async_lock);
double result = band->this_->GetMinimum(&success);
uv_mutex_unlock(band->async_lock);
info.GetReturnValue().Set(Nan::New<Number>(result));
}
/**
* Maximum value for this band.
*
* @readOnly
* @attribute maximum
* @type {Number}
*/
NAN_GETTER(RasterBand::maximumGetter) {
Nan::HandleScope scope;
RasterBand *band = Nan::ObjectWrap::Unwrap<RasterBand>(info.This());
if (!band->isAlive()) {
Nan::ThrowError("RasterBand object has already been destroyed");
return;
}
int success = 0;
uv_mutex_lock(band->async_lock);
double result = band->this_->GetMaximum(&success);
uv_mutex_unlock(band->async_lock);
info.GetReturnValue().Set(Nan::New<Number>(result));
}
/**
* Raster value offset.
*
* @attribute offset
* @type {Number}
*/
NAN_GETTER(RasterBand::offsetGetter) {
Nan::HandleScope scope;
RasterBand *band = Nan::ObjectWrap::Unwrap<RasterBand>(info.This());
if (!band->isAlive()) {
Nan::ThrowError("RasterBand object has already been destroyed");
return;
}
int success = 0;
uv_mutex_lock(band->async_lock);
double result = band->this_->GetOffset(&success);
uv_mutex_unlock(band->async_lock);
info.GetReturnValue().Set(Nan::New<Number>(result));
}
/**
* Raster value scale.
*
* @attribute scale
* @type {Number}
*/
NAN_GETTER(RasterBand::scaleGetter) {
Nan::HandleScope scope;
RasterBand *band = Nan::ObjectWrap::Unwrap<RasterBand>(info.This());
if (!band->isAlive()) {
Nan::ThrowError("RasterBand object has already been destroyed");
return;
}
int success = 0;
uv_mutex_lock(band->async_lock);
double result = band->this_->GetScale(&success);
uv_mutex_unlock(band->async_lock);
info.GetReturnValue().Set(Nan::New<Number>(result));
}
/**
* No data value for this band.
*
* @attribute noDataValue
* @type {Number|null}
*/
NAN_GETTER(RasterBand::noDataValueGetter) {
Nan::HandleScope scope;
RasterBand *band = Nan::ObjectWrap::Unwrap<RasterBand>(info.This());
if (!band->isAlive()) {
Nan::ThrowError("RasterBand object has already been destroyed");
return;
}
int success = 0;
uv_mutex_lock(band->async_lock);
double result = band->this_->GetNoDataValue(&success);
uv_mutex_unlock(band->async_lock);
if (success && !CPLIsNan(result)) {
info.GetReturnValue().Set(Nan::New<Number>(result));
return;
} else {
info.GetReturnValue().Set(Nan::Null());
return;
}
}
/**
* Raster unit type (name for the units of this raster's values).
* For instance, it might be `"m"` for an elevation model in meters,
* or `"ft"` for feet. If no units are available, a value of `""`
* will be returned.
*
* @attribute unitType
* @type {String}
*/
NAN_GETTER(RasterBand::unitTypeGetter) {
Nan::HandleScope scope;
RasterBand *band = Nan::ObjectWrap::Unwrap<RasterBand>(info.This());
if (!band->isAlive()) {
Nan::ThrowError("RasterBand object has already been destroyed");
return;
}
uv_mutex_lock(band->async_lock);
const char *result = band->this_->GetUnitType();
uv_mutex_unlock(band->async_lock);
info.GetReturnValue().Set(SafeString::New(result));
}
/**
* Pixel data type ({{#crossLink "Constants (GDT)"}}see GDT
* constants{{/crossLink}}) used for this band.
*
* @readOnly
* @attribute dataType
* @type {String|Undefined}
*/
NAN_GETTER(RasterBand::dataTypeGetter) {
Nan::HandleScope scope;
RasterBand *band = Nan::ObjectWrap::Unwrap<RasterBand>(info.This());
if (!band->isAlive()) {
Nan::ThrowError("RasterBand object has already been destroyed");
return;
}
uv_mutex_lock(band->async_lock);
GDALDataType type = band->this_->GetRasterDataType();
uv_mutex_unlock(band->async_lock);
if (type == GDT_Unknown) return;
info.GetReturnValue().Set(SafeString::New(GDALGetDataTypeName(type)));
}
/**
* Indicates if the band is read-only.
*
* @readOnly
* @attribute readOnly
* @type {Boolean}
*/
NAN_GETTER(RasterBand::readOnlyGetter) {
Nan::HandleScope scope;
RasterBand *band = Nan::ObjectWrap::Unwrap<RasterBand>(info.This());
if (!band->isAlive()) {
Nan::ThrowError("RasterBand object has already been destroyed");
return;
}
uv_mutex_lock(band->async_lock);
GDALAccess result = band->this_->GetAccess();
uv_mutex_unlock(band->async_lock);
info.GetReturnValue().Set(result == GA_Update ? Nan::False() : Nan::True());
}
/**
* An indicator if the underlying datastore can compute arbitrary overviews
* efficiently, such as is the case with OGDI over a network. Datastores with
* arbitrary overviews don't generally have any fixed overviews, but GDAL's
* `RasterIO()` method can be used in downsampling mode to get overview
* data efficiently.
*
* @readOnly
* @attribute hasArbitraryOverviews
* @type {Boolean}
*/
NAN_GETTER(RasterBand::hasArbitraryOverviewsGetter) {
Nan::HandleScope scope;
RasterBand *band = Nan::ObjectWrap::Unwrap<RasterBand>(info.This());
if (!band->isAlive()) {
Nan::ThrowError("RasterBand object has already been destroyed");
return;
}
uv_mutex_lock(band->async_lock);
bool result = band->this_->HasArbitraryOverviews();
uv_mutex_unlock(band->async_lock);
info.GetReturnValue().Set(Nan::New<Boolean>(result));
}
/**
* List of list of category names for this raster.
*
* @attribute categoryNames
* @type {Array}
*/
NAN_GETTER(RasterBand::categoryNamesGetter) {
Nan::HandleScope scope;
RasterBand *band = Nan::ObjectWrap::Unwrap<RasterBand>(info.This());
if (!band->isAlive()) {
Nan::ThrowError("RasterBand object has already been destroyed");
return;
}
uv_mutex_lock(band->async_lock);
char **names = band->this_->GetCategoryNames();
uv_mutex_unlock(band->async_lock);
Local<Array> results = Nan::New<Array>();
if (names) {
int i = 0;
while (names[i]) {
Nan::Set(results, i, Nan::New(names[i]).ToLocalChecked());
i++;
}
}
info.GetReturnValue().Set(results);
}
/**
* Color interpretation mode ({{#crossLink "Constants (GCI)"}}see GCI
* constants{{/crossLink}}).
*
* @attribute colorInterpretation
* @type {string}
*/
NAN_GETTER(RasterBand::colorInterpretationGetter) {
Nan::HandleScope scope;
RasterBand *band = Nan::ObjectWrap::Unwrap<RasterBand>(info.This());
if (!band->isAlive()) {
Nan::ThrowError("RasterBand object has already been destroyed");
return;
}
uv_mutex_lock(band->async_lock);
GDALColorInterp interp = band->this_->GetColorInterpretation();
uv_mutex_unlock(band->async_lock);
if (interp == GCI_Undefined)
return;
else
info.GetReturnValue().Set(SafeString::New(GDALGetColorInterpretationName(interp)));
}
NAN_SETTER(RasterBand::unitTypeSetter) {
Nan::HandleScope scope;
RasterBand *band = Nan::ObjectWrap::Unwrap<RasterBand>(info.This());
if (!band->isAlive()) {
Nan::ThrowError("RasterBand object has already been destroyed");
return;
}
if (!value->IsString()) {
Nan::ThrowError("Unit type must be a string");
return;
}
std::string input = *Nan::Utf8String(value);
uv_mutex_lock(band->async_lock);
CPLErr err = band->this_->SetUnitType(input.c_str());
uv_mutex_unlock(band->async_lock);
if (err) { NODE_THROW_CPLERR(err); }
}
NAN_SETTER(RasterBand::noDataValueSetter) {
Nan::HandleScope scope;
RasterBand *band = Nan::ObjectWrap::Unwrap<RasterBand>(info.This());
if (!band->isAlive()) {
Nan::ThrowError("RasterBand object has already been destroyed");
return;
}
double input;
if (value->IsNull() || value->IsUndefined()) {
input = std::numeric_limits<double>::quiet_NaN();
} else if (value->IsNumber()) {
input = Nan::To<double>(value).ToChecked();
} else {
Nan::ThrowError("No data value must be a number");
return;
}
uv_mutex_lock(band->async_lock);
CPLErr err = band->this_->SetNoDataValue(input);
uv_mutex_unlock(band->async_lock);
if (err) { NODE_THROW_CPLERR(err); }
}
NAN_SETTER(RasterBand::scaleSetter) {
Nan::HandleScope scope;
RasterBand *band = Nan::ObjectWrap::Unwrap<RasterBand>(info.This());
if (!band->isAlive()) {
Nan::ThrowError("RasterBand object has already been destroyed");
return;
}
if (!value->IsNumber()) {
Nan::ThrowError("Scale must be a number");
return;
}
double input = Nan::To<double>(value).ToChecked();
uv_mutex_lock(band->async_lock);
CPLErr err = band->this_->SetScale(input);
uv_mutex_unlock(band->async_lock);
if (err) { NODE_THROW_CPLERR(err); }
}
NAN_SETTER(RasterBand::offsetSetter) {
Nan::HandleScope scope;
RasterBand *band = Nan::ObjectWrap::Unwrap<RasterBand>(info.This());
if (!band->isAlive()) {
Nan::ThrowError("RasterBand object has already been destroyed");
return;
}
if (!value->IsNumber()) {
Nan::ThrowError("Offset must be a number");
return;
}
double input = Nan::To<double>(value).ToChecked();
uv_mutex_lock(band->async_lock);
CPLErr err = band->this_->SetOffset(input);
uv_mutex_unlock(band->async_lock);
if (err) { NODE_THROW_CPLERR(err); }
}
NAN_SETTER(RasterBand::categoryNamesSetter) {
Nan::HandleScope scope;
RasterBand *band = Nan::ObjectWrap::Unwrap<RasterBand>(info.This());
if (!band->isAlive()) {
Nan::ThrowError("RasterBand object has already been destroyed");
return;
}
if (!value->IsArray()) {
Nan::ThrowError("Category names must be an array");
return;
}
Local<Array> names = value.As<Array>();
char **list = NULL;
std::string *strlist = NULL;
if (names->Length() > 0) {
list = new char *[names->Length() + 1];
strlist = new std::string[names->Length()];
unsigned int i;
for (i = 0; i < names->Length(); i++) {
strlist[i] = *Nan::Utf8String(Nan::Get(names, i).ToLocalChecked());
list[i] = (char *)strlist[i].c_str();
}
list[i] = NULL;
}
uv_mutex_lock(band->async_lock);
int err = band->this_->SetCategoryNames(list);
uv_mutex_unlock(band->async_lock);
if (list) { delete[] list; }
if (err) { NODE_THROW_CPLERR(err); }
}
NAN_SETTER(RasterBand::colorInterpretationSetter) {
Nan::HandleScope scope;
RasterBand *band = Nan::ObjectWrap::Unwrap<RasterBand>(info.This());
if (!band->isAlive()) {
Nan::ThrowError("RasterBand object has already been destroyed");
return;
}
GDALColorInterp ci = GCI_Undefined;
if (value->IsString()) {
std::string name = *Nan::Utf8String(value);
ci = GDALGetColorInterpretationByName(name.c_str());
} else if (!value->IsNull() && !value->IsUndefined()) {
Nan::ThrowError("color interpretation must be a string or undefined");
return;
}
uv_mutex_lock(band->async_lock);
CPLErr err = band->this_->SetColorInterpretation(ci);
uv_mutex_unlock(band->async_lock);
if (err) { NODE_THROW_CPLERR(err); }
}
NAN_GETTER(RasterBand::uidGetter) {
Nan::HandleScope scope;
RasterBand *band = Nan::ObjectWrap::Unwrap<RasterBand>(info.This());
info.GetReturnValue().Set(Nan::New((int)band->uid));
}
} // namespace node_gdal