Merge branch 'master' into harfbuzz

Conflicts:
	src/build.py
This commit is contained in:
Hermann Kraus 2012-08-03 19:27:09 +02:00
commit ef0aae3733
59 changed files with 1568 additions and 852 deletions

7
.gitignore vendored
View file

@ -43,4 +43,11 @@ demo/c++/demo.tif
demo/c++/demo.jpg
demo/c++/demo.png
demo/c++/demo256.png
demo/viewer/Makefile
demo/viewer/Makefile.Debug
demo/viewer/Makefile.Release
demo/viewer/release/
demo/viewer/ui_about.h
demo/viewer/ui_info.h
demo/viewer/ui_layer_info.h
tests/cpp_tests/*-bin

View file

@ -11,6 +11,10 @@ For a complete change history, see the git log.
Not yet released
- Added support for overriding fill, stroke, and opacity for svg markers using marker properties
- Added support for setting opacity dynamically on images in polygon pattern and markers symbolizers
- Added support for filtering on a features geometry type, either `point`, `linestring`, 'polygon`,
or `collection` using the expression keyword of `[mapnik::geometry_type]` (#546)

View file

@ -91,6 +91,18 @@ struct layer_pickle_suite : boost::python::pickle_suite
std::vector<std::string> & (mapnik::layer::*_styles_)() = &mapnik::layer::styles;
void set_maximum_extent(mapnik::layer & l, boost::optional<mapnik::box2d<double> > const& box)
{
if (box)
{
l.set_maximum_extent(*box);
}
else
{
l.reset_maximum_extent();
}
}
void export_layer()
{
using namespace boost::python;
@ -196,6 +208,27 @@ void export_layer()
"<mapnik.Datasource object at 0x65470>\n"
)
.add_property("buffer_size",
&layer::buffer_size,
&layer::set_buffer_size,
"Get/Set the size of buffer around layer in pixels.\n"
"\n"
"Usage:\n"
">>> l.buffer_size\n"
"0 # zero by default\n"
">>> l.buffer_size = 2\n"
">>> l.buffer_size\n"
"2\n"
)
.add_property("maximum_extent",make_function
(&layer::maximum_extent,return_value_policy<copy_const_reference>()),
&set_maximum_extent,
"The maximum extent of the map.\n"
"\n"
"Usage:\n"
">>> m.maximum_extent = Box2d(-180,-90,180,90)\n"
)
.add_property("maxzoom",
&layer::max_zoom,
&layer::set_max_zoom,

View file

@ -57,7 +57,7 @@ void export_line_symbolizer()
"Set/get the rasterization method of the line of the point")
.add_property("stroke",make_function
(&line_symbolizer::get_stroke,
return_value_policy<copy_const_reference>()),
return_value_policy<reference_existing_object>()),
&line_symbolizer::set_stroke)
.add_property("smooth",
&line_symbolizer::smooth,

View file

@ -117,7 +117,7 @@ struct map_pickle_suite : boost::python::pickle_suite
std::vector<layer>& (Map::*layers_nonconst)() = &Map::layers;
std::vector<layer> const& (Map::*layers_const)() const = &Map::layers;
mapnik::parameters& (Map::*params_nonconst)() = &Map::get_extra_parameters;
boost::optional<mapnik::box2d<double> > const& (Map::*maximum_extent_const)() const = &Map::maximum_extent;
//boost::optional<mapnik::box2d<double> > const& (Map::*maximum_extent_const)() const = &Map::maximum_extent;
mapnik::feature_type_style find_style(mapnik::Map const& m, std::string const& name)
{
@ -192,7 +192,6 @@ mapnik::Map map_deepcopy(mapnik::Map & m, boost::python::dict memo)
return result;
}
// TODO - find a simplier way to set optional to uninitialized
void set_maximum_extent(mapnik::Map & m, boost::optional<mapnik::box2d<double> > const& box)
{
if (box)
@ -201,7 +200,7 @@ void set_maximum_extent(mapnik::Map & m, boost::optional<mapnik::box2d<double> >
}
else
{
m.maximum_extent().reset();
m.reset_maximum_extent();
}
}
@ -562,7 +561,7 @@ void export_map()
)
.add_property("maximum_extent",make_function
(maximum_extent_const,return_value_policy<copy_const_reference>()),
(&Map::maximum_extent,return_value_policy<copy_const_reference>()),
&set_maximum_extent,
"The maximum extent of the map.\n"
"\n"

View file

@ -86,13 +86,13 @@ struct markers_symbolizer_pickle_suite : boost::python::pickle_suite
};
void export_markers_symbolizer()
{
using namespace boost::python;
mapnik::enumeration_<mapnik::marker_placement_e>("marker_placement")
.value("POINT_PLACEMENT",mapnik::MARKER_POINT_PLACEMENT)
.value("INTERIOR_PLACEMENT",mapnik::MARKER_INTERIOR_PLACEMENT)
.value("LINE_PLACEMENT",mapnik::MARKER_LINE_PLACEMENT)
;
@ -115,7 +115,11 @@ void export_markers_symbolizer()
.add_property("opacity",
&markers_symbolizer::get_opacity,
&markers_symbolizer::set_opacity,
"Set/get the text opacity")
"Set/get the overall opacity")
.add_property("fill_opacity",
&markers_symbolizer::get_fill_opacity,
&markers_symbolizer::set_fill_opacity,
"Set/get the fill opacity")
.add_property("ignore_placement",
&markers_symbolizer::get_ignore_placement,
&markers_symbolizer::set_ignore_placement)

View file

@ -608,12 +608,14 @@ BOOST_PYTHON_MODULE(_mapnik)
def("has_cairo", &has_cairo, "Get cairo library status");
def("has_pycairo", &has_pycairo, "Get pycairo module status");
python_optional<mapnik::stroke>();
python_optional<mapnik::color>();
python_optional<mapnik::box2d<double> >();
python_optional<mapnik::datasource::geometry_t>();
python_optional<std::string>();
python_optional<unsigned>();
python_optional<double>();
python_optional<float>();
python_optional<bool>();
python_optional<mapnik::text_transform_e>();
register_ptr_to_python<mapnik::expression_ptr>();

View file

@ -36,10 +36,11 @@ int main( int argc, char **argv )
using mapnik::datasource_cache;
using mapnik::freetype_engine;
try
{
QCoreApplication::setOrganizationName("Mapnik");
QCoreApplication::setOrganizationDomain("mapnik.org");
QCoreApplication::setApplicationName("Viewer");
QSettings settings("viewer.ini",QSettings::IniFormat);
// register input plug-ins
@ -84,4 +85,10 @@ int main( int argc, char **argv )
if (ok) window.set_scaling_factor(scaling_factor);
}
return app.exec();
}
catch (std::exception const& ex)
{
std::cerr << "Could not start viewer: '" << ex.what() << "'\n";
return 1;
}
}

View file

@ -1015,18 +1015,34 @@ namespace agg
// Dca' = Sa.(Sca.Da + Dca.Sa - Sa.Da)/Sca + Sca.(1 - Da) + Dca.(1 - Sa)
//
// Da' = Sa + Da - Sa.Da
// http://www.w3.org/TR/SVGCompositing/
// if Sca == 0 and Dca == Da
// Dca' = Sa × Da + Sca × (1 - Da) + Dca × (1 - Sa)
// = Sa × Da + Dca × (1 - Sa)
// = Da = Dca
// otherwise if Sca == 0
// Dca' = Sca × (1 - Da) + Dca × (1 - Sa)
// = Dca × (1 - Sa)
// otherwise if Sca > 0
// Dca' = Sa × Da - Sa × Da × min(1, (1 - Dca/Da) × Sa/Sca) + Sca × (1 - Da) + Dca × (1 - Sa)
// = Sa × Da × (1 - min(1, (1 - Dca/Da) × Sa/Sca)) + Sca × (1 - Da) + Dca × (1 - Sa)
// sa * da * (255 - std::min(255, (255 - p[0]/da)*(sa/(sc*sa)) +
static AGG_INLINE void blend_pix(value_type* p,
unsigned sr, unsigned sg, unsigned sb,
unsigned sa, unsigned cover)
{
if(cover < 255)
if (cover < 255)
{
sr = (sr * cover + 255) >> 8;
sg = (sg * cover + 255) >> 8;
sb = (sb * cover + 255) >> 8;
sa = (sa * cover + 255) >> 8;
}
if(sa)
if (sa)
{
calc_type d1a = base_mask - p[Order::A];
calc_type s1a = base_mask - sa;
@ -1042,15 +1058,15 @@ namespace agg
long_type sbda = sb * da;
long_type sada = sa * da;
p[Order::R] = (value_type)(((srda + drsa <= sada) ?
if ( sr > 0) p[Order::R] = (value_type)(((srda + drsa <= sada) ?
sr * d1a + dr * s1a :
sa * (srda + drsa - sada) / sr + sr * d1a + dr * s1a + base_mask) >> base_shift);
p[Order::G] = (value_type)(((sgda + dgsa <= sada) ?
if ( sg > 0 ) p[Order::G] = (value_type)(((sgda + dgsa <= sada) ?
sg * d1a + dg * s1a :
sa * (sgda + dgsa - sada) / sg + sg * d1a + dg * s1a + base_mask) >> base_shift);
p[Order::B] = (value_type)(((sbda + dbsa <= sada) ?
if ( sb > 0) p[Order::B] = (value_type)(((sbda + dbsa <= sada) ?
sb * d1a + db * s1a :
sa * (sbda + dbsa - sada) / sb + sb * d1a + db * s1a + base_mask) >> base_shift);
@ -1502,24 +1518,29 @@ namespace agg
unsigned sr, unsigned sg, unsigned sb,
unsigned sa, unsigned cover)
{
if (cover < 255)
{
sr = (sr * cover + 255) >> 8;
sg = (sg * cover + 255) >> 8;
sb = (sb * cover + 255) >> 8;
sa = (sa * cover + 255) >> 8;
}
if (sa > 0)
{
calc_type da = p[Order::A];
calc_type da = (p[Order::A] * sa + 255) >> 8;
int dr = p[Order::R] - sr + 128;
int dg = p[Order::G] - sg + 128;
int db = p[Order::B] - sb + 128;
p[Order::R] = dr < 0 ? 0 : (dr > 255 ? 255 : dr);
p[Order::G] = dg < 0 ? 0 : (dg > 255 ? 255 : dg);
p[Order::B] = db < 0 ? 0 : (db > 255 ? 255 : db);
p[Order::A] = (value_type)(sa + da - ((sa * da + base_mask) >> base_shift));
dr = dr < 0 ? 0 : (dr > 255 ? 255 : dr);
dg = dg < 0 ? 0 : (dg > 255 ? 255 : dg);
db = db < 0 ? 0 : (db > 255 ? 255 : db);
p[Order::A] = da;
if (da < 255)
{
p[Order::R] = (dr * da + 255) >> 8;
p[Order::G] = (dg * da + 255) >> 8;
p[Order::B] = (db * da + 255) >> 8;
}
else
{
p[Order::R] = dr;
p[Order::G] = dg;
p[Order::B] = db;
}
}
};

View file

@ -127,6 +127,7 @@ protected:
double x, double y, double angle = 0.0);
void debug_draw_box(box2d<double> const& extent,
double x, double y, double angle = 0.0);
void draw_geo_extent(box2d<double> const& extent,mapnik::color const& color);
private:
buffer_type & pixmap_;

View file

@ -249,7 +249,7 @@ public:
struct directive_collector : public boost::static_visitor<>
{
directive_collector(double * filter_factor)
directive_collector(double & filter_factor)
: filter_factor_(filter_factor) {}
template <typename T>
@ -257,10 +257,10 @@ struct directive_collector : public boost::static_visitor<>
void operator () (raster_symbolizer const& sym)
{
*filter_factor_ = sym.calculate_filter_factor();
filter_factor_ = sym.calculate_filter_factor();
}
private:
double * filter_factor_;
double & filter_factor_;
};
} // namespace mapnik

View file

@ -110,7 +110,7 @@ operator << (std::basic_ostream<charT,traits>& out,
std::basic_ostringstream<charT,traits> s;
s.copyfmt(out);
s.width(0);
s << "box2d(" << std::setprecision(16)
s << "box2d(" << std::fixed << std::setprecision(16)
<< e.minx() << ',' << e.miny() << ','
<< e.maxx() << ',' << e.maxy() << ')';
out << s.str();

View file

@ -65,21 +65,12 @@ struct MAPNIK_DECL coord_transform
unsigned vertex(double *x, double *y) const
{
unsigned command = SEG_MOVETO;
bool ok = false;
bool skipped_points = false;
unsigned command = geom_.vertex(x, y);
if ( command != SEG_END)
{
double z = 0;
while (!ok && command != SEG_END)
{
command = geom_.vertex(x, y);
ok = prj_trans_->backward(*x, *y, z);
if (!ok) {
skipped_points = true;
}
}
if (skipped_points && (command == SEG_LINETO))
{
command = SEG_MOVETO;
if (!prj_trans_->backward(*x, *y, z))
return SEG_END;
}
t_->forward(x, y);
return command;

View file

@ -241,6 +241,8 @@ double path_length(PathType & path)
return length;
}
namespace label {
template <typename PathType>
bool middle_point(PathType & path, double & x, double & y)
{
@ -271,8 +273,6 @@ bool middle_point(PathType & path, double & x, double & y)
return true;
}
namespace label {
template <typename PathType>
bool centroid(PathType & path, double & x, double & y)
{

View file

@ -118,9 +118,10 @@ private:
CoordTransform t_;
freetype_engine font_engine_;
face_manager<freetype_engine> font_manager_;
label_collision_detector4 detector_;
boost::shared_ptr<label_collision_detector4> detector_;
boost::scoped_ptr<grid_rasterizer> ras_ptr;
box2d<double> query_extent_;
void setup(Map const& m);
};
}

View file

@ -43,7 +43,9 @@ namespace mapnik
class MAPNIK_DECL layer
{
public:
explicit layer(std::string const& name, std::string const& srs="+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs");
layer(std::string const& name,
std::string const& srs="+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs");
layer(layer const& l);
layer& operator=(layer const& l);
bool operator==(layer const& other) const;
@ -57,7 +59,7 @@ public:
* @return the name of the layer.
*/
const std::string& name() const;
std::string const& name() const;
/*!
* @brief Set the SRS of the layer.
@ -188,6 +190,11 @@ public:
*/
box2d<double> envelope() const;
void set_maximum_extent(box2d<double> const& box);
boost::optional<box2d<double> > const& maximum_extent() const;
void reset_maximum_extent();
void set_buffer_size(int size);
int buffer_size() const;
~layer();
private:
void swap(const layer& other);
@ -204,6 +211,8 @@ private:
std::string group_by_;
std::vector<std::string> styles_;
datasource_ptr ds_;
int buffer_size_;
boost::optional<box2d<double> > maximum_extent_;
};
}

View file

@ -332,15 +332,13 @@ public:
/*! \brief Set the map maximum extent.
* @param box The bounding box for the maximum extent.
*/
void set_maximum_extent(box2d<double>const& box);
void set_maximum_extent(box2d<double> const& box);
/*! \brief Get the map maximum extent as box2d<double>
*/
boost::optional<box2d<double> > const& maximum_extent() const;
/*! \brief Get the non-const map maximum extent as box2d<double>
*/
boost::optional<box2d<double> > & maximum_extent();
void reset_maximum_extent();
/*! \brief Get the map base path where paths should be relative to.
*/

View file

@ -49,13 +49,13 @@ 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<svg_storage_type> svg_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
class marker: private boost::noncopyable
{
public:
marker()
@ -65,20 +65,21 @@ public:
(*bitmap_data_)->set(0xff000000);
}
marker(const boost::optional<mapnik::image_ptr> &data)
marker(boost::optional<mapnik::image_ptr> const& data)
: bitmap_data_(data)
{
}
marker(const boost::optional<mapnik::path_ptr> &data)
marker(boost::optional<mapnik::svg_path_ptr> const& data)
: vector_data_(data)
{
}
marker(const marker& rhs)
: bitmap_data_(rhs.bitmap_data_), vector_data_(rhs.vector_data_)
marker(marker const& rhs)
: bitmap_data_(rhs.bitmap_data_),
vector_data_(rhs.vector_data_)
{}
box2d<double> bounding_box() const
@ -128,17 +129,14 @@ public:
return bitmap_data_;
}
boost::optional<mapnik::path_ptr> get_vector_data() const
boost::optional<mapnik::svg_path_ptr> get_vector_data() const
{
return vector_data_;
}
private:
marker& operator=(const marker&);
boost::optional<mapnik::image_ptr> bitmap_data_;
boost::optional<mapnik::path_ptr> vector_data_;
boost::optional<mapnik::svg_path_ptr> vector_data_;
};

View file

@ -27,39 +27,359 @@
#include <mapnik/markers_symbolizer.hpp>
#include <mapnik/expression_evaluator.hpp>
#include <mapnik/svg/svg_path_attributes.hpp>
#include <mapnik/svg/svg_converter.hpp>
#include <mapnik/marker.hpp> // for svg_storage_type
#include <mapnik/svg/svg_storage.hpp>
#include <mapnik/markers_placement.hpp>
// agg
#include "agg_ellipse.h"
#include "agg_basics.h"
#include "agg_renderer_base.h"
#include "agg_renderer_scanline.h"
#include "agg_rendering_buffer.h"
#include "agg_scanline_u.h"
#include "agg_image_filters.h"
#include "agg_trans_bilinear.h"
#include "agg_span_allocator.h"
#include "agg_image_accessors.h"
#include "agg_pixfmt_rgba.h"
#include "agg_span_image_filter_rgba.h"
// boost
#include <boost/optional.hpp>
namespace mapnik {
template <typename BufferType, typename SvgRenderer, typename Rasterizer, typename Detector>
struct vector_markers_rasterizer_dispatch
{
typedef typename SvgRenderer::renderer_base renderer_base;
typedef typename renderer_base::pixfmt_type pixfmt_type;
vector_markers_rasterizer_dispatch(BufferType & image_buffer,
SvgRenderer & svg_renderer,
Rasterizer & ras,
box2d<double> const& bbox,
agg::trans_affine const& marker_trans,
markers_symbolizer const& sym,
Detector & detector,
double scale_factor)
: buf_(image_buffer.raw_data(), image_buffer.width(), image_buffer.height(), image_buffer.width() * 4),
pixf_(buf_),
renb_(pixf_),
svg_renderer_(svg_renderer),
ras_(ras),
bbox_(bbox),
marker_trans_(marker_trans),
sym_(sym),
detector_(detector),
scale_factor_(scale_factor)
{
pixf_.comp_op(static_cast<agg::comp_op_e>(sym_.comp_op()));
}
template <typename T>
void add_path(T & path)
{
marker_placement_e placement_method = sym_.get_marker_placement();
if (placement_method != MARKER_LINE_PLACEMENT)
{
double x,y;
path.rewind(0);
if (placement_method == MARKER_INTERIOR_PLACEMENT)
{
label::interior_position(path, x, y);
}
else
{
label::centroid(path, x, y);
}
agg::trans_affine matrix = marker_trans_;
matrix.translate(x,y);
box2d<double> transformed_bbox = bbox_ * matrix;
if (sym_.get_allow_overlap() ||
detector_.has_placement(transformed_bbox))
{
svg_renderer_.render(ras_, sl_, renb_, matrix, 1, bbox_);
if (!sym_.get_ignore_placement())
detector_.insert(transformed_bbox);
}
}
else
{
markers_placement<T, label_collision_detector4> placement(path, bbox_, marker_trans_, detector_,
sym_.get_spacing() * scale_factor_,
sym_.get_max_error(),
sym_.get_allow_overlap());
double x, y, angle;
while (placement.get_point(x, y, angle))
{
agg::trans_affine matrix = marker_trans_;
matrix.rotate(angle);
matrix.translate(x, y);
svg_renderer_.render(ras_, sl_, renb_, matrix, 1, bbox_);
}
}
}
private:
agg::scanline_u8 sl_;
agg::rendering_buffer buf_;
pixfmt_type pixf_;
renderer_base renb_;
SvgRenderer & svg_renderer_;
Rasterizer & ras_;
box2d<double> const& bbox_;
agg::trans_affine const& marker_trans_;
markers_symbolizer const& sym_;
Detector & detector_;
double scale_factor_;
};
template <typename Rasterizer, typename RendererBuffer>
void render_raster_marker(Rasterizer & ras, RendererBuffer & renb,
agg::scanline_u8 & sl, image_data_32 const& src,
agg::trans_affine const& marker_tr, double opacity)
{
double width = src.width();
double height = src.height();
double p[8];
p[0] = 0; p[1] = 0;
p[2] = width; p[3] = 0;
p[4] = width; p[5] = height;
p[6] = 0; p[7] = height;
marker_tr.transform(&p[0], &p[1]);
marker_tr.transform(&p[2], &p[3]);
marker_tr.transform(&p[4], &p[5]);
marker_tr.transform(&p[6], &p[7]);
ras.move_to_d(p[0],p[1]);
ras.line_to_d(p[2],p[3]);
ras.line_to_d(p[4],p[5]);
ras.line_to_d(p[6],p[7]);
typedef agg::rgba8 color_type;
agg::span_allocator<color_type> sa;
agg::image_filter_bilinear filter_kernel;
agg::image_filter_lut filter(filter_kernel, false);
agg::rendering_buffer marker_buf((unsigned char *)src.getBytes(),
src.width(),
src.height(),
src.width()*4);
agg::pixfmt_rgba32_pre pixf(marker_buf);
typedef agg::image_accessor_clone<agg::pixfmt_rgba32_pre> img_accessor_type;
typedef agg::span_interpolator_linear<agg::trans_affine> interpolator_type;
typedef agg::span_image_filter_rgba_2x2<img_accessor_type,
interpolator_type> span_gen_type;
typedef agg::order_rgba order_type;
typedef agg::pixel32_type pixel_type;
typedef agg::comp_op_adaptor_rgba_pre<color_type, order_type> blender_type; // comp blender
typedef agg::pixfmt_custom_blend_rgba<blender_type, agg::rendering_buffer> pixfmt_comp_type;
typedef agg::renderer_base<pixfmt_comp_type> renderer_base;
typedef agg::renderer_scanline_aa_alpha<renderer_base,
agg::span_allocator<agg::rgba8>,
span_gen_type> renderer_type;
img_accessor_type ia(pixf);
interpolator_type interpolator(agg::trans_affine(p, 0, 0, width, height) );
span_gen_type sg(ia, interpolator, filter);
renderer_type rp(renb,sa, sg, unsigned(opacity*255));
agg::render_scanlines(ras, sl, rp);
}
template <typename BufferType, typename Rasterizer, typename Detector>
struct raster_markers_rasterizer_dispatch
{
typedef agg::rgba8 color_type;
typedef agg::order_rgba order_type;
typedef agg::pixel32_type pixel_type;
typedef agg::comp_op_adaptor_rgba_pre<color_type, order_type> blender_type; // comp blender
typedef agg::pixfmt_custom_blend_rgba<blender_type, agg::rendering_buffer> pixfmt_comp_type;
typedef agg::renderer_base<pixfmt_comp_type> renderer_base;
raster_markers_rasterizer_dispatch(BufferType & image_buffer,
Rasterizer & ras,
image_data_32 const& src,
agg::trans_affine const& marker_trans,
markers_symbolizer const& sym,
Detector & detector,
double scale_factor)
: buf_(image_buffer.raw_data(), image_buffer.width(), image_buffer.height(), image_buffer.width() * 4),
pixf_(buf_),
renb_(pixf_),
ras_(ras),
src_(src),
marker_trans_(marker_trans),
sym_(sym),
detector_(detector),
scale_factor_(scale_factor)
{
pixf_.comp_op(static_cast<agg::comp_op_e>(sym_.comp_op()));
}
template <typename T>
void add_path(T & path)
{
marker_placement_e placement_method = sym_.get_marker_placement();
box2d<double> bbox_(0,0, src_.width(),src_.height());
if (placement_method != MARKER_LINE_PLACEMENT)
{
double x,y;
path.rewind(0);
if (placement_method == MARKER_INTERIOR_PLACEMENT)
{
label::interior_position(path, x, y);
}
else
{
label::centroid(path, x, y);
}
agg::trans_affine matrix = marker_trans_;
matrix.translate(x,y);
box2d<double> transformed_bbox = bbox_ * matrix;
if (sym_.get_allow_overlap() ||
detector_.has_placement(transformed_bbox))
{
float opacity = sym_.get_opacity() ? *sym_.get_opacity() : 1;
render_raster_marker(ras_, renb_, sl_, src_,
matrix, opacity);
if (!sym_.get_ignore_placement())
detector_.insert(transformed_bbox);
}
}
else
{
markers_placement<T, label_collision_detector4> placement(path, bbox_, marker_trans_, detector_,
sym_.get_spacing() * scale_factor_,
sym_.get_max_error(),
sym_.get_allow_overlap());
double x, y, angle;
while (placement.get_point(x, y, angle))
{
agg::trans_affine matrix = marker_trans_;
matrix.rotate(angle);
matrix.translate(x,y);
float opacity = sym_.get_opacity() ? *sym_.get_opacity() : 1;
render_raster_marker(ras_, renb_, sl_, src_,
matrix, opacity);
}
}
}
private:
agg::scanline_u8 sl_;
agg::rendering_buffer buf_;
pixfmt_comp_type pixf_;
renderer_base renb_;
Rasterizer & ras_;
image_data_32 const& src_;
agg::trans_affine const& marker_trans_;
markers_symbolizer const& sym_;
Detector & detector_;
double scale_factor_;
};
template <typename T>
void build_ellipse(T const& sym, mapnik::feature_impl const& feature, svg_storage_type & marker_ellipse, svg::svg_path_adapter & svg_path)
{
expression_ptr const& width_expr = sym.get_width();
expression_ptr const& height_expr = sym.get_height();
double width = 0;
double height = 0;
if (width_expr && height_expr)
{
width = boost::apply_visitor(evaluate<Feature,value_type>(feature), *width_expr).to_double();
height = boost::apply_visitor(evaluate<Feature,value_type>(feature), *height_expr).to_double();
}
else if (width_expr)
{
width = boost::apply_visitor(evaluate<Feature,value_type>(feature), *width_expr).to_double();
height = width;
}
else if (height_expr)
{
height = boost::apply_visitor(evaluate<Feature,value_type>(feature), *height_expr).to_double();
width = height;
}
svg::svg_converter_type styled_svg(svg_path, marker_ellipse.attributes());
styled_svg.push_attr();
styled_svg.begin_path();
agg::ellipse c(0, 0, width/2.0, height/2.0);
styled_svg.storage().concat_path(c);
styled_svg.end_path();
styled_svg.pop_attr();
double lox,loy,hix,hiy;
styled_svg.bounding_rect(&lox, &loy, &hix, &hiy);
marker_ellipse.set_bounding_box(lox,loy,hix,hiy);
}
template <typename Attr>
bool push_explicit_style(Attr const& src, Attr & dst, markers_symbolizer const& sym)
{
boost::optional<stroke> const& strk = sym.get_stroke();
boost::optional<color> const& fill = sym.get_fill();
if (strk || fill)
boost::optional<float> const& opacity = sym.get_opacity();
boost::optional<float> const& fill_opacity = sym.get_fill_opacity();
if (strk || fill || opacity || fill_opacity)
{
bool success = false;
for(unsigned i = 0; i < src.size(); ++i)
{
mapnik::svg::path_attributes attr = src[i];
if (strk && attr.stroke_flag)
success = true;
dst.push_back(src[i]);
mapnik::svg::path_attributes & attr = dst.last();
if (attr.stroke_flag)
{
// TODO - stroke attributes need to be boost::optional
// for this to work properly
if (strk)
{
attr.stroke_width = strk->get_width();
color const& s_color = strk->get_color();
attr.stroke_color = agg::rgba(s_color.red()/255.0,s_color.green()/255.0,
s_color.blue()/255.0,(s_color.alpha()*strk->get_opacity())/255.0);
attr.stroke_color = agg::rgba(s_color.red()/255.0,
s_color.green()/255.0,
s_color.blue()/255.0,
s_color.alpha()/255.0);
}
if (fill && attr.fill_flag)
if (opacity)
{
attr.stroke_opacity = *opacity;
}
else if (strk)
{
attr.stroke_opacity = strk->get_opacity();
}
}
if (attr.fill_flag)
{
if (fill)
{
color const& f_color = *fill;
attr.fill_color = agg::rgba(f_color.red()/255.0,f_color.green()/255.0,
f_color.blue()/255.0,(f_color.alpha()*sym.get_opacity())/255.0);
attr.fill_color = agg::rgba(f_color.red()/255.0,
f_color.green()/255.0,
f_color.blue()/255.0,
f_color.alpha()/255.0);
}
dst.push_back(attr);
if (opacity)
{
attr.fill_opacity = *opacity;
}
return true;
else if (fill_opacity)
{
attr.fill_opacity = *fill_opacity;
}
}
}
return success;
}
return false;
}

View file

@ -24,13 +24,26 @@
#define MAPNIK_MARKERS_PLACEMENT_HPP
// mapnik
#include <mapnik/markers_placement.hpp>
#include <mapnik/geometry.hpp>
#include <mapnik/ctrans.hpp>
#include <mapnik/label_collision_detector.hpp>
#include <mapnik/global.hpp> //round
#include <mapnik/box2d.hpp>
// boost
#include <boost/utility.hpp>
// agg
#include "agg_basics.h"
#include "agg_conv_clip_polygon.h"
#include "agg_conv_clip_polyline.h"
#include "agg_trans_affine.h"
#include "agg_conv_transform.h"
#include "agg_conv_smooth_poly1.h"
// stl
#include <cmath>
namespace mapnik {
@ -47,12 +60,40 @@ public:
* converted to a positive value with similar magnitude, but
* choosen to optimize marker placement. 0 = no markers
*/
markers_placement(Locator &locator, box2d<double> const& size, agg::trans_affine const& tr, Detector &detector, double spacing, double max_error, bool allow_overlap);
markers_placement(Locator &locator, box2d<double> const& size, agg::trans_affine const& tr, Detector &detector, double spacing, double max_error, bool allow_overlap)
: locator_(locator),
size_(size),
tr_(tr),
detector_(detector),
max_error_(max_error),
allow_overlap_(allow_overlap)
{
marker_width_ = (size_ * tr_).width();
if (spacing >= 0)
{
spacing_ = spacing;
} else if (spacing < 0)
{
spacing_ = find_optimal_spacing(-spacing);
}
rewind();
}
/** Start again at first marker.
* \note Returns the same list of markers only works when they were NOT added
* to the detector.
*/
void rewind();
void rewind()
{
locator_.rewind(0);
//Get first point
done_ = agg::is_stop(locator_.vertex(&next_x, &next_y)) || spacing_ < marker_width_;
last_x = next_x;
last_y = next_y; // Force request of new segment
error_ = 0;
marker_nr_ = 0;
}
/** Get a point where the marker should be placed.
* Each time this function is called a new point is returned.
* \param x Return value for x position
@ -61,7 +102,115 @@ public:
* \param add_to_detector Add selected position to detector
* \return True if a place is found, false if none is found.
*/
bool get_point(double & x, double & y, double & angle, bool add_to_detector = true);
bool get_point(double & x, double & y, double & angle, bool add_to_detector = true)
{
if (done_) return false;
unsigned cmd;
/* This functions starts at the position of the previous marker,
walks along the path, counting how far it has to go in spacing_left.
If one marker can't be placed at the position it should go to it is
moved a bit. The error is compensated for in the next call to this
function.
error > 0: Marker too near to the end of the path.
error = 0: Perfect position.
error < 0: Marker too near to the beginning of the path.
*/
if (marker_nr_++ == 0)
{
//First marker
spacing_left_ = spacing_ / 2;
} else
{
spacing_left_ = spacing_;
}
spacing_left_ -= error_;
error_ = 0;
//Loop exits when a position is found or when no more segments are available
while (true)
{
//Do not place markers too close to the beginning of a segment
if (spacing_left_ < marker_width_/2)
{
set_spacing_left(marker_width_/2); //Only moves forward
}
//Error for this marker is too large. Skip to the next position.
if (abs(error_) > max_error_ * spacing_)
{
if (error_ > spacing_)
{
error_ = 0; //Avoid moving backwards
MAPNIK_LOG_WARN(markers_placement) << "Extremely large error in markers_placement. Please file a bug report.";
}
spacing_left_ += spacing_ - error_;
error_ = 0;
}
double dx = next_x - last_x;
double dy = next_y - last_y;
double segment_length = std::sqrt(dx * dx + dy * dy);
if (segment_length <= spacing_left_)
{
//Segment is to short to place marker. Find next segment
spacing_left_ -= segment_length;
last_x = next_x;
last_y = next_y;
while (agg::is_move_to(cmd = locator_.vertex(&next_x, &next_y)))
{
//Skip over "move" commands
last_x = next_x;
last_y = next_y;
}
if (agg::is_stop(cmd))
{
done_ = true;
return false;
}
continue; //Try again
}
/* At this point we know the following things:
- segment_length > spacing_left
- error is small enough
- at least half a marker fits into this segment
*/
//Check if marker really fits in this segment
if (segment_length < marker_width_)
{
//Segment to short => Skip this segment
set_spacing_left(segment_length + marker_width_/2); //Only moves forward
continue;
} else if (segment_length - spacing_left_ < marker_width_/2)
{
//Segment is long enough, but we are to close to the end
//Note: This function moves backwards. This could lead to an infinite
// loop when another function adds a positive offset. Therefore we
// only move backwards when there is no offset
if (error_ == 0)
{
set_spacing_left(segment_length - marker_width_/2, true);
} else
{
//Skip this segment
set_spacing_left(segment_length + marker_width_/2); //Only moves forward
}
continue; //Force checking of max_error constraint
}
angle = atan2(dy, dx);
x = last_x + dx * (spacing_left_ / segment_length);
y = last_y + dy * (spacing_left_ / segment_length);
box2d<double> box = perform_transform(angle, x, y);
if (!allow_overlap_ && !detector_.has_placement(box))
{
//10.0 is the approxmiate number of positions tried and choosen arbitrarily
set_spacing_left(spacing_left_ + spacing_ * max_error_ / 10.0); //Only moves forward
continue;
}
if (add_to_detector) detector_.insert(box);
last_x = x;
last_y = y;
return true;
}
}
private:
Locator &locator_;
box2d<double> size_;
@ -82,11 +231,69 @@ private:
unsigned marker_nr_;
/** Rotates the size_ box and translates the position. */
box2d<double> perform_transform(double angle, double dx, double dy);
box2d<double> perform_transform(double angle, double dx, double dy)
{
double x1 = size_.minx();
double x2 = size_.maxx();
double y1 = size_.miny();
double y2 = size_.maxy();
agg::trans_affine tr = tr_ * agg::trans_affine_rotation(angle).translate(dx, dy);
double xA = x1, yA = y1, xB = x2, yB = y1, xC = x2, yC = y2, xD = x1, yD = y2;
tr.transform(&xA, &yA);
tr.transform(&xB, &yB);
tr.transform(&xC, &yC);
tr.transform(&xD, &yD);
box2d<double> result(xA, yA, xC, yC);
result.expand_to_include(xB, yB);
result.expand_to_include(xD, yD);
return result;
}
/** Automatically chooses spacing. */
double find_optimal_spacing(double s);
double find_optimal_spacing(double s)
{
rewind();
//Calculate total path length
unsigned cmd = agg::path_cmd_move_to;
double length = 0;
while (!agg::is_stop(cmd))
{
double dx = next_x - last_x;
double dy = next_y - last_y;
length += std::sqrt(dx * dx + dy * dy);
last_x = next_x;
last_y = next_y;
while (agg::is_move_to(cmd = locator_.vertex(&next_x, &next_y)))
{
//Skip over "move" commands
last_x = next_x;
last_y = next_y;
}
}
unsigned points = round(length / s);
if (points == 0) return 0.0; //Path to short
return length / points;
}
/** Set spacing_left_, adjusts error_ and performs sanity checks. */
void set_spacing_left(double sl, bool allow_negative=false);
void set_spacing_left(double sl, bool allow_negative=false)
{
double delta_error = sl - spacing_left_;
if (!allow_negative && delta_error < 0)
{
MAPNIK_LOG_WARN(markers_placement) << "Unexpected negative error in markers_placement. Please file a bug report.";
return;
}
#ifdef MAPNIK_DEBUG
if (delta_error == 0.0)
{
MAPNIK_LOG_WARN(markers_placement) << "Not moving at all in set_spacing_left()! Please file a bug report.";
}
#endif
error_ += delta_error;
spacing_left_ = sl;
}
};
}

View file

@ -39,6 +39,7 @@ namespace mapnik {
// TODO - consider merging with text_symbolizer label_placement_e
enum marker_placement_enum {
MARKER_POINT_PLACEMENT,
MARKER_INTERIOR_PLACEMENT,
MARKER_LINE_PLACEMENT,
marker_placement_enum_MAX
};
@ -65,8 +66,12 @@ public:
double get_spacing() const;
void set_max_error(double max_error);
double get_max_error() const;
void set_opacity(float opacity);
boost::optional<float> get_opacity() const;
void set_fill(color const& fill);
boost::optional<color> get_fill() const;
void set_fill_opacity(float opacity);
boost::optional<float> get_fill_opacity() const;
void set_stroke(stroke const& stroke);
boost::optional<stroke> get_stroke() const;
void set_marker_placement(marker_placement_e marker_p);
@ -79,6 +84,8 @@ private:
double spacing_;
double max_error_;
boost::optional<color> fill_;
boost::optional<float> fill_opacity_;
boost::optional<float> opacity_;
boost::optional<stroke> stroke_;
marker_placement_e marker_p_;
};

View file

@ -66,10 +66,10 @@ public:
{
throw std::runtime_error("end_path : The path was not begun");
}
path_attributes attr = cur_attr();
unsigned idx = attributes_[attributes_.size() - 1].index;
path_attributes& attr = attributes_[attributes_.size() - 1];
unsigned idx = attr.index;
attr = cur_attr();
attr.index = idx;
attributes_[attributes_.size() - 1] = attr;
}
void move_to(double x, double y, bool rel=false) // M, m

View file

@ -102,6 +102,7 @@ private:
template <typename VertexSource, typename AttributeSource, typename ScanlineRenderer, typename PixelFormat>
class svg_renderer : boost::noncopyable
{
public:
typedef agg::conv_curve<VertexSource> curved_type;
typedef agg::conv_stroke<curved_type> curved_stroked_type;
typedef agg::conv_transform<curved_stroked_type> curved_stroked_trans_type;
@ -109,7 +110,6 @@ class svg_renderer : boost::noncopyable
typedef agg::conv_contour<curved_trans_type> curved_trans_contour_type;
typedef agg::renderer_base<PixelFormat> renderer_base;
public:
svg_renderer(VertexSource & source, AttributeSource const& attributes)
: source_(source),
curved_(source_),

View file

@ -21,12 +21,14 @@
*****************************************************************************/
// mapnik
#include <mapnik/layer.hpp>
#include <mapnik/feature_type_style.hpp>
#include <mapnik/graphics.hpp>
#include <mapnik/agg_renderer.hpp>
#include <mapnik/agg_rasterizer.hpp>
#include <mapnik/agg_helpers.hpp>
#include <mapnik/graphics.hpp>
#include <mapnik/debug.hpp>
#include <mapnik/layer.hpp>
#include <mapnik/feature_type_style.hpp>
#include <mapnik/marker.hpp>
#include <mapnik/marker_cache.hpp>
#include <mapnik/unicode.hpp>
@ -36,6 +38,7 @@
#include <mapnik/svg/svg_converter.hpp>
#include <mapnik/svg/svg_renderer.hpp>
#include <mapnik/svg/svg_path_adapter.hpp>
#include <mapnik/image_compositing.hpp>
#include <mapnik/image_filter.hpp>
#include <mapnik/image_util.hpp>
@ -156,6 +159,10 @@ void agg_renderer<T>::setup(Map const &m)
}
}
agg::rendering_buffer buf(pixmap_.raw_data(),width_,height_, width_ * 4);
agg::pixfmt_rgba32 pixf(buf);
pixf.premultiply();
MAPNIK_LOG_DEBUG(agg_renderer) << "agg_renderer: Scale=" << m.scale();
}
@ -166,7 +173,6 @@ template <typename T>
void agg_renderer<T>::start_map_processing(Map const& map)
{
MAPNIK_LOG_DEBUG(agg_renderer) << "agg_renderer: Start map processing bbox=" << map.get_current_extent();
ras_ptr->clip_box(0,0,width_,height_);
}
@ -191,7 +197,24 @@ void agg_renderer<T>::start_layer_processing(layer const& lay, box2d<double> con
{
detector_->clear();
}
query_extent_ = query_extent;
int buffer_size = lay.buffer_size();
if (buffer_size != 0 )
{
double padding = buffer_size * (double)(query_extent.width()/pixmap_.width());
double x0 = query_extent_.minx();
double y0 = query_extent_.miny();
double x1 = query_extent_.maxx();
double y1 = query_extent_.maxy();
query_extent_.init(x0 - padding, y0 - padding, x1 + padding , y1 + padding);
}
boost::optional<box2d<double> > const& maximum_extent = lay.maximum_extent();
if (maximum_extent)
{
query_extent_.clip(*maximum_extent);
}
}
template <typename T>
@ -362,15 +385,18 @@ void agg_renderer<T>::render_marker(pixel_position const& pos, marker const& mar
src.height(),
src.width()*4);
agg::pixfmt_rgba32_pre pixf(marker_buf);
typedef agg::image_accessor_clone<agg::pixfmt_rgba32_pre> img_accessor_type;
typedef agg::span_interpolator_linear<agg::trans_affine> interpolator_type;
typedef agg::span_image_filter_rgba_2x2<img_accessor_type,
interpolator_type> span_gen_type;
typedef agg::renderer_scanline_aa_alpha<renderer_base,
agg::span_allocator<agg::rgba8>,
span_gen_type> renderer_type;
img_accessor_type ia(pixf);
interpolator_type interpolator(agg::trans_affine(p, 0, 0, width, height) );
span_gen_type sg(ia, interpolator, filter);
agg::render_scanlines_aa(*ras_ptr, sl, renb, sa, sg);
renderer_type rp(renb,sa, sg, unsigned(opacity*255));
agg::render_scanlines(*ras_ptr, sl, rp);
}
}
}
@ -427,6 +453,27 @@ void agg_renderer<T>::debug_draw_box(R& buf, box2d<double> const& box,
agg::render_scanlines(*ras_ptr, sl_line, ren);
}
template <typename T>
void agg_renderer<T>::draw_geo_extent(box2d<double> const& extent, mapnik::color const& color)
{
box2d<double> box = t_.forward(extent);
double x0 = box.minx();
double x1 = box.maxx();
double y0 = box.miny();
double y1 = box.maxy();
unsigned rgba = color.rgba();
for (double x=x0; x<x1; x++)
{
pixmap_.setPixel(x, y0, rgba);
pixmap_.setPixel(x, y1, rgba);
}
for (double y=y0; y<y1; y++)
{
pixmap_.setPixel(x0, y, rgba);
pixmap_.setPixel(x1, y, rgba);
}
}
template class agg_renderer<image_32>;
template void agg_renderer<image_32>::debug_draw_box<agg::rendering_buffer>(
agg::rendering_buffer& buf,

View file

@ -82,32 +82,31 @@ void agg_renderer<T>::process(building_symbolizer const& sym,
boost::scoped_ptr<geometry_type> frame(new geometry_type(LineString));
boost::scoped_ptr<geometry_type> roof(new geometry_type(Polygon));
std::deque<segment_t> face_segments;
double x0(0);
double y0(0);
double x0 = 0;
double y0 = 0;
double x,y;
geom.rewind(0);
unsigned cm = geom.vertex(&x0,&y0);
for (unsigned j=1;j<geom.size();++j)
for (unsigned cm = geom.vertex(&x, &y); cm != SEG_END;
cm = geom.vertex(&x, &y))
{
double x(0);
double y(0);
cm = geom.vertex(&x,&y);
if (cm == SEG_MOVETO)
{
frame->move_to(x,y);
}
else if (cm == SEG_LINETO)
else if (cm == SEG_LINETO || cm == SEG_CLOSE)
{
frame->line_to(x,y);
face_segments.push_back(segment_t(x0,y0,x,y));
}
x0 = x;
y0 = y;
}
std::sort(face_segments.begin(),face_segments.end(), y_order);
std::deque<segment_t>::const_iterator itr=face_segments.begin();
for (;itr!=face_segments.end();++itr)
std::deque<segment_t>::const_iterator end=face_segments.end();
for (; itr!=end; ++itr)
{
boost::scoped_ptr<geometry_type> faces(new geometry_type(Polygon));
faces->move_to(itr->get<0>(),itr->get<1>());
@ -120,22 +119,22 @@ void agg_renderer<T>::process(building_symbolizer const& sym,
ren.color(agg::rgba8(int(r*0.8), int(g*0.8), int(b*0.8), int(a * sym.get_opacity())));
agg::render_scanlines(*ras_ptr, sl, ren);
ras_ptr->reset();
//
frame->move_to(itr->get<0>(),itr->get<1>());
frame->line_to(itr->get<0>(),itr->get<1>()+height);
}
geom.rewind(0);
for (unsigned j=0;j<geom.size();++j)
for (unsigned cm = geom.vertex(&x, &y); cm != SEG_END;
cm = geom.vertex(&x, &y))
{
double x,y;
unsigned cm = geom.vertex(&x,&y);
if (cm == SEG_MOVETO)
{
frame->move_to(x,y+height);
roof->move_to(x,y+height);
}
else if (cm == SEG_LINETO)
else if (cm == SEG_LINETO || cm == SEG_CLOSE)
{
frame->line_to(x,y+height);
roof->line_to(x,y+height);
@ -153,6 +152,7 @@ void agg_renderer<T>::process(building_symbolizer const& sym,
ras_ptr->add_path(roof_path);
ren.color(agg::rgba8(r, g, b, int(a * sym.get_opacity())));
agg::render_scanlines(*ras_ptr, sl, ren);
}
}
}

View file

@ -81,8 +81,6 @@ void agg_renderer<T>::process(line_pattern_symbolizer const& sym,
if (!pat) return;
box2d<double> ext = query_extent_ * 1.0;
agg::rendering_buffer buf(current_buffer_->raw_data(),width_,height_, width_ * 4);
pixfmt_type pixf(buf);
pixf.comp_op(static_cast<agg::comp_op_e>(sym.comp_op()));
@ -97,10 +95,24 @@ void agg_renderer<T>::process(line_pattern_symbolizer const& sym,
agg::trans_affine tr;
evaluate_transform(tr, feature, sym.get_transform());
box2d<double> clipping_extent = query_extent_;
if (sym.clip())
{
double padding = (double)(query_extent_.width()/pixmap_.width());
float half_stroke = (*mark)->width()/2.0;
if (half_stroke > 1)
padding *= half_stroke;
double x0 = query_extent_.minx();
double y0 = query_extent_.miny();
double x1 = query_extent_.maxx();
double y1 = query_extent_.maxy();
clipping_extent.init(x0 - padding, y0 - padding, x1 + padding , y1 + padding);
}
typedef boost::mpl::vector<clip_line_tag,transform_tag,smooth_tag> conv_types;
vertex_converter<box2d<double>, rasterizer_type, line_pattern_symbolizer,
CoordTransform, proj_transform, agg::trans_affine, conv_types>
converter(ext,ras,sym,t_,prj_trans,tr,scale_factor_);
converter(clipping_extent,ras,sym,t_,prj_trans,tr,scale_factor_);
if (sym.clip()) converter.set<clip_line_tag>(); //optional clip (default: true)
converter.set<transform_tag>(); //always transform

View file

@ -47,7 +47,6 @@
namespace mapnik {
template <typename T>
void agg_renderer<T>::process(line_symbolizer const& sym,
mapnik::feature_impl & feature,
@ -80,7 +79,24 @@ void agg_renderer<T>::process(line_symbolizer const& sym,
agg::trans_affine tr;
evaluate_transform(tr, feature, sym.get_transform());
box2d<double> ext = query_extent_ * 1.1;
box2d<double> clipping_extent = query_extent_;
if (sym.clip())
{
double padding = (double)(query_extent_.width()/pixmap_.width());
float half_stroke = stroke_.get_width()/2.0;
if (half_stroke > 1)
padding *= half_stroke;
if (fabs(sym.offset()) > 0)
padding *= fabs(sym.offset()) * 1.2;
double x0 = query_extent_.minx();
double y0 = query_extent_.miny();
double x1 = query_extent_.maxx();
double y1 = query_extent_.maxy();
clipping_extent.init(x0 - padding, y0 - padding, x1 + padding , y1 + padding);
// debugging
//box2d<double> inverse(x0 + padding, y0 + padding, x1 - padding , y1 - padding);
//draw_geo_extent(inverse,mapnik::color("red"));
}
if (sym.get_rasterizer() == RASTERIZER_FAST)
{
@ -94,11 +110,12 @@ void agg_renderer<T>::process(line_symbolizer const& sym,
rasterizer_type ras(ren);
set_join_caps_aa(stroke_,ras);
typedef boost::mpl::vector<clip_line_tag,transform_tag, offset_transform_tag, affine_transform_tag, smooth_tag, dash_tag, stroke_tag> conv_types;
typedef boost::mpl::vector<clip_line_tag, transform_tag,
offset_transform_tag, affine_transform_tag,
smooth_tag, dash_tag, stroke_tag> conv_types;
vertex_converter<box2d<double>, rasterizer_type, line_symbolizer,
CoordTransform, proj_transform, agg::trans_affine, conv_types>
converter(ext,ras,sym,t_,prj_trans,tr,scaled);
converter(clipping_extent,ras,sym,t_,prj_trans,tr,scaled);
if (sym.clip()) converter.set<clip_line_tag>(); // optional clip (default: true)
converter.set<transform_tag>(); // always transform
if (fabs(sym.offset()) > 0.0) converter.set<offset_transform_tag>(); // parallel offset
@ -117,10 +134,12 @@ void agg_renderer<T>::process(line_symbolizer const& sym,
}
else
{
typedef boost::mpl::vector<clip_line_tag,transform_tag, offset_transform_tag, affine_transform_tag, smooth_tag, dash_tag, stroke_tag> conv_types;
typedef boost::mpl::vector<clip_line_tag, transform_tag, offset_transform_tag,
affine_transform_tag, smooth_tag, dash_tag, stroke_tag> conv_types;
vertex_converter<box2d<double>, rasterizer, line_symbolizer,
CoordTransform, proj_transform, agg::trans_affine, conv_types>
converter(ext,*ras_ptr,sym,t_,prj_trans,tr,scale_factor_);
converter(clipping_extent,*ras_ptr,sym,t_,prj_trans,tr,scale_factor_);
if (sym.clip()) converter.set<clip_line_tag>(); // optional clip (default: true)
converter.set<transform_tag>(); // always transform

View file

@ -21,23 +21,27 @@
*****************************************************************************/
// mapnik
#include <mapnik/debug.hpp>
#include <mapnik/graphics.hpp>
#include <mapnik/geom_util.hpp>
#include <mapnik/agg_renderer.hpp>
#include <mapnik/agg_rasterizer.hpp>
#include <mapnik/debug.hpp>
#include <mapnik/geom_util.hpp>
#include <mapnik/expression_evaluator.hpp>
#include <mapnik/vertex_converters.hpp>
#include <mapnik/marker_helpers.hpp>
#include <mapnik/marker.hpp>
#include <mapnik/marker_cache.hpp>
#include <mapnik/marker_helpers.hpp>
#include <mapnik/svg/svg_renderer.hpp>
#include <mapnik/svg/svg_storage.hpp>
#include <mapnik/svg/svg_path_adapter.hpp>
#include <mapnik/markers_placement.hpp>
#include <mapnik/svg/svg_path_attributes.hpp>
#include <mapnik/markers_symbolizer.hpp>
// agg
#include "agg_basics.h"
#include "agg_renderer_base.h"
#include "agg_renderer_scanline.h"
#include "agg_rendering_buffer.h"
#include "agg_pixfmt_rgba.h"
#include "agg_rasterizer_scanline_aa.h"
@ -45,234 +49,13 @@
#include "agg_path_storage.h"
#include "agg_conv_clip_polyline.h"
#include "agg_conv_transform.h"
#include "agg_image_filters.h"
#include "agg_trans_bilinear.h"
#include "agg_span_allocator.h"
#include "agg_image_accessors.h"
#include "agg_span_image_filter_rgba.h"
// boost
#include <boost/optional.hpp>
namespace mapnik {
template <typename BufferType, typename SvgRenderer, typename Rasterizer, typename Detector>
struct vector_markers_rasterizer_dispatch
{
typedef agg::rgba8 color_type;
typedef agg::order_rgba order_type;
typedef agg::pixel32_type pixel_type;
typedef agg::comp_op_adaptor_rgba_pre<color_type, order_type> blender_type; // comp blender
typedef agg::pixfmt_custom_blend_rgba<blender_type, agg::rendering_buffer> pixfmt_comp_type;
typedef agg::renderer_base<pixfmt_comp_type> renderer_base;
typedef agg::renderer_scanline_aa_solid<renderer_base> renderer_type;
vector_markers_rasterizer_dispatch(BufferType & image_buffer,
SvgRenderer & svg_renderer,
Rasterizer & ras,
box2d<double> const& bbox,
agg::trans_affine const& marker_trans,
markers_symbolizer const& sym,
Detector & detector,
double scale_factor)
: buf_(image_buffer.raw_data(), image_buffer.width(), image_buffer.height(), image_buffer.width() * 4),
pixf_(buf_),
renb_(pixf_),
svg_renderer_(svg_renderer),
ras_(ras),
bbox_(bbox),
marker_trans_(marker_trans),
sym_(sym),
detector_(detector),
scale_factor_(scale_factor)
{
pixf_.comp_op(static_cast<agg::comp_op_e>(sym_.comp_op()));
}
template <typename T>
void add_path(T & path)
{
marker_placement_e placement_method = sym_.get_marker_placement();
if (placement_method == MARKER_POINT_PLACEMENT)
{
double x,y;
path.rewind(0);
label::interior_position(path, x, y);
agg::trans_affine matrix = marker_trans_;
matrix.translate(x,y);
box2d<double> transformed_bbox = bbox_ * matrix;
if (sym_.get_allow_overlap() ||
detector_.has_placement(transformed_bbox))
{
svg_renderer_.render(ras_, sl_, renb_, matrix, sym_.get_opacity(), bbox_);
if (!sym_.get_ignore_placement())
detector_.insert(transformed_bbox);
}
}
else
{
markers_placement<T, label_collision_detector4> placement(path, bbox_, marker_trans_, detector_,
sym_.get_spacing() * scale_factor_,
sym_.get_max_error(),
sym_.get_allow_overlap());
double x, y, angle;
while (placement.get_point(x, y, angle))
{
agg::trans_affine matrix = marker_trans_;
matrix.rotate(angle);
matrix.translate(x, y);
svg_renderer_.render(ras_, sl_, renb_, matrix, sym_.get_opacity(), bbox_);
}
}
}
private:
agg::scanline_u8 sl_;
agg::rendering_buffer buf_;
pixfmt_comp_type pixf_;
renderer_base renb_;
SvgRenderer & svg_renderer_;
Rasterizer & ras_;
box2d<double> const& bbox_;
agg::trans_affine const& marker_trans_;
markers_symbolizer const& sym_;
Detector & detector_;
double scale_factor_;
};
template <typename Rasterizer, typename RendererBuffer>
void render_raster_marker(Rasterizer & ras, RendererBuffer & renb,
agg::scanline_u8 & sl, image_data_32 const& src,
agg::trans_affine const& marker_tr, double opacity)
{
double width = src.width();
double height = src.height();
double p[8];
p[0] = 0; p[1] = 0;
p[2] = width; p[3] = 0;
p[4] = width; p[5] = height;
p[6] = 0; p[7] = height;
marker_tr.transform(&p[0], &p[1]);
marker_tr.transform(&p[2], &p[3]);
marker_tr.transform(&p[4], &p[5]);
marker_tr.transform(&p[6], &p[7]);
ras.move_to_d(p[0],p[1]);
ras.line_to_d(p[2],p[3]);
ras.line_to_d(p[4],p[5]);
ras.line_to_d(p[6],p[7]);
typedef agg::rgba8 color_type;
agg::span_allocator<color_type> sa;
agg::image_filter_bilinear filter_kernel;
agg::image_filter_lut filter(filter_kernel, false);
agg::rendering_buffer marker_buf((unsigned char *)src.getBytes(),
src.width(),
src.height(),
src.width()*4);
agg::pixfmt_rgba32_pre pixf(marker_buf);
typedef agg::image_accessor_clone<agg::pixfmt_rgba32_pre> img_accessor_type;
typedef agg::span_interpolator_linear<agg::trans_affine> interpolator_type;
typedef agg::span_image_filter_rgba_2x2<img_accessor_type,
interpolator_type> span_gen_type;
img_accessor_type ia(pixf);
interpolator_type interpolator(agg::trans_affine(p, 0, 0, width, height) );
span_gen_type sg(ia, interpolator, filter);
agg::render_scanlines_aa(ras, sl, renb, sa, sg);
}
template <typename BufferType, typename Rasterizer, typename Detector>
struct raster_markers_rasterizer_dispatch
{
typedef agg::rgba8 color_type;
typedef agg::order_rgba order_type;
typedef agg::pixel32_type pixel_type;
typedef agg::comp_op_adaptor_rgba_pre<color_type, order_type> blender_type; // comp blender
typedef agg::pixfmt_custom_blend_rgba<blender_type, agg::rendering_buffer> pixfmt_comp_type;
typedef agg::renderer_base<pixfmt_comp_type> renderer_base;
typedef agg::renderer_scanline_aa_solid<renderer_base> renderer_type;
raster_markers_rasterizer_dispatch(BufferType & image_buffer,
Rasterizer & ras,
image_data_32 const& src,
agg::trans_affine const& marker_trans,
markers_symbolizer const& sym,
Detector & detector,
double scale_factor)
: buf_(image_buffer.raw_data(), image_buffer.width(), image_buffer.height(), image_buffer.width() * 4),
pixf_(buf_),
renb_(pixf_),
ras_(ras),
src_(src),
marker_trans_(marker_trans),
sym_(sym),
detector_(detector),
scale_factor_(scale_factor)
{
pixf_.comp_op(static_cast<agg::comp_op_e>(sym_.comp_op()));
}
template <typename T>
void add_path(T & path)
{
marker_placement_e placement_method = sym_.get_marker_placement();
box2d<double> bbox_(0,0, src_.width(),src_.height());
if (placement_method == MARKER_POINT_PLACEMENT)
{
double x,y;
path.rewind(0);
label::interior_position(path, x, y);
agg::trans_affine matrix = marker_trans_;
matrix.translate(x,y);
box2d<double> transformed_bbox = bbox_ * matrix;
if (sym_.get_allow_overlap() ||
detector_.has_placement(transformed_bbox))
{
render_raster_marker(ras_, renb_, sl_, src_,
matrix, sym_.get_opacity());
if (!sym_.get_ignore_placement())
detector_.insert(transformed_bbox);
}
}
else
{
markers_placement<T, label_collision_detector4> placement(path, bbox_, marker_trans_, detector_,
sym_.get_spacing() * scale_factor_,
sym_.get_max_error(),
sym_.get_allow_overlap());
double x, y, angle;
while (placement.get_point(x, y, angle))
{
agg::trans_affine matrix = marker_trans_;
matrix.rotate(angle);
matrix.translate(x,y);
render_raster_marker(ras_, renb_, sl_, src_,
matrix, sym_.get_opacity());
}
}
}
private:
agg::scanline_u8 sl_;
agg::rendering_buffer buf_;
pixfmt_comp_type pixf_;
renderer_base renb_;
Rasterizer & ras_;
image_data_32 const& src_;
agg::trans_affine const& marker_trans_;
markers_symbolizer const& sym_;
Detector & detector_;
double scale_factor_;
};
template <typename T>
void agg_renderer<T>::process(markers_symbolizer const& sym,
feature_impl & feature,
@ -284,9 +67,8 @@ void agg_renderer<T>::process(markers_symbolizer const& sym,
typedef agg::comp_op_adaptor_rgba_pre<color_type, order_type> blender_type; // comp blender
typedef agg::pixfmt_custom_blend_rgba<blender_type, agg::rendering_buffer> pixfmt_comp_type;
typedef agg::renderer_base<pixfmt_comp_type> renderer_base;
typedef agg::renderer_scanline_aa_solid<renderer_base> renderer_type;
typedef label_collision_detector4 detector_type;
typedef boost::mpl::vector<clip_line_tag,transform_tag,smooth_tag> conv_types;
typedef boost::mpl::vector<clip_line_tag,clip_poly_tag,transform_tag,smooth_tag> conv_types;
std::string filename = path_processor_type::evaluate(*sym.get_filename(), feature);
@ -297,67 +79,124 @@ void agg_renderer<T>::process(markers_symbolizer const& sym,
{
ras_ptr->reset();
ras_ptr->gamma(agg::gamma_power());
agg::trans_affine geom_tr;
evaluate_transform(geom_tr, feature, sym.get_transform());
box2d<double> const& bbox = (*mark)->bounding_box();
agg::trans_affine tr;
setup_label_transform(tr, bbox, feature, sym);
tr = agg::trans_affine_scaling(scale_factor_) * tr;
coord2d center = bbox.center();
agg::trans_affine_translation recenter(-center.x, -center.y);
agg::trans_affine marker_trans = recenter * tr;
tr *= agg::trans_affine_scaling(scale_factor_);
if ((*mark)->is_vector())
{
using namespace mapnik::svg;
boost::optional<path_ptr> marker = (*mark)->get_vector_data();
vertex_stl_adapter<svg_path_storage> stl_storage((*marker)->source());
svg_path_adapter svg_path(stl_storage);
agg::pod_bvector<path_attributes> attributes;
bool result = push_explicit_style( (*marker)->attributes(), attributes, sym);
typedef agg::renderer_scanline_aa_solid<renderer_base> renderer_type;
typedef agg::pod_bvector<path_attributes> svg_attribute_type;
typedef svg_renderer<svg_path_adapter,
agg::pod_bvector<path_attributes>,
svg_attribute_type,
renderer_type,
agg::pixfmt_rgba32 > svg_renderer_type;
typedef vector_markers_rasterizer_dispatch<buffer_type, svg_renderer_type, rasterizer, detector_type> dispatch_type;
svg_renderer_type svg_renderer(svg_path, result ? attributes : (*marker)->attributes());
pixfmt_comp_type > svg_renderer_type;
typedef vector_markers_rasterizer_dispatch<buffer_type,
svg_renderer_type,
rasterizer,
detector_type > dispatch_type;
boost::optional<svg_path_ptr> const& stock_vector_marker = (*mark)->get_vector_data();
expression_ptr const& width_expr = sym.get_width();
expression_ptr const& height_expr = sym.get_height();
// special case for simple ellipse markers
// to allow for full control over rx/ry dimensions
if (filename == "shape://ellipse"
&& (width_expr || height_expr))
{
svg_storage_type marker_ellipse;
vertex_stl_adapter<svg_path_storage> stl_storage(marker_ellipse.source());
svg_path_adapter svg_path(stl_storage);
build_ellipse(sym, feature, marker_ellipse, svg_path);
svg_attribute_type attributes;
bool result = push_explicit_style( (*stock_vector_marker)->attributes(), attributes, sym);
svg_renderer_type svg_renderer(svg_path, result ? attributes : (*stock_vector_marker)->attributes());
evaluate_transform(tr, feature, sym.get_image_transform());
box2d<double> bbox = marker_ellipse.bounding_box();
coord2d center = bbox.center();
agg::trans_affine_translation recenter(-center.x, -center.y);
agg::trans_affine marker_trans = recenter * tr;
dispatch_type rasterizer_dispatch(*current_buffer_,svg_renderer,*ras_ptr,
bbox, marker_trans, sym, *detector_, scale_factor_);
vertex_converter<box2d<double>, dispatch_type, markers_symbolizer,
CoordTransform, proj_transform, agg::trans_affine, conv_types>
converter(query_extent_* 1.1,rasterizer_dispatch, sym,t_,prj_trans,tr,scale_factor_);
if (sym.clip()) converter.template set<clip_line_tag>(); //optional clip (default: true)
converter(query_extent_, rasterizer_dispatch, sym,t_,prj_trans,tr,scale_factor_);
if (sym.clip() && feature.paths().size() > 0) // optional clip (default: true)
{
eGeomType type = feature.paths()[0].type();
if (type == Polygon)
converter.template set<clip_poly_tag>();
else if (type == LineString)
converter.template set<clip_line_tag>();
// don't clip if type==Point
}
converter.template set<transform_tag>(); //always transform
if (sym.smooth() > 0.0) converter.template set<smooth_tag>(); // optional smooth converter
BOOST_FOREACH(geometry_type & geom, feature.paths())
{
converter.apply(geom);
}
}
else
{
box2d<double> const& bbox = (*mark)->bounding_box();
setup_label_transform(tr, bbox, feature, sym);
coord2d center = bbox.center();
agg::trans_affine_translation recenter(-center.x, -center.y);
agg::trans_affine marker_trans = recenter * tr;
vertex_stl_adapter<svg_path_storage> stl_storage((*stock_vector_marker)->source());
svg_path_adapter svg_path(stl_storage);
svg_attribute_type attributes;
bool result = push_explicit_style( (*stock_vector_marker)->attributes(), attributes, sym);
svg_renderer_type svg_renderer(svg_path, result ? attributes : (*stock_vector_marker)->attributes());
dispatch_type rasterizer_dispatch(*current_buffer_,svg_renderer,*ras_ptr,
bbox, marker_trans, sym, *detector_, scale_factor_);
vertex_converter<box2d<double>, dispatch_type, markers_symbolizer,
CoordTransform, proj_transform, agg::trans_affine, conv_types>
converter(query_extent_, rasterizer_dispatch, sym,t_,prj_trans,tr,scale_factor_);
if (sym.clip() && feature.paths().size() > 0) // optional clip (default: true)
{
eGeomType type = feature.paths()[0].type();
if (type == Polygon)
converter.template set<clip_poly_tag>();
else if (type == LineString)
converter.template set<clip_line_tag>();
// don't clip if type==Point
}
converter.template set<transform_tag>(); //always transform
if (sym.smooth() > 0.0) converter.template set<smooth_tag>(); // optional smooth converter
BOOST_FOREACH(geometry_type & geom, feature.paths())
{
converter.apply(geom);
}
}
}
else // raster markers
{
box2d<double> const& bbox = (*mark)->bounding_box();
setup_label_transform(tr, bbox, feature, sym);
coord2d center = bbox.center();
agg::trans_affine_translation recenter(-center.x, -center.y);
agg::trans_affine marker_trans = recenter * tr;
boost::optional<mapnik::image_ptr> marker = (*mark)->get_bitmap_data();
typedef raster_markers_rasterizer_dispatch<buffer_type,rasterizer, detector_type> dispatch_type;
dispatch_type rasterizer_dispatch(*current_buffer_,*ras_ptr, **marker,
marker_trans, sym, *detector_, scale_factor_);
vertex_converter<box2d<double>, dispatch_type, markers_symbolizer,
CoordTransform, proj_transform, agg::trans_affine, conv_types>
converter(query_extent_* 1.1, rasterizer_dispatch, sym,t_,prj_trans,tr,scale_factor_);
converter(query_extent_, rasterizer_dispatch, sym,t_,prj_trans,tr,scale_factor_);
if (sym.clip()) converter.template set<clip_line_tag>(); //optional clip (default: true)
if (sym.clip() && feature.paths().size() > 0) // optional clip (default: true)
{
eGeomType type = feature.paths()[0].type();
if (type == Polygon)
converter.template set<clip_poly_tag>();
else if (type == LineString)
converter.template set<clip_line_tag>();
// don't clip if type==Point
}
converter.template set<transform_tag>(); //always transform
if (sym.smooth() > 0.0) converter.template set<smooth_tag>(); // optional smooth converter

View file

@ -23,19 +23,16 @@
// mapnik
#include <mapnik/agg_renderer.hpp>
#include <mapnik/agg_rasterizer.hpp>
#include <mapnik/geom_util.hpp>
#include <mapnik/image_util.hpp>
#include <mapnik/metawriter.hpp>
#include <mapnik/geom_util.hpp>
#include <mapnik/point_symbolizer.hpp>
#include <mapnik/expression_evaluator.hpp>
#include <mapnik/marker.hpp>
#include <mapnik/marker_cache.hpp>
#include <mapnik/expression_evaluator.hpp>
// agg
#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"
#include "agg_trans_affine.h"
// stl
#include <string>
@ -89,7 +86,6 @@ void agg_renderer<T>::process(point_symbolizer const& sym,
prj_trans.backward(x,y,z);
t_.forward(&x,&y);
label_ext.re_center(x,y);
if (sym.get_allow_overlap() ||
detector_->has_placement(label_ext))
{
@ -106,8 +102,8 @@ void agg_renderer<T>::process(point_symbolizer const& sym,
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);
//metawriter_with_properties writer = sym.get_metawriter();
//if (writer.first) writer.first->add_box(label_ext, feature, t_, writer.second);
}
}
}

View file

@ -139,11 +139,10 @@ void agg_renderer<T>::process(polygon_pattern_symbolizer const& sym,
agg::trans_affine tr;
evaluate_transform(tr, feature, sym.get_transform());
box2d<double> inflated_extent = query_extent_ * 1.0;
typedef boost::mpl::vector<clip_poly_tag,transform_tag,smooth_tag> conv_types;
vertex_converter<box2d<double>, rasterizer, polygon_pattern_symbolizer,
CoordTransform, proj_transform, agg::trans_affine, conv_types>
converter(inflated_extent,*ras_ptr,sym,t_,prj_trans,tr,scale_factor_);
converter(query_extent_,*ras_ptr,sym,t_,prj_trans,tr,scale_factor_);
if (sym.clip()) converter.set<clip_poly_tag>(); //optional clip (default: true)
converter.set<transform_tag>(); //always transform

View file

@ -48,15 +48,13 @@ void agg_renderer<T>::process(polygon_symbolizer const& sym,
ras_ptr->reset();
set_gamma_method(sym,ras_ptr);
box2d<double> inflated_extent = query_extent_ * 1.0;
agg::trans_affine tr;
evaluate_transform(tr, feature, sym.get_transform());
typedef boost::mpl::vector<clip_poly_tag,transform_tag,affine_transform_tag,smooth_tag> conv_types;
vertex_converter<box2d<double>, rasterizer, polygon_symbolizer,
CoordTransform, proj_transform, agg::trans_affine, conv_types>
converter(inflated_extent,*ras_ptr,sym,t_,prj_trans,tr,scale_factor_);
converter(query_extent_,*ras_ptr,sym,t_,prj_trans,tr,scale_factor_);
if (sym.clip()) converter.set<clip_poly_tag>(); //optional clip (default: true)
converter.set<transform_tag>(); //always transform

View file

@ -171,7 +171,6 @@ source = Split(
json/feature_grammar.cpp
json/feature_collection_parser.cpp
json/geojson_generator.cpp
markers_placement.cpp
formatting/base.cpp
formatting/expression.cpp
formatting/list.cpp

View file

@ -497,7 +497,6 @@ public:
}
else if (cm == SEG_CLOSE)
{
line_to(x, y);
close_path();
}
}
@ -882,43 +881,32 @@ void cairo_renderer_base::process(building_symbolizer const& sym,
boost::scoped_ptr<geometry_type> frame(new geometry_type(LineString));
boost::scoped_ptr<geometry_type> roof(new geometry_type(Polygon));
std::deque<segment_t> face_segments;
double x0(0);
double y0(0);
double x0 = 0;
double y0 = 0;
double x, y;
geom.rewind(0);
unsigned cm = geom.vertex(&x0, &y0);
for (unsigned j = 1; j < geom.size(); ++j)
for (unsigned cm = geom.vertex(&x, &y); cm != SEG_END;
cm = geom.vertex(&x, &y))
{
double x=0;
double y=0;
cm = geom.vertex(&x,&y);
if (cm == SEG_MOVETO)
{
frame->move_to(x,y);
}
else if (cm == SEG_LINETO)
else if (cm == SEG_LINETO || cm == SEG_CLOSE)
{
frame->line_to(x,y);
face_segments.push_back(segment_t(x0,y0,x,y));
}
if (j != 0)
{
face_segments.push_back(segment_t(x0, y0, x, y));
}
x0 = x;
y0 = y;
}
std::sort(face_segments.begin(), face_segments.end(), y_order);
std::deque<segment_t>::const_iterator itr = face_segments.begin();
for (; itr != face_segments.end(); ++itr)
std::deque<segment_t>::const_iterator end=face_segments.end();
for (; itr != end; ++itr)
{
boost::scoped_ptr<geometry_type> faces(new geometry_type(Polygon));
faces->move_to(itr->get<0>(), itr->get<1>());
faces->line_to(itr->get<2>(), itr->get<3>());
faces->line_to(itr->get<2>(), itr->get<3>() + height);
@ -935,20 +923,18 @@ void cairo_renderer_base::process(building_symbolizer const& sym,
}
geom.rewind(0);
for (unsigned j = 0; j < geom.size(); ++j)
for (unsigned cm = geom.vertex(&x, &y); cm != SEG_END;
cm = geom.vertex(&x, &y))
{
double x, y;
unsigned cm = geom.vertex(&x, &y);
if (cm == SEG_MOVETO)
{
frame->move_to(x, y + height);
roof->move_to(x, y + height);
frame->move_to(x,y+height);
roof->move_to(x,y+height);
}
else if (cm == SEG_LINETO)
else if (cm == SEG_LINETO || cm == SEG_CLOSE)
{
frame->line_to(x, y + height);
roof->line_to(x, y + height);
frame->line_to(x,y+height);
roof->line_to(x,y+height);
}
}
@ -987,14 +973,21 @@ void cairo_renderer_base::process(line_symbolizer const& sym,
evaluate_transform(tr, feature, sym.get_transform());
box2d<double> ext = query_extent_ * 1.1;
typedef boost::mpl::vector<clip_line_tag,transform_tag, offset_transform_tag, affine_transform_tag, smooth_tag> conv_types;
typedef boost::mpl::vector<clip_line_tag, clip_poly_tag, transform_tag, offset_transform_tag, affine_transform_tag, smooth_tag> conv_types;
vertex_converter<box2d<double>, cairo_context, line_symbolizer,
CoordTransform, proj_transform, agg::trans_affine, conv_types>
converter(ext,context,sym,t_,prj_trans,tr,scale_factor_);
if (sym.clip()) converter.set<clip_line_tag>(); // optional clip (default: true)
if (sym.clip() && feature.paths().size() > 0) // optional clip (default: true)
{
eGeomType type = feature.paths()[0].type();
if (type == Polygon)
converter.set<clip_poly_tag>();
else if (type == LineString)
converter.set<clip_line_tag>();
// don't clip if type==Point
}
converter.set<transform_tag>(); // always transform
if (fabs(sym.offset()) > 0.0) converter.set<offset_transform_tag>(); // parallel offset
converter.set<affine_transform_tag>(); // optional affine transform
if (sym.smooth() > 0.0) converter.set<smooth_tag>(); // optional smooth converter
@ -1035,7 +1028,7 @@ void cairo_renderer_base::render_marker(pixel_position const& pos, marker const&
typedef coord_transform<CoordTransform,geometry_type> path_type;
agg::trans_affine transform;
mapnik::path_ptr vmarker = *marker.get_vector_data();
mapnik::svg_path_ptr vmarker = *marker.get_vector_data();
using namespace mapnik::svg;
agg::pod_bvector<path_attributes> const & attributes_ = vmarker->attributes();
for(unsigned i = 0; i < attributes_.size(); ++i)
@ -1411,7 +1404,7 @@ void cairo_renderer_base::process(markers_symbolizer const& sym,
return;
}
boost::optional<path_ptr> marker = (*mark)->get_vector_data();
boost::optional<svg_path_ptr> marker = (*mark)->get_vector_data();
box2d<double> const& bbox = (*marker)->bounding_box();
double x1 = bbox.minx();
double y1 = bbox.miny();
@ -1443,7 +1436,7 @@ void cairo_renderer_base::process(markers_symbolizer const& sym,
if (sym.get_allow_overlap() ||
detector_.has_placement(extent))
{
render_marker(pixel_position(x - 0.5 * w, y - 0.5 * h) ,**mark, tr, sym.get_opacity());
render_marker(pixel_position(x - 0.5 * w, y - 0.5 * h) ,**mark, tr, 1);
if (!sym.get_ignore_placement())
detector_.insert(extent);
@ -1462,7 +1455,7 @@ void cairo_renderer_base::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);
render_marker(pixel_position(x - 0.5 * w, y - 0.5 * h), **mark, matrix, sym.get_opacity(),false);
render_marker(pixel_position(x - 0.5 * w, y - 0.5 * h), **mark, matrix, 1,false);
}
}
context.fill();

View file

@ -321,9 +321,11 @@ void feature_style_processor<Processor>::apply_to_layer(layer const& lay, Proces
// if we've got this far, now prepare the unbuffered extent
// which is used as a bbox for clipping geometries
box2d<double> query_ext = m_.get_current_extent(); // unbuffered
if (maximum_extent) {
if (maximum_extent)
{
query_ext.clip(*maximum_extent);
}
box2d<double> layer_ext2 = lay.envelope();
if (fw_success)
{
@ -351,8 +353,8 @@ void feature_style_processor<Processor>::apply_to_layer(layer const& lay, Proces
query q(layer_ext,res,scale_denom,m_.get_current_extent());
std::vector<feature_type_style*> active_styles;
attribute_collector collector(names);
double filt_factor = 1;
directive_collector d_collector(&filt_factor);
double filt_factor = 1.0;
directive_collector d_collector(filt_factor);
// iterate through all named styles collecting active styles and attribute names
BOOST_FOREACH(std::string const& style_name, style_names)
@ -662,4 +664,3 @@ template class feature_style_processor<grid_renderer<grid> >;
template class feature_style_processor<agg_renderer<image_32> >;
}

View file

@ -21,14 +21,15 @@
*****************************************************************************/
// mapnik
#include <mapnik/layer.hpp>
#include <mapnik/feature_type_style.hpp>
#include <mapnik/debug.hpp>
#include <mapnik/grid/grid_rasterizer.hpp>
#include <mapnik/grid/grid_renderer.hpp>
#include <mapnik/grid/grid_pixfmt.hpp>
#include <mapnik/grid/grid_pixel.hpp>
#include <mapnik/grid/grid.hpp>
#include <mapnik/debug.hpp>
#include <mapnik/layer.hpp>
#include <mapnik/feature_type_style.hpp>
#include <mapnik/marker.hpp>
#include <mapnik/marker_cache.hpp>
#include <mapnik/unicode.hpp>
@ -56,13 +57,22 @@ grid_renderer<T>::grid_renderer(Map const& m, T & pixmap, double scale_factor, u
width_(pixmap_.width()),
height_(pixmap_.height()),
scale_factor_(scale_factor),
// NOTE: can change this to m dims instead of pixmap_ if render-time
// resolution support is dropped from grid_renderer python interface
t_(pixmap_.width(),pixmap_.height(),m.get_current_extent(),offset_x,offset_y),
font_engine_(),
font_manager_(font_engine_),
detector_(box2d<double>(-m.buffer_size(), -m.buffer_size(), pixmap_.width() + m.buffer_size(), pixmap_.height() + m.buffer_size())),
detector_(boost::make_shared<label_collision_detector4>(box2d<double>(-m.buffer_size(), -m.buffer_size(), m.width() + m.buffer_size() ,m.height() + m.buffer_size()))),
ras_ptr(new grid_rasterizer)
{
setup(m);
}
template <typename T>
void grid_renderer<T>::setup(Map const& m)
{
MAPNIK_LOG_DEBUG(grid_renderer) << "grid_renderer: Scale=" << m.scale();
// nothing to do for grids yet on setup
}
template <typename T>
@ -91,9 +101,25 @@ void grid_renderer<T>::start_layer_processing(layer const& lay, box2d<double> co
if (lay.clear_label_cache())
{
detector_.clear();
detector_->clear();
}
query_extent_ = query_extent;
int buffer_size = lay.buffer_size();
if (buffer_size != 0 )
{
double padding = buffer_size * (double)(query_extent.width()/pixmap_.width());
double x0 = query_extent_.minx();
double y0 = query_extent_.miny();
double x1 = query_extent_.maxx();
double y1 = query_extent_.maxy();
query_extent_.init(x0 - padding, y0 - padding, x1 + padding , y1 + padding);
}
boost::optional<box2d<double> > const& maximum_extent = lay.maximum_extent();
if (maximum_extent)
{
query_extent_.clip(*maximum_extent);
}
}
template <typename T>
@ -144,21 +170,27 @@ void grid_renderer<T>::render_marker(mapnik::feature_impl & feature, unsigned in
else
{
image_data_32 const& data = **marker.get_bitmap_data();
if (step == 1 && scale_factor_ == 1.0)
double width = data.width();
double height = data.height();
double cx = 0.5 * width;
double cy = 0.5 * height;
if (step == 1 && (std::fabs(1.0 - scale_factor_) < 0.001 && tr.is_identity()))
{
// TODO - support opacity
pixmap_.set_rectangle(feature.id(), data,
boost::math::iround(pos.x),
boost::math::iround(pos.y));
boost::math::iround(pos.x - cx),
boost::math::iround(pos.y - cy));
}
else
{
// TODO - remove support for step != or add support for agg scaling with opacity
double ratio = (1.0/step);
image_data_32 target(ratio * data.width(), ratio * data.height());
mapnik::scale_image_agg<image_data_32>(target,data, SCALING_NEAR,
scale_factor_, 0.0, 0.0, 1.0, ratio);
pixmap_.set_rectangle(feature.id(), target,
boost::math::iround(pos.x),
boost::math::iround(pos.y));
boost::math::iround(pos.x - cx),
boost::math::iround(pos.y - cy));
}
}
pixmap_.add_feature(feature);

View file

@ -20,22 +20,55 @@
*
*****************************************************************************/
/*
porting notes -->
- grid includes
- detector
- no gamma
- mapnik::pixfmt_gray32
- agg::scanline_bin sl
- grid_rendering_buffer
- agg::renderer_scanline_bin_solid
- clamping:
// - clamp sizes to > 4 pixels of interactivity
if (tr.scale() < 0.5)
{
agg::trans_affine tr2;
tr2 *= agg::trans_affine_scaling(0.5);
tr = tr2;
}
tr *= agg::trans_affine_scaling(scale_factor_*(1.0/pixmap_.get_resolution()));
- svg_renderer.render_id
- only encode feature if placements are found:
if (placed)
{
pixmap_.add_feature(feature);
}
*/
// mapnik
#include <mapnik/debug.hpp>
#include <mapnik/grid/grid_rasterizer.hpp>
#include <mapnik/grid/grid_renderer.hpp>
#include <mapnik/grid/grid_pixfmt.hpp>
#include <mapnik/grid/grid_pixel.hpp>
#include <mapnik/grid/grid.hpp>
#include <mapnik/debug.hpp>
#include <mapnik/geom_util.hpp>
#include <mapnik/expression_evaluator.hpp>
#include <mapnik/vertex_converters.hpp>
#include <mapnik/marker.hpp>
#include <mapnik/marker_cache.hpp>
#include <mapnik/marker_helpers.hpp>
#include <mapnik/markers_symbolizer.hpp>
#include <mapnik/expression_evaluator.hpp>
#include <mapnik/marker_cache.hpp>
#include <mapnik/svg/svg_renderer.hpp>
#include <mapnik/svg/svg_storage.hpp>
#include <mapnik/svg/svg_path_adapter.hpp>
#include <mapnik/svg/svg_path_attributes.hpp>
#include <mapnik/markers_placement.hpp>
#include <mapnik/markers_symbolizer.hpp>
// agg
#include "agg_basics.h"
@ -46,6 +79,11 @@
#include "agg_path_storage.h"
#include "agg_conv_clip_polyline.h"
#include "agg_conv_transform.h"
#include "agg_image_filters.h"
#include "agg_trans_bilinear.h"
#include "agg_span_allocator.h"
#include "agg_image_accessors.h"
#include "agg_span_image_filter_rgba.h"
// boost
#include <boost/optional.hpp>
@ -87,7 +125,7 @@ void grid_renderer<T>::process(markers_symbolizer const& sym,
agg::trans_affine geom_tr;
evaluate_transform(geom_tr, feature, sym.get_transform());
boost::optional<path_ptr> marker = (*mark)->get_vector_data();
boost::optional<svg_path_ptr> marker = (*mark)->get_vector_data();
box2d<double> const& bbox = (*marker)->bounding_box();
agg::trans_affine tr;
@ -137,12 +175,12 @@ void grid_renderer<T>::process(markers_symbolizer const& sym,
box2d<double> transformed_bbox = bbox * matrix;
if (sym.get_allow_overlap() ||
detector_.has_placement(transformed_bbox))
detector_->has_placement(transformed_bbox))
{
placed = true;
svg_renderer.render_id(*ras_ptr, sl, renb, feature.id(), matrix, sym.get_opacity(), bbox);
svg_renderer.render_id(*ras_ptr, sl, renb, feature.id(), matrix, 1, bbox);
if (!sym.get_ignore_placement())
detector_.insert(transformed_bbox);
detector_->insert(transformed_bbox);
}
}
else if (sym.clip())
@ -155,7 +193,7 @@ void grid_renderer<T>::process(markers_symbolizer const& sym,
clipped.clip_box(query_extent_.minx(),query_extent_.miny(),query_extent_.maxx(),query_extent_.maxy());
path_type path(t_,clipped,prj_trans);
transformed_path_type path_transformed(path,geom_tr);
markers_placement<transformed_path_type, label_collision_detector4> placement(path_transformed, bbox, marker_trans, detector_,
markers_placement<transformed_path_type, label_collision_detector4> placement(path_transformed, bbox, marker_trans, *detector_,
sym.get_spacing() * scale_factor_,
sym.get_max_error(),
sym.get_allow_overlap());
@ -166,7 +204,7 @@ void grid_renderer<T>::process(markers_symbolizer const& sym,
agg::trans_affine matrix = marker_trans;
matrix.rotate(angle);
matrix.translate(x, y);
svg_renderer.render_id(*ras_ptr, sl, renb, feature.id(), matrix, sym.get_opacity(), bbox);
svg_renderer.render_id(*ras_ptr, sl, renb, feature.id(), matrix, 1, bbox);
}
}
else
@ -175,7 +213,7 @@ void grid_renderer<T>::process(markers_symbolizer const& sym,
typedef agg::conv_transform<path_type, agg::trans_affine> transformed_path_type;
path_type path(t_,geom,prj_trans);
transformed_path_type path_transformed(path,geom_tr);
markers_placement<transformed_path_type, label_collision_detector4> placement(path_transformed, bbox, marker_trans, detector_,
markers_placement<transformed_path_type, label_collision_detector4> placement(path_transformed, bbox, marker_trans, *detector_,
sym.get_spacing() * scale_factor_,
sym.get_max_error(),
sym.get_allow_overlap());
@ -186,7 +224,7 @@ void grid_renderer<T>::process(markers_symbolizer const& sym,
agg::trans_affine matrix = marker_trans;
matrix.rotate(angle);
matrix.translate(x, y);
svg_renderer.render_id(*ras_ptr, sl, renb, feature.id(), matrix, sym.get_opacity(), bbox);
svg_renderer.render_id(*ras_ptr, sl, renb, feature.id(), matrix, 1, bbox);
}
}
}

View file

@ -26,15 +26,22 @@
#include <mapnik/grid/grid_pixfmt.hpp>
#include <mapnik/grid/grid_pixel.hpp>
#include <mapnik/grid/grid.hpp>
#include <mapnik/geom_util.hpp>
#include <mapnik/point_symbolizer.hpp>
#include <mapnik/expression_evaluator.hpp>
#include <mapnik/marker.hpp>
#include <mapnik/marker_cache.hpp>
// agg
#include "agg_trans_affine.h"
// stl
#include <string>
// boost
#include <boost/make_shared.hpp>
namespace mapnik {
template <typename T>
@ -57,14 +64,14 @@ void grid_renderer<T>::process(point_symbolizer const& sym,
if (marker)
{
box2d<double> const& bbox = (*marker)->bounding_box();
coord2d const center = bbox.center();
coord2d center = bbox.center();
agg::trans_affine tr;
evaluate_transform(tr, feature, sym.get_image_transform());
tr = agg::trans_affine_scaling(scale_factor_) * tr;
agg::trans_affine_translation const recenter(-center.x, -center.y);
agg::trans_affine const recenter_tr = recenter * tr;
agg::trans_affine_translation recenter(-center.x, -center.y);
agg::trans_affine recenter_tr = recenter * tr;
box2d<double> label_ext = bbox * recenter_tr;
for (unsigned i=0; i<feature.num_geometries(); ++i)
@ -82,7 +89,7 @@ void grid_renderer<T>::process(point_symbolizer const& sym,
t_.forward(&x,&y);
label_ext.re_center(x,y);
if (sym.get_allow_overlap() ||
detector_.has_placement(label_ext))
detector_->has_placement(label_ext))
{
render_marker(feature,
@ -94,7 +101,7 @@ void grid_renderer<T>::process(point_symbolizer const& sym,
sym.comp_op());
if (!sym.get_ignore_placement())
detector_.insert(label_ext);
detector_->insert(label_ext);
}
}
}

View file

@ -47,7 +47,7 @@ void grid_renderer<T>::process(shield_symbolizer const& sym,
sym, feature, prj_trans,
width_, height_,
scale_factor_,
t_, font_manager_, detector_,
t_, font_manager_, *detector_,
query_extent_);
bool placement_found = false;

View file

@ -37,7 +37,7 @@ void grid_renderer<T>::process(text_symbolizer const& sym,
sym, feature, prj_trans,
width_, height_,
scale_factor_ * (1.0/pixmap_.get_resolution()),
t_, font_manager_, detector_,
t_, font_manager_, *detector_,
query_extent_);
bool placement_found = false;

View file

@ -42,7 +42,8 @@ layer::layer(std::string const& name, std::string const& srs)
clear_label_cache_(false),
cache_features_(false),
group_by_(""),
ds_() {}
ds_(),
buffer_size_(0) {}
layer::layer(const layer& rhs)
: name_(rhs.name_),
@ -55,7 +56,9 @@ layer::layer(const layer& rhs)
cache_features_(rhs.cache_features_),
group_by_(rhs.group_by_),
styles_(rhs.styles_),
ds_(rhs.ds_) {}
ds_(rhs.ds_),
buffer_size_(rhs.buffer_size_),
maximum_extent_(rhs.maximum_extent_) {}
layer& layer::operator=(const layer& rhs)
{
@ -82,6 +85,8 @@ void layer::swap(const layer& rhs)
group_by_ = rhs.group_by_;
styles_=rhs.styles_;
ds_=rhs.ds_;
buffer_size_ = rhs.buffer_size_;
maximum_extent_ = rhs.maximum_extent_;
}
layer::~layer() {}
@ -176,6 +181,31 @@ void layer::set_datasource(datasource_ptr const& ds)
ds_ = ds;
}
void layer::set_maximum_extent(box2d<double> const& box)
{
maximum_extent_.reset(box);
}
boost::optional<box2d<double> > const& layer::maximum_extent() const
{
return maximum_extent_;
}
void layer::reset_maximum_extent()
{
maximum_extent_.reset();
}
void layer::set_buffer_size(int size)
{
buffer_size_ = size;
}
int layer::buffer_size() const
{
return buffer_size_;
}
box2d<double> layer::envelope() const
{
if (ds_) return ds_->envelope();

View file

@ -575,66 +575,95 @@ bool map_parser::parse_font(font_set &fset, xml_node const& f)
return false;
}
void map_parser::parse_layer(Map & map, xml_node const& lay)
void map_parser::parse_layer(Map & map, xml_node const& node)
{
std::string name;
try
{
name = lay.get_attr("name", std::string("Unnamed"));
name = node.get_attr("name", std::string("Unnamed"));
// XXX if no projection is given inherit from map? [DS]
std::string srs = lay.get_attr("srs", map.srs());
std::string srs = node.get_attr("srs", map.srs());
layer lyr(name, srs);
optional<boolean> status = lay.get_opt_attr<boolean>("status");
optional<boolean> status = node.get_opt_attr<boolean>("status");
if (status)
{
lyr.set_active(* status);
}
optional<double> min_zoom = lay.get_opt_attr<double>("minzoom");
optional<double> min_zoom = node.get_opt_attr<double>("minzoom");
if (min_zoom)
{
lyr.set_min_zoom(* min_zoom);
}
optional<double> max_zoom = lay.get_opt_attr<double>("maxzoom");
optional<double> max_zoom = node.get_opt_attr<double>("maxzoom");
if (max_zoom)
{
lyr.set_max_zoom(* max_zoom);
}
optional<boolean> queryable = lay.get_opt_attr<boolean>("queryable");
optional<boolean> queryable = node.get_opt_attr<boolean>("queryable");
if (queryable)
{
lyr.set_queryable(* queryable);
}
optional<boolean> clear_cache =
lay.get_opt_attr<boolean>("clear-label-cache");
node.get_opt_attr<boolean>("clear-label-cache");
if (clear_cache)
{
lyr.set_clear_label_cache(* clear_cache);
}
optional<boolean> cache_features =
lay.get_opt_attr<boolean>("cache-features");
node.get_opt_attr<boolean>("cache-features");
if (cache_features)
{
lyr.set_cache_features(* cache_features);
}
optional<std::string> group_by =
lay.get_opt_attr<std::string>("group-by");
node.get_opt_attr<std::string>("group-by");
if (group_by)
{
lyr.set_group_by(* group_by);
}
xml_node::const_iterator child = lay.begin();
xml_node::const_iterator end = lay.end();
optional<unsigned> buffer_size = node.get_opt_attr<unsigned>("buffer-size");
if (buffer_size)
{
lyr.set_buffer_size(*buffer_size);
}
optional<std::string> maximum_extent = node.get_opt_attr<std::string>("maximum-extent");
if (maximum_extent)
{
box2d<double> box;
if (box.from_string(*maximum_extent))
{
lyr.set_maximum_extent(box);
}
else
{
std::ostringstream s_err;
s_err << "failed to parse 'maximum-extent' in layer " << name;
if (strict_)
{
throw config_error(s_err.str());
}
else
{
MAPNIK_LOG_WARN(load_map) << "map_parser: " << s_err.str();
}
}
}
xml_node::const_iterator child = node.begin();
xml_node::const_iterator end = node.end();
for(; child != end; ++child)
{
@ -719,7 +748,7 @@ void map_parser::parse_layer(Map & map, xml_node const& lay)
{
if (!name.empty())
{
ex.append_context(std::string(" encountered during parsing of layer '") + name + "'", lay);
ex.append_context(std::string(" encountered during parsing of layer '") + name + "'", node);
}
throw;
}
@ -894,7 +923,7 @@ void map_parser::parse_point_symbolizer(rule & rule, xml_node const & sym)
symbol.set_ignore_placement(* ignore_placement);
}
point_placement_e placement =
sym.get_attr<point_placement_e>("placement", CENTROID_POINT_PLACEMENT);
sym.get_attr<point_placement_e>("placement", symbol.get_point_placement());
symbol.set_point_placement(placement);
if (file && !file->empty())
@ -1018,9 +1047,13 @@ void map_parser::parse_markers_symbolizer(rule & rule, xml_node const& node)
sym.set_filename(expr);
}
// overall opacity - impacts both fill and stroke, like svg
optional<float> opacity = node.get_opt_attr<float>("opacity");
if (opacity) sym.set_opacity(*opacity);
optional<float> fill_opacity = node.get_opt_attr<float>("fill-opacity");
if (fill_opacity) sym.set_fill_opacity(*fill_opacity);
optional<std::string> image_transform_wkt = node.get_opt_attr<std::string>("transform");
if (image_transform_wkt)
{
@ -1065,7 +1098,7 @@ void map_parser::parse_markers_symbolizer(rule & rule, xml_node const& node)
sym.set_stroke(strk);
}
marker_placement_e placement = node.get_attr<marker_placement_e>("placement", MARKER_POINT_PLACEMENT);
marker_placement_e placement = node.get_attr<marker_placement_e>("placement",sym.get_marker_placement());
sym.set_marker_placement(placement);
parse_symbolizer_base(sym, node);
rule.append(sym);

View file

@ -352,7 +352,7 @@ void Map::set_background_image(std::string const& image_filename)
void Map::set_maximum_extent(box2d<double> const& box)
{
maximum_extent_ = box;
maximum_extent_.reset(box);
}
boost::optional<box2d<double> > const& Map::maximum_extent() const
@ -360,9 +360,9 @@ boost::optional<box2d<double> > const& Map::maximum_extent() const
return maximum_extent_;
}
boost::optional<box2d<double> > & Map::maximum_extent()
void Map::reset_maximum_extent()
{
return maximum_extent_;
maximum_extent_.reset();
}
std::string const& Map::base_path() const

View file

@ -144,7 +144,7 @@ boost::optional<marker_ptr> marker_cache::find(std::string const& uri,
}
std::string known_svg_string = mark_itr->second;
using namespace mapnik::svg;
path_ptr marker_path(boost::make_shared<svg_storage_type>());
svg_path_ptr marker_path(boost::make_shared<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());
@ -173,7 +173,7 @@ boost::optional<marker_ptr> marker_cache::find(std::string const& uri,
if (is_svg(uri))
{
using namespace mapnik::svg;
path_ptr marker_path(boost::make_shared<svg_storage_type>());
svg_path_ptr marker_path(boost::make_shared<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());

View file

@ -1,253 +0,0 @@
// mapnik
#include <mapnik/markers_placement.hpp>
#include <mapnik/geometry.hpp>
#include <mapnik/ctrans.hpp>
#include <mapnik/label_collision_detector.hpp>
#include <mapnik/global.hpp> //round
// agg
#include "agg_basics.h"
#include "agg_conv_clip_polyline.h"
#include "agg_trans_affine.h"
#include "agg_conv_transform.h"
#include "agg_conv_smooth_poly1.h"
// stl
#include <cmath>
namespace mapnik
{
template <typename Locator, typename Detector>
markers_placement<Locator, Detector>::markers_placement(
Locator &locator, box2d<double> const& size, agg::trans_affine const& tr, Detector &detector, double spacing, double max_error, bool allow_overlap)
: locator_(locator), size_(size), tr_(tr), detector_(detector), max_error_(max_error), allow_overlap_(allow_overlap)
{
marker_width_ = (size_ * tr_).width();
if (spacing >= 0)
{
spacing_ = spacing;
} else if (spacing < 0)
{
spacing_ = find_optimal_spacing(-spacing);
}
rewind();
}
template <typename Locator, typename Detector>
double markers_placement<Locator, Detector>::find_optimal_spacing(double s)
{
rewind();
//Calculate total path length
unsigned cmd = agg::path_cmd_move_to;
double length = 0;
while (!agg::is_stop(cmd))
{
double dx = next_x - last_x;
double dy = next_y - last_y;
length += std::sqrt(dx * dx + dy * dy);
last_x = next_x;
last_y = next_y;
while (agg::is_move_to(cmd = locator_.vertex(&next_x, &next_y)))
{
//Skip over "move" commands
last_x = next_x;
last_y = next_y;
}
}
unsigned points = round(length / s);
if (points == 0) return 0.0; //Path to short
return length / points;
}
template <typename Locator, typename Detector>
void markers_placement<Locator, Detector>::rewind()
{
locator_.rewind(0);
//Get first point
done_ = agg::is_stop(locator_.vertex(&next_x, &next_y)) || spacing_ < marker_width_;
last_x = next_x;
last_y = next_y; // Force request of new segment
error_ = 0;
marker_nr_ = 0;
}
template <typename Locator, typename Detector>
bool markers_placement<Locator, Detector>::get_point(
double & x, double & y, double & angle, bool add_to_detector)
{
if (done_) return false;
unsigned cmd;
/* This functions starts at the position of the previous marker,
walks along the path, counting how far it has to go in spacing_left.
If one marker can't be placed at the position it should go to it is
moved a bit. The error is compensated for in the next call to this
function.
error > 0: Marker too near to the end of the path.
error = 0: Perfect position.
error < 0: Marker too near to the beginning of the path.
*/
if (marker_nr_++ == 0)
{
//First marker
spacing_left_ = spacing_ / 2;
} else
{
spacing_left_ = spacing_;
}
spacing_left_ -= error_;
error_ = 0;
//Loop exits when a position is found or when no more segments are available
while (true)
{
//Do not place markers too close to the beginning of a segment
if (spacing_left_ < marker_width_/2)
{
set_spacing_left(marker_width_/2); //Only moves forward
}
//Error for this marker is too large. Skip to the next position.
if (abs(error_) > max_error_ * spacing_)
{
if (error_ > spacing_)
{
error_ = 0; //Avoid moving backwards
MAPNIK_LOG_WARN(markers_placement) << "Extremely large error in markers_placement. Please file a bug report.";
}
spacing_left_ += spacing_ - error_;
error_ = 0;
}
double dx = next_x - last_x;
double dy = next_y - last_y;
double segment_length = std::sqrt(dx * dx + dy * dy);
if (segment_length <= spacing_left_)
{
//Segment is to short to place marker. Find next segment
spacing_left_ -= segment_length;
last_x = next_x;
last_y = next_y;
while (agg::is_move_to(cmd = locator_.vertex(&next_x, &next_y)))
{
//Skip over "move" commands
last_x = next_x;
last_y = next_y;
}
if (agg::is_stop(cmd))
{
done_ = true;
return false;
}
continue; //Try again
}
/* At this point we know the following things:
- segment_length > spacing_left
- error is small enough
- at least half a marker fits into this segment
*/
//Check if marker really fits in this segment
if (segment_length < marker_width_)
{
//Segment to short => Skip this segment
set_spacing_left(segment_length + marker_width_/2); //Only moves forward
continue;
} else if (segment_length - spacing_left_ < marker_width_/2)
{
//Segment is long enough, but we are to close to the end
//Note: This function moves backwards. This could lead to an infinite
// loop when another function adds a positive offset. Therefore we
// only move backwards when there is no offset
if (error_ == 0)
{
set_spacing_left(segment_length - marker_width_/2, true);
} else
{
//Skip this segment
set_spacing_left(segment_length + marker_width_/2); //Only moves forward
}
continue; //Force checking of max_error constraint
}
angle = atan2(dy, dx);
x = last_x + dx * (spacing_left_ / segment_length);
y = last_y + dy * (spacing_left_ / segment_length);
box2d<double> box = perform_transform(angle, x, y);
if (!allow_overlap_ && !detector_.has_placement(box))
{
//10.0 is the approxmiate number of positions tried and choosen arbitrarily
set_spacing_left(spacing_left_ + spacing_ * max_error_ / 10.0); //Only moves forward
continue;
}
if (add_to_detector) detector_.insert(box);
last_x = x;
last_y = y;
return true;
}
}
template <typename Locator, typename Detector>
box2d<double> markers_placement<Locator, Detector>::perform_transform(double angle, double dx, double dy)
{
double x1 = size_.minx();
double x2 = size_.maxx();
double y1 = size_.miny();
double y2 = size_.maxy();
agg::trans_affine tr = tr_ * agg::trans_affine_rotation(angle).translate(dx, dy);
double xA = x1, yA = y1, xB = x2, yB = y1, xC = x2, yC = y2, xD = x1, yD = y2;
tr.transform(&xA, &yA);
tr.transform(&xB, &yB);
tr.transform(&xC, &yC);
tr.transform(&xD, &yD);
box2d<double> result(xA, yA, xC, yC);
result.expand_to_include(xB, yB);
result.expand_to_include(xD, yD);
return result;
}
template <typename Locator, typename Detector>
void markers_placement<Locator, Detector>::set_spacing_left(double sl, bool allow_negative)
{
double delta_error = sl - spacing_left_;
if (!allow_negative && delta_error < 0)
{
MAPNIK_LOG_WARN(markers_placement) << "Unexpected negative error in markers_placement. Please file a bug report.";
return;
}
#ifdef MAPNIK_DEBUG
if (delta_error == 0.0)
{
MAPNIK_LOG_WARN(markers_placement) << "Not moving at all in set_spacing_left()! Please file a bug report.";
}
#endif
error_ += delta_error;
spacing_left_ = sl;
}
typedef agg::conv_clip_polyline<geometry_type> clipped_geometry_type;
typedef coord_transform<CoordTransform,geometry_type> path_type;
typedef coord_transform<CoordTransform,clipped_geometry_type> clipped_path_type;
typedef agg::conv_transform<path_type, agg::trans_affine> transformed_path_type;
template class markers_placement<geometry_type, label_collision_detector4>;
template class markers_placement<path_type, label_collision_detector4>;
template class markers_placement<clipped_geometry_type, label_collision_detector4>;
template class markers_placement<transformed_path_type, label_collision_detector4>;
template class markers_placement<clipped_path_type, label_collision_detector4>;
template class markers_placement<agg::conv_transform<clipped_path_type,agg::trans_affine>, label_collision_detector4>;
template class markers_placement<agg::conv_smooth_poly1_curve<clipped_path_type>, label_collision_detector4>;
template class markers_placement<agg::conv_smooth_poly1_curve<geometry_type>, label_collision_detector4>;
template class markers_placement<agg::conv_smooth_poly1_curve<path_type>, label_collision_detector4>;
template class markers_placement<agg::conv_smooth_poly1_curve<clipped_geometry_type>, label_collision_detector4>;
} //ns mapnik

View file

@ -30,6 +30,7 @@ namespace mapnik {
static const char * marker_placement_strings[] = {
"point",
"interior",
"line",
""
};
@ -45,10 +46,7 @@ markers_symbolizer::markers_symbolizer()
allow_overlap_(false),
spacing_(100.0),
max_error_(0.2),
marker_p_(MARKER_POINT_PLACEMENT) {
// override the default for clipping in symbolizer base
this->set_clip(false);
}
marker_p_(MARKER_POINT_PLACEMENT) { }
markers_symbolizer::markers_symbolizer(path_expression_ptr const& filename)
: symbolizer_with_image(filename),
@ -59,10 +57,7 @@ markers_symbolizer::markers_symbolizer(path_expression_ptr const& filename)
allow_overlap_(false),
spacing_(100.0),
max_error_(0.2),
marker_p_(MARKER_POINT_PLACEMENT) {
// override the default for clipping in symbolizer base
this->set_clip(false);
}
marker_p_(MARKER_POINT_PLACEMENT) { }
markers_symbolizer::markers_symbolizer(markers_symbolizer const& rhs)
: symbolizer_with_image(rhs),
@ -74,6 +69,8 @@ markers_symbolizer::markers_symbolizer(markers_symbolizer const& rhs)
spacing_(rhs.spacing_),
max_error_(rhs.max_error_),
fill_(rhs.fill_),
fill_opacity_(rhs.fill_opacity_),
opacity_(rhs.opacity_),
stroke_(rhs.stroke_),
marker_p_(rhs.marker_p_) {}
@ -117,6 +114,16 @@ double markers_symbolizer::get_max_error() const
return max_error_;
}
void markers_symbolizer::set_opacity(float opacity)
{
opacity_ = opacity;
}
boost::optional<float> markers_symbolizer::get_opacity() const
{
return opacity_;
}
void markers_symbolizer::set_fill(color const& fill)
{
fill_ = fill;
@ -127,6 +134,16 @@ boost::optional<color> markers_symbolizer::get_fill() const
return fill_;
}
void markers_symbolizer::set_fill_opacity(float opacity)
{
fill_opacity_ = opacity;
}
boost::optional<float> markers_symbolizer::get_fill_opacity() const
{
return fill_opacity_;
}
void markers_symbolizer::set_width(expression_ptr const& width)
{
width_ = width;

View file

@ -78,6 +78,11 @@ public:
{
set_attr( sym_node, "placement", sym.get_point_placement() );
}
if (sym.get_image_transform())
{
std::string tr_str = sym.get_image_transform_string();
set_attr( sym_node, "transform", tr_str );
}
serialize_symbolizer_base(sym_node, sym);
}
@ -218,6 +223,11 @@ public:
{
set_attr(sym_node, "shield-dy", displacement.y);
}
if (sym.get_image_transform())
{
std::string tr_str = sym.get_image_transform_string();
set_attr( sym_node, "transform", tr_str );
}
serialize_symbolizer_base(sym_node, sym);
}
@ -249,7 +259,6 @@ public:
{
set_attr( sym_node, "height", mapnik::to_expression_string(*sym.height()) );
}
serialize_symbolizer_base(sym_node, sym);
}
@ -283,6 +292,10 @@ public:
{
set_attr( sym_node, "fill", sym.get_fill() );
}
if (sym.get_fill_opacity() != dfl.get_fill_opacity() || explicit_defaults_)
{
set_attr( sym_node, "fill-opacity", sym.get_fill_opacity() );
}
if (sym.get_opacity() != dfl.get_opacity() || explicit_defaults_)
{
set_attr( sym_node, "opacity", sym.get_opacity() );
@ -454,7 +467,7 @@ private:
}
if ( strk.dash_offset() != dfl.dash_offset() || explicit_defaults_ )
{
set_attr( node, "stroke-dash-offset", strk.dash_offset());
set_attr( node, "stroke-dashoffset", strk.dash_offset());
}
if ( ! strk.get_dash_array().empty() )
{
@ -728,6 +741,22 @@ void serialize_layer( ptree & map_node, const layer & layer, bool explicit_defau
set_attr( layer_node, "group-by", layer.group_by() );
}
int buffer_size = layer.buffer_size();
if ( buffer_size || explicit_defaults)
{
set_attr( layer_node, "buffer-size", buffer_size );
}
optional<box2d<double> > const& maximum_extent = layer.maximum_extent();
if ( maximum_extent)
{
std::ostringstream s;
s << std::setprecision(16)
<< maximum_extent->minx() << "," << maximum_extent->miny() << ","
<< maximum_extent->maxx() << "," << maximum_extent->maxy();
set_attr( layer_node, "maximum-extent", s.str() );
}
std::vector<std::string> const& style_names = layer.styles();
for (unsigned i = 0; i < style_names.size(); ++i)
{
@ -775,7 +804,7 @@ void serialize_map(ptree & pt, Map const & map, bool explicit_defaults)
set_attr( map_node, "background-image", *image_filename );
}
unsigned buffer_size = map.buffer_size();
int buffer_size = map.buffer_size();
if ( buffer_size || explicit_defaults)
{
set_attr( map_node, "buffer-size", buffer_size );

View file

@ -221,7 +221,11 @@ void text_symbolizer_helper<FaceManagerT, DetectorT>::initialize_points()
}
else
{
if (how_placed == POINT_PLACEMENT)
if (geom.type() == LineString)
{
label::middle_point(geom, label_x,label_y);
}
else if (how_placed == POINT_PLACEMENT)
{
label::centroid(geom, label_x, label_y);
}

View file

@ -0,0 +1,66 @@
<Map srs="+init=epsg:4326" background-color="rgba(255,255,255,.5)">
<Style name="ellipse">
<Rule>
<MarkersSymbolizer
width="240"
height="240"
fill="steelblue"
fill-opacity=".7"
stroke="yellow"
stroke-width="16"
stroke-opacity=".3"
/>
<MarkersSymbolizer
width="100"
opacity=".5"
fill="darkorange"
comp-op="multiply"
transform="skewX(50)"
allow-overlap="true"
/>
<MarkersSymbolizer
width="100"
opacity=".5"
fill="darkred"
comp-op="color-burn"
transform="skewX(-50)"
allow-overlap="true"
/>
</Rule>
</Style>
<Layer name="layer" srs="+init=epsg:4326">
<StyleName>ellipse</StyleName>
<Datasource>
<Parameter name="type">csv</Parameter>
<Parameter name="inline">
x,y
2.5,2.5
</Parameter>
</Datasource>
</Layer>
<!-- points to frame data view -->
<Style name="frame">
<Rule>
<PointSymbolizer />
</Rule>
</Style>
<Layer name="frame" srs="+init=epsg:4326">
<StyleName>frame</StyleName>
<Datasource>
<Parameter name="type">csv</Parameter>
<Parameter name="inline">
x,y
0,0
5,0
0,5
5,5
</Parameter>
</Datasource>
</Layer>
</Map>

View file

@ -0,0 +1,66 @@
<Map srs="+init=epsg:4326" background-color="rgba(255,255,255,.5)">
<Style name="ellipse">
<Rule>
<MarkersSymbolizer
width="240"
height="240"
fill="steelblue"
fill-opacity=".7"
stroke="yellow"
stroke-width="16"
stroke-opacity=".3"
/>
<MarkersSymbolizer
width="100"
opacity=".5"
fill="darkorange"
comp-op="grain-extract"
transform="skewX(50)"
allow-overlap="true"
/>
<MarkersSymbolizer
width="100"
opacity=".5"
fill="darkred"
comp-op="color-burn"
transform="skewX(-50)"
allow-overlap="true"
/>
</Rule>
</Style>
<Layer name="layer" srs="+init=epsg:4326">
<StyleName>ellipse</StyleName>
<Datasource>
<Parameter name="type">csv</Parameter>
<Parameter name="inline">
x,y
2.5,2.5
</Parameter>
</Datasource>
</Layer>
<!-- points to frame data view -->
<Style name="frame">
<Rule>
<PointSymbolizer />
</Rule>
</Style>
<Layer name="frame" srs="+init=epsg:4326">
<StyleName>frame</StyleName>
<Datasource>
<Parameter name="type">csv</Parameter>
<Parameter name="inline">
x,y
0,0
5,0
0,5
5,5
</Parameter>
</Datasource>
</Layer>
</Map>

View file

@ -3,7 +3,14 @@
<Style name="1">
<Rule>
<LineSymbolizer stroke-width=".2" stroke="grey"/>
<MarkersSymbolizer stroke="green" stroke-width="1.3" fill="yellow"/>
<MarkersSymbolizer
file="shape://arrow"
transform="scale(.8,.5)"
stroke="green"
stroke-width="1.3"
fill="yellow"
placement="line"
/>
</Rule>
</Style>

View file

@ -2,8 +2,15 @@
<Map background-color="white" srs="+init=epsg:4326" minimum-version="0.7.2">
<Style name="1">
<Rule>
<LineSymbolizer stroke-width=".2" stroke="grey"/>
<MarkersSymbolizer file="../svg/crosshair16x16.svg"/>
<LineSymbolizer offset="-2" stroke-width="2" stroke="red"/>
<LineSymbolizer stroke-width="2" stroke="orange"/>
<LineSymbolizer offset="2" stroke-width="2" stroke="yellow"/>
<LineSymbolizer offset="4" stroke-width="2" stroke="green"/>
<MarkersSymbolizer
file="../svg/octocat.svg"
transform="scale(.13)"
placement="line"
/>
</Rule>
</Style>

View file

@ -0,0 +1,52 @@
<Map srs="+init=epsg:4326" background-color="steelblue">
<!-- test case for https://github.com/mapnik/mapnik/issues/1354 -->
<Style name="labels">
<Rule>
<TextSymbolizer
face-name="DejaVu Sans Book"
size="22"
halo-radius="10"
placement="point"
allow-overlap="false">
'this text halos should not overlap vertically'
</TextSymbolizer>
</Rule>
</Style>
<Layer name="points" srs="+init=epsg:4326">
<StyleName>labels</StyleName>
<Datasource>
<Parameter name="type">csv</Parameter>
<Parameter name="inline">
x,y
2,2
2,2.3
</Parameter>
</Datasource>
</Layer>
<!-- points to frame data view -->
<Style name="frame">
<Rule>
<PointSymbolizer />
</Rule>
</Style>
<Layer name="frame" srs="+init=epsg:4326">
<StyleName>frame</StyleName>
<Datasource>
<Parameter name="type">csv</Parameter>
<Parameter name="inline">
x,y
0,0
5,0
0,5
5,5
</Parameter>
</Datasource>
</Layer>
</Map>

View file

@ -0,0 +1,17 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-0.2 -1 379 334">
<path id="puddle" fill="#9CDAF1" d="m296.94 295.43c0 20.533-47.56 37.176-106.22 37.176-58.67 0-106.23-16.643-106.23-37.176s47.558-37.18 106.23-37.18c58.66 0 106.22 16.65 106.22 37.18z"/>
<g id="shadow-legs" fill="#7DBBE6">
<path d="m161.85 331.22v-26.5c0-3.422-.619-6.284-1.653-8.701 6.853 5.322 7.316 18.695 7.316 18.695v17.004c6.166.481 12.534.773 19.053.861l-.172-16.92c-.944-23.13-20.769-25.961-20.769-25.961-7.245-1.645-7.137 1.991-6.409 4.34-7.108-12.122-26.158-10.556-26.158-10.556-6.611 2.357-.475 6.607-.475 6.607 10.387 3.775 11.33 15.105 11.33 15.105v23.622c5.72.98 11.71 1.79 17.94 2.4z"/>
<path d="m245.4 283.48s-19.053-1.566-26.16 10.559c.728-2.35.839-5.989-6.408-4.343 0 0-19.824 2.832-20.768 25.961l-.174 16.946c6.509-.025 12.876-.254 19.054-.671v-17.219s.465-13.373 7.316-18.695c-1.034 2.417-1.653 5.278-1.653 8.701v26.775c6.214-.544 12.211-1.279 17.937-2.188v-24.113s.944-11.33 11.33-15.105c0-.01 6.13-4.26-.48-6.62z"/>
</g>
<path id="cat" d="m378.18 141.32l.28-1.389c-31.162-6.231-63.141-6.294-82.487-5.49 3.178-11.451 4.134-24.627 4.134-39.32 0-21.073-7.917-37.931-20.77-50.759 2.246-7.25 5.246-23.351-2.996-43.963 0 0-14.541-4.617-47.431 17.396-12.884-3.22-26.596-4.81-40.328-4.81-15.109 0-30.376 1.924-44.615 5.83-33.94-23.154-48.923-18.411-48.923-18.411-9.78 24.457-3.733 42.566-1.896 47.063-11.495 12.406-18.513 28.243-18.513 47.659 0 14.658 1.669 27.808 5.745 39.237-19.511-.71-50.323-.437-80.373 5.572l.276 1.389c30.231-6.046 61.237-6.256 80.629-5.522.898 2.366 1.899 4.661 3.021 6.879-19.177.618-51.922 3.062-83.303 11.915l.387 1.36c31.629-8.918 64.658-11.301 83.649-11.882 11.458 21.358 34.048 35.152 74.236 39.484-5.704 3.833-11.523 10.349-13.881 21.374-7.773 3.718-32.379 12.793-47.142-12.599 0 0-8.264-15.109-24.082-16.292 0 0-15.344-.235-1.059 9.562 0 0 10.267 4.838 17.351 23.019 0 0 9.241 31.01 53.835 21.061v32.032s-.943 11.33-11.33 15.105c0 0-6.137 4.249.475 6.606 0 0 28.792 2.361 28.792-21.238v-34.929s-1.142-13.852 5.663-18.667v57.371s-.47 13.688-7.551 18.881c0 0-4.723 8.494 5.663 6.137 0 0 19.824-2.832 20.769-25.961l.449-58.06h4.765l.453 58.06c.943 23.129 20.768 25.961 20.768 25.961 10.383 2.357 5.663-6.137 5.663-6.137-7.08-5.193-7.551-18.881-7.551-18.881v-56.876c6.801 5.296 5.663 18.171 5.663 18.171v34.929c0 23.6 28.793 21.238 28.793 21.238 6.606-2.357.474-6.606.474-6.606-10.386-3.775-11.33-15.105-11.33-15.105v-45.786c0-17.854-7.518-27.309-14.87-32.3 42.859-4.25 63.426-18.089 72.903-39.591 18.773.516 52.557 2.803 84.873 11.919l.384-1.36c-32.131-9.063-65.692-11.408-84.655-11.96.898-2.172 1.682-4.431 2.378-6.755 19.25-.80 51.38-.79 82.66 5.46z"/>
<path id="face" fill="#F4CBB2" d="m258.19 94.132c9.231 8.363 14.631 18.462 14.631 29.343 0 50.804-37.872 52.181-84.585 52.181-46.721 0-84.589-7.035-84.589-52.181 0-10.809 5.324-20.845 14.441-29.174 15.208-13.881 40.946-6.531 70.147-6.531 29.07-.004 54.72-7.429 69.95 6.357z"/>
<path id="eyes" fill="#FFF" d="m160.1 126.06 c0 13.994-7.88 25.336-17.6 25.336-9.72 0-17.6-11.342-17.6-25.336 0-13.992 7.88-25.33 17.6-25.33 9.72.01 17.6 11.34 17.6 25.33z m94.43 0 c0 13.994-7.88 25.336-17.6 25.336-9.72 0-17.6-11.342-17.6-25.336 0-13.992 7.88-25.33 17.6-25.33 9.72.01 17.6 11.34 17.6 25.33z"/>
<g fill="#AD5C51">
<path id="pupils" d="m154.46 126.38 c0 9.328-5.26 16.887-11.734 16.887s-11.733-7.559-11.733-16.887c0-9.331 5.255-16.894 11.733-16.894 6.47 0 11.73 7.56 11.73 16.89z m94.42 0 c0 9.328-5.26 16.887-11.734 16.887s-11.733-7.559-11.733-16.887c0-9.331 5.255-16.894 11.733-16.894 6.47 0 11.73 7.56 11.73 16.89z"/>
<circle id="nose" cx="188.5" cy="148.56" r="4.401"/>
<path id="mouth" d="m178.23 159.69c-.26-.738.128-1.545.861-1.805.737-.26 1.546.128 1.805.861 1.134 3.198 4.167 5.346 7.551 5.346s6.417-2.147 7.551-5.346c.26-.738 1.067-1.121 1.805-.861s1.121 1.067.862 1.805c-1.529 4.324-5.639 7.229-10.218 7.229s-8.68-2.89-10.21-7.22z"/>
</g>
<path id="octo" fill="#C3E4D8" d="m80.641 179.82 c0 1.174-1.376 2.122-3.07 2.122-1.693 0-3.07-.948-3.07-2.122 0-1.175 1.377-2.127 3.07-2.127 1.694 0 3.07.95 3.07 2.13z m8.5 4.72 c0 1.174-1.376 2.122-3.07 2.122-1.693 0-3.07-.948-3.07-2.122 0-1.175 1.377-2.127 3.07-2.127 1.694 0 3.07.95 3.07 2.13z m5.193 6.14 c0 1.174-1.376 2.122-3.07 2.122-1.693 0-3.07-.948-3.07-2.122 0-1.175 1.377-2.127 3.07-2.127 1.694 0 3.07.95 3.07 2.13z m4.72 7.08 c0 1.174-1.376 2.122-3.07 2.122-1.693 0-3.07-.948-3.07-2.122 0-1.175 1.377-2.127 3.07-2.127 1.694 0 3.07.95 3.07 2.13z m5.188 6.61 c0 1.174-1.376 2.122-3.07 2.122-1.693 0-3.07-.948-3.07-2.122 0-1.175 1.377-2.127 3.07-2.127 1.694 0 3.07.95 3.07 2.13z m7.09 5.66 c0 1.174-1.376 2.122-3.07 2.122-1.693 0-3.07-.948-3.07-2.122 0-1.175 1.377-2.127 3.07-2.127 1.694 0 3.07.95 3.07 2.13z m9.91 3.78 c0 1.174-1.376 2.122-3.07 2.122-1.693 0-3.07-.948-3.07-2.122 0-1.175 1.377-2.127 3.07-2.127 1.694 0 3.07.95 3.07 2.13z m9.87 0 c0 1.174-1.376 2.122-3.07 2.122-1.693 0-3.07-.948-3.07-2.122 0-1.175 1.377-2.127 3.07-2.127 1.694 0 3.07.95 3.07 2.13z m10.01 -1.64 c0 1.174-1.376 2.122-3.07 2.122-1.693 0-3.07-.948-3.07-2.122 0-1.175 1.377-2.127 3.07-2.127 1.694 0 3.07.95 3.07 2.13z"/>
<path id="drop" fill="#9CDAF1" d="m69.369 186.12l-3.066 10.683s-.8 3.861 2.84 4.546c3.8-.074 3.486-3.627 3.223-4.781z"/>
</svg>

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 47 KiB

View file

@ -0,0 +1,39 @@
#coding=utf8
import os
import mapnik
from utilities import execution_path
from nose.tools import *
def setup():
# All of the paths used are relative, if we run the tests
# from another directory we need to chdir()
os.chdir(execution_path('.'))
def test_marker_ellipse_render1():
m = mapnik.Map(256,256)
mapnik.load_map(m,'../data/good_maps/marker_ellipse_transform.xml')
m.zoom_all()
im = mapnik.Image(m.width,m.height)
mapnik.render(m,im)
actual = '/tmp/mapnik-marker-ellipse-render1.png'
expected = 'images/support/mapnik-marker-ellipse-render1.png'
im.save(actual)
expected_im = mapnik.Image.open(expected)
eq_(im.tostring(),expected_im.tostring(), 'failed comparing actual (%s) and expected (%s)' % (actual,'tests/python_tests/'+ expected))
#def test_marker_ellipse_render2():
# # currently crashes https://github.com/mapnik/mapnik/issues/1365
# m = mapnik.Map(256,256)
# mapnik.load_map(m,'../data/good_maps/marker_ellipse_transform2.xml')
# m.zoom_all()
# im = mapnik.Image(m.width,m.height)
# mapnik.render(m,im)
# actual = '/tmp/mapnik-marker-ellipse-render2.png'
# expected = 'images/support/mapnik-marker-ellipse-render2.png'
# im.save(actual)
# expected_im = mapnik.Image.open(expected)
# eq_(im.tostring(),expected_im.tostring(), 'failed comparing actual (%s) and expected (%s)' % (actual,'tests/python_tests/'+ expected))
if __name__ == "__main__":
setup()
[eval(run)() for run in dir() if 'test_' in run]

View file

@ -21,6 +21,15 @@ def test_line_symbolizer_init():
s = mapnik.LineSymbolizer()
eq_(s.rasterizer, mapnik.line_rasterizer.FULL)
def test_line_symbolizer_stroke_reference():
l = mapnik.LineSymbolizer(mapnik.Color('green'),0.1)
l.stroke.add_dash(.1,.1)
l.stroke.add_dash(.1,.1)
eq_(l.stroke.get_dashes(), [(.1,.1),(.1,.1)])
eq_(l.stroke.color,mapnik.Color('green'))
eq_(l.stroke.opacity,1.0)
assert_almost_equal(l.stroke.width,0.1)
# ShieldSymbolizer initialization
def test_shieldsymbolizer_init():
s = mapnik.ShieldSymbolizer(mapnik.Expression('[Field Name]'), 'DejaVu Sans Bold', 6, mapnik.Color('#000000'), mapnik.PathExpression('../data/images/dummy.png'))
@ -126,7 +135,8 @@ def test_pointsymbolizer_init():
def test_markersymbolizer_init():
p = mapnik.MarkersSymbolizer()
eq_(p.allow_overlap, False)
eq_(p.opacity,1)
eq_(p.opacity,None)
eq_(p.fill_opacity,None)
eq_(p.filename,'shape://ellipse')
eq_(p.placement,mapnik.marker_placement.POINT_PLACEMENT)
eq_(p.fill,None)
@ -154,9 +164,11 @@ def test_markersymbolizer_init():
p.fill = mapnik.Color('white')
p.allow_overlap = True
p.opacity = 0.5
p.fill_opacity = .01
eq_(p.allow_overlap, True)
eq_(p.opacity, 0.5)
eq_(p.fill_opacity, 0.5)
# PointSymbolizer missing image file

View file

@ -20,13 +20,13 @@ def setup():
grid_correct_old = {"keys": ["", "North West", "North East", "South West", "South East"], "data": {"South East": {"Name": "South East"}, "North East": {"Name": "North East"}, "North West": {"Name": "North West"}, "South West": {"Name": "South West"}}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " !!! ### ", " !!!!! ##### ", " !!!!! ##### ", " !!! ### ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " $$$$ %%%% ", " $$$$$ %%%%% ", " $$$$$ %%%%% ", " $$$ %%% ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "]}
# now using svg rendering
grid_correct_old2 = {"data": {"North East": {"Name": "North East"}, "North West": {"Name": "North West"}, "South East": {"Name": "South East"}, "South West": {"Name": "South West"}}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " !!! ### ", " !!! ### ", " !!! ### ", " !!! ### ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " $$$ %%% ", " $$$ %%% ", " $$$ %%% ", " $ % ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "], "keys": ["", "North West", "North East", "South West", "South East"]}
grid_correct_old2 = {"data": {"North East": {"Name": "North East"}, "North West": {"Name": "North West"}, "South East": {"Name": "South East"}, "South West": {"Name": "South West"}}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " !!! ### ", " !!! ### ", " !!! ### ", " !!! ### ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " $$$ %%% ", " $$$ %%% ", " $$$ %%% ", " $$$ %%% ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "], "keys": ["", "North West", "North East", "South West", "South East"]}
# previous rendering using agg ellipse directly
grid_correct_new = {"keys": ["", "North West", "North East", "South West", "South East"], "data": {"South East": {"Name": "South East"}, "North East": {"Name": "North East"}, "North West": {"Name": "North West"}, "South West": {"Name": "South West"}}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " !! ## ", " !!! ### ", " !! ## ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " $$$ %% ", " $$$ %%% ", " $$ %% ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "]}
grid_correct_new = {"data": {"North East": {"Name": "North East"}, "North West": {"Name": "North West"}, "South East": {"Name": "South East"}, "South West": {"Name": "South West"}}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " !! ## ", " !!! ### ", " !! ## ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " $$ %% ", " $$$ %% ", " $$ %% ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "], "keys": ["", "North West", "North East", "South West", "South East"]}
# newer rendering using svg
grid_correct_new2 = {"data": {"North East": {"Name": "North East"}, "North West": {"Name": "North West"}, "South East": {"Name": "South East"}, "South West": {"Name": "South West"}}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " !! ## ", " !!! ## ", " !! ## ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " $$ %% ", " $$ %% ", " $ % ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "], "keys": ["", "North West", "North East", "South West", "South East"]}
grid_correct_new2 = {"data": {"North East": {"Name": "North East"}, "North West": {"Name": "North West"}, "South East": {"Name": "South East"}, "South West": {"Name": "South West"}}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " !! ## ", " !!! ### ", " !! ## ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " $$ %% ", " $$$ %% ", " $$ %% ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "], "keys": ["", "North West", "North East", "South West", "South East"]}
def resolve(grid,row,col):
""" Resolve the attributes for a given pixel in a grid.
@ -108,7 +108,7 @@ def show_grids(name,g1,g2):
val += '\n\t%s\n\t%s' % (g1_file,g2_file)
return val
def test_render_grid():
def test_render_grid_old():
""" test old method """
width,height = 256,256
m = create_grid_map(width,height)
@ -117,7 +117,7 @@ def test_render_grid():
lr_lonlat = mapnik.Coord(143.40,-38.80)
m.zoom_to_box(mapnik.Box2d(ul_lonlat,lr_lonlat))
grid = mapnik.render_grid(m,0,key='Name',resolution=4,fields=['Name'])
eq_(grid,grid_correct_old2,show_grids('old',grid,grid_correct_old2))
eq_(grid,grid_correct_old2,show_grids('old-markers',grid,grid_correct_old2))
eq_(resolve(grid,0,0),None)
# check every pixel of the nw symbol
@ -128,7 +128,7 @@ def test_render_grid():
eq_(resolve(grid,23,10),expected)
eq_(resolve(grid,23,11),expected)
def test_render_grid2():
def test_render_grid_new():
""" test old against new"""
width,height = 256,256
m = create_grid_map(width,height)
@ -140,7 +140,7 @@ def test_render_grid2():
grid = mapnik.Grid(m.width,m.height,key='Name')
mapnik.render_layer(m,grid,layer=0,fields=['Name'])
utf1 = grid.encode('utf',resolution=4)
eq_(utf1,grid_correct_new2,show_grids('new',utf1,grid_correct_new2))
eq_(utf1,grid_correct_new2,show_grids('new-markers',utf1,grid_correct_new2))
# check a full view is the same as a full image
grid_view = grid.view(0,0,width,height)
@ -164,7 +164,7 @@ def test_render_grid2():
grid_feat_id = {'keys': ['', '3', '4', '2', '1'], 'data': {'1': {'Name': 'South East'}, '3': {'Name': u'North West'}, '2': {'Name': 'South West'}, '4': {'Name': 'North East'}}, 'grid': [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' !! ## ', ' !!! ### ', ' !! ## ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' $$$ %% ', ' $$$ %%% ', ' $$ %% ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ']}
grid_feat_id2 = {"data": {"1": {"Name": "South East"}, "2": {"Name": "South West"}, "3": {"Name": "North West"}, "4": {"Name": "North East"}}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " !! ## ", " !!! ## ", " !! ## ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " $$ %% ", " $$ %% ", " $ % ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "], "keys": ["", "3", "4", "2", "1"]}
grid_feat_id2 = {"data": {"1": {"Name": "South East"}, "2": {"Name": "South West"}, "3": {"Name": "North West"}, "4": {"Name": "North East"}}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " !! ## ", " !!! ### ", " !! ## ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " $$ %% ", " $$$ %% ", " $$ %% ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "], "keys": ["", "3", "4", "2", "1"]}
def test_render_grid3():
""" test using feature id"""
@ -177,7 +177,7 @@ def test_render_grid3():
grid = mapnik.Grid(m.width,m.height,key='__id__')
mapnik.render_layer(m,grid,layer=0,fields=['__id__','Name'])
utf1 = grid.encode('utf',resolution=4)
eq_(utf1,grid_feat_id2,show_grids('id',utf1,grid_feat_id2))
eq_(utf1,grid_feat_id2,show_grids('id-markers',utf1,grid_feat_id2))
# check a full view is the same as a full image
grid_view = grid.view(0,0,width,height)
# for kicks check at full res too
@ -282,7 +282,7 @@ def test_line_rendering():
eq_(utf1,line_expected,show_grids('line',utf1,line_expected))
#open('test.json','w').write(json.dumps(grid.encode()))
point_expected = {"keys": ["", "3", "4", "2", "1"], "data": {"1": {"Name": "South East"}, "3": {"Name": "North West"}, "2": {"Name": "South West"}, "4": {"Name": "North East"}}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " !!!! #### ", " !!!! #### ", " !!!! #### ", " !!!! #### ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " $$$$ %%%% ", " $$$$ %%%% ", " $$$$ %%%% ", " $$$$ %%%% ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "]}
point_expected = {"data": {"1": {"Name": "South East"}, "2": {"Name": "South West"}, "3": {"Name": "North West"}, "4": {"Name": "North East"}}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " !!!! #### ", " !!!! #### ", " !!!! #### ", " !!!! #### ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " $$$$ %%%% ", " $$$$ %%%% ", " $$$$ %%%% ", " $$$$ %%%% ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "], "keys": ["", "3", "4", "2", "1"]}
def test_point_symbolizer_grid():
width,height = 256,256

View file

@ -155,8 +155,9 @@ int main (int argc,char** argv)
{
std::clog << "found width of '" << w << "' and height of '" << h << "'\n";
}
mapnik::image_32 im(w,h);
agg::rendering_buffer buf(im.raw_data(), w, h, w * 4);
// 10 pixel buffer to avoid edge clipping of 100% svg's
mapnik::image_32 im(w+10,h+10);
agg::rendering_buffer buf(im.raw_data(), im.width(), im.height(), im.width() * 4);
pixfmt pixf(buf);
renderer_base renb(pixf);
@ -165,7 +166,7 @@ int main (int argc,char** argv)
// center the svg marker on '0,0'
agg::trans_affine mtx = agg::trans_affine_translation(-c.x,-c.y);
// render the marker at the center of the marker box
mtx.translate(0.5 * w, 0.5 * h);
mtx.translate(0.5 * im.width(), 0.5 * im.height());
mapnik::svg::vertex_stl_adapter<mapnik::svg::svg_path_storage> stl_storage((*marker.get_vector_data())->source());
mapnik::svg::svg_path_adapter svg_path(stl_storage);