From be464f0c5ba40169181e21b62b6bca1c7bdd6522 Mon Sep 17 00:00:00 2001 From: Hermann Kraus Date: Mon, 13 Feb 2012 20:20:09 +0100 Subject: [PATCH] Move code for makers placement from hpp to cpp. --- include/mapnik/markers_placement.hpp | 195 +------------------------ src/build.py | 1 + src/markers_placement.cpp | 208 +++++++++++++++++++++++++++ 3 files changed, 210 insertions(+), 194 deletions(-) create mode 100644 src/markers_placement.cpp diff --git a/include/mapnik/markers_placement.hpp b/include/mapnik/markers_placement.hpp index a3eabb6d3..d5c385c12 100644 --- a/include/mapnik/markers_placement.hpp +++ b/include/mapnik/markers_placement.hpp @@ -23,18 +23,12 @@ #ifndef MAPNIK_MARKERS_PLACEMENT_HPP #define MAPNIK_MARKERS_PLACEMENT_HPP -// agg -#include "agg_basics.h" - // mapnik #include // boost #include -// stl -#include - namespace mapnik { template @@ -53,7 +47,7 @@ private: double last_x, last_y; double next_x, next_y; /** If a marker could not be placed at the exact point where it should - * go the next markers distance will be a bit lower. */ + * go the next marker's distance will be a bit lower. */ double error_; double max_error_; unsigned marker_nr_; @@ -62,193 +56,6 @@ private: double find_optimal_spacing(double s); }; -/** Constructor for markers_placement object. - * \param locator Path along which markers are placed (type: vertex source) - * \param size Size of the marker - * \param detector Collision detection - * \param spacing Distance between markers. If the value is negative it is - * converted to a positive value with similar magnitude, but - * choosen to optimize marker placement. 0 = no markers - */ -template markers_placement::markers_placement( - Locator &locator, box2d size, Detector &detector, double spacing, double max_error, bool allow_overlap) - : locator_(locator), size_(size), detector_(detector), max_error_(max_error), allow_overlap_(allow_overlap) -{ - if (spacing >= 0) - { - spacing_ = spacing; - } else if (spacing < 0) - { - spacing_ = find_optimal_spacing(-spacing); - } - rewind(); -} - -/** Autmatically chooses spacing. */ -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; -} - -/** Start again at first marker. - * \note Returning the same list of markers only works when they were NOT added - * to the detector. - */ -template void markers_placement::rewind() -{ - locator_.rewind(0); - //Get first point - done_ = agg::is_stop(locator_.vertex(&next_x, &next_y)) || spacing_ < size_.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 - * \param y Return value for x position - * \param angle Return value for rotation angle - * \param add_to_detector Add selected position to detector - * \return True if a place is found, false if none is found. - */ -template bool markers_placement::get_point( - double *x, double *y, double *angle, bool add_to_detector) -{ - if (done_) return false; - - unsigned cmd; - double spacing_left; - if (marker_nr_++ == 0) - { - spacing_left = spacing_ / 2; - } else - { - spacing_left = spacing_; - } - - spacing_left -= error_; - error_ = 0; - - while (true) - { - //Loop exits when a position is found or when no more segments are available - if (spacing_left < size_.width()/2) - { - //Do not place markers to close to the beginning of a segment - error_ += size_.width()/2 - spacing_left; - spacing_left = size_.width()/2; - } - if (abs(error_) > max_error_ * spacing_) - { - spacing_left += spacing_ - error_; - error_ = 0; - } - double dx = next_x - last_x; - double dy = next_y - last_y; - double d = std::sqrt(dx * dx + dy * dy); - if (d <= spacing_left) - { - //Segment is to short to place marker. Find next segment - spacing_left -= d; - 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 - } - //Check if marker really fits in this segment - if (d < size_.width()) - { - //Segment to short => Skip this segment - error_ += d + size_.width()/2 - spacing_left; - spacing_left = d + size_.width()/2; - continue; - } else if (d - spacing_left < size_.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) - { - error_ += d - size_.width()/2 - spacing_left; - spacing_left = d - size_.width()/2; - } else - { - //Skip this segment - error_ += d + size_.width()/2 - spacing_left; - spacing_left = d + size_.width()/2; - } - continue; //Force checking of max_error constraint - } - *angle = atan2(dy, dx); - *x = last_x + dx * (spacing_left / d); - *y = last_y + dy * (spacing_left / d); - - box2d box = perform_transform(*angle, *x, *y); - if (!allow_overlap_ && !detector_.has_placement(box)) - { - //10.0 is choosen arbitrarily - error_ += spacing_ * max_error_ / 10.0; - spacing_left += spacing_ * max_error_ / 10.0; - continue; - } - if (add_to_detector) detector_.insert(box); - last_x = *x; - last_y = *y; - return true; - } -} - -/** Rotates the size_ box and translates the position. */ -template box2d markers_placement::perform_transform(double angle, double dx, double dy) -{ - double c = cos(angle), s = sin(angle); - double x1 = size_.minx(); - double x2 = size_.maxx(); - double y1 = size_.miny(); - double y2 = size_.maxy(); - - double x1_ = dx + x1 * c - y1 * s; - double y1_ = dy + x1 * s + y1 * c; - double x2_ = dx + x2 * c - y2 * s; - double y2_ = dy + x2 * s + y2 * c; - - return box2d(x1_, y1_, x2_, y2_); -} - } #endif // MAPNIK_MARKERS_PLACEMENT_HPP diff --git a/src/build.py b/src/build.py index ebaed4252..5dc27fd65 100644 --- a/src/build.py +++ b/src/build.py @@ -164,6 +164,7 @@ source = Split( svg_transform_parser.cpp warp.cpp json/feature_collection_parser.cpp + markers_placement.cpp """ ) diff --git a/src/markers_placement.cpp b/src/markers_placement.cpp new file mode 100644 index 000000000..f9667ed40 --- /dev/null +++ b/src/markers_placement.cpp @@ -0,0 +1,208 @@ +// mapnik +#include +#include +#include +#include + +// agg +#include "agg_basics.h" + +// stl +#include + +namespace mapnik +{ + + +/** Constructor for markers_placement object. + * \param locator Path along which markers are placed (type: vertex source) + * \param size Size of the marker + * \param detector Collision detection + * \param spacing Distance between markers. If the value is negative it is + * converted to a positive value with similar magnitude, but + * choosen to optimize marker placement. 0 = no markers + */ +template markers_placement::markers_placement( + Locator &locator, box2d size, Detector &detector, double spacing, double max_error, bool allow_overlap) + : locator_(locator), size_(size), detector_(detector), max_error_(max_error), allow_overlap_(allow_overlap) +{ + if (spacing >= 0) + { + spacing_ = spacing; + } else if (spacing < 0) + { + spacing_ = find_optimal_spacing(-spacing); + } + rewind(); +} + +/** Automatically chooses spacing. */ +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; +} + +/** Start again at first marker. + * \note Returning the same list of markers only works when they were NOT added + * to the detector. + */ +template void markers_placement::rewind() +{ + locator_.rewind(0); + //Get first point + done_ = agg::is_stop(locator_.vertex(&next_x, &next_y)) || spacing_ < size_.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 + * \param y Return value for x position + * \param angle Return value for rotation angle + * \param add_to_detector Add selected position to detector + * \return True if a place is found, false if none is found. + */ +template bool markers_placement::get_point( + double *x, double *y, double *angle, bool add_to_detector) +{ + if (done_) return false; + + unsigned cmd; + double spacing_left; + if (marker_nr_++ == 0) + { + //First marker + spacing_left = spacing_ / 2; + } else + { + spacing_left = spacing_; + } + + spacing_left -= error_; + error_ = 0; + + while (true) + { + //Loop exits when a position is found or when no more segments are available + if (spacing_left < size_.width()/2) + { + //Do not place markers to close to the beginning of a segment + error_ += size_.width()/2 - spacing_left; + spacing_left = size_.width()/2; + } + if (abs(error_) > max_error_ * spacing_) + { + spacing_left += spacing_ - error_; + error_ = 0; + } + double dx = next_x - last_x; + double dy = next_y - last_y; + double d = std::sqrt(dx * dx + dy * dy); + if (d <= spacing_left) + { + //Segment is to short to place marker. Find next segment + spacing_left -= d; + 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 + } + //Check if marker really fits in this segment + if (d < size_.width()) + { + //Segment to short => Skip this segment + error_ += d + size_.width()/2 - spacing_left; + spacing_left = d + size_.width()/2; + continue; + } else if (d - spacing_left < size_.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) + { + error_ += d - size_.width()/2 - spacing_left; + spacing_left = d - size_.width()/2; + } else + { + //Skip this segment + error_ += d + size_.width()/2 - spacing_left; + spacing_left = d + size_.width()/2; + } + continue; //Force checking of max_error constraint + } + *angle = atan2(dy, dx); + *x = last_x + dx * (spacing_left / d); + *y = last_y + dy * (spacing_left / d); + + box2d box = perform_transform(*angle, *x, *y); + if (!allow_overlap_ && !detector_.has_placement(box)) + { + //10.0 is choosen arbitrarily + error_ += spacing_ * max_error_ / 10.0; + spacing_left += spacing_ * max_error_ / 10.0; + continue; + } + if (add_to_detector) detector_.insert(box); + last_x = *x; + last_y = *y; + return true; + } +} + +/** Rotates the size_ box and translates the position. */ +template box2d markers_placement::perform_transform(double angle, double dx, double dy) +{ + double c = cos(angle), s = sin(angle); + double x1 = size_.minx(); + double x2 = size_.maxx(); + double y1 = size_.miny(); + double y2 = size_.maxy(); + + double x1_ = dx + x1 * c - y1 * s; + double y1_ = dy + x1 * s + y1 * c; + double x2_ = dx + x2 * c - y2 * s; + double y2_ = dy + x2 * s + y2 * c; + + return box2d(x1_, y1_, x2_, y2_); +} + +typedef coord_transform2 path_type; +template class markers_placement; + +} //ns mapnik