diff --git a/include/mapnik/json/error_handler.hpp b/include/mapnik/json/error_handler.hpp deleted file mode 100644 index 1e66427ad..000000000 --- a/include/mapnik/json/error_handler.hpp +++ /dev/null @@ -1,73 +0,0 @@ -/***************************************************************************** - * - * This file is part of Mapnik (c++ mapping toolkit) - * - * Copyright (C) 2015 Artem Pavlenko - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - *****************************************************************************/ - -#ifndef MAPNIK_JSON_ERROR_HANDLER_HPP -#define MAPNIK_JSON_ERROR_HANDLER_HPP - -#include - -#pragma GCC diagnostic push -#include -#include -namespace boost { namespace spirit { struct info; } } -#pragma GCC diagnostic pop - -// mapnik -#ifdef MAPNIK_LOG -#include -#endif - -// stl -#include -#include -#include - - -namespace mapnik { namespace json { - -template -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::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 diff --git a/include/mapnik/json/generic_json.hpp b/include/mapnik/json/generic_json.hpp deleted file mode 100644 index eea6dd993..000000000 --- a/include/mapnik/json/generic_json.hpp +++ /dev/null @@ -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 -#include -#include -#include -#pragma GCC diagnostic push -#include -#include -#include -#include -#pragma GCC diagnostic pop - -#include - -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 -struct unicode_string : qi::grammar -{ - unicode_string(); - qi::rule escape; - qi::rule char_esc; - qi::rule double_quoted; -}; - - -struct push_utf8 -{ - using result_type = void; - - void operator()(std::string& utf8, uchar code_point) const - { - typedef std::back_insert_iterator insert_iter; - insert_iter out_iter(utf8); - boost::utf8_output_iterator 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::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 hex4; - uint_parser hex8; - function push_utf8; - function 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 -struct generic_json : qi::grammar -{ - generic_json(); - qi::rule value; - qi::int_parser int__; - unicode_string string_; - qi::rule key_value; - qi::rule number; - qi::rule object; - qi::rule array; - qi::real_parser> strict_double; - // conversions - boost::phoenix::function> integer_converter; - boost::phoenix::function> double_converter; -}; - -}} - -#endif // MAPNIK_GENERIC_JSON_HPP diff --git a/include/mapnik/json/topojson_grammar.hpp b/include/mapnik/json/topojson_grammar.hpp deleted file mode 100644 index 5139b5c6c..000000000 --- a/include/mapnik/json/topojson_grammar.hpp +++ /dev/null @@ -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 -#include -#include -#include - -#pragma GCC diagnostic push -#include -#include -#pragma GCC diagnostic pop - -// stl -#include - -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 - result_type operator()(T0 & coord, T1 & props) const - { - mapnik::topojson::point pt; - if (coord.template is()) - { - auto const& coord_ = coord.template get(); - pt.coord = coord_; - pt.props = props; - } - return pt; - } -}; - -struct create_multi_point -{ - using result_type = mapnik::topojson::multi_point; - template - result_type operator()(T0 & coords, T1 & props) const - { - mapnik::topojson::multi_point mpt; - if (coords.template is>()) - { - auto const& points = coords.template get>(); - mpt. points = points; - mpt.props = props; - } - return mpt; - } -}; - -struct create_line_string -{ - using result_type = mapnik::topojson::linestring; - template - result_type operator()(T0 & arcs, T1 & props) const - { - mapnik::topojson::linestring line; - if (arcs.template is>()) - { - auto const& arcs_ = arcs.template get>(); - line.rings = arcs_; - line.props = props; - } - return line; - } -}; - -struct create_multi_line_string -{ - using result_type = mapnik::topojson::multi_linestring; - template - result_type operator()(T0 & arcs, T1 & props) const - { - mapnik::topojson::multi_linestring mline; - if (arcs.template is>>()) - { - auto const& arcs_ = arcs.template get>>(); - mline.lines = arcs_; - mline.props = props; - } - return mline; - } -}; - -struct create_polygon -{ - using result_type = mapnik::topojson::polygon; - template - result_type operator()(T0 & arcs, T1 & props) const - { - mapnik::topojson::polygon poly; - if (arcs.template is>>()) - { - auto const& arcs_ = arcs.template get>>(); - poly.rings = arcs_; - poly.props = props; - } - return poly; - } -}; - -struct create_multi_polygon -{ - using result_type = mapnik::topojson::multi_polygon; - template - result_type operator()(T0 & arcs, T1 & props) const - { - mapnik::topojson::multi_polygon mpoly; - if (arcs.template is>>>()) - { - auto const& arcs_ = arcs.template get>>>(); - mpoly.polygons = arcs_; - mpoly.props = props; - } - return mpoly; - } -}; - - -struct create_geometry_impl -{ - using result_type = mapnik::topojson::geometry; - template - 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>; -using arcs_type = util::variant, - std::vector>, - std::vector>>>; -template > -struct topojson_grammar : qi::grammar - -{ - topojson_grammar(); -private: - // generic JSON support - json::generic_json json; - // topoJSON - qi::rule topology; - qi::rule()> objects; - qi::rule()> arcs; - qi::rule arc; - qi::rule coordinate_; - qi::rule coordinates; - qi::rule transform; - qi::rule bbox; - qi::rule, mapnik::topojson::geometry(), space_type> geometry; - qi::rule&)> geometry_collection; - qi::rule()> ring; - qi::rule>()> rings; - qi::rule rings_array; - // properties - qi::rule properties_; - qi::symbols geometry_type_dispatch; -}; - -}} - -#endif //MAPNIK_TOPOJSON_GRAMMAR_HPP diff --git a/include/mapnik/json/topojson_grammar_impl.hpp b/include/mapnik/json/topojson_grammar_impl.hpp deleted file mode 100644 index 07b3a9756..000000000 --- a/include/mapnik/json/topojson_grammar_impl.hpp +++ /dev/null @@ -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 -#include - -#pragma GCC diagnostic push -#include -#include -#include -#include -#pragma GCC diagnostic pop - -BOOST_FUSION_ADAPT_STRUCT( - mapnik::topojson::coordinate, - (double, x) - (double, y) - ) - -BOOST_FUSION_ADAPT_STRUCT( - mapnik::topojson::arc, - (std::list, 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, geometries) - (std::vector, arcs) - (boost::optional, tr) - (boost::optional, bbox) - ) - - - -namespace mapnik { namespace topojson { - -namespace qi = boost::spirit::qi; -namespace phoenix = boost::phoenix; -namespace fusion = boost::fusion; - -template -topojson_grammar::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 const error_handler; - boost::phoenix::function 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(topology, error_handler(_1, _2, _3, _4)); -} - -}} diff --git a/src/json/generic_json.cpp b/include/mapnik/json/topojson_grammar_x3.hpp similarity index 50% rename from src/json/generic_json.cpp rename to include/mapnik/json/topojson_grammar_x3.hpp index 7fb2e9fd3..6f5d58eb0 100644 --- a/src/json/generic_json.cpp +++ b/include/mapnik/json/topojson_grammar_x3.hpp @@ -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 +#ifndef MAPNIK_TOPOJSON_GRAMMAR_X3_HPP +#define MAPNIK_TOPOJSON_GRAMMAR_X3_HPP -namespace mapnik { namespace json { +// mapnik +#include -template -generic_json::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 +#include +#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; + +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()] - ; } +grammar::topojson_grammar_type const& topojson_grammar(); + }} -using iterator_type = char const*; -template struct mapnik::json::generic_json; +#endif //MAPNIK_TOPOJSON_GRAMMAR_X3_HPP diff --git a/include/mapnik/json/topojson_grammar_x3_def.hpp b/include/mapnik/json/topojson_grammar_x3_def.hpp new file mode 100644 index 000000000..aa040c371 --- /dev/null +++ b/include/mapnik/json/topojson_grammar_x3_def.hpp @@ -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 +#include +#include +#pragma GCC diagnostic push +#include +#include +#include +#pragma GCC diagnostic pop + +BOOST_FUSION_ADAPT_STRUCT( + mapnik::topojson::coordinate, + (double, x) + (double, y) + ) + +BOOST_FUSION_ADAPT_STRUCT( + mapnik::topojson::arc, + (std::list, 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 + result_type operator()(T0 & coord, T1 & props) const + { + mapnik::topojson::point pt; + if (coord.template is()) + { + auto const& coord_ = coord.template get(); + pt.coord = coord_; + pt.props = props; + } + return pt; + } +}; + +struct create_multi_point +{ + using result_type = mapnik::topojson::multi_point; + template + result_type operator()(T0 & coords, T1 & props) const + { + mapnik::topojson::multi_point mpt; + if (coords.template is>()) + { + auto const& points = coords.template get>(); + mpt. points = points; + mpt.props = props; + } + return mpt; + } +}; + +struct create_line_string +{ + using result_type = mapnik::topojson::linestring; + template + result_type operator()(T0 & arcs, T1 & props) const + { + mapnik::topojson::linestring line; + if (arcs.template is>()) + { + auto const& arcs_ = arcs.template get>(); + line.rings = arcs_; + line.props = props; + } + return line; + } +}; + +struct create_multi_line_string +{ + using result_type = mapnik::topojson::multi_linestring; + template + result_type operator()(T0 & arcs, T1 & props) const + { + mapnik::topojson::multi_linestring mline; + if (arcs.template is>>()) + { + auto const& arcs_ = arcs.template get>>(); + mline.lines = arcs_; + mline.props = props; + } + return mline; + } +}; + +struct create_polygon +{ + using result_type = mapnik::topojson::polygon; + template + result_type operator()(T0 & arcs, T1 & props) const + { + mapnik::topojson::polygon poly; + if (arcs.template is>>()) + { + auto const& arcs_ = arcs.template get>>(); + poly.rings = arcs_; + poly.props = props; + } + return poly; + } +}; + +struct create_multi_polygon +{ + using result_type = mapnik::topojson::multi_polygon; + template + result_type operator()(T0 & arcs, T1 & props) const + { + mapnik::topojson::multi_polygon mpoly; + if (arcs.template is>>>()) + { + auto const& arcs_ = arcs.template get>>>(); + 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>; +using arcs_type = util::variant, + std::vector>, + std::vector>>>; + +struct geometry_type_ : x3::symbols +{ + 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 transform = "Transform"; +x3::rule bbox = "Bounding Box"; +x3::rule> objects= "Objects"; +x3::rule properties = "Properties"; +x3::rule geometry = "Geometry"; +x3::rule> geometry_collection = "Geometry Collection"; +x3::rule> geometry_tuple = "Geometry Tuple"; +x3::rule coordinate = "Coordinate"; +x3::rule coordinates = "Coordinates"; +x3::rule arc = "Arc"; +x3::rule> arcs = "Arcs"; +x3::rule> ring = "Ring"; +x3::rule>> rings = "Rings"; +x3::rule 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 diff --git a/plugins/input/topojson/topojson_datasource.cpp b/plugins/input/topojson/topojson_datasource.cpp index 7c5493764..0c9b99a5e 100644 --- a/plugins/input/topojson/topojson_datasource.cpp +++ b/plugins/input/topojson/topojson_datasource.cpp @@ -34,7 +34,7 @@ #include #include #include -#include +#include #include #include #include @@ -176,20 +176,22 @@ topojson_datasource::topojson_datasource(parameters const& params) } } -namespace { -using iterator_type = const char*; -const mapnik::topojson::topojson_grammar g; -} - template 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 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_ + "'"); } diff --git a/src/json/mapnik_topojson_grammar.cpp b/src/json/topojson_grammar_x3.cpp similarity index 76% rename from src/json/mapnik_topojson_grammar.cpp rename to src/json/topojson_grammar_x3.cpp index e4b9dca32..895b5e8cd 100644 --- a/src/json/mapnik_topojson_grammar.cpp +++ b/src/json/topojson_grammar_x3.cpp @@ -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 -#include -#include +#include +#include -using iterator_type = char const*; -template struct mapnik::topojson::topojson_grammar ; +namespace mapnik { namespace json { namespace grammar { + +BOOST_SPIRIT_INSTANTIATE(topojson_grammar_type, iterator_type, phrase_parse_context_type); + +}}} diff --git a/src/json/unicode_string_grammar_x3.cpp b/src/json/unicode_string_grammar_x3.cpp index 76c0e731e..515ef66bd 100644 --- a/src/json/unicode_string_grammar_x3.cpp +++ b/src/json/unicode_string_grammar_x3.cpp @@ -23,6 +23,11 @@ #include #include #include +#include +#pragma GCC diagnostic push +#include +#include +#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 const, boost::spirit::x3::unused_type>, boost::fusion::iterator_range, std::__1::allocator >, mapnik::json::json_value>, 0>, boost::fusion::std_tuple_iterator, std::__1::allocator >, mapnik::json::json_value>, 1> > >(boost::spirit::x3::rule, std::__1::allocator >, false>, char const*&, char const* const&, boost::spirit::x3::context const, boost::spirit::x3::unused_type> const&, boost::fusion::iterator_range, std::__1::allocator >, mapnik::json::json_value>, 0>, boost::fusion::std_tuple_iterator, std::__1::allocator >, mapnik::json::json_value>, 1> >&); diff --git a/test/unit/datasource/topojson.cpp b/test/unit/datasource/topojson.cpp index 7bf155e33..229e7d9b1 100644 --- a/test/unit/datasource/topojson.cpp +++ b/test/unit/datasource/topojson.cpp @@ -26,14 +26,11 @@ #include #include #include -#include +#include #include namespace { -using iterator_type = char const*; -const mapnik::topojson::topojson_grammar 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 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); } }