ol-source-vector
ol-source-vector can be used together with ol-vector-layer to draw any vector data on the map.
Demo
Setup
Plugin usage
This component is part of the Sources
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 Sources
plugin
import { createApp } from "vue";
import App from "./App.vue";
import {
Map,
Layers,
Sources,
} from "vue3-openlayers";
const app = createApp(App);
// ...
app.use(Sources);
// ...
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 Sources
.
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 Usage | Explicit Import |
---|---|
<ol-source-vector> | <Sources.OlSourceVector> |
ol-feature
component (GeoJSON)
Static features with the help of ol-feature
, should be used only for tiny static layers.
<template>
<form>
<fieldset>
<label for="fill">Fill:</label>
<input type="color" id="fill" v-model="fill" />
<label for="stroke">Stroke:</label>
<input type="color" id="stroke" v-model="stroke" />
</fieldset>
<fieldset>
<label for="strokeWidth">StrokeWidth:</label>
<input
type="number"
id="strokeWidth"
step="1"
min="0"
v-model="strokeWidth"
/>
<label for="radius">Radius:</label>
<input type="number" id="radius" step="1" min="1" v-model="radius" />
</fieldset>
</form>
<button class="btn-default" @click="changeCoordinate" type="button">
change coordinates
</button>
<ol-map
:loadTilesWhileAnimating="true"
:loadTilesWhileInteracting="true"
style="height: 400px"
>
<ol-view
ref="view"
:center="center"
:zoom="zoom"
:projection="projection"
/>
<ol-tile-layer>
<ol-source-osm />
</ol-tile-layer>
<ol-vector-layer>
<ol-source-vector>
<ol-feature>
<ol-geom-point :coordinates="coordinate"></ol-geom-point>
<ol-style>
<ol-style-circle :radius="radius">
<ol-style-fill :color="fill"></ol-style-fill>
<ol-style-stroke
:color="stroke"
:width="strokeWidth"
></ol-style-stroke>
</ol-style-circle>
</ol-style>
</ol-feature>
</ol-source-vector>
</ol-vector-layer>
</ol-map>
</template>
<script setup lang="ts">
import { ref } from "vue";
const center = ref([40, 40]);
const projection = ref("EPSG:4326");
const zoom = ref(8);
const radius = ref(40);
const strokeWidth = ref(10);
const stroke = ref("#ff0000");
const fill = ref("#ffffff");
const coordinate = ref([40, 40]);
function changeCoordinate() {
coordinate.value = coordinate.value.map((a) => a + 0.01);
}
</script>
<style scoped>
button {
border: 1px solid black;
margin: 0.5rem 0;
padding: 0.5rem;
}
button:hover,
button:focus {
background-color: lightgray;
}
</style>
url
property
Load features simply by providing url value and format GeoJSON
<template>
<form>
<fieldset>
<label for="opacity">Layer Opacity</label>
<input
type="range"
id="opacity"
min="0"
max="1"
step="0.1"
v-model.number="opacity"
/>
<span class="description">{{ opacity }}</span>
</fieldset>
</form>
<ol-map
:loadTilesWhileAnimating="true"
:loadTilesWhileInteracting="true"
style="height: 400px"
>
<ol-view
ref="view"
:center="center"
:zoom="zoom"
:projection="projection"
:constrainRotation="16"
/>
<ol-vector-layer
background="#1a2b39"
ref="vectorSourceRef"
:opacity="opacity"
>
<ol-source-vector :url="url" :format="geoJson">
<ol-style :overrideStyleFunction="styleFn"></ol-style>
</ol-source-vector>
</ol-vector-layer>
<ol-interaction-dragbox
:condition="shiftKeyOnly"
@boxstart="log('boxstart', $event)"
@boxdrag="log('boxdrag', $event)"
@boxend="log('boxend', $event)"
@boxcancel="log('boxcancel', $event)"
></ol-interaction-dragbox>
</ol-map>
</template>
<script setup lang="ts">
import type { Feature } from "ol";
import { Fill, Style } from "ol/style";
import { shiftKeyOnly } from "ol/events/condition";
import { ref, inject } from "vue";
import type { DragBoxEvent } from "ol/interaction/DragBox";
const opacity = ref(1);
const center = ref([0, 0]);
const projection = ref("EPSG:4326");
const zoom = ref(0);
const url = ref("https://openlayers.org/data/vector/ecoregions.json");
const format = inject("ol-format");
const geoJson = new format.GeoJSON();
function styleFn(feature: Feature) {
return new Style({
fill: new Fill({
color: feature.get("COLOR_BIO") || "#eeeeee",
}),
});
}
function log(eventType: string, event: DragBoxEvent) {
console.log(eventType, event);
}
</script>
features
property
<template>
<label for="count">Marker:</label>
<input type="number" id="count" v-model.number="count" max="50000" />
<ol-map
:loadTilesWhileAnimating="true"
:loadTilesWhileInteracting="true"
renderer="webgl"
style="height: 400px"
>
<ol-view
ref="view"
:center="center"
:rotation="rotation"
:zoom="zoom"
:projection="projection"
/>
<ol-tile-layer>
<ol-source-osm />
</ol-tile-layer>
<ol-interaction-clusterselect @select="featureSelected" :pointRadius="20">
<ol-style>
<ol-style-stroke color="green" :width="5"></ol-style-stroke>
<ol-style-fill color="rgba(255,255,255,0.5)"></ol-style-fill>
<ol-style-icon :src="markerIcon" :scale="0.05"></ol-style-icon>
</ol-style>
</ol-interaction-clusterselect>
<ol-animated-clusterlayer :animationDuration="500" :distance="40">
<ol-source-vector
:features="geoJsonFeatures"
:format="geoJson"
@featuresloadstart="featuresloadstart"
@featuresloadend="featuresloadend"
@featuresloaderror="featuresloaderror"
/>
<ol-style :overrideStyleFunction="overrideStyleFunction">
<ol-style-stroke color="red" :width="2"></ol-style-stroke>
<ol-style-fill color="rgba(255,255,255,0.1)"></ol-style-fill>
<ol-style-circle :radius="20">
<ol-style-stroke
color="black"
:width="15"
:lineDash="[]"
lineCap="butt"
></ol-style-stroke>
<ol-style-fill color="black"></ol-style-fill>
</ol-style-circle>
<ol-style-text>
<ol-style-fill color="white"></ol-style-fill>
</ol-style-text>
</ol-style>
</ol-animated-clusterlayer>
</ol-map>
</template>
<script setup lang="ts">
import { computed, ref } from "vue";
import markerIcon from "@/assets/marker.png";
import { arrayWith50000Points } from "./points";
import { GeoJSON } from "ol/format";
import type { FeatureLike } from "ol/Feature";
import type { SelectEvent } from "ol/interaction/Select";
const center = ref([40, 40]);
const projection = ref("EPSG:4326");
const zoom = ref(5);
const rotation = ref(0);
const count = ref(1000);
const geoJson = new GeoJSON();
const geoJsonFeatures = computed(() => {
const features = Array.from({ length: count.value }, (_, i) => {
return {
type: "Feature",
properties: {},
geometry: {
type: "Point",
coordinates: arrayWith50000Points[i],
},
};
});
const providerFeatureCollection = {
type: "FeatureCollection",
features,
};
return geoJson.readFeatures(providerFeatureCollection);
});
const overrideStyleFunction = (feature: FeatureLike, style) => {
const clusteredFeatures = feature.get("features");
const size = clusteredFeatures.length;
const color = size > 20 ? "192,0,0" : size > 8 ? "255,128,0" : "0,128,0";
const radius = Math.max(8, Math.min(size, 20));
const dash = (2 * Math.PI * radius) / 6;
const calculatedDash = [0, dash, dash, dash, dash, dash, dash];
style.getImage().getStroke().setLineDash(dash);
style
.getImage()
.getStroke()
.setColor("rgba(" + color + ",0.5)");
style.getImage().getStroke().setLineDash(calculatedDash);
style
.getImage()
.getFill()
.setColor("rgba(" + color + ",1)");
style.getImage().setRadius(radius);
style.getText().setText(size.toString());
return style;
};
const featureSelected = (event: SelectEvent) => {
console.log(event);
};
function featuresloadstart() {
console.log("features load start");
}
function featuresloaderror() {
console.log("features load error");
}
function featuresloadend() {
console.log("features load end");
}
</script>
<style scoped>
input {
margin: 0.5rem;
padding: 0.25rem 0.5rem;
font-size: 1rem;
border: 1px solid black;
width: 100px;
}
</style>
urlFunction
Next example loads features from remote WFS service by viewport BBOX. With format and strategy you can define custom vector source format and loading strategy.
<template>
<ol-map
:loadTilesWhileAnimating="true"
:loadTilesWhileInteracting="true"
style="height: 400px"
>
<ol-view
ref="view"
:center="center"
:zoom="zoom"
:projection="projection"
/>
<ol-tile-layer>
<ol-source-osm />
</ol-tile-layer>
<ol-vector-layer>
<ol-source-vector
:url="urlFunction"
:strategy="bbox"
:format="GeoJSON"
:projection="projection"
>
</ol-source-vector>
<ol-style>
<ol-style-stroke color="red" :width="5"></ol-style-stroke>
</ol-style>
</ol-vector-layer>
</ol-map>
</template>
<script setup>
import { ref, inject } from "vue";
const center = ref([-8908887.277395891, 5381918.072437216]);
const projection = ref("EPSG:3857");
const zoom = ref(14);
const urlFunction = (extent, resolution, projection) => {
const proj = projection.getCode();
const url =
"https://ahocevar.com/geoserver/wfs?service=WFS&" +
"version=1.1.0&request=GetFeature&typename=osm:water_areas&" +
"outputFormat=application/json&srsname=" +
proj +
"&" +
"bbox=" +
extent.join(",") +
"," +
proj;
return url;
};
const strategy = inject("ol-loadingstrategy");
const bbox = strategy.bbox;
const format = inject("ol-format");
const GeoJSON = new format.GeoJSON();
</script>
TopoJSON
You can also use other Vector formats like TopoJSON.
<template>
<ol-map
:loadTilesWhileAnimating="true"
:loadTilesWhileInteracting="true"
style="height: 400px"
>
<ol-view
ref="view"
:center="center"
:zoom="zoom"
:projection="projection"
/>
<ol-tile-layer>
<ol-source-osm />
</ol-tile-layer>
<ol-vector-layer>
<ol-source-vector :url="url" :format="TopoJSON" :projection="projection">
</ol-source-vector>
<ol-style>
<ol-style-stroke color="red" :width="2"></ol-style-stroke>
</ol-style>
</ol-vector-layer>
</ol-map>
</template>
<script setup>
import { ref, inject } from "vue";
const center = ref([4.4764595, 50.5010789]);
const projection = ref("EPSG:4326");
const zoom = ref(7.5);
const url =
"https://raw.githubusercontent.com/bmesuere/belgium-topojson/master/belgium.json";
const format = inject("ol-format");
const TopoJSON = new format.TopoJSON({
// don't want to render the full world polygon (stored as 'land' layer),
// which repeats all countries
layers: ["arrondissements", "provinces"],
});
</script>
Performance hints
When rendering a large number of markers on a map using OpenLayers, there can be performance differences between requesting data from a URL as GeoJSON and passing the features directly through the source vector.
Data Transfer: When requesting data from a URL, the GeoJSON data is transferred over the network as needed, rather than loading the entire dataset upfront. This can be advantageous when dealing with large datasets, as it minimizes the initial data transfer and reduces the time required to load the markers onto the map.
Data Streaming: Requesting data from a URL allows for streaming the GeoJSON data in smaller chunks, which can significantly improve performance when rendering a large number of markers. The data can be fetched progressively and rendered incrementally, providing a smoother user experience and reducing the strain on system resources.
Memory Management: By requesting data from a URL, the GeoJSON data is loaded on-demand and can be efficiently managed by OpenLayers. When passing the features directly through the source vector, all the data is loaded into memory at once, which can cause performance issues and potentially lead to out-of-memory errors when dealing with extremely large datasets.
Data Filtering and Caching: Requesting data from a URL allows for server-side data filtering, which means you can request only the necessary markers based on certain criteria. Additionally, the server can cache the GeoJSON data, enabling faster subsequent requests and reducing the load on the server and the network.
Dynamic Data Updates: If your data is dynamic and frequently updated, requesting data from a URL provides a convenient way to fetch the latest information without having to reload the entire dataset. This can be particularly useful when working with real-time data or when updates occur frequently.
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
None.
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.
<ol-source-vector :url="url" @error="handleEvent" />
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:
<template>
<!-- ... -->
<ol-source-vector :url="url" ref="sourceRef" />
<!-- ... -->
</template>
<script setup lang="ts">
import { ref, onMounted } from "vue";
import type VectorSource from "ol/source/vector";
const sourceRef = ref<{ source: VectorSource }>(null);
onMounted(() => {
const source: VectorSource = sourceRef.value?.source;
// call your method on `source`
});
</script>