Class: Metro
- Inherits:
-
ActiveRecord::Base
- Object
- ActiveRecord::Base
- Metro
- Defined in:
- lib/has_geo_lookup/models/metro.rb
Overview
Metropolitan areas defined by collections of administrative boundaries
This model represents metropolitan areas (metro areas, urban agglomerations) as collections of administrative boundaries from GeoBoundaries.org. Rather than storing separate geometry, metros are defined by their constituent geoboundaries, allowing for flexible and maintainable metro definitions.
Metro areas are useful for:
- Real estate market analysis by metropolitan region
- Economic data aggregation across municipal boundaries
- Transportation and infrastructure planning
- Population and demographic analysis
Each metro can span multiple administrative levels and boundaries, reflecting the real-world nature of metropolitan areas that often cross county, city, and sometimes state boundaries.
Instance Attribute Summary collapse
-
#country_code ⇒ String
Primary country code for this metro area.
-
#details ⇒ String
Additional descriptive information about the metro.
-
#name ⇒ String
Metro area name (e.g., "San Francisco Bay Area", "Greater London").
-
#population ⇒ Integer
Estimated metro population (optional).
Instance Method Summary collapse
-
#admin_levels ⇒ Array<String>
Get administrative levels represented in this metro.
-
#boundary_names ⇒ Array<String>
Get all boundary names that define this metro.
-
#centroid ⇒ Array<Float>?
Get the geographic center (centroid) of this metro.
-
#contains_point?(latitude, longitude) ⇒ Boolean
Check if this metro contains the given coordinates.
-
#display_name ⇒ String
Returns a human-readable description of this metro.
-
#geographic_summary ⇒ Hash
Get a summary of this metro's geographic composition.
-
#multi_state? ⇒ Boolean
Check if this metro spans multiple states/provinces.
-
#population_density ⇒ Float?
Get population density (people per km²).
-
#total_area_km2 ⇒ Float?
Calculate the total area of this metro in square kilometers.
Instance Attribute Details
#country_code ⇒ String
Primary country code for this metro area
31 32 33 |
# File 'lib/has_geo_lookup/models/metro.rb', line 31 def country_code @country_code end |
#details ⇒ String
Additional descriptive information about the metro
31 32 33 |
# File 'lib/has_geo_lookup/models/metro.rb', line 31 def details @details end |
#name ⇒ String
Metro area name (e.g., "San Francisco Bay Area", "Greater London")
31 32 33 |
# File 'lib/has_geo_lookup/models/metro.rb', line 31 def name @name end |
#population ⇒ Integer
Estimated metro population (optional)
31 32 33 |
# File 'lib/has_geo_lookup/models/metro.rb', line 31 def population @population end |
Instance Method Details
#admin_levels ⇒ Array<String>
Get administrative levels represented in this metro
Returns the different administrative levels (ADM1, ADM2, etc.) that make up this metropolitan area.
155 156 157 |
# File 'lib/has_geo_lookup/models/metro.rb', line 155 def admin_levels geoboundaries.distinct.pluck(:level).compact.sort end |
#boundary_names ⇒ Array<String>
Get all boundary names that define this metro
Returns a list of all constituent boundary names for understanding the geographic composition of this metropolitan area.
141 142 143 |
# File 'lib/has_geo_lookup/models/metro.rb', line 141 def boundary_names geoboundaries.pluck(:name).compact.sort end |
#centroid ⇒ Array<Float>?
Get the geographic center (centroid) of this metro
Calculates the centroid of all constituent boundaries combined, providing a representative center point for the metropolitan area.
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 |
# File 'lib/has_geo_lookup/models/metro.rb', line 114 def centroid return nil unless geoboundaries.any? result = self.class.connection.select_one(<<~SQL.squish) SELECT ST_Y(ST_Centroid(ST_Union(boundary))) AS lat, ST_X(ST_Centroid(ST_Union(boundary))) AS lng FROM geoboundaries WHERE id IN (#{geoboundary_ids.join(',')}) SQL result ? [result['lat'].to_f, result['lng'].to_f] : nil rescue => e Rails.logger.warn "Error calculating metro centroid: #{e.}" nil end |
#contains_point?(latitude, longitude) ⇒ Boolean
Check if this metro contains the given coordinates
Uses MySQL spatial queries against all associated geoboundaries to determine if the point falls within any boundary that defines this metropolitan area.
64 65 66 67 68 69 70 71 72 73 74 75 76 |
# File 'lib/has_geo_lookup/models/metro.rb', line 64 def contains_point?(latitude, longitude) return false unless latitude && longitude return false unless geoboundaries.any? # Check if point is contained within any of the metro's boundaries geoboundaries.joins("INNER JOIN geoboundaries gb ON gb.id = geoboundaries.id") .where("ST_Contains(gb.boundary, ST_GeomFromText(?, 4326))", "POINT(#{latitude} #{longitude})") .exists? rescue => e Rails.logger.warn "Error checking metro point containment: #{e.}" false end |
#display_name ⇒ String
Returns a human-readable description of this metro
Combines name, details, country, and constituent boundary information for comprehensive metro identification.
201 202 203 204 205 206 207 208 209 210 211 212 213 214 |
# File 'lib/has_geo_lookup/models/metro.rb', line 201 def display_name parts = [name] parts << "(#{country_code})" if country_code.present? if geoboundaries.any? boundary_count = geoboundaries.count level_count = admin_levels.count parts << "#{boundary_count} boundaries, #{level_count} admin level#{'s' if level_count != 1}" end description = parts.join(" - ") description += "\n#{details}" if details.present? description end |
#geographic_summary ⇒ Hash
Get a summary of this metro's geographic composition
Returns detailed information about the boundaries and administrative levels that make up this metropolitan area.
236 237 238 239 240 241 242 243 244 245 246 |
# File 'lib/has_geo_lookup/models/metro.rb', line 236 def geographic_summary { total_boundaries: geoboundaries.count, by_level: geoboundaries.group(:level).count, boundary_names: boundary_names, spans_multiple_states: multi_state?, admin_levels: admin_levels, total_area_km2: total_area_km2, population_density: population_density } end |
#multi_state? ⇒ Boolean
Check if this metro spans multiple states/provinces
Determines if the metro includes boundaries from multiple ADM1 (state/province) level divisions.
169 170 171 172 |
# File 'lib/has_geo_lookup/models/metro.rb', line 169 def multi_state? state_boundaries = geoboundaries.where(level: "ADM1") state_boundaries.count > 1 end |
#population_density ⇒ Float?
Get population density (people per km²)
Calculates population density based on total population and area, if both values are available.
184 185 186 187 188 189 |
# File 'lib/has_geo_lookup/models/metro.rb', line 184 def population_density return nil unless population && total_area_km2 return nil if total_area_km2.zero? (population.to_f / total_area_km2).round(1) end |
#total_area_km2 ⇒ Float?
Calculate the total area of this metro in square kilometers
Sums the areas of all constituent geoboundaries, with handling for overlapping boundaries to avoid double-counting.
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
# File 'lib/has_geo_lookup/models/metro.rb', line 88 def total_area_km2 return nil unless geoboundaries.any? # Calculate total area using ST_Union to handle overlapping boundaries result = self.class.connection.select_value(<<~SQL.squish) SELECT ST_Area(ST_Transform(ST_Union(boundary), 3857)) / 1000000 AS total_area_km2 FROM geoboundaries WHERE id IN (#{geoboundary_ids.join(',')}) SQL result&.round(2) rescue => e Rails.logger.warn "Error calculating metro area: #{e.}" nil end |