Public
Edited
Mar 13, 2023
1 fork
4 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
chart = Chart()
Insert cell
chart.update_fill(census_collection, fill_for, title_text);
Insert cell
{
chart[zoom_key] = undefined;
chart.set_transform_callback(make_fills);
}
Insert cell
colors = [
{d: 0, c:"white"},
{d: 4000, c:"rgb(255, 215, 120)"},
{d: 40e3, c:"rgb(220, 70, 80 )"},
{d: 200e3, c:"rgb(90, 0, 150 )"}];
Insert cell
Insert cell
function Legend() {
var cutoff = 0;
while (fills_legend[cutoff + 1].d < 201) ++cutoff;
var fills1 = fills_legend.slice(cutoff);
var right_margin = 25;
var w = Math.min(width - right_margin, 500);
var h = 30;
var log_d = fills1.map(f => Math.log10(f.d));
log_d[0] = Math.log10(200);
var log_end = log_d[log_d.length - 1] + 0.1;
log_d.push(log_end);
var log_scale = (w) / (log_end - log_d[0]);
var x0 = log_d[0];
log_d = log_d.map(x => Math.round((x - x0) * log_scale) );
var patterns = fills1.map(x => x.p && x.p.with({scale: 1, id: x.p.id + "-l"}));
var patterndefs = patterns.map(p => p ? p.svg() : "").join("");
var boxes = fills1.map(
function (f, i)
{
var box_w = (log_d[i+1] - log_d[i]);
var f = patterns[i] ? patterns[i].url() : f.fill;
return`<rect x="${log_d[i]}" y="1" width="${box_w}" height="${h-1}" fill="${f}" />`;
}).join("");
var tick_d = [500, 1000, 3000, 10000, 30000, 1e5, 2e5];
var ticks = tick_d.map(
function (d, i)
{
var x = 0.5 + Math.floor((Math.log10(d) - x0) * log_scale);
var tx = x + 10 * (d == 2e5);
return `<line x1="${x}" y1="${h+3.5}" x2="${x}" y2="${h+8}" stroke="black" stroke-width="1"/>\n`+
`<text x="${tx}" y="${h+18}" style="font-family: sans-serif; font-size: 12px;" text-anchor="middle">${d}</text>`;
}).join("");

var tick_minor_d = [2000, 4000, 5000, 20e3, 40e3, 50e3];
ticks += tick_minor_d.map(
function (d, i)
{
var x = 0.5 + Math.floor((Math.log10(d) - x0) * log_scale);
var tx = x + 10 * (d == 2e5);
return `<line x1="${x}" y1="${h+3.5}" x2="${x}" y2="${h+5}" stroke="black" stroke-width="1"/>`;
}).join("");

var svg1 = svg`<svg viewbox="0 0 ${w+right_margin} ${h+20}" width="${w+right_margin}" height="${h+20}">
<defs>${patterndefs}</defs>
${boxes}
<rect x="0.5" y="0.5" width="${w}" height="${h}" fill="none" stroke="#aaa" stroke-width="1"/>
<line x1="0" y1="${h+3.5}" x2="${w+1}" y2="${h+3.5}" stroke="#888" stroke-width="1"/>
${ticks}
</svg>`;
return html`<div>People per km²<br>${svg1}</div>`
}
Insert cell
// define the list of fill here
// having a persistent list gives all the derived fills stable IDs, so we
// get much less weird behaviour on the map.
fills_base = {
var Tile = HalfToneTile8({size: 1, fill_alldots: .5});
// the steps are chosen to coincide with fill levels of 1/8, 1/4 and 1/2
var steps = [.0625, .125, .25, .35, .50, .70];
function mkfill(i, st)
{
var d = colors[i-1].d * (1.03 - st) + colors[i].d * (-.03 + st);
var p = Tile.with({bg: colors[i-1].c, fg: colors[i].c, fill: st});
return {p, fill:p.url(), d};
}

var fills = [
{fill: colors[0].c, d: 100},
mkfill(1, .025)
];

for (var i = 1; i < colors.length; ++i)
{
for (var st of steps)
{
fills.push(mkfill(i, st));
}
fills.push({fill: colors[i].c, d:colors[i].d});
}
return fills;
}
Insert cell
fills_map = fills_base.map(f => {
if (!f.p) return f;
var pat_grain = f.p.with({size: grainSize, id: f.p.id + '-map'});
return {p: pat_grain, fill: pat_grain.url(), d:f.d};
})
Insert cell
fills_legend = fills_base.map(f => {
if (!f.p) return f;
var pat_grain = f.p.with({size: grainSize, id: f.p.id + '-leg'});
return {p: pat_grain, fill: pat_grain.url(), d:f.d};
})
Insert cell
fill_key = Symbol('fills')
Insert cell
zoom_key = Symbol('zoom')
Insert cell
function make_fills(transform, chart)
{
// don't update on panning:
if ( chart[zoom_key] == transform.k) return;
var sc = 1 / transform.k;
for (var f of fills_map)
{
if (f.p) {
f.p.settings.scale = sc;
}
}
var fill_patterns = fills_map.map(x => x.p ? x.p.svg() : "").join("\n");
if (!chart[fill_key]) {
chart[fill_key] = chart.svg.insert("defs",":first-child");
}
chart[zoom_key] = transform.k;
chart[fill_key].html(fill_patterns);
}
Insert cell
function fill_for(d)
{
var density = d.properties.density;
var f1 = fills_map[0];
for (var f of fills_map)
{
if (f.d > density) {break; }
f1 = f;
}
return f1.fill;
}
Insert cell
Insert cell
max_zoom = 6;
Insert cell
max_chart_h = 1500
Insert cell
import { Chart, census_collection, title_text, param }
with {sa_type as sa_type, max_zoom as max_zoom, max_chart_h as max_chart_h}
from "@roelandschoukens/mode-share-map"
Insert cell
import { HalfToneTile8 } from "@roelandschoukens/svg-tiled-pattern-fills"
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