From ea7ba2c09969661be702dc44f0fab36f093ff330 Mon Sep 17 00:00:00 2001 From: Jiri Drbalek Date: Fri, 29 Sep 2017 11:31:03 +0000 Subject: [PATCH] add polylabel placement method --- .gitmodules | 3 + SConstruct | 1 + deps/mapbox/polylabel | 1 + .../geometry/polygon_vertex_processor.hpp | 62 +++++++++++++++ include/mapnik/geometry/polylabel.hpp | 39 ++++++++++ include/mapnik/markers_placement.hpp | 10 +++ include/mapnik/markers_placements/basic.hpp | 1 + .../mapnik/markers_placements/polylabel.hpp | 77 +++++++++++++++++++ include/mapnik/symbolizer_enumerations.hpp | 2 + src/build.py | 1 + src/geometry/polylabel.cpp | 49 ++++++++++++ src/marker_helpers.cpp | 2 +- src/symbolizer_enumerations.cpp | 2 + src/text/symbolizer_helpers.cpp | 7 ++ 14 files changed, 256 insertions(+), 1 deletion(-) create mode 160000 deps/mapbox/polylabel create mode 100644 include/mapnik/geometry/polygon_vertex_processor.hpp create mode 100644 include/mapnik/geometry/polylabel.hpp create mode 100644 include/mapnik/markers_placements/polylabel.hpp create mode 100644 src/geometry/polylabel.cpp diff --git a/.gitmodules b/.gitmodules index aaa9d544d..4d54aeb4a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -18,3 +18,6 @@ path = deps/mapbox/protozero url = https://github.com/mapbox/protozero.git branch = master +[submodule "deps/mapbox/polylabel"] + path = deps/mapbox/polylabel + url = https://github.com/mapbox/polylabel.git diff --git a/SConstruct b/SConstruct index 429cc364c..372f7ac79 100644 --- a/SConstruct +++ b/SConstruct @@ -1756,6 +1756,7 @@ if not preconfigured: env.Prepend(CPPPATH = '#deps/mapbox/variant/include') env.Prepend(CPPPATH = '#deps/mapbox/geometry/include') env.Prepend(CPPPATH = '#deps/mapbox/protozero/include') + env.Prepend(CPPPATH = '#deps/mapbox/polylabel/include') # prepend deps dir for auxillary headers env.Prepend(CPPPATH = '#deps') diff --git a/deps/mapbox/polylabel b/deps/mapbox/polylabel new file mode 160000 index 000000000..51f09d04c --- /dev/null +++ b/deps/mapbox/polylabel @@ -0,0 +1 @@ +Subproject commit 51f09d04c21e7b7faf94e2300ca1fe6e1f12fa7f diff --git a/include/mapnik/geometry/polygon_vertex_processor.hpp b/include/mapnik/geometry/polygon_vertex_processor.hpp new file mode 100644 index 000000000..5c95aed14 --- /dev/null +++ b/include/mapnik/geometry/polygon_vertex_processor.hpp @@ -0,0 +1,62 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2017 Artem Pavlenko + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ + +#ifndef MAPNIK_GEOMETRY_POLYGON_VERTEX_PROCESSOR_HPP +#define MAPNIK_GEOMETRY_POLYGON_VERTEX_PROCESSOR_HPP + +// geometry +#include + +namespace mapnik { namespace geometry { + +template +struct polygon_vertex_processor +{ + template + void add_path(Path & path) + { + point p; + unsigned cmd; + linear_ring ring; + while ((cmd = path.vertex(&p.x, &p.y)) != SEG_END) + { + switch (cmd) + { + case SEG_MOVETO: + case SEG_LINETO: + ring.emplace_back(p); + break; + case SEG_CLOSE: + ring.emplace_back(ring.front()); + polygon_.emplace_back(std::move(ring)); + ring = linear_ring(); + break; + } + } + } + + polygon polygon_; +}; + +} } + +#endif // MAPNIK_GEOMETRY_POLYGON_VERTEX_PROCESSOR_HPP diff --git a/include/mapnik/geometry/polylabel.hpp b/include/mapnik/geometry/polylabel.hpp new file mode 100644 index 000000000..3eb4b9ad2 --- /dev/null +++ b/include/mapnik/geometry/polylabel.hpp @@ -0,0 +1,39 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2017 Artem Pavlenko + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ + +#ifndef MAPNIK_GEOMETRY_POLYLABEL_HPP +#define MAPNIK_GEOMETRY_POLYLABEL_HPP + +#include +#include + +namespace mapnik { namespace geometry { + +template +point polylabel(polygon const& polygon, T precision); + +template +T polylabel_precision(polygon const& polygon, double scale_factor); + +} } + +#endif // MAPNIK_GEOMETRY_POLYLABEL_HPP diff --git a/include/mapnik/markers_placement.hpp b/include/mapnik/markers_placement.hpp index 4f4de7a02..8093736ed 100644 --- a/include/mapnik/markers_placement.hpp +++ b/include/mapnik/markers_placement.hpp @@ -28,6 +28,7 @@ #include #include #include +#include #include namespace mapnik @@ -65,6 +66,9 @@ public: case MARKER_VERTEX_LAST_PLACEMENT: construct(&vertex_last_, locator, detector, params); break; + case MARKER_POLYLABEL_PLACEMENT: + construct(&polylabel_, locator, detector, params); + break; } } @@ -89,6 +93,9 @@ public: case MARKER_VERTEX_LAST_PLACEMENT: destroy(&vertex_last_); break; + case MARKER_POLYLABEL_PLACEMENT: + destroy(&polylabel_); + break; } } @@ -109,6 +116,8 @@ public: return vertex_first_.get_point(x, y, angle, ignore_placement); case MARKER_VERTEX_LAST_PLACEMENT: return vertex_last_.get_point(x, y, angle, ignore_placement); + case MARKER_POLYLABEL_PLACEMENT: + return polylabel_.get_point(x, y, angle, ignore_placement); } } @@ -122,6 +131,7 @@ private: markers_interior_placement interior_; markers_vertex_first_placement vertex_first_; markers_vertex_last_placement vertex_last_; + markers_polylabel_placement polylabel_; }; template diff --git a/include/mapnik/markers_placements/basic.hpp b/include/mapnik/markers_placements/basic.hpp index 238f6150b..9ffcd6fb5 100644 --- a/include/mapnik/markers_placements/basic.hpp +++ b/include/mapnik/markers_placements/basic.hpp @@ -46,6 +46,7 @@ struct markers_placement_params bool allow_overlap; bool avoid_edges; direction_enum direction; + double scale_factor; }; class markers_basic_placement : util::noncopyable diff --git a/include/mapnik/markers_placements/polylabel.hpp b/include/mapnik/markers_placements/polylabel.hpp new file mode 100644 index 000000000..ac07a8cfe --- /dev/null +++ b/include/mapnik/markers_placements/polylabel.hpp @@ -0,0 +1,77 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2017 Artem Pavlenko + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ + +#ifndef MAPNIK_MARKERS_PLACEMENTS_POLYLABEL_HPP +#define MAPNIK_MARKERS_PLACEMENTS_POLYLABEL_HPP + +#include +#include +#include +#include + +#include +#include + +namespace mapnik { + +template +class markers_polylabel_placement : public markers_point_placement +{ +public: + using point_placement = markers_point_placement; + using point_placement::point_placement; + + bool get_point(double &x, double &y, double &angle, bool ignore_placement) + { + if (this->done_) + { + return false; + } + + if (this->locator_.type() != geometry::geometry_types::Polygon) + { + return point_placement::get_point(x, y, angle, ignore_placement); + } + + geometry::polygon_vertex_processor vertex_processor; + vertex_processor.add_path(this->locator_); + double precision = geometry::polylabel_precision(vertex_processor.polygon_, + this->params_.scale_factor); + geometry::point placement = geometry::polylabel(vertex_processor.polygon_, precision); + + x = placement.x; + y = placement.y; + angle = 0; + + if (!this->push_to_detector(x, y, angle, ignore_placement)) + { + return false; + } + + this->done_ = true; + return true; + } +}; + +} + +#endif // MAPNIK_MARKERS_PLACEMENTS_POLYLABEL_HPP diff --git a/include/mapnik/symbolizer_enumerations.hpp b/include/mapnik/symbolizer_enumerations.hpp index d1c07c1f2..4fb4d42a9 100644 --- a/include/mapnik/symbolizer_enumerations.hpp +++ b/include/mapnik/symbolizer_enumerations.hpp @@ -106,6 +106,7 @@ enum marker_placement_enum : std::uint8_t MARKER_VERTEX_FIRST_PLACEMENT, MARKER_VERTEX_LAST_PLACEMENT, MARKER_ANGLED_POINT_PLACEMENT, + MARKER_POLYLABEL_PLACEMENT, marker_placement_enum_MAX }; @@ -139,6 +140,7 @@ enum label_placement_enum : std::uint8_t LINE_PLACEMENT, VERTEX_PLACEMENT, INTERIOR_PLACEMENT, + POLYLABEL_PLACEMENT, label_placement_enum_MAX }; diff --git a/src/build.py b/src/build.py index 2f4e5172e..c73571204 100644 --- a/src/build.py +++ b/src/build.py @@ -173,6 +173,7 @@ source = Split( geometry/closest_point.cpp geometry/reprojection.cpp geometry/envelope.cpp + geometry/polylabel.cpp expression_node.cpp expression_string.cpp expression.cpp diff --git a/src/geometry/polylabel.cpp b/src/geometry/polylabel.cpp new file mode 100644 index 000000000..1ace5949c --- /dev/null +++ b/src/geometry/polylabel.cpp @@ -0,0 +1,49 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2017 Artem Pavlenko + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ + +#include +#include +#include + +namespace mapnik { namespace geometry { + +template +T polylabel_precision(polygon const& polygon, double scale_factor) +{ + // This precision has been chosen to work well in the map (viewport) coordinates. + return 10.0 * scale_factor; +} + +template +point polylabel(polygon const& polygon, T precision) +{ + return mapbox::polylabel(polygon, precision); +} + +template +point polylabel(polygon const& polygon, double precision); + +template +double polylabel_precision(polygon const& polygon, double scale_factor); + +} } + diff --git a/src/marker_helpers.cpp b/src/marker_helpers.cpp index e40d3587a..e8c4827a9 100644 --- a/src/marker_helpers.cpp +++ b/src/marker_helpers.cpp @@ -250,7 +250,7 @@ void apply_markers_multi(feature_impl const& feature, attributes const& vars, converter.apply(va, proc); } } - else if ((placement == MARKER_POINT_PLACEMENT || placement == MARKER_INTERIOR_PLACEMENT) && + else if ((placement == MARKER_POINT_PLACEMENT || placement == MARKER_INTERIOR_PLACEMENT || placement == MARKER_POLYLABEL_PLACEMENT) && multi_policy == MARKER_LARGEST_MULTI) { // Only apply to path with largest envelope area diff --git a/src/symbolizer_enumerations.cpp b/src/symbolizer_enumerations.cpp index 6c3b0d96f..6669f39d2 100644 --- a/src/symbolizer_enumerations.cpp +++ b/src/symbolizer_enumerations.cpp @@ -68,6 +68,7 @@ static const char * marker_placement_strings[] = { "vertex-first", "vertex-last", "angled-point", + "polylabel", "" }; @@ -117,6 +118,7 @@ static const char * label_placement_strings[] = { "line", "vertex", "interior", + "polylabel", "" }; diff --git a/src/text/symbolizer_helpers.cpp b/src/text/symbolizer_helpers.cpp index 65aaeafa6..47fa00b15 100644 --- a/src/text/symbolizer_helpers.cpp +++ b/src/text/symbolizer_helpers.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -307,6 +308,12 @@ void base_symbolizer_helper::initialize_points() const points_.emplace_back(label_x, label_y); } } + else if (how_placed == POLYLABEL_PLACEMENT) + { + double precision = geometry::polylabel_precision(tranformed_poly, scale_factor_); + geometry::point pt = geometry::polylabel(tranformed_poly, precision); + points_.emplace_back(pt.x, pt.y); + } continue; } else