Merge branch 'master' into geometry-refactor

This commit is contained in:
artemp 2016-06-20 15:01:29 +01:00
commit 67d2a0e141
79 changed files with 1342 additions and 840 deletions

View file

@ -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

View file

@ -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

View file

@ -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.

View file

@ -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')

View file

@ -1,6 +1,6 @@
environment:
msvs_toolset: 14
BOOST_VERSION: 59
BOOST_VERSION: 60
FASTBUILD: 1
matrix:
- platform: x64

View file

@ -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

View file

@ -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;'

View file

@ -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:

View file

@ -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));

Binary file not shown.

View file

@ -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]]

Binary file not shown.

Binary file not shown.

View file

@ -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]]

Binary file not shown.

Binary file not shown.

View file

@ -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]]

Binary file not shown.

Binary file not shown.

View file

@ -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]]

Binary file not shown.

Binary file not shown.

View file

@ -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]]

Binary file not shown.

Binary file not shown.

View file

@ -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]]

Binary file not shown.

2
deps/mapbox/variant vendored

@ -1 +1 @@
Subproject commit 3f025adbf599d8dd9bfca02d45b37e49a2cae841
Subproject commit b5728ad76e1402c130a9330aa44b6f4b655b13b4

View file

@ -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:

View file

@ -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

View file

@ -49,7 +49,6 @@ struct geometry_empty
using coord_type = T;
};
template <typename T>
using geometry_base = mapnik::util::variant<geometry_empty<T>,
point<T>,
@ -58,11 +57,12 @@ using geometry_base = mapnik::util::variant<geometry_empty<T>,
multi_point<T>,
multi_line_string<T>,
multi_polygon<T>,
mapnik::util::recursive_wrapper<geometry_collection<T> > >;
geometry_collection<T> >;
template <typename T>
struct geometry : geometry_base<T>
{
using coord_type = T;
geometry()
: geometry_base<T>() {} // empty
@ -72,6 +72,7 @@ struct geometry : geometry_base<T>
};
template <typename T, template <typename...> class Cont>
struct geometry_collection : Cont<geometry<T>>
{

View file

@ -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 <mapnik/value.hpp>
#include <mapnik/unicode.hpp>
#include <mapnik/json/stringifier.hpp>
#include <mapnik/json/value_converters.hpp>
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<mapnik::json::json_value> const& array) const
{
std::string str = stringifier()(array);
return mapnik::value(tr_.transcode(str.c_str()));
}
mapnik::value operator()(std::vector<std::pair<std::string, mapnik::json::json_value> > const& object) const
{
std::string str = stringifier()(object);
return mapnik::value(tr_.transcode(str.c_str()));
}
template <typename T>
mapnik::value operator()(T const& val) const
{
return mapnik::value(val);
}
mapnik::transcoder const& tr_;
};
}}
#endif //MAPNIK_JSON_ATTRIBUTE_VALUE_VISITOR_HPP

View file

@ -148,13 +148,10 @@ extract_bounding_box_grammar<Iterator, Boxes, ErrorHandler>::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('[')

View file

@ -24,14 +24,10 @@
#define MAPNIK_FEATURE_GRAMMAR_HPP
// mapnik
#include <mapnik/json/geometry_grammar.hpp>
#include <mapnik/value.hpp>
#include <mapnik/feature.hpp>
#include <mapnik/unicode.hpp>
#include <mapnik/value.hpp>
#include <mapnik/json/generic_json.hpp>
#include <mapnik/json/value_converters.hpp>
#include <mapnik/json/geometry_grammar.hpp>
#include <mapnik/json/attribute_value_visitor.hpp>
#pragma GCC diagnostic push
#include <mapnik/warning_ignore.hpp>
#include <boost/spirit/include/qi.hpp>
@ -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 <typename T>
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<Iterator, void(FeatureType&), space_type>
qi::rule<Iterator, space_type> feature_type;
qi::rule<Iterator,void(FeatureType &),space_type> properties;
qi::rule<Iterator,qi::locals<std::string>, void(FeatureType &),space_type> attributes;
qi::rule<Iterator, json_value(), space_type> attribute_value;
qi::rule<Iterator, qi::locals<std::int32_t>, std::string(), space_type> stringify_object;
qi::rule<Iterator, qi::locals<std::int32_t>, std::string(), space_type> stringify_array;
// functions
phoenix::function<put_property> put_property_;
phoenix::function<set_geometry_impl> set_geometry;

View file

@ -50,23 +50,22 @@ feature_grammar<Iterator,FeatureType,ErrorHandler>::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<Iterator,FeatureType,ErrorHandler>::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<fail>(feature, error_handler(_1, _2, _3, _4));
}

View file

@ -31,15 +31,44 @@
#include <mapnik/warning_ignore.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/fusion/include/std_pair.hpp>
#pragma GCC diagnostic pop
#include <vector>
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<value_null,value_bool, value_integer, value_double, std::string>;
struct json_value;
using json_array = std::vector<json_value>;
using json_object_element = std::pair<std::string, json_value>;
using json_object = std::vector<json_object_element>;
using json_value_base = mapnik::util::variant<value_null,
value_bool,
value_integer,
value_double,
std::string,
mapnik::util::recursive_wrapper<json_array>,
mapnik::util::recursive_wrapper<json_object> >;
struct json_value : json_value_base
{
#ifdef _WINDOWS
json_value() = default;
template <typename T>
json_value(T && val)
: json_value_base(std::forward<T>(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<Iterator>::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<Iterator>::unicode_string()
;
double_quoted =
'"'
'"'
> *(char_esc(_val) | (~char_('"')) [_val += _1])
> '"'
;
@ -141,18 +174,17 @@ unicode_string<Iterator>::unicode_string()
template <typename Iterator>
struct generic_json
{
qi::rule<Iterator,space_type> value;
qi::int_parser<mapnik::value_integer,10,1,-1> int__;
qi::rule<Iterator, json_value(), space_type> value;
qi::int_parser<mapnik::value_integer, 10, 1, -1> int__;
unicode_string<Iterator> string_;
qi::rule<Iterator,space_type> key_value;
qi::rule<Iterator,json_value(),space_type> number;
qi::rule<Iterator,space_type> object;
qi::rule<Iterator,space_type> array;
qi::rule<Iterator,space_type> pairs;
qi::real_parser<double, qi::strict_real_policies<double> > strict_double;
qi::rule<Iterator, json_object_element(), space_type> key_value;
qi::rule<Iterator, json_value(), space_type> number;
qi::rule<Iterator, json_object(), space_type> object;
qi::rule<Iterator, json_array(), space_type> array;
qi::real_parser<double, qi::strict_real_policies<double>> strict_double;
// conversions
boost::phoenix::function<mapnik::detail::value_converter<mapnik::value_integer> > integer_converter;
boost::phoenix::function<mapnik::detail::value_converter<mapnik::value_double> > double_converter;
boost::phoenix::function<mapnik::detail::value_converter<mapnik::value_integer>> integer_converter;
boost::phoenix::function<mapnik::detail::value_converter<mapnik::value_double>> double_converter;
};
}}

View file

@ -47,7 +47,6 @@ geometry_grammar<Iterator, ErrorHandler>::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<Iterator, ErrorHandler>::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<Iterator, ErrorHandler>::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('}')
;

View file

@ -32,7 +32,6 @@
#pragma GCC diagnostic push
#include <mapnik/warning_ignore.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_function.hpp>
#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 <typename T0,typename T1>
result_type operator() (T0 & coords, T1 const& pos) const
{
if (pos) coords = *pos;
}
};
struct push_position_impl
{
using result_type = void;
template <typename T0, typename T1>
result_type operator() (T0 & coords, T1 const& pos) const
{
if (pos) coords.emplace_back(*pos);
}
};
template <typename Iterator, typename ErrorHandler = error_handler<Iterator> >
struct positions_grammar :
qi::grammar<Iterator,coordinates(),space_type>
@ -70,8 +49,6 @@ struct positions_grammar :
qi::rule<Iterator, positions(), space_type> ring;
qi::rule<Iterator, std::vector<positions>(), space_type> rings;
qi::rule<Iterator, std::vector<std::vector<positions> >(), space_type> rings_array;
boost::phoenix::function<set_position_impl> set_position;
boost::phoenix::function<push_position_impl> push_position;
};
}}

View file

@ -28,12 +28,33 @@
#include <boost/spirit/include/phoenix_object.hpp>
#include <boost/spirit/include/phoenix_stl.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_function.hpp>
// stl
#include <iostream>
#include <string>
namespace mapnik { namespace json {
struct set_position_impl
{
using result_type = void;
template <typename T0,typename T1>
result_type operator() (T0 & coords, T1 const& pos) const
{
if (pos) coords = *pos;
}
};
struct push_position_impl
{
using result_type = void;
template <typename T0, typename T1>
result_type operator() (T0 & coords, T1 const& pos) const
{
if (pos) coords.emplace_back(*pos);
}
};
template <typename Iterator, typename ErrorHandler>
positions_grammar<Iterator, ErrorHandler>::positions_grammar(ErrorHandler & error_handler)
: positions_grammar::base_type(coords,"coordinates")
@ -49,6 +70,9 @@ positions_grammar<Iterator, ErrorHandler>::positions_grammar(ErrorHandler & erro
using qi::fail;
using qi::on_error;
boost::phoenix::function<set_position_impl> set_position;
boost::phoenix::function<push_position_impl> 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(']')

View file

@ -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 <mapnik/json/generic_json.hpp>
#include <mapnik/util/conversions.hpp>
#include <mapnik/util/variant.hpp>
// stl
#include <string>
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<mapnik::json::json_value> 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<std::pair<std::string, mapnik::json::json_value>> 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

View file

@ -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 <typename T0, typename T1>
result_type operator()(T0 & coord, T1 & props) const
{
mapnik::topojson::point pt;
if (coord.template is<mapnik::topojson::coordinate>())
{
auto const& coord_ = coord.template get<mapnik::topojson::coordinate>();
pt.coord = coord_;
pt.props = props;
}
return pt;
}
};
struct create_multi_point
{
using result_type = mapnik::topojson::multi_point;
template <typename T0, typename T1>
result_type operator()(T0 & coords, T1 & props) const
{
mapnik::topojson::multi_point mpt;
if (coords.template is<std::vector<mapnik::topojson::coordinate>>())
{
auto const& points = coords.template get<std::vector<mapnik::topojson::coordinate>>();
mpt. points = points;
mpt.props = props;
}
return mpt;
}
};
struct create_line_string
{
using result_type = mapnik::topojson::linestring;
template <typename T0, typename T1>
result_type operator()(T0 & arcs, T1 & props) const
{
mapnik::topojson::linestring line;
if (arcs.template is<std::vector<index_type>>())
{
auto const& arcs_ = arcs.template get<std::vector<index_type>>();
line.rings = arcs_;
line.props = props;
}
return line;
}
};
struct create_multi_line_string
{
using result_type = mapnik::topojson::multi_linestring;
template <typename T0, typename T1>
result_type operator()(T0 & arcs, T1 & props) const
{
mapnik::topojson::multi_linestring mline;
if (arcs.template is<std::vector<std::vector<index_type>>>())
{
auto const& arcs_ = arcs.template get<std::vector<std::vector<index_type>>>();
mline.lines = arcs_;
mline.props = props;
}
return mline;
}
};
struct create_polygon
{
using result_type = mapnik::topojson::polygon;
template <typename T0, typename T1>
result_type operator()(T0 & arcs, T1 & props) const
{
mapnik::topojson::polygon poly;
if (arcs.template is<std::vector<std::vector<index_type>>>())
{
auto const& arcs_ = arcs.template get<std::vector<std::vector<index_type>>>();
poly.rings = arcs_;
poly.props = props;
}
return poly;
}
};
struct create_multi_polygon
{
using result_type = mapnik::topojson::multi_polygon;
template <typename T0, typename T1>
result_type operator()(T0 & arcs, T1 & props) const
{
mapnik::topojson::multi_polygon mpoly;
if (arcs.template is<std::vector<std::vector<std::vector<index_type>>>>())
{
auto const& arcs_ = arcs.template get<std::vector<std::vector<std::vector<index_type>>>>();
mpoly.polygons = arcs_;
mpoly.props = props;
}
return mpoly;
}
};
struct create_geometry_impl
{
using result_type = mapnik::topojson::geometry;
template <typename T0, typename T1, typename T2, typename T3>
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<coordinate,std::vector<coordinate>>;
using arcs_type = util::variant<std::vector<index_type>, std::vector<std::vector<index_type>>, std::vector<std::vector<std::vector<index_type>>>>;
template <typename Iterator, typename ErrorHandler = json::error_handler<Iterator> >
struct topojson_grammar : qi::grammar<Iterator, space_type, topology()>
@ -55,24 +187,18 @@ private:
qi::rule<Iterator, space_type, std::vector<mapnik::topojson::geometry>()> objects;
qi::rule<Iterator, space_type, std::vector<mapnik::topojson::arc>()> arcs;
qi::rule<Iterator, space_type, mapnik::topojson::arc()> arc;
qi::rule<Iterator, space_type, mapnik::topojson::coordinate()> coordinate;
qi::rule<Iterator, space_type, mapnik::topojson::coordinate()> coordinate_;
qi::rule<Iterator, space_type, coordinates_type()> coordinates;
qi::rule<Iterator, space_type, mapnik::topojson::transform()> transform;
qi::rule<Iterator, space_type, mapnik::topojson::bounding_box()> bbox;
qi::rule<Iterator, space_type, mapnik::topojson::geometry() > geometry;
qi::rule<Iterator, space_type, mapnik::topojson::point()> point;
qi::rule<Iterator, space_type, mapnik::topojson::multi_point()> multi_point;
qi::rule<Iterator, space_type, mapnik::topojson::linestring()> linestring;
qi::rule<Iterator, space_type, mapnik::topojson::multi_linestring()> multi_linestring;
qi::rule<Iterator, space_type, mapnik::topojson::polygon()> polygon;
qi::rule<Iterator, space_type, mapnik::topojson::multi_polygon()> multi_polygon;
qi::rule<Iterator, qi::locals<int, coordinates_type, arcs_type, properties>, mapnik::topojson::geometry(), space_type> geometry;
qi::rule<Iterator, space_type, void(std::vector<mapnik::topojson::geometry>&)> geometry_collection;
qi::rule<Iterator, space_type, std::vector<index_type>()> ring;
qi::rule<Iterator, space_type, std::vector<std::vector<index_type>>()> rings;
qi::rule<Iterator, space_type, arcs_type()> rings_array;
// properties
qi::rule<Iterator, space_type, mapnik::topojson::properties()> properties;
qi::rule<Iterator, space_type, mapnik::topojson::properties()> attributes;
qi::rule<Iterator, space_type, mapnik::json::json_value()> attribute_value;
// id
qi::rule<Iterator,space_type> id;
qi::rule<Iterator, space_type, mapnik::topojson::properties()> properties_;
qi::symbols<char, int> geometry_type_dispatch;
};
}}

View file

@ -57,42 +57,6 @@ BOOST_FUSION_ADAPT_STRUCT(
(double, maxy)
)
BOOST_FUSION_ADAPT_STRUCT(
mapnik::topojson::point,
(mapnik::topojson::coordinate, coord)
(boost::optional<mapnik::topojson::properties>, props)
)
BOOST_FUSION_ADAPT_STRUCT(
mapnik::topojson::multi_point,
(std::vector<mapnik::topojson::coordinate>, points)
(boost::optional<mapnik::topojson::properties>, props)
)
BOOST_FUSION_ADAPT_STRUCT(
mapnik::topojson::linestring,
(mapnik::topojson::index_type, ring)
(boost::optional<mapnik::topojson::properties>, props)
)
BOOST_FUSION_ADAPT_STRUCT(
mapnik::topojson::multi_linestring,
(std::vector<mapnik::topojson::index_type>, rings)
(boost::optional<mapnik::topojson::properties>, props)
)
BOOST_FUSION_ADAPT_STRUCT(
mapnik::topojson::polygon,
(std::vector<std::vector<mapnik::topojson::index_type> >, rings)
(boost::optional<mapnik::topojson::properties>, props)
)
BOOST_FUSION_ADAPT_STRUCT(
mapnik::topojson::multi_polygon,
(std::vector<std::vector<std::vector<mapnik::topojson::index_type> > >, polygons)
(boost::optional<mapnik::topojson::properties>, props)
)
BOOST_FUSION_ADAPT_STRUCT(
mapnik::topojson::topology,
(std::vector<mapnik::topojson::geometry>, geometries)
@ -101,6 +65,8 @@ BOOST_FUSION_ADAPT_STRUCT(
(boost::optional<mapnik::topojson::bounding_box>, bbox)
)
namespace mapnik { namespace topojson {
namespace qi = boost::spirit::qi;
@ -121,30 +87,43 @@ topojson_grammar<Iterator, ErrorHandler>::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<ErrorHandler> const error_handler;
boost::phoenix::function<create_geometry_impl> 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<Iterator, ErrorHandler>::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<Iterator, ErrorHandler>::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<fail>(topology, error_handler(_1, _2, _3, _4));

View file

@ -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 <mapnik/box2d.hpp>
#include <mapnik/unicode.hpp>
#include <mapnik/json/topology.hpp>
#include <mapnik/json/attribute_value_visitor.hpp>
#include <mapnik/feature_factory.hpp>
#include <mapnik/geometry_adapters.hpp>
#include <mapnik/geometry_correct.hpp>
@ -39,6 +40,11 @@ struct bounding_box_visitor
: topo_(topo),
num_arcs_(topo_.arcs.size()) {}
box2d<double> operator() (mapnik::topojson::empty const&) const
{
return box2d<double>();
}
box2d<double> operator() (mapnik::topojson::point const& pt) const
{
double x = pt.coord.x;
@ -81,50 +87,15 @@ struct bounding_box_visitor
box2d<double> operator() (mapnik::topojson::linestring const& line) const
{
box2d<double> 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<int>(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<double> operator() (mapnik::topojson::multi_linestring const& multi_line) const
{
box2d<double> 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<int>(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<double> operator() (mapnik::topojson::multi_linestring const& multi_line) const
{
box2d<double> 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<int>(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<double> operator() (mapnik::topojson::polygon const& poly) const
{
box2d<double> 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 <typename T>
mapnik::value operator()(T const& val) const
{
return mapnik::value(val);
}
mapnik::transcoder const& tr_;
};
template <typename T>
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<int>(num_arcs_))
{
auto const& arcs = topo_.arcs[arc_index];
double px = 0, py = 0;
mapnik::geometry::line_string<double> line_string;
line_string.reserve(arcs.coordinates.size());
mapnik::geometry::line_string<double> 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<int>(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<double> 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<int>(num_arcs_))
multi_line_string.reserve(multi_line_string.size() + line.size());
mapnik::geometry::line_string<double> line_string;
for (auto index : line)
{
hit = true;
double px = 0, py = 0;
mapnik::geometry::line_string<double> 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<int>(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))

View file

@ -62,13 +62,13 @@ struct multi_point
struct linestring
{
index_type ring ;
std::vector<index_type> rings ;
boost::optional<properties> props;
};
struct multi_linestring
{
std::vector<index_type> rings;
std::vector<std::vector<index_type> > lines;
boost::optional<properties> props;
};
@ -84,7 +84,10 @@ struct multi_polygon
boost::optional<properties> props;
};
using geometry = util::variant<point,
struct empty {};
using geometry = util::variant<empty,
point,
linestring,
polygon,
multi_point,

View file

@ -61,7 +61,7 @@ void render_point_symbolizer(point_symbolizer const &sym,
agg::trans_affine tr;
auto image_transform = get_optional<transform_type>(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;

View file

@ -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
{

View file

@ -163,6 +163,10 @@ protected:
void init_marker() const;
};
} //namespace
namespace geometry {
MAPNIK_DECL mapnik::box2d<double> envelope(mapnik::base_symbolizer_helper::geometry_cref const& geom);
}
} //namespace mapnik
#endif // SYMBOLIZER_HELPERS_HPP

View file

@ -24,7 +24,7 @@
#define MAPNIK_UTIL_VARIANT_HPP
#include <mapnik/config.hpp>
#include <mapbox/variant/variant.hpp>
#include <mapbox/variant.hpp>
#pragma GCC diagnostic push
#include <mapnik/warning_ignore.hpp>

View file

@ -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"

View file

@ -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}

View file

@ -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<mapnik::raster>(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

View file

@ -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_;

View file

@ -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<pgraster_overview>::const_reverse_iterator i;
for (i=overviews_.rbegin(); i!=overviews_.rend(); ++i) {

View file

@ -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 <typename T>
mapnik::eAttributeType operator() (T const& /*val*/) const
{
return mapnik::String;
}
@ -109,6 +100,11 @@ struct geometry_type_visitor
{
return static_cast<int>(mapnik::datasource_geometry_t::Polygon);
}
template <typename T>
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 <typename GeomType>
void operator() (GeomType const& g)
{

View file

@ -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 \

View file

@ -92,7 +92,7 @@ struct agg_renderer_process_visitor_l
value_double opacity = get<value_double, keys::opacity>(sym_, feature_, common_.vars_);
agg::trans_affine image_tr = agg::trans_affine_scaling(common_.scale_factor_);
auto image_transform = get_optional<transform_type>(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<double> const& bbox_image = marker.get_data()->bounding_box() * image_tr;
image_rgba8 image(bbox_image.width(), bbox_image.height());
render_pattern<buffer_type>(*ras_ptr_, marker, image_tr, 1.0, image);

View file

@ -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<transform_type>(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<double> const& bbox_image = marker.get_data()->bounding_box() * image_tr;
mapnik::image_rgba8 image(bbox_image.width(), bbox_image.height());
render_pattern<buffer_type>(*ras_ptr_, marker, image_tr, 1.0, image);

View file

@ -63,7 +63,7 @@ void agg_renderer<T0,T1>::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);
}

View file

@ -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<transform_type>(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<double> const& bbox_image = marker.get_data()->bounding_box() * image_tr;
mapnik::image_rgba8 image(bbox_image.width(), bbox_image.height());
render_pattern<image_rgba8>(ras, marker, image_tr, 1.0, image);

View file

@ -96,7 +96,7 @@ void cairo_renderer<T>::process(polygon_pattern_symbolizer const& sym,
value_double opacity = get<value_double, keys::opacity>(sym, feature, common_.vars_);
agg::trans_affine image_tr = agg::trans_affine_scaling(common_.scale_factor_);
auto image_transform = get_optional<transform_type>(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);

View file

@ -38,8 +38,6 @@ template MAPNIK_DECL mapnik::box2d<double> envelope(multi_line_string<double> co
template MAPNIK_DECL mapnik::box2d<double> envelope(multi_polygon<double> const& geom);
// collection
template MAPNIK_DECL mapnik::box2d<double> envelope(geometry_collection<double> const& geom);
//
template MAPNIK_DECL mapnik::box2d<std::int64_t> envelope(geometry<std::int64_t> const& geom);
} // end ns geometry
} // end ns mapnik

View file

@ -60,7 +60,7 @@ void grid_renderer<T>::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);
}

View file

@ -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<double>(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<double>(sym, keys::height, feature, vars, 0.0);
}
if (has_key(sym,keys::stroke_width))
{
half_stroke_width = get<double>(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);

View file

@ -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<point_count; i++) {
x[i*offset] *= DEG_TO_RAD;
y[i*offset] *= DEG_TO_RAD;
for (int i = 0; i < point_count; ++i)
{
x[i * offset] *= DEG_TO_RAD;
y[i * offset] *= DEG_TO_RAD;
}
}
if (pj_transform( dest_.proj_, source_.proj_, point_count,
offset, x,y,z) != 0)
if (pj_transform(dest_.proj_, source_.proj_, point_count,
offset, x, y, z) != 0)
{
return false;
}
for(int j=0; j<point_count; j++) {
for (int j = 0; j < point_count; ++j)
{
if (x[j] == HUGE_VAL || y[j] == HUGE_VAL)
{
return false;
@ -222,10 +223,10 @@ bool proj_transform::backward (double * x, double * y , double * z, int point_co
if (is_source_longlat_)
{
int i;
for(i=0; i<point_count; i++) {
x[i*offset] *= RAD_TO_DEG;
y[i*offset] *= RAD_TO_DEG;
for (int i = 0; i < point_count; ++i)
{
x[i * offset] *= RAD_TO_DEG;
y[i * offset] *= RAD_TO_DEG;
}
}
#endif
@ -266,7 +267,7 @@ unsigned int proj_transform::backward (std::vector<geometry::point<double>> & ls
double * x = reinterpret_cast<double*>(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<double> & 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<double>& env, int points) const
env.re_center(result.center().x, result.center().y);
env.height(result.height());
env.width(result.width());
return true;
}

View file

@ -159,7 +159,7 @@ struct render_marker_symbolizer_visitor
if (auto image_transform = get_optional<transform_type>(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<transform_type>(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<double> const& bbox = mark.bounding_box();
mapnik::image_rgba8 const& marker = mark.get_data();
// - clamp sizes to > 4 pixels of interactivity

View file

@ -465,7 +465,7 @@ void text_symbolizer_helper::init_marker() const
if (marker->is<marker_null>()) return;
agg::trans_affine trans;
auto image_transform = get_optional<transform_type>(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;

View file

@ -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())
{

View file

@ -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);

@ -1 +1 @@
Subproject commit 6e4b7ff65b6a1f0b34ae7cad52fe2c921ca9ea3f
Subproject commit e74f1cef09d579d6ff414cb56970adbe43b7a91b

@ -1 +1 @@
Subproject commit 725bc22d285ef5a558e2e241fd22ff5489f056ce
Subproject commit 5f0e6f86696a2a9a6733e42b1f400ba4ec2f8847

View file

@ -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

View file

@ -106,13 +106,18 @@ inline std::size_t count_features(mapnik::featureset_ptr features) {
}
using attr = std::tuple<std::string, mapnik::value>;
#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<attr> 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 {

View file

@ -23,6 +23,7 @@
#include "catch.hpp"
#include "ds_test_util.hpp"
#include <mapnik/unicode.hpp>
#include <mapnik/datasource.hpp>
#include <mapnik/datasource_cache.hpp>
#include <mapnik/geometry.hpp>
@ -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<std::string> 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<attr> 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");
}
}
}
}
}

View file

@ -29,13 +29,15 @@
#include <mapnik/util/fs.hpp>
/*
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 <boost/optional/optional_io.hpp>
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<double> 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<double> 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<double> 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<double> 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<double> 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<double> 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<double> 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<double> 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<double> 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<double> 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);
}
*/
}
}
}

View file

@ -21,6 +21,7 @@
*****************************************************************************/
#include "catch.hpp"
#include "ds_test_util.hpp"
#include <mapnik/util/fs.hpp>
#include <mapnik/util/file_io.hpp>
@ -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::context_type>();
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<double> bbox = mapnik::util::apply_visitor(mapnik::topojson::bounding_box_visitor(topo), geom);
CHECK(bbox.valid());
mapnik::topojson::feature_generator<mapnik::context_ptr> 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<attr> 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);
}
}
}

View file

@ -2,15 +2,18 @@
#include <mapnik/geometry.hpp>
#include <mapnik/geometry_envelope.hpp>
#include <mapnik/geometry_correct.hpp>
TEST_CASE("geometry ops - envelope") {
namespace {
SECTION("envelope_test - double") {
template <typename T>
void envelope_test()
{
using namespace mapnik::geometry;
using coord_type = T;
{
geometry<double> geom(point<double>(1,2));
mapnik::box2d<double> bbox = mapnik::geometry::envelope(geom);
geometry<coord_type> geom(point<coord_type>(1,2));
mapnik::box2d<coord_type> 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<double> geom = mapnik::geometry::geometry_empty<double>();
mapnik::box2d<double> bbox = mapnik::geometry::envelope(geom);
geometry<coord_type> geom = mapnik::geometry::geometry_empty<coord_type>();
mapnik::box2d<coord_type> bbox = mapnik::geometry::envelope(geom);
REQUIRE_FALSE( bbox.valid() );
}
{
line_string<double> line;
line_string<coord_type> line;
line.add_coord(0,0);
line.add_coord(1,1);
line.add_coord(2,2);
geometry<double> geom(line);
mapnik::box2d<double> bbox = mapnik::geometry::envelope(geom);
geometry<coord_type> geom(line);
mapnik::box2d<coord_type> bbox = mapnik::geometry::envelope(geom);
REQUIRE( bbox.minx() == 0 );
REQUIRE( bbox.miny() == 0 );
REQUIRE( bbox.maxx() == 2 );
REQUIRE( bbox.maxy() == 2 );
}
{
line_string<double> line;
line_string<coord_type> line;
line.add_coord(0,0);
line.add_coord(1,1);
line.add_coord(2,2);
line_string<double> line2;
line_string<coord_type> line2;
line2.add_coord(0,0);
line2.add_coord(-1,-1);
line2.add_coord(-2,-2);
multi_line_string<double> multi_line;
multi_line_string<coord_type> multi_line;
multi_line.emplace_back(std::move(line));
multi_line.emplace_back(std::move(line2));
geometry<double> geom(multi_line);
mapnik::box2d<double> bbox = mapnik::geometry::envelope(geom);
geometry<coord_type> geom(multi_line);
mapnik::box2d<coord_type> bbox = mapnik::geometry::envelope(geom);
REQUIRE( bbox.minx() == -2 );
REQUIRE( bbox.miny() == -2 );
REQUIRE( bbox.maxx() == 2 );
REQUIRE( bbox.maxy() == 2 );
}
{
polygon<double> poly;
linear_ring<double> ring;
polygon<coord_type> poly;
linear_ring<coord_type> 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<double> geom(poly);
mapnik::box2d<double> bbox = mapnik::geometry::envelope(geom);
geometry<coord_type> geom(poly);
mapnik::box2d<coord_type> bbox = mapnik::geometry::envelope(geom);
REQUIRE( bbox.minx() == -10 );
REQUIRE( bbox.miny() == 0 );
REQUIRE( bbox.maxx() == 0 );
REQUIRE( bbox.maxy() == 10 );
multi_polygon<double> mp;
multi_polygon<coord_type> mp;
mp.push_back(poly);
geometry<double> geom_mp(mp);
geometry<coord_type> 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<double> gc;
geometry_collection<coord_type> 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<double>(-50,-50));
gc.emplace_back(point<coord_type>(-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<double> poly;
linear_ring<double> ring;
polygon<coord_type> poly;
linear_ring<coord_type> 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<double> hole;
linear_ring<coord_type> 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<double> geom(poly);
mapnik::box2d<double> bbox = mapnik::geometry::envelope(poly);
geometry<coord_type> geom(poly);
mapnik::box2d<coord_type> 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<double> fill;
linear_ring<coord_type> 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<int>();
envelope_test<double>();
envelope_test<float>();
}
}

View file

@ -0,0 +1,15 @@
#include <mapnik/geometry.hpp>
#include <mapnik/geometry_envelope.hpp>
#include <mapnik/geometry_envelope_impl.hpp>
namespace mapnik { namespace geometry {
// instantiate types required by geometry_envelope_test
template mapnik::box2d<int> envelope(geometry<int> const& geom);
template mapnik::box2d<float> envelope(geometry<float> const& geom);
template mapnik::box2d<int> envelope(polygon<int> const& geom);
template mapnik::box2d<float> envelope(polygon<float> const& geom);
template mapnik::box2d<int> envelope(geometry_collection<int> const& geom);
template mapnik::box2d<float> envelope(geometry_collection<float> const& geom);
}}

View file

@ -38,10 +38,21 @@
#if defined(GRID_RENDERER)
#include <mapnik/grid/grid_renderer.hpp>
#endif
#if defined(HAVE_CAIRO)
#include <mapnik/cairo/cairo_renderer.hpp>
#include <mapnik/cairo/cairo_image_util.hpp>
#ifdef CAIRO_HAS_SVG_SURFACE
#include <cairo-svg.h>
#endif
#ifdef CAIRO_HAS_PS_SURFACE
#include <cairo-ps.h>
#endif
#ifdef CAIRO_HAS_PDF_SURFACE
#include <cairo-pdf.h>
#endif
#endif
#if defined(SVG_RENDERER)
#include <mapnik/svg/output/svg_renderer.hpp>
#endif
@ -53,7 +64,7 @@ namespace visual_tests
{
template <typename ImageType>
struct renderer_base
struct raster_renderer_base
{
using image_type = ImageType;
@ -80,7 +91,35 @@ struct renderer_base
}
};
struct agg_renderer : renderer_base<mapnik::image_rgba8>
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<char>(stream.rdbuf()), std::istreambuf_iterator<char>());
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<mapnik::image_rgba8>
{
static constexpr const char * name = "agg";
@ -94,7 +133,7 @@ struct agg_renderer : renderer_base<mapnik::image_rgba8>
};
#if defined(HAVE_CAIRO)
struct cairo_renderer : renderer_base<mapnik::image_rgba8>
struct cairo_renderer : raster_renderer_base<mapnik::image_rgba8>
{
static constexpr const char * name = "cairo";
@ -111,14 +150,65 @@ struct cairo_renderer : renderer_base<mapnik::image_rgba8>
return image;
}
};
using surface_create_type = cairo_surface_t *(&)(cairo_write_func_t, void *, double, double);
template <surface_create_type SurfaceCreateFunction>
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<std::ostringstream*>(closure);
ss.write(reinterpret_cast<char const *>(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<mapnik::cairo_ptr> 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<cairo_svg_surface_create_for_stream>
{
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<cairo_ps_surface_create_for_stream>
{
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<cairo_pdf_surface_create_for_stream>
{
static constexpr const char * name = "cairo-pdf";
static constexpr const char * ext = ".pdf";
};
#endif
#endif
#if defined(SVG_RENDERER)
struct svg_renderer : renderer_base<std::string>
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<std::string>
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<char>(stream.rdbuf()),(std::istreambuf_iterator<char>()));
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<mapnik::image_rgba8>
struct grid_renderer : raster_renderer_base<mapnik::image_rgba8>
{
static constexpr const char * name = "grid";
@ -335,6 +401,15 @@ private:
using renderer_type = mapnik::util::variant<renderer<agg_renderer>
#if defined(HAVE_CAIRO)
,renderer<cairo_renderer>
#ifdef CAIRO_HAS_SVG_SURFACE
,renderer<cairo_svg_renderer>
#endif
#ifdef CAIRO_HAS_PS_SURFACE
,renderer<cairo_ps_renderer>
#endif
#ifdef CAIRO_HAS_PDF_SURFACE
,renderer<cairo_pdf_renderer>
#endif
#endif
#if defined(SVG_RENDERER)
,renderer<svg_renderer>

View file

@ -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<std::string>());
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<agg_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<cairo_renderer>(output_dir, reference_dir, overwrite));
}
#ifdef CAIRO_HAS_SVG_SURFACE
if (args.count(cairo_svg_renderer::name))
{
renderers.emplace_back(renderer<cairo_svg_renderer>(output_dir, reference_dir, overwrite));
}
#endif
#ifdef CAIRO_HAS_PS_SURFACE
if (args.count(cairo_ps_renderer::name))
{
renderers.emplace_back(renderer<cairo_ps_renderer>(output_dir, reference_dir, overwrite));
}
#endif
#ifdef CAIRO_HAS_PDF_SURFACE
if (args.count(cairo_pdf_renderer::name))
{
renderers.emplace_back(renderer<cairo_pdf_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<svg_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<grid_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;
}