WME Simplify Place Geometry

Simplifies geometry of area places in WME

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

/* global simplify */
// ==UserScript==
// @name         WME Simplify Place Geometry
// @description  Simplifies geometry of area places in WME
// @version      1.1.3
// @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://greasyfork.org/users/9321
// @require		    https://greasyfork.org/scripts/9794-wlib/code/wLib.js?version=106259
// ==/UserScript==
/* global W */
/* global OpenLayers */

(function () {
    'use strict';
    /* Global vars */
    var DEFAULT_SIMPLIFICATION_FACTOR = 5;
    var simplify;
    var simplifyVersion = '1.1.3';
    var simplifyChanges = 'WME Simplify Area Geometry has been updated to version ' +
        simplifyVersion + '.\n' +
        '[*] Updated for editor compatibility.';
    var UpdateFeatureGeometry = require('Waze/Action/UpdateFeatureGeometry');
    var UpdateObject = require('Waze/Action/UpdateObject');

    function simpBootstrap() {
        if (window.W && window.W.loginManager && window.W.loginManager.events &&
            window.W.loginManager.events.register && window.$ &&
            $('#map').size()) {
            simpInit();
        } else {
            window.setTimeout(function () {
                simpBootstrap();
            }, 1000);
        }
    }

    function initializeSimplifyFunc() {
		/*
		(c) 2013, Vladimir Agafonkin
		Simplify.js, a high-performance JS polyline simplification library
		mourner.github.io/simplify-js
        */
        function getSqDist(p1, p2) {

            var dx = p1.x - p2.x,
                dy = p1.y - p2.y;

            return dx * dx + dy * dy;
        }
        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;
        }
        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;
        }
        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;
        }
        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;
        }
        return 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 */
        simplify = initializeSimplifyFunc();

        /* Add HTML to page and initialize*/
        $('#map').append(content);
        $('#simplifyarea').css(css);
        $('#simpE').val(localStorage.simplifyE ||
            DEFAULT_SIMPLIFICATION_FACTOR);
        $('#simplifylink').click(simplifyFeatureGeometry);
        $('#clearlink').click(clearFeatureGeometry);
        try {
            $('#simplifyarea').draggable();
        } catch (err) { }

        /* Event listeners */
        W.loginManager.events.register('afterloginchanged', null, simpInit);
        $('#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');
                }
            });

        /* Shortcut key = shift+j for simplifying */
        new wLib.Interface.Shortcut('simplifyFeatureGeometry', 'editing', 'S+j', simplifyFeatureGeometry, this).add();

        /* Shortcut key = ctrl+shift+j for clearing */
        new wLib.Interface.Shortcut('clearFeatureGeometry', 'editing', 'CS+j', clearFeatureGeometry, this).add();

        console.log('WME Simplify Area Geometry Initialized');

        /* Update Alert */
        if (typeof window.localStorage.simplifyVersion === 'undefined' || window.localStorage.simplifyVersion !== simplifyVersion) {
            alert(simplifyChanges);
            window.localStorage.simplifyVersion = simplifyVersion;
        }
    }

    function simplifyFeatureGeometry(e) {
        var place = W.selectionManager.selectedItems[0];
        var oldGeometry = place.geometry.clone();
        var newGeometry = oldGeometry.clone();

        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() || DEFAULT_SIMPLIFICATION_FACTOR;

        newGeometry.components[0].components = simplify(oldGeometry.components[0].components, e, false);
        if (newGeometry.components[0].components.length <
            oldGeometry.components[0].components.length &&
            newGeometry.components[0].components.length > 2) {
            W.model.actionManager.add(new UpdateFeatureGeometry(
                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,
            navAction;
        var venue = W.selectionManager.selectedItems[0].model;
        var newEntryExitPoint = {
            entry: true,
            exit: true
        };
        var oldGeometry = venue.geometry;

        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;
        }

        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 UpdateFeatureGeometry(
                venue, W.model.venues, oldGeometry, newGeometry));
            navAction = new UpdateObject(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();
} ());