terra-route
Version:
A library for routing along GeoJSON LineString networks
3 lines (2 loc) • 6.71 kB
JavaScript
const t=(t,e)=>{const n=t=>t*Math.PI/180,o=n(t[1]),s=n(t[0]),r=n(e[1]),i=r-o,c=n(e[0])-s,a=Math.sin(i/2)*Math.sin(i/2)+Math.cos(o)*Math.cos(r)*Math.sin(c/2)*Math.sin(c/2);return 2*Math.atan2(Math.sqrt(a),Math.sqrt(1-a))*6371e3/1e3};function e(t){const e=1/298.257223563,n=e*(2-e),o=Math.PI/180,s=Math.cos(t*o),r=1/(1-n*(1-s*s)),i=Math.sqrt(r),c=6378.137*o,a=c*i*s,h=c*i*r*(1-n);return function(t,e){let n=t[0]-e[0];for(;n<-180;)n+=360;for(;n>180;)n-=360;const o=n*a,s=(t[1]-e[1])*h;return Math.sqrt(o*o+s*s)}}class n{constructor(){this.heap=[],this.insertCounter=0}insert(t,e){const n={key:t,value:e,index:this.insertCounter++};let o=this.heap.length;for(this.heap.push(n);o>0;){const e=o-1>>>1,s=this.heap[e];if(t>s.key||t===s.key&&n.index>s.index)break;this.heap[o]=s,o=e}this.heap[o]=n}extractMin(){const t=this.heap,e=t.length;if(0===e)return null;const n=t[0],o=t.pop();return e>1&&(t[0]=o,this.bubbleDown(0)),n.value}size(){return this.heap.length}bubbleDown(t){const e=this.heap,n=e.length,o=e[t],s=o.key,r=o.index;for(;;){const o=1+(t<<1);if(o>=n)break;let i=o,c=e[o];const a=o+1;if(a<n){const t=e[a];(t.key<c.key||t.key===c.key&&t.index<c.index)&&(i=a,c=t)}if(!(c.key<s||c.key===s&&c.index<r))break;e[t]=c,t=i}e[t]=o}}function o(t,e,n){n[t]=!0;for(const s of e[t])n[s]||o(s,e,n)}function s(t,e){const n=JSON.stringify(t),o=JSON.stringify(e);return n<o?`${n}|${o}`:`${o}|${n}`}function r(t,e){const[n,o]=function(t,e){const[n,o]=t,[s,r]=e;return n<s||n===s&&o<=r?[t,e]:[e,t]}(t,e);return JSON.stringify([n,o])}function i(e){const n=e.geometry.coordinates;let o=0;for(let e=0;e<n.length-1;e++)o+=t(n[e],n[e+1]);return o}class c{constructor(t){this.network=void 0,this.network=t}setNetwork(t){this.network=t}getNetwork(){return this.network}getConnectedComponents(){return function(t){const e=t.features,n=new Map,o=new Map;function s(t){return`${t[0]},${t[1]}`}for(let t=0;t<e.length;t++){const n=e[t].geometry.coordinates;for(const e of n){const n=s(e);o.has(n)||o.set(n,new Set),o.get(n).add(t)}}for(let t=0;t<e.length;t++){n.set(t,new Set);const r=e[t].geometry.coordinates;for(const e of r){const r=s(e),i=o.get(r);if(i)for(const e of i)e!==t&&n.get(t).add(e)}}const r=new Set,i=[];function c(t,o){const s=[t];for(;s.length>0;){const t=s.pop();if(r.has(t))continue;r.add(t),o.push(e[t]);const i=n.get(t);if(i)for(const t of i)r.has(t)||s.push(t)}}for(let t=0;t<e.length;t++)if(!r.has(t)){const e=[];c(t,e),i.push({type:"FeatureCollection",features:e})}return i.sort((t,e)=>t.features.length-e.features.length),i}(this.network)}getConnectedComponentCount(){return function(t){const e=t.features,n=e.length,s=new Map;for(let t=0;t<n;t++){const n=e[t].geometry.coordinates;for(const e of n){const n=`${(r=e)[0]},${r[1]}`;s.has(n)||s.set(n,[]),s.get(n).push(t)}}var r;const i=Array.from({length:n},()=>[]);for(const t of s.values())for(let e=0;e<t.length;e++)for(let n=e+1;n<t.length;n++){const o=t[e],s=t[n];i[o].push(s),i[s].push(o)}const c=new Array(n).fill(!1);let a=0;for(let t=0;t<n;t++)c[t]||(o(t,i,c),a++);return a}(this.network)}getNodeAndEdgeCount(){return function(t){const e=new Set,n=new Set;for(const o of t.features){const t=o.geometry.coordinates;for(const n of t)e.add(JSON.stringify(n));for(let e=0;e<t.length-1;e++){const o=s(t[e],t[e+1]);n.add(o)}}return{nodeCount:e.size,edgeCount:n.size}}(this.network)}getNodes(){return{type:"FeatureCollection",features:function(t){const e=new Set,n=[];for(const o of t.features)for(const t of o.geometry.coordinates){const o=t.join(",");e.has(o)||(e.add(o),n.push({type:"Feature",geometry:{type:"Point",coordinates:t},properties:{}}))}return n}(this.network)}}getNodeCount(){const{nodeCount:t}=this.getNodeAndEdgeCount();return t}getEdges(){return function(t){const e=new Map;for(const n of t.features){const t=n.geometry.coordinates;for(let n=0;n<t.length-1;n++){const o=t[n],s=t[n+1],i=r(o,s);e.has(i)||e.set(i,{type:"Feature",geometry:{type:"LineString",coordinates:[o,s]},properties:{}})}}return{type:"FeatureCollection",features:Array.from(e.values())}}(this.network)}getLongestEdgeLength(){const t=this.getLongestEdge();return t?i(t):-1}getShortestEdgeLength(){const t=this.getShortestEdge();return t?i(t):-1}getLongestEdge(){const t=this.getEdges().features;if(0===t.length)return null;const e=t.sort((t,e)=>i(t)-i(e));return e[e.length-1]}getShortestEdge(){const t=this.getEdges().features;return 0===t.length?null:t.sort((t,e)=>i(t)-i(e))[0]}getEdgeCount(){const{edgeCount:t}=this.getNodeAndEdgeCount();return t}}class a{constructor(e){var o,s;this.network=null,this.distanceMeasurement=void 0,this.heapConstructor=void 0,this.coordinateIndexMap=new Map,this.coordinates=[],this.adjacencyList=[],this.distanceMeasurement=null!=(o=null==e?void 0:e.distanceMeasurement)?o:t,this.heapConstructor=null!=(s=null==e?void 0:e.heap)?s:n}buildRouteGraph(t){this.network=t,this.coordinateIndexMap=new Map,this.coordinates=[],this.adjacencyList=[];const e=this.coordinateIndexMap,n=this.coordinates,o=this.adjacencyList,s=this.distanceMeasurement;for(const r of t.features){const t=r.geometry.coordinates;for(let r=0;r<t.length-1;r++){const[i,c]=t[r],[a,h]=t[r+1];let u=e.get(i);u||(u=new Map,e.set(i,u));let d=u.get(c);void 0===d&&(d=n.length,n.push(t[r]),u.set(c,d),o[d]=[]);let f=e.get(a);f||(f=new Map,e.set(a,f));let g=f.get(h);void 0===g&&(g=n.length,n.push(t[r+1]),f.set(h,g),o[g]=[]);const l=s(t[r],t[r+1]);o[d].push({node:g,distance:l}),o[g].push({node:d,distance:l})}}}getRoute(t,e){if(!this.network)throw new Error("Network not built. Please call buildRouteGraph(network) first.");const n=this.getOrCreateIndex(t.geometry.coordinates),o=this.getOrCreateIndex(e.geometry.coordinates);if(n===o)return null;const s=new this.heapConstructor;s.insert(0,n);const r=this.coordinates.length,i=new Array(r).fill(Infinity),c=new Array(r).fill(-1),a=new Array(r).fill(!1);for(i[n]=0;s.size()>0;){const t=s.extractMin();if(!a[t]){if(t===o)break;a[t]=!0;for(const e of this.adjacencyList[t]||[]){const n=i[t]+e.distance;if(n<i[e.node]){i[e.node]=n,c[e.node]=t;const r=this.distanceMeasurement(this.coordinates[e.node],this.coordinates[o]);s.insert(n+r,e.node)}}}}if(c[o]<0)return null;const h=[];let u=o;for(;u!==n;)h.unshift(this.coordinates[u]),u=c[u];return h.unshift(this.coordinates[n]),{type:"Feature",geometry:{type:"LineString",coordinates:h},properties:{}}}getOrCreateIndex(t){const[e,n]=t;let o=this.coordinateIndexMap.get(e);o||(o=new Map,this.coordinateIndexMap.set(e,o));let s=o.get(n);return void 0===s&&(s=this.coordinates.length,this.coordinates.push(t),o.set(n,s),this.adjacencyList[s]=[]),s}}export{c as LineStringGraph,a as TerraRoute,e as createCheapRuler,t as haversineDistance};
//# sourceMappingURL=terra-route.modern.js.map