@@ -90,6 +90,46 @@ function renderSearchResults(data) {
9090 } ) ;
9191}
9292
93+ // Reverse geocode GPS coordinates — returns a single result formatted
94+ // identically to forward search so it renders in the same dropdown
95+ async function runReverseGeoSearch ( lat , lng ) {
96+ try {
97+ const searchWait = Math . max ( 0 , 1100 - ( Date . now ( ) - _lastNominatimCall ) ) ;
98+ if ( searchWait > 0 ) await new Promise ( r => setTimeout ( r , searchWait ) ) ;
99+ _lastNominatimCall = Date . now ( ) ;
100+ const r = await fetch ( `https://nominatim.openstreetmap.org/reverse?lat=${ lat } &lon=${ lng } &format=json&addressdetails=1&accept-language=en` ) ;
101+ if ( ! r . ok ) throw new Error ( `HTTP ${ r . status } ` ) ;
102+ const d = await r . json ( ) ;
103+ if ( d . error ) {
104+ dLoading . style . display = 'none' ;
105+ dResults . innerHTML = '<div style="padding:9px 12px;font-size:.73rem;color:var(--muted)">No results for these coordinates</div>' ;
106+ return ;
107+ }
108+ // Build a meaningful display_name from address fields — Nominatim reverse often
109+ // returns a street number as the first element which isn't useful for navigation
110+ const a = d . address || { } ;
111+ // Pick the most meaningful name — landmark/POI first, then road, then area
112+ const mainName = d . name || a . tourism || a . building || a . amenity || a . leisure || a . road || a . suburb || a . neighbourhood || a . city || a . town || a . village || a . state || d . display_name . split ( ',' ) [ 0 ] ;
113+ const city = a . city || a . town || a . village || '' ;
114+ const country = a . country || '' ;
115+ const displayParts = [ mainName , city , country ] . filter ( ( v , i , arr ) => v && arr . indexOf ( v ) === i ) ;
116+ const displayName = displayParts . join ( ', ' ) ;
117+
118+ const result = [ {
119+ display_name : displayName ,
120+ lat : String ( lat ) ,
121+ lon : String ( lng ) ,
122+ address : a
123+ } ] ;
124+ _searchCache [ `${ lat } ,${ lng } ` ] = result ;
125+ renderSearchResults ( result ) ;
126+ } catch ( err ) {
127+ console . warn ( 'Reverse geo search failed:' , err ) ;
128+ dLoading . style . display = 'none' ;
129+ dResults . innerHTML = '<div style="padding:9px 12px;font-size:.73rem;color:var(--accent2)">Coordinate lookup failed — try again</div>' ;
130+ }
131+ }
132+
93133async function runDestSearch ( q ) {
94134 if ( _isOffline ) {
95135 dResults . style . display = 'block' ; dLoading . style . display = 'none' ;
@@ -98,6 +138,20 @@ async function runDestSearch(q) {
98138 }
99139 dResults . style . display = 'block' ; dLoading . style . display = 'block' ;
100140 dResults . querySelectorAll ( '.dest-item' ) . forEach ( el => el . remove ( ) ) ;
141+
142+ // Detect GPS coordinate input (e.g. "48.8566, 2.3522" or "48.8566° N, 2.3522° E")
143+ const coordMatch = q . trim ( ) . match ( / ^ ( - ? \d + \. ? \d * ) [ ° \s ] * [ N S n s ] ? \s * , \s * ( - ? \d + \. ? \d * ) [ ° \s ] * [ E W e w ] ? $ / ) ;
144+ if ( coordMatch ) {
145+ let lat = parseFloat ( coordMatch [ 1 ] ) , lng = parseFloat ( coordMatch [ 2 ] ) ;
146+ // Handle S/W suffixes making coords negative
147+ if ( / [ S s ] / . test ( q ) ) lat = - Math . abs ( lat ) ;
148+ if ( / [ W w ] / . test ( q ) ) lng = - Math . abs ( lng ) ;
149+ if ( lat >= - 90 && lat <= 90 && lng >= - 180 && lng <= 180 ) {
150+ await runReverseGeoSearch ( lat , lng ) ;
151+ return ;
152+ }
153+ }
154+
101155 const zoom = map ? map . getZoom ( ) : 0 ;
102156 const cacheKey = q + ( zoom >= 3 ? `@${ map . getCenter ( ) . lng . toFixed ( 1 ) } ,${ map . getCenter ( ) . lat . toFixed ( 1 ) } ` : '' ) ;
103157 if ( _searchCache [ cacheKey ] ) { renderSearchResults ( _searchCache [ cacheKey ] ) ; return ; }
@@ -166,7 +220,7 @@ function flyTo(item) {
166220 } ) ;
167221 const popup = new maplibregl . Popup ( { maxWidth :'240px' , closeButton :true , offset :30 } )
168222 . setLngLat ( [ lng , lat ] )
169- . setHTML ( `<div class="dest-popup"><div class="dest-popup-name">${ esc ( main ) } </div><div class="dest-popup-detail">${ esc ( detail ) } </div><button class="dest-popup-btn" onclick="openPinPickerAt(${ lat } ,${ lng } )">+ Add photos to this location</button><button class="dest-popup-btn" onclick="pinEmptyLocation(${ lat } ,${ lng } )">📌 Pin this location</button></div>` )
223+ . setHTML ( `<div class="dest-popup"><div class="dest-popup-name">${ esc ( main ) } </div><div class="dest-popup-detail">${ esc ( detail ) } </div><button class="dest-popup-btn" onclick="openPinPickerAt(${ lat } ,${ lng } )">+ Add photos to this location</button><button class="dest-popup-btn" onclick="pinEmptyLocation(${ lat } ,${ lng } )">📌 Pin this location</button><div class="dest-popup-coords"> ${ lat . toFixed ( 3 ) } , ${ lng . toFixed ( 3 ) } </div>< /div>` )
170224 . addTo ( map ) ;
171225 popup . on ( 'close' , ( ) => { if ( destMarkerObj ) { destMarkerObj . marker . remove ( ) ; destMarkerObj = null ; } } ) ;
172226 destMarkerObj = { marker, popup} ;
@@ -211,7 +265,7 @@ function reopenDestPopup(){
211265 const detail = country || '' ;
212266 const popup = new maplibregl . Popup ( { maxWidth :'240px' , closeButton :true , offset :30 } )
213267 . setLngLat ( [ lng , lat ] )
214- . setHTML ( `<div class="dest-popup"><div class="dest-popup-name">${ esc ( main ) } </div><div class="dest-popup-detail">${ esc ( detail ) } </div><button class="dest-popup-btn" onclick="openPinPickerAt(${ lat } ,${ lng } )">+ Add photos to this location</button><button class="dest-popup-btn" onclick="pinEmptyLocation(${ lat } ,${ lng } )">📌 Pin this location</button></div>` )
268+ . setHTML ( `<div class="dest-popup"><div class="dest-popup-name">${ esc ( main ) } </div><div class="dest-popup-detail">${ esc ( detail ) } </div><button class="dest-popup-btn" onclick="openPinPickerAt(${ lat } ,${ lng } )">+ Add photos to this location</button><button class="dest-popup-btn" onclick="pinEmptyLocation(${ lat } ,${ lng } )">📌 Pin this location</button><div class="dest-popup-coords"> ${ lat . toFixed ( 3 ) } , ${ lng . toFixed ( 3 ) } </div>< /div>` )
215269 . addTo ( map ) ;
216270 popup . on ( 'close' , ( ) => { if ( destMarkerObj ) { destMarkerObj . marker . remove ( ) ; destMarkerObj = null ; } } ) ;
217271 destMarkerObj . popup = popup ;
0 commit comments