Created
August 16, 2017 22:01
-
-
Save smellman/b6198065fd89fa9ac653b9ad48a40379 to your computer and use it in GitHub Desktop.
Full model code of SOTM 2017 smellman's presentation.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| class OutdoorRouting | |
| include ActiveModel::Model | |
| attr_reader :result, :source_optional_id, :target_optional_id | |
| attr_accessor :source_lon, :source_lat, :target_lon, :target_lat, :search_type, :source_name, :target_name | |
| validates :source_lon, :source_lat, :target_lon, :target_lat, :search_type, presence: true | |
| def initialize(source_lon, source_lat, target_lon, target_lat, search_type, source_name, target_name, source_optional_id = nil, target_optional_id = nil) | |
| @source_lon = source_lon | |
| @source_lat = source_lat | |
| @target_lon = target_lon | |
| @target_lat = target_lat | |
| @search_type = search_type | |
| @source_name = source_name | |
| @target_name = target_name | |
| @source_optional_id = source_optional_id | |
| @target_optional_id = target_optional_id | |
| end | |
| def get_routing(language, text) | |
| if text | |
| return get_routing_text(language) | |
| else | |
| return get_routing_map | |
| end | |
| end | |
| def get_routing_map | |
| text = false | |
| calculate(text) | |
| way = get_routing_wkt | |
| first_position = get_first_position | |
| last_position = get_last_position | |
| return { | |
| type: "outdoor", | |
| floor_id: nil, | |
| floor_name: nil, | |
| source_name: @source_name, | |
| target_name: @target_name, | |
| first_position: first_position, | |
| last_position: last_position, | |
| way: way, | |
| beacon: false, | |
| beacon_message: nil, | |
| beacon_uuid: nil, | |
| beacon_major: nil, | |
| beacon_minor: nil | |
| } | |
| end | |
| def get_routing_text(language) | |
| text = true | |
| calculate(text) | |
| texts = get_routing_texts(language) | |
| return { | |
| type: "outdoor", | |
| floor_id: nil, | |
| floor_name: nil, | |
| source_name: @source_name, | |
| target_name: @target_name, | |
| texts: texts, | |
| beacon: false, | |
| beacon_message: nil, | |
| beacon_uuid: nil, | |
| beacon_major: nil, | |
| beacon_minor: nil | |
| } | |
| end | |
| def get_cost(text) | |
| calculate(text) | |
| if (text) | |
| if @result.count == 0 | |
| return Float::INFINITY | |
| end | |
| return @result.inject(0.0){|sum, n| sum + n["cost"]} | |
| end | |
| cost = @result[0]["cost"] | |
| if cost.nil? | |
| return Float::INFINITY | |
| end | |
| return cost | |
| end | |
| def get_routing_wkt | |
| @result[0]["geom_text"] | |
| end | |
| def get_routing_texts(language) | |
| generate_builder | |
| @builder.output(language) | |
| end | |
| def get_first_position | |
| @result[0]["start_position"] | |
| end | |
| def get_last_position | |
| @result[0]["end_position"] | |
| end | |
| private | |
| def generate_builder | |
| return if @builder | |
| @builder = RoutingTextBuilder.new | |
| @result.each_with_index do |r, index| | |
| @builder.put(index, r["heading"], r["length_m"]) | |
| end | |
| @builder.finish | |
| end | |
| def calculate(text) | |
| return if @result | |
| if @search_type == :pedestrain | |
| @result = ActiveRecord::Base.connection.execute(routing_for_pedestrain(text)) | |
| elsif @search_type == :wheelchair | |
| @result = ActiveRecord::Base.connection.execute(routing_for_wheelchair(text)) | |
| elsif @search_type == :blind_low_vision | |
| @result = ActiveRecord::Base.connection.execute(routing_for_blind_low_vision(text)) | |
| end | |
| end | |
| def routing_for_pedestrain(text) | |
| dijkstra_condition = <<-EOS | |
| SELECT id, source, target, length_m as cost | |
| FROM routing.ways | |
| EOS | |
| routing_sql(dijkstra_condition, text) | |
| end | |
| def routing_for_wheelchair(text) | |
| dijkstra_condition = <<-EOS | |
| SELECT | |
| routing.ways.id, routing.ways.source, routing.ways.target, | |
| routing.ways.length_m * | |
| CASE | |
| WHEN routing.osm_ways.tags ? 'incline' THEN 2 | |
| ELSE 1 | |
| END | |
| as cost | |
| FROM routing.ways | |
| JOIN routing.osm_ways | |
| ON (routing.ways.osm_id = routing.osm_ways.osm_id | |
| AND | |
| NOT routing.osm_ways.tags @> 'highway=>steps' | |
| AND | |
| NOT routing.osm_ways.tags @> 'highway=>path' | |
| AND | |
| NOT routing.osm_ways.tags @> 'surface=>wood' | |
| AND | |
| NOT routing.osm_ways.tags @> 'surface=>unpaved') | |
| EOS | |
| routing_sql(dijkstra_condition, text) | |
| end | |
| def routing_for_blind_low_vision(text) | |
| dijkstra_condition = <<-EOS | |
| SELECT | |
| routing.ways.id, routing.ways.source, routing.ways.target, | |
| routing.ways.length_m * | |
| CASE | |
| WHEN routing.osm_ways.tags @> 'highway=>steps' THEN 40 | |
| WHEN routing.osm_ways.tags @> 'tactile_paving=>yes' THEN 0.5 | |
| ELSE 2 | |
| END | |
| as cost | |
| FROM routing.ways | |
| JOIN routing.osm_ways | |
| ON ( | |
| routing.ways.osm_id = routing.osm_ways.osm_id | |
| AND | |
| ( | |
| routing.osm_ways.tags @> 'tactile_paving=>yes' | |
| OR | |
| ( | |
| routing.osm_ways.tags @> 'handrail:left=>yes' | |
| OR | |
| routing.osm_ways.tags @> 'handrail:right=>yes' | |
| OR | |
| routing.osm_ways.tags @> 'handrail=>left' | |
| OR | |
| routing.osm_ways.tags @> 'handrail=>right' | |
| OR | |
| routing.osm_ways.tags @> 'handrail=>yes' | |
| OR | |
| routing.osm_ways.tags @> 'handrail=>both' | |
| ) | |
| ) | |
| ) | |
| EOS | |
| routing_sql(dijkstra_condition, text) | |
| end | |
| def routing_sql(dijkstra_condition, text) | |
| grouping_text = "" | |
| text_query = "" | |
| if text | |
| text_query = <<-EOS | |
| with_geom.osm_id as osm_id, | |
| degrees(ST_azimuth( | |
| ST_StartPoint(ST_MakeLine(route_geom)), | |
| ST_EndPoint(ST_MakeLine(route_geom)) | |
| )) as heading, | |
| routing.osm_ways.tags as tags, | |
| max(with_geom.seq) as max_seq, | |
| EOS | |
| grouping_text = <<-EOS | |
| JOIN routing.osm_ways | |
| ON (with_geom.osm_id = routing.osm_ways.osm_id) | |
| group by with_geom.osm_id, routing.osm_ways.tags | |
| order by max_seq; | |
| EOS | |
| end | |
| <<-EOS | |
| WITH | |
| dijkstra AS ( | |
| SELECT * FROM pgr_dijkstra( | |
| $$#{dijkstra_condition}$$, | |
| (SELECT id FROM routing.ways_vertices_pgr | |
| ORDER BY the_geom <-> ST_SetSRID(ST_Point(#{source_lon},#{source_lat}),4326) LIMIT 1), | |
| (SELECT id FROM routing.ways_vertices_pgr | |
| ORDER BY the_geom <-> ST_SetSRID(ST_Point(#{target_lon},#{target_lat}),4326) LIMIT 1), | |
| false) | |
| ), | |
| with_geom AS ( | |
| SELECT dijkstra.seq, dijkstra.cost, routing.ways.name, routing.ways.osm_id, | |
| CASE | |
| WHEN dijkstra.node = routing.ways.source THEN the_geom | |
| ELSE ST_Reverse(the_geom) | |
| END AS route_geom | |
| FROM dijkstra JOIN routing.ways | |
| ON (edge = ways.id) ORDER BY seq | |
| ) | |
| SELECT | |
| sum(with_geom.cost) as cost, | |
| ST_Length(ST_MakeLine(route_geom)::geography) as length_m, | |
| ST_AsText(ST_MakeLine(route_geom)) as geom_text, | |
| #{text_query} | |
| ST_AsText(ST_StartPoint(ST_MakeLine(route_geom))) as start_position, | |
| ST_AsText(ST_EndPoint(ST_MakeLine(route_geom))) as end_position | |
| FROM with_geom | |
| #{grouping_text} | |
| EOS | |
| end | |
| end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment