dawarich/app/services/location_search/geocoding_service.rb

97 lines
2.4 KiB
Ruby
Raw Permalink Normal View History

2025-08-30 17:18:16 -04:00
# frozen_string_literal: true
module LocationSearch
class GeocodingService
2025-09-01 16:04:55 -04:00
MAX_RESULTS = 10
2025-08-30 17:18:16 -04:00
2025-09-03 13:28:26 -04:00
def initialize(query)
@query = query
2025-08-30 17:18:16 -04:00
end
2025-09-03 13:28:26 -04:00
def search
2025-08-30 17:18:16 -04:00
return [] if query.blank?
2025-09-03 13:56:38 -04:00
perform_geocoding_search(query)
2025-08-30 17:18:16 -04:00
rescue StandardError => e
Rails.logger.error "Geocoding search failed for query '#{query}': #{e.message}"
[]
end
def provider_name
Geocoder.config.lookup.to_s.capitalize
end
private
2025-09-03 13:28:26 -04:00
attr_reader :query
2025-09-03 12:51:00 -04:00
2025-09-03 13:28:26 -04:00
def perform_geocoding_search(query)
2025-08-30 17:18:16 -04:00
results = Geocoder.search(query, limit: MAX_RESULTS)
return [] if results.blank?
2025-09-03 13:28:26 -04:00
normalize_geocoding_results(results)
2025-08-30 17:18:16 -04:00
end
def normalize_geocoding_results(results)
2025-09-03 17:27:59 -04:00
normalized_results = results.filter_map do |result|
lat = result.latitude.to_f
lon = result.longitude.to_f
next unless valid_coordinates?(lat, lon)
2025-09-03 14:05:03 -04:00
{
2025-09-03 17:27:59 -04:00
lat: lat,
lon: lon,
2025-09-03 13:28:26 -04:00
name: result.address&.split(',')&.first || 'Unknown location',
address: result.address || '',
type: result.data&.dig('type') || result.data&.dig('class') || 'unknown',
provider_data: {
osm_id: result.data&.dig('osm_id'),
place_rank: result.data&.dig('place_rank'),
importance: result.data&.dig('importance')
}
2025-08-30 17:18:16 -04:00
}
end
2025-09-03 13:28:26 -04:00
deduplicate_results(normalized_results)
2025-08-30 17:18:16 -04:00
end
def deduplicate_results(results)
deduplicated = []
2025-09-03 12:51:00 -04:00
2025-08-30 17:18:16 -04:00
results.each do |result|
# Check if there's already a result within 100m
duplicate = deduplicated.find do |existing|
distance = calculate_distance_in_meters(
2025-08-30 17:18:16 -04:00
result[:lat], result[:lon],
existing[:lat], existing[:lon]
)
distance < 100 # meters
end
2025-09-03 12:51:00 -04:00
2025-08-30 17:18:16 -04:00
deduplicated << result unless duplicate
end
2025-09-03 12:51:00 -04:00
2025-08-30 17:18:16 -04:00
deduplicated
end
def calculate_distance_in_meters(lat1, lon1, lat2, lon2)
# Use Geocoder's distance calculation (same as in Distanceable concern)
distance_km = Geocoder::Calculations.distance_between(
[lat1, lon1],
[lat2, lon2],
units: :km
)
2025-09-03 14:05:03 -04:00
# Convert to meters and handle potential nil/invalid results
return 0 unless distance_km.is_a?(Numeric) && distance_km.finite?
2025-09-03 14:05:03 -04:00
distance_km * 1000 # Convert km to meters
2025-08-30 17:18:16 -04:00
end
2025-09-03 17:27:59 -04:00
def valid_coordinates?(lat, lon)
lat.between?(-90, 90) && lon.between?(-180, 180)
end
2025-08-30 17:18:16 -04:00
end
2025-09-03 12:51:00 -04:00
end