WME Segment Shift Utility

Utility for shifting street segments in WME without disconnecting nodes

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         WME Segment Shift Utility
// @namespace    https://github.com/kid4rm90s/Segment-Shift-Utility
// @version      2026.08.23.02
// @description  Utility for shifting street segments in WME without disconnecting nodes
// @include      /^https:\/\/(www|beta)\.waze\.com\/(?!user\/)(.{2,6}\/)?editor\/?.*$/*
// @author       kid4rm90s
// @connect      greasyfork.org
// @grant        GM_xmlhttpRequest
// @grant        unsafeWindow
// @icon         data:image/png;base64,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
// @require      https://greasyfork.org/scripts/560385/code/WazeToastr.js
// @require      https://cdn.jsdelivr.net/gh/wazeSpace/wme-sdk-plus@06108853094d40f67e923ba0fe0de31b1cec4412/wme-sdk-plus.js
// @exclude      https://cdn.jsdelivr.net/gh/WazeSpace/wme-sdk-plus@latest/wme-sdk-plus.js
// @require      https://cdn.jsdelivr.net/npm/@turf/[email protected]/turf.min.js
// @license      MIT
// ==/UserScript==

/* global getWmeSdk */
/* global initWmeSdkPlus */
/* global WazeToastr */
/* global turf */
/* global $ */
/* global jQuery */
/* global I18n */
/* eslint curly: ["warn", "multi-or-nest"] */

/*Scripts modified from WME RA Util (https://greasyfork.org/en/scripts/23616-wme-ra-util)
orgianl author: JustinS83 Waze*/
(function () {
  const updateMessage = ' <strong>Fixed :</strong><br> - Added support for shifting node A/B only.';
  const scriptVersion = GM_info.script.version.toString();
  const scriptName = GM_info.script.name;
  const downloadUrl = GM_info.script.downloadURL;
  const forumURL = 'https://greasyfork.org/scripts/537258-segment-shift-utility/feedback';

  const DIRECTION = {
    NORTH: 0,
    EAST: 90,
    SOUTH: 180,
    WEST: 270,
  };

  let sdk;
  let _settings;

  async function bootstrap() {
    const wmeSdk = getWmeSdk({ scriptId: 'wme-ss-util', scriptName: 'WME SS Util' });
    // NOTE: wme-sdk-plus is required ONLY for sdk.Editing.doActions() grouped-undo.
    // The base WME SDK has no transaction API (only save/undo/undoAll/redo), so
    // without SDK+ each moveNode/updateSegment becomes a separate undo step.
    const sdkPlus = await initWmeSdkPlus(wmeSdk, {
      hooks: ['Editing.Transactions'],
    });
    sdk = sdkPlus || wmeSdk;
    sdk.Events.once({ eventName: 'wme-ready' }).then(() => {
      init();
    });
  }

  function waitForWME() {
    if (!unsafeWindow.SDK_INITIALIZED) {
      setTimeout(waitForWME, 500);
      return;
    }
    unsafeWindow.SDK_INITIALIZED.then(bootstrap);
  }
  waitForWME();

function scriptupdatemonitor() {
  if (WazeToastr?.Ready) {
    // Create and start the ScriptUpdateMonitor
    const updateMonitor = new WazeToastr.Alerts.ScriptUpdateMonitor(scriptName, scriptVersion, downloadUrl, GM_xmlhttpRequest);

    // Check immediately on page load, then every 2 hours
    updateMonitor.start(2, true); // checkImmediately = true

    // Show the update dialog for the current version
    WazeToastr.Interface.ShowScriptUpdate(scriptName, scriptVersion, updateMessage, downloadUrl);
  } else {
    setTimeout(scriptupdatemonitor, 250);
  }
}
  scriptupdatemonitor();
  
  function init() {
    console.log('SS UTIL', GM_info.script);
    injectCss();
    // UpdateSegmentGeometry = require('Waze/Action/UpdateSegmentGeometry'); // Replaced by SDK
    // MoveNode = require("Waze/Action/MoveNode"); // Replaced by SDK
    // MultiAction = require("Waze/Action/MultiAction"); // Replaced by SDK

    SSUtilWindow = document.createElement('div');
    SSUtilWindow.id = 'SSUtilWindow'; // Consistent ID
    SSUtilWindow.style.position = 'fixed';
    SSUtilWindow.style.visibility = 'hidden';
    SSUtilWindow.style.top = '15%';
    SSUtilWindow.style.left = '25%';
    SSUtilWindow.style.width = '285px';
    SSUtilWindow.style.zIndex = 100;
    SSUtilWindow.style.backgroundColor = '#FFFFFE';
    SSUtilWindow.style.borderWidth = '0px';
    SSUtilWindow.style.borderStyle = 'solid';
    SSUtilWindow.style.borderRadius = '10px';
    SSUtilWindow.style.boxShadow = '5px 5px 10px Silver';
    SSUtilWindow.style.padding = '4px';

    let SSUtilWindowHTML =
      '<div id="header" style="padding: 4px; background-color:#92C3D3; border-radius: 5px;-moz-border-radius: 5px;-webkit-border-radius: 5px; color: white; font-weight: bold; text-align:center; letter-spacing: 1px;text-shadow: black 0.1em 0.1em 0.2em;"><img src="https://storage.googleapis.com/wazeopedia-files/1/1e/RA_Util.png" style="float:left"></img> Segment Shift Utility <a data-toggle="collapse" href="#divWrappers1" id="collapserLink1" style="float:right"><span id="collapser1" style="cursor:pointer;padding:2px;color:white;" class="fa fa-caret-square-o-up"></a></span></div>';
    // start collapse // I put it al the beginning
    SSUtilWindowHTML += '<div id="divWrappers1" class="collapse in">';

    //***************** Shift Amount **************************
    // Define BOX
    SSUtilWindowHTML += '<div style="display:flex; align-items:flex-start; gap:5px; flex-wrap:nowrap;">';
    SSUtilWindowHTML +=
      '<div id="contentShift" style="text-align:center; width: 120px;height: 170px;margin: 1em 5px 0px 0px;opacity:1;border-radius: 2px;-moz-border-radius: 2px;-webkit-border-radius: 4px;border-width:1px;border-style:solid;border-color:#92C3D3;padding:2px;}">';
    SSUtilWindowHTML +=
      '<b>Shift amount</b></br><input type="text" name="shiftAmount" id="shiftAmount" size="1" style="float: left; text-align: center;font: inherit; line-height: normal; width: 30px; height: 20px; margin: 5px 4px; box-sizing: border-box; display: block; padding-left: 0; border-bottom-color: rgba(black,.3); background: transparent; outline: none; color: black;" value="1"/> <div style="margin: 5px 4px;">Metre(s)';
    // Shift amount controls
    SSUtilWindowHTML +=
      '<div id="controls" style="text-align:center; padding:06px 4px;width=100px; height=100px;margin: 5px 0px;border-style:solid; border-width: 2px;border-radius: 50%;-moz-border-radius: 50%;-webkit-border-radius: 50%;box-shadow: inset 0px 0px 50px -14px rgba(0,0,0,1);-moz-box-shadow: inset 0px 0px 50px -14px rgba(0,0,0,1);-webkit-box-shadow: inset 0px 0px 50px -14px rgba(0,0,0,1); background:#92C3D3;align:center;">';
    //Single Shift Up Button
    SSUtilWindowHTML += '<span id="SSShiftUpBtn" style="cursor:pointer;font-size:14px;">';
    SSUtilWindowHTML += '<i class="fa fa-angle-double-up fa-2x" style="color: white; text-shadow: black 0.1em 0.1em 0.2em; vertical-align: top;"> </i>';
    SSUtilWindowHTML += '<span id="UpBtnCaption" style="font-weight: bold;"></span>';
    SSUtilWindowHTML += '</span><br>';
    //Single Shift Left Button
    SSUtilWindowHTML += '<span id="SSShiftLeftBtn" style="cursor:pointer;font-size:14px;margin-left:-40px;">';
    SSUtilWindowHTML += '<i class="fa fa-angle-double-left fa-2x" style="color: white; text-shadow: black 0.1em 0.1em 0.2em; vertical-align: middle"> </i>';
    SSUtilWindowHTML += '<span id="LeftBtnCaption" style="font-weight: bold;"></span>';
    SSUtilWindowHTML += '</span>';
    //Single Shift Right Button
    SSUtilWindowHTML += '<span id="SSShiftRightBtn" style="float: right;cursor:pointer;font-size:14px;margin-right:5px;">';
    SSUtilWindowHTML += '<i class="fa fa-angle-double-right fa-2x" style="color: white;text-shadow: black 0.1em 0.1em 0.2em;  vertical-align: middle"> </i>';
    SSUtilWindowHTML += '<span id="RightBtnCaption" style="font-weight: bold;"></span>';
    SSUtilWindowHTML += '</span><br>';
    //Single Shift Down Button
    SSUtilWindowHTML += '<span id="SSShiftDownBtn" style="cursor:pointer;font-size:14px;margin-top:0px;">';
    SSUtilWindowHTML += '<i class="fa fa-angle-double-down fa-2x" style="color: white;text-shadow: black 0.1em 0.1em 0.2em;  vertical-align: middle"> </i>';
    SSUtilWindowHTML += '<span id="DownBtnCaption" style="font-weight: bold;"></span>';
    SSUtilWindowHTML += '</span>';
    SSUtilWindowHTML += '</div></div></div>'; // close #controls, #Metre(s), and #contentShift
    //***************** Shift Target (right side) & Disconnect Nodes **************************
    SSUtilWindowHTML +=
      '<div id="contentTarget" style="flex:1; min-width:0; text-align:left; margin: 1em 0px 0px 0px; border-radius: 2px; border-width:1px;border-style:solid;border-color:#92C3D3;padding:2px;">';
    SSUtilWindowHTML += '<b>Shift target</b><br>';
    SSUtilWindowHTML += '<label style="white-space: nowrap;"><input type="radio" name="shiftTarget" value="whole" checked> Whole Segment</label><br>';
    SSUtilWindowHTML += '<label style="white-space: nowrap;"><input type="radio" name="shiftTarget" value="nodeA"> Shift Node A</label><br>';
    SSUtilWindowHTML += '<label style="white-space: nowrap;"><input type="radio" name="shiftTarget" value="nodeB"> Shift Node B</label>';
    SSUtilWindowHTML += '<p style="margin: 8px 0px 0px 0px;"><input type="checkbox" id="chkDisconnectNodes"> Disconnect Nodes</p>';
    SSUtilWindowHTML += '</div>'; // close #contentTarget
    SSUtilWindowHTML += '</div>'; // close flex wrapper
    SSUtilWindowHTML += '</div>'; // close #divWrappers1

    SSUtilWindow.innerHTML = SSUtilWindowHTML;
    document.body.appendChild(SSUtilWindow);

    $('#SSShiftLeftBtn').click(SSShiftLeftClick);
    $('#SSShiftRightBtn').click(SSShiftRightClick);
    $('#SSShiftUpBtn').click(SSShiftUpClick);
    $('#SSShiftDownBtn').click(SSShiftDownClick);

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

    // Keyboard shortcut support for direction shift (Alt+Arrow)
    document.addEventListener(
      'keydown',
      function (e) {
        if (!e.altKey) return;
        // Prevent triggering only while typing in a text field (e.g. shift amount),
        // but allow shortcuts when focus is on radio buttons / checkboxes.
        const t = e.target;
        const isTextEntry =
          t &&
          (t.tagName === 'TEXTAREA' ||
            (t.tagName === 'INPUT' && /^(text|number|search|email|password|url|tel)$/i.test(t.type || '')));
        if (isTextEntry) return;
        switch (e.key) {
          case 'ArrowLeft':
            e.preventDefault();
            SSShiftLeftClick(e);
            break;
          case 'ArrowRight':
            e.preventDefault();
            SSShiftRightClick(e);
            break;
          case 'ArrowUp':
            e.preventDefault();
            SSShiftUpClick(e);
            break;
          case 'ArrowDown':
            e.preventDefault();
            SSShiftDownClick(e);
            break;
        }
      },
      false
    );

    $('#collapserLink1').click(function () {
      $('#divWrappers1').slideToggle('fast');
      if ($('#collapser1').attr('class') == 'fa fa-caret-square-o-down') {
        $('#collapser1').removeClass('fa-caret-square-o-down');
        $('#collapser1').addClass('fa-caret-square-o-up');
      } else {
        $('#collapser1').removeClass('fa-caret-square-o-up');
        $('#collapser1').addClass('fa-caret-square-o-down');
      }
      saveSettingsToStorage();
    });

    const loadedSettings = JSON.parse(localStorage.getItem('WME_SSUtil'));
    const defaultSettings = {
      divTop: '15%',
      divLeft: '25%',
      Expanded: true,
      DisconnectNodes: false, // default to false (normal behavior)
    };
    _settings = loadedSettings ?? defaultSettings;

    $('#SSUtilWindow').css('left', _settings.divLeft);
    $('#SSUtilWindow').css('top', _settings.divTop);
    $('#chkDisconnectNodes').prop('checked', _settings.DisconnectNodes); // Set checkbox state from settings
    $('input[name=shiftTarget][value="whole"]').prop('checked', true); // Always start on Whole Segment
    updateDisconnectEnabled(); // Apply auto-disable based on current mode
    $('input[name=shiftTarget]').on('change', updateDisconnectEnabled);

    if (!_settings.Expanded) {
      $('#divWrappers1').hide();
      $('#collapser1').removeClass('fa-caret-square-o-up');
      $('#collapser1').addClass('fa-caret-square-o-down');
    }

    sdk.Events.on({ eventName: 'wme-selection-changed', eventHandler: checkDisplayTool });
  }

  function saveSettingsToStorage() {
    if (localStorage) {
      _settings.divLeft = $('#SSUtilWindow').css('left');
      _settings.divTop = $('#SSUtilWindow').css('top');
      _settings.Expanded = $('#collapser1').attr('class').indexOf('fa-caret-square-o-up') > -1;
      _settings.DisconnectNodes = $('#chkDisconnectNodes').is(':checked'); // Save checkbox state
      localStorage.setItem('WME_SSUtil', JSON.stringify(_settings));
    }
  }

  function updateDisconnectEnabled() {
    const isWhole = $('input[name=shiftTarget]:checked').val() === 'whole';
    $('#chkDisconnectNodes').prop('disabled', !isWhole);
  }

  function checkDisplayTool() {
    if (sdk.Editing.getSelection()?.objectType === 'segment') {
      if (sdk.Editing.getSelection().length === 0) {
        $('#SSUtilWindow').css({ visibility: 'hidden' });
      } else {
        $('#SSUtilWindow').css({ visibility: 'visible' });
        if (typeof jQuery.ui !== 'undefined') {
          $('#SSUtilWindow').draggable({
            //Gotta nuke the height setting the dragging inserts otherwise the panel cannot collapse
            stop: () => {
              $('#SSUtilWindow').css('height', '');
              saveSettingsToStorage();
            },
          });
        }
      }
    } else {
      $('#SSUtilWindow').css({ visibility: 'hidden' });
      if (typeof jQuery.ui !== 'undefined') {
        $('#SSUtilWindow').draggable({
          stop: () => {
            $('#SSUtilWindow').css('height', '');
            saveSettingsToStorage();
          },
        });
      }
    }
  }

  function performShift(shiftBearing) {
    const selectedSegmentIds = sdk.Editing.getSelection()?.ids;
    if (!selectedSegmentIds || selectedSegmentIds.length === 0) {
      return;
    }

    const numOffset = parseFloat($('#shiftAmount').val());
    if (isNaN(numOffset)) {
      console.error('SS UTIL: Invalid shift amount.');
      return;
    }

    const shiftDistance = Math.abs(numOffset);
    const mode = $('input[name=shiftTarget]:checked').val(); // 'whole' | 'nodeA' | 'nodeB'
    const disconnectNodes = mode === 'whole' && $('#chkDisconnectNodes').is(':checked');
    const description =
      mode === 'whole' ? 'Shifted segments' : `Shifted node ${mode === 'nodeA' ? 'A' : 'B'}`;

    sdk.Editing.doActions(() => {
      if (mode === 'whole') {
        // ----- Whole Segment: shift entire segment(s) -----
        const uniqueNodeIds = new Set();

        if (!disconnectNodes) {
          // Collect unique nodes from selected segments
          for (const segmentId of selectedSegmentIds) {
            const currentSegment = sdk.DataModel.Segments.getById({ segmentId });
            if (currentSegment) {
              uniqueNodeIds.add(currentSegment.fromNodeId);
              uniqueNodeIds.add(currentSegment.toNodeId);
            }
          }

          // Shift unique nodes
          for (const nodeId of uniqueNodeIds) {
            const node = sdk.DataModel.Nodes.getById({ nodeId });
            if (node) {
              const newNodeGeometry = structuredClone(node.geometry);
              const currentNodePoint = node.geometry.coordinates;
              const newNodePoint = turf.destination(currentNodePoint, shiftDistance, shiftBearing, { units: 'meters' });
              newNodeGeometry.coordinates = newNodePoint.geometry.coordinates;
              sdk.DataModel.Nodes.moveNode({ id: node.id, geometry: newNodeGeometry });
            }
          }
        }

        // Update Segment Geometries
        for (const segmentId of selectedSegmentIds) {
          const currentSegment = sdk.DataModel.Segments.getById({ segmentId });
          if (currentSegment) {
            const newGeometry = structuredClone(currentSegment.geometry);
            const originalLength = currentSegment.geometry.coordinates.length;

            if (disconnectNodes) {
              // Shift all points including end nodes
              for (let j = 0; j < originalLength; j++) {
                const currentPoint = currentSegment.geometry.coordinates[j];
                const newPoint = turf.destination(currentPoint, shiftDistance, shiftBearing, { units: 'meters' });
                newGeometry.coordinates[j] = newPoint.geometry.coordinates;
              }
            } else {
              // Shift only inner points
              for (let j = 1; j < originalLength - 1; j++) {
                const currentPoint = currentSegment.geometry.coordinates[j];
                const newPoint = turf.destination(currentPoint, shiftDistance, shiftBearing, { units: 'meters' });
                newGeometry.coordinates[j] = newPoint.geometry.coordinates;
              }
              // Update end points to match (potentially) moved nodes
              const fromNodeAfterMove = sdk.DataModel.Nodes.getById({ nodeId: currentSegment.fromNodeId });
              const toNodeAfterMove = sdk.DataModel.Nodes.getById({ nodeId: currentSegment.toNodeId });

              if (fromNodeAfterMove && newGeometry.coordinates.length > 0) {
                newGeometry.coordinates[0] = fromNodeAfterMove.geometry.coordinates;
              }
              if (toNodeAfterMove && newGeometry.coordinates.length > 1) {
                newGeometry.coordinates[originalLength - 1] = toNodeAfterMove.geometry.coordinates;
              }
            }
            sdk.DataModel.Segments.updateSegment({ segmentId: currentSegment.id, geometry: newGeometry });
          }
        }
      } else {
        // ----- Node A / Node B: move only the chosen node -----
        const isNodeA = mode === 'nodeA';
        const pickNodeId = (seg) => (isNodeA ? seg.fromNodeId : seg.toNodeId);
        const nodeIdSet = new Set();

        for (const segmentId of selectedSegmentIds) {
          const currentSegment = sdk.DataModel.Segments.getById({ segmentId });
          if (currentSegment) {
            const nodeId = pickNodeId(currentSegment);
            if (nodeId) nodeIdSet.add(nodeId);
          }
        }

        // Move unique nodes
        for (const nodeId of nodeIdSet) {
          const node = sdk.DataModel.Nodes.getById({ nodeId });
          if (node) {
            const newNodeGeometry = structuredClone(node.geometry);
            const newNodePoint = turf.destination(node.geometry.coordinates, shiftDistance, shiftBearing, { units: 'meters' });
            newNodeGeometry.coordinates = newNodePoint.geometry.coordinates;
            sdk.DataModel.Nodes.moveNode({ id: node.id, geometry: newNodeGeometry });
          }
        }

        // Snap each segment's corresponding endpoint to the moved node
        for (const segmentId of selectedSegmentIds) {
          const currentSegment = sdk.DataModel.Segments.getById({ segmentId });
          if (currentSegment) {
            const nodeId = pickNodeId(currentSegment);
            const movedNode = nodeId ? sdk.DataModel.Nodes.getById({ nodeId }) : null;
            if (!movedNode) continue;

            const newGeometry = structuredClone(currentSegment.geometry);
            const idx = isNodeA ? 0 : currentSegment.geometry.coordinates.length - 1;
            newGeometry.coordinates[idx] = movedNode.geometry.coordinates;
            sdk.DataModel.Segments.updateSegment({ segmentId: currentSegment.id, geometry: newGeometry });
          }
        }
      }
    }, description);
  }

  function getShiftAlertMessage(directionText) {
    const mode = $('input[name=shiftTarget]:checked').val();
    const amount = $('#shiftAmount').val();
    if (mode === 'whole') {
      return `The segments are shifted by <b>${amount}</b> Metres ${directionText}.`;
    }
    const nodeLabel = mode === 'nodeA' ? 'Node A' : 'Node B';
    return `${nodeLabel} is shifted by <b>${amount}</b> Metres ${directionText}.`;
  }

  //Left
  function SSShiftLeftClick(e) {
    e.stopPropagation();
    performShift(DIRECTION.WEST);
    WazeToastr.Alerts.info('WME Segment Shift Utility', getShiftAlertMessage('to the left'), false, false, 1500);
  }
  //Right
  function SSShiftRightClick(e) {
    e.stopPropagation();
    performShift(DIRECTION.EAST);
    WazeToastr.Alerts.info('WME Segment Shift Utility', getShiftAlertMessage('to the right'), false, false, 1500);
  }
  //Up
  function SSShiftUpClick(e) {
    e.stopPropagation();
    performShift(DIRECTION.NORTH);
    WazeToastr.Alerts.info('WME Segment Shift Utility', getShiftAlertMessage('to the up'), false, false, 1500);
  }
  //Down
  function SSShiftDownClick(e) {
    e.stopPropagation();
    performShift(DIRECTION.SOUTH);
    WazeToastr.Alerts.info('WME Segment Shift Utility', getShiftAlertMessage('to the down'), false, false, 1500);
  }

  function injectCss() {
    const css = [].join(' '); // No custom CSS needed if these were the only ones
    $('<style type="text/css">' + css + '</style>').appendTo('head');
  }
  /*
  Changelog
  2026.08.23.02
  - Added support for shifting node A/B only.
  2025.07.04.01
  -  Added keyboard shortcuts (Alt + Arrow keys) for quick segment shifting in all four directions. This improves workflow speed and matches the behavior of other WME utility scripts.
  */
})();