WME Simplify Place Geometry

Simplifies geometry of area places in WME

当前为 2015-02-26 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name WME Simplify Place Geometry
  3. // @description Simplifies geometry of area places in WME
  4. // @version 0.9.1
  5. // @author SAR85
  6. // @grant none
  7. // @include https://www.waze.com/editor/*
  8. // @include https://www.waze.com/*/editor/*
  9. // @include https://editor-beta.waze.com/*
  10. // @namespace https://greasyfork.org/users/9321
  11. // ==/UserScript==
  12.  
  13. /* Global vars */
  14. var simplifyVersion = "0.9.1";
  15. var simplifyChanges = "WME Simplify Area Geometry has been updated to version " + simplifyVersion + ". The shortcut has changed to 'shift+j' for simplifying geometry. A feature has been added to clear geometry of areas. The shortcut is 'ctrl+shift+j' to clear geometry.";
  16. var simpUpdateFeatureGeometry = require("Waze/Action/UpdateFeatureGeometry");
  17. var simpUpdateObject = require("Waze/Action/UpdateObject");
  18. function simpBootstrap() {
  19. var bGreasemonkeyServiceDefined = false;
  20. try
  21. {
  22. if ("object" === typeof Components.interfaces.gmIGreasemonkeyService)
  23. {
  24. bGreasemonkeyServiceDefined = true;
  25. }
  26. }
  27. catch (err)
  28. {
  29. // Ignore.
  30. }
  31. if ( "undefined" === typeof unsafeWindow || ! bGreasemonkeyServiceDefined)
  32. {
  33. unsafeWindow = ( function ()
  34. {
  35. var dummyElem = document.createElement('p');
  36. dummyElem.setAttribute ('onclick', 'return window;');
  37. return dummyElem.onclick ();
  38. } ) ();
  39. }
  40. /* begin running the code! */
  41. window.setTimeout(simpInit, 2000);
  42. /*doesn't work in FF: $(document).ready(simpInit); */
  43. }
  44.  
  45. function addSimplifyFunc() {
  46. /*
  47. (c) 2013, Vladimir Agafonkin
  48. Simplify.js, a high-performance JS polyline simplification library
  49. mourner.github.io/simplify-js
  50. */
  51. (function () { 'use strict';
  52.  
  53. // to suit your point format, run search/replace for '.x' and '.y';
  54. // for 3D version, see 3d branch (configurability would draw significant performance overhead)
  55.  
  56. // square distance between 2 points
  57. function getSqDist(p1, p2) {
  58.  
  59. var dx = p1.x - p2.x,
  60. dy = p1.y - p2.y;
  61.  
  62. return dx * dx + dy * dy;
  63. }
  64.  
  65. // square distance from a point to a segment
  66. function getSqSegDist(p, p1, p2) {
  67.  
  68. var x = p1.x,
  69. y = p1.y,
  70. dx = p2.x - x,
  71. dy = p2.y - y;
  72.  
  73. if (dx !== 0 || dy !== 0) {
  74.  
  75. var t = ((p.x - x) * dx + (p.y - y) * dy) / (dx * dx + dy * dy);
  76.  
  77. if (t > 1) {
  78. x = p2.x;
  79. y = p2.y;
  80.  
  81. } else if (t > 0) {
  82. x += dx * t;
  83. y += dy * t;
  84. }
  85. }
  86.  
  87. dx = p.x - x;
  88. dy = p.y - y;
  89.  
  90. return dx * dx + dy * dy;
  91. }
  92. // rest of the code doesn't care about point format
  93.  
  94. // basic distance-based simplification
  95. function simplifyRadialDist(points, sqTolerance) {
  96.  
  97. var prevPoint = points[0],
  98. newPoints = [prevPoint],
  99. point;
  100.  
  101. for (var i = 1, len = points.length; i < len; i++) {
  102. point = points[i];
  103.  
  104. if (getSqDist(point, prevPoint) > sqTolerance) {
  105. newPoints.push(point);
  106. prevPoint = point;
  107. }
  108. }
  109.  
  110. if (prevPoint !== point) newPoints.push(point);
  111.  
  112. return newPoints;
  113. }
  114.  
  115. // simplification using optimized Douglas-Peucker algorithm with recursion elimination
  116. function simplifyDouglasPeucker(points, sqTolerance) {
  117.  
  118. var len = points.length,
  119. MarkerArray = typeof Uint8Array !== 'undefined' ? Uint8Array : Array,
  120. markers = new MarkerArray(len),
  121. first = 0,
  122. last = len - 1,
  123. stack = [],
  124. newPoints = [],
  125. i, maxSqDist, sqDist, index;
  126.  
  127. markers[first] = markers[last] = 1;
  128.  
  129. while (last) {
  130.  
  131. maxSqDist = 0;
  132.  
  133. for (i = first + 1; i < last; i++) {
  134. sqDist = getSqSegDist(points[i], points[first], points[last]);
  135.  
  136. if (sqDist > maxSqDist) {
  137. index = i;
  138. maxSqDist = sqDist;
  139. }
  140. }
  141.  
  142. if (maxSqDist > sqTolerance) {
  143. markers[index] = 1;
  144. stack.push(first, index, index, last);
  145. }
  146.  
  147. last = stack.pop();
  148. first = stack.pop();
  149. }
  150.  
  151. for (i = 0; i < len; i++) {
  152. if (markers[i]) newPoints.push(points[i]);
  153. }
  154.  
  155. return newPoints;
  156. }
  157.  
  158. // both algorithms combined for awesome performance
  159. function simplify(points, tolerance, highestQuality) {
  160.  
  161. if (points.length <= 1) return points;
  162.  
  163. var sqTolerance = tolerance !== undefined ? tolerance * tolerance : 1;
  164.  
  165. points = highestQuality ? points : simplifyRadialDist(points, sqTolerance);
  166. points = simplifyDouglasPeucker(points, sqTolerance);
  167.  
  168. return points;
  169. }
  170.  
  171. // export as AMD module / Node module / browser or worker variable
  172. if (typeof define === 'function' && define.amd) define(function() { return simplify; });
  173. else if (typeof module !== 'undefined') module.exports = simplify;
  174. else if (typeof self !== 'undefined') self.simplify = simplify;
  175. else window.simplify = simplify;
  176.  
  177. })();
  178. }
  179.  
  180. function simpInit() {
  181. /* HTML */
  182. var tab = '<li><a href="#sidepanel-simplifyarea" data-toggle="tab" id="simplifytab">Simplify Area</a></li>';
  183. var content = '<div class="tab-pane" id="sidepanel-simplifyarea"><h1 style="display: block; position:relative; margin-top: -10px; margin-bottom: 10px">Simplify Area Place Geometry</h1><hr><p>Simplification factor:</p><p>0<input type="range" id="simpE" step="1" min="1" max="100" defaultValue="50"></input>100</p><p>Press "shift+j" to simplify or "ctrl-shift-j" to clear the geometry of a selected area place.</p></div>';
  184. /* Initialize simplification library */
  185. addSimplifyFunc();
  186. /* Add HTML to page */
  187. $('h2 + ul.nav-tabs').append(tab);
  188. $('h2 + ul.nav-tabs + .tab-content').append(content);
  189. /* Add functions to page */
  190. self.simplifyFeatureGeometry = simplifyFeatureGeometry;
  191. self.clearFeatureGeometry = clearFeatureGeometry;
  192. /* Shortcut key = shift+j for simplifying */
  193. W.accelerators.addAction('simplifyFeatureGeometry', {group: "editing"});
  194. W.accelerators.events.register('simplifyFeatureGeometry',null,function(){simplifyFeatureGeometry()});
  195. W.accelerators.registerShortcuts({'S+j': "simplifyFeatureGeometry"});
  196. /* Shortcut key = ctrl-shift-j for clearing */
  197. W.accelerators.addAction('clearFeatureGeometry', {group: "editing"});
  198. W.accelerators.events.register('clearFeatureGeometry',null,function(){clearFeatureGeometry()});
  199. W.accelerators.registerShortcuts({'CS+j': "clearFeatureGeometry"});
  200. console.log("WME Simplify Area Geometry Initialized");
  201. /* Update Alert */
  202. if (window.localStorage.simplifyVersion == 'undefined' || window.localStorage.simplifyVersion !== simplifyVersion) {
  203. alert(simplifyChanges);
  204. window.localStorage.simplifyVersion = simplifyVersion;
  205. }
  206. }
  207.  
  208. function simplifyFeatureGeometry(e) {
  209. if (!W.selectionManager.hasSelectedItems() || W.selectionManager.selectedItems[0].model.type !== "venue" || !W.selectionManager.selectedItems[0].model.isGeometryEditable() || !W.selectionManager.selectedItems[0].model.geometry instanceof OpenLayers.Geometry.Polygon) return;
  210. e = e || $('#simpE').val();
  211. var place = W.selectionManager.selectedItems[0];
  212. var oldGeometry = place.geometry.clone();
  213. var newGeometry = oldGeometry.clone();
  214. newGeometry.components[0].components = simplify(oldGeometry.components[0].components, e, false);
  215. if (newGeometry.components[0].components.length < oldGeometry.components[0].components.length) {
  216. W.model.actionManager.add(new simpUpdateFeatureGeometry(place.model,W.model.venues,oldGeometry,newGeometry));
  217. console.log("WME Simplify Area Geometry: " + place.model.attributes.name + " simplified from " + oldGeometry.components[0].components.length + " to " + newGeometry.components[0].components.length + " geo nodes using factor " + e + ".");
  218. } else {
  219. console.log("Geo nodes cannot be simplified from " + oldGeometry.components[0].components.length + " to " + newGeometry.components[0].components.length + ".");
  220. }
  221. }
  222.  
  223. function clearFeatureGeometry() {
  224. var newGeometry;
  225. var venue = W.selectionManager.selectedItems[0].model;
  226. var newEntryExitPoint = {entry: true, exit: true};
  227. var oldGeometry = venue.geometry;
  228.  
  229. if (oldGeometry.components[0].components.length > 4) {
  230. newGeometry = oldGeometry.getBounds().toGeometry();
  231. if (newGeometry.getArea() > 160) newGeometry.resize(0.5,newGeometry.getCentroid());
  232. newEntryExitPoint.point = newGeometry.getCentroid();
  233. W.model.actionManager.add(new simpUpdateFeatureGeometry(venue,W.model.venues,oldGeometry,newGeometry));
  234. W.model.actionManager.add(new simpUpdateObject(venue,{entryExitPoints: [newEntryExitPoint]}));
  235. }
  236. }
  237.  
  238. simpBootstrap();