import { aq, op } from "@uwdata/arquero"
bucket_base = "https://d33173dkfxwa5x.cloudfront.net/www/"
FA = FileAttachment
import {aq as aq, op as op} from "@uwdata/arquero"
bucket_base = "https://d33173dkfxwa5x.cloudfront.net/www/"
FA = ƒ(name)
import { aq, op } from "@uwdata/arquero"
bucket_base = "https://d33173dkfxwa5x.cloudfront.net/www/"
FA = FileAttachment
import {aq as aq, op as op} from "@uwdata/arquero"
// get the station id from the page <head>, injected by populate-places.qmd
stationId = document
.querySelector("meta[name=isithotrightnow-station]")
.getAttribute("data-station")
Is it hot in Mount Gambier right now?
answers = ({
"Hell no!": {color: "white", fill1: "#5287d1", fill2: "#2166ac" },
"No!": {color: "white", fill1: "#a2cdeb", fill2: "#67a9cf" },
"Nope": {color: "#333333", fill1: "#f5f8fa", fill2: "#d1e5f0" },
"Not really": {color: "#333333", fill1: "#f7f7f7", fill2: "#ebebeb" },
"Yup": {color: "#333333", fill1: "#fddcca", fill2: "#f7cfb7" },
"Yeah!": {color: "white", fill1: "#de7b49", fill2: "#fc946a" },
"Hell yeah!": {color: "white", fill1: "#d1231d", fill2: "#b2182b" }
})
html`
<div class="answer-icon-and-label">
<span class="answer-icon" style="background-color: ${answers[statsPlace.isit_answer].fill2};">
</span>
<span>${statsPlace.isit_answer}</span>
</div>`
datefns = import("https://cdn.skypack.dev/date-fns@3.3.1?min")
tmaxWhen = new Date(Date.parse(statsPlace.isit_maximum_dt || null))
tminWhen = new Date(Date.parse(statsPlace.isit_minimum_dt || null))
tmaxWhenFriendly = isFinite(tmaxWhen) ?
datefns.formatRelative(tmaxWhen, new Date()) : ""
tminWhenFriendly = isFinite(tminWhen) ?
datefns.formatRelative(tminWhen, new Date()) : ""
percentRating = statsPlace.isit_average.toFixed(1) + "%"
This is the distribution of temperatures typical for this time of year, which we’ve defined as a seven day window either side of today. Just like a bell curve, the plot is higher at more common temperatures and lower at rare temperatures.
We’ve compared every day this year at Mount Gambier to similar times of year in the past. Red days were hot compared to records; blue days were cold.
statsAllRaw = FA(bucket_base + "stats/stats_all.json").json()
// statsAllRaw is an object with keys by station id, but we
// need an array with an id field. let's move the ids in
statsAll = {
const statsArray = []
Object.keys(statsAllRaw).map(k => statsArray.push({...statsAllRaw[k], id: k}))
const tidiedStats = aq.from(statsArray)
.derive({
lat: d => op.parse_float(d.isit_lat),
lon: d => op.parse_float(d.isit_lon),
isit_average: d => op.parse_float(d.isit_average),
// current? (that's the actual avg temp)
isit_maximum: d => op.parse_float(d.isit_maximum),
isit_minimum: d => op.parse_float(d.isit_minimum),
max_when: d => op.parse_date(d.isit_maximum_dt),
min_when: d => op.parse_date(d.isit_minimum_dt),
slug: d =>
op.replace(
op.replace(
op.replace(
op.lower(d.isit_label),
/\s-\s/g, "-"),
/\s/g, "-"),
/[()]/g, "")
})
// mark as stale if latest obs are > 24 hours old
.derive({
max_stale:
d => ((d.max_when - op.now()) / (1000 * 60)) / (24 * 60) > 1,
min_stale:
d => ((d.min_when - op.now()) / (1000 * 60)) / (24 * 60) > 1
})
.objects()
return(tidiedStats)
}
currentStats = statsAll.filter(d => (!d.max_stale) && (!d.min_stale))
staleStats = statsAll.filter(d => d.max_stale || d.min_stale)
australia = FileAttachment("/assets/australia.geojson").json()
Plot = import("https://esm.run/@observablehq/plot")
answer_domain = [
"Hell no!",
"No!",
"Nope",
"Not really",
"Yup",
"Yeah!",
"Hell yeah!"]
Plot.plot({
projection: {
type: "equirectangular",
rotate: [-133, 28],
domain: d3.geoCircle().center([133, -28]).radius(18)()
},
marks: [
// Plot.graticule(),
Plot.geo(australia, {
fill: "#00000009",
stroke: "#dddddd"
}),
// replace stale locations with crosses
Plot.dot(staleStats, {
x: "lon",
y: "lat",
stroke: "lightgrey",
r: 6,
symbol: "times"
}),
Plot.dot(currentStats, {
x: "lon",
y: "lat",
fill: "isit_answer",
stroke: d =>
d.isit_answer == "Hell no!" || d.isit_answer == "Hell yeah!" ?
"black" :
"#00000099",
strokeWidth: 0.75,
r: 6,
}),
Plot.tip(currentStats, tipTemplate),
// enlarge dot you're pointing to
Plot.dot(currentStats, Plot.pointer({
x: "lon",
y: "lat",
fill: "isit_answer",
stroke: d =>
d.isit_answer == "Hell no!" || d.isit_answer == "Hell yeah!" ?
"black" :
"#00000099",
strokeWidth: 0.75,
r: 12,
})),
// enlargen clickable areas with invisible voronoi polys
Plot.voronoi(currentStats, {
x: "lon",
y: "lat",
fill: "transparent",
stroke: "transparent",
href: d => "/places/" + d.slug,
}),
],
color: {
type: "ordinal",
domain: answer_domain,
range: ["#2166acff",
"#67a9cfff",
"#d1e5f0ff",
"#f7f7f7ff",
"#fddbc7ff",
"#ef8a62ff",
"#b2182bff"]
},
height: 600
})
// breaking out the pointer layer for readability
// (and in case we decide to customise it later)
tipTemplate = Plot.pointer({
x: "lon",
y: "lat",
channels: {
place: {
label: "",
value: d => d.isit_label,
},
label: {
label: "",
value: d => d.isit_comment
},
separator: {
label: "",
value: d => ""
},
isit_maximum: {
label: "Max:",
value: d => d.isit_maximum + "°C"
},
isit_minimum: {
label: "Min:",
value: d => d.isit_minimum + "°C"
}
},
fontSize: 20,
fontFamily: "Roboto Condensed",
format: {
x: null,
y: null
},
lineWidth: 20,
lineHeight: 1.25,
pointerSize: 0,
fillOpacity: 0.8,
strokeOpacity: 0
})