Public
Edited
Jan 6
1 star
Insert cell
Insert cell
Insert cell
Insert cell
map = Plot.plot({
width: 1080,
height: screen.height - 200,
projection,
color,
marks: [
Plot.raster(
{ length: ppt.length },
{
x: lon,
y: lat,
fill: 0,
interpolate: interpolateBivariate(n, ppt, temp),
clip: locationConfigs[location].clip
}
),
() =>
svg`<g style="transform: translate(${
locationConfigs[location].legend.x
}px,${locationConfigs[location].legend.y}px)rotate(-45deg);">${legend()}`,
Plot.geo(locationConfigs[location].clip, {
strokeWidth: 0.25,
stroke: "white"
}),
location == "Europe" ? Plot.sphere() : null
]
})
Insert cell
Insert cell
legend = () =>
Plot.plot({
color,
axis: "both",
margin: 0,
inset: 18,
width: 150,
height: 150,
y: { reverse: true },
x: {
ticks: 4
},
marks: [
// Plot.axisX({ label: "Precipitati", lineWidth: 8, marginBottom: 40 }),
Plot.cell(d3.cross(d3.range(n), d3.range(n)), {
x: ([a, b]) => a,
y: ([a, b]) => b,
fill: ([a, b]) => n * a + b,
inset: -1,
title: ([a, b]) => `Precipitation${label(a)}\nTemperature${label(b)}`
}),
Plot.text(["Precipitation →"], {
frameAnchor: "bottom-right",
fontWeight: "bold",
fontSize: "14px",
dx: -21,
dy: -3
}),
Plot.text(["← Temperature"], {
frameAnchor: "top-left",
fontWeight: "bold",
fontSize: "14px",
rotate: 90,
dx: 12,
dy: 18
})
]
})
Insert cell
labels = ["low", "", "", "", "high"]
Insert cell
label = (i) => (labels[i] ? ` (${labels[i]})` : "")
Insert cell
projection = locationConfigs[location].projection
Insert cell
function interpolateBivariate(n, v1, v2) {
const r = d3.range(n);
const s1 = d3.scaleQuantile(v1, r);
const s2 = d3.scaleQuantile(v2, r);
const interpolate = Plot.interpolatorBarycentric();
return function (I, w, h, X, Y) {
I = I.filter((i) => !isNaN(v1[i]) && !isNaN(v2[i]));
const V1 = interpolate(I, w, h, X, Y, v1);
const V2 = interpolate(I, w, h, X, Y, v2);
return Uint8Array.from(V1, (_, i) => n * s1(V1[i]) + s2(V2[i]));
};
}
Insert cell
Insert cell
Insert cell
Insert cell
color = ({
domain: d3.range(n * n),
range: d3.cross(blues, oranges).map(mixblend)
})
Insert cell
function mixblend([a, b]) {
a = d3.rgb(a);
b = d3.rgb(b);
const l = Math.min(250, b.r + b.g + b.b);
a.r *= b.r / l;
a.g *= b.g / l;
a.b *= b.b / l;
return a.hex();
}
Insert cell
Insert cell
countries = await FileAttachment("countries-10m.json")
.json()
.then((world) =>
topojson.feature(world, {
type: "GeometryCollection",
// Maldives has a wrong winding order.
geometries: world.objects.countries.geometries.filter(
({ properties: { name } }) => name !== "Maldives"
)
})
)
Insert cell
land = FileAttachment("CNTR_RG_10M_2024_4326.json")
.json()
.then((world) =>
topojson.feature(world, {
type: "GeometryCollection",
// Maldives has a wrong winding order.
geometries: world.objects.CNTR_RG_10M_2024_4326.geometries.filter(
({ properties: { name } }) => name !== "Maldives"
)
})
)
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
_tmax = locationConfigs[location].data.tmax.arrayBuffer().then(cdf)
Insert cell
_tmin = locationConfigs[location].data.tmin
.arrayBuffer()
.then(cdf)
Insert cell
_ppt = locationConfigs[location].data.ppt.arrayBuffer().then(cdf)
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
requests = {
const vars = ["ppt", "tmax", "tmin"];
const URLS = [];
const boxes = {
UK: {
LAT_MIN: 49.674,
LON_MIN: -14.015517,
LAT_MAX: 61.061,
LON_MAX: 2.0919117
},
Europe: {
LAT_MIN: 33,
LON_MIN: -24.2,
LAT_MAX: 71.2,
LON_MAX: 62
},
"South America": {
LAT_MIN: -55, // Southern tip
LON_MIN: -81.3, // Western tip
LAT_MAX: 13, // Northern tip
LON_MAX: -34.8 // Eastern tip
},
"continental US": {
LAT_MIN: 24.396308, // Southern tip
LON_MIN: -125, // Western tip
LAT_MAX: 49.384358, // Northern tip
LON_MAX: -66.93457 // Eastern tip
},
// -178.2,6.6,-49.0,83.3
"North America": {
LAT_MIN: 6.6,
LON_MIN: -178.2,
LAT_MAX: 83.3,
LON_MAX: -49
}
};

Object.values(boxes).forEach((o) =>
vars.forEach((v) =>
URLS.push(
`http://thredds.northwestknowledge.net:8080/thredds/ncss/agg_terraclimate_${v}_1958_CurrentYear_GLOBE.nc?var=${v}&south=${o.LAT_MIN}&north=${o.LAT_MAX}&west=${o.LON_MIN}&east=${o.LON_MAX}&disableProjSubset=on&addLatLon=true&horizStride=1&accept=netcdf`
)
)
);

return URLS;
}
Insert cell
//ppt_raw = await fetch(requests[0])
Insert cell
// how do you get observable to display a full string when it shortens it?
console.log(requests)
Insert cell
Insert cell
function cdf(buffer) {
return new netcdfjs.NetCDFReader(buffer);
}
Insert cell
netcdfjs = import("https://cdn.skypack.dev/netcdfjs@3.0.0?min")
Insert cell
Plot = require(await FileAttachment("plot.umd.js").url()) // https://github.com/observablehq/plot/pull/2243
Insert cell
// via: https://observablehq.com/@d3/petroff-quincuncial?collection=@d3/d3-geo-projection
d3 = require.alias({
d3: "d3@7.1.1/dist/d3.min.js",
"d3-array": "d3@7.1.1/dist/d3.min.js",
"d3-geo": "d3@7.1.1/dist/d3.min.js",
"d3-geo-projection": "d3-geo-projection@4/dist/d3-geo-projection.min.js"
})("d3", "d3-geo-projection")
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more