2012-07-19 20:06:44 +02:00
|
|
|
// file plugin
|
|
|
|
#include "python_datasource.hpp"
|
|
|
|
#include "python_featureset.hpp"
|
|
|
|
|
|
|
|
// stl
|
|
|
|
#include <string>
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
// boost
|
2013-04-24 17:40:35 +02:00
|
|
|
|
2012-07-19 20:06:44 +02:00
|
|
|
#include <boost/python.hpp>
|
|
|
|
#include <boost/python/stl_iterator.hpp>
|
|
|
|
#include <boost/algorithm/string.hpp>
|
|
|
|
|
|
|
|
#include "python_utils.hpp"
|
|
|
|
|
|
|
|
using mapnik::datasource;
|
|
|
|
using mapnik::parameters;
|
|
|
|
|
|
|
|
DATASOURCE_PLUGIN(python_datasource)
|
|
|
|
|
2012-12-17 19:03:07 +01:00
|
|
|
python_datasource::python_datasource(parameters const& params)
|
2012-07-19 20:06:44 +02:00
|
|
|
: datasource(params),
|
2014-06-26 11:51:24 +02:00
|
|
|
desc_(python_datasource::name(), *params.get<std::string>("encoding","utf-8")),
|
2012-12-17 19:03:07 +01:00
|
|
|
factory_(*params.get<std::string>("factory", ""))
|
2012-07-19 20:06:44 +02:00
|
|
|
{
|
|
|
|
// extract any remaining parameters as keyword args for the factory
|
2013-04-24 17:40:35 +02:00
|
|
|
for (const mapnik::parameters::value_type& kv : params)
|
2012-07-19 20:06:44 +02:00
|
|
|
{
|
|
|
|
if((kv.first != "type") && (kv.first != "factory"))
|
|
|
|
{
|
2012-12-17 19:03:07 +01:00
|
|
|
kwargs_.insert(std::make_pair(kv.first, *params.get<std::string>(kv.first)));
|
2012-07-19 20:06:44 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-12-17 19:03:07 +01:00
|
|
|
// The following methods call into the Python interpreter and hence require, unfortunately, that the GIL be held.
|
2012-07-19 20:06:44 +02:00
|
|
|
using namespace boost;
|
|
|
|
|
2012-12-17 19:03:07 +01:00
|
|
|
if (factory_.empty())
|
|
|
|
{
|
|
|
|
throw mapnik::datasource_exception("Python: 'factory' option must be defined");
|
|
|
|
}
|
2012-07-19 20:06:44 +02:00
|
|
|
|
2012-10-20 02:05:51 +02:00
|
|
|
try
|
2012-07-19 20:06:44 +02:00
|
|
|
{
|
2012-10-20 02:05:51 +02:00
|
|
|
// split factory at ':' to parse out module and callable
|
|
|
|
std::vector<std::string> factory_split;
|
|
|
|
split(factory_split, factory_, is_any_of(":"));
|
|
|
|
if ((factory_split.size() < 1) || (factory_split.size() > 2))
|
|
|
|
{
|
|
|
|
throw mapnik::datasource_exception(
|
|
|
|
std::string("python: factory string must be of the form '[module:]callable' when parsing \"")
|
|
|
|
+ factory_ + '"');
|
|
|
|
}
|
|
|
|
// extract the module and the callable
|
2012-12-13 00:09:07 +01:00
|
|
|
boost::python::str module_name("__main__"), callable_name;
|
2012-10-20 02:05:51 +02:00
|
|
|
if (factory_split.size() == 1)
|
|
|
|
{
|
2012-12-13 00:09:07 +01:00
|
|
|
callable_name = boost::python::str(factory_split[0]);
|
2012-10-20 02:05:51 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2012-12-13 00:09:07 +01:00
|
|
|
module_name = boost::python::str(factory_split[0]);
|
|
|
|
callable_name = boost::python::str(factory_split[1]);
|
2012-10-20 02:05:51 +02:00
|
|
|
}
|
2012-07-19 20:06:44 +02:00
|
|
|
ensure_gil lock;
|
|
|
|
// import the main module from Python (in case we're embedding the
|
|
|
|
// interpreter directly) and also import the callable.
|
2012-12-13 00:09:07 +01:00
|
|
|
boost::python::object main_module = boost::python::import("__main__");
|
|
|
|
boost::python::object callable_module = boost::python::import(module_name);
|
|
|
|
boost::python::object callable = callable_module.attr(callable_name);
|
2012-07-19 20:06:44 +02:00
|
|
|
// prepare the arguments
|
2012-12-13 00:09:07 +01:00
|
|
|
boost::python::dict kwargs;
|
2014-07-07 19:23:15 +02:00
|
|
|
using kv_type = std::map<std::string, std::string>::value_type;
|
2013-04-24 17:40:35 +02:00
|
|
|
for (kv_type const& kv : kwargs_)
|
2012-07-19 20:06:44 +02:00
|
|
|
{
|
2012-12-13 00:09:07 +01:00
|
|
|
kwargs[boost::python::str(kv.first)] = boost::python::str(kv.second);
|
2012-07-19 20:06:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// get our wrapped data source
|
|
|
|
datasource_ = callable(*boost::python::make_tuple(), **kwargs);
|
|
|
|
}
|
2012-12-13 00:09:07 +01:00
|
|
|
catch ( boost::python::error_already_set )
|
2012-10-20 02:05:51 +02:00
|
|
|
{
|
|
|
|
throw mapnik::datasource_exception(extractException());
|
|
|
|
}
|
2012-12-17 19:03:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
python_datasource::~python_datasource() { }
|
|
|
|
|
|
|
|
// This name must match the plugin filename, eg 'python.input'
|
|
|
|
const char* python_datasource::name_="python";
|
|
|
|
|
|
|
|
const char* python_datasource::name()
|
|
|
|
{
|
|
|
|
return name_;
|
|
|
|
}
|
2012-07-19 20:06:44 +02:00
|
|
|
|
2012-12-17 19:03:07 +01:00
|
|
|
mapnik::layer_descriptor python_datasource::get_descriptor() const
|
|
|
|
{
|
|
|
|
return desc_;
|
2012-07-19 20:06:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
mapnik::datasource::datasource_t python_datasource::type() const
|
|
|
|
{
|
2014-07-07 19:23:15 +02:00
|
|
|
using return_type = boost::optional<mapnik::datasource::geometry_t>;
|
2012-07-19 20:06:44 +02:00
|
|
|
|
2012-10-20 02:05:51 +02:00
|
|
|
try
|
|
|
|
{
|
|
|
|
ensure_gil lock;
|
2012-12-13 00:09:07 +01:00
|
|
|
boost::python::object data_type = datasource_.attr("data_type");
|
|
|
|
long data_type_integer = boost::python::extract<long>(data_type);
|
2012-10-20 02:05:51 +02:00
|
|
|
return mapnik::datasource::datasource_t(data_type_integer);
|
|
|
|
}
|
2012-12-13 00:09:07 +01:00
|
|
|
catch ( boost::python::error_already_set )
|
2012-10-20 02:05:51 +02:00
|
|
|
{
|
|
|
|
throw mapnik::datasource_exception(extractException());
|
|
|
|
}
|
2012-07-19 20:06:44 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
mapnik::box2d<double> python_datasource::envelope() const
|
|
|
|
{
|
2013-05-30 18:18:25 +02:00
|
|
|
mapnik::box2d<double> box;
|
2012-10-20 02:05:51 +02:00
|
|
|
try
|
|
|
|
{
|
|
|
|
ensure_gil lock;
|
2013-05-30 18:18:25 +02:00
|
|
|
if (!PyObject_HasAttrString(datasource_.ptr(), "envelope"))
|
|
|
|
{
|
|
|
|
throw mapnik::datasource_exception("Python: could not access envelope property");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
boost::python::object py_envelope = datasource_.attr("envelope");
|
|
|
|
if (py_envelope.ptr() == boost::python::object().ptr())
|
|
|
|
{
|
|
|
|
throw mapnik::datasource_exception("Python: could not access envelope property");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
boost::python::extract<double> ex(py_envelope.attr("minx"));
|
|
|
|
if (!ex.check()) throw mapnik::datasource_exception("Python: could not convert envelope.minx");
|
|
|
|
box.set_minx(ex());
|
|
|
|
boost::python::extract<double> ex1(py_envelope.attr("miny"));
|
|
|
|
if (!ex1.check()) throw mapnik::datasource_exception("Python: could not convert envelope.miny");
|
|
|
|
box.set_miny(ex1());
|
|
|
|
boost::python::extract<double> ex2(py_envelope.attr("maxx"));
|
|
|
|
if (!ex2.check()) throw mapnik::datasource_exception("Python: could not convert envelope.maxx");
|
|
|
|
box.set_maxx(ex2());
|
|
|
|
boost::python::extract<double> ex3(py_envelope.attr("maxy"));
|
|
|
|
if (!ex3.check()) throw mapnik::datasource_exception("Python: could not convert envelope.maxy");
|
|
|
|
box.set_maxy(ex3());
|
|
|
|
}
|
|
|
|
}
|
2012-10-20 02:05:51 +02:00
|
|
|
}
|
2012-12-13 00:09:07 +01:00
|
|
|
catch ( boost::python::error_already_set )
|
2012-10-20 02:05:51 +02:00
|
|
|
{
|
|
|
|
throw mapnik::datasource_exception(extractException());
|
|
|
|
}
|
2013-05-30 18:18:25 +02:00
|
|
|
return box;
|
2012-07-19 20:06:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
boost::optional<mapnik::datasource::geometry_t> python_datasource::get_geometry_type() const
|
|
|
|
{
|
2014-07-07 19:23:15 +02:00
|
|
|
using return_type = boost::optional<mapnik::datasource::geometry_t>;
|
2012-07-19 20:06:44 +02:00
|
|
|
|
2012-10-20 02:05:51 +02:00
|
|
|
try
|
|
|
|
{
|
|
|
|
ensure_gil lock;
|
|
|
|
// if the datasource object has no geometry_type attribute, return a 'none' value
|
|
|
|
if (!PyObject_HasAttrString(datasource_.ptr(), "geometry_type"))
|
|
|
|
{
|
|
|
|
return return_type();
|
|
|
|
}
|
2012-12-13 00:09:07 +01:00
|
|
|
boost::python::object py_geometry_type = datasource_.attr("geometry_type");
|
2012-10-20 02:05:51 +02:00
|
|
|
// if the attribute value is 'None', return a 'none' value
|
2012-12-13 00:09:07 +01:00
|
|
|
if (py_geometry_type.ptr() == boost::python::object().ptr())
|
2012-10-20 02:05:51 +02:00
|
|
|
{
|
|
|
|
return return_type();
|
|
|
|
}
|
2012-12-13 00:09:07 +01:00
|
|
|
long geom_type_integer = boost::python::extract<long>(py_geometry_type);
|
2012-10-20 02:05:51 +02:00
|
|
|
return mapnik::datasource::geometry_t(geom_type_integer);
|
|
|
|
}
|
2012-12-13 00:09:07 +01:00
|
|
|
catch ( boost::python::error_already_set )
|
2012-10-20 02:05:51 +02:00
|
|
|
{
|
|
|
|
throw mapnik::datasource_exception(extractException());
|
|
|
|
}
|
2012-07-19 20:06:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
mapnik::featureset_ptr python_datasource::features(mapnik::query const& q) const
|
|
|
|
{
|
2012-10-20 02:05:51 +02:00
|
|
|
try
|
2012-07-19 20:06:44 +02:00
|
|
|
{
|
2012-10-20 02:05:51 +02:00
|
|
|
// if the query box intersects our world extent then query for features
|
|
|
|
if (envelope().intersects(q.get_bbox()))
|
|
|
|
{
|
|
|
|
ensure_gil lock;
|
2012-12-13 00:09:07 +01:00
|
|
|
boost::python::object features(datasource_.attr("features")(q));
|
2012-10-20 02:05:51 +02:00
|
|
|
// if 'None' was returned, return an empty feature set
|
2012-12-13 00:09:07 +01:00
|
|
|
if(features.ptr() == boost::python::object().ptr())
|
2012-10-20 02:05:51 +02:00
|
|
|
{
|
|
|
|
return mapnik::featureset_ptr();
|
|
|
|
}
|
2013-09-20 15:00:11 +02:00
|
|
|
return std::make_shared<python_featureset>(features);
|
2012-10-20 02:05:51 +02:00
|
|
|
}
|
|
|
|
// otherwise return an empty featureset pointer
|
|
|
|
return mapnik::featureset_ptr();
|
|
|
|
}
|
2012-12-13 00:09:07 +01:00
|
|
|
catch ( boost::python::error_already_set )
|
2012-10-20 02:05:51 +02:00
|
|
|
{
|
|
|
|
throw mapnik::datasource_exception(extractException());
|
2012-07-19 20:06:44 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-09-28 15:12:10 +02:00
|
|
|
mapnik::featureset_ptr python_datasource::features_at_point(mapnik::coord2d const& pt, double tol) const
|
2012-07-19 20:06:44 +02:00
|
|
|
{
|
|
|
|
|
2012-10-20 02:05:51 +02:00
|
|
|
try
|
|
|
|
{
|
|
|
|
ensure_gil lock;
|
2012-12-13 00:09:07 +01:00
|
|
|
boost::python::object features(datasource_.attr("features_at_point")(pt));
|
2012-10-20 02:05:51 +02:00
|
|
|
// if we returned none, return an empty set
|
2012-12-13 00:09:07 +01:00
|
|
|
if(features.ptr() == boost::python::object().ptr())
|
2012-10-20 02:05:51 +02:00
|
|
|
{
|
|
|
|
return mapnik::featureset_ptr();
|
|
|
|
}
|
|
|
|
// otherwise, return a feature set which can iterate over the iterator
|
2013-09-20 15:00:11 +02:00
|
|
|
return std::make_shared<python_featureset>(features);
|
2012-10-20 02:05:51 +02:00
|
|
|
}
|
2012-12-13 00:09:07 +01:00
|
|
|
catch ( boost::python::error_already_set )
|
2012-10-20 02:05:51 +02:00
|
|
|
{
|
|
|
|
throw mapnik::datasource_exception(extractException());
|
|
|
|
}
|
2012-07-19 20:06:44 +02:00
|
|
|
|
|
|
|
}
|