WME RA Util

Providing basic utility for RA adjustment without the need to delete & recreate

当前为 2016-10-04 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         WME RA Util
// @namespace    https://greasyfork.org/users/30701-justins83-waze
// @version      0.1.8
// @description  Providing basic utility for RA adjustment without the need to delete & recreate
// @include      https://www.waze.com/editor/*
// @include      https://www.waze.com/*/editor/*
// @include      https://beta.waze.com/*
// @require      https://greasyfork.org/scripts/9794-wlib/code/wLib.js?version=106259
// @require      https://greasyfork.org/scripts/23614-wlibext/code/wLibExt.js?version=150020
// @author       JustinS83
// @grant        none
// @license      
// ==/UserScript==

/* global W */
/* global wLib */

/*
Todo:
-diameter change
     -Determine if it is a circle
     -If a circle, determine center of circle
-shift diagonal?
-stretch diagonal?

non-normal RA color:#FF8000
normal RA color:#4cc600
*/
(function() {
    'use strict';

var RAUtilWindow = null;
var UpdateSegmentGeometry = require('Waze/Action/UpdateSegmentGeometry');
var MoveNode = require("Waze/Action/MoveNode");
var totalActions = 0;


    function bootstrap(tries) {
        tries = tries || 1;

        if (window.W &&
            window.W.map &&
            window.W.model) {

            init();

        } else if (tries < 1000) {
            setTimeout(function () {bootstrap(tries++);}, 200);
        }
    }

    bootstrap();


    function init(){
        RAUtilWindow = document.createElement('div');
        RAUtilWindow.id = "RAUtilWindow";
        RAUtilWindow.style.position = 'fixed';
        RAUtilWindow.style.visibility = 'hidden';
        RAUtilWindow.style.top = '25%';
        RAUtilWindow.style.left = '25%';
        RAUtilWindow.style.zIndex = 100;
        RAUtilWindow.style.backgroundColor = '#BEDCE5';
        RAUtilWindow.style.borderWidth = '3px';
        RAUtilWindow.style.borderStyle = 'solid';
        RAUtilWindow.style.borderRadius = '10px';
        RAUtilWindow.style.boxShadow = '5px 5px 10px Silver';
        RAUtilWindow.style.padding = '4px';

        var alertsHTML = '<div id="header" style="padding: 4px; background-color:#4cc600; font-weight: bold;">Roundabout Utility</div>';
        alertsHTML += '<div id="content" style="padding: 4px; background-color:White">';//A set of utilities for modifying roundabouts allowing preservation of segment data.';//<br>Double arrows shift by 2x configured value.';
        alertsHTML += '</br>';
        alertsHTML += 'Shift amount: <input type="text" name="shiftAmount" id="shiftAmount" size="1" style="border: 1px solid #000000" value="1"/> meter(s)&nbsp;</div>';

        alertsHTML += '<div id="controls" style="padding: 4px;">';
        alertsHTML += '<div id="singleShift">';// style="float:left;">';// padding-bottom:45px;">';

        alertsHTML += '<table style="table-layout:fixed;width:60px;height:84px;">';
        alertsHTML += '<tr style="width:20px;height:28px;">';
        alertsHTML += '<td align="center"></td>';
        alertsHTML += '<td align="center">';
        //Single Shift Buttons
        alertsHTML += '<span id="RAShiftUpBtn" style="cursor:pointer;font-size:14px;border:thin outset black;padding:2px;">';//margin-left:23px;">';
        alertsHTML += '<i class="fa fa-angle-up"> </i>';
        alertsHTML += '<span id="UpBtnCaption" style="font-weight: bold;"></span>';
        alertsHTML += '</span>';
        //alertsHTML += '</br>';
        alertsHTML += '</td>';
        alertsHTML += '<td align="center"></td>';
        alertsHTML += '</tr>';

        alertsHTML += '<tr style="width:20px;height:28px;">';
        alertsHTML += '<td align="center">';
        //alertsHTML += '&nbsp;&nbsp;';
        alertsHTML += '<span id="RAShiftLeftBtn" style="cursor:pointer;font-size:14px;border:thin outset black;padding:2px;padding-right:4px;">';//position:relative;padding:2px;padding-left:3px;padding-right:3px;margin-left:0px;top:10px;">';
        alertsHTML += '<i class="fa fa-angle-left"> </i>';
        alertsHTML += '<span id="LeftBtnCaption" style="font-weight: bold;"></span>';
        alertsHTML += '</span>';
        alertsHTML += '</td>';
        
        alertsHTML += '<td align="center"></td>';

        alertsHTML += '<td align="center">';
        //alertsHTML += '&nbsp;&nbsp;';
        alertsHTML += '<span id="RAShiftRightBtn" style="cursor:pointer;font-size:14px;border:thin outset black;padding:2px;padding-left:4px;">';//position:relative;padding:2px;padding-left:3px;padding-right:3px;top:10px;margin-left:15px;">';
        alertsHTML += '<i class="fa fa-angle-right"> </i>';
        alertsHTML += '<span id="RightBtnCaption" style="font-weight: bold;"></span>';
        alertsHTML += '</span>';
        alertsHTML += '</td>';
        alertsHTML += '</tr>';
        //alertsHTML += '</br>';

        alertsHTML += '<tr style="width:20px;height:28px;">';
        alertsHTML += '<td align="center"></td>';
        
        alertsHTML += '<td align="center">';
        //alertsHTML += '&nbsp;&nbsp;';
        alertsHTML += '<span id="RAShiftDownBtn" style="cursor:pointer;font-size:14px;border:thin outset black;padding:2px;">';//;position:relative;top:20px;margin-left:17px;">';
        alertsHTML += '<i class="fa fa-angle-down"> </i>';
        alertsHTML += '<span id="DownBtnCaption" style="font-weight: bold;"></span>';
        alertsHTML += '</span>';
        alertsHTML += '</td>';
        
        alertsHTML += '<td align="center"></td>';
        alertsHTML += '</tr>';
        //alertsHTML += '</br>';
        alertsHTML += '</table>';
        alertsHTML += '</div>';
        alertsHTML += '</div>';
/*
        //Double Shift Buttons
        alertsHTML += '<div id="doubleShift" style="position:relative;top:-18px;">';
        alertsHTML += '<span id="RAShiftDoubleUpBtn" style="cursor:pointer;font-size:14px;border:thin outset black;padding:2px;margin-left:30px;">';
        alertsHTML += '<i class="fa fa-angle-double-up"> </i>';
        alertsHTML += '<span id="DoubleUpBtnCaption" style="font-weight: bold;"></span>';
        alertsHTML += '</span>';
        alertsHTML += '</br>';

        alertsHTML += '&nbsp;&nbsp;';
        alertsHTML += '<span id="RAShiftDoubleLeftBtn" style="cursor:pointer;font-size:14px;border:thin outset black;padding:2px;margin-left:23px;">';
        alertsHTML += '<i class="fa fa-angle-double-left"> </i>';
        alertsHTML += '<span id="DoubleLeftBtnCaption" style="font-weight: bold;"></span>';
        alertsHTML += '</span>';

        alertsHTML += '&nbsp;&nbsp;';
        alertsHTML += '<span id="RAShiftDoubleRightBtn" style="cursor:pointer;font-size:14px;border:thin outset black;padding:2px;">';
        alertsHTML += '<i class="fa fa-angle-double-right"> </i>';
        alertsHTML += '<span id="DoubleRightBtnCaption" style="font-weight: bold;"></span>';
        alertsHTML += '</span>';
        alertsHTML += '</br>';

        alertsHTML += '&nbsp;&nbsp;';
        alertsHTML += '<span id="RAShiftDoubleDownBtn" style="cursor:pointer;font-size:14px;border:thin outset black;padding:2px;">';
        alertsHTML += '<i class="fa fa-angle-double-down"> </i>';
        alertsHTML += '<span id="DoubleDownBtnCaption" style="font-weight: bold;"></span>';
        alertsHTML += '</span>';
        alertsHTML += '</div>';
        alertsHTML += '</div>';
        */
        RAUtilWindow.innerHTML = alertsHTML;
        document.body.appendChild(RAUtilWindow);

        document.getElementById('RAShiftLeftBtn').addEventListener('click', RAShiftLeftBtnClick, false);
        document.getElementById('RAShiftRightBtn').addEventListener('click', RAShiftRightBtnClick, false);
        document.getElementById('RAShiftUpBtn').addEventListener('click', RAShiftUpBtnClick, false);
        document.getElementById('RAShiftDownBtn').addEventListener('click', RAShiftDownBtnClick, false);

        $('#shiftAmount').keypress(function(event) {
            if ((event.which != 46 || $(this).val().indexOf('.') != -1) && (event.which < 48 || event.which > 57)) {
                event.preventDefault();
            }
        });

        window.Waze.selectionManager.events.register("selectionchanged", null, checkDisplayTool);
        W.model.actionManager.events.register("afterundoaction",null, undotriggered);
        W.model.actionManager.events.register("afterclearactions",null,actionsCleared);
    }

    function undotriggered(){
        checkSaveChanges();
    }
    
    function actionsCleared(){
        checkSaveChanges();
        totalActions = 0;
    }

    function checkDisplayTool(){
        if(W.selectionManager.hasSelectedItems() && Waze.selectionManager.selectedItems[0].model.type === 'segment'){
            if(!AllSelectedSegmentsRA() || W.selectionManager.selectedItems.length === 0)
                $('#RAUtilWindow').css({'visibility': 'hidden'});
            else{
                $('#RAUtilWindow').css({'visibility': 'visible'});
                if(typeof jQuery.ui !== 'undefined')
                    $('#RAUtilWindow' ).draggable();
            }
        }
        else{
            $('#RAUtilWindow').css({'visibility': 'hidden'});
            if(typeof jQuery.ui !== 'undefined')
                $('#RAUtilWindow' ).draggable();
        }
        checkSaveChanges();
    }

    var pendingChanges = false;
    /**
    Returns false if there are pending changes, true if no changes need saved.
    */
    function checkSaveChanges(){
        var $RASaveChanges = $('#RAUtilSaveChanges');
        if(W.model.actionManager.index >= 0 && (totalActions === 0 && (W.model.actionManager.actions.length > 0))){
            if($RASaveChanges.length === 0){
                $RASaveChanges = $('<div>', {id:'RAUtilSaveChanges', style:'color:red'});
                $RASaveChanges.text('You must save your changes before using this utility.');
                $('#RAUtilWindow').append($RASaveChanges);
                pendingChanges = true;
            }
        }
        else
        {
            $RASaveChanges.remove();
            pendingChanges = false;
        }
    }

    function AllSelectedSegmentsRA()
    {
        for (i = 0; i < W.selectionManager.selectedItems.length; i++){
            if(W.selectionManager.selectedItems[i].model.attributes.id < 0 || !wLib.Model.isRoundaboutSegmentID(W.selectionManager.selectedItems[i].model.attributes.id))
                return false;
        }
        return true;
    }
    
    function ShiftSegmentNodesLat(segObj, latOffset)
    {
        var RASegs = wLib.Model.getAllRoundaboutSegmentsFromObj(segObj);
        var gps;
        var newGeometry = segObj.geometry.clone();
        var originalLength = segObj.geometry.components.length;
        for(i=0; i<RASegs.length; i++){
            segObj = W.model.segments.get(RASegs[i]);
            newGeometry = segObj.geometry.clone();
            originalLength = segObj.geometry.components.length;
            for(j=1; j < originalLength-1; j++){
                gps = wLib.Geometry.ConvertTo4326(segObj.geometry.components[j].x, segObj.geometry.components[j].y);
                gps.lat += latOffset;
                newGeometry.components.splice(j,0, new OL.Geometry.Point(segObj.geometry.components[j].x, wLib.Geometry.ConvertTo900913(segObj.geometry.components[j].x,gps.lat).lat));
                newGeometry.components.splice(j+1,1);
            }
            newGeometry.components[0].calculateBounds();
            newGeometry.components[originalLength-1].calculateBounds();
            W.model.actionManager.add(new UpdateSegmentGeometry(segObj, segObj.geometry, newGeometry));

            var node = W.model.nodes.objects[segObj.attributes.toNodeID];
            var newNodeGeometry = node.geometry.clone();
            gps = wLib.Geometry.ConvertTo4326(node.attributes.geometry.x, node.attributes.geometry.y);
            gps.lat += latOffset;
            newNodeGeometry.y = wLib.Geometry.ConvertTo900913(node.geometry.x, gps.lat).lat;
            newNodeGeometry.calculateBounds();
            W.model.actionManager.add(new MoveNode(node, node.geometry, newNodeGeometry));
            totalActions +=2;
        }
    }
    
    function ShiftSegmentsNodesLong(segObj, longOffset)
    {
        var RASegs = wLib.Model.getAllRoundaboutSegmentsFromObj(segObj);
        var gps, newGeometry, originalLength;

        //Loop through all RA segments & adjust
        for(i=0; i<RASegs.length; i++){
            segObj = W.model.segments.get(RASegs[i]);
            newGeometry = segObj.geometry.clone();
            originalLength = segObj.geometry.components.length;
            for(j=1; j < originalLength-1; j++){
                gps = wLib.Geometry.ConvertTo4326(segObj.geometry.components[j].x, segObj.geometry.components[j].y);
                gps.lon += longOffset;
                newGeometry.components.splice(j,0, new OL.Geometry.Point(wLib.Geometry.ConvertTo900913(gps.lon, segObj.geometry.components[j].y).lon, segObj.geometry.components[j].y));
                newGeometry.components.splice(j+1,1);
            }
            newGeometry.components[0].calculateBounds();
            newGeometry.components[originalLength-1].calculateBounds();
            W.model.actionManager.add(new UpdateSegmentGeometry(segObj, segObj.geometry, newGeometry));

            var node = W.model.nodes.objects[segObj.attributes.toNodeID];
            var newNodeGeometry = node.geometry.clone();
            gps = wLib.Geometry.ConvertTo4326(node.attributes.geometry.x, node.attributes.geometry.y);
            gps.lon += longOffset;
            newNodeGeometry.x = wLib.Geometry.ConvertTo900913(gps.lon, node.geometry.y).lon;
            newNodeGeometry.calculateBounds();
            W.model.actionManager.add(new MoveNode(node, node.geometry, newNodeGeometry));
            totalActions +=2;
        }
    }

    //Left
    function RAShiftLeftBtnClick(e)
    {
        // this traps the click to prevent it falling through to the underlying area name element and potentially causing the map view to be relocated to that area...
        e.stopPropagation();

        if(!pendingChanges){
            var segObj = W.selectionManager.selectedItems[0];
            var convertedCoords = wLib.Geometry.ConvertTo4326(segObj.geometry.components[0].x, segObj.geometry.components[0].y);
            var gpsOffsetAmount = wLib.Geometry.CalculateLongOffsetGPS(-$('#shiftAmount').val(), convertedCoords.lon, convertedCoords.lat);
            ShiftSegmentsNodesLong(segObj, gpsOffsetAmount);
        }
    }
    //Right
    function RAShiftRightBtnClick(e)
    {
        // this traps the click to prevent it falling through to the underlying area name element and potentially causing the map view to be relocated to that area...
        e.stopPropagation();

        if(!pendingChanges){
            var segObj = W.selectionManager.selectedItems[0];
            var convertedCoords = wLib.Geometry.ConvertTo4326(segObj.model.geometry.components[0].x, segObj.model.geometry.components[0].y);
            var gpsOffsetAmount = wLib.Geometry.CalculateLongOffsetGPS($('#shiftAmount').val(), convertedCoords.lon, convertedCoords.lat);
            ShiftSegmentsNodesLong(segObj, gpsOffsetAmount);
        }
    }
    //Up
    function RAShiftUpBtnClick(e)
    {
        // this traps the click to prevent it falling through to the underlying area name element and potentially causing the map view to be relocated to that area...
        e.stopPropagation();

        if(!pendingChanges){
            var segObj = W.selectionManager.selectedItems[0];
            var gpsOffsetAmount = wLib.Geometry.CalculateLatOffsetGPS($('#shiftAmount').val(), wLib.Geometry.ConvertTo4326(segObj.geometry.components[0].x, segObj.geometry.components[0].y));
            ShiftSegmentNodesLat(segObj, gpsOffsetAmount);
        }
    }
    //Down
    function RAShiftDownBtnClick(e)
    {
        // this traps the click to prevent it falling through to the underlying area name element and potentially causing the map view to be relocated to that area...
        e.stopPropagation();

        if(!pendingChanges){
            var segObj = W.selectionManager.selectedItems[0];
            var gpsOffsetAmount = wLib.Geometry.CalculateLatOffsetGPS(-$('#shiftAmount').val(), wLib.Geometry.ConvertTo4326(segObj.geometry.components[0].x, segObj.geometry.components[0].y));
            ShiftSegmentNodesLat(segObj, gpsOffsetAmount);
        }
    }
})();