intersection classes with variable degree of discretization

This commit is contained in:
Moritz Kobitzsch 2016-05-10 08:37:45 +02:00 committed by Patrick Niklaus
parent 0f3942558f
commit 4d9aa65e78
No known key found for this signature in database
GPG Key ID: E426891B5F978B1B
23 changed files with 724 additions and 702 deletions

View File

@ -12,6 +12,7 @@
- Better support for osrm-routed binary upgrade on the fly [UNIX specific]: - Better support for osrm-routed binary upgrade on the fly [UNIX specific]:
- Open sockets with SO_REUSEPORT to allow multiple osrm-routed processes serving requests from the same port. - Open sockets with SO_REUSEPORT to allow multiple osrm-routed processes serving requests from the same port.
- Add SIGNAL_PARENT_WHEN_READY environment variable to enable osrm-routed signal its parent with USR1 when it's running and waiting for requests. - Add SIGNAL_PARENT_WHEN_READY environment variable to enable osrm-routed signal its parent with USR1 when it's running and waiting for requests.
- BREAKING: Intersection Classification adds a new file to the mix (osrm.icd). This breaks the fileformat for older versions.
- Guidance: - Guidance:
- improved detection of turning streets, not reporting new-name in wrong situations - improved detection of turning streets, not reporting new-name in wrong situations

View File

@ -0,0 +1,134 @@
@routing @guidance @intersections
Feature: Intersections Data
Background:
Given the profile "car"
Given a grid size of 10 meters
Scenario: Passing Three Way South
Given the node map
| a | | b | | c |
| | | d | | |
And the ways
| nodes | name |
| ab | through |
| bc | through |
| bd | corner |
When I route I should get
| waypoints | route | turns | intersections |
| a,c | through,through | depart,arrive | true:90 true:270 false:0,true:90 true:180 false:270;true:90 true:270 false:0 |
Scenario: Passing Three Way North
Given the node map
| | | d | | |
| a | | b | | c |
And the ways
| nodes | name |
| ab | through |
| bc | through |
| bd | corner |
When I route I should get
| waypoints | route | turns | intersections |
| a,c | through,through | depart,arrive | true:90 true:270 false:0,true:0 true:90 false:270;true:90 true:270 false:0 |
Scenario: Passing Oneway Street In
Given the node map
| | | d | | |
| a | | b | | c |
And the ways
| nodes | name | oneway |
| ab | through | no |
| bc | through | no |
| db | corner | yes |
When I route I should get
| waypoints | route | turns | intersections |
| a,c | through,through | depart,arrive | true:90 true:270 false:0,false:0 true:90 false:270;true:90 true:270 false:0 |
Scenario: Passing Oneway Street Out
Given the node map
| | | d | | |
| a | | b | | c |
And the ways
| nodes | name | oneway |
| ab | through | no |
| bc | through | no |
| bd | corner | yes |
When I route I should get
| waypoints | route | turns | intersections |
| a,c | through,through | depart,arrive | true:90 true:270 false:0,true:0 true:90 false:270;true:90 true:270 false:0 |
Scenario: Passing Two Intersections
Given the node map
| | | e | | | | |
| a | | b | | c | | d |
| | | | | f | | |
And the ways
| nodes | name |
| ab | through |
| bc | through |
| cd | through |
| be | corner |
| cf | corner |
When I route I should get
| waypoints | route | turns | intersections |
| a,d | through,through | depart,arrive | true:90 true:270 false:0,true:0 true:90 false:270,true:90 true:180 false:270;true:90 true:270 false:0 |
Scenario: Passing Two Intersections, Collapsing
Given the node map
| | | e | | | | |
| a | | b | | c | | d |
| | | | | f | | |
And the ways
| nodes | name |
| ab | through |
| bc | throughbridge |
| cd | through |
| be | corner |
| cf | corner |
When I route I should get
| waypoints | route | turns | intersections |
| a,d | through,through | depart,arrive | true:90 true:270 false:0,true:0 true:90 false:270,true:90 true:180 false:270;true:90 true:270 false:0 |
| f,a | corner,throughbridge,through | depart,end of road left,arrive | true:0 true:180;true:90 false:180 true:270,true:0 false:90 true:270;true:90 true:270 false:0 |
Scenario: Roundabouts
Given the node map
| | | | | e | | | | |
| | | | | | | | | |
| | | | | a | | | | |
| | | | 1 | | 4 | | | |
| | | | | | | | | |
| f | | b | | | | d | | h |
| | | | | | | | | |
| | | | 2 | | 3 | | | |
| | | | | c | | | | |
| | | | | | | | | |
| | | | | g | | | | |
And the ways
| nodes | junction |
| abcda | roundabout |
| ea | |
| fb | |
| gc | |
| hd | |
When I route I should get
| waypoints | route | turns | intersections |
| e,f | ea,fb,fb | depart,abcda-exit-1,arrive | true:0 true:180;false:0 false:150 true:210,false:30 true:150 true:270;true:90 true:270 false:0 |
| e,g | ea,gc,gc | depart,abcda-exit-2,arrive | true:0 true:180;false:0 false:150 true:210,false:30 true:150 true:270,true:30 true:180 false:330;true:0 true:180 |
| e,h | ea,hd,hd | depart,abcda-exit-3,arrive | true:0 true:180;false:0 false:150 true:210,false:30 true:150 true:270,true:30 true:180 false:330,true:90 false:210 true:330;true:90 true:270 false:0 |
| e,2 | ea,abcda,abcda | depart,abcda-exit-undefined,arrive | true:0 true:180;false:0 false:150 true:210,false:30 true:150 true:270;true:146 false:326 false:0 |
| 1,g | abcda,gc,gc | depart,abcda-exit-2,arrive | false:34 true:214 false:0;false:34 true:214,false:30 true:150 true:270,true:30 true:180 false:330;true:0 true:180 |
| 1,3 | abcda,abcda | depart,arrive | false:34 true:214 false:0,false:30 true:150 true:270,true:30 true:180 false:330;true:34 false:214 false:0 |

View File

@ -46,8 +46,8 @@ module.exports = function () {
if (headers.has('trips')) { if (headers.has('trips')) {
subTrips = json.trips.filter(t => !!t).map(t => t.legs).map(tl => Array.prototype.concat.apply([], tl.map((sl, i) => { subTrips = json.trips.filter(t => !!t).map(t => t.legs).map(tl => Array.prototype.concat.apply([], tl.map((sl, i) => {
var toAdd = []; var toAdd = [];
if (i === 0) toAdd.push(sl.steps[0].maneuver.location); if (i === 0) toAdd.push(sl.steps[0].intersections[0].location);
toAdd.push(sl.steps[sl.steps.length-1].maneuver.location); toAdd.push(sl.steps[sl.steps.length-1].intersections[0].location);
return toAdd; return toAdd;
}))); })));
} }

View File

@ -222,7 +222,7 @@ module.exports = function () {
}); });
}; };
['osrm','osrm.names','osrm.restrictions','osrm.ebg','osrm.enw','osrm.edges','osrm.fileIndex','osrm.geometry','osrm.nodes','osrm.ramIndex','osrm.properties'].forEach(file => { ['osrm','osrm.names','osrm.restrictions','osrm.ebg','osrm.enw','osrm.edges','osrm.fileIndex','osrm.geometry','osrm.nodes','osrm.ramIndex','osrm.properties','osrm.icd'].forEach(file => {
q.defer(rename, file); q.defer(rename, file);
}); });
@ -273,7 +273,7 @@ module.exports = function () {
var q = d3.queue(); var q = d3.queue();
['osrm.hsgr','osrm.fileIndex','osrm.geometry','osrm.nodes','osrm.ramIndex','osrm.core','osrm.edges','osrm.datasource_indexes','osrm.datasource_names','osrm.level'].forEach((file) => { ['osrm.hsgr','osrm.fileIndex','osrm.geometry','osrm.nodes','osrm.ramIndex','osrm.core','osrm.edges','osrm.datasource_indexes','osrm.datasource_names','osrm.level','osrm.icd'].forEach((file) => {
q.defer(rename, file); q.defer(rename, file);
}); });

View File

@ -138,7 +138,7 @@ module.exports = function () {
}; };
this.bearingList = (instructions) => { this.bearingList = (instructions) => {
return this.extractInstructionList(instructions, s => s.maneuver.bearing_after); return this.extractInstructionList(instructions, s => s.intersections[0].bearings[s.intersections[0].bearing_after]);
}; };
this.annotationList = (instructions) => { this.annotationList = (instructions) => {
@ -178,6 +178,19 @@ module.exports = function () {
.join(','); .join(',');
}; };
this.intersectionList = (instructions) => {
return instructions.legs.reduce((m, v) => m.concat(v.steps), [])
.map( v => {
return v.intersections
.map( intersection => {
var string = intersection.entry[0]+':'+intersection.bearings[0], i;
for( i = 1; i < intersection.bearings.length; ++i )
string = string + ' ' + intersection.entry[i]+':'+intersection.bearings[i];
return string;
}).join(',')
}).join(';')
};
this.modeList = (instructions) => { this.modeList = (instructions) => {
return this.extractInstructionList(instructions, s => s.mode); return this.extractInstructionList(instructions, s => s.mode);
}; };

View File

@ -31,7 +31,7 @@ module.exports = function () {
var afterRequest = (err, res, body) => { var afterRequest = (err, res, body) => {
if (err) return cb(err); if (err) return cb(err);
if (body && body.length) { if (body && body.length) {
var instructions, bearings, turns, modes, times, distances, summary; var instructions, bearings, turns, modes, times, distances, summary, intersections;
var json = JSON.parse(body); var json = JSON.parse(body);
@ -41,6 +41,7 @@ module.exports = function () {
instructions = this.wayList(json.routes[0]); instructions = this.wayList(json.routes[0]);
bearings = this.bearingList(json.routes[0]); bearings = this.bearingList(json.routes[0]);
turns = this.turnList(json.routes[0]); turns = this.turnList(json.routes[0]);
intersections = this.intersectionList(json.routes[0]);
modes = this.modeList(json.routes[0]); modes = this.modeList(json.routes[0]);
times = this.timeList(json.routes[0]); times = this.timeList(json.routes[0]);
distances = this.distanceList(json.routes[0]); distances = this.distanceList(json.routes[0]);
@ -108,6 +109,10 @@ module.exports = function () {
} }
} }
if (headers.has('intersections')) {
got.intersections = (intersections || '').trim();
}
var putValue = (key, value) => { var putValue = (key, value) => {
if (headers.has(key)) got[key] = instructions ? value : ''; if (headers.has(key)) got[key] = instructions ? value : '';
}; };

View File

@ -9,12 +9,12 @@
#include "util/guidance/bearing_class.hpp" #include "util/guidance/bearing_class.hpp"
#include "util/guidance/entry_class.hpp" #include "util/guidance/entry_class.hpp"
#include "storage/storage_config.hpp"
#include "engine/geospatial_query.hpp" #include "engine/geospatial_query.hpp"
#include "extractor/compressed_edge_container.hpp" #include "extractor/compressed_edge_container.hpp"
#include "extractor/original_edge_data.hpp" #include "extractor/original_edge_data.hpp"
#include "extractor/profile_properties.hpp" #include "extractor/profile_properties.hpp"
#include "extractor/query_node.hpp" #include "extractor/query_node.hpp"
#include "storage/storage_config.hpp"
#include "util/graph_loader.hpp" #include "util/graph_loader.hpp"
#include "util/io.hpp" #include "util/io.hpp"
#include "util/range_table.hpp" #include "util/range_table.hpp"
@ -23,6 +23,7 @@
#include "util/simple_logger.hpp" #include "util/simple_logger.hpp"
#include "util/static_graph.hpp" #include "util/static_graph.hpp"
#include "util/static_rtree.hpp" #include "util/static_rtree.hpp"
#include "util/typedefs.hpp"
#include "osrm/coordinate.hpp" #include "osrm/coordinate.hpp"
@ -100,7 +101,8 @@ class InternalDataFacade final : public BaseDataFacade
util::ShM<util::guidance::EntryClass, false>::vector m_entry_class_table; util::ShM<util::guidance::EntryClass, false>::vector m_entry_class_table;
// the look-up table for distinct bearing classes. A bearing class lists the available bearings // the look-up table for distinct bearing classes. A bearing class lists the available bearings
// at an intersection // at an intersection
util::ShM<util::guidance::BearingClass, false>::vector m_bearing_class_table; util::RangeTable<16, false> m_bearing_ranges_table;
util::ShM<DiscreteBearing, false>::vector m_bearing_values_table;
void LoadProfileProperties(const boost::filesystem::path &properties_path) void LoadProfileProperties(const boost::filesystem::path &properties_path)
{ {
@ -299,34 +301,43 @@ class InternalDataFacade final : public BaseDataFacade
{ {
std::ifstream intersection_stream(intersection_class_file.string(), std::ios::binary); std::ifstream intersection_stream(intersection_class_file.string(), std::ios::binary);
if (!intersection_stream) if (!intersection_stream)
util::SimpleLogger().Write(logWARNING) << "Failed to open " << intersection_class_file throw util::exception("Could not open " + intersection_class_file.string() +
<< " for reading."; " for reading.");
if (!util::readAndCheckFingerprint(intersection_stream)) if (!util::readAndCheckFingerprint(intersection_stream))
{ throw util::exception("Fingeprint does not match in " +
util::SimpleLogger().Write(logWARNING) intersection_class_file.string());
<< "Fingerprint does not match or reading failed";
exit(-1);
}
{ {
util::SimpleLogger().Write(logINFO) << "Loading Bearing Class IDs"; util::SimpleLogger().Write(logINFO) << "Loading Bearing Class IDs";
std::vector<BearingClassID> bearing_class_id; std::vector<BearingClassID> bearing_class_id;
util::deserializeVector(intersection_stream, bearing_class_id); if (!util::deserializeVector(intersection_stream, bearing_class_id))
throw util::exception("Reading from " + intersection_class_file.string() + " failed.");
m_bearing_class_id_table.resize(bearing_class_id.size()); m_bearing_class_id_table.resize(bearing_class_id.size());
std::copy(bearing_class_id.begin(), bearing_class_id.end(), std::copy(bearing_class_id.begin(), bearing_class_id.end(),
&m_bearing_class_id_table[0]); &m_bearing_class_id_table[0]);
} }
{ {
util::SimpleLogger().Write(logINFO) << "Loading Bearing Classes"; util::SimpleLogger().Write(logINFO) << "Loading Bearing Classes";
// read the range table
intersection_stream >> m_bearing_ranges_table;
std::vector<util::guidance::BearingClass> bearing_classes; std::vector<util::guidance::BearingClass> bearing_classes;
util::deserializeVector(intersection_stream, bearing_classes); // and the actual bearing values
m_bearing_class_table.resize(bearing_classes.size()); std::uint64_t num_bearings;
std::copy(bearing_classes.begin(), bearing_classes.end(), &m_bearing_class_table[0]); intersection_stream >> num_bearings;
m_bearing_values_table.resize(num_bearings);
intersection_stream.read(reinterpret_cast<char *>(&m_bearing_values_table[0]),
sizeof(m_bearing_values_table[0]) * num_bearings);
if (!static_cast<bool>(intersection_stream))
throw util::exception("Reading from " + intersection_class_file.string() + " failed.");
} }
{ {
util::SimpleLogger().Write(logINFO) << "Loading Entry Classes"; util::SimpleLogger().Write(logINFO) << "Loading Entry Classes";
std::vector<util::guidance::EntryClass> entry_classes; std::vector<util::guidance::EntryClass> entry_classes;
util::deserializeVector(intersection_stream, entry_classes); if (!util::deserializeVector(intersection_stream, entry_classes))
throw util::exception("Reading from " + intersection_class_file.string() + " failed.");
m_entry_class_table.resize(entry_classes.size()); m_entry_class_table.resize(entry_classes.size());
std::copy(entry_classes.begin(), entry_classes.end(), &m_entry_class_table[0]); std::copy(entry_classes.begin(), entry_classes.end(), &m_entry_class_table[0]);
} }
@ -509,9 +520,8 @@ class InternalDataFacade final : public BaseDataFacade
bearing, bearing_range); bearing, bearing_range);
} }
std::pair<PhantomNode, PhantomNode> std::pair<PhantomNode, PhantomNode> NearestPhantomNodeWithAlternativeFromBigComponent(
NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate, const util::Coordinate input_coordinate, const double max_distance) const override final
const double max_distance) const override final
{ {
BOOST_ASSERT(m_geospatial_query.get()); BOOST_ASSERT(m_geospatial_query.get());
@ -676,7 +686,15 @@ class InternalDataFacade final : public BaseDataFacade
GetBearingClass(const BearingClassID bearing_class_id) const override final GetBearingClass(const BearingClassID bearing_class_id) const override final
{ {
BOOST_ASSERT(bearing_class_id != INVALID_BEARING_CLASSID); BOOST_ASSERT(bearing_class_id != INVALID_BEARING_CLASSID);
return m_bearing_class_table.at(bearing_class_id); auto range = m_bearing_ranges_table.GetRange(bearing_class_id);
util::guidance::BearingClass result;
for (auto itr = m_bearing_values_table.begin() + range.front();
itr != m_bearing_values_table.begin() + range.back() + 1; ++itr)
result.add(*itr);
return result;
} }
EntryClassID GetEntryClassID(const EdgeID eid) const override final EntryClassID GetEntryClassID(const EdgeID eid) const override final
@ -686,7 +704,6 @@ class InternalDataFacade final : public BaseDataFacade
util::guidance::EntryClass GetEntryClass(const EntryClassID entry_class_id) const override final util::guidance::EntryClass GetEntryClass(const EntryClassID entry_class_id) const override final
{ {
BOOST_ASSERT(entry_class_id != INVALID_ENTRY_CLASSID);
return m_entry_class_table.at(entry_class_id); return m_entry_class_table.at(entry_class_id);
} }
}; };

View File

@ -7,9 +7,9 @@
#include "storage/shared_datatype.hpp" #include "storage/shared_datatype.hpp"
#include "storage/shared_memory.hpp" #include "storage/shared_memory.hpp"
#include "extractor/compressed_edge_container.hpp"
#include "extractor/guidance/turn_instruction.hpp" #include "extractor/guidance/turn_instruction.hpp"
#include "extractor/profile_properties.hpp" #include "extractor/profile_properties.hpp"
#include "extractor/compressed_edge_container.hpp"
#include "util/guidance/bearing_class.hpp" #include "util/guidance/bearing_class.hpp"
#include "util/guidance/entry_class.hpp" #include "util/guidance/entry_class.hpp"
@ -20,6 +20,7 @@
#include "util/simple_logger.hpp" #include "util/simple_logger.hpp"
#include "util/static_graph.hpp" #include "util/static_graph.hpp"
#include "util/static_rtree.hpp" #include "util/static_rtree.hpp"
#include "util/typedefs.hpp"
#include <cstddef> #include <cstddef>
@ -51,7 +52,7 @@ class SharedDataFacade final : public BaseDataFacade
using QueryGraph = util::StaticGraph<EdgeData, true>; using QueryGraph = util::StaticGraph<EdgeData, true>;
using GraphNode = QueryGraph::NodeArrayEntry; using GraphNode = QueryGraph::NodeArrayEntry;
using GraphEdge = QueryGraph::EdgeArrayEntry; using GraphEdge = QueryGraph::EdgeArrayEntry;
using NameIndexBlock = util::RangeTable<16, true>::BlockT; using IndexBlock = util::RangeTable<16, true>::BlockT;
using InputEdge = QueryGraph::InputEdge; using InputEdge = QueryGraph::InputEdge;
using RTreeLeaf = super::RTreeLeaf; using RTreeLeaf = super::RTreeLeaf;
using SharedRTree = using SharedRTree =
@ -105,7 +106,8 @@ class SharedDataFacade final : public BaseDataFacade
util::ShM<util::guidance::EntryClass, true>::vector m_entry_class_table; util::ShM<util::guidance::EntryClass, true>::vector m_entry_class_table;
// the look-up table for distinct bearing classes. A bearing class lists the available bearings // the look-up table for distinct bearing classes. A bearing class lists the available bearings
// at an intersection // at an intersection
util::ShM<util::guidance::BearingClass, true>::vector m_bearing_class_table; std::shared_ptr<util::RangeTable<16, true>> m_bearing_ranges_table;
util::ShM<DiscreteBearing, true>::vector m_bearing_values_table;
void LoadChecksum() void LoadChecksum()
{ {
@ -206,11 +208,11 @@ class SharedDataFacade final : public BaseDataFacade
{ {
auto offsets_ptr = data_layout->GetBlockPtr<unsigned>( auto offsets_ptr = data_layout->GetBlockPtr<unsigned>(
shared_memory, storage::SharedDataLayout::NAME_OFFSETS); shared_memory, storage::SharedDataLayout::NAME_OFFSETS);
auto blocks_ptr = data_layout->GetBlockPtr<NameIndexBlock>( auto blocks_ptr = data_layout->GetBlockPtr<IndexBlock>(
shared_memory, storage::SharedDataLayout::NAME_BLOCKS); shared_memory, storage::SharedDataLayout::NAME_BLOCKS);
util::ShM<unsigned, true>::vector name_offsets( util::ShM<unsigned, true>::vector name_offsets(
offsets_ptr, data_layout->num_entries[storage::SharedDataLayout::NAME_OFFSETS]); offsets_ptr, data_layout->num_entries[storage::SharedDataLayout::NAME_OFFSETS]);
util::ShM<NameIndexBlock, true>::vector name_blocks( util::ShM<IndexBlock, true>::vector name_blocks(
blocks_ptr, data_layout->num_entries[storage::SharedDataLayout::NAME_BLOCKS]); blocks_ptr, data_layout->num_entries[storage::SharedDataLayout::NAME_BLOCKS]);
auto names_list_ptr = data_layout->GetBlockPtr<char>( auto names_list_ptr = data_layout->GetBlockPtr<char>(
@ -283,7 +285,7 @@ class SharedDataFacade final : public BaseDataFacade
m_datasource_name_lengths = std::move(datasource_name_lengths); m_datasource_name_lengths = std::move(datasource_name_lengths);
} }
void LoadIntersecionClasses() void LoadIntersectionClasses()
{ {
auto bearing_class_id_ptr = data_layout->GetBlockPtr<BearingClassID>( auto bearing_class_id_ptr = data_layout->GetBlockPtr<BearingClassID>(
shared_memory, storage::SharedDataLayout::BEARING_CLASSID); shared_memory, storage::SharedDataLayout::BEARING_CLASSID);
@ -292,11 +294,23 @@ class SharedDataFacade final : public BaseDataFacade
data_layout->num_entries[storage::SharedDataLayout::BEARING_CLASSID]); data_layout->num_entries[storage::SharedDataLayout::BEARING_CLASSID]);
m_bearing_class_id_table = std::move(bearing_class_id_table); m_bearing_class_id_table = std::move(bearing_class_id_table);
auto bearing_class_ptr = data_layout->GetBlockPtr<util::guidance::BearingClass>( auto bearing_class_ptr = data_layout->GetBlockPtr<DiscreteBearing>(
shared_memory, storage::SharedDataLayout::BEARING_CLASS); shared_memory, storage::SharedDataLayout::BEARING_VALUES);
typename util::ShM<util::guidance::BearingClass, true>::vector bearing_class_table( typename util::ShM<DiscreteBearing, true>::vector bearing_class_table(
bearing_class_ptr, data_layout->num_entries[storage::SharedDataLayout::BEARING_CLASS]); bearing_class_ptr, data_layout->num_entries[storage::SharedDataLayout::BEARING_VALUES]);
m_bearing_class_table = std::move(bearing_class_table); m_bearing_values_table = std::move(bearing_class_table);
auto offsets_ptr = data_layout->GetBlockPtr<unsigned>(
shared_memory, storage::SharedDataLayout::BEARING_OFFSETS);
auto blocks_ptr = data_layout->GetBlockPtr<IndexBlock>(
shared_memory, storage::SharedDataLayout::BEARING_BLOCKS);
util::ShM<unsigned, true>::vector bearing_offsets(
offsets_ptr, data_layout->num_entries[storage::SharedDataLayout::BEARING_OFFSETS]);
util::ShM<IndexBlock, true>::vector bearing_blocks(
blocks_ptr, data_layout->num_entries[storage::SharedDataLayout::BEARING_BLOCKS]);
m_bearing_ranges_table = util::make_unique<util::RangeTable<16, true>>(
bearing_offsets, bearing_blocks, static_cast<unsigned>(m_bearing_values_table.size()));
auto entry_class_ptr = data_layout->GetBlockPtr<util::guidance::EntryClass>( auto entry_class_ptr = data_layout->GetBlockPtr<util::guidance::EntryClass>(
shared_memory, storage::SharedDataLayout::ENTRY_CLASS); shared_memory, storage::SharedDataLayout::ENTRY_CLASS);
@ -392,7 +406,7 @@ class SharedDataFacade final : public BaseDataFacade
LoadCoreInformation(); LoadCoreInformation();
LoadProfileProperties(); LoadProfileProperties();
LoadRTree(); LoadRTree();
LoadIntersecionClasses(); LoadIntersectionClasses();
util::SimpleLogger().Write() << "number of geometries: " util::SimpleLogger().Write() << "number of geometries: "
<< m_coordinate_list.size(); << m_coordinate_list.size();
@ -702,8 +716,6 @@ class SharedDataFacade final : public BaseDataFacade
{ {
return m_profile_properties->continue_straight_at_waypoint; return m_profile_properties->continue_straight_at_waypoint;
} }
<<<<<<< HEAD
=======
BearingClassID GetBearingClassID(const NodeID id) const override final BearingClassID GetBearingClassID(const NodeID id) const override final
{ {
@ -714,7 +726,12 @@ class SharedDataFacade final : public BaseDataFacade
GetBearingClass(const BearingClassID bearing_class_id) const override final GetBearingClass(const BearingClassID bearing_class_id) const override final
{ {
BOOST_ASSERT(bearing_class_id != INVALID_BEARING_CLASSID); BOOST_ASSERT(bearing_class_id != INVALID_BEARING_CLASSID);
return m_bearing_class_table.at(bearing_class_id); auto range = m_bearing_ranges_table->GetRange(bearing_class_id);
util::guidance::BearingClass result;
for (auto itr = m_bearing_values_table.begin() + range.front();
itr != m_bearing_values_table.begin() + range.back() + 1; ++itr)
result.add(*itr);
return result;
} }
EntryClassID GetEntryClassID(const EdgeID eid) const override final EntryClassID GetEntryClassID(const EdgeID eid) const override final
@ -724,10 +741,8 @@ class SharedDataFacade final : public BaseDataFacade
util::guidance::EntryClass GetEntryClass(const EntryClassID entry_class_id) const override final util::guidance::EntryClass GetEntryClass(const EntryClassID entry_class_id) const override final
{ {
BOOST_ASSERT(entry_class_id != INVALID_ENTRY_CLASSID);
return m_entry_class_table.at(entry_class_id); return m_entry_class_table.at(entry_class_id);
} }
>>>>>>> a5cb6a1... initial version of intersection classification
}; };
} }
} }

View File

@ -12,7 +12,7 @@
#include "util/bearing.hpp" #include "util/bearing.hpp"
#include "util/coordinate.hpp" #include "util/coordinate.hpp"
#include "util/coordinate_calculation.hpp" #include "util/coordinate_calculation.hpp"
#include "util/guidance/bearing_class.hpp" #include "util/guidance/toolkit.hpp"
#include "util/guidance/entry_class.hpp" #include "util/guidance/entry_class.hpp"
#include "util/typedefs.hpp" #include "util/typedefs.hpp"
@ -28,15 +28,12 @@ namespace guidance
{ {
namespace detail namespace detail
{ {
StepManeuver stepManeuverFromGeometry(extractor::guidance::TurnInstruction instruction,
const LegGeometry &leg_geometry,
const std::size_t segment_index,
util::guidance::EntryClass entry_class,
util::guidance::BearingClass bearing_class);
StepManeuver stepManeuverFromGeometry(extractor::guidance::TurnInstruction instruction, Intersection intersectionFromGeometry(const WaypointType waypoint_type,
const WaypointType waypoint_type, const double segment_duration,
const LegGeometry &leg_geometry); const LegGeometry &leg_geometry,
const std::size_t segment_index);
} // ns detail } // ns detail
template <typename DataFacadeT> template <typename DataFacadeT>
@ -70,10 +67,13 @@ std::vector<RouteStep> assembleSteps(const DataFacadeT &facade,
if (leg_data.size() > 0) if (leg_data.size() > 0)
{ {
StepManeuver maneuver = {extractor::guidance::TurnInstruction::NO_TURN(),
WaypointType::Depart, 0};
StepManeuver maneuver = detail::stepManeuverFromGeometry( auto intersection =
extractor::guidance::TurnInstruction::NO_TURN(), WaypointType::Depart, leg_geometry); detail::intersectionFromGeometry(WaypointType::Depart, 0, leg_geometry, 0);
maneuver.location = source_node.location; intersection = util::guidance::setIntersectionClasses(std::move(intersection), source_node);
// maneuver.location = source_node.location;
// PathData saves the information we need of the segment _before_ the turn, // PathData saves the information we need of the segment _before_ the turn,
// but a RouteStep is with regard to the segment after the turn. // but a RouteStep is with regard to the segment after the turn.
@ -95,19 +95,25 @@ std::vector<RouteStep> assembleSteps(const DataFacadeT &facade,
BOOST_ASSERT(segment_duration >= 0); BOOST_ASSERT(segment_duration >= 0);
const auto name = facade.GetNameForID(step_name_id); const auto name = facade.GetNameForID(step_name_id);
const auto distance = leg_geometry.segment_distances[segment_index]; const auto distance = leg_geometry.segment_distances[segment_index];
steps.push_back(RouteStep{step_name_id, name, NO_ROTARY_NAME, std::vector<Intersection> intersections(1, intersection);
segment_duration / 10.0, distance, path_point.travel_mode, intersection = detail::intersectionFromGeometry(
maneuver, leg_geometry.FrontIndex(segment_index), WaypointType::None, segment_duration / 10., leg_geometry, segment_index);
leg_geometry.BackIndex(segment_index) + 1}); intersection.entry_class = facade.GetEntryClass(path_point.entry_classid);
if (leg_data_index + 1 < leg_data.size()){ intersection.bearing_class =
facade.GetBearingClass(facade.GetBearingClassID(path_point.turn_via_node));
steps.push_back(RouteStep{
step_name_id, name, NO_ROTARY_NAME, segment_duration / 10.0, distance,
path_point.travel_mode, maneuver, leg_geometry.FrontIndex(segment_index),
leg_geometry.BackIndex(segment_index) + 1, std::move(intersections)});
if (leg_data_index + 1 < leg_data.size())
{
step_name_id = leg_data[leg_data_index + 1].name_id; step_name_id = leg_data[leg_data_index + 1].name_id;
} else { }
else
{
step_name_id = target_node.name_id; step_name_id = target_node.name_id;
} }
maneuver = detail::stepManeuverFromGeometry( maneuver = {path_point.turn_instruction, WaypointType::None, 0};
path_point.turn_instruction, leg_geometry, segment_index,
facade.GetEntryClass(path_point.entry_classid),
facade.GetBearingClass(facade.GetBearingClassID(path_point.turn_via_node)));
segment_index++; segment_index++;
segment_duration = 0; segment_duration = 0;
} }
@ -115,10 +121,10 @@ std::vector<RouteStep> assembleSteps(const DataFacadeT &facade,
const auto distance = leg_geometry.segment_distances[segment_index]; const auto distance = leg_geometry.segment_distances[segment_index];
const int duration = segment_duration + target_duration; const int duration = segment_duration + target_duration;
BOOST_ASSERT(duration >= 0); BOOST_ASSERT(duration >= 0);
steps.push_back(RouteStep{step_name_id, facade.GetNameForID(step_name_id), steps.push_back(RouteStep{
NO_ROTARY_NAME, duration / 10., distance, target_mode, maneuver, step_name_id, facade.GetNameForID(step_name_id), NO_ROTARY_NAME, duration / 10.,
leg_geometry.FrontIndex(segment_index), distance, target_mode, maneuver, leg_geometry.FrontIndex(segment_index),
leg_geometry.BackIndex(segment_index) + 1}); leg_geometry.BackIndex(segment_index) + 1, std::vector<Intersection>(1, intersection)});
} }
// In this case the source + target are on the same edge segment // In this case the source + target are on the same edge segment
else else
@ -129,28 +135,37 @@ std::vector<RouteStep> assembleSteps(const DataFacadeT &facade,
// |---| source_duration // |---| source_duration
// |---------| target_duration // |---------| target_duration
StepManeuver maneuver = detail::stepManeuverFromGeometry( StepManeuver maneuver = {extractor::guidance::TurnInstruction::NO_TURN(),
extractor::guidance::TurnInstruction::NO_TURN(), WaypointType::Depart, leg_geometry); WaypointType::Depart, 0};
int duration = target_duration - source_duration; int duration = target_duration - source_duration;
BOOST_ASSERT(duration >= 0); BOOST_ASSERT(duration >= 0);
steps.push_back(RouteStep{source_node.name_id, facade.GetNameForID(source_node.name_id), auto intersection = detail::intersectionFromGeometry(WaypointType::Depart, duration / 10.,
NO_ROTARY_NAME, duration / 10., leg_geometry, segment_index);
leg_geometry.segment_distances[segment_index], source_mode, intersection = util::guidance::setIntersectionClasses(std::move(intersection), source_node);
steps.push_back(RouteStep{
source_node.name_id, facade.GetNameForID(source_node.name_id), NO_ROTARY_NAME,
duration / 10., leg_geometry.segment_distances[segment_index], source_mode,
std::move(maneuver), leg_geometry.FrontIndex(segment_index), std::move(maneuver), leg_geometry.FrontIndex(segment_index),
leg_geometry.BackIndex(segment_index) + 1}); leg_geometry.BackIndex(segment_index) + 1, std::vector<Intersection>(1, intersection)});
} }
BOOST_ASSERT(segment_index == number_of_segments - 1); BOOST_ASSERT(segment_index == number_of_segments - 1);
// This step has length zero, the only reason we need it is the target location // This step has length zero, the only reason we need it is the target location
auto final_maneuver = detail::stepManeuverFromGeometry( StepManeuver final_maneuver = {extractor::guidance::TurnInstruction::NO_TURN(),
extractor::guidance::TurnInstruction::NO_TURN(), WaypointType::Arrive, leg_geometry); WaypointType::Arrive, 0};
auto intersection =
detail::intersectionFromGeometry(WaypointType::Arrive, 0, leg_geometry, segment_index);
intersection = util::guidance::setIntersectionClasses(std::move(intersection), target_node);
BOOST_ASSERT(!leg_geometry.locations.empty()); BOOST_ASSERT(!leg_geometry.locations.empty());
steps.push_back(RouteStep{target_node.name_id, facade.GetNameForID(target_node.name_id), steps.push_back(RouteStep{target_node.name_id, facade.GetNameForID(target_node.name_id),
NO_ROTARY_NAME, ZERO_DURATION, ZERO_DISTANCE, target_mode, NO_ROTARY_NAME, ZERO_DURATION, ZERO_DISTANCE, target_mode,
final_maneuver, leg_geometry.locations.size() - 1, std::move(final_maneuver), leg_geometry.locations.size() - 1,
leg_geometry.locations.size()}); leg_geometry.locations.size(),
std::vector<Intersection>(1, intersection)});
return steps; return steps;
} }

View File

@ -3,6 +3,9 @@
#include "engine/guidance/step_maneuver.hpp" #include "engine/guidance/step_maneuver.hpp"
#include "extractor/travel_mode.hpp" #include "extractor/travel_mode.hpp"
#include "util/coordinate.hpp"
#include "util/guidance/bearing_class.hpp"
#include "util/guidance/entry_class.hpp"
#include <cstddef> #include <cstddef>
@ -21,6 +24,30 @@ namespace guidance
// Notable exceptions are Departure and Arrival steps. // Notable exceptions are Departure and Arrival steps.
// Departue: s --> a --> b. Represents the segment s,a with location being s. // Departue: s --> a --> b. Represents the segment s,a with location being s.
// Arrive: a --> b --> t. The segment (b,t) is already covered by the previous segment. // Arrive: a --> b --> t. The segment (b,t) is already covered by the previous segment.
// A represenetation of intermediate intersections
struct Intersection
{
double duration;
double distance;
util::Coordinate location;
double bearing_before;
double bearing_after;
util::guidance::EntryClass entry_class;
util::guidance::BearingClass bearing_class;
};
inline Intersection getInvalidIntersection()
{
return {0,
0,
util::Coordinate{util::FloatLongitude{0.0}, util::FloatLatitude{0.0}},
0,
0,
util::guidance::EntryClass(),
util::guidance::BearingClass()};
}
struct RouteStep struct RouteStep
{ {
unsigned name_id; unsigned name_id;
@ -33,11 +60,21 @@ struct RouteStep
// indices into the locations array stored the LegGeometry // indices into the locations array stored the LegGeometry
std::size_t geometry_begin; std::size_t geometry_begin;
std::size_t geometry_end; std::size_t geometry_end;
std::vector<Intersection> intersections;
}; };
inline RouteStep getInvalidRouteStep() inline RouteStep getInvalidRouteStep()
{ {
return {0, "", "", 0, 0, TRAVEL_MODE_INACCESSIBLE, getInvalidStepManeuver(), 0, 0}; return {0,
"",
"",
0,
0,
TRAVEL_MODE_INACCESSIBLE,
getInvalidStepManeuver(),
0,
0,
std::vector<Intersection>(1, getInvalidIntersection())};
} }
} }
} }

View File

@ -3,8 +3,6 @@
#include "extractor/guidance/turn_instruction.hpp" #include "extractor/guidance/turn_instruction.hpp"
#include "util/coordinate.hpp" #include "util/coordinate.hpp"
#include "util/guidance/bearing_class.hpp"
#include "util/guidance/entry_class.hpp"
#include <cstdint> #include <cstdint>
#include <vector> #include <vector>
@ -23,38 +21,16 @@ enum class WaypointType : std::uint8_t
Depart, Depart,
}; };
// A represenetation of intermediate intersections
struct IntermediateIntersection
{
double duration;
double distance;
util::Coordinate location;
util::guidance::EntryClass entry_class;
util::guidance::BearingClass bearing_class;
};
struct StepManeuver struct StepManeuver
{ {
util::Coordinate location;
double bearing_before;
double bearing_after;
extractor::guidance::TurnInstruction instruction; extractor::guidance::TurnInstruction instruction;
WaypointType waypoint_type; WaypointType waypoint_type;
unsigned exit; unsigned exit;
util::guidance::EntryClass entry_class;
util::guidance::BearingClass bearing_class;
std::vector<IntermediateIntersection> intersections;
}; };
inline StepManeuver getInvalidStepManeuver() inline StepManeuver getInvalidStepManeuver()
{ {
return {util::Coordinate{util::FloatLongitude{0.0}, util::FloatLatitude{0.0}}, return {extractor::guidance::TurnInstruction::NO_TURN(), WaypointType::None, 0};
0,
0,
extractor::guidance::TurnInstruction::NO_TURN(),
WaypointType::None,
0,
{}};
} }
} // namespace guidance } // namespace guidance

View File

@ -44,8 +44,10 @@ struct SharedDataLayout
DATASOURCE_NAME_LENGTHS, DATASOURCE_NAME_LENGTHS,
PROPERTIES, PROPERTIES,
BEARING_CLASSID, BEARING_CLASSID,
BEARING_OFFSETS,
BEARING_BLOCKS,
BEARING_VALUES,
ENTRY_CLASS, ENTRY_CLASS,
BEARING_CLASS,
NUM_BLOCKS NUM_BLOCKS
}; };

View File

@ -6,6 +6,10 @@
#include <functional> #include <functional>
#include <vector> #include <vector>
#include <boost/functional/hash.hpp>
#include "util/typedefs.hpp"
namespace osrm namespace osrm
{ {
namespace util namespace util
@ -35,24 +39,8 @@ namespace guidance
class BearingClass class BearingClass
{ {
public: public:
using FlagBaseType = std::uint32_t; // Add a bearing to the set
const static constexpr double discrete_angle_step_size = 360. / 32; void add(const DiscreteBearing bearing);
BearingClass();
// add a continuous angle to the, returns true if no item existed that uses the same discrete
// angle
bool addContinuous(const double bearing);
// add a discrete ID, returns true if no item existed that uses the same discrete angle
bool addDiscreteID(const std::uint8_t id);
//remove a bearing from the list
void resetContinuous(const double bearing);
void resetDiscreteID(const std::uint8_t id);
//is available
bool hasContinuous(const double bearing) const;
bool hasDiscrete(const std::uint8_t id) const;
// hashing // hashing
bool operator==(const BearingClass &other) const; bool operator==(const BearingClass &other) const;
@ -60,19 +48,15 @@ class BearingClass
// sorting // sorting
bool operator<(const BearingClass &other) const; bool operator<(const BearingClass &other) const;
std::vector<double> getAvailableBearings() const; const std::vector<DiscreteBearing> &getAvailableBearings() const;
// get a discrete representation of an angle. Required to map a bearing/angle to the discrete std::size_t findMatchingBearing(const double bearing) const;
// ones stored within the class
static std::uint8_t angleToDiscreteID(double angle); const constexpr static double discrete_step_size = 360. / 24.;
static double discreteIDToAngle( const std::uint8_t ); static DiscreteBearing getDiscreteBearing(const double bearing);
// we are hiding the access to the flags behind a protection wall, to make sure the bit logic
// isn't tempered with
private: private:
// given a list of possible discrete angles, the available angles flag indicates the presence of std::vector<DiscreteBearing> available_bearings;
// a given turn at the intersection
FlagBaseType available_bearings_mask;
// allow hash access to internal representation // allow hash access to internal representation
friend std::size_t std::hash<BearingClass>::operator()(const BearingClass &) const; friend std::size_t std::hash<BearingClass>::operator()(const BearingClass &) const;
@ -88,8 +72,7 @@ namespace std
inline size_t hash<::osrm::util::guidance::BearingClass>:: inline size_t hash<::osrm::util::guidance::BearingClass>::
operator()(const ::osrm::util::guidance::BearingClass &bearing_class) const operator()(const ::osrm::util::guidance::BearingClass &bearing_class) const
{ {
return hash<::osrm::util::guidance::BearingClass::FlagBaseType>()( return boost::hash_value(bearing_class.available_bearings);
bearing_class.available_bearings_mask);
} }
} // namespace std } // namespace std

View File

@ -5,6 +5,9 @@
#include <cstdint> #include <cstdint>
#include <functional> #include <functional>
#include <iostream>
#include <bitset>
namespace osrm namespace osrm
{ {
namespace util namespace util
@ -51,6 +54,11 @@ class EntryClass
// sorting // sorting
bool operator<(const EntryClass &) const; bool operator<(const EntryClass &) const;
inline void print() const
{
std::cout << "Flags: " << std::bitset<16>(enabled_entries_flags) << std::endl;
}
private: private:
// given a list of possible discrete angles, the available angles flag indicates the presence of // given a list of possible discrete angles, the available angles flag indicates the presence of
// a given turn at the intersection // a given turn at the intersection

View File

@ -3,6 +3,8 @@
/* A set of tools required for guidance in both pre and post-processing */ /* A set of tools required for guidance in both pre and post-processing */
#include "engine/guidance/route_step.hpp"
#include "engine/phantom_node.hpp"
#include "extractor/guidance/turn_instruction.hpp" #include "extractor/guidance/turn_instruction.hpp"
#include "util/guidance/bearing_class.hpp" #include "util/guidance/bearing_class.hpp"
#include "util/guidance/entry_class.hpp" #include "util/guidance/entry_class.hpp"
@ -47,74 +49,36 @@ inline extractor::guidance::DirectionModifier getTurnDirection(const double angl
return extractor::guidance::DirectionModifier::UTurn; return extractor::guidance::DirectionModifier::UTurn;
} }
inline double getMatchingDiscreteBearing(const bool requires_entry, inline engine::guidance::Intersection
const double bearing, setIntersectionClasses(engine::guidance::Intersection intersection,
const EntryClass entry_class, const engine::PhantomNode &phantom)
const std::vector<double> existing_bearings)
{ {
if (existing_bearings.empty()) BOOST_ASSERT(intersection.bearing_before == 0 || intersection.bearing_after == 0);
return 0; const double bearing = std::max(intersection.bearing_before, intersection.bearing_after);
const double discrete_bearing = intersection.bearing_class = {};
BearingClass::discreteIDToAngle(BearingClass::angleToDiscreteID(bearing)); intersection.entry_class = {};
// it they are very close to the turn, the discrete bearing should be fine if (bearing >= 180.)
if (std::abs(bearing - discrete_bearing) < 0.25 * BearingClass::discrete_angle_step_size)
{ {
const auto isValidEntry = [&]() { intersection.bearing_class.add(std::round(bearing - 180.));
const auto bound = std::upper_bound(existing_bearings.begin(), existing_bearings.end(), if (phantom.forward_segment_id.id != SPECIAL_SEGMENTID &&
(discrete_bearing - 0.001)); phantom.reverse_segment_id.id != SPECIAL_SEGMENTID)
const auto index = bound == existing_bearings.end() intersection.entry_class.activate(0);
? 0 intersection.bearing_class.add(std::round(bearing));
: std::distance(existing_bearings.begin(), bound); intersection.entry_class.activate(1);
return entry_class.allowsEntry(index);
};
BOOST_ASSERT(!requires_entry || isValidEntry());
return discrete_bearing;
} }
else else
{ {
// the next larger bearing or first if we are wrapping around at zero intersection.bearing_class.add(std::round(bearing));
const auto next_index = intersection.entry_class.activate(0);
std::distance(existing_bearings.begin(), intersection.bearing_class.add(std::round(bearing + 180.));
std::lower_bound(existing_bearings.begin(), existing_bearings.end(), if (phantom.forward_segment_id.id != SPECIAL_SEGMENTID &&
discrete_bearing)) % phantom.reverse_segment_id.id != SPECIAL_SEGMENTID)
existing_bearings.size(); intersection.entry_class.activate(1);
// next smaller bearing or last if we are wrapping around at zero
const auto previous_index =
(next_index + existing_bearings.size() - 1) % existing_bearings.size();
const auto difference = [](const double first, const double second) {
return std::min(std::abs(first - second), 360.0 - std::abs(first - second));
};
const auto next_bearing = existing_bearings[next_index];
const auto previous_bearing = existing_bearings[previous_index];
const auto decideOnBearing = [&](
const std::size_t preferred_index, const double preferred_bearing,
const std::size_t alternative_index, const double alternative_bearing) {
if (!requires_entry || entry_class.allowsEntry(preferred_index))
return preferred_bearing;
else if (entry_class.allowsEntry(alternative_index))
return alternative_bearing;
else
{
SimpleLogger().Write(logDEBUG)
<< "Cannot find a valid entry for a discrete Bearing in Turn Classificiation";
return 0.;
} }
}; return intersection;
if (difference(bearing, next_bearing) < difference(bearing, previous_index))
return decideOnBearing(next_index, next_bearing, previous_index, previous_bearing);
else
return decideOnBearing(previous_index, previous_bearing, next_index, next_bearing);
} }
return 0;
}
} // namespace guidance } // namespace guidance
} // namespace util } // namespace util
} // namespace osrm } // namespace osrm

View File

@ -61,6 +61,8 @@ using EdgeWeight = int;
using BearingClassID = std::uint32_t; using BearingClassID = std::uint32_t;
static const BearingClassID INVALID_BEARING_CLASSID = std::numeric_limits<std::uint32_t>::max(); static const BearingClassID INVALID_BEARING_CLASSID = std::numeric_limits<std::uint32_t>::max();
using DiscreteBearing = std::uint16_t;
using EntryClassID = std::uint16_t; using EntryClassID = std::uint16_t;
static const EntryClassID INVALID_ENTRY_CLASSID = std::numeric_limits<std::uint16_t>::max(); static const EntryClassID INVALID_ENTRY_CLASSID = std::numeric_limits<std::uint16_t>::max();

View File

@ -37,31 +37,12 @@ const constexpr char *modifier_names[] = {"uturn", "sharp right", "right", "s
// translations of TurnTypes. Not all types are exposed to the outside world. // translations of TurnTypes. Not all types are exposed to the outside world.
// invalid types should never be returned as part of the API // invalid types should never be returned as part of the API
const constexpr char *turn_type_names[] = {"invalid", const constexpr char *turn_type_names[] = {
"new name", "invalid", "new name", "continue", "turn", "merge",
"continue", "on ramp", "off ramp", "fork", "end of road", "notification",
"turn", "roundabout", "roundabout", "rotary", "rotary", "roundabout turn",
"merge", "roundabout turn", "invalid", "invalid", "invalid", "invalid",
"on ramp", "invalid", "invalid", "invalid", "invalid", "invalid"};
"off ramp",
"fork",
"end of road",
"notification",
"roundabout",
"roundabout",
"rotary",
"rotary",
"roundabout turn",
"roundabout turn",
"invalid",
"invalid",
"invalid",
"invalid",
"invalid",
"invalid",
"invalid",
"invalid",
"invalid"};
const constexpr char *waypoint_type_names[] = {"invalid", "arrive", "depart"}; const constexpr char *waypoint_type_names[] = {"invalid", "arrive", "depart"};
@ -97,47 +78,80 @@ util::json::Array coordinateToLonLat(const util::Coordinate coordinate)
return array; return array;
} }
util::json::Object getConnection(const bool entry_allowed, const double bearing) util::json::Object
getIntersection(const guidance::Intersection &intersection, bool locate_before, bool locate_after)
{ {
util::json::Object result; util::json::Object result;
result.values["entry_allowed"] = entry_allowed ? "true" : "false"; util::json::Array bearings;
result.values["bearing"] = bearing; util::json::Array entry;
const auto &available_bearings = intersection.bearing_class.getAvailableBearings();
for (std::size_t i = 0; i < available_bearings.size(); ++i)
{
bearings.values.push_back(std::to_string(available_bearings[i]));
entry.values.push_back(intersection.entry_class.allowsEntry(i) ? "true" : "false");
}
result.values["location"] = detail::coordinateToLonLat(intersection.location);
bool requires_correction = false;
if (locate_before || (!available_bearings.empty() && available_bearings.front()==0))
{
// bearings are oriented in the direction of driving. For the in-bearing, we actually need
// to
// find the bearing from the view of the intersection. This means we have to rotate the
// bearing
// by 180 degree.
const auto rotated_bearing_before = (intersection.bearing_before >= 180.0)
? (intersection.bearing_before - 180.0)
: (intersection.bearing_before + 180.0);
result.values["bearing_before"] =
intersection.bearing_class.findMatchingBearing(rotated_bearing_before);
}
else
{
result.values["bearing_before"] = available_bearings.size();
requires_correction = true;
}
if (locate_after || (!available_bearings.empty() && available_bearings.front()==0))
{
result.values["bearing_after"] =
intersection.bearing_class.findMatchingBearing(intersection.bearing_after);
}
else
{
result.values["bearing_after"] = available_bearings.size();
requires_correction = true;
}
if (requires_correction)
{
bearings.values.push_back("0");
entry.values.push_back("false");
}
result.values["bearings"] = bearings;
result.values["entry"] = entry;
return result; return result;
} }
util::json::Array getConnections(const util::guidance::EntryClass entry_class, util::json::Array getIntersection(const guidance::RouteStep &step)
const util::guidance::BearingClass bearing_class)
{ {
util::json::Array result; util::json::Array result;
const auto bearings = bearing_class.getAvailableBearings(); bool first = true;
for (size_t connection = 0; connection < bearings.size(); ++connection) for (const auto &intersection : step.intersections)
{ {
result.values.push_back( // on waypoints, the first/second bearing is invalid. In these cases, we cannot locate the
getConnection(entry_class.allowsEntry(connection), bearings[connection])); // bearing as part of the available bearings at the intersection.
result.values.push_back(getIntersection(
intersection, !first || step.maneuver.waypoint_type != guidance::WaypointType::Depart,
!first || step.maneuver.waypoint_type != guidance::WaypointType::Arrive));
first = false;
} }
return result; return result;
} }
util::json::Object getIntersection(const guidance::StepManeuver maneuver)
{
util::json::Object result;
// bearings are oriented in the direction of driving. For the in-bearing, we actually need to
// find the bearing from the view of the intersection. This means we have to rotate the bearing
// by 180 degree.
const auto rotated_bearing_before = (maneuver.bearing_before >= 180.0)
? (maneuver.bearing_before - 180.0)
: (maneuver.bearing_before + 180.0);
result.values["from_bearing"] =
getMatchingDiscreteBearing(false, rotated_bearing_before, maneuver.entry_class,
maneuver.bearing_class.getAvailableBearings());
result.values["to_bearing"] =
getMatchingDiscreteBearing(true, maneuver.bearing_after, maneuver.entry_class,
maneuver.bearing_class.getAvailableBearings());
result.values["connections"] = getConnections(maneuver.entry_class, maneuver.bearing_class);
return result;
}
// FIXME this actually needs to be configurable from the profiles // FIXME this actually needs to be configurable from the profiles
std::string modeToString(const extractor::TravelMode mode) std::string modeToString(const extractor::TravelMode mode)
{ {
@ -193,10 +207,7 @@ util::json::Object makeStepManeuver(const guidance::StepManeuver &maneuver)
{ {
util::json::Object step_maneuver; util::json::Object step_maneuver;
if (maneuver.waypoint_type == guidance::WaypointType::None) if (maneuver.waypoint_type == guidance::WaypointType::None)
{
step_maneuver.values["type"] = detail::instructionTypeToString(maneuver.instruction.type); step_maneuver.values["type"] = detail::instructionTypeToString(maneuver.instruction.type);
step_maneuver.values["intersection"] = detail::getIntersection(maneuver);
}
else else
step_maneuver.values["type"] = detail::waypointTypeToString(maneuver.waypoint_type); step_maneuver.values["type"] = detail::waypointTypeToString(maneuver.waypoint_type);
@ -204,18 +215,9 @@ util::json::Object makeStepManeuver(const guidance::StepManeuver &maneuver)
step_maneuver.values["modifier"] = step_maneuver.values["modifier"] =
detail::instructionModifierToString(maneuver.instruction.direction_modifier); detail::instructionModifierToString(maneuver.instruction.direction_modifier);
step_maneuver.values["location"] = detail::coordinateToLonLat(maneuver.location);
step_maneuver.values["bearing_before"] = std::round(maneuver.bearing_before);
step_maneuver.values["bearing_after"] = std::round(maneuver.bearing_after);
if (maneuver.exit != 0) if (maneuver.exit != 0)
step_maneuver.values["exit"] = maneuver.exit; step_maneuver.values["exit"] = maneuver.exit;
// TODO currently we need this to comply with the api.
// We should move this to an additional entry, the moment we
// actually compute the correct locations of the intersections
if (!maneuver.intersections.empty() && maneuver.exit == 0)
step_maneuver.values["exit"] = maneuver.intersections.size();
return step_maneuver; return step_maneuver;
} }
@ -231,6 +233,7 @@ util::json::Object makeRouteStep(guidance::RouteStep step, util::json::Value geo
route_step.values["mode"] = detail::modeToString(std::move(step.mode)); route_step.values["mode"] = detail::modeToString(std::move(step.mode));
route_step.values["maneuver"] = makeStepManeuver(std::move(step.maneuver)); route_step.values["maneuver"] = makeStepManeuver(std::move(step.maneuver));
route_step.values["geometry"] = std::move(geometry); route_step.values["geometry"] = std::move(geometry);
route_step.values["intersections"] = detail::getIntersection(step);
return route_step; return route_step;
} }

View File

@ -2,6 +2,7 @@
#include <boost/assert.hpp> #include <boost/assert.hpp>
#include <cmath>
#include <cstddef> #include <cstddef>
namespace osrm namespace osrm
@ -12,48 +13,11 @@ namespace guidance
{ {
namespace detail namespace detail
{ {
namespace
StepManeuver stepManeuverFromGeometry(extractor::guidance::TurnInstruction instruction,
const WaypointType waypoint_type,
const LegGeometry &leg_geometry)
{ {
BOOST_ASSERT(waypoint_type != WaypointType::None); void fillInIntermediate(Intersection &intersection,
BOOST_ASSERT(leg_geometry.locations.size() >= 2);
double pre_turn_bearing = 0, post_turn_bearing = 0;
Coordinate turn_coordinate;
if (waypoint_type == WaypointType::Depart)
{
turn_coordinate = leg_geometry.locations.front();
const auto post_turn_coordinate = *(leg_geometry.locations.begin() + 1);
post_turn_bearing =
util::coordinate_calculation::bearing(turn_coordinate, post_turn_coordinate);
}
else
{
BOOST_ASSERT(waypoint_type == WaypointType::Arrive);
turn_coordinate = leg_geometry.locations.back();
const auto pre_turn_coordinate = *(leg_geometry.locations.end() - 2);
pre_turn_bearing =
util::coordinate_calculation::bearing(pre_turn_coordinate, turn_coordinate);
}
return StepManeuver{std::move(turn_coordinate),
pre_turn_bearing,
post_turn_bearing,
std::move(instruction),
waypoint_type,
INVALID_EXIT_NR,
// BearingClass,EntryClass, and Intermediate intersections are unknown yet
{},
{},
{}};
}
StepManeuver stepManeuverFromGeometry(extractor::guidance::TurnInstruction instruction,
const LegGeometry &leg_geometry, const LegGeometry &leg_geometry,
const std::size_t segment_index, const std::size_t segment_index)
util::guidance::EntryClass entry_class,
util::guidance::BearingClass bearing_class)
{ {
auto turn_index = leg_geometry.BackIndex(segment_index); auto turn_index = leg_geometry.BackIndex(segment_index);
BOOST_ASSERT(turn_index > 0); BOOST_ASSERT(turn_index > 0);
@ -64,15 +28,60 @@ StepManeuver stepManeuverFromGeometry(extractor::guidance::TurnInstruction instr
const auto turn_coordinate = leg_geometry.locations[turn_index]; const auto turn_coordinate = leg_geometry.locations[turn_index];
const auto post_turn_coordinate = leg_geometry.locations[turn_index + 1]; const auto post_turn_coordinate = leg_geometry.locations[turn_index + 1];
const double pre_turn_bearing = intersection.bearing_before =
util::coordinate_calculation::bearing(pre_turn_coordinate, turn_coordinate); util::coordinate_calculation::bearing(pre_turn_coordinate, turn_coordinate);
const double post_turn_bearing = intersection.bearing_after =
util::coordinate_calculation::bearing(turn_coordinate, post_turn_coordinate); util::coordinate_calculation::bearing(turn_coordinate, post_turn_coordinate);
// add a step without intermediate intersections intersection.location = turn_coordinate;
return StepManeuver{std::move(turn_coordinate), pre_turn_bearing, post_turn_bearing, }
std::move(instruction), WaypointType::None, INVALID_EXIT_NR,
std::move(entry_class), std::move(bearing_class), {}}; void fillInDepart(Intersection &intersection, const LegGeometry &leg_geometry)
{
BOOST_ASSERT(leg_geometry.locations.size() >= 2);
const auto turn_coordinate = leg_geometry.locations.front();
const auto post_turn_coordinate = *(leg_geometry.locations.begin() + 1);
intersection.location = turn_coordinate;
intersection.bearing_before = 0;
intersection.bearing_after =
util::coordinate_calculation::bearing(turn_coordinate, post_turn_coordinate);
std::cout << "Depart: " << intersection.bearing_before << " " << intersection.bearing_after << std::endl;
}
void fillInArrive(Intersection &intersection, const LegGeometry &leg_geometry)
{
BOOST_ASSERT(leg_geometry.locations.size() >= 2);
const auto turn_coordinate = leg_geometry.locations.back();
const auto pre_turn_coordinate = *(leg_geometry.locations.end() - 2);
intersection.location = turn_coordinate;
intersection.bearing_before =
util::coordinate_calculation::bearing(pre_turn_coordinate, turn_coordinate);
intersection.bearing_after = 0;
std::cout << "Arrive: " << intersection.bearing_before << " " << intersection.bearing_after << std::endl;
}
} // namespace
Intersection intersectionFromGeometry(const WaypointType waypoint_type,
const double segment_duration,
const LegGeometry &leg_geometry,
const std::size_t segment_index)
{
Intersection intersection;
intersection.duration = segment_duration;
intersection.distance = leg_geometry.segment_distances[segment_index];
switch (waypoint_type)
{
case WaypointType::None:
fillInIntermediate(intersection, leg_geometry, segment_index);
break;
case WaypointType::Depart:
fillInDepart(intersection, leg_geometry);
break;
case WaypointType::Arrive:
fillInArrive(intersection, leg_geometry);
break;
}
return intersection;
} }
} // ns detail } // ns detail
} // ns engine } // ns engine

View File

@ -50,14 +50,16 @@ void print(const std::vector<RouteStep> &steps)
<< " Duration: " << step.duration << " Distance: " << step.distance << " Duration: " << step.duration << " Distance: " << step.distance
<< " Geometry: " << step.geometry_begin << " " << step.geometry_end << " Geometry: " << step.geometry_begin << " " << step.geometry_end
<< " exit: " << step.maneuver.exit << " exit: " << step.maneuver.exit
<< " Intersections: " << step.maneuver.intersections.size() << " ["; << " Intersections: " << step.intersections.size() << " [";
for (const auto &intersection : step.maneuver.intersections) for (const auto &intersection : step.intersections)
std::cout << "(" << intersection.duration << " " << intersection.distance << ")"; {
std::cout << "(" << intersection.duration << " " << intersection.distance << " "
<< " Bearings: " << intersection.bearing_before << " "
<< intersection.bearing_after << ")";
}
std::cout << "] name[" << step.name_id << "]: " << step.name std::cout << "] name[" << step.name_id << "]: " << step.name << std::endl;
<< " Bearings: " << step.maneuver.bearing_before << " "
<< step.maneuver.bearing_after << std::endl;
} }
} }
@ -67,8 +69,21 @@ RouteStep forwardInto(RouteStep destination, const RouteStep &source)
// Overwrites turn instruction and increases exit NR // Overwrites turn instruction and increases exit NR
destination.duration += source.duration; destination.duration += source.duration;
destination.distance += source.distance; destination.distance += source.distance;
if (destination.geometry_begin < source.geometry_begin)
{
destination.intersections.insert(destination.intersections.end(),
source.intersections.begin(), source.intersections.end());
}
else
{
destination.intersections.insert(destination.intersections.begin(),
source.intersections.begin(), source.intersections.end());
}
destination.geometry_begin = std::min(destination.geometry_begin, source.geometry_begin); destination.geometry_begin = std::min(destination.geometry_begin, source.geometry_begin);
destination.geometry_end = std::max(destination.geometry_end, source.geometry_end); destination.geometry_end = std::max(destination.geometry_end, source.geometry_end);
return destination; return destination;
} }
@ -185,7 +200,8 @@ void closeOffRoundabout(const bool on_roundabout,
// Normal exit from the roundabout, or exit from a previously fixed roundabout. Propagate the // Normal exit from the roundabout, or exit from a previously fixed roundabout. Propagate the
// index back to the entering location and prepare the current silent set of instructions for // index back to the entering location and prepare the current silent set of instructions for
// removal. // removal.
const auto exit_bearing = steps[step_index].maneuver.bearing_after; std::vector<std::size_t> intermediate_steps;
const auto exit_bearing = steps[step_index].intersections.back().bearing_after;
if (step_index > 1) if (step_index > 1)
{ {
// The very first route-step is head, so we cannot iterate past that one // The very first route-step is head, so we cannot iterate past that one
@ -198,6 +214,7 @@ void closeOffRoundabout(const bool on_roundabout,
{ {
propagation_step.maneuver.exit = step.maneuver.exit; propagation_step.maneuver.exit = step.maneuver.exit;
propagation_step.geometry_end = step.geometry_end; propagation_step.geometry_end = step.geometry_end;
// remember rotary name // remember rotary name
if (propagation_step.maneuver.instruction.type == TurnType::EnterRotary || if (propagation_step.maneuver.instruction.type == TurnType::EnterRotary ||
propagation_step.maneuver.instruction.type == TurnType::EnterRotaryAtExit) propagation_step.maneuver.instruction.type == TurnType::EnterRotaryAtExit)
@ -237,7 +254,7 @@ void closeOffRoundabout(const bool on_roundabout,
const auto angle = 540 - rotated_exit; const auto angle = 540 - rotated_exit;
return angle > 360 ? angle - 360 : angle; return angle > 360 ? angle - 360 : angle;
}(propagation_step.maneuver.bearing_before, exit_bearing); }(propagation_step.intersections.back().bearing_before, exit_bearing);
propagation_step.maneuver.instruction.direction_modifier = propagation_step.maneuver.instruction.direction_modifier =
::osrm::util::guidance::getTurnDirection(angle); ::osrm::util::guidance::getTurnDirection(angle);
@ -274,9 +291,8 @@ RouteStep elongate(RouteStep step, const RouteStep &by_step)
// if we elongate in the back, we only need to copy the intersections to the beginning. // if we elongate in the back, we only need to copy the intersections to the beginning.
// the bearings remain the same, as the location of the turn doesn't change // the bearings remain the same, as the location of the turn doesn't change
step.maneuver.intersections.insert(step.maneuver.intersections.end(), step.intersections.insert(step.intersections.end(), by_step.intersections.begin(),
by_step.maneuver.intersections.begin(), by_step.intersections.end());
by_step.maneuver.intersections.end());
} }
else else
{ {
@ -286,14 +302,10 @@ RouteStep elongate(RouteStep step, const RouteStep &by_step)
step.geometry_begin = by_step.geometry_begin; step.geometry_begin = by_step.geometry_begin;
// elongating in the front changes the location of the maneuver // elongating in the front changes the location of the maneuver
step.maneuver.location = by_step.maneuver.location; step.maneuver = by_step.maneuver;
step.maneuver.bearing_before = by_step.maneuver.bearing_before;
step.maneuver.bearing_after = by_step.maneuver.bearing_after;
step.maneuver.instruction = by_step.maneuver.instruction;
step.maneuver.intersections.insert(step.maneuver.intersections.begin(), step.intersections.insert(step.intersections.begin(), by_step.intersections.begin(),
by_step.maneuver.intersections.begin(), by_step.intersections.end());
by_step.maneuver.intersections.end());
} }
return step; return step;
} }
@ -359,8 +371,8 @@ void collapseTurnAt(std::vector<RouteStep> &steps,
} }
} }
// Potential U-Turn // Potential U-Turn
else if (bearingsAreReversed(one_back_step.maneuver.bearing_before, else if (bearingsAreReversed(one_back_step.intersections.front().bearing_before,
current_step.maneuver.bearing_after)) current_step.intersections.front().bearing_after))
{ {
BOOST_ASSERT(two_back_index < steps.size()); BOOST_ASSERT(two_back_index < steps.size());
@ -438,18 +450,6 @@ std::vector<RouteStep> postProcess(std::vector<RouteStep> steps)
bool on_roundabout = false; bool on_roundabout = false;
bool has_entered_roundabout = false; bool has_entered_roundabout = false;
// adds an intersection to the initial route step
// It includes the length of the last step, until the intersection
// Also updates the length of the respective segment
auto addIntersection = [](RouteStep into, const RouteStep &last_step,
const RouteStep &intersection) {
into.maneuver.intersections.push_back(
{last_step.duration, last_step.distance, intersection.maneuver.location,
intersection.maneuver.entry_class, intersection.maneuver.bearing_class});
return forwardInto(std::move(into), intersection);
};
// count the exits forward. if enter/exit roundabout happen both, no further treatment is // count the exits forward. if enter/exit roundabout happen both, no further treatment is
// required. We might end up with only one of them (e.g. starting within a roundabout) // required. We might end up with only one of them (e.g. starting within a roundabout)
// or having a via-point in the roundabout. // or having a via-point in the roundabout.
@ -492,8 +492,9 @@ std::vector<RouteStep> postProcess(std::vector<RouteStep> steps)
{ {
// count intersections. We cannot use exit, since intersections can follow directly // count intersections. We cannot use exit, since intersections can follow directly
// after a roundabout // after a roundabout
steps[last_valid_instruction] = addIntersection( steps[last_valid_instruction].intersections.insert(
std::move(steps[last_valid_instruction]), steps[step_index - 1], step); steps[last_valid_instruction].intersections.end(), step.intersections.begin(),
step.intersections.end());
step.maneuver.instruction = TurnInstruction::NO_TURN(); step.maneuver.instruction = TurnInstruction::NO_TURN();
} }
else if (!isSilent(instruction)) else if (!isSilent(instruction))
@ -664,8 +665,7 @@ void trimShortSegments(std::vector<RouteStep> &steps, LegGeometry &geometry)
// update initial turn direction/bearings. Due to the duplicated first coordinate, // update initial turn direction/bearings. Due to the duplicated first coordinate,
// the initial bearing is invalid // the initial bearing is invalid
designated_depart.maneuver = detail::stepManeuverFromGeometry( designated_depart.maneuver = {TurnInstruction::NO_TURN(), WaypointType::Depart, 0};
TurnInstruction::NO_TURN(), WaypointType::Depart, geometry);
// finally remove the initial (now duplicated move) // finally remove the initial (now duplicated move)
steps.erase(steps.begin()); steps.erase(steps.begin());
@ -678,8 +678,8 @@ void trimShortSegments(std::vector<RouteStep> &steps, LegGeometry &geometry)
geometry.segment_offsets.begin(), geometry.segment_offsets.begin(),
[](const std::size_t val) { return val - 1; }); [](const std::size_t val) { return val - 1; });
steps.front().maneuver = detail::stepManeuverFromGeometry( steps.front().maneuver = {TurnInstruction::NO_TURN(), WaypointType::Depart, 0};
TurnInstruction::NO_TURN(), WaypointType::Depart, geometry); std::cout << "Removed coordinate: " << std::endl;
} }
// and update the leg geometry indices for the removed entry // and update the leg geometry indices for the removed entry
@ -705,8 +705,8 @@ void trimShortSegments(std::vector<RouteStep> &steps, LegGeometry &geometry)
BOOST_ASSERT(geometry.segment_distances.back() < 1); BOOST_ASSERT(geometry.segment_distances.back() < 1);
geometry.segment_distances.pop_back(); geometry.segment_distances.pop_back();
next_to_last_step.maneuver = detail::stepManeuverFromGeometry( next_to_last_step.maneuver = {TurnInstruction::NO_TURN(), WaypointType::Arrive, 0};
TurnInstruction::NO_TURN(), WaypointType::Arrive, geometry); next_to_last_step.intersections.front().bearing_after = 0;
steps.pop_back(); steps.pop_back();
// Because we eliminated a really short segment, it was probably // Because we eliminated a really short segment, it was probably
@ -736,8 +736,7 @@ void trimShortSegments(std::vector<RouteStep> &steps, LegGeometry &geometry)
next_to_last_step.geometry_end--; next_to_last_step.geometry_end--;
steps.back().geometry_begin--; steps.back().geometry_begin--;
steps.back().geometry_end--; steps.back().geometry_end--;
steps.back().maneuver = detail::stepManeuverFromGeometry(TurnInstruction::NO_TURN(), steps.back().maneuver = {TurnInstruction::NO_TURN(), WaypointType::Arrive, 0};
WaypointType::Arrive, geometry);
} }
} }
@ -762,6 +761,11 @@ std::vector<RouteStep> assignRelativeLocations(std::vector<RouteStep> steps,
: extractor::guidance::DirectionModifier::UTurn; : extractor::guidance::DirectionModifier::UTurn;
steps.front().maneuver.instruction.direction_modifier = initial_modifier; steps.front().maneuver.instruction.direction_modifier = initial_modifier;
steps.front().intersections.front().bearing_before = 0;
steps.front().intersections.front().bearing_after =
util::coordinate_calculation::bearing(leg_geometry.locations[0], leg_geometry.locations[1]);
steps.front().intersections.front() = util::guidance::setIntersectionClasses(
std::move(steps.front().intersections.front()), source_node);
const auto distance_from_end = util::coordinate_calculation::haversineDistance( const auto distance_from_end = util::coordinate_calculation::haversineDistance(
target_node.input_location, leg_geometry.locations.back()); target_node.input_location, leg_geometry.locations.back());
@ -775,6 +779,14 @@ std::vector<RouteStep> assignRelativeLocations(std::vector<RouteStep> steps,
: extractor::guidance::DirectionModifier::UTurn; : extractor::guidance::DirectionModifier::UTurn;
steps.back().maneuver.instruction.direction_modifier = final_modifier; steps.back().maneuver.instruction.direction_modifier = final_modifier;
BOOST_ASSERT(steps.back().intersections.size() == 1);
steps.back().intersections.front().bearing_before = util::coordinate_calculation::bearing(
leg_geometry.locations[leg_geometry.locations.size() - 2],
leg_geometry.locations[leg_geometry.locations.size() - 1]);
steps.back().intersections.front().bearing_after = 0;
steps.back().intersections.front() = util::guidance::setIntersectionClasses(
std::move(steps.back().intersections.front()), target_node);
return steps; return steps;
} }

View File

@ -14,6 +14,7 @@
#include "util/lua_util.hpp" #include "util/lua_util.hpp"
#include "util/make_unique.hpp" #include "util/make_unique.hpp"
#include "util/name_table.hpp" #include "util/name_table.hpp"
#include "util/range_table.hpp"
#include "util/simple_logger.hpp" #include "util/simple_logger.hpp"
#include "util/timing_util.hpp" #include "util/timing_util.hpp"
@ -624,10 +625,24 @@ void Extractor::WriteIntersectionClassificationData(
util::writeFingerprint(file_out_stream); util::writeFingerprint(file_out_stream);
util::serializeVector(file_out_stream, node_based_intersection_classes); util::serializeVector(file_out_stream, node_based_intersection_classes);
static_assert(std::is_trivially_copyable<util::guidance::BearingClass>::value, // create range table for vectors:
"BearingClass Serialization requires trivial copyable bearing classes"); std::vector<unsigned> bearing_counts;
bearing_counts.reserve(bearing_classes.size());
std::uint64_t total_bearings = 0;
for (const auto &bearing_class : bearing_classes){
bearing_counts.push_back(static_cast<unsigned>(bearing_class.getAvailableBearings().size()));
total_bearings += bearing_class.getAvailableBearings().size();
}
util::serializeVector(file_out_stream, bearing_classes); util::RangeTable<> bearing_class_range_table(bearing_counts);
file_out_stream << bearing_class_range_table;
file_out_stream << total_bearings;
for( const auto &bearing_class : bearing_classes)
{
const auto &bearings = bearing_class.getAvailableBearings();
file_out_stream.write( reinterpret_cast<const char*>(&bearings[0]), sizeof(bearings[0]) * bearings.size() );
}
static_assert(std::is_trivially_copyable<util::guidance::EntryClass>::value, static_assert(std::is_trivially_copyable<util::guidance::EntryClass>::value,
"EntryClass Serialization requires trivial copyable entry classes"); "EntryClass Serialization requires trivial copyable entry classes");

View File

@ -15,234 +15,17 @@ namespace guidance
struct TurnPossibility struct TurnPossibility
{ {
TurnPossibility(bool entry_allowed, double bearing, std::uint8_t discrete_id) TurnPossibility(bool entry_allowed, double bearing)
: entry_allowed(entry_allowed), bearing(std::move(bearing)), : entry_allowed(entry_allowed), bearing(std::move(bearing))
discrete_id(std::move(discrete_id))
{ {
} }
TurnPossibility() : entry_allowed(false), bearing(0), discrete_id(0) {} TurnPossibility() : entry_allowed(false), bearing(0) {}
bool entry_allowed; bool entry_allowed;
double bearing; double bearing;
std::uint8_t discrete_id;
}; };
namespace
{
bool hasConflicts(const std::vector<TurnPossibility> &turns)
{
if (turns.size() <= 1)
return false;
for (std::size_t pos = 0; pos < turns.size(); ++pos)
{
if (turns[pos].discrete_id == turns[(pos + 1) % turns.size()].discrete_id)
return true;
}
return false;
}
// Fix cases with nearly identical turns. If the difference between turns is just to small to be
// visible, we will not report the angle twice
std::vector<TurnPossibility> fixIdenticalTurns(std::vector<TurnPossibility> intersection)
{
BOOST_ASSERT(intersection.size() > 1);
for (auto itr = intersection.begin(); itr != intersection.end(); ++itr)
{
const auto next = [&]() {
auto next_itr = std::next(itr);
if (next_itr != intersection.end())
return next_itr;
return intersection.begin();
}();
// conflict here?
if (itr->discrete_id == next->discrete_id)
{
if (angularDeviation(itr->bearing, next->bearing) < 0.5) // very small angular difference
{
if (!itr->entry_allowed || next->entry_allowed)
{
itr = intersection.erase(itr);
}
else
{
intersection.erase(next);
}
if (itr == intersection.end())
break;
}
}
}
return intersection;
}
std::vector<TurnPossibility> fixAroundBorder(std::vector<TurnPossibility> intersection)
{
BOOST_ASSERT(intersection.size() > 1);
// We can solve a conflict by reporting different bearing availabilities, as long as
// both conflicting turns are on different sides of the bearing separator.
//
// Consider this example:
// ID(0)
// . b
// a .... (ID 1)
// . c
// ID(2)
//
// Both b and c map to the ID 1. Due to the split, we can set ID 0 and 2. In
// deducing the best available bearing for a turn, we can now find 0 to be closest
// to b and 2 to be closest to c. This only works when there are no other bearings
// close to the conflicting assignment, though.
for (std::size_t current_index = 0; current_index < intersection.size(); ++current_index)
{
const auto next_index = (current_index + 1) % intersection.size();
if (intersection[current_index].discrete_id == intersection[next_index].discrete_id)
{
const double border = util::guidance::BearingClass::discreteIDToAngle(
util::guidance::BearingClass::angleToDiscreteID(
intersection[current_index].bearing));
// if both are on different sides of the separation, we can check for possible
// resolution
if (intersection[current_index].bearing < border &&
intersection[next_index].bearing > border)
{
const auto shift_angle = [](const double bearing, const double delta) {
auto shifted_angle = bearing + delta;
if (shifted_angle < 0)
return shifted_angle + 360.;
if (shifted_angle > 360)
return shifted_angle - 360.;
return shifted_angle;
};
// conflict resolution is possible, if both bearings are available
const auto left_id = util::guidance::BearingClass::angleToDiscreteID(
shift_angle(intersection[current_index].bearing,
-util::guidance::BearingClass::discrete_angle_step_size));
const auto right_id = util::guidance::BearingClass::angleToDiscreteID(
shift_angle(intersection[next_index].bearing,
util::guidance::BearingClass::discrete_angle_step_size));
const bool resolvable = [&]() {
if (intersection.size() == 2)
return true;
// cannot shift to the left without generating another conflict
if (intersection[current_index + intersection.size() - 1].discrete_id ==
left_id)
return false;
// cannot shift to the right without generating another conflict
if (intersection[(next_index + 1) % intersection.size()].discrete_id ==
right_id)
return false;
return true;
}();
if (resolvable)
{
intersection[current_index].discrete_id = left_id;
intersection[next_index].discrete_id = right_id;
}
}
}
}
return intersection;
}
// return an empty set of turns, if the conflict is not possible to be handled
std::vector<TurnPossibility> handleConflicts(std::vector<TurnPossibility> intersection)
{
intersection = fixIdenticalTurns(std::move(intersection));
if (!hasConflicts(intersection))
return intersection;
intersection = fixAroundBorder(intersection);
// if the intersection still has conflicts, we cannot handle it correctly
if (hasConflicts(intersection))
intersection.clear();
return intersection;
#if 0
const auto border = util::guidance::BearingClass::discreteIDToAngle(
util::guidance::BearingClass::angleToDiscreteID(intersection[0].bearing));
// at least two turns
auto previous_id = util::guidance::BearingClass::angleToDiscreteID(turns.back().bearing);
for (std::size_t i = 0; i < turns.size(); ++i)
{
if (turns[i].entry_allowed)
entry_class.activate(turn_index);
const auto discrete_id =
util::guidance::BearingClass::angleToDiscreteID(turns[i].bearing);
const auto prev_index = (i + turns.size() - 1) % turns.size();
if (discrete_id != previous_id)
{
// if we go back in IDs, conflict resolution has to deal with multiple conflicts
if (previous_id > discrete_id &&
previous_id != (util::guidance::BearingClass::angleToDiscreteID(
turns[prev_index].bearing)))
{
std::cout << "Previous ID conflict " << (int)previous_id << " "
<< (int)discrete_id << std::endl;
break;
}
++turn_index;
bearing_class.addDiscreteID(discrete_id);
previous_id = discrete_id;
}
else
{
// the previous turn was handled into a conflict. Such a conflict cannot be
// correctly expressed.
// We have to report a unclassified setting.
if (util::guidance::BearingClass::angleToDiscreteID(turns[prev_index].bearing) !=
previous_id)
break;
if (turns[i].bearing >= border && turns[prev_index].bearing < border)
{
const auto shift_angle = [](const double bearing, const double delta) {
auto shifted_angle = bearing + delta;
if (shifted_angle < 0)
return shifted_angle + 360.;
if (shifted_angle > 360)
return shifted_angle - 360.;
return shifted_angle;
};
// conflict resolution is possible, if both bearings are available
const auto left_id = util::guidance::BearingClass::angleToDiscreteID(
shift_angle(turns[prev_index].bearing,
-util::guidance::BearingClass::discrete_angle_step_size));
const auto right_id = util::guidance::BearingClass::angleToDiscreteID(
shift_angle(turns[i].bearing,
util::guidance::BearingClass::discrete_angle_step_size));
if (!bearing_class.hasDiscrete(left_id) && !bearing_class.hasDiscrete(right_id))
{
bearing_class.resetDiscreteID(discrete_id);
bearing_class.addDiscreteID(left_id);
bearing_class.addDiscreteID(right_id);
++turn_index;
previous_id = right_id;
}
}
}
}
#endif
return intersection;
};
} // namespace
std::pair<util::guidance::EntryClass, util::guidance::BearingClass> std::pair<util::guidance::EntryClass, util::guidance::BearingClass>
classifyIntersection(NodeID nid, classifyIntersection(NodeID nid,
const Intersection &intersection, const Intersection &intersection,
@ -266,63 +49,63 @@ classifyIntersection(NodeID nid,
const double bearing = const double bearing =
util::coordinate_calculation::bearing(node_coordinate, edge_coordinate); util::coordinate_calculation::bearing(node_coordinate, edge_coordinate);
turns.push_back({road.entry_allowed, bearing, turns.push_back({road.entry_allowed, bearing});
util::guidance::BearingClass::angleToDiscreteID(bearing)});
} }
std::sort(turns.begin(), turns.end(), std::sort(turns.begin(), turns.end(),
[](const TurnPossibility left, const TurnPossibility right) { [](const TurnPossibility left, const TurnPossibility right) {
return left.bearing < right.bearing; return util::guidance::BearingClass::getDiscreteBearing(left.bearing) <
util::guidance::BearingClass::getDiscreteBearing(right.bearing) ||
(util::guidance::BearingClass::getDiscreteBearing(left.bearing) ==
util::guidance::BearingClass::getDiscreteBearing(right.bearing) &&
left.bearing < right.bearing);
}); });
// check for conflicts
const bool has_conflicts = hasConflicts(turns);
if (has_conflicts)
{ // try to handle conflicts, if possible
turns = handleConflicts(std::move(turns));
}
util::guidance::EntryClass entry_class; util::guidance::EntryClass entry_class;
util::guidance::BearingClass bearing_class; util::guidance::BearingClass bearing_class;
const bool canBeDiscretized = [&]() {
if (turns.size() <= 1)
return true;
DiscreteBearing last_discrete_bearing =
util::guidance::BearingClass::getDiscreteBearing(std::round(turns.back().bearing));
for (const auto turn : turns)
{
const DiscreteBearing discrete_bearing =
util::guidance::BearingClass::getDiscreteBearing(std::round(turn.bearing));
if (discrete_bearing == last_discrete_bearing)
return false;
last_discrete_bearing = discrete_bearing;
}
return true;
}();
// finally transfer data to the entry/bearing classes // finally transfer data to the entry/bearing classes
std::size_t number = 0; std::size_t number = 0;
if (canBeDiscretized)
{
for (const auto turn : turns) for (const auto turn : turns)
{ {
if (turn.entry_allowed) if (turn.entry_allowed)
entry_class.activate(number); entry_class.activate(number);
bearing_class.addDiscreteID(turn.discrete_id); auto discrete_bearing_class =
util::guidance::BearingClass::getDiscreteBearing(std::round(turn.bearing));
bearing_class.add(std::round(discrete_bearing_class *
util::guidance::BearingClass::discrete_step_size));
++number; ++number;
} }
static std::size_t mapping_failure_count = 0;
if (turns.empty())
{
++mapping_failure_count;
util::SimpleLogger().Write(logDEBUG)
<< "Failed to provide full turn list for intersection ( " << mapping_failure_count
<< " ) for " << intersection.size() << " roads";
std::cout << std::endl;
for (const auto &road : intersection)
{
const auto eid = road.turn.eid;
const auto edge_coordinate =
getRepresentativeCoordinate(nid, node_based_graph.GetTarget(eid), eid, false,
compressed_geometries, query_nodes);
const double bearing =
util::coordinate_calculation::bearing(node_coordinate, edge_coordinate);
std::cout << " " << bearing << "("
<< (int)util::guidance::BearingClass::angleToDiscreteID(bearing) << ")";
} }
std::cout << std::endl; else
std::cout << "Location of intersection: " << std::setprecision(12) << " " {
<< util::toFloating(query_nodes[nid].lat) << " " for (const auto turn : turns)
<< util::toFloating(query_nodes[nid].lon) << std::endl; {
return {}; if (turn.entry_allowed)
entry_class.activate(number);
bearing_class.add(std::round(turn.bearing));
++number;
}
} }
return std::make_pair(entry_class, bearing_class); return std::make_pair(entry_class, bearing_class);
} }

View File

@ -31,6 +31,7 @@
#include <cstdint> #include <cstdint>
#include <fstream> #include <fstream>
#include <iostream>
#include <iterator> #include <iterator>
#include <new> #include <new>
#include <string> #include <string>
@ -574,36 +575,75 @@ int Storage::Run()
{ {
boost::filesystem::ifstream intersection_stream(config.intersection_class_path, boost::filesystem::ifstream intersection_stream(config.intersection_class_path,
std::ios::binary); std::ios::binary);
if (!static_cast<bool>(intersection_stream))
throw util::exception("Could not open " + config.intersection_class_path.string() +
" for reading.");
if (!util::readAndCheckFingerprint(intersection_stream)) if (!util::readAndCheckFingerprint(intersection_stream))
{ throw util::exception("Fingerprint of " + config.intersection_class_path.string() +
util::SimpleLogger().Write(logWARNING) " does not match or could not read from file");
<< "Fingerprint does not match or reading failed";
}
std::vector<BearingClassID> bearing_class_id_table; std::vector<BearingClassID> bearing_class_id_table;
util::deserializeVector(intersection_stream, bearing_class_id_table); if (!util::deserializeVector(intersection_stream, bearing_class_id_table))
throw util::exception("Failed to read from " + config.names_data_path.string());
shared_layout_ptr->SetBlockSize<BearingClassID>(SharedDataLayout::BEARING_CLASSID, shared_layout_ptr->SetBlockSize<BearingClassID>(SharedDataLayout::BEARING_CLASSID,
bearing_class_id_table.size()); bearing_class_id_table.size());
auto bearing_id_ptr = shared_layout_ptr->GetBlockPtr<BearingClassID, true>( auto bearing_id_ptr = shared_layout_ptr->GetBlockPtr<BearingClassID, true>(
shared_memory_ptr, SharedDataLayout::BEARING_CLASSID); shared_memory_ptr, SharedDataLayout::BEARING_CLASSID);
std::copy(bearing_class_id_table.begin(), bearing_class_id_table.end(), bearing_id_ptr); std::copy(bearing_class_id_table.begin(), bearing_class_id_table.end(), bearing_id_ptr);
auto bearing_class_ptr = unsigned bearing_blocks = 0;
shared_layout_ptr->GetBlockPtr<util::guidance::BearingClass, true>( intersection_stream.read((char *)&bearing_blocks, sizeof(unsigned));
shared_memory_ptr, SharedDataLayout::BEARING_CLASS); unsigned sum_lengths = 0;
std::vector<util::guidance::BearingClass> bearing_class_table; intersection_stream.read((char *)&sum_lengths, sizeof(unsigned));
util::deserializeVector(intersection_stream, bearing_class_table);
shared_layout_ptr->SetBlockSize<util::guidance::BearingClass>( shared_layout_ptr->SetBlockSize<unsigned>(SharedDataLayout::BEARING_OFFSETS,
SharedDataLayout::BEARING_CLASS, bearing_class_table.size()); bearing_blocks);
shared_layout_ptr->SetBlockSize<typename util::RangeTable<16, true>::BlockT>(
SharedDataLayout::BEARING_BLOCKS, bearing_blocks);
unsigned *bearing_offsets_ptr = shared_layout_ptr->GetBlockPtr<unsigned, true>(
shared_memory_ptr, SharedDataLayout::BEARING_OFFSETS);
if (shared_layout_ptr->GetBlockSize(SharedDataLayout::BEARING_OFFSETS) > 0)
{
intersection_stream.read(
reinterpret_cast<char *>(bearing_offsets_ptr),
shared_layout_ptr->GetBlockSize(SharedDataLayout::BEARING_OFFSETS));
}
unsigned *bearing_blocks_ptr = shared_layout_ptr->GetBlockPtr<unsigned, true>(
shared_memory_ptr, SharedDataLayout::BEARING_BLOCKS);
if (shared_layout_ptr->GetBlockSize(SharedDataLayout::BEARING_BLOCKS) > 0)
{
intersection_stream.read(
reinterpret_cast<char *>(bearing_blocks_ptr),
shared_layout_ptr->GetBlockSize(SharedDataLayout::BEARING_BLOCKS));
}
std::uint64_t num_bearings;
intersection_stream >> num_bearings;
std::vector<DiscreteBearing> bearing_class_table(num_bearings);
intersection_stream.read(reinterpret_cast<char *>(&bearing_class_table[0]),
sizeof(bearing_class_table[0]) * num_bearings);
shared_layout_ptr->SetBlockSize<DiscreteBearing>(SharedDataLayout::BEARING_VALUES,
num_bearings);
auto bearing_class_ptr = shared_layout_ptr->GetBlockPtr<DiscreteBearing, true>(
shared_memory_ptr, SharedDataLayout::BEARING_VALUES);
std::copy(bearing_class_table.begin(), bearing_class_table.end(), bearing_class_ptr); std::copy(bearing_class_table.begin(), bearing_class_table.end(), bearing_class_ptr);
auto entry_class_ptr = if (!static_cast<bool>(intersection_stream))
shared_layout_ptr->GetBlockPtr<util::guidance::EntryClass, true>( throw util::exception("Failed to read from " + config.names_data_path.string());
shared_memory_ptr, SharedDataLayout::ENTRY_CLASS);
std::vector<util::guidance::EntryClass> entry_class_table; std::vector<util::guidance::EntryClass> entry_class_table;
util::deserializeVector(intersection_stream, entry_class_table); if(!util::deserializeVector(intersection_stream, entry_class_table))
shared_layout_ptr->SetBlockSize<util::guidance::EntryClass>( throw util::exception("Failed to read from " + config.names_data_path.string());
SharedDataLayout::ENTRY_CLASS, entry_class_table.size());
shared_layout_ptr->SetBlockSize<util::guidance::EntryClass>(SharedDataLayout::ENTRY_CLASS,
entry_class_table.size());
auto entry_class_ptr = shared_layout_ptr->GetBlockPtr<util::guidance::EntryClass, true>(
shared_memory_ptr, SharedDataLayout::ENTRY_CLASS);
std::copy(entry_class_table.begin(), entry_class_table.end(), entry_class_ptr); std::copy(entry_class_table.begin(), entry_class_table.end(), entry_class_ptr);
} }

View File

@ -1,6 +1,8 @@
#include "extractor/guidance/discrete_angle.hpp" #include "extractor/guidance/discrete_angle.hpp"
#include "util/guidance/bearing_class.hpp" #include "util/guidance/bearing_class.hpp"
#include "util/guidance/toolkit.hpp"
#include <algorithm>
#include <boost/assert.hpp> #include <boost/assert.hpp>
namespace osrm namespace osrm
@ -10,83 +12,69 @@ namespace util
namespace guidance namespace guidance
{ {
static_assert(
360 / BearingClass::discrete_angle_step_size <= 8 * sizeof(BearingClass::FlagBaseType),
"The number of expressable bearings does not fit into the datatype used for storage.");
std::uint8_t BearingClass::angleToDiscreteID(double angle)
{
BOOST_ASSERT(angle >= 0. && angle <= 360.);
// shift angle by half the step size to have the class be located around the center
angle = (angle + 0.5 * BearingClass::discrete_angle_step_size);
if (angle > 360)
angle -= 360;
return std::uint8_t(angle / BearingClass::discrete_angle_step_size);
}
double BearingClass::discreteIDToAngle(std::uint8_t id)
{
BOOST_ASSERT(0 <= id && id <= 360. / discrete_angle_step_size);
return discrete_angle_step_size * id;
}
void BearingClass::resetContinuous(const double bearing) {
const auto id = angleToDiscreteID(bearing);
resetDiscreteID(id);
}
void BearingClass::resetDiscreteID(const std::uint8_t id) {
available_bearings_mask &= ~(1<<id);
}
bool BearingClass::hasContinuous(const double bearing) const
{
const auto id = angleToDiscreteID(bearing);
return hasDiscrete(id);
}
bool BearingClass::hasDiscrete(const std::uint8_t id) const
{
return 0 != (available_bearings_mask & (1<<id));
}
BearingClass::BearingClass() : available_bearings_mask(0) {}
bool BearingClass::operator==(const BearingClass &other) const bool BearingClass::operator==(const BearingClass &other) const
{ {
return other.available_bearings_mask == available_bearings_mask; BOOST_ASSERT(std::is_sorted(available_bearings.begin(), available_bearings.end()));
BOOST_ASSERT(std::is_sorted(other.available_bearings.begin(), other.available_bearings.end()));
if (other.available_bearings.size() != available_bearings.size())
return false;
for (std::size_t i = 0; i < available_bearings.size(); ++i)
if (available_bearings[i] != other.available_bearings[i])
return false;
return true;
} }
bool BearingClass::operator<(const BearingClass &other) const bool BearingClass::operator<(const BearingClass &other) const
{ {
return available_bearings_mask < other.available_bearings_mask; BOOST_ASSERT(std::is_sorted(available_bearings.begin(), available_bearings.end()));
BOOST_ASSERT(std::is_sorted(other.available_bearings.begin(), other.available_bearings.end()));
if (available_bearings.size() < other.available_bearings.size())
return true;
if (available_bearings.size() > other.available_bearings.size())
return false;
for (std::size_t i = 0; i < available_bearings.size(); ++i)
{
if (available_bearings[i] < other.available_bearings[i])
return true;
if (available_bearings[i] > other.available_bearings[i])
return false;
} }
bool BearingClass::addContinuous(const double angle) return false;
{
return addDiscreteID(angleToDiscreteID(angle));
} }
bool BearingClass::addDiscreteID(const std::uint8_t discrete_id) void BearingClass::add(const DiscreteBearing bearing)
{ {
const auto mask = (1 << discrete_id); available_bearings.push_back(bearing);
const auto is_new = (0 == (available_bearings_mask & mask));
available_bearings_mask |= mask;
return is_new;
} }
std::vector<double> BearingClass::getAvailableBearings() const const std::vector<DiscreteBearing> &BearingClass::getAvailableBearings() const
{ {
std::vector<double> result; return available_bearings;
// account for some basic inaccuracries of double
for (std::size_t discrete_id = 0; discrete_id * discrete_angle_step_size <= 361; ++discrete_id)
{
// ervery set bit indicates a bearing
if (available_bearings_mask & (1 << discrete_id))
result.push_back(discrete_id * discrete_angle_step_size);
} }
return result;
DiscreteBearing BearingClass::getDiscreteBearing(const double bearing)
{
BOOST_ASSERT(0. <= bearing && bearing <= 360.);
auto shifted_bearing = (bearing + 0.5 * discrete_step_size);
if (shifted_bearing > 360.)
shifted_bearing -= 360;
return static_cast<DiscreteBearing>(shifted_bearing / discrete_step_size);
}
std::size_t BearingClass::findMatchingBearing(const double bearing) const
{
// the small size of the intersections allows a linear compare
auto discrete_bearing = static_cast<DiscreteBearing>(bearing);
auto max_element =
std::max_element(available_bearings.begin(), available_bearings.end(),
[&](const DiscreteBearing first, const DiscreteBearing second) {
return angularDeviation(first, discrete_bearing) >
angularDeviation(second, discrete_bearing);
});
return std::distance(available_bearings.begin(), max_element);
} }
} // namespace guidance } // namespace guidance