diff --git a/.gitmodules b/.gitmodules index 8ca2e9845..4cca9a4a8 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,8 @@ [submodule "test/data"] path = test/data url = https://github.com/mapnik/test-data.git + branch = master [submodule "test/data-visual"] path = test/data-visual url = https://github.com/mapnik/test-data-visual.git + branch = master \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 49849e340..763444947 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,44 +6,134 @@ Developers: Please commit along with changes. For a complete change history, see the git log. +## 3.0.1 + +Released: July 27th, 2015 + +(Packaged from 28f6f4d) + +#### Summary + +The 3.0.1 fixes a few bugs in geojson parsing, svg parsing, and rendering. It also avoids a potential hang when using `line-geometry-transform` and includes a speedup for text rendering compared to v3.0.0. It is fully back compatibility with v3.0.0 and everyone is encouraged to upgrade. + +- Fixed text placement performance after #2949 (#2963) +- Fixed rendering behavior for `text-minimum-path-length` which regressed in 3.0.0 (#2990) +- Fixed handling of `xml:id` in SVG parsing (#2989) +- Fixed handling of out of range `rx` and `ry` in SVG `rect` (#2991) +- Fixed reporting of envelope from `mapnik::memory_datasource` when new features are added (#2985) +- Fixed parsing of GeoJSON when unknown properties encountered at `FeatureCollection` level (#2983) +- Fixed parsing of GeoJSON when properties contained `{}` (#2964) +- Fixed potential hang due to invalid use of `line-geometry-transform` (6d6cb15) +- Moved unmaintained plugins out of core: `osm`, `occi`, and `rasterlite` (#2980) + ## 3.0.0 -- Added new and experimental `dot` symbolizer for fast rendering of points +Released: July 7th, 2015 + +(Packaged from e6891a0) + +#### Summary + +The 3.0 release is a major milestone for Mapnik and includes many performance and design improvements. The is the first release to provide text shaping using the harfbuzz library. This harfbuzz support unlocks improved rendering and layer for many new languages, particularly SE Asian scripts. The internal storage for working with images and geometries has been made more flexible, faster, and strongly typed. The python bindings that were previously bundled with Mapnik have now been moved to and are versioned independently. + +#### Notice + + - Mapnik 3.0.0 requires a compiler capable of `std=c++11`. + - It is highly recommended you use the `clang++` compiler on both OS X and Linux since it has robust c++11 support lower memory requirements. + +##### Major Changes + - Improved support for International Text (now uses harfbuzz library for text shaping) -- Uses latest c++11 features for better performance (especially map loading) + +- Uses latest C++11 features for better performance (especially map loading) + - Expressions everywhere: all symbolizer properties can now be data driven expressions (with the exception of `face-name` and `fontset-name` on the `TextSymbolizer`). + +- Rewritten geometry storage based on `std::vector` (#2739) + - Separate storage of polygon exterior rings and interior rings to allow for more robust clipping of parts. + - Enforces consistent winding order per OGC spec (exterior rings are CCW, interior CW) + - Reduced memory consumption for layers with many points + - Ability to adapt Mapnik geometries to boost::geometry operations (in a zero-copy way) + - Ability to have i/o grammars for json/wkt work on geometries rather than paths for better efficiency and simpler code + +- Added new and experimental `dot` symbolizer for fast rendering of points + - New functions supported in expressions: `exp`, `sin`, `cos`, `tan`, `atan`, `abs`. + - New constants supported in expressions: `PI`, `DEG_TO_RAD`, `RAD_TO_DEG` + +- Added support for a variety of different grayscale images: + - `mapnik.imageType.null` + - `mapnik.imageType.rgba8` + - `mapnik.imageType.gray8` + - `mapnik.imageType.gray8s` + - `mapnik.imageType.gray16` + - `mapnik.imageType.gray16s` + - `mapnik.imageType.gray32` + - `mapnik.imageType.gray32s` + - `mapnik.imageType.gray32f` + - `mapnik.imageType.gray64` + - `mapnik.imageType.gray64s` + - `mapnik.imageType.gray64f` + - Pattern symbolizers now support SVG input and applying transformations on them dynamically + - Experimental / interface may change: `@variables` can be passed to renderer and evaluated in expressions + - Supports being built with clang++ using `-fvisibility=hidden -flto` for smaller binaries + - Supports being built with Visual Studio 2014 CTP #3 + - Shield icons are now pixel snapped for crisp rendering + - `MarkersSymbolizer` now supports `avoid-edges`, `offset`, `geometry-transform`, `simplify` for `line` placement and two new `placement` options called `vertex-last` and `vertex-first` to place a single marker at the end or beginning of a path. Also `clip` is now respected when rendering markers on a LineString geometry. + - `TextSymbolizer` now supports `smooth`, `simplify`, `halo-opacity`, `halo-comp-op`, and `halo-transform` + - `ShieldSymbolizer` now supports `smooth`, `simplify`, `halo-opacity`, `halo-comp-op`, and `halo-transform` + +- The `text-transform` property of `TextSymbolizer` now supports `reverse` value to flip direction of text. + +- The `TextSymbolizer` now supports `font-feature-settings` for advanced control over Opentype font rendering (https://developer.mozilla.org/en-US/docs/Web/CSS/font-feature-settings) + - New GroupSymbolizer for applying multiple symbolizers in a single layout -Released ... - -(Packaged from ...) - -Summary: TODO - -- PostGIS: Added support for rendering 3D and 4D geometries (previously silently skipped) (#44) - - AGG renderer: fixed geometry offsetting to work after smoothing to produce more consistent results (#2202) - AGG renderer: increased `vertex_dist_epsilon` to ensure nearly coincident points are discarded more readily (#2196) -- GDAL plugin: Added back support for user driven `nodata` on rgb(a) images (#2023) +- GDAL plugin + - Now keeps datasets open for the lifetime of the datasource (rather than per featureset) + - Added back support for user driven `nodata` on rgb(a) images (#2023) + - Allowed nodata to override alpha band if set on rgba images (#2023) + - Added `nodata_tolerance` option to set nearby pixels transparent (has similar effect to the `nearblack` program) (#2023) + - At process exit Mapnik core no longer calls `dlclose` on gdal.input (#2716) -- GDAL plugin: Allowed nodata to override alpha band if set on rgba images (#2023) +- TopoJSON plugin + - Now supporting optional `bbox` property on layer + - Fixed support for reporting correct `feature.id()` + - Now supports `inline` string for passing data from memory + - Faster parsing via static initialization of grammars + - Fix crash on invalid arc index -- GDAL plugin: Added `nodata_tolerance` option to set nearby pixels transparent (has similar effect to the `nearblack` program) (#2023) +- GeoJSON plugin + - Now supporting optional `bbox` property on layer + - Fixed support for reporting correct `feature.id()` + - Now supports `inline` string for passing data from memory + - Faster parsing via static initialization of grammars -- Added support for web fonts: .woff format (#2113) +- SQLite plugin + - Fixed support for handling all column types + +- CSV Plugin + - Added the ability to pass an `extent` in options + +- PostGIS plugin + - Added Async support to - https://github.com/mapnik/mapnik/wiki/PostGIS-Async + - Added support for rendering 3D and 4D geometries (previously silently skipped) (#44) + +- Added support for web fonts: `.woff` format (#2113) - Added missing support for `geometry-transform` in `line-pattern` and `polygon-pattern` symbolizers (#2065) @@ -51,11 +141,9 @@ Summary: TODO - Upgraded unifont to `unifont-6.3.20131020` -- CSV Plugin: added the ability to pass an `extent` in options - - Fixed crash when rendering to cairo context from python (#2031) -- Moved `label-position-tolerance` from unsigned type to double +- Moved `label-position-tolerance` from `unsigned` type to `double` - Added support for more seamless blurring by rendering to a larger internal image to avoid edge effects (#1478) @@ -67,8 +155,6 @@ Summary: TODO - Added `color-to-alpha` `image-filter` to allow for applying alpha in proportion to color similiarity (#2023) -- Added Async support to PostGIS plugin - https://github.com/mapnik/mapnik/wiki/PostGIS-Async - - Fixed alpha handling bug with `comp-op:dst-over` (#1995) - Fixed alpha handling bug with building-fill-opacity (#2011) @@ -133,6 +219,9 @@ are not set. (#1966) - Added `scale-hsla` image-filter that allows scaling colors in HSL color space. RGB is converted to HSL (hue-saturation-lightness) and then each value (and the original alpha value) is stretched based on the specified scaling values. An example syntax is `scale-hsla(0,1,0,1,0,1,0,1)` which means no change because the full range will be kept (0 for lowest, 1 for highest). Other examples are: 1) `scale-hsla(0,0,0,1,0,1,0,1)` which would force all colors to be red in hue in the same way `scale-hsla(1,1,0,1,0,1,0,1)` would, 2) `scale-hsla(0,1,1,1,0,1,0,1)` which would cause all colors to become fully saturated, 3) `scale-hsla(0,1,1,1,0,1,.5,1)` which would force no colors to be any more transparent than half, and 4) `scale-hsla(0,1,1,1,0,1,0,.5)` which would force all colors to be at least half transparent. (#1954) +- The `shapeindex` tool now works correctly with point 3d geometry types + + ## 2.2.0 Released June 3rd, 2013 diff --git a/INSTALL.md b/INSTALL.md index 974a2809c..b88abd2a4 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -5,17 +5,23 @@ Mapnik runs on Linux, OS X, Windows, and BSD systems. To configure and build Mapnik do: ```bash - $ ./configure - $ make +./configure +make ``` To trigger parallel compilation you can pass a JOBS value to make: ```bash - $ JOBS=4 make +JOBS=4 make ``` -(Note that compiling Mapnik needs several GBytes of RAM. If you use parallel compilation it needs more.) +Mapnik needs > 2 GB of RAM to build. If you use parallel compilation it needs more. + +If you are on a system with less memory make sure you only build with one JOB: + +```bash +JOBS=1 make +``` To use a Python interpreter that is not named `python` for your build, do something like the following instead: @@ -178,4 +184,4 @@ tutorials on how to programmatically use Mapnik. ### Contributers -Read docs/contributing.markdown for resources for getting involved with Mapnik development. +Read docs/contributing.md for resources for getting involved with Mapnik development. diff --git a/Makefile b/Makefile index ef02f71f7..3ff06a59c 100755 --- a/Makefile +++ b/Makefile @@ -12,6 +12,25 @@ all: mapnik install: $(PYTHON) scons/scons.py -j$(JOBS) --config=cache --implicit-cache --max-drift=1 install +release: + export MAPNIK_VERSION=$(shell ./utils/mapnik-config/mapnik-config --version) && \ + export TARBALL_NAME="mapnik-v$${MAPNIK_VERSION}" && \ + cd /tmp/ && \ + rm -rf $${TARBALL_NAME} && \ + git clone --depth 1 --branch v$${MAPNIK_VERSION} git@github.com:mapnik/mapnik.git $${TARBALL_NAME} && \ + cd $${TARBALL_NAME} && \ + git checkout "tags/v$${MAPNIK_VERSION}" && \ + git submodule update --depth 1 --init && \ + rm -rf test/data/.git && \ + rm -rf test/data/.gitignore && \ + rm -rf test/data-visual/.git && \ + rm -rf test/data-visual/.gitignore && \ + rm -rf .git && \ + rm -rf .gitignore && \ + cd ../ && \ + tar cjf $${TARBALL_NAME}.tar.bz2 $${TARBALL_NAME}/ && \ + aws s3 cp --acl public-read $${TARBALL_NAME}.tar.bz2 s3://mapnik/dist/v$${MAPNIK_VERSION}/ + python: if [ ! -d ./bindings/python ]; then git clone git@github.com:mapnik/python-mapnik.git --recursive ./bindings/python; else (cd bindings/python && git pull && git submodule update --init); fi; make diff --git a/SConstruct b/SConstruct index f4e910b22..359801da3 100644 --- a/SConstruct +++ b/SConstruct @@ -110,14 +110,7 @@ PLUGINS = { # plugins with external dependencies 'pgraster': {'default':True,'path':None,'inc':'libpq-fe.h','lib':'pq','lang':'C'}, 'gdal': {'default':True,'path':None,'inc':'gdal_priv.h','lib':'gdal','lang':'C++'}, 'ogr': {'default':True,'path':None,'inc':'ogrsf_frmts.h','lib':'gdal','lang':'C++'}, - # configured with custom paths, hence 'path': PREFIX/INCLUDES/LIBS - 'occi': {'default':False,'path':'OCCI','inc':'occi.h','lib':'clntsh','lang':'C++'}, 'sqlite': {'default':True,'path':'SQLITE','inc':'sqlite3.h','lib':'sqlite3','lang':'C'}, - 'rasterlite': {'default':False,'path':'RASTERLITE','inc':['sqlite3.h','rasterlite.h'],'lib':'rasterlite','lang':'C'}, - - # todo: osm plugin does also depend on libxml2 (but there is a separate check for that) - 'osm': {'default':False,'path':None,'inc':None,'lib':None,'lang':'C'}, - # plugins without external dependencies requiring CheckLibWithHeader... 'shape': {'default':True,'path':None,'inc':None,'lib':None,'lang':'C++'}, 'csv': {'default':True,'path':None,'inc':None,'lib':None,'lang':'C++'}, @@ -402,7 +395,7 @@ opts.AddVariables( BoolVariable('FULL_LIB_PATH', 'Embed the full and absolute path to libmapnik when linking ("install_name" on OS X/rpath on Linux)', 'True'), BoolVariable('ENABLE_SONAME', 'Embed a soname in libmapnik on Linux', 'True'), EnumVariable('THREADING','Set threading support','multi', ['multi','single']), - EnumVariable('XMLPARSER','Set xml parser','libxml2', ['libxml2','ptree']), + EnumVariable('XMLPARSER','Set xml parser','ptree', ['libxml2','ptree']), BoolVariable('DEMO', 'Compile demo c++ application', 'True'), BoolVariable('PGSQL2SQLITE', 'Compile and install a utility to convert postgres tables to sqlite', 'False'), BoolVariable('SHAPEINDEX', 'Compile and install a utility to generate shapefile indexes in the custom format (.index) Mapnik supports', 'True'), @@ -1273,18 +1266,19 @@ if not preconfigured: # libxml2 should be optional but is currently not # https://github.com/mapnik/mapnik/issues/913 - if env.get('XML2_LIBS') or env.get('XML2_INCLUDES'): - REQUIRED_LIBSHEADERS.insert(0,['libxml2','libxml/parser.h',True,'C']) - if env.get('XML2_INCLUDES'): - inc_path = env['XML2_INCLUDES'] - env.AppendUnique(CPPPATH = fix_path(inc_path)) - if env.get('XML2_LIBS'): - lib_path = env['XML2_LIBS'] - env.AppendUnique(LIBPATH = fix_path(lib_path)) - elif conf.parse_config('XML2_CONFIG',checks='--cflags'): - env['HAS_LIBXML2'] = True - else: - env['MISSING_DEPS'].append('libxml2') + if env.get('XMLPARSER') and env['XMLPARSER'] == 'libxml2': + if env.get('XML2_LIBS') or env.get('XML2_INCLUDES'): + OPTIONAL_LIBSHEADERS.insert(0,['libxml2','libxml/parser.h',True,'C']) + if env.get('XML2_INCLUDES'): + inc_path = env['XML2_INCLUDES'] + env.AppendUnique(CPPPATH = fix_path(inc_path)) + if env.get('XML2_LIBS'): + lib_path = env['XML2_LIBS'] + env.AppendUnique(LIBPATH = fix_path(lib_path)) + elif conf.parse_config('XML2_CONFIG',checks='--cflags'): + env['HAS_LIBXML2'] = True + else: + env['MISSING_DEPS'].append('libxml2') if not env['HOST']: if conf.CheckHasDlfcn(): @@ -1904,6 +1898,8 @@ if not HELP_REQUESTED: # Build the requested and able-to-be-compiled input plug-ins GDAL_BUILT = False OGR_BUILT = False + POSTGIS_BUILT = False + PGRASTER_BUILT = False for plugin in env['PLUGINS']: if env['PLUGIN_LINKING'] == 'static' or plugin not in env['REQUESTED_PLUGINS']: if os.path.exists('plugins/input/%s.input' % plugin): @@ -1913,11 +1909,17 @@ if not HELP_REQUESTED: if details['lib'] in env['LIBS']: if env['PLUGIN_LINKING'] == 'shared': SConscript('plugins/input/%s/build.py' % plugin) + # hack to avoid breaking on plugins with the same dep if plugin == 'ogr': OGR_BUILT = True if plugin == 'gdal': GDAL_BUILT = True + if plugin == 'postgis': POSTGIS_BUILT = True + if plugin == 'pgraster': PGRASTER_BUILT = True if plugin == 'ogr' or plugin == 'gdal': if GDAL_BUILT and OGR_BUILT: env['LIBS'].remove(details['lib']) + elif plugin == 'postgis' or plugin == 'pgraster': + if POSTGIS_BUILT and PGRASTER_BUILT: + env['LIBS'].remove(details['lib']) else: env['LIBS'].remove(details['lib']) elif not details['lib']: diff --git a/benchmark/build.py b/benchmark/build.py index 8b2049e24..800a9abb5 100644 --- a/benchmark/build.py +++ b/benchmark/build.py @@ -46,6 +46,7 @@ benchmarks = [ "test_offset_converter.cpp", "test_marker_cache.cpp", "test_quad_tree.cpp", + "test_noop_rendering.cpp", # "test_numeric_cast_vs_static_cast.cpp", ] for cpp_test in benchmarks: diff --git a/benchmark/compare_images.hpp b/benchmark/compare_images.hpp index 8fc6b1986..fb0e4ac95 100644 --- a/benchmark/compare_images.hpp +++ b/benchmark/compare_images.hpp @@ -29,19 +29,7 @@ namespace benchmark { image_rgba8 const& dest = util::get(desc_any); image_rgba8 const& src = util::get(src_any); - unsigned int width = src.width(); - unsigned int height = src.height(); - if ((width != dest.width()) || height != dest.height()) return false; - for (unsigned int y = 0; y < height; ++y) - { - const unsigned int* row_from = src.get_row(y); - const unsigned int* row_to = dest.get_row(y); - for (unsigned int x = 0; x < width; ++x) - { - if (row_from[x] != row_to[x]) return false; - } - } - return true; + return compare(dest, src, 0, true) == 0; } } diff --git a/benchmark/run b/benchmark/run index b17f82ad3..f91853995 100755 --- a/benchmark/run +++ b/benchmark/run @@ -55,5 +55,5 @@ run test_offset_converter 10 1000 --threads 1 ./benchmark/out/test_quad_tree \ - --iterations 10000 \ + --iterations 1000 \ --threads 10 diff --git a/benchmark/test_noop_rendering.cpp b/benchmark/test_noop_rendering.cpp new file mode 100644 index 000000000..03d2e675c --- /dev/null +++ b/benchmark/test_noop_rendering.cpp @@ -0,0 +1,53 @@ +#include "bench_framework.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +class test : public benchmark::test_case +{ +public: + test(mapnik::parameters const& params) + : test_case(params) {} + + bool validate() const + { + return true; + } + bool operator()() const + { + mapnik::Map m(256,256,"+init=epsg:3857"); + + mapnik::parameters params; + params["type"]="memory"; + auto ds = std::make_shared(params); + // add whitespace to trigger phony "reprojection" + mapnik::layer lay("layer",m.srs() + " "); + lay.set_datasource(ds); + lay.add_style("style"); + m.add_layer(lay); + // dummy style to ensure that layer is processed + m.insert_style("style",mapnik::feature_type_style()); + // dummy bbox, but "valid" because minx and miny are less + // with an invalid bbox then layer.visible() returns false + // and the initial rendering setup is not run + m.zoom_to_box(mapnik::box2d(-1,-1,0,0)); + for (unsigned i=0;i ren(m,im); + ren.apply(); + } + return true; + } +}; + +BENCHMARK(test,"rendering with reprojection") diff --git a/bootstrap.sh b/bootstrap.sh index 695613d64..5d38b16eb 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -60,8 +60,7 @@ function install_mason_deps() { install freetype 2.5.5 libfreetype & install harfbuzz 0.9.40 libharfbuzz & install jpeg_turbo 1.4.0 libjpeg & - install libxml2 2.9.2 libxml2 & - install libpng 1.6.16 libpng & + install libpng 1.6.17 libpng & install webp 0.4.2 libwebp & install icu 54.1 & install proj 4.8.0 libproj & @@ -117,8 +116,6 @@ PG_INCLUDES = '${MASON_LINKED_REL}/include' PG_LIBS = '${MASON_LINKED_REL}/lib' FREETYPE_INCLUDES = '${MASON_LINKED_REL}/include/freetype2' FREETYPE_LIBS = '${MASON_LINKED_REL}/lib' -XML2_INCLUDES = '${MASON_LINKED_REL}/include/libxml2' -XML2_LIBS = '${MASON_LINKED_REL}/lib' SVG_RENDERER = True CAIRO_INCLUDES = '${MASON_LINKED_REL}/include' CAIRO_LIBS = '${MASON_LINKED_REL}/lib' diff --git a/deps/agg/include/agg_color_gray.h b/deps/agg/include/agg_color_gray.h index f2cd0ff9c..bf300b8f1 100644 --- a/deps/agg/include/agg_color_gray.h +++ b/deps/agg/include/agg_color_gray.h @@ -652,8 +652,8 @@ struct gray16 //-------------------------------------------------------------------- self_type& opacity(double a_) { - if (a_ < 0) a_ = 0; - else if(a_ > 1) a_ = 1; + if (a_ < 0) a = 0; + else if(a_ > 1) a = 1; else a = (value_type)uround(a_ * double(base_mask)); return *this; } diff --git a/docs/contributing.markdown b/docs/contributing.md similarity index 92% rename from docs/contributing.markdown rename to docs/contributing.md index e7f327977..bb486a6db 100644 --- a/docs/contributing.markdown +++ b/docs/contributing.md @@ -44,6 +44,20 @@ We host our code on github.com/mapnik and encourage anyone interested to fork th If you just have a question about the code, or a feature you want to discuss then feel free to create a new issue at github.com/mapnik-support. +## Plugins + +Mapnik has a plugin interface for reading various geodata formats. Plugins are both viable inside of Mapnik core and also able to be used outside of Mapnik. + +Plugins should be developed outside of core except in rare cases when most of the following are met: + + - The plugin has no external dependencies or dependencies are easily installed + - The plugin has excellent unit tests + - The plugin has a maintainer willing to support the plugin over time + - Setup and testing of the plugin is easy on travis.ci (see .travis.yml) + + +Therefore plugins that depend on proprietary, unmaintained, or difficult to test third-party dependencies are not viable for Mapnik core. However they are still likely very valuable for the Mapnik community, so get in touch via https://github.com/mapnik/mapnik-support if we can help you develop your plugin outside of core. + ## Code Philosophy diff --git a/docs/design.markdown b/docs/design.md similarity index 100% rename from docs/design.markdown rename to docs/design.md diff --git a/include/mapnik/gradient.hpp b/include/mapnik/gradient.hpp index eb70be4c2..f8eff6e7d 100644 --- a/include/mapnik/gradient.hpp +++ b/include/mapnik/gradient.hpp @@ -79,8 +79,9 @@ class MAPNIK_DECL gradient public: gradient(); gradient(gradient const& other); - gradient& operator=(const gradient& rhs); - + gradient(gradient && other); + gradient& operator=(gradient rhs); + bool operator==(gradient const& other) const; void set_gradient_type(gradient_e grad); gradient_e get_gradient_type() const; @@ -100,7 +101,7 @@ public: void get_control_points(double &x1, double &y1, double &x2, double &y2) const; private: - void swap(const gradient& other) throw(); + void swap(gradient& other) throw(); }; } diff --git a/include/mapnik/grid/grid_pixfmt.hpp b/include/mapnik/grid/grid_pixfmt.hpp index a26a936c9..fbd6f5173 100644 --- a/include/mapnik/grid/grid_pixfmt.hpp +++ b/include/mapnik/grid/grid_pixfmt.hpp @@ -38,7 +38,7 @@ namespace mapnik { //============================================================blender_gray -template struct blender_gray +template struct blender_gray { using color_type = ColorT; using value_type = typename color_type::value_type; @@ -55,7 +55,7 @@ template struct blender_gray //=====================================================apply_gamma_dir_gray -template class apply_gamma_dir_gray +template class apply_gamma_dir_gray { public: using value_type = typename ColorT::value_type; @@ -74,7 +74,7 @@ private: //=====================================================apply_gamma_inv_gray -template class apply_gamma_inv_gray +template class apply_gamma_inv_gray { public: using value_type = typename ColorT::value_type; @@ -93,7 +93,7 @@ private: //=================================================pixfmt_alpha_blend_gray -template +template class pixfmt_alpha_blend_gray { public: @@ -160,7 +160,7 @@ public: void attach(rbuf_type& rb) { m_rbuf = &rb; } //-------------------------------------------------------------------- - template + template bool attach(PixFmt& pixf, int x1, int y1, int x2, int y2) { agg::rect_i r(x1, y1, x2, y2); @@ -538,10 +538,10 @@ public: } //-------------------------------------------------------------------- - template void for_each_pixel(Function f) + template + void for_each_pixel(Function f) { - unsigned y; - for(y = 0; y < height(); ++y) + for(unsigned y = 0; y < height(); ++y) { row_data r = m_rbuf->row(y); if(r.ptr) @@ -562,19 +562,19 @@ public: } //-------------------------------------------------------------------- - template void apply_gamma_dir(const GammaLut& g) + template void apply_gamma_dir(const GammaLut& g) { for_each_pixel(apply_gamma_dir_gray(g)); } //-------------------------------------------------------------------- - template void apply_gamma_inv(const GammaLut& g) + template void apply_gamma_inv(const GammaLut& g) { for_each_pixel(apply_gamma_inv_gray(g)); } //-------------------------------------------------------------------- - template + template void copy_from(const RenBuf2& from, int xdst, int ydst, int xsrc, int ysrc, @@ -590,7 +590,7 @@ public: } //-------------------------------------------------------------------- - template + template void blend_from_color(const SrcPixelFormatRenderer& from, const color_type& color, int xdst, int ydst, @@ -617,7 +617,7 @@ public: } //-------------------------------------------------------------------- - template + template void blend_from_lut(const SrcPixelFormatRenderer& from, const color_type* color_lut, int xdst, int ydst, diff --git a/include/mapnik/image_filter.hpp b/include/mapnik/image_filter.hpp index 37c332786..8301adefd 100644 --- a/include/mapnik/image_filter.hpp +++ b/include/mapnik/image_filter.hpp @@ -254,10 +254,10 @@ void apply_convolution_3x3(Src const& src_view, Dst & dst_view, Filter const& fi typename Src::x_iterator dst_it = dst_view.row_begin(0); // top row - for (std::size_t x = 0 ; x < static_cast(src_view.width()); ++x) + for (std::ptrdiff_t x = 0 ; x < src_view.width(); ++x) { (*dst_it)[3] = src_loc[loc11][3]; // Dst.a = Src.a - for (std::size_t i = 0; i < 3; ++i) + for (std::ptrdiff_t i = 0; i < 3; ++i) { bits32f p[9]; @@ -275,7 +275,7 @@ void apply_convolution_3x3(Src const& src_view, Dst & dst_view, Filter const& fi p[6] = src_loc[loc02][i]; } - if ( x == static_cast(src_view.width())-1) + if ( x == (src_view.width())-1) { p[5] = p[4]; p[8] = p[7]; @@ -296,15 +296,15 @@ void apply_convolution_3x3(Src const& src_view, Dst & dst_view, Filter const& fi ++dst_it; } // carrige-return - src_loc += point2(-static_cast(src_view.width()),1); + src_loc += point2(-src_view.width(),1); // 1... height-1 rows - for (std::size_t y = 1; y(src_view.height())-1; ++y) + for (std::ptrdiff_t y = 1; y < src_view.height()-1; ++y) { - for (std::size_t x = 0; x < static_cast(src_view.width()); ++x) + for (std::ptrdiff_t x = 0; x < src_view.width(); ++x) { (*dst_it)[3] = src_loc[loc11][3]; // Dst.a = Src.a - for (std::size_t i = 0; i < 3; ++i) + for (std::ptrdiff_t i = 0; i < 3; ++i) { bits32f p[9]; @@ -325,7 +325,7 @@ void apply_convolution_3x3(Src const& src_view, Dst & dst_view, Filter const& fi p[6] = src_loc[loc02][i]; } - if ( x == static_cast(src_view.width()) - 1) + if ( x == (src_view.width()) - 1) { p[2] = p[1]; p[5] = p[4]; @@ -343,15 +343,15 @@ void apply_convolution_3x3(Src const& src_view, Dst & dst_view, Filter const& fi ++src_loc.x(); } // carrige-return - src_loc += point2(-static_cast(src_view.width()),1); + src_loc += point2(-src_view.width(),1); } // bottom row - //src_loc = src_view.xy_at(0,static_cast(src_view.height())-1); - for (std::size_t x = 0 ; x < static_cast(src_view.width()); ++x) + //src_loc = src_view.xy_at(0,src_view.height()-1); + for (std::ptrdiff_t x = 0 ; x < src_view.width(); ++x) { (*dst_it)[3] = src_loc[loc11][3]; // Dst.a = Src.a - for (std::size_t i = 0; i < 3; ++i) + for (std::ptrdiff_t i = 0; i < 3; ++i) { bits32f p[9]; @@ -369,7 +369,7 @@ void apply_convolution_3x3(Src const& src_view, Dst & dst_view, Filter const& fi p[3] = src_loc[loc01][i]; } - if ( x == static_cast(src_view.width())-1) + if ( x == (src_view.width())-1) { p[2] = p[1]; p[5] = p[4]; @@ -431,10 +431,10 @@ void apply_filter(Src & src, color_to_alpha const& op) double cr = static_cast(op.color.red())/255.0; double cg = static_cast(op.color.green())/255.0; double cb = static_cast(op.color.blue())/255.0; - for (std::size_t y=0; y(src_view.height()); ++y) + for (std::ptrdiff_t y = 0; y < src_view.height(); ++y) { rgba8_view_t::x_iterator src_it = src_view.row_begin(static_cast(y)); - for (std::size_t x=0; x(src_view.width()); ++x) + for (std::ptrdiff_t x = 0; x < src_view.width(); ++x) { uint8_t & r = get_color(src_it[x], red_t()); uint8_t & g = get_color(src_it[x], green_t()); @@ -485,17 +485,17 @@ template void apply_filter(Src & src, colorize_alpha const& op) { using namespace boost::gil; - std::size_t size = op.size(); + std::ptrdiff_t size = op.size(); if (op.size() == 1) { // no interpolation if only one stop mapnik::filter::color_stop const& stop = op[0]; mapnik::color const& c = stop.color; rgba8_view_t src_view = rgba8_view(src); - for (std::size_t y=0; y(src_view.height()); ++y) + for (std::ptrdiff_t y = 0; y < src_view.height(); ++y) { rgba8_view_t::x_iterator src_it = src_view.row_begin(static_cast(y)); - for (std::size_t x=0; x(src_view.width()); ++x) + for (std::ptrdiff_t x = 0; x < src_view.width(); ++x) { uint8_t & r = get_color(src_it[x], red_t()); uint8_t & g = get_color(src_it[x], green_t()); @@ -533,10 +533,10 @@ void apply_filter(Src & src, colorize_alpha const& op) if (grad_lut.build_lut()) { rgba8_view_t src_view = rgba8_view(src); - for (std::size_t y=0; y(src_view.height()); ++y) + for (std::ptrdiff_t y = 0; y < src_view.height(); ++y) { rgba8_view_t::x_iterator src_it = src_view.row_begin(static_cast(y)); - for (std::size_t x=0; x(src_view.width()); ++x) + for (std::ptrdiff_t x = 0; x < src_view.width(); ++x) { uint8_t & r = get_color(src_it[x], red_t()); uint8_t & g = get_color(src_it[x], green_t()); @@ -598,10 +598,10 @@ void apply_filter(Src & src, scale_hsla const& transform) if (tinting || set_alpha) { rgba8_view_t src_view = rgba8_view(src); - for (std::size_t y=0; y(src_view.height()); ++y) + for (std::ptrdiff_t y = 0; y < src_view.height(); ++y) { rgba8_view_t::x_iterator src_it = src_view.row_begin(static_cast(y)); - for (std::size_t x=0; x(src_view.width()); ++x) + for (std::ptrdiff_t x = 0; x < src_view.width(); ++x) { uint8_t & r = get_color(src_it[x], red_t()); uint8_t & g = get_color(src_it[x], green_t()); @@ -681,10 +681,10 @@ void apply_filter(Src & src, gray const& /*op*/) rgba8_view_t src_view = rgba8_view(src); - for (std::size_t y=0; y(src_view.height()); ++y) + for (std::ptrdiff_t y = 0; y < src_view.height(); ++y) { rgba8_view_t::x_iterator src_it = src_view.row_begin(static_cast(y)); - for (std::size_t x=0; x(src_view.width()); ++x) + for (std::ptrdiff_t x = 0; x < src_view.width(); ++x) { // formula taken from boost/gil/color_convert.hpp:rgb_to_luminance uint8_t & r = get_color(src_it[x], red_t()); @@ -699,7 +699,7 @@ void apply_filter(Src & src, gray const& /*op*/) template void x_gradient_impl(Src const& src_view, Dst const& dst_view) { - for (std::size_t y=0; y(src_view.height()); ++y) + for (std::ptrdiff_t y = 0; y < src_view.height(); ++y) { typename Src::x_iterator src_it = src_view.row_begin(static_cast(y)); typename Dst::x_iterator dst_it = dst_view.row_begin(static_cast(y)); @@ -708,13 +708,13 @@ void x_gradient_impl(Src const& src_view, Dst const& dst_view) dst_it[0][1] = 128 + (src_it[0][1] - src_it[1][1]) / 2; dst_it[0][2] = 128 + (src_it[0][2] - src_it[1][2]) / 2; - dst_it[dst_view.width()-1][0] = 128 + (src_it[static_cast(src_view.width())-2][0] - src_it[static_cast(src_view.width())-1][0]) / 2; - dst_it[dst_view.width()-1][1] = 128 + (src_it[static_cast(src_view.width())-2][1] - src_it[static_cast(src_view.width())-1][1]) / 2; - dst_it[dst_view.width()-1][2] = 128 + (src_it[static_cast(src_view.width())-2][2] - src_it[static_cast(src_view.width())-1][2]) / 2; + dst_it[dst_view.width()-1][0] = 128 + (src_it[(src_view.width())-2][0] - src_it[(src_view.width())-1][0]) / 2; + dst_it[dst_view.width()-1][1] = 128 + (src_it[(src_view.width())-2][1] - src_it[(src_view.width())-1][1]) / 2; + dst_it[dst_view.width()-1][2] = 128 + (src_it[(src_view.width())-2][2] - src_it[(src_view.width())-1][2]) / 2; - dst_it[0][3] = dst_it[static_cast(src_view.width())-1][3] = 255; + dst_it[0][3] = dst_it[(src_view.width())-1][3] = 255; - for (std::size_t x=1; x(src_view.width())-1; ++x) + for (std::ptrdiff_t x = 1; x < src_view.width()-1; ++x) { dst_it[x][0] = 128 + (src_it[x-1][0] - src_it[x+1][0]) / 2; dst_it[x][1] = 128 + (src_it[x-1][1] - src_it[x+1][1]) / 2; @@ -746,10 +746,10 @@ void apply_filter(Src & src, invert const& /*op*/) rgba8_view_t src_view = rgba8_view(src); - for (std::size_t y=0; y(src_view.height()); ++y) + for (std::ptrdiff_t y = 0; y < src_view.height(); ++y) { rgba8_view_t::x_iterator src_it = src_view.row_begin(static_cast(y)); - for (std::size_t x=0; x(src_view.width()); ++x) + for (std::ptrdiff_t x = 0; x < src_view.width(); ++x) { // we only work with premultiplied source, // thus all color values must be <= alpha diff --git a/include/mapnik/image_null.hpp b/include/mapnik/image_null.hpp index c318c766c..67dbbd358 100644 --- a/include/mapnik/image_null.hpp +++ b/include/mapnik/image_null.hpp @@ -50,7 +50,6 @@ public: image(image const&) {} image(image &&) noexcept {} image& operator=(image) { return *this; } - imageconst& operator=(image const& rhs) const { return rhs; } bool operator==(image const&) const { return true; } bool operator<(image const&) const { return false; } diff --git a/include/mapnik/json/extract_bounding_box_grammar_impl.hpp b/include/mapnik/json/extract_bounding_box_grammar_impl.hpp index c6fd321ba..fc08584fe 100644 --- a/include/mapnik/json/extract_bounding_box_grammar_impl.hpp +++ b/include/mapnik/json/extract_bounding_box_grammar_impl.hpp @@ -63,10 +63,9 @@ extract_bounding_box_grammar::extract_bounding_box_gramm start = features(_r1) ; - features = iter_pos[_a = _1] >> -(lit('{') >> -lit("\"type\"") - >> lit(':') >> lit("\"FeatureCollection\"") - >> *(lit(',') >> (json.key_value - lit("\"features\""))) - >> lit(',') >> lit("\"features\"") + features = iter_pos[_a = _1] >> -(lit('{') + >> *((json.key_value - lit("\"features\"")) >> lit(',')) + >> lit("\"features\"") >> lit(':')) >> lit('[') >> (feature(_r1,_a) % lit(',')) >> lit(']') ; @@ -78,6 +77,8 @@ extract_bounding_box_grammar::extract_bounding_box_gramm | coords[_b = _1] | + json.string_ + | char_))][push_box(_r1, _r2, _b, _1)] ; diff --git a/include/mapnik/json/geometry_generator_grammar_impl.hpp b/include/mapnik/json/geometry_generator_grammar_impl.hpp index c6968cfc0..f94498065 100644 --- a/include/mapnik/json/geometry_generator_grammar_impl.hpp +++ b/include/mapnik/json/geometry_generator_grammar_impl.hpp @@ -23,13 +23,21 @@ // mapnik #include #include +#include // boost +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#pragma GCC diagnostic ignored "-Wunused-local-typedef" +#pragma GCC diagnostic ignored "-Wshadow" +#pragma GCC diagnostic ignored "-Wsign-conversion" +#pragma GCC diagnostic ignored "-Wconversion" #include #include #include #include #include +#pragma GCC diagnostic pop namespace mapnik { namespace json { diff --git a/include/mapnik/label_collision_detector.hpp b/include/mapnik/label_collision_detector.hpp index 6dce9ff2c..74a76be7f 100644 --- a/include/mapnik/label_collision_detector.hpp +++ b/include/mapnik/label_collision_detector.hpp @@ -212,12 +212,18 @@ public: void insert(box2d const& box) { - tree_.insert(label(box), box); + if (tree_.extent().intersects(box)) + { + tree_.insert(label(box), box); + } } void insert(box2d const& box, mapnik::value_unicode_string const& text) { - tree_.insert(label(box, text), box); + if (tree_.extent().intersects(box)) + { + tree_.insert(label(box, text), box); + } } void clear() diff --git a/include/mapnik/memory_datasource.hpp b/include/mapnik/memory_datasource.hpp index a6ab9188c..cd2b61130 100644 --- a/include/mapnik/memory_datasource.hpp +++ b/include/mapnik/memory_datasource.hpp @@ -56,6 +56,7 @@ private: datasource::datasource_t type_; bool bbox_check_; mutable box2d extent_; + mutable bool dirty_extent_ = true; }; } diff --git a/include/mapnik/offset_converter.hpp b/include/mapnik/offset_converter.hpp index f2c585c74..51d43e347 100644 --- a/include/mapnik/offset_converter.hpp +++ b/include/mapnik/offset_converter.hpp @@ -53,7 +53,7 @@ struct offset_converter , pre_first_(vertex2d::no_init) , pre_(vertex2d::no_init) , cur_(vertex2d::no_init) - {} + {} enum status { @@ -238,8 +238,8 @@ private: */ static void displace(vertex2d & v, double dx, double dy, double a) { - v.x += dx * std::cos(a) + dy * std::sin(a); - v.y += dx * std::sin(a) - dy * std::cos(a); + v.x += dx * std::cos(a) - dy * std::sin(a); + v.y += dx * std::sin(a) + dy * std::cos(a); } /** @@ -247,8 +247,8 @@ private: */ void displace(vertex2d & v, double a) const { - v.x += offset_ * std::sin(a); - v.y -= offset_ * std::cos(a); + v.x -= offset_ * std::sin(a); + v.y += offset_ * std::cos(a); } /** @@ -256,8 +256,8 @@ private: */ void displace(vertex2d & v, vertex2d const& u, double a) const { - v.x = u.x + offset_ * std::sin(a); - v.y = u.y - offset_ * std::cos(a); + v.x = u.x - offset_ * std::sin(a); + v.y = u.y + offset_ * std::cos(a); v.cmd = u.cmd; } @@ -266,16 +266,27 @@ private: double sa = offset_ * std::sin(a); double ca = offset_ * std::cos(a); double h = std::tan(0.5 * (b - a)); - if (h > 1.5) + double hsa = h * sa; + double hca = h * ca; + double abs_offset = std::abs(offset_); + if (hsa > 1.0 * abs_offset) { - h = 1.5; + hsa = 1.0 * abs_offset; } - else if (h < -1.5) + else if (hsa < -1.0 * abs_offset) { - h = -1.5; + hsa = -1.0 * abs_offset; } - v.x = v.x + sa + h * ca; - v.y = v.y - ca + h * sa; + if (hca > 1.0 * abs_offset) + { + hca = 1.0 * abs_offset; + } + else if (hca < -1.0 * abs_offset) + { + hca = -1.0 * abs_offset; + } + v.x = v.x - sa - hca; + v.y = v.y + ca - hsa; } status init_vertices() @@ -288,31 +299,51 @@ private: vertex2d v1(vertex2d::no_init); vertex2d v2(vertex2d::no_init); vertex2d w(vertex2d::no_init); + vertex2d start(vertex2d::no_init); vertex2d start_v2(vertex2d::no_init); std::vector points; std::vector close_points; bool is_polygon = false; std::size_t cpt = 0; v0.cmd = geom_.vertex(&v0.x, &v0.y); - v1.x = v0.x; - v1.y = v0.y; - v1.cmd = v0.cmd; + v1 = v0; // PUSH INITIAL - points.push_back(vertex2d(v0.x, v0.y, v0.cmd)); + points.push_back(v0); + if (v0.cmd == SEG_END) // not enough vertices in source + { + return status_ = process; + } + start = v0; while ((v0.cmd = geom_.vertex(&v0.x, &v0.y)) != SEG_END) { - points.push_back(vertex2d(v0.x, v0.y, v0.cmd)); if (v0.cmd == SEG_CLOSE) { is_polygon = true; - close_points.push_back(vertex2d(v1.x, v1.y, v1.cmd)); + auto & prev = points.back(); + if (prev.x == start.x && prev.y == start.y) + { + prev.x = v0.x; // hack + prev.y = v0.y; + prev.cmd = SEG_CLOSE; // account for dupes (line_to(move_to) + close_path) in agg poly clipper + std::size_t size = points.size(); + if (size > 1) close_points.push_back(points[size - 2]); + else close_points.push_back(prev); + continue; + } + else + { + close_points.push_back(v1); + } } - v1.x = v0.x; - v1.y = v0.y; - v1.cmd = v0.cmd; + else if (v0.cmd == SEG_MOVETO) + { + start = v0; + } + v1 = v0; + points.push_back(v0); } // Push SEG_END - points.push_back(vertex2d(v0.x, v0.y, v0.cmd)); + points.push_back(vertex2d(v0.x,v0.y,SEG_END)); std::size_t i = 0; v1 = points[i++]; v2 = points[i++]; @@ -326,17 +357,28 @@ private: } double angle_a = 0; + // The vector parts from v1 to v0. + double v_x1x0 = 0; + double v_y1y0 = 0; + // The vector parts from v1 to v2; + double v_x1x2 = v2.x - v1.x; + double v_y1y2 = v2.y - v1.y; + if (is_polygon) { - double x = v1.x - close_points[cpt].x; - double y = v1.y - close_points[cpt].y; + v_x1x0 = close_points[cpt].x - v1.x; + v_y1y0 = close_points[cpt].y - v1.y; cpt++; - x = std::abs(x) < std::numeric_limits::epsilon() ? 0 : x; - y = std::abs(y) < std::numeric_limits::epsilon() ? 0 : y; - angle_a = std::atan2(y, x); + angle_a = std::atan2(-v_y1y0, -v_x1x0); } - double angle_b = std::atan2((v2.y - v1.y), (v2.x - v1.x)); + // dot product + double dot; + // determinate + double det; + double angle_b = std::atan2(v_y1y2, v_x1x2); + // Angle between the two vectors double joint_angle; + double curve_angle; if (!is_polygon) { @@ -346,33 +388,28 @@ private: } else { - joint_angle = explement_reflex_angle(angle_b - angle_a); + dot = v_x1x0 * v_x1x2 + v_y1y0 * v_y1y2; // dot product + det = v_x1x0 * v_y1y2 - v_y1y0 * v_x1x2; // determinant + + joint_angle = std::atan2(det, dot); // atan2(y, x) or atan2(sin, cos) + if (joint_angle < 0) joint_angle = joint_angle + 2 * M_PI; + joint_angle = std::fmod(joint_angle, 2 * M_PI); + + if (offset_ > 0.0) + { + joint_angle = 2 * M_PI - joint_angle; + } - double half_turns = half_turn_segments_ * std::fabs(joint_angle); int bulge_steps = 0; - if (offset_ < 0.0) + if (std::abs(joint_angle) > M_PI) { - if (joint_angle > 0.0) - { - joint_angle = joint_angle - 2 * M_PI; - } - else - { - bulge_steps = 1 + static_cast(std::floor(half_turns / M_PI)); - } - } - else - { - if (joint_angle < 0.0) - { - joint_angle = joint_angle + 2 * M_PI; - } - else - { - bulge_steps = 1 + static_cast(std::floor(half_turns / M_PI)); - } + curve_angle = explement_reflex_angle(angle_b - angle_a); + // Bulge steps should be determined by the inverse of the joint angle. + double half_turns = half_turn_segments_ * std::fabs(curve_angle); + bulge_steps = 1 + static_cast(std::floor(half_turns / M_PI)); } + if (bulge_steps == 0) { displace2(v1, angle_a, angle_b); @@ -415,18 +452,15 @@ private: v1.y = start_.y; if (cpt < close_points.size()) { - double x = v1.x - close_points[cpt].x; - double y = v1.y - close_points[cpt].y; - x = std::abs(x) < std::numeric_limits::epsilon() ? 0.0 : x; - y = std::abs(y) < std::numeric_limits::epsilon() ? 0.0 : y; - angle_b = std::atan2(y,x); + v_x1x2 = v1.x - close_points[cpt].x; + v_y1y2 = v1.y - close_points[cpt].y; cpt++; } + start_v2.x = v2.x; + start_v2.y = v2.y; } - start_v2.x = v2.x; - start_v2.y = v2.y; } - if (v2.cmd == SEG_MOVETO) + if (is_polygon && v2.cmd == SEG_MOVETO) { start_.x = v2.x; start_.y = v2.y; @@ -445,34 +479,39 @@ private: v2.x = start_.x; v2.y = start_.y; } - angle_a = angle_b; - angle_b = std::atan2((v2.y - v1.y), (v2.x - v1.x)); - joint_angle = explement_reflex_angle(angle_b - angle_a); - double half_turns = half_turn_segments_ * std::fabs(joint_angle); + // Switch the previous vector's direction as the origin has changed + v_x1x0 = -v_x1x2; + v_y1y0 = -v_y1y2; + // Calculate new angle_a + angle_a = std::atan2(v_y1y2, v_x1x2); + + // Calculate the new vector + v_x1x2 = v2.x - v1.x; + v_y1y2 = v2.y - v1.y; + // Calculate the new angle_b + angle_b = std::atan2(v_y1y2, v_x1x2); + + dot = v_x1x0 * v_x1x2 + v_y1y0 * v_y1y2; // dot product + det = v_x1x0 * v_y1y2 - v_y1y0 * v_x1x2; // determinant + + joint_angle = std::atan2(det, dot); // atan2(y, x) or atan2(sin, cos) + if (joint_angle < 0) joint_angle = joint_angle + 2 * M_PI; + joint_angle = std::fmod(joint_angle, 2 * M_PI); + + if (offset_ > 0.0) + { + joint_angle = 2 * M_PI - joint_angle; + } + int bulge_steps = 0; - if (offset_ < 0.0) + if (std::abs(joint_angle) > M_PI) { - if (joint_angle > 0.0) - { - joint_angle = joint_angle - 2 * M_PI; - } - else - { - bulge_steps = 1 + static_cast(std::floor(half_turns / M_PI)); - } - } - else - { - if (joint_angle < 0.0) - { - joint_angle = joint_angle + 2 * M_PI; - } - else - { - bulge_steps = 1 + static_cast(std::floor(half_turns / M_PI)); - } + curve_angle = explement_reflex_angle(angle_b - angle_a); + // Bulge steps should be determined by the inverse of the joint angle. + double half_turns = half_turn_segments_ * std::fabs(curve_angle); + bulge_steps = 1 + static_cast(std::floor(half_turns / M_PI)); } #ifdef MAPNIK_LOG @@ -516,10 +555,9 @@ private: displace(w, v1, angle_a); w.cmd = SEG_LINETO; push_vertex(w); - for (int s = 0; ++s < bulge_steps;) { - displace(w, v1, angle_a + (joint_angle * s) / bulge_steps); + displace(w, v1, angle_a + (curve_angle * s) / bulge_steps); w.cmd = SEG_LINETO; push_vertex(w); } diff --git a/include/mapnik/renderer_common/process_group_symbolizer.hpp b/include/mapnik/renderer_common/process_group_symbolizer.hpp index 006d1a9f3..d593b8ead 100644 --- a/include/mapnik/renderer_common/process_group_symbolizer.hpp +++ b/include/mapnik/renderer_common/process_group_symbolizer.hpp @@ -211,7 +211,7 @@ void render_offset_placements(placements_list const& placements, pixel_position const& offset, F render_text) { - for (glyph_positions_ptr glyphs : placements) + for (auto const& glyphs : placements) { // move the glyphs to the correct offset pixel_position base_point = glyphs->get_base_point(); diff --git a/include/mapnik/renderer_common/process_markers_symbolizer.hpp b/include/mapnik/renderer_common/process_markers_symbolizer.hpp index 9d0852675..16e18ecf2 100644 --- a/include/mapnik/renderer_common/process_markers_symbolizer.hpp +++ b/include/mapnik/renderer_common/process_markers_symbolizer.hpp @@ -118,9 +118,9 @@ struct render_marker_symbolizer_visitor if (clip) // optional clip (default: true) { geometry::geometry_types type = geometry::geometry_type(feature_.get_geometry()); - if (type == geometry::geometry_types::Polygon) + if (type == geometry::geometry_types::Polygon || type == geometry::geometry_types::MultiPolygon) converter.template set(); - else if (type == geometry::geometry_types::LineString) + else if (type == geometry::geometry_types::LineString || type == geometry::geometry_types::MultiLineString) converter.template set(); } @@ -223,9 +223,9 @@ struct render_marker_symbolizer_visitor if (clip) // optional clip (default: true) { geometry::geometry_types type = geometry::geometry_type(feature_.get_geometry()); - if (type == geometry::geometry_types::Polygon) + if (type == geometry::geometry_types::Polygon || type == geometry::geometry_types::MultiPolygon) converter.template set(); - else if (type == geometry::geometry_types::LineString) + else if (type == geometry::geometry_types::LineString || type == geometry::geometry_types::MultiLineString) converter.template set(); } converter.template set(); //always transform diff --git a/include/mapnik/svg/svg_parser.hpp b/include/mapnik/svg/svg_parser.hpp index ef9eafc06..63caacd8b 100644 --- a/include/mapnik/svg/svg_parser.hpp +++ b/include/mapnik/svg/svg_parser.hpp @@ -38,15 +38,18 @@ namespace mapnik { namespace svg { class MAPNIK_DECL svg_parser : private util::noncopyable { + using error_message_container = std::vector ; public: explicit svg_parser(svg_converter_type & path); ~svg_parser(); - void parse(std::string const& filename); - void parse_from_string(std::string const& svg); + error_message_container const& error_messages() const; + bool parse(std::string const& filename); + bool parse_from_string(std::string const& svg); svg_converter_type & path_; bool is_defs_; std::map gradient_map_; std::pair temporary_gradient_; + error_message_container error_messages_; }; }} diff --git a/plugins/input/osm/dataset_deliverer.h b/include/mapnik/svg/svg_parser_exception.hpp similarity index 63% rename from plugins/input/osm/dataset_deliverer.h rename to include/mapnik/svg/svg_parser_exception.hpp index dde269b65..89139da88 100644 --- a/plugins/input/osm/dataset_deliverer.h +++ b/include/mapnik/svg/svg_parser_exception.hpp @@ -2,7 +2,7 @@ * * This file is part of Mapnik (c++ mapping toolkit) * - * Copyright (C) 2011 Artem Pavlenko + * Copyright (C) 2015 Artem Pavlenko * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -20,28 +20,35 @@ * *****************************************************************************/ -#ifndef DATASET_DELIVERER_H -#define DATASET_DELIVERER_H +#ifndef MAPNIK_SVG_PARSER_EXCEPTION_HPP +#define MAPNIK_SVG_PARSER_EXCEPTION_HPP -#include "osm.h" -#include +// mapnik +#include +#include -using namespace std; +// stl +#include -class dataset_deliverer +namespace mapnik { namespace svg { + +class MAPNIK_DECL svg_parser_exception : public std::exception { -private: - static osm_dataset* dataset; - static std::string last_bbox; - static std::string last_filename; - public: - static osm_dataset *load_from_file(const string&, const string&); + svg_parser_exception(std::string const& message) + : message_(message) {} - static void release() + ~svg_parser_exception() throw() {} + + virtual const char* what() const throw() { - delete dataset; + return message_.c_str(); } +private: + std::string message_; }; -#endif // DATASET_DELIVERER_H +}} + + +#endif // MAPNIK_SVG_PARSER_EXCEPTION_HPP diff --git a/include/mapnik/svg/svg_path_adapter.hpp b/include/mapnik/svg/svg_path_adapter.hpp index 957717166..beb8222cb 100644 --- a/include/mapnik/svg/svg_path_adapter.hpp +++ b/include/mapnik/svg/svg_path_adapter.hpp @@ -45,7 +45,7 @@ using namespace agg; template class path_adapter : util::noncopyable { public: - using container_type = VertexContainer ; + using container_type = VertexContainer; using self_type = path_adapter; //-------------------------------------------------------------------- diff --git a/include/mapnik/text/face.hpp b/include/mapnik/text/face.hpp index 9c50bebcc..60a37e692 100644 --- a/include/mapnik/text/face.hpp +++ b/include/mapnik/text/face.hpp @@ -44,7 +44,7 @@ extern "C" namespace mapnik { -class font_face : util::noncopyable +class MAPNIK_DECL font_face : util::noncopyable { public: font_face(FT_Face face); diff --git a/include/mapnik/text/glyph_positions.hpp b/include/mapnik/text/glyph_positions.hpp index 554e40667..e956edda8 100644 --- a/include/mapnik/text/glyph_positions.hpp +++ b/include/mapnik/text/glyph_positions.hpp @@ -26,6 +26,7 @@ #include #include #include +#include // agg #include "agg_trans_affine.h" @@ -37,8 +38,6 @@ namespace mapnik { -struct glyph_info; - struct glyph_position { glyph_position(glyph_info const& _glyph, pixel_position const& _pos, rotation const& _rot) @@ -82,7 +81,6 @@ public: void set_marker(marker_info_ptr marker, pixel_position const& marker_pos); marker_info_ptr get_marker() const; pixel_position const& marker_pos() const; - box2d const & bbox() const; private: std::vector data_; pixel_position base_point_; @@ -90,7 +88,7 @@ private: pixel_position marker_pos_; box2d bbox_; }; -using glyph_positions_ptr = std::shared_ptr; +using glyph_positions_ptr = std::unique_ptr; using placements_list = std::list; } diff --git a/include/mapnik/text/harfbuzz_shaper.hpp b/include/mapnik/text/harfbuzz_shaper.hpp index 8c4b8309f..ac436a166 100644 --- a/include/mapnik/text/harfbuzz_shaper.hpp +++ b/include/mapnik/text/harfbuzz_shaper.hpp @@ -28,7 +28,9 @@ #include #include #include +#include #include +#include // stl #include @@ -38,6 +40,9 @@ #include #include +// icu +#include + namespace mapnik { diff --git a/include/mapnik/text/icu_shaper.hpp b/include/mapnik/text/icu_shaper.hpp index dedeafd81..bcd822b27 100644 --- a/include/mapnik/text/icu_shaper.hpp +++ b/include/mapnik/text/icu_shaper.hpp @@ -27,6 +27,10 @@ #include #include #include +#include +#include +#include +#include #include // stl @@ -59,12 +63,11 @@ static void shape_text(text_line & line, UErrorCode err = U_ZERO_ERROR; mapnik::value_unicode_string shaped; mapnik::value_unicode_string reordered; - unsigned char_index = 0; for (auto const& text_item : list) { - face_set_ptr face_set = font_manager.get_face_set(text_item.format->face_name, text_item.format->fontset); - double size = text_item.format->text_size * scale_factor; + face_set_ptr face_set = font_manager.get_face_set(text_item.format_->face_name, text_item.format_->fontset); + double size = text_item.format_->text_size * scale_factor; face_set->set_unscaled_character_sizes(); for (auto const& face : *face_set) { @@ -87,33 +90,35 @@ static void shape_text(text_line & line, std::size_t num_chars = static_cast(num_char); shaped.releaseBuffer(length); bool shaped_status = true; + double max_glyph_height = 0; if (U_SUCCESS(err) && (num_chars == length)) { + unsigned char_index = 0; U_NAMESPACE_QUALIFIER StringCharacterIterator iter(shaped); for (iter.setToStart(); iter.hasNext();) { UChar ch = iter.nextPostInc(); - glyph_info tmp; - tmp.glyph_index = FT_Get_Char_Index(face->get_face(), ch); - tmp.offset.clear(); - if (tmp.glyph_index == 0) + auto codepoint = FT_Get_Char_Index(face->get_face(), ch); + glyph_info g(codepoint,char_index,text_item.format_); + //g.offset.clear(); + if (g.glyph_index == 0) { shaped_status = false; break; } - if (face->glyph_dimensions(tmp)) + if (face->glyph_dimensions(g)) { - tmp.char_index = char_index; - tmp.face = face; - tmp.format = text_item.format; - tmp.scale_multiplier = size / face->get_face()->units_per_EM; - width_map[char_index++] += tmp.advance(); - line.add_glyph(tmp, scale_factor); + g.face = face; + g.scale_multiplier = size / face->get_face()->units_per_EM; + double tmp_height = g.height(); + if (tmp_height > max_glyph_height) max_glyph_height = tmp_height; + width_map[char_index++] += g.advance(); + line.add_glyph(std::move(g), scale_factor); } } } if (!shaped_status) continue; - line.update_max_char_height(face->get_char_height(size)); + line.update_max_char_height(max_glyph_height); return; } } diff --git a/include/mapnik/text/itemizer.hpp b/include/mapnik/text/itemizer.hpp index fe036ad24..b8589448d 100644 --- a/include/mapnik/text/itemizer.hpp +++ b/include/mapnik/text/itemizer.hpp @@ -27,6 +27,7 @@ #include #include #include +#include // stl #include @@ -41,7 +42,7 @@ namespace mapnik { -struct text_item : util::noncopyable +struct MAPNIK_DECL text_item : util::noncopyable { text_item(unsigned s, unsigned e, @@ -71,7 +72,7 @@ struct text_item : util::noncopyable // - format // - script (http://en.wikipedia.org/wiki/Scripts_in_Unicode) -class text_itemizer +class MAPNIK_DECL text_itemizer : util::noncopyable { public: text_itemizer(); diff --git a/include/mapnik/text/placement_finder.hpp b/include/mapnik/text/placement_finder.hpp index f3ffd512a..0460b2c52 100644 --- a/include/mapnik/text/placement_finder.hpp +++ b/include/mapnik/text/placement_finder.hpp @@ -26,7 +26,6 @@ #include #include #include -#include #include #include #include @@ -75,7 +74,7 @@ private: // Checks for collision. bool collision(box2d const& box, const value_unicode_string &repeat_key, bool line_placement) const; // Adds marker to glyph_positions and to collision detector. Returns false if there is a collision. - bool add_marker(glyph_positions_ptr glyphs, pixel_position const& pos) const; + bool add_marker(glyph_positions_ptr & glyphs, pixel_position const& pos) const; // Maps upright==auto, left-only and right-only to left,right to simplify processing. // angle = angle of at start of line (to estimate best option for upright==auto) text_upright_e simplify_upright(text_upright_e upright, double angle) const; diff --git a/include/mapnik/text/placement_finder_impl.hpp b/include/mapnik/text/placement_finder_impl.hpp index 739238924..1b8f84147 100644 --- a/include/mapnik/text/placement_finder_impl.hpp +++ b/include/mapnik/text/placement_finder_impl.hpp @@ -19,23 +19,19 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ -//mapnik -#include -#include -#include -#include + +// mapnik +//#include +//#include #include #include #include -#include +//#include #include #include +#include -// agg -#include "agg_conv_clip_polyline.h" - -// stl -#include +#include namespace mapnik { diff --git a/include/mapnik/text/renderer.hpp b/include/mapnik/text/renderer.hpp index a0b521d30..1b6bac112 100644 --- a/include/mapnik/text/renderer.hpp +++ b/include/mapnik/text/renderer.hpp @@ -56,8 +56,8 @@ public: text_renderer (halo_rasterizer_e rasterizer, composite_mode_e comp_op = src_over, composite_mode_e halo_comp_op = src_over, - double scale_factor=1.0, - stroker_ptr stroker=stroker_ptr()); + double scale_factor = 1.0, + stroker_ptr stroker = stroker_ptr()); void set_transform(agg::trans_affine const& transform); void set_halo_transform(agg::trans_affine const& halo_transform); protected: diff --git a/include/mapnik/text/scrptrun.hpp b/include/mapnik/text/scrptrun.hpp index e4c607f6c..67942b0a8 100644 --- a/include/mapnik/text/scrptrun.hpp +++ b/include/mapnik/text/scrptrun.hpp @@ -17,9 +17,9 @@ #ifndef __SCRPTRUN_H #define __SCRPTRUN_H -#include "unicode/utypes.h" -#include "unicode/uobject.h" -#include "unicode/uscript.h" +#include +#include +#include struct ScriptRecord { diff --git a/include/mapnik/text/symbolizer_helpers.hpp b/include/mapnik/text/symbolizer_helpers.hpp index 714d566ba..8a44bddfc 100644 --- a/include/mapnik/text/symbolizer_helpers.hpp +++ b/include/mapnik/text/symbolizer_helpers.hpp @@ -22,10 +22,13 @@ #ifndef SYMBOLIZER_HELPERS_HPP #define SYMBOLIZER_HELPERS_HPP -//mapnik +// mapnik #include +#include #include #include +#include +#include namespace mapnik { diff --git a/include/mapnik/text/text_line.hpp b/include/mapnik/text/text_line.hpp index 1bd02db66..cf47026df 100644 --- a/include/mapnik/text/text_line.hpp +++ b/include/mapnik/text/text_line.hpp @@ -25,17 +25,17 @@ //stl #include #include +#include +#include namespace mapnik { -struct glyph_info; - // This class stores all glyphs of a line in left to right order. // It can be used for rendering but no text processing (like line breaking) // should be done! -class text_line : util::noncopyable +class MAPNIK_DECL text_line : util::noncopyable { public: using glyph_vector = std::vector; @@ -97,6 +97,6 @@ private: unsigned space_count_; }; -} //namespace mapnik +} // namespace mapnik #endif // MAPNIK_TEXT_LINE_HPP diff --git a/include/mapnik/transform_processor.hpp b/include/mapnik/transform_processor.hpp index fd45ecc38..bec6bff3b 100644 --- a/include/mapnik/transform_processor.hpp +++ b/include/mapnik/transform_processor.hpp @@ -32,6 +32,9 @@ // agg #include +// stl +#include + namespace mapnik { class feature_impl; @@ -149,13 +152,19 @@ struct transform_processor void operator() (skewX_node const& node) { - double angle = deg2rad(eval(node.angle_)); + auto degrees = std::fmod(eval(node.angle_),90.0); + if (degrees < -89.0) degrees = -89.0; + else if (degrees > 89.0) degrees = 89.0; + auto angle = deg2rad(degrees); transform_.multiply(agg::trans_affine_skewing(angle, 0.0)); } void operator() (skewY_node const& node) { - double angle = deg2rad(eval(node.angle_)); + auto degrees = std::fmod(eval(node.angle_),90.0); + if (degrees < -89.0) degrees = -89.0; + else if (degrees > 89.0) degrees = 89.0; + auto angle = deg2rad(degrees); transform_.multiply(agg::trans_affine_skewing(0.0, angle)); } diff --git a/include/mapnik/util/spirit_transform_attribute.hpp b/include/mapnik/util/spirit_transform_attribute.hpp index 1c0bdac54..e924fd822 100644 --- a/include/mapnik/util/spirit_transform_attribute.hpp +++ b/include/mapnik/util/spirit_transform_attribute.hpp @@ -24,6 +24,20 @@ #define MAPNIK_UTIL_SPIRIT_TRANSFORM_ATTRIBUTE_HPP #include +#include + +#include +#include + +// boost +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#pragma GCC diagnostic ignored "-Wunused-local-typedef" +#pragma GCC diagnostic ignored "-Wshadow" +#pragma GCC diagnostic ignored "-Wsign-conversion" +#pragma GCC diagnostic ignored "-Wconversion" +#include +#pragma GCC diagnostic pop namespace boost { namespace spirit { namespace traits { diff --git a/include/mapnik/value_hash.hpp b/include/mapnik/value_hash.hpp index e0aec264e..4c12bbe97 100644 --- a/include/mapnik/value_hash.hpp +++ b/include/mapnik/value_hash.hpp @@ -29,7 +29,6 @@ // stl #include -#include // icu #include @@ -52,7 +51,6 @@ struct value_hasher std::size_t operator() (value_unicode_string const& val) const { - assert(val.hashCode() > 0); return static_cast(val.hashCode()); } diff --git a/include/mapnik/version.hpp b/include/mapnik/version.hpp index 6ed43f574..a0a7b9d8b 100644 --- a/include/mapnik/version.hpp +++ b/include/mapnik/version.hpp @@ -27,9 +27,9 @@ #define MAPNIK_MAJOR_VERSION 3 #define MAPNIK_MINOR_VERSION 0 -#define MAPNIK_PATCH_VERSION 0 +#define MAPNIK_PATCH_VERSION 1 -// translates to 300000 +// translates to 300001 #define MAPNIK_VERSION (MAPNIK_MAJOR_VERSION*100000) + (MAPNIK_MINOR_VERSION*100) + (MAPNIK_PATCH_VERSION) #ifndef MAPNIK_STRINGIFY diff --git a/localize.sh b/localize.sh index ee62e5987..31c6a6ea5 100755 --- a/localize.sh +++ b/localize.sh @@ -8,6 +8,7 @@ else fi export PATH=$(pwd)/utils/nik2img/:${PATH} +export PATH=$(pwd)/utils/mapnik-config/:${PATH} # mapnik-settings.env is an optional file to store # environment variables that should be used before diff --git a/plugins/input/geojson/geojson_datasource.cpp b/plugins/input/geojson/geojson_datasource.cpp index 739ef733e..e1ebb4751 100644 --- a/plugins/input/geojson/geojson_datasource.cpp +++ b/plugins/input/geojson/geojson_datasource.cpp @@ -218,7 +218,7 @@ void geojson_datasource::initialise_index(Iterator start, Iterator end) // parse first feature to extract attributes schema. // NOTE: this doesn't yield correct answer for geoJSON in general, just an indication Iterator itr2 = start + geometry_index.first; - Iterator end2 = itr + geometry_index.second; + Iterator end2 = itr2 + geometry_index.second; mapnik::context_ptr ctx = std::make_shared(); mapnik::feature_ptr feature(mapnik::feature_factory::create(ctx,1)); if (!boost::spirit::qi::phrase_parse(itr2, end2, (geojson_datasource_static_feature_grammar)(boost::phoenix::ref(*feature)), space)) diff --git a/plugins/input/occi/README b/plugins/input/occi/README deleted file mode 100644 index a36411c88..000000000 --- a/plugins/input/occi/README +++ /dev/null @@ -1,12 +0,0 @@ -# -# To regenerate C++ class declarations & implementations for the Spatial -# object types of your database you should execute OTT in your server ! -# - -echo "TYPE MDSYS.SDO_POINT_TYPE AS SDOPointType" > spatial_types.typ -echo "TYPE MDSYS.SDO_GEOMETRY AS SDOGeometry" >> spatial_types.typ - -ott userid=scott/tiger attraccess=private intype=spatial_types.typ code=cpp \ - cppfile=spatial_classeso.cpp hfile=spatial_classesh.h mapfile=spatial_classesm.cpp \ - mapfunc=RegisterClasses - diff --git a/plugins/input/occi/build.py b/plugins/input/occi/build.py deleted file mode 100644 index e0aeb7b4b..000000000 --- a/plugins/input/occi/build.py +++ /dev/null @@ -1,74 +0,0 @@ -# -# This file is part of Mapnik (c++ mapping toolkit) -# -# Copyright (C) 2015 Artem Pavlenko -# -# Mapnik 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 -# -# - -Import ('plugin_base') -Import ('env') - -PLUGIN_NAME = 'occi' - -plugin_env = plugin_base.Clone() - -plugin_sources = Split( - """ - %(PLUGIN_NAME)s_types.cpp - %(PLUGIN_NAME)s_datasource.cpp - %(PLUGIN_NAME)s_featureset.cpp - spatial_classesm.cpp - spatial_classeso.cpp - """ % locals() -) - -libraries = [ 'clntsh', 'occi' ] -libraries.append('boost_system%s' % env['BOOST_APPEND']) -libraries.append(env['ICU_LIB_NAME']) - -if env['PLUGIN_LINKING'] == 'shared': - libraries.append(env['MAPNIK_NAME']) - - # libocci.dylib, at least for 11.2 links to libstdc++ - # so we defer symbol resolution to runtime in order to - # dodge linking errors like - # Undefined symbols for architecture x86_64: - # "std::string::_Rep::_M_destroy(std::allocator const&)", referenced from: - # RegisterClasses(oracle::occi::Environment*) in spatial_classesm.os - - if env['PLATFORM'] == 'Darwin': - plugin_env.Append(LINKFLAGS='-undefined dynamic_lookup') - - TARGET = plugin_env.SharedLibrary('../%s' % PLUGIN_NAME, - SHLIBPREFIX='', - SHLIBSUFFIX='.input', - source=plugin_sources, - LIBS=libraries) - - # if the plugin links to libmapnik ensure it is built first - Depends(TARGET, env.subst('../../../src/%s' % env['MAPNIK_LIB_NAME'])) - - if 'uninstall' not in COMMAND_LINE_TARGETS: - env.Install(env['MAPNIK_INPUT_PLUGINS_DEST'], TARGET) - env.Alias('install', env['MAPNIK_INPUT_PLUGINS_DEST']) - -plugin_obj = { - 'LIBS': libraries, - 'SOURCES': plugin_sources, -} - -Return('plugin_obj') diff --git a/plugins/input/occi/occi_datasource.cpp b/plugins/input/occi/occi_datasource.cpp deleted file mode 100644 index 1c2809239..000000000 --- a/plugins/input/occi/occi_datasource.cpp +++ /dev/null @@ -1,641 +0,0 @@ -/***************************************************************************** - * - * This file is part of Mapnik (c++ mapping toolkit) - * - * Copyright (C) 2015 Artem Pavlenko - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - *****************************************************************************/ - -#include "occi_datasource.hpp" -#include "occi_featureset.hpp" - -// mapnik -#include -#include -#include -#include -#include - -// boost -#include -#include - -// stl -#include -#include -#include -#include -#include - -using mapnik::datasource; -using mapnik::parameters; -using mapnik::query; -using mapnik::featureset_ptr; -using mapnik::layer_descriptor; -using mapnik::attribute_descriptor; -using mapnik::datasource_exception; -using mapnik::box2d; -using mapnik::coord2d; - -using oracle::occi::Environment; -using oracle::occi::Connection; -using oracle::occi::Statement; -using oracle::occi::ResultSet; -using oracle::occi::MetaData; -using oracle::occi::SQLException; -using oracle::occi::Type; -using oracle::occi::StatelessConnectionPool; - -const double occi_datasource::FMAX = std::numeric_limits::max(); -const std::string occi_datasource::METADATA_TABLE = "USER_SDO_GEOM_METADATA"; - -DATASOURCE_PLUGIN(occi_datasource) - -occi_datasource::occi_datasource(parameters const& params) - : datasource (params), - type_(datasource::Vector), - fields_(*params.get("fields", "*")), - geometry_field_(*params.get("geometry_field", "")), - srid_initialized_(false), - extent_initialized_(false), - bbox_token_("!bbox!"), - scale_denom_token_("!scale_denominator!"), - pixel_width_token_("!pixel_width!"), - pixel_height_token_("!pixel_height!"), - desc_(occi_datasource::name(), *params.get("encoding", "utf-8")), - use_wkb_(*params.get("use_wkb", false)), - row_limit_(*params.get("row_limit", 0)), - row_prefetch_(*params.get("row_prefetch", 100)), - pool_(0), - conn_(0) -{ -#ifdef MAPNIK_STATS - mapnik::progress_timer __stats__(std::clog, "occi_datasource::init"); -#endif - - if (! params.get("user")) throw datasource_exception("OCCI Plugin: no specified"); - if (! params.get("password")) throw datasource_exception("OCCI Plugin: no specified"); - if (! params.get("host")) throw datasource_exception("OCCI Plugin: no string specified"); - - boost::optional table = params.get("table"); - if (! table) - { - throw datasource_exception("OCCI Plugin: no parameter specified"); - } - else - { - table_ = *table; - } - estimate_extent_ = *params.get("estimate_extent",false); - use_spatial_index_ = *params.get("use_spatial_index",true); - use_connection_pool_ = *params.get("use_connection_pool",true); - - boost::optional ext = params.get("extent"); - if (ext) extent_initialized_ = extent_.from_string(*ext); - - boost::optional srid = params.get("srid"); - if (srid) - { - srid_ = *srid; - srid_initialized_ = true; - } - - // connect to environment - if (use_connection_pool_) - { - try - { - pool_ = occi_environment::instance().create_pool( - *params.get("user"), - *params.get("password"), - *params.get("host"), - *params.get("max_size", 5), - *params.get("initial_size", 1), - 1); - } - catch (SQLException& ex) - { - throw datasource_exception("OCCI Plugin: " + ex.getMessage()); - } - } - else - { - try - { - conn_ = occi_environment::instance().create_connection( - *params.get("user"), - *params.get("password"), - *params.get("host")); - } - catch (SQLException& ex) - { - throw datasource_exception("OCCI Plugin: " + ex.getMessage()); - } - } - - // extract real table name - table_name_ = mapnik::sql_utils::table_from_sql(table_); - - // get SRID and/or GEOMETRY_FIELD from metadata table only if we need to - if (! srid_initialized_ || geometry_field_ == "") - { -#ifdef MAPNIK_STATS - mapnik::progress_timer __stats__(std::clog, "occi_datasource::get_srid_and_geometry_field"); -#endif - - std::ostringstream s; - s << "SELECT srid, column_name FROM " << METADATA_TABLE << " WHERE"; - s << " LOWER(table_name) = LOWER('" << table_name_ << "')"; - - if (geometry_field_ != "") - { - s << " AND LOWER(column_name) = LOWER('" << geometry_field_ << "')"; - } - - MAPNIK_LOG_DEBUG(occi) << "occi_datasource: " << s.str(); - - try - { - occi_connection_ptr conn; - if (use_connection_pool_) conn.set_pool(pool_); - else conn.set_connection(conn_, false); - - ResultSet* rs = conn.execute_query(s.str()); - if (rs && rs->next ()) - { - if (! srid_initialized_) - { - srid_ = rs->getInt(1); - srid_initialized_ = true; - } - - if (geometry_field_ == "") - { - geometry_field_ = rs->getString(2); - } - } - } - catch (SQLException& ex) - { - throw datasource_exception("OCCI Plugin: " + ex.getMessage()); - } - } - - // get columns description - { -#ifdef MAPNIK_STATS - mapnik::progress_timer __stats__(std::clog, "occi_datasource::get_column_description"); -#endif - - std::ostringstream s; - s << "SELECT " << fields_ << " FROM (" << table_name_ << ") WHERE ROWNUM < 1"; - - MAPNIK_LOG_DEBUG(occi) << "occi_datasource: " << s.str(); - - try - { - occi_connection_ptr conn; - if (use_connection_pool_) conn.set_pool(pool_); - else conn.set_connection(conn_, false); - - ResultSet* rs = conn.execute_query(s.str()); - if (rs) - { - std::vector listOfColumns = rs->getColumnListMetaData(); - - for (unsigned int i = 0; i < listOfColumns.size(); ++i) - { - MetaData columnObj = listOfColumns[i]; - - std::string fld_name = columnObj.getString(MetaData::ATTR_NAME); - int type_oid = columnObj.getInt(MetaData::ATTR_DATA_TYPE); - - /* - int type_code = columnObj.getInt(MetaData::ATTR_TYPECODE); - if (type_code == OCCI_TYPECODE_OBJECT) - { - desc_.add_descriptor(attribute_descriptor(fld_name,mapnik::Object)); - continue; - } - */ - - switch (type_oid) - { - case oracle::occi::OCCIBOOL: - desc_.add_descriptor(attribute_descriptor(fld_name,mapnik::Boolean)); - break; - case oracle::occi::OCCIINT: - case oracle::occi::OCCIUNSIGNED_INT: - desc_.add_descriptor(attribute_descriptor(fld_name,mapnik::Integer)); - break; - case oracle::occi::OCCIFLOAT: - case oracle::occi::OCCIBFLOAT: - case oracle::occi::OCCIDOUBLE: - case oracle::occi::OCCIBDOUBLE: - case oracle::occi::OCCINUMBER: - case oracle::occi::OCCI_SQLT_NUM: - desc_.add_descriptor(attribute_descriptor(fld_name,mapnik::Double)); - break; - case oracle::occi::OCCICHAR: - case oracle::occi::OCCISTRING: - case oracle::occi::OCCI_SQLT_AFC: - case oracle::occi::OCCI_SQLT_AVC: - case oracle::occi::OCCI_SQLT_CHR: - case oracle::occi::OCCI_SQLT_LNG: - case oracle::occi::OCCI_SQLT_LVC: - case oracle::occi::OCCI_SQLT_STR: - case oracle::occi::OCCI_SQLT_VCS: - case oracle::occi::OCCI_SQLT_VNU: - case oracle::occi::OCCI_SQLT_VBI: - case oracle::occi::OCCI_SQLT_VST: - case oracle::occi::OCCIROWID: - case oracle::occi::OCCI_SQLT_RDD: - case oracle::occi::OCCI_SQLT_RID: - case oracle::occi::OCCIDATE: - case oracle::occi::OCCI_SQLT_DAT: - case oracle::occi::OCCI_SQLT_DATE: - case oracle::occi::OCCI_SQLT_TIME: - case oracle::occi::OCCI_SQLT_TIME_TZ: - case oracle::occi::OCCITIMESTAMP: - case oracle::occi::OCCI_SQLT_TIMESTAMP: - case oracle::occi::OCCI_SQLT_TIMESTAMP_LTZ: - case oracle::occi::OCCI_SQLT_TIMESTAMP_TZ: - desc_.add_descriptor(attribute_descriptor(fld_name,mapnik::String)); - break; - case oracle::occi::OCCIINTERVALDS: - case oracle::occi::OCCIINTERVALYM: - case oracle::occi::OCCI_SQLT_INTERVAL_YM: - case oracle::occi::OCCI_SQLT_INTERVAL_DS: - case oracle::occi::OCCIANYDATA: - case oracle::occi::OCCIBLOB: - case oracle::occi::OCCIBFILE: - case oracle::occi::OCCIBYTES: - case oracle::occi::OCCICLOB: - case oracle::occi::OCCIVECTOR: - case oracle::occi::OCCIMETADATA: - case oracle::occi::OCCIPOBJECT: - case oracle::occi::OCCIREF: - case oracle::occi::OCCIREFANY: - case oracle::occi::OCCISTREAM: - case oracle::occi::OCCICURSOR: - case oracle::occi::OCCI_SQLT_FILE: - case oracle::occi::OCCI_SQLT_CFILE: - case oracle::occi::OCCI_SQLT_REF: - case oracle::occi::OCCI_SQLT_CLOB: - case oracle::occi::OCCI_SQLT_BLOB: - case oracle::occi::OCCI_SQLT_RSET: - MAPNIK_LOG_WARN(occi) << "occi_datasource: Unsupported datatype " - << occi_enums::resolve_datatype(type_oid) - << " (type_oid=" << type_oid << ")"; - break; - default: - MAPNIK_LOG_WARN(occi) << "occi_datasource: Unknown datatype " - << "(type_oid=" << type_oid << ")"; - break; - } - } - } - } - catch (SQLException& ex) - { - throw datasource_exception(ex.getMessage()); - } - } -} - -occi_datasource::~occi_datasource() -{ - if (use_connection_pool_) - { - if (pool_ != 0) - { - occi_environment::instance().destroy_pool(pool_); - } - } - else - { - if (conn_ != 0) - { - occi_environment::instance().destroy_connection(conn_); - } - } -} - -const char * occi_datasource::name() -{ - return "occi"; -} - -mapnik::datasource::datasource_t occi_datasource::type() const -{ - return type_; -} - -box2d occi_datasource::envelope() const -{ - if (extent_initialized_) return extent_; - - double lox = 0.0, loy = 0.0, hix = 0.0, hiy = 0.0; - - - if (estimate_extent_) - { -#ifdef MAPNIK_STATS - mapnik::progress_timer __stats__(std::clog, "occi_datasource::envelope(estimate_extent)"); -#endif - - std::ostringstream s; - s << "SELECT MIN(c.x), MIN(c.y), MAX(c.x), MAX(c.y) FROM "; - s << " (SELECT SDO_AGGR_MBR(" << geometry_field_ << ") shape FROM " << table_ << ") a, "; - s << " TABLE(SDO_UTIL.GETVERTICES(a.shape)) c"; - - MAPNIK_LOG_DEBUG(occi) << "occi_datasource: " << s.str(); - - try - { - occi_connection_ptr conn; - if (use_connection_pool_) conn.set_pool(pool_); - else conn.set_connection(conn_, false); - - ResultSet* rs = conn.execute_query(s.str()); - if (rs && rs->next()) - { - lox = rs->getDouble(1); - loy = rs->getDouble(2); - hix = rs->getDouble(3); - hiy = rs->getDouble(4); - extent_.init(lox, loy, hix, hiy); - extent_initialized_ = true; - } - } - catch (SQLException& ex) - { - throw datasource_exception("OCCI Plugin: " + ex.getMessage()); - } - } - else if (use_spatial_index_) - { -#ifdef MAPNIK_STATS - mapnik::progress_timer __stats__(std::clog, "occi_datasource::envelope(use_spatial_index)"); -#endif - - std::ostringstream s; - s << "SELECT dim.sdo_lb, dim.sdo_ub FROM "; - s << METADATA_TABLE << " m, TABLE(m.diminfo) dim "; - s << " WHERE LOWER(m.table_name) = LOWER('" << table_name_ << "') AND dim.sdo_dimname = 'X'"; - s << " UNION "; - s << "SELECT dim.sdo_lb, dim.sdo_ub FROM "; - s << METADATA_TABLE << " m, TABLE(m.diminfo) dim "; - s << " WHERE LOWER(m.table_name) = LOWER('" << table_name_ << "') AND dim.sdo_dimname = 'Y'"; - - MAPNIK_LOG_DEBUG(occi) << "occi_datasource: " << s.str(); - - try - { - occi_connection_ptr conn; - if (use_connection_pool_) conn.set_pool(pool_); - else conn.set_connection(conn_, false); - - ResultSet* rs = conn.execute_query(s.str()); - if (rs) - { - if (rs->next()) - { - lox = rs->getDouble(1); - hix = rs->getDouble(2); - } - - if (rs->next()) - { - loy = rs->getDouble(1); - hiy = rs->getDouble(2); - } - extent_.init(lox, loy, hix, hiy); - extent_initialized_ = true; - } - } - catch (SQLException& ex) - { - throw datasource_exception("OCCI Plugin: " + ex.getMessage()); - } - } - - if (! extent_initialized_) - { - throw datasource_exception("OCCI Plugin: unable to determine the extent of a table"); - } - - return extent_; -} - - -boost::optional occi_datasource::get_geometry_type() const -{ - return boost::optional(); -} - -layer_descriptor occi_datasource::get_descriptor() const -{ - return desc_; -} - -std::string occi_datasource::sql_bbox(box2d const& env) const -{ - std::ostringstream b; - b << std::setprecision(16); - b << "MDSYS.SDO_GEOMETRY(" << SDO_GTYPE_2DPOLYGON << "," << srid_ << ",NULL,"; - b << " MDSYS.SDO_ELEM_INFO_ARRAY(1," << SDO_ETYPE_POLYGON << "," << SDO_INTERPRETATION_RECTANGLE << "),"; - b << " MDSYS.SDO_ORDINATE_ARRAY("; - b << env.minx() << "," << env.miny() << ", "; - b << env.maxx() << "," << env.maxy() << "))"; - return b.str(); -} - -std::string occi_datasource::populate_tokens(std::string const& sql, double scale_denom, box2d const& env, double pixel_width, double pixel_height) const -{ - std::string populated_sql = sql; - - if (boost::algorithm::icontains(populated_sql, scale_denom_token_)) - { - std::ostringstream ss; - ss << scale_denom; - boost::algorithm::replace_all(populated_sql, scale_denom_token_, ss.str()); - } - - if (boost::algorithm::icontains(sql, pixel_width_token_)) - { - std::ostringstream ss; - ss << pixel_width; - boost::algorithm::replace_all(populated_sql, pixel_width_token_, ss.str()); - } - - if (boost::algorithm::icontains(sql, pixel_height_token_)) - { - std::ostringstream ss; - ss << pixel_height; - boost::algorithm::replace_all(populated_sql, pixel_height_token_, ss.str()); - } - - if (boost::algorithm::icontains(populated_sql, bbox_token_)) - { - boost::algorithm::replace_all(populated_sql, bbox_token_, sql_bbox(env)); - } - - return populated_sql; -} - -featureset_ptr occi_datasource::features(query const& q) const -{ -#ifdef MAPNIK_STATS - mapnik::progress_timer __stats__(std::clog, "occi_datasource::features"); -#endif - - box2d const& box = q.get_bbox(); - const double px_gw = 1.0 / std::get<0>(q.resolution()); - const double px_gh = 1.0 / std::get<1>(q.resolution()); - const double scale_denom = q.scale_denominator(); - - std::ostringstream s; - s << "SELECT "; - if (use_wkb_) - { - s << "SDO_UTIL.TO_WKBGEOMETRY(" << geometry_field_ << ")"; - } - else - { - s << geometry_field_; - } - std::set const& props = q.property_names(); - std::set::const_iterator pos = props.begin(); - std::set::const_iterator end = props.end(); - mapnik::context_ptr ctx = std::make_shared(); - for (; pos != end; ++pos) - { - s << ", " << *pos; - ctx->push(*pos); - } - - std::string query = populate_tokens(table_, scale_denom, box, px_gw, px_gh); - - if (use_spatial_index_) - { - std::ostringstream spatial_sql; - spatial_sql << " WHERE SDO_FILTER("; - spatial_sql << geometry_field_ << "," << sql_bbox(box); - spatial_sql << ", 'querytype = WINDOW') = 'TRUE'"; - - if (boost::algorithm::ifind_first(query, "WHERE")) - { - boost::algorithm::ireplace_first(query, "WHERE", spatial_sql.str() + " AND "); - } - else if (boost::algorithm::ifind_first(query, table_name_)) - { - boost::algorithm::ireplace_first(query, table_name_, table_name_ + " " + spatial_sql.str()); - } - else - { - MAPNIK_LOG_WARN(occi) << "occi_datasource: cannot determine where to add the spatial filter declaration"; - } - } - - s << " FROM " << query; - - if (row_limit_ > 0) - { - s << " WHERE ROWNUM < " << row_limit_; - } - - MAPNIK_LOG_DEBUG(occi) << "occi_datasource: " << s.str(); - - return std::make_shared(pool_, - conn_, - ctx, - s.str(), - desc_.get_encoding(), - use_connection_pool_, - use_wkb_, - row_prefetch_); -} - -featureset_ptr occi_datasource::features_at_point(coord2d const& pt, double tol) const -{ -#ifdef MAPNIK_STATS - mapnik::progress_timer __stats__(std::clog, "occi_datasource::features_at_point"); -#endif - - std::ostringstream s; - s << "SELECT "; - if (use_wkb_) - { - s << "SDO_UTIL.TO_WKBGEOMETRY(" << geometry_field_ << ")"; - } - else - { - s << geometry_field_; - } - std::vector::const_iterator itr = desc_.get_descriptors().begin(); - std::vector::const_iterator end = desc_.get_descriptors().end(); - mapnik::context_ptr ctx = std::make_shared(); - while (itr != end) - { - s << ", " << itr->get_name(); - ctx->push(itr->get_name()); - ++itr; - } - - box2d box(pt.x - tol, pt.y - tol, pt.x + tol, pt.y + tol); - std::string query = populate_tokens(table_, FMAX, box, 0, 0); - - if (use_spatial_index_) - { - std::ostringstream spatial_sql; - spatial_sql << " WHERE SDO_FILTER("; - spatial_sql << geometry_field_ << "," << sql_bbox(box); - spatial_sql << ", 'querytype = WINDOW') = 'TRUE'"; - - if (boost::algorithm::ifind_first(query, "WHERE")) - { - boost::algorithm::ireplace_first(query, "WHERE", spatial_sql.str() + " AND "); - } - else if (boost::algorithm::ifind_first(query, table_name_)) - { - boost::algorithm::ireplace_first(query, table_name_, table_name_ + " " + spatial_sql.str()); - } - else - { - MAPNIK_LOG_WARN(occi) << "occi_datasource: Cannot determine where to add the spatial filter declaration"; - } - } - - s << " FROM " << query; - - if (row_limit_ > 0) - { - s << " WHERE ROWNUM < " << row_limit_; - } - - MAPNIK_LOG_DEBUG(occi) << "occi_datasource: " << s.str(); - - return std::make_shared(pool_, - conn_, - ctx, - s.str(), - desc_.get_encoding(), - use_connection_pool_, - use_wkb_, - row_prefetch_); -} diff --git a/plugins/input/occi/occi_datasource.hpp b/plugins/input/occi/occi_datasource.hpp deleted file mode 100644 index 0affc339e..000000000 --- a/plugins/input/occi/occi_datasource.hpp +++ /dev/null @@ -1,95 +0,0 @@ -/***************************************************************************** - * - * This file is part of Mapnik (c++ mapping toolkit) - * - * Copyright (C) 2015 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 OCCI_DATASOURCE_HPP -#define OCCI_DATASOURCE_HPP - -// mapnik -#include -#include -#include -#include -#include -#include -#include -#include - -// boost -#include -#include - -// stl -#include -#include - -// oci -#include "occi_types.hpp" - -class occi_datasource : public mapnik::datasource -{ -public: - occi_datasource(mapnik::parameters const& params); - virtual ~occi_datasource (); - mapnik::datasource::datasource_t type() const; - static const char * name(); - mapnik::featureset_ptr features(mapnik::query const& q) const; - mapnik::featureset_ptr features_at_point(mapnik::coord2d const& pt, double tol = 0) const; - mapnik::box2d envelope() const; - boost::optional get_geometry_type() const; - mapnik::layer_descriptor get_descriptor() const; - -private: - std::string sql_bbox(mapnik::box2d const& env) const; - std::string populate_tokens(std::string const& sql, - double scale_denom, - mapnik::box2d const& env, - double pixel_width, - double pixel_height) const; - - static const std::string METADATA_TABLE; - static const double FMAX; - - mapnik::datasource::datasource_t type_; - std::string table_; - std::string table_name_; - std::string fields_; - std::string geometry_field_; - int srid_; - bool srid_initialized_; - mutable bool extent_initialized_; - mutable mapnik::box2d extent_; - const std::string bbox_token_; - const std::string scale_denom_token_; - const std::string pixel_width_token_; - const std::string pixel_height_token_; - mapnik::layer_descriptor desc_; - bool use_wkb_; - mapnik::value_integer row_limit_; - int row_prefetch_; - oracle::occi::StatelessConnectionPool* pool_; - oracle::occi::Connection* conn_; - bool use_connection_pool_; - bool use_spatial_index_; - bool estimate_extent_; -}; - -#endif // OCCI_DATASOURCE_HPP diff --git a/plugins/input/occi/occi_featureset.cpp b/plugins/input/occi/occi_featureset.cpp deleted file mode 100644 index 0a0b942eb..000000000 --- a/plugins/input/occi/occi_featureset.cpp +++ /dev/null @@ -1,516 +0,0 @@ -/***************************************************************************** - * - * This file is part of Mapnik (c++ mapping toolkit) - * - * Copyright (C) 2015 Artem Pavlenko - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - *****************************************************************************/ - -// mapnik -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// ogr -#include "occi_featureset.hpp" - -using mapnik::query; -using mapnik::box2d; -using mapnik::feature_ptr; -using mapnik::geometry_type; -using mapnik::geometry_utils; -using mapnik::transcoder; -using mapnik::datasource_exception; -using mapnik::feature_factory; - -using oracle::occi::Connection; -using oracle::occi::Statement; -using oracle::occi::ResultSet; -using oracle::occi::StatelessConnectionPool; -using oracle::occi::MetaData; -using oracle::occi::SQLException; -using oracle::occi::Type; -using oracle::occi::Number; -using oracle::occi::Blob; - -occi_featureset::occi_featureset(StatelessConnectionPool* pool, - Connection* conn, - mapnik::context_ptr const& ctx, - std::string const& sqlstring, - std::string const& encoding, - bool use_connection_pool, - bool use_wkb, - unsigned prefetch_rows) - : rs_(nullptr), - tr_(new transcoder(encoding)), - feature_id_(1), - ctx_(ctx), - use_wkb_(use_wkb) -{ - if (use_connection_pool) - { - conn_.set_pool(pool); - } - else - { - conn_.set_connection(conn, false); - } - - try - { - rs_ = conn_.execute_query(sqlstring, prefetch_rows); - } - catch (SQLException &ex) - { - MAPNIK_LOG_ERROR(occi) << "OCCI Plugin: error processing " << sqlstring << " : " << ex.getMessage(); - - rs_ = nullptr; - } -} - -occi_featureset::~occi_featureset() -{ -} - -feature_ptr occi_featureset::next() -{ - while (rs_ != nullptr && rs_->next() == oracle::occi::ResultSet::DATA_AVAILABLE) - { - feature_ptr feature(feature_factory::create(ctx_, feature_id_)); - - if (use_wkb_) - { - Blob blob = rs_->getBlob(1); - blob.open(oracle::occi::OCCI_LOB_READONLY); - - unsigned int size = blob.length(); - if (buffer_.size() < size) - { - buffer_.resize(size); - } - - oracle::occi::Stream* instream = blob.getStream(1, 0); - instream->readBuffer(buffer_.data(), size); - blob.closeStream(instream); - blob.close(); - - if (! geometry_utils::from_wkb(feature->paths(), buffer_.data(), size)) - { - continue; - } - } - else - { - const std::unique_ptr geom(dynamic_cast(rs_->getObject(1))); - if (geom.get()) - { - convert_geometry(geom.get(), feature); - } - else - { - continue; - } - } - - std::vector listOfColumns = rs_->getColumnListMetaData(); - - for (unsigned int i = 1; i < listOfColumns.size(); ++i) - { - MetaData columnObj = listOfColumns[i]; - - std::string fld_name = columnObj.getString(MetaData::ATTR_NAME); - int type_oid = columnObj.getInt(MetaData::ATTR_DATA_TYPE); - - /* - int type_code = columnObj.getInt(MetaData::ATTR_TYPECODE); - if (type_code == OCCI_TYPECODE_OBJECT) - { - continue; - } - */ - - switch (type_oid) - { - case oracle::occi::OCCIBOOL: - feature->put(fld_name, (rs_->getInt(i + 1) != 0)); - break; - case oracle::occi::OCCIINT: - case oracle::occi::OCCIUNSIGNED_INT: - feature->put(fld_name, static_cast(rs_->getInt(i + 1))); - break; - case oracle::occi::OCCIFLOAT: - case oracle::occi::OCCIBFLOAT: - feature->put(fld_name, (double)rs_->getFloat(i + 1)); - break; - case oracle::occi::OCCIDOUBLE: - case oracle::occi::OCCIBDOUBLE: - case oracle::occi::OCCINUMBER: - case oracle::occi::OCCI_SQLT_NUM: - feature->put(fld_name, rs_->getDouble(i + 1)); - break; - case oracle::occi::OCCICHAR: - case oracle::occi::OCCISTRING: - case oracle::occi::OCCI_SQLT_AFC: - case oracle::occi::OCCI_SQLT_AVC: - case oracle::occi::OCCI_SQLT_CHR: - case oracle::occi::OCCI_SQLT_LNG: - case oracle::occi::OCCI_SQLT_LVC: - case oracle::occi::OCCI_SQLT_STR: - case oracle::occi::OCCI_SQLT_VCS: - case oracle::occi::OCCI_SQLT_VNU: - case oracle::occi::OCCI_SQLT_VBI: - case oracle::occi::OCCI_SQLT_VST: - case oracle::occi::OCCIROWID: - case oracle::occi::OCCI_SQLT_RDD: - case oracle::occi::OCCI_SQLT_RID: - case oracle::occi::OCCIDATE: - case oracle::occi::OCCI_SQLT_DAT: - case oracle::occi::OCCI_SQLT_DATE: - case oracle::occi::OCCI_SQLT_TIME: - case oracle::occi::OCCI_SQLT_TIME_TZ: - case oracle::occi::OCCITIMESTAMP: - case oracle::occi::OCCI_SQLT_TIMESTAMP: - case oracle::occi::OCCI_SQLT_TIMESTAMP_LTZ: - case oracle::occi::OCCI_SQLT_TIMESTAMP_TZ: - feature->put(fld_name, static_cast(tr_->transcode(rs_->getString(i + 1).c_str()))); - break; - case oracle::occi::OCCIINTERVALDS: - case oracle::occi::OCCIINTERVALYM: - case oracle::occi::OCCI_SQLT_INTERVAL_YM: - case oracle::occi::OCCI_SQLT_INTERVAL_DS: - case oracle::occi::OCCIANYDATA: - case oracle::occi::OCCIBLOB: - case oracle::occi::OCCIBFILE: - case oracle::occi::OCCIBYTES: - case oracle::occi::OCCICLOB: - case oracle::occi::OCCIVECTOR: - case oracle::occi::OCCIMETADATA: - case oracle::occi::OCCIPOBJECT: - case oracle::occi::OCCIREF: - case oracle::occi::OCCIREFANY: - case oracle::occi::OCCISTREAM: - case oracle::occi::OCCICURSOR: - case oracle::occi::OCCI_SQLT_FILE: - case oracle::occi::OCCI_SQLT_CFILE: - case oracle::occi::OCCI_SQLT_REF: - case oracle::occi::OCCI_SQLT_CLOB: - case oracle::occi::OCCI_SQLT_BLOB: - case oracle::occi::OCCI_SQLT_RSET: - { - MAPNIK_LOG_WARN(occi) << "occi_featureset: Unsupported datatype " - << occi_enums::resolve_datatype(type_oid) - << " (type_oid=" << type_oid << ")"; - break; - } - default: // shouldn't get here - { - MAPNIK_LOG_WARN(occi) << "occi_featureset: Unknown datatype " - << "(type_oid=" << type_oid << ")"; - break; - } - } - } - - ++feature_id_; - - return feature; - } - - return feature_ptr(); -} - - -void occi_featureset::convert_geometry(SDOGeometry* geom, feature_ptr feature) -{ - int gtype = (int)geom->getSdo_gtype(); - int dimensions = gtype / 1000; - int lrsvalue = (gtype - dimensions * 1000) / 100; - int geomtype = (gtype - dimensions * 1000 - lrsvalue * 100); - - const std::vector& elem_info = geom->getSdo_elem_info(); - const std::vector& ordinates = geom->getSdo_ordinates(); - const int ordinates_size = (int)ordinates.size(); - - switch (geomtype) - { - case SDO_GTYPE_POINT: - { - SDOPointType* sdopoint = geom->getSdo_point(); - if (sdopoint && ! sdopoint->isNull()) - { - std::unique_ptr point = std::make_unique(mapnik::geometry::geometry_types::Point); - point->move_to(sdopoint->getX(), sdopoint->getY()); - feature->add_geometry(point.release()); - } - } - break; - case SDO_GTYPE_LINE: - { - if (ordinates_size >= dimensions) - { - const bool is_single_geom = true; - const bool is_point_type = false; - convert_ordinates(feature, - mapnik::geometry::geometry_types::LineString, - elem_info, - ordinates, - dimensions, - is_single_geom, - is_point_type); - } - } - break; - case SDO_GTYPE_POLYGON: - { - if (ordinates_size >= dimensions) - { - const bool is_single_geom = true; - const bool is_point_type = false; - convert_ordinates(feature, - mapnik::geometry::geometry_types::Polygon, - elem_info, - ordinates, - dimensions, - is_single_geom, - is_point_type); - } - } - break; - case SDO_GTYPE_MULTIPOINT: - { - if (ordinates_size >= dimensions) - { - const bool is_single_geom = false; - const bool is_point_type = true; - convert_ordinates(feature, - mapnik::geometry::geometry_types::Point, - elem_info, - ordinates, - dimensions, - is_single_geom, - is_point_type); - } - } - break; - case SDO_GTYPE_MULTILINE: - { - if (ordinates_size >= dimensions) - { - const bool is_single_geom = false; - const bool is_point_type = false; - - convert_ordinates(feature, - mapnik::geometry::geometry_types::LineString, - elem_info, - ordinates, - dimensions, - is_single_geom, - is_point_type); - } - } - break; - case SDO_GTYPE_MULTIPOLYGON: - { - if (ordinates_size >= dimensions) - { - const bool is_single_geom = false; - const bool is_point_type = false; - - convert_ordinates(feature, - mapnik::geometry::geometry_types::Polygon, - elem_info, - ordinates, - dimensions, - is_single_geom, - is_point_type); - } - - } - break; - case SDO_GTYPE_COLLECTION: - { - if (ordinates_size >= dimensions) - { - const bool is_single_geom = false; - const bool is_point_type = false; - - convert_ordinates(feature, - mapnik::geometry::geometry_types::Polygon, - elem_info, - ordinates, - dimensions, - is_single_geom, - is_point_type); - } - } - break; - case SDO_GTYPE_UNKNOWN: - default: - { - MAPNIK_LOG_WARN(occi) << "occi_featureset: Unknown oracle enum " - << occi_enums::resolve_gtype(geomtype) - << "(gtype=" << gtype << ")"; - } - break; - } -} - -void occi_featureset::convert_ordinates(mapnik::feature_ptr feature, - const mapnik::geometry_type::types& geom_type, - const std::vector& elem_info, - const std::vector& ordinates, - const int dimensions, - const bool is_single_geom, - const bool is_point_geom) -{ - const int elem_size = elem_info.size(); - const int ord_size = ordinates.size(); - - if (elem_size >= 0) - { - int offset = elem_info[0]; - int etype = elem_info[1]; - int interp = elem_info[2]; - - if (! is_single_geom && elem_size > SDO_ELEM_INFO_SIZE) - { - geometry_type* geom = new geometry_type(geom_type); - - for (int i = SDO_ELEM_INFO_SIZE; i < elem_size; i+=3) - { - int next_offset = elem_info[i]; - int next_etype = elem_info[i + 1]; - int next_interp = elem_info[i + 2]; - bool is_linear_element = true; - bool is_unknown_etype = false; - mapnik::geometry_type::types gtype = mapnik::geometry::geometry_types::Point; - - switch (etype) - { - case SDO_ETYPE_POINT: - if (interp == SDO_INTERPRETATION_POINT) {} - if (interp > SDO_INTERPRETATION_POINT) {} - gtype = mapnik::geometry::geometry_types::Point; - break; - - case SDO_ETYPE_LINESTRING: - if (interp == SDO_INTERPRETATION_STRAIGHT) {} - if (interp == SDO_INTERPRETATION_CIRCULAR) {} - gtype = mapnik::geometry::geometry_types::LineString; - break; - - case SDO_ETYPE_POLYGON: - case SDO_ETYPE_POLYGON_INTERIOR: - if (interp == SDO_INTERPRETATION_STRAIGHT) {} - if (interp == SDO_INTERPRETATION_CIRCULAR) {} - if (interp == SDO_INTERPRETATION_RECTANGLE) {} - if (interp == SDO_INTERPRETATION_CIRCLE) {} - gtype = mapnik::geometry::geometry_types::Polygon; - break; - - case SDO_ETYPE_COMPOUND_LINESTRING: - case SDO_ETYPE_COMPOUND_POLYGON: - case SDO_ETYPE_COMPOUND_POLYGON_INTERIOR: - // interp = next ETYPE to consider - is_linear_element = false; - gtype = mapnik::geometry::geometry_types::Polygon; - break; - - case SDO_ETYPE_UNKNOWN: // unknown - default: - is_unknown_etype = true; - break; - } - - if (is_unknown_etype) - { - break; - } - - if (is_linear_element) - { - if (geom) - { - feature->add_geometry(geom); - } - - geom = new geometry_type(gtype); - fill_geometry_type(geom, - offset - 1, - next_offset - 1, - ordinates, - dimensions, - is_point_geom); - } - - offset = next_offset; - etype = next_etype; - interp = next_interp; - } - - if (geom) - { - feature->add_geometry(geom); - geom = 0; - } - } - else - { - geometry_type * geom = new geometry_type(geom_type); - fill_geometry_type(geom, - offset - 1, - ord_size, - ordinates, - dimensions, - is_point_geom); - - feature->add_geometry(geom); - } - } -} - -void occi_featureset::fill_geometry_type(geometry_type* geom, - const int real_offset, - const int next_offset, - const std::vector& ordinates, - const int dimensions, - const bool is_point_geom) -{ - geom->move_to((double) ordinates[real_offset], (double) ordinates[real_offset + 1]); - - if (is_point_geom) - { - for (int p = real_offset + dimensions; p < next_offset; p += dimensions) - { - geom->move_to((double) ordinates[p], (double) ordinates[p + 1]); - } - } - else - { - for (int p = real_offset + dimensions; p < next_offset; p += dimensions) - { - geom->line_to((double) ordinates[p], (double) ordinates[p + 1]); - } - } -} diff --git a/plugins/input/occi/occi_featureset.hpp b/plugins/input/occi/occi_featureset.hpp deleted file mode 100644 index 13445dd96..000000000 --- a/plugins/input/occi/occi_featureset.hpp +++ /dev/null @@ -1,77 +0,0 @@ -/***************************************************************************** - * - * This file is part of Mapnik (c++ mapping toolkit) - * - * Copyright (C) 2015 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 OCCI_FEATURESET_HPP -#define OCCI_FEATURESET_HPP - -// mapnik -#include -#include -#include -// stl -#include - -// oci -#include "occi_types.hpp" - -#include - -class occi_featureset : public mapnik::Featureset -{ -public: - occi_featureset(oracle::occi::StatelessConnectionPool* pool, - oracle::occi::Connection* conn, - mapnik::context_ptr const& ctx, - std::string const& sqlstring, - std::string const& encoding, - bool use_connection_pool, - bool use_wkb, - unsigned prefetch_rows); - virtual ~occi_featureset(); - mapnik::feature_ptr next(); - -private: - void convert_geometry (SDOGeometry* geom, mapnik::feature_ptr feature); - void convert_ordinates (mapnik::feature_ptr feature, - const mapnik::geometry_type::types& geom_type, - const std::vector& elem_info, - const std::vector& ordinates, - const int dimensions, - const bool is_single_geom, - const bool is_point_geom); - void fill_geometry_type (mapnik::geometry_type* geom, - const int real_offset, - const int next_offset, - const std::vector& ordinates, - const int dimensions, - const bool is_point_geom); - - occi_connection_ptr conn_; - oracle::occi::ResultSet* rs_; - const std::unique_ptr tr_; - mapnik::value_integer feature_id_; - mapnik::context_ptr ctx_; - bool use_wkb_; - std::vector buffer_; -}; - -#endif // OCCI_FEATURESET_HPP diff --git a/plugins/input/occi/occi_types.cpp b/plugins/input/occi/occi_types.cpp deleted file mode 100644 index 439670680..000000000 --- a/plugins/input/occi/occi_types.cpp +++ /dev/null @@ -1,91 +0,0 @@ -/***************************************************************************** - * - * This file is part of Mapnik (c++ mapping toolkit) - * - * Copyright (C) 2015 Artem Pavlenko - * - * This library is free software, you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation, either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY, without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library, if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - *****************************************************************************/ - -#include "occi_types.hpp" - -std::string occi_enums::resolve_gtype(int gtype) -{ - switch (gtype) - { - case SDO_GTYPE_UNKNOWN: return "SDO_GTYPE_UNKNOWN"; - case SDO_GTYPE_POINT: return "SDO_GTYPE_POINT"; - case SDO_GTYPE_LINE: return "SDO_GTYPE_LINE"; - case SDO_GTYPE_POLYGON: return "SDO_GTYPE_POLYGON"; - case SDO_GTYPE_MULTIPOINT: return "SDO_GTYPE_MULTIPOINT"; - case SDO_GTYPE_MULTILINE: return "SDO_GTYPE_MULTILINE"; - case SDO_GTYPE_MULTIPOLYGON: return "SDO_GTYPE_MULTIPOLYGON"; - case SDO_GTYPE_COLLECTION: return "SDO_GTYPE_COLLECTION"; - default: return ""; - } -} - -std::string occi_enums::resolve_etype(int etype) -{ - switch (etype) - { - case SDO_ETYPE_UNKNOWN: return "SDO_ETYPE_UNKNOWN"; - case SDO_ETYPE_POINT: return "SDO_ETYPE_POINT"; - case SDO_ETYPE_LINESTRING: return "SDO_ETYPE_LINESTRING"; - case SDO_ETYPE_POLYGON: return "SDO_ETYPE_POLYGON"; - case SDO_ETYPE_POLYGON_INTERIOR: return "SDO_ETYPE_POLYGON_INTERIOR"; - case SDO_ETYPE_COMPOUND_LINESTRING: return "SDO_ETYPE_COMPOUND_LINESTRING"; - case SDO_ETYPE_COMPOUND_POLYGON: return "SDO_ETYPE_COMPOUND_POLYGON"; - case SDO_ETYPE_COMPOUND_POLYGON_INTERIOR: return "SDO_ETYPE_COMPOUND_POLYGON_INTERIOR"; - default: return ""; - } -} - -std::string occi_enums::resolve_datatype(int type_id) -{ - switch (type_id) - { - case oracle::occi::OCCIINT: return "OCCIINT"; - case oracle::occi::OCCIUNSIGNED_INT: return "OCCIUNSIGNED_INT"; - case oracle::occi::OCCIFLOAT: return "OCCIFLOAT"; - case oracle::occi::OCCIBFLOAT: return "OCCIBFLOAT"; - case oracle::occi::OCCIDOUBLE: return "OCCIDOUBLE"; - case oracle::occi::OCCIBDOUBLE: return "OCCIBDOUBLE"; - case oracle::occi::OCCINUMBER: return "OCCINUMBER"; - case oracle::occi::OCCI_SQLT_NUM: return "OCCI_SQLT_NUM"; - case oracle::occi::OCCICHAR: return "OCCICHAR"; - case oracle::occi::OCCISTRING: return "OCCISTRING"; - case oracle::occi::OCCI_SQLT_AFC: return "OCCI_SQLT_AFC"; - case oracle::occi::OCCI_SQLT_AVC: return "OCCI_SQLT_AVC"; - case oracle::occi::OCCI_SQLT_CHR: return "OCCI_SQLT_CHR"; - case oracle::occi::OCCI_SQLT_LVC: return "OCCI_SQLT_LVC"; - case oracle::occi::OCCI_SQLT_LNG: return "OCCI_SQLT_LNG"; - case oracle::occi::OCCI_SQLT_STR: return "OCCI_SQLT_STR"; - case oracle::occi::OCCI_SQLT_VCS: return "OCCI_SQLT_VCS"; - case oracle::occi::OCCI_SQLT_VNU: return "OCCI_SQLT_VNU"; - case oracle::occi::OCCI_SQLT_VBI: return "OCCI_SQLT_VBI"; - case oracle::occi::OCCI_SQLT_VST: return "OCCI_SQLT_VST"; - case oracle::occi::OCCI_SQLT_RDD: return "OCCI_SQLT_RDD"; - case oracle::occi::OCCIDATE: return "OCCIDATE"; - case oracle::occi::OCCITIMESTAMP: return "OCCITIMESTAMP"; - case oracle::occi::OCCI_SQLT_DAT: return "OCCI_SQLT_DAT"; - case oracle::occi::OCCI_SQLT_TIMESTAMP: return "OCCI_SQLT_TIMESTAMP"; - case oracle::occi::OCCI_SQLT_TIMESTAMP_LTZ: return "OCCI_SQLT_TIMESTAMP_LTZ"; - case oracle::occi::OCCI_SQLT_TIMESTAMP_TZ: return "OCCI_SQLT_TIMESTAMP_TZ"; - case oracle::occi::OCCIPOBJECT: return "OCCIPOBJECT"; - default: return ""; - } -} diff --git a/plugins/input/occi/occi_types.hpp b/plugins/input/occi/occi_types.hpp deleted file mode 100644 index cc8cd41fa..000000000 --- a/plugins/input/occi/occi_types.hpp +++ /dev/null @@ -1,265 +0,0 @@ -/***************************************************************************** - * - * This file is part of Mapnik (c++ mapping toolkit) - * - * Copyright (C) 2015 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 OCCI_TYPES_HPP -#define OCCI_TYPES_HPP - -// mapnik -#include -#include - -// occi -#include - -// ott generated SDOGeometry classes -#include "spatial_classesh.h" -#include "spatial_classesm.h" - -// check for oracle support -#if OCCI_MAJOR_VERSION >= 10 && OCCI_MINOR_VERSION >= 1 -// We have at least ORACLE 10g >= 10.2.0.X -#else -#error Only ORACLE 10g >= 10.2.0.X is supported ! -#endif - -// geometry types definitions -enum -{ - SDO_GTYPE_UNKNOWN = 0, - SDO_GTYPE_POINT = 1, - SDO_GTYPE_LINE = 2, - SDO_GTYPE_POLYGON = 3, - SDO_GTYPE_COLLECTION = 4, - SDO_GTYPE_MULTIPOINT = 5, - SDO_GTYPE_MULTILINE = 6, - SDO_GTYPE_MULTIPOLYGON = 7, - - SDO_GTYPE_2DPOINT = 2001, - SDO_GTYPE_2DLINE = 2002, - SDO_GTYPE_2DPOLYGON = 2003, - SDO_GTYPE_2DMULTIPOINT = 2005, - SDO_GTYPE_2DMULTILINE = 2006, - SDO_GTYPE_2DMULTIPOLYGON = 2007, - - SDO_ELEM_INFO_SIZE = 3, - - SDO_ETYPE_UNKNOWN = 0, - SDO_ETYPE_POINT = 1, - SDO_ETYPE_LINESTRING = 2, - SDO_ETYPE_POLYGON = 1003, - SDO_ETYPE_POLYGON_INTERIOR = 2003, - SDO_ETYPE_COMPOUND_LINESTRING = 4, - SDO_ETYPE_COMPOUND_POLYGON = 1005, - SDO_ETYPE_COMPOUND_POLYGON_INTERIOR = 2005, - - SDO_INTERPRETATION_POINT = 1, - SDO_INTERPRETATION_RECTANGLE = 3, - SDO_INTERPRETATION_CIRCLE = 4, - SDO_INTERPRETATION_STRAIGHT = 1, - SDO_INTERPRETATION_CIRCULAR = 2 -}; - -class occi_environment : public mapnik::singleton -{ - friend class mapnik::CreateStatic; - -public: - - oracle::occi::Environment* get_environment() - { - return env_; - } - - oracle::occi::Connection* create_connection( - const std::string& user, - const std::string& password, - const std::string& host) - { - MAPNIK_LOG_DEBUG(occi) << "occi_environment: create_connection"; - - return env_->createConnection(user, password, host); - } - - void destroy_connection(oracle::occi::Connection* conn) - { - env_->terminateConnection(conn); - } - - oracle::occi::StatelessConnectionPool* create_pool( - const std::string& user, - const std::string& password, - const std::string& host, - int max_size, - int initial_size, - int incr_size) - { - MAPNIK_LOG_DEBUG(occi) << "occi_environment: create_pool"; - - return env_->createStatelessConnectionPool( - user, - password, - host, - max_size, - initial_size, - incr_size, - oracle::occi::StatelessConnectionPool::HOMOGENEOUS); - } - - void destroy_pool(oracle::occi::StatelessConnectionPool* pool) - { - env_->terminateStatelessConnectionPool( - pool, - oracle::occi::StatelessConnectionPool::SPD_FORCE); - } - -private: - - occi_environment() - : env_(0) - { - MAPNIK_LOG_DEBUG(occi) << "occi_environment: constructor"; - - env_ = oracle::occi::Environment::createEnvironment( - (oracle::occi::Environment::Mode)(oracle::occi::Environment::OBJECT - | oracle::occi::Environment::THREADED_MUTEXED)); - RegisterClasses(env_); - } - - ~occi_environment() - { - MAPNIK_LOG_DEBUG(occi) << "occi_environment: destructor"; - - oracle::occi::Environment::terminateEnvironment(env_); - env_ = 0; - } - - oracle::occi::Environment* env_; -}; - - -class occi_connection_ptr -{ -public: - explicit occi_connection_ptr() - : pool_(0), - conn_(0), - stmt_(0), - rs_(0), - owns_connection_(false) - { - } - - ~occi_connection_ptr() - { - close_query(true); - } - - void set_pool(oracle::occi::StatelessConnectionPool* pool) - { - close_query(true); - - pool_ = pool; - conn_ = pool_->getConnection(); - owns_connection_ = true; - } - - void set_connection(oracle::occi::Connection* conn, bool owns_connection) - { - close_query(true); - - pool_ = 0; - conn_ = conn; - owns_connection_ = owns_connection; - } - - oracle::occi::ResultSet* execute_query(std::string const& s, const unsigned prefetch = 0) - { - close_query(false); - - MAPNIK_LOG_DEBUG(occi) << "occi_connection_ptr: " << s; - - stmt_ = conn_->createStatement(s); - - if (prefetch > 0) - { - stmt_->setPrefetchMemorySize(0); - stmt_->setPrefetchRowCount(prefetch); - } - - rs_ = stmt_->executeQuery(); - - return rs_; - } - -private: - void close_query(const bool release_connection) - { - if (conn_) - { - if (stmt_) - { - if (rs_) - { - stmt_->closeResultSet(rs_); - rs_ = 0; - } - - conn_->terminateStatement(stmt_); - stmt_ = 0; - } - - if (release_connection) - { - if (pool_) - { - pool_->releaseConnection(conn_); - } - else - { - if (owns_connection_) - { - occi_environment::instance().destroy_connection(conn_); - } - } - - conn_ = 0; - } - } - } - - oracle::occi::StatelessConnectionPool* pool_; - oracle::occi::Connection* conn_; - oracle::occi::Statement* stmt_; - oracle::occi::ResultSet* rs_; - bool owns_connection_; -}; - -class occi_enums -{ -public: - - static std::string resolve_gtype(int gtype); - static std::string resolve_etype(int etype); - static std::string resolve_datatype(int type_id); -}; - -#endif // OCCI_TYPES_HPP diff --git a/plugins/input/occi/spatial_classesh.h b/plugins/input/occi/spatial_classesh.h deleted file mode 100644 index 9543b4ef0..000000000 --- a/plugins/input/occi/spatial_classesh.h +++ /dev/null @@ -1,147 +0,0 @@ -#ifndef SPATIAL_CLASSESH_ORACLE -# define SPATIAL_CLASSESH_ORACLE - -#ifndef OCCI_ORACLE -# include -#endif - -class SDOPointType; -class SDOGeometry; - -/************************************************************/ -// generated declarations for the SDO_POINT_TYPE object type. -/************************************************************/ - -class SDOPointType : public oracle::occi::PObject { - -private: - - oracle::occi::Number X; - oracle::occi::Number Y; - oracle::occi::Number Z; - -public: - - oracle::occi::Number getX() const; - - void setX(const oracle::occi::Number &value); - - oracle::occi::Number getY() const; - - void setY(const oracle::occi::Number &value); - - oracle::occi::Number getZ() const; - - void setZ(const oracle::occi::Number &value); - - void *operator new(size_t size); - - void *operator new(size_t size, const oracle::occi::Connection * sess, - const OCCI_STD_NAMESPACE::string& table); - - void *operator new(size_t, void *ctxOCCI_); - - void *operator new(size_t size, const oracle::occi::Connection *sess, - const OCCI_STD_NAMESPACE::string &tableName, - const OCCI_STD_NAMESPACE::string &typeName, - const OCCI_STD_NAMESPACE::string &tableSchema, - const OCCI_STD_NAMESPACE::string &typeSchema); - - OCCI_STD_NAMESPACE::string getSQLTypeName() const; - - void getSQLTypeName(oracle::occi::Environment *env, void **schemaName, - unsigned int &schemaNameLen, void **typeName, - unsigned int &typeNameLen) const; - - SDOPointType(); - - SDOPointType(void *ctxOCCI_) : oracle::occi::PObject (ctxOCCI_) { } - - static void *readSQL(void *ctxOCCI_); - - virtual void readSQL(oracle::occi::AnyData& streamOCCI_); - - static void writeSQL(void *objOCCI_, void *ctxOCCI_); - - virtual void writeSQL(oracle::occi::AnyData& streamOCCI_); - - ~SDOPointType(); - -}; - -/************************************************************/ -// generated declarations for the SDO_GEOMETRY object type. -/************************************************************/ - -class SDOGeometry : public oracle::occi::PObject { - -private: - - oracle::occi::Number SDO_GTYPE; - oracle::occi::Number SDO_SRID; - SDOPointType * SDO_POINT; - OCCI_STD_NAMESPACE::vector< oracle::occi::Number > SDO_ELEM_INFO; - OCCI_STD_NAMESPACE::vector< oracle::occi::Number > SDO_ORDINATES; - -public: - - oracle::occi::Number getSdo_gtype() const; - - void setSdo_gtype(const oracle::occi::Number &value); - - oracle::occi::Number getSdo_srid() const; - - void setSdo_srid(const oracle::occi::Number &value); - - SDOPointType * getSdo_point() const; - - void setSdo_point(SDOPointType * value); - - OCCI_STD_NAMESPACE::vector< oracle::occi::Number >& getSdo_elem_info(); - - const OCCI_STD_NAMESPACE::vector< oracle::occi::Number >& getSdo_elem_info() const; - - void setSdo_elem_info(const OCCI_STD_NAMESPACE::vector< oracle::occi::Number > &value); - - OCCI_STD_NAMESPACE::vector< oracle::occi::Number >& getSdo_ordinates(); - - const OCCI_STD_NAMESPACE::vector< oracle::occi::Number >& getSdo_ordinates() const; - - void setSdo_ordinates(const OCCI_STD_NAMESPACE::vector< oracle::occi::Number > &value); - - void *operator new(size_t size); - - void *operator new(size_t size, const oracle::occi::Connection * sess, - const OCCI_STD_NAMESPACE::string& table); - - void *operator new(size_t, void *ctxOCCI_); - - void *operator new(size_t size, const oracle::occi::Connection *sess, - const OCCI_STD_NAMESPACE::string &tableName, - const OCCI_STD_NAMESPACE::string &typeName, - const OCCI_STD_NAMESPACE::string &tableSchema, - const OCCI_STD_NAMESPACE::string &typeSchema); - - OCCI_STD_NAMESPACE::string getSQLTypeName() const; - - void getSQLTypeName(oracle::occi::Environment *env, void **schemaName, - unsigned int &schemaNameLen, void **typeName, - unsigned int &typeNameLen) const; - - SDOGeometry(); - - SDOGeometry(void *ctxOCCI_) : oracle::occi::PObject (ctxOCCI_) { } - - static void *readSQL(void *ctxOCCI_); - - virtual void readSQL(oracle::occi::AnyData& streamOCCI_); - - static void writeSQL(void *objOCCI_, void *ctxOCCI_); - - virtual void writeSQL(oracle::occi::AnyData& streamOCCI_); - - ~SDOGeometry(); - -}; - -#endif diff --git a/plugins/input/occi/spatial_classesm.cpp b/plugins/input/occi/spatial_classesm.cpp deleted file mode 100644 index aa4ea865c..000000000 --- a/plugins/input/occi/spatial_classesm.cpp +++ /dev/null @@ -1,11 +0,0 @@ - -#ifndef SPATIAL_CLASSESM_ORACLE -# include "spatial_classesm.h" -#endif - -void RegisterClasses(oracle::occi::Environment* envOCCI_) -{ - oracle::occi::Map *mapOCCI_ = envOCCI_->getMap(); - mapOCCI_->put("MDSYS.SDO_POINT_TYPE", &SDOPointType::readSQL, &SDOPointType::writeSQL); - mapOCCI_->put("MDSYS.SDO_GEOMETRY", &SDOGeometry::readSQL, &SDOGeometry::writeSQL); -} diff --git a/plugins/input/occi/spatial_classesm.h b/plugins/input/occi/spatial_classesm.h deleted file mode 100644 index 78ca1c4e5..000000000 --- a/plugins/input/occi/spatial_classesm.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef SPATIAL_CLASSESM_ORACLE -# define SPATIAL_CLASSESM_ORACLE - -#ifndef OCCI_ORACLE -# include -#endif - -#ifndef SPATIAL_CLASSESH_ORACLE -# include "spatial_classesh.h" -#endif - -void RegisterClasses(oracle::occi::Environment* envOCCI_); - -#endif diff --git a/plugins/input/occi/spatial_classeso.cpp b/plugins/input/occi/spatial_classeso.cpp deleted file mode 100644 index cbc704656..000000000 --- a/plugins/input/occi/spatial_classeso.cpp +++ /dev/null @@ -1,312 +0,0 @@ -#ifndef SPATIAL_CLASSESH_ORACLE -# include "spatial_classesh.h" -#endif - - -/*****************************************************************/ -// generated method implementations for the SDO_POINT_TYPE object type. -/*****************************************************************/ - -oracle::occi::Number SDOPointType::getX() const -{ - return X; -} - -void SDOPointType::setX(const oracle::occi::Number &value) -{ - X = value; -} - -oracle::occi::Number SDOPointType::getY() const -{ - return Y; -} - -void SDOPointType::setY(const oracle::occi::Number &value) -{ - Y = value; -} - -oracle::occi::Number SDOPointType::getZ() const -{ - return Z; -} - -void SDOPointType::setZ(const oracle::occi::Number &value) -{ - Z = value; -} - -void *SDOPointType::operator new(size_t size) -{ - return oracle::occi::PObject::operator new(size); -} - -void *SDOPointType::operator new(size_t size, const oracle::occi::Connection * sess, - const OCCI_STD_NAMESPACE::string& table) -{ - return oracle::occi::PObject::operator new(size, sess, table, - (char *) "MDSYS.SDO_POINT_TYPE"); -} - -void *SDOPointType::operator new(size_t size, void *ctxOCCI_) -{ - return oracle::occi::PObject::operator new(size, ctxOCCI_); -} - -void *SDOPointType::operator new(size_t size, - const oracle::occi::Connection *sess, - const OCCI_STD_NAMESPACE::string &tableName, - const OCCI_STD_NAMESPACE::string &typeName, - const OCCI_STD_NAMESPACE::string &tableSchema, - const OCCI_STD_NAMESPACE::string &typeSchema) -{ - return oracle::occi::PObject::operator new(size, sess, tableName, - typeName, tableSchema, typeSchema); -} - -OCCI_STD_NAMESPACE::string SDOPointType::getSQLTypeName() const -{ - return OCCI_STD_NAMESPACE::string("MDSYS.SDO_POINT_TYPE"); -} - -void SDOPointType::getSQLTypeName(oracle::occi::Environment *env, void **schemaName, - unsigned int &schemaNameLen, void **typeName, unsigned int &typeNameLen) const -{ - PObject::getSQLTypeName(env, &SDOPointType::readSQL, schemaName, - schemaNameLen, typeName, typeNameLen); -} - -SDOPointType::SDOPointType() -{ -} - -void *SDOPointType::readSQL(void *ctxOCCI_) -{ - SDOPointType *objOCCI_ = new(ctxOCCI_) SDOPointType(ctxOCCI_); - oracle::occi::AnyData streamOCCI_(ctxOCCI_); - - try - { - if (streamOCCI_.isNull()) - objOCCI_->setNull(); - else - objOCCI_->readSQL(streamOCCI_); - } - catch (oracle::occi::SQLException& excep) - { - delete objOCCI_; - excep.setErrorCtx(ctxOCCI_); - return (void *)nullptr; - } - return (void *)objOCCI_; -} - -void SDOPointType::readSQL(oracle::occi::AnyData& streamOCCI_) -{ - X = streamOCCI_.getNumber(); - Y = streamOCCI_.getNumber(); - Z = streamOCCI_.getNumber(); -} - -void SDOPointType::writeSQL(void *objectOCCI_, void *ctxOCCI_) -{ - SDOPointType *objOCCI_ = (SDOPointType *) objectOCCI_; - oracle::occi::AnyData streamOCCI_(ctxOCCI_); - - try - { - if (objOCCI_->isNull()) - streamOCCI_.setNull(); - else - objOCCI_->writeSQL(streamOCCI_); - } - catch (oracle::occi::SQLException& excep) - { - excep.setErrorCtx(ctxOCCI_); - } - return; -} - -void SDOPointType::writeSQL(oracle::occi::AnyData& streamOCCI_) -{ - streamOCCI_.setNumber(X); - streamOCCI_.setNumber(Y); - streamOCCI_.setNumber(Z); -} - -SDOPointType::~SDOPointType() -{ -} - -/*****************************************************************/ -// generated method implementations for the SDO_GEOMETRY object type. -/*****************************************************************/ - -oracle::occi::Number SDOGeometry::getSdo_gtype() const -{ - return SDO_GTYPE; -} - -void SDOGeometry::setSdo_gtype(const oracle::occi::Number &value) -{ - SDO_GTYPE = value; -} - -oracle::occi::Number SDOGeometry::getSdo_srid() const -{ - return SDO_SRID; -} - -void SDOGeometry::setSdo_srid(const oracle::occi::Number &value) -{ - SDO_SRID = value; -} - -SDOPointType * SDOGeometry::getSdo_point() const -{ - return SDO_POINT; -} - -void SDOGeometry::setSdo_point(SDOPointType * value) -{ - SDO_POINT = value; -} - -OCCI_STD_NAMESPACE::vector< oracle::occi::Number >& SDOGeometry::getSdo_elem_info() -{ - return SDO_ELEM_INFO; -} - -const OCCI_STD_NAMESPACE::vector< oracle::occi::Number >& SDOGeometry::getSdo_elem_info() const -{ - return SDO_ELEM_INFO; -} - -void SDOGeometry::setSdo_elem_info(const OCCI_STD_NAMESPACE::vector< oracle::occi::Number > &value) -{ - SDO_ELEM_INFO = value; -} - -OCCI_STD_NAMESPACE::vector< oracle::occi::Number >& SDOGeometry::getSdo_ordinates() -{ - return SDO_ORDINATES; -} - -const OCCI_STD_NAMESPACE::vector< oracle::occi::Number >& SDOGeometry::getSdo_ordinates() const -{ - return SDO_ORDINATES; -} - -void SDOGeometry::setSdo_ordinates(const OCCI_STD_NAMESPACE::vector< oracle::occi::Number > &value) -{ - SDO_ORDINATES = value; -} - -void *SDOGeometry::operator new(size_t size) -{ - return oracle::occi::PObject::operator new(size); -} - -void *SDOGeometry::operator new(size_t size, const oracle::occi::Connection * sess, - const OCCI_STD_NAMESPACE::string& table) -{ - return oracle::occi::PObject::operator new(size, sess, table, - (char *) "MDSYS.SDO_GEOMETRY"); -} - -void *SDOGeometry::operator new(size_t size, void *ctxOCCI_) -{ - return oracle::occi::PObject::operator new(size, ctxOCCI_); -} - -void *SDOGeometry::operator new(size_t size, - const oracle::occi::Connection *sess, - const OCCI_STD_NAMESPACE::string &tableName, - const OCCI_STD_NAMESPACE::string &typeName, - const OCCI_STD_NAMESPACE::string &tableSchema, - const OCCI_STD_NAMESPACE::string &typeSchema) -{ - return oracle::occi::PObject::operator new(size, sess, tableName, - typeName, tableSchema, typeSchema); -} - -OCCI_STD_NAMESPACE::string SDOGeometry::getSQLTypeName() const -{ - return OCCI_STD_NAMESPACE::string("MDSYS.SDO_GEOMETRY"); -} - -void SDOGeometry::getSQLTypeName(oracle::occi::Environment *env, void **schemaName, - unsigned int &schemaNameLen, void **typeName, unsigned int &typeNameLen) const -{ - PObject::getSQLTypeName(env, &SDOGeometry::readSQL, schemaName, - schemaNameLen, typeName, typeNameLen); -} - -SDOGeometry::SDOGeometry() -{ - SDO_POINT = (SDOPointType *) 0; -} - -void *SDOGeometry::readSQL(void *ctxOCCI_) -{ - SDOGeometry *objOCCI_ = new(ctxOCCI_) SDOGeometry(ctxOCCI_); - oracle::occi::AnyData streamOCCI_(ctxOCCI_); - - try - { - if (streamOCCI_.isNull()) - objOCCI_->setNull(); - else - objOCCI_->readSQL(streamOCCI_); - } - catch (oracle::occi::SQLException& excep) - { - delete objOCCI_; - excep.setErrorCtx(ctxOCCI_); - return (void *)nullptr; - } - return (void *)objOCCI_; -} - -void SDOGeometry::readSQL(oracle::occi::AnyData& streamOCCI_) -{ - SDO_GTYPE = streamOCCI_.getNumber(); - SDO_SRID = streamOCCI_.getNumber(); - SDO_POINT = (SDOPointType *) streamOCCI_.getObject(&SDOPointType::readSQL); - oracle::occi::getVector(streamOCCI_, SDO_ELEM_INFO); - oracle::occi::getVector(streamOCCI_, SDO_ORDINATES); -} - -void SDOGeometry::writeSQL(void *objectOCCI_, void *ctxOCCI_) -{ - SDOGeometry *objOCCI_ = (SDOGeometry *) objectOCCI_; - oracle::occi::AnyData streamOCCI_(ctxOCCI_); - - try - { - if (objOCCI_->isNull()) - streamOCCI_.setNull(); - else - objOCCI_->writeSQL(streamOCCI_); - } - catch (oracle::occi::SQLException& excep) - { - excep.setErrorCtx(ctxOCCI_); - } - return; -} - -void SDOGeometry::writeSQL(oracle::occi::AnyData& streamOCCI_) -{ - streamOCCI_.setNumber(SDO_GTYPE); - streamOCCI_.setNumber(SDO_SRID); - streamOCCI_.setObject(SDO_POINT); - oracle::occi::setVector(streamOCCI_, SDO_ELEM_INFO); - oracle::occi::setVector(streamOCCI_, SDO_ORDINATES); -} - -SDOGeometry::~SDOGeometry() -{ - delete SDO_POINT; -} diff --git a/plugins/input/occi/spatial_types.typ b/plugins/input/occi/spatial_types.typ deleted file mode 100644 index 5c9c0a72e..000000000 --- a/plugins/input/occi/spatial_types.typ +++ /dev/null @@ -1,4 +0,0 @@ -TYPE MDSYS.SDO_POINT_TYPE AS SDOPointType -TYPE MDSYS.SDO_GEOMETRY AS SDOGeometry - - diff --git a/plugins/input/osm/basiccurl.cpp b/plugins/input/osm/basiccurl.cpp deleted file mode 100755 index fd8361453..000000000 --- a/plugins/input/osm/basiccurl.cpp +++ /dev/null @@ -1,75 +0,0 @@ -/***************************************************************************** - * - * This file is part of Mapnik (c++ mapping toolkit) - * - * Copyright (C) 2015 Artem Pavlenko - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - *****************************************************************************/ - -#include "basiccurl.h" - -#include -#include - -CURL_LOAD_DATA* grab_http_response(const char* url) -{ - CURL_LOAD_DATA* data; - - CURL* curl = curl_easy_init(); - - if(curl) - { - data = do_grab(curl, url); - curl_easy_cleanup(curl); - return data; - } - return nullptr; -} - -CURL_LOAD_DATA* do_grab(CURL* curl,const char* url) -{ - CURL_LOAD_DATA* data = (CURL_LOAD_DATA*)malloc(sizeof(CURL_LOAD_DATA)); - data->data = nullptr; - data->nbytes = 0; - - curl_easy_setopt(curl, CURLOPT_URL, url); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, response_callback); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, data); - - CURLcode res = curl_easy_perform(curl); - if (res !=0) { - std::clog << "error grabbing data\n"; - } - - return data; -} - -size_t response_callback(void* ptr, size_t size, size_t nmemb, void* d) -{ - size_t rsize = size * nmemb; - CURL_LOAD_DATA* data = (CURL_LOAD_DATA*)d; - - // fprintf(stderr,"rsize is %d\n", rsize); - - data->data = (char*)realloc(data->data, (data->nbytes + rsize) * sizeof(char)); - std::memcpy(&(data->data[data->nbytes]), ptr, rsize); - data->nbytes += rsize; - - // fprintf(stderr,"data->nbytes is %d\n", data->nbytes); - - return rsize; -} diff --git a/plugins/input/osm/basiccurl.h b/plugins/input/osm/basiccurl.h deleted file mode 100755 index 7ab529783..000000000 --- a/plugins/input/osm/basiccurl.h +++ /dev/null @@ -1,40 +0,0 @@ -/***************************************************************************** - * - * This file is part of Mapnik (c++ mapping toolkit) - * - * Copyright (C) 2011 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 BASICCURL_H -#define BASICCURL_H - -#include -#include -#include - -typedef struct -{ - char *data; - int nbytes; -} CURL_LOAD_DATA; - -CURL_LOAD_DATA *grab_http_response(const char *url); -CURL_LOAD_DATA *do_grab(CURL *curl, const char *url); -size_t response_callback(void *ptr ,size_t size, size_t nmemb, void *data); - -#endif // BASICCURL_H diff --git a/plugins/input/osm/build.py b/plugins/input/osm/build.py deleted file mode 100644 index 061fc859c..000000000 --- a/plugins/input/osm/build.py +++ /dev/null @@ -1,69 +0,0 @@ -# -# This file is part of Mapnik (c++ mapping toolkit) -# -# Copyright (C) 2015 Artem Pavlenko -# -# Mapnik 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 -# -# - -Import ('plugin_base') -Import ('env') -from copy import copy - -PLUGIN_NAME = 'osm' - -plugin_env = plugin_base.Clone() - -plugin_sources = Split( - """ - %(PLUGIN_NAME)s.cpp - %(PLUGIN_NAME)s_datasource.cpp - %(PLUGIN_NAME)s_featureset.cpp - osmparser.cpp - dataset_deliverer.cpp - """ % locals() -) - -plugin_env['LIBS'] = [] -plugin_env.Append(LIBS='xml2') - -# Link Library to Dependencies -libraries = copy(plugin_env['LIBS']) -libraries.append(env['ICU_LIB_NAME']) -libraries.append('boost_system%s' % env['BOOST_APPEND']) - -if env['PLUGIN_LINKING'] == 'shared': - libraries.append(env['MAPNIK_NAME']) - - TARGET = plugin_env.SharedLibrary('../%s' % PLUGIN_NAME, - SHLIBPREFIX='', - SHLIBSUFFIX='.input', - source=plugin_sources, - LIBS=libraries) - - # if the plugin links to libmapnik ensure it is built first - Depends(TARGET, env.subst('../../../src/%s' % env['MAPNIK_LIB_NAME'])) - - if 'uninstall' not in COMMAND_LINE_TARGETS: - env.Install(env['MAPNIK_INPUT_PLUGINS_DEST'], TARGET) - env.Alias('install', env['MAPNIK_INPUT_PLUGINS_DEST']) - -plugin_obj = { - 'LIBS': libraries, - 'SOURCES': plugin_sources, -} - -Return('plugin_obj') diff --git a/plugins/input/osm/dataset_deliverer.cpp b/plugins/input/osm/dataset_deliverer.cpp deleted file mode 100644 index 6d7fe31d9..000000000 --- a/plugins/input/osm/dataset_deliverer.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/***************************************************************************** - * - * This file is part of Mapnik (c++ mapping toolkit) - * - * Copyright (C) 2015 Artem Pavlenko - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - *****************************************************************************/ - -// mapnik -#include -#include -#include - -// std -#include - -#include "dataset_deliverer.h" - -osm_dataset * dataset_deliverer::dataset = nullptr; -std::string dataset_deliverer::last_bbox = ""; -std::string dataset_deliverer::last_filename = ""; - -osm_dataset* dataset_deliverer::load_from_file(const string& file, const string& parser) -{ - // Only actually load from file if we haven't done so already - if (dataset == nullptr) - { - if (!mapnik::util::exists(file)) - { - throw mapnik::datasource_exception("OSM Plugin: '" + file + "' does not exist"); - } - - dataset = new osm_dataset; - if (dataset->load(file.c_str(), parser) == false) - { - return nullptr; - } - - atexit(dataset_deliverer::release); - last_filename = file; - } - else if(file != last_filename) - { - dataset = new osm_dataset; - if (dataset->load(file.c_str(), parser) == false) - { - return nullptr; - } - last_filename = file; - } - return dataset; -} diff --git a/plugins/input/osm/osm.cpp b/plugins/input/osm/osm.cpp deleted file mode 100644 index 76d3bac18..000000000 --- a/plugins/input/osm/osm.cpp +++ /dev/null @@ -1,230 +0,0 @@ -/***************************************************************************** - * - * This file is part of Mapnik (c++ mapping toolkit) - * - * Copyright (C) 2015 Artem Pavlenko - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - *****************************************************************************/ - -#include "osm.h" -#include "osmparser.h" - -#include - -#include -#include -#include -#include -#include -#include - -polygon_types osm_way::ptypes; - -bool osm_dataset::load(const char* filename,std::string const& parser) -{ - if (parser == "libxml2") - { - return osmparser::parse(this, filename); - } - return false; -} - -osm_dataset::~osm_dataset() -{ - clear(); -} - -void osm_dataset::clear() -{ - MAPNIK_LOG_DEBUG(osm) << "osm_dataset: Clear"; - - MAPNIK_LOG_DEBUG(osm) << "osm_dataset: -- Deleting ways"; - for (unsigned int count = 0; count < ways.size(); ++count) - { - delete ways[count]; - ways[count] = nullptr; - } - ways.clear(); - - MAPNIK_LOG_DEBUG(osm) << "osm_dataset: -- Deleting nodes"; - for (unsigned int count = 0; count < nodes.size(); ++count) - { - delete nodes[count]; - nodes[count] = nullptr; - } - nodes.clear(); - - MAPNIK_LOG_DEBUG(osm) << "osm_dataset: Clear done"; -} - -std::string osm_dataset::to_string() -{ - std::string result; - - for (unsigned int count = 0; count < nodes.size(); ++count) - { - result += nodes[count]->to_string(); - } - - for (unsigned int count = 0; count < ways.size(); ++count) - { - result += ways[count]->to_string(); - } - - return result; -} - -bounds osm_dataset::get_bounds() -{ - bounds b (-180, -90, 180, 90); - for (unsigned int count = 0; count < nodes.size(); ++count) - { - if(nodes[count]->lon > b.w) b.w = nodes[count]->lon; - if(nodes[count]->lon < b.e) b.e = nodes[count]->lon; - if(nodes[count]->lat > b.s) b.s = nodes[count]->lat; - if(nodes[count]->lat < b.n) b.n = nodes[count]->lat; - } - return b; -} - -osm_node* osm_dataset::next_node() -{ - if (node_i != nodes.end()) - { - return *(node_i++); - } - return nullptr; -} - -osm_way* osm_dataset::next_way() -{ - if (way_i != ways.end()) - { - return *(way_i++); - } - return nullptr; -} - -osm_item* osm_dataset::next_item() -{ - osm_item* item = nullptr; - if (next_item_mode == Node) - { - item = next_node(); - if (item == nullptr) - { - next_item_mode = Way; - rewind_ways(); - item = next_way(); - } - } - else - { - item = next_way(); - } - return item; -} - -std::set osm_dataset::get_keys() -{ - std::set keys; - for (unsigned int count = 0; count < nodes.size(); ++count) - { - for (std::map::iterator i = nodes[count]->keyvals.begin(); - i != nodes[count]->keyvals.end(); i++) - { - keys.insert(i->first); - } - } - - for (unsigned int count = 0; count < ways.size(); ++count) - { - for (std::map::iterator i = ways[count]->keyvals.begin(); - i != ways[count]->keyvals.end(); i++) - { - keys.insert(i->first); - } - } - return keys; -} - - -std::string osm_item::to_string() -{ - std::ostringstream strm; - strm << "id=" << id << std::endl << "Keyvals: " << std::endl; - - for (std::map::iterator i = keyvals.begin(); - i != keyvals.end(); i++) - { - strm << "Key " << i->first << " Value " << i->second << std::endl; - } - - return strm.str(); -} - -std::string osm_node::to_string() -{ - std::ostringstream strm; - strm << "Node: " << osm_item::to_string() << " lat=" << lat <<" lon=" <id << " "; - } - } - - strm << std::endl; - return strm.str(); -} - -bounds osm_way::get_bounds() -{ - bounds b (-180, -90, 180, 90); - - for (unsigned int count = 0; count < nodes.size(); ++count) - { - if(nodes[count]->lon > b.w) b.w = nodes[count]->lon; - if(nodes[count]->lon < b.e) b.e = nodes[count]->lon; - if(nodes[count]->lat > b.s) b.s = nodes[count]->lat; - if(nodes[count]->lat < b.n) b.n = nodes[count]->lat; - } - return b; -} - -bool osm_way::is_polygon() -{ - for (unsigned int count = 0; count < ptypes.ptypes.size(); ++count) - { - if (keyvals.find(ptypes.ptypes[count].first) != keyvals.end() && - (ptypes.ptypes[count].second.empty() || keyvals[ptypes.ptypes[count].first] == ptypes.ptypes[count].second)) - { - return true; - } - } - - return false; -} diff --git a/plugins/input/osm/osm.h b/plugins/input/osm/osm.h deleted file mode 100644 index 86a48a9af..000000000 --- a/plugins/input/osm/osm.h +++ /dev/null @@ -1,136 +0,0 @@ -/***************************************************************************** - * - * This file is part of Mapnik (c++ mapping toolkit) - * - * Copyright (C) 2011 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 OSM_H -#define OSM_H - -#include -#include -#include -#include -#include -#include - -struct bounds -{ - double w, s, e, n; - bounds() { w = -180; s = -90; e = 180; n = 90; } - bounds(double w_, double s_, double e_, double n_) - { - this->w = w_; - this->s = s_; - this->e = e_; - this->n = n_; - } -}; - -class polygon_types -{ -public: - std::vector > ptypes; - - polygon_types() - { - ptypes.push_back(std::pair("water", "")); - ptypes.push_back(std::pair("aeroway", "")); - ptypes.push_back(std::pair("building", "")); - ptypes.push_back(std::pair("natural", "wood")); - ptypes.push_back(std::pair("natural", "water")); - ptypes.push_back(std::pair("natural", "heath")); - ptypes.push_back(std::pair("natural", "marsh")); - ptypes.push_back(std::pair("military", "danger_area")); - ptypes.push_back(std::pair("landuse", "forest")); - ptypes.push_back(std::pair("landuse", "industrial")); - ptypes.push_back(std::pair("leisure", "park")); - ptypes.push_back(std::pair("area", "yes")); - } -}; - -struct osm_item -{ - mapnik::value_integer id; - std::map keyvals; - virtual std::string to_string(); - virtual ~osm_item() {} -}; - -struct osm_node : public osm_item -{ - double lat, lon; - std::string to_string(); -}; - -struct osm_way : public osm_item -{ - std::vector nodes; - std::string to_string(); - bounds get_bounds(); - bool is_polygon(); - static polygon_types ptypes; -}; - -class osm_dataset -{ -public: - osm_dataset() - { - node_i = nodes.begin(); - way_i = ways.begin(); - next_item_mode = Node; - } - - osm_dataset(const char* name) - { - node_i = nodes.begin(); - way_i = ways.begin(); - next_item_mode = Node; - load(name); - } - - ~osm_dataset(); - - bool load(const char* name, std::string const& parser = "libxml2"); - void clear(); - void add_node(osm_node* n) { nodes.push_back(n); } - void add_way(osm_way* w) { ways.push_back(w); } - std::string to_string(); - bounds get_bounds(); - std::set get_keys(); - void rewind_nodes() { node_i = nodes.begin(); } - void rewind_ways() { way_i = ways.begin(); } - void rewind() { rewind_nodes(); rewind_ways(); next_item_mode = Node; } - osm_node * next_node(); - osm_way * next_way(); - osm_item * next_item(); - bool current_item_is_node() { return next_item_mode == Node; } - bool current_item_is_way() { return next_item_mode == Way; } - -private: - int next_item_mode; - enum { Node, Way }; - std::vector::iterator node_i; - std::vector::iterator way_i; - std::vector nodes; - std::vector ways; -}; - -#endif // OSM_H diff --git a/plugins/input/osm/osm_datasource.cpp b/plugins/input/osm/osm_datasource.cpp deleted file mode 100644 index 4bbe1bfde..000000000 --- a/plugins/input/osm/osm_datasource.cpp +++ /dev/null @@ -1,159 +0,0 @@ -/***************************************************************************** - * - * This file is part of Mapnik (c++ mapping toolkit) - * - * Copyright (C) 2015 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 - * - *****************************************************************************/ - -// stl -#include -#include - -// mapnik -#include -#include -#include -#include - -// boost - -#include "osm_datasource.hpp" -#include "osm_featureset.hpp" -#include "dataset_deliverer.h" -#include "osmtagtypes.h" -#include "osmparser.h" - -using mapnik::String; -using mapnik::Double; -using mapnik::Integer; -using mapnik::datasource_exception; -using mapnik::filter_in_box; -using mapnik::filter_at_point; -using mapnik::attribute_descriptor; - -DATASOURCE_PLUGIN(osm_datasource) - -osm_datasource::osm_datasource(const parameters& params) - : datasource (params), - extent_(), - type_(datasource::Vector), - desc_(osm_datasource::name(), *params.get("encoding", "utf-8")) -{ - osm_data_ = nullptr; - std::string osm_filename = *params.get("file", ""); - std::string parser = *params.get("parser", "libxml2"); - std::string url = *params.get("url", ""); - std::string bbox = *params.get("bbox", ""); - - // load the data - if (url != "" && bbox != "") - { - throw datasource_exception("Error loading from URL is no longer supported (removed in >= Mapnik 2.3.x"); - } - else if (osm_filename != "") - { - // if we supplied a filename, load from file - if ((osm_data_ = dataset_deliverer::load_from_file(osm_filename, parser)) == nullptr) - { - std::string s("OSM Plugin: Error loading from file '"); - s += osm_filename + "'"; - throw datasource_exception(s); - } - } - else - { - throw datasource_exception("OSM Plugin: Neither 'file' nor 'url' and 'bbox' specified"); - } - - - osm_tag_types tagtypes; - tagtypes.add_type("maxspeed", mapnik::Integer); - tagtypes.add_type("z_order", mapnik::Integer); - - osm_data_->rewind(); - - // Need code to get the attributes of all the data - std::set keys = osm_data_->get_keys(); - - // Add the attributes to the datasource descriptor - assume they are - // all of type String - for (auto const& key : keys) - { - desc_.add_descriptor(attribute_descriptor(key, tagtypes.get_type(key))); - } - // Get the bounds of the data and set extent_ accordingly - bounds b = osm_data_->get_bounds(); - extent_ = box2d(b.w,b.s,b.e,b.n); -} - -osm_datasource::~osm_datasource() -{ - // Do not do as is now static variable and cleaned up at exit - //delete osm_data_; -} - -const char * osm_datasource::name() -{ - return "osm"; -} - -mapnik::datasource::datasource_t osm_datasource::type() const -{ - return type_; -} - -layer_descriptor osm_datasource::get_descriptor() const -{ - return desc_; -} - -featureset_ptr osm_datasource::features(const query& q) const -{ - filter_in_box filter(q.get_bbox()); - // so we need to filter osm features by bbox here... - - return std::make_shared >(filter, - osm_data_, - q.property_names(), - desc_.get_encoding()); -} - -featureset_ptr osm_datasource::features_at_point(coord2d const& pt, double tol) const -{ - filter_at_point filter(pt); - // collect all attribute names - std::set names; - for (auto const& elem : desc_.get_descriptors()) - { - names.insert(elem.get_name()); - } - return std::make_shared >(filter, - osm_data_, - names, - desc_.get_encoding()); -} - -box2d osm_datasource::envelope() const -{ - return extent_; -} - -boost::optional osm_datasource::get_geometry_type() const -{ - return boost::optional(mapnik::datasource_geometry_t::Collection); -} diff --git a/plugins/input/osm/osm_datasource.hpp b/plugins/input/osm/osm_datasource.hpp deleted file mode 100644 index 1bd8bb387..000000000 --- a/plugins/input/osm/osm_datasource.hpp +++ /dev/null @@ -1,73 +0,0 @@ -/***************************************************************************** - * - * This file is part of Mapnik (c++ mapping toolkit) - * - * Copyright (C) 2015 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 OSM_DATASOURCE_HPP -#define OSM_DATASOURCE_HPP - -// mapnik -#include -#include -#include -#include -#include -#include -#include - -// boost -#include -#include - -// stl -#include -#include - -#include "osm.h" - -using mapnik::datasource; -using mapnik::parameters; -using mapnik::query; -using mapnik::featureset_ptr; -using mapnik::layer_descriptor; -using mapnik::coord2d; -using mapnik::box2d; - -class osm_datasource : public datasource -{ -public: - osm_datasource(const parameters& params); - virtual ~osm_datasource(); - mapnik::datasource::datasource_t type() const; - static const char * name(); - featureset_ptr features(const query& q) const; - featureset_ptr features_at_point(coord2d const& pt, double tol = 0) const; - box2d envelope() const; - boost::optional get_geometry_type() const; - layer_descriptor get_descriptor() const; - -private: - box2d extent_; - osm_dataset* osm_data_; - mapnik::datasource::datasource_t type_; - layer_descriptor desc_; -}; - -#endif // OSM_DATASOURCE_HPP diff --git a/plugins/input/osm/osm_featureset.cpp b/plugins/input/osm/osm_featureset.cpp deleted file mode 100644 index 46e796681..000000000 --- a/plugins/input/osm/osm_featureset.cpp +++ /dev/null @@ -1,132 +0,0 @@ -/***************************************************************************** - * - * This file is part of Mapnik (c++ mapping toolkit) - * - * Copyright (C) 2015 Artem Pavlenko - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - *****************************************************************************/ - -// mapnik -#include -#include -#include -#include -#include -#include -#include - -#include "osm_featureset.hpp" - -using mapnik::feature_ptr; -using mapnik::feature_factory; - -template -osm_featureset::osm_featureset(const filterT& filter, - osm_dataset* dataset, - const std::set& - attribute_names, - std::string const& encoding) - : filter_(filter), - query_ext_(), - tr_(new transcoder(encoding)), - dataset_ (dataset), - attribute_names_ (attribute_names), - ctx_(std::make_shared()) -{ - dataset_->rewind(); -} - -template -feature_ptr osm_featureset::next() -{ - feature_ptr feature; - - osm_item* cur_item = dataset_->next_item(); - if (!cur_item) return feature_ptr(); - if (dataset_->current_item_is_node()) - { - feature = feature_factory::create(ctx_, cur_item->id); - double lat = static_cast(cur_item)->lat; - double lon = static_cast(cur_item)->lon; - feature->set_geometry(mapnik::geometry::point(lon,lat)); - } - else if (dataset_->current_item_is_way()) - { - // Loop until we find a feature which passes the filter - while (cur_item) - { - bounds b = static_cast(cur_item)->get_bounds(); - if (filter_.pass(box2d(b.w, b.s, b.e, b.n)) - && - static_cast(cur_item)->nodes.size()) break; - cur_item = dataset_->next_item(); - } - - if (!cur_item) return feature_ptr(); - feature = feature_factory::create(ctx_, cur_item->id); - if (static_cast(cur_item)->is_polygon()) - { - mapnik::geometry::linear_ring ring; - for (unsigned int count = 0; - count < static_cast(cur_item)->nodes.size(); - count++) - { - ring.add_coord(static_cast(cur_item)->nodes[count]->lon, - static_cast(cur_item)->nodes[count]->lat); - } - mapnik::geometry::polygon geom; - geom.set_exterior_ring(std::move(ring)); - mapnik::geometry::correct(geom); - feature->set_geometry(std::move(geom)); - } - else - { - mapnik::geometry::line_string geom; - for (unsigned int count = 0; - count < static_cast(cur_item)->nodes.size(); - count++) - { - geom.add_coord(static_cast(cur_item)->nodes[count]->lon, - static_cast(cur_item)->nodes[count]->lat); - } - feature->set_geometry(std::move(geom)); - } - } - else - { - MAPNIK_LOG_ERROR(osm_featureset) << "Current item is neither node nor way.\n"; - } - - std::set::const_iterator itr = attribute_names_.begin(); - std::set::const_iterator end = attribute_names_.end(); - std::map::iterator end_keyvals = cur_item->keyvals.end(); - for (; itr != end; itr++) - { - std::map::iterator i = cur_item->keyvals.find(*itr); - if (i != end_keyvals) - { - feature->put_new(i->first, tr_->transcode(i->second.c_str())); - } - } - return feature; -} - -template -osm_featureset::~osm_featureset() {} - -template class osm_featureset; -template class osm_featureset; diff --git a/plugins/input/osm/osm_featureset.hpp b/plugins/input/osm/osm_featureset.hpp deleted file mode 100644 index 7eed2e636..000000000 --- a/plugins/input/osm/osm_featureset.hpp +++ /dev/null @@ -1,72 +0,0 @@ -/***************************************************************************** - * - * This file is part of Mapnik (c++ mapping toolkit) - * - * Copyright (C) 2015 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 OSM_FS_HH -#define OSM_FS_HH - -// stl -#include - -// boost - - -// mapnik -#include -#include -#include -#include -#include - -#include "osm.h" - -using mapnik::Featureset; -using mapnik::box2d; -using mapnik::feature_ptr; -using mapnik::transcoder; - -template -class osm_featureset : public Featureset -{ -public: - osm_featureset(const filterT& filter, - osm_dataset* dataset, - const std::set& attribute_names, - std::string const& encoding); - virtual ~osm_featureset(); - feature_ptr next(); - -private: - filterT filter_; - box2d query_ext_; - const std::unique_ptr tr_; - std::vector attr_ids_; - mutable box2d feature_ext_; - mutable int total_geom_size; - osm_dataset *dataset_; - std::set attribute_names_; - mapnik::context_ptr ctx_; - - osm_featureset(const osm_featureset&); - const osm_featureset& operator=(const osm_featureset&); -}; - -#endif // OSM_FS_HH diff --git a/plugins/input/osm/osmparser.cpp b/plugins/input/osm/osmparser.cpp deleted file mode 100644 index b6c3d9bc2..000000000 --- a/plugins/input/osm/osmparser.cpp +++ /dev/null @@ -1,150 +0,0 @@ -// much of this is based on osm2pgsql - -#include "osmparser.h" -#include "osm.h" -#include -#include -#include - -osm_item* osmparser::cur_item=nullptr; -mapnik::value_integer osmparser::curID=0; -bool osmparser::in_node=false, osmparser::in_way=false; -osm_dataset* osmparser::components=nullptr; -std::string osmparser::error=""; -std::map osmparser::tmp_node_store=std::map(); - -void osmparser::processNode(xmlTextReaderPtr reader) -{ - xmlChar *name = xmlTextReaderName(reader); - if(name==nullptr) - name=xmlStrdup(BAD_CAST "--"); - - switch(xmlTextReaderNodeType(reader)) - { - case XML_READER_TYPE_ELEMENT: - startElement(reader,name); - break; - - case XML_READER_TYPE_END_ELEMENT: - endElement(name); - } - xmlFree(name); -} - -void osmparser::startElement(xmlTextReaderPtr reader, const xmlChar *name) -{ - std::string tags; - xmlChar *xid, *xlat, *xlon, *xk, *xv; - - if(xmlStrEqual(name,BAD_CAST "node")) - { - curID = 0; - in_node = true; - osm_node *node=new osm_node; - xlat=xmlTextReaderGetAttribute(reader,BAD_CAST "lat"); - xlon=xmlTextReaderGetAttribute(reader,BAD_CAST "lon"); - xid=xmlTextReaderGetAttribute(reader,BAD_CAST "id"); - assert(xlat); - assert(xlon); - assert(xid); - node->lat=atof((char*)xlat); - node->lon=atof((char*)xlon); - mapnik::util::string2int((char *)xid, node->id); - cur_item = node; - tmp_node_store[node->id] = node; - xmlFree(xid); - xmlFree(xlon); - xmlFree(xlat); - } - else if (xmlStrEqual(name,BAD_CAST "way")) - { - curID=0; - in_way = true; - osm_way *way=new osm_way; - xid=xmlTextReaderGetAttribute(reader,BAD_CAST "id"); - assert(xid); - mapnik::util::string2int((char *)xid, way->id); - cur_item = way; - xmlFree(xid); - } - else if (xmlStrEqual(name,BAD_CAST "nd")) - { - xid=xmlTextReaderGetAttribute(reader,BAD_CAST "ref"); - assert(xid); - mapnik::value_integer ndid; - mapnik::util::string2int((char *)xid, ndid); - if(tmp_node_store.find(ndid)!=tmp_node_store.end()) - { - (static_cast(cur_item))->nodes.push_back - (tmp_node_store[ndid]); - } - xmlFree(xid); - } - else if (xmlStrEqual(name,BAD_CAST "tag")) - { - std::string key="", value=""; - xk = xmlTextReaderGetAttribute(reader,BAD_CAST "k"); - xv = xmlTextReaderGetAttribute(reader,BAD_CAST "v"); - assert(xk); - assert(xv); - cur_item->keyvals[(char*)xk] = (char*)xv; - xmlFree(xk); - xmlFree(xv); - } - if (xmlTextReaderIsEmptyElement(reader)) - { - // Fake endElement for empty nodes - endElement(name); - } -} - -void osmparser::endElement(const xmlChar* name) -{ - if(xmlStrEqual(name,BAD_CAST "node")) - { - in_node = false; - components->add_node(static_cast(cur_item)); - } - else if(xmlStrEqual(name,BAD_CAST "way")) - { - in_way = false; - components->add_way(static_cast(cur_item)); - } -} - -bool osmparser::parse(osm_dataset *ds, const char* filename) -{ - components=ds; - xmlTextReaderPtr reader = xmlNewTextReaderFilename(filename); - int ret=do_parse(reader); - xmlFreeTextReader(reader); - return (ret==0) ? true:false; -} - -bool osmparser::parse(osm_dataset *ds,char* data, int nbytes) -{ - // from cocoasamurai.blogspot.com/2008/10/getting-some-xml-love-with- - // libxml2.html, converted from Objective-C to straight C - - components=ds; - xmlTextReaderPtr reader = xmlReaderForMemory(data,nbytes,nullptr,nullptr,0); - int ret=do_parse(reader); - xmlFreeTextReader(reader); - return (ret==0) ? true:false; -} - - -int osmparser::do_parse(xmlTextReaderPtr reader) -{ - int ret=-1; - if(reader!=nullptr) - { - ret = xmlTextReaderRead(reader); - while(ret==1) - { - processNode(reader); - ret=xmlTextReaderRead(reader); - } - } - return ret; -} diff --git a/plugins/input/osm/osmparser.h b/plugins/input/osm/osmparser.h deleted file mode 100644 index 9f2aeacd8..000000000 --- a/plugins/input/osm/osmparser.h +++ /dev/null @@ -1,54 +0,0 @@ -/***************************************************************************** - * - * This file is part of Mapnik (c++ mapping toolkit) - * - * Copyright (C) 2011 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 OSMPARSER_H -#define OSMPARSER_H - -#include -#include -#include -#include -#include -#include "osm.h" -#include - -class osmparser -{ -public: - static void processNode(xmlTextReaderPtr reader); - static void startElement(xmlTextReaderPtr reader, const xmlChar* name); - static void endElement(const xmlChar* name); - static bool parse(osm_dataset* ds, const char* filename); - static bool parse(osm_dataset* ds, char* data, int nbytes); - -private: - static osm_item *cur_item; - static mapnik::value_integer curID; - static bool in_node, in_way; - static osm_dataset* components; - static std::string error; - static std::map tmp_node_store; - - static int do_parse(xmlTextReaderPtr); -}; - -#endif // OSMPARSER_H diff --git a/plugins/input/osm/osmtagtypes.h b/plugins/input/osm/osmtagtypes.h deleted file mode 100644 index 191514757..000000000 --- a/plugins/input/osm/osmtagtypes.h +++ /dev/null @@ -1,50 +0,0 @@ -/***************************************************************************** - * - * This file is part of Mapnik (c++ mapping toolkit) - * - * Copyright (C) 2011 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 OSMTAGTYPES_H -#define OSMTAGTYPES_H - -// osmtagtypes.h -// for finding the types of particular tags - -// mapnik -#include - -class osm_tag_types -{ -public: - void add_type(std::string tag, mapnik::eAttributeType type) - { - types[tag] = type; - } - - mapnik::eAttributeType get_type(std::string tag) - { - std::map::iterator i = types.find(tag); - return (i == types.end()) ? mapnik::String : i->second; - } - -private: - std::map types; -}; - -#endif // OSMTAGTYPES_H diff --git a/plugins/input/pgraster/pgraster_wkb_reader.cpp b/plugins/input/pgraster/pgraster_wkb_reader.cpp index 0320fe106..87f71c093 100644 --- a/plugins/input/pgraster/pgraster_wkb_reader.cpp +++ b/plugins/input/pgraster/pgraster_wkb_reader.cpp @@ -271,18 +271,30 @@ mapnik::raster_ptr read_grayscale_band(mapnik::box2d const& bbox, int val; + int nodataval; uint8_t * data = image.bytes(); int ps = 4; // sizeof(image::pixel_type) int off; - val = reader(); // nodata value, need to read anyway + nodataval = reader(); // nodata value, need to read anyway for (int y=0; y 255 ) val = 255; + // Calculate pixel offset off = y * width * ps + x * ps; - // Pixel space is RGBA + // Pixel space is RGBA, fill all w/ same value for Grey data[off+0] = val; data[off+1] = val; data[off+2] = val; + // Set the alpha channel for transparent nodata values + // Nodata handling is *manual* at the driver level + if ( hasnodata && val == nodataval ) { + data[off+3] = 0x00; // transparent + } else { + data[off+3] = 0xFF; // opaque + } } } mapnik::raster_ptr raster = std::make_shared(bbox, image, 1.0); diff --git a/plugins/input/rasterlite/build.py b/plugins/input/rasterlite/build.py deleted file mode 100644 index 779a48908..000000000 --- a/plugins/input/rasterlite/build.py +++ /dev/null @@ -1,71 +0,0 @@ -# -# This file is part of Mapnik (c++ mapping toolkit) -# -# Copyright (C) 2015 Artem Pavlenko -# -# Mapnik 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 -# -# - -Import ('plugin_base') -Import ('env') - -PLUGIN_NAME = 'rasterlite' - -plugin_env = plugin_base.Clone() - -plugin_sources = Split( - """ - %(PLUGIN_NAME)s_datasource.cpp - %(PLUGIN_NAME)s_featureset.cpp - """ % locals() -) - -# Link Library to Dependencies -libraries = [env['PLUGINS']['rasterlite']['lib']] -libraries.append(env['ICU_LIB_NAME']) -libraries.append('boost_system%s' % env['BOOST_APPEND']) - -if env['RUNTIME_LINK'] == 'static': - libraries.append('geotiff') - libraries.append('spatialite') - libraries.append('sqlite3') - libraries.append('geos_c') - libraries.append('geos') - libraries.append('proj') - libraries.append('z') - -if env['PLUGIN_LINKING'] == 'shared': - libraries.append(env['MAPNIK_NAME']) - - TARGET = plugin_env.SharedLibrary('../%s' % PLUGIN_NAME, - SHLIBPREFIX='', - SHLIBSUFFIX='.input', - source=plugin_sources, - LIBS=libraries) - - # if the plugin links to libmapnik ensure it is built first - Depends(TARGET, env.subst('../../../src/%s' % env['MAPNIK_LIB_NAME'])) - - if 'uninstall' not in COMMAND_LINE_TARGETS: - env.Install(env['MAPNIK_INPUT_PLUGINS_DEST'], TARGET) - env.Alias('install', env['MAPNIK_INPUT_PLUGINS_DEST']) - -plugin_obj = { - 'LIBS': libraries, - 'SOURCES': plugin_sources, -} - -Return('plugin_obj') diff --git a/plugins/input/rasterlite/rasterlite_datasource.cpp b/plugins/input/rasterlite/rasterlite_datasource.cpp deleted file mode 100644 index 026101865..000000000 --- a/plugins/input/rasterlite/rasterlite_datasource.cpp +++ /dev/null @@ -1,191 +0,0 @@ -/***************************************************************************** - * - * This file is part of Mapnik (c++ mapping toolkit) - * - * Copyright (C) 2015 Artem Pavlenko - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - *****************************************************************************/ - -#include "rasterlite_datasource.hpp" -#include "rasterlite_featureset.hpp" - -// boost - -// mapnik -#include -#include -#include -#include - -using mapnik::datasource; -using mapnik::parameters; - -DATASOURCE_PLUGIN(rasterlite_datasource) - -using mapnik::box2d; -using mapnik::coord2d; -using mapnik::query; -using mapnik::featureset_ptr; -using mapnik::layer_descriptor; -using mapnik::datasource_exception; - - -/* - * Opens a GDALDataset and returns a pointer to it. - * Caller is responsible for calling GDALClose on it - */ -inline void* rasterlite_datasource::open_dataset() const -{ - void* dataset = rasterliteOpen (dataset_name_.c_str(), table_name_.c_str()); - - if (! dataset) - { - throw datasource_exception("Rasterlite Plugin: Error opening dataset"); - } - - if (rasterliteIsError (dataset)) - { - std::string error (rasterliteGetLastError(dataset)); - - rasterliteClose (dataset); - - throw datasource_exception(error); - } - - return dataset; -} - - -rasterlite_datasource::rasterlite_datasource(parameters const& params) - : datasource(params), - desc_(rasterlite_datasource::name(),"utf-8") -{ - MAPNIK_LOG_DEBUG(rasterlite) << "rasterlite_datasource: Initializing..."; - - boost::optional file = params.get("file"); - if (!file) throw datasource_exception("missing parameter"); - - boost::optional table = params.get("table"); - if (!table) throw datasource_exception("missing
parameter"); - - table_name_ = *table; - - boost::optional base = params.get("base"); - if (base) - dataset_name_ = *base + "/" + *file; - else - dataset_name_ = *file; - - if (!mapnik::util::exists(dataset_name_)) throw datasource_exception(dataset_name_ + " does not exist"); - - void *dataset = open_dataset(); - - double x0, y0, x1, y1; - if (rasterliteGetExtent (dataset, &x0, &y0, &x1, &y1) != RASTERLITE_OK) - { - std::string error (rasterliteGetLastError(dataset)); - - rasterliteClose (dataset); - - throw datasource_exception(error); - } - - extent_.init(x0,y0,x1,y1); - -#ifdef MAPNIK_LOG - int srid, auth_srid; - const char *auth_name; - const char *ref_sys_name; - const char *proj4text; - - int tile_count; - double pixel_x_size, pixel_y_size; - int levels = rasterliteGetLevels (dataset); - - if (rasterliteGetSrid(dataset, &srid, &auth_name, &auth_srid, &ref_sys_name, &proj4text) != RASTERLITE_OK) - { - std::string error (rasterliteGetLastError(dataset)); - - rasterliteClose (dataset); - - throw datasource_exception(error); - } - - MAPNIK_LOG_DEBUG(rasterlite) << "rasterlite_datasource: Data Source=" << rasterliteGetTablePrefix(dataset); - MAPNIK_LOG_DEBUG(rasterlite) << "rasterlite_datasource: SRID=" << srid; - MAPNIK_LOG_DEBUG(rasterlite) << "rasterlite_datasource: Authority=" << auth_name; - MAPNIK_LOG_DEBUG(rasterlite) << "rasterlite_datasource: AuthSRID=" << auth_srid; - MAPNIK_LOG_DEBUG(rasterlite) << "rasterlite_datasource: RefSys Name=" << ref_sys_name; - MAPNIK_LOG_DEBUG(rasterlite) << "rasterlite_datasource: Proj4Text=" << proj4text; - MAPNIK_LOG_DEBUG(rasterlite) << "rasterlite_datasource: Extent=" << x0 << "," << y0 << " " << x1 << "," << y1 << ")"; - MAPNIK_LOG_DEBUG(rasterlite) << "rasterlite_datasource: Levels=" << levels; - - for (int i = 0; i < levels; i++) - { - if (rasterliteGetResolution(dataset, i, &pixel_x_size, &pixel_y_size, &tile_count) == RASTERLITE_OK) - { - MAPNIK_LOG_DEBUG(rasterlite) << "rasterlite_datasource: Level=" << i - << " x=" << pixel_x_size - << " y=" << pixel_y_size - << " tiles=" << tile_count; - } - } -#endif - - rasterliteClose(dataset); -} - -rasterlite_datasource::~rasterlite_datasource() -{ -} - -const char * rasterlite_datasource::name() -{ - return "rasterlite"; -} - -mapnik::datasource::datasource_t rasterlite_datasource::type() const -{ - return datasource::Raster; -} - -box2d rasterlite_datasource::envelope() const -{ - return extent_; -} - -boost::optional rasterlite_datasource::get_geometry_type() const -{ - return boost::optional(); -} - -layer_descriptor rasterlite_datasource::get_descriptor() const -{ - return desc_; -} - -featureset_ptr rasterlite_datasource::features(query const& q) const -{ - rasterlite_query gq = q; - return std::make_shared(open_dataset(), gq); -} - -featureset_ptr rasterlite_datasource::features_at_point(coord2d const& pt, double tol) const -{ - rasterlite_query gq = pt; - return std::make_shared(open_dataset(), gq); -} diff --git a/plugins/input/rasterlite/rasterlite_datasource.hpp b/plugins/input/rasterlite/rasterlite_datasource.hpp deleted file mode 100644 index dd304465b..000000000 --- a/plugins/input/rasterlite/rasterlite_datasource.hpp +++ /dev/null @@ -1,66 +0,0 @@ -/***************************************************************************** - * - * This file is part of Mapnik (c++ mapping toolkit) - * - * Copyright (C) 2015 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 RASTERLITE_DATASOURCE_HPP -#define RASTERLITE_DATASOURCE_HPP - -// mapnik -#include -#include -#include -#include -#include -#include -#include - -// boost -#include -#include - -// stl -#include -#include - -#include "rasterlite_include.hpp" - -class rasterlite_datasource : public mapnik::datasource -{ -public: - rasterlite_datasource(mapnik::parameters const& params); - virtual ~rasterlite_datasource (); - mapnik::datasource::datasource_t type() const; - static const char * name(); - mapnik::featureset_ptr features(mapnik::query const& q) const; - mapnik::featureset_ptr features_at_point(mapnik::coord2d const& pt, double tol = 0) const; - mapnik::box2d envelope() const; - boost::optional get_geometry_type() const; - mapnik::layer_descriptor get_descriptor() const; - -private: - void* open_dataset() const; - mapnik::box2d extent_; - std::string dataset_name_; - std::string table_name_; - mapnik::layer_descriptor desc_; -}; - -#endif // RASTERLITE_DATASOURCE_HPP diff --git a/plugins/input/rasterlite/rasterlite_featureset.cpp b/plugins/input/rasterlite/rasterlite_featureset.cpp deleted file mode 100644 index 270a87e53..000000000 --- a/plugins/input/rasterlite/rasterlite_featureset.cpp +++ /dev/null @@ -1,136 +0,0 @@ -/***************************************************************************** - * - * This file is part of Mapnik (c++ mapping toolkit) - * - * Copyright (C) 2015 Artem Pavlenko - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - *****************************************************************************/ - -#include "rasterlite_featureset.hpp" - -// mapnik -#include -#include -#include -#include -#include -#include -#include - -#include - -using mapnik::coord2d; -using mapnik::box2d; -using mapnik::feature_ptr; -using mapnik::query; -using mapnik::feature_factory; - - -rasterlite_featureset::rasterlite_featureset(void* dataset, - rasterlite_query q) - : dataset_(dataset), - gquery_(q), - first_(true), - ctx_(std::make_shared()) -{ - rasterliteSetBackgroundColor(dataset_, 255, 0, 255); - rasterliteSetTransparentColor(dataset_, 255, 0, 255); -} - -rasterlite_featureset::~rasterlite_featureset() -{ - MAPNIK_LOG_DEBUG(rasterlite) << "rasterlite_featureset: Closing"; - - rasterliteClose(dataset_); -} - -feature_ptr rasterlite_featureset::next() -{ - if (first_) - { - first_ = false; - MAPNIK_LOG_DEBUG(gdal) << "rasterlite_featureset: Next feature in Dataset=" << &dataset_; - return mapnik::util::apply_visitor(query_dispatch(*this), gquery_); - } - return feature_ptr(); -} - -feature_ptr rasterlite_featureset::get_feature(mapnik::query const& q) -{ - MAPNIK_LOG_DEBUG(rasterlite) << "rasterlite_featureset: Running get_feature"; - - feature_ptr feature(feature_factory::create(ctx_,1)); - - double x0, y0, x1, y1; - rasterliteGetExtent (dataset_, &x0, &y0, &x1, &y1); - - box2d raster_extent(x0, y0, x1, y1); - box2d intersect = raster_extent.intersect(q.get_bbox()); - - const int width = static_cast(std::get<0>(q.resolution()) * intersect.width() + 0.5); - const int height = static_cast(std::get<0>(q.resolution()) * intersect.height() + 0.5); - - const double pixel_size = (intersect.width() >= intersect.height()) ? - (intersect.width() / (double) width) : (intersect.height() / (double) height); - - MAPNIK_LOG_DEBUG(rasterlite) << "rasterlite_featureset: Raster extent=" << raster_extent; - MAPNIK_LOG_DEBUG(rasterlite) << "rasterlite_featureset: View extent=" << q.get_bbox(); - MAPNIK_LOG_DEBUG(rasterlite) << "rasterlite_featureset: Intersect extent=" << intersect; - MAPNIK_LOG_DEBUG(rasterlite) << "rasterlite_featureset: Query resolution=" << std::get<0>(q.resolution()) << "," << std::get<1>(q.resolution()); - MAPNIK_LOG_DEBUG(rasterlite) << "rasterlite_featureset: Size=" << width << " " << height; - MAPNIK_LOG_DEBUG(rasterlite) << "rasterlite_featureset: Pixel Size=" << pixel_size; - - if (width > 0 && height > 0) - { - int size = 0; - void* raster = 0; - - if (rasterliteGetRawImageByRect(dataset_, - intersect.minx(), - intersect.miny(), - intersect.maxx(), - intersect.maxy(), - pixel_size, - width, - height, - GAIA_RGBA_ARRAY, - &raster, - &size) == RASTERLITE_OK) - { - if (size > 0) - { - mapnik::image_rgba8 image(width,height); - unsigned char* raster_data = static_cast(raster); - std::memcpy(image.bytes(), raster_data, size); - feature->set_raster(std::make_shared(intersect, std::move(image), 1.0)); - MAPNIK_LOG_DEBUG(rasterlite) << "rasterlite_featureset: Done"; - } - else - { - MAPNIK_LOG_ERROR(rasterlite) << "Rasterlite Plugin: Error " << rasterliteGetLastError (dataset_); - } - } - - return feature; - } - return feature_ptr(); -} - -feature_ptr rasterlite_featureset::get_feature_at_point(mapnik::coord2d const& pt) -{ - return feature_ptr(); -} diff --git a/plugins/input/rasterlite/rasterlite_featureset.hpp b/plugins/input/rasterlite/rasterlite_featureset.hpp deleted file mode 100644 index cb251c86a..000000000 --- a/plugins/input/rasterlite/rasterlite_featureset.hpp +++ /dev/null @@ -1,71 +0,0 @@ -/***************************************************************************** - * - * This file is part of Mapnik (c++ mapping toolkit) - * - * Copyright (C) 2015 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 RASTERLITE_FEATURESET_HPP -#define RASTERLITE_FEATURESET_HPP - -// mapnik -#include -#include -#include -#include - -#include "rasterlite_include.hpp" - -using rasterlite_query = mapnik::util::variant; - -class rasterlite_featureset : public mapnik::Featureset -{ - struct query_dispatch - { - query_dispatch( rasterlite_featureset & featureset) - : featureset_(featureset) {} - - mapnik::feature_ptr operator() (mapnik::query const& q) const - { - return featureset_.get_feature(q); - } - - mapnik::feature_ptr operator() (mapnik::coord2d const& p) const - { - return featureset_.get_feature_at_point(p); - } - - rasterlite_featureset & featureset_; - }; - -public: - rasterlite_featureset(void* dataset, - rasterlite_query q); - virtual ~rasterlite_featureset(); - mapnik::feature_ptr next(); - -private: - mapnik::feature_ptr get_feature(mapnik::query const& q); - mapnik::feature_ptr get_feature_at_point(mapnik::coord2d const& p); - void* dataset_; - rasterlite_query gquery_; - bool first_; - mapnik::context_ptr ctx_; -}; - -#endif // RASTERLITE_FEATURESET_HPP diff --git a/plugins/input/rasterlite/rasterlite_include.hpp b/plugins/input/rasterlite/rasterlite_include.hpp deleted file mode 100644 index b6a9dd274..000000000 --- a/plugins/input/rasterlite/rasterlite_include.hpp +++ /dev/null @@ -1,31 +0,0 @@ -/***************************************************************************** - * - * This file is part of Mapnik (c++ mapping toolkit) - * - * Copyright (C) 2015 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 RASTERLITE_INCLUDE_HPP -#define RASTERLITE_INCLUDE_HPP - -extern "C" { -#include -#include -} - -#endif // RASTERLITE_INCLUDE_HPP diff --git a/src/agg/process_group_symbolizer.cpp b/src/agg/process_group_symbolizer.cpp index 836e13533..f2c8adf48 100644 --- a/src/agg/process_group_symbolizer.cpp +++ b/src/agg/process_group_symbolizer.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -120,7 +121,7 @@ struct thunk_renderer render_offset_placements( thunk.placements_, offset_, - [&] (glyph_positions_ptr glyphs) + [&] (glyph_positions_ptr const& glyphs) { marker_info_ptr mark = glyphs->get_marker(); if (mark) diff --git a/src/agg/process_line_pattern_symbolizer.cpp b/src/agg/process_line_pattern_symbolizer.cpp index 7cdd16648..a6be6b154 100644 --- a/src/agg/process_line_pattern_symbolizer.cpp +++ b/src/agg/process_line_pattern_symbolizer.cpp @@ -50,7 +50,6 @@ #include "agg_span_allocator.h" #include "agg_span_pattern_rgba.h" #include "agg_renderer_outline_image.h" -#include "agg_conv_clip_polyline.h" namespace mapnik { diff --git a/src/agg/process_line_symbolizer.cpp b/src/agg/process_line_symbolizer.cpp index e2004e921..b3a01c7e8 100644 --- a/src/agg/process_line_symbolizer.cpp +++ b/src/agg/process_line_symbolizer.cpp @@ -31,6 +31,7 @@ #include #include #include +#include // agg #include "agg_basics.h" #include "agg_rendering_buffer.h" @@ -164,12 +165,19 @@ void agg_renderer::process(line_symbolizer const& sym, rasterizer_type ras(ren); set_join_caps_aa(sym, ras, feature, common_.vars_); - using vertex_converter_type = vertex_converter; vertex_converter_type converter(clip_box,sym,common_.t_,prj_trans,tr,feature,common_.vars_,common_.scale_factor_); - if (clip) converter.set(); // optional clip (default: true) + if (clip) + { + geometry::geometry_types type = geometry::geometry_type(feature.get_geometry()); + if (type == geometry::geometry_types::Polygon || type == geometry::geometry_types::MultiPolygon) + converter.template set(); + else if (type == geometry::geometry_types::LineString || type == geometry::geometry_types::MultiLineString) + converter.template set(); + } converter.set(); // always transform if (std::fabs(offset) > 0.0) converter.set(); // parallel offset converter.set(); // optional affine transform @@ -183,14 +191,20 @@ void agg_renderer::process(line_symbolizer const& sym, } else { - using vertex_converter_type = vertex_converter; vertex_converter_type converter(clip_box, sym,common_.t_,prj_trans,tr,feature,common_.vars_,common_.scale_factor_); - - if (clip) converter.set(); // optional clip (default: true) + if (clip) + { + geometry::geometry_types type = geometry::geometry_type(feature.get_geometry()); + if (type == geometry::geometry_types::Polygon || type == geometry::geometry_types::MultiPolygon) + converter.template set(); + else if (type == geometry::geometry_types::LineString || type == geometry::geometry_types::MultiLineString) + converter.template set(); + } converter.set(); // always transform if (std::fabs(offset) > 0.0) converter.set(); // parallel offset converter.set(); // optional affine transform diff --git a/src/agg/process_markers_symbolizer.cpp b/src/agg/process_markers_symbolizer.cpp index ab06ed30c..cb9428260 100644 --- a/src/agg/process_markers_symbolizer.cpp +++ b/src/agg/process_markers_symbolizer.cpp @@ -50,7 +50,6 @@ #include "agg_rasterizer_scanline_aa.h" #include "agg_scanline_u.h" #include "agg_path_storage.h" -#include "agg_conv_clip_polyline.h" #include "agg_conv_transform.h" diff --git a/src/agg/process_shield_symbolizer.cpp b/src/agg/process_shield_symbolizer.cpp index e8d1cc5c5..3d28eff60 100644 --- a/src/agg/process_shield_symbolizer.cpp +++ b/src/agg/process_shield_symbolizer.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include namespace mapnik { @@ -60,7 +61,7 @@ void agg_renderer::process(shield_symbolizer const& sym, double opacity = get(sym,keys::opacity, feature, common_.vars_, 1.0); placements_list const& placements = helper.get(); - for (glyph_positions_ptr glyphs : placements) + for (auto const& glyphs : placements) { marker_info_ptr mark = glyphs->get_marker(); if (mark) diff --git a/src/agg/process_text_symbolizer.cpp b/src/agg/process_text_symbolizer.cpp index c30566d61..b3794bcf5 100644 --- a/src/agg/process_text_symbolizer.cpp +++ b/src/agg/process_text_symbolizer.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include namespace mapnik { @@ -67,7 +68,7 @@ void agg_renderer::process(text_symbolizer const& sym, } placements_list const& placements = helper.get(); - for (glyph_positions_ptr glyphs : placements) + for (auto const& glyphs : placements) { ren.render(*glyphs); } diff --git a/src/build.py b/src/build.py index 520a34710..bdc343803 100644 --- a/src/build.py +++ b/src/build.py @@ -84,7 +84,8 @@ if '-DHAVE_WEBP' in env['CPPDEFINES']: lib_env['LIBS'].append('webp') enabled_imaging_libraries.append('webp_reader.cpp') -lib_env['LIBS'].append('xml2') +if env['XMLPARSER'] == 'libxml2' and env['HAS_LIBXML2']: + lib_env['LIBS'].append('xml2') if '-DBOOST_REGEX_HAS_ICU' in env['CPPDEFINES']: lib_env['LIBS'].append('icui18n') diff --git a/src/cairo/cairo_context.cpp b/src/cairo/cairo_context.cpp index 1a20e90f0..c8ba13c8a 100644 --- a/src/cairo/cairo_context.cpp +++ b/src/cairo/cairo_context.cpp @@ -28,8 +28,8 @@ #include #include +#include -#include namespace mapnik { cairo_face::cairo_face(std::shared_ptr const& library, face_ptr const& face) @@ -227,17 +227,13 @@ void cairo_context::set_line_width(double width) void cairo_context::set_dash(dash_array const& dashes, double scale_factor) { - std::valarray d(dashes.size() * 2); - dash_array::const_iterator itr = dashes.begin(); - dash_array::const_iterator end = dashes.end(); - std::size_t index = 0; - - for (; itr != end; ++itr) + std::vector d; + d.reserve(dashes.size() * 2); + for (auto const& dash : dashes) { - d[index++] = itr->first * scale_factor; - d[index++] = itr->second * scale_factor; + d.emplace_back(dash.first * scale_factor); + d.emplace_back(dash.second * scale_factor); } - cairo_set_dash(cairo_.get() , &d[0], static_cast(d.size()), 0/*offset*/); check_object_status_and_throw_exception(*this); } diff --git a/src/cairo/process_group_symbolizer.cpp b/src/cairo/process_group_symbolizer.cpp index 79338664d..a2dc4abbe 100644 --- a/src/cairo/process_group_symbolizer.cpp +++ b/src/cairo/process_group_symbolizer.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -95,7 +96,7 @@ struct thunk_renderer render_offset_placements( thunk.placements_, offset_, - [&] (glyph_positions_ptr glyphs) + [&] (glyph_positions_ptr const& glyphs) { marker_info_ptr mark = glyphs->get_marker(); if (mark) diff --git a/src/cairo/process_line_symbolizer.cpp b/src/cairo/process_line_symbolizer.cpp index 1510547e4..ed64d5c01 100644 --- a/src/cairo/process_line_symbolizer.cpp +++ b/src/cairo/process_line_symbolizer.cpp @@ -29,6 +29,7 @@ #include #include #include +#include namespace mapnik { @@ -82,6 +83,7 @@ void cairo_renderer::process(line_symbolizer const& sym, clipping_extent.pad(padding); } using vertex_converter_type = vertex_converter::process(line_symbolizer const& sym, vertex_converter_type converter(clipping_extent,sym,common_.t_,prj_trans,tr,feature,common_.vars_,common_.scale_factor_); - if (clip) converter.set(); // optional clip (default: true) + if (clip) + { + geometry::geometry_types type = geometry::geometry_type(feature.get_geometry()); + if (type == geometry::geometry_types::Polygon || type == geometry::geometry_types::MultiPolygon) + converter.template set(); + else if (type == geometry::geometry_types::LineString || type == geometry::geometry_types::MultiLineString) + converter.template set(); + } converter.set(); // always transform if (std::fabs(offset) > 0.0) converter.set(); // parallel offset converter.set(); // optional affine transform diff --git a/src/cairo/process_text_symbolizer.cpp b/src/cairo/process_text_symbolizer.cpp index 176b6c635..2d34e213c 100644 --- a/src/cairo/process_text_symbolizer.cpp +++ b/src/cairo/process_text_symbolizer.cpp @@ -27,6 +27,7 @@ #include #include #include +#include namespace mapnik { @@ -55,7 +56,7 @@ void cairo_renderer::process(shield_symbolizer const& sym, double opacity = get(sym,keys::opacity,feature, common_.vars_, 1.0); placements_list const &placements = helper.get(); - for (glyph_positions_ptr glyphs : placements) + for (auto const& glyphs : placements) { marker_info_ptr mark = glyphs->get_marker(); if (mark) { @@ -93,7 +94,7 @@ void cairo_renderer::process(text_symbolizer const& sym, composite_mode_e halo_comp_op = get(sym, keys::halo_comp_op, feature, common_.vars_, src_over); placements_list const& placements = helper.get(); - for (glyph_positions_ptr glyphs : placements) + for (auto const& glyphs : placements) { context_.add_text(*glyphs, face_manager_, comp_op, halo_comp_op, common_.scale_factor_); } diff --git a/src/color_factory.cpp b/src/color_factory.cpp index d13a89562..dc54e89e5 100644 --- a/src/color_factory.cpp +++ b/src/color_factory.cpp @@ -45,7 +45,7 @@ color parse_color(std::string const& str) } else { - throw config_error("Failed to a parse color: \"" + str + "\""); + throw config_error("Failed to parse color: \"" + str + "\""); } } diff --git a/src/gradient.cpp b/src/gradient.cpp index ddf665870..037cf8fc7 100644 --- a/src/gradient.cpp +++ b/src/gradient.cpp @@ -66,13 +66,36 @@ gradient::gradient(gradient const& other) units_(other.units_), gradient_type_(other.gradient_type_) {} -gradient & gradient::operator=(const gradient& rhs) +gradient::gradient(gradient && other) + : transform_(std::move(other.transform_)), + x1_(std::move(other.x1_)), + y1_(std::move(other.y1_)), + x2_(std::move(other.x2_)), + y2_(std::move(other.y2_)), + r_(std::move(other.r_)), + stops_(std::move(other.stops_)), + units_(std::move(other.units_)), + gradient_type_(std::move(other.gradient_type_)) {} + +gradient & gradient::operator=(gradient rhs) { - gradient tmp(rhs); - swap(tmp); + swap(rhs); return *this; } +bool gradient::operator==(gradient const& other) const +{ + return transform_ == other.transform_ && + x1_ == other.x1_ && + y1_ == other.y1_ && + x2_ == other.x2_ && + y2_ == other.y2_ && + r_ == other.r_ && + std::equal(stops_.begin(),stops_.end(), other.stops_.begin()), + units_ == other.units_ && + gradient_type_ == other.gradient_type_; +} + void gradient::set_gradient_type(gradient_e grad) { gradient_type_=grad; @@ -108,7 +131,7 @@ void gradient::add_stop(double offset,mapnik::color const& c) bool gradient::has_stop() const { - return ! stops_.empty(); + return !stops_.empty(); } stop_array const& gradient::get_stop_array() const @@ -116,13 +139,17 @@ stop_array const& gradient::get_stop_array() const return stops_; } -void gradient::swap(const gradient& other) throw() +void gradient::swap(gradient& other) throw() { - gradient_type_=other.gradient_type_; - stops_=other.stops_; - units_=other.units_; - transform_=other.transform_; - other.get_control_points(x1_,y1_,x2_,y2_,r_); + std::swap(gradient_type_, other.gradient_type_); + std::swap(stops_, other.stops_); + std::swap(units_, other.units_); + std::swap(transform_, other.transform_); + std::swap(x1_, other.x1_); + std::swap(y1_, other.y1_); + std::swap(x2_, other.x2_); + std::swap(y2_, other.y2_); + std::swap(r_, other.r_); } void gradient::set_control_points(double x1, double y1, double x2, double y2, double r) diff --git a/src/grid/process_group_symbolizer.cpp b/src/grid/process_group_symbolizer.cpp index 25c360027..f73118969 100644 --- a/src/grid/process_group_symbolizer.cpp +++ b/src/grid/process_group_symbolizer.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -129,7 +130,7 @@ struct thunk_renderer render_offset_placements( thunk.placements_, offset_, - [&] (glyph_positions_ptr glyphs) + [&] (glyph_positions_ptr const& glyphs) { marker_info_ptr mark = glyphs->get_marker(); if (mark) diff --git a/src/grid/process_line_symbolizer.cpp b/src/grid/process_line_symbolizer.cpp index 0dc6f3f6a..c569f6b2c 100644 --- a/src/grid/process_line_symbolizer.cpp +++ b/src/grid/process_line_symbolizer.cpp @@ -31,6 +31,7 @@ #include #include #include +#include // agg #include "agg_rasterizer_scanline_aa.h" #include "agg_renderer_scanline.h" @@ -89,14 +90,21 @@ void grid_renderer::process(line_symbolizer const& sym, padding *= common_.scale_factor_; clipping_extent.pad(padding); } - using vertex_converter_type = vertex_converter; vertex_converter_type converter(clipping_extent,sym,common_.t_,prj_trans,tr,feature,common_.vars_,common_.scale_factor_); - if (clip) converter.set(); // optional clip (default: true) + if (clip) + { + geometry::geometry_types type = geometry::geometry_type(feature.get_geometry()); + if (type == geometry::geometry_types::Polygon || type == geometry::geometry_types::MultiPolygon) + converter.template set(); + else if (type == geometry::geometry_types::LineString || type == geometry::geometry_types::MultiLineString) + converter.template set(); + } converter.set(); // always transform if (std::fabs(offset) > 0.0) converter.set(); // parallel offset converter.set(); // optional affine transform diff --git a/src/grid/process_shield_symbolizer.cpp b/src/grid/process_shield_symbolizer.cpp index ae4ffa2d1..50bc8209f 100644 --- a/src/grid/process_shield_symbolizer.cpp +++ b/src/grid/process_shield_symbolizer.cpp @@ -31,6 +31,7 @@ #include #include #include +#include // agg #include "agg_trans_affine.h" @@ -64,7 +65,7 @@ void grid_renderer::process(shield_symbolizer const& sym, placements_list const& placements = helper.get(); value_integer feature_id = feature.id(); - for (glyph_positions_ptr glyphs : placements) + for (auto const& glyphs : placements) { marker_info_ptr mark = glyphs->get_marker(); if (mark) diff --git a/src/grid/process_text_symbolizer.cpp b/src/grid/process_text_symbolizer.cpp index a615a5db6..7fe0ff377 100644 --- a/src/grid/process_text_symbolizer.cpp +++ b/src/grid/process_text_symbolizer.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include namespace mapnik { @@ -66,7 +67,7 @@ void grid_renderer::process(text_symbolizer const& sym, placements_list const& placements = helper.get(); value_integer feature_id = feature.id(); - for (glyph_positions_ptr glyphs : placements) + for (auto const& glyphs : placements) { ren.render(*glyphs, feature_id); placement_found = true; diff --git a/src/group/group_symbolizer_helper.cpp b/src/group/group_symbolizer_helper.cpp index a8e453a69..2ba70ff9b 100644 --- a/src/group/group_symbolizer_helper.cpp +++ b/src/group/group_symbolizer_helper.cpp @@ -34,9 +34,6 @@ #include #include -//agg -#include "agg_conv_clip_polyline.h" - namespace mapnik { namespace detail { template diff --git a/src/image_util.cpp b/src/image_util.cpp index 63d544088..7f86fdb3c 100644 --- a/src/image_util.cpp +++ b/src/image_util.cpp @@ -368,6 +368,13 @@ template MAPNIK_DECL void save_to_file (image_view_any const&, std::string const&, rgba_palette const& palette); +template MAPNIK_DECL std::string save_to_string (image_view_rgba8 const&, + std::string const&); + +template MAPNIK_DECL std::string save_to_string (image_view_rgba8 const&, + std::string const&, + rgba_palette const& palette); + template MAPNIK_DECL std::string save_to_string (image_view_any const&, std::string const&); diff --git a/src/marker_cache.cpp b/src/marker_cache.cpp index 3f8f9396b..330f4d094 100644 --- a/src/marker_cache.cpp +++ b/src/marker_cache.cpp @@ -175,7 +175,15 @@ std::shared_ptr marker_cache::find(std::string const& uri, svg_path_adapter svg_path(stl_storage); svg_converter_type svg(svg_path, marker_path->attributes()); svg_parser p(svg); - p.parse_from_string(known_svg_string); + + if (!p.parse_from_string(known_svg_string)) + { + for (auto const& msg : p.error_messages()) + { + MAPNIK_LOG_ERROR(marker_cache) << "SVG PARSING ERROR:\"" << msg << "\""; + } + return std::make_shared(mapnik::marker_null()); + } //svg.arrange_orientations(); double lox,loy,hix,hiy; svg.bounding_rect(&lox, &loy, &hix, &hiy); @@ -207,7 +215,16 @@ std::shared_ptr marker_cache::find(std::string const& uri, svg_path_adapter svg_path(stl_storage); svg_converter_type svg(svg_path, marker_path->attributes()); svg_parser p(svg); - p.parse(uri); + + + if (!p.parse(uri)) + { + for (auto const& msg : p.error_messages()) + { + MAPNIK_LOG_ERROR(marker_cache) << "SVG PARSING ERROR:\"" << msg << "\""; + } + return std::make_shared(mapnik::marker_null()); + } //svg.arrange_orientations(); double lox,loy,hix,hiy; svg.bounding_rect(&lox, &loy, &hix, &hiy); diff --git a/src/memory_datasource.cpp b/src/memory_datasource.cpp index c30f6745f..f7bd61d4e 100644 --- a/src/memory_datasource.cpp +++ b/src/memory_datasource.cpp @@ -82,6 +82,7 @@ void memory_datasource::push(feature_ptr feature) // TODO - collect attribute descriptors? //desc_.add_descriptor(attribute_descriptor(fld_name,mapnik::Integer)); features_.push_back(feature); + dirty_extent_ = true; } datasource::datasource_t memory_datasource::type() const @@ -110,10 +111,11 @@ void memory_datasource::set_envelope(box2d const& box) box2d memory_datasource::envelope() const { - if (!extent_.valid()) + if (!extent_.valid() || dirty_extent_) { accumulate_extent func(extent_); std::for_each(features_.begin(),features_.end(),func); + dirty_extent_ = false; } return extent_; } diff --git a/src/svg/svg_parser.cpp b/src/svg/svg_parser.cpp index 1ef103c3b..f57451e44 100644 --- a/src/svg/svg_parser.cpp +++ b/src/svg/svg_parser.cpp @@ -26,7 +26,8 @@ #include #include #include - +#include +#include #include "agg_ellipse.h" #include "agg_rounded_rect.h" #include "agg_span_gradient.h" @@ -41,42 +42,40 @@ #include #include #include +// rapidxml +#include #pragma GCC diagnostic pop #include #include #include #include - -// xml2 -#include - +#include namespace mapnik { namespace svg { -bool parse_reader(svg_parser & parser,xmlTextReaderPtr reader); -void process_node(svg_parser & parser,xmlTextReaderPtr reader); -void start_element(svg_parser & parser,xmlTextReaderPtr reader); -void end_element(svg_parser & parser,xmlTextReaderPtr reader); -void parse_path(svg_parser & parser,xmlTextReaderPtr reader); -void parse_dimensions(svg_parser & parser,xmlTextReaderPtr reader); -void parse_polygon(svg_parser & parser,xmlTextReaderPtr reader); -void parse_polyline(svg_parser & parser,xmlTextReaderPtr reader); -void parse_line(svg_parser & parser,xmlTextReaderPtr reader); -void parse_rect(svg_parser & parser,xmlTextReaderPtr reader); -void parse_circle(svg_parser & parser,xmlTextReaderPtr reader); -void parse_ellipse(svg_parser & parser,xmlTextReaderPtr reader); -void parse_linear_gradient(svg_parser & parser,xmlTextReaderPtr reader); -void parse_radial_gradient(svg_parser & parser,xmlTextReaderPtr reader); -bool parse_common_gradient(svg_parser & parser,xmlTextReaderPtr reader); -void parse_gradient_stop(svg_parser & parser,xmlTextReaderPtr reader); -void parse_attr(svg_parser & parser,xmlTextReaderPtr reader); -void parse_attr(svg_parser & parser,const xmlChar * name, const xmlChar * value ); +namespace rapidxml = boost::property_tree::detail::rapidxml; + +bool traverse_tree(svg_parser & parser,rapidxml::xml_node const* node); +void end_element(svg_parser & parser,rapidxml::xml_node const* node); +void parse_path(svg_parser & parser,rapidxml::xml_node const* node); +void parse_dimensions(svg_parser & parser,rapidxml::xml_node const* node); +void parse_polygon(svg_parser & parser,rapidxml::xml_node const* node); +void parse_polyline(svg_parser & parser,rapidxml::xml_node const* node); +void parse_line(svg_parser & parser,rapidxml::xml_node const* node); +void parse_rect(svg_parser & parser,rapidxml::xml_node const* node); +void parse_circle(svg_parser & parser,rapidxml::xml_node const* node); +void parse_ellipse(svg_parser & parser,rapidxml::xml_node const* node); +void parse_linear_gradient(svg_parser & parser,rapidxml::xml_node const* node); +void parse_radial_gradient(svg_parser & parser,rapidxml::xml_node const* node); +bool parse_common_gradient(svg_parser & parser,rapidxml::xml_node const* node); +void parse_gradient_stop(svg_parser & parser,rapidxml::xml_node const* node); +void parse_attr(svg_parser & parser,rapidxml::xml_node const* node); +void parse_attr(svg_parser & parser,char const * name, char const* value); + using color_lookup_type = std::vector >; - namespace qi = boost::spirit::qi; - using pairs_type = std::vector >; template @@ -99,7 +98,8 @@ struct key_value_sequence_ordered qi::rule key, value; }; -agg::rgba8 parse_color(const char* str) +template +mapnik::color parse_color(T & error_messages, const char* str) { mapnik::color c(100,100,100); try @@ -108,25 +108,38 @@ agg::rgba8 parse_color(const char* str) } catch (mapnik::config_error const& ex) { - MAPNIK_LOG_ERROR(svg_parser) << ex.what(); + + error_messages.emplace_back(ex.what()); } + return c; +} + +template +agg::rgba8 parse_color_agg(T & error_messages, const char* str) +{ + auto c = parse_color(error_messages, str); return agg::rgba8(c.red(), c.green(), c.blue(), c.alpha()); } -double parse_double(const char* str) +template +double parse_double(T & error_messages, const char* str) { using namespace boost::spirit::qi; qi::double_type double_; double val = 0.0; - parse(str, str + std::strlen(str),double_,val); + if (!parse(str, str + std::strlen(str),double_,val)) + { + error_messages.emplace_back("Failed to parse double: \"" + std::string(str) + "\""); + } return val; } -/* - * parse a double that might end with a % - * if it does then set the ref bool true and divide the result by 100 - */ -double parse_double_optional_percent(const char* str, bool &percent) + +// parse a double that might end with a % +// if it does then set the ref bool true and divide the result by 100 + +template +double parse_double_optional_percent(T & error_messages, const char* str, bool &percent) { using namespace boost::spirit::qi; using boost::phoenix::ref; @@ -135,21 +148,15 @@ double parse_double_optional_percent(const char* str, bool &percent) qi::char_type char_; double val = 0.0; - char unit='\0'; - parse(str, str + std::strlen(str),double_[ref(val)=_1] >> *char_('%')[ref(unit)=_1]); - if (unit =='%') + if (!parse(str, str + std::strlen(str),double_[ref(val)=_1, ref(percent) = false] + >> -char_('%')[ref(val)/100.0, ref(percent) = true])) { - percent = true; - val/=100.0; - } - else - { - percent = false; + error_messages.emplace_back("Failed to parse double (optional %) from " + std::string(str)); } return val; } -bool parse_style (const char* str, pairs_type & v) +bool parse_style (char const* str, pairs_type & v) { using namespace boost::spirit::qi; using skip_type = boost::spirit::ascii::space_type; @@ -157,538 +164,497 @@ bool parse_style (const char* str, pairs_type & v) return phrase_parse(str, str + std::strlen(str), kv_parser, skip_type(), v); } -bool parse_reader(svg_parser & parser, xmlTextReaderPtr reader) +bool parse_id_from_url (char const* str, std::string & id) { - int ret = xmlTextReaderRead(reader); - try { - while (ret == 1) + using namespace boost::spirit::qi; + using skip_type = boost::spirit::ascii::space_type; + qi::_1_type _1; + qi::char_type char_; + qi::lit_type lit; + return phrase_parse(str, str + std::strlen(str), + lit("url") > "(" > "#" > *(char_ - lit(')'))[boost::phoenix::ref(id) += _1] > ")", + skip_type()); +} + +bool traverse_tree(svg_parser & parser, rapidxml::xml_node const* node) +{ + auto const* name = node->name(); + switch (node->type()) + { + case rapidxml::node_element: + { + if (std::strcmp(name, "defs") == 0) { - process_node(parser,reader); - ret = xmlTextReaderRead(reader); + if (node->first_node() != nullptr) + { + parser.is_defs_ = true; + } + } + // the gradient tags *should* be in defs, but illustrator seems not to put them in there so + // accept them anywhere + else if (std::strcmp(name, "linearGradient") == 0) + { + parse_linear_gradient(parser, node); + } + else if (std::strcmp(name, "radialGradient") == 0) + { + parse_radial_gradient(parser, node); + } + else if (std::strcmp(name, "stop") == 0) + { + parse_gradient_stop(parser, node); + } + + if (!parser.is_defs_) // FIXME + { + if (std::strcmp(name, "g") == 0) + { + if (node->first_node() != nullptr) + { + parser.path_.push_attr(); + parse_attr(parser, node); + } + } + else + { + parser.path_.push_attr(); + parse_attr(parser, node); + if (parser.path_.display()) + { + if (std::strcmp(name, "path") == 0) + { + parse_path(parser, node); + } + else if (std::strcmp("polygon", name) == 0) + { + parse_polygon(parser, node); + } + else if (std::strcmp("polyline", name) == 0) + { + parse_polyline(parser, node); + } + else if (std::strcmp(name, "line") == 0) + { + parse_line(parser, node); + } + else if (std::strcmp(name, "rect") == 0) + { + parse_rect(parser, node); + } + else if (std::strcmp(name, "circle") == 0) + { + parse_circle(parser, node); + } + else if (std::strcmp(name, "ellipse") == 0) + { + parse_ellipse(parser, node); + } + else if (std::strcmp(name, "svg") == 0) + { + parse_dimensions(parser, node); + } + else + { + //std::cerr << "unprocessed node <--[" << node->name() << "]\n"; + } + } + parser.path_.pop_attr(); + } + } + + for (auto const* child = node->first_node(); + child; child = child->next_sibling()) + { + traverse_tree(parser, child); + } + + end_element(parser, node); + } + break; +#if 0 // + // Data nodes + case rapidxml::node_data: + case rapidxml::node_cdata: + { + + if (node->value_size() > 0) // Don't add empty text nodes + { + // parsed text values should have leading and trailing + // whitespace trimmed. + //std::string trimmed = node->value(); + //mapnik::util::trim(trimmed); + std::cerr << "CDATA:" << node->value() << std::endl; } } - catch (std::exception const& ex) - { - xmlFreeTextReader(reader); - throw ex; - } - xmlFreeTextReader(reader); - if (ret != 0) - { - // parsing failure - return false; + break; +#endif + default: + break; } return true; } -void start_element(svg_parser & parser, xmlTextReaderPtr reader) +void end_element(svg_parser & parser, rapidxml::xml_node const* node) { - const xmlChar *name; - name = xmlTextReaderConstName(reader); - - if (xmlStrEqual(name, BAD_CAST "defs")) + auto const* name = node->name(); + if (!parser.is_defs_ && std::strcmp(name, "g") == 0) { - if (xmlTextReaderIsEmptyElement(reader) == 0) - parser.is_defs_ = true; - } - // the gradient tags *should* be in defs, but illustrator seems not to put them in there so - // accept them anywhere - else if (xmlStrEqual(name, BAD_CAST "linearGradient")) - { - parse_linear_gradient(parser,reader); - } - else if (xmlStrEqual(name, BAD_CAST "radialGradient")) - { - parse_radial_gradient(parser,reader); - } - else if (xmlStrEqual(name, BAD_CAST "stop")) - { - parse_gradient_stop(parser,reader); - } - if ( !parser.is_defs_ ) - { - - if (xmlStrEqual(name, BAD_CAST "g")) + if (node->first_node() != nullptr) { - if (xmlTextReaderIsEmptyElement(reader) == 0) - { - parser.path_.push_attr(); - parse_attr(parser,reader); - } - } - else - { - parser.path_.push_attr(); - parse_attr(parser,reader); - if (parser.path_.display()) - { - if (xmlStrEqual(name, BAD_CAST "path")) - { - parse_path(parser,reader); - } - else if (xmlStrEqual(name, BAD_CAST "polygon") ) - { - parse_polygon(parser,reader); - } - else if (xmlStrEqual(name, BAD_CAST "polyline")) - { - parse_polyline(parser,reader); - } - else if (xmlStrEqual(name, BAD_CAST "line")) - { - parse_line(parser,reader); - } - else if (xmlStrEqual(name, BAD_CAST "rect")) - { - parse_rect(parser,reader); - } - else if (xmlStrEqual(name, BAD_CAST "circle")) - { - parse_circle(parser,reader); - } - else if (xmlStrEqual(name, BAD_CAST "ellipse")) - { - parse_ellipse(parser,reader); - } - else if (xmlStrEqual(name, BAD_CAST "svg")) - { - parse_dimensions(parser,reader); - } -#ifdef MAPNIK_LOG - else if (!xmlStrEqual(name, BAD_CAST "svg")) - { - MAPNIK_LOG_WARN(svg_parser) << "svg_parser: Unhandled svg element=" << name; - } -#endif - } parser.path_.pop_attr(); } } -} - -void end_element(svg_parser & parser, xmlTextReaderPtr reader) -{ - const xmlChar *name; - name = xmlTextReaderConstName(reader); - - - if (!parser.is_defs_ && xmlStrEqual(name, BAD_CAST "g")) + else if (std::strcmp(name, "defs") == 0) { - parser.path_.pop_attr(); + if (node->first_node() != nullptr) + { + parser.is_defs_ = false; + } } - else if (xmlStrEqual(name, BAD_CAST "defs")) - { - parser.is_defs_ = false; - } - else if ((xmlStrEqual(name, BAD_CAST "linearGradient")) || (xmlStrEqual(name, BAD_CAST "radialGradient"))) + else if (std::strcmp(name, "linearGradient") == 0 || std::strcmp(name, "radialGradient") == 0) { parser.gradient_map_[parser.temporary_gradient_.first] = parser.temporary_gradient_.second; } - } -void process_node(svg_parser & parser, xmlTextReaderPtr reader) +void parse_attr(svg_parser & parser, char const* name, char const* value ) { - int node_type = xmlTextReaderNodeType(reader); - switch (node_type) - { - case 1: //start element - start_element(parser,reader); - break; - case 15:// end element - end_element(parser,reader); - break; - default: - break; - } -} - -void parse_attr(svg_parser & parser, const xmlChar * name, const xmlChar * value ) -{ - if (xmlStrEqual(name, BAD_CAST "transform")) + if (std::strcmp(name, "transform") == 0) { agg::trans_affine tr; - mapnik::svg::parse_svg_transform((const char*) value,tr); + mapnik::svg::parse_svg_transform(value,tr); parser.path_.transform().premultiply(tr); } - else if (xmlStrEqual(name, BAD_CAST "fill")) + else if (std::strcmp(name, "fill") == 0) { - if (xmlStrEqual(value, BAD_CAST "none")) + std::string id; + if (std::strcmp(value, "none") == 0) { parser.path_.fill_none(); } - else if (boost::starts_with((const char*)value, "url(#")) + else if (parse_id_from_url(value, id)) { // see if we have a known gradient fill - std::string id = std::string((const char*)&value[5]); - // get rid of the trailing ) - id.erase(id.end()-1); if (parser.gradient_map_.count(id) > 0) { parser.path_.add_fill_gradient(parser.gradient_map_[id]); } else { - MAPNIK_LOG_ERROR(svg_parser) << "Failed to find gradient fill: " << id; + std::stringstream ss; + ss << "Failed to find gradient fill: " << id; + parser.error_messages_.push_back(ss.str()); } } else { - parser.path_.fill(parse_color((const char*) value)); + parser.path_.fill(parse_color_agg(parser.error_messages_, value)); } } - else if (xmlStrEqual(name, BAD_CAST "fill-opacity")) + else if (std::strcmp(name,"fill-opacity") == 0) { - parser.path_.fill_opacity(parse_double((const char*) value)); + parser.path_.fill_opacity(parse_double(parser.error_messages_, value)); } - else if (xmlStrEqual(name, BAD_CAST "fill-rule")) + else if (std::strcmp(name, "fill-rule") == 0) { - if (xmlStrEqual(value, BAD_CAST "evenodd")) + if (std::strcmp(value, "evenodd") == 0) { parser.path_.even_odd(true); } } - else if (xmlStrEqual(name, BAD_CAST "stroke")) + else if (std::strcmp(name, "stroke") == 0) { - if (xmlStrEqual(value, BAD_CAST "none")) + std::string id; + if (std::strcmp(value, "none") == 0) { parser.path_.stroke_none(); } - else if (boost::starts_with((const char*)value, "url(#")) + else if (parse_id_from_url(value, id)) { // see if we have a known gradient fill - std::string id = std::string((const char*)&value[5]); - // get rid of the trailing ) - id.erase(id.end()-1); if (parser.gradient_map_.count(id) > 0) { parser.path_.add_stroke_gradient(parser.gradient_map_[id]); } else { - MAPNIK_LOG_ERROR(svg_parser) << "Failed to find gradient fill: " << id; + std::stringstream ss; + ss << "Failed to find gradient stroke: " << id; + parser.error_messages_.push_back(ss.str()); } } else { - parser.path_.stroke(parse_color((const char*) value)); + parser.path_.stroke(parse_color_agg(parser.error_messages_, value)); } } - else if (xmlStrEqual(name, BAD_CAST "stroke-width")) + else if (std::strcmp(name, "stroke-width") == 0) { - parser.path_.stroke_width(parse_double((const char*)value)); + parser.path_.stroke_width(parse_double(parser.error_messages_, value)); } - else if (xmlStrEqual(name, BAD_CAST "stroke-opacity")) + else if (std::strcmp(name, "stroke-opacity") == 0) { - parser.path_.stroke_opacity(parse_double((const char*)value)); + parser.path_.stroke_opacity(parse_double(parser.error_messages_, value)); } - else if(xmlStrEqual(name,BAD_CAST "stroke-width")) + else if(std::strcmp(name, "stroke-linecap") == 0) { - parser.path_.stroke_width(parse_double((const char*) value)); - } - else if(xmlStrEqual(name,BAD_CAST "stroke-linecap")) - { - if(xmlStrEqual(value,BAD_CAST "butt")) + if(std::strcmp(value, "butt") == 0) parser.path_.line_cap(agg::butt_cap); - else if(xmlStrEqual(value,BAD_CAST "round")) + else if(std::strcmp(value, "round") == 0) parser.path_.line_cap(agg::round_cap); - else if(xmlStrEqual(value,BAD_CAST "square")) + else if(std::strcmp(value, "square") == 0) parser.path_.line_cap(agg::square_cap); } - else if(xmlStrEqual(name,BAD_CAST "stroke-linejoin")) + else if(std::strcmp(name, "stroke-linejoin") == 0) { - if(xmlStrEqual(value,BAD_CAST "miter")) + if(std::strcmp(value, "miter") == 0) parser.path_.line_join(agg::miter_join); - else if(xmlStrEqual(value,BAD_CAST "round")) + else if(std::strcmp(value, "round") == 0) parser.path_.line_join(agg::round_join); - else if(xmlStrEqual(value,BAD_CAST "bevel")) + else if(std::strcmp(value, "bevel") == 0) parser.path_.line_join(agg::bevel_join); } - else if(xmlStrEqual(name,BAD_CAST "stroke-miterlimit")) + else if(std::strcmp(name, "stroke-miterlimit") == 0) { - parser.path_.miter_limit(parse_double((const char*)value)); + parser.path_.miter_limit(parse_double(parser.error_messages_,value)); } - else if(xmlStrEqual(name, BAD_CAST "opacity")) + else if(std::strcmp(name, "opacity") == 0) { - double opacity = parse_double((const char*)value); + double opacity = parse_double(parser.error_messages_, value); parser.path_.opacity(opacity); } - else if (xmlStrEqual(name, BAD_CAST "visibility")) + else if (std::strcmp(name, "visibility") == 0) { - parser.path_.visibility(!xmlStrEqual(value, BAD_CAST "hidden")); + parser.path_.visibility(std::strcmp(value, "hidden") != 0); } - else if (xmlStrEqual(name, BAD_CAST "display") && xmlStrEqual(value, BAD_CAST "none")) + else if (std::strcmp(name, "display") == 0 && std::strcmp(value, "none") == 0) { parser.path_.display(false); } } -void parse_attr(svg_parser & parser, xmlTextReaderPtr reader) +void parse_attr(svg_parser & parser, rapidxml::xml_node const* node) { - const xmlChar *name, *value; - - if (xmlTextReaderMoveToFirstAttribute(reader) == 1) + for (rapidxml::xml_attribute const* attr = node->first_attribute(); + attr; attr = attr->next_attribute()) { - do + auto const* name = attr->name(); + if (std::strcmp(name, "style") == 0) { - name = xmlTextReaderConstName(reader); - value = xmlTextReaderConstValue(reader); - - if (xmlStrEqual(name, BAD_CAST "style")) + using cont_type = std::vector >; + using value_type = cont_type::value_type; + cont_type vec; + parse_style(attr->value(), vec); + for (value_type kv : vec ) { - using cont_type = std::vector >; - using value_type = cont_type::value_type; - cont_type vec; - parse_style((const char*)value, vec); - for (value_type kv : vec ) - { - parse_attr(parser,BAD_CAST kv.first.c_str(),BAD_CAST kv.second.c_str()); - } + parse_attr(parser, kv.first.c_str(), kv.second.c_str()); } - else - { - parse_attr(parser,name,value); - } - } while(xmlTextReaderMoveToNextAttribute(reader) == 1); - } - xmlTextReaderMoveToElement(reader); -} - -void parse_dimensions(svg_parser & parser, xmlTextReaderPtr reader) -{ - xmlChar *value; - double width = 0; - double height = 0; - value = xmlTextReaderGetAttribute(reader, BAD_CAST "width"); - if (value) - { - width = parse_double((const char*)value); - xmlFree(value); - } - xmlChar *value2; - value2 = xmlTextReaderGetAttribute(reader, BAD_CAST "width"); - if (value2) - { - height = parse_double((const char*)value2); - xmlFree(value2); - } - parser.path_.set_dimensions(width,height); - -} -void parse_path(svg_parser & parser, xmlTextReaderPtr reader) -{ - xmlChar *value; - - value = xmlTextReaderGetAttribute(reader, BAD_CAST "d"); - if (value) - { - // d="" (empty paths) are valid - if (std::strlen((const char*)value) < 1) - { - xmlFree(value); } else + { + parse_attr(parser,name, attr->value()); + } + } +} + +void parse_dimensions(svg_parser & parser, rapidxml::xml_node const* node) +{ + double width = 0; + double height = 0; + auto const* width_attr = node->first_attribute("width"); + if (width_attr) + { + width = parse_double(parser.error_messages_, width_attr->value()); + } + auto const* height_attr = node->first_attribute("height"); + if (height_attr) + { + height = parse_double(parser.error_messages_, height_attr->value()); + } + parser.path_.set_dimensions(width, height); +} + +void parse_path(svg_parser & parser, rapidxml::xml_node const* node) +{ + auto const* attr = node->first_attribute("d"); + if (attr != nullptr) + { + auto const* value = attr->value(); + if (std::strlen(value) > 0) { parser.path_.begin_path(); - if (!mapnik::svg::parse_path((const char*) value, parser.path_)) + if (!mapnik::svg::parse_path(value, parser.path_)) { - xmlFree(value); - xmlChar *id_value; - id_value = xmlTextReaderGetAttribute(reader, BAD_CAST "id"); - if (id_value) + auto const* id_attr = node->first_attribute("xml:id"); + if (id_attr == nullptr) id_attr = node->first_attribute("id"); + if (id_attr) { - std::string id_string((const char *) id_value); - xmlFree(id_value); - throw std::runtime_error(std::string("unable to parse invalid svg with id '") + id_string + "'"); + parser.error_messages_.push_back(std::string("unable to parse invalid svg with id '") + + id_attr->value() + "'"); } else { - throw std::runtime_error("unable to parse invalid svg "); + parser.error_messages_.push_back(std::string("unable to parse invalid svg ")); } } parser.path_.end_path(); - xmlFree(value); } } } -void parse_polygon(svg_parser & parser, xmlTextReaderPtr reader) +void parse_polygon(svg_parser & parser, rapidxml::xml_node const* node) { - xmlChar *value; - - value = xmlTextReaderGetAttribute(reader, BAD_CAST "points"); - if (value) + auto const* attr = node->first_attribute("points"); + if (attr != nullptr) { parser.path_.begin_path(); - if (!mapnik::svg::parse_points((const char*) value, parser.path_)) + if (!mapnik::svg::parse_points(attr->value(), parser.path_)) { - xmlFree(value); - throw std::runtime_error("Failed to parse "); + parser.error_messages_.push_back(std::string("Failed to parse 'points'")); } parser.path_.close_subpath(); parser.path_.end_path(); - xmlFree(value); } } -void parse_polyline(svg_parser & parser, xmlTextReaderPtr reader) +void parse_polyline(svg_parser & parser, rapidxml::xml_node const* node) { - xmlChar *value; - - value = xmlTextReaderGetAttribute(reader, BAD_CAST "points"); - if (value) + auto const* attr = node->first_attribute("points"); + if (attr != nullptr) { parser.path_.begin_path(); - if (!mapnik::svg::parse_points((const char*) value, parser.path_)) + if (!mapnik::svg::parse_points(attr->value(), parser.path_)) { - xmlFree(value); - throw std::runtime_error("Failed to parse "); + parser.error_messages_.push_back(std::string("Failed to parse 'points'")); } - parser.path_.end_path(); - xmlFree(value); } } -void parse_line(svg_parser & parser, xmlTextReaderPtr reader) +void parse_line(svg_parser & parser, rapidxml::xml_node const* node) { - xmlChar *value; double x1 = 0.0; double y1 = 0.0; double x2 = 0.0; double y2 = 0.0; - value = xmlTextReaderGetAttribute(reader, BAD_CAST "x1"); - if (value) - { - x1 = parse_double((const char*)value); - xmlFree(value); - } + auto const* x1_attr = node->first_attribute("x1"); + if (x1_attr) x1 = parse_double(parser.error_messages_, x1_attr->value()); - value = xmlTextReaderGetAttribute(reader, BAD_CAST "y1"); - if (value) - { - y1 = parse_double((const char*)value); - xmlFree(value); - } + auto const* y1_attr = node->first_attribute("y1"); + if (y1_attr) y1 = parse_double(parser.error_messages_, y1_attr->value()); - value = xmlTextReaderGetAttribute(reader, BAD_CAST "x2"); - if (value) - { - x2 = parse_double((const char*)value); - xmlFree(value); - } + auto const* x2_attr = node->first_attribute("x2"); + if (x2_attr) x2 = parse_double(parser.error_messages_, x2_attr->value()); - value = xmlTextReaderGetAttribute(reader, BAD_CAST "y2"); - if (value) - { - y2 = parse_double((const char*)value); - xmlFree(value); - } + auto const* y2_attr = node->first_attribute("y2"); + if (y2_attr) y2 = parse_double(parser.error_messages_, y2_attr->value()); parser.path_.begin_path(); parser.path_.move_to(x1, y1); parser.path_.line_to(x2, y2); parser.path_.end_path(); - } -void parse_circle(svg_parser & parser, xmlTextReaderPtr reader) +void parse_circle(svg_parser & parser, rapidxml::xml_node const* node) { - xmlChar *value; double cx = 0.0; double cy = 0.0; double r = 0.0; - value = xmlTextReaderGetAttribute(reader, BAD_CAST "cx"); - if (value) + + auto * attr = node->first_attribute("cx"); + if (attr != nullptr) { - cx = parse_double((const char*)value); - xmlFree(value); + cx = parse_double(parser.error_messages_, attr->value()); } - value = xmlTextReaderGetAttribute(reader, BAD_CAST "cy"); - if (value) + attr = node->first_attribute("cy"); + if (attr != nullptr) { - cy = parse_double((const char*)value); - xmlFree(value); + cy = parse_double(parser.error_messages_, attr->value()); } - value = xmlTextReaderGetAttribute(reader, BAD_CAST "r"); - if (value) + attr = node->first_attribute("r"); + if (attr != nullptr) { - r = parse_double((const char*)value); - xmlFree(value); + r = parse_double(parser.error_messages_, attr->value()); } parser.path_.begin_path(); - if(r != 0.0) { - if(r < 0.0) throw std::runtime_error("parse_circle: Invalid radius"); - agg::ellipse c(cx, cy, r, r); - parser.path_.storage().concat_path(c); + if (r < 0.0) + { + parser.error_messages_.emplace_back("parse_circle: Invalid radius"); + } + else + { + agg::ellipse c(cx, cy, r, r); + parser.path_.storage().concat_path(c); + } } - parser.path_.end_path(); } -void parse_ellipse(svg_parser & parser, xmlTextReaderPtr reader) +void parse_ellipse(svg_parser & parser, rapidxml::xml_node const * node) { - xmlChar *value; double cx = 0.0; double cy = 0.0; double rx = 0.0; double ry = 0.0; - value = xmlTextReaderGetAttribute(reader, BAD_CAST "cx"); - if (value) + auto * attr = node->first_attribute("cx"); + if (attr != nullptr) { - cx = parse_double((const char*)value); - xmlFree(value); + cx = parse_double(parser.error_messages_, attr->value()); } - value = xmlTextReaderGetAttribute(reader, BAD_CAST "cy"); - if (value) + attr = node->first_attribute("cy"); + if (attr) { - cy = parse_double((const char*)value); - xmlFree(value); + cy = parse_double(parser.error_messages_, attr->value()); } - value = xmlTextReaderGetAttribute(reader, BAD_CAST "rx"); - if (value) + attr = node->first_attribute("rx"); + if (attr != nullptr) { - rx = parse_double((const char*)value); - xmlFree(value); + rx = parse_double(parser.error_messages_, attr->value()); } - value = xmlTextReaderGetAttribute(reader, BAD_CAST "ry"); - if (value) + attr = node->first_attribute("ry"); + if (attr != nullptr) { - ry = parse_double((const char*)value); - xmlFree(value); + ry = parse_double(parser.error_messages_, attr->value()); } - parser.path_.begin_path(); - - if(rx != 0.0 && ry != 0.0) + if (rx != 0.0 && ry != 0.0) { - if(rx < 0.0) throw std::runtime_error("parse_ellipse: Invalid rx"); - if(ry < 0.0) throw std::runtime_error("parse_ellipse: Invalid ry"); - agg::ellipse c(cx, cy, rx, ry); - parser.path_.storage().concat_path(c); + + if (rx < 0.0) + { + parser.error_messages_.emplace_back("parse_ellipse: Invalid rx"); + } + else if (ry < 0.0) + { + parser.error_messages_.emplace_back("parse_ellipse: Invalid ry"); + } + else + { + parser.path_.begin_path(); + agg::ellipse c(cx, cy, rx, ry); + parser.path_.storage().concat_path(c); + parser.path_.end_path(); + } } - - parser.path_.end_path(); - } -void parse_rect(svg_parser & parser, xmlTextReaderPtr reader) +void parse_rect(svg_parser & parser, rapidxml::xml_node const* node) { - xmlChar *value; + // http://www.w3.org/TR/SVGTiny12/shapes.html#RectElement double x = 0.0; double y = 0.0; double w = 0.0; @@ -696,183 +662,155 @@ void parse_rect(svg_parser & parser, xmlTextReaderPtr reader) double rx = 0.0; double ry = 0.0; - value = xmlTextReaderGetAttribute(reader, BAD_CAST "x"); - if (value) + auto * attr = node->first_attribute("x"); + if (attr != nullptr) { - x = parse_double((const char*)value); - xmlFree(value); + x = parse_double(parser.error_messages_, attr->value()); } - value = xmlTextReaderGetAttribute(reader, BAD_CAST "y"); - if (value) + attr = node->first_attribute("y"); + if (attr != nullptr) { - y = parse_double((const char*)value); - xmlFree(value); + y = parse_double(parser.error_messages_, attr->value()); } - value = xmlTextReaderGetAttribute(reader, BAD_CAST "width"); - if (value) + attr = node->first_attribute("width"); + if (attr != nullptr) { - w = parse_double((const char*)value); - xmlFree(value); + w = parse_double(parser.error_messages_, attr->value()); } - value = xmlTextReaderGetAttribute(reader, BAD_CAST "height"); - if (value) + attr = node->first_attribute("height"); + if (attr) { - h = parse_double((const char*)value); - xmlFree(value); + h = parse_double(parser.error_messages_, attr->value()); } bool rounded = true; - value = xmlTextReaderGetAttribute(reader, BAD_CAST "rx"); - if (value) + attr = node->first_attribute("rx"); + if (attr != nullptr) { - rx = parse_double((const char*)value); - xmlFree(value); + rx = parse_double(parser.error_messages_, attr->value()); + if ( rx > 0.5 * w ) rx = 0.5 * w; } else rounded = false; - value = xmlTextReaderGetAttribute(reader, BAD_CAST "ry"); - if (value) + attr = node->first_attribute("ry"); + if (attr != nullptr) { - ry = parse_double((const char*)value); + ry = parse_double(parser.error_messages_, attr->value()); + if ( ry > 0.5 * h ) ry = 0.5 * h; if (!rounded) { rx = ry; rounded = true; } - xmlFree(value); } else if (rounded) { ry = rx; } - if(w != 0.0 && h != 0.0) + if (w != 0.0 && h != 0.0) { - if(w < 0.0) throw std::runtime_error("parse_rect: Invalid width"); - if(h < 0.0) throw std::runtime_error("parse_rect: Invalid height"); - if(rx < 0.0) throw std::runtime_error("parse_rect: Invalid rx"); - if(ry < 0.0) throw std::runtime_error("parse_rect: Invalid ry"); - parser.path_.begin_path(); - - if(rounded) + if(w < 0.0) { - agg::rounded_rect r; - r.rect(x,y,x+w,y+h); - r.radius(rx,ry); - parser.path_.storage().concat_path(r); + parser.error_messages_.emplace_back("parse_rect: Invalid width"); + } + else if(h < 0.0) + { + parser.error_messages_.emplace_back("parse_rect: Invalid height"); + } + else if(rx < 0.0) + { + parser.error_messages_.emplace_back("parse_rect: Invalid rx"); + } + else if(ry < 0.0) + { + parser.error_messages_.emplace_back("parse_rect: Invalid ry"); } else { - parser.path_.move_to(x, y); - parser.path_.line_to(x + w, y); - parser.path_.line_to(x + w, y + h); - parser.path_.line_to(x, y + h); - parser.path_.close_subpath(); + parser.path_.begin_path(); + + if(rounded) + { + agg::rounded_rect r; + r.rect(x,y,x+w,y+h); + r.radius(rx,ry); + parser.path_.storage().concat_path(r); + } + else + { + parser.path_.move_to(x, y); + parser.path_.line_to(x + w, y); + parser.path_.line_to(x + w, y + h); + parser.path_.line_to(x, y + h); + parser.path_.close_subpath(); + } + parser.path_.end_path(); } - parser.path_.end_path(); } } - -/* - * -*/ -void parse_gradient_stop(svg_parser & parser, xmlTextReaderPtr reader) +void parse_gradient_stop(svg_parser & parser, rapidxml::xml_node const* node) { - xmlChar *value; - double offset = 0.0; mapnik::color stop_color; double opacity = 1.0; - value = xmlTextReaderGetAttribute(reader, BAD_CAST "offset"); - if (value) + auto * attr = node->first_attribute("offset"); + if (attr != nullptr) { - offset = parse_double((const char*)value); - xmlFree(value); + offset = parse_double(parser.error_messages_,attr->value()); } - value = xmlTextReaderGetAttribute(reader, BAD_CAST "style"); - if (value) + attr = node->first_attribute("style"); + if (attr != nullptr) { using cont_type = std::vector >; using value_type = cont_type::value_type; cont_type vec; - parse_style((const char*)value, vec); + parse_style(attr->value(), vec); for (value_type kv : vec ) { if (kv.first == "stop-color") { - try - { - stop_color = mapnik::parse_color(kv.second.c_str()); - } - catch (mapnik::config_error const& ex) - { - MAPNIK_LOG_ERROR(svg_parser) << ex.what(); - } + stop_color = parse_color(parser.error_messages_, kv.second.c_str()); } else if (kv.first == "stop-opacity") { - opacity = parse_double(kv.second.c_str()); + opacity = parse_double(parser.error_messages_,kv.second.c_str()); } } - xmlFree(value); } - value = xmlTextReaderGetAttribute(reader, BAD_CAST "stop-color"); - if (value) + attr = node->first_attribute("stop-color"); + if (attr != nullptr) { - try - { - stop_color = mapnik::parse_color((const char *) value); - } - catch (mapnik::config_error const& ex) - { - MAPNIK_LOG_ERROR(svg_parser) << ex.what(); - } - xmlFree(value); + stop_color = parse_color(parser.error_messages_, attr->value()); } - value = xmlTextReaderGetAttribute(reader, BAD_CAST "stop-opacity"); - if (value) + attr = node->first_attribute("stop-opacity"); + if (attr != nullptr) { - opacity = parse_double((const char *) value); - xmlFree(value); + opacity = parse_double(parser.error_messages_, attr->value()); } - stop_color.set_alpha(static_cast(opacity * 255)); parser.temporary_gradient_.second.add_stop(offset, stop_color); - - /* - MAPNIK_LOG_DEBUG(svg_parser) << "\tFound Stop: " << offset << " " - << (unsigned)stop_color.red() << " " - << (unsigned)stop_color.green() << " " - << (unsigned)stop_color.blue() << " " - << (unsigned)stop_color.alpha(); - */ } -bool parse_common_gradient(svg_parser & parser, xmlTextReaderPtr reader) +bool parse_common_gradient(svg_parser & parser, rapidxml::xml_node const* node) { - xmlChar *value; - std::string id; - value = xmlTextReaderGetAttribute(reader, BAD_CAST "id"); - if (value) + auto * attr = node->first_attribute("xml:id"); + if (attr == nullptr) attr = node->first_attribute("id"); + + if (attr != nullptr) { // start a new gradient - gradient new_grad; - id = std::string((const char *) value); - parser.temporary_gradient_ = std::make_pair(id, new_grad); - xmlFree(value); + parser.temporary_gradient_ = std::make_pair(std::string(attr->value()), gradient()); } else { @@ -881,28 +819,31 @@ bool parse_common_gradient(svg_parser & parser, xmlTextReaderPtr reader) } // check if we should inherit from another tag - value = xmlTextReaderGetAttribute(reader, BAD_CAST "xlink:href"); - if (value) + attr = node->first_attribute("xlink:href"); + if (attr != nullptr) { - if (value[0] == '#') + auto const* value = attr->value(); + if (std::strlen(value) > 1 && value[0] == '#') { - std::string linkid = (const char *) &value[1]; + std::string linkid(&value[1]); // FIXME !!! if (parser.gradient_map_.count(linkid)) { parser.temporary_gradient_.second = parser.gradient_map_[linkid]; } else { - MAPNIK_LOG_ERROR(svg_parser) << "Failed to find linked gradient " << linkid; + std::stringstream ss; + ss << "Failed to find linked gradient " << linkid; + parser.error_messages_.push_back(ss.str()); + return false; } } - xmlFree(value); } - value = xmlTextReaderGetAttribute(reader, BAD_CAST "gradientUnits"); - if (value) + attr = node->first_attribute("gradientUnits"); + if (attr != nullptr) { - if (xmlStrEqual(value, BAD_CAST "userSpaceOnUse")) + if (std::strcmp(attr->value(), "userSpaceOnUse") == 0) { parser.temporary_gradient_.second.set_units(USER_SPACE_ON_USE); } @@ -910,39 +851,21 @@ bool parse_common_gradient(svg_parser & parser, xmlTextReaderPtr reader) { parser.temporary_gradient_.second.set_units(OBJECT_BOUNDING_BOX); } - xmlFree(value); } - value = xmlTextReaderGetAttribute(reader, BAD_CAST "gradientTransform"); - if (value) + attr = node->first_attribute("gradientTransform"); + if (attr != nullptr) { agg::trans_affine tr; - mapnik::svg::parse_svg_transform((const char*) value,tr); + mapnik::svg::parse_svg_transform(attr->value(),tr); parser.temporary_gradient_.second.set_transform(tr); - xmlFree(value); } - return true; } -/** - * -*/ -void parse_radial_gradient(svg_parser & parser, xmlTextReaderPtr reader) +void parse_radial_gradient(svg_parser & parser, rapidxml::xml_node const* node) { - if (!parse_common_gradient(parser,reader)) - return; - - xmlChar *value; + parse_common_gradient(parser, node); double cx = 0.5; double cy = 0.5; double fx = 0.0; @@ -950,43 +873,38 @@ void parse_radial_gradient(svg_parser & parser, xmlTextReaderPtr reader) double r = 0.5; bool has_percent=true; - value = xmlTextReaderGetAttribute(reader, BAD_CAST "cx"); - if (value) + auto * attr = node->first_attribute("cx"); + if (attr != nullptr) { - cx = parse_double_optional_percent((const char*)value, has_percent); - xmlFree(value); + cx = parse_double_optional_percent(parser.error_messages_, attr->value(), has_percent); } - value = xmlTextReaderGetAttribute(reader, BAD_CAST "cy"); - if (value) + attr = node->first_attribute("cy"); + if (attr != nullptr) { - cy = parse_double_optional_percent((const char*)value, has_percent); - xmlFree(value); + cy = parse_double_optional_percent(parser.error_messages_, attr->value(), has_percent); } - value = xmlTextReaderGetAttribute(reader, BAD_CAST "fx"); - if (value) + attr = node->first_attribute("fx"); + if (attr != nullptr) { - fx = parse_double_optional_percent((const char*)value, has_percent); - xmlFree(value); + fx = parse_double_optional_percent(parser.error_messages_,attr->value(), has_percent); } else fx = cx; - value = xmlTextReaderGetAttribute(reader, BAD_CAST "fy"); - if (value) + attr = node->first_attribute("fy"); + if (attr != nullptr) { - fy = parse_double_optional_percent((const char*)value, has_percent); - xmlFree(value); + fy = parse_double_optional_percent(parser.error_messages_, attr->value(), has_percent); } else fy = cy; - value = xmlTextReaderGetAttribute(reader, BAD_CAST "r"); - if (value) + attr = node->first_attribute("r"); + if (attr != nullptr) { - r = parse_double_optional_percent((const char*)value, has_percent); - xmlFree(value); + r = parse_double_optional_percent(parser.error_messages_, attr->value(), has_percent); } // this logic for detecting %'s will not support mixed coordinates. if (has_percent && parser.temporary_gradient_.second.get_units() == USER_SPACE_ON_USE) @@ -1002,44 +920,38 @@ void parse_radial_gradient(svg_parser & parser, xmlTextReaderPtr reader) //MAPNIK_LOG_DEBUG(svg_parser) << "Found Radial Gradient: " << " " << cx << " " << cy << " " << fx << " " << fy << " " << r; } -void parse_linear_gradient(svg_parser & parser, xmlTextReaderPtr reader) +void parse_linear_gradient(svg_parser & parser, rapidxml::xml_node const* node) { - if (!parse_common_gradient(parser,reader)) - return; + parse_common_gradient(parser, node); - xmlChar *value; double x1 = 0.0; double x2 = 1.0; double y1 = 0.0; double y2 = 1.0; bool has_percent=true; - value = xmlTextReaderGetAttribute(reader, BAD_CAST "x1"); - if (value) + auto * attr = node->first_attribute("x1"); + if (attr != nullptr) { - x1 = parse_double_optional_percent((const char*)value, has_percent); - xmlFree(value); + x1 = parse_double_optional_percent(parser.error_messages_, attr->value(), has_percent); } - value = xmlTextReaderGetAttribute(reader, BAD_CAST "x2"); - if (value) + attr = node->first_attribute("x2"); + if (attr != nullptr) { - x2 = parse_double_optional_percent((const char*)value, has_percent); - xmlFree(value); + x2 = parse_double_optional_percent(parser.error_messages_, attr->value(), has_percent); } - value = xmlTextReaderGetAttribute(reader, BAD_CAST "y1"); - if (value) + attr = node->first_attribute("y1"); + if (attr != nullptr) { - y1 = parse_double_optional_percent((const char*)value, has_percent); - xmlFree(value); + y1 = parse_double_optional_percent(parser.error_messages_, attr->value(), has_percent); } - value = xmlTextReaderGetAttribute(reader, BAD_CAST "y2"); - if (value) + attr = node->first_attribute("y2"); + if (attr != nullptr) { - y2 = parse_double_optional_percent((const char*)value, has_percent); - xmlFree(value); + y2 = parse_double_optional_percent(parser.error_messages_, attr->value(), has_percent); } // this logic for detecting %'s will not support mixed coordinates. if (has_percent && parser.temporary_gradient_.second.get_units() == USER_SPACE_ON_USE) @@ -1051,8 +963,6 @@ void parse_linear_gradient(svg_parser & parser, xmlTextReaderPtr reader) parser.temporary_gradient_.second.set_control_points(x1,y1,x2,y2); // add this here in case we have no end tag, will be replaced if we do parser.gradient_map_[parser.temporary_gradient_.first] = parser.temporary_gradient_.second; - - //MAPNIK_LOG_DEBUG(svg_parser) << "Found Linear Gradient: " << "(" << x1 << " " << y1 << "),(" << x2 << " " << y2 << ")"; } svg_parser::svg_parser(svg_converter stream(filename.c_str()); + if (!stream) { - MAPNIK_LOG_ERROR(svg_parser) << "Unable to open '" << filename << "'"; + std::stringstream ss; + ss << "Unable to open '" << filename << "'"; + error_messages_.push_back(ss.str()); + return false; } - else if (!parse_reader(*this,reader)) + + stream.unsetf(std::ios::skipws); + std::vector buffer(std::istreambuf_iterator(stream.rdbuf()), + std::istreambuf_iterator()); + buffer.push_back(0); + + const int flags = rapidxml::parse_trim_whitespace | rapidxml::parse_validate_closing_tags; + rapidxml::xml_document<> doc; + try { - MAPNIK_LOG_ERROR(svg_parser) << "Unable to parse '" << filename << "'"; + doc.parse(buffer.data()); } + catch (rapidxml::parse_error const& ex) + { + std::stringstream ss; + ss << "svg_parser::parse - Unable to parse '" << filename << "'"; + error_messages_.push_back(ss.str()); + return false; + } + + for (rapidxml::xml_node const* child = doc.first_node(); + child; child = child->next_sibling()) + { + traverse_tree(*this, child); + } + return error_messages_.empty() ? true : false; } -void svg_parser::parse_from_string(std::string const& svg) +bool svg_parser::parse_from_string(std::string const& svg) { - xmlTextReaderPtr reader = xmlReaderForMemory(svg.c_str(),safe_cast(svg.size()),nullptr,nullptr, - (XML_PARSE_NOBLANKS | XML_PARSE_NOCDATA | XML_PARSE_NOERROR | XML_PARSE_NOWARNING)); - if (reader == nullptr) + const int flags = rapidxml::parse_trim_whitespace | rapidxml::parse_validate_closing_tags; + rapidxml::xml_document<> doc; + std::vector buffer(svg.begin(), svg.end()); + buffer.push_back(0); + try { - MAPNIK_LOG_ERROR(svg_parser) << "Unable to parse '" << svg << "'"; + doc.parse(buffer.data()); } - else if (!parse_reader(*this,reader)) + catch (rapidxml::parse_error const& ex) { - MAPNIK_LOG_ERROR(svg_parser) << "Unable to parse '" << svg << "'"; + std::stringstream ss; + ss << "Unable to parse '" << svg << "'"; + error_messages_.push_back(ss.str()); + return false; } + for (rapidxml::xml_node const* child = doc.first_node(); + child; child = child->next_sibling()) + { + traverse_tree(*this, child); + } + return error_messages_.empty() ? true : false; } +svg_parser::error_message_container const& svg_parser::error_messages() const +{ + return error_messages_; +} }} diff --git a/src/text/face.cpp b/src/text/face.cpp index 10afa65ae..79f35d8fe 100644 --- a/src/text/face.cpp +++ b/src/text/face.cpp @@ -36,12 +36,12 @@ font_face::font_face(FT_Face face) bool font_face::set_character_sizes(double size) { - return !FT_Set_Char_Size(face_,0,(FT_F26Dot6)(size * (1<<6)),0,0); + return (FT_Set_Char_Size(face_,0,(FT_F26Dot6)(size * (1<<6)),0,0) == 0); } bool font_face::set_unscaled_character_sizes() { - return !FT_Set_Char_Size(face_,0,face_->units_per_EM,0,0); + return (FT_Set_Char_Size(face_,0,face_->units_per_EM,0,0) == 0); } bool font_face::glyph_dimensions(glyph_info & glyph) const diff --git a/src/text/placement_finder.cpp b/src/text/placement_finder.cpp index c4e0866a5..53dda7da7 100644 --- a/src/text/placement_finder.cpp +++ b/src/text/placement_finder.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -32,9 +33,6 @@ #include #include -// agg -#include "agg_conv_clip_polyline.h" - // stl #include @@ -118,7 +116,7 @@ text_upright_e placement_finder::simplify_upright(text_upright_e upright, double bool placement_finder::find_point_placement(pixel_position const& pos) { - glyph_positions_ptr glyphs = std::make_shared(); + glyph_positions_ptr glyphs = std::make_unique(); std::vector > bboxes; glyphs->reserve(layouts_.glyphs_count()); @@ -182,11 +180,26 @@ bool placement_finder::find_point_placement(pixel_position const& pos) // add_marker first checks for collision and then updates the detector. if (has_marker_ && !add_marker(glyphs, pos)) return false; - for (box2d const& bbox : bboxes) + box2d label_box; + bool first = true; + for (box2d const& box : bboxes) { - detector_.insert(bbox, layouts_.text()); + if (first) + { + label_box = box; + first = false; + } + else + { + label_box.expand_to_include(box); + } + detector_.insert(box, layouts_.text()); + } + // do not render text off the canvas + if (extent_.intersects(label_box)) + { + placements_.push_back(std::move(glyphs)); } - placements_.push_back(glyphs); return true; } @@ -200,7 +213,7 @@ bool placement_finder::single_line_placement(vertex_cache &pp, text_upright_e or vertex_cache::scoped_state begin(pp); text_upright_e real_orientation = simplify_upright(orientation, pp.angle()); - glyph_positions_ptr glyphs = std::make_shared(); + glyph_positions_ptr glyphs = std::make_unique(); std::vector > bboxes; glyphs->reserve(layouts_.glyphs_count()); bboxes.reserve(layouts_.glyphs_count()); @@ -213,7 +226,8 @@ bool placement_finder::single_line_placement(vertex_cache &pp, text_upright_e or pixel_position align_offset = layout.alignment_offset(); pixel_position const& layout_displacement = layout.displacement(); double sign = (real_orientation == UPRIGHT_LEFT) ? -1 : 1; - double offset = layout_displacement.y + 0.5 * sign * layout.height(); + //double offset = 0 - (layout_displacement.y + 0.5 * sign * layout.height()); + double offset = layout_displacement.y - 0.5 * sign * layout.height(); double adjust_character_spacing = .0; double layout_width = layout.width(); bool adjust = layout.horizontal_alignment() == H_ADJUST; @@ -232,7 +246,7 @@ bool placement_finder::single_line_placement(vertex_cache &pp, text_upright_e or { // Only subtract half the line height here and half at the end because text is automatically // centered on the line - offset -= sign * line.height()/2; + offset += sign * line.height()/2; vertex_cache & off_pp = pp.get_offseted(offset, sign * layout_width); vertex_cache::scoped_state off_state(off_pp); // TODO: Remove this when a clean implementation in vertex_cache::get_offseted is done double line_width = adjust ? (line.glyphs_width() + line.space_count() * adjust_character_spacing) : line.width(); @@ -300,7 +314,7 @@ bool placement_finder::single_line_placement(vertex_cache &pp, text_upright_e or glyphs->emplace_back(glyph, pos, rot); } // See comment above - offset -= sign * line.height()/2; + offset += sign * line.height()/2; } } @@ -325,11 +339,26 @@ bool placement_finder::single_line_placement(vertex_cache &pp, text_upright_e or return single_line_placement(pp, real_orientation == UPRIGHT_RIGHT ? UPRIGHT_LEFT : UPRIGHT_RIGHT); } + box2d label_box; + bool first = true; for (box2d const& box : bboxes) { + if (first) + { + label_box = box; + first = false; + } + else + { + label_box.expand_to_include(box); + } detector_.insert(box, layouts_.text()); } - placements_.push_back(glyphs); + // do not render text off the canvas + if (extent_.intersects(label_box)) + { + placements_.push_back(std::move(glyphs)); + } return true; } @@ -368,9 +397,7 @@ bool placement_finder::collision(const box2d &box, const value_unicode_s margin = (text_props_->margin != 0 ? text_props_->margin : text_props_->minimum_distance) * scale_factor_; repeat_distance = text_props_->repeat_distance * scale_factor_; } - return !detector_.extent().intersects(box) - || - (text_props_->avoid_edges && !extent_.contains(box)) + return (text_props_->avoid_edges && !extent_.contains(box)) || (text_props_->minimum_padding > 0 && !extent_.contains(box + (scale_factor_ * text_props_->minimum_padding))) @@ -391,7 +418,7 @@ void placement_finder::set_marker(marker_info_ptr m, box2d box, bool mar } -bool placement_finder::add_marker(glyph_positions_ptr glyphs, pixel_position const& pos) const +bool placement_finder::add_marker(glyph_positions_ptr & glyphs, pixel_position const& pos) const { pixel_position real_pos = (marker_unlocked_ ? pos : glyphs->get_base_point()) + marker_displacement_; box2d bbox = marker_box_; diff --git a/src/text/renderer.cpp b/src/text/renderer.cpp index 05a460631..227c35dbe 100644 --- a/src/text/renderer.cpp +++ b/src/text/renderer.cpp @@ -92,13 +92,12 @@ void text_renderer::prepare_glyphs(glyph_positions const& positions) template void composite_bitmap(T & pixmap, FT_Bitmap *bitmap, unsigned rgba, int x, int y, double opacity, composite_mode_e comp_op) { - int x_max=x+bitmap->width; - int y_max=y+bitmap->rows; - int i,p,j,q; + int x_max = x + bitmap->width; + int y_max = y + bitmap->rows; - for (i=x,p=0;ibuffer[q*bitmap->width+p]; if (gray) diff --git a/src/text/symbolizer_helpers.cpp b/src/text/symbolizer_helpers.cpp index 99c7c0356..860359af6 100644 --- a/src/text/symbolizer_helpers.cpp +++ b/src/text/symbolizer_helpers.cpp @@ -41,8 +41,6 @@ #include #include -//agg -#include "agg_conv_clip_polyline.h" namespace mapnik { namespace detail { @@ -79,11 +77,10 @@ struct split_multi_geometries { using container_type = T; split_multi_geometries(container_type & cont, view_transform const& t, - proj_transform const& prj_trans, double minimum_path_length) + proj_transform const& prj_trans) : cont_(cont), t_(t), - prj_trans_(prj_trans), - minimum_path_length_(minimum_path_length) {} + prj_trans_(prj_trans) { } void operator() (geometry::geometry_empty const&) const {} void operator() (geometry::multi_point const& multi_pt) const @@ -95,18 +92,7 @@ struct split_multi_geometries } void operator() (geometry::line_string const& line) const { - if (minimum_path_length_ > 0) - { - box2d bbox = t_.forward(geometry::envelope(line), prj_trans_); - if (bbox.width() >= minimum_path_length_) - { - cont_.push_back(base_symbolizer_helper::geometry_cref(std::cref(line))); - } - } - else - { - cont_.push_back(base_symbolizer_helper::geometry_cref(std::cref(line))); - } + cont_.push_back(base_symbolizer_helper::geometry_cref(std::cref(line))); } void operator() (geometry::multi_line_string const& multi_line) const @@ -119,18 +105,7 @@ struct split_multi_geometries void operator() (geometry::polygon const& poly) const { - if (minimum_path_length_ > 0) - { - box2d bbox = t_.forward(geometry::envelope(poly), prj_trans_); - if (bbox.width() >= minimum_path_length_) - { - cont_.push_back(base_symbolizer_helper::geometry_cref(std::cref(poly))); - } - } - else - { - cont_.push_back(base_symbolizer_helper::geometry_cref(std::cref(poly))); - } + cont_.push_back(base_symbolizer_helper::geometry_cref(std::cref(poly))); } void operator() (geometry::multi_polygon const& multi_poly) const @@ -158,7 +133,6 @@ struct split_multi_geometries container_type & cont_; view_transform const& t_; proj_transform const& prj_trans_; - double minimum_path_length_; }; } // ns detail @@ -207,10 +181,9 @@ struct largest_bbox_first void base_symbolizer_helper::initialize_geometries() const { - double minimum_path_length = text_props_->minimum_path_length; auto const& geom = feature_.get_geometry(); util::apply_visitor(detail::split_multi_geometries - (geometries_to_process_, t_, prj_trans_, minimum_path_length ), geom); + (geometries_to_process_, t_, prj_trans_), geom); if (!geometries_to_process_.empty()) { auto type = geometry::geometry_type(geom); diff --git a/src/vertex_cache.cpp b/src/vertex_cache.cpp index 071b8d08b..c0d575892 100644 --- a/src/vertex_cache.cpp +++ b/src/vertex_cache.cpp @@ -155,8 +155,14 @@ vertex_cache & vertex_cache::get_offseted(double offset, double region_width) // find the point on the offset line closest to the current position, // which we'll use to make the offset line aligned to this one. - double seek = offseted_line->position_closest_to(current_position_); + // TODO: `position_closest_to` is disable since it is too expensive: + // https://github.com/mapnik/mapnik/issues/2937 + //double seek = offseted_line->position_closest_to(current_position_); + double seek = (position_ + region_width/2.0) * offseted_line->length() / length() - region_width/2.0; + if (seek < 0) seek = 0; + if (seek > offseted_line->length()) seek = offseted_line->length(); offseted_line->move(seek); + return *offseted_line; } diff --git a/test/cleanup.hpp b/test/cleanup.hpp index 264a6b559..f175e72b4 100644 --- a/test/cleanup.hpp +++ b/test/cleanup.hpp @@ -1,9 +1,11 @@ #ifndef TEST_MEMORY_CLEANUP #define TEST_MEMORY_CLEANUP +#if defined(HAVE_LIBXML2) #include #include #include +#endif #if defined(HAVE_CAIRO) #include @@ -21,6 +23,7 @@ inline void run_cleanup() // only call this once, on exit // to make sure valgrind output is clean // http://xmlsoft.org/xmlmem.html +#if defined(HAVE_LIBXML2) xmlCleanupCharEncodingHandlers(); xmlCleanupEncodingAliases(); xmlCleanupGlobals(); @@ -29,6 +32,7 @@ inline void run_cleanup() xmlCleanupInputCallbacks(); xmlCleanupOutputCallbacks(); xmlCleanupMemory(); +#endif #if defined(HAVE_CAIRO) // http://cairographics.org/manual/cairo-Error-handling.html#cairo-debug-reset-static-data @@ -45,9 +49,9 @@ inline void run_cleanup() #endif // https://trac.osgeo.org/proj/wiki/ProjAPI#EnvironmentFunctions pj_deallocate_grids(); -#endif +#endif } } -#endif \ No newline at end of file +#endif diff --git a/test/data b/test/data index a6838f99a..d0a23b2a5 160000 --- a/test/data +++ b/test/data @@ -1 +1 @@ -Subproject commit a6838f99a4c4be37989cea6718442cf4b53bd616 +Subproject commit d0a23b2a512d2ea83f08a9c1dc50e9b9b4a08dd5 diff --git a/test/data-visual b/test/data-visual index 7080866ca..e3d79ed49 160000 --- a/test/data-visual +++ b/test/data-visual @@ -1 +1 @@ -Subproject commit 7080866ca1b3523fead1a25c017bd87082806eb4 +Subproject commit e3d79ed493485afbb5c79cd90913c8db488561b6 diff --git a/test/standalone/csv_test.cpp b/test/standalone/csv_test.cpp index 0a78d493e..e04166f3f 100644 --- a/test/standalone/csv_test.cpp +++ b/test/standalone/csv_test.cpp @@ -10,7 +10,7 @@ #include #include #include - +#include #include #include #include @@ -157,519 +157,524 @@ void require_geometry(mapnik::feature_ptr feature, } } // anonymous namespace -const bool registered = mapnik::datasource_cache::instance().register_datasources("./plugins/input/"); +static const std::string csv_plugin("./plugins/input/csv.input"); + +const bool registered = mapnik::datasource_cache::instance().register_datasources(csv_plugin); TEST_CASE("csv") { - REQUIRE(registered); - // make the tests silent since we intentially test error conditions that are noisy - auto const severity = mapnik::logger::instance().get_severity(); - mapnik::logger::instance().set_severity(mapnik::logger::none); + if (mapnik::util::exists(csv_plugin)) + { - // check the CSV datasource is loaded - const std::vector plugin_names = - mapnik::datasource_cache::instance().plugin_names(); - const bool have_csv_plugin = - std::find(plugin_names.begin(), plugin_names.end(), "csv") != plugin_names.end(); + REQUIRE(registered); - SECTION("broken files") { - if (have_csv_plugin) { - std::vector broken; - add_csv_files("test/data/csv/fails", broken); - add_csv_files("test/data/csv/warns", broken); - broken.emplace_back("test/data/csv/fails/does_not_exist.csv"); + // make the tests silent since we intentially test error conditions that are noisy + auto const severity = mapnik::logger::instance().get_severity(); + mapnik::logger::instance().set_severity(mapnik::logger::none); - for (auto const &path : broken) { - REQUIRE_THROWS(get_csv_ds(path.native())); - } - } - } // END SECTION + // check the CSV datasource is loaded + const std::vector plugin_names = + mapnik::datasource_cache::instance().plugin_names(); + const bool have_csv_plugin = + std::find(plugin_names.begin(), plugin_names.end(), "csv") != plugin_names.end(); - SECTION("good files") { - if (have_csv_plugin) { - std::vector good; - add_csv_files("test/data/csv", good); - add_csv_files("test/data/csv/warns", good); + SECTION("broken files") { + if (have_csv_plugin) { + std::vector broken; + add_csv_files("test/data/csv/fails", broken); + add_csv_files("test/data/csv/warns", broken); + broken.emplace_back("test/data/csv/fails/does_not_exist.csv"); - for (auto const &path : good) { - auto ds = get_csv_ds(path.native(), false); - // require a non-null pointer returned - REQUIRE(bool(ds)); - } - } - } // END SECTION + for (auto const &path : broken) { + REQUIRE_THROWS(get_csv_ds(path.native())); + } + } + } // END SECTION - SECTION("lon/lat detection") { - for (auto const &lon_name : {std::string("lon"), std::string("lng")}) { - auto ds = get_csv_ds((boost::format("test/data/csv/%1%_lat.csv") % lon_name).str()); - auto fields = ds->get_descriptor().get_descriptors(); - require_field_names(fields, {lon_name, "lat"}); - require_field_types(fields, {mapnik::Integer, mapnik::Integer}); + SECTION("good files") { + if (have_csv_plugin) { + std::vector good; + add_csv_files("test/data/csv", good); + add_csv_files("test/data/csv/warns", good); - CHECK(ds->get_geometry_type() == mapnik::datasource_geometry_t::Point); + for (auto const &path : good) { + auto ds = get_csv_ds(path.native(), false); + // require a non-null pointer returned + REQUIRE(bool(ds)); + } + } + } // END SECTION - mapnik::query query(ds->envelope()); - for (auto const &field : fields) { - query.add_property_name(field.get_name()); - } - auto features = ds->features(query); - auto feature = features->next(); + SECTION("lon/lat detection") { + for (auto const &lon_name : {std::string("lon"), std::string("lng")}) { + auto ds = get_csv_ds((boost::format("test/data/csv/%1%_lat.csv") % lon_name).str()); + auto fields = ds->get_descriptor().get_descriptors(); + require_field_names(fields, {lon_name, "lat"}); + require_field_types(fields, {mapnik::Integer, mapnik::Integer}); - require_attributes(feature, { - attr { lon_name, mapnik::value_integer(0) }, - attr { "lat", mapnik::value_integer(0) } - }); - } - } // END SECTION + CHECK(ds->get_geometry_type() == mapnik::datasource_geometry_t::Point); - SECTION("type detection") { - auto ds = get_csv_ds("test/data/csv/nypd.csv"); - auto fields = ds->get_descriptor().get_descriptors(); - require_field_names(fields, {"Precinct", "Phone", "Address", "City", "geo_longitude", "geo_latitude", "geo_accuracy"}); - require_field_types(fields, {mapnik::String, mapnik::String, mapnik::String, mapnik::String, mapnik::Double, mapnik::Double, mapnik::String}); + mapnik::query query(ds->envelope()); + for (auto const &field : fields) { + query.add_property_name(field.get_name()); + } + auto features = ds->features(query); + auto feature = features->next(); - CHECK(ds->get_geometry_type() == mapnik::datasource_geometry_t::Point); - CHECK(count_features(all_features(ds)) == 2); + require_attributes(feature, { + attr { lon_name, mapnik::value_integer(0) }, + attr { "lat", mapnik::value_integer(0) } + }); + } + } // END SECTION - auto feature = all_features(ds)->next(); - require_attributes(feature, { - attr { "City", mapnik::value_unicode_string("New York, NY") } - , attr { "geo_accuracy", mapnik::value_unicode_string("house") } - , attr { "Phone", mapnik::value_unicode_string("(212) 334-0711") } - , attr { "Address", mapnik::value_unicode_string("19 Elizabeth Street") } - , attr { "Precinct", mapnik::value_unicode_string("5th Precinct") } - , attr { "geo_longitude", mapnik::value_integer(-70) } - , attr { "geo_latitude", mapnik::value_integer(40) } - }); - } // END SECTION + SECTION("type detection") { + auto ds = get_csv_ds("test/data/csv/nypd.csv"); + auto fields = ds->get_descriptor().get_descriptors(); + require_field_names(fields, {"Precinct", "Phone", "Address", "City", "geo_longitude", "geo_latitude", "geo_accuracy"}); + require_field_types(fields, {mapnik::String, mapnik::String, mapnik::String, mapnik::String, mapnik::Double, mapnik::Double, mapnik::String}); - SECTION("skipping blank rows") { - auto ds = get_csv_ds("test/data/csv/blank_rows.csv"); - auto fields = ds->get_descriptor().get_descriptors(); - require_field_names(fields, {"x", "y", "name"}); - require_field_types(fields, {mapnik::Integer, mapnik::Integer, mapnik::String}); + CHECK(ds->get_geometry_type() == mapnik::datasource_geometry_t::Point); + CHECK(count_features(all_features(ds)) == 2); - CHECK(ds->get_geometry_type() == mapnik::datasource_geometry_t::Point); - CHECK(count_features(all_features(ds)) == 2); - } // END SECTION + auto feature = all_features(ds)->next(); + require_attributes(feature, { + attr { "City", mapnik::value_unicode_string("New York, NY") } + , attr { "geo_accuracy", mapnik::value_unicode_string("house") } + , attr { "Phone", mapnik::value_unicode_string("(212) 334-0711") } + , attr { "Address", mapnik::value_unicode_string("19 Elizabeth Street") } + , attr { "Precinct", mapnik::value_unicode_string("5th Precinct") } + , attr { "geo_longitude", mapnik::value_integer(-70) } + , attr { "geo_latitude", mapnik::value_integer(40) } + }); + } // END SECTION - SECTION("empty rows") { - auto ds = get_csv_ds("test/data/csv/empty_rows.csv"); - auto fields = ds->get_descriptor().get_descriptors(); - require_field_names(fields, {"x", "y", "text", "date", "integer", "boolean", "float", "time", "datetime", "empty_column"}); - require_field_types(fields, {mapnik::Integer, mapnik::Integer, mapnik::String, mapnik::String, mapnik::Integer, mapnik::Boolean, mapnik::Double, mapnik::String, mapnik::String, mapnik::String}); + SECTION("skipping blank rows") { + auto ds = get_csv_ds("test/data/csv/blank_rows.csv"); + auto fields = ds->get_descriptor().get_descriptors(); + require_field_names(fields, {"x", "y", "name"}); + require_field_types(fields, {mapnik::Integer, mapnik::Integer, mapnik::String}); - CHECK(ds->get_geometry_type() == mapnik::datasource_geometry_t::Point); - CHECK(count_features(all_features(ds)) == 4); + CHECK(ds->get_geometry_type() == mapnik::datasource_geometry_t::Point); + CHECK(count_features(all_features(ds)) == 2); + } // END SECTION - auto featureset = all_features(ds); - auto feature = featureset->next(); - require_attributes(feature, { - attr { "x", mapnik::value_integer(0) } - , attr { "empty_column", mapnik::value_unicode_string("") } - , attr { "text", mapnik::value_unicode_string("a b") } - , attr { "float", mapnik::value_double(1.0) } - , attr { "datetime", mapnik::value_unicode_string("1971-01-01T04:14:00") } - , attr { "y", mapnik::value_integer(0) } - , attr { "boolean", mapnik::value_bool(true) } - , attr { "time", mapnik::value_unicode_string("04:14:00") } - , attr { "date", mapnik::value_unicode_string("1971-01-01") } - , attr { "integer", mapnik::value_integer(40) } - }); + SECTION("empty rows") { + auto ds = get_csv_ds("test/data/csv/empty_rows.csv"); + auto fields = ds->get_descriptor().get_descriptors(); + require_field_names(fields, {"x", "y", "text", "date", "integer", "boolean", "float", "time", "datetime", "empty_column"}); + require_field_types(fields, {mapnik::Integer, mapnik::Integer, mapnik::String, mapnik::String, mapnik::Integer, mapnik::Boolean, mapnik::Double, mapnik::String, mapnik::String, mapnik::String}); - while (bool(feature = featureset->next())) { - CHECK(feature->size() == 10); - CHECK(feature->get("empty_column") == mapnik::value_unicode_string("")); - } - } // END SECTION + CHECK(ds->get_geometry_type() == mapnik::datasource_geometry_t::Point); + CHECK(count_features(all_features(ds)) == 4); - SECTION("slashes") { - auto ds = get_csv_ds("test/data/csv/has_attributes_with_slashes.csv"); - auto fields = ds->get_descriptor().get_descriptors(); - require_field_names(fields, {"x", "y", "name"}); - // NOTE: y column is integer, even though a double value is used below in the test? - require_field_types(fields, {mapnik::Integer, mapnik::Integer, mapnik::String}); + auto featureset = all_features(ds); + auto feature = featureset->next(); + require_attributes(feature, { + attr { "x", mapnik::value_integer(0) } + , attr { "empty_column", mapnik::value_unicode_string("") } + , attr { "text", mapnik::value_unicode_string("a b") } + , attr { "float", mapnik::value_double(1.0) } + , attr { "datetime", mapnik::value_unicode_string("1971-01-01T04:14:00") } + , attr { "y", mapnik::value_integer(0) } + , attr { "boolean", mapnik::value_bool(true) } + , attr { "time", mapnik::value_unicode_string("04:14:00") } + , attr { "date", mapnik::value_unicode_string("1971-01-01") } + , attr { "integer", mapnik::value_integer(40) } + }); - auto featureset = all_features(ds); - require_attributes(featureset->next(), { - attr{"x", 0} - , attr{"y", 0} - , attr{"name", mapnik::value_unicode_string("a/a") } }); - require_attributes(featureset->next(), { - attr{"x", 1} - , attr{"y", 4} - , attr{"name", mapnik::value_unicode_string("b/b") } }); - require_attributes(featureset->next(), { - attr{"x", 10} - , attr{"y", 2.5} - , attr{"name", mapnik::value_unicode_string("c/c") } }); - } // END SECTION + while (bool(feature = featureset->next())) { + CHECK(feature->size() == 10); + CHECK(feature->get("empty_column") == mapnik::value_unicode_string("")); + } + } // END SECTION - SECTION("wkt field") { - using mapnik::geometry::geometry_types; + SECTION("slashes") { + auto ds = get_csv_ds("test/data/csv/has_attributes_with_slashes.csv"); + auto fields = ds->get_descriptor().get_descriptors(); + require_field_names(fields, {"x", "y", "name"}); + // NOTE: y column is integer, even though a double value is used below in the test? + require_field_types(fields, {mapnik::Integer, mapnik::Integer, mapnik::String}); - auto ds = get_csv_ds("test/data/csv/wkt.csv"); - auto fields = ds->get_descriptor().get_descriptors(); - require_field_names(fields, {"type"}); - require_field_types(fields, {mapnik::String}); + auto featureset = all_features(ds); + require_attributes(featureset->next(), { + attr{"x", 0} + , attr{"y", 0} + , attr{"name", mapnik::value_unicode_string("a/a") } }); + require_attributes(featureset->next(), { + attr{"x", 1} + , attr{"y", 4} + , attr{"name", mapnik::value_unicode_string("b/b") } }); + require_attributes(featureset->next(), { + attr{"x", 10} + , attr{"y", 2.5} + , attr{"name", mapnik::value_unicode_string("c/c") } }); + } // END SECTION - auto featureset = all_features(ds); - require_geometry(featureset->next(), 1, geometry_types::Point); - require_geometry(featureset->next(), 1, geometry_types::LineString); - require_geometry(featureset->next(), 1, geometry_types::Polygon); - require_geometry(featureset->next(), 1, geometry_types::Polygon); - require_geometry(featureset->next(), 4, geometry_types::MultiPoint); - require_geometry(featureset->next(), 2, geometry_types::MultiLineString); - require_geometry(featureset->next(), 2, geometry_types::MultiPolygon); - require_geometry(featureset->next(), 2, geometry_types::MultiPolygon); - } // END SECTION + SECTION("wkt field") { + using mapnik::geometry::geometry_types; - SECTION("handling of missing header") { - // TODO: does this mean 'missing_header.csv' should be in the warnings - // subdirectory, since it doesn't work in strict mode? - auto ds = get_csv_ds("test/data/csv/missing_header.csv", false); - auto fields = ds->get_descriptor().get_descriptors(); - require_field_names(fields, {"one", "two", "x", "y", "_4", "aftermissing"}); - auto feature = all_features(ds)->next(); - REQUIRE(feature); - REQUIRE(feature->has_key("_4")); - CHECK(feature->get("_4") == mapnik::value_unicode_string("missing")); - } // END SECTION + auto ds = get_csv_ds("test/data/csv/wkt.csv"); + auto fields = ds->get_descriptor().get_descriptors(); + require_field_names(fields, {"type"}); + require_field_types(fields, {mapnik::String}); - SECTION("handling of headers that are numbers") { - auto ds = get_csv_ds("test/data/csv/numbers_for_headers.csv"); - auto fields = ds->get_descriptor().get_descriptors(); - require_field_names(fields, {"x", "y", "1990", "1991", "1992"}); - auto feature = all_features(ds)->next(); - require_attributes(feature, { - attr{"x", 0} - , attr{"y", 0} - , attr{"1990", 1} - , attr{"1991", 2} - , attr{"1992", 3} - }); - auto expression = mapnik::parse_expression("[1991]=2"); - REQUIRE(bool(expression)); - auto value = mapnik::util::apply_visitor( - mapnik::evaluate( - *feature, mapnik::attributes()), *expression); - CHECK(value == true); - } // END SECTION + auto featureset = all_features(ds); + require_geometry(featureset->next(), 1, geometry_types::Point); + require_geometry(featureset->next(), 1, geometry_types::LineString); + require_geometry(featureset->next(), 1, geometry_types::Polygon); + require_geometry(featureset->next(), 1, geometry_types::Polygon); + require_geometry(featureset->next(), 4, geometry_types::MultiPoint); + require_geometry(featureset->next(), 2, geometry_types::MultiLineString); + require_geometry(featureset->next(), 2, geometry_types::MultiPolygon); + require_geometry(featureset->next(), 2, geometry_types::MultiPolygon); + } // END SECTION - SECTION("quoted numbers") { - using ustring = mapnik::value_unicode_string; + SECTION("handling of missing header") { + // TODO: does this mean 'missing_header.csv' should be in the warnings + // subdirectory, since it doesn't work in strict mode? + auto ds = get_csv_ds("test/data/csv/missing_header.csv", false); + auto fields = ds->get_descriptor().get_descriptors(); + require_field_names(fields, {"one", "two", "x", "y", "_4", "aftermissing"}); + auto feature = all_features(ds)->next(); + REQUIRE(feature); + REQUIRE(feature->has_key("_4")); + CHECK(feature->get("_4") == mapnik::value_unicode_string("missing")); + } // END SECTION - auto ds = get_csv_ds("test/data/csv/quoted_numbers.csv"); - auto fields = ds->get_descriptor().get_descriptors(); - require_field_names(fields, {"x", "y", "label"}); - auto featureset = all_features(ds); + SECTION("handling of headers that are numbers") { + auto ds = get_csv_ds("test/data/csv/numbers_for_headers.csv"); + auto fields = ds->get_descriptor().get_descriptors(); + require_field_names(fields, {"x", "y", "1990", "1991", "1992"}); + auto feature = all_features(ds)->next(); + require_attributes(feature, { + attr{"x", 0} + , attr{"y", 0} + , attr{"1990", 1} + , attr{"1991", 2} + , attr{"1992", 3} + }); + auto expression = mapnik::parse_expression("[1991]=2"); + REQUIRE(bool(expression)); + auto value = mapnik::util::apply_visitor( + mapnik::evaluate( + *feature, mapnik::attributes()), *expression); + CHECK(value == true); + } // END SECTION - require_attributes(featureset->next(), { - attr{"x", 0}, attr{"y", 0}, attr{"label", ustring("0,0") } }); - require_attributes(featureset->next(), { - attr{"x", 5}, attr{"y", 5}, attr{"label", ustring("5,5") } }); - require_attributes(featureset->next(), { - attr{"x", 0}, attr{"y", 5}, attr{"label", ustring("0,5") } }); - require_attributes(featureset->next(), { - attr{"x", 5}, attr{"y", 0}, attr{"label", ustring("5,0") } }); - require_attributes(featureset->next(), { - attr{"x", 2.5}, attr{"y", 2.5}, attr{"label", ustring("2.5,2.5") } }); + SECTION("quoted numbers") { + using ustring = mapnik::value_unicode_string; - } // END SECTION + auto ds = get_csv_ds("test/data/csv/quoted_numbers.csv"); + auto fields = ds->get_descriptor().get_descriptors(); + require_field_names(fields, {"x", "y", "label"}); + auto featureset = all_features(ds); - SECTION("reading newlines") { - for (auto const &platform : {std::string("windows"), std::string("mac")}) { - std::string file_name = (boost::format("test/data/csv/%1%_newlines.csv") % platform).str(); - auto ds = get_csv_ds(file_name); - auto fields = ds->get_descriptor().get_descriptors(); - require_field_names(fields, {"x", "y", "z"}); - require_attributes(all_features(ds)->next(), { - attr{"x", 1}, attr{"y", 10}, attr{"z", 9999.9999} }); - } - } // END SECTION + require_attributes(featureset->next(), { + attr{"x", 0}, attr{"y", 0}, attr{"label", ustring("0,0") } }); + require_attributes(featureset->next(), { + attr{"x", 5}, attr{"y", 5}, attr{"label", ustring("5,5") } }); + require_attributes(featureset->next(), { + attr{"x", 0}, attr{"y", 5}, attr{"label", ustring("0,5") } }); + require_attributes(featureset->next(), { + attr{"x", 5}, attr{"y", 0}, attr{"label", ustring("5,0") } }); + require_attributes(featureset->next(), { + attr{"x", 2.5}, attr{"y", 2.5}, attr{"label", ustring("2.5,2.5") } }); - SECTION("mixed newlines") { - using ustring = mapnik::value_unicode_string; + } // END SECTION - for (auto const &file : { - std::string("test/data/csv/mac_newlines_with_unix_inline.csv") - , std::string("test/data/csv/mac_newlines_with_unix_inline_escaped.csv") - , std::string("test/data/csv/windows_newlines_with_unix_inline.csv") - , std::string("test/data/csv/windows_newlines_with_unix_inline_escaped.csv") + SECTION("reading newlines") { + for (auto const &platform : {std::string("windows"), std::string("mac")}) { + std::string file_name = (boost::format("test/data/csv/%1%_newlines.csv") % platform).str(); + auto ds = get_csv_ds(file_name); + auto fields = ds->get_descriptor().get_descriptors(); + require_field_names(fields, {"x", "y", "z"}); + require_attributes(all_features(ds)->next(), { + attr{"x", 1}, attr{"y", 10}, attr{"z", 9999.9999} }); + } + } // END SECTION + + SECTION("mixed newlines") { + using ustring = mapnik::value_unicode_string; + + for (auto const &file : { + std::string("test/data/csv/mac_newlines_with_unix_inline.csv") + , std::string("test/data/csv/mac_newlines_with_unix_inline_escaped.csv") + , std::string("test/data/csv/windows_newlines_with_unix_inline.csv") + , std::string("test/data/csv/windows_newlines_with_unix_inline_escaped.csv") + }) { + auto ds = get_csv_ds(file); + auto fields = ds->get_descriptor().get_descriptors(); + require_field_names(fields, {"x", "y", "line"}); + require_attributes(all_features(ds)->next(), { + attr{"x", 0}, attr{"y", 0} + , attr{"line", ustring("many\n lines\n of text\n with unix newlines")} }); + } + } // END SECTION + + SECTION("tabs") { + auto ds = get_csv_ds("test/data/csv/tabs_in_csv.csv"); + auto fields = ds->get_descriptor().get_descriptors(); + require_field_names(fields, {"x", "y", "z"}); + require_attributes(all_features(ds)->next(), { + attr{"x", -122}, attr{"y", 48}, attr{"z", 0} }); + } // END SECTION + + SECTION("separators") { + using ustring = mapnik::value_unicode_string; + + for (auto const &file : { + std::string("test/data/csv/pipe_delimiters.csv") + , std::string("test/data/csv/semicolon_delimiters.csv") + }) { + auto ds = get_csv_ds(file); + auto fields = ds->get_descriptor().get_descriptors(); + require_field_names(fields, {"x", "y", "z"}); + require_attributes(all_features(ds)->next(), { + attr{"x", 0}, attr{"y", 0}, attr{"z", ustring("hello")} }); + } + } // END SECTION + + SECTION("null and bool keywords are empty strings") { + using ustring = mapnik::value_unicode_string; + + auto ds = get_csv_ds("test/data/csv/nulls_and_booleans_as_strings.csv"); + auto fields = ds->get_descriptor().get_descriptors(); + require_field_names(fields, {"x", "y", "null", "boolean"}); + require_field_types(fields, {mapnik::Integer, mapnik::Integer, mapnik::String, mapnik::Boolean}); + + auto featureset = all_features(ds); + require_attributes(featureset->next(), { + attr{"x", 0}, attr{"y", 0}, attr{"null", ustring("null")}, attr{"boolean", true}}); + require_attributes(featureset->next(), { + attr{"x", 0}, attr{"y", 0}, attr{"null", ustring("")}, attr{"boolean", false}}); + } // END SECTION + + SECTION("nonexistent query fields throw") { + auto ds = get_csv_ds("test/data/csv/lon_lat.csv"); + auto fields = ds->get_descriptor().get_descriptors(); + require_field_names(fields, {"lon", "lat"}); + require_field_types(fields, {mapnik::Integer, mapnik::Integer}); + + mapnik::query query(ds->envelope()); + for (auto const &field : fields) { + query.add_property_name(field.get_name()); + } + // also add an invalid one, triggering throw + query.add_property_name("bogus"); + + REQUIRE_THROWS(ds->features(query)); + } // END SECTION + + SECTION("leading zeros mean strings") { + using ustring = mapnik::value_unicode_string; + + auto ds = get_csv_ds("test/data/csv/leading_zeros.csv"); + auto fields = ds->get_descriptor().get_descriptors(); + require_field_names(fields, {"x", "y", "fips"}); + require_field_types(fields, {mapnik::Integer, mapnik::Integer, mapnik::String}); + + auto featureset = all_features(ds); + require_attributes(featureset->next(), { + attr{"x", 0}, attr{"y", 0}, attr{"fips", ustring("001")}}); + require_attributes(featureset->next(), { + attr{"x", 0}, attr{"y", 0}, attr{"fips", ustring("003")}}); + require_attributes(featureset->next(), { + attr{"x", 0}, attr{"y", 0}, attr{"fips", ustring("005")}}); + } // END SECTION + + SECTION("advanced geometry detection") { + using row = std::pair; + + for (row r : { + row{"point", mapnik::datasource_geometry_t::Point} + , row{"poly", mapnik::datasource_geometry_t::Polygon} + , row{"multi_poly", mapnik::datasource_geometry_t::Polygon} + , row{"line", mapnik::datasource_geometry_t::LineString} }) { - auto ds = get_csv_ds(file); - auto fields = ds->get_descriptor().get_descriptors(); - require_field_names(fields, {"x", "y", "line"}); - require_attributes(all_features(ds)->next(), { - attr{"x", 0}, attr{"y", 0} - , attr{"line", ustring("many\n lines\n of text\n with unix newlines")} }); - } - } // END SECTION + std::string file_name = (boost::format("test/data/csv/%1%_wkt.csv") % r.first).str(); + auto ds = get_csv_ds(file_name); + CHECK(ds->get_geometry_type() == r.second); + } + } // END SECTION - SECTION("tabs") { - auto ds = get_csv_ds("test/data/csv/tabs_in_csv.csv"); - auto fields = ds->get_descriptor().get_descriptors(); - require_field_names(fields, {"x", "y", "z"}); - require_attributes(all_features(ds)->next(), { - attr{"x", -122}, attr{"y", 48}, attr{"z", 0} }); - } // END SECTION + SECTION("creation of CSV from in-memory strings") { + using ustring = mapnik::value_unicode_string; - SECTION("separators") { - using ustring = mapnik::value_unicode_string; + for (auto const &name : {std::string("Winthrop, WA"), std::string(u8"Qu\u00e9bec")}) { + std::string csv_string = + (boost::format( + "wkt,Name\n" + "\"POINT (120.15 48.47)\",\"%1%\"\n" + ) % name).str(); - for (auto const &file : { - std::string("test/data/csv/pipe_delimiters.csv") - , std::string("test/data/csv/semicolon_delimiters.csv") - }) { - auto ds = get_csv_ds(file); - auto fields = ds->get_descriptor().get_descriptors(); - require_field_names(fields, {"x", "y", "z"}); - require_attributes(all_features(ds)->next(), { - attr{"x", 0}, attr{"y", 0}, attr{"z", ustring("hello")} }); - } - } // END SECTION + mapnik::parameters params; + params["type"] = std::string("csv"); + params["inline"] = csv_string; + auto ds = mapnik::datasource_cache::instance().create(params); + REQUIRE(bool(ds)); - SECTION("null and bool keywords are empty strings") { - using ustring = mapnik::value_unicode_string; + auto feature = all_features(ds)->next(); + REQUIRE(bool(feature)); + REQUIRE(feature->has_key("Name")); + CHECK(feature->get("Name") == ustring(name.c_str())); + } + } // END SECTION - auto ds = get_csv_ds("test/data/csv/nulls_and_booleans_as_strings.csv"); - auto fields = ds->get_descriptor().get_descriptors(); - require_field_names(fields, {"x", "y", "null", "boolean"}); - require_field_types(fields, {mapnik::Integer, mapnik::Integer, mapnik::String, mapnik::Boolean}); + SECTION("geojson quoting") { + using mapnik::geometry::geometry_types; - auto featureset = all_features(ds); - require_attributes(featureset->next(), { - attr{"x", 0}, attr{"y", 0}, attr{"null", ustring("null")}, attr{"boolean", true}}); - require_attributes(featureset->next(), { - attr{"x", 0}, attr{"y", 0}, attr{"null", ustring("")}, attr{"boolean", false}}); - } // END SECTION + for (auto const &file : { + std::string("test/data/csv/geojson_double_quote_escape.csv") + , std::string("test/data/csv/geojson_single_quote.csv") + , std::string("test/data/csv/geojson_2x_double_quote_filebakery_style.csv") + }) { + auto ds = get_csv_ds(file); + auto fields = ds->get_descriptor().get_descriptors(); + require_field_names(fields, {"type"}); + require_field_types(fields, {mapnik::String}); - SECTION("nonexistent query fields throw") { - auto ds = get_csv_ds("test/data/csv/lon_lat.csv"); - auto fields = ds->get_descriptor().get_descriptors(); - require_field_names(fields, {"lon", "lat"}); - require_field_types(fields, {mapnik::Integer, mapnik::Integer}); + auto featureset = all_features(ds); + require_geometry(featureset->next(), 1, geometry_types::Point); + require_geometry(featureset->next(), 1, geometry_types::LineString); + require_geometry(featureset->next(), 1, geometry_types::Polygon); + require_geometry(featureset->next(), 1, geometry_types::Polygon); + require_geometry(featureset->next(), 4, geometry_types::MultiPoint); + require_geometry(featureset->next(), 2, geometry_types::MultiLineString); + require_geometry(featureset->next(), 2, geometry_types::MultiPolygon); + require_geometry(featureset->next(), 2, geometry_types::MultiPolygon); + } + } // END SECTION - mapnik::query query(ds->envelope()); - for (auto const &field : fields) { - query.add_property_name(field.get_name()); - } - // also add an invalid one, triggering throw - query.add_property_name("bogus"); + SECTION("blank undelimited rows are still parsed") { + using ustring = mapnik::value_unicode_string; - REQUIRE_THROWS(ds->features(query)); - } // END SECTION + // TODO: does this mean this CSV file should be in the warnings + // subdirectory, since it doesn't work in strict mode? + auto ds = get_csv_ds("test/data/csv/more_headers_than_column_values.csv", false); + auto fields = ds->get_descriptor().get_descriptors(); + require_field_names(fields, {"x", "y", "one", "two", "three"}); + require_field_types(fields, {mapnik::Integer, mapnik::Integer, mapnik::String, mapnik::String, mapnik::String}); - SECTION("leading zeros mean strings") { - using ustring = mapnik::value_unicode_string; + require_attributes(all_features(ds)->next(), { + attr{"x", 0}, attr{"y", 0}, attr{"one", ustring("")}, attr{"two", ustring("")}, attr{"three", ustring("")} }); + } // END SECTION - auto ds = get_csv_ds("test/data/csv/leading_zeros.csv"); - auto fields = ds->get_descriptor().get_descriptors(); - require_field_names(fields, {"x", "y", "fips"}); - require_field_types(fields, {mapnik::Integer, mapnik::Integer, mapnik::String}); + SECTION("fewer headers than rows throws") { + REQUIRE_THROWS(get_csv_ds("test/data/csv/more_column_values_than_headers.csv")); + } // END SECTION - auto featureset = all_features(ds); - require_attributes(featureset->next(), { - attr{"x", 0}, attr{"y", 0}, attr{"fips", ustring("001")}}); - require_attributes(featureset->next(), { - attr{"x", 0}, attr{"y", 0}, attr{"fips", ustring("003")}}); - require_attributes(featureset->next(), { - attr{"x", 0}, attr{"y", 0}, attr{"fips", ustring("005")}}); - } // END SECTION + SECTION("feature ID only incremented for valid rows") { + auto ds = get_csv_ds("test/data/csv/warns/feature_id_counting.csv", false); + auto fs = all_features(ds); - SECTION("advanced geometry detection") { - using row = std::pair; + // first + auto feature = fs->next(); + REQUIRE(bool(feature)); + CHECK(feature->id() == 1); - for (row r : { - row{"point", mapnik::datasource_geometry_t::Point} - , row{"poly", mapnik::datasource_geometry_t::Polygon} - , row{"multi_poly", mapnik::datasource_geometry_t::Polygon} - , row{"line", mapnik::datasource_geometry_t::LineString} - }) { - std::string file_name = (boost::format("test/data/csv/%1%_wkt.csv") % r.first).str(); - auto ds = get_csv_ds(file_name); - CHECK(ds->get_geometry_type() == r.second); - } - } // END SECTION + // second, should have skipped bogus one + feature = fs->next(); + REQUIRE(bool(feature)); + CHECK(feature->id() == 2); - SECTION("creation of CSV from in-memory strings") { - using ustring = mapnik::value_unicode_string; + feature = fs->next(); + CHECK(!feature); + } // END SECTION - for (auto const &name : {std::string("Winthrop, WA"), std::string(u8"Qu\u00e9bec")}) { - std::string csv_string = - (boost::format( - "wkt,Name\n" - "\"POINT (120.15 48.47)\",\"%1%\"\n" - ) % name).str(); + SECTION("dynamically defining headers") { + using ustring = mapnik::value_unicode_string; + using row = std::pair; - mapnik::parameters params; - params["type"] = std::string("csv"); - params["inline"] = csv_string; - auto ds = mapnik::datasource_cache::instance().create(params); - REQUIRE(bool(ds)); + for (auto const &r : { + row{"test/data/csv/fails/needs_headers_two_lines.csv", 2} + , row{"test/data/csv/fails/needs_headers_one_line.csv", 1} + , row{"test/data/csv/fails/needs_headers_one_line_no_newline.csv", 1} + }) { + mapnik::parameters params; + params["type"] = std::string("csv"); + params["file"] = r.first; + params["headers"] = "x,y,name"; + auto ds = mapnik::datasource_cache::instance().create(params); + REQUIRE(bool(ds)); - auto feature = all_features(ds)->next(); - REQUIRE(bool(feature)); - REQUIRE(feature->has_key("Name")); - CHECK(feature->get("Name") == ustring(name.c_str())); - } - } // END SECTION + auto fields = ds->get_descriptor().get_descriptors(); + require_field_names(fields, {"x", "y", "name"}); + require_field_types(fields, {mapnik::Integer, mapnik::Integer, mapnik::String}); + require_attributes(all_features(ds)->next(), { + attr{"x", 0}, attr{"y", 0}, attr{"name", ustring("data_name")} }); + REQUIRE(count_features(all_features(ds)) == r.second); + } + } // END SECTION - SECTION("geojson quoting") { - using mapnik::geometry::geometry_types; + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wlong-long" + SECTION("64bit int fields work") { + auto ds = get_csv_ds("test/data/csv/64bit_int.csv"); + auto fields = ds->get_descriptor().get_descriptors(); + require_field_names(fields, {"x", "y", "bigint"}); + require_field_types(fields, {mapnik::Integer, mapnik::Integer, mapnik::Integer}); - for (auto const &file : { - std::string("test/data/csv/geojson_double_quote_escape.csv") - , std::string("test/data/csv/geojson_single_quote.csv") - , std::string("test/data/csv/geojson_2x_double_quote_filebakery_style.csv") - }) { - auto ds = get_csv_ds(file); - auto fields = ds->get_descriptor().get_descriptors(); - require_field_names(fields, {"type"}); - require_field_types(fields, {mapnik::String}); + auto fs = all_features(ds); + auto feature = fs->next(); + require_attributes(feature, { + attr{"x", 0}, attr{"y", 0}, attr{"bigint", 2147483648} }); - auto featureset = all_features(ds); - require_geometry(featureset->next(), 1, geometry_types::Point); - require_geometry(featureset->next(), 1, geometry_types::LineString); - require_geometry(featureset->next(), 1, geometry_types::Polygon); - require_geometry(featureset->next(), 1, geometry_types::Polygon); - require_geometry(featureset->next(), 4, geometry_types::MultiPoint); - require_geometry(featureset->next(), 2, geometry_types::MultiLineString); - require_geometry(featureset->next(), 2, geometry_types::MultiPolygon); - require_geometry(featureset->next(), 2, geometry_types::MultiPolygon); - } - } // END SECTION + feature = fs->next(); + require_attributes(feature, { + attr{"x", 0}, attr{"y", 0}, attr{"bigint", 9223372036854775807ll} }); + require_attributes(feature, { + attr{"x", 0}, attr{"y", 0}, attr{"bigint", 0x7FFFFFFFFFFFFFFFll} }); + } // END SECTION + #pragma GCC diagnostic pop - SECTION("blank undelimited rows are still parsed") { - using ustring = mapnik::value_unicode_string; + SECTION("various number types") { + auto ds = get_csv_ds("test/data/csv/number_types.csv"); + auto fields = ds->get_descriptor().get_descriptors(); + require_field_names(fields, {"x", "y", "floats"}); + require_field_types(fields, {mapnik::Integer, mapnik::Integer, mapnik::Double}); + auto fs = all_features(ds); + for (double d : { .0, +.0, 1e-06, -1e-06, 0.000001, 1.234e+16, 1.234e+16 }) { + auto feature = fs->next(); + REQUIRE(bool(feature)); + CHECK(feature->get("floats").get() == Approx(d)); + } + } // END SECTION - // TODO: does this mean this CSV file should be in the warnings - // subdirectory, since it doesn't work in strict mode? - auto ds = get_csv_ds("test/data/csv/more_headers_than_column_values.csv", false); - auto fields = ds->get_descriptor().get_descriptors(); - require_field_names(fields, {"x", "y", "one", "two", "three"}); - require_field_types(fields, {mapnik::Integer, mapnik::Integer, mapnik::String, mapnik::String, mapnik::String}); + SECTION("manually supplied extent") { + std::string csv_string("wkt,Name\n"); + mapnik::parameters params; + params["type"] = std::string("csv"); + params["inline"] = csv_string; + params["extent"] = "-180,-90,180,90"; + auto ds = mapnik::datasource_cache::instance().create(params); + REQUIRE(bool(ds)); + auto box = ds->envelope(); + CHECK(box.minx() == -180); + CHECK(box.miny() == -90); + CHECK(box.maxx() == 180); + CHECK(box.maxy() == 90); + } // END SECTION - require_attributes(all_features(ds)->next(), { - attr{"x", 0}, attr{"y", 0}, attr{"one", ustring("")}, attr{"two", ustring("")}, attr{"three", ustring("")} }); - } // END SECTION + SECTION("inline geojson") { + std::string csv_string = "geojson\n'{\"coordinates\":[-92.22568,38.59553],\"type\":\"Point\"}'"; + mapnik::parameters params; + params["type"] = std::string("csv"); + params["inline"] = csv_string; + auto ds = mapnik::datasource_cache::instance().create(params); + REQUIRE(bool(ds)); - SECTION("fewer headers than rows throws") { - REQUIRE_THROWS(get_csv_ds("test/data/csv/more_column_values_than_headers.csv")); - } // END SECTION + auto fields = ds->get_descriptor().get_descriptors(); + require_field_names(fields, {}); - SECTION("feature ID only incremented for valid rows") { - auto ds = get_csv_ds("test/data/csv/warns/feature_id_counting.csv", false); - auto fs = all_features(ds); + // TODO: this originally had the following comment: + // - re-enable after https://github.com/mapnik/mapnik/issues/2319 is fixed + // but that seems to have been merged and tested separately? + auto fs = all_features(ds); + auto feat = fs->next(); + CHECK(feature_count(feat->get_geometry()) == 1); + } // END SECTION - // first - auto feature = fs->next(); - REQUIRE(bool(feature)); - CHECK(feature->id() == 1); - - // second, should have skipped bogus one - feature = fs->next(); - REQUIRE(bool(feature)); - CHECK(feature->id() == 2); - - feature = fs->next(); - CHECK(!feature); - } // END SECTION - - SECTION("dynamically defining headers") { - using ustring = mapnik::value_unicode_string; - using row = std::pair; - - for (auto const &r : { - row{"test/data/csv/fails/needs_headers_two_lines.csv", 2} - , row{"test/data/csv/fails/needs_headers_one_line.csv", 1} - , row{"test/data/csv/fails/needs_headers_one_line_no_newline.csv", 1} - }) { - mapnik::parameters params; - params["type"] = std::string("csv"); - params["file"] = r.first; - params["headers"] = "x,y,name"; - auto ds = mapnik::datasource_cache::instance().create(params); - REQUIRE(bool(ds)); - - auto fields = ds->get_descriptor().get_descriptors(); - require_field_names(fields, {"x", "y", "name"}); - require_field_types(fields, {mapnik::Integer, mapnik::Integer, mapnik::String}); - require_attributes(all_features(ds)->next(), { - attr{"x", 0}, attr{"y", 0}, attr{"name", ustring("data_name")} }); - REQUIRE(count_features(all_features(ds)) == r.second); - } - } // END SECTION - -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wlong-long" - SECTION("64bit int fields work") { - auto ds = get_csv_ds("test/data/csv/64bit_int.csv"); - auto fields = ds->get_descriptor().get_descriptors(); - require_field_names(fields, {"x", "y", "bigint"}); - require_field_types(fields, {mapnik::Integer, mapnik::Integer, mapnik::Integer}); - - auto fs = all_features(ds); - auto feature = fs->next(); - require_attributes(feature, { - attr{"x", 0}, attr{"y", 0}, attr{"bigint", 2147483648} }); - - feature = fs->next(); - require_attributes(feature, { - attr{"x", 0}, attr{"y", 0}, attr{"bigint", 9223372036854775807ll} }); - require_attributes(feature, { - attr{"x", 0}, attr{"y", 0}, attr{"bigint", 0x7FFFFFFFFFFFFFFFll} }); - } // END SECTION -#pragma GCC diagnostic pop - - SECTION("various number types") { - auto ds = get_csv_ds("test/data/csv/number_types.csv"); - auto fields = ds->get_descriptor().get_descriptors(); - require_field_names(fields, {"x", "y", "floats"}); - require_field_types(fields, {mapnik::Integer, mapnik::Integer, mapnik::Double}); - - auto fs = all_features(ds); - for (double d : { .0, +.0, 1e-06, -1e-06, 0.000001, 1.234e+16, 1.234e+16 }) { - auto feature = fs->next(); - REQUIRE(bool(feature)); - CHECK(feature->get("floats").get() == Approx(d)); - } - } // END SECTION - - SECTION("manually supplied extent") { - std::string csv_string("wkt,Name\n"); - mapnik::parameters params; - params["type"] = std::string("csv"); - params["inline"] = csv_string; - params["extent"] = "-180,-90,180,90"; - auto ds = mapnik::datasource_cache::instance().create(params); - REQUIRE(bool(ds)); - - auto box = ds->envelope(); - CHECK(box.minx() == -180); - CHECK(box.miny() == -90); - CHECK(box.maxx() == 180); - CHECK(box.maxy() == 90); - } // END SECTION - - SECTION("inline geojson") { - std::string csv_string = "geojson\n'{\"coordinates\":[-92.22568,38.59553],\"type\":\"Point\"}'"; - mapnik::parameters params; - params["type"] = std::string("csv"); - params["inline"] = csv_string; - auto ds = mapnik::datasource_cache::instance().create(params); - REQUIRE(bool(ds)); - - auto fields = ds->get_descriptor().get_descriptors(); - require_field_names(fields, {}); - - // TODO: this originally had the following comment: - // - re-enable after https://github.com/mapnik/mapnik/issues/2319 is fixed - // but that seems to have been merged and tested separately? - auto fs = all_features(ds); - auto feat = fs->next(); - CHECK(feature_count(feat->get_geometry()) == 1); - } // END SECTION - - mapnik::logger::instance().set_severity(severity); + mapnik::logger::instance().set_severity(severity); + } } // END TEST CASE diff --git a/test/unit/core/copy_move_test.cpp b/test/unit/core/copy_move_test.cpp index 3b38ae76c..a219178a9 100644 --- a/test/unit/core/copy_move_test.cpp +++ b/test/unit/core/copy_move_test.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -19,55 +20,58 @@ SECTION("layers") { { mapnik::Map m0(100,100); mapnik::Map m2(200,100); - mapnik::datasource_cache::instance().register_datasources("plugins/input/shape.input"); - mapnik::parameters p; - p["type"]="shape"; - p["file"]="demo/data/boundaries"; - p["encoding"]="latin1"; - auto ds0 = mapnik::datasource_cache::instance().create(p); + std::string shape_plugin("./plugins/input/shape.input"); + if (mapnik::util::exists(shape_plugin)) + { + mapnik::parameters p; + p["type"]="shape"; + p["file"]="demo/data/boundaries"; + p["encoding"]="latin1"; + auto ds0 = mapnik::datasource_cache::instance().create(p); - auto ds1 = ds0; // shared ptr copy - REQUIRE( (ds1 == ds0) ); - REQUIRE( !(*ds1 != *ds0) ); - REQUIRE( (ds1.get() == ds0.get()) ); - ds1 = mapnik::datasource_cache::instance().create(p); // new with the same parameters - REQUIRE( (ds1 != ds0) ); - REQUIRE( (*ds1 == *ds0) ); - auto ds2 = std::move(ds1); - REQUIRE( (ds2 != ds0) ); - REQUIRE( (*ds2 == *ds0) ); + auto ds1 = ds0; // shared ptr copy + REQUIRE( (ds1 == ds0) ); + REQUIRE( !(*ds1 != *ds0) ); + REQUIRE( (ds1.get() == ds0.get()) ); + ds1 = mapnik::datasource_cache::instance().create(p); // new with the same parameters + REQUIRE( (ds1 != ds0) ); + REQUIRE( (*ds1 == *ds0) ); + auto ds2 = std::move(ds1); + REQUIRE( (ds2 != ds0) ); + REQUIRE( (*ds2 == *ds0) ); - // mapnik::layer - mapnik::layer l0("test-layer"); - l0.set_datasource(ds0); + // mapnik::layer + mapnik::layer l0("test-layer"); + l0.set_datasource(ds0); - mapnik::layer l1 = l0; // copy assignment - REQUIRE( (l1 == l0) ); - mapnik::layer l2(l0); // copy ctor - REQUIRE( (l2 == l0) ); - mapnik::layer l3(mapnik::layer("test-layer")); // move ctor - l3.set_datasource(ds2); + mapnik::layer l1 = l0; // copy assignment + REQUIRE( (l1 == l0) ); + mapnik::layer l2(l0); // copy ctor + REQUIRE( (l2 == l0) ); + mapnik::layer l3(mapnik::layer("test-layer")); // move ctor + l3.set_datasource(ds2); - REQUIRE( (l3 == l0) ); - mapnik::layer l4 = std::move(l3); - REQUIRE( (l4 == l0) ); // move assignment + REQUIRE( (l3 == l0) ); + mapnik::layer l4 = std::move(l3); + REQUIRE( (l4 == l0) ); // move assignment - m0.add_layer(l4); - m0.set_background(mapnik::color("skyblue")); - m2.set_background(mapnik::color("skyblue")); + m0.add_layer(l4); + m0.set_background(mapnik::color("skyblue")); + m2.set_background(mapnik::color("skyblue")); - auto m1 = m0; //copy + auto m1 = m0; //copy - REQUIRE( (m0 == m1) ); - REQUIRE( (m0 != m2) ); + REQUIRE( (m0 == m1) ); + REQUIRE( (m0 != m2) ); - m2 = m1; // copy - REQUIRE( (m2 == m1) ); - m2 = std::move(m1); - REQUIRE( (m2 == m0) ); - REQUIRE( (m1 != m0) ); + m2 = m1; // copy + REQUIRE( (m2 == m1) ); + m2 = std::move(m1); + REQUIRE( (m2 == m0) ); + REQUIRE( (m1 != m0) ); - REQUIRE( (m0 == m2) ); + REQUIRE( (m0 == m2) ); + } } catch (std::exception const & ex) { diff --git a/test/unit/core/exceptions_test.cpp b/test/unit/core/exceptions_test.cpp index 332460082..46abcce55 100644 --- a/test/unit/core/exceptions_test.cpp +++ b/test/unit/core/exceptions_test.cpp @@ -62,7 +62,6 @@ SECTION("handling") { std::string csv_plugin("./plugins/input/csv.input"); if (mapnik::util::exists(csv_plugin)) { try { - mapnik::datasource_cache::instance().register_datasource(csv_plugin); mapnik::parameters p; p["type"]="csv"; p["inline"]="x,y\n0,0"; @@ -88,7 +87,6 @@ SECTION("handling") { std::string shape_plugin("./plugins/input/shape.input"); if (mapnik::util::exists(shape_plugin)) { try { - mapnik::datasource_cache::instance().register_datasource(shape_plugin); mapnik::parameters p2; p2["type"]="shape"; p2["file"]="foo"; diff --git a/test/unit/datasource/geojson.cpp b/test/unit/datasource/geojson.cpp new file mode 100644 index 000000000..9bcacca95 --- /dev/null +++ b/test/unit/datasource/geojson.cpp @@ -0,0 +1,122 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2015 Artem Pavlenko + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ + +#include "catch.hpp" + +#include +#include +#include +#include +#include + +TEST_CASE("geojson") { + + std::string geojson_plugin("./plugins/input/geojson.input"); + if (mapnik::util::exists(geojson_plugin)) + { + SECTION("json feature cache-feature=\"true\"") + { + // Create datasource + mapnik::parameters params; + params["type"] = "geojson"; + params["file"] = "./test/data/json/feature.json"; + params["cache-features"] = true; + auto ds = mapnik::datasource_cache::instance().create(params); + REQUIRE(bool(ds)); + auto fields = ds->get_descriptor().get_descriptors(); + mapnik::query query(ds->envelope()); + for (auto const &field : fields) + { + query.add_property_name(field.get_name()); + } + auto features = ds->features(query); + REQUIRE(features != nullptr); + auto feature = features->next(); + REQUIRE(feature != nullptr); + } + + SECTION("json feature cache-feature=\"false\"") + { + mapnik::parameters params; + params["type"] = "geojson"; + params["file"] = "./test/data/json/feature.json"; + params["cache-features"] = false; + auto ds = mapnik::datasource_cache::instance().create(params); + REQUIRE(bool(ds)); + auto fields = ds->get_descriptor().get_descriptors(); + mapnik::query query(ds->envelope()); + for (auto const &field : fields) + { + query.add_property_name(field.get_name()); + } + auto features = ds->features(query); + REQUIRE(features != nullptr); + auto feature = features->next(); + REQUIRE(feature != nullptr); + } + + SECTION("json extra properties cache-feature=\"true\"") + { + // Create datasource + mapnik::parameters params; + params["type"] = "geojson"; + params["file"] = "./test/data/json/feature_collection_extra_properties.json"; + params["cache-features"] = true; + auto ds = mapnik::datasource_cache::instance().create(params); + REQUIRE(bool(ds)); + auto fields = ds->get_descriptor().get_descriptors(); + mapnik::query query(ds->envelope()); + for (auto const &field : fields) + { + query.add_property_name(field.get_name()); + } + auto features = ds->features(query); + REQUIRE(features != nullptr); + auto feature = features->next(); + REQUIRE(feature != nullptr); + REQUIRE(feature->envelope() == mapnik::box2d(123,456,123,456)); + } + + SECTION("json extra properties cache-feature=\"false\"") + { + // Create datasource + mapnik::parameters params; + params["type"] = "geojson"; + params["file"] = "./test/data/json/feature_collection_extra_properties.json"; + params["cache-features"] = false; + auto ds = mapnik::datasource_cache::instance().create(params); + REQUIRE(bool(ds)); + auto fields = ds->get_descriptor().get_descriptors(); + mapnik::query query(ds->envelope()); + for (auto const &field : fields) + { + query.add_property_name(field.get_name()); + } + auto features = ds->features(query); + REQUIRE(features != nullptr); + auto feature = features->next(); + REQUIRE(feature != nullptr); + REQUIRE(feature->envelope() == mapnik::box2d(123,456,123,456)); + } + + } +} diff --git a/test/unit/imaging/image_painted_test.cpp b/test/unit/imaging/image_painted_test.cpp index b567d5e6a..5d0b403f7 100644 --- a/test/unit/imaging/image_painted_test.cpp +++ b/test/unit/imaging/image_painted_test.cpp @@ -8,6 +8,7 @@ #include #include #include +#include TEST_CASE("image") { @@ -17,47 +18,49 @@ SECTION("painting") { try { - datasource_cache::instance().register_datasources("plugins/input/csv.input"); - - Map m(256, 256); - - feature_type_style lines_style; + std::string csv_plugin("./plugins/input/csv.input"); + if (mapnik::util::exists(csv_plugin)) { - rule r; - line_symbolizer line_sym; - r.append(std::move(line_sym)); - lines_style.add_rule(std::move(r)); + Map m(256, 256); + + feature_type_style lines_style; + { + rule r; + line_symbolizer line_sym; + r.append(std::move(line_sym)); + lines_style.add_rule(std::move(r)); + } + m.insert_style("lines", std::move(lines_style)); + + feature_type_style markers_style; + { + rule r; + r.set_filter(parse_expression("False")); + markers_symbolizer mark_sym; + r.append(std::move(mark_sym)); + markers_style.add_rule(std::move(r)); + } + m.insert_style("markers", std::move(markers_style)); + + parameters p; + p["type"] = "csv"; + p["separator"] = "|"; + p["inline"] = "wkt\nLINESTRING(-10 0, 0 20, 10 0, 15 5)"; + + layer lyr("layer"); + lyr.set_datasource(datasource_cache::instance().create(p)); + lyr.add_style("lines"); + lyr.add_style("markers"); + m.add_layer(lyr); + + m.zoom_all(); + + image_rgba8 image(m.width(), m.height()); + agg_renderer ren(m, image); + ren.apply(); + + REQUIRE(image.painted() == true); } - m.insert_style("lines", std::move(lines_style)); - - feature_type_style markers_style; - { - rule r; - r.set_filter(parse_expression("False")); - markers_symbolizer mark_sym; - r.append(std::move(mark_sym)); - markers_style.add_rule(std::move(r)); - } - m.insert_style("markers", std::move(markers_style)); - - parameters p; - p["type"] = "csv"; - p["separator"] = "|"; - p["inline"] = "wkt\nLINESTRING(-10 0, 0 20, 10 0, 15 5)"; - - layer lyr("layer"); - lyr.set_datasource(datasource_cache::instance().create(p)); - lyr.add_style("lines"); - lyr.add_style("markers"); - m.add_layer(lyr); - - m.zoom_all(); - - image_rgba8 image(m.width(), m.height()); - agg_renderer ren(m, image); - ren.apply(); - - REQUIRE(image.painted() == true); } catch (std::exception const & ex) { diff --git a/test/unit/run.cpp b/test/unit/run.cpp index 15ad0991a..8ae4ddc73 100644 --- a/test/unit/run.cpp +++ b/test/unit/run.cpp @@ -1,10 +1,14 @@ #define CATCH_CONFIG_RUNNER #include "catch.hpp" +#include + #include "cleanup.hpp" // run_cleanup() int main (int argc, char* const argv[]) { + mapnik::datasource_cache::instance().register_datasources("plugins/input/"); + int result = Catch::Session().run( argc, argv ); testing::run_cleanup(); diff --git a/test/unit/svg/svg_parser_test.cpp b/test/unit/svg/svg_parser_test.cpp new file mode 100644 index 000000000..a390bb4ad --- /dev/null +++ b/test/unit/svg/svg_parser_test.cpp @@ -0,0 +1,758 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2015 Artem Pavlenko + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ + +#include "catch.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace detail { + +template +struct vertex_equal +{ + template + bool operator() (T const& lhs, T const& rhs) const + { + static const double eps = 1.0 / std::pow(10,N); + return (std::fabs(std::get<0>(lhs) - std::get<0>(rhs)) < eps) + && (std::fabs(std::get<1>(lhs) - std::get<1>(rhs)) < eps) + && std::get<2>(lhs) == std::get<2>(rhs); + } +}; +} + +TEST_CASE("SVG parser") { + + SECTION("SVG i/o") + { + std::string svg_name("FAIL"); + std::shared_ptr marker = mapnik::marker_cache::instance().find(svg_name, false); + REQUIRE(marker); + REQUIRE(marker->is()); + } + + SECTION("SVG::parse i/o") + { + std::string svg_name("FAIL"); + + using namespace mapnik::svg; + mapnik::svg_storage_type path; + vertex_stl_adapter stl_storage(path.source()); + svg_path_adapter svg_path(stl_storage); + svg_converter_type svg(svg_path, path.attributes()); + svg_parser p(svg); + + if (!p.parse(svg_name)) + { + auto const& errors = p.error_messages(); + REQUIRE(errors.size() == 1); + REQUIRE(errors[0] == "Unable to open 'FAIL'"); + } + } + + SECTION("SVG::parse_from_string syntax error") + { + std::string svg_name("./test/data/svg/invalid.svg"); + std::ifstream in(svg_name.c_str()); + std::string svg_str((std::istreambuf_iterator(in)), + std::istreambuf_iterator()); + + using namespace mapnik::svg; + mapnik::svg_storage_type path; + vertex_stl_adapter stl_storage(path.source()); + svg_path_adapter svg_path(stl_storage); + svg_converter_type svg(svg_path, path.attributes()); + svg_parser p(svg); + + if (!p.parse_from_string(svg_str)) + { + auto const& errors = p.error_messages(); + REQUIRE(errors.size() == 1); + REQUIRE(errors[0] == "Unable to parse '\n\n'"); + } + } + + SECTION("SVG::parse_from_string syntax error") + { + std::string svg_name("./test/data/svg/invalid.svg"); + + using namespace mapnik::svg; + mapnik::svg_storage_type path; + vertex_stl_adapter stl_storage(path.source()); + svg_path_adapter svg_path(stl_storage); + svg_converter_type svg(svg_path, path.attributes()); + svg_parser p(svg); + + if (!p.parse(svg_name)) + { + auto const& errors = p.error_messages(); + REQUIRE(errors.size() == 1); + REQUIRE(errors[0] == "svg_parser::parse - Unable to parse './test/data/svg/invalid.svg'"); + } + } + + SECTION("SVG parser color ") + { + + std::string svg_name("./test/data/svg/color_fail.svg"); + std::ifstream in(svg_name.c_str()); + std::string svg_str((std::istreambuf_iterator(in)), + std::istreambuf_iterator()); + + using namespace mapnik::svg; + mapnik::svg_storage_type path; + vertex_stl_adapter stl_storage(path.source()); + svg_path_adapter svg_path(stl_storage); + svg_converter_type svg(svg_path, path.attributes()); + svg_parser p(svg); + + if (!p.parse_from_string(svg_str)) + { + auto const& errors = p.error_messages(); + REQUIRE(errors.size() == 3); + REQUIRE(errors[0] == "Failed to parse color: \"fail\""); + REQUIRE(errors[1] == "Failed to parse double: \"fail\""); + REQUIRE(errors[2] == "Failed to parse color: \"fail\""); + } + } + + SECTION("SVG - cope with erroneous geometries") + { + std::string svg_name("./test/data/svg/errors.svg"); + std::ifstream in(svg_name.c_str()); + std::string svg_str((std::istreambuf_iterator(in)), + std::istreambuf_iterator()); + + using namespace mapnik::svg; + mapnik::svg_storage_type path; + vertex_stl_adapter stl_storage(path.source()); + svg_path_adapter svg_path(stl_storage); + svg_converter_type svg(svg_path, path.attributes()); + svg_parser p(svg); + + if (!p.parse_from_string(svg_str)) + { + auto const& errors = p.error_messages(); + REQUIRE(errors.size() == 14); + REQUIRE(errors[0] == "parse_rect: Invalid width"); + REQUIRE(errors[1] == "Failed to parse double: \"FAIL\""); + REQUIRE(errors[2] == "parse_rect: Invalid height"); + REQUIRE(errors[3] == "parse_rect: Invalid rx"); + REQUIRE(errors[4] == "parse_rect: Invalid ry"); + REQUIRE(errors[5] == "unable to parse invalid svg "); + REQUIRE(errors[6] == "unable to parse invalid svg with id 'fail-path'"); + REQUIRE(errors[7] == "unable to parse invalid svg with id 'fail-path'"); + REQUIRE(errors[8] == "parse_circle: Invalid radius"); + REQUIRE(errors[9] == "Failed to parse 'points'"); + REQUIRE(errors[10] == "Failed to parse 'points'"); + REQUIRE(errors[11] == "parse_ellipse: Invalid rx"); + REQUIRE(errors[12] == "parse_ellipse: Invalid ry"); + REQUIRE(errors[13] == "parse_rect: Invalid height"); + } + } + + SECTION("SVG parser double % ") + { + + std::string svg_name("./test/data/svg/gradient-radial-error.svg"); + std::ifstream in(svg_name.c_str()); + std::string svg_str((std::istreambuf_iterator(in)), + std::istreambuf_iterator()); + + using namespace mapnik::svg; + mapnik::svg_storage_type path; + vertex_stl_adapter stl_storage(path.source()); + svg_path_adapter svg_path(stl_storage); + svg_converter_type svg(svg_path, path.attributes()); + svg_parser p(svg); + + if (!p.parse_from_string(svg_str)) + { + auto const& errors = p.error_messages(); + REQUIRE(errors.size() == 1); + REQUIRE(errors[0] == "Failed to parse double (optional %) from FAIL"); + } + } + + SECTION("SVG parser display=none") + { + + std::string svg_name("./test/data/svg/invisible.svg"); + std::shared_ptr marker = mapnik::marker_cache::instance().find(svg_name, false); + REQUIRE(marker); + REQUIRE(marker->is()); + mapnik::marker_svg const& svg = mapnik::util::get(*marker); + auto bbox = svg.bounding_box(); + REQUIRE(bbox == mapnik::box2d(0, 0, 1, 1)); + auto storage = svg.get_data(); + REQUIRE(storage); + mapnik::svg::vertex_stl_adapter stl_storage(storage->source()); + mapnik::svg::svg_path_adapter path(stl_storage); + double x,y; + REQUIRE(path.vertex(&x,&y) == mapnik::SEG_END); + } + + SECTION("SVG parser stroke-linecap=square") + { + + std::string svg_name("./test/data/svg/stroke-linecap-square.svg"); + std::shared_ptr marker = mapnik::marker_cache::instance().find(svg_name, false); + REQUIRE(marker); + REQUIRE(marker->is()); + mapnik::marker_svg const& svg = mapnik::util::get(*marker); + auto bbox = svg.bounding_box(); + REQUIRE(bbox == mapnik::box2d(5, 60, 220, 60)); + auto storage = svg.get_data(); + REQUIRE(storage); + mapnik::svg::vertex_stl_adapter stl_storage(storage->source()); + mapnik::svg::svg_path_adapter path(stl_storage); + + auto const& attrs = storage->attributes(); + agg::line_cap_e expected_cap(agg::square_cap); + REQUIRE(attrs.size() == 1 ); + REQUIRE(attrs[0].line_cap == expected_cap); + + double x,y; + unsigned cmd; + std::vector> vec; + while ((cmd = path.vertex(&x,&y)) != mapnik::SEG_END) + { + vec.emplace_back(x, y, cmd); + } + std::vector> expected = { std::make_tuple(5, 60, 1), + std::make_tuple(220, 60, 2) }; + REQUIRE(std::equal(expected.begin(),expected.end(), vec.begin())); + } + + SECTION("SVG ") + { + // + std::string svg_name("./test/data/svg/rect.svg"); + std::shared_ptr marker = mapnik::marker_cache::instance().find(svg_name, false); + REQUIRE(marker); + REQUIRE(marker->is()); + mapnik::marker_svg const& svg = mapnik::util::get(*marker); + auto bbox = svg.bounding_box(); + REQUIRE(bbox == mapnik::box2d(0, 0, 20, 15)); + auto storage = svg.get_data(); + REQUIRE(storage); + mapnik::svg::vertex_stl_adapter stl_storage(storage->source()); + mapnik::svg::svg_path_adapter path(stl_storage); + double x,y; + unsigned cmd; + std::vector> vec; + while ((cmd = path.vertex(&x,&y)) != mapnik::SEG_END) + { + vec.emplace_back(x, y, cmd); + //std::cerr << x << "," << y << " cmd=" << cmd << std::endl; + } + std::vector> expected = { std::make_tuple(0, 0, 1), + std::make_tuple(20, 0, 2), + std::make_tuple(20, 15, 2), + std::make_tuple(0, 15, 2), + std::make_tuple(0, 0, 79) }; + REQUIRE(std::equal(expected.begin(),expected.end(), vec.begin())); + } + SECTION("SVG rounded ") + { + // + std::string svg_name("./test/data/svg/rounded_rect.svg"); + std::shared_ptr marker = mapnik::marker_cache::instance().find(svg_name, false); + REQUIRE(marker); + REQUIRE(marker->is()); + mapnik::marker_svg const& svg = mapnik::util::get(*marker); + auto bbox = svg.bounding_box(); + REQUIRE(bbox == mapnik::box2d(0, 0, 20, 15)); + auto storage = svg.get_data(); + REQUIRE(storage); + mapnik::svg::vertex_stl_adapter stl_storage(storage->source()); + mapnik::svg::svg_path_adapter path(stl_storage); + double x,y; + unsigned cmd; + std::vector> vec; + + while ((cmd = path.vertex(&x,&y)) != mapnik::SEG_END) + { + vec.emplace_back(x, y, cmd); + } + + std::vector> expected = {std::make_tuple(0, 5,1), + std::make_tuple(0.481856, 2.85842,2), + std::make_tuple(1.83455, 1.12961,2), + std::make_tuple(3.79736, 0.146789,2), + std::make_tuple(5, 0,2), + std::make_tuple(15, 0,2), + std::make_tuple(17.1416, 0.481856,2), + std::make_tuple(18.8704, 1.83455,2), + std::make_tuple(19.8532, 3.79736,2), + std::make_tuple(20, 5,2), + std::make_tuple(20, 10,2), + std::make_tuple(19.5181, 12.1416,2), + std::make_tuple(18.1654, 13.8704,2), + std::make_tuple(16.2026, 14.8532,2), + std::make_tuple(15, 15,2), + std::make_tuple(5, 15,2), + std::make_tuple(2.85842, 14.5181,2), + std::make_tuple(1.12961, 13.1654,2), + std::make_tuple(0.146789, 11.2026,2), + std::make_tuple(0, 10,2), + std::make_tuple(0, 10,95)}; + + REQUIRE(std::equal(expected.begin(),expected.end(), vec.begin(),detail::vertex_equal<3>())); + } + + SECTION("SVG rounded s missing rx or ry") + { + std::string svg_name("./test/data/svg/rounded_rect-missing-one-radius.svg"); + std::shared_ptr marker = mapnik::marker_cache::instance().find(svg_name, false); + REQUIRE(marker); + REQUIRE(marker->is()); + mapnik::marker_svg const& svg = mapnik::util::get(*marker); + auto bbox = svg.bounding_box(); + REQUIRE(bbox == mapnik::box2d(0, 0, 20, 15)); + auto storage = svg.get_data(); + REQUIRE(storage); + mapnik::svg::vertex_stl_adapter stl_storage(storage->source()); + mapnik::svg::svg_path_adapter path(stl_storage); + double x,y; + unsigned cmd; + std::vector> vec; + + while ((cmd = path.vertex(&x,&y)) != mapnik::SEG_END) + { + vec.emplace_back(x, y, cmd); + } + std::vector> expected = {std::make_tuple(0, 5,1), + std::make_tuple(0.481856, 2.85842,2), + std::make_tuple(1.83455, 1.12961,2), + std::make_tuple(3.79736, 0.146789,2), + std::make_tuple(5, 0,2), + std::make_tuple(15, 0,2), + std::make_tuple(17.1416, 0.481856,2), + std::make_tuple(18.8704, 1.83455,2), + std::make_tuple(19.8532, 3.79736,2), + std::make_tuple(20, 5,2), + std::make_tuple(20, 10,2), + std::make_tuple(19.5181, 12.1416,2), + std::make_tuple(18.1654, 13.8704,2), + std::make_tuple(16.2026, 14.8532,2), + std::make_tuple(15, 15,2), + std::make_tuple(5, 15,2), + std::make_tuple(2.85842, 14.5181,2), + std::make_tuple(1.12961, 13.1654,2), + std::make_tuple(0.146789, 11.2026,2), + std::make_tuple(0, 10,2), + std::make_tuple(0, 10,95)}; + + REQUIRE(std::equal(expected.begin(),expected.end(), vec.begin(),detail::vertex_equal<3>())); + } + + SECTION("SVG beveled ") + { + std::string svg_name("./test/data/svg/stroke-linejoin-bevel.svg"); + std::shared_ptr marker = mapnik::marker_cache::instance().find(svg_name, false); + REQUIRE(marker); + REQUIRE(marker->is()); + mapnik::marker_svg const& svg = mapnik::util::get(*marker); + auto bbox = svg.bounding_box(); + REQUIRE(bbox == mapnik::box2d(10, 10, 30, 25)); + auto storage = svg.get_data(); + REQUIRE(storage); + mapnik::svg::vertex_stl_adapter stl_storage(storage->source()); + + auto const& attrs = storage->attributes(); + agg::line_join_e expected_join(agg::bevel_join); + REQUIRE(attrs.size() == 1 ); + REQUIRE(attrs[0].line_join == expected_join); + } + + SECTION("SVG ") + { + // + std::string svg_name("./test/data/svg/line.svg"); + std::shared_ptr marker = mapnik::marker_cache::instance().find(svg_name, false); + REQUIRE(marker); + REQUIRE(marker->is()); + mapnik::marker_svg const& svg = mapnik::util::get(*marker); + auto bbox = svg.bounding_box(); + REQUIRE(bbox == mapnik::box2d(1.0,1.0,1199.0,399.0)); + auto storage = svg.get_data(); + REQUIRE(storage); + mapnik::svg::vertex_stl_adapter stl_storage(storage->source()); + mapnik::svg::svg_path_adapter path(stl_storage); + double x,y; + unsigned cmd; + std::vector> vec; + std::size_t num_vertices = path.total_vertices(); + for (std::size_t i = 0; i < num_vertices; ++i) + { + cmd = path.vertex(&x,&y); + vec.emplace_back(x, y, cmd); + } + std::vector> expected = {std::make_tuple(1, 1, 1), + std::make_tuple(1199, 1, 2), + std::make_tuple(1199, 399, 2), + std::make_tuple(1, 399, 2), + std::make_tuple(1, 1, 79), + std::make_tuple(0, 0, 0), + std::make_tuple(100, 300, 1), + std::make_tuple(300, 100, 2), + std::make_tuple(0, 0, 0), + std::make_tuple(300, 300, 1), + std::make_tuple(500, 100, 2), + std::make_tuple(0, 0, 0), + std::make_tuple(500, 300, 1), + std::make_tuple(700, 100, 2), + std::make_tuple(0, 0, 0), + std::make_tuple(700, 300, 1), + std::make_tuple(900, 100, 2), + std::make_tuple(0, 0, 0), + std::make_tuple(900, 300, 1), + std::make_tuple(1100, 100, 2)}; + + REQUIRE(std::equal(expected.begin(),expected.end(), vec.begin())); + } + + SECTION("SVG ") + { + // + std::string svg_name("./test/data/svg/polyline.svg"); + std::shared_ptr marker = mapnik::marker_cache::instance().find(svg_name, false); + REQUIRE(marker); + REQUIRE(marker->is()); + mapnik::marker_svg const& svg = mapnik::util::get(*marker); + auto bbox = svg.bounding_box(); + REQUIRE(bbox == mapnik::box2d(1.0,1.0,1199.0,399.0)); + auto storage = svg.get_data(); + REQUIRE(storage); + mapnik::svg::vertex_stl_adapter stl_storage(storage->source()); + mapnik::svg::svg_path_adapter path(stl_storage); + double x,y; + unsigned cmd; + std::vector> vec; + std::size_t num_vertices = path.total_vertices(); + for (std::size_t i = 0; i < num_vertices; ++i) + { + cmd = path.vertex(&x,&y); + vec.emplace_back(x, y, cmd); + } + + std::vector> expected = {std::make_tuple(1, 1, 1), + std::make_tuple(1199, 1, 2), + std::make_tuple(1199, 399, 2), + std::make_tuple(1, 399, 2), + std::make_tuple(1, 1, 79), + std::make_tuple(0, 0, 0), + std::make_tuple(50, 375, 1), + std::make_tuple(150, 375, 2), + std::make_tuple(150, 325, 2), + std::make_tuple(250, 325, 2), + std::make_tuple(250, 375, 2), + std::make_tuple(350, 375, 2), + std::make_tuple(350, 250, 2), + std::make_tuple(450, 250, 2), + std::make_tuple(450, 375, 2), + std::make_tuple(550, 375, 2), + std::make_tuple(550, 175, 2), + std::make_tuple(650, 175, 2), + std::make_tuple(650, 375, 2), + std::make_tuple(750, 375, 2), + std::make_tuple(750, 100, 2), + std::make_tuple(850, 100, 2), + std::make_tuple(850, 375, 2), + std::make_tuple(950, 375, 2), + std::make_tuple(950, 25, 2), + std::make_tuple(1050, 25, 2), + std::make_tuple(1050, 375, 2), + std::make_tuple(1150, 375, 2)}; + + REQUIRE(std::equal(expected.begin(),expected.end(), vec.begin())); + } + + SECTION("SVG ") + { + // + std::string svg_name("./test/data/svg/polygon.svg"); + std::shared_ptr marker = mapnik::marker_cache::instance().find(svg_name, false); + REQUIRE(marker); + REQUIRE(marker->is()); + mapnik::marker_svg const& svg = mapnik::util::get(*marker); + auto bbox = svg.bounding_box(); + REQUIRE(bbox == mapnik::box2d(1.0,1.0,1199.0,399.0)); + auto storage = svg.get_data(); + REQUIRE(storage); + mapnik::svg::vertex_stl_adapter stl_storage(storage->source()); + mapnik::svg::svg_path_adapter path(stl_storage); + double x,y; + unsigned cmd; + std::vector> vec; + std::size_t num_vertices = path.total_vertices(); + for (std::size_t i = 0; i < num_vertices; ++i) + { + cmd = path.vertex(&x,&y); + vec.emplace_back(x, y, cmd); + } + + std::vector> expected = {std::make_tuple(1, 1, 1), + std::make_tuple(1199, 1, 2), + std::make_tuple(1199, 399, 2), + std::make_tuple(1, 399, 2), + std::make_tuple(1, 1, 79), + std::make_tuple(0, 0, 0), + std::make_tuple(350, 75, 1), + std::make_tuple(379, 161, 2), + std::make_tuple(469, 161, 2), + std::make_tuple(397, 215, 2), + std::make_tuple(423, 301, 2), + std::make_tuple(350, 250, 2), + std::make_tuple(277, 301, 2), + std::make_tuple(303, 215, 2), + std::make_tuple(231, 161, 2), + std::make_tuple(321, 161, 2), + std::make_tuple(350, 75, 79), + std::make_tuple(0, 0, 0), + std::make_tuple(850, 75, 1), + std::make_tuple(958, 137.5, 2), + std::make_tuple(958, 262.5, 2), + std::make_tuple(850, 325, 2), + std::make_tuple(742, 262.6, 2), + std::make_tuple(742, 137.5, 2), + std::make_tuple(850, 75, 79)}; + + REQUIRE(std::equal(expected.begin(),expected.end(), vec.begin())); + } + + SECTION("SVG ") + { + // + std::string svg_name("./test/data/svg/gradient.svg"); + std::shared_ptr marker = mapnik::marker_cache::instance().find(svg_name, false); + REQUIRE(marker); + REQUIRE(marker->is()); + mapnik::marker_svg const& svg = mapnik::util::get(*marker); + auto bbox = svg.bounding_box(); + REQUIRE(bbox == mapnik::box2d(1.0,1.0,799.0,599.0)); + auto storage = svg.get_data(); + REQUIRE(storage); + mapnik::svg::vertex_stl_adapter stl_storage(storage->source()); + mapnik::svg::svg_path_adapter path(stl_storage); + double x,y; + unsigned cmd; + std::vector> vec; + std::size_t num_vertices = path.total_vertices(); + + for (std::size_t i = 0; i < num_vertices; ++i) + { + cmd = path.vertex(&x,&y); + vec.emplace_back(x, y, cmd); + } + + std::vector> expected = {std::make_tuple(1, 1, 1), + std::make_tuple(799, 1, 2), + std::make_tuple(799, 599, 2), + std::make_tuple(1, 599, 2), + std::make_tuple(1, 1, 79), + std::make_tuple(0, 0, 0), + std::make_tuple(100, 100, 1), + std::make_tuple(700, 100, 2), + std::make_tuple(700, 300, 2), + std::make_tuple(100, 300, 2), + std::make_tuple(100, 100, 79), + std::make_tuple(0, 0, 0), + std::make_tuple(100, 320, 1), + std::make_tuple(700, 320, 2), + std::make_tuple(700, 520, 2), + std::make_tuple(100, 520, 2), + std::make_tuple(100, 320, 79)}; + + REQUIRE(std::equal(expected.begin(),expected.end(), vec.begin())); + } + + SECTION("SVG missing def") + { + std::string svg_name("./test/data/svg/gradient-nodef.svg"); + using namespace mapnik::svg; + mapnik::svg_storage_type path; + vertex_stl_adapter stl_storage(path.source()); + svg_path_adapter svg_path(stl_storage); + svg_converter_type svg(svg_path, path.attributes()); + svg_parser p(svg); + REQUIRE(!p.parse(svg_name)); + auto const& errors = p.error_messages(); + REQUIRE(errors.size() == 2); + REQUIRE(errors[0] == "Failed to find gradient fill: MyGradient"); + REQUIRE(errors[1] == "Failed to find gradient stroke: MyGradient"); + } + + SECTION("SVG missing id") + { + + std::string svg_name("./test/data/svg/gradient-no-id.svg"); + std::ifstream in(svg_name.c_str()); + std::string svg_str((std::istreambuf_iterator(in)), + std::istreambuf_iterator()); + + using namespace mapnik::svg; + mapnik::svg_storage_type path; + vertex_stl_adapter stl_storage(path.source()); + svg_path_adapter svg_path(stl_storage); + svg_converter_type svg(svg_path, path.attributes()); + svg_parser p(svg); + + if (!p.parse_from_string(svg_str)) + { + auto const& errors = p.error_messages(); + REQUIRE(errors.size() == 2); + REQUIRE(errors[0] == "Failed to find gradient fill: MyGradient"); + REQUIRE(errors[1] == "Failed to find gradient stroke: MyGradient"); + } + } + + SECTION("SVG missing inheritance") + { + // + std::string svg_name("./test/data/svg/gradient-inherit.svg"); + std::shared_ptr marker = mapnik::marker_cache::instance().find(svg_name, false); + REQUIRE(marker); + REQUIRE(marker->is()); + mapnik::marker_svg const& svg = mapnik::util::get(*marker); + auto bbox = svg.bounding_box(); + REQUIRE(bbox == mapnik::box2d(1.0,1.0,699.0,199.0)); + auto storage = svg.get_data(); + REQUIRE(storage); + mapnik::svg::vertex_stl_adapter stl_storage(storage->source()); + + auto const& attrs = storage->attributes(); + REQUIRE(attrs.size() == 3 ); + REQUIRE(attrs[1].fill_gradient == attrs[2].fill_gradient); + + mapnik::svg::svg_path_adapter path(stl_storage); + double x,y; + unsigned cmd; + std::vector> vec; + std::size_t num_vertices = path.total_vertices(); + for (std::size_t i = 0; i < num_vertices; ++i) + { + cmd = path.vertex(&x,&y); + vec.emplace_back(x, y, cmd); + } + + std::vector> expected = {std::make_tuple(1, 1, 1), + std::make_tuple(699, 1, 2), + std::make_tuple(699, 199, 2), + std::make_tuple(1, 199, 2), + std::make_tuple(1, 1, 79), + std::make_tuple(0, 0, 0), + std::make_tuple(100, 50, 1), + std::make_tuple(300, 50, 2), + std::make_tuple(300, 150, 2), + std::make_tuple(100, 150, 2), + std::make_tuple(100, 50, 79), + std::make_tuple(0, 0, 0), + std::make_tuple(400, 50, 1), + std::make_tuple(600, 50, 2), + std::make_tuple(600, 150, 2), + std::make_tuple(400, 150, 2), + std::make_tuple(400, 50, 79)}; + + REQUIRE(std::equal(expected.begin(),expected.end(), vec.begin())); + } + + SECTION("SVG with transformations") + { + // + std::string svg_name("./test/data/svg/gradient-transform.svg"); + std::shared_ptr marker = mapnik::marker_cache::instance().find(svg_name, false); + REQUIRE(marker); + REQUIRE(marker->is()); + mapnik::marker_svg const& svg = mapnik::util::get(*marker); + auto bbox = svg.bounding_box(); + REQUIRE(bbox == mapnik::box2d(1.0,1.0,799.0,599.0)); + auto storage = svg.get_data(); + REQUIRE(storage); + + auto const& attrs = storage->attributes(); + REQUIRE(attrs.size() == 3 ); + REQUIRE(attrs[1].fill_gradient == attrs[2].fill_gradient); + REQUIRE(attrs[1].fill_gradient.get_gradient_type() == mapnik::RADIAL); + agg::trans_affine transform; + transform *= agg::trans_affine_translation(240,155); + REQUIRE(attrs[1].fill_gradient.get_transform() == transform); + } + + SECTION("SVG with xlink:href") + { + std::string svg_name("./test/data/svg/gradient-xhref.svg"); + std::shared_ptr marker = mapnik::marker_cache::instance().find(svg_name, false); + REQUIRE(marker); + REQUIRE(marker->is()); + mapnik::marker_svg const& svg = mapnik::util::get(*marker); + auto bbox = svg.bounding_box(); + REQUIRE(bbox == mapnik::box2d(20,20,460,230)); + auto storage = svg.get_data(); + REQUIRE(storage); + + auto const& attrs = storage->attributes(); + REQUIRE(attrs.size() == 2 ); + REQUIRE(attrs[0].fill_gradient.get_gradient_type() == mapnik::LINEAR); + REQUIRE(attrs[1].fill_gradient.get_gradient_type() == mapnik::LINEAR); + REQUIRE(attrs[1].fill_gradient.has_stop()); + } + + SECTION("SVG with radial percents") + { + std::string svg_name("./test/data/svg/gradient-radial-percents.svg"); + std::shared_ptr marker = mapnik::marker_cache::instance().find(svg_name, false); + REQUIRE(marker); + REQUIRE(marker->is()); + mapnik::marker_svg const& svg = mapnik::util::get(*marker); + auto bbox = svg.bounding_box(); + REQUIRE(bbox == mapnik::box2d(0,0,200,200)); + auto storage = svg.get_data(); + REQUIRE(storage); + + double x1, x2, y1, y2, r; + auto const& attrs = storage->attributes(); + REQUIRE(attrs.size() == 1 ); + REQUIRE(attrs[0].fill_gradient.get_gradient_type() == mapnik::RADIAL); + REQUIRE(attrs[0].fill_gradient.has_stop()); + attrs[0].fill_gradient.get_control_points(x1, y1, x2, y2, r); + REQUIRE(x1 == 0); + REQUIRE(y1 == 25); + REQUIRE(x2 == 10); + REQUIRE(y2 == 10); + REQUIRE(r == 75); + } +} diff --git a/test/unit/text/shaping.cpp b/test/unit/text/shaping.cpp new file mode 100644 index 000000000..2e2701b8f --- /dev/null +++ b/test/unit/text/shaping.cpp @@ -0,0 +1,24 @@ +#include "catch.hpp" +#include +#include +#include + +TEST_CASE("shapers compile") { + + mapnik::text_line line(0,0); + mapnik::text_itemizer itemizer; + std::map width_map; + double scale_factor = 1; + mapnik::font_library fl; + mapnik::freetype_engine::font_file_mapping_type font_file_mapping; + mapnik::freetype_engine::font_memory_cache_type font_memory_cache; + mapnik::face_manager fm(fl,font_file_mapping,font_memory_cache); + mapnik::harfbuzz_shaper::shape_text(line,itemizer, + width_map, + fm, + scale_factor); + mapnik::icu_shaper::shape_text(line,itemizer, + width_map, + fm, + scale_factor); +} \ No newline at end of file diff --git a/test/unit/vertex_adapter/offset_converter.cpp b/test/unit/vertex_adapter/offset_converter.cpp index 50740b7eb..b4b7d73c7 100644 --- a/test/unit/vertex_adapter/offset_converter.cpp +++ b/test/unit/vertex_adapter/offset_converter.cpp @@ -27,14 +27,22 @@ struct fake_path : fake_path(l.begin(), l.size()) { } - fake_path(std::vector const &v) - : fake_path(v.begin(), v.size()) { + fake_path(std::vector const &v, bool make_invalid = false) + : fake_path(v.begin(), v.size(), make_invalid) { } template - fake_path(Itr itr, size_t sz) { + fake_path(Itr itr, size_t sz, bool make_invalid = false) { size_t num_coords = sz >> 1; + if (make_invalid) + { + num_coords++; + } vertices_.reserve(num_coords); + if (make_invalid) + { + vertices_.push_back(std::make_tuple(0,0,mapnik::SEG_END)); + } for (size_t i = 0; i < num_coords; ++i) { double x = *itr++; @@ -70,11 +78,37 @@ double dist(double x0, double y0, double x1, double y1) return std::sqrt(dx*dx + dy*dy); } +void test_null_segment(double const &offset) +{ + fake_path path = {}; + mapnik::offset_converter off_path_new(path); + off_path_new.set_offset(offset); + double x0 = 0; + double y0 = 0; + REQUIRE(off_path_new.vertex(&x0, &y0) == mapnik::SEG_END); + REQUIRE(off_path_new.vertex(&x0, &y0) == mapnik::SEG_END); + REQUIRE(off_path_new.vertex(&x0, &y0) == mapnik::SEG_END); +} + +void test_invalid_segment(double const &offset) +{ + std::vector v_path = {1, 1, 1, 2}; + fake_path path(v_path, true); + mapnik::offset_converter off_path_new(path); + off_path_new.set_offset(offset); + double x0 = 0; + double y0 = 0; + REQUIRE(off_path_new.vertex(&x0, &y0) == mapnik::SEG_END); + REQUIRE(off_path_new.vertex(&x0, &y0) == mapnik::SEG_END); + REQUIRE(off_path_new.vertex(&x0, &y0) == mapnik::SEG_END); +} + + void test_simple_segment(double const &offset) { fake_path path = {0, 0, 1, 0}, off_path = {0, offset, 1, offset}; mapnik::offset_converter off_path_new(path); - off_path_new.set_offset(-offset); + off_path_new.set_offset(offset); double x0, y0, x1, y1; unsigned cmd0 = off_path_new.vertex(&x0, &y0); @@ -116,7 +150,7 @@ void test_straight_line(double const &offset) { fake_path path = {0, 0, 1, 0, 9, 0, 10, 0}, off_path = {0, offset, 1, offset, 9, offset, 10, offset}; mapnik::offset_converter off_path_new(path); - off_path_new.set_offset(-offset); + off_path_new.set_offset(offset); double x0, y0, x1, y1; unsigned cmd0 = off_path_new.vertex(&x0, &y0); @@ -166,7 +200,7 @@ void test_offset_curve(double const &offset) { fake_path path(pos), off_path(off_pos); mapnik::offset_converter off_path_new(path); - off_path_new.set_offset(-offset); + off_path_new.set_offset(offset); double x0, y0, x1, y1; unsigned cmd0 = off_path_new.vertex(&x0, &y0); @@ -222,7 +256,7 @@ void test_s_shaped_curve(double const &offset) { fake_path path(pos), off_path(off_pos); mapnik::offset_converter off_path_new(path); - off_path_new.set_offset(-offset); + off_path_new.set_offset(offset); double x0, y0, x1, y1; unsigned cmd0 = off_path_new.vertex(&x0, &y0); @@ -263,6 +297,41 @@ void test_s_shaped_curve(double const &offset) { TEST_CASE("offset converter") { +SECTION("null segment") { + try { + + std::vector offsets = { 1, -1 }; + for (double offset : offsets) { + // test simple straight line segment - should be easy to + // find the correspondance here. + offset_test::test_null_segment(offset); + } + } + catch (std::exception const& ex) + { + std::cerr << ex.what() << "\n"; + REQUIRE(false); + } +} + +SECTION("invalid segment") { + try { + + std::vector offsets = { 1, -1 }; + for (double offset : offsets) { + // test simple straight line segment - should be easy to + // find the correspondance here. + offset_test::test_invalid_segment(offset); + } + } + catch (std::exception const& ex) + { + std::cerr << ex.what() << "\n"; + REQUIRE(false); + } +} + + SECTION("simple segment") { try { diff --git a/test/visual/compare_images.hpp b/test/visual/compare_images.hpp deleted file mode 100644 index d227d7864..000000000 --- a/test/visual/compare_images.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/***************************************************************************** - * - * This file is part of Mapnik (c++ mapping toolkit) - * - * Copyright (C) 2015 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 COMPARE_IMAGES_HPP -#define COMPARE_IMAGES_HPP - -// stl -#include - -// mapnik -#include -#include - -namespace visual_tests -{ - -template -unsigned compare_images(Image const & actual, std::string const & reference) -{ - std::unique_ptr reader(mapnik::get_image_reader(reference, "png")); - if (!reader.get()) - { - throw mapnik::image_reader_exception("Failed to load: " + reference); - } - - mapnik::image_any ref_image_any = reader->read(0, 0, reader->width(), reader->height()); - Image const & reference_image = mapnik::util::get(ref_image_any); - - return mapnik::compare(actual, reference_image, 0, true); -} - -} - -#endif diff --git a/test/visual/config.hpp b/test/visual/config.hpp index 8353d4b2e..0dac1d983 100644 --- a/test/visual/config.hpp +++ b/test/visual/config.hpp @@ -26,6 +26,7 @@ // stl #include #include +#include // boost #include @@ -35,21 +36,23 @@ namespace visual_tests struct map_size { - map_size(int _width, int _height) : width(_width), height(_height) { } + map_size(std::size_t _width, std::size_t _height) : width(_width), height(_height) { } map_size() { } - unsigned width = 0; - unsigned height = 0; + std::size_t width = 0; + std::size_t height = 0; }; struct config { config() : status(true), scales({ 1.0, 2.0 }), - sizes({ { 500, 100 } }) { } + sizes({ { 500, 100 } }), + tiles({ { 1, 1 } }) { } bool status; std::vector scales; std::vector sizes; + std::vector tiles; }; enum result_state : std::uint8_t @@ -66,11 +69,13 @@ struct result result_state state; std::string renderer_name; map_size size; + map_size tiles; double scale_factor; boost::filesystem::path actual_image_path; boost::filesystem::path reference_image_path; std::string error_message; unsigned diff; + std::chrono::high_resolution_clock::duration duration; }; using result_list = std::vector; diff --git a/test/visual/renderer.hpp b/test/visual/renderer.hpp index f097dac2b..6ad979165 100644 --- a/test/visual/renderer.hpp +++ b/test/visual/renderer.hpp @@ -23,15 +23,16 @@ #ifndef RENDERER_HPP #define RENDERER_HPP -#include "compare_images.hpp" - // stl #include #include #include +#include // mapnik #include +#include +#include #include #if defined(GRID_RENDERER) #include @@ -56,10 +57,20 @@ struct renderer_base using image_type = ImageType; static constexpr const char * ext = ".png"; + static constexpr const bool support_tiles = true; unsigned compare(image_type const & actual, boost::filesystem::path const& reference) const { - return compare_images(actual, reference.string()); + std::unique_ptr reader(mapnik::get_image_reader(reference.string(), "png")); + if (!reader.get()) + { + throw mapnik::image_reader_exception("Failed to load: " + reference.string()); + } + + mapnik::image_any ref_image_any = reader->read(0, 0, reader->width(), reader->height()); + ImageType const & reference_image = mapnik::util::get(ref_image_any); + + return mapnik::compare(actual, reference_image, 0, true); } void save(image_type const & image, boost::filesystem::path const& path) const @@ -106,6 +117,7 @@ struct svg_renderer : 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 { @@ -125,7 +137,7 @@ struct svg_renderer : renderer_base } std::string expected(std::istreambuf_iterator(stream.rdbuf()),(std::istreambuf_iterator())); stream.close(); - return std::fabs(actual.size() - expected.size()); + 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 @@ -191,19 +203,76 @@ struct grid_renderer : renderer_base }; #endif +template +void set_rectangle(T const & src, T & dst, std::size_t x, std::size_t y) +{ + mapnik::box2d ext0(0, 0, dst.width(), dst.height()); + mapnik::box2d ext1(x, y, x + src.width(), y + src.height()); + + if (ext0.intersects(ext1)) + { + mapnik::box2d box = ext0.intersect(ext1); + for (std::size_t pix_y = box.miny(); pix_y < static_cast(box.maxy()); ++pix_y) + { + typename T::pixel_type * row_to = dst.get_row(pix_y); + typename T::pixel_type const * row_from = src.get_row(pix_y - y); + + for (std::size_t pix_x = box.minx(); pix_x < static_cast(box.maxx()); ++pix_x) + { + row_to[pix_x] = row_from[pix_x - x]; + } + } + } +} + template class renderer { public: + using renderer_type = Renderer; + using image_type = typename Renderer::image_type; + renderer(boost::filesystem::path const & _output_dir, boost::filesystem::path const & _reference_dir, bool _overwrite) : ren(), output_dir(_output_dir), reference_dir(_reference_dir), overwrite(_overwrite) { } - result test(std::string const & name, mapnik::Map const & map, double scale_factor) const + image_type render(mapnik::Map const & map, double scale_factor) const { - typename Renderer::image_type image(ren.render(map, scale_factor)); - boost::filesystem::path reference = reference_dir / image_file_name(name, map.width(), map.height(), scale_factor, true, Renderer::ext); + return ren.render(map, scale_factor); + } + + image_type render(mapnik::Map & map, double scale_factor, map_size const & tiles) const + { + mapnik::box2d box = map.get_current_extent(); + image_type image(map.width(), map.height()); + map.resize(image.width() / tiles.width, image.height() / tiles.height); + double tile_box_width = box.width() / tiles.width; + double tile_box_height = box.height() / tiles.height; + for (std::size_t tile_y = 0; tile_y < tiles.height; tile_y++) + { + for (std::size_t tile_x = 0; tile_x < tiles.width; tile_x++) + { + mapnik::box2d tile_box( + box.minx() + tile_x * tile_box_width, + box.miny() + tile_y * tile_box_height, + box.minx() + (tile_x + 1) * tile_box_width, + box.miny() + (tile_y + 1) * tile_box_height); + map.zoom_to_box(tile_box); + image_type tile(ren.render(map, scale_factor)); + set_rectangle(tile, image, tile_x * tile.width(), (tiles.height - 1 - tile_y) * tile.height()); + } + } + return image; + } + + result report(image_type const & image, + std::string const & name, + map_size const & size, + map_size const & tiles, + double scale_factor) const + { + boost::filesystem::path reference = reference_dir / image_file_name(name, size, tiles, scale_factor, true); bool reference_exists = boost::filesystem::exists(reference); result res; @@ -211,14 +280,15 @@ public: res.name = name; res.renderer_name = Renderer::name; res.scale_factor = scale_factor; - res.size = map_size(map.width(), map.height()); + res.size = size; + res.tiles = tiles; res.reference_image_path = reference; res.diff = reference_exists ? ren.compare(image, reference) : 0; if (res.diff) { boost::filesystem::create_directories(output_dir); - boost::filesystem::path path = output_dir / image_file_name(name, map.width(), map.height(), scale_factor, false, Renderer::ext); + boost::filesystem::path path = output_dir / image_file_name(name, size, tiles, scale_factor, false); res.actual_image_path = path; res.state = STATE_FAIL; ren.save(image, path); @@ -235,16 +305,23 @@ public: private: std::string image_file_name(std::string const & test_name, - double width, - double height, + map_size const & size, + map_size const & tiles, double scale_factor, - bool reference, - std::string const& ext) const + bool reference) const { std::stringstream s; - s << test_name << '-' << width << '-' << height << '-' - << std::fixed << std::setprecision(1) << scale_factor - << '-' << Renderer::name << (reference ? "-reference" : "") << ext; + s << test_name << '-' << size.width << '-' << size.height << '-'; + if (tiles.width > 1 || tiles.height > 1) + { + s << tiles.width << 'x' << tiles.height << '-'; + } + s << std::fixed << std::setprecision(1) << scale_factor << '-' << Renderer::name; + if (reference) + { + s << "-reference"; + } + s << Renderer::ext; return s.str(); } diff --git a/test/visual/report.cpp b/test/visual/report.cpp index aeca371a0..deaf5b9c5 100644 --- a/test/visual/report.cpp +++ b/test/visual/report.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include "report.hpp" @@ -32,8 +33,12 @@ namespace visual_tests void console_report::report(result const & r) { - s << '"' << r.name << '-' << r.size.width << '-' << r.size.height << '-' << std::fixed << - std::setprecision(1) << r.scale_factor << "\" with " << r.renderer_name << "... "; + s << '"' << r.name << '-' << r.size.width << '-' << r.size.height; + if (r.tiles.width > 1 || r.tiles.height > 1) + { + s << '-' << r.tiles.width << 'x' << r.tiles.height; + } + s << '-' << std::fixed << std::setprecision(1) << r.scale_factor << "\" with " << r.renderer_name << "... "; switch (r.state) { @@ -51,6 +56,11 @@ void console_report::report(result const & r) break; } + if (show_duration) + { + s << " (" << std::chrono::duration_cast(r.duration).count() << " milliseconds)"; + } + s << std::endl; } @@ -61,6 +71,10 @@ unsigned console_report::summary(result_list const & results) unsigned fail = 0; unsigned overwrite = 0; + using namespace std::chrono; + using duration_map_type = std::map; + duration_map_type durations; + for (auto const & r : results) { switch (r.state) @@ -70,12 +84,37 @@ unsigned console_report::summary(result_list const & results) case STATE_ERROR: error++; break; case STATE_OVERWRITE: overwrite++; break; } + + if (show_duration) + { + duration_map_type::iterator duration = durations.find(r.renderer_name); + if (duration == durations.end()) + { + durations.emplace(r.renderer_name, r.duration); + } + else + { + duration->second += r.duration; + } + } } s << std::endl; s << "Visual rendering: " << fail << " failed / " << ok << " passed / " << overwrite << " overwritten / " << error << " errors" << std::endl; + if (show_duration) + { + high_resolution_clock::duration total(0); + for (auto const & duration : durations) + { + s << duration.first << ": \t" << duration_cast(duration.second).count() + << " milliseconds" << std::endl; + total += duration.second; + } + s << "total: \t" << duration_cast(total).count() << " milliseconds" << std::endl; + } + return fail + error; } diff --git a/test/visual/report.hpp b/test/visual/report.hpp index 3b6146ec5..837ed5e95 100644 --- a/test/visual/report.hpp +++ b/test/visual/report.hpp @@ -36,7 +36,7 @@ namespace visual_tests class console_report { public: - console_report() : s(std::clog) + console_report(bool _show_duration) : s(std::clog), show_duration(_show_duration) { } @@ -49,12 +49,13 @@ public: protected: std::ostream & s; + bool show_duration; }; class console_short_report : public console_report { public: - console_short_report() : console_report() + console_short_report(bool _show_duration) : console_report(_show_duration) { } diff --git a/test/visual/run.cpp b/test/visual/run.cpp index 1353bcc42..62ca73b0e 100644 --- a/test/visual/run.cpp +++ b/test/visual/run.cpp @@ -31,6 +31,18 @@ #include "cleanup.hpp" // run_cleanup() +#ifdef MAPNIK_LOG +using log_levels_map = std::map; + +log_levels_map log_levels +{ + { "debug", mapnik::logger::severity_type::debug }, + { "warn", mapnik::logger::severity_type::warn }, + { "error", mapnik::logger::severity_type::error }, + { "none", mapnik::logger::severity_type::none } +}; +#endif + int main(int argc, char** argv) { using namespace visual_tests; @@ -41,6 +53,8 @@ int main(int argc, char** argv) ("help,h", "produce usage message") ("verbose,v", "verbose output") ("overwrite,o", "overwrite reference image") + ("duration,d", "output rendering duration") + ("iterations,i", po::value()->default_value(1), "number of iterations for benchmarking") ("jobs,j", po::value()->default_value(1), "number of parallel threads") ("styles-dir", po::value()->default_value("test/data-visual/styles"), "directory with styles") ("images-dir", po::value()->default_value("test/data-visual/images"), "directory with reference images") @@ -49,6 +63,11 @@ int main(int argc, char** argv) ("styles", po::value>(), "selected styles to test") ("fonts", po::value()->default_value("fonts"), "font search path") ("plugins", po::value()->default_value("plugins/input"), "input plugins search path") +#ifdef MAPNIK_LOG + ("log", po::value()->default_value(std::find_if(log_levels.begin(), log_levels.end(), + [](log_levels_map::value_type const & level) { return level.second == mapnik::logger::get_severity(); } )->first), + "log level (debug, warn, error, none)") +#endif ; po::positional_options_description p; @@ -63,6 +82,20 @@ int main(int argc, char** argv) return 1; } +#ifdef MAPNIK_LOG + std::string log_level(vm["log"].as()); + log_levels_map::const_iterator level_iter = log_levels.find(log_level); + if (level_iter == log_levels.end()) + { + std::cerr << "Error: Unknown log level: " << log_level << std::endl; + return 1; + } + else + { + mapnik::logger::set_severity(level_iter->second); + } +#endif + mapnik::freetype_engine::register_fonts(vm["fonts"].as(), true); mapnik::datasource_cache::instance().register_datasources(vm["plugins"].as()); @@ -77,8 +110,10 @@ int main(int argc, char** argv) output_dir, vm["images-dir"].as(), vm.count("overwrite"), + vm["iterations"].as(), vm["jobs"].as()); - report_type report = vm.count("verbose") ? report_type((console_report())) : report_type((console_short_report())); + bool show_duration = vm.count("duration"); + report_type report(vm.count("verbose") ? report_type((console_report(show_duration))) : report_type((console_short_report(show_duration)))); result_list results; try diff --git a/test/visual/runner.cpp b/test/visual/runner.cpp index f2562c197..45dc36656 100644 --- a/test/visual/runner.cpp +++ b/test/visual/runner.cpp @@ -34,32 +34,97 @@ namespace visual_tests class renderer_visitor { public: - renderer_visitor(std::string const & name, mapnik::Map const & map, double scale_factor) - : name_(name), map_(map), scale_factor_(scale_factor) + renderer_visitor(std::string const & name, + mapnik::Map & map, + map_size const & tiles, + double scale_factor, + result_list & results, + report_type & report, + std::size_t iterations) + : name_(name), + map_(map), + tiles_(tiles), + scale_factor_(scale_factor), + results_(results), + report_(report), + iterations_(iterations) { } - template - result operator()(T const & renderer) const + template ::type* = nullptr> + void operator()(T const & renderer) { - return renderer.test(name_, map_, scale_factor_); + test(renderer); + } + + template ::type* = nullptr> + void operator()(T const & renderer) + { + if (tiles_.width == 1 && tiles_.height == 1) + { + test(renderer); + } } private: + template + void test(T const & renderer) + { + map_size size { map_.width(), map_.height() }; + std::chrono::high_resolution_clock::time_point start(std::chrono::high_resolution_clock::now()); + for (std::size_t i = iterations_ ; i > 0; i--) + { + typename T::image_type image(render(renderer)); + if (i == 1) + { + std::chrono::high_resolution_clock::time_point end(std::chrono::high_resolution_clock::now()); + result r(renderer.report(image, name_, size, tiles_, scale_factor_)); + r.duration = end - start; + mapnik::util::apply_visitor(report_visitor(r), report_); + results_.push_back(std::move(r)); + } + } + } + + template ::type* = nullptr> + typename T::image_type render(T const & renderer) + { + if (tiles_.width == 1 && tiles_.height == 1) + { + return renderer.render(map_, scale_factor_); + } + else + { + return renderer.render(map_, scale_factor_, tiles_); + } + } + + template ::type* = nullptr> + typename T::image_type render(T const & renderer) + { + return renderer.render(map_, scale_factor_); + } + std::string const & name_; - mapnik::Map const & map_; + mapnik::Map & map_; + map_size const & tiles_; double scale_factor_; + result_list & results_; + report_type & report_; + std::size_t iterations_; }; runner::runner(runner::path_type const & styles_dir, runner::path_type const & output_dir, runner::path_type const & reference_dir, bool overwrite, + std::size_t iterations, std::size_t jobs) : styles_dir_(styles_dir), output_dir_(output_dir), reference_dir_(reference_dir), jobs_(jobs), + iterations_(iterations), renderers_{ renderer(output_dir_, reference_dir_, overwrite) #if defined(HAVE_CAIRO) ,renderer(output_dir_, reference_dir_, overwrite) @@ -173,12 +238,12 @@ result_list runner::test_range(files_iterator begin, files_iterator end, std::re result_list runner::test_one(runner::path_type const& style_path, config cfg, report_type & report) const { - mapnik::Map m(cfg.sizes.front().width, cfg.sizes.front().height); + mapnik::Map map(cfg.sizes.front().width, cfg.sizes.front().height); result_list results; try { - mapnik::load_map(m, style_path.string(), true); + mapnik::load_map(map, style_path.string(), true); } catch (std::exception const& ex) { @@ -191,7 +256,7 @@ result_list runner::test_one(runner::path_type const& style_path, config cfg, re throw; } - mapnik::parameters const & params = m.get_extra_parameters(); + mapnik::parameters const & params = map.get_extra_parameters(); boost::optional status = params.get("status", cfg.status); @@ -208,32 +273,52 @@ result_list runner::test_one(runner::path_type const& style_path, config cfg, re parse_map_sizes(*sizes, cfg.sizes); } + boost::optional tiles = params.get("tiles"); + + if (tiles) + { + cfg.tiles.clear(); + parse_map_sizes(*tiles, cfg.tiles); + } + + boost::optional bbox_string = params.get("bbox"); + mapnik::box2d box; + + if (bbox_string) + { + box.from_string(*bbox_string); + } + std::string name(style_path.stem().string()); - for (map_size const & size : cfg.sizes) + for (auto const & size : cfg.sizes) { - m.resize(size.width, size.height); - - boost::optional bbox_string = params.get("bbox"); - - if (bbox_string) + for (auto const & scale_factor : cfg.scales) { - mapnik::box2d bbox; - bbox.from_string(*bbox_string); - m.zoom_to_box(bbox); - } - else - { - m.zoom_all(); - } - - for (double const & scale_factor : cfg.scales) - { - for(auto const& ren : renderers_) + for (auto const & tiles_count : cfg.tiles) { - result r = mapnik::util::apply_visitor(renderer_visitor(name, m, scale_factor), ren); - results.emplace_back(r); - mapnik::util::apply_visitor(report_visitor(r), report); + if (!tiles_count.width || !tiles_count.height) + { + throw std::runtime_error("Cannot render zero tiles."); + } + if (size.width % tiles_count.width || size.height % tiles_count.height) + { + throw std::runtime_error("Tile size is not an integer."); + } + + for (auto const & ren : renderers_) + { + map.resize(size.width, size.height); + if (box.valid()) + { + map.zoom_to_box(box); + } + else + { + map.zoom_all(); + } + mapnik::util::apply_visitor(renderer_visitor(name, map, tiles_count, scale_factor, results, report, iterations_), ren); + } } } } diff --git a/test/visual/runner.hpp b/test/visual/runner.hpp index 77949e46e..a4c91baef 100644 --- a/test/visual/runner.hpp +++ b/test/visual/runner.hpp @@ -54,6 +54,7 @@ public: path_type const & output_dir, path_type const & reference_dir, bool overwrite, + std::size_t iterations, std::size_t jobs); result_list test_all(report_type & report) const; @@ -70,6 +71,7 @@ private: const path_type output_dir_; const path_type reference_dir_; const std::size_t jobs_; + const std::size_t iterations_; const renderer_type renderers_[boost::mpl::size::value]; }; diff --git a/utils/svg2png/svg2png.cpp b/utils/svg2png/svg2png.cpp index 7e3aca532..3e00663bb 100644 --- a/utils/svg2png/svg2png.cpp +++ b/utils/svg2png/svg2png.cpp @@ -48,32 +48,16 @@ #include "agg_pixfmt_rgba.h" #include "agg_scanline_u.h" -#include // for xmlInitParser(), xmlCleanupParser() - struct main_marker_visitor { - main_marker_visitor(std::string & svg_name, - int & return_value, + main_marker_visitor(std::string const& svg_name, bool verbose, bool auto_open) : svg_name_(svg_name), - return_value_(return_value), verbose_(verbose), auto_open_(auto_open) {} - void operator() (mapnik::marker_null const&) - { - std::clog << "svg2png error: '" << svg_name_ << "' is not a valid vector!\n"; - return_value_ = -1; - } - - void operator() (mapnik::marker_rgba8 const&) - { - std::clog << "svg2png error: '" << svg_name_ << "' is not a valid vector!\n"; - return_value_ = -1; - } - - void operator() (mapnik::marker_svg const& marker) + int operator() (mapnik::marker_svg const& marker) { using pixfmt = agg::pixfmt_rgba32_pre; using renderer_base = agg::renderer_base; @@ -111,28 +95,37 @@ struct main_marker_visitor svg_renderer_this.render(ras_ptr, sl, renb, mtx, opacity, bbox); - boost::algorithm::ireplace_last(svg_name_,".svg",".png"); + std::string png_name(svg_name_); + boost::algorithm::ireplace_last(png_name,".svg",".png"); demultiply_alpha(im); - mapnik::save_to_file(im,svg_name_,"png"); + mapnik::save_to_file(im,png_name,"png"); + int status = 0; if (auto_open_) { std::ostringstream s; #ifdef DARWIN - s << "open " << svg_name_; + s << "open " << png_name; #else - s << "xdg-open " << svg_name_; + s << "xdg-open " << png_name; #endif int ret = system(s.str().c_str()); if (ret != 0) - return_value_ = ret; + status = ret; } - std::clog << "rendered to: " << svg_name_ << "\n"; + std::clog << "rendered to: " << png_name << "\n"; + return status; + } + // default + template + int operator() (T const&) + { + std::clog << "svg2png error: '" << svg_name_ << "' is not a valid vector!\n"; + return -1; } private: - std::string & svg_name_; - int & return_value_; + std::string const& svg_name_; bool verbose_; bool auto_open_; }; @@ -143,7 +136,7 @@ int main (int argc,char** argv) bool verbose = false; bool auto_open = false; - int return_value = 0; + int status = 0; std::vector svg_files; mapnik::logger::instance().set_severity(mapnik::logger::error); @@ -203,8 +196,6 @@ int main (int argc,char** argv) return 0; } - xmlInitParser(); - while (itr != svg_files.end()) { std::string svg_name (*itr++); @@ -214,20 +205,19 @@ int main (int argc,char** argv) } std::shared_ptr marker = mapnik::marker_cache::instance().find(svg_name, false); - main_marker_visitor visitor(svg_name, return_value, verbose, auto_open); - mapnik::util::apply_visitor(visitor, *marker); + main_marker_visitor visitor(svg_name, verbose, auto_open); + status = mapnik::util::apply_visitor(visitor, *marker); } } + catch (std::exception const& ex) + { + std::clog << "Exception caught:" << ex.what() << std::endl; + return -1; + } catch (...) { std::clog << "Exception of unknown type!" << std::endl; - xmlCleanupParser(); return -1; } - - // only call this once, on exit - // to make sure valgrind output is clean - // http://xmlsoft.org/xmlmem.html - xmlCleanupParser(); - return return_value; + return status; }