@multisetai/vps 1.0.7-beta.1 → 1.0.7-beta.3

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/README.md CHANGED
@@ -154,14 +154,12 @@ interface IMultisetSdkConfig {
154
154
  confidenceCheck?: boolean;
155
155
  /** Minimum confidence (0.2–0.8) required when confidenceCheck is enabled. */
156
156
  confidenceThreshold?: number;
157
- /** Total single-frame attempts per localization run (1–5). */
158
- requestAttempts?: number;
159
- /** Time in seconds between attempts in a localization run (1–5). */
160
- localizationInterval?: number;
161
157
  /** Include device geo pose as a hint in localization requests. */
162
158
  passGeoPose?: boolean;
163
159
  /** Request geo coordinates in the localization response (if supported by backend). */
164
160
  geoCoordinatesInResponse?: boolean;
161
+ /** Max time in ms to wait for a valid viewer pose before failing. Default: 10000. */
162
+ localizationTrackingTimeoutMs?: number;
165
163
 
166
164
  // Localization lifecycle callbacks
167
165
  onLocalizationInit?: () => void;
@@ -206,7 +204,7 @@ The client and controller emit events through callback functions:
206
204
 
207
205
  `MultisetClient` together with `WebxrController` supports a higher-level localization flow similar to the Unity SingleFrameLocalizationManager:
208
206
 
209
- - **Multiple attempts per run**: `requestAttempts` and `localizationInterval` control how many single-frame attempts are made and how long to wait between them.
207
+ - **Multiple attempts per run**: `localizeFrame` performs several internal single-frame attempts and picks the best result.
210
208
  - **Confidence-based acceptance**: When `confidenceCheck` is `true`, only results with `confidence >= confidenceThreshold` (0.2–0.8) are treated as successful.
211
209
  - **Auto-localize**: When `autoLocalize` is `true`, a localization run is started automatically when the AR session starts.
212
210
  - **Re-localize on tracking loss**: When `relocalization` is `true`, the controller automatically tries to re-localize after tracking has been lost for a short period.
@@ -250,24 +248,9 @@ const arButton = await controller.initialize(buttonContainer);
250
248
 
251
249
  **Returns**: The created AR button element.
252
250
 
253
- ##### `captureFrame(): Promise<ILocalizeAndMapDetails | null>`
254
-
255
- Captures the current camera frame and performs localization.
256
-
257
- ```typescript
258
- const result = await controller.captureFrame();
259
- if (result?.localizeData?.poseFound) {
260
- const { position, rotation, confidence } = result.localizeData;
261
- console.log('Localized at:', position);
262
- }
263
- ```
264
-
265
- **Returns**: Object with `localizeData` and optional `mapDetails`, or `null` if no pose is found or capture fails.
266
-
267
251
  ##### `localizeFrame(): Promise<ILocalizeAndMapDetails | null>`
268
252
 
269
- Runs a full localization cycle using the configured `requestAttempts`, `localizationInterval`,
270
- `confidenceCheck`, and `confidenceThreshold`:
253
+ Captures one frame from the active AR session and localizes against the configured map. Waits for a valid viewer pose up to `localizationTrackingTimeoutMs` before failing.
271
254
 
272
255
  ```typescript
273
256
  const result = await controller.localizeFrame();
@@ -279,7 +262,8 @@ if (result?.localizeData?.poseFound) {
279
262
  This method:
280
263
 
281
264
  - Fires `onLocalizationInit` at the start of the run
282
- - Performs multiple attempts and picks the best result by confidence
265
+ - Waits for a valid viewer pose (up to `localizationTrackingTimeoutMs`, default 10 s)
266
+ - Captures the camera frame and calls the localization API
283
267
  - Applies the confidence threshold when `confidenceCheck` is enabled
284
268
  - Calls `onLocalizationSuccess` or `onLocalizationFailure` accordingly
285
269
 
@@ -338,6 +322,7 @@ All TypeScript types are exported from the main entry points:
338
322
  // Core types
339
323
  import type {
340
324
  IMultisetSdkConfig,
325
+ IMultisetPublicConfig,
341
326
  IMultisetSdkEndpoints,
342
327
  IFrameCaptureEvent,
343
328
  ICameraIntrinsicsEvent,
@@ -378,8 +363,6 @@ const client = new MultisetClient({
378
363
  relocalization: true,
379
364
  confidenceCheck: true,
380
365
  confidenceThreshold: 0.5,
381
- requestAttempts: 3,
382
- localizationInterval: 2,
383
366
  passGeoPose: true,
384
367
  geoCoordinatesInResponse: true,
385
368
  onAuthorize: (token) => console.log('Authorized:', token),
@@ -14,11 +14,7 @@ interface ILocalizeResponse {
14
14
  poseFound: boolean;
15
15
  position: IPosition;
16
16
  rotation: IRotation;
17
- retrieval_scores: number[];
18
- num_matches: number[];
19
17
  confidence: number;
20
- retreived_imgs: string[];
21
- retrieved_imgs?: string[];
22
18
  mapIds: string[];
23
19
  mapCodes: string[];
24
20
  responseTime: number;
@@ -130,6 +126,8 @@ interface ILocalizeAndMapDetails {
130
126
  mapDetails?: IGetMapsDetailsResponse;
131
127
  }
132
128
 
129
+ /** The subset of config that is safe to expose publicly (no credentials). */
130
+ type IMultisetPublicConfig = Omit<IMultisetSdkConfig, 'clientId' | 'clientSecret'>;
133
131
  interface IMultisetSdkConfig {
134
132
  clientId: string;
135
133
  clientSecret: string;
@@ -140,6 +138,8 @@ interface IMultisetSdkConfig {
140
138
  endpoints?: Partial<IMultisetSdkEndpoints>;
141
139
  /** If true, show the mesh in the AR session. Default is false. */
142
140
  showMesh?: boolean;
141
+ /** If true, show the coordinate gizmo when a pose is found. Defaults to true. */
142
+ showGizmo?: boolean;
143
143
  /** If true, automatically start a localization run when the AR session starts. */
144
144
  autoLocalize?: boolean;
145
145
  /** If true, automatically re-localize when tracking is lost and then recovered. */
@@ -156,6 +156,8 @@ interface IMultisetSdkConfig {
156
156
  passGeoPose?: boolean;
157
157
  /** Request geo coordinates in the localization response (if supported by backend). */
158
158
  geoCoordinatesInResponse?: boolean;
159
+ /** Max time in ms to wait for a valid viewer pose before failing. Default 10000. */
160
+ localizationTrackingTimeoutMs?: number;
159
161
  /** Invoked at the start of a localization run. */
160
162
  onLocalizationInit?: () => void;
161
163
  /** Invoked after a successful localization that meets confidence criteria (if enabled). */
@@ -217,13 +219,14 @@ interface ILocalizeResultEvent {
217
219
  declare const DEFAULT_ENDPOINTS: IMultisetSdkEndpoints;
218
220
 
219
221
  declare class MultisetClient {
222
+ private readonly credentials;
220
223
  private readonly config;
221
224
  private readonly endpoints;
222
225
  private accessToken;
223
226
  private mapDetailsCache;
224
- constructor(config: IMultisetSdkConfig);
227
+ constructor(input: IMultisetSdkConfig);
225
228
  get token(): string | null;
226
- getConfig(): IMultisetSdkConfig;
229
+ getConfig(): IMultisetPublicConfig;
227
230
  downloadFile(key: string): Promise<string>;
228
231
  authorize(): Promise<string>;
229
232
  private handleError;
@@ -231,7 +234,6 @@ declare class MultisetClient {
231
234
  private getGeoPoseComponents;
232
235
  private queryLocalization;
233
236
  private fetchMapDetails;
234
- private fetchMapSetDetails;
235
237
  }
236
238
 
237
- export { DEFAULT_ENDPOINTS, type ICameraIntrinsicsEvent, type IFrameCaptureEvent, type IGetMapsDetailsResponse, type ILocalizeAndMapDetails, type ILocalizeResponse, type ILocalizeResultEvent, type IMapSetMapsResponse, type IMultisetSdkConfig as IMultisetClientOptions, type IMultisetSdkConfig, type IMultisetSdkEndpoints, type IPoseResultEvent, type MapType, MultisetClient };
239
+ export { DEFAULT_ENDPOINTS, type ICameraIntrinsicsEvent, type IFrameCaptureEvent, type IGetMapsDetailsResponse, type ILocalizeAndMapDetails, type ILocalizeResponse, type ILocalizeResultEvent, type IMapSetMapsResponse, type IMultisetSdkConfig as IMultisetClientOptions, type IMultisetPublicConfig, type IMultisetSdkConfig, type IMultisetSdkEndpoints, type IPoseResultEvent, type MapType, MultisetClient };
@@ -9,14 +9,15 @@ var DEFAULT_ENDPOINTS = {
9
9
  fileDownloadUrl: "https://api.multiset.ai/v1/file"
10
10
  };
11
11
  var MultisetClient = class {
12
- constructor(config) {
13
- this.config = config;
12
+ constructor(input) {
14
13
  this.accessToken = null;
15
14
  this.mapDetailsCache = {};
16
- this.config = config;
15
+ const { clientId, clientSecret, ...rest } = input;
16
+ this.credentials = { clientId, clientSecret };
17
+ this.config = rest;
17
18
  this.endpoints = {
18
19
  ...DEFAULT_ENDPOINTS,
19
- ...config.endpoints
20
+ ...rest.endpoints
20
21
  };
21
22
  }
22
23
  get token() {
@@ -45,24 +46,23 @@ var MultisetClient = class {
45
46
  }
46
47
  }
47
48
  async authorize() {
48
- var _a, _b, _c, _d, _e;
49
49
  try {
50
50
  const response = await axios.post(
51
51
  this.endpoints.authUrl,
52
52
  {},
53
53
  {
54
54
  auth: {
55
- username: this.config.clientId,
56
- password: this.config.clientSecret
55
+ username: this.credentials.clientId,
56
+ password: this.credentials.clientSecret
57
57
  }
58
58
  }
59
59
  );
60
- const token = (_c = (_a = response.data) == null ? void 0 : _a.token) != null ? _c : (_b = response.data) == null ? void 0 : _b.access_token;
60
+ const token = response.data?.token ?? response.data?.access_token;
61
61
  if (!token) {
62
62
  throw new Error("Authorization succeeded but no token was returned.");
63
63
  }
64
64
  this.accessToken = token;
65
- (_e = (_d = this.config).onAuthorize) == null ? void 0 : _e.call(_d, token);
65
+ this.config.onAuthorize?.(token);
66
66
  return token;
67
67
  } catch (error) {
68
68
  this.handleError(error);
@@ -70,24 +70,22 @@ var MultisetClient = class {
70
70
  }
71
71
  }
72
72
  handleError(error) {
73
- var _a, _b, _c, _d;
74
73
  if (axios.isAxiosError(error)) {
75
74
  const axiosError = error;
76
- (_b = (_a = this.config).onError) == null ? void 0 : _b.call(_a, axiosError);
75
+ this.config.onError?.(axiosError);
77
76
  } else {
78
- (_d = (_c = this.config).onError) == null ? void 0 : _d.call(_c, error);
77
+ this.config.onError?.(error);
79
78
  }
80
79
  }
81
80
  async localizeWithFrame(frame, intrinsics) {
82
- var _a, _b, _c, _d, _e, _f, _g;
83
81
  if (!this.accessToken) {
84
82
  throw new Error("Access token is missing. Call authorize() first.");
85
83
  }
86
- (_b = (_a = this.config).onFrameCaptured) == null ? void 0 : _b.call(_a, frame);
87
- (_d = (_c = this.config).onCameraIntrinsics) == null ? void 0 : _d.call(_c, intrinsics);
84
+ this.config.onFrameCaptured?.(frame);
85
+ this.config.onCameraIntrinsics?.(intrinsics);
88
86
  const queryResult = await this.queryLocalization(frame, intrinsics);
89
- if ((_e = queryResult == null ? void 0 : queryResult.localizeData) == null ? void 0 : _e.poseFound) {
90
- (_g = (_f = this.config).onPoseResult) == null ? void 0 : _g.call(_f, queryResult.localizeData);
87
+ if (queryResult?.localizeData?.poseFound) {
88
+ this.config.onPoseResult?.(queryResult.localizeData);
91
89
  }
92
90
  return queryResult;
93
91
  }
@@ -111,7 +109,6 @@ var MultisetClient = class {
111
109
  }
112
110
  }
113
111
  async queryLocalization(frame, intrinsics) {
114
- var _a;
115
112
  const formData = new FormData();
116
113
  if (this.config.mapType === "map") {
117
114
  formData.append("mapCode", this.config.code);
@@ -154,7 +151,7 @@ var MultisetClient = class {
154
151
  const result = {
155
152
  localizeData: data
156
153
  };
157
- if (this.config.showMesh && this.config.mapType === "map" && ((_a = data.mapCodes) == null ? void 0 : _a.length)) {
154
+ if (this.config.showMesh && data.mapCodes?.length) {
158
155
  const mapCode = data.mapCodes[0];
159
156
  const cached = this.mapDetailsCache[mapCode];
160
157
  if (cached) {
@@ -189,22 +186,6 @@ var MultisetClient = class {
189
186
  return null;
190
187
  }
191
188
  }
192
- async fetchMapSetDetails(mapSetId) {
193
- try {
194
- const response = await axios.get(
195
- `${this.endpoints.mapSetDetailsUrl}${mapSetId}`,
196
- {
197
- headers: {
198
- Authorization: `Bearer ${this.accessToken}`
199
- }
200
- }
201
- );
202
- return response.data;
203
- } catch (error) {
204
- this.handleError(error);
205
- return null;
206
- }
207
- }
208
189
  };
209
190
 
210
191
  export { DEFAULT_ENDPOINTS, MultisetClient };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/lib/core/config.ts","../../src/lib/core/client.ts"],"names":[],"mappings":";;;AA8FO,IAAM,iBAAA,GAA2C;AAAA,EACtD,OAAA,EAAS,sCAAA;AAAA,EACT,QAAA,EAAU,+CAAA;AAAA,EACV,aAAA,EAAe,qCAAA;AAAA,EACf,gBAAA,EAAkB,yCAAA;AAAA,EAClB,eAAA,EAAiB;AACnB;ACrFO,IAAM,iBAAN,MAAqB;AAAA,EAK1B,YAA6B,MAAA,EAA4B;AAA5B,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AAH7B,IAAA,IAAA,CAAQ,WAAA,GAA6B,IAAA;AACrC,IAAA,IAAA,CAAQ,kBAA2D,EAAC;AAGlE,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AACd,IAAA,IAAA,CAAK,SAAA,GAAY;AAAA,MACf,GAAG,iBAAA;AAAA,MACH,GAAG,MAAA,CAAO;AAAA,KACZ;AAAA,EACF;AAAA,EAEA,IAAI,KAAA,GAAuB;AACzB,IAAA,OAAO,IAAA,CAAK,WAAA;AAAA,EACd;AAAA,EAEA,SAAA,GAAgC;AAC9B,IAAA,OAAO,IAAA,CAAK,MAAA;AAAA,EACd;AAAA,EAEA,MAAM,aAAa,GAAA,EAA8B;AAC/C,IAAA,IAAI,CAAC,IAAA,CAAK,WAAA,IAAe,CAAC,GAAA,EAAK;AAC7B,MAAA,OAAO,EAAA;AAAA,IACT;AAEA,IAAA,IAAI;AACF,MAAA,MAAM,QAAA,GAAW,MAAM,KAAA,CAAM,GAAA;AAAA,QAC3B,GAAG,IAAA,CAAK,SAAA,CAAU,eAAe,CAAA,KAAA,EAAQ,kBAAA,CAAmB,GAAG,CAAC,CAAA,CAAA;AAAA,QAChE;AAAA,UACE,OAAA,EAAS;AAAA,YACP,aAAA,EAAe,CAAA,OAAA,EAAU,IAAA,CAAK,WAAW,CAAA;AAAA;AAC3C;AACF,OACF;AAEA,MAAA,OAAO,QAAA,CAAS,MAAA,KAAW,GAAA,GAAM,QAAA,CAAS,KAAK,GAAA,GAAM,EAAA;AAAA,IACvD,SAAS,KAAA,EAAO;AACd,MAAA,IAAA,CAAK,YAAY,KAAK,CAAA;AACtB,MAAA,OAAO,EAAA;AAAA,IACT;AAAA,EACF;AAAA,EAEA,MAAM,SAAA,GAA6B;AA1DrC,IAAA,IAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA;AA2DI,IAAA,IAAI;AACF,MAAA,MAAM,QAAA,GAAW,MAAM,KAAA,CAAM,IAAA;AAAA,QAC3B,KAAK,SAAA,CAAU,OAAA;AAAA,QACf,EAAC;AAAA,QACD;AAAA,UACE,IAAA,EAAM;AAAA,YACJ,QAAA,EAAU,KAAK,MAAA,CAAO,QAAA;AAAA,YACtB,QAAA,EAAU,KAAK,MAAA,CAAO;AAAA;AACxB;AACF,OACF;AAEA,MAAA,MAAM,KAAA,GAAA,CACJ,oBAAS,IAAA,KAAT,IAAA,GAAA,KAAA,CAAA,GAAA,EAAA,CAAe,UAAf,IAAA,GAAA,EAAA,GAAA,CAAwB,EAAA,GAAA,QAAA,CAAS,SAAT,IAAA,GAAA,KAAA,CAAA,GAAA,EAAA,CAAe,YAAA;AAEzC,MAAA,IAAI,CAAC,KAAA,EAAO;AACV,QAAA,MAAM,IAAI,MAAM,oDAAoD,CAAA;AAAA,MACtE;AAEA,MAAA,IAAA,CAAK,WAAA,GAAc,KAAA;AACnB,MAAA,CAAA,EAAA,GAAA,CAAA,EAAA,GAAA,IAAA,CAAK,MAAA,EAAO,gBAAZ,IAAA,GAAA,KAAA,CAAA,GAAA,EAAA,CAAA,IAAA,CAAA,EAAA,EAA0B,KAAA,CAAA;AAC1B,MAAA,OAAO,KAAA;AAAA,IACT,SAAS,KAAA,EAAO;AACd,MAAA,IAAA,CAAK,YAAY,KAAK,CAAA;AACtB,MAAA,MAAM,KAAA;AAAA,IACR;AAAA,EACF;AAAA,EAEQ,YAAY,KAAA,EAAsB;AAvF5C,IAAA,IAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA;AAwFI,IAAA,IAAI,KAAA,CAAM,YAAA,CAAa,KAAK,CAAA,EAAG;AAC7B,MAAA,MAAM,UAAA,GAAa,KAAA;AACnB,MAAA,CAAA,EAAA,GAAA,CAAA,EAAA,GAAA,IAAA,CAAK,MAAA,EAAO,YAAZ,IAAA,GAAA,MAAA,GAAA,EAAA,CAAA,IAAA,CAAA,EAAA,EAAsB,UAAA,CAAA;AAAA,IACxB,CAAA,MAAO;AACL,MAAA,CAAA,EAAA,GAAA,CAAA,EAAA,GAAA,IAAA,CAAK,MAAA,EAAO,YAAZ,IAAA,GAAA,MAAA,GAAA,EAAA,CAAA,IAAA,CAAA,EAAA,EAAsB,KAAA,CAAA;AAAA,IACxB;AAAA,EACF;AAAA,EAEA,MAAM,iBAAA,CACJ,KAAA,EACA,UAAA,EACwC;AAnG5C,IAAA,IAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA;AAoGI,IAAA,IAAI,CAAC,KAAK,WAAA,EAAa;AACrB,MAAA,MAAM,IAAI,MAAM,kDAAkD,CAAA;AAAA,IACpE;AAEA,IAAA,CAAA,EAAA,GAAA,CAAA,EAAA,GAAA,IAAA,CAAK,MAAA,EAAO,oBAAZ,IAAA,GAAA,MAAA,GAAA,EAAA,CAAA,IAAA,CAAA,EAAA,EAA8B,KAAA,CAAA;AAC9B,IAAA,CAAA,EAAA,GAAA,CAAA,EAAA,GAAA,IAAA,CAAK,MAAA,EAAO,uBAAZ,IAAA,GAAA,MAAA,GAAA,EAAA,CAAA,IAAA,CAAA,EAAA,EAAiC,UAAA,CAAA;AAEjC,IAAA,MAAM,WAAA,GAAc,MAAM,IAAA,CAAK,iBAAA,CAAkB,OAAO,UAAU,CAAA;AAElE,IAAA,IAAA,CAAI,EAAA,GAAA,WAAA,IAAA,IAAA,GAAA,MAAA,GAAA,WAAA,CAAa,YAAA,KAAb,IAAA,GAAA,MAAA,GAAA,EAAA,CAA2B,SAAA,EAAW;AACxC,MAAA,CAAA,EAAA,GAAA,CAAA,EAAA,GAAA,IAAA,CAAK,MAAA,EAAO,YAAA,KAAZ,IAAA,GAAA,MAAA,GAAA,EAAA,CAAA,IAAA,CAAA,EAAA,EAA2B,WAAA,CAAY,YAAA,CAAA;AAAA,IACzC;AAEA,IAAA,OAAO,WAAA;AAAA,EACT;AAAA,EAEA,MAAc,oBAAA,GAEZ;AACA,IAAA,IACE,OAAO,cAAc,WAAA,IACrB,EAAE,iBAAiB,SAAA,CAAA,IACnB,CAAC,UAAU,WAAA,EACX;AACA,MAAA,OAAO,IAAA;AAAA,IACT;AAEA,IAAA,IAAI;AACF,MAAA,MAAM,WAAW,MAAM,IAAI,OAAA,CAA6B,CAAC,SAAS,MAAA,KAAW;AAC3E,QAAA,SAAA,CAAU,WAAA,CAAY,kBAAA,CAAmB,OAAA,EAAS,MAAA,EAAQ;AAAA,UACxD,kBAAA,EAAoB,IAAA;AAAA,UACpB,OAAA,EAAS,GAAA;AAAA,UACT,UAAA,EAAY;AAAA,SACb,CAAA;AAAA,MACH,CAAC,CAAA;AAED,MAAA,MAAM,EAAE,QAAA,EAAU,SAAA,EAAW,QAAA,KAAa,QAAA,CAAS,MAAA;AACnD,MAAA,MAAM,YAAA,GACJ,OAAO,QAAA,KAAa,QAAA,IAAY,CAAC,MAAA,CAAO,KAAA,CAAM,QAAQ,CAAA,GAAI,QAAA,GAAW,CAAA;AAEvE,MAAA,OAAO,EAAE,QAAA,EAAU,SAAA,EAAW,QAAA,EAAU,YAAA,EAAa;AAAA,IACvD,CAAA,CAAA,MAAQ;AACN,MAAA,OAAO,IAAA;AAAA,IACT;AAAA,EACF;AAAA,EAEA,MAAc,iBAAA,CACZ,KAAA,EACA,UAAA,EACwC;AArJ5C,IAAA,IAAA,EAAA;AAsJI,IAAA,MAAM,QAAA,GAAW,IAAI,QAAA,EAAS;AAE9B,IAAA,IAAI,IAAA,CAAK,MAAA,CAAO,OAAA,KAAY,KAAA,EAAO;AACjC,MAAA,QAAA,CAAS,MAAA,CAAO,SAAA,EAAW,IAAA,CAAK,MAAA,CAAO,IAAI,CAAA;AAAA,IAC7C,CAAA,MAAO;AACL,MAAA,QAAA,CAAS,MAAA,CAAO,YAAA,EAAc,IAAA,CAAK,MAAA,CAAO,IAAI,CAAA;AAAA,IAChD;AACA,IAAA,QAAA,CAAS,MAAA,CAAO,iBAAiB,MAAM,CAAA;AACvC,IAAA,QAAA,CAAS,MAAA,CAAO,IAAA,EAAM,CAAA,EAAG,UAAA,CAAW,EAAE,CAAA,CAAE,CAAA;AACxC,IAAA,QAAA,CAAS,MAAA,CAAO,IAAA,EAAM,CAAA,EAAG,UAAA,CAAW,EAAE,CAAA,CAAE,CAAA;AACxC,IAAA,QAAA,CAAS,MAAA,CAAO,IAAA,EAAM,CAAA,EAAG,UAAA,CAAW,EAAE,CAAA,CAAE,CAAA;AACxC,IAAA,QAAA,CAAS,MAAA,CAAO,IAAA,EAAM,CAAA,EAAG,UAAA,CAAW,EAAE,CAAA,CAAE,CAAA;AACxC,IAAA,QAAA,CAAS,MAAA,CAAO,OAAA,EAAS,CAAA,EAAG,KAAA,CAAM,KAAK,CAAA,CAAE,CAAA;AACzC,IAAA,QAAA,CAAS,MAAA,CAAO,QAAA,EAAU,CAAA,EAAG,KAAA,CAAM,MAAM,CAAA,CAAE,CAAA;AAC3C,IAAA,QAAA,CAAS,MAAA,CAAO,YAAA,EAAc,KAAA,CAAM,IAAI,CAAA;AACxC,IAAA,IAAI,IAAA,CAAK,OAAO,wBAAA,EAA0B;AACxC,MAAA,QAAA,CAAS,MAAA,CAAO,4BAA4B,MAAM,CAAA;AAAA,IACpD;AACA,IAAA,IAAI,IAAA,CAAK,OAAO,WAAA,EAAa;AAC3B,MAAA,MAAM,UAAA,GAAa,MAAM,IAAA,CAAK,oBAAA,EAAqB;AACnD,MAAA,IAAI,UAAA,EAAY;AACd,QAAA,MAAM,EAAE,QAAA,EAAU,SAAA,EAAW,QAAA,EAAS,GAAI,UAAA;AAC1C,QAAA,MAAM,UAAU,CAAA,EAAG,QAAQ,CAAA,CAAA,EAAI,SAAS,IAAI,QAAQ,CAAA,CAAA;AACpD,QAAA,QAAA,CAAS,MAAA,CAAO,WAAW,OAAO,CAAA;AAAA,MACpC;AAAA,IACF;AAEA,IAAA,IAAI;AACF,MAAA,MAAM,QAAA,GAAW,MAAM,KAAA,CAAM,IAAA;AAAA,QAC3B,KAAK,SAAA,CAAU,QAAA;AAAA,QACf,QAAA;AAAA,QACA;AAAA,UACE,OAAA,EAAS;AAAA,YACP,aAAA,EAAe,CAAA,OAAA,EAAU,IAAA,CAAK,WAAW,CAAA;AAAA;AAC3C;AACF,OACF;AAEA,MAAA,MAAM,OAAO,QAAA,CAAS,IAAA;AACtB,MAAA,IAAI,CAAC,KAAK,SAAA,EAAW;AACnB,QAAA,OAAO,IAAA;AAAA,MACT;AAEA,MAAA,MAAM,MAAA,GAAiC;AAAA,QACrC,YAAA,EAAc;AAAA,OAChB;AAEA,MAAA,IACE,IAAA,CAAK,MAAA,CAAO,QAAA,IACZ,IAAA,CAAK,MAAA,CAAO,YAAY,KAAA,KAAA,CACxB,EAAA,GAAA,IAAA,CAAK,QAAA,KAAL,IAAA,GAAA,KAAA,CAAA,GAAA,EAAA,CAAe,MAAA,CAAA,EACf;AACA,QAAA,MAAM,OAAA,GAAU,IAAA,CAAK,QAAA,CAAS,CAAC,CAAA;AAC/B,QAAA,MAAM,MAAA,GAAS,IAAA,CAAK,eAAA,CAAgB,OAAO,CAAA;AAC3C,QAAA,IAAI,MAAA,EAAQ;AACV,UAAA,MAAA,CAAO,UAAA,GAAa,MAAA;AAAA,QACtB,CAAA,MAAO;AACL,UAAA,MAAM,UAAA,GAAa,MAAM,IAAA,CAAK,eAAA,CAAgB,OAAO,CAAA;AACrD,UAAA,IAAI,UAAA,EAAY;AACd,YAAA,IAAA,CAAK,eAAA,CAAgB,OAAO,CAAA,GAAI,UAAA;AAChC,YAAA,MAAA,CAAO,UAAA,GAAa,UAAA;AAAA,UACtB;AAAA,QACF;AAAA,MACF;AAEA,MAAA,OAAO,MAAA;AAAA,IACT,SAAS,KAAA,EAAO;AACd,MAAA,IAAA,CAAK,YAAY,KAAK,CAAA;AACtB,MAAA,OAAO,IAAA;AAAA,IACT;AAAA,EACF;AAAA,EAEA,MAAc,gBAAgB,OAAA,EAA0D;AACtF,IAAA,IAAI;AACF,MAAA,MAAM,QAAA,GAAW,MAAM,KAAA,CAAM,GAAA;AAAA,QAC3B,CAAA,EAAG,IAAA,CAAK,SAAA,CAAU,aAAa,GAAG,OAAO,CAAA,CAAA;AAAA,QACzC;AAAA,UACE,OAAA,EAAS;AAAA,YACP,aAAA,EAAe,CAAA,OAAA,EAAU,IAAA,CAAK,WAAW,CAAA;AAAA;AAC3C;AACF,OACF;AACA,MAAA,OAAO,QAAA,CAAS,IAAA;AAAA,IAClB,SAAS,KAAA,EAAO;AACd,MAAA,IAAA,CAAK,YAAY,KAAK,CAAA;AACtB,MAAA,OAAO,IAAA;AAAA,IACT;AAAA,EACF;AAAA,EAEA,MAAc,mBAAmB,QAAA,EAAuD;AACtF,IAAA,IAAI;AACF,MAAA,MAAM,QAAA,GAAW,MAAM,KAAA,CAAM,GAAA;AAAA,QAC3B,CAAA,EAAG,IAAA,CAAK,SAAA,CAAU,gBAAgB,GAAG,QAAQ,CAAA,CAAA;AAAA,QAC7C;AAAA,UACE,OAAA,EAAS;AAAA,YACP,aAAA,EAAe,CAAA,OAAA,EAAU,IAAA,CAAK,WAAW,CAAA;AAAA;AAC3C;AACF,OACF;AACA,MAAA,OAAO,QAAA,CAAS,IAAA;AAAA,IAClB,SAAS,KAAA,EAAO;AACd,MAAA,IAAA,CAAK,YAAY,KAAK,CAAA;AACtB,MAAA,OAAO,IAAA;AAAA,IACT;AAAA,EACF;AACF","file":"index.js","sourcesContent":["import type { ILocalizeAndMapDetails, MapType } from './types';\n\nexport interface IMultisetSdkConfig {\n clientId: string;\n clientSecret: string;\n /** Map or map-set code used for localization. */\n code: string;\n /** Map or map-set type ('map' or 'map-set'). */\n mapType: MapType;\n endpoints?: Partial<IMultisetSdkEndpoints>;\n /** If true, show the mesh in the AR session. Default is false. */\n showMesh?: boolean;\n /** If true, automatically start a localization run when the AR session starts. */\n autoLocalize?: boolean;\n /** If true, automatically re-localize when tracking is lost and then recovered. */\n relocalization?: boolean;\n /** When enabled, only accept a localization result if confidence >= confidenceThreshold. */\n confidenceCheck?: boolean;\n /** Minimum confidence (0.2–0.8) required when confidenceCheck is enabled. */\n confidenceThreshold?: number;\n /** Total single-frame attempts per localization run (1–5). */\n requestAttempts?: number;\n /** Time in seconds between attempts in a localization run (1–5). */\n localizationInterval?: number;\n /** Include device geo pose as a hint in localization requests. */\n passGeoPose?: boolean;\n /** Request geo coordinates in the localization response (if supported by backend). */\n geoCoordinatesInResponse?: boolean;\n\n /** Invoked at the start of a localization run. */\n onLocalizationInit?: () => void;\n /** Invoked after a successful localization that meets confidence criteria (if enabled). */\n onLocalizationSuccess?: (result: ILocalizeAndMapDetails) => void;\n /** Invoked when all attempts fail or the best result is below the confidence threshold. */\n onLocalizationFailure?: (reason?: string) => void;\n\n /** Called after a successful authorization with the access token. */\n onAuthorize?: (token: string) => void;\n /** Called whenever a camera frame is captured and sent for localization. */\n onFrameCaptured?: (payload: IFrameCaptureEvent) => void;\n /** Called with the camera intrinsics used for a localization request. */\n onCameraIntrinsics?: (intrinsics: ICameraIntrinsicsEvent) => void;\n /** Called with the raw pose/localization result returned by the backend. */\n onPoseResult?: (payload: IPoseResultEvent) => void;\n /** Called when any error occurs during authorization or localization. */\n onError?: (error: unknown) => void;\n}\n\nexport interface IMultisetSdkEndpoints {\n authUrl: string;\n queryUrl: string;\n mapDetailsUrl: string;\n fileDownloadUrl: string;\n mapSetDetailsUrl: string;\n}\n\nexport interface IFrameCaptureEvent {\n blob: Blob;\n width: number;\n height: number;\n}\n\nexport interface ICameraIntrinsicsEvent {\n fx: number;\n fy: number;\n px: number;\n py: number;\n width: number;\n height: number;\n}\n\nexport interface IPoseResultEvent {\n poseFound: boolean;\n position: {\n x: number;\n y: number;\n z: number;\n };\n rotation: {\n x: number;\n y: number;\n z: number;\n w: number;\n };\n mapIds: string[];\n confidence?: number;\n}\n\nexport interface ILocalizeResultEvent {\n frame: IFrameCaptureEvent;\n intrinsics: ICameraIntrinsicsEvent;\n response: ILocalizeAndMapDetails | null;\n}\n\nexport const DEFAULT_ENDPOINTS: IMultisetSdkEndpoints = {\n authUrl: 'https://api.multiset.ai/v1/m2m/token',\n queryUrl: 'https://api.multiset.ai/v1/vps/map/query-form',\n mapDetailsUrl: 'https://api.multiset.ai/v1/vps/map/',\n mapSetDetailsUrl: 'https://api.multiset.ai/v1/vps/map-set/',\n fileDownloadUrl: 'https://api.multiset.ai/v1/file',\n};\n","import axios, { AxiosError } from 'axios';\nimport type {\n IMultisetSdkConfig,\n IMultisetSdkEndpoints,\n IFrameCaptureEvent,\n ICameraIntrinsicsEvent,\n} from './config';\nimport { DEFAULT_ENDPOINTS } from './config';\nimport type {\n ILocalizeAndMapDetails,\n ILocalizeResponse,\n IGetMapsDetailsResponse,\n IMapSetMapsResponse,\n} from './types';\n\nexport class MultisetClient {\n private readonly endpoints: IMultisetSdkEndpoints;\n private accessToken: string | null = null;\n private mapDetailsCache: Record<string, IGetMapsDetailsResponse> = {};\n\n constructor(private readonly config: IMultisetSdkConfig) {\n this.config = config;\n this.endpoints = {\n ...DEFAULT_ENDPOINTS,\n ...config.endpoints,\n };\n }\n\n get token(): string | null {\n return this.accessToken;\n }\n\n getConfig(): IMultisetSdkConfig {\n return this.config;\n }\n\n async downloadFile(key: string): Promise<string> {\n if (!this.accessToken || !key) {\n return '';\n }\n\n try {\n const response = await axios.get<{ url: string }>(\n `${this.endpoints.fileDownloadUrl}?key=${encodeURIComponent(key)}`,\n {\n headers: {\n Authorization: `Bearer ${this.accessToken}`,\n },\n }\n );\n\n return response.status === 200 ? response.data.url : '';\n } catch (error) {\n this.handleError(error);\n return '';\n }\n }\n\n async authorize(): Promise<string> {\n try {\n const response = await axios.post(\n this.endpoints.authUrl,\n {},\n {\n auth: {\n username: this.config.clientId,\n password: this.config.clientSecret,\n },\n }\n );\n\n const token: string | undefined =\n response.data?.token ?? response.data?.access_token;\n\n if (!token) {\n throw new Error('Authorization succeeded but no token was returned.');\n }\n\n this.accessToken = token;\n this.config.onAuthorize?.(token);\n return token;\n } catch (error) {\n this.handleError(error);\n throw error;\n }\n }\n\n private handleError(error: unknown): void {\n if (axios.isAxiosError(error)) {\n const axiosError = error as AxiosError;\n this.config.onError?.(axiosError);\n } else {\n this.config.onError?.(error);\n }\n }\n\n async localizeWithFrame(\n frame: IFrameCaptureEvent,\n intrinsics: ICameraIntrinsicsEvent\n ): Promise<ILocalizeAndMapDetails | null> {\n if (!this.accessToken) {\n throw new Error('Access token is missing. Call authorize() first.');\n }\n\n this.config.onFrameCaptured?.(frame);\n this.config.onCameraIntrinsics?.(intrinsics);\n\n const queryResult = await this.queryLocalization(frame, intrinsics);\n\n if (queryResult?.localizeData?.poseFound) {\n this.config.onPoseResult?.(queryResult.localizeData);\n }\n\n return queryResult;\n }\n\n private async getGeoPoseComponents(): Promise<\n { latitude: number, longitude: number, altitude: number } | null\n > {\n if (\n typeof navigator === 'undefined' ||\n !('geolocation' in navigator) ||\n !navigator.geolocation\n ) {\n return null;\n }\n\n try {\n const position = await new Promise<GeolocationPosition>((resolve, reject) => {\n navigator.geolocation.getCurrentPosition(resolve, reject, {\n enableHighAccuracy: true,\n timeout: 10000,\n maximumAge: 0,\n });\n });\n\n const { latitude, longitude, altitude } = position.coords;\n const safeAltitude =\n typeof altitude === 'number' && !Number.isNaN(altitude) ? altitude : 0.0;\n\n return { latitude, longitude, altitude: safeAltitude };\n } catch {\n return null;\n }\n }\n\n private async queryLocalization(\n frame: IFrameCaptureEvent,\n intrinsics: ICameraIntrinsicsEvent\n ): Promise<ILocalizeAndMapDetails | null> {\n const formData = new FormData();\n\n if (this.config.mapType === 'map') {\n formData.append('mapCode', this.config.code);\n } else {\n formData.append('mapSetCode', this.config.code);\n }\n formData.append('isRightHanded', 'true');\n formData.append('fx', `${intrinsics.fx}`);\n formData.append('fy', `${intrinsics.fy}`);\n formData.append('px', `${intrinsics.px}`);\n formData.append('py', `${intrinsics.py}`);\n formData.append('width', `${frame.width}`);\n formData.append('height', `${frame.height}`);\n formData.append('queryImage', frame.blob);\n if (this.config.geoCoordinatesInResponse) {\n formData.append('geoCoordinatesInResponse', 'true');\n }\n if (this.config.passGeoPose) {\n const components = await this.getGeoPoseComponents();\n if (components) {\n const { latitude, longitude, altitude } = components;\n const geoHint = `${latitude},${longitude},${altitude}`;\n formData.append('geoHint', geoHint);\n }\n }\n\n try {\n const response = await axios.post<ILocalizeResponse>(\n this.endpoints.queryUrl,\n formData,\n {\n headers: {\n Authorization: `Bearer ${this.accessToken}`,\n },\n }\n );\n\n const data = response.data;\n if (!data.poseFound) {\n return null;\n }\n\n const result: ILocalizeAndMapDetails = {\n localizeData: data,\n };\n\n if (\n this.config.showMesh &&\n this.config.mapType === 'map' &&\n data.mapCodes?.length\n ) {\n const mapCode = data.mapCodes[0];\n const cached = this.mapDetailsCache[mapCode];\n if (cached) {\n result.mapDetails = cached;\n } else {\n const mapDetails = await this.fetchMapDetails(mapCode);\n if (mapDetails) {\n this.mapDetailsCache[mapCode] = mapDetails;\n result.mapDetails = mapDetails;\n }\n }\n }\n\n return result;\n } catch (error) {\n this.handleError(error);\n return null;\n }\n }\n\n private async fetchMapDetails(mapCode: string): Promise<IGetMapsDetailsResponse | null> {\n try {\n const response = await axios.get<IGetMapsDetailsResponse>(\n `${this.endpoints.mapDetailsUrl}${mapCode}`,\n {\n headers: {\n Authorization: `Bearer ${this.accessToken}`,\n },\n }\n );\n return response.data;\n } catch (error) {\n this.handleError(error);\n return null;\n }\n }\n\n private async fetchMapSetDetails(mapSetId: string): Promise<IMapSetMapsResponse | null> {\n try {\n const response = await axios.get<IMapSetMapsResponse>(\n `${this.endpoints.mapSetDetailsUrl}${mapSetId}`,\n {\n headers: {\n Authorization: `Bearer ${this.accessToken}`,\n },\n }\n );\n return response.data;\n } catch (error) {\n this.handleError(error);\n return null;\n }\n }\n}\n"]}
1
+ {"version":3,"sources":["../../src/lib/core/config.ts","../../src/lib/core/client.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { ILocalizeAndMapDetails, MapType } from './types';\n\n/** The subset of config that is safe to expose publicly (no credentials). */\nexport type IMultisetPublicConfig = Omit<IMultisetSdkConfig, 'clientId' | 'clientSecret'>;\n\nexport interface IMultisetSdkConfig {\n clientId: string;\n clientSecret: string;\n /** Map or map-set code used for localization. */\n code: string;\n /** Map or map-set type ('map' or 'map-set'). */\n mapType: MapType;\n endpoints?: Partial<IMultisetSdkEndpoints>;\n /** If true, show the mesh in the AR session. Default is false. */\n showMesh?: boolean;\n /** If true, show the coordinate gizmo when a pose is found. Defaults to true. */\n showGizmo?: boolean;\n /** If true, automatically start a localization run when the AR session starts. */\n autoLocalize?: boolean;\n /** If true, automatically re-localize when tracking is lost and then recovered. */\n relocalization?: boolean;\n /** When enabled, only accept a localization result if confidence >= confidenceThreshold. */\n confidenceCheck?: boolean;\n /** Minimum confidence (0.2–0.8) required when confidenceCheck is enabled. */\n confidenceThreshold?: number;\n /** Total single-frame attempts per localization run (1–5). */\n requestAttempts?: number;\n /** Time in seconds between attempts in a localization run (1–5). */\n localizationInterval?: number;\n /** Include device geo pose as a hint in localization requests. */\n passGeoPose?: boolean;\n /** Request geo coordinates in the localization response (if supported by backend). */\n geoCoordinatesInResponse?: boolean;\n /** Max time in ms to wait for a valid viewer pose before failing. Default 10000. */\n localizationTrackingTimeoutMs?: number;\n\n /** Invoked at the start of a localization run. */\n onLocalizationInit?: () => void;\n /** Invoked after a successful localization that meets confidence criteria (if enabled). */\n onLocalizationSuccess?: (result: ILocalizeAndMapDetails) => void;\n /** Invoked when all attempts fail or the best result is below the confidence threshold. */\n onLocalizationFailure?: (reason?: string) => void;\n\n /** Called after a successful authorization with the access token. */\n onAuthorize?: (token: string) => void;\n /** Called whenever a camera frame is captured and sent for localization. */\n onFrameCaptured?: (payload: IFrameCaptureEvent) => void;\n /** Called with the camera intrinsics used for a localization request. */\n onCameraIntrinsics?: (intrinsics: ICameraIntrinsicsEvent) => void;\n /** Called with the raw pose/localization result returned by the backend. */\n onPoseResult?: (payload: IPoseResultEvent) => void;\n /** Called when any error occurs during authorization or localization. */\n onError?: (error: unknown) => void;\n}\n\nexport interface IMultisetSdkEndpoints {\n authUrl: string;\n queryUrl: string;\n mapDetailsUrl: string;\n fileDownloadUrl: string;\n mapSetDetailsUrl: string;\n}\n\nexport interface IFrameCaptureEvent {\n blob: Blob;\n width: number;\n height: number;\n}\n\nexport interface ICameraIntrinsicsEvent {\n fx: number;\n fy: number;\n px: number;\n py: number;\n width: number;\n height: number;\n}\n\nexport interface IPoseResultEvent {\n poseFound: boolean;\n position: {\n x: number;\n y: number;\n z: number;\n };\n rotation: {\n x: number;\n y: number;\n z: number;\n w: number;\n };\n mapIds: string[];\n confidence?: number;\n}\n\nexport interface ILocalizeResultEvent {\n frame: IFrameCaptureEvent;\n intrinsics: ICameraIntrinsicsEvent;\n response: ILocalizeAndMapDetails | null;\n}\n\nexport const DEFAULT_ENDPOINTS: IMultisetSdkEndpoints = {\n authUrl: 'https://api.multiset.ai/v1/m2m/token',\n queryUrl: 'https://api.multiset.ai/v1/vps/map/query-form',\n mapDetailsUrl: 'https://api.multiset.ai/v1/vps/map/',\n mapSetDetailsUrl: 'https://api.multiset.ai/v1/vps/map-set/',\n fileDownloadUrl: 'https://api.multiset.ai/v1/file',\n};\n","import axios, { AxiosError } from 'axios';\nimport type {\n IMultisetSdkConfig,\n IMultisetPublicConfig,\n IMultisetSdkEndpoints,\n IFrameCaptureEvent,\n ICameraIntrinsicsEvent,\n} from './config';\nimport { DEFAULT_ENDPOINTS } from './config';\nimport type {\n ILocalizeAndMapDetails,\n ILocalizeResponse,\n IGetMapsDetailsResponse,\n} from './types';\n\nexport class MultisetClient {\n private readonly credentials: { clientId: string; clientSecret: string };\n private readonly config: IMultisetPublicConfig;\n private readonly endpoints: IMultisetSdkEndpoints;\n private accessToken: string | null = null;\n private mapDetailsCache: Record<string, IGetMapsDetailsResponse> = {};\n\n constructor(input: IMultisetSdkConfig) {\n const { clientId, clientSecret, ...rest } = input;\n this.credentials = { clientId, clientSecret };\n this.config = rest;\n this.endpoints = {\n ...DEFAULT_ENDPOINTS,\n ...rest.endpoints,\n };\n }\n\n get token(): string | null {\n return this.accessToken;\n }\n\n getConfig(): IMultisetPublicConfig {\n return this.config;\n }\n\n async downloadFile(key: string): Promise<string> {\n if (!this.accessToken || !key) {\n return '';\n }\n\n try {\n const response = await axios.get<{ url: string }>(\n `${this.endpoints.fileDownloadUrl}?key=${encodeURIComponent(key)}`,\n {\n headers: {\n Authorization: `Bearer ${this.accessToken}`,\n },\n }\n );\n\n return response.status === 200 ? response.data.url : '';\n } catch (error) {\n this.handleError(error);\n return '';\n }\n }\n\n async authorize(): Promise<string> {\n try {\n const response = await axios.post(\n this.endpoints.authUrl,\n {},\n {\n auth: {\n username: this.credentials.clientId,\n password: this.credentials.clientSecret,\n },\n }\n );\n\n const token: string | undefined =\n response.data?.token ?? response.data?.access_token;\n\n if (!token) {\n throw new Error('Authorization succeeded but no token was returned.');\n }\n\n this.accessToken = token;\n this.config.onAuthorize?.(token);\n return token;\n } catch (error) {\n this.handleError(error);\n throw error;\n }\n }\n\n private handleError(error: unknown): void {\n if (axios.isAxiosError(error)) {\n const axiosError = error as AxiosError;\n this.config.onError?.(axiosError);\n } else {\n this.config.onError?.(error);\n }\n }\n\n async localizeWithFrame(\n frame: IFrameCaptureEvent,\n intrinsics: ICameraIntrinsicsEvent\n ): Promise<ILocalizeAndMapDetails | null> {\n if (!this.accessToken) {\n throw new Error('Access token is missing. Call authorize() first.');\n }\n\n this.config.onFrameCaptured?.(frame);\n this.config.onCameraIntrinsics?.(intrinsics);\n\n const queryResult = await this.queryLocalization(frame, intrinsics);\n\n if (queryResult?.localizeData?.poseFound) {\n this.config.onPoseResult?.(queryResult.localizeData);\n }\n\n return queryResult;\n }\n\n private async getGeoPoseComponents(): Promise<\n { latitude: number, longitude: number, altitude: number } | null\n > {\n if (\n typeof navigator === 'undefined' ||\n !('geolocation' in navigator) ||\n !navigator.geolocation\n ) {\n return null;\n }\n\n try {\n const position = await new Promise<GeolocationPosition>((resolve, reject) => {\n navigator.geolocation.getCurrentPosition(resolve, reject, {\n enableHighAccuracy: true,\n timeout: 10000,\n maximumAge: 0,\n });\n });\n\n const { latitude, longitude, altitude } = position.coords;\n const safeAltitude =\n typeof altitude === 'number' && !Number.isNaN(altitude) ? altitude : 0.0;\n\n return { latitude, longitude, altitude: safeAltitude };\n } catch {\n return null;\n }\n }\n\n private async queryLocalization(\n frame: IFrameCaptureEvent,\n intrinsics: ICameraIntrinsicsEvent\n ): Promise<ILocalizeAndMapDetails | null> {\n const formData = new FormData();\n\n if (this.config.mapType === 'map') {\n formData.append('mapCode', this.config.code);\n } else {\n formData.append('mapSetCode', this.config.code);\n }\n formData.append('isRightHanded', 'true');\n formData.append('fx', `${intrinsics.fx}`);\n formData.append('fy', `${intrinsics.fy}`);\n formData.append('px', `${intrinsics.px}`);\n formData.append('py', `${intrinsics.py}`);\n formData.append('width', `${frame.width}`);\n formData.append('height', `${frame.height}`);\n formData.append('queryImage', frame.blob);\n if (this.config.geoCoordinatesInResponse) {\n formData.append('geoCoordinatesInResponse', 'true');\n }\n if (this.config.passGeoPose) {\n const components = await this.getGeoPoseComponents();\n if (components) {\n const { latitude, longitude, altitude } = components;\n const geoHint = `${latitude},${longitude},${altitude}`;\n formData.append('geoHint', geoHint);\n }\n }\n\n try {\n const response = await axios.post<ILocalizeResponse>(\n this.endpoints.queryUrl,\n formData,\n {\n headers: {\n Authorization: `Bearer ${this.accessToken}`,\n },\n }\n );\n\n const data = response.data;\n if (!data.poseFound) {\n return null;\n }\n\n const result: ILocalizeAndMapDetails = {\n localizeData: data,\n };\n\n if (\n this.config.showMesh &&\n data.mapCodes?.length\n ) {\n const mapCode = data.mapCodes[0];\n const cached = this.mapDetailsCache[mapCode];\n if (cached) {\n result.mapDetails = cached;\n } else {\n const mapDetails = await this.fetchMapDetails(mapCode);\n if (mapDetails) {\n this.mapDetailsCache[mapCode] = mapDetails;\n result.mapDetails = mapDetails;\n }\n }\n }\n\n return result;\n } catch (error) {\n this.handleError(error);\n return null;\n }\n }\n\n private async fetchMapDetails(mapCode: string): Promise<IGetMapsDetailsResponse | null> {\n try {\n const response = await axios.get<IGetMapsDetailsResponse>(\n `${this.endpoints.mapDetailsUrl}${mapCode}`,\n {\n headers: {\n Authorization: `Bearer ${this.accessToken}`,\n },\n }\n );\n return response.data;\n } catch (error) {\n this.handleError(error);\n return null;\n }\n }\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- export { DEFAULT_ENDPOINTS, ICameraIntrinsicsEvent, IFrameCaptureEvent, IGetMapsDetailsResponse, ILocalizeAndMapDetails, ILocalizeResponse, ILocalizeResultEvent, IMapSetMapsResponse, IMultisetClientOptions, IMultisetClientOptions as IMultisetSdkConfig, IMultisetSdkEndpoints, IPoseResultEvent, MapType, MultisetClient } from './core/index.js';
1
+ export { DEFAULT_ENDPOINTS, ICameraIntrinsicsEvent, IFrameCaptureEvent, IGetMapsDetailsResponse, ILocalizeAndMapDetails, ILocalizeResponse, ILocalizeResultEvent, IMapSetMapsResponse, IMultisetClientOptions, IMultisetPublicConfig, IMultisetClientOptions as IMultisetSdkConfig, IMultisetSdkEndpoints, IPoseResultEvent, MapType, MultisetClient } from './core/index.js';
2
2
  export { IWebxrControllerOptions, WebxrController } from './webxr/index.js';
3
3
  import 'three';