I'm trying to display geoJSON data on Leaflet, using the path generator from d3 and React. With my following code, I'm able to generate a path that matchs the map correctly, see attached image.

Everything is fine ! But, I soon as I pan or zoom within Leaflet, the drawing disappears ... The canvas html element is still here, and even correctly transformed to its new position but there is no more drawings.
Indeed, it appears only after the pan is completed. Thus, as long as I don't release my mouse click, the drawing is here and even correctly moved along the map. But eventually when the pan is finished, it disappears.
Any idea on this ? :)
Thanks for your help.
US data : https://bost.ocks.org/mike/leaflet/us-states.json
import React, { useEffect, useState } from 'react'
import 'leaflet/dist/leaflet.css'
import * as d3 from 'd3'
var L = null
const source = require('./data/states-us.geojson')
const AppCanvas = () => {
const [mapElement, setMap] = useState(null)
useEffect(() => {
L = require('leaflet')
// Map creation
if (!mapElement) {
const origin = [37.8, -96.9]
const initialZoom = 4
const map = L.map('map')
.setView(origin, initialZoom)
.addLayer(
new L.TileLayer(
'http://{s}.tile.stamen.com/toner-lite/{z}/{x}/{y}.png'
)
)
map.whenReady(() => {
// Create canvas element to the correct size
L.canvas().addTo(map)
const canvas = d3.select('#map').select('canvas')
const projection = d3.geoTransform({
point: function (x, y) {
const point = map.latLngToLayerPoint(new L.LatLng(y, x))
this.stream.point(point.x, point.y) // this : NO ARROW FUNCTION
},
})
const context = canvas.node().getContext('2d')
// Path generator
const path = d3
.geoPath()
.projection(projection)
.context(context)
d3.json(source).then((data) => {
context.beginPath()
path(data)
context.stroke()
})
setMap(map)
})
}
})
return (
<div>
<div
style={{
position: 'absolute',
top: 0,
bottom: 0,
left: 0,
right: 0,
zIndex: 0,
}}
id="map"
></div>
</div>
)
}
Canvas to follow the map and be rerender every pan / zoom end.
I've found a solution : re-render the path every times the 'update' event is trigger on the Leaflet canvas layer.
const canvasLayer = L.canvas().addTo(map)
canvasLayer.on('update', (e) => {
context.save()
context.clearRect(
0,
0,
map.getSize().x,
map.getSize().y
)
context.beginPath()
path(data)
context.stroke()
context.restore()
})
Maybe it's not the best solution regarding performance, but I least it's visually great. The canvas is ALWAYS rendered and ALWAYS correctly positioned on the map. You can find examples on the internet where the canvas is just rendered again after the the map 'zoom' event, resulting in a visually removed / added canvas for the user. This is not the case here.
I've tested out a canvas with 350 polygons / 5000 points and no performance issue, everything is smooth.

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With