#include "gdal_coordinate_transformation.hpp"
#include "gdal_common.hpp"
#include "gdal_dataset.hpp"
#include "gdal_spatial_reference.hpp"
namespace node_gdal {
Nan::Persistent<FunctionTemplate> CoordinateTransformation::constructor;
void CoordinateTransformation::Initialize(Local<Object> target) {
Nan::HandleScope scope;
Local<FunctionTemplate> lcons = Nan::New<FunctionTemplate>(CoordinateTransformation::New);
lcons->InstanceTemplate()->SetInternalFieldCount(1);
lcons->SetClassName(Nan::New("CoordinateTransformation").ToLocalChecked());
Nan::SetPrototypeMethod(lcons, "toString", toString);
Nan::SetPrototypeMethod(lcons, "transformPoint", transformPoint);
Nan::Set(target, Nan::New("CoordinateTransformation").ToLocalChecked(), Nan::GetFunction(lcons).ToLocalChecked());
constructor.Reset(lcons);
}
CoordinateTransformation::CoordinateTransformation(OGRCoordinateTransformation *transform)
: Nan::ObjectWrap(), this_(transform) {
LOG("Created CoordinateTransformation [%p]", transform);
}
CoordinateTransformation::CoordinateTransformation() : Nan::ObjectWrap(), this_(0) {
}
CoordinateTransformation::~CoordinateTransformation() {
if (this_) {
LOG("Disposing CoordinateTransformation [%p]", this_);
OGRCoordinateTransformation::DestroyCT(this_);
LOG("Disposed CoordinateTransformation [%p]", this_);
this_ = NULL;
}
}
/**
* Object for transforming between coordinate systems.
*
* @throws Error
* @constructor
* @class gdal.CoordinateTransformation
* @param {gdal.SpatialReference} source
* @param {gdal.SpatialReference|gdal.Dataset} target If a raster Dataset, the
* conversion will represent a conversion to pixel coordinates.
*/
NAN_METHOD(CoordinateTransformation::New) {
Nan::HandleScope scope;
CoordinateTransformation *f;
SpatialReference *source, *target;
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();
f = static_cast<CoordinateTransformation *>(ptr);
} else {
if (info.Length() < 2) {
Nan::ThrowError("Invalid number of arguments");
return;
}
NODE_ARG_WRAPPED(0, "source", SpatialReference, source);
if (!info[1]->IsObject() || info[1]->IsNull()) {
Nan::ThrowTypeError("target must be a SpatialReference or Dataset object");
return;
}
if (Nan::New(SpatialReference::constructor)->HasInstance(info[1])) {
// srs -> srs
NODE_ARG_WRAPPED(1, "target", SpatialReference, target);
OGRCoordinateTransformation *transform = OGRCreateCoordinateTransformation(source->get(), target->get());
if (!transform) {
NODE_THROW_LAST_CPLERR();
return;
}
f = new CoordinateTransformation(transform);
} else if (Nan::New(Dataset::constructor)->HasInstance(info[1])) {
// srs -> px/line
// todo: allow additional options using StringList
Dataset *ds;
char **papszTO = NULL;
char *src_wkt;
ds = Nan::ObjectWrap::Unwrap<Dataset>(info[1].As<Object>());
if (!ds->getDataset()) {
#if GDAL_VERSION_MAJOR < 2
if (ds->getDatasource()) {
Nan::ThrowError("Only raster datasets can be used to create geotransform coordinate transformations");
return;
}
#endif
Nan::ThrowError("Dataset already closed");
return;
}
OGRErr err = source->get()->exportToWkt(&src_wkt);
if (err) {
NODE_THROW_OGRERR(err);
return;
}
papszTO = CSLSetNameValue(papszTO, "DST_SRS", src_wkt);
papszTO = CSLSetNameValue(papszTO, "INSERT_CENTER_LONG", "FALSE");
GeoTransformTransformer *transform = new GeoTransformTransformer();
transform->hSrcImageTransformer = GDALCreateGenImgProjTransformer2(ds->getDataset(), NULL, papszTO);
if (!transform->hSrcImageTransformer) {
NODE_THROW_LAST_CPLERR();
return;
}
f = new CoordinateTransformation(transform);
CPLFree(src_wkt);
CSLDestroy(papszTO);
} else {
Nan::ThrowTypeError("target must be a SpatialReference or Dataset object");
return;
}
}
f->Wrap(info.This());
info.GetReturnValue().Set(info.This());
}
Local<Value> CoordinateTransformation::New(OGRCoordinateTransformation *transform) {
Nan::EscapableHandleScope scope;
if (!transform) { return scope.Escape(Nan::Null()); }
CoordinateTransformation *wrapped = new CoordinateTransformation(transform);
Local<Value> ext = Nan::New<External>(wrapped);
Local<Object> obj =
Nan::NewInstance(Nan::GetFunction(Nan::New(CoordinateTransformation::constructor)).ToLocalChecked(), 1, &ext)
.ToLocalChecked();
return scope.Escape(obj);
}
NAN_METHOD(CoordinateTransformation::toString) {
Nan::HandleScope scope;
info.GetReturnValue().Set(Nan::New("CoordinateTransformation").ToLocalChecked());
}
/**
* Transform point from source to destination space.
*
* @example
* ```
* pt = transform.transformPoint(0, 0, 0);
* pt = transform.transformPoint({x: 0, y: 0, z: 0});```
*
* @method transformPoint
* @param {Number} x
* @param {Number} y
* @param {Number} [z]
* @return {Object} A regular object containing `x`, `y`, `z` properties.
*/
NAN_METHOD(CoordinateTransformation::transformPoint) {
Nan::HandleScope scope;
CoordinateTransformation *transform = Nan::ObjectWrap::Unwrap<CoordinateTransformation>(info.This());
double x, y, z = 0;
if (info.Length() == 1 && info[0]->IsObject()) {
Local<Object> obj = info[0].As<Object>();
Local<Value> arg_x = Nan::Get(obj, Nan::New("x").ToLocalChecked()).ToLocalChecked();
Local<Value> arg_y = Nan::Get(obj, Nan::New("y").ToLocalChecked()).ToLocalChecked();
Local<Value> arg_z = Nan::Get(obj, Nan::New("z").ToLocalChecked()).ToLocalChecked();
if (!arg_x->IsNumber() || !arg_y->IsNumber()) {
Nan::ThrowError("point must contain numerical properties x and y");
return;
}
x = static_cast<double>(Nan::To<double>(arg_x).ToChecked());
y = static_cast<double>(Nan::To<double>(arg_y).ToChecked());
if (arg_z->IsNumber()) { z = static_cast<double>(Nan::To<double>(arg_z).ToChecked()); }
} else {
NODE_ARG_DOUBLE(0, "x", x);
NODE_ARG_DOUBLE(1, "y", y);
NODE_ARG_DOUBLE_OPT(2, "z", z);
}
if (!transform->this_->Transform(1, &x, &y, &z)) {
Nan::ThrowError("Error transforming point");
return;
}
Local<Object> result = Nan::New<Object>();
Nan::Set(result, Nan::New("x").ToLocalChecked(), Nan::New<Number>(x));
Nan::Set(result, Nan::New("y").ToLocalChecked(), Nan::New<Number>(y));
Nan::Set(result, Nan::New("z").ToLocalChecked(), Nan::New<Number>(z));
info.GetReturnValue().Set(result);
}
} // namespace node_gdal