WME Route Checker

Allows editors to check the route between two segments

当前为 2020-01-25 提交的版本,查看 最新版本

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