a2aabd37a5
Testy / Testy warstwy logicznej (silnik) (pull_request) Successful in 10m39s
Testy / Build obrazu silnika B (swisseph) (pull_request) Failing after 28s
Testy / Kontrola składni wszystkich warstw (pull_request) Successful in 21s
build / build (push) Successful in 57s
Testy / Testy warstwy logicznej (silnik) (push) Successful in 10m47s
Testy / Build obrazu silnika B (swisseph) (push) Failing after 29s
Testy / Kontrola składni wszystkich warstw (push) Successful in 22s
QoL 2/2: pole „Szukaj miejsca" (nazwa/adres/POI) + interaktywna mapa na formularzach horoskopu / interpretacji / kalendarza. Bez klucza API. - geocode.py: proxy OSM/Nominatim PO STRONIE SERWERA (poprawny User-Agent, throttling ~1 req/s, cache TTL 1h) — endpointy /geocode i /reverse. Wolanie z serwera, nie z przegladarki: latwiej trzymac polityke Nominatim i dziala niezaleznie od secure-context (http://<ip>). - _location_picker.html: wspolny partial (search + wyniki + mapa + atrybucja), wpiety includem do 3 formularzy z polami lat/lon. - geo.js: Leaflet — wyszukiwanie (debounce), klik na wynik ustawia lat/lon i centruje mape, klik/drag pineski ustawia wspolrzedne + /reverse pokazuje nazwe; sync z „Tu i teraz" (now.js emituje event astrololo:coords). - Leaflet 1.9.4 vendorowany lokalnie (static/vendor/leaflet, BSD-2-Clause, permisywny) — niezaleznosc od CDN; kafelki mapy z OSM. Marker jako divIcon (bez plikow PNG). - styles.css: style pod ciemny motyw. Zweryfikowane w przegladarce: domyslny widok (pineska na Szpitalu Barlickiego), wyszukanie „Wawel Krakow" -> lista -> klik ustawia 50.0547/19.9361 i przesuwa mape, klik w mape ustawia wspolrzedne + reverse wypelnia nazwe. Zero bledow w konsoli. /geocode i /reverse zwracaja szpital; cache dziala. Uwaga wdrozeniowa: pod prezentacji potrzebuje egressu do nominatim.openstreetmap.org; przegladarki — do tile.openstreetmap.org. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
117 lines
4.7 KiB
JavaScript
117 lines
4.7 KiB
JavaScript
// Wyszukiwarka lokalizacji + mapa (OpenStreetMap / Leaflet, bez klucza API).
|
|
// Wpina się do formularza z polami input[name=lat] / input[name=lon]:
|
|
// - wpisanie nazwy/adresu/POI -> /geocode -> lista trafień -> klik ustawia lat/lon,
|
|
// - klik na mapie lub przeciągnięcie pineski -> ustawia lat/lon + /reverse pokazuje nazwę,
|
|
// - „Tu i teraz" (now.js) emituje event 'astrololo:coords' -> mapa się synchronizuje.
|
|
// Bez internetu wyszukiwarka/kafelki nie działają, ale ręczne lat/lon dalej tak.
|
|
(function () {
|
|
function ready(fn) {
|
|
if (document.readyState !== 'loading') fn();
|
|
else document.addEventListener('DOMContentLoaded', fn);
|
|
}
|
|
|
|
ready(function () {
|
|
var picker = document.getElementById('geoPicker');
|
|
if (!picker || typeof L === 'undefined') return; // brak Leafleta = po cichu pas
|
|
var latEl = document.querySelector('input[name=lat]');
|
|
var lonEl = document.querySelector('input[name=lon]');
|
|
if (!latEl || !lonEl) return;
|
|
|
|
var searchEl = document.getElementById('geoSearch');
|
|
var searchBtn = document.getElementById('geoSearchBtn');
|
|
var mapToggle = document.getElementById('geoMapToggle');
|
|
var resultsEl = document.getElementById('geoResults');
|
|
var mapEl = document.getElementById('map');
|
|
|
|
var num = function (el) { var v = parseFloat(el.value); return isNaN(v) ? 0 : v; };
|
|
var curLat = function () { return num(latEl); };
|
|
var curLon = function () { return num(lonEl); };
|
|
|
|
var pin = L.divIcon({ className: 'geo-pin', html: '📍', iconSize: [24, 24], iconAnchor: [12, 24] });
|
|
var map = L.map(mapEl).setView([curLat(), curLon()], 12);
|
|
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
|
maxZoom: 19,
|
|
attribution: '© OpenStreetMap contributors'
|
|
}).addTo(map);
|
|
var marker = L.marker([curLat(), curLon()], { draggable: true, icon: pin }).addTo(map);
|
|
|
|
// Kontener mógł być renderowany zanim ustaliła się jego szerokość — poprawka.
|
|
setTimeout(function () { map.invalidateSize(); }, 60);
|
|
|
|
function setCoords(lat, lon, fromMap) {
|
|
latEl.value = lat.toFixed(4);
|
|
lonEl.value = lon.toFixed(4);
|
|
marker.setLatLng([lat, lon]);
|
|
if (!fromMap) map.setView([lat, lon], Math.max(map.getZoom(), 12));
|
|
if (fromMap) reverseName(lat, lon);
|
|
}
|
|
|
|
function reverseName(lat, lon) {
|
|
fetch('/reverse?lat=' + lat + '&lon=' + lon)
|
|
.then(function (r) { return r.ok ? r.json() : null; })
|
|
.then(function (d) { if (d && d.name) searchEl.value = d.name; })
|
|
.catch(function () {});
|
|
}
|
|
|
|
marker.on('dragend', function () {
|
|
var p = marker.getLatLng();
|
|
setCoords(p.lat, p.lng, true);
|
|
});
|
|
map.on('click', function (e) { setCoords(e.latlng.lat, e.latlng.lng, true); });
|
|
|
|
// ---- wyszukiwanie po nazwie ----
|
|
function renderResults(items) {
|
|
resultsEl.innerHTML = '';
|
|
if (!items.length) { resultsEl.hidden = true; return; }
|
|
items.forEach(function (it) {
|
|
var li = document.createElement('li');
|
|
li.textContent = it.name;
|
|
li.addEventListener('click', function () {
|
|
setCoords(it.lat, it.lon, false);
|
|
searchEl.value = it.name;
|
|
resultsEl.hidden = true;
|
|
});
|
|
resultsEl.appendChild(li);
|
|
});
|
|
resultsEl.hidden = false;
|
|
}
|
|
|
|
function doSearch() {
|
|
var q = searchEl.value.trim();
|
|
if (q.length < 3) { resultsEl.hidden = true; return; }
|
|
fetch('/geocode?q=' + encodeURIComponent(q))
|
|
.then(function (r) { return r.ok ? r.json() : { results: [] }; })
|
|
.then(function (d) { renderResults(d.results || []); })
|
|
.catch(function () { renderResults([]); });
|
|
}
|
|
|
|
var timer = null;
|
|
searchBtn.addEventListener('click', doSearch);
|
|
searchEl.addEventListener('keydown', function (e) {
|
|
if (e.key === 'Enter') { e.preventDefault(); doSearch(); } // nie wysyłaj formularza
|
|
});
|
|
searchEl.addEventListener('input', function () {
|
|
clearTimeout(timer); timer = setTimeout(doSearch, 500); // debounce (limit Nominatim)
|
|
});
|
|
|
|
// ---- zwijanie/rozwijanie mapy ----
|
|
mapToggle.addEventListener('click', function () {
|
|
var hidden = mapEl.hasAttribute('hidden');
|
|
if (hidden) {
|
|
mapEl.removeAttribute('hidden');
|
|
mapToggle.textContent = 'Ukryj mapę';
|
|
setTimeout(function () { map.invalidateSize(); map.setView([curLat(), curLon()]); }, 60);
|
|
} else {
|
|
mapEl.setAttribute('hidden', '');
|
|
mapToggle.textContent = 'Pokaż mapę';
|
|
}
|
|
});
|
|
|
|
// ---- synchronizacja z „Tu i teraz" ----
|
|
document.addEventListener('astrololo:coords', function () {
|
|
marker.setLatLng([curLat(), curLon()]);
|
|
map.setView([curLat(), curLon()], Math.max(map.getZoom(), 12));
|
|
});
|
|
});
|
|
})();
|