diff --git a/.gitignore b/.gitignore index 5d2536c3e..fe40dfa29 100644 --- a/.gitignore +++ b/.gitignore @@ -43,4 +43,11 @@ demo/c++/demo.tif demo/c++/demo.jpg demo/c++/demo.png demo/c++/demo256.png +demo/viewer/Makefile +demo/viewer/Makefile.Debug +demo/viewer/Makefile.Release +demo/viewer/release/ +demo/viewer/ui_about.h +demo/viewer/ui_info.h +demo/viewer/ui_layer_info.h tests/cpp_tests/*-bin diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a8a48d2c..f6fce3b26 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,10 @@ For a complete change history, see the git log. Not yet released +- Added support for overriding fill, stroke, and opacity for svg markers using marker properties + +- Added support for setting opacity dynamically on images in polygon pattern and markers symbolizers + - Added support for filtering on a features geometry type, either `point`, `linestring`, 'polygon`, or `collection` using the expression keyword of `[mapnik::geometry_type]` (#546) diff --git a/bindings/python/mapnik_layer.cpp b/bindings/python/mapnik_layer.cpp index 0b2e8d63b..ae99e5971 100644 --- a/bindings/python/mapnik_layer.cpp +++ b/bindings/python/mapnik_layer.cpp @@ -91,6 +91,18 @@ struct layer_pickle_suite : boost::python::pickle_suite std::vector & (mapnik::layer::*_styles_)() = &mapnik::layer::styles; +void set_maximum_extent(mapnik::layer & l, boost::optional > const& box) +{ + if (box) + { + l.set_maximum_extent(*box); + } + else + { + l.reset_maximum_extent(); + } +} + void export_layer() { using namespace boost::python; @@ -196,6 +208,27 @@ void export_layer() "\n" ) + .add_property("buffer_size", + &layer::buffer_size, + &layer::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" + ">>> 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, + "The maximum extent of the map.\n" + "\n" + "Usage:\n" + ">>> m.maximum_extent = Box2d(-180,-90,180,90)\n" + ) + .add_property("maxzoom", &layer::max_zoom, &layer::set_max_zoom, diff --git a/bindings/python/mapnik_line_symbolizer.cpp b/bindings/python/mapnik_line_symbolizer.cpp index e7c9a58be..e87c52bec 100644 --- a/bindings/python/mapnik_line_symbolizer.cpp +++ b/bindings/python/mapnik_line_symbolizer.cpp @@ -57,7 +57,7 @@ void export_line_symbolizer() "Set/get the rasterization method of the line of the point") .add_property("stroke",make_function (&line_symbolizer::get_stroke, - return_value_policy()), + return_value_policy()), &line_symbolizer::set_stroke) .add_property("smooth", &line_symbolizer::smooth, diff --git a/bindings/python/mapnik_map.cpp b/bindings/python/mapnik_map.cpp index 3bc1ca983..e2a21e674 100644 --- a/bindings/python/mapnik_map.cpp +++ b/bindings/python/mapnik_map.cpp @@ -117,7 +117,7 @@ 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::*params_nonconst)() = &Map::get_extra_parameters; -boost::optional > const& (Map::*maximum_extent_const)() const = &Map::maximum_extent; +//boost::optional > const& (Map::*maximum_extent_const)() const = &Map::maximum_extent; mapnik::feature_type_style find_style(mapnik::Map const& m, std::string const& name) { @@ -192,7 +192,6 @@ mapnik::Map map_deepcopy(mapnik::Map & m, boost::python::dict memo) return result; } -// TODO - find a simplier way to set optional to uninitialized void set_maximum_extent(mapnik::Map & m, boost::optional > const& box) { if (box) @@ -201,7 +200,7 @@ void set_maximum_extent(mapnik::Map & m, boost::optional > } else { - m.maximum_extent().reset(); + m.reset_maximum_extent(); } } @@ -562,7 +561,7 @@ void export_map() ) .add_property("maximum_extent",make_function - (maximum_extent_const,return_value_policy()), + (&Map::maximum_extent,return_value_policy()), &set_maximum_extent, "The maximum extent of the map.\n" "\n" diff --git a/bindings/python/mapnik_markers_symbolizer.cpp b/bindings/python/mapnik_markers_symbolizer.cpp index 698569378..2ab881c05 100644 --- a/bindings/python/mapnik_markers_symbolizer.cpp +++ b/bindings/python/mapnik_markers_symbolizer.cpp @@ -86,13 +86,13 @@ struct markers_symbolizer_pickle_suite : boost::python::pickle_suite }; - void export_markers_symbolizer() { using namespace boost::python; mapnik::enumeration_("marker_placement") .value("POINT_PLACEMENT",mapnik::MARKER_POINT_PLACEMENT) + .value("INTERIOR_PLACEMENT",mapnik::MARKER_INTERIOR_PLACEMENT) .value("LINE_PLACEMENT",mapnik::MARKER_LINE_PLACEMENT) ; @@ -115,7 +115,11 @@ void export_markers_symbolizer() .add_property("opacity", &markers_symbolizer::get_opacity, &markers_symbolizer::set_opacity, - "Set/get the text opacity") + "Set/get the overall opacity") + .add_property("fill_opacity", + &markers_symbolizer::get_fill_opacity, + &markers_symbolizer::set_fill_opacity, + "Set/get the fill opacity") .add_property("ignore_placement", &markers_symbolizer::get_ignore_placement, &markers_symbolizer::set_ignore_placement) diff --git a/bindings/python/mapnik_python.cpp b/bindings/python/mapnik_python.cpp index b86a7e33d..71231aa25 100644 --- a/bindings/python/mapnik_python.cpp +++ b/bindings/python/mapnik_python.cpp @@ -608,12 +608,14 @@ BOOST_PYTHON_MODULE(_mapnik) def("has_cairo", &has_cairo, "Get cairo library status"); def("has_pycairo", &has_pycairo, "Get pycairo module status"); + python_optional(); python_optional(); python_optional >(); python_optional(); python_optional(); python_optional(); python_optional(); + python_optional(); python_optional(); python_optional(); register_ptr_to_python(); diff --git a/demo/viewer/main.cpp b/demo/viewer/main.cpp index 905844720..c5cb4ad4c 100644 --- a/demo/viewer/main.cpp +++ b/demo/viewer/main.cpp @@ -36,52 +36,59 @@ int main( int argc, char **argv ) using mapnik::datasource_cache; using mapnik::freetype_engine; - QCoreApplication::setOrganizationName("Mapnik"); - QCoreApplication::setOrganizationDomain("mapnik.org"); - QCoreApplication::setApplicationName("Viewer"); - - QSettings settings("viewer.ini",QSettings::IniFormat); - - // register input plug-ins - QString plugins_dir = settings.value("mapnik/plugins_dir", - QVariant("/usr/local/lib/mapnik/input/")).toString(); - datasource_cache::instance()->register_datasources(plugins_dir.toStdString()); - // register fonts - int count = settings.beginReadArray("mapnik/fonts"); - for (int index=0; index < count; ++index) + try { - settings.setArrayIndex(index); - QString font_dir = settings.value("dir").toString(); - freetype_engine::register_fonts(font_dir.toStdString()); - } - settings.endArray(); + QCoreApplication::setOrganizationName("Mapnik"); + QCoreApplication::setOrganizationDomain("mapnik.org"); + QCoreApplication::setApplicationName("Viewer"); + QSettings settings("viewer.ini",QSettings::IniFormat); - QApplication app( argc, argv ); - MainWindow window; - window.show(); - if (argc > 1) window.open(argv[1]); - if (argc >= 3) - { - QStringList list = QString(argv[2]).split(","); - if (list.size()==4) + // register input plug-ins + QString plugins_dir = settings.value("mapnik/plugins_dir", + QVariant("/usr/local/lib/mapnik/input/")).toString(); + datasource_cache::instance()->register_datasources(plugins_dir.toStdString()); + // register fonts + int count = settings.beginReadArray("mapnik/fonts"); + for (int index=0; index < count; ++index) + { + settings.setArrayIndex(index); + QString font_dir = settings.value("dir").toString(); + freetype_engine::register_fonts(font_dir.toStdString()); + } + settings.endArray(); + + QApplication app( argc, argv ); + MainWindow window; + window.show(); + if (argc > 1) window.open(argv[1]); + if (argc >= 3) + { + QStringList list = QString(argv[2]).split(","); + if (list.size()==4) + { + bool ok; + double x0 = list[0].toDouble(&ok); + double y0 = list[1].toDouble(&ok); + double x1 = list[2].toDouble(&ok); + double y1 = list[3].toDouble(&ok); + if (ok) window.set_default_extent(x0,y0,x1,y1); + } + } + else + { + window.zoom_all(); + } + if (argc == 4) { bool ok; - double x0 = list[0].toDouble(&ok); - double y0 = list[1].toDouble(&ok); - double x1 = list[2].toDouble(&ok); - double y1 = list[3].toDouble(&ok); - if (ok) window.set_default_extent(x0,y0,x1,y1); + double scaling_factor = QString(argv[3]).toDouble(&ok); + if (ok) window.set_scaling_factor(scaling_factor); } + return app.exec(); } - else + catch (std::exception const& ex) { - window.zoom_all(); + std::cerr << "Could not start viewer: '" << ex.what() << "'\n"; + return 1; } - if (argc == 4) - { - bool ok; - double scaling_factor = QString(argv[3]).toDouble(&ok); - if (ok) window.set_scaling_factor(scaling_factor); - } - return app.exec(); } diff --git a/deps/agg/include/agg_pixfmt_rgba.h b/deps/agg/include/agg_pixfmt_rgba.h index a134eb6fc..127638dc9 100644 --- a/deps/agg/include/agg_pixfmt_rgba.h +++ b/deps/agg/include/agg_pixfmt_rgba.h @@ -1015,18 +1015,34 @@ namespace agg // Dca' = Sa.(Sca.Da + Dca.Sa - Sa.Da)/Sca + Sca.(1 - Da) + Dca.(1 - Sa) // // Da' = Sa + Da - Sa.Da + + + // http://www.w3.org/TR/SVGCompositing/ + // if Sca == 0 and Dca == Da + // Dca' = Sa × Da + Sca × (1 - Da) + Dca × (1 - Sa) + // = Sa × Da + Dca × (1 - Sa) + // = Da = Dca + // otherwise if Sca == 0 + // Dca' = Sca × (1 - Da) + Dca × (1 - Sa) + // = Dca × (1 - Sa) + // otherwise if Sca > 0 + // Dca' = Sa × Da - Sa × Da × min(1, (1 - Dca/Da) × Sa/Sca) + Sca × (1 - Da) + Dca × (1 - Sa) + // = Sa × Da × (1 - min(1, (1 - Dca/Da) × Sa/Sca)) + Sca × (1 - Da) + Dca × (1 - Sa) + + // sa * da * (255 - std::min(255, (255 - p[0]/da)*(sa/(sc*sa)) + static AGG_INLINE void blend_pix(value_type* p, unsigned sr, unsigned sg, unsigned sb, unsigned sa, unsigned cover) { - if(cover < 255) + if (cover < 255) { sr = (sr * cover + 255) >> 8; sg = (sg * cover + 255) >> 8; sb = (sb * cover + 255) >> 8; sa = (sa * cover + 255) >> 8; } - if(sa) + + if (sa) { calc_type d1a = base_mask - p[Order::A]; calc_type s1a = base_mask - sa; @@ -1042,15 +1058,15 @@ namespace agg long_type sbda = sb * da; long_type sada = sa * da; - p[Order::R] = (value_type)(((srda + drsa <= sada) ? + if ( sr > 0) p[Order::R] = (value_type)(((srda + drsa <= sada) ? sr * d1a + dr * s1a : sa * (srda + drsa - sada) / sr + sr * d1a + dr * s1a + base_mask) >> base_shift); - p[Order::G] = (value_type)(((sgda + dgsa <= sada) ? + if ( sg > 0 ) p[Order::G] = (value_type)(((sgda + dgsa <= sada) ? sg * d1a + dg * s1a : sa * (sgda + dgsa - sada) / sg + sg * d1a + dg * s1a + base_mask) >> base_shift); - p[Order::B] = (value_type)(((sbda + dbsa <= sada) ? + if ( sb > 0) p[Order::B] = (value_type)(((sbda + dbsa <= sada) ? sb * d1a + db * s1a : sa * (sbda + dbsa - sada) / sb + sb * d1a + db * s1a + base_mask) >> base_shift); @@ -1502,24 +1518,29 @@ namespace agg unsigned sr, unsigned sg, unsigned sb, unsigned sa, unsigned cover) { - if (cover < 255) - { - sr = (sr * cover + 255) >> 8; - sg = (sg * cover + 255) >> 8; - sb = (sb * cover + 255) >> 8; - sa = (sa * cover + 255) >> 8; - } - if (sa > 0) - { - calc_type da = p[Order::A]; - int dr = p[Order::R] - sr + 128; - int dg = p[Order::G] - sg + 128; - int db = p[Order::B] - sb + 128; + calc_type da = (p[Order::A] * sa + 255) >> 8; - p[Order::R] = dr < 0 ? 0 : (dr > 255 ? 255 : dr); - p[Order::G] = dg < 0 ? 0 : (dg > 255 ? 255 : dg); - p[Order::B] = db < 0 ? 0 : (db > 255 ? 255 : db); - p[Order::A] = (value_type)(sa + da - ((sa * da + base_mask) >> base_shift)); + int dr = p[Order::R] - sr + 128; + int dg = p[Order::G] - sg + 128; + int db = p[Order::B] - sb + 128; + + dr = dr < 0 ? 0 : (dr > 255 ? 255 : dr); + dg = dg < 0 ? 0 : (dg > 255 ? 255 : dg); + db = db < 0 ? 0 : (db > 255 ? 255 : db); + + p[Order::A] = da; + + if (da < 255) + { + p[Order::R] = (dr * da + 255) >> 8; + p[Order::G] = (dg * da + 255) >> 8; + p[Order::B] = (db * da + 255) >> 8; + } + else + { + p[Order::R] = dr; + p[Order::G] = dg; + p[Order::B] = db; } } }; diff --git a/include/mapnik/agg_renderer.hpp b/include/mapnik/agg_renderer.hpp index 7bbaee16a..1bb85a615 100644 --- a/include/mapnik/agg_renderer.hpp +++ b/include/mapnik/agg_renderer.hpp @@ -127,6 +127,7 @@ protected: double x, double y, double angle = 0.0); void debug_draw_box(box2d const& extent, double x, double y, double angle = 0.0); + void draw_geo_extent(box2d const& extent,mapnik::color const& color); private: buffer_type & pixmap_; diff --git a/include/mapnik/attribute_collector.hpp b/include/mapnik/attribute_collector.hpp index f814d1ac5..81cdad888 100644 --- a/include/mapnik/attribute_collector.hpp +++ b/include/mapnik/attribute_collector.hpp @@ -249,7 +249,7 @@ public: struct directive_collector : public boost::static_visitor<> { - directive_collector(double * filter_factor) + directive_collector(double & filter_factor) : filter_factor_(filter_factor) {} template @@ -257,10 +257,10 @@ struct directive_collector : public boost::static_visitor<> void operator () (raster_symbolizer const& sym) { - *filter_factor_ = sym.calculate_filter_factor(); + filter_factor_ = sym.calculate_filter_factor(); } private: - double * filter_factor_; + double & filter_factor_; }; } // namespace mapnik diff --git a/include/mapnik/box2d.hpp b/include/mapnik/box2d.hpp index 828106901..113a69378 100644 --- a/include/mapnik/box2d.hpp +++ b/include/mapnik/box2d.hpp @@ -110,7 +110,7 @@ operator << (std::basic_ostream& out, std::basic_ostringstream s; s.copyfmt(out); s.width(0); - s << "box2d(" << std::setprecision(16) + s << "box2d(" << std::fixed << std::setprecision(16) << e.minx() << ',' << e.miny() << ',' << e.maxx() << ',' << e.maxy() << ')'; out << s.str(); diff --git a/include/mapnik/ctrans.hpp b/include/mapnik/ctrans.hpp index a06872422..a56bc4bc4 100644 --- a/include/mapnik/ctrans.hpp +++ b/include/mapnik/ctrans.hpp @@ -52,34 +52,25 @@ struct MAPNIK_DECL coord_transform : t_(0), geom_(geom), prj_trans_(0) {} - + void set_proj_trans(proj_transform const& prj_trans) { prj_trans_ = &prj_trans; } - + void set_trans(Transform const& t) { t_ = &t; } - + unsigned vertex(double *x, double *y) const { - unsigned command = SEG_MOVETO; - bool ok = false; - bool skipped_points = false; - double z = 0; - while (!ok && command != SEG_END) + unsigned command = geom_.vertex(x, y); + if ( command != SEG_END) { - command = geom_.vertex(x, y); - ok = prj_trans_->backward(*x, *y, z); - if (!ok) { - skipped_points = true; - } - } - if (skipped_points && (command == SEG_LINETO)) - { - command = SEG_MOVETO; + double z = 0; + if (!prj_trans_->backward(*x, *y, z)) + return SEG_END; } t_->forward(x, y); return command; diff --git a/include/mapnik/geom_util.hpp b/include/mapnik/geom_util.hpp index 31432aa33..c3dda724b 100644 --- a/include/mapnik/geom_util.hpp +++ b/include/mapnik/geom_util.hpp @@ -241,6 +241,8 @@ double path_length(PathType & path) return length; } +namespace label { + template bool middle_point(PathType & path, double & x, double & y) { @@ -271,8 +273,6 @@ bool middle_point(PathType & path, double & x, double & y) return true; } -namespace label { - template bool centroid(PathType & path, double & x, double & y) { diff --git a/include/mapnik/grid/grid_renderer.hpp b/include/mapnik/grid/grid_renderer.hpp index 53e0b786a..6817d1c83 100644 --- a/include/mapnik/grid/grid_renderer.hpp +++ b/include/mapnik/grid/grid_renderer.hpp @@ -118,9 +118,10 @@ private: CoordTransform t_; freetype_engine font_engine_; face_manager font_manager_; - label_collision_detector4 detector_; + boost::shared_ptr detector_; boost::scoped_ptr ras_ptr; box2d query_extent_; + void setup(Map const& m); }; } diff --git a/include/mapnik/layer.hpp b/include/mapnik/layer.hpp index ab0eb4a8e..1be1b23c0 100644 --- a/include/mapnik/layer.hpp +++ b/include/mapnik/layer.hpp @@ -43,7 +43,9 @@ namespace mapnik class MAPNIK_DECL layer { public: - explicit layer(std::string const& name, std::string const& srs="+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs"); + layer(std::string const& name, + std::string const& srs="+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs"); + layer(layer const& l); layer& operator=(layer const& l); bool operator==(layer const& other) const; @@ -57,7 +59,7 @@ public: * @return the name of the layer. */ - const std::string& name() const; + std::string const& name() const; /*! * @brief Set the SRS of the layer. @@ -188,6 +190,11 @@ public: */ box2d envelope() const; + void set_maximum_extent(box2d const& box); + boost::optional > const& maximum_extent() const; + void reset_maximum_extent(); + void set_buffer_size(int size); + int buffer_size() const; ~layer(); private: void swap(const layer& other); @@ -204,6 +211,8 @@ private: std::string group_by_; std::vector styles_; datasource_ptr ds_; + int buffer_size_; + boost::optional > maximum_extent_; }; } diff --git a/include/mapnik/map.hpp b/include/mapnik/map.hpp index e79934e8a..89badafe5 100644 --- a/include/mapnik/map.hpp +++ b/include/mapnik/map.hpp @@ -332,15 +332,13 @@ public: /*! \brief Set the map maximum extent. * @param box The bounding box for the maximum extent. */ - void set_maximum_extent(box2dconst& box); + void set_maximum_extent(box2d const& box); /*! \brief Get the map maximum extent as box2d */ boost::optional > const& maximum_extent() const; - /*! \brief Get the non-const map maximum extent as box2d - */ - boost::optional > & maximum_extent(); + void reset_maximum_extent(); /*! \brief Get the map base path where paths should be relative to. */ diff --git a/include/mapnik/marker.hpp b/include/mapnik/marker.hpp index d26819892..c882727a3 100644 --- a/include/mapnik/marker.hpp +++ b/include/mapnik/marker.hpp @@ -49,13 +49,13 @@ namespace mapnik typedef agg::pod_bvector attr_storage; typedef mapnik::svg::svg_storage svg_storage_type; -typedef boost::shared_ptr path_ptr; +typedef boost::shared_ptr svg_path_ptr; typedef boost::shared_ptr image_ptr; /** * A class to hold either vector or bitmap marker data. This allows these to be treated equally * in the image caches and most of the render paths. */ -class marker +class marker: private boost::noncopyable { public: marker() @@ -65,20 +65,21 @@ public: (*bitmap_data_)->set(0xff000000); } - marker(const boost::optional &data) + marker(boost::optional const& data) : bitmap_data_(data) { } - marker(const boost::optional &data) + marker(boost::optional const& data) : vector_data_(data) { } - marker(const marker& rhs) - : bitmap_data_(rhs.bitmap_data_), vector_data_(rhs.vector_data_) + marker(marker const& rhs) + : bitmap_data_(rhs.bitmap_data_), + vector_data_(rhs.vector_data_) {} box2d bounding_box() const @@ -128,17 +129,14 @@ public: return bitmap_data_; } - boost::optional get_vector_data() const + boost::optional get_vector_data() const { return vector_data_; } - private: - marker& operator=(const marker&); - boost::optional bitmap_data_; - boost::optional vector_data_; + boost::optional vector_data_; }; diff --git a/include/mapnik/marker_helpers.hpp b/include/mapnik/marker_helpers.hpp index efc565fc2..54a1201fe 100644 --- a/include/mapnik/marker_helpers.hpp +++ b/include/mapnik/marker_helpers.hpp @@ -27,39 +27,359 @@ #include #include #include +#include +#include // for svg_storage_type +#include +#include + +// agg +#include "agg_ellipse.h" +#include "agg_basics.h" +#include "agg_renderer_base.h" +#include "agg_renderer_scanline.h" +#include "agg_rendering_buffer.h" +#include "agg_scanline_u.h" +#include "agg_image_filters.h" +#include "agg_trans_bilinear.h" +#include "agg_span_allocator.h" +#include "agg_image_accessors.h" +#include "agg_pixfmt_rgba.h" +#include "agg_span_image_filter_rgba.h" // boost #include namespace mapnik { + +template +struct vector_markers_rasterizer_dispatch +{ + typedef typename SvgRenderer::renderer_base renderer_base; + typedef typename renderer_base::pixfmt_type pixfmt_type; + + vector_markers_rasterizer_dispatch(BufferType & image_buffer, + SvgRenderer & svg_renderer, + Rasterizer & ras, + box2d const& bbox, + agg::trans_affine const& marker_trans, + markers_symbolizer const& sym, + Detector & detector, + double scale_factor) + : buf_(image_buffer.raw_data(), image_buffer.width(), image_buffer.height(), image_buffer.width() * 4), + pixf_(buf_), + renb_(pixf_), + svg_renderer_(svg_renderer), + ras_(ras), + bbox_(bbox), + marker_trans_(marker_trans), + sym_(sym), + detector_(detector), + scale_factor_(scale_factor) + { + pixf_.comp_op(static_cast(sym_.comp_op())); + } + + template + void add_path(T & path) + { + marker_placement_e placement_method = sym_.get_marker_placement(); + + if (placement_method != MARKER_LINE_PLACEMENT) + { + double x,y; + path.rewind(0); + if (placement_method == MARKER_INTERIOR_PLACEMENT) + { + label::interior_position(path, x, y); + } + else + { + label::centroid(path, x, y); + } + agg::trans_affine matrix = marker_trans_; + matrix.translate(x,y); + box2d transformed_bbox = bbox_ * matrix; + + if (sym_.get_allow_overlap() || + detector_.has_placement(transformed_bbox)) + { + svg_renderer_.render(ras_, sl_, renb_, matrix, 1, bbox_); + + if (!sym_.get_ignore_placement()) + detector_.insert(transformed_bbox); + } + } + else + { + markers_placement placement(path, bbox_, marker_trans_, detector_, + sym_.get_spacing() * scale_factor_, + sym_.get_max_error(), + sym_.get_allow_overlap()); + double x, y, angle; + while (placement.get_point(x, y, angle)) + { + agg::trans_affine matrix = marker_trans_; + matrix.rotate(angle); + matrix.translate(x, y); + svg_renderer_.render(ras_, sl_, renb_, matrix, 1, bbox_); + } + } + } +private: + agg::scanline_u8 sl_; + agg::rendering_buffer buf_; + pixfmt_type pixf_; + renderer_base renb_; + SvgRenderer & svg_renderer_; + Rasterizer & ras_; + box2d const& bbox_; + agg::trans_affine const& marker_trans_; + markers_symbolizer const& sym_; + Detector & detector_; + double scale_factor_; +}; + +template +void render_raster_marker(Rasterizer & ras, RendererBuffer & renb, + agg::scanline_u8 & sl, image_data_32 const& src, + agg::trans_affine const& marker_tr, double opacity) +{ + double width = src.width(); + double height = src.height(); + double p[8]; + p[0] = 0; p[1] = 0; + p[2] = width; p[3] = 0; + p[4] = width; p[5] = height; + p[6] = 0; p[7] = height; + + marker_tr.transform(&p[0], &p[1]); + marker_tr.transform(&p[2], &p[3]); + marker_tr.transform(&p[4], &p[5]); + marker_tr.transform(&p[6], &p[7]); + + ras.move_to_d(p[0],p[1]); + ras.line_to_d(p[2],p[3]); + ras.line_to_d(p[4],p[5]); + ras.line_to_d(p[6],p[7]); + + typedef agg::rgba8 color_type; + agg::span_allocator sa; + agg::image_filter_bilinear filter_kernel; + agg::image_filter_lut filter(filter_kernel, false); + + agg::rendering_buffer marker_buf((unsigned char *)src.getBytes(), + src.width(), + src.height(), + src.width()*4); + agg::pixfmt_rgba32_pre pixf(marker_buf); + + typedef agg::image_accessor_clone img_accessor_type; + typedef agg::span_interpolator_linear interpolator_type; + typedef agg::span_image_filter_rgba_2x2 span_gen_type; + typedef agg::order_rgba order_type; + typedef agg::pixel32_type pixel_type; + typedef agg::comp_op_adaptor_rgba_pre blender_type; // comp blender + typedef agg::pixfmt_custom_blend_rgba pixfmt_comp_type; + typedef agg::renderer_base renderer_base; + typedef agg::renderer_scanline_aa_alpha, + span_gen_type> renderer_type; + img_accessor_type ia(pixf); + interpolator_type interpolator(agg::trans_affine(p, 0, 0, width, height) ); + span_gen_type sg(ia, interpolator, filter); + renderer_type rp(renb,sa, sg, unsigned(opacity*255)); + agg::render_scanlines(ras, sl, rp); +} + +template +struct raster_markers_rasterizer_dispatch +{ + typedef agg::rgba8 color_type; + typedef agg::order_rgba order_type; + typedef agg::pixel32_type pixel_type; + typedef agg::comp_op_adaptor_rgba_pre blender_type; // comp blender + typedef agg::pixfmt_custom_blend_rgba pixfmt_comp_type; + typedef agg::renderer_base renderer_base; + + raster_markers_rasterizer_dispatch(BufferType & image_buffer, + Rasterizer & ras, + image_data_32 const& src, + agg::trans_affine const& marker_trans, + markers_symbolizer const& sym, + Detector & detector, + double scale_factor) + : buf_(image_buffer.raw_data(), image_buffer.width(), image_buffer.height(), image_buffer.width() * 4), + pixf_(buf_), + renb_(pixf_), + ras_(ras), + src_(src), + marker_trans_(marker_trans), + sym_(sym), + detector_(detector), + scale_factor_(scale_factor) + { + pixf_.comp_op(static_cast(sym_.comp_op())); + } + + template + void add_path(T & path) + { + marker_placement_e placement_method = sym_.get_marker_placement(); + box2d bbox_(0,0, src_.width(),src_.height()); + + if (placement_method != MARKER_LINE_PLACEMENT) + { + double x,y; + path.rewind(0); + if (placement_method == MARKER_INTERIOR_PLACEMENT) + { + label::interior_position(path, x, y); + } + else + { + label::centroid(path, x, y); + } + agg::trans_affine matrix = marker_trans_; + matrix.translate(x,y); + box2d transformed_bbox = bbox_ * matrix; + + if (sym_.get_allow_overlap() || + detector_.has_placement(transformed_bbox)) + { + + float opacity = sym_.get_opacity() ? *sym_.get_opacity() : 1; + render_raster_marker(ras_, renb_, sl_, src_, + matrix, opacity); + if (!sym_.get_ignore_placement()) + detector_.insert(transformed_bbox); + } + } + else + { + markers_placement placement(path, bbox_, marker_trans_, detector_, + sym_.get_spacing() * scale_factor_, + sym_.get_max_error(), + sym_.get_allow_overlap()); + double x, y, angle; + while (placement.get_point(x, y, angle)) + { + agg::trans_affine matrix = marker_trans_; + matrix.rotate(angle); + matrix.translate(x,y); + float opacity = sym_.get_opacity() ? *sym_.get_opacity() : 1; + render_raster_marker(ras_, renb_, sl_, src_, + matrix, opacity); + } + } + } +private: + agg::scanline_u8 sl_; + agg::rendering_buffer buf_; + pixfmt_comp_type pixf_; + renderer_base renb_; + Rasterizer & ras_; + image_data_32 const& src_; + agg::trans_affine const& marker_trans_; + markers_symbolizer const& sym_; + Detector & detector_; + double scale_factor_; +}; + + +template +void build_ellipse(T const& sym, mapnik::feature_impl const& feature, svg_storage_type & marker_ellipse, svg::svg_path_adapter & svg_path) +{ + expression_ptr const& width_expr = sym.get_width(); + expression_ptr const& height_expr = sym.get_height(); + double width = 0; + double height = 0; + if (width_expr && height_expr) + { + width = boost::apply_visitor(evaluate(feature), *width_expr).to_double(); + height = boost::apply_visitor(evaluate(feature), *height_expr).to_double(); + } + else if (width_expr) + { + width = boost::apply_visitor(evaluate(feature), *width_expr).to_double(); + height = width; + } + else if (height_expr) + { + height = boost::apply_visitor(evaluate(feature), *height_expr).to_double(); + width = height; + } + svg::svg_converter_type styled_svg(svg_path, marker_ellipse.attributes()); + styled_svg.push_attr(); + styled_svg.begin_path(); + agg::ellipse c(0, 0, width/2.0, height/2.0); + styled_svg.storage().concat_path(c); + styled_svg.end_path(); + styled_svg.pop_attr(); + double lox,loy,hix,hiy; + styled_svg.bounding_rect(&lox, &loy, &hix, &hiy); + marker_ellipse.set_bounding_box(lox,loy,hix,hiy); +} + template bool push_explicit_style(Attr const& src, Attr & dst, markers_symbolizer const& sym) { boost::optional const& strk = sym.get_stroke(); boost::optional const& fill = sym.get_fill(); - if (strk || fill) + boost::optional const& opacity = sym.get_opacity(); + boost::optional const& fill_opacity = sym.get_fill_opacity(); + if (strk || fill || opacity || fill_opacity) { + bool success = false; for(unsigned i = 0; i < src.size(); ++i) { - mapnik::svg::path_attributes attr = src[i]; - - if (strk && attr.stroke_flag) + success = true; + dst.push_back(src[i]); + mapnik::svg::path_attributes & attr = dst.last(); + if (attr.stroke_flag) { - attr.stroke_width = strk->get_width(); - color const& s_color = strk->get_color(); - attr.stroke_color = agg::rgba(s_color.red()/255.0,s_color.green()/255.0, - s_color.blue()/255.0,(s_color.alpha()*strk->get_opacity())/255.0); + // TODO - stroke attributes need to be boost::optional + // for this to work properly + if (strk) + { + attr.stroke_width = strk->get_width(); + color const& s_color = strk->get_color(); + attr.stroke_color = agg::rgba(s_color.red()/255.0, + s_color.green()/255.0, + s_color.blue()/255.0, + s_color.alpha()/255.0); + } + if (opacity) + { + attr.stroke_opacity = *opacity; + } + else if (strk) + { + attr.stroke_opacity = strk->get_opacity(); + } } - if (fill && attr.fill_flag) + if (attr.fill_flag) { - color const& f_color = *fill; - attr.fill_color = agg::rgba(f_color.red()/255.0,f_color.green()/255.0, - f_color.blue()/255.0,(f_color.alpha()*sym.get_opacity())/255.0); + if (fill) + { + color const& f_color = *fill; + attr.fill_color = agg::rgba(f_color.red()/255.0, + f_color.green()/255.0, + f_color.blue()/255.0, + f_color.alpha()/255.0); + } + if (opacity) + { + attr.fill_opacity = *opacity; + } + else if (fill_opacity) + { + attr.fill_opacity = *fill_opacity; + } } - dst.push_back(attr); } - return true; + return success; } return false; } diff --git a/include/mapnik/markers_placement.hpp b/include/mapnik/markers_placement.hpp index a0828caba..5ac83a22d 100644 --- a/include/mapnik/markers_placement.hpp +++ b/include/mapnik/markers_placement.hpp @@ -24,13 +24,26 @@ #define MAPNIK_MARKERS_PLACEMENT_HPP // mapnik +#include +#include +#include +#include +#include //round #include // boost #include // agg +#include "agg_basics.h" +#include "agg_conv_clip_polygon.h" +#include "agg_conv_clip_polyline.h" +#include "agg_trans_affine.h" #include "agg_conv_transform.h" +#include "agg_conv_smooth_poly1.h" + +// stl +#include namespace mapnik { @@ -47,12 +60,40 @@ public: * converted to a positive value with similar magnitude, but * choosen to optimize marker placement. 0 = no markers */ - markers_placement(Locator &locator, box2d const& size, agg::trans_affine const& tr, Detector &detector, double spacing, double max_error, bool allow_overlap); + markers_placement(Locator &locator, box2d const& size, agg::trans_affine const& tr, Detector &detector, double spacing, double max_error, bool allow_overlap) + : locator_(locator), + size_(size), + tr_(tr), + detector_(detector), + max_error_(max_error), + allow_overlap_(allow_overlap) + { + marker_width_ = (size_ * tr_).width(); + if (spacing >= 0) + { + spacing_ = spacing; + } else if (spacing < 0) + { + spacing_ = find_optimal_spacing(-spacing); + } + rewind(); + } + /** Start again at first marker. * \note Returns the same list of markers only works when they were NOT added * to the detector. */ - void rewind(); + void rewind() + { + locator_.rewind(0); + //Get first point + done_ = agg::is_stop(locator_.vertex(&next_x, &next_y)) || spacing_ < marker_width_; + last_x = next_x; + last_y = next_y; // Force request of new segment + error_ = 0; + marker_nr_ = 0; + } + /** Get a point where the marker should be placed. * Each time this function is called a new point is returned. * \param x Return value for x position @@ -61,7 +102,115 @@ public: * \param add_to_detector Add selected position to detector * \return True if a place is found, false if none is found. */ - bool get_point(double & x, double & y, double & angle, bool add_to_detector = true); + bool get_point(double & x, double & y, double & angle, bool add_to_detector = true) + { + if (done_) return false; + unsigned cmd; + /* This functions starts at the position of the previous marker, + walks along the path, counting how far it has to go in spacing_left. + If one marker can't be placed at the position it should go to it is + moved a bit. The error is compensated for in the next call to this + function. + + error > 0: Marker too near to the end of the path. + error = 0: Perfect position. + error < 0: Marker too near to the beginning of the path. + */ + if (marker_nr_++ == 0) + { + //First marker + spacing_left_ = spacing_ / 2; + } else + { + spacing_left_ = spacing_; + } + spacing_left_ -= error_; + error_ = 0; + //Loop exits when a position is found or when no more segments are available + while (true) + { + //Do not place markers too close to the beginning of a segment + if (spacing_left_ < marker_width_/2) + { + set_spacing_left(marker_width_/2); //Only moves forward + } + //Error for this marker is too large. Skip to the next position. + if (abs(error_) > max_error_ * spacing_) + { + if (error_ > spacing_) + { + error_ = 0; //Avoid moving backwards + MAPNIK_LOG_WARN(markers_placement) << "Extremely large error in markers_placement. Please file a bug report."; + } + spacing_left_ += spacing_ - error_; + error_ = 0; + } + double dx = next_x - last_x; + double dy = next_y - last_y; + double segment_length = std::sqrt(dx * dx + dy * dy); + if (segment_length <= spacing_left_) + { + //Segment is to short to place marker. Find next segment + spacing_left_ -= segment_length; + last_x = next_x; + last_y = next_y; + while (agg::is_move_to(cmd = locator_.vertex(&next_x, &next_y))) + { + //Skip over "move" commands + last_x = next_x; + last_y = next_y; + } + if (agg::is_stop(cmd)) + { + done_ = true; + return false; + } + continue; //Try again + } + /* At this point we know the following things: + - segment_length > spacing_left + - error is small enough + - at least half a marker fits into this segment + */ + //Check if marker really fits in this segment + if (segment_length < marker_width_) + { + //Segment to short => Skip this segment + set_spacing_left(segment_length + marker_width_/2); //Only moves forward + continue; + } else if (segment_length - spacing_left_ < marker_width_/2) + { + //Segment is long enough, but we are to close to the end + //Note: This function moves backwards. This could lead to an infinite + // loop when another function adds a positive offset. Therefore we + // only move backwards when there is no offset + if (error_ == 0) + { + set_spacing_left(segment_length - marker_width_/2, true); + } else + { + //Skip this segment + set_spacing_left(segment_length + marker_width_/2); //Only moves forward + } + continue; //Force checking of max_error constraint + } + angle = atan2(dy, dx); + x = last_x + dx * (spacing_left_ / segment_length); + y = last_y + dy * (spacing_left_ / segment_length); + box2d box = perform_transform(angle, x, y); + if (!allow_overlap_ && !detector_.has_placement(box)) + { + //10.0 is the approxmiate number of positions tried and choosen arbitrarily + set_spacing_left(spacing_left_ + spacing_ * max_error_ / 10.0); //Only moves forward + continue; + } + if (add_to_detector) detector_.insert(box); + last_x = x; + last_y = y; + return true; + } + } + private: Locator &locator_; box2d size_; @@ -82,11 +231,69 @@ private: unsigned marker_nr_; /** Rotates the size_ box and translates the position. */ - box2d perform_transform(double angle, double dx, double dy); + box2d perform_transform(double angle, double dx, double dy) + { + double x1 = size_.minx(); + double x2 = size_.maxx(); + double y1 = size_.miny(); + double y2 = size_.maxy(); + agg::trans_affine tr = tr_ * agg::trans_affine_rotation(angle).translate(dx, dy); + double xA = x1, yA = y1, xB = x2, yB = y1, xC = x2, yC = y2, xD = x1, yD = y2; + tr.transform(&xA, &yA); + tr.transform(&xB, &yB); + tr.transform(&xC, &yC); + tr.transform(&xD, &yD); + box2d result(xA, yA, xC, yC); + result.expand_to_include(xB, yB); + result.expand_to_include(xD, yD); + return result; + } + /** Automatically chooses spacing. */ - double find_optimal_spacing(double s); + double find_optimal_spacing(double s) + { + rewind(); + //Calculate total path length + unsigned cmd = agg::path_cmd_move_to; + double length = 0; + while (!agg::is_stop(cmd)) + { + double dx = next_x - last_x; + double dy = next_y - last_y; + length += std::sqrt(dx * dx + dy * dy); + last_x = next_x; + last_y = next_y; + while (agg::is_move_to(cmd = locator_.vertex(&next_x, &next_y))) + { + //Skip over "move" commands + last_x = next_x; + last_y = next_y; + } + } + unsigned points = round(length / s); + if (points == 0) return 0.0; //Path to short + return length / points; + } + /** Set spacing_left_, adjusts error_ and performs sanity checks. */ - void set_spacing_left(double sl, bool allow_negative=false); + void set_spacing_left(double sl, bool allow_negative=false) + { + double delta_error = sl - spacing_left_; + if (!allow_negative && delta_error < 0) + { + MAPNIK_LOG_WARN(markers_placement) << "Unexpected negative error in markers_placement. Please file a bug report."; + return; + } + #ifdef MAPNIK_DEBUG + if (delta_error == 0.0) + { + MAPNIK_LOG_WARN(markers_placement) << "Not moving at all in set_spacing_left()! Please file a bug report."; + } + #endif + error_ += delta_error; + spacing_left_ = sl; + } + }; } diff --git a/include/mapnik/markers_symbolizer.hpp b/include/mapnik/markers_symbolizer.hpp index e8ba4cd0e..0a441a98d 100644 --- a/include/mapnik/markers_symbolizer.hpp +++ b/include/mapnik/markers_symbolizer.hpp @@ -39,6 +39,7 @@ namespace mapnik { // TODO - consider merging with text_symbolizer label_placement_e enum marker_placement_enum { MARKER_POINT_PLACEMENT, + MARKER_INTERIOR_PLACEMENT, MARKER_LINE_PLACEMENT, marker_placement_enum_MAX }; @@ -65,8 +66,12 @@ public: double get_spacing() const; void set_max_error(double max_error); double get_max_error() const; + void set_opacity(float opacity); + boost::optional get_opacity() const; void set_fill(color const& fill); boost::optional get_fill() const; + void set_fill_opacity(float opacity); + boost::optional get_fill_opacity() const; void set_stroke(stroke const& stroke); boost::optional get_stroke() const; void set_marker_placement(marker_placement_e marker_p); @@ -79,6 +84,8 @@ private: double spacing_; double max_error_; boost::optional fill_; + boost::optional fill_opacity_; + boost::optional opacity_; boost::optional stroke_; marker_placement_e marker_p_; }; diff --git a/include/mapnik/svg/svg_converter.hpp b/include/mapnik/svg/svg_converter.hpp index 7e61a09ff..4ba4c4bff 100644 --- a/include/mapnik/svg/svg_converter.hpp +++ b/include/mapnik/svg/svg_converter.hpp @@ -66,10 +66,10 @@ public: { throw std::runtime_error("end_path : The path was not begun"); } - path_attributes attr = cur_attr(); - unsigned idx = attributes_[attributes_.size() - 1].index; + path_attributes& attr = attributes_[attributes_.size() - 1]; + unsigned idx = attr.index; + attr = cur_attr(); attr.index = idx; - attributes_[attributes_.size() - 1] = attr; } void move_to(double x, double y, bool rel=false) // M, m diff --git a/include/mapnik/svg/svg_renderer.hpp b/include/mapnik/svg/svg_renderer.hpp index fad181e02..f3b61c041 100644 --- a/include/mapnik/svg/svg_renderer.hpp +++ b/include/mapnik/svg/svg_renderer.hpp @@ -102,14 +102,14 @@ private: template class svg_renderer : boost::noncopyable { +public: typedef agg::conv_curve curved_type; typedef agg::conv_stroke curved_stroked_type; typedef agg::conv_transform curved_stroked_trans_type; typedef agg::conv_transform curved_trans_type; typedef agg::conv_contour curved_trans_contour_type; typedef agg::renderer_base renderer_base; - -public: + svg_renderer(VertexSource & source, AttributeSource const& attributes) : source_(source), curved_(source_), diff --git a/src/agg/agg_renderer.cpp b/src/agg/agg_renderer.cpp index 92cda8973..a1ce58534 100644 --- a/src/agg/agg_renderer.cpp +++ b/src/agg/agg_renderer.cpp @@ -21,12 +21,14 @@ *****************************************************************************/ // mapnik -#include -#include -#include #include #include #include +#include + +#include +#include +#include #include #include #include @@ -36,6 +38,7 @@ #include #include #include + #include #include #include @@ -156,6 +159,10 @@ void agg_renderer::setup(Map const &m) } } + agg::rendering_buffer buf(pixmap_.raw_data(),width_,height_, width_ * 4); + agg::pixfmt_rgba32 pixf(buf); + pixf.premultiply(); + MAPNIK_LOG_DEBUG(agg_renderer) << "agg_renderer: Scale=" << m.scale(); } @@ -166,7 +173,6 @@ template void agg_renderer::start_map_processing(Map const& map) { MAPNIK_LOG_DEBUG(agg_renderer) << "agg_renderer: Start map processing bbox=" << map.get_current_extent(); - ras_ptr->clip_box(0,0,width_,height_); } @@ -191,7 +197,24 @@ void agg_renderer::start_layer_processing(layer const& lay, box2d con { 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) + { + query_extent_.clip(*maximum_extent); + } } template @@ -362,15 +385,18 @@ void agg_renderer::render_marker(pixel_position const& pos, marker const& mar src.height(), src.width()*4); agg::pixfmt_rgba32_pre pixf(marker_buf); - typedef agg::image_accessor_clone img_accessor_type; typedef agg::span_interpolator_linear interpolator_type; typedef agg::span_image_filter_rgba_2x2 span_gen_type; + typedef agg::renderer_scanline_aa_alpha, + span_gen_type> renderer_type; img_accessor_type ia(pixf); interpolator_type interpolator(agg::trans_affine(p, 0, 0, width, height) ); span_gen_type sg(ia, interpolator, filter); - agg::render_scanlines_aa(*ras_ptr, sl, renb, sa, sg); + renderer_type rp(renb,sa, sg, unsigned(opacity*255)); + agg::render_scanlines(*ras_ptr, sl, rp); } } } @@ -427,6 +453,27 @@ void agg_renderer::debug_draw_box(R& buf, box2d const& box, agg::render_scanlines(*ras_ptr, sl_line, ren); } +template +void agg_renderer::draw_geo_extent(box2d const& extent, mapnik::color const& color) +{ + box2d box = t_.forward(extent); + double x0 = box.minx(); + double x1 = box.maxx(); + double y0 = box.miny(); + double y1 = box.maxy(); + unsigned rgba = color.rgba(); + for (double x=x0; x; template void agg_renderer::debug_draw_box( agg::rendering_buffer& buf, diff --git a/src/agg/process_building_symbolizer.cpp b/src/agg/process_building_symbolizer.cpp index 8f82ca2b8..4184bc42f 100644 --- a/src/agg/process_building_symbolizer.cpp +++ b/src/agg/process_building_symbolizer.cpp @@ -82,32 +82,31 @@ void agg_renderer::process(building_symbolizer const& sym, boost::scoped_ptr frame(new geometry_type(LineString)); boost::scoped_ptr roof(new geometry_type(Polygon)); std::deque face_segments; - double x0(0); - double y0(0); - + double x0 = 0; + double y0 = 0; + double x,y; geom.rewind(0); - unsigned cm = geom.vertex(&x0,&y0); - for (unsigned j=1;jmove_to(x,y); } - else if (cm == SEG_LINETO) + else if (cm == SEG_LINETO || cm == SEG_CLOSE) { frame->line_to(x,y); face_segments.push_back(segment_t(x0,y0,x,y)); } - x0 = x; y0 = y; } + std::sort(face_segments.begin(),face_segments.end(), y_order); std::deque::const_iterator itr=face_segments.begin(); - for (;itr!=face_segments.end();++itr) + std::deque::const_iterator end=face_segments.end(); + + for (; itr!=end; ++itr) { boost::scoped_ptr faces(new geometry_type(Polygon)); faces->move_to(itr->get<0>(),itr->get<1>()); @@ -120,22 +119,22 @@ void agg_renderer::process(building_symbolizer const& sym, ren.color(agg::rgba8(int(r*0.8), int(g*0.8), int(b*0.8), int(a * sym.get_opacity()))); agg::render_scanlines(*ras_ptr, sl, ren); ras_ptr->reset(); - + // frame->move_to(itr->get<0>(),itr->get<1>()); frame->line_to(itr->get<0>(),itr->get<1>()+height); + } geom.rewind(0); - for (unsigned j=0;jmove_to(x,y+height); roof->move_to(x,y+height); } - else if (cm == SEG_LINETO) + else if (cm == SEG_LINETO || cm == SEG_CLOSE) { frame->line_to(x,y+height); roof->line_to(x,y+height); @@ -153,6 +152,7 @@ void agg_renderer::process(building_symbolizer const& sym, ras_ptr->add_path(roof_path); ren.color(agg::rgba8(r, g, b, int(a * sym.get_opacity()))); agg::render_scanlines(*ras_ptr, sl, ren); + } } } diff --git a/src/agg/process_line_pattern_symbolizer.cpp b/src/agg/process_line_pattern_symbolizer.cpp index 5f79a7755..66dc01ca2 100644 --- a/src/agg/process_line_pattern_symbolizer.cpp +++ b/src/agg/process_line_pattern_symbolizer.cpp @@ -56,7 +56,7 @@ void agg_renderer::process(line_pattern_symbolizer const& sym, { typedef agg::rgba8 color; typedef agg::order_rgba order; - typedef agg::pixel32_type pixel_type; + typedef agg::pixel32_type pixel_type; typedef agg::comp_op_adaptor_rgba_pre blender_type; typedef agg::pattern_filter_bilinear_rgba8 pattern_filter_type; typedef agg::line_image_pattern pattern_type; @@ -64,7 +64,7 @@ void agg_renderer::process(line_pattern_symbolizer const& sym, typedef agg::renderer_base renderer_base; typedef agg::renderer_outline_image renderer_type; typedef agg::rasterizer_outline_aa rasterizer_type; - + std::string filename = path_processor_type::evaluate( *sym.get_filename(), feature); boost::optional mark = marker_cache::instance()->find(filename,true); @@ -81,8 +81,6 @@ void agg_renderer::process(line_pattern_symbolizer const& sym, if (!pat) return; - box2d ext = query_extent_ * 1.0; - agg::rendering_buffer buf(current_buffer_->raw_data(),width_,height_, width_ * 4); pixfmt_type pixf(buf); pixf.comp_op(static_cast(sym.comp_op())); @@ -91,26 +89,40 @@ void agg_renderer::process(line_pattern_symbolizer const& sym, pattern_source source(*(*pat)); pattern_type pattern (filter,source); - renderer_type ren(ren_base, pattern); + renderer_type ren(ren_base, pattern); rasterizer_type ras(ren); - + agg::trans_affine tr; evaluate_transform(tr, feature, sym.get_transform()); + box2d clipping_extent = query_extent_; + if (sym.clip()) + { + double padding = (double)(query_extent_.width()/pixmap_.width()); + float half_stroke = (*mark)->width()/2.0; + if (half_stroke > 1) + padding *= half_stroke; + double x0 = query_extent_.minx(); + double y0 = query_extent_.miny(); + double x1 = query_extent_.maxx(); + double y1 = query_extent_.maxy(); + clipping_extent.init(x0 - padding, y0 - padding, x1 + padding , y1 + padding); + } + typedef boost::mpl::vector conv_types; vertex_converter, rasterizer_type, line_pattern_symbolizer, CoordTransform, proj_transform, agg::trans_affine, conv_types> - converter(ext,ras,sym,t_,prj_trans,tr,scale_factor_); - + converter(clipping_extent,ras,sym,t_,prj_trans,tr,scale_factor_); + if (sym.clip()) converter.set(); //optional clip (default: true) - converter.set(); //always transform + converter.set(); //always transform if (sym.smooth() > 0.0) converter.set(); // optional smooth converter - + BOOST_FOREACH(geometry_type & geom, feature.paths()) { if (geom.size() > 1) { - converter.apply(geom); + converter.apply(geom); } } } diff --git a/src/agg/process_line_symbolizer.cpp b/src/agg/process_line_symbolizer.cpp index 1d5388aa3..02263be95 100644 --- a/src/agg/process_line_symbolizer.cpp +++ b/src/agg/process_line_symbolizer.cpp @@ -47,7 +47,6 @@ namespace mapnik { - template void agg_renderer::process(line_symbolizer const& sym, mapnik::feature_impl & feature, @@ -60,7 +59,7 @@ void agg_renderer::process(line_symbolizer const& sym, unsigned g=col.green(); unsigned b=col.blue(); unsigned a=col.alpha(); - + ras_ptr->reset(); set_gamma_method(stroke_, ras_ptr); @@ -76,11 +75,28 @@ void agg_renderer::process(line_symbolizer const& sym, pixfmt_comp_type pixf(buf); pixf.comp_op(static_cast(sym.comp_op())); renderer_base renb(pixf); - + agg::trans_affine tr; evaluate_transform(tr, feature, sym.get_transform()); - box2d ext = query_extent_ * 1.1; + box2d clipping_extent = query_extent_; + if (sym.clip()) + { + double padding = (double)(query_extent_.width()/pixmap_.width()); + float half_stroke = stroke_.get_width()/2.0; + if (half_stroke > 1) + padding *= half_stroke; + if (fabs(sym.offset()) > 0) + padding *= fabs(sym.offset()) * 1.2; + double x0 = query_extent_.minx(); + double y0 = query_extent_.miny(); + double x1 = query_extent_.maxx(); + double y1 = query_extent_.maxy(); + clipping_extent.init(x0 - padding, y0 - padding, x1 + padding , y1 + padding); + // debugging + //box2d inverse(x0 + padding, y0 + padding, x1 - padding , y1 - padding); + //draw_geo_extent(inverse,mapnik::color("red")); + } if (sym.get_rasterizer() == RASTERIZER_FAST) { @@ -94,11 +110,12 @@ void agg_renderer::process(line_symbolizer const& sym, rasterizer_type ras(ren); set_join_caps_aa(stroke_,ras); - typedef boost::mpl::vector conv_types; + typedef boost::mpl::vector conv_types; vertex_converter, rasterizer_type, line_symbolizer, CoordTransform, proj_transform, agg::trans_affine, conv_types> - converter(ext,ras,sym,t_,prj_trans,tr,scaled); - + converter(clipping_extent,ras,sym,t_,prj_trans,tr,scaled); if (sym.clip()) converter.set(); // optional clip (default: true) converter.set(); // always transform if (fabs(sym.offset()) > 0.0) converter.set(); // parallel offset @@ -117,10 +134,12 @@ void agg_renderer::process(line_symbolizer const& sym, } else { - typedef boost::mpl::vector conv_types; + typedef boost::mpl::vector conv_types; + vertex_converter, rasterizer, line_symbolizer, CoordTransform, proj_transform, agg::trans_affine, conv_types> - converter(ext,*ras_ptr,sym,t_,prj_trans,tr,scale_factor_); + converter(clipping_extent,*ras_ptr,sym,t_,prj_trans,tr,scale_factor_); if (sym.clip()) converter.set(); // optional clip (default: true) converter.set(); // always transform diff --git a/src/agg/process_markers_symbolizer.cpp b/src/agg/process_markers_symbolizer.cpp index d57b22b11..a84787702 100644 --- a/src/agg/process_markers_symbolizer.cpp +++ b/src/agg/process_markers_symbolizer.cpp @@ -21,23 +21,27 @@ *****************************************************************************/ // mapnik -#include #include -#include #include #include + +#include +#include #include #include +#include #include #include -#include #include +#include #include -#include +#include #include // agg #include "agg_basics.h" +#include "agg_renderer_base.h" +#include "agg_renderer_scanline.h" #include "agg_rendering_buffer.h" #include "agg_pixfmt_rgba.h" #include "agg_rasterizer_scanline_aa.h" @@ -45,234 +49,13 @@ #include "agg_path_storage.h" #include "agg_conv_clip_polyline.h" #include "agg_conv_transform.h" -#include "agg_image_filters.h" -#include "agg_trans_bilinear.h" -#include "agg_span_allocator.h" -#include "agg_image_accessors.h" -#include "agg_span_image_filter_rgba.h" + + // boost #include namespace mapnik { -template -struct vector_markers_rasterizer_dispatch -{ - typedef agg::rgba8 color_type; - typedef agg::order_rgba order_type; - typedef agg::pixel32_type pixel_type; - typedef agg::comp_op_adaptor_rgba_pre blender_type; // comp blender - typedef agg::pixfmt_custom_blend_rgba pixfmt_comp_type; - typedef agg::renderer_base renderer_base; - typedef agg::renderer_scanline_aa_solid renderer_type; - - vector_markers_rasterizer_dispatch(BufferType & image_buffer, - SvgRenderer & svg_renderer, - Rasterizer & ras, - box2d const& bbox, - agg::trans_affine const& marker_trans, - markers_symbolizer const& sym, - Detector & detector, - double scale_factor) - : buf_(image_buffer.raw_data(), image_buffer.width(), image_buffer.height(), image_buffer.width() * 4), - pixf_(buf_), - renb_(pixf_), - svg_renderer_(svg_renderer), - ras_(ras), - bbox_(bbox), - marker_trans_(marker_trans), - sym_(sym), - detector_(detector), - scale_factor_(scale_factor) - { - pixf_.comp_op(static_cast(sym_.comp_op())); - } - - template - void add_path(T & path) - { - marker_placement_e placement_method = sym_.get_marker_placement(); - - if (placement_method == MARKER_POINT_PLACEMENT) - { - double x,y; - path.rewind(0); - label::interior_position(path, x, y); - agg::trans_affine matrix = marker_trans_; - matrix.translate(x,y); - box2d transformed_bbox = bbox_ * matrix; - - if (sym_.get_allow_overlap() || - detector_.has_placement(transformed_bbox)) - { - svg_renderer_.render(ras_, sl_, renb_, matrix, sym_.get_opacity(), bbox_); - - if (!sym_.get_ignore_placement()) - detector_.insert(transformed_bbox); - } - } - else - { - markers_placement placement(path, bbox_, marker_trans_, detector_, - sym_.get_spacing() * scale_factor_, - sym_.get_max_error(), - sym_.get_allow_overlap()); - double x, y, angle; - while (placement.get_point(x, y, angle)) - { - agg::trans_affine matrix = marker_trans_; - matrix.rotate(angle); - matrix.translate(x, y); - svg_renderer_.render(ras_, sl_, renb_, matrix, sym_.get_opacity(), bbox_); - } - } - } -private: - agg::scanline_u8 sl_; - agg::rendering_buffer buf_; - pixfmt_comp_type pixf_; - renderer_base renb_; - SvgRenderer & svg_renderer_; - Rasterizer & ras_; - box2d const& bbox_; - agg::trans_affine const& marker_trans_; - markers_symbolizer const& sym_; - Detector & detector_; - double scale_factor_; -}; - -template -void render_raster_marker(Rasterizer & ras, RendererBuffer & renb, - agg::scanline_u8 & sl, image_data_32 const& src, - agg::trans_affine const& marker_tr, double opacity) -{ - - double width = src.width(); - double height = src.height(); - double p[8]; - p[0] = 0; p[1] = 0; - p[2] = width; p[3] = 0; - p[4] = width; p[5] = height; - p[6] = 0; p[7] = height; - - marker_tr.transform(&p[0], &p[1]); - marker_tr.transform(&p[2], &p[3]); - marker_tr.transform(&p[4], &p[5]); - marker_tr.transform(&p[6], &p[7]); - - ras.move_to_d(p[0],p[1]); - ras.line_to_d(p[2],p[3]); - ras.line_to_d(p[4],p[5]); - ras.line_to_d(p[6],p[7]); - - typedef agg::rgba8 color_type; - agg::span_allocator sa; - agg::image_filter_bilinear filter_kernel; - agg::image_filter_lut filter(filter_kernel, false); - - agg::rendering_buffer marker_buf((unsigned char *)src.getBytes(), - src.width(), - src.height(), - src.width()*4); - agg::pixfmt_rgba32_pre pixf(marker_buf); - - typedef agg::image_accessor_clone img_accessor_type; - typedef agg::span_interpolator_linear interpolator_type; - typedef agg::span_image_filter_rgba_2x2 span_gen_type; - img_accessor_type ia(pixf); - interpolator_type interpolator(agg::trans_affine(p, 0, 0, width, height) ); - span_gen_type sg(ia, interpolator, filter); - agg::render_scanlines_aa(ras, sl, renb, sa, sg); -} - -template -struct raster_markers_rasterizer_dispatch -{ - typedef agg::rgba8 color_type; - typedef agg::order_rgba order_type; - typedef agg::pixel32_type pixel_type; - typedef agg::comp_op_adaptor_rgba_pre blender_type; // comp blender - typedef agg::pixfmt_custom_blend_rgba pixfmt_comp_type; - typedef agg::renderer_base renderer_base; - typedef agg::renderer_scanline_aa_solid renderer_type; - - raster_markers_rasterizer_dispatch(BufferType & image_buffer, - Rasterizer & ras, - image_data_32 const& src, - agg::trans_affine const& marker_trans, - markers_symbolizer const& sym, - Detector & detector, - double scale_factor) - : buf_(image_buffer.raw_data(), image_buffer.width(), image_buffer.height(), image_buffer.width() * 4), - pixf_(buf_), - renb_(pixf_), - ras_(ras), - src_(src), - marker_trans_(marker_trans), - sym_(sym), - detector_(detector), - scale_factor_(scale_factor) - { - pixf_.comp_op(static_cast(sym_.comp_op())); - } - - template - void add_path(T & path) - { - marker_placement_e placement_method = sym_.get_marker_placement(); - box2d bbox_(0,0, src_.width(),src_.height()); - - if (placement_method == MARKER_POINT_PLACEMENT) - { - double x,y; - path.rewind(0); - label::interior_position(path, x, y); - agg::trans_affine matrix = marker_trans_; - matrix.translate(x,y); - box2d transformed_bbox = bbox_ * matrix; - - if (sym_.get_allow_overlap() || - detector_.has_placement(transformed_bbox)) - { - - render_raster_marker(ras_, renb_, sl_, src_, - matrix, sym_.get_opacity()); - if (!sym_.get_ignore_placement()) - detector_.insert(transformed_bbox); - } - } - else - { - markers_placement placement(path, bbox_, marker_trans_, detector_, - sym_.get_spacing() * scale_factor_, - sym_.get_max_error(), - sym_.get_allow_overlap()); - double x, y, angle; - while (placement.get_point(x, y, angle)) - { - agg::trans_affine matrix = marker_trans_; - matrix.rotate(angle); - matrix.translate(x,y); - render_raster_marker(ras_, renb_, sl_, src_, - matrix, sym_.get_opacity()); - } - } - } -private: - agg::scanline_u8 sl_; - agg::rendering_buffer buf_; - pixfmt_comp_type pixf_; - renderer_base renb_; - Rasterizer & ras_; - image_data_32 const& src_; - agg::trans_affine const& marker_trans_; - markers_symbolizer const& sym_; - Detector & detector_; - double scale_factor_; -}; - - template void agg_renderer::process(markers_symbolizer const& sym, feature_impl & feature, @@ -284,9 +67,8 @@ void agg_renderer::process(markers_symbolizer const& sym, typedef agg::comp_op_adaptor_rgba_pre blender_type; // comp blender typedef agg::pixfmt_custom_blend_rgba pixfmt_comp_type; typedef agg::renderer_base renderer_base; - typedef agg::renderer_scanline_aa_solid renderer_type; typedef label_collision_detector4 detector_type; - typedef boost::mpl::vector conv_types; + typedef boost::mpl::vector conv_types; std::string filename = path_processor_type::evaluate(*sym.get_filename(), feature); @@ -297,67 +79,124 @@ void agg_renderer::process(markers_symbolizer const& sym, { ras_ptr->reset(); ras_ptr->gamma(agg::gamma_power()); - agg::trans_affine geom_tr; evaluate_transform(geom_tr, feature, sym.get_transform()); - - box2d const& bbox = (*mark)->bounding_box(); agg::trans_affine tr; - setup_label_transform(tr, bbox, feature, sym); - tr = agg::trans_affine_scaling(scale_factor_) * tr; - coord2d center = bbox.center(); - agg::trans_affine_translation recenter(-center.x, -center.y); - agg::trans_affine marker_trans = recenter * tr; + tr *= agg::trans_affine_scaling(scale_factor_); if ((*mark)->is_vector()) { using namespace mapnik::svg; - boost::optional marker = (*mark)->get_vector_data(); - - - vertex_stl_adapter stl_storage((*marker)->source()); - svg_path_adapter svg_path(stl_storage); - - agg::pod_bvector attributes; - bool result = push_explicit_style( (*marker)->attributes(), attributes, sym); - + typedef agg::renderer_scanline_aa_solid renderer_type; + typedef agg::pod_bvector svg_attribute_type; typedef svg_renderer, + svg_attribute_type, renderer_type, - agg::pixfmt_rgba32 > svg_renderer_type; - typedef vector_markers_rasterizer_dispatch dispatch_type; + pixfmt_comp_type > svg_renderer_type; + typedef vector_markers_rasterizer_dispatch dispatch_type; + boost::optional const& stock_vector_marker = (*mark)->get_vector_data(); + expression_ptr const& width_expr = sym.get_width(); + expression_ptr const& height_expr = sym.get_height(); - - svg_renderer_type svg_renderer(svg_path, result ? attributes : (*marker)->attributes()); - - dispatch_type rasterizer_dispatch(*current_buffer_,svg_renderer,*ras_ptr, - bbox, marker_trans, sym, *detector_, scale_factor_); - - - vertex_converter, dispatch_type, markers_symbolizer, - CoordTransform, proj_transform, agg::trans_affine, conv_types> - converter(query_extent_* 1.1,rasterizer_dispatch, sym,t_,prj_trans,tr,scale_factor_); - - if (sym.clip()) converter.template set(); //optional clip (default: true) - converter.template set(); //always transform - if (sym.smooth() > 0.0) converter.template set(); // optional smooth converter - - BOOST_FOREACH(geometry_type & geom, feature.paths()) + // special case for simple ellipse markers + // to allow for full control over rx/ry dimensions + if (filename == "shape://ellipse" + && (width_expr || height_expr)) { - converter.apply(geom); + svg_storage_type marker_ellipse; + vertex_stl_adapter stl_storage(marker_ellipse.source()); + svg_path_adapter svg_path(stl_storage); + build_ellipse(sym, feature, marker_ellipse, svg_path); + svg_attribute_type attributes; + bool result = push_explicit_style( (*stock_vector_marker)->attributes(), attributes, sym); + svg_renderer_type svg_renderer(svg_path, result ? attributes : (*stock_vector_marker)->attributes()); + evaluate_transform(tr, feature, sym.get_image_transform()); + box2d bbox = marker_ellipse.bounding_box(); + coord2d center = bbox.center(); + agg::trans_affine_translation recenter(-center.x, -center.y); + agg::trans_affine marker_trans = recenter * tr; + dispatch_type rasterizer_dispatch(*current_buffer_,svg_renderer,*ras_ptr, + bbox, marker_trans, sym, *detector_, scale_factor_); + vertex_converter, dispatch_type, markers_symbolizer, + CoordTransform, proj_transform, agg::trans_affine, conv_types> + converter(query_extent_, rasterizer_dispatch, sym,t_,prj_trans,tr,scale_factor_); + if (sym.clip() && feature.paths().size() > 0) // optional clip (default: true) + { + eGeomType type = feature.paths()[0].type(); + if (type == Polygon) + converter.template set(); + else if (type == LineString) + converter.template set(); + // don't clip if type==Point + } + converter.template set(); //always transform + if (sym.smooth() > 0.0) converter.template set(); // optional smooth converter + BOOST_FOREACH(geometry_type & geom, feature.paths()) + { + converter.apply(geom); + } + } + else + { + box2d const& bbox = (*mark)->bounding_box(); + setup_label_transform(tr, bbox, feature, sym); + coord2d center = bbox.center(); + agg::trans_affine_translation recenter(-center.x, -center.y); + agg::trans_affine marker_trans = recenter * tr; + vertex_stl_adapter stl_storage((*stock_vector_marker)->source()); + svg_path_adapter svg_path(stl_storage); + svg_attribute_type attributes; + bool result = push_explicit_style( (*stock_vector_marker)->attributes(), attributes, sym); + svg_renderer_type svg_renderer(svg_path, result ? attributes : (*stock_vector_marker)->attributes()); + dispatch_type rasterizer_dispatch(*current_buffer_,svg_renderer,*ras_ptr, + bbox, marker_trans, sym, *detector_, scale_factor_); + vertex_converter, dispatch_type, markers_symbolizer, + CoordTransform, proj_transform, agg::trans_affine, conv_types> + converter(query_extent_, rasterizer_dispatch, sym,t_,prj_trans,tr,scale_factor_); + if (sym.clip() && feature.paths().size() > 0) // optional clip (default: true) + { + eGeomType type = feature.paths()[0].type(); + if (type == Polygon) + converter.template set(); + else if (type == LineString) + converter.template set(); + // don't clip if type==Point + } + converter.template set(); //always transform + if (sym.smooth() > 0.0) converter.template set(); // optional smooth converter + BOOST_FOREACH(geometry_type & geom, feature.paths()) + { + converter.apply(geom); + } } } else // raster markers { + box2d const& bbox = (*mark)->bounding_box(); + setup_label_transform(tr, bbox, feature, sym); + coord2d center = bbox.center(); + agg::trans_affine_translation recenter(-center.x, -center.y); + agg::trans_affine marker_trans = recenter * tr; boost::optional marker = (*mark)->get_bitmap_data(); typedef raster_markers_rasterizer_dispatch dispatch_type; dispatch_type rasterizer_dispatch(*current_buffer_,*ras_ptr, **marker, marker_trans, sym, *detector_, scale_factor_); vertex_converter, dispatch_type, markers_symbolizer, CoordTransform, proj_transform, agg::trans_affine, conv_types> - converter(query_extent_* 1.1, rasterizer_dispatch, sym,t_,prj_trans,tr,scale_factor_); + converter(query_extent_, rasterizer_dispatch, sym,t_,prj_trans,tr,scale_factor_); - if (sym.clip()) converter.template set(); //optional clip (default: true) + if (sym.clip() && feature.paths().size() > 0) // optional clip (default: true) + { + eGeomType type = feature.paths()[0].type(); + if (type == Polygon) + converter.template set(); + else if (type == LineString) + converter.template set(); + // don't clip if type==Point + } converter.template set(); //always transform if (sym.smooth() > 0.0) converter.template set(); // optional smooth converter diff --git a/src/agg/process_point_symbolizer.cpp b/src/agg/process_point_symbolizer.cpp index aac4355a2..58e1fe9cd 100644 --- a/src/agg/process_point_symbolizer.cpp +++ b/src/agg/process_point_symbolizer.cpp @@ -23,19 +23,16 @@ // mapnik #include #include -#include #include -#include + +#include +#include +#include #include #include -#include // agg -#include "agg_basics.h" -#include "agg_rendering_buffer.h" -#include "agg_pixfmt_rgba.h" -#include "agg_rasterizer_scanline_aa.h" -#include "agg_scanline_u.h" +#include "agg_trans_affine.h" // stl #include @@ -89,7 +86,6 @@ void agg_renderer::process(point_symbolizer const& sym, prj_trans.backward(x,y,z); t_.forward(&x,&y); label_ext.re_center(x,y); - if (sym.get_allow_overlap() || detector_->has_placement(label_ext)) { @@ -106,8 +102,8 @@ void agg_renderer::process(point_symbolizer const& sym, if (!sym.get_ignore_placement()) detector_->insert(label_ext); - metawriter_with_properties writer = sym.get_metawriter(); - if (writer.first) writer.first->add_box(label_ext, feature, t_, writer.second); + //metawriter_with_properties writer = sym.get_metawriter(); + //if (writer.first) writer.first->add_box(label_ext, feature, t_, writer.second); } } } diff --git a/src/agg/process_polygon_pattern_symbolizer.cpp b/src/agg/process_polygon_pattern_symbolizer.cpp index 2a5ec53d3..a8b5a7c9f 100644 --- a/src/agg/process_polygon_pattern_symbolizer.cpp +++ b/src/agg/process_polygon_pattern_symbolizer.cpp @@ -139,11 +139,10 @@ void agg_renderer::process(polygon_pattern_symbolizer const& sym, agg::trans_affine tr; evaluate_transform(tr, feature, sym.get_transform()); - box2d inflated_extent = query_extent_ * 1.0; typedef boost::mpl::vector conv_types; vertex_converter, rasterizer, polygon_pattern_symbolizer, CoordTransform, proj_transform, agg::trans_affine, conv_types> - converter(inflated_extent,*ras_ptr,sym,t_,prj_trans,tr,scale_factor_); + converter(query_extent_,*ras_ptr,sym,t_,prj_trans,tr,scale_factor_); if (sym.clip()) converter.set(); //optional clip (default: true) converter.set(); //always transform diff --git a/src/agg/process_polygon_symbolizer.cpp b/src/agg/process_polygon_symbolizer.cpp index 2b1266659..323f987ef 100644 --- a/src/agg/process_polygon_symbolizer.cpp +++ b/src/agg/process_polygon_symbolizer.cpp @@ -48,15 +48,13 @@ void agg_renderer::process(polygon_symbolizer const& sym, ras_ptr->reset(); set_gamma_method(sym,ras_ptr); - box2d inflated_extent = query_extent_ * 1.0; - agg::trans_affine tr; evaluate_transform(tr, feature, sym.get_transform()); typedef boost::mpl::vector conv_types; vertex_converter, rasterizer, polygon_symbolizer, CoordTransform, proj_transform, agg::trans_affine, conv_types> - converter(inflated_extent,*ras_ptr,sym,t_,prj_trans,tr,scale_factor_); + converter(query_extent_,*ras_ptr,sym,t_,prj_trans,tr,scale_factor_); if (sym.clip()) converter.set(); //optional clip (default: true) converter.set(); //always transform @@ -72,7 +70,7 @@ void agg_renderer::process(polygon_symbolizer const& sym, } agg::rendering_buffer buf(current_buffer_->raw_data(),width_,height_, width_ * 4); - + color const& fill = sym.get_fill(); unsigned r=fill.red(); unsigned g=fill.green(); diff --git a/src/build.py b/src/build.py index 5f8525d2e..c43f7fb0a 100644 --- a/src/build.py +++ b/src/build.py @@ -171,7 +171,6 @@ source = Split( json/feature_grammar.cpp json/feature_collection_parser.cpp json/geojson_generator.cpp - markers_placement.cpp formatting/base.cpp formatting/expression.cpp formatting/list.cpp diff --git a/src/cairo_renderer.cpp b/src/cairo_renderer.cpp index a34bdb847..5bbc169d2 100644 --- a/src/cairo_renderer.cpp +++ b/src/cairo_renderer.cpp @@ -497,7 +497,6 @@ public: } else if (cm == SEG_CLOSE) { - line_to(x, y); close_path(); } } @@ -882,43 +881,32 @@ void cairo_renderer_base::process(building_symbolizer const& sym, boost::scoped_ptr frame(new geometry_type(LineString)); boost::scoped_ptr roof(new geometry_type(Polygon)); std::deque face_segments; - double x0(0); - double y0(0); - + double x0 = 0; + double y0 = 0; + double x, y; geom.rewind(0); - unsigned cm = geom.vertex(&x0, &y0); - - for (unsigned j = 1; j < geom.size(); ++j) + for (unsigned cm = geom.vertex(&x, &y); cm != SEG_END; + cm = geom.vertex(&x, &y)) { - double x=0; - double y=0; - - cm = geom.vertex(&x,&y); - if (cm == SEG_MOVETO) { frame->move_to(x,y); } - else if (cm == SEG_LINETO) + else if (cm == SEG_LINETO || cm == SEG_CLOSE) { frame->line_to(x,y); + face_segments.push_back(segment_t(x0,y0,x,y)); } - - if (j != 0) - { - face_segments.push_back(segment_t(x0, y0, x, y)); - } - x0 = x; y0 = y; } std::sort(face_segments.begin(), face_segments.end(), y_order); std::deque::const_iterator itr = face_segments.begin(); - for (; itr != face_segments.end(); ++itr) + std::deque::const_iterator end=face_segments.end(); + for (; itr != end; ++itr) { boost::scoped_ptr faces(new geometry_type(Polygon)); - faces->move_to(itr->get<0>(), itr->get<1>()); faces->line_to(itr->get<2>(), itr->get<3>()); faces->line_to(itr->get<2>(), itr->get<3>() + height); @@ -935,20 +923,18 @@ void cairo_renderer_base::process(building_symbolizer const& sym, } geom.rewind(0); - for (unsigned j = 0; j < geom.size(); ++j) + for (unsigned cm = geom.vertex(&x, &y); cm != SEG_END; + cm = geom.vertex(&x, &y)) { - double x, y; - unsigned cm = geom.vertex(&x, &y); - if (cm == SEG_MOVETO) { - frame->move_to(x, y + height); - roof->move_to(x, y + height); + frame->move_to(x,y+height); + roof->move_to(x,y+height); } - else if (cm == SEG_LINETO) + else if (cm == SEG_LINETO || cm == SEG_CLOSE) { - frame->line_to(x, y + height); - roof->line_to(x, y + height); + frame->line_to(x,y+height); + roof->line_to(x,y+height); } } @@ -987,14 +973,21 @@ void cairo_renderer_base::process(line_symbolizer const& sym, evaluate_transform(tr, feature, sym.get_transform()); box2d ext = query_extent_ * 1.1; - typedef boost::mpl::vector conv_types; + typedef boost::mpl::vector conv_types; vertex_converter, cairo_context, line_symbolizer, CoordTransform, proj_transform, agg::trans_affine, conv_types> converter(ext,context,sym,t_,prj_trans,tr,scale_factor_); - if (sym.clip()) converter.set(); // optional clip (default: true) + if (sym.clip() && feature.paths().size() > 0) // optional clip (default: true) + { + eGeomType type = feature.paths()[0].type(); + if (type == Polygon) + converter.set(); + else if (type == LineString) + converter.set(); + // don't clip if type==Point + } converter.set(); // always transform - if (fabs(sym.offset()) > 0.0) converter.set(); // parallel offset converter.set(); // optional affine transform if (sym.smooth() > 0.0) converter.set(); // optional smooth converter @@ -1035,7 +1028,7 @@ void cairo_renderer_base::render_marker(pixel_position const& pos, marker const& typedef coord_transform path_type; agg::trans_affine transform; - mapnik::path_ptr vmarker = *marker.get_vector_data(); + mapnik::svg_path_ptr vmarker = *marker.get_vector_data(); using namespace mapnik::svg; agg::pod_bvector const & attributes_ = vmarker->attributes(); for(unsigned i = 0; i < attributes_.size(); ++i) @@ -1411,7 +1404,7 @@ void cairo_renderer_base::process(markers_symbolizer const& sym, return; } - boost::optional marker = (*mark)->get_vector_data(); + boost::optional marker = (*mark)->get_vector_data(); box2d const& bbox = (*marker)->bounding_box(); double x1 = bbox.minx(); double y1 = bbox.miny(); @@ -1443,7 +1436,7 @@ void cairo_renderer_base::process(markers_symbolizer const& sym, if (sym.get_allow_overlap() || detector_.has_placement(extent)) { - render_marker(pixel_position(x - 0.5 * w, y - 0.5 * h) ,**mark, tr, sym.get_opacity()); + render_marker(pixel_position(x - 0.5 * w, y - 0.5 * h) ,**mark, tr, 1); if (!sym.get_ignore_placement()) detector_.insert(extent); @@ -1462,7 +1455,7 @@ void cairo_renderer_base::process(markers_symbolizer const& sym, while (placement.get_point(x, y, angle)) { agg::trans_affine matrix = recenter * tr * agg::trans_affine_rotation(angle) * agg::trans_affine_translation(x, y); - render_marker(pixel_position(x - 0.5 * w, y - 0.5 * h), **mark, matrix, sym.get_opacity(),false); + render_marker(pixel_position(x - 0.5 * w, y - 0.5 * h), **mark, matrix, 1,false); } } context.fill(); diff --git a/src/feature_style_processor.cpp b/src/feature_style_processor.cpp index f3226240a..d351185a5 100644 --- a/src/feature_style_processor.cpp +++ b/src/feature_style_processor.cpp @@ -102,35 +102,35 @@ struct feature_style_processor::symbol_dispatch : public boost::stati : output_(output), f_(f), prj_trans_(prj_trans) {} - + template void operator () (T const& sym) const { process_impl::value>::process(output_,sym,f_,prj_trans_); } - + Processor & output_; mapnik::feature_impl & f_; proj_transform const& prj_trans_; }; -typedef char (&no_tag)[1]; -typedef char (&yes_tag)[2]; +typedef char (&no_tag)[1]; +typedef char (&yes_tag)[2]; template -struct process_memfun_helper {}; - -template no_tag has_process_helper(...); +struct process_memfun_helper {}; + +template no_tag has_process_helper(...); template yes_tag has_process_helper(process_memfun_helper* p); - -template + +template struct has_process -{ +{ typedef typename T0::processor_impl_type processor_impl_type; - BOOST_STATIC_CONSTANT(bool - , value = sizeof(has_process_helper(0)) == sizeof(yes_tag) - ); -}; + BOOST_STATIC_CONSTANT(bool + , value = sizeof(has_process_helper(0)) == sizeof(yes_tag) + ); +}; template @@ -321,9 +321,11 @@ 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) { + if (maximum_extent) + { query_ext.clip(*maximum_extent); } + box2d layer_ext2 = lay.envelope(); if (fw_success) { @@ -351,8 +353,8 @@ void feature_style_processor::apply_to_layer(layer const& lay, Proces query q(layer_ext,res,scale_denom,m_.get_current_extent()); std::vector active_styles; attribute_collector collector(names); - double filt_factor = 1; - directive_collector d_collector(&filt_factor); + double filt_factor = 1.0; + directive_collector d_collector(filt_factor); // iterate through all named styles collecting active styles and attribute names BOOST_FOREACH(std::string const& style_name, style_names) @@ -520,7 +522,7 @@ void feature_style_processor::render_style( { p.start_style_processing(*style); - + #if defined(RENDERING_STATS) std::ostringstream s1; s1 << "rendering style for layer: '" << lay.name() @@ -662,4 +664,3 @@ template class feature_style_processor >; template class feature_style_processor >; } - diff --git a/src/grid/grid_renderer.cpp b/src/grid/grid_renderer.cpp index ab231e853..4e2b11663 100644 --- a/src/grid/grid_renderer.cpp +++ b/src/grid/grid_renderer.cpp @@ -21,14 +21,15 @@ *****************************************************************************/ // mapnik -#include -#include -#include #include #include #include #include #include + +#include +#include +#include #include #include #include @@ -56,13 +57,22 @@ grid_renderer::grid_renderer(Map const& m, T & pixmap, double scale_factor, u width_(pixmap_.width()), height_(pixmap_.height()), scale_factor_(scale_factor), + // NOTE: can change this to m dims instead of pixmap_ if render-time + // resolution support is dropped from grid_renderer python interface t_(pixmap_.width(),pixmap_.height(),m.get_current_extent(),offset_x,offset_y), font_engine_(), font_manager_(font_engine_), - detector_(box2d(-m.buffer_size(), -m.buffer_size(), pixmap_.width() + m.buffer_size(), pixmap_.height() + m.buffer_size())), + detector_(boost::make_shared(box2d(-m.buffer_size(), -m.buffer_size(), m.width() + m.buffer_size() ,m.height() + m.buffer_size()))), ras_ptr(new grid_rasterizer) +{ + setup(m); +} + +template +void grid_renderer::setup(Map const& m) { MAPNIK_LOG_DEBUG(grid_renderer) << "grid_renderer: Scale=" << m.scale(); + // nothing to do for grids yet on setup } template @@ -91,9 +101,25 @@ void grid_renderer::start_layer_processing(layer const& lay, box2d co if (lay.clear_label_cache()) { - detector_.clear(); + 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) + { + query_extent_.clip(*maximum_extent); + } } template @@ -144,21 +170,27 @@ void grid_renderer::render_marker(mapnik::feature_impl & feature, unsigned in else { image_data_32 const& data = **marker.get_bitmap_data(); - if (step == 1 && scale_factor_ == 1.0) + double width = data.width(); + double height = data.height(); + double cx = 0.5 * width; + double cy = 0.5 * height; + if (step == 1 && (std::fabs(1.0 - scale_factor_) < 0.001 && tr.is_identity())) { + // TODO - support opacity pixmap_.set_rectangle(feature.id(), data, - boost::math::iround(pos.x), - boost::math::iround(pos.y)); + boost::math::iround(pos.x - cx), + boost::math::iround(pos.y - cy)); } else { + // TODO - remove support for step != or add support for agg scaling with opacity double ratio = (1.0/step); image_data_32 target(ratio * data.width(), ratio * data.height()); mapnik::scale_image_agg(target,data, SCALING_NEAR, scale_factor_, 0.0, 0.0, 1.0, ratio); pixmap_.set_rectangle(feature.id(), target, - boost::math::iround(pos.x), - boost::math::iround(pos.y)); + boost::math::iround(pos.x - cx), + boost::math::iround(pos.y - cy)); } } pixmap_.add_feature(feature); diff --git a/src/grid/process_markers_symbolizer.cpp b/src/grid/process_markers_symbolizer.cpp index c3a81468d..cef9cb478 100644 --- a/src/grid/process_markers_symbolizer.cpp +++ b/src/grid/process_markers_symbolizer.cpp @@ -20,22 +20,55 @@ * *****************************************************************************/ +/* + +porting notes --> + + - grid includes + - detector + - no gamma + - mapnik::pixfmt_gray32 + - agg::scanline_bin sl + - grid_rendering_buffer + - agg::renderer_scanline_bin_solid + - clamping: + // - clamp sizes to > 4 pixels of interactivity + if (tr.scale() < 0.5) + { + agg::trans_affine tr2; + tr2 *= agg::trans_affine_scaling(0.5); + tr = tr2; + } + tr *= agg::trans_affine_scaling(scale_factor_*(1.0/pixmap_.get_resolution())); + - svg_renderer.render_id + - only encode feature if placements are found: + if (placed) + { + pixmap_.add_feature(feature); + } + +*/ + // mapnik -#include #include #include #include #include #include + +#include +#include +#include +#include #include #include #include -#include -#include -#include #include +#include #include +#include #include +#include // agg #include "agg_basics.h" @@ -46,6 +79,11 @@ #include "agg_path_storage.h" #include "agg_conv_clip_polyline.h" #include "agg_conv_transform.h" +#include "agg_image_filters.h" +#include "agg_trans_bilinear.h" +#include "agg_span_allocator.h" +#include "agg_image_accessors.h" +#include "agg_span_image_filter_rgba.h" // boost #include @@ -87,7 +125,7 @@ void grid_renderer::process(markers_symbolizer const& sym, agg::trans_affine geom_tr; evaluate_transform(geom_tr, feature, sym.get_transform()); - boost::optional marker = (*mark)->get_vector_data(); + boost::optional marker = (*mark)->get_vector_data(); box2d const& bbox = (*marker)->bounding_box(); agg::trans_affine tr; @@ -137,12 +175,12 @@ void grid_renderer::process(markers_symbolizer const& sym, box2d transformed_bbox = bbox * matrix; if (sym.get_allow_overlap() || - detector_.has_placement(transformed_bbox)) + detector_->has_placement(transformed_bbox)) { placed = true; - svg_renderer.render_id(*ras_ptr, sl, renb, feature.id(), matrix, sym.get_opacity(), bbox); + svg_renderer.render_id(*ras_ptr, sl, renb, feature.id(), matrix, 1, bbox); if (!sym.get_ignore_placement()) - detector_.insert(transformed_bbox); + detector_->insert(transformed_bbox); } } else if (sym.clip()) @@ -155,7 +193,7 @@ void grid_renderer::process(markers_symbolizer const& sym, clipped.clip_box(query_extent_.minx(),query_extent_.miny(),query_extent_.maxx(),query_extent_.maxy()); path_type path(t_,clipped,prj_trans); transformed_path_type path_transformed(path,geom_tr); - markers_placement placement(path_transformed, bbox, marker_trans, detector_, + markers_placement placement(path_transformed, bbox, marker_trans, *detector_, sym.get_spacing() * scale_factor_, sym.get_max_error(), sym.get_allow_overlap()); @@ -166,7 +204,7 @@ void grid_renderer::process(markers_symbolizer const& sym, agg::trans_affine matrix = marker_trans; matrix.rotate(angle); matrix.translate(x, y); - svg_renderer.render_id(*ras_ptr, sl, renb, feature.id(), matrix, sym.get_opacity(), bbox); + svg_renderer.render_id(*ras_ptr, sl, renb, feature.id(), matrix, 1, bbox); } } else @@ -175,7 +213,7 @@ void grid_renderer::process(markers_symbolizer const& sym, typedef agg::conv_transform transformed_path_type; path_type path(t_,geom,prj_trans); transformed_path_type path_transformed(path,geom_tr); - markers_placement placement(path_transformed, bbox, marker_trans, detector_, + markers_placement placement(path_transformed, bbox, marker_trans, *detector_, sym.get_spacing() * scale_factor_, sym.get_max_error(), sym.get_allow_overlap()); @@ -186,7 +224,7 @@ void grid_renderer::process(markers_symbolizer const& sym, agg::trans_affine matrix = marker_trans; matrix.rotate(angle); matrix.translate(x, y); - svg_renderer.render_id(*ras_ptr, sl, renb, feature.id(), matrix, sym.get_opacity(), bbox); + svg_renderer.render_id(*ras_ptr, sl, renb, feature.id(), matrix, 1, bbox); } } } diff --git a/src/grid/process_point_symbolizer.cpp b/src/grid/process_point_symbolizer.cpp index d1689f03b..eec6bf685 100644 --- a/src/grid/process_point_symbolizer.cpp +++ b/src/grid/process_point_symbolizer.cpp @@ -26,15 +26,22 @@ #include #include #include + #include #include #include #include #include +// agg +#include "agg_trans_affine.h" + // stl #include +// boost +#include + namespace mapnik { template @@ -57,14 +64,14 @@ void grid_renderer::process(point_symbolizer const& sym, if (marker) { box2d const& bbox = (*marker)->bounding_box(); - coord2d const center = bbox.center(); + coord2d center = bbox.center(); agg::trans_affine tr; evaluate_transform(tr, feature, sym.get_image_transform()); tr = agg::trans_affine_scaling(scale_factor_) * tr; - agg::trans_affine_translation const recenter(-center.x, -center.y); - agg::trans_affine const recenter_tr = recenter * tr; + agg::trans_affine_translation recenter(-center.x, -center.y); + agg::trans_affine recenter_tr = recenter * tr; box2d label_ext = bbox * recenter_tr; for (unsigned i=0; i::process(point_symbolizer const& sym, t_.forward(&x,&y); label_ext.re_center(x,y); if (sym.get_allow_overlap() || - detector_.has_placement(label_ext)) + detector_->has_placement(label_ext)) { render_marker(feature, @@ -94,7 +101,7 @@ void grid_renderer::process(point_symbolizer const& sym, sym.comp_op()); if (!sym.get_ignore_placement()) - detector_.insert(label_ext); + detector_->insert(label_ext); } } } diff --git a/src/grid/process_shield_symbolizer.cpp b/src/grid/process_shield_symbolizer.cpp index cf3d164b0..4792c2a46 100644 --- a/src/grid/process_shield_symbolizer.cpp +++ b/src/grid/process_shield_symbolizer.cpp @@ -47,7 +47,7 @@ void grid_renderer::process(shield_symbolizer const& sym, sym, feature, prj_trans, width_, height_, scale_factor_, - t_, font_manager_, detector_, + t_, font_manager_, *detector_, query_extent_); bool placement_found = false; diff --git a/src/grid/process_text_symbolizer.cpp b/src/grid/process_text_symbolizer.cpp index 1352793de..35a2d50fd 100644 --- a/src/grid/process_text_symbolizer.cpp +++ b/src/grid/process_text_symbolizer.cpp @@ -37,7 +37,7 @@ void grid_renderer::process(text_symbolizer const& sym, sym, feature, prj_trans, width_, height_, scale_factor_ * (1.0/pixmap_.get_resolution()), - t_, font_manager_, detector_, + t_, font_manager_, *detector_, query_extent_); bool placement_found = false; diff --git a/src/layer.cpp b/src/layer.cpp index 985eb5bad..09fbb0eb6 100644 --- a/src/layer.cpp +++ b/src/layer.cpp @@ -42,7 +42,8 @@ layer::layer(std::string const& name, std::string const& srs) clear_label_cache_(false), cache_features_(false), group_by_(""), - ds_() {} + ds_(), + buffer_size_(0) {} layer::layer(const layer& rhs) : name_(rhs.name_), @@ -55,7 +56,9 @@ layer::layer(const layer& rhs) cache_features_(rhs.cache_features_), group_by_(rhs.group_by_), styles_(rhs.styles_), - ds_(rhs.ds_) {} + ds_(rhs.ds_), + buffer_size_(rhs.buffer_size_), + maximum_extent_(rhs.maximum_extent_) {} layer& layer::operator=(const layer& rhs) { @@ -82,6 +85,8 @@ void layer::swap(const layer& rhs) group_by_ = rhs.group_by_; styles_=rhs.styles_; ds_=rhs.ds_; + buffer_size_ = rhs.buffer_size_; + maximum_extent_ = rhs.maximum_extent_; } layer::~layer() {} @@ -176,6 +181,31 @@ void layer::set_datasource(datasource_ptr const& ds) ds_ = ds; } +void layer::set_maximum_extent(box2d const& box) +{ + maximum_extent_.reset(box); +} + +boost::optional > const& layer::maximum_extent() const +{ + return maximum_extent_; +} + +void layer::reset_maximum_extent() +{ + maximum_extent_.reset(); +} + +void layer::set_buffer_size(int size) +{ + buffer_size_ = size; +} + +int layer::buffer_size() const +{ + return buffer_size_; +} + box2d layer::envelope() const { if (ds_) return ds_->envelope(); diff --git a/src/load_map.cpp b/src/load_map.cpp index 881e8379e..4f6efcb3f 100644 --- a/src/load_map.cpp +++ b/src/load_map.cpp @@ -575,66 +575,95 @@ bool map_parser::parse_font(font_set &fset, xml_node const& f) return false; } -void map_parser::parse_layer(Map & map, xml_node const& lay) +void map_parser::parse_layer(Map & map, xml_node const& node) { std::string name; try { - name = lay.get_attr("name", std::string("Unnamed")); + name = node.get_attr("name", std::string("Unnamed")); // XXX if no projection is given inherit from map? [DS] - std::string srs = lay.get_attr("srs", map.srs()); + std::string srs = node.get_attr("srs", map.srs()); layer lyr(name, srs); - optional status = lay.get_opt_attr("status"); + optional status = node.get_opt_attr("status"); if (status) { lyr.set_active(* status); } - optional min_zoom = lay.get_opt_attr("minzoom"); + optional min_zoom = node.get_opt_attr("minzoom"); if (min_zoom) { lyr.set_min_zoom(* min_zoom); } - optional max_zoom = lay.get_opt_attr("maxzoom"); + optional max_zoom = node.get_opt_attr("maxzoom"); if (max_zoom) { lyr.set_max_zoom(* max_zoom); } - optional queryable = lay.get_opt_attr("queryable"); + optional queryable = node.get_opt_attr("queryable"); if (queryable) { lyr.set_queryable(* queryable); } optional clear_cache = - lay.get_opt_attr("clear-label-cache"); + node.get_opt_attr("clear-label-cache"); if (clear_cache) { lyr.set_clear_label_cache(* clear_cache); } optional cache_features = - lay.get_opt_attr("cache-features"); + node.get_opt_attr("cache-features"); if (cache_features) { lyr.set_cache_features(* cache_features); } optional group_by = - lay.get_opt_attr("group-by"); + node.get_opt_attr("group-by"); if (group_by) { lyr.set_group_by(* group_by); } - xml_node::const_iterator child = lay.begin(); - xml_node::const_iterator end = lay.end(); + optional buffer_size = node.get_opt_attr("buffer-size"); + if (buffer_size) + { + lyr.set_buffer_size(*buffer_size); + } + + optional maximum_extent = node.get_opt_attr("maximum-extent"); + if (maximum_extent) + { + box2d box; + if (box.from_string(*maximum_extent)) + { + lyr.set_maximum_extent(box); + } + else + { + std::ostringstream s_err; + s_err << "failed to parse 'maximum-extent' in layer " << name; + if (strict_) + { + throw config_error(s_err.str()); + } + else + { + MAPNIK_LOG_WARN(load_map) << "map_parser: " << s_err.str(); + } + } + } + + xml_node::const_iterator child = node.begin(); + xml_node::const_iterator end = node.end(); for(; child != end; ++child) { @@ -719,7 +748,7 @@ void map_parser::parse_layer(Map & map, xml_node const& lay) { if (!name.empty()) { - ex.append_context(std::string(" encountered during parsing of layer '") + name + "'", lay); + ex.append_context(std::string(" encountered during parsing of layer '") + name + "'", node); } throw; } @@ -894,7 +923,7 @@ void map_parser::parse_point_symbolizer(rule & rule, xml_node const & sym) symbol.set_ignore_placement(* ignore_placement); } point_placement_e placement = - sym.get_attr("placement", CENTROID_POINT_PLACEMENT); + sym.get_attr("placement", symbol.get_point_placement()); symbol.set_point_placement(placement); if (file && !file->empty()) @@ -1018,9 +1047,13 @@ void map_parser::parse_markers_symbolizer(rule & rule, xml_node const& node) sym.set_filename(expr); } + // overall opacity - impacts both fill and stroke, like svg optional opacity = node.get_opt_attr("opacity"); if (opacity) sym.set_opacity(*opacity); + optional fill_opacity = node.get_opt_attr("fill-opacity"); + if (fill_opacity) sym.set_fill_opacity(*fill_opacity); + optional image_transform_wkt = node.get_opt_attr("transform"); if (image_transform_wkt) { @@ -1065,7 +1098,7 @@ void map_parser::parse_markers_symbolizer(rule & rule, xml_node const& node) sym.set_stroke(strk); } - marker_placement_e placement = node.get_attr("placement", MARKER_POINT_PLACEMENT); + marker_placement_e placement = node.get_attr("placement",sym.get_marker_placement()); sym.set_marker_placement(placement); parse_symbolizer_base(sym, node); rule.append(sym); diff --git a/src/map.cpp b/src/map.cpp index 7a9d1f14b..c94509df7 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -352,7 +352,7 @@ void Map::set_background_image(std::string const& image_filename) void Map::set_maximum_extent(box2d const& box) { - maximum_extent_ = box; + maximum_extent_.reset(box); } boost::optional > const& Map::maximum_extent() const @@ -360,9 +360,9 @@ boost::optional > const& Map::maximum_extent() const return maximum_extent_; } -boost::optional > & Map::maximum_extent() +void Map::reset_maximum_extent() { - return maximum_extent_; + maximum_extent_.reset(); } std::string const& Map::base_path() const diff --git a/src/marker_cache.cpp b/src/marker_cache.cpp index 96c0e0a45..9dcaea7f4 100644 --- a/src/marker_cache.cpp +++ b/src/marker_cache.cpp @@ -144,7 +144,7 @@ boost::optional marker_cache::find(std::string const& uri, } std::string known_svg_string = mark_itr->second; using namespace mapnik::svg; - path_ptr marker_path(boost::make_shared()); + svg_path_ptr marker_path(boost::make_shared()); vertex_stl_adapter stl_storage(marker_path->source()); svg_path_adapter svg_path(stl_storage); svg_converter_type svg(svg_path, marker_path->attributes()); @@ -173,7 +173,7 @@ boost::optional marker_cache::find(std::string const& uri, if (is_svg(uri)) { using namespace mapnik::svg; - path_ptr marker_path(boost::make_shared()); + svg_path_ptr marker_path(boost::make_shared()); vertex_stl_adapter stl_storage(marker_path->source()); svg_path_adapter svg_path(stl_storage); svg_converter_type svg(svg_path, marker_path->attributes()); diff --git a/src/markers_placement.cpp b/src/markers_placement.cpp deleted file mode 100644 index 6ea4b0e14..000000000 --- a/src/markers_placement.cpp +++ /dev/null @@ -1,253 +0,0 @@ -// mapnik -#include -#include -#include -#include -#include //round -// agg -#include "agg_basics.h" -#include "agg_conv_clip_polyline.h" -#include "agg_trans_affine.h" -#include "agg_conv_transform.h" -#include "agg_conv_smooth_poly1.h" -// stl -#include - -namespace mapnik -{ -template -markers_placement::markers_placement( - Locator &locator, box2d const& size, agg::trans_affine const& tr, Detector &detector, double spacing, double max_error, bool allow_overlap) - : locator_(locator), size_(size), tr_(tr), detector_(detector), max_error_(max_error), allow_overlap_(allow_overlap) -{ - marker_width_ = (size_ * tr_).width(); - if (spacing >= 0) - { - spacing_ = spacing; - } else if (spacing < 0) - { - spacing_ = find_optimal_spacing(-spacing); - } - rewind(); -} - -template -double markers_placement::find_optimal_spacing(double s) -{ - rewind(); - //Calculate total path length - unsigned cmd = agg::path_cmd_move_to; - double length = 0; - while (!agg::is_stop(cmd)) - { - double dx = next_x - last_x; - double dy = next_y - last_y; - length += std::sqrt(dx * dx + dy * dy); - last_x = next_x; - last_y = next_y; - while (agg::is_move_to(cmd = locator_.vertex(&next_x, &next_y))) - { - //Skip over "move" commands - last_x = next_x; - last_y = next_y; - } - } - unsigned points = round(length / s); - if (points == 0) return 0.0; //Path to short - return length / points; -} - - -template -void markers_placement::rewind() -{ - locator_.rewind(0); - //Get first point - done_ = agg::is_stop(locator_.vertex(&next_x, &next_y)) || spacing_ < marker_width_; - last_x = next_x; - last_y = next_y; // Force request of new segment - error_ = 0; - marker_nr_ = 0; -} - -template -bool markers_placement::get_point( - double & x, double & y, double & angle, bool add_to_detector) -{ - if (done_) return false; - - unsigned cmd; - - /* This functions starts at the position of the previous marker, - walks along the path, counting how far it has to go in spacing_left. - If one marker can't be placed at the position it should go to it is - moved a bit. The error is compensated for in the next call to this - function. - - error > 0: Marker too near to the end of the path. - error = 0: Perfect position. - error < 0: Marker too near to the beginning of the path. - */ - - if (marker_nr_++ == 0) - { - //First marker - spacing_left_ = spacing_ / 2; - } else - { - spacing_left_ = spacing_; - } - - spacing_left_ -= error_; - error_ = 0; - - //Loop exits when a position is found or when no more segments are available - while (true) - { - //Do not place markers too close to the beginning of a segment - if (spacing_left_ < marker_width_/2) - { - set_spacing_left(marker_width_/2); //Only moves forward - } - //Error for this marker is too large. Skip to the next position. - if (abs(error_) > max_error_ * spacing_) - { - if (error_ > spacing_) - { - error_ = 0; //Avoid moving backwards - - MAPNIK_LOG_WARN(markers_placement) << "Extremely large error in markers_placement. Please file a bug report."; - } - spacing_left_ += spacing_ - error_; - error_ = 0; - } - double dx = next_x - last_x; - double dy = next_y - last_y; - double segment_length = std::sqrt(dx * dx + dy * dy); - if (segment_length <= spacing_left_) - { - //Segment is to short to place marker. Find next segment - spacing_left_ -= segment_length; - last_x = next_x; - last_y = next_y; - while (agg::is_move_to(cmd = locator_.vertex(&next_x, &next_y))) - { - //Skip over "move" commands - last_x = next_x; - last_y = next_y; - } - if (agg::is_stop(cmd)) - { - done_ = true; - return false; - } - continue; //Try again - } - - /* At this point we know the following things: - - segment_length > spacing_left - - error is small enough - - at least half a marker fits into this segment - */ - - //Check if marker really fits in this segment - if (segment_length < marker_width_) - { - //Segment to short => Skip this segment - set_spacing_left(segment_length + marker_width_/2); //Only moves forward - continue; - } else if (segment_length - spacing_left_ < marker_width_/2) - { - //Segment is long enough, but we are to close to the end - - //Note: This function moves backwards. This could lead to an infinite - // loop when another function adds a positive offset. Therefore we - // only move backwards when there is no offset - if (error_ == 0) - { - set_spacing_left(segment_length - marker_width_/2, true); - } else - { - //Skip this segment - set_spacing_left(segment_length + marker_width_/2); //Only moves forward - } - continue; //Force checking of max_error constraint - } - angle = atan2(dy, dx); - x = last_x + dx * (spacing_left_ / segment_length); - y = last_y + dy * (spacing_left_ / segment_length); - - box2d box = perform_transform(angle, x, y); - - if (!allow_overlap_ && !detector_.has_placement(box)) - { - //10.0 is the approxmiate number of positions tried and choosen arbitrarily - set_spacing_left(spacing_left_ + spacing_ * max_error_ / 10.0); //Only moves forward - continue; - } - if (add_to_detector) detector_.insert(box); - last_x = x; - last_y = y; - return true; - } -} - - -template -box2d markers_placement::perform_transform(double angle, double dx, double dy) -{ - double x1 = size_.minx(); - double x2 = size_.maxx(); - double y1 = size_.miny(); - double y2 = size_.maxy(); - - agg::trans_affine tr = tr_ * agg::trans_affine_rotation(angle).translate(dx, dy); - - double xA = x1, yA = y1, xB = x2, yB = y1, xC = x2, yC = y2, xD = x1, yD = y2; - tr.transform(&xA, &yA); - tr.transform(&xB, &yB); - tr.transform(&xC, &yC); - tr.transform(&xD, &yD); - - box2d result(xA, yA, xC, yC); - result.expand_to_include(xB, yB); - result.expand_to_include(xD, yD); - return result; -} - -template -void markers_placement::set_spacing_left(double sl, bool allow_negative) -{ - double delta_error = sl - spacing_left_; - if (!allow_negative && delta_error < 0) - { - MAPNIK_LOG_WARN(markers_placement) << "Unexpected negative error in markers_placement. Please file a bug report."; - - return; - } -#ifdef MAPNIK_DEBUG - if (delta_error == 0.0) - { - MAPNIK_LOG_WARN(markers_placement) << "Not moving at all in set_spacing_left()! Please file a bug report."; - } -#endif - error_ += delta_error; - spacing_left_ = sl; -} - -typedef agg::conv_clip_polyline clipped_geometry_type; -typedef coord_transform path_type; -typedef coord_transform clipped_path_type; -typedef agg::conv_transform transformed_path_type; - -template class markers_placement; -template class markers_placement; -template class markers_placement; -template class markers_placement; -template class markers_placement; -template class markers_placement, label_collision_detector4>; -template class markers_placement, label_collision_detector4>; -template class markers_placement, label_collision_detector4>; -template class markers_placement, label_collision_detector4>; -template class markers_placement, label_collision_detector4>; -} //ns mapnik diff --git a/src/markers_symbolizer.cpp b/src/markers_symbolizer.cpp index c494bf7fc..9de55e492 100644 --- a/src/markers_symbolizer.cpp +++ b/src/markers_symbolizer.cpp @@ -30,6 +30,7 @@ namespace mapnik { static const char * marker_placement_strings[] = { "point", + "interior", "line", "" }; @@ -45,10 +46,7 @@ markers_symbolizer::markers_symbolizer() allow_overlap_(false), spacing_(100.0), max_error_(0.2), - marker_p_(MARKER_POINT_PLACEMENT) { - // override the default for clipping in symbolizer base - this->set_clip(false); - } + marker_p_(MARKER_POINT_PLACEMENT) { } markers_symbolizer::markers_symbolizer(path_expression_ptr const& filename) : symbolizer_with_image(filename), @@ -59,10 +57,7 @@ markers_symbolizer::markers_symbolizer(path_expression_ptr const& filename) allow_overlap_(false), spacing_(100.0), max_error_(0.2), - marker_p_(MARKER_POINT_PLACEMENT) { - // override the default for clipping in symbolizer base - this->set_clip(false); - } + marker_p_(MARKER_POINT_PLACEMENT) { } markers_symbolizer::markers_symbolizer(markers_symbolizer const& rhs) : symbolizer_with_image(rhs), @@ -74,6 +69,8 @@ markers_symbolizer::markers_symbolizer(markers_symbolizer const& rhs) spacing_(rhs.spacing_), max_error_(rhs.max_error_), fill_(rhs.fill_), + fill_opacity_(rhs.fill_opacity_), + opacity_(rhs.opacity_), stroke_(rhs.stroke_), marker_p_(rhs.marker_p_) {} @@ -117,6 +114,16 @@ double markers_symbolizer::get_max_error() const return max_error_; } +void markers_symbolizer::set_opacity(float opacity) +{ + opacity_ = opacity; +} + +boost::optional markers_symbolizer::get_opacity() const +{ + return opacity_; +} + void markers_symbolizer::set_fill(color const& fill) { fill_ = fill; @@ -127,6 +134,16 @@ boost::optional markers_symbolizer::get_fill() const return fill_; } +void markers_symbolizer::set_fill_opacity(float opacity) +{ + fill_opacity_ = opacity; +} + +boost::optional markers_symbolizer::get_fill_opacity() const +{ + return fill_opacity_; +} + void markers_symbolizer::set_width(expression_ptr const& width) { width_ = width; diff --git a/src/save_map.cpp b/src/save_map.cpp index 7ba99a75d..388866cfc 100644 --- a/src/save_map.cpp +++ b/src/save_map.cpp @@ -78,6 +78,11 @@ public: { set_attr( sym_node, "placement", sym.get_point_placement() ); } + if (sym.get_image_transform()) + { + std::string tr_str = sym.get_image_transform_string(); + set_attr( sym_node, "transform", tr_str ); + } serialize_symbolizer_base(sym_node, sym); } @@ -218,6 +223,11 @@ public: { set_attr(sym_node, "shield-dy", displacement.y); } + if (sym.get_image_transform()) + { + std::string tr_str = sym.get_image_transform_string(); + set_attr( sym_node, "transform", tr_str ); + } serialize_symbolizer_base(sym_node, sym); } @@ -249,7 +259,6 @@ public: { set_attr( sym_node, "height", mapnik::to_expression_string(*sym.height()) ); } - serialize_symbolizer_base(sym_node, sym); } @@ -283,6 +292,10 @@ public: { set_attr( sym_node, "fill", sym.get_fill() ); } + if (sym.get_fill_opacity() != dfl.get_fill_opacity() || explicit_defaults_) + { + set_attr( sym_node, "fill-opacity", sym.get_fill_opacity() ); + } if (sym.get_opacity() != dfl.get_opacity() || explicit_defaults_) { set_attr( sym_node, "opacity", sym.get_opacity() ); @@ -454,7 +467,7 @@ private: } if ( strk.dash_offset() != dfl.dash_offset() || explicit_defaults_ ) { - set_attr( node, "stroke-dash-offset", strk.dash_offset()); + set_attr( node, "stroke-dashoffset", strk.dash_offset()); } if ( ! strk.get_dash_array().empty() ) { @@ -728,6 +741,22 @@ 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(); + if ( buffer_size || explicit_defaults) + { + set_attr( layer_node, "buffer-size", buffer_size ); + } + + optional > const& maximum_extent = layer.maximum_extent(); + if ( maximum_extent) + { + std::ostringstream s; + s << std::setprecision(16) + << maximum_extent->minx() << "," << maximum_extent->miny() << "," + << maximum_extent->maxx() << "," << maximum_extent->maxy(); + set_attr( layer_node, "maximum-extent", s.str() ); + } + std::vector const& style_names = layer.styles(); for (unsigned i = 0; i < style_names.size(); ++i) { @@ -775,7 +804,7 @@ void serialize_map(ptree & pt, Map const & map, bool explicit_defaults) set_attr( map_node, "background-image", *image_filename ); } - unsigned buffer_size = map.buffer_size(); + int buffer_size = map.buffer_size(); if ( buffer_size || explicit_defaults) { set_attr( map_node, "buffer-size", buffer_size ); diff --git a/src/symbolizer_helpers.cpp b/src/symbolizer_helpers.cpp index 4b63e707c..4da9bac35 100644 --- a/src/symbolizer_helpers.cpp +++ b/src/symbolizer_helpers.cpp @@ -221,7 +221,11 @@ void text_symbolizer_helper::initialize_points() } else { - if (how_placed == POINT_PLACEMENT) + if (geom.type() == LineString) + { + label::middle_point(geom, label_x,label_y); + } + else if (how_placed == POINT_PLACEMENT) { label::centroid(geom, label_x, label_y); } diff --git a/tests/data/good_maps/marker_ellipse_transform.xml b/tests/data/good_maps/marker_ellipse_transform.xml new file mode 100644 index 000000000..e96595619 --- /dev/null +++ b/tests/data/good_maps/marker_ellipse_transform.xml @@ -0,0 +1,66 @@ + + + + + + ellipse + + csv + +x,y +2.5,2.5 + + + + + + + + + + frame + + csv + +x,y +0,0 +5,0 +0,5 +5,5 + + + + + \ No newline at end of file diff --git a/tests/data/good_maps/marker_ellipse_transform2.xml b/tests/data/good_maps/marker_ellipse_transform2.xml new file mode 100644 index 000000000..048353adf --- /dev/null +++ b/tests/data/good_maps/marker_ellipse_transform2.xml @@ -0,0 +1,66 @@ + + + + + + ellipse + + csv + +x,y +2.5,2.5 + + + + + + + + + + frame + + csv + +x,y +0,0 +5,0 +0,5 +5,5 + + + + + \ No newline at end of file diff --git a/tests/data/good_maps/markers_symbolizer_lines.xml b/tests/data/good_maps/markers_symbolizer_lines.xml index 74adff287..e1f879ad9 100644 --- a/tests/data/good_maps/markers_symbolizer_lines.xml +++ b/tests/data/good_maps/markers_symbolizer_lines.xml @@ -3,7 +3,14 @@ diff --git a/tests/data/good_maps/markers_symbolizer_lines_file.xml b/tests/data/good_maps/markers_symbolizer_lines_file.xml index 39d2f1dbd..e08621485 100644 --- a/tests/data/good_maps/markers_symbolizer_lines_file.xml +++ b/tests/data/good_maps/markers_symbolizer_lines_file.xml @@ -2,8 +2,15 @@ diff --git a/tests/data/good_maps/text_halo_and_collision.xml b/tests/data/good_maps/text_halo_and_collision.xml new file mode 100644 index 000000000..b44b3e2dd --- /dev/null +++ b/tests/data/good_maps/text_halo_and_collision.xml @@ -0,0 +1,52 @@ + + + + + + + + labels + + csv + +x,y +2,2 +2,2.3 + + + + + + + + + + frame + + csv + +x,y +0,0 +5,0 +0,5 +5,5 + + + + + \ No newline at end of file diff --git a/tests/data/svg/octocat.svg b/tests/data/svg/octocat.svg new file mode 100644 index 000000000..1f883261b --- /dev/null +++ b/tests/data/svg/octocat.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/python_tests/images/support/mapnik-marker-ellipse-render1.png b/tests/python_tests/images/support/mapnik-marker-ellipse-render1.png new file mode 100644 index 000000000..5a74baad0 Binary files /dev/null and b/tests/python_tests/images/support/mapnik-marker-ellipse-render1.png differ diff --git a/tests/python_tests/images/support/mapnik-wgs842merc-reprojection-render.png b/tests/python_tests/images/support/mapnik-wgs842merc-reprojection-render.png index b5f8a7c1b..029212655 100644 Binary files a/tests/python_tests/images/support/mapnik-wgs842merc-reprojection-render.png and b/tests/python_tests/images/support/mapnik-wgs842merc-reprojection-render.png differ diff --git a/tests/python_tests/markers_complex_rendering_test.py b/tests/python_tests/markers_complex_rendering_test.py new file mode 100644 index 000000000..a3173abbd --- /dev/null +++ b/tests/python_tests/markers_complex_rendering_test.py @@ -0,0 +1,39 @@ +#coding=utf8 +import os +import mapnik +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_marker_ellipse_render1(): + m = mapnik.Map(256,256) + mapnik.load_map(m,'../data/good_maps/marker_ellipse_transform.xml') + m.zoom_all() + im = mapnik.Image(m.width,m.height) + mapnik.render(m,im) + actual = '/tmp/mapnik-marker-ellipse-render1.png' + expected = 'images/support/mapnik-marker-ellipse-render1.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_marker_ellipse_render2(): +# # currently crashes https://github.com/mapnik/mapnik/issues/1365 +# m = mapnik.Map(256,256) +# mapnik.load_map(m,'../data/good_maps/marker_ellipse_transform2.xml') +# m.zoom_all() +# im = mapnik.Image(m.width,m.height) +# mapnik.render(m,im) +# actual = '/tmp/mapnik-marker-ellipse-render2.png' +# expected = 'images/support/mapnik-marker-ellipse-render2.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)) + +if __name__ == "__main__": + setup() + [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 7806ada66..0197d2e7c 100644 --- a/tests/python_tests/object_test.py +++ b/tests/python_tests/object_test.py @@ -21,6 +21,15 @@ def test_line_symbolizer_init(): s = mapnik.LineSymbolizer() eq_(s.rasterizer, mapnik.line_rasterizer.FULL) +def test_line_symbolizer_stroke_reference(): + l = mapnik.LineSymbolizer(mapnik.Color('green'),0.1) + l.stroke.add_dash(.1,.1) + l.stroke.add_dash(.1,.1) + eq_(l.stroke.get_dashes(), [(.1,.1),(.1,.1)]) + eq_(l.stroke.color,mapnik.Color('green')) + eq_(l.stroke.opacity,1.0) + assert_almost_equal(l.stroke.width,0.1) + # ShieldSymbolizer initialization def test_shieldsymbolizer_init(): s = mapnik.ShieldSymbolizer(mapnik.Expression('[Field Name]'), 'DejaVu Sans Bold', 6, mapnik.Color('#000000'), mapnik.PathExpression('../data/images/dummy.png')) @@ -126,7 +135,8 @@ def test_pointsymbolizer_init(): def test_markersymbolizer_init(): p = mapnik.MarkersSymbolizer() eq_(p.allow_overlap, False) - eq_(p.opacity,1) + eq_(p.opacity,None) + eq_(p.fill_opacity,None) eq_(p.filename,'shape://ellipse') eq_(p.placement,mapnik.marker_placement.POINT_PLACEMENT) eq_(p.fill,None) @@ -154,9 +164,11 @@ def test_markersymbolizer_init(): p.fill = mapnik.Color('white') p.allow_overlap = True p.opacity = 0.5 + p.fill_opacity = .01 eq_(p.allow_overlap, True) eq_(p.opacity, 0.5) + eq_(p.fill_opacity, 0.5) # PointSymbolizer missing image file diff --git a/tests/python_tests/render_grid_test.py b/tests/python_tests/render_grid_test.py index f2ce75b3c..2a9369946 100644 --- a/tests/python_tests/render_grid_test.py +++ b/tests/python_tests/render_grid_test.py @@ -20,13 +20,13 @@ def setup(): grid_correct_old = {"keys": ["", "North West", "North East", "South West", "South East"], "data": {"South East": {"Name": "South East"}, "North East": {"Name": "North East"}, "North West": {"Name": "North West"}, "South West": {"Name": "South West"}}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " !!! ### ", " !!!!! ##### ", " !!!!! ##### ", " !!! ### ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " $$$$ %%%% ", " $$$$$ %%%%% ", " $$$$$ %%%%% ", " $$$ %%% ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "]} # now using svg rendering -grid_correct_old2 = {"data": {"North East": {"Name": "North East"}, "North West": {"Name": "North West"}, "South East": {"Name": "South East"}, "South West": {"Name": "South West"}}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " !!! ### ", " !!! ### ", " !!! ### ", " !!! ### ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " $$$ %%% ", " $$$ %%% ", " $$$ %%% ", " $ % ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "], "keys": ["", "North West", "North East", "South West", "South East"]} +grid_correct_old2 = {"data": {"North East": {"Name": "North East"}, "North West": {"Name": "North West"}, "South East": {"Name": "South East"}, "South West": {"Name": "South West"}}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " !!! ### ", " !!! ### ", " !!! ### ", " !!! ### ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " $$$ %%% ", " $$$ %%% ", " $$$ %%% ", " $$$ %%% ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "], "keys": ["", "North West", "North East", "South West", "South East"]} # previous rendering using agg ellipse directly -grid_correct_new = {"keys": ["", "North West", "North East", "South West", "South East"], "data": {"South East": {"Name": "South East"}, "North East": {"Name": "North East"}, "North West": {"Name": "North West"}, "South West": {"Name": "South West"}}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " !! ## ", " !!! ### ", " !! ## ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " $$$ %% ", " $$$ %%% ", " $$ %% ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "]} +grid_correct_new = {"data": {"North East": {"Name": "North East"}, "North West": {"Name": "North West"}, "South East": {"Name": "South East"}, "South West": {"Name": "South West"}}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " !! ## ", " !!! ### ", " !! ## ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " $$ %% ", " $$$ %% ", " $$ %% ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "], "keys": ["", "North West", "North East", "South West", "South East"]} # newer rendering using svg -grid_correct_new2 = {"data": {"North East": {"Name": "North East"}, "North West": {"Name": "North West"}, "South East": {"Name": "South East"}, "South West": {"Name": "South West"}}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " !! ## ", " !!! ## ", " !! ## ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " $$ %% ", " $$ %% ", " $ % ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "], "keys": ["", "North West", "North East", "South West", "South East"]} +grid_correct_new2 = {"data": {"North East": {"Name": "North East"}, "North West": {"Name": "North West"}, "South East": {"Name": "South East"}, "South West": {"Name": "South West"}}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " !! ## ", " !!! ### ", " !! ## ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " $$ %% ", " $$$ %% ", " $$ %% ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "], "keys": ["", "North West", "North East", "South West", "South East"]} def resolve(grid,row,col): """ Resolve the attributes for a given pixel in a grid. @@ -108,7 +108,7 @@ def show_grids(name,g1,g2): val += '\n\t%s\n\t%s' % (g1_file,g2_file) return val -def test_render_grid(): +def test_render_grid_old(): """ test old method """ width,height = 256,256 m = create_grid_map(width,height) @@ -117,7 +117,7 @@ def test_render_grid(): lr_lonlat = mapnik.Coord(143.40,-38.80) m.zoom_to_box(mapnik.Box2d(ul_lonlat,lr_lonlat)) grid = mapnik.render_grid(m,0,key='Name',resolution=4,fields=['Name']) - eq_(grid,grid_correct_old2,show_grids('old',grid,grid_correct_old2)) + eq_(grid,grid_correct_old2,show_grids('old-markers',grid,grid_correct_old2)) eq_(resolve(grid,0,0),None) # check every pixel of the nw symbol @@ -128,7 +128,7 @@ def test_render_grid(): eq_(resolve(grid,23,10),expected) eq_(resolve(grid,23,11),expected) -def test_render_grid2(): +def test_render_grid_new(): """ test old against new""" width,height = 256,256 m = create_grid_map(width,height) @@ -140,7 +140,7 @@ def test_render_grid2(): grid = mapnik.Grid(m.width,m.height,key='Name') mapnik.render_layer(m,grid,layer=0,fields=['Name']) utf1 = grid.encode('utf',resolution=4) - eq_(utf1,grid_correct_new2,show_grids('new',utf1,grid_correct_new2)) + eq_(utf1,grid_correct_new2,show_grids('new-markers',utf1,grid_correct_new2)) # check a full view is the same as a full image grid_view = grid.view(0,0,width,height) @@ -164,7 +164,7 @@ def test_render_grid2(): grid_feat_id = {'keys': ['', '3', '4', '2', '1'], 'data': {'1': {'Name': 'South East'}, '3': {'Name': u'North West'}, '2': {'Name': 'South West'}, '4': {'Name': 'North East'}}, 'grid': [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' !! ## ', ' !!! ### ', ' !! ## ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' $$$ %% ', ' $$$ %%% ', ' $$ %% ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ']} -grid_feat_id2 = {"data": {"1": {"Name": "South East"}, "2": {"Name": "South West"}, "3": {"Name": "North West"}, "4": {"Name": "North East"}}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " !! ## ", " !!! ## ", " !! ## ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " $$ %% ", " $$ %% ", " $ % ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "], "keys": ["", "3", "4", "2", "1"]} +grid_feat_id2 = {"data": {"1": {"Name": "South East"}, "2": {"Name": "South West"}, "3": {"Name": "North West"}, "4": {"Name": "North East"}}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " !! ## ", " !!! ### ", " !! ## ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " $$ %% ", " $$$ %% ", " $$ %% ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "], "keys": ["", "3", "4", "2", "1"]} def test_render_grid3(): """ test using feature id""" @@ -177,7 +177,7 @@ def test_render_grid3(): grid = mapnik.Grid(m.width,m.height,key='__id__') mapnik.render_layer(m,grid,layer=0,fields=['__id__','Name']) utf1 = grid.encode('utf',resolution=4) - eq_(utf1,grid_feat_id2,show_grids('id',utf1,grid_feat_id2)) + eq_(utf1,grid_feat_id2,show_grids('id-markers',utf1,grid_feat_id2)) # check a full view is the same as a full image grid_view = grid.view(0,0,width,height) # for kicks check at full res too @@ -282,7 +282,7 @@ def test_line_rendering(): eq_(utf1,line_expected,show_grids('line',utf1,line_expected)) #open('test.json','w').write(json.dumps(grid.encode())) -point_expected = {"keys": ["", "3", "4", "2", "1"], "data": {"1": {"Name": "South East"}, "3": {"Name": "North West"}, "2": {"Name": "South West"}, "4": {"Name": "North East"}}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " !!!! #### ", " !!!! #### ", " !!!! #### ", " !!!! #### ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " $$$$ %%%% ", " $$$$ %%%% ", " $$$$ %%%% ", " $$$$ %%%% ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "]} +point_expected = {"data": {"1": {"Name": "South East"}, "2": {"Name": "South West"}, "3": {"Name": "North West"}, "4": {"Name": "North East"}}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " !!!! #### ", " !!!! #### ", " !!!! #### ", " !!!! #### ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " $$$$ %%%% ", " $$$$ %%%% ", " $$$$ %%%% ", " $$$$ %%%% ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "], "keys": ["", "3", "4", "2", "1"]} def test_point_symbolizer_grid(): width,height = 256,256 diff --git a/utils/svg2png/svg2png.cpp b/utils/svg2png/svg2png.cpp index 2c9113b08..83a4b695e 100644 --- a/utils/svg2png/svg2png.cpp +++ b/utils/svg2png/svg2png.cpp @@ -155,8 +155,9 @@ int main (int argc,char** argv) { std::clog << "found width of '" << w << "' and height of '" << h << "'\n"; } - mapnik::image_32 im(w,h); - agg::rendering_buffer buf(im.raw_data(), w, h, w * 4); + // 10 pixel buffer to avoid edge clipping of 100% svg's + mapnik::image_32 im(w+10,h+10); + agg::rendering_buffer buf(im.raw_data(), im.width(), im.height(), im.width() * 4); pixfmt pixf(buf); renderer_base renb(pixf); @@ -165,7 +166,7 @@ int main (int argc,char** argv) // center the svg marker on '0,0' agg::trans_affine mtx = agg::trans_affine_translation(-c.x,-c.y); // render the marker at the center of the marker box - mtx.translate(0.5 * w, 0.5 * h); + mtx.translate(0.5 * im.width(), 0.5 * im.height()); mapnik::svg::vertex_stl_adapter stl_storage((*marker.get_vector_data())->source()); mapnik::svg::svg_path_adapter svg_path(stl_storage);