WME RA Util

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

目前为 2016-10-04 提交的版本。查看 最新版本

// ==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);
        }
    }
})();