gps-plus-slam-js 1.0.1 → 1.0.4

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.
package/dist/index.d.ts CHANGED
@@ -499,7 +499,7 @@ declare const geoHashToLatLong: (geoHash: string) => LatLong;
499
499
  declare const distanceInMeters: (a: LatLong, b: LatLong) => number;
500
500
  declare const distanceInMetersRelative: (origin: LatLong, coord: LatLong) => number;
501
501
  declare const calcRelativeCoordsInMeters: (origin: LatLong, coord: LatLong, altitude?: number, originAltitude?: number) => vec3;
502
- declare const calcGpsCoords: (origin: LatLongAlt, relative: vec3) => LatLongAlt;
502
+ declare const calcGpsCoords: (origin: LatLong, relative: vec3) => LatLong;
503
503
  declare const toEarthCenteredCoordinates: (coord: LatLong, altitude?: number) => vec3;
504
504
  declare const getGoogleMapsLink: (coord: LatLong) => string;
505
505
  declare const getGoogleMapsDirectionsLink: (destination: LatLong, origin: LatLong) => string;
@@ -518,7 +518,7 @@ declare const getOpenStreetMapLink: (coord: LatLong, zoomLevel?: number) => stri
518
518
  * @param zeroRef - GPS origin for NUE→GPS conversion
519
519
  * @returns GPS coordinates as { lat, lon, altitude? }
520
520
  */
521
- declare const fusedGpsFromOdom: (alignmentMatrix: Matrix4, odomPosition: Vector3, zeroRef: LatLongAlt) => LatLongAlt;
521
+ declare const fusedGpsFromOdom: (alignmentMatrix: Matrix4, odomPosition: Vector3, zeroRef: LatLong) => LatLongAlt;
522
522
  //#endregion
523
523
  //#region ../src/state/selectors.d.ts
524
524
  /** Returns the 4×4 alignment matrix, or null if not yet computed. */
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
1
  /*! gps-plus-slam-js | (c) 2026 cs-util-com | UNLICENSED — see EULA.md */
2
- import{configureStore as e,createSlice as t}from"@reduxjs/toolkit";import{ed25519 as n}from"@noble/curves/ed25519.js";import{hexToBytes as r}from"@noble/curves/utils.js";import{mat4 as i,quat as a,vec3 as o,vec4 as s}from"gl-matrix";import{castDraft as c}from"immer";let l=!1;function u(){l=!0}function d(){if(!l)throw Error(`gps-plus-slam-js: license not activated. Construct your store via createRecorderStore() from gps-plus-slam-app-framework, or pass a valid licenseKey to createGpsSlamStore(). See EULA §3.`)}function f(e){return((...t)=>(d(),e(...t)))}function p(e){let t=((...t)=>(d(),e(...t)));return Object.assign(t,e),t.toString=()=>e.type,t}function m(e){return(t,n)=>(d(),e(t,n))}function h(e){let t=e.replace(/-/g,`+`).replace(/_/g,`/`),n=`=`.repeat((4-t.length%4)%4),r=atob(t+n);return Uint8Array.from(r,e=>e.charCodeAt(0))}function g(e){let t=new Date,i=e.indexOf(`.`);if(i<=0||i>=e.length-1)throw Error(`Invalid license key format: expected "payload.signature" with base64url-encoded parts.`);let a,o;try{a=h(e.substring(0,i)),o=h(e.substring(i+1))}catch{throw Error(`Invalid license key format: base64url decoding failed.`)}let s=r(`dfe5c62120b7a0ce962b17907a15b27cf1b056e6619bf6ce55e6e094b322a470`);if(!n.verify(o,a,s))throw Error(`Invalid license key: signature verification failed.`);let c=JSON.parse(new TextDecoder().decode(a));if(typeof c!=`object`||!c)throw Error(`Invalid license key: payload must be a JSON object.`);let{type:l,exp:d}=c;if(l!==`community`)throw Error(`Invalid license key: "type" must be "community".`);if(typeof d!=`number`||!Number.isFinite(d))throw Error(`Invalid license key: "exp" must be a finite number (Unix timestamp in seconds).`);if(d<Math.floor(t.getTime()/1e3)){let e=new Date(d*1e3);throw Error(`License key expired on ${e.toISOString()}. Update to the latest version for a renewed community key.`)}return u(),{type:l,expiresAt:new Date(d*1e3)}}const _=180/Math.PI,v=e=>{let t=e[0],n=e[1],r=e[2],i=e[3],a=2*(i*t+n*r),o=1-2*(t*t+n*n),s=Math.atan2(a,o)*_,c=2*(i*n-r*t),l=Math.abs(c)>=1?Math.sign(c)*90:Math.asin(c)*_,u=2*(i*r+t*n),d=1-2*(n*n+r*r);return{pitch:l,yaw:Math.atan2(u,d)*_,roll:s}},y=1/298.257223563,b=y*(2-y),x=40075016.6856/360,S=39940652.7422/360,C=Math.PI/180,ee=180/Math.PI,w=`0123456789bcdefghjkmnpqrstuvwxyz`,T=[16,8,4,2,1],E=(e,t,n)=>Math.min(Math.max(e,t),n),te=e=>E(e,-180,180),D=e=>e*C,ne=e=>e*ee,re=e=>{if(!Number.isFinite(e))throw RangeError(`Coordinate values must be finite numbers`);return String(e)},ie=e=>({lat:re(e.lat),lon:re(e.lon)}),ae=(e,t=0)=>{let n=D(e.lat),r=D(e.lon),i=Math.sin(n),a=Math.cos(n),s=Math.sin(r),c=Math.cos(r),l=6378137/Math.sqrt(1-b*i*i),u=(l+t)*a*c,d=(l+t)*a*s,f=(l*(1-b)+t)*i;return o.fromValues(u,d,f)},oe=f((e,t)=>{if(t<1||t>23)throw RangeError(`levelOfDetail must be between 1 and 23 (inclusive)`);let n=E(e.lat,-85.05112878,85.05112878),r=te(e.lon),i=Math.sin(D(n)),a=256*2**t,o=(r+180)/360*a,s=(.5-Math.log((1+i)/(1-i))/(4*Math.PI))*a,c=2**t-1,l=E(Math.floor(o/256),0,c),u=E(Math.floor(s/256),0,c),d=``;for(let e=t;e>0;--e){let t=0,n=1<<e-1;(l&n)!==0&&(t+=1),(u&n)!==0&&(t+=2),d+=t.toString()}return d}),se=f(e=>{if(!/^[0-3]+$/.test(e))throw Error(`QuadKey must consist of digits 0-3`);let t=e.length;if(t===0)throw Error(`QuadKey must not be empty`);let n=0,r=0;for(let i=0;i<t;i+=1){let a=Number.parseInt(e[i],10),o=1<<t-i-1;a&1&&(n+=o),a&2&&(r+=o)}let i=256*2**t,a=(n+.5)*256,o=(r+.5)*256,s=a/i,c=o/i,l=s*360-180;return{lat:ne(Math.atan(Math.sinh(Math.PI*(1-2*c)))),lon:l}}),ce=f((e,t)=>{if(t<=0)throw RangeError(`precision must be greater than 0`);let n=-90,r=90,i=-180,a=180,o=``,s=0,c=0,l=!0;for(;o.length<t;){if(l){let t=(i+a)/2;e.lon>=t?(c|=T[s],i=t):a=t}else{let t=(n+r)/2;e.lat>=t?(c|=T[s],n=t):r=t}l=!l,s<4?s+=1:(o+=w[c],s=0,c=0)}return o}),le=f(e=>{if(e.length===0)throw Error(`geoHash must not be empty`);let t=-90,n=90,r=-180,i=180,a=!0;for(let o of e){let e=w.indexOf(o);if(e===-1)throw Error(`Invalid geohash character: ${o}`);for(let o of T){if(a){let t=(r+i)/2;(e&o)===0?i=t:r=t}else{let r=(t+n)/2;(e&o)===0?n=r:t=r}a=!a}}return{lat:(t+n)/2,lon:(r+i)/2}}),ue=f((e,t)=>{let n=D(e.lat),r=D(t.lat),i=r-n,a=D(t.lon-e.lon),o=Math.sin(i/2),s=Math.sin(a/2),c=o*o+Math.cos(n)*Math.cos(r)*s*s,l=Math.min(1,Math.max(0,c));return 6371008.8*(2*Math.atan2(Math.sqrt(l),Math.sqrt(1-l)))}),de=f((e,t)=>{let n=O(e,t);return Math.hypot(n[0],n[2])}),O=f((e,t,n=0,r=0)=>{let i=(t.lon-e.lon)*x*Math.cos(D(e.lat)),a=(t.lat-e.lat)*S,s=n-r;return o.fromValues(a,s,i)}),k=f((e,t)=>{let n=t[0],r=t[2];return{lat:n/S+e.lat,lon:r/(x*Math.cos(D(e.lat)))+e.lon,altitude:e.altitude==null?void 0:e.altitude+t[1]}}),fe=f((e,t=0)=>ae(e,t)),A=e=>{let{lat:t,lon:n}=ie(e);return`${t}%2C${n}`},pe=f(e=>`https://www.google.com/maps/search/?api=1&query=${A(e)}`),me=f((e,t)=>`https://www.google.com/maps/dir/?api=1&origin=${A(t)}&destination=${A(e)}`),he=f((e,t=19)=>{let{lat:n,lon:r}=ie(e);return`https://www.openstreetmap.org/query?lat=${n}&lon=${r}#map=${t}/${n}/${r}`}),ge=f((e,t,n)=>{let r=i.fromValues(...e),a=o.fromValues(t[0],t[1],t[2]),s=o.create();return o.transformMat4(s,a,r),k(n,s)}),_e=()=>({points:[]}),j=()=>({gpsMarkers:[],gpsLines:[],areas:[],heatMap:{},heatAreas:{}}),M=()=>({arPlanes:{},floorDetections:[],eventAreas:[],currentEventAreaId:null,currentEventAreaPoints:[]}),N=e=>o.fromValues(e[0],e[1],e[2]),P=e=>a.fromValues(e[0],e[1],e[2],e[3]),ve=e=>i.fromValues(...e),F=e=>[e[0],e[1],e[2]],I=e=>[e[0],e[1],e[2],e[3]],L=e=>[e[0],e[1],e[2],e[3],e[4],e[5],e[6],e[7],e[8],e[9],e[10],e[11],e[12],e[13],e[14],e[15]],R=e=>[e[0],e[1],e[2]],ye=e=>[e[0],e[1],e[2],e[3]],be=e=>[...e],z=Object.freeze([0,0,0,1]),B=Object.freeze([0,0,0]),V=Object.freeze([1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]),H=e=>{let t=Se(e);if(t===0)return z;let n=1/t;return[e[0]*n,e[1]*n,e[2]*n,e[3]*n]},xe=e=>{for(let t=0;t<16;t++)if(e[t]!==V[t])return!1;return!0},Se=e=>Math.sqrt(e[0]*e[0]+e[1]*e[1]+e[2]*e[2]+e[3]*e[3]),Ce=e=>e[0]===0&&e[1]===0&&e[2]===0&&e[3]===1,we=(e,t)=>Math.abs(e[0])<t&&Math.abs(e[1])<t&&Math.abs(e[2])<t&&Math.abs(e[3]-1)<t,Te=(e,t,n=1e-10)=>{let r=Math.abs(e[0]-t[0])<n&&Math.abs(e[1]-t[1])<n&&Math.abs(e[2]-t[2])<n&&Math.abs(e[3]-t[3])<n,i=Math.abs(e[0]+t[0])<n&&Math.abs(e[1]+t[1])<n&&Math.abs(e[2]+t[2])<n&&Math.abs(e[3]+t[3])<n;return r||i},Ee=(e,t)=>{let n=a.create();return a.multiply(n,e,t),[n[0],n[1],n[2],n[3]]},De=e=>{let t=a.create();return a.invert(t,e),[t[0],t[1],t[2],t[3]]},U=e=>[-e[2],e[1],e[0]],Oe=e=>[e[2],e[1],-e[0]],W=e=>[-e[2],e[1],e[0],e[3]],ke=e=>[e[2],e[1],-e[0],e[3]],G=e=>[e[1],e[2],e[0],e[3]],Ae=e=>[e[2],e[0],e[1],e[3]],K=(e,t,n)=>{if(!Number.isFinite(e)||!Number.isFinite(t)||!Number.isFinite(n))throw Error(`eulerToQuaternion: non-finite input (alpha=${e}, beta=${t}, gamma=${n})`);let r=e*Math.PI/180,i=t*Math.PI/180,o=n*Math.PI/180,s=a.create(),c=a.create(),l=a.create();a.setAxisAngle(s,[0,0,1],r),a.setAxisAngle(c,[1,0,0],i),a.setAxisAngle(l,[0,1,0],o);let u=a.create();return a.multiply(u,s,c),a.multiply(u,u,l),[u[0],u[1],u[2],u[3]]},je=t({name:`arElements`,initialState:M(),reducers:{recordPhoneHeight:(e,t)=>{e.floorDetections.push(t.payload)},addOrUpdateArPlane:(e,t)=>{let n=t.payload;if(n.parentPlaneId){delete e.arPlanes[n.planeId];return}e.arPlanes[n.planeId]=c(n)},startEventArea:(e,t)=>{e.currentEventAreaId=t.payload.id,e.currentEventAreaPoints=[]},addCornerToEventArea:(e,t)=>{e.currentEventAreaId&&e.currentEventAreaPoints.push(c(R(t.payload.position)))},correctArDriftForEventArea:(e,t)=>{let n=t.payload.drift;for(let t of e.currentEventAreaPoints)t[0]+=n[0],t[1]+=n[1],t[2]+=n[2]},finishEventArea:(e,t)=>{e.currentEventAreaId&&(e.eventAreas.push({id:e.currentEventAreaId,label:t.payload.label,polygon:[],isInArSpace:!0,arPoints:e.currentEventAreaPoints}),e.currentEventAreaId=null,e.currentEventAreaPoints=[])},anchorEventAreaInWorldSpace:(e,t)=>{let{areaId:n,alignmentMatrix:r,zeroRef:i}=t.payload,a=e.eventAreas.findIndex(e=>e.id===n);if(a===-1)return;let s=e.eventAreas[a];if(!s.isInArSpace||!s.arPoints)throw Error(`The area ${s.id} is already anchored in world space`);let c=ve(r),l=s.arPoints.map(e=>{let t=N(e);return k(i,o.transformMat4(o.create(),t,c))});e.eventAreas[a]={...s,isInArSpace:!1,polygon:l,arPoints:void 0}},resetArElements:()=>M()}}),{recordPhoneHeight:Me,addOrUpdateArPlane:Ne,startEventArea:Pe,addCornerToEventArea:Fe,correctArDriftForEventArea:Ie,finishEventArea:Le,anchorEventAreaInWorldSpace:Re,resetArElements:ze}=je.actions,Be=p(Me),Ve=p(Ne),He=p(Pe),Ue=p(Fe),We=p(Ie),Ge=p(Le),Ke=p(Re),qe=p(ze),Je=m(je.reducer);function Ye(e,t,n,r){let{inCentroid:i,refCentroid:a,refVec:s,tmp:c}=r;o.set(i,0,0,0),o.set(a,0,0,0);let l=0;for(let n=0;n<e.length;n++){let r=t[n][3];o.scaleAndAdd(i,i,e[n],r),q(s,t[n]),o.scaleAndAdd(a,a,s,r),l+=r}if(l===0)throw Error(`Weights must not sum to zero when solving Kabsch`);let u=1/l;if(o.scale(i,i,u),o.scale(a,a,u),!n)return{inCentroid:i,refCentroid:a,scaleRatio:1};let d=0,f=0;for(let n=0;n<e.length;n++)o.subtract(c,e[n],i),d+=o.length(c),q(s,t[n]),o.subtract(c,s,a),f+=o.length(c);return{inCentroid:i,refCentroid:a,scaleRatio:d===0?1:f/d}}function Xe(e,t,n,r,a,s,c){let{negCentroid:l,scaleVec:u}=c;return i.fromRotationTranslation(e,r,n),s&&(o.set(u,a,a,a),i.scale(e,e,u)),o.negate(l,t),i.translate(e,e,l),e}function Ze(e,t,n={}){let r=e.length;if(r!==t.length)throw Error(`Length of the point lists was not equal: ${r} vs ${t.length}`);if(r===0)return i.create();let s=n.solveRotation??!0,c=n.solveScale??!1,l=n.ignoreYAxisForRotation??!1,u=n.rotationIterations??9,{inCentroid:d,refCentroid:f,scaleRatio:p}=Ye(e,t,c,{inCentroid:o.create(),refCentroid:o.create(),refVec:o.create(),tmp:o.create()}),m=a.create();s?new Qe(l).solve(e,t,d,f,m,u):a.identity(m);let h={negCentroid:o.create(),scaleVec:o.create()};return Xe(i.create(),d,f,m,p,c,h)}var Qe=class e{static twoPi=Math.PI*2;static basisX=o.fromValues(1,0,0);static basisY=o.fromValues(0,1,0);static basisZ=o.fromValues(0,0,1);constructor(e){this.ignoreYAxis=e}solve(t,n,r,i,o,s){let c=this.transposeMultSubtract(t,n,r,i);if(this.ignoreYAxis){let t=c[0][0],n=c[0][2],r=c[2][0],i=c[2][2],s=Math.atan2(r-n,t+i);a.setAxisAngle(o,e.basisY,s)}else this.extractRotation(c,o,s)}transposeMultSubtract(e,t,n,r){let i=[o.create(),o.create(),o.create()],a=o.clone(n),s=o.clone(r);this.ignoreYAxis&&(a[1]=0,s[1]=0);let c=o.create(),l=o.create(),u=o.create();for(let n=0;n<e.length;n++){o.copy(c,e[n]),q(u,t[n]),this.ignoreYAxis&&(c[1]=0,u[1]=0);let r=t[n][3];o.subtract(c,c,a),o.scale(c,c,r),o.subtract(l,u,s),i[0][0]+=c[0]*l[0],i[1][0]+=c[1]*l[0],i[2][0]+=c[2]*l[0],i[0][1]+=c[0]*l[1],i[1][1]+=c[1]*l[1],i[2][1]+=c[2]*l[1],i[0][2]+=c[0]*l[2],i[1][2]+=c[1]*l[2],i[2][2]+=c[2]*l[2]}return i}extractRotation(t,n,r){let i=[o.create(),o.create(),o.create()],s=o.create(),c=o.create(),l=o.create();for(let u=0;u<r;u++){this.fillMatrixFromQuaternion(n,i),o.cross(s,i[0],t[0]),o.cross(l,i[1],t[1]),o.add(s,s,l),o.cross(l,i[2],t[2]),o.add(s,s,l);let r=o.dot(i[0],t[0])+o.dot(i[1],t[1])+o.dot(i[2],t[2])+1e-9;r=Math.abs(r),o.scale(s,s,1/r);let u=o.length(s);if(u<1e-9)break;o.scale(c,s,1/u);let d=u%e.twoPi,f=a.create();a.setAxisAngle(f,c,d),a.multiply(n,f,n),a.normalize(n,n)}}fillMatrixFromQuaternion(t,n){o.transformQuat(n[0],e.basisX,t),o.transformQuat(n[1],e.basisY,t),o.transformQuat(n[2],e.basisZ,t)}};const q=(e,t)=>(e[0]=t[0],e[1]=t[1],e[2]=t[2],e);function $e(e,t,n,r,i,a,o=Math.random){let s=Array.from(e);if(n>s.length)throw RangeError(`minSampleSize must be smaller than the number of elements: minSampleSize=${n}, elements=${s.length}`);return nt(s,et(s,t,n,r,i,a,o),i,a)}function et(e,t,n,r,i,a,o){let s=null,c=Array(n),l=new Uint8Array(e.length);for(let u=0;u<r;u+=1){it(e,n,o,c,l);let{inliers:r,outliers:u}=tt(e,l,i(c),a);if(s===null||n+r.length>=t){let e=[...c,...r],t=i(e),n=rt(t,`createModel must assign totalModelError for comparison during RANSAC`);(s===null||n<s.error)&&(t.inliers=e,t.outliers=u,s={model:t,error:n})}}return s?.model??null}function tt(e,t,n,r){let i=[],a=[];for(let o=0;o<e.length;o+=1){if(t?.[o])continue;let s=e[o];(r(n,s)?i:a).push(s)}return{inliers:i,outliers:a}}function nt(e,t,n,r){let i=t??n(e);rt(i,`RANSAC failed to produce a model with totalModelError`);let{inliers:a,outliers:o}=tt(e,null,i,r);return a.length>0&&(i=n(a),rt(i,`createModel must assign totalModelError for final RANSAC model`)),i.inliers=a,i.outliers=o,i}function rt(e,t){let{totalModelError:n}=e;if(n==null)throw Error(t);return n}function it(e,t,n,r,i){i.fill(0);let a=0;for(;a<t;){let t=Math.floor(n()*e.length);i[t]||(i[t]=1,r[a]=e[t],a+=1)}}const at={useOnlyRecentData:!1,recentSeconds:180,includeTimeWeight:!0,weightByTimeFactor:1321,ignoreYAxisForRotation:!0,useRansac:!1,ransacInlierRatio:.95,ransacSampleRatio:.3,ransacMaxIterations:230,ransacErrorTolerance:1,geohashPrecision:8,geohashWindowSize:10,gpsAccuracyExponent:.5},ot=e=>e.map(e=>R(e)),st=e=>e.map(e=>H(e)),ct=e=>e.map(Q),lt=e=>e.map(e=>[e[0],e[1],e[2],e[3]]),J=e=>Array.isArray(e)?e:F(e),Y=e=>Array.isArray(e)?e:I(e),ut=(e,t,n,r)=>({odometryPosOffset:R(J(e)),odometryRotOffset:H(Y(t)),latestLoopClosureFixPointPos:n?R(J(n)):null,latestLoopClosureFixPointRot:r?H(Y(r)):null}),dt=(e,t,n,r)=>({odometryPositions:[],odometryRotations:[],gpsPositions:[],gpsPositionsVec4:[],alignmentMatrix:[...V],alignmentRotation:[...z],alignmentTranslation:[...B],alignmentRotationInDegree:[...B],...ut(e,t,n,r),gpsAccuracyMedian:null,gpsAccuracyMean:null,currentGpsPosGeoHash:null}),ft=()=>dt(B,z,null,null),pt=(e,t,n,r)=>{let i=e.map(e=>J(e)),a=t.map(e=>H(Y(e))),o=n.map(Q),s=r.gpsAccuracyExponent;for(let e of o)e.weight=ht(e.latLongAccuracy,s);let c=o.map(Ot),l=r.includeTimeWeight?wt(c,o,r.weightByTimeFactor):c,u=r.useOnlyRecentData?Ct(o,r.recentSeconds):0;return{allPositionTuples:i,allRotationTuples:a,allGpsPoints:o,allVec4Tuples:l,solverPositionsTyped:i.slice(u).map(N),solverGpsPoints:o.slice(u),solverWeightedVec4Tuples:l.slice(u)}},mt=e=>{let{odometryPositions:t,odometryRotations:n,gpsPoints:r,odometryPosOffset:i=B,odometryRotOffset:s=z,latestLoopClosureFixPointPos:c=null,latestLoopClosureFixPointRot:l=null,alignmentConfig:u,random:d}=e;if(t.length!==n.length||t.length!==r.length)throw Error(`GpsEvents requires odometry positions, rotations, and gps points to have identical lengths`);let f={...at,...u??{}};if(t.length===0)return dt(i,s,c,l);let{allPositionTuples:p,allRotationTuples:m,allGpsPoints:h,allVec4Tuples:g,solverPositionsTyped:_,solverGpsPoints:v,solverWeightedVec4Tuples:y}=pt(t,n,r,f),b=yt(_,y,f,d??Math.random),x=a.create(),S=o.create(),C=o.create();_t(b.matrix,x,S,C);let{mean:ee,median:w}=Et(h),T=Tt(r,f.geohashPrecision,f.geohashWindowSize);return{odometryPositions:ot(p),odometryRotations:st(m),gpsPositions:ct(h),gpsPositionsVec4:lt(g),alignmentMatrix:L(b.matrix),alignmentRotation:I(x),alignmentTranslation:F(S),alignmentRotationInDegree:F(C),...ut(i,s,c,l),gpsAccuracyMedian:w,gpsAccuracyMean:ee,currentGpsPosGeoHash:T}},ht=(e,t)=>e!=null&&e>0?1/Math.max(e,1)**+t:1,gt=(e,t,n,r,i,s)=>{let c=e.odometryPositions,l=e.odometryRotations,u=e.gpsPositions,d=e.gpsPositionsVec4;c.push(J(t)),l.push(H(Y(n)));let f=Q(r);u.push(f);let p={...at,...i??{}};f.weight=ht(f.latLongAccuracy,p.gpsAccuracyExponent);let m=Ot(f);if(p.includeTimeWeight){d.push(m);let e=wt(d,u,p.weightByTimeFactor);for(let t=0;t<e.length;t++)d[t]=e[t]}else d.push(m);let h=0;p.useOnlyRecentData&&(h=Ct(u,p.recentSeconds));let g=yt(c.slice(h).map(N),d.slice(h),p,s??Math.random);e.alignmentMatrix=L(g.matrix);let _=a.create(),v=o.create(),y=o.create();_t(g.matrix,_,v,y),e.alignmentRotation=I(_),e.alignmentTranslation=F(v),e.alignmentRotationInDegree=F(y);let{mean:b,median:x}=Et(u);e.gpsAccuracyMean=b,e.gpsAccuracyMedian=x,e.currentGpsPosGeoHash=Tt(u,p.geohashPrecision,p.geohashWindowSize)},_t=(e,t,n,r)=>{i.getRotation(t,e),a.normalize(t,t),i.getTranslation(n,e),Dt(t,r)},X=o.create(),vt=o.create(),yt=(e,t,n,r)=>{if(e.length===0)return{matrix:i.create(),meanError:0};let a=e.length,o=[];for(let n=0;n<a;n+=1){let r=t[n];o.push({odom:e[n],ref:s.fromValues(r[0],r[1],r[2],r[3])})}if(n.useRansac&&o.length>=3){let e=o.length,t=Math.max(3,Math.min(e,Math.ceil(e*n.ransacSampleRatio))),i=$e(o,Math.max(t,Math.min(e,Math.ceil(e*n.ransacInlierRatio))),t,n.ransacMaxIterations,e=>bt(e,n.ignoreYAxisForRotation),(e,t)=>xt(e,t)<e.meanAlignmentError+n.ransacErrorTolerance,r);return{matrix:i.alignmentMatrix,meanError:i.meanAlignmentError}}let c=bt(o,n.ignoreYAxisForRotation);return{matrix:c.alignmentMatrix,meanError:c.meanAlignmentError}},bt=(e,t)=>{let n=Array.from(e),r=n.length,i=Array(r),a=Array(r);for(let e=0;e<r;e+=1)i[e]=n[e].odom,a[e]=n[e].ref;let s=Ze(i,a,{ignoreYAxisForRotation:t}),c=0;for(let e=0;e<r;e+=1)o.transformMat4(X,i[e],s),c+=St(X,n[e].ref);return{alignmentMatrix:s,meanAlignmentError:r===0?0:c/r,totalModelError:c}},xt=(e,t)=>(o.transformMat4(X,t.odom,e.alignmentMatrix),St(X,t.ref)),St=(e,t)=>(o.set(vt,t[0],t[1],t[2]),o.squaredDistance(e,vt)),Ct=(e,t)=>{if(e.length===0||t<=0)return 0;let n=e[e.length-1].timestamp-t*1e3;for(let t=0;t<e.length;t+=1)if(e[t].timestamp>=n)return t;return 0},wt=(e,t,n)=>{if(e.length<2)return e.map(e=>[e[0],e[1],e[2],e[3]]);let r=t[t.length-1].timestamp,i=t[0].timestamp,a=Math.max(1,r-i);return e.map((e,i)=>{let o=n*((r-t[i].timestamp)/a)+1,s=t[i].weight,c=1/(1/(s>0&&Number.isFinite(s)?s:1)+o);return[e[0],e[1],e[2],c]})},Z=new Map,Tt=(e,t,n)=>{if(e.length===0)return null;let r=Math.max(0,e.length-n);Z.clear();for(let n=r;n<e.length;n+=1){let r=e[n],i=ce(kt(r),t),a=Z.get(i);a?(a.count+=1,a.index=n):Z.set(i,{count:1,index:n})}let i=null,a=-1,o=-1;for(let[e,{count:t,index:n}]of Z)(t>a||t===a&&n>o)&&(i=e,a=t,o=n);return i},Et=e=>{let t=e.map(e=>e.latLongAccuracy).filter(e=>typeof e==`number`&&Number.isFinite(e));if(t.length===0)return{mean:null,median:null};let n=t.reduce((e,t)=>e+t,0)/t.length,r=t.slice().sort((e,t)=>e-t),i=Math.floor(r.length/2);return{mean:n,median:r.length%2==0?(r[i-1]+r[i])/2:r[i]}},Dt=(e,t)=>{let n=e[0],r=e[1],i=e[2],a=e[3],s=2*(a*n+r*i),c=1-2*(n*n+r*r),l=Math.atan2(s,c),u=2*(a*r-i*n),d;d=Math.abs(u)>=1?Math.PI/2*Math.sign(u):Math.asin(u);let f=2*(a*i+n*r),p=1-2*(r*r+i*i),m=Math.atan2(f,p),h=180/Math.PI,g=t??o.create();return g[0]=l*h,g[1]=d*h,g[2]=m*h,g},Ot=e=>[e.coordinates[0],e.coordinates[1],e.coordinates[2],e.weight>0&&Number.isFinite(e.weight)?e.weight:1],kt=e=>({lat:e.latitude,lon:e.longitude}),Q=e=>({...e,zeroRef:{lat:e.zeroRef.lat,lon:e.zeroRef.lon},coordinates:[e.coordinates[0],e.coordinates[1],e.coordinates[2]],timestamp:e.timestamp,deviceRotation:e.deviceRotation?H(Y(e.deviceRotation)):void 0}),At=({lat:e,lon:t})=>{if(!Number.isFinite(e))throw Error(`Invalid latitude value: ${e}`);if(!Number.isFinite(t))throw Error(`Invalid longitude value: ${t}`);if(e<-90||e>90)throw Error(`Invalid latitude range: ${e}`);if(t<-180||t>180)throw Error(`Invalid longitude range: ${t}`)},jt=o.fromValues(1,1,1),Mt=(e,t)=>[e[0]+t[0],e[1]+t[1],e[2]+t[2]],Nt=(e,t,n)=>{let r=O(t,{lat:e.latitude,lon:e.longitude},e.altitude??0,0),i=n?.alpha!=null&&n?.beta!=null&&n?.gamma!=null?G(K(n.alpha,n.beta,n.gamma)):void 0;return{...e,zeroRef:t,coordinates:F(r),weight:1,deviceRotation:i}},Pt=(e,t,n)=>{if(e)return K(e.alpha,e.beta,e.gamma);if(t)return t;throw Error(`Sensor rotation missing for ${n}: neither raw orientation nor legacy quaternion provided`)},Ft=e=>{let t=a.invert(a.create(),e);if(!t)throw Error(`Encountered non-invertible quaternion`);return t},It=e=>{let t=Ft(a.normalize(a.create(),P(e.lastSensorRot))),n=a.normalize(a.create(),P(e.lastValidOdomRot)),r=a.multiply(a.create(),t,n),i=a.normalize(a.create(),P(e.newSensorRot)),o=a.multiply(a.create(),i,r),s=a.normalize(a.create(),P(e.newOdomRot)),c=a.multiply(a.create(),o,s);return a.normalize(c,c),I(c)},Lt=(e,t)=>{let n=i.create();return i.fromRotationTranslationScale(n,a.normalize(a.create(),P(t)),N(e),jt),n},Rt=(e,t,n)=>{let r=o.create(),s=o.create(),c=a.create(),l=a.create();i.getTranslation(r,e),i.getTranslation(s,t),i.getRotation(c,e),i.getRotation(l,t);let u=o.create();o.lerp(u,r,s,n);let d=a.create();return a.slerp(d,c,l,n),i.fromRotationTranslation(i.create(),d,u)},zt=(e,t)=>{if(e.length===0)return[];let n=e[e.length-1],r=i.invert(i.create(),n);if(!r)throw Error(`End pose matrix is not invertible`);let a=i.multiply(i.create(),t,r),o=i.create(),s=e.length-1;return e.map((e,t)=>{let n=Rt(o,a,s<=0?1:t/s);return i.multiply(i.create(),n,e)})},Bt=(e,t,n)=>{for(let r=e.length-1;r>=0;--r)if(n(e[r],t))return r;return-1},Vt=(e,t)=>e[0]===t[0]&&e[1]===t[1]&&e[2]===t[2],Ht=(e,t)=>e[0]===t[0]&&e[1]===t[1]&&e[2]===t[2]&&e[3]===t[3],Ut=t({name:`gpsData`,initialState:null,reducers:{setZeroPos:{reducer:(e,t)=>e===null?{zero:t.payload,gpsEvents:ft(),odometryPath:_e(),referencePoints:[]}:e,prepare:e=>(At(e),{payload:e})},recordGpsEvent:(e,t)=>{if(!e)return e;let{odomPosition:n,odomRotation:r,rawGpsPoint:i,rawDeviceOrientation:a}=t.payload,o=Nt(i,e.zero,a);gt(e.gpsEvents,U(n),W(r),o)},odometryTrackingRestarted:(e,t)=>{if(!e)return e;let n=Mt(e.gpsEvents.odometryPosOffset,U(t.payload.lastValidOdomPos)),r=Pt(t.payload.lastSensorOrientation,t.payload.lastSensorRot,`lastSensor`),i=Pt(t.payload.newSensorOrientation,t.payload.newSensorRot,`newSensor`);e.gpsEvents=c(mt({odometryPositions:[],odometryRotations:[],gpsPoints:[],odometryPosOffset:n,odometryRotOffset:It({...t.payload,lastValidOdomRot:W(t.payload.lastValidOdomRot),newOdomRot:W(t.payload.newOdomRot),lastSensorRot:G(r),newSensorRot:G(i)}),latestLoopClosureFixPointPos:null,latestLoopClosureFixPointRot:null}))},arLoopClosureDetected:(e,t)=>{if(!e)return e;let n=e.gpsEvents;if(n.odometryPositions.length===0)return e;let r=n.latestLoopClosureFixPointPos?Bt(n.odometryPositions,n.latestLoopClosureFixPointPos,Vt):0,s=n.latestLoopClosureFixPointRot?Bt(n.odometryRotations,n.latestLoopClosureFixPointRot,Ht):0;if(r<0||s<0)throw Error(`Loop closure fix point could not be located in history`);if(r!==s)throw Error(`Loop closure fix point indices for position and rotation diverged`);let l=n.odometryPositions.slice(0,r).map(e=>R(e)),u=n.odometryRotations.slice(0,r).map(e=>H(e)),d=n.odometryPositions.slice(r).map(e=>R(e)),f=n.odometryRotations.slice(r).map(e=>H(e)),p=d.map((e,t)=>Lt(e,f[t])),m=Lt(U(t.payload.lastPos),H(W(t.payload.lastRot)));p.push(m);let h=zt(p,Lt(U(t.payload.newPos),H(W(t.payload.newRot)))),g=[],_=[],v=o.create();for(let e=0;e<h.length-1;e+=1){let t=h[e],n=o.create();i.getTranslation(n,t);let r=a.create();if(i.getRotation(r,t),a.normalize(r,r),i.getScaling(v,t),Math.hypot(v[0]-1,v[1]-1,v[2]-1)>.05)throw Error(`Unexpected scale drift encountered during loop closure correction`);g.push(F(n)),_.push(I(r))}e.gpsEvents=c(mt({odometryPositions:l.concat(g),odometryRotations:u.concat(_),gpsPoints:n.gpsPositions,odometryPosOffset:n.odometryPosOffset,odometryRotOffset:n.odometryRotOffset,latestLoopClosureFixPointPos:null,latestLoopClosureFixPointRot:null}))},add2dImage:(e,t)=>{if(!e)return e;e.odometryPath.points.push(c({imageFile:t.payload.imageFile,screenRotation:t.payload.screenRotation,position:U(t.payload.position),rotation:H(W(t.payload.rotation)),capturedAt:t.payload.capturedAt}))},markReferencePoint:(e,t)=>{if(!e)return e;let{id:n,position:r,rotation:i,rawGpsPoint:a,timestamp:o}=t.payload,s=Nt(a,e.zero);e.referencePoints.push(c({id:n,position:U(r),rotation:H(W(i)),gpsPoint:Q(s),timestamp:o??Date.now()}))}}}),{setZeroPos:Wt,recordGpsEvent:Gt,odometryTrackingRestarted:Kt,arLoopClosureDetected:qt,add2dImage:Jt,markReferencePoint:Yt}=Ut.actions,Xt=p(Wt),Zt=p(Gt),Qt=p(Kt),$t=p(qt),en=p(Jt),tn=p(Yt),nn=m(Ut.reducer),rn=t({name:`gpsElements`,initialState:j(),reducers:{addMarker:(e,t)=>{e.gpsMarkers.push(t.payload)},addLine:(e,t)=>{e.gpsLines.push(t.payload)},addArea:(e,t)=>{e.areas.push(t.payload)},addToHeatMaps:(e,t)=>{let{category:n,tiles:r}=t.payload,i=e.heatMap[n]??{};e.heatMap[n]={...i,...r}},addHeatMapArea:(e,t)=>{let n=t.payload,r=n.category;if(e.heatAreas[r]||(e.heatAreas[r]={}),e.heatAreas[r][n.geoHash])throw Error(`HeatArea collision detected for GeoHash: ${n.geoHash}`);e.heatAreas[r][n.geoHash]=n},resetGpsElements:()=>j()}}),{addMarker:an,addLine:on,addArea:sn,addToHeatMaps:cn,addHeatMapArea:ln,resetGpsElements:un}=rn.actions,dn=p(an),fn=p(on),pn=p(sn),mn=p(cn),hn=p(ln),gn=p(un),_n=m(rn.reducer),vn=()=>({gpsData:null,gpsElements:j(),arElements:M()}),yn=e=>{let t=Array.from({length:Math.min(e.length,4)},(t,n)=>e[n].toFixed(2));e.length>4&&t.push(`…`);let n=`[${t.join(`, `)}]`;if(e.length===4){let{pitch:t,yaw:r,roll:i}=v(e);return`${n} (pitch=${t.toFixed(0)}°, yaw=${r.toFixed(0)}°, roll=${i.toFixed(0)}°)`}return n},bn=e=>(e.length===3||e.length===4||e.length===16)&&e.every(e=>typeof e==`number`),$=(e,t=0)=>{if(t>10||e==null)return e;if(Array.isArray(e))return bn(e)?yn(e):e.map(e=>$(e,t+1));if(typeof e==`object`){let n={};for(let[r,i]of Object.entries(e))n[r]=$(i,t+1);return n}return e},xn=(t={})=>{let{preloadedState:n,enableDevToolsSanitizers:r=!0,enableDevChecks:i=!0,licenseKey:a}=t;if(a==null)throw Error(`License key required. Pass options.licenseKey or use createRecorderStore() from gps-plus-slam-app-framework. See EULA §3.`);g(a);let o=vn(),s=n?{gpsData:n.gpsData??o.gpsData,gpsElements:n.gpsElements??o.gpsElements,arElements:n.arElements??o.arElements}:void 0;return e({reducer:{gpsData:nn,gpsElements:_n,arElements:Je},preloadedState:s,middleware:e=>e({serializableCheck:i,immutableCheck:i}),devTools:r?{actionSanitizer:$,stateSanitizer:$}:!0})},Sn=[],Cn=[],wn=[],Tn=[],En={median:null,mean:null},Dn=f(function(e){let t=e.gpsData?.gpsEvents;return!t||t.gpsPositions.length===0?null:t.alignmentMatrix}),On=f(function(e){let t=e.gpsData?.gpsEvents;return!t||t.gpsPositions.length===0?null:t.alignmentRotation}),kn=f(function(e){return e.gpsData?.gpsEvents?.odometryPositions??Sn}),An=f(function(e){return e.gpsData?.gpsEvents?.odometryRotations??Cn}),jn=f(function(e){return e.gpsData?.gpsEvents?.gpsPositions??wn}),Mn=f(function(e){return e.gpsData?.referencePoints??Tn}),Nn=f(function(e){return e.gpsData?.zero??null}),Pn=f(function(e){let t=e.gpsData?.gpsEvents?.gpsAccuracyMedian??null,n=e.gpsData?.gpsEvents?.gpsAccuracyMean??null;return t===null&&n===null?En:{median:t,mean:n}}),Fn=`1.0.0`;export{V as IDENTITY_MATRIX4,z as IDENTITY_QUATERNION,Fn as LIB_VERSION,B as ZERO_VECTOR3,en as add2dImage,pn as addArea,Ue as addCornerToEventArea,hn as addHeatMapArea,fn as addLine,dn as addMarker,Ve as addOrUpdateArPlane,mn as addToHeatMaps,Ke as anchorEventAreaInWorldSpace,Je as arElementsReducer,$t as arLoopClosureDetected,ce as calcGeoHash,k as calcGpsCoords,oe as calcQuadKey,O as calcRelativeCoordsInMeters,be as cloneMatrix4,ye as cloneQuaternion,R as cloneVector3,We as correctArDriftForEventArea,xn as createGpsSlamStore,ue as distanceInMeters,de as distanceInMetersRelative,G as enuQuaternionToNUE,K as eulerToQuaternion,Ge as finishEventArea,ve as fromMatrix4,P as fromQuaternion,N as fromVector3,ge as fusedGpsFromOdom,le as geoHashToLatLong,Dn as getAlignmentMatrix,On as getAlignmentRotation,me as getGoogleMapsDirectionsLink,pe as getGoogleMapsLink,Pn as getGpsAccuracyStats,jn as getGpsPositions,kn as getOdometryPositions,An as getOdometryRotations,he as getOpenStreetMapLink,Mn as getReferencePoints,Nn as getZeroReference,nn as gpsDataReducer,_n as gpsElementsReducer,De as invertQuaternion,xe as isIdentityMatrix4,Ce as isIdentityQuaternion,we as isNearIdentityQuaternion,tn as markReferencePoint,Ee as multiplyQuaternions,H as normalizeQuaternion,Ae as nueQuaternionToENU,ke as nueQuaternionToWebXR,Oe as nueToWebXR,Qt as odometryTrackingRestarted,se as quadKeyToLatLong,Se as quaternionMagnitude,Te as quaternionsEquivalent,Zt as recordGpsEvent,Be as recordPhoneHeight,qe as resetArElements,gn as resetGpsElements,$ as sanitizeForDevTools,Xt as setZeroPos,He as startEventArea,fe as toEarthCenteredCoordinates,L as toMatrix4,I as toQuaternion,F as toVector3,g as validateLicenseKey,W as webxrQuaternionToNUE,U as webxrToNUE};
2
+ import{configureStore as e,createSlice as t}from"@reduxjs/toolkit";import{ed25519 as n}from"@noble/curves/ed25519.js";import{hexToBytes as r}from"@noble/curves/utils.js";import{mat4 as i,quat as a,vec3 as o,vec4 as s}from"gl-matrix";import{castDraft as c}from"immer";let l=!1;function u(){l=!0}function d(){if(!l)throw Error(`gps-plus-slam-js: license not activated. Construct your store via createRecorderStore() from gps-plus-slam-app-framework, or pass a valid licenseKey to createGpsSlamStore(). See EULA §3.`)}function f(e){return((...t)=>(d(),e(...t)))}function p(e){let t=((...t)=>(d(),e(...t)));return Object.assign(t,e),t.toString=()=>e.type,t}function m(e){return(t,n)=>(d(),e(t,n))}function h(e){let t=e.replace(/-/g,`+`).replace(/_/g,`/`),n=`=`.repeat((4-t.length%4)%4),r=atob(t+n);return Uint8Array.from(r,e=>e.charCodeAt(0))}function g(e){let t=new Date,i=e.indexOf(`.`);if(i<=0||i>=e.length-1)throw Error(`Invalid license key format: expected "payload.signature" with base64url-encoded parts.`);let a,o;try{a=h(e.substring(0,i)),o=h(e.substring(i+1))}catch{throw Error(`Invalid license key format: base64url decoding failed.`)}let s=r(`dfe5c62120b7a0ce962b17907a15b27cf1b056e6619bf6ce55e6e094b322a470`);if(!n.verify(o,a,s))throw Error(`Invalid license key: signature verification failed.`);let c=JSON.parse(new TextDecoder().decode(a));if(typeof c!=`object`||!c)throw Error(`Invalid license key: payload must be a JSON object.`);let{type:l,exp:d}=c;if(l!==`community`)throw Error(`Invalid license key: "type" must be "community".`);if(typeof d!=`number`||!Number.isFinite(d))throw Error(`Invalid license key: "exp" must be a finite number (Unix timestamp in seconds).`);if(d<Math.floor(t.getTime()/1e3)){let e=new Date(d*1e3);throw Error(`License key expired on ${e.toISOString()}. Update to the latest version for a renewed community key.`)}return u(),{type:l,expiresAt:new Date(d*1e3)}}const _=180/Math.PI,v=e=>{let t=e[0],n=e[1],r=e[2],i=e[3],a=2*(i*t+n*r),o=1-2*(t*t+n*n),s=Math.atan2(a,o)*_,c=2*(i*n-r*t),l=Math.abs(c)>=1?Math.sign(c)*90:Math.asin(c)*_,u=2*(i*r+t*n),d=1-2*(n*n+r*r);return{pitch:l,yaw:Math.atan2(u,d)*_,roll:s}},y=1/298.257223563,b=y*(2-y),x=40075016.6856/360,S=39940652.7422/360,C=Math.PI/180,ee=180/Math.PI,w=`0123456789bcdefghjkmnpqrstuvwxyz`,T=[16,8,4,2,1],E=(e,t,n)=>Math.min(Math.max(e,t),n),te=e=>E(e,-180,180),D=e=>e*C,ne=e=>e*ee,re=e=>{if(!Number.isFinite(e))throw RangeError(`Coordinate values must be finite numbers`);return String(e)},ie=e=>({lat:re(e.lat),lon:re(e.lon)}),ae=(e,t=0)=>{let n=D(e.lat),r=D(e.lon),i=Math.sin(n),a=Math.cos(n),s=Math.sin(r),c=Math.cos(r),l=6378137/Math.sqrt(1-b*i*i),u=(l+t)*a*c,d=(l+t)*a*s,f=(l*(1-b)+t)*i;return o.fromValues(u,d,f)},oe=f((e,t)=>{if(t<1||t>23)throw RangeError(`levelOfDetail must be between 1 and 23 (inclusive)`);let n=E(e.lat,-85.05112878,85.05112878),r=te(e.lon),i=Math.sin(D(n)),a=256*2**t,o=(r+180)/360*a,s=(.5-Math.log((1+i)/(1-i))/(4*Math.PI))*a,c=2**t-1,l=E(Math.floor(o/256),0,c),u=E(Math.floor(s/256),0,c),d=``;for(let e=t;e>0;--e){let t=0,n=1<<e-1;(l&n)!==0&&(t+=1),(u&n)!==0&&(t+=2),d+=t.toString()}return d}),se=f(e=>{if(!/^[0-3]+$/.test(e))throw Error(`QuadKey must consist of digits 0-3`);let t=e.length;if(t===0)throw Error(`QuadKey must not be empty`);let n=0,r=0;for(let i=0;i<t;i+=1){let a=Number.parseInt(e[i],10),o=1<<t-i-1;a&1&&(n+=o),a&2&&(r+=o)}let i=256*2**t,a=(n+.5)*256,o=(r+.5)*256,s=a/i,c=o/i,l=s*360-180;return{lat:ne(Math.atan(Math.sinh(Math.PI*(1-2*c)))),lon:l}}),ce=f((e,t)=>{if(t<=0)throw RangeError(`precision must be greater than 0`);let n=-90,r=90,i=-180,a=180,o=``,s=0,c=0,l=!0;for(;o.length<t;){if(l){let t=(i+a)/2;e.lon>=t?(c|=T[s],i=t):a=t}else{let t=(n+r)/2;e.lat>=t?(c|=T[s],n=t):r=t}l=!l,s<4?s+=1:(o+=w[c],s=0,c=0)}return o}),le=f(e=>{if(e.length===0)throw Error(`geoHash must not be empty`);let t=-90,n=90,r=-180,i=180,a=!0;for(let o of e){let e=w.indexOf(o);if(e===-1)throw Error(`Invalid geohash character: ${o}`);for(let o of T){if(a){let t=(r+i)/2;(e&o)===0?i=t:r=t}else{let r=(t+n)/2;(e&o)===0?n=r:t=r}a=!a}}return{lat:(t+n)/2,lon:(r+i)/2}}),ue=f((e,t)=>{let n=D(e.lat),r=D(t.lat),i=r-n,a=D(t.lon-e.lon),o=Math.sin(i/2),s=Math.sin(a/2),c=o*o+Math.cos(n)*Math.cos(r)*s*s,l=Math.min(1,Math.max(0,c));return 6371008.8*(2*Math.atan2(Math.sqrt(l),Math.sqrt(1-l)))}),de=f((e,t)=>{let n=O(e,t);return Math.hypot(n[0],n[2])}),O=f((e,t,n=0,r=0)=>{let i=(t.lon-e.lon)*x*Math.cos(D(e.lat)),a=(t.lat-e.lat)*S,s=n-r;return o.fromValues(a,s,i)}),k=f((e,t)=>{let n=t[0],r=t[2];return{lat:n/S+e.lat,lon:r/(x*Math.cos(D(e.lat)))+e.lon}}),fe=f((e,t=0)=>ae(e,t)),A=e=>{let{lat:t,lon:n}=ie(e);return`${t}%2C${n}`},pe=f(e=>`https://www.google.com/maps/search/?api=1&query=${A(e)}`),me=f((e,t)=>`https://www.google.com/maps/dir/?api=1&origin=${A(t)}&destination=${A(e)}`),he=f((e,t=19)=>{let{lat:n,lon:r}=ie(e);return`https://www.openstreetmap.org/query?lat=${n}&lon=${r}#map=${t}/${n}/${r}`}),ge=f((e,t,n)=>{let r=i.fromValues(...e),a=o.fromValues(t[0],t[1],t[2]),s=o.create();o.transformMat4(s,a,r);let{lat:c,lon:l}=k(n,s);return{lat:c,lon:l,altitude:s[1]}}),_e=()=>({points:[]}),j=()=>({gpsMarkers:[],gpsLines:[],areas:[],heatMap:{},heatAreas:{}}),M=()=>({arPlanes:{},floorDetections:[],eventAreas:[],currentEventAreaId:null,currentEventAreaPoints:[]}),N=e=>o.fromValues(e[0],e[1],e[2]),P=e=>a.fromValues(e[0],e[1],e[2],e[3]),ve=e=>i.fromValues(...e),F=e=>[e[0],e[1],e[2]],I=e=>[e[0],e[1],e[2],e[3]],L=e=>[e[0],e[1],e[2],e[3],e[4],e[5],e[6],e[7],e[8],e[9],e[10],e[11],e[12],e[13],e[14],e[15]],R=e=>[e[0],e[1],e[2]],ye=e=>[e[0],e[1],e[2],e[3]],be=e=>[...e],z=Object.freeze([0,0,0,1]),B=Object.freeze([0,0,0]),V=Object.freeze([1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]),H=e=>{let t=Se(e);if(t===0)return z;let n=1/t;return[e[0]*n,e[1]*n,e[2]*n,e[3]*n]},xe=e=>{for(let t=0;t<16;t++)if(e[t]!==V[t])return!1;return!0},Se=e=>Math.sqrt(e[0]*e[0]+e[1]*e[1]+e[2]*e[2]+e[3]*e[3]),Ce=e=>e[0]===0&&e[1]===0&&e[2]===0&&e[3]===1,we=(e,t)=>Math.abs(e[0])<t&&Math.abs(e[1])<t&&Math.abs(e[2])<t&&Math.abs(e[3]-1)<t,Te=(e,t,n=1e-10)=>{let r=Math.abs(e[0]-t[0])<n&&Math.abs(e[1]-t[1])<n&&Math.abs(e[2]-t[2])<n&&Math.abs(e[3]-t[3])<n,i=Math.abs(e[0]+t[0])<n&&Math.abs(e[1]+t[1])<n&&Math.abs(e[2]+t[2])<n&&Math.abs(e[3]+t[3])<n;return r||i},Ee=(e,t)=>{let n=a.create();return a.multiply(n,e,t),[n[0],n[1],n[2],n[3]]},De=e=>{let t=a.create();return a.invert(t,e),[t[0],t[1],t[2],t[3]]},U=e=>[-e[2],e[1],e[0]],Oe=e=>[e[2],e[1],-e[0]],W=e=>[-e[2],e[1],e[0],e[3]],ke=e=>[e[2],e[1],-e[0],e[3]],G=e=>[e[1],e[2],e[0],e[3]],Ae=e=>[e[2],e[0],e[1],e[3]],K=(e,t,n)=>{if(!Number.isFinite(e)||!Number.isFinite(t)||!Number.isFinite(n))throw Error(`eulerToQuaternion: non-finite input (alpha=${e}, beta=${t}, gamma=${n})`);let r=e*Math.PI/180,i=t*Math.PI/180,o=n*Math.PI/180,s=a.create(),c=a.create(),l=a.create();a.setAxisAngle(s,[0,0,1],r),a.setAxisAngle(c,[1,0,0],i),a.setAxisAngle(l,[0,1,0],o);let u=a.create();return a.multiply(u,s,c),a.multiply(u,u,l),[u[0],u[1],u[2],u[3]]},je=t({name:`arElements`,initialState:M(),reducers:{recordPhoneHeight:(e,t)=>{e.floorDetections.push(t.payload)},addOrUpdateArPlane:(e,t)=>{let n=t.payload;if(n.parentPlaneId){delete e.arPlanes[n.planeId];return}e.arPlanes[n.planeId]=c(n)},startEventArea:(e,t)=>{e.currentEventAreaId=t.payload.id,e.currentEventAreaPoints=[]},addCornerToEventArea:(e,t)=>{e.currentEventAreaId&&e.currentEventAreaPoints.push(c(R(t.payload.position)))},correctArDriftForEventArea:(e,t)=>{let n=t.payload.drift;for(let t of e.currentEventAreaPoints)t[0]+=n[0],t[1]+=n[1],t[2]+=n[2]},finishEventArea:(e,t)=>{e.currentEventAreaId&&(e.eventAreas.push({id:e.currentEventAreaId,label:t.payload.label,polygon:[],isInArSpace:!0,arPoints:e.currentEventAreaPoints}),e.currentEventAreaId=null,e.currentEventAreaPoints=[])},anchorEventAreaInWorldSpace:(e,t)=>{let{areaId:n,alignmentMatrix:r,zeroRef:i}=t.payload,a=e.eventAreas.findIndex(e=>e.id===n);if(a===-1)return;let s=e.eventAreas[a];if(!s.isInArSpace||!s.arPoints)throw Error(`The area ${s.id} is already anchored in world space`);let c=ve(r),l=s.arPoints.map(e=>{let t=N(e);return k(i,o.transformMat4(o.create(),t,c))});e.eventAreas[a]={...s,isInArSpace:!1,polygon:l,arPoints:void 0}},resetArElements:()=>M()}}),{recordPhoneHeight:Me,addOrUpdateArPlane:Ne,startEventArea:Pe,addCornerToEventArea:Fe,correctArDriftForEventArea:Ie,finishEventArea:Le,anchorEventAreaInWorldSpace:Re,resetArElements:ze}=je.actions,Be=p(Me),Ve=p(Ne),He=p(Pe),Ue=p(Fe),We=p(Ie),Ge=p(Le),Ke=p(Re),qe=p(ze),Je=m(je.reducer);function Ye(e,t,n,r){let{inCentroid:i,refCentroid:a,refVec:s,tmp:c}=r;o.set(i,0,0,0),o.set(a,0,0,0);let l=0;for(let n=0;n<e.length;n++){let r=t[n][3];o.scaleAndAdd(i,i,e[n],r),q(s,t[n]),o.scaleAndAdd(a,a,s,r),l+=r}if(l===0)throw Error(`Weights must not sum to zero when solving Kabsch`);let u=1/l;if(o.scale(i,i,u),o.scale(a,a,u),!n)return{inCentroid:i,refCentroid:a,scaleRatio:1};let d=0,f=0;for(let n=0;n<e.length;n++)o.subtract(c,e[n],i),d+=o.length(c),q(s,t[n]),o.subtract(c,s,a),f+=o.length(c);return{inCentroid:i,refCentroid:a,scaleRatio:d===0?1:f/d}}function Xe(e,t,n,r,a,s,c){let{negCentroid:l,scaleVec:u}=c;return i.fromRotationTranslation(e,r,n),s&&(o.set(u,a,a,a),i.scale(e,e,u)),o.negate(l,t),i.translate(e,e,l),e}function Ze(e,t,n={}){let r=e.length;if(r!==t.length)throw Error(`Length of the point lists was not equal: ${r} vs ${t.length}`);if(r===0)return i.create();let s=n.solveRotation??!0,c=n.solveScale??!1,l=n.ignoreYAxisForRotation??!1,u=n.rotationIterations??9,{inCentroid:d,refCentroid:f,scaleRatio:p}=Ye(e,t,c,{inCentroid:o.create(),refCentroid:o.create(),refVec:o.create(),tmp:o.create()}),m=a.create();s?new Qe(l).solve(e,t,d,f,m,u):a.identity(m);let h={negCentroid:o.create(),scaleVec:o.create()};return Xe(i.create(),d,f,m,p,c,h)}var Qe=class e{static twoPi=Math.PI*2;static basisX=o.fromValues(1,0,0);static basisY=o.fromValues(0,1,0);static basisZ=o.fromValues(0,0,1);constructor(e){this.ignoreYAxis=e}solve(t,n,r,i,o,s){let c=this.transposeMultSubtract(t,n,r,i);if(this.ignoreYAxis){let t=c[0][0],n=c[0][2],r=c[2][0],i=c[2][2],s=Math.atan2(r-n,t+i);a.setAxisAngle(o,e.basisY,s)}else this.extractRotation(c,o,s)}transposeMultSubtract(e,t,n,r){let i=[o.create(),o.create(),o.create()],a=o.clone(n),s=o.clone(r);this.ignoreYAxis&&(a[1]=0,s[1]=0);let c=o.create(),l=o.create(),u=o.create();for(let n=0;n<e.length;n++){o.copy(c,e[n]),q(u,t[n]),this.ignoreYAxis&&(c[1]=0,u[1]=0);let r=t[n][3];o.subtract(c,c,a),o.scale(c,c,r),o.subtract(l,u,s),i[0][0]+=c[0]*l[0],i[1][0]+=c[1]*l[0],i[2][0]+=c[2]*l[0],i[0][1]+=c[0]*l[1],i[1][1]+=c[1]*l[1],i[2][1]+=c[2]*l[1],i[0][2]+=c[0]*l[2],i[1][2]+=c[1]*l[2],i[2][2]+=c[2]*l[2]}return i}extractRotation(t,n,r){let i=[o.create(),o.create(),o.create()],s=o.create(),c=o.create(),l=o.create();for(let u=0;u<r;u++){this.fillMatrixFromQuaternion(n,i),o.cross(s,i[0],t[0]),o.cross(l,i[1],t[1]),o.add(s,s,l),o.cross(l,i[2],t[2]),o.add(s,s,l);let r=o.dot(i[0],t[0])+o.dot(i[1],t[1])+o.dot(i[2],t[2])+1e-9;r=Math.abs(r),o.scale(s,s,1/r);let u=o.length(s);if(u<1e-9)break;o.scale(c,s,1/u);let d=u%e.twoPi,f=a.create();a.setAxisAngle(f,c,d),a.multiply(n,f,n),a.normalize(n,n)}}fillMatrixFromQuaternion(t,n){o.transformQuat(n[0],e.basisX,t),o.transformQuat(n[1],e.basisY,t),o.transformQuat(n[2],e.basisZ,t)}};const q=(e,t)=>(e[0]=t[0],e[1]=t[1],e[2]=t[2],e);function $e(e,t,n,r,i,a,o=Math.random){let s=Array.from(e);if(n>s.length)throw RangeError(`minSampleSize must be smaller than the number of elements: minSampleSize=${n}, elements=${s.length}`);return nt(s,et(s,t,n,r,i,a,o),i,a)}function et(e,t,n,r,i,a,o){let s=null,c=Array(n),l=new Uint8Array(e.length);for(let u=0;u<r;u+=1){it(e,n,o,c,l);let{inliers:r,outliers:u}=tt(e,l,i(c),a);if(s===null||n+r.length>=t){let e=[...c,...r],t=i(e),n=rt(t,`createModel must assign totalModelError for comparison during RANSAC`);(s===null||n<s.error)&&(t.inliers=e,t.outliers=u,s={model:t,error:n})}}return s?.model??null}function tt(e,t,n,r){let i=[],a=[];for(let o=0;o<e.length;o+=1){if(t?.[o])continue;let s=e[o];(r(n,s)?i:a).push(s)}return{inliers:i,outliers:a}}function nt(e,t,n,r){let i=t??n(e);rt(i,`RANSAC failed to produce a model with totalModelError`);let{inliers:a,outliers:o}=tt(e,null,i,r);return a.length>0&&(i=n(a),rt(i,`createModel must assign totalModelError for final RANSAC model`)),i.inliers=a,i.outliers=o,i}function rt(e,t){let{totalModelError:n}=e;if(n==null)throw Error(t);return n}function it(e,t,n,r,i){i.fill(0);let a=0;for(;a<t;){let t=Math.floor(n()*e.length);i[t]||(i[t]=1,r[a]=e[t],a+=1)}}const at={useOnlyRecentData:!1,recentSeconds:180,includeTimeWeight:!0,weightByTimeFactor:1321,ignoreYAxisForRotation:!0,useRansac:!1,ransacInlierRatio:.95,ransacSampleRatio:.3,ransacMaxIterations:230,ransacErrorTolerance:1,geohashPrecision:8,geohashWindowSize:10,gpsAccuracyExponent:.5},ot=e=>e.map(e=>R(e)),st=e=>e.map(e=>H(e)),ct=e=>e.map(Q),lt=e=>e.map(e=>[e[0],e[1],e[2],e[3]]),J=e=>Array.isArray(e)?e:F(e),Y=e=>Array.isArray(e)?e:I(e),ut=(e,t,n,r)=>({odometryPosOffset:R(J(e)),odometryRotOffset:H(Y(t)),latestLoopClosureFixPointPos:n?R(J(n)):null,latestLoopClosureFixPointRot:r?H(Y(r)):null}),dt=(e,t,n,r)=>({odometryPositions:[],odometryRotations:[],gpsPositions:[],gpsPositionsVec4:[],alignmentMatrix:[...V],alignmentRotation:[...z],alignmentTranslation:[...B],alignmentRotationInDegree:[...B],...ut(e,t,n,r),gpsAccuracyMedian:null,gpsAccuracyMean:null,currentGpsPosGeoHash:null}),ft=()=>dt(B,z,null,null),pt=(e,t,n,r)=>{let i=e.map(e=>J(e)),a=t.map(e=>H(Y(e))),o=n.map(Q),s=r.gpsAccuracyExponent;for(let e of o)e.weight=ht(e.latLongAccuracy,s);let c=o.map(Ot),l=r.includeTimeWeight?wt(c,o,r.weightByTimeFactor):c,u=r.useOnlyRecentData?Ct(o,r.recentSeconds):0;return{allPositionTuples:i,allRotationTuples:a,allGpsPoints:o,allVec4Tuples:l,solverPositionsTyped:i.slice(u).map(N),solverGpsPoints:o.slice(u),solverWeightedVec4Tuples:l.slice(u)}},mt=e=>{let{odometryPositions:t,odometryRotations:n,gpsPoints:r,odometryPosOffset:i=B,odometryRotOffset:s=z,latestLoopClosureFixPointPos:c=null,latestLoopClosureFixPointRot:l=null,alignmentConfig:u,random:d}=e;if(t.length!==n.length||t.length!==r.length)throw Error(`GpsEvents requires odometry positions, rotations, and gps points to have identical lengths`);let f={...at,...u??{}};if(t.length===0)return dt(i,s,c,l);let{allPositionTuples:p,allRotationTuples:m,allGpsPoints:h,allVec4Tuples:g,solverPositionsTyped:_,solverGpsPoints:v,solverWeightedVec4Tuples:y}=pt(t,n,r,f),b=yt(_,y,f,d??Math.random),x=a.create(),S=o.create(),C=o.create();_t(b.matrix,x,S,C);let{mean:ee,median:w}=Et(h),T=Tt(r,f.geohashPrecision,f.geohashWindowSize);return{odometryPositions:ot(p),odometryRotations:st(m),gpsPositions:ct(h),gpsPositionsVec4:lt(g),alignmentMatrix:L(b.matrix),alignmentRotation:I(x),alignmentTranslation:F(S),alignmentRotationInDegree:F(C),...ut(i,s,c,l),gpsAccuracyMedian:w,gpsAccuracyMean:ee,currentGpsPosGeoHash:T}},ht=(e,t)=>e!=null&&e>0?1/Math.max(e,1)**+t:1,gt=(e,t,n,r,i,s)=>{let c=e.odometryPositions,l=e.odometryRotations,u=e.gpsPositions,d=e.gpsPositionsVec4;c.push(J(t)),l.push(H(Y(n)));let f=Q(r);u.push(f);let p={...at,...i??{}};f.weight=ht(f.latLongAccuracy,p.gpsAccuracyExponent);let m=Ot(f);if(p.includeTimeWeight){d.push(m);let e=wt(d,u,p.weightByTimeFactor);for(let t=0;t<e.length;t++)d[t]=e[t]}else d.push(m);let h=0;p.useOnlyRecentData&&(h=Ct(u,p.recentSeconds));let g=yt(c.slice(h).map(N),d.slice(h),p,s??Math.random);e.alignmentMatrix=L(g.matrix);let _=a.create(),v=o.create(),y=o.create();_t(g.matrix,_,v,y),e.alignmentRotation=I(_),e.alignmentTranslation=F(v),e.alignmentRotationInDegree=F(y);let{mean:b,median:x}=Et(u);e.gpsAccuracyMean=b,e.gpsAccuracyMedian=x,e.currentGpsPosGeoHash=Tt(u,p.geohashPrecision,p.geohashWindowSize)},_t=(e,t,n,r)=>{i.getRotation(t,e),a.normalize(t,t),i.getTranslation(n,e),Dt(t,r)},X=o.create(),vt=o.create(),yt=(e,t,n,r)=>{if(e.length===0)return{matrix:i.create(),meanError:0};let a=e.length,o=[];for(let n=0;n<a;n+=1){let r=t[n];o.push({odom:e[n],ref:s.fromValues(r[0],r[1],r[2],r[3])})}if(n.useRansac&&o.length>=3){let e=o.length,t=Math.max(3,Math.min(e,Math.ceil(e*n.ransacSampleRatio))),i=$e(o,Math.max(t,Math.min(e,Math.ceil(e*n.ransacInlierRatio))),t,n.ransacMaxIterations,e=>bt(e,n.ignoreYAxisForRotation),(e,t)=>xt(e,t)<e.meanAlignmentError+n.ransacErrorTolerance,r);return{matrix:i.alignmentMatrix,meanError:i.meanAlignmentError}}let c=bt(o,n.ignoreYAxisForRotation);return{matrix:c.alignmentMatrix,meanError:c.meanAlignmentError}},bt=(e,t)=>{let n=Array.from(e),r=n.length,i=Array(r),a=Array(r);for(let e=0;e<r;e+=1)i[e]=n[e].odom,a[e]=n[e].ref;let s=Ze(i,a,{ignoreYAxisForRotation:t}),c=0;for(let e=0;e<r;e+=1)o.transformMat4(X,i[e],s),c+=St(X,n[e].ref);return{alignmentMatrix:s,meanAlignmentError:r===0?0:c/r,totalModelError:c}},xt=(e,t)=>(o.transformMat4(X,t.odom,e.alignmentMatrix),St(X,t.ref)),St=(e,t)=>(o.set(vt,t[0],t[1],t[2]),o.squaredDistance(e,vt)),Ct=(e,t)=>{if(e.length===0||t<=0)return 0;let n=e[e.length-1].timestamp-t*1e3;for(let t=0;t<e.length;t+=1)if(e[t].timestamp>=n)return t;return 0},wt=(e,t,n)=>{if(e.length<2)return e.map(e=>[e[0],e[1],e[2],e[3]]);let r=t[t.length-1].timestamp,i=t[0].timestamp,a=Math.max(1,r-i);return e.map((e,i)=>{let o=n*((r-t[i].timestamp)/a)+1,s=t[i].weight,c=1/(1/(s>0&&Number.isFinite(s)?s:1)+o);return[e[0],e[1],e[2],c]})},Z=new Map,Tt=(e,t,n)=>{if(e.length===0)return null;let r=Math.max(0,e.length-n);Z.clear();for(let n=r;n<e.length;n+=1){let r=e[n],i=ce(kt(r),t),a=Z.get(i);a?(a.count+=1,a.index=n):Z.set(i,{count:1,index:n})}let i=null,a=-1,o=-1;for(let[e,{count:t,index:n}]of Z)(t>a||t===a&&n>o)&&(i=e,a=t,o=n);return i},Et=e=>{let t=e.map(e=>e.latLongAccuracy).filter(e=>typeof e==`number`&&Number.isFinite(e));if(t.length===0)return{mean:null,median:null};let n=t.reduce((e,t)=>e+t,0)/t.length,r=t.slice().sort((e,t)=>e-t),i=Math.floor(r.length/2);return{mean:n,median:r.length%2==0?(r[i-1]+r[i])/2:r[i]}},Dt=(e,t)=>{let n=e[0],r=e[1],i=e[2],a=e[3],s=2*(a*n+r*i),c=1-2*(n*n+r*r),l=Math.atan2(s,c),u=2*(a*r-i*n),d;d=Math.abs(u)>=1?Math.PI/2*Math.sign(u):Math.asin(u);let f=2*(a*i+n*r),p=1-2*(r*r+i*i),m=Math.atan2(f,p),h=180/Math.PI,g=t??o.create();return g[0]=l*h,g[1]=d*h,g[2]=m*h,g},Ot=e=>[e.coordinates[0],e.coordinates[1],e.coordinates[2],e.weight>0&&Number.isFinite(e.weight)?e.weight:1],kt=e=>({lat:e.latitude,lon:e.longitude}),Q=e=>({...e,zeroRef:{lat:e.zeroRef.lat,lon:e.zeroRef.lon},coordinates:[e.coordinates[0],e.coordinates[1],e.coordinates[2]],timestamp:e.timestamp,deviceRotation:e.deviceRotation?H(Y(e.deviceRotation)):void 0}),At=({lat:e,lon:t})=>{if(!Number.isFinite(e))throw Error(`Invalid latitude value: ${e}`);if(!Number.isFinite(t))throw Error(`Invalid longitude value: ${t}`);if(e<-90||e>90)throw Error(`Invalid latitude range: ${e}`);if(t<-180||t>180)throw Error(`Invalid longitude range: ${t}`)},jt=o.fromValues(1,1,1),Mt=(e,t)=>[e[0]+t[0],e[1]+t[1],e[2]+t[2]],Nt=(e,t,n)=>{let r=O(t,{lat:e.latitude,lon:e.longitude},e.altitude??0,0),i=n?.alpha!=null&&n?.beta!=null&&n?.gamma!=null?G(K(n.alpha,n.beta,n.gamma)):void 0;return{...e,zeroRef:t,coordinates:F(r),weight:1,deviceRotation:i}},Pt=(e,t,n)=>{if(e)return K(e.alpha,e.beta,e.gamma);if(t)return t;throw Error(`Sensor rotation missing for ${n}: neither raw orientation nor legacy quaternion provided`)},Ft=e=>{let t=a.invert(a.create(),e);if(!t)throw Error(`Encountered non-invertible quaternion`);return t},It=e=>{let t=Ft(a.normalize(a.create(),P(e.lastSensorRot))),n=a.normalize(a.create(),P(e.lastValidOdomRot)),r=a.multiply(a.create(),t,n),i=a.normalize(a.create(),P(e.newSensorRot)),o=a.multiply(a.create(),i,r),s=a.normalize(a.create(),P(e.newOdomRot)),c=a.multiply(a.create(),o,s);return a.normalize(c,c),I(c)},Lt=(e,t)=>{let n=i.create();return i.fromRotationTranslationScale(n,a.normalize(a.create(),P(t)),N(e),jt),n},Rt=(e,t,n)=>{let r=o.create(),s=o.create(),c=a.create(),l=a.create();i.getTranslation(r,e),i.getTranslation(s,t),i.getRotation(c,e),i.getRotation(l,t);let u=o.create();o.lerp(u,r,s,n);let d=a.create();return a.slerp(d,c,l,n),i.fromRotationTranslation(i.create(),d,u)},zt=(e,t)=>{if(e.length===0)return[];let n=e[e.length-1],r=i.invert(i.create(),n);if(!r)throw Error(`End pose matrix is not invertible`);let a=i.multiply(i.create(),t,r),o=i.create(),s=e.length-1;return e.map((e,t)=>{let n=Rt(o,a,s<=0?1:t/s);return i.multiply(i.create(),n,e)})},Bt=(e,t,n)=>{for(let r=e.length-1;r>=0;--r)if(n(e[r],t))return r;return-1},Vt=(e,t)=>e[0]===t[0]&&e[1]===t[1]&&e[2]===t[2],Ht=(e,t)=>e[0]===t[0]&&e[1]===t[1]&&e[2]===t[2]&&e[3]===t[3],Ut=t({name:`gpsData`,initialState:null,reducers:{setZeroPos:{reducer:(e,t)=>e===null?{zero:t.payload,gpsEvents:ft(),odometryPath:_e(),referencePoints:[]}:e,prepare:e=>(At(e),{payload:e})},recordGpsEvent:(e,t)=>{if(!e)return e;let{odomPosition:n,odomRotation:r,rawGpsPoint:i,rawDeviceOrientation:a}=t.payload,o=Nt(i,e.zero,a);gt(e.gpsEvents,U(n),W(r),o)},odometryTrackingRestarted:(e,t)=>{if(!e)return e;let n=Mt(e.gpsEvents.odometryPosOffset,U(t.payload.lastValidOdomPos)),r=Pt(t.payload.lastSensorOrientation,t.payload.lastSensorRot,`lastSensor`),i=Pt(t.payload.newSensorOrientation,t.payload.newSensorRot,`newSensor`);e.gpsEvents=c(mt({odometryPositions:[],odometryRotations:[],gpsPoints:[],odometryPosOffset:n,odometryRotOffset:It({...t.payload,lastValidOdomRot:W(t.payload.lastValidOdomRot),newOdomRot:W(t.payload.newOdomRot),lastSensorRot:G(r),newSensorRot:G(i)}),latestLoopClosureFixPointPos:null,latestLoopClosureFixPointRot:null}))},arLoopClosureDetected:(e,t)=>{if(!e)return e;let n=e.gpsEvents;if(n.odometryPositions.length===0)return e;let r=n.latestLoopClosureFixPointPos?Bt(n.odometryPositions,n.latestLoopClosureFixPointPos,Vt):0,s=n.latestLoopClosureFixPointRot?Bt(n.odometryRotations,n.latestLoopClosureFixPointRot,Ht):0;if(r<0||s<0)throw Error(`Loop closure fix point could not be located in history`);if(r!==s)throw Error(`Loop closure fix point indices for position and rotation diverged`);let l=n.odometryPositions.slice(0,r).map(e=>R(e)),u=n.odometryRotations.slice(0,r).map(e=>H(e)),d=n.odometryPositions.slice(r).map(e=>R(e)),f=n.odometryRotations.slice(r).map(e=>H(e)),p=d.map((e,t)=>Lt(e,f[t])),m=Lt(U(t.payload.lastPos),H(W(t.payload.lastRot)));p.push(m);let h=zt(p,Lt(U(t.payload.newPos),H(W(t.payload.newRot)))),g=[],_=[],v=o.create();for(let e=0;e<h.length-1;e+=1){let t=h[e],n=o.create();i.getTranslation(n,t);let r=a.create();if(i.getRotation(r,t),a.normalize(r,r),i.getScaling(v,t),Math.hypot(v[0]-1,v[1]-1,v[2]-1)>.05)throw Error(`Unexpected scale drift encountered during loop closure correction`);g.push(F(n)),_.push(I(r))}e.gpsEvents=c(mt({odometryPositions:l.concat(g),odometryRotations:u.concat(_),gpsPoints:n.gpsPositions,odometryPosOffset:n.odometryPosOffset,odometryRotOffset:n.odometryRotOffset,latestLoopClosureFixPointPos:null,latestLoopClosureFixPointRot:null}))},add2dImage:(e,t)=>{if(!e)return e;e.odometryPath.points.push(c({imageFile:t.payload.imageFile,screenRotation:t.payload.screenRotation,position:U(t.payload.position),rotation:H(W(t.payload.rotation)),capturedAt:t.payload.capturedAt}))},markReferencePoint:(e,t)=>{if(!e)return e;let{id:n,position:r,rotation:i,rawGpsPoint:a,timestamp:o}=t.payload,s=Nt(a,e.zero);e.referencePoints.push(c({id:n,position:U(r),rotation:H(W(i)),gpsPoint:Q(s),timestamp:o??Date.now()}))}}}),{setZeroPos:Wt,recordGpsEvent:Gt,odometryTrackingRestarted:Kt,arLoopClosureDetected:qt,add2dImage:Jt,markReferencePoint:Yt}=Ut.actions,Xt=p(Wt),Zt=p(Gt),Qt=p(Kt),$t=p(qt),en=p(Jt),tn=p(Yt),nn=m(Ut.reducer),rn=t({name:`gpsElements`,initialState:j(),reducers:{addMarker:(e,t)=>{e.gpsMarkers.push(t.payload)},addLine:(e,t)=>{e.gpsLines.push(t.payload)},addArea:(e,t)=>{e.areas.push(t.payload)},addToHeatMaps:(e,t)=>{let{category:n,tiles:r}=t.payload,i=e.heatMap[n]??{};e.heatMap[n]={...i,...r}},addHeatMapArea:(e,t)=>{let n=t.payload,r=n.category;if(e.heatAreas[r]||(e.heatAreas[r]={}),e.heatAreas[r][n.geoHash])throw Error(`HeatArea collision detected for GeoHash: ${n.geoHash}`);e.heatAreas[r][n.geoHash]=n},resetGpsElements:()=>j()}}),{addMarker:an,addLine:on,addArea:sn,addToHeatMaps:cn,addHeatMapArea:ln,resetGpsElements:un}=rn.actions,dn=p(an),fn=p(on),pn=p(sn),mn=p(cn),hn=p(ln),gn=p(un),_n=m(rn.reducer),vn=()=>({gpsData:null,gpsElements:j(),arElements:M()}),yn=e=>{let t=Array.from({length:Math.min(e.length,4)},(t,n)=>e[n].toFixed(2));e.length>4&&t.push(`…`);let n=`[${t.join(`, `)}]`;if(e.length===4){let{pitch:t,yaw:r,roll:i}=v(e);return`${n} (pitch=${t.toFixed(0)}°, yaw=${r.toFixed(0)}°, roll=${i.toFixed(0)}°)`}return n},bn=e=>(e.length===3||e.length===4||e.length===16)&&e.every(e=>typeof e==`number`),$=(e,t=0)=>{if(t>10||e==null)return e;if(Array.isArray(e))return bn(e)?yn(e):e.map(e=>$(e,t+1));if(typeof e==`object`){let n={};for(let[r,i]of Object.entries(e))n[r]=$(i,t+1);return n}return e},xn=(t={})=>{let{preloadedState:n,enableDevToolsSanitizers:r=!0,enableDevChecks:i=!0,licenseKey:a}=t;if(a==null)throw Error(`License key required. Pass options.licenseKey or use createRecorderStore() from gps-plus-slam-app-framework. See EULA §3.`);g(a);let o=vn(),s=n?{gpsData:n.gpsData??o.gpsData,gpsElements:n.gpsElements??o.gpsElements,arElements:n.arElements??o.arElements}:void 0;return e({reducer:{gpsData:nn,gpsElements:_n,arElements:Je},preloadedState:s,middleware:e=>e({serializableCheck:i,immutableCheck:i}),devTools:r?{actionSanitizer:$,stateSanitizer:$}:!0})},Sn=[],Cn=[],wn=[],Tn=[],En={median:null,mean:null},Dn=f(function(e){let t=e.gpsData?.gpsEvents;return!t||t.gpsPositions.length===0?null:t.alignmentMatrix}),On=f(function(e){let t=e.gpsData?.gpsEvents;return!t||t.gpsPositions.length===0?null:t.alignmentRotation}),kn=f(function(e){return e.gpsData?.gpsEvents?.odometryPositions??Sn}),An=f(function(e){return e.gpsData?.gpsEvents?.odometryRotations??Cn}),jn=f(function(e){return e.gpsData?.gpsEvents?.gpsPositions??wn}),Mn=f(function(e){return e.gpsData?.referencePoints??Tn}),Nn=f(function(e){return e.gpsData?.zero??null}),Pn=f(function(e){let t=e.gpsData?.gpsEvents?.gpsAccuracyMedian??null,n=e.gpsData?.gpsEvents?.gpsAccuracyMean??null;return t===null&&n===null?En:{median:t,mean:n}}),Fn=`1.0.0`;export{V as IDENTITY_MATRIX4,z as IDENTITY_QUATERNION,Fn as LIB_VERSION,B as ZERO_VECTOR3,en as add2dImage,pn as addArea,Ue as addCornerToEventArea,hn as addHeatMapArea,fn as addLine,dn as addMarker,Ve as addOrUpdateArPlane,mn as addToHeatMaps,Ke as anchorEventAreaInWorldSpace,Je as arElementsReducer,$t as arLoopClosureDetected,ce as calcGeoHash,k as calcGpsCoords,oe as calcQuadKey,O as calcRelativeCoordsInMeters,be as cloneMatrix4,ye as cloneQuaternion,R as cloneVector3,We as correctArDriftForEventArea,xn as createGpsSlamStore,ue as distanceInMeters,de as distanceInMetersRelative,G as enuQuaternionToNUE,K as eulerToQuaternion,Ge as finishEventArea,ve as fromMatrix4,P as fromQuaternion,N as fromVector3,ge as fusedGpsFromOdom,le as geoHashToLatLong,Dn as getAlignmentMatrix,On as getAlignmentRotation,me as getGoogleMapsDirectionsLink,pe as getGoogleMapsLink,Pn as getGpsAccuracyStats,jn as getGpsPositions,kn as getOdometryPositions,An as getOdometryRotations,he as getOpenStreetMapLink,Mn as getReferencePoints,Nn as getZeroReference,nn as gpsDataReducer,_n as gpsElementsReducer,De as invertQuaternion,xe as isIdentityMatrix4,Ce as isIdentityQuaternion,we as isNearIdentityQuaternion,tn as markReferencePoint,Ee as multiplyQuaternions,H as normalizeQuaternion,Ae as nueQuaternionToENU,ke as nueQuaternionToWebXR,Oe as nueToWebXR,Qt as odometryTrackingRestarted,se as quadKeyToLatLong,Se as quaternionMagnitude,Te as quaternionsEquivalent,Zt as recordGpsEvent,Be as recordPhoneHeight,qe as resetArElements,gn as resetGpsElements,$ as sanitizeForDevTools,Xt as setZeroPos,He as startEventArea,fe as toEarthCenteredCoordinates,L as toMatrix4,I as toQuaternion,F as toVector3,g as validateLicenseKey,W as webxrQuaternionToNUE,U as webxrToNUE};
@@ -0,0 +1,40 @@
1
+
2
+ //#region ../src/licensing/community-license-key.d.ts
3
+ /**
4
+ * Community license key for the gps-plus-slam-js core library.
5
+ *
6
+ * Generated by the core library maintainers and bundled with the core
7
+ * `gps-plus-slam-js` package via the dedicated sub-path export
8
+ * `gps-plus-slam-js/community-license-key`. Lives here (rather than in the
9
+ * open-source `gps-plus-slam-app-framework` package) so that:
10
+ * - the bundled token and the public key it validates against ship in the
11
+ * same npm artifact, version-locked through the dep graph;
12
+ * - the K5/R8 re-sign script + lifetime guardrail can both read and write
13
+ * a same-package sibling file (no cross-repo path traversal);
14
+ * - direct-core consumers (without AppFramework) get the same frictionless
15
+ * OSS onboarding AppFramework users have today.
16
+ *
17
+ * The token is **not** re-exported from the package's root barrel (`./`).
18
+ * Consumers must opt in by explicitly importing
19
+ * `gps-plus-slam-js/community-license-key` and passing the constant to the
20
+ * licensing store. Installing the package alone does not auto-apply
21
+ * community-license terms.
22
+ *
23
+ * The key has a rolling expiration of 12 months. Each release of the core
24
+ * package renews it via `scripts/resign-community-key.mjs`, executed inside
25
+ * `.github/workflows/publish.yml` immediately before `pnpm publish`. Active
26
+ * users who regularly update their dependencies will never be affected by
27
+ * the expiration. AppFramework releases pull whatever core version their
28
+ * `^X.Y.Z` range resolves to at install time, so framework releases also
29
+ * transitively refresh the bundled token.
30
+ *
31
+ * For licenses with extended validity beyond the community key's scope,
32
+ * contact support@csutil.com.
33
+ *
34
+ * @see EULA.md §3 — License Key
35
+ * @see docs/2026-04-25-private-key-security-plan.md — secret-backed renewal
36
+ * @see ../../GpsPlusSlamJs_Docs/docs/2026-05-01-community-key-resign-cross-repo-issue.md
37
+ */
38
+ declare const COMMUNITY_LICENSE_KEY = "eyJ0eXBlIjoiY29tbXVuaXR5IiwiZXhwIjoxODA5MjQ3MDU4fQ.OCzMOU_IZZjWpNgZDss7eIsVFtfD_uvUY6ST8PUw77vGYCxK_9FyI87CeKEBK9UFQYwqaJBw1x-yr_jT2OFPDQ";
39
+ //#endregion
40
+ export { COMMUNITY_LICENSE_KEY };
@@ -0,0 +1,2 @@
1
+ /*! gps-plus-slam-js | (c) 2026 cs-util-com | UNLICENSED — see EULA.md */
2
+ const e=`eyJ0eXBlIjoiY29tbXVuaXR5IiwiZXhwIjoxODA5MjQ3MDU4fQ.OCzMOU_IZZjWpNgZDss7eIsVFtfD_uvUY6ST8PUw77vGYCxK_9FyI87CeKEBK9UFQYwqaJBw1x-yr_jT2OFPDQ`;export{e as COMMUNITY_LICENSE_KEY};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gps-plus-slam-js",
3
- "version": "1.0.1",
3
+ "version": "1.0.4",
4
4
  "description": "TypeScript library for real-time GPS + AR odometry alignment.",
5
5
  "author": "cs-util-com",
6
6
  "keywords": [
@@ -17,17 +17,22 @@
17
17
  "sensor-fusion"
18
18
  ],
19
19
  "type": "module",
20
+ "//": "repository.url points at the private source repo because npm Trusted Publishing requires it to match the publishing repo's OIDC claim. User-facing links live in `homepage` and `bugs`, which point at the public mirror.",
20
21
  "homepage": "https://github.com/cs-util-com/location-based-webxr#readme",
22
+ "bugs": {
23
+ "url": "https://github.com/cs-util-com/location-based-webxr/issues"
24
+ },
21
25
  "repository": {
22
26
  "type": "git",
23
- "url": "git+https://github.com/cs-util-com/location-based-webxr.git"
27
+ "url": "git+https://github.com/cs-util-com/gps-plus-slam.git"
24
28
  },
25
29
  "files": [
26
30
  "dist",
27
31
  "EULA.md"
28
32
  ],
29
33
  "exports": {
30
- ".": "./dist/index.js"
34
+ ".": "./dist/index.js",
35
+ "./community-license-key": "./dist/licensing/community-license-key.js"
31
36
  },
32
37
  "types": "./dist/index.d.ts",
33
38
  "engines": {
@@ -69,6 +74,7 @@
69
74
  },
70
75
  "scripts": {
71
76
  "generate-keys": "node scripts/generate-keys.mjs",
77
+ "resign-community-key": "node scripts/resign-community-key.mjs",
72
78
  "test": "pnpm run test:core && pnpm run test:e2e:index && pnpm run test:guardrail",
73
79
  "test:core": "pnpm run format && pnpm run lint && pnpm run check:all && pnpm run typecheck && pnpm run typecheck:tests && pnpm run test:unit",
74
80
  "test:guardrail": "pnpm --filter gps-plus-slam-investigation run test:guardrail",