micro =require("micromodal@0.4.10")micro.init({awaitOpenAnimation:true,awaitCloseAnimation:true,// hide the area controls when a modal is openonShow: () =>document.getElementById("area-search-controls").classList.add("hide"),onClose: () =>document.getElementById("area-search-controls").classList.remove("hide")});
James Goldie, 360info
Data: NSW DPIE
GET THIS MAP
Click to copy this into your story
r = require.alias({maplibregl:"maplibre-gl@2.1.9/dist/maplibre-gl.js",});maplibregl =r("maplibregl").catch(() =>window["maplibregl"]);
/* this is a bit different to regular mapbox/maplibre instantiation it lets have the map react to other values in the document, like a button or a timer, without reinstantiating! (based on https://observablehq.com/@tmcw/using-mapbox-gl-js) */viewof map = {let container =html`<div style="position: absolute; left: 0; top: 0; height: 100vh; width: 100%;" />`;// Give the container dimensions.yield container;// Create the \`map\` object with the mapboxgl.Map constructor, referencing// the container divlet map =new maplibregl.Map({ container,bounds: [[111,-46], [155,-9]],fitBoundsOptions: {padding: {top:130,bottom:70,left:5,right:5} },maxZoom:14,antialias:true,style:"style.json",attributionControl:false,// very loose to accomodate all of aus on portrait screensmaxBounds: [[95,-70], [170,15]], });// on map load:// - dispatch its value back to ojs// - add a prop to the layer that adds/removes a popup from the map// (we can't do this on initial layer def because the map isn't ready yet) map.on("load", () => { container.value= map; container.dispatchEvent(newCustomEvent("input")); map.addSource("postcodes", {"url":"tiles/postcodes/tiles.json","type":"vector","promoteId":"POA_NAME21" });// this layer shades all postcodes map.addLayer({id:"postcode-colors",source:"postcodes","source-layer":"postcodeswgs84",type:"fill" });// add pattern for focused postcodes map.loadImage("stripes.png",function(err, image) {if (err) throw err; map.addImage("focusStripes", image); });/* popup */const popup =new maplibregl.Popup({closeButton:true,closeOnClick:false,className:"map-popup" }); map.on("click","postcode-colors",function(e) {// change the cursor style as a ui indicator.console.log("Mouse entering...") map.getCanvas().style.cursor="pointer";// display a warning if projections aren't available, or show them for// this layer if they areconst noProjectionsMessage ="We don't have projections for postcode "+ e.features[0].properties.POA_NAME21+", but try another nearby.";var projectionMessage ="<h3>Postcode "+ e.features[0].properties.POA_NAME21+"</h3>"+"<p>Up to <strong>"+ e.features[0].state.ensmin+" to "+ e.features[0].state.ensmax+ (selectedPeriod =="1995"?"":" more") +"</strong> 35°C+ days in a typical year around "+ selectedPeriod +"</p>"var description = (e.features[0].state.ensmin===undefined| e.features[0].state.ensmax===undefined) ?noProjectionsMessage: projectionMessage;// populate popup; locate based on mouse's back-projected position popup.setLngLat(e.lngLat).setHTML(description).addTo(map); }); });}
viewof selectedPeriod = Inputs.radio(newMap([ ["1995","1995"], ["Change in 2030","2030"], ["Change in 2050","2050"] ]), { value:"1995" });viewof selectedScenario = Inputs.radio(newMap([ ["Medium emissions","rcp45diff"], ["High emissions","rcp85diff"] ]), { value:"rcp45diff",disabled: selectedPeriod =="1995" })// override scenario with historical if 1995 is selectedactualScenario = (selectedPeriod =="1995"?"historical": selectedScenario)
viewof areaSearch = Inputs.search(postcodeSuburbMap, {placeholder:"Enter your postcode or suburb",// don't show the number of resultsformat: () =>``});// display a menu of results and a search button when we're down to 30 resultsviewof selectedPostcode = areaSearch.length>0&& areaSearch.length<100? Inputs.select(areaSearch, {format: d =>`${d.SAL_NAME21} (${d.POA_NAME21})` }) :md``viewof goBtn = areaSearch.length>0&& areaSearch.length<100? Inputs.button(`🔍 Find`, {reduce: () =>zoomToPostcode(selectedPostcode.POA_NAME21) }) :md``// need a switch for when our tile search fails due to a postcode being too// small (bit of a workaround for not having the time to re-do the tiles!)mutable tileSearchFailed =false;areaWarn = tileSearchFailed ?md`🔍 This postcode's either very small or not in view — zoom in or out a bit and search again.`: areaSearch.length==0?md`❌ No areas matched this search`:md``
projections =FileAttachment("/data/news-stats-postcodes-short.csv").csv({ typed:true });postcodeSuburbMap =FileAttachment("/data/postcode-suburb-map.csv").csv();// postcodeList = FileAttachment("/data/postcode-list.csv").csv();// filter data here (note postcodes need to be re-padded b/c observable's type// inference makes them numeric)filteredProjections = projections.filter( d => (d.file_period== selectedPeriod) && (d.file_scenario== actualScenario)).map(d => ({ ...d,postcode:String(d.geo_name).padStart(4,"0") }));
yellowRedFill = [0,"#ffffb2",5,"#fed976",10,"#feb24c",20,"#fd8d3c",40,"#f03b20",80,"#bd0026"]yellowRedStroke = [0,"#ffff46",5,"#fdbf1a",10,"#f68e01",20,"#e86302",40,"#bf240d",80,"#8e001c"]rainbowFill = [0,"#3288bd",7,"#99d594",14,"#e6f598",30,"#ffffbf",90,"#fee08b",180,"#fc8d59",366,"#d53e4f"]rainbowStroke = [0,"#26668e",7,"#5bbc53",14,"#d0ec3e",30,"#ffff4f",90,"#fdc62a",180,"#fa5305",366,"#a92534"]// when the filtered data changes, rejoin it to the map tiles and update the// colour schemeupdateMapData = { filteredProjections.forEach(row => { map.setFeatureState( {source:"postcodes",sourceLayer:"postcodeswgs84",id: row.postcode, }, {ensmean: row.ensmean,ensmax: row.ensmax,ensmin: row.ensmin } ); });if (actualScenario.endsWith("diff")) {// extra days in future: yellow-redconsole.log(actualScenario +": switching to yellow-red") map.setPaintProperty("postcode-colors","fill-color", ["case", ["==", ["feature-state","ensmean"],null],"#666666", ["interpolate", ["linear"], ["feature-state","ensmean"],...yellowRedFill] ]); map.setPaintProperty("postcode-colors","fill-outline-color", ["case", ["==", ["feature-state","ensmean"],null],"#666666", ["interpolate", ["linear"], ["feature-state","ensmean"],...yellowRedStroke] ]); } else {// historical: diverging rainbowconsole.log(actualScenario +": switching to rainbow") map.setPaintProperty("postcode-colors","fill-color", ["case", ["==", ["feature-state","ensmean"],null],"#666666", ["interpolate", ["linear"], ["feature-state","ensmean"],...rainbowFill] ]); map.setPaintProperty("postcode-colors","fill-outline-color", ["case", ["==", ["feature-state","ensmean"],null],"#666666", ["interpolate", ["linear"], ["feature-state","ensmean"],...rainbowStroke] ]); }// TODO - also set fill pattern if highlighted// map.setPaintProperty("postcode-colors", "fill-patterm",// ["case", ["==", ["feature-state", "isFocus"], true],// "focusStripes",// // null?// ]);}
functionzoomToPostcode(postcode) {// TODO - stop highlighting previously selected postcodeconsole.log("Starting postcode search for "+ postcode)// get the postcode's featureconst targetPostcodeFeature = map.querySourceFeatures("postcodes", {sourceLayer:"postcodeswgs84",filter: ["==","POA_NAME21", postcode] });if (targetPostcodeFeature.length!=1) {console.error("Can only zoom to 1 postcode at a time, not ", targetPostcodeFeature.length); mutable tileSearchFailed =true; }// get the feature's geometryconst geom = targetPostcodeFeature[0].geometry.coordinates[0];// reduce the geometry to boundsconst postcodeBounds = geom.reduce( (accum, current) => accum.extend(current),new maplibregl.LngLatBounds(geom[0], geom[0]));// zoom to postcode map.fitBounds(postcodeBounds, {padding: {top:40,bottom:30,left:10,right:10},maxZoom:12 });// highlight postcode? map.setFeatureState( {source:"postcodes",sourceLayer:"postcodeswgs84",id: postcode, }, { inFocus:true } );// display info panel// (hide the area controls when a modal is open)setTimeout( micro.show("modal-focused-postcode", {onShow: () =>document.getElementById("area-search-controls").classList.add("hide"),onClose: () =>document.getElementById("area-search-controls").classList.remove("hide") }),1000);}
This map shows how a set of climate models estimate 35°C+ days will change across Australia in the coming decades. You can narrow in on a particular postcode.
Because more than one climate models is used, we tell you the range of estimates among them.
NARCLiM1.5 is used by scientists to compare against an earlier version of NARCliM, which had slightly less warming. These estimates should therefore be considered an upper bound.