use a set to make visvalingam a lot faster
This commit is contained in:
parent
22150f908f
commit
b243f03c90
5 changed files with 102 additions and 49 deletions
|
@ -63,10 +63,6 @@ void export_line_symbolizer()
|
||||||
&line_symbolizer::smooth,
|
&line_symbolizer::smooth,
|
||||||
&line_symbolizer::set_smooth,
|
&line_symbolizer::set_smooth,
|
||||||
"smooth value (0..1.0)")
|
"smooth value (0..1.0)")
|
||||||
.add_property("simplify_algorithm",
|
|
||||||
&line_symbolizer::simplify_algorithm,
|
|
||||||
&line_symbolizer::set_simplify_algorithm,
|
|
||||||
"simplfication algorithm")
|
|
||||||
.add_property("simplify_tolerance",
|
.add_property("simplify_tolerance",
|
||||||
&line_symbolizer::simplify_tolerance,
|
&line_symbolizer::simplify_tolerance,
|
||||||
&line_symbolizer::set_simplify_tolerance,
|
&line_symbolizer::set_simplify_tolerance,
|
||||||
|
|
|
@ -88,10 +88,6 @@ void export_polygon_symbolizer()
|
||||||
&polygon_symbolizer::smooth,
|
&polygon_symbolizer::smooth,
|
||||||
&polygon_symbolizer::set_smooth,
|
&polygon_symbolizer::set_smooth,
|
||||||
"smooth value (0..1.0)")
|
"smooth value (0..1.0)")
|
||||||
.add_property("simplify_algorithm",
|
|
||||||
&polygon_symbolizer::simplify_algorithm,
|
|
||||||
&polygon_symbolizer::set_simplify_algorithm,
|
|
||||||
"simplfication algorithm")
|
|
||||||
.add_property("simplify_tolerance",
|
.add_property("simplify_tolerance",
|
||||||
&polygon_symbolizer::simplify_tolerance,
|
&polygon_symbolizer::simplify_tolerance,
|
||||||
&polygon_symbolizer::set_simplify_tolerance,
|
&polygon_symbolizer::set_simplify_tolerance,
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
// STL
|
// STL
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
// Boost
|
// Boost
|
||||||
#include <boost/optional.hpp>
|
#include <boost/optional.hpp>
|
||||||
|
@ -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;
|
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) {
|
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,
|
initial,
|
||||||
process,
|
process,
|
||||||
|
closing,
|
||||||
end
|
end
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -120,6 +122,8 @@ private:
|
||||||
switch (algorithm_) {
|
switch (algorithm_) {
|
||||||
case visvalingam_whyatt:
|
case visvalingam_whyatt:
|
||||||
return output_vertex_cached(x, y);
|
return output_vertex_cached(x, y);
|
||||||
|
case radial_distance:
|
||||||
|
return output_vertex_distance(x, y);
|
||||||
default:
|
default:
|
||||||
throw std::runtime_error("simplification algorithm not yet implemented");
|
throw std::runtime_error("simplification algorithm not yet implemented");
|
||||||
}
|
}
|
||||||
|
@ -131,13 +135,63 @@ private:
|
||||||
if (pos_ >= vertices_.size())
|
if (pos_ >= vertices_.size())
|
||||||
return SEG_END;
|
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;
|
*x = vtx.x;
|
||||||
*y = vtx.y;
|
*y = vtx.y;
|
||||||
pos_++;
|
|
||||||
return vtx.cmd;
|
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()
|
status init_vertices()
|
||||||
{
|
{
|
||||||
if (status_ != initial) // already initialized
|
if (status_ != initial) // already initialized
|
||||||
|
@ -148,70 +202,76 @@ private:
|
||||||
switch (algorithm_) {
|
switch (algorithm_) {
|
||||||
case visvalingam_whyatt:
|
case visvalingam_whyatt:
|
||||||
return init_vertices_visvalingam_whyatt();
|
return init_vertices_visvalingam_whyatt();
|
||||||
|
case radial_distance:
|
||||||
|
// Use
|
||||||
|
vertices_.push_back(vertex2d(vertex2d::no_init));
|
||||||
|
return status_ = process;
|
||||||
default:
|
default:
|
||||||
throw std::runtime_error("simplification algorithm not yet implemented");
|
throw std::runtime_error("simplification algorithm not yet implemented");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
status init_vertices_visvalingam_whyatt() {
|
status init_vertices_visvalingam_whyatt() {
|
||||||
typedef std::vector<weighted_vertex *> WeightedVertices;
|
typedef std::set<weighted_vertex *, weighted_vertex::ascending_sort> VertexSet;
|
||||||
|
typedef std::vector<weighted_vertex *> VertexList;
|
||||||
|
|
||||||
WeightedVertices v;
|
std::vector<weighted_vertex *> v_list;
|
||||||
vertex2d vtx(vertex2d::no_init);
|
vertex2d vtx(vertex2d::no_init);
|
||||||
while ((vtx.cmd = geom_.vertex(&vtx.x, &vtx.y)) != SEG_END)
|
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;
|
return status_ = process;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connect the vertices in a linked list.
|
// Connect the vertices in a linked list and insert them into the set.
|
||||||
for (WeightedVertices::iterator end = v.end(), begin = v.begin(), i = v.begin(); i != end; i++)
|
VertexSet v;
|
||||||
|
for (VertexList::iterator i = v_list.begin(); i != v_list.end(); i++)
|
||||||
{
|
{
|
||||||
(*i)->prev = i == begin ? NULL : *(i - 1);
|
(*i)->prev = i == v_list.begin() ? NULL : *(i - 1);
|
||||||
(*i)->next = i + 1 == end ? NULL : *(i + 1);
|
(*i)->next = i + 1 == v_list.end() ? NULL : *(i + 1);
|
||||||
(*i)->weight = (*i)->nominalWeight();
|
(*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.
|
// 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();
|
VertexSet::iterator lowest = v.begin();
|
||||||
v.pop_back();
|
weighted_vertex *removed = *lowest;
|
||||||
|
if (removed->weight >= tolerance_) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
v.erase(lowest);
|
||||||
|
|
||||||
// Connect adjacent vertices with each other
|
// Connect adjacent vertices with each other
|
||||||
if (removed->prev) removed->prev->next = removed->next;
|
if (removed->prev) removed->prev->next = removed->next;
|
||||||
if (removed->next) removed->next->prev = removed->prev;
|
if (removed->next) removed->next->prev = removed->prev;
|
||||||
if (removed->prev) removed->prev->weight = std::max(removed->weight, removed->prev->nominalWeight());
|
// Adjust weight and reinsert prev/next to move them to their correct position.
|
||||||
if (removed->next) removed->next->weight = std::max(removed->weight, removed->next->nominalWeight());
|
if (removed->prev) {
|
||||||
|
v.erase(removed->prev);
|
||||||
if (front == removed) {
|
removed->prev->weight = std::max(removed->weight, removed->prev->nominalWeight());
|
||||||
front = removed->next;
|
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;
|
v.clear();
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Traverse the remaining list and insert them into the vertex cache.
|
// Traverse the remaining list and insert them into the vertex cache.
|
||||||
for (weighted_vertex *vtx = front; vtx;) {
|
for (VertexList::iterator i = v_list.begin(); i != v_list.end(); i++)
|
||||||
vertices_.push_back(vtx->coord);
|
{
|
||||||
weighted_vertex *removed = vtx;
|
if ((*i)->weight >= tolerance_) {
|
||||||
vtx = vtx->next;
|
vertices_.push_back((*i)->coord);
|
||||||
delete removed;
|
}
|
||||||
|
delete *i;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialization finished.
|
// Initialization finished.
|
||||||
|
@ -224,7 +284,7 @@ private:
|
||||||
simplify_algorithm_e algorithm_;
|
simplify_algorithm_e algorithm_;
|
||||||
size_t pos_;
|
size_t pos_;
|
||||||
std::vector<vertex2d> vertices_;
|
std::vector<vertex2d> vertices_;
|
||||||
|
vertex2d previous_vertex_;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -107,6 +107,7 @@ struct converter_traits<T,mapnik::simplify_tag>
|
||||||
template <typename Args>
|
template <typename Args>
|
||||||
static void setup(geometry_type & geom, Args const& args)
|
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());
|
geom.set_simplify_tolerance(boost::fusion::at_c<2>(args).simplify_tolerance());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -883,7 +883,7 @@ void map_parser::parse_symbolizer_base(symbolizer_base &sym, xml_node const &pt)
|
||||||
if (clip) sym.set_clip(*clip);
|
if (clip) sym.set_clip(*clip);
|
||||||
|
|
||||||
// simplify algorithm
|
// simplify algorithm
|
||||||
optional<std::string> simplify_algorithm_name = pt.get_opt_attr<std::string>("simlify-op");
|
optional<std::string> simplify_algorithm_name = pt.get_opt_attr<std::string>("simplify-algorithm");
|
||||||
if (simplify_algorithm_name)
|
if (simplify_algorithm_name)
|
||||||
{
|
{
|
||||||
optional<simplify_algorithm_e> simplify_algorithm = simplify_algorithm_from_string(*simplify_algorithm_name);
|
optional<simplify_algorithm_e> simplify_algorithm = simplify_algorithm_from_string(*simplify_algorithm_name);
|
||||||
|
|
Loading…
Reference in a new issue