Layer Controls
VControlLayer
Overview
The VControlLayer component provides an interactive control for managing layer visibility and opacity. It supports both MapLibre native layers and deck.gl layers, automatically detecting the layer type.
Features:
- Visibility toggle (show/hide layer)
- Opacity slider (0-100%)
- Auto-detection of layer type (MapLibre vs deck.gl)
- Support for fill, line, circle, and symbol layers
- v-model bindings for reactive state management
Usage
<script setup lang="ts">
import { VMap, VControlLayer, VLayerMaplibreGeojson } from '@geoql/v-maplibre';
const mapOptions = {
style: 'https://basemaps.cartocdn.com/gl/positron-gl-style/style.json',
center: [-74.5, 40],
zoom: 9,
};
const geojsonSource = {
type: 'geojson',
data: {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
geometry: { type: 'Point', coordinates: [-74.5, 40] },
properties: {},
},
],
},
};
const layerSpec = {
id: 'points-layer',
type: 'circle',
paint: {
'circle-radius': 10,
'circle-color': '#007cbf',
},
};
</script>
<template>
<VMap :options="mapOptions" style="height: 500px">
<VLayerMaplibreGeojson
source-id="points"
layer-id="points-layer"
:source="geojsonSource"
:layer="layerSpec"
/>
<VControlLayer
layer-id="points-layer"
position="top-right"
title="Points Layer"
/>
</VMap>
</template>
Props
layerId
- Type:
string - Required:
true
The ID of the layer to control. Must match either a MapLibre layer ID or a deck.gl layer ID.
position
- Type:
'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' - Required:
false - Default:
'top-right'
Position of the control on the map.
visible
- Type:
boolean - Required:
false - Default:
true
Initial visibility state of the layer. Supports v-model binding with v-model:visible.
opacity
- Type:
number - Required:
false - Default:
1
Initial opacity of the layer (0-1). Supports v-model binding with v-model:opacity.
title
- Type:
string - Required:
false - Default:
'Layer Control'
Title displayed in the control panel.
layerType
- Type:
'maplibre' | 'deckgl' - Required:
false - Default: Auto-detected
Force the layer type. If not specified, the component auto-detects whether the layer is a MapLibre or deck.gl layer.
Events
@visibility-change
Emitted when the layer visibility changes.
- Payload:
boolean
@opacity-change
Emitted when the layer opacity changes.
- Payload:
number
@update:visible
Emitted for v-model:visible binding.
- Payload:
boolean
@update:opacity
Emitted for v-model:opacity binding.
- Payload:
number
v-model Bindings
The component supports two-way binding for both visibility and opacity:
<script setup lang="ts">
import { ref } from 'vue';
const isVisible = ref(true);
const layerOpacity = ref(1);
</script>
<template>
<VControlLayer
layer-id="my-layer"
v-model:visible="isVisible"
v-model:opacity="layerOpacity"
/>
<p>Visible: {{ isVisible }}</p>
<p>Opacity: {{ layerOpacity }}</p>
</template>
Layer Type Support
MapLibre Layers
For MapLibre native layers, the control automatically maps to the correct opacity property:
| Layer Type | Opacity Property |
|---|---|
fill | fill-opacity |
line | line-opacity |
circle | circle-opacity |
symbol | icon-opacity |
deck.gl Layers
For deck.gl layers, the control uses the layer's visible and opacity props. The component clones the layer with updated properties using the immutable pattern required by deck.gl.
With deck.gl Layers
<script setup lang="ts">
import { VMap, VControlLayer, VLayerDeckglScatterplot } from '@geoql/v-maplibre';
const mapOptions = {
style: 'https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json',
center: [-122.4, 37.8],
zoom: 11,
};
const data = [
{ coordinates: [-122.4, 37.8], size: 100 },
{ coordinates: [-122.5, 37.7], size: 200 },
];
</script>
<template>
<VMap :options="mapOptions" style="height: 500px">
<VLayerDeckglScatterplot
id="scatter-layer"
:data="data"
:get-position="(d) => d.coordinates"
:get-radius="(d) => d.size"
:get-fill-color="[255, 140, 0]"
/>
<VControlLayer
layer-id="scatter-layer"
position="top-left"
title="Scatter Points"
/>
</VMap>
</template>
TypeScript
import type { ControlPosition, LayerType } from '@geoql/v-maplibre';
// Props interface
interface LayerControlOptions {
layerId: string;
position?: ControlPosition;
visible?: boolean;
opacity?: number;
title?: string;
layerType?: LayerType;
}
VControlLayerGroup
The VControlLayerGroup component provides a collapsible panel for controlling multiple layers at once. Each layer gets its own visibility toggle and opacity slider within a single unified control.
Overview
Use VControlLayerGroup when you need to:
- Control multiple related layers in one place
- Provide a cleaner UI than multiple individual
VControlLayercomponents - Allow users to collapse/expand the layer panel
Features:
- Collapsible panel header
- Per-layer visibility toggle and opacity slider
- Support for mixed MapLibre and deck.gl layers
- Configurable layer list via props
Usage
<script setup lang="ts">
import { VMap, VControlLayerGroup, VLayerMaplibreGeojson } from '@geoql/v-maplibre';
const mapOptions = {
style: 'https://basemaps.cartocdn.com/gl/positron-gl-style/style.json',
center: [-96, 37.8],
zoom: 4,
};
const statesGeoJson = ref(null);
// ... fetch GeoJSON data
const layerGroupConfig = [
{ id: 'states-fill', title: 'US States', visible: true, opacity: 0.5 },
{ id: 'states-outline', title: 'State Borders', visible: true, opacity: 1 },
];
</script>
<template>
<VMap :options="mapOptions" style="height: 500px">
<VLayerMaplibreGeojson
source-id="states"
layer-id="states-fill"
:source="{ type: 'geojson', data: statesGeoJson }"
:layer="{
id: 'states-fill',
type: 'fill',
source: 'states',
paint: { 'fill-color': '#627BC1', 'fill-opacity': 0.5 }
}"
/>
<VLayerMaplibreGeojson
source-id="states"
layer-id="states-outline"
:source="{ type: 'geojson', data: statesGeoJson }"
:layer="{
id: 'states-outline',
type: 'line',
source: 'states',
paint: { 'line-color': '#627BC1', 'line-width': 2 }
}"
/>
<VControlLayerGroup
:layers="layerGroupConfig"
title="Map Layers"
position="top-right"
:collapsible="true"
/>
</VMap>
</template>
Props
layers
- Type:
LayerConfig[] - Required:
true
Array of layer configurations. Each layer config has:
| Property | Type | Default | Description |
|---|---|---|---|
id | string | - | Layer ID (must match MapLibre or deck.gl layer) |
title | string | - | Display title in the control |
visible | boolean | true | Initial visibility |
opacity | number | 1 | Initial opacity (0-1) |
type | 'maplibre' | 'deckgl' | Auto-detected | Force layer type |
position
- Type:
'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' - Required:
false - Default:
'top-right'
Position of the control on the map.
title
- Type:
string - Required:
false - Default:
'Layers'
Title displayed in the panel header.
collapsible
- Type:
boolean - Required:
false - Default:
true
Whether the panel can be collapsed by clicking the header.
collapsed
- Type:
boolean - Required:
false - Default:
false
Initial collapsed state of the panel.
Events
@visibility-change
Emitted when a layer's visibility changes.
- Payload:
{ layerId: string; visible: boolean }
@opacity-change
Emitted when a layer's opacity changes.
- Payload:
{ layerId: string; opacity: number }
@update:layers
Emitted when the layers array should be updated (for v-model binding).
- Payload:
LayerConfig[]
Mixed Layer Types
VControlLayerGroup supports controlling both MapLibre and deck.gl layers in the same panel:
<script setup lang="ts">
const mixedLayers = [
{ id: 'maplibre-fill-layer', title: 'Fill Layer', type: 'maplibre' },
{ id: 'deckgl-scatter-layer', title: 'Scatter Points', type: 'deckgl' },
{ id: 'maplibre-line-layer', title: 'Roads', type: 'maplibre' },
];
</script>
<template>
<VControlLayerGroup
:layers="mixedLayers"
title="All Layers"
/>
</template>
TypeScript
import type { ControlPosition, LayerType, LayerConfig } from '@geoql/v-maplibre';
// LayerConfig interface
interface LayerConfig {
id: string;
title: string;
visible?: boolean;
opacity?: number;
type?: LayerType;
}
// Props interface
interface LayerGroupOptions {
layers: LayerConfig[];
position?: ControlPosition;
title?: string;
collapsible?: boolean;
collapsed?: boolean;
}