parosm 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,19 @@
1
+ cache/
2
+ .rvmrc
3
+ *.gem
4
+ *.rbc
5
+ .bundle
6
+ .config
7
+ .yardoc
8
+ Gemfile.lock
9
+ InstalledFiles
10
+ _yardoc
11
+ coverage
12
+ doc/
13
+ lib/bundler/man
14
+ pkg
15
+ rdoc
16
+ spec/reports
17
+ test/tmp
18
+ test/version_tmp
19
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in parosm.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Geronimo Diaz
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,34 @@
1
+ # Parosm
2
+
3
+ Simple OSM parser. Extracted from [Mormon](https://github.com/geronimod/mormon) to reuse. It include nodes, ways and simple routing information between nodes.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'parosm'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install parosm
18
+
19
+ ## Usage
20
+
21
+ loader = Parosm::Loader.new osm_file
22
+
23
+
24
+ ## Contributing
25
+
26
+ 1. Fork it
27
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
28
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
29
+ 4. Push to the branch (`git push origin my-new-feature`)
30
+ 5. Create new Pull Request
31
+
32
+ ## License
33
+
34
+ See [LICENSE.txt](LICENSE.txt)
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/lib/parosm.rb ADDED
@@ -0,0 +1,3 @@
1
+ require "parosm/loader"
2
+ require "parosm/weight"
3
+
@@ -0,0 +1,243 @@
1
+ require "parosm/version"
2
+
3
+ require 'nokogiri'
4
+ require 'tmpdir'
5
+
6
+ module Parosm
7
+ class Loader
8
+
9
+ @route_types = [:cycle, :car, :train, :foot, :horse]
10
+ @cache_dir = File.join Dir.tmpdir, "parosm", "cache"
11
+
12
+ class << self
13
+ attr_reader :route_types
14
+ attr_accessor :cache_dir
15
+ end
16
+
17
+ attr_reader :options, :routing, :nodes, :ways, :tiles, :routeable_nodes, :route_types,
18
+ :osm_filename
19
+
20
+ def initialize(filename, options = {})
21
+ @options = options
22
+
23
+ @tiles = {}
24
+ @nodes = {}
25
+ @ways = {}
26
+
27
+ @routing = {}
28
+ @routeable_nodes = {}
29
+
30
+ Loader.route_types.each do |type|
31
+ @routing[type] = {}
32
+ @routeable_nodes[type] = {}
33
+ end
34
+
35
+ @osm_filename = filename
36
+ @options[:cache] ? load_cached : parse
37
+ end
38
+
39
+ def report
40
+ report = "Loaded %d nodes,\n" % @nodes.keys.size
41
+ report += "%d ways, and...\n" % @ways.keys.size
42
+
43
+ Loader.route_types.each do |type|
44
+ report += " %d %s routes\n" % [@routing[type].keys.size, type]
45
+ end
46
+
47
+ report
48
+ end
49
+
50
+ def cache_filename
51
+ File.join Loader.cache_dir, File.basename(@osm_filename) + ".pstore"
52
+ end
53
+
54
+ private
55
+ def load_cached
56
+ require "pstore"
57
+
58
+ store_path = cache_filename
59
+
60
+ FileUtils.mkdir_p Loader.cache_dir
61
+ FileUtils.touch store_path
62
+
63
+ store = PStore.new store_path
64
+
65
+ if !File.zero? store_path
66
+ puts "Loading from cache %s..." % store.path
67
+
68
+ store.transaction(true) do
69
+ @tiles = store[:tiles]
70
+ @nodes = store[:nodes]
71
+ @ways = store[:ways]
72
+ @tiles = store[:tiles]
73
+
74
+ Loader.route_types.each do |type|
75
+ @routing[type] = store[:routing][type]
76
+ @routeable_nodes[type] = store[:routeable_nodes][type]
77
+ end
78
+ end
79
+
80
+ else
81
+ puts "Parsing %s..." % @osm_filename
82
+ parse
83
+
84
+ puts "Creating cache %s..." % store.path
85
+ store.transaction do
86
+ store[:tiles] = @tiles
87
+ store[:nodes] = @nodes
88
+ store[:ways] = @ways
89
+ store[:tiles] = @tiles
90
+
91
+ store[:routing] = {}
92
+ store[:routeable_nodes] = {}
93
+
94
+ Loader.route_types.each do |type|
95
+ store[:routing][type] = @routing[type]
96
+ store[:routeable_nodes][type] = @routeable_nodes[type]
97
+ end
98
+ end
99
+ end
100
+
101
+ end
102
+
103
+ def parse
104
+ puts "Loading %s.." % @osm_filename
105
+
106
+ if !File.exists?(@osm_filename)
107
+ print "No such data file %s" % @osm_filename
108
+ return false
109
+ end
110
+
111
+ osm = Nokogiri::XML File.open(@osm_filename)
112
+
113
+ load_nodes osm
114
+ load_ways osm
115
+ end
116
+
117
+ def load_nodes(nokosm)
118
+ nokosm.css('node').each do |node|
119
+ node_id = node[:id]
120
+
121
+ @nodes[node_id] = {
122
+ lat: node[:lat].to_f,
123
+ lon: node[:lon].to_f,
124
+ tags: {}
125
+ }
126
+
127
+ node.css('tag').each do |t|
128
+ k,v = t[:k].to_sym, t[:v]
129
+ @nodes[node_id][:tags][k] = v unless useless_tags.include?(k)
130
+ end
131
+ end
132
+ end
133
+
134
+ def load_ways(nokosm)
135
+ nokosm.css('way').each do |way|
136
+ way_id = way[:id]
137
+
138
+ @ways[way_id] = {
139
+ nodes: way.css('nd').map { |nd| nd[:ref] },
140
+ tags: {}
141
+ }
142
+
143
+ way.css('tag').each do |t|
144
+ k,v = t[:k].to_sym, t[:v]
145
+ @ways[way_id][:tags][k] = v unless useless_tags.include?(k)
146
+ end
147
+
148
+ store_way @ways[way_id]
149
+ end
150
+ end
151
+
152
+ def useless_tags
153
+ [:created_by]
154
+ end
155
+
156
+ def way_access(highway, railway)
157
+ access = {}
158
+ access[:cycle] = [:primary, :secondary, :tertiary, :unclassified, :minor, :cycleway,
159
+ :residential, :track, :service].include? highway.to_sym
160
+
161
+ access[:car] = [:motorway, :trunk, :primary, :secondary, :tertiary,
162
+ :unclassified, :minor, :residential, :service].include? highway.to_sym
163
+
164
+ access[:train] = [:rail, :light_rail, :subway].include? railway.to_sym
165
+ access[:foot] = access[:cycle] || [:footway, :steps].include?(highway.to_sym)
166
+ access[:horse] = [:track, :unclassified, :bridleway].include? highway.to_sym
167
+ access
168
+ end
169
+
170
+ def store_way(way)
171
+ tags = way[:tags]
172
+
173
+ highway = equivalent tags.fetch(:highway, "")
174
+ railway = equivalent tags.fetch(:railway, "")
175
+ oneway = tags.fetch(:oneway, "")
176
+ reversible = !['yes','true','1'].include?(oneway)
177
+
178
+ access = way_access highway, railway
179
+
180
+ # Store routing information
181
+ last = -1
182
+ way[:nodes].each do |node|
183
+ if last != -1
184
+ Loader.route_types.each do |route_type|
185
+ if access[route_type]
186
+ weight = Parosm::Weight.get route_type, highway.to_sym
187
+ add_link(last, node, route_type, weight)
188
+ add_link(node, last, route_type, weight) if reversible || route_type == :foot
189
+ end
190
+ end
191
+ end
192
+ last = node
193
+ end
194
+ end
195
+
196
+ def routeable_from(node, route_type)
197
+ @routeable_nodes[route_type][node] = 1
198
+ end
199
+
200
+ def add_link(fr, to, route_type, weight = 1)
201
+ routeable_from fr, route_type
202
+ return if @routing[route_type][fr].keys.include?(to)
203
+ @routing[route_type][fr][to] = weight
204
+ rescue
205
+ @routing[route_type][fr] = { to => weight }
206
+ end
207
+
208
+ def way_type(tags)
209
+ # Look for a variety of tags (priority order - first one found is used)
210
+ [:highway, :railway, :waterway, :natural].each do |type|
211
+ value = tags.fetch(type, '')
212
+ return equivalent(value) if value
213
+ end
214
+ nil
215
+ end
216
+
217
+ def equivalent(tag)
218
+ {
219
+ primary_link: "primary",
220
+ trunk: "primary",
221
+ trunk_link: "primary",
222
+ secondary_link: "secondary",
223
+ tertiary: "secondary",
224
+ tertiary_link: "secondary",
225
+ residential: "unclassified",
226
+ minor: "unclassified",
227
+ steps: "footway",
228
+ driveway: "service",
229
+ pedestrian: "footway",
230
+ bridleway: "cycleway",
231
+ track: "cycleway",
232
+ arcade: "footway",
233
+ canal: "river",
234
+ riverbank: "river",
235
+ lake: "river",
236
+ light_rail: "railway",
237
+ living_street: "unclassified"
238
+ }[tag.to_sym] || tag
239
+ end
240
+
241
+ end
242
+ end
243
+
@@ -0,0 +1,3 @@
1
+ module Parosm
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,32 @@
1
+ module Parosm
2
+ class Weight
3
+ # Represents an hipotetical velocity of the transport in the way type
4
+ @weightings = {
5
+ motorway: { car: 10 },
6
+ trunk: { car: 10, cycle: 0.05 },
7
+ primary: { cycle: 0.3, car: 2, foot: 1, horse: 0.1 },
8
+ secondary: { cycle: 1, car: 1.5, foot: 1, horse: 0.2 },
9
+ tertiary: { cycle: 1, car: 1, foot: 1, horse: 0.3 },
10
+ unclassified: { cycle: 1, car: 1, foot: 1, horse: 1 },
11
+ minor: { cycle: 1, car: 1, foot: 1, horse: 1 },
12
+ cycleway: { cycle: 3, foot: 0.2 },
13
+ residential: { cycle: 3, car: 0.7, foot: 1, horse: 1 },
14
+ track: { cycle: 1, car: 1, foot: 1, horse: 1, mtb: 3 },
15
+ service: { cycle: 1, car: 1, foot: 1, horse: 1 },
16
+ bridleway: { cycle: 0.8, foot: 1, horse: 10, mtb: 3 },
17
+ footway: { cycle: 0.2, foot: 1 },
18
+ steps: { foot: 1, cycle: 0.3 },
19
+ rail: { train: 1 },
20
+ light_rail: { train: 1 },
21
+ subway: { train: 1 }
22
+ }
23
+
24
+ def self.weightings
25
+ @weightings
26
+ end
27
+
28
+ def self.get(transport, way_type)
29
+ @weightings[way_type] && @weightings[way_type][transport]
30
+ end
31
+ end
32
+ end
data/parosm.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'parosm/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "parosm"
8
+ gem.version = Parosm::VERSION
9
+ gem.authors = ["Geronimo Diaz"]
10
+ gem.email = ["geronimod@gmail.com"]
11
+ gem.description = %q{ OSM Parser }
12
+ gem.summary = %q{ Simple ruby OSM data parser }
13
+ gem.homepage = ""
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.rubyforge_project = "parosm"
21
+
22
+ gem.add_dependency "nokogiri"
23
+ gem.add_development_dependency "rspec"
24
+
25
+ end
@@ -0,0 +1,65 @@
1
+ require 'spec_helper'
2
+ require 'parosm'
3
+
4
+ def spec_osm_file
5
+ File.join File.dirname(__FILE__), "spec.osm"
6
+ end
7
+
8
+ describe Parosm::Loader do
9
+ def common_specs(loader)
10
+ loader.nodes.keys.size.should eq 534
11
+ loader.ways.keys.size.should eq 135
12
+
13
+ loader.routing[:cycle].keys.size.should eq 240
14
+ loader.routing[:car].keys.size.should eq 240
15
+ loader.routing[:train].keys.size.should eq 0
16
+ loader.routing[:foot].keys.size.should eq 281
17
+ loader.routing[:horse].keys.size.should eq 216
18
+ end
19
+
20
+ describe "whitout cache" do
21
+ before :each do
22
+ @loader = Parosm::Loader.new spec_osm_file
23
+ end
24
+
25
+ it "should load the correct data" do
26
+ common_specs @loader
27
+ end
28
+
29
+ it "should has the correct nodes" do
30
+ map = { "448193026" => 1, "448193243" => 1, "448193220" => 1, "318099173" => 1 }
31
+ @loader.routing[:foot]["448193024"].should eq(map)
32
+ end
33
+ end
34
+
35
+ describe "with cache" do
36
+ it "should exists the cached version" do
37
+ @loader = Parosm::Loader.new spec_osm_file, :cache => true
38
+ File.exists?(@loader.cache_filename).should eq true
39
+ File.zero?(@loader.cache_filename).should eq false
40
+ end
41
+
42
+ it "should let change the cache dir" do
43
+ cache_dir = File.join File.dirname(__FILE__), "..", "cache"
44
+ Parosm::Loader.cache_dir = cache_dir
45
+ @loader = Parosm::Loader.new spec_osm_file, :cache => true
46
+ cache_filename = File.join Parosm::Loader.cache_dir, File.basename(spec_osm_file) + ".pstore"
47
+ @loader.cache_filename.should eq cache_filename
48
+ end
49
+
50
+ it "should have stored the same data" do
51
+ @without_cache = Parosm::Loader.new spec_osm_file
52
+ @with_cache = Parosm::Loader.new spec_osm_file, :cache => true
53
+
54
+ common_specs @without_cache
55
+ common_specs @with_cache
56
+
57
+ @without_cache.nodes.should eq @with_cache.nodes
58
+ @without_cache.ways.should eq @with_cache.ways
59
+ @without_cache.routing.should eq @with_cache.routing
60
+ @without_cache.routeable_nodes.should eq @with_cache.routeable_nodes
61
+ end
62
+
63
+ end
64
+
65
+ end