WME Place NavPoints

Add place entry point indicators to the map

// ==UserScript==
// @name         WME Place NavPoints
// @namespace    WazeDev
// @version      2019.07.14.003
// @description  Add place entry point indicators to the map
// @author       MapOMatic
// @include      /^https:\/\/(www|beta)\.waze\.com\/(?!user\/)(.{2,6}\/)?editor.*$/
// @require      https://greasyfork.org/scripts/24851-wazewrap/code/WazeWrap.js
// @grant        none
// ==/UserScript==

/* global W */
/* global OL */
/* global $ */

let _settings = {
    visible: true,
    plaVisible: true
};

function drawLines() {
    var layer = W.map.landmarkLayer;
    layer.removeFeatures(layer.getFeaturesByAttribute('isNavLine', true));
	if (_settings.visible && W.map.getZoom() >= 6) {
        var features = [];
		var bounds = W.map.getExtent().scale(2.0);
        W.model.venues.getObjectArray()
			.filter(v => (_settings.plaVisible || !v.isParkingLot()) && bounds.intersectsBounds(v.geometry.getBounds()))
			.forEach(v => {
            var pts = [];
            pts.push(v.geometry.getCentroid());
            if (v.attributes.entryExitPoints.length) {
                pts.push(v.attributes.entryExitPoints[0].getPoint());
            }
            var s = findClosestSegment(pts[pts.length-1], false, false);
            if (s) {
                pts.push(s.closestPoint);
                var ls = new OL.Geometry.LineString(pts);
                features.push(new OL.Feature.Vector(ls, {isNavLine: true }, {strokeColor: '#0FF', strokeWidth: 2, strokeDashstyle: '6 4'}));
            }
            features.push(new OL.Feature.Vector(pts[pts.length - 1], {isNavLine: true}, {pointRadius: 4, strokeWidth: 2, fillColor: '#A00', strokeColor: '#0FF', fillOpacity: 1}));
    	});
    	layer.addFeatures(features);
    }
}

function findClosestSegment(mygeometry, ignorePLR, ignoreUnnamedPR) {
    let segments = W.model.segments.getObjectArray();
    let minDistance = Infinity;
    let closestSegment;

    for (let idx = 0; idx < segments.length; idx++) {
        let s = segments[idx];

        let { roadType } = s.attributes;

        if (s.isDeleted()) {
            debugger;
            continue;
        }

        if ([10, 16, 18, 19].includes(roadType)) //10 ped boardwalk, 16 stairway, 18 railroad, 19 runway, 3 freeway
            continue;

        if (ignorePLR && roadType === 20) //PLR
            continue;

        if (ignoreUnnamedPR)
            if (roadType === 17 && WazeWrap.Model.getStreetName(s.attributes.primaryStreetID) === null) //PR
                continue;


        let distanceToSegment = mygeometry.distanceTo(s.geometry, { details: true });

        if (distanceToSegment.distance < minDistance) {
            minDistance = distanceToSegment.distance;
            closestSegment = s;
            closestSegment.closestPoint = new OL.Geometry.Point(distanceToSegment.x1, distanceToSegment.y1);
        }
    }
    return closestSegment;
};

function saveSettings() {
    localStorage.setItem('wme_place_navpoints', JSON.stringify(_settings));
}

function errorHandler(callback) {
    try {
        callback();
    } catch (ex) {
        console.error(ex);
    }
}

function onPlacesLayerCheckedChanged(checked) {
    _settings.visible = checked;
    $("#layer-switcher-item_pla_navpoints").attr('disabled', checked ? null : true);
    saveSettings();
    drawLines();
}

function onPlaLayerCheckedChanged(checked) {
    _settings.plaVisible = checked;
    saveSettings();
    drawLines();
}

function init() {
    var loadedSettings = JSON.parse(localStorage.getItem('wme_place_navpoints'));
    $.extend(_settings, loadedSettings);
    W.model.events.register('mergeend', null, () => errorHandler(drawLines));
    W.map.events.register('zoomend', null, () => errorHandler(drawLines));
    W.model.venues.on('objectschanged', () => errorHandler(drawLines));
    W.model.venues.on('objectsadded', () => errorHandler(drawLines));
    W.model.venues.on('objectsremoved', () => errorHandler(drawLines));
    W.model.segments.on('objectschanged', () => errorHandler(drawLines));
    W.model.segments.on('objectsadded', () => errorHandler(drawLines));
    W.model.segments.on('objectsremoved', () => errorHandler(drawLines));
    drawLines();
    WazeWrap.Interface.AddLayerCheckbox('Display', 'Place NavPoints', _settings.visible, onPlacesLayerCheckedChanged, null);
    WazeWrap.Interface.AddLayerCheckbox('Display', 'PLA NavPoints', _settings.visible, onPlaLayerCheckedChanged, null);
    $("#layer-switcher-item_pla_navpoints").attr('disabled', _settings.visible ? null : true).parent().css({'margin-left': '10px'});
}

function bootstrap() {
    if (W && W.map && WazeWrap.Ready) {
        init();
    } else {
        setTimeout(bootstrap, 200);
    }
}

bootstrap();