terra-route 0.0.14 → 0.0.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/CHANGELOG.md +105 -0
  2. package/README.md +6 -6
  3. package/dist/heap/four-ary-heap.d.ts +2 -0
  4. package/dist/terra-route.cjs +1 -1
  5. package/dist/terra-route.cjs.map +1 -1
  6. package/dist/terra-route.d.ts +12 -5
  7. package/dist/terra-route.modern.js +1 -1
  8. package/dist/terra-route.modern.js.map +1 -1
  9. package/dist/terra-route.module.js +1 -1
  10. package/dist/terra-route.module.js.map +1 -1
  11. package/dist/terra-route.umd.js +1 -1
  12. package/dist/terra-route.umd.js.map +1 -1
  13. package/fta.config.json +9 -0
  14. package/jest.config.js +1 -1
  15. package/package.json +35 -8
  16. package/scripts/bump.mjs +57 -0
  17. package/scripts/release.mjs +30 -0
  18. package/scripts/versionrc.cjs +26 -0
  19. package/src/distance/haversine.ts +13 -15
  20. package/src/heap/fibonacci-heap.ts +9 -0
  21. package/src/heap/four-ary-heap.ts +10 -0
  22. package/src/heap/heap.d.ts +5 -0
  23. package/src/heap/min-heap.ts +9 -0
  24. package/src/heap/pairing-heap.ts +5 -0
  25. package/src/terra-route.compare.spec.ts +4 -5
  26. package/src/terra-route.spec.ts +81 -0
  27. package/src/terra-route.ts +282 -196
  28. package/dist/fibonacci-heap.d.ts +0 -11
  29. package/dist/graph/graph.d.ts +0 -114
  30. package/dist/graph/methods/bounding-box.d.ts +0 -13
  31. package/dist/graph/methods/connected.d.ts +0 -9
  32. package/dist/graph/methods/duplicates.d.ts +0 -7
  33. package/dist/graph/methods/leaf.d.ts +0 -12
  34. package/dist/graph/methods/nodes.d.ts +0 -17
  35. package/dist/graph/methods/spatial-index/geokdbush.d.ts +0 -3
  36. package/dist/graph/methods/spatial-index/kdbush.d.ts +0 -16
  37. package/dist/graph/methods/spatial-index/tinyqueue.d.ts +0 -11
  38. package/dist/graph/methods/unify.d.ts +0 -2
  39. package/dist/graph/methods/unique-segments.d.ts +0 -5
  40. package/dist/graph/methods/unique.d.ts +0 -5
  41. package/dist/heap/min-heap.d.ts +0 -9
  42. package/dist/min-heap.d.ts +0 -8
  43. package/dist/test-utils/utils.d.ts +0 -50
  44. package/instructions.md +0 -13
package/CHANGELOG.md ADDED
@@ -0,0 +1,105 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file. See [commit-and-tag-version](https://github.com/absolute-version/commit-and-tag-version) for commit guidelines.
4
+
5
+ ## [0.0.17](https://github.com/JamesLMilner/terra-route/compare/terra-route@0.0.16...terra-route@0.0.17) (2026-05-02)
6
+
7
+
8
+ ### chore
9
+
10
+ * add repository field to package.json for release script (#16) ([](https://github.com/JamesLMilner/terra-route/commit/596452266757fdd81bbe40df56c8d2b00d4ca247)), closes [#16](https://github.com/JamesLMilner/terra-route/issues/16)
11
+
12
+ ## [0.0.16](https://github.com/JamesLMilner/terra-route/compare/terra-route@0.0.15...terra-route@0.0.16) (2026-05-02)
13
+
14
+
15
+ ### chore
16
+
17
+ * autogenerate API docs on tag push (#15) ([](https://github.com/JamesLMilner/terra-route/commit/91a7d303c5d4b49bbc38d02aec7dfbba91e8f941)), closes [#15](https://github.com/JamesLMilner/terra-route/issues/15)
18
+ * fix release script (#14) ([](https://github.com/JamesLMilner/terra-route/commit/2c598bc8bd30ab4a397af768f0148411c0bd4bd0)), closes [#14](https://github.com/JamesLMilner/terra-route/issues/14)
19
+
20
+ ## 0.0.15 (2026-03-16)
21
+
22
+
23
+ ### fix
24
+
25
+ * ensure package-lock.json is correct ([](https://github.com/JamesLMilner/terra-route/commit/9240fba53bdc5a17c206fbecedbeedf6ad09af74))
26
+ * revert back to simple working implementation ([](https://github.com/JamesLMilner/terra-route/commit/aea3411181034e5104e8b8ae618b64e86ee5105b))
27
+ * skip visited nodes ([](https://github.com/JamesLMilner/terra-route/commit/02e04446462750b39ccf127155cb9d8ae7459090))
28
+
29
+
30
+ ### test
31
+
32
+ * adds tests for graph ([](https://github.com/JamesLMilner/terra-route/commit/aa230c85804f68fdd337e032105dbf0291338a9c))
33
+ * improve testing around the library ([](https://github.com/JamesLMilner/terra-route/commit/8affef59af2e683cc53ef856fafadd5867522385))
34
+
35
+
36
+ ### docs
37
+
38
+ * add in the logo and shorten the example ([](https://github.com/JamesLMilner/terra-route/commit/a102d1b02caec9de2727f6515abb77267ebb8178))
39
+ * fix alignment after previous commit ([](https://github.com/JamesLMilner/terra-route/commit/c0366de4d2ba0a3b6527aa5bc0ef1b87397c0865))
40
+ * improve rendering of graph in README ([](https://github.com/JamesLMilner/terra-route/commit/29b34559582679194c2812eb98cd08b6e32671db))
41
+ * TerraDraw -> Terra Draw ([](https://github.com/JamesLMilner/terra-route/commit/0644d999fbe50964d665b4bf3eaafc1290377197))
42
+ * update README with more accurate information ([](https://github.com/JamesLMilner/terra-route/commit/8678cbdb8700e8c37c275a156c8b97fd530aeaac))
43
+
44
+
45
+ * Merge pull request #3 from JamesLMilner/logo ([](https://github.com/JamesLMilner/terra-route/commit/418aeac2d1444b68fd993905d95795c74833ad37)), closes [#3](https://github.com/JamesLMilner/terra-route/issues/3)
46
+ * Merge pull request #2 from JamesLMilner/unifiy ([](https://github.com/JamesLMilner/terra-route/commit/df9e10800e46f520133d314091e33f5b29c9afef)), closes [#2](https://github.com/JamesLMilner/terra-route/issues/2)
47
+ * Merge pull request #1 from JamesLMilner/different-heap ([](https://github.com/JamesLMilner/terra-route/commit/ae0a9b96c7820eeee3f0167cba3a0fda743b8537)), closes [#1](https://github.com/JamesLMilner/terra-route/issues/1)
48
+ * remove unused config from package.json ([](https://github.com/JamesLMilner/terra-route/commit/3d1759274ff28e2750a2568c0efdaa025756f1a7))
49
+ * fix install and tests ([](https://github.com/JamesLMilner/terra-route/commit/b9fccdacfb3493eac8785de8c0296030ab2b4200))
50
+ * bump version to 0.0.4 ([](https://github.com/JamesLMilner/terra-route/commit/788bec7831baf5571e3784df971825bd093ef695))
51
+ * add API docs link ([](https://github.com/JamesLMilner/terra-route/commit/0d47ba71e3c83fe802803b45ab302bf9f7068df0))
52
+ * Fix readme section for limitations ([](https://github.com/JamesLMilner/terra-route/commit/891caba7ec1d3bc06cc1cb085a4b907890b12bd4))
53
+ * separate out instance creation from network graph building ([](https://github.com/JamesLMilner/terra-route/commit/42459d8b40f606c10c1ea5bf5827d46bc06fd6d8))
54
+ * fix package-lock.json ([](https://github.com/JamesLMilner/terra-route/commit/26a0a6ab4984e32836a7a255cfd87d9ec050737f))
55
+ * rebuild and bump ([](https://github.com/JamesLMilner/terra-route/commit/7b2dc41c41e5aac67094ee3e816710dac3801534))
56
+ * export measurments ([](https://github.com/JamesLMilner/terra-route/commit/a4dd5f70d55fd35cbbdbcc5e12328481c326d18f))
57
+ * Initial commit ([](https://github.com/JamesLMilner/terra-route/commit/91aaadffd02ea34d58914e57b18f521188124270))
58
+
59
+
60
+ ### refactor
61
+
62
+ * remove graph statistic related code from the library (#6) ([](https://github.com/JamesLMilner/terra-route/commit/1729abbaee7b37e855ea1297937578b9c05aa53b)), closes [#6](https://github.com/JamesLMilner/terra-route/issues/6)
63
+
64
+
65
+ ### feat
66
+
67
+ * add additional methods for LineStringGraph ([](https://github.com/JamesLMilner/terra-route/commit/6c628dbba6ba6dd52e36e3615298fd980479b8e2))
68
+ * add expandRouteGraph method (#7) ([](https://github.com/JamesLMilner/terra-route/commit/3d1da87c2a51d63ab8ec67e581b0bdbe1d16f947)), closes [#7](https://github.com/JamesLMilner/terra-route/issues/7)
69
+ * add getUnifiedNetwork to API ([](https://github.com/JamesLMilner/terra-route/commit/44c3e2aa5f9bdd73f00495ad94bb9a4025d1f408))
70
+ * add graph properties such as number of nodes/edges and connected components ([](https://github.com/JamesLMilner/terra-route/commit/e77f5abc0baf06e29ff7f98d0a34cb704066b24c))
71
+ * export LineStringGraph for determining graph properties ([](https://github.com/JamesLMilner/terra-route/commit/f1c31d85227c1e48279f51bf9644908be060d453))
72
+ * use bidirectional astar for faster route finding ([](https://github.com/JamesLMilner/terra-route/commit/e41fbd03f319d4bfae86b19c16f24faa73c27c57))
73
+ * use fibonacci heap for performance ([](https://github.com/JamesLMilner/terra-route/commit/0c3972821735610e4acd0c6e26f1a64fc02848ac))
74
+
75
+
76
+ ### perf
77
+
78
+ * improve performance by using Four-ary Heap and Compressed Sparse Row (#5) ([](https://github.com/JamesLMilner/terra-route/commit/64796c216e6555dfe4b02aea83ba43084d74533e)), closes [#5](https://github.com/JamesLMilner/terra-route/issues/5)
79
+ * improve performance of Terra Route route method ([](https://github.com/JamesLMilner/terra-route/commit/9c1770bbb2b371fe5c7fab43a11cdc83bca2e797))
80
+ * use bi-directional A* (#8) ([](https://github.com/JamesLMilner/terra-route/commit/400d06abcc595b7428e170d8f839d09386ddc8c2)), closes [#8](https://github.com/JamesLMilner/terra-route/issues/8)
81
+ * use landmark-assisted heuristics (#11) ([](https://github.com/JamesLMilner/terra-route/commit/fa347996d6692b8e04a7d29a9f3785730f43f06d)), closes [#11](https://github.com/JamesLMilner/terra-route/issues/11)
82
+ * use peak min to improve getRoute performance (#10) ([](https://github.com/JamesLMilner/terra-route/commit/85615fe656fd4675072fbe60a25d9ac04d0f52fd)), closes [#10](https://github.com/JamesLMilner/terra-route/issues/10)
83
+
84
+
85
+ ### chore
86
+
87
+ * add files to npmignore ([](https://github.com/JamesLMilner/terra-route/commit/3b04e9962ce903fd0ddfc34630aaab752e9f9995))
88
+ * add fta as analysis tool (#9) ([](https://github.com/JamesLMilner/terra-route/commit/7a9088706d8a5ca768642a231347a8c5d925f20c)), closes [#9](https://github.com/JamesLMilner/terra-route/issues/9)
89
+ * add release scripts (#13) ([](https://github.com/JamesLMilner/terra-route/commit/98d231b69bc701e8f9a228b5fa79ec5cc427b3cd)), closes [#13](https://github.com/JamesLMilner/terra-route/issues/13)
90
+ * add release workflows (#12) ([](https://github.com/JamesLMilner/terra-route/commit/0e1ad96b980b008ba479898ae462f3b000b31af5)), closes [#12](https://github.com/JamesLMilner/terra-route/issues/12)
91
+ * avoid circular dependency in unify.ts ([](https://github.com/JamesLMilner/terra-route/commit/40625e6fb639f638cc1853ac8b56b787342cd1b7))
92
+ * bump to 0.0.10 ([](https://github.com/JamesLMilner/terra-route/commit/4c4793f03104147edc28efb01d03b63b9b520171))
93
+ * bump to 0.0.11 ([](https://github.com/JamesLMilner/terra-route/commit/3564b5c93ca0a19fd79724ef430729ffd0091e1b))
94
+ * bump to 0.0.5 ([](https://github.com/JamesLMilner/terra-route/commit/42c4edbd5d55c72e5199c5f86eeb6802464f0197))
95
+ * bump to 0.0.7 ([](https://github.com/JamesLMilner/terra-route/commit/14e2eae514f4e162dcbd44af63bd15c3ec4f7887))
96
+ * bump to 0.0.9 ([](https://github.com/JamesLMilner/terra-route/commit/5b3386786e09b3634753e0acf69bdde90d535ab7))
97
+ * bump to v0.0.12 ([](https://github.com/JamesLMilner/terra-route/commit/4f68a5592a50507b9b5f7db6f8ccdd0458ce5b5c))
98
+ * bump to v0.0.12 ([](https://github.com/JamesLMilner/terra-route/commit/2963cf00e9ed8633fabf495574902c49cea71144))
99
+ * bump to v0.0.13 ([](https://github.com/JamesLMilner/terra-route/commit/411995ff01cab4efd929fcf0679935b813688503))
100
+ * bump to version 0.0.6 ([](https://github.com/JamesLMilner/terra-route/commit/51df48741672d551d7808b5f7b827d5bdec671d2))
101
+ * fix benchmarking ([](https://github.com/JamesLMilner/terra-route/commit/9a857e01aa42bcf02129769f7945b9c8709f40cf))
102
+ * lower branch coverage for now ([](https://github.com/JamesLMilner/terra-route/commit/37d4d1a9f218a08f002623bf018462d3dcb6c73f))
103
+ * refactor benchmarking to be tidier ([](https://github.com/JamesLMilner/terra-route/commit/bf59717e607f1fc92d933af8b777d254fc4f19d3))
104
+ * update benchmarks in README ([](https://github.com/JamesLMilner/terra-route/commit/8d1e2515a9f36efda17f7e1705ef0a0529462055))
105
+ * update caniuse-lite ([](https://github.com/JamesLMilner/terra-route/commit/44d18057c902d6a29bcf43b3c2600ff384829a3c))
package/README.md CHANGED
@@ -60,16 +60,16 @@ npm run benchmark
60
60
  Here is an example output of a benchmark run for routing:
61
61
 
62
62
  <pre>
63
- Terra Route | █████ 151ms
64
- GeoJSON Path Finder | ██████████████████ 571ms
65
- ngraph.graph | ██████████████████████████████████████████████████ 1564ms
63
+ Terra Route | █████ 142ms
64
+ GeoJSON Path Finder | ██████████████████ 570ms
65
+ ngraph.graph | ██████████████████████████████████████████████████ 1570ms
66
66
  </pre>
67
67
 
68
- Using default Haversine distance, Terra Route is approximately 3.75x faster than GeoJSON Path Finder with Haversine distance for A -> B path finding. If you pass in the CheapRuler distance metric (you can use the exposed `createCheapRuler` function), it is approximately x8 faster than GeoJSON Path Finder with Haversine distance.
68
+ Using default Haversine distance, Terra Route is approximately 4.6x faster than GeoJSON Path Finder with Haversine distance for A -> B path finding. If you pass in the CheapRuler distance metric (you can use the exposed `createCheapRuler` function), it is approximately x8 faster than GeoJSON Path Finder with Haversine distance.
69
69
 
70
- For initialisation of the network, Terra Route is approximately 10x faster with Haversine than GeoJSON Path Finder. Terra Draw splits out instantiating the Class of the library from the actual graph building, which is done via `buildRouteGraph`. This allows you to defer graph creation to an appropriate time.
70
+ For initialisation of the network, Terra Route is approximately 19x faster with Haversine than GeoJSON Path Finder. Terra Draw splits out instantiating the Class of the library from the actual graph building, which is done via `buildRouteGraph`. This allows you to defer graph creation to an appropriate time.
71
71
 
72
- Terra Route uses an [bi-directional A* algorithm for pathfinding](https://en.wikipedia.org/wiki/A*_search_algorithm) and by default uses a [four-ary heap](https://en.wikipedia.org/wiki/D-ary_heap) for the underlying priority queue, although this is configurable.
72
+ Terra Route uses an [A* algorithm](https://en.wikipedia.org/wiki/A*_search_algorithm) landmark-assisted heuristics. By default it uses a [four-ary heap](https://en.wikipedia.org/wiki/D-ary_heap) for the underlying priority queue, although this is configurable.
73
73
 
74
74
  ## Limitations
75
75
 
@@ -12,6 +12,8 @@ export declare class FourAryHeap implements Heap {
12
12
  private insertCounter;
13
13
  insert(key: number, value: number): void;
14
14
  extractMin(): number | null;
15
+ peekMinKey(): number;
16
+ clear(): void;
15
17
  size(): number;
16
18
  private bubbleDown;
17
19
  }
@@ -1,2 +1,2 @@
1
- var t=function(t,r){var e=function(t){return t*Math.PI/180},s=e(t[1]),i=e(t[0]),n=e(r[1]),a=n-s,h=e(r[0])-i,c=Math.sin(a/2)*Math.sin(a/2)+Math.cos(s)*Math.cos(n)*Math.sin(h/2)*Math.sin(h/2);return 2*Math.atan2(Math.sqrt(c),Math.sqrt(1-c))*6371e3/1e3},r=/*#__PURE__*/function(){function t(){this.keys=[],this.values=[],this.idxs=[],this.length=0,this.insertCounter=0}var r=t.prototype;return r.insert=function(t,r){var e=this.length;this.length=e+1;for(var s=t,i=r,n=this.insertCounter++;e>0;){var a=e-1>>>2,h=this.keys[a],c=this.idxs[a];if(s>h||s===h&&n>c)break;this.keys[e]=h,this.values[e]=this.values[a],this.idxs[e]=c,e=a}this.keys[e]=s,this.values[e]=i,this.idxs[e]=n},r.extractMin=function(){var t=this.length;if(0===t)return null;var r=this.values[0],e=t-1;return this.length=e,e>0&&(this.keys[0]=this.keys[e],this.values[0]=this.values[e],this.idxs[0]=this.idxs[e],this.bubbleDown(0)),r},r.size=function(){return this.length},r.bubbleDown=function(t){for(var r=this.length,e=this.keys,s=this.values,i=this.idxs,n=e[t],a=s[t],h=i[t];;){var c=1+(t<<2);if(c>=r)break;var o=c,u=e[c],l=i[c],f=s[c],v=c+1;if(v<r){var d=e[v],I=i[v];(d<u||d===u&&I<l)&&(o=v,u=d,l=I,f=s[v])}var S=c+2;if(S<r){var g=e[S],y=i[S];(g<u||g===u&&y<l)&&(o=S,u=g,l=y,f=s[S])}var p=c+3;if(p<r){var m=e[p],w=i[p];(m<u||m===u&&w<l)&&(o=p,u=m,l=w,f=s[p])}if(!(u<n||u===n&&l<h))break;e[t]=u,s[t]=f,i[t]=l,t=o}e[t]=n,s[t]=a,i[t]=h},t}();exports.TerraRoute=/*#__PURE__*/function(){function e(e){var s,i;this.network=null,this.distanceMeasurement=void 0,this.heapConstructor=void 0,this.coordinateIndexMap=new Map,this.coordinates=[],this.adjacencyList=[],this.csrOffsets=null,this.csrIndices=null,this.csrDistances=null,this.csrNodeCount=0,this.gScoreScratch=null,this.cameFromScratch=null,this.visitedScratch=null,this.hScratch=null,this.gScoreRevScratch=null,this.cameFromRevScratch=null,this.visitedRevScratch=null,this.hRevScratch=null,this.scratchCapacity=0,this.distanceMeasurement=null!=(s=null==e?void 0:e.distanceMeasurement)?s:t,this.heapConstructor=null!=(i=null==e?void 0:e.heap)?i:r}var s=e.prototype;return s.buildRouteGraph=function(t){var r=this;this.network=t,this.coordinateIndexMap=new Map,this.coordinates=[],this.adjacencyList=[],this.csrOffsets=null,this.csrIndices=null,this.csrDistances=null,this.csrNodeCount=0;var e=this.coordinateIndexMap,s=this.coordinates,i=this.distanceMeasurement,n=[];this.forEachSegment(t,function(t,i){var a,h,c=r.indexCoordinate(t,e,s),o=r.indexCoordinate(i,e,s);n[c]=(null!=(a=n[c])?a:0)+1,n[o]=(null!=(h=n[o])?h:0)+1});var a=this.coordinates.length;this.csrNodeCount=a;for(var h=new Int32Array(a+1),c=0;c<a;c++){var o,u=null!=(o=n[c])?o:0;h[c+1]=h[c]+u}var l=h[a],f=new Int32Array(l),v=new Float64Array(l),d=h.slice();this.forEachSegment(t,function(t,e){var s=r.coordinateIndexMap.get(t[0]).get(t[1]),n=r.coordinateIndexMap.get(e[0]).get(e[1]),a=i(t,e),h=d[s]++;f[h]=n,v[h]=a,h=d[n]++,f[h]=s,v[h]=a}),this.csrOffsets=h,this.csrIndices=f,this.csrDistances=v,this.adjacencyList=new Array(a)},s.expandRouteGraph=function(t){var r=this;if(null===this.network)throw new Error("Network not built. Please call buildRouteGraph(network) first.");this.network={type:"FeatureCollection",features:[].concat(this.network.features,t.features)};for(var e=this.coordinateIndexMap,s=this.coordinates,i=this.distanceMeasurement,n=this.adjacencyList,a=0;a<n.length;a++)void 0===n[a]&&(n[a]=[]);this.forEachSegment(t,function(t,a){var h=r.indexCoordinate(t,e,s,function(t){n[t]=[]}),c=r.indexCoordinate(a,e,s,function(t){n[t]=[]}),o=i(t,a);n[h].push({node:c,distance:o}),n[c].push({node:h,distance:o})}),this.rebuildCsrFromAdjacency()},s.rebuildCsrFromAdjacency=function(){var t=this.coordinates.length,r=this.adjacencyList,e=new Int32Array(t);if(this.csrOffsets&&this.csrIndices&&this.csrDistances)for(var s=this.csrOffsets,i=Math.min(this.csrNodeCount,t),n=0;n<i;n++)e[n]+=s[n+1]-s[n];for(var a=0;a<t;a++){var h=r[a];h&&h.length&&(e[a]+=h.length)}for(var c=new Int32Array(t+1),o=0;o<t;o++)c[o+1]=c[o]+e[o];var u=c[t],l=new Int32Array(u),f=new Float64Array(u),v=c.slice();if(this.csrOffsets&&this.csrIndices&&this.csrDistances)for(var d=this.csrOffsets,I=this.csrIndices,S=this.csrDistances,g=Math.min(this.csrNodeCount,t),y=0;y<g;y++){for(var p=d[y+1],m=v[y],w=d[y];w<p;w++)l[m]=I[w],f[m]=S[w],m++;v[y]=m}for(var M=0;M<t;M++){var N=r[M];if(N&&0!==N.length){for(var C=v[M],x=0,F=N.length;x<F;x++){var O=N[x];l[C]=O.node,f[C]=O.distance,C++}v[M]=C}}this.csrOffsets=c,this.csrIndices=l,this.csrDistances=f,this.csrNodeCount=t,this.adjacencyList=new Array(t)},s.getRoute=function(t,r){if(null===this.network)throw new Error("Network not built. Please call buildRouteGraph(network) first.");var e=this.getOrCreateIndex(t.geometry.coordinates),s=this.getOrCreateIndex(r.geometry.coordinates);if(e===s)return null;var i=this.coordinates,n=this.adjacencyList,a=i.length;this.ensureScratch(a);var h=this.gScoreScratch,c=this.gScoreRevScratch,o=this.cameFromScratch,u=this.cameFromRevScratch,l=this.visitedScratch,f=this.visitedRevScratch,v=this.hScratch,d=this.hRevScratch;h.fill(Number.POSITIVE_INFINITY,0,a),c.fill(Number.POSITIVE_INFINITY,0,a),o.fill(-1,0,a),u.fill(-1,0,a),l.fill(0,0,a),f.fill(0,0,a),v.fill(-1,0,a),d.fill(-1,0,a);var I=new this.heapConstructor,S=new this.heapConstructor;h[e]=0,c[s]=0,I.insert(0,e),S.insert(0,s);for(var g=Number.POSITIVE_INFINITY,y=-1,p=0,m=0;I.size()>0&&S.size()>0&&!(y>=0&&p+m>=g);)if(I.size()<=S.size()){var w=I.extractMin();if(0!==l[w])continue;if(p=h[w],l[w]=1,0!==f[w]){var M=h[w]+c[w];M<g&&(g=M,y=w)}if(this.csrOffsets&&w<this.csrNodeCount)for(var N=this.csrOffsets,C=this.csrIndices,x=this.csrDistances,F=N[w],O=N[w+1];F<O;F++){var b=C[F],R=h[w]+x[F];if(R<h[b]){h[b]=R,o[b]=w;var k=c[b];if(k!==Number.POSITIVE_INFINITY){var A=R+k;A<g&&(g=A,y=b)}I.insert(R,b)}}else{var T=n[w];if(T&&T.length)for(var E=0,D=T.length;E<D;E++){var P=T[E],j=P.node,L=h[w]+P.distance;if(L<h[j]){h[j]=L,o[j]=w;var V=c[j];if(V!==Number.POSITIVE_INFINITY){var Y=L+V;Y<g&&(g=Y,y=j)}I.insert(L,j)}}}}else{var _=S.extractMin();if(0!==f[_])continue;if(m=c[_],f[_]=1,0!==l[_]){var z=h[_]+c[_];z<g&&(g=z,y=_)}if(this.csrOffsets&&_<this.csrNodeCount)for(var q=this.csrOffsets,G=this.csrIndices,U=this.csrDistances,B=q[_],H=q[_+1];B<H;B++){var J=G[B],K=c[_]+U[B];if(K<c[J]){c[J]=K,u[J]=_;var Q=h[J];if(Q!==Number.POSITIVE_INFINITY){var W=K+Q;W<g&&(g=W,y=J)}S.insert(K,J)}}else{var X=n[_];if(X&&X.length)for(var Z=0,$=X.length;Z<$;Z++){var tt=X[Z],rt=tt.node,et=c[_]+tt.distance;if(et<c[rt]){c[rt]=et,u[rt]=_;var st=h[rt];if(st!==Number.POSITIVE_INFINITY){var it=et+st;it<g&&(g=it,y=rt)}S.insert(et,rt)}}}}if(y<0)return null;for(var nt=[],at=y;at!==e&&at>=0;)nt.push(i[at]),at=o[at];if(at!==e)return null;for(nt.push(i[e]),nt.reverse(),at=y;at!==s;){if((at=u[at])<0)return null;nt.push(i[at])}return{type:"Feature",geometry:{type:"LineString",coordinates:nt},properties:{}}},s.getOrCreateIndex=function(t){var r=t[0],e=t[1],s=this.coordinateIndexMap.get(r);void 0===s&&(s=new Map,this.coordinateIndexMap.set(r,s));var i=s.get(e);if(void 0===i&&(i=this.coordinates.length,this.coordinates.push(t),s.set(e,i),this.adjacencyList[i]=[],this.csrOffsets)){var n=this.csrNodeCount;if(i===n){var a=new Int32Array(n+2);a.set(this.csrOffsets,0),a[n+1]=a[n],this.csrOffsets=a,this.csrNodeCount=n+1}}return i},s.ensureScratch=function(t){if(!(this.scratchCapacity>=t&&this.gScoreScratch&&this.cameFromScratch&&this.visitedScratch&&this.hScratch&&this.gScoreRevScratch&&this.cameFromRevScratch&&this.visitedRevScratch&&this.hRevScratch)){var r=0|t;this.gScoreScratch=new Float64Array(r),this.cameFromScratch=new Int32Array(r),this.visitedScratch=new Uint8Array(r),this.hScratch=new Float64Array(r),this.gScoreRevScratch=new Float64Array(r),this.cameFromRevScratch=new Int32Array(r),this.visitedRevScratch=new Uint8Array(r),this.hRevScratch=new Float64Array(r),this.scratchCapacity=r}},s.forEachSegment=function(t,r){for(var e=t.features,s=0,i=e.length;s<i;s++)for(var n=e[s].geometry.coordinates,a=0,h=n.length-1;a<h;a++)r(n[a],n[a+1])},s.indexCoordinate=function(t,r,e,s){var i=t[0],n=t[1],a=r.get(i);void 0===a&&(a=new Map,r.set(i,a));var h=a.get(n);return void 0===h&&(h=e.length,e.push(t),a.set(n,h),s&&s(h)),h},e}(),exports.createCheapRuler=function(t){var r=1/298.257223563,e=r*(2-r),s=Math.PI/180,i=Math.cos(t*s),n=1/(1-e*(1-i*i)),a=Math.sqrt(n),h=6378.137*s,c=h*a*i,o=h*a*n*(1-e);return function(t,r){for(var e=t[0]-r[0];e<-180;)e+=360;for(;e>180;)e-=360;var s=e*c,i=(t[1]-r[1])*o;return Math.sqrt(s*s+i*i)}},exports.haversineDistance=t;
1
+ var t=Math.PI/180,r=function(r,s){var i=r[1]*t,e=s[1]*t,a=s[0]*t-r[0]*t,n=Math.sin((e-i)/2),h=Math.sin(a/2),c=n*n+Math.cos(i)*Math.cos(e)*h*h;return 2*Math.atan2(Math.sqrt(c),Math.sqrt(1-c))*6371},s=/*#__PURE__*/function(){function t(){this.keys=[],this.values=[],this.idxs=[],this.length=0,this.insertCounter=0}var r=t.prototype;return r.insert=function(t,r){var s=this.length;this.length=s+1;for(var i=t,e=r,a=this.insertCounter++;s>0;){var n=s-1>>>2,h=this.keys[n],c=this.idxs[n];if(i>h||i===h&&a>c)break;this.keys[s]=h,this.values[s]=this.values[n],this.idxs[s]=c,s=n}this.keys[s]=i,this.values[s]=e,this.idxs[s]=a},r.extractMin=function(){var t=this.length;if(0===t)return null;var r=this.values[0],s=t-1;return this.length=s,s>0&&(this.keys[0]=this.keys[s],this.values[0]=this.values[s],this.idxs[0]=this.idxs[s],this.bubbleDown(0)),r},r.peekMinKey=function(){return 0===this.length?Number.POSITIVE_INFINITY:this.keys[0]},r.clear=function(){this.length=0,this.insertCounter=0},r.size=function(){return this.length},r.bubbleDown=function(t){for(var r=this.length,s=this.keys,i=this.values,e=this.idxs,a=s[t],n=i[t],h=e[t];;){var c=1+(t<<2);if(c>=r)break;var o=c,u=s[c],l=e[c],d=i[c],f=c+1;if(f<r){var v=s[f],m=e[f];(v<u||v===u&&m<l)&&(o=f,u=v,l=m,d=i[f])}var p=c+2;if(p<r){var y=s[p],k=e[p];(y<u||y===u&&k<l)&&(o=p,u=y,l=k,d=i[p])}var g=c+3;if(g<r){var w=s[g],C=e[g];(w<u||w===u&&C<l)&&(o=g,u=w,l=C,d=i[g])}if(!(u<a||u===a&&l<h))break;s[t]=u,i[t]=d,e[t]=l,t=o}s[t]=a,i[t]=n,e[t]=h},t}();exports.TerraRoute=/*#__PURE__*/function(){function t(t){var i,e;this.network=null,this.distanceMeasurement=void 0,this.heapConstructor=void 0,this.coordinateIndexMap=new Map,this.coordinates=[],this.adjacencyList=[],this.csrOffsets=null,this.csrIndices=null,this.csrDistances=null,this.csrNodeCount=0,this.landmarkNodeCount=0,this.landmarkCount=0,this.landmarkDistancesFlat=null,this.maxLandmarks=4,this.landmarksDirty=!0,this.gScoreScratch=null,this.cameFromScratch=null,this.visitedScratch=null,this.heuristicScratch=null,this.heuristicStampScratch=null,this.heuristicQueryStamp=1,this.scratchCapacity=0,this.openForward=null,this.distanceMeasurement=null!=(i=null==t?void 0:t.distanceMeasurement)?i:r,this.heapConstructor=null!=(e=null==t?void 0:t.heap)?e:s}var i=t.prototype;return i.buildRouteGraph=function(t){this.network=t,this.coordinateIndexMap=new Map,this.coordinates=[],this.adjacencyList=[],this.csrOffsets=null,this.csrIndices=null,this.csrDistances=null,this.csrNodeCount=0,this.landmarkNodeCount=0,this.landmarkCount=0,this.landmarkDistancesFlat=null,this.landmarksDirty=!0;for(var r=this.coordinateIndexMap,s=this.coordinates,i=this.distanceMeasurement,e=[],a=[],n=[],h=[],c=t.features,o=0,u=c.length;o<u;o++)for(var l=c[o].geometry.coordinates,d=0,f=l.length-1;d<f;d++){var v,m,p=l[d],y=l[d+1],k=this.indexCoordinate(p,r,s),g=this.indexCoordinate(y,r,s);e[k]=(null!=(v=e[k])?v:0)+1,e[g]=(null!=(m=e[g])?m:0)+1,a.push(k),n.push(g),h.push(i(p,y))}var w=this.coordinates.length;this.csrNodeCount=w;for(var C=new Int32Array(w+1),I=0;I<w;I++){var S,M=null!=(S=e[I])?S:0;C[I+1]=C[I]+M}for(var F=C[w],x=new Int32Array(F),N=new Float64Array(F),D=C.slice(),b=0,A=a.length;b<A;b++){var O=a[b],L=n[b],j=h[b],E=D[O]++;x[E]=L,N[E]=j,x[E=D[L]++]=O,N[E]=j}this.csrOffsets=C,this.csrIndices=x,this.csrDistances=N,this.adjacencyList=new Array(w)},i.expandRouteGraph=function(t){var r=this;if(null===this.network)throw new Error("Network not built. Please call buildRouteGraph(network) first.");this.network={type:"FeatureCollection",features:[].concat(this.network.features,t.features)};for(var s=this.coordinateIndexMap,i=this.coordinates,e=this.distanceMeasurement,a=this.adjacencyList,n=0;n<a.length;n++)void 0===a[n]&&(a[n]=[]);this.forEachSegment(t,function(t,n){var h=r.indexCoordinate(t,s,i,function(t){a[t]=[]}),c=r.indexCoordinate(n,s,i,function(t){a[t]=[]}),o=e(t,n);a[h].push({node:c,distance:o}),a[c].push({node:h,distance:o})}),this.rebuildCsrFromAdjacency()},i.rebuildCsrFromAdjacency=function(){var t=this.coordinates.length,r=this.adjacencyList,s=new Int32Array(t);if(this.csrOffsets&&this.csrIndices&&this.csrDistances)for(var i=this.csrOffsets,e=Math.min(this.csrNodeCount,t),a=0;a<e;a++)s[a]+=i[a+1]-i[a];for(var n=0;n<t;n++){var h=r[n];h&&h.length&&(s[n]+=h.length)}for(var c=new Int32Array(t+1),o=0;o<t;o++)c[o+1]=c[o]+s[o];var u=c[t],l=new Int32Array(u),d=new Float64Array(u),f=c.slice();if(this.csrOffsets&&this.csrIndices&&this.csrDistances)for(var v=this.csrOffsets,m=this.csrIndices,p=this.csrDistances,y=Math.min(this.csrNodeCount,t),k=0;k<y;k++){for(var g=v[k+1],w=f[k],C=v[k];C<g;C++)l[w]=m[C],d[w]=p[C],w++;f[k]=w}for(var I=0;I<t;I++){var S=r[I];if(S&&0!==S.length){for(var M=f[I],F=0,x=S.length;F<x;F++){var N=S[F];l[M]=N.node,d[M]=N.distance,M++}f[I]=M}}this.csrOffsets=c,this.csrIndices=l,this.csrDistances=d,this.csrNodeCount=t,this.landmarksDirty=!0,this.landmarkNodeCount=0,this.landmarkCount=0,this.landmarkDistancesFlat=null,this.adjacencyList=new Array(t)},i.getRoute=function(t,r){var s;if(null===this.network)throw new Error("Network not built. Please call buildRouteGraph(network) first.");var i=this.getOrCreateIndex(t.geometry.coordinates),e=this.getOrCreateIndex(r.geometry.coordinates);if(i===e)return null;var a=this.coordinates,n=this.adjacencyList,h=this.csrOffsets,c=this.csrIndices,o=this.csrDistances,u=this.csrNodeCount,l=!!h,d=Number.POSITIVE_INFINITY,f=this.distanceMeasurement,v=r.geometry.coordinates;this.ensureLandmarkHeuristicData();var m=this.landmarkDistancesFlat,p=this.landmarkNodeCount,y=this.landmarkCount,k=a.length;this.ensureScratch(k);var g=this.gScoreScratch,w=this.cameFromScratch,C=this.visitedScratch,I=this.heuristicScratch,S=this.heuristicStampScratch;g.fill(d,0,k),w.fill(-1,0,k),C.fill(0,0,k);var M=this.heuristicQueryStamp+1>>>0;0===M&&(S.fill(0,0,k),M=1),this.heuristicQueryStamp=M;var F=function(t){if(S[t]!==M)if(S[t]=M,m&&y>0&&t<p&&e<p){for(var r=0,s=0,i=0;s<y;s++,i+=p){var n=m[i+t],h=m[i+e];if(Number.isFinite(n)&&Number.isFinite(h)){var c=h>=n?h-n:n-h;c>r&&(r=c)}}I[t]=r}else I[t]=f(a[t],v);return I[t]},x=null!=(s=this.openForward)?s:this.openForward=new this.heapConstructor,N=x,D=!!N.clear,b=D?x:new this.heapConstructor;for(D&&N.clear(),g[i]=0,b.insert(F(i),i);b.size()>0;){var A=b.extractMin();if(null===A)break;if(0===C[A]){if(A===e)break;C[A]=1;var O=g[A];if(l&&A<u)for(var L=h[A],j=h[A+1];L<j;L++){var E=c[L],P=O+o[L];P>=g[E]||(g[E]=P,w[E]=A,b.insert(P+F(E),E))}else{var R=n[A];if(!R||0===R.length)continue;for(var T=0,q=R.length;T<q;T++){var G=R[T],H=G.node,U=O+G.distance;U>=g[H]||(g[H]=U,w[H]=A,b.insert(U+F(H),H))}}}}if(g[e]===d)return null;for(var z=[],Q=e;Q!==i&&Q>=0;)z.push(a[Q]),Q=w[Q];return z.push(a[i]),z.reverse(),{type:"Feature",geometry:{type:"LineString",coordinates:z},properties:{}}},i.buildLandmarkHeuristicData=function(){var t=this.csrOffsets,r=this.csrIndices,s=this.csrDistances,i=this.csrNodeCount;if(!t||!r||!s||0===i)return this.landmarkNodeCount=0,this.landmarkCount=0,void(this.landmarkDistancesFlat=null);for(var e=Math.min(this.maxLandmarks,i),a=new Uint8Array(i),n=[],h=0,c=0;c<e;c++){a[h]=1;var o=this.computeShortestDistancesFrom(h,i,t,r,s);n.push(o);for(var u=-1,l=-1,d=0;d<i;d++)if(0===a[d]){var f=o[d];Number.isFinite(f)&&f>u&&(u=f,l=d)}if(l<0)break;h=l}for(var v=n.length,m=new Float64Array(v*i),p=0;p<v;p++)m.set(n[p],p*i);this.landmarkNodeCount=i,this.landmarkCount=v,this.landmarkDistancesFlat=m,this.landmarksDirty=!1},i.ensureLandmarkHeuristicData=function(){this.landmarksDirty&&this.buildLandmarkHeuristicData()},i.computeShortestDistancesFrom=function(t,r,s,i,e){var a=Number.POSITIVE_INFINITY,n=new Float64Array(r),h=new Uint8Array(r);n.fill(a),n[t]=0;var c=new this.heapConstructor;for(c.insert(0,t);c.size()>0;){var o=c.extractMin();if(null===o)break;if(0===h[o]){h[o]=1;for(var u=n[o],l=s[o],d=s[o+1];l<d;l++){var f=i[l],v=u+e[l];v>=n[f]||(n[f]=v,c.insert(v,f))}}}return n},i.getOrCreateIndex=function(t){var r=t[0],s=t[1],i=this.coordinateIndexMap.get(r);void 0===i&&(i=new Map,this.coordinateIndexMap.set(r,i));var e=i.get(s);if(void 0===e&&(e=this.coordinates.length,this.coordinates.push(t),i.set(s,e),this.adjacencyList[e]=[],this.csrOffsets)){var a=this.csrNodeCount;if(e===a){var n=new Int32Array(a+2);n.set(this.csrOffsets,0),n[a+1]=n[a],this.csrOffsets=n,this.csrNodeCount=a+1}}return e},i.ensureScratch=function(t){if(!(this.scratchCapacity>=t&&this.gScoreScratch&&this.cameFromScratch&&this.visitedScratch&&this.heuristicScratch&&this.heuristicStampScratch)){var r=0|t;this.gScoreScratch=new Float64Array(r),this.cameFromScratch=new Int32Array(r),this.visitedScratch=new Uint8Array(r),this.heuristicScratch=new Float64Array(r),this.heuristicStampScratch=new Uint32Array(r),this.scratchCapacity=r}},i.forEachSegment=function(t,r){for(var s=t.features,i=0,e=s.length;i<e;i++)for(var a=s[i].geometry.coordinates,n=0,h=a.length-1;n<h;n++)r(a[n],a[n+1])},i.indexCoordinate=function(t,r,s,i){var e=t[0],a=t[1],n=r.get(e);void 0===n&&(n=new Map,r.set(e,n));var h=n.get(a);return void 0===h&&(h=s.length,s.push(t),n.set(a,h),i&&i(h)),h},t}(),exports.createCheapRuler=function(t){var r=1/298.257223563,s=r*(2-r),i=Math.PI/180,e=Math.cos(t*i),a=1/(1-s*(1-e*e)),n=Math.sqrt(a),h=6378.137*i,c=h*n*e,o=h*n*a*(1-s);return function(t,r){for(var s=t[0]-r[0];s<-180;)s+=360;for(;s>180;)s-=360;var i=s*c,e=(t[1]-r[1])*o;return Math.sqrt(i*i+e*e)}},exports.haversineDistance=r;
2
2
  //# sourceMappingURL=terra-route.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"terra-route.cjs","sources":["../src/distance/haversine.ts","../src/heap/four-ary-heap.ts","../src/terra-route.ts","../src/distance/cheap-ruler.ts"],"sourcesContent":["import { Position } from \"geojson\";\n\n/** Distance measured in kilometers */\nexport const haversineDistance = (pointOne: Position, pointTwo: Position): number => {\n const toRadians = (latOrLng: number) => (latOrLng * Math.PI) / 180;\n\n const phiOne = toRadians(pointOne[1]);\n const lambdaOne = toRadians(pointOne[0]);\n const phiTwo = toRadians(pointTwo[1]);\n const lambdaTwo = toRadians(pointTwo[0]);\n const deltaPhi = phiTwo - phiOne;\n const deltalambda = lambdaTwo - lambdaOne;\n\n const a =\n Math.sin(deltaPhi / 2) * Math.sin(deltaPhi / 2) +\n Math.cos(phiOne) *\n Math.cos(phiTwo) *\n Math.sin(deltalambda / 2) *\n Math.sin(deltalambda / 2);\n const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));\n\n const radius = 6371e3;\n const distance = radius * c;\n\n\n // Convert distance from meters to kilometers\n return distance / 1000;\n}\n","import { Heap } from \"./heap\";\n\ninterface Node {\n key: number;\n value: number;\n index: number; // insertion order for stable tie-breaking\n}\n\n/**\n * A 4-ary min-heap with stable tie-breaking on insertion order.\n * Parent(i) = floor((i - 1) / 4)\n * Children(i) = 4*i + 1 .. 4*i + 4\n */\nexport class FourAryHeap implements Heap {\n // Parallel arrays for fewer object allocations and faster property access\n private keys: number[] = [];\n private values: number[] = [];\n private idxs: number[] = [];\n private length = 0; // number of valid elements\n private insertCounter = 0;\n\n insert(key: number, value: number): void {\n // Bubble-up using local variables (avoid temporary node object)\n let i = this.length;\n this.length = i + 1;\n\n let ck = key;\n let cv = value;\n let ci = this.insertCounter++;\n\n while (i > 0) {\n const p = (i - 1) >>> 2; // divide by 4\n const pk = this.keys[p];\n const pi = this.idxs[p];\n if (ck > pk || (ck === pk && ci > pi)) break;\n\n // move parent down\n this.keys[i] = pk;\n this.values[i] = this.values[p];\n this.idxs[i] = pi;\n i = p;\n }\n\n // place the new node\n this.keys[i] = ck;\n this.values[i] = cv;\n this.idxs[i] = ci;\n }\n\n extractMin(): number | null {\n const n = this.length;\n if (n === 0) return null;\n\n const minValue = this.values[0];\n const last = n - 1;\n this.length = last;\n\n if (last > 0) {\n // Move last element to root then bubble down\n this.keys[0] = this.keys[last];\n this.values[0] = this.values[last];\n this.idxs[0] = this.idxs[last];\n this.bubbleDown(0);\n }\n\n return minValue;\n }\n\n size(): number {\n return this.length;\n }\n\n private bubbleDown(i: number): void {\n const n = this.length;\n const k = this.keys;\n const v = this.values;\n const idx = this.idxs;\n\n const nodeK = k[i];\n const nodeV = v[i];\n const nodeI = idx[i];\n\n while (true) {\n const c1 = (i << 2) + 1; // 4*i + 1\n if (c1 >= n) break; // no children\n\n // find smallest among up to 4 children\n let smallest = c1;\n let sK = k[c1];\n let sI = idx[c1];\n let sV = v[c1];\n\n const c2 = c1 + 1;\n if (c2 < n) {\n const k2 = k[c2];\n const i2 = idx[c2];\n if (k2 < sK || (k2 === sK && i2 < sI)) {\n smallest = c2;\n sK = k2;\n sI = i2;\n sV = v[c2];\n }\n }\n\n const c3 = c1 + 2;\n if (c3 < n) {\n const k3 = k[c3];\n const i3 = idx[c3];\n if (k3 < sK || (k3 === sK && i3 < sI)) {\n smallest = c3;\n sK = k3;\n sI = i3;\n sV = v[c3];\n }\n }\n\n const c4 = c1 + 3;\n if (c4 < n) {\n const k4 = k[c4];\n const i4 = idx[c4];\n if (k4 < sK || (k4 === sK && i4 < sI)) {\n smallest = c4;\n sK = k4;\n sI = i4;\n sV = v[c4];\n }\n }\n\n if (sK < nodeK || (sK === nodeK && sI < nodeI)) {\n k[i] = sK;\n v[i] = sV;\n idx[i] = sI;\n i = smallest;\n } else {\n break;\n }\n }\n\n k[i] = nodeK;\n v[i] = nodeV;\n idx[i] = nodeI;\n }\n}\n","import { FeatureCollection, LineString, Point, Feature, Position } from \"geojson\"; // Import GeoJSON types\nimport { haversineDistance } from \"./distance/haversine\"; // Great-circle distance function (default heuristic/edge weight)\nimport { createCheapRuler } from \"./distance/cheap-ruler\"; // Factory for faster planar distance (exported for consumers)\nimport { HeapConstructor } from \"./heap/heap\"; // Heap interface so users can plug custom heaps\nimport { FourAryHeap } from \"./heap/four-ary-heap\";\n\ninterface Router {\n buildRouteGraph(network: FeatureCollection<LineString>): void;\n expandRouteGraph(network: FeatureCollection<LineString>): void;\n getRoute(start: Feature<Point>, end: Feature<Point>): Feature<LineString> | null;\n}\n\nclass TerraRoute implements Router {\n private network: FeatureCollection<LineString> | null = null; // The last network used to build the graph\n private distanceMeasurement: (a: Position, b: Position) => number; // Distance function used for edges and heuristic\n private heapConstructor: HeapConstructor; // Heap class used by A*\n\n // Map from longitude → (map from latitude → index) to deduplicate coordinates and get node indices quickly\n private coordinateIndexMap: Map<number, Map<number, number>> = new Map(); // Nested map for exact coord lookup\n private coordinates: Position[] = []; // Array of all unique coordinates by index\n // Sparse adjacency list used during build and for any nodes added dynamically later\n private adjacencyList: Array<Array<{ node: number, distance: number }>> = []; // Per-node neighbor arrays used only pre-CSR or for dynamic nodes\n\n // Compressed Sparse Row adjacency representation for fast neighbor iteration in getRoute\n private csrOffsets: Int32Array | null = null; // Row pointer: length = nodeCount + 1, offsets into indices/distances\n private csrIndices: Int32Array | null = null; // Column indices: neighbor node IDs, length = totalEdges\n private csrDistances: Float64Array | null = null; // Edge weights aligned to csrIndices, length = totalEdges\n private csrNodeCount = 0; // Number of nodes captured in the CSR arrays\n\n // Reusable typed scratch buffers for A*\n private gScoreScratch: Float64Array | null = null; // gScore per node (cost from start)\n private cameFromScratch: Int32Array | null = null; // Predecessor per node for path reconstruction\n private visitedScratch: Uint8Array | null = null; // Visited set to avoid reprocessing\n private hScratch: Float64Array | null = null; // Per-node heuristic cache for the current query (lazy compute)\n // Reverse-direction scratch for bidirectional search\n private gScoreRevScratch: Float64Array | null = null; // gScore per node from the end\n private cameFromRevScratch: Int32Array | null = null; // Successor per node (next step toward the end)\n private visitedRevScratch: Uint8Array | null = null; // Visited set for reverse search\n private hRevScratch: Float64Array | null = null; // Heuristic cache for reverse direction per query\n private scratchCapacity = 0; // Current capacity of scratch arrays\n\n constructor(options?: {\n distanceMeasurement?: (a: Position, b: Position) => number; // Optional distance function override\n heap?: HeapConstructor; // Optional heap implementation override\n }) {\n this.distanceMeasurement = options?.distanceMeasurement ?? haversineDistance; // Default to haversine\n this.heapConstructor = options?.heap ?? FourAryHeap; // Default to MinHeap\n }\n\n /**\n * Builds a graph (CSR) from a LineString FeatureCollection.\n * Two-pass build: pass 1 assigns node indices and counts degrees; pass 2 fills CSR arrays.\n */\n public buildRouteGraph(network: FeatureCollection<LineString>): void {\n this.network = network; // Keep a reference to the network\n\n // Reset internal structures for a fresh build\n this.coordinateIndexMap = new Map(); // Clear coordinate index map\n this.coordinates = []; // Clear coordinates array\n this.adjacencyList = []; // Will not be populated during build; reserved for dynamic nodes post-build\n // Reset CSR structures (will rebuild below)\n this.csrOffsets = null;\n this.csrIndices = null;\n this.csrDistances = null;\n this.csrNodeCount = 0;\n\n // Hoist to locals for speed (avoid repeated property lookups in hot loops)\n const coordIndexMapLocal = this.coordinateIndexMap; // Local alias for coord map\n const coordsLocal = this.coordinates; // Local alias for coordinates array\n const measureDistance = this.distanceMeasurement; // Local alias for distance function\n\n // Pass 1: assign indices and count degrees per node\n const degree: number[] = []; // Dynamic degree array; grows as nodes are discovered\n this.forEachSegment(network, (a, b) => {\n const indexA = this.indexCoordinate(a, coordIndexMapLocal, coordsLocal);\n const indexB = this.indexCoordinate(b, coordIndexMapLocal, coordsLocal);\n\n degree[indexA] = (degree[indexA] ?? 0) + 1;\n degree[indexB] = (degree[indexB] ?? 0) + 1;\n });\n\n // Build CSR arrays from degree counts\n const nodeCount = this.coordinates.length; // Total nodes discovered\n this.csrNodeCount = nodeCount; // CSR covers all built nodes\n const offsets = new Int32Array(nodeCount + 1); // Row pointer array\n for (let i = 0; i < nodeCount; i++) {\n const deg = degree[i] ?? 0; // Degree of node i\n offsets[i + 1] = offsets[i] + deg; // Prefix sum\n }\n const totalEdges = offsets[nodeCount]; // Total adjacency entries\n const indices = new Int32Array(totalEdges); // Neighbor indices array\n const distances = new Float64Array(totalEdges); // Distances array aligned to indices\n\n // Pass 2: fill CSR arrays using a write cursor per node\n const cursor = offsets.slice(); // Current write positions per node\n this.forEachSegment(network, (a, b) => {\n // Read back indices (guaranteed to exist from pass 1)\n const indexA = this.coordinateIndexMap.get(a[0])!.get(a[1])!;\n const indexB = this.coordinateIndexMap.get(b[0])!.get(b[1])!;\n\n const segmentDistance = measureDistance(a, b); // Edge weight once\n\n let pos = cursor[indexA]++;\n indices[pos] = indexB;\n distances[pos] = segmentDistance;\n pos = cursor[indexB]++;\n indices[pos] = indexA;\n distances[pos] = segmentDistance;\n });\n\n // Commit CSR to instance\n this.csrOffsets = offsets;\n this.csrIndices = indices;\n this.csrDistances = distances;\n\n // Prepare sparse shell only for dynamically added nodes later (no prefilled neighbor arrays)\n this.adjacencyList = new Array(nodeCount);\n }\n\n /**\n * Expands (merges) the existing graph with an additional LineString FeatureCollection.\n */\n public expandRouteGraph(network: FeatureCollection<LineString>): void {\n if (this.network === null) {\n throw new Error(\"Network not built. Please call buildRouteGraph(network) first.\");\n }\n\n // Merge the feature arrays for reference/debugging. We avoid copying properties deeply.\n this.network = {\n type: \"FeatureCollection\",\n features: [...this.network.features, ...network.features],\n };\n\n const coordIndexMapLocal = this.coordinateIndexMap;\n const coordsLocal = this.coordinates;\n const measureDistance = this.distanceMeasurement;\n const adj = this.adjacencyList;\n\n // Ensure we have adjacency arrays for any existing CSR-only nodes.\n // (During buildRouteGraph, adjacencyList is sized but entries are undefined.)\n for (let i = 0; i < adj.length; i++) {\n if (adj[i] === undefined) adj[i] = [];\n }\n\n // Add new edges into the adjacency list (sparse), then rebuild CSR from adjacency.\n this.forEachSegment(network, (a, b) => {\n const indexA = this.indexCoordinate(a, coordIndexMapLocal, coordsLocal, (idx) => { adj[idx] = []; });\n const indexB = this.indexCoordinate(b, coordIndexMapLocal, coordsLocal, (idx) => { adj[idx] = []; });\n\n const segmentDistance = measureDistance(a, b);\n\n adj[indexA].push({ node: indexB, distance: segmentDistance });\n adj[indexB].push({ node: indexA, distance: segmentDistance });\n });\n\n this.rebuildCsrFromAdjacency();\n }\n\n /**\n * Rebuild CSR arrays for the full node set, using:\n * - Existing CSR edges (from the last build/expand)\n * - Any additional edges stored in `adjacencyList`\n */\n private rebuildCsrFromAdjacency(): void {\n const nodeCount = this.coordinates.length;\n const adj = this.adjacencyList;\n\n // Compute degree using CSR degree + adjacency degree\n const degree = new Int32Array(nodeCount);\n\n if (this.csrOffsets && this.csrIndices && this.csrDistances) {\n const csrOffsets = this.csrOffsets;\n const covered = Math.min(this.csrNodeCount, nodeCount);\n for (let i = 0; i < covered; i++) {\n degree[i] += (csrOffsets[i + 1] - csrOffsets[i]);\n }\n }\n\n for (let i = 0; i < nodeCount; i++) {\n const neighbors = adj[i];\n if (neighbors && neighbors.length) degree[i] += neighbors.length;\n }\n\n const offsets = new Int32Array(nodeCount + 1);\n for (let i = 0; i < nodeCount; i++) {\n offsets[i + 1] = offsets[i] + degree[i];\n }\n const totalEdges = offsets[nodeCount];\n const indices = new Int32Array(totalEdges);\n const distances = new Float64Array(totalEdges);\n const cursor = offsets.slice();\n\n // Copy existing CSR edges first\n if (this.csrOffsets && this.csrIndices && this.csrDistances) {\n const csrOffsets = this.csrOffsets;\n const csrIndices = this.csrIndices;\n const csrDistances = this.csrDistances;\n const covered = Math.min(this.csrNodeCount, nodeCount);\n for (let n = 0; n < covered; n++) {\n const startOff = csrOffsets[n];\n const endOff = csrOffsets[n + 1];\n let pos = cursor[n];\n for (let i = startOff; i < endOff; i++) {\n indices[pos] = csrIndices[i];\n distances[pos] = csrDistances[i];\n pos++;\n }\n cursor[n] = pos;\n }\n }\n\n // Append adjacency edges\n for (let n = 0; n < nodeCount; n++) {\n const neighbors = adj[n];\n if (!neighbors || neighbors.length === 0) continue;\n let pos = cursor[n];\n for (let i = 0, len = neighbors.length; i < len; i++) {\n const nb = neighbors[i];\n indices[pos] = nb.node;\n distances[pos] = nb.distance;\n pos++;\n }\n cursor[n] = pos;\n }\n\n // Commit and reset adjacency (we've absorbed edges into CSR)\n this.csrOffsets = offsets;\n this.csrIndices = indices;\n this.csrDistances = distances;\n this.csrNodeCount = nodeCount;\n\n // Keep adjacency list for *future* dynamic additions, but clear existing edges to avoid duplication.\n this.adjacencyList = new Array(nodeCount);\n }\n\n /**\n * Computes the shortest route between two points in the network using the A* algorithm.\n * \n * @param start - A GeoJSON Point Feature representing the start location.\n * @param end - A GeoJSON Point Feature representing the end location.\n * @returns A GeoJSON LineString Feature representing the shortest path, or null if no path is found.\n * \n * @throws Error if the network has not been built yet with buildRouteGraph(network).\n */\n public getRoute(\n start: Feature<Point>, // Start point feature\n end: Feature<Point> // End point feature\n ): Feature<LineString> | null {\n if (this.network === null) { // Guard: graph must be built first\n throw new Error(\"Network not built. Please call buildRouteGraph(network) first.\");\n }\n\n // Ensure start/end exist in index maps\n const startIndex = this.getOrCreateIndex(start.geometry.coordinates); // Get or insert start node index\n const endIndex = this.getOrCreateIndex(end.geometry.coordinates); // Get or insert end node index\n\n // Trivial case: same node\n if (startIndex === endIndex) {\n return null; // No path needed\n }\n\n // Local aliases\n const coords = this.coordinates; // Alias to coordinates array\n const adj = this.adjacencyList; // Alias to sparse adjacency list (for dynamic nodes)\n\n // Ensure and init scratch buffers\n const nodeCount = coords.length; // Current number of nodes (may be >= csrNodeCount if new nodes added)\n this.ensureScratch(nodeCount); // Allocate scratch arrays if needed\n\n // Non-null after ensure\n const gF = this.gScoreScratch!; // forward gScore (from start)\n const gR = this.gScoreRevScratch!; // reverse gScore (from end)\n const prevF = this.cameFromScratch!; // predecessor in forward search\n const nextR = this.cameFromRevScratch!; // successor in reverse search (toward end)\n const visF = this.visitedScratch!;\n const visR = this.visitedRevScratch!;\n const hF = this.hScratch!;\n const hR = this.hRevScratch!;\n\n gF.fill(Number.POSITIVE_INFINITY, 0, nodeCount);\n gR.fill(Number.POSITIVE_INFINITY, 0, nodeCount);\n prevF.fill(-1, 0, nodeCount);\n nextR.fill(-1, 0, nodeCount);\n visF.fill(0, 0, nodeCount);\n visR.fill(0, 0, nodeCount);\n hF.fill(-1, 0, nodeCount);\n hR.fill(-1, 0, nodeCount);\n\n const openF = new this.heapConstructor();\n const openR = new this.heapConstructor();\n\n const startCoord = coords[startIndex];\n const endCoord = coords[endIndex];\n\n gF[startIndex] = 0;\n gR[endIndex] = 0;\n\n // Bidirectional Dijkstra (A* with zero heuristic). This keeps correctness simple and matches the\n // reference pathfinder while still saving work by meeting in the middle.\n openF.insert(0, startIndex);\n openR.insert(0, endIndex);\n\n // Best meeting point found so far\n let bestPathCost = Number.POSITIVE_INFINITY;\n let meetingNode = -1;\n\n // Main bidirectional loop: expand alternately.\n // Without a heap peek, a safe and effective stopping rule is based on the last extracted keys:\n // once min_g_forward + min_g_reverse >= bestPathCost, no shorter path can still be found.\n let lastExtractedGForward = 0;\n let lastExtractedGReverse = 0;\n while (openF.size() > 0 && openR.size() > 0) {\n if (meetingNode >= 0 && (lastExtractedGForward + lastExtractedGReverse) >= bestPathCost) {\n break;\n }\n\n // Expand one step from each side, prioritizing the smaller frontier.\n const expandForward = openF.size() <= openR.size();\n\n if (expandForward) {\n const current = openF.extractMin()!;\n if (visF[current] !== 0) continue;\n lastExtractedGForward = gF[current];\n visF[current] = 1;\n\n // If reverse has finalized this node, we have a candidate meeting.\n if (visR[current] !== 0) {\n const total = gF[current] + gR[current];\n if (total < bestPathCost) {\n bestPathCost = total;\n meetingNode = current;\n }\n }\n\n // Relax neighbors and push newly improved ones\n if (this.csrOffsets && current < this.csrNodeCount) {\n const csrOffsets = this.csrOffsets!;\n const csrIndices = this.csrIndices!;\n const csrDistances = this.csrDistances!;\n for (let i = csrOffsets[current], endOff = csrOffsets[current + 1]; i < endOff; i++) {\n const nbNode = csrIndices[i];\n const tentativeG = gF[current] + csrDistances[i];\n if (tentativeG < gF[nbNode]) {\n gF[nbNode] = tentativeG;\n prevF[nbNode] = current;\n const otherG = gR[nbNode];\n if (otherG !== Number.POSITIVE_INFINITY) {\n const total = tentativeG + otherG;\n if (total < bestPathCost) { bestPathCost = total; meetingNode = nbNode; }\n }\n openF.insert(tentativeG, nbNode);\n }\n }\n } else {\n const neighbors = adj[current];\n if (neighbors && neighbors.length) {\n for (let i = 0, n = neighbors.length; i < n; i++) {\n const nb = neighbors[i];\n const nbNode = nb.node;\n const tentativeG = gF[current] + nb.distance;\n if (tentativeG < gF[nbNode]) {\n gF[nbNode] = tentativeG;\n prevF[nbNode] = current;\n const otherG = gR[nbNode];\n if (otherG !== Number.POSITIVE_INFINITY) {\n const total = tentativeG + otherG;\n if (total < bestPathCost) { bestPathCost = total; meetingNode = nbNode; }\n }\n openF.insert(tentativeG, nbNode);\n }\n }\n }\n }\n } else {\n const current = openR.extractMin()!;\n if (visR[current] !== 0) continue;\n lastExtractedGReverse = gR[current];\n visR[current] = 1;\n\n if (visF[current] !== 0) {\n const total = gF[current] + gR[current];\n if (total < bestPathCost) {\n bestPathCost = total;\n meetingNode = current;\n }\n }\n\n // Reverse direction: same neighbor iteration because graph is undirected.\n // Store successor pointer (next step toward end) i.e. nextR[neighbor] = current.\n if (this.csrOffsets && current < this.csrNodeCount) {\n const csrOffsets = this.csrOffsets!;\n const csrIndices = this.csrIndices!;\n const csrDistances = this.csrDistances!;\n for (let i = csrOffsets[current], endOff = csrOffsets[current + 1]; i < endOff; i++) {\n const nbNode = csrIndices[i];\n const tentativeG = gR[current] + csrDistances[i];\n if (tentativeG < gR[nbNode]) {\n gR[nbNode] = tentativeG;\n nextR[nbNode] = current;\n const otherG = gF[nbNode];\n if (otherG !== Number.POSITIVE_INFINITY) {\n const total = tentativeG + otherG;\n if (total < bestPathCost) { bestPathCost = total; meetingNode = nbNode; }\n }\n openR.insert(tentativeG, nbNode);\n }\n }\n } else {\n const neighbors = adj[current];\n if (neighbors && neighbors.length) {\n for (let i = 0, n = neighbors.length; i < n; i++) {\n const nb = neighbors[i];\n const nbNode = nb.node;\n const tentativeG = gR[current] + nb.distance;\n if (tentativeG < gR[nbNode]) {\n gR[nbNode] = tentativeG;\n nextR[nbNode] = current;\n const otherG = gF[nbNode];\n if (otherG !== Number.POSITIVE_INFINITY) {\n const total = tentativeG + otherG;\n if (total < bestPathCost) { bestPathCost = total; meetingNode = nbNode; }\n }\n openR.insert(tentativeG, nbNode);\n }\n }\n }\n }\n }\n }\n\n if (meetingNode < 0) {\n return null;\n }\n\n // Reconstruct path: start -> meeting using prevF, then meeting -> end using nextR\n const path: Position[] = [];\n\n // Walk back from meeting to start, collecting nodes\n let cur = meetingNode;\n while (cur !== startIndex && cur >= 0) {\n path.push(coords[cur]);\n cur = prevF[cur];\n }\n if (cur !== startIndex) {\n // Forward tree doesn't connect start to meeting (shouldn't happen if meeting is valid)\n return null;\n }\n path.push(coords[startIndex]);\n path.reverse();\n\n // Walk from meeting to end (skip meeting node because it's already included)\n cur = meetingNode;\n while (cur !== endIndex) {\n cur = nextR[cur];\n if (cur < 0) {\n return null;\n }\n path.push(coords[cur]);\n }\n\n return {\n type: \"Feature\",\n geometry: { type: \"LineString\", coordinates: path },\n properties: {},\n };\n }\n\n /**\n * Helper to index start/end in getRoute.\n */\n private getOrCreateIndex(coord: Position): number { // Ensure a coordinate has a node index, creating if absent\n const lng = coord[0]; // Extract longitude\n const lat = coord[1]; // Extract latitude\n\n let latMap = this.coordinateIndexMap.get(lng); // Get lat→index map for this longitude\n if (latMap === undefined) { // Create if missing\n latMap = new Map<number, number>();\n this.coordinateIndexMap.set(lng, latMap);\n }\n\n let index = latMap.get(lat); // Lookup index by latitude\n if (index === undefined) { // If not found, append new node\n\n index = this.coordinates.length; // New index at end\n this.coordinates.push(coord); // Store coordinate\n latMap.set(lat, index); // Record mapping\n\n // Ensure sparse adjacency slot for dynamically added nodes\n this.adjacencyList[index] = []; // Init empty neighbor array\n\n // Extend CSR offsets to keep indices consistent (no neighbors for new node)\n if (this.csrOffsets) { // Only adjust if CSR already built\n\n // Only need to expand offsets by one; indices/distances remain unchanged\n const oldCount = this.csrNodeCount; // Nodes currently covered by CSR\n\n // Appending exactly one new node at the end\n if (index === oldCount) {\n const newOffsets = new Int32Array(oldCount + 2); // Allocate offsets for +1 node\n newOffsets.set(this.csrOffsets, 0); // Copy previous offsets\n\n // Last offset repeats to indicate zero neighbors\n newOffsets[oldCount + 1] = newOffsets[oldCount]; // Replicate last pointer\n this.csrOffsets = newOffsets; // Swap in new offsets\n this.csrNodeCount = oldCount + 1; // Increment CSR node count\n }\n }\n }\n\n return index;\n }\n\n // Ensure scratch arrays are allocated with at least `size` capacity.\n private ensureScratch(size: number): void {\n const ifAlreadyBigEnough = this.scratchCapacity >= size\n && this.gScoreScratch\n && this.cameFromScratch\n && this.visitedScratch\n && this.hScratch\n && this.gScoreRevScratch\n && this.cameFromRevScratch\n && this.visitedRevScratch\n && this.hRevScratch;\n\n if (ifAlreadyBigEnough) {\n return; // Nothing to do\n }\n const capacity = size | 0; // Ensure integer\n this.gScoreScratch = new Float64Array(capacity);\n this.cameFromScratch = new Int32Array(capacity);\n this.visitedScratch = new Uint8Array(capacity);\n this.hScratch = new Float64Array(capacity);\n this.gScoreRevScratch = new Float64Array(capacity);\n this.cameFromRevScratch = new Int32Array(capacity);\n this.visitedRevScratch = new Uint8Array(capacity);\n this.hRevScratch = new Float64Array(capacity);\n this.scratchCapacity = capacity;\n }\n\n\n // Iterate all consecutive segment pairs in a LineString FeatureCollection.\n // Kept as a simple loop helper to avoid repeating nested iteration logic.\n private forEachSegment(\n network: FeatureCollection<LineString>,\n fn: (a: Position, b: Position) => void,\n ): void {\n const features = network.features;\n for (let f = 0, fLen = features.length; f < fLen; f++) {\n const lineCoords = features[f].geometry.coordinates;\n for (let i = 0, len = lineCoords.length - 1; i < len; i++) {\n // GeoJSON coordinates are compatible with Position (number[]), and the project assumes [lng,lat]\n fn(lineCoords[i] as Position, lineCoords[i + 1] as Position);\n }\n }\n }\n\n // Hot-path coordinate indexer used by both build/expand.\n // Accepts explicit maps/arrays so callers can hoist them once.\n private indexCoordinate(\n coord: Position,\n coordIndexMapLocal: Map<number, Map<number, number>>,\n coordsLocal: Position[],\n onNewIndex?: (index: number) => void,\n ): number {\n const lng = coord[0];\n const lat = coord[1];\n\n let latMap = coordIndexMapLocal.get(lng);\n if (latMap === undefined) {\n latMap = new Map<number, number>();\n coordIndexMapLocal.set(lng, latMap);\n }\n\n let idx = latMap.get(lat);\n if (idx === undefined) {\n idx = coordsLocal.length;\n coordsLocal.push(coord);\n latMap.set(lat, idx);\n if (onNewIndex) onNewIndex(idx);\n }\n\n return idx;\n }\n\n}\n\nexport { TerraRoute, createCheapRuler, haversineDistance } ","import { Position } from \"geojson\";\n\n// This code is based on Mapbox's cheap-ruler library:\n\n// ISC License\n\n// Copyright (c) 2024, Mapbox\n\n// Permission to use, copy, modify, and/or distribute this software for any purpose\n// with or without fee is hereby granted, provided that the above copyright notice\n// and this permission notice appear in all copies.\n\n// THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH\n// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND\n// FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,\n// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS\n// OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER\n// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF\n// THIS SOFTWARE.\n\n/**\n * Creates a function for fast geodesic distance approximation using local scaling constants\n * based on a reference latitude. Useful for city-scale distances.\n *\n * @param {number} lat - Reference latitude in degrees\n * @returns {(a: Position, b: Position) => number} - Function that computes distance between two points\n * \n * @example\n * const distance = createCheapRuler(50.5);\n * const d = distance([30.5, 50.5], [30.51, 50.49]);\n * \n */\nexport function createCheapRuler(lat: number): (a: Position, b: Position) => number {\n const RE = 6378.137; // Earth's equatorial radius in kilometers\n const FE = 1 / 298.257223563; // Earth's flattening\n const E2 = FE * (2 - FE);\n const RAD = Math.PI / 180;\n\n const cosLat = Math.cos(lat * RAD);\n const w2 = 1 / (1 - E2 * (1 - cosLat * cosLat));\n const w = Math.sqrt(w2);\n\n const m = RAD * RE;\n const kx = m * w * cosLat; // scale for longitude\n const ky = m * w * w2 * (1 - E2); // scale for latitude\n\n return function distance(a: Position, b: Position): number {\n let deltaLng = a[0] - b[0];\n\n while (deltaLng < -180) deltaLng += 360;\n while (deltaLng > 180) deltaLng -= 360;\n\n const dx = deltaLng * kx;\n const dy = (a[1] - b[1]) * ky;\n\n return Math.sqrt(dx * dx + dy * dy);\n };\n}"],"names":["haversineDistance","pointOne","pointTwo","toRadians","latOrLng","Math","PI","phiOne","lambdaOne","phiTwo","deltaPhi","deltalambda","a","sin","cos","atan2","sqrt","FourAryHeap","keys","this","values","idxs","length","insertCounter","_proto","prototype","insert","key","value","i","ck","cv","ci","p","pk","pi","extractMin","n","minValue","last","bubbleDown","size","k","v","idx","nodeK","nodeV","nodeI","c1","smallest","sK","sI","sV","c2","k2","i2","c3","k3","i3","c4","k4","i4","TerraRoute","options","_options$distanceMeas","_options$heap","network","distanceMeasurement","heapConstructor","coordinateIndexMap","Map","coordinates","adjacencyList","csrOffsets","csrIndices","csrDistances","csrNodeCount","gScoreScratch","cameFromScratch","visitedScratch","hScratch","gScoreRevScratch","cameFromRevScratch","visitedRevScratch","hRevScratch","scratchCapacity","heap","buildRouteGraph","_this","coordIndexMapLocal","coordsLocal","measureDistance","degree","forEachSegment","b","_degree$indexA","_degree$indexB","indexA","indexCoordinate","indexB","nodeCount","offsets","Int32Array","_degree$i","deg","totalEdges","indices","distances","Float64Array","cursor","slice","get","segmentDistance","pos","Array","expandRouteGraph","_this2","Error","type","features","concat","adj","undefined","push","node","distance","rebuildCsrFromAdjacency","covered","min","neighbors","endOff","len","nb","getRoute","start","end","startIndex","getOrCreateIndex","geometry","endIndex","coords","ensureScratch","gF","gR","prevF","nextR","visF","visR","hF","hR","fill","Number","POSITIVE_INFINITY","openF","openR","bestPathCost","meetingNode","lastExtractedGForward","lastExtractedGReverse","current","total","nbNode","tentativeG","otherG","path","cur","reverse","properties","coord","lng","lat","latMap","set","index","oldCount","newOffsets","capacity","Uint8Array","fn","f","fLen","lineCoords","onNewIndex","FE","E2","RAD","cosLat","w2","w","m","kx","ky","deltaLng","dx","dy"],"mappings":"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"}
1
+ {"version":3,"file":"terra-route.cjs","sources":["../src/distance/haversine.ts","../src/heap/four-ary-heap.ts","../src/terra-route.ts","../src/distance/cheap-ruler.ts"],"sourcesContent":["import { Position } from \"geojson\";\n\nconst DEG_TO_RAD = Math.PI / 180;\nconst EARTH_RADIUS_KM = 6371;\n\n/** Distance measured in kilometers */\nexport const haversineDistance = (pointOne: Position, pointTwo: Position): number => {\n const phiOne = pointOne[1] * DEG_TO_RAD;\n const lambdaOne = pointOne[0] * DEG_TO_RAD;\n const phiTwo = pointTwo[1] * DEG_TO_RAD;\n const lambdaTwo = pointTwo[0] * DEG_TO_RAD;\n const deltaPhi = phiTwo - phiOne;\n const deltalambda = lambdaTwo - lambdaOne;\n const sinDeltaPhi = Math.sin(deltaPhi / 2);\n const sinDeltaLambda = Math.sin(deltalambda / 2);\n\n const a =\n sinDeltaPhi * sinDeltaPhi +\n Math.cos(phiOne) *\n Math.cos(phiTwo) *\n sinDeltaLambda *\n sinDeltaLambda;\n const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));\n\n return EARTH_RADIUS_KM * c;\n}\n","import { Heap } from \"./heap\";\n\ninterface Node {\n key: number;\n value: number;\n index: number; // insertion order for stable tie-breaking\n}\n\n/**\n * A 4-ary min-heap with stable tie-breaking on insertion order.\n * Parent(i) = floor((i - 1) / 4)\n * Children(i) = 4*i + 1 .. 4*i + 4\n */\nexport class FourAryHeap implements Heap {\n // Parallel arrays for fewer object allocations and faster property access\n private keys: number[] = [];\n private values: number[] = [];\n private idxs: number[] = [];\n private length = 0; // number of valid elements\n private insertCounter = 0;\n\n insert(key: number, value: number): void {\n // Bubble-up using local variables (avoid temporary node object)\n let i = this.length;\n this.length = i + 1;\n\n let ck = key;\n let cv = value;\n let ci = this.insertCounter++;\n\n while (i > 0) {\n const p = (i - 1) >>> 2; // divide by 4\n const pk = this.keys[p];\n const pi = this.idxs[p];\n if (ck > pk || (ck === pk && ci > pi)) break;\n\n // move parent down\n this.keys[i] = pk;\n this.values[i] = this.values[p];\n this.idxs[i] = pi;\n i = p;\n }\n\n // place the new node\n this.keys[i] = ck;\n this.values[i] = cv;\n this.idxs[i] = ci;\n }\n\n extractMin(): number | null {\n const n = this.length;\n if (n === 0) return null;\n\n const minValue = this.values[0];\n const last = n - 1;\n this.length = last;\n\n if (last > 0) {\n // Move last element to root then bubble down\n this.keys[0] = this.keys[last];\n this.values[0] = this.values[last];\n this.idxs[0] = this.idxs[last];\n this.bubbleDown(0);\n }\n\n return minValue;\n }\n\n peekMinKey(): number {\n return this.length === 0 ? Number.POSITIVE_INFINITY : this.keys[0];\n }\n\n clear(): void {\n // Keep backing arrays to avoid allocations; just reset counters.\n this.length = 0;\n this.insertCounter = 0;\n }\n\n size(): number {\n return this.length;\n }\n\n private bubbleDown(i: number): void {\n const n = this.length;\n const k = this.keys;\n const v = this.values;\n const idx = this.idxs;\n\n const nodeK = k[i];\n const nodeV = v[i];\n const nodeI = idx[i];\n\n while (true) {\n const c1 = (i << 2) + 1; // 4*i + 1\n if (c1 >= n) break; // no children\n\n // find smallest among up to 4 children\n let smallest = c1;\n let sK = k[c1];\n let sI = idx[c1];\n let sV = v[c1];\n\n const c2 = c1 + 1;\n if (c2 < n) {\n const k2 = k[c2];\n const i2 = idx[c2];\n if (k2 < sK || (k2 === sK && i2 < sI)) {\n smallest = c2;\n sK = k2;\n sI = i2;\n sV = v[c2];\n }\n }\n\n const c3 = c1 + 2;\n if (c3 < n) {\n const k3 = k[c3];\n const i3 = idx[c3];\n if (k3 < sK || (k3 === sK && i3 < sI)) {\n smallest = c3;\n sK = k3;\n sI = i3;\n sV = v[c3];\n }\n }\n\n const c4 = c1 + 3;\n if (c4 < n) {\n const k4 = k[c4];\n const i4 = idx[c4];\n if (k4 < sK || (k4 === sK && i4 < sI)) {\n smallest = c4;\n sK = k4;\n sI = i4;\n sV = v[c4];\n }\n }\n\n if (sK < nodeK || (sK === nodeK && sI < nodeI)) {\n k[i] = sK;\n v[i] = sV;\n idx[i] = sI;\n i = smallest;\n } else {\n break;\n }\n }\n\n k[i] = nodeK;\n v[i] = nodeV;\n idx[i] = nodeI;\n }\n}\n","import { FeatureCollection, LineString, Point, Feature, Position } from \"geojson\"; // Import GeoJSON types\nimport { haversineDistance } from \"./distance/haversine\"; // Great-circle distance function (default heuristic/edge weight)\nimport { createCheapRuler } from \"./distance/cheap-ruler\"; // Factory for faster planar distance (exported for consumers)\nimport { HeapConstructor } from \"./heap/heap\"; // Heap interface so users can plug custom heaps\nimport { FourAryHeap } from \"./heap/four-ary-heap\";\n\ninterface Router {\n buildRouteGraph(network: FeatureCollection<LineString>): void;\n expandRouteGraph(network: FeatureCollection<LineString>): void;\n getRoute(start: Feature<Point>, end: Feature<Point>): Feature<LineString> | null;\n}\n\nclass TerraRoute implements Router {\n private network: FeatureCollection<LineString> | null = null; // The last network used to build the graph\n private distanceMeasurement: (a: Position, b: Position) => number; // Distance function used for edges and heuristic\n private heapConstructor: HeapConstructor; // Heap class used by A*\n\n // Map from longitude → (map from latitude → index) to deduplicate coordinates and get node indices quickly\n private coordinateIndexMap: Map<number, Map<number, number>> = new Map(); // Nested map for exact coord lookup\n private coordinates: Position[] = []; // Array of all unique coordinates by index\n // Sparse adjacency list used during build and for any nodes added dynamically later\n private adjacencyList: Array<Array<{ node: number, distance: number }>> = []; // Per-node neighbor arrays used only pre-CSR or for dynamic nodes\n\n // Compressed Sparse Row adjacency representation for fast neighbor iteration in getRoute\n private csrOffsets: Int32Array | null = null; // Row pointer: length = nodeCount + 1, offsets into indices/distances\n private csrIndices: Int32Array | null = null; // Column indices: neighbor node IDs, length = totalEdges\n private csrDistances: Float64Array | null = null; // Edge weights aligned to csrIndices, length = totalEdges\n private csrNodeCount = 0; // Number of nodes captured in the CSR arrays\n // ALT-style landmark heuristic data (query-time lower bounds via triangle inequality)\n private landmarkNodeCount = 0;\n private landmarkCount = 0;\n private landmarkDistancesFlat: Float64Array | null = null; // Layout: [landmark0 distances..., landmark1 distances...]\n private readonly maxLandmarks = 4;\n private landmarksDirty = true;\n\n // Reusable typed scratch buffers for shortest-path search\n private gScoreScratch: Float64Array | null = null; // gScore per node (cost from start)\n private cameFromScratch: Int32Array | null = null; // Predecessor per node for path reconstruction\n private visitedScratch: Uint8Array | null = null; // Visited set to avoid reprocessing\n private heuristicScratch: Float64Array | null = null; // Cached h(node) per query for A*\n private heuristicStampScratch: Uint32Array | null = null; // Query-stamp per node for heuristic cache validity\n private heuristicQueryStamp = 1; // Monotonic stamp to avoid clearing heuristic cache each query\n private scratchCapacity = 0; // Current capacity of scratch arrays\n\n // Reused open set to avoid heap allocations during repeated getRoute calls.\n // (If a custom heap doesn't implement `clear()`, we fall back to constructing anew.)\n private openForward: InstanceType<HeapConstructor> | null = null;\n\n constructor(options?: {\n distanceMeasurement?: (a: Position, b: Position) => number; // Optional distance function override\n heap?: HeapConstructor; // Optional heap implementation override\n }) {\n this.distanceMeasurement = options?.distanceMeasurement ?? haversineDistance; // Default to haversine\n this.heapConstructor = options?.heap ?? FourAryHeap; // Default to MinHeap\n }\n\n /**\n * Builds a graph (CSR) from a LineString FeatureCollection.\n * Two-pass build: pass 1 assigns node indices and counts degrees; pass 2 fills CSR arrays.\n */\n public buildRouteGraph(network: FeatureCollection<LineString>): void {\n this.network = network; // Keep a reference to the network\n\n // Reset internal structures for a fresh build\n this.coordinateIndexMap = new Map(); // Clear coordinate index map\n this.coordinates = []; // Clear coordinates array\n this.adjacencyList = []; // Will not be populated during build; reserved for dynamic nodes post-build\n // Reset CSR structures (will rebuild below)\n this.csrOffsets = null;\n this.csrIndices = null;\n this.csrDistances = null;\n this.csrNodeCount = 0;\n this.landmarkNodeCount = 0;\n this.landmarkCount = 0;\n this.landmarkDistancesFlat = null;\n this.landmarksDirty = true;\n\n // Hoist to locals for speed (avoid repeated property lookups in hot loops)\n const coordIndexMapLocal = this.coordinateIndexMap; // Local alias for coord map\n const coordsLocal = this.coordinates; // Local alias for coordinates array\n const measureDistance = this.distanceMeasurement; // Local alias for distance function\n\n // Assign indices, count degrees and capture edges in one pass\n const degree: number[] = []; // Dynamic degree array; grows as nodes are discovered\n const edgeFrom: number[] = [];\n const edgeTo: number[] = [];\n const edgeDistance: number[] = [];\n\n const features = network.features;\n for (let f = 0, featureLength = features.length; f < featureLength; f++) {\n const lineCoords = features[f].geometry.coordinates;\n for (let i = 0, segmentLength = lineCoords.length - 1; i < segmentLength; i++) {\n const from = lineCoords[i] as Position;\n const to = lineCoords[i + 1] as Position;\n\n const indexA = this.indexCoordinate(from, coordIndexMapLocal, coordsLocal);\n const indexB = this.indexCoordinate(to, coordIndexMapLocal, coordsLocal);\n\n degree[indexA] = (degree[indexA] ?? 0) + 1;\n degree[indexB] = (degree[indexB] ?? 0) + 1;\n\n edgeFrom.push(indexA);\n edgeTo.push(indexB);\n edgeDistance.push(measureDistance(from, to));\n }\n }\n\n // Build CSR arrays from degree counts\n const nodeCount = this.coordinates.length; // Total nodes discovered\n this.csrNodeCount = nodeCount; // CSR covers all built nodes\n const offsets = new Int32Array(nodeCount + 1); // Row pointer array\n for (let i = 0; i < nodeCount; i++) {\n const deg = degree[i] ?? 0; // Degree of node i\n offsets[i + 1] = offsets[i] + deg; // Prefix sum\n }\n const totalEdges = offsets[nodeCount]; // Total adjacency entries\n const indices = new Int32Array(totalEdges); // Neighbor indices array\n const distances = new Float64Array(totalEdges); // Distances array aligned to indices\n\n // Fill CSR arrays using a write cursor per node\n const cursor = offsets.slice(); // Current write positions per node\n for (let i = 0, edgeLength = edgeFrom.length; i < edgeLength; i++) {\n const indexA = edgeFrom[i];\n const indexB = edgeTo[i];\n const segmentDistance = edgeDistance[i];\n\n let pos = cursor[indexA]++;\n indices[pos] = indexB;\n distances[pos] = segmentDistance;\n pos = cursor[indexB]++;\n indices[pos] = indexA;\n distances[pos] = segmentDistance;\n }\n\n // Commit CSR to instance\n this.csrOffsets = offsets;\n this.csrIndices = indices;\n this.csrDistances = distances;\n\n // Prepare sparse shell only for dynamically added nodes later (no prefilled neighbor arrays)\n this.adjacencyList = new Array(nodeCount);\n }\n\n /**\n * Expands (merges) the existing graph with an additional LineString FeatureCollection.\n */\n public expandRouteGraph(network: FeatureCollection<LineString>): void {\n if (this.network === null) {\n throw new Error(\"Network not built. Please call buildRouteGraph(network) first.\");\n }\n\n // Merge the feature arrays for reference/debugging. We avoid copying properties deeply.\n this.network = {\n type: \"FeatureCollection\",\n features: [...this.network.features, ...network.features],\n };\n\n const coordIndexMapLocal = this.coordinateIndexMap;\n const coordsLocal = this.coordinates;\n const measureDistance = this.distanceMeasurement;\n const adj = this.adjacencyList;\n\n // Ensure we have adjacency arrays for any existing CSR-only nodes.\n // (During buildRouteGraph, adjacencyList is sized but entries are undefined.)\n for (let i = 0; i < adj.length; i++) {\n if (adj[i] === undefined) adj[i] = [];\n }\n\n // Add new edges into the adjacency list (sparse), then rebuild CSR from adjacency.\n this.forEachSegment(network, (a, b) => {\n const indexA = this.indexCoordinate(a, coordIndexMapLocal, coordsLocal, (idx) => { adj[idx] = []; });\n const indexB = this.indexCoordinate(b, coordIndexMapLocal, coordsLocal, (idx) => { adj[idx] = []; });\n\n const segmentDistance = measureDistance(a, b);\n\n adj[indexA].push({ node: indexB, distance: segmentDistance });\n adj[indexB].push({ node: indexA, distance: segmentDistance });\n });\n\n this.rebuildCsrFromAdjacency();\n }\n\n /**\n * Rebuild CSR arrays for the full node set, using:\n * - Existing CSR edges (from the last build/expand)\n * - Any additional edges stored in `adjacencyList`\n */\n private rebuildCsrFromAdjacency(): void {\n const nodeCount = this.coordinates.length;\n const adj = this.adjacencyList;\n\n // Compute degree using CSR degree + adjacency degree\n const degree = new Int32Array(nodeCount);\n\n if (this.csrOffsets && this.csrIndices && this.csrDistances) {\n const csrOffsets = this.csrOffsets;\n const covered = Math.min(this.csrNodeCount, nodeCount);\n for (let i = 0; i < covered; i++) {\n degree[i] += (csrOffsets[i + 1] - csrOffsets[i]);\n }\n }\n\n for (let i = 0; i < nodeCount; i++) {\n const neighbors = adj[i];\n if (neighbors && neighbors.length) degree[i] += neighbors.length;\n }\n\n const offsets = new Int32Array(nodeCount + 1);\n for (let i = 0; i < nodeCount; i++) {\n offsets[i + 1] = offsets[i] + degree[i];\n }\n const totalEdges = offsets[nodeCount];\n const indices = new Int32Array(totalEdges);\n const distances = new Float64Array(totalEdges);\n const cursor = offsets.slice();\n\n // Copy existing CSR edges first\n if (this.csrOffsets && this.csrIndices && this.csrDistances) {\n const csrOffsets = this.csrOffsets;\n const csrIndices = this.csrIndices;\n const csrDistances = this.csrDistances;\n const covered = Math.min(this.csrNodeCount, nodeCount);\n for (let n = 0; n < covered; n++) {\n const startOff = csrOffsets[n];\n const endOff = csrOffsets[n + 1];\n let pos = cursor[n];\n for (let i = startOff; i < endOff; i++) {\n indices[pos] = csrIndices[i];\n distances[pos] = csrDistances[i];\n pos++;\n }\n cursor[n] = pos;\n }\n }\n\n // Append adjacency edges\n for (let n = 0; n < nodeCount; n++) {\n const neighbors = adj[n];\n if (!neighbors || neighbors.length === 0) continue;\n let pos = cursor[n];\n for (let i = 0, len = neighbors.length; i < len; i++) {\n const nb = neighbors[i];\n indices[pos] = nb.node;\n distances[pos] = nb.distance;\n pos++;\n }\n cursor[n] = pos;\n }\n\n // Commit and reset adjacency (we've absorbed edges into CSR)\n this.csrOffsets = offsets;\n this.csrIndices = indices;\n this.csrDistances = distances;\n this.csrNodeCount = nodeCount;\n this.landmarksDirty = true;\n this.landmarkNodeCount = 0;\n this.landmarkCount = 0;\n this.landmarkDistancesFlat = null;\n\n // Keep adjacency list for *future* dynamic additions, but clear existing edges to avoid duplication.\n this.adjacencyList = new Array(nodeCount);\n }\n\n /**\n * Computes the shortest route between two points in the network using the A* algorithm.\n * \n * @param start - A GeoJSON Point Feature representing the start location.\n * @param end - A GeoJSON Point Feature representing the end location.\n * @returns A GeoJSON LineString Feature representing the shortest path, or null if no path is found.\n * \n * @throws Error if the network has not been built yet with buildRouteGraph(network).\n */\n public getRoute(\n start: Feature<Point>, // Start point feature\n end: Feature<Point> // End point feature\n ): Feature<LineString> | null {\n if (this.network === null) { // Guard: graph must be built first\n throw new Error(\"Network not built. Please call buildRouteGraph(network) first.\");\n }\n\n // Ensure start/end exist in index maps\n const startIndex = this.getOrCreateIndex(start.geometry.coordinates); // Get or insert start node index\n const endIndex = this.getOrCreateIndex(end.geometry.coordinates); // Get or insert end node index\n\n // Trivial case: same node\n if (startIndex === endIndex) {\n return null; // No path needed\n }\n\n // Local aliases\n const coordinates = this.coordinates; // Alias to coordinates array\n const adjacency = this.adjacencyList; // Alias to sparse adjacency list (for dynamic nodes)\n const csrOffsets = this.csrOffsets;\n const csrIndices = this.csrIndices;\n const csrDistances = this.csrDistances;\n const csrNodeCount = this.csrNodeCount;\n const hasCsr = !!csrOffsets; // indices/distances should exist whenever offsets exist\n const PositiveInfinity = Number.POSITIVE_INFINITY;\n const measureDistance = this.distanceMeasurement;\n const endCoordinates = end.geometry.coordinates;\n\n this.ensureLandmarkHeuristicData();\n const landmarkDistancesFlat = this.landmarkDistancesFlat;\n const landmarkNodeCount = this.landmarkNodeCount;\n const landmarkCount = this.landmarkCount;\n\n // Ensure and init scratch buffers\n const nodeCount = coordinates.length; // Current number of nodes (may be >= csrNodeCount if new nodes added)\n this.ensureScratch(nodeCount); // Allocate scratch arrays if needed\n\n // Non-null after ensure\n const gF = this.gScoreScratch!; // gScore from start\n const prevF = this.cameFromScratch!; // predecessor for reconstruction\n const visF = this.visitedScratch!;\n const heuristic = this.heuristicScratch!;\n const heuristicStamp = this.heuristicStampScratch!;\n\n gF.fill(PositiveInfinity, 0, nodeCount);\n prevF.fill(-1, 0, nodeCount);\n visF.fill(0, 0, nodeCount);\n\n // Increment query stamp for heuristic cache validity; handle wraparound.\n let queryStamp = (this.heuristicQueryStamp + 1) >>> 0;\n if (queryStamp === 0) {\n heuristicStamp.fill(0, 0, nodeCount);\n queryStamp = 1;\n }\n this.heuristicQueryStamp = queryStamp;\n\n const getHeuristic = (node: number): number => {\n if (heuristicStamp[node] !== queryStamp) {\n heuristicStamp[node] = queryStamp;\n\n if (landmarkDistancesFlat && landmarkCount > 0 && node < landmarkNodeCount && endIndex < landmarkNodeCount) {\n let lowerBound = 0;\n for (let l = 0, offset = 0; l < landmarkCount; l++, offset += landmarkNodeCount) {\n const distanceToNode = landmarkDistancesFlat[offset + node];\n const distanceToEnd = landmarkDistancesFlat[offset + endIndex];\n\n if (!Number.isFinite(distanceToNode) || !Number.isFinite(distanceToEnd)) {\n continue;\n }\n\n const landmarkLowerBound = distanceToEnd >= distanceToNode\n ? distanceToEnd - distanceToNode\n : distanceToNode - distanceToEnd;\n\n if (landmarkLowerBound > lowerBound) {\n lowerBound = landmarkLowerBound;\n }\n }\n heuristic[node] = lowerBound;\n } else {\n heuristic[node] = measureDistance(coordinates[node], endCoordinates);\n }\n }\n return heuristic[node];\n };\n\n // Prefer reusing heaps if supported.\n const openFReuse = this.openForward ?? (this.openForward = new this.heapConstructor());\n const openFAny = openFReuse as unknown as { clear?: () => void };\n const canReuse = !!openFAny.clear;\n\n const openF2 = canReuse ? openFReuse : new this.heapConstructor();\n\n if (canReuse) {\n openFAny.clear!();\n }\n\n gF[startIndex] = 0;\n openF2.insert(getHeuristic(startIndex), startIndex);\n\n while (openF2.size() > 0) {\n const current = openF2.extractMin();\n if (current === null) {\n break;\n }\n if (visF[current] !== 0) {\n continue;\n }\n if (current === endIndex) {\n break;\n }\n\n visF[current] = 1;\n const currentDistance = gF[current];\n\n const isCsrNode = hasCsr && current < csrNodeCount;\n if (!isCsrNode) {\n const neighbors = adjacency[current];\n if (!neighbors || neighbors.length === 0) {\n continue;\n }\n\n for (let i = 0, n = neighbors.length; i < n; i++) {\n const nb = neighbors[i];\n const nbNode = nb.node;\n const tentativeG = currentDistance + nb.distance;\n if (tentativeG >= gF[nbNode]) {\n continue;\n }\n\n gF[nbNode] = tentativeG;\n prevF[nbNode] = current;\n openF2.insert(tentativeG + getHeuristic(nbNode), nbNode);\n }\n continue;\n }\n\n for (let i = csrOffsets![current], endOff = csrOffsets![current + 1]; i < endOff; i++) {\n const nbNode = csrIndices![i];\n const tentativeG = currentDistance + csrDistances![i];\n if (tentativeG >= gF[nbNode]) {\n continue;\n }\n\n gF[nbNode] = tentativeG;\n prevF[nbNode] = current;\n openF2.insert(tentativeG + getHeuristic(nbNode), nbNode);\n }\n }\n\n if (gF[endIndex] === PositiveInfinity) {\n return null;\n }\n\n // Reconstruct path from end back to start through predecessor links.\n const path: Position[] = [];\n\n let cur = endIndex;\n while (cur !== startIndex && cur >= 0) {\n path.push(coordinates[cur]);\n cur = prevF[cur];\n }\n path.push(coordinates[startIndex]);\n path.reverse();\n\n return {\n type: \"Feature\",\n geometry: { type: \"LineString\", coordinates: path },\n properties: {},\n };\n }\n\n // Build ALT heuristic tables by running shortest-path trees from selected landmarks.\n private buildLandmarkHeuristicData(): void {\n const offsets = this.csrOffsets;\n const indices = this.csrIndices;\n const distances = this.csrDistances;\n const nodeCount = this.csrNodeCount;\n\n if (!offsets || !indices || !distances || nodeCount === 0) {\n this.landmarkNodeCount = 0;\n this.landmarkCount = 0;\n this.landmarkDistancesFlat = null;\n return;\n }\n\n const targetLandmarkCount = Math.min(this.maxLandmarks, nodeCount);\n const selected = new Uint8Array(nodeCount);\n const allDistances: Float64Array[] = [];\n\n let source = 0;\n for (let landmarkIndex = 0; landmarkIndex < targetLandmarkCount; landmarkIndex++) {\n selected[source] = 1;\n\n const computedDistances = this.computeShortestDistancesFrom(source, nodeCount, offsets, indices, distances);\n allDistances.push(computedDistances);\n\n let farthestDistance = -1;\n let farthestIndex = -1;\n for (let node = 0; node < nodeCount; node++) {\n if (selected[node] !== 0) {\n continue;\n }\n\n const distanceAtNode = computedDistances[node];\n if (!Number.isFinite(distanceAtNode)) {\n continue;\n }\n\n if (distanceAtNode > farthestDistance) {\n farthestDistance = distanceAtNode;\n farthestIndex = node;\n }\n }\n\n if (farthestIndex < 0) {\n break;\n }\n source = farthestIndex;\n }\n\n const landmarkCount = allDistances.length;\n\n const flat = new Float64Array(landmarkCount * nodeCount);\n for (let i = 0; i < landmarkCount; i++) {\n flat.set(allDistances[i], i * nodeCount);\n }\n\n this.landmarkNodeCount = nodeCount;\n this.landmarkCount = landmarkCount;\n this.landmarkDistancesFlat = flat;\n this.landmarksDirty = false;\n }\n\n // Build landmark tables only when needed by getRoute.\n private ensureLandmarkHeuristicData(): void {\n if (!this.landmarksDirty) {\n return;\n }\n this.buildLandmarkHeuristicData();\n }\n\n // Dijkstra over CSR to compute all-pairs distances from one source node.\n private computeShortestDistancesFrom(\n source: number,\n nodeCount: number,\n offsets: Int32Array,\n indices: Int32Array,\n distances: Float64Array,\n ): Float64Array {\n const PositiveInfinity = Number.POSITIVE_INFINITY;\n const bestDistance = new Float64Array(nodeCount);\n const visited = new Uint8Array(nodeCount);\n bestDistance.fill(PositiveInfinity);\n bestDistance[source] = 0;\n\n const openSet = new this.heapConstructor();\n openSet.insert(0, source);\n\n while (openSet.size() > 0) {\n const current = openSet.extractMin();\n if (current === null) {\n break;\n }\n if (visited[current] !== 0) {\n continue;\n }\n\n visited[current] = 1;\n const currentDistance = bestDistance[current];\n\n for (let i = offsets[current], endOffset = offsets[current + 1]; i < endOffset; i++) {\n const neighbor = indices[i];\n const tentativeDistance = currentDistance + distances[i];\n if (tentativeDistance >= bestDistance[neighbor]) {\n continue;\n }\n\n bestDistance[neighbor] = tentativeDistance;\n openSet.insert(tentativeDistance, neighbor);\n }\n }\n\n return bestDistance;\n }\n\n /**\n * Helper to index start/end in getRoute.\n */\n private getOrCreateIndex(coord: Position): number { // Ensure a coordinate has a node index, creating if absent\n const lng = coord[0]; // Extract longitude\n const lat = coord[1]; // Extract latitude\n\n let latMap = this.coordinateIndexMap.get(lng); // Get lat→index map for this longitude\n if (latMap === undefined) { // Create if missing\n latMap = new Map<number, number>();\n this.coordinateIndexMap.set(lng, latMap);\n }\n\n let index = latMap.get(lat); // Lookup index by latitude\n if (index === undefined) { // If not found, append new node\n\n index = this.coordinates.length; // New index at end\n this.coordinates.push(coord); // Store coordinate\n latMap.set(lat, index); // Record mapping\n\n // Ensure sparse adjacency slot for dynamically added nodes\n this.adjacencyList[index] = []; // Init empty neighbor array\n\n // Extend CSR offsets to keep indices consistent (no neighbors for new node)\n if (this.csrOffsets) { // Only adjust if CSR already built\n\n // Only need to expand offsets by one; indices/distances remain unchanged\n const oldCount = this.csrNodeCount; // Nodes currently covered by CSR\n\n // Appending exactly one new node at the end\n if (index === oldCount) {\n const newOffsets = new Int32Array(oldCount + 2); // Allocate offsets for +1 node\n newOffsets.set(this.csrOffsets, 0); // Copy previous offsets\n\n // Last offset repeats to indicate zero neighbors\n newOffsets[oldCount + 1] = newOffsets[oldCount]; // Replicate last pointer\n this.csrOffsets = newOffsets; // Swap in new offsets\n this.csrNodeCount = oldCount + 1; // Increment CSR node count\n }\n }\n }\n\n return index;\n }\n\n // Ensure scratch arrays are allocated with at least `size` capacity.\n private ensureScratch(size: number): void {\n const ifAlreadyBigEnough = this.scratchCapacity >= size\n && this.gScoreScratch\n && this.cameFromScratch\n && this.visitedScratch\n && this.heuristicScratch\n && this.heuristicStampScratch;\n\n if (ifAlreadyBigEnough) {\n return; // Nothing to do\n }\n const capacity = size | 0; // Ensure integer\n this.gScoreScratch = new Float64Array(capacity);\n this.cameFromScratch = new Int32Array(capacity);\n this.visitedScratch = new Uint8Array(capacity);\n this.heuristicScratch = new Float64Array(capacity);\n this.heuristicStampScratch = new Uint32Array(capacity);\n this.scratchCapacity = capacity;\n }\n\n\n // Iterate all consecutive segment pairs in a LineString FeatureCollection.\n // Kept as a simple loop helper to avoid repeating nested iteration logic.\n private forEachSegment(\n network: FeatureCollection<LineString>,\n fn: (a: Position, b: Position) => void,\n ): void {\n const features = network.features;\n for (let f = 0, fLen = features.length; f < fLen; f++) {\n const lineCoords = features[f].geometry.coordinates;\n for (let i = 0, len = lineCoords.length - 1; i < len; i++) {\n // GeoJSON coordinates are compatible with Position (number[]), and the project assumes [lng,lat]\n fn(lineCoords[i] as Position, lineCoords[i + 1] as Position);\n }\n }\n }\n\n // Hot-path coordinate indexer used by both build/expand.\n // Accepts explicit maps/arrays so callers can hoist them once.\n private indexCoordinate(\n coord: Position,\n coordIndexMapLocal: Map<number, Map<number, number>>,\n coordsLocal: Position[],\n onNewIndex?: (index: number) => void,\n ): number {\n const lng = coord[0];\n const lat = coord[1];\n\n let latMap = coordIndexMapLocal.get(lng);\n if (latMap === undefined) {\n latMap = new Map<number, number>();\n coordIndexMapLocal.set(lng, latMap);\n }\n\n let idx = latMap.get(lat);\n if (idx === undefined) {\n idx = coordsLocal.length;\n coordsLocal.push(coord);\n latMap.set(lat, idx);\n if (onNewIndex) onNewIndex(idx);\n }\n\n return idx;\n }\n\n}\n\nexport { TerraRoute, createCheapRuler, haversineDistance } ","import { Position } from \"geojson\";\n\n// This code is based on Mapbox's cheap-ruler library:\n\n// ISC License\n\n// Copyright (c) 2024, Mapbox\n\n// Permission to use, copy, modify, and/or distribute this software for any purpose\n// with or without fee is hereby granted, provided that the above copyright notice\n// and this permission notice appear in all copies.\n\n// THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH\n// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND\n// FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,\n// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS\n// OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER\n// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF\n// THIS SOFTWARE.\n\n/**\n * Creates a function for fast geodesic distance approximation using local scaling constants\n * based on a reference latitude. Useful for city-scale distances.\n *\n * @param {number} lat - Reference latitude in degrees\n * @returns {(a: Position, b: Position) => number} - Function that computes distance between two points\n * \n * @example\n * const distance = createCheapRuler(50.5);\n * const d = distance([30.5, 50.5], [30.51, 50.49]);\n * \n */\nexport function createCheapRuler(lat: number): (a: Position, b: Position) => number {\n const RE = 6378.137; // Earth's equatorial radius in kilometers\n const FE = 1 / 298.257223563; // Earth's flattening\n const E2 = FE * (2 - FE);\n const RAD = Math.PI / 180;\n\n const cosLat = Math.cos(lat * RAD);\n const w2 = 1 / (1 - E2 * (1 - cosLat * cosLat));\n const w = Math.sqrt(w2);\n\n const m = RAD * RE;\n const kx = m * w * cosLat; // scale for longitude\n const ky = m * w * w2 * (1 - E2); // scale for latitude\n\n return function distance(a: Position, b: Position): number {\n let deltaLng = a[0] - b[0];\n\n while (deltaLng < -180) deltaLng += 360;\n while (deltaLng > 180) deltaLng -= 360;\n\n const dx = deltaLng * kx;\n const dy = (a[1] - b[1]) * ky;\n\n return Math.sqrt(dx * dx + dy * dy);\n };\n}"],"names":["DEG_TO_RAD","Math","PI","haversineDistance","pointOne","pointTwo","phiOne","phiTwo","deltalambda","sinDeltaPhi","sin","sinDeltaLambda","a","cos","atan2","sqrt","FourAryHeap","keys","this","values","idxs","length","insertCounter","_proto","prototype","insert","key","value","i","ck","cv","ci","p","pk","pi","extractMin","n","minValue","last","bubbleDown","peekMinKey","Number","POSITIVE_INFINITY","clear","size","k","v","idx","nodeK","nodeV","nodeI","c1","smallest","sK","sI","sV","c2","k2","i2","c3","k3","i3","c4","k4","i4","TerraRoute","options","_options$distanceMeas","_options$heap","network","distanceMeasurement","heapConstructor","coordinateIndexMap","Map","coordinates","adjacencyList","csrOffsets","csrIndices","csrDistances","csrNodeCount","landmarkNodeCount","landmarkCount","landmarkDistancesFlat","maxLandmarks","landmarksDirty","gScoreScratch","cameFromScratch","visitedScratch","heuristicScratch","heuristicStampScratch","heuristicQueryStamp","scratchCapacity","openForward","heap","buildRouteGraph","coordIndexMapLocal","coordsLocal","measureDistance","degree","edgeFrom","edgeTo","edgeDistance","features","f","featureLength","lineCoords","geometry","segmentLength","_degree$indexA","_degree$indexB","from","to","indexA","indexCoordinate","indexB","push","nodeCount","offsets","Int32Array","_degree$_i","deg","totalEdges","indices","distances","Float64Array","cursor","slice","edgeLength","segmentDistance","pos","Array","expandRouteGraph","_this","Error","type","concat","adj","undefined","forEachSegment","b","node","distance","rebuildCsrFromAdjacency","covered","min","neighbors","endOff","len","nb","getRoute","start","end","startIndex","getOrCreateIndex","endIndex","adjacency","hasCsr","PositiveInfinity","endCoordinates","ensureLandmarkHeuristicData","ensureScratch","gF","prevF","visF","heuristic","heuristicStamp","fill","queryStamp","getHeuristic","lowerBound","l","offset","distanceToNode","distanceToEnd","isFinite","landmarkLowerBound","openFReuse","_this$openForward","openFAny","canReuse","openF2","current","currentDistance","nbNode","tentativeG","path","cur","reverse","properties","buildLandmarkHeuristicData","targetLandmarkCount","selected","Uint8Array","allDistances","source","landmarkIndex","computedDistances","computeShortestDistancesFrom","farthestDistance","farthestIndex","distanceAtNode","flat","set","bestDistance","visited","openSet","endOffset","neighbor","tentativeDistance","coord","lng","lat","latMap","get","index","oldCount","newOffsets","capacity","Uint32Array","fn","fLen","onNewIndex","FE","E2","RAD","cosLat","w2","w","m","kx","ky","deltaLng","dx","dy"],"mappings":"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"}
@@ -18,15 +18,19 @@ declare class TerraRoute implements Router {
18
18
  private csrIndices;
19
19
  private csrDistances;
20
20
  private csrNodeCount;
21
+ private landmarkNodeCount;
22
+ private landmarkCount;
23
+ private landmarkDistancesFlat;
24
+ private readonly maxLandmarks;
25
+ private landmarksDirty;
21
26
  private gScoreScratch;
22
27
  private cameFromScratch;
23
28
  private visitedScratch;
24
- private hScratch;
25
- private gScoreRevScratch;
26
- private cameFromRevScratch;
27
- private visitedRevScratch;
28
- private hRevScratch;
29
+ private heuristicScratch;
30
+ private heuristicStampScratch;
31
+ private heuristicQueryStamp;
29
32
  private scratchCapacity;
33
+ private openForward;
30
34
  constructor(options?: {
31
35
  distanceMeasurement?: (a: Position, b: Position) => number;
32
36
  heap?: HeapConstructor;
@@ -57,6 +61,9 @@ declare class TerraRoute implements Router {
57
61
  */
58
62
  getRoute(start: Feature<Point>, // Start point feature
59
63
  end: Feature<Point>): Feature<LineString> | null;
64
+ private buildLandmarkHeuristicData;
65
+ private ensureLandmarkHeuristicData;
66
+ private computeShortestDistancesFrom;
60
67
  /**
61
68
  * Helper to index start/end in getRoute.
62
69
  */
@@ -1,2 +1,2 @@
1
- const t=(t,s)=>{const e=t=>t*Math.PI/180,i=e(t[1]),n=e(t[0]),r=e(s[1]),c=r-i,h=e(s[0])-n,o=Math.sin(c/2)*Math.sin(c/2)+Math.cos(i)*Math.cos(r)*Math.sin(h/2)*Math.sin(h/2);return 2*Math.atan2(Math.sqrt(o),Math.sqrt(1-o))*6371e3/1e3};function s(t){const s=1/298.257223563,e=s*(2-s),i=Math.PI/180,n=Math.cos(t*i),r=1/(1-e*(1-n*n)),c=Math.sqrt(r),h=6378.137*i,o=h*c*n,a=h*c*r*(1-e);return function(t,s){let e=t[0]-s[0];for(;e<-180;)e+=360;for(;e>180;)e-=360;const i=e*o,n=(t[1]-s[1])*a;return Math.sqrt(i*i+n*n)}}class e{constructor(){this.keys=[],this.values=[],this.idxs=[],this.length=0,this.insertCounter=0}insert(t,s){let e=this.length;this.length=e+1;let i=t,n=s,r=this.insertCounter++;for(;e>0;){const t=e-1>>>2,s=this.keys[t],n=this.idxs[t];if(i>s||i===s&&r>n)break;this.keys[e]=s,this.values[e]=this.values[t],this.idxs[e]=n,e=t}this.keys[e]=i,this.values[e]=n,this.idxs[e]=r}extractMin(){const t=this.length;if(0===t)return null;const s=this.values[0],e=t-1;return this.length=e,e>0&&(this.keys[0]=this.keys[e],this.values[0]=this.values[e],this.idxs[0]=this.idxs[e],this.bubbleDown(0)),s}size(){return this.length}bubbleDown(t){const s=this.length,e=this.keys,i=this.values,n=this.idxs,r=e[t],c=i[t],h=n[t];for(;;){const c=1+(t<<2);if(c>=s)break;let o=c,a=e[c],l=n[c],d=i[c];const u=c+1;if(u<s){const t=e[u],s=n[u];(t<a||t===a&&s<l)&&(o=u,a=t,l=s,d=i[u])}const f=c+2;if(f<s){const t=e[f],s=n[f];(t<a||t===a&&s<l)&&(o=f,a=t,l=s,d=i[f])}const I=c+3;if(I<s){const t=e[I],s=n[I];(t<a||t===a&&s<l)&&(o=I,a=t,l=s,d=i[I])}if(!(a<r||a===r&&l<h))break;e[t]=a,i[t]=d,n[t]=l,t=o}e[t]=r,i[t]=c,n[t]=h}}class i{constructor(s){var i,n;this.network=null,this.distanceMeasurement=void 0,this.heapConstructor=void 0,this.coordinateIndexMap=new Map,this.coordinates=[],this.adjacencyList=[],this.csrOffsets=null,this.csrIndices=null,this.csrDistances=null,this.csrNodeCount=0,this.gScoreScratch=null,this.cameFromScratch=null,this.visitedScratch=null,this.hScratch=null,this.gScoreRevScratch=null,this.cameFromRevScratch=null,this.visitedRevScratch=null,this.hRevScratch=null,this.scratchCapacity=0,this.distanceMeasurement=null!=(i=null==s?void 0:s.distanceMeasurement)?i:t,this.heapConstructor=null!=(n=null==s?void 0:s.heap)?n:e}buildRouteGraph(t){this.network=t,this.coordinateIndexMap=new Map,this.coordinates=[],this.adjacencyList=[],this.csrOffsets=null,this.csrIndices=null,this.csrDistances=null,this.csrNodeCount=0;const s=this.coordinateIndexMap,e=this.coordinates,i=this.distanceMeasurement,n=[];this.forEachSegment(t,(t,i)=>{var r,c;const h=this.indexCoordinate(t,s,e),o=this.indexCoordinate(i,s,e);n[h]=(null!=(r=n[h])?r:0)+1,n[o]=(null!=(c=n[o])?c:0)+1});const r=this.coordinates.length;this.csrNodeCount=r;const c=new Int32Array(r+1);for(let t=0;t<r;t++){var h;const s=null!=(h=n[t])?h:0;c[t+1]=c[t]+s}const o=c[r],a=new Int32Array(o),l=new Float64Array(o),d=c.slice();this.forEachSegment(t,(t,s)=>{const e=this.coordinateIndexMap.get(t[0]).get(t[1]),n=this.coordinateIndexMap.get(s[0]).get(s[1]),r=i(t,s);let c=d[e]++;a[c]=n,l[c]=r,c=d[n]++,a[c]=e,l[c]=r}),this.csrOffsets=c,this.csrIndices=a,this.csrDistances=l,this.adjacencyList=new Array(r)}expandRouteGraph(t){if(null===this.network)throw new Error("Network not built. Please call buildRouteGraph(network) first.");this.network={type:"FeatureCollection",features:[...this.network.features,...t.features]};const s=this.coordinateIndexMap,e=this.coordinates,i=this.distanceMeasurement,n=this.adjacencyList;for(let t=0;t<n.length;t++)void 0===n[t]&&(n[t]=[]);this.forEachSegment(t,(t,r)=>{const c=this.indexCoordinate(t,s,e,t=>{n[t]=[]}),h=this.indexCoordinate(r,s,e,t=>{n[t]=[]}),o=i(t,r);n[c].push({node:h,distance:o}),n[h].push({node:c,distance:o})}),this.rebuildCsrFromAdjacency()}rebuildCsrFromAdjacency(){const t=this.coordinates.length,s=this.adjacencyList,e=new Int32Array(t);if(this.csrOffsets&&this.csrIndices&&this.csrDistances){const s=this.csrOffsets,i=Math.min(this.csrNodeCount,t);for(let t=0;t<i;t++)e[t]+=s[t+1]-s[t]}for(let i=0;i<t;i++){const t=s[i];t&&t.length&&(e[i]+=t.length)}const i=new Int32Array(t+1);for(let s=0;s<t;s++)i[s+1]=i[s]+e[s];const n=i[t],r=new Int32Array(n),c=new Float64Array(n),h=i.slice();if(this.csrOffsets&&this.csrIndices&&this.csrDistances){const s=this.csrOffsets,e=this.csrIndices,i=this.csrDistances,n=Math.min(this.csrNodeCount,t);for(let t=0;t<n;t++){const n=s[t+1];let o=h[t];for(let h=s[t];h<n;h++)r[o]=e[h],c[o]=i[h],o++;h[t]=o}}for(let e=0;e<t;e++){const t=s[e];if(!t||0===t.length)continue;let i=h[e];for(let s=0,e=t.length;s<e;s++){const e=t[s];r[i]=e.node,c[i]=e.distance,i++}h[e]=i}this.csrOffsets=i,this.csrIndices=r,this.csrDistances=c,this.csrNodeCount=t,this.adjacencyList=new Array(t)}getRoute(t,s){if(null===this.network)throw new Error("Network not built. Please call buildRouteGraph(network) first.");const e=this.getOrCreateIndex(t.geometry.coordinates),i=this.getOrCreateIndex(s.geometry.coordinates);if(e===i)return null;const n=this.coordinates,r=this.adjacencyList,c=n.length;this.ensureScratch(c);const h=this.gScoreScratch,o=this.gScoreRevScratch,a=this.cameFromScratch,l=this.cameFromRevScratch,d=this.visitedScratch,u=this.visitedRevScratch,f=this.hScratch,I=this.hRevScratch;h.fill(Number.POSITIVE_INFINITY,0,c),o.fill(Number.POSITIVE_INFINITY,0,c),a.fill(-1,0,c),l.fill(-1,0,c),d.fill(0,0,c),u.fill(0,0,c),f.fill(-1,0,c),I.fill(-1,0,c);const S=new this.heapConstructor,g=new this.heapConstructor;h[e]=0,o[i]=0,S.insert(0,e),g.insert(0,i);let v=Number.POSITIVE_INFINITY,y=-1,m=0,w=0;for(;S.size()>0&&g.size()>0&&!(y>=0&&m+w>=v);)if(S.size()<=g.size()){const t=S.extractMin();if(0!==d[t])continue;if(m=h[t],d[t]=1,0!==u[t]){const s=h[t]+o[t];s<v&&(v=s,y=t)}if(this.csrOffsets&&t<this.csrNodeCount){const s=this.csrOffsets,e=this.csrIndices,i=this.csrDistances;for(let n=s[t],r=s[t+1];n<r;n++){const s=e[n],r=h[t]+i[n];if(r<h[s]){h[s]=r,a[s]=t;const e=o[s];if(e!==Number.POSITIVE_INFINITY){const t=r+e;t<v&&(v=t,y=s)}S.insert(r,s)}}}else{const s=r[t];if(s&&s.length)for(let e=0,i=s.length;e<i;e++){const i=s[e],n=i.node,r=h[t]+i.distance;if(r<h[n]){h[n]=r,a[n]=t;const s=o[n];if(s!==Number.POSITIVE_INFINITY){const t=r+s;t<v&&(v=t,y=n)}S.insert(r,n)}}}}else{const t=g.extractMin();if(0!==u[t])continue;if(w=o[t],u[t]=1,0!==d[t]){const s=h[t]+o[t];s<v&&(v=s,y=t)}if(this.csrOffsets&&t<this.csrNodeCount){const s=this.csrOffsets,e=this.csrIndices,i=this.csrDistances;for(let n=s[t],r=s[t+1];n<r;n++){const s=e[n],r=o[t]+i[n];if(r<o[s]){o[s]=r,l[s]=t;const e=h[s];if(e!==Number.POSITIVE_INFINITY){const t=r+e;t<v&&(v=t,y=s)}g.insert(r,s)}}}else{const s=r[t];if(s&&s.length)for(let e=0,i=s.length;e<i;e++){const i=s[e],n=i.node,r=o[t]+i.distance;if(r<o[n]){o[n]=r,l[n]=t;const s=h[n];if(s!==Number.POSITIVE_INFINITY){const t=r+s;t<v&&(v=t,y=n)}g.insert(r,n)}}}}if(y<0)return null;const p=[];let M=y;for(;M!==e&&M>=0;)p.push(n[M]),M=a[M];if(M!==e)return null;for(p.push(n[e]),p.reverse(),M=y;M!==i;){if(M=l[M],M<0)return null;p.push(n[M])}return{type:"Feature",geometry:{type:"LineString",coordinates:p},properties:{}}}getOrCreateIndex(t){const s=t[0],e=t[1];let i=this.coordinateIndexMap.get(s);void 0===i&&(i=new Map,this.coordinateIndexMap.set(s,i));let n=i.get(e);if(void 0===n&&(n=this.coordinates.length,this.coordinates.push(t),i.set(e,n),this.adjacencyList[n]=[],this.csrOffsets)){const t=this.csrNodeCount;if(n===t){const s=new Int32Array(t+2);s.set(this.csrOffsets,0),s[t+1]=s[t],this.csrOffsets=s,this.csrNodeCount=t+1}}return n}ensureScratch(t){if(this.scratchCapacity>=t&&this.gScoreScratch&&this.cameFromScratch&&this.visitedScratch&&this.hScratch&&this.gScoreRevScratch&&this.cameFromRevScratch&&this.visitedRevScratch&&this.hRevScratch)return;const s=0|t;this.gScoreScratch=new Float64Array(s),this.cameFromScratch=new Int32Array(s),this.visitedScratch=new Uint8Array(s),this.hScratch=new Float64Array(s),this.gScoreRevScratch=new Float64Array(s),this.cameFromRevScratch=new Int32Array(s),this.visitedRevScratch=new Uint8Array(s),this.hRevScratch=new Float64Array(s),this.scratchCapacity=s}forEachSegment(t,s){const e=t.features;for(let t=0,i=e.length;t<i;t++){const i=e[t].geometry.coordinates;for(let t=0,e=i.length-1;t<e;t++)s(i[t],i[t+1])}}indexCoordinate(t,s,e,i){const n=t[0],r=t[1];let c=s.get(n);void 0===c&&(c=new Map,s.set(n,c));let h=c.get(r);return void 0===h&&(h=e.length,e.push(t),c.set(r,h),i&&i(h)),h}}export{i as TerraRoute,s as createCheapRuler,t as haversineDistance};
1
+ const t=Math.PI/180,s=(s,e)=>{const i=s[1]*t,n=e[1]*t,r=e[0]*t-s[0]*t,a=Math.sin((n-i)/2),c=Math.sin(r/2),o=a*a+Math.cos(i)*Math.cos(n)*c*c;return 2*Math.atan2(Math.sqrt(o),Math.sqrt(1-o))*6371};function e(t){const s=1/298.257223563,e=s*(2-s),i=Math.PI/180,n=Math.cos(t*i),r=1/(1-e*(1-n*n)),a=Math.sqrt(r),c=6378.137*i,o=c*a*n,h=c*a*r*(1-e);return function(t,s){let e=t[0]-s[0];for(;e<-180;)e+=360;for(;e>180;)e-=360;const i=e*o,n=(t[1]-s[1])*h;return Math.sqrt(i*i+n*n)}}class i{constructor(){this.keys=[],this.values=[],this.idxs=[],this.length=0,this.insertCounter=0}insert(t,s){let e=this.length;this.length=e+1;let i=t,n=s,r=this.insertCounter++;for(;e>0;){const t=e-1>>>2,s=this.keys[t],n=this.idxs[t];if(i>s||i===s&&r>n)break;this.keys[e]=s,this.values[e]=this.values[t],this.idxs[e]=n,e=t}this.keys[e]=i,this.values[e]=n,this.idxs[e]=r}extractMin(){const t=this.length;if(0===t)return null;const s=this.values[0],e=t-1;return this.length=e,e>0&&(this.keys[0]=this.keys[e],this.values[0]=this.values[e],this.idxs[0]=this.idxs[e],this.bubbleDown(0)),s}peekMinKey(){return 0===this.length?Number.POSITIVE_INFINITY:this.keys[0]}clear(){this.length=0,this.insertCounter=0}size(){return this.length}bubbleDown(t){const s=this.length,e=this.keys,i=this.values,n=this.idxs,r=e[t],a=i[t],c=n[t];for(;;){const a=1+(t<<2);if(a>=s)break;let o=a,h=e[a],l=n[a],u=i[a];const d=a+1;if(d<s){const t=e[d],s=n[d];(t<h||t===h&&s<l)&&(o=d,h=t,l=s,u=i[d])}const f=a+2;if(f<s){const t=e[f],s=n[f];(t<h||t===h&&s<l)&&(o=f,h=t,l=s,u=i[f])}const m=a+3;if(m<s){const t=e[m],s=n[m];(t<h||t===h&&s<l)&&(o=m,h=t,l=s,u=i[m])}if(!(h<r||h===r&&l<c))break;e[t]=h,i[t]=u,n[t]=l,t=o}e[t]=r,i[t]=a,n[t]=c}}class n{constructor(t){var e,n;this.network=null,this.distanceMeasurement=void 0,this.heapConstructor=void 0,this.coordinateIndexMap=new Map,this.coordinates=[],this.adjacencyList=[],this.csrOffsets=null,this.csrIndices=null,this.csrDistances=null,this.csrNodeCount=0,this.landmarkNodeCount=0,this.landmarkCount=0,this.landmarkDistancesFlat=null,this.maxLandmarks=4,this.landmarksDirty=!0,this.gScoreScratch=null,this.cameFromScratch=null,this.visitedScratch=null,this.heuristicScratch=null,this.heuristicStampScratch=null,this.heuristicQueryStamp=1,this.scratchCapacity=0,this.openForward=null,this.distanceMeasurement=null!=(e=null==t?void 0:t.distanceMeasurement)?e:s,this.heapConstructor=null!=(n=null==t?void 0:t.heap)?n:i}buildRouteGraph(t){this.network=t,this.coordinateIndexMap=new Map,this.coordinates=[],this.adjacencyList=[],this.csrOffsets=null,this.csrIndices=null,this.csrDistances=null,this.csrNodeCount=0,this.landmarkNodeCount=0,this.landmarkCount=0,this.landmarkDistancesFlat=null,this.landmarksDirty=!0;const s=this.coordinateIndexMap,e=this.coordinates,i=this.distanceMeasurement,n=[],r=[],a=[],c=[],o=t.features;for(let t=0,u=o.length;t<u;t++){const u=o[t].geometry.coordinates;for(let t=0,o=u.length-1;t<o;t++){var h,l;const o=u[t],d=u[t+1],f=this.indexCoordinate(o,s,e),m=this.indexCoordinate(d,s,e);n[f]=(null!=(h=n[f])?h:0)+1,n[m]=(null!=(l=n[m])?l:0)+1,r.push(f),a.push(m),c.push(i(o,d))}}const u=this.coordinates.length;this.csrNodeCount=u;const d=new Int32Array(u+1);for(let t=0;t<u;t++){var f;const s=null!=(f=n[t])?f:0;d[t+1]=d[t]+s}const m=d[u],y=new Int32Array(m),k=new Float64Array(m),p=d.slice();for(let t=0,s=r.length;t<s;t++){const s=r[t],e=a[t],i=c[t];let n=p[s]++;y[n]=e,k[n]=i,n=p[e]++,y[n]=s,k[n]=i}this.csrOffsets=d,this.csrIndices=y,this.csrDistances=k,this.adjacencyList=new Array(u)}expandRouteGraph(t){if(null===this.network)throw new Error("Network not built. Please call buildRouteGraph(network) first.");this.network={type:"FeatureCollection",features:[...this.network.features,...t.features]};const s=this.coordinateIndexMap,e=this.coordinates,i=this.distanceMeasurement,n=this.adjacencyList;for(let t=0;t<n.length;t++)void 0===n[t]&&(n[t]=[]);this.forEachSegment(t,(t,r)=>{const a=this.indexCoordinate(t,s,e,t=>{n[t]=[]}),c=this.indexCoordinate(r,s,e,t=>{n[t]=[]}),o=i(t,r);n[a].push({node:c,distance:o}),n[c].push({node:a,distance:o})}),this.rebuildCsrFromAdjacency()}rebuildCsrFromAdjacency(){const t=this.coordinates.length,s=this.adjacencyList,e=new Int32Array(t);if(this.csrOffsets&&this.csrIndices&&this.csrDistances){const s=this.csrOffsets,i=Math.min(this.csrNodeCount,t);for(let t=0;t<i;t++)e[t]+=s[t+1]-s[t]}for(let i=0;i<t;i++){const t=s[i];t&&t.length&&(e[i]+=t.length)}const i=new Int32Array(t+1);for(let s=0;s<t;s++)i[s+1]=i[s]+e[s];const n=i[t],r=new Int32Array(n),a=new Float64Array(n),c=i.slice();if(this.csrOffsets&&this.csrIndices&&this.csrDistances){const s=this.csrOffsets,e=this.csrIndices,i=this.csrDistances,n=Math.min(this.csrNodeCount,t);for(let t=0;t<n;t++){const n=s[t+1];let o=c[t];for(let c=s[t];c<n;c++)r[o]=e[c],a[o]=i[c],o++;c[t]=o}}for(let e=0;e<t;e++){const t=s[e];if(!t||0===t.length)continue;let i=c[e];for(let s=0,e=t.length;s<e;s++){const e=t[s];r[i]=e.node,a[i]=e.distance,i++}c[e]=i}this.csrOffsets=i,this.csrIndices=r,this.csrDistances=a,this.csrNodeCount=t,this.landmarksDirty=!0,this.landmarkNodeCount=0,this.landmarkCount=0,this.landmarkDistancesFlat=null,this.adjacencyList=new Array(t)}getRoute(t,s){var e;if(null===this.network)throw new Error("Network not built. Please call buildRouteGraph(network) first.");const i=this.getOrCreateIndex(t.geometry.coordinates),n=this.getOrCreateIndex(s.geometry.coordinates);if(i===n)return null;const r=this.coordinates,a=this.adjacencyList,c=this.csrOffsets,o=this.csrIndices,h=this.csrDistances,l=this.csrNodeCount,u=!!c,d=Number.POSITIVE_INFINITY,f=this.distanceMeasurement,m=s.geometry.coordinates;this.ensureLandmarkHeuristicData();const y=this.landmarkDistancesFlat,k=this.landmarkNodeCount,p=this.landmarkCount,g=r.length;this.ensureScratch(g);const w=this.gScoreScratch,C=this.cameFromScratch,I=this.visitedScratch,S=this.heuristicScratch,M=this.heuristicStampScratch;w.fill(d,0,g),C.fill(-1,0,g),I.fill(0,0,g);let F=this.heuristicQueryStamp+1>>>0;0===F&&(M.fill(0,0,g),F=1),this.heuristicQueryStamp=F;const N=t=>{if(M[t]!==F)if(M[t]=F,y&&p>0&&t<k&&n<k){let s=0;for(let e=0,i=0;e<p;e++,i+=k){const e=y[i+t],r=y[i+n];if(!Number.isFinite(e)||!Number.isFinite(r))continue;const a=r>=e?r-e:e-r;a>s&&(s=a)}S[t]=s}else S[t]=f(r[t],m);return S[t]},b=null!=(e=this.openForward)?e:this.openForward=new this.heapConstructor,x=b,D=!!x.clear,v=D?b:new this.heapConstructor;for(D&&x.clear(),w[i]=0,v.insert(N(i),i);v.size()>0;){const t=v.extractMin();if(null===t)break;if(0!==I[t])continue;if(t===n)break;I[t]=1;const s=w[t];if(u&&t<l)for(let e=c[t],i=c[t+1];e<i;e++){const i=o[e],n=s+h[e];n>=w[i]||(w[i]=n,C[i]=t,v.insert(n+N(i),i))}else{const e=a[t];if(!e||0===e.length)continue;for(let i=0,n=e.length;i<n;i++){const n=e[i],r=n.node,a=s+n.distance;a>=w[r]||(w[r]=a,C[r]=t,v.insert(a+N(r),r))}}}if(w[n]===d)return null;const A=[];let O=n;for(;O!==i&&O>=0;)A.push(r[O]),O=C[O];return A.push(r[i]),A.reverse(),{type:"Feature",geometry:{type:"LineString",coordinates:A},properties:{}}}buildLandmarkHeuristicData(){const t=this.csrOffsets,s=this.csrIndices,e=this.csrDistances,i=this.csrNodeCount;if(!t||!s||!e||0===i)return this.landmarkNodeCount=0,this.landmarkCount=0,void(this.landmarkDistancesFlat=null);const n=Math.min(this.maxLandmarks,i),r=new Uint8Array(i),a=[];let c=0;for(let o=0;o<n;o++){r[c]=1;const n=this.computeShortestDistancesFrom(c,i,t,s,e);a.push(n);let o=-1,h=-1;for(let t=0;t<i;t++){if(0!==r[t])continue;const s=n[t];Number.isFinite(s)&&s>o&&(o=s,h=t)}if(h<0)break;c=h}const o=a.length,h=new Float64Array(o*i);for(let t=0;t<o;t++)h.set(a[t],t*i);this.landmarkNodeCount=i,this.landmarkCount=o,this.landmarkDistancesFlat=h,this.landmarksDirty=!1}ensureLandmarkHeuristicData(){this.landmarksDirty&&this.buildLandmarkHeuristicData()}computeShortestDistancesFrom(t,s,e,i,n){const r=Number.POSITIVE_INFINITY,a=new Float64Array(s),c=new Uint8Array(s);a.fill(r),a[t]=0;const o=new this.heapConstructor;for(o.insert(0,t);o.size()>0;){const t=o.extractMin();if(null===t)break;if(0!==c[t])continue;c[t]=1;const s=a[t];for(let r=e[t],c=e[t+1];r<c;r++){const t=i[r],e=s+n[r];e>=a[t]||(a[t]=e,o.insert(e,t))}}return a}getOrCreateIndex(t){const s=t[0],e=t[1];let i=this.coordinateIndexMap.get(s);void 0===i&&(i=new Map,this.coordinateIndexMap.set(s,i));let n=i.get(e);if(void 0===n&&(n=this.coordinates.length,this.coordinates.push(t),i.set(e,n),this.adjacencyList[n]=[],this.csrOffsets)){const t=this.csrNodeCount;if(n===t){const s=new Int32Array(t+2);s.set(this.csrOffsets,0),s[t+1]=s[t],this.csrOffsets=s,this.csrNodeCount=t+1}}return n}ensureScratch(t){if(this.scratchCapacity>=t&&this.gScoreScratch&&this.cameFromScratch&&this.visitedScratch&&this.heuristicScratch&&this.heuristicStampScratch)return;const s=0|t;this.gScoreScratch=new Float64Array(s),this.cameFromScratch=new Int32Array(s),this.visitedScratch=new Uint8Array(s),this.heuristicScratch=new Float64Array(s),this.heuristicStampScratch=new Uint32Array(s),this.scratchCapacity=s}forEachSegment(t,s){const e=t.features;for(let t=0,i=e.length;t<i;t++){const i=e[t].geometry.coordinates;for(let t=0,e=i.length-1;t<e;t++)s(i[t],i[t+1])}}indexCoordinate(t,s,e,i){const n=t[0],r=t[1];let a=s.get(n);void 0===a&&(a=new Map,s.set(n,a));let c=a.get(r);return void 0===c&&(c=e.length,e.push(t),a.set(r,c),i&&i(c)),c}}export{n as TerraRoute,e as createCheapRuler,s as haversineDistance};
2
2
  //# sourceMappingURL=terra-route.modern.js.map