Parse lat and lon independently when using dms notation

This commit is contained in:
Anton Khorev 2024-07-13 17:40:01 +03:00
parent 392d3d1226
commit 7917a7db80
2 changed files with 22 additions and 7 deletions

View file

@ -206,13 +206,9 @@ class GeocoderController < ApplicationController
if query = params[:query]
query.strip!
if latlon = query.match(/^(?<ns>[NS])\s*(?<nsd>\d{1,3}(?:\.\d+)?)°?\W*(?<ew>[EW])\s*(?<ewd>\d{1,3}(?:\.\d+)?)°?$/) || # [NSEW] decimal degrees
query.match(/^(?<nsd>\d{1,3}(?:\.\d+)?)°?\s*(?<ns>[NS])\W*(?<ewd>\d{1,3}(?:\.\d+)?)°?\s*(?<ew>[EW])$/) || # decimal degrees [NSEW]
query.match(/^(?<ns>[NS])\s*(?<nsd>\d{1,3})°?(?:\s*(?<nsm>\d{1,3}(?:\.\d+)?)[']?)\W*(?<ew>[EW])\s*(?<ewd>\d{1,3})°?(?:\s*(?<ewm>\d{1,3}(?:\.\d+)?)[']?)$/) || # [NSEW] degrees, decimal minutes
query.match(/^(?<nsd>\d{1,3})°?(?:\s*(?<nsm>\d{1,3}(?:\.\d+)?)[']?)\s*(?<ns>[NS])\W*(?<ewd>\d{1,3})°?(?:\s*(?<ewm>\d{1,3}(?:\.\d+)?)[']?)\s*(?<ew>[EW])$/) || # degrees, decimal minutes [NSEW]
query.match(/^(?<ns>[NS])\s*(?<nsd>\d{1,3})°?\s*(?<nsm>\d{1,2})[']?(?:\s*(?<nss>\d{1,3}(?:\.\d+)?)["″]?)\W*(?<ew>[EW])\s*(?<ewd>\d{1,3})°?\s*(?<ewm>\d{1,2})[']?(?:\s*(?<ews>\d{1,3}(?:\.\d+)?)["″]?)$/) || # [NSEW] degrees, minutes, decimal seconds
query.match(/^(?<nsd>\d{1,3})°?\s*(?<nsm>\d{1,2})[']?(?:\s*(?<nss>\d{1,3}(?:\.\d+)?)["″]?)\s*(?<ns>[NS])\W*(?<ewd>\d{1,3})°?\s*(?<ewm>\d{1,2})[']?(?:\s*(?<ews>\d{1,3}(?:\.\d+)?)["″]?)\s*(?<ew>[EW])$/) # degrees, minutes, decimal seconds [NSEW]
params.merge!(to_decdeg(latlon.named_captures)).delete(:query)
if latlon = query.match(/^(?<ns>[NS])\s*#{dms_regexp('ns')}\W*(?<ew>[EW])\s*#{dms_regexp('ew')}$/) ||
query.match(/^#{dms_regexp('ns')}\s*(?<ns>[NS])\W*#{dms_regexp('ew')}\s*(?<ew>[EW])$/)
params.merge!(to_decdeg(latlon.named_captures.compact)).delete(:query)
elsif latlon = query.match(%r{^(?<lat>[+-]?\d+(?:\.\d+)?)(?:\s+|\s*[,/]\s*)(?<lon>[+-]?\d+(?:\.\d+)?)$})
params.merge!(:lat => latlon["lat"], :lon => latlon["lon"]).delete(:query)
@ -224,6 +220,14 @@ class GeocoderController < ApplicationController
params.permit(:query, :lat, :lon, :latlon_digits, :zoom, :minlat, :minlon, :maxlat, :maxlon)
end
def dms_regexp(name_prefix)
/
(?: (?<#{name_prefix}d>\d{1,3}(?:\.\d+)?)°? ) |
(?: (?<#{name_prefix}d>\d{1,3})°?\s*(?<#{name_prefix}m>\d{1,2}(?:\.\d+)?)[']? ) |
(?: (?<#{name_prefix}d>\d{1,3})°?\s*(?<#{name_prefix}m>\d{1,2})[']?\s*(?<#{name_prefix}s>\d{1,2}(?:\.\d+)?)["″]? )
/x
end
def to_decdeg(captures)
ns = captures.fetch("ns").casecmp("s").zero? ? -1 : 1
nsd = BigDecimal(captures.fetch("nsd", "0"))

View file

@ -261,6 +261,17 @@ class GeocoderControllerTest < ActionDispatch::IntegrationTest
end
end
#
# Test identification of lat/lon pairs with mixed precision
def test_identify_latlon_ne_mixed_precision
latlon_check "N1 5 E15", 1.083333, 15
latlon_check "N1 5 9 E15", 1.085833, 15
latlon_check "N1 5 9 E1 5", 1.085833, 1.083333
latlon_check "N15 E1 5", 15, 1.083333
latlon_check "N15 E1 5 9", 15, 1.085833
latlon_check "N1 5 E1 5 9", 1.083333, 1.085833
end
#
# Test identification of lat/lon pairs with values close to zero
def test_identify_latlon_close_to_zero