Remove polygon copying overhead

This commit is contained in:
Michael Krasnyk 2017-08-31 12:31:04 +02:00
parent 421115200b
commit 20ff138f08
2 changed files with 40 additions and 50 deletions

View File

@ -20,7 +20,7 @@ struct LocationDependentData
{ {
using point_t = boost::geometry::model::d2:: using point_t = boost::geometry::model::d2::
point_xy<double, boost::geometry::cs::spherical_equatorial<boost::geometry::degree>>; point_xy<double, boost::geometry::cs::spherical_equatorial<boost::geometry::degree>>;
using segment_t = std::pair<point_t, point_t>; using segment_t = boost::geometry::model::segment<point_t>;
using polygon_t = boost::geometry::model::polygon<point_t>; using polygon_t = boost::geometry::model::polygon<point_t>;
using polygon_bands_t = std::vector<std::vector<segment_t>>; using polygon_bands_t = std::vector<std::vector<segment_t>>;
using box_t = boost::geometry::model::box<point_t>; using box_t = boost::geometry::model::box<point_t>;

View File

@ -87,56 +87,42 @@ void LocationDependentData::loadLocationDependentData(const boost::filesystem::p
return index; return index;
}; };
auto convert_to_ring = [](const auto &coordinates_array) -> polygon_t::ring_type { auto index_polygon = [this, &bounding_boxes](const auto &rings, auto properties_index) {
polygon_t::ring_type ring; // At least an outer ring in polygon https://tools.ietf.org/html/rfc7946#section-3.1.6
for (rapidjson::SizeType i = 0; i < coordinates_array.Size(); ++i)
{
util::validateCoordinate(coordinates_array[i]);
const auto &coords = coordinates_array[i].GetArray();
ring.emplace_back(coords[0].GetDouble(), coords[1].GetDouble());
}
return ring;
};
auto index_polygon = [this, &bounding_boxes, &convert_to_ring](const auto &rings,
auto properties_index) {
// https://tools.ietf.org/html/rfc7946#section-3.1.6
BOOST_ASSERT(rings.Size() > 0); BOOST_ASSERT(rings.Size() > 0);
polygon_t polygon;
polygon.outer() = convert_to_ring(rings[0].GetArray());
for (rapidjson::SizeType iring = 1; iring < rings.Size(); ++iring)
{
polygon.inners().emplace_back(convert_to_ring(rings[iring].GetArray()));
}
auto envelop = boost::geometry::return_envelope<box_t>(polygon);
bounding_boxes.emplace_back(envelop, polygons.size());
// here is a part of ExtractPolygon::ExtractPolygon code auto to_point = [](const auto &json) -> point_t {
// TODO: remove copy overhead util::validateCoordinate(json);
constexpr const int32_t segments_per_band = 10; const auto &coords = json.GetArray();
constexpr const int32_t max_bands = 10000; return {coords[0].GetDouble(), coords[1].GetDouble()};
const auto y_min = envelop.min_corner().y(); };
const auto y_max = envelop.max_corner().y();
std::vector<segment_t> segments; std::vector<segment_t> segments;
auto add_ring = [&segments](const auto &ring) { auto append_ring_segments = [&segments, &to_point](const auto &coordinates_array) -> box_t {
auto it = ring.begin(); box_t envelop;
const auto end = ring.end(); if (!coordinates_array.Empty())
BOOST_ASSERT(it != end);
auto last_it = it++;
while (it != end)
{ {
segments.emplace_back(*last_it, *it); point_t curr = to_point(coordinates_array[0]), next;
last_it = it++; for (rapidjson::SizeType i = 1; i < coordinates_array.Size(); ++i, curr = next)
{
next = to_point(coordinates_array[i]);
segments.emplace_back(curr, next);
boost::geometry::expand(envelop, next);
} }
}
return envelop;
}; };
add_ring(polygon.outer()); auto envelop = append_ring_segments(rings[0].GetArray());
for (const auto &ring : polygon.inners()) bounding_boxes.emplace_back(envelop, polygons.size());
add_ring(ring); for (rapidjson::SizeType iring = 1; iring < rings.Size(); ++iring)
{
append_ring_segments(rings[iring].GetArray());
}
int32_t num_bands = static_cast<int32_t>(segments.size()) / segments_per_band; constexpr const std::size_t segments_per_band = 10;
constexpr const std::size_t max_bands = 10000;
auto num_bands = segments.size() / segments_per_band;
if (num_bands < 1) if (num_bands < 1)
{ {
num_bands = 1; num_bands = 1;
@ -147,6 +133,9 @@ void LocationDependentData::loadLocationDependentData(const boost::filesystem::p
} }
polygon_bands_t bands(num_bands); polygon_bands_t bands(num_bands);
const auto y_min = envelop.min_corner().y();
const auto y_max = envelop.max_corner().y();
const auto dy = (y_max - y_min) / num_bands; const auto dy = (y_max - y_min) / num_bands;
for (const auto &segment : segments) for (const auto &segment : segments)
@ -155,15 +144,13 @@ void LocationDependentData::loadLocationDependentData(const boost::filesystem::p
const std::pair<coord_t, coord_t> mm = const std::pair<coord_t, coord_t> mm =
std::minmax(segment.first.y(), segment.second.y()); std::minmax(segment.first.y(), segment.second.y());
const auto band_min = std::min<coord_t>(num_bands - 1, (mm.first - y_min) / dy); const auto band_min = std::min<coord_t>(num_bands - 1, (mm.first - y_min) / dy);
const auto band_max = std::min<coord_t>( const auto band_max = std::min<coord_t>(num_bands, ((mm.second - y_min) / dy) + 1);
num_bands, ((mm.second - y_min) / dy) + 1); // TODO: use integer coordinates
for (auto band = band_min; band < band_max; ++band) for (auto band = band_min; band < band_max; ++band)
{ {
bands[band].push_back(segment); bands[band].push_back(segment);
} }
} }
// EOC
polygons.emplace_back(std::make_pair(bands, properties_index)); polygons.emplace_back(std::make_pair(bands, properties_index));
}; };
@ -221,7 +208,7 @@ LocationDependentData::properties_t LocationDependentData::operator()(const poin
const auto y_min = envelop.min_corner().y(); const auto y_min = envelop.min_corner().y();
const auto y_max = envelop.max_corner().y(); const auto y_max = envelop.max_corner().y();
auto dy = (y_max - y_min) / bands.size(); const auto dy = (y_max - y_min) / bands.size();
std::size_t band = (point.y() - y_min) / dy; std::size_t band = (point.y() - y_min) / dy;
if (band >= bands.size()) if (band >= bands.size())
@ -277,6 +264,9 @@ LocationDependentData::properties_t LocationDependentData::operator()(const osmi
// For more complicated scenarios a proper merging of multiple tags // For more complicated scenarios a proper merging of multiple tags
// at one or many locations must be provided // at one or many locations must be provided
const auto &nodes = way.nodes(); const auto &nodes = way.nodes();
// TODO: the next call requires an OSM file preprocessed with a command
// osmium add-locations-to-ways --keep-untagged-nodes
const auto &location = nodes.back().location(); const auto &location = nodes.back().location();
const point_t point(location.lon(), location.lat()); const point_t point(location.lon(), location.lat());