@tileflow/static 0.1.0-alpha.3 → 0.1.0-alpha.30

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.
@@ -0,0 +1,600 @@
1
+ import {
2
+ MAX_OVERLAY_LATITUDE,
3
+ isSupportedStaticSymbolCodePoint,
4
+ jsonByteLength,
5
+ normalizeStaticCoordinate,
6
+ normalizeStaticOverlay,
7
+ roundNumber,
8
+ staticPaddingSchema,
9
+ staticSceneLimits,
10
+ staticSceneSchema,
11
+ stripUndefined
12
+ } from "./chunk-6DWVCK64.js";
13
+
14
+ // src/auto-fit.ts
15
+ import { z as z2 } from "zod";
16
+
17
+ // src/errors.ts
18
+ import { z } from "zod";
19
+ var safeErrorTextSchema = z.string().min(1).max(512).refine(hasNoControlCharacters, { message: "Expected no control characters" });
20
+ var safeErrorIdentifierSchema = z.string().min(1).max(128).regex(/^[A-Za-z0-9][A-Za-z0-9._:-]*$/u);
21
+ var staticMapErrorResponseSchema = z.object({
22
+ code: safeErrorIdentifierSchema.optional(),
23
+ error: safeErrorTextSchema,
24
+ remainingUnits: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER).optional(),
25
+ requestId: safeErrorIdentifierSchema.optional(),
26
+ requiredPlan: safeErrorIdentifierSchema.optional(),
27
+ retryable: z.boolean().optional()
28
+ }).strip();
29
+ var StaticMapError = class extends Error {
30
+ code;
31
+ remainingUnits;
32
+ requestId;
33
+ requiredPlan;
34
+ response;
35
+ retryable;
36
+ status;
37
+ constructor(response, status) {
38
+ const parsed = staticMapErrorResponseSchema.parse(response);
39
+ super(parsed.error);
40
+ this.name = "StaticMapError";
41
+ this.code = parsed.code ?? null;
42
+ this.remainingUnits = parsed.remainingUnits ?? null;
43
+ this.requestId = parsed.requestId ?? null;
44
+ this.requiredPlan = parsed.requiredPlan ?? null;
45
+ this.response = Object.freeze(parsed);
46
+ this.retryable = parsed.retryable ?? null;
47
+ this.status = status;
48
+ }
49
+ };
50
+ function hasNoControlCharacters(value) {
51
+ return !Array.from(value).some((character) => {
52
+ const codePoint = character.codePointAt(0) ?? 0;
53
+ return codePoint <= 31 || codePoint === 127;
54
+ });
55
+ }
56
+
57
+ // src/auto-fit.ts
58
+ var autoFitOverlayReferenceSchema = z2.object({
59
+ id: z2.string().min(1).max(64),
60
+ index: z2.number().int().min(0).max(23),
61
+ type: z2.enum(["circle", "line", "marker", "polygon", "symbol"])
62
+ }).strict();
63
+ var autoFitViewportSchema = z2.object({
64
+ height: z2.number().int().min(64).max(1280),
65
+ width: z2.number().int().min(64).max(1280)
66
+ }).strict();
67
+ var autoFitDetailsSchema = z2.object({
68
+ overlay: autoFitOverlayReferenceSchema.optional(),
69
+ padding: staticPaddingSchema.optional(),
70
+ projection: z2.enum(["globe", "mercator"]).optional(),
71
+ requiredInsets: staticPaddingSchema.optional(),
72
+ ringIndex: z2.number().int().min(0).max(15).optional(),
73
+ segmentIndex: z2.number().int().min(0).max(1999).optional(),
74
+ viewport: autoFitViewportSchema.optional()
75
+ }).strict();
76
+ var staticOverlayInvalidDetailsSchema = z2.object({
77
+ latitude: z2.number().finite(),
78
+ limit: z2.literal(MAX_OVERLAY_LATITUDE),
79
+ overlay: autoFitOverlayReferenceSchema
80
+ }).strict();
81
+ var staticLabelUnsupportedDetailsSchema = z2.object({
82
+ codePoint: z2.number().int().min(0).max(1114111),
83
+ overlay: autoFitOverlayReferenceSchema.extend({ type: z2.literal("symbol") }).strict()
84
+ }).strict();
85
+ var staticIconUnavailableDetailsSchema = z2.object({
86
+ icon: z2.string().max(64).regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/u),
87
+ overlay: autoFitOverlayReferenceSchema.extend({ type: z2.literal("symbol") }).strict()
88
+ }).strict();
89
+ var staticSymbolTooLargeDetailsSchema = z2.object({
90
+ height: z2.number().finite().positive().max(16384),
91
+ limit: z2.literal(2048),
92
+ overlay: autoFitOverlayReferenceSchema.extend({ type: z2.literal("symbol") }).strict(),
93
+ width: z2.number().finite().positive().max(16384)
94
+ }).strict();
95
+ var autoFitErrorBase = {
96
+ error: z2.string().min(1).max(512),
97
+ retryable: z2.literal(false)
98
+ };
99
+ var staticMapRequestErrorResponseSchema = z2.discriminatedUnion("code", [
100
+ z2.object({
101
+ ...autoFitErrorBase,
102
+ code: z2.literal("AUTO_FIT_EMPTY"),
103
+ reason: z2.literal("NO_FITTABLE_OVERLAYS")
104
+ }).strict(),
105
+ z2.object({
106
+ ...autoFitErrorBase,
107
+ code: z2.literal("AUTO_FIT_AMBIGUOUS"),
108
+ details: autoFitDetailsSchema,
109
+ reason: z2.literal("ANTIMERIDIAN_WRAP_REQUIRED")
110
+ }).strict(),
111
+ z2.object({
112
+ ...autoFitErrorBase,
113
+ code: z2.literal("AUTO_FIT_IMPOSSIBLE"),
114
+ details: autoFitDetailsSchema.optional(),
115
+ reason: z2.enum([
116
+ "GLOBE_NOT_SIMULTANEOUSLY_VISIBLE",
117
+ "INSUFFICIENT_VIEWPORT",
118
+ "CAMERA_UNRESOLVABLE",
119
+ "PROJECTED_FOOTPRINT_OUTSIDE_VIEWPORT"
120
+ ])
121
+ }).strict(),
122
+ z2.object({
123
+ ...autoFitErrorBase,
124
+ code: z2.literal("STATIC_OVERLAY_INVALID"),
125
+ details: staticOverlayInvalidDetailsSchema,
126
+ reason: z2.literal("OVERLAY_LATITUDE_OUT_OF_RANGE")
127
+ }).strict(),
128
+ z2.object({
129
+ ...autoFitErrorBase,
130
+ code: z2.literal("STATIC_MAP_LABEL_UNSUPPORTED"),
131
+ details: staticLabelUnsupportedDetailsSchema,
132
+ reason: z2.literal("UNSUPPORTED_GLYPH")
133
+ }).strict(),
134
+ z2.object({
135
+ ...autoFitErrorBase,
136
+ code: z2.literal("STATIC_MAP_ICON_UNAVAILABLE"),
137
+ details: staticIconUnavailableDetailsSchema,
138
+ reason: z2.enum(["ICON_NOT_FOUND", "MANAGED_SPRITE_REQUIRED"])
139
+ }).strict(),
140
+ z2.object({
141
+ ...autoFitErrorBase,
142
+ code: z2.literal("STATIC_MAP_PLACEMENT_UNAVAILABLE"),
143
+ reason: z2.literal("MAP_REDEPLOY_REQUIRED")
144
+ }).strict(),
145
+ z2.object({
146
+ ...autoFitErrorBase,
147
+ code: z2.literal("STATIC_MAP_SYMBOL_TOO_LARGE"),
148
+ details: staticSymbolTooLargeDetailsSchema,
149
+ reason: z2.literal("COMPOSED_IMAGE_TOO_LARGE")
150
+ }).strict(),
151
+ z2.object({
152
+ ...autoFitErrorBase,
153
+ code: z2.literal("STATIC_MAP_ATTRIBUTION_UNSUPPORTED"),
154
+ reason: z2.enum(["UNSUPPORTED_DECLARATION", "UNSUPPORTED_GLYPH"])
155
+ }).strict(),
156
+ z2.object({
157
+ ...autoFitErrorBase,
158
+ code: z2.literal("STATIC_MAP_ATTRIBUTION_TOO_LARGE"),
159
+ reason: z2.literal("CONTENT_LIMIT_EXCEEDED")
160
+ }).strict(),
161
+ z2.object({
162
+ ...autoFitErrorBase,
163
+ code: z2.literal("STATIC_MAP_ATTRIBUTION_DOES_NOT_FIT"),
164
+ reason: z2.literal("BLOCK_DOES_NOT_FIT")
165
+ }).strict()
166
+ ]);
167
+ var StaticMapRequestError = class extends StaticMapError {
168
+ code;
169
+ details;
170
+ reason;
171
+ retryable = false;
172
+ status;
173
+ constructor(response, status = 422) {
174
+ super(response, status);
175
+ this.name = "StaticMapRequestError";
176
+ this.code = response.code;
177
+ this.details = "details" in response ? response.details : void 0;
178
+ this.reason = response.reason;
179
+ this.status = status;
180
+ }
181
+ };
182
+ var staticAutoFitPlanSchema = z2.object({
183
+ bounds: z2.tuple([
184
+ z2.number().finite(),
185
+ z2.number().finite().min(-90).max(90),
186
+ z2.number().finite(),
187
+ z2.number().finite().min(-90).max(90)
188
+ ]),
189
+ longitudeOffsets: z2.array(z2.number().int().min(-360).max(360)).max(24),
190
+ nominalInsets: staticPaddingSchema,
191
+ requiredInsets: staticPaddingSchema
192
+ }).strict();
193
+ function analyzeStaticAutoFit(scene) {
194
+ if (scene.camera.type !== "auto") {
195
+ throw new Error("Static auto-fit analysis requires an auto camera");
196
+ }
197
+ if (scene.overlays.length === 0) {
198
+ return failure({
199
+ code: "AUTO_FIT_EMPTY",
200
+ error: "Auto-fit requires at least one overlay",
201
+ reason: "NO_FITTABLE_OVERLAYS",
202
+ retryable: false
203
+ });
204
+ }
205
+ const components = [];
206
+ let south = Number.POSITIVE_INFINITY;
207
+ let north = Number.NEGATIVE_INFINITY;
208
+ let nominalExpansion = 0;
209
+ for (const [overlayIndex, overlay] of scene.overlays.entries()) {
210
+ const coordinates = overlayCoordinates(overlay);
211
+ const ambiguity = findAmbiguousSegment(overlay);
212
+ if (ambiguity) {
213
+ return failure({
214
+ code: "AUTO_FIT_AMBIGUOUS",
215
+ details: {
216
+ overlay: {
217
+ id: overlay.id ?? `overlay-${overlayIndex + 1}`,
218
+ index: overlayIndex,
219
+ type: overlay.type
220
+ },
221
+ ...ambiguity.ringIndex === void 0 ? {} : { ringIndex: ambiguity.ringIndex },
222
+ segmentIndex: ambiguity.segmentIndex
223
+ },
224
+ error: `Overlay overlays.${overlayIndex} requires an explicit antimeridian wrap decision`,
225
+ reason: "ANTIMERIDIAN_WRAP_REQUIRED",
226
+ retryable: false
227
+ });
228
+ }
229
+ const longitudes = coordinates.map(([longitude]) => longitude);
230
+ components.push({
231
+ east: Math.max(...longitudes),
232
+ overlayIndex,
233
+ west: Math.min(...longitudes)
234
+ });
235
+ for (const [, latitude] of coordinates) {
236
+ south = Math.min(south, latitude);
237
+ north = Math.max(north, latitude);
238
+ }
239
+ nominalExpansion = Math.max(nominalExpansion, nominalOverlayExpansion(overlay));
240
+ }
241
+ const longitudePlan = fitLongitudeComponents(components);
242
+ const nominalInset = Math.ceil(nominalExpansion);
243
+ const nominalInsets = allSides(nominalInset);
244
+ const padding = normalizedPadding(scene.camera.padding, scene.size);
245
+ if ([padding.bottom, padding.left, padding.right, padding.top].some(
246
+ (value) => !Number.isSafeInteger(value + nominalInset)
247
+ )) {
248
+ return failure({
249
+ code: "AUTO_FIT_IMPOSSIBLE",
250
+ details: {
251
+ padding,
252
+ viewport: { height: scene.size.height, width: scene.size.width }
253
+ },
254
+ error: "Auto-fit nominal footprint leaves no usable viewport",
255
+ reason: "INSUFFICIENT_VIEWPORT",
256
+ retryable: false
257
+ });
258
+ }
259
+ const requiredInsets = {
260
+ bottom: padding.bottom + nominalInset,
261
+ left: padding.left + nominalInset,
262
+ right: padding.right + nominalInset,
263
+ top: padding.top + nominalInset
264
+ };
265
+ if (requiredInsets.left + requiredInsets.right >= scene.size.width || requiredInsets.top + requiredInsets.bottom >= scene.size.height) {
266
+ return failure({
267
+ code: "AUTO_FIT_IMPOSSIBLE",
268
+ details: {
269
+ padding,
270
+ requiredInsets,
271
+ viewport: { height: scene.size.height, width: scene.size.width }
272
+ },
273
+ error: "Auto-fit nominal footprint leaves no usable viewport",
274
+ reason: "INSUFFICIENT_VIEWPORT",
275
+ retryable: false
276
+ });
277
+ }
278
+ return {
279
+ ok: true,
280
+ plan: {
281
+ bounds: [
282
+ roundNumber(longitudePlan.west),
283
+ roundNumber(south),
284
+ roundNumber(longitudePlan.east),
285
+ roundNumber(north)
286
+ ],
287
+ longitudeOffsets: longitudePlan.offsets,
288
+ nominalInsets,
289
+ requiredInsets
290
+ }
291
+ };
292
+ }
293
+ function failure(response) {
294
+ return { ...response, ok: false };
295
+ }
296
+ function overlayCoordinates(overlay) {
297
+ if (overlay.type === "circle" || overlay.type === "marker" || overlay.type === "symbol") {
298
+ return [overlay.coordinate];
299
+ }
300
+ if (overlay.type === "line") {
301
+ return overlay.coordinates;
302
+ }
303
+ return overlay.coordinates.flat();
304
+ }
305
+ function findAmbiguousSegment(overlay) {
306
+ if (overlay.type === "circle" || overlay.type === "marker" || overlay.type === "symbol")
307
+ return null;
308
+ const paths = overlay.type === "line" ? [overlay.coordinates] : overlay.coordinates;
309
+ for (const [ringIndex, path] of paths.entries()) {
310
+ for (let segmentIndex = 0; segmentIndex < path.length - 1; segmentIndex += 1) {
311
+ const start = path[segmentIndex];
312
+ const end = path[segmentIndex + 1];
313
+ if (start && end && Math.abs(end[0] - start[0]) >= 180) {
314
+ return {
315
+ ...overlay.type === "polygon" ? { ringIndex } : {},
316
+ segmentIndex
317
+ };
318
+ }
319
+ }
320
+ }
321
+ return null;
322
+ }
323
+ function nominalOverlayExpansion(overlay) {
324
+ if (overlay.type === "circle" || overlay.type === "marker") {
325
+ return overlay.radius + overlay.strokeWidth;
326
+ }
327
+ if (overlay.type === "symbol") return 0;
328
+ if (overlay.type === "line") {
329
+ return overlay.width / 2 + 0.5;
330
+ }
331
+ return overlay.stroke && overlay.strokeWidth > 0 ? overlay.strokeWidth / 2 + 0.5 : 0;
332
+ }
333
+ function fitLongitudeComponents(components) {
334
+ const candidates = /* @__PURE__ */ new Set();
335
+ for (const component of components) {
336
+ candidates.add(toCircle(component.west));
337
+ candidates.add(toCircle(component.east));
338
+ }
339
+ let best;
340
+ for (const cut of [...candidates].sort((left, right) => left - right)) {
341
+ const assignedStarts = [];
342
+ let west = Number.POSITIVE_INFINITY;
343
+ let east = Number.NEGATIVE_INFINITY;
344
+ let valid = true;
345
+ for (const component of components) {
346
+ const width = component.east - component.west;
347
+ let start = toCircle(component.west);
348
+ if (start < cut) start += 360;
349
+ const end = start + width;
350
+ if (end > cut + 360 + 1e-9) {
351
+ valid = false;
352
+ break;
353
+ }
354
+ assignedStarts.push(start);
355
+ west = Math.min(west, start);
356
+ east = Math.max(east, end);
357
+ }
358
+ if (!valid) continue;
359
+ const span = east - west;
360
+ const canonicalWest2 = canonicalLongitude(west - 180);
361
+ const bestCanonicalWest = best ? canonicalLongitude(best.west - 180) : Number.POSITIVE_INFINITY;
362
+ if (!best || span < best.span - 1e-9 || Math.abs(span - best.span) <= 1e-9 && canonicalWest2 < bestCanonicalWest) {
363
+ best = { assignedStarts, east, span, west };
364
+ }
365
+ }
366
+ if (!best) {
367
+ const west = Math.min(...components.map((component) => component.west));
368
+ const east = Math.max(...components.map((component) => component.east));
369
+ return { east, offsets: components.map(() => 0), west };
370
+ }
371
+ const unshiftedWest = best.west - 180;
372
+ const canonicalWest = canonicalLongitude(unshiftedWest);
373
+ const globalOffset = canonicalWest - unshiftedWest;
374
+ const offsets = components.map((component, index) => {
375
+ const assignedLongitude = (best?.assignedStarts[index] ?? 0) - 180 + globalOffset;
376
+ return Math.round(assignedLongitude - component.west);
377
+ });
378
+ return {
379
+ east: canonicalWest + best.span,
380
+ offsets,
381
+ west: canonicalWest
382
+ };
383
+ }
384
+ function toCircle(longitude) {
385
+ const value = (longitude + 180) % 360;
386
+ return value < 0 ? value + 360 : value;
387
+ }
388
+ function canonicalLongitude(longitude) {
389
+ const value = ((longitude + 180) % 360 + 360) % 360;
390
+ return value - 180;
391
+ }
392
+ function allSides(value) {
393
+ return { bottom: value, left: value, right: value, top: value };
394
+ }
395
+ function normalizedPadding(padding, size) {
396
+ if (padding === void 0) {
397
+ return allSides(Math.min(32, Math.ceil(Math.min(size.width, size.height) * 0.05)));
398
+ }
399
+ if (typeof padding === "number") return allSides(padding);
400
+ return {
401
+ bottom: padding.bottom ?? 0,
402
+ left: padding.left ?? 0,
403
+ right: padding.right ?? 0,
404
+ top: padding.top ?? 0
405
+ };
406
+ }
407
+ function autoFitErrorResponse(failure2) {
408
+ const { ok: _ok, ...response } = failure2;
409
+ return response;
410
+ }
411
+ function findStaticOverlayLatitudeFailure(input) {
412
+ if (!input || typeof input !== "object" || Array.isArray(input)) return null;
413
+ const overlays = input.overlays;
414
+ if (!Array.isArray(overlays)) return null;
415
+ for (const [index, candidate] of overlays.entries()) {
416
+ if (index >= staticSceneLimits.maxOverlays) break;
417
+ if (!candidate || typeof candidate !== "object" || Array.isArray(candidate)) continue;
418
+ const overlay = candidate;
419
+ const type = overlay.type;
420
+ if (!["circle", "line", "marker", "polygon", "symbol"].includes(String(type))) continue;
421
+ const coordinates = type === "circle" || type === "marker" || type === "symbol" ? [overlay.coordinate] : overlayCoordinateCandidates(type, overlay.coordinates);
422
+ for (const coordinate of coordinates) {
423
+ if (!Array.isArray(coordinate) || coordinate.length !== 2 || typeof coordinate[1] !== "number" || !Number.isFinite(coordinate[1]) || Math.abs(coordinate[1]) <= MAX_OVERLAY_LATITUDE) {
424
+ continue;
425
+ }
426
+ const id = typeof overlay.id === "string" && overlay.id.trim() ? overlay.id.trim() : `overlay-${index + 1}`;
427
+ const response = {
428
+ code: "STATIC_OVERLAY_INVALID",
429
+ details: {
430
+ latitude: coordinate[1],
431
+ limit: MAX_OVERLAY_LATITUDE,
432
+ overlay: {
433
+ id: id.slice(0, 64),
434
+ index,
435
+ type
436
+ }
437
+ },
438
+ error: `Overlay overlays.${index} latitude ${coordinate[1]} exceeds the supported \xB1${MAX_OVERLAY_LATITUDE}\xB0 range`,
439
+ reason: "OVERLAY_LATITUDE_OUT_OF_RANGE",
440
+ retryable: false
441
+ };
442
+ return { ...response, ok: false };
443
+ }
444
+ }
445
+ return null;
446
+ }
447
+ function findStaticSymbolLabelGlyphFailure(input) {
448
+ if (!input || typeof input !== "object" || Array.isArray(input)) return null;
449
+ const overlays = input.overlays;
450
+ if (!Array.isArray(overlays)) return null;
451
+ for (const [index, candidate] of overlays.entries()) {
452
+ if (index >= staticSceneLimits.maxOverlays) break;
453
+ if (!candidate || typeof candidate !== "object" || Array.isArray(candidate)) continue;
454
+ const overlay = candidate;
455
+ if (overlay.type !== "symbol") continue;
456
+ const label = overlay.label;
457
+ const text = typeof label === "string" ? label : label && typeof label === "object" && !Array.isArray(label) ? label.text : void 0;
458
+ if (typeof text !== "string") continue;
459
+ const unsupported = Array.from(text).find(
460
+ (character) => !isSupportedStaticSymbolCodePoint(character.codePointAt(0))
461
+ );
462
+ if (!unsupported) continue;
463
+ const codePoint = unsupported.codePointAt(0);
464
+ const id = typeof overlay.id === "string" && overlay.id.trim() ? overlay.id.trim() : `overlay-${index + 1}`;
465
+ return {
466
+ code: "STATIC_MAP_LABEL_UNSUPPORTED",
467
+ details: {
468
+ codePoint,
469
+ overlay: { id: id.slice(0, 64), index, type: "symbol" }
470
+ },
471
+ error: `Overlay overlays.${index} label contains unsupported glyph U+${codePoint.toString(16).toUpperCase().padStart(4, "0")}`,
472
+ ok: false,
473
+ reason: "UNSUPPORTED_GLYPH",
474
+ retryable: false
475
+ };
476
+ }
477
+ return null;
478
+ }
479
+ function overlayCoordinateCandidates(type, value) {
480
+ if (!Array.isArray(value)) return [];
481
+ if (type === "line") {
482
+ return value.slice(0, staticSceneLimits.maxPathCoordinates);
483
+ }
484
+ return value.slice(0, 16).flatMap(
485
+ (ring) => Array.isArray(ring) ? ring.slice(0, staticSceneLimits.maxPathCoordinates) : []
486
+ );
487
+ }
488
+
489
+ // src/scene.ts
490
+ function validateStaticScene(input) {
491
+ const overlayLatitudeFailure = findStaticOverlayLatitudeFailure(input);
492
+ if (overlayLatitudeFailure) return overlayLatitudeFailure;
493
+ const labelGlyphFailure = findStaticSymbolLabelGlyphFailure(input);
494
+ if (labelGlyphFailure) return labelGlyphFailure;
495
+ const parsed = staticSceneSchema.safeParse(input);
496
+ if (!parsed.success) {
497
+ return {
498
+ error: parsed.error.issues.map((issue) => `${issue.path.join(".") || "scene"}: ${issue.message}`).join("; "),
499
+ ok: false
500
+ };
501
+ }
502
+ if (jsonByteLength(parsed.data.overlays) > staticSceneLimits.maxGeoJsonBytes) {
503
+ return {
504
+ error: `overlays exceed ${staticSceneLimits.maxGeoJsonBytes} bytes`,
505
+ ok: false
506
+ };
507
+ }
508
+ const scene = normalizeParsedStaticScene(parsed.data);
509
+ if (scene.camera.type === "auto") {
510
+ const analysis = analyzeStaticAutoFit(scene);
511
+ if (!analysis.ok) return analysis;
512
+ }
513
+ return { ok: true, scene };
514
+ }
515
+ function normalizeStaticScene(scene) {
516
+ const overlayLatitudeFailure = findStaticOverlayLatitudeFailure(scene);
517
+ if (overlayLatitudeFailure) {
518
+ const { ok: _ok, ...response } = overlayLatitudeFailure;
519
+ throw new StaticMapRequestError(response);
520
+ }
521
+ const labelGlyphFailure = findStaticSymbolLabelGlyphFailure(scene);
522
+ if (labelGlyphFailure) {
523
+ const { ok: _ok, ...response } = labelGlyphFailure;
524
+ throw new StaticMapRequestError(response);
525
+ }
526
+ const normalized = normalizeParsedStaticScene(staticSceneSchema.parse(scene));
527
+ if (normalized.camera.type === "auto") {
528
+ const analysis = analyzeStaticAutoFit(normalized);
529
+ if (!analysis.ok) {
530
+ throw new StaticMapRequestError(autoFitErrorResponse(analysis));
531
+ }
532
+ }
533
+ return normalized;
534
+ }
535
+ function normalizeParsedStaticScene(parsed) {
536
+ return stripUndefined({
537
+ ...parsed.attribution && Object.keys(parsed.attribution).length > 0 ? {
538
+ attribution: parsed.attribution.mode === "external" ? { mode: "external" } : stripUndefined({
539
+ mode: parsed.attribution.mode,
540
+ position: parsed.attribution.position
541
+ })
542
+ } : {},
543
+ camera: parsed.camera.type === "center" ? {
544
+ bearing: roundNumber(parsed.camera.bearing ?? 0),
545
+ center: normalizeStaticCoordinate(parsed.camera.center),
546
+ type: "center",
547
+ zoom: roundNumber(parsed.camera.zoom)
548
+ } : parsed.camera.type === "bounds" ? {
549
+ bearing: roundNumber(parsed.camera.bearing ?? 0),
550
+ bounds: parsed.camera.bounds.map(roundNumber),
551
+ padding: parsed.camera.padding ?? 32,
552
+ type: "bounds"
553
+ } : {
554
+ bearing: roundNumber(parsed.camera.bearing ?? 0),
555
+ maxZoom: roundNumber(parsed.camera.maxZoom ?? 16),
556
+ padding: normalizeAutoPadding(parsed.camera.padding, parsed.size),
557
+ type: "auto"
558
+ },
559
+ ...parsed.format && parsed.format !== "png" ? { format: parsed.format } : {},
560
+ map: parsed.map,
561
+ overlays: parsed.overlays.map(
562
+ (overlay, index) => normalizeStaticOverlay({
563
+ ...overlay,
564
+ id: overlay.id ?? `overlay-${index + 1}`
565
+ })
566
+ ),
567
+ size: {
568
+ dpr: parsed.size.dpr ?? 1,
569
+ height: parsed.size.height,
570
+ width: parsed.size.width
571
+ },
572
+ theme: parsed.theme
573
+ });
574
+ }
575
+ function normalizeAutoPadding(padding, size) {
576
+ if (padding === void 0) {
577
+ const value = Math.min(32, Math.ceil(Math.min(size.width, size.height) * 0.05));
578
+ return { bottom: value, left: value, right: value, top: value };
579
+ }
580
+ if (typeof padding === "number") {
581
+ return { bottom: padding, left: padding, right: padding, top: padding };
582
+ }
583
+ return {
584
+ bottom: padding.bottom ?? 0,
585
+ left: padding.left ?? 0,
586
+ right: padding.right ?? 0,
587
+ top: padding.top ?? 0
588
+ };
589
+ }
590
+
591
+ export {
592
+ staticMapErrorResponseSchema,
593
+ StaticMapError,
594
+ staticMapRequestErrorResponseSchema,
595
+ StaticMapRequestError,
596
+ staticAutoFitPlanSchema,
597
+ analyzeStaticAutoFit,
598
+ validateStaticScene,
599
+ normalizeStaticScene
600
+ };