diff --git a/bindings/python/mapnik_layer.cpp b/bindings/python/mapnik_layer.cpp index 04f442508..f0d28cc28 100644 --- a/bindings/python/mapnik_layer.cpp +++ b/bindings/python/mapnik_layer.cpp @@ -103,6 +103,31 @@ void set_maximum_extent(mapnik::layer & l, boost::optional } } +void set_buffer_size(mapnik::layer & l, boost::optional const& buffer_size) +{ + if (buffer_size) + { + l.set_buffer_size(*buffer_size); + } + else + { + l.reset_buffer_size(); + } +} + +PyObject * get_buffer_size(mapnik::layer & l) +{ + boost::optional buffer_size = l.buffer_size(); + if (buffer_size) + { + return PyInt_FromLong(*buffer_size); + } + else + { + Py_RETURN_NONE; + } +} + void export_layer() { using namespace boost::python; @@ -224,17 +249,18 @@ void export_layer() ) .add_property("buffer_size", - &layer::buffer_size, - &layer::set_buffer_size, + &get_buffer_size, + &set_buffer_size, "Get/Set the size of buffer around layer in pixels.\n" "\n" "Usage:\n" - ">>> l.buffer_size\n" - "0 # zero by default\n" + ">>> print(l.buffer_size)\n" + "None # None by default\n" ">>> l.buffer_size = 2\n" ">>> l.buffer_size\n" "2\n" ) + .add_property("maximum_extent",make_function (&layer::maximum_extent,return_value_policy()), &set_maximum_extent, diff --git a/bindings/python/mapnik_python.cpp b/bindings/python/mapnik_python.cpp index 3f6e76daf..666a02292 100644 --- a/bindings/python/mapnik_python.cpp +++ b/bindings/python/mapnik_python.cpp @@ -736,6 +736,7 @@ BOOST_PYTHON_MODULE(_mapnik) python_optional(); python_optional(); python_optional(); + python_optional(); python_optional(); register_ptr_to_python(); register_ptr_to_python(); diff --git a/include/mapnik/feature_style_processor_impl.hpp b/include/mapnik/feature_style_processor_impl.hpp index 48a4e545e..345016426 100644 --- a/include/mapnik/feature_style_processor_impl.hpp +++ b/include/mapnik/feature_style_processor_impl.hpp @@ -239,7 +239,21 @@ void feature_style_processor::apply_to_layer(layer const& lay, Proces } #endif - box2d buffered_query_ext = m_.get_buffered_extent(); // buffered + + box2d query_ext = m_.get_current_extent(); // unbuffered + box2d buffered_query_ext(query_ext); // buffered + + boost::optional layer_buffer_size = lay.buffer_size(); + if (layer_buffer_size) // if layer overrides buffer size, use this value to compute buffered extent + { + double extra = 2.0 * m_.scale() * *layer_buffer_size; + buffered_query_ext.width(query_ext.width() + extra); + buffered_query_ext.height(query_ext.height() + extra); + } + else + { + buffered_query_ext = m_.get_buffered_extent(); + } // clip buffered extent by maximum extent, if supplied boost::optional > const& maximum_extent = m_.maximum_extent(); @@ -310,7 +324,6 @@ void feature_style_processor::apply_to_layer(layer const& lay, Proces // if we've got this far, now prepare the unbuffered extent // which is used as a bbox for clipping geometries - box2d query_ext = m_.get_current_extent(); // unbuffered if (maximum_extent) { query_ext.clip(*maximum_extent); diff --git a/include/mapnik/layer.hpp b/include/mapnik/layer.hpp index 78a17441c..c102b28f1 100644 --- a/include/mapnik/layer.hpp +++ b/include/mapnik/layer.hpp @@ -194,7 +194,8 @@ public: boost::optional > const& maximum_extent() const; void reset_maximum_extent(); void set_buffer_size(int size); - int buffer_size() const; + boost::optional const& buffer_size() const; + void reset_buffer_size(); ~layer(); private: void swap(const layer& other); @@ -211,7 +212,7 @@ private: std::string group_by_; std::vector styles_; datasource_ptr ds_; - int buffer_size_; + boost::optional buffer_size_; boost::optional > maximum_extent_; }; } diff --git a/src/agg/agg_renderer.cpp b/src/agg/agg_renderer.cpp index b7642a7b3..61a43fce7 100644 --- a/src/agg/agg_renderer.cpp +++ b/src/agg/agg_renderer.cpp @@ -181,16 +181,6 @@ void agg_renderer::start_layer_processing(layer const& lay, box2d con } query_extent_ = query_extent; - int buffer_size = lay.buffer_size(); - if (buffer_size != 0 ) - { - double padding = buffer_size * (double)(query_extent.width()/pixmap_.width()); - double x0 = query_extent_.minx(); - double y0 = query_extent_.miny(); - double x1 = query_extent_.maxx(); - double y1 = query_extent_.maxy(); - query_extent_.init(x0 - padding, y0 - padding, x1 + padding , y1 + padding); - } boost::optional > const& maximum_extent = lay.maximum_extent(); if (maximum_extent) diff --git a/src/grid/grid_renderer.cpp b/src/grid/grid_renderer.cpp index 3cd8bf61a..3862dbfd9 100644 --- a/src/grid/grid_renderer.cpp +++ b/src/grid/grid_renderer.cpp @@ -104,16 +104,6 @@ void grid_renderer::start_layer_processing(layer const& lay, box2d co detector_->clear(); } query_extent_ = query_extent; - int buffer_size = lay.buffer_size(); - if (buffer_size != 0 ) - { - double padding = buffer_size * (double)(query_extent.width()/pixmap_.width()); - double x0 = query_extent_.minx(); - double y0 = query_extent_.miny(); - double x1 = query_extent_.maxx(); - double y1 = query_extent_.maxy(); - query_extent_.init(x0 - padding, y0 - padding, x1 + padding , y1 + padding); - } boost::optional > const& maximum_extent = lay.maximum_extent(); if (maximum_extent) diff --git a/src/layer.cpp b/src/layer.cpp index 44abff6fb..61739c7a7 100644 --- a/src/layer.cpp +++ b/src/layer.cpp @@ -42,8 +42,7 @@ layer::layer(std::string const& name, std::string const& srs) clear_label_cache_(false), cache_features_(false), group_by_(""), - ds_(), - buffer_size_(0) {} + ds_() {} layer::layer(const layer& rhs) : name_(rhs.name_), @@ -198,14 +197,20 @@ void layer::reset_maximum_extent() void layer::set_buffer_size(int size) { - buffer_size_ = size; + buffer_size_.reset(size); } -int layer::buffer_size() const +boost::optional const& layer::buffer_size() const { return buffer_size_; } +void layer::reset_buffer_size() +{ + buffer_size_.reset(); +} + + box2d layer::envelope() const { if (ds_) return ds_->envelope(); diff --git a/src/save_map.cpp b/src/save_map.cpp index 55996e471..da2be65d4 100644 --- a/src/save_map.cpp +++ b/src/save_map.cpp @@ -750,10 +750,10 @@ void serialize_layer( ptree & map_node, const layer & layer, bool explicit_defau set_attr( layer_node, "group-by", layer.group_by() ); } - int buffer_size = layer.buffer_size(); + boost::optional const& buffer_size = layer.buffer_size(); if ( buffer_size || explicit_defaults) { - set_attr( layer_node, "buffer-size", buffer_size ); + set_attr( layer_node, "buffer-size", *buffer_size ); } optional > const& maximum_extent = layer.maximum_extent(); diff --git a/tests/data/good_maps/layer_buffer_size_reduction.xml b/tests/data/good_maps/layer_buffer_size_reduction.xml new file mode 100644 index 000000000..36d160afc --- /dev/null +++ b/tests/data/good_maps/layer_buffer_size_reduction.xml @@ -0,0 +1,33 @@ + + + + + point_style + + sqlite + ../sqlite/qgis_spatiallite.sqlite + point + + + + diff --git a/tests/python_tests/images/support/mapnik-layer-buffer-size-cairo.png b/tests/python_tests/images/support/mapnik-layer-buffer-size-cairo.png new file mode 100644 index 000000000..c7899c16c Binary files /dev/null and b/tests/python_tests/images/support/mapnik-layer-buffer-size-cairo.png differ diff --git a/tests/python_tests/images/support/mapnik-layer-buffer-size.png b/tests/python_tests/images/support/mapnik-layer-buffer-size.png new file mode 100644 index 000000000..6d53eecef Binary files /dev/null and b/tests/python_tests/images/support/mapnik-layer-buffer-size.png differ diff --git a/tests/python_tests/layer_buffer_size_test.py b/tests/python_tests/layer_buffer_size_test.py new file mode 100644 index 000000000..4b6fb1ff4 --- /dev/null +++ b/tests/python_tests/layer_buffer_size_test.py @@ -0,0 +1,42 @@ +#coding=utf8 +import os +import mapnik +import cairo +from utilities import execution_path +from nose.tools import * + +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_layer_buffer_size_1(): + m = mapnik.Map(512,512) + mapnik.load_map(m,'../data/good_maps/layer_buffer_size_reduction.xml') + m.zoom_all() + im = mapnik.Image(m.width,m.height) + mapnik.render(m,im) + actual = '/tmp/mapnik-layer-buffer-size.png' + expected = 'images/support/mapnik-layer-buffer-size.png' + im.save(actual) + expected_im = mapnik.Image.open(expected) + eq_(im.tostring(),expected_im.tostring(), 'failed comparing actual (%s) and expected (%s)' % (actual,'tests/python_tests/'+ expected)) + +def test_layer_buffer_size_2(): + actual = '/tmp/mapnik-layer-buffer-size-cairo.png' + expected = 'images/support/mapnik-layer-buffer-size-cairo.png' + m = mapnik.Map(512,512) + mapnik.load_map(m,'../data/good_maps/layer_buffer_size_reduction.xml') + m.zoom_all() + surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, m.width, m.height) + mapnik.render(m, surface) + surface.write_to_png(actual) + surface.finish() + expected_im = mapnik.Image.open(expected) + actual_im = mapnik.Image.open(actual) + eq_(actual_im.tostring(),expected_im.tostring(), 'failed comparing actual (%s) and expected (%s)' % (actual,'tests/python_tests/'+ expected)) + + +if __name__ == "__main__": + setup() + [eval(run)() for run in dir() if 'test_' in run] diff --git a/tests/python_tests/layer_test.py b/tests/python_tests/layer_test.py index 5e23fd684..e27d4b98f 100644 --- a/tests/python_tests/layer_test.py +++ b/tests/python_tests/layer_test.py @@ -20,7 +20,7 @@ def test_layer_init(): eq_(l.maxzoom > 1e+6,True) eq_(l.group_by,"") eq_(l.maximum_extent,None) - eq_(l.buffer_size,0.0) + eq_(l.buffer_size,None) eq_(len(l.styles),0) if __name__ == "__main__":