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 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: []