Merge branch 'large-geojson'
This commit is contained in:
commit
c99cb19643
12 changed files with 593 additions and 72 deletions
|
@ -120,6 +120,7 @@ PLUGINS = { # plugins with external dependencies
|
|||
'csv': {'default':True,'path':None,'inc':None,'lib':None,'lang':'C++'},
|
||||
'raster': {'default':True,'path':None,'inc':None,'lib':None,'lang':'C++'},
|
||||
'geojson': {'default':True,'path':None,'inc':None,'lib':None,'lang':'C++'},
|
||||
'large_geojson': {'default':True,'path':None,'inc':None,'lib':None,'lang':'C++'},
|
||||
'topojson':{'default':True,'path':None,'inc':None,'lib':None,'lang':'C++'},
|
||||
'python': {'default':False,'path':None,'inc':None,'lib':None,'lang':'C++'},
|
||||
}
|
||||
|
|
|
@ -38,7 +38,10 @@ struct error_handler
|
|||
Iterator err_pos, boost::spirit::info const& what) const
|
||||
{
|
||||
std::stringstream s;
|
||||
s << what << " expected but got: " << std::string(err_pos, std::min(err_pos + 16,last));
|
||||
auto start = err_pos;
|
||||
std::advance(err_pos,16);
|
||||
auto end = err_pos;
|
||||
s << what << " expected but got: " << std::string(start, end);
|
||||
throw std::runtime_error(s.str());
|
||||
}
|
||||
};
|
||||
|
|
109
include/mapnik/json/extract_bounding_box_grammar.hpp
Normal file
109
include/mapnik/json/extract_bounding_box_grammar.hpp
Normal file
|
@ -0,0 +1,109 @@
|
|||
/*****************************************************************************
|
||||
*
|
||||
* 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_EXTRACT_BOUNDING_BOX_GRAMMAR_HPP
|
||||
#define MAPNIK_JSON_EXTRACT_BOUNDING_BOX_GRAMMAR_HPP
|
||||
|
||||
// mapnik
|
||||
#include <mapnik/json/generic_json.hpp>
|
||||
#include <mapnik/json/error_handler.hpp>
|
||||
#include <mapnik/box2d.hpp>
|
||||
|
||||
// boost
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||
#pragma GCC diagnostic ignored "-Wunused-local-typedef"
|
||||
#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 = std::tuple<double,double>;
|
||||
using boxes = std::vector<std::pair<box2d<double>, std::pair<std::size_t, std::size_t>>>;
|
||||
|
||||
namespace qi = boost::spirit::qi;
|
||||
namespace standard_wide = boost::spirit::standard_wide;
|
||||
using standard_wide::space_type;
|
||||
|
||||
struct calculate_bounding_box_impl
|
||||
{
|
||||
using result_type = void;
|
||||
template <typename T0, typename T1>
|
||||
result_type operator() (T0 & bbox, T1 const& pos) const
|
||||
{
|
||||
if (pos)
|
||||
{
|
||||
double x = std::get<0>(*pos);
|
||||
double y = std::get<1>(*pos);
|
||||
if (!bbox.valid())
|
||||
{
|
||||
bbox.init(x, y, x, y); //TODO: add init(x,y) convinience method
|
||||
}
|
||||
else
|
||||
{
|
||||
bbox.expand_to_include(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct push_box_impl
|
||||
{
|
||||
using result_type = void;
|
||||
template <typename T0, typename T1, typename T2, typename T3>
|
||||
void operator() (T0 & boxes, T1 const& begin, T2 const& box, T3 const& range) const
|
||||
{
|
||||
if (box.valid()) boxes.emplace_back(box, std::make_pair(std::distance(begin, range.begin()), std::distance(range.begin(), range.end())));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Iterator, typename ErrorHandler = error_handler<Iterator> >
|
||||
struct extract_bounding_box_grammar :
|
||||
qi::grammar<Iterator, void(boxes&), space_type>
|
||||
{
|
||||
extract_bounding_box_grammar();
|
||||
// rules
|
||||
qi::rule<Iterator, void(boxes&), space_type> start;
|
||||
qi::rule<Iterator, qi::locals<Iterator>, void(boxes&), space_type> features;
|
||||
qi::rule<Iterator, qi::locals<int, box2d<double>>, void(boxes&, Iterator const&), space_type> feature;
|
||||
qi::rule<Iterator, qi::locals<box2d<double>>, box2d<double>(), space_type> coords;
|
||||
qi::rule<Iterator, boost::optional<position>(), space_type> pos;
|
||||
qi::rule<Iterator, void(box2d<double>&), space_type> ring;
|
||||
qi::rule<Iterator, void(box2d<double>&), space_type> rings;
|
||||
qi::rule<Iterator, void(box2d<double>&), space_type> rings_array;
|
||||
// generic JSON support
|
||||
json::generic_json<Iterator> json;
|
||||
// phoenix functions
|
||||
boost::phoenix::function<push_box_impl> push_box;
|
||||
boost::phoenix::function<calculate_bounding_box_impl> calculate_bounding_box;
|
||||
// error handler
|
||||
boost::phoenix::function<ErrorHandler> const error_handler;
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif // MAPNIK_JSON_EXTRACT_BOUNDING_BOX_GRAMMAR_HPP
|
135
include/mapnik/json/extract_bounding_box_grammar_impl.hpp
Normal file
135
include/mapnik/json/extract_bounding_box_grammar_impl.hpp
Normal file
|
@ -0,0 +1,135 @@
|
|||
/*****************************************************************************
|
||||
*
|
||||
* 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/json/extract_bounding_box_grammar.hpp>
|
||||
|
||||
// boost
|
||||
#include <boost/spirit/include/qi_omit.hpp>
|
||||
#include <boost/spirit/include/phoenix_object.hpp>
|
||||
#include <boost/spirit/include/phoenix_stl.hpp>
|
||||
#include <boost/spirit/include/phoenix_operator.hpp>
|
||||
#include <boost/spirit/repository/include/qi_iter_pos.hpp>
|
||||
// stl
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
namespace mapnik { namespace json {
|
||||
|
||||
namespace repo = boost::spirit::repository;
|
||||
|
||||
template <typename Iterator, typename ErrorHandler>
|
||||
extract_bounding_box_grammar<Iterator, ErrorHandler>::extract_bounding_box_grammar()
|
||||
: extract_bounding_box_grammar::base_type(start,"bounding boxes")
|
||||
{
|
||||
qi::lit_type lit;
|
||||
qi::double_type double_;
|
||||
qi::_val_type _val;
|
||||
qi::_1_type _1;
|
||||
qi::_2_type _2;
|
||||
qi::_3_type _3;
|
||||
qi::_4_type _4;
|
||||
qi::no_skip_type no_skip;
|
||||
qi::omit_type omit;
|
||||
qi::_r1_type _r1;
|
||||
qi::_r2_type _r2;
|
||||
qi::_a_type _a;
|
||||
qi::_b_type _b;
|
||||
qi::eps_type eps;
|
||||
qi::raw_type raw;
|
||||
standard_wide::char_type char_;
|
||||
boost::spirit::repository::qi::iter_pos_type iter_pos;
|
||||
using qi::fail;
|
||||
using qi::on_error;
|
||||
|
||||
start = features(_r1)
|
||||
;
|
||||
features = iter_pos[_a = _1] >> -(lit('{') >> -lit("\"type\"")
|
||||
>> lit(':') >> lit("\"FeatureCollection\"")
|
||||
>> *(lit(',') >> (json.key_value - lit("\"features\"")))
|
||||
>> lit(',') >> lit("\"features\"")
|
||||
>> lit(':'))
|
||||
>> lit('[') >> (feature(_r1,_a) % lit(',')) >> lit(']')
|
||||
;
|
||||
feature = raw[lit('{')[_a = 1] >> lit("\"type\"") >> lit(':') >> lit("\"Feature\"")
|
||||
>> *(eps(_a > 0) >> (lit('{')[_a += 1]
|
||||
|
|
||||
lit('}')[_a -=1]
|
||||
|
|
||||
coords[_b = _1]
|
||||
|
|
||||
char_))][push_box(_r1, _r2, _b, _1)]
|
||||
;
|
||||
coords = lit("\"coordinates\"") >> lit(':') >> (rings_array(_a) | rings (_a) | ring(_a) | pos[calculate_bounding_box(_a,_1)])[_val = _a]
|
||||
;
|
||||
pos = lit('[') > -(double_ > lit(',') > double_) > omit[*(lit(',') > double_)] > lit(']')
|
||||
;
|
||||
ring = lit('[') >> pos[calculate_bounding_box(_r1,_1)] % lit(',') > lit(']')
|
||||
;
|
||||
rings = lit('[') >> ring(_r1) % lit(',') > lit(']')
|
||||
;
|
||||
rings_array = lit('[') >> rings(_r1) % lit(',') > lit(']')
|
||||
;
|
||||
|
||||
// generic json types
|
||||
json.value = json.object | json.array | json.string_ | json.number
|
||||
;
|
||||
json.pairs = json.key_value % lit(',')
|
||||
;
|
||||
json.key_value = (json.string_ >> lit(':') >> json.value)
|
||||
;
|
||||
json.object = lit('{') >> *json.pairs >> lit('}')
|
||||
;
|
||||
json.array = lit('[')
|
||||
>> json.value >> *(lit(',') >> json.value)
|
||||
>> lit(']')
|
||||
;
|
||||
json.number = json.strict_double
|
||||
| json.int__
|
||||
| lit("true")
|
||||
| lit("false")
|
||||
| lit("null")
|
||||
;
|
||||
json.unesc_char.add
|
||||
("\\\"", '\"') // quotation mark
|
||||
("\\\\", '\\') // reverse solidus
|
||||
("\\/", '/') // solidus
|
||||
("\\b", '\b') // backspace
|
||||
("\\f", '\f') // formfeed
|
||||
("\\n", '\n') // newline
|
||||
("\\r", '\r') // carrige return
|
||||
("\\t", '\t') // tab
|
||||
;
|
||||
json.string_ %= lit('"') >> no_skip[*(json.unesc_char | "\\u" >> json.hex4 | (char_ - lit('"')))] >> lit('"')
|
||||
;
|
||||
|
||||
coords.name("Coordinates");
|
||||
pos.name("Position");
|
||||
ring.name("Ring");
|
||||
rings.name("Rings");
|
||||
rings_array.name("Rings array");
|
||||
|
||||
// error handler
|
||||
on_error<fail>(coords, error_handler(_1, _2, _3, _4));
|
||||
}
|
||||
|
||||
}}
|
|
@ -17,70 +17,25 @@
|
|||
* 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_POLYGON_CLIPPER_HPP
|
||||
#define MAPNIK_POLYGON_CLIPPER_HPP
|
||||
|
||||
// stl
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <deque>
|
||||
|
||||
// mapnik
|
||||
#include <mapnik/box2d.hpp>
|
||||
#include <mapnik/debug.hpp>
|
||||
#include <mapnik/geometry.hpp>
|
||||
#include <mapnik/util/boost_geometry_adapters.hpp>
|
||||
|
||||
// boost
|
||||
#include <boost/geometry.hpp>
|
||||
#include <boost/geometry/geometries/point_xy.hpp>
|
||||
#include <boost/geometry/geometries/box.hpp>
|
||||
#include <boost/geometry/geometries/polygon.hpp>
|
||||
#include <boost/geometry/geometries/register/point.hpp>
|
||||
|
||||
BOOST_GEOMETRY_REGISTER_POINT_2D(mapnik::coord2d, double, cs::cartesian, x, y)
|
||||
|
||||
// register mapnik::box2d<double>
|
||||
namespace boost { namespace geometry { namespace traits
|
||||
{
|
||||
|
||||
template<> struct tag<mapnik::box2d<double> > { using type = box_tag; };
|
||||
|
||||
template<> struct point_type<mapnik::box2d<double> > { using type = mapnik::coord2d; };
|
||||
|
||||
template <>
|
||||
struct indexed_access<mapnik::box2d<double>, min_corner, 0>
|
||||
{
|
||||
using ct = coordinate_type<mapnik::coord2d>::type;
|
||||
static inline ct get(mapnik::box2d<double> const& b) { return b.minx();}
|
||||
static inline void set(mapnik::box2d<double> &b, ct const& value) { b.set_minx(value); }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct indexed_access<mapnik::box2d<double>, min_corner, 1>
|
||||
{
|
||||
using ct = coordinate_type<mapnik::coord2d>::type;
|
||||
static inline ct get(mapnik::box2d<double> const& b) { return b.miny();}
|
||||
static inline void set(mapnik::box2d<double> &b, ct const& value) { b.set_miny(value); }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct indexed_access<mapnik::box2d<double>, max_corner, 0>
|
||||
{
|
||||
using ct = coordinate_type<mapnik::coord2d>::type;
|
||||
static inline ct get(mapnik::box2d<double> const& b) { return b.maxx();}
|
||||
static inline void set(mapnik::box2d<double> &b, ct const& value) { b.set_maxx(value); }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct indexed_access<mapnik::box2d<double>, max_corner, 1>
|
||||
{
|
||||
using ct = coordinate_type<mapnik::coord2d>::type;
|
||||
static inline ct get(mapnik::box2d<double> const& b) { return b.maxy();}
|
||||
static inline void set(mapnik::box2d<double> &b , ct const& value) { b.set_maxy(value); }
|
||||
};
|
||||
|
||||
}}}
|
||||
|
||||
namespace mapnik {
|
||||
|
||||
|
|
77
include/mapnik/util/boost_geometry_adapters.hpp
Normal file
77
include/mapnik/util/boost_geometry_adapters.hpp
Normal file
|
@ -0,0 +1,77 @@
|
|||
/*****************************************************************************
|
||||
*
|
||||
* 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 BOOST_GEOMETRY_ADAPTERS_HPP
|
||||
#define BOOST_GEOMETRY_ADAPTERS_HPP
|
||||
|
||||
// boost
|
||||
#include <boost/geometry.hpp>
|
||||
#include <boost/geometry/geometries/box.hpp>
|
||||
#include <boost/geometry/geometries/polygon.hpp>
|
||||
#include <boost/geometry/geometries/register/point.hpp>
|
||||
|
||||
BOOST_GEOMETRY_REGISTER_POINT_2D(mapnik::coord2d, double, cs::cartesian, x, y)
|
||||
|
||||
// register mapnik::box2d<double>
|
||||
namespace boost { namespace geometry { namespace traits
|
||||
{
|
||||
|
||||
template<> struct tag<mapnik::box2d<double> > { using type = box_tag; };
|
||||
|
||||
template<> struct point_type<mapnik::box2d<double> > { using type = mapnik::coord2d; };
|
||||
|
||||
template <>
|
||||
struct indexed_access<mapnik::box2d<double>, min_corner, 0>
|
||||
{
|
||||
using ct = coordinate_type<mapnik::coord2d>::type;
|
||||
static inline ct get(mapnik::box2d<double> const& b) { return b.minx();}
|
||||
static inline void set(mapnik::box2d<double> &b, ct const& value) { b.set_minx(value); }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct indexed_access<mapnik::box2d<double>, min_corner, 1>
|
||||
{
|
||||
using ct = coordinate_type<mapnik::coord2d>::type;
|
||||
static inline ct get(mapnik::box2d<double> const& b) { return b.miny();}
|
||||
static inline void set(mapnik::box2d<double> &b, ct const& value) { b.set_miny(value); }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct indexed_access<mapnik::box2d<double>, max_corner, 0>
|
||||
{
|
||||
using ct = coordinate_type<mapnik::coord2d>::type;
|
||||
static inline ct get(mapnik::box2d<double> const& b) { return b.maxx();}
|
||||
static inline void set(mapnik::box2d<double> &b, ct const& value) { b.set_maxx(value); }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct indexed_access<mapnik::box2d<double>, max_corner, 1>
|
||||
{
|
||||
using ct = coordinate_type<mapnik::coord2d>::type;
|
||||
static inline ct get(mapnik::box2d<double> const& b) { return b.maxy();}
|
||||
static inline void set(mapnik::box2d<double> &b , ct const& value) { b.set_maxy(value); }
|
||||
};
|
||||
|
||||
}}}
|
||||
|
||||
|
||||
#endif //BOOST_GEOMETRY_ADAPTERS_HPP
|
|
@ -31,6 +31,7 @@ plugin_sources = Split(
|
|||
"""
|
||||
%(PLUGIN_NAME)s_datasource.cpp
|
||||
%(PLUGIN_NAME)s_featureset.cpp
|
||||
large_%(PLUGIN_NAME)s_featureset.cpp
|
||||
""" % locals()
|
||||
)
|
||||
|
||||
|
|
|
@ -22,18 +22,20 @@
|
|||
|
||||
#include "geojson_datasource.hpp"
|
||||
#include "geojson_featureset.hpp"
|
||||
|
||||
#include "large_geojson_featureset.hpp"
|
||||
#include <fstream>
|
||||
#include <algorithm>
|
||||
|
||||
// boost
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/spirit/include/qi.hpp>
|
||||
|
||||
// mapnik
|
||||
#include <mapnik/boolean.hpp>
|
||||
#include <mapnik/unicode.hpp>
|
||||
#include <mapnik/utils.hpp>
|
||||
#include <mapnik/feature.hpp>
|
||||
#include <mapnik/feature_factory.hpp>
|
||||
#include <mapnik/feature_kv_iterator.hpp>
|
||||
#include <mapnik/value_types.hpp>
|
||||
#include <mapnik/box2d.hpp>
|
||||
|
@ -45,8 +47,8 @@
|
|||
#include <mapnik/util/file_io.hpp>
|
||||
#include <mapnik/make_unique.hpp>
|
||||
#include <mapnik/json/feature_collection_grammar.hpp>
|
||||
|
||||
#include <boost/spirit/include/qi.hpp>
|
||||
#include <mapnik/json/extract_bounding_box_grammar_impl.hpp>
|
||||
#include <mapnik/util/boost_geometry_adapters.hpp> // boost.geometry - register box2d<double>
|
||||
|
||||
using mapnik::datasource;
|
||||
using mapnik::parameters;
|
||||
|
@ -95,7 +97,7 @@ geojson_datasource::geojson_datasource(parameters const& params)
|
|||
: datasource(params),
|
||||
type_(datasource::Vector),
|
||||
desc_(geojson_datasource::name(),
|
||||
*params.get<std::string>("encoding","utf-8")),
|
||||
*params.get<std::string>("encoding","utf-8")),
|
||||
filename_(),
|
||||
inline_string_(),
|
||||
extent_(),
|
||||
|
@ -124,15 +126,25 @@ geojson_datasource::geojson_datasource(parameters const& params)
|
|||
}
|
||||
else
|
||||
{
|
||||
|
||||
mapnik::util::file file(filename_);
|
||||
if (!file.open())
|
||||
{
|
||||
throw mapnik::datasource_exception("GeoJSON Plugin: could not open: '" + filename_ + "'");
|
||||
}
|
||||
|
||||
std::string file_buffer;
|
||||
file_buffer.resize(file.size());
|
||||
std::fread(&file_buffer[0], file.size(), 1, file.get());
|
||||
parse_geojson(file_buffer);
|
||||
cache_features_ = *params.get<mapnik::boolean_type>("cache_features", true);
|
||||
if (cache_features_)
|
||||
{
|
||||
parse_geojson(file_buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
initialise_index(file_buffer.begin(), file_buffer.end());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -142,6 +154,70 @@ const mapnik::transcoder tr("utf8");
|
|||
const mapnik::json::feature_collection_grammar<base_iterator_type,mapnik::feature_impl> fc_grammar(tr);
|
||||
}
|
||||
|
||||
template <typename Iterator>
|
||||
void geojson_datasource::initialise_index(Iterator start, Iterator end)
|
||||
{
|
||||
mapnik::json::boxes boxes;
|
||||
mapnik::json::extract_bounding_box_grammar<Iterator> bbox_grammar;
|
||||
boost::spirit::standard_wide::space_type space;
|
||||
if (!boost::spirit::qi::phrase_parse(start, end, (bbox_grammar)(boost::phoenix::ref(boxes)) , space))
|
||||
{
|
||||
throw mapnik::datasource_exception("GeoJSON Plugin: could not parse: '" + filename_ + "'");
|
||||
}
|
||||
#if BOOST_VERSION >= 105600
|
||||
tree_ = std::make_unique<spatial_index_type>(boxes);
|
||||
#else
|
||||
tree_ = std::make_unique<spatial_index_type>(16, 4);
|
||||
#endif
|
||||
for (auto const& item : boxes)
|
||||
{
|
||||
auto const& box = std::get<0>(item);
|
||||
auto const& geometry_index = std::get<1>(item);
|
||||
#if BOOST_VERSION < 105600
|
||||
tree_->insert(box, geometry_index);
|
||||
#endif
|
||||
if (!extent_.valid())
|
||||
{
|
||||
extent_ = box;
|
||||
// parse first feature to extract attributes schema.
|
||||
// NOTE: this doesn't yield correct answer for geoJSON in general, just an indication
|
||||
mapnik::util::file file(filename_);
|
||||
if (!file.open())
|
||||
{
|
||||
throw mapnik::datasource_exception("GeoJSON Plugin: could not open: '" + filename_ + "'");
|
||||
}
|
||||
|
||||
std::fseek(file.get(), geometry_index.first, SEEK_SET);
|
||||
std::vector<char> json;
|
||||
json.resize(geometry_index.second);
|
||||
std::fread(json.data(), geometry_index.second, 1, file.get());
|
||||
|
||||
using chr_iterator_type = std::vector<char>::const_iterator;
|
||||
chr_iterator_type start = json.begin();
|
||||
chr_iterator_type end = json.end();
|
||||
|
||||
mapnik::context_ptr ctx = std::make_shared<mapnik::context_type>();
|
||||
mapnik::feature_ptr feature(mapnik::feature_factory::create(ctx,1));
|
||||
using namespace boost::spirit;
|
||||
standard_wide::space_type space;
|
||||
if (!qi::phrase_parse(start, end, (fc_grammar.feature_g)(boost::phoenix::ref(*feature)), space))
|
||||
{
|
||||
throw std::runtime_error("Failed to parse geojson feature");
|
||||
}
|
||||
for ( auto const& kv : *feature)
|
||||
{
|
||||
desc_.add_descriptor(mapnik::attribute_descriptor(std::get<0>(kv),
|
||||
mapnik::util::apply_visitor(attr_value_converter(),
|
||||
std::get<1>(kv))));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
extent_.expand_to_include(box);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void geojson_datasource::parse_geojson(T const& buffer)
|
||||
{
|
||||
|
@ -158,7 +234,7 @@ void geojson_datasource::parse_geojson(T const& buffer)
|
|||
}
|
||||
|
||||
#if BOOST_VERSION >= 105600
|
||||
using values_container = std::vector< std::pair<box_type, std::size_t> >;
|
||||
using values_container = std::vector< std::pair<box_type, std::pair<std::size_t, std::size_t>>>;
|
||||
values_container values;
|
||||
values.reserve(features_.size());
|
||||
#else
|
||||
|
@ -187,9 +263,9 @@ void geojson_datasource::parse_geojson(T const& buffer)
|
|||
}
|
||||
}
|
||||
#if BOOST_VERSION >= 105600
|
||||
values.emplace_back(box_type(point_type(box.minx(),box.miny()),point_type(box.maxx(),box.maxy())), geometry_index);
|
||||
values.emplace_back(box, std::make_pair(geometry_index,0));
|
||||
#else
|
||||
tree_->insert(box_type(point_type(box.minx(),box.miny()),point_type(box.maxx(),box.maxy())),geometry_index);
|
||||
tree_->insert(box, std::make_pair(geometry_index));
|
||||
#endif
|
||||
++geometry_index;
|
||||
}
|
||||
|
@ -248,21 +324,36 @@ mapnik::layer_descriptor geojson_datasource::get_descriptor() const
|
|||
mapnik::featureset_ptr geojson_datasource::features(mapnik::query const& q) const
|
||||
{
|
||||
// if the query box intersects our world extent then query for features
|
||||
mapnik::box2d<double> const& b = q.get_bbox();
|
||||
if (extent_.intersects(b))
|
||||
mapnik::box2d<double> const& box = q.get_bbox();
|
||||
if (extent_.intersects(box))
|
||||
{
|
||||
box_type box(point_type(b.minx(),b.miny()),point_type(b.maxx(),b.maxy()));
|
||||
#if BOOST_VERSION >= 105600
|
||||
geojson_featureset::array_type index_array;
|
||||
if (tree_)
|
||||
{
|
||||
tree_->query(boost::geometry::index::intersects(box),std::back_inserter(index_array));
|
||||
return std::make_shared<geojson_featureset>(features_, std::move(index_array));
|
||||
|
||||
if (cache_features_)
|
||||
{
|
||||
return std::make_shared<geojson_featureset>(features_, std::move(index_array));
|
||||
}
|
||||
else
|
||||
{
|
||||
std::sort(index_array.begin(),index_array.end(),
|
||||
[] (item_type const& item0, item_type const& item1)
|
||||
{
|
||||
return item0.second.first < item1.second.first;
|
||||
});
|
||||
return std::make_shared<large_geojson_featureset>(filename_, std::move(index_array));
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (tree_)
|
||||
{
|
||||
return std::make_shared<geojson_featureset>(features_, tree_->find(box));
|
||||
if (cache_features_)
|
||||
return std::make_shared<geojson_featureset>(features_, tree_->find(box));
|
||||
else
|
||||
return std::make_shared<large_geojson_featureset>(features_, tree_->find(box));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -88,15 +88,13 @@ struct options_type<geojson_linear<Max,Min> >
|
|||
class geojson_datasource : public mapnik::datasource
|
||||
{
|
||||
public:
|
||||
using point_type = boost::geometry::model::point<double, 2, boost::geometry::cs::cartesian>;
|
||||
using box_type = boost::geometry::model::box<point_type>;
|
||||
|
||||
using box_type = mapnik::box2d<double>;
|
||||
#if BOOST_VERSION >= 105600
|
||||
using item_type = std::pair<box_type,std::size_t>;
|
||||
using item_type = std::pair<box_type, std::pair<std::size_t, std::size_t> >;
|
||||
using spatial_index_type = boost::geometry::index::rtree<item_type,geojson_linear<16,4> >;
|
||||
#else
|
||||
using item_type = std::size_t;
|
||||
using spatial_index_type = boost::geometry::index::rtree<box_type,std::size_t>;
|
||||
using item_type = std::pair<std::size_t, std::size_t>;
|
||||
using spatial_index_type = boost::geometry::index::rtree<box_type,item_type>;
|
||||
#endif
|
||||
|
||||
// constructor
|
||||
|
@ -109,8 +107,11 @@ public:
|
|||
mapnik::box2d<double> envelope() const;
|
||||
mapnik::layer_descriptor get_descriptor() const;
|
||||
boost::optional<mapnik::datasource::geometry_t> get_geometry_type() const;
|
||||
//
|
||||
template <typename T>
|
||||
void parse_geojson(T const& buffer);
|
||||
template <typename Iterator>
|
||||
void initialise_index(Iterator start, Iterator end);
|
||||
private:
|
||||
mapnik::datasource::datasource_t type_;
|
||||
mapnik::layer_descriptor desc_;
|
||||
|
@ -119,6 +120,7 @@ private:
|
|||
mapnik::box2d<double> extent_;
|
||||
std::vector<mapnik::feature_ptr> features_;
|
||||
std::unique_ptr<spatial_index_type> tree_;
|
||||
bool cache_features_ = true;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -44,9 +44,9 @@ mapnik::feature_ptr geojson_featureset::next()
|
|||
{
|
||||
#if BOOST_VERSION >= 105600
|
||||
geojson_datasource::item_type const& item = *index_itr_++;
|
||||
std::size_t index = item.second;
|
||||
std::size_t index = item.second.first;
|
||||
#else
|
||||
std::size_t index = *index_itr_++;
|
||||
std::size_t index = (*index_itr_++).second;
|
||||
#endif
|
||||
if ( index < features_.size())
|
||||
{
|
||||
|
|
93
plugins/input/geojson/large_geojson_featureset.cpp
Normal file
93
plugins/input/geojson/large_geojson_featureset.cpp
Normal file
|
@ -0,0 +1,93 @@
|
|||
/*****************************************************************************
|
||||
*
|
||||
* This file is part of Mapnik (c++ mapping toolkit)
|
||||
*
|
||||
* Copyright (C) 2014 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/feature.hpp>
|
||||
#include <mapnik/feature_factory.hpp>
|
||||
#include <mapnik/json/geometry_grammar_impl.hpp>
|
||||
#include <mapnik/json/feature_grammar_impl.hpp>
|
||||
#include <mapnik/utils.hpp>
|
||||
// stl
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
|
||||
#include "large_geojson_featureset.hpp"
|
||||
|
||||
//namespace {
|
||||
//using base_iterator_type = std::string::const_iterator;
|
||||
//const mapnik::transcoder tr("utf8");
|
||||
//const mapnik::json::feature_collection_grammar<base_iterator_type,mapnik::feature_impl> fc_grammar(tr);
|
||||
//}
|
||||
|
||||
large_geojson_featureset::large_geojson_featureset(std::string const& filename,
|
||||
array_type && index_array)
|
||||
:
|
||||
#ifdef _WINDOWS
|
||||
file_(_wfopen(mapnik::utf8_to_utf16(filename).c_str(), L"rb"), std::fclose),
|
||||
#else
|
||||
file_(std::fopen(filename.c_str(),"rb"), std::fclose),
|
||||
#endif
|
||||
index_array_(std::move(index_array)),
|
||||
index_itr_(index_array_.begin()),
|
||||
index_end_(index_array_.end()),
|
||||
ctx_(std::make_shared<mapnik::context_type>())
|
||||
{
|
||||
if (!file_) throw std::runtime_error("Can't open " + filename);
|
||||
}
|
||||
|
||||
large_geojson_featureset::~large_geojson_featureset() {}
|
||||
|
||||
mapnik::feature_ptr large_geojson_featureset::next()
|
||||
{
|
||||
if (index_itr_ != index_end_)
|
||||
{
|
||||
#if BOOST_VERSION >= 105600
|
||||
geojson_datasource::item_type const& item = *index_itr_++;
|
||||
std::size_t file_offset = item.second.first;
|
||||
std::size_t size = item.second.second;
|
||||
#else
|
||||
std::pair<std::size_t,std::size_t> index = *index_itr_++;
|
||||
std::size_t file_offset = index.first;
|
||||
std::size_t size = index.second;
|
||||
#endif
|
||||
std::fseek(file_.get(), file_offset, SEEK_SET);
|
||||
std::vector<char> json;
|
||||
json.resize(size);
|
||||
std::fread(json.data(), size, 1, file_.get());
|
||||
using chr_iterator_type = std::vector<char>::const_iterator;
|
||||
chr_iterator_type start = json.begin();
|
||||
chr_iterator_type end = json.end();
|
||||
|
||||
static const mapnik::transcoder tr("utf8");
|
||||
static const mapnik::json::feature_grammar<chr_iterator_type,mapnik::feature_impl> grammar(tr);
|
||||
using namespace boost::spirit;
|
||||
standard_wide::space_type space;
|
||||
mapnik::feature_ptr feature(mapnik::feature_factory::create(ctx_,1));
|
||||
if (!qi::phrase_parse(start, end, (grammar)(boost::phoenix::ref(*feature)), space))
|
||||
{
|
||||
throw std::runtime_error("Failed to parse geojson feature");
|
||||
}
|
||||
return feature;
|
||||
}
|
||||
return mapnik::feature_ptr();
|
||||
}
|
54
plugins/input/geojson/large_geojson_featureset.hpp
Normal file
54
plugins/input/geojson/large_geojson_featureset.hpp
Normal file
|
@ -0,0 +1,54 @@
|
|||
/*****************************************************************************
|
||||
*
|
||||
* This file is part of Mapnik (c++ mapping toolkit)
|
||||
*
|
||||
* Copyright (C) 2014 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 LARGE_GEOJSON_FEATURESET_HPP
|
||||
#define LARGE_GEOJSON_FEATURESET_HPP
|
||||
|
||||
#include <mapnik/feature.hpp>
|
||||
#include "geojson_datasource.hpp"
|
||||
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
#include <fstream>
|
||||
#include <cstdio>
|
||||
|
||||
class large_geojson_featureset : public mapnik::Featureset
|
||||
{
|
||||
public:
|
||||
using array_type = std::deque<geojson_datasource::item_type>;
|
||||
using file_ptr = std::unique_ptr<std::FILE, int (*)(std::FILE *)>;
|
||||
|
||||
large_geojson_featureset(std::string const& filename,
|
||||
array_type && index_array);
|
||||
virtual ~large_geojson_featureset();
|
||||
mapnik::feature_ptr next();
|
||||
|
||||
private:
|
||||
file_ptr file_;
|
||||
|
||||
const array_type index_array_;
|
||||
array_type::const_iterator index_itr_;
|
||||
array_type::const_iterator index_end_;
|
||||
mapnik::context_ptr ctx_;
|
||||
};
|
||||
|
||||
#endif // LARGE_GEOJSON_FEATURESET_HPP
|
Loading…
Add table
Reference in a new issue