WME Route Checker

Allows editors to check the route between two segments

当前为 2018-03-05 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name WME Route Checker
  3. // @namespace http://userscripts.org/users/419370
  4. // @description Allows editors to check the route between two segments
  5. // @include https://www.waze.com/*/editor*
  6. // @include https://www.waze.com/editor*
  7. // @include https://beta.waze.com/*
  8. // @exclude https://www.waze.com/*user/*editor/*
  9. // @version 1.25
  10. // @grant none
  11. // ==/UserScript==
  12.  
  13. // globals
  14. var wmerc_version = "1.25";
  15.  
  16. var AVOID_TOLLS = 1;
  17. var AVOID_FREEWAYS = 2;
  18. var AVOID_DIRT = 4;
  19. var ALLOW_UTURNS = 16;
  20. var ROUTE_DIST = 32;
  21. var VEHICLE_TAXI = 64;
  22. var VEHICLE_BIKE = 128;
  23.  
  24. var route_options = ALLOW_UTURNS; // default
  25.  
  26. var routeZIndex = 0;
  27.  
  28. var routeColors = ["#8309e1", "#52BAD9", "#888800" ];
  29.  
  30.  
  31. function showRouteOptions() {
  32. if (W.selectionManager.selectedItems.length != 2) {
  33. WMERC_lineLayer_route.destroyFeatures();
  34. WMERC_lineLayer_route.setZIndex(-999);
  35. return;
  36. }
  37.  
  38. if (getId('routeOptions') !== null) {
  39. return;
  40. }
  41.  
  42. // hook into edit panel on the left
  43. var userTabs = getId('edit-panel');
  44. var segmentBox = getElementsByClassName('segment', userTabs)[0];
  45. var navTabs = getElementsByClassName('nav-tabs', segmentBox)[0];
  46. var routeTab = document.createElement('li');
  47. routeTab.innerHTML = '<a href="#route-checker" data-toggle="tab" id="route-tab-link">Routes</a>';
  48. navTabs.appendChild(routeTab);
  49. getId('route-tab-link').onclick = fetchRoute;
  50. var tabContent = getElementsByClassName('tab-content', segmentBox)[0];
  51.  
  52. var routeContent = document.createElement('div');
  53. routeContent.id = 'route-checker';
  54. routeContent.className = 'tab-pane';
  55. tabContent.appendChild(routeContent);
  56. // add new edit tab to left of the map
  57. var routeOptions = document.createElement('div');
  58. routeOptions.id = "routeOptions";
  59. routeOptions.style.borderTop = "solid 2px #E9E9E9";
  60. routeOptions.style.borderBottom = "solid 2px #E9E9E9";
  61. routeContent.appendChild(routeOptions);
  62. if (location.hostname.match(/editor.*.waze.com/)) {
  63. var coords1 = getCoords(W.selectionManager.selectedItems[0]);
  64. var coords2 = getCoords(W.selectionManager.selectedItems[1]);
  65. var url = getLivemap()
  66. + "&from_lon="+coords1.lon + "&from_lat="+coords1.lat
  67. + "&to_lon="+coords2.lon + "&to_lat="+coords2.lat
  68. + "&at_req=0&at_text=Now";
  69. routeOptions.innerHTML = '<p><b><a href="'+url+'" title="Opens in new tab" target="LiveMap" style="color:#8309e1">Show routes in LiveMap</a> &raquo;</b></p>';
  70. } else {
  71. routeOptions.innerHTML = '<p><b><a href="#" id="goroutes" title="WME Route Checker v'+wmerc_version+'" style="color:#8309e1">'
  72. + 'Show routes between these 2 segments</a> &raquo;</b><br>'
  73. + '<b>Vehicle:</b>'
  74. + ' <input type="radio" name="_vehicleType" id="_vehicleType_private" value="0" checked> Private'
  75. + ' <input type="radio" name="_vehicleType" id="_vehicleType_taxi" value="1"> Taxi'
  76. + ' <input type="radio" name="_vehicleType" id="_vehicleType_bike" value="1"> Motorcycle<br>'
  77. + '<b>Route:</b>'
  78. + ' <input type="radio" name="_routeType" id="_routeType_fast" value="0" checked> Fastest'
  79. + ' <input type="radio" name="_routeType" id="_routeType_short" value="1"> Shortest<br>'
  80. + '<b>Avoid:</b>'
  81. + ' <input type="checkbox" id="_avoidTolls" /> Tolls'
  82. + ' <input type="checkbox" id="_avoidFreeways" /> Freeways'
  83. + ' <input type="checkbox" id="_avoidDirt" /> Dirt Trails<br>'
  84. + '<b>Allow:</b>'
  85. + ' <input type="checkbox" id="_allowUTurns" /> U-Turns</p>';
  86.  
  87. getId('_avoidTolls').checked = route_options & AVOID_TOLLS;
  88. getId('_avoidFreeways').checked = route_options & AVOID_FREEWAYS;
  89. getId('_avoidDirt').checked = route_options & AVOID_DIRT;
  90. getId('_allowUTurns').checked = route_options & ALLOW_UTURNS;
  91. getId('_routeType_short').checked = route_options & ROUTE_DIST;
  92. getId('_vehicleType_taxi').checked = route_options & VEHICLE_TAXI;
  93. getId('_vehicleType_bike').checked = route_options & VEHICLE_BIKE;
  94.  
  95. // automatically start getting route when user clicks on link
  96. getId('goroutes').onclick = fetchRoute;
  97. }
  98.  
  99. // create empty div ready for instructions
  100. var routeTest = document.createElement('div');
  101. routeTest.id = "routeTest";
  102. routeContent.appendChild(routeTest);
  103.  
  104. return;
  105. }
  106.  
  107. function saveOptions() {
  108. route_options = (getId('_avoidTolls').checked ? AVOID_TOLLS : 0)
  109. + (getId('_avoidFreeways').checked ? AVOID_FREEWAYS : 0)
  110. + (getId('_avoidDirt').checked ? AVOID_DIRT : 0)
  111. + (getId('_allowUTurns').checked ? ALLOW_UTURNS : 0)
  112. + (getId('_routeType_short').checked ? ROUTE_DIST : 0)
  113. + (getId('_vehicleType_taxi').checked ? VEHICLE_TAXI : 0)
  114. + (getId('_vehicleType_bike').checked ? VEHICLE_BIKE : 0);
  115.  
  116. console.log("WME Route Checker: saving options: " + route_options);
  117. localStorage.WMERouteChecker = JSON.stringify(route_options);
  118. }
  119.  
  120. function getOptions() {
  121. var list = 'AVOID_TOLL_ROADS' + (route_options & AVOID_TOLLS ? ':t' : ':f') + ','
  122. + 'AVOID_PRIMARIES' + (route_options & AVOID_FREEWAYS ? ':t' : ':f') + ','
  123. + 'AVOID_TRAILS' + (route_options & AVOID_DIRT ? ':t' : ':f') + ','
  124. + 'ALLOW_UTURNS' + (route_options & ALLOW_UTURNS ? ':t' : ':f');
  125. return list;
  126. }
  127.  
  128. function getCoords(segment) {
  129. var numpoints = segment.geometry.components.length;
  130. var middle = Math.floor(numpoints / 2);
  131.  
  132. var seglat, seglon;
  133. if (numpoints % 2 == 1 || numpoints < 2) { // odd number, middle point
  134. seglat = segment.geometry.components[middle].y;
  135. seglon = segment.geometry.components[middle].x;
  136. }
  137. else { // even number - take average of middle two points
  138. seglat = (segment.geometry.components[middle].y
  139. + segment.geometry.components[middle-1].y) / 2.0;
  140. seglon = (segment.geometry.components[middle].x
  141. + segment.geometry.components[middle-1].x) / 2.0;
  142. }
  143. return OL.Layer.SphericalMercator.inverseMercator(seglon,seglat);
  144. }
  145.  
  146. function fetchRoute(reverse) {
  147. saveOptions();
  148.  
  149. var coords1, coords2;
  150. reverse = (reverse !== false);
  151. if (reverse) {
  152. coords1 = getCoords(W.selectionManager.selectedItems[0]);
  153. coords2 = getCoords(W.selectionManager.selectedItems[1]);
  154. } else {
  155. coords1 = getCoords(W.selectionManager.selectedItems[1]);
  156. coords2 = getCoords(W.selectionManager.selectedItems[0]);
  157. }
  158.  
  159. var img = '<img src="https://www.waze.com/images/search_indicator.gif" hspace="4">';
  160.  
  161. // get the route, fix and parse the json
  162. getId('routeTest').innerHTML = "<p><b>Fetching route from LiveMap " + img + "</b></p>";
  163. var url = getRoutingManager();
  164. var data = {
  165. from: "x:" + coords1.lon + " y:" + coords1.lat + " bd:true",
  166. to: "x:" + coords2.lon + " y:" + coords2.lat + " bd:true",
  167. returnJSON: true,
  168. returnGeometries: true,
  169. returnInstructions: true,
  170. type: (route_options & ROUTE_DIST ? 'DISTANCE' : 'HISTORIC_TIME'),
  171. clientVersion: '4.0.0',
  172. timeout: 60000,
  173. nPaths: 3,
  174. options: getOptions()};
  175. if (route_options & VEHICLE_TAXI) {
  176. data.vehicleType = 'TAXI';
  177. }
  178. else if (route_options & VEHICLE_BIKE) {
  179. data.vehicleType = 'MOTORCYCLE';
  180. }
  181. //data.id = "beta";
  182.  
  183. $.ajax({
  184. dataType: "json",
  185. url: url,
  186. data: data,
  187. dataFilter: function(data, dataType) {
  188. return data.replace(/NaN/g, '0');
  189. },
  190. success: function(json) {
  191. showNavigation(json, reverse);
  192. }
  193. });
  194. return false;
  195. }
  196.  
  197. function getLivemap() {
  198. var center_lonlat=new OL.LonLat(W.map.center.lon,W.map.center.lat);
  199. center_lonlat.transform(new OL.Projection ("EPSG:900913"),new OL.Projection("EPSG:4326"));
  200. var coords = '?lon='+center_lonlat.lon+'&lat='+center_lonlat.lat;
  201.  
  202. if (route_options & VEHICLE_TAXI) {
  203. coords += "&rp_vehicleType=TAXI";
  204. }
  205. else if (route_options & VEHICLE_BIKE) {
  206. coords += "&rp_vehicleType=MOTORCYCLE";
  207. }
  208. if (route_options & ROUTE_DIST) {
  209. coords += '&rp_type=DISTANCE';
  210. }
  211. coords += "&rp_options=" + getOptions();
  212.  
  213. return 'https://www.waze.com/livemap'+coords;
  214. }
  215.  
  216. function getRoutingManager() {
  217. if (W.model.countries.get(235) || W.model.countries.get(40)) { // US & Canada
  218. return '/RoutingManager/routingRequest';
  219. } else if (W.model.countries.get(106)) { // Israel
  220. return '/il-RoutingManager/routingRequest';
  221. } else {
  222. return '/row-RoutingManager/routingRequest';
  223. }
  224. }
  225.  
  226. function plotRoute(coords, index) {
  227. var points = [];
  228. for (var i in coords) {
  229. if (i > 0) {
  230. var point = OL.Layer.SphericalMercator.forwardMercator(coords[i].x, coords[i].y);
  231. points.push(new OL.Geometry.Point(point.lon,point.lat));
  232. }
  233. }
  234. var newline = new OL.Geometry.LineString(points);
  235.  
  236. var style = {
  237. strokeColor: routeColors[index],
  238. strokeOpacity: 0.7,
  239. strokeWidth: 8 - index * 2,
  240. };
  241. var lineFeature = new OL.Feature.Vector(newline, {type: "routeArrow"}, style);
  242.  
  243. // Display new segment
  244. WMERC_lineLayer_route.addFeatures([lineFeature]);
  245. }
  246.  
  247. function showNavigation(nav_json, reverse) {
  248. // plot the route
  249. WMERC_lineLayer_route.destroyFeatures();
  250. if (typeof nav_json.alternatives !== "undefined"){
  251. for (var r = nav_json.alternatives.length-1; r >= 0; r--) {
  252. plotRoute(nav_json.alternatives[r].coords, r);
  253. }
  254. } else {
  255. plotRoute(nav_json.coords, 0);
  256. }
  257. WMERC_lineLayer_route.setVisibility(true);
  258. WMERC_lineLayer_route.setZIndex(routeZIndex);
  259.  
  260. // hide segment details
  261. var userTabs = getId('edit-panel');
  262. var segmentBox = getElementsByClassName('segment', userTabs)[0];
  263. var tabContent = getElementsByClassName('tab-content', segmentBox)[0];
  264.  
  265. // write instructions
  266. instructions = getId('routeTest');
  267. instructions.innerHTML = '';
  268. instructions.style.display = 'block';
  269. instructions.style.height = document.getElementById('map').style.height;
  270.  
  271. var nav_coords;
  272. if (typeof nav_json.alternatives !== "undefined") {
  273. for (var r = 0; r < nav_json.alternatives.length; r++) {
  274. showInstructions(nav_json.alternatives[r], r);
  275. }
  276. nav_coords = nav_json.alternatives[0].coords;
  277. } else {
  278. showInstructions(nav_json, 0);
  279. nav_coords = nav_json.coords;
  280. }
  281. var lon1 = nav_coords[0].x;
  282. var lat1 = nav_coords[0].y;
  283.  
  284. var end = nav_coords.length - 1;
  285. var lon2 = nav_coords[end].x;
  286. var lat2 = nav_coords[end].y;
  287.  
  288. var rerouteArgs = '{lon:'+lon1+',lat:'+lat1+'},{lon:'+lon2+',lat:'+lat2+'}';
  289.  
  290. // footer for extra links
  291. var footer = document.createElement('div');
  292. footer.className = 'routes_footer';
  293.  
  294. // create link to reverse the route
  295. var reverseLink = document.createElement('a');
  296. reverseLink.innerHTML = '&#8646; Reverse Route';
  297. reverseLink.href = '#';
  298. reverseLink.setAttribute('onClick', 'fetchRoute('+!reverse+');');
  299. reverseLink.addEventListener('click', function() { fetchRoute(!reverse); }, false);
  300. footer.appendChild(reverseLink);
  301.  
  302. footer.appendChild(document.createTextNode(' | '));
  303.  
  304. var url = getLivemap()
  305. + "&from_lon="+lon1 + "&from_lat="+lat1
  306. + "&to_lon="+lon2 + "&to_lat="+lat2
  307. + "&at_req=0&at_text=Now";
  308.  
  309. // create link to view the navigation instructions
  310. var livemapLink = document.createElement('a');
  311. livemapLink.innerHTML = 'View in LiveMap &raquo;';
  312. livemapLink.href = url;
  313. livemapLink.target="LiveMap";
  314. footer.appendChild(livemapLink);
  315.  
  316. footer.appendChild(document.createElement('br'));
  317.  
  318. // add link to script homepage and version
  319. var scriptLink = document.createElement('a');
  320. scriptLink.innerHTML = 'WME Route Checker v' + wmerc_version;
  321. scriptLink.href = 'https://www.waze.com/forum/viewtopic.php?t=64777';
  322. scriptLink.style.fontStyle = 'italic';
  323. scriptLink.target="_blank";
  324. footer.appendChild(scriptLink);
  325.  
  326. instructions.appendChild(footer);
  327.  
  328. return false;
  329. }
  330.  
  331. function showInstructions(nav_json, r) {
  332. // for each route returned by Waze...
  333. var route = nav_json.response;
  334. var streetNames = route.streetNames;
  335.  
  336. if (r > 0) { // divider
  337. instructions.appendChild(document.createElement('p'));
  338. }
  339.  
  340. // name of the route, with coloured icon
  341. var route_name = document.createElement('p');
  342. route_name.className = 'route';
  343. route_name.style.borderColor = routeColors[r];
  344. route_name.innerHTML = '<b style="color:'+routeColors[r]+'">Via ' + route.routeName + '</b>';
  345. instructions.appendChild(route_name);
  346.  
  347. if (route.tollMeters > 0) {
  348. route_name.innerHTML = '<span style="float: right">TOLL</span>' + route_name.innerHTML;
  349. }
  350.  
  351. var optail = '';
  352. var prevStreet = '';
  353. var currentItem = null;
  354. var totalDist = 0;
  355. var totalTime = 0;
  356. //var detourSaving = 0;
  357. // street name at starting point
  358. var streetName = streetNames[route.results[0].street];
  359. var departFrom = 'depart';
  360. if (!streetName || streetName === null)
  361. streetName = '';
  362. else {
  363. departFrom = 'depart from ' + streetName;
  364. streetName = ' from <span style="color: blue">' + streetName + '<span>';
  365. }
  366. // turn icon at starting coordinates
  367. if (r === 0) {
  368. addTurnImageToMap(nav_json.coords[0], getTurnImage('FORWARD'), departFrom);
  369. }
  370.  
  371. // add first instruction (depart)
  372. currentItem = document.createElement('a');
  373. currentItem.className = 'step';
  374. currentItem.innerHTML = getTurnImageSrc(getTurnImage('FORWARD')) + 'depart' + streetName;
  375. instructions.appendChild(currentItem);
  376.  
  377. var segments = [];
  378. // iterate over all the steps in the list
  379. for (var i = 0; i < route.results.length; i++) {
  380. totalDist += route.results[i].length;
  381. totalTime += route.results[i].crossTime;
  382. //detourSaving += route.results[i].detourSavings;
  383. segments.push(route.results[i].path.segmentId);
  384. if (!route.results[i].instruction)
  385. continue;
  386. var opcode = route.results[i].instruction.opcode;
  387. if (!opcode)
  388. continue;
  389.  
  390. // ignore these
  391. if (opcode.match(/ROUNDABOUT_EXIT|CONTINUE|NONE/)) {
  392. continue;
  393. }
  394.  
  395. // the image for the turn
  396. var dirImage = getTurnImage(opcode);
  397. var dirImageSrc = '';
  398. if (dirImage !== '') {
  399. dirImageSrc = '';
  400. }
  401. // the name that TTS will read out (in blue)
  402. streetName = getNextStreetName(route.results, i, route.streetNames);
  403.  
  404. // roundabouts with nth exit instructions
  405. if (opcode == 'ROUNDABOUT_ENTER') {
  406. opcode += route.results[i].instruction.arg + 'th exit';
  407. opcode = opcode.replace(/1th/, '1st');
  408. opcode = opcode.replace(/2th/, '2nd');
  409. opcode = opcode.replace(/3th/, '3rd');
  410. }
  411.  
  412. // convert opcode to pretty text
  413. opcode = opcode.replace(/APPROACHING_DESTINATION/, 'arrive');
  414. opcode = opcode.replace(/ROUNDABOUT_(EXIT_)?LEFT/, 'at the roundabout, turn left');
  415. opcode = opcode.replace(/ROUNDABOUT_(EXIT_)?RIGHT/, 'at the roundabout, turn right');
  416. opcode = opcode.replace(/ROUNDABOUT_(EXIT_)?STRAIGHT/, 'at the roundabout, continue straight');
  417. opcode = opcode.replace(/ROUNDABOUT_ENTER/, 'at the roundabout, take ');
  418. opcode = opcode.toLowerCase().replace(/_/, ' ');
  419. opcode = opcode.replace(/uturn/, 'make a U-turn');
  420. opcode = opcode.replace(/roundabout u/, 'at the roundabout, make a U-turn');
  421.  
  422. // convert keep to exit if needed
  423. var keepSide = W.model.isLeftHand ? /keep left/ : /keep right/;
  424. if (opcode.match(keepSide) && i+1 < route.results.length &&
  425. isKeepForExit(route.results[i].roadType, route.results[i+1].roadType)) {
  426. opcode = opcode.replace(/keep (.*)/, 'exit $1');
  427. }
  428.  
  429. // show turn symbol on the map (for first route only)
  430. if (r === 0) {
  431. if (opcode == 'arrive') {
  432. var end = nav_json.coords.length - 1;
  433. var title = 'arrive at ' + (streetName !== '' ? streetName : 'destination');
  434. addTurnImageToMap(nav_json.coords[end], dirImage, title);
  435. } else {
  436. //var title = opcode + (streetName != '' ? ' onto ' + streetName : '');
  437. var title = opcode.replace(/at the roundabout, /, '');
  438. if (streetName !== '') title += ' onto ' + streetName;
  439. addTurnImageToMap(route.results[i+1].path, dirImage, title);
  440. }
  441. }
  442.  
  443. // pretty street name
  444. if (streetName !== '') {
  445. if (opcode == 'arrive') {
  446. streetName = ' at <span style="color: blue">' + streetName + '</span>';
  447. } else {
  448. streetName = ' onto <span style="color: blue">' + streetName + '</span>';
  449. }
  450. }
  451. // display new instruction
  452. currentItem = document.createElement('a');
  453. currentItem.className = 'step';
  454. currentItem.innerHTML = getTurnImageSrc(dirImage) + opcode + streetName;
  455. if (opcode.match(/0th exit/)) {
  456. currentItem.style.color = 'red';
  457. }
  458. instructions.appendChild(currentItem);
  459. }
  460.  
  461. // append distance and time to last instruction
  462. currentItem.title = (totalDist/1609).toFixed(3) + " miles";
  463. currentItem.innerHTML += ' - ' + totalDist/1000 + ' km';
  464. currentItem.innerHTML += ' - ' + timeFromSecs(totalTime);
  465. //if (detourSaving > 0) {
  466. // currentItem.innerHTML += '<br>&nbsp; <i>detour saved ' + timeFromSecs(detourSaving) + '</i>';
  467. //}
  468. selectAll = document.createElement('a');
  469. selectAll.className = 'step select';
  470. selectAll.innerHTML = 'Select route segments &#8605;';
  471. selectAll.href = "#";
  472. selectAll.setAttribute('onClick', 'selectSegmentIDs('+segments+');');
  473. selectAll.addEventListener('click', function() { selectSegmentIDs(segments); }, false);
  474. instructions.appendChild(selectAll);
  475. }
  476.  
  477. function selectSegmentIDs(segments) {
  478. var objects = [];
  479. for (var i = 0; i < segments.length; i++) {
  480. objects.push(W.model.segments.objects[segments[i]]);
  481. }
  482. W.selectionManager.select(objects);
  483. return false;
  484. }
  485.  
  486. function getNextStreetName(results, index, streetNames) {
  487. var streetName = '';
  488. var unnamedCount = 0;
  489. var unnamedLength = 0;
  490.  
  491. // destination
  492. if (index == results.length-1) {
  493. streetName = streetNames[results[index].street];
  494. if (!streetName || streetName === null)
  495. streetName = '';
  496. }
  497. // look ahead to next street name
  498. while (++index < results.length && streetName === '') {
  499. streetName = streetNames[results[index].street];
  500. if (!streetName || streetName === null)
  501. streetName = '';
  502.  
  503. // "Navigation instructions for unnamed segments" <- in the Wiki
  504. if (streetName === '' && !isFreewayOrRamp(results[index].roadType)
  505. && !isRoundabout(results[index].path.segmentId)) {
  506. unnamedLength += length;
  507. unnamedCount++;
  508. if (unnamedCount >= 4 || unnamedLength >= 400) {
  509. //console.log("- unnamed segments too long; break");
  510. break;
  511. }
  512. }
  513. }
  514. return streetName;
  515. }
  516.  
  517. function getTurnImage(opcode) {
  518. var dirImage = '';
  519. if (W.model.countries.top.leftHandTraffic) {
  520. opcode = opcode.replace(/(ROUNDABOUT_)(EXIT_)?/, "$1UK_");
  521. }
  522. switch (opcode) {
  523. case "CONTINUE":
  524. case "NONE": dirImage = "big_direction_forwardc0958c4d4c5c79bcb656d34f3afb3ea2"; break;
  525. case "TURN_LEFT": dirImage = "big_direction_left5b94fa33f945d46ab1bdd1131ac0457e"; break;
  526. case "TURN_RIGHT": dirImage = "big_direction_right2d403871f04763260a40c537e231897e"; break;
  527. case "KEEP_LEFT":
  528. case "EXIT_LEFT": dirImage = "big_direction_exit_left1c1498a6dec9582bae81d34ec9e6dc3b"; break;
  529. case "KEEP_RIGHT":
  530. case "EXIT_RIGHT": dirImage = "big_direction_exit_rightba4fee1380f556a8570252c6745f1442"; break;
  531. case "UTURN": dirImage = "big_direction_u63cf785b68a57e8663020098cd07ed76"; break;
  532. case "APPROACHING_DESTINATION": dirImage = "big_direction_end25226c71aed0efd3a2db41978066febc"; break;
  533. case "ROUNDABOUT_LEFT":
  534. case "ROUNDABOUT_EXIT_LEFT": dirImage = "big_directions_roundabout_l54dc48b91e36549b26bae30135462780"; break;
  535. case "ROUNDABOUT_UK_LEFT": dirImage = "big_directions_roundabout_UK_ldc86a0b99cfcd4ed03b0192d5b350c70"; break;
  536. case "ROUNDABOUT_RIGHT":
  537. case "ROUNDABOUT_EXIT_RIGHT": dirImage = "big_directions_roundabout_rc114740b6cafc42177a53aa6c803c14d"; break;
  538. case "ROUNDABOUT_UK_RIGHT": dirImage = "big_directions_roundabout_r_UKc34794c4d01ec8a9fa012150d2f1e02a"; break;
  539. case "ROUNDABOUT_STRAIGHT":
  540. case "ROUNDABOUT_EXIT_STRAIGHT": dirImage = "big_directions_roundabout_sffadf4fd7b277b8ef2f21688e79b9351"; break;
  541. case "ROUNDABOUT_UK_STRAIGHT": dirImage = "big_directions_roundabout_UK_s01ea5c47f4e08b20532505d84b3271e0"; break;
  542. case "ROUNDABOUT_ENTER":
  543. case "ROUNDABOUT_EXIT": dirImage = "big_directions_roundabout9f9bf37022d431be50fecc457cd6e3df"; break;
  544. case "ROUNDABOUT_UK_ENTER":
  545. case "ROUNDABOUT_UK_EXIT": dirImage = "big_directions_roundabout_UK7dce607d7359326a799fd9d3ad8542aa"; break;
  546. case "ROUNDABOUT_U": dirImage = "big_directions_roundabout_u3634283a7d740f30eb18c203f6a357be"; break;
  547. case "ROUNDABOUT_UK_U": dirImage = "big_directions_roundabout_u_UKba204c8a12885976f9bc5b07165b8644"; break;
  548. default: dirImage = '';
  549. }
  550. return dirImage;
  551. }
  552.  
  553. function getTurnImageSrc(dirImage) {
  554. var imgRoot = 'https://editor-assets.waze.com/production/img/';
  555. if (dirImage !== '') {
  556. return '<img src="' + imgRoot + dirImage + '.png" style="float: left; top: 0; padding-right: 4px" width="16" height="16" />';
  557. }
  558. return '';
  559. }
  560.  
  561. function isKeepForExit(fromType, toType) {
  562. // primary to non-primary
  563. if (isPrimaryRoad(fromType) && !isPrimaryRoad(toType)) {
  564. return true;
  565. }
  566. // ramp to non-primary or non-ramp
  567. if (isRamp(fromType) && !isPrimaryRoad(toType) && !isRamp(toType)) {
  568. return true;
  569. }
  570. return false;
  571. }
  572.  
  573. function isFreewayOrRamp(t) {
  574. return t === 3 /*FREEWAY*/ || t === 4 /*RAMP*/;
  575. }
  576.  
  577. function isPrimaryRoad(t) {
  578. return t === 3 /*FREEWAY*/ || t === 6 /*MAJOR_HIGHWAY*/ || t === 7 /*MINOR_HIGHWAY*/;
  579. }
  580.  
  581. function isRamp(t) {
  582. return t === 4 /*RAMP*/;
  583. }
  584.  
  585. function isRoundabout(id) {
  586. segment = W.model.segments.get(id);
  587. if (typeof segment != "undefined") {
  588. return segment.attributes.junctionId !== null;
  589. }
  590. return false;
  591. }
  592.  
  593. function timeFromSecs(seconds)
  594. {
  595. var hh = '00'+Math.floor(((seconds/86400)%1)*24);
  596. var mm = '00'+Math.floor(((seconds/3600)%1)*60);
  597. var ss = '00'+Math.round(((seconds/60)%1)*60);
  598. return hh.slice(-2) + ':' + mm.slice(-2) + ':' + ss.slice(-2);
  599. }
  600.  
  601. function addTurnImageToMap(location, image, title) {
  602. if (image === '') return;
  603. var coords = OL.Layer.SphericalMercator.forwardMercator(location.x, location.y);
  604. var point = new OL.Geometry.Point(coords.lon,coords.lat);
  605. var imgRoot = 'https://editor-assets.waze.com/production/img/';
  606.  
  607. var style = {
  608. externalGraphic: imgRoot + image + ".png",
  609. graphicWidth: 30,
  610. graphicHeight: 32,
  611. graphicZIndex: 9999,
  612. label: title,
  613. labelXOffset: 20,
  614. labelAlign: 'l',
  615. labelOutlineColor: 'white',
  616. labelOutlineWidth: 3,
  617. fontWeight: 'bold',
  618. fontColor: routeColors[0]
  619. };
  620. if (title.match(/0th exit/)) {
  621. style.fontColor = 'red';
  622. }
  623.  
  624. var imageFeature = new OL.Feature.Vector(point, null, style);
  625. // Display new segment
  626. WMERC_lineLayer_route.addFeatures([imageFeature]);
  627. }
  628.  
  629. /* helper function */
  630. function getElementsByClassName(classname, node) {
  631. if(!node) node = document.getElementsByTagName("body")[0];
  632. var a = [];
  633. var re = new RegExp('\\b' + classname + '\\b');
  634. var els = node.getElementsByTagName("*");
  635. for (var i=0,j=els.length; i<j; i++)
  636. if (re.test(els[i].className)) a.push(els[i]);
  637. return a;
  638. }
  639.  
  640. function getId(node) {
  641. return document.getElementById(node);
  642. }
  643.  
  644. function initialiseRouteChecker() {
  645. if (typeof Waze == 'undefined') {
  646. return; // not WME
  647. }
  648.  
  649. console.log("WME Route Checker: initialising v" + wmerc_version);
  650. if (localStorage.WMERouteChecker) {
  651. route_options = JSON.parse(localStorage.WMERouteChecker);
  652. console.log("WME Route Checker: loaded options: " + route_options);
  653. }
  654.  
  655. /* dirty hack to inject stylesheet in to the DOM */
  656. var style = document.createElement('style');
  657. style.innerHTML = "#routeTest {padding: 0 4px 0 0; overflow-y: auto;}\n"
  658. + "#routeTest p.route {margin: 0; padding: 4px 8px; border-bottom: silver solid 3px; background: #eee}\n"
  659. + "#routeTest a.step {display: block; margin: 0; padding: 3px 8px; text-decoration: none; color:black;border-bottom: silver solid 1px;}\n"
  660. + "#routeTest a.step:hover {background: #ffd;}\n"
  661. + "#routeTest a.step:active {background: #dfd;}\n"
  662. + "#routeTest a.select {color: #00f; text-align: right}\n"
  663. + "#routeTest div.routes_footer {text-align: center; margin-bottom: 25px;}\n";
  664. (document.body || document.head || document.documentElement).appendChild(style);
  665.  
  666. // add a new layer for routes
  667. WMERC_lineLayer_route = new OL.Layer.Vector("Route Checker Script",
  668. { rendererOptions: { zIndexing: true },
  669. shortcutKey: "S+t",
  670. uniqueName: 'route_checker' }
  671. );
  672. W.map.addLayer(WMERC_lineLayer_route);
  673. W.map.addControl(new OL.Control.DrawFeature(WMERC_lineLayer_route, OL.Handler.Path));
  674.  
  675. // find best ZIndex
  676. for (var i = 0; i < W.map.layers.length; i++) {
  677. var l = W.map.layers[i];
  678. if (/.Control.SelectHighlightFeature/.test(l.name)) {
  679. routeZIndex = l.getZIndex()+1;
  680. }
  681. }
  682.  
  683. // hack in translation:
  684. I18n.translations[I18n.locale].layers.name.route_checker = "Route Checker Script";
  685.  
  686. // ...and then hide it
  687. $("label:contains('Route Checker Script')").parent().remove();
  688.  
  689. // add listener for whenever selection changes
  690. W.selectionManager.events.register("selectionchanged", null, showRouteOptions);
  691.  
  692. showRouteOptions(); // for permalinks
  693. }
  694.  
  695. // bootstrap!
  696. (function()
  697. {
  698. setTimeout(initialiseRouteChecker, 1003);
  699. })();
  700.  
  701. /* end ======================================================================= */