From 5abb40e3bef1807d6cd8b2284e713aa3b221c8d2 Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Thu, 22 Oct 2020 15:58:51 +0100 Subject: [PATCH 01/56] Update proj to 6.1.0 (travis) --- bootstrap.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bootstrap.sh b/bootstrap.sh index 24706b8be..3edb974c5 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -56,7 +56,7 @@ function install_mason_deps() { install sqlite 3.17.0 libsqlite3 install expat 2.2.0 libexpat install icu ${ICU_VERSION} - install proj 4.9.3 libproj + install proj 6.0.1 libproj install pixman 0.34.0 libpixman-1 install cairo 1.14.8 libcairo install webp 0.6.0 libwebp From 8cba41b2d8ac4710ba7ff7fb0adccb0cf33692f3 Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Thu, 22 Oct 2020 16:03:26 +0100 Subject: [PATCH 02/56] Upgrade to latest mason ed144f5b --- bootstrap.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bootstrap.sh b/bootstrap.sh index 3edb974c5..f87f983d8 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -8,7 +8,7 @@ todo - shrink icu data ' -MASON_VERSION="cce4ebf4" +MASON_VERSION="ed144f5b" function setup_mason() { if [[ ! -d ./.mason ]]; then From f9b18c36148e7fc3e18d6e0ad230746d40dcd640 Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Thu, 22 Oct 2020 16:13:33 +0100 Subject: [PATCH 03/56] fix proj version typo --- bootstrap.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bootstrap.sh b/bootstrap.sh index f87f983d8..ea185456f 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -56,7 +56,7 @@ function install_mason_deps() { install sqlite 3.17.0 libsqlite3 install expat 2.2.0 libexpat install icu ${ICU_VERSION} - install proj 6.0.1 libproj + install proj 6.1.0 libproj install pixman 0.34.0 libpixman-1 install cairo 1.14.8 libcairo install webp 0.6.0 libwebp From 12cdc1af69286f8a5917dd55fdc965b41c746d3a Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Thu, 22 Oct 2020 16:13:57 +0100 Subject: [PATCH 04/56] Update test values (epsg:2193) --- test/unit/projection/proj_transform.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/projection/proj_transform.cpp b/test/unit/projection/proj_transform.cpp index 289d8e70d..f8cd718d2 100644 --- a/test/unit/projection/proj_transform.cpp +++ b/test/unit/projection/proj_transform.cpp @@ -178,7 +178,7 @@ SECTION("Test proj antimeridian bbox") // 274000 7173000 # top-most // END // - const mapnik::box2d normal(148.7667597489, -60.1222810241, + const mapnik::box2d normal(148.7639922894, -60.1222810241, 159.9548489296, -24.9771195155); { From 309c7b34229ae345095d784d215630c413bc3841 Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Thu, 22 Oct 2020 16:25:53 +0100 Subject: [PATCH 05/56] Update sqlite to v3.21.0 --- bootstrap.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bootstrap.sh b/bootstrap.sh index ea185456f..d5d141aa6 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -53,7 +53,7 @@ function install_mason_deps() { install libpng 1.6.28 libpng install libtiff 4.0.7 libtiff install libpq 9.6.2 - install sqlite 3.17.0 libsqlite3 + install sqlite 3.21.0 libsqlite3 install expat 2.2.0 libexpat install icu ${ICU_VERSION} install proj 6.1.0 libproj From 88241b32eeaccdc9eda7b92b7e7b6d4deac88e12 Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Fri, 15 Jan 2021 15:21:09 +0000 Subject: [PATCH 06/56] Initial implementation of new proj7 APIs support --- SConstruct | 79 +++++----- benchmark/data/gdal-wgs.xml | 4 +- benchmark/data/raster-wgs.xml | 4 +- benchmark/src/test_noop_rendering.cpp | 4 +- benchmark/src/test_polygon_clipping.cpp | 2 +- benchmark/src/test_proj_transform1.cpp | 6 +- include/mapnik/layer.hpp | 2 +- include/mapnik/proj_transform.hpp | 6 +- include/mapnik/projection.hpp | 13 +- src/build.py | 2 +- src/proj_transform.cpp | 115 ++++++--------- src/projection.cpp | 135 ++++++++---------- src/well_known_srs.cpp | 8 +- test/data | 2 +- test/data-visual | 2 +- test/unit/core/exceptions_test.cpp | 5 +- test/unit/geometry/geometry_reprojection.cpp | 72 +++++----- test/unit/geometry/geometry_strategy_test.cpp | 4 +- test/unit/projection/proj_transform.cpp | 26 ++-- .../vertex_adapter/transform_path_adapter.cpp | 8 +- 20 files changed, 226 insertions(+), 273 deletions(-) diff --git a/SConstruct b/SConstruct index a2c8439c8..e357bd4aa 100644 --- a/SConstruct +++ b/SConstruct @@ -1,6 +1,6 @@ # This file is part of Mapnik (c++ mapping toolkit) # -# Copyright (C) 2017 Artem Pavlenko +# Copyright (C) 2021 Artem Pavlenko # # Mapnik is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -79,7 +79,7 @@ SCONF_TEMP_DIR = '.sconf_temp' BOOST_SEARCH_PREFIXES = ['/usr/local','/opt/local','/sw','/usr',] BOOST_MIN_VERSION = '1.61' #CAIRO_MIN_VERSION = '1.8.0' - +PROJ_MIN_VERSION = (7, 2, 0) HARFBUZZ_MIN_VERSION = (0, 9, 34) HARFBUZZ_MIN_VERSION_STRING = "%s.%s.%s" % HARFBUZZ_MIN_VERSION @@ -92,7 +92,7 @@ pretty_dep_names = { 'gdal':'GDAL C++ library | configured using gdal-config program | try setting GDAL_CONFIG SCons option | more info: https://github.com/mapnik/mapnik/wiki/GDAL', 'ogr':'OGR-enabled GDAL C++ Library | configured using gdal-config program | try setting GDAL_CONFIG SCons option | more info: https://github.com/mapnik/mapnik/wiki/OGR', 'cairo':'Cairo C library | configured using pkg-config | try setting PKG_CONFIG_PATH SCons option', - 'proj':'Proj.4 C Projections library | configure with PROJ_LIBS & PROJ_INCLUDES | more info: http://trac.osgeo.org/proj/', + 'proj':'Proj C Projections library | configure with PROJ_LIBS & PROJ_INCLUDES | more info: http://trac.osgeo.org/proj/', 'pg':'Postgres C Library required for PostGIS plugin | configure with pg_config program or configure with PG_LIBS & PG_INCLUDES | more info: https://github.com/mapnik/mapnik/wiki/PostGIS', 'sqlite3':'SQLite3 C Library | configure with SQLITE_LIBS & SQLITE_INCLUDES | more info: https://github.com/mapnik/mapnik/wiki/SQLite', 'jpeg':'JPEG C library | configure with JPEG_LIBS & JPEG_INCLUDES', @@ -116,7 +116,7 @@ pretty_dep_names = { 'boost_regex_icu':'libboost_regex built with optional ICU unicode support is needed for unicode regex support in mapnik.', 'sqlite_rtree':'The SQLite plugin requires libsqlite3 built with RTREE support (-DSQLITE_ENABLE_RTREE=1)', 'pgsql2sqlite_rtree':'The pgsql2sqlite program requires libsqlite3 built with RTREE support (-DSQLITE_ENABLE_RTREE=1)', - 'PROJ_LIB':'The directory where proj4 stores its data files. Must exist for proj4 to work correctly', + 'PROJ_LIB':'The directory where proj stores its data files. Must exist for proj to work correctly', 'GDAL_DATA':'The directory where GDAL stores its data files. Must exist for GDAL to work correctly', 'ICU_DATA':'The directory where icu stores its data files. If ICU reports a path, it must exist. ICU can also be built without .dat files and in that case this path is empty' } @@ -422,7 +422,7 @@ opts.AddVariables( BoolVariable('WEBP', 'Build Mapnik with WEBP read', 'True'), PathVariable('WEBP_INCLUDES', 'Search path for libwebp include files', '/usr/include', PathVariable.PathAccept), PathVariable('WEBP_LIBS','Search path for libwebp library files','/usr/' + LIBDIR_SCHEMA_DEFAULT, PathVariable.PathAccept), - BoolVariable('PROJ', 'Build Mapnik with proj4 support to enable transformations between many different projections', 'True'), + BoolVariable('PROJ', 'Build Mapnik with proj support to enable transformations between many different projections', 'True'), PathVariable('PROJ_INCLUDES', 'Search path for PROJ.4 include files', '/usr/include', PathVariable.PathAccept), PathVariable('PROJ_LIBS', 'Search path for PROJ.4 library files', '/usr/' + LIBDIR_SCHEMA_DEFAULT, PathVariable.PathAccept), ('PG_INCLUDES', 'Search path for libpq (postgres client) include files', ''), @@ -940,51 +940,40 @@ def CheckProjData(context, silent=False): context.Message('Checking for PROJ_LIB directory...') ret, out = context.TryRun(""" -// This is narly, could eventually be replaced using https://github.com/OSGeo/proj.4/pull/551] -#include +#include #include -#include +#include +#include +#include +#include -static void my_proj4_logger(void * user_data, int /*level*/, const char * msg) +std::vector split_searchpath(std::string const& paths) { - std::string* posMsg = static_cast(user_data); - *posMsg += msg; + std::vector output; + std::stringstream ss(paths); + std::string path; + + for( std::string path;std::getline(ss, path, ':');) + { + output.push_back(path); + } + return output; } -// https://github.com/OSGeo/gdal/blob/ddbf6d39aa4b005a77ca4f27c2d61a3214f336f8/gdal/alg/gdalapplyverticalshiftgrid.cpp#L616-L633 - -std::string find_proj_path(const char * pszFilename) { - std::string osMsg; - std::string osFilename; - projCtx ctx = pj_ctx_alloc(); - pj_ctx_set_app_data(ctx, &osMsg); - pj_ctx_set_debug(ctx, PJ_LOG_DEBUG_MAJOR); - pj_ctx_set_logger(ctx, my_proj4_logger); - PAFile f = pj_open_lib(ctx, pszFilename, "rb"); - if( f ) +int main() +{ + PJ_INFO info = proj_info(); + std::string result = info.searchpath; + for (auto path : split_searchpath(result)) { - pj_ctx_fclose(ctx, f); + std::ifstream file(path + "/proj.db"); + if (file) + { + std::cout << path; + return 0; + } } - size_t nPos = osMsg.find("fopen("); - if( nPos != std::string::npos ) - { - osFilename = osMsg.substr(nPos + strlen("fopen(")); - nPos = osFilename.find(")"); - if( nPos != std::string::npos ) - osFilename = osFilename.substr(0, nPos); - } - pj_ctx_free(ctx); - return osFilename; -} - - -int main() { - std::string result = find_proj_path(" "); - std::cout << result; - if (result.empty()) { - return -1; - } - return 0; + return -1; } """, '.cpp') @@ -992,7 +981,7 @@ int main() { if silent: context.did_show_result=1 if ret: - context.Result('pj_open_lib returned %s' % value) + context.Result('proj_info.searchpath returned %s' % value) else: context.Result('Failed to detect (mapnik-config will have null value)') return value @@ -1565,7 +1554,7 @@ if not preconfigured: env['SKIPPED_DEPS'].append('jpeg') if env['PROJ']: - OPTIONAL_LIBSHEADERS.append(['proj', 'proj_api.h', False,'C','-DMAPNIK_USE_PROJ4']) + OPTIONAL_LIBSHEADERS.append(['proj', 'proj.h', False,'C','-DMAPNIK_USE_PROJ']) inc_path = env['%s_INCLUDES' % 'PROJ'] lib_path = env['%s_LIBS' % 'PROJ'] env.AppendUnique(CPPPATH = fix_path(inc_path)) diff --git a/benchmark/data/gdal-wgs.xml b/benchmark/data/gdal-wgs.xml index aa5866532..7a0ca6012 100644 --- a/benchmark/data/gdal-wgs.xml +++ b/benchmark/data/gdal-wgs.xml @@ -1,7 +1,7 @@ + srs="epsg:4326"> style ./valid.geotiff.tif diff --git a/benchmark/data/raster-wgs.xml b/benchmark/data/raster-wgs.xml index 3ee054d2d..d879f4fc8 100644 --- a/benchmark/data/raster-wgs.xml +++ b/benchmark/data/raster-wgs.xml @@ -1,7 +1,7 @@ + srs="epsg:4326"> style ./valid.geotiff.tif diff --git a/benchmark/src/test_noop_rendering.cpp b/benchmark/src/test_noop_rendering.cpp index 03d2e675c..55c09e34c 100644 --- a/benchmark/src/test_noop_rendering.cpp +++ b/benchmark/src/test_noop_rendering.cpp @@ -11,7 +11,7 @@ #include #include - + class test : public benchmark::test_case { public: @@ -24,7 +24,7 @@ public: } bool operator()() const { - mapnik::Map m(256,256,"+init=epsg:3857"); + mapnik::Map m(256,256,"epsg:3857"); mapnik::parameters params; params["type"]="memory"; diff --git a/benchmark/src/test_polygon_clipping.cpp b/benchmark/src/test_polygon_clipping.cpp index aed74fe33..e27300423 100644 --- a/benchmark/src/test_polygon_clipping.cpp +++ b/benchmark/src/test_polygon_clipping.cpp @@ -51,7 +51,7 @@ void render(mapnik::geometry::multi_polygon const& geom, agg::pixfmt_rgba32_plain pixf(buf); ren_base renb(pixf); renderer ren(renb); - mapnik::proj_transform prj_trans(mapnik::projection("+init=epsg:4326"),mapnik::projection("+init=epsg:4326")); + mapnik::proj_transform prj_trans(mapnik::projection("epsg:4326"),mapnik::projection("epsg:4326")); ren.color(agg::rgba8(127,127,127,255)); agg::rasterizer_scanline_aa<> ras; for (auto const& poly : geom) diff --git a/benchmark/src/test_proj_transform1.cpp b/benchmark/src/test_proj_transform1.cpp index fa050da12..fd8cf6a7b 100644 --- a/benchmark/src/test_proj_transform1.cpp +++ b/benchmark/src/test_proj_transform1.cpp @@ -56,13 +56,13 @@ public: } }; -// echo -180 -60 | cs2cs -f "%.10f" +init=epsg:4326 +to +init=epsg:3857 +// echo -180 -60 | cs2cs -f "%.10f" epsg:4326 +to epsg:3857 int main(int argc, char** argv) { mapnik::box2d from(-180,-80,180,80); mapnik::box2d to(-20037508.3427892476,-15538711.0963092316,20037508.3427892476,15538711.0963092316); - std::string from_str("+init=epsg:4326"); - std::string to_str("+init=epsg:3857"); + std::string from_str("epsg:4326"); + std::string to_str("epsg:3857"); std::string from_str2("+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs"); std::string to_str2("+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over"); return benchmark::sequencer(argc, argv) diff --git a/include/mapnik/layer.hpp b/include/mapnik/layer.hpp index 81f8bf534..96a161816 100644 --- a/include/mapnik/layer.hpp +++ b/include/mapnik/layer.hpp @@ -42,7 +42,7 @@ using datasource_ptr = std::shared_ptr; * @brief A Mapnik map layer. * * Create a layer with a named string and, optionally, an srs string either - * with a Proj.4 epsg code ('+init=epsg:') or with a Proj.4 literal + * with a Proj.4 epsg code ('epsg:') or with a Proj.4 literal * ('+proj='). If no srs is specified it will default to * '+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs' */ diff --git a/include/mapnik/proj_transform.hpp b/include/mapnik/proj_transform.hpp index 588a048eb..475faeefc 100644 --- a/include/mapnik/proj_transform.hpp +++ b/include/mapnik/proj_transform.hpp @@ -27,12 +27,12 @@ #include #include #include +#include // stl #include namespace mapnik { -class projection; template class box2d; class MAPNIK_DECL proj_transform : private util::noncopyable @@ -40,7 +40,7 @@ class MAPNIK_DECL proj_transform : private util::noncopyable public: proj_transform(projection const& source, projection const& dest); - + ~proj_transform(); bool equal() const; bool is_known() const; bool forward (double& x, double& y , double& z) const; @@ -59,6 +59,8 @@ public: mapnik::projection const& dest() const; private: + PJ_CONTEXT* ctx_ = nullptr; + PJ* transform_ = nullptr; projection const& source_; projection const& dest_; bool is_source_longlat_; diff --git a/include/mapnik/projection.hpp b/include/mapnik/projection.hpp index cba5f7110..ac78a4469 100644 --- a/include/mapnik/projection.hpp +++ b/include/mapnik/projection.hpp @@ -37,6 +37,13 @@ MAPNIK_DISABLE_WARNING_POP #include #include + +// fwd decl +struct projCtx_t; +struct PJconsts; +using PJ_CONTEXT = struct projCtx_t; +using PJ = struct PJconsts; + namespace mapnik { class proj_init_error : public std::runtime_error @@ -66,7 +73,7 @@ public: void forward(double & x, double & y) const; void inverse(double & x,double & y) const; std::string expanded() const; - void init_proj4() const; + void init_proj() const; private: void swap (projection& rhs); @@ -75,8 +82,8 @@ private: std::string params_; bool defer_proj_init_; mutable bool is_geographic_; - mutable void * proj_; - mutable void * proj_ctx_; + mutable PJ * proj_; + mutable PJ_CONTEXT * proj_ctx_; }; template diff --git a/src/build.py b/src/build.py index 3231a0085..1904b02f8 100644 --- a/src/build.py +++ b/src/build.py @@ -76,7 +76,7 @@ if '-DHAVE_PNG' in env['CPPDEFINES']: lib_env['LIBS'].append('png') enabled_imaging_libraries.append('png_reader.cpp') -if '-DMAPNIK_USE_PROJ4' in env['CPPDEFINES']: +if '-DMAPNIK_USE_PROJ' in env['CPPDEFINES']: lib_env['LIBS'].append('proj') if '-DHAVE_TIFF' in env['CPPDEFINES']: diff --git a/src/proj_transform.cpp b/src/proj_transform.cpp index ed0563837..a3ff8180d 100644 --- a/src/proj_transform.cpp +++ b/src/proj_transform.cpp @@ -32,9 +32,9 @@ // boost #include -#ifdef MAPNIK_USE_PROJ4 -// proj4 -#include +#ifdef MAPNIK_USE_PROJ +// proj +#include #endif // stl @@ -126,16 +126,44 @@ proj_transform::proj_transform(projection const& source, } if (!known_trans) { -#ifdef MAPNIK_USE_PROJ4 - source_.init_proj4(); - dest_.init_proj4(); +#ifdef MAPNIK_USE_PROJ + transform_ = proj_create_crs_to_crs(ctx_, + source_.params().c_str(), + dest_.params().c_str(), nullptr); + if (transform_ == nullptr) + { + throw std::runtime_error(std::string("Cannot initialize proj_transform for given projections without proj4 support (-DMAPNIK_USE_PROJ): '") + source_.params() + "'->'" + dest_.params() + "'"); + } + PJ* transform_gis = proj_normalize_for_visualization(ctx_, transform_); + if (transform_gis == nullptr) + { + throw std::runtime_error(std::string("Cannot initialize proj_transform for given projections without proj4 support (-DMAPNIK_USE_PROJ): '") + source_.params() + "'->'" + dest_.params() + "'"); + } + proj_destroy(transform_); + transform_ = transform_gis; #else - throw std::runtime_error(std::string("Cannot initialize proj_transform for given projections without proj4 support (-DMAPNIK_USE_PROJ4): '") + source_.params() + "'->'" + dest_.params() + "'"); + throw std::runtime_error(std::string("Cannot initialize proj_transform for given projections without proj4 support (-DMAPNIK_USE_PROJ): '") + source_.params() + "'->'" + dest_.params() + "'"); #endif } } } +proj_transform::~proj_transform() +{ +#ifdef MAPNIK_USE_PROJ + if (transform_) + { + proj_destroy(transform_); + transform_ = nullptr; + } + if (ctx_) + { + proj_context_destroy(ctx_); + ctx_ = nullptr; + } +#endif +} + bool proj_transform::equal() const { return is_source_equal_dest_; @@ -189,7 +217,6 @@ unsigned int proj_transform::forward (std::vector> & ls) bool proj_transform::forward (double * x, double * y , double * z, int point_count, int offset) const { - if (is_source_equal_dest_) return true; @@ -202,37 +229,14 @@ bool proj_transform::forward (double * x, double * y , double * z, int point_cou return merc2lonlat(x, y, point_count, offset); } -#ifdef MAPNIK_USE_PROJ4 - if (is_source_longlat_) - { - int i; - for(i=0; i -#ifdef MAPNIK_USE_PROJ4 -// proj4 -#include - #if defined(MAPNIK_THREADSAFE) && PJ_VERSION < 480 - #include - static std::mutex mutex_; - #ifdef _MSC_VER - #pragma NOTE(mapnik is building against < proj 4.8, reprojection will be faster if you use >= 4.8) - #else - #warning mapnik is building against < proj 4.8, reprojection will be faster if you use >= 4.8 - #endif - #endif +#ifdef MAPNIK_USE_PROJ +// proj +#include #endif namespace mapnik { - projection::projection(std::string const& params, bool defer_proj_init) : params_(params), defer_proj_init_(defer_proj_init), @@ -58,13 +48,13 @@ projection::projection(std::string const& params, bool defer_proj_init) } else { -#ifdef MAPNIK_USE_PROJ4 - init_proj4(); +#ifdef MAPNIK_USE_PROJ + init_proj(); #else - throw std::runtime_error(std::string("Cannot initialize projection '") + params_ + " ' without proj4 support (-DMAPNIK_USE_PROJ4)"); + throw std::runtime_error(std::string("Cannot initialize projection '") + params_ + " ' without proj support (-DMAPNIK_USE_PROJ)"); #endif } - if (!defer_proj_init_) init_proj4(); + if (!defer_proj_init_) init_proj(); } projection::projection(projection const& rhs) @@ -74,7 +64,7 @@ projection::projection(projection const& rhs) proj_(nullptr), proj_ctx_(nullptr) { - if (!defer_proj_init_) init_proj4(); + if (!defer_proj_init_) init_proj(); } projection& projection::operator=(projection const& rhs) @@ -83,7 +73,7 @@ projection& projection::operator=(projection const& rhs) swap(tmp); proj_ctx_ = nullptr; proj_ = nullptr; - if (!defer_proj_init_) init_proj4(); + if (!defer_proj_init_) init_proj(); return *this; } @@ -97,34 +87,35 @@ bool projection::operator!=(const projection& other) const return !(*this == other); } -void projection::init_proj4() const +void projection::init_proj() const { -#ifdef MAPNIK_USE_PROJ4 +#ifdef MAPNIK_USE_PROJ if (!proj_) { -#if PJ_VERSION >= 480 - proj_ctx_ = pj_ctx_alloc(); - proj_ = pj_init_plus_ctx(proj_ctx_, params_.c_str()); + proj_ctx_ = proj_context_create(); + proj_ = proj_create_crs_to_crs(proj_ctx_, "epsg:4326", params_.c_str(), nullptr); if (!proj_ || !proj_ctx_) { if (proj_ctx_) { - pj_ctx_free(proj_ctx_); + proj_context_destroy(proj_ctx_); proj_ctx_ = nullptr; } if (proj_) { - pj_free(proj_); + proj_destroy(proj_); proj_ = nullptr; } throw proj_init_error(params_); } -#else - #if defined(MAPNIK_THREADSAFE) - std::lock_guard lock(mutex_); - #endif - proj_ = pj_init_plus(params_.c_str()); - if (!proj_) throw proj_init_error(params_); -#endif - is_geographic_ = pj_is_latlong(proj_) ? true : false; + // determine the type of CRS + PJ* crs = proj_create(proj_ctx_, params_.c_str()); + if (crs) + { + PJ_TYPE type = proj_get_type(crs); + is_geographic_ = (type == PJ_TYPE_GEOGRAPHIC_2D_CRS + || + type == PJ_TYPE_GEOGRAPHIC_3D_CRS) ? true : false; + } + proj_destroy(crs); } #endif } @@ -151,82 +142,68 @@ std::string const& projection::params() const void projection::forward(double & x, double &y ) const { -#ifdef MAPNIK_USE_PROJ4 +#ifdef MAPNIK_USE_PROJ if (!proj_) { - throw std::runtime_error("projection::forward not supported unless proj4 is initialized"); - } - #if defined(MAPNIK_THREADSAFE) && PJ_VERSION < 480 - std::lock_guard lock(mutex_); - #endif - projUV p; - p.u = x * DEG_TO_RAD; - p.v = y * DEG_TO_RAD; - p = pj_fwd(p,proj_); - x = p.u; - y = p.v; - if (is_geographic_) - { - x *=RAD_TO_DEG; - y *=RAD_TO_DEG; + throw std::runtime_error("projection::forward not supported unless proj is initialized"); } + PJ_COORD coord; + coord.lpzt.z = 0.0; + coord.lpzt.t = HUGE_VAL; + coord.lpzt.lam = x; + coord.lpzt.phi = y; + PJ_COORD coord_out = proj_trans(proj_, PJ_FWD, coord); + x = coord_out.xy.x; + y = coord_out.xy.y; #else - throw std::runtime_error("projection::forward not supported without proj4 support (-DMAPNIK_USE_PROJ4)"); + throw std::runtime_error("projection::forward not supported without proj support (-DMAPNIK_USE_PROJ)"); #endif } void projection::inverse(double & x,double & y) const { -#ifdef MAPNIK_USE_PROJ4 +#ifdef MAPNIK_USE_PROJ if (!proj_) { - throw std::runtime_error("projection::inverse not supported unless proj4 is initialized"); + throw std::runtime_error("projection::forward not supported unless proj is initialized"); } - - #if defined(MAPNIK_THREADSAFE) && PJ_VERSION < 480 - std::lock_guard lock(mutex_); - #endif - if (is_geographic_) - { - x *=DEG_TO_RAD; - y *=DEG_TO_RAD; - } - projUV p; - p.u = x; - p.v = y; - p = pj_inv(p,proj_); - x = RAD_TO_DEG * p.u; - y = RAD_TO_DEG * p.v; + PJ_COORD coord; + coord.xyzt.z = 0.0; + coord.xyzt.t = HUGE_VAL; + coord.xyzt.x = x; + coord.xyzt.y = y; + PJ_COORD coord_out = proj_trans(proj_, PJ_INV, coord); + x = coord_out.xy.x; + y = coord_out.xy.y; #else - throw std::runtime_error("projection::inverse not supported without proj4 support (-DMAPNIK_USE_PROJ4)"); + throw std::runtime_error("projection::inverse not supported without proj support (-DMAPNIK_USE_PROJ)"); #endif } projection::~projection() { -#ifdef MAPNIK_USE_PROJ4 - #if defined(MAPNIK_THREADSAFE) && PJ_VERSION < 480 - std::lock_guard lock(mutex_); - #endif +#ifdef MAPNIK_USE_PROJ if (proj_) { - pj_free(proj_); + proj_destroy(proj_); proj_ = nullptr; } - #if PJ_VERSION >= 480 if (proj_ctx_) { - pj_ctx_free(proj_ctx_); + proj_context_destroy(proj_ctx_); proj_ctx_ = nullptr; } - #endif #endif } std::string projection::expanded() const { -#ifdef MAPNIK_USE_PROJ4 - if (proj_) return mapnik::util::trim_copy(pj_get_def( proj_, 0 )); +#ifdef MAPNIK_USE_PROJ + if (proj_) + { + PJ_PROJ_INFO info = proj_pj_info(proj_); + return mapnik::util::trim_copy(info.definition); + } #endif return params_; } diff --git a/src/well_known_srs.cpp b/src/well_known_srs.cpp index 93fd6815e..8a09bbd78 100644 --- a/src/well_known_srs.cpp +++ b/src/well_known_srs.cpp @@ -51,11 +51,11 @@ static const char * well_known_srs_strings[] = { boost::optional is_well_known_srs(std::string const& srs) { - if (srs == "+init=epsg:4326" || srs == MAPNIK_LONGLAT_PROJ) + if (srs == "epsg:4326" || srs == MAPNIK_LONGLAT_PROJ) { return boost::optional(mapnik::WGS_84); } - else if (srs == "+init=epsg:3857" || srs == MAPNIK_GMERC_PROJ) + else if (srs == "epsg:3857" || srs == MAPNIK_GMERC_PROJ) { return boost::optional(mapnik::G_MERC); } @@ -65,11 +65,11 @@ boost::optional is_well_known_srs(std::string const& srs) boost::optional is_known_geographic(std::string const& srs) { std::string trimmed = util::trim_copy(srs); - if (trimmed == "+init=epsg:3857") + if (trimmed == "epsg:3857") { return boost::optional(false); } - else if (trimmed == "+init=epsg:4326") + else if (trimmed == "epsg:4326") { return boost::optional(true); } diff --git a/test/data b/test/data index c67cf1185..004b17069 160000 --- a/test/data +++ b/test/data @@ -1 +1 @@ -Subproject commit c67cf11850d65e963e6103b6141f1eca67667aa2 +Subproject commit 004b170698a826de599e42678a27868e861e595e diff --git a/test/data-visual b/test/data-visual index bacfd251d..d0072a383 160000 --- a/test/data-visual +++ b/test/data-visual @@ -1 +1 @@ -Subproject commit bacfd251da550fa82ea56f6710dc6f85431480c2 +Subproject commit d0072a38390375794be2804b65e36982b4e50711 diff --git a/test/unit/core/exceptions_test.cpp b/test/unit/core/exceptions_test.cpp index 16ed51974..62d414ba6 100644 --- a/test/unit/core/exceptions_test.cpp +++ b/test/unit/core/exceptions_test.cpp @@ -1,4 +1,3 @@ - #include "catch.hpp" #include @@ -25,7 +24,7 @@ TEST_CASE("exceptions") { SECTION("handling") { try { - mapnik::projection srs("foo"); + mapnik::projection srs("FAIL"); // to avoid unused variable warning srs.params(); REQUIRE(false); @@ -38,7 +37,7 @@ SECTION("handling") { mapnik::projection srs("+proj=longlat foo",true); REQUIRE(srs.is_geographic()); REQUIRE(true); - srs.init_proj4(); + srs.init_proj(); // oddly init_proj4 does not throw with old proj/ubuntu precise //REQUIRE(false); } catch (...) { diff --git a/test/unit/geometry/geometry_reprojection.cpp b/test/unit/geometry/geometry_reprojection.cpp index cf64a41e8..011525de7 100644 --- a/test/unit/geometry/geometry_reprojection.cpp +++ b/test/unit/geometry/geometry_reprojection.cpp @@ -11,8 +11,8 @@ TEST_CASE("geometry reprojection") { SECTION("test_projection_4326_3857 - Empty Geometry Object") { using namespace mapnik::geometry; - mapnik::projection source("+init=epsg:4326"); - mapnik::projection dest("+init=epsg:3857"); + mapnik::projection source("epsg:4326"); + mapnik::projection dest("epsg:3857"); mapnik::proj_transform proj_trans(source, dest); { geometry_empty geom; @@ -37,8 +37,8 @@ SECTION("test_projection_4326_3857 - Empty Geometry Object") { SECTION("test_projection_4326_3857 - Empty Geometry in Geometry Variant") { using namespace mapnik::geometry; - mapnik::projection source("+init=epsg:4326"); - mapnik::projection dest("+init=epsg:3857"); + mapnik::projection source("epsg:4326"); + mapnik::projection dest("epsg:3857"); mapnik::proj_transform proj_trans(source, dest); { geometry geom = geometry_empty(); @@ -66,8 +66,8 @@ SECTION("test_projection_4326_3857 - Empty Geometry in Geometry Variant") { SECTION("test_projection_4326_3857 - Point Geometry Object") { using namespace mapnik::geometry; - mapnik::projection source("+init=epsg:4326"); - mapnik::projection dest("+init=epsg:3857"); + mapnik::projection source("epsg:4326"); + mapnik::projection dest("epsg:3857"); mapnik::proj_transform proj_trans1(source, dest); mapnik::proj_transform proj_trans2(dest, source); point geom1(-97.552175, 35.522895); @@ -119,8 +119,8 @@ SECTION("test_projection_4326_3857 - Point Geometry Object") { SECTION("test_projection_4326_3857 - Point Geometry Variant Object") { using namespace mapnik::geometry; - mapnik::projection source("+init=epsg:4326"); - mapnik::projection dest("+init=epsg:3857"); + mapnik::projection source("epsg:4326"); + mapnik::projection dest("epsg:3857"); mapnik::proj_transform proj_trans1(source, dest); mapnik::proj_transform proj_trans2(dest, source); double x1 = -97.552175; @@ -176,8 +176,8 @@ SECTION("test_projection_4326_3857 - Point Geometry Variant Object") { SECTION("test_projection_4326_3857 - Line_String Geometry Object") { using namespace mapnik::geometry; - mapnik::projection source("+init=epsg:4326"); - mapnik::projection dest("+init=epsg:3857"); + mapnik::projection source("epsg:4326"); + mapnik::projection dest("epsg:3857"); mapnik::proj_transform proj_trans1(source, dest); mapnik::proj_transform proj_trans2(dest, source); line_string geom1; @@ -241,8 +241,8 @@ SECTION("test_projection_4326_3857 - Line_String Geometry Object") { SECTION("test_projection_4326_3857 - Line_String Geometry Variant Object") { using namespace mapnik::geometry; - mapnik::projection source("+init=epsg:4326"); - mapnik::projection dest("+init=epsg:3857"); + mapnik::projection source("epsg:4326"); + mapnik::projection dest("epsg:3857"); mapnik::proj_transform proj_trans1(source, dest); mapnik::proj_transform proj_trans2(dest, source); line_string geom1_; @@ -316,8 +316,8 @@ SECTION("test_projection_4326_3857 - Line_String Geometry Variant Object") { SECTION("test_projection_4326_3857 - Polygon Geometry Object") { using namespace mapnik::geometry; - mapnik::projection source("+init=epsg:4326"); - mapnik::projection dest("+init=epsg:3857"); + mapnik::projection source("epsg:4326"); + mapnik::projection dest("epsg:3857"); mapnik::proj_transform proj_trans1(source, dest); mapnik::proj_transform proj_trans2(dest, source); polygon geom1; @@ -411,8 +411,8 @@ SECTION("test_projection_4326_3857 - Polygon Geometry Object") { SECTION("test_projection_4326_3857 - Polygon Geometry Variant Object") { using namespace mapnik::geometry; - mapnik::projection source("+init=epsg:4326"); - mapnik::projection dest("+init=epsg:3857"); + mapnik::projection source("epsg:4326"); + mapnik::projection dest("epsg:3857"); mapnik::proj_transform proj_trans1(source, dest); mapnik::proj_transform proj_trans2(dest, source); polygon geom1_; @@ -503,8 +503,8 @@ SECTION("test_projection_4326_3857 - Polygon Geometry Variant Object") { SECTION("test_projection_4326_3857 - Multi_Point Geometry Object") { using namespace mapnik::geometry; - mapnik::projection source("+init=epsg:4326"); - mapnik::projection dest("+init=epsg:3857"); + mapnik::projection source("epsg:4326"); + mapnik::projection dest("epsg:3857"); mapnik::proj_transform proj_trans1(source, dest); mapnik::proj_transform proj_trans2(dest, source); multi_point geom1; @@ -568,8 +568,8 @@ SECTION("test_projection_4326_3857 - Multi_Point Geometry Object") { SECTION("test_projection_4326_3857 - Multi_Point Geometry Variant Object") { using namespace mapnik::geometry; - mapnik::projection source("+init=epsg:4326"); - mapnik::projection dest("+init=epsg:3857"); + mapnik::projection source("epsg:4326"); + mapnik::projection dest("epsg:3857"); mapnik::proj_transform proj_trans1(source, dest); mapnik::proj_transform proj_trans2(dest, source); multi_point geom1_; @@ -643,8 +643,8 @@ SECTION("test_projection_4326_3857 - Multi_Point Geometry Variant Object") { SECTION("test_projection_4326_3857 - Multi_Line_String Geometry Object") { using namespace mapnik::geometry; - mapnik::projection source("+init=epsg:4326"); - mapnik::projection dest("+init=epsg:3857"); + mapnik::projection source("epsg:4326"); + mapnik::projection dest("epsg:3857"); mapnik::proj_transform proj_trans1(source, dest); mapnik::proj_transform proj_trans2(dest, source); line_string geom1a; @@ -720,8 +720,8 @@ SECTION("test_projection_4326_3857 - Multi_Line_String Geometry Object") { SECTION("test_projection_4326_3857 - Multi_Line_String Geometry Variant Object") { using namespace mapnik::geometry; - mapnik::projection source("+init=epsg:4326"); - mapnik::projection dest("+init=epsg:3857"); + mapnik::projection source("epsg:4326"); + mapnik::projection dest("epsg:3857"); mapnik::proj_transform proj_trans1(source, dest); mapnik::proj_transform proj_trans2(dest, source); line_string geom1a_; @@ -799,8 +799,8 @@ SECTION("test_projection_4326_3857 - Multi_Line_String Geometry Variant Object") SECTION("test_projection_4326_3857 - Multi_Polygon Geometry Object") { using namespace mapnik::geometry; - mapnik::projection source("+init=epsg:4326"); - mapnik::projection dest("+init=epsg:3857"); + mapnik::projection source("epsg:4326"); + mapnik::projection dest("epsg:3857"); mapnik::proj_transform proj_trans1(source, dest); mapnik::proj_transform proj_trans2(dest, source); polygon geom1a; @@ -898,8 +898,8 @@ SECTION("test_projection_4326_3857 - Multi_Polygon Geometry Object") { SECTION("test_projection_4326_3857 - Multi_Polygon Geometry Variant Object") { using namespace mapnik::geometry; - mapnik::projection source("+init=epsg:4326"); - mapnik::projection dest("+init=epsg:3857"); + mapnik::projection source("epsg:4326"); + mapnik::projection dest("epsg:3857"); mapnik::proj_transform proj_trans1(source, dest); mapnik::proj_transform proj_trans2(dest, source); polygon geom1a_; @@ -993,8 +993,8 @@ SECTION("test_projection_4326_3857 - Multi_Polygon Geometry Variant Object") { SECTION("test_projection_4326_3857 - Geometry Collection Object") { using namespace mapnik::geometry; - mapnik::projection source("+init=epsg:4326"); - mapnik::projection dest("+init=epsg:3857"); + mapnik::projection source("epsg:4326"); + mapnik::projection dest("epsg:3857"); mapnik::proj_transform proj_trans1(source, dest); mapnik::proj_transform proj_trans2(dest, source); polygon geom1a; @@ -1092,8 +1092,8 @@ SECTION("test_projection_4326_3857 - Geometry Collection Object") { SECTION("test_projection_4326_3857 - Geometry Collection Variant Object") { using namespace mapnik::geometry; - mapnik::projection source("+init=epsg:4326"); - mapnik::projection dest("+init=epsg:3857"); + mapnik::projection source("epsg:4326"); + mapnik::projection dest("epsg:3857"); mapnik::proj_transform proj_trans1(source, dest); mapnik::proj_transform proj_trans2(dest, source); polygon geom1a_; @@ -1187,8 +1187,8 @@ SECTION("test_projection_4326_3857 - Geometry Collection Variant Object") { SECTION("test_projection_4269_3857 - Line_String Geometry Object") { using namespace mapnik::geometry; - mapnik::projection source("+init=epsg:4269"); - mapnik::projection dest("+init=epsg:3857"); + mapnik::projection source("epsg:4269"); + mapnik::projection dest("epsg:3857"); mapnik::proj_transform proj_trans1(source, dest); mapnik::proj_transform proj_trans2(dest, source); line_string geom1; @@ -1252,8 +1252,8 @@ SECTION("test_projection_4269_3857 - Line_String Geometry Object") { SECTION("test_projection_4269_3857 - Point Geometry Object") { using namespace mapnik::geometry; - mapnik::projection source("+init=epsg:4269"); - mapnik::projection dest("+init=epsg:3857"); + mapnik::projection source("epsg:4269"); + mapnik::projection dest("epsg:3857"); mapnik::proj_transform proj_trans1(source, dest); mapnik::proj_transform proj_trans2(dest, source); point geom1(-97.552175, 35.522895); diff --git a/test/unit/geometry/geometry_strategy_test.cpp b/test/unit/geometry/geometry_strategy_test.cpp index d052f9edf..b8add9a81 100644 --- a/test/unit/geometry/geometry_strategy_test.cpp +++ b/test/unit/geometry/geometry_strategy_test.cpp @@ -18,8 +18,8 @@ SECTION("proj and view strategy") { mapnik::view_transform vt(256, 256, e); mapnik::view_strategy vs(vt); mapnik::unview_strategy uvs(vt); - mapnik::projection source("+init=epsg:4326"); - mapnik::projection dest("+init=epsg:3857"); + mapnik::projection source("epsg:4326"); + mapnik::projection dest("epsg:3857"); mapnik::proj_transform proj_trans(source, dest); mapnik::proj_transform proj_trans_rev(dest, source); mapnik::proj_strategy ps(proj_trans); diff --git a/test/unit/projection/proj_transform.cpp b/test/unit/projection/proj_transform.cpp index f8cd718d2..a900c4184 100644 --- a/test/unit/projection/proj_transform.cpp +++ b/test/unit/projection/proj_transform.cpp @@ -15,8 +15,8 @@ TEST_CASE("projection transform") SECTION("Test bounding box transforms - 4326 to 3857") { - mapnik::projection proj_4326("+init=epsg:4326"); - mapnik::projection proj_3857("+init=epsg:3857"); + mapnik::projection proj_4326("epsg:4326"); + mapnik::projection proj_3857("epsg:3857"); mapnik::proj_transform prj_trans(proj_4326, proj_3857); double minx = -45.0; @@ -48,8 +48,8 @@ SECTION("Test bounding box transforms - 4326 to 3857") #if defined(MAPNIK_USE_PROJ4) && PJ_VERSION >= 480 SECTION("test pj_transform failure behavior") { - mapnik::projection proj_4269("+init=epsg:4269"); - mapnik::projection proj_3857("+init=epsg:3857"); + mapnik::projection proj_4269("epsg:4269"); + mapnik::projection proj_3857("epsg:3857"); mapnik::proj_transform prj_trans(proj_4269, proj_3857); mapnik::proj_transform prj_trans2(proj_3857, proj_4269); @@ -125,8 +125,8 @@ SECTION("test pj_transform failure behavior") // Github Issue https://github.com/mapnik/mapnik/issues/2648 SECTION("Test proj antimeridian bbox") { - mapnik::projection prj_geog("+init=epsg:4326"); - mapnik::projection prj_proj("+init=epsg:2193"); + mapnik::projection prj_geog("epsg:4326"); + mapnik::projection prj_proj("epsg:2193"); mapnik::proj_transform prj_trans_fwd(prj_proj, prj_geog); mapnik::proj_transform prj_trans_rev(prj_geog, prj_proj); @@ -134,7 +134,7 @@ SECTION("Test proj antimeridian bbox") // reference values taken from proj4 command line tool: // (non-corner points assume PROJ_ENVELOPE_POINTS == 20) // - // cs2cs -Ef %.10f +init=epsg:2193 +to +init=epsg:4326 < 1") { - mapnik::projection const proj_4326("+init=epsg:4326"); - mapnik::projection const proj_3857("+init=epsg:3857"); - mapnik::projection const proj_2193("+init=epsg:2193"); + mapnik::projection const proj_4326("epsg:4326"); + mapnik::projection const proj_3857("epsg:3857"); + mapnik::projection const proj_2193("epsg:2193"); SECTION("lonlat <-> Web Mercator") { - // cs2cs -Ef %.10f +init=epsg:4326 +to +init=epsg:3857 < 1") #ifdef MAPNIK_USE_PROJ4 SECTION("lonlat <-> New Zealand Transverse Mercator 2000") { - // cs2cs -Ef %.10f +init=epsg:4326 +to +init=epsg:2193 < extent(16310607, 7704513, 16310621, 7704527); mapnik::view_transform tr(512, 512, extent); - mapnik::projection proj1("+init=epsg:2330"); - mapnik::projection proj2("+init=epsg:4326"); + mapnik::projection proj1("epsg:2330"); + mapnik::projection proj2("epsg:4326"); mapnik::proj_transform prj_trans(proj1, proj2); path_type path(tr, va, prj_trans); @@ -78,8 +78,8 @@ SECTION("polygon closing - epsg 32633") { va_type va(g); mapnik::box2d extent(166022, 0, 833978, 9329005); mapnik::view_transform tr(512, 512, extent); - mapnik::projection proj1("+init=epsg:32633"); - mapnik::projection proj2("+init=epsg:4326"); + mapnik::projection proj1("epsg:32633"); + mapnik::projection proj2("epsg:4326"); mapnik::proj_transform prj_trans(proj1, proj2); path_type path(tr, va, prj_trans); From b1982cc68a67f1a86affa8185bae10a79c43d8e3 Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Thu, 21 Jan 2021 10:59:54 +0000 Subject: [PATCH 07/56] Upgrade to latest mason + `proj-v7.2.1` + `sqlite-v3.34.0` --- bootstrap.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bootstrap.sh b/bootstrap.sh index c48e21e75..b13c02660 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -8,7 +8,7 @@ todo - shrink icu data ' -MASON_VERSION="fde1d9f5" +MASON_VERSION="485514d8" function setup_mason() { if [[ ! -d ./.mason ]]; then @@ -53,10 +53,10 @@ function install_mason_deps() { install libpng 1.6.28 libpng install libtiff 4.0.7 libtiff install libpq 9.6.2 - install sqlite 3.21.0 libsqlite3 + install sqlite 3.34.0 libsqlite3 install expat 2.2.0 libexpat install icu ${ICU_VERSION} - install proj 6.1.0 libproj + install proj 7.2.1 libproj install pixman 0.34.0 libpixman-1 install cairo 1.14.8 libcairo install webp 0.6.0 libwebp From d35f0596d1647a3554c16fe7617a4572d54ed45f Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Thu, 21 Jan 2021 11:01:20 +0000 Subject: [PATCH 08/56] Update data-visual --- test/data-visual | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/data-visual b/test/data-visual index d0072a383..5afb277ff 160000 --- a/test/data-visual +++ b/test/data-visual @@ -1 +1 @@ -Subproject commit d0072a38390375794be2804b65e36982b4e50711 +Subproject commit 5afb277fface40d463d5fb0f41d8e72bb67501b6 From 19511463abcc3550c976d3221e646425529e17b7 Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Thu, 21 Jan 2021 12:46:57 +0000 Subject: [PATCH 09/56] Include for HUGE_VAL --- src/projection.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/projection.cpp b/src/projection.cpp index 8a28fd4e4..bfe13ad89 100644 --- a/src/projection.cpp +++ b/src/projection.cpp @@ -31,6 +31,7 @@ #ifdef MAPNIK_USE_PROJ // proj #include +#include // HUGE_VAL #endif namespace mapnik { From ff14ad34254574040420f944508aeacfad93f100 Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Thu, 21 Jan 2021 12:47:41 +0000 Subject: [PATCH 10/56] Link to libsqlite3 when required --- benchmark/build.py | 1 + demo/c++/build.py | 2 +- src/build.py | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/benchmark/build.py b/benchmark/build.py index b08724d8d..7f77e49b1 100644 --- a/benchmark/build.py +++ b/benchmark/build.py @@ -9,6 +9,7 @@ test_env = env.Clone() test_env['LIBS'] = [env['MAPNIK_NAME']] test_env.AppendUnique(LIBS=copy(env['LIBMAPNIK_LIBS'])) test_env.AppendUnique(LIBS='mapnik-wkt') +test_env.AppendUnique(LIBS='sqlite3') if env['PLATFORM'] == 'Linux': test_env.AppendUnique(LIBS='dl') test_env.AppendUnique(LIBS='rt') diff --git a/demo/c++/build.py b/demo/c++/build.py index c059b05da..d2d2bc5fa 100644 --- a/demo/c++/build.py +++ b/demo/c++/build.py @@ -41,7 +41,7 @@ if env['HAS_CAIRO']: demo_env.Append(CPPDEFINES = '-DHAVE_CAIRO') libraries = [env['MAPNIK_NAME']] -libraries.extend(copy(env['LIBMAPNIK_LIBS'])) +libraries.extend([copy(env['LIBMAPNIK_LIBS']), 'sqlite3', 'pthread']) rundemo = demo_env.Program('rundemo', source, LIBS=libraries) Depends(rundemo, env.subst('../../src/%s' % env['MAPNIK_LIB_NAME'])) diff --git a/src/build.py b/src/build.py index 1904b02f8..171fb941e 100644 --- a/src/build.py +++ b/src/build.py @@ -78,6 +78,7 @@ if '-DHAVE_PNG' in env['CPPDEFINES']: if '-DMAPNIK_USE_PROJ' in env['CPPDEFINES']: lib_env['LIBS'].append('proj') + lib_env['LIBS'].append('sqlite3') if '-DHAVE_TIFF' in env['CPPDEFINES']: lib_env['LIBS'].append('tiff') From 6cc353f8a2187346e46831b73d2ebad6dd767a09 Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Fri, 22 Jan 2021 15:22:18 +0000 Subject: [PATCH 11/56] Use 'proj_backward_strategy' and avoid creating an extra proj_transform --- src/text/symbolizer_helpers.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/text/symbolizer_helpers.cpp b/src/text/symbolizer_helpers.cpp index 996b807cb..d85c998d3 100644 --- a/src/text/symbolizer_helpers.cpp +++ b/src/text/symbolizer_helpers.cpp @@ -326,10 +326,9 @@ void base_symbolizer_helper::initialize_points() const else if (type == geometry::geometry_types::Polygon) { auto const& poly = util::get>(geom); - proj_transform backwart_transform(prj_trans_.dest(), prj_trans_.source()); view_strategy vs(t_); - proj_strategy ps(backwart_transform); - using transform_group_type = geometry::strategy_group; + proj_backward_strategy ps(prj_trans_); + using transform_group_type = geometry::strategy_group; transform_group_type transform_group(ps, vs); geometry::polygon tranformed_poly(geometry::transform(poly, transform_group)); if (how_placed == INTERIOR_PLACEMENT) From db9829d702fb7d2a2ce9ee06fcb56fbaad499ba2 Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Fri, 22 Jan 2021 15:23:11 +0000 Subject: [PATCH 12/56] Fix missing proj context and cleanup/simplify proj_transform --- include/mapnik/proj_transform.hpp | 8 +------- src/proj_transform.cpp | 30 ++++++++++-------------------- 2 files changed, 11 insertions(+), 27 deletions(-) diff --git a/include/mapnik/proj_transform.hpp b/include/mapnik/proj_transform.hpp index 475faeefc..5fec3c589 100644 --- a/include/mapnik/proj_transform.hpp +++ b/include/mapnik/proj_transform.hpp @@ -38,8 +38,7 @@ template class box2d; class MAPNIK_DECL proj_transform : private util::noncopyable { public: - proj_transform(projection const& source, - projection const& dest); + proj_transform(projection const& source, projection const& dest); ~proj_transform(); bool equal() const; bool is_known() const; @@ -55,14 +54,9 @@ public: bool backward (box2d & box) const; bool forward (box2d & box, int points) const; bool backward (box2d & box, int points) const; - mapnik::projection const& source() const; - mapnik::projection const& dest() const; - private: PJ_CONTEXT* ctx_ = nullptr; PJ* transform_ = nullptr; - projection const& source_; - projection const& dest_; bool is_source_longlat_; bool is_dest_longlat_; bool is_source_equal_dest_; diff --git a/src/proj_transform.cpp b/src/proj_transform.cpp index a3ff8180d..2d393f03b 100644 --- a/src/proj_transform.cpp +++ b/src/proj_transform.cpp @@ -95,19 +95,17 @@ auto envelope_points(box2d const& env, std::size_t num_points) proj_transform::proj_transform(projection const& source, projection const& dest) - : source_(source), - dest_(dest), - is_source_longlat_(false), + : is_source_longlat_(false), is_dest_longlat_(false), is_source_equal_dest_(false), wgs84_to_merc_(false), merc_to_wgs84_(false) { - is_source_equal_dest_ = (source_ == dest_); + is_source_equal_dest_ = (source == dest); if (!is_source_equal_dest_) { - is_source_longlat_ = source_.is_geographic(); - is_dest_longlat_ = dest_.is_geographic(); + is_source_longlat_ = source.is_geographic(); + is_dest_longlat_ = dest.is_geographic(); boost::optional src_k = source.well_known(); boost::optional dest_k = dest.well_known(); bool known_trans = false; @@ -127,22 +125,23 @@ proj_transform::proj_transform(projection const& source, if (!known_trans) { #ifdef MAPNIK_USE_PROJ + ctx_ = proj_context_create(); transform_ = proj_create_crs_to_crs(ctx_, - source_.params().c_str(), - dest_.params().c_str(), nullptr); + source.params().c_str(), + dest.params().c_str(), nullptr); if (transform_ == nullptr) { - throw std::runtime_error(std::string("Cannot initialize proj_transform for given projections without proj4 support (-DMAPNIK_USE_PROJ): '") + source_.params() + "'->'" + dest_.params() + "'"); + throw std::runtime_error(std::string("Cannot initialize proj_transform for given projections without proj4 support (-DMAPNIK_USE_PROJ): '") + source.params() + "'->'" + dest.params() + "'"); } PJ* transform_gis = proj_normalize_for_visualization(ctx_, transform_); if (transform_gis == nullptr) { - throw std::runtime_error(std::string("Cannot initialize proj_transform for given projections without proj4 support (-DMAPNIK_USE_PROJ): '") + source_.params() + "'->'" + dest_.params() + "'"); + throw std::runtime_error(std::string("Cannot initialize proj_transform for given projections without proj4 support (-DMAPNIK_USE_PROJ): '") + source.params() + "'->'" + dest.params() + "'"); } proj_destroy(transform_); transform_ = transform_gis; #else - throw std::runtime_error(std::string("Cannot initialize proj_transform for given projections without proj4 support (-DMAPNIK_USE_PROJ): '") + source_.params() + "'->'" + dest_.params() + "'"); + throw std::runtime_error(std::string("Cannot initialize proj_transform for given projections without proj4 support (-DMAPNIK_USE_PROJ): '") + source.params() + "'->'" + dest.params() + "'"); #endif } } @@ -452,13 +451,4 @@ bool proj_transform::forward(box2d& env, int points) const return true; } -mapnik::projection const& proj_transform::source() const -{ - return source_; -} -mapnik::projection const& proj_transform::dest() const -{ - return dest_; -} - } From 5c086b0cd5140e701824a744b958e81055527e65 Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Wed, 27 Jan 2021 09:40:28 +0000 Subject: [PATCH 13/56] Add proj_transform caching to minimise expensive initialisations calls in libproj >= 6 [WIP] --- .../mapnik/feature_style_processor_impl.hpp | 59 ++++++++++++------- include/mapnik/map.hpp | 8 ++- src/map.cpp | 43 +++++++++++--- src/proj_transform.cpp | 6 +- src/projection.cpp | 23 ++++---- 5 files changed, 94 insertions(+), 45 deletions(-) diff --git a/include/mapnik/feature_style_processor_impl.hpp b/include/mapnik/feature_style_processor_impl.hpp index b0a5fe9b7..144e8d86e 100644 --- a/include/mapnik/feature_style_processor_impl.hpp +++ b/include/mapnik/feature_style_processor_impl.hpp @@ -255,8 +255,18 @@ void feature_style_processor::prepare_layer(layer_rendering_material } processor_context_ptr current_ctx = ds->get_context(ctx_map); - proj_transform prj_trans(mat.proj0_,mat.proj1_); - + std::string key = mat.proj0_.params() + mat.proj1_.params(); + auto itr = m_.proj_cache().find(key); + proj_transform * proj_trans_ptr; + if (itr == m_.proj_cache().end()) + { + proj_trans_ptr = m_.proj_cache().emplace(key, + std::make_unique(mat.proj0_, mat.proj1_)).first->second.get(); + } + else + { + proj_trans_ptr = itr->second.get(); + } box2d query_ext = extent; // unbuffered box2d buffered_query_ext(query_ext); // buffered @@ -286,22 +296,22 @@ void feature_style_processor::prepare_layer(layer_rendering_material bool early_return = false; // first, try intersection of map extent forward projected into layer srs - if (prj_trans.forward(buffered_query_ext, PROJ_ENVELOPE_POINTS) && buffered_query_ext.intersects(layer_ext)) + if (proj_trans_ptr->forward(buffered_query_ext, PROJ_ENVELOPE_POINTS) && buffered_query_ext.intersects(layer_ext)) { fw_success = true; layer_ext.clip(buffered_query_ext); } // if no intersection and projections are also equal, early return - else if (prj_trans.equal()) + else if (proj_trans_ptr->equal()) { early_return = true; } // next try intersection of layer extent back projected into map srs - else if (prj_trans.backward(layer_ext, PROJ_ENVELOPE_POINTS) && buffered_query_ext_map_srs.intersects(layer_ext)) + else if (proj_trans_ptr->backward(layer_ext, PROJ_ENVELOPE_POINTS) && buffered_query_ext_map_srs.intersects(layer_ext)) { layer_ext.clip(buffered_query_ext_map_srs); // forward project layer extent back into native projection - if (! prj_trans.forward(layer_ext, PROJ_ENVELOPE_POINTS)) + if (! proj_trans_ptr->forward(layer_ext, PROJ_ENVELOPE_POINTS)) { MAPNIK_LOG_ERROR(feature_style_processor) << "feature_style_processor: Layer=" << lay.name() @@ -353,17 +363,17 @@ void feature_style_processor::prepare_layer(layer_rendering_material layer_ext2 = lay.envelope(); if (fw_success) { - if (prj_trans.forward(query_ext, PROJ_ENVELOPE_POINTS)) + if (proj_trans_ptr->forward(query_ext, PROJ_ENVELOPE_POINTS)) { layer_ext2.clip(query_ext); } } else { - if (prj_trans.backward(layer_ext2, PROJ_ENVELOPE_POINTS)) + if (proj_trans_ptr->backward(layer_ext2, PROJ_ENVELOPE_POINTS)) { layer_ext2.clip(query_ext); - prj_trans.forward(layer_ext2, PROJ_ENVELOPE_POINTS); + proj_trans_ptr->forward(layer_ext2, PROJ_ENVELOPE_POINTS); } } @@ -496,7 +506,18 @@ void feature_style_processor::render_material(layer_rendering_materia std::vector const & rule_caches = mat.rule_caches_; - proj_transform prj_trans(mat.proj0_,mat.proj1_); + std::string key = mat.proj0_.params() + mat.proj1_.params(); + auto itr = m_.proj_cache().find(key); + proj_transform * proj_trans_ptr; + if (itr == m_.proj_cache().end()) + { + proj_trans_ptr = m_.proj_cache().emplace(key, + std::make_unique(mat.proj0_, mat.proj1_)).first->second.get(); + } + else + { + proj_trans_ptr = itr->second.get(); + } bool cache_features = lay.cache_features() && active_styles.size() > 1; @@ -525,10 +546,9 @@ void feature_style_processor::render_material(layer_rendering_materia cache->prepare(); render_style(p, style, - rule_caches[i], + rule_caches[i++], cache, - prj_trans); - ++i; + *proj_trans_ptr); } cache->clear(); } @@ -540,8 +560,7 @@ void feature_style_processor::render_material(layer_rendering_materia for (feature_type_style const* style : active_styles) { cache->prepare(); - render_style(p, style, rule_caches[i], cache, prj_trans); - ++i; + render_style(p, style, rule_caches[i++], cache, *proj_trans_ptr); } cache->clear(); } @@ -565,9 +584,8 @@ void feature_style_processor::render_material(layer_rendering_materia { cache->prepare(); render_style(p, style, - rule_caches[i], - cache, prj_trans); - ++i; + rule_caches[i++], + cache, *proj_trans_ptr); } } // We only have a single style and no grouping. @@ -579,10 +597,9 @@ void feature_style_processor::render_material(layer_rendering_materia { featureset_ptr features = *featuresets++; render_style(p, style, - rule_caches[i], + rule_caches[i++], features, - prj_trans); - ++i; + *proj_trans_ptr); } } } diff --git a/include/mapnik/map.hpp b/include/mapnik/map.hpp index e62f966b7..b53c26a11 100644 --- a/include/mapnik/map.hpp +++ b/include/mapnik/map.hpp @@ -33,7 +33,7 @@ #include #include #include - +#include #include MAPNIK_DISABLE_WARNING_PUSH #include @@ -104,7 +104,7 @@ private: boost::optional font_directory_; freetype_engine::font_file_mapping_type font_file_mapping_; freetype_engine::font_memory_cache_type font_memory_cache_; - + mutable std::map> proj_cache_; public: using const_style_iterator = std::map::const_iterator; @@ -502,6 +502,10 @@ public: { return font_memory_cache_; } + std::map> & proj_cache() const + { + return proj_cache_; + } private: friend void swap(Map & rhs, Map & lhs); diff --git a/src/map.cpp b/src/map.cpp index 9d6eabfcf..22bd01f8f 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -517,7 +517,6 @@ void Map::zoom_all() { return; } - projection proj0(srs_); box2d ext; bool success = false; bool first = true; @@ -526,10 +525,24 @@ void Map::zoom_all() if (layer.active()) { std::string const& layer_srs = layer.srs(); - projection proj1(layer_srs); - proj_transform prj_trans(proj0,proj1); + + std::string key = srs_ + layer_srs; + auto itr = proj_cache_.find(key); + proj_transform * proj_trans_ptr; + if (itr == proj_cache_.end()) + { + projection proj0(srs_, true); + projection proj1(layer_srs, true); + proj_trans_ptr = proj_cache_.emplace(key, + std::make_unique(proj0, proj1)).first->second.get(); + } + else + { + proj_trans_ptr = itr->second.get(); + } + box2d layer_ext = layer.envelope(); - if (prj_trans.backward(layer_ext, PROJ_ENVELOPE_POINTS)) + if (proj_trans_ptr->backward(layer_ext, PROJ_ENVELOPE_POINTS)) { success = true; MAPNIK_LOG_DEBUG(map) << "map: Layer " << layer.name() << " original ext=" << layer.envelope(); @@ -707,11 +720,23 @@ featureset_ptr Map::query_point(unsigned index, double x, double y) const mapnik::datasource_ptr ds = layer.datasource(); if (ds) { - mapnik::projection dest(srs_); - mapnik::projection source(layer.srs()); - proj_transform prj_trans(source,dest); + std::string key = srs_ + layer.srs(); + auto itr = proj_cache_.find(key); + proj_transform * proj_trans_ptr; + if (itr == proj_cache_.end()) + { + mapnik::projection dest(srs_, true); + mapnik::projection source(layer.srs(), true); + proj_trans_ptr = proj_cache_.emplace(key, + std::make_unique(source, dest)).first->second.get(); + } + else + { + proj_trans_ptr = itr->second.get(); + } + double z = 0; - if (!prj_trans.equal() && !prj_trans.backward(x,y,z)) + if (!proj_trans_ptr->equal() && !proj_trans_ptr->backward(x,y,z)) { throw std::runtime_error("query_point: could not project x,y into layer srs"); } @@ -721,7 +746,7 @@ featureset_ptr Map::query_point(unsigned index, double x, double y) const { map_ex.clip(*maximum_extent_); } - if (!prj_trans.backward(map_ex,PROJ_ENVELOPE_POINTS)) + if (!proj_trans_ptr->backward(map_ex,PROJ_ENVELOPE_POINTS)) { std::ostringstream s; s << "query_point: could not project map extent '" << map_ex diff --git a/src/proj_transform.cpp b/src/proj_transform.cpp index 2d393f03b..529780eb7 100644 --- a/src/proj_transform.cpp +++ b/src/proj_transform.cpp @@ -131,17 +131,17 @@ proj_transform::proj_transform(projection const& source, dest.params().c_str(), nullptr); if (transform_ == nullptr) { - throw std::runtime_error(std::string("Cannot initialize proj_transform for given projections without proj4 support (-DMAPNIK_USE_PROJ): '") + source.params() + "'->'" + dest.params() + "'"); + throw std::runtime_error(std::string("Cannot initialize proj_transform for given projections: '") + source.params() + "'->'" + dest.params() + "'"); } PJ* transform_gis = proj_normalize_for_visualization(ctx_, transform_); if (transform_gis == nullptr) { - throw std::runtime_error(std::string("Cannot initialize proj_transform for given projections without proj4 support (-DMAPNIK_USE_PROJ): '") + source.params() + "'->'" + dest.params() + "'"); + throw std::runtime_error(std::string("Cannot initialize proj_transform for given projections: '") + source.params() + "'->'" + dest.params() + "'"); } proj_destroy(transform_); transform_ = transform_gis; #else - throw std::runtime_error(std::string("Cannot initialize proj_transform for given projections without proj4 support (-DMAPNIK_USE_PROJ): '") + source.params() + "'->'" + dest.params() + "'"); + throw std::runtime_error(std::string("Cannot initialize proj_transform for given projections without proj support (-DMAPNIK_USE_PROJ): '") + source.params() + "'->'" + dest.params() + "'"); #endif } } diff --git a/src/projection.cpp b/src/projection.cpp index bfe13ad89..2da83a9e4 100644 --- a/src/projection.cpp +++ b/src/projection.cpp @@ -94,7 +94,8 @@ void projection::init_proj() const if (!proj_) { proj_ctx_ = proj_context_create(); - proj_ = proj_create_crs_to_crs(proj_ctx_, "epsg:4326", params_.c_str(), nullptr); + //proj_ = proj_create_crs_to_crs(proj_ctx_, "epsg:4326", params_.c_str(), nullptr); + proj_ = proj_create(proj_ctx_, params_.c_str()); if (!proj_ || !proj_ctx_) { if (proj_ctx_) { @@ -108,15 +109,17 @@ void projection::init_proj() const throw proj_init_error(params_); } // determine the type of CRS - PJ* crs = proj_create(proj_ctx_, params_.c_str()); - if (crs) - { - PJ_TYPE type = proj_get_type(crs); - is_geographic_ = (type == PJ_TYPE_GEOGRAPHIC_2D_CRS - || - type == PJ_TYPE_GEOGRAPHIC_3D_CRS) ? true : false; - } - proj_destroy(crs); + //PJ* crs = proj_create(proj_ctx_, params_.c_str()); + //if (crs) + //if (proj_) + //{ + //PJ_TYPE type = proj_get_type(crs); + PJ_TYPE type = proj_get_type(proj_); + is_geographic_ = (type == PJ_TYPE_GEOGRAPHIC_2D_CRS + || + type == PJ_TYPE_GEOGRAPHIC_3D_CRS) ? true : false; + //} + //proj_destroy(crs); } #endif } From e5c182591ae0ce8f2339b29f160f393b333f6f55 Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Mon, 1 Feb 2021 10:57:52 +0000 Subject: [PATCH 14/56] use unordered_map for proj_transform caching --- include/mapnik/map.hpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/include/mapnik/map.hpp b/include/mapnik/map.hpp index b53c26a11..e3837ee9d 100644 --- a/include/mapnik/map.hpp +++ b/include/mapnik/map.hpp @@ -41,7 +41,7 @@ MAPNIK_DISABLE_WARNING_PUSH MAPNIK_DISABLE_WARNING_POP // stl -#include +#include #include #include #include @@ -57,6 +57,7 @@ class layer; class MAPNIK_DECL Map : boost::equality_comparable { + using proj_cache_type = std::unordered_map>; public: enum aspect_fix_mode @@ -104,7 +105,7 @@ private: boost::optional font_directory_; freetype_engine::font_file_mapping_type font_file_mapping_; freetype_engine::font_memory_cache_type font_memory_cache_; - mutable std::map> proj_cache_; + mutable proj_cache_type proj_cache_; public: using const_style_iterator = std::map::const_iterator; @@ -502,7 +503,7 @@ public: { return font_memory_cache_; } - std::map> & proj_cache() const + proj_cache_type& proj_cache() const { return proj_cache_; } From cb75f00780d40082e70110cf1499dc07054b473e Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Thu, 25 Feb 2021 11:05:11 +0000 Subject: [PATCH 15/56] Move proj_transform initialisation to `add_layer` method --- .../mapnik/feature_style_processor_impl.hpp | 19 ++---- include/mapnik/map.hpp | 15 +---- src/map.cpp | 63 +++++++++---------- .../unit/renderer/feature_style_processor.cpp | 4 +- 4 files changed, 38 insertions(+), 63 deletions(-) diff --git a/include/mapnik/feature_style_processor_impl.hpp b/include/mapnik/feature_style_processor_impl.hpp index 144e8d86e..3de36ad75 100644 --- a/include/mapnik/feature_style_processor_impl.hpp +++ b/include/mapnik/feature_style_processor_impl.hpp @@ -257,16 +257,11 @@ void feature_style_processor::prepare_layer(layer_rendering_material processor_context_ptr current_ctx = ds->get_context(ctx_map); std::string key = mat.proj0_.params() + mat.proj1_.params(); auto itr = m_.proj_cache().find(key); - proj_transform * proj_trans_ptr; if (itr == m_.proj_cache().end()) { - proj_trans_ptr = m_.proj_cache().emplace(key, - std::make_unique(mat.proj0_, mat.proj1_)).first->second.get(); - } - else - { - proj_trans_ptr = itr->second.get(); + throw std::runtime_error("Failed to initialise projection transform"); } + proj_transform * proj_trans_ptr = itr->second.get(); box2d query_ext = extent; // unbuffered box2d buffered_query_ext(query_ext); // buffered @@ -508,17 +503,11 @@ void feature_style_processor::render_material(layer_rendering_materia std::string key = mat.proj0_.params() + mat.proj1_.params(); auto itr = m_.proj_cache().find(key); - proj_transform * proj_trans_ptr; if (itr == m_.proj_cache().end()) { - proj_trans_ptr = m_.proj_cache().emplace(key, - std::make_unique(mat.proj0_, mat.proj1_)).first->second.get(); + throw std::runtime_error("Failed to initialize projection transform"); } - else - { - proj_trans_ptr = itr->second.get(); - } - + proj_transform* proj_trans_ptr = itr->second.get(); bool cache_features = lay.cache_features() && active_styles.size() > 1; datasource_ptr ds = lay.datasource(); diff --git a/include/mapnik/map.hpp b/include/mapnik/map.hpp index e3837ee9d..dabb8653d 100644 --- a/include/mapnik/map.hpp +++ b/include/mapnik/map.hpp @@ -105,7 +105,7 @@ private: boost::optional font_directory_; freetype_engine::font_file_mapping_type font_file_mapping_; freetype_engine::font_memory_cache_type font_memory_cache_; - mutable proj_cache_type proj_cache_; + proj_cache_type proj_cache_; public: using const_style_iterator = std::map::const_iterator; @@ -261,12 +261,6 @@ public: */ layer const& get_layer(size_t index) const; - /*! \brief Get a layer. - * @param index layer number. - * @return Non-constant layer. - */ - layer& get_layer(size_t index); - /*! \brief Remove a layer. * @param index layer number. */ @@ -277,11 +271,6 @@ public: */ std::vector const& layers() const; - /*! \brief Get all layers. - * @return Non-constant layers. - */ - std::vector & layers(); - /*! \brief Remove all layers and styles from the map. */ void remove_all(); @@ -503,7 +492,7 @@ public: { return font_memory_cache_; } - proj_cache_type& proj_cache() const + proj_cache_type const& proj_cache() const { return proj_cache_; } diff --git a/src/map.cpp b/src/map.cpp index 22bd01f8f..8e943bbf1 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -110,8 +110,9 @@ Map::Map(Map const& rhs) extra_params_(rhs.extra_params_), font_directory_(rhs.font_directory_), font_file_mapping_(rhs.font_file_mapping_), - // on copy discard memory cache - font_memory_cache_() {} + // on copy discard memory caches + font_memory_cache_(), + proj_cache_() {} Map::Map(Map && rhs) @@ -133,7 +134,8 @@ Map::Map(Map && rhs) extra_params_(std::move(rhs.extra_params_)), font_directory_(std::move(rhs.font_directory_)), font_file_mapping_(std::move(rhs.font_file_mapping_)), - font_memory_cache_(std::move(rhs.font_memory_cache_)) {} + font_memory_cache_(std::move(rhs.font_memory_cache_)), + proj_cache_(std::move(rhs.proj_cache_)) {} Map::~Map() {} @@ -164,7 +166,7 @@ void swap (Map & lhs, Map & rhs) std::swap(lhs.extra_params_, rhs.extra_params_); std::swap(lhs.font_directory_,rhs.font_directory_); std::swap(lhs.font_file_mapping_,rhs.font_file_mapping_); - // on assignment discard memory cache + // on assignment discard memory caches //std::swap(lhs.font_memory_cache_,rhs.font_memory_cache_); } @@ -323,11 +325,29 @@ size_t Map::layer_count() const void Map::add_layer(layer const& l) { + std::string key = srs_ + l.srs(); + auto itr = proj_cache_.find(key); + if (itr == proj_cache_.end()) + { + mapnik::projection source(srs_, true); + mapnik::projection dest(l.srs(), true); + proj_cache_.emplace(key, + std::make_unique(source, dest)); + } layers_.emplace_back(l); } void Map::add_layer(layer && l) { + std::string key = srs_ + l.srs(); + auto itr = proj_cache_.find(key); + if (itr == proj_cache_.end()) + { + mapnik::projection source(srs_, true); + mapnik::projection dest(l.srs(), true); + proj_cache_.emplace(key, + std::make_unique(source, dest)); + } layers_.push_back(std::move(l)); } @@ -350,21 +370,11 @@ layer const& Map::get_layer(size_t index) const return layers_[index]; } -layer& Map::get_layer(size_t index) -{ - return layers_[index]; -} - std::vector const& Map::layers() const { return layers_; } -std::vector & Map::layers() -{ - return layers_; -} - unsigned Map::width() const { return width_; @@ -528,19 +538,12 @@ void Map::zoom_all() std::string key = srs_ + layer_srs; auto itr = proj_cache_.find(key); - proj_transform * proj_trans_ptr; + if (itr == proj_cache_.end()) { - projection proj0(srs_, true); - projection proj1(layer_srs, true); - proj_trans_ptr = proj_cache_.emplace(key, - std::make_unique(proj0, proj1)).first->second.get(); + throw std::runtime_error("Failed to initialise projection transform"); } - else - { - proj_trans_ptr = itr->second.get(); - } - + proj_transform* proj_trans_ptr = itr->second.get(); box2d layer_ext = layer.envelope(); if (proj_trans_ptr->backward(layer_ext, PROJ_ENVELOPE_POINTS)) { @@ -722,18 +725,12 @@ featureset_ptr Map::query_point(unsigned index, double x, double y) const { std::string key = srs_ + layer.srs(); auto itr = proj_cache_.find(key); - proj_transform * proj_trans_ptr; + if (itr == proj_cache_.end()) { - mapnik::projection dest(srs_, true); - mapnik::projection source(layer.srs(), true); - proj_trans_ptr = proj_cache_.emplace(key, - std::make_unique(source, dest)).first->second.get(); - } - else - { - proj_trans_ptr = itr->second.get(); + throw std::runtime_error("Failed to initialise projection transform"); } + proj_transform * proj_trans_ptr = itr->second.get(); double z = 0; if (!proj_trans_ptr->equal() && !proj_trans_ptr->backward(x,y,z)) diff --git a/test/unit/renderer/feature_style_processor.cpp b/test/unit/renderer/feature_style_processor.cpp index 03b9b344d..ed125bf52 100644 --- a/test/unit/renderer/feature_style_processor.cpp +++ b/test/unit/renderer/feature_style_processor.cpp @@ -195,7 +195,7 @@ SECTION("test_renderer - apply() with single layer") { rendering_result result; test_renderer renderer(map, result); std::set attributes; - mapnik::layer & layer = map.get_layer(0); + mapnik::layer const& layer = map.get_layer(0); renderer.apply(layer, attributes); REQUIRE(renderer.painted()); @@ -222,7 +222,7 @@ SECTION("test_renderer - apply_to_layer") { test_renderer renderer(map, result); std::set attributes; mapnik::projection map_proj(map.srs(), true); - mapnik::layer & layer = map.get_layer(0); + mapnik::layer const& layer = map.get_layer(0); renderer.apply_to_layer(layer, renderer, map_proj, From a149ebed16a8c339da96caee1ba013e9fcfa6d67 Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Tue, 2 Mar 2021 16:39:32 +0000 Subject: [PATCH 16/56] use std::size_t consistently (proj API) --- include/mapnik/proj_transform.hpp | 8 ++++---- src/proj_transform.cpp | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/include/mapnik/proj_transform.hpp b/include/mapnik/proj_transform.hpp index 5fec3c589..daf84b52c 100644 --- a/include/mapnik/proj_transform.hpp +++ b/include/mapnik/proj_transform.hpp @@ -44,16 +44,16 @@ public: bool is_known() const; bool forward (double& x, double& y , double& z) const; bool backward (double& x, double& y , double& z) const; - bool forward (double *x, double *y , double *z, int point_count, int offset = 1) const; - bool backward (double *x, double *y , double *z, int point_count, int offset = 1) const; + bool forward (double *x, double *y , double *z, std::size_t point_count, std::size_t offset = 1) const; + bool backward (double *x, double *y , double *z, std::size_t point_count, std::size_t offset = 1) const; bool forward (geometry::point & p) const; bool backward (geometry::point & p) const; unsigned int forward (std::vector> & ls) const; unsigned int backward (std::vector> & ls) const; bool forward (box2d & box) const; bool backward (box2d & box) const; - bool forward (box2d & box, int points) const; - bool backward (box2d & box, int points) const; + bool forward (box2d & box, std::size_t points) const; + bool backward (box2d & box, std::size_t points) const; private: PJ_CONTEXT* ctx_ = nullptr; PJ* transform_ = nullptr; diff --git a/src/proj_transform.cpp b/src/proj_transform.cpp index 529780eb7..380b55b3a 100644 --- a/src/proj_transform.cpp +++ b/src/proj_transform.cpp @@ -214,7 +214,7 @@ unsigned int proj_transform::forward (std::vector> & ls) return 0; } -bool proj_transform::forward (double * x, double * y , double * z, int point_count, int offset) const +bool proj_transform::forward (double * x, double * y , double * z, std::size_t point_count, std::size_t offset) const { if (is_source_equal_dest_) return true; @@ -240,7 +240,7 @@ bool proj_transform::forward (double * x, double * y , double * z, int point_cou return true; } -bool proj_transform::backward (double * x, double * y , double * z, int point_count, int offset) const +bool proj_transform::backward (double * x, double * y , double * z, std::size_t point_count, std::size_t offset) const { if (is_source_equal_dest_) return true; @@ -372,7 +372,7 @@ bool proj_transform::backward (box2d & box) const // Alternative is to provide proper clipping box // in the target srs by setting map 'maximum-extent' -bool proj_transform::backward(box2d& env, int points) const +bool proj_transform::backward(box2d& env, std::size_t points) const { if (is_source_equal_dest_) return true; @@ -411,7 +411,7 @@ bool proj_transform::backward(box2d& env, int points) const return true; } -bool proj_transform::forward(box2d& env, int points) const +bool proj_transform::forward(box2d& env, std::size_t points) const { if (is_source_equal_dest_) return true; From 11ff758c38b7ed8ee0d7d794d30d710d56255802 Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Tue, 2 Mar 2021 17:07:13 +0000 Subject: [PATCH 17/56] Add proj_transform::definition() method --- include/mapnik/proj_transform.hpp | 1 + src/proj_transform.cpp | 23 ++++++++++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/include/mapnik/proj_transform.hpp b/include/mapnik/proj_transform.hpp index daf84b52c..2287ef74f 100644 --- a/include/mapnik/proj_transform.hpp +++ b/include/mapnik/proj_transform.hpp @@ -54,6 +54,7 @@ public: bool backward (box2d & box) const; bool forward (box2d & box, std::size_t points) const; bool backward (box2d & box, std::size_t points) const; + std::string definition() const; private: PJ_CONTEXT* ctx_ = nullptr; PJ* transform_ = nullptr; diff --git a/src/proj_transform.cpp b/src/proj_transform.cpp index 380b55b3a..5f391bb0d 100644 --- a/src/proj_transform.cpp +++ b/src/proj_transform.cpp @@ -28,7 +28,7 @@ #include #include #include - +#include // boost #include @@ -451,4 +451,25 @@ bool proj_transform::forward(box2d& env, std::size_t points) const return true; } +std::string proj_transform::definition() const +{ +#ifdef MAPNIK_USE_PROJ + if (transform_) + { + PJ_PROJ_INFO info = proj_pj_info(transform_); + return mapnik::util::trim_copy(info.definition); + } + else +#endif + if (wgs84_to_merc_) + { + return "wgs84 => merc"; + } + else if (merc_to_wgs84_) + { + return "merc => wgs84"; + } + return "unknown"; + } + } From 6fedae386d8f77a09e24d2020d296ddfdc3fb08a Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Wed, 3 Mar 2021 11:13:42 +0000 Subject: [PATCH 18/56] add c++20 --- SConstruct | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SConstruct b/SConstruct index e357bd4aa..9c329091f 100644 --- a/SConstruct +++ b/SConstruct @@ -1219,7 +1219,7 @@ int main() context.Result(ret) return ret -__cplusplus = {'14':'201402L', '17':'201703L'} +__cplusplus = {'14':'201402L', '17':'201703L', '20':'202002L'} def supports_cxx_std (context, silent=False): cplusplus_string = __cplusplus[env['CXX_STD']] From b75737fd6a1dea2fb9531c8e2be91cd502a892fe Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Wed, 3 Mar 2021 15:01:58 +0000 Subject: [PATCH 19/56] Implement proj_transform caching using boost::unordered_map which allows calling `find` method with compatible key type. In this case `std::pair` avoiding potentially expensive temp string keys. (TODO: In the future use c++20 `std::unordered_map::find` transparent keys facility) --- .../mapnik/feature_style_processor_impl.hpp | 24 +++------- include/mapnik/map.hpp | 35 ++++++++++++--- src/map.cpp | 44 +++++++++---------- 3 files changed, 54 insertions(+), 49 deletions(-) diff --git a/include/mapnik/feature_style_processor_impl.hpp b/include/mapnik/feature_style_processor_impl.hpp index 3de36ad75..d85c3235d 100644 --- a/include/mapnik/feature_style_processor_impl.hpp +++ b/include/mapnik/feature_style_processor_impl.hpp @@ -66,10 +66,9 @@ struct layer_rendering_material std::vector materials_; layer_rendering_material(layer const& lay, projection const& dest) - : - lay_(lay), - proj0_(dest), - proj1_(lay.srs(),true) {} + : lay_(lay), + proj0_(dest), + proj1_(lay.srs(), true) {} layer_rendering_material(layer_rendering_material && rhs) = default; }; @@ -255,13 +254,7 @@ void feature_style_processor::prepare_layer(layer_rendering_material } processor_context_ptr current_ctx = ds->get_context(ctx_map); - std::string key = mat.proj0_.params() + mat.proj1_.params(); - auto itr = m_.proj_cache().find(key); - if (itr == m_.proj_cache().end()) - { - throw std::runtime_error("Failed to initialise projection transform"); - } - proj_transform * proj_trans_ptr = itr->second.get(); + proj_transform * proj_trans_ptr = m_.get_proj_transform(mat.proj0_.params(), mat.proj1_.params()); box2d query_ext = extent; // unbuffered box2d buffered_query_ext(query_ext); // buffered @@ -500,14 +493,7 @@ void feature_style_processor::render_material(layer_rendering_materia layer const& lay = mat.lay_; std::vector const & rule_caches = mat.rule_caches_; - - std::string key = mat.proj0_.params() + mat.proj1_.params(); - auto itr = m_.proj_cache().find(key); - if (itr == m_.proj_cache().end()) - { - throw std::runtime_error("Failed to initialize projection transform"); - } - proj_transform* proj_trans_ptr = itr->second.get(); + proj_transform * proj_trans_ptr = m_.get_proj_transform(mat.proj0_.params(), mat.proj1_.params()); bool cache_features = lay.cache_features() && active_styles.size() > 1; datasource_ptr ds = lay.datasource(); diff --git a/include/mapnik/map.hpp b/include/mapnik/map.hpp index dabb8653d..73b6f2de7 100644 --- a/include/mapnik/map.hpp +++ b/include/mapnik/map.hpp @@ -38,10 +38,12 @@ MAPNIK_DISABLE_WARNING_PUSH #include #include +#include +#include +#include MAPNIK_DISABLE_WARNING_POP // stl -#include #include #include #include @@ -57,8 +59,32 @@ class layer; class MAPNIK_DECL Map : boost::equality_comparable { - using proj_cache_type = std::unordered_map>; public: + using key_type = std::pair; + using compatible_key_type = std::pair; + + struct compatible_hash + { + template + std::size_t operator() (KeyType const& key) const + { + using hash_type = boost::hash; + std::size_t seed = hash_type{}(key.first); + seed ^= hash_type{}(key.second) + 0x9e3779b9 + (seed << 6) + (seed >> 2); + return seed; + } + }; + + struct compatible_predicate + { + bool operator()(compatible_key_type const& k1, + compatible_key_type const& k2) const + { + return k1 == k2; + } + }; + + using proj_cache_type = boost::unordered_map, compatible_hash>; enum aspect_fix_mode { @@ -492,11 +518,8 @@ public: { return font_memory_cache_; } - proj_cache_type const& proj_cache() const - { - return proj_cache_; - } + proj_transform * get_proj_transform(std::string const& source, std::string const& dest) const; private: friend void swap(Map & rhs, Map & lhs); void fixAspectRatio(); diff --git a/src/map.cpp b/src/map.cpp index 8e943bbf1..374b8212a 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -323,15 +323,27 @@ size_t Map::layer_count() const return layers_.size(); } +proj_transform * Map::get_proj_transform(std::string const& source, std::string const& dest) const +{ + compatible_key_type key = std::make_pair(source, dest); + auto itr = proj_cache_.find(key, compatible_hash{}, compatible_predicate{}); + if (itr == proj_cache_.end()) + { + throw std::runtime_error("Failed to initialise projection transform:" + + key.first.to_string() + " -> " + key.second.to_string()); + } + return itr->second.get(); +} + void Map::add_layer(layer const& l) { - std::string key = srs_ + l.srs(); - auto itr = proj_cache_.find(key); + compatible_key_type key = std::make_pair(srs_, l.srs()); + auto itr = proj_cache_.find(key, compatible_hash{}, compatible_predicate{}); if (itr == proj_cache_.end()) { mapnik::projection source(srs_, true); mapnik::projection dest(l.srs(), true); - proj_cache_.emplace(key, + proj_cache_.emplace(std::make_pair(srs_, l.srs()), std::make_unique(source, dest)); } layers_.emplace_back(l); @@ -339,13 +351,13 @@ void Map::add_layer(layer const& l) void Map::add_layer(layer && l) { - std::string key = srs_ + l.srs(); - auto itr = proj_cache_.find(key); + compatible_key_type key = std::make_pair(srs_, l.srs()); + auto itr = proj_cache_.find(key, compatible_hash{}, compatible_predicate{}); if (itr == proj_cache_.end()) { mapnik::projection source(srs_, true); mapnik::projection dest(l.srs(), true); - proj_cache_.emplace(key, + proj_cache_.emplace(make_pair(srs_, l.srs()), std::make_unique(source, dest)); } layers_.push_back(std::move(l)); @@ -535,15 +547,7 @@ void Map::zoom_all() if (layer.active()) { std::string const& layer_srs = layer.srs(); - - std::string key = srs_ + layer_srs; - auto itr = proj_cache_.find(key); - - if (itr == proj_cache_.end()) - { - throw std::runtime_error("Failed to initialise projection transform"); - } - proj_transform* proj_trans_ptr = itr->second.get(); + proj_transform * proj_trans_ptr = get_proj_transform(srs_, layer_srs);; box2d layer_ext = layer.envelope(); if (proj_trans_ptr->backward(layer_ext, PROJ_ENVELOPE_POINTS)) { @@ -723,15 +727,7 @@ featureset_ptr Map::query_point(unsigned index, double x, double y) const mapnik::datasource_ptr ds = layer.datasource(); if (ds) { - std::string key = srs_ + layer.srs(); - auto itr = proj_cache_.find(key); - - if (itr == proj_cache_.end()) - { - throw std::runtime_error("Failed to initialise projection transform"); - } - proj_transform * proj_trans_ptr = itr->second.get(); - + proj_transform * proj_trans_ptr = get_proj_transform(srs_ ,layer.srs()); double z = 0; if (!proj_trans_ptr->equal() && !proj_trans_ptr->backward(x,y,z)) { From 554556e4d51f1bc1cbe4ecff3f70e12345ed8a39 Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Thu, 4 Mar 2021 10:18:42 +0000 Subject: [PATCH 20/56] Add support for libproj >=8 --- include/mapnik/projection.hpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/include/mapnik/projection.hpp b/include/mapnik/projection.hpp index ac78a4469..4d7be1708 100644 --- a/include/mapnik/projection.hpp +++ b/include/mapnik/projection.hpp @@ -39,9 +39,14 @@ MAPNIK_DISABLE_WARNING_POP // fwd decl +#if PROJ_VERSION_MAJOR >= 8 +struct pj_ctx; +using PJ_CONTEXT = struct pj_ctx; +#else struct projCtx_t; -struct PJconsts; using PJ_CONTEXT = struct projCtx_t; +#endif +struct PJconsts; using PJ = struct PJconsts; namespace mapnik { @@ -59,7 +64,7 @@ class MAPNIK_DECL projection public: projection(std::string const& params, - bool defer_proj_init = false); + bool defer_proj_init = false); projection(projection const& rhs); ~projection(); From b532beccde8c80cb5a882efbd2d68e13a8d42bbc Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Thu, 4 Mar 2021 10:19:44 +0000 Subject: [PATCH 21/56] mapnik::Map ctor/operator= - initialise proj transforms --- include/mapnik/map.hpp | 1 + src/map.cpp | 24 +++++++++++++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/include/mapnik/map.hpp b/include/mapnik/map.hpp index 73b6f2de7..0140201ae 100644 --- a/include/mapnik/map.hpp +++ b/include/mapnik/map.hpp @@ -523,6 +523,7 @@ public: private: friend void swap(Map & rhs, Map & lhs); void fixAspectRatio(); + void init_proj_transforms(); }; DEFINE_ENUM(aspect_fix_mode_e,Map::aspect_fix_mode); diff --git a/src/map.cpp b/src/map.cpp index 374b8212a..43e121e93 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -112,7 +112,10 @@ Map::Map(Map const& rhs) font_file_mapping_(rhs.font_file_mapping_), // on copy discard memory caches font_memory_cache_(), - proj_cache_() {} + proj_cache_() +{ + init_proj_transforms(); +} Map::Map(Map && rhs) @@ -142,6 +145,7 @@ Map::~Map() {} Map& Map::operator=(Map rhs) { swap(*this, rhs); + init_proj_transforms(); return *this; } @@ -375,6 +379,7 @@ void Map::remove_all() fontsets_.clear(); font_file_mapping_.clear(); font_memory_cache_.clear(); + proj_cache_.clear(); } layer const& Map::get_layer(size_t index) const @@ -442,6 +447,7 @@ std::string const& Map::srs() const void Map::set_srs(std::string const& _srs) { srs_ = _srs; + init_proj_transforms(); } void Map::set_buffer_size(int _buffer_size) @@ -790,4 +796,20 @@ void Map::set_extra_parameters(parameters& params) extra_params_ = params; } +void Map::init_proj_transforms() +{ + for (auto const& l : layers_) + { + compatible_key_type key = std::make_pair(srs_, l.srs()); + auto itr = proj_cache_.find(key, compatible_hash{}, compatible_predicate{}); + if (itr == proj_cache_.end()) + { + mapnik::projection source(srs_, true); + mapnik::projection dest(l.srs(), true); + proj_cache_.emplace(std::make_pair(srs_, l.srs()), + std::make_unique(source, dest)); + } + } +} + } From fb325f527b93e58f389f956b5d4cad53afc0de46 Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Mon, 8 Mar 2021 13:19:11 +0000 Subject: [PATCH 22/56] Fix `updateData` signal signature so it actually works + cleanups --- demo/viewer/layerlistmodel.cpp | 15 ++++++++++----- demo/viewer/layerwidget.cpp | 13 +++++-------- demo/viewer/layerwidget.hpp | 28 ++++++++++++++-------------- 3 files changed, 29 insertions(+), 27 deletions(-) diff --git a/demo/viewer/layerlistmodel.cpp b/demo/viewer/layerlistmodel.cpp index 55af8e76e..ec0eaac26 100644 --- a/demo/viewer/layerlistmodel.cpp +++ b/demo/viewer/layerlistmodel.cpp @@ -21,13 +21,12 @@ #include "layerlistmodel.hpp" #include - -#include +#include #include using mapnik::Map; -LayerListModel::LayerListModel(std::shared_ptr map,QObject *parent) +LayerListModel::LayerListModel(std::shared_ptr map, QObject *parent) : QAbstractListModel(parent), map_(map) {} @@ -37,7 +36,7 @@ int LayerListModel::rowCount(QModelIndex const&) const return 0; } -QVariant LayerListModel::data(QModelIndex const& index,int role) const +QVariant LayerListModel::data(QModelIndex const& index, int role) const { if (!index.isValid() || !map_) return QVariant(); @@ -64,6 +63,13 @@ QVariant LayerListModel::data(QModelIndex const& index,int role) const else return QVariant(Qt::Unchecked); } + else if (role == Qt::ForegroundRole) + { + if (map_->layers().at(index.row()).active()) + return QBrush(QColor("black")); + else + return QBrush(QColor("lightgrey")); + } else { return QVariant(); @@ -101,7 +107,6 @@ bool LayerListModel::setData(const QModelIndex &index, Qt::ItemFlags LayerListModel::flags(QModelIndex const& index) const { Qt::ItemFlags flags = QAbstractItemModel::flags(index); - if (index.isValid()) flags |= Qt::ItemIsUserCheckable; return flags; diff --git a/demo/viewer/layerwidget.cpp b/demo/viewer/layerwidget.cpp index a6ab6b8f4..5733f1e63 100644 --- a/demo/viewer/layerwidget.cpp +++ b/demo/viewer/layerwidget.cpp @@ -29,12 +29,9 @@ #include #include #include -#include #include "layerlistmodel.hpp" #include "layer_info_dialog.hpp" -using namespace std; - LayerTab::LayerTab(QWidget* parent) : QListView(parent) {} @@ -45,11 +42,11 @@ void LayerTab::paintEvent(QPaintEvent *e) } void LayerTab::dataChanged(const QModelIndex &topLeft, - const QModelIndex &bottomRight) + const QModelIndex &bottomRight, + const QVector &roles) { - QListView::dataChanged(topLeft, bottomRight); - qDebug("FIXME : update map view!"); - emit update_mapwidget(); + emit update_mapwidget(); + QListView::dataChanged(topLeft, bottomRight, roles); } void LayerTab::selectionChanged(const QItemSelection & selected, const QItemSelection &) @@ -57,7 +54,7 @@ void LayerTab::selectionChanged(const QItemSelection & selected, const QItemSele QModelIndexList list = selected.indexes(); if (list.size() != 0) { - std::cout << "SELECTED LAYER ->" << list[0].row() << "\n"; + qDebug("SELECTED LAYER -> %d",list[0].row()); emit layerSelected(list[0].row()); } } diff --git a/demo/viewer/layerwidget.hpp b/demo/viewer/layerwidget.hpp index f59196de7..6761d79a4 100644 --- a/demo/viewer/layerwidget.hpp +++ b/demo/viewer/layerwidget.hpp @@ -27,19 +27,19 @@ class LayerTab : public QListView { - Q_OBJECT - public: - LayerTab(QWidget* parent=0); - void paintEvent(QPaintEvent *e); - signals: - void update_mapwidget(); - void layerSelected(int) const; - public slots: - void layerInfo(); - void layerInfo2(QModelIndex const&); - protected slots: - void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); - void selectionChanged(const QItemSelection & selected, const QItemSelection &); + Q_OBJECT +public: + LayerTab(QWidget* parent=0); + void paintEvent(QPaintEvent *e); +signals: + void update_mapwidget(); + void layerSelected(int) const; +public slots: + void layerInfo(); + void layerInfo2(QModelIndex const&); +protected slots: + void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles); + void selectionChanged(const QItemSelection & selected, const QItemSelection &); }; class StyleTab : public QTreeView @@ -48,7 +48,7 @@ class StyleTab : public QTreeView public: StyleTab(QWidget* parent=0); protected: - void contextMenuEvent(QContextMenuEvent * event ); + void contextMenuEvent(QContextMenuEvent * event ); }; #endif From 7cbbd7305be80a7fc20155539ab3c87a4d82caf4 Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Tue, 9 Mar 2021 10:48:32 +0000 Subject: [PATCH 23/56] Map::mapnik - restore non-const access to layers + create proj_transform and cache in `get_proj_transform` + declare `proj_cache_` `mutable` --- include/mapnik/map.hpp | 13 ++++++++++++- src/map.cpp | 16 ++++++++++++++-- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/include/mapnik/map.hpp b/include/mapnik/map.hpp index 0140201ae..face79aca 100644 --- a/include/mapnik/map.hpp +++ b/include/mapnik/map.hpp @@ -131,7 +131,7 @@ private: boost::optional font_directory_; freetype_engine::font_file_mapping_type font_file_mapping_; freetype_engine::font_memory_cache_type font_memory_cache_; - proj_cache_type proj_cache_; + mutable proj_cache_type proj_cache_; public: using const_style_iterator = std::map::const_iterator; @@ -287,6 +287,12 @@ public: */ layer const& get_layer(size_t index) const; + /*! \brief Get a layer. + * @param index layer number. + * @return Non-constant layer. + */ + layer& get_layer(size_t index); + /*! \brief Remove a layer. * @param index layer number. */ @@ -297,6 +303,11 @@ public: */ std::vector const& layers() const; + /*! \brief Get all layers. + * @return Non-constant layers. + */ + std::vector & layers(); + /*! \brief Remove all layers and styles from the map. */ void remove_all(); diff --git a/src/map.cpp b/src/map.cpp index 43e121e93..a0ca76bf5 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -333,8 +333,10 @@ proj_transform * Map::get_proj_transform(std::string const& source, std::string auto itr = proj_cache_.find(key, compatible_hash{}, compatible_predicate{}); if (itr == proj_cache_.end()) { - throw std::runtime_error("Failed to initialise projection transform:" + - key.first.to_string() + " -> " + key.second.to_string()); + mapnik::projection srs1(source, true); + mapnik::projection srs2(dest, true); + return proj_cache_.emplace(std::make_pair(source, dest), + std::make_unique(srs1, srs2)).first->second.get(); } return itr->second.get(); } @@ -387,11 +389,21 @@ layer const& Map::get_layer(size_t index) const return layers_[index]; } +layer& Map::get_layer(size_t index) +{ + return layers_[index]; +} + std::vector const& Map::layers() const { return layers_; } +std::vector & Map::layers() +{ + return layers_; +} + unsigned Map::width() const { return width_; From 977c65e9deb199dc3e110c828430d189378667f0 Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Tue, 9 Mar 2021 11:33:21 +0000 Subject: [PATCH 24/56] Fix proj_transform order in Map::query_point --- src/map.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/map.cpp b/src/map.cpp index a0ca76bf5..4cad760fe 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -745,7 +745,7 @@ featureset_ptr Map::query_point(unsigned index, double x, double y) const mapnik::datasource_ptr ds = layer.datasource(); if (ds) { - proj_transform * proj_trans_ptr = get_proj_transform(srs_ ,layer.srs()); + proj_transform * proj_trans_ptr = get_proj_transform(layer.srs(), srs_); double z = 0; if (!proj_trans_ptr->equal() && !proj_trans_ptr->backward(x,y,z)) { From eb49b5776a2c94ef82e1c33866753a6f6a0bc742 Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Tue, 9 Mar 2021 15:02:11 +0000 Subject: [PATCH 25/56] Fix BBOX in geographic WGS84 (epsg:4326) calc logic --- demo/viewer/mapwidget.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/demo/viewer/mapwidget.cpp b/demo/viewer/mapwidget.cpp index de4523059..a9eb166f5 100644 --- a/demo/viewer/mapwidget.cpp +++ b/demo/viewer/mapwidget.cpp @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include #include #include @@ -592,8 +592,12 @@ void MapWidget::updateMap() double y0 = ext.miny(); double x1 = ext.maxx(); double y1 = ext.maxy(); - prj.inverse(x0,y0); - prj.inverse(x1,y1); + double z = 0; + std::string dest_srs = {"epsg:4326"}; + mapnik::proj_transform proj_tr(map_->srs(), dest_srs); + + proj_tr.forward(x0, y0, z); + proj_tr.forward(x1, y1, z); std::cout << "BBOX (WGS84): " << x0 << "," << y0 << "," << x1 << "," << y1 << "\n"; update(); // emit signal to interested widgets From f8877e2263941e3f190a014d0ae7c40bba5d714a Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Tue, 9 Mar 2021 16:12:54 +0000 Subject: [PATCH 26/56] Add missing © --- test/unit/map/background.cpp | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/test/unit/map/background.cpp b/test/unit/map/background.cpp index 18c1b4bf6..b83c6119d 100644 --- a/test/unit/map/background.cpp +++ b/test/unit/map/background.cpp @@ -1,3 +1,24 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2021 Artem Pavlenko + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ #include "catch.hpp" @@ -63,4 +84,3 @@ SECTION("set background - cairo") { #endif } - From 0e0d222ede1724278beebea3ac05714da0e1b852 Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Tue, 9 Mar 2021 16:13:20 +0000 Subject: [PATCH 27/56] Print mapnik::Map (useful gathering `query_map_point` data) --- demo/viewer/mapwidget.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/demo/viewer/mapwidget.cpp b/demo/viewer/mapwidget.cpp index a9eb166f5..39b682e8e 100644 --- a/demo/viewer/mapwidget.cpp +++ b/demo/viewer/mapwidget.cpp @@ -598,6 +598,7 @@ void MapWidget::updateMap() proj_tr.forward(x0, y0, z); proj_tr.forward(x1, y1, z); + std::cout << "MAP SIZE:" << map_->width() << "," << map_->height() << std::endl; std::cout << "BBOX (WGS84): " << x0 << "," << y0 << "," << x1 << "," << y1 << "\n"; update(); // emit signal to interested widgets From ebc3dabd6da84ad5fe5ed791afe104287a4003ea Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Tue, 9 Mar 2021 16:14:26 +0000 Subject: [PATCH 28/56] Add basic `Map::query_map_point` test --- test/unit/map/query_map_point.cpp | 82 +++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 test/unit/map/query_map_point.cpp diff --git a/test/unit/map/query_map_point.cpp b/test/unit/map/query_map_point.cpp new file mode 100644 index 000000000..982f3ac48 --- /dev/null +++ b/test/unit/map/query_map_point.cpp @@ -0,0 +1,82 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2021 Artem Pavlenko + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ + +#include "catch.hpp" + +#include +#include +#include +#include + +TEST_CASE("Query map point") { + +SECTION("Polygons") { + + mapnik::Map map(882,780); + mapnik::load_map(map, "./test/data/good_maps/wgs842merc_reprojection.xml"); + map.zoom_all(); + + { + auto featureset = map.query_map_point(0u, 351, 94); + while (auto feature = featureset->next()) + { + auto val = feature->get("ADMIN"); + CHECK(val.to_string() == "Greenland"); + } + + } + { + auto featureset = map.query_map_point(0u, 402, 182); + while (auto feature = featureset->next()) + { + auto val = feature->get("ADMIN"); + CHECK(val.to_string() == "Iceland"); + } + } + { + auto featureset = map.query_map_point(0u, 339, 687); + while (auto feature = featureset->next()) + { + auto val = feature->get("ADMIN"); + CHECK(val.to_string() == "Antarctica"); + } + + } + + { + auto featureset = map.query_map_point(0u, 35, 141); + while (auto feature = featureset->next()) + { + auto val = feature->get("ADMIN"); + CHECK(val.to_string() == "Russia"); + } + } + { + auto featureset = map.query_map_point(0u, 737, 297); + while (auto feature = featureset->next()) + { + auto val = feature->get("ADMIN"); + CHECK(val.to_string() == "Japan"); + } + } +} +} From 0714e615455fad96f74e20210f74fc35f09bd5e7 Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Tue, 9 Mar 2021 16:29:23 +0000 Subject: [PATCH 29/56] Refactor "Query map point" unit test --- test/unit/map/query_map_point.cpp | 66 +++++++++++-------------------- 1 file changed, 22 insertions(+), 44 deletions(-) diff --git a/test/unit/map/query_map_point.cpp b/test/unit/map/query_map_point.cpp index 982f3ac48..6279db258 100644 --- a/test/unit/map/query_map_point.cpp +++ b/test/unit/map/query_map_point.cpp @@ -27,6 +27,23 @@ #include #include + +namespace { + +bool test_query_point(mapnik::Map const& map, + double x, double y, + std::string const& name, std::string const& expected_val) +{ + auto featureset = map.query_map_point(0u, x, y); + while (auto feature = featureset->next()) + { + auto val = feature->get(name); + return (val.to_string() == expected_val); + } + return false; +} +} + TEST_CASE("Query map point") { SECTION("Polygons") { @@ -34,49 +51,10 @@ SECTION("Polygons") { mapnik::Map map(882,780); mapnik::load_map(map, "./test/data/good_maps/wgs842merc_reprojection.xml"); map.zoom_all(); - - { - auto featureset = map.query_map_point(0u, 351, 94); - while (auto feature = featureset->next()) - { - auto val = feature->get("ADMIN"); - CHECK(val.to_string() == "Greenland"); - } - - } - { - auto featureset = map.query_map_point(0u, 402, 182); - while (auto feature = featureset->next()) - { - auto val = feature->get("ADMIN"); - CHECK(val.to_string() == "Iceland"); - } - } - { - auto featureset = map.query_map_point(0u, 339, 687); - while (auto feature = featureset->next()) - { - auto val = feature->get("ADMIN"); - CHECK(val.to_string() == "Antarctica"); - } - - } - - { - auto featureset = map.query_map_point(0u, 35, 141); - while (auto feature = featureset->next()) - { - auto val = feature->get("ADMIN"); - CHECK(val.to_string() == "Russia"); - } - } - { - auto featureset = map.query_map_point(0u, 737, 297); - while (auto feature = featureset->next()) - { - auto val = feature->get("ADMIN"); - CHECK(val.to_string() == "Japan"); - } - } + CHECK(test_query_point(map, 351, 94, "ADMIN", "Greenland")); + CHECK(test_query_point(map, 402, 182,"ADMIN", "Iceland")); + CHECK(test_query_point(map, 339, 687,"ADMIN", "Antarctica")); + CHECK(test_query_point(map, 35, 141, "ADMIN", "Russia")); + CHECK(test_query_point(map, 737, 297,"ADMIN", "Japan")); } } From ec880947e04458a35c4fe85e724c206e588bcef0 Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Thu, 11 Mar 2021 15:04:53 +0000 Subject: [PATCH 30/56] fix merge artifacts --- test/unit/projection/proj_transform.cpp | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/test/unit/projection/proj_transform.cpp b/test/unit/projection/proj_transform.cpp index 79484e328..4f91b4adb 100644 --- a/test/unit/projection/proj_transform.cpp +++ b/test/unit/projection/proj_transform.cpp @@ -207,14 +207,10 @@ SECTION("Test proj antimeridian bbox") SECTION("proj_transform of coordinate arrays with stride > 1") { -<<<<<<< HEAD + mapnik::projection const proj_4326("epsg:4326"); mapnik::projection const proj_3857("epsg:3857"); mapnik::projection const proj_2193("epsg:2193"); -======= - mapnik::projection const proj_4326("+init=epsg:4326"); - mapnik::projection const proj_3857("+init=epsg:3857"); ->>>>>>> master SECTION("lonlat <-> Web Mercator") { @@ -249,12 +245,9 @@ SECTION("proj_transform of coordinate arrays with stride > 1") #ifdef MAPNIK_USE_PROJ4 SECTION("lonlat <-> New Zealand Transverse Mercator 2000") { -<<<<<<< HEAD - // cs2cs -Ef %.10f epsg:4326 +to epsg:2193 <>>>>>> master + // cs2cs -Ef %.10f epsg:4326 +to epsg:2193 < Date: Thu, 11 Mar 2021 15:16:31 +0000 Subject: [PATCH 31/56] Fix mason version --- bootstrap.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bootstrap.sh b/bootstrap.sh index 0ed84fdf5..8f1594f75 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -8,7 +8,7 @@ todo - shrink icu data ' -MASON_VERSION="edb620c7" +MASON_VERSION="485514d8" function setup_mason() { if [[ ! -d ./.mason ]]; then From f4322041807812a7ff12af13a5c10212ea21c22a Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Fri, 12 Mar 2021 13:14:31 +0000 Subject: [PATCH 32/56] Update test/data-visual --- test/data-visual | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/data-visual b/test/data-visual index 5afb277ff..84e669d4d 160000 --- a/test/data-visual +++ b/test/data-visual @@ -1 +1 @@ -Subproject commit 5afb277fface40d463d5fb0f41d8e72bb67501b6 +Subproject commit 84e669d4d8dd94d17cd39446876878c908f46eb7 From c71bcdc92d772d8e223f84b99f0da4846a746ec7 Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Fri, 12 Mar 2021 14:39:14 +0000 Subject: [PATCH 33/56] update visual tests affected by switching to well_known_srs --- test/data-visual | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/data-visual b/test/data-visual index 84e669d4d..e6b726350 160000 --- a/test/data-visual +++ b/test/data-visual @@ -1 +1 @@ -Subproject commit 84e669d4d8dd94d17cd39446876878c908f46eb7 +Subproject commit e6b7263502bae5913595180d4b42520edf3582bc From baad6b3819dce3d5cad5ecef2c2e6b13e6e30393 Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Fri, 12 Mar 2021 16:24:11 +0000 Subject: [PATCH 34/56] Update data and data-visual to use `epsg:XXXX` syntax to define SRS (old proj4 syntax is not recommended in proj >=6) --- test/data | 2 +- test/data-visual | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/data b/test/data index 004b17069..dd0c41c3f 160000 --- a/test/data +++ b/test/data @@ -1 +1 @@ -Subproject commit 004b170698a826de599e42678a27868e861e595e +Subproject commit dd0c41c3f9f5dc98291a727af00bb42734d2a8c0 diff --git a/test/data-visual b/test/data-visual index e6b726350..2aaf138d6 160000 --- a/test/data-visual +++ b/test/data-visual @@ -1 +1 @@ -Subproject commit e6b7263502bae5913595180d4b42520edf3582bc +Subproject commit 2aaf138d6db922ba3f0e00b8157157d259df574c From 609980455948ba42300c23a15d1841248d981703 Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Mon, 15 Mar 2021 18:06:26 +0000 Subject: [PATCH 35/56] Use POSIX definition of `pi` constant (M_PI) + define `tau` in terms of `pi` + better namings + remove deprecated proj4 style definitions --- include/mapnik/layer.hpp | 2 +- include/mapnik/map.hpp | 6 ++--- include/mapnik/util/math.hpp | 8 +++--- include/mapnik/well_known_srs.hpp | 6 ++--- src/map.cpp | 2 +- src/proj_transform.cpp | 4 +-- src/projection.cpp | 9 ------- src/well_known_srs.cpp | 44 ++++++++++--------------------- 8 files changed, 28 insertions(+), 53 deletions(-) diff --git a/include/mapnik/layer.hpp b/include/mapnik/layer.hpp index 96a161816..dd28057d5 100644 --- a/include/mapnik/layer.hpp +++ b/include/mapnik/layer.hpp @@ -50,7 +50,7 @@ class MAPNIK_DECL layer { public: layer(std::string const& name, - std::string const& srs=MAPNIK_LONGLAT_PROJ); + std::string const& srs = MAPNIK_GEOGRAPHIC_PROJ); // copy layer(layer const& l); // move diff --git a/include/mapnik/map.hpp b/include/mapnik/map.hpp index face79aca..ba1b08b7b 100644 --- a/include/mapnik/map.hpp +++ b/include/mapnik/map.hpp @@ -110,8 +110,8 @@ public: }; private: - static const unsigned MIN_MAPSIZE=16; - static const unsigned MAX_MAPSIZE=MIN_MAPSIZE<<10; + static const unsigned MIN_MAPSIZE = 16; + static const unsigned MAX_MAPSIZE = MIN_MAPSIZE << 10; unsigned width_; unsigned height_; std::string srs_; @@ -153,7 +153,7 @@ public: * @param height Initial map height. * @param srs Initial map projection. */ - Map(int width, int height, std::string const& srs = MAPNIK_LONGLAT_PROJ); + Map(int width, int height, std::string const& srs = MAPNIK_GEOGRAPHIC_PROJ); /*! \brief Copy Constructor. * diff --git a/include/mapnik/util/math.hpp b/include/mapnik/util/math.hpp index d2f1f6ef7..7e163ba57 100644 --- a/include/mapnik/util/math.hpp +++ b/include/mapnik/util/math.hpp @@ -27,8 +27,8 @@ namespace mapnik { namespace util { -constexpr double pi = 3.1415926535897932384626433832795; -constexpr double tau = 6.283185307179586476925286766559; +constexpr double pi = 3.14159265358979323846; +constexpr double tau = 2.0 * pi; template constexpr T const& clamp(T const& v, T const& lo, T const& hi) @@ -38,12 +38,12 @@ constexpr T const& clamp(T const& v, T const& lo, T const& hi) constexpr double degrees(double rad) { - return rad * (360 / tau); + return rad * (180.0 / pi); } constexpr double radians(double deg) { - return deg * (tau / 360); + return deg * (pi / 180); } MAPNIK_DECL double normalize_angle(double angle); diff --git a/include/mapnik/well_known_srs.hpp b/include/mapnik/well_known_srs.hpp index a2154a8e5..29ad18c05 100644 --- a/include/mapnik/well_known_srs.hpp +++ b/include/mapnik/well_known_srs.hpp @@ -41,7 +41,7 @@ namespace mapnik { enum well_known_srs_enum : std::uint8_t { WGS_84, - G_MERC, + WEB_MERC, well_known_srs_enum_MAX }; @@ -53,8 +53,8 @@ constexpr double MERC_MAX_EXTENT = EARTH_RADIUS * util::pi; constexpr double MERC_MAX_LATITUDE = 85.0511287798065923778; // MERC_MAX_LATITUDE = degrees(2 * atan(exp(pi)) - pi / 2) -extern MAPNIK_DECL std::string const MAPNIK_LONGLAT_PROJ; -extern MAPNIK_DECL std::string const MAPNIK_GMERC_PROJ; +extern MAPNIK_DECL std::string const MAPNIK_GEOGRAPHIC_PROJ; +extern MAPNIK_DECL std::string const MAPNIK_WEBMERCATOR_PROJ; MAPNIK_DECL boost::optional is_known_geographic(std::string const& srs); MAPNIK_DECL boost::optional is_well_known_srs(std::string const& srs); diff --git a/src/map.cpp b/src/map.cpp index 4cad760fe..10e281863 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -66,7 +66,7 @@ IMPLEMENT_ENUM( aspect_fix_mode_e, aspect_fix_mode_strings ) Map::Map() : width_(400), height_(400), - srs_(MAPNIK_LONGLAT_PROJ), + srs_(MAPNIK_GEOGRAPHIC_PROJ), buffer_size_(0), background_image_comp_op_(src_over), background_image_opacity_(1.0), diff --git a/src/proj_transform.cpp b/src/proj_transform.cpp index 5f391bb0d..4571cdad9 100644 --- a/src/proj_transform.cpp +++ b/src/proj_transform.cpp @@ -111,12 +111,12 @@ proj_transform::proj_transform(projection const& source, bool known_trans = false; if (src_k && dest_k) { - if (*src_k == WGS_84 && *dest_k == G_MERC) + if (*src_k == WGS_84 && *dest_k == WEB_MERC) { wgs84_to_merc_ = true; known_trans = true; } - else if (*src_k == G_MERC && *dest_k == WGS_84) + else if (*src_k == WEB_MERC && *dest_k == WGS_84) { merc_to_wgs84_ = true; known_trans = true; diff --git a/src/projection.cpp b/src/projection.cpp index 2da83a9e4..302ff418e 100644 --- a/src/projection.cpp +++ b/src/projection.cpp @@ -94,7 +94,6 @@ void projection::init_proj() const if (!proj_) { proj_ctx_ = proj_context_create(); - //proj_ = proj_create_crs_to_crs(proj_ctx_, "epsg:4326", params_.c_str(), nullptr); proj_ = proj_create(proj_ctx_, params_.c_str()); if (!proj_ || !proj_ctx_) { @@ -108,18 +107,10 @@ void projection::init_proj() const } throw proj_init_error(params_); } - // determine the type of CRS - //PJ* crs = proj_create(proj_ctx_, params_.c_str()); - //if (crs) - //if (proj_) - //{ - //PJ_TYPE type = proj_get_type(crs); PJ_TYPE type = proj_get_type(proj_); is_geographic_ = (type == PJ_TYPE_GEOGRAPHIC_2D_CRS || type == PJ_TYPE_GEOGRAPHIC_3D_CRS) ? true : false; - //} - //proj_destroy(crs); } #endif } diff --git a/src/well_known_srs.cpp b/src/well_known_srs.cpp index 8a09bbd78..25462bd67 100644 --- a/src/well_known_srs.cpp +++ b/src/well_known_srs.cpp @@ -36,28 +36,27 @@ MAPNIK_DISABLE_WARNING_POP namespace mapnik { -extern std::string const MAPNIK_LONGLAT_PROJ = - "+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs"; +extern std::string const MAPNIK_GEOGRAPHIC_PROJ = + "epsg:4326"; //wgs84 -extern std::string const MAPNIK_GMERC_PROJ = - "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0" - " +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over"; +extern std::string const MAPNIK_WEBMERCATOR_PROJ = + "epsg:3857"; // webmercator static const char * well_known_srs_strings[] = { - "mapnik-longlat", - "mapnik-gmerc", + MAPNIK_GEOGRAPHIC_PROJ.c_str(), + MAPNIK_WEBMERCATOR_PROJ.c_str(), "" }; boost::optional is_well_known_srs(std::string const& srs) { - if (srs == "epsg:4326" || srs == MAPNIK_LONGLAT_PROJ) + if (srs == MAPNIK_GEOGRAPHIC_PROJ) { return boost::optional(mapnik::WGS_84); } - else if (srs == "epsg:3857" || srs == MAPNIK_GMERC_PROJ) + else if (srs == MAPNIK_WEBMERCATOR_PROJ) { - return boost::optional(mapnik::G_MERC); + return boost::optional(mapnik::WEB_MERC); } return boost::optional(); } @@ -65,28 +64,13 @@ boost::optional is_well_known_srs(std::string const& srs) boost::optional is_known_geographic(std::string const& srs) { std::string trimmed = util::trim_copy(srs); - if (trimmed == "epsg:3857") - { - return boost::optional(false); - } - else if (trimmed == "epsg:4326") + if (trimmed == MAPNIK_GEOGRAPHIC_PROJ) { return boost::optional(true); } - else if (srs.find("+proj=") != std::string::npos) + else if (trimmed == MAPNIK_WEBMERCATOR_PROJ) { - if ((srs.find("+proj=longlat") != std::string::npos) || - (srs.find("+proj=latlong") != std::string::npos) || - (srs.find("+proj=lonlat") != std::string::npos) || - (srs.find("+proj=latlon") != std::string::npos) - ) - { - return boost::optional(true); - } - else - { - return boost::optional(false); - } + return boost::optional(true); } return boost::optional(); } @@ -99,7 +83,7 @@ bool lonlat2merc(double & x, double & y) auto dx = clamp(x, -180.0, 180.0); auto dy = clamp(y, -MERC_MAX_LATITUDE, MERC_MAX_LATITUDE); x = EARTH_RADIUS * radians(dx); - y = EARTH_RADIUS * std::log(std::tan(radians(90 + dy) / 2)); + y = EARTH_RADIUS * std::log(std::tan(radians(90.0 + dy) / 2.0)); return true; } @@ -127,7 +111,7 @@ bool merc2lonlat(double & x, double & y) auto rx = clamp(x / EARTH_RADIUS, -pi, pi); auto ry = clamp(y / EARTH_RADIUS, -pi, pi); x = degrees(rx); - y = degrees(2 * std::atan(std::exp(ry)) - pi / 2); + y = degrees(2.0 * std::atan(std::exp(ry)) - pi / 2.0); return true; } From 387d54fe9df73b964076750cc7ff69af6f4197b9 Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Mon, 15 Mar 2021 18:09:21 +0000 Subject: [PATCH 36/56] Defer projection init --- demo/viewer/mainwindow.cpp | 8 +++--- demo/viewer/mapwidget.cpp | 52 +++++++++++++++++++------------------- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/demo/viewer/mainwindow.cpp b/demo/viewer/mainwindow.cpp index cdfd43471..338180f66 100644 --- a/demo/viewer/mainwindow.cpp +++ b/demo/viewer/mainwindow.cpp @@ -421,11 +421,11 @@ void MainWindow::set_default_extent(double x0,double y0, double x1, double y1) if (map_ptr) { mapnik::projection prj(map_ptr->srs()); - prj.forward(x0,y0); - prj.forward(x1,y1); - default_extent_=mapnik::box2d(x0,y0,x1,y1); + prj.forward(x0, y0); + prj.forward(x1, y1); + default_extent_=mapnik::box2d(x0, y0, x1, y1); mapWidget_->zoomToBox(default_extent_); - std::cout << "SET DEFAULT EXT\n"; + std::cout << "SET DEFAULT EXT:" << default_extent_ << std::endl; } } catch (...) {} diff --git a/demo/viewer/mapwidget.cpp b/demo/viewer/mapwidget.cpp index 39b682e8e..b1ea939b0 100644 --- a/demo/viewer/mapwidget.cpp +++ b/demo/viewer/mapwidget.cpp @@ -156,7 +156,7 @@ void MapWidget::mousePressEvent(QMouseEvent* e) { QVector > info; - projection map_proj(map_->srs()); // map projection + projection map_proj(map_->srs(), true); // map projection double scale_denom = scale_denominator(map_->scale(),map_proj.is_geographic()); view_transform t(map_->width(),map_->height(),map_->get_current_extent()); @@ -170,7 +170,7 @@ void MapWidget::mousePressEvent(QMouseEvent* e) double x = e->x(); double y = e->y(); std::cout << "query at " << x << "," << y << "\n"; - projection layer_proj(layer.srs()); + projection layer_proj(layer.srs(), true); mapnik::proj_transform prj_trans(map_proj,layer_proj); //std::auto_ptr data(new mapnik::memory_datasource); mapnik::featureset_ptr fs = map_->query_map_point(index,x,y); @@ -586,43 +586,43 @@ void MapWidget::updateMap() try { - projection prj(map_->srs()); // map projection - box2d ext = map_->get_current_extent(); - double x0 = ext.minx(); - double y0 = ext.miny(); - double x1 = ext.maxx(); - double y1 = ext.maxy(); - double z = 0; - std::string dest_srs = {"epsg:4326"}; - mapnik::proj_transform proj_tr(map_->srs(), dest_srs); + projection prj(map_->srs(), true); // map projection + box2d ext = map_->get_current_extent(); + double x0 = ext.minx(); + double y0 = ext.miny(); + double x1 = ext.maxx(); + double y1 = ext.maxy(); + double z = 0; + std::string dest_srs = {"epsg:4326"}; + mapnik::proj_transform proj_tr(map_->srs(), dest_srs); - proj_tr.forward(x0, y0, z); - proj_tr.forward(x1, y1, z); - std::cout << "MAP SIZE:" << map_->width() << "," << map_->height() << std::endl; - std::cout << "BBOX (WGS84): " << x0 << "," << y0 << "," << x1 << "," << y1 << "\n"; - update(); - // emit signal to interested widgets - emit mapViewChanged(); - } - catch (...) - { - std::cerr << "Unknown exception caught!\n"; - } + proj_tr.forward(x0, y0, z); + proj_tr.forward(x1, y1, z); + std::cout << "MAP SIZE:" << map_->width() << "," << map_->height() << std::endl; + std::cout << "BBOX (WGS84): " << x0 << "," << y0 << "," << x1 << "," << y1 << "\n"; + update(); + // emit signal to interested widgets + emit mapViewChanged(); + } + catch (...) + { + std::cerr << "Unknown exception caught!\n"; + } } } std::shared_ptr MapWidget::getMap() { - return map_; + return map_; } void MapWidget::setMap(std::shared_ptr map) { - map_ = map; + map_ = map; } void MapWidget::layerSelected(int index) { - selectedLayer_ = index; + selectedLayer_ = index; } From 56d63073b4971892dd4b0896b00e730d2224905d Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Mon, 15 Mar 2021 18:10:09 +0000 Subject: [PATCH 37/56] Update test --- test/unit/core/exceptions_test.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/unit/core/exceptions_test.cpp b/test/unit/core/exceptions_test.cpp index 62d414ba6..2c6f14999 100644 --- a/test/unit/core/exceptions_test.cpp +++ b/test/unit/core/exceptions_test.cpp @@ -34,11 +34,11 @@ SECTION("handling") { // https://github.com/mapnik/mapnik/issues/2170 try { - mapnik::projection srs("+proj=longlat foo",true); + mapnik::projection srs("epsg:4326 foo",true); REQUIRE(srs.is_geographic()); REQUIRE(true); srs.init_proj(); - // oddly init_proj4 does not throw with old proj/ubuntu precise + // oddly init_proj does not throw with old proj/ubuntu precise //REQUIRE(false); } catch (...) { REQUIRE(true); From fbc6bec12a6e9edaf17f83177a5c4ffb15603968 Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Mon, 15 Mar 2021 18:18:24 +0000 Subject: [PATCH 38/56] Update visual data references --- test/data-visual | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/data-visual b/test/data-visual index 2aaf138d6..1f20cf257 160000 --- a/test/data-visual +++ b/test/data-visual @@ -1 +1 @@ -Subproject commit 2aaf138d6db922ba3f0e00b8157157d259df574c +Subproject commit 1f20cf257f35224d3c139a6015b1cf70814b0d24 From 199e609a7983f107ea0dc2da49055153fcdadf15 Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Tue, 16 Mar 2021 11:49:53 +0000 Subject: [PATCH 39/56] Fix compiler warning - use non-reference type 'boost::tuples::cons<...>' --- test/unit/geometry/geometry_equal.hpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/unit/geometry/geometry_equal.hpp b/test/unit/geometry/geometry_equal.hpp index c0819aed1..e7e7b13e5 100644 --- a/test/unit/geometry/geometry_equal.hpp +++ b/test/unit/geometry/geometry_equal.hpp @@ -128,7 +128,7 @@ struct geometry_equal_visitor REQUIRE(false); } - for(auto const& p : zip_crange(ls1, ls2)) + for (auto const p : zip_crange(ls1, ls2)) { REQUIRE(p.template get<0>().x == Approx(p.template get<1>().x)); REQUIRE(p.template get<0>().y == Approx(p.template get<1>().y)); @@ -143,7 +143,7 @@ struct geometry_equal_visitor REQUIRE(false); } - for (auto const& p : zip_crange(p1, p2)) + for (auto const p : zip_crange(p1, p2)) { (*this)(static_cast> const&>(p.template get<0>()), static_cast> const&>(p.template get<1>())); @@ -173,7 +173,7 @@ struct geometry_equal_visitor REQUIRE(false); } - for (auto const& ls : zip_crange(mls1, mls2)) + for (auto const ls : zip_crange(mls1, mls2)) { (*this)(ls.template get<0>(),ls.template get<1>()); } @@ -187,7 +187,7 @@ struct geometry_equal_visitor REQUIRE(false); } - for (auto const& poly : zip_crange(mpoly1, mpoly2)) + for (auto const poly : zip_crange(mpoly1, mpoly2)) { (*this)(poly.template get<0>(),poly.template get<1>()); } @@ -203,7 +203,7 @@ struct geometry_equal_visitor REQUIRE(false); } - for (auto const& g : zip_crange(c1, c2)) + for (auto const g : zip_crange(c1, c2)) { assert_g_equal(g.template get<0>(),g.template get<1>()); } @@ -217,7 +217,7 @@ struct geometry_equal_visitor REQUIRE(false); } - for (auto const& g : zip_crange(c1, c2)) + for (auto const g : zip_crange(c1, c2)) { assert_g_equal(g.template get<0>(),g.template get<1>()); } From 9c7e21fa47f084cce55342702aafb3f7f6c4e2a3 Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Thu, 18 Mar 2021 15:32:13 +0000 Subject: [PATCH 40/56] Re-use `proj_transform` as creating a new one is extremely expensive on libproj >= 6 (NOTE: `epsg:4326` and `epsg:3857` are special cases which use internal implementations and are not expensive to create on stack). --- benchmark/src/test_proj_transform1.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/benchmark/src/test_proj_transform1.cpp b/benchmark/src/test_proj_transform1.cpp index fd8cf6a7b..a87f22ea3 100644 --- a/benchmark/src/test_proj_transform1.cpp +++ b/benchmark/src/test_proj_transform1.cpp @@ -9,7 +9,7 @@ class test : public benchmark::test_case std::string dest_; mapnik::box2d from_; mapnik::box2d to_; - bool defer_proj4_init_; + bool defer_proj_init_; public: test(mapnik::parameters const& params, std::string const& src, @@ -22,11 +22,11 @@ public: dest_(dest), from_(from), to_(to), - defer_proj4_init_(defer_proj) {} + defer_proj_init_(defer_proj) {} bool validate() const { - mapnik::projection src(src_,defer_proj4_init_); - mapnik::projection dest(dest_,defer_proj4_init_); + mapnik::projection src(src_,defer_proj_init_); + mapnik::projection dest(dest_,defer_proj_init_); mapnik::proj_transform tr(src,dest); mapnik::box2d bbox = from_; if (!tr.forward(bbox)) return false; @@ -38,15 +38,15 @@ public: } bool operator()() const { + mapnik::projection src(src_,defer_proj_init_); + mapnik::projection dest(dest_,defer_proj_init_); + mapnik::proj_transform tr(src,dest); for (std::size_t i=0;i box(j,k,j,k); if (!tr.forward(box)) throw std::runtime_error("could not transform coords"); } From ebbd544d756259018349ed96c07ae0e6e9a913d9 Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Thu, 18 Mar 2021 16:05:09 +0000 Subject: [PATCH 41/56] Add text to indicate if proj_transform is internal or external (libproj) --- benchmark/src/test_proj_transform1.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/benchmark/src/test_proj_transform1.cpp b/benchmark/src/test_proj_transform1.cpp index a87f22ea3..be85685d8 100644 --- a/benchmark/src/test_proj_transform1.cpp +++ b/benchmark/src/test_proj_transform1.cpp @@ -66,9 +66,9 @@ int main(int argc, char** argv) std::string from_str2("+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs"); std::string to_str2("+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over"); return benchmark::sequencer(argc, argv) - .run("lonlat->merc epsg", from_str, to_str, from, to, true) - .run("lonlat->merc literal", from_str2, to_str2, from, to, true) - .run("merc->lonlat epsg", to_str, from_str, to, from, true) - .run("merc->lonlat literal", to_str2, from_str2, to, from, true) + .run("lonlat->merc epsg (internal)", from_str, to_str, from, to, true) + .run("lonlat->merc literal (libproj)", from_str2, to_str2, from, to, true) + .run("merc->lonlat epsg (internal)", to_str, from_str, to, from, true) + .run("merc->lonlat literal (libproj)", to_str2, from_str2, to, from, true) .done(); } From 6b9b42f314575c15acc564b013b888b0b3672139 Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Thu, 18 Mar 2021 16:05:58 +0000 Subject: [PATCH 42/56] Declare `proj_cache_` `thread_local static` to allow mapnik::Map to be thread-safe under certain conditions + refactor (benchmark/src/test_rendering_shared_map.cpp) --- include/mapnik/map.hpp | 3 +- src/map.cpp | 62 +++++++++++++++++++----------------------- 2 files changed, 30 insertions(+), 35 deletions(-) diff --git a/include/mapnik/map.hpp b/include/mapnik/map.hpp index ba1b08b7b..af60147ec 100644 --- a/include/mapnik/map.hpp +++ b/include/mapnik/map.hpp @@ -131,7 +131,7 @@ private: boost::optional font_directory_; freetype_engine::font_file_mapping_type font_file_mapping_; freetype_engine::font_memory_cache_type font_memory_cache_; - mutable proj_cache_type proj_cache_; + thread_local static proj_cache_type proj_cache_; public: using const_style_iterator = std::map::const_iterator; @@ -534,6 +534,7 @@ public: private: friend void swap(Map & rhs, Map & lhs); void fixAspectRatio(); + void init_proj_transform(std::string const& source, std::string const& dest); void init_proj_transforms(); }; diff --git a/src/map.cpp b/src/map.cpp index 10e281863..5763f1474 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -111,8 +111,7 @@ Map::Map(Map const& rhs) font_directory_(rhs.font_directory_), font_file_mapping_(rhs.font_file_mapping_), // on copy discard memory caches - font_memory_cache_(), - proj_cache_() + font_memory_cache_() { init_proj_transforms(); } @@ -137,11 +136,12 @@ Map::Map(Map && rhs) extra_params_(std::move(rhs.extra_params_)), font_directory_(std::move(rhs.font_directory_)), font_file_mapping_(std::move(rhs.font_file_mapping_)), - font_memory_cache_(std::move(rhs.font_memory_cache_)), - proj_cache_(std::move(rhs.proj_cache_)) {} + font_memory_cache_(std::move(rhs.font_memory_cache_)) {} Map::~Map() {} +thread_local Map::proj_cache_type Map::proj_cache_ = proj_cache_type(); + Map& Map::operator=(Map rhs) { swap(*this, rhs); @@ -329,6 +329,7 @@ size_t Map::layer_count() const proj_transform * Map::get_proj_transform(std::string const& source, std::string const& dest) const { + compatible_key_type key = std::make_pair(source, dest); auto itr = proj_cache_.find(key, compatible_hash{}, compatible_predicate{}); if (itr == proj_cache_.end()) @@ -343,29 +344,13 @@ proj_transform * Map::get_proj_transform(std::string const& source, std::string void Map::add_layer(layer const& l) { - compatible_key_type key = std::make_pair(srs_, l.srs()); - auto itr = proj_cache_.find(key, compatible_hash{}, compatible_predicate{}); - if (itr == proj_cache_.end()) - { - mapnik::projection source(srs_, true); - mapnik::projection dest(l.srs(), true); - proj_cache_.emplace(std::make_pair(srs_, l.srs()), - std::make_unique(source, dest)); - } + init_proj_transform(srs_, l.srs()); layers_.emplace_back(l); } void Map::add_layer(layer && l) { - compatible_key_type key = std::make_pair(srs_, l.srs()); - auto itr = proj_cache_.find(key, compatible_hash{}, compatible_predicate{}); - if (itr == proj_cache_.end()) - { - mapnik::projection source(srs_, true); - mapnik::projection dest(l.srs(), true); - proj_cache_.emplace(make_pair(srs_, l.srs()), - std::make_unique(source, dest)); - } + init_proj_transform(srs_, l.srs()); layers_.push_back(std::move(l)); } @@ -458,8 +443,9 @@ std::string const& Map::srs() const void Map::set_srs(std::string const& _srs) { + if (srs_ != _srs) init_proj_transforms(); srs_ = _srs; - init_proj_transforms(); + } void Map::set_buffer_size(int _buffer_size) @@ -808,20 +794,28 @@ void Map::set_extra_parameters(parameters& params) extra_params_ = params; } -void Map::init_proj_transforms() + +void Map::init_proj_transform(std::string const& source, std::string const& dest) { - for (auto const& l : layers_) + compatible_key_type key = std::make_pair(source, dest); + auto itr = proj_cache_.find(key, compatible_hash{}, compatible_predicate{}); + if (itr == proj_cache_.end()) { - compatible_key_type key = std::make_pair(srs_, l.srs()); - auto itr = proj_cache_.find(key, compatible_hash{}, compatible_predicate{}); - if (itr == proj_cache_.end()) - { - mapnik::projection source(srs_, true); - mapnik::projection dest(l.srs(), true); - proj_cache_.emplace(std::make_pair(srs_, l.srs()), - std::make_unique(source, dest)); - } + mapnik::projection p0(source, true); + mapnik::projection p1(dest, true); + proj_cache_.emplace(std::make_pair(source, dest), + std::make_unique(p0, p1)); } } +void Map::init_proj_transforms() +{ + std::for_each(layers_.begin(), + layers_.end(), + [this] (auto const& l) + { + init_proj_transform(srs_, l.srs()); + }); +} + } From 6af16764aab9a3c8eb70ea6b204a97c7402cbc5c Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Fri, 19 Mar 2021 13:22:16 +0000 Subject: [PATCH 43/56] proj - add version check based on major*10000+minor*100+patch format --- include/mapnik/projection.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/mapnik/projection.hpp b/include/mapnik/projection.hpp index 4d7be1708..ffca994d3 100644 --- a/include/mapnik/projection.hpp +++ b/include/mapnik/projection.hpp @@ -39,7 +39,7 @@ MAPNIK_DISABLE_WARNING_POP // fwd decl -#if PROJ_VERSION_MAJOR >= 8 +#if PROJ_VERSION >= 80000 struct pj_ctx; using PJ_CONTEXT = struct pj_ctx; #else From 9df7afd0a3bbe6aa98c413e7c8b59ffbfb8abe03 Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Fri, 19 Mar 2021 13:24:14 +0000 Subject: [PATCH 44/56] Add 'proj_version' check + define PROJ_MIN_VERSION as 7.2.0 --- SConstruct | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/SConstruct b/SConstruct index 7297324ba..9d3fe10d7 100644 --- a/SConstruct +++ b/SConstruct @@ -67,6 +67,7 @@ BOOST_SEARCH_PREFIXES = ['/usr/local','/opt/local','/sw','/usr',] BOOST_MIN_VERSION = '1.61' #CAIRO_MIN_VERSION = '1.8.0' PROJ_MIN_VERSION = (7, 2, 0) +PROJ_MIN_VERSION_STRING = "%s.%s.%s" % PROJ_MIN_VERSION HARFBUZZ_MIN_VERSION = (0, 9, 34) HARFBUZZ_MIN_VERSION_STRING = "%s.%s.%s" % HARFBUZZ_MIN_VERSION @@ -80,6 +81,7 @@ pretty_dep_names = { 'ogr':'OGR-enabled GDAL C++ Library | configured using gdal-config program | try setting GDAL_CONFIG SCons option | more info: https://github.com/mapnik/mapnik/wiki/OGR', 'cairo':'Cairo C library | configured using pkg-config | try setting PKG_CONFIG_PATH SCons option', 'proj':'Proj C Projections library | configure with PROJ_LIBS & PROJ_INCLUDES | more info: http://trac.osgeo.org/proj/', + 'proj-min-version':'libproj >=%s required' % PROJ_MIN_VERSION_STRING, 'pg':'Postgres C Library required for PostGIS plugin | configure with pg_config program or configure with PG_LIBS & PG_INCLUDES | more info: https://github.com/mapnik/mapnik/wiki/PostGIS', 'sqlite3':'SQLite3 C Library | configure with SQLITE_LIBS & SQLITE_INCLUDES | more info: https://github.com/mapnik/mapnik/wiki/SQLite', 'jpeg':'JPEG C library | configure with JPEG_LIBS & JPEG_INCLUDES', @@ -918,6 +920,34 @@ def CheckGdalData(context, silent=False): context.Result('Failed to detect (mapnik-config will have null value)') return value +def proj_version(context): + context.Message('Checking for Proj version >=%s...' % PROJ_MIN_VERSION_STRING) + ret, out = context.TryRun(""" +#include +#include +#define PROJ_VERSION_ATLEAST(major,minor,micro) \ + ((major)*10000+(minor)*100+(micro) <= \ + PROJ_VERSION_MAJOR*10000+PROJ_VERSION_MINOR*100+PROJ_VERSION_PATCH) +int main() +{ + std::cout << PROJ_VERSION_ATLEAST(%s, %s, %s) << ";" + << PROJ_VERSION_MAJOR << "." + << PROJ_VERSION_MINOR << "." + << PROJ_VERSION_PATCH; + return 0; +} +""" % PROJ_MIN_VERSION,'.cpp') + if not ret: + context.Result('error (could not get version from proj.h)') + else: + ok_str, found_version_str = out.strip().split(';', 1) + major,minor,patch = found_version_str.split('.') + ret = int(ok_str), int(major)*10000+int(minor)*100+int(patch) + if ret: + context.Result('yes (found Proj %s)' % found_version_str) + else: + context.Result('no (found Proj %s)' % found_version_str) + return ret def CheckProjData(context, silent=False): @@ -1234,6 +1264,7 @@ conf_tests = { 'prioritize_paths' : prioritize_paths, 'FindBoost' : FindBoost, 'CheckBoost' : CheckBoost, 'CheckIcuData' : CheckIcuData, + 'proj_version' : proj_version, 'CheckProjData' : CheckProjData, 'CheckGdalData' : CheckGdalData, 'CheckCairoHasFreetype' : CheckCairoHasFreetype, @@ -1686,6 +1717,13 @@ if not preconfigured: else: color_print(4, 'Could not find optional header or shared library for %s' % libname) env['SKIPPED_DEPS'].append(libname) + elif libname == 'proj': + result, version = conf.proj_version() + if not result: + env['SKIPPED_DEPS'].append('proj-min-version') + else: + env.Append(CPPDEFINES = define) + env.Append(CPPDEFINES = "-DPROJ_VERSION=%d" % version) else: env.Append(CPPDEFINES = define) else: From 5afe22030f7a79773595e7f2792562133bd74f3e Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Fri, 19 Mar 2021 16:37:21 +0000 Subject: [PATCH 45/56] Fix `proj_version` on macOS --- SConstruct | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/SConstruct b/SConstruct index 9d3fe10d7..e8fe2d84e 100644 --- a/SConstruct +++ b/SConstruct @@ -923,20 +923,17 @@ def CheckGdalData(context, silent=False): def proj_version(context): context.Message('Checking for Proj version >=%s...' % PROJ_MIN_VERSION_STRING) ret, out = context.TryRun(""" -#include -#include +#include "proj.h" +#include #define PROJ_VERSION_ATLEAST(major,minor,micro) \ ((major)*10000+(minor)*100+(micro) <= \ PROJ_VERSION_MAJOR*10000+PROJ_VERSION_MINOR*100+PROJ_VERSION_PATCH) int main() { - std::cout << PROJ_VERSION_ATLEAST(%s, %s, %s) << ";" - << PROJ_VERSION_MAJOR << "." - << PROJ_VERSION_MINOR << "." - << PROJ_VERSION_PATCH; + printf("%d;%d.%d.%d", PROJ_VERSION_ATLEAST{min-version}, PROJ_VERSION_MAJOR, PROJ_VERSION_MINOR, PROJ_VERSION_PATCH); return 0; } -""" % PROJ_MIN_VERSION,'.cpp') +""".replace("{min-version}", str(PROJ_MIN_VERSION)),'.c') if not ret: context.Result('error (could not get version from proj.h)') else: From 602948f783973d71f97a5f2826849166ca824ee6 Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Mon, 22 Mar 2021 15:43:34 +0000 Subject: [PATCH 46/56] Upgrade mason pkgs: * libgdal 2.2.3 * libpng 1.6.32 * jpeg_turbo 1.5.2 * libtiff 4.0.8 --- bootstrap.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bootstrap.sh b/bootstrap.sh index 8f1594f75..2893aecb9 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -49,9 +49,9 @@ BOOST_VERSION="1.75.0" function install_mason_deps() { install ccache 3.3.1 install zlib 1.2.8 - install jpeg_turbo 1.5.1 libjpeg - install libpng 1.6.28 libpng - install libtiff 4.0.7 libtiff + install jpeg_turbo 1.5.2 libjpeg + install libpng 1.6.32 libpng + install libtiff 4.0.8 libtiff install libpq 9.6.2 install sqlite 3.34.0 libsqlite3 install icu ${ICU_VERSION} @@ -59,7 +59,7 @@ function install_mason_deps() { install pixman 0.34.0 libpixman-1 install cairo 1.14.8 libcairo install webp 0.6.0 libwebp - install libgdal 2.1.3 libgdal + install libgdal 2.2.3 libgdal install boost ${BOOST_VERSION} install boost_libsystem ${BOOST_VERSION} install boost_libfilesystem ${BOOST_VERSION} From 59aa5087fc3ec5e89492077ca2e5b800e2f412db Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Mon, 22 Mar 2021 16:03:08 +0000 Subject: [PATCH 47/56] upgrade libpq to 9.6.5 --- bootstrap.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bootstrap.sh b/bootstrap.sh index 2893aecb9..7480c8202 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -52,7 +52,7 @@ function install_mason_deps() { install jpeg_turbo 1.5.2 libjpeg install libpng 1.6.32 libpng install libtiff 4.0.8 libtiff - install libpq 9.6.2 + install libpq 9.6.5 install sqlite 3.34.0 libsqlite3 install icu ${ICU_VERSION} install proj 7.2.1 libproj From 3a302667a1caf6831b6dc96c14e0729debbc1286 Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Wed, 24 Mar 2021 09:42:52 +0000 Subject: [PATCH 48/56] Fix return value (MAPNIK_WEBMERCATOR_PROJ is not geographic SRS) --- src/well_known_srs.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/well_known_srs.cpp b/src/well_known_srs.cpp index 25462bd67..96086ee43 100644 --- a/src/well_known_srs.cpp +++ b/src/well_known_srs.cpp @@ -70,7 +70,7 @@ boost::optional is_known_geographic(std::string const& srs) } else if (trimmed == MAPNIK_WEBMERCATOR_PROJ) { - return boost::optional(true); + return boost::optional(false); } return boost::optional(); } From b24fc9a8da457ab3cca3034e0415eaff3ef15409 Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Wed, 24 Mar 2021 11:21:50 +0000 Subject: [PATCH 49/56] Remove proj4 related stuff --- test/cleanup.hpp | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/test/cleanup.hpp b/test/cleanup.hpp index f9f993047..a8b8457ba 100644 --- a/test/cleanup.hpp +++ b/test/cleanup.hpp @@ -16,9 +16,6 @@ MAPNIK_DISABLE_WARNING_PUSH #endif #include -#ifdef MAPNIK_USE_PROJ4 -#include -#endif MAPNIK_DISABLE_WARNING_POP @@ -47,15 +44,6 @@ inline void run_cleanup() // http://icu-project.org/apiref/icu4c/uclean_8h.html#a93f27d0ddc7c196a1da864763f2d8920 u_cleanup(); - -#ifdef MAPNIK_USE_PROJ4 - // http://trac.osgeo.org/proj/ticket/149 - #if PJ_VERSION >= 480 - pj_clear_initcache(); - #endif - // https://trac.osgeo.org/proj/wiki/ProjAPI#EnvironmentFunctions - pj_deallocate_grids(); -#endif } } From f82ed08d6a84d822e13d4bb44d0b971ba8271c67 Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Wed, 24 Mar 2021 11:22:32 +0000 Subject: [PATCH 50/56] Replace remaining MAPNIK_USE_PROJ4 -> MAPNIK_USE_PROJ --- test/unit/geometry/geometry_reprojection.cpp | 4 ++-- test/unit/vertex_adapter/transform_path_adapter.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/unit/geometry/geometry_reprojection.cpp b/test/unit/geometry/geometry_reprojection.cpp index 4e236a092..589a8c361 100644 --- a/test/unit/geometry/geometry_reprojection.cpp +++ b/test/unit/geometry/geometry_reprojection.cpp @@ -1185,7 +1185,7 @@ SECTION("test_projection_4326_3857 - Geometry Collection Variant Object") { } } // END SECTION -#ifdef MAPNIK_USE_PROJ4 +#ifdef MAPNIK_USE_PROJ SECTION("test_projection_4269_3857 - Line_String Geometry Object") { using namespace mapnik::geometry; mapnik::projection source("epsg:4269"); @@ -1305,6 +1305,6 @@ SECTION("test_projection_4269_3857 - Point Geometry Object") { } } // End Section -#endif // MAPNIK_USE_PROJ4 +#endif // MAPNIK_USE_PROJ } // End Testcase diff --git a/test/unit/vertex_adapter/transform_path_adapter.cpp b/test/unit/vertex_adapter/transform_path_adapter.cpp index d9b8c50ba..a73762e4a 100644 --- a/test/unit/vertex_adapter/transform_path_adapter.cpp +++ b/test/unit/vertex_adapter/transform_path_adapter.cpp @@ -11,7 +11,7 @@ TEST_CASE("transform_path_adapter") { -#ifdef MAPNIK_USE_PROJ4 +#ifdef MAPNIK_USE_PROJ SECTION("polygon closing - epsg 2330") { mapnik::geometry::polygon g; g.emplace_back(); @@ -116,5 +116,5 @@ SECTION("polygon closing - epsg 32633") { CHECK( y == 0 ); } -#endif //MAPNIK_USE_PROJ4 +#endif //MAPNIK_USE_PROJ } From 2bb5bdb9fe9e4f38e52f664e6cfda4dae8d94551 Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Wed, 24 Mar 2021 11:23:27 +0000 Subject: [PATCH 51/56] Fix existing and add more transformation test + test for `is_geographic` (ref 3a302667a1caf6831b6dc96c14e0729debbc1286) --- test/unit/projection/proj_transform.cpp | 113 ++++++++++++------------ 1 file changed, 57 insertions(+), 56 deletions(-) diff --git a/test/unit/projection/proj_transform.cpp b/test/unit/projection/proj_transform.cpp index 4f91b4adb..d3f7707aa 100644 --- a/test/unit/projection/proj_transform.cpp +++ b/test/unit/projection/proj_transform.cpp @@ -3,12 +3,7 @@ #include #include #include - -#ifdef MAPNIK_USE_PROJ4 -// proj4 -#include -#endif - +#include TEST_CASE("projection transform") { @@ -17,6 +12,10 @@ SECTION("Test bounding box transforms - 4326 to 3857") { mapnik::projection proj_4326("epsg:4326"); mapnik::projection proj_3857("epsg:3857"); + + CHECK(proj_4326.is_geographic()); + CHECK(!proj_3857.is_geographic()); + mapnik::proj_transform prj_trans(proj_4326, proj_3857); double minx = -45.0; @@ -45,36 +44,15 @@ SECTION("Test bounding box transforms - 4326 to 3857") } -#if defined(MAPNIK_USE_PROJ4) && PJ_VERSION >= 480 -SECTION("test pj_transform failure behavior") +#if defined(MAPNIK_USE_PROJ) +SECTION("test proj_transform failure behavior") { mapnik::projection proj_4269("epsg:4269"); mapnik::projection proj_3857("epsg:3857"); mapnik::proj_transform prj_trans(proj_4269, proj_3857); mapnik::proj_transform prj_trans2(proj_3857, proj_4269); - auto proj_ctx0 = pj_ctx_alloc(); - REQUIRE( proj_ctx0 != nullptr ); - auto proj0 = pj_init_plus_ctx(proj_ctx0, proj_4269.params().c_str()); - REQUIRE( proj0 != nullptr ); - - auto proj_ctx1 = pj_ctx_alloc(); - REQUIRE( proj_ctx1 != nullptr ); - auto proj1 = pj_init_plus_ctx(proj_ctx1, proj_3857.params().c_str()); - REQUIRE( proj1 != nullptr ); - - // first test valid values directly against proj - double x = -180.0; - double y = -60.0; - x *= DEG_TO_RAD; - y *= DEG_TO_RAD; - CHECK( x == Approx(-3.1415926536) ); - CHECK( y == Approx(-1.0471975512) ); - CHECK( 0 == pj_transform(proj0, proj1, 1, 0, &x, &y, nullptr) ); - CHECK( x == Approx(-20037508.3427892439) ); - CHECK( y == Approx(-8399737.8896366451) ); - - // now test mapnik class + // test valid coordinate double x0 = -180.0; double y0 = -60.0; CHECK( prj_trans.forward(&x0,&y0,nullptr,1,1) ); @@ -86,38 +64,61 @@ SECTION("test pj_transform failure behavior") CHECK( x1 == Approx(-20037508.3427892439) ); CHECK( y1 == Approx(-8399737.8896366451) ); - // longitude value outside the value range for mercator - x = -181.0; - y = -91.0; - x *= DEG_TO_RAD; - y *= DEG_TO_RAD; - CHECK( x == Approx(-3.1590459461) ); - CHECK( y == Approx(-1.5882496193) ); - CHECK( 0 == pj_transform(proj0, proj1, 1, 0, &x, &y, nullptr) ); - CHECK( std::isinf(x) ); - CHECK( std::isinf(y) ); - - // now test mapnik class + // now test invalid coordinate double x2 = -181.0; double y2 = -91.0; - CHECK( false == prj_trans.forward(&x2,&y2,nullptr,1,1) ); + prj_trans.forward(&x2,&y2,nullptr,1,1); CHECK( std::isinf(x2) ); CHECK( std::isinf(y2) ); double x3 = -181.0; double y3 = -91.0; - CHECK( false == prj_trans2.backward(&x3,&y3,nullptr,1,1) ); + prj_trans2.backward(&x3,&y3,nullptr,1,1); CHECK( std::isinf(x3) ); CHECK( std::isinf(y3) ); +} - // cleanup - pj_ctx_free(proj_ctx0); - proj_ctx0 = nullptr; - pj_free(proj0); - proj0 = nullptr; - pj_ctx_free(proj_ctx1); - proj_ctx1 = nullptr; - pj_free(proj1); - proj1 = nullptr; +SECTION("test forward/backward transformations") +{ + //WGS 84 - World Geodetic System 1984, used in GPS + mapnik::projection proj_4236("epsg:4236"); + //OSGB 1936 / British National Grid -- United Kingdom Ordnance Survey + mapnik::projection proj_27700("epsg:27700"); + //WGS 84 / Equal Earth Greenwich + mapnik::projection proj_8857("epsg:8857"); + //European Terrestrial Reference System 1989 (ETRS89) + mapnik::projection proj_4937("epsg:4937"); + //"Webmercator" WGS 84 / Pseudo-Mercator -- Spherical Mercator, Google Maps, OpenStreetMap, Bing, ArcGIS, ESRI + mapnik::projection proj_3857("epsg:3857"); + + mapnik::proj_transform tr1(proj_4236, proj_27700); + mapnik::proj_transform tr2(proj_4236, proj_8857); + mapnik::proj_transform tr3(proj_4236, proj_4236); + mapnik::proj_transform tr4(proj_4236, proj_4937); + mapnik::proj_transform tr5(proj_4236, proj_3857); + std::initializer_list> transforms = { + tr1, tr2, tr3, tr4, tr5 + }; + + std::initializer_list> coords = { + {-4.0278869, 57.8796955}, // City of Dornoch + {-4.2488787, 55.8609825}, // City of Glasgow + {-1.4823897, 51.8726941}, // Charlbury + }; + + for (auto const& c : coords) + { + double x0, y0; + std::tie(x0, y0) = c; + for (mapnik::proj_transform const& tr : transforms) + { + double x1 = x0; + double y1 = y0; + tr.forward (&x1, &y1, nullptr, 1, 1); + tr.backward(&x1, &y1, nullptr, 1, 1); + CHECK (x0 == Approx(x1)); + CHECK (y0 == Approx(y1)); + } + } } // Github Issue https://github.com/mapnik/mapnik/issues/2648 @@ -242,11 +243,11 @@ SECTION("proj_transform of coordinate arrays with stride > 1") } } - #ifdef MAPNIK_USE_PROJ4 +#ifdef MAPNIK_USE_PROJ SECTION("lonlat <-> New Zealand Transverse Mercator 2000") { - mapnik::projection const proj_2193("+init=epsg:2193"); + mapnik::projection const proj_2193("epsg:2193"); // cs2cs -Ef %.10f epsg:4326 +to epsg:2193 < 1") CHECK(points[1].y == Approx(-39.283333)); } } - #endif // MAPNIK_USE_PROJ4 +#endif // MAPNIK_USE_PROJ } } From d756b534e31f5bca0a3e51d6d499afe62eab6bbb Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Wed, 24 Mar 2021 11:42:18 +0000 Subject: [PATCH 52/56] Fix names and add welsh reference for good measure --- test/unit/projection/proj_transform.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/unit/projection/proj_transform.cpp b/test/unit/projection/proj_transform.cpp index d3f7707aa..4bb8533eb 100644 --- a/test/unit/projection/proj_transform.cpp +++ b/test/unit/projection/proj_transform.cpp @@ -100,9 +100,10 @@ SECTION("test forward/backward transformations") }; std::initializer_list> coords = { - {-4.0278869, 57.8796955}, // City of Dornoch - {-4.2488787, 55.8609825}, // City of Glasgow - {-1.4823897, 51.8726941}, // Charlbury + {-4.0278869, 57.8796955}, // Dórnach, Highland + {-4.2488787, 55.8609825}, // Glaschú, Alba + {-1.4823897, 51.8726941}, // Charlbury, England + {-3.9732612, 51.7077400} // Felindre, Cymru }; for (auto const& c : coords) From c76f65a49a9de6056dcdf02d9ac4edef671284f3 Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Wed, 24 Mar 2021 11:44:56 +0000 Subject: [PATCH 53/56] Add missing header (linux) --- test/unit/projection/proj_transform.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/test/unit/projection/proj_transform.cpp b/test/unit/projection/proj_transform.cpp index 4bb8533eb..d7136efd8 100644 --- a/test/unit/projection/proj_transform.cpp +++ b/test/unit/projection/proj_transform.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include TEST_CASE("projection transform") From 503b9c5bbfe0c9e07247629d8a45a1c1e62c34ca Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Thu, 25 Mar 2021 12:16:15 +0000 Subject: [PATCH 54/56] Move proj_transform_cache declarations and implementaion into separate translation unit (ref VC++ C2492) --- .../mapnik/feature_style_processor_impl.hpp | 6 +- include/mapnik/map.hpp | 36 +-------- include/mapnik/proj_transform_cache.hpp | 76 +++++++++++++++++++ src/build.py | 1 + src/map.cpp | 43 +++-------- src/proj_transform_cache.cpp | 58 ++++++++++++++ .../unit/renderer/feature_style_processor.cpp | 1 - 7 files changed, 154 insertions(+), 67 deletions(-) create mode 100644 include/mapnik/proj_transform_cache.hpp create mode 100644 src/proj_transform_cache.cpp diff --git a/include/mapnik/feature_style_processor_impl.hpp b/include/mapnik/feature_style_processor_impl.hpp index d85c3235d..96c4da3d1 100644 --- a/include/mapnik/feature_style_processor_impl.hpp +++ b/include/mapnik/feature_style_processor_impl.hpp @@ -41,7 +41,7 @@ #include #include #include -#include +#include #include #include #include @@ -254,7 +254,7 @@ void feature_style_processor::prepare_layer(layer_rendering_material } processor_context_ptr current_ctx = ds->get_context(ctx_map); - proj_transform * proj_trans_ptr = m_.get_proj_transform(mat.proj0_.params(), mat.proj1_.params()); + proj_transform const* proj_trans_ptr = m_.get_proj_transform(mat.proj0_.params(), mat.proj1_.params()); box2d query_ext = extent; // unbuffered box2d buffered_query_ext(query_ext); // buffered @@ -493,7 +493,7 @@ void feature_style_processor::render_material(layer_rendering_materia layer const& lay = mat.lay_; std::vector const & rule_caches = mat.rule_caches_; - proj_transform * proj_trans_ptr = m_.get_proj_transform(mat.proj0_.params(), mat.proj1_.params()); + proj_transform const* proj_trans_ptr = m_.get_proj_transform(mat.proj0_.params(), mat.proj1_.params()); bool cache_features = lay.cache_features() && active_styles.size() > 1; datasource_ptr ds = lay.datasource(); diff --git a/include/mapnik/map.hpp b/include/mapnik/map.hpp index af60147ec..7b0c155c5 100644 --- a/include/mapnik/map.hpp +++ b/include/mapnik/map.hpp @@ -38,9 +38,6 @@ MAPNIK_DISABLE_WARNING_PUSH #include #include -#include -#include -#include MAPNIK_DISABLE_WARNING_POP // stl @@ -56,36 +53,11 @@ using featureset_ptr = std::shared_ptr; class feature_type_style; class view_transform; class layer; +struct proj_transform_cache; class MAPNIK_DECL Map : boost::equality_comparable { public: - using key_type = std::pair; - using compatible_key_type = std::pair; - - struct compatible_hash - { - template - std::size_t operator() (KeyType const& key) const - { - using hash_type = boost::hash; - std::size_t seed = hash_type{}(key.first); - seed ^= hash_type{}(key.second) + 0x9e3779b9 + (seed << 6) + (seed >> 2); - return seed; - } - }; - - struct compatible_predicate - { - bool operator()(compatible_key_type const& k1, - compatible_key_type const& k2) const - { - return k1 == k2; - } - }; - - using proj_cache_type = boost::unordered_map, compatible_hash>; - enum aspect_fix_mode { // grow the width or height of the specified geo bbox to fill the map size. default behaviour. @@ -131,9 +103,8 @@ private: boost::optional font_directory_; freetype_engine::font_file_mapping_type font_file_mapping_; freetype_engine::font_memory_cache_type font_memory_cache_; - thread_local static proj_cache_type proj_cache_; + std::unique_ptr proj_cache_ = {}; public: - using const_style_iterator = std::map::const_iterator; using style_iterator = std::map::iterator; using const_fontset_iterator = std::map::const_iterator; @@ -530,7 +501,8 @@ public: return font_memory_cache_; } - proj_transform * get_proj_transform(std::string const& source, std::string const& dest) const; + proj_transform const* get_proj_transform(std::string const& source, std::string const& dest) const; + private: friend void swap(Map & rhs, Map & lhs); void fixAspectRatio(); diff --git a/include/mapnik/proj_transform_cache.hpp b/include/mapnik/proj_transform_cache.hpp new file mode 100644 index 000000000..b080081aa --- /dev/null +++ b/include/mapnik/proj_transform_cache.hpp @@ -0,0 +1,76 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2021 Artem Pavlenko + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ + + +#ifndef MAPNIK_PROJ_TRANSFORM_CACHE_HPP +#define MAPNIK_PROJ_TRANSFORM_CACHE_HPP + +#include +#include + +MAPNIK_DISABLE_WARNING_PUSH +#include +#include +#include +#include +MAPNIK_DISABLE_WARNING_POP + +namespace mapnik { + +struct proj_transform_cache : util::noncopyable +{ + using key_type = std::pair; + using compatible_key_type = std::pair; + + struct compatible_hash + { + template + std::size_t operator() (KeyType const& key) const + { + using hash_type = boost::hash; + std::size_t seed = hash_type{}(key.first); + seed ^= hash_type{}(key.second) + 0x9e3779b9 + (seed << 6) + (seed >> 2); + return seed; + } + }; + + struct compatible_predicate + { + bool operator()(compatible_key_type const& k1, + compatible_key_type const& k2) const + { + return k1 == k2; + } + }; + + using cache_type = boost::unordered_map, compatible_hash>; + + proj_transform_cache() = default; + + thread_local static cache_type cache_; + void init(std::string const& source, std::string const& dest) const; + proj_transform const* get(std::string const& source, std::string const& dest) const; +}; + +} + +#endif // MAPNIK_PROJ_TRANSFORM_CACHE_HPP diff --git a/src/build.py b/src/build.py index 171fb941e..9227c1089 100644 --- a/src/build.py +++ b/src/build.py @@ -214,6 +214,7 @@ source = Split( twkb.cpp projection.cpp proj_transform.cpp + proj_transform_cache.cpp scale_denominator.cpp simplify.cpp parse_transform.cpp diff --git a/src/map.cpp b/src/map.cpp index 5763f1474..8d20225ca 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -31,7 +31,7 @@ #include #include #include -#include +#include #include #include #include @@ -63,8 +63,9 @@ static const char * aspect_fix_mode_strings[] = { IMPLEMENT_ENUM( aspect_fix_mode_e, aspect_fix_mode_strings ) + Map::Map() -: width_(400), +: width_(400), height_(400), srs_(MAPNIK_GEOGRAPHIC_PROJ), buffer_size_(0), @@ -136,11 +137,11 @@ Map::Map(Map && rhs) extra_params_(std::move(rhs.extra_params_)), font_directory_(std::move(rhs.font_directory_)), font_file_mapping_(std::move(rhs.font_file_mapping_)), - font_memory_cache_(std::move(rhs.font_memory_cache_)) {} + font_memory_cache_(std::move(rhs.font_memory_cache_)), + proj_cache_(std::move(rhs.proj_cache_)) {} Map::~Map() {} -thread_local Map::proj_cache_type Map::proj_cache_ = proj_cache_type(); Map& Map::operator=(Map rhs) { @@ -327,21 +328,6 @@ size_t Map::layer_count() const return layers_.size(); } -proj_transform * Map::get_proj_transform(std::string const& source, std::string const& dest) const -{ - - compatible_key_type key = std::make_pair(source, dest); - auto itr = proj_cache_.find(key, compatible_hash{}, compatible_predicate{}); - if (itr == proj_cache_.end()) - { - mapnik::projection srs1(source, true); - mapnik::projection srs2(dest, true); - return proj_cache_.emplace(std::make_pair(source, dest), - std::make_unique(srs1, srs2)).first->second.get(); - } - return itr->second.get(); -} - void Map::add_layer(layer const& l) { init_proj_transform(srs_, l.srs()); @@ -366,7 +352,6 @@ void Map::remove_all() fontsets_.clear(); font_file_mapping_.clear(); font_memory_cache_.clear(); - proj_cache_.clear(); } layer const& Map::get_layer(size_t index) const @@ -551,7 +536,7 @@ void Map::zoom_all() if (layer.active()) { std::string const& layer_srs = layer.srs(); - proj_transform * proj_trans_ptr = get_proj_transform(srs_, layer_srs);; + proj_transform const* proj_trans_ptr = proj_cache_->get(srs_, layer_srs);; box2d layer_ext = layer.envelope(); if (proj_trans_ptr->backward(layer_ext, PROJ_ENVELOPE_POINTS)) { @@ -731,7 +716,7 @@ featureset_ptr Map::query_point(unsigned index, double x, double y) const mapnik::datasource_ptr ds = layer.datasource(); if (ds) { - proj_transform * proj_trans_ptr = get_proj_transform(layer.srs(), srs_); + proj_transform const* proj_trans_ptr = proj_cache_->get(layer.srs(), srs_); double z = 0; if (!proj_trans_ptr->equal() && !proj_trans_ptr->backward(x,y,z)) { @@ -794,18 +779,14 @@ void Map::set_extra_parameters(parameters& params) extra_params_ = params; } +mapnik::proj_transform const* Map::get_proj_transform(std::string const& source, std::string const& dest) const +{ + return proj_cache_->get(source, dest); +} void Map::init_proj_transform(std::string const& source, std::string const& dest) { - compatible_key_type key = std::make_pair(source, dest); - auto itr = proj_cache_.find(key, compatible_hash{}, compatible_predicate{}); - if (itr == proj_cache_.end()) - { - mapnik::projection p0(source, true); - mapnik::projection p1(dest, true); - proj_cache_.emplace(std::make_pair(source, dest), - std::make_unique(p0, p1)); - } + proj_cache_->init(source, dest); } void Map::init_proj_transforms() diff --git a/src/proj_transform_cache.cpp b/src/proj_transform_cache.cpp new file mode 100644 index 000000000..1e051bd8a --- /dev/null +++ b/src/proj_transform_cache.cpp @@ -0,0 +1,58 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2021 Artem Pavlenko + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ + +#include + +namespace mapnik { + +thread_local proj_transform_cache::cache_type proj_transform_cache::cache_ = cache_type(); + +void proj_transform_cache::init(std::string const& source, std::string const& dest) const +{ + compatible_key_type key = std::make_pair(source, dest); + auto itr = cache_.find(key, compatible_hash{}, compatible_predicate{}); + if (itr == cache_.end()) + { + mapnik::projection p0(source, true); + mapnik::projection p1(dest, true); + cache_.emplace(std::make_pair(source, dest), + std::make_unique(p0, p1)); + } +} + +proj_transform const* proj_transform_cache::get(std::string const& source, std::string const& dest) const +{ + + compatible_key_type key = std::make_pair(source, dest); + auto itr = cache_.find(key, compatible_hash{}, compatible_predicate{}); + if (itr == cache_.end()) + { + mapnik::projection srs1(source, true); + mapnik::projection srs2(dest, true); + return cache_.emplace(std::make_pair(source, dest), + std::make_unique(srs1, srs2)).first->second.get(); + } + return itr->second.get(); +} + + +} // namespace mapnik diff --git a/test/unit/renderer/feature_style_processor.cpp b/test/unit/renderer/feature_style_processor.cpp index ed125bf52..28d87ec22 100644 --- a/test/unit/renderer/feature_style_processor.cpp +++ b/test/unit/renderer/feature_style_processor.cpp @@ -8,7 +8,6 @@ #include #include #include -#include #include #include #include From 5dfd1a24fce7069847ced4644ec49ad2327b53fe Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Thu, 25 Mar 2021 16:03:15 +0000 Subject: [PATCH 55/56] Fix potential iterator out of range condition by using appropriate function (ref #4218) via @mathisloge ``` template< class InputIt1, class InputIt2, class BinaryPredicate > bool equal( InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, BinaryPredicate p ); ``` --- plugins/input/csv/csv_utils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/input/csv/csv_utils.cpp b/plugins/input/csv/csv_utils.cpp index edae079a1..dc5eaf6df 100644 --- a/plugins/input/csv/csv_utils.cpp +++ b/plugins/input/csv/csv_utils.cpp @@ -233,7 +233,7 @@ struct ignore_case_equal_pred bool ignore_case_equal(std::string const& s0, std::string const& s1) { return std::equal(s0.begin(), s0.end(), - s1.begin(), ignore_case_equal_pred()); + s1.begin(), s1.end(), ignore_case_equal_pred()); } void csv_file_parser::add_feature(mapnik::value_integer, mapnik::csv_line const & ) From 3489bdbe366af18aaed92da882f88be11664704b Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Thu, 25 Mar 2021 16:38:21 +0000 Subject: [PATCH 56/56] rename PROJ_VERSION to MAPNIK_PROJ_VERSION to avoid clashes with libproj --- SConstruct | 2 +- include/mapnik/projection.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/SConstruct b/SConstruct index e8fe2d84e..67f647272 100644 --- a/SConstruct +++ b/SConstruct @@ -1720,7 +1720,7 @@ if not preconfigured: env['SKIPPED_DEPS'].append('proj-min-version') else: env.Append(CPPDEFINES = define) - env.Append(CPPDEFINES = "-DPROJ_VERSION=%d" % version) + env.Append(CPPDEFINES = "-DMAPNIK_PROJ_VERSION=%d" % version) else: env.Append(CPPDEFINES = define) else: diff --git a/include/mapnik/projection.hpp b/include/mapnik/projection.hpp index ffca994d3..0f0918456 100644 --- a/include/mapnik/projection.hpp +++ b/include/mapnik/projection.hpp @@ -39,7 +39,7 @@ MAPNIK_DISABLE_WARNING_POP // fwd decl -#if PROJ_VERSION >= 80000 +#if MAPNIK_PROJ_VERSION >= 80000 struct pj_ctx; using PJ_CONTEXT = struct pj_ctx; #else