import clone from '@turf/clone'; import center from '@turf/center'; import centroid from '@turf/centroid'; import turfBBox from '@turf/bbox'; import rhumbBearing from '@turf/rhumb-bearing'; import rhumbDistance from '@turf/rhumb-distance'; import rhumbDestination from '@turf/rhumb-destination'; import { featureEach, coordEach } from '@turf/meta'; import { isObject, point } from '@turf/helpers'; import { getType, getCoords, getCoord } from '@turf/invariant'; /** * Scale a GeoJSON from a given point by a factor of scaling (ex: factor=2 would make the GeoJSON 200% larger). * If a FeatureCollection is provided, the origin point will be calculated based on each individual Feature. * * @name transformScale * @param {GeoJSON} geojson GeoJSON to be scaled * @param {number} factor of scaling, positive or negative values greater than 0 * @param {Object} [options={}] Optional parameters * @param {string|Coord} [options.origin='centroid'] Point from which the scaling will occur (string options: sw/se/nw/ne/center/centroid) * @param {boolean} [options.mutate=false] allows GeoJSON input to be mutated (significant performance increase if true) * @returns {GeoJSON} scaled GeoJSON * @example * var poly = turf.polygon([[[0,29],[3.5,29],[2.5,32],[0,29]]]); * var scaledPoly = turf.transformScale(poly, 3); * * //addToMap * var addToMap = [poly, scaledPoly]; * scaledPoly.properties = {stroke: '#F00', 'stroke-width': 4}; */ function transformScale(geojson, factor, options) { // Optional parameters options = options || {}; if (!isObject(options)) throw new Error("options is invalid"); var origin = options.origin; var mutate = options.mutate; // Input validation if (!geojson) throw new Error("geojson required"); if (typeof factor !== "number" || factor === 0) throw new Error("invalid factor"); var originIsPoint = Array.isArray(origin) || typeof origin === "object"; // Clone geojson to avoid side effects if (mutate !== true) geojson = clone(geojson); // Scale each Feature separately if (geojson.type === "FeatureCollection" && !originIsPoint) { featureEach(geojson, function (feature, index) { geojson.features[index] = scale(feature, factor, origin); }); return geojson; } // Scale Feature/Geometry return scale(geojson, factor, origin); } /** * Scale Feature/Geometry * * @private * @param {Feature|Geometry} feature GeoJSON Feature/Geometry * @param {number} factor of scaling, positive or negative values greater than 0 * @param {string|Coord} [origin="centroid"] Point from which the scaling will occur (string options: sw/se/nw/ne/center/centroid) * @returns {Feature|Geometry} scaled GeoJSON Feature/Geometry */ function scale(feature, factor, origin) { // Default params var isPoint = getType(feature) === "Point"; origin = defineOrigin(feature, origin); // Shortcut no-scaling if (factor === 1 || isPoint) return feature; // Scale each coordinate coordEach(feature, function (coord) { var originalDistance = rhumbDistance(origin, coord); var bearing = rhumbBearing(origin, coord); var newDistance = originalDistance * factor; var newCoord = getCoords(rhumbDestination(origin, newDistance, bearing)); coord[0] = newCoord[0]; coord[1] = newCoord[1]; if (coord.length === 3) coord[2] *= factor; }); return feature; } /** * Define Origin * * @private * @param {GeoJSON} geojson GeoJSON * @param {string|Coord} origin sw/se/nw/ne/center/centroid * @returns {Feature} Point origin */ function defineOrigin(geojson, origin) { // Default params if (origin === undefined || origin === null) origin = "centroid"; // Input Coord if (Array.isArray(origin) || typeof origin === "object") return getCoord(origin); // Define BBox var bbox = geojson.bbox ? geojson.bbox : turfBBox(geojson); var west = bbox[0]; var south = bbox[1]; var east = bbox[2]; var north = bbox[3]; switch (origin) { case "sw": case "southwest": case "westsouth": case "bottomleft": return point([west, south]); case "se": case "southeast": case "eastsouth": case "bottomright": return point([east, south]); case "nw": case "northwest": case "westnorth": case "topleft": return point([west, north]); case "ne": case "northeast": case "eastnorth": case "topright": return point([east, north]); case "center": return center(geojson); case undefined: case null: case "centroid": return centroid(geojson); default: throw new Error("invalid origin"); } } export default transformScale;