Merge branch 'text-extend' of https://github.com/mapycz/mapnik into mapycz-text-extend

This commit is contained in:
artemp 2017-03-14 11:53:52 +00:00
commit c1053181f5
12 changed files with 490 additions and 100 deletions

View file

@ -0,0 +1,232 @@
/*****************************************************************************
*
* 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_EXTEND_CONVERTER_HPP
#define MAPNIK_EXTEND_CONVERTER_HPP
#include <mapnik/vertex.hpp>
#pragma GCC diagnostic push
#include <mapnik/warning_ignore.hpp>
#include <boost/optional.hpp>
#include <boost/msm/back/state_machine.hpp>
#include <boost/msm/front/state_machine_def.hpp>
#include <boost/msm/front/functor_row.hpp>
#pragma GCC diagnostic pop
// stl
#include <cmath>
namespace mapnik
{
namespace detail
{
namespace msm = boost::msm;
namespace mpl = boost::mpl;
using namespace msm::front;
template <typename T>
T extend(T const & v1, T const & v2, double length)
{
double dx = v2.x - v1.x;
double dy = v2.y - v1.y;
double l12 = std::sqrt(dx * dx + dy * dy);
double coef = 1.0 + length / l12;
return vertex2d(v1.x + dx * coef,
v1.y + dy * coef, v2.cmd);
}
namespace events
{
struct vertex_event
{
vertex_event(vertex2d const & vertex) : vertex(vertex) { }
vertex2d const & vertex;
};
struct move_to : vertex_event { using vertex_event::vertex_event; };
struct line_to : vertex_event { using vertex_event::vertex_event; };
struct close : vertex_event { using vertex_event::vertex_event; };
struct end : vertex_event { using vertex_event::vertex_event; };
}
namespace actions
{
struct store
{
template <class FSM, class EVT, class SourceState, class TargetState>
void operator()(EVT const & e, FSM & m, SourceState&, TargetState&)
{
m.v2 = m.v1;
m.v1 = e.vertex;
m.output = boost::none;
}
};
struct output
{
template <class FSM, class EVT, class SourceState, class TargetState>
void operator()(EVT const & e, FSM & m, SourceState&, TargetState&)
{
m.output = e.vertex;
}
};
struct store_and_output
{
template <class FSM, class EVT, class SourceState, class TargetState>
void operator()(EVT const & e, FSM & m, SourceState&, TargetState&)
{
m.v2 = m.v1;
m.v1 = e.vertex;
m.output = m.v2;
}
};
struct output_begin
{
template <class FSM, class EVT, class SourceState, class TargetState>
void operator()(EVT const & e, FSM & m, SourceState&, TargetState&)
{
m.v2 = m.v1;
m.v1 = e.vertex;
m.output = extend(m.v1, m.v2, m.extend_length);
}
};
struct output_end
{
template <class FSM, class EVT, class SourceState, class TargetState>
void operator()(EVT const & e, FSM & m, SourceState&, TargetState&)
{
m.output = extend(m.v2, m.v1, m.extend_length);
m.v1 = e.vertex;
}
};
}
struct extender_def : public msm::front::state_machine_def<extender_def>
{
using no_exception_thrown = int;
using no_message_queue = int;
struct initial : public msm::front::state<> { };
struct vertex_one : public msm::front::state<> { };
struct vertex_two : public msm::front::state<> { };
struct end : public msm::front::state<> { };
using initial_state = initial;
struct transition_table : mpl::vector<
// Start Event Next Action Guard
// +------------+-----------------+------------+--------------------+------+
Row < initial , events::move_to , vertex_one , actions::store >,
Row < initial , events::line_to , vertex_one , actions::store >,
Row < initial , events::close , initial >,
Row < initial , events::end , end , actions::output >,
Row < vertex_one , events::move_to , vertex_one , actions::store_and_output >,
Row < vertex_one , events::line_to , vertex_two , actions::output_begin >,
Row < vertex_one , events::close , initial , actions::store_and_output >,
Row < vertex_one , events::end , end , actions::store_and_output >,
Row < vertex_two , events::move_to , vertex_one , actions::output_end >,
Row < vertex_two , events::line_to , vertex_two , actions::store_and_output >,
Row < vertex_two , events::close , initial , actions::output_end >,
Row < vertex_two , events::end , end , actions::output_end >,
Row < end , events::end , end , actions::output >
> {};
extender_def(double extend_length)
: extend_length(extend_length)
{
}
boost::optional<vertex2d> output;
vertex2d v1, v2;
double extend_length;
};
using extender = msm::back::state_machine<extender_def>;
}
template <typename Geometry>
struct extend_converter
{
extend_converter(Geometry & geom)
: extend_converter(geom, 0)
{}
extend_converter(Geometry & geom, double extend)
: geom_(geom), extender_(extend)
{}
void set_extend(double extend)
{
extender_.extend_length = extend;
}
unsigned vertex(double * x, double * y)
{
using namespace detail;
vertex2d v;
do
{
v.cmd = geom_.vertex(&v.x, &v.y);
switch (v.cmd)
{
case SEG_MOVETO:
extender_.process_event(events::move_to(v));
break;
case SEG_LINETO:
extender_.process_event(events::line_to(v));
break;
case SEG_CLOSE:
extender_.process_event(events::close(v));
break;
case SEG_END:
extender_.process_event(events::end(v));
break;
}
} while(!extender_.output);
vertex2d const & output = *extender_.output;
*x = output.x;
*y = output.y;
return output.cmd;
}
void rewind(unsigned)
{
geom_.rewind(0);
extender_.start();
}
private:
Geometry & geom_;
detail::extender extender_;
};
}
#endif // MAPNIK_EXTEND_CONVERTER_HPP

View file

@ -369,6 +369,13 @@ struct symbolizer_default<value_bool, keys::avoid_edges>
// font-feature-settings
// extend
template <>
struct symbolizer_default<value_double, keys::extend>
{
static value_double value() { return 0.0; }
};
} // namespace mapnik
#endif // MAPNIK_SYMBOLIZER_DEFAULT_VALUES_HPP

View file

@ -92,6 +92,7 @@ enum class keys : std::uint8_t
direction,
avoid_edges,
ff_settings,
extend,
MAX_SYMBOLIZER_KEY
};

View file

@ -59,7 +59,7 @@ 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, transform_tag, affine_transform_tag, extend_tag, simplify_tag, smooth_tag>;
class base_symbolizer_helper
{

View file

@ -36,6 +36,7 @@
#include <mapnik/symbolizer_enumerations.hpp>
#include <mapnik/symbolizer_keys.hpp>
#include <mapnik/symbolizer.hpp>
#include <mapnik/extend_converter.hpp>
#pragma GCC diagnostic push
#include <mapnik/warning_ignore_agg.hpp>
@ -65,6 +66,7 @@ struct stroke_tag {};
struct dash_tag {};
struct affine_transform_tag {};
struct offset_transform_tag {};
struct extend_tag {};
namespace detail {
@ -254,6 +256,23 @@ struct converter_traits<T,mapnik::offset_transform_tag>
}
};
template <typename T>
struct converter_traits<T, mapnik::extend_tag>
{
using geometry_type = T;
using conv_type = extend_converter<geometry_type>;
template <typename Args>
static void setup(geometry_type & geom, Args const& args)
{
auto const& sym = args.sym;
auto const& feat = args.feature;
auto const& vars = args.vars;
double extend = get<value_double, keys::extend>(sym, feat, vars);
geom.set_extend(extend * args.scale_factor);
}
};
template <typename T0, typename T1>
struct is_switchable

View file

@ -895,6 +895,7 @@ void map_parser::parse_symbolizer_base(symbolizer_base &sym, xml_node const& nod
set_symbolizer_property<symbolizer_base,composite_mode_e>(sym, keys::comp_op, node);
set_symbolizer_property<symbolizer_base,transform_type>(sym, keys::geometry_transform, node);
set_symbolizer_property<symbolizer_base,simplify_algorithm_e>(sym, keys::simplify_algorithm, node);
set_symbolizer_property<symbolizer_base,double>(sym, keys::extend, node);
}
void map_parser::parse_point_symbolizer(rule & rule, xml_node const & node)

View file

@ -158,6 +158,7 @@ static const property_meta_type key_meta[const_max_key] =
property_types::target_direction},
property_meta_type{ "avoid-edges",nullptr, property_types::target_bool },
property_meta_type{ "font-feature-settings", nullptr, property_types::target_font_feature_settings },
property_meta_type{ "extend", nullptr, property_types::target_double},
};

View file

@ -327,10 +327,12 @@ text_symbolizer_helper::text_symbolizer_helper(
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_);
value_double extend = mapnik::get<value_double, keys::extend>(sym_, feature_, vars_);
if (clip) converter_.template set<clip_line_tag>();
converter_.template set<transform_tag>(); //always transform
converter_.template set<affine_transform_tag>();
if (extend > 0.0) converter_.template set<extend_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
@ -452,12 +454,15 @@ text_symbolizer_helper::text_symbolizer_helper(
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_);
value_double extend = mapnik::get<value_double, keys::extend>(sym_, feature_, vars_);
if (clip) converter_.template set<clip_line_tag>();
converter_.template set<transform_tag>(); //always transform
converter_.template set<affine_transform_tag>();
if (extend > 0.0) converter_.template set<extend_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())
{
init_marker();

View file

@ -0,0 +1,128 @@
#include "catch.hpp"
#include "fake_path.hpp"
// mapnik
#include <mapnik/extend_converter.hpp>
// stl
#include <iostream>
namespace offset_test {
TEST_CASE("extend converter") {
SECTION("empty") {
try
{
fake_path path = {};
mapnik::extend_converter<fake_path> c(path, 1000);
double x, y;
REQUIRE(c.vertex(&x, &y) == mapnik::SEG_END);
}
catch (std::exception const& ex)
{
std::cerr << ex.what() << "\n";
REQUIRE(false);
}
}
SECTION("one point") {
try
{
fake_path path = { 0, 0 };
mapnik::extend_converter<fake_path> c(path, 1000);
double x, y;
REQUIRE(c.vertex(&x, &y) == mapnik::SEG_MOVETO);
REQUIRE(x == 0);
REQUIRE(y == 0);
REQUIRE(c.vertex(&x, &y) == mapnik::SEG_END);
REQUIRE(c.vertex(&x, &y) == mapnik::SEG_END);
}
catch (std::exception const& ex)
{
std::cerr << ex.what() << "\n";
REQUIRE(false);
}
}
SECTION("two points") {
try
{
fake_path path = { 0, 0 , 1, 0};
mapnik::extend_converter<fake_path> c(path, 1000);
double x, y;
REQUIRE(c.vertex(&x, &y) == mapnik::SEG_MOVETO);
REQUIRE(x == -1000);
REQUIRE(y == 0);
REQUIRE(c.vertex(&x, &y) == mapnik::SEG_LINETO);
REQUIRE(x == 1001);
REQUIRE(y == 0);
REQUIRE(c.vertex(&x, &y) == mapnik::SEG_END);
}
catch (std::exception const& ex)
{
std::cerr << ex.what() << "\n";
REQUIRE(false);
}
}
SECTION("three points") {
try
{
fake_path path = { 0, 0, 1, 0, 2, 0 };
mapnik::extend_converter<fake_path> c(path, 1000);
double x, y;
REQUIRE(c.vertex(&x, &y) == mapnik::SEG_MOVETO);
REQUIRE(x == -1000);
REQUIRE(y == 0);
REQUIRE(c.vertex(&x, &y) == mapnik::SEG_LINETO);
REQUIRE(x == 1);
REQUIRE(y == 0);
REQUIRE(c.vertex(&x, &y) == mapnik::SEG_LINETO);
REQUIRE(x == 1002);
REQUIRE(y == 0);
REQUIRE(c.vertex(&x, &y) == mapnik::SEG_END);
}
catch (std::exception const& ex)
{
std::cerr << ex.what() << "\n";
REQUIRE(false);
}
}
SECTION("more points") {
try
{
fake_path path = { 0, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0 };
mapnik::extend_converter<fake_path> c(path, 1000);
double x, y;
REQUIRE(c.vertex(&x, &y) == mapnik::SEG_MOVETO);
REQUIRE(x == -1000);
REQUIRE(y == 0);
REQUIRE(c.vertex(&x, &y) == mapnik::SEG_LINETO);
REQUIRE(x == 1);
REQUIRE(y == 0);
REQUIRE(c.vertex(&x, &y) == mapnik::SEG_LINETO);
REQUIRE(x == 2);
REQUIRE(y == 0);
REQUIRE(c.vertex(&x, &y) == mapnik::SEG_LINETO);
REQUIRE(x == 3);
REQUIRE(y == 0);
REQUIRE(c.vertex(&x, &y) == mapnik::SEG_LINETO);
REQUIRE(x == 4);
REQUIRE(y == 0);
REQUIRE(c.vertex(&x, &y) == mapnik::SEG_LINETO);
REQUIRE(x == 1005);
REQUIRE(y == 0);
REQUIRE(c.vertex(&x, &y) == mapnik::SEG_END);
}
catch (std::exception const& ex)
{
std::cerr << ex.what() << "\n";
REQUIRE(false);
}
}
}
}

View file

@ -0,0 +1,93 @@
/*****************************************************************************
*
* 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
*
*****************************************************************************/
// mapnik
#include <mapnik/vertex.hpp>
// stl
#include <vector>
#include <tuple>
namespace detail
{
template <typename T>
struct fake_path
{
using coord_type = std::tuple<T, T, unsigned>;
using cont_type = std::vector<coord_type>;
cont_type vertices_;
typename cont_type::iterator itr_;
fake_path(std::initializer_list<T> l)
: fake_path(l.begin(), l.size())
{
}
fake_path(std::vector<T> const &v, bool make_invalid = false)
: fake_path(v.begin(), v.size(), make_invalid)
{
}
template <typename Itr>
fake_path(Itr itr, size_t sz, bool make_invalid = false)
{
size_t num_coords = sz >> 1;
vertices_.reserve(num_coords + (make_invalid ? 1 : 0));
if (make_invalid)
{
vertices_.push_back(std::make_tuple(0,0,mapnik::SEG_END));
}
for (size_t i = 0; i < num_coords; ++i)
{
T x = *itr++;
T y = *itr++;
unsigned cmd = (i == 0) ? mapnik::SEG_MOVETO : mapnik::SEG_LINETO;
vertices_.push_back(std::make_tuple(x, y, cmd));
}
itr_ = vertices_.begin();
}
unsigned vertex(T *x, T *y)
{
if (itr_ == vertices_.end())
{
return mapnik::SEG_END;
}
*x = std::get<0>(*itr_);
*y = std::get<1>(*itr_);
unsigned cmd = std::get<2>(*itr_);
++itr_;
return cmd;
}
void rewind(unsigned)
{
itr_ = vertices_.begin();
}
};
}
using fake_path = detail::fake_path<double>;

View file

@ -1,5 +1,6 @@
#include "catch.hpp"
#include "fake_path.hpp"
// mapnik
#include <mapnik/vertex_cache.hpp>
@ -7,53 +8,6 @@
// stl
#include <iostream>
#include <vector>
#include <tuple>
struct fake_path
{
using coord_type = std::tuple<double, double, unsigned>;
using cont_type = std::vector<coord_type>;
cont_type vertices_;
cont_type::iterator itr_;
fake_path(std::initializer_list<double> l)
: fake_path(l.begin(), l.size()) {
}
fake_path(std::vector<double> const &v)
: fake_path(v.begin(), v.size()) {
}
template <typename Itr>
fake_path(Itr itr, size_t sz) {
size_t num_coords = sz >> 1;
vertices_.reserve(num_coords);
for (size_t i = 0; i < num_coords; ++i) {
double x = *itr++;
double y = *itr++;
unsigned cmd = (i == 0) ? agg::path_cmd_move_to : agg::path_cmd_line_to;
vertices_.push_back(std::make_tuple(x, y, cmd));
}
itr_ = vertices_.begin();
}
unsigned vertex(double *x, double *y) {
if (itr_ == vertices_.end()) {
return agg::path_cmd_stop;
}
*x = std::get<0>(*itr_);
*y = std::get<1>(*itr_);
unsigned cmd = std::get<2>(*itr_);
++itr_;
return cmd;
}
void rewind(unsigned) {
itr_ = vertices_.begin();
}
};
double dist(mapnik::pixel_position const &a,
mapnik::pixel_position const &b)

View file

@ -1,66 +1,15 @@
#include "catch.hpp"
#include "fake_path.hpp"
// mapnik
#include <mapnik/vertex.hpp>
#include <mapnik/offset_converter.hpp>
// stl
#include <iostream>
#include <vector>
#include <tuple>
namespace offset_test {
struct fake_path
{
using coord_type = std::tuple<double, double, unsigned>;
using cont_type = std::vector<coord_type>;
cont_type vertices_;
cont_type::iterator itr_;
fake_path(std::initializer_list<double> l)
: fake_path(l.begin(), l.size()) {
}
fake_path(std::vector<double> const &v, bool make_invalid = false)
: fake_path(v.begin(), v.size(), make_invalid) {
}
template <typename Itr>
fake_path(Itr itr, size_t sz, bool make_invalid = false) {
size_t num_coords = sz >> 1;
vertices_.reserve(num_coords + (make_invalid ? 1 : 0));
if (make_invalid)
{
vertices_.push_back(std::make_tuple(0,0,mapnik::SEG_END));
}
for (size_t i = 0; i < num_coords; ++i) {
double x = *itr++;
double y = *itr++;
unsigned cmd = (i == 0) ? mapnik::SEG_MOVETO : mapnik::SEG_LINETO;
vertices_.push_back(std::make_tuple(x, y, cmd));
}
itr_ = vertices_.begin();
}
unsigned vertex(double *x, double *y) {
if (itr_ == vertices_.end()) {
return mapnik::SEG_END;
}
*x = std::get<0>(*itr_);
*y = std::get<1>(*itr_);
unsigned cmd = std::get<2>(*itr_);
++itr_;
return cmd;
}
void rewind(unsigned) {
itr_ = vertices_.begin();
}
};
static double DELTA_BUFF = 0.5;
double dist(double x0, double y0, double x1, double y1)