Core concepts โ
Let's build the map step by step to understand how the library works
Data โ
Map 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>
<Map :data="data">
<MapFeatures />
</Map>
</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>
<Map
:data="data"
:data-transformer="dataTransformer"
>
<MapFeatures />
</Map>
</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 { geoMercator } from 'd3-geo-projection'
</script>
<template>
<Map
:data="data"
:data-transformer="dataTransformer"
:projection="geoMercator"
>
<MapFeatures />
</Map>
</template>Details
- You can tweak a projection with Map.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>
<Map
:data="data"
:data-transformer="dataTransformer"
:projection="geoMercator"
>
<MapFeatures>
<template #default="{ features }">
<MapFeature
v-for="feature in features"
:key="feature.id"
:data="feature"
/>
</template>
</MapFeatures>
</Map>
</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>
<Map
:data="data"
:projection="geoMercator"
:data-transformer="dataTransformer"
>
<MapFeatures />
<MapMesh stroke="#fff" />
</Map>
</template>Default stroke width is 0.5
Graticule โ
Use MapGraticule to draw latitude and longitude grid lines
<template>
<Map
:data="data"
:projection="geoMercator"
:data-transformer="dataTransformer"
>
<MapGraticule stroke="#cbd5e1" />
<MapFeatures />
<MapMesh stroke="#fff" />
</Map>
</template>Zoom โ
Wrap layers with MapZoom to enable pan and zoom
<template>
<Map
:data="data"
:projection="geoMercator"
:data-transformer="dataTransformer"
>
<MapZoom>
<MapFeatures />
<MapMesh stroke="#fff" />
<MapGraticule stroke="#cbd5e1" />
</MapZoom>
</Map>
</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>
<Map
:data="data"
:projection="geoMercator"
:data-transformer="dataTransformer"
>
<MapZoom>
<MapFeatures />
<MapMarker :coordinates="[-83.0457538, 42.331427]">
<text>Sweet home ๐งก</text>
<circle r="3" />
</MapMarker>
<MapMesh stroke="#fff" />
<MapGraticule stroke="#cbd5e1" />
</MapZoom>
</Map>
</template>Styling โ
MapFeature*, MapMarker, MapMesh, and MapGraticule accept a styles prop
const styles = {
default: { fill: 'green' }, // default state
hover: { fill: 'green', opacity: 0.8 }, // on hover
active: { fill: 'darkgreen' }, // on mousedown
}<template>
<Map
:data="data"
:projection="geoMercator"
:data-transformer="dataTransformer"
>
<MapZoom>
<MapFeatures :styles="styles"/>
<MapMarker
:styles="styles"
:coordinates="[-83.0457538, 42.331427]"
>
<text>Sweet home ๐งก</text>
<circle r="3" />
</MapMarker>
<MapMesh stroke="#fff" />
<MapGraticule stroke="#cbd5e1" />
</MapZoom>
</Map>
</template>* MapFeatures forwards
stylesto internally renderedMapFeature's
CSS โ
You can define styles for map components via plain CSS
| Component | CSS selector |
|---|---|
| Global defaults | :root |
| Map | .d3-map |
| MapFeature | [name="feature"] |
| MapMesh | [name="mesh"] |
| MapMarker | [name="marker"] |
| MapGraticule lines | [name="graticule"] |
| MapGraticule border | [name="border"] |
| MapGraticule background | [name="background"] |
| MapZoom | [name="zoom"] |
Source: packages/core/src/index.css
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">
<Map
:data="data"
:projection="geoMercator"
:data-transformer="dataTransformer"
:aspect-ratio="2 / 1"
>
<MapZoom>
<MapFeatures :styles="styles"/>
<MapMarker
:styles="styles"
:coordinates="[-83.0457538, 42.331427]"
>
<text>Sweet home ๐งก</text>
<circle r="3" />
</MapMarker>
<MapMesh stroke="#fff" />
<MapGraticule stroke="#cbd5e1" />
</MapZoom>
</Map>
</div>
</template>Details
Map 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.
That's it โ
What's next?
- Explore examples
- Check out components