OpenLayers
OpenLayers: high-performance open-source map library with vector tiles, projections, OGC services (WMS/WFS), drawing interactions, and GeoJSON/KML support
You are an expert in building map applications with OpenLayers, the open-source JavaScript library for interactive maps. ## Key Points - Dispose of the map with `map.setTarget(null)` in single-page applications when the component unmounts, to avoid memory leaks from orphaned event listeners and canvas elements. - Forgetting to import `ol/ol.css` causes controls, popups, and attribution to render without styling, making the map appear broken. ## Quick Example ```bash npm install ol ```
skilldb get maps-geolocation-services-skills/OpenLayersFull skill: 236 linesOpenLayers — Maps & Geolocation
You are an expert in building map applications with OpenLayers, the open-source JavaScript library for interactive maps.
Core Philosophy
OpenLayers is the power tool of web mapping. Where Leaflet optimizes for simplicity and Mapbox for design, OpenLayers optimizes for completeness and standards compliance. It supports every major geospatial format (GeoJSON, KML, GML, WMS, WFS, WCS, WMTS, MVT), arbitrary coordinate projections (not just Web Mercator), and fine-grained control over rendering, interactions, and layer composition. Choose OpenLayers when your application needs GIS-grade capabilities -- custom projections, OGC service integration, complex vector editing, or multi-CRS support.
Coordinate projections are the single most common source of bugs. OpenLayers defaults to EPSG:3857 (Web Mercator, measured in meters), but geographic data from APIs and files typically uses EPSG:4326 (longitude/latitude in degrees). Always use fromLonLat() to convert geographic coordinates to the map's projection and toLonLat() for the reverse. Passing raw latitude/longitude values directly to the View center places the map thousands of kilometers from the intended location.
OpenLayers is a library, not a platform. It does not include a tile server, a geocoding API, or a routing engine. You bring your own data sources and combine them using OpenLayers' layer system. This means OpenLayers works with any tile provider (OSM, Mapbox, custom WMS/WMTS servers) and any data format, but it also means you are responsible for assembling the full stack yourself.
Anti-Patterns
- Passing raw lat/lng values to
View.centerwithout projection conversion -- OpenLayers uses EPSG:3857 by default. Raw geographic coordinates place the map at the wrong location. Always usefromLonLat([lng, lat]). - Confusing coordinate order -- OpenLayers uses
[longitude, latitude](x, y) order forfromLonLat. Swapping to[latitude, longitude]places markers in the wrong hemisphere. The order matches GeoJSON convention, not the common "lat, lng" verbal convention. - Loading entire large GeoJSON datasets at once -- For datasets with thousands of features, load only the visible viewport using
strategy: bboxon the VectorSource. Loading everything upfront causes memory issues and rendering lag. - Not disposing the map in single-page applications -- Failing to call
map.setTarget(null)when the component unmounts leaks event listeners and canvas elements, degrading performance over time. - Forgetting to import
ol/ol.css-- Without the CSS, controls, popups, and attribution render without styling, making the map appear visually broken even though the JavaScript is working correctly.
Overview
OpenLayers is a full-featured, open-source map library that supports raster tiles, vector tiles, WMS, WFS, GeoJSON, KML, and other OGC standards. It provides fine-grained control over projections, coordinate systems, map interactions, and rendering. OpenLayers is commonly used in GIS applications, government/municipal mapping portals, and projects that need advanced cartographic features beyond what simpler libraries offer.
Setup & Configuration
Installation
npm install ol
Basic Map
import Map from "ol/Map";
import View from "ol/View";
import TileLayer from "ol/layer/Tile";
import OSM from "ol/source/OSM";
import "ol/ol.css";
const map = new Map({
target: "map",
layers: [
new TileLayer({
source: new OSM(),
}),
],
view: new View({
center: [0, 0], // EPSG:3857 (Web Mercator) coordinates
zoom: 2,
}),
});
Setting Center with Longitude/Latitude
OpenLayers uses EPSG:3857 (meters) by default. Use fromLonLat to convert from geographic coordinates.
import { fromLonLat, toLonLat } from "ol/proj";
const map = new Map({
target: "map",
layers: [new TileLayer({ source: new OSM() })],
view: new View({
center: fromLonLat([13.405, 52.52]), // Berlin
zoom: 12,
}),
});
Core Patterns
Vector Layer with GeoJSON
import VectorLayer from "ol/layer/Vector";
import VectorSource from "ol/source/Vector";
import GeoJSON from "ol/format/GeoJSON";
import { Style, Fill, Stroke, Circle } from "ol/style";
const geojsonSource = new VectorSource({
url: "/data/locations.geojson",
format: new GeoJSON(),
});
const vectorLayer = new VectorLayer({
source: geojsonSource,
style: new Style({
image: new Circle({
radius: 7,
fill: new Fill({ color: "#3399CC" }),
stroke: new Stroke({ color: "#fff", width: 2 }),
}),
stroke: new Stroke({ color: "#3399CC", width: 3 }),
fill: new Fill({ color: "rgba(51, 153, 204, 0.2)" }),
}),
});
map.addLayer(vectorLayer);
Click Interaction and Popups
import Overlay from "ol/Overlay";
const popupEl = document.getElementById("popup");
const popup = new Overlay({
element: popupEl,
positioning: "bottom-center",
offset: [0, -15],
autoPan: true,
});
map.addOverlay(popup);
map.on("click", (evt) => {
const feature = map.forEachFeatureAtPixel(evt.pixel, (f) => f);
if (feature) {
const coords = feature.getGeometry().getCoordinates();
popupEl.innerHTML = `<strong>${feature.get("name")}</strong>`;
popup.setPosition(coords);
} else {
popup.setPosition(undefined);
}
});
Drawing and Editing Features
import Draw from "ol/interaction/Draw";
import Modify from "ol/interaction/Modify";
const drawSource = new VectorSource();
const drawLayer = new VectorLayer({ source: drawSource });
map.addLayer(drawLayer);
// Enable polygon drawing
const drawInteraction = new Draw({
source: drawSource,
type: "Polygon",
});
map.addInteraction(drawInteraction);
drawInteraction.on("drawend", (event) => {
const geojsonFormat = new GeoJSON();
const geojson = geojsonFormat.writeFeature(event.feature, {
featureProjection: "EPSG:3857",
dataProjection: "EPSG:4326",
});
console.log("Drawn polygon:", geojson);
});
// Enable feature modification
const modifyInteraction = new Modify({ source: drawSource });
map.addInteraction(modifyInteraction);
WMS Layer (Web Map Service)
import TileWMS from "ol/source/TileWMS";
const wmsLayer = new TileLayer({
source: new TileWMS({
url: "https://example.com/geoserver/wms",
params: {
LAYERS: "my_workspace:my_layer",
TILED: true,
FORMAT: "image/png",
},
serverType: "geoserver",
}),
});
map.addLayer(wmsLayer);
Vector Tiles
import VectorTileLayer from "ol/layer/VectorTile";
import VectorTileSource from "ol/source/VectorTile";
import MVT from "ol/format/MVT";
const vtLayer = new VectorTileLayer({
source: new VectorTileSource({
format: new MVT(),
url: "https://tiles.example.com/{z}/{x}/{y}.pbf",
maxZoom: 14,
}),
});
map.addLayer(vtLayer);
Coordinate Projection
import { register } from "ol/proj/proj4";
import proj4 from "proj4";
// Register a custom projection (e.g., EPSG:25832 UTM zone 32N)
proj4.defs(
"EPSG:25832",
"+proj=utm +zone=32 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs"
);
register(proj4);
// Now you can use EPSG:25832 in OpenLayers
const view = new View({
projection: "EPSG:25832",
center: [500000, 5800000],
zoom: 10,
});
Best Practices
- Always use
fromLonLat()andtoLonLat()to convert between geographic coordinates (EPSG:4326) and the map's native projection (EPSG:3857) — passing raw lat/lng values directly toView.centerplaces the map at the wrong location. - When loading large GeoJSON datasets, use a
VectorSourcewith aurlloader and thestrategy: bboxloading strategy to fetch only features within the current viewport, rather than loading everything at once. - Dispose of the map with
map.setTarget(null)in single-page applications when the component unmounts, to avoid memory leaks from orphaned event listeners and canvas elements.
Common Pitfalls
- Confusing coordinate order: OpenLayers uses
[longitude, latitude](x, y) order forfromLonLat, but[latitude, longitude]is common in other contexts. Swapping them puts the map in the wrong hemisphere. - Forgetting to import
ol/ol.csscauses controls, popups, and attribution to render without styling, making the map appear broken.
Install this skill directly: skilldb add maps-geolocation-services-skills
Related Skills
Google Maps Platform
"Google Maps Platform: Maps JavaScript API, Places, Geocoding, Directions, Street View, React (@vis.gl/react-google-maps)"
HERE Maps
HERE Maps Platform: Maps API for JavaScript, geocoding, routing, isoline routing, fleet telematics, and platform data services
Leaflet
"Leaflet: open-source maps, markers, popups, layers, GeoJSON, plugins, React (react-leaflet), tile providers"
Mapbox
"Mapbox: interactive maps, GL JS, geocoding, directions, markers, layers, 3D terrain, React (react-map-gl)"
OpenStreetMap & Nominatim
OpenStreetMap tile usage and Nominatim geocoding API: forward/reverse geocoding, tile servers, Overpass API queries, and attribution requirements
Radar
"Radar: geofencing, geocoding, trip tracking, address validation, maps, fraud detection, JavaScript SDK"