diff --git a/bindings/python/mapnik_line_symbolizer.cpp b/bindings/python/mapnik_line_symbolizer.cpp index 2931c41ac..1c859e62c 100644 --- a/bindings/python/mapnik_line_symbolizer.cpp +++ b/bindings/python/mapnik_line_symbolizer.cpp @@ -63,10 +63,6 @@ void export_line_symbolizer() &line_symbolizer::smooth, &line_symbolizer::set_smooth, "smooth value (0..1.0)") - .add_property("simplify_algorithm", - &line_symbolizer::simplify_algorithm, - &line_symbolizer::set_simplify_algorithm, - "simplfication algorithm") .add_property("simplify_tolerance", &line_symbolizer::simplify_tolerance, &line_symbolizer::set_simplify_tolerance, diff --git a/bindings/python/mapnik_polygon_symbolizer.cpp b/bindings/python/mapnik_polygon_symbolizer.cpp index 1a0442d24..9933dbde3 100644 --- a/bindings/python/mapnik_polygon_symbolizer.cpp +++ b/bindings/python/mapnik_polygon_symbolizer.cpp @@ -88,10 +88,6 @@ void export_polygon_symbolizer() &polygon_symbolizer::smooth, &polygon_symbolizer::set_smooth, "smooth value (0..1.0)") - .add_property("simplify_algorithm", - &polygon_symbolizer::simplify_algorithm, - &polygon_symbolizer::set_simplify_algorithm, - "simplfication algorithm") .add_property("simplify_tolerance", &polygon_symbolizer::simplify_tolerance, &polygon_symbolizer::set_simplify_tolerance, diff --git a/include/mapnik/simplify_converter.hpp b/include/mapnik/simplify_converter.hpp index b50a21194..1287b611f 100644 --- a/include/mapnik/simplify_converter.hpp +++ b/include/mapnik/simplify_converter.hpp @@ -8,6 +8,7 @@ // STL #include +#include // Boost #include @@ -37,9 +38,9 @@ struct weighted_vertex : private boost::noncopyable { return std::abs((double)((A.x - C.x) * (B.y - A.y) - (A.x - B.x) * (C.y - A.y))) / 2.0; } - struct descending_sort { + struct ascending_sort { bool operator() (const weighted_vertex *a, const weighted_vertex *b) { - return a->weight > b->weight; + return b->weight > a->weight; } }; }; @@ -60,6 +61,7 @@ public: { initial, process, + closing, end }; @@ -120,6 +122,8 @@ private: switch (algorithm_) { case visvalingam_whyatt: return output_vertex_cached(x, y); + case radial_distance: + return output_vertex_distance(x, y); default: throw std::runtime_error("simplification algorithm not yet implemented"); } @@ -131,13 +135,63 @@ private: if (pos_ >= vertices_.size()) return SEG_END; - vertex2d const& vtx = vertices_[pos_]; + 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; - pos_++; return vtx.cmd; } + 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 @@ -148,70 +202,76 @@ private: switch (algorithm_) { case visvalingam_whyatt: return init_vertices_visvalingam_whyatt(); + case radial_distance: + // Use + vertices_.push_back(vertex2d(vertex2d::no_init)); + return status_ = process; default: throw std::runtime_error("simplification algorithm not yet implemented"); } } status init_vertices_visvalingam_whyatt() { - typedef std::vector WeightedVertices; + typedef std::set VertexSet; + typedef std::vector VertexList; - WeightedVertices v; + std::vector v_list; vertex2d vtx(vertex2d::no_init); while ((vtx.cmd = geom_.vertex(&vtx.x, &vtx.y)) != SEG_END) { - v.push_back(new weighted_vertex(vtx)); + v_list.push_back(new weighted_vertex(vtx)); } - if (!v.size()) { + if (!v_list.size()) { return status_ = process; } - // Connect the vertices in a linked list. - for (WeightedVertices::iterator end = v.end(), begin = v.begin(), i = v.begin(); i != end; i++) + // 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 == begin ? NULL : *(i - 1); - (*i)->next = i + 1 == end ? NULL : *(i + 1); + (*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); } - weighted_vertex *front = v.front(); - - typename weighted_vertex::descending_sort descending; - - std::sort(v.begin(), v.end(), descending); // Use Visvalingam-Whyatt algorithm to calculate each point's weight. - while (v.size() > 0 && v.back()->weight < tolerance_) + while (v.size() > 0) { - weighted_vertex *removed = v.back(); - v.pop_back(); + 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; - if (removed->prev) removed->prev->weight = std::max(removed->weight, removed->prev->nominalWeight()); - if (removed->next) removed->next->weight = std::max(removed->weight, removed->next->nominalWeight()); - - if (front == removed) { - front = removed->next; + // 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); } - - delete removed; - - // TODO: find a way so that we can efficiently resort the vector. - // We only changed remove->prev and removed->next, so we should only - // have to deal with them. E.g. use binary search to find prev/next - // using the old value, determine new position using binary search - // and if different, move it. - std::sort(v.begin(), v.end(), descending); } + v.clear(); + // Traverse the remaining list and insert them into the vertex cache. - for (weighted_vertex *vtx = front; vtx;) { - vertices_.push_back(vtx->coord); - weighted_vertex *removed = vtx; - vtx = vtx->next; - delete removed; + 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. @@ -224,7 +284,7 @@ private: simplify_algorithm_e algorithm_; size_t pos_; std::vector vertices_; - + vertex2d previous_vertex_; }; } diff --git a/include/mapnik/vertex_converters.hpp b/include/mapnik/vertex_converters.hpp index bcb361f2f..cfa51bbc4 100644 --- a/include/mapnik/vertex_converters.hpp +++ b/include/mapnik/vertex_converters.hpp @@ -107,6 +107,7 @@ struct converter_traits 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()); } }; diff --git a/src/load_map.cpp b/src/load_map.cpp index 5b4636d79..c012d58ea 100644 --- a/src/load_map.cpp +++ b/src/load_map.cpp @@ -883,7 +883,7 @@ void map_parser::parse_symbolizer_base(symbolizer_base &sym, xml_node const &pt) if (clip) sym.set_clip(*clip); // simplify algorithm - optional simplify_algorithm_name = pt.get_opt_attr("simlify-op"); + optional simplify_algorithm_name = pt.get_opt_attr("simplify-algorithm"); if (simplify_algorithm_name) { optional simplify_algorithm = simplify_algorithm_from_string(*simplify_algorithm_name);