@turf/isolines
Advanced tools
Comparing version 6.5.0 to 7.0.0-alpha.0
@@ -1,514 +0,8 @@ | ||
import bbox from '@turf/bbox'; | ||
import { featureEach, coordEach } from '@turf/meta'; | ||
import { collectionOf, getCoords } from '@turf/invariant'; | ||
import { isObject, featureCollection, multiLineString } from '@turf/helpers'; | ||
import objectAssign from 'object-assign'; | ||
import bbox from "@turf/bbox"; | ||
import { coordEach } from "@turf/meta"; | ||
import { collectionOf } from "@turf/invariant"; | ||
import { multiLineString, featureCollection, isObject } from "@turf/helpers"; | ||
import isoContours from "./lib/marchingsquares-isocontours.js"; | ||
import gridToMatrix from "./lib/grid-to-matrix.js"; | ||
/** | ||
* @license GNU Affero General Public License. | ||
* Copyright (c) 2015, 2015 Ronny Lorenz <ronny@tbi.univie.ac.at> | ||
* v. 1.2.0 | ||
* https://github.com/RaumZeit/MarchingSquares.js | ||
* | ||
* MarchingSquaresJS is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU Affero General Public License as published by | ||
* the Free Software Foundation, either version 3 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* MarchingSquaresJS is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU Affero General Public License for more details. | ||
* | ||
* As additional permission under GNU Affero General Public License version 3 | ||
* section 7, third-party projects (personal or commercial) may distribute, | ||
* include, or link against UNMODIFIED VERSIONS of MarchingSquaresJS without the | ||
* requirement that said third-party project for that reason alone becomes | ||
* subject to any requirement of the GNU Affero General Public License version 3. | ||
* Any modifications to MarchingSquaresJS, however, must be shared with the public | ||
* and made available. | ||
* | ||
* In summary this: | ||
* - allows you to use MarchingSquaresJS at no cost | ||
* - allows you to use MarchingSquaresJS for both personal and commercial purposes | ||
* - allows you to distribute UNMODIFIED VERSIONS of MarchingSquaresJS under any | ||
* license as long as this license notice is included | ||
* - enables you to keep the source code of your program that uses MarchingSquaresJS | ||
* undisclosed | ||
* - forces you to share any modifications you have made to MarchingSquaresJS, | ||
* e.g. bug-fixes | ||
* | ||
* You should have received a copy of the GNU Affero General Public License | ||
* along with MarchingSquaresJS. If not, see <http://www.gnu.org/licenses/>. | ||
*/ | ||
/** | ||
* Compute the isocontour(s) of a scalar 2D field given | ||
* a certain threshold by applying the Marching Squares | ||
* Algorithm. The function returns a list of path coordinates | ||
*/ | ||
var defaultSettings = { | ||
successCallback: null, | ||
verbose: false, | ||
}; | ||
var settings = {}; | ||
function isoContours(data, threshold, options) { | ||
/* process options */ | ||
options = options ? options : {}; | ||
var optionKeys = Object.keys(defaultSettings); | ||
for (var i = 0; i < optionKeys.length; i++) { | ||
var key = optionKeys[i]; | ||
var val = options[key]; | ||
val = | ||
typeof val !== "undefined" && val !== null ? val : defaultSettings[key]; | ||
settings[key] = val; | ||
} | ||
if (settings.verbose) | ||
console.log( | ||
"MarchingSquaresJS-isoContours: computing isocontour for " + threshold | ||
); | ||
var ret = contourGrid2Paths(computeContourGrid(data, threshold)); | ||
if (typeof settings.successCallback === "function") | ||
settings.successCallback(ret); | ||
return ret; | ||
} | ||
/* | ||
Thats all for the public interface, below follows the actual | ||
implementation | ||
*/ | ||
/* | ||
################################ | ||
Isocontour implementation below | ||
################################ | ||
*/ | ||
/* assume that x1 == 1 && x0 == 0 */ | ||
function interpolateX(y, y0, y1) { | ||
return (y - y0) / (y1 - y0); | ||
} | ||
/* compute the isocontour 4-bit grid */ | ||
function computeContourGrid(data, threshold) { | ||
var rows = data.length - 1; | ||
var cols = data[0].length - 1; | ||
var ContourGrid = { rows: rows, cols: cols, cells: [] }; | ||
for (var j = 0; j < rows; ++j) { | ||
ContourGrid.cells[j] = []; | ||
for (var i = 0; i < cols; ++i) { | ||
/* compose the 4-bit corner representation */ | ||
var cval = 0; | ||
var tl = data[j + 1][i]; | ||
var tr = data[j + 1][i + 1]; | ||
var br = data[j][i + 1]; | ||
var bl = data[j][i]; | ||
if (isNaN(tl) || isNaN(tr) || isNaN(br) || isNaN(bl)) { | ||
continue; | ||
} | ||
cval |= tl >= threshold ? 8 : 0; | ||
cval |= tr >= threshold ? 4 : 0; | ||
cval |= br >= threshold ? 2 : 0; | ||
cval |= bl >= threshold ? 1 : 0; | ||
/* resolve ambiguity for cval == 5 || 10 via averaging */ | ||
var flipped = false; | ||
if (cval === 5 || cval === 10) { | ||
var average = (tl + tr + br + bl) / 4; | ||
if (cval === 5 && average < threshold) { | ||
cval = 10; | ||
flipped = true; | ||
} else if (cval === 10 && average < threshold) { | ||
cval = 5; | ||
flipped = true; | ||
} | ||
} | ||
/* add cell to ContourGrid if it contains edges */ | ||
if (cval !== 0 && cval !== 15) { | ||
var top, bottom, left, right; | ||
top = bottom = left = right = 0.5; | ||
/* interpolate edges of cell */ | ||
if (cval === 1) { | ||
left = 1 - interpolateX(threshold, tl, bl); | ||
bottom = 1 - interpolateX(threshold, br, bl); | ||
} else if (cval === 2) { | ||
bottom = interpolateX(threshold, bl, br); | ||
right = 1 - interpolateX(threshold, tr, br); | ||
} else if (cval === 3) { | ||
left = 1 - interpolateX(threshold, tl, bl); | ||
right = 1 - interpolateX(threshold, tr, br); | ||
} else if (cval === 4) { | ||
top = interpolateX(threshold, tl, tr); | ||
right = interpolateX(threshold, br, tr); | ||
} else if (cval === 5) { | ||
top = interpolateX(threshold, tl, tr); | ||
right = interpolateX(threshold, br, tr); | ||
bottom = 1 - interpolateX(threshold, br, bl); | ||
left = 1 - interpolateX(threshold, tl, bl); | ||
} else if (cval === 6) { | ||
bottom = interpolateX(threshold, bl, br); | ||
top = interpolateX(threshold, tl, tr); | ||
} else if (cval === 7) { | ||
left = 1 - interpolateX(threshold, tl, bl); | ||
top = interpolateX(threshold, tl, tr); | ||
} else if (cval === 8) { | ||
left = interpolateX(threshold, bl, tl); | ||
top = 1 - interpolateX(threshold, tr, tl); | ||
} else if (cval === 9) { | ||
bottom = 1 - interpolateX(threshold, br, bl); | ||
top = 1 - interpolateX(threshold, tr, tl); | ||
} else if (cval === 10) { | ||
top = 1 - interpolateX(threshold, tr, tl); | ||
right = 1 - interpolateX(threshold, tr, br); | ||
bottom = interpolateX(threshold, bl, br); | ||
left = interpolateX(threshold, bl, tl); | ||
} else if (cval === 11) { | ||
top = 1 - interpolateX(threshold, tr, tl); | ||
right = 1 - interpolateX(threshold, tr, br); | ||
} else if (cval === 12) { | ||
left = interpolateX(threshold, bl, tl); | ||
right = interpolateX(threshold, br, tr); | ||
} else if (cval === 13) { | ||
bottom = 1 - interpolateX(threshold, br, bl); | ||
right = interpolateX(threshold, br, tr); | ||
} else if (cval === 14) { | ||
left = interpolateX(threshold, bl, tl); | ||
bottom = interpolateX(threshold, bl, br); | ||
} else { | ||
console.log( | ||
"MarchingSquaresJS-isoContours: Illegal cval detected: " + cval | ||
); | ||
} | ||
ContourGrid.cells[j][i] = { | ||
cval: cval, | ||
flipped: flipped, | ||
top: top, | ||
right: right, | ||
bottom: bottom, | ||
left: left, | ||
}; | ||
} | ||
} | ||
} | ||
return ContourGrid; | ||
} | ||
function isSaddle(cell) { | ||
return cell.cval === 5 || cell.cval === 10; | ||
} | ||
function isTrivial(cell) { | ||
return cell.cval === 0 || cell.cval === 15; | ||
} | ||
function clearCell(cell) { | ||
if (!isTrivial(cell) && cell.cval !== 5 && cell.cval !== 10) { | ||
cell.cval = 15; | ||
} | ||
} | ||
function getXY(cell, edge) { | ||
if (edge === "top") { | ||
return [cell.top, 1.0]; | ||
} else if (edge === "bottom") { | ||
return [cell.bottom, 0.0]; | ||
} else if (edge === "right") { | ||
return [1.0, cell.right]; | ||
} else if (edge === "left") { | ||
return [0.0, cell.left]; | ||
} | ||
} | ||
function contourGrid2Paths(grid) { | ||
var paths = []; | ||
var path_idx = 0; | ||
var epsilon = 1e-7; | ||
grid.cells.forEach(function (g, j) { | ||
g.forEach(function (gg, i) { | ||
if (typeof gg !== "undefined" && !isSaddle(gg) && !isTrivial(gg)) { | ||
var p = tracePath(grid.cells, j, i); | ||
var merged = false; | ||
/* we may try to merge paths at this point */ | ||
if (p.info === "mergeable") { | ||
/* | ||
search backwards through the path array to find an entry | ||
that starts with where the current path ends... | ||
*/ | ||
var x = p.path[p.path.length - 1][0], | ||
y = p.path[p.path.length - 1][1]; | ||
for (var k = path_idx - 1; k >= 0; k--) { | ||
if ( | ||
Math.abs(paths[k][0][0] - x) <= epsilon && | ||
Math.abs(paths[k][0][1] - y) <= epsilon | ||
) { | ||
for (var l = p.path.length - 2; l >= 0; --l) { | ||
paths[k].unshift(p.path[l]); | ||
} | ||
merged = true; | ||
break; | ||
} | ||
} | ||
} | ||
if (!merged) paths[path_idx++] = p.path; | ||
} | ||
}); | ||
}); | ||
return paths; | ||
} | ||
/* | ||
construct consecutive line segments from starting cell by | ||
walking arround the enclosed area clock-wise | ||
*/ | ||
function tracePath(grid, j, i) { | ||
var maxj = grid.length; | ||
var p = []; | ||
var dxContour = [0, 0, 1, 1, 0, 0, 0, 0, -1, 0, 1, 1, -1, 0, -1, 0]; | ||
var dyContour = [0, -1, 0, 0, 1, 1, 1, 1, 0, -1, 0, 0, 0, -1, 0, 0]; | ||
var dx, dy; | ||
var startEdge = [ | ||
"none", | ||
"left", | ||
"bottom", | ||
"left", | ||
"right", | ||
"none", | ||
"bottom", | ||
"left", | ||
"top", | ||
"top", | ||
"none", | ||
"top", | ||
"right", | ||
"right", | ||
"bottom", | ||
"none", | ||
]; | ||
var nextEdge = [ | ||
"none", | ||
"bottom", | ||
"right", | ||
"right", | ||
"top", | ||
"top", | ||
"top", | ||
"top", | ||
"left", | ||
"bottom", | ||
"right", | ||
"right", | ||
"left", | ||
"bottom", | ||
"left", | ||
"none", | ||
]; | ||
var edge; | ||
var currentCell = grid[j][i]; | ||
var cval = currentCell.cval; | ||
var edge = startEdge[cval]; | ||
var pt = getXY(currentCell, edge); | ||
/* push initial segment */ | ||
p.push([i + pt[0], j + pt[1]]); | ||
edge = nextEdge[cval]; | ||
pt = getXY(currentCell, edge); | ||
p.push([i + pt[0], j + pt[1]]); | ||
clearCell(currentCell); | ||
/* now walk arround the enclosed area in clockwise-direction */ | ||
var k = i + dxContour[cval]; | ||
var l = j + dyContour[cval]; | ||
var prev_cval = cval; | ||
while (k >= 0 && l >= 0 && l < maxj && (k != i || l != j)) { | ||
currentCell = grid[l][k]; | ||
if (typeof currentCell === "undefined") { | ||
/* path ends here */ | ||
//console.log(k + " " + l + " is undefined, stopping path!"); | ||
break; | ||
} | ||
cval = currentCell.cval; | ||
if (cval === 0 || cval === 15) { | ||
return { path: p, info: "mergeable" }; | ||
} | ||
edge = nextEdge[cval]; | ||
dx = dxContour[cval]; | ||
dy = dyContour[cval]; | ||
if (cval === 5 || cval === 10) { | ||
/* select upper or lower band, depending on previous cells cval */ | ||
if (cval === 5) { | ||
if (currentCell.flipped) { | ||
/* this is actually a flipped case 10 */ | ||
if (dyContour[prev_cval] === -1) { | ||
edge = "left"; | ||
dx = -1; | ||
dy = 0; | ||
} else { | ||
edge = "right"; | ||
dx = 1; | ||
dy = 0; | ||
} | ||
} else { | ||
/* real case 5 */ | ||
if (dxContour[prev_cval] === -1) { | ||
edge = "bottom"; | ||
dx = 0; | ||
dy = -1; | ||
} | ||
} | ||
} else if (cval === 10) { | ||
if (currentCell.flipped) { | ||
/* this is actually a flipped case 5 */ | ||
if (dxContour[prev_cval] === -1) { | ||
edge = "top"; | ||
dx = 0; | ||
dy = 1; | ||
} else { | ||
edge = "bottom"; | ||
dx = 0; | ||
dy = -1; | ||
} | ||
} else { | ||
/* real case 10 */ | ||
if (dyContour[prev_cval] === 1) { | ||
edge = "left"; | ||
dx = -1; | ||
dy = 0; | ||
} | ||
} | ||
} | ||
} | ||
pt = getXY(currentCell, edge); | ||
p.push([k + pt[0], l + pt[1]]); | ||
clearCell(currentCell); | ||
k += dx; | ||
l += dy; | ||
prev_cval = cval; | ||
} | ||
return { path: p, info: "closed" }; | ||
} | ||
/** | ||
* Takes a {@link Point} grid and returns a correspondent matrix {Array<Array<number>>} | ||
* of the 'property' values | ||
* | ||
* @name gridToMatrix | ||
* @param {FeatureCollection<Point>} grid of points | ||
* @param {Object} [options={}] Optional parameters | ||
* @param {string} [options.zProperty='elevation'] the property name in `points` from which z-values will be pulled | ||
* @param {boolean} [options.flip=false] returns the matrix upside-down | ||
* @param {boolean} [options.flags=false] flags, adding a `matrixPosition` array field ([row, column]) to its properties, | ||
* the grid points with coordinates on the matrix | ||
* @returns {Array<Array<number>>} matrix of property values | ||
* @example | ||
* var extent = [-70.823364, -33.553984, -70.473175, -33.302986]; | ||
* var cellSize = 3; | ||
* var grid = turf.pointGrid(extent, cellSize); | ||
* // add a random property to each point between 0 and 60 | ||
* for (var i = 0; i < grid.features.length; i++) { | ||
* grid.features[i].properties.elevation = (Math.random() * 60); | ||
* } | ||
* gridToMatrix(grid); | ||
* //= [ | ||
* [ 1, 13, 10, 9, 10, 13, 18], | ||
* [34, 8, 5, 4, 5, 8, 13], | ||
* [10, 5, 2, 1, 2, 5, 4], | ||
* [ 0, 4, 56, 19, 1, 4, 9], | ||
* [10, 5, 2, 1, 2, 5, 10], | ||
* [57, 8, 5, 4, 5, 0, 57], | ||
* [ 3, 13, 10, 9, 5, 13, 18], | ||
* [18, 13, 10, 9, 78, 13, 18] | ||
* ] | ||
*/ | ||
function gridToMatrix(grid, options) { | ||
// Optional parameters | ||
options = options || {}; | ||
if (!isObject(options)) throw new Error("options is invalid"); | ||
var zProperty = options.zProperty || "elevation"; | ||
var flip = options.flip; | ||
var flags = options.flags; | ||
// validation | ||
collectionOf(grid, "Point", "input must contain Points"); | ||
var pointsMatrix = sortPointsByLatLng(grid, flip); | ||
var matrix = []; | ||
// create property matrix from sorted points | ||
// looping order matters here | ||
for (var r = 0; r < pointsMatrix.length; r++) { | ||
var pointRow = pointsMatrix[r]; | ||
var row = []; | ||
for (var c = 0; c < pointRow.length; c++) { | ||
var point = pointRow[c]; | ||
// Check if zProperty exist | ||
if (point.properties[zProperty]) row.push(point.properties[zProperty]); | ||
else row.push(0); | ||
// add flags | ||
if (flags === true) point.properties.matrixPosition = [r, c]; | ||
} | ||
matrix.push(row); | ||
} | ||
return matrix; | ||
} | ||
/** | ||
* Sorts points by latitude and longitude, creating a 2-dimensional array of points | ||
* | ||
* @private | ||
* @param {FeatureCollection<Point>} points GeoJSON Point features | ||
* @param {boolean} [flip=false] returns the matrix upside-down | ||
* @returns {Array<Array<Point>>} points ordered by latitude and longitude | ||
*/ | ||
function sortPointsByLatLng(points, flip) { | ||
var pointsByLatitude = {}; | ||
// divide points by rows with the same latitude | ||
featureEach(points, function (point) { | ||
var lat = getCoords(point)[1]; | ||
if (!pointsByLatitude[lat]) pointsByLatitude[lat] = []; | ||
pointsByLatitude[lat].push(point); | ||
}); | ||
// sort points (with the same latitude) by longitude | ||
var orderedRowsByLatitude = Object.keys(pointsByLatitude).map(function (lat) { | ||
var row = pointsByLatitude[lat]; | ||
var rowOrderedByLongitude = row.sort(function (a, b) { | ||
return getCoords(a)[0] - getCoords(b)[0]; | ||
}); | ||
return rowOrderedByLongitude; | ||
}); | ||
// sort rows (of points with the same latitude) by latitude | ||
var pointMatrix = orderedRowsByLatitude.sort(function (a, b) { | ||
if (flip) return getCoords(a[0])[1] - getCoords(b[0])[1]; | ||
else return getCoords(b[0])[1] - getCoords(a[0])[1]; | ||
}); | ||
return pointMatrix; | ||
} | ||
/** | ||
* Takes a grid {@link FeatureCollection} of {@link Point} features with z-values and an array of | ||
@@ -543,32 +37,25 @@ * value breaks and generates [isolines](https://en.wikipedia.org/wiki/Contour_line). | ||
function isolines(pointGrid, breaks, options) { | ||
// Optional parameters | ||
options = options || {}; | ||
if (!isObject(options)) throw new Error("options is invalid"); | ||
var zProperty = options.zProperty || "elevation"; | ||
var commonProperties = options.commonProperties || {}; | ||
var breaksProperties = options.breaksProperties || []; | ||
// Input validation | ||
collectionOf(pointGrid, "Point", "Input must contain Points"); | ||
if (!breaks) throw new Error("breaks is required"); | ||
if (!Array.isArray(breaks)) throw new Error("breaks must be an Array"); | ||
if (!isObject(commonProperties)) | ||
throw new Error("commonProperties must be an Object"); | ||
if (!Array.isArray(breaksProperties)) | ||
throw new Error("breaksProperties must be an Array"); | ||
// Isoline methods | ||
var matrix = gridToMatrix(pointGrid, { zProperty: zProperty, flip: true }); | ||
var createdIsoLines = createIsoLines( | ||
matrix, | ||
breaks, | ||
zProperty, | ||
commonProperties, | ||
breaksProperties | ||
); | ||
var scaledIsolines = rescaleIsolines(createdIsoLines, matrix, pointGrid); | ||
return featureCollection(scaledIsolines); | ||
// Optional parameters | ||
options = options || {}; | ||
if (!isObject(options)) | ||
throw new Error("options is invalid"); | ||
const zProperty = options.zProperty || "elevation"; | ||
const commonProperties = options.commonProperties || {}; | ||
const breaksProperties = options.breaksProperties || []; | ||
// Input validation | ||
collectionOf(pointGrid, "Point", "Input must contain Points"); | ||
if (!breaks) | ||
throw new Error("breaks is required"); | ||
if (!Array.isArray(breaks)) | ||
throw new Error("breaks must be an Array"); | ||
if (!isObject(commonProperties)) | ||
throw new Error("commonProperties must be an Object"); | ||
if (!Array.isArray(breaksProperties)) | ||
throw new Error("breaksProperties must be an Array"); | ||
// Isoline methods | ||
const matrix = gridToMatrix(pointGrid, { zProperty: zProperty, flip: true }); | ||
const createdIsoLines = createIsoLines(matrix, breaks, zProperty, commonProperties, breaksProperties); | ||
const scaledIsolines = rescaleIsolines(createdIsoLines, matrix, pointGrid); | ||
return featureCollection(scaledIsolines); | ||
} | ||
/** | ||
@@ -583,3 +70,3 @@ * Creates the isolines lines (featuresCollection of MultiLineString features) from the 2D data grid | ||
* @param {Array<Array<number>>} matrix Grid Data | ||
* @param {Array<number>} breaks Breaks | ||
* @param {Array<number>} breaks BreakProps | ||
* @param {string} zProperty name of the z-values property | ||
@@ -590,22 +77,13 @@ * @param {Object} [commonProperties={}] GeoJSON properties passed to ALL isolines | ||
*/ | ||
function createIsoLines( | ||
matrix, | ||
breaks, | ||
zProperty, | ||
commonProperties, | ||
breaksProperties | ||
) { | ||
var results = []; | ||
for (var i = 1; i < breaks.length; i++) { | ||
var threshold = +breaks[i]; // make sure it's a number | ||
var properties = objectAssign({}, commonProperties, breaksProperties[i]); | ||
properties[zProperty] = threshold; | ||
var isoline = multiLineString(isoContours(matrix, threshold), properties); | ||
results.push(isoline); | ||
} | ||
return results; | ||
function createIsoLines(matrix, breaks, zProperty, commonProperties, breaksProperties) { | ||
const results = []; | ||
for (let i = 1; i < breaks.length; i++) { | ||
const threshold = +breaks[i]; // make sure it's a number | ||
const properties = Object.assign(Object.assign({}, commonProperties), breaksProperties[i]); | ||
properties[zProperty] = threshold; | ||
const isoline = multiLineString(isoContours(matrix, threshold), properties); | ||
results.push(isoline); | ||
} | ||
return results; | ||
} | ||
/** | ||
@@ -621,31 +99,25 @@ * Translates and scales isolines | ||
function rescaleIsolines(createdIsoLines, matrix, points) { | ||
// get dimensions (on the map) of the original grid | ||
var gridBbox = bbox(points); // [ minX, minY, maxX, maxY ] | ||
var originalWidth = gridBbox[2] - gridBbox[0]; | ||
var originalHeigth = gridBbox[3] - gridBbox[1]; | ||
// get origin, which is the first point of the last row on the rectangular data on the map | ||
var x0 = gridBbox[0]; | ||
var y0 = gridBbox[1]; | ||
// get number of cells per side | ||
var matrixWidth = matrix[0].length - 1; | ||
var matrixHeight = matrix.length - 1; | ||
// calculate the scaling factor between matrix and rectangular grid on the map | ||
var scaleX = originalWidth / matrixWidth; | ||
var scaleY = originalHeigth / matrixHeight; | ||
var resize = function (point) { | ||
point[0] = point[0] * scaleX + x0; | ||
point[1] = point[1] * scaleY + y0; | ||
}; | ||
// resize and shift each point/line of the createdIsoLines | ||
createdIsoLines.forEach(function (isoline) { | ||
coordEach(isoline, resize); | ||
}); | ||
return createdIsoLines; | ||
// get dimensions (on the map) of the original grid | ||
const gridBbox = bbox(points); // [ minX, minY, maxX, maxY ] | ||
const originalWidth = gridBbox[2] - gridBbox[0]; | ||
const originalHeigth = gridBbox[3] - gridBbox[1]; | ||
// get origin, which is the first point of the last row on the rectangular data on the map | ||
const x0 = gridBbox[0]; | ||
const y0 = gridBbox[1]; | ||
// get number of cells per side | ||
const matrixWidth = matrix[0].length - 1; | ||
const matrixHeight = matrix.length - 1; | ||
// calculate the scaling factor between matrix and rectangular grid on the map | ||
const scaleX = originalWidth / matrixWidth; | ||
const scaleY = originalHeigth / matrixHeight; | ||
const resize = (point) => { | ||
point[0] = point[0] * scaleX + x0; | ||
point[1] = point[1] * scaleY + y0; | ||
}; | ||
// resize and shift each point/line of the createdIsoLines | ||
createdIsoLines.forEach((isoline) => { | ||
coordEach(isoline, resize); | ||
}); | ||
return createdIsoLines; | ||
} | ||
export default isolines; |
@@ -1,521 +0,11 @@ | ||
'use strict'; | ||
var bbox = require('@turf/bbox'); | ||
var meta = require('@turf/meta'); | ||
var invariant = require('@turf/invariant'); | ||
var helpers = require('@turf/helpers'); | ||
var objectAssign = require('object-assign'); | ||
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } | ||
var bbox__default = /*#__PURE__*/_interopDefaultLegacy(bbox); | ||
var objectAssign__default = /*#__PURE__*/_interopDefaultLegacy(objectAssign); | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const tslib_1 = require("tslib"); | ||
const bbox_1 = tslib_1.__importDefault(require("@turf/bbox")); | ||
const meta_1 = require("@turf/meta"); | ||
const invariant_1 = require("@turf/invariant"); | ||
const helpers_1 = require("@turf/helpers"); | ||
const marchingsquares_isocontours_1 = tslib_1.__importDefault(require("./lib/marchingsquares-isocontours")); | ||
const grid_to_matrix_1 = tslib_1.__importDefault(require("./lib/grid-to-matrix")); | ||
/** | ||
* @license GNU Affero General Public License. | ||
* Copyright (c) 2015, 2015 Ronny Lorenz <ronny@tbi.univie.ac.at> | ||
* v. 1.2.0 | ||
* https://github.com/RaumZeit/MarchingSquares.js | ||
* | ||
* MarchingSquaresJS is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU Affero General Public License as published by | ||
* the Free Software Foundation, either version 3 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* MarchingSquaresJS is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU Affero General Public License for more details. | ||
* | ||
* As additional permission under GNU Affero General Public License version 3 | ||
* section 7, third-party projects (personal or commercial) may distribute, | ||
* include, or link against UNMODIFIED VERSIONS of MarchingSquaresJS without the | ||
* requirement that said third-party project for that reason alone becomes | ||
* subject to any requirement of the GNU Affero General Public License version 3. | ||
* Any modifications to MarchingSquaresJS, however, must be shared with the public | ||
* and made available. | ||
* | ||
* In summary this: | ||
* - allows you to use MarchingSquaresJS at no cost | ||
* - allows you to use MarchingSquaresJS for both personal and commercial purposes | ||
* - allows you to distribute UNMODIFIED VERSIONS of MarchingSquaresJS under any | ||
* license as long as this license notice is included | ||
* - enables you to keep the source code of your program that uses MarchingSquaresJS | ||
* undisclosed | ||
* - forces you to share any modifications you have made to MarchingSquaresJS, | ||
* e.g. bug-fixes | ||
* | ||
* You should have received a copy of the GNU Affero General Public License | ||
* along with MarchingSquaresJS. If not, see <http://www.gnu.org/licenses/>. | ||
*/ | ||
/** | ||
* Compute the isocontour(s) of a scalar 2D field given | ||
* a certain threshold by applying the Marching Squares | ||
* Algorithm. The function returns a list of path coordinates | ||
*/ | ||
var defaultSettings = { | ||
successCallback: null, | ||
verbose: false, | ||
}; | ||
var settings = {}; | ||
function isoContours(data, threshold, options) { | ||
/* process options */ | ||
options = options ? options : {}; | ||
var optionKeys = Object.keys(defaultSettings); | ||
for (var i = 0; i < optionKeys.length; i++) { | ||
var key = optionKeys[i]; | ||
var val = options[key]; | ||
val = | ||
typeof val !== "undefined" && val !== null ? val : defaultSettings[key]; | ||
settings[key] = val; | ||
} | ||
if (settings.verbose) | ||
console.log( | ||
"MarchingSquaresJS-isoContours: computing isocontour for " + threshold | ||
); | ||
var ret = contourGrid2Paths(computeContourGrid(data, threshold)); | ||
if (typeof settings.successCallback === "function") | ||
settings.successCallback(ret); | ||
return ret; | ||
} | ||
/* | ||
Thats all for the public interface, below follows the actual | ||
implementation | ||
*/ | ||
/* | ||
################################ | ||
Isocontour implementation below | ||
################################ | ||
*/ | ||
/* assume that x1 == 1 && x0 == 0 */ | ||
function interpolateX(y, y0, y1) { | ||
return (y - y0) / (y1 - y0); | ||
} | ||
/* compute the isocontour 4-bit grid */ | ||
function computeContourGrid(data, threshold) { | ||
var rows = data.length - 1; | ||
var cols = data[0].length - 1; | ||
var ContourGrid = { rows: rows, cols: cols, cells: [] }; | ||
for (var j = 0; j < rows; ++j) { | ||
ContourGrid.cells[j] = []; | ||
for (var i = 0; i < cols; ++i) { | ||
/* compose the 4-bit corner representation */ | ||
var cval = 0; | ||
var tl = data[j + 1][i]; | ||
var tr = data[j + 1][i + 1]; | ||
var br = data[j][i + 1]; | ||
var bl = data[j][i]; | ||
if (isNaN(tl) || isNaN(tr) || isNaN(br) || isNaN(bl)) { | ||
continue; | ||
} | ||
cval |= tl >= threshold ? 8 : 0; | ||
cval |= tr >= threshold ? 4 : 0; | ||
cval |= br >= threshold ? 2 : 0; | ||
cval |= bl >= threshold ? 1 : 0; | ||
/* resolve ambiguity for cval == 5 || 10 via averaging */ | ||
var flipped = false; | ||
if (cval === 5 || cval === 10) { | ||
var average = (tl + tr + br + bl) / 4; | ||
if (cval === 5 && average < threshold) { | ||
cval = 10; | ||
flipped = true; | ||
} else if (cval === 10 && average < threshold) { | ||
cval = 5; | ||
flipped = true; | ||
} | ||
} | ||
/* add cell to ContourGrid if it contains edges */ | ||
if (cval !== 0 && cval !== 15) { | ||
var top, bottom, left, right; | ||
top = bottom = left = right = 0.5; | ||
/* interpolate edges of cell */ | ||
if (cval === 1) { | ||
left = 1 - interpolateX(threshold, tl, bl); | ||
bottom = 1 - interpolateX(threshold, br, bl); | ||
} else if (cval === 2) { | ||
bottom = interpolateX(threshold, bl, br); | ||
right = 1 - interpolateX(threshold, tr, br); | ||
} else if (cval === 3) { | ||
left = 1 - interpolateX(threshold, tl, bl); | ||
right = 1 - interpolateX(threshold, tr, br); | ||
} else if (cval === 4) { | ||
top = interpolateX(threshold, tl, tr); | ||
right = interpolateX(threshold, br, tr); | ||
} else if (cval === 5) { | ||
top = interpolateX(threshold, tl, tr); | ||
right = interpolateX(threshold, br, tr); | ||
bottom = 1 - interpolateX(threshold, br, bl); | ||
left = 1 - interpolateX(threshold, tl, bl); | ||
} else if (cval === 6) { | ||
bottom = interpolateX(threshold, bl, br); | ||
top = interpolateX(threshold, tl, tr); | ||
} else if (cval === 7) { | ||
left = 1 - interpolateX(threshold, tl, bl); | ||
top = interpolateX(threshold, tl, tr); | ||
} else if (cval === 8) { | ||
left = interpolateX(threshold, bl, tl); | ||
top = 1 - interpolateX(threshold, tr, tl); | ||
} else if (cval === 9) { | ||
bottom = 1 - interpolateX(threshold, br, bl); | ||
top = 1 - interpolateX(threshold, tr, tl); | ||
} else if (cval === 10) { | ||
top = 1 - interpolateX(threshold, tr, tl); | ||
right = 1 - interpolateX(threshold, tr, br); | ||
bottom = interpolateX(threshold, bl, br); | ||
left = interpolateX(threshold, bl, tl); | ||
} else if (cval === 11) { | ||
top = 1 - interpolateX(threshold, tr, tl); | ||
right = 1 - interpolateX(threshold, tr, br); | ||
} else if (cval === 12) { | ||
left = interpolateX(threshold, bl, tl); | ||
right = interpolateX(threshold, br, tr); | ||
} else if (cval === 13) { | ||
bottom = 1 - interpolateX(threshold, br, bl); | ||
right = interpolateX(threshold, br, tr); | ||
} else if (cval === 14) { | ||
left = interpolateX(threshold, bl, tl); | ||
bottom = interpolateX(threshold, bl, br); | ||
} else { | ||
console.log( | ||
"MarchingSquaresJS-isoContours: Illegal cval detected: " + cval | ||
); | ||
} | ||
ContourGrid.cells[j][i] = { | ||
cval: cval, | ||
flipped: flipped, | ||
top: top, | ||
right: right, | ||
bottom: bottom, | ||
left: left, | ||
}; | ||
} | ||
} | ||
} | ||
return ContourGrid; | ||
} | ||
function isSaddle(cell) { | ||
return cell.cval === 5 || cell.cval === 10; | ||
} | ||
function isTrivial(cell) { | ||
return cell.cval === 0 || cell.cval === 15; | ||
} | ||
function clearCell(cell) { | ||
if (!isTrivial(cell) && cell.cval !== 5 && cell.cval !== 10) { | ||
cell.cval = 15; | ||
} | ||
} | ||
function getXY(cell, edge) { | ||
if (edge === "top") { | ||
return [cell.top, 1.0]; | ||
} else if (edge === "bottom") { | ||
return [cell.bottom, 0.0]; | ||
} else if (edge === "right") { | ||
return [1.0, cell.right]; | ||
} else if (edge === "left") { | ||
return [0.0, cell.left]; | ||
} | ||
} | ||
function contourGrid2Paths(grid) { | ||
var paths = []; | ||
var path_idx = 0; | ||
var epsilon = 1e-7; | ||
grid.cells.forEach(function (g, j) { | ||
g.forEach(function (gg, i) { | ||
if (typeof gg !== "undefined" && !isSaddle(gg) && !isTrivial(gg)) { | ||
var p = tracePath(grid.cells, j, i); | ||
var merged = false; | ||
/* we may try to merge paths at this point */ | ||
if (p.info === "mergeable") { | ||
/* | ||
search backwards through the path array to find an entry | ||
that starts with where the current path ends... | ||
*/ | ||
var x = p.path[p.path.length - 1][0], | ||
y = p.path[p.path.length - 1][1]; | ||
for (var k = path_idx - 1; k >= 0; k--) { | ||
if ( | ||
Math.abs(paths[k][0][0] - x) <= epsilon && | ||
Math.abs(paths[k][0][1] - y) <= epsilon | ||
) { | ||
for (var l = p.path.length - 2; l >= 0; --l) { | ||
paths[k].unshift(p.path[l]); | ||
} | ||
merged = true; | ||
break; | ||
} | ||
} | ||
} | ||
if (!merged) paths[path_idx++] = p.path; | ||
} | ||
}); | ||
}); | ||
return paths; | ||
} | ||
/* | ||
construct consecutive line segments from starting cell by | ||
walking arround the enclosed area clock-wise | ||
*/ | ||
function tracePath(grid, j, i) { | ||
var maxj = grid.length; | ||
var p = []; | ||
var dxContour = [0, 0, 1, 1, 0, 0, 0, 0, -1, 0, 1, 1, -1, 0, -1, 0]; | ||
var dyContour = [0, -1, 0, 0, 1, 1, 1, 1, 0, -1, 0, 0, 0, -1, 0, 0]; | ||
var dx, dy; | ||
var startEdge = [ | ||
"none", | ||
"left", | ||
"bottom", | ||
"left", | ||
"right", | ||
"none", | ||
"bottom", | ||
"left", | ||
"top", | ||
"top", | ||
"none", | ||
"top", | ||
"right", | ||
"right", | ||
"bottom", | ||
"none", | ||
]; | ||
var nextEdge = [ | ||
"none", | ||
"bottom", | ||
"right", | ||
"right", | ||
"top", | ||
"top", | ||
"top", | ||
"top", | ||
"left", | ||
"bottom", | ||
"right", | ||
"right", | ||
"left", | ||
"bottom", | ||
"left", | ||
"none", | ||
]; | ||
var edge; | ||
var currentCell = grid[j][i]; | ||
var cval = currentCell.cval; | ||
var edge = startEdge[cval]; | ||
var pt = getXY(currentCell, edge); | ||
/* push initial segment */ | ||
p.push([i + pt[0], j + pt[1]]); | ||
edge = nextEdge[cval]; | ||
pt = getXY(currentCell, edge); | ||
p.push([i + pt[0], j + pt[1]]); | ||
clearCell(currentCell); | ||
/* now walk arround the enclosed area in clockwise-direction */ | ||
var k = i + dxContour[cval]; | ||
var l = j + dyContour[cval]; | ||
var prev_cval = cval; | ||
while (k >= 0 && l >= 0 && l < maxj && (k != i || l != j)) { | ||
currentCell = grid[l][k]; | ||
if (typeof currentCell === "undefined") { | ||
/* path ends here */ | ||
//console.log(k + " " + l + " is undefined, stopping path!"); | ||
break; | ||
} | ||
cval = currentCell.cval; | ||
if (cval === 0 || cval === 15) { | ||
return { path: p, info: "mergeable" }; | ||
} | ||
edge = nextEdge[cval]; | ||
dx = dxContour[cval]; | ||
dy = dyContour[cval]; | ||
if (cval === 5 || cval === 10) { | ||
/* select upper or lower band, depending on previous cells cval */ | ||
if (cval === 5) { | ||
if (currentCell.flipped) { | ||
/* this is actually a flipped case 10 */ | ||
if (dyContour[prev_cval] === -1) { | ||
edge = "left"; | ||
dx = -1; | ||
dy = 0; | ||
} else { | ||
edge = "right"; | ||
dx = 1; | ||
dy = 0; | ||
} | ||
} else { | ||
/* real case 5 */ | ||
if (dxContour[prev_cval] === -1) { | ||
edge = "bottom"; | ||
dx = 0; | ||
dy = -1; | ||
} | ||
} | ||
} else if (cval === 10) { | ||
if (currentCell.flipped) { | ||
/* this is actually a flipped case 5 */ | ||
if (dxContour[prev_cval] === -1) { | ||
edge = "top"; | ||
dx = 0; | ||
dy = 1; | ||
} else { | ||
edge = "bottom"; | ||
dx = 0; | ||
dy = -1; | ||
} | ||
} else { | ||
/* real case 10 */ | ||
if (dyContour[prev_cval] === 1) { | ||
edge = "left"; | ||
dx = -1; | ||
dy = 0; | ||
} | ||
} | ||
} | ||
} | ||
pt = getXY(currentCell, edge); | ||
p.push([k + pt[0], l + pt[1]]); | ||
clearCell(currentCell); | ||
k += dx; | ||
l += dy; | ||
prev_cval = cval; | ||
} | ||
return { path: p, info: "closed" }; | ||
} | ||
/** | ||
* Takes a {@link Point} grid and returns a correspondent matrix {Array<Array<number>>} | ||
* of the 'property' values | ||
* | ||
* @name gridToMatrix | ||
* @param {FeatureCollection<Point>} grid of points | ||
* @param {Object} [options={}] Optional parameters | ||
* @param {string} [options.zProperty='elevation'] the property name in `points` from which z-values will be pulled | ||
* @param {boolean} [options.flip=false] returns the matrix upside-down | ||
* @param {boolean} [options.flags=false] flags, adding a `matrixPosition` array field ([row, column]) to its properties, | ||
* the grid points with coordinates on the matrix | ||
* @returns {Array<Array<number>>} matrix of property values | ||
* @example | ||
* var extent = [-70.823364, -33.553984, -70.473175, -33.302986]; | ||
* var cellSize = 3; | ||
* var grid = turf.pointGrid(extent, cellSize); | ||
* // add a random property to each point between 0 and 60 | ||
* for (var i = 0; i < grid.features.length; i++) { | ||
* grid.features[i].properties.elevation = (Math.random() * 60); | ||
* } | ||
* gridToMatrix(grid); | ||
* //= [ | ||
* [ 1, 13, 10, 9, 10, 13, 18], | ||
* [34, 8, 5, 4, 5, 8, 13], | ||
* [10, 5, 2, 1, 2, 5, 4], | ||
* [ 0, 4, 56, 19, 1, 4, 9], | ||
* [10, 5, 2, 1, 2, 5, 10], | ||
* [57, 8, 5, 4, 5, 0, 57], | ||
* [ 3, 13, 10, 9, 5, 13, 18], | ||
* [18, 13, 10, 9, 78, 13, 18] | ||
* ] | ||
*/ | ||
function gridToMatrix(grid, options) { | ||
// Optional parameters | ||
options = options || {}; | ||
if (!helpers.isObject(options)) throw new Error("options is invalid"); | ||
var zProperty = options.zProperty || "elevation"; | ||
var flip = options.flip; | ||
var flags = options.flags; | ||
// validation | ||
invariant.collectionOf(grid, "Point", "input must contain Points"); | ||
var pointsMatrix = sortPointsByLatLng(grid, flip); | ||
var matrix = []; | ||
// create property matrix from sorted points | ||
// looping order matters here | ||
for (var r = 0; r < pointsMatrix.length; r++) { | ||
var pointRow = pointsMatrix[r]; | ||
var row = []; | ||
for (var c = 0; c < pointRow.length; c++) { | ||
var point = pointRow[c]; | ||
// Check if zProperty exist | ||
if (point.properties[zProperty]) row.push(point.properties[zProperty]); | ||
else row.push(0); | ||
// add flags | ||
if (flags === true) point.properties.matrixPosition = [r, c]; | ||
} | ||
matrix.push(row); | ||
} | ||
return matrix; | ||
} | ||
/** | ||
* Sorts points by latitude and longitude, creating a 2-dimensional array of points | ||
* | ||
* @private | ||
* @param {FeatureCollection<Point>} points GeoJSON Point features | ||
* @param {boolean} [flip=false] returns the matrix upside-down | ||
* @returns {Array<Array<Point>>} points ordered by latitude and longitude | ||
*/ | ||
function sortPointsByLatLng(points, flip) { | ||
var pointsByLatitude = {}; | ||
// divide points by rows with the same latitude | ||
meta.featureEach(points, function (point) { | ||
var lat = invariant.getCoords(point)[1]; | ||
if (!pointsByLatitude[lat]) pointsByLatitude[lat] = []; | ||
pointsByLatitude[lat].push(point); | ||
}); | ||
// sort points (with the same latitude) by longitude | ||
var orderedRowsByLatitude = Object.keys(pointsByLatitude).map(function (lat) { | ||
var row = pointsByLatitude[lat]; | ||
var rowOrderedByLongitude = row.sort(function (a, b) { | ||
return invariant.getCoords(a)[0] - invariant.getCoords(b)[0]; | ||
}); | ||
return rowOrderedByLongitude; | ||
}); | ||
// sort rows (of points with the same latitude) by latitude | ||
var pointMatrix = orderedRowsByLatitude.sort(function (a, b) { | ||
if (flip) return invariant.getCoords(a[0])[1] - invariant.getCoords(b[0])[1]; | ||
else return invariant.getCoords(b[0])[1] - invariant.getCoords(a[0])[1]; | ||
}); | ||
return pointMatrix; | ||
} | ||
/** | ||
* Takes a grid {@link FeatureCollection} of {@link Point} features with z-values and an array of | ||
@@ -550,32 +40,25 @@ * value breaks and generates [isolines](https://en.wikipedia.org/wiki/Contour_line). | ||
function isolines(pointGrid, breaks, options) { | ||
// Optional parameters | ||
options = options || {}; | ||
if (!helpers.isObject(options)) throw new Error("options is invalid"); | ||
var zProperty = options.zProperty || "elevation"; | ||
var commonProperties = options.commonProperties || {}; | ||
var breaksProperties = options.breaksProperties || []; | ||
// Input validation | ||
invariant.collectionOf(pointGrid, "Point", "Input must contain Points"); | ||
if (!breaks) throw new Error("breaks is required"); | ||
if (!Array.isArray(breaks)) throw new Error("breaks must be an Array"); | ||
if (!helpers.isObject(commonProperties)) | ||
throw new Error("commonProperties must be an Object"); | ||
if (!Array.isArray(breaksProperties)) | ||
throw new Error("breaksProperties must be an Array"); | ||
// Isoline methods | ||
var matrix = gridToMatrix(pointGrid, { zProperty: zProperty, flip: true }); | ||
var createdIsoLines = createIsoLines( | ||
matrix, | ||
breaks, | ||
zProperty, | ||
commonProperties, | ||
breaksProperties | ||
); | ||
var scaledIsolines = rescaleIsolines(createdIsoLines, matrix, pointGrid); | ||
return helpers.featureCollection(scaledIsolines); | ||
// Optional parameters | ||
options = options || {}; | ||
if (!helpers_1.isObject(options)) | ||
throw new Error("options is invalid"); | ||
const zProperty = options.zProperty || "elevation"; | ||
const commonProperties = options.commonProperties || {}; | ||
const breaksProperties = options.breaksProperties || []; | ||
// Input validation | ||
invariant_1.collectionOf(pointGrid, "Point", "Input must contain Points"); | ||
if (!breaks) | ||
throw new Error("breaks is required"); | ||
if (!Array.isArray(breaks)) | ||
throw new Error("breaks must be an Array"); | ||
if (!helpers_1.isObject(commonProperties)) | ||
throw new Error("commonProperties must be an Object"); | ||
if (!Array.isArray(breaksProperties)) | ||
throw new Error("breaksProperties must be an Array"); | ||
// Isoline methods | ||
const matrix = grid_to_matrix_1.default(pointGrid, { zProperty: zProperty, flip: true }); | ||
const createdIsoLines = createIsoLines(matrix, breaks, zProperty, commonProperties, breaksProperties); | ||
const scaledIsolines = rescaleIsolines(createdIsoLines, matrix, pointGrid); | ||
return helpers_1.featureCollection(scaledIsolines); | ||
} | ||
/** | ||
@@ -590,3 +73,3 @@ * Creates the isolines lines (featuresCollection of MultiLineString features) from the 2D data grid | ||
* @param {Array<Array<number>>} matrix Grid Data | ||
* @param {Array<number>} breaks Breaks | ||
* @param {Array<number>} breaks BreakProps | ||
* @param {string} zProperty name of the z-values property | ||
@@ -597,22 +80,13 @@ * @param {Object} [commonProperties={}] GeoJSON properties passed to ALL isolines | ||
*/ | ||
function createIsoLines( | ||
matrix, | ||
breaks, | ||
zProperty, | ||
commonProperties, | ||
breaksProperties | ||
) { | ||
var results = []; | ||
for (var i = 1; i < breaks.length; i++) { | ||
var threshold = +breaks[i]; // make sure it's a number | ||
var properties = objectAssign__default['default']({}, commonProperties, breaksProperties[i]); | ||
properties[zProperty] = threshold; | ||
var isoline = helpers.multiLineString(isoContours(matrix, threshold), properties); | ||
results.push(isoline); | ||
} | ||
return results; | ||
function createIsoLines(matrix, breaks, zProperty, commonProperties, breaksProperties) { | ||
const results = []; | ||
for (let i = 1; i < breaks.length; i++) { | ||
const threshold = +breaks[i]; // make sure it's a number | ||
const properties = Object.assign(Object.assign({}, commonProperties), breaksProperties[i]); | ||
properties[zProperty] = threshold; | ||
const isoline = helpers_1.multiLineString(marchingsquares_isocontours_1.default(matrix, threshold), properties); | ||
results.push(isoline); | ||
} | ||
return results; | ||
} | ||
/** | ||
@@ -628,32 +102,25 @@ * Translates and scales isolines | ||
function rescaleIsolines(createdIsoLines, matrix, points) { | ||
// get dimensions (on the map) of the original grid | ||
var gridBbox = bbox__default['default'](points); // [ minX, minY, maxX, maxY ] | ||
var originalWidth = gridBbox[2] - gridBbox[0]; | ||
var originalHeigth = gridBbox[3] - gridBbox[1]; | ||
// get origin, which is the first point of the last row on the rectangular data on the map | ||
var x0 = gridBbox[0]; | ||
var y0 = gridBbox[1]; | ||
// get number of cells per side | ||
var matrixWidth = matrix[0].length - 1; | ||
var matrixHeight = matrix.length - 1; | ||
// calculate the scaling factor between matrix and rectangular grid on the map | ||
var scaleX = originalWidth / matrixWidth; | ||
var scaleY = originalHeigth / matrixHeight; | ||
var resize = function (point) { | ||
point[0] = point[0] * scaleX + x0; | ||
point[1] = point[1] * scaleY + y0; | ||
}; | ||
// resize and shift each point/line of the createdIsoLines | ||
createdIsoLines.forEach(function (isoline) { | ||
meta.coordEach(isoline, resize); | ||
}); | ||
return createdIsoLines; | ||
// get dimensions (on the map) of the original grid | ||
const gridBbox = bbox_1.default(points); // [ minX, minY, maxX, maxY ] | ||
const originalWidth = gridBbox[2] - gridBbox[0]; | ||
const originalHeigth = gridBbox[3] - gridBbox[1]; | ||
// get origin, which is the first point of the last row on the rectangular data on the map | ||
const x0 = gridBbox[0]; | ||
const y0 = gridBbox[1]; | ||
// get number of cells per side | ||
const matrixWidth = matrix[0].length - 1; | ||
const matrixHeight = matrix.length - 1; | ||
// calculate the scaling factor between matrix and rectangular grid on the map | ||
const scaleX = originalWidth / matrixWidth; | ||
const scaleY = originalHeigth / matrixHeight; | ||
const resize = (point) => { | ||
point[0] = point[0] * scaleX + x0; | ||
point[1] = point[1] * scaleY + y0; | ||
}; | ||
// resize and shift each point/line of the createdIsoLines | ||
createdIsoLines.forEach((isoline) => { | ||
meta_1.coordEach(isoline, resize); | ||
}); | ||
return createdIsoLines; | ||
} | ||
module.exports = isolines; | ||
module.exports.default = isolines; | ||
exports.default = isolines; |
{ | ||
"name": "@turf/isolines", | ||
"version": "6.5.0", | ||
"version": "7.0.0-alpha.0", | ||
"description": "turf isolines module", | ||
@@ -39,22 +39,23 @@ "author": "Turf Authors", | ||
}, | ||
"types": "index.d.ts", | ||
"types": "dist/js/index.d.ts", | ||
"sideEffects": false, | ||
"files": [ | ||
"dist", | ||
"index.d.ts" | ||
"dist" | ||
], | ||
"scripts": { | ||
"bench": "node -r esm bench.js", | ||
"build": "rollup -c ../../rollup.config.js && echo '{\"type\":\"module\"}' > dist/es/package.json", | ||
"bench": "ts-node bench.js", | ||
"build": "npm-run-all build:*", | ||
"build:es": "tsc --outDir dist/es --module esnext --declaration false && echo '{\"type\":\"module\"}' > dist/es/package.json", | ||
"build:js": "tsc", | ||
"docs": "node ../../scripts/generate-readmes", | ||
"test": "npm-run-all test:*", | ||
"test:tape": "node -r esm test.js", | ||
"test:types": "tsc --esModuleInterop --noEmit types.ts" | ||
"test:tape": "ts-node -r esm test.js", | ||
"test:types": "tsc --esModuleInterop --noEmit --strict types.ts" | ||
}, | ||
"devDependencies": { | ||
"@turf/envelope": "^6.5.0", | ||
"@turf/point-grid": "^6.5.0", | ||
"@turf/random": "^6.5.0", | ||
"@turf/rhumb-destination": "^6.5.0", | ||
"@turf/truncate": "^6.5.0", | ||
"@turf/envelope": "^7.0.0-alpha.0", | ||
"@turf/point-grid": "^7.0.0-alpha.0", | ||
"@turf/random": "^7.0.0-alpha.0", | ||
"@turf/rhumb-destination": "^7.0.0-alpha.0", | ||
"@turf/truncate": "^7.0.0-alpha.0", | ||
"benchmark": "*", | ||
@@ -66,12 +67,15 @@ "load-json-file": "*", | ||
"tape": "*", | ||
"ts-node": "*", | ||
"tslint": "*", | ||
"typescript": "*", | ||
"write-json-file": "*" | ||
}, | ||
"dependencies": { | ||
"@turf/bbox": "^6.5.0", | ||
"@turf/helpers": "^6.5.0", | ||
"@turf/invariant": "^6.5.0", | ||
"@turf/meta": "^6.5.0", | ||
"object-assign": "*" | ||
"@turf/bbox": "^7.0.0-alpha.0", | ||
"@turf/helpers": "^7.0.0-alpha.0", | ||
"@turf/invariant": "^7.0.0-alpha.0", | ||
"@turf/meta": "^7.0.0-alpha.0", | ||
"tslib": "^2.3.0" | ||
}, | ||
"gitHead": "5375941072b90d489389db22b43bfe809d5e451e" | ||
"gitHead": "0edc4c491b999e5ace770a61e1cf549f7c004189" | ||
} |
@@ -10,13 +10,14 @@ # @turf/isolines | ||
**Parameters** | ||
### Parameters | ||
- `pointGrid` **[FeatureCollection][4]<[Point][5]>** input points | ||
- `breaks` **[Array][6]<[number][7]>** values of `zProperty` where to draw isolines | ||
- `options` **[Object][8]** Optional parameters (optional, default `{}`) | ||
- `options.zProperty` **[string][9]** the property name in `points` from which z-values will be pulled (optional, default `'elevation'`) | ||
- `options.commonProperties` **[Object][8]** GeoJSON properties passed to ALL isolines (optional, default `{}`) | ||
- `options.breaksProperties` **[Array][6]<[Object][8]>** GeoJSON properties passed, in order, to the correspondent isoline; | ||
* `pointGrid` **[FeatureCollection][4]<[Point][5]>** input points | ||
* `breaks` **[Array][6]<[number][7]>** values of `zProperty` where to draw isolines | ||
* `options` **[Object][8]** Optional parameters (optional, default `{}`) | ||
* `options.zProperty` **[string][9]** the property name in `points` from which z-values will be pulled (optional, default `'elevation'`) | ||
* `options.commonProperties` **[Object][8]** GeoJSON properties passed to ALL isolines (optional, default `{}`) | ||
* `options.breaksProperties` **[Array][6]<[Object][8]>** GeoJSON properties passed, in order, to the correspondent isoline; | ||
the breaks array will define the order in which the isolines are created (optional, default `[]`) | ||
**Examples** | ||
### Examples | ||
@@ -40,3 +41,3 @@ ```javascript | ||
Returns **[FeatureCollection][4]<[MultiLineString][10]>** a FeatureCollection of [MultiLineString][11] features representing isolines | ||
Returns **[FeatureCollection][4]<[MultiLineString][10]>** a FeatureCollection of [MultiLineString][11] features representing isolines | ||
@@ -47,3 +48,3 @@ [1]: https://tools.ietf.org/html/rfc7946#section-3.3 | ||
[3]: http://en.wikipedia.org/wiki/Isoline | ||
[3]: https://en.wikipedia.org/wiki/Contour_line | ||
@@ -50,0 +51,0 @@ [4]: https://tools.ietf.org/html/rfc7946#section-3.3 |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Copyleft License
License(Experimental) Copyleft license information was found.
Found 1 instance in 1 package
Mixed license
License(Experimental) Package contains multiple licenses.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
Non-permissive License
License(Experimental) A license not known to be considered permissive was found.
Found 1 instance in 1 package
Wildcard dependency
QualityPackage has a dependency with a floating version range. This can cause issues if the dependency publishes a new major version.
Found 1 instance in 1 package
Copyleft License
License(Experimental) Copyleft license information was found.
Found 1 instance in 1 package
Mixed license
License(Experimental) Package contains multiple licenses.
Found 1 instance in 1 package
Non-permissive License
License(Experimental) A license not known to be considered permissive was found.
Found 1 instance in 1 package
56875
13
1293
0
88
15
1
1
+ Addedtslib@^2.3.0
+ Added@turf/bbox@7.2.0(transitive)
+ Added@turf/helpers@7.2.0(transitive)
+ Added@turf/invariant@7.2.0(transitive)
+ Added@turf/meta@7.2.0(transitive)
+ Added@types/geojson@7946.0.16(transitive)
+ Addedtslib@2.8.1(transitive)
- Removedobject-assign@*
- Removed@turf/bbox@6.5.0(transitive)
- Removed@turf/helpers@6.5.0(transitive)
- Removed@turf/invariant@6.5.0(transitive)
- Removed@turf/meta@6.5.0(transitive)
- Removedobject-assign@4.1.1(transitive)
Updated@turf/bbox@^7.0.0-alpha.0
Updated@turf/helpers@^7.0.0-alpha.0
Updated@turf/meta@^7.0.0-alpha.0