Add MultiPolygon support
This commit is contained in:
parent
fc39e0ce1a
commit
7ad9e13f1e
@ -1,12 +1,6 @@
|
||||
@extract
|
||||
Feature: osrm-extract lua ways:get_nodes()
|
||||
|
||||
Background:
|
||||
Given the node map
|
||||
"""
|
||||
a b
|
||||
"""
|
||||
|
||||
Scenario: osrm-extract - Passing base file
|
||||
Given the profile file
|
||||
"""
|
||||
@ -23,6 +17,10 @@ Feature: osrm-extract lua ways:get_nodes()
|
||||
functions.process_way = way_function
|
||||
return functions
|
||||
"""
|
||||
And the node map
|
||||
"""
|
||||
a b
|
||||
"""
|
||||
And the ways
|
||||
| nodes |
|
||||
| ab |
|
||||
@ -36,6 +34,10 @@ Feature: osrm-extract lua ways:get_nodes()
|
||||
|
||||
Scenario: osrm-extract location-dependent data without add-locations-to-ways preprocessing
|
||||
Given the profile "testbot"
|
||||
And the node map
|
||||
"""
|
||||
a b
|
||||
"""
|
||||
And the ways
|
||||
| nodes |
|
||||
| ab |
|
||||
@ -61,6 +63,10 @@ Feature: osrm-extract lua ways:get_nodes()
|
||||
functions.process_way = way_function
|
||||
return functions
|
||||
"""
|
||||
And the node map
|
||||
"""
|
||||
a b
|
||||
"""
|
||||
And the ways with locations
|
||||
| nodes |
|
||||
| ab |
|
||||
@ -70,3 +76,38 @@ Feature: osrm-extract lua ways:get_nodes()
|
||||
Then it should exit successfully
|
||||
And stdout should contain "answer 42"
|
||||
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"
|
||||
|
@ -38,7 +38,8 @@ struct LocationDependentData
|
||||
void loadLocationDependentData(const boost::filesystem::path &file_path);
|
||||
|
||||
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;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -76,8 +76,6 @@ inline void validateFeature(const rapidjson::Value &feature)
|
||||
const auto coord_array = feature["geometry"].GetObject()["coordinates"].GetArray();
|
||||
if (coord_array.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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -59,54 +59,84 @@ void LocationDependentData::loadLocationDependentData(const boost::filesystem::p
|
||||
|
||||
const auto &features_array = geojson["features"].GetArray();
|
||||
std::vector<rtree_t::value_type> bounding_boxes;
|
||||
for (rapidjson::SizeType i = 0; i < features_array.Size(); i++)
|
||||
|
||||
auto convert_value = [](const auto &property) -> property_t {
|
||||
if (property.IsString())
|
||||
return std::string(property.GetString());
|
||||
if (property.IsNumber())
|
||||
return property.GetDouble();
|
||||
if (property.IsBool())
|
||||
return property.GetBool();
|
||||
return {};
|
||||
};
|
||||
|
||||
auto collect_properties = [this, &convert_value](const auto &object) -> std::size_t {
|
||||
properties_t object_properties;
|
||||
for (const auto &property : object)
|
||||
{
|
||||
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[i]);
|
||||
const auto &feature = features_array[i].GetObject();
|
||||
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"))
|
||||
if (std::strcmp(geometry["type"].GetString(), "Polygon") == 0)
|
||||
{
|
||||
util::Log(logDEBUG) << "Skipping non-polygon shape in geojson file";
|
||||
continue;
|
||||
// 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);
|
||||
}
|
||||
|
||||
// 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)
|
||||
else if (std::strcmp(geometry["type"].GetString(), "MultiPolygon") == 0)
|
||||
{
|
||||
util::validateCoordinate(coords_outer_array[i]);
|
||||
const auto &coords = coords_outer_array[i].GetArray();
|
||||
polygon.outer().emplace_back(coords[0].GetDouble(), coords[1].GetDouble());
|
||||
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);
|
||||
}
|
||||
}
|
||||
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 {
|
||||
if (property.IsString())
|
||||
return std::string(property.GetString());
|
||||
if (property.IsNumber())
|
||||
return property.GetDouble();
|
||||
if (property.IsBool())
|
||||
return property.GetBool();
|
||||
return {};
|
||||
};
|
||||
properties_t properties;
|
||||
for (const auto &property : feature["properties"].GetObject())
|
||||
{
|
||||
properties.insert({property.name.GetString(), convert_value(property.value)});
|
||||
}
|
||||
polygons.push_back(std::make_pair(polygon, properties));
|
||||
}
|
||||
|
||||
// Create R-tree for bounding boxes of collected polygons
|
||||
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
|
||||
@ -136,7 +166,7 @@ sol::table LocationDependentData::operator()(sol::state &state, const osmium::Wa
|
||||
|
||||
auto table = sol::table(state, sol::create);
|
||||
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);
|
||||
}
|
||||
|
1
test/data/regions/hong-kong.geojson
Normal file
1
test/data/regions/hong-kong.geojson
Normal file
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user