您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Show the angle between two selected (and connected) segments
当前为
// ==UserScript== // @name WME Junction Angle Info // @description Show the angle between two selected (and connected) segments // @include https://www.waze.com/editor* // @include https://www.waze.com/*/editor* // @include https://beta.waze.com/editor* // @include https://beta.waze.com/*/editor* // @exclude https://www.waze.com/user* // @exclude https://www.waze.com/*/user* // @version 2.2.1 // @grant GM_addElement // @namespace https://gf.qytechs.cn/scripts/35547-wme-junction-angle-info/ // @copyright 2018 seb-d59, 2016 Michael Wikberg <[email protected]> // @license CC-BY-NC-SA // @icon data:image/png;base64,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 // ==/UserScript== /** * Copyright 2016 Michael Wikberg <[email protected]> * WME Junction Angle Info extension is licensed under a Creative Commons * Attribution-NonCommercial-ShareAlike 3.0 Unported License. * * Contributions by: * 2014 Paweł Pyrczak "tkr85" <[email protected]> * 2014 "AlanOfTheBerg" <[email protected]> * 2014 "berestovskyy" <?> * 2015 "FZ69617" <?> * 2015 "wlodek76" <?> * 2016 Sergey Kuznetsov "WazeRus" <[email protected]> (Russian translation) * 2016 "MajkiiTelini" <?> Czech translation * 2016 "witoco" <?> (Latin-American Spanish translation) * 2017 "seb-d59" (Check override instruction and French translation) <https://www.waze.com/forum/memberlist.php?mode=viewprofile&u=16863068> * 2019 thank to Sapozhnik for the Ukrainian (український) translation */ /*jshint eqnull:true, nonew:true, nomen:true, curly:true, latedef:true, unused:strict, noarg:true, loopfunc:true */ /*jshint trailing:true, forin:true, noempty:true, maxparams:7, maxerr:100, eqeqeq:true, strict:true, undef:true */ /*jshint bitwise:true, newcap:true, immed:true, onevar:true, browser:true, nonbsp:true, freeze:true */ /*global I18n, console, $*/ function run_ja() { "use strict"; /* * First some variable and enumeration definitions */ var junctionangle_version = "2.2.1"; var junctionangle_debug = 1; //0: no output, 1: basic info, 2: debug 3: verbose debug, 4: insane debug var ja_last_restart = 0, ja_roundabout_points = [], ja_options = {}, ja_mapLayer; var TURN_ANGLE = 45.50; //Turn vs. keep angle - based on map experiments (45.04 specified in Wiki). var U_TURN_ANGLE = 168.24; //U-Turn angle based on map experiments. var GRAY_ZONE = 1.5; //Gray zone angle intended to prevent from irregularities observed on map. var OVERLAPPING_ANGLE = 0.666; //Experimentally measured overlapping angle. var ja_routing_type = { BC: "junction_none", KEEP: "junction_keep", KEEP_LEFT: "junction_keep_left", KEEP_RIGHT: "junction_keep_right", TURN: "junction_turn", TURN_LEFT: "junction_turn_left", TURN_RIGHT: "junction_turn_right", EXIT: "junction_exit", EXIT_LEFT: "junction_exit_left", EXIT_RIGHT: "junction_exit_right", U_TURN: "junction_u_turn", PROBLEM: "junction_problem", NO_TURN: "junction_no_turn", NO_U_TURN: "junction_no_u_turn", ROUNDABOUT: "junction_roundabout", ROUNDABOUT_EXIT: "junction_roundabout_exit", OverrideBC: "Override_none", OverrideCONTINUE: "Override_continue", OverrideKEEP_LEFT: "Override_keep_left", OverrideKEEP_RIGHT: "Override_keep_right", OverrideTURN_LEFT: "Override_turn_left", OverrideTURN_RIGHT: "Override_turn_right", OverrideEXIT: "Override_exit", OverrideEXIT_LEFT: "Override_exit_left", OverrideEXIT_RIGHT: "Override_exit_right", OverrideU_TURN: "Override_u_turn" }; var ja_road_type = { //Streets NARROW_STREET: 22, STREET: 1, PRIMARY_STREET: 2, //Highways RAMP: 4, FREEWAY: 3, MAJOR_HIGHWAY: 6, MINOR_HIGHWAY: 7, //Other drivable DIRT_ROAD: 8, FERRY: 14, PRIVATE_ROAD: 17, PARKING_LOT_ROAD: 20, //Non-drivable WALKING_TRAIL: 5, PEDESTRIAN_BOARDWALK: 10, STAIRWAY: 16, RAILROAD: 18, RUNWAY: 19 }; var ja_vehicle_types = { TRUCK: 1, PUBLIC: 2, TAXI: 4, BUS: 8, HOV2: 16, HOV3: 32, RV: 64, TOWING: 128, MOTORBIKE: 256, PRIVATE: 512, HAZ: 1024 }; var ja_settings = { defaultOn: { elementType: "checkbox", elementId: "_jaCbShowLayer", defaultValue: true }, angleMode: { elementType: "select", elementId: "_jaSelAngleMode", defaultValue: "aDeparture", options: ["aAbsolute", "aDeparture"]}, angleDisplay: { elementType: "select", elementId: "_jaSelAngleDisplay", defaultValue: "displayFancy", options: ["displayFancy", "displaySimple"]}, angleDisplayArrows: { elementType: "select", elementId: "_jaSelAngleDisplayArrows", defaultValue: "⇐⇒⇖⇗⇑", options: ["<><>^", "⇦⇨⇦⇨⇧", "⇐⇒⇐⇒⇑", "←→←→↑", "⇐⇒⇖⇗⇑", "←→↖↗↑"]}, override: { elementType: "checkbox", elementId: "_jaCbOverride", defaultValue: true, group: "guess" }, overrideAngles: { elementType: "checkbox", elementId: "_jaCboverrideAngles", defaultValue: false, group: "override" }, guess: { elementType: "checkbox", elementId: "_jaCbGuessRouting", defaultValue: true }, noInstructionColor: { elementType: "color", elementId: "_jaTbNoInstructionColor", defaultValue: "#ffffff", group: "guess"}, continueInstructionColor: { elementType: "color", elementId: "_jaTbContinueInstructionColor", defaultValue: "#ffffff", group: "guess"}, keepInstructionColor: { elementType: "color", elementId: "_jaTbKeepInstructionColor", defaultValue: "#cbff84", group: "guess"}, exitInstructionColor: { elementType: "color", elementId: "_jaTbExitInstructionColor", defaultValue: "#6cb5ff", group: "guess"}, turnInstructionColor: { elementType: "color", elementId: "_jaTbTurnInstructionColor", defaultValue: "#4cc600", group: "guess"}, uTurnInstructionColor: { elementType: "color", elementId: "_jaTbUTurnInstructionColor", defaultValue: "#b66cff", group: "guess"}, noTurnColor: { elementType: "color", elementId: "_jaTbNoTurnColor", defaultValue: "#a0a0a0", group: "guess"}, problemColor: { elementType: "color", elementId: "_jaTbProblemColor", defaultValue: "#feed40", group: "guess"}, roundaboutOverlayDisplay: { elementType: "select", elementId: "_jaSelRoundaboutOverlayDisplay", defaultValue: "rOverNever", options: ["rOverNever","rOverSelected","rOverAlways"]}, roundaboutOverlayColor: { elementType: "color", elementId: "_jaTbRoundaboutOverlayColor", defaultValue: "#aa0000", group: "roundaboutOverlayDisplay"}, roundaboutColor: { elementType: "color", elementId: "_jaTbRoundaboutColor", defaultValue: "#ff8000", group: "roundaboutOverlayDisplay"}, decimals: { elementType: "number", elementId: "_jaTbDecimals", defaultValue: 2, min: 0, max: 2}, pointSize: { elementType: "number", elementId: "_jaTbPointSize", defaultValue: 12, min: 6, max: 20} }; var ja_arrow = { get: function(at) { var arrows = ja_getOption("angleDisplayArrows"); return arrows[at % arrows.length]; }, left: function() { return this.get(0); }, right: function() { return this.get(1); }, left_up: function() { return this.get(2); }, right_up: function() { return this.get(3); }, up: function() { return this.get(4); } }; var ja_calculation_Interval = { set: function(){ this.clear(); ja_log("Starting IntervalID", 2); this.IntervalID = window.setInterval(ja_calculate, 500); //console.log("this",this); }, clear: function(){ if(typeof this.IntervalID === "number"){ ja_log("Cleared IntervalID ID : " + this.IntervalID, 2); window.clearInterval(this.IntervalID); delete this.IntervalID; ja_calculate(); //console.log("this",this); } } }; function testSelectedItem(){ if (window.W.selectionManager.getSelectedFeatures().length > 1) { return; } var registerInterval = false; window.W.selectionManager.getSelectedFeatures().forEach(function(element) { switch (element.model.type) { case "node": case "segment": registerInterval = true; break; default: break; } }); if (registerInterval == true){ ja_calculation_Interval.set(); }else{ ja_calculation_Interval.clear(); } ja_log("ja_calculation_Interval.start = " + registerInterval,2); } /* * Main logic functions */ function junctionangle_init() { //Listen for selected nodes change event //window.W.selectionManager.events.register("selectionchanged", null, ja_calculate); window.W.selectionManager.events.register("selectionchanged", null, testSelectedItem); //Temporary workaround. Beta editor changed the event listener logic, but live is still using the old version //if-else should be removed once not needed anymore /* if("events" in window.W.model.segments) { //Live window.W.model.segments.events.on({ "objectschanged": ja_calculate, "objectsremoved": ja_calculate }); window.W.model.nodes.events.on({ "objectschanged": ja_calculate, "objectsremoved": ja_calculate }); } else if("_events" in window.W.model.segments) { */ //Beta editor window.W.model.segments.on({ "objectschanged": ja_calculate, "objectsremoved": ja_calculate }); window.W.model.nodes.on({ "objectschanged": ja_calculate, "objectsremoved": ja_calculate }); // } //Recalculate on zoom end also window.W.map.olMap.events.register("zoomend", null, ja_calculate); window.W.map.olMap.events.register("move", null, ja_calculate); ja_load(); ja_loadTranslations(); setupHtml(); //Add support for translations. Default (and fallback) is "en". //Note, don't make typos in "acceleratorName", as it has to match the layer name (with whitespace removed) // to actually work. Took me a while to figure that out... I18n.translations[window.I18n.locale].layers.name.junction_angles = ja_getMessage("name"); /** * Initialize JAI OpenLayers vector layer */ if (window.W.map.getLayersBy("uniqueName","junction_angles").length === 0) { // Create a vector layer and give it your style map. ja_mapLayer = new window.OpenLayers.Layer.Vector(ja_getMessage("name"), { displayInLayerSwitcher: true, uniqueName: "junction_angles", shortcutKey: "S+j", accelerator: "toggle" + ja_getMessage("name").replace(/\s+/g,''), className: "junction-angles", styleMap: new window.OpenLayers.StyleMap(ja_style()) }); //Set visibility according to user preference //ja_mapLayer.setVisibility(ja_getOption("defaultOn")); window.W.map.addLayer(ja_mapLayer); ja_log("version " + junctionangle_version + " loaded.", 0); ja_log(window.W.map, 3); ja_log(window.W.model, 3); ja_log(window.W.loginManager, 3); ja_log(window.W.selectionManager, 3); ja_log(ja_mapLayer, 3); ja_log(window.OpenLayers, 3); } else { ja_log("Oh, nice.. We already had a layer?", 3); } createToggler(); ja_apply(); // MTE mode event /* W.app.modeController.model.bind('change:mode', function(){ if (W.app.modeController.getState() === undefined){ createToggler(); setupHtml(); ja_apply(); } }); */ // reload after changing WME units W.prefs.on('change:isImperial', function(){ createToggler(); setupHtml(); ja_apply(); }); ja_calculate(); } function setupHtml(){ var i, ja_select_option, navTabs, tabContent; var ja_settings_dom = document.createElement("div"); var ja_settings_dom_panel = document.createElement("div"); var ja_settings_dom_content = document.createElement("div"); var ja_settings_header = document.createElement('h4'); var style = document.createElement('style'); var form = document.createElement('form'); var section = document.createElement('div'); var ja_reset_button = document.createElement('button'); var userTabs = document.getElementById('user-info'); var ja_info = document.createElement('ul'); var ja_version_elem = document.createElement('li'); var jatab = document.createElement('li'); /** * Add JAI tab configuration options */ ja_settings_dom_panel.className = "side-panel-section"; ja_settings_dom_content.className = "tab-content"; ja_settings_header.appendChild(document.createTextNode(ja_getMessage("settingsTitle"))); ja_settings_dom_content.appendChild(ja_settings_header); style.appendChild(document.createTextNode(function () {/* #jaOptions > *:first-child { margin-top: 1em; } #jaOptions * { vertical-align: middle; } #jaOptions label { display: inline; } #jaOptions input, select { display: inline; margin-right: 7px; box-sizing: border-box; border: 1px solid #cccccc; border-radius: 5px; padding: 3px; } #jaOptions input[type="number"] { width: 4em; padding: 6px; } #jaOptions input[type="color"] { width: 15%; height: 2em; padding: 4px; } @supports (-webkit-appearance:none) { #jaOptions input[type="color"] { padding: 0px 2px 0px 2px; } } #jaOptions .disabled { position: relative; } #jaOptions .disabled:after { content: " "; z-index: 10; display: block; position: absolute; height: 100%; top: 0; left: 0; right: 0; background: rgba(255, 255, 255, 0.666); } */}.toString().match(/[^]*\/\*([^]*)\*\/\}$/)[1])); section.className = "form-group"; form.className = "attributes-form side-panel-section"; section.id = "jaOptions"; ja_log("---------- Creating settings HTML ----------", 2); Object.getOwnPropertyNames(ja_settings).forEach(function (a) { var setting = ja_settings[a]; var ja_controls_container = document.createElement('div'); var ja_input = document.createElement('input'); var ja_label = document.createElement('label'); ja_controls_container.className = "controls-container"; ja_input.type = setting.elementType; switch (setting.elementType) { case 'color': ja_input.id = setting.elementId; ja_controls_container.appendChild(ja_input); break; case 'number': ja_input.id = setting.elementId; ja_input.setAttribute("min", setting.min); ja_input.setAttribute("max", setting.max); ja_controls_container.appendChild(ja_input); break; /* case 'text': ja_input.id = setting.elementId; ja_input.size = (setting.max ? setting.max : 8); ja_input.maxlength = (setting.max ? setting.max : 7); ja_controls_container.appendChild(ja_input); break; */ case 'checkbox': ja_input.id = setting.elementId; ja_controls_container.appendChild(ja_input); break; case 'select': ja_input = document.createElement('select'); //Override <input> with <select> ja_input.id = setting.elementId; for(i = 0; i < setting.options.length; i++) { ja_select_option = document.createElement('option'); ja_select_option.value = setting.options[i]; ja_select_option.appendChild(document.createTextNode(ja_getMessage(setting.options[i]))); ja_input.appendChild(ja_select_option); } ja_controls_container.appendChild(ja_input); break; default: ja_log("Unknown setting type " + setting.elementType, 2); } ja_input.onchange = function() { ja_onchange(this); }; ja_label.setAttribute("for", setting.elementId); ja_label.appendChild(document.createTextNode(ja_getMessage(a))); ja_controls_container.appendChild(ja_label); section.appendChild(ja_controls_container); }); section.appendChild(document.createElement('br')); ja_reset_button.type = "button"; ja_reset_button.className = "btn btn-default"; ja_reset_button.addEventListener("click", ja_reset, true); ja_reset_button.appendChild(document.createTextNode(ja_getMessage("resetToDefault"))); section.appendChild(document.createElement('div')); section.appendChild(ja_reset_button); form.appendChild(section); ja_settings_dom_content.appendChild(form); navTabs = userTabs.getElementsByClassName('nav-tabs')[0]; tabContent = userTabs.getElementsByClassName('tab-content')[0]; ja_settings_dom.id = "sidepanel-ja"; ja_settings_dom.className = "tab-pane"; ja_settings_dom_content.style.paddingTop = "0"; ja_settings_dom.appendChild(style); ja_settings_dom_panel.appendChild(ja_settings_dom_content); ja_settings_dom.appendChild(ja_settings_dom_panel); //Add some version info etc ja_info.className = "list-unstyled -side-panel-section"; ja_info.style.fontSize = "11px"; ja_version_elem.appendChild(document.createTextNode(ja_getMessage("name") + ": v" + junctionangle_version)); ja_info.appendChild(ja_version_elem); //Add some useful links ja_info.appendChild(ja_helpLink( 'https://wiki.waze.com/wiki/Roundabouts/USA#Understanding_navigation_instructions', 'roundaboutnav') ); ja_info.appendChild(ja_helpLink('https://www.waze.com/forum/viewtopic.php?f=819&t=61926', 'ghissues')); ja_settings_dom.appendChild(ja_info); tabContent.appendChild(ja_settings_dom); jatab.innerHTML = '<!--suppress HtmlUnknownAnchorTarget --><a href="#sidepanel-ja" data-toggle="tab">JAI</a>'; if(navTabs != null) { navTabs.appendChild(jatab); } } function createToggler(){ // script group's toggler---------------- var oldTogglers = document.querySelectorAll('.togglers'); oldTogglers.forEach(function(elt,idx){ if(elt.id != "toolboxUl"){ // if script group dosn't exist we create them. if (oldTogglers[idx].querySelector('.layer-switcher-group_scripts') === null) { var newScriptsToggler = document.createElement('li'); newScriptsToggler.className = 'group'; newScriptsToggler.innerHTML = '<div class="controls-container main toggler">\ <input class="layer-switcher-group_scripts toggle" id="layer-switcher-group_scripts" type="checkbox">\ <label for="layer-switcher-group_scripts">\ <span class="label-text">Scripts</span>\ </label>\ </div>\ <ul class="children">\ </ul>'; oldTogglers[idx].appendChild(newScriptsToggler); } // JAI toggler var newToggler = document.createElement('li'); newToggler.innerHTML = '<div class="controls-container toggler">\ <input class="layer-switcher-item_junction_angles toggle" id="layer-switcher-item_junction_angles" type="checkbox">\ <label for="layer-switcher-item_junction_angles">\ <span class="label-text">'+ja_getMessage("name")+'</span>\ </label>\ </div>'; var groupScripts = document.querySelector('.layer-switcher-group_scripts').parentNode.parentNode; var newScriptsChildren = groupScripts.getElementsByClassName("children")[0]; // insert JAI toggler at the end of children of "group_scripts" newScriptsChildren.appendChild(newToggler); var toggler = document.getElementById('layer-switcher-item_junction_angles'); var groupToggler = document.getElementById('layer-switcher-group_scripts'); // restore old state groupToggler.checked = (typeof(localStorage.groupScriptsToggler) !=="undefined" ? JSON.parse(localStorage.groupScriptsToggler) : true); //Set toggler according to user preference toggler.checked = ja_getOption("defaultOn"); toggler.disabled = !groupToggler.checked; // togglers events toggler.addEventListener('click', function(e) { ja_mapLayer.setVisibility(e.target.checked); }); groupToggler.addEventListener('click', function(e) { toggler.disabled = !e.target.checked; ja_mapLayer.setVisibility(toggler.checked ? e.target.checked : toggler.checked); localStorage.setItem('groupScriptsToggler', e.target.checked); }); //Set visibility according JAI's toggler and scripts group's toggler state ja_mapLayer.setVisibility(toggler.checked ? groupToggler.checked : toggler.checked); } }); } function ja_guess_routing_instruction(node, s_in_a, s_out_a, angles) { /********************************************************************************** * @param node Junction node * @param s_in_a "In" segment id * @param s_out_a "Out" segment id * @param angles array of segment absolute angles [0] angle, [1] segment id, 2[?] * @returns {string} **********************************************************************************/ var s_n = {}, s_in = null, s_out = {}, street_n = {}, street_in = null, angle; var s_in_id = s_in_a; var s_out_id = s_out_a; ja_log("Guessing routing instructions from " + s_in_a + " via node " + node.attributes.id + " to " + s_out_a,2); ja_log(node, 4); ja_log(s_in_a, 4); ja_log(s_out_a, 4); ja_log(angles, 3); s_in_a = window.$.grep(angles, function(element){ return element[1] === s_in_a; }); s_out_a = window.$.grep(angles, function(element){ return element[1] === s_out_a; }); node.attributes.segIDs.forEach(function(element) { if (element === s_in_id) { s_in = getByID(node.model.segments,element); street_in = ja_get_streets(element); //Set empty name for streets if not defined if(typeof street_in.primary === 'undefined') { street_in.primary = {}; } if(typeof street_in.primary.name === 'undefined') { street_in.primary.name = ""; } } else { if(element === s_out_id) { //store for later use s_out[element] = getByID(node.model.segments,element); //Set empty name for streets if not defined if(typeof s_out[element].primary === 'undefined') { s_out[element].primary = { name: "" }; } } s_n[element] = getByID(node.model.segments,element); street_n[element] = ja_get_streets(element); if(typeof street_n[element].primary === 'undefined') { street_n[element].primary = { name: ""}; } } }); ja_log(s_n, 3); ja_log(street_n,3); ja_log(s_in,3); ja_log(street_in,2); if (s_in === null || street_in === null) { //Should never happen, but adding to make code validation happy return ja_routing_type.PROBLEM; } angle = ja_angle_diff(s_in_a[0], (s_out_a[0]), false); ja_log("turn angle is: " + angle, 2); //Check turn possibility first if(!ja_is_turn_allowed(s_in, node, s_out[s_out_id])) { ja_log("Turn is disallowed!", 2); return ja_routing_type.NO_TURN; } //seb-d59: //Check override instruction if (ja_getOption("override")){ var WazeModelGraphTurnData = window.require("Waze/Model/Graph/TurnData"); var turn = new WazeModelGraphTurnData(); turn = window.W.model.getTurnGraph().getTurnThroughNode(node, getByID(window.W.model.segments,s_in_id), getByID(window.W.model.segments,s_out_id)); var opcode = turn.getTurnData().getInstructionOpcode(); switch (opcode) { case "NONE": ja_log("turn opcode override is: " + opcode, 2); return ja_routing_type.OverrideBC; break; case "CONTINUE": ja_log("turn opcode override is: " + opcode, 2); return ja_routing_type.OverrideCONTINUE; break; case "TURN_LEFT": ja_log("turn opcode override is: " + opcode, 2); return ja_routing_type.OverrideTURN_LEFT; break; case "TURN_RIGHT": ja_log("turn opcode override is: " + opcode, 2); return ja_routing_type.OverrideTURN_RIGHT; break; case "KEEP_LEFT": ja_log("turn opcode override is: " + opcode, 2); return ja_routing_type.OverrideKEEP_LEFT; break; case "KEEP_RIGHT": ja_log("turn opcode override is: " + opcode, 2); return ja_routing_type.OverrideKEEP_RIGHT; break; case "EXIT_LEFT": ja_log("turn opcode override is: " + opcode, 2); return ja_routing_type.OverrideEXIT_LEFT; break; case "EXIT_RIGHT": ja_log("turn opcode override is: " + opcode, 2); return ja_routing_type.OverrideEXIT_RIGHT; break; case "UTURN": ja_log("turn opcode override is: " + opcode, 2); return ja_routing_type.OverrideU_TURN; break; default: ja_log("no turn opcode override", 2); } } //Roundabout - no true instruction guessing here! if (s_in.attributes.junctionID) { if (s_out[s_out_id].attributes.junctionID) { ja_log("Roundabout continuation - no instruction", 2); return ja_routing_type.BC; } else { ja_log("Roundabout exit - no instruction", 2); //exit just to visually distinguish from roundabout continuation return ja_routing_type.ROUNDABOUT_EXIT; } } else if (s_out[s_out_id].attributes.junctionID) { ja_log("Roundabout entry - no instruction", 2); //no instruction since it's normally the only continuation - true instruction can be computed for //entry-exit selection only return ja_routing_type.BC; } //Check for U-turn, which is emitted even if there is only one s-out if (Math.abs(angle) > U_TURN_ANGLE + GRAY_ZONE) { ja_log("Angle is >= 170 - U-Turn", 2); return ja_routing_type.U_TURN; } else if (Math.abs(angle) > U_TURN_ANGLE - GRAY_ZONE) { ja_log("Angle is in gray zone 169-171", 2); return ja_routing_type.PROBLEM; } //No other possible turns if(node.attributes.segIDs.length <= 2) { ja_log("Only one possible turn - no instruction", 2); return ja_routing_type.BC; } //No instruction /* * * Here be dragons! * */ if(Math.abs(angle) < TURN_ANGLE - GRAY_ZONE) { ja_log("Turn is <= 44", 2); /* * Filter out disallowed and non-"BC eligible" turns. */ ja_log("Original angles and street_n:", 2); ja_log(angles, 2); ja_log(street_n, 2); ja_log(s_n, 2); angles = angles.filter(function (a) { ja_log("Filtering angle: " + ja_angle_diff(s_in_a, a[0], false), 2); if(s_out_id === a[1] || (typeof s_n[a[1]] !== 'undefined' && ja_is_turn_allowed(s_in, node, s_n[a[1]]) && Math.abs(ja_angle_diff(s_in_a, a[0], false)) < TURN_ANGLE //Any angle above 45.04 is not eligible )) { ja_log(true, 4); return true; } else { ja_log(false, 4); if(street_n[a[1]]) { delete s_n[a[1]]; delete street_n[a[1]]; } return false; } }); ja_log("Filtered angles and street_n:", 2); ja_log(angles, 2); ja_log(street_n, 2); ja_log(s_n, 2); if(angles.length <= 1) { ja_log("Only one allowed turn left", 2); return ja_routing_type.BC; } //No instruction /* * Apply simplified BC logic */ var bc_matches = {}, bc_prio = 0, bc_count = 0; var bc_collect = function(a, prio) { ja_log("Potential BC = " + prio, 2); ja_log(a, 2); if (prio > bc_prio) { //highest priority wins now bc_matches = {}; bc_prio = prio; bc_count = 0; } if (prio === bc_prio) { bc_matches[a[1]] = a; bc_count++; } ja_log("BC candidates:", 2); ja_log(bc_matches, 2); }; //Check each eligible turn against routing rules for(var k=0; k< angles.length; k++) { var a = angles[k]; ja_log("Checking angle " + k, 2); ja_log(a, 2); var tmp_angle = ja_angle_diff(s_in_a[0], a[0], false); ja_log(tmp_angle, 2); var tmp_s_out = {}; tmp_s_out[a[1]] = s_n[a[1]]; var tmp_street_out = {}; tmp_street_out[a[1]] = street_n[a[1]]; var name_match = ja_primary_name_match(street_in, tmp_street_out) || ja_alt_name_match(street_in, tmp_street_out) || ja_cross_name_match(street_in, tmp_street_out); if(name_match && ja_segment_type_match(s_in, tmp_s_out)) { ja_log("BC name and type match", 2); bc_collect(a, 3); } else if(name_match) { ja_log("BC name match", 2); bc_collect(a, 2); } else if(ja_segment_type_match(s_in, tmp_s_out)) { ja_log("BC type match", 2); bc_collect(a, 1); } //Else: Non-BC } //If s-out is the only BC, that's it. if (bc_matches[s_out_id] !== undefined && bc_count === 1) { ja_log("\"straight\": no instruction", 2); return ja_routing_type.BC; } ja_log("BC logic did not apply; using old default rules instead.", 2); //FZ69617: Sort angles in left most first order ja_log("Unsorted angles", 4); ja_log(angles, 4); angles.sort(function(a, b) { return ja_angle_dist(a[0], s_in_a[0][0]) - ja_angle_dist(b[0], s_in_a[0][0]); }); ja_log("Sorted angles", 4); ja_log(angles, 4); //wlodek76: FIXING KEEP LEFT/RIGHT regarding to left most segment //WIKI WAZE: When there are more than two segments less than 45.04°, only the left most segment will be // KEEP LEFT, all the rest will be KEEP RIGHT //FZ69617: Wiki seems to be wrong here - experiments shows that "more than two" must be read as "at least two" //FZ69617: Wiki also does not mention differences between RHT and LHT countries for this consideration, // but map experiments seem to prove that we have to use reverse logic for LHT countries. if (!s_in.model.isLeftHand) { //RHT if (angles[0][1] === s_out_id) { //s-out is left most segment //wlodek76: KEEP LEFT/RIGHT overlapping case //WIKI WAZE: If the left most segment is overlapping another segment, it will also be KEEP RIGHT. if (!ja_overlapping_angles(angles[0][0], angles[1][0])) { ja_log("Left most <45 segment: keep left", 2); return ja_routing_type.KEEP_LEFT; } } } else { //LHT //FZ69617: KEEP RIGHT/LEFT logic for right most segment //MISSING IN WIKI: When there are at least two segments less than 45.04°, only the right most segment will // be KEEP RIGHT, all the rest will be KEEP LEFT if (angles[angles.length - 1][1] === s_out_id) { //s-out is right most segment //FZ69617: KEEP RIGHT/LEFT overlapping case //MISSING IN WIKI: If the right most segment is overlapping another segment, it will also be KEEP LEFT. if (!ja_overlapping_angles(angles[angles.length - 1][0], angles[angles.length - 2][0])) { ja_log("Right most <45 segment: keep right", 2); return ja_routing_type.KEEP_RIGHT; } } } //FZ69617: Two overlapping segments logic //WAZE WIKI: If the only two segments less than 45.04° overlap each other, neither will get an instruction. //... //wlodek76: Three overlapping segments logic //MISSING IN WIKI: If the ONLY THREE segments less than 45.04° overlap each other, neither will get an instruction. //... //FZ69617: Two or more overlapping segments logic //MISSING IN WIKI: If there are two or more segments less than 45.04° and all these segmentes overlap each other, // neither will get an instruction. var overlap_i = 1; while(overlap_i < angles.length && ja_overlapping_angles(angles[0][0], angles[overlap_i][0])) { ++overlap_i; } if(overlap_i > 1 && overlap_i === angles.length) { ja_log("Two or more overlapping segments only: no instruction", 2); return ja_routing_type.BC; } //Primary to non-primary if(ja_is_primary_road(s_in) && !ja_is_primary_road(s_out[s_out_id])) { ja_log("Primary to non-primary = exit", 2); return s_in.model.isLeftHand ? ja_routing_type.EXIT_LEFT : ja_routing_type.EXIT_RIGHT; } //Ramp to non-primary or non-ramp if(ja_is_ramp(s_in) && !ja_is_primary_road(s_out[s_out_id]) && !ja_is_ramp(s_out[s_out_id]) ) { ja_log("Ramp to non-primary and non-ramp = exit", 2); return s_in.model.isLeftHand ? ja_routing_type.EXIT_LEFT : ja_routing_type.EXIT_RIGHT; } ja_log("DEFAULT: keep", 2); return s_in.model.isLeftHand ? ja_routing_type.KEEP_LEFT : ja_routing_type.KEEP_RIGHT; } else if (Math.abs(angle) < TURN_ANGLE + GRAY_ZONE) { ja_log("Angle is in gray zone 44-46", 2); return ja_routing_type.PROBLEM; } else { ja_log("Normal turn", 2); return ja_routing_type.TURN; //Normal turn (left|right) } } function findLayer(partOf_id){ var layer; for (var i=0; i < window.W.map.layers.length; i++){ if (window.W.map.layers[i].id.search(partOf_id) != -1){ layer={id: window.W.map.layers[i].id, name: window.W.map.layers[i].name, number : i}; ja_log("Number: " + i + "; id : " + layer.id + "; name :" + layer.name ,3); return layer; } } } function testLayerZIndex(){ // seb-d59: // Here i search the selection layer and i read the z-index // and put JAI's layer under this one. var zIndex = 0; ja_mapLayer.setZIndex(500); // now selection layer has no name ... var layer = {} layer = findLayer("OpenLayers_Layer_Vector_RootContainer"); var layerOBJ = window.W.map.layers[layer.number]; //ja_log("id : " + layerOBJ.id + "; name :" + layerOBJ.name + " zIndex: " + layerOBJ.getZIndex() ,3); zIndex = parseInt(layerOBJ.getZIndex()) - 1 ; ja_mapLayer.setZIndex(zIndex); ja_log("ja_mapLayer new zIndex: " + ja_mapLayer.getZIndex() ,3); } function ja_calculate_real() { var ja_start_time = Date.now(); var ja_nodes = []; var restart = false; ja_log("Actually calculating now", 2); ja_roundabout_points = []; ja_log(window.W.map, 3); if (typeof ja_mapLayer === 'undefined') { return; } //clear old info ja_mapLayer.destroyFeatures(); testLayerZIndex(); if (ja_getOption("roundaboutOverlayDisplay") === "rOverAlways") { ja_draw_roundabout_overlay(); } //try to show all angles for all selected segments if (window.W.selectionManager.getSelectedFeatures().length === 0) { return; } ja_log("Checking junctions for " + window.W.selectionManager.getSelectedFeatures().length + " segments", 2); window.W.selectionManager.getSelectedFeatures().forEach(function(element) { ja_log(element, 3); switch (element.model.type) { case "node": ja_nodes.push(element.model.attributes.id); break; case "segment": //segments selected? if (element.model.attributes.fromNodeID != null && ja_nodes.indexOf(element.model.attributes.fromNodeID) === -1) { ja_nodes.push(element.model.attributes.fromNodeID); } if (element.model.attributes.toNodeID != null && ja_nodes.indexOf(element.model.attributes.toNodeID) === -1) { ja_nodes.push(element.model.attributes.toNodeID); } break; case "venue": break; default: ja_log("Found unknown item type: " + element.model.type, 2); break; } ja_log(ja_nodes, 2); }); //Figure out if we have a selected roundabout and do some magic var ja_selected_roundabouts = {}; ja_nodes.forEach(function(node) { ja_log(getByID(window.W.model.nodes,node), 3); var tmp_s = null, tmp_n = null, tmp_junctionID = null; if(getByID(window.W.model.nodes,node) == null || typeof getByID(window.W.model.nodes,node).attributes.segIDs === 'undefined') { return; } getByID(window.W.model.nodes,node).attributes.segIDs.forEach(function(segment) { ja_log(segment, 3); if(getByID(window.W.model.segments,segment).attributes.junctionID) { ja_log("Roundabout detected: " + getByID(window.W.model.segments,segment).attributes.junctionID, 3); tmp_junctionID = getByID(window.W.model.segments,segment).attributes.junctionID; } else { tmp_s = segment; tmp_n = node; } ja_log("tmp_s: " + (tmp_s === null ? 'null' : tmp_s), 3); }); ja_log("final tmp_s: " + (tmp_s === null ? 'null' : tmp_s), 3); if(tmp_junctionID === null) { return; } if (ja_selected_roundabouts.hasOwnProperty(tmp_junctionID)) { ja_selected_roundabouts[tmp_junctionID].out_s = tmp_s; ja_selected_roundabouts[tmp_junctionID].out_n = node; } else { ja_selected_roundabouts[tmp_junctionID] = { 'in_s': tmp_s, 'in_n': tmp_n, 'out_s': null, 'out_n': null, 'p': getByID(window.W.model.junctions,tmp_junctionID).geometry }; } }); //Do some fancy painting for the roundabouts... for(var tmp_roundabout in ja_selected_roundabouts) { if (ja_selected_roundabouts.hasOwnProperty(tmp_roundabout)) { ja_log(tmp_roundabout, 3); ja_log(ja_selected_roundabouts[tmp_roundabout], 3); //New roundabouts don't have coordinates yet.. if(typeof ja_selected_roundabouts[tmp_roundabout].p === 'undefined' || ja_selected_roundabouts[tmp_roundabout].out_n === null ) { continue; } //Draw circle overlay for this roundabout if(ja_getOption("roundaboutOverlayDisplay") === "rOverSelected") { ja_draw_roundabout_overlay(tmp_roundabout); } //Transform LonLat to actual layer projection var tmp_roundabout_center = ja_coordinates_to_point(ja_selected_roundabouts[tmp_roundabout].p.coordinates); var angle = ja_angle_between_points( getByID(window.W.model.nodes,ja_selected_roundabouts[tmp_roundabout].in_n).geometry, tmp_roundabout_center, getByID(window.W.model.nodes,ja_selected_roundabouts[tmp_roundabout].out_n).geometry ); ja_mapLayer.addFeatures([ new window.OpenLayers.Feature.Vector( tmp_roundabout_center, { angle: ja_round(angle) + '°', ja_type: ja_is_roundabout_normal( tmp_roundabout, ja_selected_roundabouts[tmp_roundabout].in_n) ? ja_routing_type.TURN : ja_routing_type.ROUNDABOUT } ) ]); } } var ja_label_distance; /* * Define a base distance to markers, depending on the zoom level */ switch (window.W.map.olMap.zoom) { case 22: //10: ja_label_distance = 2.8; break; case 21: //9: ja_label_distance = 4; break; case 20: //8: ja_label_distance = 8; break; case 19: //7: ja_label_distance = 15; break; case 18: //6: ja_label_distance = 25; break; case 17: //5: ja_label_distance = 40; break; case 16: //4: ja_label_distance = 80; break; case 15: //3: ja_label_distance = 150; break; case 14: //2: ja_label_distance = 300; break; case 13: //1: ja_label_distance = 400; break; default: ja_log("Unsupported zoom level: " + window.W.map.olMap.zoom + "!", 2); } ja_label_distance *= (1 + (0.2 * parseInt(ja_getOption("decimals")))); ja_log("zoom: " + window.W.map.olMap.zoom + " -> distance: " + ja_label_distance, 2); /** * Collect double-turn (inc. U-turn) segments info */ var doubleTurns = { data: {}, //Structure: map<s_id, map<s_out_id, list<{s_in_id, angle, turn_type}>>> collect: function (s_id, s_in_id, s_out_id, angle, turn_type) { ja_log("Collecting double-turn path from " + s_in_id + " to " + s_out_id + " via " + s_id + " with angle " + angle + " type: " + turn_type, 2); var info = this.data[s_id]; if (info === undefined) { info = this.data[s_id] = {}; } var list = info[s_out_id]; if (list === undefined) { list = info[s_out_id] = []; } list.push({ s_in_id: s_in_id, angle: angle, turn_type: turn_type }); }, forEachItem: function (s_id, s_out_id, fn) { var info = this.data[s_id]; if (info !== undefined) { var list = info[s_out_id]; if (list !== undefined) { list.forEach(function(item, i) { fn(item, i); }); } } } }; //Loop through all 15m or less long segments and collect double-turn disallowed ones if (ja_getOption("angleMode") === "aDeparture" && ja_nodes.length > 1) { window.W.selectionManager.getSelectedFeatures().forEach(function (selectedSegment) { var segmentId = selectedSegment.model.attributes.id; var segment = window.W.model.segments.objects[segmentId]; ja_log("Checking " + segmentId + " for double turns ...", 2); var len = ja_segment_length(segment); ja_log("Segment " + segmentId + " length: " + len, 2); if (Math.round(len) <= 15) { var fromNode = getByID(window.W.model.nodes,segment.attributes.fromNodeID); var toNode = getByID(window.W.model.nodes,segment.attributes.toNodeID); var a_from = ja_getAngleMidleSeg(segment.attributes.fromNodeID, segment); var a_to = ja_getAngleMidleSeg(segment.attributes.toNodeID, segment); fromNode.attributes.segIDs.forEach(function (fromSegmentId) { if (fromSegmentId === segmentId) return; var fromSegment = window.W.model.segments.objects[fromSegmentId]; if(!ja_is_up_to_primary_road(fromSegment)) return; var from_a = ja_getAngle(segment.attributes.fromNodeID, fromSegment); var from_angle = ja_angle_diff(from_a, a_from, false); ja_log("Segment from " + fromSegmentId + " angle: " + from_a + ", turn angle: " + from_angle, 2); toNode.attributes.segIDs.forEach(function (toSegmentId) { if (toSegmentId === segmentId) return; var toSegment = window.W.model.segments.objects[toSegmentId]; if(!ja_is_up_to_primary_road(toSegment)) return; var to_a = ja_getAngle(segment.attributes.toNodeID, toSegment); var to_angle = ja_angle_diff(to_a, a_to, false); ja_log("Segment to " + toSegmentId + " angle: " + to_a + ", turn angle: " + to_angle, 2); var angle = Math.abs(to_angle - from_angle); ja_log("Angle from " + fromSegmentId + " to " + toSegmentId + " is: " + angle, 2); //Determine whether a turn is disallowed if (angle >= 175 - GRAY_ZONE && angle <= 185 + GRAY_ZONE) { var turn_type = (angle >= 175 + GRAY_ZONE && angle <= 185 - GRAY_ZONE) ? ja_routing_type.NO_U_TURN : ja_routing_type.PROBLEM; if (ja_is_turn_allowed(fromSegment, fromNode, segment) && ja_is_turn_allowed(segment, toNode, toSegment)) { doubleTurns.collect(segmentId, fromSegmentId, toSegmentId, angle, turn_type); } if (ja_is_turn_allowed(toSegment, toNode, segment) && ja_is_turn_allowed(segment, fromNode, fromSegment)) { doubleTurns.collect(segmentId, toSegmentId, fromSegmentId, angle, turn_type); } } }); }); } }); } ja_log("Collected double-turn segments:", 2); ja_log(doubleTurns.data, 2); //Start looping through selected nodes for (var i = 0; i < ja_nodes.length; i++) { var node = getByID(window.W.model.nodes,ja_nodes[i]); var angles = []; var ja_selected_segments_count = 0; var ja_selected_angles = []; var a; if (node == null || !node.hasOwnProperty('attributes')) { //Oh oh.. should not happen? We want to use a node that does not exist ja_log("Oh oh.. should not happen?",2); ja_log(node, 2); ja_log(ja_nodes[i], 2); ja_log(window.W.model, 3); ja_log(window.W.model.nodes, 3); continue; } //check connected segments var ja_current_node_segments = node.attributes.segIDs; ja_log(node, 2); //ignore of we have less than 2 segments if (ja_current_node_segments.length <= 1) { ja_log("Found only " + ja_current_node_segments.length + " connected segments at " + ja_nodes[i] + ", not calculating anything...", 2); continue; } ja_log("Calculating angles for " + ja_current_node_segments.length + " segments", 2); ja_log(ja_current_node_segments, 3); ja_current_node_segments.forEach(function (nodeSegment, j) { var s = window.W.model.segments.objects[nodeSegment]; if(typeof s === 'undefined') { //Meh. Something went wrong, and we lost track of the segment. This needs a proper fix, but for now // it should be sufficient to just restart the calculation ja_log("Failed to read segment data from model. Restarting calculations.", 1); if(ja_last_restart === 0) { ja_last_restart = new Date().getTime(); setTimeout(function(){ja_calculate();}, 500); } restart = true; } a = ja_getAngle(ja_nodes[i], s); ja_log("Segment " + nodeSegment + " angle is " + a, 2); angles[j] = [a, nodeSegment, s == null ? false : s.isSelected()]; if (s == null ? false : s.isSelected()) { ja_selected_segments_count++; } }); if(restart) { return; } //make sure we have the selected angles in correct order ja_log(ja_current_node_segments, 3); window.W.selectionManager.getSelectedFeatures().forEach(function (selectedSegment) { var selectedSegmentId = selectedSegment.model.attributes.id; ja_log("Checking if " + selectedSegmentId + " is in current node", 3); if(ja_current_node_segments.indexOf(selectedSegmentId) >= 0) { ja_log("It is!", 4); //find the angle for(var j=0; j < angles.length; j++) { if(angles[j][1] === selectedSegmentId) { ja_selected_angles.push(angles[j]); break; } } } else { ja_log("It's not..", 4); } }); ja_log(angles, 3); var ha, point; //if we have two connected segments selected, do some magic to get the turn angle only =) if (ja_selected_segments_count === 2) { var ja_extra_space_multiplier = 1; a = ja_angle_diff(ja_selected_angles[0][0], ja_selected_angles[1][0], false); ha = (parseFloat(ja_selected_angles[0][0]) + parseFloat(ja_selected_angles[1][0]))/2; if((Math.abs(ja_selected_angles[0][0]) + Math.abs(ja_selected_angles[1][0])) > 180 && ((ja_selected_angles[0][0] < 0 && ja_selected_angles[1][0] > 0) || (ja_selected_angles[0][0] > 0 && ja_selected_angles[1][0] < 0)) ) { ha += 180; } if (Math.abs(a) > 120) { ja_log("Sharp angle", 2); ja_extra_space_multiplier = 2; } //Move point a bit if it's on the top (Bridge icon will obscure it otherwise) if(ha > 40 && ha < 120) { ja_extra_space_multiplier = 2; } ja_log("Angle between " + ja_selected_angles[0][1] + " and " + ja_selected_angles[1][1] + " is " + a + " and position for label should be at " + ha, 3); //Guess some routing instructions based on segment types, angles etc var ja_junction_type = ja_routing_type.TURN; //Default to old behavior if(ja_getOption("guess")) { ja_log(ja_selected_angles, 2); ja_log(angles, 2); ja_junction_type = ja_guess_routing_instruction(node, ja_selected_angles[0][1], ja_selected_angles[1][1], angles); ja_log("Type is: " + ja_junction_type, 2); } //get the initial marker point point = new window.OpenLayers.Geometry.Point( node.geometry.x + (ja_extra_space_multiplier * ja_label_distance * Math.cos((ha * Math.PI) / 180)), node.geometry.y + (ja_extra_space_multiplier * ja_label_distance * Math.sin((ha * Math.PI) / 180)) ); ja_draw_marker(point, node, ja_label_distance, a, ha, true, ja_junction_type); //draw double turn markers doubleTurns.forEachItem(ja_selected_angles[0][1], ja_selected_angles[1][1], function(item) { ja_draw_marker(point, node, ja_label_distance, item.angle, ha, true, item.turn_type); }); } else { //sort angle data (ascending) angles.sort(function (a, b) { return a[0] - b[0]; }); ja_log(angles, 3); ja_log(ja_selected_segments_count, 3); //get all segment angles angles.forEach(function(angle, j) { a = (360 + (angles[(j + 1) % angles.length][0] - angle[0])) % 360; ha = (360 + ((a / 2) + angle[0])) % 360; var a_in = angles.filter(function(a) { return !!a[2]; })[0]; //Show only one angle for nodes with only 2 connected segments and a single selected segment // (not on both sides). Skipping the one > 180 if (ja_selected_segments_count === 1 && angles.length === 2 && a >=180 && ja_getOption("angleMode") !== "aDeparture" ) { ja_log("Skipping marker, as we need only one of them", 2); return; } if(ja_getOption("angleMode") === "aDeparture" && ja_selected_segments_count > 0) { if(a_in[1] === angle[1]) { ja_log("in == out. skipping.", 2); return; } ja_log("Angle in:",2); ja_log(a_in,2); ja_log(ja_guess_routing_instruction(node, a_in[1], angle[1], angles), 2); //FIXME: we might want to try to keep the marker on the segment, instead of just //in the direction of the first part ha = angle[0]; a = ja_angle_diff(a_in[0], angles[j][0], false); point = new window.OpenLayers.Geometry.Point( node.geometry.x + (ja_label_distance * 2 * Math.cos((ha * Math.PI) / 180)), node.geometry.y + (ja_label_distance * 2 * Math.sin((ha * Math.PI) / 180)) ); ja_draw_marker(point, node, ja_label_distance, a, ha, true, ja_getOption("guess") ? ja_guess_routing_instruction(node, a_in[1], angle[1], angles) : ja_routing_type.TURN); //draw double turn markers doubleTurns.forEachItem(a_in[1], angle[1], function(item) { ja_draw_marker(point, node, ja_label_distance, item.angle, ha, true, item.turn_type); }); } else { ja_log("Angle between " + angle[1] + " and " + angles[(j + 1) % angles.length][1] + " is " + a + " and position for label should be at " + ha, 3); point = new window.OpenLayers.Geometry.Point( node.geometry.x + (ja_label_distance * 1.25 * Math.cos((ha * Math.PI) / 180)), node.geometry.y + (ja_label_distance * 1.25 * Math.sin((ha * Math.PI) / 180)) ); ja_draw_marker(point, node, ja_label_distance, a, ha); } }); } } //testLayerZIndex(); ja_last_restart = 0; var ja_end_time = Date.now(); ja_log("Calculation took " + String(ja_end_time - ja_start_time) + " ms", 2); } /* * Drawing functions */ /** * * @param point Estimated point for marker * @param node Node the marker is for * @param ja_label_distance Arbitrary distance to be used in moving markers further away etc * @param a Angle to display * @param ha Angle to marker from node (FIXME: either point or ha is probably unnecessary) * @param withRouting true: show routing guessing markers, false: show "normal" angle markers * @param ja_junction_type If using routing, this needs to be set to the desired type */ function ja_draw_marker(point, node, ja_label_distance, a, ha, withRouting, ja_junction_type) { //Try to estimate of the point is "too close" to another point //(or maybe something else in the future; like turn restriction arrows or something) //FZ69617: Exctract initial label distance from point var ja_tmp_distance = Math.abs(ha) % 180 < 45 || Math.abs(ha) % 180 > 135 ? (point.x - node.geometry.x) / (Math.cos((ha * Math.PI) / 180)) : (point.y - node.geometry.y) / (Math.sin((ha * Math.PI) / 180)); ja_log("Starting distance estimation", 3); while(ja_mapLayer.features.some(function(feature){ if(typeof feature.attributes.ja_type !== 'undefined' && feature.attributes.ja_type !== 'roundaboutOverlay') { //Arbitrarily chosen minimum distance.. Should actually use the real bounds of the markers, //but that didn't work out.. Bounds are always 0.. if(ja_label_distance / 1.4 > feature.geometry.distanceTo(point)) { ja_log(ja_label_distance / 1.5 > feature.geometry.distanceTo(point) + " is kinda close..", 3); return true; } } return false; })) { //add 1/4 of the original distance and hope for the best =) ja_tmp_distance += ja_label_distance / 4; ja_log("setting distance to " + ja_tmp_distance, 2); point = new window.OpenLayers.Geometry.Point( node.geometry.x + (ja_tmp_distance * Math.cos((ha * Math.PI) / 180)), node.geometry.y + (ja_tmp_distance * Math.sin((ha * Math.PI) / 180)) ); } ja_log("Distance estimation done", 3); var angleString = ja_round(Math.abs(a)) + "°"; //FZ69617: Add direction arrows for turn instructions only if (ja_getOption("angleDisplay") === "displaySimple") { switch(ja_junction_type) { case ja_routing_type.TURN: angleString = a > 0 ? ja_arrow.left() + angleString : angleString + ja_arrow.right(); break; case ja_routing_type.TURN_LEFT: angleString = ja_arrow.left() + angleString; break; case ja_routing_type.TURN_RIGHT: angleString = angleString + ja_arrow.right(); break; case ja_routing_type.EXIT: case ja_routing_type.KEEP: angleString = a > 0 ? ja_arrow.left_up() + angleString : angleString + ja_arrow.right_up(); break; case ja_routing_type.EXIT_LEFT: case ja_routing_type.KEEP_LEFT: angleString = ja_arrow.left_up() + angleString; break; case ja_routing_type.EXIT_RIGHT: case ja_routing_type.KEEP_RIGHT: angleString += ja_arrow.right_up(); break; //Override case ja_routing_type.OverrideBC: angleString = ja_getOption("overrideAngles") ? angleString : ""; break; case ja_routing_type.OverrideCONTINUE: angleString = ja_arrow.up() + (ja_getOption("overrideAngles") ? angleString : ""); break; case ja_routing_type.OverrideTURN_LEFT: angleString = ja_arrow.left() + (ja_getOption("overrideAngles") ? angleString : ""); break; case ja_routing_type.OverrideTURN_RIGHT: angleString = (ja_getOption("overrideAngles") ? angleString : "") + ja_arrow.right(); break; case ja_routing_type.OverrideEXIT_LEFT: case ja_routing_type.OverrideKEEP_LEFT: angleString = ja_arrow.left_up() + (ja_getOption("overrideAngles") ? angleString : ""); break; case ja_routing_type.OverrideEXIT_RIGHT: case ja_routing_type.OverrideKEEP_RIGHT: angleString = (ja_getOption("overrideAngles") ? angleString : "") + ja_arrow.right_up(); default: ja_log("No extra format for junction type: " + ja_junction_type, 2); } } else { switch(ja_junction_type) { case ja_routing_type.TURN: angleString = (a > 0 ? ja_arrow.left() : ja_arrow.right()) + "\n" + angleString; break; case ja_routing_type.TURN_LEFT: angleString = ja_arrow.left() + "\n" + angleString; break; case ja_routing_type.TURN_RIGHT: angleString = ja_arrow.right() + "\n" + angleString; break; case ja_routing_type.EXIT: case ja_routing_type.KEEP: angleString = (a > 0 ? ja_arrow.left_up() : ja_arrow.right_up()) + "\n" + angleString; break; case ja_routing_type.EXIT_LEFT: case ja_routing_type.KEEP_LEFT: angleString = ja_arrow.left_up() + "\n" + angleString; break; case ja_routing_type.EXIT_RIGHT: case ja_routing_type.KEEP_RIGHT: angleString = ja_arrow.right_up() + "\n" + angleString; break; case ja_routing_type.PROBLEM: angleString = "?\n" + angleString; break; //Override case ja_routing_type.OverrideBC: angleString = ja_getOption("overrideAngles") ? angleString : ""; break; case ja_routing_type.OverrideCONTINUE: angleString = ja_arrow.up() + (ja_getOption("overrideAngles") ? ("\n" + angleString) : ""); break; case ja_routing_type.OverrideTURN_LEFT: angleString = ja_arrow.left() + (ja_getOption("overrideAngles") ? ("\n" + angleString) : ""); break; case ja_routing_type.OverrideTURN_RIGHT: angleString = ja_arrow.right() + (ja_getOption("overrideAngles") ? ("\n" + angleString) : ""); break; case ja_routing_type.OverrideEXIT_LEFT: case ja_routing_type.OverrideKEEP_LEFT: angleString = ja_arrow.left_up() + (ja_getOption("overrideAngles") ? ("\n" + angleString) : ""); break; case ja_routing_type.OverrideEXIT_RIGHT: case ja_routing_type.OverrideKEEP_RIGHT: angleString = ja_arrow.right_up() + (ja_getOption("overrideAngles") ? ("\n" + angleString) : ""); break; default: ja_log("No extra format for junction type: " + ja_junction_type, 2); } } var anglePoint = withRouting ? new window.OpenLayers.Feature.Vector( point, { angle: angleString, ja_type: ja_junction_type } ): new window.OpenLayers.Feature.Vector( point, { angle: ja_round(a) + "°", ja_type: "generic" } ); ja_log(anglePoint, 3); //Don't paint points inside an overlaid roundabout if(ja_roundabout_points.some(function (roundaboutPoint){ return roundaboutPoint.containsPoint(point); })) { return; } //Draw a line to the point ja_mapLayer.addFeatures([ new window.OpenLayers.Feature.Vector( new window.OpenLayers.Geometry.LineString([node.geometry, point]), {}, {strokeOpacity: 0.6, strokeWidth: 1.2, strokeDashstyle: "solid", strokeColor: "#ff9966"} ) ] ); //push the angle point ja_mapLayer.addFeatures([anglePoint]); } function ja_draw_roundabout_overlay(junctionId) { (junctionId === undefined ? (window.W.model.junctions.getObjectArray()) : (function (junction) { return junction === undefined ? [] : [ junction ]; }) (getByID(window.W.model.junctions,junctionId))).forEach(function (element) { ja_log(element, 3); var nodes = {}; element.attributes.segIDs.forEach(function(s) { var seg = getByID(window.W.model.segments,s); ja_log(seg, 3); nodes[seg.attributes.fromNodeID] = getByID(window.W.model.nodes,seg.attributes.fromNodeID); nodes[seg.attributes.toNodeID] = getByID(window.W.model.nodes,seg.attributes.toNodeID); }); ja_log(nodes, 3); var center = ja_coordinates_to_point(element.geometry.coordinates); ja_log(center, 3); var distances = []; Object.getOwnPropertyNames(nodes).forEach(function(name) { ja_log("Checking " + name + " distance", 3); var dist = Math.sqrt( Math.pow(nodes[name].attributes.geometry.x - center.x, 2) + Math.pow(nodes[name].attributes.geometry.y - center.y, 2) ); distances.push(dist); }); ja_log(distances, 3); ja_log("Mean distance is " + distances.reduce(function(a,b){return a + b;}) / distances.length, 3); var circle = window.OpenLayers.Geometry.Polygon.createRegularPolygon( center, distances.reduce(function(a,b){return a + b;}) / distances.length, 40, 0 ); var roundaboutCircle = new window.OpenLayers.Feature.Vector(circle, {'ja_type': 'roundaboutOverlay'}); ja_roundabout_points.push(circle); ja_mapLayer.addFeatures([roundaboutCircle]); }); } /* * Segment and routing helpers */ /** * Check if segment in type matches any other segments * @param segment_in * @param segments * @returns {boolean} */ function ja_segment_type_match(segment_in, segments) { ja_log(segment_in, 2); ja_log(segments, 2); return Object.getOwnPropertyNames(segments).some(function (segment_n_id, index) { var segment_n = segments[segment_n_id]; ja_log("PT Checking element " + index, 2); ja_log(segment_n, 2); if(segment_n.attributes.id === segment_in.attributes.id) { return false; } ja_log("PT checking sn.rt " + segment_n.attributes.roadType + " vs i.pt: " + segment_in.attributes.roadType, 2); return (segment_n.attributes.roadType === segment_in.attributes.roadType); }); } function ja_is_primary_road(seg) { var t = seg.attributes.roadType; return t === ja_road_type.FREEWAY || t === ja_road_type.MAJOR_HIGHWAY || t === ja_road_type.MINOR_HIGHWAY; } function ja_is_up_to_primary_road(seg) { var t = seg.attributes.roadType; return t === ja_road_type.FREEWAY || t === ja_road_type.RAMP || t === ja_road_type.MAJOR_HIGHWAY || t === ja_road_type.MINOR_HIGHWAY || t === ja_road_type.PRIMARY_STREET; } function ja_is_ramp(seg) { var t = seg.attributes.roadType; return t === ja_road_type.RAMP; } function ja_is_turn_allowed(s_from, via_node, s_to) { ja_log("Allow from " + s_from.attributes.id + " to " + s_to.attributes.id + " via " + via_node.attributes.id + "? " + via_node.isTurnAllowedBySegDirections(s_from, s_to) + " | " + s_from.isTurnAllowed(s_to, via_node), 2); //Is there a driving direction restriction? if(!via_node.isTurnAllowedBySegDirections(s_from, s_to)) { ja_log("Driving direction restriction applies", 3); return false; } //Is turn allowed by other means (e.g. turn restrictions)? if(!s_from.isTurnAllowed(s_to, via_node)) { ja_log("Other restriction applies", 3); return false; } if(s_to.attributes.fromNodeID === via_node.attributes.id) { ja_log("FWD direction",3); return ja_is_car_allowed_by_restrictions(s_to.attributes.fwdRestrictions); } else { ja_log("REV direction",3); return ja_is_car_allowed_by_restrictions(s_to.attributes.revRestrictions); } } function ja_is_car_allowed_by_restrictions(restrictions) { ja_log("Checking restrictions for cars", 2); if(typeof restrictions === 'undefined' || restrictions == null || restrictions.length === 0) { ja_log("No car type restrictions to check...", 3); return true; } ja_log(restrictions, 3); return !restrictions.some(function(element) { /*jshint bitwise: false*/ ja_log("Checking restriction " + element, 3); //noinspection JSBitwiseOperatorUsage var ret = element.allDay && //All day restriction element.days === 127 && //Every week day ( element.vehicleTypes === -1 || //All vehicle types element.vehicleTypes & ja_vehicle_types.PRIVATE //or at least private cars ); if (ret) { ja_log("There is an all-day-all-week restriction", 3); var fromDate = Date.parse(element.fromDate); var toDate = Date.parse(element.toDate); ja_log("From: " + fromDate + ", to: " + toDate + ". " + ret, 3); if(isNaN(fromDate && isNaN(toDate))) { ja_log("No start nor end date defined"); return false; } var fRes, tRes; if(!isNaN(fromDate) && new Date() > fromDate) { ja_log("From date is in the past", 3); fRes = 2; } else if(isNaN(fromDate)) { ja_log("From date is invalid/not set", 3); fRes = 1; } else { ja_log("From date is in the future: " + fromDate, 3); fRes = 0; } if(!isNaN(toDate) && new Date() < toDate) { ja_log("To date is in the future", 3); tRes = 2; } else if(isNaN(toDate)) { ja_log("To date is invalid/not set", 3); tRes = 1; } else { ja_log("To date is in the past: " + toDate, 3); tRes = 0; } // Car allowed unless // - toDate is in the future and fromDate is unset or in the past // - fromDate is in the past and toDate is unset in the future // Hope I got this right ;) return (fRes <= 1 && tRes <= 1); } return ret; }); } /** * From wiki: * A Cross-match is when the primary name of one segment is identical to the alternate name of an adjacent segment. * It had the same priory as a Primary name match. In order for a Cross match to work there must be at least one * alt name on both involved segments (even though they don't necessarily match each other). It will work even if * the are no Primary names on those segments. It will not work if all three segments at a split have a matching * Primary name or a matching Alternate name. * @param street_in * @param streets * @returns {boolean} */ function ja_cross_name_match(street_in, streets) { ja_log("CN: init", 2); ja_log(street_in, 2); ja_log(streets, 2); return Object.getOwnPropertyNames(streets).some(function (street_n_id, index) { var street_n_element = streets[street_n_id]; ja_log("CN: Checking element " + index, 2); ja_log(street_n_element, 2); return (street_in.secondary.some(function (street_in_secondary){ ja_log("CN2a: checking n.p: " + street_n_element.primary.name + " vs in.s: " + street_in_secondary.name, 2); //wlodek76: CROSS-MATCH works when two compared segments contain at least one ALT NAME //when alt name is empty cross-match does not work //FZ69617: This no longer seems to be needed //if (street_n_element.secondary.length === 0) { return false; } return street_n_element.primary.name === street_in_secondary.name; }) || street_n_element.secondary.some(function (street_n_secondary) { ja_log("CN2b: checking in.p: " + street_in.primary.name + " vs n.s: " + street_n_secondary.name, 2); //wlodek76: CROSS-MATCH works when two compared segments contain at least one ALT NAME //when alt name is empty cross-match does not work //FZ69617: This no longer seems to be needed //if (street_in.secondary.length === 0) { return false; } //wlodek76: missing return from checking primary name with alternate names return street_in.primary.name === street_n_secondary.name; })); }); } function ja_alt_name_match(street_in, streets) { return Object.getOwnPropertyNames(streets).some(function (street_n_id, index) { var street_n_element = streets[street_n_id]; ja_log("AN alt name check: Checking element " + index, 2); ja_log(street_n_element, 2); if(street_in.secondary.length === 0) { return false; } if(street_n_element.secondary.length === 0) { return false; } return street_in.secondary.some(function (street_in_secondary, index2) { ja_log("AN2 checking element " + index2, 2); ja_log(street_in_secondary, 2); return street_n_element.secondary.some(function (street_n_secondary_element, index3) { ja_log("AN3 Checking in.s: " + street_in_secondary.name + " vs n.s." + index3 + ": " + street_n_secondary_element.name, 2); return street_in_secondary.name === street_n_secondary_element.name; }); }); }); } function ja_primary_name_match(street_in, streets) { ja_log("PN", 2); ja_log(street_in, 2); ja_log(streets, 2); return Object.getOwnPropertyNames(streets).some(function (id, index, array) { var element = streets[id]; ja_log("PN Checking element " + index + " of " + array.length, 2); ja_log(element, 2); return (element.primary.name === street_in.primary.name); }); } function ja_get_streets(segmentId) { var primary = window.W.model.streets.objects[window.W.model.segments.objects[segmentId].attributes.primaryStreetID]; var secondary = []; window.W.model.segments.objects[segmentId].attributes.streetIDs.forEach(function (element) { secondary.push(window.W.model.streets.objects[element]); }); ja_log(primary, 3); ja_log(secondary, 3); return { primary: primary, secondary: secondary }; } /** * Computes segment's length in meters * @param segment Segment to compute the length of * @returns {number} */ function ja_segment_length(segment) { var len = segment.geometry.getGeodesicLength(window.W.map.olMap.projection); ja_log("segment: " + segment.attributes.id + " computed len: " + len + " attrs len: " + segment.attributes.length, 3); return len; } /** * Checks whether the two segments (connected at the same node) overlap each other. * @param a1 Angle of the 1st segment * @param a2 Angle of the 2nd segment */ function ja_overlapping_angles(a1, a2) { // If two angles are close < 2 degree they are overlapped. // Method of recognizing overlapped segment by server is unknown for me yet, I took this from WME Validator // information about this. // TODO: verify overlapping check on the side of routing server. return Math.abs(ja_angle_diff(a1, a2, true)) < OVERLAPPING_ANGLE; } /* * Misc math and map element functions */ /** * * @param p0 From point * @param p1 Center point * @param p2 To point * @returns {number} */ function ja_angle_between_points(p0,p1,p2) { ja_log("p0 " + p0,3); ja_log("p1 " + p1,3); ja_log("p2 " + p2,3); var a = Math.pow(p1.x-p0.x,2) + Math.pow(p1.y-p0.y,2); var b = Math.pow(p1.x-p2.x,2) + Math.pow(p1.y-p2.y,2); var c = Math.pow(p2.x-p0.x,2) + Math.pow(p2.y-p0.y,2); var angle = Math.acos((a+b-c) / Math.sqrt(4*a*b)) / (Math.PI / 180); ja_log("angle is " + angle,3); return angle; } /** * get absolute (or turn) angle between 2 inputs. * 0,90,true -> 90 0,90,false -> -90 * 0,170,true -> 170 0,170,false -> -10 * @param aIn absolute s_in angle (from node) * @param aOut absolute s_out angle (from node) * @param absolute return absolute or turn angle? * @returns {number} */ function ja_angle_diff(aIn, aOut, absolute) { var a = parseFloat(aOut) - parseFloat(aIn); if(a > 180) { a -= 360; } if(a < -180) { a+= 360; } return absolute ? a : (a > 0 ? a - 180 : a + 180); } function ja_angle_dist(a, s_in_angle) { ja_log("Computing out-angle " + a + " distance to in-angle " + s_in_angle, 4); var diff = ja_angle_diff(a, s_in_angle, true); ja_log("Diff is " + diff + ", returning: " + (diff < 0 ? diff + 360 : diff), 4); return diff < 0 ? diff + 360 : diff; } function ja_is_roundabout_normal(junctionID, n_in) { ja_log("Check normal roundabout", 3); var junction = getByID(window.W.model.junctions,junctionID); var nodes = {}; var numValidExits = 0; junction.attributes.segIDs.forEach(function (element, index) { var s = getByID(window.W.model.segments,element); ja_log("index: " + index, 3); //ja_log(s, 3); if (!nodes.hasOwnProperty(s.attributes.toNodeID)) { ja_log("Adding node id: " + s.attributes.toNodeID, 3); //Check if node has allowed exits var allowed = false; var currNode = getByID(window.W.model.nodes,s.attributes.toNodeID); ja_log(currNode, 3); currNode.attributes.segIDs.forEach(function (element2) { var s_exit = getByID(window.W.model.segments,element2); ja_log(s_exit, 3); if (s_exit.attributes.junctionID === null) { ja_log("Checking: " + s_exit.attributes.id, 3); if (currNode.isTurnAllowedBySegDirections(s, s_exit)) { //Exit possibly allowed ja_log("Exit allowed", 3); allowed = true; } else { ja_log("Exit not allowed", 3); } } else { //part of the junction.. Ignoring ja_log(s_exit.attributes.id + " is in the roundabout. ignoring", 3); } }); if (allowed) { numValidExits++; nodes[s.attributes.toNodeID] = getByID(window.W.model.nodes,s.attributes.toNodeID); } } }); var is_normal = true; ja_log(n_in, 3); ja_log(junction, 3); ja_log(nodes, 3); //If we have more than 4 possible exits, the roundabout is non-normal, and we don't want to paint the //offending angles. if (numValidExits > 4) { return false; } for (var n in nodes) { if (nodes.hasOwnProperty(n)) { ja_log("Checking " + n, 3); if (String(n) === String(n_in)) { ja_log("Not comparing to n_in ;)", 3); } else { var angle = ja_angle_between_points( getByID(window.W.model.nodes,n_in).geometry, ja_coordinates_to_point(junction.geometry.coordinates), getByID(window.W.model.nodes,n).geometry ); ja_log("Angle is: " + angle, 3); ja_log("Normalized angle is: " + (angle % 90), 3); //angle = Math.abs((angle%90 - 90)) angle = Math.abs((angle % 90)); ja_log("Angle is: " + angle, 3); // 90 +/- 15 is considered "normal" if (angle <= 15 || 90 - angle <= 15) { ja_log("turn is normal", 3); } else { ja_log("turn is NOT normal", 3); is_normal = false; //Push a marker on the node to show which exit is "not normal" ja_mapLayer.addFeatures([ new window.OpenLayers.Feature.Vector( getByID(window.W.model.nodes,n).geometry, { angle: '±' + ja_round(Math.min(angle, 90 - angle)), ja_type: ja_routing_type.ROUNDABOUT } )] ); } } } } return is_normal; } /** * Helper to get get correct projections for roundabout center point */ function ja_coordinates_to_point(coordinates) { return window.OpenLayers.Projection.transform( new window.OpenLayers.Geometry.Point( coordinates[0], coordinates[1] ), "EPSG:4326", ja_mapLayer.projection.projCode ); } function ja_get_first_point(segment) { return segment.geometry.components[0]; } function ja_get_last_point(segment) { return segment.geometry.components[segment.geometry.components.length - 1]; } function ja_get_second_point(segment) { return segment.geometry.components[1]; } function ja_get_next_to_last_point(segment) { return segment.geometry.components[segment.geometry.components.length - 2]; } //get the absolute angle for a segment end point function ja_getAngle(ja_node, ja_segment) { ja_log("node: " + ja_node, 2); ja_log("segment: " + ja_segment, 2); if (ja_node == null || ja_segment == null) { return null; } var ja_dx, ja_dy; if (ja_segment.attributes.fromNodeID === ja_node) { ja_dx = ja_get_second_point(ja_segment).x - ja_get_first_point(ja_segment).x; ja_dy = ja_get_second_point(ja_segment).y - ja_get_first_point(ja_segment).y; } else { ja_dx = ja_get_next_to_last_point(ja_segment).x - ja_get_last_point(ja_segment).x; ja_dy = ja_get_next_to_last_point(ja_segment).y - ja_get_last_point(ja_segment).y; } ja_log(ja_node + " / " + ja_segment + ": dx:" + ja_dx + ", dy:" + ja_dy, 2); var ja_angle = Math.atan2(ja_dy, ja_dx); return ((ja_angle * 180 / Math.PI)) % 360; } //get the absolute angle for a midle segment (to prevent a false positive on the curved segment) function ja_getAngleMidleSeg(ja_node, ja_segment) { ja_log("node: " + ja_node, 2); ja_log("segment: " + ja_segment, 2); if (ja_node == null || ja_segment == null) { return null; } var ja_dx, ja_dy; if (ja_segment.attributes.fromNodeID === ja_node) { ja_dx = ja_get_last_point(ja_segment).x - ja_get_first_point(ja_segment).x; ja_dy = ja_get_last_point(ja_segment).y - ja_get_first_point(ja_segment).y; } else { ja_dx = ja_get_first_point(ja_segment).x - ja_get_last_point(ja_segment).x; ja_dy = ja_get_first_point(ja_segment).y - ja_get_last_point(ja_segment).y; } ja_log(ja_node + " / " + ja_segment + ": dx:" + ja_dx + ", dy:" + ja_dy, 2); var ja_angle = Math.atan2(ja_dy, ja_dx); return ((ja_angle * 180 / Math.PI)) % 360; } /** * Decimal adjustment of a number. Borrowed (with some modifications) from * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round * ja_round(55.55); with 1 decimal // 55.6 * ja_round(55.549); with 1 decimal // 55.5 * ja_round(55); with -1 decimals // 60 * ja_round(54.9); with -1 decimals // 50 * * @param {Number} value The number. * @returns {Number} The adjusted value. */ function ja_round(value) { var ja_rounding = -parseInt(ja_getOption("decimals")); var valueArray; if (typeof ja_rounding === 'undefined' || +ja_rounding === 0) { return Math.round(value); } value = +value; // If the value is not a number or the exp is not an integer... if (isNaN(value) || !(typeof ja_rounding === 'number' && ja_rounding % 1 === 0)) { return NaN; } // Shift valueArray = value.toString().split('e'); value = Math.round(+(valueArray[0] + 'e' + (valueArray[1] ? (+valueArray[1] - ja_rounding) : -ja_rounding))); // Shift back valueArray = value.toString().split('e'); return +(valueArray[0] + 'e' + (valueArray[1] ? (+valueArray[1] + ja_rounding) : ja_rounding)); } /* * WME interface helper functions */ function ja_getOption(name) { ja_log("Loading option: " + name, 2); if(!ja_options.hasOwnProperty(name) || typeof ja_options[name] === 'undefined') { ja_options[name] = ja_settings[name].defaultValue; } //Check for invalid values //Select values if(ja_settings[name].elementType === "select" && ja_settings[name].options.lastIndexOf(ja_options[name]) < 0) { ja_log(ja_settings[name].options, 2); ja_log("Found invalid value for setting " + name + ": " + ja_options[name] + ". Using default.", 2); ja_options[name] = ja_settings[name].defaultValue; } //Color values else if(ja_settings[name].elementType === "color" && String(ja_options[name]).match(/#[0-9a-f]{6}/) == null) { ja_log("Found invalid value for setting " + name + ": \"" + ja_options[name] + "\". Using default.", 2); ja_options[name] = ja_settings[name].defaultValue; } //Numeric values else if(ja_settings[name].elementType === "number") { var minValue = typeof ja_settings[name].min === 'undefined' ? Number.MIN_VALUE : ja_settings[name].min; var maxValue = typeof ja_settings[name].max === 'undefined' ? Number.MAX_VALUE : ja_settings[name].max; if(isNaN(ja_options[name]) || ja_options[name] < minValue || ja_options[name] > maxValue) { ja_log("Found invalid value for setting " + name + ": \"" + ja_options[name] + "\". Using default.", 2); ja_options[name] = ja_settings[name].defaultValue; } } //Checkboxes else if(ja_settings[name].elementType === "checkbox" && ja_options[name] !== true && ja_options[name] !== false) { ja_log("Found invalid value for setting " + name + ": \"" + ja_options[name] + "\". Using default.", 2); ja_options[name] = ja_settings[name].defaultValue; } ja_log("Got value: " + ja_options[name], 2); return ja_options[name]; } function ja_setOption(name, val) { ja_options[name] = val; if(localStorage) { localStorage.setItem("wme_ja_options", JSON.stringify(ja_options)); } ja_log(ja_options,3); } var ja_onchange = function(e) { var applyPending = false; var settingName = Object.getOwnPropertyNames(ja_settings).filter(function(a){ ja_log(ja_settings[a], 4); return ja_settings[a].elementId === e.id; })[0]; ja_log(e, 3); ja_log(settingName, 3); switch(ja_settings[settingName].elementType) { case "checkbox": ja_log("Checkbox setting " + e.id + ": stored value is: " + ja_options[settingName] + ", new value: " + e.checked, 3); if (ja_options[settingName] !== e.checked) { applyPending = true; } break; case "select": case "color": case "number": ja_log("Setting " + e.id + ": stored value is: " + ja_options[settingName] + ", new value: " + e.value, 3); if (String(ja_options[settingName]) !== String(e.value)) { applyPending = true; } break; default: ja_log("Unknown setting " + e.id + ": stored value is: " + ja_options[settingName] + ", new value: " + e.value, 3); } function disable_input(element, disable) { element.disabled = disable; if (disable) { $(element.parentNode).addClass("disabled"); } else { $(element.parentNode).removeClass("disabled"); } } //Enable|disable certain dependent settings switch(e.id) { case ja_settings.override.elementId: Object.getOwnPropertyNames(ja_settings).forEach(function (a) { var setting = ja_settings[a]; if(setting.group && setting.group === 'override') { ja_log(a + ": " + !e.checked , 3); disable_input(document.getElementById(setting.elementId), (!e.checked || e.disabled) ); } }); break; case ja_settings.guess.elementId: Object.getOwnPropertyNames(ja_settings).forEach(function (a) { var setting = ja_settings[a]; if(setting.group && (setting.group === 'guess' || setting.group === 'override')) { ja_log(a + ": " + !e.checked , 3); disable_input(document.getElementById(setting.elementId), !e.checked); } }); break; case ja_settings.roundaboutOverlayDisplay.elementId: Object.getOwnPropertyNames(ja_settings).forEach(function (a) { var setting = ja_settings[a]; if(setting.group && setting.group === 'roundaboutOverlayDisplay') { ja_log(a +": " + e.value, 3); disable_input(document.getElementById(setting.elementId), e.value === "rOverNever"); } }); break; default: ja_log("Nothing to do for " + e.id, 2); } ja_log("Apply pending configuration changes? " + applyPending, 2); if(applyPending) { ja_log("Applying new settings now", 3); setTimeout(function(){ja_save();}, 500); } else { ja_log("No new settings to apply", 3); } }; var ja_load = function loadJAOptions() { ja_log("Should load settings now.", 2); if(localStorage != null) { ja_log("We have local storage! =)",2); try { ja_options = JSON.parse(localStorage.getItem("wme_ja_options")); } catch (e){ ja_log("Loading settings failed.. " + e.message, 2); ja_options = null; } } if(ja_options == null) { ja_reset(); } else { ja_log(ja_options, 2); setTimeout(function(){ja_apply();}, 500); } }; var ja_save = function saveJAOptions() { ja_log("Saving settings", 2); Object.getOwnPropertyNames(ja_settings).forEach(function (a) { var setting = ja_settings[a]; ja_log(setting, 2); switch (setting.elementType) { case "checkbox": ja_setOption(a, document.getElementById(setting.elementId).checked); break; case "color": var re = /^#[0-9a-f]{6}$/; if(re.test(document.getElementById(setting.elementId).value)) { ja_setOption(a, document.getElementById(setting.elementId).value); } else { ja_setOption(a, ja_settings[a]['default']); } break; case "number": var val = parseInt(document.getElementById(setting.elementId).value); if(!isNaN(val) && val === parseInt(val) && setting.min <= val && val <= setting.max) { ja_setOption(a, document.getElementById(setting.elementId).value); } else { ja_setOption(a, ja_settings[a]['default']); } break; case "text": case "select": ja_setOption(a, document.getElementById(setting.elementId).value); break; default: ja_log("Unknown setting type " + setting.elementType, 2); } }); ja_apply(); return false; }; var ja_apply = function applyJAOptions() { ja_log("Applying stored (or default) settings", 2); if(typeof window.W.map.getLayersBy("uniqueName","junction_angles")[0] === 'undefined') { ja_log("WME not ready yet, trying again in 400 ms", 2); setTimeout(function(){ja_apply();}, 400); return; } if (document.getElementById("sidepanel-ja") == null) { ja_log("WME not ready (no settings tab)", 2); } else { ja_log(Object.getOwnPropertyNames(ja_settings), 2); Object.getOwnPropertyNames(ja_settings).forEach(function (a) { var setting = ja_settings[a]; ja_log(a, 2); ja_log(setting, 2); ja_log(document.getElementById(setting.elementId), 2); switch (setting.elementType) { case "checkbox": document.getElementById(setting.elementId).checked = ja_getOption(a); document.getElementById(setting.elementId).onchange(null); break; case "color": case "number": case "text": document.getElementById(setting.elementId).value = ja_getOption(a); break; case "select": document.getElementById(setting.elementId).value = ja_getOption(a); document.getElementById(setting.elementId).onchange(null); break; default: ja_log("Unknown setting type " + setting.elementType, 2); } }); } window.W.map.getLayersBy("uniqueName","junction_angles")[0].styleMap = ja_style(); ja_calculate_real(); ja_log(ja_options, 2); }; var ja_reset = function resetJAOptions() { ja_log("Resetting settings", 2); if(localStorage != null) { localStorage.removeItem("wme_ja_options"); } ja_options = {}; ja_apply(); return false; }; function ja_helpLink(url, text) { var elem = document.createElement('li'); var l = document.createElement('a'); l.href = url; l.target = "_blank"; l.appendChild(document.createTextNode(ja_getMessage(text))); elem.appendChild(l); return elem; } var ja_calculation_timer = { start: function() { ja_log("Starting timer", 2); this.cancel(); var ja_calculation_timer_self = this; this.timeoutID = window.setTimeout(function(){ja_calculation_timer_self.calculate();}, 200); }, calculate: function() { ja_calculate_real(); delete this.timeoutID; }, cancel: function() { if(typeof this.timeoutID === "number") { window.clearTimeout(this.timeoutID); ja_log("Cleared timeout ID : " + this.timeoutID, 2); delete this.timeoutID; } } }; function ja_calculate() { ja_calculation_timer.start(); } function ja_get_contrast_color(hex_color) { ja_log("Parsing YIQ-based contrast color for: " + hex_color + " ...", 2); var r = parseInt(hex_color.substr(1, 2), 16); var g = parseInt(hex_color.substr(3, 2), 16); var b = parseInt(hex_color.substr(5, 2), 16); var yiq = ((r * 299) + (g * 587) + (b * 114)) / 1000; return (yiq >= 128) ? 'black' : 'white'; } function ja_get_style_rule(routingType, fillColorOption) { return new window.OpenLayers.Rule( { filter: new window.OpenLayers.Filter.Comparison({ type: window.OpenLayers.Filter.Comparison.EQUAL_TO, property: "ja_type", value: routingType }), symbolizer: { pointRadius: 3 + parseInt(ja_getOption("pointSize"), 10) + (parseInt(ja_getOption("decimals")) > 0 ? 4 * parseInt(ja_getOption("decimals")) : 0), fontSize: (parseInt(ja_getOption("pointSize")) - 1) + "px", fillColor: ja_getOption(fillColorOption), strokeColor: "#183800", fontColor: ja_get_contrast_color(ja_getOption(fillColorOption)) } }); } function ja_get_styleOverride_rule(routingType, fillColorOption) { return new window.OpenLayers.Rule( { filter: new window.OpenLayers.Filter.Comparison({ type: window.OpenLayers.Filter.Comparison.EQUAL_TO, property: "ja_type", value: routingType }), symbolizer: { pointRadius: 2+ parseInt(ja_getOption("pointSize"), 10) + (parseInt(ja_getOption("decimals")) > 0 ? 4 * parseInt(ja_getOption("decimals")) : 0), fontSize: (parseInt(ja_getOption("pointSize")) + (ja_getOption("overrideAngles") ? (-1) : 8)) + "px", fillColor: ja_getOption(fillColorOption), strokeColor: "#F68F23", //183800 strokeWidth: 5, fontColor: ja_get_contrast_color(ja_getOption(fillColorOption)) } }); } function ja_style() { ja_log("Point radius will be: " + (parseInt(ja_getOption("pointSize"), 10)) + (parseInt(ja_getOption("decimals") > 0 ? (4 * parseInt(ja_getOption("decimals"))).toString() : "0")), 2); return new window.OpenLayers.Style({ fillColor: "#ffcc88", strokeColor: "#ff9966", strokeWidth: 2, label: "${angle}", fontWeight: "bold", pointRadius: parseInt(ja_getOption("pointSize"), 10) + (parseInt(ja_getOption("decimals")) > 0 ? 4 * parseInt(ja_getOption("decimals")) : 0), fontSize: "10px" }, { rules: [ new window.OpenLayers.Rule({ symbolizer: { } }), ja_get_style_rule(ja_routing_type.TURN, "turnInstructionColor"), ja_get_style_rule(ja_routing_type.TURN_LEFT, "turnInstructionColor"), ja_get_style_rule(ja_routing_type.TURN_RIGHT, "turnInstructionColor"), ja_get_style_rule(ja_routing_type.BC, "noInstructionColor"), ja_get_style_rule(ja_routing_type.KEEP, "keepInstructionColor"), ja_get_style_rule(ja_routing_type.KEEP_LEFT, "keepInstructionColor"), ja_get_style_rule(ja_routing_type.KEEP_RIGHT, "keepInstructionColor"), ja_get_style_rule(ja_routing_type.EXIT, "exitInstructionColor"), ja_get_style_rule(ja_routing_type.EXIT_LEFT, "exitInstructionColor"), ja_get_style_rule(ja_routing_type.EXIT_RIGHT, "exitInstructionColor"), ja_get_style_rule(ja_routing_type.NO_TURN, "noTurnColor"), ja_get_style_rule(ja_routing_type.PROBLEM, "problemColor"), ja_get_style_rule(ja_routing_type.ROUNDABOUT, "roundaboutColor"), ja_get_style_rule(ja_routing_type.ROUNDABOUT_EXIT, "exitInstructionColor"), ja_get_style_rule(ja_routing_type.U_TURN, "uTurnInstructionColor"), ja_get_style_rule(ja_routing_type.NO_U_TURN, "problemColor"), ja_get_styleOverride_rule(ja_routing_type.OverrideTURN_LEFT, "turnInstructionColor"), ja_get_styleOverride_rule(ja_routing_type.OverrideTURN_RIGHT, "turnInstructionColor"), ja_get_styleOverride_rule(ja_routing_type.OverrideBC, "noInstructionColor"), ja_get_styleOverride_rule(ja_routing_type.OverrideCONTINUE, "continueInstructionColor"), ja_get_styleOverride_rule(ja_routing_type.OverrideKEEP_LEFT, "keepInstructionColor"), ja_get_styleOverride_rule(ja_routing_type.OverrideKEEP_RIGHT, "keepInstructionColor"), ja_get_styleOverride_rule(ja_routing_type.OverrideEXIT, "exitInstructionColor"), ja_get_styleOverride_rule(ja_routing_type.OverrideEXIT_LEFT, "exitInstructionColor"), ja_get_styleOverride_rule(ja_routing_type.OverrideEXIT_RIGHT, "exitInstructionColor"), ja_get_styleOverride_rule(ja_routing_type.OverrideU_TURN, "uTurnInstructionColor"), new window.OpenLayers.Rule( { filter: new window.OpenLayers.Filter.Comparison({ type: window.OpenLayers.Filter.Comparison.EQUAL_TO, property: "ja_type", value: "roundaboutOverlay" }), symbolizer: { pointRadius: 3 + parseInt(ja_getOption("pointSize"), 10) + (parseInt(ja_getOption("decimals")) > 0 ? 4 * parseInt(ja_getOption("decimals")) : 0), fontSize: "12px", fillColor: ja_getOption("roundaboutOverlayColor"), fillOpacity: 0.1, strokeColor: ja_getOption("roundaboutOverlayColor"), label: "" } }) ] }); } /* * Translation helpers */ function ja_getMessage(key) { var tr = I18n.translate('ja.' + key), no_tr = I18n.missingTranslation('ja.' + key); return tr === no_tr ? key : tr; } function ja_loadTranslations() { var set_trans = function(def) { /*jshint -W093*/ return I18n.translations[I18n.locale].ja = def; }; ja_log("Loading translations",2); //Apply switch (I18n.locale) { default: //Default language (English) set_trans({ name: "Junction Angle Info", settingsTitle: "Junction Angle Info settings", resetToDefault: "Reset to default", defaultOn: "Show layer by default", aAbsolute: "Absolute", aDeparture: "Departure", angleMode: "Angle mode", angleDisplay: "Angle display style", angleDisplayArrows: "Direction arrows", displayFancy: "Fancy", displaySimple: "Simple", override: "Check \"override instruction\"", overrideAngles: "Show angles of \"override instruction\"", guess: "Estimate routing instructions", noInstructionColor: "Color for best continuation", continueInstructionColor: "Color for continue straight prompt", keepInstructionColor: "Color for keep prompt", exitInstructionColor: "Color for exit prompt", turnInstructionColor: "Color for turn prompt", uTurnInstructionColor: "Color for U-turn prompt", noTurnColor: "Color for disallowed turns", problemColor: "Color for angles to avoid", roundaboutColor: "Color for non-normal roundabouts", roundaboutOverlayColor: "Color for roundabout overlay", roundaboutOverlayDisplay: "Show roundabout", rOverNever: "Never", rOverSelected: "When selected", rOverAlways: "Always", decimals: "Number of decimals", pointSize: "Base point size", roundaboutnav: "WIKI: Roundabouts", ghissues: "JAI issue tracker" }); break; //Czech (čeština) case 'cs': set_trans({ name: "Junction Angle Info", settingsTitle: "Nastavení JAI", resetToDefault: "Výchozí nastavení", defaultOn: "Vždy aktivní", aAbsolute: "Absolutní", aDeparture: "Odjezdový", angleMode: "Styl zobrazení úhlů", angleDisplay: "Styl výpisu úhlů", angleDisplayArrows: "Směrové šipky", displayFancy: "Zdobný", displaySimple: "Jednoduchý", override: "Zvýraznit vynucené hlasové pokyny", overrideAngles: "Zobrazit úhly vynucených pokynů", guess: "Odhadovat navigační hlášky", noInstructionColor: "Bez hlášení", continueInstructionColor: "\"Pokračujte rovně\"", keepInstructionColor: "\"Držte se/Zůstaňte\"", exitInstructionColor: "\"Sjeďte\"", turnInstructionColor: "\"Odbočte\"", uTurnInstructionColor: "\"Otočte se\"", noTurnColor: "Nepovolené směry", problemColor: "Nejasné úhly", roundaboutColor: "Rozbité kruháče", roundaboutOverlayColor: "Kruháče", roundaboutOverlayDisplay: "ukazovat kruháče", rOverNever: "Ne-", rOverSelected: "Při výběru", rOverAlways: "Vždy", decimals: "Počet des. míst", pointSize: "Velikost písma", roundaboutnav: "US WIKI: Kruhové objezdy", ghissues: "Hlášení problémů JAI" }); break; //Finnish (Suomen kieli) case 'fi': set_trans({ name: "Risteyskulmat", settingsTitle: "Rysteyskulmien asetukset", resetToDefault: "Palauta", defaultOn: "Näytä taso oletuksena", aAbsolute: "Absoluuttinen", aDeparture: "Käännös", angleMode: "Kulmien näyttö", angleDisplay: "Näyttötyyli", angleDisplayArrows: "Suuntanuolet", displayFancy: "Nätti", displaySimple: "Yksinkertainen", override: "Check \"override instruction\"", overrideAngles: "Show angles of \"override instruction\"", guess: "Arvioi reititysohjeet", noInstructionColor: "ohjeeton \"Suora\"-väri", continueInstructionColor: "Color for continue straight", keepInstructionColor: "\"Pysy vasemmalla/oikealla\"-ohjeen väri", exitInstructionColor: "\"Poistu\"-ohjeen väri", turnInstructionColor: "\"Käänny\"-ohjeen väri", uTurnInstructionColor: "\"Käänny ympäri\"-ohjeen väri", noTurnColor: "Kielletyn käännöksen väri", problemColor: "Vältettävien kulmien väri", roundaboutColor: "Liikenneympyrän (jolla ei-suoria kulmia) ohjeen väri", roundaboutOverlayColor: "Liikenneympyrän korostusväri", roundaboutOverlayDisplay: "Korosta liikenneympyrä", rOverNever: "Ei ikinä", rOverSelected: "Kun valittu", rOverAlways: "Aina", decimals: "Desimaalien määrä", pointSize: "Ympyrän peruskoko" }); break; //Polish (język polski) case 'pl': set_trans({ settingsTitle: "Ustawienia", resetToDefault: "Przywróć domyślne", defaultOn: "Pokazać warstwę domyślnie", aAbsolute: "Absolutne", aDeparture: "Rozjazdy", angleMode: "Tryb wyświetlania kątów", angleDisplay: "Styl kierunków", displayFancy: "Dwuliniowy", displaySimple: "Prosty", angleDisplayArrows: "Strzałki kierunków", override: "Check \"override instruction\"", overrideAngles: "Show angles of \"override instruction\"", guess: "Szacuj komunikaty trasy", noInstructionColor: "Kolor najlepszej kontynuacji", continueInstructionColor: "Color for continue straight", keepInstructionColor: "Kolor dla \"kieruj się\"", exitInstructionColor: "Kolor dla \"zjedź\"", turnInstructionColor: "Kolor dla \"skręć\"", uTurnInstructionColor: "Kolor dla \"zawróć\"", noTurnColor: "Kolor niedozwolonych manewrów", problemColor: "Kolor problematycznych kątów", roundaboutColor: "Kolor rond niestandardowych", roundaboutOverlayColor: "Kolor znacznika rond", roundaboutOverlayDisplay: "Pokazuj ronda", rOverNever: "Nigdy", rOverSelected: "Gdy zaznaczone", rOverAlways: "Zawsze", decimals: "Ilość cyfr po przecinku", pointSize: "Rozmiar punktów pomiaru" }); break; //Russian (русский) case 'ru': set_trans({ name: "Углы поворотов", settingsTitle: "Настройки Junction Angle Info", resetToDefault: "Сбросить настройки", defaultOn: "По умолчанию показывать", aAbsolute: "Абсолютные", aDeparture: "Повороты", angleMode: "- режим углов", angleDisplay: "- стиль отображения", angleDisplayArrows: "- стрелки направлений", displayFancy: "Модный", displaySimple: "Простой", override: "Визуализировать изменённые подсказки", overrideAngles: "Показывать углы изменённых подсказок", guess: "Ожидаемые подсказки", noInstructionColor: "- нет подсказки", continueInstructionColor: "Цвет изменённой подсказки \"продолжайте движение прямо\"", keepInstructionColor: "- держитесь", exitInstructionColor: "- съезд", turnInstructionColor: "- поверните", uTurnInstructionColor: "- развернитесь", noTurnColor: "- запрещённый манёвр", problemColor: "- угол следует избегать", roundaboutColor: "- некорректное кольцо", roundaboutOverlayColor: "- цвет кольца", roundaboutOverlayDisplay: "- показ колец", rOverNever: "Никогда", rOverSelected: "Если выбрано", rOverAlways: "Всегда", decimals: "- знаков после запятой", pointSize: "- размер кружка", roundaboutnav: "Вики: круговые перекрестки", ghissues: "Сообщить об ошибке" }); break; //Swedish (svenska) case 'sv': set_trans({ name: "Korsningsvinklar", settingsTitle: "Inställningar för korsningsvinklar", resetToDefault: "Återställ", defaultOn: "Visa skiktet som standard", aAbsolute: "Absolut", aDeparture: "Sväng", angleMode: "Vinkelvisning", angleDisplay: "Vinkelstil", angleDisplayArrows: "Riktningspilar", displayFancy: "Grafisk", displaySimple: "Simpel", override: "Check \"override instruction\"", overrideAngles: "Show angles of \"override instruction\"", guess: "Gissa navigeringsinstruktioner", noInstructionColor: "Färg för \"ingen instruktion\"", continueInstructionColor: "Color for continue straight", keepInstructionColor: "Färg för \"håll höger/vänster\"-instruktion", exitInstructionColor: "Färg för \"ta av\"-instruktion", turnInstructionColor: "Färg för \"sväng\"-instruktion", uTurnInstructionColor: "Färg för \"U-sväng\"-instruktion", noTurnColor: "Färg förbjuden sväng", problemColor: "Färg för vinklar att undvika", roundaboutColor: "Färg för rondell (med icke-räta vinklar)", roundaboutOverlayColor: "Färg för rondellcirkel", roundaboutOverlayDisplay: "Visa cirkel på rondell", rOverNever: "Aldrig", rOverSelected: "När vald", rOverAlways: "Alltid", decimals: "Decimaler", pointSize: "Cirkelns basstorlek" }); break; //French (Francais) case 'fr': set_trans({ name: "Junction Angle Info", settingsTitle: "Paramètres de Junction Angle Info", defaultOn: "Activer au démarrage", angleMode: "Mode Angle", aAbsolute: "Absolu", aDeparture: "Départ", angleDisplay: "Style d'affichage d'Angles", angleDisplayArrows: "Flèches de Direction", displayFancy: "Fancy", displaySimple: "Simple", override: "Contrôler les \"overrides instruction\"", overrideAngles: "Afficher les angles des \"overrides\" actives", guess: "Estimer les instructions routage", noInstructionColor: "Couleur sans instruction", continueInstructionColor: "Couleur pour \"continuez tout droit\"", keepInstructionColor: "Couleur pour serrez", exitInstructionColor: "Couleur pour sortez", turnInstructionColor: "Couleur pour tournez", uTurnInstructionColor: "Couleur pour demi-tour", noTurnColor: "Couleur des virages interdits", problemColor: "Couleur des angles à éviter", roundaboutOverlayDisplay: "Surligner les rond-point", rOverNever: "Jamais", rOverSelected: "Sélectionné", rOverAlways: "Toujours", roundaboutOverlayColor: "Couleur de surlignage rond-point", roundaboutColor: "Couleur pour les ronds-points anormaux", decimals: "Nombre de decimales", pointSize: "Taille des bulles", resetToDefault: "Réinitialiser par défaut", roundaboutnav: "WIKI: Rond-point (en)", ghissues: "JAI Reporter un problème" }); break; //Latin-American Spanish (español latinoamericano) case 'es-419': set_trans({ name: "Información en Ángulos de Intersección (JAI)", settingsTitle: "Configuración de Información en Ángulos", resetToDefault: "Limpiar configuración", defaultOn: "Activo al iniciar", aAbsolute: "Absoluto", aDeparture: "Salida", angleMode: "Modo de ángulos", angleDisplay: "Estilo para mostrar", angleDisplayArrows: "Flechas de dirección", displayFancy: "Lujoso", displaySimple: "Simple", override: "Revisar \"instrucciones forzadas\"", overrideAngles: "Ver ángulos en \"instrucciones forzadas\"", guess: "Estimar instrucciones de giro", noInstructionColor: "Sin instrucción", continueInstructionColor: "\"Sigue derecho\"", keepInstructionColor: "\"Mantente\"", exitInstructionColor: "\"Sale\"", turnInstructionColor: "\"Gira\"", uTurnInstructionColor: "\"Gira en U\"", noTurnColor: "Giros deshabilitados", problemColor: "Ángulos a evitar", roundaboutColor: "Rotondas anormales", roundaboutOverlayColor: "Rotondas", roundaboutOverlayDisplay: "Mostrar rotondas", rOverNever: "Nunca", rOverSelected: "Seleccionadas", rOverAlways: "Siempre", decimals: "Decimales", pointSize: "Tamaño del texto", roundaboutnav: "WIKI: Rotondas", ghissues: "Seguimiento de problemas" }); break; //Ukrainian (український) case 'uk': set_trans({ name: "Junction Angle Info", settingsTitle: "Налаштування Junction Angle Info", resetToDefault: "Скинути налаштування", defaultOn: "За замовчуванням показувати", aAbsolute: "Абсолютні", aDeparture: "Повороти", angleMode: "- режим кутів", angleDisplay: "- стиль відображення", angleDisplayArrows: "- стрілки напрямків", displayFancy: "Модний", displaySimple: "Простий", override: "Візуалізувати \"змінені підказки\"", overrideAngles: "Показувати кути для \"змінених підказок\"", guess: "Очікувані підказки:", noInstructionColor: "- немає підказки", continueInstructionColor: "- продовжуйте рух прямо", keepInstructionColor: "- тримайтеся", exitInstructionColor: "- з'їзд", turnInstructionColor: "- поверніть", uTurnInstructionColor: "- розверніться", noTurnColor: "- заборонений маневр", problemColor: "- кут слід уникати", roundaboutColor: "- некоректне кільце", roundaboutOverlayColor: "- колір кільця", roundaboutOverlayDisplay: "- показ кілець", rOverNever: "Ніколи", rOverSelected: "Якщо вибрано", rOverAlways: "Завжди", decimals: "- знаків після коми", pointSize: "- розмір шрифту", roundaboutnav: "WIKI: кругові перехрестя(en)", ghissues: "JAI - Повідомити про помилку" }); break; } } /* * Bootstrapping and logging */ function ja_bootstrap(retries) { retries = retries || 0; //If Waze has not been defined in ~15 seconds, it probably won't work anyway. Might need tuning //for really slow devices? if (retries >= 30) { ja_log("Failed to bootstrap 30 times. Giving up.", 0); return; } try { //User logged in and WME ready if ( ja_is_model_ready() && ja_is_dom_ready() && window.W.loginManager.isLoggedIn()) { setTimeout(function () { junctionangle_init(); }, 500); } //Some part of the WME was not yet fully loaded. Retry. else { setTimeout(function () { ja_bootstrap(++retries); }, 500); } } catch (err) { ja_log(err, 1); setTimeout(function () { ja_bootstrap(++retries); }, 500); } } function ja_is_model_ready() { if(typeof window.W === 'undefined' || typeof window.W.map === 'undefined') { return false; } else { //return 'undefined' !== typeof window.W.map.events.register && return 'undefined' !== typeof window.W.map.olMap.events.register && 'undefined' !== typeof window.W.selectionManager.events.register && 'undefined' !== typeof window.W.loginManager.events.register; } } function ja_is_dom_ready() { if(null === document.getElementById('user-info')) { return false; } else { return document.getElementById('user-info').getElementsByClassName('nav-tabs').length > 0 && document.getElementById('user-info').getElementsByClassName('tab-content').length > 0; } } /** * Debug logging. * @param ja_log_msg * @param ja_log_level */ function ja_log(ja_log_msg, ja_log_level) { //##NO_FF_START## //Firefox addons should not use console.(log|error|debug), so these lines //are removed by the FF addon packaging script. if(typeof ja_log_level === 'undefined') { ja_log_level = 1; } if (ja_log_level <= junctionangle_debug) { if (typeof ja_log_msg === "object") { console.debug(ja_log_msg); } else { console.debug("WME Junction Angle: " + ja_log_msg); } } //##NO_FF_END## } function getByID(obj, id){ if (typeof(obj.getObjectById) == "function"){ return obj.getObjectById(id); }else if (typeof(obj.getObjectById) == "undefined"){ return obj.get(id); } } ja_bootstrap(); } //Dynamically create, add and run the script in the real page context. We really do need access to many of the objects... let run_ja_script = GM_addElement('script', { textContent: "" + run_ja.toString() + " \n" + "run_ja();" });
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址