API, Backend & Frontend
This commit is contained in:
116
API/open-data.js
Normal file
116
API/open-data.js
Normal file
@@ -0,0 +1,116 @@
|
||||
/**
|
||||
* 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: [] })'
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user