mapnik/src/load_map.cpp

1551 lines
50 KiB
C++
Raw Normal View History

/*****************************************************************************
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
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2011 Artem Pavlenko
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*****************************************************************************/
// mapnik
2007-10-08 19:42:41 +02:00
#include <mapnik/load_map.hpp>
#include <mapnik/xml_tree.hpp>
#include <mapnik/version.hpp>
#include <mapnik/image_reader.hpp>
#include <mapnik/color.hpp>
#include <mapnik/color_factory.hpp>
2011-08-29 23:07:45 +02:00
#include <mapnik/symbolizer.hpp>
#include <mapnik/feature_type_style.hpp>
2009-12-16 21:02:06 +01:00
#include <mapnik/layer.hpp>
#include <mapnik/datasource_cache.hpp>
#include <mapnik/font_engine_freetype.hpp>
#include <mapnik/font_set.hpp>
2012-03-13 09:02:53 +01:00
#include <mapnik/xml_loader.hpp>
2007-10-08 19:42:41 +02:00
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>
#include <mapnik/raster_colorizer.hpp>
2009-12-16 21:02:06 +01:00
#include <mapnik/svg/svg_path_parser.hpp>
#include <mapnik/metawriter_factory.hpp>
2012-02-18 00:39:14 +01:00
#include <mapnik/text_placements/registry.hpp>
2012-02-12 03:55:13 +01:00
#include <mapnik/text_placements/dummy.hpp>
#include <mapnik/symbolizer.hpp>
#include <mapnik/rule.hpp>
2012-03-12 01:09:26 +01:00
#include <mapnik/config_error.hpp>
2007-10-08 19:42:41 +02:00
// boost
#include <boost/optional.hpp>
#include <boost/algorithm/string.hpp>
2012-02-23 18:09:02 +01:00
#include <boost/algorithm/string/trim.hpp>
2007-10-08 19:42:41 +02:00
#include <boost/lexical_cast.hpp>
#include <boost/tokenizer.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>
#include <boost/static_assert.hpp>
#include <boost/filesystem/operations.hpp>
// agg
#include "agg_trans_affine.h"
2007-10-08 19:42:41 +02:00
// stl
#include <iostream>
2012-03-12 02:12:58 +01:00
#include <sstream>
using boost::lexical_cast;
using boost::bad_lexical_cast;
using boost::tokenizer;
using std::endl;
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
{
using boost::optional;
class map_parser : boost::noncopyable {
public:
2010-05-27 12:20:10 +02:00
map_parser( bool strict, std::string const& filename = "" ) :
2010-06-02 13:03:30 +02:00
strict_( strict ),
filename_( filename ),
relative_to_xml_(true),
2012-03-11 23:24:28 +01:00
font_manager_(font_engine_)
2012-03-13 15:56:11 +01:00
{}
void parse_map(Map & map, xml_node const& sty, std::string const& base_path);
private:
2012-03-06 15:47:08 +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);
void parse_metawriter(Map & map, xml_node const& lay);
2012-03-07 01:35:37 +01:00
void parse_metawriter_in_symbolizer(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);
void 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);
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);
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 );
2012-03-11 23:24:28 +01:00
void parse_raster_colorizer(raster_colorizer_ptr const& rc, xml_node const& node);
2012-03-07 01:35:37 +01:00
void parse_stroke(stroke & strk, xml_node const & sym);
2010-05-27 12:20:10 +02: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);
void find_unused_nodes_recursive(xml_node const& node, std::stringstream &error_text);
2010-05-27 12:20:10 +02:00
2012-03-11 23:24:28 +01:00
std::string ensure_relative_to_xml(boost::optional<std::string> opt_path);
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_;
};
2012-03-08 01:29:19 +01:00
#include <mapnik/internal/dump_xml.hpp>
void load_map(Map & map, std::string const& filename, bool strict)
{
2012-03-11 23:24:28 +01:00
// TODO - use xml encoding?
xml_tree tree("utf8");
tree.set_filename(filename);
2012-03-13 09:02:53 +01:00
read_xml(filename, tree.root());
map_parser parser(strict, filename);
parser.parse_map(map, tree.root(), "");
#ifdef MAPNIK_DEBUG
2012-03-08 01:29:19 +01:00
dump_xml(tree.root());
#endif
}
void load_map_string(Map & map, std::string const& str, bool strict, std::string base_path)
{
2012-03-11 23:24:28 +01:00
// TODO - use xml encoding?
xml_tree tree("utf8");
if (!base_path.empty())
2012-03-13 09:02:53 +01:00
read_xml_string(str, tree.root(), base_path); // accept base_path passed into function
else
2012-03-13 09:02:53 +01:00
read_xml_string(str, tree.root(), map.base_path()); // default to map base_path
map_parser parser(strict, base_path);
2012-03-07 01:35:37 +01:00
parser.parse_map(map, tree.root(), base_path);
}
void map_parser::parse_map(Map & map, xml_node const& pt, std::string const& base_path)
{
2010-05-27 12:20:10 +02:00
try
{
xml_node const& map_node = pt.get_child("Map");
2010-06-02 13:03:30 +02:00
try
{
parameters extra_attr;
// 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
optional<boolean> paths_from_xml = map_node.get_opt_attr<boolean>("paths-from-xml");
if (paths_from_xml)
{
relative_to_xml_ = *paths_from_xml;
}
optional<std::string> base_path_from_xml = map_node.get_opt_attr<std::string>("base");
if (!base_path.empty())
{
map.set_base_path(base_path);
}
else if (base_path_from_xml)
{
map.set_base_path(*base_path_from_xml);
}
else
{
boost::filesystem::path xml_path(filename_);
// TODO - should we make this absolute?
2012-02-02 02:53:35 +01:00
#if (BOOST_FILESYSTEM_VERSION == 3)
std::string base = xml_path.parent_path().string();
#else // v2
std::string base = xml_path.branch_path().string();
#endif
map.set_base_path(base);
}
optional<color> bgcolor = map_node.get_opt_attr<color>("background-color");
2012-02-02 02:53:35 +01:00
if (bgcolor)
{
map.set_background(*bgcolor);
2010-06-02 13:03:30 +02:00
}
2012-02-02 02:53:35 +01:00
optional<std::string> image_filename = map_node.get_opt_attr<std::string>("background-image");
if (image_filename)
2012-02-02 02:53:35 +01:00
{
map.set_background_image(ensure_relative_to_xml(image_filename));
}
2012-02-02 02:53:35 +01:00
map.set_srs(map_node.get_attr("srs", map.srs()));
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);
}
optional<std::string> maximum_extent = map_node.get_opt_attr<std::string>("maximum-extent");
if (maximum_extent)
{
box2d<double> box;
if (box.from_string(*maximum_extent))
{
map.set_maximum_extent(box);
}
else
{
std::ostringstream s_err;
s_err << "failed to parse 'maximum-extent'";
if (strict_)
throw config_error(s_err.str());
else
std::clog << "### WARNING: " << s_err.str() << std::endl;
}
}
optional<std::string> font_directory = map_node.get_opt_attr<std::string>("font-directory");
if (font_directory)
{
extra_attr["font-directory"] = *font_directory;
freetype_engine::register_fonts(ensure_relative_to_xml(font_directory), false);
}
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)
{
extra_attr["minimum-version"] = *min_version_string;
2010-06-02 13:03:30 +02:00
boost::char_separator<char> sep(".");
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];
for (boost::tokenizer<boost::char_separator<char> >::iterator beg = tokens.begin();
beg != tokens.end(); ++beg)
2010-06-02 13:03:30 +02:00
{
2012-02-02 02:53:35 +01:00
try
2010-06-02 13:03:30 +02:00
{
n[i] = boost::lexical_cast<int>(boost::trim_copy(*beg));
}
catch (boost::bad_lexical_cast & ex)
{
std::clog << *beg << " : " << ex.what() << "\n";
break;
}
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
}
map.set_extra_attributes(extra_attr);
2010-06-02 13:03:30 +02:00
}
catch (const config_error & ex)
{
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);
}
catch (node_not_found const&)
{
throw config_error("Not a map file. Node 'Map' not found.");
}
2012-03-12 02:12:58 +01:00
find_unused_nodes(pt);
}
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)
{
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)
{
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("MetaWriter"))
{
parse_metawriter(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)
{
2012-03-13 15:56:11 +01:00
if (paramIter->is("Parameter"))
{
std::string name = paramIter->get_attr<std::string>("name");
std::string value = paramIter->get_text();
params[name] = value;
}
}
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"))
{
2012-03-13 15:56:11 +01:00
std::string name = itr->get_attr("name", std::string("Unnamed"));
parameters & params = map.get_extra_parameters();
xml_node::const_iterator paramIter = itr->begin();
xml_node::const_iterator endParam = itr->end();
for (; paramIter != endParam; ++paramIter)
{
2012-03-13 15:56:11 +01:00
if (paramIter->is("Parameter"))
{
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)
{
2012-03-13 15:56:11 +01:00
if (*type == "int")
{
is_string = false;
int value = paramIter->get_value<int>();
params[name] = value;
}
else if (*type == "float")
{
is_string = false;
double value = paramIter->get_value<double>();
params[name] = value;
}
}
2012-03-13 15:56:11 +01:00
if (is_string)
{
2012-03-13 15:56:11 +01:00
std::string value = paramIter->get_text();
params[name] = value;
}
}
}
}
}
2012-03-06 17:29:33 +01:00
} catch (const config_error & ex) {
2012-03-12 01:09:26 +01:00
ex.append_context(include);
2012-03-06 17:29:33 +01:00
throw;
}
map.init_metawriters();
}
2012-03-06 17:29:33 +01:00
void map_parser::parse_style(Map & map, xml_node const& sty)
{
2011-06-24 02:53:00 +02:00
std::string name("<missing name>");
try
{
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);
style.set_filter_mode(filter_mode);
2012-03-06 17:29:33 +01:00
xml_node::const_iterator ruleIter = sty.begin();
xml_node::const_iterator endRule = sty.end();
2010-06-02 13:03:30 +02:00
for (; ruleIter!=endRule; ++ruleIter)
{
2012-03-06 17:29:33 +01:00
if (ruleIter->is("Rule"))
2010-06-02 13:03:30 +02:00
{
2012-03-07 01:35:37 +01:00
parse_rule(style, *ruleIter);
2010-06-02 13:03:30 +02:00
}
}
map.insert_style(name, style);
} catch (const config_error & ex) {
2012-03-12 01:09:26 +01:00
ex.append_context(std::string("in style '") + name + "'", sty);
throw;
}
}
2012-03-06 17:29:33 +01:00
void map_parser::parse_metawriter(Map & map, xml_node const& pt)
{
2011-06-24 02:53:00 +02:00
std::string name("<missing name>");
metawriter_ptr writer;
try
{
2012-03-06 17:29:33 +01:00
name = pt.get_attr<std::string>("name");
writer = metawriter_create(pt);
map.insert_metawriter(name, writer);
} catch (const config_error & ex) {
2012-03-12 01:09:26 +01:00
ex.append_context(std::string("in meta writer '") + name + "'", pt);
}
}
2012-03-06 17:29:33 +01:00
void map_parser::parse_fontset(Map & map, xml_node const& fset)
{
2011-06-24 02:53:00 +02:00
std::string name("<missing name>");
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
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-03-06 17:29:33 +01:00
parse_font(fontset, *itr);
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
fontsets_.insert(pair<std::string, font_set>(name, fontset));
} catch (const config_error & ex) {
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;
}
}
2012-03-06 17:29:33 +01:00
void map_parser::parse_font(font_set &fset, xml_node const& f)
{
2012-03-06 17:29:33 +01:00
optional<std::string> face_name = f.get_opt_attr<std::string>("face-name");
if (face_name)
{
if ( strict_ )
{
ensure_font_face(*face_name);
}
fset.add_face_name(*face_name);
}
else
{
2012-03-12 01:09:26 +01:00
throw config_error("Must have 'face-name' set", f);
}
}
2012-03-06 17:29:33 +01:00
void map_parser::parse_layer(Map & map, xml_node const& lay)
{
std::string name;
try
{
2012-03-06 17:29:33 +01:00
name = lay.get_attr("name", std::string("Unnamed"));
2010-06-02 13:03:30 +02:00
// XXX if no projection is given inherit from map? [DS]
2012-03-06 17:29:33 +01:00
std::string srs = lay.get_attr("srs", map.srs());
2010-06-02 13:03:30 +02:00
layer lyr(name, srs);
2012-03-06 17:29:33 +01:00
optional<boolean> status = lay.get_opt_attr<boolean>("status");
2010-06-02 13:03:30 +02:00
if (status)
{
lyr.set_active(* status );
2010-06-02 13:03:30 +02:00
}
2012-02-02 02:53:35 +01:00
optional<double> min_zoom = lay.get_opt_attr<double>("minzoom");
if (min_zoom)
2010-06-02 13:03:30 +02:00
{
lyr.set_min_zoom( * min_zoom );
2010-06-02 13:03:30 +02:00
}
optional<double> max_zoom = lay.get_opt_attr<double>("maxzoom");
if (max_zoom)
2010-06-02 13:03:30 +02:00
{
lyr.set_max_zoom( * max_zoom );
2010-06-02 13:03:30 +02:00
}
2012-03-06 17:29:33 +01:00
optional<boolean> queryable = lay.get_opt_attr<boolean>("queryable");
2010-06-02 13:03:30 +02:00
if (queryable)
{
lyr.set_queryable( * queryable );
2010-06-02 13:03:30 +02:00
}
optional<boolean> clear_cache =
2012-03-06 17:29:33 +01:00
lay.get_opt_attr<boolean>("clear-label-cache");
2010-06-02 13:03:30 +02:00
if (clear_cache)
{
lyr.set_clear_label_cache( * clear_cache );
}
optional<boolean> cache_features =
2012-03-06 17:29:33 +01:00
lay.get_opt_attr<boolean>("cache-features");
if (cache_features)
{
lyr.set_cache_features( * cache_features );
}
2011-10-13 01:30:18 +02:00
optional<std::string> group_by =
2012-03-06 17:29:33 +01:00
lay.get_opt_attr<std::string>("group-by");
2011-10-13 01:30:18 +02:00
if (group_by)
{
lyr.set_group_by( * group_by );
}
2010-06-02 13:03:30 +02:00
2012-03-06 17:29:33 +01:00
xml_node::const_iterator child = lay.begin();
xml_node::const_iterator end = lay.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();
if (style_name.empty())
{
std::ostringstream ss;
ss << "StyleName is empty in Layer: '" << lyr.name() << "'";
if (strict_)
throw config_error(ss.str());
else
std::clog << "### WARNING: " << ss.str() << std::endl;
}
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");
2010-06-02 13:03:30 +02:00
if( base )
{
std::map<std::string,parameters>::const_iterator base_itr = datasource_templates_.find(*base);
if (base_itr!=datasource_templates_.end())
params = base_itr->second;
}
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
{
2012-03-06 17:29:33 +01:00
std::string name = paramIter->get_attr<std::string>("name");
std::string value = paramIter->get_text();
2010-06-02 13:03:30 +02:00
params[name] = value;
}
}
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
if (base_param){
params["base"] = ensure_relative_to_xml(base_param);
2010-06-02 13:03:30 +02:00
}
else if (file_param){
params["file"] = ensure_relative_to_xml(file_param);
2010-06-02 13:03:30 +02:00
}
2010-06-02 13:03:30 +02:00
//now we are ready to create datasource
try
{
boost::shared_ptr<datasource> ds =
datasource_cache::instance()->create(params);
lyr.set_datasource(ds);
}
catch (const std::exception & ex )
2010-06-02 13:03:30 +02:00
{
throw config_error( ex.what() );
}
catch (...)
{
throw config_error("Unknown exception occured attempting to create datasoure for layer '" + lyr.name() + "'");
2010-06-02 13:03:30 +02:00
}
}
}
map.addLayer(lyr);
2012-02-02 02:53:35 +01:00
}
catch (const config_error & ex)
{
2012-03-06 17:29:33 +01:00
if (!name.empty())
{
2012-03-12 01:09:26 +01:00
ex.append_context(std::string(" encountered during parsing of layer '") + name + "'", lay);
2010-06-02 13:03:30 +02:00
}
throw;
}
}
2012-03-07 01:35:37 +01:00
void map_parser::parse_rule(feature_type_style & style, xml_node const& r)
{
std::string name;
try
{
2012-03-07 01:35:37 +01:00
name = r.get_attr("name", std::string());
rule rule(name);
2010-06-02 13:03:30 +02:00
2012-03-07 03:57:31 +01:00
xml_node const* child = r.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
}
2012-03-07 01:35:37 +01:00
if (r.has_child("ElseFilter"))
2010-06-02 13:03:30 +02:00
{
rule.set_else(true);
}
2012-03-07 01:35:37 +01:00
if (r.has_child("AlsoFilter"))
{
rule.set_also(true);
}
2012-03-07 01:35:37 +01:00
child = r.get_opt_child("MinScaleDenominator");
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
}
2012-03-07 01:35:37 +01:00
child = r.get_opt_child("MaxScaleDenominator");
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
}
2012-03-07 01:35:37 +01:00
xml_node::const_iterator symIter = r.begin();
xml_node::const_iterator endSym = r.end();
2010-06-02 13:03:30 +02:00
for( ;symIter != endSym; ++symIter)
{
2012-03-07 01:35:37 +01:00
if ( symIter->is("PointSymbolizer"))
2010-06-02 13:03:30 +02:00
{
2012-03-07 01:35:37 +01:00
parse_point_symbolizer(rule, *symIter);
2010-06-02 13:03:30 +02:00
}
2012-03-07 01:35:37 +01:00
else if (symIter->is("LinePatternSymbolizer"))
2010-06-02 13:03:30 +02:00
{
2012-03-07 01:35:37 +01:00
parse_line_pattern_symbolizer(rule, *symIter);
2010-06-02 13:03:30 +02:00
}
2012-03-07 01:35:37 +01:00
else if (symIter->is("PolygonPatternSymbolizer"))
2010-06-02 13:03:30 +02:00
{
2012-03-07 01:35:37 +01:00
parse_polygon_pattern_symbolizer(rule, *symIter);
2010-06-02 13:03:30 +02:00
}
2012-03-07 01:35:37 +01:00
else if (symIter->is("TextSymbolizer"))
2010-06-02 13:03:30 +02:00
{
2012-03-07 01:35:37 +01:00
parse_text_symbolizer(rule, *symIter);
2010-06-02 13:03:30 +02:00
}
2012-03-07 01:35:37 +01:00
else if (symIter->is("ShieldSymbolizer"))
2010-06-02 13:03:30 +02:00
{
2012-03-07 01:35:37 +01:00
parse_shield_symbolizer(rule, *symIter);
2010-06-02 13:03:30 +02:00
}
2012-03-07 01:35:37 +01:00
else if (symIter->is("LineSymbolizer"))
2010-06-02 13:03:30 +02:00
{
2012-03-07 01:35:37 +01:00
parse_line_symbolizer(rule, *symIter);
2010-06-02 13:03:30 +02:00
}
2012-03-07 01:35:37 +01:00
else if (symIter->is("PolygonSymbolizer"))
2010-06-02 13:03:30 +02:00
{
2012-03-07 01:35:37 +01:00
parse_polygon_symbolizer(rule, *symIter);
2010-06-02 13:03:30 +02:00
}
2012-03-07 01:35:37 +01:00
else if (symIter->is("BuildingSymbolizer"))
2010-06-02 13:03:30 +02:00
{
2012-03-07 01:35:37 +01:00
parse_building_symbolizer(rule, *symIter);
2010-06-02 13:03:30 +02:00
}
2012-03-07 01:35:37 +01:00
else if (symIter->is("RasterSymbolizer"))
2010-06-02 13:03:30 +02:00
{
2012-03-07 01:35:37 +01:00
parse_raster_symbolizer( rule, *symIter);
2010-06-02 13:03:30 +02:00
}
2012-03-07 01:35:37 +01:00
else if (symIter->is("MarkersSymbolizer"))
2010-06-02 13:03:30 +02:00
{
2012-03-07 01:35:37 +01:00
parse_markers_symbolizer(rule, *symIter);
2010-06-02 13:03:30 +02:00
}
}
style.add_rule(rule);
}
catch (const config_error & ex)
{
2012-03-07 01:35:37 +01:00
if (!name.empty() )
2010-06-02 13:03:30 +02:00
{
2012-03-12 01:09:26 +01:00
ex.append_context(std::string("in rule '") + name + "'", r);
2010-06-02 13:03:30 +02:00
}
throw;
}
}
2012-03-07 01:35:37 +01:00
void map_parser::parse_metawriter_in_symbolizer(symbolizer_base &sym, xml_node const &pt)
{
2012-03-07 01:35:37 +01:00
optional<std::string> writer = pt.get_opt_attr<std::string>("meta-writer");
if (!writer) return;
2012-03-07 01:35:37 +01:00
optional<std::string> output = pt.get_opt_attr<std::string>("meta-output");
sym.add_metawriter(*writer, output);
}
2012-03-07 01:35:37 +01:00
void map_parser::parse_point_symbolizer(rule & rule, xml_node const & sym)
{
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");
optional<boolean> allow_overlap = sym.get_opt_attr<boolean>("allow-overlap");
optional<boolean> ignore_placement = sym.get_opt_attr<boolean>("ignore-placement");
optional<float> opacity = sym.get_opt_attr<float>("opacity");
optional<std::string> transform_wkt = sym.get_opt_attr<std::string>("transform");
point_symbolizer symbol;
if (allow_overlap)
{
symbol.set_allow_overlap( * allow_overlap );
}
if (opacity)
{
symbol.set_opacity( * opacity );
}
if (ignore_placement)
{
symbol.set_ignore_placement( * ignore_placement );
}
point_placement_e placement =
sym.get_attr<point_placement_e>("placement", CENTROID_POINT_PLACEMENT);
symbol.set_point_placement( placement );
2010-06-02 13:03:30 +02:00
if (file)
{
try
{
if( base )
{
std::map<std::string,std::string>::const_iterator itr = file_sources_.find(*base);
if (itr!=file_sources_.end())
{
*file = itr->second + "/" + *file;
}
}
*file = ensure_relative_to_xml(file);
2012-02-02 02:53:35 +01:00
2012-03-07 01:35:37 +01:00
symbol.set_filename(parse_path(*file));
2010-06-02 13:03:30 +02:00
if (transform_wkt)
{
agg::trans_affine tr;
if (!mapnik::svg::parse_transform((*transform_wkt).c_str(),tr))
2010-12-11 02:10:45 +01:00
{
std::stringstream ss;
2012-02-02 02:53:35 +01:00
ss << "Could not parse transform from '" << transform_wkt
<< "', expected string like: 'matrix(1, 0, 0, 1, 0, 0)'";
2010-12-11 02:10:45 +01:00
if (strict_)
throw config_error(ss.str()); // value_error here?
else
2012-02-02 02:53:35 +01:00
std::clog << "### WARNING: " << ss << endl;
2010-12-11 02:10:45 +01:00
}
2010-06-02 13:03:30 +02:00
boost::array<double,6> matrix;
tr.store_to(&matrix[0]);
symbol.set_transform(matrix);
}
}
catch (image_reader_exception const & ex )
{
2011-06-24 02:53:00 +02:00
std::string msg("Failed to load image file '" + * file +
2012-02-02 02:53:35 +01:00
"': " + ex.what());
2010-06-02 13:03:30 +02:00
if (strict_)
{
throw config_error(msg);
}
else
{
2011-06-24 02:53:00 +02:00
std::clog << "### WARNING: " << msg << endl;
2010-06-02 13:03:30 +02:00
}
}
}
2012-03-07 01:35:37 +01:00
parse_metawriter_in_symbolizer(symbol, sym);
rule.append(symbol);
}
catch (const config_error & ex)
{
2012-03-12 01:09:26 +01:00
ex.append_context("in PointSymbolizer", sym);
2010-06-02 13:03:30 +02:00
throw;
}
}
2010-05-27 16:21:31 +02:00
2012-03-07 01:35:37 +01:00
void map_parser::parse_markers_symbolizer(rule & rule, xml_node const& sym)
2010-05-27 16:21:31 +02:00
{
try
{
std::string filename("");
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");
optional<std::string> transform_wkt = sym.get_opt_attr<std::string>("transform");
if (file)
{
try
{
if (base)
{
std::map<std::string,std::string>::const_iterator itr = file_sources_.find(*base);
if (itr!=file_sources_.end())
{
*file = itr->second + "/" + *file;
}
}
filename = ensure_relative_to_xml(file);
}
catch (...)
{
2011-06-24 02:53:00 +02:00
std::string msg("Failed to load marker file '" + *file + "'!");
if (strict_)
{
throw config_error(msg);
}
else
{
2011-06-24 02:53:00 +02:00
std::clog << "### WARNING: " << msg << endl;
}
}
}
2010-05-27 16:21:31 +02:00
markers_symbolizer symbol(parse_path(filename));
2012-03-07 01:35:37 +01:00
optional<float> opacity = sym.get_opt_attr<float>("opacity");
2010-06-15 15:36:33 +02:00
if (opacity) symbol.set_opacity( *opacity );
2012-02-02 02:53:35 +01:00
if (transform_wkt)
{
agg::trans_affine tr;
if (!mapnik::svg::parse_transform((*transform_wkt).c_str(),tr))
2010-12-11 02:10:45 +01:00
{
std::stringstream ss;
ss << "Could not parse transform from '" << transform_wkt
<< "', expected string like: 'matrix(1, 0, 0, 1, 0, 0)'";
2010-12-11 02:10:45 +01:00
if (strict_)
throw config_error(ss.str()); // value_error here?
else
2012-02-02 02:53:35 +01:00
std::clog << "### WARNING: " << ss << endl;
2010-12-11 02:10:45 +01:00
}
boost::array<double,6> matrix;
tr.store_to(&matrix[0]);
symbol.set_transform(matrix);
}
2012-02-02 02:53:35 +01:00
2012-03-07 01:35:37 +01:00
optional<color> c = sym.get_opt_attr<color>("fill");
if (c) symbol.set_fill(*c);
2012-03-07 01:35:37 +01:00
optional<double> spacing = sym.get_opt_attr<double>("spacing");
if (spacing) symbol.set_spacing(*spacing);
2012-03-07 01:35:37 +01:00
optional<double> max_error = sym.get_opt_attr<double>("max-error");
if (max_error) symbol.set_max_error(*max_error);
2012-03-07 01:35:37 +01:00
optional<boolean> allow_overlap = sym.get_opt_attr<boolean>("allow-overlap");
if (allow_overlap) symbol.set_allow_overlap(*allow_overlap);
2012-03-07 01:35:37 +01:00
optional<double> w = sym.get_opt_attr<double>("width");
optional<double> h = sym.get_opt_attr<double>("height");
if (w && h)
{
symbol.set_width(*w);
symbol.set_height(*h);
}
else if (w)
{
symbol.set_width(*w);
symbol.set_height(*w);
2012-02-02 02:53:35 +01:00
}
else if (h)
{
symbol.set_width(*h);
symbol.set_height(*h);
}
2012-02-02 02:53:35 +01:00
stroke strk;
parse_stroke(strk,sym);
symbol.set_stroke(strk);
2012-03-07 01:35:37 +01:00
marker_placement_e placement = sym.get_attr<marker_placement_e>("placement", MARKER_LINE_PLACEMENT);
symbol.set_marker_placement( placement );
marker_type_e dfl_marker_type = ARROW;
if (placement == MARKER_POINT_PLACEMENT)
dfl_marker_type = ELLIPSE;
2012-03-07 01:35:37 +01:00
marker_type_e marker_type = sym.get_attr<marker_type_e>("marker-type", dfl_marker_type);
symbol.set_marker_type( marker_type );
parse_metawriter_in_symbolizer(symbol, sym);
rule.append(symbol);
2010-05-27 16:21:31 +02:00
}
catch (const config_error & ex)
{
2012-03-12 01:09:26 +01:00
ex.append_context("in MarkersSymbolizer", sym);
throw;
2010-05-27 16:21:31 +02:00
}
}
2012-03-07 01:35:37 +01:00
void map_parser::parse_line_pattern_symbolizer( rule & rule, xml_node const & sym )
{
try
{
2012-03-07 01:35:37 +01:00
std::string file = sym.get_attr<std::string>("file");
optional<std::string> base = sym.get_opt_attr<std::string>("base");
2012-02-02 02:53:35 +01:00
2010-06-02 13:03:30 +02:00
try
{
if( base )
{
std::map<std::string,std::string>::const_iterator itr = file_sources_.find(*base);
if (itr!=file_sources_.end())
{
file = itr->second + "/" + file;
}
}
file = ensure_relative_to_xml(file);
2010-06-02 13:03:30 +02:00
line_pattern_symbolizer symbol(parse_path(file));
parse_metawriter_in_symbolizer(symbol, sym);
2010-06-02 13:03:30 +02:00
rule.append(symbol);
}
catch (image_reader_exception const & ex )
{
2011-06-24 02:53:00 +02:00
std::string msg("Failed to load image file '" + file +
2012-02-02 02:53:35 +01:00
"': " + ex.what());
2010-06-02 13:03:30 +02:00
if (strict_)
{
throw config_error(msg);
}
else
{
2011-06-24 02:53:00 +02:00
std::clog << "### WARNING: " << msg << endl;
2010-06-02 13:03:30 +02:00
}
}
}
catch (const config_error & ex)
{
2012-03-12 01:09:26 +01:00
ex.append_context("in LinePatternSymbolizer", sym);
2010-06-02 13:03:30 +02:00
throw;
}
}
void map_parser::parse_polygon_pattern_symbolizer( rule & rule,
2012-03-07 01:35:37 +01:00
xml_node const & sym )
{
try
{
2012-03-07 01:35:37 +01:00
std::string file = sym.get_attr<std::string>("file");
optional<std::string> base = sym.get_opt_attr<std::string>("base");
2012-02-02 02:53:35 +01:00
2010-06-02 13:03:30 +02:00
try
{
if( base )
{
std::map<std::string,std::string>::iterator itr = file_sources_.find(*base);
if (itr!=file_sources_.end())
{
file = itr->second + "/" + file;
}
}
file = ensure_relative_to_xml(file);
2010-06-02 13:03:30 +02:00
polygon_pattern_symbolizer symbol(parse_path(file));
// pattern alignment
2012-03-07 01:35:37 +01:00
pattern_alignment_e p_alignment = sym.get_attr<pattern_alignment_e>("alignment",LOCAL_ALIGNMENT);
symbol.set_alignment(p_alignment);
// gamma
2012-03-07 01:35:37 +01:00
optional<double> gamma = sym.get_opt_attr<double>("gamma");
if (gamma) symbol.set_gamma(*gamma);
// gamma method
2012-03-07 01:35:37 +01:00
optional<gamma_method_e> gamma_method = sym.get_opt_attr<gamma_method_e>("gamma-method");
if (gamma_method) symbol.set_gamma_method(*gamma_method);
parse_metawriter_in_symbolizer(symbol, sym);
2010-06-02 13:03:30 +02:00
rule.append(symbol);
}
catch (image_reader_exception const & ex )
{
2011-06-24 02:53:00 +02:00
std::string msg("Failed to load image file '" + file +
2012-02-02 02:53:35 +01:00
"': " + ex.what());
2010-06-02 13:03:30 +02:00
if (strict_)
{
throw config_error(msg);
}
else
{
2011-06-24 02:53:00 +02:00
std::clog << "### WARNING: " << msg << endl;
2010-06-02 13:03:30 +02:00
}
}
}
catch (const config_error & ex)
{
2012-03-12 01:09:26 +01:00
ex.append_context("in PolygonPatternSymbolizer", sym);
2010-06-02 13:03:30 +02:00
throw;
}
}
2012-03-07 01:35:37 +01:00
void map_parser::parse_text_symbolizer( rule & rule, xml_node const& sym )
{
try
{
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");
if (placement_type) {
2012-02-18 00:39:14 +01:00
placement_finder = placements::registry::instance()->from_xml(*placement_type, sym, fontsets_);
} else {
2012-03-12 21:22:07 +01:00
placement_finder = boost::make_shared<text_placements_dummy>();
2012-02-18 00:39:14 +01:00
placement_finder->defaults.from_xml(sym, fontsets_);
}
if (strict_ &&
2012-03-13 15:56:11 +01:00
!placement_finder->defaults.format.fontset.size())
ensure_font_face(placement_finder->defaults.format.face_name);
text_symbolizer text_symbol = text_symbolizer(placement_finder);
parse_metawriter_in_symbolizer(text_symbol, sym);
2010-06-02 13:03:30 +02:00
rule.append(text_symbol);
}
catch (const config_error & ex)
{
2012-03-12 01:09:26 +01:00
ex.append_context("in TextSymbolizer", sym);
2010-06-02 13:03:30 +02:00
throw;
}
}
2012-03-07 01:35:37 +01:00
void map_parser::parse_shield_symbolizer(rule & rule, xml_node const& sym )
{
try
{
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");
if (placement_type) {
2012-02-18 00:39:14 +01:00
placement_finder = placements::registry::instance()->from_xml(*placement_type, sym, fontsets_);
} else {
2012-03-12 21:22:07 +01:00
placement_finder = boost::make_shared<text_placements_dummy>();
}
placement_finder->defaults.from_xml(sym, fontsets_);
if (strict_ &&
2012-03-13 15:56:11 +01:00
!placement_finder->defaults.format.fontset.size())
ensure_font_face(placement_finder->defaults.format.face_name);
shield_symbolizer shield_symbol = shield_symbolizer(placement_finder);
/* Symbolizer specific attributes. */
2012-03-07 01:35:37 +01:00
optional<std::string> transform_wkt = sym.get_opt_attr<std::string>("transform");
2012-01-22 04:21:01 +01:00
if (transform_wkt)
{
agg::trans_affine tr;
if (!mapnik::svg::parse_transform((*transform_wkt).c_str(),tr))
{
std::stringstream ss;
ss << "Could not parse transform from '" << transform_wkt << "', expected string like: 'matrix(1, 0, 0, 1, 0, 0)'";
if (strict_)
throw config_error(ss.str()); // value_error here?
else
std::clog << "### WARNING: " << ss << endl;
}
boost::array<double,6> matrix;
tr.store_to(&matrix[0]);
shield_symbol.set_transform(matrix);
}
// shield displacement
2012-03-07 01:35:37 +01:00
double shield_dx = sym.get_attr("shield-dx", 0.0);
double shield_dy = sym.get_attr("shield-dy", 0.0);
2012-01-22 04:21:01 +01:00
shield_symbol.set_shield_displacement(shield_dx,shield_dy);
// opacity
2012-03-07 01:35:37 +01:00
optional<double> opacity = sym.get_opt_attr<double>("opacity");
2012-01-22 04:21:01 +01:00
if (opacity)
{
shield_symbol.set_opacity(*opacity);
}
2010-06-02 13:03:30 +02:00
2012-01-22 04:21:01 +01:00
// text-opacity
// TODO: Could be problematic because it is named opacity in TextSymbolizer but opacity has a diffrent meaning here.
optional<double> text_opacity =
2012-03-07 01:35:37 +01:00
sym.get_opt_attr<double>("text-opacity");
2012-01-22 04:21:01 +01:00
if (text_opacity)
{
shield_symbol.set_text_opacity( * text_opacity );
}
2010-06-02 13:03:30 +02:00
2012-01-22 04:21:01 +01:00
// unlock_image
optional<boolean> unlock_image =
2012-03-07 01:35:37 +01:00
sym.get_opt_attr<boolean>("unlock-image");
2012-01-22 04:21:01 +01:00
if (unlock_image)
{
shield_symbol.set_unlock_image( * unlock_image );
}
2010-06-02 13:03:30 +02:00
2012-01-22 04:21:01 +01:00
parse_metawriter_in_symbolizer(shield_symbol, sym);
2010-06-02 13:03:30 +02:00
2012-03-07 01:35:37 +01:00
std::string image_file = sym.get_attr<std::string>("file");
optional<std::string> base = sym.get_opt_attr<std::string>("base");
2012-01-22 04:21:01 +01:00
2010-06-02 13:03:30 +02:00
try
{
if( base )
{
std::map<std::string,std::string>::const_iterator itr = file_sources_.find(*base);
if (itr!=file_sources_.end())
{
image_file = itr->second + "/" + image_file;
}
}
image_file = ensure_relative_to_xml(image_file);
2012-01-22 04:21:01 +01:00
shield_symbol.set_filename(parse_path(image_file));
2010-06-02 13:03:30 +02:00
}
catch (image_reader_exception const & ex )
{
2011-06-24 02:53:00 +02:00
std::string msg("Failed to load image file '" + image_file +
2012-02-02 02:53:35 +01:00
"': " + ex.what());
2010-06-02 13:03:30 +02:00
if (strict_)
{
throw config_error(msg);
}
else
{
2011-06-24 02:53:00 +02:00
std::clog << "### WARNING: " << msg << endl;
2010-06-02 13:03:30 +02:00
}
}
2012-01-22 04:21:01 +01:00
rule.append(shield_symbol);
}
catch (const config_error & ex)
{
2012-03-12 01:09:26 +01:00
ex.append_context("in ShieldSymbolizer", sym);
2010-06-02 13:03:30 +02:00
throw;
}
}
2012-03-07 01:35:37 +01:00
void map_parser::parse_stroke(stroke & strk, xml_node const & sym)
{
// stroke color
2012-03-07 01:35:37 +01:00
optional<color> c = sym.get_opt_attr<color>("stroke");
if (c) strk.set_color(*c);
// stroke-width
2012-03-07 01:35:37 +01:00
optional<double> width = sym.get_opt_attr<double>("stroke-width");
if (width) strk.set_width(*width);
// stroke-opacity
2012-03-07 01:35:37 +01:00
optional<double> opacity = sym.get_opt_attr<double>("stroke-opacity");
if (opacity) strk.set_opacity(*opacity);
// 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");
if (line_join) strk.set_line_join(*line_join);
// 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");
if (line_cap) strk.set_line_cap(*line_cap);
// stroke-gamma
2012-03-07 01:35:37 +01:00
optional<double> gamma = sym.get_opt_attr<double>("stroke-gamma");
if (gamma) strk.set_gamma(*gamma);
// 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");
if (gamma_method) strk.set_gamma_method(*gamma_method);
// stroke-dashoffset
2012-03-07 01:35:37 +01:00
optional<double> dash_offset = sym.get_opt_attr<double>("stroke-dashoffset");
if (dash_offset) strk.set_dash_offset(*dash_offset);
// 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)
{
tokenizer<> tok (*str);
std::vector<double> dash_array;
tokenizer<>::iterator itr = tok.begin();
for (; itr != tok.end(); ++itr)
2010-06-02 13:03:30 +02:00
{
try
2010-06-02 13:03:30 +02:00
{
double f = boost::lexical_cast<double>(*itr);
dash_array.push_back(f);
2010-06-02 13:03:30 +02:00
}
catch ( boost::bad_lexical_cast &)
2010-06-02 13:03:30 +02:00
{
throw config_error(std::string("Failed to parse dasharray ") +
"'. Expected a " +
"list of floats but got '" + (*str) + "'");
}
}
if (dash_array.size())
{
size_t size = dash_array.size();
if ( size % 2)
{
for (size_t i=0; i < size ;++i)
2010-06-02 13:03:30 +02:00
{
dash_array.push_back(dash_array[i]);
2010-06-02 13:03:30 +02:00
}
}
std::vector<double>::const_iterator pos = dash_array.begin();
while (pos != dash_array.end())
{
strk.add_dash(*pos,*(pos + 1));
pos +=2;
}
2010-06-02 13:03:30 +02:00
}
}
}
2012-03-07 01:35:37 +01:00
void map_parser::parse_line_symbolizer( rule & rule, xml_node const & sym )
{
try
{
stroke strk;
parse_stroke(strk,sym);
line_symbolizer symbol = line_symbolizer(strk);
// rasterizer method
2012-03-07 01:35:37 +01:00
line_rasterizer_e rasterizer = sym.get_attr<line_rasterizer_e>("rasterizer", RASTERIZER_FULL);
//optional<line_rasterizer_e> rasterizer_method = sym.get_opt_attr<line_rasterizer_e>("full");
symbol.set_rasterizer(rasterizer);
parse_metawriter_in_symbolizer(symbol, sym);
rule.append(symbol);
}
catch (const config_error & ex)
{
2012-03-12 01:09:26 +01:00
ex.append_context("in LineSymbolizer", sym);
2010-06-02 13:03:30 +02:00
throw;
}
}
2012-02-02 02:53:35 +01:00
2012-03-07 01:35:37 +01:00
void map_parser::parse_polygon_symbolizer( rule & rule, xml_node const & sym )
{
try
{
2010-06-02 13:03:30 +02:00
polygon_symbolizer poly_sym;
// fill
2012-03-07 01:35:37 +01:00
optional<color> fill = sym.get_opt_attr<color>("fill");
2010-06-02 13:03:30 +02:00
if (fill) poly_sym.set_fill(*fill);
// fill-opacity
2012-03-07 01:35:37 +01:00
optional<double> opacity = sym.get_opt_attr<double>("fill-opacity");
2010-06-02 13:03:30 +02:00
if (opacity) poly_sym.set_opacity(*opacity);
// gamma
2012-03-07 01:35:37 +01:00
optional<double> gamma = sym.get_opt_attr<double>("gamma");
2010-06-02 13:03:30 +02:00
if (gamma) poly_sym.set_gamma(*gamma);
// gamma method
2012-03-07 01:35:37 +01:00
optional<gamma_method_e> gamma_method = sym.get_opt_attr<gamma_method_e>("gamma-method");
if (gamma_method) poly_sym.set_gamma_method(*gamma_method);
// smooth value
optional<double> smooth = sym.get_opt_attr<double>("smooth");
if (smooth) poly_sym.set_smooth(*smooth);
parse_metawriter_in_symbolizer(poly_sym, sym);
2010-06-02 13:03:30 +02:00
rule.append(poly_sym);
}
catch (const config_error & ex)
{
2012-03-12 01:09:26 +01:00
ex.append_context("in PolygonSymbolizer", sym);
2010-06-02 13:03:30 +02:00
throw;
}
}
2012-03-07 01:35:37 +01:00
void map_parser::parse_building_symbolizer( rule & rule, xml_node const & sym )
{
2012-02-02 02:53:35 +01:00
try
{
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");
2010-06-02 13:03:30 +02:00
if (fill) building_sym.set_fill(*fill);
// fill-opacity
2012-03-07 01:35:37 +01:00
optional<double> opacity = sym.get_opt_attr<double>("fill-opacity");
2010-06-02 13:03:30 +02:00
if (opacity) building_sym.set_opacity(*opacity);
// height
2012-03-11 23:24:28 +01:00
optional<expression_ptr> height = sym.get_opt_attr<expression_ptr>("height");
if (height) building_sym.set_height(*height);
2010-06-02 13:03:30 +02:00
parse_metawriter_in_symbolizer(building_sym, sym);
2010-06-02 13:03:30 +02:00
rule.append(building_sym);
}
catch (const config_error & ex)
{
2012-03-12 01:09:26 +01:00
ex.append_context("in BuildingSymbolizer", 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
}
}
2012-03-07 01:35:37 +01:00
void map_parser::parse_raster_symbolizer( rule & rule, xml_node const & sym )
{
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");
2010-06-02 13:03:30 +02:00
if (mode) raster_sym.set_mode(*mode);
// scaling
2012-03-07 01:35:37 +01:00
optional<std::string> scaling = sym.get_opt_attr<std::string>("scaling");
2010-06-02 13:03:30 +02:00
if (scaling) raster_sym.set_scaling(*scaling);
// opacity
2012-03-07 01:35:37 +01:00
optional<double> opacity = sym.get_opt_attr<double>("opacity");
2010-06-02 13:03:30 +02:00
if (opacity) raster_sym.set_opacity(*opacity);
// filter factor
2012-03-07 01:35:37 +01:00
optional<double> filter_factor = sym.get_opt_attr<double>("filter-factor");
if (filter_factor) raster_sym.set_filter_factor(*filter_factor);
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");
2011-09-16 14:21:21 +02:00
if (mesh_size) raster_sym.set_mesh_size(*mesh_size);
2012-03-07 01:35:37 +01:00
xml_node::const_iterator cssIter = sym.begin();
xml_node::const_iterator endCss = sym.end();
2010-06-02 13:03:30 +02:00
for(; cssIter != endCss; ++cssIter)
{
2012-03-07 01:35:37 +01:00
if (cssIter->is("RasterColorizer"))
2010-06-02 13:03:30 +02:00
{
2012-03-10 01:20:50 +01:00
raster_colorizer_ptr colorizer = boost::make_shared<raster_colorizer>();
2010-06-02 13:03:30 +02:00
raster_sym.set_colorizer(colorizer);
2012-03-07 01:35:37 +01:00
parse_raster_colorizer(colorizer, *cssIter);
2010-06-02 13:03:30 +02:00
}
}
2010-08-12 02:11:01 +02:00
//Note: raster_symbolizer doesn't support metawriters
2010-06-02 13:03:30 +02:00
rule.append(raster_sym);
}
catch (const config_error & ex)
{
2012-03-12 01:09:26 +01:00
ex.append_context("in RasterSymbolizer", sym);
2010-06-02 13:03:30 +02:00
throw;
}
}
void map_parser::parse_raster_colorizer(raster_colorizer_ptr const& rc,
2012-03-12 01:09:26 +01:00
xml_node const& node)
{
try
{
// 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
if(default_mode == COLORIZER_INHERIT) {
throw config_error("RasterColorizer mode must not be INHERIT. ");
}
rc->set_default_mode( default_mode );
2010-06-02 13:03:30 +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
{
rc->set_default_color( *default_color );
}
2012-02-02 02:53:35 +01: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)
{
if(*eps < 0) {
throw config_error("RasterColorizer epsilon must be > 0. ");
}
rc->set_epsilon( *eps );
}
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();
float maximumValue = -std::numeric_limits<float>::max();
2010-06-02 13:03:30 +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
{
// colour is optional.
2012-03-07 01:35:37 +01:00
optional<color> stopcolor = stopIter->get_opt_attr<color>("color");
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);
// 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
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
if(value < maximumValue) {
throw config_error("stop tag values must be in ascending order");
2010-06-02 13:03:30 +02:00
}
maximumValue = *value;
optional<std::string> label =
2012-03-13 15:56:11 +01:00
stopIter->get_opt_attr<std::string>("label");
//append the stop
colorizer_stop tmpStop;
tmpStop.set_color(*stopcolor);
tmpStop.set_mode(mode);
tmpStop.set_value(*value);
if (label)
tmpStop.set_label(*label);
2012-02-02 02:53:35 +01:00
rc->add_stop(tmpStop);
2010-06-02 13:03:30 +02:00
}
}
}
catch (const config_error & ex)
{
2012-03-12 01:09:26 +01:00
ex.append_context("in RasterColorizer", node);
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
void map_parser::ensure_font_face( std::string const& face_name )
{
if ( ! font_manager_.get_face( face_name ) )
{
2010-06-02 13:03:30 +02:00
throw config_error("Failed to find font face '" +
face_name + "'");
}
}
std::string map_parser::ensure_relative_to_xml( boost::optional<std::string> opt_path )
{
if (relative_to_xml_)
{
boost::filesystem::path xml_path = filename_;
boost::filesystem::path rel_path = *opt_path;
2012-02-02 02:53:35 +01:00
if ( !rel_path.has_root_path() )
{
2012-02-02 02:53:35 +01:00
#if (BOOST_FILESYSTEM_VERSION == 3)
// TODO - normalize is now deprecated, use make_preferred?
boost::filesystem::path full = boost::filesystem::absolute(xml_path.parent_path()/rel_path);
2012-02-02 02:53:35 +01:00
#else // v2
boost::filesystem::path full = boost::filesystem::complete(xml_path.branch_path()/rel_path).normalize();
2012-02-02 02:53:35 +01:00
#endif
#ifdef MAPNIK_DEBUG
std::clog << "\nModifying relative paths to be relative to xml...\n";
std::clog << "original base path: " << *opt_path << "\n";
std::clog << "relative base path: " << full.string() << "\n";
2012-02-02 02:53:35 +01:00
#endif
return full.string();
}
}
return *opt_path;
}
2012-03-12 02:12:58 +01:00
void map_parser::find_unused_nodes(xml_node const& root)
{
2012-03-12 02:12:58 +01:00
std::stringstream error_message;
find_unused_nodes_recursive(root, error_message);
if (!error_message.str().empty())
{
2012-03-12 02:12:58 +01:00
throw config_error("The following nodes or attributes were not processed while parsing the xml file:" + error_message.str());
}
}
void map_parser::find_unused_nodes_recursive(xml_node const& node, std::stringstream &error_message)
{
if (!node.processed())
{
if (node.is_text()) {
error_message << "\n* text '" << node.text() << "'";
} else {
error_message << "\n* node '" << node.name() << "' in line " << node.line();
}
2012-03-12 02:12:58 +01:00
return; //All attributes and children are automatically unprocessed, too.
}
xml_node::attribute_map const& attr = node.get_attributes();
xml_node::attribute_map::const_iterator aitr = attr.begin();
xml_node::attribute_map::const_iterator aend = attr.end();
for (;aitr!=aend; aitr++)
{
if (!aitr->second.processed)
{
error_message << "\n* attribute '" << aitr->first <<
2012-03-13 15:56:11 +01:00
"' with value '" << aitr->second.value <<
"' in line " << node.line();
}
}
2012-03-12 02:12:58 +01:00
xml_node::const_iterator itr = node.begin();
xml_node::const_iterator end = node.end();
for (; itr!=end; itr++)
{
find_unused_nodes_recursive(*itr, error_message);
}
}
} // end of namespace mapnik