osmer 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 +20 -0
- data/README.rdoc +87 -0
- data/bin/osmer +7 -0
- data/data/empty.osm +2 -0
- data/lib/osmer.rb +92 -0
- data/lib/osmer/app.rb +12 -0
- data/lib/osmer/data/app.rb +16 -0
- data/lib/osmer/mapper/address.rb +19 -0
- data/lib/osmer/mapper/area.rb +13 -0
- data/lib/osmer/mapper/base.rb +28 -0
- data/lib/osmer/mapper/length.rb +13 -0
- data/lib/osmer/mapper/name.rb +4 -0
- data/lib/osmer/mapper/string.rb +4 -0
- data/lib/osmer/mapper/type.rb +91 -0
- data/lib/osmer/schema/app.rb +33 -0
- data/lib/osmer/schema/base.rb +68 -0
- data/lib/osmer/schema/custom.rb +198 -0
- data/lib/osmer/schema/osm2pgsql.rb +154 -0
- data/lib/osmer/target/pg.rb +61 -0
- data/lib/osmer/thor_base.rb +30 -0
- data/lib/osmer/updater.rb +123 -0
- data/lib/osmer/version.rb +9 -0
- metadata +141 -0
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
@@ -0,0 +1,87 @@
|
|
1
|
+
= Osmer
|
2
|
+
|
3
|
+
Osmer is a tool to manage local OSM database: import data, transform it to convenient scheme and update it.
|
4
|
+
|
5
|
+
== Features
|
6
|
+
|
7
|
+
* Importing and updating data in osm2pgsql schema (osm2pgsql required)
|
8
|
+
* Custom derived schemas for analysis or rendering convenience
|
9
|
+
* BBox restriction of imported data
|
10
|
+
* Data reprojection between schemas
|
11
|
+
* [planned] Rails-like config/database.yml support for seamless integration with webapps
|
12
|
+
* [planned] Importing and updating data in pgsnapshot schema (osmosis required)
|
13
|
+
|
14
|
+
== Configuration
|
15
|
+
|
16
|
+
Database schemas definition goes to Osmfile which contains basic flags and schema definitions:
|
17
|
+
|
18
|
+
prefix 'osmer' # Prefix for all tables
|
19
|
+
|
20
|
+
schema :source, :type => :osm2pgsql do # Osm2pgsql schema named 'source'
|
21
|
+
|
22
|
+
bbox 35.859375, 54.367759, 36.562495, 54.775345 # It has bounding box restriction as minlon, minlat, maxlon, maxlat
|
23
|
+
|
24
|
+
updates :daily do # It may be updated on daily basis
|
25
|
+
dump 'http://data.gis-lab.info/osm_dump/dump/RU-KLU/RU-KLU-{today}.osm.pbf' # Where initial dump should be downloaded from here
|
26
|
+
diff 'http://data.gis-lab.info/osm_dump/diff/RU/RU-{yesterday}-{today}.osc.gz' # And diff should be downloaded from here
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
schema :rendering, :projection => 900913 do # Rendering schema containing only necessary data
|
32
|
+
|
33
|
+
multipolygons :places do # Table of place boundaries
|
34
|
+
map :place => [:city, :town, :village, :hamlet] # It contains features which have place tag with one of values: city, town, village, hamlet
|
35
|
+
|
36
|
+
with :area # And also polygon area
|
37
|
+
end
|
38
|
+
|
39
|
+
lines :roads do # Table of roads
|
40
|
+
map :highway # It contains features with highway tag
|
41
|
+
|
42
|
+
with :length, :ref => :string # And also road length and ref tag to additional table column
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
== Usage
|
48
|
+
|
49
|
+
Osmer may be used as command-line executable with following commands:
|
50
|
+
|
51
|
+
* osmer schema create [SCHEMA] # Create given osm schema in database (create all if none specified)
|
52
|
+
* osmer schema drop [SCHEMA] # Drop given osm schema in database (drop all if none specified)
|
53
|
+
* osmer schema recreate [SCHEMA] # Recreate given osm schema in database (recreate all if none specified)
|
54
|
+
|
55
|
+
* osmer data import [SCHEMA] [FILE] # Import data to given schema from file
|
56
|
+
* osmer data update [SCHEMA] [FILE] # Update data in schema from given change file
|
57
|
+
|
58
|
+
== Details
|
59
|
+
|
60
|
+
=== Schema
|
61
|
+
|
62
|
+
Schema is a set of tables representing osm data in a common way. Examples of schemas may be:
|
63
|
+
|
64
|
+
* Osm2Pgsql schema
|
65
|
+
* PgSnapshot schema populated with Osmosis
|
66
|
+
* Custom set of tables, representing data in convenient way
|
67
|
+
|
68
|
+
All tables in schema share common projection.
|
69
|
+
In database schemas are represented with table prefixes (native schemas will be supported later)
|
70
|
+
|
71
|
+
In code, schema is representd by an object, which have following methods:
|
72
|
+
|
73
|
+
* create! - to create schema in database
|
74
|
+
* drop! - to drop schema in database
|
75
|
+
* attach_listener! - (optional) - to add listener to schema data, which may be used to have autoupdate functionality
|
76
|
+
|
77
|
+
listener with name <name> is a stored procedure set:
|
78
|
+
|
79
|
+
<name>_insert(id,*args)
|
80
|
+
<name>_update(id,*args)
|
81
|
+
<name>_delete(id)
|
82
|
+
|
83
|
+
== Contributors
|
84
|
+
|
85
|
+
See https://github.com/alno/osmer/graphs/contributors
|
86
|
+
|
87
|
+
Copyright © 2012 Alexey Noskov, released under the MIT license
|
data/bin/osmer
ADDED
data/data/empty.osm
ADDED
data/lib/osmer.rb
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
class Osmer
|
2
|
+
|
3
|
+
module Data; end
|
4
|
+
module Mapper; end
|
5
|
+
module Schema; end
|
6
|
+
module Target; end
|
7
|
+
|
8
|
+
module Utils
|
9
|
+
|
10
|
+
# Based on https://github.com/intridea/omniauth/blob/v1.0.2/lib/omniauth.rb#L129-139
|
11
|
+
def camelize(str)
|
12
|
+
str.to_s.gsub(/\/(.?)/){ "::" + $1.upcase }.gsub(/(^|_)(.)/){ $2.upcase }
|
13
|
+
end
|
14
|
+
|
15
|
+
def underscore(str)
|
16
|
+
str.to_s.gsub(/::(.?)/){ "/" + $1.downcase }.gsub(/(.)([A-Z])/){ "#{$1}_#{$2.downcase}" }.downcase
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
module Configurable
|
22
|
+
|
23
|
+
def configure(file = nil, &block)
|
24
|
+
if file
|
25
|
+
dsl.instance_eval{ eval File.read(file) }
|
26
|
+
elsif block
|
27
|
+
dsl.instance_eval(&block)
|
28
|
+
end
|
29
|
+
|
30
|
+
self
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def dsl
|
36
|
+
self.class.const_get('Dsl').new(self)
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
include Utils
|
42
|
+
include Configurable
|
43
|
+
|
44
|
+
attr_reader :schemas
|
45
|
+
attr_accessor :prefix
|
46
|
+
|
47
|
+
def initialize
|
48
|
+
@schemas = []
|
49
|
+
end
|
50
|
+
|
51
|
+
def add_schema(name, options)
|
52
|
+
type = options.delete(:type) || 'custom'
|
53
|
+
|
54
|
+
require "osmer/schema/#{type}"
|
55
|
+
|
56
|
+
schema = Osmer::Schema.const_get(camelize type).new self, name, options
|
57
|
+
schemas << schema
|
58
|
+
schema
|
59
|
+
end
|
60
|
+
|
61
|
+
def find_schema(name)
|
62
|
+
schemas.find{|s| s.name.to_s == name.to_s }
|
63
|
+
end
|
64
|
+
|
65
|
+
def create_all!(db)
|
66
|
+
@schemas.each{|s| s.create! db }
|
67
|
+
end
|
68
|
+
|
69
|
+
def drop_all!(db)
|
70
|
+
@schemas.reverse.each{|s| s.drop! db }
|
71
|
+
end
|
72
|
+
|
73
|
+
def recreate_all!(db)
|
74
|
+
drop_all! db
|
75
|
+
create_all! db
|
76
|
+
end
|
77
|
+
|
78
|
+
class Dsl < Struct.new(:osmer)
|
79
|
+
|
80
|
+
def schema(name, options, &block)
|
81
|
+
schema = osmer.add_schema name, options
|
82
|
+
schema.configure &block if block
|
83
|
+
schema
|
84
|
+
end
|
85
|
+
|
86
|
+
def prefix(value)
|
87
|
+
osmer.prefix = value
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
data/lib/osmer/app.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'osmer/thor_base'
|
2
|
+
require 'osmer/data/app'
|
3
|
+
require 'osmer/schema/app'
|
4
|
+
|
5
|
+
require 'thor/group'
|
6
|
+
|
7
|
+
class Osmer::App < Osmer::ThorBase
|
8
|
+
|
9
|
+
register Osmer::Data::App, 'data', 'data <command>', 'OSM data management'
|
10
|
+
register Osmer::Schema::App, 'schema', 'schema <command>', 'OSM schema management'
|
11
|
+
|
12
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'osmer/thor_base'
|
2
|
+
|
3
|
+
class Osmer::Data::App < Osmer::ThorBase
|
4
|
+
namespace :data
|
5
|
+
|
6
|
+
desc "import SCHEMA [FILE]", "Import data to given schema from file"
|
7
|
+
def import(schema, file = nil)
|
8
|
+
osmer.find_schema(schema).import_data! db, file
|
9
|
+
end
|
10
|
+
|
11
|
+
desc "update SCHEMA [FILE]", "Update data in schema from given change file"
|
12
|
+
def update(schema, file = nil)
|
13
|
+
osmer.find_schema(schema).update_data! db, file
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'osmer/mapper/base'
|
2
|
+
|
3
|
+
class Osmer::Mapper::Address < Osmer::Mapper::Base
|
4
|
+
|
5
|
+
def assigns
|
6
|
+
{ :street => "src_tags->'addr:street'",
|
7
|
+
:housenumber => "src_tags->'addr:housenumber'",
|
8
|
+
:city => "src_tags->'addr:city'",
|
9
|
+
:postcode => "src_tags->'addr:postcode'" }
|
10
|
+
end
|
11
|
+
|
12
|
+
def fields
|
13
|
+
{ :street => "TEXT",
|
14
|
+
:housenumber => 'TEXT',
|
15
|
+
:city => "TEXT",
|
16
|
+
:postcode => "TEXT" }
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'osmer'
|
2
|
+
|
3
|
+
class Osmer::Mapper::Base
|
4
|
+
|
5
|
+
attr_reader :table, :name
|
6
|
+
|
7
|
+
def initialize(table, name, options = {})
|
8
|
+
@table = table
|
9
|
+
@name = name
|
10
|
+
end
|
11
|
+
|
12
|
+
def fields
|
13
|
+
{ name => "TEXT" }
|
14
|
+
end
|
15
|
+
|
16
|
+
def assigns
|
17
|
+
{ name => "src_tags->'#{name}'" }
|
18
|
+
end
|
19
|
+
|
20
|
+
def conditions
|
21
|
+
[]
|
22
|
+
end
|
23
|
+
|
24
|
+
def indexes
|
25
|
+
{}
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'osmer/mapper/base'
|
2
|
+
|
3
|
+
class Osmer::Mapper::Type < Osmer::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, :types => expression_multi }
|
17
|
+
else
|
18
|
+
{ :type => expression }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def fields
|
23
|
+
if @multi
|
24
|
+
{ :type => 'TEXT NOT NULL', :types => 'TEXT[]' }
|
25
|
+
else
|
26
|
+
{ :type => 'TEXT NOT NULL' }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def indexes
|
31
|
+
if @multi
|
32
|
+
{ :type => "BTREE(type)", :types => "GIN(types)" }
|
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,33 @@
|
|
1
|
+
require 'osmer/thor_base'
|
2
|
+
|
3
|
+
class Osmer::Schema::App < Osmer::ThorBase
|
4
|
+
namespace :schema
|
5
|
+
|
6
|
+
desc "create [SCHEMA]", "Create given osm schema in database (create all if none specified)"
|
7
|
+
def create(schema = nil)
|
8
|
+
if schema
|
9
|
+
osmer.find_schema(schema).create! db
|
10
|
+
else
|
11
|
+
osmer.create_all! db
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
desc "recreate [SCHEMA]", "Recreate given osm schema in database (recreate all if none specified)"
|
16
|
+
def recreate(schema = nil)
|
17
|
+
if schema
|
18
|
+
osmer.find_schema(schema).recreate! db
|
19
|
+
else
|
20
|
+
osmer.recreate_all! db
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
desc "drop [SCHEMA]", "Drop given osm schema in database (drop all if none specified)"
|
25
|
+
def drop(schema = nil)
|
26
|
+
if schema
|
27
|
+
osmer.find_schema(schema).drop! db
|
28
|
+
else
|
29
|
+
osmer.drop_all! db
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'osmer'
|
2
|
+
|
3
|
+
class Osmer::Schema::Base
|
4
|
+
|
5
|
+
include Osmer::Configurable
|
6
|
+
|
7
|
+
attr_reader :ns, :name
|
8
|
+
attr_accessor :projection
|
9
|
+
|
10
|
+
def initialize(ns, name, options = {})
|
11
|
+
@ns = ns
|
12
|
+
@name = name
|
13
|
+
@projection = 4326
|
14
|
+
|
15
|
+
options.each do |k,v| # Assign all schema options
|
16
|
+
send "#{k}=", v
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Create schema in given database
|
21
|
+
def create!(db)
|
22
|
+
raise StandardError.new("Not implemented")
|
23
|
+
end
|
24
|
+
|
25
|
+
# Drop schema in given database
|
26
|
+
def drop!(db)
|
27
|
+
raise StandardError.new("Not implemented")
|
28
|
+
end
|
29
|
+
|
30
|
+
def recreate!(db)
|
31
|
+
drop! db
|
32
|
+
create! db
|
33
|
+
end
|
34
|
+
|
35
|
+
# Add collection listener
|
36
|
+
# name - stored procedure name
|
37
|
+
#
|
38
|
+
def attach_listener!(conn, collection, name, fields)
|
39
|
+
raise StandardError.new("Not implemented")
|
40
|
+
end
|
41
|
+
|
42
|
+
def detach_listener!(conn, collection, name, fields)
|
43
|
+
raise StandardError.new("Not implemented")
|
44
|
+
end
|
45
|
+
|
46
|
+
def table_prefix
|
47
|
+
[@ns.prefix, name].compact.join('_')
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
class Dsl < Struct.new(:schema)
|
53
|
+
|
54
|
+
def method_missing(method, *args)
|
55
|
+
if schema.respond_to?("#{method}=") && !args.empty?
|
56
|
+
if args.size > 1
|
57
|
+
schema.send "#{method}=", args
|
58
|
+
else
|
59
|
+
schema.send "#{method}=", args.first
|
60
|
+
end
|
61
|
+
else
|
62
|
+
super
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
@@ -0,0 +1,198 @@
|
|
1
|
+
require 'osmer/schema/base'
|
2
|
+
|
3
|
+
class Osmer::Schema::Custom < Osmer::Schema::Base
|
4
|
+
|
5
|
+
attr_reader :tables
|
6
|
+
attr_accessor :source
|
7
|
+
|
8
|
+
def initialize(*args)
|
9
|
+
@tables = []
|
10
|
+
super
|
11
|
+
end
|
12
|
+
|
13
|
+
def dsl
|
14
|
+
Dsl.new(self)
|
15
|
+
end
|
16
|
+
|
17
|
+
def create!(db)
|
18
|
+
db.in_transaction do |conn|
|
19
|
+
tables.each do |table|
|
20
|
+
create_table! db, conn, table
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def drop!(db)
|
26
|
+
db.in_transaction do |conn|
|
27
|
+
tables.reverse.each do |table|
|
28
|
+
drop_table! db, conn, table
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def create_table!(db, conn, table)
|
36
|
+
table_name = "#{table_prefix}_#{table.name}"
|
37
|
+
table_fields = { :id => 'INT8', :tags => 'HSTORE' }
|
38
|
+
table_assigns = { :tags => 'src_tags' }
|
39
|
+
table_conditions = []
|
40
|
+
table_indexes = { :geometry => 'GIST(geometry)' }
|
41
|
+
|
42
|
+
if table.type.to_s.start_with? 'multi'
|
43
|
+
table_assigns[:geometry] = "ST_Transform(ST_Multi(src_geometry), #{projection})"
|
44
|
+
else
|
45
|
+
table_assigns[:geometry] = "ST_Transform(src_geometry, #{projection})"
|
46
|
+
end
|
47
|
+
|
48
|
+
table.mappers.each do |k,v|
|
49
|
+
table_fields.merge! v.fields
|
50
|
+
table_assigns.merge! v.assigns
|
51
|
+
table_indexes.merge! v.indexes
|
52
|
+
table_conditions |= v.conditions
|
53
|
+
end
|
54
|
+
|
55
|
+
table_assigns_keys = table_assigns.keys.to_a
|
56
|
+
table_assigns_values = table_assigns_keys.map{|k| table_assigns[k] }
|
57
|
+
table_condition = table_conditions.map{|c| "(#{c})"}.join(' AND ')
|
58
|
+
|
59
|
+
conn.exec "CREATE TABLE #{table_name}(#{table_fields.map{|k,v| "#{k} #{v}"}.join(', ')})"
|
60
|
+
conn.exec "SELECT AddGeometryColumn('#{table_name}', 'geometry', #{projection}, '#{db.geometry_type table.type}', 2)"
|
61
|
+
|
62
|
+
table_indexes.each do |key,desc|
|
63
|
+
conn.exec "CREATE INDEX #{table_name}_#{key}_index ON #{table_name} USING #{desc}"
|
64
|
+
end
|
65
|
+
|
66
|
+
conn.exec %Q{CREATE OR REPLACE FUNCTION #{table_name}_insert(src_id BIGINT, src_tags HSTORE, src_geometry GEOMETRY) RETURNS BOOLEAN AS $$
|
67
|
+
BEGIN
|
68
|
+
IF #{table_condition} THEN
|
69
|
+
INSERT INTO #{table_name} (id, #{table_assigns_keys.join(', ')}) VALUES (src_id, #{table_assigns_values.join(', ')});
|
70
|
+
RETURN FOUND;
|
71
|
+
ELSE
|
72
|
+
RETURN FALSE;
|
73
|
+
END IF;
|
74
|
+
END; $$ LANGUAGE plpgsql;}
|
75
|
+
|
76
|
+
conn.exec %Q{CREATE OR REPLACE FUNCTION #{table_name}_update(src_id BIGINT, src_tags HSTORE, src_geometry GEOMETRY) RETURNS BOOLEAN AS $$
|
77
|
+
BEGIN
|
78
|
+
IF #{table_condition} THEN
|
79
|
+
UPDATE #{table_name} SET #{table_assigns.map{|k,v| "#{k} = #{v}"}.join(', ')} WHERE id = src_id;
|
80
|
+
|
81
|
+
IF NOT FOUND THEN
|
82
|
+
INSERT INTO #{table_name} (id, #{table_assigns_keys.join(', ')}) VALUES (src_id, #{table_assigns_values.join(', ')});
|
83
|
+
END IF;
|
84
|
+
|
85
|
+
RETURN FOUND;
|
86
|
+
ELSE
|
87
|
+
DELETE FROM #{table_name} WHERE id = src_id;
|
88
|
+
RETURN FOUND;
|
89
|
+
END IF;
|
90
|
+
END; $$ LANGUAGE plpgsql;}
|
91
|
+
|
92
|
+
conn.exec %Q{CREATE OR REPLACE FUNCTION #{table_name}_delete(src_id BIGINT) RETURNS BOOLEAN AS $$
|
93
|
+
BEGIN
|
94
|
+
DELETE FROM #{table_name} WHERE id = src_id;
|
95
|
+
RETURN FOUND;
|
96
|
+
END; $$ LANGUAGE plpgsql;}
|
97
|
+
|
98
|
+
table.source_schema.attach_listener! conn, table.source_table, table_name, listener_fields
|
99
|
+
|
100
|
+
conn.exec "ANALYZE #{table_name}"
|
101
|
+
end
|
102
|
+
|
103
|
+
def drop_table!(db, conn, table)
|
104
|
+
table_name = "#{table_prefix}_#{table.name}"
|
105
|
+
|
106
|
+
table.source_schema.detach_listener! conn, table.source_table, table_name, listener_fields
|
107
|
+
|
108
|
+
conn.exec "DROP TABLE IF EXISTS #{table_name}"
|
109
|
+
end
|
110
|
+
|
111
|
+
def listener_fields
|
112
|
+
[:tags, :geometry]
|
113
|
+
end
|
114
|
+
|
115
|
+
class Dsl < Osmer::Schema::Base::Dsl
|
116
|
+
|
117
|
+
def multipolygons(name, options = {}, &block)
|
118
|
+
table name, :multipolygons, options, &block
|
119
|
+
end
|
120
|
+
|
121
|
+
def multilines(name, options = {}, &block)
|
122
|
+
table name, :multilines, options, &block
|
123
|
+
end
|
124
|
+
|
125
|
+
def polygons(name, options = {}, &block)
|
126
|
+
table name, :polygons, options, &block
|
127
|
+
end
|
128
|
+
|
129
|
+
def lines(name, options = {}, &block)
|
130
|
+
table name, :lines, options, &block
|
131
|
+
end
|
132
|
+
|
133
|
+
def points(name, options = {}, &block)
|
134
|
+
table name, :points, options, &block
|
135
|
+
end
|
136
|
+
|
137
|
+
def table(name, type, options, &block)
|
138
|
+
schema.tables << Table.new(schema, name, type, options).configure(&block)
|
139
|
+
end
|
140
|
+
|
141
|
+
end
|
142
|
+
|
143
|
+
class Table
|
144
|
+
|
145
|
+
include Osmer::Configurable
|
146
|
+
|
147
|
+
attr_reader :schema, :name, :type, :source_schema, :source_table, :mappers
|
148
|
+
|
149
|
+
def initialize(schema, name, type, options)
|
150
|
+
require 'osmer/mapper/type'
|
151
|
+
require 'osmer/mapper/name'
|
152
|
+
|
153
|
+
@schema, @name, @type = schema, name, type
|
154
|
+
|
155
|
+
@source_table = type.to_s.gsub(/\Amulti/,'')
|
156
|
+
@source_schema = schema.ns.find_schema(options[:source] || schema.source || :source) or raise StandardError.new("Source schema not found")
|
157
|
+
|
158
|
+
@mappers = {
|
159
|
+
:type => Osmer::Mapper::Type.new(self, :type),
|
160
|
+
:name => Osmer::Mapper::Name.new(self, :name)
|
161
|
+
}
|
162
|
+
end
|
163
|
+
|
164
|
+
def projection
|
165
|
+
schema.projection
|
166
|
+
end
|
167
|
+
|
168
|
+
class Dsl < Struct.new(:table)
|
169
|
+
|
170
|
+
include Osmer::Utils
|
171
|
+
|
172
|
+
def map(*args)
|
173
|
+
table.mappers[:type].add_args(*args)
|
174
|
+
end
|
175
|
+
|
176
|
+
def with(*args)
|
177
|
+
args.each do |arg|
|
178
|
+
if arg.is_a? Hash
|
179
|
+
arg.each(&method(:add_mapper))
|
180
|
+
else
|
181
|
+
add_mapper arg, arg
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
private
|
187
|
+
|
188
|
+
def add_mapper(key, type)
|
189
|
+
require "osmer/mapper/#{type}"
|
190
|
+
|
191
|
+
table.mappers[key.to_s] = Osmer::Mapper.const_get(camelize type).new table, key
|
192
|
+
end
|
193
|
+
|
194
|
+
end
|
195
|
+
|
196
|
+
end
|
197
|
+
|
198
|
+
end
|
@@ -0,0 +1,154 @@
|
|
1
|
+
require 'osmer/schema/base'
|
2
|
+
|
3
|
+
class Osmer::Schema::Osm2pgsql < Osmer::Schema::Base
|
4
|
+
|
5
|
+
attr_accessor :osm2pgsql_binary, :bbox, :updater
|
6
|
+
|
7
|
+
def initialize(*args)
|
8
|
+
@osm2pgsql_binary = 'osm2pgsql'
|
9
|
+
super
|
10
|
+
end
|
11
|
+
|
12
|
+
# Create schema in given database
|
13
|
+
def create!(db)
|
14
|
+
db.in_transaction do |conn|
|
15
|
+
raise StandardError.new("Schema #{name} already created!") unless schema_tables(conn).empty?
|
16
|
+
end
|
17
|
+
|
18
|
+
osm2pgsql_exec db, "'#{empty_file}'", "creating osm2pgsql schema"
|
19
|
+
end
|
20
|
+
|
21
|
+
# Drop schema in given database
|
22
|
+
def drop!(db)
|
23
|
+
db.in_transaction do |conn|
|
24
|
+
schema_tables(conn).each do |table|
|
25
|
+
conn.exec "DROP TABLE IF EXISTS #{table}"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def import_data!(db, file = nil)
|
31
|
+
raise StandardError.new("No file or updater specified") unless file or updater
|
32
|
+
|
33
|
+
db.in_transaction do |conn|
|
34
|
+
schema_tables(conn).each do |table|
|
35
|
+
conn.exec "DELETE FROM #{table}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
if file
|
40
|
+
osm2pgsql_exec db, "-a '#{file}'", "importing data with osm2pgsql from #{file}"
|
41
|
+
else
|
42
|
+
updater.load_dump db, self do |f|
|
43
|
+
osm2pgsql_exec db, "-a '#{f}'", "importing data with osm2pgsql from #{f}"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def update_data!(db, file = nil)
|
49
|
+
raise StandardError.new("No file or updater specified") unless file or updater
|
50
|
+
|
51
|
+
if file
|
52
|
+
osm2pgsql_exec db, "-a '#{file}'", "importing data with osm2pgsql from #{file}"
|
53
|
+
else
|
54
|
+
updater.load_diffs db, self do |f|
|
55
|
+
osm2pgsql_exec db, "-a '#{f}'", "importing data with osm2pgsql from #{f}"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def attach_listener!(conn, collection, name, fields)
|
61
|
+
table = collection_table collection
|
62
|
+
args = fields.map{|f| collection_field(collection, f) }.join(', ')
|
63
|
+
|
64
|
+
conn.exec %Q{CREATE OR REPLACE FUNCTION #{name}_insert_proxy() RETURNS trigger AS $$
|
65
|
+
BEGIN
|
66
|
+
PERFORM #{name}_insert(NEW.osm_id, #{args});
|
67
|
+
RETURN NULL;
|
68
|
+
END; $$ LANGUAGE plpgsql}
|
69
|
+
|
70
|
+
conn.exec %Q{CREATE OR REPLACE FUNCTION #{name}_update_proxy() RETURNS trigger AS $$
|
71
|
+
BEGIN
|
72
|
+
PERFORM #{name}_update(NEW.osm_id, #{args});
|
73
|
+
RETURN NULL;
|
74
|
+
END; $$ LANGUAGE plpgsql}
|
75
|
+
|
76
|
+
conn.exec %Q{CREATE OR REPLACE FUNCTION #{name}_delete_proxy() RETURNS trigger AS $$
|
77
|
+
BEGIN
|
78
|
+
PERFORM #{name}_delete(OLD.osm_id);
|
79
|
+
RETURN NULL;
|
80
|
+
END; $$ LANGUAGE plpgsql}
|
81
|
+
|
82
|
+
# Create new triggers
|
83
|
+
conn.exec "CREATE TRIGGER #{name}_insert_trigger AFTER INSERT ON #{table} FOR EACH ROW EXECUTE PROCEDURE #{name}_insert_proxy()"
|
84
|
+
conn.exec "CREATE TRIGGER #{name}_update_trigger AFTER UPDATE ON #{table} FOR EACH ROW EXECUTE PROCEDURE #{name}_update_proxy()"
|
85
|
+
conn.exec "CREATE TRIGGER #{name}_delete_trigger AFTER DELETE ON #{table} FOR EACH ROW EXECUTE PROCEDURE #{name}_delete_proxy()"
|
86
|
+
|
87
|
+
# Prepopulate dependency
|
88
|
+
conn.exec "SELECT #{name}_insert(NEW.osm_id, #{args}) FROM #{table} NEW"
|
89
|
+
end
|
90
|
+
|
91
|
+
def detach_listener!(conn, collection, name, fields)
|
92
|
+
table = collection_table collection
|
93
|
+
|
94
|
+
# Drop triggers
|
95
|
+
conn.exec "DROP TRIGGER IF EXISTS #{name}_insert_trigger ON #{table}"
|
96
|
+
conn.exec "DROP TRIGGER IF EXISTS #{name}_update_trigger ON #{table}"
|
97
|
+
conn.exec "DROP TRIGGER IF EXISTS #{name}_delete_trigger ON #{table}"
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
101
|
+
|
102
|
+
def osm2pgsql_exec(db, tail, desc)
|
103
|
+
cmd = "'#{osm2pgsql_binary}' -j -m -G --slim -U #{db[:username]} -d #{db[:database]} -H #{db[:host]} -p '#{table_prefix}'"
|
104
|
+
cmd << " --bbox #{bbox.join(',')}" if bbox # Restrict import if bbox specified
|
105
|
+
|
106
|
+
case projection.to_i
|
107
|
+
when 4326 then cmd << ' --latlong'
|
108
|
+
when 900913 then cmd << ' --merc'
|
109
|
+
else raise StandardError.new("Unsupported projection #{projection}")
|
110
|
+
end
|
111
|
+
|
112
|
+
puts "#{cmd} #{tail}"
|
113
|
+
system "#{cmd} #{tail}" or raise StandardError.new("Error #{desc}")
|
114
|
+
end
|
115
|
+
|
116
|
+
def schema_tables(conn)
|
117
|
+
res = conn.exec "select table_name from information_schema.tables where table_schema = 'public' AND table_name LIKE '#{table_prefix}_%'"
|
118
|
+
tables = res.values.flatten
|
119
|
+
res.clear
|
120
|
+
tables
|
121
|
+
end
|
122
|
+
|
123
|
+
def collection_table(collection)
|
124
|
+
case collection.to_s
|
125
|
+
when 'polygons' then "#{table_prefix}_polygon"
|
126
|
+
when 'lines' then "#{table_prefix}_line"
|
127
|
+
when 'points' then "#{table_prefix}_point"
|
128
|
+
else raise StandardError.new("Unsupported collection '#{collection}'")
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def collection_field(collection, field)
|
133
|
+
case field.to_s
|
134
|
+
when 'geometry' then 'NEW.way'
|
135
|
+
when 'tags' then 'NEW.tags'
|
136
|
+
else raise StandardError.new("Unsupported field '#{field}' in collection '#{collection}'")
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def empty_file
|
141
|
+
File.expand_path("../../../../data/empty.osm", __FILE__)
|
142
|
+
end
|
143
|
+
|
144
|
+
class Dsl < Osmer::Schema::Base::Dsl
|
145
|
+
|
146
|
+
def updates(interval = nil, &block)
|
147
|
+
require 'osmer/updater'
|
148
|
+
|
149
|
+
schema.updater = Osmer::Updater.new(interval).configure(&block)
|
150
|
+
end
|
151
|
+
|
152
|
+
end
|
153
|
+
|
154
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'osmer'
|
2
|
+
|
3
|
+
require 'pg'
|
4
|
+
|
5
|
+
class Osmer::Target::Pg
|
6
|
+
|
7
|
+
attr_accessor :options
|
8
|
+
|
9
|
+
def initialize(options)
|
10
|
+
@options = options
|
11
|
+
end
|
12
|
+
|
13
|
+
def [](key)
|
14
|
+
@options[key]
|
15
|
+
end
|
16
|
+
|
17
|
+
def in_transaction
|
18
|
+
conn = Connection.new connection_options
|
19
|
+
|
20
|
+
begin
|
21
|
+
conn.exec "BEGIN TRANSACTION"
|
22
|
+
|
23
|
+
yield conn
|
24
|
+
|
25
|
+
conn.exec "COMMIT"
|
26
|
+
ensure
|
27
|
+
conn.close
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
GEOTYPES = { 'point' => 'POINT', 'line' => 'LINESTRING', 'polygon' => 'POLYGON', 'multiline' => 'MULTILINESTRING', 'multipolygon' => 'MULTIPOLYGON' }
|
32
|
+
|
33
|
+
def geometry_type(type)
|
34
|
+
GEOTYPES[type.to_s.gsub(/s\z/,'')] or raise StandardError.new("Unknown geometry type #{type.inspect}")
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def connection_options
|
40
|
+
{ :host => options[:host], :user => options[:username], :password => options[:password], :dbname => options[:database] }
|
41
|
+
end
|
42
|
+
|
43
|
+
class Connection < Struct.new(:options)
|
44
|
+
|
45
|
+
def initialize(*args)
|
46
|
+
super
|
47
|
+
@conn = PG.connect options
|
48
|
+
end
|
49
|
+
|
50
|
+
def exec(q, *args)
|
51
|
+
puts " #{q}"
|
52
|
+
@conn.exec q, *args
|
53
|
+
end
|
54
|
+
|
55
|
+
def close
|
56
|
+
@conn.close
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'osmer'
|
2
|
+
require 'thor'
|
3
|
+
|
4
|
+
class Osmer::ThorBase < Thor
|
5
|
+
|
6
|
+
class_option :config, :aliases => '-c', :default => 'Osmfile', :desc => 'Config file location'
|
7
|
+
|
8
|
+
class_option :dbhost, :aliases => '-h', :default => 'localhost', :desc => 'Database host'
|
9
|
+
class_option :dbport, :aliases => '-p', :default => '5432', :desc => 'Database port'
|
10
|
+
class_option :dbuser, :aliases => '-U', :desc => 'Database user'
|
11
|
+
class_option :dbpass, :aliases => '-P', :desc => 'Database password'
|
12
|
+
class_option :dbname, :aliases => '-d', :desc => 'Database name'
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def app_options
|
17
|
+
@app_options = parent_options.merge(options)
|
18
|
+
end
|
19
|
+
|
20
|
+
def db
|
21
|
+
require 'osmer/target/pg'
|
22
|
+
|
23
|
+
@db ||= Osmer::Target::Pg.new :username => app_options[:dbuser], :password => app_options[:dbpass], :host => app_options[:dbhost], :port => app_options[:dbport], :database => app_options[:dbname]
|
24
|
+
end
|
25
|
+
|
26
|
+
def osmer
|
27
|
+
@osmer ||= Osmer.new.configure app_options[:config]
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
require 'osmer'
|
2
|
+
|
3
|
+
class Osmer::Updater
|
4
|
+
include Osmer::Configurable
|
5
|
+
|
6
|
+
attr_accessor :interval, :dump, :diff
|
7
|
+
|
8
|
+
def initialize(interval = nil)
|
9
|
+
@interval = interval
|
10
|
+
end
|
11
|
+
|
12
|
+
def load_diffs(db, schema)
|
13
|
+
db.in_transaction do |conn|
|
14
|
+
init_meta_table conn, schema
|
15
|
+
cur = get_current_version conn, schema
|
16
|
+
|
17
|
+
puts "Requesting diff versions"
|
18
|
+
versions = get_diff_versions
|
19
|
+
|
20
|
+
while version = versions.detect{|d| d[0] == cur }
|
21
|
+
puts "Downloading diff #{version.join('-')}"
|
22
|
+
file = download_file diff, version
|
23
|
+
|
24
|
+
puts "Applying diff #{file}"
|
25
|
+
yield file
|
26
|
+
|
27
|
+
set_current_version conn, schema, version[1]
|
28
|
+
cur = version[1]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def load_dump(db, schema)
|
34
|
+
db.in_transaction do |conn|
|
35
|
+
init_meta_table conn, schema
|
36
|
+
|
37
|
+
puts "Requesting last dump version"
|
38
|
+
version = get_dump_version
|
39
|
+
|
40
|
+
puts "Downloading dump #{version.join('-')}"
|
41
|
+
file = download_file dump, version
|
42
|
+
|
43
|
+
puts "Loading dump #{file}"
|
44
|
+
yield file
|
45
|
+
|
46
|
+
set_current_version conn, schema, version[0]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def reset(db, schema)
|
51
|
+
db.in_transaction do |conn|
|
52
|
+
init_meta_table conn, schema
|
53
|
+
reset_current_version conn, schema
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def meta_table(schema)
|
60
|
+
"#{schema.ns.prefix}_schema_versions"
|
61
|
+
end
|
62
|
+
|
63
|
+
def init_meta_table(conn, schema)
|
64
|
+
conn.exec "CREATE TABLE IF NOT EXISTS #{meta_table(schema)}(schema VARCHAR(255) NOT NULL PRIMARY KEY, version INT8 NOT NULL)"
|
65
|
+
end
|
66
|
+
|
67
|
+
def get_current_version(conn, schema)
|
68
|
+
conn.exec("SELECT version FROM #{meta_table(schema)} WHERE schema = $1", [schema.name]).values.first.first
|
69
|
+
end
|
70
|
+
|
71
|
+
def reset_current_version(conn, schema)
|
72
|
+
conn.exec "DELETE FROM #{meta_table(schema)} WHERE schema = $1", [schema.name]
|
73
|
+
end
|
74
|
+
|
75
|
+
def set_current_version(conn, schema, version)
|
76
|
+
if conn.exec("UPDATE #{meta_table(schema)} SET version = $2 WHERE schema = $1", [schema.name, version]).cmdtuples == 0
|
77
|
+
conn.exec "INSERT INTO #{meta_table(schema)}(schema, version) VALUES ($1, $2)", [schema.name, version]
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def get_diff_versions
|
82
|
+
get_available_versions(diff).uniq
|
83
|
+
end
|
84
|
+
|
85
|
+
def get_dump_version
|
86
|
+
get_available_versions(dump).max
|
87
|
+
end
|
88
|
+
|
89
|
+
def get_available_versions(url)
|
90
|
+
require 'open-uri'
|
91
|
+
|
92
|
+
dir_content = open(url.gsub(/\/[^\/]+\z/, '/')).read
|
93
|
+
file_regexp = Regexp.compile Regexp.quote(url.gsub(/.*\//, '')).gsub(/\\\{\w+\\\}/,'(\d+)')
|
94
|
+
|
95
|
+
dir_content.scan(file_regexp).uniq
|
96
|
+
end
|
97
|
+
|
98
|
+
def download_file(template, subst)
|
99
|
+
require 'fileutils'
|
100
|
+
|
101
|
+
FileUtils.mkdir_p "/tmp/osmer"
|
102
|
+
|
103
|
+
url = template.split(/\{\w+\}/).zip(subst).flatten.compact.join
|
104
|
+
file = "/tmp/osmer/#{url.gsub(/.*\//, '')}"
|
105
|
+
|
106
|
+
unless File.exists? file
|
107
|
+
system "wget -O '#{file}' '#{url}'" or StandardError.new("Error downloading file '#{file}' from '#{url}'")
|
108
|
+
end
|
109
|
+
|
110
|
+
file
|
111
|
+
end
|
112
|
+
|
113
|
+
class Dsl < Struct.new(:updater)
|
114
|
+
|
115
|
+
[:interval, :dump, :diff].each do |method|
|
116
|
+
define_method method do |value|
|
117
|
+
updater.send "#{method}=", value
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
metadata
ADDED
@@ -0,0 +1,141 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: osmer
|
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-08-26 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: thor
|
38
|
+
prerelease: false
|
39
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
hash: 43
|
45
|
+
segments:
|
46
|
+
- 0
|
47
|
+
- 14
|
48
|
+
- 6
|
49
|
+
version: 0.14.6
|
50
|
+
type: :runtime
|
51
|
+
version_requirements: *id002
|
52
|
+
- !ruby/object:Gem::Dependency
|
53
|
+
name: rspec
|
54
|
+
prerelease: false
|
55
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
hash: 3
|
61
|
+
segments:
|
62
|
+
- 2
|
63
|
+
- 0
|
64
|
+
version: "2.0"
|
65
|
+
type: :development
|
66
|
+
version_requirements: *id003
|
67
|
+
description: Gem for managing OpenStreetMap data in PostgreSQL database
|
68
|
+
email:
|
69
|
+
- alexey.noskov@gmail.com
|
70
|
+
executables:
|
71
|
+
- osmer
|
72
|
+
extensions: []
|
73
|
+
|
74
|
+
extra_rdoc_files:
|
75
|
+
- README.rdoc
|
76
|
+
- MIT-LICENSE
|
77
|
+
files:
|
78
|
+
- lib/osmer/updater.rb
|
79
|
+
- lib/osmer/schema/app.rb
|
80
|
+
- lib/osmer/schema/custom.rb
|
81
|
+
- lib/osmer/schema/osm2pgsql.rb
|
82
|
+
- lib/osmer/schema/base.rb
|
83
|
+
- lib/osmer/app.rb
|
84
|
+
- lib/osmer/mapper/string.rb
|
85
|
+
- lib/osmer/mapper/type.rb
|
86
|
+
- lib/osmer/mapper/area.rb
|
87
|
+
- lib/osmer/mapper/base.rb
|
88
|
+
- lib/osmer/mapper/name.rb
|
89
|
+
- lib/osmer/mapper/length.rb
|
90
|
+
- lib/osmer/mapper/address.rb
|
91
|
+
- lib/osmer/data/app.rb
|
92
|
+
- lib/osmer/thor_base.rb
|
93
|
+
- lib/osmer/version.rb
|
94
|
+
- lib/osmer/target/pg.rb
|
95
|
+
- lib/osmer.rb
|
96
|
+
- bin/osmer
|
97
|
+
- data/empty.osm
|
98
|
+
- MIT-LICENSE
|
99
|
+
- README.rdoc
|
100
|
+
homepage: http://github.com/alno/osmer
|
101
|
+
licenses: []
|
102
|
+
|
103
|
+
post_install_message:
|
104
|
+
rdoc_options:
|
105
|
+
- --line-numbers
|
106
|
+
- --inline-source
|
107
|
+
- --title
|
108
|
+
- Osmer
|
109
|
+
- --main
|
110
|
+
- README.rdoc
|
111
|
+
require_paths:
|
112
|
+
- lib
|
113
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
114
|
+
none: false
|
115
|
+
requirements:
|
116
|
+
- - ">="
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
hash: 3
|
119
|
+
segments:
|
120
|
+
- 0
|
121
|
+
version: "0"
|
122
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
123
|
+
none: false
|
124
|
+
requirements:
|
125
|
+
- - ">="
|
126
|
+
- !ruby/object:Gem::Version
|
127
|
+
hash: 23
|
128
|
+
segments:
|
129
|
+
- 1
|
130
|
+
- 3
|
131
|
+
- 6
|
132
|
+
version: 1.3.6
|
133
|
+
requirements: []
|
134
|
+
|
135
|
+
rubyforge_project:
|
136
|
+
rubygems_version: 1.8.11
|
137
|
+
signing_key:
|
138
|
+
specification_version: 3
|
139
|
+
summary: Gem for managing OpenStreetMap data in PostgreSQL database
|
140
|
+
test_files: []
|
141
|
+
|