#654 - add support for linear and radial gradients in SVG symbols. Thanks to Toby Collett for the coding & Paul Wicks for funding.

This commit is contained in:
Robert Coup 2011-01-26 01:18:40 +00:00
parent a79018d0a0
commit 386a4f5daf
28 changed files with 1938 additions and 729 deletions

View file

@ -32,6 +32,10 @@
#include <mapnik/label_collision_detector.hpp>
#include <mapnik/placement_finder.hpp>
#include <mapnik/map.hpp>
#include <mapnik/marker.hpp>
// agg
#include <agg_trans_affine.h>
// boost
#include <boost/utility.hpp>
@ -53,6 +57,8 @@ public:
void end_map_processing(Map const& map);
void start_layer_processing(layer const& lay);
void end_layer_processing(layer const& lay);
void render_marker(const int x, const int y, marker &marker, const agg::trans_affine & tr, double opacity);
void process(point_symbolizer const& sym,
Feature const& feature,
proj_transform const& prj_trans);

View file

@ -36,6 +36,7 @@
#include <mapnik/label_collision_detector.hpp>
#include <mapnik/placement_finder.hpp>
#include <mapnik/map.hpp>
#include <mapnik/marker.hpp>
// cairo
#include <cairomm/context.h>
@ -115,6 +116,8 @@ public:
return false;
};
protected:
void render_marker(const int x, const int y, marker &marker, const agg::trans_affine & mtx, double opacity=1.0);
Map const& m_;
Cairo::RefPtr<Cairo::Context> context_;
CoordTransform t_;

107
include/mapnik/gradient.hpp Normal file
View file

@ -0,0 +1,107 @@
/*****************************************************************************
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2010 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
*
*****************************************************************************/
//$Id$
#ifndef GRADIENT_HPP
#define GRADIENT_HPP
#include <agg_trans_affine.h>
// mapnik
#include <mapnik/color.hpp>
#include <mapnik/enumeration.hpp>
// stl
#include <vector>
namespace mapnik
{
typedef std::pair<double, mapnik::color> stop_pair;
typedef std::vector<stop_pair > stop_array;
enum gradient_enum
{
NO_GRADIENT,
LINEAR,
RADIAL,
gradient_enum_MAX
};
DEFINE_ENUM( gradient_e, gradient_enum );
enum gradient_unit_enum
{
USER_SPACE_ON_USE,
USER_SPACE_ON_USE_BOUNDING_BOX, // used to indicate % age values were specified. This are % of the svg image extent.
OBJECT_BOUNDING_BOX, // (i.e., the abstract coordinate system where (0,0) is at the top/left of the object bounding box and (1,1) is at the bottom/right of the object bounding box)
gradient_unit_enum_MAX
};
DEFINE_ENUM( gradient_unit_e, gradient_unit_enum );
class MAPNIK_DECL gradient
{
gradient_e gradient_type_;
stop_array stops_;
// control points for the gradient, x1/y1 is the start point, x2/y2 the stop point.
double x1_;
double y1_;
double x2_;
double y2_;
// for radial gradients r specifies the radius of the stop circle centered on x2/y2
double r_;
// units for the coordinates
gradient_unit_e units_;
// transform
agg::trans_affine transform_;
public:
explicit gradient();
gradient(gradient const& other);
gradient& operator=(const gradient& rhs);
void set_gradient_type(gradient_e grad);
gradient_e get_gradient_type() const;
void set_transform(agg::trans_affine transform);
agg::trans_affine get_transform() const;
void set_units(gradient_unit_e units);
gradient_unit_e get_units() const;
void add_stop(double offset, color const& c);
bool has_stop() const;
stop_array const& get_stop_array() const;
void set_control_points(double x1, double y1, double x2, double y2, double r=0);
void get_control_points(double &x1, double &y1, double &x2, double &y2, double &r) const;
void get_control_points(double &x1, double &y1, double &x2, double &y2) const;
private:
void swap(const gradient& other) throw();
};
}
#endif //GRADIENT_HPP

View file

@ -1,63 +0,0 @@
/*****************************************************************************
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2009 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
*
*****************************************************************************/
//$Id$
#ifndef MAPNIK_IMAGE_CACHE_HPP
#define MAPNIK_IMAGE_CACHE_HPP
// mapnik
#include <mapnik/utils.hpp>
#include <mapnik/config.hpp>
#include <mapnik/image_data.hpp>
// boost
#include <boost/utility.hpp>
#include <boost/unordered_map.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/optional.hpp>
#ifdef MAPNIK_THREADSAFE
#include <boost/thread/mutex.hpp>
#endif
namespace mapnik
{
typedef boost::shared_ptr<image_data_32> image_ptr;
struct MAPNIK_DECL image_cache :
public singleton <image_cache, CreateStatic>,
private boost::noncopyable
{
friend class CreateStatic<image_cache>;
#ifdef MAPNIK_THREADSAFE
static boost::mutex mutex_;
#endif
static boost::unordered_map<std::string,image_ptr> cache_;
static bool insert(std::string const& key, image_ptr);
static boost::optional<image_ptr> find(std::string const& key, bool update_cache = false);
};
}
#endif // MAPNIK_IMAGE_CACHE_HPP

127
include/mapnik/marker.hpp Normal file
View file

@ -0,0 +1,127 @@
/*****************************************************************************
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2006 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
*
*****************************************************************************/
//$Id: image_data.hpp 39 2005-04-10 20:39:53Z pavlenko $
#ifndef MARKER_HPP
#define MARKER_HPP
#include <mapnik/global.hpp>
#include <mapnik/image_data.hpp>
#include <mapnik/svg/svg_path_attributes.hpp>
#include <mapnik/svg/svg_storage.hpp>
#include <mapnik/svg/svg_path_adapter.hpp>
#include "agg_path_storage.h"
// boost
#include <boost/utility.hpp>
#include <boost/unordered_map.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/optional.hpp>
#include <cassert>
#include <cstring>
namespace mapnik
{
typedef agg::pod_bvector<mapnik::svg::path_attributes> attr_storage;
typedef mapnik::svg::svg_storage<mapnik::svg::svg_path_storage,attr_storage> svg_storage_type;
typedef boost::shared_ptr<svg_storage_type> path_ptr;
typedef boost::shared_ptr<image_data_32> image_ptr;
/**
* A class to hold either vector or bitmap marker data. This allows these to be treated equally
* in the image caches and most of the render paths.
*/
class marker
{
public:
marker()
{
// create default OGC 4x4 black pixel
bitmap_data_ = boost::optional<mapnik::image_ptr>(new image_data_32(4,4));
(*bitmap_data_)->set(0xff000000);
}
marker(const boost::optional<mapnik::image_ptr> &data) : bitmap_data_(data)
{
}
marker(const boost::optional<mapnik::path_ptr> &data) : vector_data_(data)
{
}
marker(const marker& rhs) : bitmap_data_(rhs.bitmap_data_), vector_data_(rhs.vector_data_)
{
}
inline unsigned width() const
{
if (is_bitmap())
return (*bitmap_data_)->width();
else if (is_vector())
return (*vector_data_)->bounding_box().width();
return 0;
}
inline unsigned height() const
{
if (is_bitmap())
return (*bitmap_data_)->height();
else if (is_vector())
return (*vector_data_)->bounding_box().height();
return 0;
}
inline bool is_bitmap() const
{
return bitmap_data_;
}
inline bool is_vector() const
{
return vector_data_;
}
boost::optional<mapnik::image_ptr> get_bitmap_data()
{
return bitmap_data_;
}
boost::optional<mapnik::path_ptr> get_vector_data()
{
return vector_data_;
}
private:
marker& operator=(const marker&);
boost::optional<mapnik::image_ptr> bitmap_data_;
boost::optional<mapnik::path_ptr> vector_data_;
};
}
#endif //MARKER_HPP

View file

@ -27,6 +27,7 @@
// mapnik
#include <mapnik/utils.hpp>
#include <mapnik/marker.hpp>
#include <mapnik/config.hpp>
#include <mapnik/svg/svg_path_attributes.hpp>
#include <mapnik/svg/svg_storage.hpp>
@ -47,9 +48,8 @@ namespace mapnik
using namespace mapnik::svg;
typedef agg::pod_bvector<path_attributes> attr_storage;
typedef svg_storage<svg_path_storage,attr_storage> svg_storage_type;
typedef boost::shared_ptr<svg_storage_type> path_ptr;
typedef boost::shared_ptr<marker> marker_ptr;
struct MAPNIK_DECL marker_cache :
public singleton <marker_cache, CreateStatic>,
@ -60,9 +60,9 @@ struct MAPNIK_DECL marker_cache :
#ifdef MAPNIK_THREADSAFE
static boost::mutex mutex_;
#endif
static boost::unordered_map<std::string,path_ptr> cache_;
static bool insert(std::string const& key, path_ptr);
static boost::optional<path_ptr> find(std::string const& key, bool update_cache = false);
static boost::unordered_map<std::string,marker_ptr> cache_;
static bool insert(std::string const& key, marker_ptr);
static boost::optional<marker_ptr> find(std::string const& key, bool update_cache = false);
};
}

View file

@ -201,7 +201,19 @@ public:
attr.fill_color.opacity(a * f.opacity());
attr.fill_flag = true;
}
void add_fill_gradient(mapnik::gradient& grad)
{
path_attributes& attr = cur_attr();
attr.fill_gradient = grad;
}
void add_stroke_gradient(mapnik::gradient& grad)
{
path_attributes& attr = cur_attr();
attr.stroke_gradient = grad;
}
void stroke(const agg::rgba8& s)
{
path_attributes& attr = cur_attr();
@ -215,6 +227,12 @@ public:
{
cur_attr().even_odd_flag = flag;
}
void visibility(bool flag)
{
cur_attr().visibility_flag = flag;
}
void stroke_width(double w)
{

View file

@ -27,10 +27,14 @@
#include <mapnik/svg/svg_path_attributes.hpp>
#include <mapnik/svg/svg_converter.hpp>
#include <mapnik/svg/svg_path_adapter.hpp>
#include <mapnik/gradient.hpp>
// boost
#include <boost/utility.hpp>
#include <libxml/xmlreader.h>
#include <map>
namespace mapnik { namespace svg {
class svg_parser : private boost::noncopyable
@ -51,12 +55,20 @@ private:
void parse_rect(xmlTextReaderPtr reader);
void parse_circle(xmlTextReaderPtr reader);
void parse_ellipse(xmlTextReaderPtr reader);
void parse_linear_gradient(xmlTextReaderPtr reader);
void parse_radial_gradient(xmlTextReaderPtr reader);
bool parse_common_gradient(xmlTextReaderPtr reader);
void parse_gradient_stop(xmlTextReaderPtr reader);
void parse_pattern(xmlTextReaderPtr reader);
void parse_attr(xmlTextReaderPtr reader);
void parse_attr(const xmlChar * name, const xmlChar * value );
private:
svg_converter_type & path_;
bool is_defs_;
std::map<std::string, gradient> gradient_map_;
std::pair<std::string, gradient> temporary_gradient_;
};
}}

View file

@ -30,6 +30,8 @@
#include "agg_pixfmt_rgba.h"
#include "agg_trans_affine.h"
#include <mapnik/gradient.hpp>
namespace mapnik {
namespace svg {
@ -42,11 +44,14 @@ struct path_attributes
bool fill_flag;
bool stroke_flag;
bool even_odd_flag;
bool visibility_flag;
agg::line_join_e line_join;
agg::line_cap_e line_cap;
double miter_limit;
double stroke_width;
agg::trans_affine transform;
mapnik::gradient fill_gradient;
mapnik::gradient stroke_gradient;
// Empty constructor
path_attributes() :
@ -57,11 +62,14 @@ struct path_attributes
fill_flag(true),
stroke_flag(false),
even_odd_flag(false),
visibility_flag(true),
line_join(agg::miter_join),
line_cap(agg::butt_cap),
miter_limit(4.0),
stroke_width(1.0),
transform()
transform(),
fill_gradient(),
stroke_gradient()
{
}
@ -74,11 +82,15 @@ struct path_attributes
fill_flag(attr.fill_flag),
stroke_flag(attr.stroke_flag),
even_odd_flag(attr.even_odd_flag),
visibility_flag(attr.visibility_flag),
line_join(attr.line_join),
line_cap(attr.line_cap),
miter_limit(attr.miter_limit),
stroke_width(attr.stroke_width),
transform(attr.transform) {}
transform(attr.transform),
fill_gradient(attr.fill_gradient),
stroke_gradient(attr.stroke_gradient)
{}
// Copy constructor with new index value
path_attributes(path_attributes const& attr, unsigned idx)
@ -89,11 +101,15 @@ struct path_attributes
fill_flag(attr.fill_flag),
stroke_flag(attr.stroke_flag),
even_odd_flag(attr.even_odd_flag),
visibility_flag(attr.visibility_flag),
line_join(attr.line_join),
line_cap(attr.line_cap),
miter_limit(attr.miter_limit),
stroke_width(attr.stroke_width),
transform(attr.transform) {}
transform(attr.transform),
fill_gradient(attr.fill_gradient),
stroke_gradient(attr.stroke_gradient)
{}
};
}}

View file

@ -24,6 +24,8 @@
#define MAPNIK_SVG_RENDERER_HPP
#include <mapnik/svg/svg_path_attributes.hpp>
#include <mapnik/gradient.hpp>
#include <mapnik/box2d.hpp>
#include <boost/utility.hpp>
@ -37,9 +39,67 @@
#include "agg_bounding_rect.h"
#include "agg_rasterizer_scanline_aa.h"
#include "agg_rendering_buffer.h"
#include "agg_rasterizer_scanline_aa.h"
#include "agg_scanline_u.h"
#include "agg_scanline_p.h"
#include "agg_renderer_scanline.h"
#include "agg_span_allocator.h"
#include "agg_span_gradient.h"
#include "agg_gradient_lut.h"
#include "agg_gamma_lut.h"
#include "agg_span_interpolator_linear.h"
#include "agg_pixfmt_rgba.h"
#include "agg_path_storage.h"
#include "agg_ellipse.h"
#include <boost/foreach.hpp>
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.
*/
class linear_gradient_from_segment
{
public:
linear_gradient_from_segment(double x1, double y1, double x2, double y2) :
x1_(x1*agg::gradient_subpixel_scale),
y1_(y1*agg::gradient_subpixel_scale),
x2_(x2*agg::gradient_subpixel_scale),
y2_(y2*agg::gradient_subpixel_scale)
{
double dx = x2_-x1_;
double dy = y2_-y1_;
length_sqr_ = dx*dx+dy*dy;
}
int calculate(int x, int y, int d) const
{
if (length_sqr_ <= 0)
return 0;
double u = ((x-x1_)*(x2_-x1_) + (y-y1_)*(y2_-y1_))/length_sqr_;
if (u < 0)
u = 0;
else if (u > 1)
u = 1;
return u*d;
}
private:
double x1_;
double y1_;
double x2_;
double y2_;
double length_sqr_;
};
template <typename VertexSource, typename AttributeSource>
class svg_renderer : boost::noncopyable
{
@ -48,6 +108,9 @@ class svg_renderer : boost::noncopyable
typedef agg::conv_transform<curved_stroked_type> curved_stroked_trans_type;
typedef agg::conv_transform<curved_type> curved_trans_type;
typedef agg::conv_contour<curved_trans_type> curved_trans_contour_type;
typedef agg::pixfmt_rgba32_plain pixfmt;
typedef agg::renderer_base<pixfmt> renderer_base;
typedef agg::renderer_scanline_aa_solid<renderer_base> renderer_solid;
public:
svg_renderer(VertexSource & source, AttributeSource const& attributes)
@ -56,12 +119,134 @@ public:
curved_stroked_(curved_),
attributes_(attributes) {}
template <typename Rasterizer, typename Scanline, typename Renderer>
void render_gradient(Rasterizer& ras,
Scanline& sl,
Renderer& ren,
const gradient &grad,
agg::trans_affine const& mtx,
double opacity,
const box2d<double> &symbol_bbox,
const box2d<double> &path_bbox)
{
typedef agg::gamma_lut<agg::int8u, agg::int8u> gamma_lut_type;
typedef agg::gradient_lut<agg::color_interpolator<agg::rgba8>, 1024> color_func_type;
typedef agg::span_interpolator_linear<> interpolator_type;
typedef agg::span_allocator<agg::rgba8> span_allocator_type;
span_allocator_type m_alloc;
color_func_type m_gradient_lut;
gamma_lut_type m_gamma_lut;
double x1,x2,y1,y2,radius;
grad.get_control_points(x1,y1,x2,y2,radius);
m_gradient_lut.remove_all();
BOOST_FOREACH ( mapnik::stop_pair const& st, grad.get_stop_array() )
{
mapnik::color const& stop_color = st.second;
unsigned r= stop_color.red();
unsigned g= stop_color.green();
unsigned b= stop_color.blue();
unsigned a= stop_color.alpha();
//std::clog << "r: " << r << " g: " << g << " b: " << b << "a: " << a << "\n";
m_gradient_lut.add_color(st.first, agg::rgba8(r, g, b, int(a * opacity)));
}
m_gradient_lut.build_lut();
agg::trans_affine transform = mtx;
transform.invert();
agg::trans_affine tr;
tr = grad.get_transform();
tr.invert();
transform *= tr;
if (grad.get_units() != USER_SPACE_ON_USE)
{
double bx1=symbol_bbox.minx();
double by1=symbol_bbox.miny();
double bx2=symbol_bbox.maxx();
double by2=symbol_bbox.maxy();
if (grad.get_units() == OBJECT_BOUNDING_BOX)
{
bx1=path_bbox.minx();
by1=path_bbox.miny();
bx2=path_bbox.maxx();
by2=path_bbox.maxy();
}
transform.translate(-bx1,-by1);
transform.scale(1.0/(bx2-bx1),1.0/(by2-by1));
}
if (grad.get_gradient_type() == RADIAL)
{
typedef agg::gradient_radial_focus gradient_adaptor_type;
typedef agg::span_gradient<agg::rgba8,
interpolator_type,
gradient_adaptor_type,
color_func_type> span_gradient_type;
// the agg radial gradient assumes it is centred on 0
transform.translate(-x2,-y2);
// 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;
transform.scale(scaleup,scaleup);
interpolator_type span_interpolator(transform);
gradient_adaptor_type gradient_adaptor(radius,(x1-x2),(y1-y2));
span_gradient_type span_gradient(span_interpolator,
gradient_adaptor,
m_gradient_lut,
0, radius);
render_scanlines_aa(ras, sl, ren, m_alloc, span_gradient);
}
else
{
typedef linear_gradient_from_segment gradient_adaptor_type;
typedef agg::span_gradient<agg::rgba8,
interpolator_type,
gradient_adaptor_type,
color_func_type> span_gradient_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;
transform.scale(scaleup,scaleup);
interpolator_type span_interpolator(transform);
gradient_adaptor_type gradient_adaptor(x1,y1,x2,y2);
span_gradient_type span_gradient(span_interpolator,
gradient_adaptor,
m_gradient_lut,
0, scaleup);
render_scanlines_aa(ras, sl, ren, m_alloc, span_gradient);
}
}
template <typename Rasterizer, typename Scanline, typename Renderer>
void render(Rasterizer& ras,
Scanline& sl,
Renderer& ren,
agg::trans_affine const& mtx,
double opacity=1.0)
double opacity,
const box2d<double> &symbol_bbox)
{
using namespace agg;
@ -76,7 +261,15 @@ public:
for(unsigned i = 0; i < attributes_.size(); ++i)
{
mapnik::svg::path_attributes const& attr = attributes_[i];
if (!attr.visibility_flag)
continue;
transform = attr.transform;
double bx1,by1,bx2,by2;
bounding_rect_single(curved_trans, attr.index, &bx1, &by1, &bx2, &by2);
box2d<double> path_bbox(bx1,by1,bx2,by2);
transform *= mtx;
double scl = transform.scale();
//curved_.approximation_method(curve_inc);
@ -84,11 +277,11 @@ public:
curved_.angle_tolerance(0.0);
rgba8 color;
if(attr.fill_flag)
if (attr.fill_flag || attr.fill_gradient.get_gradient_type() != NO_GRADIENT)
{
ras.reset();
ras.filling_rule(attr.even_odd_flag ? fill_even_odd : fill_non_zero);
if(fabs(curved_trans_contour.width()) < 0.0001)
{
ras.add_path(curved_trans, attr.index);
@ -99,14 +292,24 @@ public:
ras.add_path(curved_trans_contour, attr.index);
}
color = attr.fill_color;
color.opacity(color.opacity() * attr.opacity * opacity);
ren.color(color);
render_scanlines(ras, sl, ren);
if(attr.fill_gradient.get_gradient_type() != NO_GRADIENT)
{
render_gradient(ras, sl, ren, attr.fill_gradient, transform, attr.opacity * opacity, symbol_bbox, path_bbox);
}
else
{
ras.filling_rule(attr.even_odd_flag ? fill_even_odd : fill_non_zero);
color = attr.fill_color;
color.opacity(color.opacity() * attr.opacity * opacity);
renderer_solid ren_s(ren);
ren_s.color(color);
render_scanlines(ras, sl, ren_s);
}
}
if(attr.stroke_flag)
if (attr.stroke_flag || attr.stroke_gradient.get_gradient_type() != NO_GRADIENT)
{
std::clog << "stroking\n";
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);
@ -123,12 +326,21 @@ public:
curved_.angle_tolerance(0.2);
}
ras.reset();
ras.filling_rule(fill_non_zero);
ras.add_path(curved_stroked_trans, attr.index);
color = attr.stroke_color;
color.opacity(color.opacity() * attr.opacity * opacity);
ren.color(color);
render_scanlines(ras, sl, ren);
if(attr.stroke_gradient.get_gradient_type() != NO_GRADIENT)
{
render_gradient(ras, sl, ren, attr.stroke_gradient, transform, attr.opacity * opacity, symbol_bbox, path_bbox);
}
else
{
ras.filling_rule(fill_non_zero);
color = attr.stroke_color;
color.opacity(color.opacity() * attr.opacity * opacity);
renderer_solid ren_s(ren);
ren_s.color(color);
render_scanlines(ras, sl, ren_s);
}
}
}
}

View file

@ -104,8 +104,8 @@ source = Split(
filter_factory.cpp
font_engine_freetype.cpp
font_set.cpp
gradient.cpp
graphics.cpp
image_cache.cpp
image_reader.cpp
image_util.cpp
layer.cpp

View file

@ -24,8 +24,7 @@
// mapnik
#include <mapnik/agg_renderer.hpp>
#include <mapnik/agg_rasterizer.hpp>
#include <mapnik/image_util.hpp>
#include <mapnik/image_cache.hpp>
#include <mapnik/marker_cache.hpp>
#include <mapnik/unicode.hpp>
#include <mapnik/placement_finder.hpp>
#include <mapnik/config_error.hpp>
@ -126,11 +125,12 @@ agg_renderer<T>::agg_renderer(Map const& m, T & pixmap, double scale_factor, uns
boost::optional<std::string> const& image_filename = m.background_image();
if (image_filename)
{
boost::optional<mapnik::image_ptr> bg_image = mapnik::image_cache::instance()->find(*image_filename,true);
if (bg_image)
boost::optional<mapnik::marker_ptr> bg_marker = mapnik::marker_cache::instance()->find(*image_filename,true);
if (bg_marker && (*bg_marker)->is_bitmap())
{
int w = (*bg_image)->width();
int h = (*bg_image)->height();
mapnik::image_ptr bg_image = *(*bg_marker)->get_bitmap_data();
int w = bg_image->width();
int h = bg_image->height();
if ( w > 0 && h > 0)
{
// repeat background-image both vertically and horizontally
@ -140,7 +140,7 @@ agg_renderer<T>::agg_renderer(Map const& m, T & pixmap, double scale_factor, uns
{
for (unsigned y=0;y<y_steps;++y)
{
pixmap_.set_rectangle_alpha2(*(*bg_image), x*w, y*h, 1.0f);
pixmap_.set_rectangle_alpha2(*bg_image, x*w, y*h, 1.0f);
}
}
}

View file

@ -25,7 +25,7 @@
#include <mapnik/agg_renderer.hpp>
#include <mapnik/agg_rasterizer.hpp>
#include <mapnik/agg_pattern_source.hpp>
#include <mapnik/image_cache.hpp>
#include <mapnik/marker_cache.hpp>
// agg
#include "agg_basics.h"
@ -58,7 +58,11 @@ void agg_renderer<T>::process(line_pattern_symbolizer const& sym,
agg::pixfmt_rgba32_plain pixf(buf);
std::string filename = path_processor_type::evaluate( *sym.get_filename(), feature);
boost::optional<image_ptr> pat = image_cache::instance()->find(filename,true);
boost::optional<marker_ptr> mark = marker_cache::instance()->find(filename,true);
if (!mark || !(*mark)->is_bitmap()) return;
boost::optional<image_ptr> pat = (*mark)->get_bitmap_data();
if (!pat) return;

View file

@ -24,8 +24,7 @@
#include <mapnik/agg_renderer.hpp>
#include <mapnik/agg_rasterizer.hpp>
#include <mapnik/image_util.hpp>
#include <mapnik/image_cache.hpp>
#include <mapnik/svg/marker_cache.hpp>
#include <mapnik/marker_cache.hpp>
#include <mapnik/svg/svg_renderer.hpp>
#include <mapnik/svg/svg_path_adapter.hpp>
#include <mapnik/markers_placement.hpp>
@ -50,7 +49,7 @@ void agg_renderer<T>::process(markers_symbolizer const& sym,
proj_transform const& prj_trans)
{
typedef coord_transform2<CoordTransform,geometry_type> path_type;
typedef agg::pixfmt_rgba32 pixfmt;
typedef agg::pixfmt_rgba32_plain pixfmt;
typedef agg::renderer_base<pixfmt> renderer_base;
typedef agg::renderer_scanline_aa_solid<renderer_base> renderer_solid;
@ -73,9 +72,10 @@ void agg_renderer<T>::process(markers_symbolizer const& sym,
if (!filename.empty())
{
boost::optional<path_ptr> marker = mapnik::marker_cache::instance()->find(filename, true);
if (marker && *marker)
boost::optional<marker_ptr> mark = mapnik::marker_cache::instance()->find(filename, true);
if (mark && *mark && (*mark)->is_vector())
{
boost::optional<path_ptr> marker = (*mark)->get_vector_data();
box2d<double> const& bbox = (*marker)->bounding_box();
double x1 = bbox.minx();
double y1 = bbox.miny();
@ -111,7 +111,7 @@ void agg_renderer<T>::process(markers_symbolizer const& sym,
while (placement.get_point(&x, &y, &angle))
{
agg::trans_affine matrix = recenter * tr *agg::trans_affine_rotation(angle) * agg::trans_affine_translation(x, y);
svg_renderer.render(*ras_ptr, sl, ren, matrix, sym.get_opacity());
svg_renderer.render(*ras_ptr, sl, renb, matrix, sym.get_opacity(),bbox);
if (writer.first)
//writer.first->add_box(label_ext, feature, t_, writer.second);
std::clog << "### Warning metawriter not yet supported for LINE placement\n";

View file

@ -25,9 +25,8 @@
#include <mapnik/agg_renderer.hpp>
#include <mapnik/agg_rasterizer.hpp>
#include <mapnik/image_util.hpp>
#include <mapnik/image_cache.hpp>
#include <mapnik/metawriter.hpp>
#include <mapnik/svg/marker_cache.hpp>
#include <mapnik/marker_cache.hpp>
#include <mapnik/svg/svg_converter.hpp>
#include <mapnik/svg/svg_renderer.hpp>
#include <mapnik/svg/svg_path_adapter.hpp>
@ -44,126 +43,104 @@
namespace mapnik {
template <typename T>
void agg_renderer<T>::process(point_symbolizer const& sym,
Feature const& feature,
proj_transform const& prj_trans)
void agg_renderer<T>::render_marker(const int x, const int y, marker &marker, const agg::trans_affine & tr, double opacity)
{
typedef agg::pixfmt_rgba32 pixfmt;
typedef agg::renderer_base<pixfmt> renderer_base;
typedef agg::renderer_scanline_aa_solid<renderer_base> renderer_solid;
double x;
double y;
double z=0;
std::string filename = path_processor_type::evaluate(*sym.get_filename(), feature);
boost::optional<mapnik::image_ptr> data;
if (is_svg(filename))
if (marker.is_vector())
{
// SVG
using namespace mapnik::svg;
boost::optional<path_ptr> marker;
typedef agg::pixfmt_rgba32_plain pixfmt;
typedef agg::renderer_base<pixfmt> renderer_base;
ras_ptr->reset();
ras_ptr->gamma(agg::gamma_linear());
agg::scanline_u8 sl;
agg::rendering_buffer buf(pixmap_.raw_data(), width_, height_, width_ * 4);
pixfmt pixf(buf);
renderer_base renb(pixf);
renderer_solid ren(renb);
box2d<double> extent;
marker = marker_cache::instance()->find(filename, true);
if (marker && *marker)
{
box2d<double> const& bbox = (*marker)->bounding_box();
double x1 = bbox.minx();
double y1 = bbox.miny();
double x2 = bbox.maxx();
double y2 = bbox.maxy();
agg::trans_affine recenter = agg::trans_affine_translation(-0.5*(x1+x2),-0.5*(y1+y2));
vertex_stl_adapter<svg_path_storage> stl_storage((*marker)->source());
svg_path_adapter svg_path(stl_storage);
svg_renderer<svg_path_adapter,
agg::pod_bvector<path_attributes> > svg_renderer(svg_path,
(*marker)->attributes());
for (unsigned i=0; i<feature.num_geometries(); ++i)
{
geometry_type const& geom = feature.get_geometry(i);
geom.label_position(&x,&y);
prj_trans.backward(x,y,z);
t_.forward(&x,&y);
agg::trans_affine tr;
boost::array<double,6> const& m = sym.get_transform();
tr.load_from(&m[0]);
tr = recenter * tr;
tr *= agg::trans_affine_scaling(scale_factor_);
tr *= agg::trans_affine_translation(x, y);
tr.transform(&x1,&y1);
tr.transform(&x2,&y2);
extent.init(x1,y1,x2,y2);
if (sym.get_allow_overlap() ||
detector_.has_placement(extent))
{
svg_renderer.render(*ras_ptr, sl, ren, tr, sym.get_opacity());
if (!sym.get_ignore_placement())
detector_.insert(extent);
metawriter_with_properties writer = sym.get_metawriter();
if (writer.first)
{
writer.first->add_box(extent, feature, t_, writer.second);
}
}
}
}
box2d<double> const& bbox = (*marker.get_vector_data())->bounding_box();
double x1 = bbox.minx();
double y1 = bbox.miny();
double x2 = bbox.maxx();
double y2 = bbox.maxy();
agg::trans_affine recenter = agg::trans_affine_translation(0.5*(marker.width()-(x1+x2)),0.5*(marker.height()-(y1+y2)));
vertex_stl_adapter<svg_path_storage> stl_storage((*marker.get_vector_data())->source());
svg_path_adapter svg_path(stl_storage);
svg_renderer<svg_path_adapter,
agg::pod_bvector<path_attributes> > svg_renderer(svg_path,
(*marker.get_vector_data())->attributes());
agg::trans_affine mtx = recenter * tr;
mtx *= agg::trans_affine_scaling(scale_factor_);
mtx *= agg::trans_affine_translation(x, y);
svg_renderer.render(*ras_ptr, sl, renb, mtx, opacity, bbox);
}
else
{
if ( filename.empty() )
{
// default OGC 4x4 black square
data = boost::optional<mapnik::image_ptr>(new image_data_32(4,4));
(*data)->set(0xff000000);
}
else
{
data = mapnik::image_cache::instance()->find(filename,true);
}
//int px = int(floor(x - 0.5 * marker.width()));
// int py = int(floor(y - 0.5 * marker.height()));
if ( data )
pixmap_.set_rectangle_alpha2(**marker.get_bitmap_data(), x, y, opacity);
}
}
template <typename T>
void agg_renderer<T>::process(point_symbolizer const& sym,
Feature const& feature,
proj_transform const& prj_trans)
{
std::string filename = path_processor_type::evaluate(*sym.get_filename(), feature);
boost::optional<mapnik::marker_ptr> marker;
if ( !filename.empty() )
{
marker = marker_cache::instance()->find(filename, true);
}
else
{
marker.reset(boost::shared_ptr<mapnik::marker> (new mapnik::marker()));
}
if (marker)
{
for (unsigned i=0; i<feature.num_geometries(); ++i)
{
for (unsigned i=0; i<feature.num_geometries(); ++i)
geometry_type const& geom = feature.get_geometry(i);
double x;
double y;
double z=0;
geom.label_position(&x,&y);
prj_trans.backward(x,y,z);
t_.forward(&x,&y);
int w = (*marker)->width();
int h = (*marker)->height();
int px = int(floor(x - 0.5 * w));
int py = int(floor(y - 0.5 * h));
box2d<double> label_ext (px, py, px + w, py + h);
if (sym.get_allow_overlap() ||
detector_.has_placement(label_ext))
{
geometry_type const& geom = feature.get_geometry(i);
geom.label_position(&x,&y);
prj_trans.backward(x,y,z);
t_.forward(&x,&y);
int w = (*data)->width();
int h = (*data)->height();
int px = int(floor(x - 0.5 * w));
int py = int(floor(y - 0.5 * h));
box2d<double> label_ext (px, py, px + w, py + h);
if (sym.get_allow_overlap() ||
detector_.has_placement(label_ext))
{
pixmap_.set_rectangle_alpha2(*(*data), px, py, sym.get_opacity());
if (!sym.get_ignore_placement())
detector_.insert(label_ext);
metawriter_with_properties writer = sym.get_metawriter();
if (writer.first) writer.first->add_box(label_ext, feature, t_, writer.second);
}
agg::trans_affine tr;
boost::array<double,6> const& m = sym.get_transform();
tr.load_from(&m[0]);
render_marker(px,py,**marker,tr, sym.get_opacity());
if (!sym.get_ignore_placement())
detector_.insert(label_ext);
metawriter_with_properties writer = sym.get_metawriter();
if (writer.first) writer.first->add_box(label_ext, feature, t_, writer.second);
}
}
}
}
template void agg_renderer<image_32>::process(point_symbolizer const&,

View file

@ -24,7 +24,7 @@
// mapnik
#include <mapnik/agg_renderer.hpp>
#include <mapnik/agg_rasterizer.hpp>
#include <mapnik/image_cache.hpp>
#include <mapnik/marker_cache.hpp>
// agg
#include "agg_basics.h"
@ -73,7 +73,15 @@ void agg_renderer<T>::process(polygon_pattern_symbolizer const& sym,
ras_ptr->gamma(agg::gamma_linear());
std::string filename = path_processor_type::evaluate( *sym.get_filename(), feature);
boost::optional<image_ptr> pat = image_cache::instance()->find(filename,true);
boost::optional<mapnik::marker_ptr> marker;
if ( !filename.empty() )
{
marker = marker_cache::instance()->find(filename, true);
}
if (!marker || !(*marker)->is_bitmap()) return;
boost::optional<image_ptr> pat = (*marker)->get_bitmap_data();
if (!pat) return;

View file

@ -24,16 +24,13 @@
#include <mapnik/agg_renderer.hpp>
#include <mapnik/agg_rasterizer.hpp>
#include <mapnik/image_util.hpp>
#include <mapnik/image_cache.hpp>
#include <mapnik/svg/marker_cache.hpp>
#include <mapnik/marker_cache.hpp>
#include <mapnik/svg/svg_converter.hpp>
#include <mapnik/svg/svg_renderer.hpp>
#include <mapnik/svg/svg_path_adapter.hpp>
#include "agg_basics.h"
#include "agg_rendering_buffer.h"
#include "agg_pixfmt_rgba.h"
#include "agg_rasterizer_scanline_aa.h"
#include "agg_scanline_u.h"
namespace mapnik {
@ -44,12 +41,6 @@ void agg_renderer<T>::process(shield_symbolizer const& sym,
proj_transform const& prj_trans)
{
typedef coord_transform2<CoordTransform,geometry_type> path_type;
typedef agg::pixfmt_rgba32 pixfmt;
typedef agg::renderer_base<pixfmt> renderer_base;
typedef agg::renderer_scanline_aa_solid<renderer_base> renderer_solid;
metawriter_with_properties writer = sym.get_metawriter();
UnicodeString text;
if( sym.get_no_text() )
@ -77,310 +68,161 @@ void agg_renderer<T>::process(shield_symbolizer const& sym,
tr = agg::trans_affine_scaling(scale_factor_) * tr;
std::string filename = path_processor_type::evaluate( *sym.get_filename(), feature);
if (is_svg(filename)) //SVG Label
boost::optional<mapnik::marker_ptr> marker;
if ( !filename.empty() )
{
using namespace mapnik::svg;
boost::optional<path_ptr> marker = marker_cache::instance()->find(filename, true);
ras_ptr->reset();
ras_ptr->gamma(agg::gamma_linear());
agg::scanline_u8 sl;
agg::rendering_buffer buf(pixmap_.raw_data(), width_, height_, width_ * 4);
pixfmt pixf(buf);
renderer_base renb(pixf);
renderer_solid ren(renb);
if (text.length() > 0 && marker && *marker)
{
face_set_ptr faces;
box2d<double> const& bbox = (*marker)->bounding_box();
double x1 = bbox.minx();
double y1 = bbox.miny();
double x2 = bbox.maxx();
double y2 = bbox.maxy();
agg::trans_affine recenter = agg::trans_affine_translation(-0.5*(x1+x2),-0.5*(y1+y2));
tr.transform(&x1,&y1);
tr.transform(&x2,&y2);
vertex_stl_adapter<svg_path_storage> stl_storage((*marker)->source());
svg_path_adapter svg_path(stl_storage);
svg_renderer<svg_path_adapter,
agg::pod_bvector<path_attributes> > svg_renderer(svg_path,
(*marker)->attributes());
int w = int(x2 - x1);
int h = int(y2 - y1);
if (sym.get_fontset().size() > 0)
{
faces = font_manager_.get_face_set(sym.get_fontset());
}
else
{
faces = font_manager_.get_face_set(sym.get_face_name());
}
stroker_ptr strk = font_manager_.get_stroker();
if (strk && faces->size() > 0)
{
text_renderer<T> text_ren(pixmap_, faces, *strk);
text_ren.set_pixel_size(sym.get_text_size() * scale_factor_);
text_ren.set_fill(sym.get_fill());
text_ren.set_halo_fill(sym.get_halo_fill());
text_ren.set_halo_radius(sym.get_halo_radius() * scale_factor_);
text_ren.set_opacity(sym.get_text_opacity());
placement_finder<label_collision_detector4> finder(detector_);
string_info info(text);
faces->get_string_info(info);
unsigned num_geom = feature.num_geometries();
for (unsigned i=0;i<num_geom;++i)
{
geometry_type const& geom = feature.get_geometry(i);
if (geom.num_points() > 0 )
{
path_type path(t_,geom,prj_trans);
position const& shield_pos = sym.get_shield_displacement();
label_placement_enum how_placed = sym.get_label_placement();
if (how_placed == POINT_PLACEMENT || how_placed == VERTEX_PLACEMENT)
{
// for every vertex, try and place a shield/text
geom.rewind(0);
for( unsigned jj = 0; jj < geom.num_points(); jj++ )
{
double label_x;
double label_y;
double z=0.0;
placement text_placement(info, sym, scale_factor_, w, h, false);
text_placement.avoid_edges = sym.get_avoid_edges();
text_placement.allow_overlap = sym.get_allow_overlap();
if( how_placed == VERTEX_PLACEMENT )
geom.vertex(&label_x,&label_y); // by vertex
else
geom.middle_point(&label_x, &label_y); // by middle of line
prj_trans.backward(label_x,label_y, z);
t_.forward(&label_x,&label_y);
// position SVG label at vertex + shield_displacement
label_x = label_x + boost::get<0>(shield_pos) * scale_factor_;
label_y = label_y + boost::get<1>(shield_pos) * scale_factor_;
// position text relative to label position
// NOTE: (text) displacement applied in placement_finder.cpp FIXME!
finder.find_point_placement( text_placement,label_x,label_y,0.0,
sym.get_vertical_alignment(),
sym.get_line_spacing() * scale_factor_,
sym.get_character_spacing() * scale_factor_,
sym.get_horizontal_alignment(),
sym.get_justify_alignment() );
// check to see if image overlaps anything too, there is only ever 1 placement found for points and verticies
if( text_placement.placements.size() > 0)
{
double x = text_placement.placements[0].starting_x;
double y = text_placement.placements[0].starting_y;
box2d<double> label_ext;
label_ext.init( floor(label_x - 0.5 * w), floor(label_y - 0.5 * h),
ceil (label_x + 0.5 * w), ceil (label_y + 0.5 * h) );
if ( sym.get_allow_overlap() || detector_.has_placement(label_ext) )
{
agg::trans_affine matrix = recenter * tr * agg::trans_affine_translation(label_x, label_y);
svg_renderer.render(*ras_ptr, sl, ren, matrix, sym.get_opacity());
box2d<double> dim = text_ren.prepare_glyphs(&text_placement.placements[0]);
text_ren.render(x,y);
detector_.insert(label_ext);
finder.update_detector(text_placement);
if (writer.first) {
writer.first->add_box(label_ext, feature, t_, writer.second);
writer.first->add_text(text_placement, faces, feature, t_, writer.second);
}
}
}
}
}
else if (geom.num_points() > 1 && sym.get_label_placement() == LINE_PLACEMENT)
{
placement text_placement(info, sym, scale_factor_, w, h, true);
text_placement.avoid_edges = sym.get_avoid_edges();
finder.find_point_placements<path_type>(text_placement,path);
for (unsigned int ii = 0; ii < text_placement.placements.size(); ++ ii)
{
double x = floor(text_placement.placements[ii].starting_x);
double y = floor(text_placement.placements[ii].starting_y);
agg::trans_affine matrix = recenter * tr * agg::trans_affine_translation(x, y);
svg_renderer.render(*ras_ptr, sl, ren, matrix, sym.get_opacity());
if (writer.first) writer.first->add_box(box2d<double>(x,y,x+w,y+h), feature, t_, writer.second);
box2d<double> dim = text_ren.prepare_glyphs(&text_placement.placements[ii]);
text_ren.render(x,y);
}
finder.update_detector(text_placement);
if (writer.first) writer.first->add_text(text_placement, faces, feature, t_, writer.second);
}
}
}
}
}
marker = marker_cache::instance()->find(filename, true);
}
else
{
boost::optional<image_ptr> data = image_cache::instance()->find(filename,true);
marker.reset(boost::shared_ptr<mapnik::marker> (new mapnik::marker()));
}
if (text.length() > 0 && data)
if (text.length() > 0 && marker)
{
face_set_ptr faces;
if (sym.get_fontset().size() > 0)
{
face_set_ptr faces;
faces = font_manager_.get_face_set(sym.get_fontset());
}
else
{
faces = font_manager_.get_face_set(sym.get_face_name());
}
if (sym.get_fontset().size() > 0)
stroker_ptr strk = font_manager_.get_stroker();
if (strk && faces->size() > 0)
{
text_renderer<T> ren(pixmap_, faces, *strk);
ren.set_pixel_size(sym.get_text_size() * scale_factor_);
ren.set_fill(sym.get_fill());
ren.set_halo_fill(sym.get_halo_fill());
ren.set_halo_radius(sym.get_halo_radius() * scale_factor_);
ren.set_opacity(sym.get_text_opacity());
placement_finder<label_collision_detector4> finder(detector_);
string_info info(text);
faces->get_string_info(info);
int w = (*marker)->width();
int h = (*marker)->height();
metawriter_with_properties writer = sym.get_metawriter();
for (unsigned i = 0; i < feature.num_geometries(); ++i)
{
faces = font_manager_.get_face_set(sym.get_fontset());
}
else
{
faces = font_manager_.get_face_set(sym.get_face_name());
}
stroker_ptr strk = font_manager_.get_stroker();
if (strk && faces->size() > 0)
{
text_renderer<T> ren(pixmap_, faces, *strk);
ren.set_pixel_size(sym.get_text_size() * scale_factor_);
ren.set_fill(sym.get_fill());
ren.set_halo_fill(sym.get_halo_fill());
ren.set_halo_radius(sym.get_halo_radius() * scale_factor_);
ren.set_opacity(sym.get_text_opacity());
placement_finder<label_collision_detector4> finder(detector_);
string_info info(text);
faces->get_string_info(info);
int w = (*data)->width();
int h = (*data)->height();
unsigned num_geom = feature.num_geometries();
for (unsigned i=0;i<num_geom;++i)
geometry_type const& geom = feature.get_geometry(i);
if (geom.num_points() > 0 )
{
geometry_type const& geom = feature.get_geometry(i);
if (geom.num_points() > 0 )
path_type path(t_,geom,prj_trans);
label_placement_enum how_placed = sym.get_label_placement();
if (how_placed == POINT_PLACEMENT || how_placed == VERTEX_PLACEMENT)
{
path_type path(t_,geom,prj_trans);
label_placement_enum how_placed = sym.get_label_placement();
if (how_placed == POINT_PLACEMENT || how_placed == VERTEX_PLACEMENT)
// for every vertex, try and place a shield/text
geom.rewind(0);
placement text_placement(info, sym, scale_factor_, w, h, false);
text_placement.avoid_edges = sym.get_avoid_edges();
text_placement.allow_overlap = sym.get_allow_overlap();
position const& pos = sym.get_displacement();
position const& shield_pos = sym.get_shield_displacement();
for( unsigned jj = 0; jj < geom.num_points(); jj++ )
{
// for every vertex, try and place a shield/text
geom.rewind(0);
placement text_placement(info, sym, scale_factor_, w, h, false);
text_placement.avoid_edges = sym.get_avoid_edges();
text_placement.allow_overlap = sym.get_allow_overlap();
position const& pos = sym.get_displacement();
position const& shield_pos = sym.get_shield_displacement();
for( unsigned jj = 0; jj < geom.num_points(); jj++ )
double label_x;
double label_y;
double z=0.0;
if( how_placed == VERTEX_PLACEMENT )
geom.vertex(&label_x,&label_y); // by vertex
else
geom.label_position(&label_x, &label_y); // by middle of line or by point
prj_trans.backward(label_x,label_y, z);
t_.forward(&label_x,&label_y);
label_x += boost::get<0>(shield_pos);
label_y += boost::get<1>(shield_pos);
finder.find_point_placement( text_placement,label_x,label_y,0.0,
sym.get_vertical_alignment(),
sym.get_line_spacing(),
sym.get_character_spacing(),
sym.get_horizontal_alignment(),
sym.get_justify_alignment() );
// check to see if image overlaps anything too, there is only ever 1 placement found for points and verticies
if( text_placement.placements.size() > 0)
{
double label_x;
double label_y;
double z=0.0;
if( how_placed == VERTEX_PLACEMENT )
geom.vertex(&label_x,&label_y); // by vertex
else
geom.label_position(&label_x, &label_y); // by middle of line or by point
prj_trans.backward(label_x,label_y, z);
t_.forward(&label_x,&label_y);
label_x += boost::get<0>(shield_pos);
label_y += boost::get<1>(shield_pos);
finder.find_point_placement( text_placement,label_x,label_y,0.0,
sym.get_vertical_alignment(),
sym.get_line_spacing(),
sym.get_character_spacing(),
sym.get_horizontal_alignment(),
sym.get_justify_alignment() );
double x = floor(text_placement.placements[0].starting_x);
double y = floor(text_placement.placements[0].starting_y);
int px;
int py;
box2d<double> label_ext;
// check to see if image overlaps anything too, there is only ever 1 placement found for points and verticies
if( text_placement.placements.size() > 0)
if( !sym.get_unlock_image() )
{
double x = floor(text_placement.placements[0].starting_x);
double y = floor(text_placement.placements[0].starting_y);
int px;
int py;
box2d<double> label_ext;
// center image at text center position
// remove displacement from image label
double lx = x - boost::get<0>(pos);
double ly = y - boost::get<1>(pos);
px=int(floor(lx - (0.5 * w)));
py=int(floor(ly - (0.5 * h)));
label_ext.init( floor(lx - 0.5 * w), floor(ly - 0.5 * h), ceil (lx + 0.5 * w), ceil (ly + 0.5 * h) );
}
else
{ // center image at reference location
px=int(floor(label_x - 0.5 * w));
py=int(floor(label_y - 0.5 * h));
label_ext.init( floor(label_x - 0.5 * w), floor(label_y - 0.5 * h), ceil (label_x + 0.5 * w), ceil (label_y + 0.5 * h));
}
if( !sym.get_unlock_image() )
{
// center image at text center position
// remove displacement from image label
double lx = x - boost::get<0>(pos);
double ly = y - boost::get<1>(pos);
px=int(floor(lx - (0.5 * w)));
py=int(floor(ly - (0.5 * h)));
label_ext.init( floor(lx - 0.5 * w), floor(ly - 0.5 * h), ceil (lx + 0.5 * w), ceil (ly + 0.5 * h) );
}
else
{ // center image at reference location
px=int(floor(label_x - 0.5 * w));
py=int(floor(label_y - 0.5 * h));
label_ext.init( floor(label_x - 0.5 * w), floor(label_y - 0.5 * h), ceil (label_x + 0.5 * w), ceil (label_y + 0.5 * h));
}
if ( sym.get_allow_overlap() || detector_.has_placement(label_ext) )
{
render_marker(px,py,**marker,tr,sym.get_opacity());
if ( sym.get_allow_overlap() || detector_.has_placement(label_ext) )
{
//pixmap_.set_rectangle_alpha(px,py,*data);
pixmap_.set_rectangle_alpha2(*(*data),px,py,sym.get_opacity());
box2d<double> dim = ren.prepare_glyphs(&text_placement.placements[0]);
ren.render(x,y);
detector_.insert(label_ext);
finder.update_detector(text_placement);
if (writer.first) {
writer.first->add_box(box2d<double>(px,py,px+w,py+h), feature, t_, writer.second);
writer.first->add_text(text_placement, faces, feature, t_, writer.second);
}
box2d<double> dim = ren.prepare_glyphs(&text_placement.placements[0]);
ren.render(x,y);
detector_.insert(label_ext);
finder.update_detector(text_placement);
if (writer.first) {
writer.first->add_box(box2d<double>(px,py,px+w,py+h), feature, t_, writer.second);
writer.first->add_text(text_placement, faces, feature, t_, writer.second);
}
}
}
}
}
else if (geom.num_points() > 1 && sym.get_label_placement() == LINE_PLACEMENT)
else if (geom.num_points() > 1 && how_placed == LINE_PLACEMENT)
{
placement text_placement(info, sym, scale_factor_, w, h, true);
text_placement.avoid_edges = sym.get_avoid_edges();
finder.find_point_placements<path_type>(text_placement,path);
position const& pos = sym.get_displacement();
for (unsigned int ii = 0; ii < text_placement.placements.size(); ++ ii)
{
placement text_placement(info, sym, scale_factor_, w, h, true);
text_placement.avoid_edges = sym.get_avoid_edges();
finder.find_point_placements<path_type>(text_placement,path);
position const& pos = sym.get_displacement();
for (unsigned int ii = 0; ii < text_placement.placements.size(); ++ ii)
{
double x = floor(text_placement.placements[ii].starting_x);
double y = floor(text_placement.placements[ii].starting_y);
double lx = x - boost::get<0>(pos);
double ly = y - boost::get<1>(pos);
int px=int(floor(lx - (0.5*w)));
int py=int(floor(ly - (0.5*h)));
//pixmap_.set_rectangle_alpha(px,py,*data);
pixmap_.set_rectangle_alpha2(*(*data),px,py,sym.get_opacity());
if (writer.first) writer.first->add_box(box2d<double>(px,py,px+w,py+h), feature, t_, writer.second);
box2d<double> dim = ren.prepare_glyphs(&text_placement.placements[ii]);
ren.render(x,y);
}
finder.update_detector(text_placement);
if (writer.first) writer.first->add_text(text_placement, faces, feature, t_, writer.second);
double x = floor(text_placement.placements[ii].starting_x);
double y = floor(text_placement.placements[ii].starting_y);
double lx = x - boost::get<0>(pos);
double ly = y - boost::get<1>(pos);
int px=int(floor(lx - (0.5*w)));
int py=int(floor(ly - (0.5*h)));
render_marker(px,py,**marker,tr,sym.get_opacity());
if (writer.first) writer.first->add_box(box2d<double>(px,py,px+w,py+h), feature, t_, writer.second);
box2d<double> dim = ren.prepare_glyphs(&text_placement.placements[ii]);
ren.render(x,y);
}
finder.update_detector(text_placement);
if (writer.first) writer.first->add_text(text_placement, faces, feature, t_, writer.second);
}
}
}

View file

@ -32,7 +32,10 @@
#include <mapnik/arrow.hpp>
#include <mapnik/config_error.hpp>
#include <mapnik/parse_path.hpp>
#include <mapnik/image_cache.hpp>
#include <mapnik/marker_cache.hpp>
#include <mapnik/svg/svg_path_adapter.hpp>
#include <mapnik/svg/svg_path_attributes.hpp>
// cairo
#include <cairomm/context.h>
#include <cairomm/surface.h>
@ -123,6 +126,62 @@ private:
Cairo::RefPtr<Cairo::SurfacePattern> pattern_;
};
class cairo_gradient : private boost::noncopyable
{
public:
cairo_gradient(const mapnik::gradient &grad, double opacity=1.0)
{
double x1,x2,y1,y2,r;
grad.get_control_points(x1,y1,x2,y2,r);
if (grad.get_gradient_type() == LINEAR)
{
pattern_ = Cairo::LinearGradient::create(x1, y1, x2, y2);
}
else if (grad.get_gradient_type() == RADIAL)
{
pattern_ = Cairo::RadialGradient::create(x1, y1, 0, x2, y2, r);
}
units_ = grad.get_units();
BOOST_FOREACH ( mapnik::stop_pair const& st, grad.get_stop_array() )
{
mapnik::color const& stop_color = st.second;
double r= static_cast<double> (stop_color.red())/255.0;
double g= static_cast<double> (stop_color.green())/255.0;
double b= static_cast<double> (stop_color.blue())/255.0;
double a= static_cast<double> (stop_color.alpha())/255.0;
pattern_->add_color_stop_rgba(st.first, r, g, b, a*opacity);
}
double m[6];
agg::trans_affine tr = grad.get_transform();
tr.invert();
tr.store_to(m);
pattern_->set_matrix(Cairo::Matrix(m[0],m[1],m[2],m[3],m[4],m[5]));
}
~cairo_gradient(void)
{
}
Cairo::RefPtr<Cairo::Gradient> const& gradient(void) const
{
return pattern_;
}
gradient_unit_e units() const
{
return units_;
}
private:
Cairo::RefPtr<Cairo::Gradient> pattern_;
gradient_unit_e units_;
};
class cairo_face : private boost::noncopyable
{
public:
@ -265,6 +324,11 @@ public:
context_->set_dash(d, 0.0);
}
void set_fill_rule(Cairo::FillRule fill_rule)
{
context_->set_fill_rule(fill_rule);
}
void move_to(double x, double y)
{
#if CAIRO_VERSION < CAIRO_VERSION_ENCODE(1, 6, 0)
@ -277,6 +341,15 @@ public:
context_->move_to(x, y);
}
void curve_to(double ct1_x, double ct1_y, double ct2_x, double ct2_y, double end_x, double end_y)
{
context_->curve_to(ct1_x,ct1_y,ct2_x,ct2_y,end_x,end_y);
}
void close_path()
{
context_->close_path();
}
void line_to(double x, double y)
{
@ -291,11 +364,11 @@ public:
}
template <typename T>
void add_path(T& path)
void add_path(T& path, unsigned start_index = 0)
{
double x, y;
path.rewind(0);
path.rewind(start_index);
for (unsigned cm = path.vertex(&x, &y); cm != SEG_END; cm = path.vertex(&x, &y))
{
@ -310,6 +383,63 @@ public:
}
}
template <typename T>
void add_agg_path(T& path, unsigned start_index = 0)
{
double x, y;
path.rewind(start_index);
for (unsigned cm = path.vertex(&x, &y); !agg::is_stop(cm); cm = path.vertex(&x, &y))
{
if (agg::is_move_to(cm))
{
move_to(x, y);
}
else if (agg::is_drawing(cm))
{
if (agg::is_curve3(cm))
{
double end_x;
double end_y;
std::cerr << "Curve 3 not implemented" << std::endl;
path.vertex(&end_x, &end_y);
curve_to(x,y,x,y,end_x,end_y);
}
else if (agg::is_curve4(cm))
{
double ct2_x;
double ct2_y;
double end_x;
double end_y;
path.vertex(&ct2_x, &ct2_y);
path.vertex(&end_x, &end_y);
curve_to(x,y,ct2_x,ct2_y,end_x,end_y);
}
else if (agg::is_line_to(cm))
{
line_to(x, y);
}
else
{
std::cerr << "Unimplemented drawing command: " << cm << std::endl;
move_to(x, y);
}
}
else if (agg::is_close(cm))
{
close_path();
}
else
{
std::cerr << "Unimplemented path command: " << cm << std::endl;
}
}
}
void rectangle(double x, double y, double w, double h)
{
context_->rectangle(x, y, w, h);
@ -335,6 +465,29 @@ public:
context_->set_source(pattern.pattern());
}
void set_gradient(cairo_gradient const& pattern, const box2d<double> &bbox)
{
Cairo::RefPtr<Cairo::Gradient> p = pattern.gradient();
double bx1=bbox.minx();
double by1=bbox.miny();
double bx2=bbox.maxx();
double by2=bbox.maxy();
if (pattern.units() != USER_SPACE_ON_USE)
{
if (pattern.units() == OBJECT_BOUNDING_BOX)
{
context_->get_path_extents (bx1, by1, bx2, by2);
}
Cairo::Matrix m = p->get_matrix();
m.scale(1.0/(bx2-bx1),1.0/(by2-by1));
m.translate(-bx1,-by1);
p->set_matrix(m);
}
context_->set_source(p);
}
void add_image(double x, double y, image_data_32 & data, double opacity = 1.0)
{
cairo_pattern pattern(data);
@ -362,6 +515,26 @@ public:
context_->set_matrix(matrix);
}
void transform(Cairo::Matrix const& matrix)
{
context_->transform(matrix);
}
void translate(double x, double y)
{
context_->translate(x,y);
}
void save()
{
context_->save();
}
void restore()
{
context_->restore();
}
void show_glyph(unsigned long index, double x, double y)
{
Cairo::Glyph glyph;
@ -734,25 +907,123 @@ void cairo_renderer_base::process(line_symbolizer const& sym,
}
}
void cairo_renderer_base::render_marker(const int x, const int y, marker &marker, const agg::trans_affine & tr, double opacity)
{
cairo_context context(context_);
if (marker.is_vector())
{
box2d<double> bbox;
bbox = (*marker.get_vector_data())->bounding_box();
coord<double,2> c = bbox.center();
agg::trans_affine recenter = agg::trans_affine_translation(0.5 * marker.width()-c.x,0.5 * marker.height()-c.y);
agg::trans_affine mtx = tr;
mtx = recenter * mtx;
mtx *= agg::trans_affine_translation(x, y);
typedef coord_transform2<CoordTransform,geometry_type> path_type;
mapnik::path_ptr vmarker = *marker.get_vector_data();
agg::pod_bvector<path_attributes> const & attributes_ = vmarker->attributes();
for(unsigned i = 0; i < attributes_.size(); ++i)
{
mapnik::svg::path_attributes const& attr = attributes_[i];
if (!attr.visibility_flag)
continue;
context.save();
agg::trans_affine transform = attr.transform;
transform *= mtx;
if (transform.is_valid() && !transform.is_identity())
{
double m[6];
transform.store_to(m);
context.transform(Cairo::Matrix(m[0],m[1],m[2],m[3],m[4],m[5]));
}
vertex_stl_adapter<svg_path_storage> stl_storage(vmarker->source());
svg_path_adapter svg_path(stl_storage);
if (attr.fill_flag || attr.fill_gradient.get_gradient_type() != NO_GRADIENT)
{
context.add_agg_path(svg_path,attr.index);
if (attr.even_odd_flag)
{
context.set_fill_rule(Cairo::FILL_RULE_EVEN_ODD);
}
else
{
context.set_fill_rule(Cairo::FILL_RULE_WINDING);
}
if(attr.fill_gradient.get_gradient_type() != NO_GRADIENT)
{
cairo_gradient g(attr.fill_gradient,attr.opacity*opacity);
context.set_gradient(g,bbox);
context.fill();
}
else if(attr.fill_flag)
{
context.set_color(attr.fill_color.r,attr.fill_color.g,attr.fill_color.b,attr.opacity*opacity);
context.fill();
}
}
if(attr.stroke_gradient.get_gradient_type() != NO_GRADIENT || attr.stroke_flag)
{
context.add_agg_path(svg_path,attr.index);
if(attr.stroke_gradient.get_gradient_type() != NO_GRADIENT)
{
context.set_line_width(attr.stroke_width);
context.set_line_cap(line_cap_enum(attr.line_cap));
context.set_line_join(line_join_enum(attr.line_join));
context.set_miter_limit(attr.miter_limit);
cairo_gradient g(attr.stroke_gradient,attr.opacity*opacity);
context.set_gradient(g,bbox);
context.stroke();
}
else if(attr.stroke_flag)
{
context.set_color(attr.stroke_color.r,attr.stroke_color.g,attr.stroke_color.b,attr.opacity*opacity);
context.set_line_width(attr.stroke_width);
context.set_line_cap(line_cap_enum(attr.line_cap));
context.set_line_join(line_join_enum(attr.line_join));
context.set_miter_limit(attr.miter_limit);
context.stroke();
}
}
context.restore();
}
}
else if (marker.is_bitmap())
{
context.add_image(x, y, **marker.get_bitmap_data(), opacity);
}
}
void cairo_renderer_base::process(point_symbolizer const& sym,
Feature const& feature,
proj_transform const& prj_trans)
{
std::string filename = path_processor_type::evaluate( *sym.get_filename(), feature);
boost::optional<mapnik::image_ptr> data;
if ( filename.empty() )
boost::optional<marker_ptr> marker;
if ( !filename.empty() )
{
// default OGC 4x4 black pixel
data = boost::optional<mapnik::image_ptr>(new image_data_32(4,4));
(*data)->set(0xff000000);
marker = marker_cache::instance()->find(filename, true);
}
else
{
data = mapnik::image_cache::instance()->find(filename,true);
marker.reset(boost::shared_ptr<mapnik::marker> (new mapnik::marker()));
}
if (data)
if (marker)
{
for (unsigned i = 0; i < feature.num_geometries(); ++i)
{
@ -760,23 +1031,28 @@ void cairo_renderer_base::process(point_symbolizer const& sym,
double x;
double y;
double z = 0;
geom.label_position(&x, &y);
prj_trans.backward(x, y, z);
t_.forward(&x, &y);
int w = (*data)->width();
int h = (*data)->height();
int w = (*marker)->width();
int h = (*marker)->height();
int px = int(floor(x - 0.5 * w));
int py = int(floor(y - 0.5 * h));
box2d<double> label_ext (px, py, px + w, py + h);
if (sym.get_allow_overlap() ||
detector_.has_placement(label_ext))
detector_.has_placement(label_ext))
{
cairo_context context(context_);
agg::trans_affine mtx;
boost::array<double,6> const& m = sym.get_transform();
mtx.load_from(&m[0]);
context.add_image(px, py, *(*data), sym.get_opacity());
detector_.insert(label_ext);
render_marker(px,py,**marker, mtx, sym.get_opacity());
if (!sym.get_ignore_placement())
detector_.insert(label_ext);
metawriter_with_properties writer = sym.get_metawriter();
if (writer.first)
{
@ -793,15 +1069,42 @@ void cairo_renderer_base::process(shield_symbolizer const& sym,
{
typedef coord_transform2<CoordTransform,geometry_type> path_type;
expression_ptr name_expr = sym.get_name();
if (!name_expr) return;
value_type result = boost::apply_visitor(evaluate<Feature,value_type>(feature),*name_expr);
UnicodeString text = result.to_unicode();
UnicodeString text;
if( sym.get_no_text() )
text = UnicodeString( " " ); // TODO: fix->use 'space' as the text to render
else
{
expression_ptr name_expr = sym.get_name();
if (!name_expr) return;
value_type result = boost::apply_visitor(evaluate<Feature,value_type>(feature),*name_expr);
text = result.to_unicode();
}
if ( sym.get_text_transform() == UPPERCASE)
{
text = text.toUpper();
}
else if ( sym.get_text_transform() == LOWERCASE)
{
text = text.toLower();
}
agg::trans_affine tr;
boost::array<double,6> const& m = sym.get_transform();
tr.load_from(&m[0]);
std::string filename = path_processor_type::evaluate( *sym.get_filename(), feature);
boost::optional<mapnik::image_ptr> data = mapnik::image_cache::instance()->find(filename,true);
if (text.length() > 0 && data)
boost::optional<marker_ptr> marker;
if ( !filename.empty() )
{
marker = marker_cache::instance()->find(filename, true);
}
else
{
marker.reset(boost::shared_ptr<mapnik::marker> (new mapnik::marker()));
}
if (text.length() > 0 && marker)
{
face_set_ptr faces;
@ -819,85 +1122,124 @@ void cairo_renderer_base::process(shield_symbolizer const& sym,
cairo_context context(context_);
string_info info(text);
placement_finder<label_collision_detector4> finder(detector_);
faces->set_pixel_sizes(sym.get_text_size());
faces->get_string_info(info);
placement_finder<label_collision_detector4> finder(detector_);
int w = (*data)->width();
int h = (*data)->height();
int w = (*marker)->width();
int h = (*marker)->height();
metawriter_with_properties writer = sym.get_metawriter();
for (unsigned i = 0; i < feature.num_geometries(); ++i)
{
geometry_type const& geom = feature.get_geometry(i);
if (geom.num_points() > 0) // don't bother with empty geometries
{
path_type path(t_, geom, prj_trans);
if (sym.get_label_placement() == POINT_PLACEMENT)
label_placement_enum how_placed = sym.get_label_placement();
if (how_placed == POINT_PLACEMENT || how_placed == VERTEX_PLACEMENT)
{
double label_x;
double label_y;
double z = 0.0;
// for every vertex, try and place a shield/text
geom.rewind(0);
placement text_placement(info, sym, 1.0, w, h, false);
text_placement.avoid_edges = sym.get_avoid_edges();
geom.label_position(&label_x, &label_y);
prj_trans.backward(label_x, label_y, z);
t_.forward(&label_x, &label_y);
finder.find_point_placement(text_placement, label_x, label_y);
text_placement.allow_overlap = sym.get_allow_overlap();
position const& pos = sym.get_displacement();
position const& shield_pos = sym.get_shield_displacement();
for( unsigned jj = 0; jj < geom.num_points(); jj++ )
{
double label_x;
double label_y;
double z=0.0;
for (unsigned int ii = 0; ii < text_placement.placements.size(); ++ ii)
{
double x = text_placement.placements[ii].starting_x;
double y = text_placement.placements[ii].starting_y;
// remove displacement from image label
position pos = sym.get_displacement();
double lx = x - boost::get<0>(pos);
double ly = y - boost::get<1>(pos);
int px = int(lx - (0.5 * w)) ;
int py = int(ly - (0.5 * h)) ;
box2d<double> label_ext (floor(lx - 0.5 * w), floor(ly - 0.5 * h), ceil (lx + 0.5 * w), ceil (ly + 0.5 * h));
if( how_placed == VERTEX_PLACEMENT )
geom.vertex(&label_x,&label_y); // by vertex
else
geom.label_position(&label_x, &label_y); // by middle of line or by point
prj_trans.backward(label_x,label_y, z);
t_.forward(&label_x,&label_y);
if (detector_.has_placement(label_ext))
{
context.add_image(px, py, *(*data));
context.add_text(text_placement.placements[ii],
face_manager_,
faces,
sym.get_text_size(),
sym.get_fill(),
sym.get_halo_radius(),
sym.get_halo_fill()
);
if (writer.first) {
writer.first->add_box(box2d<double>(px,py,px+w,py+h), feature, t_, writer.second);
writer.first->add_text(text_placement, faces, feature, t_, writer.second); //Only 1 placement
label_x += boost::get<0>(shield_pos);
label_y += boost::get<1>(shield_pos);
finder.find_point_placement( text_placement,label_x,label_y,0.0,
sym.get_vertical_alignment(),
sym.get_line_spacing(),
sym.get_character_spacing(),
sym.get_horizontal_alignment(),
sym.get_justify_alignment() );
for (unsigned int ii = 0; ii < text_placement.placements.size(); ++ ii)
{
double x = text_placement.placements[ii].starting_x;
double y = text_placement.placements[ii].starting_y;
int px;
int py;
box2d<double> label_ext;
if( !sym.get_unlock_image() )
{
// center image at text center position
// remove displacement from image label
double lx = x - boost::get<0>(pos);
double ly = y - boost::get<1>(pos);
px=int(floor(lx - (0.5 * w)));
py=int(floor(ly - (0.5 * h)));
label_ext.init( floor(lx - 0.5 * w), floor(ly - 0.5 * h), ceil (lx + 0.5 * w), ceil (ly + 0.5 * h) );
}
else
{ // center image at reference location
px=int(floor(label_x - 0.5 * w));
py=int(floor(label_y - 0.5 * h));
label_ext.init( floor(label_x - 0.5 * w), floor(label_y - 0.5 * h), ceil (label_x + 0.5 * w), ceil (label_y + 0.5 * h));
}
detector_.insert(label_ext);
}
}
finder.update_detector(text_placement);
if ( sym.get_allow_overlap() || detector_.has_placement(label_ext) )
{
render_marker(px,py,**marker, tr, sym.get_opacity());
context.add_text(text_placement.placements[ii],
face_manager_,
faces,
sym.get_text_size(),
sym.get_fill(),
sym.get_halo_radius(),
sym.get_halo_fill()
);
if (writer.first) {
writer.first->add_box(box2d<double>(px,py,px+w,py+h), feature, t_, writer.second);
writer.first->add_text(text_placement, faces, feature, t_, writer.second); //Only 1 placement
}
detector_.insert(label_ext);
}
}
finder.update_detector(text_placement);
}
}
else if (geom.num_points() > 1 && sym.get_label_placement() == LINE_PLACEMENT)
else if (geom.num_points() > 1 && how_placed == LINE_PLACEMENT)
{
placement text_placement(info, sym, 1.0, w, h, true);
text_placement.avoid_edges = sym.get_avoid_edges();
finder.find_point_placements<path_type>(text_placement, path);
position const& pos = sym.get_displacement();
for (unsigned int ii = 0; ii < text_placement.placements.size(); ++ ii)
{
double x = text_placement.placements[ii].starting_x;
double y = text_placement.placements[ii].starting_y;
int px = int(x - (w/2));
int py = int(y - (h/2));
double lx = x - boost::get<0>(pos);
double ly = y - boost::get<1>(pos);
int px=int(floor(lx - (0.5*w)));
int py=int(floor(ly - (0.5*h)));
render_marker(x,y,**marker, tr, sym.get_opacity());
context.add_image(px, py, *(*data));
context.add_text(text_placement.placements[ii],
face_manager_,
faces,
@ -924,14 +1266,14 @@ void cairo_renderer_base::process(line_pattern_symbolizer const& sym,
typedef coord_transform2<CoordTransform,geometry_type> path_type;
std::string filename = path_processor_type::evaluate( *sym.get_filename(), feature);
boost::optional<mapnik::image_ptr> image = mapnik::image_cache::instance()->find(filename,true);
if (!image) return;
boost::optional<mapnik::marker_ptr> marker = mapnik::marker_cache::instance()->find(filename,true);
if (!marker && !(*marker)->is_bitmap()) return;
unsigned width((*image)->width());
unsigned height((*image)->height());
unsigned width((*marker)->width());
unsigned height((*marker)->height());
cairo_context context(context_);
cairo_pattern pattern(*(*image));
cairo_pattern pattern(**((*marker)->get_bitmap_data()));
pattern.set_extend(Cairo::EXTEND_REPEAT);
pattern.set_filter(Cairo::FILTER_BILINEAR);
@ -994,11 +1336,10 @@ void cairo_renderer_base::process(polygon_pattern_symbolizer const& sym,
cairo_context context(context_);
std::string filename = path_processor_type::evaluate( *sym.get_filename(), feature);
boost::optional<mapnik::image_ptr> image = mapnik::image_cache::instance()->find(filename,true);
boost::optional<mapnik::marker_ptr> marker = mapnik::marker_cache::instance()->find(filename,true);
if (!marker && !(*marker)->is_bitmap()) return;
if (!image) return;
cairo_pattern pattern(*(*image));
cairo_pattern pattern(**((*marker)->get_bitmap_data()));
pattern.set_extend(Cairo::EXTEND_REPEAT);

143
src/gradient.cpp Normal file
View file

@ -0,0 +1,143 @@
/*****************************************************************************
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2010 Artem Pavelenko
*
* 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 <mapnik/gradient.hpp>
#include <iostream>
namespace mapnik
{
static const char * gradient_strings[] = {
"no-gradient",
"linear",
"radial",
""
};
IMPLEMENT_ENUM( gradient_e, gradient_strings );
gradient::gradient()
: gradient_type_(NO_GRADIENT),
stops_(),
x1_(0),
y1_(0),
x2_(0),
y2_(0),
r_(0),
units_(OBJECT_BOUNDING_BOX)
{
}
gradient::gradient(gradient const& other)
: gradient_type_(other.gradient_type_),
stops_(other.stops_),
x1_(other.x1_),
y1_(other.y1_),
x2_(other.x2_),
y2_(other.y2_),
r_(other.r_),
units_(other.units_),
transform_(other.transform_)
{}
gradient & gradient::operator=(const gradient& rhs)
{
gradient tmp(rhs);
swap(tmp);
return *this;
}
void gradient::set_gradient_type(gradient_e grad)
{
gradient_type_=grad;
}
gradient_e gradient::get_gradient_type() const
{
return gradient_type_;
}
void gradient::set_transform(agg::trans_affine transform)
{
transform_ = transform;
}
agg::trans_affine gradient::get_transform() const
{
return transform_;
}
void gradient::set_units(gradient_unit_e units)
{
units_ = units;
}
gradient_unit_e gradient::get_units() const
{
return units_;
}
void gradient::add_stop(double offset,mapnik::color const& c)
{
stops_.push_back(mapnik::stop_pair(offset,c));
}
bool gradient::has_stop() const
{
return ! stops_.empty();
}
stop_array const& gradient::get_stop_array() const
{
return stops_;
}
void gradient::swap(const gradient& other) throw()
{
gradient_type_=other.gradient_type_;
stops_=other.stops_;
units_=other.units_;
transform_=other.transform_;
other.get_control_points(x1_,y1_,x2_,y2_,r_);
}
void gradient::set_control_points(double x1, double y1, double x2, double y2, double r)
{
x1_ = x1;
y1_ = y1;
x2_ = x2;
y2_ = y2;
r_ = r;
}
void gradient::get_control_points(double &x1, double &y1, double &x2, double &y2, double &r) const
{
get_control_points(x1,y1,x2,y2);
r=r_;
}
void gradient::get_control_points(double &x1, double &y1, double &x2, double &y2) const
{
x1 = x1_;
y1 = y1_;
x2 = x2_;
y2 = y2_;
}
}

View file

@ -1,99 +0,0 @@
/*****************************************************************************
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2009 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
*
*****************************************************************************/
//$Id$
// mapnik
#include <mapnik/image_cache.hpp>
#include <mapnik/image_reader.hpp>
// boost
#include <boost/assert.hpp>
#include <boost/filesystem/operations.hpp>
#include <boost/algorithm/string.hpp>
namespace mapnik
{
boost::unordered_map<std::string, image_ptr> image_cache::cache_;
bool image_cache::insert (std::string const& uri, image_ptr img)
{
#ifdef MAPNIK_THREADSAFE
mutex::scoped_lock lock(mutex_);
#endif
return cache_.insert(std::make_pair(uri,img)).second;
}
boost::optional<image_ptr> image_cache::find(std::string const& uri, bool update_cache)
{
#ifdef MAPNIK_THREADSAFE
mutex::scoped_lock lock(mutex_);
#endif
typedef boost::unordered_map<std::string, image_ptr>::const_iterator iterator_type;
boost::optional<image_ptr> result;
iterator_type itr = cache_.find(uri);
if (itr != cache_.end())
{
result.reset(itr->second);
return result;
}
// we can't find image in cache, lets try to load it from filesystem
boost::filesystem::path path(uri);
if (exists(path))
{
try
{
std::auto_ptr<mapnik::image_reader> reader(mapnik::get_image_reader(uri));
if (reader.get())
{
unsigned width = reader->width();
unsigned height = reader->height();
BOOST_ASSERT(width > 0 && height > 0);
mapnik::image_ptr image(new mapnik::image_data_32(width,height));
reader->read(0,0,*image);
result.reset(image);
if (update_cache)
{
cache_.insert(std::make_pair(uri,image));
}
}
}
catch (...)
{
std::cerr << "Exception caught while loading image: " << uri << std::endl;
}
}
else
{
std::cerr << "### WARNING image does not exist: " << uri << std::endl;
}
return result;
}
#ifdef MAPNIK_THREADSAFE
boost::mutex image_cache::mutex_;
#endif
}

View file

@ -23,11 +23,13 @@
//$Id$
// mapnik
#include <mapnik/svg/marker_cache.hpp>
#include <mapnik/marker_cache.hpp>
#include <mapnik/svg/svg_parser.hpp>
#include <mapnik/svg/svg_storage.hpp>
#include <mapnik/svg/svg_path_adapter.hpp>
#include <mapnik/svg/svg_converter.hpp>
#include <mapnik/image_util.hpp>
#include <mapnik/image_reader.hpp>
// boost
#include <boost/assert.hpp>
@ -37,9 +39,9 @@
namespace mapnik
{
boost::unordered_map<std::string, path_ptr> marker_cache::cache_;
boost::unordered_map<std::string, marker_ptr> marker_cache::cache_;
bool marker_cache::insert (std::string const& uri, path_ptr path)
bool marker_cache::insert (std::string const& uri, marker_ptr path)
{
#ifdef MAPNIK_THREADSAFE
mutex::scoped_lock lock(mutex_);
@ -47,13 +49,13 @@ bool marker_cache::insert (std::string const& uri, path_ptr path)
return cache_.insert(std::make_pair(uri,path)).second;
}
boost::optional<path_ptr> marker_cache::find(std::string const& uri, bool update_cache)
boost::optional<marker_ptr> marker_cache::find(std::string const& uri, bool update_cache)
{
#ifdef MAPNIK_THREADSAFE
mutex::scoped_lock lock(mutex_);
#endif
typedef boost::unordered_map<std::string, path_ptr>::const_iterator iterator_type;
boost::optional<path_ptr> result;
typedef boost::unordered_map<std::string, marker_ptr>::const_iterator iterator_type;
boost::optional<marker_ptr> result;
iterator_type itr = cache_.find(uri);
if (itr != cache_.end())
{
@ -65,34 +67,66 @@ boost::optional<path_ptr> marker_cache::find(std::string const& uri, bool update
boost::filesystem::path path(uri);
if (exists(path))
{
using namespace mapnik::svg;
try
if (is_svg(uri))
{
path_ptr marker(new svg_storage_type);
vertex_stl_adapter<svg_path_storage> stl_storage(marker->source());
svg_path_adapter svg_path(stl_storage);
svg_converter_type svg(svg_path, marker->attributes());
svg_parser p(svg);
p.parse(uri);
//svg.arrange_orientations();
double lox,loy,hix,hiy;
svg.bounding_rect(&lox, &loy, &hix, &hiy);
marker->set_bounding_box(lox,loy,hix,hiy);
if (update_cache)
using namespace mapnik::svg;
try
{
cache_.insert(std::make_pair(uri,marker));
path_ptr marker_path(new 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());
svg_parser p(svg);
p.parse(uri);
//svg.arrange_orientations();
double lox,loy,hix,hiy;
svg.bounding_rect(&lox, &loy, &hix, &hiy);
marker_path->set_bounding_box(lox,loy,hix,hiy);
marker_ptr mark(new marker(marker_path));
result.reset(mark);
if (update_cache)
{
cache_.insert(std::make_pair(uri,*result));
}
return result;
}
catch (...)
{
std::cerr << "Exception caught while loading SVG: " << uri << std::endl;
}
return marker;
}
catch (...)
else
{
std::cerr << "Exception caught while loading SVG: " << uri << std::endl;
try
{
std::auto_ptr<mapnik::image_reader> reader(mapnik::get_image_reader(uri));
if (reader.get())
{
unsigned width = reader->width();
unsigned height = reader->height();
BOOST_ASSERT(width > 0 && height > 0);
mapnik::image_ptr image(new mapnik::image_data_32(width,height));
reader->read(0,0,*image);
marker_ptr mark(new marker(image));
result.reset(mark);
if (update_cache)
{
cache_.insert(std::make_pair(uri,*result));
}
}
}
catch (...)
{
std::cerr << "Exception caught while loading image: " << uri << std::endl;
}
}
}
else
{
std::cerr << "### WARNING SVG does not exist: " << uri << std::endl;
std::cerr << "### WARNING Marker does not exist: " << uri << std::endl;
}
return result;
}

View file

@ -27,10 +27,15 @@
#include "agg_ellipse.h"
#include "agg_rounded_rect.h"
#include "agg_span_gradient.h"
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/fusion/include/std_pair.hpp>
#include <boost/foreach.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/lexical_cast.hpp>
#include <iostream>
#include <string>
@ -40,6 +45,8 @@
namespace mapnik { namespace svg {
typedef std::vector<std::pair<double, agg::rgba8> > color_lookup_type;
namespace qi = boost::spirit::qi;
typedef std::vector<std::pair<std::string, std::string> > pairs_type;
@ -84,6 +91,30 @@ double parse_double(const char* str)
return val;
}
/*
* parse a double that might end with a %
* if it does then set the ref bool true and divide the result by 100
*/
double parse_double_optional_percent(const char* str, bool &percent)
{
using namespace boost::spirit::qi;
using boost::phoenix::ref;
double val = 0.0;
char unit='\0';
parse(str, str+ strlen(str),double_[ref(val)=_1] >> *char_('%')[ref(unit)=_1]);
if (unit =='%')
{
percent = true;
val/=100.0;
}
else
{
percent = false;
}
return val;
}
bool parse_style (const char* str, pairs_type & v)
{
using namespace boost::spirit::qi;
@ -179,6 +210,24 @@ void svg_parser::start_element(xmlTextReaderPtr reader)
{
parse_ellipse(reader);
}
// the gradient tags *should* be in defs, but illustrator seems not to put them in there so
// accept them anywhere
else if (xmlStrEqual(name, BAD_CAST "linearGradient"))
{
parse_linear_gradient(reader);
}
else if (xmlStrEqual(name, BAD_CAST "radialGradient"))
{
parse_radial_gradient(reader);
}
else if (xmlStrEqual(name, BAD_CAST "stop"))
{
parse_gradient_stop(reader);
}
else
{
std::clog << "unhandled element: " << name << "\n";
}
}
void svg_parser::end_element(xmlTextReaderPtr reader)
@ -193,6 +242,10 @@ void svg_parser::end_element(xmlTextReaderPtr reader)
{
is_defs_ = false;
}
else if ((xmlStrEqual(name, BAD_CAST "linearGradient")) || (xmlStrEqual(name, BAD_CAST "radialGradient")))
{
gradient_map_[temporary_gradient_.first] = temporary_gradient_.second;
}
}
void svg_parser::parse_attr(const xmlChar * name, const xmlChar * value )
@ -209,6 +262,21 @@ void svg_parser::parse_attr(const xmlChar * name, const xmlChar * value )
{
path_.fill_none();
}
else if (boost::starts_with((const char*)value, "url(#"))
{
// see if we have a known gradient fill
std::string id = std::string((const char*)&value[5]);
// get rid of the trailing )
id.erase(id.end()-1);
if (gradient_map_.count(id) > 0)
{
path_.add_fill_gradient(gradient_map_[id]);
}
else
{
std::cerr << "Failed to find gradient fill: " << id << std::endl;
}
}
else
{
path_.fill(parse_color((const char*) value));
@ -231,6 +299,21 @@ void svg_parser::parse_attr(const xmlChar * name, const xmlChar * value )
{
path_.stroke_none();
}
else if (boost::starts_with((const char*)value, "url(#"))
{
// see if we have a known gradient fill
std::string id = std::string((const char*)&value[5]);
// get rid of the trailing )
id.erase(id.end()-1);
if (gradient_map_.count(id) > 0)
{
path_.add_stroke_gradient(gradient_map_[id]);
}
else
{
std::cerr << "Failed to find gradient fill: " << id << std::endl;
}
}
else
{
path_.stroke(parse_color((const char*) value));
@ -276,6 +359,10 @@ void svg_parser::parse_attr(const xmlChar * name, const xmlChar * value )
double opacity = parse_double((const char*)value);
path_.opacity(opacity);
}
else if (xmlStrEqual(name, BAD_CAST "visibility"))
{
path_.visibility(!xmlStrEqual(value, BAD_CAST "hidden"));
}
}
@ -401,9 +488,6 @@ void svg_parser::parse_circle(xmlTextReaderPtr reader)
if(r != 0.0)
{
if(r < 0.0) throw std::runtime_error("parse_circle: Invalid radius");
//path_.move_to(cx+r,cy);
//path_.arc_to(r,r,0,1,0,cx-r,cy);
//path_.arc_to(r,r,0,1,0,cx+r,cy);
agg::ellipse c(cx, cy, r, r);
path_.storage().concat_path(c);
}
@ -435,9 +519,6 @@ void svg_parser::parse_ellipse(xmlTextReaderPtr reader)
{
if(rx < 0.0) throw std::runtime_error("parse_ellipse: Invalid rx");
if(ry < 0.0) throw std::runtime_error("parse_ellipse: Invalid ry");
//path_.move_to(cx+rx,cy);
//path_.arc_to(rx,ry,0,1,0,cx-rx,cy);
//path_.arc_to(rx,ry,0,1,0,cx+rx,cy);
agg::ellipse c(cx, cy, rx, ry);
path_.storage().concat_path(c);
}
@ -524,4 +605,237 @@ void svg_parser::parse_rect(xmlTextReaderPtr reader)
}
}
/*
* <stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="1"
id="stop3763" />
*/
void svg_parser::parse_gradient_stop(xmlTextReaderPtr reader)
{
const xmlChar *value;
double offset = 0.0;
mapnik::color stop_color;
double opacity = 1.0;
value = xmlTextReaderGetAttribute(reader, BAD_CAST "offset");
if (value) offset = parse_double((const char*)value);
value = xmlTextReaderGetAttribute(reader, BAD_CAST "style");
if (value)
{
typedef std::vector<std::pair<std::string,std::string> > cont_type;
typedef cont_type::value_type value_type;
cont_type vec;
parse_style((const char*)value, vec);
BOOST_FOREACH(value_type kv , vec )
{
if (kv.first == "stop-color")
{
try
{
mapnik::color_factory::init_from_string(stop_color,kv.second.c_str());
}
catch (mapnik::config_error & ex)
{
std::cerr << ex.what() << std::endl;
}
}
else if (kv.first == "stop-opacity")
{
opacity = parse_double(kv.second.c_str());
}
}
}
value = xmlTextReaderGetAttribute(reader, BAD_CAST "stop-color");
if (value)
{
try
{
mapnik::color_factory::init_from_string(stop_color,(const char *) value);
}
catch (mapnik::config_error & ex)
{
std::cerr << ex.what() << std::endl;
}
}
value = xmlTextReaderGetAttribute(reader, BAD_CAST "stop-opacity");
if (value)
{
opacity = parse_double((const char *) value);
}
stop_color.set_alpha(opacity*255);
temporary_gradient_.second.add_stop(offset, stop_color);
std::cerr << "\tFound Stop: " << offset << " " << (unsigned)stop_color.red() << " " << (unsigned)stop_color.green() << " " << (unsigned)stop_color.blue() << " " << (unsigned)stop_color.alpha() << std::endl;
}
bool svg_parser::parse_common_gradient(xmlTextReaderPtr reader)
{
const xmlChar *value;
std::string id;
value = xmlTextReaderGetAttribute(reader, BAD_CAST "id");
if (value)
{
// start a new gradient
gradient new_grad;
id = std::string((const char *) value);
temporary_gradient_ = std::make_pair(id, new_grad);
}
else
{
// no point without an ID
return false;
}
// check if we should inherit from another tag
value = xmlTextReaderGetAttribute(reader, BAD_CAST "xlink:href");
if (value && value[0] == '#')
{
std::string linkid = (const char *) &value[1];
if (gradient_map_.count(linkid))
{
std::cerr << "\tLoading linked gradient properties from " << linkid << std::endl;
temporary_gradient_.second = gradient_map_[linkid];
}
else
{
std::cerr << "Failed to find linked gradient " << linkid << std::endl;
}
}
value = xmlTextReaderGetAttribute(reader, BAD_CAST "gradientUnits");
if (value && std::string((const char*) value) == "userSpaceOnUse")
{
temporary_gradient_.second.set_units(USER_SPACE_ON_USE);
}
else
{
temporary_gradient_.second.set_units(OBJECT_BOUNDING_BOX);
}
value = xmlTextReaderGetAttribute(reader, BAD_CAST "gradientTransform");
if (value)
{
agg::trans_affine tr;
mapnik::svg::parse_transform((const char*) value,tr);
temporary_gradient_.second.set_transform(tr);
}
return true;
}
/**
* <radialGradient
collect="always"
xlink:href="#linearGradient3759"
id="radialGradient3765"
cx="-1.2957155"
cy="-21.425594"
fx="-1.2957155"
fy="-21.425594"
r="5.1999998"
gradientUnits="userSpaceOnUse" />
*/
void svg_parser::parse_radial_gradient(xmlTextReaderPtr reader)
{
if (!parse_common_gradient(reader))
return;
const xmlChar *value;
double cx = 0.5;
double cy = 0.5;
double fx = 0.0;
double fy = 0.0;
double r = 0.5;
bool has_percent=true;
value = xmlTextReaderGetAttribute(reader, BAD_CAST "cx");
if (value) cx = parse_double_optional_percent((const char*)value, has_percent);
value = xmlTextReaderGetAttribute(reader, BAD_CAST "cy");
if (value) cy = parse_double_optional_percent((const char*)value, has_percent);
value = xmlTextReaderGetAttribute(reader, BAD_CAST "fx");
if (value)
fx = parse_double_optional_percent((const char*)value, has_percent);
else
fx = cx;
value = xmlTextReaderGetAttribute(reader, BAD_CAST "fy");
if (value)
fy = parse_double_optional_percent((const char*)value, has_percent);
else
fy = cy;
value = xmlTextReaderGetAttribute(reader, BAD_CAST "r");
if (value) r = parse_double_optional_percent((const char*)value, has_percent);
// this logic for detecting %'s will not support mixed coordinates.
if (has_percent && temporary_gradient_.second.get_units() == USER_SPACE_ON_USE)
{
temporary_gradient_.second.set_units(USER_SPACE_ON_USE_BOUNDING_BOX);
}
temporary_gradient_.second.set_gradient_type(RADIAL);
temporary_gradient_.second.set_control_points(fx,fy,cx,cy,r);
// add this here in case we have no end tag, will be replaced if we do
gradient_map_[temporary_gradient_.first] = temporary_gradient_.second;
std::cerr << "Found Radial Gradient: " << " " << cx << " " << cy << " " << fx << " " << fy << " " << r << std::endl;
}
void svg_parser::parse_linear_gradient(xmlTextReaderPtr reader)
{
if (!parse_common_gradient(reader))
return;
const xmlChar *value;
double x1 = 0.0;
double x2 = 1.0;
double y1 = 0.0;
double y2 = 1.0;
bool has_percent=true;
value = xmlTextReaderGetAttribute(reader, BAD_CAST "x1");
if (value) x1 = parse_double_optional_percent((const char*)value, has_percent);
value = xmlTextReaderGetAttribute(reader, BAD_CAST "x2");
if (value) x2 = parse_double_optional_percent((const char*)value, has_percent);
value = xmlTextReaderGetAttribute(reader, BAD_CAST "y1");
if (value) y1 = parse_double_optional_percent((const char*)value, has_percent);
value = xmlTextReaderGetAttribute(reader, BAD_CAST "y2");
if (value) y2 = parse_double_optional_percent((const char*)value, has_percent);
// this logic for detecting %'s will not support mixed coordinates.
if (has_percent && temporary_gradient_.second.get_units() == USER_SPACE_ON_USE)
{
temporary_gradient_.second.set_units(USER_SPACE_ON_USE_BOUNDING_BOX);
}
temporary_gradient_.second.set_gradient_type(LINEAR);
temporary_gradient_.second.set_control_points(x1,y1,x2,y2);
// add this here in case we have no end tag, will be replaced if we do
gradient_map_[temporary_gradient_.first] = temporary_gradient_.second;
std::cerr << "Found Linear Gradient: " << "(" << x1 << " " << y1 << "),(" << x2 << " " << y2 << ")" << std::endl;
}
void svg_parser::parse_pattern(xmlTextReaderPtr reader)
{
//const xmlChar *value;
}
}}

View file

@ -5,7 +5,7 @@
<Rule>
<ShieldSymbolizer
name="[label]"
file="../svg/rect.svg"
file="../images/dummy.png"
face_name="DejaVu Sans Book"
size="10"
transform="scale(5) translate(15, 15) rotate(20) skewX(20) skewY(5)"
@ -17,10 +17,44 @@
no_text="false"
opacity=".5"
text-opacity=".3"
allow_overlap="true"
avoid_edges="false"
/>
</Rule>
<Rule>
<PointSymbolizer
allow_overlap="true" />
</Rule>
</Style>
<Style name="2">
<Rule>
<LineSymbolizer></LineSymbolizer>
</Rule>
<Rule>
<ShieldSymbolizer
name="[label]"
file="../svg/ellipses.svg"
allow_overlap="true"
face_name="DejaVu Sans Book"
size="10"
no_text="false"
opacity=".5"
text-opacity=".3"
spacing="50"
line_spacing="10"
placement="line"
/>
</Rule>
</Style>
<Layer name="line" srs="+init=epsg:4326">
<StyleName>2</StyleName>
<Datasource>
<Parameter name="file">../json/lines.json</Parameter>
<Parameter name="layer_by_index">0</Parameter>
<Parameter name="type">ogr</Parameter>
</Datasource>
</Layer>
<Layer name="point" srs="+init=epsg:4326">
<StyleName>1</StyleName>

View file

@ -0,0 +1,21 @@
{
"type": "FeatureCollection",
/* comment */
"features": [
{ "type": "Feature",
"properties": { "x": 0, "y": 0, "label": "D" },
"geometry" : { "type": "LineString", "coordinates": [ [1, 0], [2, 1], [3, 2], [4, 3], [5, 4], [6, 5] ] }
},
{ "type": "Feature",
"properties": { "x": 2, "y": 0, "label": "V" },
"geometry" : { "type": "LineString", "coordinates": [ [2, 0], [2, 1], [2, 2], [2, 3], [2, 4], [2, 5] ] }
},
]
}

View file

@ -18,7 +18,7 @@
},
{ "type": "Feature",
"properties": { "x": 0, "y": 0, "label": "0,5" },
"properties": { "x": 0, "y": 5, "label": "0,5" },
"geometry" : { "type": "Point", "coordinates": [ 0, 5 ] }
},
{ "type": "Feature",

View file

@ -1,13 +1,118 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" version="1.1"
xmlns="http://www.w3.org/2000/svg">
<ellipse cy="-20" rx="10" ry="4" fill="green"/>
<ellipse cy="-20" rx="4" ry="10" fill="blue"/>
<circle cy="-20" r="5" stroke="green" stroke-width=".4" fill="red"/>
</svg>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="100%"
height="100%"
version="1.1"
id="svg2"
inkscape:version="0.48.0 r9654"
sodipodi:docname="ellipses.svg">
<metadata
id="metadata16">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1280"
inkscape:window-height="758"
id="namedview14"
showgrid="false"
inkscape:zoom="21.841736"
inkscape:cx="-11.26864"
inkscape:cy="1072.9165"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg2" />
<defs
id="defs12">
<linearGradient
collect="always"
id="linearGradient3775"
x1="0"
y1="-30"
x2="0"
y2="-10"
gradientUnits="userSpaceOnUse">
<stop
style="stop-color:#008000;stop-opacity:1;"
offset="0"
id="stop3771" />
<stop
style="stop-color:#008000;stop-opacity:0;"
offset="1"
id="stop3773" />
</linearGradient>
<radialGradient
collect="always"
id="radialGradient3765"
cx="-2.5"
cy="-22.5"
fx="0.5"
fy="-20.5"
r="5"
gradientUnits="userSpaceOnUse">
<stop
style="stop-color:#ff0000;stop-opacity:1;"
offset="0"
id="stop3761" />
<stop
id="stop3764"
offset="0.5"
style="stop-color:#ff4040;stop-opacity:1;" />
<stop
id="stop3767"
offset="0.6"
style="stop-color:#80ff80;stop-opacity:1;" />
<stop
style="stop-color:#ffacac;stop-opacity:1;"
offset="0.75"
id="stop3762" />
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="1"
id="stop3763" />
</radialGradient>
</defs>
<ellipse
cy="-20"
rx="10"
ry="4"
fill="green"
id="ellipse4"
style="fill-opacity:1;fill:url(#linearGradient3775)" />
<ellipse
cy="-20"
rx="4"
ry="10"
fill="blue"
id="ellipse6" />
<circle
cy="-20"
r="5"
stroke="green"
stroke-width=".4"
fill="red"
id="circle8"
style="fill-opacity:1;fill:url(#radialGradient3765);stroke-width:0.40000000000000002;stroke-miterlimit:4;stroke-dasharray:none" />
</svg>

Before

Width:  |  Height:  |  Size: 408 B

After

Width:  |  Height:  |  Size: 3 KiB

15
tests/data/svg/radial.svg Normal file
View file

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
width="500"
height="500"
version="1.1" >
<radialGradient id="rad" >
<stop style="stop-color:#000000;stop-opacity:1;" offset="0" />
<stop style="stop-color:#000000;stop-opacity:0;" offset="1" />
</radialGradient>
<circle fill="url(#rad)" cx="250" cy="250" r="250"/>
</svg>

After

Width:  |  Height:  |  Size: 449 B

View file

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
width="500"
height="500"
id="svg2"
version="1.1" >
<radialGradient id="rad1" cx="0%" cy="0%" r="100%">
<stop style="stop-color:#888888;stop-opacity:1;" offset="0" />
<stop style="stop-color:#000000;stop-opacity:0;" offset="1" />
</radialGradient>
<radialGradient id="rad2" gradientUnits="userSpaceOnUse" cx="0" cy="0" r="100">
<stop style="stop-color:#FF0000;stop-opacity:1;" offset="0" />
<stop style="stop-color:#000000;stop-opacity:0;" offset="1" />
</radialGradient>
<radialGradient id="rad3" cx="0" cy="1" r="1">
<stop style="stop-color:#00FF00;stop-opacity:1;" offset="0" />
<stop style="stop-color:#000000;stop-opacity:0;" offset="1" />
</radialGradient>
<radialGradient id="rad4" gradientUnits="userSpaceOnUse" cx="50%" cy="50%" r="50%">
<stop style="stop-color:#0000FF;stop-opacity:1;" offset="0" />
<stop style="stop-color:#000000;stop-opacity:0;" offset="1" />
</radialGradient>
<circle fill="url(#rad1)" cx="50" cy="50" r="50"/>
<circle fill="url(#rad2)" cx="-50" cy="50" r="50"/>
<circle fill="url(#rad3)" cx="50" cy="-50" r="50"/>
<circle fill="url(#rad4)" cx="-50" cy="-50" r="50"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB