index.cjs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  1. "use strict";Object.defineProperty(exports, "__esModule", {value: true});// index.ts
  2. var _bbox = require('@turf/bbox');
  3. var _booleanpointinpolygon = require('@turf/boolean-point-in-polygon');
  4. var _distance = require('@turf/distance');
  5. var _transformscale = require('@turf/transform-scale');
  6. var _cleancoords = require('@turf/clean-coords');
  7. var _bboxpolygon = require('@turf/bbox-polygon');
  8. var _invariant = require('@turf/invariant');
  9. var _helpers = require('@turf/helpers');
  10. // lib/javascript-astar.js
  11. function pathTo(node) {
  12. var curr = node, path = [];
  13. while (curr.parent) {
  14. path.unshift(curr);
  15. curr = curr.parent;
  16. }
  17. return path;
  18. }
  19. function getHeap() {
  20. return new BinaryHeap(function(node) {
  21. return node.f;
  22. });
  23. }
  24. var astar = {
  25. /**
  26. * Perform an A* Search on a graph given a start and end node.
  27. *
  28. * @private
  29. * @memberof astar
  30. * @param {Graph} graph Graph
  31. * @param {GridNode} start Start
  32. * @param {GridNode} end End
  33. * @param {Object} [options] Options
  34. * @param {bool} [options.closest] Specifies whether to return the path to the closest node if the target is unreachable.
  35. * @param {Function} [options.heuristic] Heuristic function (see astar.heuristics).
  36. * @returns {Object} Search
  37. */
  38. search: function(graph, start, end, options) {
  39. var _a;
  40. graph.cleanDirty();
  41. options = options || {};
  42. var heuristic = options.heuristic || astar.heuristics.manhattan, closest = (_a = options.closest) != null ? _a : false;
  43. var openHeap = getHeap(), closestNode = start;
  44. start.h = heuristic(start, end);
  45. openHeap.push(start);
  46. while (openHeap.size() > 0) {
  47. var currentNode = openHeap.pop();
  48. if (currentNode === end) {
  49. return pathTo(currentNode);
  50. }
  51. currentNode.closed = true;
  52. var neighbors = graph.neighbors(currentNode);
  53. for (var i = 0, il = neighbors.length; i < il; ++i) {
  54. var neighbor = neighbors[i];
  55. if (neighbor.closed || neighbor.isWall()) {
  56. continue;
  57. }
  58. var gScore = currentNode.g + neighbor.getCost(currentNode), beenVisited = neighbor.visited;
  59. if (!beenVisited || gScore < neighbor.g) {
  60. neighbor.visited = true;
  61. neighbor.parent = currentNode;
  62. neighbor.h = neighbor.h || heuristic(neighbor, end);
  63. neighbor.g = gScore;
  64. neighbor.f = neighbor.g + neighbor.h;
  65. graph.markDirty(neighbor);
  66. if (closest) {
  67. if (neighbor.h < closestNode.h || neighbor.h === closestNode.h && neighbor.g < closestNode.g) {
  68. closestNode = neighbor;
  69. }
  70. }
  71. if (!beenVisited) {
  72. openHeap.push(neighbor);
  73. } else {
  74. openHeap.rescoreElement(neighbor);
  75. }
  76. }
  77. }
  78. }
  79. if (closest) {
  80. return pathTo(closestNode);
  81. }
  82. return [];
  83. },
  84. // See list of heuristics: http://theory.stanford.edu/~amitp/GameProgramming/Heuristics.html
  85. heuristics: {
  86. manhattan: function(pos0, pos1) {
  87. var d1 = Math.abs(pos1.x - pos0.x);
  88. var d2 = Math.abs(pos1.y - pos0.y);
  89. return d1 + d2;
  90. },
  91. diagonal: function(pos0, pos1) {
  92. var D = 1;
  93. var D2 = Math.sqrt(2);
  94. var d1 = Math.abs(pos1.x - pos0.x);
  95. var d2 = Math.abs(pos1.y - pos0.y);
  96. return D * (d1 + d2) + (D2 - 2 * D) * Math.min(d1, d2);
  97. }
  98. },
  99. cleanNode: function(node) {
  100. node.f = 0;
  101. node.g = 0;
  102. node.h = 0;
  103. node.visited = false;
  104. node.closed = false;
  105. node.parent = null;
  106. }
  107. };
  108. function Graph(gridIn, options) {
  109. options = options || {};
  110. this.nodes = [];
  111. this.diagonal = !!options.diagonal;
  112. this.grid = [];
  113. for (var x = 0; x < gridIn.length; x++) {
  114. this.grid[x] = [];
  115. for (var y = 0, row = gridIn[x]; y < row.length; y++) {
  116. var node = new GridNode(x, y, row[y]);
  117. this.grid[x][y] = node;
  118. this.nodes.push(node);
  119. }
  120. }
  121. this.init();
  122. }
  123. Graph.prototype.init = function() {
  124. this.dirtyNodes = [];
  125. for (var i = 0; i < this.nodes.length; i++) {
  126. astar.cleanNode(this.nodes[i]);
  127. }
  128. };
  129. Graph.prototype.cleanDirty = function() {
  130. for (var i = 0; i < this.dirtyNodes.length; i++) {
  131. astar.cleanNode(this.dirtyNodes[i]);
  132. }
  133. this.dirtyNodes = [];
  134. };
  135. Graph.prototype.markDirty = function(node) {
  136. this.dirtyNodes.push(node);
  137. };
  138. Graph.prototype.neighbors = function(node) {
  139. var ret = [], x = node.x, y = node.y, grid = this.grid;
  140. if (grid[x - 1] && grid[x - 1][y]) {
  141. ret.push(grid[x - 1][y]);
  142. }
  143. if (grid[x + 1] && grid[x + 1][y]) {
  144. ret.push(grid[x + 1][y]);
  145. }
  146. if (grid[x] && grid[x][y - 1]) {
  147. ret.push(grid[x][y - 1]);
  148. }
  149. if (grid[x] && grid[x][y + 1]) {
  150. ret.push(grid[x][y + 1]);
  151. }
  152. if (this.diagonal) {
  153. if (grid[x - 1] && grid[x - 1][y - 1]) {
  154. ret.push(grid[x - 1][y - 1]);
  155. }
  156. if (grid[x + 1] && grid[x + 1][y - 1]) {
  157. ret.push(grid[x + 1][y - 1]);
  158. }
  159. if (grid[x - 1] && grid[x - 1][y + 1]) {
  160. ret.push(grid[x - 1][y + 1]);
  161. }
  162. if (grid[x + 1] && grid[x + 1][y + 1]) {
  163. ret.push(grid[x + 1][y + 1]);
  164. }
  165. }
  166. return ret;
  167. };
  168. Graph.prototype.toString = function() {
  169. var graphString = [], nodes = this.grid, rowDebug, row, y, l;
  170. for (var x = 0, len = nodes.length; x < len; x++) {
  171. rowDebug = [];
  172. row = nodes[x];
  173. for (y = 0, l = row.length; y < l; y++) {
  174. rowDebug.push(row[y].weight);
  175. }
  176. graphString.push(rowDebug.join(" "));
  177. }
  178. return graphString.join("\n");
  179. };
  180. function GridNode(x, y, weight) {
  181. this.x = x;
  182. this.y = y;
  183. this.weight = weight;
  184. }
  185. GridNode.prototype.toString = function() {
  186. return "[" + this.x + " " + this.y + "]";
  187. };
  188. GridNode.prototype.getCost = function(fromNeighbor) {
  189. if (fromNeighbor && fromNeighbor.x !== this.x && fromNeighbor.y !== this.y) {
  190. return this.weight * 1.41421;
  191. }
  192. return this.weight;
  193. };
  194. GridNode.prototype.isWall = function() {
  195. return this.weight === 0;
  196. };
  197. function BinaryHeap(scoreFunction) {
  198. this.content = [];
  199. this.scoreFunction = scoreFunction;
  200. }
  201. BinaryHeap.prototype = {
  202. push: function(element) {
  203. this.content.push(element);
  204. this.sinkDown(this.content.length - 1);
  205. },
  206. pop: function() {
  207. var result = this.content[0];
  208. var end = this.content.pop();
  209. if (this.content.length > 0) {
  210. this.content[0] = end;
  211. this.bubbleUp(0);
  212. }
  213. return result;
  214. },
  215. remove: function(node) {
  216. var i = this.content.indexOf(node);
  217. var end = this.content.pop();
  218. if (i !== this.content.length - 1) {
  219. this.content[i] = end;
  220. if (this.scoreFunction(end) < this.scoreFunction(node)) {
  221. this.sinkDown(i);
  222. } else {
  223. this.bubbleUp(i);
  224. }
  225. }
  226. },
  227. size: function() {
  228. return this.content.length;
  229. },
  230. rescoreElement: function(node) {
  231. this.sinkDown(this.content.indexOf(node));
  232. },
  233. sinkDown: function(n) {
  234. var element = this.content[n];
  235. while (n > 0) {
  236. var parentN = (n + 1 >> 1) - 1, parent = this.content[parentN];
  237. if (this.scoreFunction(element) < this.scoreFunction(parent)) {
  238. this.content[parentN] = element;
  239. this.content[n] = parent;
  240. n = parentN;
  241. } else {
  242. break;
  243. }
  244. }
  245. },
  246. bubbleUp: function(n) {
  247. var length = this.content.length, element = this.content[n], elemScore = this.scoreFunction(element);
  248. while (true) {
  249. var child2N = n + 1 << 1, child1N = child2N - 1;
  250. var swap = null, child1Score;
  251. if (child1N < length) {
  252. var child1 = this.content[child1N];
  253. child1Score = this.scoreFunction(child1);
  254. if (child1Score < elemScore) {
  255. swap = child1N;
  256. }
  257. }
  258. if (child2N < length) {
  259. var child2 = this.content[child2N], child2Score = this.scoreFunction(child2);
  260. if (child2Score < (swap === null ? elemScore : child1Score)) {
  261. swap = child2N;
  262. }
  263. }
  264. if (swap !== null) {
  265. this.content[n] = this.content[swap];
  266. this.content[swap] = element;
  267. n = swap;
  268. } else {
  269. break;
  270. }
  271. }
  272. }
  273. };
  274. // index.ts
  275. function shortestPath(start, end, options = {}) {
  276. options = options || {};
  277. if (!_helpers.isObject.call(void 0, options)) throw new Error("options is invalid");
  278. let obstacles = options.obstacles || _helpers.featureCollection.call(void 0, []);
  279. let resolution = options.resolution || 100;
  280. if (!start) throw new Error("start is required");
  281. if (!end) throw new Error("end is required");
  282. if (resolution && (!_helpers.isNumber.call(void 0, resolution) || resolution <= 0))
  283. throw new Error("options.resolution must be a number, greater than 0");
  284. const startCoord = _invariant.getCoord.call(void 0, start);
  285. const endCoord = _invariant.getCoord.call(void 0, end);
  286. start = _helpers.point.call(void 0, startCoord);
  287. end = _helpers.point.call(void 0, endCoord);
  288. if (obstacles.type === "FeatureCollection") {
  289. if (obstacles.features.length === 0) {
  290. return _helpers.lineString.call(void 0, [startCoord, endCoord]);
  291. }
  292. } else if (obstacles.type === "Polygon") {
  293. obstacles = _helpers.featureCollection.call(void 0, [_helpers.feature.call(void 0, _invariant.getGeom.call(void 0, obstacles))]);
  294. } else {
  295. throw new Error("invalid obstacles");
  296. }
  297. const collection = obstacles;
  298. collection.features.push(start);
  299. collection.features.push(end);
  300. 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));
  301. const [west, south, east, north] = box;
  302. const width = _distance.distance.call(void 0, [west, south], [east, south], options);
  303. const division = width / resolution;
  304. collection.features.pop();
  305. collection.features.pop();
  306. const xFraction = division / _distance.distance.call(void 0, [west, south], [east, south], options);
  307. const cellWidth = xFraction * (east - west);
  308. const yFraction = division / _distance.distance.call(void 0, [west, south], [west, north], options);
  309. const cellHeight = yFraction * (north - south);
  310. const bboxHorizontalSide = east - west;
  311. const bboxVerticalSide = north - south;
  312. const columns = Math.floor(bboxHorizontalSide / cellWidth);
  313. const rows = Math.floor(bboxVerticalSide / cellHeight);
  314. const deltaX = (bboxHorizontalSide - columns * cellWidth) / 2;
  315. const deltaY = (bboxVerticalSide - rows * cellHeight) / 2;
  316. const pointMatrix = [];
  317. const matrix = [];
  318. let closestToStart;
  319. let closestToEnd;
  320. let minDistStart = Infinity;
  321. let minDistEnd = Infinity;
  322. let currentY = north - deltaY;
  323. let r = 0;
  324. while (currentY >= south) {
  325. const matrixRow = [];
  326. const pointMatrixRow = [];
  327. let currentX = west + deltaX;
  328. let c = 0;
  329. while (currentX <= east) {
  330. const pt = _helpers.point.call(void 0, [currentX, currentY]);
  331. const isInsideObstacle = isInside(pt, obstacles);
  332. matrixRow.push(isInsideObstacle ? 0 : 1);
  333. pointMatrixRow.push(currentX + "|" + currentY);
  334. const distStart = _distance.distance.call(void 0, pt, start);
  335. if (!isInsideObstacle && distStart < minDistStart) {
  336. minDistStart = distStart;
  337. closestToStart = { x: c, y: r };
  338. }
  339. const distEnd = _distance.distance.call(void 0, pt, end);
  340. if (!isInsideObstacle && distEnd < minDistEnd) {
  341. minDistEnd = distEnd;
  342. closestToEnd = { x: c, y: r };
  343. }
  344. currentX += cellWidth;
  345. c++;
  346. }
  347. matrix.push(matrixRow);
  348. pointMatrix.push(pointMatrixRow);
  349. currentY -= cellHeight;
  350. r++;
  351. }
  352. const graph = new Graph(matrix, { diagonal: true });
  353. const startOnMatrix = graph.grid[closestToStart.y][closestToStart.x];
  354. const endOnMatrix = graph.grid[closestToEnd.y][closestToEnd.x];
  355. const result = astar.search(graph, startOnMatrix, endOnMatrix);
  356. const path = [startCoord];
  357. result.forEach(function(coord) {
  358. const coords = pointMatrix[coord.x][coord.y].split("|");
  359. path.push([+coords[0], +coords[1]]);
  360. });
  361. path.push(endCoord);
  362. return _cleancoords.cleanCoords.call(void 0, _helpers.lineString.call(void 0, path));
  363. }
  364. function isInside(pt, polygons) {
  365. for (let i = 0; i < polygons.features.length; i++) {
  366. if (_booleanpointinpolygon.booleanPointInPolygon.call(void 0, pt, polygons.features[i])) {
  367. return true;
  368. }
  369. }
  370. return false;
  371. }
  372. var turf_shortest_path_default = shortestPath;
  373. exports.default = turf_shortest_path_default; exports.shortestPath = shortestPath;
  374. //# sourceMappingURL=index.cjs.map