Merge branch 'master' into spirit-x3

This commit is contained in:
artemp 2016-01-12 15:13:17 +00:00
commit 7e5337b486
30 changed files with 2611 additions and 1345 deletions

View file

@ -85,7 +85,7 @@ script:
- if [[ ${COVERAGE} != true ]]; then
make bench;
fi
- if [[ ${TEST_RESULT} != 0 ]]; then exit $TEST_RESULT ; fi;
- if [[ ${TEST_RESULT:-0} != 0 ]]; then exit $TEST_RESULT ; fi;
- if [[ ${MASON_PUBLISH} == true ]]; then
./mason_latest.sh build;
./mason_latest.sh link;

View file

@ -294,6 +294,7 @@ opts.AddVariables(
# Note: setting DEBUG=True will override any custom OPTIMIZATION level
BoolVariable('DEBUG', 'Compile a debug version of Mapnik', 'False'),
BoolVariable('DEBUG_UNDEFINED', 'Compile a version of Mapnik using clang/llvm undefined behavior asserts', 'False'),
BoolVariable('DEBUG_SANITIZE', 'Compile a version of Mapnik using clang/llvm address sanitation', 'False'),
ListVariable('INPUT_PLUGINS','Input drivers to include',DEFAULT_PLUGINS,PLUGINS.keys()),
('WARNING_CXXFLAGS', 'Compiler flags you can set to reduce warning levels which are placed after -Wall.', ''),
@ -1785,7 +1786,7 @@ if not preconfigured:
# Common flags for g++/clang++ CXX compiler.
# TODO: clean up code more to make -Wextra -Wsign-compare -Wsign-conversion -Wconversion viable
common_cxx_flags = '-Wall %s %s -ftemplate-depth-300 -Wsign-compare -Wshadow ' % (env['WARNING_CXXFLAGS'], pthread)
common_cxx_flags = '-fvisibility=hidden -fvisibility-inlines-hidden -Wall %s %s -ftemplate-depth-300 -Wsign-compare -Wshadow ' % (env['WARNING_CXXFLAGS'], pthread)
if 'clang++' in env['CXX']:
common_cxx_flags += ' -Wno-unsequenced '
@ -1793,12 +1794,15 @@ if not preconfigured:
if env['DEBUG']:
env.Append(CXXFLAGS = common_cxx_flags + '-O0')
else:
# TODO - add back -fvisibility-inlines-hidden
# https://github.com/mapnik/mapnik/issues/1863
env.Append(CXXFLAGS = common_cxx_flags + '-O%s' % (env['OPTIMIZATION']))
if env['DEBUG_UNDEFINED']:
env.Append(CXXFLAGS = '-fsanitize=undefined-trap -fsanitize-undefined-trap-on-error -ftrapv -fwrapv')
if env['DEBUG_SANITIZE']:
env.Append(CXXFLAGS = ['-fsanitize=address'])
env.Append(LINKFLAGS = ['-fsanitize=address'])
# if requested, sort LIBPATH and CPPPATH one last time before saving...
if env['PRIORITIZE_LINKING']:
conf.prioritize_paths(silent=True)

33
bootstrap.sh Executable file → Normal file
View file

@ -51,26 +51,28 @@ function install() {
ICU_VERSION="55.1"
function install_mason_deps() {
install jpeg_turbo 1.4.0 libjpeg &
install libpng 1.6.20 libpng &
install libtiff 4.0.4beta libtiff &
install libpq 9.4.1 &
install sqlite 3.8.8.3 libsqlite3 &
install expat 2.1.0 libexpat &
wait
install icu ${ICU_VERSION} &
install proj 4.8.0 libproj &
install pixman 0.32.6 libpixman-1 &
install cairo 1.14.2 libcairo &
install protobuf 2.6.1 &
# technically protobuf is not a mapnik core dep, but installing
# here by default helps make mapnik-vector-tile builds easier
wait
install webp 0.4.2 libwebp &
install gdal 1.11.2 libgdal &
install boost 1.59.0 &
install boost_liball 1.59.0 &
install freetype 2.6 libfreetype &
install harfbuzz 0.9.40 libharfbuzz &
install jpeg_turbo 1.4.0 libjpeg &
install libpng 1.6.17 libpng &
install webp 0.4.2 libwebp &
install icu ${ICU_VERSION} &
install proj 4.8.0 libproj &
install libtiff 4.0.4beta libtiff &
install libpq 9.4.0 &
install sqlite 3.8.8.1 libsqlite3 &
install expat 2.1.0 libexpat &
install pixman 0.32.6 libpixman-1 &
install cairo 1.14.2 libcairo &
install protobuf 2.6.1 &
install harfbuzz 0.9.41 libharfbuzz &
wait
# technically protobuf is not a mapnik core dep, but installing
# here by default helps make mapnik-vector-tile builds easier
}
MASON_LINKED_ABS=$(pwd)/mason_packages/.link
@ -89,7 +91,6 @@ function make_config() {
echo "
CXX = '$CXX'
CC = '$CC'
CUSTOM_CXXFLAGS = '-fvisibility=hidden -fvisibility-inlines-hidden -DU_CHARSET_IS_UTF8=1'
RUNTIME_LINK = 'static'
INPUT_PLUGINS = 'all'
PATH = '${MASON_LINKED_REL}/bin'

View file

@ -2,8 +2,8 @@
// Anti-Grain Geometry - Version 2.4
// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
//
// Permission to copy, use, modify, sell and distribute this software
// is granted provided this copyright notice appears in all copies.
// Permission to copy, use, modify, sell and distribute this software
// is granted provided this copyright notice appears in all copies.
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
@ -21,6 +21,7 @@
#include "agg_basics.h"
#include "agg_vertex_sequence.h"
#include <mapnik/config.hpp>
namespace agg
{
@ -29,7 +30,7 @@ namespace agg
//
// See Implementation agg_vcgen_dash.cpp
//
class vcgen_dash
class MAPNIK_DECL vcgen_dash
{
enum max_dashes_e
{

View file

@ -90,16 +90,16 @@ struct evaluate
return geom.value<value_type,feature_type>(feature_);
}
value_type operator() (binary_node<tags::logical_and> const & x) const
value_type operator() (binary_node<tags::logical_and> const& x) const
{
return (util::apply_visitor(*this, x.left).to_bool())
&& (util::apply_visitor(*this, x.right).to_bool());
}
value_type operator() (binary_node<tags::logical_or> const & x) const
value_type operator() (binary_node<tags::logical_or> const& x) const
{
return (util::apply_visitor(*this,x.left).to_bool())
|| (util::apply_visitor(*this,x.right).to_bool());
return (util::apply_visitor(*this, x.left).to_bool())
|| (util::apply_visitor(*this, x.right).to_bool());
}
template <typename Tag>

View file

@ -36,7 +36,7 @@ geometry_empty reproject_internal(geometry_empty const&, proj_transform const&,
}
template <typename T>
point<T> reproject_internal(point<T> const & p, proj_transform const& proj_trans, unsigned int & n_err)
point<T> reproject_internal(point<T> const& p, proj_transform const& proj_trans, unsigned int & n_err)
{
point<T> new_p(p);
if (!proj_trans.forward(new_p))
@ -47,7 +47,7 @@ point<T> reproject_internal(point<T> const & p, proj_transform const& proj_trans
}
template <typename T>
line_string<T> reproject_internal(line_string<T> const & ls, proj_transform const& proj_trans, unsigned int & n_err)
line_string<T> reproject_internal(line_string<T> const& ls, proj_transform const& proj_trans, unsigned int & n_err)
{
line_string<T> new_ls(ls);
unsigned int err = proj_trans.forward(new_ls);
@ -59,7 +59,7 @@ line_string<T> reproject_internal(line_string<T> const & ls, proj_transform cons
}
template <typename T>
polygon<T> reproject_internal(polygon<T> const & poly, proj_transform const& proj_trans, unsigned int & n_err)
polygon<T> reproject_internal(polygon<T> const& poly, proj_transform const& proj_trans, unsigned int & n_err)
{
polygon<T> new_poly;
linear_ring<T> new_ext(poly.exterior_ring);
@ -171,7 +171,8 @@ geometry_collection<T> reproject_internal(geometry_collection<T> const & c, proj
}
template <typename T>
struct geom_reproj_copy_visitor {
struct geom_reproj_copy_visitor
{
geom_reproj_copy_visitor(proj_transform const & proj_trans, unsigned int & n_err)
: proj_trans_(proj_trans),

View file

@ -63,10 +63,10 @@ struct set_position_impl
struct push_position_impl
{
using result_type = void;
template <typename T0,typename T1>
template <typename T0, typename T1>
result_type operator() (T0 & coords, T1 const& pos) const
{
if (pos) coords.push_back(*pos);
if (pos) coords.emplace_back(*pos);
}
};

View file

@ -108,7 +108,7 @@ public:
marker_svg(marker_svg && rhs) noexcept
: vector_data_(rhs.vector_data_) {}
box2d<double> bounding_box() const
inline box2d<double> bounding_box() const
{
return vector_data_->bounding_box();
}
@ -122,11 +122,15 @@ public:
return vector_data_->bounding_box().height();
}
mapnik::svg_path_ptr get_data() const
inline mapnik::svg_path_ptr get_data() const
{
return vector_data_;
}
inline std::tuple<double,double> dimensions() const
{
return std::make_tuple(vector_data_->width(), vector_data_->height());
}
private:
mapnik::svg_path_ptr vector_data_;
@ -136,7 +140,7 @@ struct marker_null
{
marker_null() = default;
public:
box2d<double> bounding_box() const
inline box2d<double> bounding_box() const
{
return box2d<double>();
}

View file

@ -224,7 +224,15 @@ public:
attr.stroke_color.opacity(a * s.opacity());
attr.stroke_flag = true;
}
void dash_array(dash_array && dash)
{
path_attributes& attr = cur_attr();
attr.dash = std::move(dash);
}
void dash_offset(double offset)
{
cur_attr().dash_offset = offset;
}
void even_odd(bool flag)
{
cur_attr().even_odd_flag = flag;

View file

@ -30,6 +30,7 @@
// mapnik
#include <mapnik/gradient.hpp>
#include <mapnik/symbolizer_base.hpp> // dash_array
namespace mapnik {
namespace svg {
@ -56,7 +57,8 @@ struct path_attributes
bool even_odd_flag;
bool visibility_flag;
bool display_flag;
dash_array dash;
double dash_offset;
// Empty constructor
path_attributes() :
fill_gradient(),
@ -78,9 +80,10 @@ struct path_attributes
stroke_none(false),
even_odd_flag(false),
visibility_flag(true),
display_flag(true)
{
}
display_flag(true),
dash(),
dash_offset(0.0)
{}
// Copy constructor
path_attributes(path_attributes const& attr)
@ -103,7 +106,9 @@ struct path_attributes
stroke_none(attr.stroke_none),
even_odd_flag(attr.even_odd_flag),
visibility_flag(attr.visibility_flag),
display_flag(attr.display_flag)
display_flag(attr.display_flag),
dash(attr.dash),
dash_offset(attr.dash_offset)
{}
// Copy constructor with new index value
path_attributes(path_attributes const& attr, unsigned idx)
@ -126,7 +131,9 @@ struct path_attributes
stroke_none(attr.stroke_none),
even_odd_flag(attr.even_odd_flag),
visibility_flag(attr.visibility_flag),
display_flag(attr.display_flag)
display_flag(attr.display_flag),
dash(attr.dash),
dash_offset(attr.dash_offset)
{}
};

View file

@ -73,46 +73,29 @@ namespace mapnik { namespace svg {
>> *(-lit(',') >> coord [ line_to_(_1,_a) ] ); // *line_to
H = (lit('H')[_a = false] | lit('h')[_a = true])
>> +double_[ hline_to_(_1,_a) ] ; // +hline_to
>> (double_[ hline_to_(_1,_a) ] % -lit(',')) ; // +hline_to
V = (lit('V')[_a = false] | lit('v')[_a = true])
>> +double_ [ vline_to_(_1,_a) ]; // +vline_to
>> (double_ [ vline_to_(_1,_a) ] % -lit(',')); // +vline_to
L = (lit('L')[_a = false] | lit('l')[_a = true])
>> +coord [ line_to_(_1,_a) ]; // +line_to
>> (coord [ line_to_(_1,_a) ] % -lit(',')); // +line_to
C = (lit('C')[_a = false] | lit('c')[_a = true])
>> +(coord
>> -lit(',')
>> coord
>> -lit(',')
>> coord) [ curve4_(_1,_2,_3,_a) ]; // +curve4
>> ((coord >> -lit(',') >> coord >> -lit(',') >> coord) [ curve4_(_1,_2,_3,_a) ] % -lit(',')); // +curve4
S = (lit('S')[_a = false] | lit('s')[_a = true])
>> +(coord
>> -lit(',')
>> coord) [ curve4_smooth_(_1,_2,_a) ]; // +curve4_smooth (smooth curveto)
>> ((coord >> -lit(',') >> coord) [ curve4_smooth_(_1,_2,_a) ] % -lit(',')); // +curve4_smooth (smooth curveto)
Q = (lit('Q')[_a = false] | lit('q')[_a = true])
>> +(coord
>> -lit(',')
>> coord) [ curve3_(_1,_2,_a) ]; // +curve3 (quadratic-bezier-curveto)
>> ((coord >> -lit(',') >> coord) [ curve3_(_1,_2,_a) ] % -lit(',')); // +curve3 (quadratic-bezier-curveto)
T = (lit('T')[_a = false] | lit('t')[_a = true])
>> +(coord ) [ curve3_smooth_(_1,_a) ]; // +curve3_smooth (smooth-quadratic-bezier-curveto)
>> ((coord ) [ curve3_smooth_(_1,_a) ] % -lit(',')); // +curve3_smooth (smooth-quadratic-bezier-curveto)
A = (lit('A')[_a = false] | lit('a')[_a = true])
>> +(coord
>> -lit(',')
>> double_
>> -lit(',')
>> int_
>> -lit(',')
>> int_
>> -lit(',')
>> coord) [arc_to_(_1,_2,_3,_4,_5,_a)]; // arc_to;
>> ((coord >> -lit(',') >> double_ >> -lit(',')
>> int_ >> -lit(',') >> int_ >> -lit(',') >> coord) [arc_to_(_1,_2,_3,_4,_5,_a)] % -lit(',')); // arc_to;
Z = no_case[lit('z')] [close_()]; // close path

View file

@ -43,6 +43,7 @@
#include "agg_conv_stroke.h"
#include "agg_conv_contour.h"
#include "agg_conv_curve.h"
#include "agg_conv_dash.h"
#include "agg_color_rgba.h"
#include "agg_bounding_rect.h"
#include "agg_rendering_buffer.h"
@ -60,12 +61,10 @@
namespace mapnik {
namespace svg {
// Arbitrary linear gradient specified by two control points. Gradient
// value is taken as the normalised distance along the line segment
// represented by the two points.
/**
* Arbitrary linear gradient specified by two control points. Gradient
* value is taken as the normalised distance along the line segment
* represented by the two points.
*/
class linear_gradient_from_segment
{
public:
@ -105,19 +104,28 @@ template <typename VertexSource, typename AttributeSource, typename ScanlineRend
class svg_renderer_agg : util::noncopyable
{
public:
using curved_type = agg::conv_curve<VertexSource> ;
using curved_stroked_type = agg::conv_stroke<curved_type> ;
using curved_type = agg::conv_curve<VertexSource>;
// stroke
using curved_stroked_type = agg::conv_stroke<curved_type>;
using curved_stroked_trans_type = agg::conv_transform<curved_stroked_type>;
using curved_trans_type = agg::conv_transform<curved_type> ;
using curved_trans_contour_type = agg::conv_contour<curved_trans_type> ;
using renderer_base = agg::renderer_base<PixelFormat> ;
using vertex_source_type = VertexSource ;
using attribute_source_type = AttributeSource ;
// stroke dash-array
using curved_dashed_type = agg::conv_dash<curved_type>;
using curved_dashed_stroked_type = agg::conv_stroke<curved_dashed_type>;
using curved_dashed_stroked_trans_type = agg::conv_transform<curved_dashed_stroked_type>;
// fill
using curved_trans_type = agg::conv_transform<curved_type>;
using curved_trans_contour_type = agg::conv_contour<curved_trans_type>;
// renderer
using renderer_base = agg::renderer_base<PixelFormat>;
using vertex_source_type = VertexSource;
using attribute_source_type = AttributeSource;
svg_renderer_agg(VertexSource & source, AttributeSource const& attributes)
: source_(source),
curved_(source_),
curved_dashed_(curved_),
curved_stroked_(curved_),
curved_dashed_stroked_(curved_dashed_),
attributes_(attributes) {}
template <typename Rasterizer, typename Scanline, typename Renderer>
@ -191,11 +199,11 @@ public:
// scale everything up since agg turns things into integers a bit too soon
int scaleup=255;
radius*=scaleup;
x1*=scaleup;
y1*=scaleup;
x2*=scaleup;
y2*=scaleup;
radius *= scaleup;
x1 *= scaleup;
y1 *= scaleup;
x2 *= scaleup;
y2 *= scaleup;
transform.scale(scaleup,scaleup);
interpolator_type span_interpolator(transform);
@ -217,10 +225,10 @@ public:
color_func_type>;
// scale everything up since agg turns things into integers a bit too soon
int scaleup=255;
x1*=scaleup;
y1*=scaleup;
x2*=scaleup;
y2*=scaleup;
x1 *= scaleup;
y1 *= scaleup;
x2 *= scaleup;
y2 *= scaleup;
transform.scale(scaleup,scaleup);
@ -247,10 +255,11 @@ public:
{
using namespace agg;
trans_affine transform;
curved_stroked_trans_type curved_stroked_trans(curved_stroked_,transform);
curved_trans_type curved_trans(curved_,transform);
curved_dashed_stroked_trans_type curved_dashed_stroked_trans(curved_dashed_stroked_, transform);
curved_trans_type curved_trans(curved_,transform);
curved_trans_contour_type curved_trans_contour(curved_trans);
curved_trans_contour.auto_detect_orientation(true);
@ -274,7 +283,6 @@ public:
if (attr.fill_flag || attr.fill_gradient.get_gradient_type() != NO_GRADIENT)
{
ras.reset();
// https://github.com/mapnik/mapnik/issues/1129
if(std::fabs(curved_trans_contour.width()) <= 1)
{
@ -288,7 +296,8 @@ public:
if(attr.fill_gradient.get_gradient_type() != NO_GRADIENT)
{
render_gradient(ras, sl, ren, attr.fill_gradient, transform, attr.fill_opacity * attr.opacity * opacity, symbol_bbox, curved_trans, attr.index);
render_gradient(ras, sl, ren, attr.fill_gradient, transform,
attr.fill_opacity * attr.opacity * opacity, symbol_bbox, curved_trans, attr.index);
}
else
{
@ -304,37 +313,79 @@ public:
if (attr.stroke_flag || attr.stroke_gradient.get_gradient_type() != NO_GRADIENT)
{
curved_stroked_.width(attr.stroke_width);
//m_curved_stroked.line_join((attr.line_join == miter_join) ? miter_join_round : attr.line_join);
curved_stroked_.line_join(attr.line_join);
curved_stroked_.line_cap(attr.line_cap);
curved_stroked_.miter_limit(attr.miter_limit);
curved_stroked_.inner_join(inner_round);
curved_stroked_.approximation_scale(scl);
// If the *visual* line width is considerable we
// turn on processing of curve cusps.
//---------------------
if(attr.stroke_width * scl > 1.0)
if (attr.dash.size() > 0)
{
curved_.angle_tolerance(0.2);
}
ras.reset();
ras.add_path(curved_stroked_trans, attr.index);
curved_dashed_stroked_.width(attr.stroke_width);
curved_dashed_stroked_.line_join(attr.line_join);
curved_dashed_stroked_.line_cap(attr.line_cap);
curved_dashed_stroked_.miter_limit(attr.miter_limit);
curved_dashed_stroked_.inner_join(inner_round);
curved_dashed_stroked_.approximation_scale(scl);
if(attr.stroke_gradient.get_gradient_type() != NO_GRADIENT)
{
render_gradient(ras, sl, ren, attr.stroke_gradient, transform, attr.stroke_opacity * attr.opacity * opacity, symbol_bbox, curved_trans, attr.index);
// If the *visual* line width is considerable we
// turn on processing of curve cups.
//---------------------
if (attr.stroke_width * scl > 1.0)
{
curved_.angle_tolerance(0.2);
}
ras.reset();
curved_dashed_.remove_all_dashes();
for (auto d : attr.dash)
{
curved_dashed_.add_dash(std::get<0>(d),std::get<1>(d));
}
curved_dashed_.dash_start(attr.dash_offset);
ras.add_path(curved_dashed_stroked_trans, attr.index);
if (attr.stroke_gradient.get_gradient_type() != NO_GRADIENT)
{
render_gradient(ras, sl, ren, attr.stroke_gradient, transform,
attr.stroke_opacity * attr.opacity * opacity, symbol_bbox, curved_trans, attr.index);
}
else
{
ras.filling_rule(fill_non_zero);
color = attr.stroke_color;
color.opacity(color.opacity() * attr.stroke_opacity * attr.opacity * opacity);
ScanlineRenderer ren_s(ren);
color.premultiply();
ren_s.color(color);
render_scanlines(ras, sl, ren_s);
}
}
else
{
ras.filling_rule(fill_non_zero);
color = attr.stroke_color;
color.opacity(color.opacity() * attr.stroke_opacity * attr.opacity * opacity);
ScanlineRenderer ren_s(ren);
color.premultiply();
ren_s.color(color);
render_scanlines(ras, sl, ren_s);
curved_stroked_.width(attr.stroke_width);
curved_stroked_.line_join(attr.line_join);
curved_stroked_.line_cap(attr.line_cap);
curved_stroked_.miter_limit(attr.miter_limit);
curved_stroked_.inner_join(inner_round);
curved_stroked_.approximation_scale(scl);
// If the *visual* line width is considerable we
// turn on processing of curve cups.
//---------------------
if (attr.stroke_width * scl > 1.0)
{
curved_.angle_tolerance(0.2);
}
ras.reset();
ras.add_path(curved_stroked_trans, attr.index);
if (attr.stroke_gradient.get_gradient_type() != NO_GRADIENT)
{
render_gradient(ras, sl, ren, attr.stroke_gradient, transform,
attr.stroke_opacity * attr.opacity * opacity, symbol_bbox, curved_trans, attr.index);
}
else
{
ras.filling_rule(fill_non_zero);
color = attr.stroke_color;
color.opacity(color.opacity() * attr.stroke_opacity * attr.opacity * opacity);
ScanlineRenderer ren_s(ren);
color.premultiply();
ren_s.color(color);
render_scanlines(ras, sl, ren_s);
}
}
}
}
@ -409,7 +460,7 @@ public:
// If the *visual* line width is considerable we
// turn on processing of curve cusps.
//---------------------
if(attr.stroke_width * scl > 1.0)
if (attr.stroke_width * scl > 1.0)
{
curved_.angle_tolerance(0.2);
}
@ -431,7 +482,9 @@ private:
VertexSource & source_;
curved_type curved_;
curved_dashed_type curved_dashed_;
curved_stroked_type curved_stroked_;
curved_dashed_stroked_type curved_dashed_stroked_;
AttributeSource const& attributes_;
};

View file

@ -317,12 +317,8 @@ struct evaluate_expression_wrapper<mapnik::dash_array>
mapnik::value_type val = util::apply_visitor(mapnik::evaluate<T2,mapnik::value_type,T3>(feature,vars), expr);
if (val.is_null()) return dash_array();
dash_array dash;
std::vector<double> buf;
std::string str = val.to_string();
if (util::parse_dasharray(str,buf))
{
util::add_dashes(buf,dash);
}
util::parse_dasharray(str,dash);
return dash;
}
};

View file

@ -404,9 +404,8 @@ struct set_symbolizer_property_impl<Symbolizer,dash_array,false>
boost::optional<std::string> str = node.get_opt_attr<std::string>(name);
if (str)
{
std::vector<double> buf;
dash_array dash;
if (util::parse_dasharray(*str,buf) && util::add_dashes(buf,dash))
if (util::parse_dasharray(*str,dash))
{
put(sym,key,dash);
}

View file

@ -23,32 +23,13 @@
#ifndef MAPNIK_UTIL_DASHARRAY_PARSER_HPP
#define MAPNIK_UTIL_DASHARRAY_PARSER_HPP
#include <mapnik/symbolizer_base.hpp>
#include <vector>
#include <string>
namespace mapnik { namespace util {
bool parse_dasharray(std::string const& value, std::vector<double>& dasharray);
inline bool add_dashes(std::vector<double> & buf, std::vector<std::pair<double,double> > & dash)
{
if (buf.empty()) return false;
size_t size = buf.size();
if (size % 2 == 1)
{
buf.insert(buf.end(),buf.begin(),buf.end());
}
std::vector<double>::const_iterator pos = buf.begin();
while (pos != buf.end())
{
if (*pos > 0.0 || *(pos+1) > 0.0) // avoid both dash and gap eq 0.0
{
dash.emplace_back(*pos,*(pos + 1));
}
pos +=2;
}
return !buf.empty();
}
bool parse_dasharray(std::string const& value, dash_array & dash);
}}

View file

@ -283,29 +283,20 @@ namespace detail {
template <typename T>
struct unwrapper
{
T const& operator() (T const& obj) const
{
return obj;
}
T& operator() (T & obj) const
{
return obj;
}
static T const& apply_const(T const& obj) {return obj;}
static T& apply(T & obj) {return obj;}
};
template <typename T>
struct unwrapper<recursive_wrapper<T>>
{
auto operator() (recursive_wrapper<T> const& obj) const
static auto apply_const(recursive_wrapper<T> const& obj)
-> typename recursive_wrapper<T>::type const&
{
return obj.get();
}
auto operator() (recursive_wrapper<T> & obj) const
-> typename recursive_wrapper<T>::type &
static auto apply(recursive_wrapper<T> & obj)
-> typename recursive_wrapper<T>::type&
{
return obj.get();
}
@ -314,8 +305,13 @@ struct unwrapper<recursive_wrapper<T>>
template <typename T>
struct unwrapper<std::reference_wrapper<T>>
{
auto operator() (std::reference_wrapper<T> const& obj) const
-> typename recursive_wrapper<T>::type const&
static auto apply_const(std::reference_wrapper<T> const& obj)
-> typename std::reference_wrapper<T>::type const&
{
return obj.get();
}
static auto apply(std::reference_wrapper<T> & obj)
-> typename std::reference_wrapper<T>::type&
{
return obj.get();
}
@ -332,7 +328,7 @@ struct dispatcher<F, V, R, T, Types...>
{
if (v.get_type_index() == sizeof...(Types))
{
return f(unwrapper<T>()(v. template get<T>()));
return f(unwrapper<T>::apply_const(v. template get<T>()));
}
else
{
@ -344,7 +340,7 @@ struct dispatcher<F, V, R, T, Types...>
{
if (v.get_type_index() == sizeof...(Types))
{
return f(unwrapper<T>()(v. template get<T>()));
return f(unwrapper<T>::apply(v. template get<T>()));
}
else
{
@ -380,8 +376,8 @@ struct binary_dispatcher_rhs<F, V, R, T0, T1, Types...>
{
if (rhs.get_type_index() == sizeof...(Types)) // call binary functor
{
return f(unwrapper<T0>()(lhs. template get<T0>()),
unwrapper<T1>()(rhs. template get<T1>()));
return f(unwrapper<T0>::apply_const(lhs. template get<T0>()),
unwrapper<T1>::apply_const(rhs. template get<T1>()));
}
else
{
@ -393,8 +389,8 @@ struct binary_dispatcher_rhs<F, V, R, T0, T1, Types...>
{
if (rhs.get_type_index() == sizeof...(Types)) // call binary functor
{
return f(unwrapper<T0>()(lhs. template get<T0>()),
unwrapper<T1>()(rhs. template get<T1>()));
return f(unwrapper<T0>::apply(lhs. template get<T0>()),
unwrapper<T1>::apply(rhs. template get<T1>()));
}
else
{
@ -430,7 +426,8 @@ struct binary_dispatcher_lhs<F, V, R, T0, T1, Types...>
{
if (lhs.get_type_index() == sizeof...(Types)) // call binary functor
{
return f(lhs. template get<T1>(), rhs. template get<T0>());
return f(unwrapper<T1>::apply_const(lhs. template get<T1>()),
unwrapper<T0>::apply_const(rhs. template get<T0>()));
}
else
{
@ -442,7 +439,8 @@ struct binary_dispatcher_lhs<F, V, R, T0, T1, Types...>
{
if (lhs.get_type_index() == sizeof...(Types)) // call binary functor
{
return f(lhs. template get<T1>(), rhs. template get<T0>());
return f(unwrapper<T1>::apply(lhs. template get<T1>()),
unwrapper<T0>::apply(rhs. template get<T0>()));
}
else
{
@ -480,7 +478,8 @@ struct binary_dispatcher<F, V, R, T, Types...>
{
if (v0.get_type_index() == v1.get_type_index())
{
return f(v0. template get<T>(), v1. template get<T>()); // call binary functor
return f(unwrapper<T>::apply_const(v0. template get<T>()),
unwrapper<T>::apply_const(v1. template get<T>())); // call binary functor
}
else
{
@ -500,7 +499,8 @@ struct binary_dispatcher<F, V, R, T, Types...>
{
if (v0.get_type_index() == v1.get_type_index())
{
return f(v0. template get<T>(), v1. template get<T>()); // call binary functor
return f(unwrapper<T>::apply(v0. template get<T>()),
unwrapper<T>::apply(v1. template get<T>())); // call binary functor
}
else
{

View file

@ -60,8 +60,7 @@ if env['PLUGIN_LINKING'] == 'shared':
SHLIBPREFIX='',
SHLIBSUFFIX='.input',
source=plugin_sources,
LIBS=libraries,
LINKFLAGS=env['CUSTOM_LDFLAGS'])
LIBS=libraries)
# if the plugin links to libmapnik ensure it is built first
Depends(TARGET, env.subst('../../../src/%s' % env['MAPNIK_LIB_NAME']))

View file

@ -28,27 +28,6 @@ ECHO ========
SET PATH=C:\Python27;%PATH%
SET PATH=C:\Program Files\7-Zip;%PATH%
:: *nix style find command comes with git:
ECHO checking for unix style 'find'
find %USERPROFILE% -name "*.blabla"
IF %ERRORLEVEL% EQU 0 GOTO NIX_FIND_FOUND
IF DEFINED GIT_INSTALL_ROOT SET TEMP_GIT_DIR=%GIT_INSTALL_ROOT%&& GOTO TEST_FIND_AGAIN
IF EXIST "C:\Program Files (x86)\Git" SET TEMP_GIT_DIR=C:\Program Files (x86)\Git&& GOTO TEST_FIND_AGAIN
IF EXIST "C:\Program Files\Git" SET TEMP_GIT_DIR=C:\Program Files\Git&& GOTO TEST_FIND_AGAIN
:TEST_FIND_AGAIN
SET PATH=%TEMP_GIT_DIR%\bin;%PATH%
SET PATH=%TEMP_GIT_DIR%\usr\bin;%PATH%
ECHO %PATH%
::check again
find %USERPROFILE% -name "*.blabla"
IF %ERRORLEVEL% NEQ 0 (ECHO unix style find not found && GOTO ERROR)
:NIX_FIND_FOUND
ECHO find was found
::cloning mapnik-gyp
if EXIST mapnik-gyp ECHO mapnik-gyp already cloned && GOTO MAPNIK_GYP_ALREADY_HERE
CALL git clone https://github.com/mapnik/mapnik-gyp.git

View file

@ -36,7 +36,29 @@ namespace mapnik {
namespace util {
bool parse_dasharray(std::string const& value, std::vector<double>& dasharray)
namespace {
inline bool setup_dashes(std::vector<double> & buf, dash_array & dash)
{
if (buf.empty()) return false;
size_t size = buf.size();
if (size % 2 == 1)
{
buf.insert(buf.end(),buf.begin(),buf.end());
}
std::vector<double>::const_iterator pos = buf.begin();
while (pos != buf.end())
{
if (*pos > 0.0 || *(pos+1) > 0.0) // avoid both dash and gap eq 0.0
{
dash.emplace_back(*pos,*(pos + 1));
}
pos +=2;
}
return !buf.empty();
}
}
bool parse_dasharray(std::string const& value, dash_array & dash)
{
using namespace boost::spirit;
qi::double_type double_;
@ -49,18 +71,19 @@ bool parse_dasharray(std::string const& value, std::vector<double>& dasharray)
// dasharray ::= (length | percentage) (comma-wsp dasharray)?
// no support for 'percentage' as viewport is unknown at load_map
//
std::vector<double> buf;
auto first = value.begin();
auto last = value.end();
bool r = qi::phrase_parse(first, last,
(double_[boost::phoenix::push_back(boost::phoenix::ref(dasharray), _1)] %
(double_[boost::phoenix::push_back(boost::phoenix::ref(buf), _1)] %
no_skip[char_(", ")]
| lit("none")),
space);
if (first != last)
if (r && first == last)
{
return false;
return setup_dashes(buf, dash);
}
return r;
return false;
}
} // end namespace util

View file

@ -29,6 +29,7 @@
#include <mapnik/svg/svg_parser_exception.hpp>
#include <mapnik/util/file_io.hpp>
#include <mapnik/util/utf_conv_win.hpp>
#include <mapnik/util/dasharray_parser.hpp>
#include "agg_ellipse.h"
#include "agg_rounded_rect.h"
#include "agg_span_gradient.h"
@ -439,7 +440,19 @@ void parse_attr(svg_parser & parser, char const* name, char const* value )
{
parser.path_.miter_limit(parse_double(parser.error_messages_,value));
}
else if (std::strcmp(name,"stroke-dasharray") == 0)
{
dash_array dash;
if (util::parse_dasharray(value, dash))
{
parser.path_.dash_array(std::move(dash));
}
}
else if (std::strcmp(name,"stroke-dashoffset") == 0)
{
double offset = parse_double(parser.error_messages_, value);
parser.path_.dash_offset(offset);
}
else if(std::strcmp(name, "opacity") == 0)
{
double opacity = parse_double(parser.error_messages_, value);

View file

@ -44,9 +44,9 @@ namespace mapnik { namespace svg {
svg_path_grammar<iterator_type,skip_type,PathType> g(p);
iterator_type first = wkt;
iterator_type last = wkt + std::strlen(wkt);
return qi::phrase_parse(first, last, g, skip_type());
bool status = qi::phrase_parse(first, last, g, skip_type());
return (status && (first == last));
}
template bool MAPNIK_DECL parse_path<svg_converter_type>(const char*, svg_converter_type&);
template bool parse_path<svg_converter_type>(const char*, svg_converter_type&);
}}
}}

View file

@ -598,13 +598,9 @@ image_any tiff_reader<T>::read(unsigned x, unsigned y, unsigned width, unsigned
}
template <typename T>
void tiff_reader<T>::read_generic(std::size_t, std::size_t, image_rgba8& image)
void tiff_reader<T>::read_generic(std::size_t, std::size_t, image_rgba8&)
{
TIFF* tif = open(stream_);
if (tif)
{
throw std::runtime_error("tiff_reader: TODO - tiff is not stripped or tiled");
}
throw std::runtime_error("tiff_reader: TODO - tiff is not stripped or tiled");
}
template <typename T>

File diff suppressed because it is too large Load diff

@ -1 +1 @@
Subproject commit 972b039bfc5a5e189f68852c22df717a30c1adf7
Subproject commit af88691110b39c75f97140ab4b608b258e53ad51

View file

@ -68,6 +68,10 @@ mapnik::datasource_ptr get_csv_ds(std::string const& file_name, bool strict = tr
mapnik::parameters params;
params["type"] = std::string("csv");
params["file"] = file_name;
if (!base.empty())
{
params["base"] = base;
}
params["strict"] = mapnik::value_bool(strict);
auto ds = mapnik::datasource_cache::instance().create(params);
// require a non-null pointer returned
@ -288,7 +292,7 @@ TEST_CASE("csv") {
INFO(ret_posix);
CHECK(mapnik::util::exists(filepath + ".index"));
}
auto ds = get_csv_ds(filepath,true,base);
auto ds = get_csv_ds(filename,true,base);
CHECK(ds->type() == mapnik::datasource::datasource_t::Vector);
auto fields = ds->get_descriptor().get_descriptors();
require_field_names(fields, {"Precinct", "Phone", "Address", "City", "geo_longitude", "geo_latitude", "geo_accuracy"});

View file

@ -29,24 +29,22 @@
#include <mapnik/util/fs.hpp>
/*
Compile and run just this test:
clang++ -o test-postgis -g -I./test/ test/unit/run.cpp test/unit/datasource/postgis.cpp `mapnik-config --all-flags` && ./test-postgis -d yes
*/
namespace {
#include <boost/optional/optional_io.hpp>
int run(std::string const& command, bool silent = false)
int run(std::string const& command, bool okay_to_fail = false)
{
std::string cmd;
if (std::getenv("DYLD_LIBRARY_PATH") != nullptr)
{
cmd += std::string("DYLD_LIBRARY_PATH=") + std::getenv("DYLD_LIBRARY_PATH") + " ";
cmd += std::string("DYLD_LIBRARY_PATH=") + std::getenv("DYLD_LIBRARY_PATH") + " && ";
}
cmd += command;
if (silent)
// silence output unless MAPNIK_TEST_DEBUG is defined
if (std::getenv("MAPNIK_TEST_DEBUG") == nullptr)
{
#ifndef _WINDOWS
cmd += " 2>/dev/null";
@ -54,41 +52,243 @@ int run(std::string const& command, bool silent = false)
cmd += " 2> nul";
#endif
}
else
{
std::clog << "Running " << cmd << "\n";
}
bool worked = (std::system(cmd.c_str()) == 0);
if (silent == true) return true;
if (okay_to_fail == true) return true;
return worked;
}
std::string dbname("mapnik-tmp-postgis-test-db");
TEST_CASE("postgis") {
SECTION("Postgis data initialization")
{
REQUIRE(run("dropdb mapnik-tmp-postgis-test-db",true));
REQUIRE(run("createdb -T template_postgis mapnik-tmp-postgis-test-db"));
std::stringstream cmd;
cmd << "psql -q mapnik-tmp-postgis-test-db -f ./test/data/sql/table1.sql";
REQUIRE(run(cmd.str()));
//don't add 'true' here, to get error message, when drop fails. If it works nothing is output
REQUIRE(run("dropdb --if-exists " + dbname));
REQUIRE(run("createdb -T template_postgis " + dbname));
//REQUIRE(run("createdb " + dbname));
// Breaks when raster support is missing (unfortunately this is common)
//REQUIRE(run("psql -c 'CREATE EXTENSION postgis;' " + dbname, true));
REQUIRE(run("psql -q -f ./test/data/sql/postgis-create-db-and-tables.sql " + dbname));
}
std::string datasource_plugin("./plugins/input/postgis.input");
if (mapnik::util::exists(datasource_plugin))
mapnik::parameters params;
params["type"] = "postgis";
params["dbname"] = dbname;
SECTION("Postgis should throw without 'table' parameter")
{
SECTION("Postgis plugin initialization")
{
mapnik::parameters params;
params["type"] = "postgis";
params["dbname"] = "mapnik-tmp-postgis-test-db";
params["table"] = "test";
auto ds = mapnik::datasource_cache::instance().create(params);
REQUIRE(ds != nullptr);
CHECK(ds->type() == mapnik::datasource::datasource_t::Vector);
auto fields = ds->get_descriptor().get_descriptors();
require_field_names(fields, {"gid"});
require_field_types(fields, {mapnik::Integer});
}
CHECK_THROWS(mapnik::datasource_cache::instance().create(params));
}
}
SECTION("Postgis should throw with 'max_async_connection' greater than 'max_size'")
{
params["table"] = "test";
params["max_async_connection"] = "2";
params["max_size"] = "1";
CHECK_THROWS(mapnik::datasource_cache::instance().create(params));
}
}
SECTION("Postgis should throw with invalid metadata query")
{
params["table"] = "does_not_exist";
CHECK_THROWS(mapnik::datasource_cache::instance().create(params));
}
SECTION("Postgis should throw with invalid key field")
{
params["table"] = "test_invalid_id";
params["key_field"] = "id";
CHECK_THROWS(mapnik::datasource_cache::instance().create(params));
}
SECTION("Postgis should throw with multicolumn primary key")
{
params["table"] = "test_invalid_multi_col_pk";
params["autodetect_key_field"] = "true";
CHECK_THROWS(mapnik::datasource_cache::instance().create(params));
}
SECTION("Postgis should throw without geom column")
{
params["table"] = "test_no_geom_col";
auto ds = mapnik::datasource_cache::instance().create(params);
REQUIRE(ds != nullptr);
CHECK_THROWS(all_features(ds));
}
SECTION("Postgis should throw with invalid credentials")
{
params["table"] = "test";
params["user"] = "not_a_valid_user";
params["password"] = "not_a_valid_pwd";
CHECK_THROWS(mapnik::datasource_cache::instance().create(params));
}
SECTION("Postgis initialize dataset with persist_connection, schema, extent, geometry field, autodectect key field, simplify_geometries, row_limit")
{
params["persist_connection"] = "false";
params["table"] = "public.test";
params["geometry_field"] = "geom";
params["autodetect_key_field"] = "true";
params["extent"] = "-1 -1, -1 2, 4 3, 3 -1, -1 -1";
params["simplify_geometries"] = "true";
params["row_limit"] = "1";
auto ds = mapnik::datasource_cache::instance().create(params);
}
SECTION("Postgis dataset geometry type")
{
params["table"] = "(SELECT * FROM test WHERE gid=1) as data";
auto ds = mapnik::datasource_cache::instance().create(params);
REQUIRE(ds != nullptr);
CHECK(ds->get_geometry_type() == mapnik::datasource_geometry_t::Point);
}
SECTION("Postgis query field names")
{
params["table"] = "test";
auto ds = mapnik::datasource_cache::instance().create(params);
REQUIRE(ds != nullptr);
REQUIRE(ds->type() == mapnik::datasource::datasource_t::Vector);
auto fields = ds->get_descriptor().get_descriptors();
require_field_names(fields, { "gid", "colbigint", "col_text", "col-char", "col+bool", "colnumeric", "colsmallint", "colfloat4", "colfloat8", "colcharacter" });
require_field_types(fields, { mapnik::Integer, mapnik::Integer, mapnik::String, mapnik::String, mapnik::Boolean, mapnik::Double, mapnik::Integer, mapnik::Double, mapnik::Double, mapnik::String });
}
SECTION("Postgis iterate features")
{
params["table"] = "test";
params["key_field"] = "gid";
params["max_async_connection"] = "2";
//params["cursor_size"] = "2";
auto ds = mapnik::datasource_cache::instance().create(params);
REQUIRE(ds != nullptr);
auto featureset = ds->features_at_point(mapnik::coord2d(1, 1));
mapnik::feature_ptr feature;
while ((bool(feature = featureset->next()))) {
REQUIRE(feature->get(2).to_string() == feature->get("col_text").to_string());
REQUIRE(feature->get(4).to_bool() == feature->get("col+bool").to_bool());
REQUIRE(feature->get(5).to_double() == feature->get("colnumeric").to_double());
REQUIRE(feature->get(5).to_string() == feature->get("colnumeric").to_string());
}
featureset = all_features(ds);
feature = featureset->next();
//deactivate char tests for now: not yet implemented.
//add at postgis_datasource.cpp:423
//case 18: // char
//REQUIRE("A" == feature->get("col-char").to_string());
feature = featureset->next();
//REQUIRE("B" == feature->get("col-char").to_string());
feature = featureset->next();
REQUIRE(false == feature->get("col+bool").to_bool());
}
SECTION("Postgis cursorresultest")
{
params["table"] = "(SELECT * FROM test) as data";
params["cursor_size"] = "2";
auto ds = mapnik::datasource_cache::instance().create(params);
REQUIRE(ds != nullptr);
auto featureset = all_features(ds);
CHECK(count_features(featureset) == 8);
featureset = all_features(ds);
mapnik::feature_ptr feature;
while (bool(feature = featureset->next())) {
CHECK(feature->size() == 10);
}
featureset = all_features(ds);
require_geometry(featureset->next(), 1, mapnik::geometry::geometry_types::Point);
require_geometry(featureset->next(), 1, mapnik::geometry::geometry_types::Point);
require_geometry(featureset->next(), 2, mapnik::geometry::geometry_types::MultiPoint);
require_geometry(featureset->next(), 1, mapnik::geometry::geometry_types::LineString);
require_geometry(featureset->next(), 2, mapnik::geometry::geometry_types::MultiLineString);
require_geometry(featureset->next(), 1, mapnik::geometry::geometry_types::Polygon);
require_geometry(featureset->next(), 2, mapnik::geometry::geometry_types::MultiPolygon);
require_geometry(featureset->next(), 3, mapnik::geometry::geometry_types::GeometryCollection);
}
SECTION("Postgis bbox query")
{
params["table"] = "(SELECT * FROM public.test) as data WHERE geom && !bbox!";
auto ds = mapnik::datasource_cache::instance().create(params);
REQUIRE(ds != nullptr);
mapnik::box2d<double> ext = ds->envelope();
CAPTURE(ext);
INFO(std::setprecision(6) << std::fixed << ext.minx() << "/" << ext.miny() << " " << ext.maxx() << "/" << ext.maxy());
REQUIRE(ext.minx() == -2);
REQUIRE(ext.miny() == -2);
REQUIRE(ext.maxx() == 5);
REQUIRE(ext.maxy() == 4);
}
SECTION("Postgis query extent: full dataset")
{
//include schema to increase coverage
params["table"] = "(SELECT * FROM public.test) as data";
auto ds = mapnik::datasource_cache::instance().create(params);
REQUIRE(ds != nullptr);
mapnik::box2d<double> ext = ds->envelope();
CAPTURE(ext);
INFO(std::setprecision(6) << std::fixed << ext.minx() << "/" << ext.miny() << " " << ext.maxx() << "/" << ext.maxy());
REQUIRE(ext.minx() == -2);
REQUIRE(ext.miny() == -2);
REQUIRE(ext.maxx() == 5);
REQUIRE(ext.maxy() == 4);
}
/* deactivated for merging: still investigating a proper fix
SECTION("Postgis query extent from subquery")
{
params["table"] = "(SELECT * FROM test where gid=4) as data";
auto ds = mapnik::datasource_cache::instance().create(params);
REQUIRE(ds != nullptr);
mapnik::box2d<double> ext = ds->envelope();
CAPTURE(ext);
INFO(std::setprecision(6) << std::fixed << ext.minx() << "/" << ext.miny() << " " << ext.maxx() << "/" << ext.maxy());
REQUIRE(ext.minx() == 0);
REQUIRE(ext.miny() == 0);
REQUIRE(ext.maxx() == 1);
REQUIRE(ext.maxy() == 2);
}
*/
SECTION("Postgis query extent: from subquery with 'extent_from_subquery=true'")
{
params["table"] = "(SELECT * FROM test where gid=4) as data";
params["extent_from_subquery"] = "true";
auto ds = mapnik::datasource_cache::instance().create(params);
REQUIRE(ds != nullptr);
mapnik::box2d<double> ext = ds->envelope();
CAPTURE(ext);
INFO(std::setprecision(6) << std::fixed << ext.minx() << "/" << ext.miny() << " " << ext.maxx() << "/" << ext.maxy());
REQUIRE(ext.minx() == 0);
REQUIRE(ext.miny() == 0);
REQUIRE(ext.maxx() == 1);
REQUIRE(ext.maxy() == 2);
}
/* deactivated for merging: still investigating a proper fix
SECTION("Postgis query extent: subset with 'extent_from_subquery=true' and 'scale_denominator'")
{
// !!!! postgis-vt-util::z() returns 'null' when 'scale_denominator > 600000000'
// https://github.com/mapbox/postgis-vt-util/blob/559f073877696a6bfea41baf3e1065f9cf4d18d1/postgis-vt-util.sql#L615-L617
params["table"] = "(SELECT * FROM test where gid=4 AND z(!scale_denominator!) BETWEEN 0 AND 22) as data";
params["extent_from_subquery"] = "true";
auto ds = mapnik::datasource_cache::instance().create(params);
REQUIRE(ds != nullptr);
mapnik::box2d<double> ext = ds->envelope();
CAPTURE(ext);
INFO("" << std::setprecision(6) << std::fixed << ext.minx() << "/" << ext.miny() << " " << ext.maxx() << "/" << ext.maxy());
REQUIRE(ext.minx() == 0);
REQUIRE(ext.miny() == 0);
REQUIRE(ext.maxx() == 1);
REQUIRE(ext.maxy() == 2);
}
*/
}

View file

@ -1,17 +1,73 @@
#define CATCH_CONFIG_RUNNER
#include "catch.hpp"
#include <string>
#include <mapnik/util/fs.hpp>
#include <mapnik/datasource_cache.hpp>
#include <boost/filesystem/convenience.hpp>
#include "cleanup.hpp" // run_cleanup()
std::string plugin_path;
inline void set_plugin_path(Catch::ConfigData&, std::string const& _plugin_path ) {
plugin_path = _plugin_path;
}
std::string working_dir;
inline void set_working_dir(Catch::ConfigData&, std::string const& _working_dir ) {
working_dir = _working_dir;
}
int main (int argc, char* const argv[])
{
mapnik::datasource_cache::instance().register_datasources("plugins/input/");
Catch::Session session;
int result = Catch::Session().run( argc, argv );
auto & cli = session.cli();
cli["-p"]["--plugins"]
.describe("path to mapnik plugins")
.bind(&set_plugin_path, "plugins");
cli["-C"]["--working-directory"]
.describe("change working directory")
.bind(&set_working_dir, "working directory");
int result = session.applyCommandLine(argc, argv);
if (!plugin_path.empty())
{
if (!mapnik::util::exists(plugin_path))
{
std::clog << "Could not find " << plugin_path << "\n";
return -1;
}
mapnik::datasource_cache::instance().register_datasources(plugin_path);
}
else
{
mapnik::datasource_cache::instance().register_datasources("plugins/input/");
}
if (!working_dir.empty())
{
if (!mapnik::util::exists(working_dir))
{
std::clog << "Could not find " << working_dir << "\n";
return -1;
}
boost::filesystem::current_path(working_dir);
}
if (result == 0)
{
result = session.run();
}
testing::run_cleanup();
return result;
}

View file

@ -0,0 +1,176 @@
/*****************************************************************************
*
* 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
*
*****************************************************************************/
#include "catch.hpp"
#include <mapnik/debug.hpp>
#include <mapnik/vertex.hpp>
#include <mapnik/svg/svg_path_parser.hpp>
#include <mapnik/svg/svg_converter.hpp>
#include <mapnik/marker.hpp>
namespace {
template <int N = 6>
struct vertex_equal
{
template <typename T>
bool operator() (T const& lhs, T const& rhs) const
{
static const double eps = 1.0 / std::pow(10,N);
return (std::fabs(std::get<0>(lhs) - std::get<0>(rhs)) < eps)
&& (std::fabs(std::get<1>(lhs) - std::get<1>(rhs)) < eps)
&& std::get<2>(lhs) == std::get<2>(rhs);
}
};
template <typename Expected>
void test_path_parser(std::string const& str, Expected const& expected)
{
using namespace mapnik::svg;
mapnik::svg_path_ptr marker_path(std::make_shared<mapnik::svg_storage_type>());
vertex_stl_adapter<svg_path_storage> stl_storage(marker_path->source());
svg_path_adapter svg_path(stl_storage);
svg_converter_type svg(svg_path, marker_path->attributes());
CHECK(mapnik::svg::parse_path(str.c_str(), svg));
double x,y;
unsigned cmd;
auto & p = svg.storage();
std::vector<std::tuple<double,double,unsigned>> vec;
while ((cmd = p.vertex(&x,&y)) != mapnik::SEG_END)
{
vec.emplace_back(x, y, cmd);
//std::cerr << "std::make_tuple(" << x << ", " << y << ", " << cmd << ")," << std::endl;
}
REQUIRE(std::equal(expected.begin(),expected.end(), vec.begin(), vertex_equal<3>()));
}
} // anonymous ns
TEST_CASE("SVG path parser") {
SECTION("MoveTo/LineTo")
{
std::string str = "M 100 100 L 300 100 L 200 300 z";
std::string str2 = "M100,100L300,100L200,300z";
std::string str3 = "M100,100l200 0L200,300z";
std::vector<std::tuple<double,double,unsigned>> expected = {
std::make_tuple(100, 100, 1),
std::make_tuple(300, 100, 2),
std::make_tuple(200, 300, 2),
std::make_tuple(100, 100, 79) };
test_path_parser(str, expected);
test_path_parser(str2, expected);
test_path_parser(str3, expected);
}
SECTION("MoveTo/HLine/VLine")
{
std::string str = "M100 100H300V200z";
std::string str2 = "M100,100h200v100z";
std::vector<std::tuple<double,double,unsigned>> expected = {
std::make_tuple(100, 100, 1),
std::make_tuple(300, 100, 2),
std::make_tuple(300, 200, 2),
std::make_tuple(100, 100, 79)
};
test_path_parser(str, expected);
test_path_parser(str2, expected);
}
SECTION("Arcs")
{
std::string str = "M300,200 h-150 a150,150 0 1,0 150,-150 z";
std::vector<std::tuple<double,double,unsigned>> expected = {
std::make_tuple(300, 200, 1),
std::make_tuple(150, 200, 2),
std::make_tuple(150, 282.843, 4),
std::make_tuple(217.157, 350, 4),
std::make_tuple(300, 350, 4),
std::make_tuple(382.843, 350, 4),
std::make_tuple(450, 282.843, 4),
std::make_tuple(450, 200, 4),
std::make_tuple(450, 117.157, 4),
std::make_tuple(382.843, 50, 4),
std::make_tuple(300, 50, 4),
std::make_tuple(300, 200, 79)};
test_path_parser(str, expected);
}
SECTION("Arcs 2")
{
std::string str = "M275,175 v-150 a150,150 0 0,0 -150,150 z";
std::vector<std::tuple<double,double,unsigned>> expected = {
std::make_tuple(275, 175, 1),
std::make_tuple(275, 25, 2),
std::make_tuple(192.157, 25, 4),
std::make_tuple(125, 92.1573, 4),
std::make_tuple(125, 175, 4),
std::make_tuple(275, 175, 79)};
test_path_parser(str, expected);
}
SECTION("Arcs 3")
{
std::string str = "M600,350 l 50,-25"
"a25,25 -30 0,1 50,-25 l 50,-25"
"a25,50 -30 0,1 50,-25 l 50,-25"
"a25,75 -30 0,1 50,-25 l 50,-25"
"a25,100 -30 0,1 50,-25 l 50,-25";
std::vector<std::tuple<double,double,unsigned>> expected = {
std::make_tuple(600, 350, 1),
std::make_tuple(650, 325, 2),
std::make_tuple(643.096, 311.193, 4),
std::make_tuple(648.693, 294.404, 4),
std::make_tuple(662.5, 287.5, 4),
std::make_tuple(676.307, 280.596, 4),
std::make_tuple(693.096, 286.193, 4),
std::make_tuple(700, 300, 4),
std::make_tuple(750, 275, 2),
std::make_tuple(734.991, 248.079, 4),
std::make_tuple(734.017, 220.66, 4),
std::make_tuple(747.825, 213.756, 4),
std::make_tuple(761.632, 206.852, 4),
std::make_tuple(784.991, 223.079, 4),
std::make_tuple(800, 250, 4),
std::make_tuple(850, 225, 2),
std::make_tuple(827.153, 184.812, 4),
std::make_tuple(819.825, 146.636, 4),
std::make_tuple(833.632, 139.733, 4),
std::make_tuple(847.44, 132.829, 4),
std::make_tuple(877.153, 159.812, 4),
std::make_tuple(900, 200, 4),
std::make_tuple(950, 175, 2),
std::make_tuple(919.382, 121.506, 4),
std::make_tuple(905.754, 72.5436, 4),
std::make_tuple(919.561, 65.64, 4),
std::make_tuple(933.368, 58.7365, 4),
std::make_tuple(969.382, 96.5057, 4),
std::make_tuple(1000, 150, 4),
std::make_tuple(1050, 125, 2)};
test_path_parser(str, expected);
}
}

View file

@ -150,7 +150,8 @@ result_list runner::test_all(report_type & report) const
result_list runner::test(std::vector<std::string> const & style_names, report_type & report) const
{
std::vector<runner::path_type> files(style_names.size());
std::vector<runner::path_type> files;
files.reserve(style_names.size());
std::transform(style_names.begin(), style_names.end(), std::back_inserter(files),
[&](runner::path_type const & name)
{

View file

@ -67,6 +67,11 @@ struct main_marker_visitor
double opacity = 1;
int w = marker.width();
int h = marker.height();
if (w == 0 || h == 0)
{
// fallback to svg width/height or viewBox
std::tie(w, h) = marker.dimensions();
}
if (verbose_)
{
std::clog << "found width of '" << w << "' and height of '" << h << "'\n";