Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

295 lines (250 sloc) 9.675 kB
#include "utils.hpp"
#include "mapnik_feature.hpp"
#include "mapnik_geometry.hpp"
// mapnik
#include <mapnik/unicode.hpp>
#include <mapnik/feature_factory.hpp>
#include <mapnik/value_types.hpp>
// boost
#include <boost/version.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/make_shared.hpp>
#if MAPNIK_VERSION >= 200100
#include <mapnik/json/geojson_generator.hpp>
static mapnik::json::feature_generator generator;
#endif
Persistent<FunctionTemplate> Feature::constructor;
void Feature::Initialize(Handle<Object> target) {
HandleScope scope;
constructor = Persistent<FunctionTemplate>::New(FunctionTemplate::New(Feature::New));
constructor->InstanceTemplate()->SetInternalFieldCount(1);
constructor->SetClassName(String::NewSymbol("Feature"));
NODE_SET_PROTOTYPE_METHOD(constructor, "id", id);
NODE_SET_PROTOTYPE_METHOD(constructor, "extent", extent);
NODE_SET_PROTOTYPE_METHOD(constructor, "attributes", attributes);
NODE_SET_PROTOTYPE_METHOD(constructor, "addGeometry", addGeometry);
NODE_SET_PROTOTYPE_METHOD(constructor, "addAttributes", addAttributes);
NODE_SET_PROTOTYPE_METHOD(constructor, "numGeometries", numGeometries);
NODE_SET_PROTOTYPE_METHOD(constructor, "toString", toString);
NODE_SET_PROTOTYPE_METHOD(constructor, "toJSON", toJSON);
target->Set(String::NewSymbol("Feature"),constructor->GetFunction());
}
Feature::Feature(mapnik::feature_ptr f) :
ObjectWrap(),
this_(f) {}
Feature::Feature(int id) :
ObjectWrap(),
this_() {
#if MAPNIK_VERSION >= 200100
// TODO - accept/require context object to reused
ctx_ = boost::make_shared<mapnik::context_type>();
this_ = mapnik::feature_factory::create(ctx_,id);
#else
this_ = mapnik::feature_factory::create(id);
#endif
}
Feature::~Feature()
{
}
Handle<Value> Feature::New(const Arguments& args)
{
HandleScope scope;
if (!args.IsConstructCall())
return ThrowException(String::New("Cannot call constructor as function, you need to use 'new' keyword"));
if (args[0]->IsExternal())
{
Local<External> ext = Local<External>::Cast(args[0]);
void* ptr = ext->Value();
Feature* f = static_cast<Feature*>(ptr);
f->Wrap(args.This());
return args.This();
}
// TODO - expose mapnik.Context
if (args.Length() > 1 || args.Length() < 1 || !args[0]->IsNumber()) {
return ThrowException(Exception::TypeError(
String::New("requires one argument: an integer feature id")));
}
Feature* f = new Feature(args[0]->IntegerValue());
f->Wrap(args.This());
return args.This();
}
Handle<Value> Feature::New(mapnik::feature_ptr f_ptr)
{
HandleScope scope;
Feature* f = new Feature(f_ptr);
Handle<Value> ext = External::New(f);
Handle<Object> obj = constructor->GetFunction()->NewInstance(1, &ext);
return scope.Close(obj);
}
Handle<Value> Feature::id(const Arguments& args)
{
HandleScope scope;
Feature* fp = node::ObjectWrap::Unwrap<Feature>(args.This());
return scope.Close(Number::New(fp->get()->id()));
}
Handle<Value> Feature::extent(const Arguments& args)
{
HandleScope scope;
Feature* fp = node::ObjectWrap::Unwrap<Feature>(args.This());
Local<Array> a = Array::New(4);
mapnik::box2d<double> const& e = fp->get()->envelope();
a->Set(0, Number::New(e.minx()));
a->Set(1, Number::New(e.miny()));
a->Set(2, Number::New(e.maxx()));
a->Set(3, Number::New(e.maxy()));
return scope.Close(a);
}
Handle<Value> Feature::attributes(const Arguments& args)
{
HandleScope scope;
Feature* fp = node::ObjectWrap::Unwrap<Feature>(args.This());
Local<Object> feat = Object::New();
#if MAPNIK_VERSION >= 200100
mapnik::feature_ptr feature = fp->get();
mapnik::feature_impl::iterator itr = feature->begin();
mapnik::feature_impl::iterator end = feature->end();
for ( ;itr!=end; ++itr)
{
node_mapnik::params_to_object serializer( feat , boost::get<0>(*itr));
boost::apply_visitor( serializer, boost::get<1>(*itr).base() );
}
#else
std::map<std::string,mapnik::value> const& fprops = fp->get()->props();
std::map<std::string,mapnik::value>::const_iterator it = fprops.begin();
std::map<std::string,mapnik::value>::const_iterator end = fprops.end();
for (; it != end; ++it)
{
node_mapnik::params_to_object serializer( feat , it->first);
boost::apply_visitor( serializer, it->second.base() );
}
#endif
return scope.Close(feat);
}
Handle<Value> Feature::numGeometries(const Arguments& args)
{
HandleScope scope;
Feature* fp = node::ObjectWrap::Unwrap<Feature>(args.This());
return scope.Close(Integer::New(fp->get()->num_geometries()));
}
// TODO void?
Handle<Value> Feature::addGeometry(const Arguments& args)
{
HandleScope scope;
Feature* fp = node::ObjectWrap::Unwrap<Feature>(args.This());
if (args.Length() >= 1 ) {
Local<Value> value = args[0];
if (value->IsNull() || value->IsUndefined()) {
return ThrowException(Exception::TypeError(String::New("mapnik.Geometry instance expected")));
} else {
Local<Object> obj = value->ToObject();
if (Geometry::constructor->HasInstance(obj)) {
Geometry* g = node::ObjectWrap::Unwrap<Geometry>(obj);
try
{
std::auto_ptr<mapnik::geometry_type> geom_ptr = g->get();
if (geom_ptr.get()) {
fp->get()->add_geometry(geom_ptr.get());
geom_ptr.release();
} else {
return ThrowException(Exception::Error(
String::New("empty geometry!")));
}
}
catch (std::exception const& ex )
{
return ThrowException(Exception::Error(
String::New(ex.what())));
}
}
}
}
return Undefined();
}
Handle<Value> Feature::addAttributes(const Arguments& args)
{
HandleScope scope;
Feature* fp = node::ObjectWrap::Unwrap<Feature>(args.This());
if (args.Length() > 0 ) {
Local<Value> value = args[0];
if (value->IsNull() || value->IsUndefined()) {
return ThrowException(Exception::TypeError(String::New("object expected")));
} else {
Local<Object> attr = value->ToObject();
try
{
Local<Array> names = attr->GetPropertyNames();
unsigned int i = 0;
unsigned int a_length = names->Length();
boost::scoped_ptr<mapnik::transcoder> tr(new mapnik::transcoder("utf8"));
while (i < a_length) {
Local<Value> name = names->Get(i)->ToString();
Local<Value> value = attr->Get(name);
if (value->IsString()) {
mapnik::value_unicode_string ustr = tr->transcode(TOSTR(value));
#if MAPNIK_VERSION >= 200100
fp->get()->put_new(TOSTR(name),ustr);
#else
boost::put(*fp->get(),TOSTR(name),ustr);
#endif
} else if (value->IsNumber()) {
double num = value->NumberValue();
// todo - round
if (num == value->IntegerValue()) {
#if MAPNIK_VERSION >= 200100
fp->get()->put_new(TOSTR(name),static_cast<node_mapnik::value_integer>(value->IntegerValue()));
#else
boost::put(*fp->get(),TOSTR(name),static_cast<int>(value->IntegerValue()));
#endif
} else {
double dub_val = value->NumberValue();
#if MAPNIK_VERSION >= 200100
fp->get()->put_new(TOSTR(name),dub_val);
#else
boost::put(*fp->get(),TOSTR(name),dub_val);
#endif
}
} else if (value->IsNull()) {
#if MAPNIK_VERSION >= 200100
fp->get()->put_new(TOSTR(name),mapnik::value_null());
#else
boost::put(*fp->get(),TOSTR(name),mapnik::value_null());
#endif
} else {
std::clog << "unhandled type for property: " << TOSTR(name) << "\n";
}
i++;
}
}
catch (std::exception const& ex )
{
return ThrowException(Exception::Error(
String::New(ex.what())));
}
}
}
return Undefined();
}
Handle<Value> Feature::toString(const Arguments& args)
{
HandleScope scope;
Feature* fp = node::ObjectWrap::Unwrap<Feature>(args.This());
return scope.Close(String::New(fp->get()->to_string().c_str()));
}
Handle<Value> Feature::toJSON(const Arguments& args)
{
HandleScope scope;
std::string json;
#if BOOST_VERSION >= 104700 && MAPNIK_VERSION >= 200100
Feature* fp = node::ObjectWrap::Unwrap<Feature>(args.This());
// TODO - create once?
if (!generator.generate(json,*(fp->get())))
{
return ThrowException(Exception::Error(
String::New("Failed to generate GeoJSON")));
}
#else
return ThrowException(Exception::Error(
String::New("GeoJSON output requires at least boost 1.47 and mapnik 2.1.x")));
#endif
return scope.Close(String::New(json.c_str()));
}
Jump to Line
Something went wrong with that request. Please try again.