#include "gdal_common.hpp"
#include "gdal_spatial_reference.hpp"
#include "gdal_coordinate_transformation.hpp"
#include "gdal_dataset.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