Merge branch 'master' into spirit-x3

This commit is contained in:
artemp 2016-03-03 12:04:55 +01:00
commit 65e81848ba
66 changed files with 2056 additions and 1878 deletions

View file

@ -29,9 +29,9 @@ matrix:
compiler: clang
env: JOBS=8 CXX="ccache clang++-3.6 -Qunused-arguments" CC="clang-3.6" MASON_PUBLISH=true BENCH=True
addons:
apt:
sources: [ 'ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.6' ]
packages: [ 'clang-3.6', 'libstdc++-4.9-dev', 'libstdc++6' ]
apt:
sources: [ 'ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.6' ]
packages: [ 'clang-3.6', 'libstdc++-4.9-dev', 'libstdc++6' ]
# OOM killer knocking out build on render_markers_symbolizer.cpp
#
#- os: linux
@ -44,12 +44,12 @@ matrix:
- os: osx
compiler: clang
# https://docs.travis-ci.com/user/languages/objective-c/#Supported-OS-X-iOS-SDK-versions
osx_image: xcode7.2 # upgrades clang from 6 -> 7
env: JOBS=8 MASON_PUBLISH=true HEAVY_JOBS=4
osx_image: xcode7.3 # upgrades clang from 6 -> 7
env: JOBS=4 MASON_PUBLISH=true HEAVY_JOBS=2
- os: osx
compiler: clang
osx_image: xcode7.2 # upgrades clang from 6 -> 7
env: JOBS=8 COVERAGE=true HEAVY_JOBS=4
osx_image: xcode7.3 # upgrades clang from 6 -> 7
env: JOBS=4 COVERAGE=true HEAVY_JOBS=2
before_install:
- source scripts/travis-common.sh

View file

@ -1,7 +1,7 @@
#include "bench_framework.hpp"
#include <cstring>
#include <cstdlib>
#include "../plugins/input/csv/csv_utils.hpp"
#include "../plugins/input/csv/csv_getline.hpp"
class test : public benchmark::test_case
{

View file

@ -32,26 +32,12 @@
#pragma GCC diagnostic push
#include <mapnik/warning_ignore.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/fusion/include/adapt_adt.hpp>
#pragma GCC diagnostic pop
// stl
#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
{
@ -61,162 +47,6 @@ namespace phoenix = boost::phoenix;
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
{
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_small;
qi::rule<Iterator, color(), ascii_space_type> css_color;
named_colors_ named;
phoenix::function<percent_conv_impl> percent_converter;
phoenix::function<alpha_conv_impl> alpha_converter;
phoenix::function<hsl_conv_impl> hsl_converter;

View file

@ -22,11 +22,184 @@
// 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.
// mapnik
#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
{
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>
css_color_grammar<Iterator>::css_color_grammar()
: css_color_grammar::base_type(css_color)
@ -43,6 +216,8 @@ css_color_grammar<Iterator>::css_color_grammar()
ascii::no_case_type no_case;
using phoenix::at_c;
named_colors named;
css_color %= rgba_color
| rgba_percent_color
| hsl_percent_color

View file

@ -20,20 +20,16 @@
*
*****************************************************************************/
#ifndef MAPNIK_CVS_GRAMMAR_HPP
#define MAPNIK_CVS_GRAMMAR_HPP
//#define BOOST_SPIRIT_DEBUG
#ifndef MAPNIK_CSV_GRAMMAR_HPP
#define MAPNIK_CSV_GRAMMAR_HPP
#include <mapnik/csv/csv_types.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
namespace mapnik {
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>
{
@ -110,4 +106,4 @@ private:
}
#endif // MAPNIK_CVS_GRAMMAR_HPP
#endif // MAPNIK_CSV_GRAMMAR_HPP

View 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

View file

@ -24,13 +24,18 @@
#define MAPNIK_JSON_ERROR_HANDLER_HPP
#include <mapnik/config.hpp>
#pragma GCC diagnostic push
#include <mapnik/warning_ignore.hpp>
#include <boost/spirit/home/qi.hpp>
#include <boost/spirit/home/support/info.hpp>
#include <boost/spirit/home/qi/nonterminal/error_handler.hpp>
namespace boost { namespace spirit { struct info; } }
#pragma GCC diagnostic pop
// mapnik
#ifdef MAPNIK_LOG
#include <mapnik/debug.hpp>
#endif
// stl
#include <cassert>
#include <string>

View file

@ -33,12 +33,8 @@
#include <mapnik/warning_ignore.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_function.hpp>
#include <boost/fusion/adapted/std_tuple.hpp>
#pragma GCC diagnostic pop
// stl
#include <tuple>
namespace mapnik { namespace json {
using position_type = mapnik::geometry::point<double>;

View file

@ -22,7 +22,7 @@
// mapnik
#include <mapnik/json/extract_bounding_box_grammar.hpp>
#include <mapnik/geometry_fusion_adapted.hpp>
// boost
#include <boost/spirit/include/qi_omit.hpp>
#include <boost/spirit/include/phoenix_object.hpp>

View file

@ -27,7 +27,6 @@
#include <mapnik/global.hpp>
#include <mapnik/geometry.hpp>
#include <mapnik/geometry_type.hpp>
#include <mapnik/geometry_fusion_adapted.hpp>
// boost
#pragma GCC diagnostic push
#include <mapnik/warning_ignore.hpp>

View file

@ -24,6 +24,7 @@
#include <mapnik/json/geometry_generator_grammar.hpp>
#include <mapnik/util/spirit_transform_attribute.hpp>
#include <mapnik/geometry_types.hpp>
#include <mapnik/geometry_fusion_adapted.hpp>
// boost
#pragma GCC diagnostic push

View file

@ -30,9 +30,11 @@
#include <mapnik/json/positions_grammar.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/phoenix_function.hpp>
#pragma GCC diagnostic pop
#include <memory>

View file

@ -24,12 +24,10 @@
#include <mapnik/config.hpp>
#include <mapnik/json/error_handler.hpp>
#include <mapnik/json/geometry_grammar.hpp>
#include <mapnik/json/positions_grammar_impl.hpp>
#include <mapnik/geometry_fusion_adapted.hpp>
// boost
#include <boost/spirit/include/phoenix_object.hpp>
#include <boost/spirit/include/phoenix_stl.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_function.hpp>
namespace mapnik { namespace json {

View file

@ -24,25 +24,13 @@
#define MAPNIK_JSON_GEOMETRY_PARSER_HPP
// mapnik
#include <mapnik/geometry.hpp>
#include <mapnik/json/geometry_grammar.hpp>
// boost
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <string>
namespace mapnik { namespace json {
inline 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);
}
bool from_geojson(std::string const& json, mapnik::geometry::geometry<double> & geom);
}}

View file

@ -25,6 +25,7 @@
#include <mapnik/geometry.hpp>
#include <mapnik/geometry_correct.hpp>
#include <mapnik/json/positions.hpp>
namespace mapnik { namespace json {

View 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

View file

@ -25,29 +25,19 @@
// mapnik
#include <mapnik/util/variant.hpp>
#include <mapnik/json/positions.hpp>
#include <mapnik/json/generic_json.hpp>
#include <mapnik/json/error_handler.hpp>
#include <mapnik/geometry.hpp>
#include <mapnik/geometry_fusion_adapted.hpp>
#pragma GCC diagnostic push
#include <mapnik/warning_ignore.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_function.hpp>
#include <boost/fusion/adapted/std_tuple.hpp>
#pragma GCC diagnostic pop
// stl
#include <tuple>
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;
struct set_position_impl

View file

@ -22,7 +22,7 @@
// mapnik
#include <mapnik/json/positions_grammar.hpp>
#include <mapnik/geometry_fusion_adapted.hpp>
// boost
#include <boost/spirit/include/qi_omit.hpp>
#include <boost/spirit/include/phoenix_object.hpp>

View file

@ -25,14 +25,12 @@
// mapnik
#include <mapnik/json/error_handler.hpp>
#include <mapnik/json/generic_json.hpp>
#include <mapnik/json/topology.hpp>
#include <mapnik/json/value_converters.hpp>
#pragma GCC diagnostic push
#include <mapnik/warning_ignore.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_function.hpp>
#pragma GCC diagnostic pop
// stl
@ -50,8 +48,6 @@ struct topojson_grammar : qi::grammar<Iterator, space_type, topology()>
{
topojson_grammar();
private:
// generic JSON support
json::generic_json<Iterator> json_;
// topoJSON
qi::rule<Iterator, space_type, mapnik::topojson::topology()> topology;
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::multi_polygon()> multi_polygon;
qi::rule<Iterator, space_type, void(std::vector<mapnik::topojson::geometry>&)> geometry_collection;
qi::rule<Iterator, space_type, std::vector<index_type>()> ring;
// properties
qi::rule<Iterator, space_type, mapnik::topojson::properties()> properties;
qi::rule<Iterator, space_type, mapnik::topojson::properties()> attributes;
qi::rule<Iterator, space_type, mapnik::json::json_value()> attribute_value;
// id
qi::rule<Iterator,space_type> id;
// error handler
boost::phoenix::function<ErrorHandler> const error_handler;
};
}}

View file

@ -21,6 +21,84 @@
*****************************************************************************/
#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 {
@ -46,26 +124,32 @@ topojson_grammar<Iterator, ErrorHandler>::topojson_grammar()
using qi::on_error;
using phoenix::push_back;
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_.value >> *(lit(',') >> json_.value)
json.array = lit('[')
>> json.value >> *(lit(',') >> json.value)
>> lit(']')
;
json_.number = json_.strict_double[_val = json_.double_converter(_1)]
| json_.int__[_val = json_.integer_converter(_1)]
json.number = json.strict_double[_val = json.double_converter(_1)]
| json.int__[_val = json.integer_converter(_1)]
| lit("true")[_val = true]
| lit("false")[_val = false]
| lit("null")[_val = construct<value_null>()]
@ -96,7 +180,7 @@ topojson_grammar<Iterator, ErrorHandler>::topojson_grammar()
objects = lit("\"objects\"")
>> lit(':')
>> lit('{')
>> -((omit[json_.string_]
>> -((omit[json.string_]
>> lit(':')
>> (geometry_collection(_val) | geometry)) % lit(','))
>> lit('}')
@ -109,7 +193,7 @@ topojson_grammar<Iterator, ErrorHandler>::topojson_grammar()
multi_point |
multi_linestring |
multi_polygon |
omit[json_.object]
omit[json.object]
;
geometry_collection = lit('{')
@ -171,7 +255,7 @@ topojson_grammar<Iterator, ErrorHandler>::topojson_grammar()
>> lit('}')
;
id = lit("\"id\"") >> lit(':') >> omit[json_.value]
id = lit("\"id\"") >> lit(':') >> omit[json.value]
;
ring = lit('[') >> -(int_ % lit(',')) >> lit(']')
@ -179,13 +263,13 @@ topojson_grammar<Iterator, ErrorHandler>::topojson_grammar()
properties = lit("\"properties\"")
>> 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(':')
>> lit('[') >> -( arc % lit(',')) >> lit(']') ;
@ -199,7 +283,7 @@ topojson_grammar<Iterator, ErrorHandler>::topojson_grammar()
objects.name("objects");
arc.name("arc");
arcs.name("arcs");
json_.value.name("value");
json.value.name("value");
coordinate.name("coordinate");
point.name("point");

View file

@ -28,8 +28,6 @@
#pragma GCC diagnostic push
#include <mapnik/warning_ignore.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/adapted/std_tuple.hpp>
#include <boost/optional.hpp>
#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

View file

@ -36,7 +36,7 @@
#include <mapnik/vertex_processor.hpp>
#include <mapnik/renderer_common/apply_vertex_converter.hpp>
#include <mapnik/renderer_common/render_markers_symbolizer.hpp>
#include <mapnik/vertex_converters.hpp>
// agg
#include "agg_trans_affine.h"
@ -158,140 +158,29 @@ void setup_transform_scaling(agg::trans_affine & tr,
attributes const& vars,
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
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)
{
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);
}
}
}
using vector_dispatch_type = vector_markers_dispatch<mapnik::label_collision_detector4>;
using raster_dispatch_type = raster_markers_dispatch<mapnik::label_collision_detector4>;
template <typename Converter, typename Processor>
void apply_markers_multi(feature_impl const& feature, attributes const& vars, Converter & converter, Processor & 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);
extern 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);
if (type == geometry::geometry_types::Point
||
type == geometry::geometry_types::LineString
||
type == geometry::geometry_types::Polygon)
{
apply_markers_single(converter, proc, geom);
}
else
{
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);
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);
}
}
}
}
}

View file

@ -29,7 +29,6 @@
#include <mapnik/markers_placements/vertext_first.hpp>
#include <mapnik/markers_placements/vertext_last.hpp>
#include <mapnik/symbolizer_enumerations.hpp>
#include <mapnik/util/variant.hpp>
namespace mapnik
{
@ -38,70 +37,71 @@ template <typename Locator, typename Detector>
class markers_placement_finder : util::noncopyable
{
public:
using markers_placement = util::variant<markers_point_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_;
};
using basic_placement = markers_basic_placement<Locator, Detector>;
markers_placement_finder(marker_placement_e placement_type,
Locator &locator,
Detector &detector,
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.
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:
// Factory function for particular placement implementations.
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);
}
}
basic_placement* active_placement_;
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);
}
};
}

View 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

View file

@ -33,14 +33,8 @@ template <typename Locator, typename Detector>
class markers_interior_placement : public markers_point_placement<Locator, Detector>
{
public:
markers_interior_placement(Locator &locator, Detector &detector, markers_placement_params const& params)
: markers_point_placement<Locator, Detector>(locator, detector, params)
{
}
markers_interior_placement(markers_interior_placement && rhs)
: markers_point_placement<Locator, Detector>(std::move(rhs))
{}
using point_placement = markers_point_placement<Locator, Detector>;
using point_placement::point_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)
{
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)
@ -73,20 +67,10 @@ public:
angle = 0;
box2d<double> box = this->perform_transform(angle, x, y);
if (this->params_.avoid_edges && !this->detector_.extent().contains(box))
if (!this->push_to_detector(x, y, angle, ignore_placement))
{
return false;
}
if (!this->params_.allow_overlap && !this->detector_.has_placement(box))
{
return false;
}
if (!ignore_placement)
{
this->detector_.insert(box);
}
this->done_ = true;
return true;

View file

@ -35,29 +35,23 @@ template <typename Locator, typename Detector>
class markers_line_placement : public markers_point_placement<Locator, Detector>
{
public:
markers_line_placement(Locator &locator, Detector &detector, markers_placement_params const& params)
: markers_point_placement<Locator, Detector>(locator, detector, params),
using point_placement = markers_point_placement<Locator, Detector>;
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),
spacing_(0.0),
marker_width_((params.size * params.tr).width()),
path_(locator)
{
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()
{
this->locator_.rewind(0);
this->done_ = false;
point_placement::rewind();
first_point_ = true;
}
@ -70,7 +64,7 @@ public:
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_;
@ -102,16 +96,10 @@ public:
{
continue;
}
box2d<double> box = this->perform_transform(angle, x, y);
if ((this->params_.avoid_edges && !this->detector_.extent().contains(box))
|| (!this->params_.allow_overlap && !this->detector_.has_placement(box)))
if (!this->push_to_detector(x, y, angle, ignore_placement))
{
continue;
}
if (!ignore_placement)
{
this->detector_.insert(box);
}
return true;
}
}

View file

@ -23,148 +23,54 @@
#ifndef MAPNIK_MARKERS_PLACEMENTS_POINT_HPP
#define MAPNIK_MARKERS_PLACEMENTS_POINT_HPP
#include <mapnik/box2d.hpp>
#include <mapnik/geom_util.hpp>
#include <mapnik/geometry_types.hpp>
#include <mapnik/util/math.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>
#include <mapnik/markers_placements/basic.hpp>
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_point_placement : util::noncopyable
class markers_point_placement : public markers_basic_placement<Locator, Detector>
{
public:
markers_point_placement(Locator &locator, Detector &detector, markers_placement_params const& params)
: locator_(locator),
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;
}
using basic_placement = markers_basic_placement<Locator, Detector>;
using basic_placement::basic_placement;
// 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)
{
if (done_)
if (this->done_)
{
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;
}
}
else
{
if (!label::centroid(locator_, x, y))
if (!label::centroid(this->locator_, x, y))
{
done_ = true;
this->done_ = true;
return false;
}
}
angle = 0;
box2d<double> box = perform_transform(angle, x, y);
if (params_.avoid_edges && !detector_.extent().contains(box))
{
return false;
}
if (!params_.allow_overlap && !detector_.has_placement(box))
if (!this->push_to_detector(x, y, angle, ignore_placement))
{
return false;
}
if (!ignore_placement)
{
detector_.insert(box);
}
done_ = true;
this->done_ = 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;
}
}
};
}

View file

@ -31,14 +31,8 @@ template <typename Locator, typename Detector>
class markers_vertex_first_placement : public markers_point_placement<Locator, Detector>
{
public:
markers_vertex_first_placement(Locator &locator, Detector &detector, markers_placement_params const& params)
: markers_point_placement<Locator, Detector>(locator, detector, params)
{
}
markers_vertex_first_placement(markers_vertex_first_placement && rhs)
: markers_point_placement<Locator, Detector>(std::move(rhs))
{}
using point_placement = markers_point_placement<Locator, Detector>;
using point_placement::point_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)
{
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;
@ -75,20 +69,10 @@ public:
}
}
box2d<double> box = this->perform_transform(angle, x, y);
if (this->params_.avoid_edges && !this->detector_.extent().contains(box))
if (!this->push_to_detector(x, y, angle, ignore_placement))
{
return false;
}
if (!this->params_.allow_overlap && !this->detector_.has_placement(box))
{
return false;
}
if (!ignore_placement)
{
this->detector_.insert(box);
}
this->done_ = true;
return true;

View file

@ -31,13 +31,8 @@ template <typename Locator, typename Detector>
class markers_vertex_last_placement : public markers_point_placement<Locator, Detector>
{
public:
markers_vertex_last_placement(Locator &locator, Detector &detector, markers_placement_params const& params)
: markers_point_placement<Locator, Detector>(locator, detector, params)
{}
markers_vertex_last_placement(markers_vertex_last_placement && rhs)
: markers_point_placement<Locator, Detector>(std::move(rhs))
{}
using point_placement = markers_point_placement<Locator, Detector>;
using point_placement::point_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->params_.avoid_edges && !this->detector_.extent().contains(box))
if (!this->push_to_detector(x, y, angle, ignore_placement))
{
return false;
}
if (!this->params_.allow_overlap && !this->detector_.has_placement(box))
{
return false;
}
if (!ignore_placement)
{
this->detector_.insert(box);
}
this->done_ = true;
return true;

View file

@ -40,53 +40,8 @@ namespace mapnik { namespace svg {
#include <mapnik/warning_ignore.hpp>
#include <boost/spirit/include/karma_nonterminal.hpp>
#include <boost/spirit/include/karma_rule.hpp>
#include <boost/fusion/adapted/struct.hpp>
#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 {
using namespace boost::spirit;

View file

@ -30,8 +30,53 @@
#include <mapnik/warning_ignore.hpp>
#include <boost/spirit/include/karma.hpp>
#include <boost/fusion/include/std_pair.hpp>
#include <boost/fusion/adapted/struct.hpp>
#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 {
using namespace boost::spirit;

View file

@ -45,6 +45,10 @@ public:
private:
UConverter * conv_;
};
// convinience method
void MAPNIK_DECL to_utf8(mapnik::value_unicode_string const& input, std::string & target);
}
#endif // MAPNIK_UNICODE_HPP

View file

@ -24,18 +24,13 @@
#define MAPNIK_GEOMETRY_TO_GEOJSON_HPP
// mapnik
#include <mapnik/geometry.hpp>
#include <mapnik/json/geometry_generator_grammar.hpp>
#include <string>
namespace mapnik { namespace util {
inline 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);
}
bool to_geojson(std::string & json, mapnik::geometry::geometry<double> const& geom);
}}

View file

@ -38,11 +38,11 @@ namespace value_adl_barrier {
class MAPNIK_DECL value : public value_base
{
friend value operator+(value const&,value const&);
friend value operator-(value const&,value const&);
friend value operator*(value const&,value const&);
friend value operator/(value const&,value const&);
friend value operator%(value const&,value const&);
friend MAPNIK_DECL value operator+(value const&,value const&);
friend MAPNIK_DECL value operator-(value const&,value const&);
friend MAPNIK_DECL value operator*(value const&,value const&);
friend MAPNIK_DECL value operator/(value const&,value const&);
friend MAPNIK_DECL value operator%(value const&,value const&);
public:
value() = default;

View file

@ -34,32 +34,10 @@ struct point_vertex_adapter
{
using value_type = typename point<T>::value_type;
point_vertex_adapter(point<T> const& pt)
: pt_(pt),
first_(true) {}
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_vertex_adapter(point<T> const& pt);
unsigned vertex(value_type * x, value_type * y) const;
void rewind(unsigned) const;
geometry_types type () const;
point<T> const& pt_;
mutable bool first_;
};
@ -68,110 +46,23 @@ template <typename T>
struct line_string_vertex_adapter
{
using value_type = typename point<T>::value_type;
line_string_vertex_adapter(line_string<T> const& line)
: line_(line),
current_index_(0),
end_index_(line.size())
{}
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_vertex_adapter(line_string<T> const& line);
unsigned vertex(value_type * x, value_type * y) const;
void rewind(unsigned) const;
geometry_types type () const;
line_string<T> const& line_;
mutable std::size_t current_index_;
const std::size_t end_index_;
const std::size_t end_index_;
};
template <typename T>
struct polygon_vertex_adapter
{
using value_type = typename point<T>::value_type;
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) {}
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;
}
polygon_vertex_adapter(polygon<T> const& poly);
void rewind(unsigned) const;
unsigned vertex(value_type * x, value_type * y) const;
geometry_types type () const;
private:
polygon<T> const& poly_;
mutable std::size_t rings_itr_;
@ -185,47 +76,10 @@ template <typename T>
struct ring_vertex_adapter
{
using value_type = typename point<T>::value_type;
ring_vertex_adapter(linear_ring<T> const& ring)
: ring_(ring),
current_index_(0),
end_index_(ring_.size()),
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;
}
ring_vertex_adapter(linear_ring<T> const& ring);
void rewind(unsigned) const;
unsigned vertex(value_type * x, value_type * y) const;
geometry_types type () const;
private:
linear_ring<T> const& ring_;
mutable std::size_t current_index_;
@ -233,6 +87,11 @@ private:
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>
struct vertex_adapter_traits {};

View file

@ -27,7 +27,6 @@
#include <mapnik/global.hpp>
#include <mapnik/geometry.hpp>
#include <mapnik/geometry_type.hpp>
#include <mapnik/geometry_fusion_adapted.hpp>
#pragma GCC diagnostic push
#include <mapnik/warning_ignore.hpp>

View file

@ -20,9 +20,10 @@
*
*****************************************************************************/
#include <mapnik/geometry.hpp>
// mapnik
#include <mapnik/wkt/wkt_generator_grammar.hpp>
#include <mapnik/util/spirit_transform_attribute.hpp>
#include <mapnik/geometry_fusion_adapted.hpp>
// boost
#include <boost/spirit/include/phoenix.hpp>

View file

@ -24,9 +24,8 @@
#define MAPNIK_WKT_GRAMMAR_HPP
// mapnik
#include <mapnik/config.hpp>
#include <mapnik/geometry.hpp>
#include <mapnik/geometry_fusion_adapted.hpp>
#pragma GCC diagnostic push
#include <mapnik/warning_ignore.hpp>
#include <boost/assert.hpp>

View file

@ -20,8 +20,8 @@
*
*****************************************************************************/
#include <mapnik/config.hpp>
#include <mapnik/wkt/wkt_grammar.hpp>
#include <mapnik/geometry_fusion_adapted.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_fusion.hpp>

View file

@ -39,6 +39,7 @@ else:
plugin_sources = Split(
"""
%(PLUGIN_NAME)s_utils.cpp
%(PLUGIN_NAME)s_datasource.cpp
%(PLUGIN_NAME)s_featureset.cpp
%(PLUGIN_NAME)s_inline_featureset.cpp

View file

@ -21,6 +21,7 @@
*****************************************************************************/
#include "csv_utils.hpp"
#include "csv_getline.hpp"
#include "csv_datasource.hpp"
#include "csv_featureset.hpp"
#include "csv_inline_featureset.hpp"
@ -36,9 +37,12 @@
#include <mapnik/memory_featureset.hpp>
#include <mapnik/boolean.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/value_types.hpp>
#include <mapnik/util/fs.hpp>
#include <mapnik/make_unique.hpp>
#include <mapnik/util/spatial_index.hpp>
#include <mapnik/geom_util.hpp>
#if defined(MAPNIK_MEMORY_MAPPED_FILE)
@ -65,21 +69,13 @@ DATASOURCE_PLUGIN(csv_datasource)
csv_datasource::csv_datasource(parameters const& params)
: datasource(params),
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>()),
extent_initialized_(false),
tree_(nullptr),
locator_(),
has_disk_index_(false)
tree_(nullptr)
{
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");
if (quote_param)
{
@ -170,297 +166,89 @@ csv_datasource::csv_datasource(parameters const& params)
csv_datasource::~csv_datasource() {}
template <typename T>
void csv_datasource::parse_csv(T & stream)
void csv_datasource::parse_csv(std::istream & csv_file)
{
auto file_length = detail::file_length(stream);
// set back to start
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::vector<item_type> boxes;
csv_utils::csv_file_parser::parse_csv(csv_file, boxes);
std::for_each(headers_.begin(), headers_.end(),
[ & ](std::string const& header){ ctx_->push(header); });
mapnik::transcoder tr(desc_.get_encoding());
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)
if (!has_disk_index_)
{
stream.setstate(std::ios::failbit);
pos = 0;
if (!csv_line.empty())
// bulk insert initialise r-tree
tree_ = std::make_unique<spatial_index_type>(boxes);
}
}
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()
@ -483,8 +271,8 @@ mapnik::layer_descriptor csv_datasource::get_descriptor() const
return desc_;
}
template <typename T>
boost::optional<mapnik::datasource_geometry_t> csv_datasource::get_geometry_type_impl(T & stream) const
boost::optional<mapnik::datasource_geometry_t>
csv_datasource::get_geometry_type_impl(std::istream & stream) const
{
boost::optional<mapnik::datasource_geometry_t> result;
if (tree_)
@ -505,7 +293,7 @@ boost::optional<mapnik::datasource_geometry_t> csv_datasource::get_geometry_type
try
{
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);
if (result)
{
@ -548,7 +336,7 @@ boost::optional<mapnik::datasource_geometry_t> csv_datasource::get_geometry_type
try
{
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);
if (result)
{

View file

@ -32,6 +32,7 @@
#include <mapnik/coord.hpp>
#include <mapnik/feature_layer_desc.hpp>
#include <mapnik/value_types.hpp>
#include "csv_utils.hpp"
#pragma GCC diagnostic push
#include <mapnik/warning_ignore.hpp>
@ -41,8 +42,8 @@
#pragma GCC diagnostic pop
// stl
#include <iosfwd>
#include <vector>
#include <deque>
#include <string>
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:
using box_type = mapnik::box2d<double>;
@ -84,26 +86,15 @@ public:
mapnik::layer_descriptor get_descriptor() const;
boost::optional<mapnik::datasource_geometry_t> get_geometry_type() const;
private:
template <typename T>
void parse_csv(T & stream);
template <typename T>
boost::optional<mapnik::datasource_geometry_t> get_geometry_type_impl(T & stream) const;
void parse_csv(std::istream & );
virtual void add_feature(mapnik::value_integer index, mapnik::csv_line const & values);
boost::optional<mapnik::datasource_geometry_t> get_geometry_type_impl(std::istream & ) const;
mapnik::layer_descriptor desc_;
mapnik::box2d<double> extent_;
std::string filename_;
mapnik::value_integer row_limit_;
std::string inline_string_;
char separator_;
char quote_;
std::vector<std::string> headers_;
std::string manual_headers_;
bool strict_;
mapnik::context_ptr ctx_;
bool extent_initialized_;
std::unique_ptr<spatial_index_type> tree_;
detail::geometry_column_locator locator_;
bool has_disk_index_;
};
#endif // MAPNIK_CSV_DATASOURCE_HPP

View file

@ -31,7 +31,7 @@
#include <vector>
#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)
:
#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)
{
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>())
{
mapnik::feature_ptr feature(mapnik::feature_factory::create(ctx_, ++feature_id_));
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 mapnik::feature_ptr();

View file

@ -28,7 +28,6 @@
#include "csv_utils.hpp"
#include "csv_datasource.hpp"
#include <deque>
#include <cstdio>
#if defined(MAPNIK_MEMORY_MAPPED_FILE)
#pragma GCC diagnostic push
@ -41,7 +40,7 @@
class csv_featureset : public mapnik::Featureset
{
using locator_type = detail::geometry_column_locator;
using locator_type = csv_utils::geometry_column_locator;
public:
using array_type = std::deque<csv_datasource::item_type>;
csv_featureset(std::string const& filename,
@ -70,7 +69,7 @@ private:
array_type::const_iterator index_end_;
mapnik::context_ptr ctx_;
mapnik::value_integer feature_id_ = 0;
detail::geometry_column_locator const& locator_;
locator_type const& locator_;
mapnik::transcoder tr_;
};

View 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

View file

@ -37,7 +37,7 @@
csv_index_featureset::csv_index_featureset(std::string const& filename,
mapnik::filter_in_box const& filter,
detail::geometry_column_locator const& locator,
locator_type const& locator,
char separator,
char quote,
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)
{
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>())
{
mapnik::feature_ptr feature(mapnik::feature_factory::create(ctx_, ++feature_id_));
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 mapnik::feature_ptr();

View file

@ -41,7 +41,7 @@
class csv_index_featureset : public mapnik::Featureset
{
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:
csv_index_featureset(std::string const& filename,
@ -60,7 +60,7 @@ private:
std::vector<std::string> headers_;
mapnik::context_ptr ctx_;
mapnik::value_integer feature_id_ = 0;
detail::geometry_column_locator const& locator_;
locator_type const& locator_;
mapnik::transcoder tr_;
#if defined (MAPNIK_MEMORY_MAPPED_FILE)
using file_source_type = boost::interprocess::ibufferstream;

View file

@ -33,7 +33,7 @@
#include <deque>
csv_inline_featureset::csv_inline_featureset(std::string const& inline_string,
detail::geometry_column_locator const& locator,
locator_type const& locator,
char separator,
char quote,
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* end = start + str.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>())
{
mapnik::feature_ptr feature(mapnik::feature_factory::create(ctx_, ++feature_id_));
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 mapnik::feature_ptr();

View file

@ -28,11 +28,10 @@
#include "csv_utils.hpp"
#include "csv_datasource.hpp"
#include <deque>
#include <cstdio>
class csv_inline_featureset : public mapnik::Featureset
{
using locator_type = detail::geometry_column_locator;
using locator_type = csv_utils::geometry_column_locator;
public:
using array_type = std::deque<csv_datasource::item_type>;
csv_inline_featureset(std::string const& inline_string,
@ -55,7 +54,7 @@ private:
array_type::const_iterator index_end_;
mapnik::context_ptr ctx_;
mapnik::value_integer feature_id_ = 0;
detail::geometry_column_locator const& locator_;
locator_type const& locator_;
mapnik::transcoder tr_;
};

View 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

View file

@ -24,220 +24,26 @@
#define MAPNIK_CSV_UTILS_DATASOURCE_HPP
// mapnik
#include <mapnik/debug.hpp>
#include <mapnik/box2d.hpp>
#include <mapnik/geometry.hpp>
#include <mapnik/geometry_correct.hpp>
#include <mapnik/wkt/wkt_factory.hpp>
#include <mapnik/json/geometry_parser.hpp>
#include <mapnik/value_types.hpp>
#include <mapnik/util/conversions.hpp>
#include <mapnik/csv/csv_grammar.hpp>
#include <mapnik/util/trim.hpp>
#include <mapnik/datasource.hpp>
#pragma GCC diagnostic push
#include <mapnik/warning_ignore.hpp>
#include <boost/algorithm/string.hpp>
#pragma GCC diagnostic pop
#include <mapnik/csv/csv_types.hpp>
// std
#include <iosfwd>
#include <string>
#include <cstdio>
#include <algorithm>
#include <vector>
namespace csv_utils
{
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 parse_line(std::string const& line_str, char separator, char quote);
template <typename Iterator>
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;
}
bool is_likely_number(std::string const& value);
static inline 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);
}
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);
}
bool ignore_case_equal(std::string const& s0, std::string const& s1);
struct geometry_column_locator
{
@ -249,87 +55,18 @@ struct geometry_column_locator
std::size_t index2;
};
static inline 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;
}
namespace detail {
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;
}
}
std::size_t file_length(std::istream & stream);
static inline 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;
}
std::tuple<char, bool, char, char> autodect_csv_flavour(std::istream & stream, std::size_t file_length);
static inline 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)
{
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);
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 detail
mapnik::geometry::geometry<double> extract_geometry(std::vector<std::string> const& row, geometry_column_locator const& locator);
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)
@ -348,8 +85,8 @@ void process_properties(Feature & feature, Headers const& headers, Values const&
std::string value = mapnik::util::trim_copy(*val_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;
if (locator.index == i && (locator.type == geometry_column_locator::WKT
|| locator.type == geometry_column_locator::GEOJSON) ) continue;
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

View file

@ -45,21 +45,27 @@ using mapnik::layer_descriptor;
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)
: datasource(params),
dataset_(nullptr),
dataset_(nullptr, &GDALClose),
desc_(gdal_datasource::name(), "utf-8"),
nodata_value_(params.get<double>("nodata")),
nodata_tolerance_(*params.get<double>("nodata_tolerance",1e-12))
{
MAPNIK_LOG_DEBUG(gdal) << "gdal_datasource: Initializing...";
GDALAllRegister_once_();
#ifdef MAPNIK_STATS
mapnik::progress_timer __stats__(std::clog, "gdal_datasource::init");
#endif
GDALAllRegister();
boost::optional<std::string> file = params.get<std::string>("file");
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 (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
#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_)
@ -92,7 +100,7 @@ gdal_datasource::gdal_datasource(parameters const& params)
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();
width_ = dataset_->GetRasterXSize();
@ -182,8 +190,7 @@ gdal_datasource::gdal_datasource(parameters const& params)
gdal_datasource::~gdal_datasource()
{
MAPNIK_LOG_DEBUG(gdal) << "gdal_featureset: Closing Dataset=" << dataset_;
GDALClose(dataset_);
MAPNIK_LOG_DEBUG(gdal) << "gdal_featureset: Closing Dataset=" << dataset_.get();
}
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");
#endif
gdal_query gq = q;
// TODO - move to std::make_shared, but must reduce # of args to <= 9
return featureset_ptr(new gdal_featureset(*dataset_,
return std::make_shared<gdal_featureset>(*dataset_,
band_,
gq,
gdal_query(q),
extent_,
width_,
height_,
@ -230,7 +234,7 @@ featureset_ptr gdal_datasource::features(query const& q) const
dx_,
dy_,
nodata_value_,
nodata_tolerance_));
nodata_tolerance_);
}
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");
#endif
gdal_query gq = pt;
// TODO - move to std::make_shared, but must reduce # of args to <= 9
return featureset_ptr(new gdal_featureset(*dataset_,
return std::make_shared<gdal_featureset>(*dataset_,
band_,
gq,
gdal_query(pt),
extent_,
width_,
height_,
@ -252,5 +253,5 @@ featureset_ptr gdal_datasource::features_at_point(coord2d const& pt, double tol)
dx_,
dy_,
nodata_value_,
nodata_tolerance_));
nodata_tolerance_);
}

View file

@ -55,8 +55,7 @@ public:
boost::optional<mapnik::datasource_geometry_t> get_geometry_type() const;
mapnik::layer_descriptor get_descriptor() const;
private:
GDALDataset* open_dataset() const;
GDALDataset* dataset_;
std::unique_ptr<GDALDataset, decltype(&GDALClose)> dataset_;
mapnik::box2d<double> extent_;
std::string dataset_name_;
int band_;

View file

@ -21,7 +21,7 @@
*****************************************************************************/
// mapnik
#include <mapnik/make_unique.hpp>
#include <mapnik/datasource.hpp>
#include <mapnik/global.hpp>
#include <mapnik/debug.hpp>
#include <mapnik/image.hpp>
@ -29,7 +29,6 @@
#include <mapnik/view_transform.hpp>
#include <mapnik/feature.hpp>
#include <mapnik/feature_factory.hpp>
#include <mapnik/util/variant.hpp>
// stl
#include <cmath>
@ -39,8 +38,6 @@
#include "gdal_featureset.hpp"
#include <gdal_priv.h>
using mapnik::query;
using mapnik::coord2d;
using mapnik::box2d;
using mapnik::feature_ptr;
using mapnik::view_transform;
@ -77,8 +74,6 @@ gdal_featureset::gdal_featureset(GDALDataset& dataset,
gdal_featureset::~gdal_featureset()
{
MAPNIK_LOG_DEBUG(gdal) << "gdal_featureset: Closing Dataset=" << &dataset_;
}
feature_ptr gdal_featureset::next()

View file

@ -24,13 +24,12 @@
#define GDAL_FEATURESET_HPP
// mapnik
#include <mapnik/feature.hpp>
#include <mapnik/featureset.hpp>
#include <mapnik/query.hpp>
#include <mapnik/util/variant.hpp>
// boost
#include <boost/optional.hpp>
#include "gdal_datasource.hpp"
class GDALDataset;
class GDALRasterBand;

View file

@ -222,6 +222,7 @@ source = Split(
warp.cpp
css_color_grammar_x3.cpp
vertex_cache.cpp
vertex_adapters.cpp
text/font_library.cpp
text/text_layout.cpp
text/text_line.cpp

View 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);
}
}}

View 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);
}
}}

View 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>;

View file

@ -23,7 +23,7 @@
// mapnik
#include <mapnik/marker_helpers.hpp>
#include <mapnik/svg/svg_converter.hpp>
#include <mapnik/label_collision_detector.hpp>
#include "agg_ellipse.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

View file

@ -20,9 +20,9 @@
*
*****************************************************************************/
#include <mapnik/label_collision_detector.hpp>
#include <mapnik/svg/svg_storage.hpp>
#include <mapnik/svg/svg_path_adapter.hpp>
#include <mapnik/vertex_converters.hpp>
#include <mapnik/marker_cache.hpp>
#include <mapnik/marker_helpers.hpp>
#include <mapnik/geometry_type.hpp>
@ -39,14 +39,6 @@ struct render_marker_symbolizer_visitor
using vector_dispatch_type = vector_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,
markers_symbolizer const& sym,
mapnik::feature_impl & feature,
@ -62,134 +54,131 @@ struct render_marker_symbolizer_visitor
clip_box_(clip_box),
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_svg 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_);
// https://github.com/mapnik/mapnik/issues/1316
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_);
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
// to allow for full control over rx/ry dimensions
if (filename_ == "shape://ellipse"
&& (has_key(sym_,keys::width) || has_key(sym_,keys::height)))
{
svg_path_ptr marker_ellipse = std::make_shared<svg_storage_type>();
vertex_stl_adapter<svg_path_storage> stl_storage(marker_ellipse->source());
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_);
marker_ptr = std::make_shared<svg_storage_type>();
is_ellipse = true;
}
else
{
box2d<double> const& bbox = mark.bounding_box();
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
{
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_);
setup_transform_scaling(image_tr, mark.width(), mark.height(), feature_, common_.vars_, sym_);
@ -210,30 +199,7 @@ struct render_marker_symbolizer_visitor
common_.vars_,
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_);
render_marker(mark, rasterizer_dispatch);
}
private:
@ -279,7 +245,7 @@ void render_markers_symbolizer(markers_symbolizer const& sym,
box2d<double> const& clip_box,
markers_renderer_context & renderer_context)
{
using Detector = decltype(*common.detector_);
using Detector = label_collision_detector4;
using RendererType = renderer_common;
using ContextType = markers_renderer_context;
using VisitorType = detail::render_marker_symbolizer_visitor<Detector,

View file

@ -60,4 +60,12 @@ transcoder::~transcoder()
{
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
View 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>;
}}

View file

@ -32,6 +32,7 @@ source = Split(
mapnik-index.cpp
process_csv_file.cpp
process_geojson_file.cpp
../../plugins/input/csv/csv_utils.cpp
"""
)

View file

@ -21,13 +21,16 @@
*****************************************************************************/
#include "process_csv_file.hpp"
#include "../../plugins/input/csv/csv_getline.hpp"
#include "../../plugins/input/csv/csv_utils.hpp"
#include <mapnik/datasource.hpp>
#include <mapnik/geometry_envelope.hpp>
#include <mapnik/util/utf_conv_win.hpp>
#if defined(MAPNIK_MEMORY_MAPPED_FILE)
#pragma GCC diagnostic push
#include <mapnik/warning_ignore.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/interprocess/mapped_region.hpp>
#include <boost/interprocess/streams/bufferstream.hpp>
#pragma GCC diagnostic pop
@ -35,14 +38,19 @@
#endif
#include <fstream>
#include <iomanip>
#include <iostream>
#include <sstream>
namespace mapnik { namespace detail {
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)
{
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)
using file_source_type = boost::interprocess::ibufferstream;
file_source_type csv_file;
@ -57,7 +65,7 @@ std::pair<bool,box2d<double>> process_csv_file(T & boxes, std::string const& fil
else
{
std::clog << "Error : cannot mmap " << filename << std::endl;
return std::make_pair(false, extent);
return std::make_pair(false, p.extent_);
}
#else
#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())
{
std::clog << "Error : cannot open " << filename << std::endl;
return std::make_pair(false, extent);
return std::make_pair(false, p.extent_);
}
#endif
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
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())
try
{
std::size_t index = 0;
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);
}
p.parse_csv(csv_file, boxes);
return std::make_pair(true, p.extent_);
}
else // parse first line as headers
catch (std::exception const& ex)
{
while (csv_utils::getline_csv(csv_file,csv_line,newline, quote))
{
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::clog << ex.what() << std::endl;
return std::make_pair(false, p.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>;

View file

@ -21,11 +21,6 @@
*****************************************************************************/
#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)
#pragma GCC diagnostic push
@ -35,9 +30,10 @@
#include <boost/spirit/include/qi.hpp>
#pragma GCC diagnostic pop
#include <mapnik/mapped_memory_cache.hpp>
#else
#include <mapnik/util/file_io.hpp>
#endif
#include <mapnik/json/positions_grammar.hpp>
#include <mapnik/json/extract_bounding_box_grammar_impl.hpp>
#include <mapnik/json/feature_collection_grammar_impl.hpp>