您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Straighten selected WME segment(s) by aligning along straight line between two end points and removing geometry nodes.
- // ==UserScript==
- // @name WME Straighten Up!
- // @namespace https://greasyfork.org/users/166843
- // @version 2024.01.31.01
- // @description Straighten selected WME segment(s) by aligning along straight line between two end points and removing geometry nodes.
- // @author dBsooner
- // @match http*://*.waze.com/*editor*
- // @exclude http*://*.waze.com/user/editor*
- // @require https://greasyfork.org/scripts/24851-wazewrap/code/WazeWrap.js
- // @grant GM_xmlhttpRequest
- // @connect greasyfork.org
- // @license GPLv3
- // ==/UserScript==
- // Original credit to jonny3D and impulse200
- /* global I18n, GM_info, GM_xmlhttpRequest, W, WazeWrap */
- (function () {
- 'use strict';
- // eslint-disable-next-line no-nested-ternary
- const _SCRIPT_SHORT_NAME = `WME SU!${(/beta/.test(GM_info.script.name) ? ' β' : /\(DEV\)/i.test(GM_info.script.name) ? ' Ω' : '')}`,
- _SCRIPT_LONG_NAME = GM_info.script.name,
- _IS_ALPHA_VERSION = /[Ω]/.test(_SCRIPT_SHORT_NAME),
- _IS_BETA_VERSION = /[β]/.test(_SCRIPT_SHORT_NAME),
- // SCRIPT_AUTHOR = GM_info.script.author,
- _PROD_DL_URL = 'https://greasyfork.org/scripts/388349-wme-straighten-up/code/WME%20Straighten%20Up!.user.js',
- _FORUM_URL = 'https://www.waze.com/forum/viewtopic.php?f=819&t=289116',
- _SETTINGS_STORE_NAME = 'WMESU',
- _BETA_DL_URL = 'YUhSMGNITTZMeTluY21WaGMzbG1iM0pyTG05eVp5OXpZM0pwY0hSekx6TTRPRE0xTUMxM2JXVXRjM1J5WVdsbmFIUmxiaTExY0MxaVpYUmhMMk52WkdVdlYwMUZKVEl3VTNSeVlXbG5hSFJsYmlVeU1GVndJU1V5TUNoaVpYUmhLUzUxYzJWeUxtcHo=',
- _ALERT_UPDATE = true,
- _SCRIPT_VERSION = GM_info.script.version.toString(),
- _SCRIPT_VERSION_CHANGES = ['BUGFIX: Check for micro dog leg (mDL)'],
- _DEBUG = /[βΩ]/.test(_SCRIPT_SHORT_NAME),
- _LOAD_BEGIN_TIME = performance.now(),
- _elems = {
- b: document.createElement('b'),
- br: document.createElement('br'),
- div: document.createElement('div'),
- li: document.createElement('li'),
- ol: document.createElement('ol'),
- option: document.createElement('option'),
- p: document.createElement('p'),
- select: document.createElement('select'),
- 'wz-button': document.createElement('wz-button'),
- 'wz-card': document.createElement('wz-card')
- },
- _timeouts = { onWmeReady: undefined, saveSettingsToStorage: undefined };
- let _settings = {};
- function log(message, data = '') { console.log(`${_SCRIPT_SHORT_NAME}:`, message, data); }
- function logError(message, data = '') { console.error(`${_SCRIPT_SHORT_NAME}:`, new Error(message), data); }
- function logWarning(message, data = '') { console.warn(`${_SCRIPT_SHORT_NAME}:`, message, data); }
- function logDebug(message, data = '') {
- if (_DEBUG)
- log(message, data);
- }
- function $extend(...args) {
- const extended = {},
- deep = Object.prototype.toString.call(args[0]) === '[object Boolean]' ? args[0] : false,
- merge = function (obj) {
- Object.keys(obj).forEach((prop) => {
- if (Object.prototype.hasOwnProperty.call(obj, prop)) {
- if (deep && Object.prototype.toString.call(obj[prop]) === '[object Object]')
- extended[prop] = $extend(true, extended[prop], obj[prop]);
- else if ((obj[prop] !== undefined) && (obj[prop] !== null))
- extended[prop] = obj[prop];
- }
- });
- };
- for (let i = deep ? 1 : 0, { length } = args; i < length; i++) {
- if (args[i])
- merge(args[i]);
- }
- return extended;
- }
- function createElem(type = '', attrs = {}, eventListener = []) {
- const el = _elems[type]?.cloneNode(false) || _elems.div.cloneNode(false),
- applyEventListeners = function ([evt, cb]) {
- return this.addEventListener(evt, cb);
- };
- Object.keys(attrs).forEach((attr) => {
- if ((attrs[attr] !== undefined) && (attrs[attr] !== 'undefined') && (attrs[attr] !== null) && (attrs[attr] !== 'null')) {
- if ((attr === 'disabled') || (attr === 'checked') || (attr === 'selected') || (attr === 'textContent') || (attr === 'innerHTML'))
- el[attr] = attrs[attr];
- else
- el.setAttribute(attr, attrs[attr]);
- }
- });
- if (eventListener.length > 0) {
- eventListener.forEach((obj) => {
- Object.entries(obj).map(applyEventListeners.bind(el));
- });
- }
- return el;
- }
- function createTextNode(str = '') {
- return document.createTextNode(str);
- }
- function dec(s = '') {
- return atob(atob(s));
- }
- function checkTimeout(obj) {
- if (obj.toIndex) {
- if (_timeouts[obj.timeout]?.[obj.toIndex]) {
- window.clearTimeout(_timeouts[obj.timeout][obj.toIndex]);
- delete (_timeouts[obj.timeout][obj.toIndex]);
- }
- }
- else {
- if (_timeouts[obj.timeout])
- window.clearTimeout(_timeouts[obj.timeout]);
- _timeouts[obj.timeout] = undefined;
- }
- }
- async function loadSettingsFromStorage() {
- const defaultSettings = {
- conflictingNames: 'warning',
- longJnMove: 'warning',
- microDogLegs: 'warning',
- nonContinuousSelection: 'warning',
- sanityCheck: 'warning',
- runStraightenUpShortcut: '',
- lastSaved: 0,
- lastVersion: undefined
- },
- loadedSettings = JSON.parse(localStorage.getItem(_SETTINGS_STORE_NAME));
- _settings = $extend(true, {}, defaultSettings, loadedSettings);
- const serverSettings = await WazeWrap.Remote.RetrieveSettings(_SETTINGS_STORE_NAME);
- if (serverSettings?.lastSaved > _settings.lastSaved)
- $extend(_settings, serverSettings);
- _timeouts.saveSettingsToStorage = window.setTimeout(saveSettingsToStorage, 5000);
- return Promise.resolve();
- }
- function saveSettingsToStorage() {
- checkTimeout({ timeout: 'saveSettingsToStorage' });
- if (localStorage) {
- _settings.lastVersion = _SCRIPT_VERSION;
- _settings.lastSaved = Date.now();
- localStorage.setItem(_SETTINGS_STORE_NAME, JSON.stringify(_settings));
- WazeWrap.Remote.SaveSettings(_SETTINGS_STORE_NAME, _settings);
- logDebug('Settings saved.');
- }
- }
- function checkShortcutChanged() {
- let keys = '';
- const { shortcut } = W.accelerators.Actions.runStraightenUpShortcut;
- if (shortcut) {
- if (shortcut.altKey)
- keys += 'A';
- if (shortcut.shiftKey)
- keys += 'S';
- if (shortcut.ctrlKey)
- keys += 'C';
- if (keys !== '')
- keys += '+';
- if (shortcut.keyCode)
- keys += shortcut.keyCode;
- }
- else {
- keys = '';
- }
- if (_settings.runStraightenUpShortcut !== keys) {
- _settings.runStraightenUpShortcut = keys;
- saveSettingsToStorage();
- }
- }
- function showScriptInfoAlert() {
- if (_ALERT_UPDATE && (_SCRIPT_VERSION !== _settings.lastVersion)) {
- const divElemRoot = createElem('div');
- divElemRoot.appendChild(createElem('p', { textContent: 'What\'s New:' }));
- const ulElem = createElem('ul');
- if (_SCRIPT_VERSION_CHANGES.length > 0) {
- for (let idx = 0, { length } = _SCRIPT_VERSION_CHANGES; idx < length; idx++)
- ulElem.appendChild(createElem('li', { innerHTML: _SCRIPT_VERSION_CHANGES[idx] }));
- }
- else {
- ulElem.appendChild(createElem('li', { textContent: 'Nothing major.' }));
- }
- divElemRoot.appendChild(ulElem);
- WazeWrap.Interface.ShowScriptUpdate(_SCRIPT_SHORT_NAME, _SCRIPT_VERSION, divElemRoot.innerHTML, (_IS_BETA_VERSION ? dec(_BETA_DL_URL) : _PROD_DL_URL).replace(/code\/.*\.js/, ''), _FORUM_URL);
- }
- }
- // рассчитаем пересчечение перпендикуляра точки с наклонной прямой
- // Calculate the intersection of the perpendicular point with an inclined line
- function getIntersectCoord(a, b, c, d) {
- // второй вариант по-проще: http://rsdn.ru/forum/alg/2589531.hot
- const r = [2];
- // eslint-disable-next-line no-mixed-operators
- r[1] = -1.0 * (c * b - a * d) / (a * a + b * b);
- r[0] = (-r[1] * (b + a) - c + d) / (a - b);
- return { x: r[0], y: r[1] };
- }
- // определим направляющие
- // Define guides
- function getDeltaDirect(a, b) {
- let d = 0.0;
- if (a < b)
- d = 1.0;
- else if (a > b)
- d = -1.0;
- return d;
- }
- function checkNameContinuity(segmentSelectionArr = []) {
- const streetIds = [],
- streetIdsForEach = (streetId) => { streetIds.push(streetId); };
- for (let idx = 0, { length } = segmentSelectionArr; idx < length; idx++) {
- if (idx > 0) {
- if ((segmentSelectionArr[idx].getPrimaryStreetID() > 0) && streetIds.includes(segmentSelectionArr[idx].getPrimaryStreetID()))
- // eslint-disable-next-line no-continue
- continue;
- if (segmentSelectionArr[idx].getAttribute('streetIDs').length > 0) {
- let included = false;
- for (let idx2 = 0, len = segmentSelectionArr[idx].getAttribute('streetIDs').length; idx2 < len; idx2++) {
- included = streetIds.includes(segmentSelectionArr[idx].getAttribute('streetIDs')[idx2]);
- if (included)
- break;
- }
- if (included === true)
- // eslint-disable-next-line no-continue
- continue;
- else
- return false;
- }
- return false;
- }
- if (idx === 0) {
- if (segmentSelectionArr[idx].getPrimaryStreetID() > 0)
- streetIds.push(segmentSelectionArr[idx].getPrimaryStreetID());
- if (segmentSelectionArr[idx].getAttribute('streetIDs').length > 0)
- segmentSelectionArr[idx].getAttribute('streetIDs').forEach(streetIdsForEach);
- }
- }
- return true;
- }
- function distanceBetweenPoints(lon1, lat1, lon2, lat2, measurement) {
- // eslint-disable-next-line no-nested-ternary
- const multiplier = measurement === 'meters' ? 1000 : measurement === 'miles' ? 0.621371192237334 : measurement === 'feet' ? 3280.8398950131 : 1;
- const R = 6371; // KM
- const φ1 = lat1 * (Math.PI / 180);
- const φ2 = lat2 * (Math.PI / 180);
- const Δφ = (lat2 - lat1) * (Math.PI / 180);
- const Δλ = (lon2 - lon1) * (Math.PI / 180);
- const a = Math.sin(Δφ / 2) * Math.sin(Δφ / 2) + Math.cos(φ1) * Math.cos(φ2) * Math.sin(Δλ / 2) * Math.sin(Δλ / 2);
- const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
- const d = R * c;
- return d * multiplier;
- }
- function checkForMicroDogLegs(distinctNodes, singleSegmentId) {
- if (!distinctNodes || (distinctNodes.length < 1))
- return false;
- const nodesChecked = [],
- nodesObjArr = W.model.nodes.getByIds(distinctNodes);
- if (!nodesObjArr || (nodesObjArr.length < 1))
- return false;
- const checkGeoComp = function (geoComp) {
- const testNode4326 = { lon: geoComp[0], lat: geoComp[1] };
- if ((this.lon !== testNode4326.lon) || (this.lat !== testNode4326.lat)) {
- if (distanceBetweenPoints(this.lon, this.lat, testNode4326.lon, testNode4326.lat, 'meters') < 2)
- return false;
- }
- return true;
- };
- for (let idx = 0, { length } = nodesObjArr; idx < length; idx++) {
- if (!nodesChecked.includes(nodesObjArr[idx])) {
- nodesChecked.push(nodesObjArr[idx]);
- const segmentsObjArr = W.model.segments.getByIds(nodesObjArr[idx].getSegmentIds()) || [],
- node4326 = {
- lon: nodesObjArr[idx].getGeometry().coordinates[0],
- lat: nodesObjArr[idx].getGeometry().coordinates[1]
- };
- for (let idx2 = 0, len = segmentsObjArr.length; idx2 < len; idx2++) {
- const segObj = segmentsObjArr[idx2];
- if (!singleSegmentId
- || (singleSegmentId && (segObj.getID() === singleSegmentId))) {
- if (!segObj.getGeometry().coordinates.every(checkGeoComp.bind(node4326)))
- return true;
- }
- }
- }
- }
- return false;
- }
- function doStraightenSegments(sanityContinue, nonContinuousContinue, conflictingNamesContinue, microDogLegsContinue, longJnMoveContinue, passedObj) {
- const segmentSelection = W.selectionManager.getSegmentSelection();
- if (longJnMoveContinue && passedObj) {
- const {
- segmentsToRemoveGeometryArr, nodesToMoveArr, distinctNodes, endPointNodeIds
- } = passedObj;
- logDebug(`${I18n.t('wmesu.log.StraighteningSegments')}: ${distinctNodes.join(', ')} (${distinctNodes.length})`);
- logDebug(`${I18n.t('wmesu.log.EndPoints')}: ${endPointNodeIds.join(' & ')}`);
- if (segmentsToRemoveGeometryArr?.length > 0) {
- const UpdateSegmentGeometry = require('Waze/Action/UpdateSegmentGeometry');
- segmentsToRemoveGeometryArr.forEach((obj) => {
- W.model.actionManager.add(new UpdateSegmentGeometry(obj.segment, obj.geometry, obj.newGeo));
- logDebug(`${I18n.t('wmesu.log.RemovedGeometryNodes')} # ${obj.segment.getID()}`);
- });
- }
- if (nodesToMoveArr?.length > 0) {
- const MoveNode = require('Waze/Action/MoveNode');
- let straightened = false;
- nodesToMoveArr.forEach((node) => {
- if ((Math.abs(node.geometry.coordinates[0] - node.nodeGeo.coordinates[0]) > 0.00000001) || (Math.abs(node.geometry.coordinates[1] - node.nodeGeo.coordinates[1]) > 0.00000001)) {
- logDebug(`${I18n.t('wmesu.log.MovingJunctionNode')} # ${node.node.getID()} `
- + `- ${I18n.t('wmesu.common.From')}: ${node.geometry.coordinates[0]},${node.geometry.coordinates[1]} - `
- + `${I18n.t('wmesu.common.To')}: ${node.nodeGeo.coordinates[0]},${node.nodeGeo.coordinates[1]}`);
- W.model.actionManager.add(new MoveNode(node.node, node.geometry, node.nodeGeo, node.connectedSegObjs, {}));
- straightened = true;
- }
- });
- if (!straightened) {
- logDebug(I18n.t('wmesu.log.AllNodesStraight'));
- WazeWrap.Alerts.info(_SCRIPT_SHORT_NAME, I18n.t('wmesu.log.AllNodesStraight'));
- }
- }
- }
- else if (segmentSelection.segments.length > 1) {
- const segmentsToRemoveGeometryArr = [],
- nodesToMoveArr = [];
- if ((segmentSelection.segments.length > 10) && !sanityContinue) {
- if (_settings.sanityCheck === 'error') {
- WazeWrap.Alerts.error(_SCRIPT_SHORT_NAME, I18n.t('wmesu.error.TooManySegments'));
- return;
- }
- if (_settings.sanityCheck === 'warning') {
- WazeWrap.Alerts.confirm(
- _SCRIPT_SHORT_NAME,
- I18n.t('wmesu.prompts.SanityCheckConfirm'),
- () => { doStraightenSegments(true, false, false, false, false, undefined); },
- () => { },
- I18n.t('wmesu.common.Yes'),
- I18n.t('wmesu.common.No')
- );
- return;
- }
- }
- sanityContinue = true;
- if ((segmentSelection.multipleConnectedComponents === true) && !nonContinuousContinue) {
- if (_settings.nonContinuousSelection === 'error') {
- WazeWrap.Alerts.error(_SCRIPT_SHORT_NAME, I18n.t('wmesu.error.NonContinuous'));
- return;
- }
- if (_settings.nonContinuousSelection === 'warning') {
- WazeWrap.Alerts.confirm(
- _SCRIPT_SHORT_NAME,
- I18n.t('wmesu.prompts.NonContinuousConfirm'),
- () => { doStraightenSegments(sanityContinue, true, false, false, false, undefined); },
- () => { },
- I18n.t('wmesu.common.Yes'),
- I18n.t('wmesu.common.No')
- );
- return;
- }
- }
- nonContinuousContinue = true;
- if (_settings.conflictingNames !== 'nowarning') {
- const continuousNames = checkNameContinuity(segmentSelection.segments);
- if (!continuousNames && !conflictingNamesContinue && (_settings.conflictingNames === 'error')) {
- WazeWrap.Alerts.error(_SCRIPT_SHORT_NAME, I18n.t('wmesu.error.ConflictingNames'));
- return;
- }
- if (!continuousNames && !conflictingNamesContinue && (_settings.conflictingNames === 'warning')) {
- WazeWrap.Alerts.confirm(
- _SCRIPT_SHORT_NAME,
- I18n.t('wmesu.prompts.ConflictingNamesConfirm'),
- () => { doStraightenSegments(sanityContinue, nonContinuousContinue, true, false, false, undefined); },
- () => { },
- I18n.t('wmesu.common.Yes'),
- I18n.t('wmesu.common.No')
- );
- return;
- }
- }
- conflictingNamesContinue = true;
- const allNodeIds = [],
- dupNodeIds = [];
- let endPointNodeIds,
- longMove = false;
- for (let idx = 0, { length } = segmentSelection.segments; idx < length; idx++) {
- allNodeIds.push(segmentSelection.segments[idx].getFromNode().getID());
- allNodeIds.push(segmentSelection.segments[idx].getToNode().getID());
- if (segmentSelection.segments[idx].type === 'segment') {
- const newGeo = structuredClone(segmentSelection.segments[idx].getGeometry());
- // Remove the geometry nodes
- if (newGeo.coordinates.length > 2) {
- newGeo.coordinates.splice(1, newGeo.coordinates.length - 2);
- segmentsToRemoveGeometryArr.push({ segment: segmentSelection.segments[idx], geometry: segmentSelection.segments[idx].getGeometry(), newGeo });
- }
- }
- }
- allNodeIds.forEach((nodeId, idx) => {
- if (allNodeIds.indexOf(nodeId, idx + 1) > -1) {
- if (!dupNodeIds.includes(nodeId))
- dupNodeIds.push(nodeId);
- }
- });
- const distinctNodes = [...new Set(allNodeIds)];
- if (!microDogLegsContinue && (checkForMicroDogLegs(distinctNodes, undefined) === true)) {
- if (_settings.microDogLegs === 'error') {
- WazeWrap.Alerts.error(_SCRIPT_SHORT_NAME, I18n.t('wmesu.error.MicroDogLegs'));
- return;
- }
- if (_settings.microDogLegs === 'warning') {
- WazeWrap.Alerts.confirm(
- _SCRIPT_SHORT_NAME,
- I18n.t('wmesu.prompts.MicroDogLegsConfirm'),
- () => { doStraightenSegments(sanityContinue, nonContinuousContinue, conflictingNamesContinue, true, false, undefined); },
- () => { },
- I18n.t('wmesu.common.Yes'),
- I18n.t('wmesu.common.No')
- );
- return;
- }
- }
- microDogLegsContinue = true;
- if (segmentSelection.multipleConnectedComponents === false)
- endPointNodeIds = distinctNodes.filter((nodeId) => !dupNodeIds.includes(nodeId));
- else
- endPointNodeIds = [segmentSelection.segments[0].getFromNode().getID(), segmentSelection.segments[(segmentSelection.segments.length - 1)].getToNode().getID()];
- const endPointNodeObjs = W.model.nodes.getByIds(endPointNodeIds),
- endPointNode1Geo = structuredClone(endPointNodeObjs[0].getGeometry()),
- endPointNode2Geo = structuredClone(endPointNodeObjs[1].getGeometry());
- if (getDeltaDirect(endPointNode1Geo.coordinates[0], endPointNode2Geo.coordinates[0]) < 0) {
- let [t] = endPointNode1Geo.coordinates;
- [endPointNode1Geo.coordinates[0]] = endPointNode2Geo.coordinates;
- endPointNode2Geo.coordinates[0] = t;
- [, t] = endPointNode1Geo.coordinates;
- [, endPointNode1Geo.coordinates[1]] = endPointNode2Geo.coordinates;
- endPointNode2Geo.coordinates[1] = t;
- endPointNodeIds.push(endPointNodeIds[0]);
- endPointNodeIds.splice(0, 1);
- endPointNodeObjs.push(endPointNodeObjs[0]);
- endPointNodeObjs.splice(0, 1);
- }
- const a = endPointNode2Geo.coordinates[1] - endPointNode1Geo.coordinates[1],
- b = endPointNode1Geo.coordinates[0] - endPointNode2Geo.coordinates[0],
- c = endPointNode2Geo.coordinates[0] * endPointNode1Geo.coordinates[1] - endPointNode1Geo.coordinates[0] * endPointNode2Geo.coordinates[1];
- distinctNodes.forEach((nodeId) => {
- if (!endPointNodeIds.includes(nodeId)) {
- const node = W.model.nodes.getObjectById(nodeId),
- nodeGeo = structuredClone(node.getGeometry());
- const d = nodeGeo.coordinates[1] * a - nodeGeo.coordinates[0] * b,
- r1 = getIntersectCoord(a, b, c, d);
- nodeGeo.coordinates[0] = r1.x;
- nodeGeo.coordinates[1] = r1.y;
- const connectedSegObjs = {};
- for (let idx = 0, { length } = node.getAttribute('segIDs'); idx < length; idx++) {
- const segId = node.getAttribute('segIDs')[idx];
- connectedSegObjs[segId] = structuredClone(W.model.segments.getObjectById(segId).getGeometry());
- }
- const fromNodeLonLat = { x: node.getGeometry().coordinates[0], y: node.getGeometry().coordinates[1] },
- toNodeLonLat = r1;
- if (distanceBetweenPoints(fromNodeLonLat.x, fromNodeLonLat.y, toNodeLonLat.x, toNodeLonLat.y, 'meters') > 10)
- longMove = true;
- nodesToMoveArr.push({
- node, geometry: node.getGeometry(), nodeGeo, connectedSegObjs
- });
- }
- });
- if (longMove && (_settings.longJnMove === 'error')) {
- WazeWrap.Alerts.error(_SCRIPT_SHORT_NAME, I18n.t('wmesu.error.LongJnMove'));
- return;
- }
- if (longMove && (_settings.longJnMove === 'warning')) {
- WazeWrap.Alerts.confirm(
- _SCRIPT_SHORT_NAME,
- I18n.t('wmesu.prompts.LongJnMoveConfirm'),
- () => {
- doStraightenSegments(sanityContinue, nonContinuousContinue, conflictingNamesContinue, microDogLegsContinue, true, {
- segmentsToRemoveGeometryArr, nodesToMoveArr, distinctNodes, endPointNodeIds
- });
- },
- () => { },
- I18n.t('wmesu.common.Yes'),
- I18n.t('wmesu.common.No')
- );
- return;
- }
- doStraightenSegments(sanityContinue, nonContinuousContinue, conflictingNamesContinue, microDogLegsContinue, true, {
- segmentsToRemoveGeometryArr, nodesToMoveArr, distinctNodes, endPointNodeIds
- });
- }
- else if (segmentSelection.segments.length === 1) {
- const seg = segmentSelection.segments[0];
- if (seg.type === 'segment') {
- if (!microDogLegsContinue && (checkForMicroDogLegs([seg.getFromNode().getID(), seg.getToNode().getID()], seg.getID()) === true)) {
- if (_settings.microDogLegs === 'error') {
- WazeWrap.Alerts.error(_SCRIPT_SHORT_NAME, I18n.t('wmesu.error.MicroDogLegs'));
- return;
- }
- if (_settings.microDogLegs === 'warning') {
- WazeWrap.Alerts.confirm(
- _SCRIPT_SHORT_NAME,
- I18n.t('wmesu.prompts.MicroDogLegsConfirm'),
- () => { doStraightenSegments(sanityContinue, nonContinuousContinue, conflictingNamesContinue, true, false, undefined); },
- () => { },
- I18n.t('wmesu.common.Yes'),
- I18n.t('wmesu.common.No')
- );
- return;
- }
- }
- microDogLegsContinue = true;
- const newGeo = structuredClone(seg.getGeometry());
- // Remove the geometry nodes
- if (newGeo.coordinates.length > 2) {
- const UpdateSegmentGeometry = require('Waze/Action/UpdateSegmentGeometry');
- newGeo.coordinates.splice(1, newGeo.coordinates.length - 2);
- W.model.actionManager.add(new UpdateSegmentGeometry(seg, seg.getGeometry(), newGeo, { createNodes: true, snappedFeatures: undefined }));
- logDebug(`${I18n.t('wmesu.log.RemovedGeometryNodes')} # ${seg.getID()}`);
- }
- }
- }
- else {
- logWarning(I18n.t('wmesu.log.NoSegmentsSelected'));
- }
- }
- function insertSimplifyStreetGeometryButtons() {
- const wmeSuDiv = document.getElementById('WME-SU-div'),
- elem = document.getElementById('segment-edit-general');
- if (!elem)
- return;
- const docFrags = document.createDocumentFragment();
- if (!wmeSuDiv) {
- const contentDiv = createElem('div', { style: 'align-items:center; cursor:pointer; display:flex; font-size:13px; gap:8px; justify-content:flex-start;', textContent: I18n.t('wmesu.StraightenUp') });
- contentDiv.appendChild(createElem('wz-button', {
- id: 'WME-SU', color: 'secondary', size: 'xs', textContent: I18n.t('wmesu.common.DoIt'), title: I18n.t('wmesu.StraightenUpTitle')
- }, [{ click: doStraightenSegments }]));
- const wzCard = createElem('wz-card', { style: '--wz-card-padding:4px 8px; --wz-card-margin:0; --wz-card-width:auto; display:block; margin-bottom:8px;' });
- wzCard.appendChild(contentDiv);
- const divElemRoot = createElem('div', { id: 'WME-SU-div' });
- divElemRoot.appendChild(wzCard);
- docFrags.appendChild(divElemRoot);
- }
- if (docFrags.firstChild)
- elem.insertBefore(docFrags, elem.firstChild);
- }
- function loadTranslations() {
- return new Promise((resolve) => {
- const translations = {
- en: {
- StraightenUp: 'Straighten Up!',
- StraightenUpTitle: 'Click here to straighten the selected segment(s) by removing geometry nodes and moving junction nodes as needed.',
- common: {
- DoIt: 'Do It',
- From: 'from',
- Help: 'Help',
- No: 'No',
- Note: 'Note',
- NothingMajor: 'Nothing major.',
- To: 'to',
- Warning: 'Warning',
- WhatsNew: 'What\'s new',
- Yes: 'Yes'
- },
- error: {
- ConflictingNames: 'You selected segments that do not share at least one name in common amongst all the segments and have the conflicting names setting set to error. '
- + 'Segments not straightened.',
- LongJnMove: 'One or more of the junction nodes that were to be moved would have been moved further than 10m and you have the long junction node move setting set to '
- + 'give error. Segments not straightened.',
- MicroDogLegs: 'One or more of the junctions nodes in the selection have a geonode within 2 meters. This is usually the sign of a micro dog leg (mDL).<br><br>'
- + 'You have the setting for possibe micro doglegs set to give error. Segments not straightened.',
- NonContinuous: 'You selected segments that are not all connected and have the non-continuous selected segments setting set to give error. Segments not straightened.',
- TooManySegments: 'You selected too many segments and have the sanity check setting set to give error. Segments not straightened.'
- },
- help: {
- Note01: 'This script uses the action manager, so changes can be undone before saving.',
- Warning01: 'Enabling (Give warning, No warning) any of these settings can cause unexpected results. Use with caution!',
- Step01: 'Select the starting segment.',
- Step02: 'ALT+click the ending segment.',
- Step02note: 'If the segments you wanted to straighten are not all selected, unselect them and start over using CTRL+click to select each segment instead.',
- Step03: 'Click "Straighten up!" button in the sidebar.'
- },
- log: {
- AllNodesStraight: 'All junction nodes that would be moved are already considered \'straight\'. No junction nodes were moved.',
- EndPoints: 'End points',
- MovingJunctionNode: 'Moving junction node',
- NoSegmentsSelected: 'No segments selected.',
- RemovedGeometryNodes: 'Removed geometry nodes for segment',
- Segment: I18n.t('objects.segment.name'),
- StraighteningSegments: 'Straightening segments'
- },
- prompts: {
- ConflictingNamesConfirm: 'You selected segments that do not share at least one name in common amongst all the segments. Are you sure you wish to continue straightening?',
- LongJnMoveConfirm: 'One or more of the junction nodes that are to be moved would be moved further than 10m. Are you sure you wish to continue straightening?',
- MicroDogLegsConfirm: 'One or more of the junction nodes in the selection have a geonode within 2 meters. This is usually the sign of a micro dog leg (mDL).<br>'
- + 'This geonode could exist on any segment connected to the junction nodes, not just the segments you selected.<br><br>'
- + '<b>You should not continue until you are certain there are no micro dog legs.<b><br><br>'
- + 'Are you sure you wish to continue straightening?',
- NonContinuousConfirm: 'You selected segments that do not all connect. Are you sure you wish to continue straightening?',
- SanityCheckConfirm: 'You selected many segments. Are you sure you wish to continue straightening?'
- },
- settings: {
- GiveError: 'Give error',
- GiveWarning: 'Give warning',
- NoWarning: 'No warning',
- ConflictingNames: 'Segments with conflicting names',
- ConflictingNamesTitle: 'Select what to do if the selected segments do not share at least one name among their primary and alternate names (based on name, city and state).',
- LongJnMove: 'Long junction node moves',
- LongJnMoveTitle: 'Select what to do if one or more of the junction nodes would move further than 10m.',
- MicroDogLegs: 'Possible micro doglegs (mDL)',
- MicroDogLegsTitle: 'Select what to do if one or more of the junction nodes in the selection have a geometry node within 2m of itself, which is a possible micro dogleg (mDL).',
- NonContinuousSelection: 'Non-continuous selected segments',
- NonContinuousSelectionTitle: 'Select what to do if the selected segments are not continuous.',
- SanityCheck: 'Sanity check',
- SanityCheckTitle: 'Select what to do if you selected a many segments.'
- }
- },
- ru: {
- StraightenUp: 'Выпрямить сегменты!',
- StraightenUpTitle: 'Нажмите, чтобы выпрямить выбранные сегменты, удалив лишние геометрические точки и переместив узлы перекрёстков в ровную линию.',
- common: {
- DoIt: 'Сделай это',
- From: 'с',
- Help: 'Помощь',
- No: 'Нет',
- Note: 'Примечание',
- NothingMajor: 'Не критично.',
- To: 'до',
- Warning: 'Предупреждение',
- WhatsNew: 'Что нового',
- Yes: 'Да'
- },
- error: {
- ConflictingNames: 'Вы выбрали сегменты, которые не имеют хотя бы одного общего названия улицы среди выделенных.'
- + 'Сегменты не были выпрямлены.',
- LongJnMove: 'Для выпрямления сегментов, их узлы должны быть перемещены более чем на 10 м, но в настройках у вас установлено ограничение перемещения на такое большое '
- + 'расстояние. Сегменты не были выпрямлены.',
- MicroDogLegs: 'Один или несколько узлов выбранных сегментов имеют точку в пределах 2 метров. Обычно это признак “<a href=”https://wazeopedia.waze.com/wiki/Benelux/Junction_Arrows” target=”blank”>микроискривления</a>”.<br><br>'
- + 'В настройках для возможных микроискривлений у вас выставлено ограничение, чтобы выдать ошибку. Сегменты не были выпрямлены.',
- NonContinuous: 'Вы выбрали сегменты, которые не соединены между собой, но в настройках у вас установлено ограничение для работы с такими сегментами. Сегменты не были '
- + 'выпрямлены.',
- TooManySegments: 'Вы выбрали слишком много сегментов, но в настройках у вас включено ограничение на количество одновременно обрабатываемых сегментов. Сегменты не были '
- + 'выпрямлены.'
- },
- help: {
- Note01: 'Этот скрипт использует историю действий, поэтому перед их сохранением изменения можно отменить.',
- Warning01: 'Настройка любого из этих параметров в положение (Выдать предупреждение, Не предупреждать) может привести к неожиданным результатам. Используйте с осторожностью!',
- Step01: 'Выделите начальный сегмент.',
- Step02: 'При помощи Alt-кнопки, выделите конечный сегмент.',
- Step02note: 'Если выделены не все нужные вам сегменты, при помощи Ctrl-кнопки можно дополнительно выделить или снять выделения сегментов.',
- Step03: 'Нажмите ‘Выпрямить сегменты!’ на левой панели.'
- },
- log: {
- AllNodesStraight: 'Все узлы, которые нужно было выпрямить, уже выровнены в линию. Сегменты оставлены без изменений.',
- EndPoints: 'конечные точки',
- MovingJunctionNode: 'Перемещение узла',
- NoSegmentsSelected: 'Сегменты не выделены.',
- RemovedGeometryNodes: 'Удалены лишние точки сегмента',
- Segment: I18n.t('objects.segment.name'),
- StraighteningSegments: 'Выпрямление сегментов'
- },
- prompts: {
- ConflictingNamesConfirm: 'Вы выбрали сегменты, которые не имеют хотя бы одного общего названия среди всех сегментов. Вы уверены, что хотите продолжить выпрямление?',
- LongJnMoveConfirm: 'Один или несколько узлов будут перемещены более, чем на 10 метров. Вы уверены, что хотите продолжить выпрямление?',
- MicroDogLegsConfirm: 'Один или несколько узлов выбранных сегментов имеют точки в пределах 2 метров. Обычно это признак “<a href=”https://wazeopedia.waze.com/wiki/Benelux/Junction_Arrows” target=”blank”>микроискривления</a>”.<br>'
- + 'Такая точка может находиться в любом сегменте, соединенном с выбранными вами сегментами и узлами, а не только на них самих.<br><br>'
- + '<b>Вы не должны продолжать до тех пор, пока не убедитесь, что у вас нет “микроискривлений”.<b><br><br>'
- + 'Вы уверены,что готовы продолжать выпрямление?',
- NonContinuousConfirm: 'Вы выбрали сегменты, которые не соединяются друг с другом. Вы уверены, что хотите продолжить выпрямление?',
- SanityCheckConfirm: 'Вы выбрали слишком много сегментов. Вы уверены, что хотите продолжить выпрямление?'
- },
- settings: {
- GiveError: 'Выдать ошибку',
- GiveWarning: 'Выдать предупреждение',
- NoWarning: 'Не предупреждать',
- ConflictingNames: 'Сегменты с разными названиями',
- ConflictingNamesTitle: 'Выберите, что делать, если выбранные сегменты не содержат хотя бы одно название среди своих основных и альтернативных названий (на основе улицы, '
- + 'города и района).',
- LongJnMove: 'Перемещение узлов на большие расстояния',
- LongJnMoveTitle: 'Выберите, что делать, если один или несколько узлов будут перемещаться дальше, чем на 10 метров.',
- MicroDogLegs: 'Допускать “<a href=”https://wazeopedia.waze.com/wiki/Benelux/Junction_Arrows” target=”blank”>микроискривления</a>”',
- MicroDogLegsTitle: 'Выберите, что делать, если один или несколько узлов соединения в выделении имеют точку в пределах 2 м от себя, что является возможным “микроискривлением”.',
- NonContinuous: 'Не соединённые сегменты',
- NonContinuousTitle: 'Выберите, что делать, если выбранные сегменты не соединены друг с другом.',
- SanityCheck: 'Ограничение нагрузки',
- SanityCheckTitle: 'Выберите, что делать, если вы выбрали слишком много сегментов.'
- }
- }
- },
- locale = I18n.currentLocale();
- I18n.translations[locale].wmesu = translations.en;
- translations['en-US'] = { ...translations.en };
- I18n.translations[locale].wmesu = $extend(true, {}, translations.en, translations[locale]);
- resolve();
- });
- }
- function checkSuVersion() {
- if (_IS_ALPHA_VERSION)
- return;
- let updateMonitor;
- try {
- updateMonitor = new WazeWrap.Alerts.ScriptUpdateMonitor(_SCRIPT_LONG_NAME, _SCRIPT_VERSION, (_IS_BETA_VERSION ? dec(_BETA_DL_URL) : _PROD_DL_URL), GM_xmlhttpRequest);
- updateMonitor.start();
- }
- catch (err) {
- logError('Upgrade version check:', err);
- }
- }
- async function onWazeWrapReady() {
- log('Initializing.');
- checkSuVersion();
- if (W.loginManager.getUserRank() < 2)
- return;
- await loadSettingsFromStorage();
- await loadTranslations();
- const onSelectionChange = function () {
- const setting = this.id.substr(6);
- if (this.value.toLowerCase() !== _settings[setting]) {
- _settings[setting] = this.value.toLowerCase();
- saveSettingsToStorage();
- }
- },
- buildSelections = (selected) => {
- const docFrags = document.createDocumentFragment();
- docFrags.appendChild(createElem('option', { value: 'nowarning', selected: selected === 'nowarning', textContent: I18n.t('wmesu.settings.NoWarning') }));
- docFrags.appendChild(createElem('option', { value: 'warning', selected: selected === 'warning', textContent: I18n.t('wmesu.settings.GiveWarning') }));
- docFrags.appendChild(createElem('option', { value: 'error', selected: selected === 'error', textContent: I18n.t('wmesu.settings.GiveError') }));
- return docFrags;
- },
- buildSection = (section) => {
- const selectElem = createElem('select', {
- id: `WMESU-${section}`,
- style: 'font-size:11px;height:22px;',
- title: I18n.t(`wmesu.settings.${section.charAt(0).toUpperCase()}${section.slice(1)}Title`)
- }, [{ change: onSelectionChange }]);
- selectElem.appendChild(buildSelections(_settings[section]));
- const divElemDiv = createElem('div', { id: `WMESU-div-${section}`, class: 'controls-container' });
- divElemDiv.appendChild(selectElem);
- const divElemDivDiv = createElem('div', { style: 'display:inline-block;font-size:11px;', textContent: I18n.t(`wmesu.settings.${section.charAt(0).toUpperCase()}${section.slice(1)}`) });
- divElemDiv.appendChild(divElemDivDiv);
- return divElemDiv;
- },
- tabContent = () => {
- const docFrags = document.createDocumentFragment();
- docFrags.appendChild(createElem('div', { style: 'margin-bottom:0px;font-size:13px;font-weight:600;', textContent: _SCRIPT_SHORT_NAME }));
- docFrags.appendChild(createElem('div', { style: 'margin-top:0px;font-size:11px;font-weight:600;color:#aaa;', textContent: _SCRIPT_VERSION }));
- docFrags.appendChild(buildSection('conflictingNames'));
- docFrags.appendChild(buildSection('longJnMove'));
- docFrags.appendChild(buildSection('microDogLegs'));
- docFrags.appendChild(buildSection('nonContinuousSelection'));
- docFrags.appendChild(buildSection('sanityCheck'));
- const divElemDiv = createElem('div', { style: 'margin-top:20px;' });
- divElemDiv.appendChild(createElem('div', { style: 'font-size:14px;font-weight:600;', textContent: I18n.t('wmesu.common.Help') }));
- let liElem = createElem('li');
- liElem.appendChild(createElem('p', { style: 'font-weight:100;margin-bottom:0px;', textContent: I18n.t('wmesu.help.Step01') }));
- const olElem = createElem('ol', { style: 'font-weight:600;' });
- olElem.appendChild(liElem);
- const pElem = createElem('p', { style: 'font-weight:100;margin-bottom:0px;' });
- pElem.appendChild(createTextNode(I18n.t('wmesu.help.Step02')));
- pElem.appendChild(createElem('br'));
- pElem.appendChild(createElem('b', { textContent: `${I18n.t('wmesu.common.Note')}:` }));
- pElem.appendChild(createTextNode(` ${I18n.t('wmesu.help.Step02note')}`));
- liElem = createElem('li');
- liElem.appendChild(pElem);
- olElem.appendChild(liElem);
- liElem = createElem('li');
- liElem.appendChild(createElem('p', { style: 'font-weight:100;margin-bottom:0px;', textContent: I18n.t('wmesu.help.Step03') }));
- olElem.appendChild(liElem);
- const divElemDivDiv = createElem('div');
- divElemDivDiv.appendChild(olElem);
- divElemDiv.appendChild(divElemDivDiv);
- divElemDiv.appendChild(createElem('b', { textContent: `${I18n.t('wmesu.common.Warning')}:` }));
- divElemDiv.appendChild(createTextNode(` ${I18n.t('wmesu.help.Warning01')}`));
- divElemDiv.appendChild(createElem('br'));
- divElemDiv.appendChild(createElem('br'));
- divElemDiv.appendChild(createElem('b', { textContent: `${I18n.t('wmesu.common.Note')}:` }));
- divElemDiv.appendChild(createTextNode(` ${I18n.t('wmesu.help.Note01')}`));
- docFrags.appendChild(divElemDiv);
- return docFrags;
- };
- const { tabLabel, tabPane } = W.userscripts.registerSidebarTab('SU!');
- tabLabel.textContent = 'SU!';
- tabLabel.title = _SCRIPT_LONG_NAME;
- tabPane.appendChild(tabContent());
- tabPane.id = 'WMESUSettings';
- await W.userscripts.waitForElementConnected(tabPane);
- logDebug('Enabling MOs.');
- W.selectionManager.events.register('selectionchanged', null, insertSimplifyStreetGeometryButtons);
- if (W.selectionManager.getSegmentSelection().segments.length > 0)
- insertSimplifyStreetGeometryButtons();
- window.addEventListener('beforeunload', () => { checkShortcutChanged(); }, false);
- new WazeWrap.Interface.Shortcut(
- 'runStraightenUpShortcut',
- 'Run straighten up',
- 'editing',
- 'Straighten Up',
- _settings.runStraightenUpShortcut,
- () => document.getElementById('WME-SU')?.dispatchEvent(new MouseEvent('click', { bubbles: true })),
- null
- ).add();
- showScriptInfoAlert();
- log(`Fully initialized in ${Math.round(performance.now() - _LOAD_BEGIN_TIME)} ms.`);
- setTimeout(checkShortcutChanged, 10000);
- }
- function onWmeReady(tries = 1) {
- if (typeof tries === 'object')
- tries = 1;
- checkTimeout({ timeout: 'onWmeReady' });
- if (WazeWrap?.Ready) {
- logDebug('WazeWrap is ready. Proceeding with initialization.');
- onWazeWrapReady();
- }
- else if (tries < 1000) {
- logDebug(`WazeWrap is not in Ready state. Retrying ${tries} of 1000.`);
- _timeouts.onWmeReady = window.setTimeout(onWmeReady, 200, ++tries);
- }
- else {
- logError('onWmeReady timed out waiting for WazeWrap Ready state.');
- }
- }
- function onWmeInitialized() {
- if (W.userscripts?.state?.isReady) {
- logDebug('W is ready and already in "wme-ready" state. Proceeding with initialization.');
- onWmeReady();
- }
- else {
- logDebug('W is ready, but state is not "wme-ready". Adding event listener.');
- document.addEventListener('wme-ready', onWmeReady, { once: true });
- }
- }
- function bootstrap() {
- if (!W) {
- logDebug('W is not available. Adding event listener.');
- document.addEventListener('wme-initialized', onWmeInitialized, { once: true });
- }
- else {
- onWmeInitialized();
- }
- }
- bootstrap();
- }
- )();