TopoJSON - implement parsing using spirit::x3

This commit is contained in:
artemp 2016-12-19 16:18:19 +01:00
parent 053eca1232
commit 18f721fd17
10 changed files with 487 additions and 711 deletions

View file

@ -1,73 +0,0 @@
/*****************************************************************************
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2015 Artem Pavlenko
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*****************************************************************************/
#ifndef MAPNIK_JSON_ERROR_HANDLER_HPP
#define MAPNIK_JSON_ERROR_HANDLER_HPP
#include <mapnik/config.hpp>
#pragma GCC diagnostic push
#include <mapnik/warning_ignore.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>
#include <sstream>
namespace mapnik { namespace json {
template <typename Iterator>
struct error_handler
{
using result_type = boost::spirit::qi::error_handler_result;
result_type operator() (
Iterator,
Iterator end,
Iterator err_pos,
boost::spirit::info const& what) const
{
#ifdef MAPNIK_LOG
std::stringstream s;
using difference_type = typename std::iterator_traits<Iterator>::difference_type;
auto start_err = err_pos;
std::advance(err_pos, std::min(std::distance(err_pos, end), difference_type(16)));
auto end_err = err_pos;
assert(end_err <= end);
s << "Mapnik GeoJSON parsing error:" << what << " expected but got: " << std::string(start_err, end_err);
MAPNIK_LOG_ERROR(error_handler) << s.str();
#endif
return boost::spirit::qi::fail;
}
};
}}
#endif // MAPNIK_JSON_ERROR_HANDLER_HPP

View file

@ -1,167 +0,0 @@
/*****************************************************************************
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2015 Artem Pavlenko
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*****************************************************************************/
#ifndef MAPNIK_GENERIC_JSON_HPP
#define MAPNIK_GENERIC_JSON_HPP
#include <mapnik/value/types.hpp>
#include <mapnik/util/variant.hpp>
#include <mapnik/json/value_converters.hpp>
#include <mapnik/json/json_value.hpp>
#pragma GCC diagnostic push
#include <mapnik/warning_ignore.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/fusion/include/std_pair.hpp>
#pragma GCC diagnostic pop
#include <vector>
namespace mapnik { namespace json {
namespace qi = boost::spirit::qi;
namespace standard = boost::spirit::standard;
namespace phoenix = boost::phoenix;
using space_type = standard::space_type;
using uchar = std::uint32_t; // a unicode code point
// unicode string grammar via boost/libs/spirit/example/qi/json/json/parser/grammar.hpp
template <typename Iterator>
struct unicode_string : qi::grammar<Iterator, std::string()>
{
unicode_string();
qi::rule<Iterator, void(std::string&)> escape;
qi::rule<Iterator, void(std::string&)> char_esc;
qi::rule<Iterator, std::string()> double_quoted;
};
struct push_utf8
{
using result_type = void;
void operator()(std::string& utf8, uchar code_point) const
{
typedef std::back_insert_iterator<std::string> insert_iter;
insert_iter out_iter(utf8);
boost::utf8_output_iterator<insert_iter> utf8_iter(out_iter);
*utf8_iter++ = code_point;
}
};
struct push_esc
{
using result_type = void;
void operator()(std::string& utf8, uchar c) const
{
switch (c)
{
case ' ': utf8 += ' '; break;
case '\t': utf8 += '\t'; break;
case '0': utf8 += char(0); break;
case 'a': utf8 += 0x7; break;
case 'b': utf8 += 0x8; break;
case 't': utf8 += 0x9; break;
case 'n': utf8 += 0xA; break;
case 'v': utf8 += 0xB; break;
case 'f': utf8 += 0xC; break;
case 'r': utf8 += 0xD; break;
case 'e': utf8 += 0x1B; break;
case '"': utf8 += '"'; break;
case '/': utf8 += '/'; break;
case '\\': utf8 += '\\'; break;
case '_': push_utf8()(utf8, 0xA0); break;
case 'N': push_utf8()(utf8, 0x85); break;
case 'L': push_utf8()(utf8, 0x2028); break;
case 'P': push_utf8()(utf8, 0x2029); break;
}
}
};
template< typename Iterator>
unicode_string<Iterator>::unicode_string()
: unicode_string::base_type(double_quoted)
{
qi::char_type char_;
qi::_val_type _val;
qi::_r1_type _r1;
qi::_1_type _1;
qi::lit_type lit;
qi::eol_type eol;
qi::repeat_type repeat;
qi::hex_type hex;
using boost::spirit::qi::uint_parser;
using boost::phoenix::function;
using boost::phoenix::ref;
uint_parser<uchar, 16, 4, 4> hex4;
uint_parser<uchar, 16, 8, 8> hex8;
function<push_utf8> push_utf8;
function<push_esc> push_esc;
escape =
('x' > hex) [push_utf8(_r1, _1)]
|
('u' > hex4) [push_utf8(_r1, _1)]
|
('U' > hex8) [push_utf8(_r1, _1)]
|
char_("0abtnvfre\"/\\N_LP \t") [push_esc(_r1, _1)]
|
eol // continue to next line
;
char_esc =
'\\' > escape(_r1)
;
double_quoted =
'"'
> *(char_esc(_val) | (~char_('"')) [_val += _1])
> '"'
;
}
template <typename Iterator>
struct generic_json : qi::grammar<Iterator, json_value(), space_type>
{
generic_json();
qi::rule<Iterator, json_value(), space_type> value;
qi::int_parser<mapnik::value_integer, 10, 1, -1> int__;
unicode_string<Iterator> string_;
qi::rule<Iterator, json_object_element(), space_type> key_value;
qi::rule<Iterator, json_value(), space_type> number;
qi::rule<Iterator, json_object(), space_type> object;
qi::rule<Iterator, json_array(), space_type> array;
qi::real_parser<double, qi::strict_real_policies<double>> strict_double;
// conversions
boost::phoenix::function<mapnik::detail::value_converter<mapnik::value_integer>> integer_converter;
boost::phoenix::function<mapnik::detail::value_converter<mapnik::value_double>> double_converter;
};
}}
#endif // MAPNIK_GENERIC_JSON_HPP

View file

@ -1,209 +0,0 @@
/*****************************************************************************
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2015 Artem Pavlenko
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*****************************************************************************/
#ifndef MAPNIK_TOPOJSON_GRAMMAR_HPP
#define MAPNIK_TOPOJSON_GRAMMAR_HPP
// mapnik
#include <mapnik/json/error_handler.hpp>
#include <mapnik/json/topology.hpp>
#include <mapnik/json/generic_json.hpp>
#include <mapnik/json/value_converters.hpp>
#pragma GCC diagnostic push
#include <mapnik/warning_ignore.hpp>
#include <boost/spirit/include/qi.hpp>
#pragma GCC diagnostic pop
// stl
#include <vector>
namespace mapnik { namespace topojson {
namespace qi = boost::spirit::qi;
namespace fusion = boost::fusion;
using space_type = mapnik::json::space_type;
struct create_point
{
using result_type = mapnik::topojson::point;
template <typename T0, typename T1>
result_type operator()(T0 & coord, T1 & props) const
{
mapnik::topojson::point pt;
if (coord.template is<mapnik::topojson::coordinate>())
{
auto const& coord_ = coord.template get<mapnik::topojson::coordinate>();
pt.coord = coord_;
pt.props = props;
}
return pt;
}
};
struct create_multi_point
{
using result_type = mapnik::topojson::multi_point;
template <typename T0, typename T1>
result_type operator()(T0 & coords, T1 & props) const
{
mapnik::topojson::multi_point mpt;
if (coords.template is<std::vector<mapnik::topojson::coordinate>>())
{
auto const& points = coords.template get<std::vector<mapnik::topojson::coordinate>>();
mpt. points = points;
mpt.props = props;
}
return mpt;
}
};
struct create_line_string
{
using result_type = mapnik::topojson::linestring;
template <typename T0, typename T1>
result_type operator()(T0 & arcs, T1 & props) const
{
mapnik::topojson::linestring line;
if (arcs.template is<std::vector<index_type>>())
{
auto const& arcs_ = arcs.template get<std::vector<index_type>>();
line.rings = arcs_;
line.props = props;
}
return line;
}
};
struct create_multi_line_string
{
using result_type = mapnik::topojson::multi_linestring;
template <typename T0, typename T1>
result_type operator()(T0 & arcs, T1 & props) const
{
mapnik::topojson::multi_linestring mline;
if (arcs.template is<std::vector<std::vector<index_type>>>())
{
auto const& arcs_ = arcs.template get<std::vector<std::vector<index_type>>>();
mline.lines = arcs_;
mline.props = props;
}
return mline;
}
};
struct create_polygon
{
using result_type = mapnik::topojson::polygon;
template <typename T0, typename T1>
result_type operator()(T0 & arcs, T1 & props) const
{
mapnik::topojson::polygon poly;
if (arcs.template is<std::vector<std::vector<index_type>>>())
{
auto const& arcs_ = arcs.template get<std::vector<std::vector<index_type>>>();
poly.rings = arcs_;
poly.props = props;
}
return poly;
}
};
struct create_multi_polygon
{
using result_type = mapnik::topojson::multi_polygon;
template <typename T0, typename T1>
result_type operator()(T0 & arcs, T1 & props) const
{
mapnik::topojson::multi_polygon mpoly;
if (arcs.template is<std::vector<std::vector<std::vector<index_type>>>>())
{
auto const& arcs_ = arcs.template get<std::vector<std::vector<std::vector<index_type>>>>();
mpoly.polygons = arcs_;
mpoly.props = props;
}
return mpoly;
}
};
struct create_geometry_impl
{
using result_type = mapnik::topojson::geometry;
template <typename T0, typename T1, typename T2, typename T3>
result_type operator()(T0 geom_type, T1 & coord, T2 & arcs, T3 & props) const
{
switch (geom_type)
{
case 1: //Point
return create_point()(coord, props);
case 2: //LineString
return create_line_string()(arcs, props);
case 3: //Polygon
return create_polygon()(arcs, props);
case 4: //MultiPoint
return create_multi_point()(coord, props);
case 5: //MultiLineString
return create_multi_line_string()(arcs, props);
case 6: //MultiPolygon
return create_multi_polygon()(arcs, props);
default:
break;
}
return mapnik::topojson::geometry(); //empty
}
};
using coordinates_type = util::variant<coordinate,std::vector<coordinate>>;
using arcs_type = util::variant<std::vector<index_type>,
std::vector<std::vector<index_type>>,
std::vector<std::vector<std::vector<index_type>>>>;
template <typename Iterator, typename ErrorHandler = json::error_handler<Iterator> >
struct topojson_grammar : qi::grammar<Iterator, space_type, topology()>
{
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;
qi::rule<Iterator, space_type, std::vector<mapnik::topojson::arc>()> arcs;
qi::rule<Iterator, space_type, mapnik::topojson::arc()> arc;
qi::rule<Iterator, space_type, mapnik::topojson::coordinate()> coordinate_;
qi::rule<Iterator, space_type, coordinates_type()> coordinates;
qi::rule<Iterator, space_type, mapnik::topojson::transform()> transform;
qi::rule<Iterator, space_type, mapnik::topojson::bounding_box()> bbox;
qi::rule<Iterator, qi::locals<int, coordinates_type, arcs_type, properties>, mapnik::topojson::geometry(), space_type> geometry;
qi::rule<Iterator, space_type, void(std::vector<mapnik::topojson::geometry>&)> geometry_collection;
qi::rule<Iterator, space_type, std::vector<index_type>()> ring;
qi::rule<Iterator, space_type, std::vector<std::vector<index_type>>()> rings;
qi::rule<Iterator, space_type, arcs_type()> rings_array;
// properties
qi::rule<Iterator, space_type, mapnik::topojson::properties()> properties_;
qi::symbols<char, int> geometry_type_dispatch;
};
}}
#endif //MAPNIK_TOPOJSON_GRAMMAR_HPP

View file

@ -1,206 +0,0 @@
/*****************************************************************************
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2015 Artem Pavlenko
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*****************************************************************************/
#include <mapnik/json/topojson_grammar.hpp>
#include <mapnik/json/generic_json.hpp>
#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/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::topology,
(std::vector<mapnik::topojson::geometry>, geometries)
(std::vector<mapnik::topojson::arc>, arcs)
(boost::optional<mapnik::topojson::transform>, tr)
(boost::optional<mapnik::topojson::bounding_box>, bbox)
)
namespace mapnik { namespace topojson {
namespace qi = boost::spirit::qi;
namespace phoenix = boost::phoenix;
namespace fusion = boost::fusion;
template <typename Iterator, typename ErrorHandler>
topojson_grammar<Iterator, ErrorHandler>::topojson_grammar()
: topojson_grammar::base_type(topology, "topojson")
{
qi::lit_type lit;
qi::double_type double_;
qi::int_type int_;
qi::omit_type omit;
qi::_val_type _val;
qi::_1_type _1;
qi::_2_type _2;
qi::_3_type _3;
qi::_4_type _4;
qi::_r1_type _r1;
qi::_a_type _a;
qi::_b_type _b;
qi::_c_type _c;
qi::_d_type _d;
using qi::fail;
using qi::on_error;
using phoenix::push_back;
geometry_type_dispatch.add
("\"Point\"",1)
("\"LineString\"",2)
("\"Polygon\"",3)
("\"MultiPoint\"",4)
("\"MultiLineString\"",5)
("\"MultiPolygon\"",6)
("\"GeometryCollection\"",7)
;
// error handler
boost::phoenix::function<ErrorHandler> const error_handler;
boost::phoenix::function<create_geometry_impl> const create_geometry;
// topo json
topology = lit('{') >> lit("\"type\"") >> lit(':') >> lit("\"Topology\"")
>> ( (lit(',') >> objects ) ^ ( lit(',') >> arcs) ^ (lit(',') >> transform) ^ (lit(',') >> bbox))
>> lit('}')
;
transform = lit("\"transform\"") >> lit(':') >> lit('{')
>> lit("\"scale\"") >> lit(':')
>> lit('[')
>> double_ >> lit(',')
>> double_ >> lit(']') >> lit(',')
>> lit("\"translate\"") >> lit(':')
>> lit('[') >> double_ >> lit(',') >> double_ >> lit(']')
>> lit('}')
;
bbox = lit("\"bbox\"") >> lit(':')
>> lit('[') >> double_ >> lit(',') >> double_
>> lit(',') >> double_ >> lit(',') >> double_
>> lit(']')
;
objects = lit("\"objects\"")
>> lit(':')
>> lit('{')
>> -((omit[json.string_]
>> lit(':')
>> (geometry_collection(_val) | geometry[push_back(_val, _1)]) % lit(',')))
>> lit('}')
;
geometry = lit('{')[_a = 0]
> ((lit("\"type\"") > lit(':') > geometry_type_dispatch[_a = _1])
|
(lit("\"coordinates\"") > lit(':') > coordinates[_b = _1])
|
(lit("\"arcs\"") > lit(':') > rings_array[_c = _1])
|
properties_[_d = _1]
|
json.key_value) % lit(',')
> lit('}')[_val = create_geometry(_a, _b, _c, _d)]
;
geometry_collection = lit('{')
>> lit("\"type\"") >> lit(':') >> lit("\"GeometryCollection\"")
>> lit(',') >> lit("\"geometries\"") >> lit(':')
>> lit('[')
>> -(geometry[push_back(_r1, _1)] % lit(','))
>> lit(']')
>> lit('}')
;
ring = lit('[') >> -(int_ % lit(',')) >> lit(']')
;
rings = lit('[') >> -(ring % lit(',')) >> lit(']')
;
rings_array = lit('[') >> -(rings % lit(',')) >> lit(']')
|
rings
|
ring
;
properties_ = lit("\"properties\"")
>> lit(':')
>> lit('{') >> (json.string_ >> lit(':') >> json.value) % lit(',') >> lit('}')
;
arcs = lit("\"arcs\"") >> lit(':')
>> lit('[') >> -( arc % lit(',')) >> lit(']') ;
arc = lit('[') >> -(coordinate_ % lit(',')) >> lit(']') ;
coordinate_ = lit('[') > double_ > lit(',') > double_ > lit(']');
coordinates = (lit('[') >> coordinate_ % lit(',') > lit(']'))
| coordinate_;
topology.name("topology");
transform.name("transform");
objects.name("objects");
arc.name("arc");
arcs.name("arcs");
json.value.name("value");
coordinate_.name("coordinate");
geometry.name("geometry");
properties_.name("properties");
geometry_collection.name("geometry_collection");
// error handler
on_error<fail>(topology, error_handler(_1, _2, _3, _4));
}
}}

View file

@ -2,7 +2,7 @@
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2015 Artem Pavlenko
* Copyright (C) 2016 Artem Pavlenko
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -20,44 +20,29 @@
*
*****************************************************************************/
#include <mapnik/json/generic_json.hpp>
#ifndef MAPNIK_TOPOJSON_GRAMMAR_X3_HPP
#define MAPNIK_TOPOJSON_GRAMMAR_X3_HPP
namespace mapnik { namespace json {
// mapnik
#include <mapnik/json/topology.hpp>
template <typename Iterator>
generic_json<Iterator>::generic_json()
: generic_json::base_type(value)
{
qi::lit_type lit;
qi::_val_type _val;
qi::_1_type _1;
using phoenix::construct;
// generic json types
value = object | array | string_ | number
;
#pragma GCC diagnostic push
#include <mapnik/warning_ignore.hpp>
#include <boost/spirit/home/x3.hpp>
#pragma GCC diagnostic pop
key_value = string_ > lit(':') > value
;
namespace mapnik { namespace json { namespace grammar {
object = lit('{')
> -(key_value % lit(','))
> lit('}')
;
namespace x3 = boost::spirit::x3;
array = lit('[')
> -(value % lit(','))
> lit(']')
;
using topojson_grammar_type = x3::rule<class topojson_rule_tag, topojson::topology>;
BOOST_SPIRIT_DECLARE(topojson_grammar_type);
number = strict_double[_val = double_converter(_1)]
| int__[_val = integer_converter(_1)]
| lit("true") [_val = true]
| lit ("false") [_val = false]
| lit("null")[_val = construct<value_null>()]
;
}
grammar::topojson_grammar_type const& topojson_grammar();
}}
using iterator_type = char const*;
template struct mapnik::json::generic_json<iterator_type>;
#endif //MAPNIK_TOPOJSON_GRAMMAR_X3_HPP

View file

@ -0,0 +1,427 @@
/*****************************************************************************
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2016 Artem Pavlenko
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*****************************************************************************/
#ifndef MAPNIK_JSON_TOPOJSON_GRAMMAR_X3_DEF_HPP
#define MAPNIK_JSON_TOPOJSON_GRAMMAR_X3_DEF_HPP
#include <mapnik/json/topojson_grammar_x3.hpp>
#include <mapnik/json/unicode_string_grammar_x3.hpp>
#include <mapnik/json/generic_json_grammar_x3.hpp>
#pragma GCC diagnostic push
#include <mapnik/warning_ignore.hpp>
#include <boost/fusion/adapted/struct.hpp>
#include <boost/fusion/adapted/std_tuple.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)
)
namespace mapnik { namespace json { namespace grammar {
using index_type = topojson::index_type;
struct create_point
{
using result_type = mapnik::topojson::point;
template <typename T0, typename T1>
result_type operator()(T0 & coord, T1 & props) const
{
mapnik::topojson::point pt;
if (coord.template is<mapnik::topojson::coordinate>())
{
auto const& coord_ = coord.template get<mapnik::topojson::coordinate>();
pt.coord = coord_;
pt.props = props;
}
return pt;
}
};
struct create_multi_point
{
using result_type = mapnik::topojson::multi_point;
template <typename T0, typename T1>
result_type operator()(T0 & coords, T1 & props) const
{
mapnik::topojson::multi_point mpt;
if (coords.template is<std::vector<mapnik::topojson::coordinate>>())
{
auto const& points = coords.template get<std::vector<mapnik::topojson::coordinate>>();
mpt. points = points;
mpt.props = props;
}
return mpt;
}
};
struct create_line_string
{
using result_type = mapnik::topojson::linestring;
template <typename T0, typename T1>
result_type operator()(T0 & arcs, T1 & props) const
{
mapnik::topojson::linestring line;
if (arcs.template is<std::vector<index_type>>())
{
auto const& arcs_ = arcs.template get<std::vector<index_type>>();
line.rings = arcs_;
line.props = props;
}
return line;
}
};
struct create_multi_line_string
{
using result_type = mapnik::topojson::multi_linestring;
template <typename T0, typename T1>
result_type operator()(T0 & arcs, T1 & props) const
{
mapnik::topojson::multi_linestring mline;
if (arcs.template is<std::vector<std::vector<index_type>>>())
{
auto const& arcs_ = arcs.template get<std::vector<std::vector<index_type>>>();
mline.lines = arcs_;
mline.props = props;
}
return mline;
}
};
struct create_polygon
{
using result_type = mapnik::topojson::polygon;
template <typename T0, typename T1>
result_type operator()(T0 & arcs, T1 & props) const
{
mapnik::topojson::polygon poly;
if (arcs.template is<std::vector<std::vector<index_type>>>())
{
auto const& arcs_ = arcs.template get<std::vector<std::vector<index_type>>>();
poly.rings = arcs_;
poly.props = props;
}
return poly;
}
};
struct create_multi_polygon
{
using result_type = mapnik::topojson::multi_polygon;
template <typename T0, typename T1>
result_type operator()(T0 & arcs, T1 & props) const
{
mapnik::topojson::multi_polygon mpoly;
if (arcs.template is<std::vector<std::vector<std::vector<index_type>>>>())
{
auto const& arcs_ = arcs.template get<std::vector<std::vector<std::vector<index_type>>>>();
mpoly.polygons = arcs_;
mpoly.props = props;
}
return mpoly;
}
};
auto create_geometry = [] (auto const& ctx)
{
auto const geom_type = std::get<0>(_attr(ctx));
auto const& coord = std::get<1>(_attr(ctx));
auto const& arcs = std::get<2>(_attr(ctx));
auto const& props = std::get<3>(_attr(ctx));
mapnik::topojson::geometry geom; //empty
switch (geom_type)
{
case 1: //Point
geom = create_point()(coord, props);
break;
case 2: //LineString
geom = create_line_string()(arcs, props);
break;
case 3: //Polygon
geom = create_polygon()(arcs, props);
break;
case 4: //MultiPoint
geom = create_multi_point()(coord, props);
break;
case 5: //MultiLineString
geom = create_multi_line_string()(arcs, props);
break;
case 6: //MultiPolygon
geom = create_multi_polygon()(arcs, props);
break;
}
_val(ctx) = std::move(geom);
};
auto assign_bbox = [] (auto const& ctx)
{
_val(ctx).bbox = std::move(_attr(ctx));
};
auto assign_transform = [] (auto const& ctx)
{
_val(ctx).tr = std::move(_attr(ctx));
};
auto assign_arcs = [] (auto const& ctx)
{
_val(ctx).arcs = std::move(_attr(ctx));
};
auto assign_objects = [] (auto const& ctx)
{
_val(ctx).geometries = std::move(_attr(ctx));
};
auto push_geometry = [] (auto const& ctx)
{
_val(ctx).push_back(std::move(_attr(ctx)));
};
auto push_collection = [] (auto const& ctx)
{
auto & dest = _val(ctx);
auto const& src = _attr(ctx);
if (dest.empty()) dest = std::move(src);
else
dest.insert(std::end(dest),
std::make_move_iterator(std::begin(src)),
std::make_move_iterator(std::end(src)));
};
auto assign_geometry_type = [] (auto const& ctx)
{
std::get<0>(_val(ctx)) = _attr(ctx);
};
auto assign_coordinates = [] (auto const& ctx)
{
std::get<1>(_val(ctx)) = std::move(_attr(ctx));
};
auto assign_rings = [] (auto const& ctx)
{
std::get<2>(_val(ctx)) = std::move(_attr(ctx));
};
auto assign_properties = [] (auto const& ctx)
{
std::get<3>(_val(ctx)) = std::move(_attr(ctx));
};
namespace x3 = boost::spirit::x3;
using x3::lit;
using x3::double_;
using x3::int_;
using x3::omit;
namespace
{
// import unicode string rule
auto const& json_string = json::unicode_string_grammar();
// json value
auto const& json_value = json::generic_json_grammar();
}
using coordinates_type = util::variant<topojson::coordinate,std::vector<topojson::coordinate>>;
using arcs_type = util::variant<std::vector<index_type>,
std::vector<std::vector<index_type>>,
std::vector<std::vector<std::vector<index_type>>>>;
struct geometry_type_ : x3::symbols<int>
{
geometry_type_()
{
add
("\"Point\"",1)
("\"LineString\"",2)
("\"Polygon\"",3)
("\"MultiPoint\"",4)
("\"MultiLineString\"",5)
("\"MultiPolygon\"",6)
;
}
} geometry_type;
// start rule
topojson_grammar_type const topology = "Topology";
// rules
x3::rule<class transform_tag, mapnik::topojson::transform> transform = "Transform";
x3::rule<class bbox_tag, mapnik::topojson::bounding_box> bbox = "Bounding Box";
x3::rule<class objects_tag, std::vector<mapnik::topojson::geometry>> objects= "Objects";
x3::rule<class properties_tag, mapnik::topojson::properties> properties = "Properties";
x3::rule<class geometry_tag, mapnik::topojson::geometry> geometry = "Geometry";
x3::rule<class geometry_collection_tag, std::vector<mapnik::topojson::geometry>> geometry_collection = "Geometry Collection";
x3::rule<class geometry_tuple_tag,
std::tuple<int,
coordinates_type,
arcs_type,
mapnik::topojson::properties>> geometry_tuple = "Geometry Tuple";
x3::rule<class coordinate_tag, mapnik::topojson::coordinate> coordinate = "Coordinate";
x3::rule<class coordinates_tag, coordinates_type> coordinates = "Coordinates";
x3::rule<class arc_tag, mapnik::topojson::arc> arc = "Arc";
x3::rule<class arcs_tag, std::vector<mapnik::topojson::arc>> arcs = "Arcs";
x3::rule<class ring_type, std::vector<index_type>> ring = "Ring";
x3::rule<class rings_type, std::vector<std::vector<index_type>>> rings = "Rings";
x3::rule<class rings_array_type, arcs_type> rings_array = "Rings Array";
// defs
auto const topology_def = lit('{') >
-(((lit("\"type\"") > lit(':') > lit("\"Topology\""))
|
bbox[assign_bbox]
|
transform[assign_transform]
|
objects[assign_objects]
|
arcs[assign_arcs]) % lit(','))
> lit('}')
;
auto const transform_def = lit("\"transform\"") > lit(':') > lit('{')
> lit("\"scale\"") > lit(':')
> lit('[')
> double_ > lit(',')
> double_ > lit(']') > lit(',')
> lit("\"translate\"") > lit(':')
> lit('[') > double_ > lit(',') > double_ > lit(']')
> lit('}')
;
auto const bbox_def = lit("\"bbox\"") > lit(':')
> lit('[') > double_ > lit(',') > double_
> lit(',') > double_ > lit(',') > double_
> lit(']')
;
auto const objects_def = lit("\"objects\"") > lit(':')
> lit('{')
> ((omit[json_string] > lit(':') > ((geometry_collection[push_collection] | geometry[push_geometry]))) % lit(','))
> lit('}')
;
auto const geometry_tuple_def =
((lit("\"type\"") > lit(':') > geometry_type[assign_geometry_type])
|
(lit("\"coordinates\"") > lit(':') > coordinates[assign_coordinates])
|
(lit("\"arcs\"") > lit(':') > rings_array[assign_rings])
|
properties[assign_properties]
|
omit[json_string >> lit(':') >> json_value]) % lit(',')
;
auto const geometry_def = lit("{") > geometry_tuple[create_geometry] > lit("}");
auto const geometry_collection_def = (lit('{') >> lit("\"type\"") >> lit(':') >> lit("\"GeometryCollection\"") >> -omit[lit(',') >> bbox])
> lit(',') > lit("\"geometries\"") > lit(':')
> lit('[')
> -(geometry[push_geometry] % lit(','))
> lit(']')
> lit('}')
;
auto const ring_def = lit('[') >> (int_ % lit(',')) >> lit(']')
;
auto const rings_def = lit('[') >> (ring % lit(',')) >> lit(']')
;
auto const rings_array_def = (lit('[') >> (rings % lit(',')) >> lit(']'))
|
rings
|
ring
;
auto const properties_def = lit("\"properties\"")
> lit(':')
> lit('{') > (json_string > lit(':') > json_value) % lit(',') > lit('}')
;
auto const arcs_def = lit("\"arcs\"") >> lit(':') >> lit('[') >> -( arc % lit(',')) >> lit(']') ;
auto const arc_def = lit('[') >> -(coordinate % lit(',')) >> lit(']') ;
auto const coordinate_def = lit('[') >> double_ >> lit(',') >> double_ >> omit[*(lit(',') >> double_)] >> lit(']');
auto const coordinates_def = (lit('[') >> coordinate % lit(',') >> lit(']')) | coordinate;
BOOST_SPIRIT_DEFINE(
topology,
transform,
bbox,
objects,
geometry_tuple,
geometry,
geometry_collection,
ring,
rings,
rings_array,
properties,
arcs,
arc,
coordinate,
coordinates
);
}}}
namespace mapnik { namespace json {
grammar::topojson_grammar_type const& topojson_grammar()
{
return grammar::topology;
}
}}
#endif //MAPNIK_TOPOJSON_GRAMMAR_X3_DEF_HPP

View file

@ -34,7 +34,7 @@
#include <mapnik/value/types.hpp>
#include <mapnik/box2d.hpp>
#include <mapnik/geometry/boost_adapters.hpp>
#include <mapnik/json/topojson_grammar.hpp>
#include <mapnik/json/topojson_grammar_x3.hpp>
#include <mapnik/json/topojson_utils.hpp>
#include <mapnik/util/variant.hpp>
#include <mapnik/util/file_io.hpp>
@ -176,20 +176,22 @@ topojson_datasource::topojson_datasource(parameters const& params)
}
}
namespace {
using iterator_type = const char*;
const mapnik::topojson::topojson_grammar<iterator_type> g;
}
template <typename T>
void topojson_datasource::parse_topojson(T const& buffer)
{
boost::spirit::standard::space_type space;
auto itr = buffer;
auto end = buffer + std::strlen(buffer);
bool result = boost::spirit::qi::phrase_parse(itr, end, g, space, topo_);
if (!result)
using space_type = boost::spirit::x3::standard::space_type;
try
{
boost::spirit::x3::phrase_parse(itr, end, mapnik::json::topojson_grammar(), space_type(), topo_);
}
catch (boost::spirit::x3::expectation_failure<char const*> const& ex)
{
std::clog << "failed to parse TopoJSON..." << std::endl;
std::clog << ex.what() << std::endl;
std::clog << "Expected: " << ex.which();
std::clog << " Got: \"" << std::string(ex.where(), ex.where() + 200) << "...\"" << std::endl;
throw mapnik::datasource_exception("topojson_datasource: Failed parse TopoJSON file '" + filename_ + "'");
}

View file

@ -2,7 +2,7 @@
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2015 Artem Pavlenko
* Copyright (C) 2016 Artem Pavlenko
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -20,9 +20,11 @@
*
*****************************************************************************/
#include <mapnik/feature.hpp>
#include <mapnik/json/topojson_grammar_impl.hpp>
#include <string>
#include <mapnik/json/json_grammar_config.hpp>
#include <mapnik/json/topojson_grammar_x3_def.hpp>
using iterator_type = char const*;
template struct mapnik::topojson::topojson_grammar<iterator_type> ;
namespace mapnik { namespace json { namespace grammar {
BOOST_SPIRIT_INSTANTIATE(topojson_grammar_type, iterator_type, phrase_parse_context_type);
}}}

View file

@ -23,6 +23,11 @@
#include <mapnik/json/unicode_string_grammar_x3_def.hpp>
#include <mapnik/json/json_grammar_config.hpp>
#include <mapnik/json/extract_bounding_boxes_x3_config.hpp>
#include <mapnik/json/json_value.hpp>
#pragma GCC diagnostic push
#include <mapnik/warning_ignore.hpp>
#include <boost/fusion/adapted/std_tuple.hpp>
#pragma GCC diagnostic pop
namespace mapnik { namespace json { namespace grammar {
@ -44,3 +49,5 @@ BOOST_SPIRIT_INSTANTIATE_UNUSED(unicode_string_grammar_type, iterator_type, extr
BOOST_SPIRIT_INSTANTIATE_UNUSED(unicode_string_grammar_type, iterator_type, extract_bounding_boxes_reverse_context_type_f);
}}}
template bool mapnik::json::grammar::parse_rule<char const*, boost::spirit::x3::context<boost::spirit::x3::skipper_tag, boost::spirit::x3::char_class<boost::spirit::char_encoding::standard, boost::spirit::x3::space_tag> const, boost::spirit::x3::unused_type>, boost::fusion::iterator_range<boost::fusion::std_tuple_iterator<std::__1::tuple<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, mapnik::json::json_value>, 0>, boost::fusion::std_tuple_iterator<std::__1::tuple<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, mapnik::json::json_value>, 1> > >(boost::spirit::x3::rule<mapnik::json::grammar::unicode_string_tag, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, false>, char const*&, char const* const&, boost::spirit::x3::context<boost::spirit::x3::skipper_tag, boost::spirit::x3::char_class<boost::spirit::char_encoding::standard, boost::spirit::x3::space_tag> const, boost::spirit::x3::unused_type> const&, boost::fusion::iterator_range<boost::fusion::std_tuple_iterator<std::__1::tuple<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, mapnik::json::json_value>, 0>, boost::fusion::std_tuple_iterator<std::__1::tuple<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, mapnik::json::json_value>, 1> >&);

View file

@ -26,14 +26,11 @@
#include <mapnik/util/fs.hpp>
#include <mapnik/util/file_io.hpp>
#include <mapnik/json/topology.hpp>
#include <mapnik/json/topojson_grammar.hpp>
#include <mapnik/json/topojson_grammar_x3.hpp>
#include <mapnik/json/topojson_utils.hpp>
namespace {
using iterator_type = char const*;
const mapnik::topojson::topojson_grammar<iterator_type> grammar;
bool parse_topology(std::string const& filename, mapnik::topojson::topology & topo)
{
mapnik::util::file file(filename);
@ -41,11 +38,22 @@ bool parse_topology(std::string const& filename, mapnik::topojson::topology & to
buffer.resize(file.size());
std::fread(&buffer[0], buffer.size(), 1, file.get());
if (!file) return false;
boost::spirit::standard::space_type space;
iterator_type itr = buffer.c_str();
iterator_type end = itr + buffer.length();
bool result = boost::spirit::qi::phrase_parse(itr, end, grammar, space, topo);
return (result && (itr == end));
using space_type = boost::spirit::x3::standard::space_type;
char const* itr = buffer.c_str();
char const* end = itr + buffer.length();
try
{
boost::spirit::x3::phrase_parse(itr, end, mapnik::json::topojson_grammar(), space_type() , topo);
}
catch (boost::spirit::x3::expectation_failure<char const*> const& ex)
{
std::cerr << "failed to parse TopoJSON..." << std::endl;
std::cerr << ex.what() << std::endl;
std::cerr << "Expected: " << ex.which();
std::cerr << " Got: \"" << std::string(ex.where(), ex.where() + 200) << "...\"" << std::endl;
return false;
}
return (itr == end);
}
}