Added osrm-extract-conditionals tool

This commit is contained in:
Michael Krasnyk 2016-12-20 11:16:05 +01:00 committed by Patrick Niklaus
parent 7961fa8863
commit 337ecefa45
5 changed files with 719 additions and 8 deletions

View File

@ -14,6 +14,8 @@
- Datafile versioning is now based on OSRM semver values, rather than source code checksums. - Datafile versioning is now based on OSRM semver values, rather than source code checksums.
Datafiles are compatible between patch levels, but incompatible between minor version or higher bumps. Datafiles are compatible between patch levels, but incompatible between minor version or higher bumps.
- libOSRM now creates an own watcher thread then used in shared memory mode to listen for data updates - libOSRM now creates an own watcher thread then used in shared memory mode to listen for data updates
- Tools:
- Added osrm-extract-conditionals tool for checking conditional values in OSM data
# 5.5.1 # 5.5.1
- Changes from 5.5.0 - Changes from 5.5.0

View File

@ -623,6 +623,14 @@ if(BUILD_TOOLS)
target_link_libraries(osrm-io-benchmark ${BOOST_BASE_LIBRARIES}) target_link_libraries(osrm-io-benchmark ${BOOST_BASE_LIBRARIES})
install(TARGETS osrm-io-benchmark DESTINATION bin) install(TARGETS osrm-io-benchmark DESTINATION bin)
find_package(Shapefile) # package libshp-dev
if(Shapefile_FOUND)
add_executable(osrm-extract-conditionals src/tools/extract-conditionals.cpp $<TARGET_OBJECTS:UTIL>)
target_include_directories(osrm-extract-conditionals PRIVATE ${LIBSHAPEFILE_INCLUDE_DIR})
target_link_libraries(osrm-extract-conditionals ${OSMIUM_LIBRARIES} ${BOOST_BASE_LIBRARIES} ${Boost_PROGRAM_OPTIONS_LIBRARY} ${LIBSHAPEFILE_LIBRARY})
install(TARGETS osrm-extract-conditionals DESTINATION bin)
endif()
endif() endif()
if (ENABLE_ASSERTIONS) if (ENABLE_ASSERTIONS)

21
cmake/FindShapefile.cmake Normal file
View File

@ -0,0 +1,21 @@
# - Try to find Shapefile C Library
# http://shapelib.maptools.org/
#
# Exports:
# Shapefile_FOUND
# LIBSHAPEFILE_INCLUDE_DIR
# LIBSHAPEFILE_LIBRARY
# Hints:
# LIBSHAPEFILE_LIBRARY_DIR
find_path(LIBSHAPEFILE_INCLUDE_DIR
shapefil.h)
find_library(LIBSHAPEFILE_LIBRARY
NAMES shp
HINTS "${LIBSHAPEFILE_LIBRARY_DIR}")
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Shapefile DEFAULT_MSG
LIBSHAPEFILE_LIBRARY LIBSHAPEFILE_INCLUDE_DIR)
mark_as_advanced(LIBSHAPEFILE_INCLUDE_DIR LIBSHAPEFILE_LIBRARY)

View File

@ -374,15 +374,16 @@ struct opening_hours_grammar : qi::grammar<Iterator, Skipper, std::vector<Openin
; ;
// Selectors // Selectors
selector_sequence = (wide_range_selectors(_a) || small_range_selectors(_a))[_val = _a]; selector_sequence = (wide_range_selectors(_a) >> small_range_selectors(_a))[_val = _a];
wide_range_selectors wide_range_selectors
= (monthday_selector(_r1) = (-monthday_selector(_r1)
|| year_selector(_r1) >> -year_selector(_r1)
|| week_selector(_r1) // TODO week_selector >> -week_selector(_r1) // TODO week_selector
) >> -lit(':'); ) >> -lit(':')
;
small_range_selectors = (weekday_selector(_r1) >> (&~lit(',') | eoi)) || time_selector(_r1); small_range_selectors = -(weekday_selector(_r1) >> (&~lit(',') | eoi)) >> -time_selector(_r1);
// Time selector // Time selector
time_selector = (timespan % ',')[ph::bind(&OpeningHours::times, _r1) = _1]; time_selector = (timespan % ',')[ph::bind(&OpeningHours::times, _r1) = _1];
@ -458,7 +459,7 @@ struct opening_hours_grammar : qi::grammar<Iterator, Skipper, std::vector<Openin
date_offset = (plus_or_minus >> wday) | day_offset; date_offset = (plus_or_minus >> wday) | day_offset;
date_from date_from
= ((-year[_a = _1] >> (month[_b = _1] || (daynum[_c = _1] >> (&~lit(':') | eoi)))) = ((-year[_a = _1] >> ((month[_b = _1] >> -daynum[_c = _1]) | daynum[_c = _1]))
| variable_date) | variable_date)
[_val = ph::construct<OpeningHours::Monthday>(_a, _b, _c)] [_val = ph::construct<OpeningHours::Monthday>(_a, _b, _c)]
; ;
@ -504,7 +505,10 @@ struct opening_hours_grammar : qi::grammar<Iterator, Skipper, std::vector<Openin
("Sa", 6) ("Sa", 6)
; ;
daynum = uint2_p[_pass = bind([](unsigned x) { return 01 <= x && x <= 31; }, _1), _val = _1]; daynum
= uint2_p[_pass = bind([](unsigned x) { return 01 <= x && x <= 31; }, _1), _val = _1]
>> (&~lit(':') | eoi)
;
weeknum = uint2_p[_pass = bind([](unsigned x) { return 01 <= x && x <= 53; }, _1), _val = _1]; weeknum = uint2_p[_pass = bind([](unsigned x) { return 01 <= x && x <= 53; }, _1), _val = _1];

View File

@ -0,0 +1,676 @@
#include "util/conditional_restrictions.hpp"
#include "util/exception.hpp"
#include "util/log.hpp"
#include "util/opening_hours.hpp"
#include "util/version.hpp"
#include <boost/algorithm/string/predicate.hpp>
#include <boost/filesystem.hpp>
#include <boost/fusion/include/adapt_adt.hpp>
#include <boost/geometry.hpp>
#include <boost/geometry/index/rtree.hpp>
#include <boost/program_options.hpp>
#include <boost/scope_exit.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/support_line_pos_iterator.hpp>
#include <osmium/handler.hpp>
#include <osmium/index/map/sparse_mem_array.hpp>
#include <osmium/io/any_input.hpp>
#include <osmium/tags/regex_filter.hpp>
#include <osmium/visitor.hpp>
#include <shapefil.h>
#include <fstream>
#include <iostream>
#include <unordered_map>
#include <unordered_set>
#include <chrono>
#include <cstdlib>
//#include <ctime>
// Program arguments parsing functions
auto ParseGlobalArguments(int argc, char *argv[])
{
namespace po = boost::program_options;
po::options_description global_options("Global options");
global_options.add_options()("version,v", "Show version")("help,h", "Show this help message");
po::options_description commands("Commands");
commands.add_options()("command", po::value<std::string>())(
"subargs", po::value<std::vector<std::string>>());
po::positional_options_description pos;
pos.add("command", 1).add("subargs", -1);
po::variables_map vm;
std::vector<std::string> command_options;
try
{
po::options_description cmdline_options;
cmdline_options.add(global_options).add(commands);
po::parsed_options parsed = po::command_line_parser(argc, argv)
.options(cmdline_options)
.positional(pos)
.allow_unregistered()
.run();
// split parsed options
auto command = std::find_if(parsed.options.begin(), parsed.options.end(), [](auto &o) {
return o.string_key == "command";
});
for (auto it = command; it != parsed.options.end(); ++it)
{
std::copy(it->original_tokens.begin(),
it->original_tokens.end(),
back_inserter(command_options));
}
parsed.options.erase(command, parsed.options.end());
po::store(parsed, vm);
}
catch (const po::error &e)
{
throw osrm::util::exception(e.what());
}
if (vm.count("version"))
{
std::cout << OSRM_VERSION << std::endl;
std::exit(EXIT_SUCCESS);
}
if (vm.count("help") || command_options.empty())
{
const auto *executable = argv[0];
std::cout << boost::filesystem::path(executable).filename().string()
<< " [<global options>] <command> [<args>]\n\n"
"Supported commands are:\n\n"
" cond-dump Save conditional restrictions in CSV format\n"
" cond-check Check conditional restrictions and save in CSV format\n"
<< "\n"
<< global_options;
std::exit(EXIT_SUCCESS);
}
return command_options;
}
void ParseRestrictionsDumpArguments(const char *executable,
const std::vector<std::string> &arguments,
std::string &osm_filename,
std::string &csv_filename)
{
namespace po = boost::program_options;
po::options_description options("cond-dump");
options.add_options()("help,h", "Show this help message")(
"input,i",
po::value<std::string>(&osm_filename),
"OSM input file in .osm, .osm.bz2 or .osm.pbf format")(
"output,o",
po::value<std::string>(&csv_filename),
"Output conditional restrictions file in CSV format");
po::positional_options_description pos;
pos.add("input", 1).add("output", 1);
po::variables_map vm;
po::store(po::command_line_parser(arguments).options(options).positional(pos).run(), vm);
if (vm.count("help"))
{
std::cout << boost::filesystem::path(executable).filename().string()
<< " cond-dump [<args>]\n\n"
<< options;
std::exit(EXIT_SUCCESS);
}
po::notify(vm);
}
void ParseRestrictionsCheckArguments(const char *executable,
const std::vector<std::string> &arguments,
std::string &input_filename,
std::string &output_filename,
std::string &tz_filename,
std::time_t &utc_time,
std::int64_t &restriction_value)
{
namespace po = boost::program_options;
po::options_description options("cond-dump");
options.add_options()("help,h", "Show this help message")(
"input,i",
po::value<std::string>(&input_filename),
"Input conditional restrictions file in CSV format")(
"output,o",
po::value<std::string>(&output_filename),
"Output conditional restrictions file in CSV format")(
"tz-shapes,s",
po::value<std::string>(&tz_filename)->default_value(tz_filename),
"Timezones shape file without extension")(
"utc-time,t",
po::value<std::time_t>(&utc_time)->default_value(utc_time),
"UTC time-stamp [default=current UTC time]")(
"value,v",
po::value<std::int64_t>(&restriction_value)->default_value(restriction_value),
"Restriction value for active time conditional restrictions");
po::positional_options_description pos;
pos.add("input", 1).add("output", 1);
po::variables_map vm;
po::store(po::command_line_parser(arguments).options(options).positional(pos).run(), vm);
if (vm.count("help"))
{
std::cout << boost::filesystem::path(executable).filename().string()
<< " cond-check [<args>]\n\n"
<< options;
std::exit(EXIT_SUCCESS);
}
po::notify(vm);
}
// Data types and functions for conditional restriction
struct ConditionalRestriction
{
osmium::object_id_type from;
osmium::object_id_type via;
osmium::object_id_type to;
std::string tag;
std::string value;
std::string condition;
};
struct LocatedConditionalRestriction
{
osmium::Location location;
ConditionalRestriction restriction;
};
BOOST_FUSION_ADAPT_ADT(LocatedConditionalRestriction,
(obj.restriction.from, obj.restriction.from = val) //
(obj.restriction.via, obj.restriction.via = val) //
(obj.restriction.to, obj.restriction.to = val) //
(obj.restriction.tag, obj.restriction.tag = val) //
(obj.restriction.value, obj.restriction.value = val) //
(obj.restriction.condition, obj.restriction.condition = val) //
(obj.location.lon(), obj.location.set_lon(val)) //
(obj.location.lat(), obj.location.set_lat(val)))
// The first pass relations handler that collects conditional restrictions
class ConditionalRestrictionsCollector : public osmium::handler::Handler
{
public:
ConditionalRestrictionsCollector(std::vector<ConditionalRestriction> &restrictions)
: restrictions(restrictions)
{
tag_filter.add(true, std::regex("^restriction.*:conditional$"));
}
void relation(const osmium::Relation &relation) const
{
// Check if relation contains any ":conditional" tag
const osmium::TagList &tags = relation.tags();
typename decltype(tag_filter)::iterator first(tag_filter, tags.begin(), tags.end());
typename decltype(tag_filter)::iterator last(tag_filter, tags.end(), tags.end());
if (first == last)
return;
// Get member references of from(way) -> via(node) -> to(way)
auto from = invalid_id, via = invalid_id, to = invalid_id;
for (const auto &member : relation.members())
{
if (member.ref() == 0)
continue;
if (member.type() == osmium::item_type::node && strcmp(member.role(), "via") == 0)
{
via = member.ref();
}
else if (member.type() == osmium::item_type::way && strcmp(member.role(), "from") == 0)
{
from = member.ref();
}
else if (member.type() == osmium::item_type::way && strcmp(member.role(), "to") == 0)
{
to = member.ref();
}
}
if (from == invalid_id || via == invalid_id || to == invalid_id)
return;
for (; first != last; ++first)
{
// Parse condition and add independent value/condition pairs
const auto &parsed = osrm::util::ParseConditionalRestrictions(first->value());
if (parsed.empty())
{
osrm::util::Log(logWARNING) << "Conditional restriction parsing failed for \""
<< first->value() << "\" at the turn " << from << " -> "
<< via << " -> " << to;
continue;
}
for (auto &restriction : parsed)
{
restrictions.push_back(
{from, via, to, first->key(), restriction.value, restriction.condition});
}
}
}
private:
const osmium::object_id_type invalid_id = std::numeric_limits<osmium::object_id_type>::max();
osmium::tags::Filter<std::regex> tag_filter;
std::vector<ConditionalRestriction> &restrictions;
};
// The second pass handler that collects related nodes and ways.
// process_restrictions method calls for every collected conditional restriction
// a callback function with the prototype:
// (location, from node, via node, to node, tag, value, condition)
// If the restriction value starts with "only_" the callback function will be called
// for every edge adjacent to `via` node but not containing `to` node.
class ConditionalRestrictionsHandler : public osmium::handler::Handler
{
public:
ConditionalRestrictionsHandler(const std::vector<ConditionalRestriction> &restrictions)
: restrictions(restrictions)
{
for (auto &restriction : restrictions)
related_vias.insert(restriction.via);
via_adjacency.reserve(restrictions.size() * 2);
}
void node(const osmium::Node &node)
{
const osmium::object_id_type id = node.id();
if (related_vias.find(id) != related_vias.end())
{
location_storage.set(static_cast<osmium::unsigned_object_id_type>(id), node.location());
}
}
void way(const osmium::Way &way)
{
const auto &nodes = way.nodes();
if (related_vias.find(nodes.front().ref()) != related_vias.end())
via_adjacency.push_back({nodes.front().ref(), way.id(), nodes[1].ref()});
if (related_vias.find(nodes.back().ref()) != related_vias.end())
via_adjacency.push_back({nodes.back().ref(), way.id(), nodes[nodes.size() - 2].ref()});
}
template <typename Callback> void process_restrictions(Callback callback)
{
location_storage.sort();
std::sort(via_adjacency.begin(), via_adjacency.end());
auto adjacent_nodes = [this](auto node) {
auto first = std::lower_bound(
via_adjacency.begin(), via_adjacency.end(), adjacency_type{node, 0, 0});
auto last =
std::upper_bound(first, via_adjacency.end(), adjacency_type{node + 1, 0, 0});
return std::make_pair(first, last);
};
auto find_node = [](auto nodes, auto way) {
return std::find_if(
nodes.first, nodes.second, [way](const auto &n) { return std::get<1>(n) == way; });
};
for (auto &restriction : restrictions)
{
auto nodes = adjacent_nodes(restriction.via);
auto from = find_node(nodes, restriction.from);
if (from == nodes.second)
continue;
const auto &location =
location_storage.get(static_cast<osmium::unsigned_object_id_type>(restriction.via));
if (boost::algorithm::starts_with(restriction.value, "only_"))
{
for (auto it = nodes.first; it != nodes.second; ++it)
{
if (std::get<1>(*it) == restriction.to)
continue;
callback(location,
std::get<2>(*from),
restriction.via,
std::get<2>(*it),
restriction.tag,
restriction.value,
restriction.condition);
}
}
else
{
auto to = find_node(nodes, restriction.to);
if (to != nodes.second)
{
callback(location,
std::get<2>(*from),
restriction.via,
std::get<2>(*to),
restriction.tag,
restriction.value,
restriction.condition);
}
}
}
}
private:
using index_type =
osmium::index::map::SparseMemArray<osmium::unsigned_object_id_type, osmium::Location>;
using adjacency_type =
std::tuple<osmium::object_id_type, osmium::object_id_type, osmium::object_id_type>;
const std::vector<ConditionalRestriction> &restrictions;
std::unordered_set<osmium::object_id_type> related_vias;
std::vector<adjacency_type> via_adjacency;
index_type location_storage;
};
int RestrictionsDumpCommand(const char *executable, const std::vector<std::string> &arguments)
{
std::string osm_filename, csv_filename;
ParseRestrictionsDumpArguments(executable, arguments, osm_filename, csv_filename);
// Read OSM input file
const osmium::io::File input_file(osm_filename);
// Read relations
std::vector<ConditionalRestriction> conditional_restrictions_prior;
ConditionalRestrictionsCollector restrictions_collector(conditional_restrictions_prior);
osmium::io::Reader reader1(
input_file, osmium::io::read_meta::no, osmium::osm_entity_bits::relation);
osmium::apply(reader1, restrictions_collector);
reader1.close();
// Handle nodes and ways in relations
ConditionalRestrictionsHandler restrictions_handler(conditional_restrictions_prior);
osmium::io::Reader reader2(input_file,
osmium::io::read_meta::no,
osmium::osm_entity_bits::node | osmium::osm_entity_bits::way);
osmium::apply(reader2, restrictions_handler);
reader2.close();
// Prepare output stream
std::streambuf *buf = std::cout.rdbuf();
std::ofstream of;
if (!csv_filename.empty())
{
of.open(csv_filename, std::ios::binary);
buf = of.rdbuf();
}
std::ostream stream(buf);
// Process collected restrictions and print CSV output
restrictions_handler.process_restrictions([&stream](const osmium::Location &location,
osmium::object_id_type from,
osmium::object_id_type via,
osmium::object_id_type to,
const std::string &tag,
const std::string &value,
const std::string &condition) {
stream << from << "," << via << "," << to << "," << tag << "," << value << ",\""
<< condition << "\""
<< "," << std::setprecision(6) << location.lon() << "," << std::setprecision(6)
<< location.lat() << "\n";
});
return EXIT_SUCCESS;
}
// Time zone shape polygons loaded in R-tree
// local_time_t is a pair of a time zone shape polygon and the corresponding local time
// rtree_t is a lookup R-tree that maps a geographic point to an index in a local_time_t vector
using point_t = boost::geometry::model::
point<double, 2, boost::geometry::cs::spherical_equatorial<boost::geometry::degree>>;
using polygon_t = boost::geometry::model::polygon<point_t>;
using box_t = boost::geometry::model::box<point_t>;
using rtree_t =
boost::geometry::index::rtree<std::pair<box_t, size_t>, boost::geometry::index::rstar<8>>;
using local_time_t = std::pair<polygon_t, struct tm>;
// Function loads time zone shape polygons, computes a zone local time for utc_time,
// creates a lookup R-tree and returns a lambda function that maps a point
// to the corresponding local time
auto LoadLocalTimesRTree(const std::string &tz_shapes_filename, std::time_t utc_time)
{
// Load time zones shapes and collect local times of utc_time
auto shphandle = SHPOpen(tz_shapes_filename.c_str(), "rb");
auto dbfhandle = DBFOpen(tz_shapes_filename.c_str(), "rb");
BOOST_SCOPE_EXIT(&shphandle, &dbfhandle)
{
DBFClose(dbfhandle);
SHPClose(shphandle);
}
BOOST_SCOPE_EXIT_END
if (!shphandle || !dbfhandle)
{
throw osrm::util::exception("failed to open " + tz_shapes_filename + ".shp or " +
tz_shapes_filename + ".dbf file");
}
int num_entities, shape_type;
SHPGetInfo(shphandle, &num_entities, &shape_type, NULL, NULL);
if (num_entities != DBFGetRecordCount(dbfhandle))
{
throw osrm::util::exception("inconsistent " + tz_shapes_filename + ".shp and " +
tz_shapes_filename + ".dbf files");
}
const auto tzid = DBFGetFieldIndex(dbfhandle, "TZID");
if (tzid == -1)
{
throw osrm::util::exception("did not find field called 'TZID' in the " +
tz_shapes_filename + ".dbf file");
}
// Lambda function that returns local time in the tzname time zone
// Thread safety: MT-Unsafe const:env
std::unordered_map<std::string, struct tm> local_time_memo;
auto get_local_time_in_tz = [utc_time, &local_time_memo](const char *tzname) {
auto it = local_time_memo.find(tzname);
if (it == local_time_memo.end())
{
struct tm timeinfo;
setenv("TZ", tzname, 1);
tzset();
localtime_r(&utc_time, &timeinfo);
it = local_time_memo.insert({tzname, timeinfo}).first;
}
return it->second;
};
// Get all time zone shapes and save local times in a vector
std::vector<rtree_t::value_type> polygons;
std::vector<local_time_t> local_times;
for (int shape = 0; shape < num_entities; ++shape)
{
auto object = SHPReadObject(shphandle, shape);
BOOST_SCOPE_EXIT(&object) { SHPDestroyObject(object); }
BOOST_SCOPE_EXIT_END
if (object && object->nSHPType == SHPT_POLYGON)
{
// Find time zone polygon and place its bbox in into R-Tree
polygon_t polygon;
for (int vertex = 0; vertex < object->nVertices; ++vertex)
{
polygon.outer().emplace_back(object->padfX[vertex], object->padfY[vertex]);
}
polygons.emplace_back(boost::geometry::return_envelope<box_t>(polygon),
local_times.size());
// Get time zone name and emplace polygon and local time for the UTC input
const auto tzname = DBFReadStringAttribute(dbfhandle, shape, tzid);
local_times.emplace_back(local_time_t{polygon, get_local_time_in_tz(tzname)});
// std::cout << boost::geometry::dsv(boost::geometry::return_envelope<box_t>(polygon))
// << " " << tzname << " " << asctime(&local_times.back().second);
}
}
// Create R-tree for collected shape polygons
rtree_t rtree(polygons);
// Return a lambda function that maps the input point and UTC time to the local time
// binds rtree and local_times
return [rtree, local_times](const point_t &point) {
std::vector<rtree_t::value_type> result;
rtree.query(boost::geometry::index::intersects(point), std::back_inserter(result));
for (const auto v : result)
{
const auto index = v.second;
if (boost::geometry::within(point, local_times[index].first))
return local_times[index].second;
}
return tm{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
};
}
int RestrictionsCheckCommand(const char *executable, const std::vector<std::string> &arguments)
{
std::string input_filename, output_filename;
std::string tz_filename = "tz_world";
std::time_t utc_time = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
std::int64_t restriction_value = 32000;
ParseRestrictionsCheckArguments(executable,
arguments,
input_filename,
output_filename,
tz_filename,
utc_time,
restriction_value);
// Prepare input stream
std::streambuf *input_buffer = std::cin.rdbuf();
std::ifstream input_file;
if (!input_filename.empty())
{
input_file.open(input_filename, std::ios::binary);
input_buffer = input_file.rdbuf();
}
std::istream input_stream(input_buffer);
input_stream.unsetf(std::ios::skipws);
boost::spirit::istream_iterator sfirst(input_stream);
boost::spirit::istream_iterator slast;
boost::spirit::line_pos_iterator<boost::spirit::istream_iterator> first(sfirst);
boost::spirit::line_pos_iterator<boost::spirit::istream_iterator> last(slast);
// Parse CSV file
namespace qi = boost::spirit::qi;
std::vector<LocatedConditionalRestriction> conditional_restrictions;
qi::rule<decltype(first), LocatedConditionalRestriction()> csv_line =
qi::ulong_long >> ',' >> qi::ulong_long >> ',' >> qi::ulong_long >> ',' >>
qi::as_string[+(~qi::lit(','))] >> ',' >> qi::as_string[+(~qi::lit(','))] >> ',' >> '"' >>
qi::as_string[qi::no_skip[*(~qi::lit('"'))]] >> '"' >> ',' >> qi::double_ >> ',' >>
qi::double_;
const auto ok = qi::phrase_parse(first, last, *(csv_line), qi::space, conditional_restrictions);
if (!ok || first != last)
{
osrm::util::Log(logERROR) << input_filename << ":" << first.position() << ": parsing error";
return EXIT_FAILURE;
}
// Load R-tree with local times
auto get_local_time = LoadLocalTimesRTree(tz_filename, utc_time);
// Prepare output stream
std::streambuf *output_buffer = std::cout.rdbuf();
std::ofstream output_file;
if (!output_filename.empty())
{
output_file.open(output_filename, std::ios::binary);
output_buffer = output_file.rdbuf();
}
std::ostream output_stream(output_buffer);
// For each conditional restriction if condition is active than print a line
for (auto &value : conditional_restrictions)
{
const auto &location = value.location;
const auto &restriction = value.restriction;
// Get local time of the restriction
const auto &local_time = get_local_time(point_t{location.lon(), location.lat()});
// TODO: check restriction type [:<transportation mode>][:<direction>]
// http://wiki.openstreetmap.org/wiki/Conditional_restrictions#Tagging
// TODO: parsing will fail for combined conditions, e.g. Sa-Su AND weight>7
// http://wiki.openstreetmap.org/wiki/Conditional_restrictions#Combined_conditions:_AND
const auto &opening_hours = osrm::util::ParseOpeningHours(restriction.condition);
if (opening_hours.empty())
{
osrm::util::Log(logWARNING)
<< "Condition parsing failed for \"" << restriction.condition << "\" at the turn "
<< restriction.from << " -> " << restriction.via << " -> " << restriction.to;
continue;
}
if (osrm::util::CheckOpeningHours(opening_hours, local_time))
{
output_stream << restriction.from << "," << restriction.via << "," << restriction.to
<< "," << restriction_value << "\n";
}
}
return EXIT_SUCCESS;
}
int main(int argc, char *argv[]) try
{
osrm::util::LogPolicy::GetInstance().Unmute();
// Parse program argument
auto arguments = ParseGlobalArguments(argc, argv);
BOOST_ASSERT(!arguments.empty());
std::unordered_map<std::string, int (*)(const char *, const std::vector<std::string> &)>
commands = {{"cond-dump", &RestrictionsDumpCommand},
{"cond-check", &RestrictionsCheckCommand}};
auto command = commands.find(arguments.front());
if (command == commands.end())
{
std::cerr << "Unknown command: " << arguments.front() << "\n\n"
<< "Available commands:\n";
for (auto &command : commands)
std::cerr << " " << command.first << "\n";
return EXIT_FAILURE;
}
arguments.erase(arguments.begin());
return command->second(argv[0], arguments);
}
catch (const std::exception &e)
{
osrm::util::Log(logERROR) << e.what();
return EXIT_FAILURE;
}