[Git][debian-gis-team/mapnik][upstream] New upstream version 3.0.19~rc1+ds

Bas Couwenberg gitlab at salsa.debian.org
Mon Mar 5 15:23:23 UTC 2018


Bas Couwenberg pushed to branch upstream at Debian GIS Project / mapnik


Commits:
07e55bf9 by Bas Couwenberg at 2018-03-05T15:37:28+01:00
New upstream version 3.0.19~rc1+ds
- - - - -


16 changed files:

- CHANGELOG.md
- SConstruct
- deps/agg/include/agg_rendering_buffer.h
- + include/mapnik/grid_vertex_converter.hpp
- include/mapnik/symbolizer_enumerations.hpp
- include/mapnik/text/placements/list.hpp
- include/mapnik/text/placements/simple.hpp
- include/mapnik/text/symbolizer_helpers.hpp
- include/mapnik/text/text_properties.hpp
- include/mapnik/version.hpp
- src/geometry/interior.cpp
- src/load_map.cpp
- src/symbolizer_enumerations.cpp
- src/text/symbolizer_helpers.cpp
- src/text/text_properties.cpp
- + test/unit/geometry/grid_vertex_converter.cpp


Changes:

=====================================
CHANGELOG.md
=====================================
--- 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


=====================================
SConstruct
=====================================
--- 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)
 {


=====================================
deps/agg/include/agg_rendering_buffer.h
=====================================
--- 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;
 


=====================================
include/mapnik/grid_vertex_converter.hpp
=====================================
--- /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


=====================================
include/mapnik/symbolizer_enumerations.hpp
=====================================
--- 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
 };
 


=====================================
include/mapnik/text/placements/list.hpp
=====================================
--- 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) :


=====================================
include/mapnik/text/placements/simple.hpp
=====================================
--- 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,


=====================================
include/mapnik/text/symbolizer_helpers.hpp
=====================================
--- 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;
 


=====================================
include/mapnik/text/text_properties.hpp
=====================================
--- 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.


=====================================
include/mapnik/version.hpp
=====================================
--- 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)
 


=====================================
src/geometry/interior.cpp
=====================================
--- 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


=====================================
src/load_map.cpp
=====================================
--- 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())


=====================================
src/symbolizer_enumerations.cpp
=====================================
--- 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",
     ""
 };
 


=====================================
src/text/symbolizer_helpers.cpp
=====================================
--- 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,


=====================================
src/text/text_properties.cpp
=====================================
--- 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);


=====================================
test/unit/geometry/grid_vertex_converter.cpp
=====================================
--- /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);
+}
+
+}
+



View it on GitLab: https://salsa.debian.org/debian-gis-team/mapnik/commit/07e55bf93952a69e768b531f655e1e07ac7652b2

---
View it on GitLab: https://salsa.debian.org/debian-gis-team/mapnik/commit/07e55bf93952a69e768b531f655e1e07ac7652b2
You're receiving this email because of your account on salsa.debian.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.alioth.debian.org/pipermail/pkg-grass-devel/attachments/20180305/874893d0/attachment-0001.html>


More information about the Pkg-grass-devel mailing list