diff --git a/SConstruct b/SConstruct index d640afe15..0618f5792 100644 --- a/SConstruct +++ b/SConstruct @@ -116,6 +116,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++'}, + 'geobuf': {'default':True,'path':None,'inc':None,'lib':None,'lang':'C++'}, 'topojson':{'default':True,'path':None,'inc':None,'lib':None,'lang':'C++'} } diff --git a/plugins/input/geobuf/build.py b/plugins/input/geobuf/build.py new file mode 100644 index 000000000..5e1eba2db --- /dev/null +++ b/plugins/input/geobuf/build.py @@ -0,0 +1,64 @@ +# +# This file is part of Mapnik (c++ mapping toolkit) +# +# Copyright (C) 2013 Artem Pavlenko +# +# Mapnik 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 +# +# + +Import ('env') + +Import ('plugin_base') + +PLUGIN_NAME = 'geobuf' + +plugin_env = plugin_base.Clone() + +plugin_sources = Split( + """ + %(PLUGIN_NAME)s_datasource.cpp + %(PLUGIN_NAME)s_featureset.cpp + """ % locals() +) + +# Link Library to Dependencies +libraries = [] +libraries.append(env['ICU_LIB_NAME']) +libraries.append('boost_system%s' % env['BOOST_APPEND']) +libraries.append('mapnik-json') + +if env['PLUGIN_LINKING'] == 'shared': + libraries.append(env['MAPNIK_NAME']) + + TARGET = plugin_env.SharedLibrary('../%s' % PLUGIN_NAME, + SHLIBPREFIX='', + SHLIBSUFFIX='.input', + source=plugin_sources, + LIBS=libraries) + + # if the plugin links to libmapnik ensure it is built first + Depends(TARGET, env.subst('../../../src/%s' % env['MAPNIK_LIB_NAME'])) + + if 'uninstall' not in COMMAND_LINE_TARGETS: + env.Install(env['MAPNIK_INPUT_PLUGINS_DEST'], TARGET) + env.Alias('install', env['MAPNIK_INPUT_PLUGINS_DEST']) + +plugin_obj = { + 'LIBS': libraries, + 'SOURCES': plugin_sources, +} + +Return('plugin_obj') diff --git a/plugins/input/geobuf/geobuf.hpp b/plugins/input/geobuf/geobuf.hpp new file mode 100644 index 000000000..f3b8ee44f --- /dev/null +++ b/plugins/input/geobuf/geobuf.hpp @@ -0,0 +1,583 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2017 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_UTIL_GEOBUF_HPP +#define MAPNIK_UTIL_GEOBUF_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "pbf.hpp" + +namespace mapnik { namespace util { + +enum geometry_type_e +{ + Unknown = -1, + Point = 0, + MultiPoint = 1, + LineString = 2, + MultiLineString = 3, + Polygon = 4, + MultiPolygon = 5, + GeometryCollection = 6 +}; + +namespace detail { +struct value_visitor +{ + value_visitor(mapnik::feature_impl & feature, mapnik::transcoder const& tr, std::string const& name) + : feature_(feature), + tr_(tr), + name_(name) {} + + void operator() (std::string const& val) // unicode + { + feature_.put_new(name_, tr_.transcode(val.c_str())); + } + + template + void operator() (T val) + { + feature_.put_new(name_, val); + } + + mapnik::feature_impl & feature_; + mapnik::transcoder const& tr_; + std::string const& name_; +}; +} + +template +struct geobuf : mapnik::util::noncopyable +{ + using value_type = mapnik::util::variant; + unsigned dim = 2; + double precision = std::pow(10,6); + bool is_topo = false; + bool transformed = false; + std::size_t lengths = 0; + std::vector keys_; + std::vector values_; + mbgl::pbf pbf_; + FeatureCallback & callback_; + mapnik::context_ptr ctx_; + const std::unique_ptr tr_; +public: + //ctor + geobuf (unsigned char const* buf, std::size_t size, FeatureCallback & callback) + : pbf_(buf, size), + callback_(callback), + ctx_(std::make_shared()), + tr_(new mapnik::transcoder("utf8")) {} + + void read() + { + while (pbf_.next()) + { + switch (pbf_.tag) + { + case 1: // keys + { + keys_.push_back(pbf_.string()); + break; + } + case 2: + { + dim = pbf_.varint(); + break; + } + case 3: + { + precision = std::pow(10,pbf_.varint()); + break; + } + case 4: + { + auto feature_collection = pbf_.message(); + read_feature_collection(feature_collection); + break; + } + case 6: + { + pbf_.message(); + MAPNIK_LOG_DEBUG(geobuf) << "standalone Geometry is not supported"; + break; + } + case 7: + { + pbf_.message(); + MAPNIK_LOG_DEBUG(geobuf) << "Topology is not supported"; + break; + } + default: + MAPNIK_LOG_DEBUG(geobuf) << "FIXME tag=" << pbf_.tag; + break; + } + } + } + +private: + + double transform(std::int64_t input) + { + return (transformed) ? (static_cast(input)) : (input/precision); + } + + template + void read_value(T & pbf) + { + while (pbf.next()) + { + switch (pbf.tag) + { + case 1: + { + values_.emplace_back(pbf.string()); + break; + } + case 2: + { + values_.emplace_back(pbf.float64()); + break; + } + case 3: + { + values_.emplace_back(static_cast(pbf.varint())); + break; + } + case 4: + { + values_.emplace_back(-static_cast(pbf.varint())); + break; + } + case 5: + { + values_.emplace_back(pbf.boolean()); + break; + } + case 6: + { + values_.emplace_back(pbf.string()); // JSON value + break; + } + default: + break; + } + } + } + + template + void read_props(T & pbf, Feature & feature) + { + std::uint8_t const* end = pbf.data + pbf.varint(); + while (pbf.data < end) + { + auto key_index = pbf.varint(); + auto value_index = pbf.varint(); + assert(key_index < keys_.size()); + assert(value_index < values_.size()); + std::string const& name = keys_[key_index]; + mapnik::util::apply_visitor(detail::value_visitor(feature, *tr_, name), values_[value_index]); + } + values_.clear(); + } + + template + void read_feature (T & pbf) + { + auto feature = feature_factory::create(ctx_,1); + while (pbf.next()) + { + switch (pbf.tag) + { + case 1: + { + auto message = pbf.message(); + auto geom = read_geometry(message); + feature->set_geometry(std::move(geom)); + break; + } + case 11: + { + auto feature_id = pbf.string(); + break; + } + case 12: + { + feature->set_id(pbf.template svarint()); + break; + } + case 13: + { + auto message = pbf.message(); + read_value(message); + break; + } + case 14: + { + // feature props + read_props(pbf, *feature); + break; + } + case 15: + { + // generic props + read_props(pbf, *feature); + break; + } + default: + MAPNIK_LOG_DEBUG(geobuf) << "FAIL tag=" << pbf.tag; + break; + } + } + callback_(feature); + } + + template + void read_feature_collection(T & pbf) + { + while (pbf.next()) + { + switch (pbf.tag) + { + case 1: + { + auto message = pbf.message(); + read_feature(message); + break; + } + case 13: + { + auto message = pbf.message(); + read_value(message); + break; + } + case 15: + { + pbf.skipBytes(pbf.varint()); + break; + } + default: + break; + } + } + } + + template + mapnik::geometry::point read_point(T & pbf) + { + auto size = pbf.varint(); + std::uint8_t const* end = pbf.data + size; + std::size_t count = 0; + double x, y; + while (pbf.data < end) + { + auto p = pbf.svarint(); + auto index = count % dim; + if ( index == 0) + { + x = transform(p); + } + else if (index == 1) + { + y = transform(p); + } + ++count; + } + return mapnik::geometry::point(x, y); + } + + template + mapnik::geometry::geometry read_coords(T & pbf, geometry_type_e type, + boost::optional> const& lengths) + { + mapnik::geometry::geometry geom = mapnik::geometry::geometry_empty(); + switch (type) + { + case Point: + { + geom = read_point(pbf); + break; + } + case Polygon: + { + geom = read_polygon(pbf, lengths); + break; + } + case LineString: + { + geom = read_line_string(pbf); + break; + } + case MultiPoint: + { + geom = read_multi_point(pbf); + break; + } + case MultiLineString: + { + geom = read_multi_linestring(pbf, lengths); + } + case MultiPolygon: + { + geom = read_multi_polygon(pbf, lengths); + break; + } + case GeometryCollection: + { + throw std::runtime_error("GeometryCollection is not supported"); + } + default: + { + break; + } + } + return geom; + } + + template + std::vector read_lengths(T & pbf) + { + std::vector lengths; + auto size = pbf.varint(); + std::uint8_t const* end = pbf.data + size; + while (pbf.data < end) + { + lengths.push_back(pbf.varint()); + } + return lengths; + } + + template + void read_linear_ring(T & pbf, int len, std::size_t size, Ring & ring, bool close = false) + { + int i = 0; + double x = 0.0; + double y = 0.0; + ring.reserve(close ? size + 1 : size); + std::uint8_t const* end = pbf.data + size; + while ( (len > 0) ? i < len : pbf.data < end) + { + for (unsigned d = 0; d < dim; ++d) + { + std::int64_t delta = pbf.template svarint(); + if (d == 0) x += delta; + else if (d == 1) y += delta; + } + ring.emplace_back(transform(x), transform(y)); + ++i; + } + if (close && !ring.empty()) + { + ring.emplace_back(ring.front()); + } + + } + + template + mapnik::geometry::multi_point read_multi_point(T & pbf) + { + mapnik::geometry::multi_point multi_point; + double x = 0.0; + double y = 0.0; + auto size = pbf.varint(); + std::uint8_t const* end = pbf.data + size; + while (pbf.data < end) + { + for (unsigned d = 0; d < dim; ++d) + { + if (d == 0) x += pbf.svarint(); + else if (d == 1) y += pbf.svarint(); + } + mapnik::geometry::point pt(transform(x), transform(y)); + multi_point.push_back(std::move(pt)); + } + return multi_point; + } + + template + mapnik::geometry::line_string read_line_string(T & pbf) + { + mapnik::geometry::line_string line; + auto size = pbf.varint(); + read_linear_ring(pbf, 0, size, line); + return line; + + } + + template + mapnik::geometry::multi_line_string read_multi_linestring(T & pbf, boost::optional> const& lengths) + { + mapnik::geometry::multi_line_string multi_line; + multi_line.reserve(!lengths ? 1 : lengths->size()); + auto size = pbf.varint(); + if (!lengths) + { + mapnik::geometry::line_string line; + read_linear_ring(pbf, 0, size, line); + multi_line.push_back(std::move(line)); + } + else + { + for (auto len : *lengths) + { + mapnik::geometry::line_string line; + read_linear_ring(pbf, len, size, line); + multi_line.push_back(std::move(line)); + } + } + return multi_line; + } + + template + mapnik::geometry::polygon read_polygon(T & pbf, boost::optional> const& lengths) + { + mapnik::geometry::polygon poly; + poly.reserve(!lengths ? 1 : lengths->size()); + auto size = pbf.varint(); + + if (!lengths) + { + mapnik::geometry::linear_ring ring; + read_linear_ring(pbf, 0, size, ring, true); + poly.push_back(std::move(ring)); + } + else + { + for (auto len : *lengths) + { + mapnik::geometry::linear_ring ring; + read_linear_ring(pbf, len, size, ring, true); + poly.push_back(std::move(ring)); + } + } + return poly; + } + + template + mapnik::geometry::multi_polygon read_multi_polygon(T & pbf, boost::optional> const& lengths) + { + mapnik::geometry::multi_polygon multi_poly; +#if 0 // FIXME + auto size = pbf.varint(); + if (!lengths) + { + //std::unique_ptr poly(new geometry_type(mapnik::geometry_type::types::Polygon)); + mapnik::geometry::polygon poly; + read_linear_ring(pbf, 0, size, poly, true); + paths.push_back(poly.release()); + } + else if ((*lengths).size() > 0) + { + int j = 1; + for (int i = 0; i < (*lengths)[0]; ++i) + { + //std::unique_ptr poly(new geometry_type(mapnik::geometry_type::types::Polygon)); + mapnik::geometry::polygon poly; + for (int k = 0; k < (*lengths)[j]; ++k) + { + mapnik::geometry::linear_ring ring; + read_linear_ring(pbf, (*lengths)[j + 1 + k], size, ring, true); + poly.push_back(std::move(ring)); + } + multi_poly.push_back(std::move(poly)); + j += (*lengths)[j] + 1; + } + } +#endif + return multi_poly; + } + + template + mapnik::geometry::geometry read_geometry(T & pbf) + { + mapnik::geometry::geometry geom = mapnik::geometry::geometry_empty(); + geometry_type_e type = Unknown; + boost::optional> lengths; + while (pbf.next()) + { + switch (pbf.tag) + { + case 1: // type + { + type = static_cast(pbf.varint()); + break; + } + case 2: + { + lengths = std::move(read_lengths(pbf)); + break; + } + case 3: + { + geom = std::move(read_coords(pbf, type, lengths)); + break; + } + case 4: + { + MAPNIK_LOG_DEBUG(geobuf) << "fixme read geometry=?"; + break; + } + case 11: + { + auto geom_id = pbf.string(); + MAPNIK_LOG_DEBUG(geobuf) << "geometry id's are not supported geom_id=" << geom_id ; + break; + } + case 12: + { + auto geom_id = pbf.template varint(); + MAPNIK_LOG_DEBUG(geobuf) << "geometry id's are not supported geom_id=" << geom_id ; + break; + } + case 13: + { + auto value = pbf.message(); + read_value(value); + break; + } + case 14: + case 15: + { + pbf.skipBytes(pbf.varint()); + break; + } + default: + break; + } + } + return geom; + } +}; + +}} + +#endif // MAPNIK_UTIL_GEOBUF_HPP diff --git a/plugins/input/geobuf/geobuf_datasource.cpp b/plugins/input/geobuf/geobuf_datasource.cpp new file mode 100644 index 000000000..e4f052020 --- /dev/null +++ b/plugins/input/geobuf/geobuf_datasource.cpp @@ -0,0 +1,253 @@ +/***************************************************************************** + * + * 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 + * + *****************************************************************************/ + +#include "geobuf_datasource.hpp" +#include "geobuf_featureset.hpp" +#include "geobuf.hpp" + +#include +#include +#include + +// boost + +#include + +// mapnik +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using mapnik::datasource; +using mapnik::parameters; + +DATASOURCE_PLUGIN(geobuf_datasource) + +struct attr_value_converter +{ + mapnik::eAttributeType operator() (mapnik::value_integer) const + { + return mapnik::Integer; + } + + mapnik::eAttributeType operator() (double) const + { + return mapnik::Double; + } + + mapnik::eAttributeType operator() (float) const + { + return mapnik::Double; + } + + mapnik::eAttributeType operator() (bool) const + { + return mapnik::Boolean; + } + + mapnik::eAttributeType operator() (std::string const& ) const + { + return mapnik::String; + } + + mapnik::eAttributeType operator() (mapnik::value_unicode_string const&) const + { + return mapnik::String; + } + + mapnik::eAttributeType operator() (mapnik::value_null const& ) const + { + return mapnik::String; + } +}; + +geobuf_datasource::geobuf_datasource(parameters const& params) + : datasource(params), + type_(datasource::Vector), + desc_(geobuf_datasource::name(), + *params.get("encoding","utf-8")), + filename_(), + extent_(), + features_(), + tree_(nullptr) +{ + boost::optional file = params.get("file"); + if (!file) throw mapnik::datasource_exception("Geobuf Plugin: missing parameter"); + + boost::optional base = params.get("base"); + if (base) + filename_ = *base + "/" + *file; + else + filename_ = *file; + + + mapnik::util::file in(filename_); + if (!in.is_open()) + { + throw mapnik::datasource_exception("Geobuf Plugin: could not open: '" + filename_ + "'"); + } + std::vector geobuf; + geobuf.resize(in.size()); + std::fread(geobuf.data(), in.size(), 1, in.get()); + parse_geobuf(geobuf.data(), geobuf.size()); +} + +namespace { +template +struct push_feature +{ + using features_container = T; + push_feature(features_container & features) + : features_(features) {} + + void operator() (mapnik::feature_ptr const& feature) + { + features_.push_back(feature); + } + features_container & features_; +}; +} + + +void geobuf_datasource::parse_geobuf(std::uint8_t const* data, std::size_t size) +{ + using push_feature_callback = push_feature>; + push_feature_callback callback(features_); + mapnik::util::geobuf buf(data, size, callback); + buf.read(); + std::cerr << "Num of features = " << features_.size() << std::endl; + using values_container = std::vector< std::pair>>; + values_container values; + values.reserve(features_.size()); + tree_ = std::make_unique(values); + std::size_t geometry_index = 0; + for (mapnik::feature_ptr const& f : features_) + { + mapnik::box2d box = f->envelope(); + if (box.valid()) + { + if (geometry_index == 0) + { + extent_ = box; + for ( auto const& kv : *f) + { + 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); + } + } + + values.emplace_back(box, std::make_pair(geometry_index,0)); + ++geometry_index; + } + // packing algorithm + tree_ = std::make_unique(values); +} + +geobuf_datasource::~geobuf_datasource() {} + +const char * geobuf_datasource::name() +{ + return "geobuf"; +} + +boost::optional geobuf_datasource::get_geometry_type() const +{ + boost::optional result; + int multi_type = 0; + unsigned num_features = features_.size(); + for (unsigned i = 0; i < num_features && i < 5; ++i) + { + result = mapnik::util::to_ds_type(features_[i]->get_geometry()); + if (result) + { + int type = static_cast(*result); + if (multi_type > 0 && multi_type != type) + { + result.reset(mapnik::datasource_geometry_t::Collection); + return result; + } + multi_type = type; + } + } + return result; +} + +mapnik::datasource::datasource_t geobuf_datasource::type() const +{ + return type_; +} + +mapnik::box2d geobuf_datasource::envelope() const +{ + return extent_; +} + +mapnik::layer_descriptor geobuf_datasource::get_descriptor() const +{ + return desc_; +} + +mapnik::featureset_ptr geobuf_datasource::features(mapnik::query const& q) const +{ + // if the query box intersects our world extent then query for features + mapnik::box2d const& box = q.get_bbox(); + if (extent_.intersects(box)) + { + geobuf_featureset::array_type index_array; + if (tree_) + { + tree_->query(boost::geometry::index::intersects(box), std::back_inserter(index_array)); + return std::make_shared(features_, std::move(index_array)); + } + } + return mapnik::featureset_ptr(); +} + +mapnik::featureset_ptr geobuf_datasource::features_at_point(mapnik::coord2d const& pt, double tol) const +{ + mapnik::box2d query_bbox(pt, pt); + query_bbox.pad(tol); + mapnik::query q(query_bbox); + std::vector const& desc = desc_.get_descriptors(); + std::vector::const_iterator itr = desc.begin(); + std::vector::const_iterator end = desc.end(); + for ( ;itr!=end;++itr) + { + q.add_property_name(itr->get_name()); + } + return features(q); +} diff --git a/plugins/input/geobuf/geobuf_datasource.hpp b/plugins/input/geobuf/geobuf_datasource.hpp new file mode 100644 index 000000000..4f8f83bbf --- /dev/null +++ b/plugins/input/geobuf/geobuf_datasource.hpp @@ -0,0 +1,104 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2017 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 GEOBUF_DATASOURCE_HPP +#define GEOBUF_DATASOURCE_HPP + +// mapnik +#include +#include +#include +#include +#include +#include +#include +#include +// boost +#include +#pragma GCC diagnostic push +#include +#include +#include +#include +#pragma GCC diagnostic pop + +// stl +#include +#include +#include +#include +#include + +using mapnik::datasource; + +template +struct geobuf_linear : boost::geometry::index::linear {}; + +namespace boost { namespace geometry { namespace index { namespace detail { namespace rtree { + +template +struct options_type > +{ + using type = options, + insert_default_tag, + choose_by_content_diff_tag, + split_default_tag, + linear_tag, +#if BOOST_VERSION >= 105700 + node_variant_static_tag>; +#else + node_s_mem_static_tag>; + +#endif +}; + +}}}}} + +class geobuf_datasource : public mapnik::datasource +{ +public: + using box_type = mapnik::box2d; + using item_type = std::pair>; + using spatial_index_type = boost::geometry::index::rtree >; + + // constructor + geobuf_datasource(mapnik::parameters const& params); + virtual ~geobuf_datasource (); + mapnik::datasource::datasource_t type() const; + static const char * name(); + mapnik::featureset_ptr features(mapnik::query const& q) const; + mapnik::featureset_ptr features_at_point(mapnik::coord2d const& pt, double tol = 0) const; + mapnik::box2d envelope() const; + mapnik::layer_descriptor get_descriptor() const; + boost::optional get_geometry_type() const; + void parse_geobuf(std::uint8_t const* buffer, std::size_t size); +private: + mapnik::datasource::datasource_t type_; + mapnik::layer_descriptor desc_; + std::string filename_; + mapnik::box2d extent_; + std::vector features_; + std::unique_ptr tree_; +}; + + +#endif // GEOBUF_DATASOURCE_HPP diff --git a/plugins/input/geobuf/geobuf_featureset.cpp b/plugins/input/geobuf/geobuf_featureset.cpp new file mode 100644 index 000000000..ed0543381 --- /dev/null +++ b/plugins/input/geobuf/geobuf_featureset.cpp @@ -0,0 +1,60 @@ +/***************************************************************************** + * + * 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 +#include +// stl +#include +#include +#include + +#include "geobuf_featureset.hpp" + +geobuf_featureset::geobuf_featureset(std::vector const& features,array_type && index_array) + : features_(features), + index_array_(std::move(index_array)), + index_itr_(index_array_.begin()), + index_end_(index_array_.end()), + ctx_(std::make_shared()) {} + +geobuf_featureset::~geobuf_featureset() {} + +mapnik::feature_ptr geobuf_featureset::next() +{ + if (index_itr_ != index_end_) + { +#if BOOST_VERSION >= 105600 + geobuf_datasource::item_type const& item = *index_itr_++; + std::size_t index = item.second.first; + //std::size_t size = item.second.second; + //std::cerr << file_offset << " (" << size << ") " << item.first << std::endl; +#else + std::size_t index = *index_itr_++; +#endif + if ( index < features_.size()) + { + return features_.at(index); + } + } + return mapnik::feature_ptr(); +} diff --git a/plugins/input/geobuf/geobuf_featureset.hpp b/plugins/input/geobuf/geobuf_featureset.hpp new file mode 100644 index 000000000..a1a0668aa --- /dev/null +++ b/plugins/input/geobuf/geobuf_featureset.hpp @@ -0,0 +1,50 @@ +/***************************************************************************** + * + * 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 GEOBUF_FEATURESET_HPP +#define GEOBUF_FEATURESET_HPP + +#include +#include "geobuf_datasource.hpp" + +#include +#include +#include + +class geobuf_featureset : public mapnik::Featureset +{ +public: + typedef std::deque array_type; + geobuf_featureset(std::vector const& features, + array_type && index_array); + virtual ~geobuf_featureset(); + mapnik::feature_ptr next(); + +private: + std::vector const& features_; + const array_type index_array_; + array_type::const_iterator index_itr_; + array_type::const_iterator index_end_; + mapnik::context_ptr ctx_; +}; + +#endif // GEOBUF_FEATURESET_HPP diff --git a/plugins/input/geobuf/pbf.hpp b/plugins/input/geobuf/pbf.hpp new file mode 100644 index 000000000..d017219a5 --- /dev/null +++ b/plugins/input/geobuf/pbf.hpp @@ -0,0 +1,184 @@ +#ifndef MBGL_UTIL_PBF +#define MBGL_UTIL_PBF + +/* + * Some parts are from upb - a minimalist implementation of protocol buffers. + * + * Copyright (c) 2008-2011 Google Inc. See LICENSE for details. + * Author: Josh Haberman + */ + +#include +#include + +namespace mbgl { + +struct pbf { + struct exception : std::exception { const char *what() const noexcept { return "pbf exception"; } }; + struct unterminated_varint_exception : exception { const char *what() const noexcept { return "pbf unterminated varint exception"; } }; + struct varint_too_long_exception : exception { const char *what() const noexcept { return "pbf varint too long exception"; } }; + struct unknown_field_type_exception : exception { const char *what() const noexcept { return "pbf unknown field type exception"; } }; + struct end_of_buffer_exception : exception { const char *what() const noexcept { return "pbf end of buffer exception"; } }; + + inline pbf(const unsigned char *data, size_t length); + inline pbf(); + + inline operator bool() const; + + inline bool next(); + inline bool next(uint32_t tag); + template inline T varint(); + template inline T svarint(); + + template inline T fixed(); + inline float float32(); + inline double float64(); + + inline std::string string(); + inline bool boolean(); + + inline pbf message(); + + inline void skip(); + inline void skipValue(uint32_t val); + inline void skipBytes(uint32_t bytes); + + const uint8_t *data = nullptr; + const uint8_t *end = nullptr; + uint32_t value = 0; + uint32_t tag = 0; +}; + +pbf::pbf(const unsigned char *data_, size_t length) + : data(data_), + end(data_ + length), + value(0), + tag(0) { +} + +pbf::pbf() + : data(nullptr), + end(nullptr), + value(0), + tag(0) { +} + + +pbf::operator bool() const { + return data < end; +} + +bool pbf::next() { + if (data < end) { + value = static_cast(varint()); + tag = value >> 3; + return true; + } + return false; +} + +bool pbf::next(uint32_t requested_tag) { + while (next()) { + if (tag == requested_tag) { + return true; + } else { + skip(); + } + } + return false; +} + +template +T pbf::varint() { + uint8_t byte = 0x80; + T result = 0; + int bitpos; + for (bitpos = 0; bitpos < 70 && (byte & 0x80); bitpos += 7) { + if (data >= end) { + throw unterminated_varint_exception(); + } + result |= ((T)(byte = *data) & 0x7F) << bitpos; + + data++; + } + if (bitpos == 70 && (byte & 0x80)) { + throw varint_too_long_exception(); + } + + return result; +} + +template +T pbf::svarint() { + T n = varint(); + return (n >> 1) ^ -(T)(n & 1); +} + +template +T pbf::fixed() { + skipBytes(bytes); + T result; + memcpy(&result, data - bytes, bytes); + return result; +} + +float pbf::float32() { + return fixed(); +} + +double pbf::float64() { + return fixed(); +} + +std::string pbf::string() { + uint32_t bytes = static_cast(varint()); + const char *string_data = reinterpret_cast(data); + skipBytes(bytes); + return std::string(string_data, bytes); +} + +bool pbf::boolean() { + skipBytes(1); + return *(bool *)(data - 1); +} + +pbf pbf::message() { + uint32_t bytes = static_cast(varint()); + const uint8_t *pos = data; + skipBytes(bytes); + return pbf(pos, bytes); +} + +void pbf::skip() { + skipValue(value); +} + +void pbf::skipValue(uint32_t val) { + switch (val & 0x7) { + case 0: // varint + varint(); + break; + case 1: // 64 bit + skipBytes(8); + break; + case 2: // string/message + skipBytes(static_cast(varint())); + break; + case 5: // 32 bit + skipBytes(4); + break; + default: + throw unknown_field_type_exception(); + } +} + +void pbf::skipBytes(uint32_t bytes) { + if (data + bytes > end) { + throw end_of_buffer_exception(); + } + data += bytes; +} + +} // end namespace mbgl + +#endif