Show:

#include "gdal_common.hpp"
#include "gdal_geometry.hpp"
#include "gdal_linestring.hpp"
#include "gdal_point.hpp"
#include "collections/linestring_points.hpp"

#include <stdlib.h>

namespace node_gdal {

Nan::Persistent<FunctionTemplate> LineString::constructor;

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

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

	Nan::SetPrototypeMethod(lcons, "toString", toString);
	Nan::SetPrototypeMethod(lcons, "getLength", getLength);
	Nan::SetPrototypeMethod(lcons, "value", value);
	Nan::SetPrototypeMethod(lcons, "addSubLineString", addSubLineString);

	ATTR(lcons, "points", pointsGetter, READ_ONLY_SETTER);

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

	constructor.Reset(lcons);
}

LineString::LineString(OGRLineString *geom)
	: Nan::ObjectWrap(),
	  this_(geom),
	  owned_(true),
	  size_(0)
{
	LOG("Created LineString [%p]", geom);
}

LineString::LineString()
	: Nan::ObjectWrap(),
	  this_(NULL),
	  owned_(true),
	  size_(0)
{
}

LineString::~LineString()
{
	if(this_) {
		LOG("Disposing LineString [%p] (%s)", this_, owned_ ? "owned" : "unowned");
		if (owned_) {
			OGRGeometryFactory::destroyGeometry(this_);
			Nan::AdjustExternalMemory(-size_);
		}
		LOG("Disposed LineString [%p]", this_);
		this_ = NULL;
	}
}

/**
 * Concrete representation of a multi-vertex line.
 *
 * @example
 * ```
 * var lineString = new gdal.LineString();
 * lineString.points.add(new gdal.Point(0,0));
 * lineString.points.add(new gdal.Point(0,10));```
 *
 * @constructor
 * @class gdal.LineString
 * @extends gdal.Geometry
 */
NAN_METHOD(LineString::New)
{
	Nan::HandleScope scope;
	LineString *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<LineString *>(ptr);

	} else {
		if (info.Length() != 0) {
			Nan::ThrowError("LineString constructor doesn't take any arguments");
			return;
		}
		f = new LineString(new OGRLineString());
	}

	Local<Value> points = LineStringPoints::New(info.This());
	Nan::SetPrivate(info.This(), Nan::New("points_").ToLocalChecked(), points);

	f->Wrap(info.This());
	info.GetReturnValue().Set(info.This());
}

Local<Value> LineString::New(OGRLineString *geom)
{
	Nan::EscapableHandleScope scope;
	return scope.Escape(LineString::New(geom, true));
}

Local<Value> LineString::New(OGRLineString *geom, bool owned)
{
	Nan::EscapableHandleScope scope;

	if (!geom) {
		return scope.Escape(Nan::Null());
	}

	//make a copy of geometry owned by a feature
	// + no need to track when a feature is destroyed
	// + no need to throw errors when a method trys to modify an owned read-only geometry
	// - is slower

	if (!owned) {
		geom = static_cast<OGRLineString*>(geom->clone());
	};

	LineString *wrapped = new LineString(geom);
	wrapped->owned_ = true;

	UPDATE_AMOUNT_OF_GEOMETRY_MEMORY(wrapped);

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

	return scope.Escape(obj);
}

NAN_METHOD(LineString::toString)
{
	Nan::HandleScope scope;
	info.GetReturnValue().Set(Nan::New("LineString").ToLocalChecked());
}

/**
 * Computes the length of the line string.
 *
 * @method getLength
 * @return Number
 */
NODE_WRAPPED_METHOD_WITH_RESULT(LineString, getLength, Number, get_Length);

/**
 * Returns the point at the specified distance along the line string.
 *
 * @method value
 * @param {Number} distance
 * @return {gdal.Point}
 */
NAN_METHOD(LineString::value)
{
	Nan::HandleScope scope;

	LineString *geom = Nan::ObjectWrap::Unwrap<LineString>(info.This());

	OGRPoint *pt = new OGRPoint();
	double dist;

	NODE_ARG_DOUBLE(0, "distance", dist);

	geom->this_->Value(dist, pt);

	info.GetReturnValue().Set(Point::New(pt));
}

/**
 * Add a segment of another linestring to this one.
 * 
 * Adds the request range of vertices to the end of this line string in an efficient manner. If the start index is larger than the end index then the vertices will be reversed as they are copied.
 *
 * @method addSubLineString
 * @param {gdal.LineString} line the other linestring
 * @param {int} [start=0] the first vertex to copy, defaults to 0 to start with the first vertex in the other linestring
 * @param {int} [end=-1] the last vertex to copy, defaults to -1 indicating the last vertex of the other linestring
 * @return {void}
 */
NAN_METHOD(LineString::addSubLineString)
{
	Nan::HandleScope scope;

	LineString *geom = Nan::ObjectWrap::Unwrap<LineString>(info.This());
	LineString *other;
	int start = 0;
	int end = -1;
	
	NODE_ARG_WRAPPED(0, "line", LineString, other);
	NODE_ARG_INT_OPT(1, "start", start);
	NODE_ARG_INT_OPT(2, "end", end);

	int n = other->get()->getNumPoints();

	if(start < 0 || end < -1 || start >= n || end >= n) {
		Nan::ThrowRangeError("Invalid start or end index for linestring");
		return;
	}

	geom->this_->addSubLineString(other->get(), start, end);

	UPDATE_AMOUNT_OF_GEOMETRY_MEMORY(geom);

	return;
}

/**
 * Points that make up the line string.
 *
 * @attribute points
 * @type {gdal.LineStringPoints}
 */
NAN_GETTER(LineString::pointsGetter)
{
	Nan::HandleScope scope;
	info.GetReturnValue().Set(Nan::GetPrivate(info.This(), Nan::New("points_").ToLocalChecked()).ToLocalChecked());
}

} // namespace node_gdal