WME E95

Setup road properties in one click

当前为 2019-05-04 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name WME E95
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.1
  5. // @description Setup road properties in one click
  6. // @author Anton Shevchuk
  7. // @include https://www.waze.com/editor*
  8. // @include https://www.waze.com/*/editor*
  9. // @exclude https://www.waze.com/user/editor*
  10. // @icon data:image/png;base64,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
  11. // @grant none
  12. // ==/UserScript==
  13. /* jshint esversion: 6 */
  14. /* global require */
  15.  
  16. (function($, WazeApi) {
  17. 'use strict';
  18.  
  19. let WazeActionUpdateObject = require("Waze/Action/UpdateObject");
  20. let WazeActionUpdateFeatureAddress = require("Waze/Action/UpdateFeatureAddress");
  21.  
  22. // Road Types
  23. const types = {
  24. street : 1,
  25. primary : 2,
  26. private : 17,
  27. };
  28.  
  29. // Road Flags
  30. // for setup flags use binary operators
  31. // e.g. flags.tunnel | flags.headlights
  32. const flags = {
  33. tunnel : 0b00000001,
  34. // ??? : 0b00000010,
  35. // ??? : 0b00000100,
  36. // ??? : 0b00001000,
  37. unpaved : 0b00010000,
  38. headlights : 0b00100000,
  39. };
  40.  
  41. // Buttons:
  42. // title - for buttons
  43. // keyCode - key for shortcuts (Alt+...)
  44. // detectCity - try to detect city name by closures segments
  45. // clearCity - clear city name
  46. // attributes - native settings for object
  47. const buttons = {
  48. A: {
  49. title: 'PR',
  50. keyCode: 49,
  51. detectCity: true,
  52. attributes: {
  53. fwdMaxSpeed: 20,
  54. revMaxSpeed: 20,
  55. roadType: types.private,
  56. }
  57. },
  58. B: {
  59. title: '50',
  60. keyCode: 50,
  61. detectCity: true,
  62. attributes: {
  63. fwdMaxSpeed: 50,
  64. revMaxSpeed: 50,
  65. roadType: types.street,
  66. }
  67. },
  68. C: {
  69. title: '90',
  70. keyCode: 51,
  71. clearCity: true,
  72. attributes: {
  73. fwdMaxSpeed: 90,
  74. revMaxSpeed: 90,
  75. roadType: types.street,
  76. flags: flags.headlights,
  77. }
  78. }
  79. };
  80.  
  81. // Update segment attributes
  82. function setupRoad(segment, settings) {
  83. let addr = segment.getAddress().attributes;
  84. // Change address
  85. let address = {
  86. countryID: addr.country ? addr.country.id : WazeApi.model.countries.top.id,
  87. stateID: addr.state ? addr.state.id : WazeApi.model.states.top.id,
  88. cityName: addr.city ? addr.city.attributes.name : null,
  89. streetName: addr.street ? addr.street.name : null,
  90. };
  91.  
  92. // Settings: Clear city
  93. if (settings.clearCity) {
  94. address.cityName = null;
  95. }
  96.  
  97. // Settings: Detect city
  98. if (settings.detectCity) {
  99. address.cityName = getCity(segment);
  100. }
  101.  
  102. // Check city
  103. address.emptyCity = (address.cityName === null);
  104.  
  105. // Check street
  106. address.emptyStreet = (address.streetName === null);
  107.  
  108. // Update segment properties
  109. WazeApi.model.actionManager.add(
  110. new WazeActionUpdateObject(
  111. segment,
  112. settings.attributes
  113. )
  114. );
  115. // Update segment address
  116. WazeApi.model.actionManager.add(
  117. new WazeActionUpdateFeatureAddress(
  118. segment,
  119. address,
  120. {
  121. streetIDField: 'primaryStreetID'
  122. }
  123. )
  124. );
  125. }
  126.  
  127. // Update street handler
  128. function processHandler() {
  129. process(this.dataset.e95);
  130. }
  131. function process(index) {
  132. // Get all selected segments
  133. let selected = WazeApi.selectionManager.getSelectedFeatures();
  134. for (let i = 0, total = selected.length; i < total; i++) {
  135. let segment = WazeApi.model.segments.getObjectById(selected[i].model.attributes.id);
  136. if (!segment) continue;
  137. if (!segment.getPermissions()) {
  138. console.log('E95: you don\'t have permissions');
  139. continue;
  140. }
  141. setupRoad(segment, buttons[index]);
  142. }
  143. }
  144.  
  145. // Detect city name by connected segments
  146. function getCity(segment) {
  147. // get cities
  148. // W.model.cities.getValidCities();
  149. // distance to city center
  150. // W.model.cities.getObjectById(644304).getAttributes().geometry.distanceTo(W.model.segments.getObjectById(374688209).getAttributes().geometry);
  151. let cityName = null;
  152. // TODO: replace follow magic with segment.getConnectedSegments() and segment.getConnectedSegmentsByDirection() when it will work
  153. let connected = WazeApi.model.nodes.getObjectById(segment.getAttributes().fromNodeID).getSegmentIds(); // segments from point A
  154. connected = connected.concat(WazeApi.model.nodes.getObjectById(segment.getAttributes().toNodeID).getSegmentIds()); // segments from point B
  155. connected.filter(id => id !== segment.getID());
  156.  
  157. for (let i = 0, total = connected.length; i < total; i++) {
  158. let city = WazeApi.model.segments.getObjectById(connected[i]).getAddress().getCity();
  159. // skip segments with empty cities
  160. if (city && !city.isEmpty()) {
  161. cityName = city.getName();
  162. break;
  163. }
  164. }
  165. console.log('E-95: detected city ' + cityName);
  166. return cityName;
  167. }
  168.  
  169. // Create UI controls everytime
  170. // Uses native JS function for better performance
  171. function createUI() {
  172. // container for buttons
  173. let controls = document.createElement('div');
  174. controls.className = 'controls';
  175. // create all buttons
  176. for (let btn in buttons) {
  177. let button = document.createElement('button');
  178. button.className = 'waze-btn waze-btn-small waze-btn-white road-e95 road-' + btn;
  179. button.style.marginRight = '4px';
  180. button.innerHTML = buttons[btn].title;
  181. button.dataset.e95 = btn;
  182. controls.appendChild(button);
  183. }
  184.  
  185. let label = document.createElement('label');
  186. label.className = 'control-label';
  187. label.innerHTML = 'Quick properties';
  188.  
  189. let group = document.createElement('div');
  190. group.className = 'form-group e95';
  191. group.appendChild(label);
  192. group.appendChild(controls);
  193.  
  194. document.getElementById('segment-edit-general').prepend(group);
  195. }
  196.  
  197. function init() {
  198. // Check for changes in the edit-panel
  199. var speedlimitsObserver = new MutationObserver(function(mutations) {
  200. mutations.forEach(function(mutation) {
  201. for (let i = 0, total = mutation.addedNodes.length; i < total; i++) {
  202. let node = mutation.addedNodes[i];
  203. // Only fire up if it's a node
  204. if (node.nodeType === Node.ELEMENT_NODE &&
  205. node.querySelector('div.selection') &&
  206. !node.querySelector('div.form-group.e95')) {
  207. createUI();
  208. }
  209. }
  210. });
  211. });
  212.  
  213. speedlimitsObserver.observe(document.getElementById('edit-panel'), { childList: true, subtree: true });
  214. console.log('E95: observer was run');
  215.  
  216. // Handler for all buttons
  217. $('#edit-panel').on('click', 'button.road-e95', processHandler);
  218.  
  219. // Handler for button shortcuts
  220. $(document).on('keyup', function(e) {
  221. if (e.altKey && !e.ctrlKey && !e.shiftKey) {
  222. for (let btn in buttons) {
  223. if (buttons[btn].keyCode === e.which) {
  224. process(btn);
  225. break;
  226. }
  227. }
  228. }
  229. });
  230. console.log('E95: handler was initialized');
  231. }
  232.  
  233. // Bootstrap plugin
  234. function bootstrap(tries = 1) {
  235. console.log('E95: attempt ' + tries);
  236. if (WazeApi &&
  237. WazeApi.map &&
  238. WazeApi.model &&
  239. WazeApi.loginManager.user) {
  240. console.log('E95: was initialized');
  241. init();
  242. } else if (tries < 100) {
  243. tries++;
  244. setTimeout(() => bootstrap(tries), 800);
  245. } else {
  246. console.error('E95: initialization failed');
  247. }
  248. }
  249.  
  250. console.log('E95: initialization');
  251. bootstrap();
  252. })(window.jQuery, window.W);