diff --git a/bindings/python/mapnik_line_symbolizer.cpp b/bindings/python/mapnik_line_symbolizer.cpp index 2c36800b74..d8460c4c77 100644 --- a/bindings/python/mapnik_line_symbolizer.cpp +++ b/bindings/python/mapnik_line_symbolizer.cpp @@ -48,6 +48,10 @@ void export_line_symbolizer() (&line_symbolizer::get_stroke, return_value_policy()), &line_symbolizer::set_stroke) + .add_property("simplify_tolerance", + &line_symbolizer::simplify_tolerance, + &line_symbolizer::set_simplify_tolerance, + "simplification tolerance measure") .add_property("offset", &line_symbolizer::offset, &line_symbolizer::set_offset, diff --git a/bindings/python/mapnik_polygon_symbolizer.cpp b/bindings/python/mapnik_polygon_symbolizer.cpp index 218cf52792..b4d4d23e5e 100644 --- a/bindings/python/mapnik_polygon_symbolizer.cpp +++ b/bindings/python/mapnik_polygon_symbolizer.cpp @@ -60,6 +60,10 @@ void export_polygon_symbolizer() &polygon_symbolizer::smooth, &polygon_symbolizer::set_smooth, "Set/get the polygon geometry's smooth value") + .add_property("simplify_tolerance", + &polygon_symbolizer::simplify_tolerance, + &polygon_symbolizer::set_simplify_tolerance, + "simplfication tolerance measure") ; } diff --git a/include/mapnik/simplify.hpp b/include/mapnik/simplify.hpp new file mode 100644 index 0000000000..f895229d87 --- /dev/null +++ b/include/mapnik/simplify.hpp @@ -0,0 +1,25 @@ +#ifndef MAPNIK_SIMPLIFY_HPP +#define MAPNIK_SIMPLIFY_HPP + +#include + +// Boost +#include + +namespace mapnik +{ + +enum simplify_algorithm_e +{ + radial_distance = 0, + douglas_peucker, + visvalingam_whyatt, + zhao_saalfeld +}; + +MAPNIK_DECL boost::optional simplify_algorithm_from_string(std::string const& name); +MAPNIK_DECL boost::optional simplify_algorithm_to_string(simplify_algorithm_e algorithm); + +} + +#endif // MAPNIK_SIMPLIFY_HPP diff --git a/include/mapnik/simplify_converter.hpp b/include/mapnik/simplify_converter.hpp new file mode 100644 index 0000000000..2476311d22 --- /dev/null +++ b/include/mapnik/simplify_converter.hpp @@ -0,0 +1,452 @@ +#ifndef MAPNIK_SIMPLIFY_CONVERTER_HPP +#define MAPNIK_SIMPLIFY_CONVERTER_HPP + +#include +#include +#include +#include + +// STL +#include +#include +#include +#include +// Boost +#include + +namespace mapnik +{ + +struct weighted_vertex : private boost::noncopyable +{ + vertex2d coord; + double weight; + weighted_vertex *prev; + weighted_vertex *next; + + weighted_vertex(vertex2d coord_) : + coord(coord_), + weight(std::numeric_limits::infinity()), + prev(NULL), + next(NULL) {} + + double nominalWeight() + { + if (prev == NULL || next == NULL || coord.cmd != SEG_LINETO) { + return std::numeric_limits::infinity(); + } + vertex2d const& A = prev->coord; + vertex2d const& B = next->coord; + vertex2d const& C = coord; + return std::abs((double)((A.x - C.x) * (B.y - A.y) - (A.x - B.x) * (C.y - A.y))) / 2.0; + } + + struct ascending_sort + { + bool operator() (const weighted_vertex *a, const weighted_vertex *b) + { + return b->weight > a->weight; + } + }; +}; + +struct sleeve +{ + vertex2d v[5]; + + sleeve(vertex2d const& v0, vertex2d const& v1, double offset) + { + double a = atan2((v1.y - v0.y), (v1.x - v0.x)); + double dx = offset * cos(a); + double dy = offset * sin(a); + v[0].x = v0.x + dy; + v[0].y = v0.y - dx; + v[1].x = v0.x - dy; + v[1].y = v0.y + dx; + v[2].x = v1.x - dy; + v[2].y = v1.y + dx; + v[3].x = v1.x + dy; + v[3].y = v1.y - dx; + v[4].x = v0.x + dy; + v[4].y = v0.y - dx; + } + + bool inside(vertex2d const& q) + { + bool inside=false; + + for (unsigned i=0;i<4;++i) + { + if ((((v[i+1].y <= q.y) && (q.y < v[i].y)) || + ((v[i].y <= q.y) && (q.y < v[i+1].y))) && + (q.x < (v[i].x - v[i+1].x) * (q.y - v[i+1].y)/ (v[i].y - v[i+1].y) + v[i+1].x)) + inside=!inside; + } + return inside; + } + void print() + { + std::cerr << "LINESTRING(" + << v[0].x << " " << -v[0].y << "," + << v[1].x << " " << -v[1].y << "," + << v[2].x << " " << -v[2].y << "," + << v[3].x << " " << -v[3].y << "," + << v[0].x << " " << -v[0].y << ")" << std::endl; + + } +}; + +template +struct MAPNIK_DECL simplify_converter +{ +public: + simplify_converter(Geometry& geom) + : geom_(geom), + tolerance_(0.0), + status_(initial), + algorithm_(radial_distance), + pos_(0) + {} + + enum status + { + initial, + process, + closing, + end, + cache + }; + + simplify_algorithm_e get_simplify_algorithm() + { + return algorithm_; + } + + void set_simplify_algorithm(simplify_algorithm_e value) + { + if (algorithm_ != value) + { + algorithm_ = value; + reset(); + } + } + + double get_simplify_tolerance() + { + return tolerance_; + } + + void set_simplify_tolerance(double value) + { + if (tolerance_ != value) { + tolerance_ = value; + reset(); + } + } + + void reset() + { + geom_.rewind(0); + vertices_.clear(); + status_ = initial; + pos_ = 0; + } + + void rewind(unsigned int) const + { + pos_ = 0; + } + + unsigned vertex(double* x, double* y) + { + if (tolerance_ == 0.0) + return geom_.vertex(x, y); + + if (status_ == initial) + init_vertices(); + + return output_vertex(x, y); + } + +private: + unsigned output_vertex(double* x, double* y) + { + switch (algorithm_) + { + case visvalingam_whyatt: + return output_vertex_cached(x, y); + case radial_distance: + return output_vertex_distance(x, y); + case zhao_saalfeld: + return output_vertex_sleeve(x, y); + default: + throw std::runtime_error("simplification algorithm not yet implemented"); + } + + return SEG_END; + } + + unsigned output_vertex_cached(double* x, double* y) { + if (pos_ >= vertices_.size()) + return SEG_END; + + previous_vertex_ = vertices_[pos_]; + *x = previous_vertex_.x; + *y = previous_vertex_.y; + pos_++; + return previous_vertex_.cmd; + } + + unsigned output_vertex_distance(double* x, double* y) { + if (status_ == closing) { + status_ = end; + return SEG_CLOSE; + } + + vertex2d last(vertex2d::no_init); + vertex2d vtx(vertex2d::no_init); + while ((vtx.cmd = geom_.vertex(&vtx.x, &vtx.y)) != SEG_END) + { + if (vtx.cmd == SEG_LINETO) { + if (distance_to_previous(vtx) > tolerance_) { + // Only output a vertex if it's far enough away from the previous + break; + } else { + last = vtx; + // continue + } + } else if (vtx.cmd == SEG_CLOSE) { + if (last.cmd == vertex2d::no_init) { + // The previous vertex was already output in the previous call. + // We can now safely output SEG_CLOSE. + status_ = end; + } else { + // We eliminated the previous point because it was too close, but + // we have to output it now anyway, since this is the end of the + // vertex stream. Make sure that we output SEG_CLOSE in the next call. + vtx = last; + status_ = closing; + } + break; + } else if (vtx.cmd == SEG_MOVETO) { + break; + } else { + throw std::runtime_error("Unknown vertex command"); + } + } + + previous_vertex_ = vtx; + *x = vtx.x; + *y = vtx.y; + return vtx.cmd; + } + + template + bool fit_sleeve(Iterator itr,Iterator end, vertex2d const& v) + { + sleeve s(*itr,v,tolerance_); + ++itr; // skip first vertex + for (; itr!=end; ++itr) + { + if (!s.inside(*itr)) + { + return false; + } + } + return true; + } + + unsigned output_vertex_sleeve(double* x, double* y) + { + vertex2d vtx(vertex2d::no_init); + std::size_t min_size = 1; + while ((vtx.cmd = geom_.vertex(&vtx.x, &vtx.y)) != SEG_END) + { + //if ((std::fabs(vtx.x - previous_vertex_.x) < 0.5) && + // (std::fabs(vtx.y - previous_vertex_.y) < 0.5)) + // continue; + + if (status_ == cache && + vertices_.size() >= min_size) + status_ = process; + + previous_vertex_ = vtx; + + if (vtx.cmd == SEG_MOVETO) + { + if (sleeve_cont_.size() > 1) + { + vertices_.push_back(sleeve_cont_.back()); + sleeve_cont_.clear(); + } + vertices_.push_back(vtx); + sleeve_cont_.push_back(vtx); + if (status_ == process) break; + } + else if (vtx.cmd == SEG_LINETO) + { + if (sleeve_cont_.size() > 1 && !fit_sleeve(sleeve_cont_.begin(), sleeve_cont_.end(), vtx)) + { + vertex2d last = vtx; + vtx = sleeve_cont_.back(); + sleeve_cont_.clear(); + sleeve_cont_.push_back(vtx); + sleeve_cont_.push_back(last); + vertices_.push_back(vtx); + if (status_ == process) break; + } + else + { + sleeve_cont_.push_back(vtx); + } + } + else if (vtx.cmd == SEG_CLOSE) + { + if (sleeve_cont_.size() > 1) + { + vertices_.push_back(sleeve_cont_.back()); + sleeve_cont_.clear(); + } + vertices_.push_back(vtx); + if (status_ == process) break; + } + } + + if (status_ == cache) + { + if (vertices_.size() < min_size) + return SEG_END; + status_ = process; + } + + if (vtx.cmd == SEG_END) + { + if (sleeve_cont_.size() > 1) + { + vertices_.push_back(sleeve_cont_.back()); + } + sleeve_cont_.clear(); + vertices_.push_back(vtx); + } + + if (vertices_.size() > 0) + { + vertex2d v = vertices_.front(); + vertices_.pop_front(); + *x = v.x; + *y = v.y; + return v.cmd; + } + return SEG_END; + } + + double distance_to_previous(vertex2d const& vtx) { + double dx = previous_vertex_.x - vtx.x; + double dy = previous_vertex_.y - vtx.y; + return dx * dx + dy * dy; + } + + status init_vertices() + { + if (status_ != initial) // already initialized + return status_; + + reset(); + + switch (algorithm_) { + case visvalingam_whyatt: + return init_vertices_visvalingam_whyatt(); + case radial_distance: + // Use + vertices_.push_back(vertex2d(vertex2d::no_init)); + return status_ = process; + case zhao_saalfeld: + return status_ = cache; + default: + throw std::runtime_error("simplification algorithm not yet implemented"); + } + } + + status init_vertices_visvalingam_whyatt() + { + typedef std::set VertexSet; + typedef std::vector VertexList; + + std::vector v_list; + vertex2d vtx(vertex2d::no_init); + while ((vtx.cmd = geom_.vertex(&vtx.x, &vtx.y)) != SEG_END) + { + v_list.push_back(new weighted_vertex(vtx)); + } + + if (v_list.empty()) { + return status_ = process; + } + + // Connect the vertices in a linked list and insert them into the set. + VertexSet v; + for (VertexList::iterator i = v_list.begin(); i != v_list.end(); ++i) + { + (*i)->prev = i == v_list.begin() ? NULL : *(i - 1); + (*i)->next = i + 1 == v_list.end() ? NULL : *(i + 1); + (*i)->weight = (*i)->nominalWeight(); + v.insert(*i); + } + + // Use Visvalingam-Whyatt algorithm to calculate each point's weight. + while (v.size() > 0) + { + VertexSet::iterator lowest = v.begin(); + weighted_vertex *removed = *lowest; + if (removed->weight >= tolerance_) { + break; + } + + v.erase(lowest); + + // Connect adjacent vertices with each other + if (removed->prev) removed->prev->next = removed->next; + if (removed->next) removed->next->prev = removed->prev; + // Adjust weight and reinsert prev/next to move them to their correct position. + if (removed->prev) { + v.erase(removed->prev); + removed->prev->weight = std::max(removed->weight, removed->prev->nominalWeight()); + v.insert(removed->prev); + } + if (removed->next) { + v.erase(removed->next); + removed->next->weight = std::max(removed->weight, removed->next->nominalWeight()); + v.insert(removed->next); + } + } + + v.clear(); + + // Traverse the remaining list and insert them into the vertex cache. + for (VertexList::iterator i = v_list.begin(); i != v_list.end(); ++i) + { + if ((*i)->weight >= tolerance_) + { + vertices_.push_back((*i)->coord); + } + delete *i; + } + + // Initialization finished. + return status_ = process; + } + + Geometry& geom_; + double tolerance_; + status status_; + simplify_algorithm_e algorithm_; + std::deque vertices_; + std::deque sleeve_cont_; + vertex2d previous_vertex_; + mutable size_t pos_; +}; + + +} + +#endif // MAPNIK_SIMPLIFY_CONVERTER_HPP diff --git a/include/mapnik/symbolizer.hpp b/include/mapnik/symbolizer.hpp index 31ffb784c7..a4a051a271 100644 --- a/include/mapnik/symbolizer.hpp +++ b/include/mapnik/symbolizer.hpp @@ -28,6 +28,7 @@ #include #include #include +#include // boost #include @@ -55,12 +56,18 @@ class MAPNIK_DECL symbolizer_base std::string get_transform_string() const; void set_clip(bool clip); bool clip() const; + void set_simplify_algorithm(simplify_algorithm_e algorithm); + simplify_algorithm_e simplify_algorithm() const; + void set_simplify_tolerance(double simplify_tolerance); + double simplify_tolerance() const; void set_smooth(double smooth); double smooth() const; private: composite_mode_e comp_op_; transform_type affine_transform_; bool clip_; + simplify_algorithm_e simplify_algorithm_value_; + double simplify_tolerance_value_; double smooth_value_; }; diff --git a/include/mapnik/utils.hpp b/include/mapnik/utils.hpp index 93c379c75b..73e68e5518 100644 --- a/include/mapnik/utils.hpp +++ b/include/mapnik/utils.hpp @@ -140,6 +140,7 @@ template // mapnik -#include #include #include +#include // agg #include "agg_conv_clip_polygon.h" @@ -60,12 +60,14 @@ #include "agg_conv_dash.h" #include "agg_conv_transform.h" + namespace mapnik { struct transform_tag {}; struct clip_line_tag {}; struct clip_poly_tag {}; struct smooth_tag {}; +struct simplify_tag {}; struct stroke_tag {}; struct dash_tag {}; struct affine_transform_tag {}; @@ -98,6 +100,19 @@ struct converter_traits } }; +template +struct converter_traits +{ + typedef T geometry_type; + typedef simplify_converter conv_type; + + template + static void setup(geometry_type & geom, Args const& args) + { + geom.set_simplify_algorithm(boost::fusion::at_c<2>(args).simplify_algorithm()); + geom.set_simplify_tolerance(boost::fusion::at_c<2>(args).simplify_tolerance()); + } +}; template struct converter_traits diff --git a/plugins/input/sqlite/sqlite_datasource.cpp b/plugins/input/sqlite/sqlite_datasource.cpp index 1b769af530..f662efb2f9 100644 --- a/plugins/input/sqlite/sqlite_datasource.cpp +++ b/plugins/input/sqlite/sqlite_datasource.cpp @@ -524,7 +524,7 @@ boost::optional sqlite_datasource::get_geometry_ if (data) { boost::ptr_vector paths; - if (mapnik::geometry_utils::from_wkb(paths, data, size, mapnik::wkbAuto)) + if (mapnik::geometry_utils::from_wkb(paths, data, size, format_)) { mapnik::util::to_ds_type(paths,result); if (result) diff --git a/src/agg/process_line_pattern_symbolizer.cpp b/src/agg/process_line_pattern_symbolizer.cpp index e10b408b69..e0177527e7 100644 --- a/src/agg/process_line_pattern_symbolizer.cpp +++ b/src/agg/process_line_pattern_symbolizer.cpp @@ -109,13 +109,14 @@ void agg_renderer::process(line_pattern_symbolizer const& sym, clipping_extent.init(x0 - padding, y0 - padding, x1 + padding , y1 + padding); } - typedef boost::mpl::vector conv_types; + typedef boost::mpl::vector conv_types; vertex_converter, rasterizer_type, line_pattern_symbolizer, CoordTransform, proj_transform, agg::trans_affine, conv_types> converter(clipping_extent,ras,sym,t_,prj_trans,tr,scale_factor_); if (sym.clip()) converter.set(); //optional clip (default: true) converter.set(); //always transform + if (sym.simplify_tolerance() > 0.0) converter.set(); // optional simplify converter if (sym.smooth() > 0.0) converter.set(); // optional smooth converter BOOST_FOREACH(geometry_type & geom, feature.paths()) diff --git a/src/agg/process_line_symbolizer.cpp b/src/agg/process_line_symbolizer.cpp index d6b05399d0..bf4fe88f95 100644 --- a/src/agg/process_line_symbolizer.cpp +++ b/src/agg/process_line_symbolizer.cpp @@ -76,7 +76,7 @@ void agg_renderer::process(line_symbolizer const& sym, typedef agg::renderer_base renderer_base; typedef boost::mpl::vector conv_types; + simplify_tag, smooth_tag, dash_tag, stroke_tag> conv_types; pixfmt_comp_type pixf(buf); pixf.comp_op(static_cast(sym.comp_op())); @@ -121,6 +121,7 @@ void agg_renderer::process(line_symbolizer const& sym, converter.set(); // always transform if (fabs(sym.offset()) > 0.0) converter.set(); // parallel offset converter.set(); // optional affine transform + if (sym.simplify_tolerance() > 0.0) converter.set(); // optional simplify converter if (sym.smooth() > 0.0) converter.set(); // optional smooth converter BOOST_FOREACH( geometry_type & geom, feature.paths()) @@ -141,6 +142,7 @@ void agg_renderer::process(line_symbolizer const& sym, converter.set(); // always transform if (fabs(sym.offset()) > 0.0) converter.set(); // parallel offset converter.set(); // optional affine transform + if (sym.simplify_tolerance() > 0.0) converter.set(); // optional simplify converter if (sym.smooth() > 0.0) converter.set(); // optional smooth converter if (stroke_.has_dash()) converter.set(); converter.set(); //always stroke diff --git a/src/agg/process_polygon_pattern_symbolizer.cpp b/src/agg/process_polygon_pattern_symbolizer.cpp index 5a3a64a8f4..6fbc8fbb48 100644 --- a/src/agg/process_polygon_pattern_symbolizer.cpp +++ b/src/agg/process_polygon_pattern_symbolizer.cpp @@ -139,13 +139,14 @@ void agg_renderer::process(polygon_pattern_symbolizer const& sym, agg::trans_affine tr; evaluate_transform(tr, feature, sym.get_transform()); - typedef boost::mpl::vector conv_types; + typedef boost::mpl::vector conv_types; vertex_converter, rasterizer, polygon_pattern_symbolizer, CoordTransform, proj_transform, agg::trans_affine, conv_types> converter(query_extent_,*ras_ptr,sym,t_,prj_trans,tr,scale_factor_); if (prj_trans.equal() && sym.clip()) converter.set(); //optional clip (default: true) converter.set(); //always transform + if (sym.simplify_tolerance() > 0.0) converter.set(); // optional simplify converter if (sym.smooth() > 0.0) converter.set(); // optional smooth converter BOOST_FOREACH( geometry_type & geom, feature.paths()) diff --git a/src/agg/process_polygon_symbolizer.cpp b/src/agg/process_polygon_symbolizer.cpp index c2afcc115e..a0083df13d 100644 --- a/src/agg/process_polygon_symbolizer.cpp +++ b/src/agg/process_polygon_symbolizer.cpp @@ -51,7 +51,7 @@ void agg_renderer::process(polygon_symbolizer const& sym, agg::trans_affine tr; evaluate_transform(tr, feature, sym.get_transform()); - typedef boost::mpl::vector conv_types; + typedef boost::mpl::vector conv_types; vertex_converter, rasterizer, polygon_symbolizer, CoordTransform, proj_transform, agg::trans_affine, conv_types> converter(query_extent_,*ras_ptr,sym,t_,prj_trans,tr,scale_factor_); @@ -59,6 +59,7 @@ void agg_renderer::process(polygon_symbolizer const& sym, if (prj_trans.equal() && sym.clip()) converter.set(); //optional clip (default: true) converter.set(); //always transform converter.set(); + if (sym.simplify_tolerance() > 0.0) converter.set(); // optional simplify converter if (sym.smooth() > 0.0) converter.set(); // optional smooth converter BOOST_FOREACH( geometry_type & geom, feature.paths()) diff --git a/src/build.py b/src/build.py index 40b9303524..ac7ecc1c0f 100644 --- a/src/build.py +++ b/src/build.py @@ -151,6 +151,7 @@ def ldconfig(*args,**kwargs): proj_transform.cpp distance.cpp scale_denominator.cpp + simplify.cpp memory_datasource.cpp stroke.cpp symbolizer.cpp diff --git a/src/cairo_renderer.cpp b/src/cairo_renderer.cpp index 614a2583be..0de3aea182 100644 --- a/src/cairo_renderer.cpp +++ b/src/cairo_renderer.cpp @@ -865,7 +865,7 @@ void cairo_renderer_base::process(polygon_symbolizer const& sym, agg::trans_affine tr; evaluate_transform(tr, feature, sym.get_transform()); - typedef boost::mpl::vector conv_types; + typedef boost::mpl::vector conv_types; vertex_converter, cairo_context, polygon_symbolizer, CoordTransform, proj_transform, agg::trans_affine, conv_types> converter(query_extent_,context,sym,t_,prj_trans,tr,1.0); @@ -873,6 +873,7 @@ void cairo_renderer_base::process(polygon_symbolizer const& sym, if (prj_trans.equal() && sym.clip()) converter.set(); //optional clip (default: true) converter.set(); //always transform converter.set(); + if (sym.simplify_tolerance() > 0.0) converter.set(); // optional simplify converter if (sym.smooth() > 0.0) converter.set(); // optional smooth converter BOOST_FOREACH( geometry_type & geom, feature.paths()) @@ -989,7 +990,7 @@ void cairo_renderer_base::process(line_symbolizer const& sym, { typedef boost::mpl::vector conv_types; + simplify_tag, smooth_tag, dash_tag, stroke_tag> conv_types; cairo_context context(context_); mapnik::stroke const& stroke_ = sym.get_stroke(); context.set_operator(sym.comp_op()); @@ -1030,6 +1031,7 @@ void cairo_renderer_base::process(line_symbolizer const& sym, converter.set(); // always transform if (fabs(sym.offset()) > 0.0) converter.set(); // parallel offset converter.set(); // optional affine transform + if (sym.simplify_tolerance() > 0.0) converter.set(); // optional simplify converter if (sym.smooth() > 0.0) converter.set(); // optional smooth converter BOOST_FOREACH( geometry_type & geom, feature.paths()) @@ -1386,7 +1388,7 @@ void cairo_renderer_base::process(polygon_pattern_symbolizer const& sym, agg::trans_affine tr; evaluate_transform(tr, feature, sym.get_transform()); - typedef boost::mpl::vector conv_types; + typedef boost::mpl::vector conv_types; vertex_converter, cairo_context, polygon_pattern_symbolizer, CoordTransform, proj_transform, agg::trans_affine, conv_types> converter(query_extent_,context,sym,t_,prj_trans,tr, scale_factor_); @@ -1394,6 +1396,7 @@ void cairo_renderer_base::process(polygon_pattern_symbolizer const& sym, if (prj_trans.equal() && sym.clip()) converter.set(); //optional clip (default: true) converter.set(); //always transform converter.set(); + if (sym.simplify_tolerance() > 0.0) converter.set(); // optional simplify converter if (sym.smooth() > 0.0) converter.set(); // optional smooth converter BOOST_FOREACH( geometry_type & geom, feature.paths()) diff --git a/src/grid/process_line_symbolizer.cpp b/src/grid/process_line_symbolizer.cpp index f1ca1d590b..ec2e3ef6f6 100644 --- a/src/grid/process_line_symbolizer.cpp +++ b/src/grid/process_line_symbolizer.cpp @@ -54,7 +54,7 @@ void grid_renderer::process(line_symbolizer const& sym, typedef agg::renderer_scanline_bin_solid renderer_type; typedef boost::mpl::vector conv_types; + simplify_tag, smooth_tag, dash_tag, stroke_tag> conv_types; agg::scanline_bin sl; grid_rendering_buffer buf(pixmap_.raw_data(), width_, height_, width_); @@ -93,6 +93,7 @@ void grid_renderer::process(line_symbolizer const& sym, converter.set(); // always transform if (fabs(sym.offset()) > 0.0) converter.set(); // parallel offset converter.set(); // optional affine transform + if (sym.simplify_tolerance() > 0.0) converter.set(); // optional simplify converter if (sym.smooth() > 0.0) converter.set(); // optional smooth converter if (stroke_.has_dash()) converter.set(); converter.set(); //always stroke diff --git a/src/grid/process_polygon_symbolizer.cpp b/src/grid/process_polygon_symbolizer.cpp index 59d128b7e1..281c300d6f 100644 --- a/src/grid/process_polygon_symbolizer.cpp +++ b/src/grid/process_polygon_symbolizer.cpp @@ -53,7 +53,7 @@ void grid_renderer::process(polygon_symbolizer const& sym, agg::trans_affine tr; evaluate_transform(tr, feature, sym.get_transform()); - typedef boost::mpl::vector conv_types; + typedef boost::mpl::vector conv_types; vertex_converter, grid_rasterizer, polygon_symbolizer, CoordTransform, proj_transform, agg::trans_affine, conv_types> converter(query_extent_,*ras_ptr,sym,t_,prj_trans,tr,scale_factor_); @@ -61,6 +61,7 @@ void grid_renderer::process(polygon_symbolizer const& sym, if (prj_trans.equal() && sym.clip()) converter.set(); //optional clip (default: true) converter.set(); //always transform converter.set(); + if (sym.simplify_tolerance() > 0.0) converter.set(); // optional simplify converter if (sym.smooth() > 0.0) converter.set(); // optional smooth converter diff --git a/src/load_map.cpp b/src/load_map.cpp index 018c077840..344793ef97 100644 --- a/src/load_map.cpp +++ b/src/load_map.cpp @@ -858,6 +858,25 @@ void map_parser::parse_symbolizer_base(symbolizer_base &sym, xml_node const &pt) optional clip = pt.get_opt_attr("clip"); if (clip) sym.set_clip(*clip); + // simplify algorithm + optional simplify_algorithm_name = pt.get_opt_attr("simplify-algorithm"); + if (simplify_algorithm_name) + { + optional simplify_algorithm = simplify_algorithm_from_string(*simplify_algorithm_name); + if (simplify_algorithm) + { + sym.set_simplify_algorithm(*simplify_algorithm); + } + else + { + throw config_error("failed to parse simplify-algorithm: '" + *simplify_algorithm_name + "'"); + } + } + + // simplify value + optional simplify_tolerance = pt.get_opt_attr("simplify-tolerance"); + if (simplify_tolerance) sym.set_simplify_tolerance(*simplify_tolerance); + // smooth value optional smooth = pt.get_opt_attr("smooth"); if (smooth) sym.set_smooth(*smooth); diff --git a/src/save_map.cpp b/src/save_map.cpp index dfc163a75e..7b90ce29b7 100644 --- a/src/save_map.cpp +++ b/src/save_map.cpp @@ -350,6 +350,14 @@ class serialize_symbolizer : public boost::static_visitor<> { set_attr( node, "clip", sym.clip() ); } + if (sym.simplify_algorithm() != dfl.simplify_algorithm() || explicit_defaults_) + { + set_attr( node, "simplify-algorithm", *simplify_algorithm_to_string(sym.simplify_algorithm()) ); + } + if (sym.simplify_tolerance() != dfl.simplify_tolerance() || explicit_defaults_) + { + set_attr( node, "simplify-tolerance", sym.simplify_tolerance() ); + } if (sym.smooth() != dfl.smooth() || explicit_defaults_) { set_attr( node, "smooth", sym.smooth() ); diff --git a/src/simplify.cpp b/src/simplify.cpp new file mode 100644 index 0000000000..f8e7e236c1 --- /dev/null +++ b/src/simplify.cpp @@ -0,0 +1,41 @@ +// mapnik +#include + +// boost +#include +#include + +namespace mapnik +{ + +typedef boost::bimap simplify_algorithm_lookup_type; +static const simplify_algorithm_lookup_type simplify_lookup = boost::assign::list_of + (radial_distance,"radial-distance") + (douglas_peucker,"douglas-peucker") + (visvalingam_whyatt,"visvalingam-whyatt") + (zhao_saalfeld,"zhao-saalfeld") + ; + +boost::optional simplify_algorithm_from_string(std::string const& name) +{ + boost::optional algo; + simplify_algorithm_lookup_type::right_const_iterator right_iter = simplify_lookup.right.find(name); + if (right_iter != simplify_lookup.right.end()) + { + algo.reset(right_iter->second); + } + return algo; +} + +boost::optional simplify_algorithm_to_string(simplify_algorithm_e value) +{ + boost::optional algo; + simplify_algorithm_lookup_type::left_const_iterator left_iter = simplify_lookup.left.find(value); + if (left_iter != simplify_lookup.left.end()) + { + algo.reset(left_iter->second); + } + return algo; +} + +} diff --git a/src/symbolizer.cpp b/src/symbolizer.cpp index 298c29abb6..2a876b4e88 100644 --- a/src/symbolizer.cpp +++ b/src/symbolizer.cpp @@ -44,6 +44,8 @@ void evaluate_transform(agg::trans_affine& tr, Feature const& feature, symbolizer_base::symbolizer_base() : comp_op_(src_over), clip_(true), + simplify_algorithm_value_(radial_distance), + simplify_tolerance_value_(0.0), smooth_value_(0.0) { } @@ -53,6 +55,8 @@ symbolizer_base::symbolizer_base(symbolizer_base const& other) : comp_op_(other.comp_op_), affine_transform_(other.affine_transform_), clip_(other.clip_), + simplify_algorithm_value_(other.simplify_algorithm_value_), + simplify_tolerance_value_(other.simplify_tolerance_value_), smooth_value_(other.smooth_value_) {} void symbolizer_base::set_comp_op(composite_mode_e comp_op) @@ -100,6 +104,26 @@ bool symbolizer_base::clip() const return clip_; } +void symbolizer_base::set_simplify_algorithm(simplify_algorithm_e algo) +{ + simplify_algorithm_value_ = algo; +} + +simplify_algorithm_e symbolizer_base::simplify_algorithm() const +{ + return simplify_algorithm_value_; +} + +void symbolizer_base::set_simplify_tolerance(double simplify_tolerance) +{ + simplify_tolerance_value_ = simplify_tolerance; +} + +double symbolizer_base::simplify_tolerance() const +{ + return simplify_tolerance_value_; +} + void symbolizer_base::set_smooth(double smooth) { smooth_value_ = smooth;