dawarich/app/services/reverse_geocoding/places/fetch_data.rb
Arne Schwarck 908232d397
Fix reverse geocoding issue
Previously, reverse geocoding queries in the Photon lookup did not properly
limit results within a specified search radius, leading to inaccurate or
unexpected locations being returned. This fix ensures that the :radius
parameter is passed directly, just like :limit and :distance_sort, instead
of being nested under :params.
By aligning with the Photon lookup implementation in Geocoder, this change
improves accuracy and ensures that results are correctly filtered based on
proximity, resolving issues where points were incorrectly matched due to
missing radius constraints.

Resolves: Reverse geocoding mismatch for close proximity queries
2025-01-25 22:11:35 +01:00

106 lines
3 KiB
Ruby

# frozen_string_literal: true
# This class uses Komoot's Photon API
class ReverseGeocoding::Places::FetchData
attr_reader :place
IGNORED_OSM_VALUES = %w[house residential yes detached].freeze
IGNORED_OSM_KEYS = %w[highway railway].freeze
def initialize(place_id)
@place = Place.find(place_id)
end
def call
unless DawarichSettings.reverse_geocoding_enabled?
Rails.logger.warn('Reverse geocoding is not enabled')
return
end
first_place = reverse_geocoded_places.shift
update_place(first_place)
add_suggested_place_to_a_visit
reverse_geocoded_places.each { |reverse_geocoded_place| fetch_and_create_place(reverse_geocoded_place) }
end
private
def update_place(reverse_geocoded_place)
return if reverse_geocoded_place.nil?
data = reverse_geocoded_place.data
place.update!(
name: place_name(data),
latitude: data['geometry']['coordinates'][1],
longitude: data['geometry']['coordinates'][0],
city: data['properties']['city'],
country: data['properties']['country'],
geodata: data,
source: Place.sources[:photon],
reverse_geocoded_at: Time.current
)
end
def fetch_and_create_place(reverse_geocoded_place)
data = reverse_geocoded_place.data
new_place = find_place(data)
new_place.name = place_name(data)
new_place.city = data['properties']['city']
new_place.country = data['properties']['country']
new_place.geodata = data
new_place.source = :photon
new_place.save!
add_suggested_place_to_a_visit(suggested_place: new_place)
end
def reverse_geocoded?
place.geodata.present?
end
def add_suggested_place_to_a_visit(suggested_place: place)
visits = Place.near([suggested_place.latitude, suggested_place.longitude], 0.1).flat_map(&:visits)
visits.each do |visit|
next if visit.suggested_places.include?(suggested_place)
visit.suggested_places << suggested_place
end
end
def find_place(place_data)
found_place = Place.where(
"geodata->'properties'->>'osm_id' = ?", place_data['properties']['osm_id'].to_s
).first
return found_place if found_place.present?
Place.find_or_initialize_by(
latitude: place_data['geometry']['coordinates'][1].to_f.round(5),
longitude: place_data['geometry']['coordinates'][0].to_f.round(5)
)
end
def place_name(data)
name = data.dig('properties', 'name')
type = data.dig('properties', 'osm_value')&.capitalize&.gsub('_', ' ')
address = "#{data.dig('properties', 'postcode')} #{data.dig('properties', 'street')}"
address += " #{data.dig('properties', 'housenumber')}" if data.dig('properties', 'housenumber').present?
name ||= address
"#{name} (#{type})"
end
def reverse_geocoded_places
data = Geocoder.search([place.latitude, place.longitude], limit: 10, distance_sort: true, radius: 10)
data.reject do |place|
place.data['properties']['osm_value'].in?(IGNORED_OSM_VALUES) ||
place.data['properties']['osm_key'].in?(IGNORED_OSM_KEYS)
end
end
end