Show:

#include "gdal_common.hpp"
#include "gdal_layer.hpp"
#include "gdal_feature.hpp"
#include "gdal_feature_defn.hpp"
#include "gdal_field_defn.hpp"
#include "gdal_spatial_reference.hpp"
#include "gdal_dataset.hpp"
#include "gdal_geometry.hpp"
#include "collections/layer_features.hpp"
#include "collections/layer_fields.hpp"

#include <stdlib.h>
#include <sstream>

namespace node_gdal {

Nan::Persistent<FunctionTemplate> Layer::constructor;
ObjectCache<OGRLayer, Layer> Layer::cache;

void Layer::Initialize(Local<Object> target)
{
	Nan::HandleScope scope;

	Local<FunctionTemplate> lcons = Nan::New<FunctionTemplate>(Layer::New);
	lcons->InstanceTemplate()->SetInternalFieldCount(1);
	lcons->SetClassName(Nan::New("Layer").ToLocalChecked());

	Nan::SetPrototypeMethod(lcons, "toString", toString);
	Nan::SetPrototypeMethod(lcons, "getExtent", getExtent);
	Nan::SetPrototypeMethod(lcons, "setAttributeFilter", setAttributeFilter);
	Nan::SetPrototypeMethod(lcons, "setSpatialFilter", setSpatialFilter);
	Nan::SetPrototypeMethod(lcons, "getSpatialFilter", getSpatialFilter);
	Nan::SetPrototypeMethod(lcons, "testCapability", testCapability);
	Nan::SetPrototypeMethod(lcons, "flush", syncToDisk);

	ATTR_DONT_ENUM(lcons, "ds", dsGetter, READ_ONLY_SETTER);
	ATTR_DONT_ENUM(lcons, "_uid", uidGetter, READ_ONLY_SETTER);
	ATTR(lcons, "srs", srsGetter, READ_ONLY_SETTER);
	ATTR(lcons, "features", featuresGetter, READ_ONLY_SETTER);
	ATTR(lcons, "fields", fieldsGetter, READ_ONLY_SETTER);
	ATTR(lcons, "name", nameGetter, READ_ONLY_SETTER);
	ATTR(lcons, "geomType", geomTypeGetter, READ_ONLY_SETTER);
	ATTR(lcons, "geomColumn", geomColumnGetter, READ_ONLY_SETTER);
	ATTR(lcons, "fidColumn", fidColumnGetter, READ_ONLY_SETTER);

	Nan::Set(target, Nan::New("Layer").ToLocalChecked(), Nan::GetFunction(lcons).ToLocalChecked());

	constructor.Reset(lcons);
}

Layer::Layer(OGRLayer *layer)
	: Nan::ObjectWrap(),
	  uid(0),
	  this_(layer),
	  parent_ds(0)
{
	LOG("Created layer [%p]", layer);
}

Layer::Layer()
	: Nan::ObjectWrap(),
	  uid(0),
	  this_(0),
	  parent_ds(0)
{
}

Layer::~Layer()
{
	dispose();
}

void Layer::dispose()
{
	if (this_) {

		LOG("Disposing layer [%p]", this_);

		ptr_manager.dispose(uid);

		LOG("Disposed layer [%p]", this_);
		this_ = NULL;
	}
};

/**
 * A representation of a layer of simple vector features, with access methods.
 *
 * @class gdal.Layer
 */
NAN_METHOD(Layer::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();
		Layer *f = static_cast<Layer *>(ptr);
		f->Wrap(info.This());

		Local<Value> features = LayerFeatures::New(info.This());
		Nan::SetPrivate(info.This(), Nan::New("features_").ToLocalChecked(), features);

		Local<Value> fields = LayerFields::New(info.This());
		Nan::SetPrivate(info.This(), Nan::New("fields_").ToLocalChecked(), fields);

		info.GetReturnValue().Set(info.This());
		return;
	} else {
		Nan::ThrowError("Cannot create layer directly. Create with dataset instead.");
		return;
	}

	info.GetReturnValue().Set(info.This());
}

#if GDAL_VERSION_MAJOR >= 2
Local<Value> Layer::New(OGRLayer *raw, GDALDataset *raw_parent)
#else
Local<Value> Layer::New(OGRLayer *raw, OGRDataSource *raw_parent)
#endif
{
	Nan::EscapableHandleScope scope;
	return scope.Escape(Layer::New(raw, raw_parent, false));
}

#if GDAL_VERSION_MAJOR >= 2
Local<Value> Layer::New(OGRLayer *raw, GDALDataset *raw_parent, bool result_set)
#else
Local<Value> Layer::New(OGRLayer *raw, OGRDataSource *raw_parent, bool result_set)
#endif
{
	Nan::EscapableHandleScope scope;

	if (!raw) {
		return scope.Escape(Nan::Null());
	}
	if (cache.has(raw)) {
		return scope.Escape(cache.get(raw));
	}

	Layer *wrapped = new Layer(raw);

	Local<Value> ext = Nan::New<External>(wrapped);
	Local<Object> obj = Nan::NewInstance(Nan::GetFunction(Nan::New(Layer::constructor)).ToLocalChecked(), 1, &ext).ToLocalChecked();

	cache.add(raw, obj);

	//add reference to datasource so datasource doesnt get GC'ed while layer is alive
	Local<Object> ds;
	#if GDAL_VERSION_MAJOR >= 2
		if (Dataset::dataset_cache.has(raw_parent)) {
			ds = Dataset::dataset_cache.get(raw_parent);
		}
	#else
		if (Dataset::datasource_cache.has(raw_parent)) {
			ds = Dataset::datasource_cache.get(raw_parent);
		}
	#endif
		else {
			LOG("Layer's parent dataset disappeared from cache (layer = %p, dataset = %p)", raw, raw_parent);
			Nan::ThrowError("Layer's parent dataset disappeared from cache");
			return scope.Escape(Nan::Undefined());
			//ds = Dataset::New(raw_parent); //should never happen
		}

	long parent_uid = Nan::ObjectWrap::Unwrap<Dataset>(ds)->uid;
	
	wrapped->uid = ptr_manager.add(raw, parent_uid, result_set);
	wrapped->parent_ds = raw_parent;
	Nan::SetPrivate(obj, Nan::New("ds_").ToLocalChecked(), ds);

	return scope.Escape(obj);
}

NAN_METHOD(Layer::toString)
{
	Nan::HandleScope scope;

	Layer *layer = Nan::ObjectWrap::Unwrap<Layer>(info.This());
	if (!layer->this_) {
		info.GetReturnValue().Set(Nan::New("Null layer").ToLocalChecked());
		return;
	}

	std::ostringstream ss;
	ss << "Layer (" << layer->this_->GetName() << ")";

	info.GetReturnValue().Set(SafeString::New(ss.str().c_str()));
}

/**
 * Flush pending changes to disk.
 *
 * @throws Error
 * @method flush
 */
NODE_WRAPPED_METHOD_WITH_OGRERR_RESULT(Layer, syncToDisk, SyncToDisk);

/**
 * Determines if the dataset supports the indicated operation.
 *
 * @method testCapability
 * @param {string} capability (see {{#crossLink "Constants (OLC)"}}capability list{{/crossLink}})
 * @return {Boolean}
 */
NODE_WRAPPED_METHOD_WITH_RESULT_1_STRING_PARAM(Layer, testCapability, Boolean, TestCapability, "capability");

/**
 * Fetch the extent of this layer.
 *
 * @throws Error
 * @method getExtent
 * @param {Boolean} [force=true]
 * @return {gdal.Envelope} Bounding envelope
 */
NAN_METHOD(Layer::getExtent)
{
	Nan::HandleScope scope;

	Layer *layer = Nan::ObjectWrap::Unwrap<Layer>(info.This());
	if (!layer->isAlive()) {
		Nan::ThrowError("Layer object has already been destroyed");
		return;
	}

	int force = 1;
	NODE_ARG_BOOL_OPT(0, "force", force);

	OGREnvelope *envelope = new OGREnvelope();
	OGRErr err = layer->this_->GetExtent(envelope, force);
	if(err) {
		Nan::ThrowError("Can't get layer extent without computing it");
		return;
	}

	Local<Object> obj = Nan::New<Object>();
	Nan::Set(obj, Nan::New("minX").ToLocalChecked(), Nan::New<Number>(envelope->MinX));
	Nan::Set(obj, Nan::New("maxX").ToLocalChecked(), Nan::New<Number>(envelope->MaxX));
	Nan::Set(obj, Nan::New("minY").ToLocalChecked(), Nan::New<Number>(envelope->MinY));
	Nan::Set(obj, Nan::New("maxY").ToLocalChecked(), Nan::New<Number>(envelope->MaxY));

	delete envelope;

	info.GetReturnValue().Set(obj);
}

/**
 * This method returns the current spatial filter for this layer.
 *
 * @throws Error
 * @method getSpatialFilter
 * @return {gdal.Geometry}
 */
NAN_METHOD(Layer::getSpatialFilter)
{
	Nan::HandleScope scope;

	Layer *layer = Nan::ObjectWrap::Unwrap<Layer>(info.This());
	if (!layer->isAlive()) {
		Nan::ThrowError("Layer object has already been destroyed");
		return;
	}

	info.GetReturnValue().Set(Geometry::New(layer->this_->GetSpatialFilter(), false));
}

/**
 * This method sets the geometry to be used as a spatial filter when fetching
 * features via the `layer.features.next()` method. Only features that geometrically
 * intersect the filter geometry will be returned.
 *
 * Alernatively you can pass it envelope bounds as individual arguments.
 *
 * @example
 * ```
 * layer.setSpatialFilter(geometry);
 * layer.setSpatialFilter(minX, minY, maxX, maxY);```
 *
 * @throws Error
 * @method setSpatialFilter
 * @param {gdal.Geometry} filter
 */
NAN_METHOD(Layer::setSpatialFilter)
{
	Nan::HandleScope scope;

	Layer *layer = Nan::ObjectWrap::Unwrap<Layer>(info.This());
	if (!layer->isAlive()) {
		Nan::ThrowError("Layer object has already been destroyed");
		return;
	}

	if(info.Length() == 1) {
		Geometry *filter = NULL;
		NODE_ARG_WRAPPED_OPT(0, "filter", Geometry, filter);

		if(filter) {
			layer->this_->SetSpatialFilter(filter->get());
		} else {
			layer->this_->SetSpatialFilter(NULL);
		}
	} else if(info.Length() == 4) {
		double minX, minY, maxX, maxY;
		NODE_ARG_DOUBLE(0, "minX", minX);
		NODE_ARG_DOUBLE(1, "minY", minY);
		NODE_ARG_DOUBLE(2, "maxX", maxX);
		NODE_ARG_DOUBLE(3, "maxY", maxY);

		layer->this_->SetSpatialFilterRect(minX, minY, maxX, maxY);
	} else {
		Nan::ThrowError("Invalid number of arguments");
		return;
	}

	return;
}

/**
 * Sets the attribute query string to be used when fetching features via the
 * `layer.features.next()` method. Only features for which the query evaluates as
 * `true` will be returned.
 *
 * The query string should be in the format of an SQL WHERE clause. For instance
 * "population > 1000000 and population < 5000000" where `population` is an
 * attribute in the layer. The query format is normally a restricted form of
 * SQL WHERE clause as described in the "WHERE" section of the [OGR SQL
 * tutorial](http://www.gdal.org/ogr_sql.html). In some cases (RDBMS backed
 * drivers) the native capabilities of the database may be used to interprete
 * the WHERE clause in which case the capabilities will be broader than those
 * of OGR SQL.
 *
 * @example
 * ```
 * layer.setAttributeFilter('population > 1000000 and population < 5000000');```
 *
 * @throws Error
 * @method setAttributeFilter
 * @param {String} filter
 */
NAN_METHOD(Layer::setAttributeFilter)
{
	Nan::HandleScope scope;

	Layer *layer = Nan::ObjectWrap::Unwrap<Layer>(info.This());
	if (!layer->isAlive()) {
		Nan::ThrowError("Layer object has already been destroyed");
		return;
	}

	std::string filter = "";
	NODE_ARG_OPT_STR(0, "filter", filter);

	OGRErr err;
	if(filter.empty()){
		err = layer->this_->SetAttributeFilter(NULL);
	} else {
		err = layer->this_->SetAttributeFilter(filter.c_str());
	}

	if (err) {
		NODE_THROW_OGRERR(err);
		return;
	}

	return;
}

/*
NAN_METHOD(Layer::getLayerDefn)
{
	Nan::HandleScope scope;

	Layer *layer = Nan::ObjectWrap::Unwrap<Layer>(info.This());

	if (!layer->isAlive()) {
		Nan::ThrowError("Layer object already destroyed");
		return;
	}

	info.GetReturnValue().Set(FeatureDefn::New(layer->this_->GetLayerDefn(), false));
}*/

/**
 * @readOnly
 * @attribute ds
 * @type {gdal.Dataset}
 */
NAN_GETTER(Layer::dsGetter)
{
	Nan::HandleScope scope;
	info.GetReturnValue().Set(Nan::GetPrivate(info.This(), Nan::New("ds_").ToLocalChecked()).ToLocalChecked());
}

/**
 * @readOnly
 * @attribute srs
 * @type {gdal.SpatialReference}
 */
NAN_GETTER(Layer::srsGetter)
{
	Nan::HandleScope scope;
	Layer *layer = Nan::ObjectWrap::Unwrap<Layer>(info.This());
	if (!layer->isAlive()) {
		Nan::ThrowError("Layer object has already been destroyed");
		return;
	}
	info.GetReturnValue().Set(SpatialReference::New(layer->this_->GetSpatialRef(), false));
}

/**
 * @readOnly
 * @attribute name
 * @type {String}
 */
NAN_GETTER(Layer::nameGetter)
{
	Nan::HandleScope scope;
	Layer *layer = Nan::ObjectWrap::Unwrap<Layer>(info.This());
	if (!layer->isAlive()) {
		Nan::ThrowError("Layer object has already been destroyed");
		return;
	}
	info.GetReturnValue().Set(SafeString::New(layer->this_->GetName()));
}

/**
 * @readOnly
 * @attribute geomColumn
 * @type {String}
 */
NAN_GETTER(Layer::geomColumnGetter)
{
	Nan::HandleScope scope;
	Layer *layer = Nan::ObjectWrap::Unwrap<Layer>(info.This());
	if (!layer->isAlive()) {
		Nan::ThrowError("Layer object has already been destroyed");
		return;
	}
	info.GetReturnValue().Set(SafeString::New(layer->this_->GetGeometryColumn()));
}

/**
 * @readOnly
 * @attribute fidColumn
 * @type {String}
 */
NAN_GETTER(Layer::fidColumnGetter)
{
	Nan::HandleScope scope;
	Layer *layer = Nan::ObjectWrap::Unwrap<Layer>(info.This());
	if (!layer->isAlive()) {
		Nan::ThrowError("Layer object has already been destroyed");
		return;
	}
	info.GetReturnValue().Set(SafeString::New(layer->this_->GetFIDColumn()));
}

/**
 * @readOnly
 * @attribute geomType
 * @type {Integer} (see {{#crossLink "Constants (wkb)"}}geometry types{{/crossLink}})
 */
NAN_GETTER(Layer::geomTypeGetter)
{
	Nan::HandleScope scope;
	Layer *layer = Nan::ObjectWrap::Unwrap<Layer>(info.This());
	if (!layer->isAlive()) {
		Nan::ThrowError("Layer object has already been destroyed");
		return;
	}
	info.GetReturnValue().Set(Nan::New<Integer>(layer->this_->GetGeomType()));
}

/**
 * @readOnly
 * @attribute features
 * @type {gdal.LayerFeatures}
 */
NAN_GETTER(Layer::featuresGetter)
{
	Nan::HandleScope scope;
	info.GetReturnValue().Set(Nan::GetPrivate(info.This(), Nan::New("features_").ToLocalChecked()).ToLocalChecked());
}

/**
 * @readOnly
 * @attribute fields
 * @type {gdal.LayerFields}
 */
NAN_GETTER(Layer::fieldsGetter)
{
	Nan::HandleScope scope;
	info.GetReturnValue().Set(Nan::GetPrivate(info.This(), Nan::New("fields_").ToLocalChecked()).ToLocalChecked());
}

NAN_GETTER(Layer::uidGetter)
{
	Nan::HandleScope scope;
	Layer *layer = Nan::ObjectWrap::Unwrap<Layer>(info.This());
	info.GetReturnValue().Set(Nan::New((int)layer->uid));
}

} // namespace node_gdal