Vue Map - OpenStreetMap Provider
Set provider to "osm" to use the OpenStreetMap (OSM) provider. This provider uses the OpenLayers library to display maps.
DevExtreme does not include map services for the OSM provider. Specify self-hosted or third-party services in the following providerConfig properties:
- tileServer: Tile source settings
- calculateLocation: A function that converts addresses to coordinates
- calculateRoute: A function that calculates route geometry
The following code snippet configures a Map that uses the OSM provider:
jQuery
$(() => {
$("#mapContainer").dxMap({
center: { lat: 40.749825, lng: -73.987963 },
zoom: 10,
provider: "osm",
providerConfig: {
tileServer: {
url: "https://tiles.example.com/{z}/{x}/{y}.png",
attribution: "© <a href='https://www.openstreetmap.org/copyright'>OpenStreetMap contributors</a>"
}
}
});
});Angular
<dx-map
[zoom]="10"
provider="osm">
<dxo-map-center
[lat]="40.749825"
[lng]="-73.987963">
</dxo-map-center>
<dxo-map-provider-config
[tileServer]="tileServer">
</dxo-map-provider-config>
</dx-map>
import { Component } from '@angular/core';
import { DxMapModule } from 'devextreme-angular';
@Component({
// ...
standalone: true,
imports: [DxMapModule]
})
export class AppComponent {
tileServer = {
url: 'https://tiles.example.com/{z}/{x}/{y}.png',
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap contributors</a>'
};
}Vue
<template>
<DxMap
:zoom="10"
provider="osm">
<DxCenter
:lat="40.749825"
:lng="-73.987963"
/>
<DxProviderConfig
:tile-server="tileServer"
/>
</DxMap>
</template>
<script setup>
import { DxMap, DxCenter, DxProviderConfig } from 'devextreme-vue/map';
const tileServer = {
url: 'https://tiles.example.com/{z}/{x}/{y}.png',
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap contributors</a>'
};
</script>React
import { Map, ProviderConfig } from 'devextreme-react/map';
const centerCoordinates = { lat: 40.749825, lng: -73.987963 };
const tileServer = {
url: 'https://tiles.example.com/{z}/{x}/{y}.png',
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap contributors</a>'
};
export default function App() {
return (
<Map
defaultZoom={10}
defaultCenter={centerCoordinates}
provider="osm">
<ProviderConfig
tileServer={tileServer}
/>
</Map>
);
}Configure OpenLayers
DevExtreme neither ships with OpenLayers nor specifies the library as a dependency. This section explains how to add OpenLayers resources to your application.
Modular Applications
Install the ol package:
npm install ol
The devextreme/ui/map/openlayers module registers OpenLayers for the Map. Import this module and the OpenLayers stylesheet (ol/ol.css) before Map initialization:
jQuery
import 'devextreme/ui/map/openlayers'; import 'ol/ol.css';
Angular
import 'devextreme/ui/map/openlayers'; // ...
"styles": [
// ...
"node_modules/ol/ol.css"
]Vue
<script setup> import 'devextreme/ui/map/openlayers'; import 'ol/ol.css'; // ... </script>
React
import 'devextreme/ui/map/openlayers'; import 'ol/ol.css'; // ...
ol package contains only ES modules and does not include a CommonJS build.Non-Modular Applications
Load the OpenLayers script (ol.js) and stylesheet (ol.css) before Map initialization:
<head>
<!-- ... -->
<link rel="stylesheet" href="/lib/ol/ol.css">
<script src="/lib/ol/ol.js"></script>
</head>Refer to the OpenLayers website for download options: OpenLayers - Download.
Configure a Tile Server
Assign an OsmTileServerConfig object to providerConfig.tileServer to configure a tile source. Specify the following fields in this object:
- url: A tile URL template
- attribution: Copyright notices that your tile and data providers require
- subdomains (optional): Values for the
{s}placeholder in url - maxZoom (optional): The highest zoom level that your tile source supports
The following code snippet configures a tile source that uses two subdomains:
const tileServer = {
url: 'https://{s}.tiles.example.com/{z}/{x}/{y}.png',
subdomains: ['a', 'b'],
maxZoom: 18,
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap contributors</a>'
};Assign a function to tileServer to display different tiles for each map type:
const tileSources = {
roadmap: {
url: 'https://tiles.example.com/roadmap/{z}/{x}/{y}.png',
attribution: 'Roadmap tile attribution'
},
satellite: {
url: 'https://tiles.example.com/satellite/{z}/{x}/{y}.jpg',
attribution: 'Satellite tile attribution'
},
hybrid: {
url: 'https://tiles.example.com/hybrid/{z}/{x}/{y}.jpg',
attribution: 'Hybrid tile attribution'
}
};
const tileServer = (type) => tileSources[type];The Map displays one tile source at a time. A "hybrid" tile source must include both satellite imagery and labels.
Review the terms of use for your tile service. For instance, comply with the Tile Usage Policy if you use public OpenStreetMap tile servers.
Calculate Locations
The OSM provider uses the providerConfig.calculateLocation function to convert addresses and place names to coordinates. Implement this function to request coordinates from a geocoding service:
async function calculateLocation(query) {
const response = await fetch(`/api/geocode?q=${encodeURIComponent(query)}`);
if (response.status === 404) {
return undefined;
}
if (!response.ok) {
throw new Error(`Geocoding request failed: ${response.status}`);
}
const { lat, lng } = await response.json();
return { lat, lng };
}This example requests coordinates from an application endpoint (/api/geocode). The endpoint returns a { lat, lng } object for a resolved address and 404 for an unknown address. Adapt request/response formats to your geocoding service.
If the calculateLocation promise rejects or resolves with undefined, the Map uses {lat: 0, lng: 0} for center and markers. The component also does not display routes that contain unresolved addresses.
Send Requests to a Routing Service
calculateRoute receives a CalculateOsmRouteInfo object. The object contains route waypoints (CalculateOsmRouteInfo.locations) and the transportation mode. Send these values to a routing service and return the route geometry received from the service:
async function calculateRoute({ locations, mode }) {
const response = await fetch('/api/route', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ locations, mode })
});
if (!response.ok) {
throw new Error(`Routing request failed: ${response.status}`);
}
const route = await response.json(); // A GeoJSON FeatureCollection
return route.features[0].geometry; // A GeoJSON LineString object
}This example sends route parameters to an application endpoint (/api/route) that returns a GeoJSON FeatureCollection. Adapt request and response formats to your routing service.
Return Route Geometry
calculateRoute can return route geometry in one of the following OsmRouteResult formats:
// An array of coordinate pairs (latitude then longitude)
const coordinatePairs = [
[40.749825, -73.987963],
[40.753182, -73.982253]
];
// A GeoJSON LineString object (longitude then latitude)
const lineString = {
type: 'LineString',
coordinates: [
[-73.987963, 40.749825],
[-73.982253, 40.753182]
]
};Use Predefined Coordinates
calculateRoute does not have to request route geometry from a routing service. It can return predefined coordinates instead. The function receives only route waypoints and the transportation mode, so use waypoints to identify predefined routes:
const predefinedRoutes = {
// Key: route waypoints. Value: route geometry (latitude then longitude).
'40.749825,-73.987963;40.753182,-73.982253': [
[40.749825, -73.987963],
[40.753634, -73.985198],
[40.752408, -73.982291],
[40.753182, -73.982253]
]
};
async function calculateRoute({ locations }) {
const key = locations.map(({ lat, lng }) => `${lat},${lng}`).join(';');
const coordinates = predefinedRoutes[key];
if (!coordinates) {
throw new Error(`No predefined route for ${key}`);
}
return coordinates;
}Handle Failed Routes
If calculateRoute fails, the Map does not display a route. The addRoute(routeOptions) promise resolves with undefined:
const route = await mapInstance.addRoute(routeOptions);
if (!route) {
// Notify users that the route is unavailable
}You can assign the following function to calculateRoute to draw straight lines between route waypoints if a routing request fails:
async function calculateRouteWithFallback(params) {
try {
return await calculateRoute(params);
} catch {
return params.locations.map(({ lat, lng }) => [lat, lng]);
}
}Security
The Map renders OsmTileServerConfig.attribution and markers[].tooltip.text values as HTML. If these values come from untrusted sources, encode tooltip.text and sanitize attribution (retain notices and links that your tile and data providers require). Refer to the following help topics for more information:
- Potentially Vulnerable API - OsmTileServerConfig.attribution
- Potentially Vulnerable API - markers[].tooltip.text
The browser requests tiles and runs calculateLocation and calculateRoute. Users can read API keys in tile URLs and service requests. Use keys that your service allows in client applications (for instance, keys restricted to your domain). If keys must stay confidential, route requests to the map service through your server.
Add map service origins to your Content Security Policy as described in the following help topic: CSP Directives for Map Integrations.
Keyboard Navigation
You can use the following keys to interact with the Map when provider is "osm":
| Key | Action |
|---|---|
| Tab / Shift + Tab | Moves focus to the map and between interactive markers (markers that have a tooltip or an onClick handler). Markers outside the visible area do not receive focus. |
| Enter / Space | Displays or hides the focused marker tooltip and calls the marker's onClick handler. |
| Esc | Hides the most recently opened tooltip. |
While the map has focus, you can also use arrow keys to pan the map and the + and - keys to zoom in and out. OpenLayers implements these shortcuts in KeyboardPan and KeyboardZoom.
Screen Reader Support
Interactive markers use a generic accessible name ("Map marker"). Specify a descriptive name for each marker in the onMarkerAdded handler:
function onMarkerAdded(e) {
// e.originalMarker is an OpenLayers Overlay
e.originalMarker.getElement().setAttribute('aria-label', 'Empire State Building');
}If marker tooltips contain plain text, you can assign this text to each marker's aria-label attribute to specify descriptive names:
function onMarkerAdded(e) {
const { tooltip } = e.options;
const text = typeof tooltip === 'string' ? tooltip : tooltip?.text;
if (text) {
e.originalMarker.getElement().setAttribute('aria-label', text);
}
}The Map Cannot Find OpenLayers
The Map logs an error if the component cannot find a compatible OpenLayers version. Ensure that your application loads OpenLayers before Map initialization. Modular applications must import the devextreme/ui/map/openlayers module.
A Package Manager or Bundler Cannot Resolve OpenLayers
The devextreme/ui/map/openlayers module imports the ol package, but devextreme does not declare ol as a dependency. Yarn Plug'n'Play does not allow packages to import undeclared dependencies. Declare ol in your application dependencies and add a packageExtensions entry to .yarnrc.yml:
packageExtensions:
"devextreme@*":
dependencies:
ol: "10.10.0"Specify the ol version that your application uses.
The Map Does Not Display Tiles
Check the following:
- providerConfig.tileServer specifies a tile URL for the current map type. A tileServer function must return a tile source synchronously for each map type.
- Tile requests include the credentials that your tile service requires.
- Your tile server allows cross-origin requests (CORS). OpenLayers sends CORS requests for tile images.
- Tile URLs use HTTPS if your application uses HTTPS.
- Your Content Security Policy allows tile server origins (the
img-srcdirective).
Attribution or Zoom Buttons Are Missing
Check the following:
- The tile source specifies attribution. A URL template string cannot include attribution. Use an OsmTileServerConfig object instead.
- controls is
true(required for zoom buttons). - Your application loads the OpenLayers stylesheet (
ol.css).
OpenLayers displays attribution in a collapsed control by default. Users can click the i button to expand this control.
Markers or the Map Center Appear at Zero Coordinates
The Map uses {lat: 0, lng: 0} if it cannot convert an address to coordinates. Check the following:
- Your application specifies providerConfig.calculateLocation.
- The calculateLocation promise resolves with numeric lat and lng values.
- Your geocoding service finds the address.
The Map Does Not Display Routes
Check the following:
- Your application specifies providerConfig.calculateRoute.
- Each route contains two or more locations. Address strings require calculateLocation.
- The calculateRoute promise resolves with a valid OsmRouteResult value.
Routes appear in unexpected locations if the coordinate order is incorrect. Coordinate pairs list latitude then longitude. GeoJSON LineString positions list longitude then latitude.