active_road 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. data/.gitignore +9 -0
  2. data/.rvmrc +1 -0
  3. data/.travis.yml +11 -0
  4. data/Gemfile +14 -0
  5. data/Guardfile +26 -0
  6. data/LICENSE.txt +19 -0
  7. data/README.md +37 -0
  8. data/Rakefile +46 -0
  9. data/active_road.gemspec +38 -0
  10. data/app/models/active_road/access_link.rb +44 -0
  11. data/app/models/active_road/access_point.rb +65 -0
  12. data/app/models/active_road/base.rb +5 -0
  13. data/app/models/active_road/junction.rb +52 -0
  14. data/app/models/active_road/junction_conditionnal_cost.rb +13 -0
  15. data/app/models/active_road/logical_road.rb +41 -0
  16. data/app/models/active_road/path.rb +85 -0
  17. data/app/models/active_road/physical_road.rb +65 -0
  18. data/app/models/active_road/physical_road_conditionnal_cost.rb +10 -0
  19. data/app/models/active_road/physical_road_filter.rb +41 -0
  20. data/app/models/active_road/street_number.rb +81 -0
  21. data/app/models/active_road/terra_import.rb +148 -0
  22. data/config/database.yml +20 -0
  23. data/config/database.yml.ci +12 -0
  24. data/config/routes.rb +2 -0
  25. data/db/init.sql +6 -0
  26. data/db/migrate/20110914160756_create_street_numbers.rb +18 -0
  27. data/db/migrate/20120201114800_create_physical_roads.rb +17 -0
  28. data/db/migrate/20120201162800_create_logical_roads.rb +16 -0
  29. data/db/migrate/20120203154500_create_junctions.rb +25 -0
  30. data/db/migrate/20120401083409_create_physical_roads_spatial_index.rb +9 -0
  31. data/db/migrate/20120419093427_add_kind_to_physical_roads.rb +10 -0
  32. data/db/migrate/20121010125851_create_junction_conditionnal_costs.rb +13 -0
  33. data/db/migrate/20121011124923_create_physical_road_conditionnal_costs.rb +13 -0
  34. data/db/migrate/20121012134251_add_start_end_ref_to_junction_conditionnal_cost.rb +6 -0
  35. data/db/migrate/20121012134440_add_tags_junction.rb +5 -0
  36. data/db/migrate/20121012134457_add_tags_physical_road.rb +5 -0
  37. data/db/migrate/20121106095002_add_objectid_to_street_number.rb +5 -0
  38. data/db/migrate/20130419155438_add_length_and_minimum_width_to_physical_road.rb +6 -0
  39. data/db/migrate/20130507162801_setup_hstore.rb +9 -0
  40. data/db/migrate/20130509075631_change_tags_type_for_physical_road.rb +10 -0
  41. data/db/migrate/20130509081745_index_physical_roads_tags.rb +9 -0
  42. data/db/migrate/20130513134422_change_tags_type_for_junction.rb +10 -0
  43. data/db/migrate/20130513134511_index_junctions_tags.rb +9 -0
  44. data/db/migrate/20130607114951_change_length_name_for_physical_road.rb +11 -0
  45. data/db/migrate/20130801151637_add_constraints_to_physical_roads.rb +37 -0
  46. data/db/migrate/20130809155019_add_height_and_waiting_constraint_to_junction.rb +6 -0
  47. data/db/migrate/20130812143049_fix_waiting_constraint_type_for_junction.rb +11 -0
  48. data/lib/active_road.rb +19 -0
  49. data/lib/active_road/engine.rb +13 -0
  50. data/lib/active_road/migration.rb +15 -0
  51. data/lib/active_road/shortest_path.rb +2 -0
  52. data/lib/active_road/shortest_path/finder.rb +172 -0
  53. data/lib/active_road/version.rb +3 -0
  54. data/lib/tasks/activeroad_tasks.rake +4 -0
  55. data/log/.gitignore +0 -0
  56. data/script/console +2 -0
  57. data/script/import-tiger-numbers +201 -0
  58. data/script/rails +9 -0
  59. data/spec/dummy/README.rdoc +261 -0
  60. data/spec/dummy/Rakefile +6 -0
  61. data/spec/dummy/app/assets/javascripts/application.js +15 -0
  62. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  63. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  64. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  65. data/spec/dummy/app/mailers/.gitkeep +0 -0
  66. data/spec/dummy/app/models/.gitkeep +0 -0
  67. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  68. data/spec/dummy/config.ru +4 -0
  69. data/spec/dummy/config/application.rb +56 -0
  70. data/spec/dummy/config/boot.rb +10 -0
  71. data/spec/dummy/config/database.yml +20 -0
  72. data/spec/dummy/config/environment.rb +5 -0
  73. data/spec/dummy/config/environments/development.rb +37 -0
  74. data/spec/dummy/config/environments/production.rb +67 -0
  75. data/spec/dummy/config/environments/test.rb +37 -0
  76. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  77. data/spec/dummy/config/initializers/inflections.rb +15 -0
  78. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  79. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  80. data/spec/dummy/config/initializers/session_store.rb +8 -0
  81. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  82. data/spec/dummy/config/locales/en.yml +5 -0
  83. data/spec/dummy/config/routes.rb +4 -0
  84. data/spec/dummy/db/schema.rb +87 -0
  85. data/spec/dummy/db/structure.sql +10250 -0
  86. data/spec/dummy/lib/assets/.gitkeep +0 -0
  87. data/spec/dummy/log/.gitkeep +0 -0
  88. data/spec/dummy/public/404.html +26 -0
  89. data/spec/dummy/public/422.html +26 -0
  90. data/spec/dummy/public/500.html +25 -0
  91. data/spec/dummy/public/favicon.ico +0 -0
  92. data/spec/dummy/script/rails +6 -0
  93. data/spec/factories/junction.rb +18 -0
  94. data/spec/factories/junction_conditionnal_cost.rb +13 -0
  95. data/spec/factories/logical_road.rb +8 -0
  96. data/spec/factories/physical_road.rb +9 -0
  97. data/spec/factories/physical_road_conditionnal_cost.rb +9 -0
  98. data/spec/factories/street_number.rb +11 -0
  99. data/spec/fixtures/terra.xml +1772 -0
  100. data/spec/fixtures/terra_minimal.xml +28 -0
  101. data/spec/fixtures/trajectory_arc.xml +10 -0
  102. data/spec/fixtures/trajectory_node.xml +11 -0
  103. data/spec/lib/active_road/shortest_path/finder_spec.rb +157 -0
  104. data/spec/models/active_road/access_point_spec.rb +36 -0
  105. data/spec/models/active_road/junction_conditionnal_cost_spec.rb +28 -0
  106. data/spec/models/active_road/junction_spec.rb +25 -0
  107. data/spec/models/active_road/logical_road_spec.rb +46 -0
  108. data/spec/models/active_road/path_spec.rb +7 -0
  109. data/spec/models/active_road/physical_road_conditionnal_cost_spec.rb +19 -0
  110. data/spec/models/active_road/physical_road_filter_spec.rb +85 -0
  111. data/spec/models/active_road/physical_road_spec.rb +17 -0
  112. data/spec/models/active_road/street_number_spec.rb +78 -0
  113. data/spec/models/active_road/terra_import_spec.rb +113 -0
  114. data/spec/spec_helper.rb +46 -0
  115. data/spec/support/georuby_ext.rb +15 -0
  116. data/travis/before_install.sh +10 -0
  117. data/travis/before_script.sh +9 -0
  118. metadata +460 -0
@@ -0,0 +1,85 @@
1
+ # A path is a link between differents objects :
2
+ # - Departure
3
+ # - Arrival
4
+ class ActiveRoad::Path
5
+
6
+ attr_accessor :departure, :arrival, :physical_road
7
+ alias_method :road, :physical_road
8
+
9
+ def initialize(attributes = {})
10
+ attributes.each do |k, v|
11
+ send("#{k}=", v)
12
+ end
13
+ end
14
+
15
+ def name
16
+ "Path : #{departure} -> #{arrival}"
17
+ end
18
+
19
+ def locations_on_road
20
+ [departure, arrival].collect do |endpoint|
21
+ location =
22
+ if GeoRuby::SimpleFeatures::Point === endpoint
23
+ road.locate_point endpoint
24
+ else
25
+ endpoint.location_on_road road
26
+ end
27
+ location = [0, [location, 1].min].max
28
+ end
29
+ end
30
+
31
+ def length_in_meter
32
+ length_on_road * road.length_in_meter
33
+ end
34
+
35
+ def length_on_road
36
+ begin_on_road, end_on_road = locations_on_road.sort
37
+ end_on_road - begin_on_road
38
+ end
39
+
40
+ def self.all(departure, arrivals, physical_road)
41
+ Array(arrivals).collect do |arrival|
42
+ new :departure => departure, :arrival => arrival, :physical_road => physical_road
43
+ end
44
+ end
45
+
46
+ delegate :access_to_road?, :to => :arrival
47
+
48
+ def paths(tags = {})
49
+ arrival.paths(tags) - [reverse]
50
+ end
51
+
52
+ def reverse
53
+ self.class.new :departure => arrival, :arrival => departure, :physical_road => physical_road
54
+ end
55
+
56
+ def geometry_without_cache
57
+ sorted_locations_on_road = locations_on_road.sort
58
+ reverse = (sorted_locations_on_road != locations_on_road)
59
+ geometry = road.line_substring *sorted_locations_on_road
60
+ geometry = geometry.reverse if reverse
61
+ geometry
62
+ end
63
+
64
+ def geometry_with_cache
65
+ @geometry ||= geometry_without_cache
66
+ end
67
+ alias_method :geometry, :geometry_with_cache
68
+ alias_method :to_geometry, :geometry
69
+
70
+ def to_s
71
+ name
72
+ end
73
+
74
+ def ==(other)
75
+ [:departure, :arrival, :physical_road].all? do |attribute|
76
+ other.respond_to?(attribute) and send(attribute) == other.send(attribute)
77
+ end
78
+ end
79
+ alias_method :eql?, :==
80
+
81
+ def hash
82
+ [departure, arrival, physical_road].hash
83
+ end
84
+
85
+ end
@@ -0,0 +1,65 @@
1
+ require "activerecord-postgres-hstore"
2
+ require "enumerize"
3
+
4
+ module ActiveRoad
5
+ class PhysicalRoad < ActiveRoad::Base
6
+ extend Enumerize
7
+ extend ActiveModel::Naming
8
+
9
+ serialize :tags, ActiveRecord::Coders::Hstore
10
+
11
+ attr_accessible :objectid, :tags, :geometry, :logical_road_id, :length_in_meter, :minimum_width, :covering, :transport_mode, :slope, :cant, :physical_road_type
12
+
13
+ # TODO : Pass covering in array mode???
14
+ enumerize :covering, :in => [:slippery_gravel, :gravel, :asphalt_road, :asphalt_road_damaged, :pavement, :irregular_pavement, :slippery_pavement]
15
+ enumerize :transport_mode, :in => [:pedestrian, :bike]
16
+
17
+ enumerize :minimum_width, :in => [:wide, :enlarged, :narrow, :cramped], :default => :wide
18
+ enumerize :slope, :in => [:flat, :medium, :significant, :steep], :default => :flat
19
+ enumerize :cant, :in => [:flat, :medium, :significant, :steep], :default => :flat
20
+ enumerize :physical_road_type, :in => [:path_link, :stairs, :crossing], :default => :path_link
21
+
22
+ validates_uniqueness_of :objectid
23
+
24
+ has_many :numbers, :class_name => "ActiveRoad::StreetNumber", :inverse_of => :physical_road
25
+ belongs_to :logical_road, :class_name => "ActiveRoad::LogicalRoad"
26
+ has_and_belongs_to_many :junctions, :uniq => true
27
+ has_many :physical_road_conditionnal_costs
28
+
29
+ acts_as_geom :geometry => :line_string
30
+ delegate :locate_point, :interpolate_point, :to => :geometry
31
+
32
+ before_validation :update_length_in_meter
33
+
34
+ def update_length_in_meter
35
+ if ( geometry )
36
+ spherical_factory = ::RGeo::Geographic.spherical_factory
37
+ self.update_attribute :length_in_meter, spherical_factory.line_string(geometry.points.collect(&:to_rgeo)).length
38
+ end
39
+ end
40
+
41
+ def name
42
+ logical_road.try(:name) or objectid
43
+ end
44
+
45
+ alias_method :to_s, :name
46
+
47
+ def self.nearest_to(location, distance = 100)
48
+ with_in(location, distance).closest_to(location)
49
+ end
50
+
51
+ def self.closest_to(location)
52
+ location_as_text = location.to_ewkt(false)
53
+ order("ST_Distance(geometry, GeomFromText('#{location_as_text}', 4326))")
54
+ end
55
+
56
+ def self.with_in(location, distance = 100)
57
+ # FIXME why ST_DWithin doesn't use meters ??
58
+ distance = distance / 1000.0
59
+
60
+ location_as_text = location.to_ewkt(false)
61
+ where "ST_DWithin(ST_GeomFromText(?, 4326), geometry, ?)", location_as_text, distance
62
+ end
63
+
64
+ end
65
+ end
@@ -0,0 +1,10 @@
1
+ module ActiveRoad
2
+ class PhysicalRoadConditionnalCost < ActiveRoad::Base
3
+ belongs_to :physical_road
4
+
5
+ validates_presence_of :physical_road_id
6
+ validates_presence_of :tags
7
+ validates_presence_of :cost
8
+
9
+ end
10
+ end
@@ -0,0 +1,41 @@
1
+ # Filter Physical Road by :
2
+ # - constraints
3
+ class ActiveRoad::PhysicalRoadFilter
4
+ attr_accessor :relation, :constraints
5
+
6
+ def initialize(constraints = {}, relation = ActiveRoad::PhysicalRoad.scoped )
7
+ @relation, @constraints = relation, constraints
8
+ end
9
+
10
+ # Must define an sql request with forbidden tags
11
+ def sql_request
12
+ sql_request = ""
13
+ constraints.each do |key, value|
14
+ if !( (constraints.keys.first.present? && constraints.keys.first == key) )
15
+ sql_request += " AND "
16
+ end
17
+
18
+ sql_request += "(tags -> '#{new_key}')::int > :#{key}"
19
+
20
+ # if key.to_s.include? "min_"
21
+ # new_key = key.to_s.gsub("min_", "")
22
+ # sql_request += "(tags -> '#{new_key}')::int > :#{key}"
23
+ # elsif key.to_s.include? "max_"
24
+ # new_key = key.to_s.gsub("max_", "")
25
+ # sql_request += "(tags -> '#{new_key}')::int < :#{key}"
26
+ # else
27
+ # sql_request += "tags -> '#{key}' != :#{key}"
28
+ # end
29
+ end
30
+ sql_request
31
+ end
32
+
33
+ def filter
34
+ if constraints.present?
35
+ @relation = @relation.where(constraints)
36
+ else
37
+ @relation
38
+ end
39
+ end
40
+
41
+ end
@@ -0,0 +1,81 @@
1
+ class ActiveRoad::StreetNumber < ActiveRoad::Base
2
+ attr_accessible :objectid, :number, :geometry, :physical_road_id, :location_on_road
3
+
4
+ validates_uniqueness_of :objectid
5
+ validates_presence_of :number, :stored_geometry
6
+
7
+ belongs_to :physical_road, :class_name => "ActiveRoad::PhysicalRoad"
8
+ def road
9
+ @road or physical_road
10
+ end
11
+ def road=(road)
12
+ @road ||= road
13
+ end
14
+
15
+ before_validation :compute_locate_on_road, :on => :create
16
+
17
+ def stored_geometry
18
+ read_attribute :geometry
19
+ end
20
+
21
+ def geometry
22
+ stored_geometry or computed_geometry
23
+ end
24
+
25
+ def computed_geometry
26
+ road.at computed_location_on_road
27
+ end
28
+
29
+ def location_on_road
30
+ stored_location_on_road or computed_location_on_road
31
+ end
32
+
33
+ def stored_location_on_road
34
+ read_attribute :location_on_road
35
+ end
36
+
37
+ def compute_locate_on_road
38
+ if stored_location_on_road.nil? and stored_geometry
39
+ self.location_on_road = road.geometry.locate_point(stored_geometry)
40
+ end
41
+ end
42
+
43
+ # TODO rename into estimated_location_on_road
44
+ def computed_location_on_road
45
+ if previous and self.next
46
+ number_ratio = (number.to_i - previous.number.to_i) / (self.next.number.to_i - previous.number.to_i).to_f
47
+ previous.location_on_road + number_ratio * (self.next.location_on_road - previous.location_on_road)
48
+ end
49
+ end
50
+
51
+ def previous
52
+ @previous ||= road.numbers.find :first, :conditions => ["number < ?", number], :limit => 1, :order => "number desc"
53
+ end
54
+
55
+ def next
56
+ @next ||= road.numbers.find :first, :conditions => ["number > ?", number], :limit => 1, :order => "number"
57
+ end
58
+
59
+ def number
60
+ Number.new read_attribute(:number)
61
+ end
62
+
63
+ class Number < String
64
+
65
+ alias_method :numeric_value, :to_i
66
+
67
+ def suffix
68
+ gsub(/^[0-9]+/,'')
69
+ end
70
+
71
+ def +(value)
72
+ self.class.new "#{numeric_value+value}#{suffix}"
73
+ end
74
+
75
+ def -(value)
76
+ self.class.new "#{numeric_value-value}#{suffix}"
77
+ end
78
+
79
+ end
80
+
81
+ end
@@ -0,0 +1,148 @@
1
+ module ActiveRoad
2
+ class TerraImport
3
+ attr_reader :parser
4
+
5
+ def initialize(xml_file)
6
+ @parser = ::Saxerator.parser(File.new(xml_file))
7
+ end
8
+
9
+ def extract
10
+ parser.for_tag(:LogicalRoad).each do |logical_road|
11
+ LogicalRoadXml.new(logical_road).import
12
+ end
13
+
14
+ parser.for_tag(:TrajectoryArc).each do |physical_road|
15
+ TrajectoryArcXml.new(physical_road).import
16
+ end
17
+
18
+ parser.for_tag(:TrajectoryNode).each do |junction|
19
+ TrajectoryNodeXml.new(junction).import
20
+ end
21
+
22
+ parser.for_tag(:StreetNumber).each do |street_number|
23
+ StreetNumberXml.new(street_number).import
24
+ end
25
+
26
+ end
27
+
28
+ class ElementXml
29
+ attr_reader :xml
30
+
31
+ def initialize(xml)
32
+ @xml = xml
33
+ end
34
+
35
+ def objectid
36
+ xml['ObjectId']
37
+ end
38
+
39
+ def geometry
40
+ GeoRuby::SimpleFeatures::Geometry.from_ewkt( xml['Geometry'] )
41
+ end
42
+
43
+ end
44
+
45
+ class LogicalRoadXml < ElementXml
46
+
47
+ def name
48
+ xml['Name']
49
+ end
50
+
51
+ def import
52
+ ActiveRoad::LogicalRoad.create :name => name, :objectid => objectid
53
+ end
54
+
55
+ end
56
+
57
+ class TrajectoryArcXml < ElementXml
58
+
59
+ def logical_road
60
+ ActiveRoad::LogicalRoad.find_by_objectid( logical_road_id )
61
+ end
62
+
63
+ def logical_road_id
64
+ xml['LogicalRoadRef']
65
+ end
66
+
67
+ def minimum_width
68
+ xml['MinimumWidth']
69
+ end
70
+
71
+ def length
72
+ xml['Length']
73
+ end
74
+
75
+ def tags
76
+ {}
77
+ end
78
+
79
+ def import
80
+ ActiveRoad::PhysicalRoad.create(:geometry => geometry, :tags => tags, :logical_road_id => logical_road_id, :objectid => objectid, :minimum_with => minimum_width, :length_in_meter => length) if (geometry) #&& logical_road)
81
+ end
82
+
83
+ end
84
+
85
+ class TrajectoryNodeXml < ElementXml
86
+
87
+ def tags
88
+ {} #xml['Tags'].to_s
89
+ end
90
+
91
+ # def height
92
+ # xml['Height'] || 0
93
+ # end
94
+
95
+ # def physical_road
96
+ # ActiveRoad::PhysicalRoad.find_by_objectid( physical_road_id )
97
+ # end
98
+
99
+ # def physical_road_id
100
+ # xml['PhysicalRoadRef']
101
+ # end
102
+
103
+ def physical_roads
104
+ physical_roads = []
105
+ if(xml['TrajectoryArcRef'].class == Saxerator::Builder::StringElement)
106
+ physical_roads << ActiveRoad::PhysicalRoad.find_by_objectid( xml['TrajectoryArcRef'].to_s )
107
+ else
108
+ xml['TrajectoryArcRef'].each do |trajectory_arc_ref|
109
+ physical_road = ActiveRoad::PhysicalRoad.find_by_objectid( trajectory_arc_ref.to_s )
110
+ physical_roads << physical_road if physical_road.present?
111
+ end
112
+ end
113
+ physical_roads
114
+ end
115
+
116
+ def import
117
+ junction = ActiveRoad::Junction.create :objectid => objectid, :tags => tags, :geometry => geometry
118
+ junction.physical_roads << physical_roads
119
+ end
120
+
121
+ end
122
+
123
+ class StreetNumberXml < ElementXml
124
+ # TODO : Fix location_on_road value
125
+ def physical_road
126
+ ActiveRoad::PhysicalRoad.find_by_objectid( physical_road_ref )
127
+ end
128
+
129
+ def physical_road_ref
130
+ xml['TrajectoryArcRef']
131
+ end
132
+
133
+ def number
134
+ xml['Number']
135
+ end
136
+
137
+ def location_on_road
138
+ xml['LocationOnRoad'] || 0
139
+ end
140
+
141
+ def import
142
+ ActiveRoad::StreetNumber.create(:number => number , :objectid => objectid, :geometry => geometry, :location_on_road => location_on_road)#, :physical_road_id => physical_road.id)
143
+ end
144
+
145
+ end
146
+
147
+ end
148
+ end
@@ -0,0 +1,20 @@
1
+ development:
2
+ adapter: postgresql
3
+ database: roads
4
+ username: roads
5
+ password: roads0
6
+ template: template_postgis
7
+
8
+ test:
9
+ adapter: postgresql
10
+ database: roads_test
11
+ username: roads
12
+ password: roads0
13
+ template: template_postgis
14
+
15
+ osm:
16
+ adapter: postgresql
17
+ database: osm
18
+ username: osm
19
+ password: osm
20
+ host: osm.dbx2.dryade.priv