From efd5a31929eddfd9a06fef43db0b713e67e73640 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Sun, 2 Dec 2018 15:39:19 -0800 Subject: [PATCH] 4x faster polygon decoding --- src/feature_builder.hpp | 129 +++++++++++++++++++++++----------------- 1 file changed, 74 insertions(+), 55 deletions(-) diff --git a/src/feature_builder.hpp b/src/feature_builder.hpp index 1fa7bd0..f3d28d5 100644 --- a/src/feature_builder.hpp +++ b/src/feature_builder.hpp @@ -98,15 +98,13 @@ struct line_string_handler bool first_ = true; }; -template -using annotated_ring = std::pair, vtzero::ring_type>; - template struct polygon_handler { - using geom_type = std::vector>; - polygon_handler(geom_type& geom, int dx, int dy, int zoom_factor) - : geom_(geom), + polygon_handler(vtzero::polygon_feature_builder & builder, mapbox::geometry::box const& bbox, int dx, int dy, int zoom_factor) + : builder_(builder), + bbox_(bbox), + extent_({0,0},{0,0}), dx_(dx), dy_(dy), zoom_factor_(zoom_factor) {} @@ -114,8 +112,8 @@ struct polygon_handler void ring_begin(std::uint32_t count) { first_ = true; - geom_.emplace_back(); - geom_.back().first.reserve(count); + ring_.clear(); + ring_.reserve(count); } void ring_point(vtzero::point const& pt) @@ -124,7 +122,18 @@ struct polygon_handler { CoordinateType x = pt.x * zoom_factor_ - dx_; CoordinateType y = pt.y * zoom_factor_ - dy_; - geom_.back().first.emplace_back(x, y); + ring_.emplace_back(x, y); + if (first_) { + extent_.min.x = x; + extent_.max.x = x; + extent_.min.y = y; + extent_.max.y = y; + } else { + if (x < extent_.min.x) extent_.min.x = x; + if (x > extent_.max.x) extent_.max.x = x; + if (y < extent_.min.y) extent_.min.y = y; + if (y > extent_.max.y) extent_.max.y = y; + } cur_x_ = pt.x; cur_y_ = pt.y; first_ = false; @@ -133,16 +142,64 @@ struct polygon_handler void ring_end(vtzero::ring_type type) { - geom_.back().second = type; + if (type == vtzero::ring_type::outer) { + if (!boost::geometry::intersects(extent_, bbox_)) { + skip_ = true; + } else { + skip_ = false; + valid_ = true; + } + } + if (!skip_ && ring_.size() > 3) { + // only clip if not fully within bbox + if (bbox_.min.x >= extent_.min.x && + bbox_.max.x <= extent_.max.x && + bbox_.min.y >= extent_.min.y && + bbox_.max.y <= extent_.max.y) { + builder_.add_ring(static_cast(ring_.size())); + for (auto const& pt : ring_) { + builder_.set_point(static_cast(pt.x), static_cast(pt.y)); + }; + } else { + // TODO: reuse this result memory? + std::vector> result; + if (type == vtzero::ring_type::inner) { + boost::geometry::reverse(ring_); + } + boost::geometry::intersection(ring_, bbox_, result); + for (auto && geom : result) { + if (type == vtzero::ring_type::inner) { + boost::geometry::reverse(geom); + } + for (auto const& ring : geom) { + if (ring.size() > 3) { + builder_.add_ring(static_cast(ring.size())); + for (auto const& pt : ring) { + builder_.set_point(static_cast(pt.x), static_cast(pt.y)); + }; + } + } + } + } + } } - geom_type& geom_; + bool valid() const { + return valid_; + } + + vtzero::polygon_feature_builder & builder_; + mapbox::geometry::linear_ring ring_; + mapbox::geometry::box const& bbox_; + mapbox::geometry::box extent_; int const dx_; int const dy_; int const zoom_factor_; CoordinateType cur_x_ = 0; CoordinateType cur_y_ = 0; bool first_ = true; + bool skip_ = false; + bool valid_ = false; }; } // namespace detail @@ -219,50 +276,12 @@ struct overzoomed_feature_builder } void apply_geometry_polygon(vtzero::feature const& feature) { - std::vector> rings; - vtzero::decode_polygon_geometry(feature.geometry(), detail::polygon_handler(rings, dx_, dy_, zoom_factor_)); - if (!rings.empty()) - { - vtzero::polygon_feature_builder feature_builder{layer_builder_}; - feature_builder.copy_id(feature); - bool valid = false; - bool process = false; - for (auto& r : rings) - { - if (r.second == vtzero::ring_type::outer) - { - auto extent = mapbox::geometry::envelope(r.first); - process = boost::geometry::intersects(extent, bbox_); - } - if (process) - { - std::vector> result; - if (r.second == vtzero::ring_type::inner) boost::geometry::reverse(r.first); - // ^^ reverse inner rings before clipping as we're dealing with a disassembled polygon - boost::geometry::intersection(r.first, bbox_, result); - for (auto const& p : result) - { - for (auto const& ring : p) - { - if (ring.size() > 3) - { - valid = true; - feature_builder.add_ring(static_cast(ring.size())); - if (r.second == vtzero::ring_type::outer) - { - std::for_each(ring.begin(), ring.end(), [&feature_builder](auto const& pt) { feature_builder.set_point(static_cast(pt.x), static_cast(pt.y)); }); - } - else - { - // apply points in reverse to preserve original winding order of inner rings - std::for_each(ring.rbegin(), ring.rend(), [&feature_builder](auto const& pt) { feature_builder.set_point(static_cast(pt.x), static_cast(pt.y)); }); - } - } - } - } - } - } - if (valid) finalize(feature_builder, feature); + vtzero::polygon_feature_builder feature_builder{layer_builder_}; + feature_builder.copy_id(feature); + detail::polygon_handler handler(feature_builder, bbox_, dx_, dy_, zoom_factor_); + vtzero::decode_polygon_geometry(feature.geometry(), handler); + if (handler.valid()) { + finalize(feature_builder, feature); } }