117 lines
3.5 KiB
JavaScript
117 lines
3.5 KiB
JavaScript
/**
|
|
* 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<Array<{name,address,phone,latitude,longitude}>>}
|
|
*/
|
|
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: [] })'
|
|
);
|
|
}
|