terragona 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ .idea
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source "https://rubygems.org/"
2
+
3
+ gem 'pg'
4
+ gem 'sequel', "~> 3.48.0"
5
+ gem 'httpi', "~> 1.1.0"
6
+ gem 'pry-debugger'
7
+ gem 'diskcached'
8
+ gem 'similar_text', '~> 0.0.4'
9
+ gem 'geokit'
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
+ ![alt tag](https://cloud.githubusercontent.com/assets/6061036/5606205/2216ff3c-9402-11e4-9123-fff91369208e.png)
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
@@ -0,0 +1,3 @@
1
+ module Terragona
2
+ VERSION = "0.1.0"
3
+ end
data/lib/terragona.rb ADDED
@@ -0,0 +1,2 @@
1
+ require_relative './terragona/base'
2
+
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: []