UNPKG

@panoramax/web-viewer

Version:

Panoramax web viewer for geolocated pictures

204 lines (187 loc) 5.89 kB
import { NominatimBaseUrl, AdresseDataGouvBaseURL } from "./services"; const PLACETYPE_ZOOM = { "house": 20, "housenumber": 20, "street": 18, "locality": 15, "district": 13, "municipality": 12, "city": 12, "county": 8, "region": 7, "state": 7, "country": 5 }; /** * Transforms a set of parameters into an URL-ready string * It also removes null/undefined values * * @param {object} params The parameters object * @return {string} The URL query part * @private */ function geocoderParamsToURLString(params) { let p = {}; Object.entries(params) .filter(e => e[1] !== undefined && e[1] !== null) .forEach(e => p[e[0]] = e[1]); return new URLSearchParams(p).toString(); } /** * Nominatim (OSM) geocoder, ready to use for our Map * @private */ export function forwardGeocodingNominatim(config) { // Transform parameters into Nominatim format const params = { q: config.query, countrycodes: config.countries, limit: config.limit, viewbox: config.bbox, }; return fetch(`${NominatimBaseUrl()}/search?${geocoderParamsToURLString(params)}&format=geocodejson&addressdetails=1`) .then(res => res.json()) .then(res => { const finalRes = { features: [] }; const listedNames = []; (res.features || []).forEach(f => { const plname = geocodeJsonToPlaceName(f.properties?.geocoding) || f.properties?.geocoding?.label; if(!listedNames.includes(plname)) { finalRes.features.push({ place_type: ["place"], place_name: plname, center: f.geometry.coordinates, zoom: PLACETYPE_ZOOM[f.properties?.geocoding?.type], }); listedNames.push(plname); } }); return finalRes; }); } export function reverseGeocodingNominatim(lat, lon) { return fetch(`${NominatimBaseUrl()}/reverse?lat=${lat}&lon=${lon}&zoom=18&format=geocodejson`) .then(res => res.json()) .then(res => geocodeJsonToPlaceName(res?.features?.shift()?.properties?.geocoding)); } /** * Base adresse nationale (FR) geocoder, ready to use for our Map * @param {object} config Configuration sent by MapLibre GL Geocoder, following the geocoderApi format ( https://maplibre.org/maplibre-gl-geocoder/types/MaplibreGeocoderApiConfig.html ) * @returns {object} GeoJSON Feature collection in Carmen GeoJSON format ( https://maplibre.org/maplibre-gl-geocoder/types/CarmenGeojsonFeature.html ) * @private */ export function forwardGeocodingBAN(config) { return forwardGeocodingStandard(config, AdresseDataGouvBaseURL()); } /** * Transforms GeocodeJSON search result into a nice-to-display address. * @param {object} props The GecodeJSON API feature properties * @returns {string} The clean-up string for display * @private */ function geocodeJsonToPlaceName(props) { // API format @ https://github.com/geocoders/geocodejson-spec/blob/master/draft/README.md if(!props || typeof props != "object") { return ""; } // P1 = main name, P2=locality-like, P3=country+high-level admin let p1 = props.name; let p2 = [], p3 = []; switch(props.type) { case "hamlet": case "croft": case "isolated_dwelling": case "neighbourhood": case "allotments": case "quarter": case "farm": case "farmyard": case "industrial": case "commercial": case "retail": case "city_block": case "residential": case "locality": case "district": p3.push(props.city); p3.push(props.county); p3.push(props.state); p3.push(props.country); break; case "city": p3.push(props.county); p3.push(props.state); p3.push(props.country); break; case "region": p3.push(props.county); p3.push(props.state); p3.push(props.country); break; case "country": break; case "house": case "housenumber": p2.push(props.housenumber); p2.push(props.street); p2.push(props.locality); p2.push(props.district); p3.push(props.city); p3.push(props.county); p3.push(props.state); p3.push(props.country); break; case "street": case "road": default: p2.push(props.street); p2.push(props.locality); p2.push(props.district); p3.push(props.city); p3.push(props.county); p3.push(props.state); p3.push(props.country); break; } p2 = p2.filter(v => v); p2 = p2.filter((v,i) => v != p1 && (i === 0 || p2[i-1] !== v)); p2 = p2.length > 0 ? (props.housenumber ? p2.slice(0,2).join(" ") : p2.shift()) : null; if(p2 === p1) { p2 = null; } p3 = p3.filter(v => v); p3 = p3.filter((v,i) => v != p1 && (!p2 || !p2.includes(v)) && (i === 0 || p3[i-1] !== v)); let res = [p1, p2, p3.shift()].filter(v => v); return res.join(", "); } /** * Standard forward geocoder * @param {object} config Configuration sent by MapLibre GL Geocoder, following the geocoderApi format ( https://maplibre.org/maplibre-gl-geocoder/types/MaplibreGeocoderApiConfig.html ) * @param {string} endpoint The URL endpoint (everything before the /?q=...) * @returns {object} GeoJSON Feature collection in Carmen GeoJSON format ( https://maplibre.org/maplibre-gl-geocoder/types/CarmenGeojsonFeature.html ) * @private */ export function forwardGeocodingStandard(config, endpoint) { // Transform parameters into BAN format const params = { q: config.query, limit: config.limit }; if(typeof config.proximity === "string") { const [lat, lon] = config.proximity.split(",").map(v => parseFloat(v.trim())); params.lat = lat; params.lon = lon; } return fetch(`${endpoint}/?${geocoderParamsToURLString(params)}`) .then(res => res.json()) .then(res => { const finalRes = { features: [] }; const listedNames = []; (res.features || []).forEach(f => { const plname = geocodeJsonToPlaceName(f.properties); if(!listedNames.includes(plname) && f.properties.type != "other") { finalRes.features.push({ place_type: ["place"], place_name: plname, center: f.geometry.coordinates, zoom: PLACETYPE_ZOOM[f.properties.type], }); listedNames.push(plname); } }); return finalRes; }); }