Added location dependent data
This commit is contained in:
@@ -0,0 +1,147 @@
|
||||
#include "extractor/location_dependent_data.hpp"
|
||||
|
||||
#include "util/exception.hpp"
|
||||
#include "util/geojson_validation.hpp"
|
||||
|
||||
#include <rapidjson/document.h>
|
||||
#include <rapidjson/error/en.h>
|
||||
#include <rapidjson/istreamwrapper.h>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace extractor
|
||||
{
|
||||
|
||||
LocationDependentData::LocationDependentData(const boost::filesystem::path &file_path)
|
||||
{
|
||||
if (file_path.empty())
|
||||
return;
|
||||
|
||||
if (!boost::filesystem::exists(file_path) || !boost::filesystem::is_regular_file(file_path))
|
||||
{
|
||||
throw osrm::util::exception(std::string("File with location-dependent data ") +
|
||||
file_path.string() + " does not exists");
|
||||
}
|
||||
|
||||
std::ifstream file(file_path.string());
|
||||
if (!file.is_open())
|
||||
throw osrm::util::exception("failed to open " + file_path.string());
|
||||
|
||||
rapidjson::IStreamWrapper isw(file);
|
||||
rapidjson::Document geojson;
|
||||
geojson.ParseStream(isw);
|
||||
if (geojson.HasParseError())
|
||||
{
|
||||
throw osrm::util::exception(std::string("Failed to parse ") + file_path.string() + ":" +
|
||||
std::to_string(geojson.GetErrorOffset()) + " error: " +
|
||||
rapidjson::GetParseError_En(geojson.GetParseError()));
|
||||
}
|
||||
|
||||
BOOST_ASSERT(geojson.HasMember("type"));
|
||||
BOOST_ASSERT(geojson["type"].IsString());
|
||||
BOOST_ASSERT(std::strcmp(geojson["type"].GetString(), "FeatureCollection") == 0);
|
||||
BOOST_ASSERT(geojson.HasMember("features"));
|
||||
BOOST_ASSERT(geojson["features"].IsArray());
|
||||
|
||||
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++)
|
||||
{
|
||||
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 {
|
||||
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";
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
struct table_setter : public boost::static_visitor<>
|
||||
{
|
||||
table_setter(sol::table &table, const std::string &key) : table(table), key(key) {}
|
||||
template <typename T> void operator()(const T &value) const { table.set(key, value); }
|
||||
void operator()(const boost::blank &) const { /* ignore */}
|
||||
|
||||
sol::table &table;
|
||||
const std::string &key;
|
||||
};
|
||||
}
|
||||
|
||||
sol::table LocationDependentData::operator()(sol::state &state, const osmium::Way &way) const
|
||||
{
|
||||
if (rtree.empty())
|
||||
return sol::make_object(state, sol::nil);
|
||||
|
||||
// HEURISTIC: use a single node (last) of the way to localize the way
|
||||
// For more complicated scenarios a proper merging of multiple tags
|
||||
// at one or many locations must be provided
|
||||
const auto &nodes = way.nodes();
|
||||
const auto &location = nodes.back().location();
|
||||
const point_t point(location.lon(), location.lat());
|
||||
|
||||
auto table = sol::table(state, sol::create);
|
||||
|
||||
// Search the R-tree and collect a Lua table of tags that correspond to the location
|
||||
for (auto it = rtree.qbegin(
|
||||
boost::geometry::index::intersects(point) &&
|
||||
boost::geometry::index::satisfies([this, &point](const rtree_t::value_type &v) {
|
||||
return boost::geometry::within(point, polygons[v.second].first);
|
||||
}));
|
||||
it != rtree.qend();
|
||||
++it)
|
||||
{
|
||||
for (const auto &pair : polygons[it->second].second)
|
||||
{
|
||||
boost::apply_visitor(table_setter(table, pair.first), pair.second);
|
||||
}
|
||||
}
|
||||
|
||||
return table;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -80,8 +80,9 @@ template <class T> double lonToDouble(T const &object)
|
||||
return static_cast<double>(util::toFloating(object.lon));
|
||||
}
|
||||
|
||||
Sol2ScriptingEnvironment::Sol2ScriptingEnvironment(const std::string &file_name)
|
||||
: file_name(file_name)
|
||||
Sol2ScriptingEnvironment::Sol2ScriptingEnvironment(
|
||||
const std::string &file_name, const boost::filesystem::path &location_dependent_data_path)
|
||||
: file_name(file_name), location_dependent_data(location_dependent_data_path)
|
||||
{
|
||||
util::Log() << "Using script " << file_name;
|
||||
}
|
||||
@@ -666,7 +667,7 @@ LuaScriptingContext &Sol2ScriptingEnvironment::GetSol2Context()
|
||||
auto &ref = script_contexts.local(initialized);
|
||||
if (!initialized)
|
||||
{
|
||||
ref = std::make_unique<LuaScriptingContext>();
|
||||
ref = std::make_unique<LuaScriptingContext>(location_dependent_data);
|
||||
InitContext(*ref);
|
||||
}
|
||||
|
||||
@@ -983,7 +984,7 @@ void LuaScriptingContext::ProcessWay(const osmium::Way &way,
|
||||
way_function(profile_table, way, result, relations);
|
||||
break;
|
||||
case 2:
|
||||
way_function(profile_table, way, result);
|
||||
way_function(profile_table, way, result, location_dependent_data(state, way));
|
||||
break;
|
||||
case 1:
|
||||
case 0:
|
||||
|
||||
@@ -10,7 +10,8 @@ namespace osrm
|
||||
|
||||
void extract(const extractor::ExtractorConfig &config)
|
||||
{
|
||||
extractor::Sol2ScriptingEnvironment scripting_environment(config.profile_path.string().c_str());
|
||||
extractor::Sol2ScriptingEnvironment scripting_environment(config.profile_path.string(),
|
||||
config.location_dependent_data_path);
|
||||
extractor::Extractor(config).run(scripting_environment);
|
||||
}
|
||||
|
||||
|
||||
@@ -62,7 +62,11 @@ return_code parseArguments(int argc,
|
||||
->implicit_value(true)
|
||||
->default_value(false),
|
||||
"Save conditional restrictions found during extraction to disk for use "
|
||||
"during contraction");
|
||||
"during contraction")("location-dependent-data",
|
||||
boost::program_options::value<boost::filesystem::path>(
|
||||
&extractor_config.location_dependent_data_path)
|
||||
->composing(),
|
||||
"GeoJSON files with location-dependent data");
|
||||
|
||||
bool dummy;
|
||||
// hidden options, will be allowed on command line, but will not be
|
||||
|
||||
Reference in New Issue
Block a user