Conditional turn restriction support (#3841)
* optionally include condition and via node coords in InputRestrictionContainer * only write conditionals to disk, custom serialization for restrictions * conditional turn lookup, reuse timezone validation from extract-conditionals * adapt updater to use coordinates/osm ids, remove internal to external map * add utc time now parameter to contraction * only compile timezone code where libshp is found, adapt test running * slight refactor, more tests * catch invalid via nodes in restriction parsing, set default cucumber origin to guinée * add another run to test mld routed paths * cosmetic review changes * Simplify Timezoner for windows build * Split declaration and parsing parts for opening hours * adjust conditional tests to run without shapefiles * always include parse conditionals option * Adjust travis timeout * Added dummy TZ shapefile with test timezone polygons * [skip ci] update changelog
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
#include "util/timezones.hpp"
|
||||
#include "util/exception.hpp"
|
||||
#include "util/log.hpp"
|
||||
|
||||
#include <boost/scope_exit.hpp>
|
||||
|
||||
#ifdef ENABLE_SHAPEFILE
|
||||
#include <shapefil.h>
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
// 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
|
||||
namespace osrm
|
||||
{
|
||||
namespace updater
|
||||
{
|
||||
|
||||
bool SupportsShapefiles()
|
||||
{
|
||||
#ifdef ENABLE_SHAPEFILE
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
Timezoner::Timezoner(std::string tz_filename, std::time_t utc_time_now)
|
||||
{
|
||||
util::Log() << "Time zone validation based on UTC time : " << utc_time_now;
|
||||
// Thread safety: MT-Unsafe const:env
|
||||
default_time = *gmtime(&utc_time_now);
|
||||
LoadLocalTimesRTree(tz_filename, utc_time_now);
|
||||
}
|
||||
|
||||
void Timezoner::LoadLocalTimesRTree(const std::string &tz_shapes_filename, std::time_t utc_time)
|
||||
{
|
||||
if (tz_shapes_filename.empty())
|
||||
return;
|
||||
#ifdef ENABLE_SHAPEFILE
|
||||
// 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;
|
||||
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 = rtree_t(polygons);
|
||||
#endif
|
||||
}
|
||||
|
||||
struct tm Timezoner::operator()(const point_t &point) const
|
||||
{
|
||||
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 default_time;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user