/** * Load pharmacy rows from a public open-data URL (JSON array, GeoJSON, or similar). * No default URL — pass the dataset URL from the client (regional open-data portals). */ function rowsFromGeoJson(json) { if (!json || json.type !== 'FeatureCollection' || !Array.isArray(json.features)) { return []; } return json.features .map((f) => { const p = f.properties || {}; const name = p.name || p.nombre || p.denominacion || p.title; const address = p.address || p.direccion || p.domicilio || [p.calle, p.municipio, p.codigo_postal].filter(Boolean).join(', '); let lat; let lon; if (f.geometry?.type === 'Point' && Array.isArray(f.geometry.coordinates)) { lon = f.geometry.coordinates[0]; lat = f.geometry.coordinates[1]; } return { name: name ? String(name).trim() : null, address: address ? String(address).trim() : null, phone: p.phone || p.telefono || p.tel || null, latitude: lat != null ? Number(lat) : null, longitude: lon != null ? Number(lon) : null, }; }) .filter((r) => r.name && r.address); } function rowsFromJsonArray(json) { if (!Array.isArray(json)) return []; return json .map((row) => { if (!row || typeof row !== 'object') return null; const name = row.name || row.nombre || row.farmacia; const address = row.address || row.direccion || row.domicilio || row.full_address; return { name: name ? String(name).trim() : null, address: address ? String(address).trim() : null, phone: row.phone || row.telefono || null, latitude: row.latitude != null ? Number(row.latitude) : row.lat != null ? Number(row.lat) : null, longitude: row.longitude != null ? Number(row.longitude) : row.lon != null ? Number(row.lon) : row.lng != null ? Number(row.lng) : null, }; }) .filter((r) => r && r.name && r.address); } /** * @param {string} openDataUrl - HTTPS URL returning JSON * @returns {Promise>} */ export async function fetchPharmaciesFromOpenDataUrl(openDataUrl) { if (!openDataUrl || typeof openDataUrl !== 'string' || !openDataUrl.trim()) { throw new Error('openData source requires a non-empty openDataUrl'); } const u = openDataUrl.trim(); if (!/^https?:\/\//i.test(u)) { throw new Error('openDataUrl must be an http(s) URL'); } const res = await fetch(u, { headers: { Accept: 'application/json', 'User-Agent': 'FarmaFinder/1.0 (open-data pharmacy import)', }, }); const text = await res.text(); if (!res.ok) { throw new Error(`Open data HTTP ${res.status}: ${text.slice(0, 120)}`); } let json; try { json = text ? JSON.parse(text) : null; } catch { throw new Error('Open data response is not JSON'); } if (json && json.type === 'FeatureCollection') { return rowsFromGeoJson(json); } if (Array.isArray(json)) { return rowsFromJsonArray(json); } if (json && Array.isArray(json.data)) { return rowsFromJsonArray(json.data); } if (json && Array.isArray(json.records)) { return rowsFromJsonArray(json.records); } throw new Error( 'Unsupported open-data JSON shape (expected FeatureCollection, array, or { data: [] })' ); }