chart = {
const svg = d3.select(DOM.svg(width, height));
const defs = svg.append('defs');
defs.append('filter').attr('id', 'shadow')
.append('feDropShadow')
.attr('dx', 2)
.attr('dy', 2)
.attr('stdDeviation', 2);
const g = svg.append('g');
const gLegend = svg.append('g').classed('legend', true);
g.selectAll('path.ea')
.data(ea.features)
.join(s => s.append('path').classed('ea', true))
.attr('fill', 'white')
.attr('stroke-width', 2)
.attr('stroke', '#eee')
.attr('d', path);
const getAttr = (s) => (d) => d[s] || '';
const attr = [
getAttr('LOC_STREET'),
d => (d = d['USEDASCODE'], d && d[0] || ''),
getAttr('PROP_CLASS'),
][1];
const propClasses = eaParcels.features
.map(d => attr(d.properties))
.filter((el, i, arr) => el && arr.indexOf(el) === i)
.sort(d3.ascending);
const colorScale = d3.scaleOrdinal()
.domain(propClasses)
.range(d3.quantize(d3.interpolateRainbow, propClasses.length));
const eaStreetCategories = d3.group(eaStreets.features, d => d.properties.highway);
const primaryStreets = {
type: 'FeatureCollection',
features: Array.from(eaStreetCategories).reduce((acc, e) => [
'primary'].includes(e[0]) ? acc.concat(e[1]) : acc, []),
};
const filteredStreets = {
type: 'FeatureCollection',
features: Array.from(eaStreetCategories).reduce((acc, e) => [
'primary', 'secondary', 'tertiary', 'residential'].includes(e[0]) ? acc.concat(e[1]) : acc, []),
};
const mainMask = turf.union(...turf.buffer(primaryStreets, 200, {units: 'feet'}).features);
const b = turf.union(...[
['primary', 32],
['secondary', 20],
['tertiary', 18],
['residential', 12],
]
.map(([id, radius]) =>
turf.buffer(
{type: 'FeatureCollection', features: eaStreetCategories.get(id)},
radius,
{units: 'feet'},
)
)
.reduce((acc, fc) => acc.concat(fc.features), []));
defs.append('mask').attr('id', 'main-clip')
.call(s => s.append('rect')
.attr('fill', 'black')
.attr('width', width)
.attr('height', height))
.call(s => s.append('path')
.attr('fill', 'white')
.attr('d', path(mainMask))
)
const steps = d3.range(0, 400, 20).reverse();
const stepColors = d3.scaleOrdinal(steps, d3.quantize(d3.interpolateMagma, steps.length));
const l = eaParcels.length;
const layers = steps.map(radius => ({
radius, feature: turf.union(...eaBuildings.features
.filter((_, i) => i % 5 === 0)
.reduce((acc, feature) => {
let buf = turf.buffer(feature, radius, {units: 'feet'});
if (buf) {
buf = turf.intersect(mainMask, buf);
if (buf) {
return acc.concat(buf);
}
}
return acc;
}, []))
}));
const contours = g.append('g').classed('contours', true)
.selectAll('path')
.data(layers)
.join('path')
.attr('fill', d => stepColors(d.radius))
.attr('d', d => path(d.feature));
contours.attr('mask', 'url(#main-clip)').append('g').classed('ea-buildings', true)
.selectAll('path')
.data(eaBuildings.features)
.join('path')
.attr('d', path)
.on('click', d => console.log('d', d))
g.append('g').selectAll('path').data(eaBuildings.features).join('path').attr('d', path);
g.append('path').datum(b).attr('stroke-width', 0.1).attr('stroke', 'black').attr('fill', 'none').attr('d', path);
svg.call(d3.zoom().on('zoom', () => g.attr('transform', d3.event.transform)));
return svg.node();
}