parosm 0.0.1

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.
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