2006-10-03 10:39:43 +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-10-03 10:39:43 +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-10-03 10:39:43 +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 .
*
* This library is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* Lesser General Public License for more details .
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin St , Fifth Floor , Boston , MA 02110 - 1301 USA
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2012-04-08 02:20:56 +02:00
2006-10-04 13:22:18 +02:00
// mapnik
2012-04-08 02:20:56 +02:00
# include <mapnik/debug.hpp>
2007-10-08 19:42:41 +02:00
# include <mapnik/load_map.hpp>
2012-03-06 15:18:11 +01:00
# include <mapnik/xml_tree.hpp>
2009-12-05 04:58:48 +01:00
# include <mapnik/version.hpp>
2012-03-21 16:47:33 +01:00
# include <mapnik/image_compositing.hpp>
2012-07-07 01:45:58 +02:00
# include <mapnik/image_scaling.hpp>
2006-10-04 13:22:18 +02:00
# include <mapnik/color.hpp>
# include <mapnik/color_factory.hpp>
2011-08-29 23:07:45 +02:00
# include <mapnik/symbolizer.hpp>
2013-11-28 07:50:15 +01:00
# include <mapnik/gamma_method.hpp>
2011-08-29 23:07:45 +02:00
# include <mapnik/feature_type_style.hpp>
2006-10-04 13:22:18 +02:00
# include <mapnik/layer.hpp>
# include <mapnik/datasource_cache.hpp>
2007-09-25 20:47:12 +02:00
# include <mapnik/font_engine_freetype.hpp>
2008-06-29 12:58:48 +02:00
# include <mapnik/font_set.hpp>
2012-03-13 09:02:53 +01:00
# include <mapnik/xml_loader.hpp>
2012-02-12 12:46:07 +01:00
# include <mapnik/expression.hpp>
2010-06-18 17:39:32 +02:00
# include <mapnik/parse_path.hpp>
2012-05-27 23:50:09 +02:00
# include <mapnik/parse_transform.hpp>
2010-03-12 15:49:34 +01:00
# include <mapnik/raster_colorizer.hpp>
2010-06-01 17:27:19 +02:00
# include <mapnik/svg/svg_path_parser.hpp>
2013-11-08 05:09:22 +01:00
# include <mapnik/text/placements/registry.hpp>
# include <mapnik/text/placements/dummy.hpp>
2012-01-22 03:39:59 +01:00
# include <mapnik/rule.hpp>
2012-03-12 01:09:26 +01:00
# include <mapnik/config_error.hpp>
2012-03-23 12:56:23 +01:00
# include <mapnik/util/dasharray_parser.hpp>
2012-04-06 22:39:13 +02:00
# include <mapnik/util/conversions.hpp>
2012-12-07 23:06:13 +01:00
# include <mapnik/util/trim.hpp>
2012-07-11 07:38:53 +02:00
# include <mapnik/marker_cache.hpp>
2012-12-17 03:19:52 +01:00
# include <mapnik/noncopyable.hpp>
2013-06-03 04:28:24 +02:00
# include <mapnik/util/fs.hpp>
2013-06-18 23:26:58 +02:00
# include <mapnik/image_filter_types.hpp>
2013-11-09 04:13:51 +01:00
# include <mapnik/projection.hpp>
2013-12-20 00:11:35 +01:00
# include <mapnik/group/group_rule.hpp>
2011-02-28 14:17:46 +01:00
2013-11-28 07:50:15 +01:00
2007-10-08 19:42:41 +02:00
// boost
# include <boost/optional.hpp>
# include <boost/algorithm/string.hpp>
# include <boost/tokenizer.hpp>
# include <boost/property_tree/ptree.hpp>
# include <boost/property_tree/xml_parser.hpp>
# include <boost/static_assert.hpp>
2009-05-01 03:59:01 +02:00
2010-06-01 17:27:19 +02:00
// agg
# include "agg_trans_affine.h"
2006-10-04 13:22:18 +02:00
using boost : : tokenizer ;
2007-09-25 20:47:12 +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
namespace mapnik
2006-10-03 10:39:43 +02:00
{
2010-04-09 20:47:19 +02:00
using boost : : optional ;
2013-12-09 22:15:24 +01:00
constexpr unsigned name2int ( const char * str , int off = 0 )
{
return ! str [ off ] ? 5381 : ( name2int ( str , off + 1 ) * 33 ) ^ str [ off ] ;
}
class map_parser : mapnik : : noncopyable
{
2010-04-09 20:47:19 +02:00
public :
2012-03-16 01:50:55 +01:00
map_parser ( bool strict , std : : string const & filename = " " ) :
strict_ ( strict ) ,
filename_ ( filename ) ,
2010-06-02 13:03:30 +02:00
relative_to_xml_ ( true ) ,
2013-06-03 01:06:32 +02:00
font_manager_ ( font_engine_ ) ,
xml_base_path_ ( )
2012-03-13 15:56:11 +01:00
{ }
2010-04-09 20:47:19 +02:00
2012-03-13 11:11:28 +01:00
void parse_map ( Map & map , xml_node const & sty , std : : string const & base_path ) ;
2010-04-09 20:47:19 +02:00
private :
2012-03-16 01:50:55 +01:00
void parse_map_include ( Map & map , xml_node const & include ) ;
2012-03-06 17:29:33 +01:00
void parse_style ( Map & map , xml_node const & sty ) ;
void parse_layer ( Map & map , xml_node const & lay ) ;
2012-05-07 17:08:16 +02:00
void parse_symbolizer_base ( symbolizer_base & sym , xml_node const & pt ) ;
2010-05-27 12:20:10 +02:00
2012-03-06 17:29:33 +01:00
void parse_fontset ( Map & map , xml_node const & fset ) ;
2012-04-19 20:22:42 +02:00
bool parse_font ( font_set & fset , xml_node const & f ) ;
2010-05-27 12:20:10 +02:00
2012-03-07 01:35:37 +01:00
void parse_rule ( feature_type_style & style , xml_node const & r ) ;
2013-12-20 00:11:35 +01:00
void parse_symbolizers ( rule & rule , xml_node const & node ) ;
2012-03-07 01:35:37 +01:00
void parse_point_symbolizer ( rule & rule , xml_node const & sym ) ;
void parse_line_pattern_symbolizer ( rule & rule , xml_node const & sym ) ;
void parse_polygon_pattern_symbolizer ( rule & rule , xml_node const & sym ) ;
void parse_text_symbolizer ( rule & rule , xml_node const & sym ) ;
void parse_shield_symbolizer ( rule & rule , xml_node const & sym ) ;
void parse_line_symbolizer ( rule & rule , xml_node const & sym ) ;
void parse_polygon_symbolizer ( rule & rule , xml_node const & sym ) ;
2012-03-16 01:50:55 +01:00
void parse_building_symbolizer ( rule & rule , xml_node const & sym ) ;
void parse_raster_symbolizer ( rule & rule , xml_node const & sym ) ;
void parse_markers_symbolizer ( rule & rule , xml_node const & sym ) ;
2013-12-20 00:11:35 +01:00
void parse_group_symbolizer ( rule & rule , xml_node const & sym ) ;
2012-07-31 23:11:05 +02:00
void parse_debug_symbolizer ( rule & rule , xml_node const & sym ) ;
2013-12-20 00:11:35 +01:00
void parse_group_rule ( group_symbolizer_properties & prop , xml_node const & r ) ;
void parse_simple_layout ( group_symbolizer_properties & prop , xml_node const & node ) ;
void parse_pair_layout ( group_symbolizer_properties & prop , xml_node const & nd ) ;
2013-02-01 22:32:48 +01:00
bool parse_raster_colorizer ( raster_colorizer_ptr const & rc , xml_node const & node ) ;
2013-11-28 07:50:15 +01:00
void parse_stroke ( symbolizer_base & symbol , xml_node const & sym ) ;
2012-03-13 11:11:28 +01:00
void ensure_font_face ( std : : string const & face_name ) ;
2012-03-12 02:12:58 +01:00
void find_unused_nodes ( xml_node const & root ) ;
2012-12-07 05:15:27 +01:00
void find_unused_nodes_recursive ( xml_node const & node , std : : string & error_text ) ;
2013-03-13 00:56:31 +01:00
std : : string ensure_relative_to_xml ( boost : : optional < std : : string > const & opt_path ) ;
2012-08-23 23:31:50 +02:00
void ensure_exists ( std : : string const & file_path ) ;
2012-02-25 01:34:39 +01:00
boost : : optional < color > get_opt_color_attr ( boost : : property_tree : : ptree const & node ,
2012-03-13 15:56:11 +01:00
std : : string const & name ) ;
2010-05-27 12:20:10 +02:00
bool strict_ ;
std : : string filename_ ;
bool relative_to_xml_ ;
std : : map < std : : string , parameters > datasource_templates_ ;
freetype_engine font_engine_ ;
face_manager < freetype_engine > font_manager_ ;
std : : map < std : : string , std : : string > file_sources_ ;
std : : map < std : : string , font_set > fontsets_ ;
2013-06-03 01:06:32 +02:00
std : : string xml_base_path_ ;
2010-04-09 20:47:19 +02:00
} ;
2012-04-08 02:20:56 +02:00
//#include <mapnik/internal/dump_xml.hpp>
2013-06-03 01:24:21 +02:00
void load_map ( Map & map , std : : string const & filename , bool strict , std : : string base_path )
2010-04-09 20:47:19 +02:00
{
2012-03-11 23:24:28 +01:00
// TODO - use xml encoding?
xml_tree tree ( " utf8 " ) ;
2012-03-06 15:18:11 +01:00
tree . set_filename ( filename ) ;
2012-03-13 09:02:53 +01:00
read_xml ( filename , tree . root ( ) ) ;
2012-03-06 15:18:11 +01:00
map_parser parser ( strict , filename ) ;
2013-06-03 01:24:21 +02:00
parser . parse_map ( map , tree . root ( ) , base_path ) ;
2012-04-08 02:20:56 +02:00
//dump_xml(tree.root());
2010-04-09 20:47:19 +02:00
}
2009-01-16 00:51:07 +01:00
2012-03-05 19:44:20 +01:00
void load_map_string ( Map & map , std : : string const & str , bool strict , std : : string base_path )
2010-04-09 20:47:19 +02:00
{
2012-03-11 23:24:28 +01:00
// TODO - use xml encoding?
xml_tree tree ( " utf8 " ) ;
2011-05-26 01:48:07 +02:00
if ( ! base_path . empty ( ) )
2013-06-03 01:06:32 +02:00
{
2012-03-13 09:02:53 +01:00
read_xml_string ( str , tree . root ( ) , base_path ) ; // accept base_path passed into function
2013-06-03 01:06:32 +02:00
}
2011-05-26 01:48:07 +02:00
else
2013-06-03 01:06:32 +02:00
{
2013-06-03 01:24:21 +02:00
read_xml_string ( str , tree . root ( ) , map . base_path ( ) ) ; // FIXME - this value is not fully known yet
2013-06-03 01:06:32 +02:00
}
2012-03-06 15:18:11 +01:00
map_parser parser ( strict , base_path ) ;
2012-03-07 01:35:37 +01:00
parser . parse_map ( map , tree . root ( ) , base_path ) ;
2012-02-25 01:34:39 +01:00
}
2012-03-06 15:18:11 +01:00
void map_parser : : parse_map ( Map & map , xml_node const & pt , std : : string const & base_path )
2010-04-09 20:47:19 +02:00
{
2010-05-27 12:20:10 +02:00
try
{
2012-03-06 15:18:11 +01:00
xml_node const & map_node = pt . get_child ( " Map " ) ;
2010-06-02 13:03:30 +02:00
try
{
2011-01-25 21:47:56 +01:00
// Check if relative paths should be interpreted as relative to/from XML location
// Default is true, and map_parser::ensure_relative_to_xml will be called to modify path
2012-03-06 15:18:11 +01:00
optional < boolean > paths_from_xml = map_node . get_opt_attr < boolean > ( " paths-from-xml " ) ;
2011-01-25 21:47:56 +01:00
if ( paths_from_xml )
{
relative_to_xml_ = * paths_from_xml ;
}
2012-03-06 15:18:11 +01:00
optional < std : : string > base_path_from_xml = map_node . get_opt_attr < std : : string > ( " base " ) ;
2011-05-26 01:48:07 +02:00
if ( ! base_path . empty ( ) )
{
2012-03-06 15:18:11 +01:00
map . set_base_path ( base_path ) ;
2011-05-26 01:48:07 +02:00
}
else if ( base_path_from_xml )
{
2012-03-06 15:18:11 +01:00
map . set_base_path ( * base_path_from_xml ) ;
2011-05-26 01:48:07 +02:00
}
2013-06-03 01:06:32 +02:00
else if ( ! filename_ . empty ( ) )
2011-05-26 01:48:07 +02:00
{
2013-06-03 04:28:24 +02:00
map . set_base_path ( mapnik : : util : : dirname ( filename_ ) ) ;
2011-05-26 01:48:07 +02:00
}
2013-06-03 01:06:32 +02:00
xml_base_path_ = map . base_path ( ) ;
2011-05-26 01:48:07 +02:00
2012-03-06 15:18:11 +01:00
optional < color > bgcolor = map_node . get_opt_attr < color > ( " background-color " ) ;
2012-02-02 02:53:35 +01:00
if ( bgcolor )
2010-07-19 13:10:03 +02:00
{
2012-03-06 15:18:11 +01:00
map . set_background ( * bgcolor ) ;
2010-06-02 13:03:30 +02:00
}
2012-02-02 02:53:35 +01:00
2012-03-06 15:18:11 +01:00
optional < std : : string > image_filename = map_node . get_opt_attr < std : : string > ( " background-image " ) ;
2010-07-19 13:10:03 +02:00
if ( image_filename )
2012-02-02 02:53:35 +01:00
{
2011-01-25 21:47:56 +01:00
map . set_background_image ( ensure_relative_to_xml ( image_filename ) ) ;
2010-07-19 13:10:03 +02:00
}
2012-02-02 02:53:35 +01:00
2013-07-25 07:00:38 +02:00
optional < std : : string > comp_op_name = map_node . get_opt_attr < std : : string > ( " background-image-comp-op " ) ;
if ( comp_op_name )
{
optional < composite_mode_e > comp_op = comp_op_from_string ( * comp_op_name ) ;
if ( comp_op )
{
map . set_background_image_comp_op ( * comp_op ) ;
}
else
{
throw config_error ( " failed to parse background-image-comp-op: ' " + * comp_op_name + " ' " ) ;
}
}
2013-09-20 15:00:11 +02:00
2013-11-28 07:50:15 +01:00
optional < double > opacity = map_node . get_opt_attr < double > ( " background-image-opacity " ) ;
2013-07-25 07:00:38 +02:00
if ( opacity )
{
map . set_background_image_opacity ( * opacity ) ;
}
2013-05-30 00:16:44 +02:00
std : : string srs = map_node . get_attr ( " srs " , map . srs ( ) ) ;
try
{
// create throwaway projection object here to ensure it is valid
projection proj ( srs ) ;
}
2013-11-09 04:13:51 +01:00
catch ( std : : exception const & ex )
2013-05-30 00:16:44 +02:00
{
throw mapnik : : config_error ( ex . what ( ) ) ;
}
map . set_srs ( srs ) ;
2010-04-09 20:47:19 +02:00
2012-03-06 15:18:11 +01:00
optional < unsigned > buffer_size = map_node . get_opt_attr < unsigned > ( " buffer-size " ) ;
2010-06-02 13:03:30 +02:00
if ( buffer_size )
{
map . set_buffer_size ( * buffer_size ) ;
}
2010-04-09 20:47:19 +02:00
2012-03-06 15:18:11 +01:00
optional < std : : string > maximum_extent = map_node . get_opt_attr < std : : string > ( " maximum-extent " ) ;
2011-04-14 04:32:51 +02:00
if ( maximum_extent )
{
box2d < double > box ;
if ( box . from_string ( * maximum_extent ) )
{
map . set_maximum_extent ( box ) ;
}
else
{
2012-12-07 05:15:27 +01:00
std : : string s_err ( " failed to parse Map maximum-extent ' " ) ;
s_err + = * maximum_extent + " ' " ;
2012-03-06 15:18:11 +01:00
if ( strict_ )
2012-04-08 02:20:56 +02:00
{
2012-12-07 05:15:27 +01:00
throw config_error ( s_err ) ;
2012-04-08 02:20:56 +02:00
}
2011-04-14 04:32:51 +02:00
else
2012-04-08 02:20:56 +02:00
{
2012-12-07 05:15:27 +01:00
MAPNIK_LOG_ERROR ( load_map ) < < " map_parser: " < < s_err ;
2012-04-08 02:20:56 +02:00
}
2011-04-14 04:32:51 +02:00
}
}
2012-03-06 15:18:11 +01:00
optional < std : : string > font_directory = map_node . get_opt_attr < std : : string > ( " font-directory " ) ;
2010-10-07 03:49:07 +02:00
if ( font_directory )
{
2012-03-23 23:07:28 +01:00
if ( ! freetype_engine : : register_fonts ( ensure_relative_to_xml ( font_directory ) , false ) )
{
if ( strict_ )
{
2012-03-24 01:27:01 +01:00
throw config_error ( std : : string ( " Failed to load fonts from: " ) + * font_directory ) ;
2012-03-23 23:07:28 +01:00
}
}
2010-10-07 03:49:07 +02:00
}
2012-03-06 15:18:11 +01:00
optional < std : : string > min_version_string = map_node . get_opt_attr < std : : string > ( " minimum-version " ) ;
2012-02-02 02:53:35 +01:00
2010-06-02 13:03:30 +02:00
if ( min_version_string )
{
boost : : char_separator < char > sep ( " . " ) ;
2012-03-06 15:18:11 +01:00
boost : : tokenizer < boost : : char_separator < char > > tokens ( * min_version_string , sep ) ;
2010-06-02 13:03:30 +02:00
unsigned i = 0 ;
bool success = false ;
int n [ 3 ] ;
2012-03-06 15:18:11 +01:00
for ( boost : : tokenizer < boost : : char_separator < char > > : : iterator beg = tokens . begin ( ) ;
beg ! = tokens . end ( ) ; + + beg )
2010-06-02 13:03:30 +02:00
{
2012-12-07 23:06:13 +01:00
std : : string item = mapnik : : util : : trim_copy ( * beg ) ;
2012-04-06 22:39:13 +02:00
if ( ! mapnik : : util : : string2int ( item , n [ i ] ) )
2010-06-02 13:03:30 +02:00
{
2012-04-06 22:39:13 +02:00
throw config_error ( std : : string ( " Invalid version string encountered: ' " )
2013-11-28 07:50:15 +01:00
+ * beg + " ' in ' " + * min_version_string + " ' " ) ;
2010-06-02 13:03:30 +02:00
}
2012-02-02 02:53:35 +01:00
if ( i = = 2 )
2010-06-02 13:03:30 +02:00
{
success = true ;
break ;
}
+ + i ;
}
if ( success )
{
int min_version = ( n [ 0 ] * 100000 ) + ( n [ 1 ] * 100 ) + ( n [ 2 ] ) ;
if ( min_version > MAPNIK_VERSION )
{
throw config_error ( std : : string ( " This map uses features only present in Mapnik version " ) + * min_version_string + " and newer " ) ;
}
2012-02-02 02:53:35 +01:00
2010-06-02 13:03:30 +02:00
}
2012-02-02 02:53:35 +01:00
2010-06-02 13:03:30 +02:00
}
}
2013-03-23 01:58:33 +01:00
catch ( config_error const & ex )
2010-06-02 13:03:30 +02:00
{
2012-03-12 01:09:26 +01:00
ex . append_context ( map_node ) ;
2010-06-02 13:03:30 +02:00
throw ;
}
2012-02-02 02:53:35 +01:00
2012-03-06 15:47:08 +01:00
parse_map_include ( map , map_node ) ;
2010-08-10 08:01:16 +02:00
}
2012-03-06 15:18:11 +01:00
catch ( node_not_found const & )
2010-08-10 08:01:16 +02:00
{
throw config_error ( " Not a map file. Node 'Map' not found. " ) ;
}
2012-03-12 02:12:58 +01:00
find_unused_nodes ( pt ) ;
2010-08-10 08:01:16 +02:00
}
2012-02-02 02:53:35 +01:00
2012-03-06 15:47:08 +01:00
void map_parser : : parse_map_include ( Map & map , xml_node const & include )
2010-08-10 08:01:16 +02:00
{
2012-03-06 17:29:33 +01:00
try
{
2012-03-13 15:56:11 +01:00
xml_node : : const_iterator itr = include . begin ( ) ;
xml_node : : const_iterator end = include . end ( ) ;
2010-06-02 13:03:30 +02:00
2012-03-13 15:56:11 +01:00
for ( ; itr ! = end ; + + itr )
2011-01-04 16:22:49 +01:00
{
2012-03-13 15:56:11 +01:00
if ( itr - > is_text ( ) ) continue ;
if ( itr - > is ( " Include " ) )
{
parse_map_include ( map , * itr ) ;
}
else if ( itr - > is ( " Style " ) )
{
parse_style ( map , * itr ) ;
}
else if ( itr - > is ( " Layer " ) )
{
parse_layer ( map , * itr ) ;
}
else if ( itr - > is ( " FontSet " ) )
{
parse_fontset ( map , * itr ) ;
}
else if ( itr - > is ( " FileSource " ) )
2010-06-02 13:03:30 +02:00
{
2012-03-13 15:56:11 +01:00
std : : string name = itr - > get_attr < std : : string > ( " name " ) ;
std : : string value = itr - > get_text ( ) ;
file_sources_ [ name ] = value ;
}
else if ( itr - > is ( " Datasource " ) )
{
std : : string name = itr - > get_attr ( " name " , std : : string ( " Unnamed " ) ) ;
parameters params ;
xml_node : : const_iterator paramIter = itr - > begin ( ) ;
xml_node : : const_iterator endParam = itr - > end ( ) ;
for ( ; paramIter ! = endParam ; + + paramIter )
2011-01-04 16:22:49 +01:00
{
2012-03-13 15:56:11 +01:00
if ( paramIter - > is ( " Parameter " ) )
{
2013-01-04 23:36:24 +01:00
std : : string param_name = paramIter - > get_attr < std : : string > ( " name " ) ;
2012-03-13 15:56:11 +01:00
std : : string value = paramIter - > get_text ( ) ;
2013-01-04 23:36:24 +01:00
params [ param_name ] = value ;
2012-03-13 15:56:11 +01:00
}
2011-01-04 16:22:49 +01:00
}
2012-03-13 15:56:11 +01:00
datasource_templates_ [ name ] = params ;
2010-06-02 13:03:30 +02:00
}
2012-03-13 15:56:11 +01:00
else if ( itr - > is ( " Parameters " ) )
2011-12-03 02:48:09 +01:00
{
2012-03-13 15:56:11 +01:00
parameters & params = map . get_extra_parameters ( ) ;
xml_node : : const_iterator paramIter = itr - > begin ( ) ;
xml_node : : const_iterator endParam = itr - > end ( ) ;
for ( ; paramIter ! = endParam ; + + paramIter )
2011-12-03 02:48:09 +01:00
{
2012-03-13 15:56:11 +01:00
if ( paramIter - > is ( " Parameter " ) )
2011-12-05 23:46:38 +01:00
{
2012-03-13 15:56:11 +01:00
std : : string name = paramIter - > get_attr < std : : string > ( " name " ) ;
bool is_string = true ;
boost : : optional < std : : string > type = paramIter - > get_opt_attr < std : : string > ( " type " ) ;
if ( type )
2011-12-05 23:46:38 +01:00
{
2012-03-13 15:56:11 +01:00
if ( * type = = " int " )
{
is_string = false ;
2012-12-03 14:12:09 +01:00
mapnik : : value_integer value = paramIter - > get_value < mapnik : : value_integer > ( ) ;
2012-03-13 15:56:11 +01:00
params [ name ] = value ;
}
else if ( * type = = " float " )
{
is_string = false ;
2012-12-03 14:12:09 +01:00
double value = paramIter - > get_value < mapnik : : value_double > ( ) ;
2012-03-13 15:56:11 +01:00
params [ name ] = value ;
}
2011-12-05 23:46:38 +01:00
}
2012-03-13 15:56:11 +01:00
if ( is_string )
2011-12-05 23:46:38 +01:00
{
2012-03-13 15:56:11 +01:00
std : : string value = paramIter - > get_text ( ) ;
2011-12-05 23:46:38 +01:00
params [ name ] = value ;
}
}
2011-12-03 02:48:09 +01:00
}
}
}
2013-03-23 01:58:33 +01:00
} catch ( config_error const & ex ) {
2012-03-12 01:09:26 +01:00
ex . append_context ( include ) ;
2012-03-06 17:29:33 +01:00
throw ;
}
2010-04-09 20:47:19 +02:00
}
2007-09-25 20:47:12 +02:00
2012-03-06 17:29:33 +01:00
void map_parser : : parse_style ( Map & map , xml_node const & sty )
2010-04-09 20:47:19 +02:00
{
2011-06-24 02:53:00 +02:00
std : : string name ( " <missing name> " ) ;
2010-04-09 20:47:19 +02:00
try
2008-06-29 12:58:48 +02:00
{
2012-03-06 17:29:33 +01:00
name = sty . get_attr < std : : string > ( " name " ) ;
2010-06-02 13:03:30 +02:00
feature_type_style style ;
2012-03-06 17:29:33 +01:00
filter_mode_e filter_mode = sty . get_attr < filter_mode_e > ( " filter-mode " , FILTER_ALL ) ;
2011-02-01 23:55:50 +01:00
style . set_filter_mode ( filter_mode ) ;
2012-04-05 18:04:11 +02:00
2012-04-03 14:10:30 +02:00
// compositing
optional < std : : string > comp_op_name = sty . get_opt_attr < std : : string > ( " comp-op " ) ;
if ( comp_op_name )
{
2012-04-18 16:37:14 +02:00
optional < composite_mode_e > comp_op = comp_op_from_string ( * comp_op_name ) ;
if ( comp_op )
{
style . set_comp_op ( * comp_op ) ;
}
else
{
throw config_error ( " failed to parse comp-op: ' " + * comp_op_name + " ' " ) ;
}
2012-04-03 14:10:30 +02:00
}
2012-04-18 13:14:23 +02:00
2013-11-28 07:50:15 +01:00
optional < double > opacity = sty . get_opt_attr < double > ( " opacity " ) ;
if ( opacity ) style . set_opacity ( * opacity ) ;
2012-04-25 11:53:34 +02:00
2014-02-27 19:11:17 +01:00
optional < boolean > image_filters_inflate = sty . get_opt_attr < boolean > ( " image-filters-inflate " ) ;
if ( image_filters_inflate )
{
style . set_image_filters_inflate ( * image_filters_inflate ) ;
}
2012-06-19 21:10:28 +02:00
// image filters
2012-04-18 13:14:23 +02:00
optional < std : : string > filters = sty . get_opt_attr < std : : string > ( " image-filters " ) ;
if ( filters )
2012-04-03 14:10:30 +02:00
{
2013-01-04 04:14:19 +01:00
std : : string filter_str = * filters ;
2013-02-18 16:15:36 +01:00
bool result = filter : : parse_image_filters ( filter_str , style . image_filters ( ) ) ;
if ( ! result )
2012-04-25 11:53:34 +02:00
{
2013-02-18 16:15:36 +01:00
throw config_error ( " failed to parse image-filters: ' " + filter_str + " ' " ) ;
2012-07-05 18:16:11 +02:00
}
2012-04-25 11:53:34 +02:00
}
2012-07-05 18:16:11 +02:00
// direct image filters (applied directly on main image buffer
// TODO : consider creating a separate XML node e.g
// <ImageFilter name="myfilter" op="blur emboss"/>
//
2012-04-25 11:53:34 +02:00
optional < std : : string > direct_filters = sty . get_opt_attr < std : : string > ( " direct-image-filters " ) ;
if ( direct_filters )
{
2013-01-04 04:14:19 +01:00
std : : string filter_str = * direct_filters ;
std : : string : : const_iterator itr = filter_str . begin ( ) ;
std : : string : : const_iterator end = filter_str . end ( ) ;
2014-01-26 23:00:58 +01:00
boost : : spirit : : qi : : ascii : : space_type space ;
2012-07-05 18:16:11 +02:00
bool result = boost : : spirit : : qi : : phrase_parse ( itr , end ,
2012-08-23 17:13:22 +02:00
sty . get_tree ( ) . image_filters_grammar ,
2014-01-26 23:00:58 +01:00
space ,
2012-04-25 11:53:34 +02:00
style . direct_image_filters ( ) ) ;
2012-04-18 13:14:23 +02:00
if ( ! result | | itr ! = end )
{
2012-04-25 11:53:34 +02:00
throw config_error ( " failed to parse direct-image-filters: ' " + std : : string ( itr , end ) + " ' " ) ;
2012-07-05 18:16:11 +02:00
}
2012-04-18 13:14:23 +02:00
}
2012-07-05 18:16:11 +02:00
2012-04-18 13:14:23 +02:00
// rules
2013-12-09 22:15:24 +01:00
for ( auto const & rule_ : sty )
2010-06-02 13:03:30 +02:00
{
2013-12-09 22:15:24 +01:00
if ( rule_ . is ( " Rule " ) )
2010-06-02 13:03:30 +02:00
{
2013-12-09 22:15:24 +01:00
parse_rule ( style , rule_ ) ;
2010-06-02 13:03:30 +02:00
}
}
map . insert_style ( name , style ) ;
2013-12-09 22:15:24 +01:00
}
catch ( config_error const & ex )
{
2012-03-12 01:09:26 +01:00
ex . append_context ( std : : string ( " in style ' " ) + name + " ' " , sty ) ;
2010-07-06 02:37:05 +02:00
throw ;
}
}
2012-03-06 17:29:33 +01:00
void map_parser : : parse_fontset ( Map & map , xml_node const & fset )
2010-04-09 20:47:19 +02:00
{
2011-06-24 02:53:00 +02:00
std : : string name ( " <missing name> " ) ;
2010-04-09 20:47:19 +02:00
try
{
2012-03-06 17:29:33 +01:00
name = fset . get_attr < std : : string > ( " name " ) ;
2010-06-02 13:03:30 +02:00
font_set fontset ( name ) ;
2012-03-06 17:29:33 +01:00
xml_node : : const_iterator itr = fset . begin ( ) ;
xml_node : : const_iterator end = fset . end ( ) ;
2010-06-02 13:03:30 +02:00
2012-04-19 20:22:42 +02:00
bool success = false ;
2010-06-02 13:03:30 +02:00
for ( ; itr ! = end ; + + itr )
{
2012-03-06 17:29:33 +01:00
if ( itr - > is ( " Font " ) )
2010-06-02 13:03:30 +02:00
{
2012-04-19 20:22:42 +02:00
if ( parse_font ( fontset , * itr ) )
{
success = true ;
}
2010-06-02 13:03:30 +02:00
}
}
2012-04-19 20:22:42 +02:00
// if not at least one face-name is valid
if ( ! success )
{
throw mapnik : : config_error ( " no valid fonts could be loaded " ) ;
}
2010-06-02 13:03:30 +02:00
map . insert_fontset ( name , fontset ) ;
2012-02-02 02:53:35 +01:00
2010-06-02 13:03:30 +02:00
// XXX Hack because map object isn't accessible by text_symbolizer
// when it's parsed
2013-01-17 22:53:48 +01:00
fontsets_ . insert ( std : : pair < std : : string , font_set > ( name , fontset ) ) ;
2012-04-19 20:22:42 +02:00
}
2013-03-23 01:58:33 +01:00
catch ( config_error const & ex )
2012-04-19 20:22:42 +02:00
{
2012-03-12 01:09:26 +01:00
ex . append_context ( std : : string ( " in FontSet ' " ) + name + " ' " , fset ) ;
2010-06-02 13:03:30 +02:00
throw ;
2010-04-09 20:47:19 +02:00
}
}
2008-06-29 12:58:48 +02:00
2012-04-19 20:22:42 +02:00
bool map_parser : : parse_font ( font_set & fset , xml_node const & f )
2010-04-09 20:47:19 +02:00
{
2012-03-06 17:29:33 +01:00
optional < std : : string > face_name = f . get_opt_attr < std : : string > ( " face-name " ) ;
2011-12-20 21:31:38 +01:00
if ( face_name )
2007-09-25 20:47:12 +02:00
{
2012-04-19 20:22:42 +02:00
face_ptr face = font_manager_ . get_face ( * face_name ) ;
if ( face )
2011-12-20 21:31:38 +01:00
{
2012-04-19 20:22:42 +02:00
fset . add_face_name ( * face_name ) ;
return true ;
}
else if ( strict_ )
2011-12-20 21:31:38 +01:00
{
2012-04-19 20:22:42 +02:00
throw config_error ( " Failed to find font face ' " +
* face_name + " ' " ) ;
2011-12-20 21:31:38 +01:00
}
}
else
{
2012-03-12 01:09:26 +01:00
throw config_error ( " Must have 'face-name' set " , f ) ;
2007-09-25 20:47:12 +02:00
}
2012-04-19 20:22:42 +02:00
return false ;
2010-04-09 20:47:19 +02:00
}
2012-08-01 16:44:36 +02:00
void map_parser : : parse_layer ( Map & map , xml_node const & node )
2010-04-09 20:47:19 +02:00
{
std : : string name ;
try
2007-09-25 20:47:12 +02:00
{
2012-08-01 16:44:36 +02:00
name = node . get_attr ( " name " , std : : string ( " Unnamed " ) ) ;
2010-06-02 13:03:30 +02:00
2013-05-30 00:16:44 +02:00
// If no projection is given inherit from map
2012-08-01 16:44:36 +02:00
std : : string srs = node . get_attr ( " srs " , map . srs ( ) ) ;
2013-05-30 00:16:44 +02:00
try
{
// create throwaway projection object here to ensure it is valid
projection proj ( srs ) ;
}
2013-11-09 04:13:51 +01:00
catch ( std : : exception const & ex )
2013-05-30 00:16:44 +02:00
{
throw mapnik : : config_error ( ex . what ( ) ) ;
}
2010-06-02 13:03:30 +02:00
layer lyr ( name , srs ) ;
2012-08-01 16:44:36 +02:00
optional < boolean > status = node . get_opt_attr < boolean > ( " status " ) ;
2010-06-02 13:03:30 +02:00
if ( status )
{
2012-03-16 01:50:55 +01:00
lyr . set_active ( * status ) ;
2010-06-02 13:03:30 +02:00
}
2012-02-02 02:53:35 +01:00
2012-08-01 16:44:36 +02:00
optional < double > min_zoom = node . get_opt_attr < double > ( " minzoom " ) ;
2012-03-12 16:39:59 +01:00
if ( min_zoom )
2010-06-02 13:03:30 +02:00
{
2012-03-16 01:50:55 +01:00
lyr . set_min_zoom ( * min_zoom ) ;
2010-06-02 13:03:30 +02:00
}
2012-03-13 11:11:28 +01:00
2012-08-01 16:44:36 +02:00
optional < double > max_zoom = node . get_opt_attr < double > ( " maxzoom " ) ;
2012-03-12 16:39:59 +01:00
if ( max_zoom )
2010-06-02 13:03:30 +02:00
{
2012-03-16 01:50:55 +01:00
lyr . set_max_zoom ( * max_zoom ) ;
2010-06-02 13:03:30 +02:00
}
2012-08-01 16:44:36 +02:00
optional < boolean > queryable = node . get_opt_attr < boolean > ( " queryable " ) ;
2010-06-02 13:03:30 +02:00
if ( queryable )
{
2012-03-16 01:50:55 +01:00
lyr . set_queryable ( * queryable ) ;
2010-06-02 13:03:30 +02:00
}
optional < boolean > clear_cache =
2012-08-01 16:44:36 +02:00
node . get_opt_attr < boolean > ( " clear-label-cache " ) ;
2010-06-02 13:03:30 +02:00
if ( clear_cache )
{
2012-03-16 01:50:55 +01:00
lyr . set_clear_label_cache ( * clear_cache ) ;
2010-06-02 13:03:30 +02:00
}
2011-03-01 18:09:29 +01:00
optional < boolean > cache_features =
2012-08-01 16:44:36 +02:00
node . get_opt_attr < boolean > ( " cache-features " ) ;
2011-03-01 18:09:29 +01:00
if ( cache_features )
{
2012-03-16 01:50:55 +01:00
lyr . set_cache_features ( * cache_features ) ;
2011-03-01 18:09:29 +01:00
}
2011-10-13 01:30:18 +02:00
optional < std : : string > group_by =
2012-08-01 16:44:36 +02:00
node . get_opt_attr < std : : string > ( " group-by " ) ;
2011-10-13 01:30:18 +02:00
if ( group_by )
{
2012-03-16 01:50:55 +01:00
lyr . set_group_by ( * group_by ) ;
2011-10-13 01:30:18 +02:00
}
2012-04-05 18:04:11 +02:00
2012-08-01 16:44:36 +02:00
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
{
2013-11-28 07:50:15 +01:00
std : : string s_err ( " failed to parse Layer maximum-extent ' " ) ;
s_err + = * maximum_extent + " ' for ' " + name + " ' " ;
if ( strict_ )
{
throw config_error ( s_err ) ;
}
else
{
MAPNIK_LOG_ERROR ( load_map ) < < " map_parser: " < < s_err ;
}
2012-08-01 16:44:36 +02:00
}
}
xml_node : : const_iterator child = node . begin ( ) ;
xml_node : : const_iterator end = node . end ( ) ;
2010-06-02 13:03:30 +02:00
2012-03-06 17:29:33 +01:00
for ( ; child ! = end ; + + child )
2010-06-02 13:03:30 +02:00
{
2012-03-06 17:29:33 +01:00
if ( child - > is ( " StyleName " ) )
2010-06-02 13:03:30 +02:00
{
2012-03-07 15:26:13 +01:00
std : : string style_name = child - > get_text ( ) ;
2011-02-05 04:15:17 +01:00
if ( style_name . empty ( ) )
{
2012-12-07 05:15:27 +01:00
std : : string ss ( " StyleName is empty in Layer: ' " ) ;
ss + = lyr . name ( ) + " ' " ;
2011-02-05 04:15:17 +01:00
if ( strict_ )
2012-04-08 02:20:56 +02:00
{
2012-12-07 05:15:27 +01:00
throw config_error ( ss ) ;
2012-04-08 02:20:56 +02:00
}
2011-02-05 04:15:17 +01:00
else
2012-04-08 02:20:56 +02:00
{
2012-12-07 05:15:27 +01:00
MAPNIK_LOG_WARN ( load_map ) < < " map_parser: " < < ss ;
2012-04-08 02:20:56 +02:00
}
2011-02-05 04:15:17 +01:00
}
else
{
lyr . add_style ( style_name ) ;
}
2010-06-02 13:03:30 +02:00
}
2012-03-06 17:29:33 +01:00
else if ( child - > is ( " Datasource " ) )
2010-06-02 13:03:30 +02:00
{
parameters params ;
2012-03-06 17:29:33 +01:00
optional < std : : string > base = child - > get_opt_attr < std : : string > ( " base " ) ;
2012-03-16 01:50:55 +01:00
if ( base )
2010-06-02 13:03:30 +02:00
{
std : : map < std : : string , parameters > : : const_iterator base_itr = datasource_templates_ . find ( * base ) ;
if ( base_itr ! = datasource_templates_ . end ( ) )
2012-10-10 23:33:56 +02:00
{
2010-06-02 13:03:30 +02:00
params = base_itr - > second ;
2012-10-10 23:33:56 +02:00
}
else
{
MAPNIK_LOG_ERROR ( datasource ) < < " Datasource template ' " < < * base
2013-11-28 07:50:15 +01:00
< < " ' not found for layer ' " < < name < < " ' " ;
2012-10-10 23:33:56 +02:00
}
2010-06-02 13:03:30 +02:00
}
2012-03-06 17:29:33 +01:00
xml_node : : const_iterator paramIter = child - > begin ( ) ;
xml_node : : const_iterator endParam = child - > end ( ) ;
2010-06-02 13:03:30 +02:00
for ( ; paramIter ! = endParam ; + + paramIter )
{
2012-03-06 17:29:33 +01:00
if ( paramIter - > is ( " Parameter " ) )
2010-06-02 13:03:30 +02:00
{
2013-01-04 04:14:19 +01:00
std : : string param_name = paramIter - > get_attr < std : : string > ( " name " ) ;
2012-03-06 17:29:33 +01:00
std : : string value = paramIter - > get_text ( ) ;
2013-01-04 04:14:19 +01:00
params [ param_name ] = value ;
2010-06-02 13:03:30 +02:00
}
}
2011-01-25 21:47:56 +01:00
boost : : optional < std : : string > base_param = params . get < std : : string > ( " base " ) ;
boost : : optional < std : : string > file_param = params . get < std : : string > ( " file " ) ;
2010-06-02 13:03:30 +02:00
2011-01-25 21:47:56 +01:00
if ( base_param ) {
params [ " base " ] = ensure_relative_to_xml ( base_param ) ;
2010-06-02 13:03:30 +02:00
}
2011-01-25 21:47:56 +01:00
else if ( file_param ) {
params [ " file " ] = ensure_relative_to_xml ( file_param ) ;
2010-06-02 13:03:30 +02:00
}
2010-04-09 20:47:19 +02:00
2010-06-02 13:03:30 +02:00
//now we are ready to create datasource
try
{
2013-09-20 15:00:11 +02:00
std : : shared_ptr < datasource > ds =
2012-09-07 17:23:03 +02:00
datasource_cache : : instance ( ) . create ( params ) ;
2010-06-02 13:03:30 +02:00
lyr . set_datasource ( ds ) ;
}
2012-08-17 02:58:08 +02:00
catch ( std : : exception const & ex )
2010-06-02 13:03:30 +02:00
{
2012-03-16 01:50:55 +01:00
throw config_error ( ex . what ( ) ) ;
2010-06-02 13:03:30 +02:00
}
catch ( . . . )
{
2011-08-30 01:51:15 +02:00
throw config_error ( " Unknown exception occured attempting to create datasoure for layer ' " + lyr . name ( ) + " ' " ) ;
2010-06-02 13:03:30 +02:00
}
}
}
2013-11-28 07:50:15 +01:00
map . add_layer ( lyr ) ;
2012-02-02 02:53:35 +01:00
}
2013-03-23 01:58:33 +01:00
catch ( config_error const & ex )
2011-01-04 16:22:49 +01:00
{
2012-03-06 17:29:33 +01:00
if ( ! name . empty ( ) )
2011-01-04 16:22:49 +01:00
{
2012-08-01 16:44:36 +02:00
ex . append_context ( std : : string ( " encountered during parsing of layer ' " ) + name + " ' " , node ) ;
2010-06-02 13:03:30 +02:00
}
throw ;
2007-09-25 20:47:12 +02:00
}
2010-04-09 20:47:19 +02:00
}
2007-09-25 20:47:12 +02:00
2013-12-09 22:15:24 +01:00
void map_parser : : parse_rule ( feature_type_style & style , xml_node const & node )
2010-04-09 20:47:19 +02:00
{
std : : string name ;
try
2007-09-25 20:47:12 +02:00
{
2013-12-09 22:15:24 +01:00
name = node . get_attr ( " name " , std : : string ( ) ) ;
2011-12-13 17:47:45 +01:00
rule rule ( name ) ;
2010-06-02 13:03:30 +02:00
2013-12-09 22:15:24 +01:00
xml_node const * child = node . get_opt_child ( " Filter " ) ;
2012-03-07 01:35:37 +01:00
if ( child )
2010-06-02 13:03:30 +02:00
{
2012-03-07 01:35:37 +01:00
rule . set_filter ( child - > get_value < expression_ptr > ( ) ) ;
2010-06-02 13:03:30 +02:00
}
2013-12-09 22:15:24 +01:00
if ( node . has_child ( " ElseFilter " ) )
2010-06-02 13:03:30 +02:00
{
rule . set_else ( true ) ;
}
2013-12-09 22:15:24 +01:00
if ( node . has_child ( " AlsoFilter " ) )
2011-08-30 19:38:27 +02:00
{
rule . set_also ( true ) ;
}
2013-12-09 22:15:24 +01:00
child = node . get_opt_child ( " MinScaleDenominator " ) ;
2012-03-07 01:35:37 +01:00
if ( child )
2010-06-02 13:03:30 +02:00
{
2012-03-07 01:35:37 +01:00
rule . set_min_scale ( child - > get_value < double > ( ) ) ;
2010-06-02 13:03:30 +02:00
}
2013-12-09 22:15:24 +01:00
child = node . get_opt_child ( " MaxScaleDenominator " ) ;
2012-03-07 01:35:37 +01:00
if ( child )
2010-06-02 13:03:30 +02:00
{
2012-03-07 01:35:37 +01:00
rule . set_max_scale ( child - > get_value < double > ( ) ) ;
2010-06-02 13:03:30 +02:00
}
2013-12-20 00:11:35 +01:00
parse_symbolizers ( rule , node ) ;
2010-06-02 13:03:30 +02:00
style . add_rule ( rule ) ;
2010-04-09 20:47:19 +02:00
}
2013-03-23 01:58:33 +01:00
catch ( config_error const & ex )
2010-04-09 20:47:19 +02:00
{
2012-03-16 01:50:55 +01:00
if ( ! name . empty ( ) )
2010-06-02 13:03:30 +02:00
{
2013-12-09 22:15:24 +01:00
ex . append_context ( std : : string ( " in rule ' " ) + name + " ' " , node ) ;
2010-06-02 13:03:30 +02:00
}
throw ;
2010-04-09 20:47:19 +02:00
}
}
2013-12-20 00:11:35 +01:00
void map_parser : : parse_symbolizers ( rule & rule , xml_node const & node )
{
for ( auto const & sym_node : node )
{
switch ( name2int ( sym_node . name ( ) . c_str ( ) ) )
{
case name2int ( " PointSymbolizer " ) :
parse_point_symbolizer ( rule , sym_node ) ;
sym_node . set_processed ( true ) ;
break ;
case name2int ( " LinePatternSymbolizer " ) :
parse_line_pattern_symbolizer ( rule , sym_node ) ;
sym_node . set_processed ( true ) ;
break ;
case name2int ( " PolygonPatternSymbolizer " ) :
parse_polygon_pattern_symbolizer ( rule , sym_node ) ;
sym_node . set_processed ( true ) ;
break ;
case name2int ( " TextSymbolizer " ) :
parse_text_symbolizer ( rule , sym_node ) ;
sym_node . set_processed ( true ) ;
break ;
case name2int ( " ShieldSymbolizer " ) :
parse_shield_symbolizer ( rule , sym_node ) ;
sym_node . set_processed ( true ) ;
break ;
case name2int ( " LineSymbolizer " ) :
parse_line_symbolizer ( rule , sym_node ) ;
sym_node . set_processed ( true ) ;
break ;
case name2int ( " PolygonSymbolizer " ) :
parse_polygon_symbolizer ( rule , sym_node ) ;
sym_node . set_processed ( true ) ;
break ;
case name2int ( " BuildingSymbolizer " ) :
parse_building_symbolizer ( rule , sym_node ) ;
sym_node . set_processed ( true ) ;
break ;
case name2int ( " RasterSymbolizer " ) :
parse_raster_symbolizer ( rule , sym_node ) ;
sym_node . set_processed ( true ) ;
break ;
case name2int ( " MarkersSymbolizer " ) :
parse_markers_symbolizer ( rule , sym_node ) ;
sym_node . set_processed ( true ) ;
break ;
case name2int ( " GroupSymbolizer " ) :
parse_group_symbolizer ( rule , sym_node ) ;
sym_node . set_processed ( true ) ;
break ;
case name2int ( " DebugSymbolizer " ) :
parse_debug_symbolizer ( rule , sym_node ) ;
sym_node . set_processed ( true ) ;
break ;
default :
break ;
}
}
}
2013-12-04 17:11:11 +01:00
// helper method
template < typename Symbolizer , typename T >
void set_symbolizer_property ( Symbolizer & sym , keys key , xml_node const & node )
{
typedef T value_type ;
std : : string const & name = std : : get < 0 > ( get_meta ( key ) ) ;
try
{
optional < value_type > val = node . get_opt_attr < value_type > ( name ) ;
if ( val ) put ( sym , key , * val ) ;
}
catch ( config_error const & )
{
// try parser as an expression
optional < expression_ptr > val = node . get_opt_attr < expression_ptr > ( name ) ;
if ( val ) put ( sym , key , * val ) ;
}
}
2012-05-07 17:08:16 +02:00
void map_parser : : parse_symbolizer_base ( symbolizer_base & sym , xml_node const & pt )
2010-07-06 02:37:05 +02:00
{
2012-04-05 16:59:11 +02:00
optional < std : : string > comp_op_name = pt . get_opt_attr < std : : string > ( " comp-op " ) ;
if ( comp_op_name )
{
2012-04-18 16:37:14 +02:00
optional < composite_mode_e > comp_op = comp_op_from_string ( * comp_op_name ) ;
if ( comp_op )
{
2013-11-28 07:50:15 +01:00
put ( sym , keys : : comp_op , * comp_op ) ;
2012-04-18 16:37:14 +02:00
}
else
{
throw config_error ( " failed to parse comp-op: ' " + * comp_op_name + " ' " ) ;
}
2012-04-05 16:59:11 +02:00
}
2012-07-05 18:16:11 +02:00
2012-07-04 03:34:11 +02:00
optional < std : : string > geometry_transform_wkt = pt . get_opt_attr < std : : string > ( " geometry-transform " ) ;
if ( geometry_transform_wkt )
2012-04-18 10:46:19 +02:00
{
2013-09-20 15:00:11 +02:00
mapnik : : transform_list_ptr tl = std : : make_shared < mapnik : : transform_list > ( ) ;
2012-07-04 03:34:11 +02:00
if ( ! mapnik : : parse_transform ( * tl , * geometry_transform_wkt , pt . get_tree ( ) . transform_expr_grammar ) )
2012-04-18 10:46:19 +02:00
{
2012-12-07 05:15:27 +01:00
std : : string ss ( " Could not parse transform from ' " ) ;
ss + = * geometry_transform_wkt + " ', expected transform attribute " ;
throw config_error ( ss ) ;
2012-04-18 10:46:19 +02:00
}
2013-11-28 07:50:15 +01:00
put ( sym , keys : : geometry_transform , tl ) ;
2012-04-18 10:46:19 +02:00
}
2012-07-05 18:16:11 +02:00
2013-12-04 17:11:11 +01:00
// clip
set_symbolizer_property < symbolizer_base , boolean > ( sym , keys : : clip , pt ) ;
2012-08-16 16:53:38 +02:00
// simplify algorithm
2012-08-16 22:26:58 +02:00
optional < std : : string > simplify_algorithm_name = pt . get_opt_attr < std : : string > ( " simplify-algorithm " ) ;
2012-08-16 16:53:38 +02:00
if ( simplify_algorithm_name )
{
optional < simplify_algorithm_e > simplify_algorithm = simplify_algorithm_from_string ( * simplify_algorithm_name ) ;
if ( simplify_algorithm )
{
2013-11-28 07:50:15 +01:00
put ( sym , keys : : simplify_algorithm , * simplify_algorithm ) ;
2012-08-16 16:53:38 +02:00
}
else
{
throw config_error ( " failed to parse simplify-algorithm: ' " + * simplify_algorithm_name + " ' " ) ;
}
}
2012-08-14 17:04:57 +02:00
// simplify value
2013-12-04 17:11:11 +01:00
set_symbolizer_property < symbolizer_base , double > ( sym , keys : : simplify_tolerance , pt ) ;
2012-05-07 17:08:16 +02:00
// smooth value
2013-12-04 17:11:11 +01:00
set_symbolizer_property < symbolizer_base , double > ( sym , keys : : smooth , pt ) ;
2010-07-06 02:37:05 +02:00
}
2012-03-07 01:35:37 +01:00
void map_parser : : parse_point_symbolizer ( rule & rule , xml_node const & sym )
2010-04-09 20:47:19 +02:00
{
try
{
2012-03-07 01:35:37 +01:00
optional < std : : string > file = sym . get_opt_attr < std : : string > ( " file " ) ;
optional < std : : string > base = sym . get_opt_attr < std : : string > ( " base " ) ;
2013-12-03 06:47:25 +01:00
optional < std : : string > image_transform_wkt = sym . get_opt_attr < std : : string > ( " transform " ) ;
2012-07-05 18:16:11 +02:00
2012-03-07 01:35:37 +01:00
point_symbolizer symbol ;
2014-04-25 05:48:19 +02:00
set_symbolizer_property < point_symbolizer , boolean > ( symbol , keys : : allow_overlap , sym ) ;
2014-03-10 21:30:49 +01:00
set_symbolizer_property < point_symbolizer , double > ( symbol , keys : : opacity , sym ) ;
set_symbolizer_property < point_symbolizer , boolean > ( symbol , keys : : ignore_placement , sym ) ;
2013-11-28 07:50:15 +01:00
boost : : optional < point_placement_e > placement = sym . get_opt_attr < point_placement_e > ( " placement " ) ;
if ( placement ) put ( symbol , keys : : point_placement_type , point_placement_enum ( * placement ) ) ;
2010-06-02 13:03:30 +02:00
2012-07-04 03:04:35 +02:00
if ( file & & ! file - > empty ( ) )
2010-06-02 13:03:30 +02:00
{
2012-07-04 03:04:35 +02:00
if ( base )
2010-06-02 13:03:30 +02:00
{
2012-07-04 03:04:35 +02:00
std : : map < std : : string , std : : string > : : const_iterator itr = file_sources_ . find ( * base ) ;
if ( itr ! = file_sources_ . end ( ) )
2010-06-02 13:03:30 +02:00
{
2012-07-04 03:04:35 +02:00
* file = itr - > second + " / " + * file ;
2010-06-02 13:03:30 +02:00
}
2012-07-04 03:04:35 +02:00
}
2010-06-02 13:03:30 +02:00
2012-07-04 03:04:35 +02:00
* file = ensure_relative_to_xml ( file ) ;
2012-08-23 23:31:50 +02:00
std : : string filename = * file ;
ensure_exists ( filename ) ;
2013-11-28 07:50:15 +01:00
put ( symbol , keys : : file , parse_path ( filename , sym . get_tree ( ) . path_expr_grammar ) ) ;
2011-02-01 09:16:32 +01:00
2012-07-04 03:04:35 +02:00
if ( image_transform_wkt )
{
2013-09-20 15:00:11 +02:00
mapnik : : transform_list_ptr tl = std : : make_shared < mapnik : : transform_list > ( ) ;
2012-07-04 03:04:35 +02:00
if ( ! mapnik : : parse_transform ( * tl , * image_transform_wkt , sym . get_tree ( ) . transform_expr_grammar ) )
2010-06-02 13:03:30 +02:00
{
2012-08-14 00:35:22 +02:00
throw mapnik : : config_error ( " Failed to parse transform: ' " + * image_transform_wkt + " ' " ) ;
2010-06-02 13:03:30 +02:00
}
2013-11-28 07:50:15 +01:00
put ( symbol , keys : : image_transform , tl ) ;
2010-06-02 13:03:30 +02:00
}
2010-04-15 08:09:21 +02:00
}
2012-05-07 17:08:16 +02:00
parse_symbolizer_base ( symbol , sym ) ;
2013-11-28 07:50:15 +01:00
rule . append ( std : : move ( symbol ) ) ;
2010-04-09 20:47:19 +02:00
}
2013-03-23 01:58:33 +01:00
catch ( config_error const & ex )
2010-04-09 20:47:19 +02:00
{
2012-08-23 23:04:03 +02:00
ex . append_context ( sym ) ;
2010-06-02 13:03:30 +02:00
throw ;
2007-09-25 20:47:12 +02:00
}
2010-04-09 20:47:19 +02:00
}
2007-09-25 20:47:12 +02:00
2012-08-28 23:14:02 +02:00
void map_parser : : parse_markers_symbolizer ( rule & rule , xml_node const & sym )
2010-05-27 16:21:31 +02:00
{
try
{
2010-05-30 05:16:51 +02:00
std : : string filename ( " " ) ;
2012-08-28 23:14:02 +02:00
optional < std : : string > file = sym . get_opt_attr < std : : string > ( " file " ) ;
optional < std : : string > base = sym . get_opt_attr < std : : string > ( " base " ) ;
2011-02-05 04:15:17 +01:00
2012-07-04 03:04:35 +02:00
if ( file & & ! file - > empty ( ) )
2010-05-30 05:16:51 +02:00
{
2012-08-14 00:35:22 +02:00
if ( base )
2010-05-30 05:16:51 +02:00
{
2012-08-14 00:35:22 +02:00
std : : map < std : : string , std : : string > : : const_iterator itr = file_sources_ . find ( * base ) ;
if ( itr ! = file_sources_ . end ( ) )
2010-05-30 05:16:51 +02:00
{
2012-08-14 00:35:22 +02:00
* file = itr - > second + " / " + * file ;
2010-05-30 05:16:51 +02:00
}
}
2012-08-14 00:35:22 +02:00
filename = ensure_relative_to_xml ( file ) ;
2010-05-30 05:16:51 +02:00
}
2010-05-27 16:21:31 +02:00
2012-08-28 23:14:02 +02:00
optional < std : : string > marker_type = sym . get_opt_attr < std : : string > ( " marker-type " ) ;
2012-07-11 07:38:53 +02:00
if ( marker_type )
{
2012-08-22 01:36:15 +02:00
// TODO - revisit whether to officially deprecate marker-type
// https://github.com/mapnik/mapnik/issues/1427
//MAPNIK_LOG_WARN(markers_symbolizer) << "'marker-type' is deprecated and will be removed in Mapnik 3.x, use file='shape://<type>' to specify known svg shapes";
2012-07-11 07:38:53 +02:00
// back compatibility with Mapnik 2.0.0
if ( ! marker_type - > empty ( ) & & filename . empty ( ) )
{
if ( * marker_type = = " ellipse " )
{
2012-09-07 17:23:03 +02:00
filename = marker_cache : : instance ( ) . known_svg_prefix_ + " ellipse " ;
2012-07-11 07:38:53 +02:00
}
else if ( * marker_type = = " arrow " )
{
2012-09-07 17:23:03 +02:00
filename = marker_cache : : instance ( ) . known_svg_prefix_ + " arrow " ;
2012-07-11 07:38:53 +02:00
}
}
}
2012-08-28 23:14:02 +02:00
markers_symbolizer symbol ;
2012-07-11 07:38:53 +02:00
2012-07-04 03:04:35 +02:00
if ( ! filename . empty ( ) )
2012-04-07 01:50:11 +02:00
{
2012-08-23 23:31:50 +02:00
ensure_exists ( filename ) ;
2013-11-28 07:50:15 +01:00
put ( symbol , keys : : file , parse_path ( filename , sym . get_tree ( ) . path_expr_grammar ) ) ;
2012-04-07 01:50:11 +02:00
}
2012-08-03 23:14:03 +02:00
// overall opacity to be applied to all paths
2014-03-10 21:30:49 +01:00
set_symbolizer_property < markers_symbolizer , double > ( symbol , keys : : opacity , sym ) ;
2012-05-01 17:47:33 +02:00
2014-03-10 21:30:49 +01:00
// fill opacity
set_symbolizer_property < markers_symbolizer , double > ( symbol , keys : : fill_opacity , sym ) ;
2012-07-31 03:31:21 +02:00
2012-08-28 23:14:02 +02:00
optional < std : : string > image_transform_wkt = sym . get_opt_attr < std : : string > ( " transform " ) ;
2012-05-01 17:47:33 +02:00
if ( image_transform_wkt )
2010-06-03 14:35:31 +02:00
{
2013-09-20 15:00:11 +02:00
mapnik : : transform_list_ptr tl = std : : make_shared < mapnik : : transform_list > ( ) ;
2012-08-28 23:14:02 +02:00
if ( ! mapnik : : parse_transform ( * tl , * image_transform_wkt , sym . get_tree ( ) . transform_expr_grammar ) )
2010-12-11 02:10:45 +01:00
{
2012-08-14 00:35:22 +02:00
throw mapnik : : config_error ( " Failed to parse transform: ' " + * image_transform_wkt + " ' " ) ;
2010-12-11 02:10:45 +01:00
}
2013-11-28 07:50:15 +01:00
put ( symbol , keys : : image_transform , tl ) ;
2010-06-03 14:35:31 +02:00
}
2012-07-05 18:16:11 +02:00
2012-08-28 23:14:02 +02:00
optional < color > c = sym . get_opt_attr < color > ( " fill " ) ;
2013-11-28 07:50:15 +01:00
if ( c ) put ( symbol , keys : : fill , * c ) ;
2012-09-07 15:41:07 +02:00
2012-08-28 23:14:02 +02:00
optional < double > spacing = sym . get_opt_attr < double > ( " spacing " ) ;
2013-11-28 07:50:15 +01:00
if ( spacing ) put ( symbol , keys : : spacing , * spacing ) ;
2012-09-07 15:41:07 +02:00
2012-08-28 23:14:02 +02:00
optional < double > max_error = sym . get_opt_attr < double > ( " max-error " ) ;
2013-11-28 07:50:15 +01:00
if ( max_error ) put ( symbol , keys : : max_error , * max_error ) ;
2012-09-07 15:41:07 +02:00
2014-04-25 05:48:19 +02:00
set_symbolizer_property < markers_symbolizer , boolean > ( symbol , keys : : allow_overlap , sym ) ;
2012-09-07 15:41:07 +02:00
2014-03-10 21:30:49 +01:00
set_symbolizer_property < markers_symbolizer , boolean > ( symbol , keys : : ignore_placement , sym ) ;
2010-08-10 14:03:45 +02:00
2012-08-28 23:14:02 +02:00
optional < expression_ptr > width = sym . get_opt_attr < expression_ptr > ( " width " ) ;
2013-11-28 07:50:15 +01:00
if ( width ) put ( symbol , keys : : width , * width ) ;
2012-06-29 04:54:25 +02:00
2012-08-28 23:14:02 +02:00
optional < expression_ptr > height = sym . get_opt_attr < expression_ptr > ( " height " ) ;
2013-11-28 07:50:15 +01:00
if ( height ) put ( symbol , keys : : height , * height ) ;
2012-02-02 02:53:35 +01:00
2013-11-28 07:50:15 +01:00
parse_stroke ( symbol , sym ) ;
2010-08-19 19:33:01 +02:00
2013-11-28 07:50:15 +01:00
optional < marker_placement_e > placement = sym . get_opt_attr < marker_placement_e > ( " placement " ) ;
if ( placement ) put ( symbol , keys : : markers_placement_type , marker_placement_enum ( * placement ) ) ;
2012-11-21 02:58:39 +01:00
2013-11-28 07:50:15 +01:00
optional < marker_multi_policy_e > mpolicy = sym . get_opt_attr < marker_multi_policy_e > ( " multi-policy " ) ;
if ( mpolicy ) put ( symbol , keys : : markers_multipolicy , marker_multi_policy_enum ( * mpolicy ) ) ;
2012-11-21 02:58:39 +01:00
2012-08-28 23:14:02 +02:00
parse_symbolizer_base ( symbol , sym ) ;
2013-11-28 07:50:15 +01:00
rule . append ( std : : move ( symbol ) ) ;
2010-05-27 16:21:31 +02:00
}
2012-07-09 11:21:12 +02:00
catch ( config_error const & ex )
2010-05-27 16:21:31 +02:00
{
2012-08-28 23:14:02 +02:00
ex . append_context ( sym ) ;
2010-05-30 05:16:51 +02:00
throw ;
2010-05-27 16:21:31 +02:00
}
}
2012-03-16 01:50:55 +01:00
void map_parser : : parse_line_pattern_symbolizer ( rule & rule , xml_node const & sym )
2010-04-09 20:47:19 +02:00
{
try
2007-09-25 20:47:12 +02:00
{
2012-03-07 01:35:37 +01:00
std : : string file = sym . get_attr < std : : string > ( " file " ) ;
2012-07-04 03:04:35 +02:00
if ( file . empty ( ) )
{
throw config_error ( " empty file attribute " ) ;
}
2012-03-07 01:35:37 +01:00
optional < std : : string > base = sym . get_opt_attr < std : : string > ( " base " ) ;
2012-02-02 02:53:35 +01:00
2012-07-04 03:04:35 +02:00
if ( base )
2010-06-02 13:03:30 +02:00
{
2012-07-04 03:04:35 +02:00
std : : map < std : : string , std : : string > : : const_iterator itr = file_sources_ . find ( * base ) ;
if ( itr ! = file_sources_ . end ( ) )
2010-06-02 13:03:30 +02:00
{
2012-07-04 03:04:35 +02:00
file = itr - > second + " / " + file ;
2010-06-02 13:03:30 +02:00
}
}
2012-07-04 03:04:35 +02:00
file = ensure_relative_to_xml ( file ) ;
2012-08-23 23:31:50 +02:00
ensure_exists ( file ) ;
2013-11-28 07:50:15 +01:00
line_pattern_symbolizer symbol ;
put ( symbol , keys : : file , parse_path ( file , sym . get_tree ( ) . path_expr_grammar ) ) ;
2012-07-04 03:04:35 +02:00
2012-09-12 14:23:49 +02:00
// offset value
optional < double > offset = sym . get_opt_attr < double > ( " offset " ) ;
2013-11-28 07:50:15 +01:00
if ( offset ) put ( symbol , keys : : offset , * offset ) ;
2012-09-12 14:23:49 +02:00
2012-07-04 03:04:35 +02:00
parse_symbolizer_base ( symbol , sym ) ;
2013-11-28 07:50:15 +01:00
rule . append ( std : : move ( symbol ) ) ;
2007-09-25 20:47:12 +02:00
}
2013-03-23 01:58:33 +01:00
catch ( config_error const & ex )
2010-04-09 20:47:19 +02:00
{
2012-08-23 23:04:03 +02:00
ex . append_context ( sym ) ;
2010-06-02 13:03:30 +02:00
throw ;
2010-04-09 20:47:19 +02:00
}
}
2007-09-25 20:47:12 +02:00
2012-03-16 01:50:55 +01:00
void map_parser : : parse_polygon_pattern_symbolizer ( rule & rule ,
2012-03-23 00:37:24 +01:00
xml_node const & sym )
2010-04-09 20:47:19 +02:00
{
try
2007-09-25 20:47:12 +02:00
{
2012-03-07 01:35:37 +01:00
std : : string file = sym . get_attr < std : : string > ( " file " ) ;
2012-02-02 02:53:35 +01:00
2012-07-04 03:04:35 +02:00
if ( file . empty ( ) )
2010-06-02 13:03:30 +02:00
{
2012-07-04 03:04:35 +02:00
throw config_error ( " empty file attribute " ) ;
}
2011-01-25 21:47:56 +01:00
2012-07-04 03:04:35 +02:00
optional < std : : string > base = sym . get_opt_attr < std : : string > ( " base " ) ;
2010-06-21 00:36:49 +02:00
2012-07-04 03:04:35 +02:00
if ( base )
{
std : : map < std : : string , std : : string > : : iterator itr = file_sources_ . find ( * base ) ;
if ( itr ! = file_sources_ . end ( ) )
2012-04-07 01:50:11 +02:00
{
2012-07-04 03:04:35 +02:00
file = itr - > second + " / " + file ;
2012-04-07 01:50:11 +02:00
}
2012-07-04 03:04:35 +02:00
}
2011-06-29 00:37:35 +02:00
2012-07-04 03:04:35 +02:00
file = ensure_relative_to_xml ( file ) ;
2012-08-23 23:31:50 +02:00
ensure_exists ( file ) ;
2013-11-28 07:50:15 +01:00
polygon_pattern_symbolizer symbol ;
put ( symbol , keys : : file , parse_path ( file , sym . get_tree ( ) . path_expr_grammar ) ) ;
2012-07-04 03:04:35 +02:00
// pattern alignment
2013-11-28 07:50:15 +01:00
optional < pattern_alignment_e > p_alignment = sym . get_opt_attr < pattern_alignment_e > ( " alignment " ) ;
if ( p_alignment ) put ( symbol , keys : : alignment , pattern_alignment_enum ( * p_alignment ) ) ;
2012-07-05 18:16:11 +02:00
2012-07-04 03:04:35 +02:00
// opacity
2014-03-10 21:30:49 +01:00
set_symbolizer_property < polygon_pattern_symbolizer , double > ( symbol , keys : : opacity , sym ) ;
2012-07-05 18:16:11 +02:00
2012-07-04 03:04:35 +02:00
// gamma
optional < double > gamma = sym . get_opt_attr < double > ( " gamma " ) ;
2013-11-28 07:50:15 +01:00
if ( gamma ) put ( symbol , keys : : gamma , * gamma ) ;
2012-07-04 03:04:35 +02:00
// gamma method
optional < gamma_method_e > gamma_method = sym . get_opt_attr < gamma_method_e > ( " gamma-method " ) ;
2013-11-28 07:50:15 +01:00
if ( gamma_method ) put ( symbol , keys : : gamma_method , gamma_method_enum ( * gamma_method ) ) ;
2012-07-04 03:04:35 +02:00
parse_symbolizer_base ( symbol , sym ) ;
2013-11-28 07:50:15 +01:00
rule . append ( std : : move ( symbol ) ) ;
2007-09-25 20:47:12 +02:00
}
2013-03-23 01:58:33 +01:00
catch ( config_error const & ex )
2010-04-09 20:47:19 +02:00
{
2012-08-23 23:04:03 +02:00
ex . append_context ( sym ) ;
2010-06-02 13:03:30 +02:00
throw ;
2010-04-09 20:47:19 +02:00
}
}
2007-09-25 20:47:12 +02:00
2012-03-16 01:50:55 +01:00
void map_parser : : parse_text_symbolizer ( rule & rule , xml_node const & sym )
2010-04-09 20:47:19 +02:00
{
try
2007-09-25 20:47:12 +02:00
{
2011-02-28 14:17:46 +01:00
text_placements_ptr placement_finder ;
2012-03-07 01:35:37 +01:00
optional < std : : string > placement_type = sym . get_opt_attr < std : : string > ( " placement-type " ) ;
2013-11-28 07:50:15 +01:00
if ( placement_type )
{
2012-09-07 17:23:03 +02:00
placement_finder = placements : : registry : : instance ( ) . from_xml ( * placement_type , sym , fontsets_ ) ;
2013-11-28 07:50:15 +01:00
}
else
{
2013-09-20 15:00:11 +02:00
placement_finder = std : : make_shared < text_placements_dummy > ( ) ;
2012-02-18 00:39:14 +01:00
placement_finder - > defaults . from_xml ( sym , fontsets_ ) ;
2011-02-28 14:17:46 +01:00
}
2013-11-28 07:50:15 +01:00
if ( strict_ & &
! placement_finder - > defaults . format - > fontset )
2012-03-21 00:40:07 +01:00
{
Improved support for international text
- Implementation by @herm for GSOC 2012 (http://mapnik.org/news/2012/10/06/gsoc2012-status9/)
- C++11 port, improvements, optimizations by @artemp
- Testing and integration with master by @springmeyer
- Thank you to all the support from @behdad along the way
- Thanks for help testing @toton6868, @stephankn, @nirvn, @mfrasca, @simonsonc and many others
Refs: #2073,#2070,#2038,#2037,#1953,#1820,#1819,#1714,#1634,#1547,#1532,#1319,#1208,#1154,#1146
2013-11-22 09:06:32 +01:00
ensure_font_face ( placement_finder - > defaults . format - > face_name ) ;
2012-03-21 00:40:07 +01:00
}
2013-11-28 07:50:15 +01:00
text_symbolizer text_symbol ;
2012-05-07 17:08:16 +02:00
parse_symbolizer_base ( text_symbol , sym ) ;
2013-11-28 07:50:15 +01:00
put < text_placements_ptr > ( text_symbol , keys : : text_placements_ , placement_finder ) ;
optional < halo_rasterizer_e > halo_rasterizer_ = sym . get_opt_attr < halo_rasterizer_e > ( " halo-rasterizer " ) ;
if ( halo_rasterizer_ ) put ( text_symbol , keys : : halo_rasterizer , halo_rasterizer_enum ( * halo_rasterizer_ ) ) ;
2014-01-17 12:47:18 +01:00
optional < std : : string > halo_transform_wkt = sym . get_opt_attr < std : : string > ( " halo-transform " ) ;
if ( halo_transform_wkt )
{
mapnik : : transform_list_ptr tl = std : : make_shared < mapnik : : transform_list > ( ) ;
if ( ! mapnik : : parse_transform ( * tl , * halo_transform_wkt , sym . get_tree ( ) . transform_expr_grammar ) )
{
throw mapnik : : config_error ( " Failed to parse halo-transform: ' " + * halo_transform_wkt + " ' " ) ;
}
put ( text_symbol , keys : : halo_transform , tl ) ;
}
2013-11-28 07:50:15 +01:00
rule . append ( std : : move ( text_symbol ) ) ;
2007-09-25 20:47:12 +02:00
}
2013-03-23 01:58:33 +01:00
catch ( config_error const & ex )
2010-04-09 20:47:19 +02:00
{
2012-08-23 23:04:03 +02:00
ex . append_context ( sym ) ;
2010-06-02 13:03:30 +02:00
throw ;
2010-04-09 20:47:19 +02:00
}
}
2007-09-25 20:47:12 +02:00
2012-03-16 01:50:55 +01:00
void map_parser : : parse_shield_symbolizer ( rule & rule , xml_node const & sym )
2010-04-09 20:47:19 +02:00
{
try
2007-09-25 20:47:12 +02:00
{
2012-01-29 04:49:02 +01:00
text_placements_ptr placement_finder ;
2012-03-07 01:35:37 +01:00
optional < std : : string > placement_type = sym . get_opt_attr < std : : string > ( " placement-type " ) ;
2012-01-29 04:49:02 +01:00
if ( placement_type ) {
2012-09-07 17:23:03 +02:00
placement_finder = placements : : registry : : instance ( ) . from_xml ( * placement_type , sym , fontsets_ ) ;
2012-02-18 00:39:14 +01:00
} else {
2013-09-20 15:00:11 +02:00
placement_finder = std : : make_shared < text_placements_dummy > ( ) ;
2012-01-29 04:49:02 +01:00
}
2012-02-16 00:17:22 +01:00
placement_finder - > defaults . from_xml ( sym , fontsets_ ) ;
2012-02-12 11:34:28 +01:00
if ( strict_ & &
2013-11-28 07:50:15 +01:00
! placement_finder - > defaults . format - > fontset )
2012-03-21 00:40:07 +01:00
{
Improved support for international text
- Implementation by @herm for GSOC 2012 (http://mapnik.org/news/2012/10/06/gsoc2012-status9/)
- C++11 port, improvements, optimizations by @artemp
- Testing and integration with master by @springmeyer
- Thank you to all the support from @behdad along the way
- Thanks for help testing @toton6868, @stephankn, @nirvn, @mfrasca, @simonsonc and many others
Refs: #2073,#2070,#2038,#2037,#1953,#1820,#1819,#1714,#1634,#1547,#1532,#1319,#1208,#1154,#1146
2013-11-22 09:06:32 +01:00
ensure_font_face ( placement_finder - > defaults . format - > face_name ) ;
2012-03-21 00:40:07 +01:00
}
2012-01-29 04:49:02 +01:00
2013-11-28 07:50:15 +01:00
shield_symbolizer shield_symbol ;
put < text_placements_ptr > ( shield_symbol , keys : : text_placements_ , placement_finder ) ;
2012-06-30 00:59:56 +02:00
optional < std : : string > image_transform_wkt = sym . get_opt_attr < std : : string > ( " transform " ) ;
2012-05-01 17:47:33 +02:00
if ( image_transform_wkt )
2012-01-22 04:21:01 +01:00
{
2013-09-20 15:00:11 +02:00
mapnik : : transform_list_ptr tl = std : : make_shared < mapnik : : transform_list > ( ) ;
2012-05-27 23:50:09 +02:00
if ( ! mapnik : : parse_transform ( * tl , * image_transform_wkt , sym . get_tree ( ) . transform_expr_grammar ) )
2012-01-22 04:21:01 +01:00
{
2012-08-14 00:35:22 +02:00
throw mapnik : : config_error ( " Failed to parse transform: ' " + * image_transform_wkt + " ' " ) ;
2012-01-22 04:21:01 +01:00
}
2013-11-28 07:50:15 +01:00
put ( shield_symbol , keys : : image_transform , tl ) ;
2012-01-22 04:21:01 +01:00
}
2012-07-05 18:16:11 +02:00
2012-01-22 04:21:01 +01:00
// shield displacement
2013-11-28 07:50:15 +01:00
optional < double > shield_dx = sym . get_opt_attr < double > ( " shield-dx " ) ;
if ( shield_dx ) put ( shield_symbol , keys : : shield_dx , * shield_dx ) ;
optional < double > shield_dy = sym . get_opt_attr < double > ( " shield-dy " ) ;
if ( shield_dy ) put ( shield_symbol , keys : : shield_dy , * shield_dy ) ;
2012-01-22 04:21:01 +01:00
// opacity
2014-03-10 21:30:49 +01:00
set_symbolizer_property < shield_symbolizer , double > ( shield_symbol , keys : : opacity , sym ) ;
2010-06-02 13:03:30 +02:00
2012-01-22 04:21:01 +01:00
// text-opacity
2014-03-10 21:30:49 +01:00
set_symbolizer_property < shield_symbolizer , double > ( shield_symbol , keys : : text_opacity , sym ) ;
2010-06-02 13:03:30 +02:00
2012-01-22 04:21:01 +01:00
// unlock_image
2013-11-28 07:50:15 +01:00
optional < boolean > unlock_image = sym . get_opt_attr < boolean > ( " unlock-image " ) ;
if ( unlock_image ) put ( shield_symbol , keys : : unlock_image , * unlock_image ) ;
2010-06-02 13:03:30 +02:00
2012-07-04 03:04:35 +02:00
std : : string file = sym . get_attr < std : : string > ( " file " ) ;
if ( file . empty ( ) )
{
throw config_error ( " empty file attribute " ) ;
}
2012-03-07 01:35:37 +01:00
optional < std : : string > base = sym . get_opt_attr < std : : string > ( " base " ) ;
2012-07-04 03:04:35 +02:00
if ( base )
2010-06-02 13:03:30 +02:00
{
2012-07-04 03:04:35 +02:00
std : : map < std : : string , std : : string > : : const_iterator itr = file_sources_ . find ( * base ) ;
if ( itr ! = file_sources_ . end ( ) )
2010-06-02 13:03:30 +02:00
{
2012-07-04 03:04:35 +02:00
file = itr - > second + " / " + file ;
2010-06-02 13:03:30 +02:00
}
}
2012-07-04 03:04:35 +02:00
2012-08-17 04:20:29 +02:00
// no_text - removed property in 2.1.x that used to have a purpose
// before you could provide an expression with an empty string
2013-11-28 07:50:15 +01:00
optional < boolean > no_text = sym . get_opt_attr < boolean > ( " no-text " ) ;
2012-08-17 04:20:29 +02:00
if ( no_text )
{
2012-10-02 00:35:35 +02:00
MAPNIK_LOG_ERROR ( shield_symbolizer ) < < " 'no-text' is deprecated and will be removed in Mapnik 3.x, to create a ShieldSymbolizer without text just provide an element like: \" <ShieldSymbolizer ... />' '</> \" " ;
2013-11-28 07:50:15 +01:00
// FIXME
// if (*no_text)
// put(shield_symbol, "no-text", set_name(parse_expression("' '"));
2012-08-17 04:20:29 +02:00
}
2012-07-04 03:04:35 +02:00
file = ensure_relative_to_xml ( file ) ;
2012-08-23 23:31:50 +02:00
ensure_exists ( file ) ;
2013-11-28 07:50:15 +01:00
put ( shield_symbol , keys : : file , parse_path ( file , sym . get_tree ( ) . path_expr_grammar ) ) ;
2012-07-06 02:10:00 +02:00
parse_symbolizer_base ( shield_symbol , sym ) ;
2013-11-28 07:50:15 +01:00
rule . append ( std : : move ( shield_symbol ) ) ;
2007-09-25 20:47:12 +02:00
}
2013-03-23 01:58:33 +01:00
catch ( config_error const & ex )
2010-04-09 20:47:19 +02:00
{
2012-08-23 23:04:03 +02:00
ex . append_context ( sym ) ;
2010-06-02 13:03:30 +02:00
throw ;
2010-04-09 20:47:19 +02:00
}
}
2007-09-25 20:47:12 +02:00
2013-11-28 07:50:15 +01:00
void map_parser : : parse_stroke ( symbolizer_base & symbol , xml_node const & sym )
2010-04-09 20:47:19 +02:00
{
2013-12-04 17:11:11 +01:00
// stroke
set_symbolizer_property < symbolizer_base , color > ( symbol , keys : : stroke , sym ) ;
2010-08-19 14:20:30 +02:00
// stroke-width
2013-12-04 17:11:11 +01:00
set_symbolizer_property < symbolizer_base , double > ( symbol , keys : : stroke_width , sym ) ;
2010-08-19 14:20:30 +02:00
// stroke-opacity
2013-12-04 17:11:11 +01:00
set_symbolizer_property < symbolizer_base , double > ( symbol , keys : : stroke_opacity , sym ) ;
2011-05-17 02:33:24 +02:00
2010-08-19 14:20:30 +02:00
// stroke-linejoin
2012-03-07 01:35:37 +01:00
optional < line_join_e > line_join = sym . get_opt_attr < line_join_e > ( " stroke-linejoin " ) ;
2013-11-28 07:50:15 +01:00
if ( line_join ) put ( symbol , keys : : stroke_linejoin , line_join_enum ( * line_join ) ) ;
2011-05-17 02:33:24 +02:00
2010-08-19 14:20:30 +02:00
// stroke-linecap
2012-03-07 01:35:37 +01:00
optional < line_cap_e > line_cap = sym . get_opt_attr < line_cap_e > ( " stroke-linecap " ) ;
2013-11-28 07:50:15 +01:00
if ( line_cap ) put ( symbol , keys : : stroke_linecap , line_cap_enum ( * line_cap ) ) ;
2011-05-17 02:33:24 +02:00
2011-02-02 02:46:14 +01:00
// stroke-gamma
2012-03-07 01:35:37 +01:00
optional < double > gamma = sym . get_opt_attr < double > ( " stroke-gamma " ) ;
2013-11-28 07:50:15 +01:00
if ( gamma ) put ( symbol , keys : : stroke_gamma , * gamma ) ;
2011-05-17 02:33:24 +02:00
2012-01-13 18:20:03 +01:00
// stroke-gamma-method
2012-03-07 01:35:37 +01:00
optional < gamma_method_e > gamma_method = sym . get_opt_attr < gamma_method_e > ( " stroke-gamma-method " ) ;
2013-11-28 07:50:15 +01:00
if ( gamma_method ) put ( symbol , keys : : stroke_gamma_method , gamma_method_enum ( * gamma_method ) ) ;
2012-01-13 18:20:03 +01:00
2011-09-13 16:51:51 +02:00
// stroke-dashoffset
2012-03-07 01:35:37 +01:00
optional < double > dash_offset = sym . get_opt_attr < double > ( " stroke-dashoffset " ) ;
2013-11-28 07:50:15 +01:00
if ( dash_offset ) put ( symbol , keys : : stroke_dashoffset , * dash_offset ) ;
2011-05-17 02:33:24 +02:00
2010-08-19 14:20:30 +02:00
// stroke-dasharray
2012-03-07 01:35:37 +01:00
optional < std : : string > str = sym . get_opt_attr < std : : string > ( " stroke-dasharray " ) ;
2012-02-02 02:53:35 +01:00
if ( str )
2007-09-25 20:47:12 +02:00
{
2013-11-28 07:50:15 +01:00
std : : vector < double > buf ;
if ( util : : parse_dasharray ( ( * str ) . begin ( ) , ( * str ) . end ( ) , buf ) )
2010-06-02 13:03:30 +02:00
{
2013-11-28 07:50:15 +01:00
if ( ! buf . empty ( ) )
2010-08-19 14:20:30 +02:00
{
2013-11-28 07:50:15 +01:00
size_t size = buf . size ( ) ;
2012-04-05 18:04:11 +02:00
if ( size % 2 = = 1 )
2013-11-28 07:50:15 +01:00
buf . insert ( buf . end ( ) , buf . begin ( ) , buf . end ( ) ) ;
2012-04-05 18:04:11 +02:00
2013-11-28 07:50:15 +01:00
dash_array dash ;
std : : vector < double > : : const_iterator pos = buf . begin ( ) ;
while ( pos ! = buf . end ( ) )
2010-06-02 13:03:30 +02:00
{
2012-04-05 18:04:11 +02:00
if ( * pos > 0.0 | | * ( pos + 1 ) > 0.0 ) // avoid both dash and gap eq 0.0
2013-11-28 07:50:15 +01:00
dash . emplace_back ( * pos , * ( pos + 1 ) ) ;
2012-03-23 12:56:23 +01:00
pos + = 2 ;
2010-06-02 13:03:30 +02:00
}
2013-11-28 07:50:15 +01:00
if ( dash . size ( ) > 0 )
{
put ( symbol , keys : : stroke_dasharray , dash ) ;
}
2012-04-05 18:04:11 +02:00
}
2012-03-23 12:56:23 +01:00
}
else
{
throw config_error ( std : : string ( " Failed to parse dasharray " ) +
" '. Expected a " +
" list of floats or 'none' but got ' " + ( * str ) + " ' " ) ;
2010-06-02 13:03:30 +02:00
}
2010-08-19 14:20:30 +02:00
}
2012-07-05 18:16:11 +02:00
2012-05-02 18:04:24 +02:00
// stroke-miterlimit
optional < double > miterlimit = sym . get_opt_attr < double > ( " stroke-miterlimit " ) ;
2013-11-28 07:50:15 +01:00
if ( miterlimit ) put ( symbol , keys : : stroke_miterlimit , * miterlimit ) ;
2010-08-19 14:20:30 +02:00
}
2013-12-04 17:11:11 +01:00
void map_parser : : parse_line_symbolizer ( rule & rule , xml_node const & node )
2010-08-19 14:20:30 +02:00
{
try
{
2013-12-04 17:11:11 +01:00
line_symbolizer sym ;
parse_symbolizer_base ( sym , node ) ;
// stroke parameters
parse_stroke ( sym , node ) ;
// offset
set_symbolizer_property < symbolizer_base , double > ( sym , keys : : offset , node ) ;
// rasterizer
optional < line_rasterizer_e > rasterizer = node . get_opt_attr < line_rasterizer_e > ( " rasterizer " ) ;
if ( rasterizer ) put ( sym , keys : : line_rasterizer , line_rasterizer_enum ( * rasterizer ) ) ;
rule . append ( std : : move ( sym ) ) ;
2007-09-25 20:47:12 +02:00
}
2013-03-23 01:58:33 +01:00
catch ( config_error const & ex )
2010-04-09 20:47:19 +02:00
{
2013-12-04 17:11:11 +01:00
ex . append_context ( node ) ;
2010-06-02 13:03:30 +02:00
throw ;
2010-04-09 20:47:19 +02:00
}
}
2007-09-25 20:47:12 +02:00
2013-12-04 17:11:11 +01:00
void map_parser : : parse_polygon_symbolizer ( rule & rule , xml_node const & node )
2010-04-09 20:47:19 +02:00
{
try
2007-09-25 20:47:12 +02:00
{
2013-12-04 17:11:11 +01:00
polygon_symbolizer sym ;
parse_symbolizer_base ( sym , node ) ;
2010-06-02 13:03:30 +02:00
// fill
2013-12-04 17:11:11 +01:00
set_symbolizer_property < symbolizer_base , color > ( sym , keys : : fill , node ) ;
2010-06-02 13:03:30 +02:00
// fill-opacity
2013-12-04 17:11:11 +01:00
set_symbolizer_property < symbolizer_base , double > ( sym , keys : : fill_opacity , node ) ;
2010-06-02 13:03:30 +02:00
// gamma
2013-12-04 17:11:11 +01:00
set_symbolizer_property < symbolizer_base , double > ( sym , keys : : gamma , node ) ;
2012-01-12 16:58:10 +01:00
// gamma method
2013-12-04 17:11:11 +01:00
optional < gamma_method_e > gamma_method = node . get_opt_attr < gamma_method_e > ( " gamma-method " ) ;
if ( gamma_method ) put ( sym , keys : : gamma_method , gamma_method_enum ( * gamma_method ) ) ;
//
rule . append ( std : : move ( sym ) ) ;
2007-09-25 20:47:12 +02:00
}
2013-03-23 01:58:33 +01:00
catch ( config_error const & ex )
2010-04-09 20:47:19 +02:00
{
2013-12-04 17:11:11 +01:00
ex . append_context ( node ) ;
2010-06-02 13:03:30 +02:00
throw ;
2010-04-09 20:47:19 +02:00
}
}
2012-03-16 01:50:55 +01:00
void map_parser : : parse_building_symbolizer ( rule & rule , xml_node const & sym )
2010-04-09 20:47:19 +02:00
{
2012-02-02 02:53:35 +01:00
try
2011-02-05 04:15:17 +01:00
{
2010-06-02 13:03:30 +02:00
building_symbolizer building_sym ;
// fill
2012-03-07 01:35:37 +01:00
optional < color > fill = sym . get_opt_attr < color > ( " fill " ) ;
2013-11-28 07:50:15 +01:00
if ( fill ) put ( building_sym , keys : : fill , * fill ) ;
2010-06-02 13:03:30 +02:00
// fill-opacity
2014-03-10 21:30:49 +01:00
set_symbolizer_property < building_symbolizer , double > ( building_sym , keys : : fill_opacity , sym ) ;
2010-06-02 13:03:30 +02:00
// height
2012-03-11 23:24:28 +01:00
optional < expression_ptr > height = sym . get_opt_attr < expression_ptr > ( " height " ) ;
2013-11-28 07:50:15 +01:00
if ( height ) put ( building_sym , keys : : height , * height ) ;
2010-06-02 13:03:30 +02:00
2012-05-07 17:08:16 +02:00
parse_symbolizer_base ( building_sym , sym ) ;
2013-11-28 07:50:15 +01:00
rule . append ( std : : move ( building_sym ) ) ;
2010-04-09 20:47:19 +02:00
}
2013-03-23 01:58:33 +01:00
catch ( config_error const & ex )
2007-09-25 20:47:12 +02:00
{
2012-08-23 23:04:03 +02:00
ex . append_context ( sym ) ;
2010-06-02 13:03:30 +02:00
throw ;
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
}
2010-04-09 20:47:19 +02:00
}
2012-03-16 01:50:55 +01:00
void map_parser : : parse_raster_symbolizer ( rule & rule , xml_node const & sym )
2010-04-09 20:47:19 +02:00
{
try
{
2010-06-02 13:03:30 +02:00
raster_symbolizer raster_sym ;
// mode
2012-03-07 01:35:37 +01:00
optional < std : : string > mode = sym . get_opt_attr < std : : string > ( " mode " ) ;
2012-08-17 04:20:29 +02:00
if ( mode )
{
std : : string mode_string = * mode ;
if ( boost : : algorithm : : find_first ( mode_string , " _ " ) )
{
MAPNIK_LOG_ERROR ( raster_symbolizer ) < < " 'mode' values using \" _ \" are deprecated and will be removed in Mapnik 3.x, use \" - \" instead " ;
boost : : algorithm : : replace_all ( mode_string , " _ " , " - " ) ;
}
2013-11-28 07:50:15 +01:00
put ( raster_sym , keys : : mode , mode_string ) ;
2012-08-17 04:20:29 +02:00
}
2010-06-02 13:03:30 +02:00
// scaling
2012-03-07 01:35:37 +01:00
optional < std : : string > scaling = sym . get_opt_attr < std : : string > ( " scaling " ) ;
2012-07-07 01:45:58 +02:00
if ( scaling )
{
std : : string scaling_method = * scaling ;
if ( scaling_method = = " fast " )
{
MAPNIK_LOG_ERROR ( raster_symbolizer ) < < " 'scaling' value of 'fast' is deprecated and will be removed in Mapnik 3.x, use 'near' with Mapnik >= 2.1.x " ;
2013-11-28 07:50:15 +01:00
put ( raster_sym , keys : : scaling , SCALING_NEAR ) ;
2012-07-07 01:45:58 +02:00
}
else
{
boost : : optional < scaling_method_e > method = scaling_method_from_string ( scaling_method ) ;
if ( method )
{
2013-11-28 07:50:15 +01:00
put ( raster_sym , keys : : scaling , * method ) ;
2012-07-07 01:45:58 +02:00
}
else
{
throw config_error ( " failed to parse 'scaling': ' " + * scaling + " ' " ) ;
}
}
}
2010-06-02 13:03:30 +02:00
// opacity
2013-11-28 07:50:15 +01:00
optional < double > opacity = sym . get_opt_attr < double > ( " opacity " ) ;
if ( opacity ) put ( raster_sym , keys : : opacity , * opacity ) ;
2010-06-02 13:03:30 +02:00
2011-01-27 04:47:54 +01:00
// filter factor
2012-03-07 01:35:37 +01:00
optional < double > filter_factor = sym . get_opt_attr < double > ( " filter-factor " ) ;
2013-11-28 07:50:15 +01:00
if ( filter_factor ) put ( raster_sym , keys : : filter_factor , * filter_factor ) ;
2011-01-27 04:47:54 +01:00
2011-09-16 14:21:21 +02:00
// mesh-size
2012-03-07 01:35:37 +01:00
optional < unsigned > mesh_size = sym . get_opt_attr < unsigned > ( " mesh-size " ) ;
2013-11-28 07:50:15 +01:00
if ( mesh_size ) put < value_integer > ( raster_sym , keys : : mesh_size , * mesh_size ) ;
2011-09-16 14:21:21 +02:00
2012-10-02 01:14:41 +02:00
// premultiplied status of image
optional < boolean > premultiplied = sym . get_opt_attr < boolean > ( " premultiplied " ) ;
2013-11-28 07:50:15 +01:00
if ( premultiplied ) put ( raster_sym , keys : : premultiplied , * premultiplied ) ;
2011-09-16 14:21:21 +02:00
2013-02-01 22:32:48 +01:00
bool found_colorizer = false ;
2013-12-09 21:09:37 +01:00
for ( auto const & css : sym )
2010-06-02 13:03:30 +02:00
{
2013-12-09 21:09:37 +01:00
if ( css . is ( " RasterColorizer " ) )
2010-06-02 13:03:30 +02:00
{
2013-02-01 22:32:48 +01:00
found_colorizer = true ;
2013-09-20 15:00:11 +02:00
raster_colorizer_ptr colorizer = std : : make_shared < raster_colorizer > ( ) ;
2013-11-28 07:50:15 +01:00
put ( raster_sym , keys : : colorizer , colorizer ) ;
2013-12-09 21:09:37 +01:00
if ( parse_raster_colorizer ( colorizer , css ) )
2013-11-28 07:50:15 +01:00
put ( raster_sym , keys : : colorizer , colorizer ) ;
2010-06-02 13:03:30 +02:00
}
}
2013-11-28 07:50:15 +01:00
//look for properties one level up
2013-02-01 22:32:48 +01:00
if ( ! found_colorizer )
{
2013-09-20 15:00:11 +02:00
raster_colorizer_ptr colorizer = std : : make_shared < raster_colorizer > ( ) ;
2013-02-01 22:32:48 +01:00
if ( parse_raster_colorizer ( colorizer , sym ) )
2013-11-28 07:50:15 +01:00
put ( raster_sym , keys : : colorizer , colorizer ) ;
2013-02-01 22:32:48 +01:00
}
2012-05-10 19:15:32 +02:00
parse_symbolizer_base ( raster_sym , sym ) ;
2013-11-28 07:50:15 +01:00
rule . append ( std : : move ( raster_sym ) ) ;
2010-04-09 20:47:19 +02:00
}
2013-03-23 01:58:33 +01:00
catch ( config_error const & ex )
2009-03-29 13:05:20 +02:00
{
2012-08-23 23:04:03 +02:00
ex . append_context ( sym ) ;
2010-06-02 13:03:30 +02:00
throw ;
2010-03-12 15:49:34 +01:00
}
2010-04-09 20:47:19 +02:00
}
2010-03-19 12:19:43 +01:00
2013-12-20 00:11:35 +01:00
void map_parser : : parse_group_symbolizer ( rule & rule , xml_node const & sym )
{
try
{
group_symbolizer symbol ;
group_symbolizer_properties_ptr prop = std : : make_shared < group_symbolizer_properties > ( ) ;
set_symbolizer_property < symbolizer_base , value_integer > ( symbol , keys : : num_columns , sym ) ;
set_symbolizer_property < symbolizer_base , value_integer > ( symbol , keys : : start_column , sym ) ;
set_symbolizer_property < symbolizer_base , expression_ptr > ( symbol , keys : : repeat_key , sym ) ;
text_placements_ptr placements = std : : make_shared < text_placements_dummy > ( ) ;
placements - > defaults . placement_properties_from_xml ( sym ) ;
put < text_placements_ptr > ( symbol , keys : : text_placements_ , placements ) ;
size_t layout_count = 0 ;
for ( auto const & node : sym )
{
if ( node . is ( " GroupRule " ) )
{
parse_group_rule ( * prop , node ) ;
node . set_processed ( true ) ;
}
else if ( node . is ( " SimpleLayout " ) )
{
parse_simple_layout ( * prop , node ) ;
node . set_processed ( true ) ;
+ + layout_count ;
}
else if ( node . is ( " PairLayout " ) )
{
parse_pair_layout ( * prop , node ) ;
node . set_processed ( true ) ;
+ + layout_count ;
}
if ( layout_count > 1 )
{
throw config_error ( " Provide only one layout for a GroupSymbolizer. " ) ;
}
}
put ( symbol , keys : : group_properties , prop ) ;
parse_symbolizer_base ( symbol , sym ) ;
rule . append ( symbol ) ;
}
catch ( const config_error & ex )
{
ex . append_context ( sym ) ;
throw ;
}
}
2012-07-31 23:11:05 +02:00
void map_parser : : parse_debug_symbolizer ( rule & rule , xml_node const & sym )
{
debug_symbolizer symbol ;
parse_symbolizer_base ( symbol , sym ) ;
2013-11-28 07:50:15 +01:00
optional < debug_symbolizer_mode_e > mode = sym . get_opt_attr < debug_symbolizer_mode_e > ( " mode " ) ;
if ( mode ) put ( symbol , keys : : mode , debug_symbolizer_mode_enum ( * mode ) ) ;
rule . append ( std : : move ( symbol ) ) ;
2012-07-31 23:11:05 +02:00
}
2013-02-01 22:32:48 +01:00
bool map_parser : : parse_raster_colorizer ( raster_colorizer_ptr const & rc ,
2012-03-12 01:09:26 +01:00
xml_node const & node )
2010-04-09 20:47:19 +02:00
{
2013-02-01 22:32:48 +01:00
bool found_stops = false ;
2010-04-09 20:47:19 +02:00
try
{
2011-05-04 02:20:17 +02:00
// mode
colorizer_mode default_mode =
2012-03-07 01:35:37 +01:00
node . get_attr < colorizer_mode > ( " default-mode " , COLORIZER_LINEAR ) ;
2012-02-02 02:53:35 +01:00
2011-05-04 02:20:17 +02:00
if ( default_mode = = COLORIZER_INHERIT ) {
throw config_error ( " RasterColorizer mode must not be INHERIT. " ) ;
}
2012-03-16 01:50:55 +01:00
rc - > set_default_mode ( default_mode ) ;
2010-06-02 13:03:30 +02:00
2011-05-04 02:20:17 +02:00
// default colour
2012-03-07 01:35:37 +01:00
optional < color > default_color = node . get_opt_attr < color > ( " default-color " ) ;
2012-02-02 02:53:35 +01:00
if ( default_color )
2010-06-02 13:03:30 +02:00
{
2012-03-16 01:50:55 +01:00
rc - > set_default_color ( * default_color ) ;
2011-05-04 02:20:17 +02:00
}
2012-02-02 02:53:35 +01:00
2011-05-04 02:20:17 +02:00
// epsilon
2012-03-07 01:35:37 +01:00
optional < float > eps = node . get_opt_attr < float > ( " epsilon " ) ;
2012-02-02 02:53:35 +01:00
if ( eps )
2011-05-04 02:20:17 +02:00
{
if ( * eps < 0 ) {
throw config_error ( " RasterColorizer epsilon must be > 0. " ) ;
}
2012-03-16 01:50:55 +01:00
rc - > set_epsilon ( * eps ) ;
2011-05-04 02:20:17 +02:00
}
2012-02-02 02:53:35 +01:00
2012-03-07 01:35:37 +01:00
xml_node : : const_iterator stopIter = node . begin ( ) ;
xml_node : : const_iterator endStop = node . end ( ) ;
2011-05-04 02:20:17 +02:00
float maximumValue = - std : : numeric_limits < float > : : max ( ) ;
2010-06-02 13:03:30 +02:00
2011-05-04 02:20:17 +02:00
for ( ; stopIter ! = endStop ; + + stopIter )
{
2012-03-07 01:35:37 +01:00
if ( stopIter - > is ( " stop " ) )
2010-06-02 13:03:30 +02:00
{
2013-02-01 22:32:48 +01:00
found_stops = true ;
2011-05-04 02:20:17 +02:00
// colour is optional.
2012-03-07 01:35:37 +01:00
optional < color > stopcolor = stopIter - > get_opt_attr < color > ( " color " ) ;
2011-05-04 02:20:17 +02:00
if ( ! stopcolor ) {
* stopcolor = * default_color ;
}
// mode default to INHERIT
colorizer_mode mode =
2012-03-07 01:35:37 +01:00
stopIter - > get_attr < colorizer_mode > ( " mode " , COLORIZER_INHERIT ) ;
2011-05-04 02:20:17 +02:00
// value is required, and it must be bigger than the previous
optional < float > value =
2012-03-07 01:35:37 +01:00
stopIter - > get_opt_attr < float > ( " value " ) ;
2012-02-02 02:53:35 +01:00
2011-05-04 02:20:17 +02:00
if ( ! value ) {
throw config_error ( " stop tag missing value " ) ;
2010-06-02 13:03:30 +02:00
}
2012-02-02 02:53:35 +01:00
2011-05-04 02:20:17 +02:00
if ( value < maximumValue ) {
throw config_error ( " stop tag values must be in ascending order " ) ;
2010-06-02 13:03:30 +02:00
}
2011-05-04 02:20:17 +02:00
maximumValue = * value ;
2011-09-16 15:32:16 +02:00
optional < std : : string > label =
2012-03-13 15:56:11 +01:00
stopIter - > get_opt_attr < std : : string > ( " label " ) ;
2011-05-04 02:20:17 +02:00
//append the stop
colorizer_stop tmpStop ;
tmpStop . set_color ( * stopcolor ) ;
tmpStop . set_mode ( mode ) ;
tmpStop . set_value ( * value ) ;
2011-09-16 15:32:16 +02:00
if ( label )
tmpStop . set_label ( * label ) ;
2012-02-02 02:53:35 +01:00
2011-05-04 02:20:17 +02:00
rc - > add_stop ( tmpStop ) ;
2010-06-02 13:03:30 +02:00
}
2010-05-06 09:21:11 +02:00
}
2010-04-09 20:47:19 +02:00
}
2013-03-23 01:58:33 +01:00
catch ( config_error const & ex )
2007-09-25 20:47:12 +02:00
{
2012-08-23 23:04:03 +02:00
ex . append_context ( node ) ;
2010-06-02 13:03:30 +02:00
throw ;
2007-09-25 20:47:12 +02:00
}
2013-02-01 22:32:48 +01:00
return found_stops ;
2010-04-09 20:47:19 +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
2013-12-20 00:11:35 +01:00
void map_parser : : parse_group_rule ( group_symbolizer_properties & prop , xml_node const & node )
{
try
{
rule fake_rule ;
expression_ptr filter , repeat_key ;
xml_node const * filter_child = node . get_opt_child ( " Filter " ) ,
* rptkey_child = node . get_opt_child ( " RepeatKey " ) ;
if ( filter_child )
{
filter = filter_child - > get_value < expression_ptr > ( ) ;
}
else
{
filter = std : : make_shared < mapnik : : expr_node > ( true ) ;
}
if ( rptkey_child )
{
repeat_key = rptkey_child - > get_value < expression_ptr > ( ) ;
}
group_rule_ptr rule = std : : make_shared < group_rule > ( filter , repeat_key ) ;
parse_symbolizers ( fake_rule , node ) ;
for ( auto const & sym : fake_rule )
{
rule - > append ( sym ) ;
}
prop . add_rule ( rule ) ;
}
catch ( const config_error & ex )
{
ex . append_context ( node ) ;
throw ;
}
}
void map_parser : : parse_simple_layout ( group_symbolizer_properties & prop , xml_node const & node )
{
simple_row_layout layout ;
optional < double > item_margin = node . get_opt_attr < double > ( " item-margin " ) ;
if ( item_margin ) layout . set_item_margin ( * item_margin ) ;
prop . set_layout ( std : : move ( layout ) ) ;
}
void map_parser : : parse_pair_layout ( group_symbolizer_properties & prop , xml_node const & node )
{
pair_layout layout ;
optional < double > item_margin = node . get_opt_attr < double > ( " item-margin " ) ;
if ( item_margin ) layout . set_item_margin ( * item_margin ) ;
optional < double > max_difference = node . get_opt_attr < double > ( " max-difference " ) ;
if ( max_difference ) layout . set_max_difference ( * max_difference ) ;
prop . set_layout ( std : : move ( layout ) ) ;
}
2012-03-16 01:50:55 +01:00
void map_parser : : ensure_font_face ( std : : string const & face_name )
2010-04-09 20:47:19 +02:00
{
2012-03-16 01:50:55 +01:00
if ( ! font_manager_ . get_face ( face_name ) )
2009-05-01 03:59:01 +02:00
{
2010-06-02 13:03:30 +02:00
throw config_error ( " Failed to find font face ' " +
face_name + " ' " ) ;
2010-04-09 20:47:19 +02:00
}
}
2013-03-13 00:56:31 +01:00
std : : string map_parser : : ensure_relative_to_xml ( boost : : optional < std : : string > const & opt_path )
2010-04-09 20:47:19 +02:00
{
2012-09-07 17:23:03 +02:00
if ( marker_cache : : instance ( ) . is_uri ( * opt_path ) )
2012-07-11 07:38:53 +02:00
return * opt_path ;
2013-06-03 01:06:32 +02:00
if ( ! xml_base_path_ . empty ( ) & & relative_to_xml_ )
2011-01-04 16:22:49 +01:00
{
2013-06-03 04:28:24 +02:00
std : : string starting_path = * opt_path ;
if ( mapnik : : util : : is_relative ( starting_path ) )
2011-01-25 21:47:56 +01:00
{
2013-06-03 04:28:24 +02:00
return mapnik : : util : : make_absolute ( starting_path , xml_base_path_ ) ;
2011-01-25 21:47:56 +01:00
}
2009-05-01 03:59:01 +02:00
}
2010-04-09 20:47:19 +02:00
return * opt_path ;
}
2009-05-01 03:59:01 +02:00
2012-08-23 23:31:50 +02:00
void map_parser : : ensure_exists ( std : : string const & file_path )
{
2012-09-07 17:23:03 +02:00
if ( marker_cache : : instance ( ) . is_uri ( file_path ) )
2012-08-23 23:31:50 +02:00
return ;
// validate that the filename exists if it is not a dynamic PathExpression
if ( ! boost : : algorithm : : find_first ( file_path , " [ " ) & & ! boost : : algorithm : : find_first ( file_path , " ] " ) )
{
2013-11-28 07:50:15 +01:00
if ( ! mapnik : : util : : exists ( file_path ) )
{
throw mapnik : : config_error ( " file could not be found: ' " + file_path + " ' " ) ;
}
2012-08-23 23:31:50 +02:00
}
}
2012-03-12 02:12:58 +01:00
void map_parser : : find_unused_nodes ( xml_node const & root )
2011-02-05 04:15:17 +01:00
{
2012-12-07 05:15:27 +01:00
std : : string error_message ;
2012-03-12 02:12:58 +01:00
find_unused_nodes_recursive ( root , error_message ) ;
2012-12-07 05:15:27 +01:00
if ( ! error_message . empty ( ) )
2011-02-05 04:15:17 +01:00
{
2012-12-07 05:15:27 +01:00
std : : string msg ( " Unable to process some data while parsing ' " + filename_ + " ': " + error_message ) ;
2012-08-17 01:10:23 +02:00
if ( strict_ )
{
throw config_error ( msg ) ;
}
else
{
2012-08-23 23:11:25 +02:00
MAPNIK_LOG_ERROR ( load_map ) < < msg ;
2012-08-17 01:10:23 +02:00
}
2012-03-12 02:12:58 +01:00
}
}
2012-12-07 05:15:27 +01:00
void map_parser : : find_unused_nodes_recursive ( xml_node const & node , std : : string & error_message )
2012-03-12 02:12:58 +01:00
{
if ( ! node . processed ( ) )
{
2013-12-09 22:15:24 +01:00
if ( node . is_text ( ) )
{
2012-12-07 05:15:27 +01:00
error_message + = " \n * text ' " + node . text ( ) + " ' " ;
2013-12-09 22:15:24 +01:00
}
else
{
2012-12-07 05:15:27 +01:00
error_message + = " \n * node ' " + node . name ( ) + " ' at line " + node . line_to_string ( ) ;
2011-12-01 01:48:01 +01:00
}
2012-03-12 02:12:58 +01:00
return ; //All attributes and children are automatically unprocessed, too.
}
2013-12-09 22:15:24 +01:00
xml_node : : attribute_map const & attrs = node . get_attributes ( ) ;
for ( auto const & attr : attrs )
2012-03-12 02:12:58 +01:00
{
2013-12-09 22:15:24 +01:00
if ( ! attr . second . processed )
2012-03-12 02:12:58 +01:00
{
2013-12-09 22:15:24 +01:00
error_message + = " \n * attribute ' " + attr . first +
" ' with value ' " + attr . second . value +
2012-12-07 05:15:27 +01:00
" ' at line " + node . line_to_string ( ) ;
2011-12-01 01:48:01 +01:00
}
}
2013-12-09 22:15:24 +01:00
for ( auto const & child_node : node )
2012-03-12 02:12:58 +01:00
{
2013-12-09 22:15:24 +01:00
find_unused_nodes_recursive ( child_node , error_message ) ;
2012-03-12 02:12:58 +01:00
}
2011-02-05 04:15:17 +01:00
}
2007-09-25 20:47:12 +02:00
} // end of namespace mapnik