diff --git a/.travis.yml b/.travis.yml index 13cf1f20e..967786acd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -language: c +language: cpp git: depth: 10 @@ -25,26 +25,28 @@ matrix: - os: linux sudo: false compiler: ": clang" - env: JOBS=8 MASON_PUBLISH=true CXX="ccache clang++-3.5 -Qunused-arguments" CC="clang-3.5" TRIGGER=true + env: JOBS=8 MASON_PUBLISH=true _CXX="ccache clang++-3.8 -Qunused-arguments" _CC="clang-3.8" TRIGGER=true addons: apt: - sources: [ 'ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.5' ] - packages: [ 'clang-3.5' ] + sources: [ 'ubuntu-toolchain-r-test'] + packages: [ 'libstdc++6', 'libstdc++-5-dev'] - os: linux sudo: false compiler: ": clang-coverage" - env: JOBS=8 COVERAGE=true LLVM_VERSION="3.5" CXX="ccache clang++-3.5 -Qunused-arguments" CC="clang-3.5" + env: JOBS=8 COVERAGE=true _CXX="ccache clang++-3.8 -Qunused-arguments" _CC="clang-3.8" addons: apt: - sources: [ 'ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.5' ] - packages: [ 'clang-3.5', 'llvm-3.5-dev' ] + sources: [ 'ubuntu-toolchain-r-test'] + packages: [ 'libstdc++6','libstdc++-5-dev' ] - os: osx - compiler: clang + compiler: ": clang-osx" # https://docs.travis-ci.com/user/languages/objective-c/#Supported-OS-X-iOS-SDK-versions osx_image: xcode7.3 # upgrades clang from 6 -> 7 - env: JOBS=4 MASON_PUBLISH=true + env: JOBS=4 MASON_PUBLISH=true _CXX="ccache clang++ -Qunused-arguments" before_install: + - if [[ ${_CXX:-false} != false ]]; then export CXX=${_CXX}; fi + - if [[ ${_CC:-false} != false ]]; then export CC=${_CC}; fi - source scripts/travis-common.sh - export PYTHONUSERBASE=$(pwd)/mason_packages/.link - export PATH=${PREFIX}/bin:$(pwd)/mason_packages/.link/bin:${PYTHONUSERBASE}/bin:${PATH} @@ -56,7 +58,6 @@ before_install: - git_submodule_update --init --depth=10 install: - - if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then export CCACHE_READONLY=1; fi - on 'linux' export PYTHONPATH=${PYTHONUSERBASE}/lib/python2.7/site-packages - on 'osx' export PYTHONPATH=${PYTHONUSERBASE}/lib/python/site-packages - on 'osx' export DATA_PATH=$(brew --prefix)/var/postgres @@ -71,6 +72,13 @@ install: before_script: - source bootstrap.sh + - | + if [[ $(uname -s) == 'Linux' ]]; then + mason install clang 3.8.0 + export PATH=$(mason prefix clang 3.8.0)/bin:${PATH} + which clang++ + export LLVM_COV="$(mason prefix clang 3.8.0)/bin/llvm-cov" + fi - ccache --version - ccache -p || true - ccache --show-stats || true @@ -79,6 +87,7 @@ before_script: script: - export SCONSFLAGS='--debug=time' - configure BENCHMARK=${BENCH} + - cat config.log - make - make test - enabled ${COVERAGE} coverage diff --git a/CHANGELOG.md b/CHANGELOG.md index 073fcf61b..45467bc9d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,8 @@ Released: - Raster scaling: fixed crash and clipping negative pixel values of floating point rasters (https://github.com/mapnik/mapnik/pull/3349) - Restored support for unquoted strings in expressions (https://github.com/mapnik/mapnik/pull/3390) - [TWKB](https://github.com/TWKB/) support via https://github.com/mapnik/mapnik/pull/3356 (#3355) + - Visual test runner can render SVG, PDF and Postscript with Cairo renderer (https://github.com/mapnik/mapnik/pull/3418) + - Scale factor is now applied also to `text-line-spacing` and transforms (https://github.com/mapnik/mapnik/pull/3416) ## 3.0.10 diff --git a/README.md b/README.md index 1b677c84e..e99df9825 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ _/ _/ _/_/_/ _/_/_/ _/ _/ _/ _/ _/ ``` [![Build Status Linux](https://api.travis-ci.org/mapnik/mapnik.svg?branch=master)](http://travis-ci.org/mapnik/mapnik) +[![CircleCI](https://circleci.com/gh/mapnik/mapnik.svg?style=svg)](https://circleci.com/gh/mapnik/mapnik) [![Build Status Windows](https://ci.appveyor.com/api/projects/status/hc9l7okdjtucfqqn?branch=master&svg=true)](https://ci.appveyor.com/project/Mapbox/mapnik) [![Coverage Status](https://coveralls.io/repos/mapnik/mapnik/badge.svg?branch=master&service=github)](https://coveralls.io/github/mapnik/mapnik?branch=master) @@ -28,4 +29,4 @@ Please note that this project is released with a [Contributor Code of Conduct](h # License -Mapnik software is free and is released under the LGPL ([GNU Lesser General Public License](http://www.gnu.org/licenses/lgpl.html_)). Please see [COPYING](https://github.com/mapnik/mapnik/blob/master/COPYING) for more information. +Mapnik software is free and is released under the LGPL ([GNU Lesser General Public License](http://www.gnu.org/licenses/lgpl.html)). Please see [COPYING](https://github.com/mapnik/mapnik/blob/master/COPYING) for more information. diff --git a/SConstruct b/SConstruct index a4c4fda74..0523e184d 100644 --- a/SConstruct +++ b/SConstruct @@ -704,11 +704,7 @@ def FindBoost(context, prefixes, thread_flag): BOOST_INCLUDE_DIR = None BOOST_APPEND = None env['BOOST_APPEND'] = str() - - if env['THREADING'] == 'multi': - search_lib = 'libboost_thread' - else: - search_lib = 'libboost_filesystem' + search_lib = 'libboost_filesystem' # note: must call normpath to strip trailing slash otherwise dirname # does not remove 'lib' and 'include' @@ -1359,7 +1355,7 @@ if not preconfigured: # test for C++11 support, which is required if not env['HOST'] and not conf.supports_cxx11(): - color_print(1,"C++ compiler does not support C++11 standard (-std=c++11), which is required. Please upgrade your compiler to at least g++ 4.7 (ideally 4.8)") + color_print(1,"C++ compiler does not support C++11 standard (-std=c++11), which is required. Please upgrade your compiler") Exit(1) if not env['HOST']: @@ -1411,15 +1407,6 @@ if not preconfigured: ['program_options', 'boost/program_options.hpp', False] ] - if env['THREADING'] == 'multi': - BOOST_LIBSHEADERS.append(['thread', 'boost/thread/mutex.hpp', True]) - # on solaris the configure checks for boost_thread - # require the -pthreads flag to be able to check for - # threading support, so we add as a global library instead - # of attaching to cxxflags after configure - if env['PLATFORM'] == 'SunOS': - env.Append(CXXFLAGS = '-pthreads') - # if requested, sort LIBPATH and CPPPATH before running CheckLibWithHeader tests if env['PRIORITIZE_LINKING']: conf.prioritize_paths(silent=True) @@ -1610,6 +1597,7 @@ if not preconfigured: # prepend to make sure we link locally env.Prepend(CPPPATH = '#deps/agg/include') env.Prepend(LIBPATH = '#deps/agg') + env.Prepend(CPPPATH = '#deps/mapbox/variant/include') # prepend deps dir for auxillary headers env.Prepend(CPPPATH = '#deps') diff --git a/appveyor.yml b/appveyor.yml index f2360211b..34ddc3b6a 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,6 +1,6 @@ environment: msvs_toolset: 14 - BOOST_VERSION: 59 + BOOST_VERSION: 60 FASTBUILD: 1 matrix: - platform: x64 diff --git a/bootstrap.sh b/bootstrap.sh index 3a6470990..fd877a644 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -10,7 +10,7 @@ todo - shrink icu data ' -MASON_VERSION="694d08c" +MASON_VERSION="b709931" function setup_mason() { if [[ ! -d ./.mason ]]; then @@ -20,7 +20,6 @@ function setup_mason() { echo "Updating to latest mason" (cd ./.mason && git fetch && git checkout ${MASON_VERSION}) fi - export MASON_DIR=$(pwd)/.mason export PATH=$(pwd)/.mason:$PATH export CXX=${CXX:-clang++} export CC=${CC:-clang} @@ -32,7 +31,7 @@ function install() { mason install $1 $2 mason link $1 $2 if [[ ${3:-false} != false ]]; then - LA_FILE=$(${MASON_DIR:-~/.mason}/mason prefix $1 $2)/lib/$3.la + LA_FILE=$(mason prefix $1 $2)/lib/$3.la if [[ -f ${LA_FILE} ]]; then perl -i -p -e 's:\Q$ENV{HOME}/build/mapbox/mason\E:$ENV{PWD}:g' ${LA_FILE} else @@ -63,8 +62,11 @@ function install_mason_deps() { wait install webp 0.4.2 libwebp & install gdal 1.11.2 libgdal & - install boost 1.59.0 & - install boost_liball 1.59.0 & + install boost 1.61.0 & + install boost_libsystem 1.61.0 & + install boost_libfilesystem 1.61.0 & + install boost_libprogram_options 1.61.0 & + install boost_libregex 1.61.0 & install freetype 2.6 libfreetype & install harfbuzz 0.9.41 libharfbuzz & wait diff --git a/circle.yml b/circle.yml index e7c12d7cf..cf3e31cb1 100644 --- a/circle.yml +++ b/circle.yml @@ -7,7 +7,7 @@ machine: JOBS: 8 CCACHE_TEMPDIR: /tmp/.ccache-temp CCACHE_COMPRESS: 1 - LLVM_VERSION: 3.7 + LLVM_VERSION: 3.8 pre: - echo "here" post: @@ -25,20 +25,16 @@ dependencies: pre: # https://discuss.circleci.com/t/add-ability-to-cache-apt-get-programs/598/3 - sudo rm -rf /var/cache/apt/archives && sudo ln -s ~/.apt-cache /var/cache/apt/archives && mkdir -p ~/.apt-cache/partial - - sudo wget -O - http://llvm.org/apt/llvm-snapshot.gpg.key | sudo apt-key add - - - sudo add-apt-repository -y "deb http://llvm.org/apt/precise/ llvm-toolchain-precise-${LLVM_VERSION} main" - sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test - - sudo apt-get update -y override: - - sudo apt-get install clang-${LLVM_VERSION} -y - post: - - which clang-${LLVM_VERSION} - - which clang++-${LLVM_VERSION} + - sudo apt-get update -y database: pre: - ./bootstrap.sh - - ./configure CC="clang-${LLVM_VERSION}" CXX="$(pwd)/mason_packages/.link/bin/ccache clang++-${LLVM_VERSION} -Qunused-arguments" + - ./.mason/mason install clang ${LLVM_VERSION}.0 + - ./.mason/mason link clang ${LLVM_VERSION}.0 + - ./configure CC="$(pwd)/mason_packages/.link/bin/clang-${LLVM_VERSION}" CXX="$(pwd)/mason_packages/.link/bin/ccache $(pwd)/mason_packages/.link/bin/clang++-${LLVM_VERSION} -Qunused-arguments" - make override: - psql -c 'create database template_postgis;' diff --git a/demo/c++/README.md b/demo/c++/README.md index daa4198c1..548508ee0 100644 --- a/demo/c++/README.md +++ b/demo/c++/README.md @@ -52,11 +52,11 @@ If you do not have svn installed you can grab gyp from: Simply type: - make + make Then to run do: - ./rundemo `mapnik-config --prefix` + ./rundemo `mapnik-config --prefix` On OS X you can also create an xcode project: diff --git a/demo/c++/rundemo.cpp b/demo/c++/rundemo.cpp index c07bd5c31..c20c288cf 100644 --- a/demo/c++/rundemo.cpp +++ b/demo/c++/rundemo.cpp @@ -55,7 +55,7 @@ int main ( int, char** ) try { std::cout << " running demo ... \n"; datasource_cache::instance().register_datasources("plugins/input/"); - freetype_engine::register_font("fonts/dejavu-fonts-ttf-2.34/ttf/DejaVuSans.ttf"); + freetype_engine::register_font("fonts/dejavu-fonts-ttf-2.35/ttf/DejaVuSans.ttf"); Map m(800,600); m.set_background(parse_color("white")); @@ -230,7 +230,7 @@ int main ( int, char** ) parameters p; p["type"]="shape"; p["file"]="demo/data/boundaries"; - p["encoding"]="latin1"; + p["encoding"]="utf8"; layer lyr("Provinces"); lyr.set_datasource(datasource_cache::instance().create(p)); @@ -295,7 +295,7 @@ int main ( int, char** ) parameters p; p["type"]="shape"; p["file"]="demo/data/popplaces"; - p["encoding"] = "latin1"; + p["encoding"] = "utf8"; layer lyr("Populated Places"); lyr.set_srs(srs_lcc); lyr.set_datasource(datasource_cache::instance().create(p)); diff --git a/demo/data/boundaries.dbf b/demo/data/boundaries.dbf index aeed23561..04d2ed661 100644 Binary files a/demo/data/boundaries.dbf and b/demo/data/boundaries.dbf differ diff --git a/demo/data/boundaries.prj b/demo/data/boundaries.prj deleted file mode 100644 index 21aa9c392..000000000 --- a/demo/data/boundaries.prj +++ /dev/null @@ -1 +0,0 @@ -PROJCS["Atlas of Canada Lambert Conformal Conic",GEOGCS["GCS_North_American_1983",DATUM["D_North_American_1983",SPHEROID["GRS_1980",6378137.0,298.257222101]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Lambert_Conformal_Conic"],PARAMETER["False_Easting",0.0],PARAMETER["False_Northing",0.0],PARAMETER["Central_Meridian",-95.0],PARAMETER["Standard_Parallel_1",49.0],PARAMETER["Standard_Parallel_2",77.0],PARAMETER["Latitude_Of_Origin",49.0],UNIT["Meter",1.0]] \ No newline at end of file diff --git a/demo/data/boundaries.sbx b/demo/data/boundaries.sbx deleted file mode 100644 index c66e6e334..000000000 Binary files a/demo/data/boundaries.sbx and /dev/null differ diff --git a/demo/data/boundaries_l.dbf b/demo/data/boundaries_l.dbf index e6bc20c98..44d4ca8e1 100644 Binary files a/demo/data/boundaries_l.dbf and b/demo/data/boundaries_l.dbf differ diff --git a/demo/data/boundaries_l.prj b/demo/data/boundaries_l.prj deleted file mode 100644 index 21aa9c392..000000000 --- a/demo/data/boundaries_l.prj +++ /dev/null @@ -1 +0,0 @@ -PROJCS["Atlas of Canada Lambert Conformal Conic",GEOGCS["GCS_North_American_1983",DATUM["D_North_American_1983",SPHEROID["GRS_1980",6378137.0,298.257222101]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Lambert_Conformal_Conic"],PARAMETER["False_Easting",0.0],PARAMETER["False_Northing",0.0],PARAMETER["Central_Meridian",-95.0],PARAMETER["Standard_Parallel_1",49.0],PARAMETER["Standard_Parallel_2",77.0],PARAMETER["Latitude_Of_Origin",49.0],UNIT["Meter",1.0]] \ No newline at end of file diff --git a/demo/data/boundaries_l.sbx b/demo/data/boundaries_l.sbx deleted file mode 100644 index e609d80c5..000000000 Binary files a/demo/data/boundaries_l.sbx and /dev/null differ diff --git a/demo/data/ontdrainage.dbf b/demo/data/ontdrainage.dbf index 33d8004b6..819265027 100644 Binary files a/demo/data/ontdrainage.dbf and b/demo/data/ontdrainage.dbf differ diff --git a/demo/data/ontdrainage.prj b/demo/data/ontdrainage.prj deleted file mode 100644 index 21aa9c392..000000000 --- a/demo/data/ontdrainage.prj +++ /dev/null @@ -1 +0,0 @@ -PROJCS["Atlas of Canada Lambert Conformal Conic",GEOGCS["GCS_North_American_1983",DATUM["D_North_American_1983",SPHEROID["GRS_1980",6378137.0,298.257222101]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Lambert_Conformal_Conic"],PARAMETER["False_Easting",0.0],PARAMETER["False_Northing",0.0],PARAMETER["Central_Meridian",-95.0],PARAMETER["Standard_Parallel_1",49.0],PARAMETER["Standard_Parallel_2",77.0],PARAMETER["Latitude_Of_Origin",49.0],UNIT["Meter",1.0]] \ No newline at end of file diff --git a/demo/data/ontdrainage.sbx b/demo/data/ontdrainage.sbx deleted file mode 100644 index 3739c6198..000000000 Binary files a/demo/data/ontdrainage.sbx and /dev/null differ diff --git a/demo/data/popplaces.dbf b/demo/data/popplaces.dbf index 408968e85..d385b6f1d 100644 Binary files a/demo/data/popplaces.dbf and b/demo/data/popplaces.dbf differ diff --git a/demo/data/popplaces.prj b/demo/data/popplaces.prj deleted file mode 100644 index 21aa9c392..000000000 --- a/demo/data/popplaces.prj +++ /dev/null @@ -1 +0,0 @@ -PROJCS["Atlas of Canada Lambert Conformal Conic",GEOGCS["GCS_North_American_1983",DATUM["D_North_American_1983",SPHEROID["GRS_1980",6378137.0,298.257222101]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Lambert_Conformal_Conic"],PARAMETER["False_Easting",0.0],PARAMETER["False_Northing",0.0],PARAMETER["Central_Meridian",-95.0],PARAMETER["Standard_Parallel_1",49.0],PARAMETER["Standard_Parallel_2",77.0],PARAMETER["Latitude_Of_Origin",49.0],UNIT["Meter",1.0]] \ No newline at end of file diff --git a/demo/data/popplaces.sbx b/demo/data/popplaces.sbx deleted file mode 100644 index c02888185..000000000 Binary files a/demo/data/popplaces.sbx and /dev/null differ diff --git a/demo/data/qcdrainage.dbf b/demo/data/qcdrainage.dbf index 95ce20f20..04fcf8541 100644 Binary files a/demo/data/qcdrainage.dbf and b/demo/data/qcdrainage.dbf differ diff --git a/demo/data/qcdrainage.prj b/demo/data/qcdrainage.prj deleted file mode 100644 index 21aa9c392..000000000 --- a/demo/data/qcdrainage.prj +++ /dev/null @@ -1 +0,0 @@ -PROJCS["Atlas of Canada Lambert Conformal Conic",GEOGCS["GCS_North_American_1983",DATUM["D_North_American_1983",SPHEROID["GRS_1980",6378137.0,298.257222101]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Lambert_Conformal_Conic"],PARAMETER["False_Easting",0.0],PARAMETER["False_Northing",0.0],PARAMETER["Central_Meridian",-95.0],PARAMETER["Standard_Parallel_1",49.0],PARAMETER["Standard_Parallel_2",77.0],PARAMETER["Latitude_Of_Origin",49.0],UNIT["Meter",1.0]] \ No newline at end of file diff --git a/demo/data/qcdrainage.sbx b/demo/data/qcdrainage.sbx deleted file mode 100644 index c73534e78..000000000 Binary files a/demo/data/qcdrainage.sbx and /dev/null differ diff --git a/demo/data/roads.dbf b/demo/data/roads.dbf index 73c1db9dd..709f88182 100644 Binary files a/demo/data/roads.dbf and b/demo/data/roads.dbf differ diff --git a/demo/data/roads.prj b/demo/data/roads.prj deleted file mode 100644 index 21aa9c392..000000000 --- a/demo/data/roads.prj +++ /dev/null @@ -1 +0,0 @@ -PROJCS["Atlas of Canada Lambert Conformal Conic",GEOGCS["GCS_North_American_1983",DATUM["D_North_American_1983",SPHEROID["GRS_1980",6378137.0,298.257222101]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Lambert_Conformal_Conic"],PARAMETER["False_Easting",0.0],PARAMETER["False_Northing",0.0],PARAMETER["Central_Meridian",-95.0],PARAMETER["Standard_Parallel_1",49.0],PARAMETER["Standard_Parallel_2",77.0],PARAMETER["Latitude_Of_Origin",49.0],UNIT["Meter",1.0]] \ No newline at end of file diff --git a/demo/data/roads.sbx b/demo/data/roads.sbx deleted file mode 100644 index b804cb3b2..000000000 Binary files a/demo/data/roads.sbx and /dev/null differ diff --git a/deps/mapbox/variant b/deps/mapbox/variant index 3f025adbf..b5728ad76 160000 --- a/deps/mapbox/variant +++ b/deps/mapbox/variant @@ -1 +1 @@ -Subproject commit 3f025adbf599d8dd9bfca02d45b37e49a2cae841 +Subproject commit b5728ad76e1402c130a9330aa44b6f4b655b13b4 diff --git a/deps/mapnik/build.py b/deps/mapnik/build.py index 0d6dc8486..76529fbda 100644 --- a/deps/mapnik/build.py +++ b/deps/mapnik/build.py @@ -7,7 +7,7 @@ subdirs = { './sparsehash':{'dir':'sparsehash','glob':'*'}, './sparsehash/internal':{'dir':'sparsehash/internal','glob':'*'}, '../agg/include':{'dir':'agg','glob':'agg*'}, - '../mapbox':{'dir':'mapbox/variant','glob':'*/*.hpp'} + '../mapbox/variant/include':{'dir':'mapbox','glob':'*/*.hpp'} } if 'install' in COMMAND_LINE_TARGETS: diff --git a/include/mapnik/config.hpp b/include/mapnik/config.hpp index 6461a6c08..5b4c82be1 100644 --- a/include/mapnik/config.hpp +++ b/include/mapnik/config.hpp @@ -52,7 +52,10 @@ #define PROJ_ENVELOPE_POINTS 20 +#ifndef BOOST_MPL_LIMIT_VECTOR_SIZE #define BOOST_MPL_CFG_NO_PREPROCESSED_HEADERS #define BOOST_MPL_LIMIT_VECTOR_SIZE 30 - +#else +#warning "WARNING: BOOST_MPL_LIMIT_VECTOR_SIZE is already defined. Ensure config.hpp is included before any Boost headers" +#endif #endif // MAPNIK_CONFIG_HPP diff --git a/include/mapnik/geometry.hpp b/include/mapnik/geometry.hpp index 6c0322ebf..436f6611d 100644 --- a/include/mapnik/geometry.hpp +++ b/include/mapnik/geometry.hpp @@ -49,7 +49,6 @@ struct geometry_empty using coord_type = T; }; - template using geometry_base = mapnik::util::variant, point, @@ -58,11 +57,12 @@ using geometry_base = mapnik::util::variant, multi_point, multi_line_string, multi_polygon, - mapnik::util::recursive_wrapper > >; + geometry_collection >; template struct geometry : geometry_base { using coord_type = T; + geometry() : geometry_base() {} // empty @@ -72,6 +72,7 @@ struct geometry : geometry_base }; + template class Cont> struct geometry_collection : Cont> { diff --git a/include/mapnik/json/attribute_value_visitor.hpp b/include/mapnik/json/attribute_value_visitor.hpp new file mode 100644 index 000000000..206530cae --- /dev/null +++ b/include/mapnik/json/attribute_value_visitor.hpp @@ -0,0 +1,68 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2016 Artem Pavlenko + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ + +#ifndef MAPNIK_JSON_ATTRIBUTE_VALUE_VISITOR_HPP +#define MAPNIK_JSON_ATTRIBUTE_VALUE_VISITOR_HPP + +// mapnik +#include +#include +#include +#include + +namespace mapnik { namespace json { + +struct attribute_value_visitor +{ +public: + attribute_value_visitor(mapnik::transcoder const& tr) + : tr_(tr) {} + + mapnik::value operator()(std::string const& val) const + { + return mapnik::value(tr_.transcode(val.c_str())); + } + + mapnik::value operator()(std::vector const& array) const + { + std::string str = stringifier()(array); + return mapnik::value(tr_.transcode(str.c_str())); + } + + mapnik::value operator()(std::vector > const& object) const + { + std::string str = stringifier()(object); + return mapnik::value(tr_.transcode(str.c_str())); + } + + template + mapnik::value operator()(T const& val) const + { + return mapnik::value(val); + } + + mapnik::transcoder const& tr_; +}; + +}} + +#endif //MAPNIK_JSON_ATTRIBUTE_VALUE_VISITOR_HPP diff --git a/include/mapnik/json/extract_bounding_box_grammar_impl.hpp b/include/mapnik/json/extract_bounding_box_grammar_impl.hpp index 8a7fc331c..9e21baa08 100644 --- a/include/mapnik/json/extract_bounding_box_grammar_impl.hpp +++ b/include/mapnik/json/extract_bounding_box_grammar_impl.hpp @@ -148,13 +148,10 @@ extract_bounding_box_grammar::extract_bounding_bo json.value = json.object | json.array | json.string_ | json.number ; - json.pairs = json.key_value % lit(',') + json.key_value = json.string_ >> lit(':') >> json.value ; - json.key_value = (json.string_ >> lit(':') >> json.value) - ; - - json.object = lit('{') >> *json.pairs >> lit('}') + json.object = lit('{') >> json.key_value % lit(',') >> lit('}') ; json.array = lit('[') diff --git a/include/mapnik/json/feature_grammar.hpp b/include/mapnik/json/feature_grammar.hpp index ae59e3bf0..c329eb7f5 100644 --- a/include/mapnik/json/feature_grammar.hpp +++ b/include/mapnik/json/feature_grammar.hpp @@ -24,14 +24,10 @@ #define MAPNIK_FEATURE_GRAMMAR_HPP // mapnik -#include #include #include -#include -#include -#include -#include - +#include +#include #pragma GCC diagnostic push #include #include @@ -45,27 +41,6 @@ namespace qi = boost::spirit::qi; namespace phoenix = boost::phoenix; namespace fusion = boost::fusion; -class attribute_value_visitor - -{ -public: - attribute_value_visitor(mapnik::transcoder const& tr) - : tr_(tr) {} - - mapnik::value operator()(std::string const& val) const - { - return mapnik::value(tr_.transcode(val.c_str())); - } - - template - mapnik::value operator()(T const& val) const - { - return mapnik::value(val); - } - - mapnik::transcoder const& tr_; -}; - struct put_property { using result_type = void; @@ -101,9 +76,6 @@ struct feature_grammar : qi::grammar qi::rule feature_type; qi::rule properties; qi::rule, void(FeatureType &),space_type> attributes; - qi::rule attribute_value; - qi::rule, std::string(), space_type> stringify_object; - qi::rule, std::string(), space_type> stringify_array; // functions phoenix::function put_property_; phoenix::function set_geometry; diff --git a/include/mapnik/json/feature_grammar_impl.hpp b/include/mapnik/json/feature_grammar_impl.hpp index 61a23f985..0fc4205ad 100644 --- a/include/mapnik/json/feature_grammar_impl.hpp +++ b/include/mapnik/json/feature_grammar_impl.hpp @@ -50,23 +50,22 @@ feature_grammar::feature_grammar(mapnik::tran using phoenix::construct; // generic json types - json_.value = json_.object | json_.array | json_.string_ | json_.number + json_.value = json_.object | json_.array | json_.string_ | json_.number ; - json_.pairs = json_.key_value % lit(',') - ; - - json_.key_value = (json_.string_ > lit(':') > json_.value) + json_.key_value = json_.string_ > lit(':') > json_.value ; json_.object = lit('{') - > *json_.pairs + > -(json_.key_value % lit(',')) > lit('}') ; + json_.array = lit('[') - > json_.value > *(lit(',') > json_.value) + > -(json_.value % lit(',')) > lit(']') ; + json_.number = json_.strict_double[_val = json_.double_converter(_1)] | json_.int__[_val = json_.integer_converter(_1)] | lit("true") [_val = true] @@ -95,24 +94,13 @@ feature_grammar::feature_grammar(mapnik::tran > lit(':') > ((lit('{') > -attributes(_r1) > lit('}')) | lit("null")) ; - attributes = (json_.string_ [_a = _1] > lit(':') > attribute_value [put_property_(_r1,_a,_1)]) % lit(',') - ; - - attribute_value %= json_.number | json_.string_ | stringify_object | stringify_array - ; - - stringify_object %= char_('{')[_a = 1 ] > *(eps(_a > 0) > (char_('{')[_a +=1] | char_('}')[_a -=1] | char_)) - ; - - stringify_array %= char_('[')[_a = 1 ] > *(eps(_a > 0) > (char_('[')[_a +=1] | char_(']')[_a -=1] | char_)) + attributes = (json_.string_ [_a = _1] > lit(':') > json_.value [put_property_(_r1,_a,_1)]) % lit(',') ; feature.name("Feature"); feature_type.name("type"); properties.name("properties"); attributes.name("Attributes"); - attribute_value.name("Attribute Value"); - on_error(feature, error_handler(_1, _2, _3, _4)); } diff --git a/include/mapnik/json/generic_json.hpp b/include/mapnik/json/generic_json.hpp index 331341066..bc71eb67c 100644 --- a/include/mapnik/json/generic_json.hpp +++ b/include/mapnik/json/generic_json.hpp @@ -31,15 +31,44 @@ #include #include #include +#include #pragma GCC diagnostic pop +#include + namespace mapnik { namespace json { namespace qi = boost::spirit::qi; namespace standard = boost::spirit::standard; namespace phoenix = boost::phoenix; using space_type = standard::space_type; -using json_value = mapnik::util::variant; + +struct json_value; + +using json_array = std::vector; +using json_object_element = std::pair; +using json_object = std::vector; +using json_value_base = mapnik::util::variant, + mapnik::util::recursive_wrapper >; +struct json_value : json_value_base +{ + +#ifdef _WINDOWS + json_value() = default; + template + json_value(T && val) + : json_value_base(std::forward(val)) {} +#else + // MSVC 2015 inheriting constructors is not working in this context (support is apparently planned) + using json_value_base::json_value_base; +#endif +}; + using uchar = std::uint32_t; // a unicode code point // unicode string grammar via boost/libs/spirit/example/qi/json/json/parser/grammar.hpp @@ -121,10 +150,14 @@ unicode_string::unicode_string() escape = ('x' > hex) [push_utf8(_r1, _1)] - | ('u' > hex4) [push_utf8(_r1, _1)] - | ('U' > hex8) [push_utf8(_r1, _1)] - | char_("0abtnvfre\"/\\N_LP \t") [push_esc(_r1, _1)] - | eol // continue to next line + | + ('u' > hex4) [push_utf8(_r1, _1)] + | + ('U' > hex8) [push_utf8(_r1, _1)] + | + char_("0abtnvfre\"/\\N_LP \t") [push_esc(_r1, _1)] + | + eol // continue to next line ; char_esc = @@ -132,7 +165,7 @@ unicode_string::unicode_string() ; double_quoted = - '"' + '"' > *(char_esc(_val) | (~char_('"')) [_val += _1]) > '"' ; @@ -141,18 +174,17 @@ unicode_string::unicode_string() template struct generic_json { - qi::rule value; - qi::int_parser int__; + qi::rule value; + qi::int_parser int__; unicode_string string_; - qi::rule key_value; - qi::rule number; - qi::rule object; - qi::rule array; - qi::rule pairs; - qi::real_parser > strict_double; + qi::rule key_value; + qi::rule number; + qi::rule object; + qi::rule array; + qi::real_parser> strict_double; // conversions - boost::phoenix::function > integer_converter; - boost::phoenix::function > double_converter; + boost::phoenix::function> integer_converter; + boost::phoenix::function> double_converter; }; }} diff --git a/include/mapnik/json/geometry_grammar_impl.hpp b/include/mapnik/json/geometry_grammar_impl.hpp index 3c5693f91..285be2744 100644 --- a/include/mapnik/json/geometry_grammar_impl.hpp +++ b/include/mapnik/json/geometry_grammar_impl.hpp @@ -47,7 +47,6 @@ geometry_grammar::geometry_grammar() qi::_a_type _a; qi::_b_type _b; qi::eps_type eps; - qi::omit_type omit; using qi::fail; using qi::on_error; using phoenix::push_back; @@ -58,26 +57,26 @@ geometry_grammar::geometry_grammar() json_.value = json_.object | json_.array | json_.string_ | json_.number ; - json_.pairs = json_.key_value % lit(',') - ; - - json_.key_value = (json_.string_ > lit(':') > json_.value) + json_.key_value = json_.string_ > lit(':') > json_.value ; json_.object = lit('{') - > *json_.pairs + > -(json_.key_value % lit(',')) > lit('}') ; + json_.array = lit('[') - > json_.value > *(lit(',') > json_.value) + > -(json_.value % lit(',')) > lit(']') ; + json_.number = json_.strict_double | json_.int__ | lit("true") | lit ("false") | lit("null") ; + geometry = lit('{')[_a = 0] > (((lit("\"type\"") > lit(':') > geometry_type_dispatch[_a = _1]) | @@ -85,7 +84,7 @@ geometry_grammar::geometry_grammar() | (lit("\"geometries\"") > lit(':') > lit('[') > geometry_collection[_val = _1] > lit(']')) | - omit[json_.key_value]) % lit(',')) [create_geometry(_val,_a,_b)] + json_.key_value) % lit(',')) [create_geometry(_val,_a,_b)] > lit('}') ; diff --git a/include/mapnik/json/positions_grammar.hpp b/include/mapnik/json/positions_grammar.hpp index bc8104611..75a6b21b6 100644 --- a/include/mapnik/json/positions_grammar.hpp +++ b/include/mapnik/json/positions_grammar.hpp @@ -32,7 +32,6 @@ #pragma GCC diagnostic push #include #include -#include #pragma GCC diagnostic pop @@ -40,26 +39,6 @@ namespace mapnik { namespace json { namespace qi = boost::spirit::qi; -struct set_position_impl -{ - using result_type = void; - template - result_type operator() (T0 & coords, T1 const& pos) const - { - if (pos) coords = *pos; - } -}; - -struct push_position_impl -{ - using result_type = void; - template - result_type operator() (T0 & coords, T1 const& pos) const - { - if (pos) coords.emplace_back(*pos); - } -}; - template > struct positions_grammar : qi::grammar @@ -70,8 +49,6 @@ struct positions_grammar : qi::rule ring; qi::rule(), space_type> rings; qi::rule >(), space_type> rings_array; - boost::phoenix::function set_position; - boost::phoenix::function push_position; }; }} diff --git a/include/mapnik/json/positions_grammar_impl.hpp b/include/mapnik/json/positions_grammar_impl.hpp index 90df172c5..65f2a93c0 100644 --- a/include/mapnik/json/positions_grammar_impl.hpp +++ b/include/mapnik/json/positions_grammar_impl.hpp @@ -28,12 +28,33 @@ #include #include #include +#include // stl #include #include namespace mapnik { namespace json { +struct set_position_impl +{ + using result_type = void; + template + result_type operator() (T0 & coords, T1 const& pos) const + { + if (pos) coords = *pos; + } +}; + +struct push_position_impl +{ + using result_type = void; + template + result_type operator() (T0 & coords, T1 const& pos) const + { + if (pos) coords.emplace_back(*pos); + } +}; + template positions_grammar::positions_grammar(ErrorHandler & error_handler) : positions_grammar::base_type(coords,"coordinates") @@ -49,6 +70,9 @@ positions_grammar::positions_grammar(ErrorHandler & erro using qi::fail; using qi::on_error; + boost::phoenix::function set_position; + boost::phoenix::function push_position; + coords = rings_array[_val = _1] | rings [_val = _1] | ring[_val = _1] | pos[set_position(_val,_1)] ; pos = lit('[') > -(double_ > lit(',') > double_) > omit[*(lit(',') > double_)] > lit(']') diff --git a/include/mapnik/json/stringifier.hpp b/include/mapnik/json/stringifier.hpp new file mode 100644 index 000000000..93906f6ec --- /dev/null +++ b/include/mapnik/json/stringifier.hpp @@ -0,0 +1,101 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2016 Artem Pavlenko + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ + +#ifndef MAPNIK_JSON_STRINGIFIER_HPP +#define MAPNIK_JSON_STRINGIFIER_HPP + +// mapnik +#include +#include +#include +// stl +#include + + +namespace mapnik { namespace json { + +struct stringifier +{ + std::string operator()(std::string const& val) const + { + return "\"" + val + "\""; + } + + std::string operator()(value_null) const + { + return "null"; + } + + std::string operator()(value_bool val) const + { + return val ? "true" : "false"; + } + + std::string operator()(value_integer val) const + { + std::string str; + util::to_string(str, val); + return str; + } + + std::string operator()(value_double val) const + { + std::string str; + util::to_string(str, val); + return str; + } + + std::string operator()(std::vector const& array) const + { + std::string str = "["; + bool first = true; + for (auto const& val : array) + { + if (first) first = false; + else str += ","; + str += mapnik::util::apply_visitor(*this, val); + } + str += "]"; + return str; + } + + std::string operator()(std::vector> const& object) const + { + std::string str = "{"; + bool first = true; + for (auto const& kv : object) + { + if (first) first = false; + else str += ","; + str += kv.first; + str += ":"; + str += mapnik::util::apply_visitor(*this, kv.second); + } + str += "}"; + return str; + } +}; + +}} + + +#endif // MAPNIK_JSON_STRINGIFIER_HPP diff --git a/include/mapnik/json/topojson_grammar.hpp b/include/mapnik/json/topojson_grammar.hpp index eec2bb41a..c21be95fe 100644 --- a/include/mapnik/json/topojson_grammar.hpp +++ b/include/mapnik/json/topojson_grammar.hpp @@ -42,6 +42,138 @@ namespace qi = boost::spirit::qi; namespace fusion = boost::fusion; using space_type = mapnik::json::space_type; +struct create_point +{ + using result_type = mapnik::topojson::point; + template + result_type operator()(T0 & coord, T1 & props) const + { + mapnik::topojson::point pt; + if (coord.template is()) + { + auto const& coord_ = coord.template get(); + pt.coord = coord_; + pt.props = props; + } + return pt; + } +}; + +struct create_multi_point +{ + using result_type = mapnik::topojson::multi_point; + template + result_type operator()(T0 & coords, T1 & props) const + { + mapnik::topojson::multi_point mpt; + if (coords.template is>()) + { + auto const& points = coords.template get>(); + mpt. points = points; + mpt.props = props; + } + return mpt; + } +}; + +struct create_line_string +{ + using result_type = mapnik::topojson::linestring; + template + result_type operator()(T0 & arcs, T1 & props) const + { + mapnik::topojson::linestring line; + if (arcs.template is>()) + { + auto const& arcs_ = arcs.template get>(); + line.rings = arcs_; + line.props = props; + } + return line; + } +}; + +struct create_multi_line_string +{ + using result_type = mapnik::topojson::multi_linestring; + template + result_type operator()(T0 & arcs, T1 & props) const + { + mapnik::topojson::multi_linestring mline; + if (arcs.template is>>()) + { + auto const& arcs_ = arcs.template get>>(); + mline.lines = arcs_; + mline.props = props; + } + return mline; + } +}; + +struct create_polygon +{ + using result_type = mapnik::topojson::polygon; + template + result_type operator()(T0 & arcs, T1 & props) const + { + mapnik::topojson::polygon poly; + if (arcs.template is>>()) + { + auto const& arcs_ = arcs.template get>>(); + poly.rings = arcs_; + poly.props = props; + } + return poly; + } +}; + +struct create_multi_polygon +{ + using result_type = mapnik::topojson::multi_polygon; + template + result_type operator()(T0 & arcs, T1 & props) const + { + mapnik::topojson::multi_polygon mpoly; + if (arcs.template is>>>()) + { + auto const& arcs_ = arcs.template get>>>(); + mpoly.polygons = arcs_; + mpoly.props = props; + } + return mpoly; + } +}; + + +struct create_geometry_impl +{ + using result_type = mapnik::topojson::geometry; + template + result_type operator()(T0 geom_type, T1 & coord, T2 & arcs, T3 & props) const + { + switch (geom_type) + { + case 1: //Point + return create_point()(coord, props); + case 2: //LineString + return create_line_string()(arcs, props); + case 3: //Polygon + return create_polygon()(arcs, props); + case 4: //MultiPoint + return create_multi_point()(coord, props); + case 5: //MultiLineString + return create_multi_line_string()(arcs, props); + case 6: //MultiPolygon + return create_multi_polygon()(arcs, props); + default: + break; + } + return mapnik::topojson::geometry(); //empty + } +}; + +using coordinates_type = util::variant>; +using arcs_type = util::variant, std::vector>, std::vector>>>; template > struct topojson_grammar : qi::grammar @@ -55,24 +187,18 @@ private: qi::rule()> objects; qi::rule()> arcs; qi::rule arc; - qi::rule coordinate; + qi::rule coordinate_; + qi::rule coordinates; qi::rule transform; qi::rule bbox; - qi::rule geometry; - qi::rule point; - qi::rule multi_point; - qi::rule linestring; - qi::rule multi_linestring; - qi::rule polygon; - qi::rule multi_polygon; + qi::rule, mapnik::topojson::geometry(), space_type> geometry; qi::rule&)> geometry_collection; qi::rule()> ring; + qi::rule>()> rings; + qi::rule rings_array; // properties - qi::rule properties; - qi::rule attributes; - qi::rule attribute_value; - // id - qi::rule id; + qi::rule properties_; + qi::symbols geometry_type_dispatch; }; }} diff --git a/include/mapnik/json/topojson_grammar_impl.hpp b/include/mapnik/json/topojson_grammar_impl.hpp index ca8607996..f868cefc4 100644 --- a/include/mapnik/json/topojson_grammar_impl.hpp +++ b/include/mapnik/json/topojson_grammar_impl.hpp @@ -57,42 +57,6 @@ BOOST_FUSION_ADAPT_STRUCT( (double, maxy) ) -BOOST_FUSION_ADAPT_STRUCT( - mapnik::topojson::point, - (mapnik::topojson::coordinate, coord) - (boost::optional, props) - ) - -BOOST_FUSION_ADAPT_STRUCT( - mapnik::topojson::multi_point, - (std::vector, points) - (boost::optional, props) - ) - -BOOST_FUSION_ADAPT_STRUCT( - mapnik::topojson::linestring, - (mapnik::topojson::index_type, ring) - (boost::optional, props) - ) - -BOOST_FUSION_ADAPT_STRUCT( - mapnik::topojson::multi_linestring, - (std::vector, rings) - (boost::optional, props) - ) - -BOOST_FUSION_ADAPT_STRUCT( - mapnik::topojson::polygon, - (std::vector >, rings) - (boost::optional, props) - ) - -BOOST_FUSION_ADAPT_STRUCT( - mapnik::topojson::multi_polygon, - (std::vector > >, polygons) - (boost::optional, props) - ) - BOOST_FUSION_ADAPT_STRUCT( mapnik::topojson::topology, (std::vector, geometries) @@ -101,6 +65,8 @@ BOOST_FUSION_ADAPT_STRUCT( (boost::optional, bbox) ) + + namespace mapnik { namespace topojson { namespace qi = boost::spirit::qi; @@ -121,30 +87,43 @@ topojson_grammar::topojson_grammar() qi::_3_type _3; qi::_4_type _4; qi::_r1_type _r1; + qi::_a_type _a; + qi::_b_type _b; + qi::_c_type _c; + qi::_d_type _d; using qi::fail; using qi::on_error; using phoenix::push_back; using phoenix::construct; + geometry_type_dispatch.add + ("\"Point\"",1) + ("\"LineString\"",2) + ("\"Polygon\"",3) + ("\"MultiPoint\"",4) + ("\"MultiLineString\"",5) + ("\"MultiPolygon\"",6) + ("\"GeometryCollection\"",7) + ; + // error handler boost::phoenix::function const error_handler; - + boost::phoenix::function const create_geometry; // generic JSON types json.value = json.object | json.array | json.string_ | json.number ; - json.pairs = json.key_value % lit(',') + json.key_value = json.string_ > lit(':') > json.value ; - json.key_value = (json.string_ >> lit(':') >> json.value) - ; - - json.object = lit('{') >> *json.pairs >> lit('}') + json.object = lit('{') + > -(json.key_value % lit(',')) + > lit('}') ; json.array = lit('[') - >> json.value >> *(lit(',') >> json.value) - >> lit(']') + > -(json.value % lit(',')) + > lit(']') ; json.number = json.strict_double[_val = json.double_converter(_1)] @@ -181,101 +160,58 @@ topojson_grammar::topojson_grammar() >> lit('{') >> -((omit[json.string_] >> lit(':') - >> (geometry_collection(_val) | geometry)) % lit(',')) + >> (geometry_collection(_val) | geometry[push_back(_val, _1)]) % lit(','))) >> lit('}') ; - geometry = - point | - linestring | - polygon | - multi_point | - multi_linestring | - multi_polygon | - omit[json.object] + geometry = lit('{')[_a = 0] + > ((lit("\"type\"") > lit(':') > geometry_type_dispatch[_a = _1]) + | + (lit("\"coordinates\"") > lit(':') > coordinates[_b = _1]) + | + (lit("\"arcs\"") > lit(':') > rings_array[_c = _1]) + | + properties_[_d = _1] + | + json.key_value) % lit(',') + > lit('}')[_val = create_geometry(_a, _b, _c, _d)] ; + geometry_collection = lit('{') - >> lit("\"type\"") >> lit(':') >> lit("\"GeometryCollection\"") - >> -(lit(',') >> omit[bbox]) - >> lit(',') >> lit("\"geometries\"") >> lit(':') >> lit('[') >> -(geometry[push_back(_r1, _1)] % lit(',')) - >> lit(']') - >> lit('}') - ; - point = lit('{') - >> lit("\"type\"") >> lit(':') >> lit("\"Point\"") - >> -(lit(',') >> omit[bbox]) - >> ((lit(',') >> lit("\"coordinates\"") >> lit(':') >> coordinate) - ^ (lit(',') >> properties) /*^ (lit(',') >> omit[id])*/) + >> lit("\"type\"") >> lit(':') >> lit("\"GeometryCollection\"") + >> lit(',') >> lit("\"geometries\"") >> lit(':') + >> lit('[') + >> -(geometry[push_back(_r1, _1)] % lit(',')) + >> lit(']') >> lit('}') ; - multi_point = lit('{') - >> lit("\"type\"") >> lit(':') >> lit("\"MultiPoint\"") - >> -(lit(',') >> omit[bbox]) - >> ((lit(',') >> lit("\"coordinates\"") >> lit(':') - >> lit('[') >> -(coordinate % lit(',')) >> lit(']')) - ^ (lit(',') >> properties) ^ (lit(',') >> omit[id])) - >> lit('}') - ; - - linestring = lit('{') - >> lit("\"type\"") >> lit(':') >> lit("\"LineString\"") - >> ((lit(',') >> lit("\"arcs\"") >> lit(':') >> lit('[') >> int_ >> lit(']')) - ^ (lit(',') >> properties) ^ (lit(',') >> omit[id])) - >> lit('}') - ; - - multi_linestring = lit('{') - >> lit("\"type\"") >> lit(':') >> lit("\"MultiLineString\"") - >> -(lit(',') >> omit[bbox]) - >> ((lit(',') >> lit("\"arcs\"") >> lit(':') >> lit('[') - >> -((lit('[') >> int_ >> lit(']')) % lit(',')) >> lit(']')) - ^ (lit(',') >> properties) ^ (lit(',') >> omit[id])) - >> lit('}') - ; - - polygon = lit('{') - >> lit("\"type\"") >> lit(':') >> lit("\"Polygon\"") - >> -(lit(',') >> omit[bbox]) - >> ((lit(',') >> lit("\"arcs\"") >> lit(':') - >> lit('[') >> -(ring % lit(',')) >> lit(']')) - ^ (lit(',') >> properties) ^ (lit(',') >> omit[id])) - >> lit('}') - ; - - multi_polygon = lit('{') - >> lit("\"type\"") >> lit(':') >> lit("\"MultiPolygon\"") - >> -(lit(',') >> omit[bbox]) - >> ((lit(',') >> lit("\"arcs\"") >> lit(':') - >> lit('[') - >> -((lit('[') >> -(ring % lit(',')) >> lit(']')) % lit(',')) - >> lit(']')) ^ (lit(',') >> properties) ^ (lit(',') >> omit[id])) - >> lit('}') - ; - - id = lit("\"id\"") >> lit(':') >> omit[json.value] - ; - ring = lit('[') >> -(int_ % lit(',')) >> lit(']') ; + rings = lit('[') >> -(ring % lit(',')) >> lit(']') + ; + rings_array = lit('[') >> -(rings % lit(',')) >> lit(']') + | + rings + | + ring + ; - properties = lit("\"properties\"") + properties_ = lit("\"properties\"") >> lit(':') - >> (( lit('{') >> attributes >> lit('}')) | json.object) + >> lit('{') >> (json.string_ >> lit(':') >> json.value) % lit(',') >> lit('}') ; - attributes = (json.string_ >> lit(':') >> attribute_value) % lit(',') - ; - - attribute_value %= json.number | json.string_ ; - arcs = lit("\"arcs\"") >> lit(':') >> lit('[') >> -( arc % lit(',')) >> lit(']') ; - arc = lit('[') >> -(coordinate % lit(',')) >> lit(']') ; + arc = lit('[') >> -(coordinate_ % lit(',')) >> lit(']') ; - coordinate = lit('[') >> double_ >> lit(',') >> double_ >> lit(']'); + coordinate_ = lit('[') > double_ > lit(',') > double_ > lit(']'); + + coordinates = (lit('[') >> coordinate_ % lit(',') > lit(']')) + | coordinate_; topology.name("topology"); transform.name("transform"); @@ -283,13 +219,9 @@ topojson_grammar::topojson_grammar() arc.name("arc"); arcs.name("arcs"); json.value.name("value"); - coordinate.name("coordinate"); - - point.name("point"); - multi_point.name("multi_point"); - linestring.name("linestring"); - polygon.name("polygon"); - multi_polygon.name("multi_polygon"); + coordinate_.name("coordinate"); + geometry.name("geometry"); + properties_.name("properties"); geometry_collection.name("geometry_collection"); // error handler on_error(topology, error_handler(_1, _2, _3, _4)); diff --git a/include/mapnik/json/topojson_utils.hpp b/include/mapnik/json/topojson_utils.hpp index c4de30683..40a2c91ff 100644 --- a/include/mapnik/json/topojson_utils.hpp +++ b/include/mapnik/json/topojson_utils.hpp @@ -2,7 +2,7 @@ * * This file is part of Mapnik (c++ mapping toolkit) * - * Copyright (C) 2015 Artem Pavlenko + * Copyright (C) 2016 Artem Pavlenko * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -39,6 +40,11 @@ struct bounding_box_visitor : topo_(topo), num_arcs_(topo_.arcs.size()) {} + box2d operator() (mapnik::topojson::empty const&) const + { + return box2d(); + } + box2d operator() (mapnik::topojson::point const& pt) const { double x = pt.coord.x; @@ -81,50 +87,15 @@ struct bounding_box_visitor box2d operator() (mapnik::topojson::linestring const& line) const { box2d bbox; + bool first = true; if (num_arcs_ > 0) { - index_type index = line.ring; - index_type arc_index = index < 0 ? std::abs(index) - 1 : index; - if (arc_index >= 0 && arc_index < static_cast(num_arcs_)) - { - bool first = true; - double px = 0, py = 0; - auto const& arcs = topo_.arcs[arc_index]; - for (auto pt : arcs.coordinates) - { - double x = pt.x; - double y = pt.y; - if (topo_.tr) - { - x = (px += x) * (*topo_.tr).scale_x + (*topo_.tr).translate_x; - y = (py += y) * (*topo_.tr).scale_y + (*topo_.tr).translate_y; - } - if (first) - { - first = false; - bbox.init(x, y, x, y); - } - else - { - bbox.expand_to_include(x, y); - } - } - } - } - return bbox; - } - - box2d operator() (mapnik::topojson::multi_linestring const& multi_line) const - { - box2d bbox; - if (num_arcs_ > 0) - { - bool first = true; - for (auto index : multi_line.rings) + for (auto index : line.rings) { index_type arc_index = index < 0 ? std::abs(index) - 1 : index; if (arc_index >= 0 && arc_index < static_cast(num_arcs_)) { + double px = 0, py = 0; auto const& arcs = topo_.arcs[arc_index]; for (auto pt : arcs.coordinates) @@ -152,6 +123,47 @@ struct bounding_box_visitor return bbox; } + box2d operator() (mapnik::topojson::multi_linestring const& multi_line) const + { + box2d bbox; + if (num_arcs_ > 0) + { + bool first = true; + for (auto const& line : multi_line.lines) + { + for (auto index : line) + { + index_type arc_index = index < 0 ? std::abs(index) - 1 : index; + if (arc_index >= 0 && arc_index < static_cast(num_arcs_)) + { + double px = 0, py = 0; + auto const& arcs = topo_.arcs[arc_index]; + for (auto pt : arcs.coordinates) + { + double x = pt.x; + double y = pt.y; + if (topo_.tr) + { + x = (px += x) * (*topo_.tr).scale_x + (*topo_.tr).translate_x; + y = (py += y) * (*topo_.tr).scale_y + (*topo_.tr).translate_y; + } + if (first) + { + first = false; + bbox.init(x, y, x, y); + } + else + { + bbox.expand_to_include(x, y); + } + } + } + } + } + } + return bbox; + } + box2d operator() (mapnik::topojson::polygon const& poly) const { box2d bbox; @@ -246,26 +258,6 @@ private: }; namespace { -struct attribute_value_visitor - -{ -public: - attribute_value_visitor(mapnik::transcoder const& tr) - : tr_(tr) {} - - mapnik::value operator()(std::string const& val) const - { - return mapnik::value(tr_.transcode(val.c_str())); - } - - template - mapnik::value operator()(T const& val) const - { - return mapnik::value(val); - } - - mapnik::transcoder const& tr_; -}; template void assign_properties(mapnik::feature_impl & feature, T const& geom, mapnik::transcoder const& tr) @@ -274,7 +266,7 @@ void assign_properties(mapnik::feature_impl & feature, T const& geom, mapnik::tr { for (auto const& p : *geom.props) { - feature.put_new(std::get<0>(p), mapnik::util::apply_visitor(attribute_value_visitor(tr),std::get<1>(p))); + feature.put_new(std::get<0>(p), mapnik::util::apply_visitor(mapnik::json::attribute_value_visitor(tr),std::get<1>(p))); } } } @@ -333,29 +325,31 @@ struct feature_generator mapnik::feature_ptr feature(mapnik::feature_factory::create(ctx_,feature_id_)); if (num_arcs_ > 0) { - index_type index = line.ring; - index_type arc_index = index < 0 ? std::abs(index) - 1 : index; - if (arc_index >= 0 && arc_index < static_cast(num_arcs_)) - { - auto const& arcs = topo_.arcs[arc_index]; - double px = 0, py = 0; - mapnik::geometry::line_string line_string; - line_string.reserve(arcs.coordinates.size()); + mapnik::geometry::line_string line_string; - for (auto pt : arcs.coordinates) + for (auto index : line.rings) + { + index_type arc_index = index < 0 ? std::abs(index) - 1 : index; + if (arc_index >= 0 && arc_index < static_cast(num_arcs_)) { - double x = pt.x; - double y = pt.y; - if (topo_.tr) + auto const& arcs = topo_.arcs[arc_index]; + double px = 0, py = 0; + line_string.reserve(line_string.size() + arcs.coordinates.size()); + for (auto pt : arcs.coordinates) { - x = (px += x) * (*topo_.tr).scale_x + (*topo_.tr).translate_x; - y = (py += y) * (*topo_.tr).scale_y + (*topo_.tr).translate_y; + double x = pt.x; + double y = pt.y; + if (topo_.tr) + { + x = (px += x) * (*topo_.tr).scale_x + (*topo_.tr).translate_x; + y = (py += y) * (*topo_.tr).scale_y + (*topo_.tr).translate_y; + } + line_string.add_coord(x,y); } - line_string.add_coord(x,y); } - feature->set_geometry(std::move(line_string)); - assign_properties(*feature, line, tr_); } + feature->set_geometry(std::move(line_string)); + assign_properties(*feature, line, tr_); } return feature; } @@ -367,30 +361,34 @@ struct feature_generator { mapnik::geometry::multi_line_string multi_line_string; bool hit = false; - multi_line_string.reserve(multi_line.rings.size()); - for (auto const& index : multi_line.rings) + for (auto const& line : multi_line.lines) { - index_type arc_index = index < 0 ? std::abs(index) - 1 : index; - if (arc_index >= 0 && arc_index < static_cast(num_arcs_)) + multi_line_string.reserve(multi_line_string.size() + line.size()); + mapnik::geometry::line_string line_string; + for (auto index : line) { - hit = true; - double px = 0, py = 0; - mapnik::geometry::line_string line_string; - auto const& arcs = topo_.arcs[arc_index]; - line_string.reserve(arcs.coordinates.size()); - for (auto pt : arcs.coordinates) + index_type arc_index = index < 0 ? std::abs(index) - 1 : index; + if (arc_index >= 0 && arc_index < static_cast(num_arcs_)) { - double x = pt.x; - double y = pt.y; - if (topo_.tr) + hit = true; + double px = 0, py = 0; + auto const& arcs = topo_.arcs[arc_index]; + line_string.reserve(line_string.size() + arcs.coordinates.size()); + for (auto pt : arcs.coordinates) { - x = (px += x) * (*topo_.tr).scale_x + (*topo_.tr).translate_x; - y = (py += y) * (*topo_.tr).scale_y + (*topo_.tr).translate_y; + double x = pt.x; + double y = pt.y; + if (topo_.tr) + { + x = (px += x) * (*topo_.tr).scale_x + (*topo_.tr).translate_x; + y = (py += y) * (*topo_.tr).scale_y + (*topo_.tr).translate_y; + } + line_string.add_coord(x, y); } - line_string.add_coord(x, y); + } - multi_line_string.push_back(std::move(line_string)); } + multi_line_string.push_back(std::move(line_string)); } if (hit) { @@ -439,7 +437,7 @@ struct feature_generator } processed_coords.emplace_back(coordinate{x,y}); } - + linear_ring.reserve(linear_ring.size() + processed_coords.size()); if (reverse) { for (auto const& c : processed_coords | boost::adaptors::reversed) @@ -513,14 +511,15 @@ struct feature_generator if (topo_.tr) { - x = (px += x) * (*topo_.tr).scale_x + (*topo_.tr).translate_x; - y = (py += y) * (*topo_.tr).scale_y + (*topo_.tr).translate_y; + transform const& tr = *topo_.tr; + x = (px += x) * tr.scale_x + tr.translate_x; + y = (py += y) * tr.scale_y + tr.translate_y; } processed_coords.emplace_back(coordinate{x,y}); } using namespace boost::adaptors; - + linear_ring.reserve(linear_ring.size() + processed_coords.size()); if (reverse) { for (auto const& c : (processed_coords | reversed)) diff --git a/include/mapnik/json/topology.hpp b/include/mapnik/json/topology.hpp index 05f35b4ad..048c85528 100644 --- a/include/mapnik/json/topology.hpp +++ b/include/mapnik/json/topology.hpp @@ -62,13 +62,13 @@ struct multi_point struct linestring { - index_type ring ; + std::vector rings ; boost::optional props; }; struct multi_linestring { - std::vector rings; + std::vector > lines; boost::optional props; }; @@ -84,7 +84,10 @@ struct multi_polygon boost::optional props; }; -using geometry = util::variant(sym, keys::image_transform); - if (image_transform) evaluate_transform(tr, feature, common.vars_, *image_transform); + if (image_transform) evaluate_transform(tr, feature, common.vars_, *image_transform, common.scale_factor_); agg::trans_affine_translation recenter(-center.x, -center.y); agg::trans_affine recenter_tr = recenter * tr; diff --git a/include/mapnik/symbolizer_base.hpp b/include/mapnik/symbolizer_base.hpp index 8ac446265..fc88c579c 100644 --- a/include/mapnik/symbolizer_base.hpp +++ b/include/mapnik/symbolizer_base.hpp @@ -58,7 +58,7 @@ MAPNIK_DECL void evaluate_transform(agg::trans_affine& tr, feature_impl const& feature, attributes const& vars, transform_type const& trans_expr, - double scale_factor=1.0); + double scale_factor); struct enumeration_wrapper { diff --git a/include/mapnik/text/symbolizer_helpers.hpp b/include/mapnik/text/symbolizer_helpers.hpp index f4a56387d..83a3c1939 100644 --- a/include/mapnik/text/symbolizer_helpers.hpp +++ b/include/mapnik/text/symbolizer_helpers.hpp @@ -163,6 +163,10 @@ protected: void init_marker() const; }; -} //namespace +namespace geometry { +MAPNIK_DECL mapnik::box2d envelope(mapnik::base_symbolizer_helper::geometry_cref const& geom); +} + +} //namespace mapnik #endif // SYMBOLIZER_HELPERS_HPP diff --git a/include/mapnik/util/variant.hpp b/include/mapnik/util/variant.hpp index 05dddecf8..3fb00b388 100644 --- a/include/mapnik/util/variant.hpp +++ b/include/mapnik/util/variant.hpp @@ -24,7 +24,7 @@ #define MAPNIK_UTIL_VARIANT_HPP #include -#include +#include #pragma GCC diagnostic push #include diff --git a/include/mapnik/warning_ignore.hpp b/include/mapnik/warning_ignore.hpp index 62b789997..95249b8bf 100644 --- a/include/mapnik/warning_ignore.hpp +++ b/include/mapnik/warning_ignore.hpp @@ -23,6 +23,7 @@ #pragma GCC diagnostic ignored "-Wunknown-pragmas" // clang+gcc #pragma GCC diagnostic ignored "-Wpragmas" // gcc +#pragma GCC diagnostic ignored "-W#pragma-messages" #pragma GCC diagnostic ignored "-Wunsequenced" #pragma GCC diagnostic ignored "-Wunused-function" #pragma GCC diagnostic ignored "-Wunused-parameter" diff --git a/localize.sh b/localize.sh index 4b69c2f6b..3d034f508 100755 --- a/localize.sh +++ b/localize.sh @@ -1,10 +1,12 @@ #!/bin/bash + # TODO - use rpath to avoid needing this to run tests locally + export CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -if [ $(uname -s) = 'Darwin' ]; then - export DYLD_LIBRARY_PATH="${CURRENT_DIR}/src/":${DYLD_LIBRARY_PATH} -else - export LD_LIBRARY_PATH="${CURRENT_DIR}/src/":${LD_LIBRARY_PATH} +if [[ $(uname -s) == 'Darwin' ]]; then + export DYLD_LIBRARY_PATH="${CURRENT_DIR}/src/":${DYLD_LIBRARY_PATH:-""} +elif [[ $(uname -s) == 'Linux' ]]; then + export LD_LIBRARY_PATH="${CURRENT_DIR}/src/":${LD_LIBRARY_PATH:-""} fi export PATH=$(pwd)/utils/mapnik-render/:${PATH} diff --git a/plugins/input/gdal/gdal_featureset.cpp b/plugins/input/gdal/gdal_featureset.cpp index 2a27d35cb..1377322e3 100644 --- a/plugins/input/gdal/gdal_featureset.cpp +++ b/plugins/input/gdal/gdal_featureset.cpp @@ -44,6 +44,41 @@ using mapnik::view_transform; using mapnik::datasource_exception; using mapnik::feature_factory; +#ifdef MAPNIK_LOG +namespace { + +void get_overview_meta(GDALRasterBand* band) +{ + int band_overviews = band->GetOverviewCount(); + if (band_overviews > 0) + { + MAPNIK_LOG_DEBUG(gdal) << "gdal_featureset: " << band_overviews << " overviews found!"; + + for (int b = 0; b < band_overviews; b++) + { + GDALRasterBand * overview = band->GetOverview(b); + MAPNIK_LOG_DEBUG(gdal) << "Overview= " << b + << " Width=" << overview->GetXSize() + << " Height=" << overview->GetYSize(); + } + } + else + { + MAPNIK_LOG_DEBUG(gdal) << "gdal_featureset: No overviews found!"; + } + + int bsx,bsy; + double scale; + band->GetBlockSize(&bsx, &bsy); + scale = band->GetScale(); + + MAPNIK_LOG_DEBUG(gdal) << "Block=" << bsx << "x" << bsy + << " Scale=" << scale + << " Type=" << GDALGetDataTypeName(band->GetRasterDataType()) + << "Color=" << GDALGetColorInterpretationName(band->GetColorInterpretation()); +} +} // anonymous ns +#endif gdal_featureset::gdal_featureset(GDALDataset& dataset, int band, gdal_query q, @@ -551,6 +586,32 @@ feature_ptr gdal_featureset::get_feature(mapnik::query const& q) MAPNIK_LOG_WARN(gdal) << "warning: nodata value (" << raster_nodata << ") used to set transparency instead of alpha band"; } } + else if( dataset_.GetRasterCount() > 0 && dataset_.GetRasterBand(1) ) + { + // Check if we have a non-alpha mask band (for example a TIFF internal mask) + int flags = dataset_.GetRasterBand(1)->GetMaskFlags(); + GDALRasterBand* mask = 0; + if (flags == GMF_PER_DATASET) + { + mask = dataset_.GetRasterBand(1)->GetMaskBand(); + } + if (mask) + { + MAPNIK_LOG_DEBUG(gdal) << "gdal_featureset: found and processing mask band..."; + if (!raster_has_nodata) + { + raster_io_error = mask->RasterIO(GF_Read, x_off, y_off, width, height, image.bytes() + 3, + image.width(), image.height(), GDT_Byte, 4, 4 * image.width()); + if (raster_io_error == CE_Failure) { + throw datasource_exception(CPLGetLastErrorMsg()); + } + } + else + { + MAPNIK_LOG_WARN(gdal) << "warning: nodata value (" << raster_nodata << ") used to set transparency instead of mask band"; + } + } + } mapnik::raster_ptr raster = std::make_shared(intersect, image, filter_factor); // set nodata value to be used in raster colorizer if (nodata_value_) raster->set_nodata(*nodata_value_); @@ -620,36 +681,3 @@ feature_ptr gdal_featureset::get_feature_at_point(mapnik::coord2d const& pt) } return feature_ptr(); } - -#ifdef MAPNIK_LOG -void gdal_featureset::get_overview_meta(GDALRasterBand* band) -{ - int band_overviews = band->GetOverviewCount(); - if (band_overviews > 0) - { - MAPNIK_LOG_DEBUG(gdal) << "gdal_featureset: " << band_overviews << " overviews found!"; - - for (int b = 0; b < band_overviews; b++) - { - GDALRasterBand * overview = band->GetOverview(b); - MAPNIK_LOG_DEBUG(gdal) << "Overview= " << b - << " Width=" << overview->GetXSize() - << " Height=" << overview->GetYSize(); - } - } - else - { - MAPNIK_LOG_DEBUG(gdal) << "gdal_featureset: No overviews found!"; - } - - int bsx,bsy; - double scale; - band->GetBlockSize(&bsx, &bsy); - scale = band->GetScale(); - - MAPNIK_LOG_DEBUG(gdal) << "Block=" << bsx << "x" << bsy - << " Scale=" << scale - << " Type=" << GDALGetDataTypeName(band->GetRasterDataType()) - << "Color=" << GDALGetColorInterpretationName(band->GetColorInterpretation()); -} -#endif diff --git a/plugins/input/gdal/gdal_featureset.hpp b/plugins/input/gdal/gdal_featureset.hpp index 639028b65..f2f66f796 100644 --- a/plugins/input/gdal/gdal_featureset.hpp +++ b/plugins/input/gdal/gdal_featureset.hpp @@ -73,11 +73,6 @@ public: private: mapnik::feature_ptr get_feature(mapnik::query const& q); mapnik::feature_ptr get_feature_at_point(mapnik::coord2d const& p); - -#ifdef MAPNIK_LOG - void get_overview_meta(GDALRasterBand * band); -#endif - GDALDataset & dataset_; mapnik::context_ptr ctx_; int band_; diff --git a/plugins/input/pgraster/pgraster_datasource.cpp b/plugins/input/pgraster/pgraster_datasource.cpp index 5c49a43aa..9390f3ace 100644 --- a/plugins/input/pgraster/pgraster_datasource.cpp +++ b/plugins/input/pgraster/pgraster_datasource.cpp @@ -878,9 +878,10 @@ featureset_ptr pgraster_datasource::features_with_context(query const& q,process std::string table_with_bbox; std::string col = geometryColumn_; - if ( use_overviews_ ) { - std::string sch = schema_; - std::string tab = mapnik::sql_utils::unquote_double(raster_table_); + if ( use_overviews_ && !overviews_.empty()) { + std::string sch = overviews_[0].schema; + std::string tab = overviews_[0].table; + col = overviews_[0].column; const double scale = std::min(px_gw, px_gh); std::vector::const_reverse_iterator i; for (i=overviews_.rbegin(); i!=overviews_.rend(); ++i) { diff --git a/plugins/input/topojson/topojson_datasource.cpp b/plugins/input/topojson/topojson_datasource.cpp index 4c4e6b5df..502868562 100644 --- a/plugins/input/topojson/topojson_datasource.cpp +++ b/plugins/input/topojson/topojson_datasource.cpp @@ -66,18 +66,9 @@ struct attr_value_converter { return mapnik::Boolean; } - - mapnik::eAttributeType operator() (std::string const& /*val*/) const - { - return mapnik::String; - } - - mapnik::eAttributeType operator() (mapnik::value_unicode_string const& /*val*/) const - { - return mapnik::String; - } - - mapnik::eAttributeType operator() (mapnik::value_null const& /*val*/) const + // string, object, array + template + mapnik::eAttributeType operator() (T const& /*val*/) const { return mapnik::String; } @@ -109,6 +100,11 @@ struct geometry_type_visitor { return static_cast(mapnik::datasource_geometry_t::Polygon); } + template + int operator() (T const& ) const + { + return 0; + } }; struct collect_attributes_visitor @@ -117,6 +113,9 @@ struct collect_attributes_visitor collect_attributes_visitor(mapnik::layer_descriptor & desc): desc_(desc) {} + // no-op + void operator() (mapnik::topojson::empty) {} + // template void operator() (GeomType const& g) { diff --git a/scripts/travis-common.sh b/scripts/travis-common.sh index 15b38ddf5..40d18374e 100644 --- a/scripts/travis-common.sh +++ b/scripts/travis-common.sh @@ -62,7 +62,7 @@ commit_message_contains () { } commit_message_parse () { - if commit_message_contains '[skip tests]' || [[ $(uname -s) == 'Darwin' ]]; then + if commit_message_contains '[skip tests]'; then config_override "CPP_TESTS = False" fi if commit_message_contains '[skip utils]'; then @@ -92,8 +92,7 @@ configure () { coverage () { ./mason_packages/.link/bin/cpp-coveralls \ - --gcov /usr/bin/llvm-cov-${LLVM_VERSION} \ - --build-root . --gcov-options '\-lp' \ + --gcov ${LLVM_COV} \ --exclude mason_packages \ --exclude .sconf_temp --exclude benchmark --exclude deps \ --exclude scons --exclude test --exclude demo --exclude docs \ diff --git a/src/agg/process_line_pattern_symbolizer.cpp b/src/agg/process_line_pattern_symbolizer.cpp index 3b947a4cc..dbbb7f26d 100644 --- a/src/agg/process_line_pattern_symbolizer.cpp +++ b/src/agg/process_line_pattern_symbolizer.cpp @@ -92,7 +92,7 @@ struct agg_renderer_process_visitor_l value_double opacity = get(sym_, feature_, common_.vars_); agg::trans_affine image_tr = agg::trans_affine_scaling(common_.scale_factor_); auto image_transform = get_optional(sym_, keys::image_transform); - if (image_transform) evaluate_transform(image_tr, feature_, common_.vars_, *image_transform); + if (image_transform) evaluate_transform(image_tr, feature_, common_.vars_, *image_transform, common_.scale_factor_); mapnik::box2d const& bbox_image = marker.get_data()->bounding_box() * image_tr; image_rgba8 image(bbox_image.width(), bbox_image.height()); render_pattern(*ras_ptr_, marker, image_tr, 1.0, image); diff --git a/src/agg/process_polygon_pattern_symbolizer.cpp b/src/agg/process_polygon_pattern_symbolizer.cpp index e65488622..f2a0159d1 100644 --- a/src/agg/process_polygon_pattern_symbolizer.cpp +++ b/src/agg/process_polygon_pattern_symbolizer.cpp @@ -84,7 +84,7 @@ struct agg_renderer_process_visitor_p { agg::trans_affine image_tr = agg::trans_affine_scaling(common_.scale_factor_); auto image_transform = get_optional(sym_, keys::image_transform); - if (image_transform) evaluate_transform(image_tr, feature_, common_.vars_, *image_transform); + if (image_transform) evaluate_transform(image_tr, feature_, common_.vars_, *image_transform, common_.scale_factor_); mapnik::box2d const& bbox_image = marker.get_data()->bounding_box() * image_tr; mapnik::image_rgba8 image(bbox_image.width(), bbox_image.height()); render_pattern(*ras_ptr_, marker, image_tr, 1.0, image); diff --git a/src/agg/process_text_symbolizer.cpp b/src/agg/process_text_symbolizer.cpp index b3794bcf5..685b66c95 100644 --- a/src/agg/process_text_symbolizer.cpp +++ b/src/agg/process_text_symbolizer.cpp @@ -63,7 +63,7 @@ void agg_renderer::process(text_symbolizer const& sym, if (halo_transform) { agg::trans_affine halo_affine_transform; - evaluate_transform(halo_affine_transform, feature, common_.vars_, *halo_transform); + evaluate_transform(halo_affine_transform, feature, common_.vars_, *halo_transform, common_.scale_factor_); ren.set_halo_transform(halo_affine_transform); } diff --git a/src/cairo/process_line_pattern_symbolizer.cpp b/src/cairo/process_line_pattern_symbolizer.cpp index 7aa85ab41..f53d850a6 100644 --- a/src/cairo/process_line_pattern_symbolizer.cpp +++ b/src/cairo/process_line_pattern_symbolizer.cpp @@ -62,7 +62,7 @@ struct cairo_renderer_process_visitor_l mapnik::rasterizer ras; agg::trans_affine image_tr = agg::trans_affine_scaling(common_.scale_factor_); auto image_transform = get_optional(sym_, keys::image_transform); - if (image_transform) evaluate_transform(image_tr, feature_, common_.vars_, *image_transform); + if (image_transform) evaluate_transform(image_tr, feature_, common_.vars_, *image_transform, common_.scale_factor_); mapnik::box2d const& bbox_image = marker.get_data()->bounding_box() * image_tr; mapnik::image_rgba8 image(bbox_image.width(), bbox_image.height()); render_pattern(ras, marker, image_tr, 1.0, image); diff --git a/src/cairo/process_polygon_pattern_symbolizer.cpp b/src/cairo/process_polygon_pattern_symbolizer.cpp index c2b211fd8..4e21234b8 100644 --- a/src/cairo/process_polygon_pattern_symbolizer.cpp +++ b/src/cairo/process_polygon_pattern_symbolizer.cpp @@ -96,7 +96,7 @@ void cairo_renderer::process(polygon_pattern_symbolizer const& sym, value_double opacity = get(sym, feature, common_.vars_); agg::trans_affine image_tr = agg::trans_affine_scaling(common_.scale_factor_); auto image_transform = get_optional(sym, keys::image_transform); - if (image_transform) evaluate_transform(image_tr, feature, common_.vars_, *image_transform); + if (image_transform) evaluate_transform(image_tr, feature, common_.vars_, *image_transform, common_.scale_factor_); cairo_save_restore guard(context_); context_.set_operator(comp_op); diff --git a/src/geometry_envelope.cpp b/src/geometry_envelope.cpp index 00420fce7..bb79f59ad 100644 --- a/src/geometry_envelope.cpp +++ b/src/geometry_envelope.cpp @@ -38,8 +38,6 @@ template MAPNIK_DECL mapnik::box2d envelope(multi_line_string co template MAPNIK_DECL mapnik::box2d envelope(multi_polygon const& geom); // collection template MAPNIK_DECL mapnik::box2d envelope(geometry_collection const& geom); -// -template MAPNIK_DECL mapnik::box2d envelope(geometry const& geom); } // end ns geometry } // end ns mapnik diff --git a/src/grid/process_text_symbolizer.cpp b/src/grid/process_text_symbolizer.cpp index 7fe0ff377..d1b6a17b4 100644 --- a/src/grid/process_text_symbolizer.cpp +++ b/src/grid/process_text_symbolizer.cpp @@ -60,7 +60,7 @@ void grid_renderer::process(text_symbolizer const& sym, if (halo_transform) { agg::trans_affine halo_affine_transform; - evaluate_transform(halo_affine_transform, feature, common_.vars_, *halo_transform); + evaluate_transform(halo_affine_transform, feature, common_.vars_, *halo_transform, common_.scale_factor_); ren.set_halo_transform(halo_affine_transform); } diff --git a/src/marker_helpers.cpp b/src/marker_helpers.cpp index dc9f67980..eb0202169 100644 --- a/src/marker_helpers.cpp +++ b/src/marker_helpers.cpp @@ -37,6 +37,7 @@ void build_ellipse(symbolizer_base const& sym, mapnik::feature_impl & feature, a { double width = 0.0; double height = 0.0; + double half_stroke_width = 0.0; if (has_key(sym,keys::width) && has_key(sym,keys::height)) { width = get(sym, keys::width, feature, vars, 0.0); @@ -50,6 +51,10 @@ void build_ellipse(symbolizer_base const& sym, mapnik::feature_impl & feature, a { width = height = get(sym, keys::height, feature, vars, 0.0); } + if (has_key(sym,keys::stroke_width)) + { + half_stroke_width = get(sym, keys::stroke_width, feature, vars, 0.0) / 2.0; + } svg::svg_converter_type styled_svg(svg_path, marker_ellipse.attributes()); styled_svg.push_attr(); styled_svg.begin_path(); @@ -59,6 +64,10 @@ void build_ellipse(symbolizer_base const& sym, mapnik::feature_impl & feature, a styled_svg.pop_attr(); double lox,loy,hix,hiy; styled_svg.bounding_rect(&lox, &loy, &hix, &hiy); + lox -= half_stroke_width; + loy -= half_stroke_width; + hix += half_stroke_width; + hiy += half_stroke_width; styled_svg.set_dimensions(width,height); marker_ellipse.set_dimensions(width,height); marker_ellipse.set_bounding_box(lox,loy,hix,hiy); diff --git a/src/proj_transform.cpp b/src/proj_transform.cpp index efeb088f7..4761724de 100644 --- a/src/proj_transform.cpp +++ b/src/proj_transform.cpp @@ -200,20 +200,21 @@ bool proj_transform::backward (double * x, double * y , double * z, int point_co #ifdef MAPNIK_USE_PROJ4 if (is_dest_longlat_) { - int i; - for(i=0; i> & ls double * x = reinterpret_cast(ptr); double * y = x + 1; double * z = nullptr; - if(!backward(x, y, z, size, 2)) + if (!backward(x, y, z, size, 2)) { return size; } @@ -312,31 +313,24 @@ bool proj_transform::backward (box2d & box) const if (is_source_equal_dest_) return true; - double llx = box.minx(); - double ulx = box.minx(); - double lly = box.miny(); - double lry = box.miny(); - double lrx = box.maxx(); - double urx = box.maxx(); - double uly = box.maxy(); - double ury = box.maxy(); - double z = 0.0; - if (!backward(llx,lly,z)) + double x[4], y[4]; + x[0] = box.minx(); // llx 0 + y[0] = box.miny(); // lly 1 + x[1] = box.maxx(); // lrx 2 + y[1] = box.miny(); // lry 3 + x[2] = box.minx(); // ulx 4 + y[2] = box.maxy(); // uly 5 + x[3] = box.maxx(); // urx 6 + y[3] = box.maxy(); // ury 7 + + if (!backward(x, y, nullptr, 4, 1)) return false; - if (!backward(lrx,lry,z)) - return false; - if (!backward(ulx,uly,z)) - return false; - if (!backward(urx,ury,z)) - return false; - double minx = std::min(ulx, llx); - double miny = std::min(lly, lry); - double maxx = std::max(urx, lrx); - double maxy = std::max(ury, uly); - box.init(minx, - miny, - maxx, - maxy); + + double minx = std::min(x[0], x[2]); + double miny = std::min(y[0], y[1]); + double maxx = std::max(x[1], x[3]); + double maxy = std::max(y[2], y[3]); + box.init(minx, miny, maxx, maxy); return true; } @@ -425,7 +419,6 @@ bool proj_transform::backward(box2d& env, int points) const env.re_center(result.center().x, result.center().y); env.height(result.height()); env.width(result.width()); - return true; } diff --git a/src/renderer_common/render_markers_symbolizer.cpp b/src/renderer_common/render_markers_symbolizer.cpp index 2ecc51bd8..6706cb1cd 100644 --- a/src/renderer_common/render_markers_symbolizer.cpp +++ b/src/renderer_common/render_markers_symbolizer.cpp @@ -159,7 +159,7 @@ struct render_marker_symbolizer_visitor if (auto image_transform = get_optional(sym_, keys::image_transform)) { - evaluate_transform(image_tr, feature_, common_.vars_, *image_transform); + evaluate_transform(image_tr, feature_, common_.vars_, *image_transform, common_.scale_factor_); } vector_dispatch_type rasterizer_dispatch(marker_ptr, @@ -183,7 +183,7 @@ struct render_marker_symbolizer_visitor setup_transform_scaling(image_tr, mark.width(), mark.height(), feature_, common_.vars_, sym_); auto image_transform = get_optional(sym_, keys::image_transform); - if (image_transform) evaluate_transform(image_tr, feature_, common_.vars_, *image_transform); + if (image_transform) evaluate_transform(image_tr, feature_, common_.vars_, *image_transform, common_.scale_factor_); box2d const& bbox = mark.bounding_box(); mapnik::image_rgba8 const& marker = mark.get_data(); // - clamp sizes to > 4 pixels of interactivity diff --git a/src/text/symbolizer_helpers.cpp b/src/text/symbolizer_helpers.cpp index 741733073..9eae83cfa 100644 --- a/src/text/symbolizer_helpers.cpp +++ b/src/text/symbolizer_helpers.cpp @@ -465,7 +465,7 @@ void text_symbolizer_helper::init_marker() const if (marker->is()) return; agg::trans_affine trans; auto image_transform = get_optional(sym_, keys::image_transform); - if (image_transform) evaluate_transform(trans, feature_, vars_, *image_transform); + if (image_transform) evaluate_transform(trans, feature_, vars_, *image_transform, scale_factor_); double width = marker->width(); double height = marker->height(); double px0 = - 0.5 * width; diff --git a/src/text/text_line.cpp b/src/text/text_line.cpp index f228e5b93..2d0516731 100644 --- a/src/text/text_line.cpp +++ b/src/text/text_line.cpp @@ -51,7 +51,7 @@ text_line::text_line(text_line && rhs) void text_line::add_glyph(glyph_info && glyph, double scale_factor_) { - line_height_ = std::max(line_height_, glyph.line_height() + glyph.format->line_spacing); + line_height_ = std::max(line_height_, glyph.line_height() + glyph.format->line_spacing * scale_factor_); double advance = glyph.advance(); if (glyphs_.empty()) { diff --git a/src/xml_tree.cpp b/src/xml_tree.cpp index e342e6861..b14b8e4e0 100644 --- a/src/xml_tree.cpp +++ b/src/xml_tree.cpp @@ -72,8 +72,6 @@ DEFINE_NAME_TRAIT( int, "int") DEFINE_NAME_TRAIT( boolean_type, "boolean_type") #ifdef BIGINT DEFINE_NAME_TRAIT( mapnik::value_integer, "long long" ) -#else -DEFINE_NAME_TRAIT( mapnik::value_integer, "int" ) #endif DEFINE_NAME_TRAIT( std::string, "string" ) DEFINE_NAME_TRAIT( color, "color" ) @@ -416,7 +414,9 @@ compile_get_opt_attr(boolean_type); compile_get_opt_attr(std::string); compile_get_opt_attr(int); compile_get_opt_attr(unsigned); +#ifdef BIGINT compile_get_opt_attr(mapnik::value_integer); +#endif compile_get_opt_attr(float); compile_get_opt_attr(double); compile_get_opt_attr(color); diff --git a/test/data b/test/data index 6e4b7ff65..e74f1cef0 160000 --- a/test/data +++ b/test/data @@ -1 +1 @@ -Subproject commit 6e4b7ff65b6a1f0b34ae7cad52fe2c921ca9ea3f +Subproject commit e74f1cef09d579d6ff414cb56970adbe43b7a91b diff --git a/test/data-visual b/test/data-visual index 725bc22d2..5f0e6f866 160000 --- a/test/data-visual +++ b/test/data-visual @@ -1 +1 @@ -Subproject commit 725bc22d285ef5a558e2e241fd22ff5489f056ce +Subproject commit 5f0e6f86696a2a9a6733e42b1f400ba4ec2f8847 diff --git a/test/run b/test/run index f623609eb..54c4d6fb4 100755 --- a/test/run +++ b/test/run @@ -1,5 +1,8 @@ #!/usr/bin/env bash +set -o pipefail +set -eu + failures=0 cd "$( dirname "${BASH_SOURCE[0]}" )" @@ -18,9 +21,8 @@ if [ -d "test/data" ]; then run_substep "Running C++ Unit tests..." if [[ -f ./test/unit/run ]]; then - ./test/unit/run - failures=$((failures+$?)) ran_a_test=true + ./test/unit/run || failures=$((failures+$?)) else run_warn "Skipping unit tests since they were not built" fi @@ -31,8 +33,7 @@ if [ -d "test/data" ]; then for FILE in test/standalone/*-bin; do found_test=true ran_a_test=true - ${FILE}; - failures=$((failures+$?)) + ${FILE} || failures=$((failures+$?)) done fi if [[ $found_test == false ]]; then @@ -45,9 +46,8 @@ if [ -d "test/data" ]; then JOBS=1 fi if [[ -f ./test/visual/run ]]; then - ./test/visual/run -j $JOBS ran_a_test=true - failures=$((failures+$?)) + ./test/visual/run -j $JOBS || failures=$((failures+$?)) else run_warn "Skipping visual tests since they were not built" fi diff --git a/test/unit/datasource/ds_test_util.hpp b/test/unit/datasource/ds_test_util.hpp index 824aa8193..dd2e4a242 100644 --- a/test/unit/datasource/ds_test_util.hpp +++ b/test/unit/datasource/ds_test_util.hpp @@ -106,13 +106,18 @@ inline std::size_t count_features(mapnik::featureset_ptr features) { } using attr = std::tuple; + +#define REQUIRE_ATTRIBUTES(feature, attrs) \ + REQUIRE(bool(feature)); \ + for (auto const &kv : attrs) { \ + REQUIRE(feature->has_key(std::get<0>(kv))); \ + CHECK(feature->get(std::get<0>(kv)) == std::get<1>(kv)); \ + } \ + + inline void require_attributes(mapnik::feature_ptr feature, std::initializer_list const &attrs) { - REQUIRE(bool(feature)); - for (auto const &kv : attrs) { - REQUIRE(feature->has_key(std::get<0>(kv))); - CHECK(feature->get(std::get<0>(kv)) == std::get<1>(kv)); - } + REQUIRE_ATTRIBUTES(feature, attrs); } namespace detail { diff --git a/test/unit/datasource/geojson.cpp b/test/unit/datasource/geojson.cpp index 0b0f2052a..1edeb4b55 100644 --- a/test/unit/datasource/geojson.cpp +++ b/test/unit/datasource/geojson.cpp @@ -23,6 +23,7 @@ #include "catch.hpp" #include "ds_test_util.hpp" +#include #include #include #include @@ -654,5 +655,72 @@ TEST_CASE("geojson") { } } } + + SECTION("GeoJSON properties are properly expressed") + { + mapnik::transcoder tr("utf8"); + mapnik::parameters params; + params["type"] = "geojson"; + + std::string filename("./test/data/json/escaped.geojson"); + params["file"] = filename; + + // cleanup in the case of a failed previous run + if (mapnik::util::exists(filename + ".index")) + { + mapnik::util::remove(filename + ".index"); + } + + for (auto create_index : { true, false }) + { + if (create_index) + { + CHECK(!mapnik::util::exists(filename + ".index")); + int ret = create_disk_index(filename); + int ret_posix = (ret >> 8) & 0x000000ff; + INFO(ret); + INFO(ret_posix); + CHECK(mapnik::util::exists(filename + ".index")); + } + + for (auto cache_features : {true, false}) + { + params["cache_features"] = cache_features; + auto ds = mapnik::datasource_cache::instance().create(params); + REQUIRE(bool(ds)); + auto fields = ds->get_descriptor().get_descriptors(); + std::initializer_list names = {"NOM_FR","array","boolean","description","double","empty_array", "empty_object","int","name","object","spaces"}; + REQUIRE_FIELD_NAMES(fields, names); + + auto fs = all_features(ds); + REQUIRE(bool(fs)); + std::initializer_list attrs = { + attr{"name", tr.transcode("Test")}, + attr{"NOM_FR", tr.transcode("Québec")}, + attr{"boolean", mapnik::value_bool("true")}, + attr{"description", tr.transcode("Test: \u005C")}, + attr{"double", mapnik::value_double(1.1)}, + attr{"int", mapnik::value_integer(1)}, + attr{"object", tr.transcode("{name:\"waka\",spaces:\"value with spaces\",int:1,double:1.1,boolean:false" + ",NOM_FR:\"Québec\",array:[\"string\",\"value with spaces\",3,1.1,null,true" + ",\"Québec\"],another_object:{name:\"nested object\"}}")}, + attr{"spaces", tr.transcode("this has spaces")}, + attr{"array", tr.transcode("[\"string\",\"value with spaces\",3,1.1,null,true," + "\"Québec\",{name:\"object within an array\"}," + "[\"array\",\"within\",\"an\",\"array\"]]")}, + attr{"empty_array", tr.transcode("[]")}, + attr{"empty_object", tr.transcode("{}")}, + }; + auto feature = fs->next(); + REQUIRE(bool(feature)); + REQUIRE_ATTRIBUTES(feature, attrs); + } + // cleanup + if (create_index && mapnik::util::exists(filename + ".index")) + { + mapnik::util::remove(filename + ".index"); + } + } + } } } diff --git a/test/unit/datasource/postgis.cpp b/test/unit/datasource/postgis.cpp index 268995216..95ee65851 100644 --- a/test/unit/datasource/postgis.cpp +++ b/test/unit/datasource/postgis.cpp @@ -29,13 +29,15 @@ #include /* -Compile and run just this test: -clang++ -o test-postgis -g -I./test/ test/unit/run.cpp test/unit/datasource/postgis.cpp `mapnik-config --all-flags` && ./test-postgis -d yes + Compile and run just this test: + clang++ -o test-postgis -g -I./test/ test/unit/run.cpp test/unit/datasource/postgis.cpp `mapnik-config --all-flags` && ./test-postgis -d yes */ #include -int run(std::string const& command, bool okay_to_fail = false) +namespace { + +bool run(std::string const& command, bool okay_to_fail = false) { std::string cmd; if (std::getenv("DYLD_LIBRARY_PATH") != nullptr) @@ -61,251 +63,278 @@ int run(std::string const& command, bool okay_to_fail = false) return worked; } -std::string dbname("mapnik-tmp-postgis-test-db"); + +std::string const dbname("mapnik-tmp-postgis-test-db"); +bool status = false; + +bool ping_postmaster() +{ + return (run("psql --version") + && run("dropdb --if-exists " + dbname) + && run("createdb -T template_postgis " + dbname)); +} + +} TEST_CASE("postgis") { - SECTION("Postgis data initialization") + SECTION("Ping Postmaster (check if server is runnging and accessible") { - //don't add 'true' here, to get error message, when drop fails. If it works nothing is output - REQUIRE(run("dropdb --if-exists " + dbname)); - REQUIRE(run("createdb -T template_postgis " + dbname)); - //REQUIRE(run("createdb " + dbname)); - // Breaks when raster support is missing (unfortunately this is common) - //REQUIRE(run("psql -c 'CREATE EXTENSION postgis;' " + dbname, true)); - REQUIRE(run("psql -q -f ./test/data/sql/postgis-create-db-and-tables.sql " + dbname)); + if (!ping_postmaster()) + { + WARN("Can't run postgis.input tests - check postmaster is running and accessible"); + return; + } + else + { + status = true; + } } - - mapnik::parameters base_params; - base_params["type"] = "postgis"; - base_params["dbname"] = dbname; - - SECTION("Postgis should throw without 'table' parameter") + if (status) { - mapnik::parameters params(base_params); - CHECK_THROWS(mapnik::datasource_cache::instance().create(params)); - } - - SECTION("Postgis should throw with 'max_async_connection' greater than 'max_size'") - { - mapnik::parameters params(base_params); - params["table"] = "test"; - params["max_async_connection"] = "2"; - params["max_size"] = "1"; - CHECK_THROWS(mapnik::datasource_cache::instance().create(params)); - } - - SECTION("Postgis should throw with invalid metadata query") - { - mapnik::parameters params(base_params); - params["table"] = "does_not_exist"; - CHECK_THROWS(mapnik::datasource_cache::instance().create(params)); - } - - SECTION("Postgis should throw with invalid key field") - { - mapnik::parameters params(base_params); - params["table"] = "test_invalid_id"; - params["key_field"] = "id"; - CHECK_THROWS(mapnik::datasource_cache::instance().create(params)); - } - - SECTION("Postgis should throw with multicolumn primary key") - { - mapnik::parameters params(base_params); - params["table"] = "test_invalid_multi_col_pk"; - params["autodetect_key_field"] = "true"; - CHECK_THROWS(mapnik::datasource_cache::instance().create(params)); - } - - SECTION("Postgis should throw without geom column") - { - mapnik::parameters params(base_params); - params["table"] = "test_no_geom_col"; - auto ds = mapnik::datasource_cache::instance().create(params); - REQUIRE(ds != nullptr); - CHECK_THROWS(all_features(ds)); - } - - SECTION("Postgis should throw with invalid credentials") - { - mapnik::parameters params(base_params); - params["table"] = "test"; - params["user"] = "not_a_valid_user"; - params["password"] = "not_a_valid_pwd"; - CHECK_THROWS(mapnik::datasource_cache::instance().create(params)); - } - - SECTION("Postgis initialize dataset with persist_connection, schema, extent, geometry field, autodectect key field, simplify_geometries, row_limit") - { - mapnik::parameters params(base_params); - params["persist_connection"] = "false"; - params["table"] = "public.test"; - params["geometry_field"] = "geom"; - params["autodetect_key_field"] = "true"; - params["extent"] = "-1 -1, -1 2, 4 3, 3 -1, -1 -1"; - params["simplify_geometries"] = "true"; - params["row_limit"] = "1"; - auto ds = mapnik::datasource_cache::instance().create(params); - } - - SECTION("Postgis dataset geometry type") - { - mapnik::parameters params(base_params); - params["table"] = "(SELECT * FROM test WHERE gid=1) as data"; - auto ds = mapnik::datasource_cache::instance().create(params); - REQUIRE(ds != nullptr); - CHECK(ds->get_geometry_type() == mapnik::datasource_geometry_t::Point); - } - - SECTION("Postgis query field names") - { - mapnik::parameters params(base_params); - params["table"] = "test"; - auto ds = mapnik::datasource_cache::instance().create(params); - REQUIRE(ds != nullptr); - REQUIRE(ds->type() == mapnik::datasource::datasource_t::Vector); - auto fields = ds->get_descriptor().get_descriptors(); - require_field_names(fields, { "gid", "colbigint", "col_text", "col-char", "col+bool", "colnumeric", "colsmallint", "colfloat4", "colfloat8", "colcharacter" }); - require_field_types(fields, { mapnik::Integer, mapnik::Integer, mapnik::String, mapnik::String, mapnik::Boolean, mapnik::Double, mapnik::Integer, mapnik::Double, mapnik::Double, mapnik::String }); - } - - SECTION("Postgis iterate features") - { - mapnik::parameters params(base_params); - params["table"] = "test"; - params["key_field"] = "gid"; - params["max_async_connection"] = "2"; - //params["cursor_size"] = "2"; - auto ds = mapnik::datasource_cache::instance().create(params); - REQUIRE(ds != nullptr); - - auto featureset = ds->features_at_point(mapnik::coord2d(1, 1)); - mapnik::feature_ptr feature; - while ((bool(feature = featureset->next()))) { - REQUIRE(feature->get(2).to_string() == feature->get("col_text").to_string()); - REQUIRE(feature->get(4).to_bool() == feature->get("col+bool").to_bool()); - REQUIRE(feature->get(5).to_double() == feature->get("colnumeric").to_double()); - REQUIRE(feature->get(5).to_string() == feature->get("colnumeric").to_string()); + SECTION("Postgis data initialization") + { + //don't add 'true' here, to get error message, when drop fails. If it works nothing is output + REQUIRE(run("dropdb --if-exists " + dbname)); + REQUIRE(run("createdb -T template_postgis " + dbname)); + //REQUIRE(run("createdb " + dbname)); + // Breaks when raster support is missing (unfortunately this is common) + //REQUIRE(run("psql -c 'CREATE EXTENSION postgis;' " + dbname, true)); + REQUIRE(run("psql -q -f ./test/data/sql/postgis-create-db-and-tables.sql " + dbname)); } - featureset = all_features(ds); - feature = featureset->next(); - //deactivate char tests for now: not yet implemented. - //add at postgis_datasource.cpp:423 - //case 18: // char - //REQUIRE("A" == feature->get("col-char").to_string()); - feature = featureset->next(); - //REQUIRE("B" == feature->get("col-char").to_string()); - feature = featureset->next(); - REQUIRE(false == feature->get("col+bool").to_bool()); - } + mapnik::parameters base_params; + base_params["type"] = "postgis"; + base_params["dbname"] = dbname; - SECTION("Postgis cursorresultest") - { - mapnik::parameters params(base_params); - params["table"] = "(SELECT * FROM test) as data"; - params["cursor_size"] = "2"; - auto ds = mapnik::datasource_cache::instance().create(params); - REQUIRE(ds != nullptr); - auto featureset = all_features(ds); - CHECK(count_features(featureset) == 8); - - featureset = all_features(ds); - mapnik::feature_ptr feature; - while (bool(feature = featureset->next())) { - CHECK(feature->size() == 10); + SECTION("Postgis should throw without 'table' parameter") + { + mapnik::parameters params(base_params); + CHECK_THROWS(mapnik::datasource_cache::instance().create(params)); } - featureset = all_features(ds); - require_geometry(featureset->next(), 1, mapnik::geometry::geometry_types::Point); - require_geometry(featureset->next(), 1, mapnik::geometry::geometry_types::Point); - require_geometry(featureset->next(), 2, mapnik::geometry::geometry_types::MultiPoint); - require_geometry(featureset->next(), 1, mapnik::geometry::geometry_types::LineString); - require_geometry(featureset->next(), 2, mapnik::geometry::geometry_types::MultiLineString); - require_geometry(featureset->next(), 1, mapnik::geometry::geometry_types::Polygon); - require_geometry(featureset->next(), 2, mapnik::geometry::geometry_types::MultiPolygon); - require_geometry(featureset->next(), 3, mapnik::geometry::geometry_types::GeometryCollection); - } + SECTION("Postgis should throw with 'max_async_connection' greater than 'max_size'") + { + mapnik::parameters params(base_params); + params["table"] = "test"; + params["max_async_connection"] = "2"; + params["max_size"] = "1"; + CHECK_THROWS(mapnik::datasource_cache::instance().create(params)); + } - SECTION("Postgis bbox query") - { - mapnik::parameters params(base_params); - params["table"] = "(SELECT * FROM public.test) as data WHERE geom && !bbox!"; - auto ds = mapnik::datasource_cache::instance().create(params); - REQUIRE(ds != nullptr); - mapnik::box2d ext = ds->envelope(); - CAPTURE(ext); - INFO(std::setprecision(6) << std::fixed << ext.minx() << "/" << ext.miny() << " " << ext.maxx() << "/" << ext.maxy()); - REQUIRE(ext.minx() == -2); - REQUIRE(ext.miny() == -2); - REQUIRE(ext.maxx() == 5); - REQUIRE(ext.maxy() == 4); - } + SECTION("Postgis should throw with invalid metadata query") + { + mapnik::parameters params(base_params); + params["table"] = "does_not_exist"; + CHECK_THROWS(mapnik::datasource_cache::instance().create(params)); + } - SECTION("Postgis query extent: full dataset") - { - //include schema to increase coverage - mapnik::parameters params(base_params); - params["table"] = "(SELECT * FROM public.test) as data"; - auto ds = mapnik::datasource_cache::instance().create(params); - REQUIRE(ds != nullptr); - mapnik::box2d ext = ds->envelope(); - CAPTURE(ext); - INFO(std::setprecision(6) << std::fixed << ext.minx() << "/" << ext.miny() << " " << ext.maxx() << "/" << ext.maxy()); - REQUIRE(ext.minx() == -2); - REQUIRE(ext.miny() == -2); - REQUIRE(ext.maxx() == 5); - REQUIRE(ext.maxy() == 4); - } + SECTION("Postgis should throw with invalid key field") + { + mapnik::parameters params(base_params); + params["table"] = "test_invalid_id"; + params["key_field"] = "id"; + CHECK_THROWS(mapnik::datasource_cache::instance().create(params)); + } + + SECTION("Postgis should throw with multicolumn primary key") + { + mapnik::parameters params(base_params); + params["table"] = "test_invalid_multi_col_pk"; + params["autodetect_key_field"] = "true"; + CHECK_THROWS(mapnik::datasource_cache::instance().create(params)); + } + + SECTION("Postgis should throw without geom column") + { + mapnik::parameters params(base_params); + params["table"] = "test_no_geom_col"; + auto ds = mapnik::datasource_cache::instance().create(params); + REQUIRE(ds != nullptr); + CHECK_THROWS(all_features(ds)); + } + + SECTION("Postgis should throw with invalid credentials") + { + mapnik::parameters params(base_params); + params["table"] = "test"; + params["user"] = "not_a_valid_user"; + params["password"] = "not_a_valid_pwd"; + CHECK_THROWS(mapnik::datasource_cache::instance().create(params)); + } + + SECTION("Postgis initialize dataset with persist_connection, schema, extent, geometry field, autodectect key field, simplify_geometries, row_limit") + { + mapnik::parameters params(base_params); + params["persist_connection"] = "false"; + params["table"] = "public.test"; + params["geometry_field"] = "geom"; + params["autodetect_key_field"] = "true"; + params["extent"] = "-1 -1, -1 2, 4 3, 3 -1, -1 -1"; + params["simplify_geometries"] = "true"; + params["row_limit"] = "1"; + auto ds = mapnik::datasource_cache::instance().create(params); + } + + SECTION("Postgis dataset geometry type") + { + mapnik::parameters params(base_params); + params["table"] = "(SELECT * FROM test WHERE gid=1) as data"; + auto ds = mapnik::datasource_cache::instance().create(params); + REQUIRE(ds != nullptr); + CHECK(ds->get_geometry_type() == mapnik::datasource_geometry_t::Point); + } + + SECTION("Postgis query field names") + { + mapnik::parameters params(base_params); + params["table"] = "test"; + auto ds = mapnik::datasource_cache::instance().create(params); + REQUIRE(ds != nullptr); + REQUIRE(ds->type() == mapnik::datasource::datasource_t::Vector); + auto fields = ds->get_descriptor().get_descriptors(); + require_field_names(fields, { "gid", "colbigint", "col_text", "col-char", "col+bool", "colnumeric", "colsmallint", "colfloat4", "colfloat8", "colcharacter" }); + require_field_types(fields, { mapnik::Integer, mapnik::Integer, mapnik::String, mapnik::String, mapnik::Boolean, mapnik::Double, mapnik::Integer, mapnik::Double, mapnik::Double, mapnik::String }); + } + + SECTION("Postgis iterate features") + { + mapnik::parameters params(base_params); + params["table"] = "test"; + params["key_field"] = "gid"; + params["max_async_connection"] = "2"; + //params["cursor_size"] = "2"; + auto ds = mapnik::datasource_cache::instance().create(params); + REQUIRE(ds != nullptr); + + auto featureset = ds->features_at_point(mapnik::coord2d(1, 1)); + mapnik::feature_ptr feature; + while ((bool(feature = featureset->next()))) { + REQUIRE(feature->get(2).to_string() == feature->get("col_text").to_string()); + REQUIRE(feature->get(4).to_bool() == feature->get("col+bool").to_bool()); + REQUIRE(feature->get(5).to_double() == feature->get("colnumeric").to_double()); + REQUIRE(feature->get(5).to_string() == feature->get("colnumeric").to_string()); + } + + featureset = all_features(ds); + feature = featureset->next(); + //deactivate char tests for now: not yet implemented. + //add at postgis_datasource.cpp:423 + //case 18: // char + //REQUIRE("A" == feature->get("col-char").to_string()); + feature = featureset->next(); + //REQUIRE("B" == feature->get("col-char").to_string()); + feature = featureset->next(); + REQUIRE(false == feature->get("col+bool").to_bool()); + } + + SECTION("Postgis cursorresultest") + { + mapnik::parameters params(base_params); + params["table"] = "(SELECT * FROM test) as data"; + params["cursor_size"] = "2"; + auto ds = mapnik::datasource_cache::instance().create(params); + REQUIRE(ds != nullptr); + auto featureset = all_features(ds); + CHECK(count_features(featureset) == 8); + + featureset = all_features(ds); + mapnik::feature_ptr feature; + while (bool(feature = featureset->next())) { + CHECK(feature->size() == 10); + } + + featureset = all_features(ds); + require_geometry(featureset->next(), 1, mapnik::geometry::geometry_types::Point); + require_geometry(featureset->next(), 1, mapnik::geometry::geometry_types::Point); + require_geometry(featureset->next(), 2, mapnik::geometry::geometry_types::MultiPoint); + require_geometry(featureset->next(), 1, mapnik::geometry::geometry_types::LineString); + require_geometry(featureset->next(), 2, mapnik::geometry::geometry_types::MultiLineString); + require_geometry(featureset->next(), 1, mapnik::geometry::geometry_types::Polygon); + require_geometry(featureset->next(), 2, mapnik::geometry::geometry_types::MultiPolygon); + require_geometry(featureset->next(), 3, mapnik::geometry::geometry_types::GeometryCollection); + } + + SECTION("Postgis bbox query") + { + mapnik::parameters params(base_params); + params["table"] = "(SELECT * FROM public.test) as data WHERE geom && !bbox!"; + auto ds = mapnik::datasource_cache::instance().create(params); + REQUIRE(ds != nullptr); + mapnik::box2d ext = ds->envelope(); + CAPTURE(ext); + INFO(std::setprecision(6) << std::fixed << ext.minx() << "/" << ext.miny() << " " << ext.maxx() << "/" << ext.maxy()); + REQUIRE(ext.minx() == -2); + REQUIRE(ext.miny() == -2); + REQUIRE(ext.maxx() == 5); + REQUIRE(ext.maxy() == 4); + } + + SECTION("Postgis query extent: full dataset") + { + //include schema to increase coverage + mapnik::parameters params(base_params); + params["table"] = "(SELECT * FROM public.test) as data"; + auto ds = mapnik::datasource_cache::instance().create(params); + REQUIRE(ds != nullptr); + mapnik::box2d ext = ds->envelope(); + CAPTURE(ext); + INFO(std::setprecision(6) << std::fixed << ext.minx() << "/" << ext.miny() << " " << ext.maxx() << "/" << ext.maxy()); + REQUIRE(ext.minx() == -2); + REQUIRE(ext.miny() == -2); + REQUIRE(ext.maxx() == 5); + REQUIRE(ext.maxy() == 4); + } /* deactivated for merging: still investigating a proper fix - SECTION("Postgis query extent from subquery") - { - mapnik::parameters params(base_params); - params["table"] = "(SELECT * FROM test where gid=4) as data"; - auto ds = mapnik::datasource_cache::instance().create(params); - REQUIRE(ds != nullptr); - mapnik::box2d ext = ds->envelope(); - CAPTURE(ext); - INFO(std::setprecision(6) << std::fixed << ext.minx() << "/" << ext.miny() << " " << ext.maxx() << "/" << ext.maxy()); - REQUIRE(ext.minx() == 0); - REQUIRE(ext.miny() == 0); - REQUIRE(ext.maxx() == 1); - REQUIRE(ext.maxy() == 2); - } + SECTION("Postgis query extent from subquery") + { + mapnik::parameters params(base_params); + params["table"] = "(SELECT * FROM test where gid=4) as data"; + auto ds = mapnik::datasource_cache::instance().create(params); + REQUIRE(ds != nullptr); + mapnik::box2d ext = ds->envelope(); + CAPTURE(ext); + INFO(std::setprecision(6) << std::fixed << ext.minx() << "/" << ext.miny() << " " << ext.maxx() << "/" << ext.maxy()); + REQUIRE(ext.minx() == 0); + REQUIRE(ext.miny() == 0); + REQUIRE(ext.maxx() == 1); + REQUIRE(ext.maxy() == 2); + } */ - SECTION("Postgis query extent: from subquery with 'extent_from_subquery=true'") - { - mapnik::parameters params(base_params); - params["table"] = "(SELECT * FROM test where gid=4) as data"; - params["extent_from_subquery"] = "true"; - auto ds = mapnik::datasource_cache::instance().create(params); - REQUIRE(ds != nullptr); - mapnik::box2d ext = ds->envelope(); - CAPTURE(ext); - INFO(std::setprecision(6) << std::fixed << ext.minx() << "/" << ext.miny() << " " << ext.maxx() << "/" << ext.maxy()); - REQUIRE(ext.minx() == 0); - REQUIRE(ext.miny() == 0); - REQUIRE(ext.maxx() == 1); - REQUIRE(ext.maxy() == 2); - } + SECTION("Postgis query extent: from subquery with 'extent_from_subquery=true'") + { + mapnik::parameters params(base_params); + params["table"] = "(SELECT * FROM test where gid=4) as data"; + params["extent_from_subquery"] = "true"; + auto ds = mapnik::datasource_cache::instance().create(params); + REQUIRE(ds != nullptr); + mapnik::box2d ext = ds->envelope(); + CAPTURE(ext); + INFO(std::setprecision(6) << std::fixed << ext.minx() << "/" << ext.miny() << " " << ext.maxx() << "/" << ext.maxy()); + REQUIRE(ext.minx() == 0); + REQUIRE(ext.miny() == 0); + REQUIRE(ext.maxx() == 1); + REQUIRE(ext.maxy() == 2); + } /* deactivated for merging: still investigating a proper fix - SECTION("Postgis query extent: subset with 'extent_from_subquery=true' and 'scale_denominator'") - { - mapnik::parameters params(base_params); - // !!!! postgis-vt-util::z() returns 'null' when 'scale_denominator > 600000000' - // https://github.com/mapbox/postgis-vt-util/blob/559f073877696a6bfea41baf3e1065f9cf4d18d1/postgis-vt-util.sql#L615-L617 - params["table"] = "(SELECT * FROM test where gid=4 AND z(!scale_denominator!) BETWEEN 0 AND 22) as data"; - params["extent_from_subquery"] = "true"; - auto ds = mapnik::datasource_cache::instance().create(params); - REQUIRE(ds != nullptr); - mapnik::box2d ext = ds->envelope(); - CAPTURE(ext); - INFO("" << std::setprecision(6) << std::fixed << ext.minx() << "/" << ext.miny() << " " << ext.maxx() << "/" << ext.maxy()); - REQUIRE(ext.minx() == 0); - REQUIRE(ext.miny() == 0); - REQUIRE(ext.maxx() == 1); - REQUIRE(ext.maxy() == 2); - } + SECTION("Postgis query extent: subset with 'extent_from_subquery=true' and 'scale_denominator'") + { + mapnik::parameters params(base_params); + // !!!! postgis-vt-util::z() returns 'null' when 'scale_denominator > 600000000' + // https://github.com/mapbox/postgis-vt-util/blob/559f073877696a6bfea41baf3e1065f9cf4d18d1/postgis-vt-util.sql#L615-L617 + params["table"] = "(SELECT * FROM test where gid=4 AND z(!scale_denominator!) BETWEEN 0 AND 22) as data"; + params["extent_from_subquery"] = "true"; + auto ds = mapnik::datasource_cache::instance().create(params); + REQUIRE(ds != nullptr); + mapnik::box2d ext = ds->envelope(); + CAPTURE(ext); + INFO("" << std::setprecision(6) << std::fixed << ext.minx() << "/" << ext.miny() << " " << ext.maxx() << "/" << ext.maxy()); + REQUIRE(ext.minx() == 0); + REQUIRE(ext.miny() == 0); + REQUIRE(ext.maxx() == 1); + REQUIRE(ext.maxy() == 2); + } */ -} \ No newline at end of file + + } +} diff --git a/test/unit/datasource/topojson.cpp b/test/unit/datasource/topojson.cpp index 25f9ffdaf..6b1a2e1b1 100644 --- a/test/unit/datasource/topojson.cpp +++ b/test/unit/datasource/topojson.cpp @@ -21,6 +21,7 @@ *****************************************************************************/ #include "catch.hpp" +#include "ds_test_util.hpp" #include #include @@ -50,7 +51,7 @@ bool parse_topology(std::string const& filename, mapnik::topojson::topology & to } -TEST_CASE("topology") +TEST_CASE("topojson") { SECTION("geometry parsing") { @@ -72,4 +73,42 @@ TEST_CASE("topology") } } } + + SECTION("TopoJSON properties are properly expressed") + { + std::string filename("./test/data/topojson/escaped.topojson"); + mapnik::context_ptr ctx = std::make_shared(); + mapnik::transcoder tr("utf8"); + mapnik::topojson::topology topo; + REQUIRE(parse_topology(filename, topo)); + mapnik::value_integer feature_id = 0; + for (auto const& geom : topo.geometries) + { + mapnik::box2d bbox = mapnik::util::apply_visitor(mapnik::topojson::bounding_box_visitor(topo), geom); + CHECK(bbox.valid()); + mapnik::topojson::feature_generator visitor(ctx, tr, topo, feature_id); + mapnik::feature_ptr feature = mapnik::util::apply_visitor(visitor, geom); + CHECK(feature); + CHECK(feature->envelope() == bbox); + std::initializer_list attrs = { + attr{"name", tr.transcode("Test")}, + attr{"NOM_FR", tr.transcode("Québec")}, + attr{"boolean", mapnik::value_bool("true")}, + attr{"description", tr.transcode("Test: \u005C")}, + attr{"double", mapnik::value_double(1.1)}, + attr{"int", mapnik::value_integer(1)}, + attr{"object", tr.transcode("{name:\"waka\",spaces:\"value with spaces\",int:1,double:1.1,boolean:false" + ",NOM_FR:\"Québec\",array:[\"string\",\"value with spaces\",3,1.1,null,true" + ",\"Québec\"],another_object:{name:\"nested object\"}}")}, + attr{"spaces", tr.transcode("this has spaces")}, + attr{"array", tr.transcode("[\"string\",\"value with spaces\",3,1.1,null,true," + "\"Québec\",{name:\"object within an array\"}," + "[\"array\",\"within\",\"an\",\"array\"]]")}, + attr{"empty_array", tr.transcode("[]")}, + attr{"empty_object", tr.transcode("{}")}, + }; + REQUIRE_ATTRIBUTES(feature, attrs); + } + } + } diff --git a/test/unit/geometry/geometry_envelope_test.cpp b/test/unit/geometry/geometry_envelope_test.cpp index 7d5bc9d05..d8904dd5f 100644 --- a/test/unit/geometry/geometry_envelope_test.cpp +++ b/test/unit/geometry/geometry_envelope_test.cpp @@ -2,15 +2,18 @@ #include #include -#include -TEST_CASE("geometry ops - envelope") { +namespace { -SECTION("envelope_test - double") { +template +void envelope_test() +{ using namespace mapnik::geometry; + using coord_type = T; + { - geometry geom(point(1,2)); - mapnik::box2d bbox = mapnik::geometry::envelope(geom); + geometry geom(point(1,2)); + mapnik::box2d bbox = mapnik::geometry::envelope(geom); REQUIRE( bbox.minx() == 1 ); REQUIRE( bbox.miny() == 2 ); REQUIRE( bbox.maxx() == 1 ); @@ -18,80 +21,67 @@ SECTION("envelope_test - double") { } { // Test empty geom - geometry geom = mapnik::geometry::geometry_empty(); - mapnik::box2d bbox = mapnik::geometry::envelope(geom); + geometry geom = mapnik::geometry::geometry_empty(); + mapnik::box2d bbox = mapnik::geometry::envelope(geom); REQUIRE_FALSE( bbox.valid() ); } { - line_string line; + line_string line; line.add_coord(0,0); line.add_coord(1,1); line.add_coord(2,2); - geometry geom(line); - mapnik::box2d bbox = mapnik::geometry::envelope(geom); + geometry geom(line); + mapnik::box2d bbox = mapnik::geometry::envelope(geom); REQUIRE( bbox.minx() == 0 ); REQUIRE( bbox.miny() == 0 ); REQUIRE( bbox.maxx() == 2 ); REQUIRE( bbox.maxy() == 2 ); } { - line_string line; + line_string line; line.add_coord(0,0); line.add_coord(1,1); line.add_coord(2,2); - line_string line2; + line_string line2; line2.add_coord(0,0); line2.add_coord(-1,-1); line2.add_coord(-2,-2); - multi_line_string multi_line; + multi_line_string multi_line; multi_line.emplace_back(std::move(line)); multi_line.emplace_back(std::move(line2)); - geometry geom(multi_line); - mapnik::box2d bbox = mapnik::geometry::envelope(geom); + geometry geom(multi_line); + mapnik::box2d bbox = mapnik::geometry::envelope(geom); REQUIRE( bbox.minx() == -2 ); REQUIRE( bbox.miny() == -2 ); REQUIRE( bbox.maxx() == 2 ); REQUIRE( bbox.maxy() == 2 ); } { - polygon poly; - linear_ring ring; + polygon poly; + linear_ring ring; ring.add_coord(0,0); ring.add_coord(-10,0); ring.add_coord(-10,10); ring.add_coord(0,10); ring.add_coord(0,0); poly.set_exterior_ring(std::move(ring)); - geometry geom(poly); - mapnik::box2d bbox = mapnik::geometry::envelope(geom); + geometry geom(poly); + mapnik::box2d bbox = mapnik::geometry::envelope(geom); REQUIRE( bbox.minx() == -10 ); REQUIRE( bbox.miny() == 0 ); REQUIRE( bbox.maxx() == 0 ); REQUIRE( bbox.maxy() == 10 ); - multi_polygon mp; + multi_polygon mp; mp.push_back(poly); - geometry geom_mp(mp); + geometry geom_mp(mp); bbox = mapnik::geometry::envelope(geom_mp); REQUIRE( bbox.minx() == -10 ); REQUIRE( bbox.miny() == 0 ); REQUIRE( bbox.maxx() == 0 ); REQUIRE( bbox.maxy() == 10 ); - correct(geom); - bbox = mapnik::geometry::envelope(geom); - REQUIRE( bbox.minx() == -10 ); - REQUIRE( bbox.miny() == 0 ); - REQUIRE( bbox.maxx() == 0 ); - REQUIRE( bbox.maxy() == 10 ); - correct(geom_mp); - bbox = mapnik::geometry::envelope(geom_mp); - REQUIRE( bbox.minx() == -10 ); - REQUIRE( bbox.miny() == 0 ); - REQUIRE( bbox.maxx() == 0 ); - REQUIRE( bbox.maxy() == 10 ); - - geometry_collection gc; + geometry_collection gc; bbox = mapnik::geometry::envelope(gc); REQUIRE_FALSE( bbox.valid() ); gc.push_back(geom_mp); @@ -100,7 +90,7 @@ SECTION("envelope_test - double") { REQUIRE( bbox.miny() == 0 ); REQUIRE( bbox.maxx() == 0 ); REQUIRE( bbox.maxy() == 10 ); - gc.emplace_back(point(-50,-50)); + gc.emplace_back(point(-50,-50)); bbox = mapnik::geometry::envelope(gc); REQUIRE( bbox.minx() == -50 ); REQUIRE( bbox.miny() == -50 ); @@ -110,30 +100,30 @@ SECTION("envelope_test - double") { { // polygon with hole - polygon poly; - linear_ring ring; + polygon poly; + linear_ring ring; ring.add_coord(0,0); ring.add_coord(-10,0); ring.add_coord(-10,10); ring.add_coord(0,10); ring.add_coord(0,0); poly.set_exterior_ring(std::move(ring)); - linear_ring hole; + linear_ring hole; hole.add_coord(-7,7); hole.add_coord(-7,3); hole.add_coord(-3,3); hole.add_coord(-3,7); hole.add_coord(-7,7); poly.add_hole(std::move(hole)); - geometry geom(poly); - mapnik::box2d bbox = mapnik::geometry::envelope(poly); + geometry geom(poly); + mapnik::box2d bbox = mapnik::geometry::envelope(poly); REQUIRE( bbox.minx() == -10 ); REQUIRE( bbox.miny() == 0 ); REQUIRE( bbox.maxx() == 0 ); REQUIRE( bbox.maxy() == 10 ); // add another hole inside the first hole // which should be considered a hit - linear_ring fill; + linear_ring fill; fill.add_coord(-6,4); fill.add_coord(-6,6); fill.add_coord(-4,6); @@ -149,3 +139,14 @@ SECTION("envelope_test - double") { } } + +TEST_CASE("geometry ops - envelope") { + +SECTION("envelope_test") +{ + envelope_test(); + envelope_test(); + envelope_test(); +} + +} diff --git a/test/unit/geometry/geometry_test_helper.cpp b/test/unit/geometry/geometry_test_helper.cpp new file mode 100644 index 000000000..44bcce596 --- /dev/null +++ b/test/unit/geometry/geometry_test_helper.cpp @@ -0,0 +1,15 @@ +#include +#include +#include + + +namespace mapnik { namespace geometry { +// instantiate types required by geometry_envelope_test +template mapnik::box2d envelope(geometry const& geom); +template mapnik::box2d envelope(geometry const& geom); +template mapnik::box2d envelope(polygon const& geom); +template mapnik::box2d envelope(polygon const& geom); +template mapnik::box2d envelope(geometry_collection const& geom); +template mapnik::box2d envelope(geometry_collection const& geom); + +}} diff --git a/test/visual/renderer.hpp b/test/visual/renderer.hpp index 6b033f99c..7ea9198df 100644 --- a/test/visual/renderer.hpp +++ b/test/visual/renderer.hpp @@ -38,10 +38,21 @@ #if defined(GRID_RENDERER) #include #endif + #if defined(HAVE_CAIRO) #include #include +#ifdef CAIRO_HAS_SVG_SURFACE +#include #endif +#ifdef CAIRO_HAS_PS_SURFACE +#include +#endif +#ifdef CAIRO_HAS_PDF_SURFACE +#include +#endif +#endif + #if defined(SVG_RENDERER) #include #endif @@ -53,7 +64,7 @@ namespace visual_tests { template -struct renderer_base +struct raster_renderer_base { using image_type = ImageType; @@ -80,7 +91,35 @@ struct renderer_base } }; -struct agg_renderer : renderer_base +struct vector_renderer_base +{ + using image_type = std::string; + + static constexpr const bool support_tiles = false; + + unsigned compare(image_type const & actual, boost::filesystem::path const& reference) const + { + std::ifstream stream(reference.string().c_str(), std::ios_base::in | std::ios_base::binary); + if (!stream) + { + throw std::runtime_error("Could not open: " + reference.string()); + } + std::string expected(std::istreambuf_iterator(stream.rdbuf()), std::istreambuf_iterator()); + return std::max(actual.size(), expected.size()) - std::min(actual.size(), expected.size()); + } + + void save(image_type const & image, boost::filesystem::path const& path) const + { + std::ofstream file(path.string().c_str(), std::ios::out | std::ios::trunc | std::ios::binary); + if (!file) + { + throw std::runtime_error("Cannot open file for writing: " + path.string()); + } + file << image; + } +}; + +struct agg_renderer : raster_renderer_base { static constexpr const char * name = "agg"; @@ -94,7 +133,7 @@ struct agg_renderer : renderer_base }; #if defined(HAVE_CAIRO) -struct cairo_renderer : renderer_base +struct cairo_renderer : raster_renderer_base { static constexpr const char * name = "cairo"; @@ -111,14 +150,65 @@ struct cairo_renderer : renderer_base return image; } }; + +using surface_create_type = cairo_surface_t *(&)(cairo_write_func_t, void *, double, double); + +template +struct cairo_vector_renderer : vector_renderer_base +{ + static cairo_status_t write(void *closure, + const unsigned char *data, + unsigned int length) + { + std::ostringstream & ss = *reinterpret_cast(closure); + ss.write(reinterpret_cast(data), length); + return ss ? CAIRO_STATUS_SUCCESS : CAIRO_STATUS_WRITE_ERROR; + } + + image_type render(mapnik::Map const & map, double scale_factor) const + { + std::ostringstream ss(std::stringstream::binary); + mapnik::cairo_surface_ptr image_surface( + SurfaceCreateFunction(write, &ss, map.width(), map.height()), + mapnik::cairo_surface_closer()); + mapnik::cairo_ptr image_context(mapnik::create_context(image_surface)); + mapnik::cairo_renderer ren(map, image_context, scale_factor); + ren.apply(); + cairo_surface_finish(&*image_surface); + return ss.str(); + } +}; + +#ifdef CAIRO_HAS_SVG_SURFACE +struct cairo_svg_renderer : cairo_vector_renderer +{ + static constexpr const char * name = "cairo-svg"; + static constexpr const char * ext = ".svg"; +}; +#endif + +#ifdef CAIRO_HAS_PS_SURFACE +struct cairo_ps_renderer : cairo_vector_renderer +{ + static constexpr const char * name = "cairo-ps"; + static constexpr const char * ext = ".ps"; +}; +#endif + +#ifdef CAIRO_HAS_PDF_SURFACE +struct cairo_pdf_renderer : cairo_vector_renderer +{ + static constexpr const char * name = "cairo-pdf"; + static constexpr const char * ext = ".pdf"; +}; +#endif #endif #if defined(SVG_RENDERER) -struct svg_renderer : renderer_base +struct svg_renderer : vector_renderer_base { static constexpr const char * name = "svg"; static constexpr const char * ext = ".svg"; - static constexpr const bool support_tiles = false; image_type render(mapnik::Map const & map, double scale_factor) const { @@ -128,35 +218,11 @@ struct svg_renderer : renderer_base ren.apply(); return ss.str(); } - - unsigned compare(image_type const & actual, boost::filesystem::path const& reference) const - { - std::ifstream stream(reference.string().c_str(),std::ios_base::in|std::ios_base::binary); - if (!stream.is_open()) - { - throw std::runtime_error("could not open: '" + reference.string() + "'"); - } - std::string expected(std::istreambuf_iterator(stream.rdbuf()),(std::istreambuf_iterator())); - stream.close(); - return std::max(actual.size(), expected.size()) - std::min(actual.size(), expected.size()); - } - - void save(image_type const & image, boost::filesystem::path const& path) const - { - std::ofstream file(path.string().c_str(), std::ios::out | std::ios::trunc | std::ios::binary); - if (!file) { - throw std::runtime_error((std::string("cannot open file for writing file ") + path.string()).c_str()); - } else { - file << image; - file.close(); - } - } - }; #endif #if defined(GRID_RENDERER) -struct grid_renderer : renderer_base +struct grid_renderer : raster_renderer_base { static constexpr const char * name = "grid"; @@ -335,6 +401,15 @@ private: using renderer_type = mapnik::util::variant #if defined(HAVE_CAIRO) ,renderer +#ifdef CAIRO_HAS_SVG_SURFACE + ,renderer +#endif +#ifdef CAIRO_HAS_PS_SURFACE + ,renderer +#endif +#ifdef CAIRO_HAS_PDF_SURFACE + ,renderer +#endif #endif #if defined(SVG_RENDERER) ,renderer diff --git a/test/visual/run.cpp b/test/visual/run.cpp index 70284bc3e..587727906 100644 --- a/test/visual/run.cpp +++ b/test/visual/run.cpp @@ -48,30 +48,48 @@ namespace po = boost::program_options; runner::renderer_container create_renderers(po::variables_map const & args, boost::filesystem::path const & output_dir, - bool append_all = false) + bool force_append = false) { boost::filesystem::path reference_dir(args["images-dir"].as()); bool overwrite = args.count("overwrite"); runner::renderer_container renderers; - if (append_all || args.count(agg_renderer::name)) + if (force_append || args.count(agg_renderer::name)) { renderers.emplace_back(renderer(output_dir, reference_dir, overwrite)); } #if defined(HAVE_CAIRO) - if (append_all || args.count(cairo_renderer::name)) + if (force_append || args.count(cairo_renderer::name)) { renderers.emplace_back(renderer(output_dir, reference_dir, overwrite)); } +#ifdef CAIRO_HAS_SVG_SURFACE + if (args.count(cairo_svg_renderer::name)) + { + renderers.emplace_back(renderer(output_dir, reference_dir, overwrite)); + } +#endif +#ifdef CAIRO_HAS_PS_SURFACE + if (args.count(cairo_ps_renderer::name)) + { + renderers.emplace_back(renderer(output_dir, reference_dir, overwrite)); + } +#endif +#ifdef CAIRO_HAS_PDF_SURFACE + if (args.count(cairo_pdf_renderer::name)) + { + renderers.emplace_back(renderer(output_dir, reference_dir, overwrite)); + } +#endif #endif #if defined(SVG_RENDERER) - if (append_all || args.count(svg_renderer::name)) + if (force_append || args.count(svg_renderer::name)) { renderers.emplace_back(renderer(output_dir, reference_dir, overwrite)); } #endif #if defined(GRID_RENDERER) - if (append_all || args.count(grid_renderer::name)) + if (force_append || args.count(grid_renderer::name)) { renderers.emplace_back(renderer(output_dir, reference_dir, overwrite)); } @@ -112,6 +130,15 @@ int main(int argc, char** argv) (agg_renderer::name, "render with AGG renderer") #if defined(HAVE_CAIRO) (cairo_renderer::name, "render with Cairo renderer") +#ifdef CAIRO_HAS_SVG_SURFACE + (cairo_svg_renderer::name, "render with Cairo SVG renderer") +#endif +#ifdef CAIRO_HAS_PS_SURFACE + (cairo_ps_renderer::name, "render with Cairo PS renderer") +#endif +#ifdef CAIRO_HAS_PDF_SURFACE + (cairo_pdf_renderer::name, "render with Cairo PDF renderer") +#endif #endif #if defined(SVG_RENDERER) (svg_renderer::name, "render with SVG renderer") @@ -183,7 +210,7 @@ int main(int argc, char** argv) } catch (std::exception & e) { - std::cerr << "Error runnig tests: " << e.what() << std::endl; + std::cerr << "Error running tests: " << e.what() << std::endl; return 1; }