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 +19 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +34 -0
- data/Rakefile +1 -0
- data/lib/parosm.rb +3 -0
- data/lib/parosm/loader.rb +243 -0
- data/lib/parosm/version.rb +3 -0
- data/lib/parosm/weight.rb +32 -0
- data/parosm.gemspec +25 -0
- data/spec/parosm_spec.rb +65 -0
- data/spec/spec.osm +2459 -0
- data/spec/spec_helper.rb +7 -0
- metadata +93 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
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,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,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
|
data/spec/parosm_spec.rb
ADDED
@@ -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
|