slightlyFancyPatterns = {
const svg = d3.select(DOM.svg(width, height));
const defs = svg.append('defs');
const patternSize = 20;
defs.append('clipPath')
.attr('id', 'patternClip')
.attr('clipPathUnits', 'objectBoundingBox')
.append('rect')
.attr('x', 0)
.attr('y', .5)
.attr('width', 1)
.attr('height', .5);
const waterPattern = defs.append('pattern')
.attr('id', 'waterPattern')
.attr('width', patternSize)
.attr('height', patternSize * 1.5)
.attr('patternUnits', 'userSpaceOnUse')
waterPattern.append('rect')
.attr('width', patternSize)
.attr('height', patternSize * 2)
.attr('fill', waterColor);
const waves = waterPattern.append('g')
.style('fill', 'none')
.style('stroke', waterGlowColor)
.style('stroke-width', 0.5)
.attr('transform', 'scale(1, .75)');
waves.append('circle')
.attr('clip-path', 'url(#patternClip)')
.attr('cx', 0)
.attr('cy', 0)
.attr('r', patternSize * .5);
waves.append('circle')
.attr('clip-path', 'url(#patternClip)')
.attr('cx', patternSize)
.attr('cy', 0)
.attr('r', patternSize * .5);
// kind of centered
// bottom half of a circle: \_/
waves.append('circle')
.attr('clip-path', 'url(#patternClip)')
.attr('cx', patternSize * .5)
.attr('cy', patternSize)
.attr('r', patternSize * .5);
// see below for an image of what the above look like altogether
// the land pattern works similarly but is simpler, just some dots
const landPattern = defs.append('pattern')
.attr('id', 'landPattern')
.attr('width', patternSize)
.attr('height', patternSize)
.attr('patternUnits', 'userSpaceOnUse');
landPattern.append('rect')
.attr('width', patternSize)
.attr('height', patternSize)
.attr('fill', landColor);
const dotColor = '#eee';
// drawing dots could be done more concisely in a loop, but here they are explicitly
landPattern.append('circle')
.attr('cx', 0)
.attr('cy', 0)
.attr('r', patternSize/8)
.attr('fill', dotColor);
landPattern.append('circle')
.attr('cx', patternSize/2)
.attr('cy', 0)
.attr('r', patternSize/8)
.attr('fill', dotColor);
landPattern.append('circle')
.attr('cx', patternSize)
.attr('cy', 0)
.attr('r', patternSize/8)
.attr('fill', dotColor);
landPattern.append('circle')
.attr('cx', patternSize/4)
.attr('cy', patternSize/2)
.attr('r', patternSize/8)
.attr('fill', dotColor);
landPattern.append('circle')
.attr('cx', 3 * patternSize/4)
.attr('cy', patternSize/2)
.attr('r', patternSize/8)
.attr('fill', dotColor);
landPattern.append('circle')
.attr('cx', 0)
.attr('cy', patternSize)
.attr('r', patternSize/8)
.attr('fill', dotColor);
landPattern.append('circle')
.attr('cx', patternSize/2)
.attr('cy', patternSize)
.attr('r', patternSize/8)
.attr('fill', dotColor);
landPattern.append('circle')
.attr('cx', patternSize)
.attr('cy', patternSize)
.attr('r', patternSize/8)
.attr('fill', dotColor);
const waterLayer = svg.append('g');
waterLayer.append('rect')
.attr('width', width)
.attr('height', height)
.style('fill', 'url(#waterPattern)'); // use water pattern
const statesLayer = svg.append('g');
statesLayer.selectAll('path')
.data(states)
.enter()
.append('path')
.attr('d', geoPath)
.attr('fill', 'url(#landPattern)') // use land pattern
.attr('stroke', '#ccc');
return svg.node();
}