@turf/isolines
Version:
Generate contour lines from a grid of data.
316 lines (307 loc) • 9.97 kB
JavaScript
;Object.defineProperty(exports, "__esModule", {value: true});var __defProp = Object.defineProperty;
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __propIsEnum = Object.prototype.propertyIsEnumerable;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __spreadValues = (a, b) => {
for (var prop in b || (b = {}))
if (__hasOwnProp.call(b, prop))
__defNormalProp(a, prop, b[prop]);
if (__getOwnPropSymbols)
for (var prop of __getOwnPropSymbols(b)) {
if (__propIsEnum.call(b, prop))
__defNormalProp(a, prop, b[prop]);
}
return a;
};
// index.ts
var _bbox = require('@turf/bbox');
var _meta = require('@turf/meta');
var _invariant = require('@turf/invariant');
var _helpers = require('@turf/helpers');
// lib/grid-to-matrix.ts
function gridToMatrix(grid, options = {}) {
if (!_helpers.isObject.call(void 0, options)) throw new Error("options is invalid");
const { zProperty = "elevation", flip = false, flags = false } = options;
_invariant.collectionOf.call(void 0, grid, "Point", "input must contain Points");
var pointsMatrix = sortPointsByLatLng(grid, flip);
var matrix = [];
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];
if (point.properties == null) {
point.properties = {};
}
if (point.properties[zProperty]) row.push(point.properties[zProperty]);
else row.push(0);
if (flags === true) point.properties.matrixPosition = [r, c];
}
matrix.push(row);
}
return matrix;
}
function sortPointsByLatLng(points, flip) {
var pointsByLatitude = {};
_meta.featureEach.call(void 0, points, (point) => {
var lat = _invariant.getCoords.call(void 0, point)[1];
if (!pointsByLatitude[lat]) pointsByLatitude[lat] = [];
pointsByLatitude[lat].push(point);
});
const pointMatrix = [];
for (const row of Object.values(pointsByLatitude)) {
pointMatrix.push(row.sort((a, b) => _invariant.getCoords.call(void 0, a)[0] - _invariant.getCoords.call(void 0, b)[0]));
}
pointMatrix.sort(
flip ? (a, b) => _invariant.getCoords.call(void 0, a[0])[1] - _invariant.getCoords.call(void 0, b[0])[1] : (a, b) => _invariant.getCoords.call(void 0, b[0])[1] - _invariant.getCoords.call(void 0, a[0])[1]
);
return pointMatrix;
}
// index.ts
function isolines(pointGrid, breaks, options) {
options = options || {};
if (!_helpers.isObject.call(void 0, options)) throw new Error("options is invalid");
const zProperty = options.zProperty || "elevation";
const commonProperties = options.commonProperties || {};
const breaksProperties = options.breaksProperties || [];
_invariant.collectionOf.call(void 0, 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.call(void 0, commonProperties))
throw new Error("commonProperties must be an Object");
if (!Array.isArray(breaksProperties))
throw new Error("breaksProperties must be an Array");
const matrix = gridToMatrix(pointGrid, { zProperty, flip: true });
const dx = matrix[0].length;
if (matrix.length < 2 || dx < 2) {
throw new Error("Matrix of points must be at least 2x2");
}
for (let i = 1; i < matrix.length; i++) {
if (matrix[i].length !== dx) {
throw new Error("Matrix of points is not uniform in the x dimension");
}
}
const createdIsoLines = createIsoLines(
matrix,
breaks,
zProperty,
commonProperties,
breaksProperties
);
const scaledIsolines = rescaleIsolines(createdIsoLines, matrix, pointGrid);
return _helpers.featureCollection.call(void 0, scaledIsolines);
}
function createIsoLines(matrix, breaks, zProperty, commonProperties, breaksProperties) {
const results = [];
for (let i = 0; i < breaks.length; i++) {
const threshold = +breaks[i];
const properties = __spreadValues(__spreadValues({}, commonProperties), breaksProperties[i]);
properties[zProperty] = threshold;
const isoline = _helpers.multiLineString.call(void 0, isoContours(matrix, threshold), properties);
results.push(isoline);
}
return results;
}
function isoContours(matrix, threshold) {
const segments = [];
const dy = matrix.length;
const dx = matrix[0].length;
for (let y = 0; y < dy - 1; y++) {
for (let x = 0; x < dx - 1; x++) {
const tr = matrix[y + 1][x + 1];
const br = matrix[y][x + 1];
const bl = matrix[y][x];
const tl = matrix[y + 1][x];
let grid = (tl >= threshold ? 8 : 0) | (tr >= threshold ? 4 : 0) | (br >= threshold ? 2 : 0) | (bl >= threshold ? 1 : 0);
switch (grid) {
case 0:
continue;
case 1:
segments.push([
[x + frac(bl, br), y],
[x, y + frac(bl, tl)]
]);
break;
case 2:
segments.push([
[x + 1, y + frac(br, tr)],
[x + frac(bl, br), y]
]);
break;
case 3:
segments.push([
[x + 1, y + frac(br, tr)],
[x, y + frac(bl, tl)]
]);
break;
case 4:
segments.push([
[x + frac(tl, tr), y + 1],
[x + 1, y + frac(br, tr)]
]);
break;
case 5: {
const avg = (tl + tr + br + bl) / 4;
const above = avg >= threshold;
if (above) {
segments.push(
[
[x + frac(tl, tr), y + 1],
[x, y + frac(bl, tl)]
],
[
[x + frac(bl, br), y],
[x + 1, y + frac(br, tr)]
]
);
} else {
segments.push(
[
[x + frac(tl, tr), y + 1],
[x + 1, y + frac(br, tr)]
],
[
[x + frac(bl, br), y],
[x, y + frac(bl, tl)]
]
);
}
break;
}
case 6:
segments.push([
[x + frac(tl, tr), y + 1],
[x + frac(bl, br), y]
]);
break;
case 7:
segments.push([
[x + frac(tl, tr), y + 1],
[x, y + frac(bl, tl)]
]);
break;
case 8:
segments.push([
[x, y + frac(bl, tl)],
[x + frac(tl, tr), y + 1]
]);
break;
case 9:
segments.push([
[x + frac(bl, br), y],
[x + frac(tl, tr), y + 1]
]);
break;
case 10: {
const avg = (tl + tr + br + bl) / 4;
const above = avg >= threshold;
if (above) {
segments.push(
[
[x, y + frac(bl, tl)],
[x + frac(bl, br), y]
],
[
[x + 1, y + frac(br, tr)],
[x + frac(tl, tr), y + 1]
]
);
} else {
segments.push(
[
[x, y + frac(bl, tl)],
[x + frac(tl, tr), y + 1]
],
[
[x + 1, y + frac(br, tr)],
[x + frac(bl, br), y]
]
);
}
break;
}
case 11:
segments.push([
[x + 1, y + frac(br, tr)],
[x + frac(tl, tr), y + 1]
]);
break;
case 12:
segments.push([
[x, y + frac(bl, tl)],
[x + 1, y + frac(br, tr)]
]);
break;
case 13:
segments.push([
[x + frac(bl, br), y],
[x + 1, y + frac(br, tr)]
]);
break;
case 14:
segments.push([
[x, y + frac(bl, tl)],
[x + frac(bl, br), y]
]);
break;
case 15:
continue;
}
}
}
const contours = [];
while (segments.length > 0) {
const contour = [...segments.shift()];
contours.push(contour);
let found;
do {
found = false;
for (let i = 0; i < segments.length; i++) {
const segment = segments[i];
if (segment[0][0] === contour[contour.length - 1][0] && segment[0][1] === contour[contour.length - 1][1]) {
found = true;
contour.push(segment[1]);
segments.splice(i, 1);
break;
}
if (segment[1][0] === contour[0][0] && segment[1][1] === contour[0][1]) {
found = true;
contour.unshift(segment[0]);
segments.splice(i, 1);
break;
}
}
} while (found);
}
return contours;
function frac(z0, z1) {
if (z0 === z1) {
return 0.5;
}
let t = (threshold - z0) / (z1 - z0);
return t > 1 ? 1 : t < 0 ? 0 : t;
}
}
function rescaleIsolines(createdIsoLines, matrix, points) {
const gridBbox = _bbox.bbox.call(void 0, points);
const originalWidth = gridBbox[2] - gridBbox[0];
const originalHeigth = gridBbox[3] - gridBbox[1];
const x0 = gridBbox[0];
const y0 = gridBbox[1];
const matrixWidth = matrix[0].length - 1;
const matrixHeight = matrix.length - 1;
const scaleX = originalWidth / matrixWidth;
const scaleY = originalHeigth / matrixHeight;
const resize = (point) => {
point[0] = point[0] * scaleX + x0;
point[1] = point[1] * scaleY + y0;
};
createdIsoLines.forEach((isoline) => {
_meta.coordEach.call(void 0, isoline, resize);
});
return createdIsoLines;
}
var index_default = isolines;
exports.default = index_default; exports.isolines = isolines;
//# sourceMappingURL=index.cjs.map