Core concepts โ
Learn how d3-maps works under the hood
Data โ
MapFeatures accepts GeoJSON and TopoJSON, then normalizes them to map features: GeoJSON Feature objects and geometries.
<template>
<MapBase>
<MapFeatures :data="data" />
</MapBase>
</template>Data transformation โ
transformer preprocesses normalized GeoJSON objects before render. It is useful for filtering, enrichment, or normalization.
// e.g. don't render Antarctica ๐ฆ๐ถ
function transformer(features) {
return features.filter((feature) => (
feature.properties.name !== 'Antarctica'
))
}<template>
<MapBase>
<MapFeatures
:data="data"
:transformer="transformer"
/>
</MapBase>
</template>Projection โ
A projection transforms the Earth's curved surface into flat 2D coordinates. It defines the overall shape of the rendered map.
<script setup>
import { geoEquirectangular } from 'd3-geo'
</script>
<template>
<MapBase
:projection="geoEquirectangular"
>
<MapFeatures
:data="data"
:transformer="transformer"
/>
</MapBase>
</template>Details
- Projections are available in d3-geo and d3-geo-projection
geoNaturalEarth1from d3-geo is used by default- Observable projection gallery shows the visual differences
Features โ
A feature is any natural or human-made geographic object: country, road, land, river, etc.
MapFeatures renders the full collection, while MapFeature handles custom single-feature rendering. The slot or render-function form exposes per-feature rendering control.
<template>
<MapBase
:projection="geoEquirectangular"
>
<MapFeatures
:data="data"
:transformer="transformer"
>
<template #default="{ features }">
<MapFeature
v-for="feature in features"
:key="feature.key"
:d="feature.d"
/>
</template>
</MapFeatures>
</MapBase>
</template>Mesh โ
MapMesh renders shared borders as one <path>. This avoids double-stroked edges that appear when every feature draws its own border.
<template>
<MapBase
:projection="geoEquirectangular"
>
<MapFeatures
:data="data"
:transformer="transformer"
/>
<MapMesh :data="data" stroke="#fff" />
</MapBase>
</template>Sphere and Graticule โ
MapSphere renders map outline and background. MapGraticule renders latitude and longitude grid lines.
<template>
<MapBase
:projection="geoEquirectangular"
>
<MapSphere fill="var(--vp-c-bg-alt)" />
<MapGraticule />
<MapFeatures
:data="data"
:transformer="transformer"
/>
<MapMesh :data="data" stroke="#fff" />
<MapSphere stroke="var(--vp-c-border)" />
</MapBase>
</template>Zoom โ
MapZoom adds pan and zoom behavior. Note: wrap only elements you want to zoom, e.g. MapSphere here doesn't need zoom.
<template>
<MapBase
:projection="geoEquirectangular"
>
<MapSphere fill="var(--vp-c-bg-alt)" />
<MapZoom>
<MapGraticule />
<MapFeatures
:data="data"
:transformer="transformer"
/>
<MapMesh :data="data" stroke="#fff" />
</MapZoom>
<MapSphere stroke="var(--vp-c-border)" />
</MapBase>
</template>See detailed example and programmatic zoom example
Markers โ
MapMarker projects a point onto the map and renders arbitrary SVG content there. Coordinates use [longitude, latitude]
<template>
<MapBase
:projection="geoEquirectangular"
>
<MapSphere fill="var(--vp-c-bg-alt)" />
<MapZoom>
<MapGraticule />
<MapFeatures
:data="data"
:transformer="transformer"
/>
<MapMesh :data="data" stroke="#fff" />
<MapMarker :coordinates="[-83.0457538, 42.331427]">
<text
font-size="14"
y="-6"
text-anchor="middle"
>Sweet home ๐งก</text>
<circle r="3" />
</MapMarker>
</MapZoom>
<MapSphere stroke="var(--vp-c-border)" />
</MapBase>
</template>See a simple example and an example of markers with zoom
Styling โ
MapElement*, MapFeature, MapMarker, MapLine, and MapAnnotation expose a styles prop for interaction-state styling
const styles = {
default: { fill: 'lightblue' }, // default state
focus: { stroke: 'deepskyblue' }, // on keyboard focus
hover: { fill: 'skyblue' }, // on hover
active: { fill: 'lightskyblue' }, // on mousedown
}<template>
<MapBase
:projection="geoEquirectangular"
>
<MapSphere fill="var(--vp-c-bg-alt)" />
<MapZoom>
<MapGraticule />
<MapFeatures
:data="data"
:transformer="transformer"
:styles="styles"
/>
<MapMesh :data="data" stroke="#fff" />
<MapMarker :coordinates="[-83.0457538, 42.331427]">
<text
font-size="14"
y="-6"
text-anchor="middle"
>Sweet home ๐งก</text>
<circle r="3" />
</MapMarker>
</MapZoom>
<MapSphere stroke="var(--vp-c-border)" />
</MapBase>
</template>* MapFeatures forwards
stylesto internally renderedMapFeatures
CSS โ
The default stylesheet provides a shared base for map presentation
import '@d3-maps/vue/style.css'Plain CSS can also target the generated SVG elements directly
| Component | CSS selector |
|---|---|
| MapBase | .d3-map |
| MapFeatures | [data-d3m="feature"] |
| MapMesh | [data-d3m="mesh"] |
| MapMarker | [data-d3m="marker"] |
| MapGraticule lines | [data-d3m="graticule"] |
| MapSphere | [data-d3m="sphere"] |
| MapLine | [data-d3m="line"] |
| MapAnnotation connector | [data-d3m="annotation-line"] |
| MapZoom | [data-d3m="zoom"] |
See example (this site) packages/docs/.vitepress/theme/custom.css
Responsiveness โ
MapBase renders an SVG with viewBox, width="100%", and height="auto". Responsive layouts usually pair a parent aspect-ratio wrapper with the aspectRatio prop
<template>
<div style="aspect-ratio: 2 / 1">
<MapBase
:projection="geoEquirectangular"
:aspect-ratio="2 / 1"
>
<MapSphere fill="var(--vp-c-bg-alt)" />
<MapZoom>
<MapGraticule />
<MapFeatures
:data="data"
:transformer="transformer"
:styles="styles"
/>
<MapMesh :data="data" stroke="#fff" />
<MapMarker :coordinates="[-83.0457538, 42.331427]">
<text
font-size="14"
y="-6"
text-anchor="middle"
>Sweet home ๐งก</text>
<circle r="3" />
</MapMarker>
</MapZoom>
<MapSphere stroke="var(--vp-c-border)" />
</MapBase>
</div>
</template>Details
The parent element must have height, otherwise the map collapses.
By default the projection uses:fitExtent([[padding, padding], [width - padding, height - padding]], { type: 'Sphere' })
Top-level fit defaults to sphere. projectionConfig.padding defaults to 1. Explicit fitExtent, fitSize, fitWidth, or fitHeight override the built-in fit flow