r = require.alias({maplibregl:"[email protected]/dist/maplibre-gl.js",h3: {},deck:"[email protected]/dist.min.js"})maplibregl =r("maplibregl").catch(() =>window["maplibregl"])deck =r("deck")import { aq, op } from"@uwdata/arquero"
centroids = aq.loadCSV("/data/nuclear-commodity-centroids.csv")allData = aq.loadCSV("/data/nuclear-commodity-data-allyears2023.csv")// filter out ignored commodities, then join sources/recipients with centroidsmergedData = allData.filter(d => d.category!="Ignore").join(centroids, ["source_iso3","iso3"], [[aq.all()], ["iso3","lat","lon"]], {suffix: ["","_source"] }).join(centroids, ["recipient_iso3","iso3"], [[aq.all()], ["iso3","lat","lon"]], {suffix: ["","_recipient"] }).objects()
viewof selectedCategory = Inputs.radio( ["Mining","Enrichment","Fuel fabrication","Power Plant","Spent Fuel / HLW"], {value:"Mining",format: x =>html`<span style="color: ${colorSchemes[x][3]}">${x}</span>` })viewof selectedCountry = Inputs.text({ placeholder:"Country name" })// filter displayed data based on both category and countriescategoryData = mergedData.filter(d => d.category== selectedCategory && (d.source.toLowerCase().includes(selectedCountry.toLowerCase()) || d.recipient.toLowerCase().includes(selectedCountry.toLowerCase())))
valueExtent = d3.extent(mergedData.map(d => d.sum_value_usd))widthScale = d3.scaleLinear().domain(valueExtent).range([0.5,3.5])// we'll use a different colour scale for each category, taking a different lightness for source and recipientcolorSchemes = ({"Mining": d3.schemeYlOrBr[9],"Enrichment": d3.schemeYlGn[9],"Fuel fabrication": d3.schemeYlGnBu[9],"Power Plant": d3.schemePuBu[9],"Spent Fuel / HLW": d3.schemeRdPu[9]})sourceIndex =6targetIndex =2opacities = ({"Mining":0.55,"Enrichment":0.3,"Fuel fabrication":0.3,"Power Plant":0.3,"Spent Fuel / HLW":0.7})// convert hexcode into deckgl's [r, g, b] format @ 25% opacityconvertSchemeToDeck = (category) => {return colorSchemes[category].map(d => d3.color(d)).map(d => [d.r, d.g, d.b])}deckColorScheme =convertSchemeToDeck(selectedCategory)defaultScheme =convertSchemeToDeck("Mining")
transferArcs =new deck.MapboxLayer({id:"transferArcs",type: deck.ArcLayer,data: [],getSourcePosition: d => [Number(d.lon),Number(d.lat)],getTargetPosition: d => [Number(d.lon_recipient),Number(d.lat_recipient)],getWidth: d =>widthScale(d.sum_value_usd),getTilt: d =>Number(d.lon) >Number(d.lon_recipient) ?0:-5,pickable:true,autoHighlight:true,highlightColor: [255,200,0,255]})
viewof map = {let container =html`<div></div>`;yield container;// Create the \`map\` object with the mapboxgl.Map constructor, referencing// the container divlet map =new maplibregl.Map({ container,bounds: [[-175,-80], [175,85]],pitch:30,antialias:true,style:"style.json",attribution:false }); map.on("load", () => {// dispatch back to ojs container.value= map; container.dispatchEvent(newCustomEvent("input")); map.addLayer(transferArcs);// on arc hover, update the title text// (note that si suffix swap giga => billions) transferArcs.setProps({onHover: (info) => {if (info && info.object) {// const amountText = // d3.format("$.2s")(info.object.sum_value_usd)// .replace("G", "B")// .replace("P", "T") mutable selectedSource = info.object.source mutable selectedRecipient = info.object.recipient// mutable selectedTransfer =// md`${info.object.source} → ${info.object.recipient}` mutable selectedValue = info.object.sum_value_usd// html`US${amountText} / year <span class="small">average over 2016–2021</span>` } else { mutable selectedSource =null mutable selectedRecipient =null mutable selectedValue =null } } })// also configure the automatically-create deck instance transferArcs.deck.setProps({ pickingRadius:10 }); })}
updates = transferArcs.setProps({data: categoryData,getSourceColor: d => [...deckColorScheme[sourceIndex], opacities[selectedCategory] *255],getTargetColor: d => [...deckColorScheme[targetIndex], opacities[selectedCategory] *255]})
Chart: James Goldie, 360info Data: European Commission, Nigel Marks