2006-03-31 12:32:02 +02:00
|
|
|
/*****************************************************************************
|
Patch from David Eastcott :
1. Modified Text Symbolizer
a) corrected line fragment centering (for 2nd and subsequent lines, when line breaks occur).
b) adjusted vertical alignment calculation so that:
i) middle -> has the center of the text line(s) at the point origin
ii) bottom -> has the text line(s) below the point origin
iii) top -> has the text line(s) above the point origin
c) added new text_symbolizer attribute: 'wrap_before', value range: true/false, default == false
allows line breaks at first wrap_char before wrap_width as an alternative to the original
which was to create the line break at the first wrap_char after wrap_width
d) added new text_symbolizer attribute: 'horizontal_alignment', value range: left/middle/right, default == middle
i) left -> has all text line(s) to left of the point origin
ii) middle -> has all text line(s) centered on the the point origin
iii) right -> has all text line(s) to the right of the point origin
NOTE: dx, dy position adjustments are applied after alignments and before Justify.
e) added new text_symbolizer attribute: 'justify_alignment', value range: left/middle/right, default == middle
i) left -> after alignments, has all text line(s) are left justified (left to right reading)
ii) middle -> after alignments, has all text line(s) center justified
iii) right -> after alignments, has all text line(s) right justified (right to left reading)
f) added new text_symbolizer attribute: 'opacity', value range: 0.0 thru 1.0; 1.0 == fully opaque
g) modified positioning to compensate for both line_spacing and character_spacing, to ensure proper
centering of the text envelope. Also ensure that centering occurs correctly even if no wrapping
occurs. Line spacing is uniform and consistent and compensates for errors between text_size and
the actual size (ci.height is inconsistent, depending on case and character); fixes issue with
multi-line text where some lines have a slight gap and others are compressed together.
2. Modified shield_symbolizer
a) added the attributes:
i) allow_overlap
ii) vertical_alignment
iii) horizontal_alignment
iv) justify_alignment
v) wrap_width
vi) wrap_character
vii) wrap_before
viii) text_convert
ix) line_spacing
x) character_spacing
xi) opacity
b) added new shield_symbolizer attribute: 'unlock_image', value range: true/false, default == false
i) false == image and text placement behaviour same as before
ii) true == image placement independant of text, image is always centered at geometry point, text placed per attributes,
dx/dy only affect text.
Allows user to create point markers with text, but both the text and image rendering collision detection are done
as a pair (they come and go together - solves problem if using point_symbolizer and text_symbolizers where one or the
other are omitted due to overlaps, but not both)
c) extended choices for the attribute 'placement' to include vertex; effect is limited to the shield_symbolizer
Allows an attempted placement at every vertex available, gives additional shield placement volume when using line geometry
d) ensured that the text placement was not updating the detector unless a shield image was actually placed.
e) added new shield_symbolizer attribute: 'no_text', value range: true/false, default = false
When set true, the text for the feature is ignored ('space' subsituted) so that pure graphic symbols can be used
and no text is rendered over top of them.
2009-10-19 15:52:53 +02:00
|
|
|
*
|
2006-03-31 12:32:02 +02:00
|
|
|
* This file is part of Mapnik (c++ mapping toolkit)
|
|
|
|
*
|
2011-10-23 15:04:25 +02:00
|
|
|
* Copyright (C) 2011 Artem Pavlenko
|
2006-02-07 15:41:41 +01:00
|
|
|
*
|
2006-03-31 12:32:02 +02:00
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
2006-02-07 15:41:41 +01:00
|
|
|
*
|
2006-03-31 12:32:02 +02:00
|
|
|
* This library is distributed in the hope that it will be useful,
|
2006-02-07 15:41:41 +01:00
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
2006-03-31 12:32:02 +02:00
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
2006-02-07 15:41:41 +01:00
|
|
|
*
|
2006-03-31 12:32:02 +02:00
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
*
|
|
|
|
*****************************************************************************/
|
2006-02-07 15:41:41 +01:00
|
|
|
|
2007-10-08 19:42:41 +02:00
|
|
|
// mapnik
|
|
|
|
#include <mapnik/agg_renderer.hpp>
|
2010-06-13 14:03:42 +02:00
|
|
|
#include <mapnik/agg_rasterizer.hpp>
|
2012-03-28 13:02:03 +02:00
|
|
|
#include <mapnik/agg_helpers.hpp>
|
2012-08-02 22:10:05 +02:00
|
|
|
#include <mapnik/graphics.hpp>
|
|
|
|
|
2013-01-04 08:19:54 +01:00
|
|
|
#include <mapnik/rule.hpp>
|
2012-08-02 22:10:05 +02:00
|
|
|
#include <mapnik/debug.hpp>
|
|
|
|
#include <mapnik/layer.hpp>
|
2013-01-04 08:19:54 +01:00
|
|
|
#include <mapnik/label_collision_detector.hpp>
|
2012-08-02 22:10:05 +02:00
|
|
|
#include <mapnik/feature_type_style.hpp>
|
2012-03-08 17:37:58 +01:00
|
|
|
#include <mapnik/marker.hpp>
|
2011-01-26 02:18:40 +01:00
|
|
|
#include <mapnik/marker_cache.hpp>
|
2007-10-08 19:42:41 +02:00
|
|
|
#include <mapnik/unicode.hpp>
|
2008-06-29 12:58:48 +02:00
|
|
|
#include <mapnik/font_set.hpp>
|
2010-06-18 17:39:32 +02:00
|
|
|
#include <mapnik/parse_path.hpp>
|
2012-01-20 22:43:05 +01:00
|
|
|
#include <mapnik/map.hpp>
|
2011-01-26 10:19:00 +01:00
|
|
|
#include <mapnik/svg/svg_converter.hpp>
|
2012-08-28 02:58:49 +02:00
|
|
|
#include <mapnik/svg/svg_renderer_agg.hpp>
|
2011-01-26 10:19:00 +01:00
|
|
|
#include <mapnik/svg/svg_path_adapter.hpp>
|
2013-03-13 00:56:47 +01:00
|
|
|
#include <mapnik/pixel_position.hpp>
|
2012-08-02 22:10:05 +02:00
|
|
|
|
2012-04-03 14:10:30 +02:00
|
|
|
#include <mapnik/image_compositing.hpp>
|
2012-05-04 11:23:49 +02:00
|
|
|
#include <mapnik/image_filter.hpp>
|
2012-06-29 13:31:41 +02:00
|
|
|
#include <mapnik/image_util.hpp>
|
2008-02-05 14:58:47 +01:00
|
|
|
#include "agg_rendering_buffer.h"
|
|
|
|
#include "agg_pixfmt_rgba.h"
|
2013-07-24 00:41:59 +02:00
|
|
|
#include "agg_color_rgba.h"
|
2006-02-07 15:41:41 +01:00
|
|
|
#include "agg_scanline_u.h"
|
2012-07-17 18:10:24 +02:00
|
|
|
#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"
|
2007-10-08 19:42:41 +02:00
|
|
|
// boost
|
2012-02-17 00:03:46 +01:00
|
|
|
#include <boost/math/special_functions/round.hpp>
|
2007-10-08 19:42:41 +02:00
|
|
|
|
|
|
|
// stl
|
2010-03-12 00:19:12 +01:00
|
|
|
#include <cmath>
|
|
|
|
|
Patch from David Eastcott :
1. Modified Text Symbolizer
a) corrected line fragment centering (for 2nd and subsequent lines, when line breaks occur).
b) adjusted vertical alignment calculation so that:
i) middle -> has the center of the text line(s) at the point origin
ii) bottom -> has the text line(s) below the point origin
iii) top -> has the text line(s) above the point origin
c) added new text_symbolizer attribute: 'wrap_before', value range: true/false, default == false
allows line breaks at first wrap_char before wrap_width as an alternative to the original
which was to create the line break at the first wrap_char after wrap_width
d) added new text_symbolizer attribute: 'horizontal_alignment', value range: left/middle/right, default == middle
i) left -> has all text line(s) to left of the point origin
ii) middle -> has all text line(s) centered on the the point origin
iii) right -> has all text line(s) to the right of the point origin
NOTE: dx, dy position adjustments are applied after alignments and before Justify.
e) added new text_symbolizer attribute: 'justify_alignment', value range: left/middle/right, default == middle
i) left -> after alignments, has all text line(s) are left justified (left to right reading)
ii) middle -> after alignments, has all text line(s) center justified
iii) right -> after alignments, has all text line(s) right justified (right to left reading)
f) added new text_symbolizer attribute: 'opacity', value range: 0.0 thru 1.0; 1.0 == fully opaque
g) modified positioning to compensate for both line_spacing and character_spacing, to ensure proper
centering of the text envelope. Also ensure that centering occurs correctly even if no wrapping
occurs. Line spacing is uniform and consistent and compensates for errors between text_size and
the actual size (ci.height is inconsistent, depending on case and character); fixes issue with
multi-line text where some lines have a slight gap and others are compressed together.
2. Modified shield_symbolizer
a) added the attributes:
i) allow_overlap
ii) vertical_alignment
iii) horizontal_alignment
iv) justify_alignment
v) wrap_width
vi) wrap_character
vii) wrap_before
viii) text_convert
ix) line_spacing
x) character_spacing
xi) opacity
b) added new shield_symbolizer attribute: 'unlock_image', value range: true/false, default == false
i) false == image and text placement behaviour same as before
ii) true == image placement independant of text, image is always centered at geometry point, text placed per attributes,
dx/dy only affect text.
Allows user to create point markers with text, but both the text and image rendering collision detection are done
as a pair (they come and go together - solves problem if using point_symbolizer and text_symbolizers where one or the
other are omitted due to overlaps, but not both)
c) extended choices for the attribute 'placement' to include vertex; effect is limited to the shield_symbolizer
Allows an attempted placement at every vertex available, gives additional shield placement volume when using line geometry
d) ensured that the text placement was not updating the detector unless a shield image was actually placed.
e) added new shield_symbolizer attribute: 'no_text', value range: true/false, default = false
When set true, the text for the feature is ignored ('space' subsituted) so that pure graphic symbols can be used
and no text is rendered over top of them.
2009-10-19 15:52:53 +02:00
|
|
|
namespace mapnik
|
2006-02-07 15:41:41 +01:00
|
|
|
{
|
2009-12-16 21:02:06 +01:00
|
|
|
|
|
|
|
template <typename T>
|
2010-06-10 16:12:20 +02:00
|
|
|
agg_renderer<T>::agg_renderer(Map const& m, T & pixmap, double scale_factor, unsigned offset_x, unsigned offset_y)
|
2010-06-14 12:29:29 +02:00
|
|
|
: feature_style_processor<agg_renderer>(m, scale_factor),
|
2009-12-16 21:02:06 +01:00
|
|
|
pixmap_(pixmap),
|
2012-04-18 10:37:02 +02:00
|
|
|
internal_buffer_(),
|
2012-06-29 13:31:41 +02:00
|
|
|
current_buffer_(&pixmap),
|
2013-10-04 01:37:09 +02:00
|
|
|
t_(m.width(),m.height(),m.get_current_extent(),offset_x,offset_y),
|
2012-04-23 16:41:18 +02:00
|
|
|
style_level_compositing_(false),
|
2009-12-16 21:02:06 +01:00
|
|
|
width_(pixmap_.width()),
|
|
|
|
height_(pixmap_.height()),
|
2010-06-10 16:12:20 +02:00
|
|
|
scale_factor_(scale_factor),
|
2009-12-16 21:02:06 +01:00
|
|
|
font_engine_(),
|
|
|
|
font_manager_(font_engine_),
|
2013-09-20 15:00:11 +02:00
|
|
|
detector_(std::make_shared<label_collision_detector4>(box2d<double>(-m.buffer_size(), -m.buffer_size(), m.width() + m.buffer_size() ,m.height() + m.buffer_size()))),
|
2013-03-15 01:05:06 +01:00
|
|
|
ras_ptr(new rasterizer),
|
|
|
|
query_extent_(),
|
|
|
|
gamma_method_(GAMMA_POWER),
|
|
|
|
gamma_(1.0)
|
2011-10-12 02:05:35 +02:00
|
|
|
{
|
2012-02-02 02:48:56 +01:00
|
|
|
setup(m);
|
2011-10-12 02:05:35 +02:00
|
|
|
}
|
|
|
|
|
2013-02-21 04:52:54 +01:00
|
|
|
template <typename T>
|
2013-02-26 01:33:08 +01:00
|
|
|
agg_renderer<T>::agg_renderer(Map const& m, request const& req, T & pixmap, double scale_factor, unsigned offset_x, unsigned offset_y)
|
2013-02-21 04:52:54 +01:00
|
|
|
: feature_style_processor<agg_renderer>(m, scale_factor),
|
|
|
|
pixmap_(pixmap),
|
|
|
|
internal_buffer_(),
|
|
|
|
current_buffer_(&pixmap),
|
2013-10-04 01:37:09 +02:00
|
|
|
t_(req.width(),req.height(),req.extent(),offset_x,offset_y),
|
2013-02-21 04:52:54 +01:00
|
|
|
style_level_compositing_(false),
|
|
|
|
width_(pixmap_.width()),
|
|
|
|
height_(pixmap_.height()),
|
|
|
|
scale_factor_(scale_factor),
|
|
|
|
font_engine_(),
|
|
|
|
font_manager_(font_engine_),
|
2013-09-20 15:00:11 +02:00
|
|
|
detector_(std::make_shared<label_collision_detector4>(box2d<double>(-req.buffer_size(), -req.buffer_size(), req.width() + req.buffer_size() ,req.height() + req.buffer_size()))),
|
2013-03-15 01:05:06 +01:00
|
|
|
ras_ptr(new rasterizer),
|
|
|
|
query_extent_(),
|
|
|
|
gamma_method_(GAMMA_POWER),
|
|
|
|
gamma_(1.0)
|
2013-02-21 04:52:54 +01:00
|
|
|
{
|
|
|
|
setup(m);
|
|
|
|
}
|
|
|
|
|
2011-10-12 02:05:35 +02:00
|
|
|
template <typename T>
|
2013-09-20 15:00:11 +02:00
|
|
|
agg_renderer<T>::agg_renderer(Map const& m, T & pixmap, std::shared_ptr<label_collision_detector4> detector,
|
2011-10-12 02:05:35 +02:00
|
|
|
double scale_factor, unsigned offset_x, unsigned offset_y)
|
|
|
|
: feature_style_processor<agg_renderer>(m, scale_factor),
|
|
|
|
pixmap_(pixmap),
|
2012-04-18 10:37:02 +02:00
|
|
|
internal_buffer_(),
|
2012-04-03 14:10:30 +02:00
|
|
|
current_buffer_(&pixmap),
|
2013-10-04 01:37:09 +02:00
|
|
|
t_(m.width(),m.height(),m.get_current_extent(),offset_x,offset_y),
|
2012-04-23 16:41:18 +02:00
|
|
|
style_level_compositing_(false),
|
2011-10-12 02:05:35 +02:00
|
|
|
width_(pixmap_.width()),
|
|
|
|
height_(pixmap_.height()),
|
|
|
|
scale_factor_(scale_factor),
|
|
|
|
font_engine_(),
|
|
|
|
font_manager_(font_engine_),
|
|
|
|
detector_(detector),
|
2013-03-15 01:05:06 +01:00
|
|
|
ras_ptr(new rasterizer),
|
|
|
|
query_extent_(),
|
|
|
|
gamma_method_(GAMMA_POWER),
|
|
|
|
gamma_(1.0)
|
2011-10-12 02:05:35 +02:00
|
|
|
{
|
2012-02-02 02:48:56 +01:00
|
|
|
setup(m);
|
2011-10-12 02:05:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
2012-02-02 02:48:56 +01:00
|
|
|
void agg_renderer<T>::setup(Map const &m)
|
2009-12-16 21:02:06 +01:00
|
|
|
{
|
2010-07-19 13:10:03 +02:00
|
|
|
boost::optional<color> const& bg = m.background();
|
2012-10-04 23:24:29 +02:00
|
|
|
if (bg)
|
|
|
|
{
|
|
|
|
if (bg->alpha() < 255)
|
|
|
|
{
|
|
|
|
mapnik::color bg_color = *bg;
|
|
|
|
bg_color.premultiply();
|
|
|
|
pixmap_.set_background(bg_color);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pixmap_.set_background(*bg);
|
|
|
|
}
|
|
|
|
}
|
2012-02-02 02:48:56 +01:00
|
|
|
|
2010-07-19 13:10:03 +02:00
|
|
|
boost::optional<std::string> const& image_filename = m.background_image();
|
|
|
|
if (image_filename)
|
|
|
|
{
|
2012-10-04 23:24:29 +02:00
|
|
|
// NOTE: marker_cache returns premultiplied image, if needed
|
2012-09-07 17:23:03 +02:00
|
|
|
boost::optional<mapnik::marker_ptr> bg_marker = mapnik::marker_cache::instance().find(*image_filename,true);
|
2011-01-26 02:18:40 +01:00
|
|
|
if (bg_marker && (*bg_marker)->is_bitmap())
|
2010-07-19 13:10:03 +02:00
|
|
|
{
|
2011-01-26 02:18:40 +01:00
|
|
|
mapnik::image_ptr bg_image = *(*bg_marker)->get_bitmap_data();
|
|
|
|
int w = bg_image->width();
|
|
|
|
int h = bg_image->height();
|
2010-07-19 13:10:03 +02:00
|
|
|
if ( w > 0 && h > 0)
|
|
|
|
{
|
2010-07-19 13:10:20 +02:00
|
|
|
// repeat background-image both vertically and horizontally
|
2013-07-25 07:00:38 +02:00
|
|
|
unsigned x_steps = static_cast<unsigned>(std::ceil(width_/double(w)));
|
|
|
|
unsigned y_steps = static_cast<unsigned>(std::ceil(height_/double(h)));
|
2010-07-19 13:10:03 +02:00
|
|
|
for (unsigned x=0;x<x_steps;++x)
|
|
|
|
{
|
|
|
|
for (unsigned y=0;y<y_steps;++y)
|
|
|
|
{
|
2013-07-25 07:00:38 +02:00
|
|
|
composite(pixmap_.data(),*bg_image, m.background_image_comp_op(), m.background_image_opacity(), x*w, y*h, false);
|
2010-07-19 13:10:03 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-04-09 21:41:56 +02:00
|
|
|
MAPNIK_LOG_DEBUG(agg_renderer) << "agg_renderer: Scale=" << m.scale();
|
2009-12-16 21:02:06 +01:00
|
|
|
}
|
Patch from David Eastcott :
1. Modified Text Symbolizer
a) corrected line fragment centering (for 2nd and subsequent lines, when line breaks occur).
b) adjusted vertical alignment calculation so that:
i) middle -> has the center of the text line(s) at the point origin
ii) bottom -> has the text line(s) below the point origin
iii) top -> has the text line(s) above the point origin
c) added new text_symbolizer attribute: 'wrap_before', value range: true/false, default == false
allows line breaks at first wrap_char before wrap_width as an alternative to the original
which was to create the line break at the first wrap_char after wrap_width
d) added new text_symbolizer attribute: 'horizontal_alignment', value range: left/middle/right, default == middle
i) left -> has all text line(s) to left of the point origin
ii) middle -> has all text line(s) centered on the the point origin
iii) right -> has all text line(s) to the right of the point origin
NOTE: dx, dy position adjustments are applied after alignments and before Justify.
e) added new text_symbolizer attribute: 'justify_alignment', value range: left/middle/right, default == middle
i) left -> after alignments, has all text line(s) are left justified (left to right reading)
ii) middle -> after alignments, has all text line(s) center justified
iii) right -> after alignments, has all text line(s) right justified (right to left reading)
f) added new text_symbolizer attribute: 'opacity', value range: 0.0 thru 1.0; 1.0 == fully opaque
g) modified positioning to compensate for both line_spacing and character_spacing, to ensure proper
centering of the text envelope. Also ensure that centering occurs correctly even if no wrapping
occurs. Line spacing is uniform and consistent and compensates for errors between text_size and
the actual size (ci.height is inconsistent, depending on case and character); fixes issue with
multi-line text where some lines have a slight gap and others are compressed together.
2. Modified shield_symbolizer
a) added the attributes:
i) allow_overlap
ii) vertical_alignment
iii) horizontal_alignment
iv) justify_alignment
v) wrap_width
vi) wrap_character
vii) wrap_before
viii) text_convert
ix) line_spacing
x) character_spacing
xi) opacity
b) added new shield_symbolizer attribute: 'unlock_image', value range: true/false, default == false
i) false == image and text placement behaviour same as before
ii) true == image placement independant of text, image is always centered at geometry point, text placed per attributes,
dx/dy only affect text.
Allows user to create point markers with text, but both the text and image rendering collision detection are done
as a pair (they come and go together - solves problem if using point_symbolizer and text_symbolizers where one or the
other are omitted due to overlaps, but not both)
c) extended choices for the attribute 'placement' to include vertex; effect is limited to the shield_symbolizer
Allows an attempted placement at every vertex available, gives additional shield placement volume when using line geometry
d) ensured that the text placement was not updating the detector unless a shield image was actually placed.
e) added new shield_symbolizer attribute: 'no_text', value range: true/false, default = false
When set true, the text for the feature is ignored ('space' subsituted) so that pure graphic symbols can be used
and no text is rendered over top of them.
2009-10-19 15:52:53 +02:00
|
|
|
|
2009-12-16 21:02:06 +01:00
|
|
|
template <typename T>
|
|
|
|
agg_renderer<T>::~agg_renderer() {}
|
2008-02-05 14:58:47 +01:00
|
|
|
|
2009-12-16 21:02:06 +01:00
|
|
|
template <typename T>
|
|
|
|
void agg_renderer<T>::start_map_processing(Map const& map)
|
|
|
|
{
|
2012-04-09 21:41:56 +02:00
|
|
|
MAPNIK_LOG_DEBUG(agg_renderer) << "agg_renderer: Start map processing bbox=" << map.get_current_extent();
|
2012-02-11 00:14:15 +01:00
|
|
|
ras_ptr->clip_box(0,0,width_,height_);
|
2009-12-16 21:02:06 +01:00
|
|
|
}
|
2006-02-21 20:55:24 +01:00
|
|
|
|
2009-12-16 21:02:06 +01:00
|
|
|
template <typename T>
|
|
|
|
void agg_renderer<T>::end_map_processing(Map const& )
|
|
|
|
{
|
2012-05-01 13:35:49 +02:00
|
|
|
|
2012-06-21 21:38:06 +02:00
|
|
|
agg::rendering_buffer buf(pixmap_.raw_data(),width_,height_, width_ * 4);
|
2013-07-24 00:41:59 +02:00
|
|
|
agg::pixfmt_rgba32_pre pixf(buf);
|
2012-04-03 14:10:30 +02:00
|
|
|
pixf.demultiply();
|
2012-04-09 21:41:56 +02:00
|
|
|
MAPNIK_LOG_DEBUG(agg_renderer) << "agg_renderer: End map processing";
|
2009-12-16 21:02:06 +01:00
|
|
|
}
|
Patch from David Eastcott :
1. Modified Text Symbolizer
a) corrected line fragment centering (for 2nd and subsequent lines, when line breaks occur).
b) adjusted vertical alignment calculation so that:
i) middle -> has the center of the text line(s) at the point origin
ii) bottom -> has the text line(s) below the point origin
iii) top -> has the text line(s) above the point origin
c) added new text_symbolizer attribute: 'wrap_before', value range: true/false, default == false
allows line breaks at first wrap_char before wrap_width as an alternative to the original
which was to create the line break at the first wrap_char after wrap_width
d) added new text_symbolizer attribute: 'horizontal_alignment', value range: left/middle/right, default == middle
i) left -> has all text line(s) to left of the point origin
ii) middle -> has all text line(s) centered on the the point origin
iii) right -> has all text line(s) to the right of the point origin
NOTE: dx, dy position adjustments are applied after alignments and before Justify.
e) added new text_symbolizer attribute: 'justify_alignment', value range: left/middle/right, default == middle
i) left -> after alignments, has all text line(s) are left justified (left to right reading)
ii) middle -> after alignments, has all text line(s) center justified
iii) right -> after alignments, has all text line(s) right justified (right to left reading)
f) added new text_symbolizer attribute: 'opacity', value range: 0.0 thru 1.0; 1.0 == fully opaque
g) modified positioning to compensate for both line_spacing and character_spacing, to ensure proper
centering of the text envelope. Also ensure that centering occurs correctly even if no wrapping
occurs. Line spacing is uniform and consistent and compensates for errors between text_size and
the actual size (ci.height is inconsistent, depending on case and character); fixes issue with
multi-line text where some lines have a slight gap and others are compressed together.
2. Modified shield_symbolizer
a) added the attributes:
i) allow_overlap
ii) vertical_alignment
iii) horizontal_alignment
iv) justify_alignment
v) wrap_width
vi) wrap_character
vii) wrap_before
viii) text_convert
ix) line_spacing
x) character_spacing
xi) opacity
b) added new shield_symbolizer attribute: 'unlock_image', value range: true/false, default == false
i) false == image and text placement behaviour same as before
ii) true == image placement independant of text, image is always centered at geometry point, text placed per attributes,
dx/dy only affect text.
Allows user to create point markers with text, but both the text and image rendering collision detection are done
as a pair (they come and go together - solves problem if using point_symbolizer and text_symbolizers where one or the
other are omitted due to overlaps, but not both)
c) extended choices for the attribute 'placement' to include vertex; effect is limited to the shield_symbolizer
Allows an attempted placement at every vertex available, gives additional shield placement volume when using line geometry
d) ensured that the text placement was not updating the detector unless a shield image was actually placed.
e) added new shield_symbolizer attribute: 'no_text', value range: true/false, default = false
When set true, the text for the feature is ignored ('space' subsituted) so that pure graphic symbols can be used
and no text is rendered over top of them.
2009-10-19 15:52:53 +02:00
|
|
|
|
2009-12-16 21:02:06 +01:00
|
|
|
template <typename T>
|
2012-03-06 14:07:04 +01:00
|
|
|
void agg_renderer<T>::start_layer_processing(layer const& lay, box2d<double> const& query_extent)
|
2009-12-16 21:02:06 +01:00
|
|
|
{
|
2012-04-09 21:41:56 +02:00
|
|
|
MAPNIK_LOG_DEBUG(agg_renderer) << "agg_renderer: Start processing layer=" << lay.name();
|
|
|
|
MAPNIK_LOG_DEBUG(agg_renderer) << "agg_renderer: -- datasource=" << lay.datasource().get();
|
|
|
|
MAPNIK_LOG_DEBUG(agg_renderer) << "agg_renderer: -- query_extent=" << query_extent;
|
|
|
|
|
2009-12-16 21:02:06 +01:00
|
|
|
if (lay.clear_label_cache())
|
|
|
|
{
|
2011-10-12 02:05:35 +02:00
|
|
|
detector_->clear();
|
2009-12-16 21:02:06 +01:00
|
|
|
}
|
2012-08-01 16:44:36 +02:00
|
|
|
|
2012-03-06 14:07:04 +01:00
|
|
|
query_extent_ = query_extent;
|
2012-08-01 16:44:36 +02:00
|
|
|
boost::optional<box2d<double> > const& maximum_extent = lay.maximum_extent();
|
|
|
|
if (maximum_extent)
|
|
|
|
{
|
|
|
|
query_extent_.clip(*maximum_extent);
|
|
|
|
}
|
2009-12-16 21:02:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
void agg_renderer<T>::end_layer_processing(layer const&)
|
|
|
|
{
|
2012-04-09 21:41:56 +02:00
|
|
|
MAPNIK_LOG_DEBUG(agg_renderer) << "agg_renderer: End layer processing";
|
2009-12-16 21:02:06 +01:00
|
|
|
}
|
|
|
|
|
2012-04-03 14:10:30 +02:00
|
|
|
template <typename T>
|
|
|
|
void agg_renderer<T>::start_style_processing(feature_type_style const& st)
|
|
|
|
{
|
2012-05-01 13:35:49 +02:00
|
|
|
MAPNIK_LOG_DEBUG(agg_renderer) << "agg_renderer: Start processing style";
|
2012-06-19 21:10:28 +02:00
|
|
|
if (st.comp_op() || st.image_filters().size() > 0 || st.get_opacity() < 1)
|
|
|
|
{
|
|
|
|
style_level_compositing_ = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
style_level_compositing_ = false;
|
|
|
|
}
|
2012-06-29 13:31:41 +02:00
|
|
|
|
2012-06-19 21:10:28 +02:00
|
|
|
if (style_level_compositing_)
|
2012-04-03 14:10:30 +02:00
|
|
|
{
|
2014-02-27 19:11:17 +01:00
|
|
|
if (st.image_filters_inflate())
|
2012-06-19 21:10:28 +02:00
|
|
|
{
|
2014-02-27 19:11:17 +01:00
|
|
|
int radius = 0;
|
|
|
|
mapnik::filter::filter_radius_visitor visitor(radius);
|
|
|
|
BOOST_FOREACH(mapnik::filter::filter_type const& filter_tag, st.image_filters())
|
|
|
|
{
|
|
|
|
boost::apply_visitor(visitor, filter_tag);
|
|
|
|
}
|
|
|
|
if (radius > t_.offset())
|
|
|
|
{
|
|
|
|
t_.set_offset(radius);
|
|
|
|
}
|
|
|
|
int offset = t_.offset();
|
|
|
|
unsigned target_width = width_;
|
|
|
|
unsigned target_height = height_;
|
|
|
|
target_width = width_ + (offset * 2);
|
|
|
|
target_height = height_ + (offset * 2);
|
|
|
|
ras_ptr->clip_box(-int(offset*2),-int(offset*2),target_width,target_height);
|
|
|
|
if (!internal_buffer_ ||
|
|
|
|
(internal_buffer_->width() < target_width ||
|
|
|
|
internal_buffer_->height() < target_height))
|
|
|
|
{
|
|
|
|
internal_buffer_ = boost::make_shared<buffer_type>(target_width,target_height);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
internal_buffer_->set_background(color(0,0,0,0)); // fill with transparent colour
|
|
|
|
}
|
2012-06-19 21:10:28 +02:00
|
|
|
}
|
2012-04-18 10:37:02 +02:00
|
|
|
else
|
2012-06-19 21:10:28 +02:00
|
|
|
{
|
2014-02-27 19:11:17 +01:00
|
|
|
if (!internal_buffer_)
|
|
|
|
{
|
|
|
|
internal_buffer_ = boost::make_shared<buffer_type>(width_,height_);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
internal_buffer_->set_background(color(0,0,0,0)); // fill with transparent colour
|
|
|
|
}
|
|
|
|
t_.set_offset(0);
|
|
|
|
ras_ptr->clip_box(0,0,width_,height_);
|
2012-06-19 21:10:28 +02:00
|
|
|
}
|
2012-04-18 10:37:02 +02:00
|
|
|
current_buffer_ = internal_buffer_.get();
|
2012-04-03 14:10:30 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2013-10-04 01:37:09 +02:00
|
|
|
t_.set_offset(0);
|
|
|
|
ras_ptr->clip_box(0,0,width_,height_);
|
2012-04-03 14:10:30 +02:00
|
|
|
current_buffer_ = &pixmap_;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
void agg_renderer<T>::end_style_processing(feature_type_style const& st)
|
|
|
|
{
|
2012-06-19 21:10:28 +02:00
|
|
|
if (style_level_compositing_)
|
2012-04-03 14:10:30 +02:00
|
|
|
{
|
2012-06-19 21:10:28 +02:00
|
|
|
bool blend_from = false;
|
|
|
|
if (st.image_filters().size() > 0)
|
|
|
|
{
|
|
|
|
blend_from = true;
|
|
|
|
mapnik::filter::filter_visitor<image_32> visitor(*current_buffer_);
|
2013-04-24 17:40:35 +02:00
|
|
|
for (mapnik::filter::filter_type const& filter_tag : st.image_filters())
|
2012-06-19 21:10:28 +02:00
|
|
|
{
|
|
|
|
boost::apply_visitor(visitor, filter_tag);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (st.comp_op())
|
|
|
|
{
|
2013-10-04 01:37:09 +02:00
|
|
|
composite(pixmap_.data(), current_buffer_->data(),
|
|
|
|
*st.comp_op(), st.get_opacity(),
|
|
|
|
-t_.offset(),
|
|
|
|
-t_.offset(), false);
|
2012-06-19 21:10:28 +02:00
|
|
|
}
|
|
|
|
else if (blend_from || st.get_opacity() < 1)
|
|
|
|
{
|
2013-10-04 01:37:09 +02:00
|
|
|
composite(pixmap_.data(), current_buffer_->data(),
|
|
|
|
src_over, st.get_opacity(),
|
|
|
|
-t_.offset(),
|
|
|
|
-t_.offset(), false);
|
2012-06-19 21:10:28 +02:00
|
|
|
}
|
2013-02-28 21:22:27 +01:00
|
|
|
}
|
|
|
|
// apply any 'direct' image filters
|
|
|
|
mapnik::filter::filter_visitor<image_32> visitor(pixmap_);
|
2013-04-24 17:40:35 +02:00
|
|
|
for (mapnik::filter::filter_type const& filter_tag : st.direct_image_filters())
|
2013-02-28 21:22:27 +01:00
|
|
|
{
|
|
|
|
boost::apply_visitor(visitor, filter_tag);
|
2012-04-20 15:53:11 +02:00
|
|
|
}
|
2012-05-01 13:35:49 +02:00
|
|
|
MAPNIK_LOG_DEBUG(agg_renderer) << "agg_renderer: End processing style";
|
2012-04-03 14:10:30 +02:00
|
|
|
}
|
|
|
|
|
2011-01-26 10:19:00 +01:00
|
|
|
template <typename T>
|
2012-12-03 07:46:58 +01:00
|
|
|
void agg_renderer<T>::render_marker(pixel_position const& pos,
|
|
|
|
marker const& marker,
|
|
|
|
agg::trans_affine const& tr,
|
|
|
|
double opacity,
|
|
|
|
composite_mode_e comp_op)
|
2011-01-26 10:19:00 +01:00
|
|
|
{
|
2012-07-17 18:10:24 +02:00
|
|
|
typedef agg::rgba8 color_type;
|
|
|
|
typedef agg::order_rgba order_type;
|
2014-01-23 07:43:34 +01:00
|
|
|
typedef agg::comp_op_adaptor_rgba_pre<color_type, order_type> blender_type; // comp blender
|
2012-07-17 18:10:24 +02:00
|
|
|
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;
|
2012-10-03 22:49:06 +02:00
|
|
|
typedef agg::pod_bvector<mapnik::svg::path_attributes> svg_attribute_type;
|
2012-07-17 18:10:24 +02:00
|
|
|
|
|
|
|
ras_ptr->reset();
|
2013-03-15 01:05:06 +01:00
|
|
|
if (gamma_method_ != GAMMA_POWER || gamma_ != 1.0)
|
|
|
|
{
|
|
|
|
ras_ptr->gamma(agg::gamma_power());
|
|
|
|
gamma_method_ = GAMMA_POWER;
|
|
|
|
gamma_ = 1.0;
|
|
|
|
}
|
2012-07-17 18:10:24 +02:00
|
|
|
agg::scanline_u8 sl;
|
2013-10-24 03:09:22 +02:00
|
|
|
agg::rendering_buffer buf(current_buffer_->raw_data(),
|
|
|
|
current_buffer_->width(),
|
|
|
|
current_buffer_->height(),
|
|
|
|
current_buffer_->width() * 4);
|
2012-07-17 18:10:24 +02:00
|
|
|
pixfmt_comp_type pixf(buf);
|
|
|
|
pixf.comp_op(static_cast<agg::comp_op_e>(comp_op));
|
|
|
|
renderer_base renb(pixf);
|
|
|
|
|
2011-01-26 10:19:00 +01:00
|
|
|
if (marker.is_vector())
|
|
|
|
{
|
|
|
|
box2d<double> const& bbox = (*marker.get_vector_data())->bounding_box();
|
2011-01-31 10:56:31 +01:00
|
|
|
coord<double,2> c = bbox.center();
|
|
|
|
// center the svg marker on '0,0'
|
|
|
|
agg::trans_affine mtx = agg::trans_affine_translation(-c.x,-c.y);
|
|
|
|
// apply symbol transformation to get to map space
|
|
|
|
mtx *= tr;
|
|
|
|
mtx *= agg::trans_affine_scaling(scale_factor_);
|
|
|
|
// render the marker at the center of the marker box
|
2012-05-27 23:50:09 +02:00
|
|
|
mtx.translate(pos.x, pos.y);
|
2012-03-08 17:37:58 +01:00
|
|
|
using namespace mapnik::svg;
|
2011-01-26 10:19:00 +01:00
|
|
|
vertex_stl_adapter<svg_path_storage> stl_storage((*marker.get_vector_data())->source());
|
|
|
|
svg_path_adapter svg_path(stl_storage);
|
2012-08-28 02:58:49 +02:00
|
|
|
svg_renderer_agg<svg_path_adapter,
|
2012-10-03 22:49:06 +02:00
|
|
|
svg_attribute_type,
|
2012-05-15 17:13:08 +02:00
|
|
|
renderer_type,
|
2012-10-03 22:49:06 +02:00
|
|
|
pixfmt_comp_type> svg_renderer(svg_path,
|
2012-02-02 02:48:56 +01:00
|
|
|
(*marker.get_vector_data())->attributes());
|
2012-06-29 13:31:41 +02:00
|
|
|
|
2011-01-26 10:19:00 +01:00
|
|
|
svg_renderer.render(*ras_ptr, sl, renb, mtx, opacity, bbox);
|
|
|
|
}
|
|
|
|
else
|
2012-06-29 13:31:41 +02:00
|
|
|
{
|
2012-07-17 18:10:24 +02:00
|
|
|
double width = (*marker.get_bitmap_data())->width();
|
|
|
|
double height = (*marker.get_bitmap_data())->height();
|
|
|
|
if (std::fabs(1.0 - scale_factor_) < 0.001 && tr.is_identity())
|
2012-06-29 13:31:41 +02:00
|
|
|
{
|
2013-03-22 12:54:34 +01:00
|
|
|
double cx = 0.5 * width;
|
|
|
|
double cy = 0.5 * height;
|
2012-06-29 13:31:41 +02:00
|
|
|
composite(current_buffer_->data(), **marker.get_bitmap_data(),
|
|
|
|
comp_op, opacity,
|
|
|
|
boost::math::iround(pos.x - cx),
|
|
|
|
boost::math::iround(pos.y - cy),
|
|
|
|
false);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2012-07-17 18:10:24 +02:00
|
|
|
|
|
|
|
double p[8];
|
|
|
|
double x0 = pos.x - 0.5 * width;
|
|
|
|
double y0 = pos.y - 0.5 * height;
|
|
|
|
p[0] = x0; p[1] = y0;
|
|
|
|
p[2] = x0 + width; p[3] = y0;
|
|
|
|
p[4] = x0 + width; p[5] = y0 + height;
|
|
|
|
p[6] = x0; p[7] = y0 + height;
|
|
|
|
|
|
|
|
agg::trans_affine marker_tr;
|
|
|
|
|
|
|
|
marker_tr *= agg::trans_affine_translation(-pos.x,-pos.y);
|
|
|
|
marker_tr *= tr;
|
|
|
|
marker_tr *= agg::trans_affine_scaling(scale_factor_);
|
|
|
|
marker_tr *= agg::trans_affine_translation(pos.x,pos.y);
|
|
|
|
|
|
|
|
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_ptr->move_to_d(p[0],p[1]);
|
|
|
|
ras_ptr->line_to_d(p[2],p[3]);
|
|
|
|
ras_ptr->line_to_d(p[4],p[5]);
|
|
|
|
ras_ptr->line_to_d(p[6],p[7]);
|
|
|
|
|
|
|
|
|
|
|
|
agg::span_allocator<color_type> sa;
|
|
|
|
agg::image_filter_bilinear filter_kernel;
|
|
|
|
agg::image_filter_lut filter(filter_kernel, false);
|
|
|
|
|
|
|
|
image_data_32 const& src = **marker.get_bitmap_data();
|
|
|
|
agg::rendering_buffer marker_buf((unsigned char *)src.getBytes(),
|
|
|
|
src.width(),
|
|
|
|
src.height(),
|
|
|
|
src.width()*4);
|
2013-01-04 20:06:03 +01:00
|
|
|
agg::pixfmt_rgba32_pre marker_pixf(marker_buf);
|
2012-07-17 18:10:24 +02:00
|
|
|
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;
|
2012-08-02 19:52:09 +02:00
|
|
|
typedef agg::renderer_scanline_aa_alpha<renderer_base,
|
|
|
|
agg::span_allocator<agg::rgba8>,
|
|
|
|
span_gen_type> renderer_type;
|
2013-01-04 20:06:03 +01:00
|
|
|
img_accessor_type ia(marker_pixf);
|
2012-07-17 18:10:24 +02:00
|
|
|
interpolator_type interpolator(agg::trans_affine(p, 0, 0, width, height) );
|
|
|
|
span_gen_type sg(ia, interpolator, filter);
|
2012-08-02 19:52:09 +02:00
|
|
|
renderer_type rp(renb,sa, sg, unsigned(opacity*255));
|
|
|
|
agg::render_scanlines(*ras_ptr, sl, rp);
|
2012-06-29 13:31:41 +02:00
|
|
|
}
|
2011-01-26 10:19:00 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-09 13:39:23 +02:00
|
|
|
template <typename T>
|
|
|
|
void agg_renderer<T>::painted(bool painted)
|
|
|
|
{
|
|
|
|
pixmap_.painted(painted);
|
|
|
|
}
|
|
|
|
|
2012-05-27 23:50:09 +02:00
|
|
|
template <typename T>
|
|
|
|
void agg_renderer<T>::debug_draw_box(box2d<double> const& box,
|
|
|
|
double x, double y, double angle)
|
|
|
|
{
|
2013-10-24 03:09:22 +02:00
|
|
|
agg::rendering_buffer buf(current_buffer_->raw_data(),
|
|
|
|
current_buffer_->width(),
|
|
|
|
current_buffer_->height(),
|
|
|
|
current_buffer_->width() * 4);
|
2012-05-27 23:50:09 +02:00
|
|
|
debug_draw_box(buf, box, x, y, angle);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T> template <typename R>
|
|
|
|
void agg_renderer<T>::debug_draw_box(R& buf, box2d<double> const& box,
|
|
|
|
double x, double y, double angle)
|
|
|
|
{
|
2013-07-24 00:41:59 +02:00
|
|
|
typedef agg::pixfmt_rgba32_pre pixfmt;
|
2012-05-27 23:50:09 +02:00
|
|
|
typedef agg::renderer_base<pixfmt> renderer_base;
|
|
|
|
typedef agg::renderer_scanline_aa_solid<renderer_base> renderer_type;
|
|
|
|
|
|
|
|
agg::scanline_p8 sl_line;
|
|
|
|
pixfmt pixf(buf);
|
|
|
|
renderer_base renb(pixf);
|
|
|
|
renderer_type ren(renb);
|
|
|
|
|
|
|
|
// compute tranformation matrix
|
2012-06-26 16:00:42 +02:00
|
|
|
agg::trans_affine tr = agg::trans_affine_rotation(angle).translate(x, y);
|
2012-05-27 23:50:09 +02:00
|
|
|
// prepare path
|
|
|
|
agg::path_storage pbox;
|
|
|
|
pbox.start_new_path();
|
|
|
|
pbox.move_to(box.minx(), box.miny());
|
|
|
|
pbox.line_to(box.maxx(), box.miny());
|
|
|
|
pbox.line_to(box.maxx(), box.maxy());
|
|
|
|
pbox.line_to(box.minx(), box.maxy());
|
|
|
|
pbox.line_to(box.minx(), box.miny());
|
|
|
|
|
|
|
|
// prepare stroke with applied transformation
|
|
|
|
typedef agg::conv_transform<agg::path_storage> conv_transform;
|
|
|
|
typedef agg::conv_stroke<conv_transform> conv_stroke;
|
|
|
|
conv_transform tbox(pbox, tr);
|
|
|
|
conv_stroke sbox(tbox);
|
|
|
|
sbox.generator().width(1.0 * scale_factor_);
|
|
|
|
|
|
|
|
// render the outline
|
|
|
|
ras_ptr->reset();
|
|
|
|
ras_ptr->add_path(sbox);
|
2013-09-20 04:41:46 +02:00
|
|
|
ren.color(agg::rgba8_pre(0x33, 0x33, 0xff, 0xcc)); // blue is fine
|
2012-05-27 23:50:09 +02:00
|
|
|
agg::render_scanlines(*ras_ptr, sl_line, ren);
|
|
|
|
}
|
|
|
|
|
2012-08-02 01:40:48 +02:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-12-16 21:02:06 +01:00
|
|
|
template class agg_renderer<image_32>;
|
2012-05-27 23:50:09 +02:00
|
|
|
template void agg_renderer<image_32>::debug_draw_box<agg::rendering_buffer>(
|
|
|
|
agg::rendering_buffer& buf,
|
|
|
|
box2d<double> const& box,
|
|
|
|
double x, double y, double angle);
|
2006-02-07 15:41:41 +01:00
|
|
|
}
|