Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b168eca6d5 | |||
| e3276324b9 | |||
| f80e5db346 | |||
| 35550d8c0a | |||
| b68d79407e | |||
| 27ed69b08f | |||
| cd8fb82215 | |||
| 5c8e2b6f78 | |||
| 9d30817294 | |||
| 5ee3c4de9f | |||
| afbcb3d38c | |||
| de1d5f199f | |||
| 9158f69ea0 | |||
| 2cfd9c8d01 | |||
| 5026741652 | |||
| 3d77714c36 | |||
| 03e83ec6a0 | |||
| 9315dc1c73 | |||
| b8bb12b2e2 | |||
| bd1532847c | |||
| 3602b58517 | |||
| d7035291ea | |||
| 6b0bcb5171 | |||
| 6b8f3c7fef |
+10
-1
@@ -1,6 +1,7 @@
|
||||
# 5.8.0
|
||||
# 5.8.0 RC1
|
||||
- Changes from 5.7
|
||||
- API:
|
||||
- polyline6 support in request string
|
||||
- new parameter `approaches` for `route`, `table`, `trip` and `nearest` requests. This parameter keep waypoints on the curb side.
|
||||
'approaches' accepts both 'curb' and 'unrestricted' values.
|
||||
Note : the curb side depend on the `ProfileProperties::left_hand_driving`, it's a global property set once by the profile. If you are working with a planet dataset, the api will be wrong in some countries, and right in others.
|
||||
@@ -10,13 +11,21 @@
|
||||
- `osrm-partition` now ensures it is called before `osrm-contract` and removes inconsitent .hsgr files automatically.
|
||||
- Features
|
||||
- Added conditional restriction support with `parse-conditional-restrictions=true|false` to osrm-extract. This option saves conditional turn restrictions to the .restrictions file for parsing by contract later. Added `parse-conditionals-from-now=utc time stamp` and `--time-zone-file=/path/to/file` to osrm-contract
|
||||
- Command-line tools (osrm-extract, osrm-contract, osrm-routed, etc) now return error codes and legible error messages for common problem scenarios, rather than ugly C++ crashes
|
||||
- Speed up pre-processing by only running the Lua `node_function` for nodes that have tags. Cuts OSM file parsing time in half.
|
||||
- osrm-extract now performs generation of edge-expanded-edges using all available CPUs, which should make osrm-extract significantly faster on multi-CPU machines
|
||||
- Files
|
||||
- .osrm.nodes file was renamed to .nbg_nodes and .ebg_nodes was added
|
||||
- Guidance
|
||||
- #4075 Changed counting of exits on service roundabouts
|
||||
- Debug Tiles
|
||||
- added support for visualising turn penalties to the MLD plugin
|
||||
- added support for showing the rate (reciprocal of weight) on each edge when used
|
||||
- added support for turn weights in addition to turn durations in debug tiles
|
||||
- Bugfixes
|
||||
- Fixed a copy/paste issue assigning wrong directions in similar turns (left over right)
|
||||
- #4074: fixed a bug that would announce entering highway ramps as u-turns
|
||||
- #4122: osrm-routed/libosrm should throw exception when a dataset incompatible with the requested algorithm is loaded
|
||||
|
||||
# 5.7.1
|
||||
- Bugfixes
|
||||
|
||||
@@ -126,6 +126,7 @@ file(GLOB UpdaterGlob src/updater/*.cpp)
|
||||
file(GLOB StorageGlob src/storage/*.cpp)
|
||||
file(GLOB ServerGlob src/server/*.cpp src/server/**/*.cpp)
|
||||
file(GLOB EngineGlob src/engine/*.cpp src/engine/**/*.cpp)
|
||||
file(GLOB ErrorcodesGlob src/osrm/errorcodes.cpp)
|
||||
|
||||
add_library(UTIL OBJECT ${UtilGlob})
|
||||
add_library(EXTRACTOR OBJECT ${ExtractorGlob})
|
||||
|
||||
+1
-1
@@ -3,5 +3,5 @@ module.exports = {
|
||||
verify: '--strict --tags ~@stress --tags ~@todo -f progress --require features/support --require features/step_definitions',
|
||||
todo: '--strict --tags @todo --require features/support --require features/step_definitions',
|
||||
all: '--strict --require features/support --require features/step_definitions',
|
||||
mld: '--strict --tags ~@stress --tags ~@todo --tags ~@alternative --tags ~@matrix --tags ~@trip --tags --require features/support --require features/step_definitions -f progress'
|
||||
mld: '--strict --tags ~@stress --tags ~@todo --tags ~@alternative --tags ~@matrix --tags ~@trip --require features/support --require features/step_definitions -f progress'
|
||||
}
|
||||
|
||||
+5
-2
@@ -15,7 +15,7 @@ GET /{service}/{version}/{profile}/{coordinates}[.{format}]?option=value&option=
|
||||
| `service` | One of the following values: [`route`](#route-service), [`nearest`](#nearest-service), [`table`](#table-service), [`match`](#match-service), [`trip`](#trip-service), [`tile`](#tile-service) |
|
||||
| `version` | Version of the protocol implemented by the service. `v1` for all OSRM 5.x installations |
|
||||
| `profile` | Mode of transportation, is determined statically by the Lua profile that is used to prepare the data using `osrm-extract`. Typically `car`, `bike` or `foot` if using one of the supplied profiles. |
|
||||
| `coordinates`| String of format `{longitude},{latitude};{longitude},{latitude}[;{longitude},{latitude} ...]` or `polyline({polyline})`. |
|
||||
| `coordinates`| String of format `{longitude},{latitude};{longitude},{latitude}[;{longitude},{latitude} ...]` or `polyline({polyline}) or polyline6({polyline6})`. |
|
||||
| `format`| Only `json` is supported at the moment. This parameter is optional and defaults to `json`. |
|
||||
|
||||
Passing any `option=value` is optional. `polyline` follows Google's polyline format with precision 5 by default and can be generated using [this package](https://www.npmjs.com/package/polyline).
|
||||
@@ -419,8 +419,10 @@ Vector tiles contain two layers:
|
||||
| `speed` | `integer` | the speed on that road segment, in km/h |
|
||||
| `is_small` | `boolean` | whether this segment belongs to a small (< 1000 node) [strongly connected component](https://en.wikipedia.org/wiki/Strongly_connected_component) |
|
||||
| `datasource` | `string` | the source for the speed value (normally `lua profile` unless you're using the [traffic update feature](https://github.com/Project-OSRM/osrm-backend/wiki/Traffic), in which case it contains the stem of the filename that supplied the speed value for this segment |
|
||||
| `duration` | `float` | how long this segment takes to traverse, in seconds |
|
||||
| `duration` | `float` | how long this segment takes to traverse, in seconds. This value is to calculate the total route ETA. |
|
||||
| `weight ` | `integer` | how long this segment takes to traverse, in units (may differ from `duration` when artificial biasing is applied in the Lua profiles). ACTUAL ROUTING USES THIS VALUE. |
|
||||
| `name` | `string` | the name of the road this segment belongs to |
|
||||
| `rate` | `float` | the value of `length/weight` - analagous to `speed`, but using the `weight` value rather than `duration`, rounded to the nearest integer |
|
||||
|
||||
`turns` layer:
|
||||
|
||||
@@ -429,6 +431,7 @@ Vector tiles contain two layers:
|
||||
| `bearing_in` | `integer` | the absolute bearing that approaches the intersection. -180 to +180, 0 = North, 90 = East |
|
||||
| `turn_angle` | `integer` | the angle of the turn, relative to the `bearing_in`. -180 to +180, 0 = straight ahead, 90 = 90-degrees to the right |
|
||||
| `cost` | `float` | the time we think it takes to make that turn, in seconds. May be negative, depending on how the data model is constructed (some turns get a "bonus"). |
|
||||
| `weight` | `float` | the weight we think it takes to make that turn. May be negative, depending on how the data model is constructed (some turns get a "bonus"). ACTUAL ROUTING USES THIS VALUE |
|
||||
|
||||
|
||||
## Result objects
|
||||
|
||||
@@ -194,6 +194,8 @@ if they can not be matched successfully.
|
||||
- `options.overview` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** Add overview geometry either `full`, `simplified` according to highest zoom level it could be display on, or not at all (`false`). (optional, default `simplified`)
|
||||
- `options.timestamps` **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)<[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)>?** Timestamp of the input location (integers, UNIX-like timestamp).
|
||||
- `options.radiuses` **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)?** Standard deviation of GPS precision used for map matching. If applicable use GPS accuracy. Can be `null` for default value `5` meters or `double >= 0`.
|
||||
- `options.gaps` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** Allows the input track splitting based on huge timestamp gaps between points. Either `split` or `ignore`. (optional, default `split`)
|
||||
- `options.tidy` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** Allows the input track modification to obtain better matching quality for noisy tracks. (optional, default `false`)
|
||||
- `callback` **[Function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function)**
|
||||
|
||||
**Examples**
|
||||
|
||||
@@ -37,3 +37,37 @@ Feature: Basic Roundabout
|
||||
| h,a | gh,ab,ab | depart,roundabout turn left exit-3,arrive |
|
||||
| h,d | gh,cd,cd | depart,roundabout turn straight exit-2,arrive |
|
||||
| h,f | gh,ef,ef | depart,roundabout turn right exit-1,arrive |
|
||||
|
||||
# https://www.openstreetmap.org/way/223225602
|
||||
Scenario: Enter and Exit with changing mode
|
||||
Given the node map
|
||||
"""
|
||||
a
|
||||
b
|
||||
h g c d
|
||||
e
|
||||
f
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes | junction | highway |
|
||||
| ab | | residential |
|
||||
| cd | | residential |
|
||||
| ef | | footway |
|
||||
| gh | | footway |
|
||||
| bgecb | roundabout | residential |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns |
|
||||
| a,d | ab,cd,cd | depart,roundabout turn left exit-1,arrive |
|
||||
| a,f | ab,ef,ef,ef | depart,roundabout turn left exit-1,notification right,arrive |
|
||||
| a,h | ab,bgecb,gh,gh | depart,roundabout turn right exit-1,notification right,arrive |
|
||||
| d,f | cd,ef,ef,ef | depart,roundabout turn sharp left exit-2,notification right,arrive |
|
||||
| d,h | cd,gh,gh,gh | depart,roundabout turn left exit-2,notification right,arrive |
|
||||
| d,a | cd,ab,ab | depart,roundabout turn right exit-1,arrive |
|
||||
| f,h | ef,gh,gh,gh | depart,roundabout turn sharp left exit-3,notification right,arrive |
|
||||
| f,a | ef,ab,ab | depart,roundabout turn straight exit-2,arrive |
|
||||
| f,d | ef,cd,cd | depart,roundabout turn right exit-1,arrive |
|
||||
| h,a | gh,ab,ab | depart,roundabout turn left exit-2,arrive |
|
||||
| h,d | gh,cd,cd | depart,roundabout turn straight exit-1,arrive |
|
||||
| h,f | gh,bgecb,ef,ef | depart,roundabout turn right exit-1,notification right,arrive |
|
||||
|
||||
@@ -14,5 +14,5 @@ Feature: osrm-routed command line options: invalid options
|
||||
Scenario: osrm-routed - Missing file
|
||||
When I try to run "osrm-routed over-the-rainbow.osrm"
|
||||
Then stderr should contain "over-the-rainbow.osrm"
|
||||
And stderr should contain "not found"
|
||||
And stderr should contain "Required files are missing"
|
||||
And it should exit with an error
|
||||
|
||||
@@ -30,3 +30,38 @@ Feature: Alternative route
|
||||
| 3 | 4 | bd,dc,ca,ab,bd,bd | |
|
||||
| 5 | 6 | dc,ca,ab,bd,dc,dc | |
|
||||
| 7 | 8 | ca,ab,bd,dc,ca,ca | |
|
||||
|
||||
@4111
|
||||
Scenario: Alternative Loop Paths with single node path
|
||||
Given the node map
|
||||
"""
|
||||
a1b2c3d
|
||||
|
||||
|
||||
e f
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes | maxspeed |
|
||||
| ab | 30 |
|
||||
| bc | 3 |
|
||||
| cd | 30 |
|
||||
| ae | 30 |
|
||||
| ef | 30 |
|
||||
| fd | 30 |
|
||||
|
||||
And the query options
|
||||
| alternatives | true |
|
||||
|
||||
When I route I should get
|
||||
| from | to | route | alternative |
|
||||
| b | c | bc,bc | ab,ae,ef,fd,cd,cd |
|
||||
#| c | b | bc,bc | cd,fd,ef,ae,ab,ab | # alternative path depends on phantom snapping order
|
||||
| 1 | c | ab,bc,bc | ab,ae,ef,fd,cd,cd |
|
||||
#| c | 1 | bc,ab | cd,fd,ef,ae,ab | # alternative path depends on phantom snapping order
|
||||
| 2 | c | bc,bc | |
|
||||
| c | 2 | bc,bc | |
|
||||
| 1 | 3 | ab,ae,ef,fd,cd | ab,bc,cd |
|
||||
#| 3 | 1 | cd,fd,ef,ae,ab | cd,bc,ab | # alternative path depends on phantom snapping order
|
||||
| b | 3 | bc,cd | ab,ae,ef,fd,cd |
|
||||
#| 3 | b | cd,bc,bc | cd,fd,ef,ae,ab,ab | # alternative path depends on phantom snapping order
|
||||
|
||||
@@ -102,6 +102,9 @@ template <> struct HasShortestPathSearch<mld::Algorithm> final : std::true_type
|
||||
template <> struct HasMapMatching<mld::Algorithm> final : std::true_type
|
||||
{
|
||||
};
|
||||
template <> struct HasGetTileTurns<mld::Algorithm> final : std::true_type
|
||||
{
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+34
-10
@@ -159,12 +159,12 @@ bool Engine<routing_algorithms::ch::Algorithm>::CheckCompability(const EngineCon
|
||||
}
|
||||
else
|
||||
{
|
||||
std::ifstream in(config.storage_config.hsgr_data_path.string().c_str());
|
||||
if (!in)
|
||||
if (!boost::filesystem::exists(config.storage_config.hsgr_data_path))
|
||||
return false;
|
||||
storage::io::FileReader in(config.storage_config.hsgr_data_path,
|
||||
storage::io::FileReader::VerifyFingerprint);
|
||||
|
||||
in.seekg(0, std::ios::end);
|
||||
auto size = in.tellg();
|
||||
auto size = in.GetSize();
|
||||
return size > 0;
|
||||
}
|
||||
}
|
||||
@@ -190,14 +190,38 @@ bool Engine<routing_algorithms::corech::Algorithm>::CheckCompability(const Engin
|
||||
}
|
||||
else
|
||||
{
|
||||
std::ifstream in(config.storage_config.core_data_path.string().c_str());
|
||||
if (!in)
|
||||
if (!boost::filesystem::exists(config.storage_config.core_data_path))
|
||||
return false;
|
||||
storage::io::FileReader in(config.storage_config.core_data_path,
|
||||
storage::io::FileReader::VerifyFingerprint);
|
||||
|
||||
in.seekg(0, std::ios::end);
|
||||
std::size_t size = in.tellg();
|
||||
// An empty core files is only the 8 byte size header plus the 8 byte Fingerprint.
|
||||
return size > sizeof(std::uint64_t) + sizeof(util::FingerPrint);
|
||||
auto size = in.GetSize();
|
||||
return size > sizeof(std::uint64_t);
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
bool Engine<routing_algorithms::mld::Algorithm>::CheckCompability(const EngineConfig &config)
|
||||
{
|
||||
if (config.use_shared_memory)
|
||||
{
|
||||
storage::SharedMonitor<storage::SharedDataTimestamp> barrier;
|
||||
using mutex_type = typename decltype(barrier)::mutex_type;
|
||||
boost::interprocess::scoped_lock<mutex_type> current_region_lock(barrier.get_mutex());
|
||||
|
||||
auto mem = storage::makeSharedMemory(barrier.data().region);
|
||||
auto layout = reinterpret_cast<storage::DataLayout *>(mem->Ptr());
|
||||
return layout->GetBlockSize(storage::DataLayout::MLD_PARTITION) > 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!boost::filesystem::exists(config.storage_config.mld_partition_path))
|
||||
return false;
|
||||
storage::io::FileReader in(config.storage_config.mld_partition_path,
|
||||
storage::io::FileReader::VerifyFingerprint);
|
||||
|
||||
auto size = in.GetSize();
|
||||
return size > 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,10 +14,9 @@ namespace engine
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
constexpr double POLYLINE_DECODING_PRECISION = 1e5;
|
||||
constexpr double POLYLINE_TO_COORDINATE = COORDINATE_PRECISION / POLYLINE_DECODING_PRECISION;
|
||||
|
||||
std::string encode(std::vector<int> &numbers);
|
||||
std::int32_t decode_polyline_integer(std::string::const_iterator &first,
|
||||
std::string::const_iterator last);
|
||||
}
|
||||
using CoordVectorForwardIter = std::vector<util::Coordinate>::const_iterator;
|
||||
// Encodes geometry into polyline format.
|
||||
@@ -57,7 +56,30 @@ std::string encodePolyline(CoordVectorForwardIter begin, CoordVectorForwardIter
|
||||
|
||||
// Decodes geometry from polyline format
|
||||
// See: https://developers.google.com/maps/documentation/utilities/polylinealgorithm
|
||||
std::vector<util::Coordinate> decodePolyline(const std::string &polyline);
|
||||
|
||||
template <unsigned POLYLINE_PRECISION = 100000>
|
||||
std::vector<util::Coordinate> decodePolyline(const std::string &polyline)
|
||||
{
|
||||
double polyline_to_coordinate = COORDINATE_PRECISION / POLYLINE_PRECISION;
|
||||
std::vector<util::Coordinate> coordinates;
|
||||
std::int32_t latitude = 0, longitude = 0;
|
||||
|
||||
std::string::const_iterator first = polyline.begin();
|
||||
const std::string::const_iterator last = polyline.end();
|
||||
while (first != last)
|
||||
{
|
||||
const auto dlat = detail::decode_polyline_integer(first, last);
|
||||
const auto dlon = detail::decode_polyline_integer(first, last);
|
||||
|
||||
latitude += dlat;
|
||||
longitude += dlon;
|
||||
|
||||
coordinates.emplace_back(util::Coordinate{
|
||||
util::FixedLongitude{static_cast<std::int32_t>(longitude * polyline_to_coordinate)},
|
||||
util::FixedLatitude{static_cast<std::int32_t>(latitude * polyline_to_coordinate)}});
|
||||
}
|
||||
return coordinates;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -221,15 +221,6 @@ RoutingAlgorithms<routing_algorithms::mld::Algorithm>::ManyToManySearch(
|
||||
{
|
||||
throw util::exception("ManyToManySearch is not implemented");
|
||||
}
|
||||
|
||||
template <>
|
||||
inline std::vector<routing_algorithms::TurnData>
|
||||
RoutingAlgorithms<routing_algorithms::mld::Algorithm>::GetTileTurns(
|
||||
const std::vector<datafacade::BaseDataFacade::RTreeLeaf> &,
|
||||
const std::vector<std::size_t> &) const
|
||||
{
|
||||
throw util::exception("GetTileTurns is not implemented");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,11 @@ getTileTurns(const datafacade::ContiguousInternalMemoryDataFacade<ch::Algorithm>
|
||||
const std::vector<RTreeLeaf> &edges,
|
||||
const std::vector<std::size_t> &sorted_edge_indexes);
|
||||
|
||||
std::vector<TurnData>
|
||||
getTileTurns(const datafacade::ContiguousInternalMemoryDataFacade<mld::Algorithm> &facade,
|
||||
const std::vector<RTreeLeaf> &edges,
|
||||
const std::vector<std::size_t> &sorted_edge_indexes);
|
||||
|
||||
} // namespace routing_algorithms
|
||||
} // namespace engine
|
||||
} // namespace osrm
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "extractor/query_node.hpp"
|
||||
#include "extractor/restriction_map.hpp"
|
||||
|
||||
#include "util/concurrent_id_map.hpp"
|
||||
#include "util/deallocating_vector.hpp"
|
||||
#include "util/guidance/bearing_class.hpp"
|
||||
#include "util/guidance/entry_class.hpp"
|
||||
@@ -41,6 +42,9 @@
|
||||
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
|
||||
#include <tbb/concurrent_unordered_map.h>
|
||||
#include <tbb/concurrent_vector.h>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace extractor
|
||||
@@ -167,9 +171,9 @@ class EdgeBasedGraphFactory
|
||||
std::size_t skipped_uturns_counter;
|
||||
std::size_t skipped_barrier_turns_counter;
|
||||
|
||||
std::unordered_map<util::guidance::BearingClass, BearingClassID> bearing_class_hash;
|
||||
util::ConcurrentIDMap<util::guidance::BearingClass, BearingClassID> bearing_class_hash;
|
||||
std::vector<BearingClassID> bearing_class_by_node_based_node;
|
||||
std::unordered_map<util::guidance::EntryClass, EntryClassID> entry_class_hash;
|
||||
util::ConcurrentIDMap<util::guidance::EntryClass, EntryClassID> entry_class_hash;
|
||||
};
|
||||
} // namespace extractor
|
||||
} // namespace osrm
|
||||
|
||||
@@ -65,12 +65,16 @@ const constexpr char *scenario_names[] = {"Simple",
|
||||
|
||||
class TurnLaneHandler
|
||||
{
|
||||
using UpgradableMutex = boost::interprocess::interprocess_upgradable_mutex;
|
||||
using ScopedReaderLock = boost::interprocess::sharable_lock<UpgradableMutex>;
|
||||
using ScopedWriterLock = boost::interprocess::scoped_lock<UpgradableMutex>;
|
||||
|
||||
public:
|
||||
typedef std::vector<TurnLaneData> LaneDataVector;
|
||||
|
||||
TurnLaneHandler(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||
std::vector<std::uint32_t> &turn_lane_offsets,
|
||||
std::vector<TurnLaneType::Mask> &turn_lane_masks,
|
||||
const std::vector<std::uint32_t> &turn_lane_offsets,
|
||||
const std::vector<TurnLaneType::Mask> &turn_lane_masks,
|
||||
LaneDescriptionMap &lane_description_map,
|
||||
const TurnAnalysis &turn_analysis,
|
||||
util::guidance::LaneDataIdMap &id_map);
|
||||
@@ -86,8 +90,8 @@ class TurnLaneHandler
|
||||
// we need to be able to look at previous intersections to, in some cases, find the correct turn
|
||||
// lanes for a turn
|
||||
const util::NodeBasedDynamicGraph &node_based_graph;
|
||||
std::vector<std::uint32_t> &turn_lane_offsets;
|
||||
std::vector<TurnLaneType::Mask> &turn_lane_masks;
|
||||
const std::vector<std::uint32_t> &turn_lane_offsets;
|
||||
const std::vector<TurnLaneType::Mask> &turn_lane_masks;
|
||||
LaneDescriptionMap &lane_description_map;
|
||||
const TurnAnalysis &turn_analysis;
|
||||
util::guidance::LaneDataIdMap &id_map;
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
#include <boost/functional/hash.hpp>
|
||||
|
||||
#include "util/concurrent_id_map.hpp"
|
||||
#include "util/json_container.hpp"
|
||||
#include "util/typedefs.hpp"
|
||||
|
||||
@@ -93,9 +94,9 @@ struct TurnLaneDescription_hash
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::unordered_map<guidance::TurnLaneDescription,
|
||||
LaneDescriptionID,
|
||||
guidance::TurnLaneDescription_hash>
|
||||
typedef util::ConcurrentIDMap<guidance::TurnLaneDescription,
|
||||
LaneDescriptionID,
|
||||
guidance::TurnLaneDescription_hash>
|
||||
LaneDescriptionMap;
|
||||
|
||||
} // guidance
|
||||
|
||||
@@ -22,7 +22,7 @@ struct ProfileProperties
|
||||
: traffic_signal_penalty(0), u_turn_penalty(0),
|
||||
max_speed_for_map_matching(DEFAULT_MAX_SPEED), continue_straight_at_waypoint(true),
|
||||
use_turn_restrictions(false), left_hand_driving(false), fallback_to_duration(true),
|
||||
weight_name{"duration"}
|
||||
weight_name{"duration"}, call_tagless_node_function(true)
|
||||
{
|
||||
BOOST_ASSERT(weight_name[MAX_WEIGHT_NAME_LENGTH] == '\0');
|
||||
}
|
||||
@@ -88,6 +88,8 @@ struct ProfileProperties
|
||||
char weight_name[MAX_WEIGHT_NAME_LENGTH + 1];
|
||||
unsigned weight_precision = 1;
|
||||
bool force_split_edges = false;
|
||||
|
||||
bool call_tagless_node_function = true;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,13 +58,12 @@ class ScriptingEnvironment
|
||||
virtual void ProcessTurn(ExtractionTurn &turn) = 0;
|
||||
virtual void ProcessSegment(ExtractionSegment &segment) = 0;
|
||||
|
||||
virtual void
|
||||
ProcessElements(const std::vector<osmium::memory::Buffer::const_iterator> &osm_elements,
|
||||
const RestrictionParser &restriction_parser,
|
||||
tbb::concurrent_vector<std::pair<std::size_t, ExtractionNode>> &resulting_nodes,
|
||||
tbb::concurrent_vector<std::pair<std::size_t, ExtractionWay>> &resulting_ways,
|
||||
tbb::concurrent_vector<boost::optional<InputRestrictionContainer>>
|
||||
&resulting_restrictions) = 0;
|
||||
virtual void ProcessElements(
|
||||
const osmium::memory::Buffer &buffer,
|
||||
const RestrictionParser &restriction_parser,
|
||||
std::vector<std::pair<const osmium::Node &, ExtractionNode>> &resulting_nodes,
|
||||
std::vector<std::pair<const osmium::Way &, ExtractionWay>> &resulting_ways,
|
||||
std::vector<boost::optional<InputRestrictionContainer>> &resulting_restrictions) = 0;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,11 @@ struct LuaScriptingContext final
|
||||
bool has_way_function;
|
||||
bool has_segment_function;
|
||||
|
||||
sol::function turn_function;
|
||||
sol::function way_function;
|
||||
sol::function node_function;
|
||||
sol::function segment_function;
|
||||
|
||||
int api_version;
|
||||
};
|
||||
|
||||
@@ -60,13 +65,12 @@ class Sol2ScriptingEnvironment final : public ScriptingEnvironment
|
||||
void ProcessTurn(ExtractionTurn &turn) override;
|
||||
void ProcessSegment(ExtractionSegment &segment) override;
|
||||
|
||||
void
|
||||
ProcessElements(const std::vector<osmium::memory::Buffer::const_iterator> &osm_elements,
|
||||
const RestrictionParser &restriction_parser,
|
||||
tbb::concurrent_vector<std::pair<std::size_t, ExtractionNode>> &resulting_nodes,
|
||||
tbb::concurrent_vector<std::pair<std::size_t, ExtractionWay>> &resulting_ways,
|
||||
tbb::concurrent_vector<boost::optional<InputRestrictionContainer>>
|
||||
&resulting_restrictions) override;
|
||||
void ProcessElements(
|
||||
const osmium::memory::Buffer &buffer,
|
||||
const RestrictionParser &restriction_parser,
|
||||
std::vector<std::pair<const osmium::Node &, ExtractionNode>> &resulting_nodes,
|
||||
std::vector<std::pair<const osmium::Way &, ExtractionWay>> &resulting_ways,
|
||||
std::vector<boost::optional<InputRestrictionContainer>> &resulting_restrictions) override;
|
||||
|
||||
private:
|
||||
void InitContext(LuaScriptingContext &context);
|
||||
|
||||
@@ -31,6 +31,15 @@ void write(storage::io::FileWriter &writer,
|
||||
const detail::TurnDataContainerImpl<Ownership> &turn_data);
|
||||
}
|
||||
|
||||
struct TurnData
|
||||
{
|
||||
extractor::guidance::TurnInstruction turn_instruction;
|
||||
LaneDataID lane_data_id;
|
||||
EntryClassID entry_class_id;
|
||||
util::guidance::TurnBearing pre_turn_bearing;
|
||||
util::guidance::TurnBearing post_turn_bearing;
|
||||
};
|
||||
|
||||
namespace detail
|
||||
{
|
||||
template <storage::Ownership Ownership> class TurnDataContainerImpl
|
||||
@@ -75,17 +84,20 @@ template <storage::Ownership Ownership> class TurnDataContainerImpl
|
||||
|
||||
// Used by EdgeBasedGraphFactory to fill data structure
|
||||
template <typename = std::enable_if<Ownership == storage::Ownership::Container>>
|
||||
void push_back(extractor::guidance::TurnInstruction turn_instruction,
|
||||
LaneDataID lane_data_id,
|
||||
EntryClassID entry_class_id,
|
||||
util::guidance::TurnBearing pre_turn_bearing,
|
||||
util::guidance::TurnBearing post_turn_bearing)
|
||||
void push_back(const TurnData &data)
|
||||
{
|
||||
turn_instructions.push_back(turn_instruction);
|
||||
lane_data_ids.push_back(lane_data_id);
|
||||
entry_class_ids.push_back(entry_class_id);
|
||||
pre_turn_bearings.push_back(pre_turn_bearing);
|
||||
post_turn_bearings.push_back(post_turn_bearing);
|
||||
turn_instructions.push_back(data.turn_instruction);
|
||||
lane_data_ids.push_back(data.lane_data_id);
|
||||
entry_class_ids.push_back(data.entry_class_id);
|
||||
pre_turn_bearings.push_back(data.pre_turn_bearing);
|
||||
post_turn_bearings.push_back(data.post_turn_bearing);
|
||||
}
|
||||
|
||||
template <typename = std::enable_if<Ownership == storage::Ownership::Container>>
|
||||
void append(const std::vector<TurnData> &others)
|
||||
{
|
||||
std::for_each(
|
||||
others.begin(), others.end(), [this](const TurnData &other) { push_back(other); });
|
||||
}
|
||||
|
||||
friend void serialization::read<Ownership>(storage::io::FileReader &reader,
|
||||
|
||||
@@ -1070,6 +1070,51 @@ argumentsToMatchParameter(const Nan::FunctionCallbackInfo<v8::Value> &args,
|
||||
}
|
||||
}
|
||||
|
||||
if (obj->Has(Nan::New("gaps").ToLocalChecked()))
|
||||
{
|
||||
v8::Local<v8::Value> gaps = obj->Get(Nan::New("gaps").ToLocalChecked());
|
||||
if (gaps.IsEmpty())
|
||||
return match_parameters_ptr();
|
||||
|
||||
if (!gaps->IsString())
|
||||
{
|
||||
Nan::ThrowError("Gaps must be a string: [split, ignore]");
|
||||
return match_parameters_ptr();
|
||||
}
|
||||
|
||||
const Nan::Utf8String gaps_utf8str(gaps);
|
||||
std::string gaps_str{*gaps_utf8str, *gaps_utf8str + gaps_utf8str.length()};
|
||||
|
||||
if (gaps_str == "split")
|
||||
{
|
||||
params->gaps = osrm::MatchParameters::GapsType::Split;
|
||||
}
|
||||
else if (gaps_str == "ignore")
|
||||
{
|
||||
params->gaps = osrm::MatchParameters::GapsType::Ignore;
|
||||
}
|
||||
else
|
||||
{
|
||||
Nan::ThrowError("'gaps' param must be one of [split, ignore]");
|
||||
return match_parameters_ptr();
|
||||
}
|
||||
}
|
||||
|
||||
if (obj->Has(Nan::New("tidy").ToLocalChecked()))
|
||||
{
|
||||
v8::Local<v8::Value> tidy = obj->Get(Nan::New("tidy").ToLocalChecked());
|
||||
if (tidy.IsEmpty())
|
||||
return match_parameters_ptr();
|
||||
|
||||
if (!tidy->IsBoolean())
|
||||
{
|
||||
Nan::ThrowError("tidy must be of type Boolean");
|
||||
return match_parameters_ptr();
|
||||
}
|
||||
|
||||
params->tidy = tidy->BooleanValue();
|
||||
}
|
||||
|
||||
bool parsedSuccessfully = parseCommonParameters(obj, params);
|
||||
if (!parsedSuccessfully)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
#ifndef OSRM_ERRORCODES_HPP
|
||||
#define OSRM_ERRORCODES_HPP
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
|
||||
/**
|
||||
* Various error codes that can be returned by OSRM internal functions.
|
||||
* Note: often, these translate into return codes from `int main()` functions.
|
||||
* Thus, do not change the order - if adding new codes, append them to the
|
||||
* end, so the code values do not change for users that are checking for
|
||||
* certain values.
|
||||
*/
|
||||
enum ErrorCode
|
||||
{
|
||||
InvalidFingerprint = 2, // Start at 2 to avoid colliding with POSIX EXIT_FAILURE
|
||||
IncompatibleFileVersion,
|
||||
FileOpenError,
|
||||
FileReadError,
|
||||
FileWriteError,
|
||||
FileIOError,
|
||||
UnexpectedEndOfFile,
|
||||
IncompatibleDataset,
|
||||
UnknownAlgorithm
|
||||
#ifndef NDEBUG
|
||||
// Leave this at the end. In debug mode, we assert that the size of
|
||||
// this enum matches the number of messages we have documented, and __ENDMARKER__
|
||||
// is used as the "count" value.
|
||||
,
|
||||
__ENDMARKER__
|
||||
#endif
|
||||
};
|
||||
}
|
||||
|
||||
#endif // OSRM_ERRORCODES_HPP
|
||||
@@ -33,6 +33,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
namespace osrm
|
||||
{
|
||||
using util::exception;
|
||||
using util::RuntimeError;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -128,9 +128,16 @@ struct BaseParametersGrammar : boost::spirit::qi::grammar<Iterator, Signature>
|
||||
},
|
||||
qi::_1)];
|
||||
|
||||
polyline6_rule = qi::as_string[qi::lit("polyline6(") > +polyline_chars > ')']
|
||||
[qi::_val = ph::bind(
|
||||
[](const std::string &polyline) {
|
||||
return engine::decodePolyline<1000000>(polyline);
|
||||
},
|
||||
qi::_1)];
|
||||
|
||||
query_rule =
|
||||
((location_rule % ';') |
|
||||
polyline_rule)[ph::bind(&engine::api::BaseParameters::coordinates, qi::_r1) = qi::_1];
|
||||
((location_rule % ';') | polyline_rule |
|
||||
polyline6_rule)[ph::bind(&engine::api::BaseParameters::coordinates, qi::_r1) = qi::_1];
|
||||
|
||||
radiuses_rule = qi::lit("radiuses=") >
|
||||
(-(qi::double_ | unlimited_rule) %
|
||||
@@ -176,6 +183,7 @@ struct BaseParametersGrammar : boost::spirit::qi::grammar<Iterator, Signature>
|
||||
qi::rule<Iterator, osrm::engine::Bearing()> bearing_rule;
|
||||
qi::rule<Iterator, osrm::util::Coordinate()> location_rule;
|
||||
qi::rule<Iterator, std::vector<osrm::util::Coordinate>()> polyline_rule;
|
||||
qi::rule<Iterator, std::vector<osrm::util::Coordinate>()> polyline6_rule;
|
||||
|
||||
qi::rule<Iterator, unsigned char()> base64_char;
|
||||
qi::rule<Iterator, std::string()> polyline_chars;
|
||||
|
||||
+34
-21
@@ -1,6 +1,8 @@
|
||||
#ifndef OSRM_STORAGE_IO_HPP_
|
||||
#define OSRM_STORAGE_IO_HPP_
|
||||
|
||||
#include "osrm/error_codes.hpp"
|
||||
|
||||
#include "util/exception.hpp"
|
||||
#include "util/exception_utils.hpp"
|
||||
#include "util/fingerprint.hpp"
|
||||
@@ -10,6 +12,8 @@
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
#include <boost/iostreams/seek.hpp>
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#include <cstring>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
@@ -49,12 +53,16 @@ class FileReader
|
||||
: filepath(filepath_), fingerprint(flag)
|
||||
{
|
||||
input_stream.open(filepath, std::ios::binary);
|
||||
|
||||
// Note: filepath.string() is wrapped in std::string() because it can
|
||||
// return char * on some platforms, which makes the + operator not work
|
||||
if (!input_stream)
|
||||
throw util::exception("Error opening " + filepath.string());
|
||||
throw util::RuntimeError(
|
||||
filepath.string(), ErrorCode::FileOpenError, SOURCE_REF, std::strerror(errno));
|
||||
|
||||
if (flag == VerifyFingerprint && !ReadAndCheckFingerprint())
|
||||
{
|
||||
throw util::exception("Fingerprint mismatch in " + filepath_.string() + SOURCE_REF);
|
||||
throw util::RuntimeError(filepath.string(), ErrorCode::InvalidFingerprint, SOURCE_REF);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +74,11 @@ class FileReader
|
||||
|
||||
if (file_size == boost::filesystem::ifstream::pos_type(-1))
|
||||
{
|
||||
throw util::exception("File size for " + filepath.string() + " failed " + SOURCE_REF);
|
||||
throw util::RuntimeError("Unable to determine file size for " +
|
||||
std::string(filepath.string()),
|
||||
ErrorCode::FileIOError,
|
||||
SOURCE_REF,
|
||||
std::strerror(errno));
|
||||
}
|
||||
|
||||
// restore the current position
|
||||
@@ -101,10 +113,11 @@ class FileReader
|
||||
{
|
||||
if (result.eof())
|
||||
{
|
||||
throw util::exception("Error reading from " + filepath.string() +
|
||||
": Unexpected end of file " + SOURCE_REF);
|
||||
throw util::RuntimeError(
|
||||
filepath.string(), ErrorCode::UnexpectedEndOfFile, SOURCE_REF);
|
||||
}
|
||||
throw util::exception("Error reading from " + filepath.string() + " " + SOURCE_REF);
|
||||
throw util::RuntimeError(
|
||||
filepath.string(), ErrorCode::FileReadError, SOURCE_REF, std::strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,23 +158,19 @@ class FileReader
|
||||
|
||||
if (!loaded_fingerprint.IsValid())
|
||||
{
|
||||
util::Log(logERROR) << "Fingerprint magic number or checksum is invalid in "
|
||||
<< filepath.string();
|
||||
return false;
|
||||
throw util::RuntimeError(filepath.string(), ErrorCode::InvalidFingerprint, SOURCE_REF);
|
||||
}
|
||||
|
||||
if (!expected_fingerprint.IsDataCompatible(loaded_fingerprint))
|
||||
{
|
||||
util::Log(logERROR) << filepath.string()
|
||||
<< " is not compatible with this version of OSRM";
|
||||
|
||||
util::Log(logERROR) << "It was prepared with OSRM "
|
||||
<< loaded_fingerprint.GetMajorVersion() << "."
|
||||
<< loaded_fingerprint.GetMinorVersion() << "."
|
||||
<< loaded_fingerprint.GetPatchVersion() << " but you are running "
|
||||
<< OSRM_VERSION;
|
||||
util::Log(logERROR) << "Data is only compatible between minor releases.";
|
||||
return false;
|
||||
const std::string fileversion =
|
||||
std::to_string(loaded_fingerprint.GetMajorVersion()) + "." +
|
||||
std::to_string(loaded_fingerprint.GetMinorVersion()) + "." +
|
||||
std::to_string(loaded_fingerprint.GetPatchVersion());
|
||||
throw util::RuntimeError(std::string(filepath.string()) + " prepared with OSRM " +
|
||||
fileversion + " but this is " + OSRM_VERSION,
|
||||
ErrorCode::IncompatibleFileVersion,
|
||||
SOURCE_REF);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -192,7 +201,10 @@ class FileWriter
|
||||
{
|
||||
output_stream.open(filepath, std::ios::binary);
|
||||
if (!output_stream)
|
||||
throw util::exception("Error opening " + filepath.string());
|
||||
{
|
||||
throw util::RuntimeError(
|
||||
filepath.string(), ErrorCode::FileOpenError, SOURCE_REF, std::strerror(errno));
|
||||
}
|
||||
|
||||
if (flag == GenerateFingerprint)
|
||||
{
|
||||
@@ -216,7 +228,8 @@ class FileWriter
|
||||
|
||||
if (!result)
|
||||
{
|
||||
throw util::exception("Error writing to " + filepath.string());
|
||||
throw util::RuntimeError(
|
||||
filepath.string(), ErrorCode::FileWriteError, SOURCE_REF, std::strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
#ifndef CONCURRENT_ID_MAP_HPP
|
||||
#define CONCURRENT_ID_MAP_HPP
|
||||
|
||||
#include <boost/interprocess/sync/interprocess_upgradable_mutex.hpp>
|
||||
#include <boost/interprocess/sync/scoped_lock.hpp>
|
||||
#include <boost/interprocess/sync/sharable_lock.hpp>
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace util
|
||||
{
|
||||
|
||||
/**
|
||||
* This is a special purpose map for caching incrementing IDs
|
||||
*/
|
||||
template <typename KeyType, typename ValueType, typename HashType = std::hash<KeyType>>
|
||||
struct ConcurrentIDMap
|
||||
{
|
||||
static_assert(std::is_unsigned<ValueType>::value, "Only unsigned integer types are supported.");
|
||||
|
||||
using UpgradableMutex = boost::interprocess::interprocess_upgradable_mutex;
|
||||
using ScopedReaderLock = boost::interprocess::sharable_lock<UpgradableMutex>;
|
||||
using ScopedWriterLock = boost::interprocess::scoped_lock<UpgradableMutex>;
|
||||
|
||||
std::unordered_map<KeyType, ValueType, HashType> data;
|
||||
mutable UpgradableMutex mutex;
|
||||
|
||||
const ValueType ConcurrentFindOrAdd(const KeyType &key)
|
||||
{
|
||||
{
|
||||
ScopedReaderLock sentry{mutex};
|
||||
const auto result = data.find(key);
|
||||
if (result != data.end())
|
||||
{
|
||||
return result->second;
|
||||
}
|
||||
}
|
||||
{
|
||||
ScopedWriterLock sentry{mutex};
|
||||
const auto result = data.find(key);
|
||||
if (result != data.end())
|
||||
{
|
||||
return result->second;
|
||||
}
|
||||
const auto id = static_cast<ValueType>(data.size());
|
||||
data[key] = id;
|
||||
return id;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // util
|
||||
} // osrm
|
||||
|
||||
#endif // CONCURRENT_ID_MAP_HPP
|
||||
@@ -28,10 +28,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#ifndef OSRM_EXCEPTION_HPP
|
||||
#define OSRM_EXCEPTION_HPP
|
||||
|
||||
#include <array>
|
||||
#include <exception>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "osrm/error_codes.hpp"
|
||||
#include <boost/format.hpp>
|
||||
|
||||
namespace osrm
|
||||
@@ -39,7 +42,7 @@ namespace osrm
|
||||
namespace util
|
||||
{
|
||||
|
||||
class exception final : public std::exception
|
||||
class exception : public std::exception
|
||||
{
|
||||
public:
|
||||
explicit exception(const char *message) : message(message) {}
|
||||
@@ -54,6 +57,72 @@ class exception final : public std::exception
|
||||
virtual void anchor() const;
|
||||
const std::string message;
|
||||
};
|
||||
|
||||
/**
|
||||
* Indicates a class of error that occurred that was caused by some kind of
|
||||
* external input (common examples are out of disk space, file permission errors,
|
||||
* user supplied bad data, etc).
|
||||
*/
|
||||
|
||||
constexpr const std::array<const char *, 11> ErrorDescriptions = {{
|
||||
"", // Dummy - ErrorCode values start at 2
|
||||
"", // Dummy - ErrorCode values start at 2
|
||||
"Fingerprint did not match the expected value", // InvalidFingerprint
|
||||
"File is incompatible with this version of OSRM", // IncompatibleFileVersion
|
||||
"Problem opening file", // FileOpenError
|
||||
"Problem reading from file", // FileReadError
|
||||
"Problem writing to file", // FileWriteError
|
||||
"I/O error occurred", // FileIOError
|
||||
"Unexpected end of file", // UnexpectedEndOfFile
|
||||
"The dataset you are trying to load is not " // IncompatibleDataset
|
||||
"compatible with the routing algorithm you want to use." // ...continued...
|
||||
"Incompatible algorithm" // IncompatibleAlgorithm
|
||||
}};
|
||||
|
||||
#ifndef NDEBUG
|
||||
// Check that we have messages for every enum
|
||||
static_assert(ErrorDescriptions.size() == ErrorCode::__ENDMARKER__,
|
||||
"ErrorCode list and ErrorDescription lists are different sizes");
|
||||
#endif
|
||||
|
||||
class RuntimeError : public exception
|
||||
{
|
||||
using Base = exception;
|
||||
using Base::Base;
|
||||
|
||||
public:
|
||||
explicit RuntimeError(const std::string &message,
|
||||
const ErrorCode code_,
|
||||
const std::string &sourceref,
|
||||
const char *possiblecause = nullptr)
|
||||
: Base(BuildMessage(message, code_, sourceref, possiblecause)), code(code_)
|
||||
{
|
||||
}
|
||||
|
||||
ErrorCode GetCode() const { return code; }
|
||||
|
||||
private:
|
||||
// This function exists to 'anchor' the class, and stop the compiler from
|
||||
// copying vtable and RTTI info into every object file that includes
|
||||
// this header. (Caught by -Wweak-vtables under Clang.)
|
||||
virtual void anchor() const;
|
||||
const ErrorCode code;
|
||||
|
||||
static std::string BuildMessage(const std::string &message,
|
||||
const ErrorCode code_,
|
||||
const std::string &sourceref,
|
||||
const char *possiblecause = nullptr)
|
||||
{
|
||||
std::string result;
|
||||
result += ErrorDescriptions[code_];
|
||||
result += ": " + std::string(message);
|
||||
result += possiblecause != nullptr
|
||||
? (std::string(" (possible cause: \"") + possiblecause + "\")")
|
||||
: "";
|
||||
result += " (at " + sourceref + ")";
|
||||
return result;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,6 @@
|
||||
#define OSRM_SOURCE_FILE_ PROJECT_RELATIVE_PATH_(__FILE__)
|
||||
|
||||
// This is the macro to use
|
||||
#define SOURCE_REF std::string(" (at ") + OSRM_SOURCE_FILE_ + ":" + std::to_string(__LINE__) + ")"
|
||||
#define SOURCE_REF OSRM_SOURCE_FILE_ + ":" + std::to_string(__LINE__)
|
||||
|
||||
#endif // SOURCE_MACROS_HPP
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <iterator>
|
||||
#include <numeric>
|
||||
#include <utility>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
@@ -12,11 +13,11 @@ namespace util
|
||||
// TODO: check why this is not an option here:
|
||||
// std::adjacent_find(begin, end, [=](const auto& l, const auto& r){ return function(), false; });
|
||||
template <typename ForwardIterator, typename Function>
|
||||
Function for_each_pair(ForwardIterator begin, ForwardIterator end, Function function)
|
||||
void for_each_pair(ForwardIterator begin, ForwardIterator end, Function &&function)
|
||||
{
|
||||
if (begin == end)
|
||||
{
|
||||
return function;
|
||||
return;
|
||||
}
|
||||
|
||||
auto next = begin;
|
||||
@@ -24,19 +25,18 @@ Function for_each_pair(ForwardIterator begin, ForwardIterator end, Function func
|
||||
|
||||
while (next != end)
|
||||
{
|
||||
function(*begin, *next);
|
||||
std::forward<Function>(function)(*begin, *next);
|
||||
begin = std::next(begin);
|
||||
next = std::next(next);
|
||||
}
|
||||
return function;
|
||||
}
|
||||
|
||||
template <class ContainerT, typename Function>
|
||||
Function for_each_pair(ContainerT &container, Function function)
|
||||
void for_each_pair(ContainerT &container, Function &&function)
|
||||
{
|
||||
using std::begin;
|
||||
using std::end;
|
||||
return for_each_pair(begin(container), end(container), function);
|
||||
for_each_pair(begin(container), end(container), std::forward<Function>(function));
|
||||
}
|
||||
|
||||
} // namespace util
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "util/concurrent_id_map.hpp"
|
||||
#include "util/typedefs.hpp"
|
||||
|
||||
#include <boost/functional/hash.hpp>
|
||||
@@ -97,7 +98,7 @@ class LaneTupleIdPair
|
||||
}
|
||||
};
|
||||
|
||||
using LaneDataIdMap = std::unordered_map<LaneTupleIdPair, LaneDataID, boost::hash<LaneTupleIdPair>>;
|
||||
using LaneDataIdMap = ConcurrentIDMap<LaneTupleIdPair, LaneDataID, boost::hash<LaneTupleIdPair>>;
|
||||
|
||||
} // namespace guidance
|
||||
} // namespace util
|
||||
|
||||
Generated
+6677
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "osrm",
|
||||
"version": "5.8.0-latest.1",
|
||||
"version": "5.8.0-rc.1",
|
||||
"private": false,
|
||||
"description": "The Open Source Routing Machine is a high performance routing engine written in C++14 designed to run on OpenStreetMap data.",
|
||||
"dependencies": {
|
||||
|
||||
@@ -15,6 +15,11 @@ properties.continue_straight_at_waypoint = false
|
||||
properties.weight_name = 'duration'
|
||||
--properties.weight_name = 'cyclability'
|
||||
|
||||
-- Set to true if you need to call the node_function for every node.
|
||||
-- Generally can be left as false to avoid unnecessary Lua calls
|
||||
-- (which slow down pre-processing).
|
||||
properties.call_tagless_node_function = false
|
||||
|
||||
|
||||
local default_speed = 15
|
||||
local walking_speed = 6
|
||||
|
||||
@@ -20,6 +20,12 @@ properties.weight_name = 'routability'
|
||||
-- For shortest distance without penalties for accessibility
|
||||
--properties.weight_name = 'distance'
|
||||
|
||||
-- Set to true if you need to call the node_function for every node.
|
||||
-- Generally can be left as false to avoid unnecessary Lua calls
|
||||
-- (which slow down pre-processing).
|
||||
properties.call_tagless_node_function = false
|
||||
|
||||
|
||||
local profile = {
|
||||
default_mode = mode.driving,
|
||||
default_speed = 10,
|
||||
|
||||
@@ -14,6 +14,11 @@ properties.continue_straight_at_waypoint = false
|
||||
properties.weight_name = 'duration'
|
||||
--properties.weight_name = 'routability'
|
||||
|
||||
-- Set to true if you need to call the node_function for every node.
|
||||
-- Generally can be left as false to avoid unnecessary Lua calls
|
||||
-- (which slow down pre-processing).
|
||||
properties.call_tagless_node_function = false
|
||||
|
||||
local walking_speed = 5
|
||||
|
||||
local profile = {
|
||||
|
||||
@@ -3,6 +3,11 @@ api_version = 1
|
||||
|
||||
properties.force_split_edges = true
|
||||
|
||||
-- Set to true if you need to call the node_function for every node.
|
||||
-- Generally can be left as false to avoid unnecessary Lua calls
|
||||
-- (which slow down pre-processing).
|
||||
properties.call_tagless_node_function = false
|
||||
|
||||
-- Minimalist node_ and way_functions in order to test source_ and segment_functions
|
||||
|
||||
function node_function (node, result)
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
api_version = 1
|
||||
-- Rasterbot profile
|
||||
|
||||
-- Set to true if you need to call the node_function for every node.
|
||||
-- Generally can be left as false to avoid unnecessary Lua calls
|
||||
-- (which slow down pre-processing).
|
||||
properties.call_tagless_node_function = false
|
||||
|
||||
-- Minimalist node_ and way_functions in order to test source_ and segment_functions
|
||||
|
||||
function node_function (node, result)
|
||||
|
||||
@@ -22,6 +22,11 @@ properties.use_turn_restrictions = true
|
||||
properties.max_speed_for_map_matching = 30/3.6 --km -> m/s
|
||||
properties.weight_name = 'duration'
|
||||
|
||||
-- Set to true if you need to call the node_function for every node.
|
||||
-- Generally can be left as false to avoid unnecessary Lua calls
|
||||
-- (which slow down pre-processing).
|
||||
properties.call_tagless_node_function = false
|
||||
|
||||
local uturn_penalty = 20
|
||||
local traffic_light_penalty = 7 -- seconds
|
||||
|
||||
|
||||
@@ -171,9 +171,11 @@ void closeOffRoundabout(const bool on_roundabout,
|
||||
if (!guidance::haveSameMode(exit_step, prev_step))
|
||||
{
|
||||
BOOST_ASSERT(leavesRoundabout(exit_step.maneuver.instruction));
|
||||
prev_step.maneuver.instruction = exit_step.maneuver.instruction;
|
||||
if (!entersRoundabout(prev_step.maneuver.instruction))
|
||||
prev_step.maneuver.exit = exit_step.maneuver.exit;
|
||||
{
|
||||
prev_step.maneuver.instruction = exit_step.maneuver.instruction;
|
||||
}
|
||||
prev_step.maneuver.exit = exit_step.maneuver.exit;
|
||||
exit_step.maneuver.instruction.type = TurnType::Notification;
|
||||
step_index--;
|
||||
}
|
||||
@@ -198,9 +200,12 @@ void closeOffRoundabout(const bool on_roundabout,
|
||||
{
|
||||
auto &propagation_step = steps[propagation_index];
|
||||
auto &next_step = steps[propagation_index + 1];
|
||||
propagation_step.ElongateBy(next_step);
|
||||
propagation_step.maneuver.exit = next_step.maneuver.exit;
|
||||
next_step.Invalidate();
|
||||
if (guidance::haveSameMode(propagation_step, next_step))
|
||||
{
|
||||
propagation_step.ElongateBy(next_step);
|
||||
propagation_step.maneuver.exit = next_step.maneuver.exit;
|
||||
next_step.Invalidate();
|
||||
}
|
||||
|
||||
if (entersRoundabout(propagation_step.maneuver.instruction))
|
||||
{
|
||||
|
||||
+84
-40
@@ -428,6 +428,12 @@ void encodeVectorTile(const datafacade::ContiguousInternalMemoryDataFacadeBase &
|
||||
const auto &edge = edges[edge_index];
|
||||
const auto geometry_id = get_geometry_id(edge);
|
||||
|
||||
// Get coordinates for start/end nodes of segment (NodeIDs u and v)
|
||||
const auto a = facade.GetCoordinateOfNode(edge.u);
|
||||
const auto b = facade.GetCoordinateOfNode(edge.v);
|
||||
// Calculate the length in meters
|
||||
const double length = osrm::util::coordinate_calculation::haversineDistance(a, b);
|
||||
|
||||
// Weight values
|
||||
const auto forward_weight_vector =
|
||||
facade.GetUncompressedForwardWeights(geometry_id);
|
||||
@@ -439,6 +445,14 @@ void encodeVectorTile(const datafacade::ContiguousInternalMemoryDataFacadeBase &
|
||||
use_line_value(forward_weight);
|
||||
use_line_value(reverse_weight);
|
||||
|
||||
std::uint32_t forward_rate =
|
||||
static_cast<std::uint32_t>(round(length / forward_weight * 10.));
|
||||
std::uint32_t reverse_rate =
|
||||
static_cast<std::uint32_t>(round(length / reverse_weight * 10.));
|
||||
|
||||
use_line_value(forward_rate);
|
||||
use_line_value(reverse_rate);
|
||||
|
||||
// Duration values
|
||||
const auto forward_duration_vector =
|
||||
facade.GetUncompressedForwardDurations(geometry_id);
|
||||
@@ -489,9 +503,9 @@ void encodeVectorTile(const datafacade::ContiguousInternalMemoryDataFacadeBase &
|
||||
const auto reverse_duration =
|
||||
reverse_duration_vector[reverse_duration_vector.size() -
|
||||
edge.fwd_segment_position - 1];
|
||||
const auto forward_datasource =
|
||||
const auto forward_datasource_idx =
|
||||
forward_datasource_vector[edge.fwd_segment_position];
|
||||
const auto reverse_datasource =
|
||||
const auto reverse_datasource_idx =
|
||||
reverse_datasource_vector[reverse_datasource_vector.size() -
|
||||
edge.fwd_segment_position - 1];
|
||||
|
||||
@@ -516,14 +530,16 @@ void encodeVectorTile(const datafacade::ContiguousInternalMemoryDataFacadeBase &
|
||||
&component_id,
|
||||
&id,
|
||||
&max_datasource_id,
|
||||
&used_line_ints](const FixedLine &tile_line,
|
||||
const std::uint32_t speed_kmh,
|
||||
const std::size_t weight,
|
||||
const std::size_t duration,
|
||||
const DatasourceID datasource,
|
||||
const std::size_t name_idx,
|
||||
std::int32_t &start_x,
|
||||
std::int32_t &start_y) {
|
||||
&used_line_ints](
|
||||
const FixedLine &tile_line,
|
||||
const std::uint32_t speed_kmh_idx,
|
||||
const std::uint32_t rate_idx,
|
||||
const std::size_t weight_idx,
|
||||
const std::size_t duration_idx,
|
||||
const DatasourceID datasource_idx,
|
||||
const std::size_t name_idx,
|
||||
std::int32_t &start_x,
|
||||
std::int32_t &start_y) {
|
||||
// Here, we save the two attributes for our feature: the speed and
|
||||
// the is_small boolean. We only serve up speeds from 0-139, so all we
|
||||
// do is save the first
|
||||
@@ -547,23 +563,27 @@ void encodeVectorTile(const datafacade::ContiguousInternalMemoryDataFacadeBase &
|
||||
feature_writer, util::vector_tile::FEATURE_ATTRIBUTES_TAG);
|
||||
|
||||
field.add_element(0); // "speed" tag key offset
|
||||
field.add_element(std::min(
|
||||
speed_kmh_idx, 127u)); // save the speed value, capped at 127
|
||||
field.add_element(1); // "is_small" tag key offset
|
||||
field.add_element(
|
||||
std::min(speed_kmh, 127u)); // save the speed value, capped at 127
|
||||
field.add_element(1); // "is_small" tag key offset
|
||||
field.add_element(128 +
|
||||
(component_id.is_tiny ? 0 : 1)); // is_small feature
|
||||
field.add_element(2); // "datasource" tag key offset
|
||||
field.add_element(130 + datasource); // datasource value offset
|
||||
field.add_element(3); // "weight" tag key offset
|
||||
128 + (component_id.is_tiny ? 0 : 1)); // is_small feature offset
|
||||
field.add_element(2); // "datasource" tag key offset
|
||||
field.add_element(130 + datasource_idx); // datasource value offset
|
||||
field.add_element(3); // "weight" tag key offset
|
||||
field.add_element(130 + max_datasource_id + 1 +
|
||||
weight); // weight value offset
|
||||
field.add_element(4); // "duration" tag key offset
|
||||
weight_idx); // weight value offset
|
||||
field.add_element(4); // "duration" tag key offset
|
||||
field.add_element(130 + max_datasource_id + 1 +
|
||||
duration); // duration value offset
|
||||
field.add_element(5); // "name" tag key offset
|
||||
duration_idx); // duration value offset
|
||||
field.add_element(5); // "name" tag key offset
|
||||
|
||||
field.add_element(130 + max_datasource_id + 1 + used_line_ints.size() +
|
||||
name_idx); // name value offset
|
||||
|
||||
field.add_element(6); // rate tag key offset
|
||||
field.add_element(130 + max_datasource_id + 1 +
|
||||
rate_idx); // rate goes in used_line_ints
|
||||
}
|
||||
{
|
||||
|
||||
@@ -581,17 +601,26 @@ void encodeVectorTile(const datafacade::ContiguousInternalMemoryDataFacadeBase &
|
||||
std::int32_t start_y = 0;
|
||||
|
||||
// Calculate the speed for this line
|
||||
std::uint32_t speed_kmh =
|
||||
// Speeds are looked up in a simple 1:1 table, so the speed value == lookup
|
||||
// table index
|
||||
std::uint32_t speed_kmh_idx =
|
||||
static_cast<std::uint32_t>(round(length / forward_duration * 10 * 3.6));
|
||||
|
||||
// Rate values are in meters per weight-unit - and similar to speeds, we
|
||||
// present 1 decimal place of precision (these values are added as
|
||||
// double/10) lower down
|
||||
std::uint32_t forward_rate =
|
||||
static_cast<std::uint32_t>(round(length / forward_weight * 10.));
|
||||
|
||||
auto tile_line = coordinatesToTileLine(a, b, tile_bbox);
|
||||
if (!tile_line.empty())
|
||||
{
|
||||
encode_tile_line(tile_line,
|
||||
speed_kmh,
|
||||
speed_kmh_idx,
|
||||
line_int_offsets[forward_rate],
|
||||
line_int_offsets[forward_weight],
|
||||
line_int_offsets[forward_duration],
|
||||
forward_datasource,
|
||||
forward_datasource_idx,
|
||||
name_offset,
|
||||
start_x,
|
||||
start_y);
|
||||
@@ -606,17 +635,26 @@ void encodeVectorTile(const datafacade::ContiguousInternalMemoryDataFacadeBase &
|
||||
std::int32_t start_y = 0;
|
||||
|
||||
// Calculate the speed for this line
|
||||
std::uint32_t speed_kmh =
|
||||
// Speeds are looked up in a simple 1:1 table, so the speed value == lookup
|
||||
// table index
|
||||
std::uint32_t speed_kmh_idx =
|
||||
static_cast<std::uint32_t>(round(length / reverse_duration * 10 * 3.6));
|
||||
|
||||
// Rate values are in meters per weight-unit - and similar to speeds, we
|
||||
// present 1 decimal place of precision (these values are added as
|
||||
// double/10) lower down
|
||||
std::uint32_t reverse_rate =
|
||||
static_cast<std::uint32_t>(round(length / reverse_weight * 10.));
|
||||
|
||||
auto tile_line = coordinatesToTileLine(b, a, tile_bbox);
|
||||
if (!tile_line.empty())
|
||||
{
|
||||
encode_tile_line(tile_line,
|
||||
speed_kmh,
|
||||
speed_kmh_idx,
|
||||
line_int_offsets[reverse_rate],
|
||||
line_int_offsets[reverse_weight],
|
||||
line_int_offsets[reverse_duration],
|
||||
reverse_datasource,
|
||||
reverse_datasource_idx,
|
||||
name_offset,
|
||||
start_x,
|
||||
start_y);
|
||||
@@ -634,6 +672,7 @@ void encodeVectorTile(const datafacade::ContiguousInternalMemoryDataFacadeBase &
|
||||
line_layer_writer.add_string(util::vector_tile::KEY_TAG, "weight");
|
||||
line_layer_writer.add_string(util::vector_tile::KEY_TAG, "duration");
|
||||
line_layer_writer.add_string(util::vector_tile::KEY_TAG, "name");
|
||||
line_layer_writer.add_string(util::vector_tile::KEY_TAG, "rate");
|
||||
|
||||
// Now, we write out the possible speed value arrays and possible is_tiny
|
||||
// values. Field type 4 is the "values" field. It's a variable type field,
|
||||
@@ -695,19 +734,21 @@ void encodeVectorTile(const datafacade::ContiguousInternalMemoryDataFacadeBase &
|
||||
{
|
||||
// we need to pre-encode all values here because we need the full offsets later
|
||||
// for encoding the actual features.
|
||||
std::vector<std::tuple<util::Coordinate, unsigned, unsigned, unsigned>>
|
||||
std::vector<std::tuple<util::Coordinate, unsigned, unsigned, unsigned, unsigned>>
|
||||
encoded_turn_data(all_turn_data.size());
|
||||
std::transform(
|
||||
all_turn_data.begin(),
|
||||
all_turn_data.end(),
|
||||
encoded_turn_data.begin(),
|
||||
[&](const routing_algorithms::TurnData &t) {
|
||||
auto angle_idx = use_point_int_value(t.in_angle);
|
||||
auto turn_idx = use_point_int_value(t.turn_angle);
|
||||
auto duration_idx =
|
||||
use_point_float_value(t.duration / 10.0); // Note conversion to float here
|
||||
return std::make_tuple(t.coordinate, angle_idx, turn_idx, duration_idx);
|
||||
});
|
||||
std::transform(all_turn_data.begin(),
|
||||
all_turn_data.end(),
|
||||
encoded_turn_data.begin(),
|
||||
[&](const routing_algorithms::TurnData &t) {
|
||||
auto angle_idx = use_point_int_value(t.in_angle);
|
||||
auto turn_idx = use_point_int_value(t.turn_angle);
|
||||
auto duration_idx = use_point_float_value(
|
||||
t.duration / 10.0); // Note conversion to float here
|
||||
auto weight_idx = use_point_float_value(
|
||||
t.weight / 10.0); // Note conversion to float here
|
||||
return std::make_tuple(
|
||||
t.coordinate, angle_idx, turn_idx, duration_idx, weight_idx);
|
||||
});
|
||||
|
||||
// Now write the points layer for turn penalty data:
|
||||
// Add a layer object to the PBF stream. 3=='layer' from the vector tile spec
|
||||
@@ -734,7 +775,7 @@ void encodeVectorTile(const datafacade::ContiguousInternalMemoryDataFacadeBase &
|
||||
util::vector_tile::GEOMETRY_TYPE_POINT); // geometry type
|
||||
feature_writer.add_uint64(util::vector_tile::ID_TAG, id++); // id
|
||||
{
|
||||
// Write out the 3 properties we want on the feature. These
|
||||
// Write out the 4 properties we want on the feature. These
|
||||
// refer to indexes in the properties lookup table, which we
|
||||
// add to the tile after we add all features.
|
||||
protozero::packed_field_uint32 field(
|
||||
@@ -745,6 +786,8 @@ void encodeVectorTile(const datafacade::ContiguousInternalMemoryDataFacadeBase &
|
||||
field.add_element(std::get<2>(point_turn_data));
|
||||
field.add_element(2); // "cost" tag key offset
|
||||
field.add_element(used_point_ints.size() + std::get<3>(point_turn_data));
|
||||
field.add_element(3); // "weight" tag key offset
|
||||
field.add_element(used_point_ints.size() + std::get<4>(point_turn_data));
|
||||
}
|
||||
{
|
||||
// Add the geometry as the last field in this feature
|
||||
@@ -772,6 +815,7 @@ void encodeVectorTile(const datafacade::ContiguousInternalMemoryDataFacadeBase &
|
||||
point_layer_writer.add_string(util::vector_tile::KEY_TAG, "bearing_in");
|
||||
point_layer_writer.add_string(util::vector_tile::KEY_TAG, "turn_angle");
|
||||
point_layer_writer.add_string(util::vector_tile::KEY_TAG, "cost");
|
||||
point_layer_writer.add_string(util::vector_tile::KEY_TAG, "weight");
|
||||
|
||||
// Now, save the lists of integers and floats that our features refer to.
|
||||
for (const auto &value : used_point_ints)
|
||||
|
||||
@@ -52,54 +52,29 @@ std::string encode(std::vector<int> &numbers)
|
||||
}
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<util::Coordinate> decodePolyline(const std::string &geometry_string)
|
||||
// https://developers.google.com/maps/documentation/utilities/polylinealgorithm
|
||||
std::int32_t decode_polyline_integer(std::string::const_iterator &first,
|
||||
std::string::const_iterator last)
|
||||
{
|
||||
// https://developers.google.com/maps/documentation/utilities/polylinealgorithm
|
||||
auto decode_polyline_integer = [](auto &first, auto last) {
|
||||
// varint coding parameters
|
||||
const std::uint32_t bits_in_chunk = 5;
|
||||
const std::uint32_t continuation_bit = 1 << bits_in_chunk;
|
||||
const std::uint32_t chunk_mask = (1 << bits_in_chunk) - 1;
|
||||
// varint coding parameters
|
||||
const std::uint32_t bits_in_chunk = 5;
|
||||
const std::uint32_t continuation_bit = 1 << bits_in_chunk;
|
||||
const std::uint32_t chunk_mask = (1 << bits_in_chunk) - 1;
|
||||
|
||||
std::uint32_t result = 0;
|
||||
for (std::uint32_t value = continuation_bit, shift = 0;
|
||||
(value & continuation_bit) && (shift < CHAR_BIT * sizeof(result) - 1) && first != last;
|
||||
++first, shift += bits_in_chunk)
|
||||
{
|
||||
value = *first - 63; // convert ASCII coding [?..~] to an integer [0..63]
|
||||
result |= (value & chunk_mask) << shift;
|
||||
}
|
||||
|
||||
// change "zig-zag" sign coding to two's complement
|
||||
result = ((result & 1) == 1) ? ~(result >> 1) : (result >> 1);
|
||||
return static_cast<std::int32_t>(result);
|
||||
};
|
||||
|
||||
auto polyline_to_coordinate = [](auto value) {
|
||||
return static_cast<std::int32_t>(value * detail::POLYLINE_TO_COORDINATE);
|
||||
};
|
||||
|
||||
std::vector<util::Coordinate> coordinates;
|
||||
std::int32_t latitude = 0, longitude = 0;
|
||||
|
||||
std::string::const_iterator first = geometry_string.begin();
|
||||
const std::string::const_iterator last = geometry_string.end();
|
||||
while (first != last)
|
||||
std::uint32_t result = 0;
|
||||
for (std::uint32_t value = continuation_bit, shift = 0;
|
||||
(value & continuation_bit) && (shift < CHAR_BIT * sizeof(result) - 1) && first != last;
|
||||
++first, shift += bits_in_chunk)
|
||||
{
|
||||
const auto dlat = decode_polyline_integer(first, last);
|
||||
const auto dlon = decode_polyline_integer(first, last);
|
||||
|
||||
latitude += dlat;
|
||||
longitude += dlon;
|
||||
|
||||
coordinates.emplace_back(
|
||||
util::Coordinate{util::FixedLongitude{polyline_to_coordinate(longitude)},
|
||||
util::FixedLatitude{polyline_to_coordinate(latitude)}});
|
||||
value = *first - 63; // convert ASCII coding [?..~] to an integer [0..63]
|
||||
result |= (value & chunk_mask) << shift;
|
||||
}
|
||||
|
||||
return coordinates;
|
||||
// change "zig-zag" sign coding to two's complement
|
||||
result = ((result & 1) == 1) ? ~(result >> 1) : (result >> 1);
|
||||
return static_cast<std::int32_t>(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace ch
|
||||
|
||||
namespace
|
||||
{
|
||||
const double constexpr VIAPATH_ALPHA = 0.10;
|
||||
const double constexpr VIAPATH_ALPHA = 0.25; // alternative is local optimum on 25% sub-paths
|
||||
const double constexpr VIAPATH_EPSILON = 0.15; // alternative at most 15% longer
|
||||
const double constexpr VIAPATH_GAMMA = 0.75; // alternative shares at most 75% with the shortest.
|
||||
|
||||
@@ -398,8 +398,7 @@ bool viaNodeCandidatePassesTTest(
|
||||
{
|
||||
return false;
|
||||
}
|
||||
const EdgeWeight T_threshold =
|
||||
static_cast<EdgeWeight>(VIAPATH_EPSILON * weight_of_shortest_path);
|
||||
const EdgeWeight T_threshold = static_cast<EdgeWeight>(VIAPATH_ALPHA * weight_of_shortest_path);
|
||||
EdgeWeight unpacked_until_weight = 0;
|
||||
|
||||
std::stack<SearchSpaceEdge> unpack_stack;
|
||||
@@ -732,7 +731,7 @@ alternativePathSearch(SearchEngineData<Algorithm> &engine_working_data,
|
||||
(approximated_sharing <= upper_bound_to_shortest_path_weight * VIAPATH_GAMMA);
|
||||
const bool stretch_passes =
|
||||
(approximated_weight - approximated_sharing) <
|
||||
((1. + VIAPATH_ALPHA) * (upper_bound_to_shortest_path_weight - approximated_sharing));
|
||||
((1. + VIAPATH_EPSILON) * (upper_bound_to_shortest_path_weight - approximated_sharing));
|
||||
|
||||
if (weight_passes && sharing_passes && stretch_passes)
|
||||
{
|
||||
|
||||
@@ -7,31 +7,33 @@ namespace engine
|
||||
namespace routing_algorithms
|
||||
{
|
||||
|
||||
std::vector<TurnData>
|
||||
getTileTurns(const datafacade::ContiguousInternalMemoryDataFacade<ch::Algorithm> &facade,
|
||||
const std::vector<RTreeLeaf> &edges,
|
||||
const std::vector<std::size_t> &sorted_edge_indexes)
|
||||
namespace
|
||||
{
|
||||
std::vector<TurnData> all_turn_data;
|
||||
// Struct to hold info on all the EdgeBasedNodes that are visible in our tile
|
||||
// When we create these, we insure that (source, target) and packed_geometry_id
|
||||
// are all pointed in the same direction.
|
||||
struct EdgeBasedNodeInfo
|
||||
{
|
||||
bool is_geometry_forward; // Is the geometry forward or reverse?
|
||||
unsigned packed_geometry_id;
|
||||
};
|
||||
|
||||
// Struct to hold info on all the EdgeBasedNodes that are visible in our tile
|
||||
// When we create these, we insure that (source, target) and packed_geometry_id
|
||||
// are all pointed in the same direction.
|
||||
struct EdgeBasedNodeInfo
|
||||
{
|
||||
bool is_geometry_forward; // Is the geometry forward or reverse?
|
||||
unsigned packed_geometry_id;
|
||||
};
|
||||
struct SegmentData
|
||||
{
|
||||
NodeID target_node;
|
||||
EdgeID edge_based_node_id;
|
||||
};
|
||||
|
||||
template <typename edge_extractor, typename datafacade>
|
||||
std::vector<TurnData> generateTurns(const datafacade &facade,
|
||||
const std::vector<RTreeLeaf> &edges,
|
||||
const std::vector<std::size_t> &sorted_edge_indexes,
|
||||
edge_extractor const &find_edge)
|
||||
{
|
||||
// Lookup table for edge-based-nodes
|
||||
std::unordered_map<NodeID, EdgeBasedNodeInfo> edge_based_node_info;
|
||||
|
||||
struct SegmentData
|
||||
{
|
||||
NodeID target_node;
|
||||
EdgeID edge_based_node_id;
|
||||
};
|
||||
|
||||
std::unordered_map<NodeID, std::vector<SegmentData>> directed_graph;
|
||||
|
||||
// Reserve enough space for unique edge-based-nodes on every edge.
|
||||
// Only a tile with all unique edges will use this much, but
|
||||
// it saves us a bunch of re-allocations during iteration.
|
||||
@@ -41,8 +43,10 @@ getTileTurns(const datafacade::ContiguousInternalMemoryDataFacade<ch::Algorithm>
|
||||
return facade.GetGeometryIndex(edge.forward_segment_id.id).id;
|
||||
};
|
||||
|
||||
// Build an adjacency list for all the road segments visible in
|
||||
// the tile
|
||||
// To build a tile, we can only rely on the r-tree to quickly find all data visible within the
|
||||
// tile itself. The Rtree returns a series of segments that may or may not offer turns
|
||||
// associated with them. To be able to extract turn penalties, we extract a node based graph
|
||||
// from our edge based representation.
|
||||
for (const auto &edge_index : sorted_edge_indexes)
|
||||
{
|
||||
const auto &edge = edges[edge_index];
|
||||
@@ -79,28 +83,32 @@ getTileTurns(const datafacade::ContiguousInternalMemoryDataFacade<ch::Algorithm>
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure we traverse the startnodes in a consistent order
|
||||
// to ensure identical PBF encoding on all platforms.
|
||||
std::vector<NodeID> sorted_startnodes;
|
||||
sorted_startnodes.reserve(directed_graph.size());
|
||||
std::transform(directed_graph.begin(),
|
||||
directed_graph.end(),
|
||||
std::back_inserter(sorted_startnodes),
|
||||
[](auto const &node) { return node.first; });
|
||||
std::sort(sorted_startnodes.begin(), sorted_startnodes.end());
|
||||
|
||||
std::vector<TurnData> all_turn_data;
|
||||
|
||||
// Given a turn:
|
||||
// u---v
|
||||
// |
|
||||
// w
|
||||
// uv is the "approach"
|
||||
// vw is the "exit"
|
||||
std::vector<contractor::QueryEdge::EdgeData> unpacked_shortcut;
|
||||
std::vector<EdgeWeight> approach_weight_vector;
|
||||
std::vector<EdgeWeight> approach_duration_vector;
|
||||
|
||||
// Make sure we traverse the startnodes in a consistent order
|
||||
// to ensure identical PBF encoding on all platforms.
|
||||
std::vector<NodeID> sorted_startnodes;
|
||||
sorted_startnodes.reserve(directed_graph.size());
|
||||
for (const auto &startnode : directed_graph)
|
||||
sorted_startnodes.push_back(startnode.first);
|
||||
std::sort(sorted_startnodes.begin(), sorted_startnodes.end());
|
||||
|
||||
// Look at every node in the directed graph we created
|
||||
for (const auto &startnode : sorted_startnodes)
|
||||
{
|
||||
const auto &nodedata = directed_graph[startnode];
|
||||
BOOST_ASSERT(directed_graph.find(startnode) != directed_graph.end());
|
||||
const auto &nodedata = directed_graph.find(startnode)->second;
|
||||
// For all the outgoing edges from the node
|
||||
for (const auto &approachedge : nodedata)
|
||||
{
|
||||
@@ -110,7 +118,7 @@ getTileTurns(const datafacade::ContiguousInternalMemoryDataFacade<ch::Algorithm>
|
||||
continue;
|
||||
|
||||
// For each of the outgoing edges from our target coordinate
|
||||
for (const auto &exit_edge : directed_graph[approachedge.target_node])
|
||||
for (const auto &exit_edge : directed_graph.find(approachedge.target_node)->second)
|
||||
{
|
||||
// If the next edge has the same edge_based_node_id, then it's
|
||||
// not a turn, so skip it
|
||||
@@ -132,55 +140,32 @@ getTileTurns(const datafacade::ContiguousInternalMemoryDataFacade<ch::Algorithm>
|
||||
//
|
||||
// would offer a backward edge at `b` to `a` (due to the oneway from a to b)
|
||||
// but could also offer a shortcut (b-c-a) from `b` to `a` which is longer.
|
||||
EdgeID smaller_edge_id =
|
||||
facade.FindSmallestEdge(approachedge.edge_based_node_id,
|
||||
exit_edge.edge_based_node_id,
|
||||
[](const contractor::QueryEdge::EdgeData &data) {
|
||||
return data.forward && !data.shortcut;
|
||||
});
|
||||
EdgeID edge_based_edge_id =
|
||||
find_edge(approachedge.edge_based_node_id, exit_edge.edge_based_node_id);
|
||||
|
||||
// Depending on how the graph is constructed, we might have to look for
|
||||
// a backwards edge instead. They're equivalent, just one is available for
|
||||
// a forward routing search, and one is used for the backwards dijkstra
|
||||
// steps. Their weight should be the same, we can use either one.
|
||||
// If we didn't find a forward edge, try for a backward one
|
||||
if (SPECIAL_EDGEID == smaller_edge_id)
|
||||
if (edge_based_edge_id != SPECIAL_EDGEID)
|
||||
{
|
||||
smaller_edge_id =
|
||||
facade.FindSmallestEdge(exit_edge.edge_based_node_id,
|
||||
approachedge.edge_based_node_id,
|
||||
[](const contractor::QueryEdge::EdgeData &data) {
|
||||
return data.backward && !data.shortcut;
|
||||
});
|
||||
}
|
||||
|
||||
// If no edge was found, it means that there's no connection between these
|
||||
// nodes, due to oneways or turn restrictions. Given the edge-based-nodes
|
||||
// that we're examining here, we *should* only find directly-connected
|
||||
// edges, not shortcuts
|
||||
if (smaller_edge_id != SPECIAL_EDGEID)
|
||||
{
|
||||
const auto &data = facade.GetEdgeData(smaller_edge_id);
|
||||
BOOST_ASSERT_MSG(!data.shortcut, "Connecting edge must not be a shortcut");
|
||||
const auto &data = facade.GetEdgeData(edge_based_edge_id);
|
||||
|
||||
// Now, calculate the sum of the weight of all the segments.
|
||||
if (edge_based_node_info[approachedge.edge_based_node_id].is_geometry_forward)
|
||||
if (edge_based_node_info.find(approachedge.edge_based_node_id)
|
||||
->second.is_geometry_forward)
|
||||
{
|
||||
approach_weight_vector = facade.GetUncompressedForwardWeights(
|
||||
edge_based_node_info[approachedge.edge_based_node_id]
|
||||
.packed_geometry_id);
|
||||
edge_based_node_info.find(approachedge.edge_based_node_id)
|
||||
->second.packed_geometry_id);
|
||||
approach_duration_vector = facade.GetUncompressedForwardDurations(
|
||||
edge_based_node_info[approachedge.edge_based_node_id]
|
||||
.packed_geometry_id);
|
||||
edge_based_node_info.find(approachedge.edge_based_node_id)
|
||||
->second.packed_geometry_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
approach_weight_vector = facade.GetUncompressedReverseWeights(
|
||||
edge_based_node_info[approachedge.edge_based_node_id]
|
||||
.packed_geometry_id);
|
||||
edge_based_node_info.find(approachedge.edge_based_node_id)
|
||||
->second.packed_geometry_id);
|
||||
approach_duration_vector = facade.GetUncompressedReverseDurations(
|
||||
edge_based_node_info[approachedge.edge_based_node_id]
|
||||
.packed_geometry_id);
|
||||
edge_based_node_info.find(approachedge.edge_based_node_id)
|
||||
->second.packed_geometry_id);
|
||||
}
|
||||
const auto sum_node_weight = std::accumulate(approach_weight_vector.begin(),
|
||||
approach_weight_vector.end(),
|
||||
@@ -239,6 +224,90 @@ getTileTurns(const datafacade::ContiguousInternalMemoryDataFacade<ch::Algorithm>
|
||||
return all_turn_data;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// CH Version of finding all turn penalties. Here is where the actual work is happening
|
||||
std::vector<TurnData>
|
||||
getTileTurns(const datafacade::ContiguousInternalMemoryDataFacade<ch::Algorithm> &facade,
|
||||
const std::vector<RTreeLeaf> &edges,
|
||||
const std::vector<std::size_t> &sorted_edge_indexes)
|
||||
{
|
||||
// Define how to find the representative edge between two edge based nodes for a CH
|
||||
struct EdgeFinderCH
|
||||
{
|
||||
EdgeFinderCH(const datafacade::ContiguousInternalMemoryDataFacade<ch::Algorithm> &facade)
|
||||
: facade(facade)
|
||||
{
|
||||
}
|
||||
const datafacade::ContiguousInternalMemoryDataFacade<ch::Algorithm> &facade;
|
||||
|
||||
EdgeID operator()(const NodeID approach_node, const NodeID exit_node) const
|
||||
{
|
||||
// Find the connection between our source road and the target node
|
||||
// Since we only want to find direct edges, we cannot check shortcut edges here.
|
||||
// Otherwise we might find a forward edge even though a shorter backward edge
|
||||
// exists (due to oneways).
|
||||
//
|
||||
// a > - > - > - b
|
||||
// | |
|
||||
// |------ c ----|
|
||||
//
|
||||
// would offer a backward edge at `b` to `a` (due to the oneway from a to b)
|
||||
// but could also offer a shortcut (b-c-a) from `b` to `a` which is longer.
|
||||
EdgeID edge_id = facade.FindSmallestEdge(
|
||||
approach_node, exit_node, [](const contractor::QueryEdge::EdgeData &data) {
|
||||
return data.forward && !data.shortcut;
|
||||
});
|
||||
|
||||
// Depending on how the graph is constructed, we might have to look for
|
||||
// a backwards edge instead. They're equivalent, just one is available for
|
||||
// a forward routing search, and one is used for the backwards dijkstra
|
||||
// steps. Their weight should be the same, we can use either one.
|
||||
// If we didn't find a forward edge, try for a backward one
|
||||
if (SPECIAL_EDGEID == edge_id)
|
||||
{
|
||||
edge_id = facade.FindSmallestEdge(
|
||||
exit_node, approach_node, [](const contractor::QueryEdge::EdgeData &data) {
|
||||
return data.backward && !data.shortcut;
|
||||
});
|
||||
}
|
||||
|
||||
BOOST_ASSERT_MSG(edge_id == SPECIAL_EDGEID || !facade.GetEdgeData(edge_id).shortcut,
|
||||
"Connecting edge must not be a shortcut");
|
||||
return edge_id;
|
||||
}
|
||||
};
|
||||
|
||||
EdgeFinderCH edge_finder(facade);
|
||||
return generateTurns(facade, edges, sorted_edge_indexes, edge_finder);
|
||||
}
|
||||
|
||||
// MLD version to find all turns
|
||||
std::vector<TurnData>
|
||||
getTileTurns(const datafacade::ContiguousInternalMemoryDataFacade<mld::Algorithm> &facade,
|
||||
const std::vector<RTreeLeaf> &edges,
|
||||
const std::vector<std::size_t> &sorted_edge_indexes)
|
||||
{
|
||||
// Define how to find the representative edge between two edge-based-nodes for a MLD
|
||||
struct EdgeFinderMLD
|
||||
{
|
||||
EdgeFinderMLD(const datafacade::ContiguousInternalMemoryDataFacade<mld::Algorithm> &facade)
|
||||
: facade(facade)
|
||||
{
|
||||
}
|
||||
const datafacade::ContiguousInternalMemoryDataFacade<mld::Algorithm> &facade;
|
||||
|
||||
EdgeID operator()(const NodeID approach_node, const NodeID exit_node) const
|
||||
{
|
||||
return facade.FindEdge(approach_node, exit_node);
|
||||
}
|
||||
};
|
||||
|
||||
EdgeFinderMLD edge_finder(facade);
|
||||
|
||||
return generateTurns(facade, edges, sorted_edge_indexes, edge_finder);
|
||||
}
|
||||
|
||||
} // namespace routing_algorithms
|
||||
} // namespace engine
|
||||
} // namespace osrm
|
||||
|
||||
@@ -30,6 +30,11 @@
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <tbb/blocked_range.h>
|
||||
#include <tbb/parallel_for.h>
|
||||
#include <tbb/pipeline.h>
|
||||
#include <tbb/task_scheduler_init.h>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace extractor
|
||||
@@ -319,6 +324,7 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
const std::string &turn_duration_penalties_filename,
|
||||
const std::string &turn_penalties_index_filename)
|
||||
{
|
||||
|
||||
util::Log() << "Generating edge-expanded edges ";
|
||||
|
||||
std::size_t node_based_edge_counter = 0;
|
||||
@@ -362,189 +368,318 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
const auto weight_multiplier =
|
||||
scripting_environment.GetProfileProperties().GetWeightMultiplier();
|
||||
|
||||
// The following block generates the edge-based-edges using a parallel processing
|
||||
// pipeline. Sets of intersection IDs are batched in groups of GRAINSIZE (100)
|
||||
// `generator_stage`,
|
||||
// then those groups are processed in parallel `processor_stage`. Finally, results are
|
||||
// appended to the various buffer vectors by the `output_stage` in the same order
|
||||
// that the `generator_stage` created them in (tbb::filter::serial_in_order creates this
|
||||
// guarantee). The order needs to be maintained because we depend on it later in the
|
||||
// processing pipeline.
|
||||
{
|
||||
util::UnbufferedLog log;
|
||||
|
||||
util::Percent progress(log, m_node_based_graph->GetNumberOfNodes());
|
||||
const NodeID node_count = m_node_based_graph->GetNumberOfNodes();
|
||||
util::Percent progress(log, node_count);
|
||||
// This counter is used to keep track of how far along we've made it
|
||||
std::uint64_t nodes_completed = 0;
|
||||
|
||||
// going over all nodes (which form the center of an intersection), we compute all
|
||||
// possible turns along these intersections.
|
||||
for (const auto node_at_center_of_intersection :
|
||||
util::irange(0u, m_node_based_graph->GetNumberOfNodes()))
|
||||
{
|
||||
progress.PrintStatus(node_at_center_of_intersection);
|
||||
|
||||
const auto shape_result =
|
||||
turn_analysis.ComputeIntersectionShapes(node_at_center_of_intersection);
|
||||
NodeID current_node = 0;
|
||||
|
||||
// all nodes in the graph are connected in both directions. We check all outgoing nodes
|
||||
// to
|
||||
// find the incoming edge. This is a larger search overhead, but the cost we need to pay
|
||||
// to
|
||||
// generate edges here is worth the additional search overhead.
|
||||
//
|
||||
// a -> b <-> c
|
||||
// |
|
||||
// v
|
||||
// d
|
||||
//
|
||||
// will have:
|
||||
// a: b,rev=0
|
||||
// b: a,rev=1 c,rev=0 d,rev=0
|
||||
// c: b,rev=0
|
||||
//
|
||||
// From the flags alone, we cannot determine which nodes are connected to `b` by an
|
||||
// outgoing
|
||||
// edge. Therefore, we have to search all connected edges for edges entering `b`
|
||||
for (const EdgeID outgoing_edge :
|
||||
m_node_based_graph->GetAdjacentEdgeRange(node_at_center_of_intersection))
|
||||
{
|
||||
const NodeID node_along_road_entering =
|
||||
m_node_based_graph->GetTarget(outgoing_edge);
|
||||
// Handle intersections in sets of 100. The pipeline below has a serial bottleneck
|
||||
// during the writing phase, so we want to make the parallel workers do more work
|
||||
// to give the serial final stage time to complete its tasks.
|
||||
const constexpr unsigned GRAINSIZE = 100;
|
||||
|
||||
const auto incoming_edge = m_node_based_graph->FindEdge(
|
||||
node_along_road_entering, node_at_center_of_intersection);
|
||||
|
||||
if (m_node_based_graph->GetEdgeData(incoming_edge).reversed)
|
||||
continue;
|
||||
|
||||
++node_based_edge_counter;
|
||||
|
||||
auto intersection_with_flags_and_angles =
|
||||
turn_analysis.GetIntersectionGenerator().TransformIntersectionShapeIntoView(
|
||||
node_along_road_entering,
|
||||
incoming_edge,
|
||||
shape_result.annotated_normalized_shape.normalized_shape,
|
||||
shape_result.intersection_shape,
|
||||
shape_result.annotated_normalized_shape.performed_merges);
|
||||
|
||||
auto intersection = turn_analysis.AssignTurnTypes(
|
||||
node_along_road_entering, incoming_edge, intersection_with_flags_and_angles);
|
||||
|
||||
OSRM_ASSERT(intersection.valid(), m_coordinates[node_at_center_of_intersection]);
|
||||
|
||||
intersection = turn_lane_handler.assignTurnLanes(
|
||||
node_along_road_entering, incoming_edge, std::move(intersection));
|
||||
|
||||
// the entry class depends on the turn, so we have to classify the interesction for
|
||||
// every edge
|
||||
const auto turn_classification = classifyIntersection(intersection);
|
||||
|
||||
const auto entry_class_id = [&](const util::guidance::EntryClass entry_class) {
|
||||
if (0 == entry_class_hash.count(entry_class))
|
||||
{
|
||||
const auto id = static_cast<std::uint16_t>(entry_class_hash.size());
|
||||
entry_class_hash[entry_class] = id;
|
||||
return id;
|
||||
}
|
||||
else
|
||||
{
|
||||
return entry_class_hash.find(entry_class)->second;
|
||||
}
|
||||
}(turn_classification.first);
|
||||
|
||||
const auto bearing_class_id =
|
||||
[&](const util::guidance::BearingClass bearing_class) {
|
||||
if (0 == bearing_class_hash.count(bearing_class))
|
||||
{
|
||||
const auto id = static_cast<std::uint32_t>(bearing_class_hash.size());
|
||||
bearing_class_hash[bearing_class] = id;
|
||||
return id;
|
||||
}
|
||||
else
|
||||
{
|
||||
return bearing_class_hash.find(bearing_class)->second;
|
||||
}
|
||||
}(turn_classification.second);
|
||||
bearing_class_by_node_based_node[node_at_center_of_intersection] = bearing_class_id;
|
||||
|
||||
for (const auto &turn : intersection)
|
||||
// First part of the pipeline generates iterator ranges of IDs in sets of GRAINSIZE
|
||||
tbb::filter_t<void, tbb::blocked_range<NodeID>> generator_stage(
|
||||
tbb::filter::serial_in_order, [&](tbb::flow_control &fc) -> tbb::blocked_range<NodeID> {
|
||||
if (current_node < node_count)
|
||||
{
|
||||
// only keep valid turns
|
||||
if (!turn.entry_allowed)
|
||||
continue;
|
||||
|
||||
// only add an edge if turn is not prohibited
|
||||
const EdgeData &edge_data1 = m_node_based_graph->GetEdgeData(incoming_edge);
|
||||
const EdgeData &edge_data2 = m_node_based_graph->GetEdgeData(turn.eid);
|
||||
|
||||
BOOST_ASSERT(edge_data1.edge_id != edge_data2.edge_id);
|
||||
BOOST_ASSERT(!edge_data1.reversed);
|
||||
BOOST_ASSERT(!edge_data2.reversed);
|
||||
|
||||
// the following is the core of the loop.
|
||||
turn_data_container.push_back(
|
||||
turn.instruction,
|
||||
turn.lane_data_id,
|
||||
entry_class_id,
|
||||
util::guidance::TurnBearing(intersection[0].bearing),
|
||||
util::guidance::TurnBearing(turn.bearing));
|
||||
|
||||
// compute weight and duration penalties
|
||||
auto is_traffic_light = m_traffic_lights.count(node_at_center_of_intersection);
|
||||
ExtractionTurn extracted_turn(turn, is_traffic_light);
|
||||
extracted_turn.source_restricted = edge_data1.restricted;
|
||||
extracted_turn.target_restricted = edge_data2.restricted;
|
||||
scripting_environment.ProcessTurn(extracted_turn);
|
||||
|
||||
// turn penalties are limited to [-2^15, 2^15) which roughly
|
||||
// translates to 54 minutes and fits signed 16bit deci-seconds
|
||||
auto weight_penalty =
|
||||
boost::numeric_cast<TurnPenalty>(extracted_turn.weight * weight_multiplier);
|
||||
auto duration_penalty =
|
||||
boost::numeric_cast<TurnPenalty>(extracted_turn.duration * 10.);
|
||||
|
||||
BOOST_ASSERT(SPECIAL_NODEID != edge_data1.edge_id);
|
||||
BOOST_ASSERT(SPECIAL_NODEID != edge_data2.edge_id);
|
||||
|
||||
// NOTE: potential overflow here if we hit 2^32 routable edges
|
||||
BOOST_ASSERT(m_edge_based_edge_list.size() <=
|
||||
std::numeric_limits<NodeID>::max());
|
||||
auto turn_id = m_edge_based_edge_list.size();
|
||||
auto weight =
|
||||
boost::numeric_cast<EdgeWeight>(edge_data1.weight + weight_penalty);
|
||||
auto duration =
|
||||
boost::numeric_cast<EdgeWeight>(edge_data1.duration + duration_penalty);
|
||||
m_edge_based_edge_list.emplace_back(edge_data1.edge_id,
|
||||
edge_data2.edge_id,
|
||||
turn_id,
|
||||
weight,
|
||||
duration,
|
||||
true,
|
||||
false);
|
||||
|
||||
BOOST_ASSERT(turn_weight_penalties.size() == turn_id);
|
||||
turn_weight_penalties.push_back(weight_penalty);
|
||||
BOOST_ASSERT(turn_duration_penalties.size() == turn_id);
|
||||
turn_duration_penalties.push_back(duration_penalty);
|
||||
|
||||
// We write out the mapping between the edge-expanded edges and the
|
||||
// original nodes. Since each edge represents a possible maneuver, external
|
||||
// programs can use this to quickly perform updates to edge weights in order
|
||||
// to penalize certain turns.
|
||||
|
||||
// If this edge is 'trivial' -- where the compressed edge corresponds
|
||||
// exactly to an original OSM segment -- we can pull the turn's preceding
|
||||
// node ID directly with `node_along_road_entering`; otherwise, we need to
|
||||
// look up the node immediately preceding the turn from the compressed edge
|
||||
// container.
|
||||
const bool isTrivial = m_compressed_edge_container.IsTrivial(incoming_edge);
|
||||
|
||||
const auto &from_node =
|
||||
isTrivial ? node_along_road_entering
|
||||
: m_compressed_edge_container.GetLastEdgeSourceID(incoming_edge);
|
||||
const auto &via_node =
|
||||
m_compressed_edge_container.GetLastEdgeTargetID(incoming_edge);
|
||||
const auto &to_node =
|
||||
m_compressed_edge_container.GetFirstEdgeTargetID(turn.eid);
|
||||
|
||||
lookup::TurnIndexBlock turn_index_block = {from_node, via_node, to_node};
|
||||
|
||||
turn_penalties_index_file.WriteOne(turn_index_block);
|
||||
auto next_node = std::min(current_node + GRAINSIZE, node_count);
|
||||
auto result = tbb::blocked_range<NodeID>(current_node, next_node);
|
||||
current_node = next_node;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fc.stop();
|
||||
return tbb::blocked_range<NodeID>(node_count, node_count);
|
||||
}
|
||||
});
|
||||
|
||||
// This struct is the buffered output of the `processor_stage`. This data is
|
||||
// appended to the various output arrays/files by the `output_stage`.
|
||||
struct IntersectionData
|
||||
{
|
||||
std::size_t nodes_processed = 0;
|
||||
std::vector<lookup::TurnIndexBlock> turn_indexes;
|
||||
std::vector<EdgeBasedEdge> edges_list;
|
||||
std::vector<TurnPenalty> turn_weight_penalties;
|
||||
std::vector<TurnPenalty> turn_duration_penalties;
|
||||
std::vector<TurnData> turn_data_container;
|
||||
};
|
||||
|
||||
// Second part of the pipeline is where the intersection analysis is done for
|
||||
// each intersection
|
||||
tbb::filter_t<tbb::blocked_range<NodeID>, std::shared_ptr<IntersectionData>>
|
||||
processor_stage(
|
||||
tbb::filter::parallel, [&](const tbb::blocked_range<NodeID> &intersection_node_range) {
|
||||
|
||||
auto buffer = std::make_shared<IntersectionData>();
|
||||
buffer->nodes_processed =
|
||||
intersection_node_range.end() - intersection_node_range.begin();
|
||||
|
||||
// If we get fed a 0-length range for some reason, we can just return right away
|
||||
if (buffer->nodes_processed == 0)
|
||||
return buffer;
|
||||
|
||||
for (auto node_at_center_of_intersection = intersection_node_range.begin(),
|
||||
end = intersection_node_range.end();
|
||||
node_at_center_of_intersection < end;
|
||||
++node_at_center_of_intersection)
|
||||
{
|
||||
|
||||
// We capture the thread-local work in these objects, then flush
|
||||
// them in a controlled manner at the end of the parallel range
|
||||
|
||||
const auto shape_result =
|
||||
turn_analysis.ComputeIntersectionShapes(node_at_center_of_intersection);
|
||||
|
||||
// all nodes in the graph are connected in both directions. We check all
|
||||
// outgoing nodes to find the incoming edge. This is a larger search overhead,
|
||||
// but the cost we need to pay to generate edges here is worth the additional
|
||||
// search overhead.
|
||||
//
|
||||
// a -> b <-> c
|
||||
// |
|
||||
// v
|
||||
// d
|
||||
//
|
||||
// will have:
|
||||
// a: b,rev=0
|
||||
// b: a,rev=1 c,rev=0 d,rev=0
|
||||
// c: b,rev=0
|
||||
//
|
||||
// From the flags alone, we cannot determine which nodes are connected to
|
||||
// `b` by an outgoing edge. Therefore, we have to search all connected edges for
|
||||
// edges entering `b`
|
||||
for (const EdgeID outgoing_edge :
|
||||
m_node_based_graph->GetAdjacentEdgeRange(node_at_center_of_intersection))
|
||||
{
|
||||
const NodeID node_along_road_entering =
|
||||
m_node_based_graph->GetTarget(outgoing_edge);
|
||||
|
||||
const auto incoming_edge = m_node_based_graph->FindEdge(
|
||||
node_along_road_entering, node_at_center_of_intersection);
|
||||
|
||||
if (m_node_based_graph->GetEdgeData(incoming_edge).reversed)
|
||||
continue;
|
||||
|
||||
++node_based_edge_counter;
|
||||
|
||||
auto intersection_with_flags_and_angles =
|
||||
turn_analysis.GetIntersectionGenerator()
|
||||
.TransformIntersectionShapeIntoView(
|
||||
node_along_road_entering,
|
||||
incoming_edge,
|
||||
shape_result.annotated_normalized_shape.normalized_shape,
|
||||
shape_result.intersection_shape,
|
||||
shape_result.annotated_normalized_shape.performed_merges);
|
||||
|
||||
auto intersection =
|
||||
turn_analysis.AssignTurnTypes(node_along_road_entering,
|
||||
incoming_edge,
|
||||
intersection_with_flags_and_angles);
|
||||
|
||||
OSRM_ASSERT(intersection.valid(),
|
||||
m_coordinates[node_at_center_of_intersection]);
|
||||
|
||||
intersection = turn_lane_handler.assignTurnLanes(
|
||||
node_along_road_entering, incoming_edge, std::move(intersection));
|
||||
|
||||
// the entry class depends on the turn, so we have to classify the
|
||||
// interesction for
|
||||
// every edge
|
||||
const auto turn_classification = classifyIntersection(intersection);
|
||||
|
||||
const auto entry_class_id =
|
||||
entry_class_hash.ConcurrentFindOrAdd(turn_classification.first);
|
||||
|
||||
const auto bearing_class_id =
|
||||
bearing_class_hash.ConcurrentFindOrAdd(turn_classification.second);
|
||||
|
||||
// Note - this is strictly speaking not thread safe, but we know we
|
||||
// should never be touching the same element twice, so we should
|
||||
// be fine.
|
||||
bearing_class_by_node_based_node[node_at_center_of_intersection] =
|
||||
bearing_class_id;
|
||||
|
||||
for (const auto &turn : intersection)
|
||||
{
|
||||
// only keep valid turns
|
||||
if (!turn.entry_allowed)
|
||||
continue;
|
||||
|
||||
// only add an edge if turn is not prohibited
|
||||
const EdgeData &edge_data1 =
|
||||
m_node_based_graph->GetEdgeData(incoming_edge);
|
||||
const EdgeData &edge_data2 = m_node_based_graph->GetEdgeData(turn.eid);
|
||||
|
||||
BOOST_ASSERT(edge_data1.edge_id != edge_data2.edge_id);
|
||||
BOOST_ASSERT(!edge_data1.reversed);
|
||||
BOOST_ASSERT(!edge_data2.reversed);
|
||||
|
||||
// the following is the core of the loop.
|
||||
buffer->turn_data_container.push_back(
|
||||
{turn.instruction,
|
||||
turn.lane_data_id,
|
||||
entry_class_id,
|
||||
util::guidance::TurnBearing(intersection[0].bearing),
|
||||
util::guidance::TurnBearing(turn.bearing)});
|
||||
|
||||
// compute weight and duration penalties
|
||||
auto is_traffic_light =
|
||||
m_traffic_lights.count(node_at_center_of_intersection);
|
||||
ExtractionTurn extracted_turn(turn, is_traffic_light);
|
||||
extracted_turn.source_restricted = edge_data1.restricted;
|
||||
extracted_turn.target_restricted = edge_data2.restricted;
|
||||
scripting_environment.ProcessTurn(extracted_turn);
|
||||
|
||||
// turn penalties are limited to [-2^15, 2^15) which roughly
|
||||
// translates to 54 minutes and fits signed 16bit deci-seconds
|
||||
auto weight_penalty = boost::numeric_cast<TurnPenalty>(
|
||||
extracted_turn.weight * weight_multiplier);
|
||||
auto duration_penalty =
|
||||
boost::numeric_cast<TurnPenalty>(extracted_turn.duration * 10.);
|
||||
|
||||
BOOST_ASSERT(SPECIAL_NODEID != edge_data1.edge_id);
|
||||
BOOST_ASSERT(SPECIAL_NODEID != edge_data2.edge_id);
|
||||
|
||||
// auto turn_id = m_edge_based_edge_list.size();
|
||||
auto weight =
|
||||
boost::numeric_cast<EdgeWeight>(edge_data1.weight + weight_penalty);
|
||||
auto duration = boost::numeric_cast<EdgeWeight>(edge_data1.duration +
|
||||
duration_penalty);
|
||||
buffer->edges_list.emplace_back(
|
||||
edge_data1.edge_id,
|
||||
edge_data2.edge_id,
|
||||
SPECIAL_NODEID, // This will be updated once the main loop
|
||||
// completes!
|
||||
weight,
|
||||
duration,
|
||||
true,
|
||||
false);
|
||||
|
||||
BOOST_ASSERT(buffer->turn_weight_penalties.size() ==
|
||||
buffer->edges_list.size() - 1);
|
||||
buffer->turn_weight_penalties.push_back(weight_penalty);
|
||||
BOOST_ASSERT(buffer->turn_duration_penalties.size() ==
|
||||
buffer->edges_list.size() - 1);
|
||||
buffer->turn_duration_penalties.push_back(duration_penalty);
|
||||
|
||||
// We write out the mapping between the edge-expanded edges and the
|
||||
// original nodes. Since each edge represents a possible maneuver,
|
||||
// external programs can use this to quickly perform updates to edge
|
||||
// weights in order to penalize certain turns.
|
||||
|
||||
// If this edge is 'trivial' -- where the compressed edge corresponds
|
||||
// exactly to an original OSM segment -- we can pull the turn's
|
||||
// preceding node ID directly with `node_along_road_entering`;
|
||||
// otherwise, we need to look up the node immediately preceding the turn
|
||||
// from the compressed edge container.
|
||||
const bool isTrivial =
|
||||
m_compressed_edge_container.IsTrivial(incoming_edge);
|
||||
|
||||
const auto &from_node =
|
||||
isTrivial ? node_along_road_entering
|
||||
: m_compressed_edge_container.GetLastEdgeSourceID(
|
||||
incoming_edge);
|
||||
const auto &via_node =
|
||||
m_compressed_edge_container.GetLastEdgeTargetID(incoming_edge);
|
||||
const auto &to_node =
|
||||
m_compressed_edge_container.GetFirstEdgeTargetID(turn.eid);
|
||||
|
||||
buffer->turn_indexes.push_back({from_node, via_node, to_node});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return buffer;
|
||||
});
|
||||
|
||||
// Because we write TurnIndexBlock data as we go, we'll
|
||||
// buffer them into groups of 1000 to reduce the syscall
|
||||
// count by 1000x. This doesn't need much memory, but
|
||||
// greatly reduces the syscall overhead of writing lots
|
||||
// of small objects
|
||||
const constexpr int TURN_INDEX_WRITE_BUFFER_SIZE = 1000;
|
||||
std::vector<lookup::TurnIndexBlock> turn_indexes_write_buffer;
|
||||
turn_indexes_write_buffer.reserve(TURN_INDEX_WRITE_BUFFER_SIZE);
|
||||
|
||||
// Last part of the pipeline puts all the calculated data into the serial buffers
|
||||
tbb::filter_t<std::shared_ptr<IntersectionData>, void> output_stage(
|
||||
tbb::filter::serial_in_order, [&](const std::shared_ptr<IntersectionData> buffer) {
|
||||
|
||||
nodes_completed += buffer->nodes_processed;
|
||||
progress.PrintStatus(nodes_completed);
|
||||
|
||||
// NOTE: potential overflow here if we hit 2^32 routable edges
|
||||
m_edge_based_edge_list.append(buffer->edges_list.begin(), buffer->edges_list.end());
|
||||
BOOST_ASSERT(m_edge_based_edge_list.size() <= std::numeric_limits<NodeID>::max());
|
||||
|
||||
turn_weight_penalties.insert(turn_weight_penalties.end(),
|
||||
buffer->turn_weight_penalties.begin(),
|
||||
buffer->turn_weight_penalties.end());
|
||||
turn_duration_penalties.insert(turn_duration_penalties.end(),
|
||||
buffer->turn_duration_penalties.begin(),
|
||||
buffer->turn_duration_penalties.end());
|
||||
turn_data_container.append(buffer->turn_data_container);
|
||||
|
||||
turn_indexes_write_buffer.insert(turn_indexes_write_buffer.end(),
|
||||
buffer->turn_indexes.begin(),
|
||||
buffer->turn_indexes.end());
|
||||
|
||||
// Buffer writes to reduce syscall count
|
||||
if (turn_indexes_write_buffer.size() >= TURN_INDEX_WRITE_BUFFER_SIZE)
|
||||
{
|
||||
turn_penalties_index_file.WriteFrom(turn_indexes_write_buffer.data(),
|
||||
turn_indexes_write_buffer.size());
|
||||
turn_indexes_write_buffer.clear();
|
||||
}
|
||||
});
|
||||
|
||||
// Now, execute the pipeline. The value of "5" here was chosen by experimentation
|
||||
// on a 16-CPU machine and seemed to give the best performance. This value needs
|
||||
// to be balanced with the GRAINSIZE above - ideally, the pipeline puts as much work
|
||||
// as possible in the `intersection_handler` step so that those parallel workers don't
|
||||
// get blocked too much by the slower (io-performing) `buffer_storage`
|
||||
tbb::parallel_pipeline(tbb::task_scheduler_init::default_num_threads() * 5,
|
||||
generator_stage & processor_stage & output_stage);
|
||||
|
||||
// Flush the turn_indexes_write_buffer if it's not empty
|
||||
if (!turn_indexes_write_buffer.empty())
|
||||
{
|
||||
turn_penalties_index_file.WriteFrom(turn_indexes_write_buffer.data(),
|
||||
turn_indexes_write_buffer.size());
|
||||
turn_indexes_write_buffer.clear();
|
||||
}
|
||||
}
|
||||
|
||||
util::Log() << "Reunmbering turns";
|
||||
// Now, update the turn_id property on every EdgeBasedEdge - it will equal the
|
||||
// position in the m_edge_based_edge_list array for each object.
|
||||
tbb::parallel_for(tbb::blocked_range<NodeID>(0, m_edge_based_edge_list.size()),
|
||||
[this](const tbb::blocked_range<NodeID> &range) {
|
||||
for (auto x = range.begin(), end = range.end(); x != end; ++x)
|
||||
{
|
||||
m_edge_based_edge_list[x].data.turn_id = x;
|
||||
}
|
||||
});
|
||||
|
||||
// write weight penalties per turn
|
||||
BOOST_ASSERT(turn_weight_penalties.size() == turn_duration_penalties.size());
|
||||
{
|
||||
@@ -559,17 +694,17 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
storage::serialization::write(writer, turn_duration_penalties);
|
||||
}
|
||||
|
||||
util::Log() << "Created " << entry_class_hash.size() << " entry classes and "
|
||||
<< bearing_class_hash.size() << " Bearing Classes";
|
||||
util::Log() << "Created " << entry_class_hash.data.size() << " entry classes and "
|
||||
<< bearing_class_hash.data.size() << " Bearing Classes";
|
||||
|
||||
util::Log() << "Writing Turn Lane Data to File...";
|
||||
{
|
||||
storage::io::FileWriter writer(turn_lane_data_filename,
|
||||
storage::io::FileWriter::GenerateFingerprint);
|
||||
|
||||
std::vector<util::guidance::LaneTupleIdPair> lane_data(lane_data_map.size());
|
||||
std::vector<util::guidance::LaneTupleIdPair> lane_data(lane_data_map.data.size());
|
||||
// extract lane data sorted by ID
|
||||
for (auto itr : lane_data_map)
|
||||
for (auto itr : lane_data_map.data)
|
||||
lane_data[itr.second] = itr.first;
|
||||
|
||||
storage::serialization::write(writer, lane_data);
|
||||
@@ -591,8 +726,8 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
|
||||
std::vector<util::guidance::BearingClass> EdgeBasedGraphFactory::GetBearingClasses() const
|
||||
{
|
||||
std::vector<util::guidance::BearingClass> result(bearing_class_hash.size());
|
||||
for (const auto &pair : bearing_class_hash)
|
||||
std::vector<util::guidance::BearingClass> result(bearing_class_hash.data.size());
|
||||
for (const auto &pair : bearing_class_hash.data)
|
||||
{
|
||||
BOOST_ASSERT(pair.second < result.size());
|
||||
result[pair.second] = pair.first;
|
||||
@@ -612,8 +747,8 @@ std::vector<BearingClassID> &EdgeBasedGraphFactory::GetBearingClassIds()
|
||||
|
||||
std::vector<util::guidance::EntryClass> EdgeBasedGraphFactory::GetEntryClasses() const
|
||||
{
|
||||
std::vector<util::guidance::EntryClass> result(entry_class_hash.size());
|
||||
for (const auto &pair : entry_class_hash)
|
||||
std::vector<util::guidance::EntryClass> result(entry_class_hash.data.size());
|
||||
for (const auto &pair : entry_class_hash.data)
|
||||
{
|
||||
BOOST_ASSERT(pair.second < result.size());
|
||||
result[pair.second] = pair.first;
|
||||
|
||||
+67
-46
@@ -33,12 +33,13 @@
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
#include <boost/iterator/function_input_iterator.hpp>
|
||||
#include <boost/optional/optional.hpp>
|
||||
#include <boost/scope_exit.hpp>
|
||||
|
||||
#include <osmium/io/any_input.hpp>
|
||||
|
||||
#include <tbb/concurrent_vector.h>
|
||||
#include <tbb/pipeline.h>
|
||||
#include <tbb/task_scheduler_init.h>
|
||||
|
||||
#include <cstdlib>
|
||||
@@ -76,8 +77,9 @@ transformTurnLaneMapIntoArrays(const guidance::LaneDescriptionMap &turn_lane_map
|
||||
//
|
||||
// turn lane offsets points into the locations of the turn_lane_masks array. We use a standard
|
||||
// adjacency array like structure to store the turn lane masks.
|
||||
std::vector<std::uint32_t> turn_lane_offsets(turn_lane_map.size() + 2); // empty ID + sentinel
|
||||
for (auto entry = turn_lane_map.begin(); entry != turn_lane_map.end(); ++entry)
|
||||
std::vector<std::uint32_t> turn_lane_offsets(turn_lane_map.data.size() +
|
||||
2); // empty ID + sentinel
|
||||
for (auto entry = turn_lane_map.data.begin(); entry != turn_lane_map.data.end(); ++entry)
|
||||
turn_lane_offsets[entry->second + 1] = entry->first.size();
|
||||
|
||||
// inplace prefix sum
|
||||
@@ -85,7 +87,7 @@ transformTurnLaneMapIntoArrays(const guidance::LaneDescriptionMap &turn_lane_map
|
||||
|
||||
// allocate the current masks
|
||||
std::vector<guidance::TurnLaneType::Mask> turn_lane_masks(turn_lane_offsets.back());
|
||||
for (auto entry = turn_lane_map.begin(); entry != turn_lane_map.end(); ++entry)
|
||||
for (auto entry = turn_lane_map.data.begin(); entry != turn_lane_map.data.end(); ++entry)
|
||||
std::copy(entry->first.begin(),
|
||||
entry->first.end(),
|
||||
turn_lane_masks.begin() + turn_lane_offsets[entry->second]);
|
||||
@@ -252,11 +254,6 @@ std::vector<TurnRestriction> Extractor::ParseOSMData(ScriptingEnvironment &scrip
|
||||
|
||||
timestamp_file.WriteFrom(timestamp.c_str(), timestamp.length());
|
||||
|
||||
// initialize vectors holding parsed objects
|
||||
tbb::concurrent_vector<std::pair<std::size_t, ExtractionNode>> resulting_nodes;
|
||||
tbb::concurrent_vector<std::pair<std::size_t, ExtractionWay>> resulting_ways;
|
||||
tbb::concurrent_vector<boost::optional<InputRestrictionContainer>> resulting_restrictions;
|
||||
|
||||
std::vector<std::string> restrictions = scripting_environment.GetRestrictions();
|
||||
// setup restriction parser
|
||||
const RestrictionParser restriction_parser(
|
||||
@@ -264,46 +261,70 @@ std::vector<TurnRestriction> Extractor::ParseOSMData(ScriptingEnvironment &scrip
|
||||
config.parse_conditionals,
|
||||
restrictions);
|
||||
|
||||
// create a vector of iterators into the buffer
|
||||
for (std::vector<osmium::memory::Buffer::const_iterator> osm_elements;
|
||||
const osmium::memory::Buffer buffer = reader.read();
|
||||
osm_elements.clear())
|
||||
std::mutex process_mutex;
|
||||
|
||||
using SharedBuffer = std::shared_ptr<const osmium::memory::Buffer>;
|
||||
struct ParsedBuffer
|
||||
{
|
||||
for (auto iter = std::begin(buffer), end = std::end(buffer); iter != end; ++iter)
|
||||
{
|
||||
osm_elements.push_back(iter);
|
||||
}
|
||||
SharedBuffer buffer;
|
||||
std::vector<std::pair<const osmium::Node &, ExtractionNode>> resulting_nodes;
|
||||
std::vector<std::pair<const osmium::Way &, ExtractionWay>> resulting_ways;
|
||||
std::vector<boost::optional<InputRestrictionContainer>> resulting_restrictions;
|
||||
};
|
||||
|
||||
// clear resulting vectors
|
||||
resulting_nodes.clear();
|
||||
resulting_ways.clear();
|
||||
resulting_restrictions.clear();
|
||||
tbb::filter_t<void, SharedBuffer> buffer_reader(
|
||||
tbb::filter::serial_in_order, [&](tbb::flow_control &fc) {
|
||||
if (auto buffer = reader.read())
|
||||
{
|
||||
return std::make_shared<const osmium::memory::Buffer>(std::move(buffer));
|
||||
}
|
||||
else
|
||||
{
|
||||
fc.stop();
|
||||
return SharedBuffer{};
|
||||
}
|
||||
});
|
||||
tbb::filter_t<SharedBuffer, std::shared_ptr<ParsedBuffer>> buffer_transform(
|
||||
tbb::filter::parallel, [&](const SharedBuffer buffer) {
|
||||
if (!buffer)
|
||||
return std::shared_ptr<ParsedBuffer>{};
|
||||
|
||||
scripting_environment.ProcessElements(osm_elements,
|
||||
restriction_parser,
|
||||
resulting_nodes,
|
||||
resulting_ways,
|
||||
resulting_restrictions);
|
||||
auto parsed_buffer = std::make_shared<ParsedBuffer>();
|
||||
parsed_buffer->buffer = buffer;
|
||||
scripting_environment.ProcessElements(*buffer,
|
||||
restriction_parser,
|
||||
parsed_buffer->resulting_nodes,
|
||||
parsed_buffer->resulting_ways,
|
||||
parsed_buffer->resulting_restrictions);
|
||||
return parsed_buffer;
|
||||
});
|
||||
tbb::filter_t<std::shared_ptr<ParsedBuffer>, void> buffer_storage(
|
||||
tbb::filter::serial_in_order, [&](const std::shared_ptr<ParsedBuffer> parsed_buffer) {
|
||||
if (!parsed_buffer)
|
||||
return;
|
||||
|
||||
number_of_nodes += parsed_buffer->resulting_nodes.size();
|
||||
// put parsed objects thru extractor callbacks
|
||||
for (const auto &result : parsed_buffer->resulting_nodes)
|
||||
{
|
||||
extractor_callbacks->ProcessNode(result.first, result.second);
|
||||
}
|
||||
number_of_ways += parsed_buffer->resulting_ways.size();
|
||||
for (const auto &result : parsed_buffer->resulting_ways)
|
||||
{
|
||||
extractor_callbacks->ProcessWay(result.first, result.second);
|
||||
}
|
||||
number_of_relations += parsed_buffer->resulting_restrictions.size();
|
||||
for (const auto &result : parsed_buffer->resulting_restrictions)
|
||||
{
|
||||
extractor_callbacks->ProcessRestriction(result);
|
||||
}
|
||||
});
|
||||
|
||||
// Number of pipeline tokens that yielded the best speedup was about 1.5 * num_cores
|
||||
tbb::parallel_pipeline(tbb::task_scheduler_init::default_num_threads() * 1.5,
|
||||
buffer_reader & buffer_transform & buffer_storage);
|
||||
|
||||
number_of_nodes += resulting_nodes.size();
|
||||
// put parsed objects thru extractor callbacks
|
||||
for (const auto &result : resulting_nodes)
|
||||
{
|
||||
extractor_callbacks->ProcessNode(
|
||||
static_cast<const osmium::Node &>(*(osm_elements[result.first])), result.second);
|
||||
}
|
||||
number_of_ways += resulting_ways.size();
|
||||
for (const auto &result : resulting_ways)
|
||||
{
|
||||
extractor_callbacks->ProcessWay(
|
||||
static_cast<const osmium::Way &>(*(osm_elements[result.first])), result.second);
|
||||
}
|
||||
number_of_relations += resulting_restrictions.size();
|
||||
for (const auto &result : resulting_restrictions)
|
||||
{
|
||||
extractor_callbacks->ProcessRestriction(result);
|
||||
}
|
||||
}
|
||||
TIMER_STOP(parsing);
|
||||
util::Log() << "Parsing finished after " << TIMER_SEC(parsing) << " seconds";
|
||||
|
||||
@@ -311,7 +332,7 @@ std::vector<TurnRestriction> Extractor::ParseOSMData(ScriptingEnvironment &scrip
|
||||
<< " ways, and " << number_of_relations << " relations";
|
||||
|
||||
// take control over the turn lane map
|
||||
turn_lane_map = extractor_callbacks->moveOutLaneDescriptionMap();
|
||||
turn_lane_map.data = extractor_callbacks->moveOutLaneDescriptionMap().data;
|
||||
|
||||
extractor_callbacks.reset();
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ ExtractorCallbacks::ExtractorCallbacks(ExtractionContainers &extraction_containe
|
||||
{
|
||||
// we reserved 0, 1, 2, 3 for the empty case
|
||||
string_map[MapKey("", "", "", "")] = 0;
|
||||
lane_description_map[TurnLaneDescription()] = 0;
|
||||
lane_description_map.data[TurnLaneDescription()] = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -251,18 +251,7 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti
|
||||
return INVALID_LANE_DESCRIPTIONID;
|
||||
TurnLaneDescription lane_description = laneStringToDescription(std::move(lane_string));
|
||||
|
||||
const auto lane_description_itr = lane_description_map.find(lane_description);
|
||||
if (lane_description_itr == lane_description_map.end())
|
||||
{
|
||||
const LaneDescriptionID new_id =
|
||||
boost::numeric_cast<LaneDescriptionID>(lane_description_map.size());
|
||||
lane_description_map[lane_description] = new_id;
|
||||
return new_id;
|
||||
}
|
||||
else
|
||||
{
|
||||
return lane_description_itr->second;
|
||||
}
|
||||
return lane_description_map.ConcurrentFindOrAdd(lane_description);
|
||||
};
|
||||
|
||||
// Deduplicates street names, refs, destinations, pronunciation based on the string_map.
|
||||
|
||||
@@ -34,8 +34,8 @@ std::size_t getNumberOfTurns(const Intersection &intersection)
|
||||
} // namespace
|
||||
|
||||
TurnLaneHandler::TurnLaneHandler(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||
std::vector<std::uint32_t> &turn_lane_offsets,
|
||||
std::vector<TurnLaneType::Mask> &turn_lane_masks,
|
||||
const std::vector<std::uint32_t> &turn_lane_offsets,
|
||||
const std::vector<TurnLaneType::Mask> &turn_lane_masks,
|
||||
LaneDescriptionMap &lane_description_map,
|
||||
const TurnAnalysis &turn_analysis,
|
||||
util::guidance::LaneDataIdMap &id_map)
|
||||
@@ -781,19 +781,8 @@ Intersection TurnLaneHandler::handleSliproadTurn(Intersection intersection,
|
||||
}
|
||||
}
|
||||
|
||||
const auto combined_id = [&]() {
|
||||
auto itr = lane_description_map.find(combined_description);
|
||||
if (lane_description_map.find(combined_description) == lane_description_map.end())
|
||||
{
|
||||
const auto new_id = boost::numeric_cast<LaneDescriptionID>(lane_description_map.size());
|
||||
lane_description_map[combined_description] = new_id;
|
||||
return new_id;
|
||||
}
|
||||
else
|
||||
{
|
||||
return itr->second;
|
||||
}
|
||||
}();
|
||||
const auto combined_id = lane_description_map.ConcurrentFindOrAdd(combined_description);
|
||||
|
||||
return simpleMatchTuplesToTurns(std::move(intersection), lane_data, combined_id);
|
||||
}
|
||||
|
||||
|
||||
@@ -209,16 +209,7 @@ Intersection triviallyMatchLanesToTurns(Intersection intersection,
|
||||
util::guidance::LaneTupleIdPair key{{LaneID(data.to - data.from + 1), data.from},
|
||||
lane_string_id};
|
||||
|
||||
auto lane_data_id = boost::numeric_cast<LaneDataID>(lane_data_to_id.size());
|
||||
const auto it = lane_data_to_id.find(key);
|
||||
|
||||
if (it == lane_data_to_id.end())
|
||||
lane_data_to_id.insert({key, lane_data_id});
|
||||
else
|
||||
lane_data_id = it->second;
|
||||
|
||||
// set lane id instead after the switch:
|
||||
road.lane_data_id = lane_data_id;
|
||||
road.lane_data_id = lane_data_to_id.ConcurrentFindOrAdd(key);
|
||||
};
|
||||
|
||||
if (!lane_data.empty() && lane_data.front().tag == TurnLaneType::uturn)
|
||||
|
||||
@@ -108,7 +108,8 @@ int SourceContainer::LoadRasterSource(const std::string &path_string,
|
||||
boost::filesystem::path filepath(path_string);
|
||||
if (!boost::filesystem::exists(filepath))
|
||||
{
|
||||
throw util::exception(path_string + " does not exist" + SOURCE_REF);
|
||||
throw util::RuntimeError(
|
||||
path_string, ErrorCode::FileOpenError, SOURCE_REF, "File not found");
|
||||
}
|
||||
|
||||
RasterGrid rasterData{filepath, ncols, nrows};
|
||||
|
||||
@@ -249,7 +249,9 @@ void Sol2ScriptingEnvironment::InitContext(LuaScriptingContext &context)
|
||||
"max_turn_weight",
|
||||
sol::property(&ProfileProperties::GetMaxTurnWeight),
|
||||
"force_split_edges",
|
||||
&ProfileProperties::force_split_edges);
|
||||
&ProfileProperties::force_split_edges,
|
||||
"call_tagless_node_function",
|
||||
&ProfileProperties::call_tagless_node_function);
|
||||
|
||||
context.state.new_usertype<std::vector<std::string>>(
|
||||
"vector",
|
||||
@@ -427,15 +429,16 @@ void Sol2ScriptingEnvironment::InitContext(LuaScriptingContext &context)
|
||||
|
||||
context.state.script_file(file_name);
|
||||
|
||||
sol::function turn_function = context.state["turn_function"];
|
||||
sol::function node_function = context.state["node_function"];
|
||||
sol::function way_function = context.state["way_function"];
|
||||
sol::function segment_function = context.state["segment_function"];
|
||||
// cache references to functions for faster execution
|
||||
context.turn_function = context.state["turn_function"];
|
||||
context.node_function = context.state["node_function"];
|
||||
context.way_function = context.state["way_function"];
|
||||
context.segment_function = context.state["segment_function"];
|
||||
|
||||
context.has_turn_penalty_function = turn_function.valid();
|
||||
context.has_node_function = node_function.valid();
|
||||
context.has_way_function = way_function.valid();
|
||||
context.has_segment_function = segment_function.valid();
|
||||
context.has_turn_penalty_function = context.turn_function.valid();
|
||||
context.has_node_function = context.node_function.valid();
|
||||
context.has_way_function = context.way_function.valid();
|
||||
context.has_segment_function = context.segment_function.valid();
|
||||
|
||||
// Check profile API version
|
||||
auto maybe_version = context.state.get<sol::optional<int>>("api_version");
|
||||
@@ -486,59 +489,54 @@ LuaScriptingContext &Sol2ScriptingEnvironment::GetSol2Context()
|
||||
}
|
||||
|
||||
void Sol2ScriptingEnvironment::ProcessElements(
|
||||
const std::vector<osmium::memory::Buffer::const_iterator> &osm_elements,
|
||||
const osmium::memory::Buffer &buffer,
|
||||
const RestrictionParser &restriction_parser,
|
||||
tbb::concurrent_vector<std::pair<std::size_t, ExtractionNode>> &resulting_nodes,
|
||||
tbb::concurrent_vector<std::pair<std::size_t, ExtractionWay>> &resulting_ways,
|
||||
tbb::concurrent_vector<boost::optional<InputRestrictionContainer>> &resulting_restrictions)
|
||||
std::vector<std::pair<const osmium::Node &, ExtractionNode>> &resulting_nodes,
|
||||
std::vector<std::pair<const osmium::Way &, ExtractionWay>> &resulting_ways,
|
||||
std::vector<boost::optional<InputRestrictionContainer>> &resulting_restrictions)
|
||||
{
|
||||
// parse OSM entities in parallel, store in resulting vectors
|
||||
tbb::parallel_for(
|
||||
tbb::blocked_range<std::size_t>(0, osm_elements.size()),
|
||||
[&](const tbb::blocked_range<std::size_t> &range) {
|
||||
ExtractionNode result_node;
|
||||
ExtractionWay result_way;
|
||||
std::vector<InputRestrictionContainer> result_res;
|
||||
auto &local_context = this->GetSol2Context();
|
||||
ExtractionNode result_node;
|
||||
ExtractionWay result_way;
|
||||
std::vector<InputRestrictionContainer> result_res;
|
||||
auto &local_context = this->GetSol2Context();
|
||||
|
||||
for (auto x = range.begin(), end = range.end(); x != end; ++x)
|
||||
for (auto entity = buffer.cbegin(), end = buffer.cend(); entity != end; ++entity)
|
||||
{
|
||||
switch (entity->type())
|
||||
{
|
||||
case osmium::item_type::node:
|
||||
result_node.clear();
|
||||
if (local_context.has_node_function &&
|
||||
(!static_cast<const osmium::Node &>(*entity).tags().empty() ||
|
||||
local_context.properties.call_tagless_node_function))
|
||||
{
|
||||
const auto entity = osm_elements[x];
|
||||
|
||||
switch (entity->type())
|
||||
{
|
||||
case osmium::item_type::node:
|
||||
result_node.clear();
|
||||
if (local_context.has_node_function)
|
||||
{
|
||||
local_context.ProcessNode(static_cast<const osmium::Node &>(*entity),
|
||||
result_node);
|
||||
}
|
||||
resulting_nodes.push_back(std::make_pair(x, std::move(result_node)));
|
||||
break;
|
||||
case osmium::item_type::way:
|
||||
result_way.clear();
|
||||
if (local_context.has_way_function)
|
||||
{
|
||||
local_context.ProcessWay(static_cast<const osmium::Way &>(*entity),
|
||||
result_way);
|
||||
}
|
||||
resulting_ways.push_back(std::make_pair(x, std::move(result_way)));
|
||||
break;
|
||||
case osmium::item_type::relation:
|
||||
result_res.clear();
|
||||
result_res =
|
||||
restriction_parser.TryParse(static_cast<const osmium::Relation &>(*entity));
|
||||
for (const InputRestrictionContainer &r : result_res)
|
||||
{
|
||||
resulting_restrictions.push_back(r);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
local_context.ProcessNode(static_cast<const osmium::Node &>(*entity), result_node);
|
||||
}
|
||||
});
|
||||
resulting_nodes.push_back(std::pair<const osmium::Node &, ExtractionNode>(
|
||||
static_cast<const osmium::Node &>(*entity), std::move(result_node)));
|
||||
break;
|
||||
case osmium::item_type::way:
|
||||
result_way.clear();
|
||||
if (local_context.has_way_function)
|
||||
{
|
||||
local_context.ProcessWay(static_cast<const osmium::Way &>(*entity), result_way);
|
||||
}
|
||||
resulting_ways.push_back(std::pair<const osmium::Way &, ExtractionWay>(
|
||||
static_cast<const osmium::Way &>(*entity), std::move(result_way)));
|
||||
break;
|
||||
case osmium::item_type::relation:
|
||||
result_res.clear();
|
||||
result_res =
|
||||
restriction_parser.TryParse(static_cast<const osmium::Relation &>(*entity));
|
||||
for (const InputRestrictionContainer &r : result_res)
|
||||
{
|
||||
resulting_restrictions.push_back(r);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> Sol2ScriptingEnvironment::GetNameSuffixList()
|
||||
@@ -590,13 +588,12 @@ void Sol2ScriptingEnvironment::ProcessTurn(ExtractionTurn &turn)
|
||||
{
|
||||
auto &context = GetSol2Context();
|
||||
|
||||
sol::function turn_function = context.state["turn_function"];
|
||||
switch (context.api_version)
|
||||
{
|
||||
case 1:
|
||||
if (context.has_turn_penalty_function)
|
||||
{
|
||||
turn_function(turn);
|
||||
context.turn_function(turn);
|
||||
|
||||
// Turn weight falls back to the duration value in deciseconds
|
||||
// or uses the extracted unit-less weight value
|
||||
@@ -611,7 +608,7 @@ void Sol2ScriptingEnvironment::ProcessTurn(ExtractionTurn &turn)
|
||||
if (turn.turn_type != guidance::TurnType::NoTurn)
|
||||
{
|
||||
// Get turn duration and convert deci-seconds to seconds
|
||||
turn.duration = static_cast<double>(turn_function(turn.angle)) / 10.;
|
||||
turn.duration = static_cast<double>(context.turn_function(turn.angle)) / 10.;
|
||||
BOOST_ASSERT(turn.weight == 0);
|
||||
|
||||
// add U-turn penalty
|
||||
@@ -642,14 +639,14 @@ void Sol2ScriptingEnvironment::ProcessSegment(ExtractionSegment &segment)
|
||||
|
||||
if (context.has_segment_function)
|
||||
{
|
||||
sol::function segment_function = context.state["segment_function"];
|
||||
switch (context.api_version)
|
||||
{
|
||||
case 1:
|
||||
segment_function(segment);
|
||||
context.segment_function(segment);
|
||||
break;
|
||||
case 0:
|
||||
segment_function(segment.source, segment.target, segment.distance, segment.duration);
|
||||
context.segment_function(
|
||||
segment.source, segment.target, segment.distance, segment.duration);
|
||||
segment.weight = segment.duration; // back-compatibility fallback to duration
|
||||
break;
|
||||
}
|
||||
@@ -660,8 +657,6 @@ void LuaScriptingContext::ProcessNode(const osmium::Node &node, ExtractionNode &
|
||||
{
|
||||
BOOST_ASSERT(state.lua_state() != nullptr);
|
||||
|
||||
sol::function node_function = state["node_function"];
|
||||
|
||||
node_function(node, result);
|
||||
}
|
||||
|
||||
@@ -669,8 +664,6 @@ void LuaScriptingContext::ProcessWay(const osmium::Way &way, ExtractionWay &resu
|
||||
{
|
||||
BOOST_ASSERT(state.lua_state() != nullptr);
|
||||
|
||||
sol::function way_function = state["way_function"];
|
||||
|
||||
way_function(way, result);
|
||||
}
|
||||
}
|
||||
|
||||
+40
-2
@@ -22,10 +22,33 @@ OSRM::OSRM(engine::EngineConfig &config)
|
||||
using CoreCH = engine::routing_algorithms::corech::Algorithm;
|
||||
using MLD = engine::routing_algorithms::mld::Algorithm;
|
||||
|
||||
// First, check that necessary core data is available
|
||||
if (!config.use_shared_memory && !config.storage_config.IsValid())
|
||||
{
|
||||
throw util::exception("Required files are missing, cannot continue. Have all the "
|
||||
"pre-processing steps been run?");
|
||||
}
|
||||
else if (config.use_shared_memory)
|
||||
{
|
||||
storage::SharedMonitor<storage::SharedDataTimestamp> barrier;
|
||||
using mutex_type = typename decltype(barrier)::mutex_type;
|
||||
boost::interprocess::scoped_lock<mutex_type> current_region_lock(barrier.get_mutex());
|
||||
|
||||
auto mem = storage::makeSharedMemory(barrier.data().region);
|
||||
auto layout = reinterpret_cast<storage::DataLayout *>(mem->Ptr());
|
||||
if (layout->GetBlockSize(storage::DataLayout::NAME_CHAR_DATA) == 0)
|
||||
throw util::exception(
|
||||
"No name data loaded, cannot continue. Have you run osrm-datastore to load data?");
|
||||
}
|
||||
|
||||
// Now, check that the algorithm requested can be used with the data
|
||||
// that's available.
|
||||
|
||||
if (config.algorithm == EngineConfig::Algorithm::CoreCH ||
|
||||
config.algorithm == EngineConfig::Algorithm::CH)
|
||||
{
|
||||
bool corech_compatible = engine::Engine<CoreCH>::CheckCompability(config);
|
||||
bool ch_compatible = engine::Engine<CH>::CheckCompability(config);
|
||||
|
||||
// Activate CoreCH if we can because it is faster
|
||||
if (config.algorithm == EngineConfig::Algorithm::CH && corech_compatible)
|
||||
@@ -33,10 +56,25 @@ OSRM::OSRM(engine::EngineConfig &config)
|
||||
config.algorithm = EngineConfig::Algorithm::CoreCH;
|
||||
}
|
||||
|
||||
// throw error if dataset is not usable with CoreCH
|
||||
// throw error if dataset is not usable with CoreCH or CH
|
||||
if (config.algorithm == EngineConfig::Algorithm::CoreCH && !corech_compatible)
|
||||
{
|
||||
throw util::exception("Dataset is not compatible with CoreCH.");
|
||||
throw util::RuntimeError("Dataset is not compatible with CoreCH.",
|
||||
ErrorCode::IncompatibleDataset,
|
||||
SOURCE_REF);
|
||||
}
|
||||
else if (config.algorithm == EngineConfig::Algorithm::CH && !ch_compatible)
|
||||
{
|
||||
throw util::exception("Dataset is not compatible with CH");
|
||||
}
|
||||
}
|
||||
else if (config.algorithm == EngineConfig::Algorithm::MLD)
|
||||
{
|
||||
bool mld_compatible = engine::Engine<MLD>::CheckCompability(config);
|
||||
// throw error if dataset is not usable with MLD
|
||||
if (!mld_compatible)
|
||||
{
|
||||
throw util::exception("Dataset is not compatible with MLD.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,9 +14,9 @@ bool CheckFileList(const std::vector<boost::filesystem::path> &files)
|
||||
bool success = true;
|
||||
for (auto &path : files)
|
||||
{
|
||||
if (!boost::filesystem::is_regular_file(path))
|
||||
if (!boost::filesystem::exists(path))
|
||||
{
|
||||
util::Log(logWARNING) << "Missing/Broken File: " << path.string();
|
||||
util::Log(logERROR) << "Missing File: " << path.string();
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
@@ -62,14 +62,6 @@ bool StorageConfig::IsValid() const
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: add algorithm checks
|
||||
|
||||
// CH files
|
||||
CheckFileList({hsgr_data_path, core_data_path});
|
||||
|
||||
// MLD files
|
||||
CheckFileList({mld_partition_path, mld_storage_path, mld_graph_path});
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
+11
-1
@@ -1,6 +1,7 @@
|
||||
#include "storage/io.hpp"
|
||||
#include "osrm/contractor.hpp"
|
||||
#include "osrm/contractor_config.hpp"
|
||||
#include "osrm/exception.hpp"
|
||||
#include "util/log.hpp"
|
||||
#include "util/timezones.hpp"
|
||||
#include "util/version.hpp"
|
||||
@@ -187,9 +188,18 @@ int main(int argc, char *argv[]) try
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
catch (const osrm::RuntimeError &e)
|
||||
{
|
||||
util::DumpSTXXLStats();
|
||||
util::DumpMemoryStats();
|
||||
util::Log(logERROR) << e.what();
|
||||
return e.GetCode();
|
||||
}
|
||||
catch (const std::bad_alloc &e)
|
||||
{
|
||||
util::Log(logERROR) << "[exception] " << e.what();
|
||||
util::DumpSTXXLStats();
|
||||
util::DumpMemoryStats();
|
||||
util::Log(logERROR) << e.what();
|
||||
util::Log(logERROR) << "Please provide more memory or consider using a larger swapfile";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "customizer/customizer.hpp"
|
||||
|
||||
#include "osrm/exception.hpp"
|
||||
#include "util/log.hpp"
|
||||
#include "util/meminfo.hpp"
|
||||
#include "util/version.hpp"
|
||||
@@ -167,8 +168,15 @@ int main(int argc, char *argv[]) try
|
||||
|
||||
return exitcode;
|
||||
}
|
||||
catch (const osrm::RuntimeError &e)
|
||||
{
|
||||
util::DumpMemoryStats();
|
||||
util::Log(logERROR) << e.what();
|
||||
return e.GetCode();
|
||||
}
|
||||
catch (const std::bad_alloc &e)
|
||||
{
|
||||
util::DumpMemoryStats();
|
||||
util::Log(logERROR) << "[exception] " << e.what();
|
||||
util::Log(logERROR) << "Please provide more memory or consider using a larger swapfile";
|
||||
return EXIT_FAILURE;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "osrm/exception.hpp"
|
||||
#include "osrm/extractor.hpp"
|
||||
#include "osrm/extractor_config.hpp"
|
||||
#include "util/log.hpp"
|
||||
@@ -168,8 +169,24 @@ int main(int argc, char *argv[]) try
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
catch (const osrm::RuntimeError &e)
|
||||
{
|
||||
util::DumpSTXXLStats();
|
||||
util::DumpMemoryStats();
|
||||
util::Log(logERROR) << e.what();
|
||||
return e.GetCode();
|
||||
}
|
||||
catch (const std::system_error &e)
|
||||
{
|
||||
util::DumpSTXXLStats();
|
||||
util::DumpMemoryStats();
|
||||
util::Log(logERROR) << e.what();
|
||||
return e.code().value();
|
||||
}
|
||||
catch (const std::bad_alloc &e)
|
||||
{
|
||||
util::DumpSTXXLStats();
|
||||
util::DumpMemoryStats();
|
||||
util::Log(logERROR) << "[exception] " << e.what();
|
||||
util::Log(logERROR) << "Please provide more memory or consider using a larger swapfile";
|
||||
return EXIT_FAILURE;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "partition/partition_config.hpp"
|
||||
#include "partition/partitioner.hpp"
|
||||
|
||||
#include "osrm/exception.hpp"
|
||||
#include "util/log.hpp"
|
||||
#include "util/meminfo.hpp"
|
||||
#include "util/timing_util.hpp"
|
||||
@@ -238,8 +239,16 @@ int main(int argc, char *argv[]) try
|
||||
|
||||
return exitcode;
|
||||
}
|
||||
catch (const osrm::RuntimeError &e)
|
||||
{
|
||||
util::DumpMemoryStats();
|
||||
util::Log(logERROR) << e.what();
|
||||
return EXIT_FAILURE;
|
||||
return e.GetCode();
|
||||
}
|
||||
catch (const std::bad_alloc &e)
|
||||
{
|
||||
util::DumpMemoryStats();
|
||||
util::Log(logERROR) << "[exception] " << e.what();
|
||||
util::Log(logERROR) << "Please provide more memory or consider using a larger swapfile";
|
||||
return EXIT_FAILURE;
|
||||
|
||||
+15
-23
@@ -1,9 +1,11 @@
|
||||
#include "server/server.hpp"
|
||||
#include "util/exception.hpp"
|
||||
#include "util/exception_utils.hpp"
|
||||
#include "util/log.hpp"
|
||||
#include "util/meminfo.hpp"
|
||||
#include "util/version.hpp"
|
||||
|
||||
#include "osrm/engine_config.hpp"
|
||||
#include "osrm/exception.hpp"
|
||||
#include "osrm/osrm.hpp"
|
||||
#include "osrm/storage_config.hpp"
|
||||
|
||||
@@ -57,7 +59,7 @@ EngineConfig::Algorithm stringToAlgorithm(const std::string &algorithm)
|
||||
return EngineConfig::Algorithm::CoreCH;
|
||||
if (algorithm == "MLD")
|
||||
return EngineConfig::Algorithm::MLD;
|
||||
throw util::exception("Invalid algorithm name: " + algorithm);
|
||||
throw util::RuntimeError(algorithm, ErrorCode::UnknownAlgorithm, SOURCE_REF);
|
||||
}
|
||||
|
||||
// generate boost::program_options object for the routing part
|
||||
@@ -221,33 +223,17 @@ int main(int argc, const char *argv[]) try
|
||||
{
|
||||
config.storage_config = storage::StorageConfig(base_path);
|
||||
}
|
||||
if (!config.use_shared_memory && !config.storage_config.IsValid())
|
||||
{
|
||||
util::Log(logERROR) << "Required files are missing, cannot continue";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (!config.IsValid())
|
||||
{
|
||||
if (base_path.empty() != config.use_shared_memory)
|
||||
{
|
||||
util::Log(logWARNING) << "Path settings and shared memory conflicts.";
|
||||
}
|
||||
else
|
||||
{
|
||||
auto required_files = {config.storage_config.ram_index_path,
|
||||
config.storage_config.file_index_path,
|
||||
config.storage_config.hsgr_data_path,
|
||||
config.storage_config.node_based_nodes_data_path,
|
||||
config.storage_config.edge_based_nodes_data_path,
|
||||
config.storage_config.edges_data_path,
|
||||
config.storage_config.core_data_path,
|
||||
config.storage_config.geometries_path,
|
||||
config.storage_config.datasource_indexes_path,
|
||||
config.storage_config.names_data_path,
|
||||
config.storage_config.properties_path};
|
||||
for (auto file : required_files)
|
||||
{
|
||||
if (!boost::filesystem::is_regular_file(file))
|
||||
{
|
||||
util::Log(logWARNING) << file << " is not found";
|
||||
}
|
||||
}
|
||||
}
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
config.algorithm = stringToAlgorithm(algorithm);
|
||||
@@ -331,8 +317,14 @@ int main(int argc, const char *argv[]) try
|
||||
routing_server.reset();
|
||||
util::Log() << "shutdown completed";
|
||||
}
|
||||
catch (const osrm::RuntimeError &e)
|
||||
{
|
||||
util::Log(logERROR) << e.what();
|
||||
return e.GetCode();
|
||||
}
|
||||
catch (const std::bad_alloc &e)
|
||||
{
|
||||
util::DumpMemoryStats();
|
||||
util::Log(logWARNING) << "[exception] " << e.what();
|
||||
util::Log(logWARNING) << "Please provide more memory or consider using a larger swapfile";
|
||||
return EXIT_FAILURE;
|
||||
|
||||
+8
-1
@@ -1,8 +1,9 @@
|
||||
#include "storage/shared_memory.hpp"
|
||||
#include "storage/shared_monitor.hpp"
|
||||
#include "storage/storage.hpp"
|
||||
#include "util/exception.hpp"
|
||||
#include "osrm/exception.hpp"
|
||||
#include "util/log.hpp"
|
||||
#include "util/meminfo.hpp"
|
||||
#include "util/typedefs.hpp"
|
||||
#include "util/version.hpp"
|
||||
|
||||
@@ -172,8 +173,14 @@ int main(const int argc, const char *argv[]) try
|
||||
|
||||
return storage.Run(max_wait);
|
||||
}
|
||||
catch (const osrm::RuntimeError &e)
|
||||
{
|
||||
util::Log(logERROR) << e.what();
|
||||
return e.GetCode();
|
||||
}
|
||||
catch (const std::bad_alloc &e)
|
||||
{
|
||||
util::DumpMemoryStats();
|
||||
util::Log(logERROR) << "[exception] " << e.what();
|
||||
util::Log(logERROR) << "Please provide more memory or disable locking the virtual "
|
||||
"address space (note: this makes OSRM swap, i.e. slow)";
|
||||
|
||||
@@ -17,5 +17,6 @@ namespace util
|
||||
{
|
||||
|
||||
void exception::anchor() const {}
|
||||
void RuntimeError::anchor() const {}
|
||||
}
|
||||
}
|
||||
|
||||
+19
-3
@@ -23,9 +23,12 @@ test('constructor: does not accept more than one parameter', function(assert) {
|
||||
});
|
||||
|
||||
test('constructor: throws if necessary files do not exist', function(assert) {
|
||||
assert.plan(1);
|
||||
assert.throws(function() { new OSRM("missing.osrm"); },
|
||||
/Error opening missing.osrm.names/);
|
||||
assert.plan(2);
|
||||
assert.throws(function() { new OSRM('missing.osrm'); },
|
||||
/Required files are missing, cannot continue/);
|
||||
|
||||
assert.throws(function() { new OSRM({path: 'missing.osrm', algorithm: 'MLD'}); },
|
||||
/Required files are missing, cannot continue/);
|
||||
});
|
||||
|
||||
test('constructor: takes a shared memory argument', function(assert) {
|
||||
@@ -82,6 +85,19 @@ test('constructor: loads CoreCH if given as algorithm', function(assert) {
|
||||
assert.ok(osrm);
|
||||
});
|
||||
|
||||
test('constructor: autoswitches to CoreCH for a CH dataset if capable', function(assert) {
|
||||
assert.plan(1);
|
||||
var osrm = new OSRM({algorithm: 'CH', path: monaco_corech_path});
|
||||
assert.ok(osrm);
|
||||
});
|
||||
|
||||
test('constructor: throws if data doesn\'t match algorithm', function(assert) {
|
||||
assert.plan(3);
|
||||
assert.throws(function() { new OSRM({algorithm: 'CoreCH', path: monaco_mld_path}); });
|
||||
assert.throws(function() { new OSRM({algorithm: 'CoreCH', path: monaco_path}); });
|
||||
assert.throws(function() { new OSRM({algorithm: 'MLD', path: monaco_path}); });
|
||||
});
|
||||
|
||||
require('./route.js');
|
||||
require('./trip.js');
|
||||
require('./match.js');
|
||||
|
||||
+28
-1
@@ -133,7 +133,9 @@ test('match: match in Monaco with all options', function(assert) {
|
||||
steps: true,
|
||||
annotations: true,
|
||||
overview: 'false',
|
||||
geometries: 'geojson'
|
||||
geometries: 'geojson',
|
||||
gaps: 'split',
|
||||
tidy: false
|
||||
};
|
||||
osrm.match(options, function(err, response) {
|
||||
assert.ifError(err);
|
||||
@@ -196,3 +198,28 @@ test('match: throws on invalid timestamps param', function(assert) {
|
||||
assert.throws(function() { osrm.match(options, function(err, response) {}) },
|
||||
/Timestamp array must have the same size as the coordinates array/);
|
||||
});
|
||||
|
||||
test('match: throws on invalid gaps param', function(assert) {
|
||||
assert.plan(2);
|
||||
var osrm = new OSRM(data_path);
|
||||
var options = {
|
||||
coordinates: three_test_coordinates,
|
||||
gaps: ['invalid gaps param']
|
||||
};
|
||||
assert.throws(function() { osrm.match(options, function(err, response) {}) },
|
||||
/Gaps must be a string: \[split, ignore\]/);
|
||||
options.gaps = 'invalid gaps param';
|
||||
assert.throws(function() { osrm.match(options, function(err, response) {}) },
|
||||
/'gaps' param must be one of \[split, ignore\]/);
|
||||
});
|
||||
|
||||
test('match: throws on invalid tidy param', function(assert) {
|
||||
assert.plan(1);
|
||||
var osrm = new OSRM(data_path);
|
||||
var options = {
|
||||
coordinates: three_test_coordinates,
|
||||
tidy: 'invalid tidy param'
|
||||
};
|
||||
assert.throws(function() { osrm.match(options, function(err, response) {}) },
|
||||
/tidy must be of type Boolean/);
|
||||
});
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
#include "engine/polyline_compressor.hpp"
|
||||
#include "util/coordinate.hpp"
|
||||
|
||||
#include <boost/test/test_case_template.hpp>
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(polyline_compression)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(polyline5_test_case)
|
||||
{
|
||||
using namespace osrm::engine;
|
||||
using namespace osrm::util;
|
||||
|
||||
const std::vector<Coordinate> coords({{FixedLongitude{-73990171}, FixedLatitude{40714701}},
|
||||
{FixedLongitude{-73991801}, FixedLatitude{40717571}},
|
||||
{FixedLongitude{-73985751}, FixedLatitude{40715651}}});
|
||||
|
||||
const std::vector<Coordinate> coords_truncated(
|
||||
{{FixedLongitude{-73990170}, FixedLatitude{40714700}},
|
||||
{FixedLongitude{-73991800}, FixedLatitude{40717570}},
|
||||
{FixedLongitude{-73985750}, FixedLatitude{40715650}}});
|
||||
|
||||
BOOST_CHECK_EQUAL(encodePolyline(coords.begin(), coords.end()), "{aowFperbM}PdI~Jyd@");
|
||||
BOOST_CHECK(std::equal(coords_truncated.begin(),
|
||||
coords_truncated.end(),
|
||||
decodePolyline(encodePolyline(coords.begin(), coords.end())).begin()));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(polyline6_test_case)
|
||||
{
|
||||
using namespace osrm::engine;
|
||||
using namespace osrm::util;
|
||||
|
||||
const std::vector<Coordinate> coords({{FixedLongitude{-73990171}, FixedLatitude{40714701}},
|
||||
{FixedLongitude{-73991801}, FixedLatitude{40717571}},
|
||||
{FixedLongitude{-73985751}, FixedLatitude{40715651}}});
|
||||
|
||||
BOOST_CHECK_EQUAL(encodePolyline<1000000>(coords.begin(), coords.end()),
|
||||
"y{_tlAt`_clCkrDzdB~vBcyJ");
|
||||
BOOST_CHECK(std::equal(
|
||||
coords.begin(),
|
||||
coords.end(),
|
||||
decodePolyline<1000000>(encodePolyline<1000000>(coords.begin(), coords.end())).begin()));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
@@ -0,0 +1,34 @@
|
||||
#include <boost/test/test_case_template.hpp>
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include "fixture.hpp"
|
||||
|
||||
#include "osrm/exception.hpp"
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(table)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_incompatible_with_mld)
|
||||
{
|
||||
// Can't use the MLD algorithm with CH data
|
||||
BOOST_CHECK_THROW(
|
||||
getOSRM(OSRM_TEST_DATA_DIR "/ch/monaco.osrm", osrm::EngineConfig::Algorithm::MLD),
|
||||
osrm::exception);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_incompatible_with_corech)
|
||||
{
|
||||
// Note - CH-only data can't be used with the CoreCH algorithm
|
||||
BOOST_CHECK_THROW(
|
||||
getOSRM(OSRM_TEST_DATA_DIR "/ch/monaco.osrm", osrm::EngineConfig::Algorithm::CoreCH),
|
||||
osrm::exception);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_incompatible_with_ch)
|
||||
{
|
||||
// Can't use the CH algorithm with MLD data
|
||||
BOOST_CHECK_THROW(
|
||||
getOSRM(OSRM_TEST_DATA_DIR "/mld/monaco.osrm", osrm::EngineConfig::Algorithm::CH),
|
||||
osrm::exception);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
@@ -9,11 +9,14 @@
|
||||
// I couldn't get Boost.UnitTest to provide a test suite level fixture with custom
|
||||
// arguments per test suite (osrm base path from argv), so this has to suffice.
|
||||
|
||||
inline osrm::OSRM getOSRM(const std::string &base_path)
|
||||
inline osrm::OSRM
|
||||
getOSRM(const std::string &base_path,
|
||||
osrm::EngineConfig::Algorithm algorithm = osrm::EngineConfig::Algorithm::CH)
|
||||
{
|
||||
osrm::EngineConfig config;
|
||||
config.storage_config = {base_path};
|
||||
config.use_shared_memory = false;
|
||||
config.algorithm = algorithm;
|
||||
|
||||
return osrm::OSRM{config};
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include "osrm/coordinate.hpp"
|
||||
#include "osrm/engine_config.hpp"
|
||||
#include "osrm/exception.hpp"
|
||||
#include "osrm/json_container.hpp"
|
||||
#include "osrm/json_container.hpp"
|
||||
#include "osrm/osrm.hpp"
|
||||
|
||||
+115
-24
@@ -20,12 +20,10 @@
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(tile)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_tile)
|
||||
template <typename algorithm> void test_tile(algorithm &osrm)
|
||||
{
|
||||
using namespace osrm;
|
||||
|
||||
auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/ch/monaco.osrm");
|
||||
|
||||
// This tile should contain most of monaco
|
||||
TileParameters params{17059, 11948, 15};
|
||||
|
||||
@@ -55,7 +53,7 @@ BOOST_AUTO_TEST_CASE(test_tile)
|
||||
auto property_iter_pair = feature_message.get_packed_uint32();
|
||||
auto value_begin = property_iter_pair.begin();
|
||||
auto value_end = property_iter_pair.end();
|
||||
BOOST_CHECK_EQUAL(std::distance(value_begin, value_end), 12);
|
||||
BOOST_CHECK_EQUAL(std::distance(value_begin, value_end), 14);
|
||||
auto iter = value_begin;
|
||||
BOOST_CHECK_EQUAL(*iter++, 0); // speed key
|
||||
BOOST_CHECK_LT(*iter++, 128); // speed value
|
||||
@@ -73,6 +71,9 @@ BOOST_AUTO_TEST_CASE(test_tile)
|
||||
// name
|
||||
BOOST_CHECK_EQUAL(*iter++, 5);
|
||||
BOOST_CHECK_GT(*iter++, 130);
|
||||
// rate
|
||||
BOOST_CHECK_EQUAL(*iter++, 6);
|
||||
BOOST_CHECK_GT(*iter++, 130);
|
||||
BOOST_CHECK(iter == value_end);
|
||||
// geometry
|
||||
feature_message.next();
|
||||
@@ -140,7 +141,7 @@ BOOST_AUTO_TEST_CASE(test_tile)
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_CHECK_EQUAL(number_of_speed_keys, 6);
|
||||
BOOST_CHECK_EQUAL(number_of_speed_keys, 7);
|
||||
BOOST_CHECK_GT(number_of_speed_values, 128); // speed value resolution
|
||||
|
||||
tile_message.next();
|
||||
@@ -159,13 +160,15 @@ BOOST_AUTO_TEST_CASE(test_tile)
|
||||
BOOST_CHECK_EQUAL(feature_message.tag(), util::vector_tile::FEATURE_ATTRIBUTES_TAG);
|
||||
// properties
|
||||
auto feature_iter_pair = feature_message.get_packed_uint32();
|
||||
BOOST_CHECK_EQUAL(std::distance(feature_iter_pair.begin(), feature_iter_pair.end()), 6);
|
||||
BOOST_CHECK_EQUAL(std::distance(feature_iter_pair.begin(), feature_iter_pair.end()), 8);
|
||||
auto iter = feature_iter_pair.begin();
|
||||
BOOST_CHECK_EQUAL(*iter++, 0); // bearing_in key
|
||||
*iter++;
|
||||
BOOST_CHECK_EQUAL(*iter++, 1); // turn_angle key
|
||||
*iter++;
|
||||
BOOST_CHECK_EQUAL(*iter++, 2); // cost key
|
||||
BOOST_CHECK_EQUAL(*iter++, 2); // turn cost (duration) key
|
||||
*iter++; // skip value check, can be valud uint32
|
||||
BOOST_CHECK_EQUAL(*iter++, 3); // turn weight key
|
||||
*iter++; // skip value check, can be valud uint32
|
||||
BOOST_CHECK(iter == feature_iter_pair.end());
|
||||
// geometry
|
||||
@@ -207,15 +210,36 @@ BOOST_AUTO_TEST_CASE(test_tile)
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_CHECK_EQUAL(number_of_turn_keys, 3);
|
||||
BOOST_CHECK_EQUAL(number_of_turn_keys, 4);
|
||||
BOOST_CHECK(number_of_turns_found > 700);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_tile_turns)
|
||||
BOOST_AUTO_TEST_CASE(test_tile_ch)
|
||||
{
|
||||
using namespace osrm;
|
||||
auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/ch/monaco.osrm", osrm::EngineConfig::Algorithm::CH);
|
||||
test_tile(osrm);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_tile_corech)
|
||||
{
|
||||
using namespace osrm;
|
||||
auto osrm =
|
||||
getOSRM(OSRM_TEST_DATA_DIR "/corech/monaco.osrm", osrm::EngineConfig::Algorithm::CoreCH);
|
||||
test_tile(osrm);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_tile_mld)
|
||||
{
|
||||
using namespace osrm;
|
||||
auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/mld/monaco.osrm", osrm::EngineConfig::Algorithm::MLD);
|
||||
test_tile(osrm);
|
||||
}
|
||||
|
||||
template <typename algorithm> void test_tile_turns(algorithm &osrm)
|
||||
{
|
||||
using namespace osrm;
|
||||
|
||||
auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/ch/monaco.osrm");
|
||||
// Small tile where we can test all the values
|
||||
TileParameters params{272953, 191177, 19};
|
||||
|
||||
@@ -236,7 +260,8 @@ BOOST_AUTO_TEST_CASE(test_tile_turns)
|
||||
|
||||
std::vector<int> found_bearing_in_indexes;
|
||||
std::vector<int> found_turn_angles_indexes;
|
||||
std::vector<int> found_penalties_indexes;
|
||||
std::vector<int> found_time_penalties_indexes;
|
||||
std::vector<int> found_weight_penalties_indexes;
|
||||
|
||||
const auto check_turn_feature = [&](protozero::pbf_reader feature_message) {
|
||||
feature_message.next(); // advance parser to first entry
|
||||
@@ -251,14 +276,16 @@ BOOST_AUTO_TEST_CASE(test_tile_turns)
|
||||
BOOST_CHECK_EQUAL(feature_message.tag(), util::vector_tile::FEATURE_ATTRIBUTES_TAG);
|
||||
// properties
|
||||
auto feature_iter_pair = feature_message.get_packed_uint32();
|
||||
BOOST_CHECK_EQUAL(std::distance(feature_iter_pair.begin(), feature_iter_pair.end()), 6);
|
||||
BOOST_CHECK_EQUAL(std::distance(feature_iter_pair.begin(), feature_iter_pair.end()), 8);
|
||||
auto iter = feature_iter_pair.begin();
|
||||
BOOST_CHECK_EQUAL(*iter++, 0); // bearing_in key
|
||||
found_bearing_in_indexes.push_back(*iter++);
|
||||
BOOST_CHECK_EQUAL(*iter++, 1); // turn_angle key
|
||||
found_turn_angles_indexes.push_back(*iter++);
|
||||
BOOST_CHECK_EQUAL(*iter++, 2); // cost key
|
||||
found_penalties_indexes.push_back(*iter++); // skip value check, can be valud uint32
|
||||
BOOST_CHECK_EQUAL(*iter++, 2); // "cost" key (actually duration)
|
||||
found_time_penalties_indexes.push_back(*iter++); // skip value check, can be valud uint32
|
||||
BOOST_CHECK_EQUAL(*iter++, 3); // "weight" key
|
||||
found_weight_penalties_indexes.push_back(*iter++); // skip value check, can be valud uint32
|
||||
BOOST_CHECK(iter == feature_iter_pair.end());
|
||||
// geometry
|
||||
feature_message.next();
|
||||
@@ -323,16 +350,28 @@ BOOST_AUTO_TEST_CASE(test_tile_turns)
|
||||
}
|
||||
|
||||
// Verify that we got the expected turn penalties
|
||||
std::vector<float> actual_turn_penalties;
|
||||
for (const auto &i : found_penalties_indexes)
|
||||
std::vector<float> actual_time_turn_penalties;
|
||||
for (const auto &i : found_time_penalties_indexes)
|
||||
{
|
||||
BOOST_CHECK(float_vals.count(i) == 1);
|
||||
actual_turn_penalties.push_back(float_vals[i]);
|
||||
actual_time_turn_penalties.push_back(float_vals[i]);
|
||||
}
|
||||
std::sort(actual_turn_penalties.begin(), actual_turn_penalties.end());
|
||||
const std::vector<float> expected_turn_penalties = {
|
||||
std::sort(actual_time_turn_penalties.begin(), actual_time_turn_penalties.end());
|
||||
const std::vector<float> expected_time_turn_penalties = {
|
||||
0, 0, 0, 0, 0, 0, .1f, .1f, .3f, .4f, 1.2f, 1.9f, 5.3f, 5.5f, 5.8f, 7.1f, 7.2f, 7.2f};
|
||||
CHECK_EQUAL_RANGE(actual_turn_penalties, expected_turn_penalties);
|
||||
CHECK_EQUAL_RANGE(actual_time_turn_penalties, expected_time_turn_penalties);
|
||||
|
||||
// Verify that we got the expected turn penalties
|
||||
std::vector<float> actual_weight_turn_penalties;
|
||||
for (const auto &i : found_weight_penalties_indexes)
|
||||
{
|
||||
BOOST_CHECK(float_vals.count(i) == 1);
|
||||
actual_weight_turn_penalties.push_back(float_vals[i]);
|
||||
}
|
||||
std::sort(actual_weight_turn_penalties.begin(), actual_weight_turn_penalties.end());
|
||||
const std::vector<float> expected_weight_turn_penalties = {
|
||||
0, 0, 0, 0, 0, 0, .1f, .1f, .3f, .4f, 1.2f, 1.9f, 5.3f, 5.5f, 5.8f, 7.1f, 7.2f, 7.2f};
|
||||
CHECK_EQUAL_RANGE(actual_weight_turn_penalties, expected_weight_turn_penalties);
|
||||
|
||||
// Verify the expected turn angles
|
||||
std::vector<std::int64_t> actual_turn_angles;
|
||||
@@ -359,11 +398,34 @@ BOOST_AUTO_TEST_CASE(test_tile_turns)
|
||||
CHECK_EQUAL_RANGE(actual_turn_bearings, expected_turn_bearings);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_tile_speeds)
|
||||
BOOST_AUTO_TEST_CASE(test_tile_turns_ch)
|
||||
{
|
||||
using namespace osrm;
|
||||
auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/ch/monaco.osrm", osrm::EngineConfig::Algorithm::CH);
|
||||
|
||||
auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/ch/monaco.osrm");
|
||||
test_tile_turns(osrm);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_tile_turns_corech)
|
||||
{
|
||||
using namespace osrm;
|
||||
auto osrm =
|
||||
getOSRM(OSRM_TEST_DATA_DIR "/corech/monaco.osrm", osrm::EngineConfig::Algorithm::CoreCH);
|
||||
|
||||
test_tile_turns(osrm);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_tile_turns_mld)
|
||||
{
|
||||
using namespace osrm;
|
||||
auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/mld/monaco.osrm", osrm::EngineConfig::Algorithm::MLD);
|
||||
|
||||
test_tile_turns(osrm);
|
||||
}
|
||||
|
||||
template <typename algorithm> void test_tile_speeds(algorithm &osrm)
|
||||
{
|
||||
using namespace osrm;
|
||||
|
||||
// Small tile so we can test all the values
|
||||
// TileParameters params{272953, 191177, 19};
|
||||
@@ -383,8 +445,10 @@ BOOST_AUTO_TEST_CASE(test_tile_speeds)
|
||||
std::vector<int> found_speed_indexes;
|
||||
std::vector<int> found_component_indexes;
|
||||
std::vector<int> found_datasource_indexes;
|
||||
std::vector<int> found_weight_indexes;
|
||||
std::vector<int> found_duration_indexes;
|
||||
std::vector<int> found_name_indexes;
|
||||
std::vector<int> found_rate_indexes;
|
||||
|
||||
const auto check_feature = [&](protozero::pbf_reader feature_message) {
|
||||
feature_message.next(); // advance parser to first entry
|
||||
@@ -401,7 +465,7 @@ BOOST_AUTO_TEST_CASE(test_tile_speeds)
|
||||
auto property_iter_pair = feature_message.get_packed_uint32();
|
||||
auto value_begin = property_iter_pair.begin();
|
||||
auto value_end = property_iter_pair.end();
|
||||
BOOST_CHECK_EQUAL(std::distance(value_begin, value_end), 12);
|
||||
BOOST_CHECK_EQUAL(std::distance(value_begin, value_end), 14);
|
||||
auto iter = value_begin;
|
||||
BOOST_CHECK_EQUAL(*iter++, 0); // speed key
|
||||
found_speed_indexes.push_back(*iter++);
|
||||
@@ -411,12 +475,14 @@ BOOST_AUTO_TEST_CASE(test_tile_speeds)
|
||||
BOOST_CHECK_EQUAL(*iter++, 2); // data source key
|
||||
found_datasource_indexes.push_back(*iter++);
|
||||
BOOST_CHECK_EQUAL(*iter++, 3); // weight key
|
||||
found_duration_indexes.push_back(*iter++);
|
||||
found_weight_indexes.push_back(*iter++);
|
||||
BOOST_CHECK_EQUAL(*iter++, 4); // duration key
|
||||
found_duration_indexes.push_back(*iter++);
|
||||
// name
|
||||
BOOST_CHECK_EQUAL(*iter++, 5);
|
||||
found_name_indexes.push_back(*iter++);
|
||||
BOOST_CHECK_EQUAL(*iter++, 6);
|
||||
found_rate_indexes.push_back(*iter++);
|
||||
BOOST_CHECK(iter == value_end);
|
||||
// geometry
|
||||
feature_message.next();
|
||||
@@ -522,4 +588,29 @@ BOOST_AUTO_TEST_CASE(test_tile_speeds)
|
||||
BOOST_CHECK(actual_names == expected_names);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_tile_speeds_ch)
|
||||
{
|
||||
using namespace osrm;
|
||||
|
||||
auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/ch/monaco.osrm", osrm::EngineConfig::Algorithm::CH);
|
||||
test_tile_speeds(osrm);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_tile_speeds_corech)
|
||||
{
|
||||
using namespace osrm;
|
||||
|
||||
auto osrm =
|
||||
getOSRM(OSRM_TEST_DATA_DIR "/corech/monaco.osrm", osrm::EngineConfig::Algorithm::CoreCH);
|
||||
test_tile_speeds(osrm);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_tile_speeds_mld)
|
||||
{
|
||||
using namespace osrm;
|
||||
|
||||
auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/mld/monaco.osrm", osrm::EngineConfig::Algorithm::MLD);
|
||||
test_tile_speeds(osrm);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
||||
@@ -93,9 +93,9 @@ BOOST_AUTO_TEST_CASE(valid_urls)
|
||||
CHECK_EQUAL_RANGE(reference_5.query, result_5->query);
|
||||
BOOST_CHECK_EQUAL(reference_5.prefix_length, result_5->prefix_length);
|
||||
|
||||
// tile
|
||||
api::ParsedURL reference_6{"route", 1, "profile", "tile(1,2,3).mvt", 18UL};
|
||||
auto result_6 = api::parseURL("/route/v1/profile/tile(1,2,3).mvt");
|
||||
// polyline6
|
||||
api::ParsedURL reference_6{"route", 1, "profile", "polyline6(_ibE?_seK_seK_seK_seK)?", 18UL};
|
||||
auto result_6 = api::parseURL("/route/v1/profile/polyline6(_ibE?_seK_seK_seK_seK)?");
|
||||
BOOST_CHECK(result_6);
|
||||
BOOST_CHECK_EQUAL(reference_6.service, result_6->service);
|
||||
BOOST_CHECK_EQUAL(reference_6.version, result_6->version);
|
||||
@@ -103,17 +103,27 @@ BOOST_AUTO_TEST_CASE(valid_urls)
|
||||
CHECK_EQUAL_RANGE(reference_6.query, result_6->query);
|
||||
BOOST_CHECK_EQUAL(reference_6.prefix_length, result_6->prefix_length);
|
||||
|
||||
// polyline with %HEX
|
||||
api::ParsedURL reference_7{
|
||||
"match", 1, "car", "polyline(}jmwFz~ubMyCa@`@yDJqE)?你好&\n \"%~", 14UL};
|
||||
auto result_7 = api::parseURL(
|
||||
"/match/v1/car/polyline(%7DjmwFz~ubMyCa@%60@yDJqE)?%e4%bd%a0%e5%a5%bd&%0A%20%22%25%7e");
|
||||
// tile
|
||||
api::ParsedURL reference_7{"route", 1, "profile", "tile(1,2,3).mvt", 18UL};
|
||||
auto result_7 = api::parseURL("/route/v1/profile/tile(1,2,3).mvt");
|
||||
BOOST_CHECK(result_7);
|
||||
BOOST_CHECK_EQUAL(reference_7.service, result_7->service);
|
||||
BOOST_CHECK_EQUAL(reference_7.version, result_7->version);
|
||||
BOOST_CHECK_EQUAL(reference_7.profile, result_7->profile);
|
||||
CHECK_EQUAL_RANGE(reference_7.query, result_7->query);
|
||||
BOOST_CHECK_EQUAL(reference_7.prefix_length, result_7->prefix_length);
|
||||
|
||||
// polyline with %HEX
|
||||
api::ParsedURL reference_8{
|
||||
"match", 1, "car", "polyline(}jmwFz~ubMyCa@`@yDJqE)?你好&\n \"%~", 14UL};
|
||||
auto result_8 = api::parseURL(
|
||||
"/match/v1/car/polyline(%7DjmwFz~ubMyCa@%60@yDJqE)?%e4%bd%a0%e5%a5%bd&%0A%20%22%25%7e");
|
||||
BOOST_CHECK(result_8);
|
||||
BOOST_CHECK_EQUAL(reference_8.service, result_8->service);
|
||||
BOOST_CHECK_EQUAL(reference_8.version, result_8->version);
|
||||
BOOST_CHECK_EQUAL(reference_8.profile, result_8->profile);
|
||||
CHECK_EQUAL_RANGE(reference_8.query, result_8->query);
|
||||
BOOST_CHECK_EQUAL(reference_8.prefix_length, result_8->prefix_length);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
||||
+14
-9
@@ -49,11 +49,12 @@ BOOST_AUTO_TEST_CASE(io_nonexistent_file)
|
||||
osrm::storage::io::FileReader::VerifyFingerprint);
|
||||
BOOST_REQUIRE_MESSAGE(false, "Should not get here");
|
||||
}
|
||||
catch (const osrm::util::exception &e)
|
||||
catch (const osrm::util::RuntimeError &e)
|
||||
{
|
||||
const std::string expected("Error opening non_existent_test_io.tmp");
|
||||
const std::string expected("Problem opening file: " + IO_NONEXISTENT_FILE);
|
||||
const std::string got(e.what());
|
||||
BOOST_REQUIRE(std::equal(expected.begin(), expected.end(), got.begin()));
|
||||
BOOST_REQUIRE(e.GetCode() == osrm::ErrorCode::FileOpenError);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,12 +84,12 @@ BOOST_AUTO_TEST_CASE(file_too_small)
|
||||
osrm::storage::serialization::read(infile, buffer);
|
||||
BOOST_REQUIRE_MESSAGE(false, "Should not get here");
|
||||
}
|
||||
catch (const osrm::util::exception &e)
|
||||
catch (const osrm::util::RuntimeError &e)
|
||||
{
|
||||
const std::string expected(
|
||||
"Error reading from file_too_small_test_io.tmp: Unexpected end of file");
|
||||
const std::string expected("Unexpected end of file: " + IO_TOO_SMALL_FILE);
|
||||
const std::string got(e.what());
|
||||
BOOST_REQUIRE(std::equal(expected.begin(), expected.end(), got.begin()));
|
||||
BOOST_REQUIRE(e.GetCode() == osrm::ErrorCode::UnexpectedEndOfFile);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,11 +112,13 @@ BOOST_AUTO_TEST_CASE(io_corrupt_fingerprint)
|
||||
osrm::storage::io::FileReader::VerifyFingerprint);
|
||||
BOOST_REQUIRE_MESSAGE(false, "Should not get here");
|
||||
}
|
||||
catch (const osrm::util::exception &e)
|
||||
catch (const osrm::util::RuntimeError &e)
|
||||
{
|
||||
const std::string expected("Fingerprint mismatch in corrupt_fingerprint_file_test_io.tmp");
|
||||
const std::string expected("Fingerprint did not match the expected value: " +
|
||||
IO_CORRUPT_FINGERPRINT_FILE);
|
||||
const std::string got(e.what());
|
||||
BOOST_REQUIRE(std::equal(expected.begin(), expected.end(), got.begin()));
|
||||
BOOST_REQUIRE(e.GetCode() == osrm::ErrorCode::InvalidFingerprint);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,11 +149,13 @@ BOOST_AUTO_TEST_CASE(io_incompatible_fingerprint)
|
||||
osrm::storage::io::FileReader::VerifyFingerprint);
|
||||
BOOST_REQUIRE_MESSAGE(false, "Should not get here");
|
||||
}
|
||||
catch (const osrm::util::exception &e)
|
||||
catch (const osrm::util::RuntimeError &e)
|
||||
{
|
||||
const std::string expected("Fingerprint mismatch in " + IO_INCOMPATIBLE_FINGERPRINT_FILE);
|
||||
const std::string expected("Fingerprint did not match the expected value: " +
|
||||
IO_INCOMPATIBLE_FINGERPRINT_FILE);
|
||||
const std::string got(e.what());
|
||||
BOOST_REQUIRE(std::equal(expected.begin(), expected.end(), got.begin()));
|
||||
BOOST_REQUIRE(e.GetCode() == osrm::ErrorCode::InvalidFingerprint);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user