Core concepts โ
Let's build the map step by step to understand how the library works
Data โ
MapBase accepts either GeoJSON or TopoJSON and then transforms it into GeoJSON.
Both used for geo data encoding, but TopoJSON is recommended, it's smaller.
Simply pass data prop to render the basic map
<template>
<MapBase :data="data">
<MapFeatures />
</MapBase>
</template>Data transformation โ
Add dataTransformer to preprocess GeoJSON features before render
// e.g. don't render Antarctica ๐ฆ๐ถ
function dataTransformer(features) {
return features.filter((x) => x.properties?.name !== 'Antarctica')
}<template>
<MapBase
:data="data"
:data-transformer="dataTransformer"
>
<MapFeatures />
</MapBase>
</template>Projection โ
A map projection transforms the Earth's 3D curved surface into SVG map.
It determines how exactly map will look.
By default geoNaturalEarth1 is used in core, but you can provide your own:
<script setup>
import { geoEquirectangular } from 'd3-geo'
</script>
<template>
<MapBase
:data="data"
:data-transformer="dataTransformer"
:projection="geoEquirectangular"
>
<MapFeatures />
</MapBase>
</template>Details
- You can tweak a projection with MapBase.projectionConfig (defaults are strong though)
- Projections are available in d3-geo and d3-geo-projection
- Here you can see visualized projections
Features โ
Map feature is geographic entity, e.g. country or state.
MapFeatures render all features internally, MapFeature renders a single one.
Switch to slot/render-function when each feature needs custom render logic.
<template>
<MapBase
:data="data"
:data-transformer="dataTransformer"
:projection="geoEquirectangular"
>
<MapFeatures>
<template #default="{ features }">
<MapFeature
v-for="feature in features"
:key="feature.id"
:data="feature"
/>
</template>
</MapFeatures>
</MapBase>
</template>Mesh โ
To render borders use MapMesh instead of applying stroke. It will render a single <path> (more efficient) and ensure borders don't overlap.
<template>
<MapBase
:data="data"
:projection="geoEquirectangular"
:data-transformer="dataTransformer"
>
<MapFeatures />
<MapMesh stroke="#fff" />
</MapBase>
</template>Default stroke width is 0.5
Graticule โ
Use MapGraticule to draw latitude and longitude grid lines
<template>
<MapBase
:data="data"
:projection="geoEquirectangular"
:data-transformer="dataTransformer"
>
<MapGraticule border />
<MapFeatures />
<MapMesh stroke="#fff" />
</MapBase>
</template>Zoom โ
Wrap layers with MapZoom to enable pan and zoom
<template>
<MapBase
:data="data"
:projection="geoEquirectangular"
:data-transformer="dataTransformer"
>
<MapZoom>
<MapGraticule border />
<MapFeatures />
<MapMesh stroke="#fff" />
</MapZoom>
</MapBase>
</template>Detailed zoom usage example
Markers โ
Add any points to the map with MapMarker
- pass
coordinatesas[longitude, latitude] - and any SVG elements as a children
<template>
<MapBase
:data="data"
:projection="geoEquirectangular"
:data-transformer="dataTransformer"
>
<MapZoom>
<MapGraticule border />
<MapFeatures />
<MapMesh 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>
</MapBase>
</template>Styling โ
MapFeature*, MapMarker, MapMesh, MapGraticule, MapLine, and MapAnnotation accept a styles prop
const styles = {
default: { fill: 'lightblue' }, // default state
hover: { fill: 'skyblue' }, // on hover
active: { fill: 'lightskyblue' }, // on mousedown
}<template>
<MapBase
:data="data"
:projection="geoEquirectangular"
:data-transformer="dataTransformer"
>
<MapZoom>
<MapGraticule border />
<MapFeatures :styles="styles"/>
<MapMesh 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>
</MapBase>
</template>* MapFeatures forwards
stylesto internally renderedMapFeature's
CSS โ
- Import default stylesheet to simplify global map styles
import '@d3-maps/vue/index.css'- Define styles for map components with plain CSS
| Component | CSS selector |
|---|---|
| MapBase | .d3-map |
| MapFeature | [name="feature"] |
| MapMesh | [name="mesh"] |
| MapMarker | [name="marker"] |
| MapGraticule lines | [name="graticule"] |
| MapGraticule border | [name="border"] |
| MapGraticule background | [name="background"] |
| MapLine | [name="line"] |
| MapAnnotation connector | [name="annotation-line"] |
| MapZoom | [name="zoom"] |
See example (this site) packages/docs/.vitepress/theme/custom.css
Responsiveness โ
Simply make it with an aspect-ratio wrapper and the aspect-ratio prop
<template>
<div style="aspect-ratio: 2 / 1">
<MapBase
:data="data"
:projection="geoEquirectangular"
:data-transformer="dataTransformer"
:aspect-ratio="2 / 1"
>
<MapZoom>
<MapGraticule border />
<MapFeatures :styles="styles"/>
<MapMesh 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>
</MapBase>
</div>
</template>Details
MapBase renders an <svg> with a viewBox and width="100%" height="auto".
Hence the parent element must have height, otherwise map will collapse.
Under the hood fitExtent([[1, 1], [width - 1, height - 1]], { type: 'Sphere' } is used.
Overridable with fitExtent | fitSize | fitWidth | fitHeight passed to projectionConfig.
Result โ
That's it โ
What's next?
- Explore examples
- Check out components