diff --git a/descriptors/json_descriptor.hpp b/descriptors/json_descriptor.hpp index 13b68c6ec..4904e8527 100644 --- a/descriptors/json_descriptor.hpp +++ b/descriptors/json_descriptor.hpp @@ -52,15 +52,8 @@ template class JSONDescriptor final : public BaseDescriptor< DescriptorConfig config; DescriptionFactory description_factory, alternate_description_factory; FixedPointCoordinate current; - unsigned entered_restricted_area_count; - struct RoundAbout - { - RoundAbout() : start_index(INT_MAX), name_id(INVALID_NAMEID), leave_at_exit(INT_MAX) {} - int start_index; - unsigned name_id; - int leave_at_exit; - } round_about; + public: struct Segment { Segment() : name_id(INVALID_NAMEID), length(-1), position(0) {} @@ -69,11 +62,12 @@ template class JSONDescriptor final : public BaseDescriptor< int length; unsigned position; }; + private: std::vector shortest_path_segments, alternative_path_segments; ExtractRouteNames GenerateRouteNames; public: - explicit JSONDescriptor(DataFacadeT *facade) : facade(facade), entered_restricted_area_count(0) + explicit JSONDescriptor(DataFacadeT *facade) : facade(facade) { } @@ -143,9 +137,7 @@ template class JSONDescriptor final : public BaseDescriptor< } if (config.instructions) { - osrm::json::Array json_route_instructions; - BuildTextualDescription(description_factory, json_route_instructions, - raw_route.shortest_path_length, shortest_path_segments); + osrm::json::Array json_route_instructions = BuildTextualDescription(description_factory, shortest_path_segments); json_result.values["route_instructions"] = json_route_instructions; } description_factory.BuildRouteSummary(description_factory.get_entire_length(), @@ -222,9 +214,7 @@ template class JSONDescriptor final : public BaseDescriptor< osrm::json::Array json_current_alt_instructions; if (config.instructions) { - BuildTextualDescription( - alternate_description_factory, json_current_alt_instructions, - raw_route.alternative_path_length, alternative_path_segments); + json_alt_instructions = BuildTextualDescription(alternate_description_factory, alternative_path_segments); json_alt_instructions.values.push_back(json_current_alt_instructions); json_result.values["alternative_instructions"] = json_alt_instructions; } @@ -276,6 +266,11 @@ template class JSONDescriptor final : public BaseDescriptor< json_result.values["alternative_names"] = json_alternate_names_array; } + json_result.values["hint_data"] = BuildHintData(raw_route); + } + + inline osrm::json::Object BuildHintData(const InternalRouteResult& raw_route) const + { osrm::json::Object json_hint_object; json_hint_object.values["checksum"] = facade->GetCheckSum(); osrm::json::Array json_location_hint_array; @@ -290,24 +285,27 @@ template class JSONDescriptor final : public BaseDescriptor< hint); json_location_hint_array.values.push_back(hint); json_hint_object.values["locations"] = json_location_hint_array; - json_result.values["hint_data"] = json_hint_object; - // render the content to the output array - // TIMER_START(route_render); - // osrm::json::render(reply.content, json_result); - // TIMER_STOP(route_render); - // SimpleLogger().Write(logDEBUG) << "rendering took: " << TIMER_MSEC(route_render); + return json_hint_object; } - // TODO: reorder parameters - inline void BuildTextualDescription(DescriptionFactory &description_factory, - osrm::json::Array &json_instruction_array, - const int route_length, - std::vector &route_segments_list) + inline osrm::json::Array BuildTextualDescription(const DescriptionFactory &description_factory, + std::vector &route_segments_list) const { + osrm::json::Array json_instruction_array; + // Segment information has following format: //["instruction id","streetname",length,position,time,"length","earth_direction",azimuth] unsigned necessary_segments_running_index = 0; + + struct RoundAbout + { + RoundAbout() : start_index(INT_MAX), name_id(INVALID_NAMEID), leave_at_exit(INT_MAX) {} + int start_index; + unsigned name_id; + int leave_at_exit; + } round_about; + round_about.leave_at_exit = 0; round_about.name_id = 0; std::string temp_dist, temp_length, temp_duration, temp_bearing, temp_instruction; @@ -317,7 +315,6 @@ template class JSONDescriptor final : public BaseDescriptor< { osrm::json::Array json_instruction_row; TurnInstruction current_instruction = segment.turn_instruction; - entered_restricted_area_count += (current_instruction != segment.turn_instruction); if (TurnInstructionsClass::TurnIsNecessary(current_instruction)) { if (TurnInstruction::EnterRoundAbout == current_instruction) @@ -386,6 +383,8 @@ template class JSONDescriptor final : public BaseDescriptor< json_last_instruction_row.values.push_back(bearing::get(0.0)); json_last_instruction_row.values.push_back(0.); json_instruction_array.values.push_back(json_last_instruction_row); + + return json_instruction_array; } }; diff --git a/features/step_definitions/matching.rb b/features/step_definitions/matching.rb index 607efedd9..3015f5a43 100644 --- a/features/step_definitions/matching.rb +++ b/features/step_definitions/matching.rb @@ -58,9 +58,6 @@ When /^I match I should get$/ do |table| end end - puts table - puts sub_matchings - ok = true encoded_result = "" extended_target = "" @@ -97,3 +94,170 @@ When /^I match I should get$/ do |table| table.diff! actual end +When /^I match with turns I should get$/ do |table| + reprocess + actual = [] + OSRMLoader.load(self,"#{prepared_file}.osrm") do + table.hashes.each_with_index do |row,ri| + if row['request'] + got = {'request' => row['request'] } + response = request_url row['request'] + else + params = @query_params + trace = [] + timestamps = [] + if row['from'] and row['to'] + node = find_node_by_name(row['from']) + raise "*** unknown from-node '#{row['from']}" unless node + trace << node + + node = find_node_by_name(row['to']) + raise "*** unknown to-node '#{row['to']}" unless node + trace << node + + got = {'from' => row['from'], 'to' => row['to'] } + response = request_matching trace, timestamps, params + elsif row['trace'] + row['trace'].each_char do |n| + node = find_node_by_name(n.strip) + raise "*** unknown waypoint node '#{n.strip}" unless node + trace << node + end + if row['timestamps'] + timestamps = row['timestamps'].split(" ").compact.map { |t| t.to_i} + end + got = {'trace' => row['trace'] } + response = request_matching trace, timestamps, params + else + raise "*** no trace" + end + end + + row.each_pair do |k,v| + if k =~ /param:(.*)/ + if v=='(nil)' + params[$1]=nil + elsif v!=nil + params[$1]=v + end + got[k]=v + end + end + + if response.body.empty? == false + json = JSON.parse response.body + end + if response.body.empty? == false + if response.code == "200" + instructions = way_list json['matchings'][0]['instructions'] + bearings = bearing_list json['matchings'][0]['instructions'] + compasses = compass_list json['matchings'][0]['instructions'] + turns = turn_list json['matchings'][0]['instructions'] + modes = mode_list json['matchings'][0]['instructions'] + times = time_list json['matchings'][0]['instructions'] + distances = distance_list json['matchings'][0]['instructions'] + end + end + + if table.headers.include? 'status' + got['status'] = json['status'].to_s + end + if table.headers.include? 'message' + got['message'] = json['status_message'] + end + if table.headers.include? '#' # comment column + got['#'] = row['#'] # copy value so it always match + end + + sub_matchings = [] + if response.code == "200" + if table.headers.include? 'matchings' + sub_matchings = json['matchings'].compact.map { |sub| sub['matched_points']} + + got['route'] = (instructions || '').strip + if table.headers.include?('distance') + if row['distance']!='' + raise "*** Distance must be specied in meters. (ex: 250m)" unless row['distance'] =~ /\d+m/ + end + got['distance'] = instructions ? "#{json['route_summary']['total_distance'].to_s}m" : '' + end + if table.headers.include?('time') + raise "*** Time must be specied in seconds. (ex: 60s)" unless row['time'] =~ /\d+s/ + got['time'] = instructions ? "#{json['route_summary']['total_time'].to_s}s" : '' + end + if table.headers.include?('speed') + if row['speed'] != '' && instructions + raise "*** Speed must be specied in km/h. (ex: 50 km/h)" unless row['speed'] =~ /\d+ km\/h/ + time = json['route_summary']['total_time'] + distance = json['route_summary']['total_distance'] + speed = time>0 ? (3.6*distance/time).to_i : nil + got['speed'] = "#{speed} km/h" + else + got['speed'] = '' + end + end + if table.headers.include? 'bearing' + got['bearing'] = instructions ? bearings : '' + end + if table.headers.include? 'compass' + got['compass'] = instructions ? compasses : '' + end + if table.headers.include? 'turns' + got['turns'] = instructions ? turns : '' + end + if table.headers.include? 'modes' + got['modes'] = instructions ? modes : '' + end + if table.headers.include? 'times' + got['times'] = instructions ? times : '' + end + if table.headers.include? 'distances' + got['distances'] = instructions ? distances : '' + end + end + if table.headers.include? 'start' + got['start'] = instructions ? json['route_summary']['start_point'] : nil + end + if table.headers.include? 'end' + got['end'] = instructions ? json['route_summary']['end_point'] : nil + end + if table.headers.include? 'geometry' + got['geometry'] = json['route_geometry'] + end + end + + ok = true + encoded_result = "" + extended_target = "" + row['matchings'].split(',').each_with_index do |sub, sub_idx| + if sub_idx >= sub_matchings.length + ok = false + break + end + sub.length.times do |node_idx| + node = find_node_by_name(sub[node_idx]) + out_node = sub_matchings[sub_idx][node_idx] + if FuzzyMatch.match_location out_node, node + encoded_result += sub[node_idx] + extended_target += sub[node_idx] + else + encoded_result += "? [#{out_node[0]},#{out_node[1]}]" + extended_target += "#{sub[node_idx]} [#{node.lat},#{node.lon}]" + ok = false + end + end + end + if ok + got['matchings'] = row['matchings'] + got['timestamps'] = row['timestamps'] + else + got['matchings'] = encoded_result + row['matchings'] = extended_target + log_fail row,got, { 'matching' => {:query => @query, :response => response} } + end + + actual << got + end + end + table.diff! actual +end diff --git a/features/support/match.rb b/features/support/match.rb index 8bcf7a2c6..165dfe003 100644 --- a/features/support/match.rb +++ b/features/support/match.rb @@ -3,7 +3,7 @@ require 'net/http' HOST = "http://127.0.0.1:#{OSRM_PORT}" def request_matching trace=[], timestamps=[], options={} - defaults = { 'output' => 'json' } + defaults = { 'output' => 'json', 'instructions' => 'true' } locs = trace.compact.map { |w| "loc=#{w.lat},#{w.lon}" } ts = timestamps.compact.map { |t| "t=#{t}" } if ts.length > 0 diff --git a/features/testbot/matching_turns.feature b/features/testbot/matching_turns.feature new file mode 100644 index 000000000..2d0a55820 --- /dev/null +++ b/features/testbot/matching_turns.feature @@ -0,0 +1,82 @@ +@routing @turns @testbot +Feature: Turn directions/codes + + Background: + Given the profile "testbot" + + Scenario: Turn directions + Given the node map + | o | p | a | b | c | + | n | | | | d | + | m | | x | | e | + | l | | | | f | + | k | j | i | h | g | + + And the ways + | nodes | + | xa | + | xb | + | xc | + | xd | + | xe | + | xf | + | xg | + | xh | + | xi | + | xj | + | xk | + | xl | + | xm | + | xn | + | xo | + | xp | + + When I match with turns I should get + | trace | route | turns | matchings | + | im | xi,xm | head,left,destination | im | + | io | xi,xo | head,slight_left,destination | io | + | ia | xi,xa | head,straight,destination | ia | + | ic | xi,xc | head,slight_right,destination | ic | + | ie | xi,xe | head,right,destination | ie | + + | ko | xk,xo | head,left,destination | ko | + | ka | xk,xa | head,slight_left,destination | ka | + | kc | xk,xc | head,straight,destination | kc | + | ke | xk,xe | head,slight_right,destination | ke | + | kg | xk,xg | head,right,destination | kg | + + | ma | xm,xa | head,left,destination | ma | + | mc | xm,xc | head,slight_left,destination | mc | + | me | xm,xe | head,straight,destination | me | + | mg | xm,xg | head,slight_right,destination | mg | + | mi | xm,xi | head,right,destination | mi | + + | oc | xo,xc | head,left,destination | oc | + | oe | xo,xe | head,slight_left,destination | oe | + | og | xo,xg | head,straight,destination | og | + | oi | xo,xi | head,slight_right,destination | oi | + | ok | xo,xk | head,right,destination | ok | + + | ae | xa,xe | head,left,destination | ae | + | ag | xa,xg | head,slight_left,destination | ag | + | ai | xa,xi | head,straight,destination | ai | + | ak | xa,xk | head,slight_right,destination | ak | + | am | xa,xm | head,right,destination | am | + + | cg | xc,xg | head,left,destination | cg | + | ci | xc,xi | head,slight_left,destination | ci | + | ck | xc,xk | head,straight,destination | ck | + | cm | xc,xm | head,slight_right,destination | cm | + | co | xc,xo | head,right,destination | co | + + | ei | xe,xi | head,left,destination | ei | + | ek | xe,xk | head,slight_left,destination | ek | + | em | xe,xm | head,straight,destination | em | + | eo | xe,xo | head,slight_right,destination | eo | + | ea | xe,xa | head,right,destination | ea | + + | gk | xg,xk | head,left,destination | gk | + | gm | xg,xm | head,slight_left,destination | gm | + | go | xg,xo | head,straight,destination | go | + | ga | xg,xa | head,slight_right,destination | ga | + | gc | xg,xc | head,right,destination | gc | \ No newline at end of file diff --git a/plugins/match.hpp b/plugins/match.hpp index 120fba1ef..33ade953d 100644 --- a/plugins/match.hpp +++ b/plugins/match.hpp @@ -187,7 +187,12 @@ template class MapMatchingPlugin : public BasePlugin subtrace.values["confidence"] = sub.confidence; } - if (route_parameters.geometry) + JSONDescriptor json_descriptor(facade); + json_descriptor.SetConfig(route_parameters); + + subtrace.values["hint_data"] = json_descriptor.BuildHintData(raw_route); + + if (route_parameters.geometry || route_parameters.print_instructions) { DescriptionFactory factory; FixedPointCoordinate current_coordinate; @@ -205,13 +210,22 @@ template class MapMatchingPlugin : public BasePlugin raw_route.target_traversed_in_reverse[i], raw_route.is_via_leg(i)); } - // we need this because we don't run DP - for (auto &segment : factory.path_description) + + // we run this to get the instructions + factory.Run(route_parameters.zoom_level); + + if (route_parameters.geometry) { - segment.necessary = true; + subtrace.values["geometry"] = factory.AppendGeometryString(route_parameters.compression); } - subtrace.values["geometry"] = - factory.AppendGeometryString(route_parameters.compression); + + + if (route_parameters.print_instructions) + { + std::vector::Segment> temp_segments; + subtrace.values["instructions"] = json_descriptor.BuildTextualDescription(factory, temp_segments); + } + } subtrace.values["indices"] = osrm::json::make_array(sub.indices);