diff --git a/.gitignore b/.gitignore index 92ccec6b2..53b816877 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,10 @@ tests/cpp_tests/svg_renderer_tests/path_element_test tests/cpp_tests/svg_renderer_tests/path_element_test_case_1.svg tests/cpp_tests/svg_renderer_tests/root_element_test tests/data/sqlite/*index +demo/c++/cairo-demo.pdf +demo/c++/cairo-demo.png +demo/c++/cairo-demo256.png +demo/c++/demo.jpg +demo/c++/demo.png +demo/c++/demo256.png + diff --git a/bindings/python/mapnik_feature.cpp b/bindings/python/mapnik_feature.cpp index 3abe6d703..57cdec88c 100644 --- a/bindings/python/mapnik_feature.cpp +++ b/bindings/python/mapnik_feature.cpp @@ -34,7 +34,6 @@ #include #include #include -#include "mapnik_value_converter.hpp" mapnik::geometry_type & (mapnik::Feature::*get_geom1)(unsigned) = &mapnik::Feature::get_geometry; @@ -59,7 +58,7 @@ void feature_add_geometries_from_wkt(Feature &feature, std::string wkt) namespace boost { namespace python { -// Forward declaration + // Forward declaration template class map_indexing_suite2; @@ -95,9 +94,11 @@ namespace boost { namespace python { template static void - extension_def(Class& /*cl*/) + extension_def(Class& cl) { - + cl + .def("get", &get) + ; } static data_type& @@ -106,12 +107,25 @@ namespace boost { namespace python { typename Container::iterator i = container.props().find(i_); if (i == container.end()) { - PyErr_SetString(PyExc_KeyError, "Invalid key"); + PyErr_SetString(PyExc_KeyError, i_.c_str()); throw_error_already_set(); } + // will be auto-converted to proper python type by `mapnik_value_to_python` return i->second; } + static data_type + get(Container& container, index_type i_) + { + typename Container::iterator i = container.props().find(i_); + if (i != container.end()) + { + // will be auto-converted to proper python type by `mapnik_value_to_python` + return i->second; + } + return mapnik::value_null(); + } + static void set_item(Container& container, index_type i, data_type const& v) { diff --git a/bindings/python/mapnik_geometry.cpp b/bindings/python/mapnik_geometry.cpp index 5e5ec8c2c..30f2876df 100644 --- a/bindings/python/mapnik_geometry.cpp +++ b/bindings/python/mapnik_geometry.cpp @@ -31,6 +31,7 @@ #include #include #include +#include namespace { @@ -47,19 +48,47 @@ geometry_type const& getitem_impl(path_type & p, int key) throw boost::python::error_already_set(); } -void from_wkt_impl(path_type& p, std::string const& wkt) +void add_wkt_impl(path_type& p, std::string const& wkt) { bool result = mapnik::from_wkt(wkt , p); if (!result) throw std::runtime_error("Failed to parse WKT"); } -void from_wkb_impl(path_type& p, std::string const& wkb) +void add_wkb_impl(path_type& p, std::string const& wkb) { mapnik::geometry_utils::from_wkb(p, wkb.c_str(), wkb.size(), true); } +boost::shared_ptr from_wkt_impl(std::string const& wkt) +{ + boost::shared_ptr paths = boost::make_shared(); + bool result = mapnik::from_wkt(wkt, *paths); + if (!result) throw std::runtime_error("Failed to parse WKT"); + return paths; } +boost::shared_ptr from_wkb_impl(std::string const& wkb) +{ + boost::shared_ptr paths = boost::make_shared(); + mapnik::geometry_utils::from_wkb(*paths, wkb.c_str(), wkb.size(), true); + return paths; +} + +} + +PyObject* to_wkb( geometry_type const& geom) +{ + mapnik::util::wkb_buffer_ptr wkb = mapnik::util::to_wkb(geom,mapnik::util::wkbXDR); + return +#if PY_VERSION_HEX >= 0x03000000 + ::PyBytes_FromStringAndSize +#else + ::PyString_FromStringAndSize +#endif + ((const char*)wkb->buffer(),wkb->size()); +} + + void export_geometry() { using namespace boost::python; @@ -74,18 +103,23 @@ void export_geometry() ; using mapnik::geometry_type; - class_,boost::noncopyable>("Geometry2d",no_init) + class_, boost::noncopyable>("Geometry2d",no_init) .def("envelope",&geometry_type::envelope) // .def("__str__",&geometry_type::to_string) .def("type",&geometry_type::type) + .def("to_wkb",&to_wkb) // TODO add other geometry_type methods ; - class_("Path") + class_, boost::noncopyable>("Path") .def("__getitem__", getitem_impl,return_value_policy()) .def("__len__", &path_type::size) + .def("add_wkt",add_wkt_impl) + .def("add_wkb",add_wkb_impl) .def("from_wkt",from_wkt_impl) .def("from_wkb",from_wkb_impl) + .staticmethod("from_wkt") + .staticmethod("from_wkb") ; } diff --git a/bindings/python/mapnik_map.cpp b/bindings/python/mapnik_map.cpp index 0bfe31cc6..db332ecda 100644 --- a/bindings/python/mapnik_map.cpp +++ b/bindings/python/mapnik_map.cpp @@ -119,6 +119,8 @@ struct map_pickle_suite : boost::python::pickle_suite std::vector& (Map::*layers_nonconst)() = &Map::layers; std::vector const& (Map::*layers_const)() const = &Map::layers; +mapnik::parameters& (Map::*attr_nonconst)() = &Map::get_extra_attributes; +mapnik::parameters& (Map::*params_nonconst)() = &Map::get_extra_parameters; mapnik::feature_type_style find_style (mapnik::Map const& m, std::string const& name) { @@ -173,6 +175,7 @@ mapnik::featureset_ptr query_map_point(mapnik::Map const& m, int index, double x return m.query_map_point(idx, x, y); } + void export_map() { using namespace boost::python; @@ -449,7 +452,8 @@ void export_map() "about the hit areas rendered on the map.\n" ) - .def("extra_attributes",&Map::get_extra_attributes,return_value_policy(),"TODO") + .add_property("extra_attributes",make_function(attr_nonconst,return_value_policy()),"TODO") + .add_property("params",make_function(params_nonconst,return_value_policy()),"TODO") .add_property("aspect_fix_mode", &Map::get_aspect_fix_mode, diff --git a/bindings/python/mapnik_palette.cpp b/bindings/python/mapnik_palette.cpp index 23536896e..45866d318 100644 --- a/bindings/python/mapnik_palette.cpp +++ b/bindings/python/mapnik_palette.cpp @@ -48,6 +48,6 @@ void export_palette () ( arg("palette"), arg("type")), "Creates a new color palette from a file\n" )*/ - .def( "__init__", boost::python::make_constructor( &make_palette)) + .def( "__init__", boost::python::make_constructor(make_palette)) ; } diff --git a/bindings/python/mapnik_parameters.cpp b/bindings/python/mapnik_parameters.cpp index 662c0b5e7..a202f8190 100644 --- a/bindings/python/mapnik_parameters.cpp +++ b/bindings/python/mapnik_parameters.cpp @@ -2,7 +2,7 @@ * * This file is part of Mapnik (c++ mapping toolkit) * - * Copyright (C) 2006 Artem Pavlenko, Jean-Francois Doyon + * Copyright (C) 2011 Artem Pavlenko * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -19,50 +19,26 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ -//$Id: mapnik_parameters.cpp 17 2005-03-08 23:58:43Z pavlenko $ // boost #include +#include // mapnik #include +#include +#include using mapnik::parameter; using mapnik::parameters; -struct pickle_value : public boost::static_visitor<> -{ -public: - pickle_value( boost::python::list vals): - vals_(vals) {} - - void operator () ( int val ) - { - vals_.append(val); - } - - void operator () ( double val ) - { - vals_.append(val); - } - - void operator () ( std::string val ) - { - vals_.append(val); - } - -private: - boost::python::list vals_; - -}; - struct parameter_pickle_suite : boost::python::pickle_suite { static boost::python::tuple getinitargs(const parameter& p) { using namespace boost::python; - return boost::python::make_tuple(p.first,boost::get(p.second)); + return boost::python::make_tuple(p.first,p.second); } }; @@ -76,11 +52,7 @@ struct parameters_pickle_suite : boost::python::pickle_suite parameters::const_iterator pos=p.begin(); while(pos!=p.end()) { - boost::python::list vals; - pickle_value serializer( vals ); - mapnik::value_holder val = pos->second; - boost::apply_visitor( serializer, val ); - d[pos->first] = vals[0]; + d[pos->first] = pos->second; ++pos; } return boost::python::make_tuple(d); @@ -107,7 +79,9 @@ struct parameters_pickle_suite : boost::python::pickle_suite extract ex0(obj); extract ex1(obj); extract ex2(obj); + extract ex3(obj); + // TODO - this is never hit - we need proper python string -> std::string to get invoked here if (ex0.check()) { p[key] = ex0(); @@ -120,72 +94,131 @@ struct parameters_pickle_suite : boost::python::pickle_suite { p[key] = ex2(); } - - /* - extract_value serializer( p, key ); - mapnik::value_holder val = extract(d[key]); - boost::apply_visitor( serializer, val ); - */ + else if (ex3.check()) + { + std::string buffer; + mapnik::to_utf8(ex3(),buffer); + p[key] = buffer; + } + else + { + std::clog << "could not unpickle key: " << key << "\n"; + } } } }; -boost::python::dict dict_params(parameters& p) + +mapnik::value_holder get_params_by_key1(mapnik::parameters const& p, std::string const& key) { - boost::python::dict d; - parameters::const_iterator pos=p.begin(); - while(pos!=p.end()) + parameters::const_iterator pos = p.find(key); + if (pos != p.end()) { - boost::python::list vals; - pickle_value serializer( vals ); - mapnik::value_holder val = pos->second; - boost::apply_visitor( serializer, val ); - d[pos->first] = vals[0]; - ++pos; + // will be auto-converted to proper python type by `mapnik_params_to_python` + return pos->second; } - return d; + return mapnik::value_null(); } -boost::python::list list_params(parameters& p) +mapnik::value_holder get_params_by_key2(mapnik::parameters const& p, std::string const& key) { - boost::python::list l; - parameters::const_iterator pos=p.begin(); - while(pos!=p.end()) + parameters::const_iterator pos = p.find(key); + if (pos == p.end()) { - boost::python::list vals; - pickle_value serializer( vals ); - mapnik::value_holder val = pos->second; - boost::apply_visitor( serializer, val ); - l.append(boost::python::make_tuple(pos->first,vals[0])); - ++pos; + PyErr_SetString(PyExc_KeyError, key.c_str()); + boost::python::throw_error_already_set(); } - return l; + // will be auto-converted to proper python type by `mapnik_params_to_python` + return pos->second; } -boost::python::dict dict_param(parameter& p) +mapnik::parameter get_params_by_index(mapnik::parameters const& p, int index) { - boost::python::dict d; - d[p.first] = boost::get(p.second); - return d; + if (index < 0 || index > p.size()) + { + PyErr_SetString(PyExc_IndexError, "Index is out of range"); + throw boost::python::error_already_set(); + } + + parameters::const_iterator itr = p.begin(); + parameters::const_iterator end = p.end(); + + unsigned idx = 0; + while (itr != p.end()) + { + if (idx == index) + { + return *itr; + } + ++idx; + ++itr; + } + PyErr_SetString(PyExc_IndexError, "Index is out of range"); + throw boost::python::error_already_set(); } -boost::python::tuple tuple_param(parameter& p) +void add_parameter(mapnik::parameters & p, mapnik::parameter const& param) { - return boost::python::make_tuple(p.first,boost::get(p.second)); + p[param.first] = param.second; } +mapnik::value_holder get_param(mapnik::parameter const& p, int index) +{ + if (index == 0) + { + return p.first; + } + else if (index == 1) + { + return p.second; + } + else + { + PyErr_SetString(PyExc_IndexError, "Index is out of range"); + throw boost::python::error_already_set(); + } +} + +boost::shared_ptr create_parameter_from_string(std::string const& key, std::string const& value) +{ + return boost::make_shared(key,mapnik::value_holder(value)); +} + +boost::shared_ptr create_parameter_from_int(std::string const& key, int value) +{ + return boost::make_shared(key,mapnik::value_holder(value)); +} + +boost::shared_ptr create_parameter_from_float(std::string const& key, double value) +{ + return boost::make_shared(key,mapnik::value_holder(value)); +} + + void export_parameters() { using namespace boost::python; - class_("Parameter",init()) + class_ >("Parameter",no_init) + .def("__init__", make_constructor(create_parameter_from_string), + "Create a mapnik.Parameter from a pair of values, the first being a string\n" + "and the second being either a string, and integer, or a float") + .def("__init__", make_constructor(create_parameter_from_int), + "Create a mapnik.Parameter from a pair of values, the first being a string\n" + "and the second being either a string, and integer, or a float") + .def("__init__", make_constructor(create_parameter_from_float), + "Create a mapnik.Parameter from a pair of values, the first being a string\n" + "and the second being either a string, and integer, or a float") .def_pickle(parameter_pickle_suite()) - .def("as_dict",dict_param) - .def("as_tuple",tuple_param) + .def("__getitem__",get_param) ; class_("Parameters",init<>()) .def_pickle(parameters_pickle_suite()) - .def("as_dict",dict_params) - .def("as_list",list_params) + .def("get",get_params_by_key1) + .def("__getitem__",get_params_by_key2) + .def("__getitem__",get_params_by_index) + .def("__len__",¶meters::size) + .def("append",add_parameter) + .def("iteritems",iterator()) ; } diff --git a/bindings/python/mapnik_python.cpp b/bindings/python/mapnik_python.cpp index 5346f2c9b..70b361e75 100644 --- a/bindings/python/mapnik_python.cpp +++ b/bindings/python/mapnik_python.cpp @@ -83,24 +83,13 @@ void export_label_collision_detector(); #include #include #include "python_grid_utils.hpp" +#include "mapnik_value_converter.hpp" #if defined(HAVE_CAIRO) && defined(HAVE_PYCAIRO) #include static Pycairo_CAPI_t *Pycairo_CAPI; #endif - -namespace boost { namespace python { - - struct mapnik_value_to_python - { - static PyObject* convert(mapnik::value const& v) - { - return boost::apply_visitor(value_converter(),v.base()); - } - }; - }} - void render(const mapnik::Map& map, mapnik::image_32& image, double scale_factor = 1.0, @@ -651,5 +640,6 @@ BOOST_PYTHON_MODULE(_mapnik) register_ptr_to_python(); register_ptr_to_python(); + to_python_converter(); to_python_converter(); } diff --git a/bindings/python/mapnik_value_converter.hpp b/bindings/python/mapnik_value_converter.hpp index 292f84d14..bbac939b6 100644 --- a/bindings/python/mapnik_value_converter.hpp +++ b/bindings/python/mapnik_value_converter.hpp @@ -27,6 +27,7 @@ #include namespace boost { namespace python { + struct value_converter : public boost::static_visitor { PyObject * operator() (int val) const @@ -48,6 +49,13 @@ namespace boost { namespace python { return ::PyBool_FromLong(val); } + PyObject * operator() (std::string const& s) const + { + PyObject *obj = Py_None; + obj = ::PyUnicode_DecodeUTF8(s.c_str(),implicit_cast(s.length()),0); + return obj; + } + PyObject * operator() (UnicodeString const& s) const { std::string buffer; @@ -63,6 +71,25 @@ namespace boost { namespace python { } }; + + struct mapnik_value_to_python + { + static PyObject* convert(mapnik::value const& v) + { + return boost::apply_visitor(value_converter(),v.base()); + } + + }; + + struct mapnik_param_to_python + { + static PyObject* convert(mapnik::value_holder const& v) + { + return boost::apply_visitor(value_converter(),v); + } + }; + + } } diff --git a/demo/c++/rundemo.cpp b/demo/c++/rundemo.cpp index 65f542170..749fcd74a 100644 --- a/demo/c++/rundemo.cpp +++ b/demo/c++/rundemo.cpp @@ -253,12 +253,15 @@ int main ( int argc , char** argv) save_to_file(buf.data(),"demo.jpg","jpeg"); save_to_file(buf.data(),"demo.png","png"); save_to_file(buf.data(),"demo256.png","png256"); + save_to_file(buf.data(),"demo.tif","tiff"); + std::cout << "Three maps have been rendered using AGG in the current directory:\n" "- demo.jpg\n" "- demo.png\n" "- demo256.png\n" + "- demo.tif\n" "Have a look!\n"; - + #if defined(HAVE_CAIRO) Cairo::RefPtr image_surface; diff --git a/demo/python/rundemo.py b/demo/python/rundemo.py index d35935c79..d71a439fb 100644 --- a/demo/python/rundemo.py +++ b/demo/python/rundemo.py @@ -322,7 +322,7 @@ m.layers.append(popplaces_lyr) # Set the initial extent of the map in 'master' spherical Mercator projection m.zoom_to_box(mapnik.Box2d(-8024477.28459,5445190.38849,-7381388.20071,5662941.44855)) -# Render two maps, two PNGs, one JPEG. +# Render map im = mapnik.Image(m.width,m.height) mapnik.render(m, im) @@ -347,6 +347,9 @@ images_.append('demo_high.jpg') im.save('demo_low.jpg', 'jpeg50') images_.append('demo_low.jpg') +im.save('demo.tif', 'tiff') +images_.append('demo.tif') + # Render cairo examples if HAS_PYCAIRO_MODULE and mapnik.has_pycairo(): diff --git a/include/mapnik/map.hpp b/include/mapnik/map.hpp index 152ca0181..bbc5d9432 100644 --- a/include/mapnik/map.hpp +++ b/include/mapnik/map.hpp @@ -84,6 +84,7 @@ private: boost::optional > maximum_extent_; std::string base_path_; parameters extra_attr_; + parameters extra_params_; public: @@ -444,14 +445,34 @@ public: std::string get_metawriter_property(std::string name) const; /*! - * @brief Get extra properties that can be carried on the Map + * @brief Get extra valid attributes of the Map that are not true members */ parameters const& get_extra_attributes() const; /*! - * @brief Set extra properties that can be carried on the Map + * @brief Get non-const extra valid attributes of the Map that are not true members */ - void set_extra_attributes(parameters& params); + parameters& get_extra_attributes(); + + /*! + * @brief Set extra attributes of the Map + */ + void set_extra_attributes(parameters& attr); + + /*! + * @brief Get extra, arbitrary Parameters attached to the Map + */ + parameters const& get_extra_parameters() const; + + /*! + * @brief Get non-const extra, arbitrary Parameters attached to the Map + */ + parameters& get_extra_parameters(); + + /*! + * @brief Set extra, arbitary Parameters of the Map + */ + void set_extra_parameters(parameters& params); private: void fixAspectRatio(); diff --git a/include/mapnik/params.hpp b/include/mapnik/params.hpp index 97f4fd4f6..397e6f986 100644 --- a/include/mapnik/params.hpp +++ b/include/mapnik/params.hpp @@ -23,19 +23,22 @@ #ifndef MAPNIK_PARAMS_HPP #define MAPNIK_PARAMS_HPP -// mapnik +// boost #include #include #include #include +// mapnik +#include + // stl #include #include namespace mapnik { -typedef boost::variant value_holder; +typedef boost::variant value_holder; typedef std::pair parameter; typedef std::map param_map; diff --git a/include/mapnik/util/geometry_to_wkb.hpp b/include/mapnik/util/geometry_to_wkb.hpp new file mode 100644 index 000000000..7d2ad97b2 --- /dev/null +++ b/include/mapnik/util/geometry_to_wkb.hpp @@ -0,0 +1,235 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2011 Artem Pavlenko + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ + +//$Id$ + +#ifndef MAPNIK_GEOMETRY_TO_WKB_HPP +#define MAPNIK_GEOMETRY_TO_WKB_HPP + +// mapnik +#include +#include +// boost +#include +#include +#include +#include +// stl +#include +#include + +namespace mapnik { namespace util { + +std::string to_hex(const char* blob, unsigned size) +{ + std::string buf; + buf.reserve(size*2); + std::ostringstream s(buf); + s.seekp(0); + char hex[3]; + std::memset(hex,0,3); + for ( unsigned pos=0; pos < size; ++pos) + { + std::sprintf (hex, "%02x", int(blob[pos]) & 0xff); + s << hex; + } + return s.str(); +} + +enum wkbByteOrder { + wkbXDR=0, + wkbNDR=1 +}; + + +inline void reverse_bytes(char size, char *address) +{ + char * first = address; + char * last = first + size - 1; + for(;first < last;++first, --last) + { + char x = *last; + *last = *first; + *first = x; + } +} + +template +inline void write (S & stream, T val, std::size_t size, wkbByteOrder byte_order) +{ +#ifdef MAPNIK_BIG_ENDIAN + bool need_swap = byte_order ? wkbNDR : wkbXDR; +#else + bool need_swap = byte_order ? wkbXDR : wkbNDR; +#endif + char* buf = reinterpret_cast(&val); + if (need_swap) + { + reverse_bytes(size,buf); + } + stream.write(buf,size); +} + +struct wkb_buffer +{ + wkb_buffer(std::size_t size) + : size_(size), + data_( (size_!=0) ? static_cast(::operator new (size_)):0) + {} + + ~wkb_buffer() + { + ::operator delete(data_); + } + + inline std::size_t size() const + { + return size_; + } + + inline char* buffer() + { + return data_; + } + + std::size_t size_; + char * data_; +}; + +typedef boost::shared_ptr wkb_buffer_ptr; + +wkb_buffer_ptr to_point_wkb( geometry_type const& g, wkbByteOrder byte_order) +{ + assert(g.num_points() == 1); + std::size_t size = 1 + 4 + 8*2 ; // byteOrder + wkbType + Point + wkb_buffer_ptr wkb = boost::make_shared(size); + boost::interprocess::bufferstream ss(wkb->buffer(), wkb->size(), std::ios::out | std::ios::binary); + ss.write(reinterpret_cast(&byte_order),1); + int type = static_cast(g.type()); + write(ss,type,4,byte_order); + double x,y; + g.get_vertex(0,&x,&y); + write(ss,x,8,byte_order); + write(ss,y,8,byte_order); + assert(ss.good()); + return wkb; +} + +wkb_buffer_ptr to_line_string_wkb( geometry_type const& g, wkbByteOrder byte_order) +{ + unsigned num_points = g.num_points(); + assert(num_points > 1); + std::size_t size = 1 + 4 + 4 + 8*2*num_points ; // byteOrder + wkbType + numPoints + Point*numPoints + wkb_buffer_ptr wkb = boost::make_shared(size); + boost::interprocess::bufferstream ss(wkb->buffer(), wkb->size(), std::ios::out | std::ios::binary); + ss.write(reinterpret_cast(&byte_order),1); + int type = static_cast(g.type()); + write(ss,type,4,byte_order); + write(ss,num_points,4,byte_order); + double x,y; + for (unsigned i=0; i< num_points; ++i) + { + g.get_vertex(i,&x,&y); + write(ss,x,8,byte_order); + write(ss,y,8,byte_order); + } + assert(ss.good()); + return wkb; +} + +wkb_buffer_ptr to_polygon_wkb( geometry_type const& g, wkbByteOrder byte_order) +{ + unsigned num_points = g.num_points(); + assert(num_points > 1); + + typedef std::pair point_type; + typedef std::vector linear_ring; + boost::ptr_vector rings; + + double x,y; + std::size_t size = 1 + 4 + 4 ; // byteOrder + wkbType + numRings + for (unsigned i=0; i< num_points; ++i) + { + unsigned command = g.get_vertex(i,&x,&y); + if (command == SEG_MOVETO) + { + rings.push_back(new linear_ring); // start new loop + size += 4; // num_points + } + rings.back().push_back(std::make_pair(x,y)); + size += 2 * 8; // point + } + unsigned num_rings = rings.size(); + wkb_buffer_ptr wkb = boost::make_shared(size); + boost::interprocess::bufferstream ss(wkb->buffer(), wkb->size(), std::ios::out | std::ios::binary); + + ss.write(reinterpret_cast(&byte_order),1); + int type = static_cast(g.type()); + write(ss,type,4,byte_order); + write(ss,num_rings,4,byte_order); + + BOOST_FOREACH ( linear_ring const& ring, rings) + { + unsigned num_points = ring.size(); + write(ss,num_points,4,byte_order); + BOOST_FOREACH ( point_type const& pt, ring) + { + double x = pt.first; + double y = pt.second; + write(ss,x,8,byte_order); + write(ss,y,8,byte_order); + } + } + + assert(ss.good()); + return wkb; +} + +wkb_buffer_ptr to_wkb(geometry_type const& g, wkbByteOrder byte_order ) +{ + wkb_buffer_ptr wkb; + + switch (g.type()) + { + case mapnik::Point : + wkb = to_point_wkb(g, byte_order); + break; + case mapnik::LineString: + wkb = to_line_string_wkb(g, byte_order); + break; + case mapnik::Polygon: + wkb = to_polygon_wkb(g, byte_order); + break; + case mapnik::MultiPoint: + case mapnik::MultiLineString: + case mapnik::MultiPolygon: + break; + default: + break; + } + return wkb; +} + +}} + + +#endif // MAPNIK_GEOMETRY_TO_WKB_HPP diff --git a/src/build.py b/src/build.py index 72cf9b284..2d9b506a3 100644 --- a/src/build.py +++ b/src/build.py @@ -351,19 +351,23 @@ includes = glob.glob('../include/mapnik/*.hpp') svg_includes = glob.glob('../include/mapnik/svg/*.hpp') wkt_includes = glob.glob('../include/mapnik/wkt/*.hpp') grid_includes = glob.glob('../include/mapnik/grid/*.hpp') +util_includes = glob.glob('../include/mapnik/util/*.hpp') inc_target = os.path.normpath(env['INSTALL_PREFIX']+'/include/mapnik') svg_inc_target = os.path.normpath(env['INSTALL_PREFIX']+'/include/mapnik/svg') wkt_inc_target = os.path.normpath(env['INSTALL_PREFIX']+'/include/mapnik/wkt') grid_inc_target = os.path.normpath(env['INSTALL_PREFIX']+'/include/mapnik/grid') +util_inc_target = os.path.normpath(env['INSTALL_PREFIX']+'/include/mapnik/util') if 'uninstall' not in COMMAND_LINE_TARGETS: env.Alias(target='install', source=env.Install(inc_target, includes)) env.Alias(target='install', source=env.Install(svg_inc_target, svg_includes)) env.Alias(target='install', source=env.Install(wkt_inc_target, wkt_includes)) env.Alias(target='install', source=env.Install(grid_inc_target, grid_includes)) + env.Alias(target='install', source=env.Install(util_inc_target, util_includes)) env['create_uninstall_target'](env, inc_target) env['create_uninstall_target'](env, svg_inc_target) env['create_uninstall_target'](env, wkt_inc_target) env['create_uninstall_target'](env, grid_inc_target) +env['create_uninstall_target'](env, util_inc_target) diff --git a/src/datasource_cache.cpp b/src/datasource_cache.cpp index 9cca1c816..48ed583db 100644 --- a/src/datasource_cache.cpp +++ b/src/datasource_cache.cpp @@ -226,6 +226,10 @@ void datasource_cache::register_datasources(const std::string& str) #endif registered_=true; } + else if (!ds_name) + { + std::clog << "Problem loading plugin library '" << itr->path().string() << "' (plugin is lacking compatible interface)" << std::endl; + } } else { diff --git a/src/load_map.cpp b/src/load_map.cpp index 9b25dec5f..c20125502 100644 --- a/src/load_map.cpp +++ b/src/load_map.cpp @@ -255,10 +255,10 @@ void map_parser::parse_map( Map & map, ptree const & pt, std::string const& base } else { - std::ostringstream s; + std::ostringstream s_err; s << "failed to parse 'maximum-extent'"; if ( strict_ ) - throw config_error(s.str()); + throw config_error(s_err.str()); else std::clog << "### WARNING: " << s.str() << std::endl; } @@ -311,6 +311,7 @@ void map_parser::parse_map( Map & map, ptree const & pt, std::string const& base } } + map.set_extra_attributes(extra_attr); } catch (const config_error & ex) @@ -389,6 +390,32 @@ void map_parser::parse_map_include( Map & map, ptree const & include ) } datasource_templates_[name] = params; } + else if (v.first == "Parameters") + { + std::string name = get_attr(v.second, "name", std::string("Unnamed")); + parameters & params = map.get_extra_parameters(); + ptree::const_iterator paramIter = v.second.begin(); + ptree::const_iterator endParam = v.second.end(); + for (; paramIter != endParam; ++paramIter) + { + ptree const& param = paramIter->second; + + if (paramIter->first == "Parameter") + { + std::string name = get_attr(param, "name"); + std::string value = get_value( param, + "parameter"); + params[name] = value; + } + else if( paramIter->first != "" && + paramIter->first != "" ) + { + throw config_error(std::string("Unknown child node in ") + + "'Parameters'. Expected 'Parameter' but got '" + + paramIter->first + "'"); + } + } + } else if (v.first != "" && v.first != "") { @@ -2185,43 +2212,42 @@ std::string map_parser::ensure_relative_to_xml( boost::optional opt void map_parser::ensure_attrs(ptree const& sym, std::string name, std::string attrs) { - typedef ptree::key_type::value_type Ch; - //typedef boost::property_tree::xml_parser::xmlattr x_att; - - std::set attr_set; - boost::split(attr_set, attrs, boost::is_any_of(",")); - for (ptree::const_iterator itr = sym.begin(); itr != sym.end(); ++itr) + optional attribs = sym.get_child_optional( boost::property_tree::xml_parser::xmlattr() ); + if (attribs) { - //ptree::value_type const& v = *itr; - if (itr->first == boost::property_tree::xml_parser::xmlattr()) - { - optional attribs = sym.get_child_optional( boost::property_tree::xml_parser::xmlattr() ); - if (attribs) - { - std::ostringstream s(""); - s << "### " << name << " properties warning: "; - int missing = 0; - for (ptree::const_iterator it = attribs.get().begin(); it != attribs.get().end(); ++it) - { - std::string name = it->first; - bool found = (attr_set.find(name) != attr_set.end()); - if (!found) - { - if (missing) s << ","; - s << "'" << name << "'"; - ++missing; - } - } - if (missing) { - if (missing > 1) s << " are"; - else s << " is"; - s << " invalid, acceptable values are:\n'" << attrs << "'\n"; - std::clog << s.str(); - } - } - } - } + std::set attr_set; + boost::split(attr_set, attrs, boost::is_any_of(",")); + std::ostringstream s(""); + s << "### " << name << " properties warning: "; + int missing = 0; + for (ptree::const_iterator it = attribs.get().begin(); it != attribs.get().end(); ++it) + { + std::string name = it->first; + bool found = (attr_set.find(name) != attr_set.end()); + if (!found) + { + if (missing) + { + s << ","; + } + s << "'" << name << "'"; + ++missing; + } + } + if (missing) { + if (missing > 1) + { + s << " are"; + } + else + { + s << " is"; + } + s << " invalid, acceptable values are:\n'" << attrs << "'\n"; + std::clog << s.str(); + } + } } } // end of namespace mapnik diff --git a/src/map.cpp b/src/map.cpp index 2f6e7af43..ec974fb49 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -92,7 +92,8 @@ Map::Map(const Map& rhs) current_extent_(rhs.current_extent_), maximum_extent_(rhs.maximum_extent_), base_path_(rhs.base_path_), - extra_attr_(rhs.extra_attr_) {} + extra_attr_(rhs.extra_attr_), + extra_params_(rhs.extra_params_) {} Map& Map::operator=(const Map& rhs) { @@ -110,6 +111,7 @@ Map& Map::operator=(const Map& rhs) maximum_extent_=rhs.maximum_extent_; base_path_=rhs.base_path_; extra_attr_=rhs.extra_attr_; + extra_params_=rhs.extra_params_; return *this; } @@ -677,9 +679,29 @@ parameters const& Map::get_extra_attributes() const return extra_attr_; } -void Map::set_extra_attributes(parameters& params) +parameters& Map::get_extra_attributes() { - extra_attr_ = params; + return extra_attr_; +} + +void Map::set_extra_attributes(parameters& attr) +{ + extra_attr_ = attr; +} + +parameters const& Map::get_extra_parameters() const +{ + return extra_params_; +} + +parameters& Map::get_extra_parameters() +{ + return extra_params_; +} + +void Map::set_extra_parameters(parameters& params) +{ + extra_params_ = params; } } diff --git a/src/raster_colorizer.cpp b/src/raster_colorizer.cpp index 03f2968f5..95aec3cb7 100644 --- a/src/raster_colorizer.cpp +++ b/src/raster_colorizer.cpp @@ -120,11 +120,9 @@ void raster_colorizer::colorize(raster_ptr const& raster,const std::map::const_iterator fi = Props.find("NODATA"); if (fi != Props.end()) - //if (Props.count("NODATA")>0) { hasNoData = true; - //noDataValue = Props.at("NODATA").to_double(); - noDataValue = fi->second.to_double(); + noDataValue = static_cast(fi->second.to_double()); } for (int i=0; i(fraction * (end - start) + start); } color raster_colorizer::get_color(float value) const { @@ -213,10 +211,10 @@ color raster_colorizer::get_color(float value) const { else { float fraction = (value - stopValue) / (nextStopValue - stopValue); - float r = interpolate(stopColor.red(), nextStopColor.red(),fraction); - float g = interpolate(stopColor.green(), nextStopColor.green(),fraction); - float b = interpolate(stopColor.blue(), nextStopColor.blue(),fraction); - float a = interpolate(stopColor.alpha(), nextStopColor.alpha(),fraction); + unsigned r = interpolate(stopColor.red(), nextStopColor.red(),fraction); + unsigned g = interpolate(stopColor.green(), nextStopColor.green(),fraction); + unsigned b = interpolate(stopColor.blue(), nextStopColor.blue(),fraction); + unsigned a = interpolate(stopColor.alpha(), nextStopColor.alpha(),fraction); outputColor.set_red(r); outputColor.set_green(g); diff --git a/src/save_map.cpp b/src/save_map.cpp index 2a9e8368e..48d2f9099 100644 --- a/src/save_map.cpp +++ b/src/save_map.cpp @@ -761,6 +761,24 @@ void serialize_datasource( ptree & layer_node, datasource_ptr datasource) } } +void serialize_parameters( ptree & map_node, mapnik::parameters const& params) +{ + ptree & params_node = map_node.push_back( + ptree::value_type("Parameters", ptree()))->second; + + parameters::const_iterator it = params.begin(); + parameters::const_iterator end = params.end(); + for (; it != end; ++it) + { + boost::property_tree::ptree & param_node = params_node.push_back( + boost::property_tree::ptree::value_type("Parameter", + boost::property_tree::ptree()))->second; + param_node.put(".name", it->first ); + param_node.put_value( it->second ); + + } +} + void serialize_layer( ptree & map_node, const layer & layer, bool explicit_defaults ) { ptree & layer_node = map_node.push_back( @@ -900,6 +918,8 @@ void serialize_map(ptree & pt, Map const & map, bool explicit_defaults) { set_attr( map_node, p_it->first, p_it->second ); } + + serialize_parameters( map_node, map.get_extra_parameters()); Map::const_style_iterator it = map.styles().begin(); Map::const_style_iterator end = map.styles().end(); diff --git a/tests/data/good_maps/extra_arbitary_map_parameters.xml b/tests/data/good_maps/extra_arbitary_map_parameters.xml new file mode 100644 index 000000000..d4377cc50 --- /dev/null +++ b/tests/data/good_maps/extra_arbitary_map_parameters.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/data/good_maps/extra_known_map_attributes.xml b/tests/data/good_maps/extra_known_map_attributes.xml new file mode 100644 index 000000000..38fa59661 --- /dev/null +++ b/tests/data/good_maps/extra_known_map_attributes.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/tests/python_tests/extra_map_props_test.py b/tests/python_tests/extra_map_props_test.py new file mode 100644 index 000000000..737bab03f --- /dev/null +++ b/tests/python_tests/extra_map_props_test.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from nose.tools import * +from utilities import execution_path +from Queue import Queue +import threading + +import os, mapnik +import sqlite3 + +def setup(): + # All of the paths used are relative, if we run the tests + # from another directory we need to chdir() + os.chdir(execution_path('.')) + +def test_non_member_known_attributes(): + m = mapnik.Map(256,256) + mapnik.load_map(m,'../data/good_maps/extra_known_map_attributes.xml') + attr = m.extra_attributes + eq_(len(attr),2) + eq_(attr['font-directory'],'.') + eq_(attr['minimum-version'],'0.0.0') + +def test_arbitrary_parameters_attached_to_map(): + m = mapnik.Map(256,256) + mapnik.load_map(m,'../data/good_maps/extra_arbitary_map_parameters.xml') + attr = m.extra_attributes + eq_(len(attr),0) + + eq_(len(m.params),3) + eq_(m.params['key'],'value2') + eq_(m.params['key3'],'value3') + eq_(m.params['unicode'],u'iván') + + +if __name__ == "__main__": + setup() + [eval(run)() for run in dir() if 'test_' in run] + diff --git a/tests/python_tests/font_test.py b/tests/python_tests/font_test.py index bfc72f971..f9249b247 100644 --- a/tests/python_tests/font_test.py +++ b/tests/python_tests/font_test.py @@ -2,7 +2,7 @@ from nose.tools import * -import mapnik, pickle +import mapnik # Tests that exercise fonts. diff --git a/tests/python_tests/geometry_wkb_test.py b/tests/python_tests/geometry_wkb_test.py new file mode 100644 index 000000000..9750ef405 --- /dev/null +++ b/tests/python_tests/geometry_wkb_test.py @@ -0,0 +1,45 @@ +#encoding: utf8 + +from nose.tools import * +import os +from utilities import execution_path +import mapnik + +wkts = [ + [1,"POINT (30 10)"], + [1,"LINESTRING (30 10, 10 30, 40 40)"], + [1,"POLYGON ((30 10, 10 20, 20 40, 40 40, 30 10))"], + [1,"POLYGON ((35 10, 10 20, 15 40, 45 45, 35 10),(20 30, 35 35, 30 20, 20 30))"], + [4,"MULTIPOINT ((10 40), (40 30), (20 20), (30 10))"], + [2,"MULTILINESTRING ((10 10, 20 20, 10 40),(40 40, 30 30, 40 20, 30 10))"], + [2,"MULTIPOLYGON (((30 20, 10 40, 45 40, 30 20)),((15 5, 40 10, 10 20, 5 10, 15 5)))"], + [2,"MULTIPOLYGON (((40 40, 20 45, 45 30, 40 40)),((20 35, 45 20, 30 5, 10 10, 10 30, 20 35),(30 20, 20 25, 20 15, 30 20)))"] +] + + +def compare_wkb_from_wkt(wkt,num): + f = mapnik.Feature(1) + f.add_geometries_from_wkt(wkt) + eq_(len(f.geometries()),num) + + paths = mapnik.Path.from_wkt(wkt) + eq_(len(paths),num) + + eq_(f.geometries()[0].to_wkb(),paths[0].to_wkb()) + + paths2 = mapnik.Path() + for path in paths: + paths2.add_wkb(path.to_wkb()) + + eq_(len(paths2),num) + eq_(f.geometries()[0].to_wkb(),paths2[0].to_wkb()) + +def test_point(): + for wkt in wkts: + try: + compare_wkb_from_wkt(wkt[1],wkt[0]) + except RuntimeError, e: + raise RuntimeError('%s %s' % (e, wkt)) + +if __name__ == "__main__": + [eval(run)() for run in dir() if 'test_' in run] diff --git a/tests/python_tests/object_test.py b/tests/python_tests/object_test.py index 3fa05f03e..1e60bfafd 100644 --- a/tests/python_tests/object_test.py +++ b/tests/python_tests/object_test.py @@ -5,8 +5,9 @@ import os from nose.tools import * from utilities import execution_path from utilities import Todo +import tempfile -import mapnik, pickle +import mapnik def setup(): # All of the paths used are relative, if we run the tests @@ -122,17 +123,6 @@ def test_pointsymbolizer_init(): #def test_pointsymbolizer_missing_image(): # p = mapnik.PointSymbolizer(mapnik.PathExpression("../data/images/broken.png")) -# PointSymbolizer pickling -def test_pointsymbolizer_pickle(): - raise Todo("point_symbolizer pickling currently disabled") - p = mapnik.PointSymbolizer(mapnik.PathExpression("../data/images/dummy.png")) - p2 = pickle.loads(pickle.dumps(p,pickle.HIGHEST_PROTOCOL)) - # image type, width, and height only used in contructor... - eq_(p.filename, p2.filename) - eq_(p.allow_overlap, p2.allow_overlap) - eq_(p.opacity, p2.opacity) - eq_(p.ignore_placement, p2.ignore_placement) - eq_(p.placement, p2.placement) # PolygonSymbolizer initialization def test_polygonsymbolizer_init(): @@ -146,17 +136,6 @@ def test_polygonsymbolizer_init(): eq_(p.fill, mapnik.Color('blue')) eq_(p.fill_opacity, 1) -# PolygonSymbolizer pickling -def test_polygonsymbolizer_pickle(): - p = mapnik.PolygonSymbolizer(mapnik.Color('black')) - p.fill_opacity = .5 - # does not work for some reason... - #eq_(pickle.loads(pickle.dumps(p)), p) - p2 = pickle.loads(pickle.dumps(p,pickle.HIGHEST_PROTOCOL)) - eq_(p.fill, p2.fill) - eq_(p.fill_opacity, p2.fill_opacity) - - # Stroke initialization def test_stroke_init(): s = mapnik.Stroke() @@ -187,25 +166,6 @@ def test_stroke_dash_arrays(): eq_(s.get_dashes(), [(1,2),(3,4),(5,6)]) -# Stroke pickling -def test_stroke_pickle(): - s = mapnik.Stroke(mapnik.Color('black'),4.5) - - eq_(s.width, 4.5) - eq_(s.color, mapnik.Color('black')) - - s.add_dash(1,2) - s.add_dash(3,4) - s.add_dash(5,6) - - s2 = pickle.loads(pickle.dumps(s,pickle.HIGHEST_PROTOCOL)) - eq_(s.color, s2.color) - eq_(s.width, s2.width) - eq_(s.opacity, s2.opacity) - eq_(s.get_dashes(), s2.get_dashes()) - eq_(s.line_cap, s2.line_cap) - eq_(s.line_join, s2.line_join) - # LineSymbolizer initialization def test_linesymbolizer_init(): @@ -234,19 +194,6 @@ def test_linesymbolizer_init(): eq_(l.stroke.line_cap, mapnik.line_cap.BUTT_CAP) eq_(l.stroke.line_join, mapnik.line_join.MITER_JOIN) -# LineSymbolizer pickling -def test_linesymbolizer_pickle(): - p = mapnik.LineSymbolizer() - p2 = pickle.loads(pickle.dumps(p,pickle.HIGHEST_PROTOCOL)) - # line and stroke eq fails, so we compare attributes for now.. - s,s2 = p.stroke, p2.stroke - eq_(s.color, s2.color) - eq_(s.opacity, s2.opacity) - eq_(s.width, s2.width) - eq_(s.get_dashes(), s2.get_dashes()) - eq_(s.line_cap, s2.line_cap) - eq_(s.line_join, s2.line_join) - # TextSymbolizer initialization def test_textsymbolizer_init(): @@ -258,56 +205,6 @@ def test_textsymbolizer_init(): eq_(ts.fill, mapnik.Color('black')) eq_(ts.label_placement, mapnik.label_placement.POINT_PLACEMENT) -# TextSymbolizer pickling -def test_textsymbolizer_pickle(): - ts = mapnik.TextSymbolizer(mapnik.Expression('[Field_Name]'), 'Font Name', 8, mapnik.Color('black')) - - eq_(str(ts.name), str(mapnik.Expression('[Field_Name]'))) - eq_(ts.face_name, 'Font Name') - eq_(ts.text_size, 8) - eq_(ts.fill, mapnik.Color('black')) - - raise Todo("text_symbolizer pickling currently disabled") - - ts2 = pickle.loads(pickle.dumps(ts,pickle.HIGHEST_PROTOCOL)) - eq_(ts.name, ts2.name) - eq_(ts.face_name, ts2.face_name) - eq_(ts.allow_overlap, ts2.allow_overlap) - eq_(ts.displacement, ts2.displacement) - eq_(ts.anchor, ts2.anchor) - eq_(ts.fill, ts2.fill) - eq_(ts.force_odd_labels, ts2.force_odd_labels) - eq_(ts.halo_fill, ts2.halo_fill) - eq_(ts.halo_radius, ts2.halo_radius) - eq_(ts.label_placement, ts2.label_placement) - eq_(ts.minimum_distance, ts2.minimum_distance) - eq_(ts.text_ratio, ts2.text_ratio) - eq_(ts.text_size, ts2.text_size) - eq_(ts.wrap_width, ts2.wrap_width) - eq_(ts.vertical_alignment, ts2.vertical_alignment) - eq_(ts.label_spacing, ts2.label_spacing) - eq_(ts.label_position_tolerance, ts2.label_position_tolerance) - # 22.5 * M_PI/180.0 initialized by default - assert_almost_equal(s.max_char_angle_delta, 0.39269908169872414) - - eq_(ts.wrap_character, ts2.wrap_character) - eq_(ts.text_transform, ts2.text_transform) - eq_(ts.line_spacing, ts2.line_spacing) - eq_(ts.character_spacing, ts2.character_spacing) - - # r1341 - eq_(ts.wrap_before, ts2.wrap_before) - eq_(ts.horizontal_alignment, ts2.horizontal_alignment) - eq_(ts.justify_alignment, ts2.justify_alignment) - eq_(ts.opacity, ts2.opacity) - - # r2300 - eq_(s.minimum_padding, 0.0) - - raise Todo("FontSet pickling support needed: http://trac.mapnik.org/ticket/348") - eq_(ts.fontset, ts2.fontset) - - # Map initialization def test_layer_init(): l = mapnik.Layer('test') @@ -365,11 +262,13 @@ def test_map_init_from_string(): eq_(m.base, './') mapnik.load_map_from_string(m, map_string, False, "") # this "" will have no effect eq_(m.base, './') + + tmp_dir = tempfile.gettempdir() try: - mapnik.load_map_from_string(m, map_string, False, "/tmp") + mapnik.load_map_from_string(m, map_string, False, tmp_dir) except RuntimeError: pass # runtime error expected because shapefile path should be wrong and datasource will throw - eq_(m.base, '/tmp') # /tmp will be set despite the exception because load_map mostly worked + eq_(m.base, tmp_dir) # tmp_dir will be set despite the exception because load_map mostly worked m.base = 'foo' mapnik.load_map_from_string(m, map_string, True, ".") eq_(m.base, '.') @@ -379,19 +278,6 @@ def test_map_init_from_string(): if not 'Could not create datasource' in str(e): raise RuntimeError(e) -# Map pickling -def test_map_pickle(): - # Fails due to scale() not matching, possibly other things - raise(Todo("Map does not support pickling yet (Tickets #345).")) - - m = mapnik.Map(256, 256) - - eq_(pickle.loads(pickle.dumps(m)), m) - - m = mapnik.Map(256, 256, '+proj=latlong') - - eq_(pickle.loads(pickle.dumps(m)), m) - # Color initialization @raises(Exception) # Boost.Python.ArgumentError @@ -503,21 +389,7 @@ def test_color_equality(): eq_(c1, mapnik.Color('red')) eq_(c2, mapnik.Color('lime')) eq_(c3, mapnik.Color(0,0,255,128)) - - -# Color pickling -def test_color_pickle(): - c = mapnik.Color('blue') - eq_(pickle.loads(pickle.dumps(c)), c) - - c = mapnik.Color(0, 64, 128) - - eq_(pickle.loads(pickle.dumps(c)), c) - - c = mapnik.Color(0, 64, 128, 192) - - eq_(pickle.loads(pickle.dumps(c)), c) # Rule initialization def test_rule_init(): @@ -678,12 +550,6 @@ def test_envelope_static_init(): eq_(c.x, 150) eq_(c.y, 150) -# Box2d pickling -def test_envelope_pickle(): - e = mapnik.Box2d(100, 100, 200, 200) - - eq_(pickle.loads(pickle.dumps(e)), e) - # Box2d multiplication def test_envelope_multiplication(): e = mapnik.Box2d(100, 100, 200, 200) @@ -716,7 +582,7 @@ def test_envelope_multiplication(): eq_(c.y, 150) # Box2d clipping -def test_envelope_pickle(): +def test_envelope_clipping(): e1 = mapnik.Box2d(-180,-90,180,90) e2 = mapnik.Box2d(-120,40,-110,48) e1.clip(e2) diff --git a/tests/python_tests/parameters_test.py b/tests/python_tests/parameters_test.py new file mode 100644 index 000000000..4188cd8da --- /dev/null +++ b/tests/python_tests/parameters_test.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +from nose.tools import * +from utilities import execution_path + +import mapnik +import pickle + +def setup(): + os.chdir(execution_path('.')) + +def test_parameter(): + p = mapnik.Parameter('key','value') + eq_(p[0],'key') + eq_(p[1],'value') + + p = mapnik.Parameter('int',1) + eq_(p[0],'int') + eq_(p[1],1) + + p = mapnik.Parameter('float',1.0777) + eq_(p[0],'float') + eq_(p[1],1.0777) + + +def test_parameters(): + params = mapnik.Parameters() + p = mapnik.Parameter('float',1.0777) + eq_(p[0],'float') + eq_(p[1],1.0777) + + params.append(p) + + eq_(params[0][0],'float') + eq_(params[0][1],1.0777) + + eq_(params.get('float'),1.0777) + +def test_parameters_pickling(): + params = mapnik.Parameters() + params.append(mapnik.Parameter('oh',str('yeah'))) + + params2 = pickle.loads(pickle.dumps(params,pickle.HIGHEST_PROTOCOL)) + + eq_(params[0][0],params2[0][0]) + eq_(params[0][1],params2[0][1]) + + +if __name__ == "__main__": + setup() + [eval(run)() for run in dir() if 'test_' in run] diff --git a/tests/python_tests/pickling_test.py b/tests/python_tests/pickling_test.py new file mode 100644 index 000000000..a619dd372 --- /dev/null +++ b/tests/python_tests/pickling_test.py @@ -0,0 +1,159 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +from nose.tools import * +from utilities import execution_path +from utilities import Todo +import tempfile + +import mapnik, pickle + +def setup(): + # All of the paths used are relative, if we run the tests + # from another directory we need to chdir() + os.chdir(execution_path('.')) + +# PointSymbolizer pickling +def test_pointsymbolizer_pickle(): + raise Todo("point_symbolizer pickling currently disabled") + p = mapnik.PointSymbolizer(mapnik.PathExpression("../data/images/dummy.png")) + p2 = pickle.loads(pickle.dumps(p,pickle.HIGHEST_PROTOCOL)) + # image type, width, and height only used in contructor... + eq_(p.filename, p2.filename) + eq_(p.allow_overlap, p2.allow_overlap) + eq_(p.opacity, p2.opacity) + eq_(p.ignore_placement, p2.ignore_placement) + eq_(p.placement, p2.placement) + + +# PolygonSymbolizer pickling +def test_polygonsymbolizer_pickle(): + p = mapnik.PolygonSymbolizer(mapnik.Color('black')) + p.fill_opacity = .5 + # does not work for some reason... + #eq_(pickle.loads(pickle.dumps(p)), p) + p2 = pickle.loads(pickle.dumps(p,pickle.HIGHEST_PROTOCOL)) + eq_(p.fill, p2.fill) + eq_(p.fill_opacity, p2.fill_opacity) + + +# Stroke pickling +def test_stroke_pickle(): + s = mapnik.Stroke(mapnik.Color('black'),4.5) + + eq_(s.width, 4.5) + eq_(s.color, mapnik.Color('black')) + + s.add_dash(1,2) + s.add_dash(3,4) + s.add_dash(5,6) + + s2 = pickle.loads(pickle.dumps(s,pickle.HIGHEST_PROTOCOL)) + eq_(s.color, s2.color) + eq_(s.width, s2.width) + eq_(s.opacity, s2.opacity) + eq_(s.get_dashes(), s2.get_dashes()) + eq_(s.line_cap, s2.line_cap) + eq_(s.line_join, s2.line_join) + +# LineSymbolizer pickling +def test_linesymbolizer_pickle(): + p = mapnik.LineSymbolizer() + p2 = pickle.loads(pickle.dumps(p,pickle.HIGHEST_PROTOCOL)) + # line and stroke eq fails, so we compare attributes for now.. + s,s2 = p.stroke, p2.stroke + eq_(s.color, s2.color) + eq_(s.opacity, s2.opacity) + eq_(s.width, s2.width) + eq_(s.get_dashes(), s2.get_dashes()) + eq_(s.line_cap, s2.line_cap) + eq_(s.line_join, s2.line_join) + + + +# TextSymbolizer pickling +def test_textsymbolizer_pickle(): + ts = mapnik.TextSymbolizer(mapnik.Expression('[Field_Name]'), 'Font Name', 8, mapnik.Color('black')) + + eq_(str(ts.name), str(mapnik.Expression('[Field_Name]'))) + eq_(ts.face_name, 'Font Name') + eq_(ts.text_size, 8) + eq_(ts.fill, mapnik.Color('black')) + + raise Todo("text_symbolizer pickling currently disabled") + + ts2 = pickle.loads(pickle.dumps(ts,pickle.HIGHEST_PROTOCOL)) + eq_(ts.name, ts2.name) + eq_(ts.face_name, ts2.face_name) + eq_(ts.allow_overlap, ts2.allow_overlap) + eq_(ts.displacement, ts2.displacement) + eq_(ts.anchor, ts2.anchor) + eq_(ts.fill, ts2.fill) + eq_(ts.force_odd_labels, ts2.force_odd_labels) + eq_(ts.halo_fill, ts2.halo_fill) + eq_(ts.halo_radius, ts2.halo_radius) + eq_(ts.label_placement, ts2.label_placement) + eq_(ts.minimum_distance, ts2.minimum_distance) + eq_(ts.text_ratio, ts2.text_ratio) + eq_(ts.text_size, ts2.text_size) + eq_(ts.wrap_width, ts2.wrap_width) + eq_(ts.vertical_alignment, ts2.vertical_alignment) + eq_(ts.label_spacing, ts2.label_spacing) + eq_(ts.label_position_tolerance, ts2.label_position_tolerance) + # 22.5 * M_PI/180.0 initialized by default + assert_almost_equal(s.max_char_angle_delta, 0.39269908169872414) + + eq_(ts.wrap_character, ts2.wrap_character) + eq_(ts.text_transform, ts2.text_transform) + eq_(ts.line_spacing, ts2.line_spacing) + eq_(ts.character_spacing, ts2.character_spacing) + + # r1341 + eq_(ts.wrap_before, ts2.wrap_before) + eq_(ts.horizontal_alignment, ts2.horizontal_alignment) + eq_(ts.justify_alignment, ts2.justify_alignment) + eq_(ts.opacity, ts2.opacity) + + # r2300 + eq_(s.minimum_padding, 0.0) + + raise Todo("FontSet pickling support needed: http://trac.mapnik.org/ticket/348") + eq_(ts.fontset, ts2.fontset) + + +def test_map_pickle(): + # Fails due to scale() not matching, possibly other things + raise(Todo("Map does not support pickling yet (Tickets #345).")) + + m = mapnik.Map(256, 256) + + eq_(pickle.loads(pickle.dumps(m)), m) + + m = mapnik.Map(256, 256, '+proj=latlong') + + eq_(pickle.loads(pickle.dumps(m)), m) + +def test_color_pickle(): + c = mapnik.Color('blue') + + eq_(pickle.loads(pickle.dumps(c)), c) + + c = mapnik.Color(0, 64, 128) + + eq_(pickle.loads(pickle.dumps(c)), c) + + c = mapnik.Color(0, 64, 128, 192) + + eq_(pickle.loads(pickle.dumps(c)), c) + + +def test_envelope_pickle(): + e = mapnik.Box2d(100, 100, 200, 200) + + eq_(pickle.loads(pickle.dumps(e)), e) + + +if __name__ == "__main__": + setup() + [eval(run)() for run in dir() if 'test_' in run] diff --git a/tests/python_tests/postgis_test.py b/tests/python_tests/postgis_test.py index 0abcf8f0b..6e11693d2 100644 --- a/tests/python_tests/postgis_test.py +++ b/tests/python_tests/postgis_test.py @@ -35,7 +35,7 @@ def psql_can_connect(): call('psql %s -c "select postgis_version()"' % POSTGIS_TEMPLATE_DBNAME) return True except RuntimeError, e: - print 'Notice: skipping postgis tests as basic auth is not correctly set up. Error was: %s' % e.message + print 'Notice: skipping postgis tests (connection)' return False def shp2pgsql_on_path(): @@ -47,7 +47,7 @@ def shp2pgsql_on_path(): call('shp2pgsql') return True except RuntimeError, e: - print 'Notice: skipping postgis tests because shp2pgsql not found. Error was: %s' % e.message + print 'Notice: skipping postgis tests (shp2pgsql)' return False def createdb_and_dropdb_on_path(): @@ -60,7 +60,7 @@ def createdb_and_dropdb_on_path(): call('dropdb --help') return True except RuntimeError, e: - print 'Notice: skipping postgis tests because createdb or dropdb not found. Error was: %s' % e.message + print 'Notice: skipping postgis tests (createdb/dropdb)' return False def postgis_setup(): diff --git a/tests/python_tests/projection_test.py b/tests/python_tests/projection_test.py index 10d43b343..6a538584e 100644 --- a/tests/python_tests/projection_test.py +++ b/tests/python_tests/projection_test.py @@ -2,7 +2,7 @@ from nose.tools import * -import mapnik, pickle +import mapnik # Tests that exercise map projections. diff --git a/tests/python_tests/render_test.py b/tests/python_tests/render_test.py index 2d49fd00d..5f06162b7 100644 --- a/tests/python_tests/render_test.py +++ b/tests/python_tests/render_test.py @@ -2,10 +2,9 @@ # -*- coding: utf-8 -*- from nose.tools import * - +import tempfile import os, mapnik from nose.tools import * - from utilities import execution_path from utilities import Todo @@ -216,7 +215,7 @@ def test_render_points(): p = mapnik.Projection(projs[projdescr]) m.zoom_to_box(p.forward(mapnik.Box2d(ul_lonlat,lr_lonlat))) # Render to SVG so that it can be checked how many points are there with string comparison - svg_file = '/tmp/%s.svg' + svg_file = os.path.join(tempfile.gettempdir(),'%s.svg') mapnik.render_to_file(m, svg_file) num_points_present = len(places_ds.all_features()) svg = open(svg_file,'r').read() diff --git a/tests/python_tests/sqlite_rtree_test.py b/tests/python_tests/sqlite_rtree_test.py index 89f42bf53..b2398773e 100644 --- a/tests/python_tests/sqlite_rtree_test.py +++ b/tests/python_tests/sqlite_rtree_test.py @@ -42,9 +42,14 @@ if 'sqlite' in mapnik.DatasourceCache.instance().plugin_names(): eq_(os.path.exists(index),True) conn = sqlite3.connect(index) cur = conn.cursor() - cur.execute("Select count(*) from idx_%s_GEOMETRY" % TABLE.replace("'","")) - conn.commit() - eq_(cur.fetchone()[0],TOTAL) + try: + cur.execute("Select count(*) from idx_%s_GEOMETRY" % TABLE.replace("'","")) + conn.commit() + eq_(cur.fetchone()[0],TOTAL) + except sqlite3.OperationalError: + # don't worry about testing # of index records if + # python's sqlite module does not support rtree + pass cur.close() ds = mapnik.SQLite(file=DB,table=TABLE) diff --git a/utils/geometry_to_wkb/Jamroot b/utils/geometry_to_wkb/Jamroot new file mode 100644 index 000000000..92c6686a7 --- /dev/null +++ b/utils/geometry_to_wkb/Jamroot @@ -0,0 +1,19 @@ +###################################################################### + +MAPNIK_INCLUDE_DIR = "/opt/mapnik/include" ; +MAPNIK_LIB_DIR = "/opt/mapnik/lib" ; +BOOST_INCLUDE_DIR = "/opt/boost_1_48_0/include" ; + + +lib mapnik : : mapnik $(MAPNIK_LIB_DIR) ; +lib icu : : icuuc /usr/local/lib ; + +exe to_wkb : + main.cpp + .//mapnik + .//icu + : + $(MAPNIK_INCLUDE_DIR) + $(BOOST_INCLUDE_DIR) + ; + diff --git a/utils/geometry_to_wkb/main.cpp b/utils/geometry_to_wkb/main.cpp new file mode 100644 index 000000000..b9072e39f --- /dev/null +++ b/utils/geometry_to_wkb/main.cpp @@ -0,0 +1,109 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2011 Artem Pavlenko + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ + +//$Id$ + +#include +#include + +#include +#include +#include +#include + +#include + + +int main (int argc, char ** argv ) +{ + + if ( argc !=2) + { + std::cerr << "Usage: " << argv[0] << " \n"; + return EXIT_SUCCESS; + } + + std::cerr << "Geometry to WKB converter\n"; + + mapnik::datasource_cache::instance()->register_datasources("/opt/mapnik/lib/mapnik/input/"); + + std::string filename(argv[1]); + std::cerr << filename << std::endl; + + mapnik::parameters p; + p["type"] = "shape"; + p["file"] = filename; + + mapnik::datasource_ptr ds; + + try + { + ds = mapnik::datasource_cache::instance()->create(p); + } + catch ( ... ) + { + std::cerr << "Can't create datasource!\n"; + return EXIT_FAILURE; + } + + if (ds) + { + std::cerr << ds->envelope() << std::endl; + + mapnik::query q(ds->envelope()); + mapnik::layer_descriptor layer_desc = ds->get_descriptor(); + BOOST_FOREACH ( mapnik::attribute_descriptor const& attr_desc, layer_desc.get_descriptors()) + { + q.add_property_name(attr_desc.get_name()); + } + + mapnik::featureset_ptr fs = ds->features(q); + mapnik::feature_ptr f = fs->next(); + + while(f) + { + std::cerr << *f << std::endl; + boost::ptr_vector & paths = f->paths(); + BOOST_FOREACH ( mapnik::geometry_type const& geom, paths) + { + // NDR + { + mapnik::util::wkb_buffer_ptr wkb = mapnik::util::to_wkb(geom,mapnik::util::wkbNDR); + std::cerr << mapnik::util::to_hex(wkb->buffer(),wkb->size()) << std::endl; + } + // XDR + { + mapnik::util::wkb_buffer_ptr wkb = mapnik::util::to_wkb(geom,mapnik::util::wkbXDR); + std::cerr << mapnik::util::to_hex(wkb->buffer(),wkb->size()) << std::endl; + } + } + + f = fs->next(); + } + } + + + + return EXIT_SUCCESS; +} + +