From 5d9183fa972f3c8127dca4e80ab2f2c8e61c1919 Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Mon, 27 Oct 2025 13:56:34 +0000 Subject: [PATCH] Implement sort-by clause parser `, [DESC | ASC]` e.g "name DESC" (default to ASC) --- .../mapnik/feature_style_processor_impl.hpp | 4 +- include/mapnik/layer.hpp | 11 ++-- include/mapnik/util/sort_by.hpp | 58 +++++++++++++++++++ src/layer.cpp | 6 +- src/load_map.cpp | 20 ++++++- src/save_map.cpp | 6 +- 6 files changed, 91 insertions(+), 14 deletions(-) create mode 100644 include/mapnik/util/sort_by.hpp diff --git a/include/mapnik/feature_style_processor_impl.hpp b/include/mapnik/feature_style_processor_impl.hpp index 490193aef..e3047d397 100644 --- a/include/mapnik/feature_style_processor_impl.hpp +++ b/include/mapnik/feature_style_processor_impl.hpp @@ -427,7 +427,7 @@ void feature_style_processor::prepare_layer(layer_rendering_material& auto sort_by = lay.sort_by(); if (sort_by) { - q.add_property_name(*sort_by); + q.add_property_name((*sort_by).first); } bool cache_features = lay.cache_features() && active_styles.size() > 1; @@ -502,7 +502,7 @@ void feature_style_processor::render_material(layer_rendering_materia { cache->push(feature); } - cache->sort_by(*sort_by, true); + cache->sort_by((*sort_by).first, (*sort_by).second); std::size_t i = 0; for (feature_type_style const* style : active_styles) { diff --git a/include/mapnik/layer.hpp b/include/mapnik/layer.hpp index eb15eb62d..4c092718a 100644 --- a/include/mapnik/layer.hpp +++ b/include/mapnik/layer.hpp @@ -27,10 +27,11 @@ #include #include #include - +#include // stl #include #include +#include namespace mapnik { @@ -194,12 +195,12 @@ class MAPNIK_DECL layer /*! * @param column Set the field rendering of this layer is sorted by. */ - void set_sort_by(std::string const& column); + void set_sort_by(std::string const& column, bool desc = false); /*! - * @return optional field rendering of this layer is sorted by. + * @return optional field (+order) rendering of this layer is sorted by. */ - std::optional const& sort_by() const; + std::optional const& sort_by() const; /*! * @brief Attach a datasource for this layer. @@ -242,7 +243,7 @@ class MAPNIK_DECL layer bool clear_label_cache_; bool cache_features_; std::string group_by_; - std::optional sort_by_; + std::optional sort_by_; std::vector styles_; std::vector layers_; datasource_ptr ds_; diff --git a/include/mapnik/util/sort_by.hpp b/include/mapnik/util/sort_by.hpp new file mode 100644 index 000000000..549c39a06 --- /dev/null +++ b/include/mapnik/util/sort_by.hpp @@ -0,0 +1,58 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2025 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_SORT_BY_HPP +#define MAPNIK_SORT_BY_HPP + +#include +#include + +namespace mapnik { + +using sort_by_type = std::pair; + +inline bool parse_sort_by(std::string const& str, sort_by_type & result) +{ + namespace x3 = boost::spirit::x3; + auto itr = str.begin(); + auto end = str.end(); + auto apply_sort_by = [&](auto const& ctx) { + result.first = _attr(ctx); + }; + auto apply_desc = [&](auto const& ctx) { + result.second = true; + }; + if (!x3::phrase_parse(itr, end, + x3::no_skip[(+x3::char_("a-zA-Z_0-9-"))][apply_sort_by] + > -(x3::no_case[x3::lit("DESC")][apply_desc] | x3::no_case[x3::lit("ASC")]), + // ASC is a default + x3::space + ) || (itr != end)) + { + return false; + } + return true; +} + +} // namespace mapnik +#endif // MAPNIK_SORT_BY_HPP diff --git a/src/layer.cpp b/src/layer.cpp index 4458ce284..d6a7f0fd6 100644 --- a/src/layer.cpp +++ b/src/layer.cpp @@ -297,12 +297,12 @@ std::string const& layer::group_by() const return group_by_; } -void layer::set_sort_by(std::string const& column) +void layer::set_sort_by(std::string const& column, bool desc) { - sort_by_ = column; + sort_by_ = {column, desc}; } -std::optional const& layer::sort_by() const +std::optional const& layer::sort_by() const { return sort_by_; } diff --git a/src/load_map.cpp b/src/load_map.cpp index 62986d0e9..974b0e9c5 100644 --- a/src/load_map.cpp +++ b/src/load_map.cpp @@ -49,6 +49,7 @@ #include #include #include +#include #include #include #include @@ -751,10 +752,23 @@ void map_parser::parse_layer(Parent& parent, xml_node const& node) lyr.set_group_by(*group_by); } - optional sort_by = node.get_opt_attr("sort-by"); - if (sort_by) + optional str = node.get_opt_attr("sort-by"); + if (str) { - lyr.set_sort_by(*sort_by); + mapnik::sort_by_type result; + if (!mapnik::parse_sort_by(*str, result)) + { + std::string s_err("failed to parse Layer sort-by clause \"" + *str + "\""); + if (strict_) + { + throw config_error(s_err); + } + else + { + MAPNIK_LOG_ERROR(load_map) << "map_parser: " << s_err; + } + } + lyr.set_sort_by(result.first, result.second); } optional buffer_size = node.get_opt_attr("buffer-size"); diff --git a/src/save_map.cpp b/src/save_map.cpp index 9c0dd0462..223cd80dc 100644 --- a/src/save_map.cpp +++ b/src/save_map.cpp @@ -549,7 +549,11 @@ void serialize_layer(ptree& map_node, layer const& lyr, bool explicit_defaults) if (lyr.sort_by() || explicit_defaults) { - set_attr(layer_node, "sort-by", *lyr.sort_by()); + auto sort_by = *lyr.sort_by(); + std::string str = sort_by.first; + if (sort_by.second) + str += " DESC"; + set_attr(layer_node, "sort-by", str); } auto&& buffer_size = lyr.buffer_size();