您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Utility singleton for Waze map editor scripts
此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://updategreasyfork.deno.dev/scripts/15731/98588/WME%20Util%20Singleton.js
// ==UserScript== // @name WME Util Singleton // @namespace http://tampermonkey.net/ // @version 0.203 // @description Utility singleton for Waze map editor scripts // @author slemmon // @match https://www.waze.com/editor/* // @match https://editor-beta.waze.com/editor/* // @grant none // ==/UserScript== /* jshint -W097 */ 'use strict'; /** * ============================================= * USER INFORMATION * ============================================= * * This is a util class to be @required by other scripts. Consider the API a bit fluid at * this stage as this class is new and not very fleshed out. Documentation for each class * method / property will appear just before the method / property. * * The Ext JS framework is loaded and once loaded the W.ux.Util singleton is * created. Next we fire a jQuery event of "extready" into the document body * so that any scripts waiting on Ext JS and the W.ux.Util singleton know they * may proceed. * * Scripts using W.ux.Util should add the following to listen for the 'extready' * event: * $(document.body).on('extready', function () { * // script logic - may still need to check for dom ready and / or * // the OpenLayers map's existence here before proceeding * }); */ $.getScript('https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/ext-all.js', function () { Ext.define('W.ux.Util', { /** * Adds CSS style rule(s) to the page * @param {String/String[]/String...} rule An array of CSS style rule strings or * any number of CSS style rule params */ injectStyleRules(rule) { var styleTag = this.styleTag, args = Array.prototype.slice.call(arguments), div; // allows you to pass in an array, string, or n number of string params rule = args.length === 1 ? args.shift() : args; // if the style tag is found cached then create at the end of the <body> // and cache a reference to it if (!styleTag) { div = $("<div />", { html: '­<style></style>' }).appendTo("body"); // cache a ref to the style tag styleTag = this.styleTag = div.find('style'); } // append to the style tag the style rule / rules passed styleTag.append(Ext.Array.from(rule).join('')); }, /** * Returns the Waze editor map or false if the map is not found * @return {Object/Boolean} The map instance used by Waze or false if not found */ getMap: function () { return (W && W.map) ? W.map : false; }, /** * Simple util to return an array of [a, b] type endpoint sub-arrays given a complete * array of points (vertices) from a ring polygon geometry. For example, if you passed: * [x, y, z] in what would be returned is [[x, y], [y, z], [z, x]]. * @param {Array} points Array of all points in the ring polygon geometry * @return {Array} An array of endpoint arrays from the passed points */ getEndpoints: function (points) { var endpoints = [], len = points.length, pointB; points.forEach(function (point, i) { var pointB = (points[i + 1]) ? points[i + 1] : points[0]; endpoints.push([points[i], pointB]); }); return endpoints; }, // http://jsfiddle.net/justin_c_rounds/Gd2S2/ /** * Finds the points where two lines intersect. That could be intersections where * the lines would literally overlap or could be where the extension of a line would * logically overlap. * @param {Number} line1StartX The starting x coordinate of line 1 * @param {Number} line1StartY The starting y coordinate of line 1 * @param {Number} line1EndX The ending x coordinate of line 1 * @param {Number} line1EndY The ending y coordinate of line 1 * @param {Number} line2StartX The starting x coordinate of line 2 * @param {Number} line2StartY The starting y coordinate of line 2 * @param {Number} line2EndX The ending x coordinate of line 2 * @param {Number} line2EndY The ending y coordinate of line 2 * @return {Object} Returns an object with the following key / value info * * - x {Number} the x coordinate of the intersection or null if there is no intersection * - y {Number} the y coordinate of the intersection or null if there is no intersection * - onLine1 {Boolean} true if the intersection falls on line 1 otherwise false * - onLine2 {Boolean} true if the intersection falls on line 2 otherwise false */ findIntersectionPoint: function (line1StartX, line1StartY, line1EndX, line1EndY, line2StartX, line2StartY, line2EndX, line2EndY) { // if the lines intersect, the result contains the x and y of the intersection // (treating the lines as infinite) and booleans for whether line segment 1 // or line segment 2 contain the point var denominator, a, b, numerator1, numerator2, result = { x: null, y: null, onLine1: false, onLine2: false }; // we'll assume two arrays of two points were passed so we'll split them out if (Ext.isArray(line1StartX)) { line2EndY = line1StartY[1].y; line2EndX = line1StartY[1].x; line2StartY = line1StartY[0].y; line2StartX = line1StartY[0].x; line1EndY = line1StartX[1].y; line1EndX = line1StartX[1].x; line1StartY = line1StartX[0].y; line1StartX = line1StartX[0].x; } denominator = ((line2EndY - line2StartY) * (line1EndX - line1StartX)) - ((line2EndX - line2StartX) * (line1EndY - line1StartY)); if (denominator == 0) { return result; } a = line1StartY - line2StartY; b = line1StartX - line2StartX; numerator1 = ((line2EndX - line2StartX) * a) - ((line2EndY - line2StartY) * b); numerator2 = ((line1EndX - line1StartX) * a) - ((line1EndY - line1StartY) * b); a = numerator1 / denominator; b = numerator2 / denominator; // if we cast these lines infinitely in both directions, they intersect here: result.x = line1StartX + (a * (line1EndX - line1StartX)); result.y = line1StartY + (a * (line1EndY - line1StartY)); /* // it is worth noting that this should be the same as: x = line2StartX + (b * (line2EndX - line2StartX)); y = line2StartX + (b * (line2EndY - line2StartY)); */ // if line1 is a segment and line2 is infinite, they intersect if: if (a > 0 && a < 1) { result.onLine1 = true; } // if line2 is a segment and line1 is infinite, they intersect if: if (b > 0 && b < 1) { result.onLine2 = true; } // if line1 and line2 are segments, they intersect if both of the above are true return result; }, /** * Returns the first two intersecting features from a layer * @param {Object} layer The layer from which to find intersecting features * @return {Array} An array containing the first two intersecting features * found or an empty array if no intersection features were found */ getIntersection: function (layer) { if (!layer) { return false; } var features = layer.features, len = features.length, intersected = [], i = 0, j, feature, featureGeo, candidate, candidateGeo, intersects; for (; i < len; i++) { feature = features[i]; featureGeo = feature.geometry; j = 0; for (; j < len; j++) { candidate = features[j]; candidateGeo = candidate.geometry; intersects = featureGeo.intersects(candidateGeo); if (intersects && (featureGeo !== candidateGeo)) { intersected = [feature, candidate]; break; } } if (intersected.length) { break; } } return intersected; }, /** * Get a new polygon feature from an array of points * @param {Array} points The array of OpenLayers.Geometry.Points used to make the polygon feature * @param {Object} [attribs] Optional object to be mapped to the attributes property of the feature * @param {Object} [style] Optional style object * @return {Object} The polygon feature generaeted from the provided points */ getPolyFromPoints: function (points, attribs, style) { var ring = new OpenLayers.Geometry.LinearRing(points), polygon = new OpenLayers.Geometry.Polygon([ring]), feature = new OpenLayers.Feature.Vector(polygon, attribs, style); return feature; }, /** * @private * Internal method used by the getMergedPolygon method */ applyIntersectionPoints: function (pointsArrA, pointsArrB, altPoly, segmentsArrA, segmentsArrB, copyPoints) { var me = this, intersect, junction; pointsArrA.forEach(function (first, i) { first.isInternal = altPoly.containsPoint(first); delete first.isJunction; var junctions = []; pointsArrB.forEach(function (second, j) { intersect = me.findIntersectionPoint(segmentsArrA[i], segmentsArrB[j]); if (intersect.onLine1 && intersect.onLine2) { junction = new OpenLayers.Geometry.Point(intersect.x, intersect.y); junction.isJunction = true; junctions.unshift(junction); } }); junctions.sort(function (a, b) { var aDist = first.distanceTo(a), bDist = first.distanceTo(b); return aDist > bDist ? -1 : ((bDist > aDist) ? 1 : 0); }); junctions.forEach(function (item) { copyPoints.splice(copyPoints.indexOf(pointsArrA[i]) + 1, 0, item); }); }); }, /** * Return a single polygon feature from the outline of two intersecting * polygons * @param {Object/Object[]} polyA The first polygon or feataure owning the polygon * to be merged. May also be an array of both constituent polygons in which case * the second param if passed will be ignored * @param {Object} polyB The second polygon or feature owning the polygon to * be merged * return {Object} The merged polygon vector feature */ getMergedPolygon: function (polyA, polyB) { if (Ext.isArray(polyA)) { polyB = polyA[1]; polyA = polyA[0]; } // set polyA and B to be the polygon geometries if the parent vector feature was passed in polyA = polyA.CLASS_NAME === "OpenLayers.Feature.Vector" ? polyA.geometry : polyA; polyB = polyB.CLASS_NAME === "OpenLayers.Feature.Vector" ? polyB.geometry : polyB; var me = this, pointsA = polyA.getVertices(), pointsB = polyB.getVertices(), copyPointsA = pointsA.slice(), copyPointsB = pointsB.slice(), segmentsA = me.getEndpoints(pointsA), segmentsB = me.getEndpoints(pointsB), initialX = Infinity, initial, activePoints, hostPoly, activePoint, union, altPoints, activeIndex, next, nextAltIndex, i, altNext, altA, altB, centroidA, candidateAIntersects, centroidB, candidateBIntersects, mids, mid; // Set OpenLayers.geometry.Points in the points / vertices array where the two // polygons intersect me.applyIntersectionPoints(pointsA, pointsB, polyB, segmentsA, segmentsB, copyPointsA); me.applyIntersectionPoints(pointsB, pointsA, polyA, segmentsB, segmentsA, copyPointsB); // function to find the starting point for when we walk through the polygon to get // its outline. The initial point will be the one that's furthest left (function () { var evalPoints = function (points) { points.forEach(function (point) { if (point.x < initialX) { initialX = point.x; initial = point; activePoints = points; hostPoly = (activePoints === copyPointsA) ? polyA : polyB; } }); }; evalPoints(copyPointsA); evalPoints(copyPointsB); })(); // the union array will hold the points of the new polygon perimeter union = [initial]; altPoints = (activePoints === copyPointsA) ? copyPointsB : copyPointsA; while (activePoint !== initial) { activePoint = activePoint || initial; // find the current pointer and the next point from it activeIndex = activePoints.indexOf(activePoint); next = activePoints[activeIndex + 1] || activePoints[0]; // if the next point is not a junction add it to the union array if (!next.isJunction) { if (next !== initial) { union.push(next); } activePoint = next; } else { // the point is a junction / intersect point union.push(next); // add it to the union array nextAltIndex; // find the next perimeter junction for (i = 0; i < altPoints.length; i++) { if (altPoints[i].x === next.x && altPoints[i].y === next.y) { nextAltIndex = i; break; } } // using the next junction find the point ahead and behind it altNext = altPoints[nextAltIndex]; altA = altPoints[nextAltIndex - 1] || altPoints[altPoints.length - 1]; altB = altPoints[nextAltIndex + 1] || altPoints[0]; // see if the point behind it crosses the initial polygon centroidA = new OpenLayers.Geometry.Point((altNext.x + altA.x)/2, (altNext.y + altA.y)/2); candidateAIntersects = hostPoly.containsPoint(centroidA); centroidA.destroy(); // see if the point ahead of it crosses the initial polygon centroidB = new OpenLayers.Geometry.Point((altNext.x + altB.x)/2, (altNext.y + altB.y)/2); candidateBIntersects = hostPoly.containsPoint(centroidB); centroidB.destroy(); // the mids array will be the points from the companion polygon // between the two intersections currently being inspected mids = []; mid = {}; //if one of the lines does not intersect (its posible for there // to be no points between the junctions) if (!candidateAIntersects || !candidateBIntersects) { i = nextAltIndex; // find all the points between the junctions and add them to the // mids array to be then added to the union array while (!mid.isJunction) { if (!candidateAIntersects) { i = (i - 1 > -1) ? i - 1 : altPoints.length - 1; } else { i = (i + 1 < altPoints.length) ? i + 1 : 0; } mid = altPoints[i]; if (mid && !mid.isJunction) { mids.push(mid); } } } // add the points between the junctions from the companion polygon union = union.concat(mids); // if we're at the junction add the junction point corresponding within // the initial polygon if (mid.isJunction) { for (i = 0; i < activePoints.length; i++) { if (activePoints[i].x === mid.x && activePoints[i].y === mid.y) { activePoint = activePoints[i]; union.push(activePoint); break; } } } } // continue the loop until all points from the initial and companion polygon // have been included to create a merged perimeter } return this.getPolyFromPoints(union); } }); // this announces to any listening scripts that Ext and the W.ux.Util // singleton are now ready for use $(document.body).trigger('extready'); });