Skip to content

Commit

Permalink
Merge pull request #91 from mapbox/speed-up-poly-decode
Browse files Browse the repository at this point in the history
4x faster polygon compositing
  • Loading branch information
artemp committed Dec 3, 2018
2 parents c007e08 + efd5a31 commit ad3b875
Showing 1 changed file with 74 additions and 55 deletions.
129 changes: 74 additions & 55 deletions src/feature_builder.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,24 +98,22 @@ struct line_string_handler
bool first_ = true;
};

template <typename CoordinateType>
using annotated_ring = std::pair<mapbox::geometry::linear_ring<CoordinateType>, vtzero::ring_type>;

template <typename CoordinateType>
struct polygon_handler
{
using geom_type = std::vector<annotated_ring<CoordinateType>>;
polygon_handler(geom_type& geom, int dx, int dy, int zoom_factor)
: geom_(geom),
polygon_handler(vtzero::polygon_feature_builder & builder, mapbox::geometry::box<CoordinateType> 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) {}

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)
Expand All @@ -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;
Expand All @@ -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<unsigned>(ring_.size()));
for (auto const& pt : ring_) {
builder_.set_point(static_cast<int>(pt.x), static_cast<int>(pt.y));
};
} else {
// TODO: reuse this result memory?
std::vector<mapbox::geometry::polygon<CoordinateType>> 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<unsigned>(ring.size()));
for (auto const& pt : ring) {
builder_.set_point(static_cast<int>(pt.x), static_cast<int>(pt.y));
};
}
}
}
}
}
}

geom_type& geom_;
bool valid() const {
return valid_;
}

vtzero::polygon_feature_builder & builder_;
mapbox::geometry::linear_ring<CoordinateType> ring_;
mapbox::geometry::box<CoordinateType> const& bbox_;
mapbox::geometry::box<CoordinateType> 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
Expand Down Expand Up @@ -219,50 +276,12 @@ struct overzoomed_feature_builder
}
void apply_geometry_polygon(vtzero::feature const& feature)
{
std::vector<detail::annotated_ring<CoordinateType>> rings;
vtzero::decode_polygon_geometry(feature.geometry(), detail::polygon_handler<CoordinateType>(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<mapbox::geometry::polygon<coordinate_type>> 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<unsigned>(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<int>(pt.x), static_cast<int>(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<int>(pt.x), static_cast<int>(pt.y)); });
}
}
}
}
}
}
if (valid) finalize(feature_builder, feature);
vtzero::polygon_feature_builder feature_builder{layer_builder_};
feature_builder.copy_id(feature);
detail::polygon_handler<CoordinateType> handler(feature_builder, bbox_, dx_, dy_, zoom_factor_);
vtzero::decode_polygon_geometry(feature.geometry(), handler);
if (handler.valid()) {
finalize(feature_builder, feature);
}
}

Expand Down

0 comments on commit ad3b875

Please sign in to comment.