terragona 0.1.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/.gitignore +1 -0
- data/Gemfile +9 -0
- data/Gemfile.lock +42 -0
- data/README.md +155 -0
- data/lib/terragona/base.rb +58 -0
- data/lib/terragona/concave_hull.rb +98 -0
- data/lib/terragona/geonames.rb +186 -0
- data/lib/terragona/version.rb +3 -0
- data/lib/terragona.rb +2 -0
- data/terragona.gemspec +31 -0
- metadata +201 -0
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
.idea
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
GEM
|
2
|
+
remote: https://rubygems.org/
|
3
|
+
specs:
|
4
|
+
coderay (1.1.0)
|
5
|
+
columnize (0.9.0)
|
6
|
+
debugger (1.6.8)
|
7
|
+
columnize (>= 0.3.1)
|
8
|
+
debugger-linecache (~> 1.2.0)
|
9
|
+
debugger-ruby_core_source (~> 1.3.5)
|
10
|
+
debugger-linecache (1.2.0)
|
11
|
+
debugger-ruby_core_source (1.3.7)
|
12
|
+
diskcached (1.1.0)
|
13
|
+
geokit (1.9.0)
|
14
|
+
multi_json (>= 1.3.2)
|
15
|
+
httpi (1.1.1)
|
16
|
+
rack
|
17
|
+
method_source (0.8.2)
|
18
|
+
multi_json (1.10.1)
|
19
|
+
pg (0.17.1)
|
20
|
+
pry (0.10.1)
|
21
|
+
coderay (~> 1.1.0)
|
22
|
+
method_source (~> 0.8.1)
|
23
|
+
slop (~> 3.4)
|
24
|
+
pry-debugger (0.2.3)
|
25
|
+
debugger (~> 1.3)
|
26
|
+
pry (>= 0.9.10, < 0.11.0)
|
27
|
+
rack (1.6.0)
|
28
|
+
sequel (3.48.0)
|
29
|
+
similar_text (0.0.4)
|
30
|
+
slop (3.6.0)
|
31
|
+
|
32
|
+
PLATFORMS
|
33
|
+
ruby
|
34
|
+
|
35
|
+
DEPENDENCIES
|
36
|
+
diskcached
|
37
|
+
geokit
|
38
|
+
httpi (~> 1.1.0)
|
39
|
+
pg
|
40
|
+
pry-debugger
|
41
|
+
sequel (~> 3.48.0)
|
42
|
+
similar_text (~> 0.0.4)
|
data/README.md
ADDED
@@ -0,0 +1,155 @@
|
|
1
|
+
terragona
|
2
|
+
=========
|
3
|
+
|
4
|
+
Create polygons for [GeoNames](www.geonames.org) places.
|
5
|
+
This means: Create concave polygons using geonames places and store them in a postgres/postgis database.
|
6
|
+
See [ST_Concave_Hull](http://postgis.net/docs/ST_ConcaveHull.html).
|
7
|
+
|
8
|
+

|
9
|
+
|
10
|
+
|
11
|
+
So, am I saying you can get the geometries of all places magically? Sort of...
|
12
|
+
As you can see, the results are not *very* accurate. But they are interesting.
|
13
|
+
|
14
|
+
|
15
|
+
Install
|
16
|
+
-------
|
17
|
+
|
18
|
+
`gem install terragona`
|
19
|
+
|
20
|
+
or add `gem 'terragona'` to your Gemfile
|
21
|
+
|
22
|
+
Usage
|
23
|
+
-----
|
24
|
+
|
25
|
+
First, create a db in postgres and install the postgis extension.
|
26
|
+
|
27
|
+
You can use the GeoNames API, with the Terragona::API class, or download
|
28
|
+
a dump, (Terragona::Dump class) and specify the dump file path in opts. The API is faster
|
29
|
+
but less accurate (max 1000 points per request). The dump is more accurate but much slower (please,
|
30
|
+
use country dumps, not the world dump: it's to big -~9 million points- and could take lots of time.). For example:
|
31
|
+
with the API, the Italy polygon is drawn using 1000 points. With the dump, the input is ~95.000 points.
|
32
|
+
You can use the `max_points` option to limit this number.
|
33
|
+
|
34
|
+
The slow part of the process is when points are filtered: the ones that are isolated are discarded.
|
35
|
+
This has to be refactored.
|
36
|
+
|
37
|
+
Besides the source of the points, options `target_percent` and `max_distance_ratio` control the shape of
|
38
|
+
the polygons. See the options.
|
39
|
+
|
40
|
+
|
41
|
+
With API
|
42
|
+
```ruby
|
43
|
+
require 'terragona'
|
44
|
+
|
45
|
+
opts = {...}
|
46
|
+
countries=[{:name=>'Argentina',:fcode=>'PCLI',:country=>'AR'},
|
47
|
+
{:name=>'Uruguay',:fcode=>'PCLI',:country=>'UY'}]
|
48
|
+
|
49
|
+
terragona = Terragona::API.new(opts)
|
50
|
+
terragona.create_polygons_family(countries, 'countries', 'countries_subdivisions')
|
51
|
+
|
52
|
+
```
|
53
|
+
|
54
|
+
With Dump, and using returned children places
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
require 'terragona'
|
58
|
+
|
59
|
+
opts={
|
60
|
+
:default_country=>'IT',
|
61
|
+
:target_percent=> 0.85,
|
62
|
+
:max_distance_ratio=>1.6,
|
63
|
+
:db_username=>'mydbuser',
|
64
|
+
:db_password=>'mydbpsswd',
|
65
|
+
:db_name=>'mydb',
|
66
|
+
:dump=>'/path/to/dump/IT.txt'}
|
67
|
+
|
68
|
+
italy=[{:name=>'Italy',:fcode=>'PCLI'}]
|
69
|
+
|
70
|
+
terragona=Terragona::Dump.new(opts)
|
71
|
+
result=terragona.create_polygons_family(italy,'italy','italian_regions')
|
72
|
+
|
73
|
+
italian_rest=[]
|
74
|
+
result.each {|r|
|
75
|
+
italian_rest.concat(r[:children_places])
|
76
|
+
}
|
77
|
+
terragona.create_polygons_family(italian_rest,'province','comuni')
|
78
|
+
```
|
79
|
+
|
80
|
+
Methods
|
81
|
+
-------
|
82
|
+
|
83
|
+
```
|
84
|
+
create_polygons(<array of places>, options)
|
85
|
+
|
86
|
+
create_polygons_family(<array of places>, <first order geometries table name>, <second order geometries table name>, options)
|
87
|
+
```
|
88
|
+
|
89
|
+
Each place in the array of places is a hash with this keys:
|
90
|
+
|
91
|
+
```
|
92
|
+
:name
|
93
|
+
:fcode GeoNames Feature Code
|
94
|
+
:id (optional)
|
95
|
+
:children_fcode (optional)
|
96
|
+
:country (optional)
|
97
|
+
:field_to_compare (optional) (:adminCode1, :adminCode2 or :adminCode3)
|
98
|
+
:field_to_compare_value (optional)
|
99
|
+
```
|
100
|
+
|
101
|
+
The methods create the tables, fill them with polygons and return the following hash:
|
102
|
+
|
103
|
+
```
|
104
|
+
{:children_places=>array of hashes, :points=>array of points([x,y]), :place_name=>string, :place_id=>string}
|
105
|
+
```
|
106
|
+
|
107
|
+
Options
|
108
|
+
------
|
109
|
+
|
110
|
+
```
|
111
|
+
dump Only for Dump. Path to dump file.
|
112
|
+
max_points Only for Dump. Max number of points to consider from
|
113
|
+
dump file.
|
114
|
+
default_country Default country.
|
115
|
+
geonames_username Only for API. Geonames API username.
|
116
|
+
cache_expiration_time Default: 7200.
|
117
|
+
projection Default: EPSG 4326 (WGS84).
|
118
|
+
target_percent Require to draw the concave polygons.
|
119
|
+
Closer to 1: convex. Closer to 0, concave. Default: 0.8.
|
120
|
+
allow_holes Can the polygons have holes? Default: false.
|
121
|
+
max_distance_ratio Points distant more than this ratio times from the average
|
122
|
+
distance between points are not considered. Default: 1.6.
|
123
|
+
minimal_polygon_points Minimal number of points to build a polygon.
|
124
|
+
dont_create_polygons (boolean) Default: false.
|
125
|
+
table Table where polygons are saved. This option is overriden
|
126
|
+
by args of create_polygons_family method.
|
127
|
+
```
|
128
|
+
|
129
|
+
Postgres options
|
130
|
+
```
|
131
|
+
db_name The db *must* have the Postgis extension installed.
|
132
|
+
db_username
|
133
|
+
db_password
|
134
|
+
db_host Default: localhost.
|
135
|
+
db_port Default: 5432.
|
136
|
+
db_max_connections Default: 10.
|
137
|
+
```
|
138
|
+
|
139
|
+
TODO
|
140
|
+
----
|
141
|
+
- [x] Check of geometry type before saving
|
142
|
+
- [x] Use dumps as input (not only API)
|
143
|
+
- [ ] Generate multipolygon in ConcaveHull. Use some clustering algorithm.
|
144
|
+
- [ ] Improve/replace distant points algorithm. Use some clustering algorithm.
|
145
|
+
|
146
|
+
Useful data
|
147
|
+
-----------
|
148
|
+
* [GeoNames Country Codes](http://www.geonames.org/countries/)
|
149
|
+
* [GeoNames Feature Codes](http://www.geonames.org/export/codes.html)
|
150
|
+
* [GeoNames Dumps download page](http://download.geonames.org/export/dump/)
|
151
|
+
|
152
|
+
License
|
153
|
+
-------
|
154
|
+
|
155
|
+
MIT.
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require_relative './geonames'
|
2
|
+
require_relative './concave_hull'
|
3
|
+
require_relative './version'
|
4
|
+
|
5
|
+
module Terragona
|
6
|
+
class Base
|
7
|
+
def initialize(options={})
|
8
|
+
@options=options
|
9
|
+
@minimal_polygon_points = options[:minimal_polygon_points] || 5
|
10
|
+
end
|
11
|
+
|
12
|
+
def create_polygons(names,options={})
|
13
|
+
opts=@options.merge(options)
|
14
|
+
|
15
|
+
concave_hull = ConcaveHull.new(opts) if not opts[:dont_create_polygons]
|
16
|
+
|
17
|
+
names.map{|n|
|
18
|
+
name = @geonames.search(n)
|
19
|
+
|
20
|
+
if name[:points].count < @minimal_polygon_points
|
21
|
+
puts "No points for #{n[:name]}"
|
22
|
+
next
|
23
|
+
end
|
24
|
+
|
25
|
+
unless opts[:dont_create_polygons]
|
26
|
+
if concave_hull.perform(name[:points],name[:place_name],name[:place_id])
|
27
|
+
puts "Polygon created for #{n[:name]}"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
name
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
34
|
+
def create_polygons_family(names,parents_table,children_table,opts={})
|
35
|
+
created_names = create_polygons(names,opts.merge({:table => parents_table}))
|
36
|
+
children = []
|
37
|
+
created_names.each {|c|
|
38
|
+
children.concat(c[:children_places])
|
39
|
+
}
|
40
|
+
create_polygons(children,opts.merge({:table => children_table}))
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class API < Base
|
45
|
+
def initialize (options={})
|
46
|
+
super
|
47
|
+
@geonames = GeoNames::API.new(options)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class Dump < Base
|
52
|
+
def initialize (options={})
|
53
|
+
super
|
54
|
+
@geonames = GeoNames::Dump.new(options)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'geokit'
|
2
|
+
require 'sequel'
|
3
|
+
|
4
|
+
module Terragona
|
5
|
+
class ConcaveHull
|
6
|
+
def initialize(options = {})
|
7
|
+
@projection = options[:projection] || 4326
|
8
|
+
@table = options[:table] || 'concave_hull'
|
9
|
+
@target_percent = options[:target_percent] || 0.8
|
10
|
+
@allow_holes = options[:allow_holes]
|
11
|
+
@allow_holes = false if @allow_holes.nil?
|
12
|
+
@max_distance_ratio = options[:max_distance_ratio] || 1.6
|
13
|
+
|
14
|
+
db_options={
|
15
|
+
:database=> options[:db_name],
|
16
|
+
:user=> options[:db_username],
|
17
|
+
:password=> options[:db_password],
|
18
|
+
:host=> options[:db_host] || 'localhost',
|
19
|
+
:port=> options[:db_port] || 5432,
|
20
|
+
:max_connections=> options[:db_max_connections] || 10
|
21
|
+
}
|
22
|
+
|
23
|
+
@db = Sequel.postgres(db_options)
|
24
|
+
|
25
|
+
create_table
|
26
|
+
end
|
27
|
+
|
28
|
+
def perform(points,tags,id)
|
29
|
+
filtered_points=filter_points_by_distance(points)
|
30
|
+
points_stringified=stringify_points(filtered_points)
|
31
|
+
|
32
|
+
query = %Q{
|
33
|
+
ST_ConcaveHull(
|
34
|
+
ST_GeomFromText(
|
35
|
+
'MULTIPOINT(
|
36
|
+
#{points_stringified}
|
37
|
+
)',
|
38
|
+
#{@projection}
|
39
|
+
),
|
40
|
+
#{@target_percent},
|
41
|
+
#{@allow_holes})
|
42
|
+
}
|
43
|
+
|
44
|
+
create_concave_hull(query,tags,filtered_points.count,id)
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
def create_table
|
49
|
+
@db << "DROP TABLE IF EXISTS #{@table};"
|
50
|
+
@db << "CREATE TABLE #{@table} (id BIGINT PRIMARY KEY, name TEXT, count INT);"
|
51
|
+
@db << "SELECT AddGeometryColumn('#{@table}', 'geometry',#{@projection}, 'POLYGON', 2);"
|
52
|
+
end
|
53
|
+
|
54
|
+
def create_concave_hull(query, tags, count, id)
|
55
|
+
begin
|
56
|
+
return unless @db["SELECT ST_GeometryType(#{query})"].first[:st_geometrytype] == 'ST_Polygon'
|
57
|
+
@db << "INSERT INTO #{@table} (id, name, count, geometry) VALUES (#{id},'#{clean_str tags}',#{count},#{query})"
|
58
|
+
rescue
|
59
|
+
puts "Error with #{tags}, Id: #{id}, #{count} points."
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def drop_points_table(table)
|
64
|
+
@db << "DROP TABLE IF EXISTS #{table};"
|
65
|
+
end
|
66
|
+
|
67
|
+
def filter_points_by_distance(points)
|
68
|
+
random_points = points.count > 200 ? (0..200).map {|e|
|
69
|
+
points[rand(points.count)]
|
70
|
+
}.uniq : points
|
71
|
+
|
72
|
+
distances = points.map {|p0|
|
73
|
+
sum = random_points.map {|pf|
|
74
|
+
Geokit::LatLng.new(p0[:y],p0[:x]).distance_to(Geokit::LatLng.new(pf[:y],pf[:x]))
|
75
|
+
}
|
76
|
+
{:y=>p0[:y],:x=>p0[:x],:average=>average(sum)}
|
77
|
+
}
|
78
|
+
average_distance=average(distances.map{|d| d[:average]})
|
79
|
+
distances.map {|d|
|
80
|
+
d if d[:average] <= average_distance * @max_distance_ratio
|
81
|
+
}.compact
|
82
|
+
end
|
83
|
+
|
84
|
+
def stringify_points(points)
|
85
|
+
points.map{|p|
|
86
|
+
"#{p[:x]} #{p[:y]}"
|
87
|
+
}.join(',')
|
88
|
+
end
|
89
|
+
|
90
|
+
def average(arr)
|
91
|
+
arr.inject{ |sum, el| sum + el }.to_f / arr.size
|
92
|
+
end
|
93
|
+
|
94
|
+
def clean_str(str)
|
95
|
+
str.gsub("'",' ')
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,186 @@
|
|
1
|
+
require 'httpi'
|
2
|
+
require 'diskcached'
|
3
|
+
require 'similar_text'
|
4
|
+
require 'json'
|
5
|
+
require 'csv'
|
6
|
+
|
7
|
+
module Terragona
|
8
|
+
module GeoNames
|
9
|
+
class Base
|
10
|
+
def initialize(args = {})
|
11
|
+
@default_country = args[:default_country]
|
12
|
+
end
|
13
|
+
|
14
|
+
def search(options)
|
15
|
+
country = options[:country] || @default_country
|
16
|
+
id = options[:id]
|
17
|
+
name = options[:name]
|
18
|
+
fcode = options[:fcode]
|
19
|
+
|
20
|
+
children_fcode = options[:children_fcode] || case fcode
|
21
|
+
when 'PCLI' then 'ADM1'
|
22
|
+
when 'ADM1' then 'ADM2'
|
23
|
+
when 'ADM2' then 'ADM3'
|
24
|
+
when 'ADM3' then 'ADM4'
|
25
|
+
when 'ADM4' then 'ADM5'
|
26
|
+
when 'PPLC' then 'PPLX'
|
27
|
+
end
|
28
|
+
|
29
|
+
field_to_compare = options[:field_to_compare] || calculate_field_to_compare(fcode)
|
30
|
+
children_field_to_compare = calculate_field_to_compare(children_fcode)
|
31
|
+
field_to_compare_value = options[:field_to_compare_value]
|
32
|
+
|
33
|
+
if field_to_compare_value.nil?
|
34
|
+
fetch_geonames(name,country,nil,nil).each{|g|
|
35
|
+
if g[:fcode] == fcode
|
36
|
+
name = g[:name]
|
37
|
+
id = g[:geonameId]
|
38
|
+
field_to_compare_value = g[field_to_compare]
|
39
|
+
break
|
40
|
+
end
|
41
|
+
}
|
42
|
+
end
|
43
|
+
|
44
|
+
points = []
|
45
|
+
children_places = []
|
46
|
+
|
47
|
+
fetch_geonames(nil,country,field_to_compare,field_to_compare_value).each{|g|
|
48
|
+
points.push({:x=>g[:lng],:y=>g[:lat]})
|
49
|
+
if g[:fcode] == children_fcode and children_field_to_compare
|
50
|
+
child={:name=>g[:name],
|
51
|
+
:id=>g[:geonameId],
|
52
|
+
:fcode=>g[:fcode],
|
53
|
+
:country=>g[:countryCode],
|
54
|
+
:field_to_compare_value=>g[children_field_to_compare]}
|
55
|
+
children_places.push(child)
|
56
|
+
end
|
57
|
+
}
|
58
|
+
|
59
|
+
{:children_places=>children_places,:points=>points,:place_name=>name,:place_id=>id}
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def calculate_field_to_compare(fcode)
|
65
|
+
case fcode
|
66
|
+
when 'PCLI' then :countryCode
|
67
|
+
when 'ADM1' then :adminCode1
|
68
|
+
when 'ADM2' then :adminCode2
|
69
|
+
when 'ADM3' then :adminCode3
|
70
|
+
else nil
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class API < Base
|
76
|
+
URL = 'http://api.geonames.org/searchJSON'
|
77
|
+
def initialize(args = {})
|
78
|
+
super
|
79
|
+
@username = args[:geonames_username]
|
80
|
+
cache_expiration_time = args[:cache_expiration_time] || 7200
|
81
|
+
@cache=Diskcached.new('/tmp/cache',cache_expiration_time,true)
|
82
|
+
end
|
83
|
+
|
84
|
+
def fetch_geonames(name,country,admin_code_type,admin_code)
|
85
|
+
admin_code_str = admin_code ? "&#{admin_code_type}=#{admin_code}" : ''
|
86
|
+
name_str = name ? "q=#{name}&" : ''
|
87
|
+
|
88
|
+
@cache.cache("geonames_name=#{name}&country=#{country}#{admin_code_str}&full666") do
|
89
|
+
url = URI.escape(%Q{#{URL}?#{name_str}country=#{country}#{admin_code_str}&style=FULL&order_by=relevance&maxRows=1000&username=#{@username}})
|
90
|
+
request = HTTPI::Request.new(url)
|
91
|
+
data = HTTPI.get(request)
|
92
|
+
JSON.parse(data.body,:symbolize_names=>true)[:geonames]
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
class Dump < Base
|
99
|
+
HEADERS = [:geonameId,
|
100
|
+
:name,
|
101
|
+
:asciiname,
|
102
|
+
:alternatenames,
|
103
|
+
:lat,
|
104
|
+
:lng,
|
105
|
+
:fclass,
|
106
|
+
:fcode,
|
107
|
+
:countryCode,
|
108
|
+
:cc2,
|
109
|
+
:adminCode1,
|
110
|
+
:adminCode2,
|
111
|
+
:adminCode3,
|
112
|
+
:adminCode4,
|
113
|
+
:population,
|
114
|
+
:elevation,
|
115
|
+
:dem,
|
116
|
+
:timezone,
|
117
|
+
:modificaion_date]
|
118
|
+
|
119
|
+
def initialize(args = {})
|
120
|
+
super
|
121
|
+
|
122
|
+
if not args[:dump]
|
123
|
+
puts 'No dump file provided'
|
124
|
+
return
|
125
|
+
end
|
126
|
+
@file = File.open(args[:dump])
|
127
|
+
@admin_codes_cache = {:adminCode1=>{},
|
128
|
+
:adminCode2=>{},
|
129
|
+
:adminCode3=>{},
|
130
|
+
:adminCode4=>{}}
|
131
|
+
|
132
|
+
@max_points = args[:max_points]
|
133
|
+
end
|
134
|
+
|
135
|
+
def fetch_geonames(name, country, admin_code_type, admin_code)
|
136
|
+
if admin_code_type and
|
137
|
+
@admin_codes_cache[admin_code_type] and
|
138
|
+
@admin_codes_cache[admin_code_type][admin_code]
|
139
|
+
|
140
|
+
@admin_codes_cache[admin_code_type][admin_code]
|
141
|
+
|
142
|
+
else
|
143
|
+
dump_parser(name, country, admin_code_type, admin_code)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
private
|
148
|
+
def dump_parser(name, country, admin_code_type, admin_code)
|
149
|
+
@file.rewind
|
150
|
+
records = @max_points ? @file.first(@max_points) : @file
|
151
|
+
records.map {|l|
|
152
|
+
begin
|
153
|
+
raw=CSV.parse_line(l,{:col_sep => "\t"})
|
154
|
+
rescue
|
155
|
+
next
|
156
|
+
end
|
157
|
+
|
158
|
+
hash = {}
|
159
|
+
HEADERS.each_with_index {|h,index| hash[h] = raw[index]}
|
160
|
+
|
161
|
+
cache_hash(hash)
|
162
|
+
|
163
|
+
next unless (name and name.similar(hash[:name]) > 30) or
|
164
|
+
(name and hash[:alternatenames] and hash[:alternatenames].include? name) or
|
165
|
+
(admin_code_type and admin_code and hash[admin_code_type] == admin_code)
|
166
|
+
|
167
|
+
next if (country and country != hash[:countryCode])
|
168
|
+
|
169
|
+
hash
|
170
|
+
}.compact
|
171
|
+
end
|
172
|
+
|
173
|
+
def cache_hash(hash)
|
174
|
+
[:adminCode1,:adminCode2,:adminCode3,:adminCode4].each {|adm|
|
175
|
+
if hash[adm] and not @admin_codes_cache[adm][hash[adm]]
|
176
|
+
@admin_codes_cache[adm][hash[adm]]=[]
|
177
|
+
end
|
178
|
+
if hash[adm] and not @admin_codes_cache[adm][hash[adm]].include? hash
|
179
|
+
@admin_codes_cache[adm][hash[adm]].push(hash)
|
180
|
+
end
|
181
|
+
}
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
end
|
186
|
+
end
|
data/lib/terragona.rb
ADDED
data/terragona.gemspec
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'terragona/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "terragona"
|
8
|
+
spec.version = Terragona::VERSION
|
9
|
+
spec.authors = ["Bruno Salerno"]
|
10
|
+
spec.email = ["br.salerno@gmail.com"]
|
11
|
+
spec.description = %q{Create polygons for geonames places}
|
12
|
+
spec.summary = %q{Use API or dumps as input, draw polygons, and store them in a Postgres/Postgis db}
|
13
|
+
spec.homepage = "https://github.com/BrunoSalerno/terragona"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_runtime_dependency "pg"
|
22
|
+
spec.add_runtime_dependency "sequel", "~> 3.48.0"
|
23
|
+
spec.add_runtime_dependency "httpi", "~> 1.1.0"
|
24
|
+
spec.add_runtime_dependency "diskcached"
|
25
|
+
spec.add_runtime_dependency "similar_text", "~> 0.0.4"
|
26
|
+
spec.add_runtime_dependency "geokit"
|
27
|
+
|
28
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
29
|
+
spec.add_development_dependency "rake"
|
30
|
+
spec.add_development_dependency "pry-debugger"
|
31
|
+
end
|
metadata
ADDED
@@ -0,0 +1,201 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: terragona
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Bruno Salerno
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2015-01-04 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: pg
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: sequel
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 3.48.0
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 3.48.0
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: httpi
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 1.1.0
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 1.1.0
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: diskcached
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :runtime
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: similar_text
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ~>
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: 0.0.4
|
86
|
+
type: :runtime
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ~>
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: 0.0.4
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: geokit
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
type: :runtime
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: bundler
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ~>
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '1.3'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ~>
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '1.3'
|
126
|
+
- !ruby/object:Gem::Dependency
|
127
|
+
name: rake
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
129
|
+
none: false
|
130
|
+
requirements:
|
131
|
+
- - ! '>='
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '0'
|
134
|
+
type: :development
|
135
|
+
prerelease: false
|
136
|
+
version_requirements: !ruby/object:Gem::Requirement
|
137
|
+
none: false
|
138
|
+
requirements:
|
139
|
+
- - ! '>='
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: '0'
|
142
|
+
- !ruby/object:Gem::Dependency
|
143
|
+
name: pry-debugger
|
144
|
+
requirement: !ruby/object:Gem::Requirement
|
145
|
+
none: false
|
146
|
+
requirements:
|
147
|
+
- - ! '>='
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: '0'
|
150
|
+
type: :development
|
151
|
+
prerelease: false
|
152
|
+
version_requirements: !ruby/object:Gem::Requirement
|
153
|
+
none: false
|
154
|
+
requirements:
|
155
|
+
- - ! '>='
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
version: '0'
|
158
|
+
description: Create polygons for geonames places
|
159
|
+
email:
|
160
|
+
- br.salerno@gmail.com
|
161
|
+
executables: []
|
162
|
+
extensions: []
|
163
|
+
extra_rdoc_files: []
|
164
|
+
files:
|
165
|
+
- .gitignore
|
166
|
+
- Gemfile
|
167
|
+
- Gemfile.lock
|
168
|
+
- README.md
|
169
|
+
- lib/terragona.rb
|
170
|
+
- lib/terragona/base.rb
|
171
|
+
- lib/terragona/concave_hull.rb
|
172
|
+
- lib/terragona/geonames.rb
|
173
|
+
- lib/terragona/version.rb
|
174
|
+
- terragona.gemspec
|
175
|
+
homepage: https://github.com/BrunoSalerno/terragona
|
176
|
+
licenses:
|
177
|
+
- MIT
|
178
|
+
post_install_message:
|
179
|
+
rdoc_options: []
|
180
|
+
require_paths:
|
181
|
+
- lib
|
182
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
183
|
+
none: false
|
184
|
+
requirements:
|
185
|
+
- - ! '>='
|
186
|
+
- !ruby/object:Gem::Version
|
187
|
+
version: '0'
|
188
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
189
|
+
none: false
|
190
|
+
requirements:
|
191
|
+
- - ! '>='
|
192
|
+
- !ruby/object:Gem::Version
|
193
|
+
version: '0'
|
194
|
+
requirements: []
|
195
|
+
rubyforge_project:
|
196
|
+
rubygems_version: 1.8.23
|
197
|
+
signing_key:
|
198
|
+
specification_version: 3
|
199
|
+
summary: Use API or dumps as input, draw polygons, and store them in a Postgres/Postgis
|
200
|
+
db
|
201
|
+
test_files: []
|