@tileflow/core 0.1.0-alpha.4 → 0.1.0-alpha.41
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/LICENSE +201 -0
- package/README.md +318 -26
- package/THIRD_PARTY_NOTICES.md +444 -0
- package/dist/3d-CzLTD0RK.d.ts +1092 -0
- package/dist/3d.d.ts +1 -0
- package/dist/3d.js +12660 -0
- package/dist/browser.d.ts +2278 -0
- package/dist/browser.js +21826 -0
- package/dist/build.d.ts +595 -0
- package/dist/build.js +29737 -0
- package/dist/capture-shot-ChPQvPyi.d.ts +291 -0
- package/dist/capture.d.ts +9 -0
- package/dist/capture.js +510 -0
- package/dist/config-reference.json +40527 -0
- package/dist/icon-composition-HUh_Lj8Z.d.ts +2516 -0
- package/dist/index.d.ts +1805 -162
- package/dist/index.js +26400 -3488
- package/dist/manifest-types-DnhSJjCM.d.ts +902 -0
- package/dist/manifest.d.ts +9 -0
- package/dist/manifest.js +449 -0
- package/dist/native-profile.d.ts +206 -0
- package/dist/native-profile.js +9339 -0
- package/dist/native.d.ts +365 -0
- package/dist/native.js +5970 -0
- package/dist/runtime.d.ts +1 -0
- package/dist/runtime.js +1445 -0
- package/dist/worker/camera-tile-worker.global.js +20 -0
- package/docs/application-tile-reading.md +86 -0
- package/docs/authoring-model.md +269 -0
- package/docs/boundary-areas-and-choropleths.md +217 -0
- package/docs/buildings-3d.md +608 -0
- package/docs/camera-moves-and-tile-cache.md +112 -0
- package/docs/capture-scenes.md +40 -0
- package/docs/capture-shots.md +173 -0
- package/docs/cartographic-defaults.md +210 -0
- package/docs/compiled-style-performance.md +286 -0
- package/docs/data-is-separate-from-design.md +610 -0
- package/docs/depth-markers.md +65 -0
- package/docs/dynamic-style-images.md +100 -0
- package/docs/globe-atmosphere.md +113 -0
- package/docs/hosted-session-authorization.md +106 -0
- package/docs/map-and-terrain-examples.md +156 -0
- package/docs/map-emphasis.md +89 -0
- package/docs/native-artifact-profile.md +152 -0
- package/docs/native-resource-urls.md +369 -0
- package/docs/nautical-marks.md +308 -0
- package/docs/public-api-and-browser-subpath.md +101 -0
- package/docs/route-drawing.md +191 -0
- package/docs/shared-visual-primitives.md +101 -0
- package/docs/street-lights.md +127 -0
- package/docs/street-sky.md +156 -0
- package/docs/style-states.md +73 -0
- package/docs/sun-and-light.md +167 -0
- package/docs/theme-transitions-and-blends.md +317 -0
- package/docs/themes-and-module-styles.md +156 -0
- package/docs/trees-3d.md +295 -0
- package/package.json +67 -8
package/dist/capture.js
ADDED
|
@@ -0,0 +1,510 @@
|
|
|
1
|
+
// src/capture-scene.ts
|
|
2
|
+
import { z as z3 } from "zod";
|
|
3
|
+
|
|
4
|
+
// src/manifest-schema-classic.ts
|
|
5
|
+
import { z as z2 } from "zod";
|
|
6
|
+
|
|
7
|
+
// src/native-zod-object.ts
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
function strictNativeObject(shape) {
|
|
10
|
+
const object = new z.core.$ZodObject({
|
|
11
|
+
type: "object",
|
|
12
|
+
shape,
|
|
13
|
+
catchall: z.never()
|
|
14
|
+
});
|
|
15
|
+
return z.lazy(() => object);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// src/manifest-schema-classic.ts
|
|
19
|
+
function classic(schema) {
|
|
20
|
+
return schema;
|
|
21
|
+
}
|
|
22
|
+
var classicManifestStringOperations = {
|
|
23
|
+
string: () => z2.string(),
|
|
24
|
+
minLength(schema, minimum) {
|
|
25
|
+
return schema.min(
|
|
26
|
+
minimum
|
|
27
|
+
);
|
|
28
|
+
},
|
|
29
|
+
maxLength(schema, maximum) {
|
|
30
|
+
return schema.max(
|
|
31
|
+
maximum
|
|
32
|
+
);
|
|
33
|
+
},
|
|
34
|
+
regex: (schema, pattern, message) => schema.regex(pattern, { message }),
|
|
35
|
+
refine: (schema, check, message) => classic(schema).refine(check, message)
|
|
36
|
+
};
|
|
37
|
+
var classicManifestSchemaOperations = {
|
|
38
|
+
...classicManifestStringOperations,
|
|
39
|
+
number: (minimum, maximum) => z2.number().finite().min(minimum).max(maximum),
|
|
40
|
+
unknown: () => z2.unknown(),
|
|
41
|
+
optional: (schema) => classic(schema).optional(),
|
|
42
|
+
array: (schema) => z2.array(classic(schema)),
|
|
43
|
+
enum(values) {
|
|
44
|
+
return z2.enum(values);
|
|
45
|
+
},
|
|
46
|
+
literal: (value) => z2.literal(value),
|
|
47
|
+
union: (left, right) => z2.union([classic(left), classic(right)]),
|
|
48
|
+
tuplePair(left, right) {
|
|
49
|
+
return z2.tuple([classic(left), classic(right)]);
|
|
50
|
+
},
|
|
51
|
+
object(shape) {
|
|
52
|
+
return strictNativeObject(shape);
|
|
53
|
+
},
|
|
54
|
+
record(key, value) {
|
|
55
|
+
return z2.record(classic(key), classic(value));
|
|
56
|
+
},
|
|
57
|
+
superRefine: (schema, check) => classic(schema).superRefine(check),
|
|
58
|
+
pipe: (input, output) => classic(input).pipe(classic(output))
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
// src/portable-identity-rules.ts
|
|
62
|
+
var tileflowPortableIdMaximumLength = 64;
|
|
63
|
+
var tileflowPortableIdPattern = /^[a-z][a-z0-9-]{0,63}$/u;
|
|
64
|
+
function isTileflowPortableId(value) {
|
|
65
|
+
return typeof value === "string" && value.length <= tileflowPortableIdMaximumLength && tileflowPortableIdPattern.test(value) && !["constructor", "prototype"].includes(value) && !/^(?:AUX|CON|NUL|PRN|COM[1-9]|LPT[1-9])$/i.test(value);
|
|
66
|
+
}
|
|
67
|
+
function isTileflowThemeName(value) {
|
|
68
|
+
return isTileflowPortableId(value) && value !== "system";
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// src/portable-identity-schema.ts
|
|
72
|
+
function createPortableIdentitySchemas(operations) {
|
|
73
|
+
const bounded = operations.maxLength(
|
|
74
|
+
operations.minLength(operations.string(), 1),
|
|
75
|
+
tileflowPortableIdMaximumLength
|
|
76
|
+
);
|
|
77
|
+
const pattern = operations.regex(
|
|
78
|
+
bounded,
|
|
79
|
+
tileflowPortableIdPattern,
|
|
80
|
+
"Expected lowercase kebab-case beginning with a letter"
|
|
81
|
+
);
|
|
82
|
+
const tileflowPortableIdSchema2 = operations.refine(
|
|
83
|
+
pattern,
|
|
84
|
+
(name) => !["constructor", "prototype"].includes(name) && !/^(?:AUX|CON|NUL|PRN|COM[1-9]|LPT[1-9])$/i.test(name),
|
|
85
|
+
"Expected a portable identifier that is not a reserved filename or prototype key"
|
|
86
|
+
);
|
|
87
|
+
const tileflowThemeNameSchema2 = operations.refine(
|
|
88
|
+
tileflowPortableIdSchema2,
|
|
89
|
+
isTileflowThemeName,
|
|
90
|
+
"Expected a concrete theme name; system is browser-only"
|
|
91
|
+
);
|
|
92
|
+
return { tileflowPortableIdSchema: tileflowPortableIdSchema2, tileflowThemeNameSchema: tileflowThemeNameSchema2 };
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// src/portable-identity.ts
|
|
96
|
+
var schemas = createPortableIdentitySchemas(classicManifestStringOperations);
|
|
97
|
+
var tileflowPortableIdSchema = schemas.tileflowPortableIdSchema;
|
|
98
|
+
var tileflowThemeNameSchema = schemas.tileflowThemeNameSchema;
|
|
99
|
+
|
|
100
|
+
// src/camera-limits.ts
|
|
101
|
+
var tileflowMaxPitch = 85;
|
|
102
|
+
|
|
103
|
+
// src/capture-scene.ts
|
|
104
|
+
var tileflowCaptureSceneSchemaVersion = 1;
|
|
105
|
+
var tileflowCaptureSceneLimits = Object.freeze({
|
|
106
|
+
applicationPathLength: 2048,
|
|
107
|
+
boundsPadding: { maximum: 1024, minimum: 0 },
|
|
108
|
+
captureIdLength: tileflowPortableIdMaximumLength,
|
|
109
|
+
maximumPhysicalPixels: 16777216,
|
|
110
|
+
pitch: { maximum: tileflowMaxPitch, minimum: 0 },
|
|
111
|
+
selectorLength: 256,
|
|
112
|
+
viewport: { maximum: 4096, minimum: 64 },
|
|
113
|
+
zoom: { maximum: 24, minimum: 0 }
|
|
114
|
+
});
|
|
115
|
+
var finiteLongitudeSchema = z3.number().finite().min(-180).max(180);
|
|
116
|
+
var finiteLatitudeSchema = z3.number().finite().min(-90).max(90);
|
|
117
|
+
var bearingSchema = z3.number().finite().min(-180).max(180).optional();
|
|
118
|
+
var pitchSchema = z3.number().finite().min(tileflowCaptureSceneLimits.pitch.minimum).max(tileflowCaptureSceneLimits.pitch.maximum).optional();
|
|
119
|
+
var tileflowCaptureCenterCameraSchema = z3.object({
|
|
120
|
+
type: z3.literal("center"),
|
|
121
|
+
center: z3.tuple([finiteLongitudeSchema, finiteLatitudeSchema]),
|
|
122
|
+
zoom: z3.number().finite().min(tileflowCaptureSceneLimits.zoom.minimum).max(tileflowCaptureSceneLimits.zoom.maximum),
|
|
123
|
+
bearing: bearingSchema,
|
|
124
|
+
pitch: pitchSchema
|
|
125
|
+
}).strict();
|
|
126
|
+
var tileflowCaptureBoundsCameraSchema = z3.object({
|
|
127
|
+
type: z3.literal("bounds"),
|
|
128
|
+
bounds: z3.tuple([
|
|
129
|
+
finiteLongitudeSchema,
|
|
130
|
+
finiteLatitudeSchema,
|
|
131
|
+
finiteLongitudeSchema,
|
|
132
|
+
finiteLatitudeSchema
|
|
133
|
+
]),
|
|
134
|
+
padding: z3.number().finite().min(tileflowCaptureSceneLimits.boundsPadding.minimum).max(tileflowCaptureSceneLimits.boundsPadding.maximum).optional(),
|
|
135
|
+
bearing: bearingSchema,
|
|
136
|
+
pitch: pitchSchema
|
|
137
|
+
}).strict().superRefine((camera, context) => {
|
|
138
|
+
const [west, south, east, north] = camera.bounds;
|
|
139
|
+
if (south >= north) {
|
|
140
|
+
context.addIssue({
|
|
141
|
+
code: "custom",
|
|
142
|
+
message: "Expected south latitude to be less than north latitude",
|
|
143
|
+
path: ["bounds"]
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
if (west === east) {
|
|
147
|
+
context.addIssue({
|
|
148
|
+
code: "custom",
|
|
149
|
+
message: "Expected west and east longitude to define a non-empty span",
|
|
150
|
+
path: ["bounds"]
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
var tileflowCaptureCameraSchema = z3.discriminatedUnion("type", [
|
|
155
|
+
tileflowCaptureCenterCameraSchema,
|
|
156
|
+
tileflowCaptureBoundsCameraSchema
|
|
157
|
+
]);
|
|
158
|
+
var tileflowCaptureViewportSchema = z3.object({
|
|
159
|
+
width: z3.number().int().min(tileflowCaptureSceneLimits.viewport.minimum).max(tileflowCaptureSceneLimits.viewport.maximum),
|
|
160
|
+
height: z3.number().int().min(tileflowCaptureSceneLimits.viewport.minimum).max(tileflowCaptureSceneLimits.viewport.maximum),
|
|
161
|
+
dpr: z3.union([z3.literal(1), z3.literal(2)]).optional()
|
|
162
|
+
}).strict().superRefine((viewport, context) => {
|
|
163
|
+
const dpr = viewport.dpr ?? 1;
|
|
164
|
+
const physicalWidth = viewport.width * dpr;
|
|
165
|
+
const physicalHeight = viewport.height * dpr;
|
|
166
|
+
const physicalPixels = viewport.width * viewport.height * dpr * dpr;
|
|
167
|
+
if (physicalWidth > tileflowCaptureSceneLimits.viewport.maximum || physicalHeight > tileflowCaptureSceneLimits.viewport.maximum || physicalPixels > tileflowCaptureSceneLimits.maximumPhysicalPixels) {
|
|
168
|
+
context.addIssue({
|
|
169
|
+
code: "custom",
|
|
170
|
+
message: `Expected physical width and height at most ${tileflowCaptureSceneLimits.viewport.maximum} pixels`,
|
|
171
|
+
path: ["dpr"]
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
var tileflowCaptureIdSchema = z3.string().min(1).max(tileflowCaptureSceneLimits.captureIdLength).regex(/^[A-Za-z0-9_-]+$/, {
|
|
176
|
+
message: "Expected letters, numbers, underscores, or hyphens"
|
|
177
|
+
}).refine((name) => name !== "__proto__", {
|
|
178
|
+
message: "Expected an identifier that cannot mutate an object prototype"
|
|
179
|
+
});
|
|
180
|
+
var tileflowCaptureSceneNameSchema = tileflowPortableIdSchema;
|
|
181
|
+
var applicationPathSchema = z3.string().min(1).max(tileflowCaptureSceneLimits.applicationPathLength).refine(isPortableApplicationPath, {
|
|
182
|
+
message: "Expected a root-relative application path without an origin, credentials, fragment, backslash, or control character"
|
|
183
|
+
});
|
|
184
|
+
var tileflowCaptureMapTargetSchema = z3.object({ kind: z3.literal("map") }).strict();
|
|
185
|
+
var tileflowCaptureApplicationTargetSchema = z3.object({
|
|
186
|
+
kind: z3.literal("application"),
|
|
187
|
+
path: applicationPathSchema,
|
|
188
|
+
captureId: tileflowCaptureIdSchema.optional(),
|
|
189
|
+
selector: z3.string().trim().min(1).max(tileflowCaptureSceneLimits.selectorLength).optional(),
|
|
190
|
+
frame: z3.enum(["map", "viewport"]).optional()
|
|
191
|
+
}).strict().superRefine((target, context) => {
|
|
192
|
+
if (target.captureId && target.selector) {
|
|
193
|
+
context.addIssue({
|
|
194
|
+
code: "custom",
|
|
195
|
+
message: "Expected either captureId or selector, not both",
|
|
196
|
+
path: ["selector"]
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
var tileflowCaptureTargetSchema = z3.discriminatedUnion("kind", [
|
|
201
|
+
tileflowCaptureMapTargetSchema,
|
|
202
|
+
tileflowCaptureApplicationTargetSchema
|
|
203
|
+
]);
|
|
204
|
+
var tileflowCaptureSceneSchema = z3.object({
|
|
205
|
+
map: tileflowPortableIdSchema,
|
|
206
|
+
theme: tileflowThemeNameSchema,
|
|
207
|
+
camera: tileflowCaptureCameraSchema,
|
|
208
|
+
viewport: tileflowCaptureViewportSchema,
|
|
209
|
+
target: tileflowCaptureTargetSchema.optional()
|
|
210
|
+
}).strict();
|
|
211
|
+
function tileflowCaptureSceneFitIssue(scene) {
|
|
212
|
+
if (scene.camera.type !== "bounds" || scene.camera.padding === void 0) return void 0;
|
|
213
|
+
const room = Math.min(scene.viewport.width, scene.viewport.height);
|
|
214
|
+
if (scene.camera.padding * 2 < room) return void 0;
|
|
215
|
+
return {
|
|
216
|
+
message: `Expected bounds padding less than half the viewport's shorter side (${room / 2} pixels)`,
|
|
217
|
+
path: ["camera", "padding"]
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
function normalizeTileflowCaptureScene(input) {
|
|
221
|
+
const scene = tileflowCaptureSceneSchema.parse(input);
|
|
222
|
+
const fit = tileflowCaptureSceneFitIssue(scene);
|
|
223
|
+
if (fit) throw new RangeError(`${fit.path.join(".")}: ${fit.message}`);
|
|
224
|
+
const camera = scene.camera.type === "center" ? {
|
|
225
|
+
type: scene.camera.type,
|
|
226
|
+
center: scene.camera.center,
|
|
227
|
+
zoom: scene.camera.zoom,
|
|
228
|
+
bearing: scene.camera.bearing ?? 0,
|
|
229
|
+
pitch: scene.camera.pitch ?? 0
|
|
230
|
+
} : {
|
|
231
|
+
type: scene.camera.type,
|
|
232
|
+
bounds: scene.camera.bounds,
|
|
233
|
+
padding: scene.camera.padding ?? 0,
|
|
234
|
+
bearing: scene.camera.bearing ?? 0,
|
|
235
|
+
pitch: scene.camera.pitch ?? 0
|
|
236
|
+
};
|
|
237
|
+
const target = scene.target ?? { kind: "map" };
|
|
238
|
+
return {
|
|
239
|
+
map: scene.map,
|
|
240
|
+
theme: scene.theme,
|
|
241
|
+
camera,
|
|
242
|
+
viewport: { ...scene.viewport, dpr: scene.viewport.dpr ?? 1 },
|
|
243
|
+
target: target.kind === "map" ? target : {
|
|
244
|
+
kind: target.kind,
|
|
245
|
+
path: target.path,
|
|
246
|
+
...target.captureId ? { captureId: target.captureId } : {},
|
|
247
|
+
...target.selector ? { selector: target.selector } : {},
|
|
248
|
+
frame: target.frame ?? "map"
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
function normalizeTileflowCaptureId(value) {
|
|
253
|
+
return value === void 0 ? void 0 : tileflowCaptureIdSchema.parse(value);
|
|
254
|
+
}
|
|
255
|
+
function isPortableApplicationPath(value) {
|
|
256
|
+
if (value !== value.trim() || !value.startsWith("/") || value.startsWith("//") || value.includes("#") || value.includes("\\") || hasControlCharacter(value)) {
|
|
257
|
+
return false;
|
|
258
|
+
}
|
|
259
|
+
try {
|
|
260
|
+
const base = new URL("http://tileflow.local");
|
|
261
|
+
const resolved = new URL(value, base);
|
|
262
|
+
return resolved.origin === base.origin && resolved.username === "" && resolved.password === "" && resolved.pathname.startsWith("/");
|
|
263
|
+
} catch {
|
|
264
|
+
return false;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
function hasControlCharacter(value) {
|
|
268
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
269
|
+
const code = value.charCodeAt(index);
|
|
270
|
+
if (code <= 31 || code === 127) return true;
|
|
271
|
+
}
|
|
272
|
+
return false;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// src/capture-shot.ts
|
|
276
|
+
import { z as z4 } from "zod";
|
|
277
|
+
|
|
278
|
+
// src/capture-shot-motion.ts
|
|
279
|
+
function tileflowShotStateAt(shot, at) {
|
|
280
|
+
const { keyframes } = shot;
|
|
281
|
+
const first = keyframes[0];
|
|
282
|
+
const last = keyframes.at(-1);
|
|
283
|
+
if (!first || !last || keyframes.length < 2)
|
|
284
|
+
throw new TypeError("A shot needs at least two keyframes.");
|
|
285
|
+
const time = Math.min(last.at, Math.max(first.at, Number.isFinite(at) ? at : first.at));
|
|
286
|
+
let segment = 0;
|
|
287
|
+
while (segment < keyframes.length - 2 && time > keyframes[segment + 1].at) segment += 1;
|
|
288
|
+
const from = keyframes[segment];
|
|
289
|
+
const to = keyframes[segment + 1];
|
|
290
|
+
const linear = Math.min(1, Math.max(0, (time - from.at) / (to.at - from.at)));
|
|
291
|
+
const t = shot.easing === "linear" ? linear : smoothstep(linear);
|
|
292
|
+
return {
|
|
293
|
+
at: time,
|
|
294
|
+
camera: interpolateCamera(cameraOf(from), cameraOf(to), t),
|
|
295
|
+
...from.blend && to.blend ? { blendPosition: interpolateNumber(from.blend.position, to.blend.position, t) } : {}
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
function cameraOf({ camera }) {
|
|
299
|
+
return {
|
|
300
|
+
type: "center",
|
|
301
|
+
center: [camera.center[0], camera.center[1]],
|
|
302
|
+
zoom: camera.zoom,
|
|
303
|
+
bearing: camera.bearing ?? 0,
|
|
304
|
+
pitch: camera.pitch ?? 0
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
var maximumMercatorLatitude = 85.051129;
|
|
308
|
+
function interpolateCamera(from, to, t) {
|
|
309
|
+
return {
|
|
310
|
+
type: "center",
|
|
311
|
+
center: interpolateCenter(from.center, to.center, t),
|
|
312
|
+
zoom: interpolateNumber(from.zoom, to.zoom, t),
|
|
313
|
+
bearing: interpolateBearing(from.bearing, to.bearing, t),
|
|
314
|
+
pitch: interpolateNumber(from.pitch, to.pitch, t)
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
function interpolateNumber(from, to, t) {
|
|
318
|
+
if (t <= 0 || from === to) return from;
|
|
319
|
+
if (t >= 1) return to;
|
|
320
|
+
return from + (to - from) * t;
|
|
321
|
+
}
|
|
322
|
+
function interpolateCenter(from, to, t) {
|
|
323
|
+
if (t <= 0 || from[0] === to[0] && from[1] === to[1]) return [from[0], from[1]];
|
|
324
|
+
if (t >= 1) return [to[0], to[1]];
|
|
325
|
+
const start = mercatorFromLngLat(from);
|
|
326
|
+
const end = mercatorFromLngLat(to);
|
|
327
|
+
let dx = end[0] - start[0];
|
|
328
|
+
if (dx > 0.5) dx -= 1;
|
|
329
|
+
else if (dx < -0.5) dx += 1;
|
|
330
|
+
return lngLatFromMercator([start[0] + dx * t, start[1] + (end[1] - start[1]) * t]);
|
|
331
|
+
}
|
|
332
|
+
function interpolateBearing(from, to, t) {
|
|
333
|
+
if (t <= 0 || from === to) return from;
|
|
334
|
+
if (t >= 1) return to;
|
|
335
|
+
const delta = ((to - from) % 360 + 540) % 360 - 180;
|
|
336
|
+
return wrapDegrees(from + delta * t);
|
|
337
|
+
}
|
|
338
|
+
function mercatorFromLngLat([longitude, latitude]) {
|
|
339
|
+
const clamped = Math.max(-maximumMercatorLatitude, Math.min(maximumMercatorLatitude, latitude));
|
|
340
|
+
return [
|
|
341
|
+
(180 + longitude) / 360,
|
|
342
|
+
(180 - 180 / Math.PI * Math.log(Math.tan(Math.PI / 4 + clamped * Math.PI / 360))) / 360
|
|
343
|
+
];
|
|
344
|
+
}
|
|
345
|
+
function lngLatFromMercator([x, y]) {
|
|
346
|
+
const latitude = 360 / Math.PI * Math.atan(Math.exp((180 - y * 360) * Math.PI / 180)) - 90;
|
|
347
|
+
return [wrapDegrees(x * 360 - 180), latitude];
|
|
348
|
+
}
|
|
349
|
+
function wrapDegrees(value) {
|
|
350
|
+
if (value >= -180 && value <= 180) return value;
|
|
351
|
+
return ((value + 180) % 360 + 360) % 360 - 180;
|
|
352
|
+
}
|
|
353
|
+
function smoothstep(t) {
|
|
354
|
+
return t * t * (3 - 2 * t);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// src/capture-shot.ts
|
|
358
|
+
var tileflowCaptureShotSchemaVersion = 1;
|
|
359
|
+
var tileflowCaptureShotLimits = Object.freeze({
|
|
360
|
+
blendThemes: Object.freeze({ maximum: 8, minimum: 2 }),
|
|
361
|
+
defaultFps: 30,
|
|
362
|
+
durationMs: Object.freeze({ maximum: 6e4 }),
|
|
363
|
+
fps: Object.freeze([24, 25, 30, 50, 60]),
|
|
364
|
+
keyframes: Object.freeze({ maximum: 64, minimum: 2 }),
|
|
365
|
+
maximumFrames: 3600
|
|
366
|
+
});
|
|
367
|
+
var tileflowCaptureShotNameSchema = tileflowCaptureSceneNameSchema;
|
|
368
|
+
var tileflowCaptureShotBlendSchema = z4.object({
|
|
369
|
+
themes: z4.array(tileflowThemeNameSchema).min(tileflowCaptureShotLimits.blendThemes.minimum).max(tileflowCaptureShotLimits.blendThemes.maximum),
|
|
370
|
+
position: z4.number().finite().min(0)
|
|
371
|
+
}).strict().superRefine((blend, context) => {
|
|
372
|
+
if (new Set(blend.themes).size !== blend.themes.length) {
|
|
373
|
+
context.addIssue({ code: "custom", message: "Expected distinct themes", path: ["themes"] });
|
|
374
|
+
}
|
|
375
|
+
if (blend.position > blend.themes.length - 1) {
|
|
376
|
+
context.addIssue({
|
|
377
|
+
code: "custom",
|
|
378
|
+
message: `Expected a position from 0 to ${blend.themes.length - 1}`,
|
|
379
|
+
path: ["position"]
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
});
|
|
383
|
+
var tileflowCaptureShotKeyframeSchema = z4.object({
|
|
384
|
+
at: z4.number().int().min(0).max(tileflowCaptureShotLimits.durationMs.maximum),
|
|
385
|
+
camera: tileflowCaptureCenterCameraSchema,
|
|
386
|
+
blend: tileflowCaptureShotBlendSchema.optional()
|
|
387
|
+
}).strict();
|
|
388
|
+
var tileflowCaptureShotFields = {
|
|
389
|
+
theme: tileflowThemeNameSchema,
|
|
390
|
+
viewport: tileflowCaptureViewportSchema,
|
|
391
|
+
fps: z4.literal(tileflowCaptureShotLimits.fps).optional(),
|
|
392
|
+
keyframes: z4.array(tileflowCaptureShotKeyframeSchema).min(tileflowCaptureShotLimits.keyframes.minimum).max(tileflowCaptureShotLimits.keyframes.maximum),
|
|
393
|
+
easing: z4.enum(["smooth", "linear"]).optional()
|
|
394
|
+
};
|
|
395
|
+
var tileflowCaptureShotSchema = z4.object({ map: tileflowPortableIdSchema, ...tileflowCaptureShotFields }).strict().superRefine(checkShotTiming);
|
|
396
|
+
var tileflowMapShotSchema = z4.object(tileflowCaptureShotFields).strict().superRefine(checkShotTiming);
|
|
397
|
+
function normalizeTileflowCaptureShot(input) {
|
|
398
|
+
const shot = tileflowCaptureShotSchema.parse(input);
|
|
399
|
+
return { map: shot.map, ...normalizeShotFields(shot) };
|
|
400
|
+
}
|
|
401
|
+
function tileflowCaptureShotFrameCount(shot) {
|
|
402
|
+
const fps = shot.fps ?? tileflowCaptureShotLimits.defaultFps;
|
|
403
|
+
const duration = shot.keyframes.at(-1)?.at ?? 0;
|
|
404
|
+
return Math.floor(duration * fps / 1e3 + 1e-9) + 1;
|
|
405
|
+
}
|
|
406
|
+
function tileflowCaptureShotFrames(shot) {
|
|
407
|
+
const { map: _map, ...fields } = shot;
|
|
408
|
+
const normalized = normalizeShotFields(tileflowMapShotSchema.parse(fields));
|
|
409
|
+
const count = tileflowCaptureShotFrameCount(normalized);
|
|
410
|
+
const duration = normalized.keyframes.at(-1).at;
|
|
411
|
+
const frames = [];
|
|
412
|
+
for (let index = 0; index < count; index += 1) {
|
|
413
|
+
const at = Math.min(duration, index * 1e3 / normalized.fps);
|
|
414
|
+
frames.push({ index, ...tileflowShotStateAt(normalized, at) });
|
|
415
|
+
}
|
|
416
|
+
return frames;
|
|
417
|
+
}
|
|
418
|
+
function normalizeShotFields(shot) {
|
|
419
|
+
return {
|
|
420
|
+
theme: shot.theme,
|
|
421
|
+
viewport: { ...shot.viewport, dpr: shot.viewport.dpr ?? 1 },
|
|
422
|
+
fps: shot.fps ?? tileflowCaptureShotLimits.defaultFps,
|
|
423
|
+
easing: shot.easing ?? "smooth",
|
|
424
|
+
keyframes: shot.keyframes.map((keyframe) => ({
|
|
425
|
+
at: keyframe.at,
|
|
426
|
+
camera: {
|
|
427
|
+
type: "center",
|
|
428
|
+
center: keyframe.camera.center,
|
|
429
|
+
zoom: keyframe.camera.zoom,
|
|
430
|
+
bearing: keyframe.camera.bearing ?? 0,
|
|
431
|
+
pitch: keyframe.camera.pitch ?? 0
|
|
432
|
+
},
|
|
433
|
+
...keyframe.blend ? { blend: { themes: [...keyframe.blend.themes], position: keyframe.blend.position } } : {}
|
|
434
|
+
}))
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
function checkShotTiming(shot, context) {
|
|
438
|
+
const { keyframes } = shot;
|
|
439
|
+
if (keyframes[0] && keyframes[0].at !== 0) {
|
|
440
|
+
context.addIssue({
|
|
441
|
+
code: "custom",
|
|
442
|
+
message: "Expected the first keyframe at 0 milliseconds",
|
|
443
|
+
path: ["keyframes", 0, "at"]
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
for (let index = 1; index < keyframes.length; index += 1) {
|
|
447
|
+
if (keyframes[index].at <= keyframes[index - 1].at) {
|
|
448
|
+
context.addIssue({
|
|
449
|
+
code: "custom",
|
|
450
|
+
message: "Expected a time after the previous keyframe",
|
|
451
|
+
path: ["keyframes", index, "at"]
|
|
452
|
+
});
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
const blended = keyframes.filter((keyframe) => keyframe.blend !== void 0);
|
|
456
|
+
if (blended.length > 0) {
|
|
457
|
+
const themes = JSON.stringify(blended[0].blend.themes);
|
|
458
|
+
keyframes.forEach((keyframe, index) => {
|
|
459
|
+
if (!keyframe.blend) {
|
|
460
|
+
context.addIssue({
|
|
461
|
+
code: "custom",
|
|
462
|
+
message: "Expected a blend on every keyframe when one keyframe blends themes",
|
|
463
|
+
path: ["keyframes", index, "blend"]
|
|
464
|
+
});
|
|
465
|
+
} else if (JSON.stringify(keyframe.blend.themes) !== themes) {
|
|
466
|
+
context.addIssue({
|
|
467
|
+
code: "custom",
|
|
468
|
+
message: "Expected the same themes, in the same order, on every keyframe",
|
|
469
|
+
path: ["keyframes", index, "blend", "themes"]
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
const frames = tileflowCaptureShotFrameCount(shot);
|
|
475
|
+
if (frames > tileflowCaptureShotLimits.maximumFrames) {
|
|
476
|
+
context.addIssue({
|
|
477
|
+
code: "custom",
|
|
478
|
+
message: `Expected at most ${tileflowCaptureShotLimits.maximumFrames} frames; this shot renders ${frames}`,
|
|
479
|
+
path: ["keyframes", keyframes.length - 1, "at"]
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
export {
|
|
484
|
+
normalizeTileflowCaptureId,
|
|
485
|
+
normalizeTileflowCaptureScene,
|
|
486
|
+
normalizeTileflowCaptureShot,
|
|
487
|
+
tileflowCaptureApplicationTargetSchema,
|
|
488
|
+
tileflowCaptureBoundsCameraSchema,
|
|
489
|
+
tileflowCaptureCameraSchema,
|
|
490
|
+
tileflowCaptureCenterCameraSchema,
|
|
491
|
+
tileflowCaptureIdSchema,
|
|
492
|
+
tileflowCaptureMapTargetSchema,
|
|
493
|
+
tileflowCaptureSceneFitIssue,
|
|
494
|
+
tileflowCaptureSceneLimits,
|
|
495
|
+
tileflowCaptureSceneNameSchema,
|
|
496
|
+
tileflowCaptureSceneSchema,
|
|
497
|
+
tileflowCaptureSceneSchemaVersion,
|
|
498
|
+
tileflowCaptureShotBlendSchema,
|
|
499
|
+
tileflowCaptureShotFrameCount,
|
|
500
|
+
tileflowCaptureShotFrames,
|
|
501
|
+
tileflowCaptureShotKeyframeSchema,
|
|
502
|
+
tileflowCaptureShotLimits,
|
|
503
|
+
tileflowCaptureShotNameSchema,
|
|
504
|
+
tileflowCaptureShotSchema,
|
|
505
|
+
tileflowCaptureShotSchemaVersion,
|
|
506
|
+
tileflowCaptureTargetSchema,
|
|
507
|
+
tileflowCaptureViewportSchema,
|
|
508
|
+
tileflowPortableIdSchema,
|
|
509
|
+
tileflowThemeNameSchema
|
|
510
|
+
};
|