has_geo_lookup 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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +132 -0
- data/LICENSE.txt +21 -0
- data/README.md +389 -0
- data/Rakefile +8 -0
- data/lib/generators/has_geo_lookup/install_generator.rb +66 -0
- data/lib/generators/has_geo_lookup/templates/INSTALL.md +60 -0
- data/lib/generators/has_geo_lookup/templates/create_feature_codes.rb.erb +17 -0
- data/lib/generators/has_geo_lookup/templates/create_geoboundaries.rb.erb +24 -0
- data/lib/generators/has_geo_lookup/templates/create_geoboundaries_metros.rb.erb +10 -0
- data/lib/generators/has_geo_lookup/templates/create_geonames.rb.erb +40 -0
- data/lib/generators/has_geo_lookup/templates/create_geonames_metros.rb.erb +10 -0
- data/lib/generators/has_geo_lookup/templates/create_metros.rb.erb +17 -0
- data/lib/has_geo_lookup/concern.rb +813 -0
- data/lib/has_geo_lookup/index_checker.rb +360 -0
- data/lib/has_geo_lookup/models/feature_code.rb +194 -0
- data/lib/has_geo_lookup/models/geoboundary.rb +220 -0
- data/lib/has_geo_lookup/models/geoname.rb +152 -0
- data/lib/has_geo_lookup/models/metro.rb +247 -0
- data/lib/has_geo_lookup/railtie.rb +11 -0
- data/lib/has_geo_lookup/version.rb +5 -0
- data/lib/has_geo_lookup.rb +28 -0
- data/lib/tasks/has_geo_lookup.rake +111 -0
- data/sig/has_geo_lookup.rbs +4 -0
- metadata +183 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 556e93ddd252169fbee37ee0bfcb91c43ec80815c66db3dcafa0ff3d47cfe56e
|
4
|
+
data.tar.gz: a49a34d5cf6f0ab901a18203a9da65be64f7f65faec1e54a4d4d9d7bdb102f40
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 446d6d3e34e04ba14f418ef6d3ce62d7dc4a8045931ad6d3a9f2b12b8b315f996d2a6eeb279b43da40c4881fd5914ed194c7ff34e927f1b03425e790dc236ad0
|
7
|
+
data.tar.gz: 1bc6c467f8d9e3b592a8522dae18013d11e7d2ea6ea93001972fc528ebcddc3a423b62bff07d36b7ab122dfd0127bfe7b257f0c0f69a5eba4584355160a049a8
|
data/CHANGELOG.md
ADDED
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
2
|
+
|
3
|
+
## Our Pledge
|
4
|
+
|
5
|
+
We as members, contributors, and leaders pledge to make participation in our
|
6
|
+
community a harassment-free experience for everyone, regardless of age, body
|
7
|
+
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
8
|
+
identity and expression, level of experience, education, socio-economic status,
|
9
|
+
nationality, personal appearance, race, caste, color, religion, or sexual
|
10
|
+
identity and orientation.
|
11
|
+
|
12
|
+
We pledge to act and interact in ways that contribute to an open, welcoming,
|
13
|
+
diverse, inclusive, and healthy community.
|
14
|
+
|
15
|
+
## Our Standards
|
16
|
+
|
17
|
+
Examples of behavior that contributes to a positive environment for our
|
18
|
+
community include:
|
19
|
+
|
20
|
+
* Demonstrating empathy and kindness toward other people
|
21
|
+
* Being respectful of differing opinions, viewpoints, and experiences
|
22
|
+
* Giving and gracefully accepting constructive feedback
|
23
|
+
* Accepting responsibility and apologizing to those affected by our mistakes,
|
24
|
+
and learning from the experience
|
25
|
+
* Focusing on what is best not just for us as individuals, but for the overall
|
26
|
+
community
|
27
|
+
|
28
|
+
Examples of unacceptable behavior include:
|
29
|
+
|
30
|
+
* The use of sexualized language or imagery, and sexual attention or advances of
|
31
|
+
any kind
|
32
|
+
* Trolling, insulting or derogatory comments, and personal or political attacks
|
33
|
+
* Public or private harassment
|
34
|
+
* Publishing others' private information, such as a physical or email address,
|
35
|
+
without their explicit permission
|
36
|
+
* Other conduct which could reasonably be considered inappropriate in a
|
37
|
+
professional setting
|
38
|
+
|
39
|
+
## Enforcement Responsibilities
|
40
|
+
|
41
|
+
Community leaders are responsible for clarifying and enforcing our standards of
|
42
|
+
acceptable behavior and will take appropriate and fair corrective action in
|
43
|
+
response to any behavior that they deem inappropriate, threatening, offensive,
|
44
|
+
or harmful.
|
45
|
+
|
46
|
+
Community leaders have the right and responsibility to remove, edit, or reject
|
47
|
+
comments, commits, code, wiki edits, issues, and other contributions that are
|
48
|
+
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
49
|
+
decisions when appropriate.
|
50
|
+
|
51
|
+
## Scope
|
52
|
+
|
53
|
+
This Code of Conduct applies within all community spaces, and also applies when
|
54
|
+
an individual is officially representing the community in public spaces.
|
55
|
+
Examples of representing our community include using an official email address,
|
56
|
+
posting via an official social media account, or acting as an appointed
|
57
|
+
representative at an online or offline event.
|
58
|
+
|
59
|
+
## Enforcement
|
60
|
+
|
61
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
62
|
+
reported to the community leaders responsible for enforcement at
|
63
|
+
[INSERT CONTACT METHOD].
|
64
|
+
All complaints will be reviewed and investigated promptly and fairly.
|
65
|
+
|
66
|
+
All community leaders are obligated to respect the privacy and security of the
|
67
|
+
reporter of any incident.
|
68
|
+
|
69
|
+
## Enforcement Guidelines
|
70
|
+
|
71
|
+
Community leaders will follow these Community Impact Guidelines in determining
|
72
|
+
the consequences for any action they deem in violation of this Code of Conduct:
|
73
|
+
|
74
|
+
### 1. Correction
|
75
|
+
|
76
|
+
**Community Impact**: Use of inappropriate language or other behavior deemed
|
77
|
+
unprofessional or unwelcome in the community.
|
78
|
+
|
79
|
+
**Consequence**: A private, written warning from community leaders, providing
|
80
|
+
clarity around the nature of the violation and an explanation of why the
|
81
|
+
behavior was inappropriate. A public apology may be requested.
|
82
|
+
|
83
|
+
### 2. Warning
|
84
|
+
|
85
|
+
**Community Impact**: A violation through a single incident or series of
|
86
|
+
actions.
|
87
|
+
|
88
|
+
**Consequence**: A warning with consequences for continued behavior. No
|
89
|
+
interaction with the people involved, including unsolicited interaction with
|
90
|
+
those enforcing the Code of Conduct, for a specified period of time. This
|
91
|
+
includes avoiding interactions in community spaces as well as external channels
|
92
|
+
like social media. Violating these terms may lead to a temporary or permanent
|
93
|
+
ban.
|
94
|
+
|
95
|
+
### 3. Temporary Ban
|
96
|
+
|
97
|
+
**Community Impact**: A serious violation of community standards, including
|
98
|
+
sustained inappropriate behavior.
|
99
|
+
|
100
|
+
**Consequence**: A temporary ban from any sort of interaction or public
|
101
|
+
communication with the community for a specified period of time. No public or
|
102
|
+
private interaction with the people involved, including unsolicited interaction
|
103
|
+
with those enforcing the Code of Conduct, is allowed during this period.
|
104
|
+
Violating these terms may lead to a permanent ban.
|
105
|
+
|
106
|
+
### 4. Permanent Ban
|
107
|
+
|
108
|
+
**Community Impact**: Demonstrating a pattern of violation of community
|
109
|
+
standards, including sustained inappropriate behavior, harassment of an
|
110
|
+
individual, or aggression toward or disparagement of classes of individuals.
|
111
|
+
|
112
|
+
**Consequence**: A permanent ban from any sort of public interaction within the
|
113
|
+
community.
|
114
|
+
|
115
|
+
## Attribution
|
116
|
+
|
117
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
118
|
+
version 2.1, available at
|
119
|
+
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
|
120
|
+
|
121
|
+
Community Impact Guidelines were inspired by
|
122
|
+
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
|
123
|
+
|
124
|
+
For answers to common questions about this code of conduct, see the FAQ at
|
125
|
+
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
|
126
|
+
[https://www.contributor-covenant.org/translations][translations].
|
127
|
+
|
128
|
+
[homepage]: https://www.contributor-covenant.org
|
129
|
+
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
|
130
|
+
[Mozilla CoC]: https://github.com/mozilla/diversity
|
131
|
+
[FAQ]: https://www.contributor-covenant.org/faq
|
132
|
+
[translations]: https://www.contributor-covenant.org/translations
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2025 Michael Edlund
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,389 @@
|
|
1
|
+
# HasGeoLookup
|
2
|
+
|
3
|
+
Rails gem for geographic lookup functionality using GeoBoundaries.org and Geonames.org datasets. Provides coordinate validation with automatic radian/degree detection, spatial containment queries, distance-based lookups, and metropolitan area support.
|
4
|
+
|
5
|
+
## Features
|
6
|
+
|
7
|
+
- **Coordinate Validation** - Automatically detect and convert between radians and degrees using country boundary validation
|
8
|
+
- **Spatial Queries** - PostGIS-optimized boundary containment with graceful fallback for other databases
|
9
|
+
- **Distance-Based Lookup** - Find nearest geographic features within specified radius
|
10
|
+
- **Administrative Boundaries** - Query points against ADM1-ADM4 level boundaries from GeoBoundaries.org
|
11
|
+
- **Metro Area Support** - Define custom metropolitan areas as collections of administrative boundaries
|
12
|
+
- **Multi-Source Comparison** - Compare geographic data across different sources with extensible hooks
|
13
|
+
- **Data Coverage Analysis** - Utilities for analyzing geographic data completeness
|
14
|
+
|
15
|
+
## Installation
|
16
|
+
|
17
|
+
Add this line to your application's Gemfile:
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
gem 'has_geo_lookup'
|
21
|
+
```
|
22
|
+
|
23
|
+
And then execute:
|
24
|
+
|
25
|
+
```bash
|
26
|
+
bundle install
|
27
|
+
```
|
28
|
+
|
29
|
+
## Setup
|
30
|
+
|
31
|
+
### 1. Generate Database Migrations
|
32
|
+
|
33
|
+
```bash
|
34
|
+
rails generate has_geo_lookup:install
|
35
|
+
```
|
36
|
+
|
37
|
+
This creates migrations for all required tables:
|
38
|
+
- `geonames` - Geographic place data from Geonames.org
|
39
|
+
- `geoboundaries` - Administrative boundaries from GeoBoundaries.org
|
40
|
+
- `feature_codes` - Classification codes for geographic features
|
41
|
+
- `metros` - Metropolitan area definitions
|
42
|
+
- `metros_geoboundaries` - Join table for metro-boundary associations
|
43
|
+
|
44
|
+
### 2. Run Migrations
|
45
|
+
|
46
|
+
```bash
|
47
|
+
rails db:migrate
|
48
|
+
```
|
49
|
+
|
50
|
+
### 3. Import Geographic Data
|
51
|
+
|
52
|
+
Import geoboundaries and geonames for specific countries:
|
53
|
+
|
54
|
+
```bash
|
55
|
+
# Import both datasets for United States (recommended)
|
56
|
+
rails geo:import[US]
|
57
|
+
|
58
|
+
# Import for multiple countries
|
59
|
+
rails geo:import[CA] # Canada
|
60
|
+
rails geo:import[GB] # United Kingdom
|
61
|
+
|
62
|
+
# Or import datasets separately
|
63
|
+
rails geoboundaries:import[US] # Administrative boundaries only
|
64
|
+
rails geonames:import[US] # Geographic place names only
|
65
|
+
```
|
66
|
+
|
67
|
+
### 4. Include in Your Models
|
68
|
+
|
69
|
+
Add the concern to any model with `latitude` and `longitude` attributes:
|
70
|
+
|
71
|
+
```ruby
|
72
|
+
class Listing < ApplicationRecord
|
73
|
+
include HasGeoLookup
|
74
|
+
|
75
|
+
# Your model must have these attributes:
|
76
|
+
# - latitude (decimal)
|
77
|
+
# - longitude (decimal)
|
78
|
+
end
|
79
|
+
```
|
80
|
+
|
81
|
+
## Database Requirements
|
82
|
+
|
83
|
+
### MySQL 8.0+ with Spatial Extensions (Recommended)
|
84
|
+
|
85
|
+
The gem works well with MySQL 8.0+ spatial support:
|
86
|
+
|
87
|
+
```ruby
|
88
|
+
# Gemfile
|
89
|
+
gem 'mysql2'
|
90
|
+
```
|
91
|
+
|
92
|
+
MySQL 8.0+ provides:
|
93
|
+
- GEOMETRY column type for storing boundary polygons
|
94
|
+
- ST_Contains, ST_GeomFromText, and other spatial functions
|
95
|
+
- Coordinate validation using actual country boundaries
|
96
|
+
- Spatial indexing for performance
|
97
|
+
|
98
|
+
### PostgreSQL with PostGIS (Also Supported)
|
99
|
+
|
100
|
+
For PostgreSQL databases with PostGIS:
|
101
|
+
|
102
|
+
```ruby
|
103
|
+
# Gemfile
|
104
|
+
gem 'activerecord-postgis-adapter'
|
105
|
+
```
|
106
|
+
|
107
|
+
PostGIS provides enhanced spatial capabilities and may offer better performance for complex spatial operations.
|
108
|
+
|
109
|
+
### Other Databases
|
110
|
+
|
111
|
+
For SQLite and databases without spatial extensions:
|
112
|
+
- Spatial queries will be limited but functional
|
113
|
+
- Coordinate validation uses fallback detection methods
|
114
|
+
- Basic geographic lookup functionality remains available
|
115
|
+
|
116
|
+
## Usage
|
117
|
+
|
118
|
+
### Basic Geographic Lookup
|
119
|
+
|
120
|
+
```ruby
|
121
|
+
listing = Listing.find(123)
|
122
|
+
|
123
|
+
# Find nearest populated places within 50km
|
124
|
+
places = listing.nearest_geonames(
|
125
|
+
feature_code: "PPL", # Populated place
|
126
|
+
radius_km: 50,
|
127
|
+
limit: 10
|
128
|
+
)
|
129
|
+
|
130
|
+
# Find containing administrative boundaries
|
131
|
+
boundaries = listing.containing_geoboundaries(level: "ADM2") # Counties
|
132
|
+
|
133
|
+
# Get geographic summary
|
134
|
+
summary = listing.compare_geo_sources
|
135
|
+
puts summary
|
136
|
+
```
|
137
|
+
|
138
|
+
### Coordinate Validation
|
139
|
+
|
140
|
+
The gem automatically handles coordinate format detection:
|
141
|
+
|
142
|
+
```ruby
|
143
|
+
# These coordinates could be in radians or degrees
|
144
|
+
lat, lng = 0.768131687, 0.077125907 # Uzès, France in radians
|
145
|
+
|
146
|
+
# Automatically detects format and converts to degrees
|
147
|
+
validated_lat, validated_lng = listing.validate_and_convert_coordinates(lat, lng, "FR")
|
148
|
+
# => [44.011, 4.419] (converted to degrees)
|
149
|
+
|
150
|
+
# Already in degrees - no conversion needed
|
151
|
+
validated_lat, validated_lng = listing.validate_and_convert_coordinates(44.011, 4.419, "FR")
|
152
|
+
# => [44.011, 4.419] (unchanged)
|
153
|
+
```
|
154
|
+
|
155
|
+
### Metro Area Support
|
156
|
+
|
157
|
+
Define custom metropolitan areas:
|
158
|
+
|
159
|
+
```ruby
|
160
|
+
# Create a metro area
|
161
|
+
bay_area = Metro.create!(
|
162
|
+
name: "San Francisco Bay Area",
|
163
|
+
country_code: "US",
|
164
|
+
population: 7_750_000
|
165
|
+
)
|
166
|
+
|
167
|
+
# Associate with counties (geoboundaries)
|
168
|
+
sf_county = Geoboundary.find_by(name: "San Francisco County")
|
169
|
+
alameda_county = Geoboundary.find_by(name: "Alameda County")
|
170
|
+
bay_area.geoboundaries << [sf_county, alameda_county]
|
171
|
+
|
172
|
+
# Check if coordinates are in metro area
|
173
|
+
bay_area.contains_point?(37.7749, -122.4194) # => true
|
174
|
+
|
175
|
+
# Get metro area statistics
|
176
|
+
bay_area.total_area_km2 # => 18040.5
|
177
|
+
bay_area.population_density # => 429.8
|
178
|
+
bay_area.boundary_names # => ["San Francisco County", "Alameda County", ...]
|
179
|
+
```
|
180
|
+
|
181
|
+
### Data Coverage Analysis
|
182
|
+
|
183
|
+
```ruby
|
184
|
+
# Analyze data completeness
|
185
|
+
coverage = HasGeoLookup::DataCoverage.coverage_status("US")
|
186
|
+
puts coverage[:geonames_count] # Number of geonames records
|
187
|
+
puts coverage[:boundaries_count] # Number of boundary records
|
188
|
+
puts coverage[:feature_coverage] # Coverage by feature type
|
189
|
+
|
190
|
+
# Check if specific data exists
|
191
|
+
HasGeoLookup::DataCoverage.has_boundary_data?("US", "ADM2") # => true
|
192
|
+
HasGeoLookup::DataCoverage.has_geonames_data?("US") # => true
|
193
|
+
```
|
194
|
+
|
195
|
+
### Advanced Usage
|
196
|
+
|
197
|
+
#### Custom Source Comparison
|
198
|
+
|
199
|
+
Extend geographic comparison for your specific data sources:
|
200
|
+
|
201
|
+
```ruby
|
202
|
+
class Listing < ApplicationRecord
|
203
|
+
include HasGeoLookup
|
204
|
+
|
205
|
+
# Define additional data sources for comparison
|
206
|
+
def additional_source_columns
|
207
|
+
['api_latitude', 'api_longitude', 'geocoded_lat', 'geocoded_lng']
|
208
|
+
end
|
209
|
+
|
210
|
+
def additional_source_legend
|
211
|
+
{
|
212
|
+
'API' => 'Third-party API data',
|
213
|
+
'Geocoded' => 'Address-based geocoding'
|
214
|
+
}
|
215
|
+
end
|
216
|
+
|
217
|
+
def get_source_value(column_name)
|
218
|
+
case column_name
|
219
|
+
when 'api_latitude', 'api_longitude'
|
220
|
+
# Custom logic to fetch from your API
|
221
|
+
when 'geocoded_lat', 'geocoded_lng'
|
222
|
+
# Custom logic for geocoded coordinates
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
226
|
+
```
|
227
|
+
|
228
|
+
#### Distance Calculations
|
229
|
+
|
230
|
+
```ruby
|
231
|
+
# Find records within radius using database query
|
232
|
+
nearby_listings = Listing.joins(:nearest_geonames)
|
233
|
+
.where("geonames.feature_code = 'PPL'")
|
234
|
+
.where(geonames: { country_code: "US" })
|
235
|
+
|
236
|
+
# Calculate distance between two points
|
237
|
+
distance_km = listing.distance_to_point(40.7128, -74.0060)
|
238
|
+
```
|
239
|
+
|
240
|
+
## Configuration
|
241
|
+
|
242
|
+
### Rails Generators
|
243
|
+
|
244
|
+
Customize migration generation:
|
245
|
+
|
246
|
+
```ruby
|
247
|
+
# config/application.rb
|
248
|
+
config.generators do |g|
|
249
|
+
g.has_geo_lookup_skip_migrations = false # Set to true to skip auto-generation
|
250
|
+
end
|
251
|
+
```
|
252
|
+
|
253
|
+
### PostGIS Configuration
|
254
|
+
|
255
|
+
For PostGIS databases, ensure the extension is enabled:
|
256
|
+
|
257
|
+
```ruby
|
258
|
+
# In a migration or database setup
|
259
|
+
enable_extension 'postgis'
|
260
|
+
```
|
261
|
+
|
262
|
+
## Data Sources
|
263
|
+
|
264
|
+
This gem integrates data from:
|
265
|
+
|
266
|
+
- **[GeoBoundaries.org](https://www.geoboundaries.org/)** - Administrative boundary polygons (ADM1-ADM4 levels)
|
267
|
+
- **[Geonames.org](http://www.geonames.org/)** - Geographic place names and coordinates
|
268
|
+
- **[ISO 3166](https://en.wikipedia.org/wiki/ISO_3166)** - Country code validation
|
269
|
+
|
270
|
+
## Performance Considerations
|
271
|
+
|
272
|
+
### Database Setup and Optimization
|
273
|
+
|
274
|
+
The gem includes built-in tools to analyze and set up the complete database requirements for HasGeoLookup functionality:
|
275
|
+
|
276
|
+
```bash
|
277
|
+
# Check HasGeoLookup setup for all models (columns and indexes)
|
278
|
+
rake has_geo_lookup:check_setup
|
279
|
+
|
280
|
+
# Preview what setup changes would be made (dry run)
|
281
|
+
rake has_geo_lookup:preview_setup
|
282
|
+
|
283
|
+
# Generate Rails migration for complete HasGeoLookup setup
|
284
|
+
rake has_geo_lookup:create_setup
|
285
|
+
|
286
|
+
# Analyze a specific model in detail
|
287
|
+
rake has_geo_lookup:analyze_model[Listing]
|
288
|
+
```
|
289
|
+
|
290
|
+
**Programmatic Setup Management:**
|
291
|
+
|
292
|
+
```ruby
|
293
|
+
# Get performance analysis for all models
|
294
|
+
results = HasGeoLookup::IndexChecker.analyze_all_models
|
295
|
+
|
296
|
+
# Check a specific model
|
297
|
+
analysis = HasGeoLookup::IndexChecker.check_model(Listing)
|
298
|
+
puts "Missing indexes: #{analysis[:missing_indexes]}"
|
299
|
+
puts "Recommendations: #{analysis[:recommendations]}"
|
300
|
+
|
301
|
+
# Generate migration file for missing columns and indexes
|
302
|
+
migration_path = HasGeoLookup::IndexChecker.generate_index_migration
|
303
|
+
# Or for a specific model:
|
304
|
+
migration_path = HasGeoLookup::IndexChecker.generate_index_migration(Listing)
|
305
|
+
|
306
|
+
# Generate a comprehensive performance report
|
307
|
+
puts HasGeoLookup::IndexChecker.performance_report
|
308
|
+
```
|
309
|
+
|
310
|
+
**Complete Database Setup:**
|
311
|
+
|
312
|
+
The `create_setup` task generates proper Rails migration files that can be committed to version control and run as part of your deployment process. The migration includes:
|
313
|
+
|
314
|
+
- **Required columns**: Adds `latitude` and `longitude` decimal columns if missing
|
315
|
+
- **Recommended indexes**: Creates optimized database indexes for geographic queries
|
316
|
+
- **Proper rollback**: Includes reversible `down` migration methods
|
317
|
+
|
318
|
+
This ensures all environments get the same database structure and maintains proper migration history.
|
319
|
+
|
320
|
+
### Database Optimization
|
321
|
+
|
322
|
+
For large datasets:
|
323
|
+
|
324
|
+
```ruby
|
325
|
+
# Coordinate indexes (essential for spatial queries)
|
326
|
+
add_index :your_table, [:latitude, :longitude]
|
327
|
+
add_index :your_table, :latitude
|
328
|
+
add_index :your_table, :longitude
|
329
|
+
|
330
|
+
# Geographic attribute indexes (for filtering and joining)
|
331
|
+
add_index :your_table, :country
|
332
|
+
add_index :your_table, :state_or_province
|
333
|
+
add_index :your_table, :city
|
334
|
+
add_index :your_table, :postal_code
|
335
|
+
|
336
|
+
# For geoboundaries and geonames tables
|
337
|
+
add_index :geonames, [:country_code, :feature_code]
|
338
|
+
add_index :geoboundaries, [:level, :shape_iso]
|
339
|
+
|
340
|
+
# For PostGIS, spatial indexes are created automatically
|
341
|
+
```
|
342
|
+
|
343
|
+
### Query Optimization
|
344
|
+
|
345
|
+
```ruby
|
346
|
+
# Use specific feature codes to limit results
|
347
|
+
places = listing.nearest_geonames(
|
348
|
+
feature_code: ["PPL", "PPLA", "PPLA2"], # Cities and towns only
|
349
|
+
limit: 5
|
350
|
+
)
|
351
|
+
|
352
|
+
# Specify administrative levels for boundaries
|
353
|
+
boundaries = listing.containing_geoboundaries(level: ["ADM1", "ADM2"])
|
354
|
+
```
|
355
|
+
|
356
|
+
## Development
|
357
|
+
|
358
|
+
### Running Tests
|
359
|
+
|
360
|
+
```bash
|
361
|
+
cd has_geo_lookup
|
362
|
+
bundle install
|
363
|
+
ruby -Ilib -Itest test/has_geo_lookup_test.rb
|
364
|
+
```
|
365
|
+
|
366
|
+
### Contributing
|
367
|
+
|
368
|
+
1. Fork the repository
|
369
|
+
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
|
370
|
+
3. Add tests for your changes
|
371
|
+
4. Ensure tests pass
|
372
|
+
5. Commit your changes (`git commit -am 'Add amazing feature'`)
|
373
|
+
6. Push to the branch (`git push origin feature/amazing-feature`)
|
374
|
+
7. Create a Pull Request
|
375
|
+
|
376
|
+
## License
|
377
|
+
|
378
|
+
This gem is available as open source under the terms of the MIT License.
|
379
|
+
|
380
|
+
## Changelog
|
381
|
+
|
382
|
+
### Version 0.1.0
|
383
|
+
- Initial release
|
384
|
+
- Coordinate validation with radian/degree detection
|
385
|
+
- PostGIS spatial queries with fallback support
|
386
|
+
- GeoBoundaries and Geonames integration
|
387
|
+
- Metro area support
|
388
|
+
- Comprehensive test suite
|
389
|
+
- Rails generators for easy setup
|
data/Rakefile
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rails/generators'
|
4
|
+
require 'rails/generators/migration'
|
5
|
+
|
6
|
+
module HasGeoLookup
|
7
|
+
module Generators
|
8
|
+
# Generates database migrations for HasGeoLookup gem
|
9
|
+
#
|
10
|
+
# Creates all necessary tables for geographic lookup functionality:
|
11
|
+
# - geonames: Geographic place data from Geonames.org
|
12
|
+
# - geoboundaries: Administrative boundaries from GeoBoundaries.org
|
13
|
+
# - feature_codes: Classification codes for geographic features
|
14
|
+
# - metros: Metropolitan area definitions
|
15
|
+
# - geoboundaries_metros: Join table for metro-boundary associations
|
16
|
+
# - geonames_metros: Join table for geoname-metro associations
|
17
|
+
#
|
18
|
+
# @example Generate migrations
|
19
|
+
# rails generate has_geo_lookup:install
|
20
|
+
class InstallGenerator < Rails::Generators::Base
|
21
|
+
include Rails::Generators::Migration
|
22
|
+
|
23
|
+
source_root File.expand_path('templates', __dir__)
|
24
|
+
|
25
|
+
desc 'Generate HasGeoLookup database migrations'
|
26
|
+
|
27
|
+
# Provide a migration timestamp
|
28
|
+
def self.next_migration_number(dirname)
|
29
|
+
next_migration_number = current_migration_number(dirname) + 1
|
30
|
+
ActiveRecord::Migration.next_migration_number(next_migration_number)
|
31
|
+
end
|
32
|
+
|
33
|
+
def create_migrations
|
34
|
+
migration_template 'create_geonames.rb.erb', 'db/migrate/create_geonames.rb'
|
35
|
+
sleep 1 # Ensure different timestamps
|
36
|
+
migration_template 'create_geoboundaries.rb.erb', 'db/migrate/create_geoboundaries.rb'
|
37
|
+
sleep 1
|
38
|
+
migration_template 'create_feature_codes.rb.erb', 'db/migrate/create_feature_codes.rb'
|
39
|
+
sleep 1
|
40
|
+
migration_template 'create_metros.rb.erb', 'db/migrate/create_metros.rb'
|
41
|
+
sleep 1
|
42
|
+
migration_template 'create_geoboundaries_metros.rb.erb', 'db/migrate/create_geoboundaries_metros.rb'
|
43
|
+
sleep 1
|
44
|
+
migration_template 'create_geonames_metros.rb.erb', 'db/migrate/create_geonames_metros.rb'
|
45
|
+
end
|
46
|
+
|
47
|
+
def display_readme
|
48
|
+
readme "INSTALL.md"
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def migration_version
|
54
|
+
if Rails.version.start_with?('8')
|
55
|
+
'[8.0]'
|
56
|
+
elsif Rails.version.start_with?('7')
|
57
|
+
'[7.0]'
|
58
|
+
elsif Rails.version.start_with?('6')
|
59
|
+
'[6.0]'
|
60
|
+
else
|
61
|
+
'[5.0]'
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# HasGeoLookup Installation Complete
|
2
|
+
|
3
|
+
The HasGeoLookup gem migrations have been generated successfully!
|
4
|
+
|
5
|
+
## Next Steps
|
6
|
+
|
7
|
+
1. **Run the migrations:**
|
8
|
+
```bash
|
9
|
+
bin/rails db:migrate
|
10
|
+
```
|
11
|
+
|
12
|
+
2. **For PostGIS users (recommended):**
|
13
|
+
- Ensure PostGIS extension is available in your PostgreSQL database
|
14
|
+
- The migrations will automatically enable PostGIS and create spatial indexes
|
15
|
+
- This provides optimal performance for boundary queries and coordinate validation
|
16
|
+
|
17
|
+
3. **For non-PostGIS databases:**
|
18
|
+
- The gem will work with MySQL, SQLite, or other databases
|
19
|
+
- Spatial queries will be limited, but basic geographic lookup functionality remains available
|
20
|
+
- Coordinate validation will use fallback methods instead of boundary containment
|
21
|
+
|
22
|
+
4. **Import geographic data:**
|
23
|
+
```bash
|
24
|
+
# Import geoboundaries and geonames for a specific country (recommended)
|
25
|
+
bin/rails geo:import[US]
|
26
|
+
|
27
|
+
# Or import datasets separately
|
28
|
+
bin/rails geoboundaries:import[US] # Administrative boundaries
|
29
|
+
bin/rails geonames:import[US] # Geographic place names
|
30
|
+
```
|
31
|
+
|
32
|
+
5. **Include the concern in your models:**
|
33
|
+
```ruby
|
34
|
+
class Listing < ApplicationRecord
|
35
|
+
include HasGeoLookup
|
36
|
+
|
37
|
+
# Your model must have latitude and longitude attributes
|
38
|
+
# The gem will provide geographic lookup methods
|
39
|
+
end
|
40
|
+
```
|
41
|
+
|
42
|
+
## Available Tables
|
43
|
+
|
44
|
+
The following tables have been created:
|
45
|
+
|
46
|
+
- **`geonames`** - Geographic place data from Geonames.org
|
47
|
+
- **`geoboundaries`** - Administrative boundary polygons from GeoBoundaries.org
|
48
|
+
- **`feature_codes`** - Classification codes for geographic features
|
49
|
+
- **`metros`** - Custom metropolitan area definitions
|
50
|
+
- **`metros_geoboundaries`** - Join table linking metros to their constituent boundaries
|
51
|
+
|
52
|
+
## Key Features
|
53
|
+
|
54
|
+
- **Coordinate validation** - Automatically detect and convert between radians/degrees
|
55
|
+
- **Boundary containment** - Find which administrative boundaries contain a point
|
56
|
+
- **Distance-based lookup** - Find nearest geographic features within a radius
|
57
|
+
- **Metro area support** - Group boundaries into custom metropolitan regions
|
58
|
+
- **Multi-source comparison** - Compare geographic data across different sources
|
59
|
+
|
60
|
+
For detailed usage examples and API documentation, see the gem's README.
|