Published
Edited
Aug 18, 2022
1 fork
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
Plot.plot({
width: Math.min(800, width),
height: 400,
marginTop: 40,
style: {
fontSize: 12
},
caption: "Uitgifte: 2020-07-06 17:20:00",
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,
line: true,
},
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.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.text(beaufort, { y: "m", dx: - ( Math.min(800, width) - 60) / 2 + 8, clip: true, className: 'hello' }),
Plot.ruleX([ new Date("2020-07-06T15:20:00Z")], { stroke: 'purple', dashArray: "2 2" }),
]
})
Insert cell
Plot.plot({
width: Math.min(800, width),
height: 400,
marginTop: 40,
style: {
fontSize: 12
},
caption: "Uitgifte: 2020-07-06 17:20:00",
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],
insetBottom: 40
},
marks: [
Plot.rect(beaufort, {insetRight: Math.min(800, width) - 16 - 60 , y1: "l", y2: "u", fill: "c", clip: true }),
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.vector(timeseries, {filter: d => (d.time.getHours() % 2) === 0 && d.time.getMinutes() === 0 && d.time > new Date("2020-07-06T15:20:00Z") , x: "time", dy: (400 -30 -40)/ 2 - 20, rotate: d => d.forecastDirection + 180, stroke: "rgb(185, 1, 1)"}),
Plot.vector(timeseries, {filter: d => (d.time.getHours() % 2) === 0 && d.time.getMinutes() === 0 && d.time <= new Date("2020-07-06T15:20:00Z"), x: "time", dy: (400 -30 -40)/ 2 - 20,rotate: d => 180 + d.direction, stroke: "rgb(1, 120, 202)"}),
Plot.text(beaufort, { y: "m", dx: - ( Math.min(800, width) - 60) / 2 + 8, clip: true, fontSize: 11 }),
Plot.ruleX([ new Date("2020-07-06T15:20:00Z")], { stroke: 'purple', dashArray: "2 2" }),
Plot.ruleY([0]),
]
})
Insert cell
subplots = [{ frame: 0 }, {frame: 1}]
Insert cell
Plot.plot({
width: Math.min(800, width),
height:50,
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,
tickFormat: timeFormat,
axis: 'top',
ticks: 5,
},
y: {
insetBottom: 10,
insetTop: 10,
ticks: [],
},
marks: [
Plot.vector(timeseries, {filter: d => (d.time.getHours() % 2) === 0 && d.time.getMinutes() === 0 && d.time > new Date("2020-07-06T15:20:00Z") , x: "time", dy: 0, rotate: d => d.forecastDirection + 180, stroke: "rgb(185, 1, 1)"}),
Plot.vector(timeseries, {filter: d => (d.time.getHours() % 2) === 0 && d.time.getMinutes() === 0 && d.time <= new Date("2020-07-06T15:20:00Z"), x: "time", dy: 0,rotate: d => 180 + d.direction, stroke: "rgb(1, 120, 202)"}),
Plot.ruleX([ new Date("2020-07-06T15:20:00Z")], { stroke: 'purple', dashArray: "2 2" }),
]
})
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