paperMap = {
const svg = d3.select(DOM.svg(width, height));
const defs = svg.append('defs');
const filter = defs.append('filter')
.attr('id', 'border-blur');
filter.append('feGaussianBlur')
.attr('in', 'SourceGraphic')
.attr('stdDeviation', 5)
.attr('result', 'shadow');
const noiseFilter = defs.append('filter').attr("id", 'noise').attr('filterUnits', 'userSpaceOnUse')
noiseFilter.append("feTurbulence")
.attr('height', height * 2)
.attr('width', width)
.attr('result', 'waves')
.attr('type', 'turbulence')
.attr('baseFrequency', `0.8 0.8`)
.attr('numOctaves', 1)
.attr('seed', 53);
noiseFilter.append('feDisplacementMap')
.attr('in','SourceGraphic')
.attr('in2','waves')
.attr('height', height * 2)
.attr('scale', 50)
.attr('xChannelSelector', 'R')
.attr('yChannelSelector', 'B')
.attr('result','ripples');
noiseFilter.append('feGaussianBlur')
.attr('in', 'ripples')
.attr('color-interpolation-filters', 'sRGB') // for better results in Safari
.attr('stdDeviation', 2);
const paper = defs.append('filter')
.attr('id', 'paper-texture')
.attr('filterUnits', 'userSpaceOnUse');
paper.append('feTurbulence')
.attr('type', 'fractalNoise')
.attr('baseFrequency', '0.04')
.attr('result', 'noise')
.attr('numOctaves', '5');
paper.append('feDiffuseLighting')
.attr('in', 'noise')
.attr('lighting-color', 'white')
.attr('surfaceScale', '2')
.append('feDistantLight')
.attr('azimuth', '45')
.attr('elevation', '60')
const filterOffset = 17;
states.forEach((state) => {
defs.append('clipPath')
.attr('id', `paper-clip-${state.properties.adm1_code}`)
.append('path')
.datum(state)
.attr('d', geoPath)
.attr('transform', `translate(${filterOffset},${filterOffset})`)
});
// solid water fill
const waterLayer = svg.append('g');
waterLayer.append('rect')
.attr('width', width)
.attr('height', height)
.style('fill', waterColor);
// wide, filtered stroke for the "glow"
waterLayer.selectAll('path')
.data(states)
.enter()
.append('path')
.attr('d', geoPath)
.attr('stroke-linejoin', 'round')
.attr('fill', 'none')
.attr('stroke', waterGlowColor)
.attr('opacity',.75)
.attr('stroke-width', 40)
.attr('filter', 'url(#noise)')
.attr('transform', `translate(-${filterOffset},-${filterOffset})`);
const statesLayer = svg.append('g');
// white backround
statesLayer.selectAll('path.fill')
.data(states)
.enter()
.append('path')
.attr('d', geoPath)
.attr('class', 'white')
.attr('fill', 'white');
// state fills
statesLayer.selectAll('path.fill')
.data(states)
.enter()
.append('path')
.attr('d', geoPath)
.attr('class', 'fill')
.attr('fill', d => d.properties.color)
.attr('fill-opacity', .2);
// wide, blurred/textured stroke
statesLayer.selectAll('path.glow')
.data(states)
.enter()
.append('path')
.attr('d', geoPath)
.attr('class', 'glow')
.attr('stroke-linejoin', 'round')
.attr('stroke', d => d.properties.color)
.attr('opacity',.75)
.attr('stroke-width', 25)
.attr('fill', 'none')
.attr('clip-path', d => `url(#paper-clip-${d.properties.adm1_code})`)
.attr('filter', 'url(#noise)')
.attr('transform', `translate(-${filterOffset},-${filterOffset})`);
// overlay a rectangle with paper texture
svg.append('rect')
.attr('width','100%')
.attr('height', '100%')
.attr('filter', 'url(#paper-texture)')
.style('mix-blend-mode', 'multiply')
.style('opacity', .75)
return svg.node();
}