WME Simplify Place Geometry

Simplifies geometry of area places in WME

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

  1. // ==UserScript==
  2. // @name WME Simplify Place Geometry
  3. // @description Simplifies geometry of area places in WME
  4. // @version 1.01
  5. // @author SAR85
  6. // @copyright SAR85
  7. // @license CC BY-NC-ND
  8. // @grant none
  9. // @include https://www.waze.com/editor/*
  10. // @include https://www.waze.com/*/editor/*
  11. // @include https://editor-beta.waze.com/*
  12. // @namespace https://greasyfork.org/users/9321
  13. // ==/UserScript==
  14.  
  15. /* Global vars */
  16. var simplifyVersion = "1.01";
  17. var simplifyChanges = "WME Simplify Area Geometry has been updated to version " +
  18. simplifyVersion + ".\n" +
  19. "*Minor bug fix.";
  20. var simpUpdateFeatureGeometry = require("Waze/Action/UpdateFeatureGeometry");
  21. var simpUpdateObject = require("Waze/Action/UpdateObject");
  22.  
  23. function simpBootstrap() {
  24. var bGreasemonkeyServiceDefined = false;
  25. try {
  26. if ("object" === typeof Components.interfaces.gmIGreasemonkeyService) {
  27. bGreasemonkeyServiceDefined = true;
  28. }
  29. } catch (err) {
  30. // Ignore.
  31. }
  32. if ("undefined" === typeof unsafeWindow || !bGreasemonkeyServiceDefined) {
  33. unsafeWindow = (function () {
  34. var dummyElem = document.createElement('p');
  35. dummyElem.setAttribute('onclick', 'return window;');
  36. return dummyElem.onclick();
  37. })();
  38. }
  39. /* begin running the code! */
  40. window.setTimeout(simpInit, 3000);
  41. /*doesn't work in FF: $(document).ready(simpInit); */
  42. }
  43.  
  44. function addSimplifyFunc() {
  45. /*
  46. (c) 2013, Vladimir Agafonkin
  47. Simplify.js, a high-performance JS polyline simplification library
  48. mourner.github.io/simplify-js
  49. */
  50. (function () {
  51. '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)
  111. newPoints.push(point);
  112.  
  113. return newPoints;
  114. }
  115.  
  116. /* // simplification using optimized Douglas-Peucker algorithm with recursion elimination */
  117. function simplifyDouglasPeucker(points, sqTolerance) {
  118.  
  119. var len = points.length,
  120. MarkerArray = typeof Uint8Array !== 'undefined' ? Uint8Array : Array,
  121. markers = new MarkerArray(len),
  122. first = 0,
  123. last = len - 1,
  124. stack = [],
  125. newPoints = [],
  126. i,
  127. maxSqDist,
  128. sqDist,
  129. index;
  130.  
  131. markers[first] = markers[last] = 1;
  132. while (last) {
  133.  
  134. maxSqDist = 0;
  135.  
  136. for (i = first + 1; i < last; i++) {
  137. sqDist = getSqSegDist(points[i], points[first], points[last]);
  138.  
  139. if (sqDist > maxSqDist) {
  140. index = i;
  141. maxSqDist = sqDist;
  142. }
  143. }
  144.  
  145. if (maxSqDist > sqTolerance) {
  146. markers[index] = 1;
  147. stack.push(first, index, index, last);
  148. }
  149.  
  150. last = stack.pop();
  151. first = stack.pop();
  152. }
  153.  
  154. for (i = 0; i < len; i++) {
  155. if (markers[i])
  156. newPoints.push(points[i]);
  157. }
  158.  
  159. return newPoints;
  160. }
  161.  
  162. /* // both algorithms combined for awesome performance */
  163. function simplify(points, tolerance, highestQuality) {
  164.  
  165. if (points.length <= 1)
  166. return points;
  167.  
  168. var sqTolerance = tolerance !== undefined ? tolerance * tolerance : 1;
  169.  
  170. points = highestQuality ? points : simplifyRadialDist(points, sqTolerance);
  171. points = simplifyDouglasPeucker(points, sqTolerance);
  172.  
  173. return points;
  174. }
  175.  
  176. /* // export as AMD module / Node module / browser or worker variable */
  177. if (typeof define === 'function' && define.amd)
  178. define(function () {
  179. return simplify;
  180. });
  181. else if (typeof module !== 'undefined')
  182. module.exports = simplify;
  183. else if (typeof self !== 'undefined')
  184. self.simplify = simplify;
  185. else
  186. window.simplify = simplify;
  187.  
  188. })();
  189. }
  190.  
  191. function simpInit() {
  192. /* HTML */
  193. var content = '<div id="simplifyarea"><p id="simplifyhelp" style="text-align: center; margin-bottom: 2px; text-decoration: underline; font-weight: bold; cursor: help;">WME Simplify Area Geometry</p><p style="text-align: center; margin: 0px;">Simplification factor: <input type="number" min="1" max="20" id="simpE" style="height: 20px; background-color: rgba(0,0,0,0.8); padding-left: 2px; border: 1px solid white; color: white; width: 50px"></p><p style="color: white;margin: 2px 0 0 0;"><a id="simplifylink" style="cursor:pointer; color: rgb(27,237,30)">Simplify Geometry</a> | <a id="clearlink" style="cursor:pointer; color: red;">Clear Geometry</a></p></div>';
  194. var css = {
  195. "display" : "none",
  196. "position" : "absolute",
  197. "top" : "120px",
  198. "left" : "73px",
  199. "padding" : "4px",
  200. "background-color" : "rgba(0,0,0,0.8)",
  201. "border-radius" : "5px",
  202. "border" : "none",
  203. "color" : "white",
  204. "font-size" : "0.9em"
  205. };
  206.  
  207. /* Initialize simplification library */
  208. addSimplifyFunc();
  209.  
  210. /* Add HTML to page and initialize*/
  211. $('#map').append(content);
  212. $('#simplifyarea').css(css);
  213. $('#simpE').val(localStorage.simplifyE || '5');
  214. $('#simplifylink').click(simplifyFeatureGeometry);
  215. $('#clearlink').click(clearFeatureGeometry);
  216. try {
  217. $('#simplifyarea').draggable();
  218. } catch (err) {}
  219.  
  220. /* Event listeners */
  221. $('#simplifyhelp').click(function () {
  222. alert('To use WME Simplify Place Geometry: \n' +
  223. '1. Select an area place \n' +
  224. '2. Select an appropriate simplification factor (usually 5-10) \n' +
  225. '3. Click the link to simplify or clear the geometry');
  226. });
  227. $('#simpE').change(function () {
  228. localStorage.simplifyE = $('#simpE').val();
  229. });
  230. W.selectionManager.events.register("selectionchanged", null, function () {
  231. if (W.selectionManager.hasSelectedItems()) {
  232. var selectedItem = W.selectionManager.selectedItems[0].model;
  233. if (!(selectedItem.geometry instanceof OpenLayers.Geometry.Polygon))
  234. return;
  235. $('#simplifyarea').fadeIn('fast');
  236. } else {
  237. $('#simplifyarea').fadeOut('fast');
  238. }
  239. });
  240.  
  241. /* Add functions to page */
  242. self.simplifyFeatureGeometry = simplifyFeatureGeometry;
  243. self.clearFeatureGeometry = clearFeatureGeometry;
  244.  
  245. /* Shortcut key = shift+j for simplifying */
  246. W.accelerators.addAction('simplifyFeatureGeometry', {
  247. group : "editing"
  248. });
  249. W.accelerators.events.register('simplifyFeatureGeometry', null, function () {
  250. simplifyFeatureGeometry();
  251. });
  252. W.accelerators.registerShortcuts({
  253. 'S+j' : "simplifyFeatureGeometry"
  254. });
  255.  
  256. /* Shortcut key = ctrl-shift-j for clearing */
  257. W.accelerators.addAction('clearFeatureGeometry', {
  258. group : "editing"
  259. });
  260. W.accelerators.events.register('clearFeatureGeometry', null, function () {
  261. clearFeatureGeometry();
  262. });
  263. W.accelerators.registerShortcuts({
  264. 'CS+j' : "clearFeatureGeometry"
  265. });
  266. console.log("WME Simplify Area Geometry Initialized");
  267.  
  268. /* Update Alert */
  269. if (window.localStorage.simplifyVersion == 'undefined' || window.localStorage.simplifyVersion !== simplifyVersion) {
  270. alert(simplifyChanges);
  271. window.localStorage.simplifyVersion = simplifyVersion;
  272. }
  273. }
  274.  
  275. function simplifyFeatureGeometry(e) {
  276. 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)
  277. return;
  278. e = $('#simpE').val() || 5;
  279. var place = W.selectionManager.selectedItems[0];
  280. var oldGeometry = place.geometry.clone();
  281. var newGeometry = oldGeometry.clone();
  282. newGeometry.components[0].components = simplify(oldGeometry.components[0].components, e, false);
  283. if (newGeometry.components[0].components.length < oldGeometry.components[0].components.length) {
  284. W.model.actionManager.add(new simpUpdateFeatureGeometry(place.model, W.model.venues, oldGeometry, newGeometry));
  285. 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 + ".");
  286. } else {
  287. console.log("Geo nodes cannot be simplified from " + oldGeometry.components[0].components.length + " to " + newGeometry.components[0].components.length + ".");
  288. }
  289. }
  290.  
  291. function clearFeatureGeometry() {
  292. 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)
  293. return;
  294. var newGeometry,
  295. navAction;
  296. var venue = W.selectionManager.selectedItems[0].model;
  297. var newEntryExitPoint = {
  298. entry : true,
  299. exit : true
  300. };
  301. var oldGeometry = venue.geometry;
  302.  
  303. if (oldGeometry.components[0].components.length > 4) {
  304. newGeometry = oldGeometry.getBounds().toGeometry();
  305. if (newGeometry.getArea() > 160)
  306. newGeometry.resize(0.5, newGeometry.getCentroid());
  307. newEntryExitPoint.point = newGeometry.getCentroid();
  308. W.model.actionManager.add(new simpUpdateFeatureGeometry(venue, W.model.venues, oldGeometry, newGeometry));
  309. navAction = new simpUpdateObject(venue, {
  310. entryExitPoints : [newEntryExitPoint]
  311. });
  312. navAction.eachGeometryField = function (e, t) {
  313. var i,
  314. n,
  315. s,
  316. r,
  317. o;
  318. for (r = e.entryExitPoints, o = [], n = 0, s = r.length; s > n; n++)
  319. i = r[n], o.push(t.call(this, "point", i.point, i));
  320. return o;
  321. };
  322. W.model.actionManager.add(navAction);
  323. }
  324. }
  325.  
  326. simpBootstrap();