WME Simplify Place Geometry

Simplifies geometry of area places in WME

目前为 2015-03-04 提交的版本。查看 最新版本

// ==UserScript==
// @name         WME Simplify Place Geometry
// @description  Simplifies geometry of area places in WME
// @version      1.00
// @author       SAR85
// @copyright	 SAR85
// @license		 CC BY-NC-ND
// @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 = "1.00";
var simplifyChanges = "WME Simplify Area Geometry has been updated to version " 
    + simplifyVersion + ".\n"
    + "*User interface has been changed to draggable window in map that appears when an area place is selected.\n"
    + "*Fixed save error when using clear geometry feature.";
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 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>';
    var css = {
        "display": "none",
        "position": "absolute",
        "top": "120px",
        "left": "73px",
        "padding": "4px",
        "background-color": "rgba(0,0,0,0.8)",
        "border-radius": "5px",
        "border": "none",
        "color": "white",
        "font-size": "0.9em"
    };
	
	/* Initialize simplification library */
	addSimplifyFunc();
	
	/* Add HTML to page and initialize*/
	$('#map').append(content);
    $('#simplifyarea').css(css);
    $('#simpE').val(localStorage.simplifyE || '5');
    $('#simplifylink').click(simplifyFeatureGeometry);
    $('#clearlink').click(clearFeatureGeometry);
    try {$('#simplifyarea').draggable();}
	catch(err) {}
	
	/* Event listeners */
	$('#simplifyhelp').click(function(){
		alert('To use WME Simplify Place Geometry: \n' +
        '1. Select an area place \n' +
        '2. Select an appropriate simplification factor (usually 5-10) \n' +
        '3. Click the link to simplify or clear the geometry'
    );});
	$('#simpE').change(function() {
        localStorage.simplifyE = $('#simpE').val();
    });
	W.selectionManager.events.register("selectionchanged",null,function() {
    if (W.selectionManager.hasSelectedItems()) {
        var selectedItem = W.selectionManager.selectedItems[0].model;
        if (!(selectedItem.geometry instanceof OpenLayers.Geometry.Polygon)) return;
        $('#simplifyarea').fadeIn('fast');
    } else {
        $('#simplifyarea').fadeOut('fast');
    }});
	
	/* 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 = $('#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() {
    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;
    var newGeometry, navAction;
    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));
        navAction = new simpUpdateObject(venue,{entryExitPoints: [newEntryExitPoint]});
        navAction.eachGeometryField = function(e, t) {
            var i, n, s, r, o;
            for (r = e.entryExitPoints, o = [], n = 0, s = r.length; s > n; n++) i = r[n], o.push(t.call(this, "point", i.point, i));
            return o;
        };
        W.model.actionManager.add(navAction);
    }
}

simpBootstrap();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址