// ==UserScript==
// @name Showcase: selectable features in WME map
// @author Tom 'Glodenox' Puttemans
// @namespace http://www.tomputtemans.com/
// @version 0.3
// @description Shows how to add a vector feature layer where the features can receive events, just like Waze's features
// @include /^https:\/\/(www|beta)\.waze\.com\/(?!user\/)(.{2,6}\/)?editor.*$/
// ==/UserScript==
/* global W, OpenLayers */
async function onWmeReady() {
// Create vector layer
let mapLayer = new OpenLayers.Layer.Vector("feature_selection_showcase", {
styleMap: new OpenLayers.StyleMap({
'default': new OpenLayers.Style({
pointRadius: 50,
strokeColor: '#aaa',
strokeWidth: 4,
fillColor: '${fillColor}',
fontColor: '#fff',
fontWeight: 'bold',
fontSize: '50px',
label: '${text}'
}),
'highlight': new OpenLayers.Style({
strokeColor: '#aaa',
fillColor: '${highlightFillColor}'
}),
'select': new OpenLayers.Style({
strokeColor: '#fff',
fillColor: '${fillColor}'
}),
'highlightselected': new OpenLayers.Style({
strokeColor: '#fff',
fillColor: '${highlightFillColor}'
})
})
});
W.map.addLayer(mapLayer);
// Move the SVG root of the new layer into the layer RootContainer used by Waze.
let layerContainer = W.selectionManager.selectionMediator._rootContainerLayer;
layerContainer.layers.push(mapLayer);
layerContainer.collectRoots();
// Needed if you want to track the unselect event
let selectedFeature = null;
// Event handling example
// We need to filter out the highlight events of the other layers, hence the checks within the handlers
W.selectionManager.selectionMediator.on({
"map:selection:featureIn": (e) => e.layer == mapLayer && console.log("highlight", e), // e is the highlighted OpenLayers.Feature.Vector instance
"map:selection:featureOut": (e) => e.layer == mapLayer && console.log("unhighlight", e) // e is the no longer highlighted OpenLayers.Feature.Vector instance
});
W.selectionManager.events.on({
"selectionchanged": (e) => {
let matchedFeature = e.selected.find(feature => feature.layer == mapLayer);
if (matchedFeature) {
selectedFeature = matchedFeature;
console.log("select", selectedFeature);
} else if (selectedFeature) {
console.log("unselect", selectedFeature);
selectedFeature = null;
}
}
});
// Implementation note: W.selectionManager also has more specific "app:selection:featureselected" and "app:selection:featureunselected" events, but the unselection event doesn't trigger if you select another vector directly
// If you wish to use those events, e.layers contains the affected layers. Alternatively, you could implement the setSelected method in the attributes, which gets a boolean attribute indicating a select or unselect.
// Create a test location at the center of the map to showcase the highlighting and selection
let location = W.map.getCenter();
let vectorPoint = new OpenLayers.Geometry.Point(location.lon, location.lat);
let text = "foo";
// The repositoryObject contains methods required by the Waze logic
let vectorAttributes = {
text: text,
type: 'custom',
fillColor: "#555",
highlightFillColor: "#888",
index: `${text}`,
repositoryObject: {
isDeleted: () => false,
setSelected: (state) => null, // You could implement this method to get select/unselect callbacks
isNew: () => false,
getType: () => null,
getID: () => -1
}
};
let testVector = new OpenLayers.Feature.Vector(vectorPoint, vectorAttributes);
// Not needed by Waze, but some common scripts rely on the presence of a model field
testVector.model = vectorAttributes;
mapLayer.addFeatures([ testVector ]);
}
// Below you just find the usual bootstrap code, nothing needs to be changed there
function onWmeInitialized() {
if (W.userscripts?.state?.isReady) {
console.log('W is ready and in "wme-ready" state. Proceeding with initialization.');
onWmeReady();
} else {
console.log('W is ready, but not in "wme-ready" state. Adding event listener.');
document.addEventListener('wme-ready', onWmeReady, { once: true });
}
}
function bootstrap() {
if (!W) {
console.log('W is not available. Adding event listener.');
document.addEventListener('wme-initialized', onWmeInitialized, { once: true });
} else {
onWmeInitialized();
}
}
bootstrap();