// ==UserScript==
// @name WME Simplify Place Geometry
// @description Simplifies geometry of area places in WME
// @version 0.9.1
// @author SAR85
// @grant none
// @include https://www.waze.com/editor/*
// @include https://www.waze.com/*/editor/*
// @include https://editor-beta.waze.com/*
// @namespace https://gf.qytechs.cn/users/9321
// ==/UserScript==
/* Global vars */
var simplifyVersion = "0.9.1";
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.";
var simpUpdateFeatureGeometry = require("Waze/Action/UpdateFeatureGeometry");
var simpUpdateObject = require("Waze/Action/UpdateObject");
function simpBootstrap() {
var bGreasemonkeyServiceDefined = false;
try
{
if ("object" === typeof Components.interfaces.gmIGreasemonkeyService)
{
bGreasemonkeyServiceDefined = true;
}
}
catch (err)
{
// Ignore.
}
if ( "undefined" === typeof unsafeWindow || ! bGreasemonkeyServiceDefined)
{
unsafeWindow = ( function ()
{
var dummyElem = document.createElement('p');
dummyElem.setAttribute ('onclick', 'return window;');
return dummyElem.onclick ();
} ) ();
}
/* begin running the code! */
window.setTimeout(simpInit, 2000);
/*doesn't work in FF: $(document).ready(simpInit); */
}
function addSimplifyFunc() {
/*
(c) 2013, Vladimir Agafonkin
Simplify.js, a high-performance JS polyline simplification library
mourner.github.io/simplify-js
*/
(function () { 'use strict';
// to suit your point format, run search/replace for '.x' and '.y';
// for 3D version, see 3d branch (configurability would draw significant performance overhead)
// square distance between 2 points
function getSqDist(p1, p2) {
var dx = p1.x - p2.x,
dy = p1.y - p2.y;
return dx * dx + dy * dy;
}
// square distance from a point to a segment
function getSqSegDist(p, p1, p2) {
var x = p1.x,
y = p1.y,
dx = p2.x - x,
dy = p2.y - y;
if (dx !== 0 || dy !== 0) {
var t = ((p.x - x) * dx + (p.y - y) * dy) / (dx * dx + dy * dy);
if (t > 1) {
x = p2.x;
y = p2.y;
} else if (t > 0) {
x += dx * t;
y += dy * t;
}
}
dx = p.x - x;
dy = p.y - y;
return dx * dx + dy * dy;
}
// rest of the code doesn't care about point format
// basic distance-based simplification
function simplifyRadialDist(points, sqTolerance) {
var prevPoint = points[0],
newPoints = [prevPoint],
point;
for (var i = 1, len = points.length; i < len; i++) {
point = points[i];
if (getSqDist(point, prevPoint) > sqTolerance) {
newPoints.push(point);
prevPoint = point;
}
}
if (prevPoint !== point) newPoints.push(point);
return newPoints;
}
// simplification using optimized Douglas-Peucker algorithm with recursion elimination
function simplifyDouglasPeucker(points, sqTolerance) {
var len = points.length,
MarkerArray = typeof Uint8Array !== 'undefined' ? Uint8Array : Array,
markers = new MarkerArray(len),
first = 0,
last = len - 1,
stack = [],
newPoints = [],
i, maxSqDist, sqDist, index;
markers[first] = markers[last] = 1;
while (last) {
maxSqDist = 0;
for (i = first + 1; i < last; i++) {
sqDist = getSqSegDist(points[i], points[first], points[last]);
if (sqDist > maxSqDist) {
index = i;
maxSqDist = sqDist;
}
}
if (maxSqDist > sqTolerance) {
markers[index] = 1;
stack.push(first, index, index, last);
}
last = stack.pop();
first = stack.pop();
}
for (i = 0; i < len; i++) {
if (markers[i]) newPoints.push(points[i]);
}
return newPoints;
}
// both algorithms combined for awesome performance
function simplify(points, tolerance, highestQuality) {
if (points.length <= 1) return points;
var sqTolerance = tolerance !== undefined ? tolerance * tolerance : 1;
points = highestQuality ? points : simplifyRadialDist(points, sqTolerance);
points = simplifyDouglasPeucker(points, sqTolerance);
return points;
}
// export as AMD module / Node module / browser or worker variable
if (typeof define === 'function' && define.amd) define(function() { return simplify; });
else if (typeof module !== 'undefined') module.exports = simplify;
else if (typeof self !== 'undefined') self.simplify = simplify;
else window.simplify = simplify;
})();
}
function simpInit() {
/* HTML */
var tab = '<li><a href="#sidepanel-simplifyarea" data-toggle="tab" id="simplifytab">Simplify Area</a></li>';
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>';
/* Initialize simplification library */
addSimplifyFunc();
/* Add HTML to page */
$('h2 + ul.nav-tabs').append(tab);
$('h2 + ul.nav-tabs + .tab-content').append(content);
/* Add functions to page */
self.simplifyFeatureGeometry = simplifyFeatureGeometry;
self.clearFeatureGeometry = clearFeatureGeometry;
/* Shortcut key = shift+j for simplifying */
W.accelerators.addAction('simplifyFeatureGeometry', {group: "editing"});
W.accelerators.events.register('simplifyFeatureGeometry',null,function(){simplifyFeatureGeometry()});
W.accelerators.registerShortcuts({'S+j': "simplifyFeatureGeometry"});
/* Shortcut key = ctrl-shift-j for clearing */
W.accelerators.addAction('clearFeatureGeometry', {group: "editing"});
W.accelerators.events.register('clearFeatureGeometry',null,function(){clearFeatureGeometry()});
W.accelerators.registerShortcuts({'CS+j': "clearFeatureGeometry"});
console.log("WME Simplify Area Geometry Initialized");
/* Update Alert */
if (window.localStorage.simplifyVersion == 'undefined' || window.localStorage.simplifyVersion !== simplifyVersion) {
alert(simplifyChanges);
window.localStorage.simplifyVersion = simplifyVersion;
}
}
function simplifyFeatureGeometry(e) {
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;
e = e || $('#simpE').val();
var place = W.selectionManager.selectedItems[0];
var oldGeometry = place.geometry.clone();
var newGeometry = oldGeometry.clone();
newGeometry.components[0].components = simplify(oldGeometry.components[0].components, e, false);
if (newGeometry.components[0].components.length < oldGeometry.components[0].components.length) {
W.model.actionManager.add(new simpUpdateFeatureGeometry(place.model,W.model.venues,oldGeometry,newGeometry));
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 + ".");
} else {
console.log("Geo nodes cannot be simplified from " + oldGeometry.components[0].components.length + " to " + newGeometry.components[0].components.length + ".");
}
}
function clearFeatureGeometry() {
var newGeometry;
var venue = W.selectionManager.selectedItems[0].model;
var newEntryExitPoint = {entry: true, exit: true};
var oldGeometry = venue.geometry;
if (oldGeometry.components[0].components.length > 4) {
newGeometry = oldGeometry.getBounds().toGeometry();
if (newGeometry.getArea() > 160) newGeometry.resize(0.5,newGeometry.getCentroid());
newEntryExitPoint.point = newGeometry.getCentroid();
W.model.actionManager.add(new simpUpdateFeatureGeometry(venue,W.model.venues,oldGeometry,newGeometry));
W.model.actionManager.add(new simpUpdateObject(venue,{entryExitPoints: [newEntryExitPoint]}));
}
}
simpBootstrap();