diff --git a/CHANGELOG.md b/CHANGELOG.md index c6114f8e4..f7c607821 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,37 @@ Developers: Please commit along with changes. For a complete change history, see the git log. +## 3.0.10 + +Released: February 25, 2016 + +(Packaged from 5c0d496) + +#### Summary + + - The `shapeindex` command now has a `--index-parts` option. When used the index will be bigger + but will allow the Shapefile datasource to only parse polygon parts within the query bounds. + - WARNING: index files generated with this newer Mapnik are invalid for older versions of Mapnik. + - Any `.index` files accompanying a `.shp` must now be regenerated otherwise + it will be skipped. To avoid this problem you can delete the existing `.index` files, or ideally run `shapeindex` to recreate the `.index`. (https://github.com/mapnik/mapnik/pull/3300) + The trigger for this change was an optimization that required a new binary format for the shapefile indexes (https://github.com/mapnik/mapnik/pull/3217). + - Shapeindex - another fix for skipping `null` shapes (#3288) + - Fixed support for filter expressions starting with `not` (https://github.com/mapnik/mapnik/issues/3017) + - Ensure `mapped_memory_cache` acts as singleton across shared objects (#3306) + - Removed miniz support in PNG encoder (#3281) + - Added `-fvisibility=hidden -fvisibility-inlines-hidden` to default compiler flags + - Fixed parsing of SVG `PathElement` (https://github.com/mapnik/mapnik/issues/3225) + - JSON parsing now supports arbitrary (nested) attributes in `geometry` + - Support for rendering `dash-array` in SVGs + - SVG parser is now stricter (fails is all input is not parsable) (#3251) + - SVG parser now correctly handles optional separator `(,)` between multiple command parts + - Optimized parsing of `png` format string + - The `memory_datasource` now dynamically reports correct datasource type (vector or raster) + - Upgraded `mapbox::variant v1.1.0` + - Compare: https://github.com/mapnik/mapnik/compare/v3.0.9...v3.0.10 + + + ## 3.0.9 Released: November 26, 2015 @@ -18,12 +49,12 @@ Released: November 26, 2015 - Fixed mapnik.util.variant issue when compiling with gcc-5.x and SSO enabled by default (https://github.com/mapnik/mapnik/issues/3103) (via @nkovacs) - Fixed issue with complex scripts where some character sequences weren't rendered correctly (https://github.com/mapnik/mapnik/issues/3050) (via @jkroll20) - Revived postgis.input tests - - JSON: geometry grammar has been refactored and optimized to have expectation points + - JSON: geometry grammar has been re-factored and optimized to have expectation points - Filled missing specializations for value_bool in `mapnik::value` comparison operators - `mapnik.Image` - fixed copy semantics implementation for internal buffer - JSON parsing: unified error_handler across all grammars - Improved unit test coverage - - Raster scaling: fixed nodata handling, acurracy when working with small floats and clipping floats by \[0; 255\] (https://github.com/mapnik/mapnik/pull/3147) + - Raster scaling: fixed nodata handling, accuracy when working with small floats and clipping floats by \[0; 255\] (https://github.com/mapnik/mapnik/pull/3147) - Added [`code of conduct`](http://contributor-covenant.org) - GeoJSON plug-in is updated to skip feature with empty geometries - GeoJSON plug-in : ensure original order of features is preserved (fixed) (https://github.com/mapnik/mapnik/issues/3182) @@ -41,9 +72,9 @@ Released: October 23, 2015 - Renamed `SHAPE_MEMORY_MAPPED_FILE` define to `MAPNIK_MEMORY_MAPPED_FILE`. Pass `./configure MEMORY_MAPPED_FILE=True|False` to request support for memory mapped files across Mapnik plugins (currently shape, csv, and geojson). - - Unified `mapnik-index` utility supporing GeoJSON and CSV formats + - Unified `mapnik-index` utility supporting GeoJSON and CSV formats - Increased unit test coverage for GeoJSON and CSV plugins - - shape.input - refactor to support *.shx and improve handling various bogus shapefiles + - shape.input - re-factor to support *.shx and improve handling various bogus shapefiles - geojson.input - make JSON parser stricter + support single Feature/Geometry as well as FeatureCollection - maintain 'FT_LOAD_NO_HINTING' + support >= harfbuzz 1.0.5 - geojson.input - implement on-disk-index support @@ -109,7 +140,7 @@ Released: August 26, 2015 #### Summary -- CSV.input: plug-in has been refactored to minimise memory usage and to improve handling of larger input. +- CSV.input: plug-in has been re-factored to minimise memory usage and to improve handling of larger input. (NOTE: [large_csv](https://github.com/mapnik/mapnik/tree/large_csv) branch adds experimental trunsduction parser with deferred string initialisation) - CSV.input: added internal spatial index (boost::geometry::index::tree) for fast `bounding box` queries (https://github.com/mapnik/mapnik/pull/3010) - Fixed deadlock in recursive datasource registration via @zerebubuth (https://github.com/mapnik/mapnik/pull/3038) @@ -1200,7 +1231,7 @@ Released April 1, 2009 - Plugins: Use memory mapped files for reading shape file (r628) -- Core: Use streams to write images (i/o refactor) (r628) (#15) +- Core: Use streams to write images (i/o re-factor) (r628) (#15) # Mapnik 0.5.1 diff --git a/Makefile b/Makefile index 777303d48..c1746121e 100755 --- a/Makefile +++ b/Makefile @@ -24,7 +24,8 @@ release: git clone --depth 1 --branch v$${MAPNIK_VERSION} git@github.com:mapnik/mapnik.git $${TARBALL_NAME} && \ cd $${TARBALL_NAME} && \ git checkout "tags/v$${MAPNIK_VERSION}" && \ - git submodule update --depth 1 --init && \ + git submodule update --depth 100 --init && \ + rm -rf deps/mapbox/variant/.git && \ rm -rf test/data/.git && \ rm -rf test/data/.gitignore && \ rm -rf test/data-visual/.git && \ diff --git a/appveyor.yml b/appveyor.yml index ea7af4126..e9d278c6b 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -12,7 +12,13 @@ os: Visual Studio 2015 # limit clone to latest 5 commits clone_depth: 5 +services: + - postgresql94 #if changing this, also change PATH below + install: + - SET PGUSER=postgres + - SET PGPASSWORD=Password12! + - SET PATH=C:\Program Files\PostgreSQL\9.4\bin\;%PATH% - scripts\build-appveyor.bat artifacts: diff --git a/benchmark/bench_framework.hpp b/benchmark/bench_framework.hpp index 34c63a8e7..c5f280207 100644 --- a/benchmark/bench_framework.hpp +++ b/benchmark/bench_framework.hpp @@ -10,6 +10,7 @@ // stl #include +#include // log10, round #include // snprintf #include #include @@ -19,6 +20,12 @@ namespace benchmark { +template +using milliseconds = std::chrono::duration; + +template +using seconds = std::chrono::duration; + class test_case { protected: @@ -92,7 +99,7 @@ inline int parse_args(int argc, char** argv, mapnik::parameters & params) inline void handle_common_args(mapnik::parameters const& params) { - if (auto severity = params.get("log-severity")) { + if (auto severity = params.get("log")) { if (*severity == "debug") mapnik::logger::set_severity(mapnik::logger::debug); else if (*severity == "warn") @@ -102,7 +109,7 @@ inline void handle_common_args(mapnik::parameters const& params) else if (*severity == "none") mapnik::logger::set_severity(mapnik::logger::none); else - std::clog << "ignoring option --log-severity='" << *severity + std::clog << "ignoring option --log='" << *severity << "' (allowed values are: debug, warn, error, none)\n"; } } @@ -134,6 +141,29 @@ inline int handle_args(int argc, char** argv, mapnik::parameters & params) } \ } \ +struct big_number_fmt +{ + int w; + double v; + const char* u; + + big_number_fmt(int width, double value, int base = 1000) + : w(width), v(value), u("") + { + static const char* suffixes = "\0\0k\0M\0G\0T\0P\0E\0Z\0Y\0\0"; + u = suffixes; + + while (v > 1 && std::log10(std::round(v)) >= width && u[2]) + { + v /= base; + u += 2; + } + + // adjust width for proper alignment without suffix + w += (u == suffixes); + } +}; + template int run(T const& test_runner, std::string const& name) { @@ -156,21 +186,43 @@ int run(T const& test_runner, std::string const& name) auto opt_min_duration = test_runner.params().template get("min-duration", 0.0); std::chrono::duration min_seconds(*opt_min_duration); auto min_duration = std::chrono::duration_cast(min_seconds); - std::size_t loops = 0; + auto num_iters = test_runner.iterations(); + auto num_threads = test_runner.threads(); + auto total_iters = 0; - if (test_runner.threads() > 0) + if (num_threads > 0) { - using thread_group = std::vector >; - using value_type = thread_group::value_type; - thread_group tg; - for (std::size_t i=0;i lock_ready(mtx_ready); + + auto stub = [&](T const& test_copy) { - tg.emplace_back(new std::thread(test_runner)); + // workers will wait on this mutex until the main thread + // constructs all of them and starts measuring time + std::unique_lock my_lock(mtx_ready); + my_lock.unlock(); + test_copy(); + }; + + std::vector tg; + tg.reserve(num_threads); + for (auto i = num_threads; i-- > 0; ) + { + tg.emplace_back(stub, test_runner); } start = std::chrono::high_resolution_clock::now(); - std::for_each(tg.begin(), tg.end(), [](value_type & t) {if (t->joinable()) t->join();}); + lock_ready.unlock(); + // wait for all workers to finish + for (auto & t : tg) + { + if (t.joinable()) + t.join(); + } elapsed = std::chrono::high_resolution_clock::now() - start; - loops = 1; + // this is actually per-thread count, not total, but I think + // reporting average 'iters/thread/second' is more useful + // than 'iters/second' multiplied by the number of threads + total_iters += num_iters; } else { @@ -178,39 +230,25 @@ int run(T const& test_runner, std::string const& name) do { test_runner(); elapsed = std::chrono::high_resolution_clock::now() - start; - ++loops; + total_iters += num_iters; } while (elapsed < min_duration); } - double iters = loops * test_runner.iterations(); - double dur_total = std::chrono::duration(elapsed).count(); - double dur_avg = dur_total / iters; - char iters_unit = ' '; char msg[200]; - - if (iters >= 1e7) iters *= 1e-6, iters_unit = 'M'; - else if (iters >= 1e4) iters *= 1e-3, iters_unit = 'k'; + double dur_total = milliseconds(elapsed).count(); + auto elapsed_nonzero = std::max(elapsed, decltype(elapsed){1}); + big_number_fmt itersf(4, total_iters); + big_number_fmt ips(5, total_iters / seconds(elapsed_nonzero).count()); std::snprintf(msg, sizeof(msg), - "%-43s %3zu threads %4.0f%c iters %6.0f milliseconds", + "%-43s %3zu threads %*.0f%s iters %6.0f milliseconds %*.0f%s i/s\n", name.c_str(), - test_runner.threads(), - iters, iters_unit, - dur_total); + num_threads, + itersf.w, itersf.v, itersf.u, + dur_total, + ips.w, ips.v, ips.u + ); std::clog << msg; - - // log average time per iteration, currently only for non-threaded runs - if (test_runner.threads() == 0) - { - char unit = 'm'; - if (dur_avg < 1e-5) dur_avg *= 1e+9, unit = 'p'; - else if (dur_avg < 1e-2) dur_avg *= 1e+6, unit = 'n'; - else if (dur_avg < 1e+1) dur_avg *= 1e+3, unit = 'u'; - std::snprintf(msg, sizeof(msg), " %4.0f%cs/iter", dur_avg, unit); - std::clog << msg; - } - - std::clog << "\n"; return 0; } catch (std::exception const& ex) diff --git a/benchmark/run b/benchmark/run index 00285b2ba..6850f8326 100755 --- a/benchmark/run +++ b/benchmark/run @@ -6,8 +6,14 @@ source ./localize.sh BASE=./benchmark/out function run { - ${BASE}/$1 --threads 0 --iterations $3; - ${BASE}/$1 --threads $2 --iterations $(expr $3 / $2); + local runner="$BASE/$1 --log=none" + local threads="$2" + local iters="$3" + shift 3 + $runner --threads 0 --iterations $iters "$@" + if test $threads -gt 0; then + $runner --threads $threads --iterations $((iters/threads)) "$@" + fi } run test_getline 30 10000000 #run test_array_allocation 20 100000 diff --git a/include/mapnik/csv/csv_grammar.hpp b/include/mapnik/csv/csv_grammar.hpp index 97778205d..50937269a 100644 --- a/include/mapnik/csv/csv_grammar.hpp +++ b/include/mapnik/csv/csv_grammar.hpp @@ -86,7 +86,7 @@ struct csv_line_grammar : qi::grammar ("\\\"", '\"') ("\"\"", '\"') // double quote ; - line = -lit("\n\r") >> column(_r1, _r2) % lit(_r1) + line = -lit("\r") >> -lit("\n") >> column(_r1, _r2) % lit(_r1) ; column = quoted(_r2) | *(char_ - lit(_r1)) ; diff --git a/include/mapnik/expression_node.hpp b/include/mapnik/expression_node.hpp index 7731414a4..6fdad8f5f 100644 --- a/include/mapnik/expression_node.hpp +++ b/include/mapnik/expression_node.hpp @@ -31,6 +31,8 @@ #include #include #include +// stl +#include namespace mapnik { diff --git a/include/mapnik/marker_helpers.hpp b/include/mapnik/marker_helpers.hpp index 49c011a3b..f50dc5b6f 100644 --- a/include/mapnik/marker_helpers.hpp +++ b/include/mapnik/marker_helpers.hpp @@ -36,7 +36,7 @@ #include #include #include - +#include // agg #include "agg_trans_affine.h" @@ -158,84 +158,29 @@ void setup_transform_scaling(agg::trans_affine & tr, attributes const& vars, symbolizer_base const& sym); +using vertex_converter_type = vertex_converter; + // Apply markers to a feature with multiple geometries -template -void apply_markers_multi(feature_impl const& feature, attributes const& vars, Converter & converter, Processor & proc, symbolizer_base const& sym) -{ - using apply_vertex_converter_type = detail::apply_vertex_converter; - using vertex_processor_type = geometry::vertex_processor; +template +void apply_markers_multi(feature_impl const& feature, attributes const& vars, + vertex_converter_type & converter, Processor & proc, symbolizer_base const& sym); - auto const& geom = feature.get_geometry(); - geometry::geometry_types type = geometry::geometry_type(geom); - if (type == geometry::geometry_types::Point - || type == geometry::geometry_types::LineString - || type == geometry::geometry_types::Polygon) - { - apply_vertex_converter_type apply(converter, proc); - mapnik::util::apply_visitor(vertex_processor_type(apply), geom); - } - else - { +using vector_dispatch_type = vector_markers_dispatch; +using raster_dispatch_type = raster_markers_dispatch; - marker_multi_policy_enum multi_policy = get(sym, feature, vars); - marker_placement_enum placement = get(sym, feature, vars); +extern template void apply_markers_multi(feature_impl const& feature, attributes const& vars, + vertex_converter_type & converter, vector_dispatch_type & proc, symbolizer_base const& sym); + +extern template void apply_markers_multi(feature_impl const& feature, attributes const& vars, + vertex_converter_type & converter, raster_dispatch_type & proc, symbolizer_base const& sym); - if (placement == MARKER_POINT_PLACEMENT && - multi_policy == MARKER_WHOLE_MULTI) - { - geometry::point pt; - // test if centroid is contained by bounding box - if (geometry::centroid(geom, pt) && converter.disp_.args_.bbox.contains(pt.x, pt.y)) - { - // unset any clipping since we're now dealing with a point - converter.template unset(); - geometry::point_vertex_adapter va(pt); - converter.apply(va, proc); - } - } - else if ((placement == MARKER_POINT_PLACEMENT || placement == MARKER_INTERIOR_PLACEMENT) && - multi_policy == MARKER_LARGEST_MULTI) - { - // Only apply to path with largest envelope area - // TODO: consider using true area for polygon types - if (type == geometry::geometry_types::MultiPolygon) - { - geometry::multi_polygon const& multi_poly = mapnik::util::get >(geom); - double maxarea = 0; - geometry::polygon const* largest = 0; - for (geometry::polygon const& poly : multi_poly) - { - box2d bbox = geometry::envelope(poly); - double area = bbox.width() * bbox.height(); - if (area > maxarea) - { - maxarea = area; - largest = &poly; - } - } - if (largest) - { - geometry::polygon_vertex_adapter va(*largest); - converter.apply(va, proc); - } - } - else - { - MAPNIK_LOG_WARN(marker_symbolizer) << "TODO: if you get here -> open an issue"; - } - } - else - { - if (multi_policy != MARKER_EACH_MULTI && placement != MARKER_POINT_PLACEMENT) - { - MAPNIK_LOG_WARN(marker_symbolizer) << "marker_multi_policy != 'each' has no effect with marker_placement != 'point'"; - } - apply_vertex_converter_type apply(converter, proc); - mapnik::util::apply_visitor(vertex_processor_type(apply), geom); - } - } -} } diff --git a/include/mapnik/markers_placement.hpp b/include/mapnik/markers_placement.hpp index 3e7e3e495..02149d767 100644 --- a/include/mapnik/markers_placement.hpp +++ b/include/mapnik/markers_placement.hpp @@ -29,7 +29,6 @@ #include #include #include -#include namespace mapnik { @@ -38,70 +37,71 @@ template class markers_placement_finder : util::noncopyable { public: - using markers_placement = util::variant, - markers_line_placement, - markers_interior_placement, - markers_vertex_first_placement, - markers_vertex_last_placement>; - - class get_point_visitor - { - public: - get_point_visitor(double &x, double &y, double &angle, bool ignore_placement) - : x_(x), y_(y), angle_(angle), ignore_placement_(ignore_placement) - { - } - - template - bool operator()(T &placement) const - { - return placement.get_point(x_, y_, angle_, ignore_placement_); - } - - private: - double &x_, &y_, &angle_; - bool ignore_placement_; - }; + using basic_placement = markers_basic_placement; markers_placement_finder(marker_placement_e placement_type, Locator &locator, Detector &detector, markers_placement_params const& params) - : placement_(create(placement_type, locator, detector, params)) + : active_placement_(nullptr) { + switch (placement_type) + { + default: + case MARKER_POINT_PLACEMENT: + active_placement_ = construct(&point_, locator, detector, params); + break; + case MARKER_INTERIOR_PLACEMENT: + active_placement_ = construct(&interior_, locator, detector, params); + break; + case MARKER_LINE_PLACEMENT: + active_placement_ = construct(&line_, locator, detector, params); + break; + case MARKER_VERTEX_FIRST_PLACEMENT: + active_placement_ = construct(&vertex_first_, locator, detector, params); + break; + case MARKER_VERTEX_LAST_PLACEMENT: + active_placement_ = construct(&vertex_last_, locator, detector, params); + break; + } + // previously placement-type constructors (markers_*_placement) + // rewound the locator; reasons for rewinding here instead: + // 1) so that nobody is tempted to call now-virtual rewind() + // in placement-type class constructors + // 2) it servers as a runtime check that the above switch isn't + // missing cases and active_placement_ points to an object + active_placement_->rewind(); + } + + ~markers_placement_finder() + { + active_placement_->~basic_placement(); } // Get next point where the marker should be placed. Returns true if a place is found, false if none is found. bool get_point(double &x, double &y, double &angle, bool ignore_placement) { - return util::apply_visitor(get_point_visitor(x, y, angle, ignore_placement), placement_); + return active_placement_->get_point(x, y, angle, ignore_placement); } private: - // Factory function for particular placement implementations. - static markers_placement create(marker_placement_e placement_type, - Locator &locator, - Detector &detector, - markers_placement_params const& params) - { - switch (placement_type) - { - case MARKER_POINT_PLACEMENT: - return markers_point_placement(locator,detector,params); - case MARKER_INTERIOR_PLACEMENT: - return markers_interior_placement(locator,detector,params); - case MARKER_LINE_PLACEMENT: - return markers_line_placement(locator,detector,params); - case MARKER_VERTEX_FIRST_PLACEMENT: - return markers_vertex_first_placement(locator,detector,params); - case MARKER_VERTEX_LAST_PLACEMENT: - return markers_vertex_last_placement(locator,detector,params); - default: // point - return markers_point_placement(locator,detector,params); - } - } + basic_placement* active_placement_; - markers_placement placement_; + union + { + markers_point_placement point_; + markers_line_placement line_; + markers_interior_placement interior_; + markers_vertex_first_placement vertex_first_; + markers_vertex_last_placement vertex_last_; + }; + + template + static T* construct(T* what, Locator & locator, Detector & detector, + markers_placement_params const& params) + { + return new(what) T(locator, detector, params); + } }; } diff --git a/include/mapnik/markers_placements/basic.hpp b/include/mapnik/markers_placements/basic.hpp new file mode 100644 index 000000000..ede68bb6b --- /dev/null +++ b/include/mapnik/markers_placements/basic.hpp @@ -0,0 +1,153 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2016 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_MARKERS_PLACEMENTS_BASIC_HPP +#define MAPNIK_MARKERS_PLACEMENTS_BASIC_HPP + +// mapnik +#include +#include +#include +#include + +// agg +#include "agg_basics.h" +#include "agg_trans_affine.h" + +namespace mapnik { + +struct markers_placement_params +{ + box2d size; + agg::trans_affine tr; + double spacing; + double max_error; + bool allow_overlap; + bool avoid_edges; + direction_enum direction; +}; + +template +class markers_basic_placement : util::noncopyable +{ +public: + markers_basic_placement(Locator & locator, Detector & detector, + markers_placement_params const& params) + : locator_(locator), + detector_(detector), + params_(params), + done_(false) + { + // no need to rewind locator here, markers_placement_finder + // does that after construction + } + + markers_basic_placement(markers_basic_placement && ) = default; + + virtual ~markers_basic_placement() + { + // empty but necessary + } + + // Start again at first marker. Returns the same list of markers only works when they were NOT added to the detector. + virtual void rewind() + { + locator_.rewind(0); + done_ = false; + } + + // Get next point where the marker should be placed. Returns true if a place is found, false if none is found. + virtual bool get_point(double &x, double &y, double &angle, bool ignore_placement) = 0; + +protected: + Locator & locator_; + Detector & detector_; + markers_placement_params const& params_; + bool done_; + + // Rotates the size_ box and translates the position. + box2d perform_transform(double angle, double dx, double dy) const + { + auto tr = params_.tr * agg::trans_affine_rotation(angle).translate(dx, dy); + return box2d(params_.size, tr); + } + + // Checks transformed box placement with collision detector. + // returns false if the box: + // - a) isn't wholly inside extent and avoid_edges == true + // - b) collides with something and allow_overlap == false + // otherwise returns true, and if ignore_placement == true, + // also adds the box to collision detector + bool push_to_detector(double x, double y, double angle, bool ignore_placement) + { + auto box = perform_transform(angle, x, y); + if (params_.avoid_edges && !detector_.extent().contains(box)) + { + return false; + } + if (!params_.allow_overlap && !detector_.has_placement(box)) + { + return false; + } + if (!ignore_placement) + { + detector_.insert(box); + } + return true; + } + + bool set_direction(double & angle) const + { + switch (params_.direction) + { + case DIRECTION_UP: + angle = 0; + return true; + case DIRECTION_DOWN: + angle = M_PI; + return true; + case DIRECTION_AUTO: + if (std::fabs(util::normalize_angle(angle)) > 0.5 * M_PI) + angle += M_PI; + return true; + case DIRECTION_AUTO_DOWN: + if (std::fabs(util::normalize_angle(angle)) < 0.5 * M_PI) + angle += M_PI; + return true; + case DIRECTION_LEFT: + angle += M_PI; + return true; + case DIRECTION_LEFT_ONLY: + angle += M_PI; + return std::fabs(util::normalize_angle(angle)) < 0.5 * M_PI; + case DIRECTION_RIGHT_ONLY: + return std::fabs(util::normalize_angle(angle)) < 0.5 * M_PI; + case DIRECTION_RIGHT: + default: + return true; + } + } +}; + +} // namespace mapnik + +#endif // MAPNIK_MARKERS_PLACEMENTS_BASIC_HPP diff --git a/include/mapnik/markers_placements/interior.hpp b/include/mapnik/markers_placements/interior.hpp index 93b5e3418..6a2705e15 100644 --- a/include/mapnik/markers_placements/interior.hpp +++ b/include/mapnik/markers_placements/interior.hpp @@ -33,14 +33,8 @@ template class markers_interior_placement : public markers_point_placement { public: - markers_interior_placement(Locator &locator, Detector &detector, markers_placement_params const& params) - : markers_point_placement(locator, detector, params) - { - } - - markers_interior_placement(markers_interior_placement && rhs) - : markers_point_placement(std::move(rhs)) - {} + using point_placement = markers_point_placement; + using point_placement::point_placement; bool get_point(double &x, double &y, double &angle, bool ignore_placement) { @@ -51,7 +45,7 @@ public: if (this->locator_.type() == geometry::geometry_types::Point) { - return markers_point_placement::get_point(x, y, angle, ignore_placement); + return point_placement::get_point(x, y, angle, ignore_placement); } if (this->locator_.type() == geometry::geometry_types::LineString) @@ -73,20 +67,10 @@ public: angle = 0; - box2d box = this->perform_transform(angle, x, y); - if (this->params_.avoid_edges && !this->detector_.extent().contains(box)) + if (!this->push_to_detector(x, y, angle, ignore_placement)) { return false; } - if (!this->params_.allow_overlap && !this->detector_.has_placement(box)) - { - return false; - } - - if (!ignore_placement) - { - this->detector_.insert(box); - } this->done_ = true; return true; diff --git a/include/mapnik/markers_placements/line.hpp b/include/mapnik/markers_placements/line.hpp index fe2c2bd9f..bc99cc937 100644 --- a/include/mapnik/markers_placements/line.hpp +++ b/include/mapnik/markers_placements/line.hpp @@ -35,29 +35,23 @@ template class markers_line_placement : public markers_point_placement { public: - markers_line_placement(Locator &locator, Detector &detector, markers_placement_params const& params) - : markers_point_placement(locator, detector, params), + using point_placement = markers_point_placement; + using point_placement::point_placement; + + markers_line_placement(Locator & locator, Detector & detector, + markers_placement_params const& params) + : point_placement(locator, detector, params), first_point_(true), spacing_(0.0), marker_width_((params.size * params.tr).width()), path_(locator) { spacing_ = params.spacing < 1 ? 100 : params.spacing; - rewind(); } - markers_line_placement(markers_line_placement && rhs) - : markers_point_placement(std::move(rhs)), - first_point_(std::move(rhs.first_point_)), - spacing_(std::move(rhs.spacing_)), - marker_width_(std::move(rhs.marker_width_)), - path_(std::move(rhs.path_)) - {} - void rewind() { - this->locator_.rewind(0); - this->done_ = false; + point_placement::rewind(); first_point_ = true; } @@ -70,7 +64,7 @@ public: if (this->locator_.type() == geometry::geometry_types::Point) { - return markers_point_placement::get_point(x, y, angle, ignore_placement); + return point_placement::get_point(x, y, angle, ignore_placement); } double move = spacing_; @@ -102,16 +96,10 @@ public: { continue; } - box2d box = this->perform_transform(angle, x, y); - if ((this->params_.avoid_edges && !this->detector_.extent().contains(box)) - || (!this->params_.allow_overlap && !this->detector_.has_placement(box))) + if (!this->push_to_detector(x, y, angle, ignore_placement)) { continue; } - if (!ignore_placement) - { - this->detector_.insert(box); - } return true; } } diff --git a/include/mapnik/markers_placements/point.hpp b/include/mapnik/markers_placements/point.hpp index 13f174d9b..dbbf92c1b 100644 --- a/include/mapnik/markers_placements/point.hpp +++ b/include/mapnik/markers_placements/point.hpp @@ -23,148 +23,54 @@ #ifndef MAPNIK_MARKERS_PLACEMENTS_POINT_HPP #define MAPNIK_MARKERS_PLACEMENTS_POINT_HPP -#include #include #include -#include -#include -#include -#include - -#include "agg_basics.h" -#include "agg_trans_affine.h" - -#include +#include namespace mapnik { -struct markers_placement_params -{ - box2d size; - agg::trans_affine tr; - double spacing; - double max_error; - bool allow_overlap; - bool avoid_edges; - direction_enum direction; -}; - template -class markers_point_placement : util::noncopyable +class markers_point_placement : public markers_basic_placement { public: - markers_point_placement(Locator &locator, Detector &detector, markers_placement_params const& params) - : locator_(locator), - detector_(detector), - params_(params), - done_(false) - { - rewind(); - } - - markers_point_placement(markers_point_placement && rhs) - : locator_(rhs.locator_), - detector_(rhs.detector_), - params_(rhs.params_), - done_(rhs.done_) - {} - - - // Start again at first marker. Returns the same list of markers only works when they were NOT added to the detector. - void rewind() - { - locator_.rewind(0); - done_ = false; - } + using basic_placement = markers_basic_placement; + using basic_placement::basic_placement; // Get next point where the marker should be placed. Returns true if a place is found, false if none is found. bool get_point(double &x, double &y, double &angle, bool ignore_placement) { - if (done_) + if (this->done_) { return false; } - if (locator_.type() == geometry::geometry_types::LineString) + if (this->locator_.type() == geometry::geometry_types::LineString) { - if (!label::middle_point(locator_, x, y)) + if (!label::middle_point(this->locator_, x, y)) { - done_ = true; + this->done_ = true; return false; } } else { - if (!label::centroid(locator_, x, y)) + if (!label::centroid(this->locator_, x, y)) { - done_ = true; + this->done_ = true; return false; } } angle = 0; - box2d box = perform_transform(angle, x, y); - if (params_.avoid_edges && !detector_.extent().contains(box)) - { - return false; - } - if (!params_.allow_overlap && !detector_.has_placement(box)) + if (!this->push_to_detector(x, y, angle, ignore_placement)) { return false; } - if (!ignore_placement) - { - detector_.insert(box); - } - - done_ = true; + this->done_ = true; return true; } - -protected: - Locator &locator_; - Detector &detector_; - markers_placement_params const& params_; - bool done_; - - // Rotates the size_ box and translates the position. - box2d perform_transform(double angle, double dx, double dy) - { - agg::trans_affine tr = params_.tr * agg::trans_affine_rotation(angle).translate(dx, dy); - return box2d(params_.size, tr); - } - - bool set_direction(double & angle) - { - switch (params_.direction) - { - case DIRECTION_UP: - angle = .0; - return true; - case DIRECTION_DOWN: - angle = M_PI; - return true; - case DIRECTION_AUTO: - angle = (std::fabs(util::normalize_angle(angle)) > 0.5 * M_PI) ? (angle + M_PI) : angle; - return true; - case DIRECTION_AUTO_DOWN: - angle = (std::fabs(util::normalize_angle(angle)) < 0.5 * M_PI) ? (angle + M_PI) : angle; - return true; - case DIRECTION_LEFT: - angle += M_PI; - return true; - case DIRECTION_LEFT_ONLY: - angle += M_PI; - return std::fabs(util::normalize_angle(angle)) < 0.5 * M_PI; - case DIRECTION_RIGHT_ONLY: - return std::fabs(util::normalize_angle(angle)) < 0.5 * M_PI; - case DIRECTION_RIGHT: - default: - return true; - } - } }; } diff --git a/include/mapnik/markers_placements/vertext_first.hpp b/include/mapnik/markers_placements/vertext_first.hpp index bcdac2b4b..ce1b415b3 100644 --- a/include/mapnik/markers_placements/vertext_first.hpp +++ b/include/mapnik/markers_placements/vertext_first.hpp @@ -31,14 +31,8 @@ template class markers_vertex_first_placement : public markers_point_placement { public: - markers_vertex_first_placement(Locator &locator, Detector &detector, markers_placement_params const& params) - : markers_point_placement(locator, detector, params) - { - } - - markers_vertex_first_placement(markers_vertex_first_placement && rhs) - : markers_point_placement(std::move(rhs)) - {} + using point_placement = markers_point_placement; + using point_placement::point_placement; bool get_point(double &x, double &y, double &angle, bool ignore_placement) { @@ -49,7 +43,7 @@ public: if (this->locator_.type() == mapnik::geometry::geometry_types::Point) { - return markers_point_placement::get_point(x, y, angle, ignore_placement); + return point_placement::get_point(x, y, angle, ignore_placement); } double x0, y0; @@ -75,20 +69,10 @@ public: } } - box2d box = this->perform_transform(angle, x, y); - if (this->params_.avoid_edges && !this->detector_.extent().contains(box)) + if (!this->push_to_detector(x, y, angle, ignore_placement)) { return false; } - if (!this->params_.allow_overlap && !this->detector_.has_placement(box)) - { - return false; - } - - if (!ignore_placement) - { - this->detector_.insert(box); - } this->done_ = true; return true; diff --git a/include/mapnik/markers_placements/vertext_last.hpp b/include/mapnik/markers_placements/vertext_last.hpp index 4d57130a2..62c94a66a 100644 --- a/include/mapnik/markers_placements/vertext_last.hpp +++ b/include/mapnik/markers_placements/vertext_last.hpp @@ -31,13 +31,8 @@ template class markers_vertex_last_placement : public markers_point_placement { public: - markers_vertex_last_placement(Locator &locator, Detector &detector, markers_placement_params const& params) - : markers_point_placement(locator, detector, params) - {} - - markers_vertex_last_placement(markers_vertex_last_placement && rhs) - : markers_point_placement(std::move(rhs)) - {} + using point_placement = markers_point_placement; + using point_placement::point_placement; bool get_point(double &x, double &y, double &angle, bool ignore_placement) { @@ -80,20 +75,10 @@ public: } } - box2d box = this->perform_transform(angle, x, y); - if (this->params_.avoid_edges && !this->detector_.extent().contains(box)) + if (!this->push_to_detector(x, y, angle, ignore_placement)) { return false; } - if (!this->params_.allow_overlap && !this->detector_.has_placement(box)) - { - return false; - } - - if (!ignore_placement) - { - this->detector_.insert(box); - } this->done_ = true; return true; diff --git a/include/mapnik/text/symbolizer_helpers.hpp b/include/mapnik/text/symbolizer_helpers.hpp index 8a44bddfc..6d6fbdc49 100644 --- a/include/mapnik/text/symbolizer_helpers.hpp +++ b/include/mapnik/text/symbolizer_helpers.hpp @@ -59,7 +59,7 @@ struct placement_finder_adapter }; -using vertex_converter_type = vertex_converter; +using vertex_converter_type = vertex_converter; class base_symbolizer_helper { diff --git a/include/mapnik/unicode.hpp b/include/mapnik/unicode.hpp index 59401abfa..f3b270cb9 100644 --- a/include/mapnik/unicode.hpp +++ b/include/mapnik/unicode.hpp @@ -45,6 +45,10 @@ public: private: UConverter * conv_; }; + +// convinience method +void MAPNIK_DECL to_utf8(mapnik::value_unicode_string const& input, std::string & target); + } #endif // MAPNIK_UNICODE_HPP diff --git a/include/mapnik/value.hpp b/include/mapnik/value.hpp index c6a95d3e0..3bd59b4b7 100644 --- a/include/mapnik/value.hpp +++ b/include/mapnik/value.hpp @@ -24,805 +24,25 @@ #define MAPNIK_VALUE_HPP // mapnik +#include #include #include -#include #include -// stl -#include -#include -#include - -#include -#include -#include -#include - -// icu -#include -#include namespace mapnik { using value_base = util::variant; -inline void to_utf8(mapnik::value_unicode_string const& input, std::string & target) -{ - target.clear(); // mimic previous target.assign(...) semantics - input.toUTF8String(target); // this appends to target -} - -namespace detail { - -namespace { -template -struct both_arithmetic : std::integral_constant::value && - std::is_arithmetic::value > {}; - -struct equals -{ - static bool apply(value_null, value_unicode_string const& rhs) - { - return false; - } - - template - static auto apply(T const& lhs, T const& rhs) - -> decltype(lhs == rhs) - { - return lhs == rhs; - } -}; - -struct not_equal -{ - // back compatibility shim to equate empty string with null for != test - // https://github.com/mapnik/mapnik/issues/1859 - // TODO - consider removing entire specialization at Mapnik 3.1.x - static bool apply(value_null, value_unicode_string const& rhs) - { - if (rhs.isEmpty()) return false; - return true; - } - - template - static auto apply(T const& lhs, T const& rhs) - ->decltype(lhs != rhs) - { - return lhs != rhs; - } -}; - -struct greater_than -{ - static bool apply(value_null, value_unicode_string const& rhs) - { - return false; - } - - template - static auto apply(T const& lhs, T const& rhs) - ->decltype(lhs > rhs) - { - return lhs > rhs; - } -}; - -struct greater_or_equal -{ - static bool apply(value_null, value_unicode_string const& rhs) - { - return false; - } - - template - static auto apply(T const& lhs, T const& rhs) - ->decltype(lhs >= rhs) - { - return lhs >= rhs; - } -}; - -struct less_than -{ - static bool apply(value_null, value_unicode_string const& rhs) - { - return false; - } - - template - static auto apply(T const& lhs, T const& rhs) - ->decltype(lhs < rhs) - { - return lhs < rhs; - } -}; - -struct less_or_equal -{ - static bool apply(value_null, value_unicode_string const& rhs) - { - return false; - } - - template - static auto apply(T const& lhs, T const& rhs) - ->decltype(lhs <= rhs) - { - return lhs <= rhs; - } -}; - -} - -template -struct comparison -{ - // special case for unicode_strings (fixes MSVC C4800) - bool operator() (value_unicode_string const& lhs, - value_unicode_string const& rhs) const - { - return Op::apply(lhs, rhs) ? true : false; - } - - ////////////////////////////////////////////////////////////////////////// - // special case for unicode_string and value_null - ////////////////////////////////////////////////////////////////////////// - - bool operator() (value_null const& lhs, value_unicode_string const& rhs) const - { - return Op::apply(lhs, rhs); - } - ////////////////////////////////////////////////////////////////////////// - - - // same types - template - bool operator() (T lhs, T rhs) const - { - return Op::apply(lhs, rhs); - } - - // both types are arithmetic - promote to the common type - template ::value, int>::type = 0> - bool operator() (T const& lhs, U const& rhs) const - { - using common_type = typename std::common_type::type; - return Op::apply(static_cast(lhs),static_cast(rhs)); - } - - // - template ::value, int>::type = 0> - bool operator() (T const& lhs, U const& rhs) const - { - return default_result; - } -}; - -template -struct add -{ - using value_type = V; - value_type operator() (value_unicode_string const& lhs , - value_unicode_string const& rhs ) const - { - return lhs + rhs; - } - - value_type operator() (value_null const& lhs , - value_null const& rhs) const - { - return lhs; - } - - value_type operator() (value_unicode_string const& lhs, value_null) const - { - return lhs; - } - - value_type operator() (value_null, value_unicode_string const& rhs) const - { - return rhs; - } - - template - value_type operator() (L const& lhs, value_null const&) const - { - return lhs; - } - - template - value_type operator() (value_null const&, R const& rhs) const - { - return rhs; - } - - template - value_type operator() (L const& lhs , value_unicode_string const& rhs) const - { - std::string val; - if (util::to_string(val,lhs)) - return value_unicode_string(val.c_str()) + rhs; - return rhs; - } - - template - value_type operator() (value_unicode_string const& lhs, R const& rhs) const - { - std::string val; - if (util::to_string(val,rhs)) - return lhs + value_unicode_string(val.c_str()); - return lhs; - } - - template - value_type operator() (T1 const& lhs, T2 const& rhs) const - { - return typename std::common_type::type{ lhs + rhs }; - } - - value_type operator() (value_bool lhs, value_bool rhs) const - { - return value_integer(lhs + rhs); - } -}; - -template -struct sub -{ - using value_type = V; - - value_type operator() (value_null const& lhs , - value_null const& rhs) const - { - return lhs; - } - - value_type operator() (value_null, value_unicode_string const& rhs) const - { - return rhs; - } - value_type operator() (value_unicode_string const& lhs, value_null) const - { - return lhs; - } - - template - value_type operator() (value_unicode_string const& lhs, R const&) const - { - return lhs; - } - - template - value_type operator() (L const&, value_unicode_string const& rhs) const - { - return rhs; - } - - template - value_type operator() (L const& lhs, value_null const&) const - { - return lhs; - } - - template - value_type operator() (value_null const&, R const& rhs) const - { - return rhs; - } - - template - value_type operator() (T lhs, T rhs) const - { - return lhs - rhs ; - } - - value_type operator() (value_unicode_string const&, - value_unicode_string const&) const - { - return value_type(); - } - - template - value_type operator() (T1 const& lhs, T2 const& rhs) const - { - return typename std::common_type::type{ lhs - rhs }; - } - - - value_type operator() (value_bool lhs, value_bool rhs) const - { - return value_integer(lhs - rhs); - } -}; - -template -struct mult -{ - using value_type = V; - - value_type operator() (value_null const& lhs , - value_null const& rhs) const - { - return lhs; - } - - value_type operator() (value_unicode_string const& lhs, value_null) const - { - return lhs; - } - - value_type operator() (value_null, value_unicode_string const& rhs) const - { - return rhs; - } - - template - value_type operator() (L const& lhs, value_null const&) const - { - return lhs; - } - - template - value_type operator() (value_null const&, R const& rhs) const - { - return rhs; - } - - template - value_type operator() (value_unicode_string const& lhs, R const&) const - { - return lhs; - } - - template - value_type operator() (L const&, value_unicode_string const& rhs) const - { - return rhs; - } - - template - value_type operator() (T lhs, T rhs) const - { - return lhs * rhs; - } - - value_type operator() (value_unicode_string const&, - value_unicode_string const&) const - { - return value_type(); - } - - template - value_type operator() (T1 const& lhs, T2 const& rhs) const - { - return typename std::common_type::type{ lhs * rhs }; - } - - value_type operator() (value_bool lhs, value_bool rhs) const - { - return value_integer(lhs * rhs); - } -}; - -template -struct div -{ - using value_type = V; - - value_type operator() (value_null const& lhs , - value_null const& rhs) const - { - return lhs; - } - - value_type operator() (value_unicode_string const& lhs, value_null) const - { - return lhs; - } - - value_type operator() (value_null, value_unicode_string const& rhs) const - { - return rhs; - } - - template - value_type operator() (L const& lhs, value_null const&) const - { - return lhs; - } - - template - value_type operator() (value_null const&, R const& rhs) const - { - return rhs; - } - - template - value_type operator() (T lhs, T rhs) const - { - if (rhs == 0) return value_type(); - return lhs / rhs; - } - - value_type operator() (value_bool lhs, value_bool rhs) const - { - if (rhs == 0) return lhs; - return value_integer(lhs) / value_integer(rhs); - } - - value_type operator() (value_unicode_string const&, - value_unicode_string const&) const - { - return value_type(); - } - - template - value_type operator() (value_unicode_string const& lhs, R const&) const - { - return lhs; - } - - template - value_type operator() (L const&, value_unicode_string const& rhs) const - { - return rhs; - } - - template - value_type operator() (T1 const& lhs, T2 const& rhs) const - { - if (rhs == 0) return value_type(); - using common_type = typename std::common_type::type; - return common_type(lhs)/common_type(rhs); - } -}; - -template -struct mod -{ - using value_type = V; - - template - value_type operator() (T1 const& lhs, T2 const&) const - { - return lhs; - } - - template - value_type operator() (T lhs, T rhs) const - { - return lhs % rhs; - } - - value_type operator() (value_unicode_string const&, - value_unicode_string const&) const - { - return value_type(); - } - - value_type operator() (value_bool, - value_bool) const - { - return false; - } - - value_type operator() (value_double lhs, value_integer rhs) const - { - return std::fmod(lhs, static_cast(rhs)); - } - - value_type operator() (value_integer lhs, value_double rhs) const - { - return std::fmod(static_cast(lhs), rhs); - } - - value_type operator() (value_double lhs, value_double rhs) const - { - return std::fmod(lhs, rhs); - } -}; - -template -struct negate -{ - using value_type = V; - - template - value_type operator() (T val) const - { - return -val; - } - - value_type operator() (value_null val) const - { - return val; - } - - value_type operator() (value_bool val) const - { - return val ? value_integer(-1) : value_integer(0); - } - - value_type operator() (value_unicode_string const&) const - { - return value_type(); - } -}; - -// converters -template -struct convert {}; - -template <> -struct convert -{ - value_bool operator() (value_bool val) const - { - return val; - } - - value_bool operator() (value_unicode_string const& ustr) const - { - return !ustr.isEmpty(); - } - - value_bool operator() (value_null const&) const - { - return false; - } - - template - value_bool operator() (T val) const - { - return val > 0 ? true : false; - } -}; - -template <> -struct convert -{ - value_double operator() (value_double val) const - { - return val; - } - - value_double operator() (value_integer val) const - { - return static_cast(val); - } - - value_double operator() (value_bool val) const - { - return static_cast(val); - } - - value_double operator() (std::string const& val) const - { - value_double result; - if (util::string2double(val,result)) - return result; - return 0; - } - - value_double operator() (value_unicode_string const& val) const - { - std::string utf8; - val.toUTF8String(utf8); - return operator()(utf8); - } - - value_double operator() (value_null const&) const - { - return 0.0; - } -}; - -template <> -struct convert -{ - value_integer operator() (value_integer val) const - { - return val; - } - - value_integer operator() (value_double val) const - { - return static_cast(rint(val)); - } - - value_integer operator() (value_bool val) const - { - return static_cast(val); - } - - value_integer operator() (std::string const& val) const - { - value_integer result; - if (util::string2int(val,result)) - return result; - return value_integer(0); - } - - value_integer operator() (value_unicode_string const& val) const - { - std::string utf8; - val.toUTF8String(utf8); - return operator()(utf8); - } - - value_integer operator() (value_null const&) const - { - return value_integer(0); - } -}; - -template <> -struct convert -{ - template - std::string operator() (T val) const - { - std::string str; - util::to_string(str, val); - return str; - } - - // specializations - std::string operator() (value_unicode_string const& val) const - { - std::string utf8; - val.toUTF8String(utf8); - return utf8; - } - - std::string operator() (value_double val) const - { - std::string str; - util::to_string(str, val); // TODO set precision(16) - return str; - } - - std::string operator() (value_bool val) const - { - return val ? "true": "false"; - } - - std::string operator() (value_null const&) const - { - return std::string(); - } -}; - -struct to_unicode_impl -{ - - template - value_unicode_string operator() (T val) const - { - std::string str; - util::to_string(str,val); - return value_unicode_string(str.c_str()); - } - - // specializations - value_unicode_string const& operator() (value_unicode_string const& val) const - { - return val; - } - - value_unicode_string operator() (value_double val) const - { - std::string str; - util::to_string(str,val); - return value_unicode_string(str.c_str()); - } - - value_unicode_string operator() (value_bool val) const - { - return value_unicode_string(val ? "true" : "false"); - } - - value_unicode_string operator() (value_null const&) const - { - return value_unicode_string(); - } -}; - -struct to_expression_string_impl -{ - struct EscapingByteSink : U_NAMESPACE_QUALIFIER ByteSink - { - std::string dest_; - char quote_; - - explicit EscapingByteSink(char quote) - : quote_(quote) - {} - - virtual void Append(const char* data, int32_t n) - { - // reserve enough room to hold the appended chunk and quotes; - // if another chunk follows, or any character needs escaping, - // the string will grow naturally - if (dest_.empty()) - { - dest_.reserve(2 + static_cast(n)); - dest_.append(1, quote_); - } - else - { - dest_.reserve(dest_.size() + n + 1); - } - - for (auto end = data + n; data < end; ++data) - { - if (*data == '\\' || *data == quote_) - dest_.append(1, '\\'); - dest_.append(1, *data); - } - } - - virtual void Flush() - { - if (dest_.empty()) - dest_.append(2, quote_); - else - dest_.append(1, quote_); - } - }; - - explicit to_expression_string_impl(char quote = '\'') - : quote_(quote) {} - - std::string operator() (value_unicode_string const& val) const - { - EscapingByteSink sink(quote_); - val.toUTF8(sink); - return sink.dest_; - } - - std::string operator() (value_integer val) const - { - std::string output; - util::to_string(output,val); - return output; - } - - std::string operator() (value_double val) const - { - std::string output; - util::to_string(output,val); // TODO precision(16) - return output; - } - - std::string operator() (value_bool val) const - { - return val ? "true" : "false"; - } - - std::string operator() (value_null const&) const - { - return "null"; - } - - const char quote_; -}; - - -} // namespace detail - namespace value_adl_barrier { -class value : public value_base +class MAPNIK_DECL value : public value_base { - friend const value operator+(value const&,value const&); - friend const value operator-(value const&,value const&); - friend const value operator*(value const&,value const&); - friend const value operator/(value const&,value const&); - friend const value operator%(value const&,value const&); + friend MAPNIK_DECL value operator+(value const&,value const&); + friend MAPNIK_DECL value operator-(value const&,value const&); + friend MAPNIK_DECL value operator*(value const&,value const&); + friend MAPNIK_DECL value operator/(value const&,value const&); + friend MAPNIK_DECL value operator%(value const&,value const&); public: value() = default; @@ -856,104 +76,32 @@ public: return *this; } - bool operator==(value const& other) const - { - return util::apply_visitor(detail::comparison(), *this, other); - } + bool operator==(value const& other) const; + bool operator!=(value const& other) const; + bool operator>(value const& other) const; + bool operator>=(value const& other) const; + bool operator<(value const& other) const; + bool operator<=(value const& other) const; - bool operator!=(value const& other) const - { - return util::apply_visitor(detail::comparison(), *this, other); - } - - bool operator>(value const& other) const - { - return util::apply_visitor(detail::comparison(), *this, other); - } - - bool operator>=(value const& other) const - { - return util::apply_visitor(detail::comparison(), *this, other); - } - - bool operator<(value const& other) const - { - return util::apply_visitor(detail::comparison(), *this, other); - } - - bool operator<=(value const& other) const - { - return util::apply_visitor(detail::comparison(), *this, other); - } - - value operator- () const - { - return util::apply_visitor(detail::negate(), *this); - } + value operator-() const; bool is_null() const; - template - T convert() const - { - return util::apply_visitor(detail::convert(),*this); - } + template T convert() const; - value_bool to_bool() const - { - return util::apply_visitor(detail::convert(),*this); - } - - std::string to_expression_string(char quote = '\'') const - { - return util::apply_visitor(detail::to_expression_string_impl(quote),*this); - } - - std::string to_string() const - { - return util::apply_visitor(detail::convert(),*this); - } - - value_unicode_string to_unicode() const - { - return util::apply_visitor(detail::to_unicode_impl(),*this); - } - - value_double to_double() const - { - return util::apply_visitor(detail::convert(),*this); - } - - value_integer to_int() const - { - return util::apply_visitor(detail::convert(),*this); - } + value_bool to_bool() const; + std::string to_expression_string(char quote = '\'') const; + std::string to_string() const; + value_unicode_string to_unicode() const; + value_double to_double() const; + value_integer to_int() const; }; -inline const value operator+(value const& p1,value const& p2) -{ - return value(util::apply_visitor(detail::add(),p1, p2)); -} - -inline const value operator-(value const& p1,value const& p2) -{ - return value(util::apply_visitor(detail::sub(),p1, p2)); -} - -inline const value operator*(value const& p1,value const& p2) -{ - return value(util::apply_visitor(detail::mult(),p1, p2)); -} - -inline const value operator/(value const& p1,value const& p2) -{ - return value(util::apply_visitor(detail::div(),p1, p2)); -} - -inline const value operator%(value const& p1,value const& p2) -{ - return value(util::apply_visitor(detail::mod(),p1, p2)); -} +MAPNIK_DECL value operator+(value const& p1,value const& p2); +MAPNIK_DECL value operator-(value const& p1,value const& p2); +MAPNIK_DECL value operator*(value const& p1,value const& p2); +MAPNIK_DECL value operator/(value const& p1,value const& p2); +MAPNIK_DECL value operator%(value const& p1,value const& p2); template inline std::basic_ostream& @@ -972,36 +120,28 @@ inline std::size_t hash_value(value const& val) } // namespace value_adl_barrier -using value_adl_barrier::value; +using value = value_adl_barrier::value; namespace detail { - struct is_null_visitor { - bool operator() (value const& val) const + bool operator()(value const& val) const { return val.is_null(); } - bool operator() (value_null const&) const + bool operator()(value_null const&) const { return true; } template - bool operator() (T const&) const + bool operator()(T const&) const { return false; } }; - } // namespace detail - -inline bool value::is_null() const -{ - return util::apply_visitor(mapnik::detail::is_null_visitor(), *this); -} - } // namespace mapnik // support for std::unordered_xxx diff --git a/include/mapnik/version.hpp b/include/mapnik/version.hpp index a68d8b5cb..a105bec5b 100644 --- a/include/mapnik/version.hpp +++ b/include/mapnik/version.hpp @@ -25,7 +25,7 @@ #define MAPNIK_MAJOR_VERSION 3 #define MAPNIK_MINOR_VERSION 0 -#define MAPNIK_PATCH_VERSION 9 +#define MAPNIK_PATCH_VERSION 10 #define MAPNIK_VERSION (MAPNIK_MAJOR_VERSION*100000) + (MAPNIK_MINOR_VERSION*100) + (MAPNIK_PATCH_VERSION) diff --git a/include/mapnik/vertex_adapters.hpp b/include/mapnik/vertex_adapters.hpp index ae5f0d4a9..3a41beee0 100644 --- a/include/mapnik/vertex_adapters.hpp +++ b/include/mapnik/vertex_adapters.hpp @@ -34,32 +34,10 @@ struct point_vertex_adapter { using value_type = typename point::value_type; - point_vertex_adapter(point const& pt) - : pt_(pt), - first_(true) {} - - unsigned vertex(value_type * x, value_type * y) const - { - if (first_) - { - *x = pt_.x; - *y = pt_.y; - first_ = false; - return mapnik::SEG_MOVETO; - } - return mapnik::SEG_END; - } - - void rewind(unsigned) const - { - first_ = true; - } - - inline geometry_types type () const - { - return geometry_types::Point; - } - + point_vertex_adapter(point const& pt); + unsigned vertex(value_type * x, value_type * y) const; + void rewind(unsigned) const; + geometry_types type () const; point const& pt_; mutable bool first_; }; @@ -68,110 +46,23 @@ template struct line_string_vertex_adapter { using value_type = typename point::value_type; - line_string_vertex_adapter(line_string const& line) - : line_(line), - current_index_(0), - end_index_(line.size()) - {} - - unsigned vertex(value_type * x, value_type * y) const - { - if (current_index_ != end_index_) - { - point const& coord = line_[current_index_++]; - *x = coord.x; - *y = coord.y; - if (current_index_ == 1) - { - return mapnik::SEG_MOVETO; - } - else - { - return mapnik::SEG_LINETO; - } - } - return mapnik::SEG_END; - } - - void rewind(unsigned) const - { - current_index_ = 0; - } - - inline geometry_types type () const - { - return geometry_types::LineString; - } - + line_string_vertex_adapter(line_string const& line); + unsigned vertex(value_type * x, value_type * y) const; + void rewind(unsigned) const; + geometry_types type () const; line_string const& line_; mutable std::size_t current_index_; - const std::size_t end_index_; - + const std::size_t end_index_; }; template struct polygon_vertex_adapter { using value_type = typename point::value_type; - polygon_vertex_adapter(polygon const& poly) - : poly_(poly), - rings_itr_(0), - rings_end_(poly_.interior_rings.size() + 1), - current_index_(0), - end_index_((rings_itr_ < rings_end_) ? poly_.exterior_ring.size() : 0), - start_loop_(true) {} - - void rewind(unsigned) const - { - rings_itr_ = 0; - rings_end_ = poly_.interior_rings.size() + 1; - current_index_ = 0; - end_index_ = (rings_itr_ < rings_end_) ? poly_.exterior_ring.size() : 0; - start_loop_ = true; - } - - unsigned vertex(value_type * x, value_type * y) const - { - if (rings_itr_ == rings_end_) - { - return mapnik::SEG_END; - } - if (current_index_ < end_index_) - { - point const& coord = (rings_itr_ == 0) ? - poly_.exterior_ring[current_index_++] : poly_.interior_rings[rings_itr_- 1][current_index_++]; - *x = coord.x; - *y = coord.y; - if (start_loop_) - { - start_loop_= false; - return mapnik::SEG_MOVETO; - } - if (current_index_ == end_index_) - { - *x = 0; - *y = 0; - return mapnik::SEG_CLOSE; - } - return mapnik::SEG_LINETO; - } - else if (++rings_itr_ != rings_end_) - { - current_index_ = 0; - end_index_ = poly_.interior_rings[rings_itr_ - 1].size(); - point const& coord = poly_.interior_rings[rings_itr_ - 1][current_index_++]; - *x = coord.x; - *y = coord.y; - return mapnik::SEG_MOVETO; - } - return mapnik::SEG_END; - } - - inline geometry_types type () const - { - return geometry_types::Polygon; - } - + polygon_vertex_adapter(polygon const& poly); + void rewind(unsigned) const; + unsigned vertex(value_type * x, value_type * y) const; + geometry_types type () const; private: polygon const& poly_; mutable std::size_t rings_itr_; @@ -185,47 +76,10 @@ template struct ring_vertex_adapter { using value_type = typename point::value_type; - ring_vertex_adapter(linear_ring const& ring) - : ring_(ring), - current_index_(0), - end_index_(ring_.size()), - start_loop_(true) {} - - void rewind(unsigned) const - { - current_index_ = 0; - end_index_ = ring_.size(); - start_loop_ = true; - } - - unsigned vertex(value_type * x, value_type * y) const - { - if (current_index_ < end_index_) - { - auto const& coord = ring_[current_index_++]; - *x = coord.x; - *y = coord.y; - if (start_loop_) - { - start_loop_= false; - return mapnik::SEG_MOVETO; - } - if (current_index_ == end_index_) - { - *x = 0; - *y = 0; - return mapnik::SEG_CLOSE; - } - return mapnik::SEG_LINETO; - } - return mapnik::SEG_END; - } - - inline geometry_types type () const - { - return geometry_types::Polygon; - } - + ring_vertex_adapter(linear_ring const& ring); + void rewind(unsigned) const; + unsigned vertex(value_type * x, value_type * y) const; + geometry_types type () const; private: linear_ring const& ring_; mutable std::size_t current_index_; @@ -233,6 +87,11 @@ private: mutable bool start_loop_; }; +extern template struct MAPNIK_DECL point_vertex_adapter; +extern template struct MAPNIK_DECL line_string_vertex_adapter; +extern template struct MAPNIK_DECL polygon_vertex_adapter; +extern template struct MAPNIK_DECL ring_vertex_adapter; + template struct vertex_adapter_traits {}; diff --git a/include/mapnik/vertex_converters.hpp b/include/mapnik/vertex_converters.hpp index f10f720dc..d9ec9d0f8 100644 --- a/include/mapnik/vertex_converters.hpp +++ b/include/mapnik/vertex_converters.hpp @@ -275,7 +275,7 @@ template struct converters_helper; template -struct converters_helper +struct converters_helper { template static void set(Dispatcher & disp, std::size_t state) diff --git a/plugins/input/geojson/geojson_datasource.cpp b/plugins/input/geojson/geojson_datasource.cpp index dbb80f95b..d63e24316 100644 --- a/plugins/input/geojson/geojson_datasource.cpp +++ b/plugins/input/geojson/geojson_datasource.cpp @@ -200,6 +200,20 @@ const mapnik::json::feature_grammar ge const mapnik::json::extract_bounding_box_grammar geojson_datasource_static_bbox_grammar; } +void geojson_datasource::initialise_descriptor(mapnik::feature_ptr const& feature) +{ + for ( auto const& kv : *feature) + { + auto const& name = std::get<0>(kv); + if (!desc_.has_name(name)) + { + desc_.add_descriptor(mapnik::attribute_descriptor(name, + mapnik::util::apply_visitor(attr_value_converter(), + std::get<1>(kv)))); + } + } +} + void geojson_datasource::initialise_disk_index(std::string const& filename) { // read extent @@ -213,7 +227,7 @@ void geojson_datasource::initialise_disk_index(std::string const& filename) std::vector positions; mapnik::util::spatial_index::query_first_n(filter, index, positions, 5); + std::ifstream>::query_first_n(filter, index, positions, num_features_to_query_); mapnik::util::file file(filename_); if (!file.open()) throw mapnik::datasource_exception("GeoJSON Plugin: could not open: '" + filename_ + "'"); @@ -236,16 +250,7 @@ void geojson_datasource::initialise_disk_index(std::string const& filename) { throw std::runtime_error("Failed to parse geojson feature"); } - for ( auto const& kv : *feature) - { - auto const& name = std::get<0>(kv); - if (!desc_.has_name(name)) - { - desc_.add_descriptor(mapnik::attribute_descriptor(name, - mapnik::util::apply_visitor(attr_value_converter(), - std::get<1>(kv)))); - } - } + initialise_descriptor(feature); } } @@ -285,20 +290,18 @@ void geojson_datasource::initialise_index(Iterator start, Iterator end) if (geometry_index == 0) { extent_ = box; - for ( auto const& kv : *f) - { - desc_.add_descriptor(mapnik::attribute_descriptor(std::get<0>(kv), - mapnik::util::apply_visitor(attr_value_converter(), - std::get<1>(kv)))); - } } else { extent_.expand_to_include(box); } values.emplace_back(box, std::make_pair(geometry_index,0)); + + } + if (geometry_index++ < num_features_to_query_) + { + initialise_descriptor(f); } - ++geometry_index; } // packing algorithm tree_ = std::make_unique(values); @@ -308,14 +311,16 @@ void geojson_datasource::initialise_index(Iterator start, Iterator end) // bulk insert initialise r-tree tree_ = std::make_unique(boxes); // calculate total extent + std::size_t feature_count = 0; for (auto const& item : boxes) { auto const& box = std::get<0>(item); auto const& geometry_index = std::get<1>(item); - if (!extent_.valid()) + if (!extent_.valid()) extent_ = box; + else extent_.expand_to_include(box); + if (feature_count++ < num_features_to_query_) { - extent_ = box; - // parse first feature to extract attributes schema. + // parse first N features to extract attributes schema. // NOTE: this doesn't yield correct answer for geoJSON in general, just an indication Iterator itr2 = start + geometry_index.first; Iterator end2 = itr2 + geometry_index.second; @@ -327,16 +332,8 @@ void geojson_datasource::initialise_index(Iterator start, Iterator end) { throw std::runtime_error("Failed to parse geojson feature"); } - for ( auto const& kv : *feature) - { - desc_.add_descriptor(mapnik::attribute_descriptor(std::get<0>(kv), - mapnik::util::apply_visitor(attr_value_converter(), - std::get<1>(kv)))); - } - } - else - { - extent_.expand_to_include(box); + + initialise_descriptor(feature); } } } @@ -399,12 +396,7 @@ void geojson_datasource::parse_geojson(Iterator start, Iterator end) if (geometry_index == 0) { extent_ = box; - for ( auto const& kv : *f) - { - desc_.add_descriptor(mapnik::attribute_descriptor(std::get<0>(kv), - mapnik::util::apply_visitor(attr_value_converter(), - std::get<1>(kv)))); - } + } else { @@ -412,6 +404,10 @@ void geojson_datasource::parse_geojson(Iterator start, Iterator end) } values.emplace_back(box, std::make_pair(geometry_index,0)); } + if (geometry_index < num_features_to_query_) + { + initialise_descriptor(f); + } ++geometry_index; } // packing algorithm @@ -419,7 +415,7 @@ void geojson_datasource::parse_geojson(Iterator start, Iterator end) } -geojson_datasource::~geojson_datasource() { } +geojson_datasource::~geojson_datasource() {} const char * geojson_datasource::name() { @@ -454,7 +450,7 @@ boost::optional geojson_datasource::get_geometry_ std::vector positions; mapnik::util::spatial_index::query_first_n(filter, index, positions, 5); + std::ifstream>::query_first_n(filter, index, positions, num_features_to_query_); mapnik::util::file file(filename_); @@ -494,7 +490,7 @@ boost::optional geojson_datasource::get_geometry_ else if (cache_features_) { unsigned num_features = features_.size(); - for (unsigned i = 0; i < num_features && i < 5; ++i) + for (unsigned i = 0; i < num_features && i < num_features_to_query_; ++i) { result = mapnik::util::to_ds_type(features_[i]->get_geometry()); if (result) @@ -519,7 +515,7 @@ boost::optional geojson_datasource::get_geometry_ auto itr = tree_->qbegin(boost::geometry::index::intersects(extent_)); auto end = tree_->qend(); mapnik::context_ptr ctx = std::make_shared(); - for (std::size_t count = 0; itr !=end && count < 5; ++itr,++count) + for (std::size_t count = 0; itr !=end && count < num_features_to_query_; ++itr,++count) { geojson_datasource::item_type const& item = *itr; std::size_t file_offset = item.second.first; diff --git a/plugins/input/geojson/geojson_datasource.hpp b/plugins/input/geojson/geojson_datasource.hpp index 66f338337..a50bd9567 100644 --- a/plugins/input/geojson/geojson_datasource.hpp +++ b/plugins/input/geojson/geojson_datasource.hpp @@ -94,6 +94,7 @@ public: void initialise_index(Iterator start, Iterator end); void initialise_disk_index(std::string const& filename); private: + void initialise_descriptor(mapnik::feature_ptr const&); mapnik::datasource::datasource_t type_; mapnik::layer_descriptor desc_; std::string filename_; @@ -103,6 +104,7 @@ private: std::unique_ptr tree_; bool cache_features_ = true; bool has_disk_index_ = false; + const std::size_t num_features_to_query_ = 5; }; diff --git a/scripts/build-appveyor.bat b/scripts/build-appveyor.bat index 3c8b3d05b..3fe8cd064 100644 --- a/scripts/build-appveyor.bat +++ b/scripts/build-appveyor.bat @@ -22,6 +22,8 @@ ECHO msvs_toolset^: %msvs_toolset% SET BUILD_TYPE=%configuration% SET BUILDPLATFORM=%platform% SET TOOLS_VERSION=%msvs_toolset%.0 +SET ICU_VERSION=56.1 +ECHO ICU_VERSION^: %ICU_VERSION% IF DEFINED APPVEYOR (ECHO on AppVeyor) ELSE (ECHO NOT on AppVeyor) ECHO ======== diff --git a/src/build.py b/src/build.py index c405eb151..1568e9c34 100644 --- a/src/build.py +++ b/src/build.py @@ -221,6 +221,7 @@ source = Split( warp.cpp css_color_grammar.cpp vertex_cache.cpp + vertex_adapters.cpp text/font_library.cpp text/text_layout.cpp text/text_line.cpp @@ -257,6 +258,7 @@ source = Split( renderer_common/render_pattern.cpp renderer_common/render_thunk_extractor.cpp math.cpp + value.cpp """ ) diff --git a/src/marker_helpers.cpp b/src/marker_helpers.cpp index cf37cdbe4..9baa546b8 100644 --- a/src/marker_helpers.cpp +++ b/src/marker_helpers.cpp @@ -23,7 +23,7 @@ // mapnik #include #include - +#include #include "agg_ellipse.h" #include "agg_color_rgba.h" @@ -158,5 +158,142 @@ void setup_transform_scaling(agg::trans_affine & tr, } } +template +void apply_markers_single(vertex_converter_type & converter, Processor & proc, + geometry::geometry const& geom, geometry::geometry_types type) +{ + if (type == geometry::geometry_types::Point) + { + geometry::point_vertex_adapter va(geom.get>()); + converter.apply(va, proc); + } + else if (type == geometry::geometry_types::LineString) + { + geometry::line_string_vertex_adapter va(geom.get>()); + converter.apply(va, proc); + } + else if (type == geometry::geometry_types::Polygon) + { + geometry::polygon_vertex_adapter va(geom.get>()); + converter.apply(va, proc); + } + else if (type == geometry::geometry_types::MultiPoint) + { + for (auto const& pt : geom.get>()) + { + geometry::point_vertex_adapter va(pt); + converter.apply(va, proc); + } + } + else if (type == geometry::geometry_types::MultiLineString) + { + for (auto const& line : geom.get>()) + { + geometry::line_string_vertex_adapter va(line); + converter.apply(va, proc); + } + } + else if (type == geometry::geometry_types::MultiPolygon) + { + for (auto const& poly : geom.get>()) + { + geometry::polygon_vertex_adapter va(poly); + converter.apply(va, proc); + } + } +} + +template +void apply_markers_multi(feature_impl const& feature, attributes const& vars, + vertex_converter_type & converter, Processor & proc, symbolizer_base const& sym) +{ + auto const& geom = feature.get_geometry(); + geometry::geometry_types type = geometry::geometry_type(geom); + + if (type == geometry::geometry_types::Point + || + type == geometry::geometry_types::LineString + || + type == geometry::geometry_types::Polygon) + { + apply_markers_single(converter, proc, geom, type); + } + else + { + + marker_multi_policy_enum multi_policy = get(sym, feature, vars); + marker_placement_enum placement = get(sym, feature, vars); + + if (placement == MARKER_POINT_PLACEMENT && + multi_policy == MARKER_WHOLE_MULTI) + { + geometry::point pt; + // test if centroid is contained by bounding box + if (geometry::centroid(geom, pt) && converter.disp_.args_.bbox.contains(pt.x, pt.y)) + { + // unset any clipping since we're now dealing with a point + converter.template unset(); + geometry::point_vertex_adapter va(pt); + converter.apply(va, proc); + } + } + else if ((placement == MARKER_POINT_PLACEMENT || placement == MARKER_INTERIOR_PLACEMENT) && + multi_policy == MARKER_LARGEST_MULTI) + { + // Only apply to path with largest envelope area + // TODO: consider using true area for polygon types + if (type == geometry::geometry_types::MultiPolygon) + { + geometry::multi_polygon const& multi_poly = mapnik::util::get >(geom); + double maxarea = 0; + geometry::polygon const* largest = 0; + for (geometry::polygon const& poly : multi_poly) + { + box2d bbox = geometry::envelope(poly); + double area = bbox.width() * bbox.height(); + if (area > maxarea) + { + maxarea = area; + largest = &poly; + } + } + if (largest) + { + geometry::polygon_vertex_adapter va(*largest); + converter.apply(va, proc); + } + } + else + { + MAPNIK_LOG_WARN(marker_symbolizer) << "TODO: if you get here -> open an issue"; + } + } + else + { + if (multi_policy != MARKER_EACH_MULTI && placement != MARKER_POINT_PLACEMENT) + { + MAPNIK_LOG_WARN(marker_symbolizer) << "marker_multi_policy != 'each' has no effect with marker_placement != 'point'"; + } + if (type == geometry::geometry_types::GeometryCollection) + { + for (auto const& g : geom.get>()) + { + apply_markers_single(converter, proc, g, geometry::geometry_type(g)); + } + } + else + { + apply_markers_single(converter, proc, geom, type); + } + } + } +} +template void apply_markers_multi(feature_impl const& feature, attributes const& vars, + vertex_converter_type & converter, vector_dispatch_type & proc, + symbolizer_base const& sym); + +template void apply_markers_multi(feature_impl const& feature, attributes const& vars, + vertex_converter_type & converter, raster_dispatch_type & proc, + symbolizer_base const& sym); } // end namespace mapnik diff --git a/src/renderer_common/render_markers_symbolizer.cpp b/src/renderer_common/render_markers_symbolizer.cpp index 12da737e5..251058193 100644 --- a/src/renderer_common/render_markers_symbolizer.cpp +++ b/src/renderer_common/render_markers_symbolizer.cpp @@ -20,9 +20,9 @@ * *****************************************************************************/ +#include #include #include -#include #include #include #include @@ -39,14 +39,6 @@ struct render_marker_symbolizer_visitor using vector_dispatch_type = vector_markers_dispatch; using raster_dispatch_type = raster_markers_dispatch; - using vertex_converter_type = vertex_converter; - render_marker_symbolizer_visitor(std::string const& filename, markers_symbolizer const& sym, mapnik::feature_impl & feature, @@ -80,6 +72,15 @@ struct render_marker_symbolizer_visitor if (transform) evaluate_transform(geom_tr, feature_, common_.vars_, *transform, common_.scale_factor_); agg::trans_affine image_tr = agg::trans_affine_scaling(common_.scale_factor_); + // converter + vertex_converter_type converter(clip_box_, + sym_, + common_.t_, + prj_trans_, + geom_tr, + feature_, + common_.vars_, + common_.scale_factor_); boost::optional const& stock_vector_marker = mark.get_data(); // special case for simple ellipse markers @@ -107,14 +108,7 @@ struct render_marker_symbolizer_visitor snap_to_pixels, renderer_context_); - vertex_converter_type converter(clip_box_, - sym_, - common_.t_, - prj_trans_, - geom_tr, - feature_, - common_.vars_, - common_.scale_factor_); + if (clip) { geometry::geometry_types type = geometry::geometry_type(feature_.get_geometry()); @@ -152,15 +146,6 @@ struct render_marker_symbolizer_visitor common_.vars_, snap_to_pixels, renderer_context_); - - vertex_converter_type converter(clip_box_, - sym_, - common_.t_, - prj_trans_, - geom_tr, - feature_, - common_.vars_, - common_.scale_factor_); if (clip) { geometry::geometry_types type = geometry::geometry_type(feature_.get_geometry()); diff --git a/src/unicode.cpp b/src/unicode.cpp index dac9b29d7..abbf85df7 100644 --- a/src/unicode.cpp +++ b/src/unicode.cpp @@ -60,4 +60,12 @@ transcoder::~transcoder() { if (conv_) ucnv_close(conv_); } + + +void to_utf8(mapnik::value_unicode_string const& input, std::string & target) +{ + target.clear(); // mimic previous target.assign(...) semantics + input.toUTF8String(target); // this appends to target +} + } diff --git a/src/value.cpp b/src/value.cpp new file mode 100644 index 000000000..efcd15b5f --- /dev/null +++ b/src/value.cpp @@ -0,0 +1,921 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2016 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 +#include +#include +#include + +// stl +#include +#include +#include +// icu +#include +#include + +namespace mapnik { + +namespace detail { + +namespace { +template +struct both_arithmetic : std::integral_constant::value && + std::is_arithmetic::value> {}; + +struct equals +{ + static bool apply(value_null, value_unicode_string const& rhs) + { + return false; + } + + template + static auto apply(T const& lhs, T const& rhs) + -> decltype(lhs == rhs) + { + return lhs == rhs; + } +}; + +struct not_equal +{ + // back compatibility shim to equate empty string with null for != test + // https://github.com/mapnik/mapnik/issues/1859 + // TODO - consider removing entire specialization at Mapnik 3.1.x + static bool apply(value_null, value_unicode_string const& rhs) + { + if (rhs.isEmpty()) return false; + return true; + } + + template + static auto apply(T const& lhs, T const& rhs) + -> decltype(lhs != rhs) + { + return lhs != rhs; + } +}; + +struct greater_than +{ + static bool apply(value_null, value_unicode_string const& rhs) + { + return false; + } + + template + static auto apply(T const& lhs, T const& rhs) + -> decltype(lhs > rhs) + { + return lhs > rhs; + } +}; + +struct greater_or_equal +{ + static bool apply(value_null, value_unicode_string const& rhs) + { + return false; + } + + template + static auto apply(T const& lhs, T const& rhs) + -> decltype(lhs >= rhs) + { + return lhs >= rhs; + } +}; + +struct less_than +{ + static bool apply(value_null, value_unicode_string const& rhs) + { + return false; + } + + template + static auto apply(T const& lhs, T const& rhs) + -> decltype(lhs < rhs) + { + return lhs < rhs; + } +}; + +struct less_or_equal +{ + static bool apply(value_null, value_unicode_string const& rhs) + { + return false; + } + + template + static auto apply(T const& lhs, T const& rhs) + -> decltype(lhs <= rhs) + { + return lhs <= rhs; + } +}; +} + +template +struct comparison +{ + // special case for unicode_strings (fixes MSVC C4800) + bool operator()(value_unicode_string const& lhs, + value_unicode_string const& rhs) const + { + return Op::apply(lhs, rhs) ? true : false; + } + + ////////////////////////////////////////////////////////////////////////// + // special case for unicode_string and value_null + ////////////////////////////////////////////////////////////////////////// + + bool operator()(value_null const& lhs, value_unicode_string const& rhs) const + { + return Op::apply(lhs, rhs); + } + ////////////////////////////////////////////////////////////////////////// + + // same types + template + bool operator()(T lhs, T rhs) const + { + return Op::apply(lhs, rhs); + } + + // both types are arithmetic - promote to the common type + template ::value, int>::type = 0> + bool operator()(T const& lhs, U const& rhs) const + { + using common_type = typename std::common_type::type; + return Op::apply(static_cast(lhs), static_cast(rhs)); + } + + // + template ::value, int>::type = 0> + bool operator()(T const& lhs, U const& rhs) const + { + return default_result; + } +}; + +template +struct add +{ + using value_type = V; + value_type operator()(value_unicode_string const& lhs, + value_unicode_string const& rhs) const + { + return lhs + rhs; + } + + value_type operator()(value_null const& lhs, + value_null const& rhs) const + { + return lhs; + } + + value_type operator()(value_unicode_string const& lhs, value_null) const + { + return lhs; + } + + value_type operator()(value_null, value_unicode_string const& rhs) const + { + return rhs; + } + + template + value_type operator()(L const& lhs, value_null const&) const + { + return lhs; + } + + template + value_type operator()(value_null const&, R const& rhs) const + { + return rhs; + } + + template + value_type operator()(L const& lhs, value_unicode_string const& rhs) const + { + std::string val; + if (util::to_string(val, lhs)) + return value_unicode_string(val.c_str()) + rhs; + return rhs; + } + + template + value_type operator()(value_unicode_string const& lhs, R const& rhs) const + { + std::string val; + if (util::to_string(val, rhs)) + return lhs + value_unicode_string(val.c_str()); + return lhs; + } + + template + value_type operator()(T1 const& lhs, T2 const& rhs) const + { + return typename std::common_type::type{lhs + rhs}; + } + + value_type operator()(value_bool lhs, value_bool rhs) const + { + return value_integer(lhs + rhs); + } +}; + +template +struct sub +{ + using value_type = V; + + value_type operator()(value_null const& lhs, + value_null const& rhs) const + { + return lhs; + } + + value_type operator()(value_null, value_unicode_string const& rhs) const + { + return rhs; + } + value_type operator()(value_unicode_string const& lhs, value_null) const + { + return lhs; + } + + template + value_type operator()(value_unicode_string const& lhs, R const&) const + { + return lhs; + } + + template + value_type operator()(L const&, value_unicode_string const& rhs) const + { + return rhs; + } + + template + value_type operator()(L const& lhs, value_null const&) const + { + return lhs; + } + + template + value_type operator()(value_null const&, R const& rhs) const + { + return rhs; + } + + template + value_type operator()(T lhs, T rhs) const + { + return lhs - rhs; + } + + value_type operator()(value_unicode_string const&, + value_unicode_string const&) const + { + return value_type(); + } + + template + value_type operator()(T1 const& lhs, T2 const& rhs) const + { + return typename std::common_type::type{lhs - rhs}; + } + + value_type operator()(value_bool lhs, value_bool rhs) const + { + return value_integer(lhs - rhs); + } +}; + +template +struct mult +{ + using value_type = V; + + value_type operator()(value_null const& lhs, + value_null const& rhs) const + { + return lhs; + } + + value_type operator()(value_unicode_string const& lhs, value_null) const + { + return lhs; + } + + value_type operator()(value_null, value_unicode_string const& rhs) const + { + return rhs; + } + + template + value_type operator()(L const& lhs, value_null const&) const + { + return lhs; + } + + template + value_type operator()(value_null const&, R const& rhs) const + { + return rhs; + } + + template + value_type operator()(value_unicode_string const& lhs, R const&) const + { + return lhs; + } + + template + value_type operator()(L const&, value_unicode_string const& rhs) const + { + return rhs; + } + + template + value_type operator()(T lhs, T rhs) const + { + return lhs * rhs; + } + + value_type operator()(value_unicode_string const&, + value_unicode_string const&) const + { + return value_type(); + } + + template + value_type operator()(T1 const& lhs, T2 const& rhs) const + { + return typename std::common_type::type{lhs * rhs}; + } + + value_type operator()(value_bool lhs, value_bool rhs) const + { + return value_integer(lhs * rhs); + } +}; + +template +struct div +{ + using value_type = V; + + value_type operator()(value_null const& lhs, + value_null const& rhs) const + { + return lhs; + } + + value_type operator()(value_unicode_string const& lhs, value_null) const + { + return lhs; + } + + value_type operator()(value_null, value_unicode_string const& rhs) const + { + return rhs; + } + + template + value_type operator()(L const& lhs, value_null const&) const + { + return lhs; + } + + template + value_type operator()(value_null const&, R const& rhs) const + { + return rhs; + } + + template + value_type operator()(T lhs, T rhs) const + { + if (rhs == 0) return value_type(); + return lhs / rhs; + } + + value_type operator()(value_bool lhs, value_bool rhs) const + { + if (rhs == 0) return lhs; + return value_integer(lhs) / value_integer(rhs); + } + + value_type operator()(value_unicode_string const&, + value_unicode_string const&) const + { + return value_type(); + } + + template + value_type operator()(value_unicode_string const& lhs, R const&) const + { + return lhs; + } + + template + value_type operator()(L const&, value_unicode_string const& rhs) const + { + return rhs; + } + + template + value_type operator()(T1 const& lhs, T2 const& rhs) const + { + if (rhs == 0) return value_type(); + using common_type = typename std::common_type::type; + return common_type(lhs) / common_type(rhs); + } +}; + +template +struct mod +{ + using value_type = V; + + template + value_type operator()(T1 const& lhs, T2 const&) const + { + return lhs; + } + + template + value_type operator()(T lhs, T rhs) const + { + return lhs % rhs; + } + + value_type operator()(value_unicode_string const&, + value_unicode_string const&) const + { + return value_type(); + } + + value_type operator()(value_bool, + value_bool) const + { + return false; + } + + value_type operator()(value_double lhs, value_integer rhs) const + { + return std::fmod(lhs, static_cast(rhs)); + } + + value_type operator()(value_integer lhs, value_double rhs) const + { + return std::fmod(static_cast(lhs), rhs); + } + + value_type operator()(value_double lhs, value_double rhs) const + { + return std::fmod(lhs, rhs); + } +}; + +template +struct negate +{ + using value_type = V; + + template + value_type operator()(T val) const + { + return -val; + } + + value_type operator()(value_null val) const + { + return val; + } + + value_type operator()(value_bool val) const + { + return val ? value_integer(-1) : value_integer(0); + } + + value_type operator()(value_unicode_string const&) const + { + return value_type(); + } +}; + +// converters +template +struct convert +{ +}; + +template <> +struct convert +{ + value_bool operator()(value_bool val) const + { + return val; + } + + value_bool operator()(value_unicode_string const& ustr) const + { + return !ustr.isEmpty(); + } + + value_bool operator()(value_null const&) const + { + return false; + } + + template + value_bool operator()(T val) const + { + return val > 0 ? true : false; + } +}; + +template <> +struct convert +{ + value_double operator()(value_double val) const + { + return val; + } + + value_double operator()(value_integer val) const + { + return static_cast(val); + } + + value_double operator()(value_bool val) const + { + return static_cast(val); + } + + value_double operator()(std::string const& val) const + { + value_double result; + if (util::string2double(val, result)) + return result; + return 0; + } + + value_double operator()(value_unicode_string const& val) const + { + std::string utf8; + val.toUTF8String(utf8); + return operator()(utf8); + } + + value_double operator()(value_null const&) const + { + return 0.0; + } +}; + +template <> +struct convert +{ + value_integer operator()(value_integer val) const + { + return val; + } + + value_integer operator()(value_double val) const + { + return static_cast(rint(val)); + } + + value_integer operator()(value_bool val) const + { + return static_cast(val); + } + + value_integer operator()(std::string const& val) const + { + value_integer result; + if (util::string2int(val, result)) + return result; + return value_integer(0); + } + + value_integer operator()(value_unicode_string const& val) const + { + std::string utf8; + val.toUTF8String(utf8); + return operator()(utf8); + } + + value_integer operator()(value_null const&) const + { + return value_integer(0); + } +}; + +template <> +struct convert +{ + template + std::string operator()(T val) const + { + std::string str; + util::to_string(str, val); + return str; + } + + // specializations + std::string operator()(value_unicode_string const& val) const + { + std::string utf8; + val.toUTF8String(utf8); + return utf8; + } + + std::string operator()(value_double val) const + { + std::string str; + util::to_string(str, val); // TODO set precision(16) + return str; + } + + std::string operator()(value_bool val) const + { + return val ? "true" : "false"; + } + + std::string operator()(value_null const&) const + { + return std::string(); + } +}; + +struct to_unicode_impl +{ + + template + value_unicode_string operator()(T val) const + { + std::string str; + util::to_string(str, val); + return value_unicode_string(str.c_str()); + } + + // specializations + value_unicode_string const& operator()(value_unicode_string const& val) const + { + return val; + } + + value_unicode_string operator()(value_double val) const + { + std::string str; + util::to_string(str, val); + return value_unicode_string(str.c_str()); + } + + value_unicode_string operator()(value_bool val) const + { + return value_unicode_string(val ? "true" : "false"); + } + + value_unicode_string operator()(value_null const&) const + { + return value_unicode_string(); + } +}; + +struct to_expression_string_impl +{ + struct EscapingByteSink : U_NAMESPACE_QUALIFIER ByteSink + { + std::string dest_; + char quote_; + + explicit EscapingByteSink(char quote) + : quote_(quote) + { + } + + virtual void Append(const char* data, int32_t n) + { + // reserve enough room to hold the appended chunk and quotes; + // if another chunk follows, or any character needs escaping, + // the string will grow naturally + if (dest_.empty()) + { + dest_.reserve(2 + static_cast(n)); + dest_.append(1, quote_); + } + else + { + dest_.reserve(dest_.size() + n + 1); + } + + for (auto end = data + n; data < end; ++data) + { + if (*data == '\\' || *data == quote_) + dest_.append(1, '\\'); + dest_.append(1, *data); + } + } + + virtual void Flush() + { + if (dest_.empty()) + dest_.append(2, quote_); + else + dest_.append(1, quote_); + } + }; + + explicit to_expression_string_impl(char quote = '\'') + : quote_(quote) {} + + std::string operator()(value_unicode_string const& val) const + { + EscapingByteSink sink(quote_); + val.toUTF8(sink); + return sink.dest_; + } + + std::string operator()(value_integer val) const + { + std::string output; + util::to_string(output, val); + return output; + } + + std::string operator()(value_double val) const + { + std::string output; + util::to_string(output, val); // TODO precision(16) + return output; + } + + std::string operator()(value_bool val) const + { + return val ? "true" : "false"; + } + + std::string operator()(value_null const&) const + { + return "null"; + } + + const char quote_; +}; + +} // ns detail + +namespace value_adl_barrier { + +bool value::operator==(value const& other) const +{ + return util::apply_visitor(detail::comparison(), *this, other); +} + +bool value::operator!=(value const& other) const +{ + return util::apply_visitor(detail::comparison(), *this, other); +} + +bool value::operator>(value const& other) const +{ + return util::apply_visitor(detail::comparison(), *this, other); +} + +bool value::operator>=(value const& other) const +{ + return util::apply_visitor(detail::comparison(), *this, other); +} + +bool value::operator<(value const& other) const +{ + return util::apply_visitor(detail::comparison(), *this, other); +} + +bool value::operator<=(value const& other) const +{ + return util::apply_visitor(detail::comparison(), *this, other); +} + +value value::operator-() const +{ + return util::apply_visitor(detail::negate(), *this); +} + +value_bool value::to_bool() const +{ + return util::apply_visitor(detail::convert(), *this); +} + +std::string value::to_expression_string(char quote) const +{ + return util::apply_visitor(detail::to_expression_string_impl(quote), *this); +} + +std::string value::to_string() const +{ + return util::apply_visitor(detail::convert(), *this); +} + +value_unicode_string value::to_unicode() const +{ + return util::apply_visitor(detail::to_unicode_impl(), *this); +} + +value_double value::to_double() const +{ + return util::apply_visitor(detail::convert(), *this); +} + +value_integer value::to_int() const +{ + return util::apply_visitor(detail::convert(), *this); +} + +bool value::is_null() const +{ + return util::apply_visitor(mapnik::detail::is_null_visitor(), *this); +} + +template <> +value_double value::convert() const +{ + return util::apply_visitor(detail::convert(), *this); +} + +template <> +value_integer value::convert() const +{ + return util::apply_visitor(detail::convert(), *this); +} + +template <> +value_bool value::convert() const +{ + return util::apply_visitor(detail::convert(), *this); +} + +template <> +std::string value::convert() const +{ + return util::apply_visitor(detail::convert(), *this); +} + +// +value operator+(value const& p1, value const& p2) +{ + return value(util::apply_visitor(detail::add(), p1, p2)); +} + +value operator-(value const& p1, value const& p2) +{ + return value(util::apply_visitor(detail::sub(), p1, p2)); +} + +value operator*(value const& p1, value const& p2) +{ + return value(util::apply_visitor(detail::mult(), p1, p2)); +} + +value operator/(value const& p1, value const& p2) +{ + return value(util::apply_visitor(detail::div(), p1, p2)); +} + +value operator%(value const& p1, value const& p2) +{ + return value(util::apply_visitor(detail::mod(), p1, p2)); +} + +} // namespace value_adl_barrier +} // namespace mapnik diff --git a/src/vertex_adapters.cpp b/src/vertex_adapters.cpp new file mode 100644 index 000000000..06be6c2f4 --- /dev/null +++ b/src/vertex_adapters.cpp @@ -0,0 +1,213 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2016 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 +#include +#include +#include + +namespace mapnik { namespace geometry { + +// point adapter +template +point_vertex_adapter::point_vertex_adapter(point const& pt) + : pt_(pt), + first_(true) {} + +template +unsigned point_vertex_adapter::vertex(value_type * x, value_type * y) const +{ + if (first_) + { + *x = pt_.x; + *y = pt_.y; + first_ = false; + return mapnik::SEG_MOVETO; + } + return mapnik::SEG_END; +} + +template +void point_vertex_adapter::rewind(unsigned) const +{ + first_ = true; +} + +template +geometry_types point_vertex_adapter::type () const +{ + return geometry_types::Point; +} + +// line_string adapter +template +line_string_vertex_adapter::line_string_vertex_adapter(line_string const& line) + : line_(line), + current_index_(0), + end_index_(line.size()) +{} + +template +unsigned line_string_vertex_adapter::vertex(value_type * x, value_type * y) const +{ + if (current_index_ != end_index_) + { + point const& coord = line_[current_index_++]; + *x = coord.x; + *y = coord.y; + if (current_index_ == 1) + { + return mapnik::SEG_MOVETO; + } + else + { + return mapnik::SEG_LINETO; + } + } + return mapnik::SEG_END; +} + +template +void line_string_vertex_adapter::rewind(unsigned) const +{ + current_index_ = 0; +} + +template +geometry_types line_string_vertex_adapter::type() const +{ + return geometry_types::LineString; +} + +template +polygon_vertex_adapter::polygon_vertex_adapter(polygon const& poly) + : poly_(poly), + rings_itr_(0), + rings_end_(poly_.interior_rings.size() + 1), + current_index_(0), + end_index_((rings_itr_ < rings_end_) ? poly_.exterior_ring.size() : 0), + start_loop_(true) {} + +template +void polygon_vertex_adapter::rewind(unsigned) const +{ + rings_itr_ = 0; + rings_end_ = poly_.interior_rings.size() + 1; + current_index_ = 0; + end_index_ = (rings_itr_ < rings_end_) ? poly_.exterior_ring.size() : 0; + start_loop_ = true; +} +template +unsigned polygon_vertex_adapter::vertex(value_type * x, value_type * y) const +{ + if (rings_itr_ == rings_end_) + { + return mapnik::SEG_END; + } + if (current_index_ < end_index_) + { + point const& coord = (rings_itr_ == 0) ? + poly_.exterior_ring[current_index_++] : poly_.interior_rings[rings_itr_- 1][current_index_++]; + *x = coord.x; + *y = coord.y; + if (start_loop_) + { + start_loop_= false; + return mapnik::SEG_MOVETO; + } + if (current_index_ == end_index_) + { + *x = 0; + *y = 0; + return mapnik::SEG_CLOSE; + } + return mapnik::SEG_LINETO; + } + else if (++rings_itr_ != rings_end_) + { + current_index_ = 0; + end_index_ = poly_.interior_rings[rings_itr_ - 1].size(); + point const& coord = poly_.interior_rings[rings_itr_ - 1][current_index_++]; + *x = coord.x; + *y = coord.y; + return mapnik::SEG_MOVETO; + } + return mapnik::SEG_END; +} + +template +geometry_types polygon_vertex_adapter::type () const +{ + return geometry_types::Polygon; +} + +// ring adapter +template +ring_vertex_adapter::ring_vertex_adapter(linear_ring const& ring) + : ring_(ring), + current_index_(0), + end_index_(ring_.size()), + start_loop_(true) {} + +template +void ring_vertex_adapter::rewind(unsigned) const +{ + current_index_ = 0; + end_index_ = ring_.size(); + start_loop_ = true; +} + +template +unsigned ring_vertex_adapter::vertex(value_type * x, value_type * y) const +{ + if (current_index_ < end_index_) + { + auto const& coord = ring_[current_index_++]; + *x = coord.x; + *y = coord.y; + if (start_loop_) + { + start_loop_= false; + return mapnik::SEG_MOVETO; + } + if (current_index_ == end_index_) + { + *x = 0; + *y = 0; + return mapnik::SEG_CLOSE; + } + return mapnik::SEG_LINETO; + } + return mapnik::SEG_END; +} +template +geometry_types ring_vertex_adapter::type () const +{ + return geometry_types::Polygon; +} + +template struct point_vertex_adapter; +template struct line_string_vertex_adapter; +template struct polygon_vertex_adapter; +template struct ring_vertex_adapter; + +}} diff --git a/test/data-visual b/test/data-visual index f5623b5a3..93e70a5ae 160000 --- a/test/data-visual +++ b/test/data-visual @@ -1 +1 @@ -Subproject commit f5623b5a312c58cd9c1926008fcf114944c769a3 +Subproject commit 93e70a5ae91b2bd4b8fc3912a7e6e4b017021b2b diff --git a/test/unit/core/expressions_test.cpp b/test/unit/core/expressions_test.cpp index 946b4f155..72490a215 100644 --- a/test/unit/core/expressions_test.cpp +++ b/test/unit/core/expressions_test.cpp @@ -158,6 +158,16 @@ TEST_CASE("expressions") TRY_CHECK(eval(" [int] > 100 and [int] gt 100.0 and [double] < 2 and [double] lt 2.0 ") == true); TRY_CHECK(eval(" [int] >= 123 and [int] ge 123.0 and [double] <= 1.23456 and [double] le 1.23456 ") == true); + // empty string/null equality + TRY_CHECK(eval("[null] = null") == true); + TRY_CHECK(eval("[null] != null") == false); + TRY_CHECK(eval("[null] = ''") == false); + ///////////////////// ref: https://github.com/mapnik/mapnik/issues/1859 + TRY_CHECK(eval("[null] != ''") == false); // back compatible - will be changed in 3.1.x + ////////////////////// + TRY_CHECK(eval("'' = [null]") == false); + TRY_CHECK(eval("'' != [null]") == true); + // regex // replace TRY_CHECK(eval(" [foo].replace('(\\B)|( )','$1 ') ") == tr.transcode("b a r")); diff --git a/test/unit/datasource/geojson.cpp b/test/unit/datasource/geojson.cpp index ed2b44db7..e8c0a90a3 100644 --- a/test/unit/datasource/geojson.cpp +++ b/test/unit/datasource/geojson.cpp @@ -663,20 +663,8 @@ TEST_CASE("geojson") { auto ds = mapnik::datasource_cache::instance().create(params); REQUIRE(bool(ds)); auto fields = ds->get_descriptor().get_descriptors(); - // TODO: this combo (cache_features==false and create_index==false) - // exposes the case where not all field names are reported, which should - // ideally be fixed, but how? - if (cache_features == false && create_index == false) - { - std::initializer_list names = {"one"}; - REQUIRE_FIELD_NAMES(fields, names); - } - else - { - std::initializer_list names = {"one", "two"}; - REQUIRE_FIELD_NAMES(fields, names); - } - + std::initializer_list names = {"one", "two"}; + REQUIRE_FIELD_NAMES(fields, names); } // cleanup if (create_index && mapnik::util::exists(filename + ".index"))