osmer 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|