osm-import 0.0.0

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/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Alexey Noskov
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
File without changes
data/lib/osm-import.rb ADDED
@@ -0,0 +1 @@
1
+ require 'osm_import'
@@ -0,0 +1,18 @@
1
+ require 'osm_import/mapper'
2
+
3
+ class OsmImport::Mapper::Address < OsmImport::Mapper::Base
4
+
5
+ def assigns
6
+ { "street" => "src.tags->'addr:street'", "housenumber" => "src.tags->'addr:housenumber'", "city" => "src.tags->'addr:city'" , "postcode" => "src.tags->'addr:postcode'" }
7
+ end
8
+
9
+ def fields
10
+ { "street" => "VARCHAR(255)", "housenumber" => 'VARCHAR(255)', "city" => "VARCHAR(255)", "postcode" => "VARCHAR(100)" }
11
+ end
12
+
13
+ def after_import(tt)
14
+ tt.conn.exec "UPDATE #{tt.name} SET city = NULL WHERE city = 'undefined'"
15
+ tt.conn.exec "UPDATE #{tt.name} SET city = c.name FROM raw_osm_polygon c WHERE geometry && way AND ST_Contains(way, Geometry(geometry)) AND c.place IN ('city','town','village') AND c.name IS NOT NULL AND c.name <> '' AND city IS NULL"
16
+ end
17
+
18
+ end
@@ -0,0 +1,25 @@
1
+ require 'osm_import/mapper'
2
+
3
+ class OsmImport::Mapper::Center < OsmImport::Mapper::Base
4
+
5
+ def assigns
6
+ {}
7
+ end
8
+
9
+ def fields
10
+ {}
11
+ end
12
+
13
+ def indexes
14
+ { name => "GIST(#{name})" }
15
+ end
16
+
17
+ def after_create(tt)
18
+ tt.add_geometry_column name, :point
19
+ end
20
+
21
+ def after_import(tt)
22
+ tt.conn.exec "UPDATE #{tt.name} SET #{name} = ST_PointOnSurface(Geometry(geometry)) WHERE #{name} IS NULL"
23
+ end
24
+
25
+ end
@@ -0,0 +1,91 @@
1
+ require 'osm_import/mapper'
2
+
3
+ class OsmImport::Mapper::Type < OsmImport::Mapper::Base
4
+
5
+ attr_reader :mappings
6
+
7
+ def initialize(*args)
8
+ super
9
+
10
+ @mappings = []
11
+ @multi = false
12
+ end
13
+
14
+ def assigns
15
+ if @multi
16
+ { :type => expression, :type_array => expression_multi }
17
+ else
18
+ { :type => expression }
19
+ end
20
+ end
21
+
22
+ def fields
23
+ if @multi
24
+ { :type => 'VARCHAR(100) NOT NULL', :type_array => 'VARCHAR(100)[]' }
25
+ else
26
+ { :type => 'VARCHAR(100) NOT NULL' }
27
+ end
28
+ end
29
+
30
+ def indexes
31
+ if @multi
32
+ { :type => "BTREE(type)", :type_array => "GIN(type_array)" }
33
+ else
34
+ { :type => "BTREE(type)" }
35
+ end
36
+ end
37
+
38
+ def conditions
39
+ [ @mappings.map{|m| map_cond(m)}.join(' OR ') ]
40
+ end
41
+
42
+ def expression
43
+ "CASE #{@mappings.map{|m| "WHEN #{map_cond(m)} THEN trim(regexp_replace(src.tags->'#{m[0]}', ',.*', ''))"}.join(' ')} END"
44
+ end
45
+
46
+ def expression_multi
47
+ "(#{@mappings.map{|m| "string_to_array(coalesce(CASE WHEN #{map_cond(m)} THEN replace(src.tags->'#{m[0]}',' ','') END,''),',')"}.join(' || ')})"
48
+ end
49
+
50
+ def map_cond(m)
51
+ m[1] ? "(src.tags->'#{m[0]}') IN ('#{m[1].join("','")}')" : "(src.tags->'#{m[0]}') IS NOT NULL"
52
+ end
53
+
54
+ def add_args(*args)
55
+ args.each do |arg|
56
+ if arg.is_a? Hash
57
+ arg.each(&method(:add_arg))
58
+ else
59
+ add_arg arg, nil
60
+ end
61
+ end
62
+ end
63
+
64
+ def add_arg(key, value)
65
+ key = to_key(key)
66
+
67
+ if key == 'multi'
68
+ @multi = value
69
+ else
70
+ @mappings << [key, to_keylist(value)]
71
+ end
72
+ end
73
+
74
+ private
75
+
76
+ def to_key(key)
77
+ raise StandardError.new("Invalid key #{key.inspect}") unless key.is_a? String or key.is_a? Symbol
78
+ key.to_s
79
+ end
80
+
81
+ def to_keylist(list)
82
+ if list.nil?
83
+ list
84
+ elsif list.is_a? Array
85
+ list.map(&method(:to_key))
86
+ else
87
+ [to_key(list)]
88
+ end
89
+ end
90
+
91
+ end
@@ -0,0 +1,48 @@
1
+ require 'osm_import'
2
+
3
+ module OsmImport::Mapper
4
+
5
+ extend OsmImport::Autoloading
6
+
7
+ def self.new(name, type)
8
+ self[(type || name).to_s].new(name)
9
+ end
10
+
11
+ class Base < Struct.new(:name)
12
+
13
+ def fields
14
+ { name => "VARCHAR(255)" }
15
+ end
16
+
17
+ def assigns
18
+ { name => "src.tags->'#{name}'" }
19
+ end
20
+
21
+ def indexes
22
+ {}
23
+ end
24
+
25
+ def after_create(*args)
26
+ end
27
+
28
+ def after_import(*args)
29
+ end
30
+
31
+ end
32
+
33
+ class String < Base
34
+ end
35
+
36
+ class Id < Base
37
+
38
+ def fields
39
+ { name => "INT8" }
40
+ end
41
+
42
+ def assigns
43
+ { name => "(src.tags->'#{name}')::INT8" }
44
+ end
45
+
46
+ end
47
+
48
+ end
@@ -0,0 +1,51 @@
1
+ require 'osm_import'
2
+
3
+ class OsmImport::Schema
4
+
5
+ attr_reader :tables
6
+
7
+ def self.load(file)
8
+ new { eval File.read(file) }
9
+ end
10
+
11
+ def initialize(&block)
12
+ @tables = []
13
+
14
+ dsl.instance_eval(&block) if block
15
+ end
16
+
17
+ def dsl
18
+ @dsl ||= Dsl.new self
19
+ end
20
+
21
+ class Dsl < Struct.new(:schema)
22
+
23
+ def multipolygons(name, &block)
24
+ table :multipolygons, name, &block
25
+ end
26
+
27
+ def multilines(name, &block)
28
+ table :multilines, name, &block
29
+ end
30
+
31
+ def polygons(name, &block)
32
+ table :polygons, name, &block
33
+ end
34
+
35
+ def lines(name, &block)
36
+ table :lines, name, &block
37
+ end
38
+
39
+ def points(name, &block)
40
+ table :points, name, &block
41
+ end
42
+
43
+ def table(type, name, &block)
44
+ require 'osm_import/table'
45
+
46
+ schema.tables << OsmImport::Table.new(type, name, &block)
47
+ end
48
+
49
+ end
50
+
51
+ end
@@ -0,0 +1,56 @@
1
+ require 'osm_import'
2
+
3
+ class OsmImport::Table
4
+
5
+ attr_reader :type, :name, :type_mapper, :mappers, :after_create_callbacks, :after_import_callbacks
6
+
7
+ def initialize(type, name, &block)
8
+ require 'osm_import/mapper'
9
+
10
+ @type = type.to_s.gsub(/s$/,'')
11
+ @name = name
12
+ @type_mapper = OsmImport::Mapper.new(:type, :type)
13
+ @mappers = {}
14
+ @after_create_callbacks = []
15
+ @after_import_callbacks = []
16
+
17
+ dsl.instance_eval(&block) if block
18
+ end
19
+
20
+ def dsl
21
+ @dsl ||= Dsl.new self
22
+ end
23
+
24
+ class Dsl < Struct.new(:table)
25
+
26
+ def with(*args)
27
+ args.each do |arg|
28
+ if arg.is_a? Hash
29
+ arg.each(&method(:add_mapper))
30
+ else
31
+ add_mapper arg, nil
32
+ end
33
+ end
34
+ end
35
+
36
+ def map(*args)
37
+ table.type_mapper.add_args(*args)
38
+ end
39
+
40
+ def after_create(&block)
41
+ table.after_create_callbacks << block
42
+ end
43
+
44
+ def after_import(&block)
45
+ table.after_import_callbacks << block
46
+ end
47
+
48
+ private
49
+
50
+ def add_mapper(key, type)
51
+ table.mappers[key.to_s] = OsmImport::Mapper.new key, type
52
+ end
53
+
54
+ end
55
+
56
+ end
@@ -0,0 +1,191 @@
1
+ require 'osm_import'
2
+ require 'pg'
3
+
4
+ class OsmImport::Target::Pg < Struct.new(:options)
5
+
6
+ def projection
7
+ options[:projection]
8
+ end
9
+
10
+ def prefix
11
+ 'osm_'
12
+ end
13
+
14
+ def new_prefix
15
+ 'new_osm_'
16
+ end
17
+
18
+ def import(schema)
19
+ conn = Connection.new options
20
+
21
+ conn.exec "BEGIN TRANSACTION"
22
+
23
+ puts "Dropping old tables:"
24
+
25
+ schema.tables.each do |table|
26
+ conn.exec "DROP TABLE IF EXISTS #{new_prefix}#{table.name}"
27
+ end
28
+
29
+ puts "Preparing target tables"
30
+
31
+ tts = schema.tables.map{|table| TargetTable.new self, conn, table }
32
+
33
+ puts "Creating tables and importing data:"
34
+
35
+ tts.each do |tt|
36
+ tt.create! # Create table
37
+ tt.import! # Import data to it
38
+ end
39
+
40
+ puts "Deploy tables"
41
+ tts.each do |tt|
42
+ tt.deploy!
43
+ end
44
+
45
+ puts "Restoring geometry columns"
46
+ conn.exec "TRUNCATE geometry_columns"
47
+ conn.exec "SELECT probe_geometry_columns()"
48
+
49
+ puts "Commiting"
50
+ conn.exec "COMMIT"
51
+ conn.close
52
+ end
53
+
54
+ GEOTYPES = { 'point' => 'POINT', 'line' => 'LINESTRING', 'polygon' => 'POLYGON', 'multiline' => 'MULTILINESTRING', 'multipolygon' => 'MULTIPOLYGON' }
55
+
56
+ def geometry_type(type)
57
+ GEOTYPES[type.to_s] or raise StandardError.new("Unknown geometry type #{type.inspect}")
58
+ end
59
+
60
+ class Connection < Struct.new(:options)
61
+
62
+ def initialize(*args)
63
+ super
64
+ @conn = PG.connect options[:pg]
65
+ end
66
+
67
+ def exec(q)
68
+ puts " #{q}"
69
+ @conn.exec q
70
+ end
71
+
72
+ def close
73
+ @conn.close
74
+ end
75
+
76
+ end
77
+
78
+ class TargetTable
79
+
80
+ attr_reader :target, :conn, :table, :type_mapping, :type_array_mapping, :name_mapping, :fields, :conditions, :assigns
81
+
82
+ def initialize(target, conn, table)
83
+ @target = target
84
+ @conn = conn
85
+ @table = table
86
+
87
+ @type_mapping = table.type_mapper.expression
88
+ @type_array_mapping = table.type_mapper.expression_multi
89
+ @name_mapping = "NULLIF(COALESCE(src.tags->'name:ru', src.tags->'name', src.tags->'int_name'), '')"
90
+
91
+ @fields = table.type_mapper.fields.merge :id => 'INT8 PRIMARY KEY', :name => 'VARCHAR(255)', :tags => 'HSTORE', :osm_type => 'VARCHAR(10)'
92
+ @assigns = table.type_mapper.assigns.merge :id => osm_id_expr, :name => name_mapping, :tags => 'src.tags', :osm_type => osm_type_expr
93
+ @conditions = table.type_mapper.conditions
94
+
95
+ table.mappers.each do |key, mapper|
96
+ @fields.merge! mapper.fields
97
+ @assigns.merge! mapper.assigns
98
+ end
99
+ end
100
+
101
+ def osm_id_expr
102
+ "ABS(src.osm_id)"
103
+ end
104
+
105
+ def osm_type_expr(t = table.type)
106
+ "CASE WHEN src.osm_id < 0 THEN 'relation' ELSE '#{if t.to_s == 'point' then 'node' else 'way' end}' END"
107
+ end
108
+
109
+ def add_geometry_column(column, type)
110
+ if target.projection
111
+ conn.exec "SELECT AddGeometryColumn('#{name}', '#{column}', #{target.projection}, '#{target.geometry_type type}', 2)"
112
+ else
113
+ conn.exec "ALTER TABLE #{name} ADD COLUMN #{column} GEOGRAPHY(#{target.geometry_type type}, 4326)"
114
+ end
115
+ end
116
+
117
+ def name
118
+ "#{target.new_prefix}#{table.name}"
119
+ end
120
+
121
+ def create!
122
+ conn.exec "CREATE TABLE #{name}(#{fields.map{|k,v| "#{k} #{v}"}.join(', ')})"
123
+ add_geometry_column :geometry, table.type
124
+
125
+ table.type_mapper.after_create self
126
+
127
+ table.mappers.each do |key, mapper|
128
+ mapper.after_create self
129
+ end
130
+
131
+ table.after_create_callbacks.each do |cb|
132
+ instance_eval(&cb)
133
+ end
134
+ end
135
+
136
+ def indexes
137
+ @indexes ||= begin
138
+ res = table.type_mapper.indexes
139
+ res[:geometry] = "GIST(geometry)"
140
+
141
+ table.mappers.each do |key,mapper|
142
+ res.merge mapper.indexes
143
+ end
144
+
145
+ res
146
+ end
147
+ end
148
+
149
+ def import!
150
+ unless table.type_mapper.mappings.empty?
151
+ if table.type =~ /^multi/
152
+ geometry_expr = 'ST_Multi(way)'
153
+ else
154
+ geometry_expr = 'way'
155
+ end
156
+
157
+ field_keys = fields.keys.to_a
158
+ conn.exec "INSERT INTO #{name}(geometry, #{field_keys.join(', ')}) SELECT #{geometry_expr} AS geometry, #{field_keys.map{|k| "#{assigns[k]} AS #{k}"}.join(',')} FROM raw_osm_#{table.type.gsub('multi','')} src WHERE #{conditions.join(' AND ')}"
159
+ end
160
+
161
+ # Creating indexes
162
+
163
+ indexes.each do |key, desc|
164
+ conn.exec "CREATE INDEX #{name}_#{key}_index ON #{name} USING #{desc}"
165
+ end
166
+
167
+ table.type_mapper.after_import self
168
+
169
+ table.mappers.each do |key, mapper|
170
+ mapper.after_import self
171
+ end
172
+
173
+ table.after_import_callbacks.each do |cb|
174
+ instance_eval(&cb)
175
+ end
176
+ end
177
+
178
+ def deploy!
179
+ conn.exec "DROP TABLE IF EXISTS #{target.prefix}#{table.name}"
180
+ conn.exec "ALTER TABLE #{name} RENAME TO #{target.prefix}#{table.name}"
181
+ #conn.exec "ALTER INDEX #{name}_pkey RENAME TO #{target.prefix}#{table.name}_pkey"
182
+
183
+ indexes.each do |key,_|
184
+ conn.exec "ALTER INDEX #{name}_#{key}_index RENAME TO #{target.prefix}#{table.name}_#{key}_index"
185
+ end
186
+
187
+ end
188
+
189
+ end
190
+
191
+ end
@@ -0,0 +1,11 @@
1
+ require 'osm_import'
2
+
3
+ module OsmImport::Target
4
+
5
+ extend OsmImport::Autoloading
6
+
7
+ def self.new(name, options)
8
+ self[name].new(options)
9
+ end
10
+
11
+ end
@@ -0,0 +1,9 @@
1
+ module OsmImport
2
+ module VERSION #:nodoc:
3
+ MAJOR = 0
4
+ MINOR = 0
5
+ TINY = 0
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join('.')
8
+ end
9
+ end
data/lib/osm_import.rb ADDED
@@ -0,0 +1,37 @@
1
+ module OsmImport
2
+
3
+ module Autoloading
4
+
5
+ def const_missing(c)
6
+ require "#{underscore name}/#{underscore c}"
7
+ const_get c
8
+ end
9
+
10
+ def [](c)
11
+ const_get(camelize c)
12
+ end
13
+
14
+ private
15
+
16
+ # Based on https://github.com/intridea/omniauth/blob/v1.0.2/lib/omniauth.rb#L129-139
17
+ def camelize(str)
18
+ str.to_s.gsub(/\/(.?)/){ "::" + $1.upcase }.gsub(/(^|_)(.)/){ $2.upcase }
19
+ end
20
+
21
+ def underscore(str)
22
+ str.to_s.gsub(/::(.?)/){ "/" + $1.downcase }.gsub(/(.)([A-Z])/){ "#{$1}_#{$2.downcase}" }.downcase
23
+ end
24
+
25
+ end
26
+
27
+ def self.import(mapping_file, options = {})
28
+ require 'osm_import/schema'
29
+ require 'osm_import/target'
30
+
31
+ schema = Schema.load mapping_file
32
+ target = Target.new :pg, options
33
+
34
+ target.import(schema)
35
+ end
36
+
37
+ end
metadata ADDED
@@ -0,0 +1,116 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: osm-import
3
+ version: !ruby/object:Gem::Version
4
+ hash: 31
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 0
10
+ version: 0.0.0
11
+ platform: ruby
12
+ authors:
13
+ - Alexey Noskov
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-03-29 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: pg
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ hash: 51
29
+ segments:
30
+ - 0
31
+ - 11
32
+ - 0
33
+ version: 0.11.0
34
+ type: :runtime
35
+ version_requirements: *id001
36
+ - !ruby/object:Gem::Dependency
37
+ name: rspec
38
+ prerelease: false
39
+ requirement: &id002 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ hash: 3
45
+ segments:
46
+ - 2
47
+ - 0
48
+ version: "2.0"
49
+ type: :development
50
+ version_requirements: *id002
51
+ description: Gem for importing OpenStreetMap data to PostgreSQL database
52
+ email:
53
+ - alexey.noskov@gmail.com
54
+ executables: []
55
+
56
+ extensions: []
57
+
58
+ extra_rdoc_files:
59
+ - README.rdoc
60
+ - MIT-LICENSE
61
+ files:
62
+ - lib/osm-import.rb
63
+ - lib/osm_import/mapper.rb
64
+ - lib/osm_import/table.rb
65
+ - lib/osm_import/mapper/type.rb
66
+ - lib/osm_import/mapper/center.rb
67
+ - lib/osm_import/mapper/address.rb
68
+ - lib/osm_import/version.rb
69
+ - lib/osm_import/target.rb
70
+ - lib/osm_import/target/pg.rb
71
+ - lib/osm_import/schema.rb
72
+ - lib/osm_import.rb
73
+ - MIT-LICENSE
74
+ - README.rdoc
75
+ homepage: http://github.com/alno/osm-import
76
+ licenses: []
77
+
78
+ post_install_message:
79
+ rdoc_options:
80
+ - --line-numbers
81
+ - --inline-source
82
+ - --title
83
+ - OSM Import
84
+ - --main
85
+ - README.rdoc
86
+ require_paths:
87
+ - lib
88
+ required_ruby_version: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ">="
92
+ - !ruby/object:Gem::Version
93
+ hash: 3
94
+ segments:
95
+ - 0
96
+ version: "0"
97
+ required_rubygems_version: !ruby/object:Gem::Requirement
98
+ none: false
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ hash: 23
103
+ segments:
104
+ - 1
105
+ - 3
106
+ - 6
107
+ version: 1.3.6
108
+ requirements: []
109
+
110
+ rubyforge_project:
111
+ rubygems_version: 1.8.11
112
+ signing_key:
113
+ specification_version: 3
114
+ summary: Gem for importing OpenStreetMap data to PostgreSQL database
115
+ test_files: []
116
+