Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

4x faster polygon compositing #91

Merged
merged 1 commit into from
Dec 3, 2018
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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