/***************************************************************************** * * This file is part of Mapnik (c++ mapping toolkit) * * Copyright (C) 2021 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 * *****************************************************************************/ #ifndef MAPNIK_EVALUATE_GLOBAL_ATTRIBUTES_HPP #define MAPNIK_EVALUATE_GLOBAL_ATTRIBUTES_HPP #include #include #include #include #include #include #include #include #include #include namespace mapnik { namespace { template struct evaluate_expression { using value_type = T; explicit evaluate_expression(Attributes const& attrs) : attrs_(attrs) {} value_type operator()(attribute const&) const { throw std::runtime_error("can't evaluate feature attributes in this context"); } value_type operator()(global_attribute const& attr) const { auto itr = attrs_.find(attr.name); if (itr != attrs_.end()) { return itr->second; } return value_type(); // throw? } value_type operator()(geometry_type_attribute const&) const { throw std::runtime_error("can't evaluate geometry_type attributes in this context"); } value_type operator()(binary_node const& x) const { return (util::apply_visitor(*this, x.left).to_bool()) && (util::apply_visitor(*this, x.right).to_bool()); } value_type operator()(binary_node const& x) const { return (util::apply_visitor(*this, x.left).to_bool()) || (util::apply_visitor(*this, x.right).to_bool()); } template value_type operator()(binary_node const& x) const { typename make_op::type operation; return operation(util::apply_visitor(*this, x.left), util::apply_visitor(*this, x.right)); } template value_type operator()(unary_node const& x) const { typename make_op::type func; return func(util::apply_visitor(*this, x.expr)); } value_type operator()(unary_node const& x) const { return !(util::apply_visitor(*this, x.expr).to_bool()); } value_type operator()(regex_match_node const& x) const { value_type v = util::apply_visitor(*this, x.expr); return x.apply(v); } value_type operator()(regex_replace_node const& x) const { value_type v = util::apply_visitor(*this, x.expr); return x.apply(v); } value_type operator()(unary_function_call const& call) const { value_type arg = util::apply_visitor(*this, call.arg); return call.fun(arg); } value_type operator()(binary_function_call const& call) const { value_type arg1 = util::apply_visitor(*this, call.arg1); value_type arg2 = util::apply_visitor(*this, call.arg2); return call.fun(arg1, arg2); } template value_type operator()(ValueType const& val) const { return value_type(val); } Attributes const& attrs_; }; template struct evaluate_expression { using value_type = T; evaluate_expression(boost::none_t) {} value_type operator()(attribute const&) const { throw std::runtime_error("can't evaluate feature attributes in this context"); } value_type operator()(global_attribute const&) const { throw std::runtime_error("can't evaluate feature attributes in this context"); } value_type operator()(geometry_type_attribute const&) const { throw std::runtime_error("can't evaluate geometry_type attributes in this context"); } value_type operator()(binary_node const& x) const { return (util::apply_visitor(*this, x.left).to_bool()) && (util::apply_visitor(*this, x.right).to_bool()); } value_type operator()(binary_node const& x) const { return (util::apply_visitor(*this, x.left).to_bool()) || (util::apply_visitor(*this, x.right).to_bool()); } template value_type operator()(binary_node const& x) const { typename make_op::type operation; return operation(util::apply_visitor(*this, x.left), util::apply_visitor(*this, x.right)); } template value_type operator()(unary_node const& x) const { typename make_op::type func; return func(util::apply_visitor(*this, x.expr)); } value_type operator()(unary_node const& x) const { return !(util::apply_visitor(*this, x.expr).to_bool()); } value_type operator()(regex_match_node const& x) const { value_type v = util::apply_visitor(*this, x.expr); return x.apply(v); } value_type operator()(regex_replace_node const& x) const { value_type v = util::apply_visitor(*this, x.expr); return x.apply(v); } value_type operator()(unary_function_call const& call) const { value_type arg = util::apply_visitor(*this, call.arg); return call.fun(arg); } value_type operator()(binary_function_call const& call) const { value_type arg1 = util::apply_visitor(*this, call.arg1); value_type arg2 = util::apply_visitor(*this, call.arg2); return call.fun(arg1, arg2); } template value_type operator()(ValueType const& val) const { return value_type(val); } }; struct assign_value { template static void apply(symbolizer_base::value_type& val, expression_ptr const& expr, Attributes const& attrs, property_types target) { switch (target) { case property_types::target_color: { // evaluate expression as a string then parse as css color std::string str = util::apply_visitor(mapnik::evaluate_expression(attrs), *expr).to_string(); try { val = parse_color(str); } catch (...) { val = color(0, 0, 0); } break; } case property_types::target_double: { val = util::apply_visitor(mapnik::evaluate_expression(attrs), *expr).to_double(); break; } case property_types::target_integer: { val = util::apply_visitor(mapnik::evaluate_expression(attrs), *expr).to_int(); break; } case property_types::target_bool: { val = util::apply_visitor(mapnik::evaluate_expression(attrs), *expr).to_bool(); break; } default: // no-op break; } } }; } // namespace template std::tuple pre_evaluate_expression(expression_ptr const& expr) { try { return std::make_tuple(util::apply_visitor(mapnik::evaluate_expression(boost::none), *expr), true); } catch (...) { return std::make_tuple(T(), false); } } struct evaluate_global_attributes : util::noncopyable { template struct evaluator { evaluator(symbolizer_base::cont_type::value_type& prop, Attributes const& attrs) : prop_(prop) , attrs_(attrs) {} void operator()(expression_ptr const& expr) const { auto const& meta = get_meta(prop_.first); assign_value::apply(prop_.second, expr, attrs_, std::get<2>(meta)); } template void operator()(T const&) const { // no-op } symbolizer_base::cont_type::value_type& prop_; Attributes const& attrs_; }; template struct extract_symbolizer { extract_symbolizer(Attributes const& attrs) : attrs_(attrs) {} template void operator()(Symbolizer& sym) const { for (auto& prop : sym.properties) { util::apply_visitor(evaluator(prop, attrs_), prop.second); } } Attributes const& attrs_; }; template static void apply(Map& m, Attributes const& attrs) { for (auto& val : m.styles()) { for (auto& rule : val.second.get_rules_nonconst()) { for (auto& sym : rule) { util::apply_visitor(extract_symbolizer(attrs), sym); } } } } }; } // namespace mapnik #endif // MAPNIK_EVALUATE_GLOBAL_ATTRIBUTES_HPP