Merge branch 'large-geojson'

This commit is contained in:
artemp 2015-01-26 11:39:35 +01:00
commit c99cb19643
12 changed files with 593 additions and 72 deletions

View file

@ -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++'},
}

View file

@ -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());
}
};

View 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

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

View file

@ -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 {

View 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

View file

@ -31,6 +31,7 @@ plugin_sources = Split(
"""
%(PLUGIN_NAME)s_datasource.cpp
%(PLUGIN_NAME)s_featureset.cpp
large_%(PLUGIN_NAME)s_featureset.cpp
""" % locals()
)

View file

@ -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
}

View file

@ -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;
};

View file

@ -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())
{

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

View 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