index.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655
  1. 'use strict';
  2. var bbox = require('@turf/bbox');
  3. var meta = require('@turf/meta');
  4. var invariant = require('@turf/invariant');
  5. var helpers = require('@turf/helpers');
  6. var objectAssign = require('object-assign');
  7. function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
  8. var bbox__default = /*#__PURE__*/_interopDefaultLegacy(bbox);
  9. var objectAssign__default = /*#__PURE__*/_interopDefaultLegacy(objectAssign);
  10. /**
  11. * @license GNU Affero General Public License.
  12. * Copyright (c) 2015, 2015 Ronny Lorenz <ronny@tbi.univie.ac.at>
  13. * v. 1.2.0
  14. * https://github.com/RaumZeit/MarchingSquares.js
  15. *
  16. * MarchingSquaresJS is free software: you can redistribute it and/or modify
  17. * it under the terms of the GNU Affero General Public License as published by
  18. * the Free Software Foundation, either version 3 of the License, or
  19. * (at your option) any later version.
  20. *
  21. * MarchingSquaresJS is distributed in the hope that it will be useful,
  22. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  23. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  24. * GNU Affero General Public License for more details.
  25. *
  26. * As additional permission under GNU Affero General Public License version 3
  27. * section 7, third-party projects (personal or commercial) may distribute,
  28. * include, or link against UNMODIFIED VERSIONS of MarchingSquaresJS without the
  29. * requirement that said third-party project for that reason alone becomes
  30. * subject to any requirement of the GNU Affero General Public License version 3.
  31. * Any modifications to MarchingSquaresJS, however, must be shared with the public
  32. * and made available.
  33. *
  34. * In summary this:
  35. * - allows you to use MarchingSquaresJS at no cost
  36. * - allows you to use MarchingSquaresJS for both personal and commercial purposes
  37. * - allows you to distribute UNMODIFIED VERSIONS of MarchingSquaresJS under any
  38. * license as long as this license notice is included
  39. * - enables you to keep the source code of your program that uses MarchingSquaresJS
  40. * undisclosed
  41. * - forces you to share any modifications you have made to MarchingSquaresJS,
  42. * e.g. bug-fixes
  43. *
  44. * You should have received a copy of the GNU Affero General Public License
  45. * along with MarchingSquaresJS. If not, see <http://www.gnu.org/licenses/>.
  46. */
  47. /**
  48. * Compute the isocontour(s) of a scalar 2D field given
  49. * a certain threshold by applying the Marching Squares
  50. * Algorithm. The function returns a list of path coordinates
  51. */
  52. var defaultSettings = {
  53. successCallback: null,
  54. verbose: false,
  55. };
  56. var settings = {};
  57. function isoContours(data, threshold, options) {
  58. /* process options */
  59. options = options ? options : {};
  60. var optionKeys = Object.keys(defaultSettings);
  61. for (var i = 0; i < optionKeys.length; i++) {
  62. var key = optionKeys[i];
  63. var val = options[key];
  64. val =
  65. typeof val !== "undefined" && val !== null ? val : defaultSettings[key];
  66. settings[key] = val;
  67. }
  68. if (settings.verbose)
  69. console.log(
  70. "MarchingSquaresJS-isoContours: computing isocontour for " + threshold
  71. );
  72. var ret = contourGrid2Paths(computeContourGrid(data, threshold));
  73. if (typeof settings.successCallback === "function")
  74. settings.successCallback(ret);
  75. return ret;
  76. }
  77. /*
  78. Thats all for the public interface, below follows the actual
  79. implementation
  80. */
  81. /*
  82. ################################
  83. Isocontour implementation below
  84. ################################
  85. */
  86. /* assume that x1 == 1 && x0 == 0 */
  87. function interpolateX(y, y0, y1) {
  88. return (y - y0) / (y1 - y0);
  89. }
  90. /* compute the isocontour 4-bit grid */
  91. function computeContourGrid(data, threshold) {
  92. var rows = data.length - 1;
  93. var cols = data[0].length - 1;
  94. var ContourGrid = { rows: rows, cols: cols, cells: [] };
  95. for (var j = 0; j < rows; ++j) {
  96. ContourGrid.cells[j] = [];
  97. for (var i = 0; i < cols; ++i) {
  98. /* compose the 4-bit corner representation */
  99. var cval = 0;
  100. var tl = data[j + 1][i];
  101. var tr = data[j + 1][i + 1];
  102. var br = data[j][i + 1];
  103. var bl = data[j][i];
  104. if (isNaN(tl) || isNaN(tr) || isNaN(br) || isNaN(bl)) {
  105. continue;
  106. }
  107. cval |= tl >= threshold ? 8 : 0;
  108. cval |= tr >= threshold ? 4 : 0;
  109. cval |= br >= threshold ? 2 : 0;
  110. cval |= bl >= threshold ? 1 : 0;
  111. /* resolve ambiguity for cval == 5 || 10 via averaging */
  112. var flipped = false;
  113. if (cval === 5 || cval === 10) {
  114. var average = (tl + tr + br + bl) / 4;
  115. if (cval === 5 && average < threshold) {
  116. cval = 10;
  117. flipped = true;
  118. } else if (cval === 10 && average < threshold) {
  119. cval = 5;
  120. flipped = true;
  121. }
  122. }
  123. /* add cell to ContourGrid if it contains edges */
  124. if (cval !== 0 && cval !== 15) {
  125. var top, bottom, left, right;
  126. top = bottom = left = right = 0.5;
  127. /* interpolate edges of cell */
  128. if (cval === 1) {
  129. left = 1 - interpolateX(threshold, tl, bl);
  130. bottom = 1 - interpolateX(threshold, br, bl);
  131. } else if (cval === 2) {
  132. bottom = interpolateX(threshold, bl, br);
  133. right = 1 - interpolateX(threshold, tr, br);
  134. } else if (cval === 3) {
  135. left = 1 - interpolateX(threshold, tl, bl);
  136. right = 1 - interpolateX(threshold, tr, br);
  137. } else if (cval === 4) {
  138. top = interpolateX(threshold, tl, tr);
  139. right = interpolateX(threshold, br, tr);
  140. } else if (cval === 5) {
  141. top = interpolateX(threshold, tl, tr);
  142. right = interpolateX(threshold, br, tr);
  143. bottom = 1 - interpolateX(threshold, br, bl);
  144. left = 1 - interpolateX(threshold, tl, bl);
  145. } else if (cval === 6) {
  146. bottom = interpolateX(threshold, bl, br);
  147. top = interpolateX(threshold, tl, tr);
  148. } else if (cval === 7) {
  149. left = 1 - interpolateX(threshold, tl, bl);
  150. top = interpolateX(threshold, tl, tr);
  151. } else if (cval === 8) {
  152. left = interpolateX(threshold, bl, tl);
  153. top = 1 - interpolateX(threshold, tr, tl);
  154. } else if (cval === 9) {
  155. bottom = 1 - interpolateX(threshold, br, bl);
  156. top = 1 - interpolateX(threshold, tr, tl);
  157. } else if (cval === 10) {
  158. top = 1 - interpolateX(threshold, tr, tl);
  159. right = 1 - interpolateX(threshold, tr, br);
  160. bottom = interpolateX(threshold, bl, br);
  161. left = interpolateX(threshold, bl, tl);
  162. } else if (cval === 11) {
  163. top = 1 - interpolateX(threshold, tr, tl);
  164. right = 1 - interpolateX(threshold, tr, br);
  165. } else if (cval === 12) {
  166. left = interpolateX(threshold, bl, tl);
  167. right = interpolateX(threshold, br, tr);
  168. } else if (cval === 13) {
  169. bottom = 1 - interpolateX(threshold, br, bl);
  170. right = interpolateX(threshold, br, tr);
  171. } else if (cval === 14) {
  172. left = interpolateX(threshold, bl, tl);
  173. bottom = interpolateX(threshold, bl, br);
  174. } else {
  175. console.log(
  176. "MarchingSquaresJS-isoContours: Illegal cval detected: " + cval
  177. );
  178. }
  179. ContourGrid.cells[j][i] = {
  180. cval: cval,
  181. flipped: flipped,
  182. top: top,
  183. right: right,
  184. bottom: bottom,
  185. left: left,
  186. };
  187. }
  188. }
  189. }
  190. return ContourGrid;
  191. }
  192. function isSaddle(cell) {
  193. return cell.cval === 5 || cell.cval === 10;
  194. }
  195. function isTrivial(cell) {
  196. return cell.cval === 0 || cell.cval === 15;
  197. }
  198. function clearCell(cell) {
  199. if (!isTrivial(cell) && cell.cval !== 5 && cell.cval !== 10) {
  200. cell.cval = 15;
  201. }
  202. }
  203. function getXY(cell, edge) {
  204. if (edge === "top") {
  205. return [cell.top, 1.0];
  206. } else if (edge === "bottom") {
  207. return [cell.bottom, 0.0];
  208. } else if (edge === "right") {
  209. return [1.0, cell.right];
  210. } else if (edge === "left") {
  211. return [0.0, cell.left];
  212. }
  213. }
  214. function contourGrid2Paths(grid) {
  215. var paths = [];
  216. var path_idx = 0;
  217. var epsilon = 1e-7;
  218. grid.cells.forEach(function (g, j) {
  219. g.forEach(function (gg, i) {
  220. if (typeof gg !== "undefined" && !isSaddle(gg) && !isTrivial(gg)) {
  221. var p = tracePath(grid.cells, j, i);
  222. var merged = false;
  223. /* we may try to merge paths at this point */
  224. if (p.info === "mergeable") {
  225. /*
  226. search backwards through the path array to find an entry
  227. that starts with where the current path ends...
  228. */
  229. var x = p.path[p.path.length - 1][0],
  230. y = p.path[p.path.length - 1][1];
  231. for (var k = path_idx - 1; k >= 0; k--) {
  232. if (
  233. Math.abs(paths[k][0][0] - x) <= epsilon &&
  234. Math.abs(paths[k][0][1] - y) <= epsilon
  235. ) {
  236. for (var l = p.path.length - 2; l >= 0; --l) {
  237. paths[k].unshift(p.path[l]);
  238. }
  239. merged = true;
  240. break;
  241. }
  242. }
  243. }
  244. if (!merged) paths[path_idx++] = p.path;
  245. }
  246. });
  247. });
  248. return paths;
  249. }
  250. /*
  251. construct consecutive line segments from starting cell by
  252. walking arround the enclosed area clock-wise
  253. */
  254. function tracePath(grid, j, i) {
  255. var maxj = grid.length;
  256. var p = [];
  257. var dxContour = [0, 0, 1, 1, 0, 0, 0, 0, -1, 0, 1, 1, -1, 0, -1, 0];
  258. var dyContour = [0, -1, 0, 0, 1, 1, 1, 1, 0, -1, 0, 0, 0, -1, 0, 0];
  259. var dx, dy;
  260. var startEdge = [
  261. "none",
  262. "left",
  263. "bottom",
  264. "left",
  265. "right",
  266. "none",
  267. "bottom",
  268. "left",
  269. "top",
  270. "top",
  271. "none",
  272. "top",
  273. "right",
  274. "right",
  275. "bottom",
  276. "none",
  277. ];
  278. var nextEdge = [
  279. "none",
  280. "bottom",
  281. "right",
  282. "right",
  283. "top",
  284. "top",
  285. "top",
  286. "top",
  287. "left",
  288. "bottom",
  289. "right",
  290. "right",
  291. "left",
  292. "bottom",
  293. "left",
  294. "none",
  295. ];
  296. var edge;
  297. var currentCell = grid[j][i];
  298. var cval = currentCell.cval;
  299. var edge = startEdge[cval];
  300. var pt = getXY(currentCell, edge);
  301. /* push initial segment */
  302. p.push([i + pt[0], j + pt[1]]);
  303. edge = nextEdge[cval];
  304. pt = getXY(currentCell, edge);
  305. p.push([i + pt[0], j + pt[1]]);
  306. clearCell(currentCell);
  307. /* now walk arround the enclosed area in clockwise-direction */
  308. var k = i + dxContour[cval];
  309. var l = j + dyContour[cval];
  310. var prev_cval = cval;
  311. while (k >= 0 && l >= 0 && l < maxj && (k != i || l != j)) {
  312. currentCell = grid[l][k];
  313. if (typeof currentCell === "undefined") {
  314. /* path ends here */
  315. //console.log(k + " " + l + " is undefined, stopping path!");
  316. break;
  317. }
  318. cval = currentCell.cval;
  319. if (cval === 0 || cval === 15) {
  320. return { path: p, info: "mergeable" };
  321. }
  322. edge = nextEdge[cval];
  323. dx = dxContour[cval];
  324. dy = dyContour[cval];
  325. if (cval === 5 || cval === 10) {
  326. /* select upper or lower band, depending on previous cells cval */
  327. if (cval === 5) {
  328. if (currentCell.flipped) {
  329. /* this is actually a flipped case 10 */
  330. if (dyContour[prev_cval] === -1) {
  331. edge = "left";
  332. dx = -1;
  333. dy = 0;
  334. } else {
  335. edge = "right";
  336. dx = 1;
  337. dy = 0;
  338. }
  339. } else {
  340. /* real case 5 */
  341. if (dxContour[prev_cval] === -1) {
  342. edge = "bottom";
  343. dx = 0;
  344. dy = -1;
  345. }
  346. }
  347. } else if (cval === 10) {
  348. if (currentCell.flipped) {
  349. /* this is actually a flipped case 5 */
  350. if (dxContour[prev_cval] === -1) {
  351. edge = "top";
  352. dx = 0;
  353. dy = 1;
  354. } else {
  355. edge = "bottom";
  356. dx = 0;
  357. dy = -1;
  358. }
  359. } else {
  360. /* real case 10 */
  361. if (dyContour[prev_cval] === 1) {
  362. edge = "left";
  363. dx = -1;
  364. dy = 0;
  365. }
  366. }
  367. }
  368. }
  369. pt = getXY(currentCell, edge);
  370. p.push([k + pt[0], l + pt[1]]);
  371. clearCell(currentCell);
  372. k += dx;
  373. l += dy;
  374. prev_cval = cval;
  375. }
  376. return { path: p, info: "closed" };
  377. }
  378. /**
  379. * Takes a {@link Point} grid and returns a correspondent matrix {Array<Array<number>>}
  380. * of the 'property' values
  381. *
  382. * @name gridToMatrix
  383. * @param {FeatureCollection<Point>} grid of points
  384. * @param {Object} [options={}] Optional parameters
  385. * @param {string} [options.zProperty='elevation'] the property name in `points` from which z-values will be pulled
  386. * @param {boolean} [options.flip=false] returns the matrix upside-down
  387. * @param {boolean} [options.flags=false] flags, adding a `matrixPosition` array field ([row, column]) to its properties,
  388. * the grid points with coordinates on the matrix
  389. * @returns {Array<Array<number>>} matrix of property values
  390. * @example
  391. * var extent = [-70.823364, -33.553984, -70.473175, -33.302986];
  392. * var cellSize = 3;
  393. * var grid = turf.pointGrid(extent, cellSize);
  394. * // add a random property to each point between 0 and 60
  395. * for (var i = 0; i < grid.features.length; i++) {
  396. * grid.features[i].properties.elevation = (Math.random() * 60);
  397. * }
  398. * gridToMatrix(grid);
  399. * //= [
  400. * [ 1, 13, 10, 9, 10, 13, 18],
  401. * [34, 8, 5, 4, 5, 8, 13],
  402. * [10, 5, 2, 1, 2, 5, 4],
  403. * [ 0, 4, 56, 19, 1, 4, 9],
  404. * [10, 5, 2, 1, 2, 5, 10],
  405. * [57, 8, 5, 4, 5, 0, 57],
  406. * [ 3, 13, 10, 9, 5, 13, 18],
  407. * [18, 13, 10, 9, 78, 13, 18]
  408. * ]
  409. */
  410. function gridToMatrix(grid, options) {
  411. // Optional parameters
  412. options = options || {};
  413. if (!helpers.isObject(options)) throw new Error("options is invalid");
  414. var zProperty = options.zProperty || "elevation";
  415. var flip = options.flip;
  416. var flags = options.flags;
  417. // validation
  418. invariant.collectionOf(grid, "Point", "input must contain Points");
  419. var pointsMatrix = sortPointsByLatLng(grid, flip);
  420. var matrix = [];
  421. // create property matrix from sorted points
  422. // looping order matters here
  423. for (var r = 0; r < pointsMatrix.length; r++) {
  424. var pointRow = pointsMatrix[r];
  425. var row = [];
  426. for (var c = 0; c < pointRow.length; c++) {
  427. var point = pointRow[c];
  428. // Check if zProperty exist
  429. if (point.properties[zProperty]) row.push(point.properties[zProperty]);
  430. else row.push(0);
  431. // add flags
  432. if (flags === true) point.properties.matrixPosition = [r, c];
  433. }
  434. matrix.push(row);
  435. }
  436. return matrix;
  437. }
  438. /**
  439. * Sorts points by latitude and longitude, creating a 2-dimensional array of points
  440. *
  441. * @private
  442. * @param {FeatureCollection<Point>} points GeoJSON Point features
  443. * @param {boolean} [flip=false] returns the matrix upside-down
  444. * @returns {Array<Array<Point>>} points ordered by latitude and longitude
  445. */
  446. function sortPointsByLatLng(points, flip) {
  447. var pointsByLatitude = {};
  448. // divide points by rows with the same latitude
  449. meta.featureEach(points, function (point) {
  450. var lat = invariant.getCoords(point)[1];
  451. if (!pointsByLatitude[lat]) pointsByLatitude[lat] = [];
  452. pointsByLatitude[lat].push(point);
  453. });
  454. // sort points (with the same latitude) by longitude
  455. var orderedRowsByLatitude = Object.keys(pointsByLatitude).map(function (lat) {
  456. var row = pointsByLatitude[lat];
  457. var rowOrderedByLongitude = row.sort(function (a, b) {
  458. return invariant.getCoords(a)[0] - invariant.getCoords(b)[0];
  459. });
  460. return rowOrderedByLongitude;
  461. });
  462. // sort rows (of points with the same latitude) by latitude
  463. var pointMatrix = orderedRowsByLatitude.sort(function (a, b) {
  464. if (flip) return invariant.getCoords(a[0])[1] - invariant.getCoords(b[0])[1];
  465. else return invariant.getCoords(b[0])[1] - invariant.getCoords(a[0])[1];
  466. });
  467. return pointMatrix;
  468. }
  469. /**
  470. * Takes a grid {@link FeatureCollection} of {@link Point} features with z-values and an array of
  471. * value breaks and generates [isolines](https://en.wikipedia.org/wiki/Contour_line).
  472. *
  473. * @name isolines
  474. * @param {FeatureCollection<Point>} pointGrid input points
  475. * @param {Array<number>} breaks values of `zProperty` where to draw isolines
  476. * @param {Object} [options={}] Optional parameters
  477. * @param {string} [options.zProperty='elevation'] the property name in `points` from which z-values will be pulled
  478. * @param {Object} [options.commonProperties={}] GeoJSON properties passed to ALL isolines
  479. * @param {Array<Object>} [options.breaksProperties=[]] GeoJSON properties passed, in order, to the correspondent isoline;
  480. * the breaks array will define the order in which the isolines are created
  481. * @returns {FeatureCollection<MultiLineString>} a FeatureCollection of {@link MultiLineString} features representing isolines
  482. * @example
  483. * // create a grid of points with random z-values in their properties
  484. * var extent = [0, 30, 20, 50];
  485. * var cellWidth = 100;
  486. * var pointGrid = turf.pointGrid(extent, cellWidth, {units: 'miles'});
  487. *
  488. * for (var i = 0; i < pointGrid.features.length; i++) {
  489. * pointGrid.features[i].properties.temperature = Math.random() * 10;
  490. * }
  491. * var breaks = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
  492. *
  493. * var lines = turf.isolines(pointGrid, breaks, {zProperty: 'temperature'});
  494. *
  495. * //addToMap
  496. * var addToMap = [lines];
  497. */
  498. function isolines(pointGrid, breaks, options) {
  499. // Optional parameters
  500. options = options || {};
  501. if (!helpers.isObject(options)) throw new Error("options is invalid");
  502. var zProperty = options.zProperty || "elevation";
  503. var commonProperties = options.commonProperties || {};
  504. var breaksProperties = options.breaksProperties || [];
  505. // Input validation
  506. invariant.collectionOf(pointGrid, "Point", "Input must contain Points");
  507. if (!breaks) throw new Error("breaks is required");
  508. if (!Array.isArray(breaks)) throw new Error("breaks must be an Array");
  509. if (!helpers.isObject(commonProperties))
  510. throw new Error("commonProperties must be an Object");
  511. if (!Array.isArray(breaksProperties))
  512. throw new Error("breaksProperties must be an Array");
  513. // Isoline methods
  514. var matrix = gridToMatrix(pointGrid, { zProperty: zProperty, flip: true });
  515. var createdIsoLines = createIsoLines(
  516. matrix,
  517. breaks,
  518. zProperty,
  519. commonProperties,
  520. breaksProperties
  521. );
  522. var scaledIsolines = rescaleIsolines(createdIsoLines, matrix, pointGrid);
  523. return helpers.featureCollection(scaledIsolines);
  524. }
  525. /**
  526. * Creates the isolines lines (featuresCollection of MultiLineString features) from the 2D data grid
  527. *
  528. * Marchingsquares process the grid data as a 3D representation of a function on a 2D plane, therefore it
  529. * assumes the points (x-y coordinates) are one 'unit' distance. The result of the isolines function needs to be
  530. * rescaled, with turfjs, to the original area and proportions on the map
  531. *
  532. * @private
  533. * @param {Array<Array<number>>} matrix Grid Data
  534. * @param {Array<number>} breaks Breaks
  535. * @param {string} zProperty name of the z-values property
  536. * @param {Object} [commonProperties={}] GeoJSON properties passed to ALL isolines
  537. * @param {Object} [breaksProperties=[]] GeoJSON properties passed to the correspondent isoline
  538. * @returns {Array<MultiLineString>} isolines
  539. */
  540. function createIsoLines(
  541. matrix,
  542. breaks,
  543. zProperty,
  544. commonProperties,
  545. breaksProperties
  546. ) {
  547. var results = [];
  548. for (var i = 1; i < breaks.length; i++) {
  549. var threshold = +breaks[i]; // make sure it's a number
  550. var properties = objectAssign__default['default']({}, commonProperties, breaksProperties[i]);
  551. properties[zProperty] = threshold;
  552. var isoline = helpers.multiLineString(isoContours(matrix, threshold), properties);
  553. results.push(isoline);
  554. }
  555. return results;
  556. }
  557. /**
  558. * Translates and scales isolines
  559. *
  560. * @private
  561. * @param {Array<MultiLineString>} createdIsoLines to be rescaled
  562. * @param {Array<Array<number>>} matrix Grid Data
  563. * @param {Object} points Points by Latitude
  564. * @returns {Array<MultiLineString>} isolines
  565. */
  566. function rescaleIsolines(createdIsoLines, matrix, points) {
  567. // get dimensions (on the map) of the original grid
  568. var gridBbox = bbox__default['default'](points); // [ minX, minY, maxX, maxY ]
  569. var originalWidth = gridBbox[2] - gridBbox[0];
  570. var originalHeigth = gridBbox[3] - gridBbox[1];
  571. // get origin, which is the first point of the last row on the rectangular data on the map
  572. var x0 = gridBbox[0];
  573. var y0 = gridBbox[1];
  574. // get number of cells per side
  575. var matrixWidth = matrix[0].length - 1;
  576. var matrixHeight = matrix.length - 1;
  577. // calculate the scaling factor between matrix and rectangular grid on the map
  578. var scaleX = originalWidth / matrixWidth;
  579. var scaleY = originalHeigth / matrixHeight;
  580. var resize = function (point) {
  581. point[0] = point[0] * scaleX + x0;
  582. point[1] = point[1] * scaleY + y0;
  583. };
  584. // resize and shift each point/line of the createdIsoLines
  585. createdIsoLines.forEach(function (isoline) {
  586. meta.coordEach(isoline, resize);
  587. });
  588. return createdIsoLines;
  589. }
  590. module.exports = isolines;
  591. module.exports.default = isolines;