Skip to content

ol-overlay

HTML element attached to geographical coordinate

ol-overlay component creates a HTML element that would be displayed over the map. It has default scoped slot to render your custom content.

Setup

Plugin usage

This component is part of the Map plugin. If not installed globally, you need to import and use the plugin in your main.ts or use the explicit component import (see section below).

Import and use the Map plugin
ts
import { createApp } from "vue";
import App from "./App.vue";

import {

  Map,
  Layers,
  Sources,
} from "vue3-openlayers";

const app = createApp(App);
// ...
app.use(Map); 
// ...

Explicit import

If you don't want to install a plugin, you can import the component explicitly. It's available as a child of the named export Map.

NOTE

The following documentation refers to the plugin usage. Please make sure to adopt the component names, when you decided to use explicit component imports (e. g. <ol-map> becomes <Map.OlMap> etc.).

Usage

Plugin UsageExplicit Import
<ol-overlay><Map.OlOverlay>

Example 1: Overlay Content

Example below shows how to add custom content on to the map.

vue
<template>
  <button class="btn-default" type="button" @click="moveToEast">
    Move to the right
  </button>
  <ol-map style="height: 400px">
    <ol-view
      ref="view"
      :center="center"
      :zoom="zoom"
      :projection="projection"
    />

    <ol-tile-layer>
      <ol-source-osm />
    </ol-tile-layer>

    <ol-overlay
      :position="[item + 37.9 + offset, 40.1]"
      v-for="item in list"
      :key="item"
      :autoPan="true"
    >
      <div class="overlay-content">
        {{ item }}
      </div>
    </ol-overlay>
  </ol-map>
</template>

<script setup>
import { ref } from "vue";

const center = ref([40, 40]);
const projection = ref("EPSG:4326");
const zoom = ref(8);
const offset = ref(0);
const list = ref([2, 1, 3, 5, -1]);

function moveToEast() {
  offset.value += 0.1;
}
</script>

<style scoped>
.overlay-content {
  background: #efefef;
  box-shadow: 0 5px 10px rgb(2 2 2 / 20%);
  padding: 10px 20px;
  font-size: 16px;
  color: black;
}
</style>

Example 2: Measure Distance

vue
<template>
  <ol-map
    ref="mapRef"
    :loadTilesWhileAnimating="true"
    :loadTilesWhileInteracting="true"
    style="height: 400px"
  >
    <ol-view ref="view" :center="[-11000000, 4600000]" :zoom="15" />

    <ol-tile-layer>
      <ol-source-osm />
    </ol-tile-layer>

    <ol-overlay
      v-if="tooltipCoord"
      :position="tooltipCoord"
      :offset="[0, -15]"
      positioning="bottom-center"
      :stopEvent="false"
      :insertFirst="false"
    >
      <div class="tooltip tooltip-measure">
        {{ tooltipText }}
      </div>
    </ol-overlay>

    <ol-overlay
      v-if="helpTooltipCoord"
      :position="helpTooltipCoord"
      :offset="[0, 15]"
      positioning="top-center"
    >
      <div class="tooltip tooltip-info">
        {{ helpTooltipText }}
      </div>
    </ol-overlay>

    <ol-vector-layer>
      <ol-source-vector>
        <ol-interaction-draw
          :type="drawType"
          @drawend="drawend"
          @drawstart="drawstart"
        />
      </ol-source-vector>

      <ol-style>
        <ol-style-stroke color="#ffcc33" :width="2" />
        <ol-style-fill color="rgba(255, 255, 255, 0.2)" />
        <ol-style-circle :radius="7">
          <ol-style-fill color="#ffcc33" />
        </ol-style-circle>
      </ol-style>
    </ol-vector-layer>
  </ol-map>
</template>

<script setup lang="ts">
import { Feature, Map, type MapBrowserEvent } from "ol";
import { LineString } from "ol/geom";
import { type DrawEvent } from "ol/interaction/Draw";
import { onMounted, ref } from "vue";
import { getLength } from "ol/sphere";
import type { EventsKey } from "ol/events";
import type { Coordinate } from "ol/coordinate";
import { unByKey } from "ol/Observable";

const mapRef = ref<{ map: Map } | null>(null);
const drawType = ref("LineString");
const sketch = ref<Feature | null>(null);

const tooltipCoord = ref<Coordinate | null>(null);
const tooltipText = ref("");
const helpTooltipCoord = ref<Coordinate | null>(null);
const helpTooltipText = ref("");

let listener: EventsKey;
const continueLineMsg = "Click to continue drawing the line";

function drawstart(evt: DrawEvent) {
  sketch.value = evt.feature;
  const geom = sketch.value.getGeometry();
  if (geom instanceof LineString) {
    tooltipCoord.value = geom.getLastCoordinate();

    listener = geom.on("change", function (evt) {
      const geom = evt.target;
      if (geom instanceof LineString) {
        tooltipText.value = formatLength(geom);
        tooltipCoord.value = geom.getLastCoordinate();
      }
    });
  }
}

function drawend() {
  // remove drawn sketch
  sketch.value = null;
  // unset tooltip so that a new one can be created
  tooltipCoord.value = null;
  tooltipText.value = "";
  // cleanup listeners
  unByKey(listener);
}

function showHelpInfoOnPointermove(evt: MapBrowserEvent<PointerEvent>) {
  if (evt.dragging) {
    return;
  }
  let helpMsg = "Click to start drawing";
  const geom = sketch.value?.getGeometry();
  if (geom instanceof LineString) {
    helpMsg = continueLineMsg;
  }

  helpTooltipText.value = helpMsg;
  helpTooltipCoord.value = evt.coordinate;
}

function formatLength(line: LineString) {
  const length = getLength(line);
  let output = "";
  if (length > 100) {
    output = Math.round((length / 1000) * 100) / 100 + " " + "km";
  } else {
    output = Math.round(length * 100) / 100 + " " + "m";
  }
  return output;
}

onMounted(() => {
  mapRef.value?.map.on("pointermove", showHelpInfoOnPointermove);
  mapRef.value?.map.getViewport().addEventListener("mouseout", function () {
    helpTooltipCoord.value = null;
    helpTooltipText.value = "";
  });
});
</script>

<style scoped>
.tooltip {
  position: relative;
  background: rgba(0, 0, 0, 0.5);
  border-radius: 4px;
  color: white;
  padding: 4px 8px;
  opacity: 0.7;
  white-space: nowrap;
  font-size: 12px;
  cursor: default;
  user-select: none;
}
.tooltip-measure {
  font-weight: bold;
}
.tooltip-info {
  background-color: #ffcc33;
  color: black;
  border: 1px solid white;
}
.tooltip-measure:before {
  border-top: 6px solid rgba(0, 0, 0, 0.5);
  border-right: 6px solid transparent;
  border-left: 6px solid transparent;
  content: "";
  position: absolute;
  bottom: -6px;
  margin-left: -7px;
  left: 50%;
}
</style>

Properties

Props from OpenLayers

Properties are passed-trough from OpenLayers directly. Their types and default values can be checked-out in the official OpenLayers docs. Only some properties deviate caused by reserved keywords from Vue / HTML. This deviating props are described in the section below.

Deviating Properties

element

The property element from OpenLayers is filled by the vue components template ref. However, you are still able to override it and bind the component to another differing HTML element.

Events

You have access to all Events from the underlying source. Check out the official OpenLayers docs to see the available events tht will be fired.

html
<ol-overlay @error="handleEvent">My Overlay Text</ol-overlay>

Methods

You have access to all Methods from the underlying source. Check out the official OpenLayers docs to see the available methods.

To access the source, you can use a ref() as shown below:

vue
<template>
  <!-- ... -->
  <ol-overlay ref="overlayRef">My Overlay Text</ol-overlay>
  <!-- ... -->
</template>

<script setup lang="ts">
import { ref, onMounted } from "vue";
import type Overlay from "ol/Overlay";

const overlayRef = ref<{ overlay: Overlay }>(null);

onMounted(() => {
  const overlay: Overlay = overlayRef.value?.overlay;
  // call your method on `overlay`
});
</script>