Drawing our lines - WazeDev/WazeDev.github.io GitHub Wiki

At this point we have a script that reads & loads some user information into a tab, and saves & loads the checked state of our Enabled checkbox. Now we need to do something when the script is enabled, either when loading WME or when the user toggles the Enabled setting.

When enabled:

  • Attach selectionchange handler
  • When we have items selected
    • When a segment is selected, retrieve the geometry
    • Draw the line according to the segment geometry retrieved
  • When no items are selected
    • Clear the layer we were drawing on

When disabled:

  • Detach the event handler
  • Clear the layer we were drawing on

In order to get started we will need to declare our layer variable at the top of the script.

var wazedevSelectedLayer;

Next, at the beginning of our init method we will instantiate our layer. This is done with a new OpenLayers.Layer.Vector object. To this constructor we will pass the layer name and set the properties. The properties that we are most concerned about are giving it a unique name (I typically just added two underscores to the same name I passed as the first parameter) and a styleMap if necessary. For this script we are not going to use a styleMap for the layer, instead we will define a style and pass it to the Openlayers.Feature.Vector that will be drawn. Below will be our layer definition.

Note: You will see many scripts also define a property for displayInLayerSwitcher which used to be how you would add a layer to the layer menu. The way the layer menu is built has changed and this property no longer has any affect, so it can be omitted.

wazedevSelectedLayer = new OL.Layer.Vector("wazedevSelectedLayer",{uniqueName: "__wazedevSelectedLayer"});
        W.map.addLayer(wazedevSelectedLayer);
        wazedevSelectedLayer.setVisibility(true);

So now we have a layer that we can draw on when the user selects a segment or a Place. Now we need to set up our event handler for when something is selected/de-selected. To do this we use the following:

 W.selectionManager.events.register("selectionchanged", null, callback);

Likewise, to unregister the event:

 W.selectionManager.events.unregister("selectionchanged", null, callback);

We will add a check in our initializeSettings function, after loading out settings from localStorage, to check if the script is Enabled. If it is, we will set up our selectionchanged handler.

if(settings.Enabled)
    W.selectionManager.events.register("selectionchanged", null, drawSelection);

Next we need to modify our Enabled checkbox's changed handler to add and remove this event handler. When the script is disabled it will also need to clear the layer to make sure anything that has been drawn is removed. We will add the following:

if(this.checked)
    W.selectionManager.events.register("selectionchanged", null, drawSelection);
else
{
    W.selectionManager.events.unregister("selectionchanged", null, drawSelection);
    wazedevSelectedLayer.removeAllFeatures();
}

Our initializeSettings method should now look like this:

function initializeSettings()
    {
        loadSettings();
        setChecked('wazedevEnabled', settings.Enabled);

        $('#wazedevUsername').text(W.loginManager.user.userName);
        $('#wazedevRank').text(W.loginManager.user.rank + 1);
        $('#wazedevTotalEdits').text(W.loginManager.user.totalEdits);
        $('#wazedevTotalPoints').text(W.loginManager.user.totalPoints);
        $('#wazedevAM').text(W.loginManager.user.isAreaManager);
        $('#wazedevEditableAreas').text(W.loginManager.user.areas.length);

        if(settings.Enabled)
            W.selectionManager.events.register("selectionchanged", null, drawSelection);

        $('.wazedevSettingsCheckbox').change(function() {
             var settingName = $(this)[0].id.substr(7);
            settings[settingName] = this.checked;
            saveSettings();
            if(this.checked)
                W.selectionManager.events.register("selectionchanged", null, drawSelection);
            else
            {
                W.selectionManager.events.unregister("selectionchanged", null, drawSelection);
                wazedevSelectedLayer.removeAllFeatures();
            }
        });
    }

Now that we have our event handlers set up, lets create our drawSelection method. This method is simply going to take the last item selected and retrieve its geometry. The geometry will be used to create a new OL.Feature.Vector which is used to add to a layer to create the drawing. We will create a style which will set the color of the highlight to red, make the line round at the ends, set a width of 12 and we want to set the fill to false so selected Places still show correctly. Once the OL.Feature.Vector is created, you just add it to the layer for it to be drawn on screen.

We check if there are any selected items since this method will fire both when an item is selected and when one is deselected. When all items are deselected we want to clear the layer. You will notice that we are not clearing the layer and re-drawing all the selected items. This will result in the red lines we draw to persist as segments are selected and then one by one deselected. Two things can be done with this: 1) you could change it so when all items are deselected, the highlight still exists (possibly useful for highlighting routes) or 2) it could be changed so every selection/deselection clears the layer and then loops all selected items and draws them, which would clear the drawing of segments as they are deselected.

function drawSelection()
    {
        if(W.selectionManager.hasSelectedItems())
        {
            let style = {strokeColor: "red", strokeLinecap: "round", strokeWidth: 12, fill: false};

            let geo = W.selectionManager.selectedItems[W.selectionManager.selectedItems.length-1].model.geometry.clone();
            var feature = new OpenLayers.Feature.Vector(geo, {}, style);
            wazedevSelectedLayer.addFeatures([feature]);
        }
        else
            wazedevSelectedLayer.removeAllFeatures();
    }

At this point we now know how to:

  • Interact with a 3rd party utility script
  • Create a tab
  • Save & load from localStorage
  • React to a setting being toggle to enable/disable script features
  • Create, draw and clear a layer
  • Interact with several WME objects

Our script should now look like the following

// ==UserScript==
// @name         WazeDev First Script
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  Learning to script!
// @author       You
// @include      https://beta.waze.com/*
// @include      https://www.waze.com/forum/*
// @include      https://www.waze.com/editor*
// @include      https://www.waze.com/*/editor*
// @exclude      https://www.waze.com/user/editor*
// @require      https://greasyfork.org/scripts/24851-wazewrap/code/WazeWrap.js
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    var settings = {};
    var wazedevSelectedLayer;

    function bootstrap(tries = 1) {
        if (W && W.map &&
            W.model && W.loginManager.user &&
            $ && WazeWrap.Ready) {
            init();
        } else if (tries < 1000)
            setTimeout(function () {bootstrap(++tries);}, 200);
    }

    function init()
    {
        wazedevSelectedLayer = new OpenLayers.Layer.Vector("wazedevSelectedLayer",{uniqueName: "__wazedevSelectedLayer"});
        W.map.addLayer(wazedevSelectedLayer);
        wazedevSelectedLayer.setVisibility(true);

        var $section = $("<div>");
        $section.html([
            '<div>',
            '<h2>Our First Script!</h2>',
            '<input type="checkbox" id="wazedevEnabled" class="wazedevSettingsCheckbox"><label for="wazedevEnabled">Enabled</label>',
            '<hr>',
            '<div>',
            '<h3>User Info</h3>',
            'Username: <span id="wazedevUsername"></span></br>',
            'Rank: <span id="wazedevRank"></span></br>',
            'Total edits: <span id="wazedevTotalEdits"></span></br>',
            'Total points: <span id="wazedevTotalPoints"></span></br>',
            'Area manager: <span id="wazedevAM"></span></br>',
            'Editable areas: <span id="wazedevEditableAreas"></span>',
            '</div>',
            '</div>'
        ].join(' '));

        new WazeWrap.Interface.Tab('WazeDev', $section.html(), initializeSettings);
    }

    function initializeSettings()
    {
        loadSettings();
        setChecked('wazedevEnabled', settings.Enabled);

        $('#wazedevUsername').text(W.loginManager.user.userName);
        $('#wazedevRank').text(W.loginManager.user.rank + 1);
        $('#wazedevTotalEdits').text(W.loginManager.user.totalEdits);
        $('#wazedevTotalPoints').text(W.loginManager.user.totalPoints);
        $('#wazedevAM').text(W.loginManager.user.isAreaManager);
        $('#wazedevEditableAreas').text(W.loginManager.user.areas.length);

        if(settings.Enabled)
            W.selectionManager.events.register("selectionchanged", null, drawSelection);

        $('.wazedevSettingsCheckbox').change(function() {
             var settingName = $(this)[0].id.substr(7);
            settings[settingName] = this.checked;
            saveSettings();
            if(this.checked)
                W.selectionManager.events.register("selectionchanged", null, drawSelection);
            else
            {
                W.selectionManager.events.unregister("selectionchanged", null, drawSelection);
                wazedevSelectedLayer.removeAllFeatures();
            }
        });
    }

    function drawSelection()
    {
        if(W.selectionManager.hasSelectedItems())
        {
            let style = {strokeColor: "red", strokeLinecap: "round", strokeWidth: 12, fill: false};

            let geo = W.selectionManager.selectedItems[W.selectionManager.selectedItems.length-1].model.geometry.clone();
            var feature = new OpenLayers.Feature.Vector(geo, {}, style);
            wazedevSelectedLayer.addFeatures([feature]);
        }
        else
            wazedevSelectedLayer.removeAllFeatures();
    }

    function setChecked(checkboxId, checked) {
        $('#' + checkboxId).prop('checked', checked);
    }

    function saveSettings() {
        if (localStorage) {
            var localsettings = {
                Enabled: settings.Enabled
            };

            localStorage.setItem("wavedev_Settings", JSON.stringify(localsettings));
        }
    }

    function loadSettings() {
        var loadedSettings = $.parseJSON(localStorage.getItem("wavedev_Settings"));
        var defaultSettings = {
            Enabled: false,
        };
        settings = loadedSettings ? loadedSettings : defaultSettings;
        for (var prop in defaultSettings) {
            if (!settings.hasOwnProperty(prop))
                settings[prop] = defaultSettings[prop];
        }

    }

    bootstrap();
})();
⚠️ **GitHub.com Fallback** ⚠️