Created
October 9, 2018 11:34
-
-
Save 2803media/854534e1b71ea6c49ee47f6a7e4e75a9 to your computer and use it in GitHub Desktop.
outTurf.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.turf = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){ | |
| "use strict"; | |
| Object.defineProperty(exports, "__esModule", { value: true }); | |
| var meta_1 = require("@turf/meta"); | |
| // Note: change RADIUS => earthRadius | |
| var RADIUS = 6378137; | |
| /** | |
| * Takes one or more features and returns their area in square meters. | |
| * | |
| * @name area | |
| * @param {GeoJSON} geojson input GeoJSON feature(s) | |
| * @returns {number} area in square meters | |
| * @example | |
| * var polygon = turf.polygon([[[125, -15], [113, -22], [154, -27], [144, -15], [125, -15]]]); | |
| * | |
| * var area = turf.area(polygon); | |
| * | |
| * //addToMap | |
| * var addToMap = [polygon] | |
| * polygon.properties.area = area | |
| */ | |
| function area(geojson) { | |
| return meta_1.geomReduce(geojson, function (value, geom) { | |
| return value + calculateArea(geom); | |
| }, 0); | |
| } | |
| exports.default = area; | |
| /** | |
| * Calculate Area | |
| * | |
| * @private | |
| * @param {Geometry} geom GeoJSON Geometries | |
| * @returns {number} area | |
| */ | |
| function calculateArea(geom) { | |
| var total = 0; | |
| var i; | |
| switch (geom.type) { | |
| case "Polygon": | |
| return polygonArea(geom.coordinates); | |
| case "MultiPolygon": | |
| for (i = 0; i < geom.coordinates.length; i++) { | |
| total += polygonArea(geom.coordinates[i]); | |
| } | |
| return total; | |
| case "Point": | |
| case "MultiPoint": | |
| case "LineString": | |
| case "MultiLineString": | |
| return 0; | |
| } | |
| return 0; | |
| } | |
| function polygonArea(coords) { | |
| var total = 0; | |
| if (coords && coords.length > 0) { | |
| total += Math.abs(ringArea(coords[0])); | |
| for (var i = 1; i < coords.length; i++) { | |
| total -= Math.abs(ringArea(coords[i])); | |
| } | |
| } | |
| return total; | |
| } | |
| /** | |
| * @private | |
| * Calculate the approximate area of the polygon were it projected onto the earth. | |
| * Note that this area will be positive if ring is oriented clockwise, otherwise it will be negative. | |
| * | |
| * Reference: | |
| * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for Polygons on a Sphere", | |
| * JPL Publication 07-03, Jet Propulsion | |
| * Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409 | |
| * | |
| * @param {Array<Array<number>>} coords Ring Coordinates | |
| * @returns {number} The approximate signed geodesic area of the polygon in square meters. | |
| */ | |
| function ringArea(coords) { | |
| var p1; | |
| var p2; | |
| var p3; | |
| var lowerIndex; | |
| var middleIndex; | |
| var upperIndex; | |
| var i; | |
| var total = 0; | |
| var coordsLength = coords.length; | |
| if (coordsLength > 2) { | |
| for (i = 0; i < coordsLength; i++) { | |
| if (i === coordsLength - 2) { | |
| lowerIndex = coordsLength - 2; | |
| middleIndex = coordsLength - 1; | |
| upperIndex = 0; | |
| } | |
| else if (i === coordsLength - 1) { | |
| lowerIndex = coordsLength - 1; | |
| middleIndex = 0; | |
| upperIndex = 1; | |
| } | |
| else { | |
| lowerIndex = i; | |
| middleIndex = i + 1; | |
| upperIndex = i + 2; | |
| } | |
| p1 = coords[lowerIndex]; | |
| p2 = coords[middleIndex]; | |
| p3 = coords[upperIndex]; | |
| total += (rad(p3[0]) - rad(p1[0])) * Math.sin(rad(p2[1])); | |
| } | |
| total = total * RADIUS * RADIUS / 2; | |
| } | |
| return total; | |
| } | |
| function rad(num) { | |
| return num * Math.PI / 180; | |
| } | |
| },{"@turf/meta":5}],2:[function(require,module,exports){ | |
| 'use strict'; | |
| function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } | |
| var martinez = require('martinez-polygon-clipping'); | |
| var area = _interopDefault(require('@turf/area')); | |
| var helpers = require('@turf/helpers'); | |
| var invariant = require('@turf/invariant'); | |
| var meta = require('@turf/meta'); | |
| /** | |
| * Finds the difference between two {@link Polygon|polygons} by clipping the second polygon from the first. | |
| * | |
| * @name difference | |
| * @param {Feature<Polygon|MultiPolygon>} polygon1 input Polygon feature | |
| * @param {Feature<Polygon|MultiPolygon>} polygon2 Polygon feature to difference from polygon1 | |
| * @returns {Feature<Polygon|MultiPolygon>|null} a Polygon or MultiPolygon feature showing the area of `polygon1` excluding the area of `polygon2` (if empty returns `null`) | |
| * @example | |
| * var polygon1 = turf.polygon([[ | |
| * [128, -26], | |
| * [141, -26], | |
| * [141, -21], | |
| * [128, -21], | |
| * [128, -26] | |
| * ]], { | |
| * "fill": "#F00", | |
| * "fill-opacity": 0.1 | |
| * }); | |
| * var polygon2 = turf.polygon([[ | |
| * [126, -28], | |
| * [140, -28], | |
| * [140, -20], | |
| * [126, -20], | |
| * [126, -28] | |
| * ]], { | |
| * "fill": "#00F", | |
| * "fill-opacity": 0.1 | |
| * }); | |
| * | |
| * var difference = turf.difference(polygon1, polygon2); | |
| * | |
| * //addToMap | |
| * var addToMap = [polygon1, polygon2, difference]; | |
| */ | |
| function difference(polygon1, polygon2) { | |
| var geom1 = invariant.getGeom(polygon1); | |
| var geom2 = invariant.getGeom(polygon2); | |
| var properties = polygon1.properties || {}; | |
| // Issue #721 - JSTS/Martinez can't handle empty polygons | |
| geom1 = removeEmptyPolygon(geom1); | |
| geom2 = removeEmptyPolygon(geom2); | |
| if (!geom1) return null; | |
| if (!geom2) return helpers.feature(geom1, properties); | |
| var differenced = martinez.diff(geom1.coordinates, geom2.coordinates); | |
| if (differenced.length === 0) return null; | |
| if (differenced.length === 1) return helpers.polygon(differenced[0], properties); | |
| return helpers.multiPolygon(differenced, properties); | |
| } | |
| /** | |
| * Detect Empty Polygon | |
| * | |
| * @private | |
| * @param {Geometry<Polygon|MultiPolygon>} geom Geometry Object | |
| * @returns {Geometry<Polygon|MultiPolygon>|null} removed any polygons with no areas | |
| */ | |
| function removeEmptyPolygon(geom) { | |
| switch (geom.type) { | |
| case 'Polygon': | |
| if (area(geom) > 1) return geom; | |
| return null; | |
| case 'MultiPolygon': | |
| var coordinates = []; | |
| meta.flattenEach(geom, function (feature) { | |
| if (area(feature) > 1) coordinates.push(feature.geometry.coordinates); | |
| }); | |
| if (coordinates.length) return {type: 'MultiPolygon', coordinates: coordinates}; | |
| } | |
| } | |
| module.exports = difference; | |
| },{"@turf/area":1,"@turf/helpers":3,"@turf/invariant":4,"@turf/meta":5,"martinez-polygon-clipping":6}],3:[function(require,module,exports){ | |
| "use strict"; | |
| Object.defineProperty(exports, "__esModule", { value: true }); | |
| /** | |
| * @module helpers | |
| */ | |
| /** | |
| * Earth Radius used with the Harvesine formula and approximates using a spherical (non-ellipsoid) Earth. | |
| * | |
| * @memberof helpers | |
| * @type {number} | |
| */ | |
| exports.earthRadius = 6371008.8; | |
| /** | |
| * Unit of measurement factors using a spherical (non-ellipsoid) earth radius. | |
| * | |
| * @memberof helpers | |
| * @type {Object} | |
| */ | |
| exports.factors = { | |
| centimeters: exports.earthRadius * 100, | |
| centimetres: exports.earthRadius * 100, | |
| degrees: exports.earthRadius / 111325, | |
| feet: exports.earthRadius * 3.28084, | |
| inches: exports.earthRadius * 39.370, | |
| kilometers: exports.earthRadius / 1000, | |
| kilometres: exports.earthRadius / 1000, | |
| meters: exports.earthRadius, | |
| metres: exports.earthRadius, | |
| miles: exports.earthRadius / 1609.344, | |
| millimeters: exports.earthRadius * 1000, | |
| millimetres: exports.earthRadius * 1000, | |
| nauticalmiles: exports.earthRadius / 1852, | |
| radians: 1, | |
| yards: exports.earthRadius / 1.0936, | |
| }; | |
| /** | |
| * Units of measurement factors based on 1 meter. | |
| * | |
| * @memberof helpers | |
| * @type {Object} | |
| */ | |
| exports.unitsFactors = { | |
| centimeters: 100, | |
| centimetres: 100, | |
| degrees: 1 / 111325, | |
| feet: 3.28084, | |
| inches: 39.370, | |
| kilometers: 1 / 1000, | |
| kilometres: 1 / 1000, | |
| meters: 1, | |
| metres: 1, | |
| miles: 1 / 1609.344, | |
| millimeters: 1000, | |
| millimetres: 1000, | |
| nauticalmiles: 1 / 1852, | |
| radians: 1 / exports.earthRadius, | |
| yards: 1 / 1.0936, | |
| }; | |
| /** | |
| * Area of measurement factors based on 1 square meter. | |
| * | |
| * @memberof helpers | |
| * @type {Object} | |
| */ | |
| exports.areaFactors = { | |
| acres: 0.000247105, | |
| centimeters: 10000, | |
| centimetres: 10000, | |
| feet: 10.763910417, | |
| inches: 1550.003100006, | |
| kilometers: 0.000001, | |
| kilometres: 0.000001, | |
| meters: 1, | |
| metres: 1, | |
| miles: 3.86e-7, | |
| millimeters: 1000000, | |
| millimetres: 1000000, | |
| yards: 1.195990046, | |
| }; | |
| /** | |
| * Wraps a GeoJSON {@link Geometry} in a GeoJSON {@link Feature}. | |
| * | |
| * @name feature | |
| * @param {Geometry} geometry input geometry | |
| * @param {Object} [properties={}] an Object of key-value pairs to add as properties | |
| * @param {Object} [options={}] Optional Parameters | |
| * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature | |
| * @param {string|number} [options.id] Identifier associated with the Feature | |
| * @returns {Feature} a GeoJSON Feature | |
| * @example | |
| * var geometry = { | |
| * "type": "Point", | |
| * "coordinates": [110, 50] | |
| * }; | |
| * | |
| * var feature = turf.feature(geometry); | |
| * | |
| * //=feature | |
| */ | |
| function feature(geom, properties, options) { | |
| if (options === void 0) { options = {}; } | |
| var feat = { type: "Feature" }; | |
| if (options.id === 0 || options.id) { | |
| feat.id = options.id; | |
| } | |
| if (options.bbox) { | |
| feat.bbox = options.bbox; | |
| } | |
| feat.properties = properties || {}; | |
| feat.geometry = geom; | |
| return feat; | |
| } | |
| exports.feature = feature; | |
| /** | |
| * Creates a GeoJSON {@link Geometry} from a Geometry string type & coordinates. | |
| * For GeometryCollection type use `helpers.geometryCollection` | |
| * | |
| * @name geometry | |
| * @param {string} type Geometry Type | |
| * @param {Array<any>} coordinates Coordinates | |
| * @param {Object} [options={}] Optional Parameters | |
| * @returns {Geometry} a GeoJSON Geometry | |
| * @example | |
| * var type = "Point"; | |
| * var coordinates = [110, 50]; | |
| * var geometry = turf.geometry(type, coordinates); | |
| * // => geometry | |
| */ | |
| function geometry(type, coordinates, options) { | |
| if (options === void 0) { options = {}; } | |
| switch (type) { | |
| case "Point": return point(coordinates).geometry; | |
| case "LineString": return lineString(coordinates).geometry; | |
| case "Polygon": return polygon(coordinates).geometry; | |
| case "MultiPoint": return multiPoint(coordinates).geometry; | |
| case "MultiLineString": return multiLineString(coordinates).geometry; | |
| case "MultiPolygon": return multiPolygon(coordinates).geometry; | |
| default: throw new Error(type + " is invalid"); | |
| } | |
| } | |
| exports.geometry = geometry; | |
| /** | |
| * Creates a {@link Point} {@link Feature} from a Position. | |
| * | |
| * @name point | |
| * @param {Array<number>} coordinates longitude, latitude position (each in decimal degrees) | |
| * @param {Object} [properties={}] an Object of key-value pairs to add as properties | |
| * @param {Object} [options={}] Optional Parameters | |
| * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature | |
| * @param {string|number} [options.id] Identifier associated with the Feature | |
| * @returns {Feature<Point>} a Point feature | |
| * @example | |
| * var point = turf.point([-75.343, 39.984]); | |
| * | |
| * //=point | |
| */ | |
| function point(coordinates, properties, options) { | |
| if (options === void 0) { options = {}; } | |
| var geom = { | |
| type: "Point", | |
| coordinates: coordinates, | |
| }; | |
| return feature(geom, properties, options); | |
| } | |
| exports.point = point; | |
| /** | |
| * Creates a {@link Point} {@link FeatureCollection} from an Array of Point coordinates. | |
| * | |
| * @name points | |
| * @param {Array<Array<number>>} coordinates an array of Points | |
| * @param {Object} [properties={}] Translate these properties to each Feature | |
| * @param {Object} [options={}] Optional Parameters | |
| * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] | |
| * associated with the FeatureCollection | |
| * @param {string|number} [options.id] Identifier associated with the FeatureCollection | |
| * @returns {FeatureCollection<Point>} Point Feature | |
| * @example | |
| * var points = turf.points([ | |
| * [-75, 39], | |
| * [-80, 45], | |
| * [-78, 50] | |
| * ]); | |
| * | |
| * //=points | |
| */ | |
| function points(coordinates, properties, options) { | |
| if (options === void 0) { options = {}; } | |
| return featureCollection(coordinates.map(function (coords) { | |
| return point(coords, properties); | |
| }), options); | |
| } | |
| exports.points = points; | |
| /** | |
| * Creates a {@link Polygon} {@link Feature} from an Array of LinearRings. | |
| * | |
| * @name polygon | |
| * @param {Array<Array<Array<number>>>} coordinates an array of LinearRings | |
| * @param {Object} [properties={}] an Object of key-value pairs to add as properties | |
| * @param {Object} [options={}] Optional Parameters | |
| * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature | |
| * @param {string|number} [options.id] Identifier associated with the Feature | |
| * @returns {Feature<Polygon>} Polygon Feature | |
| * @example | |
| * var polygon = turf.polygon([[[-5, 52], [-4, 56], [-2, 51], [-7, 54], [-5, 52]]], { name: 'poly1' }); | |
| * | |
| * //=polygon | |
| */ | |
| function polygon(coordinates, properties, options) { | |
| if (options === void 0) { options = {}; } | |
| for (var _i = 0, coordinates_1 = coordinates; _i < coordinates_1.length; _i++) { | |
| var ring = coordinates_1[_i]; | |
| if (ring.length < 4) { | |
| throw new Error("Each LinearRing of a Polygon must have 4 or more Positions."); | |
| } | |
| for (var j = 0; j < ring[ring.length - 1].length; j++) { | |
| // Check if first point of Polygon contains two numbers | |
| if (ring[ring.length - 1][j] !== ring[0][j]) { | |
| throw new Error("First and last Position are not equivalent."); | |
| } | |
| } | |
| } | |
| var geom = { | |
| type: "Polygon", | |
| coordinates: coordinates, | |
| }; | |
| return feature(geom, properties, options); | |
| } | |
| exports.polygon = polygon; | |
| /** | |
| * Creates a {@link Polygon} {@link FeatureCollection} from an Array of Polygon coordinates. | |
| * | |
| * @name polygons | |
| * @param {Array<Array<Array<Array<number>>>>} coordinates an array of Polygon coordinates | |
| * @param {Object} [properties={}] an Object of key-value pairs to add as properties | |
| * @param {Object} [options={}] Optional Parameters | |
| * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature | |
| * @param {string|number} [options.id] Identifier associated with the FeatureCollection | |
| * @returns {FeatureCollection<Polygon>} Polygon FeatureCollection | |
| * @example | |
| * var polygons = turf.polygons([ | |
| * [[[-5, 52], [-4, 56], [-2, 51], [-7, 54], [-5, 52]]], | |
| * [[[-15, 42], [-14, 46], [-12, 41], [-17, 44], [-15, 42]]], | |
| * ]); | |
| * | |
| * //=polygons | |
| */ | |
| function polygons(coordinates, properties, options) { | |
| if (options === void 0) { options = {}; } | |
| return featureCollection(coordinates.map(function (coords) { | |
| return polygon(coords, properties); | |
| }), options); | |
| } | |
| exports.polygons = polygons; | |
| /** | |
| * Creates a {@link LineString} {@link Feature} from an Array of Positions. | |
| * | |
| * @name lineString | |
| * @param {Array<Array<number>>} coordinates an array of Positions | |
| * @param {Object} [properties={}] an Object of key-value pairs to add as properties | |
| * @param {Object} [options={}] Optional Parameters | |
| * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature | |
| * @param {string|number} [options.id] Identifier associated with the Feature | |
| * @returns {Feature<LineString>} LineString Feature | |
| * @example | |
| * var linestring1 = turf.lineString([[-24, 63], [-23, 60], [-25, 65], [-20, 69]], {name: 'line 1'}); | |
| * var linestring2 = turf.lineString([[-14, 43], [-13, 40], [-15, 45], [-10, 49]], {name: 'line 2'}); | |
| * | |
| * //=linestring1 | |
| * //=linestring2 | |
| */ | |
| function lineString(coordinates, properties, options) { | |
| if (options === void 0) { options = {}; } | |
| if (coordinates.length < 2) { | |
| throw new Error("coordinates must be an array of two or more positions"); | |
| } | |
| var geom = { | |
| type: "LineString", | |
| coordinates: coordinates, | |
| }; | |
| return feature(geom, properties, options); | |
| } | |
| exports.lineString = lineString; | |
| /** | |
| * Creates a {@link LineString} {@link FeatureCollection} from an Array of LineString coordinates. | |
| * | |
| * @name lineStrings | |
| * @param {Array<Array<Array<number>>>} coordinates an array of LinearRings | |
| * @param {Object} [properties={}] an Object of key-value pairs to add as properties | |
| * @param {Object} [options={}] Optional Parameters | |
| * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] | |
| * associated with the FeatureCollection | |
| * @param {string|number} [options.id] Identifier associated with the FeatureCollection | |
| * @returns {FeatureCollection<LineString>} LineString FeatureCollection | |
| * @example | |
| * var linestrings = turf.lineStrings([ | |
| * [[-24, 63], [-23, 60], [-25, 65], [-20, 69]], | |
| * [[-14, 43], [-13, 40], [-15, 45], [-10, 49]] | |
| * ]); | |
| * | |
| * //=linestrings | |
| */ | |
| function lineStrings(coordinates, properties, options) { | |
| if (options === void 0) { options = {}; } | |
| return featureCollection(coordinates.map(function (coords) { | |
| return lineString(coords, properties); | |
| }), options); | |
| } | |
| exports.lineStrings = lineStrings; | |
| /** | |
| * Takes one or more {@link Feature|Features} and creates a {@link FeatureCollection}. | |
| * | |
| * @name featureCollection | |
| * @param {Feature[]} features input features | |
| * @param {Object} [options={}] Optional Parameters | |
| * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature | |
| * @param {string|number} [options.id] Identifier associated with the Feature | |
| * @returns {FeatureCollection} FeatureCollection of Features | |
| * @example | |
| * var locationA = turf.point([-75.343, 39.984], {name: 'Location A'}); | |
| * var locationB = turf.point([-75.833, 39.284], {name: 'Location B'}); | |
| * var locationC = turf.point([-75.534, 39.123], {name: 'Location C'}); | |
| * | |
| * var collection = turf.featureCollection([ | |
| * locationA, | |
| * locationB, | |
| * locationC | |
| * ]); | |
| * | |
| * //=collection | |
| */ | |
| function featureCollection(features, options) { | |
| if (options === void 0) { options = {}; } | |
| var fc = { type: "FeatureCollection" }; | |
| if (options.id) { | |
| fc.id = options.id; | |
| } | |
| if (options.bbox) { | |
| fc.bbox = options.bbox; | |
| } | |
| fc.features = features; | |
| return fc; | |
| } | |
| exports.featureCollection = featureCollection; | |
| /** | |
| * Creates a {@link Feature<MultiLineString>} based on a | |
| * coordinate array. Properties can be added optionally. | |
| * | |
| * @name multiLineString | |
| * @param {Array<Array<Array<number>>>} coordinates an array of LineStrings | |
| * @param {Object} [properties={}] an Object of key-value pairs to add as properties | |
| * @param {Object} [options={}] Optional Parameters | |
| * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature | |
| * @param {string|number} [options.id] Identifier associated with the Feature | |
| * @returns {Feature<MultiLineString>} a MultiLineString feature | |
| * @throws {Error} if no coordinates are passed | |
| * @example | |
| * var multiLine = turf.multiLineString([[[0,0],[10,10]]]); | |
| * | |
| * //=multiLine | |
| */ | |
| function multiLineString(coordinates, properties, options) { | |
| if (options === void 0) { options = {}; } | |
| var geom = { | |
| type: "MultiLineString", | |
| coordinates: coordinates, | |
| }; | |
| return feature(geom, properties, options); | |
| } | |
| exports.multiLineString = multiLineString; | |
| /** | |
| * Creates a {@link Feature<MultiPoint>} based on a | |
| * coordinate array. Properties can be added optionally. | |
| * | |
| * @name multiPoint | |
| * @param {Array<Array<number>>} coordinates an array of Positions | |
| * @param {Object} [properties={}] an Object of key-value pairs to add as properties | |
| * @param {Object} [options={}] Optional Parameters | |
| * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature | |
| * @param {string|number} [options.id] Identifier associated with the Feature | |
| * @returns {Feature<MultiPoint>} a MultiPoint feature | |
| * @throws {Error} if no coordinates are passed | |
| * @example | |
| * var multiPt = turf.multiPoint([[0,0],[10,10]]); | |
| * | |
| * //=multiPt | |
| */ | |
| function multiPoint(coordinates, properties, options) { | |
| if (options === void 0) { options = {}; } | |
| var geom = { | |
| type: "MultiPoint", | |
| coordinates: coordinates, | |
| }; | |
| return feature(geom, properties, options); | |
| } | |
| exports.multiPoint = multiPoint; | |
| /** | |
| * Creates a {@link Feature<MultiPolygon>} based on a | |
| * coordinate array. Properties can be added optionally. | |
| * | |
| * @name multiPolygon | |
| * @param {Array<Array<Array<Array<number>>>>} coordinates an array of Polygons | |
| * @param {Object} [properties={}] an Object of key-value pairs to add as properties | |
| * @param {Object} [options={}] Optional Parameters | |
| * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature | |
| * @param {string|number} [options.id] Identifier associated with the Feature | |
| * @returns {Feature<MultiPolygon>} a multipolygon feature | |
| * @throws {Error} if no coordinates are passed | |
| * @example | |
| * var multiPoly = turf.multiPolygon([[[[0,0],[0,10],[10,10],[10,0],[0,0]]]]); | |
| * | |
| * //=multiPoly | |
| * | |
| */ | |
| function multiPolygon(coordinates, properties, options) { | |
| if (options === void 0) { options = {}; } | |
| var geom = { | |
| type: "MultiPolygon", | |
| coordinates: coordinates, | |
| }; | |
| return feature(geom, properties, options); | |
| } | |
| exports.multiPolygon = multiPolygon; | |
| /** | |
| * Creates a {@link Feature<GeometryCollection>} based on a | |
| * coordinate array. Properties can be added optionally. | |
| * | |
| * @name geometryCollection | |
| * @param {Array<Geometry>} geometries an array of GeoJSON Geometries | |
| * @param {Object} [properties={}] an Object of key-value pairs to add as properties | |
| * @param {Object} [options={}] Optional Parameters | |
| * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature | |
| * @param {string|number} [options.id] Identifier associated with the Feature | |
| * @returns {Feature<GeometryCollection>} a GeoJSON GeometryCollection Feature | |
| * @example | |
| * var pt = turf.geometry("Point", [100, 0]); | |
| * var line = turf.geometry("LineString", [[101, 0], [102, 1]]); | |
| * var collection = turf.geometryCollection([pt, line]); | |
| * | |
| * // => collection | |
| */ | |
| function geometryCollection(geometries, properties, options) { | |
| if (options === void 0) { options = {}; } | |
| var geom = { | |
| type: "GeometryCollection", | |
| geometries: geometries, | |
| }; | |
| return feature(geom, properties, options); | |
| } | |
| exports.geometryCollection = geometryCollection; | |
| /** | |
| * Round number to precision | |
| * | |
| * @param {number} num Number | |
| * @param {number} [precision=0] Precision | |
| * @returns {number} rounded number | |
| * @example | |
| * turf.round(120.4321) | |
| * //=120 | |
| * | |
| * turf.round(120.4321, 2) | |
| * //=120.43 | |
| */ | |
| function round(num, precision) { | |
| if (precision === void 0) { precision = 0; } | |
| if (precision && !(precision >= 0)) { | |
| throw new Error("precision must be a positive number"); | |
| } | |
| var multiplier = Math.pow(10, precision || 0); | |
| return Math.round(num * multiplier) / multiplier; | |
| } | |
| exports.round = round; | |
| /** | |
| * Convert a distance measurement (assuming a spherical Earth) from radians to a more friendly unit. | |
| * Valid units: miles, nauticalmiles, inches, yards, meters, metres, kilometers, centimeters, feet | |
| * | |
| * @name radiansToLength | |
| * @param {number} radians in radians across the sphere | |
| * @param {string} [units="kilometers"] can be degrees, radians, miles, or kilometers inches, yards, metres, | |
| * meters, kilometres, kilometers. | |
| * @returns {number} distance | |
| */ | |
| function radiansToLength(radians, units) { | |
| if (units === void 0) { units = "kilometers"; } | |
| var factor = exports.factors[units]; | |
| if (!factor) { | |
| throw new Error(units + " units is invalid"); | |
| } | |
| return radians * factor; | |
| } | |
| exports.radiansToLength = radiansToLength; | |
| /** | |
| * Convert a distance measurement (assuming a spherical Earth) from a real-world unit into radians | |
| * Valid units: miles, nauticalmiles, inches, yards, meters, metres, kilometers, centimeters, feet | |
| * | |
| * @name lengthToRadians | |
| * @param {number} distance in real units | |
| * @param {string} [units="kilometers"] can be degrees, radians, miles, or kilometers inches, yards, metres, | |
| * meters, kilometres, kilometers. | |
| * @returns {number} radians | |
| */ | |
| function lengthToRadians(distance, units) { | |
| if (units === void 0) { units = "kilometers"; } | |
| var factor = exports.factors[units]; | |
| if (!factor) { | |
| throw new Error(units + " units is invalid"); | |
| } | |
| return distance / factor; | |
| } | |
| exports.lengthToRadians = lengthToRadians; | |
| /** | |
| * Convert a distance measurement (assuming a spherical Earth) from a real-world unit into degrees | |
| * Valid units: miles, nauticalmiles, inches, yards, meters, metres, centimeters, kilometres, feet | |
| * | |
| * @name lengthToDegrees | |
| * @param {number} distance in real units | |
| * @param {string} [units="kilometers"] can be degrees, radians, miles, or kilometers inches, yards, metres, | |
| * meters, kilometres, kilometers. | |
| * @returns {number} degrees | |
| */ | |
| function lengthToDegrees(distance, units) { | |
| return radiansToDegrees(lengthToRadians(distance, units)); | |
| } | |
| exports.lengthToDegrees = lengthToDegrees; | |
| /** | |
| * Converts any bearing angle from the north line direction (positive clockwise) | |
| * and returns an angle between 0-360 degrees (positive clockwise), 0 being the north line | |
| * | |
| * @name bearingToAzimuth | |
| * @param {number} bearing angle, between -180 and +180 degrees | |
| * @returns {number} angle between 0 and 360 degrees | |
| */ | |
| function bearingToAzimuth(bearing) { | |
| var angle = bearing % 360; | |
| if (angle < 0) { | |
| angle += 360; | |
| } | |
| return angle; | |
| } | |
| exports.bearingToAzimuth = bearingToAzimuth; | |
| /** | |
| * Converts an angle in radians to degrees | |
| * | |
| * @name radiansToDegrees | |
| * @param {number} radians angle in radians | |
| * @returns {number} degrees between 0 and 360 degrees | |
| */ | |
| function radiansToDegrees(radians) { | |
| var degrees = radians % (2 * Math.PI); | |
| return degrees * 180 / Math.PI; | |
| } | |
| exports.radiansToDegrees = radiansToDegrees; | |
| /** | |
| * Converts an angle in degrees to radians | |
| * | |
| * @name degreesToRadians | |
| * @param {number} degrees angle between 0 and 360 degrees | |
| * @returns {number} angle in radians | |
| */ | |
| function degreesToRadians(degrees) { | |
| var radians = degrees % 360; | |
| return radians * Math.PI / 180; | |
| } | |
| exports.degreesToRadians = degreesToRadians; | |
| /** | |
| * Converts a length to the requested unit. | |
| * Valid units: miles, nauticalmiles, inches, yards, meters, metres, kilometers, centimeters, feet | |
| * | |
| * @param {number} length to be converted | |
| * @param {Units} [originalUnit="kilometers"] of the length | |
| * @param {Units} [finalUnit="kilometers"] returned unit | |
| * @returns {number} the converted length | |
| */ | |
| function convertLength(length, originalUnit, finalUnit) { | |
| if (originalUnit === void 0) { originalUnit = "kilometers"; } | |
| if (finalUnit === void 0) { finalUnit = "kilometers"; } | |
| if (!(length >= 0)) { | |
| throw new Error("length must be a positive number"); | |
| } | |
| return radiansToLength(lengthToRadians(length, originalUnit), finalUnit); | |
| } | |
| exports.convertLength = convertLength; | |
| /** | |
| * Converts a area to the requested unit. | |
| * Valid units: kilometers, kilometres, meters, metres, centimetres, millimeters, acres, miles, yards, feet, inches | |
| * @param {number} area to be converted | |
| * @param {Units} [originalUnit="meters"] of the distance | |
| * @param {Units} [finalUnit="kilometers"] returned unit | |
| * @returns {number} the converted distance | |
| */ | |
| function convertArea(area, originalUnit, finalUnit) { | |
| if (originalUnit === void 0) { originalUnit = "meters"; } | |
| if (finalUnit === void 0) { finalUnit = "kilometers"; } | |
| if (!(area >= 0)) { | |
| throw new Error("area must be a positive number"); | |
| } | |
| var startFactor = exports.areaFactors[originalUnit]; | |
| if (!startFactor) { | |
| throw new Error("invalid original units"); | |
| } | |
| var finalFactor = exports.areaFactors[finalUnit]; | |
| if (!finalFactor) { | |
| throw new Error("invalid final units"); | |
| } | |
| return (area / startFactor) * finalFactor; | |
| } | |
| exports.convertArea = convertArea; | |
| /** | |
| * isNumber | |
| * | |
| * @param {*} num Number to validate | |
| * @returns {boolean} true/false | |
| * @example | |
| * turf.isNumber(123) | |
| * //=true | |
| * turf.isNumber('foo') | |
| * //=false | |
| */ | |
| function isNumber(num) { | |
| return !isNaN(num) && num !== null && !Array.isArray(num) && !/^\s*$/.test(num); | |
| } | |
| exports.isNumber = isNumber; | |
| /** | |
| * isObject | |
| * | |
| * @param {*} input variable to validate | |
| * @returns {boolean} true/false | |
| * @example | |
| * turf.isObject({elevation: 10}) | |
| * //=true | |
| * turf.isObject('foo') | |
| * //=false | |
| */ | |
| function isObject(input) { | |
| return (!!input) && (input.constructor === Object); | |
| } | |
| exports.isObject = isObject; | |
| /** | |
| * Validate BBox | |
| * | |
| * @private | |
| * @param {Array<number>} bbox BBox to validate | |
| * @returns {void} | |
| * @throws Error if BBox is not valid | |
| * @example | |
| * validateBBox([-180, -40, 110, 50]) | |
| * //=OK | |
| * validateBBox([-180, -40]) | |
| * //=Error | |
| * validateBBox('Foo') | |
| * //=Error | |
| * validateBBox(5) | |
| * //=Error | |
| * validateBBox(null) | |
| * //=Error | |
| * validateBBox(undefined) | |
| * //=Error | |
| */ | |
| function validateBBox(bbox) { | |
| if (!bbox) { | |
| throw new Error("bbox is required"); | |
| } | |
| if (!Array.isArray(bbox)) { | |
| throw new Error("bbox must be an Array"); | |
| } | |
| if (bbox.length !== 4 && bbox.length !== 6) { | |
| throw new Error("bbox must be an Array of 4 or 6 numbers"); | |
| } | |
| bbox.forEach(function (num) { | |
| if (!isNumber(num)) { | |
| throw new Error("bbox must only contain numbers"); | |
| } | |
| }); | |
| } | |
| exports.validateBBox = validateBBox; | |
| /** | |
| * Validate Id | |
| * | |
| * @private | |
| * @param {string|number} id Id to validate | |
| * @returns {void} | |
| * @throws Error if Id is not valid | |
| * @example | |
| * validateId([-180, -40, 110, 50]) | |
| * //=Error | |
| * validateId([-180, -40]) | |
| * //=Error | |
| * validateId('Foo') | |
| * //=OK | |
| * validateId(5) | |
| * //=OK | |
| * validateId(null) | |
| * //=Error | |
| * validateId(undefined) | |
| * //=Error | |
| */ | |
| function validateId(id) { | |
| if (!id) { | |
| throw new Error("id is required"); | |
| } | |
| if (["string", "number"].indexOf(typeof id) === -1) { | |
| throw new Error("id must be a number or a string"); | |
| } | |
| } | |
| exports.validateId = validateId; | |
| // Deprecated methods | |
| function radians2degrees() { | |
| throw new Error("method has been renamed to `radiansToDegrees`"); | |
| } | |
| exports.radians2degrees = radians2degrees; | |
| function degrees2radians() { | |
| throw new Error("method has been renamed to `degreesToRadians`"); | |
| } | |
| exports.degrees2radians = degrees2radians; | |
| function distanceToDegrees() { | |
| throw new Error("method has been renamed to `lengthToDegrees`"); | |
| } | |
| exports.distanceToDegrees = distanceToDegrees; | |
| function distanceToRadians() { | |
| throw new Error("method has been renamed to `lengthToRadians`"); | |
| } | |
| exports.distanceToRadians = distanceToRadians; | |
| function radiansToDistance() { | |
| throw new Error("method has been renamed to `radiansToLength`"); | |
| } | |
| exports.radiansToDistance = radiansToDistance; | |
| function bearingToAngle() { | |
| throw new Error("method has been renamed to `bearingToAzimuth`"); | |
| } | |
| exports.bearingToAngle = bearingToAngle; | |
| function convertDistance() { | |
| throw new Error("method has been renamed to `convertLength`"); | |
| } | |
| exports.convertDistance = convertDistance; | |
| },{}],4:[function(require,module,exports){ | |
| "use strict"; | |
| Object.defineProperty(exports, "__esModule", { value: true }); | |
| var helpers_1 = require("@turf/helpers"); | |
| /** | |
| * Unwrap a coordinate from a Point Feature, Geometry or a single coordinate. | |
| * | |
| * @name getCoord | |
| * @param {Array<number>|Geometry<Point>|Feature<Point>} coord GeoJSON Point or an Array of numbers | |
| * @returns {Array<number>} coordinates | |
| * @example | |
| * var pt = turf.point([10, 10]); | |
| * | |
| * var coord = turf.getCoord(pt); | |
| * //= [10, 10] | |
| */ | |
| function getCoord(coord) { | |
| if (!coord) { | |
| throw new Error("coord is required"); | |
| } | |
| if (!Array.isArray(coord)) { | |
| if (coord.type === "Feature" && coord.geometry !== null && coord.geometry.type === "Point") { | |
| return coord.geometry.coordinates; | |
| } | |
| if (coord.type === "Point") { | |
| return coord.coordinates; | |
| } | |
| } | |
| if (Array.isArray(coord) && coord.length >= 2 && !Array.isArray(coord[0]) && !Array.isArray(coord[1])) { | |
| return coord; | |
| } | |
| throw new Error("coord must be GeoJSON Point or an Array of numbers"); | |
| } | |
| exports.getCoord = getCoord; | |
| /** | |
| * Unwrap coordinates from a Feature, Geometry Object or an Array | |
| * | |
| * @name getCoords | |
| * @param {Array<any>|Geometry|Feature} coords Feature, Geometry Object or an Array | |
| * @returns {Array<any>} coordinates | |
| * @example | |
| * var poly = turf.polygon([[[119.32, -8.7], [119.55, -8.69], [119.51, -8.54], [119.32, -8.7]]]); | |
| * | |
| * var coords = turf.getCoords(poly); | |
| * //= [[[119.32, -8.7], [119.55, -8.69], [119.51, -8.54], [119.32, -8.7]]] | |
| */ | |
| function getCoords(coords) { | |
| if (Array.isArray(coords)) { | |
| return coords; | |
| } | |
| // Feature | |
| if (coords.type === "Feature") { | |
| if (coords.geometry !== null) { | |
| return coords.geometry.coordinates; | |
| } | |
| } | |
| else { | |
| // Geometry | |
| if (coords.coordinates) { | |
| return coords.coordinates; | |
| } | |
| } | |
| throw new Error("coords must be GeoJSON Feature, Geometry Object or an Array"); | |
| } | |
| exports.getCoords = getCoords; | |
| /** | |
| * Checks if coordinates contains a number | |
| * | |
| * @name containsNumber | |
| * @param {Array<any>} coordinates GeoJSON Coordinates | |
| * @returns {boolean} true if Array contains a number | |
| */ | |
| function containsNumber(coordinates) { | |
| if (coordinates.length > 1 && helpers_1.isNumber(coordinates[0]) && helpers_1.isNumber(coordinates[1])) { | |
| return true; | |
| } | |
| if (Array.isArray(coordinates[0]) && coordinates[0].length) { | |
| return containsNumber(coordinates[0]); | |
| } | |
| throw new Error("coordinates must only contain numbers"); | |
| } | |
| exports.containsNumber = containsNumber; | |
| /** | |
| * Enforce expectations about types of GeoJSON objects for Turf. | |
| * | |
| * @name geojsonType | |
| * @param {GeoJSON} value any GeoJSON object | |
| * @param {string} type expected GeoJSON type | |
| * @param {string} name name of calling function | |
| * @throws {Error} if value is not the expected type. | |
| */ | |
| function geojsonType(value, type, name) { | |
| if (!type || !name) { | |
| throw new Error("type and name required"); | |
| } | |
| if (!value || value.type !== type) { | |
| throw new Error("Invalid input to " + name + ": must be a " + type + ", given " + value.type); | |
| } | |
| } | |
| exports.geojsonType = geojsonType; | |
| /** | |
| * Enforce expectations about types of {@link Feature} inputs for Turf. | |
| * Internally this uses {@link geojsonType} to judge geometry types. | |
| * | |
| * @name featureOf | |
| * @param {Feature} feature a feature with an expected geometry type | |
| * @param {string} type expected GeoJSON type | |
| * @param {string} name name of calling function | |
| * @throws {Error} error if value is not the expected type. | |
| */ | |
| function featureOf(feature, type, name) { | |
| if (!feature) { | |
| throw new Error("No feature passed"); | |
| } | |
| if (!name) { | |
| throw new Error(".featureOf() requires a name"); | |
| } | |
| if (!feature || feature.type !== "Feature" || !feature.geometry) { | |
| throw new Error("Invalid input to " + name + ", Feature with geometry required"); | |
| } | |
| if (!feature.geometry || feature.geometry.type !== type) { | |
| throw new Error("Invalid input to " + name + ": must be a " + type + ", given " + feature.geometry.type); | |
| } | |
| } | |
| exports.featureOf = featureOf; | |
| /** | |
| * Enforce expectations about types of {@link FeatureCollection} inputs for Turf. | |
| * Internally this uses {@link geojsonType} to judge geometry types. | |
| * | |
| * @name collectionOf | |
| * @param {FeatureCollection} featureCollection a FeatureCollection for which features will be judged | |
| * @param {string} type expected GeoJSON type | |
| * @param {string} name name of calling function | |
| * @throws {Error} if value is not the expected type. | |
| */ | |
| function collectionOf(featureCollection, type, name) { | |
| if (!featureCollection) { | |
| throw new Error("No featureCollection passed"); | |
| } | |
| if (!name) { | |
| throw new Error(".collectionOf() requires a name"); | |
| } | |
| if (!featureCollection || featureCollection.type !== "FeatureCollection") { | |
| throw new Error("Invalid input to " + name + ", FeatureCollection required"); | |
| } | |
| for (var _i = 0, _a = featureCollection.features; _i < _a.length; _i++) { | |
| var feature = _a[_i]; | |
| if (!feature || feature.type !== "Feature" || !feature.geometry) { | |
| throw new Error("Invalid input to " + name + ", Feature with geometry required"); | |
| } | |
| if (!feature.geometry || feature.geometry.type !== type) { | |
| throw new Error("Invalid input to " + name + ": must be a " + type + ", given " + feature.geometry.type); | |
| } | |
| } | |
| } | |
| exports.collectionOf = collectionOf; | |
| /** | |
| * Get Geometry from Feature or Geometry Object | |
| * | |
| * @param {Feature|Geometry} geojson GeoJSON Feature or Geometry Object | |
| * @returns {Geometry|null} GeoJSON Geometry Object | |
| * @throws {Error} if geojson is not a Feature or Geometry Object | |
| * @example | |
| * var point = { | |
| * "type": "Feature", | |
| * "properties": {}, | |
| * "geometry": { | |
| * "type": "Point", | |
| * "coordinates": [110, 40] | |
| * } | |
| * } | |
| * var geom = turf.getGeom(point) | |
| * //={"type": "Point", "coordinates": [110, 40]} | |
| */ | |
| function getGeom(geojson) { | |
| if (geojson.type === "Feature") { | |
| return geojson.geometry; | |
| } | |
| return geojson; | |
| } | |
| exports.getGeom = getGeom; | |
| /** | |
| * Get GeoJSON object's type, Geometry type is prioritize. | |
| * | |
| * @param {GeoJSON} geojson GeoJSON object | |
| * @param {string} [name="geojson"] name of the variable to display in error message | |
| * @returns {string} GeoJSON type | |
| * @example | |
| * var point = { | |
| * "type": "Feature", | |
| * "properties": {}, | |
| * "geometry": { | |
| * "type": "Point", | |
| * "coordinates": [110, 40] | |
| * } | |
| * } | |
| * var geom = turf.getType(point) | |
| * //="Point" | |
| */ | |
| function getType(geojson, name) { | |
| if (geojson.type === "FeatureCollection") { | |
| return "FeatureCollection"; | |
| } | |
| if (geojson.type === "GeometryCollection") { | |
| return "GeometryCollection"; | |
| } | |
| if (geojson.type === "Feature" && geojson.geometry !== null) { | |
| return geojson.geometry.type; | |
| } | |
| return geojson.type; | |
| } | |
| exports.getType = getType; | |
| },{"@turf/helpers":3}],5:[function(require,module,exports){ | |
| 'use strict'; | |
| Object.defineProperty(exports, '__esModule', { value: true }); | |
| var helpers = require('@turf/helpers'); | |
| /** | |
| * Callback for coordEach | |
| * | |
| * @callback coordEachCallback | |
| * @param {Array<number>} currentCoord The current coordinate being processed. | |
| * @param {number} coordIndex The current index of the coordinate being processed. | |
| * @param {number} featureIndex The current index of the Feature being processed. | |
| * @param {number} multiFeatureIndex The current index of the Multi-Feature being processed. | |
| * @param {number} geometryIndex The current index of the Geometry being processed. | |
| */ | |
| /** | |
| * Iterate over coordinates in any GeoJSON object, similar to Array.forEach() | |
| * | |
| * @name coordEach | |
| * @param {FeatureCollection|Feature|Geometry} geojson any GeoJSON object | |
| * @param {Function} callback a method that takes (currentCoord, coordIndex, featureIndex, multiFeatureIndex) | |
| * @param {boolean} [excludeWrapCoord=false] whether or not to include the final coordinate of LinearRings that wraps the ring in its iteration. | |
| * @returns {void} | |
| * @example | |
| * var features = turf.featureCollection([ | |
| * turf.point([26, 37], {"foo": "bar"}), | |
| * turf.point([36, 53], {"hello": "world"}) | |
| * ]); | |
| * | |
| * turf.coordEach(features, function (currentCoord, coordIndex, featureIndex, multiFeatureIndex, geometryIndex) { | |
| * //=currentCoord | |
| * //=coordIndex | |
| * //=featureIndex | |
| * //=multiFeatureIndex | |
| * //=geometryIndex | |
| * }); | |
| */ | |
| function coordEach(geojson, callback, excludeWrapCoord) { | |
| // Handles null Geometry -- Skips this GeoJSON | |
| if (geojson === null) return; | |
| var j, k, l, geometry, stopG, coords, | |
| geometryMaybeCollection, | |
| wrapShrink = 0, | |
| coordIndex = 0, | |
| isGeometryCollection, | |
| type = geojson.type, | |
| isFeatureCollection = type === 'FeatureCollection', | |
| isFeature = type === 'Feature', | |
| stop = isFeatureCollection ? geojson.features.length : 1; | |
| // This logic may look a little weird. The reason why it is that way | |
| // is because it's trying to be fast. GeoJSON supports multiple kinds | |
| // of objects at its root: FeatureCollection, Features, Geometries. | |
| // This function has the responsibility of handling all of them, and that | |
| // means that some of the `for` loops you see below actually just don't apply | |
| // to certain inputs. For instance, if you give this just a | |
| // Point geometry, then both loops are short-circuited and all we do | |
| // is gradually rename the input until it's called 'geometry'. | |
| // | |
| // This also aims to allocate as few resources as possible: just a | |
| // few numbers and booleans, rather than any temporary arrays as would | |
| // be required with the normalization approach. | |
| for (var featureIndex = 0; featureIndex < stop; featureIndex++) { | |
| geometryMaybeCollection = (isFeatureCollection ? geojson.features[featureIndex].geometry : | |
| (isFeature ? geojson.geometry : geojson)); | |
| isGeometryCollection = (geometryMaybeCollection) ? geometryMaybeCollection.type === 'GeometryCollection' : false; | |
| stopG = isGeometryCollection ? geometryMaybeCollection.geometries.length : 1; | |
| for (var geomIndex = 0; geomIndex < stopG; geomIndex++) { | |
| var multiFeatureIndex = 0; | |
| var geometryIndex = 0; | |
| geometry = isGeometryCollection ? | |
| geometryMaybeCollection.geometries[geomIndex] : geometryMaybeCollection; | |
| // Handles null Geometry -- Skips this geometry | |
| if (geometry === null) continue; | |
| coords = geometry.coordinates; | |
| var geomType = geometry.type; | |
| wrapShrink = (excludeWrapCoord && (geomType === 'Polygon' || geomType === 'MultiPolygon')) ? 1 : 0; | |
| switch (geomType) { | |
| case null: | |
| break; | |
| case 'Point': | |
| if (callback(coords, coordIndex, featureIndex, multiFeatureIndex, geometryIndex) === false) return false; | |
| coordIndex++; | |
| multiFeatureIndex++; | |
| break; | |
| case 'LineString': | |
| case 'MultiPoint': | |
| for (j = 0; j < coords.length; j++) { | |
| if (callback(coords[j], coordIndex, featureIndex, multiFeatureIndex, geometryIndex) === false) return false; | |
| coordIndex++; | |
| if (geomType === 'MultiPoint') multiFeatureIndex++; | |
| } | |
| if (geomType === 'LineString') multiFeatureIndex++; | |
| break; | |
| case 'Polygon': | |
| case 'MultiLineString': | |
| for (j = 0; j < coords.length; j++) { | |
| for (k = 0; k < coords[j].length - wrapShrink; k++) { | |
| if (callback(coords[j][k], coordIndex, featureIndex, multiFeatureIndex, geometryIndex) === false) return false; | |
| coordIndex++; | |
| } | |
| if (geomType === 'MultiLineString') multiFeatureIndex++; | |
| if (geomType === 'Polygon') geometryIndex++; | |
| } | |
| if (geomType === 'Polygon') multiFeatureIndex++; | |
| break; | |
| case 'MultiPolygon': | |
| for (j = 0; j < coords.length; j++) { | |
| geometryIndex = 0; | |
| for (k = 0; k < coords[j].length; k++) { | |
| for (l = 0; l < coords[j][k].length - wrapShrink; l++) { | |
| if (callback(coords[j][k][l], coordIndex, featureIndex, multiFeatureIndex, geometryIndex) === false) return false; | |
| coordIndex++; | |
| } | |
| geometryIndex++; | |
| } | |
| multiFeatureIndex++; | |
| } | |
| break; | |
| case 'GeometryCollection': | |
| for (j = 0; j < geometry.geometries.length; j++) | |
| if (coordEach(geometry.geometries[j], callback, excludeWrapCoord) === false) return false; | |
| break; | |
| default: | |
| throw new Error('Unknown Geometry Type'); | |
| } | |
| } | |
| } | |
| } | |
| /** | |
| * Callback for coordReduce | |
| * | |
| * The first time the callback function is called, the values provided as arguments depend | |
| * on whether the reduce method has an initialValue argument. | |
| * | |
| * If an initialValue is provided to the reduce method: | |
| * - The previousValue argument is initialValue. | |
| * - The currentValue argument is the value of the first element present in the array. | |
| * | |
| * If an initialValue is not provided: | |
| * - The previousValue argument is the value of the first element present in the array. | |
| * - The currentValue argument is the value of the second element present in the array. | |
| * | |
| * @callback coordReduceCallback | |
| * @param {*} previousValue The accumulated value previously returned in the last invocation | |
| * of the callback, or initialValue, if supplied. | |
| * @param {Array<number>} currentCoord The current coordinate being processed. | |
| * @param {number} coordIndex The current index of the coordinate being processed. | |
| * Starts at index 0, if an initialValue is provided, and at index 1 otherwise. | |
| * @param {number} featureIndex The current index of the Feature being processed. | |
| * @param {number} multiFeatureIndex The current index of the Multi-Feature being processed. | |
| * @param {number} geometryIndex The current index of the Geometry being processed. | |
| */ | |
| /** | |
| * Reduce coordinates in any GeoJSON object, similar to Array.reduce() | |
| * | |
| * @name coordReduce | |
| * @param {FeatureCollection|Geometry|Feature} geojson any GeoJSON object | |
| * @param {Function} callback a method that takes (previousValue, currentCoord, coordIndex) | |
| * @param {*} [initialValue] Value to use as the first argument to the first call of the callback. | |
| * @param {boolean} [excludeWrapCoord=false] whether or not to include the final coordinate of LinearRings that wraps the ring in its iteration. | |
| * @returns {*} The value that results from the reduction. | |
| * @example | |
| * var features = turf.featureCollection([ | |
| * turf.point([26, 37], {"foo": "bar"}), | |
| * turf.point([36, 53], {"hello": "world"}) | |
| * ]); | |
| * | |
| * turf.coordReduce(features, function (previousValue, currentCoord, coordIndex, featureIndex, multiFeatureIndex, geometryIndex) { | |
| * //=previousValue | |
| * //=currentCoord | |
| * //=coordIndex | |
| * //=featureIndex | |
| * //=multiFeatureIndex | |
| * //=geometryIndex | |
| * return currentCoord; | |
| * }); | |
| */ | |
| function coordReduce(geojson, callback, initialValue, excludeWrapCoord) { | |
| var previousValue = initialValue; | |
| coordEach(geojson, function (currentCoord, coordIndex, featureIndex, multiFeatureIndex, geometryIndex) { | |
| if (coordIndex === 0 && initialValue === undefined) previousValue = currentCoord; | |
| else previousValue = callback(previousValue, currentCoord, coordIndex, featureIndex, multiFeatureIndex, geometryIndex); | |
| }, excludeWrapCoord); | |
| return previousValue; | |
| } | |
| /** | |
| * Callback for propEach | |
| * | |
| * @callback propEachCallback | |
| * @param {Object} currentProperties The current Properties being processed. | |
| * @param {number} featureIndex The current index of the Feature being processed. | |
| */ | |
| /** | |
| * Iterate over properties in any GeoJSON object, similar to Array.forEach() | |
| * | |
| * @name propEach | |
| * @param {FeatureCollection|Feature} geojson any GeoJSON object | |
| * @param {Function} callback a method that takes (currentProperties, featureIndex) | |
| * @returns {void} | |
| * @example | |
| * var features = turf.featureCollection([ | |
| * turf.point([26, 37], {foo: 'bar'}), | |
| * turf.point([36, 53], {hello: 'world'}) | |
| * ]); | |
| * | |
| * turf.propEach(features, function (currentProperties, featureIndex) { | |
| * //=currentProperties | |
| * //=featureIndex | |
| * }); | |
| */ | |
| function propEach(geojson, callback) { | |
| var i; | |
| switch (geojson.type) { | |
| case 'FeatureCollection': | |
| for (i = 0; i < geojson.features.length; i++) { | |
| if (callback(geojson.features[i].properties, i) === false) break; | |
| } | |
| break; | |
| case 'Feature': | |
| callback(geojson.properties, 0); | |
| break; | |
| } | |
| } | |
| /** | |
| * Callback for propReduce | |
| * | |
| * The first time the callback function is called, the values provided as arguments depend | |
| * on whether the reduce method has an initialValue argument. | |
| * | |
| * If an initialValue is provided to the reduce method: | |
| * - The previousValue argument is initialValue. | |
| * - The currentValue argument is the value of the first element present in the array. | |
| * | |
| * If an initialValue is not provided: | |
| * - The previousValue argument is the value of the first element present in the array. | |
| * - The currentValue argument is the value of the second element present in the array. | |
| * | |
| * @callback propReduceCallback | |
| * @param {*} previousValue The accumulated value previously returned in the last invocation | |
| * of the callback, or initialValue, if supplied. | |
| * @param {*} currentProperties The current Properties being processed. | |
| * @param {number} featureIndex The current index of the Feature being processed. | |
| */ | |
| /** | |
| * Reduce properties in any GeoJSON object into a single value, | |
| * similar to how Array.reduce works. However, in this case we lazily run | |
| * the reduction, so an array of all properties is unnecessary. | |
| * | |
| * @name propReduce | |
| * @param {FeatureCollection|Feature} geojson any GeoJSON object | |
| * @param {Function} callback a method that takes (previousValue, currentProperties, featureIndex) | |
| * @param {*} [initialValue] Value to use as the first argument to the first call of the callback. | |
| * @returns {*} The value that results from the reduction. | |
| * @example | |
| * var features = turf.featureCollection([ | |
| * turf.point([26, 37], {foo: 'bar'}), | |
| * turf.point([36, 53], {hello: 'world'}) | |
| * ]); | |
| * | |
| * turf.propReduce(features, function (previousValue, currentProperties, featureIndex) { | |
| * //=previousValue | |
| * //=currentProperties | |
| * //=featureIndex | |
| * return currentProperties | |
| * }); | |
| */ | |
| function propReduce(geojson, callback, initialValue) { | |
| var previousValue = initialValue; | |
| propEach(geojson, function (currentProperties, featureIndex) { | |
| if (featureIndex === 0 && initialValue === undefined) previousValue = currentProperties; | |
| else previousValue = callback(previousValue, currentProperties, featureIndex); | |
| }); | |
| return previousValue; | |
| } | |
| /** | |
| * Callback for featureEach | |
| * | |
| * @callback featureEachCallback | |
| * @param {Feature<any>} currentFeature The current Feature being processed. | |
| * @param {number} featureIndex The current index of the Feature being processed. | |
| */ | |
| /** | |
| * Iterate over features in any GeoJSON object, similar to | |
| * Array.forEach. | |
| * | |
| * @name featureEach | |
| * @param {FeatureCollection|Feature|Geometry} geojson any GeoJSON object | |
| * @param {Function} callback a method that takes (currentFeature, featureIndex) | |
| * @returns {void} | |
| * @example | |
| * var features = turf.featureCollection([ | |
| * turf.point([26, 37], {foo: 'bar'}), | |
| * turf.point([36, 53], {hello: 'world'}) | |
| * ]); | |
| * | |
| * turf.featureEach(features, function (currentFeature, featureIndex) { | |
| * //=currentFeature | |
| * //=featureIndex | |
| * }); | |
| */ | |
| function featureEach(geojson, callback) { | |
| if (geojson.type === 'Feature') { | |
| callback(geojson, 0); | |
| } else if (geojson.type === 'FeatureCollection') { | |
| for (var i = 0; i < geojson.features.length; i++) { | |
| if (callback(geojson.features[i], i) === false) break; | |
| } | |
| } | |
| } | |
| /** | |
| * Callback for featureReduce | |
| * | |
| * The first time the callback function is called, the values provided as arguments depend | |
| * on whether the reduce method has an initialValue argument. | |
| * | |
| * If an initialValue is provided to the reduce method: | |
| * - The previousValue argument is initialValue. | |
| * - The currentValue argument is the value of the first element present in the array. | |
| * | |
| * If an initialValue is not provided: | |
| * - The previousValue argument is the value of the first element present in the array. | |
| * - The currentValue argument is the value of the second element present in the array. | |
| * | |
| * @callback featureReduceCallback | |
| * @param {*} previousValue The accumulated value previously returned in the last invocation | |
| * of the callback, or initialValue, if supplied. | |
| * @param {Feature} currentFeature The current Feature being processed. | |
| * @param {number} featureIndex The current index of the Feature being processed. | |
| */ | |
| /** | |
| * Reduce features in any GeoJSON object, similar to Array.reduce(). | |
| * | |
| * @name featureReduce | |
| * @param {FeatureCollection|Feature|Geometry} geojson any GeoJSON object | |
| * @param {Function} callback a method that takes (previousValue, currentFeature, featureIndex) | |
| * @param {*} [initialValue] Value to use as the first argument to the first call of the callback. | |
| * @returns {*} The value that results from the reduction. | |
| * @example | |
| * var features = turf.featureCollection([ | |
| * turf.point([26, 37], {"foo": "bar"}), | |
| * turf.point([36, 53], {"hello": "world"}) | |
| * ]); | |
| * | |
| * turf.featureReduce(features, function (previousValue, currentFeature, featureIndex) { | |
| * //=previousValue | |
| * //=currentFeature | |
| * //=featureIndex | |
| * return currentFeature | |
| * }); | |
| */ | |
| function featureReduce(geojson, callback, initialValue) { | |
| var previousValue = initialValue; | |
| featureEach(geojson, function (currentFeature, featureIndex) { | |
| if (featureIndex === 0 && initialValue === undefined) previousValue = currentFeature; | |
| else previousValue = callback(previousValue, currentFeature, featureIndex); | |
| }); | |
| return previousValue; | |
| } | |
| /** | |
| * Get all coordinates from any GeoJSON object. | |
| * | |
| * @name coordAll | |
| * @param {FeatureCollection|Feature|Geometry} geojson any GeoJSON object | |
| * @returns {Array<Array<number>>} coordinate position array | |
| * @example | |
| * var features = turf.featureCollection([ | |
| * turf.point([26, 37], {foo: 'bar'}), | |
| * turf.point([36, 53], {hello: 'world'}) | |
| * ]); | |
| * | |
| * var coords = turf.coordAll(features); | |
| * //= [[26, 37], [36, 53]] | |
| */ | |
| function coordAll(geojson) { | |
| var coords = []; | |
| coordEach(geojson, function (coord) { | |
| coords.push(coord); | |
| }); | |
| return coords; | |
| } | |
| /** | |
| * Callback for geomEach | |
| * | |
| * @callback geomEachCallback | |
| * @param {Geometry} currentGeometry The current Geometry being processed. | |
| * @param {number} featureIndex The current index of the Feature being processed. | |
| * @param {Object} featureProperties The current Feature Properties being processed. | |
| * @param {Array<number>} featureBBox The current Feature BBox being processed. | |
| * @param {number|string} featureId The current Feature Id being processed. | |
| */ | |
| /** | |
| * Iterate over each geometry in any GeoJSON object, similar to Array.forEach() | |
| * | |
| * @name geomEach | |
| * @param {FeatureCollection|Feature|Geometry} geojson any GeoJSON object | |
| * @param {Function} callback a method that takes (currentGeometry, featureIndex, featureProperties, featureBBox, featureId) | |
| * @returns {void} | |
| * @example | |
| * var features = turf.featureCollection([ | |
| * turf.point([26, 37], {foo: 'bar'}), | |
| * turf.point([36, 53], {hello: 'world'}) | |
| * ]); | |
| * | |
| * turf.geomEach(features, function (currentGeometry, featureIndex, featureProperties, featureBBox, featureId) { | |
| * //=currentGeometry | |
| * //=featureIndex | |
| * //=featureProperties | |
| * //=featureBBox | |
| * //=featureId | |
| * }); | |
| */ | |
| function geomEach(geojson, callback) { | |
| var i, j, g, geometry, stopG, | |
| geometryMaybeCollection, | |
| isGeometryCollection, | |
| featureProperties, | |
| featureBBox, | |
| featureId, | |
| featureIndex = 0, | |
| isFeatureCollection = geojson.type === 'FeatureCollection', | |
| isFeature = geojson.type === 'Feature', | |
| stop = isFeatureCollection ? geojson.features.length : 1; | |
| // This logic may look a little weird. The reason why it is that way | |
| // is because it's trying to be fast. GeoJSON supports multiple kinds | |
| // of objects at its root: FeatureCollection, Features, Geometries. | |
| // This function has the responsibility of handling all of them, and that | |
| // means that some of the `for` loops you see below actually just don't apply | |
| // to certain inputs. For instance, if you give this just a | |
| // Point geometry, then both loops are short-circuited and all we do | |
| // is gradually rename the input until it's called 'geometry'. | |
| // | |
| // This also aims to allocate as few resources as possible: just a | |
| // few numbers and booleans, rather than any temporary arrays as would | |
| // be required with the normalization approach. | |
| for (i = 0; i < stop; i++) { | |
| geometryMaybeCollection = (isFeatureCollection ? geojson.features[i].geometry : | |
| (isFeature ? geojson.geometry : geojson)); | |
| featureProperties = (isFeatureCollection ? geojson.features[i].properties : | |
| (isFeature ? geojson.properties : {})); | |
| featureBBox = (isFeatureCollection ? geojson.features[i].bbox : | |
| (isFeature ? geojson.bbox : undefined)); | |
| featureId = (isFeatureCollection ? geojson.features[i].id : | |
| (isFeature ? geojson.id : undefined)); | |
| isGeometryCollection = (geometryMaybeCollection) ? geometryMaybeCollection.type === 'GeometryCollection' : false; | |
| stopG = isGeometryCollection ? geometryMaybeCollection.geometries.length : 1; | |
| for (g = 0; g < stopG; g++) { | |
| geometry = isGeometryCollection ? | |
| geometryMaybeCollection.geometries[g] : geometryMaybeCollection; | |
| // Handle null Geometry | |
| if (geometry === null) { | |
| if (callback(null, featureIndex, featureProperties, featureBBox, featureId) === false) return false; | |
| continue; | |
| } | |
| switch (geometry.type) { | |
| case 'Point': | |
| case 'LineString': | |
| case 'MultiPoint': | |
| case 'Polygon': | |
| case 'MultiLineString': | |
| case 'MultiPolygon': { | |
| if (callback(geometry, featureIndex, featureProperties, featureBBox, featureId) === false) return false; | |
| break; | |
| } | |
| case 'GeometryCollection': { | |
| for (j = 0; j < geometry.geometries.length; j++) { | |
| if (callback(geometry.geometries[j], featureIndex, featureProperties, featureBBox, featureId) === false) return false; | |
| } | |
| break; | |
| } | |
| default: | |
| throw new Error('Unknown Geometry Type'); | |
| } | |
| } | |
| // Only increase `featureIndex` per each feature | |
| featureIndex++; | |
| } | |
| } | |
| /** | |
| * Callback for geomReduce | |
| * | |
| * The first time the callback function is called, the values provided as arguments depend | |
| * on whether the reduce method has an initialValue argument. | |
| * | |
| * If an initialValue is provided to the reduce method: | |
| * - The previousValue argument is initialValue. | |
| * - The currentValue argument is the value of the first element present in the array. | |
| * | |
| * If an initialValue is not provided: | |
| * - The previousValue argument is the value of the first element present in the array. | |
| * - The currentValue argument is the value of the second element present in the array. | |
| * | |
| * @callback geomReduceCallback | |
| * @param {*} previousValue The accumulated value previously returned in the last invocation | |
| * of the callback, or initialValue, if supplied. | |
| * @param {Geometry} currentGeometry The current Geometry being processed. | |
| * @param {number} featureIndex The current index of the Feature being processed. | |
| * @param {Object} featureProperties The current Feature Properties being processed. | |
| * @param {Array<number>} featureBBox The current Feature BBox being processed. | |
| * @param {number|string} featureId The current Feature Id being processed. | |
| */ | |
| /** | |
| * Reduce geometry in any GeoJSON object, similar to Array.reduce(). | |
| * | |
| * @name geomReduce | |
| * @param {FeatureCollection|Feature|Geometry} geojson any GeoJSON object | |
| * @param {Function} callback a method that takes (previousValue, currentGeometry, featureIndex, featureProperties, featureBBox, featureId) | |
| * @param {*} [initialValue] Value to use as the first argument to the first call of the callback. | |
| * @returns {*} The value that results from the reduction. | |
| * @example | |
| * var features = turf.featureCollection([ | |
| * turf.point([26, 37], {foo: 'bar'}), | |
| * turf.point([36, 53], {hello: 'world'}) | |
| * ]); | |
| * | |
| * turf.geomReduce(features, function (previousValue, currentGeometry, featureIndex, featureProperties, featureBBox, featureId) { | |
| * //=previousValue | |
| * //=currentGeometry | |
| * //=featureIndex | |
| * //=featureProperties | |
| * //=featureBBox | |
| * //=featureId | |
| * return currentGeometry | |
| * }); | |
| */ | |
| function geomReduce(geojson, callback, initialValue) { | |
| var previousValue = initialValue; | |
| geomEach(geojson, function (currentGeometry, featureIndex, featureProperties, featureBBox, featureId) { | |
| if (featureIndex === 0 && initialValue === undefined) previousValue = currentGeometry; | |
| else previousValue = callback(previousValue, currentGeometry, featureIndex, featureProperties, featureBBox, featureId); | |
| }); | |
| return previousValue; | |
| } | |
| /** | |
| * Callback for flattenEach | |
| * | |
| * @callback flattenEachCallback | |
| * @param {Feature} currentFeature The current flattened feature being processed. | |
| * @param {number} featureIndex The current index of the Feature being processed. | |
| * @param {number} multiFeatureIndex The current index of the Multi-Feature being processed. | |
| */ | |
| /** | |
| * Iterate over flattened features in any GeoJSON object, similar to | |
| * Array.forEach. | |
| * | |
| * @name flattenEach | |
| * @param {FeatureCollection|Feature|Geometry} geojson any GeoJSON object | |
| * @param {Function} callback a method that takes (currentFeature, featureIndex, multiFeatureIndex) | |
| * @example | |
| * var features = turf.featureCollection([ | |
| * turf.point([26, 37], {foo: 'bar'}), | |
| * turf.multiPoint([[40, 30], [36, 53]], {hello: 'world'}) | |
| * ]); | |
| * | |
| * turf.flattenEach(features, function (currentFeature, featureIndex, multiFeatureIndex) { | |
| * //=currentFeature | |
| * //=featureIndex | |
| * //=multiFeatureIndex | |
| * }); | |
| */ | |
| function flattenEach(geojson, callback) { | |
| geomEach(geojson, function (geometry, featureIndex, properties, bbox, id) { | |
| // Callback for single geometry | |
| var type = (geometry === null) ? null : geometry.type; | |
| switch (type) { | |
| case null: | |
| case 'Point': | |
| case 'LineString': | |
| case 'Polygon': | |
| if (callback(helpers.feature(geometry, properties, {bbox: bbox, id: id}), featureIndex, 0) === false) return false; | |
| return; | |
| } | |
| var geomType; | |
| // Callback for multi-geometry | |
| switch (type) { | |
| case 'MultiPoint': | |
| geomType = 'Point'; | |
| break; | |
| case 'MultiLineString': | |
| geomType = 'LineString'; | |
| break; | |
| case 'MultiPolygon': | |
| geomType = 'Polygon'; | |
| break; | |
| } | |
| for (var multiFeatureIndex = 0; multiFeatureIndex < geometry.coordinates.length; multiFeatureIndex++) { | |
| var coordinate = geometry.coordinates[multiFeatureIndex]; | |
| var geom = { | |
| type: geomType, | |
| coordinates: coordinate | |
| }; | |
| if (callback(helpers.feature(geom, properties), featureIndex, multiFeatureIndex) === false) return false; | |
| } | |
| }); | |
| } | |
| /** | |
| * Callback for flattenReduce | |
| * | |
| * The first time the callback function is called, the values provided as arguments depend | |
| * on whether the reduce method has an initialValue argument. | |
| * | |
| * If an initialValue is provided to the reduce method: | |
| * - The previousValue argument is initialValue. | |
| * - The currentValue argument is the value of the first element present in the array. | |
| * | |
| * If an initialValue is not provided: | |
| * - The previousValue argument is the value of the first element present in the array. | |
| * - The currentValue argument is the value of the second element present in the array. | |
| * | |
| * @callback flattenReduceCallback | |
| * @param {*} previousValue The accumulated value previously returned in the last invocation | |
| * of the callback, or initialValue, if supplied. | |
| * @param {Feature} currentFeature The current Feature being processed. | |
| * @param {number} featureIndex The current index of the Feature being processed. | |
| * @param {number} multiFeatureIndex The current index of the Multi-Feature being processed. | |
| */ | |
| /** | |
| * Reduce flattened features in any GeoJSON object, similar to Array.reduce(). | |
| * | |
| * @name flattenReduce | |
| * @param {FeatureCollection|Feature|Geometry} geojson any GeoJSON object | |
| * @param {Function} callback a method that takes (previousValue, currentFeature, featureIndex, multiFeatureIndex) | |
| * @param {*} [initialValue] Value to use as the first argument to the first call of the callback. | |
| * @returns {*} The value that results from the reduction. | |
| * @example | |
| * var features = turf.featureCollection([ | |
| * turf.point([26, 37], {foo: 'bar'}), | |
| * turf.multiPoint([[40, 30], [36, 53]], {hello: 'world'}) | |
| * ]); | |
| * | |
| * turf.flattenReduce(features, function (previousValue, currentFeature, featureIndex, multiFeatureIndex) { | |
| * //=previousValue | |
| * //=currentFeature | |
| * //=featureIndex | |
| * //=multiFeatureIndex | |
| * return currentFeature | |
| * }); | |
| */ | |
| function flattenReduce(geojson, callback, initialValue) { | |
| var previousValue = initialValue; | |
| flattenEach(geojson, function (currentFeature, featureIndex, multiFeatureIndex) { | |
| if (featureIndex === 0 && multiFeatureIndex === 0 && initialValue === undefined) previousValue = currentFeature; | |
| else previousValue = callback(previousValue, currentFeature, featureIndex, multiFeatureIndex); | |
| }); | |
| return previousValue; | |
| } | |
| /** | |
| * Callback for segmentEach | |
| * | |
| * @callback segmentEachCallback | |
| * @param {Feature<LineString>} currentSegment The current Segment being processed. | |
| * @param {number} featureIndex The current index of the Feature being processed. | |
| * @param {number} multiFeatureIndex The current index of the Multi-Feature being processed. | |
| * @param {number} geometryIndex The current index of the Geometry being processed. | |
| * @param {number} segmentIndex The current index of the Segment being processed. | |
| * @returns {void} | |
| */ | |
| /** | |
| * Iterate over 2-vertex line segment in any GeoJSON object, similar to Array.forEach() | |
| * (Multi)Point geometries do not contain segments therefore they are ignored during this operation. | |
| * | |
| * @param {FeatureCollection|Feature|Geometry} geojson any GeoJSON | |
| * @param {Function} callback a method that takes (currentSegment, featureIndex, multiFeatureIndex, geometryIndex, segmentIndex) | |
| * @returns {void} | |
| * @example | |
| * var polygon = turf.polygon([[[-50, 5], [-40, -10], [-50, -10], [-40, 5], [-50, 5]]]); | |
| * | |
| * // Iterate over GeoJSON by 2-vertex segments | |
| * turf.segmentEach(polygon, function (currentSegment, featureIndex, multiFeatureIndex, geometryIndex, segmentIndex) { | |
| * //=currentSegment | |
| * //=featureIndex | |
| * //=multiFeatureIndex | |
| * //=geometryIndex | |
| * //=segmentIndex | |
| * }); | |
| * | |
| * // Calculate the total number of segments | |
| * var total = 0; | |
| * turf.segmentEach(polygon, function () { | |
| * total++; | |
| * }); | |
| */ | |
| function segmentEach(geojson, callback) { | |
| flattenEach(geojson, function (feature, featureIndex, multiFeatureIndex) { | |
| var segmentIndex = 0; | |
| // Exclude null Geometries | |
| if (!feature.geometry) return; | |
| // (Multi)Point geometries do not contain segments therefore they are ignored during this operation. | |
| var type = feature.geometry.type; | |
| if (type === 'Point' || type === 'MultiPoint') return; | |
| // Generate 2-vertex line segments | |
| var previousCoords; | |
| var previousFeatureIndex = 0; | |
| var previousMultiIndex = 0; | |
| var prevGeomIndex = 0; | |
| if (coordEach(feature, function (currentCoord, coordIndex, featureIndexCoord, multiPartIndexCoord, geometryIndex) { | |
| // Simulating a meta.coordReduce() since `reduce` operations cannot be stopped by returning `false` | |
| if (previousCoords === undefined || featureIndex > previousFeatureIndex || multiPartIndexCoord > previousMultiIndex || geometryIndex > prevGeomIndex) { | |
| previousCoords = currentCoord; | |
| previousFeatureIndex = featureIndex; | |
| previousMultiIndex = multiPartIndexCoord; | |
| prevGeomIndex = geometryIndex; | |
| segmentIndex = 0; | |
| return; | |
| } | |
| var currentSegment = helpers.lineString([previousCoords, currentCoord], feature.properties); | |
| if (callback(currentSegment, featureIndex, multiFeatureIndex, geometryIndex, segmentIndex) === false) return false; | |
| segmentIndex++; | |
| previousCoords = currentCoord; | |
| }) === false) return false; | |
| }); | |
| } | |
| /** | |
| * Callback for segmentReduce | |
| * | |
| * The first time the callback function is called, the values provided as arguments depend | |
| * on whether the reduce method has an initialValue argument. | |
| * | |
| * If an initialValue is provided to the reduce method: | |
| * - The previousValue argument is initialValue. | |
| * - The currentValue argument is the value of the first element present in the array. | |
| * | |
| * If an initialValue is not provided: | |
| * - The previousValue argument is the value of the first element present in the array. | |
| * - The currentValue argument is the value of the second element present in the array. | |
| * | |
| * @callback segmentReduceCallback | |
| * @param {*} previousValue The accumulated value previously returned in the last invocation | |
| * of the callback, or initialValue, if supplied. | |
| * @param {Feature<LineString>} currentSegment The current Segment being processed. | |
| * @param {number} featureIndex The current index of the Feature being processed. | |
| * @param {number} multiFeatureIndex The current index of the Multi-Feature being processed. | |
| * @param {number} geometryIndex The current index of the Geometry being processed. | |
| * @param {number} segmentIndex The current index of the Segment being processed. | |
| */ | |
| /** | |
| * Reduce 2-vertex line segment in any GeoJSON object, similar to Array.reduce() | |
| * (Multi)Point geometries do not contain segments therefore they are ignored during this operation. | |
| * | |
| * @param {FeatureCollection|Feature|Geometry} geojson any GeoJSON | |
| * @param {Function} callback a method that takes (previousValue, currentSegment, currentIndex) | |
| * @param {*} [initialValue] Value to use as the first argument to the first call of the callback. | |
| * @returns {void} | |
| * @example | |
| * var polygon = turf.polygon([[[-50, 5], [-40, -10], [-50, -10], [-40, 5], [-50, 5]]]); | |
| * | |
| * // Iterate over GeoJSON by 2-vertex segments | |
| * turf.segmentReduce(polygon, function (previousSegment, currentSegment, featureIndex, multiFeatureIndex, geometryIndex, segmentIndex) { | |
| * //= previousSegment | |
| * //= currentSegment | |
| * //= featureIndex | |
| * //= multiFeatureIndex | |
| * //= geometryIndex | |
| * //= segmentInex | |
| * return currentSegment | |
| * }); | |
| * | |
| * // Calculate the total number of segments | |
| * var initialValue = 0 | |
| * var total = turf.segmentReduce(polygon, function (previousValue) { | |
| * previousValue++; | |
| * return previousValue; | |
| * }, initialValue); | |
| */ | |
| function segmentReduce(geojson, callback, initialValue) { | |
| var previousValue = initialValue; | |
| var started = false; | |
| segmentEach(geojson, function (currentSegment, featureIndex, multiFeatureIndex, geometryIndex, segmentIndex) { | |
| if (started === false && initialValue === undefined) previousValue = currentSegment; | |
| else previousValue = callback(previousValue, currentSegment, featureIndex, multiFeatureIndex, geometryIndex, segmentIndex); | |
| started = true; | |
| }); | |
| return previousValue; | |
| } | |
| /** | |
| * Callback for lineEach | |
| * | |
| * @callback lineEachCallback | |
| * @param {Feature<LineString>} currentLine The current LineString|LinearRing being processed | |
| * @param {number} featureIndex The current index of the Feature being processed | |
| * @param {number} multiFeatureIndex The current index of the Multi-Feature being processed | |
| * @param {number} geometryIndex The current index of the Geometry being processed | |
| */ | |
| /** | |
| * Iterate over line or ring coordinates in LineString, Polygon, MultiLineString, MultiPolygon Features or Geometries, | |
| * similar to Array.forEach. | |
| * | |
| * @name lineEach | |
| * @param {Geometry|Feature<LineString|Polygon|MultiLineString|MultiPolygon>} geojson object | |
| * @param {Function} callback a method that takes (currentLine, featureIndex, multiFeatureIndex, geometryIndex) | |
| * @example | |
| * var multiLine = turf.multiLineString([ | |
| * [[26, 37], [35, 45]], | |
| * [[36, 53], [38, 50], [41, 55]] | |
| * ]); | |
| * | |
| * turf.lineEach(multiLine, function (currentLine, featureIndex, multiFeatureIndex, geometryIndex) { | |
| * //=currentLine | |
| * //=featureIndex | |
| * //=multiFeatureIndex | |
| * //=geometryIndex | |
| * }); | |
| */ | |
| function lineEach(geojson, callback) { | |
| // validation | |
| if (!geojson) throw new Error('geojson is required'); | |
| flattenEach(geojson, function (feature, featureIndex, multiFeatureIndex) { | |
| if (feature.geometry === null) return; | |
| var type = feature.geometry.type; | |
| var coords = feature.geometry.coordinates; | |
| switch (type) { | |
| case 'LineString': | |
| if (callback(feature, featureIndex, multiFeatureIndex, 0, 0) === false) return false; | |
| break; | |
| case 'Polygon': | |
| for (var geometryIndex = 0; geometryIndex < coords.length; geometryIndex++) { | |
| if (callback(helpers.lineString(coords[geometryIndex], feature.properties), featureIndex, multiFeatureIndex, geometryIndex) === false) return false; | |
| } | |
| break; | |
| } | |
| }); | |
| } | |
| /** | |
| * Callback for lineReduce | |
| * | |
| * The first time the callback function is called, the values provided as arguments depend | |
| * on whether the reduce method has an initialValue argument. | |
| * | |
| * If an initialValue is provided to the reduce method: | |
| * - The previousValue argument is initialValue. | |
| * - The currentValue argument is the value of the first element present in the array. | |
| * | |
| * If an initialValue is not provided: | |
| * - The previousValue argument is the value of the first element present in the array. | |
| * - The currentValue argument is the value of the second element present in the array. | |
| * | |
| * @callback lineReduceCallback | |
| * @param {*} previousValue The accumulated value previously returned in the last invocation | |
| * of the callback, or initialValue, if supplied. | |
| * @param {Feature<LineString>} currentLine The current LineString|LinearRing being processed. | |
| * @param {number} featureIndex The current index of the Feature being processed | |
| * @param {number} multiFeatureIndex The current index of the Multi-Feature being processed | |
| * @param {number} geometryIndex The current index of the Geometry being processed | |
| */ | |
| /** | |
| * Reduce features in any GeoJSON object, similar to Array.reduce(). | |
| * | |
| * @name lineReduce | |
| * @param {Geometry|Feature<LineString|Polygon|MultiLineString|MultiPolygon>} geojson object | |
| * @param {Function} callback a method that takes (previousValue, currentLine, featureIndex, multiFeatureIndex, geometryIndex) | |
| * @param {*} [initialValue] Value to use as the first argument to the first call of the callback. | |
| * @returns {*} The value that results from the reduction. | |
| * @example | |
| * var multiPoly = turf.multiPolygon([ | |
| * turf.polygon([[[12,48],[2,41],[24,38],[12,48]], [[9,44],[13,41],[13,45],[9,44]]]), | |
| * turf.polygon([[[5, 5], [0, 0], [2, 2], [4, 4], [5, 5]]]) | |
| * ]); | |
| * | |
| * turf.lineReduce(multiPoly, function (previousValue, currentLine, featureIndex, multiFeatureIndex, geometryIndex) { | |
| * //=previousValue | |
| * //=currentLine | |
| * //=featureIndex | |
| * //=multiFeatureIndex | |
| * //=geometryIndex | |
| * return currentLine | |
| * }); | |
| */ | |
| function lineReduce(geojson, callback, initialValue) { | |
| var previousValue = initialValue; | |
| lineEach(geojson, function (currentLine, featureIndex, multiFeatureIndex, geometryIndex) { | |
| if (featureIndex === 0 && initialValue === undefined) previousValue = currentLine; | |
| else previousValue = callback(previousValue, currentLine, featureIndex, multiFeatureIndex, geometryIndex); | |
| }); | |
| return previousValue; | |
| } | |
| /** | |
| * Finds a particular 2-vertex LineString Segment from a GeoJSON using `@turf/meta` indexes. | |
| * | |
| * Negative indexes are permitted. | |
| * Point & MultiPoint will always return null. | |
| * | |
| * @param {FeatureCollection|Feature|Geometry} geojson Any GeoJSON Feature or Geometry | |
| * @param {Object} [options={}] Optional parameters | |
| * @param {number} [options.featureIndex=0] Feature Index | |
| * @param {number} [options.multiFeatureIndex=0] Multi-Feature Index | |
| * @param {number} [options.geometryIndex=0] Geometry Index | |
| * @param {number} [options.segmentIndex=0] Segment Index | |
| * @param {Object} [options.properties={}] Translate Properties to output LineString | |
| * @param {BBox} [options.bbox={}] Translate BBox to output LineString | |
| * @param {number|string} [options.id={}] Translate Id to output LineString | |
| * @returns {Feature<LineString>} 2-vertex GeoJSON Feature LineString | |
| * @example | |
| * var multiLine = turf.multiLineString([ | |
| * [[10, 10], [50, 30], [30, 40]], | |
| * [[-10, -10], [-50, -30], [-30, -40]] | |
| * ]); | |
| * | |
| * // First Segment (defaults are 0) | |
| * turf.findSegment(multiLine); | |
| * // => Feature<LineString<[[10, 10], [50, 30]]>> | |
| * | |
| * // First Segment of 2nd Multi Feature | |
| * turf.findSegment(multiLine, {multiFeatureIndex: 1}); | |
| * // => Feature<LineString<[[-10, -10], [-50, -30]]>> | |
| * | |
| * // Last Segment of Last Multi Feature | |
| * turf.findSegment(multiLine, {multiFeatureIndex: -1, segmentIndex: -1}); | |
| * // => Feature<LineString<[[-50, -30], [-30, -40]]>> | |
| */ | |
| function findSegment(geojson, options) { | |
| // Optional Parameters | |
| options = options || {}; | |
| if (!helpers.isObject(options)) throw new Error('options is invalid'); | |
| var featureIndex = options.featureIndex || 0; | |
| var multiFeatureIndex = options.multiFeatureIndex || 0; | |
| var geometryIndex = options.geometryIndex || 0; | |
| var segmentIndex = options.segmentIndex || 0; | |
| // Find FeatureIndex | |
| var properties = options.properties; | |
| var geometry; | |
| switch (geojson.type) { | |
| case 'FeatureCollection': | |
| if (featureIndex < 0) featureIndex = geojson.features.length + featureIndex; | |
| properties = properties || geojson.features[featureIndex].properties; | |
| geometry = geojson.features[featureIndex].geometry; | |
| break; | |
| case 'Feature': | |
| properties = properties || geojson.properties; | |
| geometry = geojson.geometry; | |
| break; | |
| case 'Point': | |
| case 'MultiPoint': | |
| return null; | |
| case 'LineString': | |
| case 'Polygon': | |
| case 'MultiLineString': | |
| case 'MultiPolygon': | |
| geometry = geojson; | |
| break; | |
| default: | |
| throw new Error('geojson is invalid'); | |
| } | |
| // Find SegmentIndex | |
| if (geometry === null) return null; | |
| var coords = geometry.coordinates; | |
| switch (geometry.type) { | |
| case 'Point': | |
| case 'MultiPoint': | |
| return null; | |
| case 'LineString': | |
| if (segmentIndex < 0) segmentIndex = coords.length + segmentIndex - 1; | |
| return helpers.lineString([coords[segmentIndex], coords[segmentIndex + 1]], properties, options); | |
| case 'Polygon': | |
| if (geometryIndex < 0) geometryIndex = coords.length + geometryIndex; | |
| if (segmentIndex < 0) segmentIndex = coords[geometryIndex].length + segmentIndex - 1; | |
| return helpers.lineString([coords[geometryIndex][segmentIndex], coords[geometryIndex][segmentIndex + 1]], properties, options); | |
| case 'MultiLineString': | |
| if (multiFeatureIndex < 0) multiFeatureIndex = coords.length + multiFeatureIndex; | |
| if (segmentIndex < 0) segmentIndex = coords[multiFeatureIndex].length + segmentIndex - 1; | |
| return helpers.lineString([coords[multiFeatureIndex][segmentIndex], coords[multiFeatureIndex][segmentIndex + 1]], properties, options); | |
| case 'MultiPolygon': | |
| if (multiFeatureIndex < 0) multiFeatureIndex = coords.length + multiFeatureIndex; | |
| if (geometryIndex < 0) geometryIndex = coords[multiFeatureIndex].length + geometryIndex; | |
| if (segmentIndex < 0) segmentIndex = coords[multiFeatureIndex][geometryIndex].length - segmentIndex - 1; | |
| return helpers.lineString([coords[multiFeatureIndex][geometryIndex][segmentIndex], coords[multiFeatureIndex][geometryIndex][segmentIndex + 1]], properties, options); | |
| } | |
| throw new Error('geojson is invalid'); | |
| } | |
| /** | |
| * Finds a particular Point from a GeoJSON using `@turf/meta` indexes. | |
| * | |
| * Negative indexes are permitted. | |
| * | |
| * @param {FeatureCollection|Feature|Geometry} geojson Any GeoJSON Feature or Geometry | |
| * @param {Object} [options={}] Optional parameters | |
| * @param {number} [options.featureIndex=0] Feature Index | |
| * @param {number} [options.multiFeatureIndex=0] Multi-Feature Index | |
| * @param {number} [options.geometryIndex=0] Geometry Index | |
| * @param {number} [options.coordIndex=0] Coord Index | |
| * @param {Object} [options.properties={}] Translate Properties to output Point | |
| * @param {BBox} [options.bbox={}] Translate BBox to output Point | |
| * @param {number|string} [options.id={}] Translate Id to output Point | |
| * @returns {Feature<Point>} 2-vertex GeoJSON Feature Point | |
| * @example | |
| * var multiLine = turf.multiLineString([ | |
| * [[10, 10], [50, 30], [30, 40]], | |
| * [[-10, -10], [-50, -30], [-30, -40]] | |
| * ]); | |
| * | |
| * // First Segment (defaults are 0) | |
| * turf.findPoint(multiLine); | |
| * // => Feature<Point<[10, 10]>> | |
| * | |
| * // First Segment of the 2nd Multi-Feature | |
| * turf.findPoint(multiLine, {multiFeatureIndex: 1}); | |
| * // => Feature<Point<[-10, -10]>> | |
| * | |
| * // Last Segment of last Multi-Feature | |
| * turf.findPoint(multiLine, {multiFeatureIndex: -1, coordIndex: -1}); | |
| * // => Feature<Point<[-30, -40]>> | |
| */ | |
| function findPoint(geojson, options) { | |
| // Optional Parameters | |
| options = options || {}; | |
| if (!helpers.isObject(options)) throw new Error('options is invalid'); | |
| var featureIndex = options.featureIndex || 0; | |
| var multiFeatureIndex = options.multiFeatureIndex || 0; | |
| var geometryIndex = options.geometryIndex || 0; | |
| var coordIndex = options.coordIndex || 0; | |
| // Find FeatureIndex | |
| var properties = options.properties; | |
| var geometry; | |
| switch (geojson.type) { | |
| case 'FeatureCollection': | |
| if (featureIndex < 0) featureIndex = geojson.features.length + featureIndex; | |
| properties = properties || geojson.features[featureIndex].properties; | |
| geometry = geojson.features[featureIndex].geometry; | |
| break; | |
| case 'Feature': | |
| properties = properties || geojson.properties; | |
| geometry = geojson.geometry; | |
| break; | |
| case 'Point': | |
| case 'MultiPoint': | |
| return null; | |
| case 'LineString': | |
| case 'Polygon': | |
| case 'MultiLineString': | |
| case 'MultiPolygon': | |
| geometry = geojson; | |
| break; | |
| default: | |
| throw new Error('geojson is invalid'); | |
| } | |
| // Find Coord Index | |
| if (geometry === null) return null; | |
| var coords = geometry.coordinates; | |
| switch (geometry.type) { | |
| case 'Point': | |
| return helpers.point(coords, properties, options); | |
| case 'MultiPoint': | |
| if (multiFeatureIndex < 0) multiFeatureIndex = coords.length + multiFeatureIndex; | |
| return helpers.point(coords[multiFeatureIndex], properties, options); | |
| case 'LineString': | |
| if (coordIndex < 0) coordIndex = coords.length + coordIndex; | |
| return helpers.point(coords[coordIndex], properties, options); | |
| case 'Polygon': | |
| if (geometryIndex < 0) geometryIndex = coords.length + geometryIndex; | |
| if (coordIndex < 0) coordIndex = coords[geometryIndex].length + coordIndex; | |
| return helpers.point(coords[geometryIndex][coordIndex], properties, options); | |
| case 'MultiLineString': | |
| if (multiFeatureIndex < 0) multiFeatureIndex = coords.length + multiFeatureIndex; | |
| if (coordIndex < 0) coordIndex = coords[multiFeatureIndex].length + coordIndex; | |
| return helpers.point(coords[multiFeatureIndex][coordIndex], properties, options); | |
| case 'MultiPolygon': | |
| if (multiFeatureIndex < 0) multiFeatureIndex = coords.length + multiFeatureIndex; | |
| if (geometryIndex < 0) geometryIndex = coords[multiFeatureIndex].length + geometryIndex; | |
| if (coordIndex < 0) coordIndex = coords[multiFeatureIndex][geometryIndex].length - coordIndex; | |
| return helpers.point(coords[multiFeatureIndex][geometryIndex][coordIndex], properties, options); | |
| } | |
| throw new Error('geojson is invalid'); | |
| } | |
| exports.coordEach = coordEach; | |
| exports.coordReduce = coordReduce; | |
| exports.propEach = propEach; | |
| exports.propReduce = propReduce; | |
| exports.featureEach = featureEach; | |
| exports.featureReduce = featureReduce; | |
| exports.coordAll = coordAll; | |
| exports.geomEach = geomEach; | |
| exports.geomReduce = geomReduce; | |
| exports.flattenEach = flattenEach; | |
| exports.flattenReduce = flattenReduce; | |
| exports.segmentEach = segmentEach; | |
| exports.segmentReduce = segmentReduce; | |
| exports.lineEach = lineEach; | |
| exports.lineReduce = lineReduce; | |
| exports.findSegment = findSegment; | |
| exports.findPoint = findPoint; | |
| },{"@turf/helpers":3}],6:[function(require,module,exports){ | |
| /** | |
| * martinez v0.4.3 | |
| * Martinez polygon clipping algorithm, does boolean operation on polygons (multipolygons, polygons with holes etc): intersection, union, difference, xor | |
| * | |
| * @author Alex Milevski <info@w8r.name> | |
| * @license MIT | |
| * @preserve | |
| */ | |
| (function (global, factory) { | |
| typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : | |
| typeof define === 'function' && define.amd ? define(['exports'], factory) : | |
| (factory((global.martinez = {}))); | |
| }(this, (function (exports) { 'use strict'; | |
| function DEFAULT_COMPARE (a, b) { return a > b ? 1 : a < b ? -1 : 0; } | |
| var SplayTree = function SplayTree(compare, noDuplicates) { | |
| if ( compare === void 0 ) compare = DEFAULT_COMPARE; | |
| if ( noDuplicates === void 0 ) noDuplicates = false; | |
| this._compare = compare; | |
| this._root = null; | |
| this._size = 0; | |
| this._noDuplicates = !!noDuplicates; | |
| }; | |
| var prototypeAccessors = { size: { configurable: true } }; | |
| SplayTree.prototype.rotateLeft = function rotateLeft (x) { | |
| var y = x.right; | |
| if (y) { | |
| x.right = y.left; | |
| if (y.left) { y.left.parent = x; } | |
| y.parent = x.parent; | |
| } | |
| if (!x.parent) { this._root = y; } | |
| else if (x === x.parent.left) { x.parent.left = y; } | |
| else { x.parent.right = y; } | |
| if (y) { y.left = x; } | |
| x.parent = y; | |
| }; | |
| SplayTree.prototype.rotateRight = function rotateRight (x) { | |
| var y = x.left; | |
| if (y) { | |
| x.left = y.right; | |
| if (y.right) { y.right.parent = x; } | |
| y.parent = x.parent; | |
| } | |
| if (!x.parent) { this._root = y; } | |
| else if(x === x.parent.left) { x.parent.left = y; } | |
| else { x.parent.right = y; } | |
| if (y) { y.right = x; } | |
| x.parent = y; | |
| }; | |
| SplayTree.prototype._splay = function _splay (x) { | |
| var this$1 = this; | |
| while (x.parent) { | |
| var p = x.parent; | |
| if (!p.parent) { | |
| if (p.left === x) { this$1.rotateRight(p); } | |
| else { this$1.rotateLeft(p); } | |
| } else if (p.left === x && p.parent.left === p) { | |
| this$1.rotateRight(p.parent); | |
| this$1.rotateRight(p); | |
| } else if (p.right === x && p.parent.right === p) { | |
| this$1.rotateLeft(p.parent); | |
| this$1.rotateLeft(p); | |
| } else if (p.left === x && p.parent.right === p) { | |
| this$1.rotateRight(p); | |
| this$1.rotateLeft(p); | |
| } else { | |
| this$1.rotateLeft(p); | |
| this$1.rotateRight(p); | |
| } | |
| } | |
| }; | |
| SplayTree.prototype.splay = function splay (x) { | |
| var this$1 = this; | |
| var p, gp, ggp, l, r; | |
| while (x.parent) { | |
| p = x.parent; | |
| gp = p.parent; | |
| if (gp && gp.parent) { | |
| ggp = gp.parent; | |
| if (ggp.left === gp) { ggp.left= x; } | |
| else { ggp.right = x; } | |
| x.parent = ggp; | |
| } else { | |
| x.parent = null; | |
| this$1._root = x; | |
| } | |
| l = x.left; r = x.right; | |
| if (x === p.left) { // left | |
| if (gp) { | |
| if (gp.left === p) { | |
| /* zig-zig */ | |
| if (p.right) { | |
| gp.left = p.right; | |
| gp.left.parent = gp; | |
| } else { gp.left = null; } | |
| p.right = gp; | |
| gp.parent = p; | |
| } else { | |
| /* zig-zag */ | |
| if (l) { | |
| gp.right = l; | |
| l.parent = gp; | |
| } else { gp.right = null; } | |
| x.left = gp; | |
| gp.parent = x; | |
| } | |
| } | |
| if (r) { | |
| p.left = r; | |
| r.parent = p; | |
| } else { p.left = null; } | |
| x.right= p; | |
| p.parent = x; | |
| } else { // right | |
| if (gp) { | |
| if (gp.right === p) { | |
| /* zig-zig */ | |
| if (p.left) { | |
| gp.right = p.left; | |
| gp.right.parent = gp; | |
| } else { gp.right = null; } | |
| p.left = gp; | |
| gp.parent = p; | |
| } else { | |
| /* zig-zag */ | |
| if (r) { | |
| gp.left = r; | |
| r.parent = gp; | |
| } else { gp.left = null; } | |
| x.right = gp; | |
| gp.parent = x; | |
| } | |
| } | |
| if (l) { | |
| p.right = l; | |
| l.parent = p; | |
| } else { p.right = null; } | |
| x.left = p; | |
| p.parent = x; | |
| } | |
| } | |
| }; | |
| SplayTree.prototype.replace = function replace (u, v) { | |
| if (!u.parent) { this._root = v; } | |
| else if (u === u.parent.left) { u.parent.left = v; } | |
| else { u.parent.right = v; } | |
| if (v) { v.parent = u.parent; } | |
| }; | |
| SplayTree.prototype.minNode = function minNode (u) { | |
| if ( u === void 0 ) u = this._root; | |
| if (u) { while (u.left) { u = u.left; } } | |
| return u; | |
| }; | |
| SplayTree.prototype.maxNode = function maxNode (u) { | |
| if ( u === void 0 ) u = this._root; | |
| if (u) { while (u.right) { u = u.right; } } | |
| return u; | |
| }; | |
| SplayTree.prototype.insert = function insert (key, data) { | |
| var z = this._root; | |
| var p = null; | |
| var comp = this._compare; | |
| var cmp; | |
| if (this._noDuplicates) { | |
| while (z) { | |
| p = z; | |
| cmp = comp(z.key, key); | |
| if (cmp === 0) { return; } | |
| else if (comp(z.key, key) < 0) { z = z.right; } | |
| else { z = z.left; } | |
| } | |
| } else { | |
| while (z) { | |
| p = z; | |
| if (comp(z.key, key) < 0) { z = z.right; } | |
| else { z = z.left; } | |
| } | |
| } | |
| z = { key: key, data: data, left: null, right: null, parent: p }; | |
| if (!p) { this._root = z; } | |
| else if (comp(p.key, z.key) < 0) { p.right = z; } | |
| else { p.left= z; } | |
| this.splay(z); | |
| this._size++; | |
| return z; | |
| }; | |
| SplayTree.prototype.find = function find (key) { | |
| var z = this._root; | |
| var comp = this._compare; | |
| while (z) { | |
| var cmp = comp(z.key, key); | |
| if (cmp < 0) { z = z.right; } | |
| else if (cmp > 0) { z = z.left; } | |
| else { return z; } | |
| } | |
| return null; | |
| }; | |
| /** | |
| * Whether the tree contains a node with the given key | |
| * @param{Key} key | |
| * @return {boolean} true/false | |
| */ | |
| SplayTree.prototype.contains = function contains (key) { | |
| var node = this._root; | |
| var comparator = this._compare; | |
| while (node){ | |
| var cmp = comparator(key, node.key); | |
| if (cmp === 0) { return true; } | |
| else if (cmp < 0) { node = node.left; } | |
| else { node = node.right; } | |
| } | |
| return false; | |
| }; | |
| SplayTree.prototype.remove = function remove (key) { | |
| var z = this.find(key); | |
| if (!z) { return false; } | |
| this.splay(z); | |
| if (!z.left) { this.replace(z, z.right); } | |
| else if (!z.right) { this.replace(z, z.left); } | |
| else { | |
| var y = this.minNode(z.right); | |
| if (y.parent !== z) { | |
| this.replace(y, y.right); | |
| y.right = z.right; | |
| y.right.parent = y; | |
| } | |
| this.replace(z, y); | |
| y.left = z.left; | |
| y.left.parent = y; | |
| } | |
| this._size--; | |
| return true; | |
| }; | |
| SplayTree.prototype.removeNode = function removeNode (z) { | |
| if (!z) { return false; } | |
| this.splay(z); | |
| if (!z.left) { this.replace(z, z.right); } | |
| else if (!z.right) { this.replace(z, z.left); } | |
| else { | |
| var y = this.minNode(z.right); | |
| if (y.parent !== z) { | |
| this.replace(y, y.right); | |
| y.right = z.right; | |
| y.right.parent = y; | |
| } | |
| this.replace(z, y); | |
| y.left = z.left; | |
| y.left.parent = y; | |
| } | |
| this._size--; | |
| return true; | |
| }; | |
| SplayTree.prototype.erase = function erase (key) { | |
| var z = this.find(key); | |
| if (!z) { return; } | |
| this.splay(z); | |
| var s = z.left; | |
| var t = z.right; | |
| var sMax = null; | |
| if (s) { | |
| s.parent = null; | |
| sMax = this.maxNode(s); | |
| this.splay(sMax); | |
| this._root = sMax; | |
| } | |
| if (t) { | |
| if (s) { sMax.right = t; } | |
| else { this._root = t; } | |
| t.parent = sMax; | |
| } | |
| this._size--; | |
| }; | |
| /** | |
| * Removes and returns the node with smallest key | |
| * @return {?Node} | |
| */ | |
| SplayTree.prototype.pop = function pop () { | |
| var node = this._root, returnValue = null; | |
| if (node) { | |
| while (node.left) { node = node.left; } | |
| returnValue = { key: node.key, data: node.data }; | |
| this.remove(node.key); | |
| } | |
| return returnValue; | |
| }; | |
| /* eslint-disable class-methods-use-this */ | |
| /** | |
| * Successor node | |
| * @param{Node} node | |
| * @return {?Node} | |
| */ | |
| SplayTree.prototype.next = function next (node) { | |
| var successor = node; | |
| if (successor) { | |
| if (successor.right) { | |
| successor = successor.right; | |
| while (successor && successor.left) { successor = successor.left; } | |
| } else { | |
| successor = node.parent; | |
| while (successor && successor.right === node) { | |
| node = successor; successor = successor.parent; | |
| } | |
| } | |
| } | |
| return successor; | |
| }; | |
| /** | |
| * Predecessor node | |
| * @param{Node} node | |
| * @return {?Node} | |
| */ | |
| SplayTree.prototype.prev = function prev (node) { | |
| var predecessor = node; | |
| if (predecessor) { | |
| if (predecessor.left) { | |
| predecessor = predecessor.left; | |
| while (predecessor && predecessor.right) { predecessor = predecessor.right; } | |
| } else { | |
| predecessor = node.parent; | |
| while (predecessor && predecessor.left === node) { | |
| node = predecessor; | |
| predecessor = predecessor.parent; | |
| } | |
| } | |
| } | |
| return predecessor; | |
| }; | |
| /* eslint-enable class-methods-use-this */ | |
| /** | |
| * @param{forEachCallback} callback | |
| * @return {SplayTree} | |
| */ | |
| SplayTree.prototype.forEach = function forEach (callback) { | |
| var current = this._root; | |
| var s = [], done = false, i = 0; | |
| while (!done) { | |
| // Reach the left most Node of the current Node | |
| if (current) { | |
| // Place pointer to a tree node on the stack | |
| // before traversing the node's left subtree | |
| s.push(current); | |
| current = current.left; | |
| } else { | |
| // BackTrack from the empty subtree and visit the Node | |
| // at the top of the stack; however, if the stack is | |
| // empty you are done | |
| if (s.length > 0) { | |
| current = s.pop(); | |
| callback(current, i++); | |
| // We have visited the node and its left | |
| // subtree. Now, it's right subtree's turn | |
| current = current.right; | |
| } else { done = true; } | |
| } | |
| } | |
| return this; | |
| }; | |
| /** | |
| * Walk key range from `low` to `high`. Stops if `fn` returns a value. | |
| * @param{Key} low | |
| * @param{Key} high | |
| * @param{Function} fn | |
| * @param{*?} ctx | |
| * @return {SplayTree} | |
| */ | |
| SplayTree.prototype.range = function range (low, high, fn, ctx) { | |
| var this$1 = this; | |
| var Q = []; | |
| var compare = this._compare; | |
| var node = this._root, cmp; | |
| while (Q.length !== 0 || node) { | |
| if (node) { | |
| Q.push(node); | |
| node = node.left; | |
| } else { | |
| node = Q.pop(); | |
| cmp = compare(node.key, high); | |
| if (cmp > 0) { | |
| break; | |
| } else if (compare(node.key, low) >= 0) { | |
| if (fn.call(ctx, node)) { return this$1; } // stop if smth is returned | |
| } | |
| node = node.right; | |
| } | |
| } | |
| return this; | |
| }; | |
| /** | |
| * Returns all keys in order | |
| * @return {Array<Key>} | |
| */ | |
| SplayTree.prototype.keys = function keys () { | |
| var current = this._root; | |
| var s = [], r = [], done = false; | |
| while (!done) { | |
| if (current) { | |
| s.push(current); | |
| current = current.left; | |
| } else { | |
| if (s.length > 0) { | |
| current = s.pop(); | |
| r.push(current.key); | |
| current = current.right; | |
| } else { done = true; } | |
| } | |
| } | |
| return r; | |
| }; | |
| /** | |
| * Returns `data` fields of all nodes in order. | |
| * @return {Array<Value>} | |
| */ | |
| SplayTree.prototype.values = function values () { | |
| var current = this._root; | |
| var s = [], r = [], done = false; | |
| while (!done) { | |
| if (current) { | |
| s.push(current); | |
| current = current.left; | |
| } else { | |
| if (s.length > 0) { | |
| current = s.pop(); | |
| r.push(current.data); | |
| current = current.right; | |
| } else { done = true; } | |
| } | |
| } | |
| return r; | |
| }; | |
| /** | |
| * Returns node at given index | |
| * @param{number} index | |
| * @return {?Node} | |
| */ | |
| SplayTree.prototype.at = function at (index) { | |
| // removed after a consideration, more misleading than useful | |
| // index = index % this.size; | |
| // if (index < 0) index = this.size - index; | |
| var current = this._root; | |
| var s = [], done = false, i = 0; | |
| while (!done) { | |
| if (current) { | |
| s.push(current); | |
| current = current.left; | |
| } else { | |
| if (s.length > 0) { | |
| current = s.pop(); | |
| if (i === index) { return current; } | |
| i++; | |
| current = current.right; | |
| } else { done = true; } | |
| } | |
| } | |
| return null; | |
| }; | |
| /** | |
| * Bulk-load items. Both array have to be same size | |
| * @param{Array<Key>} keys | |
| * @param{Array<Value>}[values] | |
| * @param{Boolean} [presort=false] Pre-sort keys and values, using | |
| * tree's comparator. Sorting is done | |
| * in-place | |
| * @return {AVLTree} | |
| */ | |
| SplayTree.prototype.load = function load (keys, values, presort) { | |
| if ( keys === void 0 ) keys = []; | |
| if ( values === void 0 ) values = []; | |
| if ( presort === void 0 ) presort = false; | |
| if (this._size !== 0) { throw new Error('bulk-load: tree is not empty'); } | |
| var size = keys.length; | |
| if (presort) { sort(keys, values, 0, size - 1, this._compare); } | |
| this._root = loadRecursive(null, keys, values, 0, size); | |
| this._size = size; | |
| return this; | |
| }; | |
| SplayTree.prototype.min = function min () { | |
| var node = this.minNode(this._root); | |
| if (node) { return node.key; } | |
| else { return null; } | |
| }; | |
| SplayTree.prototype.max = function max () { | |
| var node = this.maxNode(this._root); | |
| if (node) { return node.key; } | |
| else { return null; } | |
| }; | |
| SplayTree.prototype.isEmpty = function isEmpty () { return this._root === null; }; | |
| prototypeAccessors.size.get = function () { return this._size; }; | |
| /** | |
| * Create a tree and load it with items | |
| * @param{Array<Key>} keys | |
| * @param{Array<Value>?} [values] | |
| * @param{Function?} [comparator] | |
| * @param{Boolean?} [presort=false] Pre-sort keys and values, using | |
| * tree's comparator. Sorting is done | |
| * in-place | |
| * @param{Boolean?} [noDuplicates=false] Allow duplicates | |
| * @return {SplayTree} | |
| */ | |
| SplayTree.createTree = function createTree (keys, values, comparator, presort, noDuplicates) { | |
| return new SplayTree(comparator, noDuplicates).load(keys, values, presort); | |
| }; | |
| Object.defineProperties( SplayTree.prototype, prototypeAccessors ); | |
| function loadRecursive (parent, keys, values, start, end) { | |
| var size = end - start; | |
| if (size > 0) { | |
| var middle = start + Math.floor(size / 2); | |
| var key = keys[middle]; | |
| var data = values[middle]; | |
| var node = { key: key, data: data, parent: parent }; | |
| node.left = loadRecursive(node, keys, values, start, middle); | |
| node.right = loadRecursive(node, keys, values, middle + 1, end); | |
| return node; | |
| } | |
| return null; | |
| } | |
| function sort(keys, values, left, right, compare) { | |
| if (left >= right) { return; } | |
| var pivot = keys[(left + right) >> 1]; | |
| var i = left - 1; | |
| var j = right + 1; | |
| while (true) { | |
| do { i++; } while (compare(keys[i], pivot) < 0); | |
| do { j--; } while (compare(keys[j], pivot) > 0); | |
| if (i >= j) { break; } | |
| var tmp = keys[i]; | |
| keys[i] = keys[j]; | |
| keys[j] = tmp; | |
| tmp = values[i]; | |
| values[i] = values[j]; | |
| values[j] = tmp; | |
| } | |
| sort(keys, values, left, j, compare); | |
| sort(keys, values, j + 1, right, compare); | |
| } | |
| var NORMAL = 0; | |
| var NON_CONTRIBUTING = 1; | |
| var SAME_TRANSITION = 2; | |
| var DIFFERENT_TRANSITION = 3; | |
| var INTERSECTION = 0; | |
| var UNION = 1; | |
| var DIFFERENCE = 2; | |
| var XOR = 3; | |
| /** | |
| * @param {SweepEvent} event | |
| * @param {SweepEvent} prev | |
| * @param {Operation} operation | |
| */ | |
| function computeFields (event, prev, operation) { | |
| // compute inOut and otherInOut fields | |
| if (prev === null) { | |
| event.inOut = false; | |
| event.otherInOut = true; | |
| // previous line segment in sweepline belongs to the same polygon | |
| } else { | |
| if (event.isSubject === prev.isSubject) { | |
| event.inOut = !prev.inOut; | |
| event.otherInOut = prev.otherInOut; | |
| // previous line segment in sweepline belongs to the clipping polygon | |
| } else { | |
| event.inOut = !prev.otherInOut; | |
| event.otherInOut = prev.isVertical() ? !prev.inOut : prev.inOut; | |
| } | |
| // compute prevInResult field | |
| if (prev) { | |
| event.prevInResult = (!inResult(prev, operation) || prev.isVertical()) | |
| ? prev.prevInResult : prev; | |
| } | |
| } | |
| // check if the line segment belongs to the Boolean operation | |
| event.inResult = inResult(event, operation); | |
| } | |
| /* eslint-disable indent */ | |
| function inResult(event, operation) { | |
| switch (event.type) { | |
| case NORMAL: | |
| switch (operation) { | |
| case INTERSECTION: | |
| return !event.otherInOut; | |
| case UNION: | |
| return event.otherInOut; | |
| case DIFFERENCE: | |
| // return (event.isSubject && !event.otherInOut) || | |
| // (!event.isSubject && event.otherInOut); | |
| return (event.isSubject && event.otherInOut) || | |
| (!event.isSubject && !event.otherInOut); | |
| case XOR: | |
| return true; | |
| } | |
| break; | |
| case SAME_TRANSITION: | |
| return operation === INTERSECTION || operation === UNION; | |
| case DIFFERENT_TRANSITION: | |
| return operation === DIFFERENCE; | |
| case NON_CONTRIBUTING: | |
| return false; | |
| } | |
| return false; | |
| } | |
| /* eslint-enable indent */ | |
| var SweepEvent = function SweepEvent (point, left, otherEvent, isSubject, edgeType) { | |
| /** | |
| * Is left endpoint? | |
| * @type {Boolean} | |
| */ | |
| this.left = left; | |
| /** | |
| * @type {Array.<Number>} | |
| */ | |
| this.point = point; | |
| /** | |
| * Other edge reference | |
| * @type {SweepEvent} | |
| */ | |
| this.otherEvent = otherEvent; | |
| /** | |
| * Belongs to source or clipping polygon | |
| * @type {Boolean} | |
| */ | |
| this.isSubject = isSubject; | |
| /** | |
| * Edge contribution type | |
| * @type {Number} | |
| */ | |
| this.type = edgeType || NORMAL; | |
| /** | |
| * In-out transition for the sweepline crossing polygon | |
| * @type {Boolean} | |
| */ | |
| this.inOut = false; | |
| /** | |
| * @type {Boolean} | |
| */ | |
| this.otherInOut = false; | |
| /** | |
| * Previous event in result? | |
| * @type {SweepEvent} | |
| */ | |
| this.prevInResult = null; | |
| /** | |
| * Does event belong to result? | |
| * @type {Boolean} | |
| */ | |
| this.inResult = false; | |
| // connection step | |
| /** | |
| * @type {Boolean} | |
| */ | |
| this.resultInOut = false; | |
| this.isExteriorRing = true; | |
| }; | |
| /** | |
| * @param{Array.<Number>}p | |
| * @return {Boolean} | |
| */ | |
| SweepEvent.prototype.isBelow = function isBelow (p) { | |
| var p0 = this.point, p1 = this.otherEvent.point; | |
| return this.left | |
| ? (p0[0] - p[0]) * (p1[1] - p[1]) - (p1[0] - p[0]) * (p0[1] - p[1]) > 0 | |
| // signedArea(this.point, this.otherEvent.point, p) > 0 : | |
| : (p1[0] - p[0]) * (p0[1] - p[1]) - (p0[0] - p[0]) * (p1[1] - p[1]) > 0; | |
| //signedArea(this.otherEvent.point, this.point, p) > 0; | |
| }; | |
| /** | |
| * @param{Array.<Number>}p | |
| * @return {Boolean} | |
| */ | |
| SweepEvent.prototype.isAbove = function isAbove (p) { | |
| return !this.isBelow(p); | |
| }; | |
| /** | |
| * @return {Boolean} | |
| */ | |
| SweepEvent.prototype.isVertical = function isVertical () { | |
| return this.point[0] === this.otherEvent.point[0]; | |
| }; | |
| SweepEvent.prototype.clone = function clone () { | |
| var copy = new SweepEvent( | |
| this.point, this.left, this.otherEvent, this.isSubject, this.type); | |
| copy.inResult = this.inResult; | |
| copy.prevInResult = this.prevInResult; | |
| copy.isExteriorRing = this.isExteriorRing; | |
| copy.inOut = this.inOut; | |
| copy.otherInOut = this.otherInOut; | |
| return copy; | |
| }; | |
| function equals(p1, p2) { | |
| if (p1[0] === p2[0]) { | |
| if (p1[1] === p2[1]) { | |
| return true; | |
| } else { | |
| return false; | |
| } | |
| } | |
| return false; | |
| } | |
| // const EPSILON = 1e-9; | |
| // const abs = Math.abs; | |
| // TODO https://github.com/w8r/martinez/issues/6#issuecomment-262847164 | |
| // Precision problem. | |
| // | |
| // module.exports = function equals(p1, p2) { | |
| // return abs(p1[0] - p2[0]) <= EPSILON && abs(p1[1] - p2[1]) <= EPSILON; | |
| // }; | |
| /** | |
| * Signed area of the triangle (p0, p1, p2) | |
| * @param {Array.<Number>} p0 | |
| * @param {Array.<Number>} p1 | |
| * @param {Array.<Number>} p2 | |
| * @return {Number} | |
| */ | |
| function signedArea(p0, p1, p2) { | |
| return (p0[0] - p2[0]) * (p1[1] - p2[1]) - (p1[0] - p2[0]) * (p0[1] - p2[1]); | |
| } | |
| /** | |
| * @param {SweepEvent} e1 | |
| * @param {SweepEvent} e2 | |
| * @return {Number} | |
| */ | |
| function compareEvents(e1, e2) { | |
| var p1 = e1.point; | |
| var p2 = e2.point; | |
| // Different x-coordinate | |
| if (p1[0] > p2[0]) { return 1; } | |
| if (p1[0] < p2[0]) { return -1; } | |
| // Different points, but same x-coordinate | |
| // Event with lower y-coordinate is processed first | |
| if (p1[1] !== p2[1]) { return p1[1] > p2[1] ? 1 : -1; } | |
| return specialCases(e1, e2, p1, p2); | |
| } | |
| /* eslint-disable no-unused-vars */ | |
| function specialCases(e1, e2, p1, p2) { | |
| // Same coordinates, but one is a left endpoint and the other is | |
| // a right endpoint. The right endpoint is processed first | |
| if (e1.left !== e2.left) | |
| { return e1.left ? 1 : -1; } | |
| // const p2 = e1.otherEvent.point, p3 = e2.otherEvent.point; | |
| // const sa = (p1[0] - p3[0]) * (p2[1] - p3[1]) - (p2[0] - p3[0]) * (p1[1] - p3[1]) | |
| // Same coordinates, both events | |
| // are left endpoints or right endpoints. | |
| // not collinear | |
| if (signedArea(p1, e1.otherEvent.point, e2.otherEvent.point) !== 0) { | |
| // the event associate to the bottom segment is processed first | |
| return (!e1.isBelow(e2.otherEvent.point)) ? 1 : -1; | |
| } | |
| return (!e1.isSubject && e2.isSubject) ? 1 : -1; | |
| } | |
| /* eslint-enable no-unused-vars */ | |
| /** | |
| * @param {SweepEvent} se | |
| * @param {Array.<Number>} p | |
| * @param {Queue} queue | |
| * @return {Queue} | |
| */ | |
| function divideSegment(se, p, queue) { | |
| var r = new SweepEvent(p, false, se, se.isSubject); | |
| var l = new SweepEvent(p, true, se.otherEvent, se.isSubject); | |
| /* eslint-disable no-console */ | |
| if (equals(se.point, se.otherEvent.point)) { | |
| console.warn('what is that, a collapsed segment?', se); | |
| } | |
| /* eslint-enable no-console */ | |
| r.contourId = l.contourId = se.contourId; | |
| // avoid a rounding error. The left event would be processed after the right event | |
| if (compareEvents(l, se.otherEvent) > 0) { | |
| se.otherEvent.left = true; | |
| l.left = false; | |
| } | |
| // avoid a rounding error. The left event would be processed after the right event | |
| // if (compareEvents(se, r) > 0) {} | |
| se.otherEvent.otherEvent = l; | |
| se.otherEvent = r; | |
| queue.push(l); | |
| queue.push(r); | |
| return queue; | |
| } | |
| //const EPS = 1e-9; | |
| /** | |
| * Finds the magnitude of the cross product of two vectors (if we pretend | |
| * they're in three dimensions) | |
| * | |
| * @param {Object} a First vector | |
| * @param {Object} b Second vector | |
| * @private | |
| * @returns {Number} The magnitude of the cross product | |
| */ | |
| function crossProduct(a, b) { | |
| return (a[0] * b[1]) - (a[1] * b[0]); | |
| } | |
| /** | |
| * Finds the dot product of two vectors. | |
| * | |
| * @param {Object} a First vector | |
| * @param {Object} b Second vector | |
| * @private | |
| * @returns {Number} The dot product | |
| */ | |
| function dotProduct(a, b) { | |
| return (a[0] * b[0]) + (a[1] * b[1]); | |
| } | |
| /** | |
| * Finds the intersection (if any) between two line segments a and b, given the | |
| * line segments' end points a1, a2 and b1, b2. | |
| * | |
| * This algorithm is based on Schneider and Eberly. | |
| * http://www.cimec.org.ar/~ncalvo/Schneider_Eberly.pdf | |
| * Page 244. | |
| * | |
| * @param {Array.<Number>} a1 point of first line | |
| * @param {Array.<Number>} a2 point of first line | |
| * @param {Array.<Number>} b1 point of second line | |
| * @param {Array.<Number>} b2 point of second line | |
| * @param {Boolean=} noEndpointTouch whether to skip single touchpoints | |
| * (meaning connected segments) as | |
| * intersections | |
| * @returns {Array.<Array.<Number>>|Null} If the lines intersect, the point of | |
| * intersection. If they overlap, the two end points of the overlapping segment. | |
| * Otherwise, null. | |
| */ | |
| function intersection (a1, a2, b1, b2, noEndpointTouch) { | |
| // The algorithm expects our lines in the form P + sd, where P is a point, | |
| // s is on the interval [0, 1], and d is a vector. | |
| // We are passed two points. P can be the first point of each pair. The | |
| // vector, then, could be thought of as the distance (in x and y components) | |
| // from the first point to the second point. | |
| // So first, let's make our vectors: | |
| var va = [a2[0] - a1[0], a2[1] - a1[1]]; | |
| var vb = [b2[0] - b1[0], b2[1] - b1[1]]; | |
| // We also define a function to convert back to regular point form: | |
| /* eslint-disable arrow-body-style */ | |
| function toPoint(p, s, d) { | |
| return [ | |
| p[0] + s * d[0], | |
| p[1] + s * d[1] | |
| ]; | |
| } | |
| /* eslint-enable arrow-body-style */ | |
| // The rest is pretty much a straight port of the algorithm. | |
| var e = [b1[0] - a1[0], b1[1] - a1[1]]; | |
| var kross = crossProduct(va, vb); | |
| var sqrKross = kross * kross; | |
| var sqrLenA = dotProduct(va, va); | |
| //const sqrLenB = dotProduct(vb, vb); | |
| // Check for line intersection. This works because of the properties of the | |
| // cross product -- specifically, two vectors are parallel if and only if the | |
| // cross product is the 0 vector. The full calculation involves relative error | |
| // to account for possible very small line segments. See Schneider & Eberly | |
| // for details. | |
| if (sqrKross > 0/* EPS * sqrLenB * sqLenA */) { | |
| // If they're not parallel, then (because these are line segments) they | |
| // still might not actually intersect. This code checks that the | |
| // intersection point of the lines is actually on both line segments. | |
| var s = crossProduct(e, vb) / kross; | |
| if (s < 0 || s > 1) { | |
| // not on line segment a | |
| return null; | |
| } | |
| var t = crossProduct(e, va) / kross; | |
| if (t < 0 || t > 1) { | |
| // not on line segment b | |
| return null; | |
| } | |
| if (s === 0 || s === 1) { | |
| // on an endpoint of line segment a | |
| return noEndpointTouch ? null : [toPoint(a1, s, va)]; | |
| } | |
| if (t === 0 || t === 1) { | |
| // on an endpoint of line segment b | |
| return noEndpointTouch ? null : [toPoint(b1, t, vb)]; | |
| } | |
| return [toPoint(a1, s, va)]; | |
| } | |
| // If we've reached this point, then the lines are either parallel or the | |
| // same, but the segments could overlap partially or fully, or not at all. | |
| // So we need to find the overlap, if any. To do that, we can use e, which is | |
| // the (vector) difference between the two initial points. If this is parallel | |
| // with the line itself, then the two lines are the same line, and there will | |
| // be overlap. | |
| //const sqrLenE = dotProduct(e, e); | |
| kross = crossProduct(e, va); | |
| sqrKross = kross * kross; | |
| if (sqrKross > 0 /* EPS * sqLenB * sqLenE */) { | |
| // Lines are just parallel, not the same. No overlap. | |
| return null; | |
| } | |
| var sa = dotProduct(va, e) / sqrLenA; | |
| var sb = sa + dotProduct(va, vb) / sqrLenA; | |
| var smin = Math.min(sa, sb); | |
| var smax = Math.max(sa, sb); | |
| // this is, essentially, the FindIntersection acting on floats from | |
| // Schneider & Eberly, just inlined into this function. | |
| if (smin <= 1 && smax >= 0) { | |
| // overlap on an end point | |
| if (smin === 1) { | |
| return noEndpointTouch ? null : [toPoint(a1, smin > 0 ? smin : 0, va)]; | |
| } | |
| if (smax === 0) { | |
| return noEndpointTouch ? null : [toPoint(a1, smax < 1 ? smax : 1, va)]; | |
| } | |
| if (noEndpointTouch && smin === 0 && smax === 1) { return null; } | |
| // There's overlap on a segment -- two points of intersection. Return both. | |
| return [ | |
| toPoint(a1, smin > 0 ? smin : 0, va), | |
| toPoint(a1, smax < 1 ? smax : 1, va) | |
| ]; | |
| } | |
| return null; | |
| } | |
| /** | |
| * @param {SweepEvent} se1 | |
| * @param {SweepEvent} se2 | |
| * @param {Queue} queue | |
| * @return {Number} | |
| */ | |
| function possibleIntersection (se1, se2, queue) { | |
| // that disallows self-intersecting polygons, | |
| // did cost us half a day, so I'll leave it | |
| // out of respect | |
| // if (se1.isSubject === se2.isSubject) return; | |
| var inter = intersection( | |
| se1.point, se1.otherEvent.point, | |
| se2.point, se2.otherEvent.point | |
| ); | |
| var nintersections = inter ? inter.length : 0; | |
| if (nintersections === 0) { return 0; } // no intersection | |
| // the line segments intersect at an endpoint of both line segments | |
| if ((nintersections === 1) && | |
| (equals(se1.point, se2.point) || | |
| equals(se1.otherEvent.point, se2.otherEvent.point))) { | |
| return 0; | |
| } | |
| if (nintersections === 2 && se1.isSubject === se2.isSubject) { | |
| // if(se1.contourId === se2.contourId){ | |
| // console.warn('Edges of the same polygon overlap', | |
| // se1.point, se1.otherEvent.point, se2.point, se2.otherEvent.point); | |
| // } | |
| //throw new Error('Edges of the same polygon overlap'); | |
| return 0; | |
| } | |
| // The line segments associated to se1 and se2 intersect | |
| if (nintersections === 1) { | |
| // if the intersection point is not an endpoint of se1 | |
| if (!equals(se1.point, inter[0]) && !equals(se1.otherEvent.point, inter[0])) { | |
| divideSegment(se1, inter[0], queue); | |
| } | |
| // if the intersection point is not an endpoint of se2 | |
| if (!equals(se2.point, inter[0]) && !equals(se2.otherEvent.point, inter[0])) { | |
| divideSegment(se2, inter[0], queue); | |
| } | |
| return 1; | |
| } | |
| // The line segments associated to se1 and se2 overlap | |
| var events = []; | |
| var leftCoincide = false; | |
| var rightCoincide = false; | |
| if (equals(se1.point, se2.point)) { | |
| leftCoincide = true; // linked | |
| } else if (compareEvents(se1, se2) === 1) { | |
| events.push(se2, se1); | |
| } else { | |
| events.push(se1, se2); | |
| } | |
| if (equals(se1.otherEvent.point, se2.otherEvent.point)) { | |
| rightCoincide = true; | |
| } else if (compareEvents(se1.otherEvent, se2.otherEvent) === 1) { | |
| events.push(se2.otherEvent, se1.otherEvent); | |
| } else { | |
| events.push(se1.otherEvent, se2.otherEvent); | |
| } | |
| if ((leftCoincide && rightCoincide) || leftCoincide) { | |
| // both line segments are equal or share the left endpoint | |
| se2.type = NON_CONTRIBUTING; | |
| se1.type = (se2.inOut === se1.inOut) | |
| ? SAME_TRANSITION : DIFFERENT_TRANSITION; | |
| if (leftCoincide && !rightCoincide) { | |
| // honestly no idea, but changing events selection from [2, 1] | |
| // to [0, 1] fixes the overlapping self-intersecting polygons issue | |
| divideSegment(events[1].otherEvent, events[0].point, queue); | |
| } | |
| return 2; | |
| } | |
| // the line segments share the right endpoint | |
| if (rightCoincide) { | |
| divideSegment(events[0], events[1].point, queue); | |
| return 3; | |
| } | |
| // no line segment includes totally the other one | |
| if (events[0] !== events[3].otherEvent) { | |
| divideSegment(events[0], events[1].point, queue); | |
| divideSegment(events[1], events[2].point, queue); | |
| return 3; | |
| } | |
| // one line segment includes the other one | |
| divideSegment(events[0], events[1].point, queue); | |
| divideSegment(events[3].otherEvent, events[2].point, queue); | |
| return 3; | |
| } | |
| /** | |
| * @param {SweepEvent} le1 | |
| * @param {SweepEvent} le2 | |
| * @return {Number} | |
| */ | |
| function compareSegments(le1, le2) { | |
| if (le1 === le2) { return 0; } | |
| // Segments are not collinear | |
| if (signedArea(le1.point, le1.otherEvent.point, le2.point) !== 0 || | |
| signedArea(le1.point, le1.otherEvent.point, le2.otherEvent.point) !== 0) { | |
| // If they share their left endpoint use the right endpoint to sort | |
| if (equals(le1.point, le2.point)) { return le1.isBelow(le2.otherEvent.point) ? -1 : 1; } | |
| // Different left endpoint: use the left endpoint to sort | |
| if (le1.point[0] === le2.point[0]) { return le1.point[1] < le2.point[1] ? -1 : 1; } | |
| // has the line segment associated to e1 been inserted | |
| // into S after the line segment associated to e2 ? | |
| if (compareEvents(le1, le2) === 1) { return le2.isAbove(le1.point) ? -1 : 1; } | |
| // The line segment associated to e2 has been inserted | |
| // into S after the line segment associated to e1 | |
| return le1.isBelow(le2.point) ? -1 : 1; | |
| } | |
| if (le1.isSubject === le2.isSubject) { // same polygon | |
| var p1 = le1.point, p2 = le2.point; | |
| if (p1[0] === p2[0] && p1[1] === p2[1]/*equals(le1.point, le2.point)*/) { | |
| p1 = le1.otherEvent.point; p2 = le2.otherEvent.point; | |
| if (p1[0] === p2[0] && p1[1] === p2[1]) { return 0; } | |
| else { return le1.contourId > le2.contourId ? 1 : -1; } | |
| } | |
| } else { // Segments are collinear, but belong to separate polygons | |
| return le1.isSubject ? -1 : 1; | |
| } | |
| return compareEvents(le1, le2) === 1 ? 1 : -1; | |
| } | |
| function subdivide(eventQueue, subject, clipping, sbbox, cbbox, operation) { | |
| var sweepLine = new SplayTree(compareSegments); | |
| var sortedEvents = []; | |
| var rightbound = Math.min(sbbox[2], cbbox[2]); | |
| var prev, next, begin; | |
| while (eventQueue.length !== 0) { | |
| var event = eventQueue.pop(); | |
| sortedEvents.push(event); | |
| // optimization by bboxes for intersection and difference goes here | |
| if ((operation === INTERSECTION && event.point[0] > rightbound) || | |
| (operation === DIFFERENCE && event.point[0] > sbbox[2])) { | |
| break; | |
| } | |
| if (event.left) { | |
| next = prev = sweepLine.insert(event); | |
| begin = sweepLine.minNode(); | |
| if (prev !== begin) { prev = sweepLine.prev(prev); } | |
| else { prev = null; } | |
| next = sweepLine.next(next); | |
| var prevEvent = prev ? prev.key : null; | |
| var prevprevEvent = (void 0); | |
| computeFields(event, prevEvent, operation); | |
| if (next) { | |
| if (possibleIntersection(event, next.key, eventQueue) === 2) { | |
| computeFields(event, prevEvent, operation); | |
| computeFields(event, next.key, operation); | |
| } | |
| } | |
| if (prev) { | |
| if (possibleIntersection(prev.key, event, eventQueue) === 2) { | |
| var prevprev = prev; | |
| if (prevprev !== begin) { prevprev = sweepLine.prev(prevprev); } | |
| else { prevprev = null; } | |
| prevprevEvent = prevprev ? prevprev.key : null; | |
| computeFields(prevEvent, prevprevEvent, operation); | |
| computeFields(event, prevEvent, operation); | |
| } | |
| } | |
| } else { | |
| event = event.otherEvent; | |
| next = prev = sweepLine.find(event); | |
| if (prev && next) { | |
| if (prev !== begin) { prev = sweepLine.prev(prev); } | |
| else { prev = null; } | |
| next = sweepLine.next(next); | |
| sweepLine.remove(event); | |
| if (next && prev) { | |
| possibleIntersection(prev.key, next.key, eventQueue); | |
| } | |
| } | |
| } | |
| } | |
| return sortedEvents; | |
| } | |
| /** | |
| * @param {Array.<SweepEvent>} sortedEvents | |
| * @return {Array.<SweepEvent>} | |
| */ | |
| function orderEvents(sortedEvents) { | |
| var event, i, len, tmp; | |
| var resultEvents = []; | |
| for (i = 0, len = sortedEvents.length; i < len; i++) { | |
| event = sortedEvents[i]; | |
| if ((event.left && event.inResult) || | |
| (!event.left && event.otherEvent.inResult)) { | |
| resultEvents.push(event); | |
| } | |
| } | |
| // Due to overlapping edges the resultEvents array can be not wholly sorted | |
| var sorted = false; | |
| while (!sorted) { | |
| sorted = true; | |
| for (i = 0, len = resultEvents.length; i < len; i++) { | |
| if ((i + 1) < len && | |
| compareEvents(resultEvents[i], resultEvents[i + 1]) === 1) { | |
| tmp = resultEvents[i]; | |
| resultEvents[i] = resultEvents[i + 1]; | |
| resultEvents[i + 1] = tmp; | |
| sorted = false; | |
| } | |
| } | |
| } | |
| for (i = 0, len = resultEvents.length; i < len; i++) { | |
| event = resultEvents[i]; | |
| event.pos = i; | |
| } | |
| // imagine, the right event is found in the beginning of the queue, | |
| // when his left counterpart is not marked yet | |
| for (i = 0, len = resultEvents.length; i < len; i++) { | |
| event = resultEvents[i]; | |
| if (!event.left) { | |
| tmp = event.pos; | |
| event.pos = event.otherEvent.pos; | |
| event.otherEvent.pos = tmp; | |
| } | |
| } | |
| return resultEvents; | |
| } | |
| /** | |
| * @param {Number} pos | |
| * @param {Array.<SweepEvent>} resultEvents | |
| * @param {Object>} processed | |
| * @return {Number} | |
| */ | |
| function nextPos(pos, resultEvents, processed, origIndex) { | |
| var newPos = pos + 1; | |
| var length = resultEvents.length; | |
| if (newPos > length - 1) { return pos - 1; } | |
| var p = resultEvents[pos].point; | |
| var p1 = resultEvents[newPos].point; | |
| // while in range and not the current one by value | |
| while (newPos < length && p1[0] === p[0] && p1[1] === p[1]) { | |
| if (!processed[newPos]) { | |
| return newPos; | |
| } else { | |
| newPos++; | |
| } | |
| p1 = resultEvents[newPos].point; | |
| } | |
| newPos = pos - 1; | |
| while (processed[newPos] && newPos >= origIndex) { | |
| newPos--; | |
| } | |
| return newPos; | |
| } | |
| /** | |
| * @param {Array.<SweepEvent>} sortedEvents | |
| * @return {Array.<*>} polygons | |
| */ | |
| function connectEdges(sortedEvents, operation) { | |
| var i, len; | |
| var resultEvents = orderEvents(sortedEvents); | |
| // "false"-filled array | |
| var processed = {}; | |
| var result = []; | |
| var event; | |
| for (i = 0, len = resultEvents.length; i < len; i++) { | |
| if (processed[i]) { continue; } | |
| var contour = [[]]; | |
| if (!resultEvents[i].isExteriorRing) { | |
| if (operation === DIFFERENCE && !resultEvents[i].isSubject && result.length === 0) { | |
| result.push(contour); | |
| } else if (result.length === 0) { | |
| result.push([[contour]]); | |
| } else { | |
| result[result.length - 1].push(contour[0]); | |
| } | |
| } else if (operation === DIFFERENCE && !resultEvents[i].isSubject && result.length > 1) { | |
| result[result.length - 1].push(contour[0]); | |
| } else { | |
| result.push(contour); | |
| } | |
| var ringId = result.length - 1; | |
| var pos = i; | |
| var initial = resultEvents[i].point; | |
| contour[0].push(initial); | |
| while (pos >= i) { | |
| event = resultEvents[pos]; | |
| processed[pos] = true; | |
| if (event.left) { | |
| event.resultInOut = false; | |
| event.contourId = ringId; | |
| } else { | |
| event.otherEvent.resultInOut = true; | |
| event.otherEvent.contourId = ringId; | |
| } | |
| pos = event.pos; | |
| processed[pos] = true; | |
| contour[0].push(resultEvents[pos].point); | |
| pos = nextPos(pos, resultEvents, processed, i); | |
| } | |
| pos = pos === -1 ? i : pos; | |
| event = resultEvents[pos]; | |
| processed[pos] = processed[event.pos] = true; | |
| event.otherEvent.resultInOut = true; | |
| event.otherEvent.contourId = ringId; | |
| } | |
| // Handle if the result is a polygon (eg not multipoly) | |
| // Commented it again, let's see what do we mean by that | |
| // if (result.length === 1) result = result[0]; | |
| return result; | |
| } | |
| var tinyqueue = TinyQueue; | |
| var default_1 = TinyQueue; | |
| function TinyQueue(data, compare) { | |
| var this$1 = this; | |
| if (!(this instanceof TinyQueue)) { return new TinyQueue(data, compare); } | |
| this.data = data || []; | |
| this.length = this.data.length; | |
| this.compare = compare || defaultCompare; | |
| if (this.length > 0) { | |
| for (var i = (this.length >> 1) - 1; i >= 0; i--) { this$1._down(i); } | |
| } | |
| } | |
| function defaultCompare(a, b) { | |
| return a < b ? -1 : a > b ? 1 : 0; | |
| } | |
| TinyQueue.prototype = { | |
| push: function (item) { | |
| this.data.push(item); | |
| this.length++; | |
| this._up(this.length - 1); | |
| }, | |
| pop: function () { | |
| if (this.length === 0) { return undefined; } | |
| var top = this.data[0]; | |
| this.length--; | |
| if (this.length > 0) { | |
| this.data[0] = this.data[this.length]; | |
| this._down(0); | |
| } | |
| this.data.pop(); | |
| return top; | |
| }, | |
| peek: function () { | |
| return this.data[0]; | |
| }, | |
| _up: function (pos) { | |
| var data = this.data; | |
| var compare = this.compare; | |
| var item = data[pos]; | |
| while (pos > 0) { | |
| var parent = (pos - 1) >> 1; | |
| var current = data[parent]; | |
| if (compare(item, current) >= 0) { break; } | |
| data[pos] = current; | |
| pos = parent; | |
| } | |
| data[pos] = item; | |
| }, | |
| _down: function (pos) { | |
| var this$1 = this; | |
| var data = this.data; | |
| var compare = this.compare; | |
| var halfLength = this.length >> 1; | |
| var item = data[pos]; | |
| while (pos < halfLength) { | |
| var left = (pos << 1) + 1; | |
| var right = left + 1; | |
| var best = data[left]; | |
| if (right < this$1.length && compare(data[right], best) < 0) { | |
| left = right; | |
| best = data[right]; | |
| } | |
| if (compare(best, item) >= 0) { break; } | |
| data[pos] = best; | |
| pos = left; | |
| } | |
| data[pos] = item; | |
| } | |
| }; | |
| tinyqueue.default = default_1; | |
| var max = Math.max; | |
| var min = Math.min; | |
| var contourId = 0; | |
| function processPolygon(contourOrHole, isSubject, depth, Q, bbox, isExteriorRing) { | |
| var i, len, s1, s2, e1, e2; | |
| for (i = 0, len = contourOrHole.length - 1; i < len; i++) { | |
| s1 = contourOrHole[i]; | |
| s2 = contourOrHole[i + 1]; | |
| e1 = new SweepEvent(s1, false, undefined, isSubject); | |
| e2 = new SweepEvent(s2, false, e1, isSubject); | |
| e1.otherEvent = e2; | |
| if (s1[0] === s2[0] && s1[1] === s2[1]) { | |
| continue; // skip collapsed edges, or it breaks | |
| } | |
| e1.contourId = e2.contourId = depth; | |
| if (!isExteriorRing) { | |
| e1.isExteriorRing = false; | |
| e2.isExteriorRing = false; | |
| } | |
| if (compareEvents(e1, e2) > 0) { | |
| e2.left = true; | |
| } else { | |
| e1.left = true; | |
| } | |
| var x = s1[0], y = s1[1]; | |
| bbox[0] = min(bbox[0], x); | |
| bbox[1] = min(bbox[1], y); | |
| bbox[2] = max(bbox[2], x); | |
| bbox[3] = max(bbox[3], y); | |
| // Pushing it so the queue is sorted from left to right, | |
| // with object on the left having the highest priority. | |
| Q.push(e1); | |
| Q.push(e2); | |
| } | |
| } | |
| function fillQueue(subject, clipping, sbbox, cbbox, operation) { | |
| var eventQueue = new tinyqueue(null, compareEvents); | |
| var polygonSet, isExteriorRing, i, ii, j, jj; //, k, kk; | |
| for (i = 0, ii = subject.length; i < ii; i++) { | |
| polygonSet = subject[i]; | |
| for (j = 0, jj = polygonSet.length; j < jj; j++) { | |
| isExteriorRing = j === 0; | |
| if (isExteriorRing) { contourId++; } | |
| processPolygon(polygonSet[j], true, contourId, eventQueue, sbbox, isExteriorRing); | |
| } | |
| } | |
| for (i = 0, ii = clipping.length; i < ii; i++) { | |
| polygonSet = clipping[i]; | |
| for (j = 0, jj = polygonSet.length; j < jj; j++) { | |
| isExteriorRing = j === 0; | |
| if (operation === DIFFERENCE) { isExteriorRing = false; } | |
| if (isExteriorRing) { contourId++; } | |
| processPolygon(polygonSet[j], false, contourId, eventQueue, cbbox, isExteriorRing); | |
| } | |
| } | |
| return eventQueue; | |
| } | |
| var EMPTY = []; | |
| function trivialOperation(subject, clipping, operation) { | |
| var result = null; | |
| if (subject.length * clipping.length === 0) { | |
| if (operation === INTERSECTION) { | |
| result = EMPTY; | |
| } else if (operation === DIFFERENCE) { | |
| result = subject; | |
| } else if (operation === UNION || | |
| operation === XOR) { | |
| result = (subject.length === 0) ? clipping : subject; | |
| } | |
| } | |
| return result; | |
| } | |
| function compareBBoxes(subject, clipping, sbbox, cbbox, operation) { | |
| var result = null; | |
| if (sbbox[0] > cbbox[2] || | |
| cbbox[0] > sbbox[2] || | |
| sbbox[1] > cbbox[3] || | |
| cbbox[1] > sbbox[3]) { | |
| if (operation === INTERSECTION) { | |
| result = EMPTY; | |
| } else if (operation === DIFFERENCE) { | |
| result = subject; | |
| } else if (operation === UNION || | |
| operation === XOR) { | |
| result = subject.concat(clipping); | |
| } | |
| } | |
| return result; | |
| } | |
| function boolean(subject, clipping, operation) { | |
| if (typeof subject[0][0][0] === 'number') { | |
| subject = [subject]; | |
| } | |
| if (typeof clipping[0][0][0] === 'number') { | |
| clipping = [clipping]; | |
| } | |
| var trivial = trivialOperation(subject, clipping, operation); | |
| if (trivial) { | |
| return trivial === EMPTY ? null : trivial; | |
| } | |
| var sbbox = [Infinity, Infinity, -Infinity, -Infinity]; | |
| var cbbox = [Infinity, Infinity, -Infinity, -Infinity]; | |
| //console.time('fill queue'); | |
| var eventQueue = fillQueue(subject, clipping, sbbox, cbbox, operation); | |
| //console.timeEnd('fill queue'); | |
| trivial = compareBBoxes(subject, clipping, sbbox, cbbox, operation); | |
| if (trivial) { | |
| return trivial === EMPTY ? null : trivial; | |
| } | |
| //console.time('subdivide edges'); | |
| var sortedEvents = subdivide(eventQueue, subject, clipping, sbbox, cbbox, operation); | |
| //console.timeEnd('subdivide edges'); | |
| //console.time('connect vertices'); | |
| var result = connectEdges(sortedEvents, operation); | |
| //console.timeEnd('connect vertices'); | |
| return result; | |
| } | |
| function union (subject, clipping) { | |
| return boolean(subject, clipping, UNION); | |
| } | |
| function diff (subject, clipping) { | |
| return boolean(subject, clipping, DIFFERENCE); | |
| } | |
| function xor (subject, clipping){ | |
| return boolean(subject, clipping, XOR); | |
| } | |
| function intersection$1 (subject, clipping) { | |
| return boolean(subject, clipping, INTERSECTION); | |
| } | |
| /** | |
| * @enum {Number} | |
| */ | |
| var operations = { UNION: UNION, DIFFERENCE: DIFFERENCE, INTERSECTION: INTERSECTION, XOR: XOR }; | |
| exports.union = union; | |
| exports.diff = diff; | |
| exports.xor = xor; | |
| exports.intersection = intersection$1; | |
| exports.operations = operations; | |
| Object.defineProperty(exports, '__esModule', { value: true }); | |
| }))); | |
| },{}],7:[function(require,module,exports){ | |
| module.exports = { | |
| area: require('@turf/area'), | |
| difference: require('@turf/difference') | |
| }; | |
| },{"@turf/area":1,"@turf/difference":2}]},{},[7])(7) | |
| }); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment