parent
							
								
									84b35f47a1
								
							
						
					
					
						commit
						b210b22975
					
				
							
								
								
									
										6
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -70,4 +70,8 @@ win/*.suo | ||||
| win/Debug/ | ||||
| win/Release/ | ||||
| win/bin/ | ||||
| win/bin-debug/ | ||||
| win/bin-debug/ | ||||
| /osrm-extract | ||||
| /osrm-routed | ||||
| /osrm-prepare | ||||
| /nohup.out | ||||
|  | ||||
							
								
								
									
										6
									
								
								Gemfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								Gemfile
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,6 @@ | ||||
| source "http://rubygems.org" | ||||
| 
 | ||||
| gem "cucumber" | ||||
| gem "rake" | ||||
| gem "osmlib-base" | ||||
| gem "sys-proctable" | ||||
							
								
								
									
										27
									
								
								Gemfile.lock
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								Gemfile.lock
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,27 @@ | ||||
| GEM | ||||
|   remote: http://rubygems.org/ | ||||
|   specs: | ||||
|     builder (3.0.0) | ||||
|     cucumber (1.1.4) | ||||
|       builder (>= 2.1.2) | ||||
|       diff-lcs (>= 1.1.2) | ||||
|       gherkin (~> 2.7.1) | ||||
|       json (>= 1.4.6) | ||||
|       term-ansicolor (>= 1.0.6) | ||||
|     diff-lcs (1.1.3) | ||||
|     gherkin (2.7.6) | ||||
|       json (>= 1.4.6) | ||||
|     json (1.6.5) | ||||
|     osmlib-base (0.1.4) | ||||
|     rake (0.9.2.2) | ||||
|     sys-proctable (0.9.1) | ||||
|     term-ansicolor (1.0.7) | ||||
| 
 | ||||
| PLATFORMS | ||||
|   ruby | ||||
| 
 | ||||
| DEPENDENCIES | ||||
|   cucumber | ||||
|   osmlib-base | ||||
|   rake | ||||
|   sys-proctable | ||||
							
								
								
									
										181
									
								
								Rakefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										181
									
								
								Rakefile
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,181 @@ | ||||
| require 'OSM/StreamParser' | ||||
| require 'socket' | ||||
| require 'digest/sha1' | ||||
| require 'cucumber/rake/task' | ||||
| require 'sys/proctable' | ||||
| 
 | ||||
| SANDBOX = 'sandbox' | ||||
| DATA_FOLDER = 'osm_data' | ||||
| 
 | ||||
| Cucumber::Rake::Task.new do |t| | ||||
|   t.cucumber_opts = %w{--format pretty} | ||||
| end | ||||
| 
 | ||||
| areas = { | ||||
|   :kbh => { :country => 'denmark', :bbox => 'top=55.6972 left=12.5222 right=12.624 bottom=55.6376' }, | ||||
|   :frd => { :country => 'denmark', :bbox => 'top=55.7007 left=12.4765 bottom=55.6576 right=12.5698' }, | ||||
|   :regh => { :country => 'denmark', :bbox => 'top=56.164 left=11.792 bottom=55.403 right=12.731' }, | ||||
|   :dk => { :country => 'denmark', :bbox => nil }, | ||||
|   :skaane => { :counry => 'sweden', :bbox => 'top=56.55 left=12.4 bottom=55.3 right=14.6' } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| osm_data_area_name = ARGV[1] ? ARGV[1].to_s.to_sym : :kbh | ||||
| raise "Unknown data area." unless areas[osm_data_area_name] | ||||
| osm_data_country = areas[osm_data_area_name][:country] | ||||
| osm_data_area_bbox = areas[osm_data_area_name][:bbox] | ||||
| 
 | ||||
| 
 | ||||
| task osm_data_area_name.to_sym {}   #define empty task to prevent rake from whining. will break if area has same name as a task | ||||
| 
 | ||||
| 
 | ||||
| def each_process name, &block | ||||
|   Sys::ProcTable.ps do |process| | ||||
|     if process.comm.strip == name.strip | ||||
|       yield process.pid.to_i, process.state.strip | ||||
|     end | ||||
|   end | ||||
| end | ||||
| 
 | ||||
| def up? | ||||
|   find_pid('osrm-routed') != nil | ||||
| end | ||||
| 
 | ||||
| def find_pid name | ||||
|   each_process(name) { |pid,state| return pid.to_i } | ||||
|   return nil | ||||
| end | ||||
| 
 | ||||
| def wait_for_shutdown name | ||||
|   timeout = 10 | ||||
|   (timeout*10).times do | ||||
|     return if find_pid(name) == nil | ||||
|     sleep 0.1 | ||||
|   end | ||||
|   raise "*** Could not terminate #{name}." | ||||
| end | ||||
| 
 | ||||
| def write_server_ini osm_file | ||||
|   s=<<-EOF | ||||
|   Threads = 1 | ||||
|   IP = 0.0.0.0 | ||||
|   Port = 5000 | ||||
| 
 | ||||
|   hsgrData=#{DATA_FOLDER}/#{osm_file}.osrm.hsgr | ||||
|   nodesData=#{DATA_FOLDER}/#{osm_file}.osrm.nodes | ||||
|   ramIndex=#{DATA_FOLDER}/#{osm_file}.osrm.ramIndex | ||||
|   fileIndex=#{DATA_FOLDER}/#{osm_file}.osrm.fileIndex | ||||
|   namesData=#{DATA_FOLDER}/#{osm_file}.osrm.names | ||||
|   EOF | ||||
|   File.open( 'server.ini', 'w') {|f| f.write( s ) } | ||||
| end | ||||
| 
 | ||||
| 
 | ||||
| desc "Rebuild and run tests." | ||||
| task :default => [:build, :cucumber] | ||||
| 
 | ||||
| desc "Build using SConsstruct." | ||||
| task :build do | ||||
|   system "scons" | ||||
| end | ||||
| 
 | ||||
| desc "Setup config files." | ||||
| task :setup do | ||||
|   Dir.mkdir "#{SANDBOX}/#{DATA_FOLDER}" unless File.exist? "#{SANDBOX}/#{DATA_FOLDER}" | ||||
|   ['server.ini','speedprofile.ini','extractor.ini','contractor.ini'].each do |file| | ||||
|     unless File.exist? "#{SANDBOX}/#{file}" | ||||
|       puts "Copying #{file} template to sandbox/#{file}"  | ||||
|       FileUtils.cp file, "#{SANDBOX}/#{file}" | ||||
|     end | ||||
|   end | ||||
| end | ||||
| 
 | ||||
| desc "Download OSM data." | ||||
| task :download => :setup do | ||||
|   puts "Downloading..." | ||||
|   raise "Error while downloading data." unless system "curl http://download.geofabrik.de/osm/europe/#{osm_data_country}.osm.pbf -o #{SANDBOX}/#{DATA_FOLDER}/#{osm_data_country}.osm.pbf" | ||||
|   if osm_data_area_bbox | ||||
|     puts "Cropping and converting to protobuffer..." | ||||
|     raise "Error while cropping data." unless system "osmosis --read-pbf file=#{SANDBOX}/#{DATA_FOLDER}/#{osm_data_country}.osm.pbf --bounding-box #{osm_data_area_bbox} --write-pbf file=#{SANDBOX}/#{DATA_FOLDER}/#{osm_data_area_name}.osm.pbf omitmetadata=true" | ||||
|   end | ||||
| end | ||||
| 
 | ||||
| desc "Crop OSM data" | ||||
| task :crop do | ||||
|   if osm_data_area_bbox | ||||
|     raise "Error while cropping data." unless system "osmosis --read-pbf file=#{SANDBOX}/#{DATA_FOLDER}/#{osm_data_country}.osm.pbf --bounding-box #{osm_data_area_bbox} --write-pbf file=#{SANDBOX}/#{DATA_FOLDER}/#{osm_data_area_name}.osm.pbf omitmetadata=true" | ||||
|   end | ||||
| end | ||||
| 
 | ||||
| desc "Reprocess OSM data." | ||||
| task :process => :setup do | ||||
|   Dir.chdir SANDBOX do | ||||
|     raise "Error while extracting data." unless system "../osrm-extract #{DATA_FOLDER}/#{osm_data_area_name}.osm.pbf" | ||||
|     puts | ||||
|     raise "Error while preparing data." unless system "../osrm-prepare #{DATA_FOLDER}/#{osm_data_area_name}.osrm #{DATA_FOLDER}/#{osm_data_area_name}.osrm.restrictions" | ||||
|     puts | ||||
|   end | ||||
| end | ||||
| 
 | ||||
| desc "Delete preprocessing files." | ||||
| task :clean do | ||||
|   File.delete *Dir.glob("#{SANDBOX}/#{DATA_FOLDER}/*.osrm") | ||||
|   File.delete *Dir.glob("#{SANDBOX}/#{DATA_FOLDER}/*.osrm.*") | ||||
| end | ||||
| 
 | ||||
| desc "Run all cucumber test" | ||||
| task :test do | ||||
|   system "cucumber" | ||||
|   puts | ||||
| end | ||||
| 
 | ||||
| desc "Run the routing server in the terminal. Press Ctrl-C to stop." | ||||
| task :run => :setup do | ||||
|   Dir.chdir SANDBOX do | ||||
|     write_server_ini osm_data_area_name | ||||
|     system "../osrm-routed" | ||||
|   end | ||||
| end | ||||
| 
 | ||||
| desc "Launch the routing server in the background. Use rake:down to stop it." | ||||
| task :up => :setup do | ||||
|   Dir.chdir SANDBOX do | ||||
|     abort("Already up.") if up? | ||||
|     write_server_ini osm_data_area_name | ||||
|     pipe = IO.popen('../osrm-routed 1>>osrm-routed.log 2>>osrm-routed.log') | ||||
|     timeout = 5 | ||||
|     (timeout*10).times do | ||||
|       begin | ||||
|         socket = TCPSocket.new('localhost', 5000) | ||||
|         socket.puts 'ping' | ||||
|       rescue Errno::ECONNREFUSED | ||||
|         sleep 0.1 | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| end | ||||
| 
 | ||||
| desc "Stop the routing server." | ||||
| task :down do | ||||
|   pid = find_pid 'osrm-routed' | ||||
|   abort("Already down.") unless pid | ||||
|   Process.kill 'TERM', pid | ||||
| end | ||||
| 
 | ||||
| desc "Kill all osrm-extract, osrm-prepare and osrm-routed processes." | ||||
| task :kill do | ||||
|   each_process('osrm-routed') { |pid,state| Process.kill 'KILL', pid } | ||||
|   each_process('osrm-prepare') { |pid,state| Process.kill 'KILL', pid } | ||||
|   each_process('osrm-extract') { |pid,state| Process.kill 'KILL', pid } | ||||
|   wait_for_shutdown 'osrm-routed' | ||||
|   wait_for_shutdown 'osrm-prepare' | ||||
|   wait_for_shutdown 'osrm-extract'   | ||||
| end | ||||
| 
 | ||||
| desc "Get PIDs of all osrm-extract, osrm-prepare and osrm-routed processes." | ||||
| task :pid do | ||||
|   each_process 'osrm-routed' do |pid,state| | ||||
|     puts "#{pid}\t#{state}" | ||||
|   end | ||||
| end | ||||
							
								
								
									
										55
									
								
								features/access.feature
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								features/access.feature
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,55 @@ | ||||
| @routing @access | ||||
| Feature: Oneway streets | ||||
| 	Basic accessability of various way types. | ||||
| 
 | ||||
| 	Scenario: Basic access for cars | ||||
| 	 	Given the speedprofile "car" | ||||
| 	 	Then routability should be | ||||
| 		 | highway       | forw | | ||||
| 		 | motorway      | x    | | ||||
| 		 | motorway_link | x    | | ||||
| 		 | trunk         | x    | | ||||
| 		 | trunk_link    | x    | | ||||
| 		 | primary       | x    | | ||||
| 		 | secondary     | x    | | ||||
| 		 | tertiary      | x    | | ||||
| 		 | residential   | x    | | ||||
| 		 | service       | x    | | ||||
| 		 | unclassified  | x    | | ||||
| 		 | living_street | x    | | ||||
| 		 | road		     | x    | | ||||
| 		 | track         |      | | ||||
| 		 | path          |      | | ||||
| 		 | footway       |      | | ||||
| 		 | pedestrian    |      | | ||||
| 		 | steps         |      | | ||||
| 		 | pier          |      | | ||||
| 		 | cycleway      |      | | ||||
| 		 | bridleway     |      | | ||||
| 
 | ||||
| 	Scenario: Basic access for bicycles | ||||
| 	Bikes are allowed on footways etc because you can pull your bike at a lower speed. | ||||
| 	 	Given the speedprofile "bicycle" | ||||
| 	 	Then routability should be | ||||
| 		 | highway       | forw | | ||||
| 		 | motorway      |      | | ||||
| 		 | motorway_link |      | | ||||
| 		 | trunk         |      | | ||||
| 		 | trunk_link    |      | | ||||
| 		 | primary       | x    | | ||||
| 		 | secondary     | x    | | ||||
| 		 | tertiary      | x    | | ||||
| 		 | residential   | x    | | ||||
| 		 | service       | x    | | ||||
| 		 | unclassified  | x    | | ||||
| 		 | living_street | x    | | ||||
| 		 | road		     | x    | | ||||
| 		 | track         | x    | | ||||
| 		 | path          | x    | | ||||
| 		 | footway       | x    | | ||||
| 		 | pedestrian    | x    | | ||||
| 		 | steps         | x    | | ||||
| 		 | pier          | x    | | ||||
| 		 | cycleway      | x    | | ||||
| 		 | bridleway     |      | | ||||
| 
 | ||||
							
								
								
									
										55
									
								
								features/bad.feature
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								features/bad.feature
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,55 @@ | ||||
| @routing @bad | ||||
| Feature: Handle bad data in a graceful manner | ||||
| 	 | ||||
| 	Scenario: Empty dataset | ||||
| 		Given the nodes | ||||
| 		 | a | b | | ||||
| 
 | ||||
| 		Given the ways | ||||
| 		 | nodes | | ||||
| 		 | ||||
| 		When I route I should get | ||||
| 		 | from | to | route | | ||||
| 		 | a    | b  |       | | ||||
| 
 | ||||
| 	Scenario: Start/end point at the same location | ||||
| 		Given the nodes | ||||
| 		 | a | b | | ||||
| 		 | 1 | 2 | | ||||
| 
 | ||||
| 		Given the ways | ||||
| 		 | nodes | | ||||
| 		 | ab    | | ||||
| 
 | ||||
| 		When I route I should get | ||||
| 		 | from | to | route | | ||||
| 		 | a    | a  |       | | ||||
| 		 | b    | b  |       | | ||||
| 		 | 1    | 1  |       | | ||||
| 		 | 2    | 2  |       | | ||||
| 
 | ||||
| 	Scenario: Start/end point far outside data area | ||||
| 		Given the nodes | ||||
| 		 |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |    | 1 | | ||||
| 		 | a | b |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |    | 2 | | ||||
| 		 |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |    | 3 | | ||||
| 
 | ||||
| 		Given the ways | ||||
| 		 | nodes | | ||||
| 		 | ab    | | ||||
| 
 | ||||
| 		When I route I should get | ||||
| 		 | from | to | route | | ||||
| 		 | 1    | a  | ab    | | ||||
| 		 | 2    | a  | ab    | | ||||
| 		 | 3    | a  | ab    | | ||||
| 		 | 1    | b  |       | | ||||
| 		 | 2    | b  |       | | ||||
| 		 | 3    | b  |       | | ||||
| 		 | 1    | 2  |       | | ||||
| 		 | 1    | 3  |       | | ||||
| 		 | 2    | 1  |       | | ||||
| 		 | 2    | 3  |       | | ||||
| 		 | 3    | 1  |       | | ||||
| 		 | 3    | 2  |       | | ||||
| 
 | ||||
							
								
								
									
										136
									
								
								features/basic.feature
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								features/basic.feature
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,136 @@ | ||||
| @routing @basic | ||||
| Feature: Basic Routing | ||||
| 	 | ||||
| 	@smallest | ||||
| 	Scenario: A single way with two nodes | ||||
| 		Given the nodes | ||||
| 		 | a | b | | ||||
| 	 | ||||
| 		And the ways | ||||
| 		 | nodes | | ||||
| 		 | ab    | | ||||
|      | ||||
| 		When I route I should get | ||||
| 		 | from | to | route | | ||||
| 		 | a    | b  | ab    | | ||||
| 		 | b    | a  | ab    | | ||||
| 		 | ||||
| 	Scenario: Routing in between two nodes of way | ||||
| 		Given the nodes | ||||
| 		 | a | b | 1 | 2 | c | d | | ||||
| 		 | ||||
| 		And the ways | ||||
| 		 | nodes | | ||||
| 		 | abcd  | | ||||
| 
 | ||||
| 		When I route I should get | ||||
| 		 | from | to | route | | ||||
| 		 | 1    | 2  | abcd  | | ||||
| 		 | 2    | 1  | abcd  | | ||||
| 
 | ||||
| 	Scenario: Routing between the middle nodes of way | ||||
| 		Given the nodes | ||||
| 		 | a | b | c | d | e | f | | ||||
| 
 | ||||
| 		And the ways | ||||
| 		 | nodes  | | ||||
| 		 | abcdef | | ||||
| 
 | ||||
| 		When I route I should get | ||||
| 		 | from | to | route  | | ||||
| 		 | b    | c  | abcdef | | ||||
| 		 | b    | d  | abcdef | | ||||
| 		 | b    | e  | abcdef | | ||||
| 		 | c    | b  | abcdef | | ||||
| 		 | c    | d  | abcdef | | ||||
| 		 | c    | e  | abcdef | | ||||
| 		 | d    | b  | abcdef | | ||||
| 		 | d    | c  | abcdef | | ||||
| 		 | d    | e  | abcdef | | ||||
| 		 | e    | b  | abcdef | | ||||
| 		 | e    | c  | abcdef | | ||||
| 		 | e    | d  | abcdef | | ||||
| 
 | ||||
| 	Scenario: Two ways connected in a straight line | ||||
| 		Given the nodes | ||||
| 		 | a | b | c | | ||||
| 	 | ||||
| 		And the ways | ||||
| 		 | nodes | | ||||
| 		 | ab    | | ||||
| 		 | bc    | | ||||
|      | ||||
| 		When I route I should get | ||||
| 		 | from | to | route | | ||||
| 		 | a    | c  | ab,bc | | ||||
| 		 | c    | a  | bc,ab | | ||||
| 		 | a    | b  | ab    | | ||||
| 		 | b    | a  | ab    | | ||||
| 		 | b    | c  | bc    | | ||||
| 		 | c    | b  | bc    | | ||||
| 		 | ||||
| 	Scenario: 2 unconnected parallel ways | ||||
| 		Given the nodes | ||||
| 		 | a | b | | ||||
| 		 | c | d | | ||||
| 		 | ||||
| 		And the ways | ||||
| 		 | nodes | | ||||
| 		 | ab    | | ||||
| 		 | cd    | | ||||
| 	     | ||||
| 		When I route I should get | ||||
| 		 | from | to | route | | ||||
| 		 | a    | b  | ab    | | ||||
| 		 | b    | a  | ab    | | ||||
| 		 | c    | d  | cd    | | ||||
| 		 | d    | c  | cd    | | ||||
| 		 | a    | c  |       | | ||||
| 		 | c    | a  |       | | ||||
| 		 | b    | d  |       | | ||||
| 		 | d    | b  |       | | ||||
| 		 | a    | d  |       | | ||||
| 		 | d    | a  |       | | ||||
| 
 | ||||
| 	Scenario: 3 ways connected in a triangle | ||||
| 		Given the nodes | ||||
| 		 | a |   | b | | ||||
| 		 |   |   |   | | ||||
| 		 |   | c |   | | ||||
| 
 | ||||
| 		And the ways | ||||
| 		 | nodes | | ||||
| 		 | ab    | | ||||
| 		 | bc    | | ||||
| 		 | ca    | | ||||
| 		 | ||||
| 		When I route I should get | ||||
| 		 | from | to | route | | ||||
| 		 | a    | b  | ab    | | ||||
| 		 | a    | c  | ca    | | ||||
| 		 | b    | c  | bc    | | ||||
| 		 | b    | a  | ab    | | ||||
| 		 | c    | a  | ca    | | ||||
| 		 | c    | b  | bc    | | ||||
| 
 | ||||
| 	Scenario: To ways connected at a 45 degree angle | ||||
| 		Given the nodes | ||||
| 		 | a |   |   | | ||||
| 		 | b |   |   | | ||||
| 		 | c | d | e | | ||||
| 
 | ||||
| 		And the ways | ||||
| 		 | nodes | | ||||
| 		 | abc   | | ||||
| 		 | cde   | | ||||
| 
 | ||||
| 		When I route I should get | ||||
| 		 | from | to | route   | | ||||
| 		 | b    | d  | abc,cde | | ||||
| 		 | a    | e  | abc,cde | | ||||
| 		 | a    | c  | abc     | | ||||
| 		 | c    | a  | abc     | | ||||
| 		 | c    | e  | cde     | | ||||
| 		 | e    | c  | cde     | | ||||
| 
 | ||||
| 
 | ||||
							
								
								
									
										27
									
								
								features/ferry.feature
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								features/ferry.feature
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,27 @@ | ||||
| @routing @ferry | ||||
| Feature: Handle ferry routes | ||||
| 	 | ||||
| 	Scenario: Use a ferry route | ||||
| 		Given the nodes | ||||
| 		 | a | b | c |   |   | | ||||
| 		 |   |   | d |   |   | | ||||
| 		 |   |   | e | f | g | | ||||
| 	 | ||||
| 		And the ways | ||||
| 		 | nodes | highway | route | bicycle | | ||||
| 		 | abc   | primary |       |         | | ||||
| 		 | cde   |         | ferry | yes     | | ||||
| 		 | efg   | primary |       |         | | ||||
|     | ||||
| 		When I route I should get | ||||
| 		 | from | to | route       | | ||||
| 		 | a    | g  | abc,cde,efg | | ||||
| 		 | b    | f  | abc,cde,efg | | ||||
| 		 | e    | c  | cde         | | ||||
| 		 | e    | b  | cde,abc     | | ||||
| 		 | e    | a  | cde,abc     | | ||||
| 		 | c    | e  | cde         | | ||||
| 		 | c    | f  | cde,efg     | | ||||
| 		 | c    | g  | cde,efg     | | ||||
| 
 | ||||
| 
 | ||||
							
								
								
									
										166
									
								
								features/oneway.feature
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										166
									
								
								features/oneway.feature
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,166 @@ | ||||
| @routing @oneway | ||||
| Feature: Oneway streets | ||||
| 	Handle oneways streets, as defined at http://wiki.openstreetmap.org/wiki/OSM_tags_for_routing | ||||
| 	 | ||||
| 	Scenario: Simple oneway | ||||
| 		Given the speedprofile "car" | ||||
| 		Then routability should be | ||||
| 		 | highway | oneway | forw | backw | | ||||
| 		 | primary | yes    | x    |       | | ||||
| 
 | ||||
| 	Scenario: Simple reverse oneway | ||||
| 		Given the speedprofile "car" | ||||
| 		Then routability should be | ||||
| 		 | highway | oneway | forw | backw | | ||||
| 		 | primary | -1     |      | x     | | ||||
| 
 | ||||
| 	Scenario: Around the Block | ||||
| 		Given the nodes | ||||
| 		 | a | b | | ||||
| 		 | d | c |		 | ||||
| 	 | ||||
| 		And the ways | ||||
| 		 | nodes | oneway | | ||||
| 		 | ab    | yes    | | ||||
| 		 | bc    |        | | ||||
| 		 | cd    |        | | ||||
| 		 | da    |        | | ||||
|      | ||||
| 		When I route I should get | ||||
| 		 | from | to | route    | | ||||
| 		 | a    | b  | ab       | | ||||
| 		 | b    | a  | bc,cd,da | | ||||
| 	 | ||||
| 	Scenario: Avoid oneway traps | ||||
| 		Given the nodes | ||||
| 		 |   | x |   |		 | ||||
| 		 | a | b | c | | ||||
| 		 |   | y |   |		 | ||||
| 
 | ||||
| 		And the ways | ||||
| 		 | nodes | oneway | | ||||
| 		 | abc   |        | | ||||
| 		 | bx    | yes    | | ||||
| 		 | yb    | -1     | | ||||
| 
 | ||||
| 		When I route I should get | ||||
| 		 | from | to | route | | ||||
| 		 | b    | x  |       | | ||||
| 		 | b    | y  |       | | ||||
| 	 | ||||
| 	Scenario: Handle various oneway tag values | ||||
|  		Given the speedprofile "bicycle" | ||||
| 	 	Then routability should be | ||||
| 		 | highway       | oneway   | forw | backw | | ||||
| 		 | primary       |          | x    | x     | | ||||
| 		 | primary       | nonsense | x    | x     | | ||||
| 		 | primary       | no       | x    | x     | | ||||
| 		 | primary       | false    | x    | x     | | ||||
| 		 | primary       | 0        | x    | x     | | ||||
| 		 | primary       | yes      | x    |       | | ||||
| 		 | primary       | true     | x    |       | | ||||
| 		 | primary       | 1        | x    |       | | ||||
| 		 | primary       | -1       |      | x     | | ||||
| 	 | ||||
| 	Scenario: Implied oneways | ||||
| 	 	Given the speedprofile "car" | ||||
| 	 	Then routability should be | ||||
| 		 | highway        | junction   | forw | backw | | ||||
| 		 | motorway       |            | x    |       | | ||||
| 		 | motorway_link  |            | x    |       | | ||||
| 		 | trunk          |            | x    | x     | | ||||
| 		 | trunk_link     |            | x    | x     | | ||||
| 		 | primary        |            | x    | x     | | ||||
| 		 | primary_link   |            | x    | x     | | ||||
| 		 | secondary      |            | x    | x     | | ||||
| 		 | secondary_link |            | x    | x     | | ||||
| 		 | tertiary       |            | x    | x     | | ||||
| 		 | tertiary_link  |            | x    | x     | | ||||
| 		 | residential    |            | x    | x     | | ||||
| 		 | primary        | roundabout | x    |       | | ||||
| 		 | secondary      | roundabout | x    |       | | ||||
| 		 | tertiary       | roundabout | x    |       | | ||||
| 		 | residential    | roundabout | x    |       | | ||||
| 
 | ||||
| 	Scenario: Overriding implied oneways | ||||
|  		Given the speedprofile "car" | ||||
| 	 	Then routability should be | ||||
| 		 | highway       | junction   | oneway | forw | backw | | ||||
| 		 | motorway_link |            | no     | x    | x     | | ||||
| 		 | trunk_link    |            | no     | x    | x     | | ||||
| 		 | primary       | roundabout | no     | x    | x     | | ||||
| 		 | motorway_link |            | yes    | x    |       | | ||||
| 		 | trunk_link    |            | yes    | x    |       | | ||||
| 		 | primary       | roundabout | yes    | x    |       | | ||||
| 		 | motorway_link |            | -1     |      | x     | | ||||
| 		 | trunk_link    |            | -1     |      | x     | | ||||
| 		 | primary       | roundabout | -1     |      | x     | | ||||
| 
 | ||||
| 	Scenario: Disabling oneways in speedprofile | ||||
|  		Given the speedprofile "car" | ||||
| 	 	And the speedprofile settings | ||||
| 		 | obeyOneways | no | | ||||
| 		Then routability should be | ||||
| 		 | highway       | junction   | oneway | forw | backw | | ||||
| 		 | primary       |            | yes    | x    | x     | | ||||
| 		 | primary       |            | true   | x    | x     | | ||||
| 		 | primary       |            | 1      | x    | x     | | ||||
| 		 | primary       |            | -1     | x    | x     | | ||||
| 		 | motorway_link |            |        | x    | x     | | ||||
| 		 | trunk_link    |            |        | x    | x     | | ||||
| 		 | primary       | roundabout |        | x    | x     | | ||||
| 	 | ||||
| 	@bicycle | ||||
| 	Scenario: Oneway:bicycle should override normal oneways tags | ||||
|  		Given the speedprofile "bicycle" | ||||
| 	 	Then routability should be | ||||
| 		 | highway       | junction   | oneway | oneway:bicycle | forw | backw | | ||||
| 		 | primary       |            |        | yes            | x    |       | | ||||
| 		 | primary       |            | yes    | yes            | x    |       | | ||||
| 		 | primary       |            | no     | yes            | x    |       | | ||||
| 		 | primary       |            | -1     | yes            | x    |       | | ||||
| 		 | primary       | roundabout |        | yes            | x    |       | | ||||
| 		 | primary       |            |        | no             | x    | x     | | ||||
| 		 | primary       |            | yes    | no             | x    | x     | | ||||
| 		 | primary       |            | no     | no             | x    | x     | | ||||
| 		 | primary       |            | -1     | no             | x    | x     | | ||||
| 		 | primary       | roundabout |        | no             | x    | x     | | ||||
| 		 | primary       |            |        | -1             |      | x     | | ||||
| 		 | primary       |            | yes    | -1             |      | x     | | ||||
| 		 | primary       |            | no     | -1             |      | x     | | ||||
| 		 | primary       |            | -1     | -1             |      | x     | | ||||
| 		 | primary       | roundabout |        | -1             |      | x     | | ||||
| 	 | ||||
| 	@bicycle | ||||
| 	Scenario: Bicycles and contra flow | ||||
|  		Given the speedprofile "bicycle" | ||||
| 	 	Then routability should be | ||||
| 		 | highway | oneway | cycleway       | forw | backw | | ||||
| 		 | primary | yes    | opposite       | x    | x     | | ||||
| 		 | primary | yes    | opposite_track | x    | x     | | ||||
| 		 | primary | yes    | opposite_lane  | x    | x     | | ||||
| 		 | primary | -1     | opposite       | x    | x     | | ||||
| 		 | primary | -1     | opposite_track | x    | x     | | ||||
| 		 | primary | -1     | opposite_lane  | x    | x     | | ||||
| 		 | primary | no     | opposite       | x    | x     | | ||||
| 		 | primary | no     | opposite_track | x    | x     | | ||||
| 		 | primary | no     | opposite_lane  | x    | x     | | ||||
| 	 | ||||
| 	@bicycle | ||||
| 	Scenario: Cars should not be affected by bicycle tags | ||||
|  		Given the speedprofile "car" | ||||
| 		Then routability should be | ||||
| 		 | highway | junction   | oneway | oneway:bicycle | forw | backw | | ||||
| 		 | primary |            | yes    | yes            | x    |       | | ||||
| 		 | primary |            | yes    | no             | x    |       | | ||||
| 		 | primary |            | yes    | -1             | x    |       | | ||||
| 		 | primary |            | no     | yes            | x    | x     | | ||||
| 		 | primary |            | no     | no             | x    | x     | | ||||
| 		 | primary |            | no     | -1             | x    | x     | | ||||
| 		 | primary |            | -1     | yes            |      | x     | | ||||
| 		 | primary |            | -1     | no             |      | x     | | ||||
| 		 | primary |            | -1     | -1             |      | x     | | ||||
| 		 | primary | roundabout |        | yes            | x    |       | | ||||
| 		 | primary | roundabout |        | no             | x    |       | | ||||
| 		 | primary | roundabout |        | -1             | x    |       | | ||||
| 
 | ||||
							
								
								
									
										95
									
								
								features/restrictions.feature
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								features/restrictions.feature
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,95 @@ | ||||
| @routing @restrictions | ||||
| Feature: Turn restrictions | ||||
| 	Handle turn restrictions as defined by http://wiki.openstreetmap.org/wiki/Relation:restriction | ||||
| 	How this plays with u-turns can be tricky. | ||||
| 	 | ||||
| 	Scenario: No left turn | ||||
| 		Given the nodes | ||||
| 		 |   | t |   | | ||||
| 		 | a | j | b | | ||||
| 		 |   | s |   | | ||||
| 
 | ||||
| 		And the ways | ||||
| 		 | nodes | | ||||
| 		 | bj    | | ||||
| 		 | aj    | | ||||
| 		 | sj    | | ||||
| 		 | tj    | | ||||
| 
 | ||||
| 		And the relations | ||||
| 		 | from | to | via | restriction  | | ||||
| 		 | sj   | aj | j   | no_left_turn | | ||||
| 
 | ||||
| 		When I route I should get | ||||
| 		 | from | to | route | | ||||
| 		 | s    | a  |       | | ||||
| 		 | s    | b  | sj,jb | | ||||
| 		 | s    | t  | sj,tj | | ||||
| 		 | a    | b  | aj,bj | | ||||
| 		 | a    | a  | aj,sj | | ||||
| 		 | a    | t  | aj,tj | | ||||
| 		 | b    | b  | jb,aj | | ||||
| 		 | b    | s  | bj,sj | | ||||
| 		 | b    | t  | bj,tj | | ||||
| 
 | ||||
| 	Scenario: No left turn, go counter-clockwise around the block instead | ||||
| 		Given the nodes | ||||
| 		 | x | t |   | | ||||
| 		 | a | j | b | | ||||
| 		 |   | s |   | | ||||
| 
 | ||||
| 		And the ways | ||||
| 		 | nodes | | ||||
| 		 | bj    | | ||||
| 		 | aj    | | ||||
| 		 | sj    | | ||||
| 		 | tj    | | ||||
| 		 | axt   | | ||||
| 
 | ||||
| 		And the relations | ||||
| 		 | from | to | via | restriction  | | ||||
| 		 | sj   | aj | j   | no_left_turn | | ||||
| 
 | ||||
| 		When I route I should get | ||||
| 		 | from | to | route     | | ||||
| 		 | s    | a  | sj,tj,axt | | ||||
| 		 | s    | b  | sj,jb     | | ||||
| 		 | s    | t  | sj,tj     | | ||||
| 		 | a    | b  | aj,bj     | | ||||
| 		 | a    | a  | aj,sj     | | ||||
| 		 | a    | t  | aj,tj     | | ||||
| 		 | b    | b  | jb,aj     | | ||||
| 		 | b    | s  | bj,sj     | | ||||
| 		 | b    | t  | bj,tj     | | ||||
| 		 | ||||
| 	Scenario: No left turn, go clockwise around the block instead | ||||
| 		Given the nodes | ||||
| 		 |   |   | t |   | | ||||
| 		 | z | a | j | b | | ||||
| 		 | x |   | s |   | | ||||
| 
 | ||||
| 		And the ways | ||||
| 		 | nodes | | ||||
| 		 | bj    | | ||||
| 		 | aj    | | ||||
| 		 | sj    | | ||||
| 		 | tj    | | ||||
| 		 | sxza  | | ||||
| 
 | ||||
| 		And the relations | ||||
| 		 | from | to | via | restriction  | | ||||
| 		 | sj   | aj | j   | no_left_turn | | ||||
| 
 | ||||
| 		When I route I should get | ||||
| 		 | from | to | route     | | ||||
| 		 | s    | a  | sxza      | | ||||
| 		 | s    | b  | sj,jb     | | ||||
| 		 | s    | t  | sj,tj     | | ||||
| 		 | a    | b  | aj,bj     | | ||||
| 		 | a    | a  | aj,sj     | | ||||
| 		 | a    | t  | aj,tj     | | ||||
| 		 | b    | b  | jb,aj     | | ||||
| 		 | b    | s  | bj,sj     | | ||||
| 		 | b    | t  | bj,tj     | | ||||
| 
 | ||||
| 
 | ||||
							
								
								
									
										86
									
								
								features/snap.feature
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								features/snap.feature
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,86 @@ | ||||
| @routing @snap | ||||
| Feature: Snap start/end point to the nearest way  | ||||
| 	 | ||||
| 	Scenario: Snap to nearest protruding oneway | ||||
| 		Given the nodes | ||||
| 		 |   | 1 |   | 2 |   | | ||||
| 		 | 8 |   | n |   | 3 | | ||||
| 		 |   | w | c | e |   | | ||||
| 		 | 7 |   | s |   | 4 | | ||||
| 		 |   | 6 |   | 5 |   | | ||||
| 
 | ||||
| 		And the ways | ||||
| 		 | nodes | | ||||
| 		 | nc    | | ||||
| 		 | ec    | | ||||
| 		 | sc    | | ||||
| 		 | wc    | | ||||
| 
 | ||||
| 		When I route I should get | ||||
| 		 | from | to | route | | ||||
| 		 | 1    | c  | nc    | | ||||
| 		 | 2    | c  | nc    | | ||||
| 		 | 3    | c  | ec    | | ||||
| 		 | 4    | c  | ec    | | ||||
| 		 | 5    | c  | sc    | | ||||
| 		 | 6    | c  | sc    | | ||||
| 		 | 7    | c  | wc    | | ||||
| 		 | 8    | c  | wc    | | ||||
| 	 | ||||
| 	Scenario: Snap to nearest edge of a square | ||||
| 		Given the nodes | ||||
| 		 | 4 | 5 | 6 | 7 | | ||||
| 		 | 3 | a |   | u | | ||||
| 		 | 2 |   |   |   | | ||||
| 		 | 1 | d |   | b | | ||||
| 
 | ||||
| 		And the ways | ||||
| 		 | nodes | | ||||
| 		 | aub   | | ||||
| 		 | adb   | | ||||
| 
 | ||||
| 		When I route I should get | ||||
| 		 | from | to | route | | ||||
| 		 | 1    | b  | adb   | | ||||
| 		 | 2    | b  | adb   | | ||||
| 		 | 6    | b  | aub   | | ||||
| 		 | 7    | b  | aub   | | ||||
| 
 | ||||
| 	Scenario: Snap to edge right under start/end point | ||||
| 		Given the nodes | ||||
| 		 | d | e | f | g | | ||||
| 		 | c |   |   | h | | ||||
| 		 | b |   |   | i | | ||||
| 		 | a | l | k | j | | ||||
| 
 | ||||
| 		And the ways | ||||
| 		 | nodes | | ||||
| 		 | abcd  | | ||||
| 		 | defg  | | ||||
| 		 | ghij  | | ||||
| 		 | jkla  | | ||||
| 
 | ||||
| 		When I route I should get | ||||
| 		 | from | to | route | | ||||
| 		 | a    | b  | abcd  | | ||||
| 		 | a    | c  | abcd  | | ||||
| 		 | a    | d  | abcd  | | ||||
| 		 | a    | e  | abcd  | | ||||
| 		 | a    | f  | abcd  | | ||||
| 		 | a    | g  | abcd  | | ||||
| 		 | a    | h  | jkla  | | ||||
| 		 | a    | i  | jkla  | | ||||
| 		 | a    | j  | jkla  | | ||||
| 		 | a    | k  | jkla  | | ||||
| 		 | a    | l  | jkla  | | ||||
| 		 | b    | a  | abcd  | | ||||
| 		 | b    | c  | abcd  | | ||||
| 		 | b    | d  | abcd  | | ||||
| 		 | b    | e  | abcd  | | ||||
| 		 | b    | f  | abcd  | | ||||
| 		 | b    | g  | abcd  | | ||||
| 		 | b    | h  | jkla  | | ||||
| 		 | b    | i  | jkla  | | ||||
| 		 | b    | j  | jkla  | | ||||
| 		 | b    | k  | jkla  | | ||||
| 		 | b    | l  | jkla  | | ||||
							
								
								
									
										73
									
								
								features/step_definitions/data.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								features/step_definitions/data.rb
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,73 @@ | ||||
| Given /^the speedprofile "([^"]*)"$/ do |profile| | ||||
|   read_speedprofile profile | ||||
| end | ||||
| 
 | ||||
| Given /^the speedprofile settings$/ do |table| | ||||
|   table.raw.each do |row| | ||||
|     speedprofile[ row[0] ] = row[1] | ||||
|   end | ||||
| end | ||||
| 
 | ||||
| Given /^the nodes$/ do |table| | ||||
|   table.raw.each_with_index do |row,ri| | ||||
|     row.each_with_index do |name,ci| | ||||
|       unless name.empty? | ||||
|         raise "*** node invalid name '#{name}', must be single characters" unless name.size == 1 | ||||
|         raise "*** invalid node name '#{name}', must me alphanumeric" unless name.match /[a-z0-9]/ | ||||
|         raise "*** duplicate node '#{name}'" if name_node_hash[name] | ||||
|         node = OSM::Node.new make_osm_id, OSM_USER, OSM_TIMESTAMP, ORIGIN[0]+ci*ZOOM, ORIGIN[1]-ri*ZOOM  | ||||
|         node << { :name => name } | ||||
|         node.uid = OSM_UID | ||||
|         osm_db << node | ||||
|         name_node_hash[name] = node | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| end | ||||
| 
 | ||||
| Given /^the ways$/ do |table| | ||||
|   table.hashes.each do |row| | ||||
|     name = row.delete 'nodes' | ||||
|     raise "*** duplicate way '#{name}'" if name_way_hash[name] | ||||
|     way = OSM::Way.new make_osm_id, OSM_USER, OSM_TIMESTAMP | ||||
|     defaults = { 'highway' => 'primary' } | ||||
|     way << defaults.merge( 'name' => name ).merge(row) | ||||
|     way.uid = OSM_UID | ||||
|     name.each_char do |c| | ||||
|       raise "*** node invalid name '#{c}', must be single characters" unless c.size == 1 | ||||
|       raise "*** ways cannot use numbered nodes, '#{name}'" unless c.match /[a-z]/ | ||||
|       node = find_node_by_name(c) | ||||
|       raise "*** unknown node '#{c}'" unless node | ||||
|       way << node | ||||
|     end | ||||
|     osm_db << way | ||||
|     name_way_hash[name] = way | ||||
|   end | ||||
| end | ||||
| 
 | ||||
| Given /^the relations$/ do |table| | ||||
|   table.hashes.each do |row| | ||||
|     relation = OSM::Relation.new make_osm_id, OSM_USER, OSM_TIMESTAMP | ||||
|     relation << { :type => :restriction, :restriction => 'no_left_turn' } | ||||
|     from_way = find_way_by_name(row['from']) | ||||
|     raise "*** unknown way '#{row['from']}'" unless from_way | ||||
|     to_way = find_way_by_name(row['to']) | ||||
|     raise "*** unknown way '#{row['to']}'" unless to_way | ||||
|     relation << OSM::Member.new( 'way', from_way.id, 'from' ) | ||||
|     relation << OSM::Member.new( 'way', to_way.id, 'to' ) | ||||
|     c = row['via'] | ||||
|     unless c.empty? | ||||
|       raise "*** node invalid name '#{c}', must be single characters" unless c.size == 1 | ||||
|       raise "*** via node cannot use numbered nodes, '#{c}'" unless c.match /[a-z]/ | ||||
|       via_node = find_node_by_name(c) | ||||
|       raise "*** unknown node '#{row['via']}'" unless via_node | ||||
|       relation << OSM::Member.new( 'node', via_node.id, 'via' ) | ||||
|     end | ||||
|     relation.uid = OSM_UID | ||||
|     osm_db << relation | ||||
|   end | ||||
| end | ||||
| 
 | ||||
| Given /^the defaults$/ do | ||||
| end | ||||
| 
 | ||||
							
								
								
									
										72
									
								
								features/step_definitions/processing.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								features/step_definitions/processing.rb
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,72 @@ | ||||
| require 'OSM/StreamParser' | ||||
| 
 | ||||
| class OSMTestParserCallbacks < OSM::Callbacks | ||||
|   @@locations = nil | ||||
|    | ||||
|   def self.locations | ||||
|     if @@locations | ||||
|       @@locations | ||||
|     else | ||||
|       #parse the test file, so we can later reference nodes and ways by name in tests  | ||||
|       @@locations = {} | ||||
|       file = 'test/data/test.osm' | ||||
|       callbacks = OSMTestParserCallbacks.new | ||||
|       parser = OSM::StreamParser.new(:filename => file, :callbacks => callbacks) | ||||
|       parser.parse | ||||
|       puts @@locations | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   def node(node) | ||||
|     @@locations[node.name] = [node.lat,node.lon] | ||||
|   end | ||||
| end | ||||
| 
 | ||||
| 
 | ||||
| Given /^the OSM file contains$/ do |string| | ||||
|   file = 'data/test.osm' | ||||
|   File.open( file, 'w') {|f| f.write(string) } | ||||
| 
 | ||||
|   #convert from .osm to .osm.pbf, which is the format osrm reads | ||||
|   system "osmosis --read-xml data/test.osm --write-pbf data/test.osm.pbf omitmetadata=true" | ||||
| end | ||||
| 
 | ||||
| Given /^the speedprofile contains$/ do |string| | ||||
|   File.open( 'speedprofile.ini', 'w') {|f| f.write(string) } | ||||
| end | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| Given /^the data file "([^"]*)" is present$/ do |file| | ||||
|   File.exists?(file).should == true | ||||
| end | ||||
| 
 | ||||
| When /^I run the extractor with "([^"]*)"$/ do |cmd| | ||||
|   @response = `#{cmd}` | ||||
|   #Dir.chdir @test_folder do | ||||
|   #  @response = IO.popen([cmd, :err=>[:child, :out]]) { |ls_io| ls_result_with_error = ls_io.read } | ||||
|   #end | ||||
| end | ||||
| 
 | ||||
| When /^I run the preprocessor with "([^"]*)"$/ do |cmd| | ||||
|   @response = `#{cmd}` | ||||
| end | ||||
| 
 | ||||
| Given /^the preprocessed files for "([^"]*)" are present and up to date$/ do |area| | ||||
|   File.exists?("#{area}.osrm").should == true | ||||
|   File.exists?("#{area}.osrm.names").should == true | ||||
|   File.exists?("#{area}.osrm.restrictions").should == true | ||||
|   File.exists?("#{area}.osrm.hsgr").should == true | ||||
|   File.exists?("#{area}.osrm.nodes").should == true | ||||
|   File.exists?("#{area}.osrm.ramIndex").should == true | ||||
|   File.exists?("#{area}.osrm.fileIndex").should == true | ||||
| end | ||||
| 
 | ||||
| Then /^I should see the file "([^"]*)"$/ do |file| | ||||
|   File.exists?(file).should == true | ||||
| end | ||||
| 
 | ||||
| When /^preprocessed files for "([^"]*)" has been removed$/ do |file| | ||||
|   FileUtils.rm_r  Dir["#{file}.*"], :secure => true | ||||
| end | ||||
| 
 | ||||
							
								
								
									
										221
									
								
								features/step_definitions/routing.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										221
									
								
								features/step_definitions/routing.rb
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,221 @@ | ||||
| When /^I request a route from ([^"]+) to ([^"]+)$/ do |a,b| | ||||
|   @response = request_route a,b | ||||
|   #puts @response.body | ||||
|   #@response | ||||
| end | ||||
| 
 | ||||
| When /^I request a route from "([^"]*)" to "([^"]*)"$/ do |a,b| | ||||
|   locations = OSMTestParserCallbacks.locations | ||||
|   raise "Locations hash is empty. To reference nodes by name, please preprocess the test file earlier in the test." unless locations | ||||
|   raise "Unknown node: #{a}" unless locations[a] | ||||
|   raise "Unknown node: #{b}" unless locations[b] | ||||
|   @response = request_route "#{locations[a][0]},#{locations[a][1]}", "#{locations[b][0]},#{locations[b][1]}" | ||||
| end | ||||
| 
 | ||||
| Then /^I should get a response/ do | ||||
|   @response.code.should == "200" | ||||
|   @response.body.should_not == nil | ||||
|   @response.body.should_not == '' | ||||
| end | ||||
| 
 | ||||
| Then /^response should be valid JSON$/ do | ||||
|   @json = JSON.parse @response.body | ||||
| end | ||||
| 
 | ||||
| Then /^response should be well-formed$/ do | ||||
|   @json['version'].class.should == Float | ||||
|   @json['status'].class.should == Fixnum | ||||
|   @json['status_message'].class.should == String | ||||
|   @json['route_summary'].class.should == Hash | ||||
|   @json['route_geometry'].class.should == String | ||||
|   @json['route_instructions'].class.should == Array | ||||
|   @json['via_points'].class.should == Array | ||||
|   @json['transactionId'].class.should == String | ||||
| end | ||||
| 
 | ||||
| Then /^a route should be found$/ do | ||||
|   @json['status'].should == 0 | ||||
|   @json['status_message'].should == "Found route between points" | ||||
| end | ||||
| 
 | ||||
| Then /^no route should be found$/ do | ||||
|   @json['status'].should == 207 | ||||
|   @json['status_message'].should == "Cannot find route between points" | ||||
| end | ||||
| 
 | ||||
| Then /^I should get a valid response$/ do | ||||
|   step "I should get a response" | ||||
|   step "response should be valid JSON" | ||||
|   step "response should be well-formed" | ||||
|  	#step "no error should be reported in terminal" | ||||
| end | ||||
| 
 | ||||
| Then /^I should get a route$/ do | ||||
|   step "I should get a valid response" | ||||
|   step "a route should be found" | ||||
|   #puts @response.body | ||||
| end | ||||
| 
 | ||||
| Then /^I should not get a route$/ do | ||||
|   step "I should get a valid response" | ||||
|   step "no route should be found" | ||||
| end | ||||
| 
 | ||||
| Then /^the route should start at "([^']*)"$/ do |name| | ||||
|   @json['route_summary']['start_point'].should == name | ||||
| end | ||||
| 
 | ||||
| Then /^the route should end at "([^']*)"$/ do |name| | ||||
|   @json['route_summary']['end_point'].should == name | ||||
| end | ||||
| 
 | ||||
| Then /^distance should be between (\d+) and (\d+)$/ do |min,max| | ||||
|   @json['route_summary']['total_distance'].to_i.should >= min.to_i | ||||
|   @json['route_summary']['total_distance'].to_i.should <= max.to_i | ||||
| end | ||||
| 
 | ||||
| Then /^the distance should be close to (\d+)m$/ do |d| | ||||
|   @json['route_summary']['total_distance'].to_i.should >= d.to_i*0.95 | ||||
|   @json['route_summary']['total_distance'].to_i.should <= d.to_i/0.95 | ||||
| end | ||||
| 
 | ||||
| Then /^number of instructions should be (\d+)$/ do |n| | ||||
|   @json['route_instructions'].size.should == n | ||||
| end | ||||
| 
 | ||||
| Then /^there should be 1 turn$/ do | ||||
|   step 'there should be 1 turns' | ||||
| end | ||||
| 
 | ||||
| Then /^there should be (\d+) turns$/ do |n| | ||||
|   @json['route_instructions'].map {|t| t.first}.select {|t| t =~ /^Turn/ }.size.should == n.to_i | ||||
| end | ||||
| 
 | ||||
| Then /^there should be more than (\d+) turn$/ do |n| | ||||
|   @json['route_instructions'].map {|t| t.first}.select {|t| t =~ /^Turn/ }.size.should > n.to_i | ||||
| end | ||||
| 
 | ||||
| Then /^there should not be any turns$/ do | ||||
|   (@json['route_instructions'].size-1).should == 0 | ||||
| end | ||||
| 
 | ||||
| def sanitize_route route | ||||
|   route.split(',').map{|w| w.strip}.reject(&:empty?).join(', ') | ||||
| end | ||||
|   | ||||
| def computed_route | ||||
|   @json['route_instructions'].map { |r| r[1] }.reject(&:empty?).join(', ') | ||||
| end | ||||
|     | ||||
| Then /^the route should follow "([^"]*)"$/ do |route| | ||||
|   sanitize_route(route).should == computed_route | ||||
| end | ||||
| 
 | ||||
| Then /^the route should not follow "([^"]*)"$/ do |route| | ||||
|   sanitize_route(route).should_not == computed_route | ||||
| end | ||||
| 
 | ||||
| Then /^the route should include "([^"]*)"$/ do |route| | ||||
|   sanitize_route(route).should =~ /#{computed_route}/ | ||||
| end | ||||
| 
 | ||||
| Then /^the route should not include "([^"]*)"$/ do |route| | ||||
|   sanitize_route(route).should_not =~ /#{computed_route}/ | ||||
| end | ||||
| 
 | ||||
| Then /^the route should stay on "([^"]*)"$/ do |way| | ||||
|   step "the route should start at \"#{way}\"" | ||||
|   step "the route should end at \"#{way}\"" | ||||
|   step "the route should follow \"#{way}\"" | ||||
|   step "there should not be any turns" | ||||
| end | ||||
| 
 | ||||
| When /^I route between "([^"]*)" and "([^"]*)"$/ do |from,to| | ||||
|   reprocess | ||||
|   Dir.chdir 'test' do | ||||
|     from_node = name_node_hash[from] | ||||
|     to_node = name_node_hash[to] | ||||
|     a = "#{from_node.lon},#{from_node.lat}" | ||||
|     b = "#{to_node.lon},#{to_node.lat}" | ||||
|     @route = parse_response( request_route(a,b) ) | ||||
|   end | ||||
| end | ||||
| 
 | ||||
| Then /^"([^"]*)" should be returned$/ do |route| | ||||
|   @route.should == route.split(',').join(',') | ||||
| end | ||||
| 
 | ||||
| Then /^routability should be$/ do |table| | ||||
|   osrm_kill | ||||
|   build_ways_from_table table | ||||
|   reprocess | ||||
|   actual = [] | ||||
|   if table.headers&["forw","backw"] == [] | ||||
|     raise "*** routability tabel must contain either 'forw' or 'backw' column" | ||||
|   end | ||||
|   OSRMLauncher.new do | ||||
|     table.hashes.each_with_index do |row,i| | ||||
|       got = row.dup | ||||
|       attempts = [] | ||||
|       if table.headers.include? 'forw' | ||||
|         response = request_route("#{ORIGIN[1]},#{ORIGIN[0]+(1+WAY_SPACING*i)*ZOOM}","#{ORIGIN[1]},#{ORIGIN[0]+(2+WAY_SPACING*i)*ZOOM}") | ||||
|         got['forw'] = route_status response | ||||
|         if got['forw'] != row['forw'] | ||||
|           json = JSON.parse(response.body) | ||||
|           attempts << { :attempt => 'Forward', :query => @query, :response => response } | ||||
|         end | ||||
|       end | ||||
|       if table.headers.include? 'backw' | ||||
|         response = request_route("#{ORIGIN[1]},#{ORIGIN[0]+(2+WAY_SPACING*i)*ZOOM}","#{ORIGIN[1]},#{ORIGIN[0]+(1+WAY_SPACING*i)*ZOOM}") | ||||
|         got['backw'] = route_status response | ||||
|         if got['backw'] != row['backw'] | ||||
|           attempts << { :attempt => 'Backward', :query => @query, :response => response } | ||||
|         end | ||||
|       end | ||||
|       if got != row | ||||
|         log_fail row,got,attempts | ||||
|       end | ||||
|       actual << got | ||||
|     end | ||||
|   end | ||||
|   table.diff! actual | ||||
| end | ||||
| 
 | ||||
| When /^I route I should get$/ do |table| | ||||
|   osrm_kill | ||||
|   reprocess | ||||
|   actual = [] | ||||
|   OSRMLauncher.new do | ||||
|     table.hashes.each_with_index do |row,ri| | ||||
|       from_node = @name_node_hash[ row['from'] ] | ||||
|       raise "*** unknown from-node '#{row['from']}" unless from_node | ||||
|       to_node = @name_node_hash[ row['to'] ] | ||||
|       raise "*** unknown to-node '#{row['to']}" unless to_node | ||||
|       response = request_route("#{from_node.lat},#{from_node.lon}", "#{to_node.lat},#{to_node.lon}") | ||||
|       if response.code == "200" && response.body.empty? == false | ||||
|         json = JSON.parse response.body | ||||
|         if json['status'] == 0 | ||||
|           instructions = way_list json['route_instructions'] | ||||
|         end | ||||
|       end | ||||
| 
 | ||||
|       got = {'from' => row['from'], 'to' => row['to'] } | ||||
|       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? 'route' | ||||
|         got['route'] = (instructions || '').strip | ||||
|       end | ||||
| 
 | ||||
|       if got['route'] != row['route'] || got['start'] != row['start'] || got['end'] != row['end'] | ||||
|         failed = { :attempt => 'Backward', :query => @query, :response => response } | ||||
|         log_fail row,got,[failed] | ||||
|       end | ||||
|       actual << got | ||||
|     end | ||||
|   end | ||||
|   table.diff! actual | ||||
| end | ||||
							
								
								
									
										41
									
								
								features/support/config.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								features/support/config.rb
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,41 @@ | ||||
| def speedprofile | ||||
|   @speedprofile ||= reset_speedprofile | ||||
| end | ||||
| 
 | ||||
| def reset_speedprofile | ||||
|   @speedprofile = {} | ||||
|   read_speedprofile DEFAULT_SPEEDPROFILE | ||||
| end | ||||
| 
 | ||||
| def read_speedprofile profile | ||||
|   @speedprofile = {} | ||||
|   @speedprofile_str = nil | ||||
|   s = File.read "speedprofiles/#{profile}.ini" | ||||
|   s.scan /(.*)=(.*)/ do |option| | ||||
|     @speedprofile[option[0].strip] = option[1].strip | ||||
|   end | ||||
| end | ||||
| 
 | ||||
| def speedprofile_str | ||||
|   @speedprofile_str ||= "[Scenario: #{@scenario_title}]\n" + @speedprofile.map { |k,v| "    #{k} = #{v}" }.join("\n") | ||||
| end | ||||
| 
 | ||||
| def write_speedprofile | ||||
|   File.open( 'speedprofile.ini', 'w') {|f| f.write( speedprofile_str ) } | ||||
| end | ||||
| 
 | ||||
| def write_server_ini | ||||
|   s=<<-EOF | ||||
| Threads = 1 | ||||
| IP = 0.0.0.0 | ||||
| Port = 5000 | ||||
| 
 | ||||
| hsgrData=#{@osm_file}.osrm.hsgr | ||||
| nodesData=#{@osm_file}.osrm.nodes | ||||
| ramIndex=#{@osm_file}.osrm.ramIndex | ||||
| fileIndex=#{@osm_file}.osrm.fileIndex | ||||
| namesData=#{@osm_file}.osrm.names | ||||
| EOF | ||||
|   File.open( 'server.ini', 'w') {|f| f.write( s ) } | ||||
| end | ||||
| 
 | ||||
							
								
								
									
										189
									
								
								features/support/data.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										189
									
								
								features/support/data.rb
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,189 @@ | ||||
| require 'OSM/objects'       #osmlib gem | ||||
| require 'OSM/Database' | ||||
| require 'builder' | ||||
| 
 | ||||
| OSM_USER = 'osrm' | ||||
| OSM_GENERATOR = 'osrm-test' | ||||
| OSM_UID = 1 | ||||
| TEST_FOLDER = 'test' | ||||
| DATA_FOLDER = 'cache' | ||||
| PREPROCESS_LOG_FILE = 'preprocessing.log' | ||||
| LOG_FILE = 'fail.log' | ||||
| OSM_TIMESTAMP = '2000-00-00T00:00:00Z' | ||||
| DEFAULT_SPEEDPROFILE = 'bicycle' | ||||
| WAY_SPACING = 10 | ||||
| 
 | ||||
| ORIGIN = [1,1] | ||||
| NODE_SPACING = 100    #meters | ||||
| ZOOM = 0.001*(NODE_SPACING.to_f/111.0) | ||||
| 
 | ||||
| def build_ways_from_table table | ||||
|   #add one unconnected way for each row | ||||
|   table.hashes.each_with_index do |row,ri| | ||||
|     #NOTE: | ||||
|     #currently osrm crashes when processing an isolated oneway with just 2 nodes, so we use 4 | ||||
|     #this is relatated to the fact that a oneway deadend doesn't make a lot of sense | ||||
|      | ||||
|     #if we stack ways on different x coordinates, outability tests get messed up, because osrm might pick a neighboring way if the one test can't be used. | ||||
|     #instead we place all lines as a string on the same y coordinate. this prevents using neightboring ways. | ||||
|      | ||||
|     #a few nodes... | ||||
|     node1 = OSM::Node.new make_osm_id, OSM_USER, OSM_TIMESTAMP, ORIGIN[0]+(0+WAY_SPACING*ri)*ZOOM, ORIGIN[1]  | ||||
|     node2 = OSM::Node.new make_osm_id, OSM_USER, OSM_TIMESTAMP, ORIGIN[0]+(1+WAY_SPACING*ri)*ZOOM, ORIGIN[1]  | ||||
|     node3 = OSM::Node.new make_osm_id, OSM_USER, OSM_TIMESTAMP, ORIGIN[0]+(2+WAY_SPACING*ri)*ZOOM, ORIGIN[1]  | ||||
|     node4 = OSM::Node.new make_osm_id, OSM_USER, OSM_TIMESTAMP, ORIGIN[0]+(3+WAY_SPACING*ri)*ZOOM, ORIGIN[1]  | ||||
|     node1.uid = OSM_UID | ||||
|     node2.uid = OSM_UID | ||||
|     node3.uid = OSM_UID | ||||
|     node4.uid = OSM_UID | ||||
|     node1 << { :name => "a#{ri}" } | ||||
|     node2 << { :name => "b#{ri}" } | ||||
|     node3 << { :name => "c#{ri}" } | ||||
|     node4 << { :name => "d#{ri}" } | ||||
| 
 | ||||
|     osm_db << node1 | ||||
|     osm_db << node2 | ||||
|     osm_db << node3 | ||||
|     osm_db << node4 | ||||
|      | ||||
|     #...with a way between them | ||||
|     way = OSM::Way.new make_osm_id, OSM_USER, OSM_TIMESTAMP | ||||
|     way.uid = OSM_UID | ||||
|     way << node1 | ||||
|     way << node2 | ||||
|     way << node3 | ||||
|     way << node4 | ||||
|     tags = row.dup | ||||
|     tags.delete 'forw' | ||||
|     tags.delete 'backw' | ||||
|     tags['name'] = "abcd#{ri}" | ||||
|     tags.reject! { |k,v| v=='' } | ||||
|     way << tags | ||||
|     osm_db << way | ||||
|   end | ||||
| end | ||||
| 
 | ||||
| def find_node_by_name s | ||||
|   name_node_hash[s.to_s] | ||||
| end | ||||
| 
 | ||||
| def find_way_by_name s | ||||
|   name_way_hash[s.to_s] || name_way_hash[s.to_s.reverse] | ||||
| end | ||||
| 
 | ||||
| def reset_data | ||||
|   Dir.chdir TEST_FOLDER do | ||||
|     #clear_log | ||||
|     #clear_data_files | ||||
|   end | ||||
|   reset_speedprofile | ||||
|   reset_osm | ||||
|   @fingerprint = nil | ||||
| end | ||||
| 
 | ||||
| def make_osm_id | ||||
|   @osm_id = @osm_id+1 | ||||
| end | ||||
| 
 | ||||
| def reset_osm | ||||
|   osm_db.clear | ||||
|   name_node_hash.clear | ||||
|   name_way_hash.clear | ||||
|   @osm_str = nil | ||||
|   @osm_hash = nil | ||||
|    | ||||
|   ##ID -1 causes trouble, so add a few nodes to avoid it | ||||
|   #node = OSM::Node.new nil, OSM_USER, OSM_TIMESTAMP, 0,0  | ||||
|   #node = OSM::Node.new nil, OSM_USER, OSM_TIMESTAMP, 0,0  | ||||
|   @osm_id = 0 | ||||
| end | ||||
| 
 | ||||
| def clear_data_files | ||||
|   File.delete *Dir.glob("#{DATA_FOLDER}/test.*") | ||||
| end | ||||
| 
 | ||||
| def clear_log | ||||
|   File.delete *Dir.glob("*.log") | ||||
| end | ||||
| 
 | ||||
| def osm_db | ||||
|   @osm_db ||= OSM::Database.new | ||||
| end | ||||
| 
 | ||||
| def name_node_hash | ||||
|   @name_node_hash ||= {} | ||||
| end | ||||
| 
 | ||||
| def name_way_hash | ||||
|   @name_way_hash ||= {} | ||||
| end | ||||
| 
 | ||||
| def osm_str | ||||
|   return @osm_str if @osm_str | ||||
|   @osm_str = '' | ||||
|   doc = Builder::XmlMarkup.new :indent => 2, :target => @osm_str | ||||
|   doc.instruct! | ||||
|   osm_db.to_xml doc, OSM_GENERATOR | ||||
|   @osm_str | ||||
| end | ||||
| 
 | ||||
| def write_osm   | ||||
|   #write .oms file if needed | ||||
|   Dir.mkdir DATA_FOLDER unless File.exist? DATA_FOLDER | ||||
|   @osm_file = "#{DATA_FOLDER}/#{fingerprint}" | ||||
|   unless File.exist?("#{@osm_file}.osm") | ||||
|     File.open( "#{@osm_file}.osm", 'w') {|f| f.write(osm_str) } | ||||
|   end | ||||
| end | ||||
| 
 | ||||
| def convert_osm_to_pbf | ||||
|   unless File.exist?("#{@osm_file}.osm.pbf") | ||||
|     log_preprocess_info | ||||
|     log "== Converting #{@osm_file}.osm to protobuffer format...", :preprocess | ||||
|     #redirect stdout and stderr to a log file avoid output in the cucumber console | ||||
|     unless system "osmosis --read-xml #{@osm_file}.osm --write-pbf #{@osm_file}.osm.pbf omitmetadata=true 1>>#{PREPROCESS_LOG_FILE} 2>>#{PREPROCESS_LOG_FILE}" | ||||
|       raise "Failed to convert to proto buffer format. Please see #{hash}.log for more info."  | ||||
|     end | ||||
|     log '', :preprocess | ||||
|   end | ||||
| end | ||||
| 
 | ||||
| def extracted? | ||||
|   File.exist?("#{@osm_file}.osrm") && | ||||
|   File.exist?("#{@osm_file}.osrm.names") && | ||||
|   File.exist?("#{@osm_file}.osrm.restrictions") | ||||
| end | ||||
| 
 | ||||
| def prepared? | ||||
|   base = "#{DATA_FOLDER}/#{fingerprint}" | ||||
|   File.exist?("#{base}.osrm.hsgr") | ||||
| end | ||||
| 
 | ||||
| def reprocess | ||||
|   Dir.chdir TEST_FOLDER do | ||||
|     write_speedprofile | ||||
|     write_osm | ||||
|     convert_osm_to_pbf | ||||
|     unless extracted? | ||||
|       log_preprocess_info | ||||
|       log "== Extracting #{@osm_file}.osm...", :preprocess | ||||
|       unless system "../osrm-extract #{@osm_file}.osm.pbf 1>>#{PREPROCESS_LOG_FILE} 2>>#{PREPROCESS_LOG_FILE}" | ||||
|         log "*** Exited with code #{$?.exitstatus}.", :preprocess | ||||
|         raise "*** osrm-extract exited with code #{$?.exitstatus}. The file preprocess.log might contain more info."  | ||||
|       end | ||||
|       log '', :preprocess | ||||
|     end | ||||
|     unless prepared? | ||||
|       log_preprocess_info | ||||
|       log "== Preparing #{@osm_file}.osm...", :preprocess | ||||
|       unless system "../osrm-prepare #{@osm_file}.osrm #{@osm_file}.restrictions 1>>#{PREPROCESS_LOG_FILE} 2>>#{PREPROCESS_LOG_FILE}" | ||||
|         log "*** Exited with code #{$?.exitstatus}.", :preprocess | ||||
|         raise "*** osrm-prepare exited with code #{$?.exitstatus}. The file preprocess.log might contain more info."  | ||||
|       end  | ||||
|       log '', :preprocess | ||||
|     end | ||||
|     log_preprocess_done | ||||
|     write_server_ini | ||||
|   end | ||||
| end | ||||
| 
 | ||||
							
								
								
									
										38
									
								
								features/support/hash.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								features/support/hash.rb
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | ||||
| require 'digest/sha1' | ||||
| 
 | ||||
| def hash_of_file path | ||||
|   hash = Digest::SHA1.new | ||||
|   open(path,'r') do |io| | ||||
|     while !io.eof | ||||
|       buf = io.readpartial 1024 | ||||
|       hash.update buf | ||||
|     end | ||||
|   end | ||||
|   return hash.hexdigest | ||||
| end | ||||
| 
 | ||||
| def speedprofile_hash | ||||
|   @speedprofile_hash ||= Digest::SHA1.hexdigest speedprofile_str | ||||
| end | ||||
| 
 | ||||
| def osm_hash | ||||
|   @osm_hash ||= Digest::SHA1.hexdigest osm_str | ||||
| end | ||||
| 
 | ||||
| def osm_hash | ||||
|   @osm_hash ||= Digest::SHA1.hexdigest osm_str | ||||
| end | ||||
| 
 | ||||
| def bin_extract_hash | ||||
|   @bin_hash ||= hash_of_file '../osrm-extract' | ||||
| end | ||||
| 
 | ||||
| def bin_prepare_hash | ||||
|   @bin_hash ||= hash_of_file '../osrm-prepare' | ||||
| end | ||||
| 
 | ||||
| #combine state of data, speedprofile and binaries into a hash that identifies the exact test scenario | ||||
| def fingerprint | ||||
|   @fingerprint ||= Digest::SHA1.hexdigest "#{bin_extract_hash}-#{bin_prepare_hash}-#{speedprofile_hash}-#{osm_hash}" | ||||
| end | ||||
| 
 | ||||
							
								
								
									
										17
									
								
								features/support/hooks.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								features/support/hooks.rb
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | ||||
| Before do |scenario| | ||||
|   @scenario_title = scenario.title | ||||
|   @scenario_time = Time.now.strftime("%Y-%m-%dT%H:%m:%SZ") | ||||
|   reset_data | ||||
|   @has_logged_preprocess_info = false | ||||
|   @has_logged_scenario_info = false | ||||
| end | ||||
| 
 | ||||
| Around('@routing') do |scenario, block| | ||||
|   Timeout.timeout(10) do | ||||
|     block.call | ||||
|   end | ||||
| end | ||||
| 
 | ||||
| After do | ||||
|   osrm_kill | ||||
| end | ||||
							
								
								
									
										71
									
								
								features/support/launch.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								features/support/launch.rb
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,71 @@ | ||||
| require 'socket' | ||||
| require 'sys/proctable' | ||||
| 
 | ||||
| class OSRMLauncher | ||||
|   def initialize &block | ||||
|     Dir.chdir TEST_FOLDER do | ||||
|       osrm_up | ||||
|       yield | ||||
|       osrm_down | ||||
|     end | ||||
|   end | ||||
| end | ||||
| 
 | ||||
| def each_process name, &block | ||||
|   Sys::ProcTable.ps do |process| | ||||
|     if process.comm.strip == name.strip | ||||
|       yield process.pid.to_i, process.state.strip | ||||
|     end | ||||
|   end | ||||
| end | ||||
| 
 | ||||
| def osrm_up? | ||||
|   find_pid('osrm-routed') != nil | ||||
| end | ||||
| 
 | ||||
| def find_pid name | ||||
|   each_process(name) { |pid,state| return pid.to_i } | ||||
|   return nil | ||||
| end | ||||
| 
 | ||||
| def osrm_up | ||||
|   return if osrm_up? | ||||
|   pipe = IO.popen('../osrm-routed 1>>osrm-routed.log 2>>osrm-routed.log') | ||||
|   timeout = 5 | ||||
|   (timeout*10).times do | ||||
|     begin | ||||
|       socket = TCPSocket.new('localhost', 5000) | ||||
|       socket.puts 'ping' | ||||
|     rescue Errno::ECONNREFUSED | ||||
|       sleep 0.1 | ||||
|     end | ||||
|   end | ||||
|   sleep 0.1 | ||||
| end | ||||
| 
 | ||||
| def osrm_down | ||||
|   each_process('osrm-routed') { |pid,state| Process.kill 'TERM', pid } | ||||
|   each_process('osrm-prepare') { |pid,state| Process.kill 'TERM', pid } | ||||
|   each_process('osrm-extract') { |pid,state| Process.kill 'TERM', pid } | ||||
|   wait_for_shutdown 'osrm-routed' | ||||
|   wait_for_shutdown 'osrm-prepare' | ||||
|   wait_for_shutdown 'osrm-extract'   | ||||
| end | ||||
| 
 | ||||
| def osrm_kill | ||||
|   each_process('osrm-routed') { |pid,state| Process.kill 'KILL', pid } | ||||
|   each_process('osrm-prepare') { |pid,state| Process.kill 'KILL', pid } | ||||
|   each_process('osrm-extract') { |pid,state| Process.kill 'KILL', pid } | ||||
|   wait_for_shutdown 'osrm-routed' | ||||
|   wait_for_shutdown 'osrm-prepare' | ||||
|   wait_for_shutdown 'osrm-extract'   | ||||
| end | ||||
| 
 | ||||
| def wait_for_shutdown name | ||||
|   timeout = 10 | ||||
|   (timeout*10).times do | ||||
|     return if find_pid(name) == nil | ||||
|     sleep 0.1 | ||||
|   end | ||||
|   raise "*** Could not terminate #{name}." | ||||
| end | ||||
							
								
								
									
										66
									
								
								features/support/log.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								features/support/log.rb
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,66 @@ | ||||
| def log s='', type=nil | ||||
|   if type == :preprocess | ||||
|     file = PREPROCESS_LOG_FILE | ||||
|   else | ||||
|     file = LOG_FILE | ||||
|   end | ||||
|   File.open(file, 'a') {|f| f.write("#{s}\n") } | ||||
| end | ||||
| 
 | ||||
| 
 | ||||
| def log_scenario_fail_info | ||||
|   return if @has_logged_scenario_info | ||||
|   log "=========================================" | ||||
|   log "Failed scenario: #{@scenario_title}" | ||||
|   log "Time: #{@scenario_time}" | ||||
|   log | ||||
|   log '```xml' #so output can be posted directly to github comment fields | ||||
|   log osm_str.strip | ||||
|   log '```' | ||||
|   log | ||||
|   log speedprofile_str | ||||
|   log | ||||
|   @has_logged_scenario_info = true | ||||
| end | ||||
| 
 | ||||
| def log_fail expected,actual,failed | ||||
|   log_scenario_fail_info | ||||
|   log "== " | ||||
|   log "Expected: #{expected}" | ||||
|   log "Got: #{actual}" | ||||
|   log | ||||
|   failed.each do |fail| | ||||
|     log "Attempt: #{fail[:attempt]}" | ||||
|     log "Query: #{fail[:query]}" | ||||
|     log "Response: #{fail[:response].body}" | ||||
|     log | ||||
|   end | ||||
| end | ||||
| 
 | ||||
| 
 | ||||
| def log_preprocess_info | ||||
|   return if @has_logged_preprocess_info | ||||
|   log "=========================================", :preprocess | ||||
|   log "Preprocessing data for scenario: #{@scenario_title}", :preprocess | ||||
|   log "Time: #{@scenario_time}", :preprocess | ||||
|   log '', :preprocess | ||||
|   log "== OSM data:", :preprocess | ||||
|   log '```xml', :preprocess #so output can be posted directly to github comment fields | ||||
|   log osm_str, :preprocess | ||||
|   log '```', :preprocess | ||||
|   log '', :preprocess | ||||
|   log "== Speed profile:", :preprocess | ||||
|   log speedprofile_str.strip, :preprocess | ||||
|   log '', :preprocess | ||||
|   @has_logged_preprocess_info = true | ||||
| end | ||||
| 
 | ||||
| def log_preprocess str | ||||
|   log_preprocess_info | ||||
|   log str, :preprocess | ||||
| end | ||||
| 
 | ||||
| def log_preprocess_done | ||||
| end | ||||
| 
 | ||||
| 
 | ||||
							
								
								
									
										14
									
								
								features/support/osmlib.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								features/support/osmlib.rb
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,14 @@ | ||||
| #monkey-patch osmlib to fix a bug | ||||
| 
 | ||||
| module OSM | ||||
|   class Way | ||||
|     def to_xml(xml) | ||||
|       xml.way(attributes) do | ||||
|         nodes.each do |node| | ||||
|           xml.nd(:ref => node) | ||||
|         end | ||||
|         tags.to_xml(xml) | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| end | ||||
							
								
								
									
										68
									
								
								features/support/route.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								features/support/route.rb
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,68 @@ | ||||
| require 'net/http' | ||||
| 
 | ||||
| def request_route a,b | ||||
|   @query = "http://localhost:5000/viaroute&start=#{a}&dest=#{b}&output=json&geomformat=cmp" | ||||
|   #log @query | ||||
|   uri = URI.parse @query | ||||
|   Net::HTTP.get_response uri | ||||
| rescue Errno::ECONNREFUSED => e | ||||
|   raise "*** osrm-routed is not running." | ||||
| rescue Timeout::Error | ||||
|   raise "*** osrm-routed did not respond." | ||||
| end | ||||
| 
 | ||||
| def parse_response response | ||||
|   if response.code == "200" && response.body.empty? == false | ||||
|     json = JSON.parse response.body | ||||
|     if json['status'] == 0 | ||||
|       route = way_list json['route_instructions'] | ||||
|       if route.empty? | ||||
|         "Empty route: #{json['route_instructions']}" | ||||
|       else | ||||
|         "Route: #{route}" | ||||
|       end | ||||
|     elsif json['status'] == 207 | ||||
|       "No route" | ||||
|     else | ||||
|       "Status: #{json['status']}" | ||||
|     end | ||||
|   else | ||||
|     "HTTP: #{response.code}" | ||||
|   end | ||||
| end | ||||
| 
 | ||||
| def got_route? response | ||||
|   if response.code == "200" && !response.body.empty? | ||||
|     json = JSON.parse response.body | ||||
|     if json['status'] == 0 | ||||
|       return way_list( json['route_instructions']).empty? == false | ||||
|     end | ||||
|   end | ||||
|   false | ||||
| end | ||||
| 
 | ||||
| def route_status response | ||||
|   if response.code == "200" && !response.body.empty? | ||||
|     json = JSON.parse response.body | ||||
|     if json['status'] == 0 | ||||
|       if way_list( json['route_instructions']).empty? | ||||
|         return 'Empty route' | ||||
|       else | ||||
|         return 'x' | ||||
|       end | ||||
|     elsif json['status'] == 207 | ||||
|       '' | ||||
|     else | ||||
|       "Status #{json['status']}" | ||||
|     end | ||||
|   else | ||||
|     "HTTP #{response.code}" | ||||
|   end | ||||
| end | ||||
| 
 | ||||
| def way_list instructions | ||||
|   instructions. | ||||
|   #reject { |i| i[2]<=1 }.      #FIXME temporary hack to ignore instructions with length==0 | ||||
|   map { |r| r[1] }. | ||||
|   reject(&:empty?).join(',') | ||||
| end | ||||
							
								
								
									
										29
									
								
								features/weight.feature
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								features/weight.feature
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,29 @@ | ||||
| @routing @weight | ||||
| Feature: Choosing route based on length, speed, etc | ||||
| 	 | ||||
| 	Scenario: Pick the geometrically shortest route, way types being equal | ||||
| 		Given the nodes | ||||
| 		 |   | s |   | | ||||
| 		 |   | t |   | | ||||
| 		 | a |   | b | | ||||
| 
 | ||||
| 		And the ways | ||||
| 		 | nodes | | ||||
| 		 | atb   | | ||||
| 		 | asb   | | ||||
| 
 | ||||
| 		When I route I should get | ||||
| 		 | from | to | route | | ||||
| 		 | a    | b  | atb   | | ||||
| 		 | a    | b  | atb   | | ||||
| 
 | ||||
| 	Scenario: Pick the fastest way type, lengths being equal | ||||
| 		Given the nodes | ||||
| 		 | a | s | | ||||
| 		 | p | b | | ||||
| 	 | ||||
| 		And the ways | ||||
| 		 | nodes | highway   | | ||||
| 		 | apb   | primary   | | ||||
| 		 | asb   | secondary | | ||||
| 
 | ||||
							
								
								
									
										27
									
								
								speedprofiles/bicycle.ini
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								speedprofiles/bicycle.ini
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,27 @@ | ||||
| [bicycle] | ||||
| 	accessTag		= bicycle | ||||
| 	defaultSpeed	= 17 | ||||
| 	obeyOneways		= yes | ||||
| 	useRestrictions = yes | ||||
| 	obeyBollards	= no | ||||
| 
 | ||||
| 	cycleway		= 19 | ||||
| 	primary         = 19 | ||||
| 	primary_link    = 19 | ||||
| 	secondary       = 17 | ||||
| 	secondary_link  = 17 | ||||
| 	tertiary        = 15 | ||||
| 	residential     = 15 | ||||
| 	unclassified    = 15 | ||||
| 	living_street   = 13 | ||||
| 	road		    = 13 | ||||
| 	service         = 12 | ||||
| 	track			= 12 | ||||
| 	path			= 12 | ||||
| 	footway			= 10 | ||||
| 	pedestrian		= 5 | ||||
| 	pier			= 5 | ||||
| 	steps			= 3 | ||||
| 	ferry			= 5 | ||||
| 
 | ||||
| 	excludeFromGrid = ferry | ||||
							
								
								
									
										23
									
								
								speedprofiles/car.ini
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								speedprofiles/car.ini
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,23 @@ | ||||
| [car] | ||||
| 	accessTag		= motorcar | ||||
| 	defaultSpeed	= 50 | ||||
| 	obeyOneways		= yes | ||||
| 	useRestrictions = yes | ||||
| 	barrier			= bollard | ||||
| 	 | ||||
| 	motorway		= 100 | ||||
| 	motorway_link   = 90 | ||||
| 	trunk           = 90 | ||||
| 	trunk_link      = 70 | ||||
| 	primary         = 70 | ||||
| 	primary_link    = 60 | ||||
| 	secondary       = 60 | ||||
| 	secondary_link  = 50 | ||||
| 	tertiary        = 50 | ||||
| 	tertiary_link   = 40 | ||||
| 	road			= 40 | ||||
| 	residential     = 40 | ||||
| 	unclassified    = 30 | ||||
| 	service         = 20 | ||||
| 	living_street   = 10 | ||||
| 
 | ||||
							
								
								
									
										26
									
								
								speedprofiles/ebiki.ini
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								speedprofiles/ebiki.ini
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,26 @@ | ||||
| [ebike] | ||||
| accessTag = bicycle | ||||
| obeyOneways = yes | ||||
| defaultSpeed = 25 | ||||
| useRestrictions = no | ||||
| 
 | ||||
| cycleway = 25 | ||||
| trunk = 25 | ||||
| trunk_link = 25 | ||||
| primary = 25 | ||||
| primary_link = 25 | ||||
| secondary = 25 | ||||
| secondary_link = 25 | ||||
| tertiary = 25 | ||||
| unclassified = 25 | ||||
| residential = 25 | ||||
| living_street = 20 | ||||
| service = 20 | ||||
| track = 20 | ||||
| path = 15 | ||||
| pier = 5 | ||||
| pedestrian = 5 | ||||
| footway = 5 | ||||
| ferry = 5 | ||||
| 
 | ||||
| excludeFromGrid = ferry | ||||
							
								
								
									
										3
									
								
								test/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								test/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| /server.ini | ||||
| /cache | ||||
| /speedprofile.ini | ||||
							
								
								
									
										1
									
								
								test/.stxxl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								test/.stxxl
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| disk=/tmp/stxxl,1,syscall | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user