🏠 Home 

WME Junction Angle Info

Show the angle between two selected (and connected) segments


Install this script?
  1. // ==UserScript==
  2. // @name WME Junction Angle Info
  3. // @description Show the angle between two selected (and connected) segments
  4. // @match https://beta.waze.com/*editor*
  5. // @match https://www.waze.com/*editor*
  6. // @exclude https://www.waze.com/*user/*editor/*
  7. // @version 2.2.16
  8. // @grant GM_addElement
  9. // @namespace https://greasyfork.org/scripts/35547-wme-junction-angle-info/
  10. // @require https://greasyfork.org/scripts/24851-wazewrap/code/WazeWrap.js
  11. // @copyright 2018 seb-d59, 2016 Michael Wikberg <waze@wikberg.fi>
  12. // @license CC-BY-NC-SA
  13. // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAIAAADYYG7QAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAA6pSURBVFhHrZkJcBNnlscbgjEZmHAYsHVZUrek7pbUuo9W674l65Z8WzbGxoAxhzNgcweMDeY0GLDHwYBhGCABTEwIOSaTZBIyye6mcjBJFTs7O5Xazc4ks7NbU7vJzjKVrc0+RVqwhWEC5NW/VFKr3//79fe+fv3JRrp2bjs00NfW3gqqSldojCqFVk4q8LK4z+a2kEosVRVIVnrL60KMU602SfnaohIaQR0zb/72428eNr7+n6/r14bBh2+ahaJCDMOcWk9ndOuFFdeQlrbF4USZ0aqlNDJKS8YrovXNNSa7PlEVSTdVK3REMGIDcbEiuU5c3hDhmgvA6PV3X8p5P2x89ecv6XIRWCX94bFVP//l+l9lhYikpTB8MOauW1y1pqN13ab26oZkrCLo9JuBZj77CYfH0LysJt1UXtdcqXALwOJHu5bkXB8tRl85C26Mj3xm5eU7QLWN5TB8RV0MXpPVYbVRNuOJqVoT5fAx9c1V3bs2Ny2pYSxqX9AWSvlYpqkc87RPP/tNzvKRo6Y9AEzratrvAD25rrW+sYKQCUQ4F17Ti8pXrlmytLUhFHVX1kTLIq7mltrautSuPTsa1qQgubEznjP7PuKtv3sVPGkPfgeomD1bSqHA1La6OVUZikFJYx54raiOdGxYtXvf9i1b1h3q37dpc6cmiELyqdHBnNn3Ef/+pz9aKgkeM/3UinM5oOp0tLy6rLYh7g9Z043Jhqby9nVLgePg4d4Nm9vXrV99fHhw6OmjiUSshJ4CQGCRM5ssvri+SVeIIPVjuY9jy4UzkUJO6MQn2QOTRNv2NNj2NvbmgKJJD3DU1MfWrm/t3Lh6X1/34NN9q59cfnxk8NrLzw2f/PGh/v1LW5pNNgOk6RO###0k8dX7R30LkMLCO0CvrZqr3Hnjq18fNBc2X/v2yCSx//h2cG6vbhtte2VjfBuycduT+/p3Dp8e6N6zdf/h3U+PDJw+f2LkpyeODPXv6O3auHlDV1fXzu5doZQf0sItTM7m7rh1Yw9DrXrx59tlDwY0cmkAnAk9B7oRiqJI74Fth4f2n3n2xPnRM8dODe7p6926Y/OmbRuf6t7avbtn995eiKOHjyRqM32sYpUnZ3PPGKtHJinZ0I1b2SN3xzMvjICzxFxSWV0bjsWRsRcvDB4/1LmlfV9/b++B7u7dXb37d/YdOTBw7GjXzq66dK1erwNwIcGBtNgya87mnjEB6LvET54bAmeucjZBEC6vC9nc1bH3UM/A8MHdfT19R/cOHDuyrfupqtoqlVoFHBKRJGKKd5XvOb70NKTRKSxnc894YKCDJ7vBOdrkunDtwtU3riKwaAaG+3fu69q0bX2yIiGn5MCBi/GkpbK36uArT/4yu/jf6vwQ0kDQ8nNOk8cDA0HfB9vOvauA5tyVc8ja9e2hSJCUksAhxWVVtvT+2oHX1v5tlmO8QiEaMs9dPZFzmjweDOjL//pPV1oJtn0ndw+eGjRbzQhwUISi3tl0uH74zY738yBA729+++aOK5/tOdbTnFnX7T2Lc2bfR7z2zovgaamWXn3jeZihTMmGGk9f7/wwD+Kd9Tfe33L91zsu/8ueoc/3Hfl0108+3vbK6JpRSObbZvzh3z7P+T1yZLti88aqLM2lly8heRwfbHnzH7ov/m7vIHD8dufZG0+9+u6GO7h18QTkb+lbk/N7tHjpzTHUOVMV4WzcvW7V2lXegBfH8QzQOxs++mjr67/peeb3e4+C4A18hIO3OUDnV17e33RgUbyGRU+BB/5HN9/LuT5swB7N36iDy0M1C3GSwHCCLUAFuBT5x56fAgRMCUwMTE8ex9m2i0srFslsJZA5XvLgwt//4bOc90NF9ubi0oWJdHm0psIdCetsdlSuQG52jX6w5S0o1ngO0KXVL3h9ptsEQh/X0JK2rml3djyFBTKPfUet/KEX08GRTO+Bzacn5YzVJi1+pycW8kQjGKVA3uqYsKKh3wwuGrHbjFCaTI51pn3thvTJD1de++a2lo3+SRwi4Ft1lPugO+tbf/nv7EJmmaaYI/pITSxYERLIMEIrswXdhEaNvNnxwdvrf/XGuvf608fSjkY5LseM8yEBZGpdsuTZz8ej3BYcl1dY4BzUOWvf8HYYJjfgfeNnb1+FeYUsLlMQaQgualvsjnkT9SkOzlsgLFZbdErGgOyp7q+y1WmU2mgi2rml05POLDSubVZ8/8U8iDy1Xf2aaVueRafKSqAK99raQnO/9NKZ5ApH9uRS80yRBtPaDI6wy+y32kN2voxfjBWjCpFISSKta1r7j/Vfee0KtAHoB5DAdxXl1eg+qh66np2qrNz1qjXdjT0DG2DaQLBy4ecOtK7cCcapXNUC2mVRM/qFghI+KXSF3dVNNeUNFaGKsMaiK8F4yPOv51rkU/0dkMNmpsEYeaP+VaUOXaWXNfJduVrfpSks3ePF0rlskUBEybUmo9FqVurVEjmB4pjD6zTQRpKSU1q1wmjIAV1+dVQRYkFyZNdI3mDfXVDE2mPvRnefUdR4wIplKOBQbLaUwxGjbFQkJGU8iZiHYXyxmI+hLB53IZsF0uoMBCljc3gcvkAEt312elZ2LQYLdX3Z8sv/kTfM3Qq54Pn5bbjG8r7KavHZT6VJIxhyKTZPTHAwCQDxxDhLiLH4fI5QCFilIhFKECSlWMAqQSViMSmFj6hUnpsh3DsX8mHm86wn19bME/0+QCCYaTBk62cKSbmAkPFxKRsVA5ZATEjkclKpxCkKXuUajRAXYyRezOPNYcGqkiK7+naFqjMzTCZ0rVf+nOc7ibI090bJquXiv4qCYhb9mEAuwaQqTKYQyZWYjIKiiCmlgCSKBfx5HHYRl0MZtNaAh3bDvYYvEPIRq91qDEoBCLpwnukk+m40WUEbA1sOVSLAKZRUimTq7GwJCClXLCoWCtgYypdKZhUXKRiDJeDBDeo5fC5SUZcUWed8p3o9CA0otvc82LJUs3kiOV8iZ6F4CSriiPFSguSTBF9GElqlykIrbUa1w6SwGnReq7s8ijQtTWMAZETqT97Ic5yoTxjhA9CAqgbeyABpZgJQKa4oFohLhBIA4pEkWyJaIOQtREtZhLBUKSYsaqlNixooFiVCevdu5zM/YDPToep5jo+o+pGPAahYOx1oUJmGK5bO4wkXCjGBQsGTk0UojyPFFHa9yscovbS9IiCiKVPEhfzi0knYA7BN09te+N88x0dU09lPMzNkmM6TyIq4wgUCEUdCsAlioRhlERimk5OMCjcrxWaFPmK3lfs1AbOrMoS8fPm0yPoEi57afP53eY6PqPTJjwCIrX9crFQLZVQJJhbIKKGKEmrkhEmp8tBqn5F0qCiv3lEdsKTcujKrMeRABgYPiMxzAKh2+L08R1Dr8Bl0NoII99RmPx4dQktmwsp+bKYYXXmjFQ6eHZPDkQKOYM0n4xNBlUczf2qZT01nYSKBVM4SSeajaClFqJwmfdBKubQyh1IT1Dtr/eZKNx2zE3al1KZB1q5dhluLSoxT4vsuTHS81di1am5B4WMFt4H+3iJGENaK8NM3wyEMQZyOU9/EI3PnN9xYduwgq6A5MiH9m1DPMADxdPPkBiOh0Qopiq+QkbTa4LeaonZj2GSvckZaYp76AO6m6JgVZssUcSJeH62ycuH5Z2ptGW+38toXIRdV2vo3XtttoH92awoRVkdq9KtUCkMKEv5n7wekb64BIKGGI1Yq5nE5M+bPWyDil+CCH/KL5ojnEzaSSTK2Spuz1oOaJZaU0xBikksqkeqacGW1FzJLnUXwdBzv+K2+vdtvlyxbwUxgVM8XmRNulyxbwf/X8ue+ZJsL4TrlBqXKbIQGKFZTKjutchgwPS5mcHPCEmj0+Ro8AKQJ6U1xizluT7VUIen6WCziKKUhedJH/XigWzUtTAGyoMQeLZqBPK4Zqhkbf+YEeTf1Zi7S+EOZnlKZtZiC0NpohVmjsGkNPhMdovUBnc6v1pVpPbVeX11A7zdawvZoXQKprCwL+s0aa+bPq7IU3XTunyZajwN69gy8ndeQ6Z+teztmIYX41lsTT84pfeIDUZkYtlZiIweVCwVSvsai5pOljrCjvLHcVmbBdZjWofQk7I6o1eDRWwIW2DqqGI2IIhCTSUnBwxjnsHXTgAm29BPdxwGNjeEFSKHxWNWpm+XLooVIoXT7+DPvSN9cDVaYZZ7aoqBoqcxA0G69vcwSSHltQbPZR8dqy6I1QbPPYHCqMQoVK0USSoKRYqFYhNC0QqFAlUrMaCdZpql3FW7CGmrq+VHJ7Mxtj8wQoouvN9057Y6yxWKbphl9SpgGkNpGGT3ayuYk7dU5IpZ4fdgeNpt8ekuQphiS1BJCmYCDctgCbikqzMyQTkeYzAq336RyZH5wsZjH/uoO/16CWx3S4cIIhqf3qKVGicpG6V1qg0drcGvcCQeQaRxKwJIzpEgt8KVcRqdBqiMFuICLlgpEGMJY1EaT3O7U+cosZqeWsOZ+pLo6t+cNdn/BHZrdb4BkLoGUxpUOhUDBk5pJ2qc3+PVMwFiq5MFxeK91qpxJR6gmoLDIlGYlAOEKHKdIQi5DnB6T1a5x+ehUTShS4QsmPQoPxmEy/9DQLIrH9+d1y0m04vm/hHeNZPfRHGaGyi8O1AXooJGJMKhWKLfL4b3ao4ZXX43PGDCo3Kr44liyJanzagkTTupxAFLTaqPVpDHoEaffbHVqHR6D0aq2+xlHwGrymmQWMZvOMIHU9WFYVS0X/5jHAWo+/1nZjh/f/hnEYx5Xe2Wxplh0ScyWsDqrXDKb1BA2OssdCo8yvCjkrw8kWxKhxWFr3GKvcNiTNpyWSA0EocEVeoWOMSi1GiReWRZJuGMpL+PQ+WMud8ihcxikeopDlPINJRxmenYwFjNNEpGr4NKXNYKoKoeoLPO/nNy3xqliMwvXYzILCSjQ9OQuiombKbcC2gzw0VET0AQXlbmq3fCtrdwOQJak1ZFyiFQoV8LhYlyUEKES8f8BmIA7Ka4NUW4AAAAASUVORK5CYII=
  14. // ==/UserScript==
  15. /**
  16. * Copyright 2016 Michael Wikberg <waze@wikberg.fi>
  17. * WME Junction Angle Info extension is licensed under a Creative Commons
  18. * Attribution-NonCommercial-ShareAlike 3.0 Unported License.
  19. *
  20. * Contributions by:
  21. * 2014 Paweł Pyrczak "tkr85" <support@pyrczak.pl>
  22. * 2014 "AlanOfTheBerg" <alanoftheberg@gmail.com>
  23. * 2014 "berestovskyy" <?>
  24. * 2015 "FZ69617" <?>
  25. * 2015 "wlodek76" <?>
  26. * 2016 Sergey Kuznetsov "WazeRus" <sergey@izhevsk.pro> (Russian translation)
  27. * 2016 "MajkiiTelini" <?> Czech translation
  28. * 2016 "witoco" <?> (Latin-American Spanish translation)
  29. * 2017 "seb-d59" (Check override instruction and French translation) <https://www.waze.com/forum/memberlist.php?mode=viewprofile&u=16863068>
  30. * 2019 thank to Sapozhnik for the Ukrainian (український) translation
  31. */
  32. /*jshint eqnull:true, nonew:true, nomen:true, curly:true, latedef:true, unused:strict, noarg:true, loopfunc:true */
  33. /*jshint trailing:true, forin:true, noempty:true, maxparams:7, maxerr:100, eqeqeq:true, strict:true, undef:true */
  34. /*jshint bitwise:true, newcap:true, immed:true, onevar:true, browser:true, nonbsp:true, freeze:true */
  35. /*global I18n, $, W*/
  36. function run_ja() {
  37. "use strict";
  38. /*
  39. * First some variable and enumeration definitions
  40. */
  41. var newwmever = W.version.substring(1, 6) >= '2.252'
  42. var junctionangle_version = "2.2.16";
  43. var junctionangle_debug = 0; //0: no output, 1: basic info, 2: debug 3: verbose debug, 4: insane debug
  44. var ja_last_restart = 0, ja_roundabout_points = [], ja_options = {}, ja_mapLayer;
  45. var TURN_ANGLE = 45.50; //Turn vs. keep angle - based on map experiments (45.04 specified in Wiki).
  46. var U_TURN_ANGLE = 168.24; //U-Turn angle based on map experiments.
  47. var GRAY_ZONE = 1.5; //Gray zone angle intended to prevent from irregularities observed on map.
  48. var OVERLAPPING_ANGLE = 0.666; //Experimentally measured overlapping angle.
  49. var ja_routing_type = {
  50. BC: "junction_none",
  51. KEEP: "junction_keep",
  52. KEEP_LEFT: "junction_keep_left",
  53. KEEP_RIGHT: "junction_keep_right",
  54. TURN: "junction_turn",
  55. TURN_LEFT: "junction_turn_left",
  56. TURN_RIGHT: "junction_turn_right",
  57. EXIT: "junction_exit",
  58. EXIT_LEFT: "junction_exit_left",
  59. EXIT_RIGHT: "junction_exit_right",
  60. U_TURN: "junction_u_turn",
  61. PROBLEM: "junction_problem",
  62. NO_TURN: "junction_no_turn",
  63. NO_U_TURN: "junction_no_u_turn",
  64. ROUNDABOUT: "junction_roundabout",
  65. ROUNDABOUT_EXIT: "junction_roundabout_exit",
  66. OverrideBC: "Override_none",
  67. OverrideCONTINUE: "Override_continue",
  68. OverrideKEEP_LEFT: "Override_keep_left",
  69. OverrideKEEP_RIGHT: "Override_keep_right",
  70. OverrideTURN_LEFT: "Override_turn_left",
  71. OverrideTURN_RIGHT: "Override_turn_right",
  72. OverrideEXIT: "Override_exit",
  73. OverrideEXIT_LEFT: "Override_exit_left",
  74. OverrideEXIT_RIGHT: "Override_exit_right",
  75. OverrideU_TURN: "Override_u_turn"
  76. };
  77. var ja_road_type = {
  78. //Streets
  79. NARROW_STREET: 22,
  80. STREET: 1,
  81. PRIMARY_STREET: 2,
  82. //Highways
  83. RAMP: 4,
  84. FREEWAY: 3,
  85. MAJOR_HIGHWAY: 6,
  86. MINOR_HIGHWAY: 7,
  87. //Other drivable
  88. DIRT_ROAD: 8,
  89. FERRY: 14,
  90. PRIVATE_ROAD: 17,
  91. PARKING_LOT_ROAD: 20,
  92. //Non-drivable
  93. WALKING_TRAIL: 5,
  94. PEDESTRIAN_BOARDWALK: 10,
  95. STAIRWAY: 16,
  96. RAILROAD: 18,
  97. RUNWAY: 19
  98. };
  99. var ja_vehicle_types = {
  100. TRUCK: 1,
  101. PUBLIC: 2,
  102. TAXI: 4,
  103. BUS: 8,
  104. HOV2: 16,
  105. HOV3: 32,
  106. RV: 64,
  107. TOWING: 128,
  108. MOTORBIKE: 256,
  109. PRIVATE: 512,
  110. HAZ: ####
  111. };
  112. var ja_settings = {
  113. defaultOn: { elementType: "checkbox", elementId: "_jaCbShowLayer", defaultValue: true },
  114. angleMode: { elementType: "select", elementId: "_jaSelAngleMode", defaultValue: "aDeparture", options: ["aAbsolute", "aDeparture"]},
  115. angleDisplay: { elementType: "select", elementId: "_jaSelAngleDisplay", defaultValue: "displayFancy", options: ["displayFancy", "displaySimple"]},
  116. angleDisplayArrows: { elementType: "select", elementId: "_jaSelAngleDisplayArrows", defaultValue: "⇐⇒⇖⇗⇑", options: ["<><>^", "⇦⇨⇦⇨⇧", "⇐⇒⇐⇒⇑", "←→←→↑", "⇐⇒⇖⇗⇑", "←→↖↗↑"]},
  117. override: { elementType: "checkbox", elementId: "_jaCbOverride", defaultValue: true, group: "guess" },
  118. overrideAngles: { elementType: "checkbox", elementId: "_jaCboverrideAngles", defaultValue: false, group: "override" },
  119. guess: { elementType: "checkbox", elementId: "_jaCbGuessRouting", defaultValue: true },
  120. noInstructionColor: { elementType: "color", elementId: "_jaTbNoInstructionColor", defaultValue: "#ffffff", group: "guess"},
  121. continueInstructionColor: { elementType: "color", elementId: "_jaTbContinueInstructionColor", defaultValue: "#ffffff", group: "guess"},
  122. keepInstructionColor: { elementType: "color", elementId: "_jaTbKeepInstructionColor", defaultValue: "#cbff84", group: "guess"},
  123. exitInstructionColor: { elementType: "color", elementId: "_jaTbExitInstructionColor", defaultValue: "#6cb5ff", group: "guess"},
  124. turnInstructionColor: { elementType: "color", elementId: "_jaTbTurnInstructionColor", defaultValue: "#4cc600", group: "guess"},
  125. uTurnInstructionColor: { elementType: "color", elementId: "_jaTbUTurnInstructionColor", defaultValue: "#b66cff", group: "guess"},
  126. noTurnColor: { elementType: "color", elementId: "_jaTbNoTurnColor", defaultValue: "#a0a0a0", group: "guess"},
  127. problemColor: { elementType: "color", elementId: "_jaTbProblemColor", defaultValue: "#feed40", group: "guess"},
  128. roundaboutOverlayDisplay: { elementType: "select", elementId: "_jaSelRoundaboutOverlayDisplay", defaultValue: "rOverNever", options: ["rOverNever","rOverSelected","rOverAlways"]},
  129. roundaboutOverlayColor: { elementType: "color", elementId: "_jaTbRoundaboutOverlayColor", defaultValue: "#aa0000", group: "roundaboutOverlayDisplay"},
  130. roundaboutColor: { elementType: "color", elementId: "_jaTbRoundaboutColor", defaultValue: "#ff8000", group: "roundaboutOverlayDisplay"},
  131. decimals: { elementType: "number", elementId: "_jaTbDecimals", defaultValue: 2, min: 0, max: 2},
  132. pointSize: { elementType: "number", elementId: "_jaTbPointSize", defaultValue: 12, min: 6, max: 20}
  133. };
  134. var ja_arrow = {
  135. get: function(at) {
  136. var arrows = ja_getOption("angleDisplayArrows");
  137. return arrows[at % arrows.length];
  138. },
  139. left: function() { return this.get(0); },
  140. right: function() { return this.get(1); },
  141. left_up: function() { return this.get(2); },
  142. right_up: function() { return this.get(3); },
  143. up: function() { return this.get(4); }
  144. };
  145. var ja_calculation_Interval = {
  146. set: function(){
  147. this.clear();
  148. ja_log("Starting IntervalID", 2);
  149. this.IntervalID = window.setInterval(ja_calculate, 500);
  150. //console.log("this",this);
  151. },
  152. clear: function(){
  153. if(typeof this.IntervalID === "number"){
  154. ja_log("Cleared IntervalID ID : " + this.IntervalID, 2);
  155. window.clearInterval(this.IntervalID);
  156. delete this.IntervalID;
  157. ja_calculate();
  158. //console.log("this",this);
  159. }
  160. }
  161. };
  162. function getselfeat () {
  163. if (newwmever){
  164. return window.W.selectionManager.getSelectedWMEFeatures();
  165. } else {
  166. return window.W.selectionManager.getSelectedFeatures();
  167. }
  168. }
  169. function testSelectedItem(){
  170. if (getselfeat().length > 1) { return; }
  171. var registerInterval = false;
  172. getselfeat().forEach(function(element) {
  173. switch (element._wmeObject.type) {
  174. case "node":
  175. case "segment":
  176. registerInterval = true;
  177. break;
  178. default:
  179. break;
  180. }
  181. });
  182. if (registerInterval == true){
  183. ja_calculation_Interval.set();
  184. }else{
  185. ja_calculation_Interval.clear();
  186. }
  187. ja_log("ja_calculation_Interval.start = " + registerInterval,2);
  188. }
  189. /*
  190. * Main logic functions
  191. */
  192. function junctionangle_init() {
  193. //Listen for selected nodes change event
  194. //window.W.selectionManager.events.register("selectionchanged", null, ja_calculate);
  195. window.W.selectionManager.events.register("selectionchanged", null, testSelectedItem);
  196. //Temporary workaround. Beta editor changed the event listener logic, but live is still using the old version
  197. //if-else should be removed once not needed anymore
  198. /* if("events" in window.W.model.segments) {
  199. //Live
  200. window.W.model.segments.events.on({
  201. "objectschanged": ja_calculate,
  202. "objectsremoved": ja_calculate
  203. });
  204. window.W.model.nodes.events.on({
  205. "objectschanged": ja_calculate,
  206. "objectsremoved": ja_calculate
  207. });
  208. } else if("_events" in window.W.model.segments) {
  209. */ //Beta editor
  210. window.W.model.segments.on({
  211. "objectschanged": ja_calculate,
  212. "objectsremoved": ja_calculate
  213. });
  214. window.W.model.nodes.on({
  215. "objectschanged": ja_calculate,
  216. "objectsremoved": ja_calculate
  217. });
  218. // }
  219. //Recalculate on zoom end also
  220. window.W.map.olMap.events.register("zoomend", null, ja_calculate);
  221. window.W.map.olMap.events.register("move", null, ja_calculate);
  222. ja_load();
  223. ja_loadTranslations();
  224. setupHtml();
  225. //Add support for translations. Default (and fallback) is "en".
  226. //Note, don't make typos in "acceleratorName", as it has to match the layer name (with whitespace removed)
  227. // to actually work. Took me a while to figure that out...
  228. I18n.translations[window.I18n.locale].layers.name.junction_angles = ja_getMessage("name");
  229. /**
  230. * Initialize JAI OpenLayers vector layer
  231. */
  232. if (window.W.map.getLayersBy("uniqueName","junction_angles").length === 0) {
  233. // Create a vector layer and give it your style map.
  234. ja_mapLayer = new window.OpenLayers.Layer.Vector(ja_getMessage("name"), {
  235. displayInLayerSwitcher: true,
  236. uniqueName: "junction_angles",
  237. shortcutKey: "S+j",
  238. accelerator: "toggle" + ja_getMessage("name").replace(/\s+/g,''),
  239. className: "junction-angles",
  240. styleMap: new window.OpenLayers.StyleMap(ja_style())
  241. });
  242. //Set visibility according to user preference
  243. //ja_mapLayer.setVisibility(ja_getOption("defaultOn"));
  244. window.W.map.addLayer(ja_mapLayer);
  245. ja_log("version " + junctionangle_version + " loaded.", 0);
  246. ja_log(window.W.map, 3);
  247. ja_log(window.W.model, 3);
  248. ja_log(window.W.loginManager, 3);
  249. ja_log(window.W.selectionManager, 3);
  250. ja_log(ja_mapLayer, 3);
  251. ja_log(window.OpenLayers, 3);
  252. } else {
  253. ja_log("Oh, nice.. We already had a layer?", 3);
  254. }
  255. WazeWrap.Interface.AddLayerCheckbox("display", "Junction Angle Info", ja_getOption("defaultOn"), layerToggled);
  256. layerToggled(ja_getOption("defaultOn"))
  257. ja_apply();
  258. // MTE mode event
  259. /*
  260. W.app.modeController.model.bind('change:mode', function(){
  261. if (W.app.modeController.getState() === undefined){
  262. createToggler();
  263. setupHtml();
  264. ja_apply();
  265. }
  266. });
  267. */
  268. // reload after changing WME units
  269. W.prefs.on('change:isImperial', function(){
  270. setupHtml();
  271. ja_apply();
  272. });
  273. ja_calculate();
  274. }
  275. function setupHtml(){
  276. var i, ja_select_option, navTabs, tabContent;
  277. var ja_settings_dom = document.createElement("div");
  278. var ja_settings_dom_panel = document.createElement("div");
  279. var ja_settings_dom_content = document.createElement("div");
  280. var ja_settings_header = document.createElement('h4');
  281. var style = document.createElement('style');
  282. var form = document.createElement('form');
  283. var section = document.createElement('div');
  284. var ja_reset_button = document.createElement('button');
  285. var userTabs = document.getElementById('user-info');
  286. var ja_info = document.createElement('ul');
  287. var ja_version_elem = document.createElement('li');
  288. var jatab = document.createElement('li');
  289. /**
  290. * Add JAI tab configuration options
  291. */
  292. ja_settings_dom_panel.className = "side-panel-section";
  293. ja_settings_dom_content.className = "tab-content";
  294. ja_settings_header.appendChild(document.createTextNode(ja_getMessage("settingsTitle")));
  295. ja_settings_dom_content.appendChild(ja_settings_header);
  296. style.appendChild(document.createTextNode(function () {/*
  297. #jaOptions > *:first-child {
  298. margin-top: 1em;
  299. }
  300. #jaOptions * {
  301. vertical-align: middle;
  302. }
  303. #jaOptions label {
  304. display: inline;
  305. }
  306. #jaOptions input, select {
  307. display: inline;
  308. margin-right: 7px;
  309. box-sizing: border-box;
  310. border: 1px solid #cccccc;
  311. border-radius: 5px;
  312. padding: 3px;
  313. }
  314. #jaOptions input[type="number"] {
  315. width: 4em;
  316. padding: 6px;
  317. }
  318. #jaOptions input[type="color"] {
  319. width: 15%;
  320. height: 2em;
  321. padding: 4px;
  322. }
  323. @supports (-webkit-appearance:none) {
  324. #jaOptions input[type="color"] {
  325. padding: 0px 2px 0px 2px;
  326. }
  327. }
  328. #jaOptions .disabled {
  329. position: relative;
  330. }
  331. #jaOptions .disabled:after {
  332. content: " ";
  333. z-index: 10;
  334. display: block;
  335. position: absolute;
  336. height: 100%;
  337. top: 0;
  338. left: 0;
  339. right: 0;
  340. background: rgba(255, 255, 255, 0.666);
  341. }
  342. */}.toString().match(/[^]*\/\*([^]*)\*\/\}$/)[1]));
  343. section.className = "form-group";
  344. form.className = "attributes-form side-panel-section";
  345. section.id = "jaOptions";
  346. ja_log("---------- Creating settings HTML ----------", 2);
  347. Object.getOwnPropertyNames(ja_settings).forEach(function (a) {
  348. var setting = ja_settings[a];
  349. var ja_controls_container = document.createElement('div');
  350. var ja_input = document.createElement('input');
  351. var ja_label = document.createElement('label');
  352. ja_controls_container.className = "controls-container";
  353. ja_input.type = setting.elementType;
  354. switch (setting.elementType) {
  355. case 'color':
  356. ja_input.id = setting.elementId;
  357. ja_controls_container.appendChild(ja_input);
  358. break;
  359. case 'number':
  360. ja_input.id = setting.elementId;
  361. ja_input.setAttribute("min", setting.min);
  362. ja_input.setAttribute("max", setting.max);
  363. ja_controls_container.appendChild(ja_input);
  364. break;
  365. /*
  366. case 'text':
  367. ja_input.id = setting.elementId;
  368. ja_input.size = (setting.max ? setting.max : 8);
  369. ja_input.maxlength = (setting.max ? setting.max : 7);
  370. ja_controls_container.appendChild(ja_input);
  371. break;
  372. */
  373. case 'checkbox':
  374. ja_input.id = setting.elementId;
  375. ja_controls_container.appendChild(ja_input);
  376. break;
  377. case 'select':
  378. ja_input = document.createElement('select'); //Override <input> with <select>
  379. ja_input.id = setting.elementId;
  380. for(i = 0; i < setting.options.length; i++) {
  381. ja_select_option = document.createElement('option');
  382. ja_select_option.value = setting.options[i];
  383. ja_select_option.appendChild(document.createTextNode(ja_getMessage(setting.options[i])));
  384. ja_input.appendChild(ja_select_option);
  385. }
  386. ja_controls_container.appendChild(ja_input);
  387. break;
  388. default:
  389. ja_log("Unknown setting type " + setting.elementType, 2);
  390. }
  391. ja_input.onchange = function() { ja_onchange(this); };
  392. ja_label.setAttribute("for", setting.elementId);
  393. ja_label.appendChild(document.createTextNode(ja_getMessage(a)));
  394. ja_controls_container.appendChild(ja_label);
  395. section.appendChild(ja_controls_container);
  396. });
  397. section.appendChild(document.createElement('br'));
  398. ja_reset_button.type = "button";
  399. ja_reset_button.className = "btn btn-default";
  400. ja_reset_button.addEventListener("click", ja_reset, true);
  401. ja_reset_button.appendChild(document.createTextNode(ja_getMessage("resetToDefault")));
  402. section.appendChild(document.createElement('div'));
  403. section.appendChild(ja_reset_button);
  404. form.appendChild(section);
  405. ja_settings_dom_content.appendChild(form);
  406. navTabs = userTabs.getElementsByClassName('nav-tabs')[0];
  407. tabContent = userTabs.getElementsByClassName('tab-content')[0];
  408. ja_settings_dom.id = "sidepanel-ja";
  409. ja_settings_dom.className = "tab-pane";
  410. ja_settings_dom_content.style.paddingTop = "0";
  411. ja_settings_dom.appendChild(style);
  412. ja_settings_dom_panel.appendChild(ja_settings_dom_content);
  413. ja_settings_dom.appendChild(ja_settings_dom_panel);
  414. //Add some version info etc
  415. ja_info.className = "list-unstyled -side-panel-section";
  416. ja_info.style.fontSize = "11px";
  417. ja_version_elem.appendChild(document.createTextNode(ja_getMessage("name") + ": v" + junctionangle_version));
  418. ja_info.appendChild(ja_version_elem);
  419. //Add some useful links
  420. ja_info.appendChild(ja_helpLink(
  421. 'https://wiki.waze.com/wiki/Roundabouts/USA#Understanding_navigation_instructions', 'roundaboutnav')
  422. );
  423. ja_info.appendChild(ja_helpLink('https://www.waze.com/forum/viewtopic.php?f=819&t=61926', 'ghissues'));
  424. ja_settings_dom.appendChild(ja_info);
  425. tabContent.appendChild(ja_settings_dom);
  426. jatab.innerHTML = '<!--suppress HtmlUnknownAnchorTarget --><a href="#sidepanel-ja" data-toggle="tab">JAI</a>';
  427. if(navTabs != null) { navTabs.appendChild(jatab); }
  428. }
  429. function layerToggled(visible) {
  430. ja_mapLayer.setVisibility(visible);
  431. }
  432. function ja_guess_routing_instruction(node, s_in_a, s_out_a, angles) {
  433. /**********************************************************************************
  434. * @param node Junction node
  435. * @param s_in_a "In" segment id
  436. * @param s_out_a "Out" segment id
  437. * @param angles array of segment absolute angles [0] angle, [1] segment id, 2[?]
  438. * @returns {string}
  439. **********************************************************************************/
  440. var s_n = {}, s_in = null, s_out = {}, street_n = {}, street_in = null, angle;
  441. var s_in_id = s_in_a;
  442. var s_out_id = s_out_a;
  443. ja_log("Guessing routing instructions from " + s_in_a + " via node " + node.attributes.id + " to " + s_out_a,2);
  444. ja_log(node, 4);
  445. ja_log(s_in_a, 4);
  446. ja_log(s_out_a, 4);
  447. ja_log(angles, 3);
  448. s_in_a = window.$.grep(angles, function(element){
  449. return element[1] === s_in_a;
  450. });
  451. s_out_a = window.$.grep(angles, function(element){
  452. return element[1] === s_out_a;
  453. });
  454. node.attributes.segIDs.forEach(function(element) {
  455. if (element === s_in_id) {
  456. s_in = getByID(node.model.segments,element);
  457. street_in = ja_get_streets(element);
  458. //Set empty name for streets if not defined
  459. if(typeof street_in.primary === 'undefined') { street_in.primary = {}; }
  460. if(typeof street_in.primary.name === 'undefined') {
  461. street_in.primary.name = "";
  462. }
  463. } else {
  464. if(element === s_out_id) {
  465. //store for later use
  466. s_out[element] = getByID(node.model.segments,element);
  467. //Set empty name for streets if not defined
  468. if(typeof s_out[element].primary === 'undefined') {
  469. s_out[element].primary = { name: "" };
  470. }
  471. }
  472. s_n[element] = getByID(node.model.segments,element);
  473. street_n[element] = ja_get_streets(element);
  474. if(typeof street_n[element].primary === 'undefined') {
  475. street_n[element].primary = { name: ""};
  476. }
  477. }
  478. });
  479. ja_log(s_n, 3);
  480. ja_log(street_n,3);
  481. ja_log(s_in,3);
  482. ja_log(street_in,2);
  483. if (s_in === null || street_in === null) {
  484. //Should never happen, but adding to make code validation happy
  485. return ja_routing_type.PROBLEM;
  486. }
  487. angle = ja_angle_diff(s_in_a[0], (s_out_a[0]), false);
  488. ja_log("turn angle is: " + angle, 2);
  489. //Check turn possibility first
  490. if(!ja_is_turn_allowed(s_in, node, s_out[s_out_id])) {
  491. ja_log("Turn is disallowed!", 2);
  492. return ja_routing_type.NO_TURN;
  493. }
  494. //seb-d59:
  495. //Check override instruction
  496. if (ja_getOption("override")){
  497. var WazeModelGraphTurnData = window.require("Waze/Model/Graph/TurnData");
  498. var turn = new WazeModelGraphTurnData();
  499. turn = window.W.model.getTurnGraph().getTurnThroughNode(node, getByID(window.W.model.segments,s_in_id), getByID(window.W.model.segments,s_out_id));
  500. var opcode = turn.getTurnData().getInstructionOpcode();
  501. switch (opcode) {
  502. case "NONE":
  503. ja_log("turn opcode override is: " + opcode, 2);
  504. return ja_routing_type.OverrideBC;
  505. break;
  506. case "CONTINUE":
  507. ja_log("turn opcode override is: " + opcode, 2);
  508. return ja_routing_type.OverrideCONTINUE;
  509. break;
  510. case "TURN_LEFT":
  511. ja_log("turn opcode override is: " + opcode, 2);
  512. return ja_routing_type.OverrideTURN_LEFT;
  513. break;
  514. case "TURN_RIGHT":
  515. ja_log("turn opcode override is: " + opcode, 2);
  516. return ja_routing_type.OverrideTURN_RIGHT;
  517. break;
  518. case "KEEP_LEFT":
  519. ja_log("turn opcode override is: " + opcode, 2);
  520. return ja_routing_type.OverrideKEEP_LEFT;
  521. break;
  522. case "KEEP_RIGHT":
  523. ja_log("turn opcode override is: " + opcode, 2);
  524. return ja_routing_type.OverrideKEEP_RIGHT;
  525. break;
  526. case "EXIT_LEFT":
  527. ja_log("turn opcode override is: " + opcode, 2);
  528. return ja_routing_type.OverrideEXIT_LEFT;
  529. break;
  530. case "EXIT_RIGHT":
  531. ja_log("turn opcode override is: " + opcode, 2);
  532. return ja_routing_type.OverrideEXIT_RIGHT;
  533. break;
  534. case "UTURN":
  535. ja_log("turn opcode override is: " + opcode, 2);
  536. return ja_routing_type.OverrideU_TURN;
  537. break;
  538. default:
  539. ja_log("no turn opcode override", 2);
  540. }
  541. }
  542. //Roundabout - no true instruction guessing here!
  543. if (s_in.attributes.junctionID) {
  544. if (s_out[s_out_id].attributes.junctionID) {
  545. ja_log("Roundabout continuation - no instruction", 2);
  546. return ja_routing_type.BC;
  547. } else {
  548. ja_log("Roundabout exit - no instruction", 2);
  549. //exit just to visually distinguish from roundabout continuation
  550. return ja_routing_type.ROUNDABOUT_EXIT;
  551. }
  552. } else if (s_out[s_out_id].attributes.junctionID) {
  553. ja_log("Roundabout entry - no instruction", 2);
  554. //no instruction since it's normally the only continuation - true instruction can be computed for
  555. //entry-exit selection only
  556. return ja_routing_type.BC;
  557. }
  558. //Check for U-turn, which is emitted even if there is only one s-out
  559. if (Math.abs(angle) > U_TURN_ANGLE + GRAY_ZONE) {
  560. ja_log("Angle is >= 170 - U-Turn", 2);
  561. return ja_routing_type.U_TURN;
  562. } else if (Math.abs(angle) > U_TURN_ANGLE - GRAY_ZONE) {
  563. ja_log("Angle is in gray zone 169-171", 2);
  564. return ja_routing_type.PROBLEM;
  565. }
  566. //No other possible turns
  567. if(node.attributes.segIDs.length <= 2) {
  568. ja_log("Only one possible turn - no instruction", 2);
  569. return ja_routing_type.BC;
  570. } //No instruction
  571. /*
  572. *
  573. * Here be dragons!
  574. *
  575. */
  576. if(Math.abs(angle) < TURN_ANGLE - GRAY_ZONE) {
  577. ja_log("Turn is <= 44", 2);
  578. /*
  579. * Filter out disallowed and non-"BC eligible" turns.
  580. */
  581. ja_log("Original angles and street_n:", 2);
  582. ja_log(angles, 2);
  583. ja_log(street_n, 2);
  584. ja_log(s_n, 2);
  585. angles = angles.filter(function (a) {
  586. ja_log("Filtering angle: " + ja_angle_diff(s_in_a, a[0], false), 2);
  587. if(s_out_id === a[1] ||
  588. (typeof s_n[a[1]] !== 'undefined' &&
  589. ja_is_turn_allowed(s_in, node, s_n[a[1]]) &&
  590. Math.abs(ja_angle_diff(s_in_a, a[0], false)) < TURN_ANGLE //Any angle above 45.04 is not eligible
  591. )) {
  592. ja_log(true, 4);
  593. return true;
  594. } else {
  595. ja_log(false, 4);
  596. if(street_n[a[1]]) {
  597. delete s_n[a[1]];
  598. delete street_n[a[1]];
  599. }
  600. return false;
  601. }
  602. });
  603. ja_log("Filtered angles and street_n:", 2);
  604. ja_log(angles, 2);
  605. ja_log(street_n, 2);
  606. ja_log(s_n, 2);
  607. if(angles.length <= 1) {
  608. ja_log("Only one allowed turn left", 2);
  609. return ja_routing_type.BC;
  610. } //No instruction
  611. /*
  612. * Apply simplified BC logic
  613. */
  614. var bc_matches = {}, bc_prio = 0, bc_count = 0;
  615. var bc_collect = function(a, prio) {
  616. ja_log("Potential BC = " + prio, 2);
  617. ja_log(a, 2);
  618. if (prio > bc_prio) { //highest priority wins now
  619. bc_matches = {};
  620. bc_prio = prio;
  621. bc_count = 0;
  622. }
  623. if (prio === bc_prio) {
  624. bc_matches[a[1]] = a;
  625. bc_count++;
  626. }
  627. ja_log("BC candidates:", 2);
  628. ja_log(bc_matches, 2);
  629. };
  630. //Check each eligible turn against routing rules
  631. for(var k=0; k< angles.length; k++) {
  632. var a = angles[k];
  633. ja_log("Checking angle " + k, 2);
  634. ja_log(a, 2);
  635. var tmp_angle = ja_angle_diff(s_in_a[0], a[0], false);
  636. ja_log(tmp_angle, 2);
  637. var tmp_s_out = {};
  638. tmp_s_out[a[1]] = s_n[a[1]];
  639. var tmp_street_out = {};
  640. tmp_street_out[a[1]] = street_n[a[1]];
  641. var name_match = ja_primary_name_match(street_in, tmp_street_out) ||
  642. ja_alt_name_match(street_in, tmp_street_out) ||
  643. ja_cross_name_match(street_in, tmp_street_out);
  644. if(name_match && ja_segment_type_match(s_in, tmp_s_out)) {
  645. ja_log("BC name and type match", 2);
  646. bc_collect(a, 3);
  647. } else if(name_match) {
  648. ja_log("BC name match", 2);
  649. bc_collect(a, 2);
  650. } else if(ja_segment_type_match(s_in, tmp_s_out)) {
  651. ja_log("BC type match", 2);
  652. bc_collect(a, 1);
  653. }
  654. //Else: Non-BC
  655. }
  656. //If s-out is the only BC, that's it.
  657. if (bc_matches[s_out_id] !== undefined && bc_count === 1) {
  658. ja_log("\"straight\": no instruction", 2);
  659. return ja_routing_type.BC;
  660. }
  661. ja_log("BC logic did not apply; using old default rules instead.", 2);
  662. //FZ69617: Sort angles in left most first order
  663. ja_log("Unsorted angles", 4);
  664. ja_log(angles, 4);
  665. 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]); });
  666. ja_log("Sorted angles", 4);
  667. ja_log(angles, 4);
  668. //wlodek76: FIXING KEEP LEFT/RIGHT regarding to left most segment
  669. //WIKI WAZE: When there are more than two segments less than 45.04°, only the left most segment will be
  670. // KEEP LEFT, all the rest will be KEEP RIGHT
  671. //FZ69617: Wiki seems to be wrong here - experiments shows that "more than two" must be read as "at least two"
  672. //FZ69617: Wiki also does not mention differences between RHT and LHT countries for this consideration,
  673. // but map experiments seem to prove that we have to use reverse logic for LHT countries.
  674. if (!s_in.model.isLeftHand) { //RHT
  675. if (angles[0][1] === s_out_id) { //s-out is left most segment
  676. //wlodek76: KEEP LEFT/RIGHT overlapping case
  677. //WIKI WAZE: If the left most segment is overlapping another segment, it will also be KEEP RIGHT.
  678. if (!ja_overlapping_angles(angles[0][0], angles[1][0])) {
  679. ja_log("Left most <45 segment: keep left", 2);
  680. return ja_routing_type.KEEP_LEFT;
  681. }
  682. }
  683. } else { //LHT
  684. //FZ69617: KEEP RIGHT/LEFT logic for right most segment
  685. //MISSING IN WIKI: When there are at least two segments less than 45.04°, only the right most segment will
  686. // be KEEP RIGHT, all the rest will be KEEP LEFT
  687. if (angles[angles.length - 1][1] === s_out_id) { //s-out is right most segment
  688. //FZ69617: KEEP RIGHT/LEFT overlapping case
  689. //MISSING IN WIKI: If the right most segment is overlapping another segment, it will also be KEEP LEFT.
  690. if (!ja_overlapping_angles(angles[angles.length - 1][0], angles[angles.length - 2][0])) {
  691. ja_log("Right most <45 segment: keep right", 2);
  692. return ja_routing_type.KEEP_RIGHT;
  693. }
  694. }
  695. }
  696. //FZ69617: Two overlapping segments logic
  697. //WAZE WIKI: If the only two segments less than 45.04° overlap each other, neither will get an instruction.
  698. //...
  699. //wlodek76: Three overlapping segments logic
  700. //MISSING IN WIKI: If the ONLY THREE segments less than 45.04° overlap each other, neither will get an instruction.
  701. //...
  702. //FZ69617: Two or more overlapping segments logic
  703. //MISSING IN WIKI: If there are two or more segments less than 45.04° and all these segmentes overlap each other,
  704. // neither will get an instruction.
  705. var overlap_i = 1;
  706. while(overlap_i < angles.length &&
  707. ja_overlapping_angles(angles[0][0], angles[overlap_i][0])) {
  708. ++overlap_i;
  709. }
  710. if(overlap_i > 1 && overlap_i === angles.length) {
  711. ja_log("Two or more overlapping segments only: no instruction", 2);
  712. return ja_routing_type.BC;
  713. }
  714. //Primary to non-primary
  715. if(ja_is_primary_road(s_in) && !ja_is_primary_road(s_out[s_out_id])) {
  716. ja_log("Primary to non-primary = exit", 2);
  717. return s_in.model.isLeftHand ? ja_routing_type.EXIT_LEFT : ja_routing_type.EXIT_RIGHT;
  718. }
  719. //Ramp to non-primary or non-ramp
  720. if(ja_is_ramp(s_in) && !ja_is_primary_road(s_out[s_out_id]) && !ja_is_ramp(s_out[s_out_id]) ) {
  721. ja_log("Ramp to non-primary and non-ramp = exit", 2);
  722. return s_in.model.isLeftHand ? ja_routing_type.EXIT_LEFT : ja_routing_type.EXIT_RIGHT;
  723. }
  724. ja_log("DEFAULT: keep", 2);
  725. return s_in.model.isLeftHand ? ja_routing_type.KEEP_LEFT : ja_routing_type.KEEP_RIGHT;
  726. } else if (Math.abs(angle) < TURN_ANGLE + GRAY_ZONE) {
  727. ja_log("Angle is in gray zone 44-46", 2);
  728. return ja_routing_type.PROBLEM;
  729. } else {
  730. ja_log("Normal turn", 2);
  731. return ja_routing_type.TURN; //Normal turn (left|right)
  732. }
  733. }
  734. function findLayer(partOf_id){
  735. var layer;
  736. for (var i=0; i < window.W.map.layers.length; i++){
  737. if (window.W.map.layers[i].id.search(partOf_id) != -1){
  738. layer={id: window.W.map.layers[i].id, name: window.W.map.layers[i].name, number : i};
  739. ja_log("Number: " + i + "; id : " + layer.id + "; name :" + layer.name ,3);
  740. return layer;
  741. }
  742. }
  743. }
  744. function testLayerZIndex(){
  745. // seb-d59:
  746. // Here i search the selection layer and i read the z-index
  747. // and put JAI's layer under this one.
  748. var zIndex = 0;
  749. ja_mapLayer.setZIndex(500);
  750. // now selection layer has no name ...
  751. var layer = {}
  752. layer = findLayer("OpenLayers_Layer_Vector_RootContainer");
  753. var layerOBJ = window.W.map.layers[layer.number];
  754. //ja_log("id : " + layerOBJ.id + "; name :" + layerOBJ.name + " zIndex: " + layerOBJ.getZIndex() ,3);
  755. zIndex = parseInt(layerOBJ.getZIndex()) - 1 ;
  756. ja_mapLayer.setZIndex(zIndex);
  757. ja_log("ja_mapLayer new zIndex: " + ja_mapLayer.getZIndex() ,3);
  758. }
  759. function ja_calculate_real() {
  760. var ja_start_time = Date.now();
  761. var ja_nodes = [];
  762. var restart = false;
  763. ja_log("Actually calculating now", 2);
  764. ja_roundabout_points = [];
  765. ja_log(window.W.map, 3);
  766. if (typeof ja_mapLayer === 'undefined') {
  767. return;
  768. }
  769. //clear old info
  770. ja_mapLayer.destroyFeatures();
  771. testLayerZIndex();
  772. if (ja_getOption("roundaboutOverlayDisplay") === "rOverAlways") {
  773. ja_draw_roundabout_overlay();
  774. }
  775. //try to show all angles for all selected segments
  776. if (getselfeat().length === 0) { return; }
  777. ja_log("Checking junctions for " + getselfeat().length + " segments", 2);
  778. getselfeat().forEach(function(element) {
  779. ja_log(element, 3);
  780. switch (element._wmeObject.type) {
  781. case "node":
  782. ja_nodes.push(element._wmeObject.attributes.id);
  783. break;
  784. case "segment":
  785. //segments selected?
  786. if (getAttributeValue(element._wmeObject, "attributes.fromNodeID") != null &&
  787. ja_nodes.indexOf(getAttributeValue(element._wmeObject, "attributes.fromNodeID")) === -1) {
  788. ja_nodes.push(element._wmeObject.attributes.fromNodeID);
  789. }
  790. if (getAttributeValue(element._wmeObject, "attributes.toNodeID") != null &&
  791. ja_nodes.indexOf(getAttributeValue(element._wmeObject, "attributes.toNodeID")) === -1) {
  792. ja_nodes.push(element._wmeObject.attributes.toNodeID);
  793. }
  794. break;
  795. case "venue":
  796. break;
  797. default:
  798. ja_log("Found unknown item type: " + element._wmeObject.type, 2);
  799. break;
  800. }
  801. ja_log(ja_nodes, 2);
  802. });
  803. function getAttributeValue(object, path, defaultValue = null) {
  804. return path.split(".").reduce((obj, key) => obj?.[key], object) ?? defaultValue;
  805. }
  806. //Figure out if we have a selected roundabout and do some magic
  807. var ja_selected_roundabouts = {};
  808. ja_nodes.forEach(function(node) {
  809. ja_log(getByID(window.W.model.nodes,node), 3);
  810. var tmp_s = null, tmp_n = null, tmp_junctionID = null;
  811. if(getByID(window.W.model.nodes,node) == null ||
  812. typeof getByID(window.W.model.nodes,node).attributes.segIDs === 'undefined') {
  813. return;
  814. }
  815. getByID(window.W.model.nodes,node).attributes.segIDs.forEach(function(segment) {
  816. ja_log(segment, 3);
  817. if(getAttributeValue(getByID(window.W.model.segments,segment), "attributes.junctionID")) {
  818. ja_log("Roundabout detected: " + getByID(window.W.model.segments,segment).attributes.junctionID, 3);
  819. tmp_junctionID = getByID(window.W.model.segments,segment).attributes.junctionID;
  820. } else {
  821. tmp_s = segment;
  822. tmp_n = node;
  823. }
  824. ja_log("tmp_s: " + (tmp_s === null ? 'null' : tmp_s), 3);
  825. });
  826. ja_log("final tmp_s: " + (tmp_s === null ? 'null' : tmp_s), 3);
  827. if(tmp_junctionID === null) { return; }
  828. if (ja_selected_roundabouts.hasOwnProperty(tmp_junctionID)) {
  829. ja_selected_roundabouts[tmp_junctionID].out_s = tmp_s;
  830. ja_selected_roundabouts[tmp_junctionID].out_n = node;
  831. } else {
  832. ja_selected_roundabouts[tmp_junctionID] = {
  833. 'in_s': tmp_s,
  834. 'in_n': tmp_n,
  835. 'out_s': null,
  836. 'out_n': null,
  837. 'p': getByID(window.W.model.junctions,tmp_junctionID).getOLGeometry()
  838. };
  839. }
  840. });
  841. //Do some fancy painting for the roundabouts...
  842. for(var tmp_roundabout in ja_selected_roundabouts) {
  843. if (ja_selected_roundabouts.hasOwnProperty(tmp_roundabout)) {
  844. ja_log(tmp_roundabout, 3);
  845. ja_log(ja_selected_roundabouts[tmp_roundabout], 3);
  846. //New roundabouts don't have coordinates yet..
  847. if(typeof ja_selected_roundabouts[tmp_roundabout].p === 'undefined' ||
  848. ja_selected_roundabouts[tmp_roundabout].out_n === null
  849. ) {
  850. continue;
  851. }
  852. //Draw circle overlay for this roundabout
  853. if(ja_getOption("roundaboutOverlayDisplay") === "rOverSelected") {
  854. ja_draw_roundabout_overlay(tmp_roundabout);
  855. }
  856. //Transform LonLat to actual layer projection
  857. var tmp_roundabout_center = ja_coordinates_to_point([ja_selected_roundabouts[tmp_roundabout].p.x, ja_selected_roundabouts[tmp_roundabout].p.y]);
  858. var angle = ja_angle_between_points(
  859. getByID(window.W.model.nodes,ja_selected_roundabouts[tmp_roundabout].in_n).getOLGeometry(),
  860. tmp_roundabout_center,
  861. getByID(window.W.model.nodes,ja_selected_roundabouts[tmp_roundabout].out_n).getOLGeometry()
  862. );
  863. ja_mapLayer.addFeatures([
  864. new window.OpenLayers.Feature.Vector(
  865. tmp_roundabout_center,
  866. {
  867. angle: ja_round(angle) + '°',
  868. ja_type: ja_is_roundabout_normal(
  869. tmp_roundabout,
  870. ja_selected_roundabouts[tmp_roundabout].in_n) ? ja_routing_type.TURN : ja_routing_type.ROUNDABOUT
  871. }
  872. )
  873. ]);
  874. }
  875. }
  876. var ja_label_distance;
  877. /*
  878. * Define a base distance to markers, depending on the zoom level
  879. */
  880. switch (window.W.map.olMap.zoom) {
  881. case 22: //10:
  882. ja_label_distance = 2.8;
  883. break;
  884. case 21: //9:
  885. ja_label_distance = 4;
  886. break;
  887. case 20: //8:
  888. ja_label_distance = 8;
  889. break;
  890. case 19: //7:
  891. ja_label_distance = 15;
  892. break;
  893. case 18: //6:
  894. ja_label_distance = 25;
  895. break;
  896. case 17: //5:
  897. ja_label_distance = 40;
  898. break;
  899. case 16: //4:
  900. ja_label_distance = 80;
  901. break;
  902. case 15: //3:
  903. ja_label_distance = 150;
  904. break;
  905. case 14: //2:
  906. ja_label_distance = 300;
  907. break;
  908. case 13: //1:
  909. ja_label_distance = 400;
  910. break;
  911. default:
  912. ja_log("Unsupported zoom level: " + window.W.map.olMap.zoom + "!", 2);
  913. }
  914. ja_label_distance *= (1 + (0.2 * parseInt(ja_getOption("decimals"))));
  915. ja_log("zoom: " + window.W.map.olMap.zoom + " -> distance: " + ja_label_distance, 2);
  916. /**
  917. * Collect double-turn (inc. U-turn) segments info
  918. */
  919. var doubleTurns = {
  920. data: {}, //Structure: map<s_id, map<s_out_id, list<{s_in_id, angle, turn_type}>>>
  921. collect: function (s_id, s_in_id, s_out_id, angle, turn_type) {
  922. ja_log("Collecting double-turn path from " + s_in_id + " to " + s_out_id
  923. + " via " + s_id + " with angle " + angle + " type: " + turn_type, 2);
  924. var info = this.data[s_id];
  925. if (info === undefined) {
  926. info = this.data[s_id] = {};
  927. }
  928. var list = info[s_out_id];
  929. if (list === undefined) {
  930. list = info[s_out_id] = [];
  931. }
  932. list.push({ s_in_id: s_in_id, angle: angle, turn_type: turn_type });
  933. },
  934. forEachItem: function (s_id, s_out_id, fn) {
  935. var info = this.data[s_id];
  936. if (info !== undefined) {
  937. var list = info[s_out_id];
  938. if (list !== undefined) {
  939. list.forEach(function(item, i) {
  940. fn(item, i);
  941. });
  942. }
  943. }
  944. }
  945. };
  946. //Loop through all 15m or less long segments and collect double-turn disallowed ones
  947. if (ja_getOption("angleMode") === "aDeparture" && ja_nodes.length > 1) {
  948. getselfeat().forEach(function (selectedSegment) {
  949. var segmentId = selectedSegment._wmeObject.attributes.id;
  950. var segment = window.W.model.segments.objects[segmentId];
  951. ja_log("Checking " + segmentId + " for double turns ...", 2);
  952. var len = ja_segment_length(segment);
  953. ja_log("Segment " + segmentId + " length: " + len, 2);
  954. if (Math.round(len) <= 15) {
  955. var fromNode = getByID(window.W.model.nodes,segment.attributes.fromNodeID);
  956. var toNode = getByID(window.W.model.nodes,segment.attributes.toNodeID);
  957. var a_from = ja_getAngleMidleSeg(segment.attributes.fromNodeID, segment);
  958. var a_to = ja_getAngleMidleSeg(segment.attributes.toNodeID, segment);
  959. fromNode.attributes.segIDs.forEach(function (fromSegmentId) {
  960. if (fromSegmentId === segmentId) return;
  961. var fromSegment = window.W.model.segments.objects[fromSegmentId];
  962. if(!ja_is_up_to_primary_road(fromSegment)) return;
  963. var from_a = ja_getAngle(segment.attributes.fromNodeID, fromSegment);
  964. var from_angle = ja_angle_diff(from_a, a_from, false);
  965. ja_log("Segment from " + fromSegmentId + " angle: " + from_a + ", turn angle: " + from_angle, 2);
  966. toNode.attributes.segIDs.forEach(function (toSegmentId) {
  967. if (toSegmentId === segmentId) return;
  968. var toSegment = window.W.model.segments.objects[toSegmentId];
  969. if(!ja_is_up_to_primary_road(toSegment)) return;
  970. var to_a = ja_getAngle(segment.attributes.toNodeID, toSegment);
  971. var to_angle = ja_angle_diff(to_a, a_to, false);
  972. ja_log("Segment to " + toSegmentId + " angle: " + to_a + ", turn angle: " + to_angle, 2);
  973. var angle = Math.abs(to_angle - from_angle);
  974. ja_log("Angle from " + fromSegmentId + " to " + toSegmentId + " is: " + angle, 2);
  975. //Determine whether a turn is disallowed
  976. if (angle >= 175 - GRAY_ZONE && angle <= 185 + GRAY_ZONE) {
  977. var turn_type = (angle >= 175 + GRAY_ZONE && angle <= 185 - GRAY_ZONE) ?
  978. ja_routing_type.NO_U_TURN : ja_routing_type.PROBLEM;
  979. if (ja_is_turn_allowed(fromSegment, fromNode, segment) &&
  980. ja_is_turn_allowed(segment, toNode, toSegment)) {
  981. doubleTurns.collect(segmentId, fromSegmentId, toSegmentId, angle, turn_type);
  982. }
  983. if (ja_is_turn_allowed(toSegment, toNode, segment) &&
  984. ja_is_turn_allowed(segment, fromNode, fromSegment)) {
  985. doubleTurns.collect(segmentId, toSegmentId, fromSegmentId, angle, turn_type);
  986. }
  987. }
  988. });
  989. });
  990. }
  991. });
  992. }
  993. ja_log("Collected double-turn segments:", 2);
  994. ja_log(doubleTurns.data, 2);
  995. //Start looping through selected nodes
  996. for (var i = 0; i < ja_nodes.length; i++) {
  997. var node = getByID(window.W.model.nodes,ja_nodes[i]);
  998. var angles = [];
  999. var ja_selected_segments_count = 0;
  1000. var ja_selected_angles = [];
  1001. var a;
  1002. if (node == null || !node.hasOwnProperty('attributes')) {
  1003. //Oh oh.. should not happen? We want to use a node that does not exist
  1004. ja_log("Oh oh.. should not happen?",2);
  1005. ja_log(node, 2);
  1006. ja_log(ja_nodes[i], 2);
  1007. ja_log(window.W.model, 3);
  1008. ja_log(window.W.model.nodes, 3);
  1009. continue;
  1010. }
  1011. //check connected segments
  1012. var ja_current_node_segments = node.attributes.segIDs;
  1013. ja_log(node, 2);
  1014. //ignore of we have less than 2 segments
  1015. if (ja_current_node_segments.length <= 1) {
  1016. ja_log("Found only " + ja_current_node_segments.length + " connected segments at " + ja_nodes[i] +
  1017. ", not calculating anything...", 2);
  1018. continue;
  1019. }
  1020. ja_log("Calculating angles for " + ja_current_node_segments.length + " segments", 2);
  1021. ja_log(ja_current_node_segments, 3);
  1022. ja_current_node_segments.forEach(function (nodeSegment, j) {
  1023. var s = window.W.model.segments.objects[nodeSegment];
  1024. if(typeof s === 'undefined') {
  1025. //Meh. Something went wrong, and we lost track of the segment. This needs a proper fix, but for now
  1026. // it should be sufficient to just restart the calculation
  1027. ja_log("Failed to read segment data from model. Restarting calculations.", 1);
  1028. if(ja_last_restart === 0) {
  1029. ja_last_restart = new Date().getTime();
  1030. setTimeout(function(){ja_calculate();}, 500);
  1031. }
  1032. restart = true;
  1033. }
  1034. a = ja_getAngle(ja_nodes[i], s);
  1035. ja_log("Segment " + nodeSegment + " angle is " + a, 2);
  1036. angles[j] = [a, nodeSegment, s == null ? false : s.isSelected()];
  1037. if (s == null ? false : s.isSelected()) {
  1038. ja_selected_segments_count++;
  1039. }
  1040. });
  1041. if(restart) { return; }
  1042. //make sure we have the selected angles in correct order
  1043. ja_log(ja_current_node_segments, 3);
  1044. getselfeat().forEach(function (selectedSegment) {
  1045. var selectedSegmentId = selectedSegment._wmeObject.attributes.id;
  1046. ja_log("Checking if " + selectedSegmentId + " is in current node", 3);
  1047. if(ja_current_node_segments.indexOf(selectedSegmentId) >= 0) {
  1048. ja_log("It is!", 4);
  1049. //find the angle
  1050. for(var j=0; j < angles.length; j++) {
  1051. if(angles[j][1] === selectedSegmentId) {
  1052. ja_selected_angles.push(angles[j]);
  1053. break;
  1054. }
  1055. }
  1056. } else {
  1057. ja_log("It's not..", 4);
  1058. }
  1059. });
  1060. ja_log(angles, 3);
  1061. var ha, point;
  1062. //if we have two connected segments selected, do some magic to get the turn angle only =)
  1063. if (ja_selected_segments_count === 2) {
  1064. var ja_extra_space_multiplier = 1;
  1065. a = ja_angle_diff(ja_selected_angles[0][0], ja_selected_angles[1][0], false);
  1066. ha = (parseFloat(ja_selected_angles[0][0]) + parseFloat(ja_selected_angles[1][0]))/2;
  1067. if((Math.abs(ja_selected_angles[0][0]) + Math.abs(ja_selected_angles[1][0])) > 180 &&
  1068. ((ja_selected_angles[0][0] < 0 && ja_selected_angles[1][0] > 0) ||
  1069. (ja_selected_angles[0][0] > 0 && ja_selected_angles[1][0] < 0))
  1070. ) {
  1071. ha += 180;
  1072. }
  1073. if (Math.abs(a) > 120) {
  1074. ja_log("Sharp angle", 2);
  1075. ja_extra_space_multiplier = 2;
  1076. }
  1077. //Move point a bit if it's on the top (Bridge icon will obscure it otherwise)
  1078. if(ha > 40 && ha < 120) { ja_extra_space_multiplier = 2; }
  1079. ja_log("Angle between " + ja_selected_angles[0][1] + " and " + ja_selected_angles[1][1] + " is " +
  1080. a + " and position for label should be at " + ha, 3);
  1081. //Guess some routing instructions based on segment types, angles etc
  1082. var ja_junction_type = ja_routing_type.TURN; //Default to old behavior
  1083. if(ja_getOption("guess")) {
  1084. ja_log(ja_selected_angles, 2);
  1085. ja_log(angles, 2);
  1086. ja_junction_type =
  1087. ja_guess_routing_instruction(node, ja_selected_angles[0][1], ja_selected_angles[1][1], angles);
  1088. ja_log("Type is: " + ja_junction_type, 2);
  1089. }
  1090. //get the initial marker point
  1091. point = new window.OpenLayers.Geometry.Point(
  1092. node.getOLGeometry().x + (ja_extra_space_multiplier * ja_label_distance * Math.cos((ha * Math.PI) / 180)),
  1093. node.getOLGeometry().y + (ja_extra_space_multiplier * ja_label_distance * Math.sin((ha * Math.PI) / 180))
  1094. );
  1095. ja_draw_marker(point, node, ja_label_distance, a, ha, true, ja_junction_type);
  1096. //draw double turn markers
  1097. doubleTurns.forEachItem(ja_selected_angles[0][1], ja_selected_angles[1][1], function(item) {
  1098. ja_draw_marker(point, node, ja_label_distance, item.angle, ha, true, item.turn_type);
  1099. });
  1100. }
  1101. else {
  1102. //sort angle data (ascending)
  1103. angles.sort(function (a, b) {
  1104. return a[0] - b[0];
  1105. });
  1106. ja_log(angles, 3);
  1107. ja_log(ja_selected_segments_count, 3);
  1108. //get all segment angles
  1109. angles.forEach(function(angle, j) {
  1110. a = (360 + (angles[(j + 1) % angles.length][0] - angle[0])) % 360;
  1111. ha = (360 + ((a / 2) + angle[0])) % 360;
  1112. var a_in = angles.filter(function(a) {
  1113. return !!a[2];
  1114. })[0];
  1115. //Show only one angle for nodes with only 2 connected segments and a single selected segment
  1116. // (not on both sides). Skipping the one > 180
  1117. if (ja_selected_segments_count === 1 &&
  1118. angles.length === 2 &&
  1119. a >=180 &&
  1120. ja_getOption("angleMode") !== "aDeparture"
  1121. ) {
  1122. ja_log("Skipping marker, as we need only one of them", 2);
  1123. return;
  1124. }
  1125. if(ja_getOption("angleMode") === "aDeparture" && ja_selected_segments_count > 0) {
  1126. if(a_in[1] === angle[1]) {
  1127. ja_log("in == out. skipping.", 2);
  1128. return;
  1129. }
  1130. ja_log("Angle in:",2);
  1131. ja_log(a_in,2);
  1132. ja_log(ja_guess_routing_instruction(node, a_in[1], angle[1], angles), 2);
  1133. //FIXME: we might want to try to keep the marker on the segment, instead of just
  1134. //in the direction of the first part
  1135. ha = angle[0];
  1136. a = ja_angle_diff(a_in[0], angles[j][0], false);
  1137. point = new window.OpenLayers.Geometry.Point(
  1138. node.getOLGeometry().x + (ja_label_distance * 2 * Math.cos((ha * Math.PI) / 180)),
  1139. node.getOLGeometry().y + (ja_label_distance * 2 * Math.sin((ha * Math.PI) / 180))
  1140. );
  1141. ja_draw_marker(point, node, ja_label_distance, a, ha, true,
  1142. ja_getOption("guess") ?
  1143. ja_guess_routing_instruction(node, a_in[1], angle[1], angles) : ja_routing_type.TURN);
  1144. //draw double turn markers
  1145. doubleTurns.forEachItem(a_in[1], angle[1], function(item) {
  1146. ja_draw_marker(point, node, ja_label_distance, item.angle, ha, true, item.turn_type);
  1147. });
  1148. } else {
  1149. ja_log("Angle between " + angle[1] + " and " + angles[(j + 1) % angles.length][1] + " is " +
  1150. a + " and position for label should be at " + ha, 3);
  1151. point = new window.OpenLayers.Geometry.Point(
  1152. node.getOLGeometry().x + (ja_label_distance * 1.25 * Math.cos((ha * Math.PI) / 180)),
  1153. node.getOLGeometry().y + (ja_label_distance * 1.25 * Math.sin((ha * Math.PI) / 180))
  1154. );
  1155. ja_draw_marker(point, node, ja_label_distance, a, ha);
  1156. }
  1157. });
  1158. }
  1159. }
  1160. //testLayerZIndex();
  1161. ja_last_restart = 0;
  1162. var ja_end_time = Date.now();
  1163. ja_log("Calculation took " + String(ja_end_time - ja_start_time) + " ms", 2);
  1164. }
  1165. /*
  1166. * Drawing functions
  1167. */
  1168. /**
  1169. *
  1170. * @param point Estimated point for marker
  1171. * @param node Node the marker is for
  1172. * @param ja_label_distance Arbitrary distance to be used in moving markers further away etc
  1173. * @param a Angle to display
  1174. * @param ha Angle to marker from node (FIXME: either point or ha is probably unnecessary)
  1175. * @param withRouting true: show routing guessing markers, false: show "normal" angle markers
  1176. * @param ja_junction_type If using routing, this needs to be set to the desired type
  1177. */
  1178. function ja_draw_marker(point, node, ja_label_distance, a, ha, withRouting, ja_junction_type) {
  1179. //Try to estimate of the point is "too close" to another point
  1180. //(or maybe something else in the future; like turn restriction arrows or something)
  1181. //FZ69617: Exctract initial label distance from point
  1182. var ja_tmp_distance = Math.abs(ha) % 180 < 45 || Math.abs(ha) % 180 > 135 ?
  1183. (point.x - node.getOLGeometry().x) / (Math.cos((ha * Math.PI) / 180)) :
  1184. (point.y - node.getOLGeometry().y) / (Math.sin((ha * Math.PI) / 180));
  1185. ja_log("Starting distance estimation", 3);
  1186. while(ja_mapLayer.features.some(function(feature){
  1187. if(typeof feature.attributes.ja_type !== 'undefined' && feature.attributes.ja_type !== 'roundaboutOverlay') {
  1188. //Arbitrarily chosen minimum distance.. Should actually use the real bounds of the markers,
  1189. //but that didn't work out.. Bounds are always 0..
  1190. if(ja_label_distance / 1.4 > feature.geometry.distanceTo(point)) {
  1191. ja_log(ja_label_distance / 1.5 > feature.geometry.distanceTo(point) + " is kinda close..", 3);
  1192. return true;
  1193. }
  1194. }
  1195. return false;
  1196. })) {
  1197. //add 1/4 of the original distance and hope for the best =)
  1198. ja_tmp_distance += ja_label_distance / 4;
  1199. ja_log("setting distance to " + ja_tmp_distance, 2);
  1200. point = new window.OpenLayers.Geometry.Point(
  1201. node.getOLGeometry().x + (ja_tmp_distance * Math.cos((ha * Math.PI) / 180)),
  1202. node.getOLGeometry().y + (ja_tmp_distance * Math.sin((ha * Math.PI) / 180))
  1203. );
  1204. }
  1205. ja_log("Distance estimation done", 3);
  1206. var angleString = ja_round(Math.abs(a)) + "°";
  1207. //FZ69617: Add direction arrows for turn instructions only
  1208. if (ja_getOption("angleDisplay") === "displaySimple") {
  1209. switch(ja_junction_type) {
  1210. case ja_routing_type.TURN:
  1211. angleString = a > 0 ? ja_arrow.left() + angleString : angleString + ja_arrow.right();
  1212. break;
  1213. case ja_routing_type.TURN_LEFT:
  1214. angleString = ja_arrow.left() + angleString;
  1215. break;
  1216. case ja_routing_type.TURN_RIGHT:
  1217. angleString = angleString + ja_arrow.right();
  1218. break;
  1219. case ja_routing_type.EXIT:
  1220. case ja_routing_type.KEEP:
  1221. angleString = a > 0 ? ja_arrow.left_up() + angleString : angleString + ja_arrow.right_up();
  1222. break;
  1223. case ja_routing_type.EXIT_LEFT:
  1224. case ja_routing_type.KEEP_LEFT:
  1225. angleString = ja_arrow.left_up() + angleString;
  1226. break;
  1227. case ja_routing_type.EXIT_RIGHT:
  1228. case ja_routing_type.KEEP_RIGHT:
  1229. angleString += ja_arrow.right_up();
  1230. break;
  1231. //Override
  1232. case ja_routing_type.OverrideBC:
  1233. angleString = ja_getOption("overrideAngles") ? angleString : "";
  1234. break;
  1235. case ja_routing_type.OverrideCONTINUE:
  1236. angleString = ja_arrow.up() + (ja_getOption("overrideAngles") ? angleString : "");
  1237. break;
  1238. case ja_routing_type.OverrideTURN_LEFT:
  1239. angleString = ja_arrow.left() + (ja_getOption("overrideAngles") ? angleString : "");
  1240. break;
  1241. case ja_routing_type.OverrideTURN_RIGHT:
  1242. angleString = (ja_getOption("overrideAngles") ? angleString : "") + ja_arrow.right();
  1243. break;
  1244. case ja_routing_type.OverrideEXIT_LEFT:
  1245. case ja_routing_type.OverrideKEEP_LEFT:
  1246. angleString = ja_arrow.left_up() + (ja_getOption("overrideAngles") ? angleString : "");
  1247. break;
  1248. case ja_routing_type.OverrideEXIT_RIGHT:
  1249. case ja_routing_type.OverrideKEEP_RIGHT:
  1250. angleString = (ja_getOption("overrideAngles") ? angleString : "") + ja_arrow.right_up();
  1251. default:
  1252. ja_log("No extra format for junction type: " + ja_junction_type, 2);
  1253. }
  1254. } else {
  1255. switch(ja_junction_type) {
  1256. case ja_routing_type.TURN:
  1257. angleString = (a > 0 ? ja_arrow.left() : ja_arrow.right()) + "\n" + angleString;
  1258. break;
  1259. case ja_routing_type.TURN_LEFT:
  1260. angleString = ja_arrow.left() + "\n" + angleString;
  1261. break;
  1262. case ja_routing_type.TURN_RIGHT:
  1263. angleString = ja_arrow.right() + "\n" + angleString;
  1264. break;
  1265. case ja_routing_type.EXIT:
  1266. case ja_routing_type.KEEP:
  1267. angleString = (a > 0 ? ja_arrow.left_up() : ja_arrow.right_up()) + "\n" + angleString;
  1268. break;
  1269. case ja_routing_type.EXIT_LEFT:
  1270. case ja_routing_type.KEEP_LEFT:
  1271. angleString = ja_arrow.left_up() + "\n" + angleString;
  1272. break;
  1273. case ja_routing_type.EXIT_RIGHT:
  1274. case ja_routing_type.KEEP_RIGHT:
  1275. angleString = ja_arrow.right_up() + "\n" + angleString;
  1276. break;
  1277. case ja_routing_type.PROBLEM:
  1278. angleString = "?\n" + angleString;
  1279. break;
  1280. //Override
  1281. case ja_routing_type.OverrideBC:
  1282. angleString = ja_getOption("overrideAngles") ? angleString : "";
  1283. break;
  1284. case ja_routing_type.OverrideCONTINUE:
  1285. angleString = ja_arrow.up() + (ja_getOption("overrideAngles") ? ("\n" + angleString) : "");
  1286. break;
  1287. case ja_routing_type.OverrideTURN_LEFT:
  1288. angleString = ja_arrow.left() + (ja_getOption("overrideAngles") ? ("\n" + angleString) : "");
  1289. break;
  1290. case ja_routing_type.OverrideTURN_RIGHT:
  1291. angleString = ja_arrow.right() + (ja_getOption("overrideAngles") ? ("\n" + angleString) : "");
  1292. break;
  1293. case ja_routing_type.OverrideEXIT_LEFT:
  1294. case ja_routing_type.OverrideKEEP_LEFT:
  1295. angleString = ja_arrow.left_up() + (ja_getOption("overrideAngles") ? ("\n" + angleString) : "");
  1296. break;
  1297. case ja_routing_type.OverrideEXIT_RIGHT:
  1298. case ja_routing_type.OverrideKEEP_RIGHT:
  1299. angleString = ja_arrow.right_up() + (ja_getOption("overrideAngles") ? ("\n" + angleString) : "");
  1300. break;
  1301. default:
  1302. ja_log("No extra format for junction type: " + ja_junction_type, 2);
  1303. }
  1304. }
  1305. var anglePoint = withRouting ?
  1306. new window.OpenLayers.Feature.Vector(
  1307. point,
  1308. { angle: angleString, ja_type: ja_junction_type }
  1309. ): new window.OpenLayers.Feature.Vector(
  1310. point,
  1311. { angle: ja_round(a) + "°", ja_type: "generic" }
  1312. );
  1313. ja_log(anglePoint, 3);
  1314. //Don't paint points inside an overlaid roundabout
  1315. if(ja_roundabout_points.some(function (roundaboutPoint){
  1316. return roundaboutPoint.containsPoint(point);
  1317. })) {
  1318. return;
  1319. }
  1320. //Draw a line to the point
  1321. ja_mapLayer.addFeatures([
  1322. new window.OpenLayers.Feature.Vector(
  1323. new window.OpenLayers.Geometry.LineString([node.getOLGeometry(), point]),
  1324. {},
  1325. {strokeOpacity: 0.6, strokeWidth: 1.2, strokeDashstyle: "solid", strokeColor: "#ff9966"}
  1326. )
  1327. ]
  1328. );
  1329. //push the angle point
  1330. ja_mapLayer.addFeatures([anglePoint]);
  1331. }
  1332. function ja_draw_roundabout_overlay(junctionId) {
  1333. (junctionId === undefined ? (window.W.model.junctions.getObjectArray()) : (function (junction) {
  1334. return junction === undefined ? [] : [ junction ];
  1335. })
  1336. (getByID(window.W.model.junctions,junctionId))).forEach(function (element) {
  1337. ja_log(element, 3);
  1338. var nodes = {};
  1339. element.attributes.segIDs.forEach(function(s) {
  1340. var seg = getByID(window.W.model.segments,s);
  1341. ja_log(seg, 3);
  1342. nodes[seg.attributes.fromNodeID] = getByID(window.W.model.nodes,seg.attributes.fromNodeID);
  1343. nodes[seg.attributes.toNodeID] = getByID(window.W.model.nodes,seg.attributes.toNodeID);
  1344. });
  1345. ja_log(nodes, 3);
  1346. var center = ja_coordinates_to_point([element.getOLGeometry().x, element.getOLGeometry().y]);
  1347. ja_log(center, 3);
  1348. var distances = [];
  1349. Object.getOwnPropertyNames(nodes).forEach(function(name) {
  1350. ja_log("Checking " + name + " distance", 3);
  1351. var dist = Math.sqrt(
  1352. Math.pow(nodes[name].getOLGeometry().x - center.x, 2) +
  1353. Math.pow(nodes[name].getOLGeometry().y - center.y, 2)
  1354. );
  1355. distances.push(dist);
  1356. });
  1357. ja_log(distances, 3);
  1358. ja_log("Mean distance is " + distances.reduce(function(a,b){return a + b;}) / distances.length, 3);
  1359. var circle = window.OpenLayers.Geometry.Polygon.createRegularPolygon(
  1360. center,
  1361. distances.reduce(function(a,b){return a + b;}) / distances.length,
  1362. 40,
  1363. 0
  1364. );
  1365. var roundaboutCircle = new window.OpenLayers.Feature.Vector(circle, {'ja_type': 'roundaboutOverlay'});
  1366. ja_roundabout_points.push(circle);
  1367. ja_mapLayer.addFeatures([roundaboutCircle]);
  1368. });
  1369. }
  1370. /*
  1371. * Segment and routing helpers
  1372. */
  1373. /**
  1374. * Check if segment in type matches any other segments
  1375. * @param segment_in
  1376. * @param segments
  1377. * @returns {boolean}
  1378. */
  1379. function ja_segment_type_match(segment_in, segments) {
  1380. ja_log(segment_in, 2);
  1381. ja_log(segments, 2);
  1382. return Object.getOwnPropertyNames(segments).some(function (segment_n_id, index) {
  1383. var segment_n = segments[segment_n_id];
  1384. ja_log("PT Checking element " + index, 2);
  1385. ja_log(segment_n, 2);
  1386. if(segment_n.attributes.id === segment_in.attributes.id) { return false; }
  1387. ja_log("PT checking sn.rt " + segment_n.attributes.roadType +
  1388. " vs i.pt: " + segment_in.attributes.roadType, 2);
  1389. return (segment_n.attributes.roadType === segment_in.attributes.roadType);
  1390. });
  1391. }
  1392. function ja_is_primary_road(seg) {
  1393. var t = seg.attributes.roadType;
  1394. return t === ja_road_type.FREEWAY || t === ja_road_type.MAJOR_HIGHWAY || t === ja_road_type.MINOR_HIGHWAY;
  1395. }
  1396. function ja_is_up_to_primary_road(seg) {
  1397. var t = seg.attributes.roadType;
  1398. 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;
  1399. }
  1400. function ja_is_ramp(seg) {
  1401. var t = seg.attributes.roadType;
  1402. return t === ja_road_type.RAMP;
  1403. }
  1404. function ja_is_turn_allowed(s_from, via_node, s_to) {
  1405. ja_log("Allow from " + s_from.attributes.id +
  1406. " to " + s_to.attributes.id +
  1407. " via " + via_node.attributes.id + "? " +
  1408. via_node.isTurnAllowedBySegDirections(s_from, s_to) + " | " + s_from.isTurnAllowed(s_to, via_node), 2);
  1409. //Is there a driving direction restriction?
  1410. if(!via_node.isTurnAllowedBySegDirections(s_from, s_to)) {
  1411. ja_log("Driving direction restriction applies", 3);
  1412. return false;
  1413. }
  1414. //Is turn allowed by other means (e.g. turn restrictions)?
  1415. if(!s_from.isTurnAllowed(s_to, via_node)) {
  1416. ja_log("Other restriction applies", 3);
  1417. return false;
  1418. }
  1419. if(s_to.attributes.fromNodeID === via_node.attributes.id) {
  1420. ja_log("FWD direction",3);
  1421. return ja_is_car_allowed_by_restrictions(s_to.attributes.fwdRestrictions);
  1422. } else {
  1423. ja_log("REV direction",3);
  1424. return ja_is_car_allowed_by_restrictions(s_to.attributes.revRestrictions);
  1425. }
  1426. }
  1427. function ja_is_car_allowed_by_restrictions(restrictions) {
  1428. ja_log("Checking restrictions for cars", 2);
  1429. if(typeof restrictions === 'undefined' || restrictions == null || restrictions.length === 0) {
  1430. ja_log("No car type restrictions to check...", 3);
  1431. return true;
  1432. }
  1433. ja_log(restrictions, 3);
  1434. return !restrictions.some(function(element) {
  1435. /*jshint bitwise: false*/
  1436. ja_log("Checking restriction " + element, 3);
  1437. //noinspection JSBitwiseOperatorUsage
  1438. var ret = element.allDay && //All day restriction
  1439. element.days === 127 && //Every week day
  1440. ( element.vehicleTypes === -1 || //All vehicle types
  1441. element.vehicleTypes & ja_vehicle_types.PRIVATE //or at least private cars
  1442. );
  1443. if (ret) {
  1444. ja_log("There is an all-day-all-week restriction", 3);
  1445. var fromDate = Date.parse(element.fromDate);
  1446. var toDate = Date.parse(element.toDate);
  1447. ja_log("From: " + fromDate + ", to: " + toDate + ". " + ret, 3);
  1448. if(isNaN(fromDate && isNaN(toDate))) {
  1449. ja_log("No start nor end date defined");
  1450. return false;
  1451. }
  1452. var fRes, tRes;
  1453. if(!isNaN(fromDate) && new Date() > fromDate) {
  1454. ja_log("From date is in the past", 3);
  1455. fRes = 2;
  1456. } else if(isNaN(fromDate)) {
  1457. ja_log("From date is invalid/not set", 3);
  1458. fRes = 1;
  1459. } else {
  1460. ja_log("From date is in the future: " + fromDate, 3);
  1461. fRes = 0;
  1462. }
  1463. if(!isNaN(toDate) && new Date() < toDate) {
  1464. ja_log("To date is in the future", 3);
  1465. tRes = 2;
  1466. } else if(isNaN(toDate)) {
  1467. ja_log("To date is invalid/not set", 3);
  1468. tRes = 1;
  1469. } else {
  1470. ja_log("To date is in the past: " + toDate, 3);
  1471. tRes = 0;
  1472. }
  1473. // Car allowed unless
  1474. // - toDate is in the future and fromDate is unset or in the past
  1475. // - fromDate is in the past and toDate is unset in the future
  1476. // Hope I got this right ;)
  1477. return (fRes <= 1 && tRes <= 1);
  1478. }
  1479. return ret;
  1480. });
  1481. }
  1482. /**
  1483. * From wiki:
  1484. * A Cross-match is when the primary name of one segment is identical to the alternate name of an adjacent segment.
  1485. * It had the same priory as a Primary name match. In order for a Cross match to work there must be at least one
  1486. * alt name on both involved segments (even though they don't necessarily match each other). It will work even if
  1487. * the are no Primary names on those segments. It will not work if all three segments at a split have a matching
  1488. * Primary name or a matching Alternate name.
  1489. * @param street_in
  1490. * @param streets
  1491. * @returns {boolean}
  1492. */
  1493. function ja_cross_name_match(street_in, streets) {
  1494. ja_log("CN: init", 2);
  1495. ja_log(street_in, 2);
  1496. ja_log(streets, 2);
  1497. return Object.getOwnPropertyNames(streets).some(function (street_n_id, index) {
  1498. var street_n_element = streets[street_n_id];
  1499. ja_log("CN: Checking element " + index, 2);
  1500. ja_log(street_n_element, 2);
  1501. return (street_in.secondary.some(function (street_in_secondary){
  1502. ja_log("CN2a: checking n.p: " + street_n_element.primary.attributes.name +
  1503. " vs in.s: " + street_in_secondary.attributes.name, 2);
  1504. //wlodek76: CROSS-MATCH works when two compared segments contain at least one ALT NAME
  1505. //when alt name is empty cross-match does not work
  1506. //FZ69617: This no longer seems to be needed
  1507. //if (street_n_element.secondary.length === 0) { return false; }
  1508. return street_n_element.primary.attributes.name === street_in_secondary.attributes.name;
  1509. }) || street_n_element.secondary.some(function (street_n_secondary) {
  1510. ja_log("CN2b: checking in.p: " + street_in.primary.attributes.name + " vs n.s: " + street_n_secondary.attributes.name, 2);
  1511. //wlodek76: CROSS-MATCH works when two compared segments contain at least one ALT NAME
  1512. //when alt name is empty cross-match does not work
  1513. //FZ69617: This no longer seems to be needed
  1514. //if (street_in.secondary.length === 0) { return false; }
  1515. //wlodek76: missing return from checking primary name with alternate names
  1516. return street_in.primary.attributes.name === street_n_secondary.attributes.name;
  1517. }));
  1518. });
  1519. }
  1520. function ja_alt_name_match(street_in, streets) {
  1521. return Object.getOwnPropertyNames(streets).some(function (street_n_id, index) {
  1522. var street_n_element = streets[street_n_id];
  1523. ja_log("AN alt name check: Checking element " + index, 2);
  1524. ja_log(street_n_element, 2);
  1525. if(street_in.secondary.length === 0) { return false; }
  1526. if(street_n_element.secondary.length === 0) { return false; }
  1527. return street_in.secondary.some(function (street_in_secondary, index2) {
  1528. ja_log("AN2 checking element " + index2, 2);
  1529. ja_log(street_in_secondary, 2);
  1530. return street_n_element.secondary.some(function (street_n_secondary_element, index3) {
  1531. ja_log("AN3 Checking in.s: " + street_in_secondary.attributes.name +
  1532. " vs n.s." + index3 + ": " + street_n_secondary_element.attributes.name, 2);
  1533. return street_in_secondary.attributes.name === street_n_secondary_element.attributes.name;
  1534. });
  1535. });
  1536. });
  1537. }
  1538. function ja_primary_name_match(street_in, streets) {
  1539. ja_log("PN", 2);
  1540. ja_log(street_in, 2);
  1541. ja_log(streets, 2);
  1542. return Object.getOwnPropertyNames(streets).some(function (id, index, array) {
  1543. var element = streets[id];
  1544. ja_log("PN Checking element " + index + " of " + array.length, 2);
  1545. ja_log(element, 2);
  1546. return (element.primary.attributes.name === street_in.primary.attributes.name);
  1547. });
  1548. }
  1549. function ja_get_streets(segmentId) {
  1550. var primary =
  1551. window.W.model.streets.objects[window.W.model.segments.objects[segmentId].attributes.primaryStreetID];
  1552. var secondary = [];
  1553. window.W.model.segments.objects[segmentId].attributes.streetIDs.forEach(function (element) {
  1554. secondary.push(window.W.model.streets.objects[element]);
  1555. });
  1556. ja_log(primary, 3);
  1557. ja_log(secondary, 3);
  1558. return { primary: primary, secondary: secondary };
  1559. }
  1560. /**
  1561. * Computes segment's length in meters
  1562. * @param segment Segment to compute the length of
  1563. * @returns {number}
  1564. */
  1565. function ja_segment_length(segment) {
  1566. var len = segment.getOLGeometry().getGeodesicLength(window.W.map.olMap.projection);
  1567. ja_log("segment: " + segment.attributes.id
  1568. + " computed len: " + len + " attrs len: " + segment.attributes.length, 3);
  1569. return len;
  1570. }
  1571. /**
  1572. * Checks whether the two segments (connected at the same node) overlap each other.
  1573. * @param a1 Angle of the 1st segment
  1574. * @param a2 Angle of the 2nd segment
  1575. */
  1576. function ja_overlapping_angles(a1, a2) {
  1577. // If two angles are close < 2 degree they are overlapped.
  1578. // Method of recognizing overlapped segment by server is unknown for me yet, I took this from WME Validator
  1579. // information about this.
  1580. // TODO: verify overlapping check on the side of routing server.
  1581. return Math.abs(ja_angle_diff(a1, a2, true)) < OVERLAPPING_ANGLE;
  1582. }
  1583. /*
  1584. * Misc math and map element functions
  1585. */
  1586. /**
  1587. *
  1588. * @param p0 From point
  1589. * @param p1 Center point
  1590. * @param p2 To point
  1591. * @returns {number}
  1592. */
  1593. function ja_angle_between_points(p0,p1,p2) {
  1594. ja_log("p0 " + p0,3);
  1595. ja_log("p1 " + p1,3);
  1596. ja_log("p2 " + p2,3);
  1597. var a = Math.pow(p1.x-p0.x,2) + Math.pow(p1.y-p0.y,2);
  1598. var b = Math.pow(p1.x-p2.x,2) + Math.pow(p1.y-p2.y,2);
  1599. var c = Math.pow(p2.x-p0.x,2) + Math.pow(p2.y-p0.y,2);
  1600. var angle = Math.acos((a+b-c) / Math.sqrt(4*a*b)) / (Math.PI / 180);
  1601. ja_log("angle is " + angle,3);
  1602. return angle;
  1603. }
  1604. /**
  1605. * get absolute (or turn) angle between 2 inputs.
  1606. * 0,90,true -> 90 0,90,false -> -90
  1607. * 0,170,true -> 170 0,170,false -> -10
  1608. * @param aIn absolute s_in angle (from node)
  1609. * @param aOut absolute s_out angle (from node)
  1610. * @param absolute return absolute or turn angle?
  1611. * @returns {number}
  1612. */
  1613. function ja_angle_diff(aIn, aOut, absolute) {
  1614. var a = parseFloat(aOut) - parseFloat(aIn);
  1615. if(a > 180) { a -= 360; }
  1616. if(a < -180) { a+= 360; }
  1617. return absolute ? a : (a > 0 ? a - 180 : a + 180);
  1618. }
  1619. function ja_angle_dist(a, s_in_angle) {
  1620. ja_log("Computing out-angle " + a + " distance to in-angle " + s_in_angle, 4);
  1621. var diff = ja_angle_diff(a, s_in_angle, true);
  1622. ja_log("Diff is " + diff + ", returning: " + (diff < 0 ? diff + 360 : diff), 4);
  1623. return diff < 0 ? diff + 360 : diff;
  1624. }
  1625. function ja_is_roundabout_normal(junctionID, n_in) {
  1626. ja_log("Check normal roundabout", 3);
  1627. var junction = getByID(window.W.model.junctions,junctionID);
  1628. var nodes = {};
  1629. var numValidExits = 0;
  1630. junction.attributes.segIDs.forEach(function (element, index) {
  1631. var s = getByID(window.W.model.segments,element);
  1632. ja_log("index: " + index, 3);
  1633. //ja_log(s, 3);
  1634. if (!nodes.hasOwnProperty(s.attributes.toNodeID)) {
  1635. ja_log("Adding node id: " + s.attributes.toNodeID, 3);
  1636. //Check if node has allowed exits
  1637. var allowed = false;
  1638. var currNode = getByID(window.W.model.nodes,s.attributes.toNodeID);
  1639. ja_log(currNode, 3);
  1640. currNode.attributes.segIDs.forEach(function (element2) {
  1641. var s_exit = getByID(window.W.model.segments,element2);
  1642. ja_log(s_exit, 3);
  1643. if (s_exit.attributes.junctionID === null) {
  1644. ja_log("Checking: " + s_exit.attributes.id, 3);
  1645. if (currNode.isTurnAllowedBySegDirections(s, s_exit)) {
  1646. //Exit possibly allowed
  1647. ja_log("Exit allowed", 3);
  1648. allowed = true;
  1649. } else {
  1650. ja_log("Exit not allowed", 3);
  1651. }
  1652. } else {
  1653. //part of the junction.. Ignoring
  1654. ja_log(s_exit.attributes.id + " is in the roundabout. ignoring", 3);
  1655. }
  1656. });
  1657. if (allowed) {
  1658. numValidExits++;
  1659. nodes[s.attributes.toNodeID] = getByID(window.W.model.nodes,s.attributes.toNodeID);
  1660. }
  1661. }
  1662. });
  1663. var is_normal = true;
  1664. ja_log(n_in, 3);
  1665. ja_log(junction, 3);
  1666. ja_log(nodes, 3);
  1667. //If we have more than 4 possible exits, the roundabout is non-normal, and we don't want to paint the
  1668. //offending angles.
  1669. if (numValidExits > 4) { return false; }
  1670. for (var n in nodes) {
  1671. if (nodes.hasOwnProperty(n)) {
  1672. ja_log("Checking " + n, 3);
  1673. if (String(n) === String(n_in)) {
  1674. ja_log("Not comparing to n_in ;)", 3);
  1675. } else {
  1676. var angle = ja_angle_between_points(
  1677. getByID(window.W.model.nodes,n_in).getOLGeometry(),
  1678. ja_coordinates_to_point([junction.getOLGeometry().x, junction.getOLGeometry().y]),
  1679. getByID(window.W.model.nodes,n).getOLGeometry()
  1680. );
  1681. ja_log("Angle is: " + angle, 3);
  1682. ja_log("Normalized angle is: " + (angle % 90), 3);
  1683. //angle = Math.abs((angle%90 - 90))
  1684. angle = Math.abs((angle % 90));
  1685. ja_log("Angle is: " + angle, 3);
  1686. // 90 +/- 15 is considered "normal"
  1687. if (angle <= 15 || 90 - angle <= 15) {
  1688. ja_log("turn is normal", 3);
  1689. } else {
  1690. ja_log("turn is NOT normal", 3);
  1691. is_normal = false;
  1692. //Push a marker on the node to show which exit is "not normal"
  1693. ja_mapLayer.addFeatures([
  1694. new window.OpenLayers.Feature.Vector(
  1695. getByID(window.W.model.nodes,n).getOLGeometry(),
  1696. {
  1697. angle: '±' + ja_round(Math.min(angle, 90 - angle)),
  1698. ja_type: ja_routing_type.ROUNDABOUT
  1699. }
  1700. )]
  1701. );
  1702. }
  1703. }
  1704. }
  1705. }
  1706. return is_normal;
  1707. }
  1708. /**
  1709. * Helper to get get correct projections for roundabout center point
  1710. */
  1711. function ja_coordinates_to_point(coordinates) {
  1712. return new window.OpenLayers.Geometry.Point(
  1713. coordinates[0],
  1714. coordinates[1]
  1715. )
  1716. }
  1717. function getOLFeatureGeometryFromSegment(segment) {
  1718. const feature = W.map.segmentLayer.features.find((feat) => feat.attributes.wazeFeature.id === segment.attributes.id);
  1719. return feature.geometry;
  1720. }
  1721. function ja_get_first_point(segment) {
  1722. return getOLFeatureGeometryFromSegment(segment).components[0];
  1723. // return segment.getOLGeometry().components[0];
  1724. }
  1725. function ja_get_last_point(segment) {
  1726. return getOLFeatureGeometryFromSegment(segment).components.at(-1);
  1727. // return segment.getOLGeometry().components[segment.getOLGeometry().components.length - 1];
  1728. }
  1729. function ja_get_second_point(segment) {
  1730. return getOLFeatureGeometryFromSegment(segment).components[1];
  1731. // return segment.getOLGeometry().components[1];
  1732. }
  1733. function ja_get_next_to_last_point(segment) {
  1734. return getOLFeatureGeometryFromSegment(segment).components.at(-2);
  1735. // return segment.getOLGeometry().components[segment.getOLGeometry().components.length - 2];
  1736. }
  1737. //get the absolute angle for a segment end point
  1738. function ja_getAngle(ja_node, ja_segment) {
  1739. ja_log("node: " + ja_node, 2);
  1740. ja_log("segment: " + ja_segment, 2);
  1741. if (ja_node == null || ja_segment == null) { return null; }
  1742. var ja_dx, ja_dy;
  1743. if (ja_segment.attributes.fromNodeID === ja_node) {
  1744. ja_dx = ja_get_second_point(ja_segment).x - ja_get_first_point(ja_segment).x;
  1745. ja_dy = ja_get_second_point(ja_segment).y - ja_get_first_point(ja_segment).y;
  1746. } else {
  1747. ja_dx = ja_get_next_to_last_point(ja_segment).x - ja_get_last_point(ja_segment).x;
  1748. ja_dy = ja_get_next_to_last_point(ja_segment).y - ja_get_last_point(ja_segment).y;
  1749. }
  1750. ja_log(ja_node + " / " + ja_segment + ": dx:" + ja_dx + ", dy:" + ja_dy, 2);
  1751. var ja_angle = Math.atan2(ja_dy, ja_dx);
  1752. return ((ja_angle * 180 / Math.PI)) % 360;
  1753. }
  1754. //get the absolute angle for a midle segment (to prevent a false positive on the curved segment)
  1755. function ja_getAngleMidleSeg(ja_node, ja_segment) {
  1756. ja_log("node: " + ja_node, 2);
  1757. ja_log("segment: " + ja_segment, 2);
  1758. if (ja_node == null || ja_segment == null) { return null; }
  1759. var ja_dx, ja_dy;
  1760. if (ja_segment.attributes.fromNodeID === ja_node) {
  1761. ja_dx = ja_get_last_point(ja_segment).x - ja_get_first_point(ja_segment).x;
  1762. ja_dy = ja_get_last_point(ja_segment).y - ja_get_first_point(ja_segment).y;
  1763. } else {
  1764. ja_dx = ja_get_first_point(ja_segment).x - ja_get_last_point(ja_segment).x;
  1765. ja_dy = ja_get_first_point(ja_segment).y - ja_get_last_point(ja_segment).y;
  1766. }
  1767. ja_log(ja_node + " / " + ja_segment + ": dx:" + ja_dx + ", dy:" + ja_dy, 2);
  1768. var ja_angle = Math.atan2(ja_dy, ja_dx);
  1769. return ((ja_angle * 180 / Math.PI)) % 360;
  1770. }
  1771. /**
  1772. * Decimal adjustment of a number. Borrowed (with some modifications) from
  1773. * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round
  1774. * ja_round(55.55); with 1 decimal // 55.6
  1775. * ja_round(55.549); with 1 decimal // 55.5
  1776. * ja_round(55); with -1 decimals // 60
  1777. * ja_round(54.9); with -1 decimals // 50
  1778. *
  1779. * @param {Number} value The number.
  1780. * @returns {Number} The adjusted value.
  1781. */
  1782. function ja_round(value) {
  1783. var ja_rounding = -parseInt(ja_getOption("decimals"));
  1784. var valueArray;
  1785. if (typeof ja_rounding === 'undefined' || +ja_rounding === 0) {
  1786. return Math.round(value);
  1787. }
  1788. value = +value;
  1789. // If the value is not a number or the exp is not an integer...
  1790. if (isNaN(value) || !(typeof ja_rounding === 'number' && ja_rounding % 1 === 0)) {
  1791. return NaN;
  1792. }
  1793. // Shift
  1794. valueArray = value.toString().split('e');
  1795. value = Math.round(+(valueArray[0] + 'e' + (valueArray[1] ? (+valueArray[1] - ja_rounding) : -ja_rounding)));
  1796. // Shift back
  1797. valueArray = value.toString().split('e');
  1798. return +(valueArray[0] + 'e' + (valueArray[1] ? (+valueArray[1] + ja_rounding) : ja_rounding));
  1799. }
  1800. /*
  1801. * WME interface helper functions
  1802. */
  1803. function ja_getOption(name) {
  1804. ja_log("Loading option: " + name, 2);
  1805. if(!ja_options.hasOwnProperty(name) || typeof ja_options[name] === 'undefined') {
  1806. ja_options[name] = ja_settings[name].defaultValue;
  1807. }
  1808. //Check for invalid values
  1809. //Select values
  1810. if(ja_settings[name].elementType === "select" && ja_settings[name].options.lastIndexOf(ja_options[name]) < 0) {
  1811. ja_log(ja_settings[name].options, 2);
  1812. ja_log("Found invalid value for setting " + name + ": " + ja_options[name] + ". Using default.", 2);
  1813. ja_options[name] = ja_settings[name].defaultValue;
  1814. }
  1815. //Color values
  1816. else if(ja_settings[name].elementType === "color" && String(ja_options[name]).match(/#[0-9a-f]{6}/) == null) {
  1817. ja_log("Found invalid value for setting " + name + ": \"" + ja_options[name] + "\". Using default.", 2);
  1818. ja_options[name] = ja_settings[name].defaultValue;
  1819. }
  1820. //Numeric values
  1821. else if(ja_settings[name].elementType === "number") {
  1822. var minValue = typeof ja_settings[name].min === 'undefined' ? Number.MIN_VALUE : ja_settings[name].min;
  1823. var maxValue = typeof ja_settings[name].max === 'undefined' ? Number.MAX_VALUE : ja_settings[name].max;
  1824. if(isNaN(ja_options[name]) || ja_options[name] < minValue || ja_options[name] > maxValue) {
  1825. ja_log("Found invalid value for setting " + name + ": \"" + ja_options[name] + "\". Using default.", 2);
  1826. ja_options[name] = ja_settings[name].defaultValue;
  1827. }
  1828. }
  1829. //Checkboxes
  1830. else if(ja_settings[name].elementType === "checkbox" && ja_options[name] !== true && ja_options[name] !== false) {
  1831. ja_log("Found invalid value for setting " + name + ": \"" + ja_options[name] + "\". Using default.", 2);
  1832. ja_options[name] = ja_settings[name].defaultValue;
  1833. }
  1834. ja_log("Got value: " + ja_options[name], 2);
  1835. return ja_options[name];
  1836. }
  1837. function ja_setOption(name, val) {
  1838. ja_options[name] = val;
  1839. if(localStorage) {
  1840. localStorage.setItem("wme_ja_options", JSON.stringify(ja_options));
  1841. }
  1842. ja_log(ja_options,3);
  1843. }
  1844. var ja_onchange = function(e) {
  1845. var applyPending = false;
  1846. var settingName = Object.getOwnPropertyNames(ja_settings).filter(function(a){
  1847. ja_log(ja_settings[a], 4);
  1848. return ja_settings[a].elementId === e.id;
  1849. })[0];
  1850. ja_log(e, 3);
  1851. ja_log(settingName, 3);
  1852. switch(ja_settings[settingName].elementType) {
  1853. case "checkbox":
  1854. ja_log("Checkbox setting " + e.id + ": stored value is: " + ja_options[settingName] + ", new value: " + e.checked, 3);
  1855. if (ja_options[settingName] !== e.checked) { applyPending = true; }
  1856. break;
  1857. case "select":
  1858. case "color":
  1859. case "number":
  1860. ja_log("Setting " + e.id + ": stored value is: " + ja_options[settingName] + ", new value: " + e.value, 3);
  1861. if (String(ja_options[settingName]) !== String(e.value)) { applyPending = true; }
  1862. break;
  1863. default:
  1864. ja_log("Unknown setting " + e.id + ": stored value is: " + ja_options[settingName] + ", new value: " + e.value, 3);
  1865. }
  1866. function disable_input(element, disable) {
  1867. element.disabled = disable;
  1868. if (disable) {
  1869. $(element.parentNode).addClass("disabled");
  1870. } else {
  1871. $(element.parentNode).removeClass("disabled");
  1872. }
  1873. }
  1874. //Enable|disable certain dependent settings
  1875. switch(e.id) {
  1876. case ja_settings.override.elementId:
  1877. Object.getOwnPropertyNames(ja_settings).forEach(function (a) {
  1878. var setting = ja_settings[a];
  1879. if(setting.group && setting.group === 'override') {
  1880. ja_log(a + ": " + !e.checked , 3);
  1881. disable_input(document.getElementById(setting.elementId), (!e.checked || e.disabled) );
  1882. }
  1883. });
  1884. break;
  1885. case ja_settings.guess.elementId:
  1886. Object.getOwnPropertyNames(ja_settings).forEach(function (a) {
  1887. var setting = ja_settings[a];
  1888. if(setting.group && (setting.group === 'guess' || setting.group === 'override')) {
  1889. ja_log(a + ": " + !e.checked , 3);
  1890. disable_input(document.getElementById(setting.elementId), !e.checked);
  1891. }
  1892. });
  1893. break;
  1894. case ja_settings.roundaboutOverlayDisplay.elementId:
  1895. Object.getOwnPropertyNames(ja_settings).forEach(function (a) {
  1896. var setting = ja_settings[a];
  1897. if(setting.group && setting.group === 'roundaboutOverlayDisplay') {
  1898. ja_log(a +": " + e.value, 3);
  1899. disable_input(document.getElementById(setting.elementId), e.value === "rOverNever");
  1900. }
  1901. });
  1902. break;
  1903. default:
  1904. ja_log("Nothing to do for " + e.id, 2);
  1905. }
  1906. ja_log("Apply pending configuration changes? " + applyPending, 2);
  1907. if(applyPending) {
  1908. ja_log("Applying new settings now", 3);
  1909. setTimeout(function(){ja_save();}, 500);
  1910. } else {
  1911. ja_log("No new settings to apply", 3);
  1912. }
  1913. };
  1914. var ja_load = function loadJAOptions() {
  1915. ja_log("Should load settings now.", 2);
  1916. if(localStorage != null) {
  1917. ja_log("We have local storage! =)",2);
  1918. try {
  1919. ja_options = JSON.parse(localStorage.getItem("wme_ja_options"));
  1920. } catch (e){
  1921. ja_log("Loading settings failed.. " + e.message, 2);
  1922. ja_options = null;
  1923. }
  1924. }
  1925. if(ja_options == null) {
  1926. ja_reset();
  1927. } else {
  1928. ja_log(ja_options, 2);
  1929. setTimeout(function(){ja_apply();}, 500);
  1930. }
  1931. };
  1932. var ja_save = function saveJAOptions() {
  1933. ja_log("Saving settings", 2);
  1934. Object.getOwnPropertyNames(ja_settings).forEach(function (a) {
  1935. var setting = ja_settings[a];
  1936. ja_log(setting, 2);
  1937. switch (setting.elementType) {
  1938. case "checkbox":
  1939. ja_setOption(a, document.getElementById(setting.elementId).checked);
  1940. break;
  1941. case "color":
  1942. var re = /^#[0-9a-f]{6}$/;
  1943. if(re.test(document.getElementById(setting.elementId).value)) {
  1944. ja_setOption(a, document.getElementById(setting.elementId).value);
  1945. } else {
  1946. ja_setOption(a, ja_settings[a]['default']);
  1947. }
  1948. break;
  1949. case "number":
  1950. var val = parseInt(document.getElementById(setting.elementId).value);
  1951. if(!isNaN(val) && val === parseInt(val) && setting.min <= val && val <= setting.max) {
  1952. ja_setOption(a, document.getElementById(setting.elementId).value);
  1953. } else {
  1954. ja_setOption(a, ja_settings[a]['default']);
  1955. }
  1956. break;
  1957. case "text":
  1958. case "select":
  1959. ja_setOption(a, document.getElementById(setting.elementId).value);
  1960. break;
  1961. default:
  1962. ja_log("Unknown setting type " + setting.elementType, 2);
  1963. }
  1964. });
  1965. ja_apply();
  1966. return false;
  1967. };
  1968. var ja_apply = function applyJAOptions() {
  1969. ja_log("Applying stored (or default) settings", 2);
  1970. if(typeof window.W.map.getLayersBy("uniqueName","junction_angles")[0] === 'undefined') {
  1971. ja_log("WME not ready yet, trying again in 400 ms", 2);
  1972. setTimeout(function(){ja_apply();}, 400);
  1973. return;
  1974. }
  1975. if (document.getElementById("sidepanel-ja") == null) {
  1976. ja_log("WME not ready (no settings tab)", 2);
  1977. } else {
  1978. ja_log(Object.getOwnPropertyNames(ja_settings), 2);
  1979. Object.getOwnPropertyNames(ja_settings).forEach(function (a) {
  1980. var setting = ja_settings[a];
  1981. ja_log(a, 2);
  1982. ja_log(setting, 2);
  1983. ja_log(document.getElementById(setting.elementId), 2);
  1984. switch (setting.elementType) {
  1985. case "checkbox":
  1986. document.getElementById(setting.elementId).checked = ja_getOption(a);
  1987. document.getElementById(setting.elementId).onchange(null);
  1988. break;
  1989. case "color":
  1990. case "number":
  1991. case "text":
  1992. document.getElementById(setting.elementId).value = ja_getOption(a);
  1993. break;
  1994. case "select":
  1995. document.getElementById(setting.elementId).value = ja_getOption(a);
  1996. document.getElementById(setting.elementId).onchange(null);
  1997. break;
  1998. default:
  1999. ja_log("Unknown setting type " + setting.elementType, 2);
  2000. }
  2001. });
  2002. }
  2003. window.W.map.getLayersBy("uniqueName","junction_angles")[0].styleMap = ja_style();
  2004. ja_calculate_real();
  2005. ja_log(ja_options, 2);
  2006. };
  2007. var ja_reset = function resetJAOptions() {
  2008. ja_log("Resetting settings", 2);
  2009. if(localStorage != null) {
  2010. localStorage.removeItem("wme_ja_options");
  2011. }
  2012. ja_options = {};
  2013. ja_apply();
  2014. return false;
  2015. };
  2016. function ja_helpLink(url, text) {
  2017. var elem = document.createElement('li');
  2018. var l = document.createElement('a');
  2019. l.href = url;
  2020. l.target = "_blank";
  2021. l.appendChild(document.createTextNode(ja_getMessage(text)));
  2022. elem.appendChild(l);
  2023. return elem;
  2024. }
  2025. var ja_calculation_timer = {
  2026. start: function() {
  2027. ja_log("Starting timer", 2);
  2028. this.cancel();
  2029. var ja_calculation_timer_self = this;
  2030. this.timeoutID = window.setTimeout(function(){ja_calculation_timer_self.calculate();}, 200);
  2031. },
  2032. calculate: function() {
  2033. ja_calculate_real();
  2034. delete this.timeoutID;
  2035. },
  2036. cancel: function() {
  2037. if(typeof this.timeoutID === "number") {
  2038. window.clearTimeout(this.timeoutID);
  2039. ja_log("Cleared timeout ID : " + this.timeoutID, 2);
  2040. delete this.timeoutID;
  2041. }
  2042. }
  2043. };
  2044. function ja_calculate() {
  2045. ja_calculation_timer.start();
  2046. }
  2047. function ja_get_contrast_color(hex_color) {
  2048. ja_log("Parsing YIQ-based contrast color for: " + hex_color + " ...", 2);
  2049. var r = parseInt(hex_color.substr(1, 2), 16);
  2050. var g = parseInt(hex_color.substr(3, 2), 16);
  2051. var b = parseInt(hex_color.substr(5, 2), 16);
  2052. var yiq = ((r * 299) + (g * 587) + (b * 114)) / 1000;
  2053. return (yiq >= 128) ? 'black' : 'white';
  2054. }
  2055. function ja_get_style_rule(routingType, fillColorOption) {
  2056. return new window.OpenLayers.Rule(
  2057. {
  2058. filter: new window.OpenLayers.Filter.Comparison({
  2059. type: window.OpenLayers.Filter.Comparison.EQUAL_TO,
  2060. property: "ja_type",
  2061. value: routingType
  2062. }),
  2063. symbolizer: {
  2064. pointRadius: 3 + parseInt(ja_getOption("pointSize"), 10) +
  2065. (parseInt(ja_getOption("decimals")) > 0 ? 4 * parseInt(ja_getOption("decimals")) : 0),
  2066. fontSize: (parseInt(ja_getOption("pointSize")) - 1) + "px",
  2067. fillColor: ja_getOption(fillColorOption),
  2068. strokeColor: "#183800",
  2069. fontColor: ja_get_contrast_color(ja_getOption(fillColorOption))
  2070. }
  2071. });
  2072. }
  2073. function ja_get_styleOverride_rule(routingType, fillColorOption) {
  2074. return new window.OpenLayers.Rule(
  2075. {
  2076. filter: new window.OpenLayers.Filter.Comparison({
  2077. type: window.OpenLayers.Filter.Comparison.EQUAL_TO,
  2078. property: "ja_type",
  2079. value: routingType
  2080. }),
  2081. symbolizer: {
  2082. pointRadius: 2+ parseInt(ja_getOption("pointSize"), 10) +
  2083. (parseInt(ja_getOption("decimals")) > 0 ? 4 * parseInt(ja_getOption("decimals")) : 0),
  2084. fontSize: (parseInt(ja_getOption("pointSize")) + (ja_getOption("overrideAngles") ? (-1) : 8)) + "px",
  2085. fillColor: ja_getOption(fillColorOption),
  2086. strokeColor: "#F68F23", //183800
  2087. strokeWidth: 5,
  2088. fontColor: ja_get_contrast_color(ja_getOption(fillColorOption))
  2089. }
  2090. });
  2091. }
  2092. function ja_style() {
  2093. ja_log("Point radius will be: " + (parseInt(ja_getOption("pointSize"), 10)) +
  2094. (parseInt(ja_getOption("decimals") > 0 ? (4 * parseInt(ja_getOption("decimals"))).toString() : "0")), 2);
  2095. return new window.OpenLayers.Style({
  2096. fillColor: "#ffcc88",
  2097. strokeColor: "#ff9966",
  2098. strokeWidth: 2,
  2099. label: "${angle}",
  2100. fontWeight: "bold",
  2101. pointRadius: parseInt(ja_getOption("pointSize"), 10) +
  2102. (parseInt(ja_getOption("decimals")) > 0 ? 4 * parseInt(ja_getOption("decimals")) : 0),
  2103. fontSize: "10px"
  2104. }, {
  2105. rules: [
  2106. new window.OpenLayers.Rule({
  2107. symbolizer: {
  2108. }
  2109. }),
  2110. ja_get_style_rule(ja_routing_type.TURN, "turnInstructionColor"),
  2111. ja_get_style_rule(ja_routing_type.TURN_LEFT, "turnInstructionColor"),
  2112. ja_get_style_rule(ja_routing_type.TURN_RIGHT, "turnInstructionColor"),
  2113. ja_get_style_rule(ja_routing_type.BC, "noInstructionColor"),
  2114. ja_get_style_rule(ja_routing_type.KEEP, "keepInstructionColor"),
  2115. ja_get_style_rule(ja_routing_type.KEEP_LEFT, "keepInstructionColor"),
  2116. ja_get_style_rule(ja_routing_type.KEEP_RIGHT, "keepInstructionColor"),
  2117. ja_get_style_rule(ja_routing_type.EXIT, "exitInstructionColor"),
  2118. ja_get_style_rule(ja_routing_type.EXIT_LEFT, "exitInstructionColor"),
  2119. ja_get_style_rule(ja_routing_type.EXIT_RIGHT, "exitInstructionColor"),
  2120. ja_get_style_rule(ja_routing_type.NO_TURN, "noTurnColor"),
  2121. ja_get_style_rule(ja_routing_type.PROBLEM, "problemColor"),
  2122. ja_get_style_rule(ja_routing_type.ROUNDABOUT, "roundaboutColor"),
  2123. ja_get_style_rule(ja_routing_type.ROUNDABOUT_EXIT, "exitInstructionColor"),
  2124. ja_get_style_rule(ja_routing_type.U_TURN, "uTurnInstructionColor"),
  2125. ja_get_style_rule(ja_routing_type.NO_U_TURN, "problemColor"),
  2126. ja_get_styleOverride_rule(ja_routing_type.OverrideTURN_LEFT, "turnInstructionColor"),
  2127. ja_get_styleOverride_rule(ja_routing_type.OverrideTURN_RIGHT, "turnInstructionColor"),
  2128. ja_get_styleOverride_rule(ja_routing_type.OverrideBC, "noInstructionColor"),
  2129. ja_get_styleOverride_rule(ja_routing_type.OverrideCONTINUE, "continueInstructionColor"),
  2130. ja_get_styleOverride_rule(ja_routing_type.OverrideKEEP_LEFT, "keepInstructionColor"),
  2131. ja_get_styleOverride_rule(ja_routing_type.OverrideKEEP_RIGHT, "keepInstructionColor"),
  2132. ja_get_styleOverride_rule(ja_routing_type.OverrideEXIT, "exitInstructionColor"),
  2133. ja_get_styleOverride_rule(ja_routing_type.OverrideEXIT_LEFT, "exitInstructionColor"),
  2134. ja_get_styleOverride_rule(ja_routing_type.OverrideEXIT_RIGHT, "exitInstructionColor"),
  2135. ja_get_styleOverride_rule(ja_routing_type.OverrideU_TURN, "uTurnInstructionColor"),
  2136. new window.OpenLayers.Rule(
  2137. {
  2138. filter: new window.OpenLayers.Filter.Comparison({
  2139. type: window.OpenLayers.Filter.Comparison.EQUAL_TO,
  2140. property: "ja_type",
  2141. value: "roundaboutOverlay"
  2142. }),
  2143. symbolizer: {
  2144. pointRadius: 3 + parseInt(ja_getOption("pointSize"), 10) +
  2145. (parseInt(ja_getOption("decimals")) > 0 ? 4 * parseInt(ja_getOption("decimals")) : 0),
  2146. fontSize: "12px",
  2147. fillColor: ja_getOption("roundaboutOverlayColor"),
  2148. fillOpacity: 0.1,
  2149. strokeColor: ja_getOption("roundaboutOverlayColor"),
  2150. label: ""
  2151. }
  2152. })
  2153. ]
  2154. });
  2155. }
  2156. /*
  2157. * Translation helpers
  2158. */
  2159. function ja_getMessage(key) {
  2160. var tr = I18n.translate('ja.' + key), no_tr = I18n.missingTranslation('ja.' + key);
  2161. return tr === no_tr ? key : tr;
  2162. }
  2163. function ja_loadTranslations() {
  2164. var set_trans = function(def) {
  2165. /*jshint -W093*/
  2166. return I18n.translations[I18n.locale].ja = def;
  2167. };
  2168. ja_log("Loading translations",2);
  2169. //Apply
  2170. switch (I18n.locale) {
  2171. default:
  2172. //Default language (English)
  2173. set_trans({
  2174. name: "Junction Angle Info",
  2175. settingsTitle: "Junction Angle Info settings",
  2176. resetToDefault: "Reset to default",
  2177. defaultOn: "Show layer by default",
  2178. aAbsolute: "Absolute",
  2179. aDeparture: "Departure",
  2180. angleMode: "Angle mode",
  2181. angleDisplay: "Angle display style",
  2182. angleDisplayArrows: "Direction arrows",
  2183. displayFancy: "Fancy",
  2184. displaySimple: "Simple",
  2185. override: "Check \"override instruction\"",
  2186. overrideAngles: "Show angles of \"override instruction\"",
  2187. guess: "Estimate routing instructions",
  2188. noInstructionColor: "Color for best continuation",
  2189. continueInstructionColor: "Color for continue straight prompt",
  2190. keepInstructionColor: "Color for keep prompt",
  2191. exitInstructionColor: "Color for exit prompt",
  2192. turnInstructionColor: "Color for turn prompt",
  2193. uTurnInstructionColor: "Color for U-turn prompt",
  2194. noTurnColor: "Color for disallowed turns",
  2195. problemColor: "Color for angles to avoid",
  2196. roundaboutColor: "Color for non-normal roundabouts",
  2197. roundaboutOverlayColor: "Color for roundabout overlay",
  2198. roundaboutOverlayDisplay: "Show roundabout",
  2199. rOverNever: "Never",
  2200. rOverSelected: "When selected",
  2201. rOverAlways: "Always",
  2202. decimals: "Number of decimals",
  2203. pointSize: "Base point size",
  2204. roundaboutnav: "WIKI: Roundabouts",
  2205. ghissues: "JAI issue tracker"
  2206. });
  2207. break;
  2208. //Czech (čeština)
  2209. case 'cs':
  2210. set_trans({
  2211. name: "Junction Angle Info",
  2212. settingsTitle: "Nastavení JAI",
  2213. resetToDefault: "Výchozí nastavení",
  2214. defaultOn: "Vždy aktivní",
  2215. aAbsolute: "Absolutní",
  2216. aDeparture: "Odjezdový",
  2217. angleMode: "Styl zobrazení úhlů",
  2218. angleDisplay: "Styl výpisu úhlů",
  2219. angleDisplayArrows: "Směrové šipky",
  2220. displayFancy: "Zdobný",
  2221. displaySimple: "Jednoduchý",
  2222. override: "Zvýraznit vynucené hlasové pokyny",
  2223. overrideAngles: "Zobrazit úhly vynucených pokynů",
  2224. guess: "Odhadovat navigační hlášky",
  2225. noInstructionColor: "Bez hlášení",
  2226. continueInstructionColor: "\"Pokračujte rovně\"",
  2227. keepInstructionColor: "\"Držte se/Zůstaňte\"",
  2228. exitInstructionColor: "\"Sjeďte\"",
  2229. turnInstructionColor: "\"Odbočte\"",
  2230. uTurnInstructionColor: "\"Otočte se\"",
  2231. noTurnColor: "Nepovolené směry",
  2232. problemColor: "Nejasné úhly",
  2233. roundaboutColor: "Rozbité kruháče",
  2234. roundaboutOverlayColor: "Kruháče",
  2235. roundaboutOverlayDisplay: "ukazovat kruháče",
  2236. rOverNever: "Ne-",
  2237. rOverSelected: "Při výběru",
  2238. rOverAlways: "Vždy",
  2239. decimals: "Počet des. míst",
  2240. pointSize: "Velikost písma",
  2241. roundaboutnav: "US WIKI: Kruhové objezdy",
  2242. ghissues: "Hlášení problémů JAI"
  2243. });
  2244. break;
  2245. //Finnish (Suomen kieli)
  2246. case 'fi':
  2247. set_trans({
  2248. name: "Risteyskulmat",
  2249. settingsTitle: "Rysteyskulmien asetukset",
  2250. resetToDefault: "Palauta",
  2251. defaultOn: "Näytä taso oletuksena",
  2252. aAbsolute: "Absoluuttinen",
  2253. aDeparture: "Käännös",
  2254. angleMode: "Kulmien näyttö",
  2255. angleDisplay: "Näyttötyyli",
  2256. angleDisplayArrows: "Suuntanuolet",
  2257. displayFancy: "Nätti",
  2258. displaySimple: "Yksinkertainen",
  2259. override: "Check \"override instruction\"",
  2260. overrideAngles: "Show angles of \"override instruction\"",
  2261. guess: "Arvioi reititysohjeet",
  2262. noInstructionColor: "ohjeeton \"Suora\"-väri",
  2263. continueInstructionColor: "Color for continue straight",
  2264. keepInstructionColor: "\"Pysy vasemmalla/oikealla\"-ohjeen väri",
  2265. exitInstructionColor: "\"Poistu\"-ohjeen väri",
  2266. turnInstructionColor: "\"Käänny\"-ohjeen väri",
  2267. uTurnInstructionColor: "\"Käänny ympäri\"-ohjeen väri",
  2268. noTurnColor: "Kielletyn käännöksen väri",
  2269. problemColor: "Vältettävien kulmien väri",
  2270. roundaboutColor: "Liikenneympyrän (jolla ei-suoria kulmia) ohjeen väri",
  2271. roundaboutOverlayColor: "Liikenneympyrän korostusväri",
  2272. roundaboutOverlayDisplay: "Korosta liikenneympyrä",
  2273. rOverNever: "Ei ikinä",
  2274. rOverSelected: "Kun valittu",
  2275. rOverAlways: "Aina",
  2276. decimals: "Desimaalien määrä",
  2277. pointSize: "Ympyrän peruskoko"
  2278. });
  2279. break;
  2280. //Polish (język polski)
  2281. case 'pl':
  2282. set_trans({
  2283. settingsTitle: "Ustawienia",
  2284. resetToDefault: "Przywróć domyślne",
  2285. defaultOn: "Pokazać warstwę domyślnie",
  2286. aAbsolute: "Absolutne",
  2287. aDeparture: "Rozjazdy",
  2288. angleMode: "Tryb wyświetlania kątów",
  2289. angleDisplay: "Styl kierunków",
  2290. displayFancy: "Dwuliniowy",
  2291. displaySimple: "Prosty",
  2292. angleDisplayArrows: "Strzałki kierunków",
  2293. override: "Check \"override instruction\"",
  2294. overrideAngles: "Show angles of \"override instruction\"",
  2295. guess: "Szacuj komunikaty trasy",
  2296. noInstructionColor: "Kolor najlepszej kontynuacji",
  2297. continueInstructionColor: "Color for continue straight",
  2298. keepInstructionColor: "Kolor dla \"kieruj się\"",
  2299. exitInstructionColor: "Kolor dla \"zjedź\"",
  2300. turnInstructionColor: "Kolor dla \"skręć\"",
  2301. uTurnInstructionColor: "Kolor dla \"zawróć\"",
  2302. noTurnColor: "Kolor niedozwolonych manewrów",
  2303. problemColor: "Kolor problematycznych kątów",
  2304. roundaboutColor: "Kolor rond niestandardowych",
  2305. roundaboutOverlayColor: "Kolor znacznika rond",
  2306. roundaboutOverlayDisplay: "Pokazuj ronda",
  2307. rOverNever: "Nigdy",
  2308. rOverSelected: "Gdy zaznaczone",
  2309. rOverAlways: "Zawsze",
  2310. decimals: "Ilość cyfr po przecinku",
  2311. pointSize: "Rozmiar punktów pomiaru"
  2312. });
  2313. break;
  2314. //Russian (русский)
  2315. case 'ru':
  2316. set_trans({
  2317. name: "Углы поворотов",
  2318. settingsTitle: "Настройки Junction Angle Info",
  2319. resetToDefault: "Сбросить настройки",
  2320. defaultOn: "По умолчанию показывать",
  2321. aAbsolute: "Абсолютные",
  2322. aDeparture: "Повороты",
  2323. angleMode: "- режим углов",
  2324. angleDisplay: "- стиль отображения",
  2325. angleDisplayArrows: "- стрелки направлений",
  2326. displayFancy: "Модный",
  2327. displaySimple: "Простой",
  2328. override: "Визуализировать изменённые подсказки",
  2329. overrideAngles: "Показывать углы изменённых подсказок",
  2330. guess: "Ожидаемые подсказки",
  2331. noInstructionColor: "- нет подсказки",
  2332. continueInstructionColor: "Цвет изменённой подсказки \"продолжайте движение прямо\"",
  2333. keepInstructionColor: "- держитесь",
  2334. exitInstructionColor: "- съезд",
  2335. turnInstructionColor: "- поверните",
  2336. uTurnInstructionColor: "- развернитесь",
  2337. noTurnColor: "- запрещённый манёвр",
  2338. problemColor: "- угол следует избегать",
  2339. roundaboutColor: "- некорректное кольцо",
  2340. roundaboutOverlayColor: "- цвет кольца",
  2341. roundaboutOverlayDisplay: "- показ колец",
  2342. rOverNever: "Никогда",
  2343. rOverSelected: "Если выбрано",
  2344. rOverAlways: "Всегда",
  2345. decimals: "- знаков после запятой",
  2346. pointSize: "- размер кружка",
  2347. roundaboutnav: "Вики: круговые перекрестки",
  2348. ghissues: "Сообщить об ошибке"
  2349. });
  2350. break;
  2351. //Swedish (svenska)
  2352. case 'sv':
  2353. set_trans({
  2354. name: "Korsningsvinklar",
  2355. settingsTitle: "Inställningar för korsningsvinklar",
  2356. resetToDefault: "Återställ",
  2357. defaultOn: "Visa skiktet som standard",
  2358. aAbsolute: "Absolut",
  2359. aDeparture: "Sväng",
  2360. angleMode: "Vinkelvisning",
  2361. angleDisplay: "Vinkelstil",
  2362. angleDisplayArrows: "Riktningspilar",
  2363. displayFancy: "Grafisk",
  2364. displaySimple: "Simpel",
  2365. override: "Check \"override instruction\"",
  2366. overrideAngles: "Show angles of \"override instruction\"",
  2367. guess: "Gissa navigeringsinstruktioner",
  2368. noInstructionColor: "Färg för \"ingen instruktion\"",
  2369. continueInstructionColor: "Color for continue straight",
  2370. keepInstructionColor: "Färg för \"håll höger/vänster\"-instruktion",
  2371. exitInstructionColor: "Färg för \"ta av\"-instruktion",
  2372. turnInstructionColor: "Färg för \"sväng\"-instruktion",
  2373. uTurnInstructionColor: "Färg för \"U-sväng\"-instruktion",
  2374. noTurnColor: "Färg förbjuden sväng",
  2375. problemColor: "Färg för vinklar att undvika",
  2376. roundaboutColor: "Färg för rondell (med icke-räta vinklar)",
  2377. roundaboutOverlayColor: "Färg för rondellcirkel",
  2378. roundaboutOverlayDisplay: "Visa cirkel på rondell",
  2379. rOverNever: "Aldrig",
  2380. rOverSelected: "När vald",
  2381. rOverAlways: "Alltid",
  2382. decimals: "Decimaler",
  2383. pointSize: "Cirkelns basstorlek"
  2384. });
  2385. break;
  2386. //French (Francais)
  2387. case 'fr':
  2388. set_trans({
  2389. name: "Junction Angle Info",
  2390. settingsTitle: "Paramètres de Junction Angle Info",
  2391. defaultOn: "Activer au démarrage",
  2392. angleMode: "Mode Angle",
  2393. aAbsolute: "Absolu",
  2394. aDeparture: "Départ",
  2395. angleDisplay: "Style d'affichage d'Angles",
  2396. angleDisplayArrows: "Flèches de Direction",
  2397. displayFancy: "Fancy",
  2398. displaySimple: "Simple",
  2399. override: "Contrôler les \"overrides instruction\"",
  2400. overrideAngles: "Afficher les angles des \"overrides\" actives",
  2401. guess: "Estimer les instructions routage",
  2402. noInstructionColor: "Couleur sans instruction",
  2403. continueInstructionColor: "Couleur pour \"continuez tout droit\"",
  2404. keepInstructionColor: "Couleur pour serrez",
  2405. exitInstructionColor: "Couleur pour sortez",
  2406. turnInstructionColor: "Couleur pour tournez",
  2407. uTurnInstructionColor: "Couleur pour demi-tour",
  2408. noTurnColor: "Couleur des virages interdits",
  2409. problemColor: "Couleur des angles à éviter",
  2410. roundaboutOverlayDisplay: "Surligner les rond-point",
  2411. rOverNever: "Jamais",
  2412. rOverSelected: "Sélectionné",
  2413. rOverAlways: "Toujours",
  2414. roundaboutOverlayColor: "Couleur de surlignage rond-point",
  2415. roundaboutColor: "Couleur pour les ronds-points anormaux",
  2416. decimals: "Nombre de decimales",
  2417. pointSize: "Taille des bulles",
  2418. resetToDefault: "Réinitialiser par défaut",
  2419. roundaboutnav: "WIKI: Rond-point (en)",
  2420. ghissues: "JAI Reporter un problème"
  2421. });
  2422. break;
  2423. //Latin-American Spanish (español latinoamericano)
  2424. case 'es-419':
  2425. set_trans({
  2426. name: "Información en Ángulos de Intersección (JAI)",
  2427. settingsTitle: "Configuración de Información en Ángulos",
  2428. resetToDefault: "Limpiar configuración",
  2429. defaultOn: "Activo al iniciar",
  2430. aAbsolute: "Absoluto",
  2431. aDeparture: "Salida",
  2432. angleMode: "Modo de ángulos",
  2433. angleDisplay: "Estilo para mostrar",
  2434. angleDisplayArrows: "Flechas de dirección",
  2435. displayFancy: "Lujoso",
  2436. displaySimple: "Simple",
  2437. override: "Revisar \"instrucciones forzadas\"",
  2438. overrideAngles: "Ver ángulos en \"instrucciones forzadas\"",
  2439. guess: "Estimar instrucciones de giro",
  2440. noInstructionColor: "Sin instrucción",
  2441. continueInstructionColor: "\"Sigue derecho\"",
  2442. keepInstructionColor: "\"Mantente\"",
  2443. exitInstructionColor: "\"Sale\"",
  2444. turnInstructionColor: "\"Gira\"",
  2445. uTurnInstructionColor: "\"Gira en U\"",
  2446. noTurnColor: "Giros deshabilitados",
  2447. problemColor: "Ángulos a evitar",
  2448. roundaboutColor: "Rotondas anormales",
  2449. roundaboutOverlayColor: "Rotondas",
  2450. roundaboutOverlayDisplay: "Mostrar rotondas",
  2451. rOverNever: "Nunca",
  2452. rOverSelected: "Seleccionadas",
  2453. rOverAlways: "Siempre",
  2454. decimals: "Decimales",
  2455. pointSize: "Tamaño del texto",
  2456. roundaboutnav: "WIKI: Rotondas",
  2457. ghissues: "Seguimiento de problemas"
  2458. });
  2459. break;
  2460. //Ukrainian (український)
  2461. case 'uk':
  2462. set_trans({
  2463. name: "Junction Angle Info",
  2464. settingsTitle: "Налаштування Junction Angle Info",
  2465. resetToDefault: "Скинути налаштування",
  2466. defaultOn: "За замовчуванням показувати",
  2467. aAbsolute: "Абсолютні",
  2468. aDeparture: "Повороти",
  2469. angleMode: "- режим кутів",
  2470. angleDisplay: "- стиль відображення",
  2471. angleDisplayArrows: "- стрілки напрямків",
  2472. displayFancy: "Модний",
  2473. displaySimple: "Простий",
  2474. override: "Візуалізувати \"змінені підказки\"",
  2475. overrideAngles: "Показувати кути для \"змінених підказок\"",
  2476. guess: "Очікувані підказки:",
  2477. noInstructionColor: "- немає підказки",
  2478. continueInstructionColor: "- продовжуйте рух прямо",
  2479. keepInstructionColor: "- тримайтеся",
  2480. exitInstructionColor: "- з'їзд",
  2481. turnInstructionColor: "- поверніть",
  2482. uTurnInstructionColor: "- розверніться",
  2483. noTurnColor: "- заборонений маневр",
  2484. problemColor: "- кут слід уникати",
  2485. roundaboutColor: "- некоректне кільце",
  2486. roundaboutOverlayColor: "- колір кільця",
  2487. roundaboutOverlayDisplay: "- показ кілець",
  2488. rOverNever: "Ніколи",
  2489. rOverSelected: "Якщо вибрано",
  2490. rOverAlways: "Завжди",
  2491. decimals: "- знаків після коми",
  2492. pointSize: "- розмір шрифту",
  2493. roundaboutnav: "WIKI: кругові перехрестя(en)",
  2494. ghissues: "JAI - Повідомити про помилку"
  2495. });
  2496. break;
  2497. }
  2498. }
  2499. /*
  2500. * Bootstrapping and logging
  2501. */
  2502. function ja_bootstrap(retries) {
  2503. retries = retries || 0;
  2504. //If Waze has not been defined in ~15 seconds, it probably won't work anyway. Might need tuning
  2505. //for really slow devices?
  2506. if (retries >= 30) {
  2507. ja_log("Failed to bootstrap 30 times. Giving up.", 0);
  2508. return;
  2509. }
  2510. try {
  2511. //User logged in and WME ready
  2512. if (
  2513. ja_is_model_ready() &&
  2514. ja_is_dom_ready() &&
  2515. window.W.loginManager.isLoggedIn()) {
  2516. setTimeout(function () {
  2517. junctionangle_init();
  2518. }, 500);
  2519. }
  2520. //Some part of the WME was not yet fully loaded. Retry.
  2521. else {
  2522. setTimeout(function () {
  2523. ja_bootstrap(++retries);
  2524. }, 500);
  2525. }
  2526. } catch (err) {
  2527. ja_log(err, 1);
  2528. setTimeout(function () {
  2529. ja_bootstrap(++retries);
  2530. }, 500);
  2531. }
  2532. }
  2533. function ja_is_model_ready() {
  2534. if(typeof window.W === 'undefined' || typeof window.W.map === 'undefined') {
  2535. return false;
  2536. } else {
  2537. //return 'undefined' !== typeof window.W.map.events.register &&
  2538. return 'undefined' !== typeof window.W.map.olMap.events.register &&
  2539. 'undefined' !== typeof window.W.selectionManager.events.register &&
  2540. 'undefined' !== typeof window.W.loginManager.events.register;
  2541. }
  2542. }
  2543. function ja_is_dom_ready() {
  2544. if(null === document.getElementById('user-info')) {
  2545. return false;
  2546. } else {
  2547. return document.getElementById('user-info').getElementsByClassName('nav-tabs').length > 0 &&
  2548. document.getElementById('user-info').getElementsByClassName('tab-content').length > 0;
  2549. }
  2550. }
  2551. /**
  2552. * Debug logging.
  2553. * @param ja_log_msg
  2554. * @param ja_log_level
  2555. */
  2556. function ja_log(ja_log_msg, ja_log_level) {
  2557. //##NO_FF_START##
  2558. //Firefox addons should not use console.(log|error|debug), so these lines
  2559. //are removed by the FF addon packaging script.
  2560. if(typeof ja_log_level === 'undefined') { ja_log_level = 1; }
  2561. if (ja_log_level <= junctionangle_debug) {
  2562. if (typeof ja_log_msg === "object") {
  2563. console.log(ja_log_msg);
  2564. }
  2565. else {
  2566. console.log("WME Junction Angle: " + ja_log_msg);
  2567. }
  2568. }
  2569. //##NO_FF_END##
  2570. }
  2571. function getByID(obj, id){
  2572. if (typeof(obj.getObjectById) == "function"){
  2573. return obj.getObjectById(id);
  2574. }else if (typeof(obj.getObjectById) == "undefined"){
  2575. return obj.get(id);
  2576. }
  2577. }
  2578. ja_bootstrap();
  2579. }
  2580. //Dynamically create, add and run the script in the real page context. We really do need access to many of the objects...
  2581. let run_ja_script = GM_addElement('script', {
  2582. textContent: "" + run_ja.toString() + " \n" + "run_ja();"
  2583. });