Public
Edited
May 2
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
poc2 = addTooltips( // Add tooltips
Plot.plot({
width: w,
height: 600,
projection: d3.geoIdentity().reflectY(true).fitWidth(w/1.2, geoFsa),
color: {
scheme: "Viridis", // Change color scheme
unknown: "green", // For polygons with null values
type: "linear", // Linear scale for color progression
legend: true, // Add the legend
marginLeft: 15,
width: 250,
//marginTop: 0,
//marginBottom: 50,
label: "number of electric vehicles",
//percent: true, // Convert value to a percent (from a proportion)
domain: [0, 500] // Update the value domain to span 0 to 100% access
},
marks: [
Plot.geo(geoFsa, {
fill: (d) => (
data_allyrs.filter(e => (e.FSA === d.properties.CFSAUID && e.period === qperiod))[0] ? data_allyrs.filter(e => (e.FSA === d.properties.CFSAUID && e.period === qperiod)).map(function(d) {return d[evDict[evMenu]];}) : null
),
title: (d) => `FSA: ${d.properties.CFSAUID} \n
Number of ${evMenu}: ${data_allyrs.filter(e => (e.FSA === d.properties.CFSAUID && e.period === qperiod)).map(function(d) {
console.log('val: ', d[evDict[evMenu]])
return d[evDict[evMenu]] ? d[evDict[evMenu]] : "Unknown";})}`,
stroke: "grey",
strokeWidth: 0.2
},
),
],
// tooltip: {
// fill: "red",
// stroke: "blue",
// r: 8
// }
}),
{ //fill: "none",
"stroke": "orange",
"stroke-width": 1
// opacity: 0
}
)
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
viewof pcPeriodScrubber = Scrubber(pcPeriods, {
autoplay: false,
loop: false,
delay: 1100
})
Insert cell
Insert cell
Insert cell
Insert cell
// Returns raw api response
api_response = function (url) {
return new Promise(function (resolve, reject) {
resolve(d3.json(url));
});
};
Insert cell
Insert cell
Insert cell
q1_2022 = api_response("https://data.ontario.ca/api/3/action/datastore_search?resource_id=aa3af935-3c6f-4921-8606-e9a30d088c7a&limit=541").then(function (api_raw) {
return api_raw.result.records;
});
Insert cell
q1_2023 = api_response("https://data.ontario.ca/api/3/action/datastore_search?resource_id=0cf5ab4c-b495-49c1-9118-841c8d79a6fb&limit=550").then(function (api_raw) {
return api_raw.result.records;
});
Insert cell
q1_2024 = api_response("https://data.ontario.ca/api/3/action/datastore_search?resource_id=5d70275f-8d7a-47e2-92fa-16a3073bb525&limit=573").then(function (api_raw) {
return api_raw.result.records;
});
Insert cell
Insert cell
q2_2022 = api_response("https://data.ontario.ca/api/3/action/datastore_search?resource_id=c5dc8e7a-432f-4b36-85be-44eb81c4b42d&limit=543").then(function (api_raw) {
return api_raw.result.records;
});
Insert cell
q2_2023 = api_response("https://data.ontario.ca/api/3/action/datastore_search?resource_id=d7c574f6-0d89-48fa-8f1e-32b1d4001932&limit=558").then(function (api_raw) {
return api_raw.result.records;
});
Insert cell
q2_2024 = api_response("https://data.ontario.ca/api/3/action/datastore_search?resource_id=f08d1b7c-3833-4a2a-bc8d-7a948ead990f&limit=578").then(function (api_raw) {
return api_raw.result.records;
});
Insert cell
Insert cell
q3_2022 = api_response("https://data.ontario.ca/api/3/action/datastore_search?resource_id=2a2bcaa8-8314-4e4e-bc24-dfbccaf03657&limit=547").then(function (api_raw) {
return api_raw.result.records;
});
Insert cell
q3_2023 = api_response("https://data.ontario.ca/api/3/action/datastore_search?resource_id=0048d330-4d34-4ce9-bdff-91b41be1129e&limit=563").then(function (api_raw) {
return api_raw.result.records;
});
Insert cell
q3_2024 = api_response("https://data.ontario.ca/api/3/action/datastore_search?resource_id=82a8dfd1-9890-4527-bb0e-5f1e7f6c594d&limit=576").then(function (api_raw) {
return api_raw.result.records;
});
Insert cell
Insert cell
q4_2022 = api_response("https://data.ontario.ca/api/3/action/datastore_search?resource_id=751cf779-300b-4c3b-9874-7cf9b21ec02c&limit=549").then(function (api_raw) {
return api_raw.result.records;
});
Insert cell
q4_2023 = api_response("https://data.ontario.ca/api/3/action/datastore_search?resource_id=dca5bef6-df38-4c45-be73-62e71b243d3d&limit=570").then(function (api_raw) {
return api_raw.result.records;
});
Insert cell
q4_2024 = api_response("https://data.ontario.ca/api/3/action/datastore_search?resource_id=d36d8aa0-9b43-46f7-bbfd-d011c394b607&limit=579").then(function (api_raw) {
return api_raw.result.records;
});
Insert cell
Insert cell
data_allyrs
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
Insert cell
Insert cell
data_allyrs = {
// Add "period" object key for each period and concatenate all periods into one array

return [...q1_2022.map(v => ({...v, period: "Q1-2022"})),
...q2_2022.map(v => ({...v, period: "Q2-2022"})),
...q3_2022.map(v => ({...v, period: "Q3-2022"})),
...q4_2022.map(v => ({...v, period: "Q4-2022"})),
...q1_2023.map(v => ({...v, period: "Q1-2023"})),
...q2_2023.map(v => ({...v, period: "Q2-2023"})),
...q3_2023.map(v => ({...v, period: "Q3-2023"})),
...fix_cols(q4_2023).map(v => ({...v, period: "Q4-2023"})),
...fix_cols(q1_2024).map(v => ({...v, period: "Q1-2024"})),
...fix_cols(q2_2024).map(v => ({...v, period: "Q2-2024"})),
...fix_cols(q3_2024).map(v => ({...v, period: "Q3-2024"})),
...fix_cols(q4_2024).map(v => ({...v, period: "Q4-2024"}))
];
}
Insert cell
// CHECK
data_allyrs.filter(d => d.period === "Q2-2023")
Insert cell
Insert cell
data_allyrs
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
q1_2023_csv = FileAttachment("ontario_evs_by_fsa_2023-03-31_FIX.csv").csv() // downloaded csv file in case API is not working
Insert cell
q2_2023_csv = FileAttachment("evs_by_fsa_30-jun-2023_FIX.csv").csv() // downloaded csv file in case API is not working
Insert cell
Insert cell
Insert cell
geoFsa = FileAttachment("ontario_fsa_EPSG3347_simplified_1000m_mapshaper.geojson").json()
Insert cell
Insert cell
centroids_fsa = FileAttachment("ontario_fsa_saved_as_EPSG4326_centroids_latlon.csv").csv() //FileAttachment("centroids_ontario_fsa_with_coords_EPSG3347.csv").csv()
Insert cell
Insert cell
Insert cell
fix_cols = function (o) {
let o_fix = JSON.parse(JSON.stringify(o)); // deep copy original array
o_fix.forEach(function(el) {
el['TotalEV'] = el['Total EV'];
delete el['Total EV'];
})
return o_fix;
};

Insert cell
q1_2023_fix = fix_cols(q1_2023);
Insert cell
q2_2023_fix = fix_cols(q2_2023);
Insert cell
q4_2023_fix = fix_cols(q4_2023);
Insert cell
Insert cell
Insert cell
Insert cell
height = 50
Insert cell
width = 50
Insert cell
Insert cell
w = 700
Insert cell
h = 700
Insert cell
projection = d3.geoIdentity()
.reflectY(true)
.fitWidth(w, mygeojson);
//.fitExtent([0, h], [w, h], mygeojson);
Insert cell
path = d3.geoPath().projection(projection); //d3.geoPath(projection);
Insert cell
mygeojson = FileAttachment("census_lfsa_000b21a_ont_outline_saved_as_EPSG4326_geojson.geojson").json()
Insert cell
mygeojson_orig_proj = FileAttachment("census_lfsa000b21a_e_ont_geojson_EP4326.geojson").json()
Insert cell
{
const svg = d3.select(DOM.svg(w, h));
svg.append('path')
.datum(mygeojson)
.attr('d', path)
.attr('stroke', '#093672')
.attr('fill', 'none');

return svg.node();
}
Insert cell
Insert cell
Insert cell
// https://observablehq.com/@lnicoletti/choropleth
callout = (g, value) => {
if (!value) return g.style("display", "none");

g
.style("display", null)
.style("pointer-events", "none")
// .style("font", "10px sans-serif");

const path = g.selectAll("path")
.data([null])
.join("path")
.attr("fill", "white")
.attr("stroke", "lightgrey")
.attr("stroke-width", "0.5px");;

const text = g.selectAll("text")
.data([null])
.join("text")
.call(text => text
.selectAll("tspan")
.data((value + "").split(/\n/))
.join("tspan")
.attr("x", 0)
.attr("y", (d, i) => `${i * 1.1}em`)
.style("font-weight", (_, i) => i ? null : "bold")
.text(d => d));

const {x, y, width: w, height: h} = text.node().getBBox();

text.attr("transform", `translate(${-w / 2},${15 - y})`);
path.attr("d", `M${-w / 2 - 10},5H-5l5,-5l5,5H${w / 2 + 10}v${h + 20}h-${w + 20}z`);
}
Insert cell
Insert cell
topojson = require("topojson-client@3")
Insert cell
usTopo = FileAttachment("census_lfsa000b21a_e_ont_geojson_EP4326_topojson.json").json()
Insert cell
hex.grid
Insert cell
usTopo.objects.census_lfsa000b21a_e_ont_geojson_EP4326
Insert cell
usGeo = {
const us = (await topojson.feature(usTopo, usTopo.objects.census_lfsa000b21a_e_ont_geojson_EP4326));
us.features[0].geometry.coordinates = [us.features[0].geometry.coordinates[2]]
return us
}
Insert cell
Insert cell
topoFsa = FileAttachment("ontario_fsa_EPSG3347_simplified_1000m_mapshaper_topojson.json").json()
Insert cell
topoFsa.objects.ontario_fsa_EPSG3347_simplified_1000m_mapshaper
Insert cell
Insert cell
d3 = require("d3@5", "d3-hexbin", "d3-hexgrid")
Insert cell
// d3 = require(
// 'd3-array',
// 'd3-color',
// 'd3-drag',
// 'd3-ease',
// 'd3-fetch',
// 'd3-format',
// 'd3-geo',
// 'd3-hexbin',
// 'd3-hexgrid',
// 'd3-interpolate',
// 'd3-scale',
// 'd3-scale-chromatic',
// 'd3-selection',
// 'd3-shape',
// 'd3-zoom'
// )
Insert cell
import {legend} from "@d3/color-legend"
Insert cell
import {Scrubber} from "@mbostock/scrubber" // For animation Play button
Insert cell
import {addTooltips} from "@mkfreeman/plot-tooltip" // Choropleth tooltip, from https://observablehq.com/@observablehq/build-your-first-choropleth-map-with-observable-plot
Insert cell
Insert cell
// Function to position the tooltip
hover = (tip, pos, text) => {
const side_padding = 10;
const vertical_padding = 5;
const vertical_offset = 15;

// Empty it out
tip.selectAll("*").remove();

// Append the text
tip
.style("text-anchor", "middle")
.style("pointer-events", "none")
.attr("transform", `translate(${pos[0]}, ${pos[1] + 7})`)
.selectAll("text")
.data(text)
.join("text")
.style("dominant-baseline", "ideographic")
.text((d) => d)
.attr("y", (d, i) => (i - (text.length - 1)) * 15 - vertical_offset)
.style("font-weight", (d, i) => (i === 0 ? "bold" : "normal"));

const bbox = tip.node().getBBox();

// Add a rectangle (as background)
tip
.append("rect")
.attr("y", bbox.y - vertical_padding)
.attr("x", bbox.x - side_padding)
.attr("width", bbox.width + side_padding * 2)
.attr("height", bbox.height + vertical_padding * 2.5)
.style("fill", "white")
.style("stroke", "#d3d3d3")
.lower();
}
Insert cell
Insert cell
// Slider for Map Zoom Scale
// https://observablehq.com/@uwdata/cartographic-visualization
import {slider} from '@jheer/dom-utilities'
Insert cell
// from
function myScrubber(values, {
format = value => value,
initial = 0,
direction = 1,
delay = null,
buttonText = null,
autoplay = true,
loop = true,
loopDelay = null,
alternate = false
} = {}) {
values = Array.from(values);
console.log('values: ', buttonText)
const form = html`<form style="font: 12px var(--sans-serif); font-variant-numeric: tabular-nums; display: flex; height: 33px; align-items: center;">
<button name=b type=button style="margin-right: 0.4em; width: 5em;"></button>
<label style="display: flex; align-items: center;">
<input name=i type=range min=0 max=${values.length - 1} value=${initial} step=1 style="width: 180px;">
<output name=o style="margin-left: 0.4em;"></output>
</label>
</form>`;
let frame = null;
let timer = null;
let interval = null;
function start() {
form.b.textContent = "Pause";
if (delay === null) frame = requestAnimationFrame(tick);
else interval = setInterval(tick, delay);
}
function stop() {
form.b.textContent = buttonText ? buttonText : "Play";
if (frame !== null) cancelAnimationFrame(frame), frame = null;
if (timer !== null) clearTimeout(timer), timer = null;
if (interval !== null) clearInterval(interval), interval = null;
}
function running() {
return frame !== null || timer !== null || interval !== null;
}
function tick() {
if (form.i.valueAsNumber === (direction > 0 ? values.length - 1 : direction < 0 ? 0 : NaN)) {
if (!loop) return stop();
if (alternate) direction = -direction;
if (loopDelay !== null) {
if (frame !== null) cancelAnimationFrame(frame), frame = null;
if (interval !== null) clearInterval(interval), interval = null;
timer = setTimeout(() => (step(), start()), loopDelay);
return;
}
}
if (delay === null) frame = requestAnimationFrame(tick);
step();
}
function step() {
form.i.valueAsNumber = (form.i.valueAsNumber + direction + values.length) % values.length;
form.i.dispatchEvent(new CustomEvent("input", {bubbles: true}));
}
form.i.oninput = event => {
if (event && event.isTrusted && running()) stop();
form.value = values[form.i.valueAsNumber];
form.o.value = format(form.value, form.i.valueAsNumber, values);
};
form.b.onclick = () => {
if (running()) return stop();
direction = alternate && form.i.valueAsNumber === values.length - 1 ? -1 : 1;
form.i.valueAsNumber = (form.i.valueAsNumber + direction) % values.length;
form.i.dispatchEvent(new CustomEvent("input", {bubbles: true}));
start();
};
form.i.oninput();
if (autoplay) start();
else stop();
Inputs.disposal(form).then(stop);
return form;
}
Insert cell
viewof myscrubber = myScrubber(periods, {
autoplay: false,
loop: false,
delay: 500,
buttonText: "Something"
})
Insert cell
Insert cell
Insert cell
Insert cell
// Add coordinates to original dataset
geoData = {
let geoData = JSON.parse(JSON.stringify(data_allyrs.filter(d => d.period === "Q2-2023"))) // deep copy original data objects
geoData.forEach(function(datarow) {
var result = centroids_fsa_lambert.filter(function(item) {
return item.CFSAUID === datarow.FSA;
});
var isMatch = (result[0] !== undefined) ? result[0]['xcoord'] : null;
datarow.xcoord = (result[0] !== undefined) ? result[0]['ycoord'] : null; // ****NB!!! make x = latitude
datarow.ycoord = (result[0] !== undefined) ? result[0]['xcoord'] : null; // ****NB!!! make y = longitude
if (isMatch === null) {
console.log('isMatch:', datarow )
}
})

// Remove rows that did not have a matching centroid
geoData = geoData.filter(e => e.xcoord !== null); //rem = arr.filter(arr => arr.name != 'Harry');
return geoData;
}
Insert cell
centres_obj = centroids_fsa.map(function(d) {
console.log('d: ', d)
const allowed = ['xcoord', 'ycoord'];
var filtered = Object.keys(d)
.filter(key => allowed.includes(key))
.reduce((obj, key) => {
obj[key] = d[key];
return obj;
}, {});
return filtered
})
Insert cell
centres = centres_obj.map(function(obj) {
return Object.keys(obj).sort().map(function(key) {
return obj[key];
});
});
Insert cell
Insert cell
hexRadius = 5
Insert cell
hexbin = d3.hexbin()
.radius(hexRadius)
.x(function(d) { return d.xcoord; })
.y(function(d) { return d.ycoord; })
Insert cell
hexbin.centers()
Insert cell
Insert cell
{
const w = 1296;
const h = 432;
const svg = d3.select(DOM.svg(w, h));
const hexes = svg.append('g').attr('id', 'hexes')
.selectAll('.hex')
.data(junkhexPoints)
//.data(hexPoints)
.enter().append('path')
.attr('class', 'hex')
.attr('transform', function(d) {
console.log('d.x: ', d.x)
console.log('d.y: ', d.y)
console.log('d.y - 200: ', d.y - 200)
return 'translate(' + d[0].x + ', ' + d[0].y + ')'; }
//return 'translate(' + 483 + ', ' + 200 + ')'; }
//return 'translate(' + d.x - 6198177 + ', ' + 200 + ')'; }
)
.attr('d', hexbin.hexagon())
.style('fill', 'none')
.style('stroke', '#aaa')
.style('stroke-width', 2);

return svg.node()
}
Insert cell
hexPoints = hexbin(geoData)
Insert cell
{
const w = 1296;
const h = 432;
const svg = d3.select(DOM.svg(w, h));
const hexes = svg.append('g').attr('id', 'hexes')
.selectAll('.hex')
.data(hexPoints)
.enter().append('path')
.attr('class', 'hex')
.attr('transform', function(d) {
console.log('d: ', d)
console.log('projection y: ', projection([d.y, d.x])[0])
console.log('projection x: ', projection([d.y, d.x])[1])
return 'translate(' + d.x + ', ' + d.y + ')'; }
)
.attr('d', hexbin.hexagon())
.style('fill', 'none')
.style('stroke', '#aaa')
.style('stroke-width', 2);

return svg.node()
}
Insert cell
Insert cell
Insert cell
Insert cell
mygeojson
Insert cell
hexgrid = d3.hexgrid()
.extent([w, h])
//.extent([100, h])
.geography(mygeojson)
.projection(projection)
.pathGenerator(path);
Insert cell
hex = hexgrid(geoData)
Insert cell
hexgrid(centres).grid.layout
Insert cell
hex.grid.layout
Insert cell
{
const svg = DOM.svg(w, h)

d3.select(svg).selectAll('path')
.data(hex.grid.layout)
.enter()
.append('path')
.attr('d', hex.hexagon())
.attr('transform', d => `translate(${d.x} ${d.y})`)
//.style('fill', d => !d.pointDensity ? '#fff' : marketColours(d.pointDensity))
.style('stroke', '#ccc');
return svg;
}
Insert cell
hex.hexagon()
Insert cell
hex_centres = hexgrid(centres)
Insert cell
hex_centres.hexagon()
Insert cell
hex_centres.extent()
Insert cell
centroids_fsa
Insert cell
Insert cell
centroids_fsa_lambert = FileAttachment("centroids_ontario_fsa_with_coords_EPSG3347.csv").csv()
Insert cell
{
//https://d3-graph-gallery.com/graph/choropleth_basic.html
var colorScale = d3.scaleThreshold()
.domain([85, 170, 255, 340, 425, 510])
.range(d3.schemeBlues[7]);
const svg = d3.select(DOM.svg(w, h));
const g = svg.append("g");

// Colourbar
// svg
// .append("g")
// .attr("transform", "translate(610,20)")
// .append(() => legend({ colorScale, title: "Count", width: 260, tickFormat: ".0f" }));

const path = d3.geoPath();
// const projection = d3.geoIdentity()
// .scale(70)
// .center([0,20])
// .translate([width / 2, height / 2]);
const projection = d3.geoIdentity()
.reflectY(true)
.fitWidth(w, geoFsa)

//svg.append("g")
g
.selectAll("path")
.data(geoFsa.features)
.enter()
.append("path")
// draw each country
.attr("d", d3.geoPath()
.projection(projection)
)
.attr("class", "fsa")
// set the color of each country
.attr("fill", "none")
.attr('stroke', 'darkgrey')
.attr('stroke-width', .2);


g.selectAll("circle")
.data(centroids_fsa_lambert)
.enter()
.append("circle")
.attr("cx", function(d) {
let lon = d['xcoord'];
let lat = d['ycoord'];
return projection([lon, lat])[0];
})
.attr("cy", function(d) {
let lon = d['xcoord'];
let lat = d['ycoord'];
return projection([lon, lat])[1];
})
.attr("r", 2)
.style("fill", "red");


// svg.append('path')
// .datum(geoFsa)
// .attr('d', path)
// .attr('stroke', '#093672')
// .attr('fill', 'none');


return svg.node();
}
Insert cell
Plot.plot({
width: w,
height: 700,
// projection: {
// type: "identity",
// reflectY: true
// },
projection: d3.geoIdentity().reflectY(true).fitWidth(w/1.1, geoFsa),
color: {
legend: true,
label: "Opening year"
},
marks: [
Plot.geo(geoFsa),
// //Plot.geo(statemesh, {strokeOpacity: 0.2}),
// Plot.dot(walmarts, {x: "longitude", y: "latitude", stroke: "date", symbol: "square"})
]
})
Insert cell
Insert cell
tmpgeoFsa.features[0].properties.period
Insert cell
tmpgeoFsa = JSON.parse(JSON.stringify(geoFsa));
Insert cell
// Add EV data from data_allyrs to the GeoJSON properties object for each FSA
{
tmpgeoFsa.features.forEach(d => {
let this_data = data_allyrs.find(el => el['FSA'] === d.properties.CFSAUID);
if (this_data) { // there are some nulls
d.properties.period = {};
periods.forEach(p => {
d.properties.period[p] = mapBEV.get(p).get(d.properties.CFSAUID);
console.log('p: ', p)
})
} else {
console.log('NULL!!!! ', d)
}
});
return tmpgeoFsa;
}
Insert cell
// Cannot get this to work because cannot reference facet "period" within Plot.geo
// So no way to tell "fill" which period to pick
Plot.plot({
width: w,
height: 200,
projection: d3.geoIdentity().reflectY(true).fitWidth(w/5, geoFsa),
fx: { tickFormat: (d) => `${d}`, padding: 0 },
facet: {
data: data_allyrs,
x: (d) => d.period
},
marks: [
Plot.geo(
tmpgeoFsa,
{ //fx: periodMap.period,
fill: (d) => (
console.log('d: ', d),
//console.log('get period: ', data_allyrs.get(d.period)),
// console.log('d.properties.CFSAUID: ', d.properties.CFSAUID),
// console.log("this: ", d3.select(".plot-d6a7b5")),
// console.log('d.properties.period: ', d.properties["BEV"]),
//console.log('get: ', periodMap.get(period) ),
//mapBEV.get(d.properties.CFSAUID) ? mapBEV.get(d.properties.CFSAUID) : 0
// d.properties.CFSAUID === "P9N" ?
// console.log('******* VAL: ', mapBEV.get(d.properties.CFSAUID) ? mapBEV.get(d.properties.CFSAUID).get(d.properties.period) : 0) :
// console.log('..')
12
)
} // Fill color depends on broadband value
),

//Plot.dot(data_2023, {x: "ycoord", y: "xcoord", stroke: "date", symbol: "circle"}) //centroids
]
})
Insert cell
periodMap.period
Insert cell
periodMap = {
const o = {
period: new Map(
data_allyrs
// create one [FSA, value] array for each period
.map(d => ([
d.period,
// create one [FSA, value] array for each period
new Map(data_allyrs.filter(e => (e.period === d.period)).map(({FSA, BEV}) => [FSA, BEV]))
//new Map(data_allyrs.map(({FSA, BEV}) => [FSA, BEV]))
]))
)
}
return o
}
Insert cell
mapBEV_byFSA = new Map(
data_allyrs
// create one [key, value] array for each FSA
.map(d => ([
d.FSA,
// create one [key, value] array for each period
new Map(data_allyrs.filter(e => (e.FSA === d.FSA)).map(({period, BEV}) => [period, BEV]))
]))
)
Insert cell
mapBEV = new Map(
data_allyrs
// create one [key, value] array for each FSA
.map(d => ([
d.period,
// create one [key, value] array for each period
new Map(data_allyrs.filter(e => (e.period === d.period)).map(({FSA, BEV}) => [FSA, BEV]))
//new Map(data_allyrs.map(({FSA, BEV}) => [FSA, BEV]))
]))
)
Insert cell
Insert cell
h/5
Insert cell
{
const w = 700;
const h = 700;
const svg = d3.select(DOM.svg(w, h)); //d3.create("svg");
const components = gridLayout(6, 3, 25, true);

const path = d3.geoPath();
const projection = d3.geoIdentity()
.reflectY(true)
.fitWidth(w/1.5, geoFsa);

const colorScale = d3.scaleThreshold()
.domain([85, 170, 255, 340, 425, 510])
.range(d3.schemeBlues[7]);
svg.attr("viewBox", [0, 0, components.outer_width, components.outer_height]);
const cmp = svg
.selectAll("g")
.data(components.items)
.enter();

// cmp
// .append("rect")
// .attr("class", (_, i) => `item_${i + 1}`)
// .attr("transform", d => `translate(${d.x}, ${d.y})`)
// .attr("width", d => d.width)
// .attr("height", d => d.height)
// .attr("fill", "white")
// .attr("stroke", "black");

const gridGroup = cmp
.append("g")
.attr("class", (_, i) => `gridmap`)
.attr("id", (_, i) => `gridmap_${i + 1}`)
.attr("transform", d => `translate(${d.x}, ${d.y})`);
gridGroup.each((e, j) => {
// console.log("e: ", e);
console.log("j: ", j);
let counter = j;
console.log("select: ", d3.select(`#gridmap_${j + 1}`) )

d3.select(`#gridmap_${j + 1}`)
.selectAll("path")
.data(geoFsa.features)
.enter()
.append("path")
.attr("d", d3.geoPath().projection(projection))
.attr("class", "fsa")
.attr('stroke', 'darkgrey')
.attr('stroke-width', .2)

})

// .attr("fill", function (e) {
// console.log("components: ", components)
// // // let thisFSA = d.properties['CFSAUID'];
// // // const thisParam = `${evType}`;
// // // let thisVal;
// // // thisVal = data.find(o => o['FSA'] === thisFSA) ? data.find(o => o['FSA'] === thisFSA)[thisParam] : 0;
// // // console.log('this val: ', thisVal)
// return "blue"; //colorScale(thisVal);
// });

return svg.node();
}
Insert cell
{
//https://d3-graph-gallery.com/graph/choropleth_basic.html
const colorScale = d3.scaleThreshold()
.domain([85, 170, 255, 340, 425, 510])
.range(d3.schemeBlues[7]);
const svg = d3.select(DOM.svg(w, h));


const path = d3.geoPath();
const projection = d3.geoIdentity()
.reflectY(true)
.fitWidth(w, geoFsa)

svg.append("g")
.selectAll("path")
.data(geoFsa.features)
.enter()
.append("path")
// draw each country
.attr("d", d3.geoPath()
.projection(projection)
)
.attr("class", "fsa")
// set the color of each country
.attr("fill", function (d) {
let thisFSA = d.properties['CFSAUID'];
const thisParam = `${evType}`;
let thisVal;
thisVal = data_allyrs.find(o => o['FSA'] === thisFSA) ? data_allyrs.find(o => o['FSA'] === thisFSA)[thisParam] : 0;
console.log('this val: ', thisVal)

return colorScale(thisVal);
})
.attr('stroke', 'darkgrey')
.attr('stroke-width', .2);



return svg.node();
}
Insert cell
viewof mapZoom = slider('Scale', 140, 50, 1000, 1)
Insert cell
Plot.plot({
width: w,
height: 600,
projection: d3.geoIdentity().reflectY(true).fitWidth(w/1.2, geoFsa), //.scale(1),

marks: [
Plot.geo(geoFsa, {
fill: (d) => (
data_allyrs.filter(e => (e.FSA === d.properties.CFSAUID && e.period === qperiod))[0] ? data_allyrs.filter(e => (e.FSA === d.properties.CFSAUID && e.period === qperiod)).map(function(d) {return d[evDict[evMenu]];}) : 0
),
stroke: "grey",
strokeWidth: 0.2
},
),
]
})
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