From 7043c416fa8b483cab455bc98379b1a747ff6eba Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 1 Feb 2011 22:55:50 +0000 Subject: [PATCH] enable if/else type behavior in rules using style level keyword to control whether all rules will be evaluated or just the first that matches - closes #706 - thanks to original patch from kkaefer --- AUTHORS | 1 + CHANGELOG | 2 + bindings/python/mapnik/__init__.py | 1 + bindings/python/mapnik_style.cpp | 10 +++ include/mapnik/feature_style_processor.hpp | 5 ++ include/mapnik/feature_type_style.hpp | 42 ++++++------ src/SConscript | 1 + src/feature_type_style.cpp | 76 ++++++++++++++++++++++ src/load_map.cpp | 3 + src/save_map.cpp | 7 ++ tests/python_tests/filter_test.py | 12 +++- 11 files changed, 138 insertions(+), 22 deletions(-) create mode 100644 src/feature_type_style.cpp diff --git a/AUTHORS b/AUTHORS index 1b288be50..6c3b36b7e 100644 --- a/AUTHORS +++ b/AUTHORS @@ -37,6 +37,7 @@ Patches - Beau Gunderson - John Hague - Aubrey Holland + - Konstantin Käfer - Mak Kolybabi - Dennis Luxen - Tom MacWright diff --git a/CHANGELOG b/CHANGELOG index 0a0bca99f..1d67853ad 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -14,6 +14,8 @@ For a complete change history, see the SVN log. Mapnik Trunk ------------ +- Added support for drawing only first matching rule using filter-mode="first" in Style (#706) + - Added support to PointSymbolizer ('ignore_placement') for skipping adding placed points to collision detector (#564) - Added ability to register fonts within XML using Map level 'font_directory' parameter (#168) diff --git a/bindings/python/mapnik/__init__.py b/bindings/python/mapnik/__init__.py index b0ef1dff1..d62988c4b 100644 --- a/bindings/python/mapnik/__init__.py +++ b/bindings/python/mapnik/__init__.py @@ -732,6 +732,7 @@ __all__ = [ 'horizontal_alignment', 'justify_alignment', 'pattern_alignment', + 'filter_mode', # functions # datasources 'Datasource', diff --git a/bindings/python/mapnik_style.cpp b/bindings/python/mapnik_style.cpp index 349996c39..e8109d7da 100644 --- a/bindings/python/mapnik_style.cpp +++ b/bindings/python/mapnik_style.cpp @@ -24,6 +24,7 @@ #include #include +#include "mapnik_enumeration.hpp" #include using mapnik::feature_type_style; @@ -73,6 +74,11 @@ void export_style() { using namespace boost::python; + mapnik::enumeration_("filter_mode") + .value("ALL",mapnik::FILTER_ALL) + .value("FIRST",mapnik::FILTER_FIRST) + ; + class_("Rules",init<>("default ctor")) .def(vector_indexing_suite()) ; @@ -92,6 +98,10 @@ void export_style() "\n" "\n" ) + .add_property("filter_mode", + &feature_type_style::get_filter_mode, + &feature_type_style::set_filter_mode, + "Set/get the placement of the label") ; } diff --git a/include/mapnik/feature_style_processor.hpp b/include/mapnik/feature_style_processor.hpp index cdebf97e1..2bf153ebc 100644 --- a/include/mapnik/feature_style_processor.hpp +++ b/include/mapnik/feature_style_processor.hpp @@ -323,6 +323,11 @@ private: boost::apply_visitor(symbol_dispatch(p,*feature,prj_trans),sym); } } + if (style->get_filter_mode() == FILTER_FIRST) + { + // Stop iterating over rules and proceed with next feature. + break; + } } } if (do_else) diff --git a/include/mapnik/feature_type_style.hpp b/include/mapnik/feature_type_style.hpp index d605bcffc..d4a428778 100644 --- a/include/mapnik/feature_type_style.hpp +++ b/include/mapnik/feature_type_style.hpp @@ -27,44 +27,44 @@ // mapnik #include #include +#include // stl #include namespace mapnik { + +enum filter_mode_enum { + FILTER_ALL, + FILTER_FIRST, + filter_mode_enum_MAX +}; + +DEFINE_ENUM( filter_mode_e, filter_mode_enum ); + typedef std::vector rules; class feature_type_style { private: rules rules_; + filter_mode_e filter_mode_; public: - feature_type_style() {} + feature_type_style(); - feature_type_style(feature_type_style const& rhs) - : rules_(rhs.rules_) {} + feature_type_style(feature_type_style const& rhs); - feature_type_style& operator=(feature_type_style const& rhs) - { - if (this == &rhs) return *this; - rules_=rhs.rules_; - return *this; - } + feature_type_style& operator=(feature_type_style const& rhs); - void add_rule(rule const& rule) - { - rules_.push_back(rule); - } + void add_rule(rule const& rule); - rules const& get_rules() const - { - return rules_; - } + rules const& get_rules() const; - rules &get_rules_nonconst() - { - return rules_; - } + rules &get_rules_nonconst(); + void set_filter_mode(filter_mode_e mode); + + filter_mode_e get_filter_mode() const; + ~feature_type_style() {} }; } diff --git a/src/SConscript b/src/SConscript index 89cec8195..db0a51597 100644 --- a/src/SConscript +++ b/src/SConscript @@ -102,6 +102,7 @@ source = Split( expression_node.cpp expression_string.cpp filter_factory.cpp + feature_type_style.cpp font_engine_freetype.cpp font_set.cpp gradient.cpp diff --git a/src/feature_type_style.cpp b/src/feature_type_style.cpp new file mode 100644 index 000000000..cc65fea19 --- /dev/null +++ b/src/feature_type_style.cpp @@ -0,0 +1,76 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2010 Artem Pavlenko + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ + +#include + +namespace mapnik +{ + +static const char * filter_mode_strings[] = { + "all", + "first", + "" +}; + +IMPLEMENT_ENUM( filter_mode_e, filter_mode_strings ); + + +feature_type_style::feature_type_style() + : filter_mode_(FILTER_ALL) {} + +feature_type_style::feature_type_style(feature_type_style const& rhs) + : rules_(rhs.rules_), + filter_mode_(rhs.filter_mode_) {} + +feature_type_style& feature_type_style::operator=(feature_type_style const& rhs) +{ + if (this == &rhs) return *this; + rules_=rhs.rules_; + return *this; +} + +void feature_type_style::add_rule(rule const& rule) +{ + rules_.push_back(rule); +} + +rules const& feature_type_style::get_rules() const +{ + return rules_; +} + +rules &feature_type_style::get_rules_nonconst() +{ + return rules_; +} + +void feature_type_style::set_filter_mode(filter_mode_e mode) +{ + filter_mode_ = mode; +} + +filter_mode_e feature_type_style::get_filter_mode() const +{ + return filter_mode_; +} + +} diff --git a/src/load_map.cpp b/src/load_map.cpp index a65537458..f2200d990 100644 --- a/src/load_map.cpp +++ b/src/load_map.cpp @@ -347,6 +347,9 @@ void map_parser::parse_style( Map & map, ptree const & sty ) name = get_attr(sty, "name"); feature_type_style style; + filter_mode_e filter_mode = get_attr(sty, "filter-mode", FILTER_ALL); + style.set_filter_mode(filter_mode); + ptree::const_iterator ruleIter = sty.begin(); ptree::const_iterator endRule = sty.end(); diff --git a/src/save_map.cpp b/src/save_map.cpp index 31dbbb8f3..bec4c1ad1 100644 --- a/src/save_map.cpp +++ b/src/save_map.cpp @@ -632,11 +632,18 @@ void serialize_style( ptree & map_node, Map::const_style_iterator style_it, bool { const feature_type_style & style = style_it->second; const std::string & name = style_it->first; + filter_mode_e filter_mode = style.get_filter_mode(); ptree & style_node = map_node.push_back( ptree::value_type("Style", ptree()))->second; set_attr(style_node, "name", name); + + feature_type_style dfl; + if (filter_mode != dfl.get_filter_mode() || explicit_defaults) + { + set_attr(style_node, "filter-mode", filter_mode); + } rules::const_iterator it = style.get_rules().begin(); rules::const_iterator end = style.get_rules().end(); diff --git a/tests/python_tests/filter_test.py b/tests/python_tests/filter_test.py index 670069f74..6bbd835dc 100644 --- a/tests/python_tests/filter_test.py +++ b/tests/python_tests/filter_test.py @@ -42,6 +42,12 @@ map_ = ''' + ''' def test_filter_init(): @@ -80,4 +86,8 @@ def test_filter_init(): first = filters[0] for f in filters: - eq_(str(first),str(f)) \ No newline at end of file + eq_(str(first),str(f)) + + s = m.find_style('s2') + + eq_(s.filter_mode,mapnik2.filter_mode.FIRST)