module ActiveRoad module OsmPbfImporter @@relation_required_tags_keys = ["boundary", "admin_level"] @@relation_selected_tags_keys = ["boundary", "admin_level", "ref:INSEE", "name", "addr:postcode", "type"] mattr_reader :relation_required_tags_keys mattr_reader :relation_selected_tags_keys @@way_required_tags_keys = ["highway", "railway", "boundary", "admin_level", "addr:housenumber"] @@way_for_physical_road_required_tags_keys = ["highway", "railway"] @@way_for_boundary_required_tags_keys = ["boundary", "admin_level"] @@way_for_street_number_required_tags_keys = ["addr:housenumber"] @@way_selected_tags_keys = [ "name", "maxspeed", "oneway", "boundary", "admin_level", "addr:housenumber" ] # Add first_node_id and last_node_id @@way_optionnal_tags_keys = ["highway", "maxspeed", "bridge", "tunnel", "toll", "cycleway", "cycleway-right", "cycleway-left", "cycleway-both", "oneway:bicycle", "oneway", "bicycle", "segregated", "foot", "lanes", "lanes:forward", "lanes:forward:bus", "busway:right", "busway:left", "oneway_bus", "boundary", "admin_level", "access", "construction", "junction", "motor_vehicle", "psv", "bus", "addr:city", "addr:country", "addr:state", "addr:street"] mattr_reader :way_required_tags_keys mattr_reader :way_for_physical_road_required_tags_keys mattr_reader :way_for_boundary_required_tags_keys mattr_reader :way_for_street_number_required_tags_keys mattr_reader :way_selected_tags_keys mattr_reader :way_optionnal_tags_keys @@nodes_selected_tags_keys = [ "addr:housenumber", "addr:city", "addr:postcode", "addr:street" ] mattr_reader :nodes_selected_tags_keys @@pg_batch_size = 10000 # Not Rails.logger.debug a high value because postgres failed to allocate memory mattr_reader :pg_batch_size def self.included(base) base.extend(ClassMethods) end module ClassMethods end def pedestrian?(tags) highway_tag_values = %w{pedestrian footway path steps} if tags["highway"].present? && highway_tag_values.include?(tags["highway"]) true else false end end # http://wiki.openstreetmap.org/wiki/FR:Cycleway # http://wiki.openstreetmap.org/wiki/FR:Bicycle def bike?(tags) highway_tag_values = %w{cycleway} bike_tags_keys = ["cycleway:left", "cycleway:right", "cycleway", "cycleway:left"] if (tags["highway"].present? && highway_tag_values.include?(tags["highway"])) || (bike_tags_keys & tags.keys).present? true else false end end # http://wiki.openstreetmap.org/wiki/Key:railway def train?(tags) railway_tag_values = %w{rail tram funicular light_rail subway} if tags["railway"].present? && railway_tag_values.include?(tags["railway"]) true else false end end # http://wiki.openstreetmap.org/wiki/FR:France_roads_tagging def car?(tags) highway_tag_values = %w{motorway trunk trunk_link primary secondary tertiary motorway_link primary_link unclassified service road residential track} if tags["highway"].present? && highway_tag_values.include?(tags["highway"]) true else false end end def required_way?(required_tags, tags) required_tags.each do |require_tag_key| if tags.keys.include?(require_tag_key) return true end end return false end def required_relation?(tags) @@relation_required_tags_keys.each do |require_tag_key| if tags.keys.include?(require_tag_key) return true end end return false end # Return an hash with tag_key => tag_value for osm attributes def selected_tags(tags, selected_tags_keys) {}.tap do |selected_tags| tags.each do |key, value| if selected_tags_keys.include?(key) selected_tags[key] = value end end end end # def extract_tag_value(tag_value) # case tag_value # when "yes" : 1 # when "no" : 0 # when /[0-9].+/i tag_value.to_f # else 0 # end # end def physical_road_conditionnal_costs(way) [].tap do |prcc| prcc << [ "car", Float::MAX] if !way.car prcc << [ "pedestrian", Float::MAX] if !way.pedestrian prcc << [ "bike", Float::MAX] if !way.bike prcc << [ "train", Float::MAX] if !way.train end end def extract_relation_polygon(outer_geometries, inner_geometries = []) outer_rings = join_ways(outer_geometries) inner_rings = join_ways(inner_geometries) # TODO : Fix the case where many outer rings with many inner rings polygons = [].tap do |polygons| outer_rings.each { |outer_ring| polygons << GeoRuby::SimpleFeatures::Polygon.from_linear_rings( [outer_ring] + inner_rings ) } end end def join_ways(ways) closed_ways = [] endpoints_to_ways = EndpointToWayMap.new for way in ways if way.closed? closed_ways << way next end # Are there any existing ways we can join this to? to_join_to = endpoints_to_ways.get_from_either_end(way) if to_join_to.present? joined = way for existing_way in to_join_to joined = join_way(joined, existing_way) endpoints_to_ways.remove_way(existing_way) if joined.closed? closed_ways << joined break end end if !joined.closed? endpoints_to_ways.add_way(joined) end else endpoints_to_ways.add_way(way) end end if endpoints_to_ways.number_of_endpoints != 0 raise StandardError, "Unclosed boundaries" end closed_ways end def join_way(way, other) if way.closed? raise StandardError, "Trying to join a closed way to another" end if other.closed? raise StandardError, "Trying to join a way to a closed way" end if way.points.first == other.points.first new_points = other.reverse.points[0..-2] + way.points elsif way.points.first == other.points.last new_points = other.points[0..-2] + way.points elsif way.points.last == other.points.first new_points = way.points[0..-2] + other.points elsif way.points.last == other.points.last new_points = way.points[0..-2] + other.reverse.points else raise StandardError, "Trying to join two ways with no end point in common" end GeoRuby::SimpleFeatures::LineString.from_points(new_points) end class EndpointToWayMap attr_accessor :endpoints def initialize @endpoints = {} end def add_way(way) if get_from_either_end(way).present? raise StandardError, "Call to add_way would overwrite existing way(s)" end self.endpoints[way.points.first] = way self.endpoints[way.points.last] = way end def remove_way(way) endpoints.delete(way.points.first) endpoints.delete(way.points.last) end def get_from_either_end(way) [].tap do |selected_end_points| selected_end_points << endpoints[way.points.first] if endpoints.include?(way.points.first) selected_end_points << endpoints[way.points.last] if endpoints.include?(way.points.last) end end def number_of_endpoints return endpoints.size end end class Node attr_accessor :id, :lon, :lat, :ways, :end_of_way, :addr_housenumber, :tags def initialize(id, lon, lat, addr_housenumber = "", ways = [], end_of_way = false, tags = {} ) @id = id @lon = lon @lat = lat @addr_housenumber = addr_housenumber @ways = ways @end_of_way = end_of_way @tags = tags end def add_way(id) @ways << id end def marshal_dump [@id, @lon, @lat, @addr_housenumber, @ways, @end_of_way, @tags] end def marshal_load array @id, @lon, @lat, @addr_housenumber, @ways, @end_of_way, @tags = array end def used? ( ways.present? && ways.size > 1 ) || end_of_way end end class Way attr_accessor :id, :nodes, :car, :bike, :train, :pedestrian, :name, :maxspeed, :oneway, :boundary, :admin_level, :addr_housenumber, :options def initialize(id, nodes = [], car = false, bike = false, train = false, pedestrian = false, name = "", maxspeed = 0, oneway = false, boundary = "", admin_level = "", addr_housenumber = "", options = {}) @id = id @nodes = nodes @car = car @bike = bike @train = train @pedestrian = pedestrian @name = name @maxspeed = maxspeed @oneway = oneway @boundary = boundary @admin_level = admin_level @addr_housenumber = addr_housenumber @options = options end def add_node(id) @nodes << id end def marshal_dump [@id, @nodes, @car, @bike, @train, @pedestrian, @name, @maxspeed, @oneway, @boundary, @admin_level, @addr_housenumber, @options] end def marshal_load array @id, @nodes, @car, @bike, @train, @pedestrian, @name, @maxspeed, @oneway, @boundary, @admin_level, @addr_housenumber, @options = array end end end end