WME Route Checker

Allows editors to check the route between two segments

当前为 2021-12-07 提交的版本,查看 最新版本

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