terra-route
Version:
A library for routing along GeoJSON LineString networks
3 lines (2 loc) • 16.5 kB
JavaScript
const t=(t,e)=>{const n=t=>t*Math.PI/180,s=n(t[1]),r=n(t[0]),o=n(e[1]),i=o-s,a=n(e[0])-r,h=Math.sin(i/2)*Math.sin(i/2)+Math.cos(s)*Math.cos(o)*Math.sin(a/2)*Math.sin(a/2);return 2*Math.atan2(Math.sqrt(h),Math.sqrt(1-h))*6371e3/1e3};function e(t){const e=1/298.257223563,n=e*(2-e),s=Math.PI/180,r=Math.cos(t*s),o=1/(1-n*(1-r*r)),i=Math.sqrt(o),a=6378.137*s,h=a*i*r,c=a*i*o*(1-n);return function(t,e){let n=t[0]-e[0];for(;n<-180;)n+=360;for(;n>180;)n-=360;const s=n*h,r=(t[1]-e[1])*c;return Math.sqrt(s*s+r*r)}}function n(t,e,s){s[t]=!0;for(const r of e[t])s[r]||n(r,e,s)}function s(t,e){const n=JSON.stringify(t),s=JSON.stringify(e);return n<s?`${n}|${s}`:`${s}|${n}`}function r(t,e){const[n,s]=function(t,e){const[n,s]=t,[r,o]=e;return n<r||n===r&&s<=o?[t,e]:[e,t]}(t,e);return JSON.stringify([n,s])}function o(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 s=t[n],o=t[n+1],i=r(s,o);e.has(i)||e.set(i,{type:"Feature",geometry:{type:"LineString",coordinates:[s,o]},properties:{}})}}return{type:"FeatureCollection",features:Array.from(e.values())}}function i(){return i=Object.assign?Object.assign.bind():function(t){for(var e=1;e<arguments.length;e++){var n=arguments[e];for(var s in n)({}).hasOwnProperty.call(n,s)&&(t[s]=n[s])}return t},i.apply(null,arguments)}function a(e){const n=e.geometry.coordinates;let s=0;for(let e=0;e<n.length-1;e++)s+=t(n[e],n[e+1]);return s}function h(t,e){const n=t.length,s=e.length;if(n>s)return!1;for(let r=0;r<=s-n;r++){let s=!0;for(let o=0;o<n;o++)if(e[r+o][0]!==t[o][0]||e[r+o][1]!==t[o][1]){s=!1;break}if(s)return!0}const r=[...t].reverse();for(let t=0;t<=s-n;t++){let s=!0;for(let o=0;o<n;o++)if(e[t+o][0]!==r[o][0]||e[t+o][1]!==r[o][1]){s=!1;break}if(s)return!0}return!1}function c(t){const e=o(t),n=new Map;function s(t){return t.join(",")}for(let t=0;t<e.features.length;t++){const r=e.features[t].geometry.coordinates;if(r.length<2)continue;const o=s(r[0]),i=s(r[r.length-1]);n.set(o,(n.get(o)||0)+1),n.set(i,(n.get(i)||0)+1)}const r=new Map,i=new Map;for(let t=0;t<e.features.length;t++){const o=e.features[t],a=o.geometry.coordinates;if(a.length<2)continue;const h=s(a[0]),c=s(a[a.length-1]),u=n.get(h)||0,f=n.get(c)||0,l=a.map(s).join(";");1===u||1===f?r.has(l)||r.set(l,o):i.has(l)||i.set(l,o)}return{leafEdges:{type:"FeatureCollection",features:Array.from(r.values())},nonLeafEdges:{type:"FeatureCollection",features:Array.from(i.values())}}}function u(t,e,n,s,r){const o=t.geometry.coordinates;for(const t of o)if(!f(t,e,n,s,r))return!1;return!0}function f(t,e,n,s,r){const[o,i]=t;return o>=e&&o<=s&&i>=n&&i<=r}const l=[Int8Array,Uint8Array,Uint8ClampedArray,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array];class d{constructor(t,e=64,n=Float64Array,s){if(this.data=void 0,this.ids=void 0,this.coords=void 0,this._pos=void 0,this._finished=void 0,this.numItems=void 0,this.nodeSize=void 0,this.ArrayType=void 0,this.IndexArrayType=void 0,isNaN(t)||t<0)throw new Error(`Unexpected numItems value: ${t}.`);this.numItems=t,this.nodeSize=Math.min(Math.max(e,2),65535),this.ArrayType=n,this.IndexArrayType=t<65536?Uint16Array:Uint32Array;const r=l.indexOf(this.ArrayType),o=2*t*this.ArrayType.BYTES_PER_ELEMENT,i=t*this.IndexArrayType.BYTES_PER_ELEMENT,a=(8-i%8)%8;if(r<0)throw new Error(`Unexpected typed array class: ${n}.`);s?(this.data=s,this.ids=new this.IndexArrayType(this.data,8,t),this.coords=new this.ArrayType(this.data,8+i+a,2*t),this._pos=2*t,this._finished=!0):(this.data=new ArrayBuffer(8+o+i+a),this.ids=new this.IndexArrayType(this.data,8,t),this.coords=new this.ArrayType(this.data,8+i+a,2*t),this._pos=0,this._finished=!1,new Uint8Array(this.data,0,2).set([219,16+r]),new Uint16Array(this.data,2,1)[0]=this.nodeSize,new Uint32Array(this.data,4,1)[0]=this.numItems)}add(t,e){const n=this._pos>>1;return this.ids[n]=n,this.coords[this._pos++]=t,this.coords[this._pos++]=e,n}finish(){const t=this._pos>>1;if(t!==this.numItems)throw new Error(`Added ${t} items when expected ${this.numItems}.`);return g(this.ids,this.coords,this.nodeSize,0,this.numItems-1,0),this._finished=!0,this}}function g(t,e,n,s,r,o){if(r-s<=n)return;const i=s+r>>1;p(t,e,i,s,r,o),g(t,e,n,s,i-1,1-o),g(t,e,n,i+1,r,1-o)}function p(t,e,n,s,r,o){for(;r>s;){if(r-s>600){const i=r-s+1,a=n-s+1,h=Math.log(i),c=.5*Math.exp(2*h/3),u=.5*Math.sqrt(h*c*(i-c)/i)*(a-i/2<0?-1:1);p(t,e,n,Math.max(s,Math.floor(n-a*c/i+u)),Math.min(r,Math.floor(n+(i-a)*c/i+u)),o)}const i=e[2*n+o];let a=s,h=r;for(y(t,e,s,n),e[2*r+o]>i&&y(t,e,s,r);a<h;){for(y(t,e,a,h),a++,h--;e[2*a+o]<i;)a++;for(;e[2*h+o]>i;)h--}e[2*s+o]===i?y(t,e,s,h):(h++,y(t,e,h,r)),h<=n&&(s=h+1),n<=h&&(r=h-1)}}function y(t,e,n,s){m(t,n,s),m(e,2*n,2*s),m(e,2*n+1,2*s+1)}function m(t,e,n){const s=t[e];t[e]=t[n],t[n]=s}class w{constructor(t=[],e=(t,e)=>t<e?-1:t>e?1:0){if(this.data=void 0,this.length=void 0,this.compare=void 0,this.data=t,this.length=this.data.length,this.compare=e,this.length>0)for(let t=(this.length>>1)-1;t>=0;t--)this._down(t)}push(t){this.data.push(t),this._up(this.length++)}pop(){if(0===this.length)return;const t=this.data[0],e=this.data.pop();return this.length--,this.length>0&&(this.data[0]=e,this._down(0)),t}peek(){return this.data[0]}_up(t){const{data:e,compare:n}=this,s=e[t];for(;t>0;){const r=t-1>>1,o=e[r];if(n(s,o)>=0)break;e[t]=o,t=r}e[t]=s}_down(t){const{data:e,compare:n}=this,s=this.length>>1,r=e[t];for(;t<s;){let s=1+(t<<1);const o=s+1;if(o<this.length&&n(e[o],e[s])<0&&(s=o),n(e[s],r)>=0)break;e[t]=e[s],t=s}e[t]=r}}const M=Math.PI/180;function x(t,e,n,s){const r=s.minLng,o=s.maxLng,i=s.minLat,a=s.maxLat;if(t>=r&&t<=o)return e<i?S((e-i)*M):e>a?S((e-a)*M):0;const h=Math.min(S((t-r)*M),S((t-o)*M)),c=function(t,e){const n=1-2*e;return n<=0?t>0?90:-90:Math.atan(Math.tan(t*M)/n)/M}(e,h);return c>i&&c<a?I(h,n,e,c):Math.min(I(h,n,e,i),I(h,n,e,a))}function v(t,e){return t.dist-e.dist}function S(t){const e=Math.sin(t/2);return e*e}function I(t,e,n,s){return e*Math.cos(s*M)*t+S((n-s)*M)}function A(t,e,n,s,r){return I(S((t-n)*M),r,e,s)}class k{constructor(t){this.network=void 0,this.network=t}setNetwork(t){this.network=t}getNetwork(){return this.network}getNetworkInBoundingBox(t){return function(t,e){const[n,s,r,o]=e;if(n>=r||s>=o)throw new Error("Invalid bounding box: min values must be less than max values");const i=[];for(const e of t.features)u(e,n,s,r,o)&&i.push(e);return{type:"FeatureCollection",features:i}}(this.network,t)}getNetworkWithoutDuplicatesOrSubsections(){return function(t){const e=t.features,n=new Set;for(let t=0;t<e.length;t++){const s=e[t].geometry.coordinates;for(let r=0;r<e.length;r++){if(t===r)continue;const o=e[r].geometry.coordinates;if(h(s,o)&&(s.length<o.length||s.length===o.length&&t>r)){n.add(t);break}}}return{type:"FeatureCollection",features:e.filter((t,e)=>!n.has(e))}}(this.network)}getConnectedComponents(){return function(t){const e=t.features,n=new Map,s=new Map;function r(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=r(e);s.has(n)||s.set(n,new Set),s.get(n).add(t)}}for(let t=0;t<e.length;t++){n.set(t,new Set);const o=e[t].geometry.coordinates;for(const e of o){const o=r(e),i=s.get(o);if(i)for(const e of i)e!==t&&n.get(t).add(e)}}const o=new Set,i=[];function a(t,s){const r=[t];for(;r.length>0;){const t=r.pop();if(o.has(t))continue;o.add(t),s.push(e[t]);const i=n.get(t);if(i)for(const t of i)o.has(t)||r.push(t)}}for(let t=0;t<e.length;t++)if(!o.has(t)){const e=[];a(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,s=e.length,r=new Map;for(let t=0;t<s;t++){const n=e[t].geometry.coordinates;for(const e of n){const n=`${(o=e)[0]},${o[1]}`;r.has(n)||r.set(n,[]),r.get(n).push(t)}}var o;const i=Array.from({length:s},()=>[]);for(const t of r.values())for(let e=0;e<t.length;e++)for(let n=e+1;n<t.length;n++){const s=t[e],r=t[n];i[s].push(r),i[r].push(s)}const a=new Array(s).fill(!1);let h=0;for(let t=0;t<s;t++)a[t]||(n(t,i,a),h++);return h}(this.network)}getNodeAndEdgeCount(){return function(t){const e=new Set,n=new Set;for(const r of t.features){const t=r.geometry.coordinates;for(const n of t)e.add(JSON.stringify(n));for(let e=0;e<t.length-1;e++){const r=s(t[e],t[e+1]);n.add(r)}}return{nodeCount:e.size,edgeCount:n.size}}(this.network)}getNodes(){return{type:"FeatureCollection",features:function(t){const e=new Set,n=[];for(const s of t.features)for(const t of s.geometry.coordinates){const s=t.join(",");e.has(s)||(e.add(s),n.push({type:"Feature",geometry:{type:"Point",coordinates:t},properties:{}}))}return n}(this.network)}}getNodeCount(){const{nodeCount:t}=this.getNodeAndEdgeCount();return t}getEdges(){return o(this.network)}getLongestEdgeLength(){const t=this.getLongestEdge();return t?a(t):-1}getShortestEdgeLength(){const t=this.getShortestEdge();return t?a(t):-1}getLongestEdge(){const t=this.getEdges().features;if(0===t.length)return null;const e=t.sort((t,e)=>a(t)-a(e));return e[e.length-1]}getShortestEdge(){const t=this.getEdges().features;return 0===t.length?null:t.sort((t,e)=>a(t)-a(e))[0]}getEdgeCount(){const{edgeCount:t}=this.getNodeAndEdgeCount();return t}getLeafEdges(){return c(this.network).leafEdges}getPrunedEdges(t){if(t&&t>0){let e=this.network;for(let n=0;n<t;n++)e=c(e).nonLeafEdges;return e}return c(this.network).nonLeafEdges}getUnifiedNetwork(e){return function(e,n){if(e.features.length<2)return e;const s=new Set,r=new Map,o=[],a=new Map;for(const t of e.features)for(const e of t.geometry.coordinates){const t=`${e[0]},${e[1]}`;a.has(t)||(a.set(t,o.length),o.push(e))}let h=new d(o.length);for(const t of o)h.add(t[0],t[1]);function c(e,r,i){let a=null,c=Infinity;const u=function(t,e,n,s=Infinity,r=Infinity){let o=1;const i=[];void 0===s&&(s=Infinity),void 0!==r&&(o=S(r/6371));const a=new w([],v);let h={left:0,right:t.ids.length-1,axis:0,dist:0,minLng:-180,minLat:-90,maxLng:180,maxLat:90};const c=Math.cos(n*M);for(;h;){const r=h.right,u=h.left;if(r-u<=t.nodeSize)for(let s=u;s<=r;s++){const r=t.ids[s],o=A(e,n,t.coords[2*s],t.coords[2*s+1],c);a.push({id:r,dist:o})}else{const s=u+r>>1,o=t.coords[2*s],i=t.coords[2*s+1],f=t.ids[s],l=A(e,n,o,i,c);a.push({id:f,dist:l});const d=(h.axis+1)%2,g={left:u,right:s-1,axis:d,minLng:h.minLng,minLat:h.minLat,maxLng:0===h.axis?o:h.maxLng,maxLat:1===h.axis?i:h.maxLat,dist:0},p={left:s+1,right:r,axis:d,minLng:0===h.axis?o:h.minLng,minLat:1===h.axis?i:h.minLat,maxLng:h.maxLng,maxLat:h.maxLat,dist:0};g.dist=x(e,n,c,g),p.dist=x(e,n,c,p),a.push(g),a.push(p)}for(;a.length&&null!=a.peek().id;){const t=a.pop();if(t.dist>o)return i;if(i.push(t.id),i.length===s)return i}h=a.pop()}return i}(h,e[0],e[1],Infinity,n/1e3);for(const h of u){const u=o[h],f=`${u[0]},${u[1]}`;if(!s.has(f))continue;if(r.includes(u))continue;if(i.has(f))continue;const l=1e3*t(u,e);l<=n&&l<c&&(a=u,c=l)}return null!==a?a:(s.add(`${e[0]},${e[1]}`),e)}h.finish();const u=e.features.map(t=>{const e=[],n=[],s=new Set;for(const o of t.geometry.coordinates){const t=`${o[0]},${o[1]}`;if(!r.has(t)){const n=c(o,e,s);s.has(`${n[0]},${n[1]}`)?r.set(t,o):r.set(t,n)}const i=r.get(t),a=`${i[0]},${i[1]}`;s.has(a)||(n.push(i),s.add(a)),e.push(o)}return i({},t,{geometry:i({},t.geometry,{coordinates:n})})});return i({},e,{features:u})}(this.network,e)}}class L{constructor(){this.keys=[],this.values=[],this.idxs=[],this.length=0,this.insertCounter=0}insert(t,e){let n=this.length;this.length=n+1;let s=t,r=e,o=this.insertCounter++;for(;n>0;){const t=n-1>>>2,e=this.keys[t],r=this.idxs[t];if(s>e||s===e&&o>r)break;this.keys[n]=e,this.values[n]=this.values[t],this.idxs[n]=r,n=t}this.keys[n]=s,this.values[n]=r,this.idxs[n]=o}extractMin(){const t=this.length;if(0===t)return null;const e=this.values[0],n=t-1;return this.length=n,n>0&&(this.keys[0]=this.keys[n],this.values[0]=this.values[n],this.idxs[0]=this.idxs[n],this.bubbleDown(0)),e}size(){return this.length}bubbleDown(t){const e=this.length,n=this.keys,s=this.values,r=this.idxs,o=n[t],i=s[t],a=r[t];for(;;){const i=1+(t<<2);if(i>=e)break;let h=i,c=n[i],u=r[i],f=s[i];const l=i+1;if(l<e){const t=n[l],e=r[l];(t<c||t===c&&e<u)&&(h=l,c=t,u=e,f=s[l])}const d=i+2;if(d<e){const t=n[d],e=r[d];(t<c||t===c&&e<u)&&(h=d,c=t,u=e,f=s[d])}const g=i+3;if(g<e){const t=n[g],e=r[g];(t<c||t===c&&e<u)&&(h=g,c=t,u=e,f=s[g])}if(!(c<o||c===o&&u<a))break;n[t]=c,s[t]=f,r[t]=u,t=h}n[t]=o,s[t]=i,r[t]=a}}class C{constructor(e){var n,s;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.scratchCapacity=0,this.distanceMeasurement=null!=(n=null==e?void 0:e.distanceMeasurement)?n:t,this.heapConstructor=null!=(s=null==e?void 0:e.heap)?s:L}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 e=this.coordinateIndexMap,n=this.coordinates,s=this.distanceMeasurement,r=t.features,o=[];for(let t=0,s=r.length;t<s;t++){const s=r[t].geometry.coordinates;for(let t=0,r=s.length-1;t<r;t++){var i,a;const r=s[t],h=s[t+1],c=r[0],u=r[1],f=h[0],l=h[1];let d=e.get(c);void 0===d&&(d=new Map,e.set(c,d));let g=d.get(u);void 0===g&&(g=n.length,n.push(r),d.set(u,g));let p=e.get(f);void 0===p&&(p=new Map,e.set(f,p));let y=p.get(l);void 0===y&&(y=n.length,n.push(h),p.set(l,y)),o[g]=(null!=(i=o[g])?i:0)+1,o[y]=(null!=(a=o[y])?a:0)+1}}const h=this.coordinates.length;this.csrNodeCount=h;const c=new Int32Array(h+1);for(let t=0;t<h;t++){var u;const e=null!=(u=o[t])?u:0;c[t+1]=c[t]+e}const f=c[h],l=new Int32Array(f),d=new Float64Array(f),g=c.slice();for(let t=0,e=r.length;t<e;t++){const e=r[t].geometry.coordinates;for(let t=0,n=e.length-1;t<n;t++){const n=e[t],r=e[t+1],o=n[1],i=r[0],a=r[1],h=this.coordinateIndexMap.get(n[0]).get(o),c=this.coordinateIndexMap.get(i).get(a),u=s(n,r);let f=g[h]++;l[f]=c,d[f]=u,f=g[c]++,l[f]=h,d[f]=u}}this.csrOffsets=c,this.csrIndices=l,this.csrDistances=d,this.adjacencyList=new Array(h)}getRoute(t,e){if(null===this.network)throw new Error("Network not built. Please call buildRouteGraph(network) first.");const n=this.getOrCreateIndex(t.geometry.coordinates),s=this.getOrCreateIndex(e.geometry.coordinates);if(n===s)return null;const r=this.coordinates,o=this.adjacencyList,i=this.distanceMeasurement,a=r.length;this.ensureScratch(a);const h=this.gScoreScratch,c=this.cameFromScratch,u=this.visitedScratch,f=this.hScratch;h.fill(Number.POSITIVE_INFINITY,0,a),c.fill(-1,0,a),u.fill(0,0,a),f.fill(-1,0,a);const l=new this.heapConstructor,d=r[s];let g=f[n];for(g<0&&(g=i(r[n],d),f[n]=g),l.insert(g,n),h[n]=0;l.size()>0;){const t=l.extractMin();if(0===u[t]){if(t===s)break;if(u[t]=1,this.csrOffsets&&t<this.csrNodeCount){const e=this.csrOffsets,n=this.csrIndices,s=this.csrDistances,o=e[t+1];for(let a=e[t];a<o;a++){const e=n[a],o=h[t]+s[a];if(o<h[e]){h[e]=o,c[e]=t;let n=f[e];n<0&&(n=i(r[e],d),f[e]=n),l.insert(o+n,e)}}}else{const e=o[t];if(!e||0===e.length)continue;for(let n=0,s=e.length;n<s;n++){const s=e[n],o=s.node,a=h[t]+s.distance;if(a<h[o]){h[o]=a,c[o]=t;let e=f[o];e<0&&(e=i(r[o],d),f[o]=e),l.insert(a+e,o)}}}}}if(c[s]<0)return null;const p=[];let y=s;for(;y!==n;)p.push(r[y]),y=c[y];return p.push(r[n]),p.reverse(),{type:"Feature",geometry:{type:"LineString",coordinates:p},properties:{}}}getOrCreateIndex(t){const e=t[0],n=t[1];let s=this.coordinateIndexMap.get(e);void 0===s&&(s=new Map,this.coordinateIndexMap.set(e,s));let r=s.get(n);if(void 0===r&&(r=this.coordinates.length,this.coordinates.push(t),s.set(n,r),this.adjacencyList[r]=[],this.csrOffsets)){const t=this.csrNodeCount;if(r===t){const e=new Int32Array(t+2);e.set(this.csrOffsets,0),e[t+1]=e[t],this.csrOffsets=e,this.csrNodeCount=t+1}}return r}ensureScratch(t){if(this.scratchCapacity>=t&&this.gScoreScratch&&this.cameFromScratch&&this.visitedScratch&&this.hScratch)return;const e=0|t;this.gScoreScratch=new Float64Array(e),this.cameFromScratch=new Int32Array(e),this.visitedScratch=new Uint8Array(e),this.hScratch=new Float64Array(e),this.scratchCapacity=e}}export{k as LineStringGraph,C as TerraRoute,e as createCheapRuler,t as haversineDistance};
//# sourceMappingURL=terra-route.modern.js.map