map = (initialValue = []) => {
const mapWidth = 400
const mapHeight = mapWidth
const mapNode = htl.html`<svg viewBox="0 0 ${mapWidth} ${mapHeight}" />`
const map = d3.select(mapNode)
map.attr('style', `max-width: ${mapWidth}px; width: 100%`)
const deselectedOpacity = 0.25
const projection = d3.geoIdentity()
.fitExtent([[20, 20], [mapWidth - 20, mapWidth - 20]], features.sewersheds)
const path = d3.geoPath(projection)
map.append('rect')
.attr('x', 0.5)
.attr('y', 0.5)
.attr('width', mapWidth - 1)
.attr('height', mapWidth - 1)
.attr('fill', '#F5F5F5')
.attr('stroke', '#CCC')
.attr('pointer-events', 'none')
const newYork = map.append('path')
.attr('d', path(features.shoreline))
.attr('fill', '#CCC')
.attr('stroke', '#FFF')
.attr('pointer-events', 'none')
const selectedCities = initialValue
const sewershedShapes = map.append('g')
.selectAll()
.data(features.sewersheds.features)
.join('path')
.attr('class', 'plant-sewershed')
.attr('data-plant', d => d.properties.Sewershed)
.attr('d', path)
.attr('fill', d => colorScale(d.properties.Sewershed))
.attr('opacity', d => initialValue.includes(d.properties.Sewershed) ? 1 : deselectedOpacity)
.attr('stroke', '#FFF')
.attr('data-selected', d => initialValue.includes(d.properties.Sewershed))
.on('mouseover', function (e, d) {
map.select(`.plant-name[data-plant="${d.properties.Sewershed}"]`).attr('opacity', 1)
})
.on('mouseout', function (e, d) {
map.select(`.plant-name[data-plant="${d.properties.Sewershed}"]`).attr('opacity', 0)
})
.on('click', function (e, d) {{
const sel = d3.select(this)
const sewershed = d.properties.Sewershed
if (sewershed === 'UNKNOWN') return
if (view.value.includes(sewershed)) { // if already selected, remove from value array
const valueIdx = view.value.findIndex(v => sewershed === v)
view.value.splice(valueIdx, 1)
updateSelection(view.value)
} else { // if not already selected, add to value array
updateSelection(view.value.concat([sewershed]))
}
}})
const plantPoints = map.append('g')
.selectAll()
.data(features.plants.features.filter(d => d.properties.wpcp))
.join(g => {
g.append('circle')
.attr('class', 'plant-dot')
.attr('data-plant', d => d.properties.wpcp)
.attr('cx', d => projection(d.geometry.coordinates)[0])
.attr('cy', d => projection(d.geometry.coordinates)[1])
.attr('r', 5)
.attr('stroke', '#FFF')
.attr('fill', d => colorScale(d.properties.wpcp))
.attr('pointer-events', 'none')
g.append('text')
.attr('class', 'plant-name')
.attr('data-plant', d => d.properties.wpcp)
.attr('x', d => projection(d.geometry.coordinates)[0])
.attr('y', d => projection(d.geometry.coordinates)[1])
.attr('dx', 8)
.attr('dy', 4)
.attr('font-family', 'var(--sans-serif), sans-serif')
.attr('font-size', 11)
.attr('fill', '#000')
.attr('stroke', '#FFF')
.attr('stroke-width', 3)
.attr('paint-order', 'stroke')
.attr('text-anchor', 'start')
.attr('opacity', 0)
.attr('pointer-events', 'none')
.text(d => d.properties.item_id)
})
const updateSelection = (arr) => {
view.value = arr
view.dispatchEvent(new Event('input', { bubbles: true }))
sewershedShapes
.attr('opacity', d => arr.includes(d.properties.Sewershed) ? 1 : deselectedOpacity)
.attr('data-selected', d => arr.includes(d.properties.Sewershed))
}
const selectAll = htl.html`<button>All</button>`
const selectNone = htl.html`<button>None</button>`
selectAll.addEventListener('click', () => updateSelection(Array.from(wrrfAbbrs)))
selectNone.addEventListener('click', () => updateSelection([]))
const view = htl.html`
<div>
<div style=${{ position: 'relative' }}>
${map.node()}
<div style=${{
position: 'absolute',
top: '10px',
left: '10px',
fontSize: '12px',
fontFamily: 'var(--sans-serif), sans-serif',
verticalAlign: 'middle',
}}>
Select: ${selectAll} ${selectNone}
</div>
</div>
</div>
`
view.value = selectedCities
return view
}