Add MultiPolygon support

This commit is contained in:
Michael Krasnyk 2017-08-18 16:21:50 +02:00
parent fc39e0ce1a
commit 7ad9e13f1e
5 changed files with 116 additions and 45 deletions

View File

@ -1,12 +1,6 @@
@extract @extract
Feature: osrm-extract lua ways:get_nodes() Feature: osrm-extract lua ways:get_nodes()
Background:
Given the node map
"""
a b
"""
Scenario: osrm-extract - Passing base file Scenario: osrm-extract - Passing base file
Given the profile file Given the profile file
""" """
@ -23,6 +17,10 @@ Feature: osrm-extract lua ways:get_nodes()
functions.process_way = way_function functions.process_way = way_function
return functions return functions
""" """
And the node map
"""
a b
"""
And the ways And the ways
| nodes | | nodes |
| ab | | ab |
@ -36,6 +34,10 @@ Feature: osrm-extract lua ways:get_nodes()
Scenario: osrm-extract location-dependent data without add-locations-to-ways preprocessing Scenario: osrm-extract location-dependent data without add-locations-to-ways preprocessing
Given the profile "testbot" Given the profile "testbot"
And the node map
"""
a b
"""
And the ways And the ways
| nodes | | nodes |
| ab | | ab |
@ -61,6 +63,10 @@ Feature: osrm-extract lua ways:get_nodes()
functions.process_way = way_function functions.process_way = way_function
return functions return functions
""" """
And the node map
"""
a b
"""
And the ways with locations And the ways with locations
| nodes | | nodes |
| ab | | ab |
@ -70,3 +76,38 @@ Feature: osrm-extract lua ways:get_nodes()
Then it should exit successfully Then it should exit successfully
And stdout should contain "answer 42" And stdout should contain "answer 42"
And stdout should not contain "array" And stdout should not contain "array"
Scenario: osrm-extract location-dependent data with multi-polygons
Given the profile file
"""
functions = require('testbot')
function way_function(profile, way, result, location_data)
assert(location_data)
print('ISO3166-1 ' .. (location_data['ISO3166-1'] or 'none'))
result.forward_mode = mode.driving
result.forward_speed = 1
end
functions.process_way = way_function
return functions
"""
And the node locations
| node | lat | lon | id |
| a | 22.4903670 | 113.9455227 | 1 |
| b | 22.4901701 | 113.9455899 | 2 |
| c | 22.4901852 | 113.9458608 | 3 |
| d | 22.4904033 | 113.9456999 | 4 |
And the ways with locations
| nodes | # |
| ab | Hong Kong |
| cd | China Mainland |
And the data has been saved to disk
When I run "osrm-extract --profile {profile_file} {osm_file} --location-dependent-data test/data/regions/null-island.geojson --location-dependent-data test/data/regions/hong-kong.geojson"
Then it should exit successfully
And stdout should contain "1 GeoJSON polygon"
And stdout should contain "2 GeoJSON polygons"
And stdout should contain "ISO3166-1 HK"
And stdout should contain "ISO3166-1 none"

View File

@ -38,7 +38,8 @@ struct LocationDependentData
void loadLocationDependentData(const boost::filesystem::path &file_path); void loadLocationDependentData(const boost::filesystem::path &file_path);
rtree_t rtree; rtree_t rtree;
std::vector<std::pair<polygon_t, properties_t>> polygons; std::vector<std::pair<polygon_t, std::size_t>> polygons;
std::vector<properties_t> properties;
}; };
} }
} }

View File

@ -76,8 +76,6 @@ inline void validateFeature(const rapidjson::Value &feature)
const auto coord_array = feature["geometry"].GetObject()["coordinates"].GetArray(); const auto coord_array = feature["geometry"].GetObject()["coordinates"].GetArray();
if (coord_array.Empty()) if (coord_array.Empty())
throw osrm::util::exception("Feature geometry coordinates member is empty."); throw osrm::util::exception("Feature geometry coordinates member is empty.");
if (!coord_array[0].GetArray()[0].IsArray())
throw osrm::util::exception("Feature geometry coordinates array has non-array outer ring.");
} }
} }
} }

View File

@ -59,34 +59,7 @@ void LocationDependentData::loadLocationDependentData(const boost::filesystem::p
const auto &features_array = geojson["features"].GetArray(); const auto &features_array = geojson["features"].GetArray();
std::vector<rtree_t::value_type> bounding_boxes; std::vector<rtree_t::value_type> bounding_boxes;
for (rapidjson::SizeType i = 0; i < features_array.Size(); i++)
{
util::validateFeature(features_array[i]);
const auto &feature = features_array[i].GetObject();
const auto &geometry = feature["geometry"].GetObject();
BOOST_ASSERT(geometry.HasMember("type"));
// Case-sensitive check of type https://tools.ietf.org/html/rfc7946#section-1.4
if (std::strcmp(geometry["type"].GetString(), "Polygon"))
{
util::Log(logDEBUG) << "Skipping non-polygon shape in geojson file";
continue;
}
// The first array of polygon coords is the exterior ring
// https://tools.ietf.org/html/rfc7946#section-3.1.6
polygon_t polygon;
const auto &coords_outer_array = geometry["coordinates"].GetArray()[0].GetArray();
for (rapidjson::SizeType i = 0; i < coords_outer_array.Size(); ++i)
{
util::validateCoordinate(coords_outer_array[i]);
const auto &coords = coords_outer_array[i].GetArray();
polygon.outer().emplace_back(coords[0].GetDouble(), coords[1].GetDouble());
}
bounding_boxes.emplace_back(boost::geometry::return_envelope<box_t>(polygon),
polygons.size());
// Collect feature properties and store in polygons vector
auto convert_value = [](const auto &property) -> property_t { auto convert_value = [](const auto &property) -> property_t {
if (property.IsString()) if (property.IsString())
return std::string(property.GetString()); return std::string(property.GetString());
@ -96,17 +69,74 @@ void LocationDependentData::loadLocationDependentData(const boost::filesystem::p
return property.GetBool(); return property.GetBool();
return {}; return {};
}; };
properties_t properties;
for (const auto &property : feature["properties"].GetObject()) auto collect_properties = [this, &convert_value](const auto &object) -> std::size_t {
properties_t object_properties;
for (const auto &property : object)
{ {
properties.insert({property.name.GetString(), convert_value(property.value)}); object_properties.insert({property.name.GetString(), convert_value(property.value)});
}
const std::size_t index = properties.size();
properties.emplace_back(object_properties);
return index;
};
auto convert_to_ring = [](const auto &coordinates_array) -> polygon_t::ring_type {
polygon_t::ring_type ring;
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);
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());
polygons.emplace_back(std::make_pair(polygon, properties_index));
};
for (rapidjson::SizeType ifeature = 0; ifeature < features_array.Size(); ifeature++)
{
util::validateFeature(features_array[ifeature]);
const auto &feature = features_array[ifeature].GetObject();
const auto &geometry = feature["geometry"].GetObject();
BOOST_ASSERT(geometry.HasMember("type"));
// Case-sensitive check of type https://tools.ietf.org/html/rfc7946#section-1.4
if (std::strcmp(geometry["type"].GetString(), "Polygon") == 0)
{
// Collect feature properties and store in polygons vector
auto properties_index = collect_properties(feature["properties"].GetObject());
const auto &coordinates = geometry["coordinates"].GetArray();
index_polygon(coordinates, properties_index);
}
else if (std::strcmp(geometry["type"].GetString(), "MultiPolygon") == 0)
{
auto properties_index = collect_properties(feature["properties"].GetObject());
const auto &polygons = geometry["coordinates"].GetArray();
for (rapidjson::SizeType ipolygon = 0; ipolygon < polygons.Size(); ++ipolygon)
{
index_polygon(polygons[ipolygon].GetArray(), properties_index);
}
} }
polygons.push_back(std::make_pair(polygon, properties));
} }
// Create R-tree for bounding boxes of collected polygons // Create R-tree for bounding boxes of collected polygons
rtree = rtree_t(bounding_boxes); rtree = rtree_t(bounding_boxes);
util::Log() << "Parsed " << polygons.size() << " geojson polygons with location-dependent data"; util::Log() << "Parsed " << properties.size() << " location-dependent features with "
<< polygons.size() << " GeoJSON polygons";
} }
namespace namespace
@ -136,7 +166,7 @@ sol::table LocationDependentData::operator()(sol::state &state, const osmium::Wa
auto table = sol::table(state, sol::create); auto table = sol::table(state, sol::create);
auto merger = [this, &table](const rtree_t::value_type &rtree_entry) { auto merger = [this, &table](const rtree_t::value_type &rtree_entry) {
for (const auto &key_value : polygons[rtree_entry.second].second) for (const auto &key_value : properties[polygons[rtree_entry.second].second])
{ {
boost::apply_visitor(table_setter(table, key_value.first), key_value.second); boost::apply_visitor(table_setter(table, key_value.first), key_value.second);
} }

File diff suppressed because one or more lines are too long