#include "gdal_feature.hpp"
#include "collections/feature_fields.hpp"
#include "gdal_common.hpp"
#include "gdal_feature_defn.hpp"
#include "gdal_field_defn.hpp"
#include "gdal_geometry.hpp"
#include "gdal_layer.hpp"
namespace node_gdal {
Nan::Persistent<FunctionTemplate> Feature::constructor;
void Feature::Initialize(Local<Object> target) {
Nan::HandleScope scope;
Local<FunctionTemplate> lcons = Nan::New<FunctionTemplate>(Feature::New);
lcons->InstanceTemplate()->SetInternalFieldCount(1);
lcons->SetClassName(Nan::New("Feature").ToLocalChecked());
Nan::SetPrototypeMethod(lcons, "toString", toString);
Nan::SetPrototypeMethod(lcons, "getGeometry", getGeometry);
// Nan::SetPrototypeMethod(lcons, "setGeometryDirectly", setGeometryDirectly);
Nan::SetPrototypeMethod(lcons, "setGeometry", setGeometry);
// Nan::SetPrototypeMethod(lcons, "stealGeometry", stealGeometry);
Nan::SetPrototypeMethod(lcons, "clone", clone);
// Nan::SetPrototypeMethod(lcons, "equals", equals);
// Nan::SetPrototypeMethod(lcons, "getFieldDefn", getFieldDefn); (use
// defn.fields.get() instead)
Nan::SetPrototypeMethod(lcons, "setFrom", setFrom);
// Note: We should let node GC handle destroying features when they arent
// being used
// TODO: Give node more info on the amount of memory a feature is using
// Nan::AdjustExternalMemory()
// Nan::SetPrototypeMethod(lcons, "destroy", destroy);
ATTR(lcons, "fields", fieldsGetter, READ_ONLY_SETTER);
ATTR(lcons, "defn", defnGetter, READ_ONLY_SETTER);
ATTR(lcons, "fid", fidGetter, fidSetter);
Nan::Set(target, Nan::New("Feature").ToLocalChecked(), Nan::GetFunction(lcons).ToLocalChecked());
constructor.Reset(lcons);
}
Feature::Feature(OGRFeature *feature) : Nan::ObjectWrap(), this_(feature), owned_(true) {
LOG("Created Feature[%p]", feature);
}
Feature::Feature() : Nan::ObjectWrap(), this_(0), owned_(true) {
}
Feature::~Feature() {
dispose();
}
void Feature::dispose() {
if (this_) {
LOG("Disposing Feature [%p] (%s)", this_, owned_ ? "owned" : "unowned");
if (owned_) OGRFeature::DestroyFeature(this_);
LOG("Disposed Feature [%p]", this_);
this_ = NULL;
}
}
/**
* A simple feature, including geometry and attributes. Its fields and geometry
* type is defined by the given definition.
*
* ```
* //create layer and specify geometry type
* var layer = dataset.layers.create('mylayer', null, gdal.Point);
*
* //setup fields for the given layer
* layer.fields.add(new gdal.FieldDefn('elevation', gdal.OFTInteger));
* layer.fields.add(new gdal.FieldDefn('name', gdal.OFTString));
*
* //create feature using layer definition and then add it to the layer
* var feature = new gdal.Feature(layer);
* feature.fields.set('elevation', 13775);
* feature.fields.set('name', 'Grand Teton');
* feature.setGeometry(new gdal.Point(43.741208, -110.802414));
* layer.features.add(feature);```
*
* @constructor
* @class gdal.Feature
* @param {gdal.Layer|gdal.FeatureDefn} definition
*/
NAN_METHOD(Feature::New) {
Nan::HandleScope scope;
Feature *f;
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<Feature *>(ptr);
} else {
if (info.Length() < 1) {
Nan::ThrowError("Constructor expects Layer or FeatureDefn object");
return;
}
OGRFeatureDefn *def;
if (IS_WRAPPED(info[0], Layer)) {
Layer *layer = Nan::ObjectWrap::Unwrap<Layer>(info[0].As<Object>());
if (!layer->isAlive()) {
Nan::ThrowError("Layer object already destroyed");
return;
}
def = layer->get()->GetLayerDefn();
} else if (IS_WRAPPED(info[0], FeatureDefn)) {
FeatureDefn *feature_def = Nan::ObjectWrap::Unwrap<FeatureDefn>(info[0].As<Object>());
if (!feature_def->isAlive()) {
Nan::ThrowError("FeatureDefn object already destroyed");
return;
}
def = feature_def->get();
} else {
Nan::ThrowError("Constructor expects Layer or FeatureDefn object");
return;
}
OGRFeature *ogr_f = new OGRFeature(def);
f = new Feature(ogr_f);
}
Local<Value> fields = FeatureFields::New(info.This());
Nan::SetPrivate(info.This(), Nan::New("fields_").ToLocalChecked(), fields);
f->Wrap(info.This());
info.GetReturnValue().Set(info.This());
}
Local<Value> Feature::New(OGRFeature *feature) {
Nan::EscapableHandleScope scope;
return scope.Escape(Feature::New(feature, true));
}
Local<Value> Feature::New(OGRFeature *feature, bool owned) {
Nan::EscapableHandleScope scope;
if (!feature) { return scope.Escape(Nan::Null()); }
Feature *wrapped = new Feature(feature);
wrapped->owned_ = owned;
Local<Value> ext = Nan::New<External>(wrapped);
Local<Object> obj =
Nan::NewInstance(Nan::GetFunction(Nan::New(Feature::constructor)).ToLocalChecked(), 1, &ext).ToLocalChecked();
return scope.Escape(obj);
}
NAN_METHOD(Feature::toString) {
Nan::HandleScope scope;
info.GetReturnValue().Set(Nan::New("Feature").ToLocalChecked());
}
/**
* Returns the geometry of the feature.
*
* @method getGeometry
* @return {gdal.Geometry}
*/
NAN_METHOD(Feature::getGeometry) {
Nan::HandleScope scope;
Feature *feature = Nan::ObjectWrap::Unwrap<Feature>(info.This());
if (!feature->isAlive()) {
Nan::ThrowError("Feature object already destroyed");
return;
}
OGRGeometry *geom = feature->this_->GetGeometryRef();
if (!geom) {
info.GetReturnValue().Set(Nan::Null());
return;
}
info.GetReturnValue().Set(Geometry::New(geom, false));
}
/**
* Returns the definition of a particular field at an index.
*
* @method getFieldDefn
* @param {Integer} index Field index (0-based)
* @return {gdal.FieldDefn}
*/
NAN_METHOD(Feature::getFieldDefn) {
Nan::HandleScope scope;
int field_index;
NODE_ARG_INT(0, "field index", field_index);
Feature *feature = Nan::ObjectWrap::Unwrap<Feature>(info.This());
if (!feature->isAlive()) {
Nan::ThrowError("Feature object already destroyed");
return;
}
if (field_index < 0 || field_index >= feature->this_->GetFieldCount()) {
Nan::ThrowError("Invalid field index");
return;
}
info.GetReturnValue().Set(FieldDefn::New(feature->this_->GetFieldDefnRef(field_index), false));
}
// NODE_WRAPPED_METHOD_WITH_RESULT(Feature, stealGeometry, Geometry,
// StealGeometry);
/**
* Sets the feature's geometry.
*
* @throws Error
* @method setGeometry
* @param {gdal.Geometry} geometry
*/
NAN_METHOD(Feature::setGeometry) {
Nan::HandleScope scope;
Geometry *geom = NULL;
NODE_ARG_WRAPPED_OPT(0, "geometry", Geometry, geom);
Feature *feature = Nan::ObjectWrap::Unwrap<Feature>(info.This());
if (!feature->isAlive()) {
Nan::ThrowError("Feature object already destroyed");
return;
}
OGRErr err = feature->this_->SetGeometry(geom ? geom->get() : NULL);
if (err) { NODE_THROW_OGRERR(err); }
return;
}
/**
* Determines if the features are the same.
*
* @method equals
* @param {gdal.Feature} feature
* @return {Boolean} `true` if the features are the same, `false` if different
*/
NODE_WRAPPED_METHOD_WITH_RESULT_1_WRAPPED_PARAM(Feature, equals, Boolean, Equal, Feature, "feature");
/**
* Clones the feature.
*
* @method clone
* @return {gdal.Feature}
*/
NAN_METHOD(Feature::clone) {
Nan::HandleScope scope;
Feature *feature = Nan::ObjectWrap::Unwrap<Feature>(info.This());
if (!feature->isAlive()) {
Nan::ThrowError("Feature object already destroyed");
return;
}
info.GetReturnValue().Set(Feature::New(feature->this_->Clone()));
}
/**
* Releases the feature from memory.
*
* @method destroy
*/
NAN_METHOD(Feature::destroy) {
Nan::HandleScope scope;
Feature *feature = Nan::ObjectWrap::Unwrap<Feature>(info.This());
if (!feature->isAlive()) {
Nan::ThrowError("Feature object already destroyed");
return;
}
feature->dispose();
return;
}
/**
* Set one feature from another. Overwrites the contents of this feature
* from the geometry and attributes of another.
*
* @example
* ```
* var feature1 = new gdal.Feature(defn);
* var feature2 = new gdal.Feature(defn);
* feature1.setGeometry(new gdal.Point(5, 10));
* feature1.fields.set([5, 'test', 3.14]);
* feature2.setFrom(feature1);```
*
* @throws Error
* @method setFrom
* @param {gdal.Feature} feature
* @param {Array} [*index_map] Array of the indices (integers) of the feature's
* fields stored at the corresponding index of the source feature's fields. A
* value of -1 should be used to ignore the source's field. The array should not
* be `null` and be as long as the number of fields in the source feature.
* @param {Boolean} [forgiving=true] `true` if the operation should continue
* despite lacking output fields matching some of the source fields.
*/
NAN_METHOD(Feature::setFrom) {
Nan::HandleScope scope;
Feature *other_feature;
int forgiving = 1;
Local<Array> index_map;
OGRErr err = 0;
NODE_ARG_WRAPPED(0, "feature", Feature, other_feature);
Feature *feature = Nan::ObjectWrap::Unwrap<Feature>(info.This());
if (!feature->isAlive()) {
Nan::ThrowError("Feature object already destroyed");
return;
}
if (info.Length() <= 2) {
NODE_ARG_BOOL_OPT(1, "forgiving", forgiving);
err = feature->this_->SetFrom(other_feature->this_, forgiving ? TRUE : FALSE);
} else {
NODE_ARG_ARRAY(1, "index map", index_map);
NODE_ARG_BOOL_OPT(2, "forgiving", forgiving);
if (index_map->Length() < 1) {
Nan::ThrowError("index map must contain at least 1 index");
return;
}
int *index_map_ptr = new int[index_map->Length()];
for (unsigned index = 0; index < index_map->Length(); index++) {
Local<Value> field_index(Nan::Get(index_map, Nan::New<Integer>(index)).ToLocalChecked());
if (!field_index->IsUint32()) {
delete[] index_map_ptr;
Nan::ThrowError("index map must contain only integer values");
return;
}
int val = (int)Nan::To<uint32_t>(field_index).ToChecked(); // todo: validate index? perhaps ogr already
// does this and throws an error
index_map_ptr[index] = val;
}
err = feature->this_->SetFrom(other_feature->this_, index_map_ptr, forgiving ? TRUE : FALSE);
delete[] index_map_ptr;
}
if (err) {
NODE_THROW_OGRERR(err);
return;
}
return;
}
/**
* @readOnly
* @attribute fields
* @type {gdal.FeatureFields}
*/
NAN_GETTER(Feature::fieldsGetter) {
Nan::HandleScope scope;
info.GetReturnValue().Set(Nan::GetPrivate(info.This(), Nan::New("fields_").ToLocalChecked()).ToLocalChecked());
}
/**
* @attribute fid
* @type {Number}
*/
NAN_GETTER(Feature::fidGetter) {
Nan::HandleScope scope;
Feature *feature = Nan::ObjectWrap::Unwrap<Feature>(info.This());
if (!feature->isAlive()) {
Nan::ThrowError("Feature object already destroyed");
return;
}
info.GetReturnValue().Set(Nan::New<Number>(feature->this_->GetFID()));
}
/**
* @readOnly
* @attribute defn
* @type {gdal.FeatureDefn}
*/
NAN_GETTER(Feature::defnGetter) {
Nan::HandleScope scope;
Feature *feature = Nan::ObjectWrap::Unwrap<Feature>(info.This());
if (!feature->isAlive()) {
Nan::ThrowError("Feature object already destroyed");
return;
}
info.GetReturnValue().Set(FeatureDefn::New(feature->this_->GetDefnRef(), false));
}
NAN_SETTER(Feature::fidSetter) {
Nan::HandleScope scope;
Feature *feature = Nan::ObjectWrap::Unwrap<Feature>(info.This());
if (!feature->isAlive()) {
Nan::ThrowError("Feature object already destroyed");
return;
}
if (!value->IsInt32()) {
Nan::ThrowError("fid must be an integer");
return;
}
feature->this_->SetFID(Nan::To<int64_t>(value).ToChecked());
}
} // namespace node_gdal