Add grid placement for text and shield symbolizer
This commit is contained in:
parent
1bb070c842
commit
098fd27291
9 changed files with 493 additions and 21 deletions
252
include/mapnik/grid_vertex_converter.hpp
Normal file
252
include/mapnik/grid_vertex_converter.hpp
Normal file
|
@ -0,0 +1,252 @@
|
|||
/*****************************************************************************
|
||||
*
|
||||
* 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
|
||||
{
|
||||
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 (!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
|
|
@ -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
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -116,6 +116,8 @@ static const char * label_placement_strings[] = {
|
|||
"line",
|
||||
"vertex",
|
||||
"interior",
|
||||
"grid",
|
||||
"alternating-grid",
|
||||
""
|
||||
};
|
||||
|
||||
|
|
|
@ -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,13 +269,19 @@ 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)
|
||||
|
||||
switch (how_placed)
|
||||
{
|
||||
case LINE_PLACEMENT:
|
||||
point_placement_ = false;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
103
test/unit/geometry/grid_vertex_converter.cpp
Normal file
103
test/unit/geometry/grid_vertex_converter.cpp
Normal file
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in a new issue