Controls

Layer Controls

VControlLayer and VControlLayerGroup - Layer visibility toggle and opacity slider controls for MapLibre and deck.gl layers
Live Demo - Try the layer controls with multiple layer types

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 TypeOpacity Property
fillfill-opacity
lineline-opacity
circlecircle-opacity
symbolicon-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 VControlLayer components
  • 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:

PropertyTypeDefaultDescription
idstring-Layer ID (must match MapLibre or deck.gl layer)
titlestring-Display title in the control
visiblebooleantrueInitial visibility
opacitynumber1Initial opacity (0-1)
type'maplibre' | 'deckgl'Auto-detectedForce 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;
}