D3 geo map
Render a D3 geo projection as a custom shape and explode it into one shape per US state.
import { Editor, TLComponents, Tldraw, TldrawUiButton, useEditor } from 'tldraw'
import 'tldraw/tldraw.css'
import { MAP_HEIGHT, MAP_WIDTH } from './us-map-data'
import { UsMapShapeUtil } from './UsMapShapeUtil'
import './d3-map.css'
import { UsStateShapeUtil } from './UsStateShapeUtil'
// [1]
const shapeUtils = [UsMapShapeUtil, UsStateShapeUtil]
function createMap(editor: Editor) {
editor.createShape({
type: 'us-map',
x: 0,
y: 0,
props: { w: MAP_WIDTH, h: MAP_HEIGHT },
})
}
function resetMap(editor: Editor) {
const mapAndStateIds = [...editor.getCurrentPageShapeIds()].filter((id) => {
const shape = editor.getShape(id)
return shape?.type === 'us-map' || shape?.type === 'us-state'
})
editor.run(() => {
editor.deleteShapes(mapAndStateIds)
createMap(editor)
})
editor.zoomToFit({ animation: { duration: 200 } })
}
function TopPanel() {
const editor = useEditor()
return (
<div className="d3-map-top-panel tlui-menu">
<TldrawUiButton type="normal" onClick={() => resetMap(editor)}>
Reset map
</TldrawUiButton>
</div>
)
}
const components: TLComponents = {
TopPanel,
}
export default function D3MapExample() {
return (
<div className="tldraw__editor">
<Tldraw
shapeUtils={shapeUtils}
components={components}
onMount={(editor) => {
if (editor.getCurrentPageShapeIds().size === 0) {
createMap(editor)
}
editor.zoomToFit({ animation: { duration: 0 } })
}}
/>
</div>
)
}
/*
[1]
Two custom shapes: `us-map` renders every state path in one SVG (see UsMapShapeUtil.tsx),
and `us-state` renders a single state. Double-clicking the map, or clicking its "Explode
states" button, replaces the map with one `us-state` shape per state.
*/
The us-map shape draws every state path from d3-geo and us-atlas inside a single SVGContainer. Double-click it (or press its "Explode states" button) and it is replaced by one us-state shape per state, positioned and scaled from the map's page bounds. Each state can then be moved, resized, and selected on its own.
The explode runs inside editor.run, so deleting the map and creating the states is a single undo step. Use "Reset map" in the top panel to start over.
Is this page helpful?
Prev
Meta migrationsNext
Slideshow (fixed camera)