Published
Edited
Aug 18, 2022
Insert cell
# Wind speed and direction time series chart

Insert cell
windData = FileAttachment("wind_data3.json").json()
Insert cell
Insert cell
viewof table = Inputs.table(timeseries)
Insert cell
beaufort = {
let limits = [.3, 1.6, 3.4, 5.5, 8.0, 10.8, 13.9, 17.2, 20.8, 24.5, 28.5, 32.7, 40]
return limits.map( (limit, index) => {
return {
l: limit,
m: (limit + limits[index-1])/ 2,
u: limits[index-1] ? limits[index-1]: 0,
c: index === 0 ? "none" : index % 2 === 1 ? "rgb(230, 230, 230)" : "rgb(243, 243, 243)"
}
})
}
Insert cell
upperLimits = {
let limits = [
{id: 8, label: "Stormachtig", l: 17.2, c: "#EDC212", condition: "crossingUpwards"},
{id: 9, label: "Storm", l: 20.8, c: "#ED8F12", condition: "crossingUpwards"},
{id: 10, label: "Zware storm", l: 24.5, c: "#ED6312", condition: "crossingUpwards"},
{id: 11, label: "Zeer zware storm", l: 28.5, c: "#ED2912", condition: "crossingUpwards"},
{id: 12, label: "Orkaan", l: 32.7, c: "#D5102D", condition: "crossingUpwards"},
]
return limits.map( (limit, index) => {
return {
...limit, ...{
u: limits[index+1] ? limits[index+1].l: 40}
}
})
}
Insert cell
lowerLimits = {
let limits = [
{id: 0, u: 0.3, c: "none", condition: "crossingDownwards"},
{id: 1, label: "Flauw en stil", u: 1.5, c: "#AEF1F9", condition: "crossingDownwards"},
{id: 2, label: "Flauwe koelte", u: 3.4, c: "#96F7DC", condition: "crossingDownwards"},
]
return limits.map( (limit, index) => {
return {
...limit, ...{
l: limits[index-1] ? limits[index-1].u: 0}
}
})
}
Insert cell
Plot.plot({
width: Math.min(800, width),
height: 400,
caption: "Uitgifte: 2020-07-06 17:20:00",
marginTop: 40,
style: {
fontSize: 12
},
x: {
insetLeft: 16,
type: 'time',
grid: true,
domain: [ new Date("2020-07-04T17:10Z"), new Date("2020-07-07T17:10Z")],
nice: false,
ticks: 5,
tickFormat: timeFormat,
},
y: {
grid: true,
label: "↑Windsnelheid (m/s)",
domain: [0, 25],
},
marks: [

Plot.rect(beaufort, {insetRight: Math.min(800, width) - 16 - 60 , y1: "l", y2: "u", fill: "c", clip: true }),
Plot.rect([...upperLimits,...lowerLimits], {insetLeft: 0,insetRight: Math.min(800, width) - 60 -16 , y1: "u", y2: "l", fill: "c", clip: true }),
Plot.ruleY([...upperLimits,...lowerLimits], {insetLeft: 16, y: (d) => d.condition === "crossingUpwards"? d.l : d.u, stroke: "c", strokeWidth: 2, strokeDasharray: "4, 2" } ),
Plot.text(upperLimits, {
y: "l",
dx: - ( Math.min(800, width) - 60) / 2 + 16,
text: d => `${d.label}`,
dy: -8,
clip: true,
fill: "c" ,
textAnchor:"start",
}),
Plot.text(lowerLimits, {
y: "u",
dx: - ( Math.min(800, width) - 60) / 2 + 16,
text: d => `${d.label}`,
dy: 8,
clip: true,
fill: "c" ,
textAnchor:"start",
}),
Plot.text(beaufort, { y: "m", dx: - ( Math.min(800, width) - 60) / 2 + 8, clip: true, fontSize: 11 }),

Plot.line(timeseries, {x: "time", y: "forecast", stroke: "rgb(185, 1, 1)", strokeDasharray: "2, 2" } ),
Plot.line(timeseries, {x: "time", y: "speed", stroke: "rgb(1, 120, 202)"}),
Plot.line(timeseries, {x: "time", y: "squall", stroke: "rgb(82, 184, 254)"}),
Plot.ruleX([ new Date("2020-07-06T15:20:00Z")], { stroke: 'purple' }),
]
})
Insert cell
Insert cell
Insert cell
timeFormat = generateMultiFormat('Europe/Amsterdam', 'nl-NL')
Insert cell
generateMultiFormat = (timeZone, locale) => {
return function (date) {
const m = luxon.DateTime.fromJSDate(date).setZone(timeZone).setLocale(locale ?? navigator.language)
const offsetDate = new Date(date.getTime() + m.offset * 60000)
let formatString
if (d3.utcSecond(offsetDate) < offsetDate) {
formatString = '.SSS'
} else if (d3.utcMinute(offsetDate) < offsetDate) {
formatString = ':ss'
} else if (d3.utcHour(offsetDate) < offsetDate) {
formatString = 'HH:mm'
} else if (d3.utcDay(offsetDate) < offsetDate) {
formatString = 'HH:mm'
} else if (d3.utcMonth(offsetDate) < offsetDate) {
formatString = d3.utcWeek(offsetDate) < offsetDate
? 'EEE dd-MM'
: 'EEE dd-MM-yy'
} else {
formatString = d3.utcYear(offsetDate) < offsetDate
? 'MMMM'
: 'yyyy'
}
return m.toFormat(formatString)
}
}
Insert cell
d3 = import("d3@7")
Insert cell
luxon = import('https://cdn.skypack.dev/luxon');
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more