@tileflow/static 0.1.0-alpha.21 → 0.1.0-alpha.23
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 +145 -18
- package/dist/chunk-6DWVCK64.js +406 -0
- package/dist/chunk-HVVIAOBE.js +175 -0
- package/dist/{chunk-A23RC4BE.js → chunk-N63IFMFM.js} +64 -18
- package/dist/{chunk-NHQK5UZ2.js → chunk-TBI5AED6.js} +185 -43
- package/dist/chunk-XMT7VJL5.js +297 -0
- package/dist/client.d.ts +93 -10
- package/dist/client.js +16 -5
- package/dist/errors-B9rwehK0.d.ts +21 -0
- package/dist/index.d.ts +5 -4
- package/dist/index.js +55 -9
- package/dist/manifest.d.ts +419 -13
- package/dist/manifest.js +19 -3
- package/dist/overlays.d.ts +9 -3
- package/dist/overlays.js +6 -4
- package/dist/scene-contract-Cr6BNLz0.d.ts +734 -0
- package/dist/scene.d.ts +103 -4
- package/dist/scene.js +21 -3
- package/package.json +3 -6
- package/GENERATED_OUTPUT_LICENSE.md +0 -21
- package/NOTICE +0 -9
- package/TRADEMARKS.md +0 -10
- package/dist/chunk-KPT7PASB.js +0 -122
- package/dist/chunk-MGQCHCU3.js +0 -228
- package/dist/chunk-S3WXE5P4.js +0 -99
- package/dist/scene-contract-DZMhLDnf.d.ts +0 -159
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import {
|
|
2
|
+
normalizeStaticOverlay,
|
|
3
|
+
roundNumber
|
|
4
|
+
} from "./chunk-6DWVCK64.js";
|
|
5
|
+
|
|
6
|
+
// src/overlays.ts
|
|
7
|
+
function line(input) {
|
|
8
|
+
return normalizeStaticOverlay({ ...input, type: "line" });
|
|
9
|
+
}
|
|
10
|
+
function circle(input) {
|
|
11
|
+
return normalizeStaticOverlay({ ...input, type: "circle" });
|
|
12
|
+
}
|
|
13
|
+
function marker(input) {
|
|
14
|
+
return normalizeStaticOverlay({ ...input, type: "marker" });
|
|
15
|
+
}
|
|
16
|
+
function polygon(input) {
|
|
17
|
+
return normalizeStaticOverlay({ ...input, type: "polygon" });
|
|
18
|
+
}
|
|
19
|
+
function symbol(input) {
|
|
20
|
+
return normalizeStaticOverlay({ ...input, type: "symbol" });
|
|
21
|
+
}
|
|
22
|
+
function compileStaticOverlays(overlays, options = {}) {
|
|
23
|
+
if (options.longitudeOffsets !== void 0 && (options.longitudeOffsets.length !== overlays.length || options.longitudeOffsets.some(
|
|
24
|
+
(offset) => !Number.isInteger(offset) || Math.abs(offset) > 360 || offset % 360 !== 0
|
|
25
|
+
))) {
|
|
26
|
+
throw new Error(
|
|
27
|
+
"Static overlay longitude offsets must contain one -360, 0, or 360 per overlay"
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
const sources = {};
|
|
31
|
+
const layers = [];
|
|
32
|
+
const placements = {};
|
|
33
|
+
const symbols = [];
|
|
34
|
+
const pushLayer = (layer, overlay) => {
|
|
35
|
+
const layerId = String(layer.id);
|
|
36
|
+
layers.push(layer);
|
|
37
|
+
placements[layerId] = overlay.placement ?? "above-labels";
|
|
38
|
+
};
|
|
39
|
+
for (const [index, overlay] of overlays.entries()) {
|
|
40
|
+
const longitudeOffset = options.longitudeOffsets?.[index] ?? 0;
|
|
41
|
+
const id = safeLayerId(`tileflow-static-overlay-${index + 1}-${overlay.id ?? overlay.type}`);
|
|
42
|
+
const sourceId = `${id}-source`;
|
|
43
|
+
if (overlay.type === "line") {
|
|
44
|
+
sources[sourceId] = {
|
|
45
|
+
data: feature("LineString", shiftLongitudes(overlay.coordinates, longitudeOffset)),
|
|
46
|
+
type: "geojson"
|
|
47
|
+
};
|
|
48
|
+
pushLayer(
|
|
49
|
+
{
|
|
50
|
+
id,
|
|
51
|
+
layout: { "line-cap": "round", "line-join": "round" },
|
|
52
|
+
paint: {
|
|
53
|
+
"line-color": overlay.color,
|
|
54
|
+
"line-opacity": overlay.opacity,
|
|
55
|
+
"line-width": overlay.width
|
|
56
|
+
},
|
|
57
|
+
source: sourceId,
|
|
58
|
+
type: "line"
|
|
59
|
+
},
|
|
60
|
+
overlay
|
|
61
|
+
);
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
if (overlay.type === "polygon") {
|
|
65
|
+
sources[sourceId] = {
|
|
66
|
+
data: feature("Polygon", shiftLongitudes(overlay.coordinates, longitudeOffset)),
|
|
67
|
+
type: "geojson"
|
|
68
|
+
};
|
|
69
|
+
pushLayer(
|
|
70
|
+
{
|
|
71
|
+
id,
|
|
72
|
+
paint: {
|
|
73
|
+
"fill-color": overlay.fill,
|
|
74
|
+
"fill-opacity": overlay.opacity
|
|
75
|
+
},
|
|
76
|
+
source: sourceId,
|
|
77
|
+
type: "fill"
|
|
78
|
+
},
|
|
79
|
+
overlay
|
|
80
|
+
);
|
|
81
|
+
if (overlay.stroke && overlay.strokeWidth > 0) {
|
|
82
|
+
pushLayer(
|
|
83
|
+
{
|
|
84
|
+
id: `${id}-stroke`,
|
|
85
|
+
layout: { "line-cap": "round", "line-join": "round" },
|
|
86
|
+
paint: {
|
|
87
|
+
"line-color": overlay.stroke,
|
|
88
|
+
"line-width": overlay.strokeWidth
|
|
89
|
+
},
|
|
90
|
+
source: sourceId,
|
|
91
|
+
type: "line"
|
|
92
|
+
},
|
|
93
|
+
overlay
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
sources[sourceId] = {
|
|
99
|
+
data: feature("Point", shiftLongitudes(overlay.coordinate, longitudeOffset)),
|
|
100
|
+
type: "geojson"
|
|
101
|
+
};
|
|
102
|
+
if (overlay.type === "symbol") {
|
|
103
|
+
const composed = overlay.label !== void 0;
|
|
104
|
+
const imageId = composed ? safeLayerId(`__tileflow-static-symbol-${index + 1}-${overlay.id}`) : overlay.icon;
|
|
105
|
+
const alwaysVisible = overlay.collision === "always-visible";
|
|
106
|
+
const iconSize = composed ? 1 : overlay.scale;
|
|
107
|
+
const iconOffset = composed ? overlay.offset : overlay.offset.map((value) => roundNumber(value / overlay.scale));
|
|
108
|
+
pushLayer(
|
|
109
|
+
{
|
|
110
|
+
id,
|
|
111
|
+
layout: {
|
|
112
|
+
"icon-allow-overlap": alwaysVisible,
|
|
113
|
+
"icon-anchor": overlay.anchor,
|
|
114
|
+
"icon-ignore-placement": alwaysVisible,
|
|
115
|
+
"icon-image": imageId,
|
|
116
|
+
"icon-offset": iconOffset,
|
|
117
|
+
"icon-pitch-alignment": "viewport",
|
|
118
|
+
"icon-rotation-alignment": "viewport",
|
|
119
|
+
"icon-size": iconSize,
|
|
120
|
+
"symbol-placement": "point",
|
|
121
|
+
"symbol-z-order": "source"
|
|
122
|
+
},
|
|
123
|
+
paint: { "icon-opacity": overlay.opacity },
|
|
124
|
+
source: sourceId,
|
|
125
|
+
type: "symbol"
|
|
126
|
+
},
|
|
127
|
+
overlay
|
|
128
|
+
);
|
|
129
|
+
symbols.push({ imageId, overlayIndex: index });
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
pushLayer(
|
|
133
|
+
{
|
|
134
|
+
id,
|
|
135
|
+
paint: {
|
|
136
|
+
"circle-color": overlay.color,
|
|
137
|
+
"circle-opacity": overlay.type === "circle" ? overlay.opacity : 1,
|
|
138
|
+
"circle-radius": overlay.radius,
|
|
139
|
+
"circle-stroke-color": overlay.strokeColor ?? overlay.color,
|
|
140
|
+
"circle-stroke-width": overlay.strokeWidth
|
|
141
|
+
},
|
|
142
|
+
source: sourceId,
|
|
143
|
+
type: "circle"
|
|
144
|
+
},
|
|
145
|
+
overlay
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
return { layers, placements, sources, symbols };
|
|
149
|
+
}
|
|
150
|
+
function shiftLongitudes(value, offset) {
|
|
151
|
+
if (!Array.isArray(value)) return value;
|
|
152
|
+
if (value.length === 2 && typeof value[0] === "number" && typeof value[1] === "number") {
|
|
153
|
+
return [roundNumber(value[0] + offset), value[1]];
|
|
154
|
+
}
|
|
155
|
+
return value.map((entry) => shiftLongitudes(entry, offset));
|
|
156
|
+
}
|
|
157
|
+
function feature(type, coordinates) {
|
|
158
|
+
return {
|
|
159
|
+
geometry: { coordinates, type },
|
|
160
|
+
properties: {},
|
|
161
|
+
type: "Feature"
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
function safeLayerId(value) {
|
|
165
|
+
return value.replace(/[^a-zA-Z0-9_-]/g, "-").slice(0, 80);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export {
|
|
169
|
+
line,
|
|
170
|
+
circle,
|
|
171
|
+
marker,
|
|
172
|
+
polygon,
|
|
173
|
+
symbol,
|
|
174
|
+
compileStaticOverlays
|
|
175
|
+
};
|
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
import {
|
|
2
|
+
StaticMapError,
|
|
2
3
|
StaticMapRequestError,
|
|
4
|
+
staticMapErrorResponseSchema,
|
|
3
5
|
staticMapRequestErrorResponseSchema,
|
|
4
6
|
validateStaticScene
|
|
5
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-TBI5AED6.js";
|
|
6
8
|
import {
|
|
7
9
|
isSafeHttpUrl,
|
|
10
|
+
jsonByteLength,
|
|
8
11
|
stableStringify
|
|
9
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-6DWVCK64.js";
|
|
10
13
|
|
|
11
14
|
// src/client.ts
|
|
12
15
|
import { z } from "zod";
|
|
13
16
|
var maxStaticMapResponseBytes = 64 * 1024;
|
|
14
17
|
var staticMapIdempotencyKeyPattern = /^[A-Za-z0-9][A-Za-z0-9._:-]{7,127}$/;
|
|
18
|
+
var STATIC_MAP_RESULT_MEDIA_TYPE = "application/vnd.tileflow.static-map-result+json;version=1";
|
|
15
19
|
var preparedStaticMapRequestBodies = /* @__PURE__ */ new WeakMap();
|
|
16
|
-
var
|
|
20
|
+
var staticMapReadyResultBaseSchema = z.object({
|
|
17
21
|
cached: z.boolean(),
|
|
18
22
|
hash: z.string().regex(/^[A-Za-z0-9_-]{43}$/),
|
|
19
23
|
imageUrl: z.string().trim().url().max(2048).refine(isSafeHttpUrl, { message: "Expected an http(s) URL without credentials" }),
|
|
@@ -22,6 +26,39 @@ var staticMapReadyResultSchema = z.object({
|
|
|
22
26
|
status: z.literal("ready"),
|
|
23
27
|
unitCost: z.literal(15)
|
|
24
28
|
}).strict();
|
|
29
|
+
var safeAttributionTextSchema = z.string().min(1).max(16384).refine(hasNoControlCharacters, { message: "Attribution text contains control characters" });
|
|
30
|
+
var staticMapAttributionEntrySchema = z.object({
|
|
31
|
+
authority: z.enum(["platform-notice", "team-declared"]),
|
|
32
|
+
links: z.array(
|
|
33
|
+
z.object({
|
|
34
|
+
label: safeAttributionTextSchema,
|
|
35
|
+
url: z.string().trim().url().max(2048).refine(isSafeHttpUrl, {
|
|
36
|
+
message: "Expected an http(s) URL without credentials or a fragment"
|
|
37
|
+
})
|
|
38
|
+
}).strict()
|
|
39
|
+
).max(8),
|
|
40
|
+
text: safeAttributionTextSchema
|
|
41
|
+
}).strict();
|
|
42
|
+
var staticMapAttributionResultSchema = z.object({
|
|
43
|
+
entries: z.array(staticMapAttributionEntrySchema).max(18),
|
|
44
|
+
mode: z.enum(["embedded", "external"]),
|
|
45
|
+
position: z.enum(["top-left", "top-right", "bottom-left", "bottom-right"]).nullable()
|
|
46
|
+
}).strict().superRefine((result, context) => {
|
|
47
|
+
if (result.mode === "external" && result.position !== null || result.mode === "embedded" && result.position === null) {
|
|
48
|
+
context.addIssue({
|
|
49
|
+
code: "custom",
|
|
50
|
+
message: "Attribution position must match its mode",
|
|
51
|
+
path: ["position"]
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
if (jsonByteLength(result) > 32 * 1024) {
|
|
55
|
+
context.addIssue({ code: "custom", message: "Attribution result exceeds 32 KiB" });
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
var staticMapHostedResultSchema = staticMapReadyResultBaseSchema.extend({
|
|
59
|
+
attribution: staticMapAttributionResultSchema,
|
|
60
|
+
resultVersion: z.literal(1)
|
|
61
|
+
}).strict();
|
|
25
62
|
var staticMapProcessingResultSchema = z.object({
|
|
26
63
|
operationId: z.string().min(20).max(68),
|
|
27
64
|
retryAfterMs: z.number().int().min(0).max(5e3),
|
|
@@ -92,6 +129,7 @@ async function requestStaticMapUntilReady(request, options) {
|
|
|
92
129
|
const fetcher = options.fetch ?? fetch;
|
|
93
130
|
const createUrl = normalizeStaticMapEndpointUrl(options.createUrl);
|
|
94
131
|
const headers = {
|
|
132
|
+
Accept: STATIC_MAP_RESULT_MEDIA_TYPE,
|
|
95
133
|
"Content-Type": "application/json",
|
|
96
134
|
"Idempotency-Key": idempotency.key
|
|
97
135
|
};
|
|
@@ -109,17 +147,24 @@ async function requestStaticMapUntilReady(request, options) {
|
|
|
109
147
|
});
|
|
110
148
|
throwIfAborted(signal);
|
|
111
149
|
if (!response.ok) {
|
|
112
|
-
const
|
|
150
|
+
const errorBody = await readStaticMapErrorBody(response, 8 * 1024);
|
|
113
151
|
throwIfAborted(signal);
|
|
114
|
-
if (
|
|
115
|
-
|
|
152
|
+
if (response.status === 422) {
|
|
153
|
+
const requestError = staticMapRequestErrorResponseSchema.safeParse(errorBody);
|
|
154
|
+
if (requestError.success) {
|
|
155
|
+
throw new StaticMapRequestError(requestError.data, response.status);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
const structured = staticMapErrorResponseSchema.safeParse(errorBody);
|
|
159
|
+
if (structured.success) {
|
|
160
|
+
throw new StaticMapError(structured.data, response.status);
|
|
116
161
|
}
|
|
117
162
|
throw new Error(`Tileflow static map failed (${response.status}).`);
|
|
118
163
|
}
|
|
119
164
|
const json = await readJsonResponse(response);
|
|
120
165
|
throwIfAborted(signal);
|
|
121
166
|
if (response.status !== 202) {
|
|
122
|
-
const parsed =
|
|
167
|
+
const parsed = staticMapHostedResultSchema.safeParse(json);
|
|
123
168
|
if (!parsed.success) {
|
|
124
169
|
throw new Error(
|
|
125
170
|
`Tileflow static map returned an invalid response: ${parsed.error.message}`
|
|
@@ -140,6 +185,12 @@ async function requestStaticMapUntilReady(request, options) {
|
|
|
140
185
|
}
|
|
141
186
|
});
|
|
142
187
|
}
|
|
188
|
+
function hasNoControlCharacters(value) {
|
|
189
|
+
return !Array.from(value).some((character) => {
|
|
190
|
+
const codePoint = character.codePointAt(0) ?? 0;
|
|
191
|
+
return codePoint <= 31 || codePoint === 127;
|
|
192
|
+
});
|
|
193
|
+
}
|
|
143
194
|
function boundedClientDuration(value, fallback, minimum, maximum, name) {
|
|
144
195
|
const resolved = value ?? fallback;
|
|
145
196
|
if (!Number.isSafeInteger(resolved) || resolved < minimum || resolved > maximum) {
|
|
@@ -157,22 +208,14 @@ async function readJsonResponse(response) {
|
|
|
157
208
|
throw new Error("Tileflow static map returned an invalid response: expected JSON");
|
|
158
209
|
}
|
|
159
210
|
}
|
|
160
|
-
async function
|
|
211
|
+
async function readStaticMapErrorBody(response, maximumBytes) {
|
|
161
212
|
try {
|
|
162
213
|
const source = await readBoundedResponseText(response, maximumBytes, `${maximumBytes} bytes`);
|
|
163
|
-
|
|
164
|
-
return parsed.success ? parsed.data : null;
|
|
214
|
+
return JSON.parse(source);
|
|
165
215
|
} catch {
|
|
166
216
|
return null;
|
|
167
217
|
}
|
|
168
218
|
}
|
|
169
|
-
async function discardBoundedResponse(response, maximumBytes) {
|
|
170
|
-
try {
|
|
171
|
-
await readBoundedResponseText(response, maximumBytes, `${maximumBytes} bytes`);
|
|
172
|
-
} catch {
|
|
173
|
-
}
|
|
174
|
-
return null;
|
|
175
|
-
}
|
|
176
219
|
function isStaticMapRequestFailure(value) {
|
|
177
220
|
if (!("code" in value)) return false;
|
|
178
221
|
const { ok: _ok, ...response } = value;
|
|
@@ -317,7 +360,10 @@ function hasControlCharacter(value) {
|
|
|
317
360
|
}
|
|
318
361
|
|
|
319
362
|
export {
|
|
320
|
-
|
|
363
|
+
STATIC_MAP_RESULT_MEDIA_TYPE,
|
|
364
|
+
staticMapAttributionEntrySchema,
|
|
365
|
+
staticMapAttributionResultSchema,
|
|
366
|
+
staticMapHostedResultSchema,
|
|
321
367
|
staticMapProcessingResultSchema,
|
|
322
368
|
validateStaticMapIdempotencyKey,
|
|
323
369
|
createStaticMapIdempotencyKey,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
MAX_OVERLAY_LATITUDE,
|
|
3
|
+
isSupportedStaticSymbolCodePoint,
|
|
3
4
|
jsonByteLength,
|
|
4
5
|
normalizeStaticCoordinate,
|
|
5
6
|
normalizeStaticOverlay,
|
|
@@ -8,75 +9,166 @@ import {
|
|
|
8
9
|
staticSceneLimits,
|
|
9
10
|
staticSceneSchema,
|
|
10
11
|
stripUndefined
|
|
11
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-6DWVCK64.js";
|
|
12
13
|
|
|
13
14
|
// src/auto-fit.ts
|
|
15
|
+
import { z as z2 } from "zod";
|
|
16
|
+
|
|
17
|
+
// src/errors.ts
|
|
14
18
|
import { z } from "zod";
|
|
15
|
-
var
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
+
retryable: z.boolean().optional()
|
|
27
|
+
}).strip();
|
|
28
|
+
var StaticMapError = class extends Error {
|
|
29
|
+
code;
|
|
30
|
+
remainingUnits;
|
|
31
|
+
requestId;
|
|
32
|
+
response;
|
|
33
|
+
retryable;
|
|
34
|
+
status;
|
|
35
|
+
constructor(response, status) {
|
|
36
|
+
const parsed = staticMapErrorResponseSchema.parse(response);
|
|
37
|
+
super(parsed.error);
|
|
38
|
+
this.name = "StaticMapError";
|
|
39
|
+
this.code = parsed.code ?? null;
|
|
40
|
+
this.remainingUnits = parsed.remainingUnits ?? null;
|
|
41
|
+
this.requestId = parsed.requestId ?? null;
|
|
42
|
+
this.response = Object.freeze(parsed);
|
|
43
|
+
this.retryable = parsed.retryable ?? null;
|
|
44
|
+
this.status = status;
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
function hasNoControlCharacters(value) {
|
|
48
|
+
return !Array.from(value).some((character) => {
|
|
49
|
+
const codePoint = character.codePointAt(0) ?? 0;
|
|
50
|
+
return codePoint <= 31 || codePoint === 127;
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// src/auto-fit.ts
|
|
55
|
+
var autoFitOverlayReferenceSchema = z2.object({
|
|
56
|
+
id: z2.string().min(1).max(64),
|
|
57
|
+
index: z2.number().int().min(0).max(23),
|
|
58
|
+
type: z2.enum(["circle", "line", "marker", "polygon", "symbol"])
|
|
19
59
|
}).strict();
|
|
20
|
-
var autoFitViewportSchema =
|
|
21
|
-
height:
|
|
22
|
-
width:
|
|
60
|
+
var autoFitViewportSchema = z2.object({
|
|
61
|
+
height: z2.number().int().min(64).max(1280),
|
|
62
|
+
width: z2.number().int().min(64).max(1280)
|
|
23
63
|
}).strict();
|
|
24
|
-
var autoFitDetailsSchema =
|
|
64
|
+
var autoFitDetailsSchema = z2.object({
|
|
25
65
|
overlay: autoFitOverlayReferenceSchema.optional(),
|
|
26
66
|
padding: staticPaddingSchema.optional(),
|
|
27
|
-
projection:
|
|
67
|
+
projection: z2.enum(["globe", "mercator"]).optional(),
|
|
28
68
|
requiredInsets: staticPaddingSchema.optional(),
|
|
29
|
-
ringIndex:
|
|
30
|
-
segmentIndex:
|
|
69
|
+
ringIndex: z2.number().int().min(0).max(15).optional(),
|
|
70
|
+
segmentIndex: z2.number().int().min(0).max(1999).optional(),
|
|
31
71
|
viewport: autoFitViewportSchema.optional()
|
|
32
72
|
}).strict();
|
|
33
|
-
var staticOverlayInvalidDetailsSchema =
|
|
34
|
-
latitude:
|
|
35
|
-
limit:
|
|
73
|
+
var staticOverlayInvalidDetailsSchema = z2.object({
|
|
74
|
+
latitude: z2.number().finite(),
|
|
75
|
+
limit: z2.literal(MAX_OVERLAY_LATITUDE),
|
|
36
76
|
overlay: autoFitOverlayReferenceSchema
|
|
37
77
|
}).strict();
|
|
78
|
+
var staticLabelUnsupportedDetailsSchema = z2.object({
|
|
79
|
+
codePoint: z2.number().int().min(0).max(1114111),
|
|
80
|
+
overlay: autoFitOverlayReferenceSchema.extend({ type: z2.literal("symbol") }).strict()
|
|
81
|
+
}).strict();
|
|
82
|
+
var staticIconUnavailableDetailsSchema = z2.object({
|
|
83
|
+
icon: z2.string().max(64).regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/u),
|
|
84
|
+
overlay: autoFitOverlayReferenceSchema.extend({ type: z2.literal("symbol") }).strict()
|
|
85
|
+
}).strict();
|
|
86
|
+
var staticSymbolTooLargeDetailsSchema = z2.object({
|
|
87
|
+
height: z2.number().finite().positive().max(16384),
|
|
88
|
+
limit: z2.literal(2048),
|
|
89
|
+
overlay: autoFitOverlayReferenceSchema.extend({ type: z2.literal("symbol") }).strict(),
|
|
90
|
+
width: z2.number().finite().positive().max(16384)
|
|
91
|
+
}).strict();
|
|
38
92
|
var autoFitErrorBase = {
|
|
39
|
-
error:
|
|
40
|
-
retryable:
|
|
93
|
+
error: z2.string().min(1).max(512),
|
|
94
|
+
retryable: z2.literal(false)
|
|
41
95
|
};
|
|
42
|
-
var staticMapRequestErrorResponseSchema =
|
|
43
|
-
|
|
96
|
+
var staticMapRequestErrorResponseSchema = z2.discriminatedUnion("code", [
|
|
97
|
+
z2.object({
|
|
44
98
|
...autoFitErrorBase,
|
|
45
|
-
code:
|
|
46
|
-
reason:
|
|
99
|
+
code: z2.literal("AUTO_FIT_EMPTY"),
|
|
100
|
+
reason: z2.literal("NO_FITTABLE_OVERLAYS")
|
|
47
101
|
}).strict(),
|
|
48
|
-
|
|
102
|
+
z2.object({
|
|
49
103
|
...autoFitErrorBase,
|
|
50
|
-
code:
|
|
104
|
+
code: z2.literal("AUTO_FIT_AMBIGUOUS"),
|
|
51
105
|
details: autoFitDetailsSchema,
|
|
52
|
-
reason:
|
|
106
|
+
reason: z2.literal("ANTIMERIDIAN_WRAP_REQUIRED")
|
|
53
107
|
}).strict(),
|
|
54
|
-
|
|
108
|
+
z2.object({
|
|
55
109
|
...autoFitErrorBase,
|
|
56
|
-
code:
|
|
110
|
+
code: z2.literal("AUTO_FIT_IMPOSSIBLE"),
|
|
57
111
|
details: autoFitDetailsSchema.optional(),
|
|
58
|
-
reason:
|
|
112
|
+
reason: z2.enum([
|
|
59
113
|
"GLOBE_NOT_SIMULTANEOUSLY_VISIBLE",
|
|
60
114
|
"INSUFFICIENT_VIEWPORT",
|
|
61
115
|
"CAMERA_UNRESOLVABLE",
|
|
62
116
|
"PROJECTED_FOOTPRINT_OUTSIDE_VIEWPORT"
|
|
63
117
|
])
|
|
64
118
|
}).strict(),
|
|
65
|
-
|
|
119
|
+
z2.object({
|
|
66
120
|
...autoFitErrorBase,
|
|
67
|
-
code:
|
|
121
|
+
code: z2.literal("STATIC_OVERLAY_INVALID"),
|
|
68
122
|
details: staticOverlayInvalidDetailsSchema,
|
|
69
|
-
reason:
|
|
123
|
+
reason: z2.literal("OVERLAY_LATITUDE_OUT_OF_RANGE")
|
|
124
|
+
}).strict(),
|
|
125
|
+
z2.object({
|
|
126
|
+
...autoFitErrorBase,
|
|
127
|
+
code: z2.literal("STATIC_MAP_LABEL_UNSUPPORTED"),
|
|
128
|
+
details: staticLabelUnsupportedDetailsSchema,
|
|
129
|
+
reason: z2.literal("UNSUPPORTED_GLYPH")
|
|
130
|
+
}).strict(),
|
|
131
|
+
z2.object({
|
|
132
|
+
...autoFitErrorBase,
|
|
133
|
+
code: z2.literal("STATIC_MAP_ICON_UNAVAILABLE"),
|
|
134
|
+
details: staticIconUnavailableDetailsSchema,
|
|
135
|
+
reason: z2.enum(["ICON_NOT_FOUND", "MANAGED_SPRITE_REQUIRED"])
|
|
136
|
+
}).strict(),
|
|
137
|
+
z2.object({
|
|
138
|
+
...autoFitErrorBase,
|
|
139
|
+
code: z2.literal("STATIC_MAP_PLACEMENT_UNAVAILABLE"),
|
|
140
|
+
reason: z2.literal("MAP_REDEPLOY_REQUIRED")
|
|
141
|
+
}).strict(),
|
|
142
|
+
z2.object({
|
|
143
|
+
...autoFitErrorBase,
|
|
144
|
+
code: z2.literal("STATIC_MAP_SYMBOL_TOO_LARGE"),
|
|
145
|
+
details: staticSymbolTooLargeDetailsSchema,
|
|
146
|
+
reason: z2.literal("COMPOSED_IMAGE_TOO_LARGE")
|
|
147
|
+
}).strict(),
|
|
148
|
+
z2.object({
|
|
149
|
+
...autoFitErrorBase,
|
|
150
|
+
code: z2.literal("STATIC_MAP_ATTRIBUTION_UNSUPPORTED"),
|
|
151
|
+
reason: z2.enum(["UNSUPPORTED_DECLARATION", "UNSUPPORTED_GLYPH"])
|
|
152
|
+
}).strict(),
|
|
153
|
+
z2.object({
|
|
154
|
+
...autoFitErrorBase,
|
|
155
|
+
code: z2.literal("STATIC_MAP_ATTRIBUTION_TOO_LARGE"),
|
|
156
|
+
reason: z2.literal("CONTENT_LIMIT_EXCEEDED")
|
|
157
|
+
}).strict(),
|
|
158
|
+
z2.object({
|
|
159
|
+
...autoFitErrorBase,
|
|
160
|
+
code: z2.literal("STATIC_MAP_ATTRIBUTION_DOES_NOT_FIT"),
|
|
161
|
+
reason: z2.literal("BLOCK_DOES_NOT_FIT")
|
|
70
162
|
}).strict()
|
|
71
163
|
]);
|
|
72
|
-
var StaticMapRequestError = class extends
|
|
164
|
+
var StaticMapRequestError = class extends StaticMapError {
|
|
73
165
|
code;
|
|
74
166
|
details;
|
|
75
167
|
reason;
|
|
76
168
|
retryable = false;
|
|
77
169
|
status;
|
|
78
170
|
constructor(response, status = 422) {
|
|
79
|
-
super(response
|
|
171
|
+
super(response, status);
|
|
80
172
|
this.name = "StaticMapRequestError";
|
|
81
173
|
this.code = response.code;
|
|
82
174
|
this.details = "details" in response ? response.details : void 0;
|
|
@@ -84,14 +176,14 @@ var StaticMapRequestError = class extends Error {
|
|
|
84
176
|
this.status = status;
|
|
85
177
|
}
|
|
86
178
|
};
|
|
87
|
-
var staticAutoFitPlanSchema =
|
|
88
|
-
bounds:
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
179
|
+
var staticAutoFitPlanSchema = z2.object({
|
|
180
|
+
bounds: z2.tuple([
|
|
181
|
+
z2.number().finite(),
|
|
182
|
+
z2.number().finite().min(-90).max(90),
|
|
183
|
+
z2.number().finite(),
|
|
184
|
+
z2.number().finite().min(-90).max(90)
|
|
93
185
|
]),
|
|
94
|
-
longitudeOffsets:
|
|
186
|
+
longitudeOffsets: z2.array(z2.number().int().min(-360).max(360)).max(24),
|
|
95
187
|
nominalInsets: staticPaddingSchema,
|
|
96
188
|
requiredInsets: staticPaddingSchema
|
|
97
189
|
}).strict();
|
|
@@ -199,7 +291,7 @@ function failure(response) {
|
|
|
199
291
|
return { ...response, ok: false };
|
|
200
292
|
}
|
|
201
293
|
function overlayCoordinates(overlay) {
|
|
202
|
-
if (overlay.type === "circle" || overlay.type === "marker") {
|
|
294
|
+
if (overlay.type === "circle" || overlay.type === "marker" || overlay.type === "symbol") {
|
|
203
295
|
return [overlay.coordinate];
|
|
204
296
|
}
|
|
205
297
|
if (overlay.type === "line") {
|
|
@@ -208,7 +300,8 @@ function overlayCoordinates(overlay) {
|
|
|
208
300
|
return overlay.coordinates.flat();
|
|
209
301
|
}
|
|
210
302
|
function findAmbiguousSegment(overlay) {
|
|
211
|
-
if (overlay.type === "circle" || overlay.type === "marker"
|
|
303
|
+
if (overlay.type === "circle" || overlay.type === "marker" || overlay.type === "symbol")
|
|
304
|
+
return null;
|
|
212
305
|
const paths = overlay.type === "line" ? [overlay.coordinates] : overlay.coordinates;
|
|
213
306
|
for (const [ringIndex, path] of paths.entries()) {
|
|
214
307
|
for (let segmentIndex = 0; segmentIndex < path.length - 1; segmentIndex += 1) {
|
|
@@ -228,6 +321,7 @@ function nominalOverlayExpansion(overlay) {
|
|
|
228
321
|
if (overlay.type === "circle" || overlay.type === "marker") {
|
|
229
322
|
return overlay.radius + overlay.strokeWidth;
|
|
230
323
|
}
|
|
324
|
+
if (overlay.type === "symbol") return 0;
|
|
231
325
|
if (overlay.type === "line") {
|
|
232
326
|
return overlay.width / 2 + 0.5;
|
|
233
327
|
}
|
|
@@ -320,8 +414,8 @@ function findStaticOverlayLatitudeFailure(input) {
|
|
|
320
414
|
if (!candidate || typeof candidate !== "object" || Array.isArray(candidate)) continue;
|
|
321
415
|
const overlay = candidate;
|
|
322
416
|
const type = overlay.type;
|
|
323
|
-
if (!["circle", "line", "marker", "polygon"].includes(String(type))) continue;
|
|
324
|
-
const coordinates = type === "circle" || type === "marker" ? [overlay.coordinate] : overlayCoordinateCandidates(type, overlay.coordinates);
|
|
417
|
+
if (!["circle", "line", "marker", "polygon", "symbol"].includes(String(type))) continue;
|
|
418
|
+
const coordinates = type === "circle" || type === "marker" || type === "symbol" ? [overlay.coordinate] : overlayCoordinateCandidates(type, overlay.coordinates);
|
|
325
419
|
for (const coordinate of coordinates) {
|
|
326
420
|
if (!Array.isArray(coordinate) || coordinate.length !== 2 || typeof coordinate[1] !== "number" || !Number.isFinite(coordinate[1]) || Math.abs(coordinate[1]) <= MAX_OVERLAY_LATITUDE) {
|
|
327
421
|
continue;
|
|
@@ -347,6 +441,38 @@ function findStaticOverlayLatitudeFailure(input) {
|
|
|
347
441
|
}
|
|
348
442
|
return null;
|
|
349
443
|
}
|
|
444
|
+
function findStaticSymbolLabelGlyphFailure(input) {
|
|
445
|
+
if (!input || typeof input !== "object" || Array.isArray(input)) return null;
|
|
446
|
+
const overlays = input.overlays;
|
|
447
|
+
if (!Array.isArray(overlays)) return null;
|
|
448
|
+
for (const [index, candidate] of overlays.entries()) {
|
|
449
|
+
if (index >= staticSceneLimits.maxOverlays) break;
|
|
450
|
+
if (!candidate || typeof candidate !== "object" || Array.isArray(candidate)) continue;
|
|
451
|
+
const overlay = candidate;
|
|
452
|
+
if (overlay.type !== "symbol") continue;
|
|
453
|
+
const label = overlay.label;
|
|
454
|
+
const text = typeof label === "string" ? label : label && typeof label === "object" && !Array.isArray(label) ? label.text : void 0;
|
|
455
|
+
if (typeof text !== "string") continue;
|
|
456
|
+
const unsupported = Array.from(text).find(
|
|
457
|
+
(character) => !isSupportedStaticSymbolCodePoint(character.codePointAt(0))
|
|
458
|
+
);
|
|
459
|
+
if (!unsupported) continue;
|
|
460
|
+
const codePoint = unsupported.codePointAt(0);
|
|
461
|
+
const id = typeof overlay.id === "string" && overlay.id.trim() ? overlay.id.trim() : `overlay-${index + 1}`;
|
|
462
|
+
return {
|
|
463
|
+
code: "STATIC_MAP_LABEL_UNSUPPORTED",
|
|
464
|
+
details: {
|
|
465
|
+
codePoint,
|
|
466
|
+
overlay: { id: id.slice(0, 64), index, type: "symbol" }
|
|
467
|
+
},
|
|
468
|
+
error: `Overlay overlays.${index} label contains unsupported glyph U+${codePoint.toString(16).toUpperCase().padStart(4, "0")}`,
|
|
469
|
+
ok: false,
|
|
470
|
+
reason: "UNSUPPORTED_GLYPH",
|
|
471
|
+
retryable: false
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
return null;
|
|
475
|
+
}
|
|
350
476
|
function overlayCoordinateCandidates(type, value) {
|
|
351
477
|
if (!Array.isArray(value)) return [];
|
|
352
478
|
if (type === "line") {
|
|
@@ -361,6 +487,8 @@ function overlayCoordinateCandidates(type, value) {
|
|
|
361
487
|
function validateStaticScene(input) {
|
|
362
488
|
const overlayLatitudeFailure = findStaticOverlayLatitudeFailure(input);
|
|
363
489
|
if (overlayLatitudeFailure) return overlayLatitudeFailure;
|
|
490
|
+
const labelGlyphFailure = findStaticSymbolLabelGlyphFailure(input);
|
|
491
|
+
if (labelGlyphFailure) return labelGlyphFailure;
|
|
364
492
|
const parsed = staticSceneSchema.safeParse(input);
|
|
365
493
|
if (!parsed.success) {
|
|
366
494
|
return {
|
|
@@ -387,6 +515,11 @@ function normalizeStaticScene(scene) {
|
|
|
387
515
|
const { ok: _ok, ...response } = overlayLatitudeFailure;
|
|
388
516
|
throw new StaticMapRequestError(response);
|
|
389
517
|
}
|
|
518
|
+
const labelGlyphFailure = findStaticSymbolLabelGlyphFailure(scene);
|
|
519
|
+
if (labelGlyphFailure) {
|
|
520
|
+
const { ok: _ok, ...response } = labelGlyphFailure;
|
|
521
|
+
throw new StaticMapRequestError(response);
|
|
522
|
+
}
|
|
390
523
|
const normalized = normalizeParsedStaticScene(staticSceneSchema.parse(scene));
|
|
391
524
|
if (normalized.camera.type === "auto") {
|
|
392
525
|
const analysis = analyzeStaticAutoFit(normalized);
|
|
@@ -398,6 +531,12 @@ function normalizeStaticScene(scene) {
|
|
|
398
531
|
}
|
|
399
532
|
function normalizeParsedStaticScene(parsed) {
|
|
400
533
|
return stripUndefined({
|
|
534
|
+
...parsed.attribution && Object.keys(parsed.attribution).length > 0 ? {
|
|
535
|
+
attribution: parsed.attribution.mode === "external" ? { mode: "external" } : stripUndefined({
|
|
536
|
+
mode: parsed.attribution.mode,
|
|
537
|
+
position: parsed.attribution.position
|
|
538
|
+
})
|
|
539
|
+
} : {},
|
|
401
540
|
camera: parsed.camera.type === "center" ? {
|
|
402
541
|
bearing: roundNumber(parsed.camera.bearing ?? 0),
|
|
403
542
|
center: normalizeStaticCoordinate(parsed.camera.center),
|
|
@@ -414,6 +553,7 @@ function normalizeParsedStaticScene(parsed) {
|
|
|
414
553
|
padding: normalizeAutoPadding(parsed.camera.padding, parsed.size),
|
|
415
554
|
type: "auto"
|
|
416
555
|
},
|
|
556
|
+
...parsed.format && parsed.format !== "png" ? { format: parsed.format } : {},
|
|
417
557
|
map: parsed.map,
|
|
418
558
|
overlays: parsed.overlays.map(
|
|
419
559
|
(overlay, index) => normalizeStaticOverlay({
|
|
@@ -446,6 +586,8 @@ function normalizeAutoPadding(padding, size) {
|
|
|
446
586
|
}
|
|
447
587
|
|
|
448
588
|
export {
|
|
589
|
+
staticMapErrorResponseSchema,
|
|
590
|
+
StaticMapError,
|
|
449
591
|
staticMapRequestErrorResponseSchema,
|
|
450
592
|
StaticMapRequestError,
|
|
451
593
|
staticAutoFitPlanSchema,
|