Merge branch 'master' into spirit-x3
This commit is contained in:
commit
65e81848ba
66 changed files with 2056 additions and 1878 deletions
14
.travis.yml
14
.travis.yml
|
@ -29,9 +29,9 @@ matrix:
|
||||||
compiler: clang
|
compiler: clang
|
||||||
env: JOBS=8 CXX="ccache clang++-3.6 -Qunused-arguments" CC="clang-3.6" MASON_PUBLISH=true BENCH=True
|
env: JOBS=8 CXX="ccache clang++-3.6 -Qunused-arguments" CC="clang-3.6" MASON_PUBLISH=true BENCH=True
|
||||||
addons:
|
addons:
|
||||||
apt:
|
apt:
|
||||||
sources: [ 'ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.6' ]
|
sources: [ 'ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.6' ]
|
||||||
packages: [ 'clang-3.6', 'libstdc++-4.9-dev', 'libstdc++6' ]
|
packages: [ 'clang-3.6', 'libstdc++-4.9-dev', 'libstdc++6' ]
|
||||||
# OOM killer knocking out build on render_markers_symbolizer.cpp
|
# OOM killer knocking out build on render_markers_symbolizer.cpp
|
||||||
#
|
#
|
||||||
#- os: linux
|
#- os: linux
|
||||||
|
@ -44,12 +44,12 @@ matrix:
|
||||||
- os: osx
|
- os: osx
|
||||||
compiler: clang
|
compiler: clang
|
||||||
# https://docs.travis-ci.com/user/languages/objective-c/#Supported-OS-X-iOS-SDK-versions
|
# https://docs.travis-ci.com/user/languages/objective-c/#Supported-OS-X-iOS-SDK-versions
|
||||||
osx_image: xcode7.2 # upgrades clang from 6 -> 7
|
osx_image: xcode7.3 # upgrades clang from 6 -> 7
|
||||||
env: JOBS=8 MASON_PUBLISH=true HEAVY_JOBS=4
|
env: JOBS=4 MASON_PUBLISH=true HEAVY_JOBS=2
|
||||||
- os: osx
|
- os: osx
|
||||||
compiler: clang
|
compiler: clang
|
||||||
osx_image: xcode7.2 # upgrades clang from 6 -> 7
|
osx_image: xcode7.3 # upgrades clang from 6 -> 7
|
||||||
env: JOBS=8 COVERAGE=true HEAVY_JOBS=4
|
env: JOBS=4 COVERAGE=true HEAVY_JOBS=2
|
||||||
|
|
||||||
before_install:
|
before_install:
|
||||||
- source scripts/travis-common.sh
|
- source scripts/travis-common.sh
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#include "bench_framework.hpp"
|
#include "bench_framework.hpp"
|
||||||
#include <cstring>
|
#include "../plugins/input/csv/csv_getline.hpp"
|
||||||
#include <cstdlib>
|
|
||||||
#include "../plugins/input/csv/csv_utils.hpp"
|
|
||||||
|
|
||||||
class test : public benchmark::test_case
|
class test : public benchmark::test_case
|
||||||
{
|
{
|
||||||
|
|
|
@ -32,26 +32,12 @@
|
||||||
#pragma GCC diagnostic push
|
#pragma GCC diagnostic push
|
||||||
#include <mapnik/warning_ignore.hpp>
|
#include <mapnik/warning_ignore.hpp>
|
||||||
#include <boost/spirit/include/qi.hpp>
|
#include <boost/spirit/include/qi.hpp>
|
||||||
#include <boost/spirit/include/qi_action.hpp>
|
|
||||||
#include <boost/spirit/include/qi_lexeme.hpp>
|
|
||||||
#include <boost/spirit/include/phoenix_core.hpp>
|
|
||||||
#include <boost/spirit/include/phoenix_operator.hpp>
|
|
||||||
#include <boost/spirit/include/phoenix_fusion.hpp>
|
|
||||||
#include <boost/spirit/include/phoenix_function.hpp>
|
#include <boost/spirit/include/phoenix_function.hpp>
|
||||||
#include <boost/fusion/include/adapt_adt.hpp>
|
|
||||||
#pragma GCC diagnostic pop
|
#pragma GCC diagnostic pop
|
||||||
|
|
||||||
// stl
|
// stl
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
BOOST_FUSION_ADAPT_ADT(
|
|
||||||
mapnik::color,
|
|
||||||
(unsigned, unsigned, obj.red(), obj.set_red(mapnik::safe_cast<uint8_t>(val)))
|
|
||||||
(unsigned, unsigned, obj.green(), obj.set_green(mapnik::safe_cast<uint8_t>(val)))
|
|
||||||
(unsigned, unsigned, obj.blue(), obj.set_blue(mapnik::safe_cast<uint8_t>(val)))
|
|
||||||
(unsigned, unsigned, obj.alpha(), obj.set_alpha(mapnik::safe_cast<uint8_t>(val)))
|
|
||||||
)
|
|
||||||
|
|
||||||
namespace mapnik
|
namespace mapnik
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -61,162 +47,6 @@ namespace phoenix = boost::phoenix;
|
||||||
|
|
||||||
using ascii_space_type = boost::spirit::ascii::space_type;
|
using ascii_space_type = boost::spirit::ascii::space_type;
|
||||||
|
|
||||||
struct named_colors_ : qi::symbols<char,color>
|
|
||||||
{
|
|
||||||
named_colors_()
|
|
||||||
{
|
|
||||||
add
|
|
||||||
("aliceblue", color(240, 248, 255))
|
|
||||||
("antiquewhite", color(250, 235, 215))
|
|
||||||
("aqua", color(0, 255, 255))
|
|
||||||
("aquamarine", color(127, 255, 212))
|
|
||||||
("azure", color(240, 255, 255))
|
|
||||||
("beige", color(245, 245, 220))
|
|
||||||
("bisque", color(255, 228, 196))
|
|
||||||
("black", color(0, 0, 0))
|
|
||||||
("blanchedalmond", color(255,235,205))
|
|
||||||
("blue", color(0, 0, 255))
|
|
||||||
("blueviolet", color(138, 43, 226))
|
|
||||||
("brown", color(165, 42, 42))
|
|
||||||
("burlywood", color(222, 184, 135))
|
|
||||||
("cadetblue", color(95, 158, 160))
|
|
||||||
("chartreuse", color(127, 255, 0))
|
|
||||||
("chocolate", color(210, 105, 30))
|
|
||||||
("coral", color(255, 127, 80))
|
|
||||||
("cornflowerblue", color(100, 149, 237))
|
|
||||||
("cornsilk", color(255, 248, 220))
|
|
||||||
("crimson", color(220, 20, 60))
|
|
||||||
("cyan", color(0, 255, 255))
|
|
||||||
("darkblue", color(0, 0, 139))
|
|
||||||
("darkcyan", color(0, 139, 139))
|
|
||||||
("darkgoldenrod", color(184, 134, 11))
|
|
||||||
("darkgray", color(169, 169, 169))
|
|
||||||
("darkgreen", color(0, 100, 0))
|
|
||||||
("darkgrey", color(169, 169, 169))
|
|
||||||
("darkkhaki", color(189, 183, 107))
|
|
||||||
("darkmagenta", color(139, 0, 139))
|
|
||||||
("darkolivegreen", color(85, 107, 47))
|
|
||||||
("darkorange", color(255, 140, 0))
|
|
||||||
("darkorchid", color(153, 50, 204))
|
|
||||||
("darkred", color(139, 0, 0))
|
|
||||||
("darksalmon", color(233, 150, 122))
|
|
||||||
("darkseagreen", color(143, 188, 143))
|
|
||||||
("darkslateblue", color(72, 61, 139))
|
|
||||||
("darkslategrey", color(47, 79, 79))
|
|
||||||
("darkturquoise", color(0, 206, 209))
|
|
||||||
("darkviolet", color(148, 0, 211))
|
|
||||||
("deeppink", color(255, 20, 147))
|
|
||||||
("deepskyblue", color(0, 191, 255))
|
|
||||||
("dimgray", color(105, 105, 105))
|
|
||||||
("dimgrey", color(105, 105, 105))
|
|
||||||
("dodgerblue", color(30, 144, 255))
|
|
||||||
("firebrick", color(178, 34, 34))
|
|
||||||
("floralwhite", color(255, 250, 240))
|
|
||||||
("forestgreen", color(34, 139, 34))
|
|
||||||
("fuchsia", color(255, 0, 255))
|
|
||||||
("gainsboro", color(220, 220, 220))
|
|
||||||
("ghostwhite", color(248, 248, 255))
|
|
||||||
("gold", color(255, 215, 0))
|
|
||||||
("goldenrod", color(218, 165, 32))
|
|
||||||
("gray", color(128, 128, 128))
|
|
||||||
("grey", color(128, 128, 128))
|
|
||||||
("green", color(0, 128, 0))
|
|
||||||
("greenyellow", color(173, 255, 47))
|
|
||||||
("honeydew", color(240, 255, 240))
|
|
||||||
("hotpink", color(255, 105, 180))
|
|
||||||
("indianred", color(205, 92, 92))
|
|
||||||
("indigo", color(75, 0, 130))
|
|
||||||
("ivory", color(255, 255, 240))
|
|
||||||
("khaki", color(240, 230, 140))
|
|
||||||
("lavender", color(230, 230, 250))
|
|
||||||
("lavenderblush", color(255, 240, 245))
|
|
||||||
("lawngreen", color(124, 252, 0))
|
|
||||||
("lemonchiffon", color(255, 250, 205))
|
|
||||||
("lightblue", color(173, 216, 230))
|
|
||||||
("lightcoral", color(240, 128, 128))
|
|
||||||
("lightcyan", color(224, 255, 255))
|
|
||||||
("lightgoldenrodyellow", color(250, 250, 210))
|
|
||||||
("lightgray", color(211, 211, 211))
|
|
||||||
("lightgreen", color(144, 238, 144))
|
|
||||||
("lightgrey", color(211, 211, 211))
|
|
||||||
("lightpink", color(255, 182, 193))
|
|
||||||
("lightsalmon", color(255, 160, 122))
|
|
||||||
("lightseagreen", color(32, 178, 170))
|
|
||||||
("lightskyblue", color(135, 206, 250))
|
|
||||||
("lightslategray", color(119, 136, 153))
|
|
||||||
("lightslategrey", color(119, 136, 153))
|
|
||||||
("lightsteelblue", color(176, 196, 222))
|
|
||||||
("lightyellow", color(255, 255, 224))
|
|
||||||
("lime", color(0, 255, 0))
|
|
||||||
("limegreen", color(50, 205, 50))
|
|
||||||
("linen", color(250, 240, 230))
|
|
||||||
("magenta", color(255, 0, 255))
|
|
||||||
("maroon", color(128, 0, 0))
|
|
||||||
("mediumaquamarine", color(102, 205, 170))
|
|
||||||
("mediumblue", color(0, 0, 205))
|
|
||||||
("mediumorchid", color(186, 85, 211))
|
|
||||||
("mediumpurple", color(147, 112, 219))
|
|
||||||
("mediumseagreen", color(60, 179, 113))
|
|
||||||
("mediumslateblue", color(123, 104, 238))
|
|
||||||
("mediumspringgreen", color(0, 250, 154))
|
|
||||||
("mediumturquoise", color(72, 209, 204))
|
|
||||||
("mediumvioletred", color(199, 21, 133))
|
|
||||||
("midnightblue", color(25, 25, 112))
|
|
||||||
("mintcream", color(245, 255, 250))
|
|
||||||
("mistyrose", color(255, 228, 225))
|
|
||||||
("moccasin", color(255, 228, 181))
|
|
||||||
("navajowhite", color(255, 222, 173))
|
|
||||||
("navy", color(0, 0, 128))
|
|
||||||
("oldlace", color(253, 245, 230))
|
|
||||||
("olive", color(128, 128, 0))
|
|
||||||
("olivedrab", color(107, 142, 35))
|
|
||||||
("orange", color(255, 165, 0))
|
|
||||||
("orangered", color(255, 69, 0))
|
|
||||||
("orchid", color(218, 112, 214))
|
|
||||||
("palegoldenrod", color(238, 232, 170))
|
|
||||||
("palegreen", color(152, 251, 152))
|
|
||||||
("paleturquoise", color(175, 238, 238))
|
|
||||||
("palevioletred", color(219, 112, 147))
|
|
||||||
("papayawhip", color(255, 239, 213))
|
|
||||||
("peachpuff", color(255, 218, 185))
|
|
||||||
("peru", color(205, 133, 63))
|
|
||||||
("pink", color(255, 192, 203))
|
|
||||||
("plum", color(221, 160, 221))
|
|
||||||
("powderblue", color(176, 224, 230))
|
|
||||||
("purple", color(128, 0, 128))
|
|
||||||
("red", color(255, 0, 0))
|
|
||||||
("rosybrown", color(188, 143, 143))
|
|
||||||
("royalblue", color(65, 105, 225))
|
|
||||||
("saddlebrown", color(139, 69, 19))
|
|
||||||
("salmon", color(250, 128, 114))
|
|
||||||
("sandybrown", color(244, 164, 96))
|
|
||||||
("seagreen", color(46, 139, 87))
|
|
||||||
("seashell", color(255, 245, 238))
|
|
||||||
("sienna", color(160, 82, 45))
|
|
||||||
("silver", color(192, 192, 192))
|
|
||||||
("skyblue", color(135, 206, 235))
|
|
||||||
("slateblue", color(106, 90, 205))
|
|
||||||
("slategray", color(112, 128, 144))
|
|
||||||
("slategrey", color(112, 128, 144))
|
|
||||||
("snow", color(255, 250, 250))
|
|
||||||
("springgreen", color(0, 255, 127))
|
|
||||||
("steelblue", color(70, 130, 180))
|
|
||||||
("tan", color(210, 180, 140))
|
|
||||||
("teal", color(0, 128, 128))
|
|
||||||
("thistle", color(216, 191, 216))
|
|
||||||
("tomato", color(255, 99, 71))
|
|
||||||
("turquoise", color(64, 224, 208))
|
|
||||||
("violet", color(238, 130, 238))
|
|
||||||
("wheat", color(245, 222, 179))
|
|
||||||
("white", color(255, 255, 255))
|
|
||||||
("whitesmoke", color(245, 245, 245))
|
|
||||||
("yellow", color(255, 255, 0))
|
|
||||||
("yellowgreen", color(154, 205, 50))
|
|
||||||
("transparent", color(0, 0, 0, 0))
|
|
||||||
;
|
|
||||||
}
|
|
||||||
} ;
|
|
||||||
|
|
||||||
struct percent_conv_impl
|
struct percent_conv_impl
|
||||||
{
|
{
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
@ -296,7 +126,6 @@ struct css_color_grammar : qi::grammar<Iterator, color(), ascii_space_type>
|
||||||
qi::rule<Iterator, color(), ascii_space_type> hex_color;
|
qi::rule<Iterator, color(), ascii_space_type> hex_color;
|
||||||
qi::rule<Iterator, color(), ascii_space_type> hex_color_small;
|
qi::rule<Iterator, color(), ascii_space_type> hex_color_small;
|
||||||
qi::rule<Iterator, color(), ascii_space_type> css_color;
|
qi::rule<Iterator, color(), ascii_space_type> css_color;
|
||||||
named_colors_ named;
|
|
||||||
phoenix::function<percent_conv_impl> percent_converter;
|
phoenix::function<percent_conv_impl> percent_converter;
|
||||||
phoenix::function<alpha_conv_impl> alpha_converter;
|
phoenix::function<alpha_conv_impl> alpha_converter;
|
||||||
phoenix::function<hsl_conv_impl> hsl_converter;
|
phoenix::function<hsl_conv_impl> hsl_converter;
|
||||||
|
|
|
@ -22,11 +22,184 @@
|
||||||
|
|
||||||
// NOTE: This is an implementation header file and is only meant to be included
|
// NOTE: This is an implementation header file and is only meant to be included
|
||||||
// from implementation files. It therefore doesn't have an include guard.
|
// from implementation files. It therefore doesn't have an include guard.
|
||||||
|
// mapnik
|
||||||
#include <mapnik/css_color_grammar.hpp>
|
#include <mapnik/css_color_grammar.hpp>
|
||||||
|
// boost
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#include <boost/fusion/include/adapt_adt.hpp>
|
||||||
|
#include <boost/spirit/include/phoenix_core.hpp>
|
||||||
|
#include <boost/spirit/include/phoenix_operator.hpp>
|
||||||
|
#include <boost/spirit/include/phoenix_fusion.hpp>
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
|
||||||
|
|
||||||
|
BOOST_FUSION_ADAPT_ADT(
|
||||||
|
mapnik::color,
|
||||||
|
(unsigned, unsigned, obj.red(), obj.set_red(mapnik::safe_cast<uint8_t>(val)))
|
||||||
|
(unsigned, unsigned, obj.green(), obj.set_green(mapnik::safe_cast<uint8_t>(val)))
|
||||||
|
(unsigned, unsigned, obj.blue(), obj.set_blue(mapnik::safe_cast<uint8_t>(val)))
|
||||||
|
(unsigned, unsigned, obj.alpha(), obj.set_alpha(mapnik::safe_cast<uint8_t>(val)))
|
||||||
|
)
|
||||||
|
|
||||||
namespace mapnik
|
namespace mapnik
|
||||||
{
|
{
|
||||||
|
|
||||||
|
struct named_colors : qi::symbols<char,color>
|
||||||
|
{
|
||||||
|
named_colors()
|
||||||
|
{
|
||||||
|
add
|
||||||
|
("aliceblue", color(240, 248, 255))
|
||||||
|
("antiquewhite", color(250, 235, 215))
|
||||||
|
("aqua", color(0, 255, 255))
|
||||||
|
("aquamarine", color(127, 255, 212))
|
||||||
|
("azure", color(240, 255, 255))
|
||||||
|
("beige", color(245, 245, 220))
|
||||||
|
("bisque", color(255, 228, 196))
|
||||||
|
("black", color(0, 0, 0))
|
||||||
|
("blanchedalmond", color(255,235,205))
|
||||||
|
("blue", color(0, 0, 255))
|
||||||
|
("blueviolet", color(138, 43, 226))
|
||||||
|
("brown", color(165, 42, 42))
|
||||||
|
("burlywood", color(222, 184, 135))
|
||||||
|
("cadetblue", color(95, 158, 160))
|
||||||
|
("chartreuse", color(127, 255, 0))
|
||||||
|
("chocolate", color(210, 105, 30))
|
||||||
|
("coral", color(255, 127, 80))
|
||||||
|
("cornflowerblue", color(100, 149, 237))
|
||||||
|
("cornsilk", color(255, 248, 220))
|
||||||
|
("crimson", color(220, 20, 60))
|
||||||
|
("cyan", color(0, 255, 255))
|
||||||
|
("darkblue", color(0, 0, 139))
|
||||||
|
("darkcyan", color(0, 139, 139))
|
||||||
|
("darkgoldenrod", color(184, 134, 11))
|
||||||
|
("darkgray", color(169, 169, 169))
|
||||||
|
("darkgreen", color(0, 100, 0))
|
||||||
|
("darkgrey", color(169, 169, 169))
|
||||||
|
("darkkhaki", color(189, 183, 107))
|
||||||
|
("darkmagenta", color(139, 0, 139))
|
||||||
|
("darkolivegreen", color(85, 107, 47))
|
||||||
|
("darkorange", color(255, 140, 0))
|
||||||
|
("darkorchid", color(153, 50, 204))
|
||||||
|
("darkred", color(139, 0, 0))
|
||||||
|
("darksalmon", color(233, 150, 122))
|
||||||
|
("darkseagreen", color(143, 188, 143))
|
||||||
|
("darkslateblue", color(72, 61, 139))
|
||||||
|
("darkslategrey", color(47, 79, 79))
|
||||||
|
("darkturquoise", color(0, 206, 209))
|
||||||
|
("darkviolet", color(148, 0, 211))
|
||||||
|
("deeppink", color(255, 20, 147))
|
||||||
|
("deepskyblue", color(0, 191, 255))
|
||||||
|
("dimgray", color(105, 105, 105))
|
||||||
|
("dimgrey", color(105, 105, 105))
|
||||||
|
("dodgerblue", color(30, 144, 255))
|
||||||
|
("firebrick", color(178, 34, 34))
|
||||||
|
("floralwhite", color(255, 250, 240))
|
||||||
|
("forestgreen", color(34, 139, 34))
|
||||||
|
("fuchsia", color(255, 0, 255))
|
||||||
|
("gainsboro", color(220, 220, 220))
|
||||||
|
("ghostwhite", color(248, 248, 255))
|
||||||
|
("gold", color(255, 215, 0))
|
||||||
|
("goldenrod", color(218, 165, 32))
|
||||||
|
("gray", color(128, 128, 128))
|
||||||
|
("grey", color(128, 128, 128))
|
||||||
|
("green", color(0, 128, 0))
|
||||||
|
("greenyellow", color(173, 255, 47))
|
||||||
|
("honeydew", color(240, 255, 240))
|
||||||
|
("hotpink", color(255, 105, 180))
|
||||||
|
("indianred", color(205, 92, 92))
|
||||||
|
("indigo", color(75, 0, 130))
|
||||||
|
("ivory", color(255, 255, 240))
|
||||||
|
("khaki", color(240, 230, 140))
|
||||||
|
("lavender", color(230, 230, 250))
|
||||||
|
("lavenderblush", color(255, 240, 245))
|
||||||
|
("lawngreen", color(124, 252, 0))
|
||||||
|
("lemonchiffon", color(255, 250, 205))
|
||||||
|
("lightblue", color(173, 216, 230))
|
||||||
|
("lightcoral", color(240, 128, 128))
|
||||||
|
("lightcyan", color(224, 255, 255))
|
||||||
|
("lightgoldenrodyellow", color(250, 250, 210))
|
||||||
|
("lightgray", color(211, 211, 211))
|
||||||
|
("lightgreen", color(144, 238, 144))
|
||||||
|
("lightgrey", color(211, 211, 211))
|
||||||
|
("lightpink", color(255, 182, 193))
|
||||||
|
("lightsalmon", color(255, 160, 122))
|
||||||
|
("lightseagreen", color(32, 178, 170))
|
||||||
|
("lightskyblue", color(135, 206, 250))
|
||||||
|
("lightslategray", color(119, 136, 153))
|
||||||
|
("lightslategrey", color(119, 136, 153))
|
||||||
|
("lightsteelblue", color(176, 196, 222))
|
||||||
|
("lightyellow", color(255, 255, 224))
|
||||||
|
("lime", color(0, 255, 0))
|
||||||
|
("limegreen", color(50, 205, 50))
|
||||||
|
("linen", color(250, 240, 230))
|
||||||
|
("magenta", color(255, 0, 255))
|
||||||
|
("maroon", color(128, 0, 0))
|
||||||
|
("mediumaquamarine", color(102, 205, 170))
|
||||||
|
("mediumblue", color(0, 0, 205))
|
||||||
|
("mediumorchid", color(186, 85, 211))
|
||||||
|
("mediumpurple", color(147, 112, 219))
|
||||||
|
("mediumseagreen", color(60, 179, 113))
|
||||||
|
("mediumslateblue", color(123, 104, 238))
|
||||||
|
("mediumspringgreen", color(0, 250, 154))
|
||||||
|
("mediumturquoise", color(72, 209, 204))
|
||||||
|
("mediumvioletred", color(199, 21, 133))
|
||||||
|
("midnightblue", color(25, 25, 112))
|
||||||
|
("mintcream", color(245, 255, 250))
|
||||||
|
("mistyrose", color(255, 228, 225))
|
||||||
|
("moccasin", color(255, 228, 181))
|
||||||
|
("navajowhite", color(255, 222, 173))
|
||||||
|
("navy", color(0, 0, 128))
|
||||||
|
("oldlace", color(253, 245, 230))
|
||||||
|
("olive", color(128, 128, 0))
|
||||||
|
("olivedrab", color(107, 142, 35))
|
||||||
|
("orange", color(255, 165, 0))
|
||||||
|
("orangered", color(255, 69, 0))
|
||||||
|
("orchid", color(218, 112, 214))
|
||||||
|
("palegoldenrod", color(238, 232, 170))
|
||||||
|
("palegreen", color(152, 251, 152))
|
||||||
|
("paleturquoise", color(175, 238, 238))
|
||||||
|
("palevioletred", color(219, 112, 147))
|
||||||
|
("papayawhip", color(255, 239, 213))
|
||||||
|
("peachpuff", color(255, 218, 185))
|
||||||
|
("peru", color(205, 133, 63))
|
||||||
|
("pink", color(255, 192, 203))
|
||||||
|
("plum", color(221, 160, 221))
|
||||||
|
("powderblue", color(176, 224, 230))
|
||||||
|
("purple", color(128, 0, 128))
|
||||||
|
("red", color(255, 0, 0))
|
||||||
|
("rosybrown", color(188, 143, 143))
|
||||||
|
("royalblue", color(65, 105, 225))
|
||||||
|
("saddlebrown", color(139, 69, 19))
|
||||||
|
("salmon", color(250, 128, 114))
|
||||||
|
("sandybrown", color(244, 164, 96))
|
||||||
|
("seagreen", color(46, 139, 87))
|
||||||
|
("seashell", color(255, 245, 238))
|
||||||
|
("sienna", color(160, 82, 45))
|
||||||
|
("silver", color(192, 192, 192))
|
||||||
|
("skyblue", color(135, 206, 235))
|
||||||
|
("slateblue", color(106, 90, 205))
|
||||||
|
("slategray", color(112, 128, 144))
|
||||||
|
("slategrey", color(112, 128, 144))
|
||||||
|
("snow", color(255, 250, 250))
|
||||||
|
("springgreen", color(0, 255, 127))
|
||||||
|
("steelblue", color(70, 130, 180))
|
||||||
|
("tan", color(210, 180, 140))
|
||||||
|
("teal", color(0, 128, 128))
|
||||||
|
("thistle", color(216, 191, 216))
|
||||||
|
("tomato", color(255, 99, 71))
|
||||||
|
("turquoise", color(64, 224, 208))
|
||||||
|
("violet", color(238, 130, 238))
|
||||||
|
("wheat", color(245, 222, 179))
|
||||||
|
("white", color(255, 255, 255))
|
||||||
|
("whitesmoke", color(245, 245, 245))
|
||||||
|
("yellow", color(255, 255, 0))
|
||||||
|
("yellowgreen", color(154, 205, 50))
|
||||||
|
("transparent", color(0, 0, 0, 0))
|
||||||
|
;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
template <typename Iterator>
|
template <typename Iterator>
|
||||||
css_color_grammar<Iterator>::css_color_grammar()
|
css_color_grammar<Iterator>::css_color_grammar()
|
||||||
: css_color_grammar::base_type(css_color)
|
: css_color_grammar::base_type(css_color)
|
||||||
|
@ -43,6 +216,8 @@ css_color_grammar<Iterator>::css_color_grammar()
|
||||||
ascii::no_case_type no_case;
|
ascii::no_case_type no_case;
|
||||||
using phoenix::at_c;
|
using phoenix::at_c;
|
||||||
|
|
||||||
|
named_colors named;
|
||||||
|
|
||||||
css_color %= rgba_color
|
css_color %= rgba_color
|
||||||
| rgba_percent_color
|
| rgba_percent_color
|
||||||
| hsl_percent_color
|
| hsl_percent_color
|
||||||
|
|
|
@ -20,20 +20,16 @@
|
||||||
*
|
*
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
#ifndef MAPNIK_CVS_GRAMMAR_HPP
|
#ifndef MAPNIK_CSV_GRAMMAR_HPP
|
||||||
#define MAPNIK_CVS_GRAMMAR_HPP
|
#define MAPNIK_CSV_GRAMMAR_HPP
|
||||||
|
|
||||||
//#define BOOST_SPIRIT_DEBUG
|
|
||||||
|
|
||||||
|
#include <mapnik/csv/csv_types.hpp>
|
||||||
#include <boost/spirit/include/qi.hpp>
|
#include <boost/spirit/include/qi.hpp>
|
||||||
#include <boost/spirit/include/phoenix.hpp>
|
#include <boost/spirit/include/phoenix.hpp>
|
||||||
|
|
||||||
namespace mapnik {
|
namespace mapnik {
|
||||||
|
|
||||||
namespace qi = boost::spirit::qi;
|
namespace qi = boost::spirit::qi;
|
||||||
using csv_value = std::string;
|
|
||||||
using csv_line = std::vector<csv_value>;
|
|
||||||
using csv_data = std::vector<csv_line>;
|
|
||||||
|
|
||||||
struct csv_white_space_skipper : qi::primitive_parser<csv_white_space_skipper>
|
struct csv_white_space_skipper : qi::primitive_parser<csv_white_space_skipper>
|
||||||
{
|
{
|
||||||
|
@ -110,4 +106,4 @@ private:
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // MAPNIK_CVS_GRAMMAR_HPP
|
#endif // MAPNIK_CSV_GRAMMAR_HPP
|
||||||
|
|
37
include/mapnik/csv/csv_types.hpp
Normal file
37
include/mapnik/csv/csv_types.hpp
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
/*****************************************************************************
|
||||||
|
*
|
||||||
|
* 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 MAPNIK_CSV_TYPES_HPP
|
||||||
|
#define MAPNIK_CSV_TYPES_HPP
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace mapnik {
|
||||||
|
|
||||||
|
using csv_value = std::string;
|
||||||
|
using csv_line = std::vector<csv_value>;
|
||||||
|
using csv_data = std::vector<csv_line>;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // MAPNIK_CSV_TYPES_HPP
|
|
@ -24,13 +24,18 @@
|
||||||
#define MAPNIK_JSON_ERROR_HANDLER_HPP
|
#define MAPNIK_JSON_ERROR_HANDLER_HPP
|
||||||
|
|
||||||
#include <mapnik/config.hpp>
|
#include <mapnik/config.hpp>
|
||||||
|
|
||||||
#pragma GCC diagnostic push
|
#pragma GCC diagnostic push
|
||||||
#include <mapnik/warning_ignore.hpp>
|
#include <mapnik/warning_ignore.hpp>
|
||||||
#include <boost/spirit/home/qi.hpp>
|
#include <boost/spirit/home/qi/nonterminal/error_handler.hpp>
|
||||||
#include <boost/spirit/home/support/info.hpp>
|
namespace boost { namespace spirit { struct info; } }
|
||||||
#pragma GCC diagnostic pop
|
#pragma GCC diagnostic pop
|
||||||
|
|
||||||
// mapnik
|
// mapnik
|
||||||
|
#ifdef MAPNIK_LOG
|
||||||
#include <mapnik/debug.hpp>
|
#include <mapnik/debug.hpp>
|
||||||
|
#endif
|
||||||
|
|
||||||
// stl
|
// stl
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
|
@ -33,12 +33,8 @@
|
||||||
#include <mapnik/warning_ignore.hpp>
|
#include <mapnik/warning_ignore.hpp>
|
||||||
#include <boost/spirit/include/qi.hpp>
|
#include <boost/spirit/include/qi.hpp>
|
||||||
#include <boost/spirit/include/phoenix_function.hpp>
|
#include <boost/spirit/include/phoenix_function.hpp>
|
||||||
#include <boost/fusion/adapted/std_tuple.hpp>
|
|
||||||
#pragma GCC diagnostic pop
|
#pragma GCC diagnostic pop
|
||||||
|
|
||||||
// stl
|
|
||||||
#include <tuple>
|
|
||||||
|
|
||||||
namespace mapnik { namespace json {
|
namespace mapnik { namespace json {
|
||||||
|
|
||||||
using position_type = mapnik::geometry::point<double>;
|
using position_type = mapnik::geometry::point<double>;
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
|
|
||||||
// mapnik
|
// mapnik
|
||||||
#include <mapnik/json/extract_bounding_box_grammar.hpp>
|
#include <mapnik/json/extract_bounding_box_grammar.hpp>
|
||||||
|
#include <mapnik/geometry_fusion_adapted.hpp>
|
||||||
// boost
|
// boost
|
||||||
#include <boost/spirit/include/qi_omit.hpp>
|
#include <boost/spirit/include/qi_omit.hpp>
|
||||||
#include <boost/spirit/include/phoenix_object.hpp>
|
#include <boost/spirit/include/phoenix_object.hpp>
|
||||||
|
|
|
@ -27,7 +27,6 @@
|
||||||
#include <mapnik/global.hpp>
|
#include <mapnik/global.hpp>
|
||||||
#include <mapnik/geometry.hpp>
|
#include <mapnik/geometry.hpp>
|
||||||
#include <mapnik/geometry_type.hpp>
|
#include <mapnik/geometry_type.hpp>
|
||||||
#include <mapnik/geometry_fusion_adapted.hpp>
|
|
||||||
// boost
|
// boost
|
||||||
#pragma GCC diagnostic push
|
#pragma GCC diagnostic push
|
||||||
#include <mapnik/warning_ignore.hpp>
|
#include <mapnik/warning_ignore.hpp>
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include <mapnik/json/geometry_generator_grammar.hpp>
|
#include <mapnik/json/geometry_generator_grammar.hpp>
|
||||||
#include <mapnik/util/spirit_transform_attribute.hpp>
|
#include <mapnik/util/spirit_transform_attribute.hpp>
|
||||||
#include <mapnik/geometry_types.hpp>
|
#include <mapnik/geometry_types.hpp>
|
||||||
|
#include <mapnik/geometry_fusion_adapted.hpp>
|
||||||
|
|
||||||
// boost
|
// boost
|
||||||
#pragma GCC diagnostic push
|
#pragma GCC diagnostic push
|
||||||
|
|
|
@ -30,9 +30,11 @@
|
||||||
#include <mapnik/json/positions_grammar.hpp>
|
#include <mapnik/json/positions_grammar.hpp>
|
||||||
#include <mapnik/json/geometry_util.hpp>
|
#include <mapnik/json/geometry_util.hpp>
|
||||||
|
|
||||||
// spirit::qi
|
#pragma GCC diagnostic push
|
||||||
|
#include <mapnik/warning_ignore.hpp>
|
||||||
#include <boost/spirit/include/qi.hpp>
|
#include <boost/spirit/include/qi.hpp>
|
||||||
#include <boost/spirit/include/phoenix_function.hpp>
|
#include <boost/spirit/include/phoenix_function.hpp>
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
|
|
@ -24,12 +24,10 @@
|
||||||
#include <mapnik/config.hpp>
|
#include <mapnik/config.hpp>
|
||||||
#include <mapnik/json/error_handler.hpp>
|
#include <mapnik/json/error_handler.hpp>
|
||||||
#include <mapnik/json/geometry_grammar.hpp>
|
#include <mapnik/json/geometry_grammar.hpp>
|
||||||
#include <mapnik/json/positions_grammar_impl.hpp>
|
#include <mapnik/geometry_fusion_adapted.hpp>
|
||||||
|
|
||||||
// boost
|
// boost
|
||||||
#include <boost/spirit/include/phoenix_object.hpp>
|
|
||||||
#include <boost/spirit/include/phoenix_stl.hpp>
|
#include <boost/spirit/include/phoenix_stl.hpp>
|
||||||
#include <boost/spirit/include/phoenix_operator.hpp>
|
#include <boost/spirit/include/phoenix_function.hpp>
|
||||||
|
|
||||||
namespace mapnik { namespace json {
|
namespace mapnik { namespace json {
|
||||||
|
|
||||||
|
|
|
@ -24,25 +24,13 @@
|
||||||
#define MAPNIK_JSON_GEOMETRY_PARSER_HPP
|
#define MAPNIK_JSON_GEOMETRY_PARSER_HPP
|
||||||
|
|
||||||
// mapnik
|
// mapnik
|
||||||
|
#include <mapnik/geometry.hpp>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
#include <mapnik/json/geometry_grammar.hpp>
|
|
||||||
|
|
||||||
// boost
|
|
||||||
#include <boost/spirit/include/qi.hpp>
|
|
||||||
#include <boost/spirit/include/phoenix_core.hpp>
|
|
||||||
|
|
||||||
namespace mapnik { namespace json {
|
namespace mapnik { namespace json {
|
||||||
|
|
||||||
inline bool from_geojson(std::string const& json, mapnik::geometry::geometry<double> & geom)
|
bool from_geojson(std::string const& json, mapnik::geometry::geometry<double> & geom);
|
||||||
{
|
|
||||||
using namespace boost::spirit;
|
|
||||||
static const geometry_grammar<char const*> g;
|
|
||||||
standard::space_type space;
|
|
||||||
char const* start = json.c_str();
|
|
||||||
char const* end = start + json.length();
|
|
||||||
return qi::phrase_parse(start, end, g, space, geom);
|
|
||||||
}
|
|
||||||
|
|
||||||
}}
|
}}
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
|
|
||||||
#include <mapnik/geometry.hpp>
|
#include <mapnik/geometry.hpp>
|
||||||
#include <mapnik/geometry_correct.hpp>
|
#include <mapnik/geometry_correct.hpp>
|
||||||
|
#include <mapnik/json/positions.hpp>
|
||||||
|
|
||||||
namespace mapnik { namespace json {
|
namespace mapnik { namespace json {
|
||||||
|
|
||||||
|
|
40
include/mapnik/json/positions.hpp
Normal file
40
include/mapnik/json/positions.hpp
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
/*****************************************************************************
|
||||||
|
*
|
||||||
|
* 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 MAPNIK_JSON_POSITIONS_HPP
|
||||||
|
#define MAPNIK_JSON_POSITIONS_HPP
|
||||||
|
|
||||||
|
// mapnik
|
||||||
|
#include <mapnik/util/variant.hpp>
|
||||||
|
#include <mapnik/geometry.hpp>
|
||||||
|
|
||||||
|
namespace mapnik { namespace json {
|
||||||
|
|
||||||
|
struct empty {};
|
||||||
|
|
||||||
|
using position = mapnik::geometry::point<double>;
|
||||||
|
using positions = std::vector<position>;
|
||||||
|
using coordinates = util::variant<empty, position, positions, std::vector<positions>, std::vector<std::vector<positions> > > ;
|
||||||
|
|
||||||
|
}}
|
||||||
|
|
||||||
|
#endif // MAPNIK_JSON_POSITIONS_HPP
|
|
@ -25,29 +25,19 @@
|
||||||
|
|
||||||
// mapnik
|
// mapnik
|
||||||
#include <mapnik/util/variant.hpp>
|
#include <mapnik/util/variant.hpp>
|
||||||
|
#include <mapnik/json/positions.hpp>
|
||||||
#include <mapnik/json/generic_json.hpp>
|
#include <mapnik/json/generic_json.hpp>
|
||||||
#include <mapnik/json/error_handler.hpp>
|
#include <mapnik/json/error_handler.hpp>
|
||||||
#include <mapnik/geometry.hpp>
|
#include <mapnik/geometry.hpp>
|
||||||
#include <mapnik/geometry_fusion_adapted.hpp>
|
|
||||||
|
|
||||||
#pragma GCC diagnostic push
|
#pragma GCC diagnostic push
|
||||||
#include <mapnik/warning_ignore.hpp>
|
#include <mapnik/warning_ignore.hpp>
|
||||||
#include <boost/spirit/include/qi.hpp>
|
#include <boost/spirit/include/qi.hpp>
|
||||||
#include <boost/spirit/include/phoenix_function.hpp>
|
#include <boost/spirit/include/phoenix_function.hpp>
|
||||||
#include <boost/fusion/adapted/std_tuple.hpp>
|
|
||||||
#pragma GCC diagnostic pop
|
#pragma GCC diagnostic pop
|
||||||
|
|
||||||
// stl
|
|
||||||
#include <tuple>
|
|
||||||
|
|
||||||
namespace mapnik { namespace json {
|
namespace mapnik { namespace json {
|
||||||
|
|
||||||
struct empty {};
|
|
||||||
|
|
||||||
using position = mapnik::geometry::point<double>;
|
|
||||||
using positions = std::vector<position>;
|
|
||||||
using coordinates = util::variant<empty, position, positions, std::vector<positions>, std::vector<std::vector<positions> > > ;
|
|
||||||
|
|
||||||
namespace qi = boost::spirit::qi;
|
namespace qi = boost::spirit::qi;
|
||||||
|
|
||||||
struct set_position_impl
|
struct set_position_impl
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
|
|
||||||
// mapnik
|
// mapnik
|
||||||
#include <mapnik/json/positions_grammar.hpp>
|
#include <mapnik/json/positions_grammar.hpp>
|
||||||
|
#include <mapnik/geometry_fusion_adapted.hpp>
|
||||||
// boost
|
// boost
|
||||||
#include <boost/spirit/include/qi_omit.hpp>
|
#include <boost/spirit/include/qi_omit.hpp>
|
||||||
#include <boost/spirit/include/phoenix_object.hpp>
|
#include <boost/spirit/include/phoenix_object.hpp>
|
||||||
|
|
|
@ -25,14 +25,12 @@
|
||||||
|
|
||||||
// mapnik
|
// mapnik
|
||||||
#include <mapnik/json/error_handler.hpp>
|
#include <mapnik/json/error_handler.hpp>
|
||||||
#include <mapnik/json/generic_json.hpp>
|
|
||||||
#include <mapnik/json/topology.hpp>
|
#include <mapnik/json/topology.hpp>
|
||||||
#include <mapnik/json/value_converters.hpp>
|
#include <mapnik/json/value_converters.hpp>
|
||||||
|
|
||||||
#pragma GCC diagnostic push
|
#pragma GCC diagnostic push
|
||||||
#include <mapnik/warning_ignore.hpp>
|
#include <mapnik/warning_ignore.hpp>
|
||||||
#include <boost/spirit/include/qi.hpp>
|
#include <boost/spirit/include/qi.hpp>
|
||||||
#include <boost/spirit/include/phoenix_function.hpp>
|
|
||||||
#pragma GCC diagnostic pop
|
#pragma GCC diagnostic pop
|
||||||
|
|
||||||
// stl
|
// stl
|
||||||
|
@ -50,8 +48,6 @@ struct topojson_grammar : qi::grammar<Iterator, space_type, topology()>
|
||||||
{
|
{
|
||||||
topojson_grammar();
|
topojson_grammar();
|
||||||
private:
|
private:
|
||||||
// generic JSON support
|
|
||||||
json::generic_json<Iterator> json_;
|
|
||||||
// topoJSON
|
// topoJSON
|
||||||
qi::rule<Iterator, space_type, mapnik::topojson::topology()> topology;
|
qi::rule<Iterator, space_type, mapnik::topojson::topology()> topology;
|
||||||
qi::rule<Iterator, space_type, std::vector<mapnik::topojson::geometry>()> objects;
|
qi::rule<Iterator, space_type, std::vector<mapnik::topojson::geometry>()> objects;
|
||||||
|
@ -68,17 +64,13 @@ private:
|
||||||
qi::rule<Iterator, space_type, mapnik::topojson::polygon()> polygon;
|
qi::rule<Iterator, space_type, mapnik::topojson::polygon()> polygon;
|
||||||
qi::rule<Iterator, space_type, mapnik::topojson::multi_polygon()> multi_polygon;
|
qi::rule<Iterator, space_type, mapnik::topojson::multi_polygon()> multi_polygon;
|
||||||
qi::rule<Iterator, space_type, void(std::vector<mapnik::topojson::geometry>&)> geometry_collection;
|
qi::rule<Iterator, space_type, void(std::vector<mapnik::topojson::geometry>&)> geometry_collection;
|
||||||
|
|
||||||
qi::rule<Iterator, space_type, std::vector<index_type>()> ring;
|
qi::rule<Iterator, space_type, std::vector<index_type>()> ring;
|
||||||
|
|
||||||
// properties
|
// properties
|
||||||
qi::rule<Iterator, space_type, mapnik::topojson::properties()> properties;
|
qi::rule<Iterator, space_type, mapnik::topojson::properties()> properties;
|
||||||
qi::rule<Iterator, space_type, mapnik::topojson::properties()> attributes;
|
qi::rule<Iterator, space_type, mapnik::topojson::properties()> attributes;
|
||||||
qi::rule<Iterator, space_type, mapnik::json::json_value()> attribute_value;
|
qi::rule<Iterator, space_type, mapnik::json::json_value()> attribute_value;
|
||||||
// id
|
// id
|
||||||
qi::rule<Iterator,space_type> id;
|
qi::rule<Iterator,space_type> id;
|
||||||
// error handler
|
|
||||||
boost::phoenix::function<ErrorHandler> const error_handler;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -21,6 +21,84 @@
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
#include <mapnik/json/topojson_grammar.hpp>
|
#include <mapnik/json/topojson_grammar.hpp>
|
||||||
|
#include <mapnik/json/generic_json.hpp>
|
||||||
|
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#include <boost/fusion/include/adapt_struct.hpp>
|
||||||
|
#include <boost/fusion/adapted/std_tuple.hpp>
|
||||||
|
#include <boost/spirit/include/phoenix_function.hpp>
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
|
||||||
|
BOOST_FUSION_ADAPT_STRUCT(
|
||||||
|
mapnik::topojson::coordinate,
|
||||||
|
(double, x)
|
||||||
|
(double, y)
|
||||||
|
)
|
||||||
|
|
||||||
|
BOOST_FUSION_ADAPT_STRUCT(
|
||||||
|
mapnik::topojson::arc,
|
||||||
|
(std::list<mapnik::topojson::coordinate>, coordinates)
|
||||||
|
)
|
||||||
|
|
||||||
|
BOOST_FUSION_ADAPT_STRUCT(
|
||||||
|
mapnik::topojson::transform,
|
||||||
|
(double, scale_x)
|
||||||
|
(double, scale_y)
|
||||||
|
(double, translate_x)
|
||||||
|
(double, translate_y)
|
||||||
|
)
|
||||||
|
|
||||||
|
BOOST_FUSION_ADAPT_STRUCT(
|
||||||
|
mapnik::topojson::bounding_box,
|
||||||
|
(double, minx)
|
||||||
|
(double, miny)
|
||||||
|
(double, maxx)
|
||||||
|
(double, maxy)
|
||||||
|
)
|
||||||
|
|
||||||
|
BOOST_FUSION_ADAPT_STRUCT(
|
||||||
|
mapnik::topojson::point,
|
||||||
|
(mapnik::topojson::coordinate, coord)
|
||||||
|
(boost::optional<mapnik::topojson::properties>, props)
|
||||||
|
)
|
||||||
|
|
||||||
|
BOOST_FUSION_ADAPT_STRUCT(
|
||||||
|
mapnik::topojson::multi_point,
|
||||||
|
(std::vector<mapnik::topojson::coordinate>, points)
|
||||||
|
(boost::optional<mapnik::topojson::properties>, props)
|
||||||
|
)
|
||||||
|
|
||||||
|
BOOST_FUSION_ADAPT_STRUCT(
|
||||||
|
mapnik::topojson::linestring,
|
||||||
|
(mapnik::topojson::index_type, ring)
|
||||||
|
(boost::optional<mapnik::topojson::properties>, props)
|
||||||
|
)
|
||||||
|
|
||||||
|
BOOST_FUSION_ADAPT_STRUCT(
|
||||||
|
mapnik::topojson::multi_linestring,
|
||||||
|
(std::vector<mapnik::topojson::index_type>, rings)
|
||||||
|
(boost::optional<mapnik::topojson::properties>, props)
|
||||||
|
)
|
||||||
|
|
||||||
|
BOOST_FUSION_ADAPT_STRUCT(
|
||||||
|
mapnik::topojson::polygon,
|
||||||
|
(std::vector<std::vector<mapnik::topojson::index_type> >, rings)
|
||||||
|
(boost::optional<mapnik::topojson::properties>, props)
|
||||||
|
)
|
||||||
|
|
||||||
|
BOOST_FUSION_ADAPT_STRUCT(
|
||||||
|
mapnik::topojson::multi_polygon,
|
||||||
|
(std::vector<std::vector<std::vector<mapnik::topojson::index_type> > >, polygons)
|
||||||
|
(boost::optional<mapnik::topojson::properties>, props)
|
||||||
|
)
|
||||||
|
|
||||||
|
BOOST_FUSION_ADAPT_STRUCT(
|
||||||
|
mapnik::topojson::topology,
|
||||||
|
(std::vector<mapnik::topojson::geometry>, geometries)
|
||||||
|
(std::vector<mapnik::topojson::arc>, arcs)
|
||||||
|
(boost::optional<mapnik::topojson::transform>, tr)
|
||||||
|
(boost::optional<mapnik::topojson::bounding_box>, bbox)
|
||||||
|
)
|
||||||
|
|
||||||
namespace mapnik { namespace topojson {
|
namespace mapnik { namespace topojson {
|
||||||
|
|
||||||
|
@ -46,26 +124,32 @@ topojson_grammar<Iterator, ErrorHandler>::topojson_grammar()
|
||||||
using qi::on_error;
|
using qi::on_error;
|
||||||
using phoenix::push_back;
|
using phoenix::push_back;
|
||||||
using phoenix::construct;
|
using phoenix::construct;
|
||||||
// generic json types
|
|
||||||
json_.value = json_.object | json_.array | json_.string_ | json_.number
|
// error handler
|
||||||
|
boost::phoenix::function<ErrorHandler> const error_handler;
|
||||||
|
|
||||||
|
// generic JSON support
|
||||||
|
json::generic_json<Iterator> json;
|
||||||
|
// generic JSON types
|
||||||
|
json.value = json.object | json.array | json.string_ | json.number
|
||||||
;
|
;
|
||||||
|
|
||||||
json_.pairs = json_.key_value % lit(',')
|
json.pairs = json.key_value % lit(',')
|
||||||
;
|
;
|
||||||
|
|
||||||
json_.key_value = (json_.string_ >> lit(':') >> json_.value)
|
json.key_value = (json.string_ >> lit(':') >> json.value)
|
||||||
;
|
;
|
||||||
|
|
||||||
json_.object = lit('{') >> *json_.pairs >> lit('}')
|
json.object = lit('{') >> *json.pairs >> lit('}')
|
||||||
;
|
;
|
||||||
|
|
||||||
json_.array = lit('[')
|
json.array = lit('[')
|
||||||
>> json_.value >> *(lit(',') >> json_.value)
|
>> json.value >> *(lit(',') >> json.value)
|
||||||
>> lit(']')
|
>> lit(']')
|
||||||
;
|
;
|
||||||
|
|
||||||
json_.number = json_.strict_double[_val = json_.double_converter(_1)]
|
json.number = json.strict_double[_val = json.double_converter(_1)]
|
||||||
| json_.int__[_val = json_.integer_converter(_1)]
|
| json.int__[_val = json.integer_converter(_1)]
|
||||||
| lit("true")[_val = true]
|
| lit("true")[_val = true]
|
||||||
| lit("false")[_val = false]
|
| lit("false")[_val = false]
|
||||||
| lit("null")[_val = construct<value_null>()]
|
| lit("null")[_val = construct<value_null>()]
|
||||||
|
@ -96,7 +180,7 @@ topojson_grammar<Iterator, ErrorHandler>::topojson_grammar()
|
||||||
objects = lit("\"objects\"")
|
objects = lit("\"objects\"")
|
||||||
>> lit(':')
|
>> lit(':')
|
||||||
>> lit('{')
|
>> lit('{')
|
||||||
>> -((omit[json_.string_]
|
>> -((omit[json.string_]
|
||||||
>> lit(':')
|
>> lit(':')
|
||||||
>> (geometry_collection(_val) | geometry)) % lit(','))
|
>> (geometry_collection(_val) | geometry)) % lit(','))
|
||||||
>> lit('}')
|
>> lit('}')
|
||||||
|
@ -109,7 +193,7 @@ topojson_grammar<Iterator, ErrorHandler>::topojson_grammar()
|
||||||
multi_point |
|
multi_point |
|
||||||
multi_linestring |
|
multi_linestring |
|
||||||
multi_polygon |
|
multi_polygon |
|
||||||
omit[json_.object]
|
omit[json.object]
|
||||||
;
|
;
|
||||||
|
|
||||||
geometry_collection = lit('{')
|
geometry_collection = lit('{')
|
||||||
|
@ -171,7 +255,7 @@ topojson_grammar<Iterator, ErrorHandler>::topojson_grammar()
|
||||||
>> lit('}')
|
>> lit('}')
|
||||||
;
|
;
|
||||||
|
|
||||||
id = lit("\"id\"") >> lit(':') >> omit[json_.value]
|
id = lit("\"id\"") >> lit(':') >> omit[json.value]
|
||||||
;
|
;
|
||||||
|
|
||||||
ring = lit('[') >> -(int_ % lit(',')) >> lit(']')
|
ring = lit('[') >> -(int_ % lit(',')) >> lit(']')
|
||||||
|
@ -179,13 +263,13 @@ topojson_grammar<Iterator, ErrorHandler>::topojson_grammar()
|
||||||
|
|
||||||
properties = lit("\"properties\"")
|
properties = lit("\"properties\"")
|
||||||
>> lit(':')
|
>> lit(':')
|
||||||
>> (( lit('{') >> attributes >> lit('}')) | json_.object)
|
>> (( lit('{') >> attributes >> lit('}')) | json.object)
|
||||||
;
|
;
|
||||||
|
|
||||||
attributes = (json_.string_ >> lit(':') >> attribute_value) % lit(',')
|
attributes = (json.string_ >> lit(':') >> attribute_value) % lit(',')
|
||||||
;
|
;
|
||||||
|
|
||||||
attribute_value %= json_.number | json_.string_ ;
|
attribute_value %= json.number | json.string_ ;
|
||||||
|
|
||||||
arcs = lit("\"arcs\"") >> lit(':')
|
arcs = lit("\"arcs\"") >> lit(':')
|
||||||
>> lit('[') >> -( arc % lit(',')) >> lit(']') ;
|
>> lit('[') >> -( arc % lit(',')) >> lit(']') ;
|
||||||
|
@ -199,7 +283,7 @@ topojson_grammar<Iterator, ErrorHandler>::topojson_grammar()
|
||||||
objects.name("objects");
|
objects.name("objects");
|
||||||
arc.name("arc");
|
arc.name("arc");
|
||||||
arcs.name("arcs");
|
arcs.name("arcs");
|
||||||
json_.value.name("value");
|
json.value.name("value");
|
||||||
coordinate.name("coordinate");
|
coordinate.name("coordinate");
|
||||||
|
|
||||||
point.name("point");
|
point.name("point");
|
||||||
|
|
|
@ -28,8 +28,6 @@
|
||||||
|
|
||||||
#pragma GCC diagnostic push
|
#pragma GCC diagnostic push
|
||||||
#include <mapnik/warning_ignore.hpp>
|
#include <mapnik/warning_ignore.hpp>
|
||||||
#include <boost/fusion/include/adapt_struct.hpp>
|
|
||||||
#include <boost/fusion/adapted/std_tuple.hpp>
|
|
||||||
#include <boost/optional.hpp>
|
#include <boost/optional.hpp>
|
||||||
#pragma GCC diagnostic pop
|
#pragma GCC diagnostic pop
|
||||||
|
|
||||||
|
@ -126,75 +124,4 @@ struct topology
|
||||||
|
|
||||||
}}
|
}}
|
||||||
|
|
||||||
BOOST_FUSION_ADAPT_STRUCT(
|
|
||||||
mapnik::topojson::coordinate,
|
|
||||||
(double, x)
|
|
||||||
(double, y)
|
|
||||||
)
|
|
||||||
|
|
||||||
BOOST_FUSION_ADAPT_STRUCT(
|
|
||||||
mapnik::topojson::arc,
|
|
||||||
(std::list<mapnik::topojson::coordinate>, coordinates)
|
|
||||||
)
|
|
||||||
|
|
||||||
BOOST_FUSION_ADAPT_STRUCT(
|
|
||||||
mapnik::topojson::transform,
|
|
||||||
(double, scale_x)
|
|
||||||
(double, scale_y)
|
|
||||||
(double, translate_x)
|
|
||||||
(double, translate_y)
|
|
||||||
)
|
|
||||||
|
|
||||||
BOOST_FUSION_ADAPT_STRUCT(
|
|
||||||
mapnik::topojson::bounding_box,
|
|
||||||
(double, minx)
|
|
||||||
(double, miny)
|
|
||||||
(double, maxx)
|
|
||||||
(double, maxy)
|
|
||||||
)
|
|
||||||
|
|
||||||
BOOST_FUSION_ADAPT_STRUCT(
|
|
||||||
mapnik::topojson::point,
|
|
||||||
(mapnik::topojson::coordinate, coord)
|
|
||||||
(boost::optional<mapnik::topojson::properties>, props)
|
|
||||||
)
|
|
||||||
|
|
||||||
BOOST_FUSION_ADAPT_STRUCT(
|
|
||||||
mapnik::topojson::multi_point,
|
|
||||||
(std::vector<mapnik::topojson::coordinate>, points)
|
|
||||||
(boost::optional<mapnik::topojson::properties>, props)
|
|
||||||
)
|
|
||||||
|
|
||||||
BOOST_FUSION_ADAPT_STRUCT(
|
|
||||||
mapnik::topojson::linestring,
|
|
||||||
(mapnik::topojson::index_type, ring)
|
|
||||||
(boost::optional<mapnik::topojson::properties>, props)
|
|
||||||
)
|
|
||||||
|
|
||||||
BOOST_FUSION_ADAPT_STRUCT(
|
|
||||||
mapnik::topojson::multi_linestring,
|
|
||||||
(std::vector<mapnik::topojson::index_type>, rings)
|
|
||||||
(boost::optional<mapnik::topojson::properties>, props)
|
|
||||||
)
|
|
||||||
|
|
||||||
BOOST_FUSION_ADAPT_STRUCT(
|
|
||||||
mapnik::topojson::polygon,
|
|
||||||
(std::vector<std::vector<mapnik::topojson::index_type> >, rings)
|
|
||||||
(boost::optional<mapnik::topojson::properties>, props)
|
|
||||||
)
|
|
||||||
|
|
||||||
BOOST_FUSION_ADAPT_STRUCT(
|
|
||||||
mapnik::topojson::multi_polygon,
|
|
||||||
(std::vector<std::vector<std::vector<mapnik::topojson::index_type> > >, polygons)
|
|
||||||
(boost::optional<mapnik::topojson::properties>, props)
|
|
||||||
)
|
|
||||||
|
|
||||||
BOOST_FUSION_ADAPT_STRUCT(
|
|
||||||
mapnik::topojson::topology,
|
|
||||||
(std::vector<mapnik::topojson::geometry>, geometries)
|
|
||||||
(std::vector<mapnik::topojson::arc>, arcs)
|
|
||||||
(boost::optional<mapnik::topojson::transform>, tr)
|
|
||||||
(boost::optional<mapnik::topojson::bounding_box>, bbox)
|
|
||||||
)
|
|
||||||
|
|
||||||
#endif // MAPNIK_TOPOLOGY_HPP
|
#endif // MAPNIK_TOPOLOGY_HPP
|
||||||
|
|
|
@ -36,7 +36,7 @@
|
||||||
#include <mapnik/vertex_processor.hpp>
|
#include <mapnik/vertex_processor.hpp>
|
||||||
#include <mapnik/renderer_common/apply_vertex_converter.hpp>
|
#include <mapnik/renderer_common/apply_vertex_converter.hpp>
|
||||||
#include <mapnik/renderer_common/render_markers_symbolizer.hpp>
|
#include <mapnik/renderer_common/render_markers_symbolizer.hpp>
|
||||||
|
#include <mapnik/vertex_converters.hpp>
|
||||||
// agg
|
// agg
|
||||||
#include "agg_trans_affine.h"
|
#include "agg_trans_affine.h"
|
||||||
|
|
||||||
|
@ -158,140 +158,29 @@ void setup_transform_scaling(agg::trans_affine & tr,
|
||||||
attributes const& vars,
|
attributes const& vars,
|
||||||
symbolizer_base const& sym);
|
symbolizer_base const& sym);
|
||||||
|
|
||||||
|
using vertex_converter_type = vertex_converter<clip_line_tag,
|
||||||
|
clip_poly_tag,
|
||||||
|
transform_tag,
|
||||||
|
affine_transform_tag,
|
||||||
|
simplify_tag,
|
||||||
|
smooth_tag,
|
||||||
|
offset_transform_tag>;
|
||||||
|
|
||||||
// Apply markers to a feature with multiple geometries
|
// Apply markers to a feature with multiple geometries
|
||||||
|
template <typename Processor>
|
||||||
|
void apply_markers_multi(feature_impl const& feature, attributes const& vars,
|
||||||
|
vertex_converter_type & converter, Processor & proc, symbolizer_base const& sym);
|
||||||
|
|
||||||
template <typename Converter, typename Processor>
|
|
||||||
void apply_markers_single(Converter & converter, Processor & proc, geometry::geometry<double> const& geom)
|
|
||||||
{
|
|
||||||
geometry::geometry_types type = geometry::geometry_type(geom);
|
|
||||||
|
|
||||||
if (type == geometry::geometry_types::Point)
|
using vector_dispatch_type = vector_markers_dispatch<mapnik::label_collision_detector4>;
|
||||||
{
|
using raster_dispatch_type = raster_markers_dispatch<mapnik::label_collision_detector4>;
|
||||||
geometry::point_vertex_adapter<double> va(geom.get<geometry::point<double>>());
|
|
||||||
converter.apply(va, proc);
|
|
||||||
}
|
|
||||||
else if (type == geometry::geometry_types::LineString)
|
|
||||||
{
|
|
||||||
geometry::line_string_vertex_adapter<double> va(geom.get<geometry::line_string<double>>());
|
|
||||||
converter.apply(va, proc);
|
|
||||||
}
|
|
||||||
else if (type == geometry::geometry_types::Polygon)
|
|
||||||
{
|
|
||||||
geometry::polygon_vertex_adapter<double> va(geom.get<geometry::polygon<double>>());
|
|
||||||
converter.apply(va, proc);
|
|
||||||
}
|
|
||||||
else if (type == geometry::geometry_types::MultiPoint)
|
|
||||||
{
|
|
||||||
for (auto const& pt : geom.get<geometry::multi_point<double>>())
|
|
||||||
{
|
|
||||||
geometry::point_vertex_adapter<double> va(pt);
|
|
||||||
converter.apply(va, proc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (type == geometry::geometry_types::MultiLineString)
|
|
||||||
{
|
|
||||||
for (auto const& line : geom.get<geometry::multi_line_string<double>>())
|
|
||||||
{
|
|
||||||
geometry::line_string_vertex_adapter<double> va(line);
|
|
||||||
converter.apply(va, proc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (type == geometry::geometry_types::MultiPolygon)
|
|
||||||
{
|
|
||||||
for (auto const& poly : geom.get<geometry::multi_polygon<double>>())
|
|
||||||
{
|
|
||||||
geometry::polygon_vertex_adapter<double> va(poly);
|
|
||||||
converter.apply(va, proc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Converter, typename Processor>
|
extern template void apply_markers_multi<vector_dispatch_type>(feature_impl const& feature, attributes const& vars,
|
||||||
void apply_markers_multi(feature_impl const& feature, attributes const& vars, Converter & converter, Processor & proc, symbolizer_base const& sym)
|
vertex_converter_type & converter, vector_dispatch_type & proc, symbolizer_base const& sym);
|
||||||
{
|
|
||||||
using apply_vertex_converter_type = detail::apply_vertex_converter<Converter,Processor>;
|
|
||||||
apply_vertex_converter_type apply(converter, proc);
|
|
||||||
auto const& geom = feature.get_geometry();
|
|
||||||
geometry::geometry_types type = geometry::geometry_type(geom);
|
|
||||||
|
|
||||||
if (type == geometry::geometry_types::Point
|
extern template void apply_markers_multi<raster_dispatch_type>(feature_impl const& feature, attributes const& vars,
|
||||||
||
|
vertex_converter_type & converter, raster_dispatch_type & proc, symbolizer_base const& sym);
|
||||||
type == geometry::geometry_types::LineString
|
|
||||||
||
|
|
||||||
type == geometry::geometry_types::Polygon)
|
|
||||||
{
|
|
||||||
apply_markers_single(converter, proc, geom);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
|
|
||||||
marker_multi_policy_enum multi_policy = get<marker_multi_policy_enum, keys::markers_multipolicy>(sym, feature, vars);
|
|
||||||
marker_placement_enum placement = get<marker_placement_enum, keys::markers_placement_type>(sym, feature, vars);
|
|
||||||
|
|
||||||
if (placement == MARKER_POINT_PLACEMENT &&
|
|
||||||
multi_policy == MARKER_WHOLE_MULTI)
|
|
||||||
{
|
|
||||||
geometry::point<double> pt;
|
|
||||||
// test if centroid is contained by bounding box
|
|
||||||
if (geometry::centroid(geom, pt) && converter.disp_.args_.bbox.contains(pt.x, pt.y))
|
|
||||||
{
|
|
||||||
// unset any clipping since we're now dealing with a point
|
|
||||||
converter.template unset<clip_poly_tag>();
|
|
||||||
geometry::point_vertex_adapter<double> va(pt);
|
|
||||||
converter.apply(va, proc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ((placement == MARKER_POINT_PLACEMENT || placement == MARKER_INTERIOR_PLACEMENT) &&
|
|
||||||
multi_policy == MARKER_LARGEST_MULTI)
|
|
||||||
{
|
|
||||||
// Only apply to path with largest envelope area
|
|
||||||
// TODO: consider using true area for polygon types
|
|
||||||
if (type == geometry::geometry_types::MultiPolygon)
|
|
||||||
{
|
|
||||||
geometry::multi_polygon<double> const& multi_poly = mapnik::util::get<geometry::multi_polygon<double> >(geom);
|
|
||||||
double maxarea = 0;
|
|
||||||
geometry::polygon<double> const* largest = 0;
|
|
||||||
for (geometry::polygon<double> const& poly : multi_poly)
|
|
||||||
{
|
|
||||||
box2d<double> bbox = geometry::envelope(poly);
|
|
||||||
double area = bbox.width() * bbox.height();
|
|
||||||
if (area > maxarea)
|
|
||||||
{
|
|
||||||
maxarea = area;
|
|
||||||
largest = &poly;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (largest)
|
|
||||||
{
|
|
||||||
geometry::polygon_vertex_adapter<double> va(*largest);
|
|
||||||
converter.apply(va, proc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
MAPNIK_LOG_WARN(marker_symbolizer) << "TODO: if you get here -> open an issue";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (multi_policy != MARKER_EACH_MULTI && placement != MARKER_POINT_PLACEMENT)
|
|
||||||
{
|
|
||||||
MAPNIK_LOG_WARN(marker_symbolizer) << "marker_multi_policy != 'each' has no effect with marker_placement != 'point'";
|
|
||||||
}
|
|
||||||
if (type == geometry::geometry_types::GeometryCollection)
|
|
||||||
{
|
|
||||||
for (auto const& g : geom.get<geometry::geometry_collection<double>>())
|
|
||||||
{
|
|
||||||
apply_markers_single(converter, proc, g);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
apply_markers_single(converter, proc, geom);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,6 @@
|
||||||
#include <mapnik/markers_placements/vertext_first.hpp>
|
#include <mapnik/markers_placements/vertext_first.hpp>
|
||||||
#include <mapnik/markers_placements/vertext_last.hpp>
|
#include <mapnik/markers_placements/vertext_last.hpp>
|
||||||
#include <mapnik/symbolizer_enumerations.hpp>
|
#include <mapnik/symbolizer_enumerations.hpp>
|
||||||
#include <mapnik/util/variant.hpp>
|
|
||||||
|
|
||||||
namespace mapnik
|
namespace mapnik
|
||||||
{
|
{
|
||||||
|
@ -38,70 +37,71 @@ template <typename Locator, typename Detector>
|
||||||
class markers_placement_finder : util::noncopyable
|
class markers_placement_finder : util::noncopyable
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using markers_placement = util::variant<markers_point_placement<Locator, Detector>,
|
using basic_placement = markers_basic_placement<Locator, Detector>;
|
||||||
markers_line_placement<Locator, Detector>,
|
|
||||||
markers_interior_placement<Locator, Detector>,
|
|
||||||
markers_vertex_first_placement<Locator, Detector>,
|
|
||||||
markers_vertex_last_placement<Locator, Detector>>;
|
|
||||||
|
|
||||||
class get_point_visitor
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
get_point_visitor(double &x, double &y, double &angle, bool ignore_placement)
|
|
||||||
: x_(x), y_(y), angle_(angle), ignore_placement_(ignore_placement)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
bool operator()(T &placement) const
|
|
||||||
{
|
|
||||||
return placement.get_point(x_, y_, angle_, ignore_placement_);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
double &x_, &y_, &angle_;
|
|
||||||
bool ignore_placement_;
|
|
||||||
};
|
|
||||||
|
|
||||||
markers_placement_finder(marker_placement_e placement_type,
|
markers_placement_finder(marker_placement_e placement_type,
|
||||||
Locator &locator,
|
Locator &locator,
|
||||||
Detector &detector,
|
Detector &detector,
|
||||||
markers_placement_params const& params)
|
markers_placement_params const& params)
|
||||||
: placement_(create(placement_type, locator, detector, params))
|
: active_placement_(nullptr)
|
||||||
{
|
{
|
||||||
|
switch (placement_type)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
case MARKER_POINT_PLACEMENT:
|
||||||
|
active_placement_ = construct(&point_, locator, detector, params);
|
||||||
|
break;
|
||||||
|
case MARKER_INTERIOR_PLACEMENT:
|
||||||
|
active_placement_ = construct(&interior_, locator, detector, params);
|
||||||
|
break;
|
||||||
|
case MARKER_LINE_PLACEMENT:
|
||||||
|
active_placement_ = construct(&line_, locator, detector, params);
|
||||||
|
break;
|
||||||
|
case MARKER_VERTEX_FIRST_PLACEMENT:
|
||||||
|
active_placement_ = construct(&vertex_first_, locator, detector, params);
|
||||||
|
break;
|
||||||
|
case MARKER_VERTEX_LAST_PLACEMENT:
|
||||||
|
active_placement_ = construct(&vertex_last_, locator, detector, params);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// previously placement-type constructors (markers_*_placement)
|
||||||
|
// rewound the locator; reasons for rewinding here instead:
|
||||||
|
// 1) so that nobody is tempted to call now-virtual rewind()
|
||||||
|
// in placement-type class constructors
|
||||||
|
// 2) it servers as a runtime check that the above switch isn't
|
||||||
|
// missing cases and active_placement_ points to an object
|
||||||
|
active_placement_->rewind();
|
||||||
|
}
|
||||||
|
|
||||||
|
~markers_placement_finder()
|
||||||
|
{
|
||||||
|
active_placement_->~basic_placement();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get next point where the marker should be placed. Returns true if a place is found, false if none is found.
|
// Get next point where the marker should be placed. Returns true if a place is found, false if none is found.
|
||||||
bool get_point(double &x, double &y, double &angle, bool ignore_placement)
|
bool get_point(double &x, double &y, double &angle, bool ignore_placement)
|
||||||
{
|
{
|
||||||
return util::apply_visitor(get_point_visitor(x, y, angle, ignore_placement), placement_);
|
return active_placement_->get_point(x, y, angle, ignore_placement);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Factory function for particular placement implementations.
|
basic_placement* active_placement_;
|
||||||
static markers_placement create(marker_placement_e placement_type,
|
|
||||||
Locator &locator,
|
|
||||||
Detector &detector,
|
|
||||||
markers_placement_params const& params)
|
|
||||||
{
|
|
||||||
switch (placement_type)
|
|
||||||
{
|
|
||||||
case MARKER_POINT_PLACEMENT:
|
|
||||||
return markers_point_placement<Locator, Detector>(locator,detector,params);
|
|
||||||
case MARKER_INTERIOR_PLACEMENT:
|
|
||||||
return markers_interior_placement<Locator, Detector>(locator,detector,params);
|
|
||||||
case MARKER_LINE_PLACEMENT:
|
|
||||||
return markers_line_placement<Locator, Detector>(locator,detector,params);
|
|
||||||
case MARKER_VERTEX_FIRST_PLACEMENT:
|
|
||||||
return markers_vertex_first_placement<Locator, Detector>(locator,detector,params);
|
|
||||||
case MARKER_VERTEX_LAST_PLACEMENT:
|
|
||||||
return markers_vertex_last_placement<Locator, Detector>(locator,detector,params);
|
|
||||||
default: // point
|
|
||||||
return markers_point_placement<Locator, Detector>(locator,detector,params);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
markers_placement placement_;
|
union
|
||||||
|
{
|
||||||
|
markers_point_placement<Locator, Detector> point_;
|
||||||
|
markers_line_placement<Locator, Detector> line_;
|
||||||
|
markers_interior_placement<Locator, Detector> interior_;
|
||||||
|
markers_vertex_first_placement<Locator, Detector> vertex_first_;
|
||||||
|
markers_vertex_last_placement<Locator, Detector> vertex_last_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static T* construct(T* what, Locator & locator, Detector & detector,
|
||||||
|
markers_placement_params const& params)
|
||||||
|
{
|
||||||
|
return new(what) T(locator, detector, params);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
153
include/mapnik/markers_placements/basic.hpp
Normal file
153
include/mapnik/markers_placements/basic.hpp
Normal file
|
@ -0,0 +1,153 @@
|
||||||
|
/*****************************************************************************
|
||||||
|
*
|
||||||
|
* This file is part of Mapnik (c++ mapping toolkit)
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 Artem Pavlenko
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef MAPNIK_MARKERS_PLACEMENTS_BASIC_HPP
|
||||||
|
#define MAPNIK_MARKERS_PLACEMENTS_BASIC_HPP
|
||||||
|
|
||||||
|
// mapnik
|
||||||
|
#include <mapnik/box2d.hpp>
|
||||||
|
#include <mapnik/symbolizer_enumerations.hpp>
|
||||||
|
#include <mapnik/util/math.hpp>
|
||||||
|
#include <mapnik/util/noncopyable.hpp>
|
||||||
|
|
||||||
|
// agg
|
||||||
|
#include "agg_basics.h"
|
||||||
|
#include "agg_trans_affine.h"
|
||||||
|
|
||||||
|
namespace mapnik {
|
||||||
|
|
||||||
|
struct markers_placement_params
|
||||||
|
{
|
||||||
|
box2d<double> size;
|
||||||
|
agg::trans_affine tr;
|
||||||
|
double spacing;
|
||||||
|
double max_error;
|
||||||
|
bool allow_overlap;
|
||||||
|
bool avoid_edges;
|
||||||
|
direction_enum direction;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Locator, typename Detector>
|
||||||
|
class markers_basic_placement : util::noncopyable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
markers_basic_placement(Locator & locator, Detector & detector,
|
||||||
|
markers_placement_params const& params)
|
||||||
|
: locator_(locator),
|
||||||
|
detector_(detector),
|
||||||
|
params_(params),
|
||||||
|
done_(false)
|
||||||
|
{
|
||||||
|
// no need to rewind locator here, markers_placement_finder
|
||||||
|
// does that after construction
|
||||||
|
}
|
||||||
|
|
||||||
|
markers_basic_placement(markers_basic_placement && ) = default;
|
||||||
|
|
||||||
|
virtual ~markers_basic_placement()
|
||||||
|
{
|
||||||
|
// empty but necessary
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start again at first marker. Returns the same list of markers only works when they were NOT added to the detector.
|
||||||
|
virtual void rewind()
|
||||||
|
{
|
||||||
|
locator_.rewind(0);
|
||||||
|
done_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get next point where the marker should be placed. Returns true if a place is found, false if none is found.
|
||||||
|
virtual bool get_point(double &x, double &y, double &angle, bool ignore_placement) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Locator & locator_;
|
||||||
|
Detector & detector_;
|
||||||
|
markers_placement_params const& params_;
|
||||||
|
bool done_;
|
||||||
|
|
||||||
|
// Rotates the size_ box and translates the position.
|
||||||
|
box2d<double> perform_transform(double angle, double dx, double dy) const
|
||||||
|
{
|
||||||
|
auto tr = params_.tr * agg::trans_affine_rotation(angle).translate(dx, dy);
|
||||||
|
return box2d<double>(params_.size, tr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks transformed box placement with collision detector.
|
||||||
|
// returns false if the box:
|
||||||
|
// - a) isn't wholly inside extent and avoid_edges == true
|
||||||
|
// - b) collides with something and allow_overlap == false
|
||||||
|
// otherwise returns true, and if ignore_placement == true,
|
||||||
|
// also adds the box to collision detector
|
||||||
|
bool push_to_detector(double x, double y, double angle, bool ignore_placement)
|
||||||
|
{
|
||||||
|
auto box = perform_transform(angle, x, y);
|
||||||
|
if (params_.avoid_edges && !detector_.extent().contains(box))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!params_.allow_overlap && !detector_.has_placement(box))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!ignore_placement)
|
||||||
|
{
|
||||||
|
detector_.insert(box);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool set_direction(double & angle) const
|
||||||
|
{
|
||||||
|
switch (params_.direction)
|
||||||
|
{
|
||||||
|
case DIRECTION_UP:
|
||||||
|
angle = 0;
|
||||||
|
return true;
|
||||||
|
case DIRECTION_DOWN:
|
||||||
|
angle = M_PI;
|
||||||
|
return true;
|
||||||
|
case DIRECTION_AUTO:
|
||||||
|
if (std::fabs(util::normalize_angle(angle)) > 0.5 * M_PI)
|
||||||
|
angle += M_PI;
|
||||||
|
return true;
|
||||||
|
case DIRECTION_AUTO_DOWN:
|
||||||
|
if (std::fabs(util::normalize_angle(angle)) < 0.5 * M_PI)
|
||||||
|
angle += M_PI;
|
||||||
|
return true;
|
||||||
|
case DIRECTION_LEFT:
|
||||||
|
angle += M_PI;
|
||||||
|
return true;
|
||||||
|
case DIRECTION_LEFT_ONLY:
|
||||||
|
angle += M_PI;
|
||||||
|
return std::fabs(util::normalize_angle(angle)) < 0.5 * M_PI;
|
||||||
|
case DIRECTION_RIGHT_ONLY:
|
||||||
|
return std::fabs(util::normalize_angle(angle)) < 0.5 * M_PI;
|
||||||
|
case DIRECTION_RIGHT:
|
||||||
|
default:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace mapnik
|
||||||
|
|
||||||
|
#endif // MAPNIK_MARKERS_PLACEMENTS_BASIC_HPP
|
|
@ -33,14 +33,8 @@ template <typename Locator, typename Detector>
|
||||||
class markers_interior_placement : public markers_point_placement<Locator, Detector>
|
class markers_interior_placement : public markers_point_placement<Locator, Detector>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
markers_interior_placement(Locator &locator, Detector &detector, markers_placement_params const& params)
|
using point_placement = markers_point_placement<Locator, Detector>;
|
||||||
: markers_point_placement<Locator, Detector>(locator, detector, params)
|
using point_placement::point_placement;
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
markers_interior_placement(markers_interior_placement && rhs)
|
|
||||||
: markers_point_placement<Locator, Detector>(std::move(rhs))
|
|
||||||
{}
|
|
||||||
|
|
||||||
bool get_point(double &x, double &y, double &angle, bool ignore_placement)
|
bool get_point(double &x, double &y, double &angle, bool ignore_placement)
|
||||||
{
|
{
|
||||||
|
@ -51,7 +45,7 @@ public:
|
||||||
|
|
||||||
if (this->locator_.type() == geometry::geometry_types::Point)
|
if (this->locator_.type() == geometry::geometry_types::Point)
|
||||||
{
|
{
|
||||||
return markers_point_placement<Locator, Detector>::get_point(x, y, angle, ignore_placement);
|
return point_placement::get_point(x, y, angle, ignore_placement);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->locator_.type() == geometry::geometry_types::LineString)
|
if (this->locator_.type() == geometry::geometry_types::LineString)
|
||||||
|
@ -73,20 +67,10 @@ public:
|
||||||
|
|
||||||
angle = 0;
|
angle = 0;
|
||||||
|
|
||||||
box2d<double> box = this->perform_transform(angle, x, y);
|
if (!this->push_to_detector(x, y, angle, ignore_placement))
|
||||||
if (this->params_.avoid_edges && !this->detector_.extent().contains(box))
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!this->params_.allow_overlap && !this->detector_.has_placement(box))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ignore_placement)
|
|
||||||
{
|
|
||||||
this->detector_.insert(box);
|
|
||||||
}
|
|
||||||
|
|
||||||
this->done_ = true;
|
this->done_ = true;
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -35,29 +35,23 @@ template <typename Locator, typename Detector>
|
||||||
class markers_line_placement : public markers_point_placement<Locator, Detector>
|
class markers_line_placement : public markers_point_placement<Locator, Detector>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
markers_line_placement(Locator &locator, Detector &detector, markers_placement_params const& params)
|
using point_placement = markers_point_placement<Locator, Detector>;
|
||||||
: markers_point_placement<Locator, Detector>(locator, detector, params),
|
using point_placement::point_placement;
|
||||||
|
|
||||||
|
markers_line_placement(Locator & locator, Detector & detector,
|
||||||
|
markers_placement_params const& params)
|
||||||
|
: point_placement(locator, detector, params),
|
||||||
first_point_(true),
|
first_point_(true),
|
||||||
spacing_(0.0),
|
spacing_(0.0),
|
||||||
marker_width_((params.size * params.tr).width()),
|
marker_width_((params.size * params.tr).width()),
|
||||||
path_(locator)
|
path_(locator)
|
||||||
{
|
{
|
||||||
spacing_ = params.spacing < 1 ? 100 : params.spacing;
|
spacing_ = params.spacing < 1 ? 100 : params.spacing;
|
||||||
rewind();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
markers_line_placement(markers_line_placement && rhs)
|
|
||||||
: markers_point_placement<Locator, Detector>(std::move(rhs)),
|
|
||||||
first_point_(std::move(rhs.first_point_)),
|
|
||||||
spacing_(std::move(rhs.spacing_)),
|
|
||||||
marker_width_(std::move(rhs.marker_width_)),
|
|
||||||
path_(std::move(rhs.path_))
|
|
||||||
{}
|
|
||||||
|
|
||||||
void rewind()
|
void rewind()
|
||||||
{
|
{
|
||||||
this->locator_.rewind(0);
|
point_placement::rewind();
|
||||||
this->done_ = false;
|
|
||||||
first_point_ = true;
|
first_point_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,7 +64,7 @@ public:
|
||||||
|
|
||||||
if (this->locator_.type() == geometry::geometry_types::Point)
|
if (this->locator_.type() == geometry::geometry_types::Point)
|
||||||
{
|
{
|
||||||
return markers_point_placement<Locator, Detector>::get_point(x, y, angle, ignore_placement);
|
return point_placement::get_point(x, y, angle, ignore_placement);
|
||||||
}
|
}
|
||||||
|
|
||||||
double move = spacing_;
|
double move = spacing_;
|
||||||
|
@ -102,16 +96,10 @@ public:
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
box2d<double> box = this->perform_transform(angle, x, y);
|
if (!this->push_to_detector(x, y, angle, ignore_placement))
|
||||||
if ((this->params_.avoid_edges && !this->detector_.extent().contains(box))
|
|
||||||
|| (!this->params_.allow_overlap && !this->detector_.has_placement(box)))
|
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!ignore_placement)
|
|
||||||
{
|
|
||||||
this->detector_.insert(box);
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,148 +23,54 @@
|
||||||
#ifndef MAPNIK_MARKERS_PLACEMENTS_POINT_HPP
|
#ifndef MAPNIK_MARKERS_PLACEMENTS_POINT_HPP
|
||||||
#define MAPNIK_MARKERS_PLACEMENTS_POINT_HPP
|
#define MAPNIK_MARKERS_PLACEMENTS_POINT_HPP
|
||||||
|
|
||||||
#include <mapnik/box2d.hpp>
|
|
||||||
#include <mapnik/geom_util.hpp>
|
#include <mapnik/geom_util.hpp>
|
||||||
#include <mapnik/geometry_types.hpp>
|
#include <mapnik/geometry_types.hpp>
|
||||||
#include <mapnik/util/math.hpp>
|
#include <mapnik/markers_placements/basic.hpp>
|
||||||
#include <mapnik/label_collision_detector.hpp>
|
|
||||||
#include <mapnik/symbolizer_enumerations.hpp>
|
|
||||||
#include <mapnik/util/noncopyable.hpp>
|
|
||||||
|
|
||||||
#include "agg_basics.h"
|
|
||||||
#include "agg_trans_affine.h"
|
|
||||||
|
|
||||||
#include <cmath>
|
|
||||||
|
|
||||||
namespace mapnik {
|
namespace mapnik {
|
||||||
|
|
||||||
struct markers_placement_params
|
|
||||||
{
|
|
||||||
box2d<double> size;
|
|
||||||
agg::trans_affine tr;
|
|
||||||
double spacing;
|
|
||||||
double max_error;
|
|
||||||
bool allow_overlap;
|
|
||||||
bool avoid_edges;
|
|
||||||
direction_enum direction;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Locator, typename Detector>
|
template <typename Locator, typename Detector>
|
||||||
class markers_point_placement : util::noncopyable
|
class markers_point_placement : public markers_basic_placement<Locator, Detector>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
markers_point_placement(Locator &locator, Detector &detector, markers_placement_params const& params)
|
using basic_placement = markers_basic_placement<Locator, Detector>;
|
||||||
: locator_(locator),
|
using basic_placement::basic_placement;
|
||||||
detector_(detector),
|
|
||||||
params_(params),
|
|
||||||
done_(false)
|
|
||||||
{
|
|
||||||
rewind();
|
|
||||||
}
|
|
||||||
|
|
||||||
markers_point_placement(markers_point_placement && rhs)
|
|
||||||
: locator_(rhs.locator_),
|
|
||||||
detector_(rhs.detector_),
|
|
||||||
params_(rhs.params_),
|
|
||||||
done_(rhs.done_)
|
|
||||||
{}
|
|
||||||
|
|
||||||
|
|
||||||
// Start again at first marker. Returns the same list of markers only works when they were NOT added to the detector.
|
|
||||||
void rewind()
|
|
||||||
{
|
|
||||||
locator_.rewind(0);
|
|
||||||
done_ = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get next point where the marker should be placed. Returns true if a place is found, false if none is found.
|
// Get next point where the marker should be placed. Returns true if a place is found, false if none is found.
|
||||||
bool get_point(double &x, double &y, double &angle, bool ignore_placement)
|
bool get_point(double &x, double &y, double &angle, bool ignore_placement)
|
||||||
{
|
{
|
||||||
if (done_)
|
if (this->done_)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (locator_.type() == geometry::geometry_types::LineString)
|
if (this->locator_.type() == geometry::geometry_types::LineString)
|
||||||
{
|
{
|
||||||
if (!label::middle_point(locator_, x, y))
|
if (!label::middle_point(this->locator_, x, y))
|
||||||
{
|
{
|
||||||
done_ = true;
|
this->done_ = true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!label::centroid(locator_, x, y))
|
if (!label::centroid(this->locator_, x, y))
|
||||||
{
|
{
|
||||||
done_ = true;
|
this->done_ = true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
angle = 0;
|
angle = 0;
|
||||||
box2d<double> box = perform_transform(angle, x, y);
|
|
||||||
|
|
||||||
if (params_.avoid_edges && !detector_.extent().contains(box))
|
if (!this->push_to_detector(x, y, angle, ignore_placement))
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!params_.allow_overlap && !detector_.has_placement(box))
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ignore_placement)
|
this->done_ = true;
|
||||||
{
|
|
||||||
detector_.insert(box);
|
|
||||||
}
|
|
||||||
|
|
||||||
done_ = true;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
|
||||||
Locator &locator_;
|
|
||||||
Detector &detector_;
|
|
||||||
markers_placement_params const& params_;
|
|
||||||
bool done_;
|
|
||||||
|
|
||||||
// Rotates the size_ box and translates the position.
|
|
||||||
box2d<double> perform_transform(double angle, double dx, double dy)
|
|
||||||
{
|
|
||||||
agg::trans_affine tr = params_.tr * agg::trans_affine_rotation(angle).translate(dx, dy);
|
|
||||||
return box2d<double>(params_.size, tr);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool set_direction(double & angle)
|
|
||||||
{
|
|
||||||
switch (params_.direction)
|
|
||||||
{
|
|
||||||
case DIRECTION_UP:
|
|
||||||
angle = .0;
|
|
||||||
return true;
|
|
||||||
case DIRECTION_DOWN:
|
|
||||||
angle = M_PI;
|
|
||||||
return true;
|
|
||||||
case DIRECTION_AUTO:
|
|
||||||
angle = (std::fabs(util::normalize_angle(angle)) > 0.5 * M_PI) ? (angle + M_PI) : angle;
|
|
||||||
return true;
|
|
||||||
case DIRECTION_AUTO_DOWN:
|
|
||||||
angle = (std::fabs(util::normalize_angle(angle)) < 0.5 * M_PI) ? (angle + M_PI) : angle;
|
|
||||||
return true;
|
|
||||||
case DIRECTION_LEFT:
|
|
||||||
angle += M_PI;
|
|
||||||
return true;
|
|
||||||
case DIRECTION_LEFT_ONLY:
|
|
||||||
angle += M_PI;
|
|
||||||
return std::fabs(util::normalize_angle(angle)) < 0.5 * M_PI;
|
|
||||||
case DIRECTION_RIGHT_ONLY:
|
|
||||||
return std::fabs(util::normalize_angle(angle)) < 0.5 * M_PI;
|
|
||||||
case DIRECTION_RIGHT:
|
|
||||||
default:
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,14 +31,8 @@ template <typename Locator, typename Detector>
|
||||||
class markers_vertex_first_placement : public markers_point_placement<Locator, Detector>
|
class markers_vertex_first_placement : public markers_point_placement<Locator, Detector>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
markers_vertex_first_placement(Locator &locator, Detector &detector, markers_placement_params const& params)
|
using point_placement = markers_point_placement<Locator, Detector>;
|
||||||
: markers_point_placement<Locator, Detector>(locator, detector, params)
|
using point_placement::point_placement;
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
markers_vertex_first_placement(markers_vertex_first_placement && rhs)
|
|
||||||
: markers_point_placement<Locator, Detector>(std::move(rhs))
|
|
||||||
{}
|
|
||||||
|
|
||||||
bool get_point(double &x, double &y, double &angle, bool ignore_placement)
|
bool get_point(double &x, double &y, double &angle, bool ignore_placement)
|
||||||
{
|
{
|
||||||
|
@ -49,7 +43,7 @@ public:
|
||||||
|
|
||||||
if (this->locator_.type() == mapnik::geometry::geometry_types::Point)
|
if (this->locator_.type() == mapnik::geometry::geometry_types::Point)
|
||||||
{
|
{
|
||||||
return markers_point_placement<Locator, Detector>::get_point(x, y, angle, ignore_placement);
|
return point_placement::get_point(x, y, angle, ignore_placement);
|
||||||
}
|
}
|
||||||
|
|
||||||
double x0, y0;
|
double x0, y0;
|
||||||
|
@ -75,20 +69,10 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
box2d<double> box = this->perform_transform(angle, x, y);
|
if (!this->push_to_detector(x, y, angle, ignore_placement))
|
||||||
if (this->params_.avoid_edges && !this->detector_.extent().contains(box))
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!this->params_.allow_overlap && !this->detector_.has_placement(box))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ignore_placement)
|
|
||||||
{
|
|
||||||
this->detector_.insert(box);
|
|
||||||
}
|
|
||||||
|
|
||||||
this->done_ = true;
|
this->done_ = true;
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -31,13 +31,8 @@ template <typename Locator, typename Detector>
|
||||||
class markers_vertex_last_placement : public markers_point_placement<Locator, Detector>
|
class markers_vertex_last_placement : public markers_point_placement<Locator, Detector>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
markers_vertex_last_placement(Locator &locator, Detector &detector, markers_placement_params const& params)
|
using point_placement = markers_point_placement<Locator, Detector>;
|
||||||
: markers_point_placement<Locator, Detector>(locator, detector, params)
|
using point_placement::point_placement;
|
||||||
{}
|
|
||||||
|
|
||||||
markers_vertex_last_placement(markers_vertex_last_placement && rhs)
|
|
||||||
: markers_point_placement<Locator, Detector>(std::move(rhs))
|
|
||||||
{}
|
|
||||||
|
|
||||||
bool get_point(double &x, double &y, double &angle, bool ignore_placement)
|
bool get_point(double &x, double &y, double &angle, bool ignore_placement)
|
||||||
{
|
{
|
||||||
|
@ -80,20 +75,10 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
box2d<double> box = this->perform_transform(angle, x, y);
|
if (!this->push_to_detector(x, y, angle, ignore_placement))
|
||||||
if (this->params_.avoid_edges && !this->detector_.extent().contains(box))
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!this->params_.allow_overlap && !this->detector_.has_placement(box))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ignore_placement)
|
|
||||||
{
|
|
||||||
this->detector_.insert(box);
|
|
||||||
}
|
|
||||||
|
|
||||||
this->done_ = true;
|
this->done_ = true;
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -40,53 +40,8 @@ namespace mapnik { namespace svg {
|
||||||
#include <mapnik/warning_ignore.hpp>
|
#include <mapnik/warning_ignore.hpp>
|
||||||
#include <boost/spirit/include/karma_nonterminal.hpp>
|
#include <boost/spirit/include/karma_nonterminal.hpp>
|
||||||
#include <boost/spirit/include/karma_rule.hpp>
|
#include <boost/spirit/include/karma_rule.hpp>
|
||||||
#include <boost/fusion/adapted/struct.hpp>
|
|
||||||
#pragma GCC diagnostic pop
|
#pragma GCC diagnostic pop
|
||||||
|
|
||||||
/*!
|
|
||||||
* mapnik::svg::path_output_attributes is adapted as a fusion sequence
|
|
||||||
* in order to be used directly by the svg_path_attributes_grammar (below).
|
|
||||||
*
|
|
||||||
* This adaptation is the primary reason why the attributes are stored in
|
|
||||||
* this structure before being passed to the generate_path method.
|
|
||||||
*/
|
|
||||||
BOOST_FUSION_ADAPT_STRUCT(
|
|
||||||
mapnik::svg::path_output_attributes,
|
|
||||||
(std::string, fill_color_)
|
|
||||||
(double, fill_opacity_)
|
|
||||||
(std::string, stroke_color_)
|
|
||||||
(double, stroke_opacity_)
|
|
||||||
(double, stroke_width_)
|
|
||||||
(std::string, stroke_linecap_)
|
|
||||||
(std::string, stroke_linejoin_)
|
|
||||||
(double, stroke_dashoffset_)
|
|
||||||
);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* mapnik::svg::rect_output_attributes is adapted as a fusion sequence
|
|
||||||
* in order to be used directly by the svg_rect_attributes_grammar (below).
|
|
||||||
*/
|
|
||||||
BOOST_FUSION_ADAPT_STRUCT(
|
|
||||||
mapnik::svg::rect_output_attributes,
|
|
||||||
(int, x_)
|
|
||||||
(int, y_)
|
|
||||||
(unsigned, width_)
|
|
||||||
(unsigned, height_)
|
|
||||||
(std::string, fill_color_)
|
|
||||||
);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* mapnik::svg::root_output_attributes is adapted as a fusion sequence
|
|
||||||
* in order to be used directly by the svg_root_attributes_grammar (below).
|
|
||||||
*/
|
|
||||||
BOOST_FUSION_ADAPT_STRUCT(
|
|
||||||
mapnik::svg::root_output_attributes,
|
|
||||||
(unsigned, width_)
|
|
||||||
(unsigned, height_)
|
|
||||||
(double, svg_version_)
|
|
||||||
(std::string, svg_namespace_url_)
|
|
||||||
);
|
|
||||||
|
|
||||||
namespace mapnik { namespace svg {
|
namespace mapnik { namespace svg {
|
||||||
|
|
||||||
using namespace boost::spirit;
|
using namespace boost::spirit;
|
||||||
|
|
|
@ -30,8 +30,53 @@
|
||||||
#include <mapnik/warning_ignore.hpp>
|
#include <mapnik/warning_ignore.hpp>
|
||||||
#include <boost/spirit/include/karma.hpp>
|
#include <boost/spirit/include/karma.hpp>
|
||||||
#include <boost/fusion/include/std_pair.hpp>
|
#include <boost/fusion/include/std_pair.hpp>
|
||||||
|
#include <boost/fusion/adapted/struct.hpp>
|
||||||
#pragma GCC diagnostic pop
|
#pragma GCC diagnostic pop
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* mapnik::svg::path_output_attributes is adapted as a fusion sequence
|
||||||
|
* in order to be used directly by the svg_path_attributes_grammar (below).
|
||||||
|
*
|
||||||
|
* This adaptation is the primary reason why the attributes are stored in
|
||||||
|
* this structure before being passed to the generate_path method.
|
||||||
|
*/
|
||||||
|
BOOST_FUSION_ADAPT_STRUCT(
|
||||||
|
mapnik::svg::path_output_attributes,
|
||||||
|
(std::string, fill_color_)
|
||||||
|
(double, fill_opacity_)
|
||||||
|
(std::string, stroke_color_)
|
||||||
|
(double, stroke_opacity_)
|
||||||
|
(double, stroke_width_)
|
||||||
|
(std::string, stroke_linecap_)
|
||||||
|
(std::string, stroke_linejoin_)
|
||||||
|
(double, stroke_dashoffset_)
|
||||||
|
);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* mapnik::svg::rect_output_attributes is adapted as a fusion sequence
|
||||||
|
* in order to be used directly by the svg_rect_attributes_grammar (below).
|
||||||
|
*/
|
||||||
|
BOOST_FUSION_ADAPT_STRUCT(
|
||||||
|
mapnik::svg::rect_output_attributes,
|
||||||
|
(int, x_)
|
||||||
|
(int, y_)
|
||||||
|
(unsigned, width_)
|
||||||
|
(unsigned, height_)
|
||||||
|
(std::string, fill_color_)
|
||||||
|
);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* mapnik::svg::root_output_attributes is adapted as a fusion sequence
|
||||||
|
* in order to be used directly by the svg_root_attributes_grammar (below).
|
||||||
|
*/
|
||||||
|
BOOST_FUSION_ADAPT_STRUCT(
|
||||||
|
mapnik::svg::root_output_attributes,
|
||||||
|
(unsigned, width_)
|
||||||
|
(unsigned, height_)
|
||||||
|
(double, svg_version_)
|
||||||
|
(std::string, svg_namespace_url_)
|
||||||
|
);
|
||||||
|
|
||||||
namespace mapnik { namespace svg {
|
namespace mapnik { namespace svg {
|
||||||
|
|
||||||
using namespace boost::spirit;
|
using namespace boost::spirit;
|
||||||
|
|
|
@ -45,6 +45,10 @@ public:
|
||||||
private:
|
private:
|
||||||
UConverter * conv_;
|
UConverter * conv_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// convinience method
|
||||||
|
void MAPNIK_DECL to_utf8(mapnik::value_unicode_string const& input, std::string & target);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // MAPNIK_UNICODE_HPP
|
#endif // MAPNIK_UNICODE_HPP
|
||||||
|
|
|
@ -24,18 +24,13 @@
|
||||||
#define MAPNIK_GEOMETRY_TO_GEOJSON_HPP
|
#define MAPNIK_GEOMETRY_TO_GEOJSON_HPP
|
||||||
|
|
||||||
// mapnik
|
// mapnik
|
||||||
|
#include <mapnik/geometry.hpp>
|
||||||
|
|
||||||
#include <mapnik/json/geometry_generator_grammar.hpp>
|
#include <string>
|
||||||
|
|
||||||
namespace mapnik { namespace util {
|
namespace mapnik { namespace util {
|
||||||
|
|
||||||
inline bool to_geojson(std::string & json, mapnik::geometry::geometry<double> const& geom)
|
bool to_geojson(std::string & json, mapnik::geometry::geometry<double> const& geom);
|
||||||
{
|
|
||||||
using sink_type = std::back_insert_iterator<std::string>;
|
|
||||||
static const mapnik::json::geometry_generator_grammar<sink_type, mapnik::geometry::geometry<double> > grammar;
|
|
||||||
sink_type sink(json);
|
|
||||||
return boost::spirit::karma::generate(sink, grammar, geom);
|
|
||||||
}
|
|
||||||
|
|
||||||
}}
|
}}
|
||||||
|
|
||||||
|
|
|
@ -38,11 +38,11 @@ namespace value_adl_barrier {
|
||||||
|
|
||||||
class MAPNIK_DECL value : public value_base
|
class MAPNIK_DECL value : public value_base
|
||||||
{
|
{
|
||||||
friend value operator+(value const&,value const&);
|
friend MAPNIK_DECL value operator+(value const&,value const&);
|
||||||
friend value operator-(value const&,value const&);
|
friend MAPNIK_DECL value operator-(value const&,value const&);
|
||||||
friend value operator*(value const&,value const&);
|
friend MAPNIK_DECL value operator*(value const&,value const&);
|
||||||
friend value operator/(value const&,value const&);
|
friend MAPNIK_DECL value operator/(value const&,value const&);
|
||||||
friend value operator%(value const&,value const&);
|
friend MAPNIK_DECL value operator%(value const&,value const&);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
value() = default;
|
value() = default;
|
||||||
|
|
|
@ -34,32 +34,10 @@ struct point_vertex_adapter
|
||||||
{
|
{
|
||||||
using value_type = typename point<T>::value_type;
|
using value_type = typename point<T>::value_type;
|
||||||
|
|
||||||
point_vertex_adapter(point<T> const& pt)
|
point_vertex_adapter(point<T> const& pt);
|
||||||
: pt_(pt),
|
unsigned vertex(value_type * x, value_type * y) const;
|
||||||
first_(true) {}
|
void rewind(unsigned) const;
|
||||||
|
geometry_types type () const;
|
||||||
unsigned vertex(value_type * x, value_type * y) const
|
|
||||||
{
|
|
||||||
if (first_)
|
|
||||||
{
|
|
||||||
*x = pt_.x;
|
|
||||||
*y = pt_.y;
|
|
||||||
first_ = false;
|
|
||||||
return mapnik::SEG_MOVETO;
|
|
||||||
}
|
|
||||||
return mapnik::SEG_END;
|
|
||||||
}
|
|
||||||
|
|
||||||
void rewind(unsigned) const
|
|
||||||
{
|
|
||||||
first_ = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline geometry_types type () const
|
|
||||||
{
|
|
||||||
return geometry_types::Point;
|
|
||||||
}
|
|
||||||
|
|
||||||
point<T> const& pt_;
|
point<T> const& pt_;
|
||||||
mutable bool first_;
|
mutable bool first_;
|
||||||
};
|
};
|
||||||
|
@ -68,110 +46,23 @@ template <typename T>
|
||||||
struct line_string_vertex_adapter
|
struct line_string_vertex_adapter
|
||||||
{
|
{
|
||||||
using value_type = typename point<T>::value_type;
|
using value_type = typename point<T>::value_type;
|
||||||
line_string_vertex_adapter(line_string<T> const& line)
|
line_string_vertex_adapter(line_string<T> const& line);
|
||||||
: line_(line),
|
unsigned vertex(value_type * x, value_type * y) const;
|
||||||
current_index_(0),
|
void rewind(unsigned) const;
|
||||||
end_index_(line.size())
|
geometry_types type () const;
|
||||||
{}
|
|
||||||
|
|
||||||
unsigned vertex(value_type * x, value_type * y) const
|
|
||||||
{
|
|
||||||
if (current_index_ != end_index_)
|
|
||||||
{
|
|
||||||
point<T> const& coord = line_[current_index_++];
|
|
||||||
*x = coord.x;
|
|
||||||
*y = coord.y;
|
|
||||||
if (current_index_ == 1)
|
|
||||||
{
|
|
||||||
return mapnik::SEG_MOVETO;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return mapnik::SEG_LINETO;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return mapnik::SEG_END;
|
|
||||||
}
|
|
||||||
|
|
||||||
void rewind(unsigned) const
|
|
||||||
{
|
|
||||||
current_index_ = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline geometry_types type () const
|
|
||||||
{
|
|
||||||
return geometry_types::LineString;
|
|
||||||
}
|
|
||||||
|
|
||||||
line_string<T> const& line_;
|
line_string<T> const& line_;
|
||||||
mutable std::size_t current_index_;
|
mutable std::size_t current_index_;
|
||||||
const std::size_t end_index_;
|
const std::size_t end_index_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct polygon_vertex_adapter
|
struct polygon_vertex_adapter
|
||||||
{
|
{
|
||||||
using value_type = typename point<T>::value_type;
|
using value_type = typename point<T>::value_type;
|
||||||
polygon_vertex_adapter(polygon<T> const& poly)
|
polygon_vertex_adapter(polygon<T> const& poly);
|
||||||
: poly_(poly),
|
void rewind(unsigned) const;
|
||||||
rings_itr_(0),
|
unsigned vertex(value_type * x, value_type * y) const;
|
||||||
rings_end_(poly_.interior_rings.size() + 1),
|
geometry_types type () const;
|
||||||
current_index_(0),
|
|
||||||
end_index_((rings_itr_ < rings_end_) ? poly_.exterior_ring.size() : 0),
|
|
||||||
start_loop_(true) {}
|
|
||||||
|
|
||||||
void rewind(unsigned) const
|
|
||||||
{
|
|
||||||
rings_itr_ = 0;
|
|
||||||
rings_end_ = poly_.interior_rings.size() + 1;
|
|
||||||
current_index_ = 0;
|
|
||||||
end_index_ = (rings_itr_ < rings_end_) ? poly_.exterior_ring.size() : 0;
|
|
||||||
start_loop_ = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned vertex(value_type * x, value_type * y) const
|
|
||||||
{
|
|
||||||
if (rings_itr_ == rings_end_)
|
|
||||||
{
|
|
||||||
return mapnik::SEG_END;
|
|
||||||
}
|
|
||||||
if (current_index_ < end_index_)
|
|
||||||
{
|
|
||||||
point<T> const& coord = (rings_itr_ == 0) ?
|
|
||||||
poly_.exterior_ring[current_index_++] : poly_.interior_rings[rings_itr_- 1][current_index_++];
|
|
||||||
*x = coord.x;
|
|
||||||
*y = coord.y;
|
|
||||||
if (start_loop_)
|
|
||||||
{
|
|
||||||
start_loop_= false;
|
|
||||||
return mapnik::SEG_MOVETO;
|
|
||||||
}
|
|
||||||
if (current_index_ == end_index_)
|
|
||||||
{
|
|
||||||
*x = 0;
|
|
||||||
*y = 0;
|
|
||||||
return mapnik::SEG_CLOSE;
|
|
||||||
}
|
|
||||||
return mapnik::SEG_LINETO;
|
|
||||||
}
|
|
||||||
else if (++rings_itr_ != rings_end_)
|
|
||||||
{
|
|
||||||
current_index_ = 0;
|
|
||||||
end_index_ = poly_.interior_rings[rings_itr_ - 1].size();
|
|
||||||
point<T> const& coord = poly_.interior_rings[rings_itr_ - 1][current_index_++];
|
|
||||||
*x = coord.x;
|
|
||||||
*y = coord.y;
|
|
||||||
return mapnik::SEG_MOVETO;
|
|
||||||
}
|
|
||||||
return mapnik::SEG_END;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline geometry_types type () const
|
|
||||||
{
|
|
||||||
return geometry_types::Polygon;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
polygon<T> const& poly_;
|
polygon<T> const& poly_;
|
||||||
mutable std::size_t rings_itr_;
|
mutable std::size_t rings_itr_;
|
||||||
|
@ -185,47 +76,10 @@ template <typename T>
|
||||||
struct ring_vertex_adapter
|
struct ring_vertex_adapter
|
||||||
{
|
{
|
||||||
using value_type = typename point<T>::value_type;
|
using value_type = typename point<T>::value_type;
|
||||||
ring_vertex_adapter(linear_ring<T> const& ring)
|
ring_vertex_adapter(linear_ring<T> const& ring);
|
||||||
: ring_(ring),
|
void rewind(unsigned) const;
|
||||||
current_index_(0),
|
unsigned vertex(value_type * x, value_type * y) const;
|
||||||
end_index_(ring_.size()),
|
geometry_types type () const;
|
||||||
start_loop_(true) {}
|
|
||||||
|
|
||||||
void rewind(unsigned) const
|
|
||||||
{
|
|
||||||
current_index_ = 0;
|
|
||||||
end_index_ = ring_.size();
|
|
||||||
start_loop_ = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned vertex(value_type * x, value_type * y) const
|
|
||||||
{
|
|
||||||
if (current_index_ < end_index_)
|
|
||||||
{
|
|
||||||
auto const& coord = ring_[current_index_++];
|
|
||||||
*x = coord.x;
|
|
||||||
*y = coord.y;
|
|
||||||
if (start_loop_)
|
|
||||||
{
|
|
||||||
start_loop_= false;
|
|
||||||
return mapnik::SEG_MOVETO;
|
|
||||||
}
|
|
||||||
if (current_index_ == end_index_)
|
|
||||||
{
|
|
||||||
*x = 0;
|
|
||||||
*y = 0;
|
|
||||||
return mapnik::SEG_CLOSE;
|
|
||||||
}
|
|
||||||
return mapnik::SEG_LINETO;
|
|
||||||
}
|
|
||||||
return mapnik::SEG_END;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline geometry_types type () const
|
|
||||||
{
|
|
||||||
return geometry_types::Polygon;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
linear_ring<T> const& ring_;
|
linear_ring<T> const& ring_;
|
||||||
mutable std::size_t current_index_;
|
mutable std::size_t current_index_;
|
||||||
|
@ -233,6 +87,11 @@ private:
|
||||||
mutable bool start_loop_;
|
mutable bool start_loop_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
extern template struct MAPNIK_DECL point_vertex_adapter<double>;
|
||||||
|
extern template struct MAPNIK_DECL line_string_vertex_adapter<double>;
|
||||||
|
extern template struct MAPNIK_DECL polygon_vertex_adapter<double>;
|
||||||
|
extern template struct MAPNIK_DECL ring_vertex_adapter<double>;
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct vertex_adapter_traits {};
|
struct vertex_adapter_traits {};
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,6 @@
|
||||||
#include <mapnik/global.hpp>
|
#include <mapnik/global.hpp>
|
||||||
#include <mapnik/geometry.hpp>
|
#include <mapnik/geometry.hpp>
|
||||||
#include <mapnik/geometry_type.hpp>
|
#include <mapnik/geometry_type.hpp>
|
||||||
#include <mapnik/geometry_fusion_adapted.hpp>
|
|
||||||
|
|
||||||
#pragma GCC diagnostic push
|
#pragma GCC diagnostic push
|
||||||
#include <mapnik/warning_ignore.hpp>
|
#include <mapnik/warning_ignore.hpp>
|
||||||
|
|
|
@ -20,9 +20,10 @@
|
||||||
*
|
*
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
#include <mapnik/geometry.hpp>
|
// mapnik
|
||||||
#include <mapnik/wkt/wkt_generator_grammar.hpp>
|
#include <mapnik/wkt/wkt_generator_grammar.hpp>
|
||||||
#include <mapnik/util/spirit_transform_attribute.hpp>
|
#include <mapnik/util/spirit_transform_attribute.hpp>
|
||||||
|
#include <mapnik/geometry_fusion_adapted.hpp>
|
||||||
// boost
|
// boost
|
||||||
#include <boost/spirit/include/phoenix.hpp>
|
#include <boost/spirit/include/phoenix.hpp>
|
||||||
|
|
||||||
|
|
|
@ -24,9 +24,8 @@
|
||||||
#define MAPNIK_WKT_GRAMMAR_HPP
|
#define MAPNIK_WKT_GRAMMAR_HPP
|
||||||
|
|
||||||
// mapnik
|
// mapnik
|
||||||
|
#include <mapnik/config.hpp>
|
||||||
#include <mapnik/geometry.hpp>
|
#include <mapnik/geometry.hpp>
|
||||||
#include <mapnik/geometry_fusion_adapted.hpp>
|
|
||||||
|
|
||||||
#pragma GCC diagnostic push
|
#pragma GCC diagnostic push
|
||||||
#include <mapnik/warning_ignore.hpp>
|
#include <mapnik/warning_ignore.hpp>
|
||||||
#include <boost/assert.hpp>
|
#include <boost/assert.hpp>
|
||||||
|
|
|
@ -20,8 +20,8 @@
|
||||||
*
|
*
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
#include <mapnik/config.hpp>
|
|
||||||
#include <mapnik/wkt/wkt_grammar.hpp>
|
#include <mapnik/wkt/wkt_grammar.hpp>
|
||||||
|
#include <mapnik/geometry_fusion_adapted.hpp>
|
||||||
#include <boost/spirit/include/phoenix.hpp>
|
#include <boost/spirit/include/phoenix.hpp>
|
||||||
#include <boost/spirit/include/phoenix_core.hpp>
|
#include <boost/spirit/include/phoenix_core.hpp>
|
||||||
#include <boost/spirit/include/phoenix_fusion.hpp>
|
#include <boost/spirit/include/phoenix_fusion.hpp>
|
||||||
|
|
|
@ -39,6 +39,7 @@ else:
|
||||||
|
|
||||||
plugin_sources = Split(
|
plugin_sources = Split(
|
||||||
"""
|
"""
|
||||||
|
%(PLUGIN_NAME)s_utils.cpp
|
||||||
%(PLUGIN_NAME)s_datasource.cpp
|
%(PLUGIN_NAME)s_datasource.cpp
|
||||||
%(PLUGIN_NAME)s_featureset.cpp
|
%(PLUGIN_NAME)s_featureset.cpp
|
||||||
%(PLUGIN_NAME)s_inline_featureset.cpp
|
%(PLUGIN_NAME)s_inline_featureset.cpp
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
#include "csv_utils.hpp"
|
#include "csv_utils.hpp"
|
||||||
|
#include "csv_getline.hpp"
|
||||||
#include "csv_datasource.hpp"
|
#include "csv_datasource.hpp"
|
||||||
#include "csv_featureset.hpp"
|
#include "csv_featureset.hpp"
|
||||||
#include "csv_inline_featureset.hpp"
|
#include "csv_inline_featureset.hpp"
|
||||||
|
@ -36,9 +37,12 @@
|
||||||
#include <mapnik/memory_featureset.hpp>
|
#include <mapnik/memory_featureset.hpp>
|
||||||
#include <mapnik/boolean.hpp>
|
#include <mapnik/boolean.hpp>
|
||||||
#include <mapnik/util/trim.hpp>
|
#include <mapnik/util/trim.hpp>
|
||||||
|
#include <mapnik/geometry.hpp>
|
||||||
|
#include <mapnik/geometry_adapters.hpp>
|
||||||
#include <mapnik/util/geometry_to_ds_type.hpp>
|
#include <mapnik/util/geometry_to_ds_type.hpp>
|
||||||
#include <mapnik/value_types.hpp>
|
#include <mapnik/value_types.hpp>
|
||||||
#include <mapnik/util/fs.hpp>
|
#include <mapnik/util/fs.hpp>
|
||||||
|
#include <mapnik/make_unique.hpp>
|
||||||
#include <mapnik/util/spatial_index.hpp>
|
#include <mapnik/util/spatial_index.hpp>
|
||||||
#include <mapnik/geom_util.hpp>
|
#include <mapnik/geom_util.hpp>
|
||||||
#if defined(MAPNIK_MEMORY_MAPPED_FILE)
|
#if defined(MAPNIK_MEMORY_MAPPED_FILE)
|
||||||
|
@ -65,21 +69,13 @@ DATASOURCE_PLUGIN(csv_datasource)
|
||||||
csv_datasource::csv_datasource(parameters const& params)
|
csv_datasource::csv_datasource(parameters const& params)
|
||||||
: datasource(params),
|
: datasource(params),
|
||||||
desc_(csv_datasource::name(), *params.get<std::string>("encoding", "utf-8")),
|
desc_(csv_datasource::name(), *params.get<std::string>("encoding", "utf-8")),
|
||||||
extent_(),
|
|
||||||
filename_(),
|
|
||||||
row_limit_(*params.get<mapnik::value_integer>("row_limit", 0)),
|
|
||||||
inline_string_(),
|
|
||||||
separator_(0),
|
|
||||||
quote_(0),
|
|
||||||
headers_(),
|
|
||||||
manual_headers_(mapnik::util::trim_copy(*params.get<std::string>("headers", ""))),
|
|
||||||
strict_(*params.get<mapnik::boolean_type>("strict", false)),
|
|
||||||
ctx_(std::make_shared<mapnik::context_type>()),
|
ctx_(std::make_shared<mapnik::context_type>()),
|
||||||
extent_initialized_(false),
|
tree_(nullptr)
|
||||||
tree_(nullptr),
|
|
||||||
locator_(),
|
|
||||||
has_disk_index_(false)
|
|
||||||
{
|
{
|
||||||
|
row_limit_ = *params.get<mapnik::value_integer>("row_limit", 0);
|
||||||
|
manual_headers_ = mapnik::util::trim_copy(*params.get<std::string>("headers", ""));
|
||||||
|
strict_ = *params.get<mapnik::boolean_type>("strict", false);
|
||||||
|
|
||||||
auto quote_param = params.get<std::string>("quote");
|
auto quote_param = params.get<std::string>("quote");
|
||||||
if (quote_param)
|
if (quote_param)
|
||||||
{
|
{
|
||||||
|
@ -170,297 +166,89 @@ csv_datasource::csv_datasource(parameters const& params)
|
||||||
|
|
||||||
csv_datasource::~csv_datasource() {}
|
csv_datasource::~csv_datasource() {}
|
||||||
|
|
||||||
template <typename T>
|
void csv_datasource::parse_csv(std::istream & csv_file)
|
||||||
void csv_datasource::parse_csv(T & stream)
|
|
||||||
{
|
{
|
||||||
auto file_length = detail::file_length(stream);
|
std::vector<item_type> boxes;
|
||||||
// set back to start
|
csv_utils::csv_file_parser::parse_csv(csv_file, boxes);
|
||||||
stream.seekg(0, std::ios::beg);
|
|
||||||
char newline;
|
|
||||||
bool has_newline;
|
|
||||||
char detected_quote;
|
|
||||||
char detected_separator;
|
|
||||||
std::tie(newline, has_newline, detected_separator, detected_quote) = detail::autodect_csv_flavour(stream, file_length);
|
|
||||||
if (quote_ == 0) quote_ = detected_quote;
|
|
||||||
if (separator_ == 0) separator_ = detected_separator;
|
|
||||||
|
|
||||||
// set back to start
|
|
||||||
MAPNIK_LOG_DEBUG(csv) << "csv_datasource: separator: '" << separator_
|
|
||||||
<< "' quote: '" << quote_ << "'";
|
|
||||||
|
|
||||||
// rewind stream
|
|
||||||
stream.seekg(0, std::ios::beg);
|
|
||||||
//
|
|
||||||
std::string csv_line;
|
|
||||||
csv_utils::getline_csv(stream, csv_line, newline, quote_);
|
|
||||||
stream.seekg(0, std::ios::beg);
|
|
||||||
int line_number = 0;
|
|
||||||
if (!manual_headers_.empty())
|
|
||||||
{
|
|
||||||
std::size_t index = 0;
|
|
||||||
auto headers = csv_utils::parse_line(manual_headers_, separator_, quote_);
|
|
||||||
for (auto const& header : headers)
|
|
||||||
{
|
|
||||||
detail::locate_geometry_column(header, index++, locator_);
|
|
||||||
headers_.push_back(header);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else // parse first line as headers
|
|
||||||
{
|
|
||||||
while (csv_utils::getline_csv(stream, csv_line, newline, quote_))
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
auto headers = csv_utils::parse_line(csv_line, separator_, quote_);
|
|
||||||
// skip blank lines
|
|
||||||
std::string val;
|
|
||||||
if (headers.size() > 0 && headers[0].empty()) ++line_number;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
std::size_t index = 0;
|
|
||||||
for (auto const& header : headers)
|
|
||||||
{
|
|
||||||
val = mapnik::util::trim_copy(header);
|
|
||||||
if (val.empty())
|
|
||||||
{
|
|
||||||
if (strict_)
|
|
||||||
{
|
|
||||||
std::ostringstream s;
|
|
||||||
s << "CSV Plugin: expected a column header at line ";
|
|
||||||
s << line_number << ", column " << index;
|
|
||||||
s << " - ensure this row contains valid header fields: '";
|
|
||||||
s << csv_line;
|
|
||||||
throw mapnik::datasource_exception(s.str());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// create a placeholder for the empty header
|
|
||||||
std::ostringstream s;
|
|
||||||
s << "_" << index;
|
|
||||||
headers_.push_back(s.str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
detail::locate_geometry_column(val, index, locator_);
|
|
||||||
headers_.push_back(val);
|
|
||||||
}
|
|
||||||
++index;
|
|
||||||
}
|
|
||||||
++line_number;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (std::exception const& ex)
|
|
||||||
{
|
|
||||||
std::string s("CSV Plugin: error parsing headers: ");
|
|
||||||
s += ex.what();
|
|
||||||
throw mapnik::datasource_exception(s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::size_t num_headers = headers_.size();
|
|
||||||
if (!detail::valid(locator_, num_headers))
|
|
||||||
{
|
|
||||||
std::string str("CSV Plugin: could not detect column(s) with the name(s) of wkt, geojson, x/y, or ");
|
|
||||||
str += "latitude/longitude in:\n";
|
|
||||||
str += csv_line;
|
|
||||||
str += "\n - this is required for reading geometry data";
|
|
||||||
throw mapnik::datasource_exception(str);
|
|
||||||
}
|
|
||||||
|
|
||||||
mapnik::value_integer feature_count = 0;
|
|
||||||
bool extent_started = false;
|
|
||||||
|
|
||||||
std::for_each(headers_.begin(), headers_.end(),
|
std::for_each(headers_.begin(), headers_.end(),
|
||||||
[ & ](std::string const& header){ ctx_->push(header); });
|
[ & ](std::string const& header){ ctx_->push(header); });
|
||||||
|
|
||||||
mapnik::transcoder tr(desc_.get_encoding());
|
if (!has_disk_index_)
|
||||||
|
|
||||||
auto pos = stream.tellg();
|
|
||||||
// handle rare case of a single line of data and user-provided headers
|
|
||||||
// where a lack of a newline will mean that csv_utils::getline_csv returns false
|
|
||||||
bool is_first_row = false;
|
|
||||||
|
|
||||||
if (!has_newline)
|
|
||||||
{
|
{
|
||||||
stream.setstate(std::ios::failbit);
|
// bulk insert initialise r-tree
|
||||||
pos = 0;
|
tree_ = std::make_unique<spatial_index_type>(boxes);
|
||||||
if (!csv_line.empty())
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void csv_datasource::add_feature(mapnik::value_integer index,
|
||||||
|
mapnik::csv_line const & values)
|
||||||
|
{
|
||||||
|
if (index != 1) return;
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < values.size(); ++i)
|
||||||
|
{
|
||||||
|
std::string const& header = headers_.at(i);
|
||||||
|
std::string value = mapnik::util::trim_copy(values[i]);
|
||||||
|
int value_length = value.length();
|
||||||
|
if (locator_.index == i && (locator_.type == csv_utils::geometry_column_locator::WKT
|
||||||
|
|| locator_.type == csv_utils::geometry_column_locator::GEOJSON)) continue;
|
||||||
|
|
||||||
|
// First we detect likely strings,
|
||||||
|
// then try parsing likely numbers,
|
||||||
|
// then try converting to bool,
|
||||||
|
// finally falling back to string type.
|
||||||
|
|
||||||
|
// An empty string or a string of "null" will be parsed
|
||||||
|
// as a string rather than a true null value.
|
||||||
|
// Likely strings are either empty values, very long values
|
||||||
|
// or values with leading zeros like 001 (which are not safe
|
||||||
|
// to assume are numbers)
|
||||||
|
|
||||||
|
bool matched = false;
|
||||||
|
bool has_dot = value.find(".") != std::string::npos;
|
||||||
|
if (value.empty() || (value_length > 20) || (value_length > 1 && !has_dot && value[0] == '0'))
|
||||||
{
|
{
|
||||||
is_first_row = true;
|
matched = true;
|
||||||
|
desc_.add_descriptor(mapnik::attribute_descriptor(header, mapnik::String));
|
||||||
|
}
|
||||||
|
else if (csv_utils::is_likely_number(value))
|
||||||
|
{
|
||||||
|
bool has_e = value.find("e") != std::string::npos;
|
||||||
|
if (has_dot || has_e)
|
||||||
|
{
|
||||||
|
double float_val = 0.0;
|
||||||
|
if (mapnik::util::string2double(value,float_val))
|
||||||
|
{
|
||||||
|
matched = true;
|
||||||
|
desc_.add_descriptor(mapnik::attribute_descriptor(header,mapnik::Double));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mapnik::value_integer int_val = 0;
|
||||||
|
if (mapnik::util::string2int(value,int_val))
|
||||||
|
{
|
||||||
|
matched = true;
|
||||||
|
desc_.add_descriptor(mapnik::attribute_descriptor(header,mapnik::Integer));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!matched)
|
||||||
|
{
|
||||||
|
// NOTE: we don't use mapnik::util::string2bool
|
||||||
|
// here because we don't want to treat 'on' and 'off'
|
||||||
|
// as booleans, only 'true' and 'false'
|
||||||
|
if (csv_utils::ignore_case_equal(value, "true") || csv_utils::ignore_case_equal(value, "false"))
|
||||||
|
{
|
||||||
|
desc_.add_descriptor(mapnik::attribute_descriptor(header, mapnik::Boolean));
|
||||||
|
}
|
||||||
|
else // fallback to normal string
|
||||||
|
{
|
||||||
|
desc_.add_descriptor(mapnik::attribute_descriptor(header, mapnik::String));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<item_type> boxes;
|
|
||||||
while (is_first_row || csv_utils::getline_csv(stream, csv_line, newline, quote_))
|
|
||||||
{
|
|
||||||
++line_number;
|
|
||||||
if ((row_limit_ > 0) && (line_number > row_limit_))
|
|
||||||
{
|
|
||||||
MAPNIK_LOG_DEBUG(csv) << "csv_datasource: row limit hit, exiting at feature: " << feature_count;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
auto record_offset = pos;
|
|
||||||
auto record_size = csv_line.length();
|
|
||||||
pos = stream.tellg();
|
|
||||||
is_first_row = false;
|
|
||||||
|
|
||||||
// skip blank lines
|
|
||||||
if (record_size <= 10)
|
|
||||||
{
|
|
||||||
std::string trimmed = csv_line;
|
|
||||||
boost::trim_if(trimmed,boost::algorithm::is_any_of("\",'\r\n "));
|
|
||||||
if (trimmed.empty())
|
|
||||||
{
|
|
||||||
MAPNIK_LOG_DEBUG(csv) << "csv_datasource: empty row encountered at line: " << line_number;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
auto const* line_start = csv_line.data();
|
|
||||||
auto const* line_end = line_start + csv_line.size();
|
|
||||||
auto values = csv_utils::parse_line(line_start, line_end, separator_, quote_, num_headers);
|
|
||||||
unsigned num_fields = values.size();
|
|
||||||
if (num_fields != num_headers)
|
|
||||||
{
|
|
||||||
std::ostringstream s;
|
|
||||||
s << "CSV Plugin: # of columns(" << num_fields << ")";
|
|
||||||
if (num_fields > num_headers)
|
|
||||||
{
|
|
||||||
s << " > ";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
s << " < ";
|
|
||||||
}
|
|
||||||
s << "# of headers(" << num_headers << ") parsed";
|
|
||||||
throw mapnik::datasource_exception(s.str());
|
|
||||||
}
|
|
||||||
|
|
||||||
auto geom = detail::extract_geometry(values, locator_);
|
|
||||||
if (!geom.is<mapnik::geometry::geometry_empty>())
|
|
||||||
{
|
|
||||||
auto box = mapnik::geometry::envelope(geom);
|
|
||||||
boxes.emplace_back(std::move(box), make_pair(record_offset, record_size));
|
|
||||||
if (!extent_initialized_)
|
|
||||||
{
|
|
||||||
if (!extent_started)
|
|
||||||
{
|
|
||||||
extent_started = true;
|
|
||||||
extent_ = mapnik::geometry::envelope(geom);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
extent_.expand_to_include(mapnik::geometry::envelope(geom));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (++feature_count != 1) continue;
|
|
||||||
auto beg = values.begin();
|
|
||||||
for (std::size_t i = 0; i < num_headers; ++i)
|
|
||||||
{
|
|
||||||
std::string const& header = headers_.at(i);
|
|
||||||
std::string value = mapnik::util::trim_copy(*beg++);
|
|
||||||
int value_length = value.length();
|
|
||||||
if (locator_.index == i && (locator_.type == detail::geometry_column_locator::WKT
|
|
||||||
|| locator_.type == detail::geometry_column_locator::GEOJSON)) continue;
|
|
||||||
|
|
||||||
// First we detect likely strings,
|
|
||||||
// then try parsing likely numbers,
|
|
||||||
// then try converting to bool,
|
|
||||||
// finally falling back to string type.
|
|
||||||
|
|
||||||
// An empty string or a string of "null" will be parsed
|
|
||||||
// as a string rather than a true null value.
|
|
||||||
// Likely strings are either empty values, very long values
|
|
||||||
// or values with leading zeros like 001 (which are not safe
|
|
||||||
// to assume are numbers)
|
|
||||||
|
|
||||||
bool matched = false;
|
|
||||||
bool has_dot = value.find(".") != std::string::npos;
|
|
||||||
if (value.empty() || (value_length > 20) || (value_length > 1 && !has_dot && value[0] == '0'))
|
|
||||||
{
|
|
||||||
matched = true;
|
|
||||||
desc_.add_descriptor(mapnik::attribute_descriptor(header, mapnik::String));
|
|
||||||
}
|
|
||||||
else if (csv_utils::is_likely_number(value))
|
|
||||||
{
|
|
||||||
bool has_e = value.find("e") != std::string::npos;
|
|
||||||
if (has_dot || has_e)
|
|
||||||
{
|
|
||||||
double float_val = 0.0;
|
|
||||||
if (mapnik::util::string2double(value,float_val))
|
|
||||||
{
|
|
||||||
matched = true;
|
|
||||||
desc_.add_descriptor(mapnik::attribute_descriptor(header,mapnik::Double));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mapnik::value_integer int_val = 0;
|
|
||||||
if (mapnik::util::string2int(value,int_val))
|
|
||||||
{
|
|
||||||
matched = true;
|
|
||||||
desc_.add_descriptor(mapnik::attribute_descriptor(header,mapnik::Integer));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!matched)
|
|
||||||
{
|
|
||||||
// NOTE: we don't use mapnik::util::string2bool
|
|
||||||
// here because we don't want to treat 'on' and 'off'
|
|
||||||
// as booleans, only 'true' and 'false'
|
|
||||||
if (csv_utils::ignore_case_equal(value, "true") || csv_utils::ignore_case_equal(value, "false"))
|
|
||||||
{
|
|
||||||
desc_.add_descriptor(mapnik::attribute_descriptor(header, mapnik::Boolean));
|
|
||||||
}
|
|
||||||
else // fallback to normal string
|
|
||||||
{
|
|
||||||
desc_.add_descriptor(mapnik::attribute_descriptor(header, mapnik::String));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
std::ostringstream s;
|
|
||||||
s << "CSV Plugin: expected geometry column: could not parse row "
|
|
||||||
<< line_number << " "
|
|
||||||
<< values.at(locator_.index) << "'";
|
|
||||||
throw mapnik::datasource_exception(s.str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (mapnik::datasource_exception const& ex )
|
|
||||||
{
|
|
||||||
if (strict_) throw ex;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
MAPNIK_LOG_ERROR(csv) << ex.what() << " at line: " << line_number;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (std::exception const& ex)
|
|
||||||
{
|
|
||||||
std::ostringstream s;
|
|
||||||
s << "CSV Plugin: unexpected error parsing line: " << line_number
|
|
||||||
<< " - found " << headers_.size() << " with values like: " << csv_line << "\n"
|
|
||||||
<< " and got error like: " << ex.what();
|
|
||||||
if (strict_)
|
|
||||||
{
|
|
||||||
throw mapnik::datasource_exception(s.str());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
MAPNIK_LOG_ERROR(csv) << s.str();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// return early if *.index is present
|
|
||||||
if (has_disk_index_) return;
|
|
||||||
}
|
|
||||||
// bulk insert initialise r-tree
|
|
||||||
tree_ = std::make_unique<spatial_index_type>(boxes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const char * csv_datasource::name()
|
const char * csv_datasource::name()
|
||||||
|
@ -483,8 +271,8 @@ mapnik::layer_descriptor csv_datasource::get_descriptor() const
|
||||||
return desc_;
|
return desc_;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
boost::optional<mapnik::datasource_geometry_t>
|
||||||
boost::optional<mapnik::datasource_geometry_t> csv_datasource::get_geometry_type_impl(T & stream) const
|
csv_datasource::get_geometry_type_impl(std::istream & stream) const
|
||||||
{
|
{
|
||||||
boost::optional<mapnik::datasource_geometry_t> result;
|
boost::optional<mapnik::datasource_geometry_t> result;
|
||||||
if (tree_)
|
if (tree_)
|
||||||
|
@ -505,7 +293,7 @@ boost::optional<mapnik::datasource_geometry_t> csv_datasource::get_geometry_type
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
auto values = csv_utils::parse_line(str, separator_, quote_);
|
auto values = csv_utils::parse_line(str, separator_, quote_);
|
||||||
auto geom = detail::extract_geometry(values, locator_);
|
auto geom = csv_utils::extract_geometry(values, locator_);
|
||||||
result = mapnik::util::to_ds_type(geom);
|
result = mapnik::util::to_ds_type(geom);
|
||||||
if (result)
|
if (result)
|
||||||
{
|
{
|
||||||
|
@ -548,7 +336,7 @@ boost::optional<mapnik::datasource_geometry_t> csv_datasource::get_geometry_type
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
auto values = csv_utils::parse_line(str, separator_, quote_);
|
auto values = csv_utils::parse_line(str, separator_, quote_);
|
||||||
auto geom = detail::extract_geometry(values, locator_);
|
auto geom = csv_utils::extract_geometry(values, locator_);
|
||||||
result = mapnik::util::to_ds_type(geom);
|
result = mapnik::util::to_ds_type(geom);
|
||||||
if (result)
|
if (result)
|
||||||
{
|
{
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
#include <mapnik/coord.hpp>
|
#include <mapnik/coord.hpp>
|
||||||
#include <mapnik/feature_layer_desc.hpp>
|
#include <mapnik/feature_layer_desc.hpp>
|
||||||
#include <mapnik/value_types.hpp>
|
#include <mapnik/value_types.hpp>
|
||||||
|
#include "csv_utils.hpp"
|
||||||
|
|
||||||
#pragma GCC diagnostic push
|
#pragma GCC diagnostic push
|
||||||
#include <mapnik/warning_ignore.hpp>
|
#include <mapnik/warning_ignore.hpp>
|
||||||
|
@ -41,8 +42,8 @@
|
||||||
#pragma GCC diagnostic pop
|
#pragma GCC diagnostic pop
|
||||||
|
|
||||||
// stl
|
// stl
|
||||||
|
#include <iosfwd>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <deque>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
template <std::size_t Max, std::size_t Min>
|
template <std::size_t Max, std::size_t Min>
|
||||||
|
@ -67,7 +68,8 @@ struct options_type<csv_linear<Max,Min> >
|
||||||
};
|
};
|
||||||
}}}}}
|
}}}}}
|
||||||
|
|
||||||
class csv_datasource : public mapnik::datasource
|
class csv_datasource : public mapnik::datasource,
|
||||||
|
private csv_utils::csv_file_parser
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using box_type = mapnik::box2d<double>;
|
using box_type = mapnik::box2d<double>;
|
||||||
|
@ -84,26 +86,15 @@ public:
|
||||||
mapnik::layer_descriptor get_descriptor() const;
|
mapnik::layer_descriptor get_descriptor() const;
|
||||||
boost::optional<mapnik::datasource_geometry_t> get_geometry_type() const;
|
boost::optional<mapnik::datasource_geometry_t> get_geometry_type() const;
|
||||||
private:
|
private:
|
||||||
template <typename T>
|
void parse_csv(std::istream & );
|
||||||
void parse_csv(T & stream);
|
virtual void add_feature(mapnik::value_integer index, mapnik::csv_line const & values);
|
||||||
template <typename T>
|
boost::optional<mapnik::datasource_geometry_t> get_geometry_type_impl(std::istream & ) const;
|
||||||
boost::optional<mapnik::datasource_geometry_t> get_geometry_type_impl(T & stream) const;
|
|
||||||
|
|
||||||
mapnik::layer_descriptor desc_;
|
mapnik::layer_descriptor desc_;
|
||||||
mapnik::box2d<double> extent_;
|
|
||||||
std::string filename_;
|
std::string filename_;
|
||||||
mapnik::value_integer row_limit_;
|
|
||||||
std::string inline_string_;
|
std::string inline_string_;
|
||||||
char separator_;
|
|
||||||
char quote_;
|
|
||||||
std::vector<std::string> headers_;
|
|
||||||
std::string manual_headers_;
|
|
||||||
bool strict_;
|
|
||||||
mapnik::context_ptr ctx_;
|
mapnik::context_ptr ctx_;
|
||||||
bool extent_initialized_;
|
|
||||||
std::unique_ptr<spatial_index_type> tree_;
|
std::unique_ptr<spatial_index_type> tree_;
|
||||||
detail::geometry_column_locator locator_;
|
|
||||||
bool has_disk_index_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // MAPNIK_CSV_DATASOURCE_HPP
|
#endif // MAPNIK_CSV_DATASOURCE_HPP
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
|
|
||||||
csv_featureset::csv_featureset(std::string const& filename, detail::geometry_column_locator const& locator, char separator, char quote,
|
csv_featureset::csv_featureset(std::string const& filename, locator_type const& locator, char separator, char quote,
|
||||||
std::vector<std::string> const& headers, mapnik::context_ptr const& ctx, array_type && index_array)
|
std::vector<std::string> const& headers, mapnik::context_ptr const& ctx, array_type && index_array)
|
||||||
:
|
:
|
||||||
#if defined(MAPNIK_MEMORY_MAPPED_FILE)
|
#if defined(MAPNIK_MEMORY_MAPPED_FILE)
|
||||||
|
@ -72,12 +72,12 @@ csv_featureset::~csv_featureset() {}
|
||||||
mapnik::feature_ptr csv_featureset::parse_feature(char const* beg, char const* end)
|
mapnik::feature_ptr csv_featureset::parse_feature(char const* beg, char const* end)
|
||||||
{
|
{
|
||||||
auto values = csv_utils::parse_line(beg, end, separator_, quote_, headers_.size());
|
auto values = csv_utils::parse_line(beg, end, separator_, quote_, headers_.size());
|
||||||
auto geom = detail::extract_geometry(values, locator_);
|
auto geom = csv_utils::extract_geometry(values, locator_);
|
||||||
if (!geom.is<mapnik::geometry::geometry_empty>())
|
if (!geom.is<mapnik::geometry::geometry_empty>())
|
||||||
{
|
{
|
||||||
mapnik::feature_ptr feature(mapnik::feature_factory::create(ctx_, ++feature_id_));
|
mapnik::feature_ptr feature(mapnik::feature_factory::create(ctx_, ++feature_id_));
|
||||||
feature->set_geometry(std::move(geom));
|
feature->set_geometry(std::move(geom));
|
||||||
detail::process_properties(*feature, headers_, values, locator_, tr_);
|
csv_utils::process_properties(*feature, headers_, values, locator_, tr_);
|
||||||
return feature;
|
return feature;
|
||||||
}
|
}
|
||||||
return mapnik::feature_ptr();
|
return mapnik::feature_ptr();
|
||||||
|
|
|
@ -28,7 +28,6 @@
|
||||||
#include "csv_utils.hpp"
|
#include "csv_utils.hpp"
|
||||||
#include "csv_datasource.hpp"
|
#include "csv_datasource.hpp"
|
||||||
#include <deque>
|
#include <deque>
|
||||||
#include <cstdio>
|
|
||||||
|
|
||||||
#if defined(MAPNIK_MEMORY_MAPPED_FILE)
|
#if defined(MAPNIK_MEMORY_MAPPED_FILE)
|
||||||
#pragma GCC diagnostic push
|
#pragma GCC diagnostic push
|
||||||
|
@ -41,7 +40,7 @@
|
||||||
|
|
||||||
class csv_featureset : public mapnik::Featureset
|
class csv_featureset : public mapnik::Featureset
|
||||||
{
|
{
|
||||||
using locator_type = detail::geometry_column_locator;
|
using locator_type = csv_utils::geometry_column_locator;
|
||||||
public:
|
public:
|
||||||
using array_type = std::deque<csv_datasource::item_type>;
|
using array_type = std::deque<csv_datasource::item_type>;
|
||||||
csv_featureset(std::string const& filename,
|
csv_featureset(std::string const& filename,
|
||||||
|
@ -70,7 +69,7 @@ private:
|
||||||
array_type::const_iterator index_end_;
|
array_type::const_iterator index_end_;
|
||||||
mapnik::context_ptr ctx_;
|
mapnik::context_ptr ctx_;
|
||||||
mapnik::value_integer feature_id_ = 0;
|
mapnik::value_integer feature_id_ = 0;
|
||||||
detail::geometry_column_locator const& locator_;
|
locator_type const& locator_;
|
||||||
mapnik::transcoder tr_;
|
mapnik::transcoder tr_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
72
plugins/input/csv/csv_getline.hpp
Normal file
72
plugins/input/csv/csv_getline.hpp
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
/*****************************************************************************
|
||||||
|
*
|
||||||
|
* 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 MAPNIK_CSV_GETLINE_HPP
|
||||||
|
#define MAPNIK_CSV_GETLINE_HPP
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <istream>
|
||||||
|
#include <streambuf>
|
||||||
|
|
||||||
|
namespace csv_utils
|
||||||
|
{
|
||||||
|
|
||||||
|
template <class CharT, class Traits, class Allocator>
|
||||||
|
std::basic_istream<CharT, Traits>& getline_csv(std::istream& is, std::basic_string<CharT,Traits,Allocator>& s, CharT delim, CharT quote)
|
||||||
|
{
|
||||||
|
typename std::basic_string<CharT,Traits,Allocator>::size_type nread = 0;
|
||||||
|
typename std::basic_istream<CharT, Traits>::sentry sentry(is, true);
|
||||||
|
if (sentry)
|
||||||
|
{
|
||||||
|
std::basic_streambuf<CharT, Traits>* buf = is.rdbuf();
|
||||||
|
s.clear();
|
||||||
|
bool has_quote = false;
|
||||||
|
while (nread < s.max_size())
|
||||||
|
{
|
||||||
|
int c1 = buf->sbumpc();
|
||||||
|
if (Traits::eq_int_type(c1, Traits::eof()))
|
||||||
|
{
|
||||||
|
is.setstate(std::ios_base::eofbit);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
++nread;
|
||||||
|
CharT c = Traits::to_char_type(c1);
|
||||||
|
if (Traits::eq(c, quote))
|
||||||
|
has_quote = !has_quote;
|
||||||
|
if (!Traits::eq(c, delim) || has_quote)
|
||||||
|
s.push_back(c);
|
||||||
|
else
|
||||||
|
break;// Character is extracted but not appended.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (nread == 0 || nread >= s.max_size())
|
||||||
|
is.setstate(std::ios_base::failbit);
|
||||||
|
|
||||||
|
return is;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // MAPNIK_CSV_GETLINE_HPP
|
|
@ -37,7 +37,7 @@
|
||||||
|
|
||||||
csv_index_featureset::csv_index_featureset(std::string const& filename,
|
csv_index_featureset::csv_index_featureset(std::string const& filename,
|
||||||
mapnik::filter_in_box const& filter,
|
mapnik::filter_in_box const& filter,
|
||||||
detail::geometry_column_locator const& locator,
|
locator_type const& locator,
|
||||||
char separator,
|
char separator,
|
||||||
char quote,
|
char quote,
|
||||||
std::vector<std::string> const& headers,
|
std::vector<std::string> const& headers,
|
||||||
|
@ -89,12 +89,12 @@ csv_index_featureset::~csv_index_featureset() {}
|
||||||
mapnik::feature_ptr csv_index_featureset::parse_feature(char const* beg, char const* end)
|
mapnik::feature_ptr csv_index_featureset::parse_feature(char const* beg, char const* end)
|
||||||
{
|
{
|
||||||
auto values = csv_utils::parse_line(beg, end, separator_, quote_, headers_.size());
|
auto values = csv_utils::parse_line(beg, end, separator_, quote_, headers_.size());
|
||||||
auto geom = detail::extract_geometry(values, locator_);
|
auto geom = csv_utils::extract_geometry(values, locator_);
|
||||||
if (!geom.is<mapnik::geometry::geometry_empty>())
|
if (!geom.is<mapnik::geometry::geometry_empty>())
|
||||||
{
|
{
|
||||||
mapnik::feature_ptr feature(mapnik::feature_factory::create(ctx_, ++feature_id_));
|
mapnik::feature_ptr feature(mapnik::feature_factory::create(ctx_, ++feature_id_));
|
||||||
feature->set_geometry(std::move(geom));
|
feature->set_geometry(std::move(geom));
|
||||||
detail::process_properties(*feature, headers_, values, locator_, tr_);
|
csv_utils::process_properties(*feature, headers_, values, locator_, tr_);
|
||||||
return feature;
|
return feature;
|
||||||
}
|
}
|
||||||
return mapnik::feature_ptr();
|
return mapnik::feature_ptr();
|
||||||
|
|
|
@ -41,7 +41,7 @@
|
||||||
class csv_index_featureset : public mapnik::Featureset
|
class csv_index_featureset : public mapnik::Featureset
|
||||||
{
|
{
|
||||||
using value_type = std::pair<std::size_t, std::size_t>;
|
using value_type = std::pair<std::size_t, std::size_t>;
|
||||||
using locator_type = detail::geometry_column_locator;
|
using locator_type = csv_utils::geometry_column_locator;
|
||||||
public:
|
public:
|
||||||
|
|
||||||
csv_index_featureset(std::string const& filename,
|
csv_index_featureset(std::string const& filename,
|
||||||
|
@ -60,7 +60,7 @@ private:
|
||||||
std::vector<std::string> headers_;
|
std::vector<std::string> headers_;
|
||||||
mapnik::context_ptr ctx_;
|
mapnik::context_ptr ctx_;
|
||||||
mapnik::value_integer feature_id_ = 0;
|
mapnik::value_integer feature_id_ = 0;
|
||||||
detail::geometry_column_locator const& locator_;
|
locator_type const& locator_;
|
||||||
mapnik::transcoder tr_;
|
mapnik::transcoder tr_;
|
||||||
#if defined (MAPNIK_MEMORY_MAPPED_FILE)
|
#if defined (MAPNIK_MEMORY_MAPPED_FILE)
|
||||||
using file_source_type = boost::interprocess::ibufferstream;
|
using file_source_type = boost::interprocess::ibufferstream;
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
#include <deque>
|
#include <deque>
|
||||||
|
|
||||||
csv_inline_featureset::csv_inline_featureset(std::string const& inline_string,
|
csv_inline_featureset::csv_inline_featureset(std::string const& inline_string,
|
||||||
detail::geometry_column_locator const& locator,
|
locator_type const& locator,
|
||||||
char separator,
|
char separator,
|
||||||
char quote,
|
char quote,
|
||||||
std::vector<std::string> const& headers,
|
std::vector<std::string> const& headers,
|
||||||
|
@ -57,12 +57,12 @@ mapnik::feature_ptr csv_inline_featureset::parse_feature(std::string const& str)
|
||||||
auto const* start = str.data();
|
auto const* start = str.data();
|
||||||
auto const* end = start + str.size();
|
auto const* end = start + str.size();
|
||||||
auto values = csv_utils::parse_line(start, end, separator_, quote_, headers_.size());
|
auto values = csv_utils::parse_line(start, end, separator_, quote_, headers_.size());
|
||||||
auto geom = detail::extract_geometry(values, locator_);
|
auto geom = csv_utils::extract_geometry(values, locator_);
|
||||||
if (!geom.is<mapnik::geometry::geometry_empty>())
|
if (!geom.is<mapnik::geometry::geometry_empty>())
|
||||||
{
|
{
|
||||||
mapnik::feature_ptr feature(mapnik::feature_factory::create(ctx_, ++feature_id_));
|
mapnik::feature_ptr feature(mapnik::feature_factory::create(ctx_, ++feature_id_));
|
||||||
feature->set_geometry(std::move(geom));
|
feature->set_geometry(std::move(geom));
|
||||||
detail::process_properties(*feature, headers_, values, locator_, tr_);
|
csv_utils::process_properties(*feature, headers_, values, locator_, tr_);
|
||||||
return feature;
|
return feature;
|
||||||
}
|
}
|
||||||
return mapnik::feature_ptr();
|
return mapnik::feature_ptr();
|
||||||
|
|
|
@ -28,11 +28,10 @@
|
||||||
#include "csv_utils.hpp"
|
#include "csv_utils.hpp"
|
||||||
#include "csv_datasource.hpp"
|
#include "csv_datasource.hpp"
|
||||||
#include <deque>
|
#include <deque>
|
||||||
#include <cstdio>
|
|
||||||
|
|
||||||
class csv_inline_featureset : public mapnik::Featureset
|
class csv_inline_featureset : public mapnik::Featureset
|
||||||
{
|
{
|
||||||
using locator_type = detail::geometry_column_locator;
|
using locator_type = csv_utils::geometry_column_locator;
|
||||||
public:
|
public:
|
||||||
using array_type = std::deque<csv_datasource::item_type>;
|
using array_type = std::deque<csv_datasource::item_type>;
|
||||||
csv_inline_featureset(std::string const& inline_string,
|
csv_inline_featureset(std::string const& inline_string,
|
||||||
|
@ -55,7 +54,7 @@ private:
|
||||||
array_type::const_iterator index_end_;
|
array_type::const_iterator index_end_;
|
||||||
mapnik::context_ptr ctx_;
|
mapnik::context_ptr ctx_;
|
||||||
mapnik::value_integer feature_id_ = 0;
|
mapnik::value_integer feature_id_ = 0;
|
||||||
detail::geometry_column_locator const& locator_;
|
locator_type const& locator_;
|
||||||
mapnik::transcoder tr_;
|
mapnik::transcoder tr_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
496
plugins/input/csv/csv_utils.cpp
Normal file
496
plugins/input/csv/csv_utils.cpp
Normal file
|
@ -0,0 +1,496 @@
|
||||||
|
/*****************************************************************************
|
||||||
|
*
|
||||||
|
* 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 <mapnik/debug.hpp>
|
||||||
|
#include <mapnik/geometry.hpp>
|
||||||
|
#include <mapnik/geometry_correct.hpp>
|
||||||
|
#include <mapnik/wkt/wkt_factory.hpp>
|
||||||
|
#include <mapnik/json/geometry_parser.hpp>
|
||||||
|
#include <mapnik/util/conversions.hpp>
|
||||||
|
#include <mapnik/csv/csv_grammar.hpp>
|
||||||
|
#include <mapnik/util/trim.hpp>
|
||||||
|
#include <mapnik/datasource.hpp>
|
||||||
|
#include "csv_getline.hpp"
|
||||||
|
#include "csv_utils.hpp"
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
#include <string>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
namespace csv_utils
|
||||||
|
{
|
||||||
|
|
||||||
|
static const mapnik::csv_line_grammar<char const*> line_g;
|
||||||
|
static const mapnik::csv_white_space_skipper skipper{};
|
||||||
|
|
||||||
|
mapnik::csv_line parse_line(char const* start, char const* end, char separator, char quote, std::size_t num_columns)
|
||||||
|
{
|
||||||
|
mapnik::csv_line values;
|
||||||
|
if (num_columns > 0) values.reserve(num_columns);
|
||||||
|
if (!boost::spirit::qi::phrase_parse(start, end, (line_g)(separator, quote), skipper, values))
|
||||||
|
{
|
||||||
|
throw mapnik::datasource_exception("Failed to parse CSV line:\n" + std::string(start, end));
|
||||||
|
}
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
mapnik::csv_line parse_line(std::string const& line_str, char separator, char quote)
|
||||||
|
{
|
||||||
|
auto start = line_str.c_str();
|
||||||
|
auto end = start + line_str.length();
|
||||||
|
return parse_line(start, end, separator, quote, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool is_likely_number(std::string const& value)
|
||||||
|
{
|
||||||
|
return (std::strspn( value.c_str(), "e-.+0123456789" ) == value.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ignore_case_equal_pred
|
||||||
|
{
|
||||||
|
bool operator () (unsigned char a, unsigned char b) const
|
||||||
|
{
|
||||||
|
return std::tolower(a) == std::tolower(b);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
bool ignore_case_equal(std::string const& s0, std::string const& s1)
|
||||||
|
{
|
||||||
|
return std::equal(s0.begin(), s0.end(),
|
||||||
|
s1.begin(), ignore_case_equal_pred());
|
||||||
|
}
|
||||||
|
|
||||||
|
void csv_file_parser::add_feature(mapnik::value_integer, mapnik::csv_line const & )
|
||||||
|
{
|
||||||
|
// no-op by default
|
||||||
|
}
|
||||||
|
|
||||||
|
void csv_file_parser::parse_csv(std::istream & csv_file, boxes_type & boxes)
|
||||||
|
{
|
||||||
|
auto file_length = detail::file_length(csv_file);
|
||||||
|
// set back to start
|
||||||
|
csv_file.seekg(0, std::ios::beg);
|
||||||
|
char newline;
|
||||||
|
bool has_newline;
|
||||||
|
char detected_quote;
|
||||||
|
char detected_separator;
|
||||||
|
std::tie(newline, has_newline, detected_separator, detected_quote) = detail::autodect_csv_flavour(csv_file, file_length);
|
||||||
|
if (quote_ == 0) quote_ = detected_quote;
|
||||||
|
if (separator_ == 0) separator_ = detected_separator;
|
||||||
|
|
||||||
|
// set back to start
|
||||||
|
MAPNIK_LOG_DEBUG(csv) << "csv_datasource: separator: '" << separator_
|
||||||
|
<< "' quote: '" << quote_ << "'";
|
||||||
|
|
||||||
|
// rewind stream
|
||||||
|
csv_file.seekg(0, std::ios::beg);
|
||||||
|
//
|
||||||
|
std::string csv_line;
|
||||||
|
csv_utils::getline_csv(csv_file, csv_line, newline, quote_);
|
||||||
|
csv_file.seekg(0, std::ios::beg);
|
||||||
|
int line_number = 0;
|
||||||
|
if (!manual_headers_.empty())
|
||||||
|
{
|
||||||
|
std::size_t index = 0;
|
||||||
|
auto headers = csv_utils::parse_line(manual_headers_, separator_, quote_);
|
||||||
|
for (auto const& header : headers)
|
||||||
|
{
|
||||||
|
detail::locate_geometry_column(header, index++, locator_);
|
||||||
|
headers_.push_back(header);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else // parse first line as headers
|
||||||
|
{
|
||||||
|
while (csv_utils::getline_csv(csv_file, csv_line, newline, quote_))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
auto headers = csv_utils::parse_line(csv_line, separator_, quote_);
|
||||||
|
// skip blank lines
|
||||||
|
if (headers.size() > 0 && headers[0].empty()) ++line_number;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::size_t index = 0;
|
||||||
|
for (auto & header : headers)
|
||||||
|
{
|
||||||
|
mapnik::util::trim(header);
|
||||||
|
if (header.empty())
|
||||||
|
{
|
||||||
|
if (strict_)
|
||||||
|
{
|
||||||
|
std::ostringstream s;
|
||||||
|
s << "CSV Plugin: expected a column header at line ";
|
||||||
|
s << line_number << ", column " << index;
|
||||||
|
s << " - ensure this row contains valid header fields: '";
|
||||||
|
s << csv_line;
|
||||||
|
throw mapnik::datasource_exception(s.str());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// create a placeholder for the empty header
|
||||||
|
std::ostringstream s;
|
||||||
|
s << "_" << index;
|
||||||
|
headers_.push_back(s.str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
detail::locate_geometry_column(header, index, locator_);
|
||||||
|
headers_.push_back(header);
|
||||||
|
}
|
||||||
|
++index;
|
||||||
|
}
|
||||||
|
++line_number;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (std::exception const& ex)
|
||||||
|
{
|
||||||
|
std::string s("CSV Plugin: error parsing headers: ");
|
||||||
|
s += ex.what();
|
||||||
|
throw mapnik::datasource_exception(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t num_headers = headers_.size();
|
||||||
|
if (!detail::valid(locator_, num_headers))
|
||||||
|
{
|
||||||
|
std::string str("CSV Plugin: could not detect column(s) with the name(s) of wkt, geojson, x/y, or ");
|
||||||
|
str += "latitude/longitude in:\n";
|
||||||
|
str += csv_line;
|
||||||
|
str += "\n - this is required for reading geometry data";
|
||||||
|
throw mapnik::datasource_exception(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
mapnik::value_integer feature_count = 0;
|
||||||
|
auto pos = csv_file.tellg();
|
||||||
|
// handle rare case of a single line of data and user-provided headers
|
||||||
|
// where a lack of a newline will mean that csv_utils::getline_csv returns false
|
||||||
|
bool is_first_row = false;
|
||||||
|
|
||||||
|
if (!has_newline)
|
||||||
|
{
|
||||||
|
csv_file.setstate(std::ios::failbit);
|
||||||
|
pos = 0;
|
||||||
|
if (!csv_line.empty())
|
||||||
|
{
|
||||||
|
is_first_row = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (is_first_row || csv_utils::getline_csv(csv_file, csv_line, newline, quote_))
|
||||||
|
{
|
||||||
|
++line_number;
|
||||||
|
if ((row_limit_ > 0) && (line_number > row_limit_))
|
||||||
|
{
|
||||||
|
MAPNIK_LOG_DEBUG(csv) << "csv_datasource: row limit hit, exiting at feature: " << feature_count;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
auto record_offset = pos;
|
||||||
|
auto record_size = csv_line.length();
|
||||||
|
pos = csv_file.tellg();
|
||||||
|
is_first_row = false;
|
||||||
|
|
||||||
|
// skip blank lines
|
||||||
|
if (record_size <= 10)
|
||||||
|
{
|
||||||
|
std::string trimmed = csv_line;
|
||||||
|
boost::trim_if(trimmed, boost::algorithm::is_any_of("\",'\r\n "));
|
||||||
|
if (trimmed.empty())
|
||||||
|
{
|
||||||
|
MAPNIK_LOG_DEBUG(csv) << "csv_datasource: empty row encountered at line: " << line_number;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
auto const* line_start = csv_line.data();
|
||||||
|
auto const* line_end = line_start + csv_line.size();
|
||||||
|
auto values = csv_utils::parse_line(line_start, line_end, separator_, quote_, num_headers);
|
||||||
|
unsigned num_fields = values.size();
|
||||||
|
if (num_fields != num_headers)
|
||||||
|
{
|
||||||
|
std::ostringstream s;
|
||||||
|
s << "CSV Plugin: # of columns(" << num_fields << ")";
|
||||||
|
if (num_fields > num_headers)
|
||||||
|
{
|
||||||
|
s << " > ";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
s << " < ";
|
||||||
|
}
|
||||||
|
s << "# of headers(" << num_headers << ") parsed";
|
||||||
|
throw mapnik::datasource_exception(s.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
auto geom = extract_geometry(values, locator_);
|
||||||
|
if (!geom.is<mapnik::geometry::geometry_empty>())
|
||||||
|
{
|
||||||
|
auto box = mapnik::geometry::envelope(geom);
|
||||||
|
if (!extent_initialized_)
|
||||||
|
{
|
||||||
|
if (extent_.valid())
|
||||||
|
extent_.expand_to_include(box);
|
||||||
|
else
|
||||||
|
extent_ = box;
|
||||||
|
}
|
||||||
|
boxes.emplace_back(box, make_pair(record_offset, record_size));
|
||||||
|
add_feature(++feature_count, values);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::ostringstream s;
|
||||||
|
s << "CSV Plugin: expected geometry column: could not parse row "
|
||||||
|
<< line_number << " "
|
||||||
|
<< values.at(locator_.index) << "'";
|
||||||
|
throw mapnik::datasource_exception(s.str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (mapnik::datasource_exception const& ex )
|
||||||
|
{
|
||||||
|
if (strict_) throw ex;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MAPNIK_LOG_ERROR(csv) << ex.what() << " at line: " << line_number;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (std::exception const& ex)
|
||||||
|
{
|
||||||
|
std::ostringstream s;
|
||||||
|
s << "CSV Plugin: unexpected error parsing line: " << line_number
|
||||||
|
<< " - found " << headers_.size() << " with values like: " << csv_line << "\n"
|
||||||
|
<< " and got error like: " << ex.what();
|
||||||
|
if (strict_)
|
||||||
|
{
|
||||||
|
throw mapnik::datasource_exception(s.str());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MAPNIK_LOG_ERROR(csv) << s.str();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// return early if *.index is present
|
||||||
|
if (has_disk_index_) return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
std::size_t file_length(std::istream & stream)
|
||||||
|
{
|
||||||
|
stream.seekg(0, std::ios::end);
|
||||||
|
return stream.tellg();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::tuple<char, bool, char, char> autodect_csv_flavour(std::istream & stream, std::size_t file_length)
|
||||||
|
{
|
||||||
|
// autodetect newlines/quotes/separators
|
||||||
|
char newline = '\n'; // default
|
||||||
|
bool has_newline = false;
|
||||||
|
bool has_single_quote = false;
|
||||||
|
char quote = '"'; // default
|
||||||
|
char separator = ','; // default
|
||||||
|
// local counters
|
||||||
|
int num_commas = 0;
|
||||||
|
int num_tabs = 0;
|
||||||
|
int num_pipes = 0;
|
||||||
|
int num_semicolons = 0;
|
||||||
|
|
||||||
|
static std::size_t const max_size = 4000;
|
||||||
|
std::size_t size = std::min(file_length, max_size);
|
||||||
|
std::vector<char> buffer;
|
||||||
|
buffer.resize(size);
|
||||||
|
stream.read(buffer.data(), size);
|
||||||
|
for (auto c : buffer)
|
||||||
|
{
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
case '\r':
|
||||||
|
newline = '\r';
|
||||||
|
has_newline = true;
|
||||||
|
break;
|
||||||
|
case '\n':
|
||||||
|
has_newline = true;
|
||||||
|
break;
|
||||||
|
case '\'':
|
||||||
|
if (!has_single_quote)
|
||||||
|
{
|
||||||
|
quote = c;
|
||||||
|
has_single_quote = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ',':
|
||||||
|
if (!has_newline) ++num_commas;
|
||||||
|
break;
|
||||||
|
case '\t':
|
||||||
|
if (!has_newline) ++num_tabs;
|
||||||
|
break;
|
||||||
|
case '|':
|
||||||
|
if (!has_newline) ++num_pipes;
|
||||||
|
break;
|
||||||
|
case ';':
|
||||||
|
if (!has_newline) ++num_semicolons;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// detect separator
|
||||||
|
if (num_tabs > 0 && num_tabs > num_commas)
|
||||||
|
{
|
||||||
|
separator = '\t';
|
||||||
|
MAPNIK_LOG_DEBUG(csv) << "csv_datasource: auto detected tab separator";
|
||||||
|
}
|
||||||
|
else // pipes/semicolons
|
||||||
|
{
|
||||||
|
if (num_pipes > num_commas)
|
||||||
|
{
|
||||||
|
separator = '|';
|
||||||
|
MAPNIK_LOG_DEBUG(csv) << "csv_datasource: auto detected '|' separator";
|
||||||
|
}
|
||||||
|
else if (num_semicolons > num_commas)
|
||||||
|
{
|
||||||
|
separator = ';';
|
||||||
|
MAPNIK_LOG_DEBUG(csv) << "csv_datasource: auto detected ';' separator";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_newline && has_single_quote)
|
||||||
|
{
|
||||||
|
std::istringstream ss(std::string(buffer.begin(), buffer.end()));
|
||||||
|
std::size_t num_columns = 0;
|
||||||
|
for (std::string line; csv_utils::getline_csv(ss, line, newline, quote); )
|
||||||
|
{
|
||||||
|
if (size < file_length && ss.eof())
|
||||||
|
{
|
||||||
|
// we can't be sure last line
|
||||||
|
// is not truncated so skip it
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (line.size() == 0) continue; // empty lines are not interesting
|
||||||
|
auto num_quotes = std::count(line.begin(), line.end(), quote);
|
||||||
|
if (num_quotes % 2 != 0)
|
||||||
|
{
|
||||||
|
quote = '"';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
auto columns = csv_utils::parse_line(line, separator, quote);
|
||||||
|
if (num_columns > 0 && num_columns != columns.size())
|
||||||
|
{
|
||||||
|
quote = '"';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
num_columns = columns.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return std::make_tuple(newline, has_newline, separator, quote);
|
||||||
|
}
|
||||||
|
|
||||||
|
void locate_geometry_column(std::string const& header, std::size_t index, geometry_column_locator & locator)
|
||||||
|
{
|
||||||
|
std::string lower_val(header);
|
||||||
|
std::transform(lower_val.begin(), lower_val.end(), lower_val.begin(), ::tolower);
|
||||||
|
if (lower_val == "wkt" || (lower_val.find("geom") != std::string::npos))
|
||||||
|
{
|
||||||
|
locator.type = geometry_column_locator::WKT;
|
||||||
|
locator.index = index;
|
||||||
|
}
|
||||||
|
else if (lower_val == "geojson")
|
||||||
|
{
|
||||||
|
locator.type = geometry_column_locator::GEOJSON;
|
||||||
|
locator.index = index;
|
||||||
|
}
|
||||||
|
else if (lower_val == "x" || lower_val == "lon"
|
||||||
|
|| lower_val == "lng" || lower_val == "long"
|
||||||
|
|| (lower_val.find("longitude") != std::string::npos))
|
||||||
|
{
|
||||||
|
locator.index = index;
|
||||||
|
locator.type = geometry_column_locator::LON_LAT;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (lower_val == "y"
|
||||||
|
|| lower_val == "lat"
|
||||||
|
|| (lower_val.find("latitude") != std::string::npos))
|
||||||
|
{
|
||||||
|
locator.index2 = index;
|
||||||
|
locator.type = geometry_column_locator::LON_LAT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool valid(geometry_column_locator const& locator, std::size_t max_size)
|
||||||
|
{
|
||||||
|
if (locator.type == geometry_column_locator::UNKNOWN) return false;
|
||||||
|
if (locator.index >= max_size) return false;
|
||||||
|
if (locator.type == geometry_column_locator::LON_LAT && locator.index2 >= max_size) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
mapnik::geometry::geometry<double> extract_geometry(std::vector<std::string> const& row, geometry_column_locator const& locator)
|
||||||
|
{
|
||||||
|
mapnik::geometry::geometry<double> geom;
|
||||||
|
if (locator.type == geometry_column_locator::WKT)
|
||||||
|
{
|
||||||
|
auto wkt_value = row.at(locator.index);
|
||||||
|
if (mapnik::from_wkt(wkt_value, geom))
|
||||||
|
{
|
||||||
|
// correct orientations ..
|
||||||
|
mapnik::geometry::correct(geom);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw mapnik::datasource_exception("Failed to parse WKT: '" + wkt_value + "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (locator.type == geometry_column_locator::GEOJSON)
|
||||||
|
{
|
||||||
|
|
||||||
|
auto json_value = row.at(locator.index);
|
||||||
|
if (!mapnik::json::from_geojson(json_value, geom))
|
||||||
|
{
|
||||||
|
throw mapnik::datasource_exception("Failed to parse GeoJSON: '" + json_value + "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (locator.type == geometry_column_locator::LON_LAT)
|
||||||
|
{
|
||||||
|
double x, y;
|
||||||
|
auto long_value = row.at(locator.index);
|
||||||
|
auto lat_value = row.at(locator.index2);
|
||||||
|
if (!mapnik::util::string2double(long_value,x))
|
||||||
|
{
|
||||||
|
throw mapnik::datasource_exception("Failed to parse Longitude: '" + long_value + "'");
|
||||||
|
}
|
||||||
|
if (!mapnik::util::string2double(lat_value,y))
|
||||||
|
{
|
||||||
|
throw mapnik::datasource_exception("Failed to parse Latitude: '" + lat_value + "'");
|
||||||
|
}
|
||||||
|
geom = mapnik::geometry::point<double>(x,y);
|
||||||
|
}
|
||||||
|
return geom;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace csv_utils
|
|
@ -24,220 +24,26 @@
|
||||||
#define MAPNIK_CSV_UTILS_DATASOURCE_HPP
|
#define MAPNIK_CSV_UTILS_DATASOURCE_HPP
|
||||||
|
|
||||||
// mapnik
|
// mapnik
|
||||||
#include <mapnik/debug.hpp>
|
#include <mapnik/box2d.hpp>
|
||||||
#include <mapnik/geometry.hpp>
|
#include <mapnik/geometry.hpp>
|
||||||
#include <mapnik/geometry_correct.hpp>
|
#include <mapnik/value_types.hpp>
|
||||||
#include <mapnik/wkt/wkt_factory.hpp>
|
|
||||||
#include <mapnik/json/geometry_parser.hpp>
|
|
||||||
#include <mapnik/util/conversions.hpp>
|
#include <mapnik/util/conversions.hpp>
|
||||||
#include <mapnik/csv/csv_grammar.hpp>
|
|
||||||
#include <mapnik/util/trim.hpp>
|
#include <mapnik/util/trim.hpp>
|
||||||
#include <mapnik/datasource.hpp>
|
#include <mapnik/csv/csv_types.hpp>
|
||||||
|
|
||||||
#pragma GCC diagnostic push
|
|
||||||
#include <mapnik/warning_ignore.hpp>
|
|
||||||
#include <boost/algorithm/string.hpp>
|
|
||||||
#pragma GCC diagnostic pop
|
|
||||||
|
|
||||||
|
// std
|
||||||
|
#include <iosfwd>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <cstdio>
|
#include <vector>
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
namespace csv_utils
|
namespace csv_utils {
|
||||||
{
|
|
||||||
|
|
||||||
static const mapnik::csv_line_grammar<char const*> line_g;
|
mapnik::csv_line parse_line(char const* start, char const* end, char separator, char quote, std::size_t num_columns);
|
||||||
static const mapnik::csv_white_space_skipper skipper{};
|
mapnik::csv_line parse_line(std::string const& line_str, char separator, char quote);
|
||||||
|
|
||||||
template <typename Iterator>
|
bool is_likely_number(std::string const& value);
|
||||||
static mapnik::csv_line parse_line(Iterator start, Iterator end, char separator, char quote, std::size_t num_columns)
|
|
||||||
{
|
|
||||||
mapnik::csv_line values;
|
|
||||||
if (num_columns > 0) values.reserve(num_columns);
|
|
||||||
if (!boost::spirit::qi::phrase_parse(start, end, (line_g)(separator, quote), skipper, values))
|
|
||||||
{
|
|
||||||
throw mapnik::datasource_exception("Failed to parse CSV line:\n" + std::string(start, end));
|
|
||||||
}
|
|
||||||
return values;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline mapnik::csv_line parse_line(std::string const& line_str, char separator, char quote)
|
bool ignore_case_equal(std::string const& s0, std::string const& s1);
|
||||||
{
|
|
||||||
auto start = line_str.c_str();
|
|
||||||
auto end = start + line_str.length();
|
|
||||||
return parse_line(start, end, separator, quote, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool is_likely_number(std::string const& value)
|
|
||||||
{
|
|
||||||
return (std::strspn( value.c_str(), "e-.+0123456789" ) == value.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ignore_case_equal_pred
|
|
||||||
{
|
|
||||||
bool operator () (unsigned char a, unsigned char b) const
|
|
||||||
{
|
|
||||||
return std::tolower(a) == std::tolower(b);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
inline bool ignore_case_equal(std::string const& s0, std::string const& s1)
|
|
||||||
{
|
|
||||||
return std::equal(s0.begin(), s0.end(),
|
|
||||||
s1.begin(), ignore_case_equal_pred());
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class CharT, class Traits, class Allocator>
|
|
||||||
std::basic_istream<CharT, Traits>& getline_csv(std::istream& is, std::basic_string<CharT,Traits,Allocator>& s, CharT delim, CharT quote)
|
|
||||||
{
|
|
||||||
typename std::basic_string<CharT,Traits,Allocator>::size_type nread = 0;
|
|
||||||
typename std::basic_istream<CharT, Traits>::sentry sentry(is, true);
|
|
||||||
if (sentry)
|
|
||||||
{
|
|
||||||
std::basic_streambuf<CharT, Traits>* buf = is.rdbuf();
|
|
||||||
s.clear();
|
|
||||||
bool has_quote = false;
|
|
||||||
while (nread < s.max_size())
|
|
||||||
{
|
|
||||||
int c1 = buf->sbumpc();
|
|
||||||
if (Traits::eq_int_type(c1, Traits::eof()))
|
|
||||||
{
|
|
||||||
is.setstate(std::ios_base::eofbit);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
++nread;
|
|
||||||
CharT c = Traits::to_char_type(c1);
|
|
||||||
if (Traits::eq(c, quote))
|
|
||||||
has_quote = !has_quote;
|
|
||||||
if (!Traits::eq(c, delim) || has_quote)
|
|
||||||
s.push_back(c);
|
|
||||||
else
|
|
||||||
break;// Character is extracted but not appended.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (nread == 0 || nread >= s.max_size())
|
|
||||||
is.setstate(std::ios_base::failbit);
|
|
||||||
|
|
||||||
return is;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
namespace detail {
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
std::size_t file_length(T & stream)
|
|
||||||
{
|
|
||||||
stream.seekg(0, std::ios::end);
|
|
||||||
return stream.tellg();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
std::tuple<char, bool, char, char> autodect_csv_flavour(T & stream, std::size_t file_length)
|
|
||||||
{
|
|
||||||
// autodetect newlines/quotes/separators
|
|
||||||
char newline = '\n'; // default
|
|
||||||
bool has_newline = false;
|
|
||||||
bool has_single_quote = false;
|
|
||||||
char quote = '"'; // default
|
|
||||||
char separator = ','; // default
|
|
||||||
// local counters
|
|
||||||
int num_commas = 0;
|
|
||||||
int num_tabs = 0;
|
|
||||||
int num_pipes = 0;
|
|
||||||
int num_semicolons = 0;
|
|
||||||
|
|
||||||
static std::size_t const max_size = 4000;
|
|
||||||
std::size_t size = std::min(file_length, max_size);
|
|
||||||
std::vector<char> buffer;
|
|
||||||
buffer.resize(size);
|
|
||||||
stream.read(buffer.data(), size);
|
|
||||||
for (auto c : buffer)
|
|
||||||
{
|
|
||||||
switch (c)
|
|
||||||
{
|
|
||||||
case '\r':
|
|
||||||
newline = '\r';
|
|
||||||
has_newline = true;
|
|
||||||
break;
|
|
||||||
case '\n':
|
|
||||||
has_newline = true;
|
|
||||||
break;
|
|
||||||
case '\'':
|
|
||||||
if (!has_single_quote)
|
|
||||||
{
|
|
||||||
quote = c;
|
|
||||||
has_single_quote = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ',':
|
|
||||||
if (!has_newline) ++num_commas;
|
|
||||||
break;
|
|
||||||
case '\t':
|
|
||||||
if (!has_newline) ++num_tabs;
|
|
||||||
break;
|
|
||||||
case '|':
|
|
||||||
if (!has_newline) ++num_pipes;
|
|
||||||
break;
|
|
||||||
case ';':
|
|
||||||
if (!has_newline) ++num_semicolons;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// detect separator
|
|
||||||
if (num_tabs > 0 && num_tabs > num_commas)
|
|
||||||
{
|
|
||||||
separator = '\t';
|
|
||||||
MAPNIK_LOG_DEBUG(csv) << "csv_datasource: auto detected tab separator";
|
|
||||||
}
|
|
||||||
else // pipes/semicolons
|
|
||||||
{
|
|
||||||
if (num_pipes > num_commas)
|
|
||||||
{
|
|
||||||
separator = '|';
|
|
||||||
MAPNIK_LOG_DEBUG(csv) << "csv_datasource: auto detected '|' separator";
|
|
||||||
}
|
|
||||||
else if (num_semicolons > num_commas)
|
|
||||||
{
|
|
||||||
separator = ';';
|
|
||||||
MAPNIK_LOG_DEBUG(csv) << "csv_datasource: auto detected ';' separator";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (has_newline && has_single_quote)
|
|
||||||
{
|
|
||||||
std::istringstream ss(std::string(buffer.begin(), buffer.end()));
|
|
||||||
std::size_t num_columns = 0;
|
|
||||||
for (std::string line; csv_utils::getline_csv(ss, line, newline, quote); )
|
|
||||||
{
|
|
||||||
if (size < file_length && ss.eof())
|
|
||||||
{
|
|
||||||
// we can't be sure last line
|
|
||||||
// is not truncated so skip it
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (line.size() == 0) continue; // empty lines are not interesting
|
|
||||||
auto num_quotes = std::count(line.begin(), line.end(), quote);
|
|
||||||
if (num_quotes % 2 != 0)
|
|
||||||
{
|
|
||||||
quote = '"';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
auto columns = csv_utils::parse_line(line, separator, quote);
|
|
||||||
if (num_columns > 0 && num_columns != columns.size())
|
|
||||||
{
|
|
||||||
quote = '"';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
num_columns = columns.size();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return std::make_tuple(newline, has_newline, separator, quote);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct geometry_column_locator
|
struct geometry_column_locator
|
||||||
{
|
{
|
||||||
|
@ -249,87 +55,18 @@ struct geometry_column_locator
|
||||||
std::size_t index2;
|
std::size_t index2;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline void locate_geometry_column(std::string const& header, std::size_t index, geometry_column_locator & locator)
|
namespace detail {
|
||||||
{
|
|
||||||
std::string lower_val(header);
|
|
||||||
std::transform(lower_val.begin(), lower_val.end(), lower_val.begin(), ::tolower);
|
|
||||||
if (lower_val == "wkt" || (lower_val.find("geom") != std::string::npos))
|
|
||||||
{
|
|
||||||
locator.type = geometry_column_locator::WKT;
|
|
||||||
locator.index = index;
|
|
||||||
}
|
|
||||||
else if (lower_val == "geojson")
|
|
||||||
{
|
|
||||||
locator.type = geometry_column_locator::GEOJSON;
|
|
||||||
locator.index = index;
|
|
||||||
}
|
|
||||||
else if (lower_val == "x" || lower_val == "lon"
|
|
||||||
|| lower_val == "lng" || lower_val == "long"
|
|
||||||
|| (lower_val.find("longitude") != std::string::npos))
|
|
||||||
{
|
|
||||||
locator.index = index;
|
|
||||||
locator.type = geometry_column_locator::LON_LAT;
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (lower_val == "y"
|
std::size_t file_length(std::istream & stream);
|
||||||
|| lower_val == "lat"
|
|
||||||
|| (lower_val.find("latitude") != std::string::npos))
|
|
||||||
{
|
|
||||||
locator.index2 = index;
|
|
||||||
locator.type = geometry_column_locator::LON_LAT;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool valid(geometry_column_locator const& locator, std::size_t max_size)
|
std::tuple<char, bool, char, char> autodect_csv_flavour(std::istream & stream, std::size_t file_length);
|
||||||
{
|
|
||||||
if (locator.type == geometry_column_locator::UNKNOWN) return false;
|
|
||||||
if (locator.index >= max_size) return false;
|
|
||||||
if (locator.type == geometry_column_locator::LON_LAT && locator.index2 >= max_size) return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline mapnik::geometry::geometry<double> extract_geometry(std::vector<std::string> const& row, geometry_column_locator const& locator)
|
void locate_geometry_column(std::string const& header, std::size_t index, geometry_column_locator & locator);
|
||||||
{
|
bool valid(geometry_column_locator const& locator, std::size_t max_size);
|
||||||
mapnik::geometry::geometry<double> geom;
|
|
||||||
if (locator.type == geometry_column_locator::WKT)
|
|
||||||
{
|
|
||||||
auto wkt_value = row.at(locator.index);
|
|
||||||
if (mapnik::from_wkt(wkt_value, geom))
|
|
||||||
{
|
|
||||||
// correct orientations ..
|
|
||||||
mapnik::geometry::correct(geom);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw mapnik::datasource_exception("Failed to parse WKT: '" + wkt_value + "'");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (locator.type == geometry_column_locator::GEOJSON)
|
|
||||||
{
|
|
||||||
|
|
||||||
auto json_value = row.at(locator.index);
|
} // namespace detail
|
||||||
if (!mapnik::json::from_geojson(json_value, geom))
|
|
||||||
{
|
mapnik::geometry::geometry<double> extract_geometry(std::vector<std::string> const& row, geometry_column_locator const& locator);
|
||||||
throw mapnik::datasource_exception("Failed to parse GeoJSON: '" + json_value + "'");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (locator.type == geometry_column_locator::LON_LAT)
|
|
||||||
{
|
|
||||||
double x, y;
|
|
||||||
auto long_value = row.at(locator.index);
|
|
||||||
auto lat_value = row.at(locator.index2);
|
|
||||||
if (!mapnik::util::string2double(long_value,x))
|
|
||||||
{
|
|
||||||
throw mapnik::datasource_exception("Failed to parse Longitude: '" + long_value + "'");
|
|
||||||
}
|
|
||||||
if (!mapnik::util::string2double(lat_value,y))
|
|
||||||
{
|
|
||||||
throw mapnik::datasource_exception("Failed to parse Latitude: '" + lat_value + "'");
|
|
||||||
}
|
|
||||||
geom = mapnik::geometry::point<double>(x,y);
|
|
||||||
}
|
|
||||||
return geom;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Feature, typename Headers, typename Values, typename Locator, typename Transcoder>
|
template <typename Feature, typename Headers, typename Values, typename Locator, typename Transcoder>
|
||||||
void process_properties(Feature & feature, Headers const& headers, Values const& values, Locator const& locator, Transcoder const& tr)
|
void process_properties(Feature & feature, Headers const& headers, Values const& values, Locator const& locator, Transcoder const& tr)
|
||||||
|
@ -348,8 +85,8 @@ void process_properties(Feature & feature, Headers const& headers, Values const&
|
||||||
std::string value = mapnik::util::trim_copy(*val_beg++);
|
std::string value = mapnik::util::trim_copy(*val_beg++);
|
||||||
int value_length = value.length();
|
int value_length = value.length();
|
||||||
|
|
||||||
if (locator.index == i && (locator.type == detail::geometry_column_locator::WKT
|
if (locator.index == i && (locator.type == geometry_column_locator::WKT
|
||||||
|| locator.type == detail::geometry_column_locator::GEOJSON) ) continue;
|
|| locator.type == geometry_column_locator::GEOJSON) ) continue;
|
||||||
|
|
||||||
|
|
||||||
bool matched = false;
|
bool matched = false;
|
||||||
|
@ -401,7 +138,28 @@ void process_properties(Feature & feature, Headers const& headers, Values const&
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct csv_file_parser
|
||||||
|
{
|
||||||
|
using box_type = mapnik::box2d<double>;
|
||||||
|
using item_type = std::pair<box_type, std::pair<std::size_t, std::size_t>>;
|
||||||
|
using boxes_type = std::vector<item_type>;
|
||||||
|
|
||||||
}// ns detail
|
void parse_csv(std::istream & csv_file, boxes_type & boxes);
|
||||||
|
|
||||||
|
virtual void add_feature(mapnik::value_integer index, mapnik::csv_line const & values);
|
||||||
|
|
||||||
|
std::vector<std::string> headers_;
|
||||||
|
std::string manual_headers_;
|
||||||
|
geometry_column_locator locator_;
|
||||||
|
mapnik::box2d<double> extent_;
|
||||||
|
mapnik::value_integer row_limit_ = 0;
|
||||||
|
char separator_ = '\0';
|
||||||
|
char quote_ = '\0';
|
||||||
|
bool strict_ = false;
|
||||||
|
bool extent_initialized_ = false;
|
||||||
|
bool has_disk_index_ = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace csv_utils
|
||||||
|
|
||||||
#endif // MAPNIK_CSV_UTILS_DATASOURCE_HPP
|
#endif // MAPNIK_CSV_UTILS_DATASOURCE_HPP
|
||||||
|
|
|
@ -45,21 +45,27 @@ using mapnik::layer_descriptor;
|
||||||
using mapnik::datasource_exception;
|
using mapnik::datasource_exception;
|
||||||
|
|
||||||
|
|
||||||
|
static bool GDALAllRegister_once_()
|
||||||
|
{
|
||||||
|
static bool const quiet_unused = (GDALAllRegister(), true);
|
||||||
|
return quiet_unused;
|
||||||
|
}
|
||||||
|
|
||||||
gdal_datasource::gdal_datasource(parameters const& params)
|
gdal_datasource::gdal_datasource(parameters const& params)
|
||||||
: datasource(params),
|
: datasource(params),
|
||||||
dataset_(nullptr),
|
dataset_(nullptr, &GDALClose),
|
||||||
desc_(gdal_datasource::name(), "utf-8"),
|
desc_(gdal_datasource::name(), "utf-8"),
|
||||||
nodata_value_(params.get<double>("nodata")),
|
nodata_value_(params.get<double>("nodata")),
|
||||||
nodata_tolerance_(*params.get<double>("nodata_tolerance",1e-12))
|
nodata_tolerance_(*params.get<double>("nodata_tolerance",1e-12))
|
||||||
{
|
{
|
||||||
MAPNIK_LOG_DEBUG(gdal) << "gdal_datasource: Initializing...";
|
MAPNIK_LOG_DEBUG(gdal) << "gdal_datasource: Initializing...";
|
||||||
|
|
||||||
|
GDALAllRegister_once_();
|
||||||
|
|
||||||
#ifdef MAPNIK_STATS
|
#ifdef MAPNIK_STATS
|
||||||
mapnik::progress_timer __stats__(std::clog, "gdal_datasource::init");
|
mapnik::progress_timer __stats__(std::clog, "gdal_datasource::init");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
GDALAllRegister();
|
|
||||||
|
|
||||||
boost::optional<std::string> file = params.get<std::string>("file");
|
boost::optional<std::string> file = params.get<std::string>("file");
|
||||||
if (! file) throw datasource_exception("missing <file> parameter");
|
if (! file) throw datasource_exception("missing <file> parameter");
|
||||||
|
|
||||||
|
@ -79,12 +85,14 @@ gdal_datasource::gdal_datasource(parameters const& params)
|
||||||
#if GDAL_VERSION_NUM >= 1600
|
#if GDAL_VERSION_NUM >= 1600
|
||||||
if (shared_dataset_)
|
if (shared_dataset_)
|
||||||
{
|
{
|
||||||
dataset_ = reinterpret_cast<GDALDataset*>(GDALOpenShared((dataset_name_).c_str(), GA_ReadOnly));
|
auto ds = GDALOpenShared(dataset_name_.c_str(), GA_ReadOnly);
|
||||||
|
dataset_.reset(static_cast<GDALDataset*>(ds));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
dataset_ = reinterpret_cast<GDALDataset*>(GDALOpen((dataset_name_).c_str(), GA_ReadOnly));
|
auto ds = GDALOpen(dataset_name_.c_str(), GA_ReadOnly);
|
||||||
|
dataset_.reset(static_cast<GDALDataset*>(ds));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! dataset_)
|
if (! dataset_)
|
||||||
|
@ -92,7 +100,7 @@ gdal_datasource::gdal_datasource(parameters const& params)
|
||||||
throw datasource_exception(CPLGetLastErrorMsg());
|
throw datasource_exception(CPLGetLastErrorMsg());
|
||||||
}
|
}
|
||||||
|
|
||||||
MAPNIK_LOG_DEBUG(gdal) << "gdal_featureset: opened Dataset=" << dataset_;
|
MAPNIK_LOG_DEBUG(gdal) << "gdal_featureset: opened Dataset=" << dataset_.get();
|
||||||
|
|
||||||
nbands_ = dataset_->GetRasterCount();
|
nbands_ = dataset_->GetRasterCount();
|
||||||
width_ = dataset_->GetRasterXSize();
|
width_ = dataset_->GetRasterXSize();
|
||||||
|
@ -182,8 +190,7 @@ gdal_datasource::gdal_datasource(parameters const& params)
|
||||||
|
|
||||||
gdal_datasource::~gdal_datasource()
|
gdal_datasource::~gdal_datasource()
|
||||||
{
|
{
|
||||||
MAPNIK_LOG_DEBUG(gdal) << "gdal_featureset: Closing Dataset=" << dataset_;
|
MAPNIK_LOG_DEBUG(gdal) << "gdal_featureset: Closing Dataset=" << dataset_.get();
|
||||||
GDALClose(dataset_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
datasource::datasource_t gdal_datasource::type() const
|
datasource::datasource_t gdal_datasource::type() const
|
||||||
|
@ -217,12 +224,9 @@ featureset_ptr gdal_datasource::features(query const& q) const
|
||||||
mapnik::progress_timer __stats__(std::clog, "gdal_datasource::features");
|
mapnik::progress_timer __stats__(std::clog, "gdal_datasource::features");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
gdal_query gq = q;
|
return std::make_shared<gdal_featureset>(*dataset_,
|
||||||
|
|
||||||
// TODO - move to std::make_shared, but must reduce # of args to <= 9
|
|
||||||
return featureset_ptr(new gdal_featureset(*dataset_,
|
|
||||||
band_,
|
band_,
|
||||||
gq,
|
gdal_query(q),
|
||||||
extent_,
|
extent_,
|
||||||
width_,
|
width_,
|
||||||
height_,
|
height_,
|
||||||
|
@ -230,7 +234,7 @@ featureset_ptr gdal_datasource::features(query const& q) const
|
||||||
dx_,
|
dx_,
|
||||||
dy_,
|
dy_,
|
||||||
nodata_value_,
|
nodata_value_,
|
||||||
nodata_tolerance_));
|
nodata_tolerance_);
|
||||||
}
|
}
|
||||||
|
|
||||||
featureset_ptr gdal_datasource::features_at_point(coord2d const& pt, double tol) const
|
featureset_ptr gdal_datasource::features_at_point(coord2d const& pt, double tol) const
|
||||||
|
@ -239,12 +243,9 @@ featureset_ptr gdal_datasource::features_at_point(coord2d const& pt, double tol)
|
||||||
mapnik::progress_timer __stats__(std::clog, "gdal_datasource::features_at_point");
|
mapnik::progress_timer __stats__(std::clog, "gdal_datasource::features_at_point");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
gdal_query gq = pt;
|
return std::make_shared<gdal_featureset>(*dataset_,
|
||||||
|
|
||||||
// TODO - move to std::make_shared, but must reduce # of args to <= 9
|
|
||||||
return featureset_ptr(new gdal_featureset(*dataset_,
|
|
||||||
band_,
|
band_,
|
||||||
gq,
|
gdal_query(pt),
|
||||||
extent_,
|
extent_,
|
||||||
width_,
|
width_,
|
||||||
height_,
|
height_,
|
||||||
|
@ -252,5 +253,5 @@ featureset_ptr gdal_datasource::features_at_point(coord2d const& pt, double tol)
|
||||||
dx_,
|
dx_,
|
||||||
dy_,
|
dy_,
|
||||||
nodata_value_,
|
nodata_value_,
|
||||||
nodata_tolerance_));
|
nodata_tolerance_);
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,8 +55,7 @@ public:
|
||||||
boost::optional<mapnik::datasource_geometry_t> get_geometry_type() const;
|
boost::optional<mapnik::datasource_geometry_t> get_geometry_type() const;
|
||||||
mapnik::layer_descriptor get_descriptor() const;
|
mapnik::layer_descriptor get_descriptor() const;
|
||||||
private:
|
private:
|
||||||
GDALDataset* open_dataset() const;
|
std::unique_ptr<GDALDataset, decltype(&GDALClose)> dataset_;
|
||||||
GDALDataset* dataset_;
|
|
||||||
mapnik::box2d<double> extent_;
|
mapnik::box2d<double> extent_;
|
||||||
std::string dataset_name_;
|
std::string dataset_name_;
|
||||||
int band_;
|
int band_;
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
// mapnik
|
// mapnik
|
||||||
#include <mapnik/make_unique.hpp>
|
#include <mapnik/datasource.hpp>
|
||||||
#include <mapnik/global.hpp>
|
#include <mapnik/global.hpp>
|
||||||
#include <mapnik/debug.hpp>
|
#include <mapnik/debug.hpp>
|
||||||
#include <mapnik/image.hpp>
|
#include <mapnik/image.hpp>
|
||||||
|
@ -29,7 +29,6 @@
|
||||||
#include <mapnik/view_transform.hpp>
|
#include <mapnik/view_transform.hpp>
|
||||||
#include <mapnik/feature.hpp>
|
#include <mapnik/feature.hpp>
|
||||||
#include <mapnik/feature_factory.hpp>
|
#include <mapnik/feature_factory.hpp>
|
||||||
#include <mapnik/util/variant.hpp>
|
|
||||||
|
|
||||||
// stl
|
// stl
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
@ -39,8 +38,6 @@
|
||||||
#include "gdal_featureset.hpp"
|
#include "gdal_featureset.hpp"
|
||||||
#include <gdal_priv.h>
|
#include <gdal_priv.h>
|
||||||
|
|
||||||
using mapnik::query;
|
|
||||||
using mapnik::coord2d;
|
|
||||||
using mapnik::box2d;
|
using mapnik::box2d;
|
||||||
using mapnik::feature_ptr;
|
using mapnik::feature_ptr;
|
||||||
using mapnik::view_transform;
|
using mapnik::view_transform;
|
||||||
|
@ -77,8 +74,6 @@ gdal_featureset::gdal_featureset(GDALDataset& dataset,
|
||||||
|
|
||||||
gdal_featureset::~gdal_featureset()
|
gdal_featureset::~gdal_featureset()
|
||||||
{
|
{
|
||||||
MAPNIK_LOG_DEBUG(gdal) << "gdal_featureset: Closing Dataset=" << &dataset_;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
feature_ptr gdal_featureset::next()
|
feature_ptr gdal_featureset::next()
|
||||||
|
|
|
@ -24,13 +24,12 @@
|
||||||
#define GDAL_FEATURESET_HPP
|
#define GDAL_FEATURESET_HPP
|
||||||
|
|
||||||
// mapnik
|
// mapnik
|
||||||
#include <mapnik/feature.hpp>
|
#include <mapnik/featureset.hpp>
|
||||||
|
#include <mapnik/query.hpp>
|
||||||
#include <mapnik/util/variant.hpp>
|
#include <mapnik/util/variant.hpp>
|
||||||
// boost
|
// boost
|
||||||
#include <boost/optional.hpp>
|
#include <boost/optional.hpp>
|
||||||
|
|
||||||
#include "gdal_datasource.hpp"
|
|
||||||
|
|
||||||
class GDALDataset;
|
class GDALDataset;
|
||||||
class GDALRasterBand;
|
class GDALRasterBand;
|
||||||
|
|
||||||
|
|
|
@ -222,6 +222,7 @@ source = Split(
|
||||||
warp.cpp
|
warp.cpp
|
||||||
css_color_grammar_x3.cpp
|
css_color_grammar_x3.cpp
|
||||||
vertex_cache.cpp
|
vertex_cache.cpp
|
||||||
|
vertex_adapters.cpp
|
||||||
text/font_library.cpp
|
text/font_library.cpp
|
||||||
text/text_layout.cpp
|
text/text_layout.cpp
|
||||||
text/text_line.cpp
|
text/text_line.cpp
|
||||||
|
|
37
src/json/mapnik_geometry_to_geojson.cpp
Normal file
37
src/json/mapnik_geometry_to_geojson.cpp
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
/*****************************************************************************
|
||||||
|
*
|
||||||
|
* 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 <mapnik/util/geometry_to_geojson.hpp>
|
||||||
|
#include <mapnik/json/geometry_generator_grammar.hpp>
|
||||||
|
|
||||||
|
namespace mapnik { namespace util {
|
||||||
|
|
||||||
|
bool to_geojson(std::string & json, mapnik::geometry::geometry<double> const& geom)
|
||||||
|
{
|
||||||
|
using sink_type = std::back_insert_iterator<std::string>;
|
||||||
|
static const mapnik::json::geometry_generator_grammar<sink_type, mapnik::geometry::geometry<double> > grammar;
|
||||||
|
sink_type sink(json);
|
||||||
|
return boost::spirit::karma::generate(sink, grammar, geom);
|
||||||
|
}
|
||||||
|
|
||||||
|
}}
|
43
src/json/mapnik_json_geometry_parser.cpp
Normal file
43
src/json/mapnik_json_geometry_parser.cpp
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
/*****************************************************************************
|
||||||
|
*
|
||||||
|
* This file is part of Mapnik (c++ mapping toolkit)
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 Artem Pavlenko
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
#include <mapnik/json/geometry_parser.hpp>
|
||||||
|
#include <mapnik/json/geometry_grammar.hpp>
|
||||||
|
|
||||||
|
// boost
|
||||||
|
#include <boost/spirit/include/qi.hpp>
|
||||||
|
#include <boost/spirit/include/phoenix_core.hpp>
|
||||||
|
|
||||||
|
namespace mapnik { namespace json {
|
||||||
|
|
||||||
|
bool from_geojson(std::string const& json, mapnik::geometry::geometry<double> & geom)
|
||||||
|
{
|
||||||
|
using namespace boost::spirit;
|
||||||
|
static const geometry_grammar<char const*> g;
|
||||||
|
standard::space_type space;
|
||||||
|
char const* start = json.c_str();
|
||||||
|
char const* end = start + json.length();
|
||||||
|
return qi::phrase_parse(start, end, g, space, geom);
|
||||||
|
}
|
||||||
|
|
||||||
|
}}
|
27
src/json/mapnik_json_positions_grammar.cpp
Normal file
27
src/json/mapnik_json_positions_grammar.cpp
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
/*****************************************************************************
|
||||||
|
*
|
||||||
|
* 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 <mapnik/json/positions_grammar_impl.hpp>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
using iterator_type = char const*;
|
||||||
|
template struct mapnik::json::positions_grammar<iterator_type>;
|
|
@ -23,7 +23,7 @@
|
||||||
// mapnik
|
// mapnik
|
||||||
#include <mapnik/marker_helpers.hpp>
|
#include <mapnik/marker_helpers.hpp>
|
||||||
#include <mapnik/svg/svg_converter.hpp>
|
#include <mapnik/svg/svg_converter.hpp>
|
||||||
|
#include <mapnik/label_collision_detector.hpp>
|
||||||
#include "agg_ellipse.h"
|
#include "agg_ellipse.h"
|
||||||
#include "agg_color_rgba.h"
|
#include "agg_color_rgba.h"
|
||||||
|
|
||||||
|
@ -158,5 +158,142 @@ void setup_transform_scaling(agg::trans_affine & tr,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename Processor>
|
||||||
|
void apply_markers_single(vertex_converter_type & converter, Processor & proc,
|
||||||
|
geometry::geometry<double> const& geom, geometry::geometry_types type)
|
||||||
|
{
|
||||||
|
if (type == geometry::geometry_types::Point)
|
||||||
|
{
|
||||||
|
geometry::point_vertex_adapter<double> va(geom.get<geometry::point<double>>());
|
||||||
|
converter.apply(va, proc);
|
||||||
|
}
|
||||||
|
else if (type == geometry::geometry_types::LineString)
|
||||||
|
{
|
||||||
|
geometry::line_string_vertex_adapter<double> va(geom.get<geometry::line_string<double>>());
|
||||||
|
converter.apply(va, proc);
|
||||||
|
}
|
||||||
|
else if (type == geometry::geometry_types::Polygon)
|
||||||
|
{
|
||||||
|
geometry::polygon_vertex_adapter<double> va(geom.get<geometry::polygon<double>>());
|
||||||
|
converter.apply(va, proc);
|
||||||
|
}
|
||||||
|
else if (type == geometry::geometry_types::MultiPoint)
|
||||||
|
{
|
||||||
|
for (auto const& pt : geom.get<geometry::multi_point<double>>())
|
||||||
|
{
|
||||||
|
geometry::point_vertex_adapter<double> va(pt);
|
||||||
|
converter.apply(va, proc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (type == geometry::geometry_types::MultiLineString)
|
||||||
|
{
|
||||||
|
for (auto const& line : geom.get<geometry::multi_line_string<double>>())
|
||||||
|
{
|
||||||
|
geometry::line_string_vertex_adapter<double> va(line);
|
||||||
|
converter.apply(va, proc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (type == geometry::geometry_types::MultiPolygon)
|
||||||
|
{
|
||||||
|
for (auto const& poly : geom.get<geometry::multi_polygon<double>>())
|
||||||
|
{
|
||||||
|
geometry::polygon_vertex_adapter<double> va(poly);
|
||||||
|
converter.apply(va, proc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Processor>
|
||||||
|
void apply_markers_multi(feature_impl const& feature, attributes const& vars,
|
||||||
|
vertex_converter_type & converter, Processor & proc, symbolizer_base const& sym)
|
||||||
|
{
|
||||||
|
auto const& geom = feature.get_geometry();
|
||||||
|
geometry::geometry_types type = geometry::geometry_type(geom);
|
||||||
|
|
||||||
|
if (type == geometry::geometry_types::Point
|
||||||
|
||
|
||||||
|
type == geometry::geometry_types::LineString
|
||||||
|
||
|
||||||
|
type == geometry::geometry_types::Polygon)
|
||||||
|
{
|
||||||
|
apply_markers_single(converter, proc, geom, type);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
marker_multi_policy_enum multi_policy = get<marker_multi_policy_enum, keys::markers_multipolicy>(sym, feature, vars);
|
||||||
|
marker_placement_enum placement = get<marker_placement_enum, keys::markers_placement_type>(sym, feature, vars);
|
||||||
|
|
||||||
|
if (placement == MARKER_POINT_PLACEMENT &&
|
||||||
|
multi_policy == MARKER_WHOLE_MULTI)
|
||||||
|
{
|
||||||
|
geometry::point<double> pt;
|
||||||
|
// test if centroid is contained by bounding box
|
||||||
|
if (geometry::centroid(geom, pt) && converter.disp_.args_.bbox.contains(pt.x, pt.y))
|
||||||
|
{
|
||||||
|
// unset any clipping since we're now dealing with a point
|
||||||
|
converter.template unset<clip_poly_tag>();
|
||||||
|
geometry::point_vertex_adapter<double> va(pt);
|
||||||
|
converter.apply(va, proc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ((placement == MARKER_POINT_PLACEMENT || placement == MARKER_INTERIOR_PLACEMENT) &&
|
||||||
|
multi_policy == MARKER_LARGEST_MULTI)
|
||||||
|
{
|
||||||
|
// Only apply to path with largest envelope area
|
||||||
|
// TODO: consider using true area for polygon types
|
||||||
|
if (type == geometry::geometry_types::MultiPolygon)
|
||||||
|
{
|
||||||
|
geometry::multi_polygon<double> const& multi_poly = mapnik::util::get<geometry::multi_polygon<double> >(geom);
|
||||||
|
double maxarea = 0;
|
||||||
|
geometry::polygon<double> const* largest = 0;
|
||||||
|
for (geometry::polygon<double> const& poly : multi_poly)
|
||||||
|
{
|
||||||
|
box2d<double> bbox = geometry::envelope(poly);
|
||||||
|
double area = bbox.width() * bbox.height();
|
||||||
|
if (area > maxarea)
|
||||||
|
{
|
||||||
|
maxarea = area;
|
||||||
|
largest = &poly;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (largest)
|
||||||
|
{
|
||||||
|
geometry::polygon_vertex_adapter<double> va(*largest);
|
||||||
|
converter.apply(va, proc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MAPNIK_LOG_WARN(marker_symbolizer) << "TODO: if you get here -> open an issue";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (multi_policy != MARKER_EACH_MULTI && placement != MARKER_POINT_PLACEMENT)
|
||||||
|
{
|
||||||
|
MAPNIK_LOG_WARN(marker_symbolizer) << "marker_multi_policy != 'each' has no effect with marker_placement != 'point'";
|
||||||
|
}
|
||||||
|
if (type == geometry::geometry_types::GeometryCollection)
|
||||||
|
{
|
||||||
|
for (auto const& g : geom.get<geometry::geometry_collection<double>>())
|
||||||
|
{
|
||||||
|
apply_markers_single(converter, proc, g, geometry::geometry_type(g));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
apply_markers_single(converter, proc, geom, type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
template void apply_markers_multi<vector_dispatch_type>(feature_impl const& feature, attributes const& vars,
|
||||||
|
vertex_converter_type & converter, vector_dispatch_type & proc,
|
||||||
|
symbolizer_base const& sym);
|
||||||
|
|
||||||
|
template void apply_markers_multi<raster_dispatch_type>(feature_impl const& feature, attributes const& vars,
|
||||||
|
vertex_converter_type & converter, raster_dispatch_type & proc,
|
||||||
|
symbolizer_base const& sym);
|
||||||
|
|
||||||
} // end namespace mapnik
|
} // end namespace mapnik
|
||||||
|
|
|
@ -20,9 +20,9 @@
|
||||||
*
|
*
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#include <mapnik/label_collision_detector.hpp>
|
||||||
#include <mapnik/svg/svg_storage.hpp>
|
#include <mapnik/svg/svg_storage.hpp>
|
||||||
#include <mapnik/svg/svg_path_adapter.hpp>
|
#include <mapnik/svg/svg_path_adapter.hpp>
|
||||||
#include <mapnik/vertex_converters.hpp>
|
|
||||||
#include <mapnik/marker_cache.hpp>
|
#include <mapnik/marker_cache.hpp>
|
||||||
#include <mapnik/marker_helpers.hpp>
|
#include <mapnik/marker_helpers.hpp>
|
||||||
#include <mapnik/geometry_type.hpp>
|
#include <mapnik/geometry_type.hpp>
|
||||||
|
@ -39,14 +39,6 @@ struct render_marker_symbolizer_visitor
|
||||||
using vector_dispatch_type = vector_markers_dispatch<Detector>;
|
using vector_dispatch_type = vector_markers_dispatch<Detector>;
|
||||||
using raster_dispatch_type = raster_markers_dispatch<Detector>;
|
using raster_dispatch_type = raster_markers_dispatch<Detector>;
|
||||||
|
|
||||||
using vertex_converter_type = vertex_converter<clip_line_tag,
|
|
||||||
clip_poly_tag,
|
|
||||||
transform_tag,
|
|
||||||
affine_transform_tag,
|
|
||||||
simplify_tag,
|
|
||||||
smooth_tag,
|
|
||||||
offset_transform_tag>;
|
|
||||||
|
|
||||||
render_marker_symbolizer_visitor(std::string const& filename,
|
render_marker_symbolizer_visitor(std::string const& filename,
|
||||||
markers_symbolizer const& sym,
|
markers_symbolizer const& sym,
|
||||||
mapnik::feature_impl & feature,
|
mapnik::feature_impl & feature,
|
||||||
|
@ -62,134 +54,131 @@ struct render_marker_symbolizer_visitor
|
||||||
clip_box_(clip_box),
|
clip_box_(clip_box),
|
||||||
renderer_context_(renderer_context) {}
|
renderer_context_(renderer_context) {}
|
||||||
|
|
||||||
|
svg_attribute_type const& get_marker_attributes(svg_path_ptr const& stock_marker,
|
||||||
|
svg_attribute_type & custom_attr) const
|
||||||
|
{
|
||||||
|
auto const& stock_attr = stock_marker->attributes();
|
||||||
|
if (push_explicit_style(stock_attr, custom_attr, sym_, feature_, common_.vars_))
|
||||||
|
return custom_attr;
|
||||||
|
else
|
||||||
|
return stock_attr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Marker, typename Dispatch>
|
||||||
|
void render_marker(Marker const& mark, Dispatch & rasterizer_dispatch) const
|
||||||
|
{
|
||||||
|
auto const& vars = common_.vars_;
|
||||||
|
|
||||||
|
agg::trans_affine geom_tr;
|
||||||
|
if (auto geometry_transform = get_optional<transform_type>(sym_, keys::geometry_transform))
|
||||||
|
{
|
||||||
|
evaluate_transform(geom_tr, feature_, vars, *geometry_transform, common_.scale_factor_);
|
||||||
|
}
|
||||||
|
|
||||||
|
vertex_converter_type converter(clip_box_,
|
||||||
|
sym_,
|
||||||
|
common_.t_,
|
||||||
|
prj_trans_,
|
||||||
|
geom_tr,
|
||||||
|
feature_,
|
||||||
|
vars,
|
||||||
|
common_.scale_factor_);
|
||||||
|
|
||||||
|
bool clip = get<value_bool, keys::clip>(sym_, feature_, vars);
|
||||||
|
double offset = get<value_double, keys::offset>(sym_, feature_, vars);
|
||||||
|
double simplify_tolerance = get<value_double, keys::simplify_tolerance>(sym_, feature_, vars);
|
||||||
|
double smooth = get<value_double, keys::smooth>(sym_, feature_, vars);
|
||||||
|
|
||||||
|
if (clip)
|
||||||
|
{
|
||||||
|
geometry::geometry_types type = geometry::geometry_type(feature_.get_geometry());
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case geometry::geometry_types::Polygon:
|
||||||
|
case geometry::geometry_types::MultiPolygon:
|
||||||
|
converter.template set<clip_poly_tag>();
|
||||||
|
break;
|
||||||
|
case geometry::geometry_types::LineString:
|
||||||
|
case geometry::geometry_types::MultiLineString:
|
||||||
|
converter.template set<clip_line_tag>();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// silence warning: 4 enumeration values not handled in switch
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
converter.template set<transform_tag>(); //always transform
|
||||||
|
if (std::fabs(offset) > 0.0) converter.template set<offset_transform_tag>(); // parallel offset
|
||||||
|
converter.template set<affine_transform_tag>(); // optional affine transform
|
||||||
|
if (simplify_tolerance > 0.0) converter.template set<simplify_tag>(); // optional simplify converter
|
||||||
|
if (smooth > 0.0) converter.template set<smooth_tag>(); // optional smooth converter
|
||||||
|
|
||||||
|
apply_markers_multi(feature_, vars, converter, rasterizer_dispatch, sym_);
|
||||||
|
}
|
||||||
|
|
||||||
void operator() (marker_null const&) const {}
|
void operator() (marker_null const&) const {}
|
||||||
|
|
||||||
void operator() (marker_svg const& mark) const
|
void operator() (marker_svg const& mark) const
|
||||||
{
|
{
|
||||||
using namespace mapnik::svg;
|
using namespace mapnik::svg;
|
||||||
bool clip = get<value_bool, keys::clip>(sym_, feature_, common_.vars_);
|
|
||||||
double offset = get<value_double, keys::offset>(sym_, feature_, common_.vars_);
|
|
||||||
double simplify_tolerance = get<value_double, keys::simplify_tolerance>(sym_, feature_, common_.vars_);
|
|
||||||
double smooth = get<value_double, keys::smooth>(sym_, feature_, common_.vars_);
|
|
||||||
|
|
||||||
// https://github.com/mapnik/mapnik/issues/1316
|
// https://github.com/mapnik/mapnik/issues/1316
|
||||||
bool snap_to_pixels = !mapnik::marker_cache::instance().is_uri(filename_);
|
bool snap_to_pixels = !mapnik::marker_cache::instance().is_uri(filename_);
|
||||||
|
|
||||||
agg::trans_affine geom_tr;
|
|
||||||
auto transform = get_optional<transform_type>(sym_, keys::geometry_transform);
|
|
||||||
if (transform) evaluate_transform(geom_tr, feature_, common_.vars_, *transform, common_.scale_factor_);
|
|
||||||
agg::trans_affine image_tr = agg::trans_affine_scaling(common_.scale_factor_);
|
agg::trans_affine image_tr = agg::trans_affine_scaling(common_.scale_factor_);
|
||||||
|
|
||||||
boost::optional<svg_path_ptr> const& stock_vector_marker = mark.get_data();
|
boost::optional<svg_path_ptr> const& stock_vector_marker = mark.get_data();
|
||||||
|
svg_path_ptr marker_ptr = *stock_vector_marker;
|
||||||
|
bool is_ellipse = false;
|
||||||
|
|
||||||
|
svg_attribute_type s_attributes;
|
||||||
|
auto const& r_attributes = get_marker_attributes(*stock_vector_marker, s_attributes);
|
||||||
|
|
||||||
// special case for simple ellipse markers
|
// special case for simple ellipse markers
|
||||||
// to allow for full control over rx/ry dimensions
|
// to allow for full control over rx/ry dimensions
|
||||||
if (filename_ == "shape://ellipse"
|
if (filename_ == "shape://ellipse"
|
||||||
&& (has_key(sym_,keys::width) || has_key(sym_,keys::height)))
|
&& (has_key(sym_,keys::width) || has_key(sym_,keys::height)))
|
||||||
{
|
{
|
||||||
svg_path_ptr marker_ellipse = std::make_shared<svg_storage_type>();
|
marker_ptr = std::make_shared<svg_storage_type>();
|
||||||
vertex_stl_adapter<svg_path_storage> stl_storage(marker_ellipse->source());
|
is_ellipse = true;
|
||||||
svg_path_adapter svg_path(stl_storage);
|
|
||||||
build_ellipse(sym_, feature_, common_.vars_, *marker_ellipse, svg_path);
|
|
||||||
svg_attribute_type s_attributes;
|
|
||||||
bool result = push_explicit_style( (*stock_vector_marker)->attributes(), s_attributes, sym_, feature_, common_.vars_);
|
|
||||||
auto image_transform = get_optional<transform_type>(sym_, keys::image_transform);
|
|
||||||
if (image_transform) evaluate_transform(image_tr, feature_, common_.vars_, *image_transform);
|
|
||||||
vector_dispatch_type rasterizer_dispatch(marker_ellipse,
|
|
||||||
svg_path,
|
|
||||||
result ? s_attributes : (*stock_vector_marker)->attributes(),
|
|
||||||
image_tr,
|
|
||||||
sym_,
|
|
||||||
*common_.detector_,
|
|
||||||
common_.scale_factor_,
|
|
||||||
feature_,
|
|
||||||
common_.vars_,
|
|
||||||
snap_to_pixels,
|
|
||||||
renderer_context_);
|
|
||||||
|
|
||||||
vertex_converter_type converter(clip_box_,
|
|
||||||
sym_,
|
|
||||||
common_.t_,
|
|
||||||
prj_trans_,
|
|
||||||
geom_tr,
|
|
||||||
feature_,
|
|
||||||
common_.vars_,
|
|
||||||
common_.scale_factor_);
|
|
||||||
if (clip)
|
|
||||||
{
|
|
||||||
geometry::geometry_types type = geometry::geometry_type(feature_.get_geometry());
|
|
||||||
if (type == geometry::geometry_types::Polygon || type == geometry::geometry_types::MultiPolygon)
|
|
||||||
converter.template set<clip_poly_tag>();
|
|
||||||
else if (type == geometry::geometry_types::LineString || type == geometry::geometry_types::MultiLineString)
|
|
||||||
converter.template set<clip_line_tag>();
|
|
||||||
}
|
|
||||||
|
|
||||||
converter.template set<transform_tag>(); //always transform
|
|
||||||
if (std::fabs(offset) > 0.0) converter.template set<offset_transform_tag>(); // parallel offset
|
|
||||||
converter.template set<affine_transform_tag>(); // optional affine transform
|
|
||||||
if (simplify_tolerance > 0.0) converter.template set<simplify_tag>(); // optional simplify converter
|
|
||||||
if (smooth > 0.0) converter.template set<smooth_tag>(); // optional smooth converter
|
|
||||||
apply_markers_multi(feature_, common_.vars_, converter, rasterizer_dispatch, sym_);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
box2d<double> const& bbox = mark.bounding_box();
|
box2d<double> const& bbox = mark.bounding_box();
|
||||||
setup_transform_scaling(image_tr, bbox.width(), bbox.height(), feature_, common_.vars_, sym_);
|
setup_transform_scaling(image_tr, bbox.width(), bbox.height(), feature_, common_.vars_, sym_);
|
||||||
auto image_transform = get_optional<transform_type>(sym_, keys::image_transform);
|
|
||||||
if (image_transform) evaluate_transform(image_tr, feature_, common_.vars_, *image_transform);
|
|
||||||
vertex_stl_adapter<svg_path_storage> stl_storage((*stock_vector_marker)->source());
|
|
||||||
svg_path_adapter svg_path(stl_storage);
|
|
||||||
svg_attribute_type s_attributes;
|
|
||||||
bool result = push_explicit_style( (*stock_vector_marker)->attributes(), s_attributes, sym_, feature_, common_.vars_);
|
|
||||||
vector_dispatch_type rasterizer_dispatch(*stock_vector_marker,
|
|
||||||
svg_path,
|
|
||||||
result ? s_attributes : (*stock_vector_marker)->attributes(),
|
|
||||||
image_tr,
|
|
||||||
sym_,
|
|
||||||
*common_.detector_,
|
|
||||||
common_.scale_factor_,
|
|
||||||
feature_,
|
|
||||||
common_.vars_,
|
|
||||||
snap_to_pixels,
|
|
||||||
renderer_context_);
|
|
||||||
|
|
||||||
vertex_converter_type converter(clip_box_,
|
|
||||||
sym_,
|
|
||||||
common_.t_,
|
|
||||||
prj_trans_,
|
|
||||||
geom_tr,
|
|
||||||
feature_,
|
|
||||||
common_.vars_,
|
|
||||||
common_.scale_factor_);
|
|
||||||
if (clip)
|
|
||||||
{
|
|
||||||
geometry::geometry_types type = geometry::geometry_type(feature_.get_geometry());
|
|
||||||
if (type == geometry::geometry_types::Polygon || type == geometry::geometry_types::MultiPolygon)
|
|
||||||
converter.template set<clip_poly_tag>();
|
|
||||||
else if (type == geometry::geometry_types::LineString || type == geometry::geometry_types::MultiLineString)
|
|
||||||
converter.template set<clip_line_tag>();
|
|
||||||
}
|
|
||||||
|
|
||||||
converter.template set<transform_tag>(); //always transform
|
|
||||||
if (std::fabs(offset) > 0.0) converter.template set<offset_transform_tag>(); // parallel offset
|
|
||||||
converter.template set<affine_transform_tag>(); // optional affine transform
|
|
||||||
if (simplify_tolerance > 0.0) converter.template set<simplify_tag>(); // optional simplify converter
|
|
||||||
if (smooth > 0.0) converter.template set<smooth_tag>(); // optional smooth converter
|
|
||||||
apply_markers_multi(feature_, common_.vars_, converter, rasterizer_dispatch, sym_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vertex_stl_adapter<svg_path_storage> stl_storage(marker_ptr->source());
|
||||||
|
svg_path_adapter svg_path(stl_storage);
|
||||||
|
|
||||||
|
if (is_ellipse)
|
||||||
|
{
|
||||||
|
build_ellipse(sym_, feature_, common_.vars_, *marker_ptr, svg_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto image_transform = get_optional<transform_type>(sym_, keys::image_transform))
|
||||||
|
{
|
||||||
|
evaluate_transform(image_tr, feature_, common_.vars_, *image_transform);
|
||||||
|
}
|
||||||
|
|
||||||
|
vector_dispatch_type rasterizer_dispatch(marker_ptr,
|
||||||
|
svg_path,
|
||||||
|
r_attributes,
|
||||||
|
image_tr,
|
||||||
|
sym_,
|
||||||
|
*common_.detector_,
|
||||||
|
common_.scale_factor_,
|
||||||
|
feature_,
|
||||||
|
common_.vars_,
|
||||||
|
snap_to_pixels,
|
||||||
|
renderer_context_);
|
||||||
|
|
||||||
|
render_marker(mark, rasterizer_dispatch);
|
||||||
}
|
}
|
||||||
|
|
||||||
void operator() (marker_rgba8 const& mark) const
|
void operator() (marker_rgba8 const& mark) const
|
||||||
{
|
{
|
||||||
using namespace mapnik::svg;
|
|
||||||
bool clip = get<value_bool, keys::clip>(sym_, feature_, common_.vars_);
|
|
||||||
double offset = get<value_double, keys::offset>(sym_, feature_, common_.vars_);
|
|
||||||
double simplify_tolerance = get<value_double, keys::simplify_tolerance>(sym_, feature_, common_.vars_);
|
|
||||||
double smooth = get<value_double, keys::smooth>(sym_, feature_, common_.vars_);
|
|
||||||
|
|
||||||
agg::trans_affine geom_tr;
|
|
||||||
auto transform = get_optional<transform_type>(sym_, keys::geometry_transform);
|
|
||||||
if (transform) evaluate_transform(geom_tr, feature_, common_.vars_, *transform, common_.scale_factor_);
|
|
||||||
agg::trans_affine image_tr = agg::trans_affine_scaling(common_.scale_factor_);
|
agg::trans_affine image_tr = agg::trans_affine_scaling(common_.scale_factor_);
|
||||||
|
|
||||||
setup_transform_scaling(image_tr, mark.width(), mark.height(), feature_, common_.vars_, sym_);
|
setup_transform_scaling(image_tr, mark.width(), mark.height(), feature_, common_.vars_, sym_);
|
||||||
|
@ -210,30 +199,7 @@ struct render_marker_symbolizer_visitor
|
||||||
common_.vars_,
|
common_.vars_,
|
||||||
renderer_context_);
|
renderer_context_);
|
||||||
|
|
||||||
|
render_marker(mark, rasterizer_dispatch);
|
||||||
vertex_converter_type converter(clip_box_,
|
|
||||||
sym_,
|
|
||||||
common_.t_,
|
|
||||||
prj_trans_,
|
|
||||||
geom_tr,
|
|
||||||
feature_,
|
|
||||||
common_.vars_,
|
|
||||||
common_.scale_factor_);
|
|
||||||
|
|
||||||
if (clip)
|
|
||||||
{
|
|
||||||
geometry::geometry_types type = geometry::geometry_type(feature_.get_geometry());
|
|
||||||
if (type == geometry::geometry_types::Polygon || type == geometry::geometry_types::MultiPolygon)
|
|
||||||
converter.template set<clip_poly_tag>();
|
|
||||||
else if (type == geometry::geometry_types::LineString || type == geometry::geometry_types::MultiLineString)
|
|
||||||
converter.template set<clip_line_tag>();
|
|
||||||
}
|
|
||||||
converter.template set<transform_tag>(); //always transform
|
|
||||||
if (std::fabs(offset) > 0.0) converter.template set<offset_transform_tag>(); // parallel offset
|
|
||||||
converter.template set<affine_transform_tag>(); // optional affine transform
|
|
||||||
if (simplify_tolerance > 0.0) converter.template set<simplify_tag>(); // optional simplify converter
|
|
||||||
if (smooth > 0.0) converter.template set<smooth_tag>(); // optional smooth converter
|
|
||||||
apply_markers_multi(feature_, common_.vars_, converter, rasterizer_dispatch, sym_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -279,7 +245,7 @@ void render_markers_symbolizer(markers_symbolizer const& sym,
|
||||||
box2d<double> const& clip_box,
|
box2d<double> const& clip_box,
|
||||||
markers_renderer_context & renderer_context)
|
markers_renderer_context & renderer_context)
|
||||||
{
|
{
|
||||||
using Detector = decltype(*common.detector_);
|
using Detector = label_collision_detector4;
|
||||||
using RendererType = renderer_common;
|
using RendererType = renderer_common;
|
||||||
using ContextType = markers_renderer_context;
|
using ContextType = markers_renderer_context;
|
||||||
using VisitorType = detail::render_marker_symbolizer_visitor<Detector,
|
using VisitorType = detail::render_marker_symbolizer_visitor<Detector,
|
||||||
|
|
|
@ -60,4 +60,12 @@ transcoder::~transcoder()
|
||||||
{
|
{
|
||||||
if (conv_) ucnv_close(conv_);
|
if (conv_) ucnv_close(conv_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void to_utf8(mapnik::value_unicode_string const& input, std::string & target)
|
||||||
|
{
|
||||||
|
target.clear(); // mimic previous target.assign(...) semantics
|
||||||
|
input.toUTF8String(target); // this appends to target
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
213
src/vertex_adapters.cpp
Normal file
213
src/vertex_adapters.cpp
Normal file
|
@ -0,0 +1,213 @@
|
||||||
|
/*****************************************************************************
|
||||||
|
*
|
||||||
|
* This file is part of Mapnik (c++ mapping toolkit)
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 Artem Pavlenko
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#include <mapnik/vertex_adapters.hpp>
|
||||||
|
#include <mapnik/geometry.hpp>
|
||||||
|
#include <mapnik/geometry_types.hpp>
|
||||||
|
#include <mapnik/vertex.hpp>
|
||||||
|
|
||||||
|
namespace mapnik { namespace geometry {
|
||||||
|
|
||||||
|
// point adapter
|
||||||
|
template <typename T>
|
||||||
|
point_vertex_adapter<T>::point_vertex_adapter(point<T> const& pt)
|
||||||
|
: pt_(pt),
|
||||||
|
first_(true) {}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
unsigned point_vertex_adapter<T>::vertex(value_type * x, value_type * y) const
|
||||||
|
{
|
||||||
|
if (first_)
|
||||||
|
{
|
||||||
|
*x = pt_.x;
|
||||||
|
*y = pt_.y;
|
||||||
|
first_ = false;
|
||||||
|
return mapnik::SEG_MOVETO;
|
||||||
|
}
|
||||||
|
return mapnik::SEG_END;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void point_vertex_adapter<T>::rewind(unsigned) const
|
||||||
|
{
|
||||||
|
first_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
geometry_types point_vertex_adapter<T>::type () const
|
||||||
|
{
|
||||||
|
return geometry_types::Point;
|
||||||
|
}
|
||||||
|
|
||||||
|
// line_string adapter
|
||||||
|
template <typename T>
|
||||||
|
line_string_vertex_adapter<T>::line_string_vertex_adapter(line_string<T> const& line)
|
||||||
|
: line_(line),
|
||||||
|
current_index_(0),
|
||||||
|
end_index_(line.size())
|
||||||
|
{}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
unsigned line_string_vertex_adapter<T>::vertex(value_type * x, value_type * y) const
|
||||||
|
{
|
||||||
|
if (current_index_ != end_index_)
|
||||||
|
{
|
||||||
|
point<T> const& coord = line_[current_index_++];
|
||||||
|
*x = coord.x;
|
||||||
|
*y = coord.y;
|
||||||
|
if (current_index_ == 1)
|
||||||
|
{
|
||||||
|
return mapnik::SEG_MOVETO;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return mapnik::SEG_LINETO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mapnik::SEG_END;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void line_string_vertex_adapter<T>::rewind(unsigned) const
|
||||||
|
{
|
||||||
|
current_index_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
geometry_types line_string_vertex_adapter<T>::type() const
|
||||||
|
{
|
||||||
|
return geometry_types::LineString;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
polygon_vertex_adapter<T>::polygon_vertex_adapter(polygon<T> const& poly)
|
||||||
|
: poly_(poly),
|
||||||
|
rings_itr_(0),
|
||||||
|
rings_end_(poly_.interior_rings.size() + 1),
|
||||||
|
current_index_(0),
|
||||||
|
end_index_((rings_itr_ < rings_end_) ? poly_.exterior_ring.size() : 0),
|
||||||
|
start_loop_(true) {}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void polygon_vertex_adapter<T>::rewind(unsigned) const
|
||||||
|
{
|
||||||
|
rings_itr_ = 0;
|
||||||
|
rings_end_ = poly_.interior_rings.size() + 1;
|
||||||
|
current_index_ = 0;
|
||||||
|
end_index_ = (rings_itr_ < rings_end_) ? poly_.exterior_ring.size() : 0;
|
||||||
|
start_loop_ = true;
|
||||||
|
}
|
||||||
|
template <typename T>
|
||||||
|
unsigned polygon_vertex_adapter<T>::vertex(value_type * x, value_type * y) const
|
||||||
|
{
|
||||||
|
if (rings_itr_ == rings_end_)
|
||||||
|
{
|
||||||
|
return mapnik::SEG_END;
|
||||||
|
}
|
||||||
|
if (current_index_ < end_index_)
|
||||||
|
{
|
||||||
|
point<T> const& coord = (rings_itr_ == 0) ?
|
||||||
|
poly_.exterior_ring[current_index_++] : poly_.interior_rings[rings_itr_- 1][current_index_++];
|
||||||
|
*x = coord.x;
|
||||||
|
*y = coord.y;
|
||||||
|
if (start_loop_)
|
||||||
|
{
|
||||||
|
start_loop_= false;
|
||||||
|
return mapnik::SEG_MOVETO;
|
||||||
|
}
|
||||||
|
if (current_index_ == end_index_)
|
||||||
|
{
|
||||||
|
*x = 0;
|
||||||
|
*y = 0;
|
||||||
|
return mapnik::SEG_CLOSE;
|
||||||
|
}
|
||||||
|
return mapnik::SEG_LINETO;
|
||||||
|
}
|
||||||
|
else if (++rings_itr_ != rings_end_)
|
||||||
|
{
|
||||||
|
current_index_ = 0;
|
||||||
|
end_index_ = poly_.interior_rings[rings_itr_ - 1].size();
|
||||||
|
point<T> const& coord = poly_.interior_rings[rings_itr_ - 1][current_index_++];
|
||||||
|
*x = coord.x;
|
||||||
|
*y = coord.y;
|
||||||
|
return mapnik::SEG_MOVETO;
|
||||||
|
}
|
||||||
|
return mapnik::SEG_END;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
geometry_types polygon_vertex_adapter<T>::type () const
|
||||||
|
{
|
||||||
|
return geometry_types::Polygon;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ring adapter
|
||||||
|
template <typename T>
|
||||||
|
ring_vertex_adapter<T>::ring_vertex_adapter(linear_ring<T> const& ring)
|
||||||
|
: ring_(ring),
|
||||||
|
current_index_(0),
|
||||||
|
end_index_(ring_.size()),
|
||||||
|
start_loop_(true) {}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void ring_vertex_adapter<T>::rewind(unsigned) const
|
||||||
|
{
|
||||||
|
current_index_ = 0;
|
||||||
|
end_index_ = ring_.size();
|
||||||
|
start_loop_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
unsigned ring_vertex_adapter<T>::vertex(value_type * x, value_type * y) const
|
||||||
|
{
|
||||||
|
if (current_index_ < end_index_)
|
||||||
|
{
|
||||||
|
auto const& coord = ring_[current_index_++];
|
||||||
|
*x = coord.x;
|
||||||
|
*y = coord.y;
|
||||||
|
if (start_loop_)
|
||||||
|
{
|
||||||
|
start_loop_= false;
|
||||||
|
return mapnik::SEG_MOVETO;
|
||||||
|
}
|
||||||
|
if (current_index_ == end_index_)
|
||||||
|
{
|
||||||
|
*x = 0;
|
||||||
|
*y = 0;
|
||||||
|
return mapnik::SEG_CLOSE;
|
||||||
|
}
|
||||||
|
return mapnik::SEG_LINETO;
|
||||||
|
}
|
||||||
|
return mapnik::SEG_END;
|
||||||
|
}
|
||||||
|
template <typename T>
|
||||||
|
geometry_types ring_vertex_adapter<T>::type () const
|
||||||
|
{
|
||||||
|
return geometry_types::Polygon;
|
||||||
|
}
|
||||||
|
|
||||||
|
template struct point_vertex_adapter<double>;
|
||||||
|
template struct line_string_vertex_adapter<double>;
|
||||||
|
template struct polygon_vertex_adapter<double>;
|
||||||
|
template struct ring_vertex_adapter<double>;
|
||||||
|
|
||||||
|
}}
|
|
@ -32,6 +32,7 @@ source = Split(
|
||||||
mapnik-index.cpp
|
mapnik-index.cpp
|
||||||
process_csv_file.cpp
|
process_csv_file.cpp
|
||||||
process_geojson_file.cpp
|
process_geojson_file.cpp
|
||||||
|
../../plugins/input/csv/csv_utils.cpp
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -21,13 +21,16 @@
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
#include "process_csv_file.hpp"
|
#include "process_csv_file.hpp"
|
||||||
|
#include "../../plugins/input/csv/csv_getline.hpp"
|
||||||
#include "../../plugins/input/csv/csv_utils.hpp"
|
#include "../../plugins/input/csv/csv_utils.hpp"
|
||||||
|
#include <mapnik/datasource.hpp>
|
||||||
#include <mapnik/geometry_envelope.hpp>
|
#include <mapnik/geometry_envelope.hpp>
|
||||||
#include <mapnik/util/utf_conv_win.hpp>
|
#include <mapnik/util/utf_conv_win.hpp>
|
||||||
|
|
||||||
#if defined(MAPNIK_MEMORY_MAPPED_FILE)
|
#if defined(MAPNIK_MEMORY_MAPPED_FILE)
|
||||||
#pragma GCC diagnostic push
|
#pragma GCC diagnostic push
|
||||||
#include <mapnik/warning_ignore.hpp>
|
#include <mapnik/warning_ignore.hpp>
|
||||||
|
#include <boost/algorithm/string.hpp>
|
||||||
#include <boost/interprocess/mapped_region.hpp>
|
#include <boost/interprocess/mapped_region.hpp>
|
||||||
#include <boost/interprocess/streams/bufferstream.hpp>
|
#include <boost/interprocess/streams/bufferstream.hpp>
|
||||||
#pragma GCC diagnostic pop
|
#pragma GCC diagnostic pop
|
||||||
|
@ -35,14 +38,19 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iomanip>
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
namespace mapnik { namespace detail {
|
namespace mapnik { namespace detail {
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
std::pair<bool,box2d<double>> process_csv_file(T & boxes, std::string const& filename, std::string const& manual_headers, char separator, char quote)
|
std::pair<bool,box2d<double>> process_csv_file(T & boxes, std::string const& filename, std::string const& manual_headers, char separator, char quote)
|
||||||
{
|
{
|
||||||
mapnik::box2d<double> extent;
|
csv_utils::csv_file_parser p;
|
||||||
|
p.manual_headers_ = manual_headers;
|
||||||
|
p.separator_ = separator;
|
||||||
|
p.quote_ = quote;
|
||||||
|
|
||||||
#if defined(MAPNIK_MEMORY_MAPPED_FILE)
|
#if defined(MAPNIK_MEMORY_MAPPED_FILE)
|
||||||
using file_source_type = boost::interprocess::ibufferstream;
|
using file_source_type = boost::interprocess::ibufferstream;
|
||||||
file_source_type csv_file;
|
file_source_type csv_file;
|
||||||
|
@ -57,7 +65,7 @@ std::pair<bool,box2d<double>> process_csv_file(T & boxes, std::string const& fil
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
std::clog << "Error : cannot mmap " << filename << std::endl;
|
std::clog << "Error : cannot mmap " << filename << std::endl;
|
||||||
return std::make_pair(false, extent);
|
return std::make_pair(false, p.extent_);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
#if defined(_WINDOWS)
|
#if defined(_WINDOWS)
|
||||||
|
@ -68,182 +76,19 @@ std::pair<bool,box2d<double>> process_csv_file(T & boxes, std::string const& fil
|
||||||
if (!csv_file.is_open())
|
if (!csv_file.is_open())
|
||||||
{
|
{
|
||||||
std::clog << "Error : cannot open " << filename << std::endl;
|
std::clog << "Error : cannot open " << filename << std::endl;
|
||||||
return std::make_pair(false, extent);
|
return std::make_pair(false, p.extent_);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
auto file_length = ::detail::file_length(csv_file);
|
try
|
||||||
// set back to start
|
|
||||||
csv_file.seekg(0, std::ios::beg);
|
|
||||||
char newline;
|
|
||||||
bool has_newline;
|
|
||||||
char detected_quote;
|
|
||||||
char detected_separator;
|
|
||||||
std::tie(newline, has_newline, detected_separator, detected_quote) = ::detail::autodect_csv_flavour(csv_file, file_length);
|
|
||||||
if (quote == 0) quote = detected_quote;
|
|
||||||
if (separator == 0) separator = detected_separator;
|
|
||||||
// set back to start
|
|
||||||
csv_file.seekg(0, std::ios::beg);
|
|
||||||
std::string csv_line;
|
|
||||||
csv_utils::getline_csv(csv_file, csv_line, newline, quote);
|
|
||||||
csv_file.seekg(0, std::ios::beg);
|
|
||||||
int line_number = 0;
|
|
||||||
|
|
||||||
::detail::geometry_column_locator locator;
|
|
||||||
std::vector<std::string> headers;
|
|
||||||
std::clog << std::showbase << std::internal << std::setfill('0') ;
|
|
||||||
std::clog << "Parsing CSV using"
|
|
||||||
<< " NEWLINE=" << std::hex << std::setw(4) << int(newline) << std::dec
|
|
||||||
<< " SEPARATOR=" << separator
|
|
||||||
<< " QUOTE=" << quote << std::endl;
|
|
||||||
|
|
||||||
if (!manual_headers.empty())
|
|
||||||
{
|
{
|
||||||
std::size_t index = 0;
|
p.parse_csv(csv_file, boxes);
|
||||||
headers = csv_utils::parse_line(manual_headers, separator, quote);
|
return std::make_pair(true, p.extent_);
|
||||||
for (auto const& header : headers)
|
|
||||||
{
|
|
||||||
::detail::locate_geometry_column(header, index++, locator);
|
|
||||||
headers.push_back(header);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else // parse first line as headers
|
catch (std::exception const& ex)
|
||||||
{
|
{
|
||||||
while (csv_utils::getline_csv(csv_file,csv_line,newline, quote))
|
std::clog << ex.what() << std::endl;
|
||||||
{
|
return std::make_pair(false, p.extent_);
|
||||||
try
|
|
||||||
{
|
|
||||||
headers = csv_utils::parse_line(csv_line, separator, quote);
|
|
||||||
// skip blank lines
|
|
||||||
if (headers.size() > 0 && headers[0].empty()) ++line_number;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
std::size_t index = 0;
|
|
||||||
for (auto & header : headers)
|
|
||||||
{
|
|
||||||
mapnik::util::trim(header);
|
|
||||||
if (header.empty())
|
|
||||||
{
|
|
||||||
// create a placeholder for the empty header
|
|
||||||
std::ostringstream s;
|
|
||||||
s << "_" << index;
|
|
||||||
header = s.str();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
::detail::locate_geometry_column(header, index, locator);
|
|
||||||
}
|
|
||||||
++index;
|
|
||||||
}
|
|
||||||
++line_number;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (std::exception const& ex)
|
|
||||||
{
|
|
||||||
std::string s("CSV index: error parsing headers: ");
|
|
||||||
s += ex.what();
|
|
||||||
std::clog << s << std::endl;
|
|
||||||
return std::make_pair(false, extent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t num_headers = headers.size();
|
|
||||||
if (!::detail::valid(locator, num_headers))
|
|
||||||
{
|
|
||||||
std::clog << "CSV index: could not detect column(s) with the name(s) of wkt, geojson, x/y, or "
|
|
||||||
<< "latitude/longitude in:\n"
|
|
||||||
<< csv_line
|
|
||||||
<< "\n - this is required for reading geometry data"
|
|
||||||
<< std::endl;
|
|
||||||
return std::make_pair(false, extent);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto pos = csv_file.tellg();
|
|
||||||
|
|
||||||
// handle rare case of a single line of data and user-provided headers
|
|
||||||
// where a lack of a newline will mean that csv_utils::getline_csv returns false
|
|
||||||
bool is_first_row = false;
|
|
||||||
if (!has_newline)
|
|
||||||
{
|
|
||||||
csv_file.setstate(std::ios::failbit);
|
|
||||||
pos = 0;
|
|
||||||
if (!csv_line.empty())
|
|
||||||
{
|
|
||||||
is_first_row = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while (is_first_row || csv_utils::getline_csv(csv_file, csv_line, newline, quote))
|
|
||||||
{
|
|
||||||
++line_number;
|
|
||||||
auto record_offset = pos;
|
|
||||||
auto record_size = csv_line.length();
|
|
||||||
pos = csv_file.tellg();
|
|
||||||
is_first_row = false;
|
|
||||||
// skip blank lines
|
|
||||||
if (record_size <= 10)
|
|
||||||
{
|
|
||||||
std::string trimmed = csv_line;
|
|
||||||
boost::trim_if(trimmed, boost::algorithm::is_any_of("\",'\r\n "));
|
|
||||||
if (trimmed.empty())
|
|
||||||
{
|
|
||||||
std::clog << "CSV index: empty row encountered at line: " << line_number << std::endl;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try
|
|
||||||
{
|
|
||||||
auto const* start_line = csv_line.data();
|
|
||||||
auto const* end_line = start_line + csv_line.size();
|
|
||||||
auto values = csv_utils::parse_line(start_line, end_line, separator, quote, num_headers);
|
|
||||||
unsigned num_fields = values.size();
|
|
||||||
if (num_fields != num_headers)
|
|
||||||
{
|
|
||||||
std::ostringstream s;
|
|
||||||
s << "CSV Index: # of columns(" << num_fields << ")";
|
|
||||||
if (num_fields > num_headers)
|
|
||||||
{
|
|
||||||
s << " > ";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
s << " < ";
|
|
||||||
}
|
|
||||||
s << "# of headers(" << num_headers << ") parsed";
|
|
||||||
throw mapnik::datasource_exception(s.str());
|
|
||||||
}
|
|
||||||
|
|
||||||
auto geom = ::detail::extract_geometry(values, locator);
|
|
||||||
if (!geom.is<mapnik::geometry::geometry_empty>())
|
|
||||||
{
|
|
||||||
auto box = mapnik::geometry::envelope(geom);
|
|
||||||
if (!extent.valid()) extent = box;
|
|
||||||
else extent.expand_to_include(box);
|
|
||||||
boxes.emplace_back(std::move(box), make_pair(record_offset, record_size));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
std::ostringstream s;
|
|
||||||
s << "CSV Index: expected geometry column: could not parse row "
|
|
||||||
<< line_number << " "
|
|
||||||
<< values[locator.index] << "'";
|
|
||||||
throw mapnik::datasource_exception(s.str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (mapnik::datasource_exception const& ex )
|
|
||||||
{
|
|
||||||
std::clog << ex.what() << " at line: " << line_number << std::endl;
|
|
||||||
}
|
|
||||||
catch (std::exception const& ex)
|
|
||||||
{
|
|
||||||
std::ostringstream s;
|
|
||||||
s << "CSV Index: unexpected error parsing line: " << line_number
|
|
||||||
<< " - found " << headers.size() << " with values like: " << csv_line << "\n"
|
|
||||||
<< " and got error like: " << ex.what();
|
|
||||||
std::clog << s.str() << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return std::make_pair(true, extent);;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
using box_type = mapnik::box2d<double>;
|
using box_type = mapnik::box2d<double>;
|
||||||
|
|
|
@ -21,11 +21,6 @@
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
#include "process_geojson_file.hpp"
|
#include "process_geojson_file.hpp"
|
||||||
#include <mapnik/geometry.hpp>
|
|
||||||
#include <mapnik/geometry_envelope.hpp>
|
|
||||||
#include <mapnik/geometry_adapters.hpp>
|
|
||||||
#include <mapnik/util/file_io.hpp>
|
|
||||||
#include <mapnik/util/utf_conv_win.hpp>
|
|
||||||
|
|
||||||
#if defined(MAPNIK_MEMORY_MAPPED_FILE)
|
#if defined(MAPNIK_MEMORY_MAPPED_FILE)
|
||||||
#pragma GCC diagnostic push
|
#pragma GCC diagnostic push
|
||||||
|
@ -35,9 +30,10 @@
|
||||||
#include <boost/spirit/include/qi.hpp>
|
#include <boost/spirit/include/qi.hpp>
|
||||||
#pragma GCC diagnostic pop
|
#pragma GCC diagnostic pop
|
||||||
#include <mapnik/mapped_memory_cache.hpp>
|
#include <mapnik/mapped_memory_cache.hpp>
|
||||||
|
#else
|
||||||
|
#include <mapnik/util/file_io.hpp>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <mapnik/json/positions_grammar.hpp>
|
|
||||||
#include <mapnik/json/extract_bounding_box_grammar_impl.hpp>
|
#include <mapnik/json/extract_bounding_box_grammar_impl.hpp>
|
||||||
#include <mapnik/json/feature_collection_grammar_impl.hpp>
|
#include <mapnik/json/feature_collection_grammar_impl.hpp>
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue