Native MapLibre GL layer components for common use cases.
These components wrap MapLibre's native layer types:
| Component | Description |
|---|---|
VLayerMaplibreGeojson | GeoJSON data visualization |
VLayerMaplibreVector | Vector tile layers |
VLayerMaplibreRaster | Raster tile layers |
VLayerMaplibreImage | Georeferenced images |
VLayerMaplibreVideo | Georeferenced video |
VLayerMaplibreCanvas | Custom canvas rendering |
VLayerMaplibrePmtile | PMTiles archives |
Higher-level components that compose multiple MapLibre layers:
Clustered point visualization with automatic grouping.
| Prop | Type | Default | Description |
|---|---|---|---|
sourceId | string | 'cluster-source' | GeoJSON source ID |
baseLayerId | string | 'cluster' | Base ID for generated layers |
source | GeoJSONSourceSpecification | - | GeoJSON source configuration |
visibility | boolean | true | Layer visibility |
clusterPaint | object | See below | Cluster circle styling |
unclusteredPaint | object | See below | Individual point styling |
textPaint | object | See below | Cluster count text styling |
| Event | Payload | Description |
|---|---|---|
cluster-click | { features, coordinates } | Cluster circle clicked |
point-click | { features, coordinates } | Individual point clicked |
<script setup lang="ts">
import { VMap, VLayerMaplibreCluster } from '@geoql/v-maplibre';
const earthquakes = ref({
type: 'FeatureCollection',
features: [], // Load your point data
});
</script>
<template>
<VMap :options="mapOptions">
<VLayerMaplibreCluster
source-id="earthquakes"
base-layer-id="quakes"
:source="{ type: 'geojson', data: earthquakes }"
@cluster-click="handleClusterClick"
@point-click="handlePointClick"
/>
</VMap>
</template>
Route/path visualization for navigation and delivery tracking.
| Prop | Type | Default | Description |
|---|---|---|---|
id | string | Auto-generated | Unique route identifier |
coordinates | [number, number][] | - | Array of lng, lat coordinates |
color | string | '#4285F4' | Line color |
width | number | 4 | Line width in pixels |
opacity | number | 1 | Line opacity (0-1) |
lineCap | 'butt' | 'round' | 'square' | 'round' | Line cap style |
lineJoin | 'bevel' | 'round' | 'miter' | 'round' | Line join style |
visible | boolean | true | Route visibility |
interactive | boolean | true | Enable hover/click |
dashArray | number[] | - | Dash pattern for dashed lines |
blur | number | 0 | Line blur in pixels |
| Event | Payload | Description |
|---|---|---|
click | { coordinates } | Route line clicked |
mouseenter | - | Mouse enters route |
mouseleave | - | Mouse leaves route |
<script setup lang="ts">
import { VMap, VLayerMaplibreRoute } from '@geoql/v-maplibre';
const routeCoordinates = ref([
[-122.4194, 37.7749],
[-122.4089, 37.7855],
[-122.3984, 37.7925],
]);
</script>
<template>
<VMap :options="mapOptions">
<VLayerMaplibreRoute
id="my-route"
:coordinates="routeCoordinates"
color="#10b981"
:width="5"
@click="handleRouteClick"
/>
</VMap>
</template>
Isochrone/isodistance polygon visualization for travel time analysis.
| Prop | Type | Default | Description |
|---|---|---|---|
id | string | Auto-generated | Unique layer identifier |
data | IsochroneData | null | - | GeoJSON FeatureCollection from routing API |
fillOpacity | number | 0.4 | Polygon fill opacity (0-1) |
lineWidth | number | 2 | Outline width in pixels |
lineOpacity | number | 0.8 | Outline opacity (0-1) |
visible | boolean | true | Layer visibility |
interactive | boolean | true | Enable hover/click |
reverseOrder | boolean | true | Reverse feature order for proper stacking |
| Event | Payload | Description |
|---|---|---|
click | { feature, coordinates } | Polygon clicked |
mouseenter | feature | Mouse enters polygon |
mouseleave | - | Mouse leaves polygon |
The component accepts GeoJSON from routing APIs like Valhalla or OSRM. It automatically handles color format conversion (Valhalla returns colors without # prefix).
interface IsochroneData {
type: 'FeatureCollection';
features: Array<{
type: 'Feature';
properties: {
color: string; // e.g., '2563eb' or '#2563eb'
contour?: number; // Time in minutes or distance
metric?: string; // 'time' or 'distance'
};
geometry: Polygon | MultiPolygon;
}>;
}
<script setup lang="ts">
import { VMap, VMarker, VLayerMaplibreIsochrone } from '@geoql/v-maplibre';
const originPoint = ref<[number, number]>([-73.985, 40.758]);
const isochroneData = ref(null);
async function fetchIsochrone() {
const params = {
locations: [{ lat: originPoint.value[1], lon: originPoint.value[0] }],
costing: 'auto',
contours: [
{ time: 5, color: '2563eb' },
{ time: 10, color: '7c3aed' },
{ time: 15, color: 'db2777' },
],
polygons: true,
};
const response = await fetch(`/api/isochrone?json=${encodeURIComponent(JSON.stringify(params))}`);
isochroneData.value = await response.json();
}
function handleMarkerDrag(event) {
const lngLat = event.target.getLngLat();
originPoint.value = [lngLat.lng, lngLat.lat];
fetchIsochrone();
}
</script>
<template>
<VMap :options="mapOptions" @loaded="fetchIsochrone">
<VLayerMaplibreIsochrone
id="travel-zones"
:data="isochroneData"
:fill-opacity="0.5"
@click="handleZoneClick"
/>
<VMarker
:coordinates="originPoint"
:options="{ draggable: true }"
@dragend="handleMarkerDrag"
/>
</VMap>
</template>