WME version v2.297-9-g22c5cc34e
The Waze Map Editor (WME) JavaScript SDK provides a seamless way to execute community userscripts over the WME.
Using the SDK allows scripts to query the WME data model, interact with the map, read application settings, apply changes to features, register to events and more.
This SDK provides a structured and convenient way for userscripts to interact with and extend the functionality of the Waze Map Editor (WME). It offers a collection of modules and methods that streamline common tasks and facilitate seamless integration of custom scripts within the WME environment.
The following example shows how to initialize the SDK in your script:
// the sdk init function will be available after the SDK is initialized
window.SDK_INITIALIZED.then(initScript);
function initScript() {
// initialize the sdk with your script id and script name
const wmeSDK = getWmeSdk(
{scriptId: "your-userscript-id", scriptName: "Script Display Name"});
// Start using the SDK
const mapCenter = wmeSDK.Map.getMapCenter();
const topCountry = wmeSDK.DataModel.Countries.getTopCountry();
// query the WME data model
const mySegment = wmeSDK.DataModel.Segments.getById({segmentId: 123});
if (mySegment.isAtoB) {
// do something
}
// add new features
wmeSDK.DataModel.Venues.addVenue({category, geometry});
// save edits
wmeSDK.Editing.save().then(() => {
// edits saved
});
// register to events
wmeSDK.Events.once({ eventName: "wme-ready" }).then(() => { ... });
wmeSDK.Events.on({
eventName: "wme-map-move",
eventHandler: () => { ... }
});
wmeSDK.Events.on({
eventName: "wme-map-data-loaded",
eventHandler: () => { ... }
});
wmeSDK.Events.on({
eventName: "wme-selection-changed",
eventHandler: () => { ... }
});
}
Typescript type definitions for the SDK are available here.
You can install them with npm
:
npm install --save-dev https://web-assets.waze.com/wme_sdk_docs/production/latest/wme-sdk-typings.tgz
After that, you can import SDK types from the package:
import { KeyboardShortcut, WmeSDK } from "wme-sdk-typings";
if (!window.getWmeSdk) {
throw new Error("SDK not available");
}
const sdk: WmeSDK = window.getWmeSdk({ scriptId: "test", scriptName: "test" });
const shortcut: KeyboardShortcut = {
callback: () => {
console.log("Hello world!");
},
description: "test shortcut",
shortcutId: "test-shortcut",
shortcutKeys: "A+l",
};
sdk.Shortcuts.createShortcut(shortcut);
The SDK is split into the following modules according to functionality:
The SDK also adds a set of custom events triggered by the WME at various points
during its lifecycle and user interactions.
These events enable scripts to react to specific actions and states within the
WME application.
There are multiple types of events: global events, data model events & layer events.
Event | Description |
---|---|
wme-initialized |
Dispatched when WME has initialized the window.W global object and its internals, and the UI has been rendered. Note that at this point, map data has not yet been fetched. |
wme-logged-in |
Dispatched after the wme-initialized event when WME fetches the user info of the currently logged-in user, or after the user logs in using the login form. |
wme-logged-out |
Dispatched when the user logs out of WME. |
wme-map-data-loaded |
Dispatched after the wme-initialized event whenever WME fetches map data from the server (e.g. when the user scrolls the map, changes the zoom level, or presses the refresh button). This is similar to the mergeend event triggered on W.model . |
wme-ready |
Dispatched only once, after the wme-initialized , wme-logged-in , and wme-map-data-loaded events have been dispatched. |
wme-selection-changed |
Dispatched when some entity gets selected or unselected on the map. |
wme-feature-editor-opened |
Dispatched when the feature editor is opened in the side panel with a new feature. Note: Essentially, this event means the feature editor is ready in the side panel. While at this point you'll usually see the feature editor on the screen, it's not a guarantee. There might be a slight delay before the feature editor is actually visible. |
wme-map-zoom-changed |
Dispatched when the map is zoomed in or out. |
wme-map-layer-added |
Dispatched when the new layer is added to the map. |
wme-map-layer-changed |
Dispatched when a layer on the map changes visibility or name. |
wme-map-layer-removed |
Dispatched when a layer is removed from the map. |
wme-map-mouse-down |
Dispatched when the mouse button is pressed while the pointer is inside the map. |
wme-map-mouse-move |
Dispatched when the mouse is moved over the map. Note that this event is continuously fired when moving the mouse. |
wme-map-mouse-up |
Dispatched when the mouse button is released while the pointer is inside the map. |
wme-map-move |
Dispatched when the map is panned. Note that this event is continuously fired during pan |
wme-map-move-end |
Dispatched when a map move is complete. |
wme-user-settings-changed |
Dispatched when WME user settings have changed. |
wme-save-finished |
Dispatched when the save attempt has been done by the user. The event is dispatched for both successful and failed save. The event detail contains a success boolean parameter which is true for the successful save and false otherwise. |
wme-layer-checkbox-toggled |
Dispatched when the custom Map layers checkbox, registered by the script, was toggled. The event detail contains the name of the checkbox, and checked parameter which is true if the checkbox became checked and false otherwise. |
wme-editing-house-numbers |
[DEPRECATED - Use wme-map-house-number-marker-added instead] Dispatched when the user starts or stops editing house numbers. |
wme-map-house-number-marker-added |
Dispatched when the when the user clicks and adds a house number marker to the map. |
wme-house-number-added |
Dispatched when a house number is added. |
wme-house-number-deleted |
Dispatched when a house number is deleted. |
wme-house-number-moved |
Dispatched when a house number is moved. |
wme-house-number-updated |
Dispatched when a house number is updated. |
wme-after-edit |
Dispatched after user performs an create/edit/delete of an object. |
wme-after-redo-clear |
Dispatched after user performs any new edit while being able to redo previous edits. |
wme-after-undo |
Dispatched after WME user performs an undo. |
wme-no-edits |
Dispatched after WME user performs the last undo or save indicating no edits left to be able to undo or save. |
wme-save-mode-changed |
Dispatched when the current state of the save button is changed. The event detail contains a saveMode parameter with current save mode. |
wme-update-request-panel-opened |
Dispatched when WME user clicks on the map update request or the map update request map marker. |
Registering to these events is done via the sdk.Events
module:
sdk.Events.once({ eventName: "wme-ready" }).then(() => {
console.log("WME is initialized, user is logged in and map data loaded");
});
sdk.Events.on({
eventName: "wme-map-data-loaded",
eventHandler: () => {
console.log("new data loaded");
},
});
sdk.Events.on({
eventName: "wme-map-zoom-changed",
eventHandler: () => {
sdk.Map.getZoomLevel();
},
});
sdk.Events.on({
eventName: "wme-user-settings-changed",
eventHandler: () => {
sdk.Settings.getUserSettings().isImperial;
},
});
Event | Description |
---|---|
wme-data-model-objects-added |
Dispatched when objects have been added to a tracked model. |
wme-data-model-objects-changed |
Dispatched when the attributes of an object or objects in a tracked model have been changed. |
wme-data-model-objects-removed |
Dispatched when objects have been removed from a tracked model. |
wme-data-model-objects-saved |
Dispatched when objects in a tracked data model have been saved to the server. |
wme-data-model-object-changed-id |
Dispatched when an object ID in a tracked data model have been changed. |
wme-data-model-object-state-deleted |
Dispatched when objects have been marked as deleted but are not removed from the tracked data model. |
To listen to model events, they need to be activated first. The activation is done for each model individually via the sdk.Events
module:
sdk.Events.trackDataModelEvents({ dataModelName: "venues" });
After the events are activated for the model they can be handled in the same way as the global events:
sdk.Events.on({
eventName: "wme-data-model-objects-added",
eventHandler: ({dataModelName, objectIds}) => { ... },
});
Each event will include a payload of the data model name which triggered the event and an array of the object ids affected.
Once model events are not needed anymore they should be deactivated:
sdk.Events.stopDataModelEventsTracking({ dataModelName: "venues" });
Event | Description |
---|---|
wme-layer-visibility-changed |
Dispatched when a tracked layer visibility has been changed. |
wme-layer-feature-clicked |
Dispatched when a feature in a tracked layer has been clicked. |
To listen to a layer events they need to be activated first. The activation is done for each layer individually via the sdk.Events
module:
sdk.Events.trackLayerEvents({ layerName: "my_custom_layer" });
After the events are activated for the layer they can be handled in the same way as the global events:
sdk.Events.on({
eventName: "wme-layer-visibility-changed",
eventHandler: () => { ... },
});
Once layer events are not needed anymore they should be deactivated:
sdk.Events.stopLayerEventsTracking({ layerName: "my_custom_layer" });
The SDK allows you to work with atomic geometries: LineString
, Polygon
, and Point
. These are the basic building blocks for representing geographic features.
For more complex geometries, such as MultiLineStrings (a collection of LineStrings), MultiPolygons (a collection of Polygons), or GeometryCollections (a collection of different geometry types), you'll need to process them before using them with the SDK. A good way to do this is with the Turf.js library, which has a flatten
helper that breaks complex geometries into atomic ones. After installing Turf.js, you can use it like this:
import { flatten } from "@turf/flatten";
// Example feature with MultiPolygon geometry
const multiPolygonFeature = {
type: "Feature",
properties: {
foo: "bar",
},
geometry: {
type: "MultiPolygon",
coordinates: [
[
[
[0, 0],
[1, 0],
[1, 1],
[0, 1],
[0, 0],
],
],
[
[
[2, 2],
[3, 2],
[3, 3],
[2, 3],
[2, 2],
],
],
],
},
};
const flattened = flatten(multiPolygonFeature); // you can also use flatten(multiPolygonFeature.geometry)
const featuresToAdd = flattened.features.map((feature, index) => ({
geometry: feature.geometry,
id: `complex-geometry-${index}`, // generate a unique ID
properties: feature.properties,
type: feature.type,
}));
sdk.Map.addFeaturesToLayer({
features: featuresToAdd,
layerName: "your_layer_name",
});
If you're migrating your script from using internal WME methods directly to the new WME SDK, the tables below list commonly used internal methods and their corresponding methods in the SDK.
Usages of global W
variable
Pre-SDK usage | SDK method |
---|---|
W.accelerators |
Use methods in Shortcuts module |
W.app.getAppRegionCode |
Use Settings.getRegionCode |
W.Config.venues.categories \ W.Config.venues.subcategories \ W.model.categoryBrands |
DataModel.Venues contains methods for getting venue categories as well as category brands |
W.Config.user_profile.url |
Use DataModel.Users.getUserProfileLink |
W.controller.reloadData |
Use DataModel.refreshData |
W.controller.save |
Use Editing.save |
W.loginManager.user |
Use State.userInfo to get the logged in user information |
W.loginManager.isLoggedIn |
State.userInfo will be null if a user is not logged in |
W.map.* |
The Map module contains methods to interact and query with the map, such as center, zoom, adding/removing layers, drawing on the map and more. See the Map module documentation for more information |
W.map.events |
The events wme-map-zoom-changed , wme-map-move& wme-map-move-end` are triggered by the SDK |
W.model.*.on \ W.model.*.off |
Use the Events module which allows tracking changes in a data model |
W.model.*.getObjectArray \ W.model.*.objects |
Use DataModel.*.getAll |
W.model.*.getObjectById |
Use DataModel.*.getById |
W.model.getTopCountry |
Use DataModel.Countries.getTopCountry |
W.model.getTopState |
Use DataModel.States.getTopState |
W.model.isImperial |
Use Settings.getUserSettings().isImperal |
W.model.isLeftHand |
Use isLeftHandTraffic attribute on the Country SDK interface |
W.model.getTurnGraph |
Use methods in DataModel.Turns |
W.prefs.get \ W.prefs.set \ W.prefs.attributes |
Use Settings.getUserSettings \ Settings.setUserSettings |
W.prefs.on("change") |
Register to the event wme-user-settings-changed triggered by the SDK |
W.selectionManager.getSelectedFeatures \ W.selectionManager.setSelectedModels |
The Editing module contains methods to get and set selected features. See the Editing module documentation for more information |
W.selectionManager.events |
The event wme-selection-changed is triggered by the SDK when selection has changed |
W.userscripts.state |
Use State included on the SDK instance |
W.userscripts.registerSidebarTab |
Use Sidebar.registerScriptTab |
W.userscripts.waitForElementConnected |
Not required as the SDK method Sidebar.registerScriptTab returns a Promise which resolves when the tabLabel & tabPane elements are available in the DOM |
Usages of Waze/Action/*'
In general, the SDK will provide abstractions over actions and expose methods to update data models via their relevant modules.
Pre-SDK usage | SDK method |
---|---|
AddAlternateStreet |
Use DataModel.Segments.addAlternateStreet |
AddLandmark |
Use DataModel.Venues.addVenue |
AddOrGetCity |
Use DataModel.Cities.getCity \ DataModel.Cities.addCity |
AddOrGetStreet |
Use DataModel.Streets.getStreet \ DataModel.Streets.addStreet |
AddSegment |
Use DataModel.Segments.addSegment |
CreateRoundabout |
Use DataModel.Segments.createRoundabout |
DeleteSegment |
Use DataModel.Segments.deleteSegment |
MergeSegments |
Use DataModel.Segments.mergeSegments |
UpdateFeatureAddress |
Use DataModel.Segments.updateAddress \ DataModel.Venues.updateAddress |
Usages of OpenLayers
or OL
Pre-SDK usage | SDK method |
---|---|
OpenLayers.Control.DrawFeature |
Use Map.draw* methods |
OpenLayers.Geometry.LineString \ OpenLayers.Geometry.Point \ OpenLayers.Geometry.Polygon |
Use Map.addFeatureToLayer to add features or Map.draw* to draw |
OpenLayers.Layer.Vector \ OpenLayers.Feature.Vector |
Use Map.addLayer & Map.addFeatureToLayer |
OpenLayers.Style \ OpenLayers.StyleMap \ OpenLayers.Rule |
Use Map.addLayer with style object |
OpenLayers.LonLat |
SDK works with plain JS objects - { lon: number; lat: number; } |
Usages of I18n
Pre-SDK usage | SDK method |
---|---|
I18n.currentLocale() \ I18n.locale |
Settings.getLocale |
As this SDK is in active development, we will update this guide as more functionality is added. As always, your feedback is encouraged. Please report any issues encountered to help improve the SDK.
Sometimes, when initializing the SDK, you can get the following error:
window.SDK_INITIALIZED.then(initScript); // <- "Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'then')"
There might be a couple of reasons why you are getting it.
Your code may be executed before the DOMContentLoaded
event is dispatched.
To make sure that it's not the case, check your @run-at
setting.
It should be set to anything that guarantees the script execution after the DOMContentLoaded
was dispatched.
If you are using a @run-at
setting which injects your code before the DOMContentLoaded
dispatch, or if your environment does not support @run-at
or similar setting, you will need to wrap your initialization code as following:
document.addEventListener("DOMContentLoaded", () => {
window.SDK_INITIALIZED.then(initScript); // <- works fine!
});
You may be using a @grant
header.
If you're using Tampermonkey or a similar tool, it allows you to include some useful functions with @grant
.
However, if you add @grant
with anything other than none
, it will provide your script with a window
object, which is different from the one which the host uses.
So, in case you want to add a function with @grant
, make sure you add // @grant unsafeWindow
to your script and use unsafeWindow
instead of window
.
...
// @grant none
...
window.SDK_INITIALIZED.then(initScript); // <- works fine!
...
// @grant GM_setClipboard
...
window.SDK_INITIALIZED.then(initScript); // <- fails with "Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'then')"
...
// @grant GM_setClipboard
// @grant unsafeWindow
...
unsafeWindow.SDK_INITIALIZED.then(initScript); // <- works fine!
This SDK is designed to facilitate integration with the WME for script writers. However, Waze does not guarantee the functionality, accuracy, or reliability of any scripts developed using this SDK. Users are solely responsible for the scripts they create and their performance.