"use strict";Object.defineProperty(exports, "__esModule", {value: true});// index.ts var _bbox = require('@turf/bbox'); var _booleanpointinpolygon = require('@turf/boolean-point-in-polygon'); var _distance = require('@turf/distance'); var _transformscale = require('@turf/transform-scale'); var _cleancoords = require('@turf/clean-coords'); var _bboxpolygon = require('@turf/bbox-polygon'); var _invariant = require('@turf/invariant'); var _helpers = require('@turf/helpers'); // lib/javascript-astar.js function pathTo(node) { var curr = node, path = []; while (curr.parent) { path.unshift(curr); curr = curr.parent; } return path; } function getHeap() { return new BinaryHeap(function(node) { return node.f; }); } var astar = { /** * Perform an A* Search on a graph given a start and end node. * * @private * @memberof astar * @param {Graph} graph Graph * @param {GridNode} start Start * @param {GridNode} end End * @param {Object} [options] Options * @param {bool} [options.closest] Specifies whether to return the path to the closest node if the target is unreachable. * @param {Function} [options.heuristic] Heuristic function (see astar.heuristics). * @returns {Object} Search */ search: function(graph, start, end, options) { var _a; graph.cleanDirty(); options = options || {}; var heuristic = options.heuristic || astar.heuristics.manhattan, closest = (_a = options.closest) != null ? _a : false; var openHeap = getHeap(), closestNode = start; start.h = heuristic(start, end); openHeap.push(start); while (openHeap.size() > 0) { var currentNode = openHeap.pop(); if (currentNode === end) { return pathTo(currentNode); } currentNode.closed = true; var neighbors = graph.neighbors(currentNode); for (var i = 0, il = neighbors.length; i < il; ++i) { var neighbor = neighbors[i]; if (neighbor.closed || neighbor.isWall()) { continue; } var gScore = currentNode.g + neighbor.getCost(currentNode), beenVisited = neighbor.visited; if (!beenVisited || gScore < neighbor.g) { neighbor.visited = true; neighbor.parent = currentNode; neighbor.h = neighbor.h || heuristic(neighbor, end); neighbor.g = gScore; neighbor.f = neighbor.g + neighbor.h; graph.markDirty(neighbor); if (closest) { if (neighbor.h < closestNode.h || neighbor.h === closestNode.h && neighbor.g < closestNode.g) { closestNode = neighbor; } } if (!beenVisited) { openHeap.push(neighbor); } else { openHeap.rescoreElement(neighbor); } } } } if (closest) { return pathTo(closestNode); } return []; }, // See list of heuristics: http://theory.stanford.edu/~amitp/GameProgramming/Heuristics.html heuristics: { manhattan: function(pos0, pos1) { var d1 = Math.abs(pos1.x - pos0.x); var d2 = Math.abs(pos1.y - pos0.y); return d1 + d2; }, diagonal: function(pos0, pos1) { var D = 1; var D2 = Math.sqrt(2); var d1 = Math.abs(pos1.x - pos0.x); var d2 = Math.abs(pos1.y - pos0.y); return D * (d1 + d2) + (D2 - 2 * D) * Math.min(d1, d2); } }, cleanNode: function(node) { node.f = 0; node.g = 0; node.h = 0; node.visited = false; node.closed = false; node.parent = null; } }; function Graph(gridIn, options) { options = options || {}; this.nodes = []; this.diagonal = !!options.diagonal; this.grid = []; for (var x = 0; x < gridIn.length; x++) { this.grid[x] = []; for (var y = 0, row = gridIn[x]; y < row.length; y++) { var node = new GridNode(x, y, row[y]); this.grid[x][y] = node; this.nodes.push(node); } } this.init(); } Graph.prototype.init = function() { this.dirtyNodes = []; for (var i = 0; i < this.nodes.length; i++) { astar.cleanNode(this.nodes[i]); } }; Graph.prototype.cleanDirty = function() { for (var i = 0; i < this.dirtyNodes.length; i++) { astar.cleanNode(this.dirtyNodes[i]); } this.dirtyNodes = []; }; Graph.prototype.markDirty = function(node) { this.dirtyNodes.push(node); }; Graph.prototype.neighbors = function(node) { var ret = [], x = node.x, y = node.y, grid = this.grid; if (grid[x - 1] && grid[x - 1][y]) { ret.push(grid[x - 1][y]); } if (grid[x + 1] && grid[x + 1][y]) { ret.push(grid[x + 1][y]); } if (grid[x] && grid[x][y - 1]) { ret.push(grid[x][y - 1]); } if (grid[x] && grid[x][y + 1]) { ret.push(grid[x][y + 1]); } if (this.diagonal) { if (grid[x - 1] && grid[x - 1][y - 1]) { ret.push(grid[x - 1][y - 1]); } if (grid[x + 1] && grid[x + 1][y - 1]) { ret.push(grid[x + 1][y - 1]); } if (grid[x - 1] && grid[x - 1][y + 1]) { ret.push(grid[x - 1][y + 1]); } if (grid[x + 1] && grid[x + 1][y + 1]) { ret.push(grid[x + 1][y + 1]); } } return ret; }; Graph.prototype.toString = function() { var graphString = [], nodes = this.grid, rowDebug, row, y, l; for (var x = 0, len = nodes.length; x < len; x++) { rowDebug = []; row = nodes[x]; for (y = 0, l = row.length; y < l; y++) { rowDebug.push(row[y].weight); } graphString.push(rowDebug.join(" ")); } return graphString.join("\n"); }; function GridNode(x, y, weight) { this.x = x; this.y = y; this.weight = weight; } GridNode.prototype.toString = function() { return "[" + this.x + " " + this.y + "]"; }; GridNode.prototype.getCost = function(fromNeighbor) { if (fromNeighbor && fromNeighbor.x !== this.x && fromNeighbor.y !== this.y) { return this.weight * 1.41421; } return this.weight; }; GridNode.prototype.isWall = function() { return this.weight === 0; }; function BinaryHeap(scoreFunction) { this.content = []; this.scoreFunction = scoreFunction; } BinaryHeap.prototype = { push: function(element) { this.content.push(element); this.sinkDown(this.content.length - 1); }, pop: function() { var result = this.content[0]; var end = this.content.pop(); if (this.content.length > 0) { this.content[0] = end; this.bubbleUp(0); } return result; }, remove: function(node) { var i = this.content.indexOf(node); var end = this.content.pop(); if (i !== this.content.length - 1) { this.content[i] = end; if (this.scoreFunction(end) < this.scoreFunction(node)) { this.sinkDown(i); } else { this.bubbleUp(i); } } }, size: function() { return this.content.length; }, rescoreElement: function(node) { this.sinkDown(this.content.indexOf(node)); }, sinkDown: function(n) { var element = this.content[n]; while (n > 0) { var parentN = (n + 1 >> 1) - 1, parent = this.content[parentN]; if (this.scoreFunction(element) < this.scoreFunction(parent)) { this.content[parentN] = element; this.content[n] = parent; n = parentN; } else { break; } } }, bubbleUp: function(n) { var length = this.content.length, element = this.content[n], elemScore = this.scoreFunction(element); while (true) { var child2N = n + 1 << 1, child1N = child2N - 1; var swap = null, child1Score; if (child1N < length) { var child1 = this.content[child1N]; child1Score = this.scoreFunction(child1); if (child1Score < elemScore) { swap = child1N; } } if (child2N < length) { var child2 = this.content[child2N], child2Score = this.scoreFunction(child2); if (child2Score < (swap === null ? elemScore : child1Score)) { swap = child2N; } } if (swap !== null) { this.content[n] = this.content[swap]; this.content[swap] = element; n = swap; } else { break; } } } }; // index.ts function shortestPath(start, end, options = {}) { options = options || {}; if (!_helpers.isObject.call(void 0, options)) throw new Error("options is invalid"); let obstacles = options.obstacles || _helpers.featureCollection.call(void 0, []); let resolution = options.resolution || 100; if (!start) throw new Error("start is required"); if (!end) throw new Error("end is required"); if (resolution && (!_helpers.isNumber.call(void 0, resolution) || resolution <= 0)) throw new Error("options.resolution must be a number, greater than 0"); const startCoord = _invariant.getCoord.call(void 0, start); const endCoord = _invariant.getCoord.call(void 0, end); start = _helpers.point.call(void 0, startCoord); end = _helpers.point.call(void 0, endCoord); if (obstacles.type === "FeatureCollection") { if (obstacles.features.length === 0) { return _helpers.lineString.call(void 0, [startCoord, endCoord]); } } else if (obstacles.type === "Polygon") { obstacles = _helpers.featureCollection.call(void 0, [_helpers.feature.call(void 0, _invariant.getGeom.call(void 0, obstacles))]); } else { throw new Error("invalid obstacles"); } const collection = obstacles; collection.features.push(start); collection.features.push(end); const box = _bbox.bbox.call(void 0, _transformscale.transformScale.call(void 0, _bboxpolygon.bboxPolygon.call(void 0, _bbox.bbox.call(void 0, collection)), 1.15)); const [west, south, east, north] = box; const width = _distance.distance.call(void 0, [west, south], [east, south], options); const division = width / resolution; collection.features.pop(); collection.features.pop(); const xFraction = division / _distance.distance.call(void 0, [west, south], [east, south], options); const cellWidth = xFraction * (east - west); const yFraction = division / _distance.distance.call(void 0, [west, south], [west, north], options); const cellHeight = yFraction * (north - south); const bboxHorizontalSide = east - west; const bboxVerticalSide = north - south; const columns = Math.floor(bboxHorizontalSide / cellWidth); const rows = Math.floor(bboxVerticalSide / cellHeight); const deltaX = (bboxHorizontalSide - columns * cellWidth) / 2; const deltaY = (bboxVerticalSide - rows * cellHeight) / 2; const pointMatrix = []; const matrix = []; let closestToStart; let closestToEnd; let minDistStart = Infinity; let minDistEnd = Infinity; let currentY = north - deltaY; let r = 0; while (currentY >= south) { const matrixRow = []; const pointMatrixRow = []; let currentX = west + deltaX; let c = 0; while (currentX <= east) { const pt = _helpers.point.call(void 0, [currentX, currentY]); const isInsideObstacle = isInside(pt, obstacles); matrixRow.push(isInsideObstacle ? 0 : 1); pointMatrixRow.push(currentX + "|" + currentY); const distStart = _distance.distance.call(void 0, pt, start); if (!isInsideObstacle && distStart < minDistStart) { minDistStart = distStart; closestToStart = { x: c, y: r }; } const distEnd = _distance.distance.call(void 0, pt, end); if (!isInsideObstacle && distEnd < minDistEnd) { minDistEnd = distEnd; closestToEnd = { x: c, y: r }; } currentX += cellWidth; c++; } matrix.push(matrixRow); pointMatrix.push(pointMatrixRow); currentY -= cellHeight; r++; } const graph = new Graph(matrix, { diagonal: true }); const startOnMatrix = graph.grid[closestToStart.y][closestToStart.x]; const endOnMatrix = graph.grid[closestToEnd.y][closestToEnd.x]; const result = astar.search(graph, startOnMatrix, endOnMatrix); const path = [startCoord]; result.forEach(function(coord) { const coords = pointMatrix[coord.x][coord.y].split("|"); path.push([+coords[0], +coords[1]]); }); path.push(endCoord); return _cleancoords.cleanCoords.call(void 0, _helpers.lineString.call(void 0, path)); } function isInside(pt, polygons) { for (let i = 0; i < polygons.features.length; i++) { if (_booleanpointinpolygon.booleanPointInPolygon.call(void 0, pt, polygons.features[i])) { return true; } } return false; } var turf_shortest_path_default = shortestPath; exports.default = turf_shortest_path_default; exports.shortestPath = shortestPath; //# sourceMappingURL=index.cjs.map