chore: initial Observable workspace with technical README
This commit is contained in:
83
docs/data/ufo-ca-sightings.json.js
Normal file
83
docs/data/ufo-ca-sightings.json.js
Normal file
@@ -0,0 +1,83 @@
|
||||
const candidates = [
|
||||
"https://raw.githubusercontent.com/richaude/blue-book/master/scrubbed.csv",
|
||||
"https://raw.githubusercontent.com/alecfirrincieli/UFO-sightings/main/ufo-sightings.csv",
|
||||
];
|
||||
|
||||
let csvText = null;
|
||||
for (const url of candidates) {
|
||||
try {
|
||||
const response = await fetch(url);
|
||||
if (!response.ok) continue;
|
||||
const text = await response.text();
|
||||
if (text && text.includes("datetime,city,state")) {
|
||||
csvText = text;
|
||||
break;
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
|
||||
if (!csvText) {
|
||||
throw new Error("Unable to fetch UFO sightings dataset from public mirrors.");
|
||||
}
|
||||
|
||||
const { csvParse } = await import("d3-dsv");
|
||||
const rows = csvParse(csvText);
|
||||
|
||||
const normalizeKey = (value) =>
|
||||
String(value ?? "")
|
||||
.trim()
|
||||
.toLowerCase();
|
||||
|
||||
function readField(row, key) {
|
||||
const target = normalizeKey(key);
|
||||
for (const [field, value] of Object.entries(row)) {
|
||||
if (normalizeKey(field) === target) return value;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function parseNumber(value) {
|
||||
const parsed = Number(value);
|
||||
return Number.isFinite(parsed) ? parsed : null;
|
||||
}
|
||||
|
||||
const ufo = rows
|
||||
.map((row, index) => {
|
||||
const state = String(readField(row, "state") ?? "")
|
||||
.trim()
|
||||
.toLowerCase();
|
||||
const country = String(readField(row, "country") ?? "")
|
||||
.trim()
|
||||
.toLowerCase();
|
||||
const latitude = parseNumber(readField(row, "latitude"));
|
||||
const longitude = parseNumber(readField(row, "longitude"));
|
||||
|
||||
if (state !== "ca" || country !== "us") return null;
|
||||
if (latitude === null || longitude === null) return null;
|
||||
|
||||
const inCaliforniaBounds =
|
||||
latitude >= 32 &&
|
||||
latitude <= 42.5 &&
|
||||
longitude >= -125 &&
|
||||
longitude <= -114;
|
||||
|
||||
if (!inCaliforniaBounds) return null;
|
||||
|
||||
const durationSeconds =
|
||||
parseNumber(readField(row, "duration (seconds)")) ?? 0;
|
||||
|
||||
return {
|
||||
id: `ufo/${index}`,
|
||||
datetime: readField(row, "datetime") ?? null,
|
||||
city: readField(row, "city") ?? "Unknown",
|
||||
state,
|
||||
shape: String(readField(row, "shape") ?? "unknown").toLowerCase(),
|
||||
durationSeconds,
|
||||
comments: readField(row, "comments") ?? "",
|
||||
latitude,
|
||||
longitude,
|
||||
};
|
||||
})
|
||||
.filter(Boolean);
|
||||
|
||||
process.stdout.write(JSON.stringify(ufo));
|
||||
Reference in New Issue
Block a user