Permalink
Fetching contributors…
Cannot retrieve contributors at this time
236 lines (210 sloc) 6.66 KB
#include "utils.hpp"
#include "mapnik_feature.hpp"
#include "mapnik_geometry.hpp"
// mapnik
#include <mapnik/version.hpp>
#include <mapnik/unicode.hpp>
#include <mapnik/feature_factory.hpp>
#include <mapnik/json/feature_parser.hpp>
#include <mapnik/value/types.hpp>
#include <mapnik/util/feature_to_geojson.hpp>
Nan::Persistent<v8::FunctionTemplate> Feature::constructor;
/**
* **`mapnik.Feature`**
*
* A single geographic feature, with geometry and properties. This is
* typically derived from data by a datasource, but can be manually
* created.
*
* @class Feature
*/
void Feature::Initialize(v8::Local<v8::Object> target) {
Nan::HandleScope scope;
v8::Local<v8::FunctionTemplate> lcons = Nan::New<v8::FunctionTemplate>(Feature::New);
lcons->InstanceTemplate()->SetInternalFieldCount(1);
lcons->SetClassName(Nan::New("Feature").ToLocalChecked());
Nan::SetPrototypeMethod(lcons, "id", id);
Nan::SetPrototypeMethod(lcons, "extent", extent);
Nan::SetPrototypeMethod(lcons, "attributes", attributes);
Nan::SetPrototypeMethod(lcons, "geometry", geometry);
Nan::SetPrototypeMethod(lcons, "toJSON", toJSON);
Nan::SetMethod(lcons->GetFunction().As<v8::Object>(),
"fromJSON",
Feature::fromJSON);
target->Set(Nan::New("Feature").ToLocalChecked(),lcons->GetFunction());
constructor.Reset(lcons);
}
Feature::Feature(mapnik::feature_ptr f) :
Nan::ObjectWrap(),
this_(f) {}
Feature::Feature(int id) :
Nan::ObjectWrap(),
this_() {
// TODO - accept/require context object to reused
ctx_ = std::make_shared<mapnik::context_type>();
this_ = mapnik::feature_factory::create(ctx_,id);
}
Feature::~Feature()
{
}
NAN_METHOD(Feature::New)
{
if (!info.IsConstructCall())
{
Nan::ThrowError("Cannot call constructor as function, you need to use 'new' keyword");
return;
}
if (info[0]->IsExternal())
{
v8::Local<v8::External> ext = info[0].As<v8::External>();
void* ptr = ext->Value();
Feature* f = static_cast<Feature*>(ptr);
f->Wrap(info.This());
info.GetReturnValue().Set(info.This());
return;
}
// TODO - expose mapnik.Context
if (info.Length() > 1 || info.Length() < 1 || !info[0]->IsNumber()) {
Nan::ThrowTypeError("requires one argument: an integer feature id");
return;
}
Feature* f = new Feature(info[0]->IntegerValue());
f->Wrap(info.This());
info.GetReturnValue().Set(info.This());
}
/**
* @memberof Feature
* @static
* @name fromJSON
* @param {string} geojson string
*
* Create a feature from a GeoJSON representation.
*/
NAN_METHOD(Feature::fromJSON)
{
if (info.Length() < 1 || !info[0]->IsString()) {
Nan::ThrowTypeError("requires one argument: a string representing a GeoJSON feature");
return;
}
std::string json = TOSTR(info[0]);
try
{
mapnik::feature_ptr f(mapnik::feature_factory::create(std::make_shared<mapnik::context_type>(),1));
if (!mapnik::json::from_geojson(json,*f))
{
Nan::ThrowError("Failed to read GeoJSON");
return;
}
Feature* feat = new Feature(f);
v8::Local<v8::Value> ext = Nan::New<v8::External>(feat);
Nan::MaybeLocal<v8::Object> maybe_local = Nan::NewInstance(Nan::New(constructor)->GetFunction(), 1, &ext);
if (maybe_local.IsEmpty()) Nan::ThrowError("Could not create new Feature instance");
else info.GetReturnValue().Set(maybe_local.ToLocalChecked());
}
catch (std::exception const& ex)
{
// no way currently for any of the above code to throw,
// but we'll keep this try catch to protect against mapnik or v8 changing
/* LCOV_EXCL_START */
Nan::ThrowError(ex.what());
return;
/* LCOV_EXCL_STOP */
}
}
v8::Local<v8::Value> Feature::NewInstance(mapnik::feature_ptr f_ptr)
{
Nan::EscapableHandleScope scope;
Feature* f = new Feature(f_ptr);
v8::Local<v8::Value> ext = Nan::New<v8::External>(f);
Nan::MaybeLocal<v8::Object> maybe_local = Nan::NewInstance(Nan::New(constructor)->GetFunction(), 1, &ext);
if (maybe_local.IsEmpty()) Nan::ThrowError("Could not create new Feature instance");
return scope.Escape(maybe_local.ToLocalChecked());
}
/**
* @memberof Feature
* @name id
* @instance
* @returns {number} id the feature's internal id
*/
NAN_METHOD(Feature::id)
{
Feature* fp = Nan::ObjectWrap::Unwrap<Feature>(info.Holder());
info.GetReturnValue().Set(Nan::New<v8::Number>(fp->get()->id()));
}
/**
* Get the feature's extent
*
* @name extent
* @memberof Feature
* @instance
* @returns {Array<number>} extent [minx, miny, maxx, maxy] order feature extent.
*/
NAN_METHOD(Feature::extent)
{
Feature* fp = Nan::ObjectWrap::Unwrap<Feature>(info.Holder());
v8::Local<v8::Array> a = Nan::New<v8::Array>(4);
mapnik::box2d<double> const& e = fp->get()->envelope();
a->Set(0, Nan::New<v8::Number>(e.minx()));
a->Set(1, Nan::New<v8::Number>(e.miny()));
a->Set(2, Nan::New<v8::Number>(e.maxx()));
a->Set(3, Nan::New<v8::Number>(e.maxy()));
info.GetReturnValue().Set(a);
}
/**
* Get the feature's attributes as an object.
*
* @name attributes
* @memberof Feature
* @instance
* @returns {Object} attributes
*/
NAN_METHOD(Feature::attributes)
{
Feature* fp = Nan::ObjectWrap::Unwrap<Feature>(info.Holder());
v8::Local<v8::Object> feat = Nan::New<v8::Object>();
mapnik::feature_ptr feature = fp->get();
if (feature)
{
for (auto const& attr : *feature)
{
feat->Set(Nan::New<v8::String>(std::get<0>(attr)).ToLocalChecked(),
mapnik::util::apply_visitor(node_mapnik::value_converter(), std::get<1>(attr))
);
}
}
info.GetReturnValue().Set(feat);
}
/**
* Get the feature's attributes as a Mapnik geometry.
*
* @name geometry
* @memberof Feature
* @instance
* @returns {mapnik.Geometry} geometry
*/
NAN_METHOD(Feature::geometry)
{
Feature* fp = Nan::ObjectWrap::Unwrap<Feature>(info.Holder());
info.GetReturnValue().Set(Geometry::NewInstance(fp->get()));
}
/**
* Generate and return a GeoJSON representation of this feature
*
* @instance
* @name toJSON
* @memberof Feature
* @returns {string} geojson Feature object in stringified GeoJSON
*/
NAN_METHOD(Feature::toJSON)
{
Feature* fp = Nan::ObjectWrap::Unwrap<Feature>(info.Holder());
std::string json;
if (!mapnik::util::to_geojson(json, *(fp->get())))
{
/* LCOV_EXCL_START */
Nan::ThrowError("Failed to generate GeoJSON");
return;
/* LCOV_EXCL_STOP */
}
info.GetReturnValue().Set(Nan::New<v8::String>(json).ToLocalChecked());
}