This is an automated email from the git hooks/post-receive script. sebastic pushed a commit to branch upstream in repository mapnik.
commit 07e55bf93952a69e768b531f655e1e07ac7652b2 Author: Bas Couwenberg <[email protected]> Date: Mon Mar 5 15:37:28 2018 +0100 New upstream version 3.0.19~rc1+ds --- CHANGELOG.md | 14 ++ SConstruct | 12 +- deps/agg/include/agg_rendering_buffer.h | 4 + include/mapnik/grid_vertex_converter.hpp | 257 +++++++++++++++++++++++++++ include/mapnik/symbolizer_enumerations.hpp | 2 + include/mapnik/text/placements/list.hpp | 4 +- include/mapnik/text/placements/simple.hpp | 4 +- include/mapnik/text/symbolizer_helpers.hpp | 12 +- include/mapnik/text/text_properties.hpp | 4 + include/mapnik/version.hpp | 2 +- src/geometry/interior.cpp | 28 ++- src/load_map.cpp | 2 + src/symbolizer_enumerations.cpp | 2 + src/text/symbolizer_helpers.cpp | 123 ++++++++++--- src/text/text_properties.cpp | 14 ++ test/unit/geometry/grid_vertex_converter.cpp | 103 +++++++++++ 16 files changed, 543 insertions(+), 44 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e468754..7c49303 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,20 @@ Developers: Please commit along with changes. For a complete change history, see the git log. +## 3.0.19 + +Released: March XX, 2018 + +(Packaged from xxxxxxxxx) + + - Backported scaling of precision by polygon size (#3844) + - Backported GRID placement (#3847, #3854, #3855) + - Added missing `MAPNIK_DECL` to all `text_placement_` types (7ce142a5aa8e9da5ddd11266a054c1e69052230d) + - Fixed invalid memory access if input_buffer size is zero (a602c65354a4b595821d2300f38ebc107d07e2a9) + - Fixed handling of an empty polygon in grid_vertex_converter (2f2dcf1eeae71aaa7878f4bc9a39741321f07e68) + - Fixed PROJ_LIB detection logic (44f1ae3a6e9e9979d1a93343f40db6cd7dbf51d5) + - Default to `icu-config` for obtaining `ICU_DATA` if `u_getDataDirectory fails (2cef98d7f76cdd302afcf15f1c585379537e8f1d) + ## 3.0.18 Released: January 26, 2018 diff --git a/SConstruct b/SConstruct index 4a6641d..1651824 100644 --- a/SConstruct +++ b/SConstruct @@ -830,9 +830,16 @@ int main() { context.did_show_result=1 if ret[0]: context.Result('u_getDataDirectory returned %s' % ret[1]) + return ret[1].strip() else: - context.Result('Failed to detect (mapnik-config will have null value)') - return ret[1].strip() + ret = call("icu-config --icudatadir", silent=True) + if ret: + context.Result('icu-config returned %s' % ret) + return ret + else: + context.Result('Failed to detect (mapnik-config will have null value)') + return '' + def CheckGdalData(context, silent=False): @@ -866,6 +873,7 @@ def CheckProjData(context, silent=False): // This is narly, could eventually be replaced using https://github.com/OSGeo/proj.4/pull/551] #include <proj_api.h> #include <iostream> +#include <cstring> static void my_proj4_logger(void * user_data, int /*level*/, const char * msg) { diff --git a/deps/agg/include/agg_rendering_buffer.h b/deps/agg/include/agg_rendering_buffer.h index e9e278b..2bb32e9 100644 --- a/deps/agg/include/agg_rendering_buffer.h +++ b/deps/agg/include/agg_rendering_buffer.h @@ -176,6 +176,10 @@ namespace agg { m_rows.resize(height); } + else if(height == 0) + { + return; + } T* row_ptr = m_buf; diff --git a/include/mapnik/grid_vertex_converter.hpp b/include/mapnik/grid_vertex_converter.hpp new file mode 100644 index 0000000..51fa7ff --- /dev/null +++ b/include/mapnik/grid_vertex_converter.hpp @@ -0,0 +1,257 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2015 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_GRID_ADAPTERS_HPP +#define MAPNIK_GRID_ADAPTERS_HPP + +// mapnik +#include <mapnik/vertex.hpp> +#include <mapnik/image.hpp> +#include <mapnik/image_util.hpp> +#include <mapnik/geom_util.hpp> +#include <mapnik/geometry/polygon_vertex_processor.hpp> +#include <mapnik/geometry_envelope.hpp> +#include <mapnik/geometry/interior.hpp> +#include <mapnik/view_strategy.hpp> +#include <mapnik/vertex_adapters.hpp> + +// agg +#include "agg_rendering_buffer.h" +#include "agg_pixfmt_gray.h" +#include "agg_renderer_base.h" +#include "agg_renderer_scanline.h" +#include "agg_rasterizer_scanline_aa.h" +#include "agg_scanline_bin.h" +#include "agg_conv_transform.h" + +namespace mapnik { namespace geometry { + +// Generates integer coordinates of a spiral similar to the Ulam spiral +// around [0, 0], bounded by size. +class spiral_iterator +{ +public: + spiral_iterator(unsigned size) + : end_(size * size), + i_(0), + x_(0), y_(0) + { + } + + bool vertex(int * x, int * y) + { + if (i_ < end_) + { + *x = x_; + *y = y_; + + if (std::abs(x_) <= std::abs(y_) && (x_ != y_ || x_ >= 0)) + { + x_ += ((y_ >= 0) ? 1 : -1); + } + else + { + y_ += ((x_ >= 0) ? -1 : 1); + } + + ++i_; + + return true; + } + return false; + } + + void rewind() + { + i_ = x_ = y_ = 0; + } + +private: + const unsigned end_; + unsigned i_; + int x_, y_; +}; + +struct view_transform_agg_adapter +{ + void transform(double * x, double * y) const + { + vt.forward(x, y); + } + + view_transform const& vt; +}; + +// Generates grid of points laying inside a polygon. +template <typename PathType, typename T, bool Alternating = false> +struct grid_vertex_converter +{ + grid_vertex_converter(PathType & path, T dx, T dy, double scale_factor) + : grid_vertex_converter(cache_path(path), dx, dy, scale_factor) + { + } + + void rewind(unsigned) + { + si_.rewind(); + } + + unsigned vertex(T * x, T * y) + { + int spiral_x, spiral_y; + while (si_.vertex(&spiral_x, &spiral_y)) + { + T pix_x = interior_.x + spiral_x * dx_; + T pix_y = interior_.y + spiral_y * dy_; + + if (Alternating && spiral_y % 2 != 0) + { + // Every odd line is shifted by dx/2. + pix_x += this->dx_ / 2.0; + } + + if (pix_x >= 0 && static_cast<std::size_t>(pix_x) < hit_bitmap_.width() && + pix_y >= 0 && static_cast<std::size_t>(pix_y) < hit_bitmap_.height() && + get_pixel<image_gray8::pixel_type>(hit_bitmap_, pix_x, pix_y)) + { + *x = pix_x; + *y = pix_y; + vt_.backward(x, y); + return mapnik::SEG_MOVETO; + } + } + return mapnik::SEG_END; + } + + geometry_types type() const + { + return geometry_types::MultiPoint; + } + +private: + grid_vertex_converter(polygon<T> const& poly, T dx, T dy, double scale_factor) + : grid_vertex_converter(poly, dx, dy, scale_factor, mapnik::geometry::envelope(poly)) + { + } + + grid_vertex_converter(polygon<T> const& poly, T dx, T dy, double scale_factor, box2d<T> const& envelope) + : hit_bitmap_scale_(get_hit_bitmap_scale(envelope)), + dx_(dx * hit_bitmap_scale_), + dy_(dy * hit_bitmap_scale_), + vt_(envelope.valid() ? (envelope.width() * hit_bitmap_scale_) : 0, + envelope.valid() ? (envelope.height() * hit_bitmap_scale_) : 0, envelope), + hit_bitmap_(create_hit_bitmap(poly)), + interior_(interior(poly, envelope, scale_factor)), + si_(std::max(std::ceil((hit_bitmap_.width() + std::abs((hit_bitmap_.width() / 2.0) - interior_.x) * 2.0) / dx_), + std::ceil((hit_bitmap_.height() + std::abs((hit_bitmap_.height() / 2.0) - interior_.y) * 2.0) / dy_))) + { + } + + double get_hit_bitmap_scale(box2d<T> const& envelope) const + { + if (envelope.valid()) + { + T size = envelope.width() * envelope.height(); + // Polygon with huge area can lead to excessive memory allocation. + // This is more or less arbitrarily chosen limit for the maximum bitmap resolution. + // Bitmap bigger than this limit is scaled down to fit into this resolution. + const std::size_t max_size = 8192 * 8192; + if (size > max_size) + { + return std::sqrt(max_size / size); + } + } + return 1; + } + + // The polygon is rendered to a bitmap for fast hit-testing. + image_gray8 create_hit_bitmap(polygon<T> const& poly) const + { + polygon_vertex_adapter<T> va(poly); + view_transform_agg_adapter vta{ vt_ }; + agg::conv_transform<polygon_vertex_adapter<T>, view_transform_agg_adapter> tp(va, vta); + tp.rewind(0); + agg::rasterizer_scanline_aa<> ras; + ras.add_path(tp); + + image_gray8 hit_bitmap(vt_.width(), vt_.height()); + agg::rendering_buffer buf(hit_bitmap.data(), + hit_bitmap.width(), + hit_bitmap.height(), + hit_bitmap.row_size()); + agg::pixfmt_gray8 pixfmt(buf); + using renderer_base = agg::renderer_base<agg::pixfmt_gray8>; + using renderer_bin = agg::renderer_scanline_bin_solid<renderer_base>; + renderer_base rb(pixfmt); + renderer_bin ren_bin(rb); + ren_bin.color(agg::gray8(1)); + agg::scanline_bin sl_bin; + agg::render_scanlines(ras, sl_bin, ren_bin); + + return hit_bitmap; + } + + mapnik::geometry::point<T> interior(polygon<T> const& poly, + box2d<T> const& envelope, + double scale_factor) const + { + mapnik::geometry::point<T> interior; + if (envelope.valid()) + { + if (!mapnik::geometry::interior(poly, scale_factor, interior)) + { + auto center = envelope.center(); + interior.x = center.x; + interior.y = center.y; + } + + vt_.forward(&interior.x, &interior.y); + } + return interior; + } + + polygon<T> cache_path(PathType & path) const + { + mapnik::geometry::polygon_vertex_processor<T> vertex_processor; + path.rewind(0); + vertex_processor.add_path(path); + return vertex_processor.polygon_; + } + + const double hit_bitmap_scale_; + const T dx_, dy_; + const view_transform vt_; + const image_gray8 hit_bitmap_; + const mapnik::geometry::point<T> interior_; + spiral_iterator si_; +}; + +template <typename PathType, typename T> +using regular_grid_vertex_converter = grid_vertex_converter<PathType, T, false>; + +template <typename PathType, typename T> +using alternating_grid_vertex_converter = grid_vertex_converter<PathType, T, true>; + +} +} + +#endif //MAPNIK_GRID_ADAPTERS_HPP diff --git a/include/mapnik/symbolizer_enumerations.hpp b/include/mapnik/symbolizer_enumerations.hpp index 7a3e72f..3b128d1 100644 --- a/include/mapnik/symbolizer_enumerations.hpp +++ b/include/mapnik/symbolizer_enumerations.hpp @@ -138,6 +138,8 @@ enum label_placement_enum : std::uint8_t LINE_PLACEMENT, VERTEX_PLACEMENT, INTERIOR_PLACEMENT, + GRID_PLACEMENT, + ALTERNATING_GRID_PLACEMENT, label_placement_enum_MAX }; diff --git a/include/mapnik/text/placements/list.hpp b/include/mapnik/text/placements/list.hpp index c21a4d8..6414084 100644 --- a/include/mapnik/text/placements/list.hpp +++ b/include/mapnik/text/placements/list.hpp @@ -30,7 +30,7 @@ class feature_impl; struct attribute; // Tries a list of placements. -class text_placements_list: public text_placements +class MAPNIK_DECL text_placements_list: public text_placements { public: text_placements_list(); @@ -47,7 +47,7 @@ private: // List placement strategy. // See parent class for documentation of each function. -class text_placement_info_list : public text_placement_info +class MAPNIK_DECL text_placement_info_list : public text_placement_info { public: text_placement_info_list(text_placements_list const* parent, double scale_factor) : diff --git a/include/mapnik/text/placements/simple.hpp b/include/mapnik/text/placements/simple.hpp index ea0d73c..37090bd 100644 --- a/include/mapnik/text/placements/simple.hpp +++ b/include/mapnik/text/placements/simple.hpp @@ -33,7 +33,7 @@ class feature_impl; struct attribute; // Automatically generates placement options from a user selected list of directions and text sizes. -class text_placements_simple: public text_placements +class MAPNIK_DECL text_placements_simple: public text_placements { public: text_placements_simple(symbolizer_base::value_type const& positions); @@ -53,7 +53,7 @@ private: // Simple placement strategy. // See parent class for documentation of each function. -class text_placement_info_simple : public text_placement_info +class MAPNIK_DECL text_placement_info_simple : public text_placement_info { public: text_placement_info_simple(text_placements_simple const* parent, diff --git a/include/mapnik/text/symbolizer_helpers.hpp b/include/mapnik/text/symbolizer_helpers.hpp index 8fd3c15..9225730 100644 --- a/include/mapnik/text/symbolizer_helpers.hpp +++ b/include/mapnik/text/symbolizer_helpers.hpp @@ -59,7 +59,13 @@ struct placement_finder_adapter }; -using vertex_converter_type = vertex_converter<clip_line_tag, transform_tag, affine_transform_tag, simplify_tag, smooth_tag>; +using vertex_converter_type = vertex_converter<clip_line_tag, + clip_poly_tag, + transform_tag, + affine_transform_tag, + simplify_tag, + smooth_tag, + offset_transform_tag>; class base_symbolizer_helper { @@ -147,6 +153,10 @@ public: // Return all placements. placements_list const& get() const; protected: + void init_converters(); + void initialize_points() const; + template <template <typename, typename> typename GridAdapter> + void initialize_grid_points() const; bool next_point_placement() const; bool next_line_placement() const; diff --git a/include/mapnik/text/text_properties.hpp b/include/mapnik/text/text_properties.hpp index 511ba3a..50ca500 100644 --- a/include/mapnik/text/text_properties.hpp +++ b/include/mapnik/text/text_properties.hpp @@ -80,6 +80,8 @@ struct evaluated_text_properties : util::noncopyable bool allow_overlap; bool largest_bbox_only; text_upright_e upright; + double grid_cell_width; + double grid_cell_height; }; } @@ -172,6 +174,8 @@ struct text_properties_expressions symbolizer_base::value_type allow_overlap = false; symbolizer_base::value_type largest_bbox_only = true; symbolizer_base::value_type upright = enumeration_wrapper(UPRIGHT_AUTO); + symbolizer_base::value_type grid_cell_width = 0.0; + symbolizer_base::value_type grid_cell_height = 0.0; }; // Contains all text symbolizer properties which are not directly related to text formatting and layout. diff --git a/include/mapnik/version.hpp b/include/mapnik/version.hpp index 97fc912..56fc01d 100644 --- a/include/mapnik/version.hpp +++ b/include/mapnik/version.hpp @@ -27,7 +27,7 @@ #define MAPNIK_MAJOR_VERSION 3 #define MAPNIK_MINOR_VERSION 0 -#define MAPNIK_PATCH_VERSION 18 +#define MAPNIK_PATCH_VERSION 19 #define MAPNIK_VERSION (MAPNIK_MAJOR_VERSION*100000) + (MAPNIK_MINOR_VERSION*100) + (MAPNIK_PATCH_VERSION) diff --git a/src/geometry/interior.cpp b/src/geometry/interior.cpp index 164d4c4..683f3ad 100644 --- a/src/geometry/interior.cpp +++ b/src/geometry/interior.cpp @@ -153,15 +153,8 @@ struct cell }; template <class T> -boost::optional<point<T>> polylabel(polygon<T> const& polygon, T precision = 1) +point<T> polylabel(polygon<T> const& polygon, box2d<T> const& bbox , T precision = 1) { - if (polygon.exterior_ring.empty()) - { - return boost::none; - } - - // find the bounding box of the outer ring - const box2d<T> bbox = envelope(polygon.exterior_ring); const point<T> size { bbox.width(), bbox.height() }; const T cell_size = std::min(size.x, size.y); @@ -177,14 +170,14 @@ boost::optional<point<T>> polylabel(polygon<T> const& polygon, T precision = 1) if (cell_size == 0) { - return point<T>{ bbox.minx(), bbox.miny() }; + return { bbox.minx(), bbox.miny() }; } point<T> centroid; if (!mapnik::geometry::centroid(polygon, centroid)) { auto center = bbox.center(); - return point<T>{ center.x, center.y }; + return { center.x, center.y }; } fitness_functor<T> fitness_func(centroid, size); @@ -232,15 +225,18 @@ boost::optional<point<T>> polylabel(polygon<T> const& polygon, T precision = 1) template <class T> bool interior(polygon<T> const& polygon, double scale_factor, point<T> & pt) { - // This precision has been chosen to work well in the map (viewport) coordinates. - double precision = 10.0 * scale_factor; - if (boost::optional<point<T>> opt = detail::polylabel(polygon, precision)) + if (polygon.exterior_ring.empty()) { - pt = *opt; - return true; + return false; } - return false; + const box2d<T> bbox = envelope(polygon.exterior_ring); + + // Let the precision be 1% of the polygon size to be independent to map scale. + double precision = (std::max(bbox.width(), bbox.height()) / 100.0) * scale_factor; + + pt = detail::polylabel(polygon, bbox, precision); + return true; } template diff --git a/src/load_map.cpp b/src/load_map.cpp index b51f811..9c625ab 100644 --- a/src/load_map.cpp +++ b/src/load_map.cpp @@ -1134,6 +1134,7 @@ void map_parser::parse_text_symbolizer(rule & rule, xml_node const& node) set_symbolizer_property<symbolizer_base,composite_mode_e>(sym, keys::halo_comp_op, node); set_symbolizer_property<symbolizer_base,halo_rasterizer_enum>(sym, keys::halo_rasterizer, node); set_symbolizer_property<symbolizer_base,transform_type>(sym, keys::halo_transform, node); + set_symbolizer_property<symbolizer_base,value_double>(sym, keys::offset, node); rule.append(std::move(sym)); } } @@ -1175,6 +1176,7 @@ void map_parser::parse_shield_symbolizer(rule & rule, xml_node const& node) set_symbolizer_property<symbolizer_base,double>(sym, keys::shield_dy, node); set_symbolizer_property<symbolizer_base,double>(sym, keys::opacity, node); set_symbolizer_property<symbolizer_base,value_bool>(sym, keys::unlock_image, node); + set_symbolizer_property<symbolizer_base,value_double>(sym, keys::offset, node); std::string file = node.get_attr<std::string>("file"); if (file.empty()) diff --git a/src/symbolizer_enumerations.cpp b/src/symbolizer_enumerations.cpp index 94227df..bd5dcc7 100644 --- a/src/symbolizer_enumerations.cpp +++ b/src/symbolizer_enumerations.cpp @@ -116,6 +116,8 @@ static const char * label_placement_strings[] = { "line", "vertex", "interior", + "grid", + "alternating-grid", "" }; diff --git a/src/text/symbolizer_helpers.cpp b/src/text/symbolizer_helpers.cpp index 4bac8a4..ed2f914 100644 --- a/src/text/symbolizer_helpers.cpp +++ b/src/text/symbolizer_helpers.cpp @@ -33,7 +33,6 @@ #include <mapnik/geometry_centroid.hpp> #include <mapnik/geometry/interior.hpp> #include <mapnik/vertex_processor.hpp> -#include <mapnik/geom_util.hpp> #include <mapnik/parse_path.hpp> #include <mapnik/debug.hpp> #include <mapnik/symbolizer.hpp> @@ -43,6 +42,7 @@ #include <mapnik/text/placements/dummy.hpp> #include <mapnik/geometry_transform.hpp> #include <mapnik/geometry_strategy.hpp> +#include <mapnik/grid_vertex_converter.hpp> #include <mapnik/proj_strategy.hpp> #include <mapnik/view_strategy.hpp> @@ -108,6 +108,31 @@ struct apply_vertex_placement proj_transform const& prj_trans_; }; +template <template <typename, typename> typename GridAdapter, typename T, typename Points> +struct grid_placement_finder_adapter +{ + grid_placement_finder_adapter(T dx, T dy, Points & points, double scale_factor) + : dx_(dx), dy_(dy), + points_(points), + scale_factor_(scale_factor) {} + + template <typename PathT> + void add_path(PathT & path) const + { + GridAdapter<PathT, T> gpa(path, dx_, dy_, scale_factor_); + gpa.rewind(0); + double label_x, label_y; + for (unsigned cmd; (cmd = gpa.vertex(&label_x, &label_y)) != SEG_END; ) + { + points_.emplace_back(label_x, label_y); + } + } + + T dx_, dy_; + Points & points_; + double scale_factor_; +}; + template <typename T> struct split_multi_geometries { @@ -244,14 +269,20 @@ void base_symbolizer_helper::initialize_geometries() const void base_symbolizer_helper::initialize_points() const { label_placement_enum how_placed = text_props_->label_placement; - if (how_placed == LINE_PLACEMENT) - { - point_placement_ = false; - return; - } - else + + switch (how_placed) { - point_placement_ = true; + case LINE_PLACEMENT: + point_placement_ = false; + return; + case GRID_PLACEMENT: + case ALTERNATING_GRID_PLACEMENT: + point_placement_ = true; + // Points for grid placement are generated in text_symbolizer_helper + // because base_symbolizer_helper doesn't have the vertex converter. + return; + default: + point_placement_ = true; } double label_x=0.0; @@ -337,19 +368,40 @@ text_symbolizer_helper::text_symbolizer_helper( adapter_(finder_,false), converter_(query_extent_, sym_, t, prj_trans, affine_trans, feature, vars, scale_factor) { + init_converters(); + if (geometries_to_process_.size()) + { + text_symbolizer_helper::initialize_points(); + finder_.next_position(); + } +} + +void text_symbolizer_helper::init_converters() +{ // setup vertex converter value_bool clip = mapnik::get<value_bool, keys::clip>(sym_, feature_, vars_); value_double simplify_tolerance = mapnik::get<value_double, keys::simplify_tolerance>(sym_, feature_, vars_); value_double smooth = mapnik::get<value_double, keys::smooth>(sym_, feature_, vars_); + boost::optional<value_double> offset = get_optional<value_double>(sym_, keys::offset, feature_, vars_); - if (clip) converter_.template set<clip_line_tag>(); + if (clip) + { + label_placement_enum how_placed = text_props_->label_placement; + if (how_placed == GRID_PLACEMENT || how_placed == ALTERNATING_GRID_PLACEMENT) + { + converter_.template set<clip_poly_tag>(); + } + else + { + converter_.template set<clip_line_tag>(); + } + } converter_.template set<transform_tag>(); //always transform converter_.template set<affine_transform_tag>(); if (simplify_tolerance > 0.0) converter_.template set<simplify_tag>(); // optional simplify converter if (smooth > 0.0) converter_.template set<smooth_tag>(); // optional smooth converter - - if (geometries_to_process_.size()) finder_.next_position(); + if (offset) converter_.template set<offset_transform_tag>(); // optional offset converter } placements_list const& text_symbolizer_helper::get() const @@ -463,18 +515,11 @@ text_symbolizer_helper::text_symbolizer_helper( adapter_(finder_,true), converter_(query_extent_, sym_, t, prj_trans, affine_trans, feature, vars, scale_factor) { - // setup vertex converter - value_bool clip = mapnik::get<value_bool, keys::clip>(sym_, feature_, vars_); - value_double simplify_tolerance = mapnik::get<value_double, keys::simplify_tolerance>(sym_, feature_, vars_); - value_double smooth = mapnik::get<value_double, keys::smooth>(sym_, feature_, vars_); + init_converters(); - if (clip) converter_.template set<clip_line_tag>(); - converter_.template set<transform_tag>(); //always transform - converter_.template set<affine_transform_tag>(); - if (simplify_tolerance > 0.0) converter_.template set<simplify_tag>(); // optional simplify converter - if (smooth > 0.0) converter_.template set<smooth_tag>(); // optional smooth converter if (geometries_to_process_.size()) { + text_symbolizer_helper::initialize_points(); init_marker(); finder_.next_position(); } @@ -515,6 +560,44 @@ void text_symbolizer_helper::init_marker() const finder_.set_marker(std::make_shared<marker_info>(marker, trans), bbox, unlock_image, marker_displacement); } +template <template <typename, typename> typename GridAdapter> +void text_symbolizer_helper::initialize_grid_points() const +{ + for (auto const& geom : geometries_to_process_) + { + auto type = geometry::geometry_type(geom); + if (type != geometry::geometry_types::Polygon) + { + continue; + } + + using adapter_type = detail::grid_placement_finder_adapter< + GridAdapter, double, std::list<pixel_position>>; + adapter_type ga(text_props_->grid_cell_width, + text_props_->grid_cell_height, + points_, + scale_factor_); + auto const& poly = mapnik::util::get<geometry::polygon<double>>(geom); + geometry::polygon_vertex_adapter<double> va(poly); + converter_.apply(va, ga); + } +} + +void text_symbolizer_helper::initialize_points() const +{ + label_placement_enum how_placed = text_props_->label_placement; + + if (how_placed == GRID_PLACEMENT) + { + initialize_grid_points<geometry::regular_grid_vertex_converter>(); + } + else if (how_placed == ALTERNATING_GRID_PLACEMENT) + { + initialize_grid_points<geometry::alternating_grid_vertex_converter>(); + } + point_itr_ = points_.begin(); +} + template text_symbolizer_helper::text_symbolizer_helper( text_symbolizer const& sym, feature_impl const& feature, diff --git a/src/text/text_properties.cpp b/src/text/text_properties.cpp index 73210d1..2728d93 100644 --- a/src/text/text_properties.cpp +++ b/src/text/text_properties.cpp @@ -61,6 +61,8 @@ evaluated_text_properties_ptr evaluate_text_properties(text_symbolizer_propertie prop->allow_overlap = util::apply_visitor(extract_value<value_bool>(feature,attrs), text_prop.expressions.allow_overlap); prop->largest_bbox_only = util::apply_visitor(extract_value<value_bool>(feature,attrs), text_prop.expressions.largest_bbox_only); prop->upright = util::apply_visitor(extract_value<text_upright_enum>(feature,attrs), text_prop.expressions.upright); + prop->grid_cell_width = util::apply_visitor(extract_value<value_double>(feature,attrs), text_prop.expressions.grid_cell_width); + prop->grid_cell_height = util::apply_visitor(extract_value<value_double>(feature,attrs), text_prop.expressions.grid_cell_height); return prop; } @@ -108,6 +110,8 @@ void text_symbolizer_properties::text_properties_from_xml(xml_node const& node) set_property_from_xml<value_bool>(expressions.largest_bbox_only, "largest-bbox-only", node); set_property_from_xml<value_double>(expressions.max_char_angle_delta, "max-char-angle-delta", node); set_property_from_xml<text_upright_e>(expressions.upright, "upright", node); + set_property_from_xml<value_double>(expressions.grid_cell_width, "grid-cell-width", node); + set_property_from_xml<value_double>(expressions.grid_cell_height, "grid-cell-height", node); } void text_symbolizer_properties::from_xml(xml_node const& node, fontset_map const& fontsets, bool is_shield) @@ -175,6 +179,14 @@ void text_symbolizer_properties::to_xml(boost::property_tree::ptree &node, { serialize_property("upright", expressions.upright, node); } + if (!(expressions.grid_cell_width == dfl.expressions.grid_cell_width) || explicit_defaults) + { + serialize_property("grid-cell-width", expressions.grid_cell_width, node); + } + if (!(expressions.grid_cell_height == dfl.expressions.grid_cell_height) || explicit_defaults) + { + serialize_property("grid-cell-height", expressions.grid_cell_height, node); + } layout_defaults.to_xml(node, explicit_defaults, dfl.layout_defaults); format_defaults.to_xml(node, explicit_defaults, dfl.format_defaults); @@ -197,6 +209,8 @@ void text_symbolizer_properties::add_expressions(expression_set & output) const if (is_expression(expressions.allow_overlap)) output.insert(util::get<expression_ptr>(expressions.allow_overlap)); if (is_expression(expressions.largest_bbox_only)) output.insert(util::get<expression_ptr>(expressions.largest_bbox_only)); if (is_expression(expressions.upright)) output.insert(util::get<expression_ptr>(expressions.upright)); + if (is_expression(expressions.grid_cell_width)) output.insert(util::get<expression_ptr>(expressions.grid_cell_width)); + if (is_expression(expressions.grid_cell_height)) output.insert(util::get<expression_ptr>(expressions.grid_cell_height)); layout_defaults.add_expressions(output); format_defaults.add_expressions(output); diff --git a/test/unit/geometry/grid_vertex_converter.cpp b/test/unit/geometry/grid_vertex_converter.cpp new file mode 100644 index 0000000..d7b9820 --- /dev/null +++ b/test/unit/geometry/grid_vertex_converter.cpp @@ -0,0 +1,103 @@ +#include "catch.hpp" + +#include <mapnik/grid_vertex_converter.hpp> + +TEST_CASE("spiral_iterator") { + +SECTION("sprial 3x3") { + + mapnik::geometry::spiral_iterator si(3); + const mapnik::geometry::point<int> points[] = { + { 0, 0 }, { 1, 0 }, { 1, -1 }, + { 0, -1 }, { -1, -1 }, { -1, 0 }, + { -1, 1 }, { 0, 1 }, { 1, 1 } }; + + const std::size_t points_size = std::extent<decltype(points)>::value; + + int x, y; + std::size_t index = 0; + + while (si.vertex(&x, &y)) + { + REQUIRE(index < points_size); + + CHECK(x == points[index].x); + CHECK(y == points[index].y); + + index++; + } + + CHECK(index == points_size); +} + +} + +TEST_CASE("grid_vertex_converter") { + +SECTION("empty polygon") { + + mapnik::geometry::polygon<double> poly; + using path_type = mapnik::geometry::polygon_vertex_adapter<double>; + path_type path(poly); + using converter_type = mapnik::geometry::grid_vertex_converter<path_type, double>; + converter_type gvc(path, 10.0, 10.0, 1.0); + + double x, y; + unsigned cmd = gvc.vertex(&x, &y); + + CHECK(cmd == mapnik::SEG_END); + +} + +SECTION("grid of a square") { + + mapnik::geometry::polygon<double> poly; + auto & exterior_ring = poly.exterior_ring; + exterior_ring.emplace_back(-10, -10); + exterior_ring.emplace_back( 10, -10); + exterior_ring.emplace_back( 10, 10); + exterior_ring.emplace_back(-10, 10); + exterior_ring.emplace_back(-10, -10); + + using path_type = mapnik::geometry::polygon_vertex_adapter<double>; + path_type path(poly); + using converter_type = mapnik::geometry::grid_vertex_converter<path_type, double>; + converter_type gvc(path, 3.0, 3.0, 1.0); + + const mapnik::geometry::point<double> points[] = { + { 0, 0 }, { 3, 0 }, { 3, 3 }, { 0, 3 }, + { -3, 3 }, { -3, 0 }, { -3, -3 }, { 0, -3 }, + { 3, -3 }, { 6, -3 }, { 6, 0 }, { 6, 3 }, + { 6, 6 }, { 3, 6 }, { 0, 6 }, { -3, 6 }, + { -6, 6 }, { -6, 3 }, { -6, 0 }, { -6, -3 }, + { -6, -6 }, { -3, -6 }, { 0, -6 }, { 3, -6 }, + { 6, -6 }, { 9, -6 }, { 9, -3 }, { 9, 0 }, + { 9, 3 }, { 9, 6 }, { 9, 9 }, { 6, 9 }, + { 3, 9 }, { 0, 9 }, { -3, 9 }, { -6, 9 }, + { -9, 9 }, { -9, 6 }, { -9, 3 }, { -9, 0 }, + { -9, -3 }, { -9, -6 }, { -9, -9 }, { -6, -9 }, + { -3, -9 }, { 0, -9 }, { 3, -9 }, { 6, -9 }, + { 9, -9 } }; + const std::size_t points_size = std::extent<decltype(points)>::value; + + double x, y; + unsigned cmd = mapnik::SEG_END; + std::size_t index = 0; + + while ((cmd = gvc.vertex(&x, &y)) != mapnik::SEG_END) + { + REQUIRE(index < points_size); + + CHECK(cmd == mapnik::SEG_MOVETO); + CHECK(x == Approx(points[index].x)); + CHECK(y == Approx(points[index].y)); + + index++; + } + + CHECK(index == points_size); + CHECK(cmd == mapnik::SEG_END); +} + +} + -- Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-grass/mapnik.git _______________________________________________ Pkg-grass-devel mailing list [email protected] http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/pkg-grass-devel

