isard 1.4.0 → 1.6.0

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.
Files changed (135) hide show
  1. package/CHANGELOG.md +220 -2
  2. package/README.md +23 -7
  3. package/dist/basemap/furniture.d.ts +8 -0
  4. package/dist/basemap/furniture.js +26 -0
  5. package/dist/basemap/furniture.js.map +1 -0
  6. package/dist/basemap/trees.d.ts +35 -8
  7. package/dist/basemap/trees.js +105 -52
  8. package/dist/basemap/trees.js.map +1 -1
  9. package/dist/basemap/vectorstyle.d.ts +9 -3
  10. package/dist/basemap/vectorstyle.js +5 -6
  11. package/dist/basemap/vectorstyle.js.map +1 -1
  12. package/dist/basemap/vectortiles/context.d.ts +46 -0
  13. package/dist/basemap/vectortiles/context.js +6 -0
  14. package/dist/basemap/vectortiles/context.js.map +1 -0
  15. package/dist/basemap/vectortiles/extrusions.d.ts +15 -0
  16. package/dist/basemap/vectortiles/extrusions.js +61 -0
  17. package/dist/basemap/vectortiles/extrusions.js.map +1 -0
  18. package/dist/basemap/vectortiles/labels.d.ts +25 -0
  19. package/dist/basemap/vectortiles/labels.js +250 -0
  20. package/dist/basemap/vectortiles/labels.js.map +1 -0
  21. package/dist/basemap/vectortiles/lamps.d.ts +23 -0
  22. package/dist/basemap/vectortiles/lamps.js +90 -0
  23. package/dist/basemap/vectortiles/lamps.js.map +1 -0
  24. package/dist/basemap/vectortiles/requests.d.ts +28 -0
  25. package/dist/basemap/vectortiles/requests.js +74 -0
  26. package/dist/basemap/vectortiles/requests.js.map +1 -0
  27. package/dist/basemap/vectortiles/street-wind.d.ts +19 -0
  28. package/dist/basemap/vectortiles/street-wind.js +133 -0
  29. package/dist/basemap/vectortiles/street-wind.js.map +1 -0
  30. package/dist/basemap/vectortiles/style.d.ts +47 -0
  31. package/dist/basemap/vectortiles/style.js +141 -0
  32. package/dist/basemap/vectortiles/style.js.map +1 -0
  33. package/dist/basemap/vectortiles/tiles.d.ts +34 -0
  34. package/dist/basemap/vectortiles/tiles.js +62 -0
  35. package/dist/basemap/vectortiles/tiles.js.map +1 -0
  36. package/dist/basemap/vectortiles/trees.d.ts +28 -0
  37. package/dist/basemap/vectortiles/trees.js +178 -0
  38. package/dist/basemap/vectortiles/trees.js.map +1 -0
  39. package/dist/basemap/vectortiles/vectors.d.ts +37 -0
  40. package/dist/basemap/vectortiles/vectors.js +231 -0
  41. package/dist/basemap/vectortiles/vectors.js.map +1 -0
  42. package/dist/basemap/vectortiles.d.ts +49 -96
  43. package/dist/basemap/vectortiles.js +99 -774
  44. package/dist/basemap/vectortiles.js.map +1 -1
  45. package/dist/basemap/workerpool.js +25 -6
  46. package/dist/basemap/workerpool.js.map +1 -1
  47. package/dist/basemap.worker.js +2 -2
  48. package/dist/basemap.worker.js.map +14 -12
  49. package/dist/bind/layers.js +1 -0
  50. package/dist/bind/layers.js.map +1 -1
  51. package/dist/data/collada.d.ts +19 -0
  52. package/dist/data/collada.js +319 -0
  53. package/dist/data/collada.js.map +1 -0
  54. package/dist/data/ifc.d.ts +100 -0
  55. package/dist/data/ifc.js +372 -0
  56. package/dist/data/ifc.js.map +1 -0
  57. package/dist/decoder.worker.js +5 -4
  58. package/dist/decoder.worker.js.map +8 -6
  59. package/dist/engine/decoders.d.ts +4 -8
  60. package/dist/engine/decoders.js +29 -2
  61. package/dist/engine/decoders.js.map +1 -1
  62. package/dist/formats.d.ts +7 -2
  63. package/dist/formats.js +7 -2
  64. package/dist/formats.js.map +1 -1
  65. package/dist/index.d.ts +15 -2
  66. package/dist/index.js +7 -2
  67. package/dist/index.js.map +1 -1
  68. package/dist/isard_bg.wasm +0 -0
  69. package/dist/isard_core_bg.wasm +0 -0
  70. package/dist/isard_grid_bg.wasm +0 -0
  71. package/dist/isard_tess_bg.wasm +0 -0
  72. package/dist/layers/extrusions.d.ts +10 -0
  73. package/dist/layers/extrusions.js +28 -2
  74. package/dist/layers/extrusions.js.map +1 -1
  75. package/dist/layers/flow.d.ts +9 -2
  76. package/dist/layers/flow.js +51 -10
  77. package/dist/layers/flow.js.map +1 -1
  78. package/dist/layers/layers.d.ts +12 -0
  79. package/dist/layers/layers.js +44 -1
  80. package/dist/layers/layers.js.map +1 -1
  81. package/dist/layers/panoramas.d.ts +2 -0
  82. package/dist/layers/panoramas.js +27 -6
  83. package/dist/layers/panoramas.js.map +1 -1
  84. package/dist/layers/photos.d.ts +48 -0
  85. package/dist/layers/photos.js +140 -0
  86. package/dist/layers/photos.js.map +1 -0
  87. package/dist/layers/symbols.d.ts +5 -1
  88. package/dist/layers/symbols.js +111 -32
  89. package/dist/layers/symbols.js.map +1 -1
  90. package/dist/layers/trees.d.ts +28 -0
  91. package/dist/layers/trees.js +51 -0
  92. package/dist/layers/trees.js.map +1 -0
  93. package/dist/layers/vectors.d.ts +28 -2
  94. package/dist/layers/vectors.js +81 -7
  95. package/dist/layers/vectors.js.map +1 -1
  96. package/dist/map/layer-spec.d.ts +3 -0
  97. package/dist/map/layer-spec.js +2 -0
  98. package/dist/map/layer-spec.js.map +1 -1
  99. package/dist/map/workers.js +3 -0
  100. package/dist/map/workers.js.map +1 -1
  101. package/dist/map.d.ts +8 -0
  102. package/dist/map.js +15 -2
  103. package/dist/map.js.map +1 -1
  104. package/dist/panoramax.d.ts +49 -0
  105. package/dist/panoramax.js +71 -0
  106. package/dist/panoramax.js.map +1 -0
  107. package/dist/react/Layer.d.ts +1 -0
  108. package/dist/react/Layer.js.map +1 -1
  109. package/dist/react/VectorGeometry.d.ts +5 -0
  110. package/dist/react/VectorGeometry.js +12 -4
  111. package/dist/react/VectorGeometry.js.map +1 -1
  112. package/dist/util/gltf.d.ts +9 -0
  113. package/dist/util/gltf.js +27 -0
  114. package/dist/util/gltf.js.map +1 -0
  115. package/dist/util/read.d.ts +4 -0
  116. package/dist/util/read.js +28 -0
  117. package/dist/util/read.js.map +1 -0
  118. package/dist/util/roofs.d.ts +10 -0
  119. package/dist/util/roofs.js +30 -0
  120. package/dist/util/roofs.js.map +1 -0
  121. package/dist/wasm/isard.d.ts +74 -22
  122. package/dist/wasm/isard.js +153 -22
  123. package/dist/wasm/isard_core.d.ts +62 -22
  124. package/dist/wasm/isard_core.js +129 -22
  125. package/dist/wasm/isard_tess.d.ts +6 -3
  126. package/dist/wasm/isard_tess.js +27 -4
  127. package/dist/workers/basemap/geometry.d.ts +38 -22
  128. package/dist/workers/basemap/labels.d.ts +14 -1
  129. package/dist/workers/basemap/lamps.d.ts +10 -0
  130. package/dist/workers/basemap/street.d.ts +1 -1
  131. package/dist/workers/basemap/trees.d.ts +3 -1
  132. package/dist/workers/basemap.worker.d.ts +43 -3
  133. package/dist/workers/basemap.worker.js +14 -2
  134. package/dist/workers/basemap.worker.js.map +1 -1
  135. package/package.json +1 -1
@@ -0,0 +1,46 @@
1
+ /**
2
+ * What the layers of a `VectorTiles` (its extrusions, streets' wind, trees, vectors and labels) use of it: built by
3
+ * the instance for its layers only, its own fields staying private.
4
+ */
5
+ import type { LoadedSprite } from '../../layers/icons.ts';
6
+ import type { Layer, RasterLayer } from '../../layers/layers.ts';
7
+ import type { GeoMap } from '../../map.ts';
8
+ import type { BasemapResponse, BasemapWork } from '../../workers/basemap.worker.ts';
9
+ import type { Style, StyleLayer } from '../vectorstyle.ts';
10
+ import type { BasemapWorkers } from '../workerpool.ts';
11
+ import type { TileRequests } from './requests.ts';
12
+ import type { LoadedStyle } from './style.ts';
13
+ /** A renderer sent to the workers: its key, the source's deepest level, its style layers */
14
+ export interface Renderer {
15
+ key: string;
16
+ maxzoom: number;
17
+ layers: StyleLayer[];
18
+ }
19
+ export interface VectorTilesContext {
20
+ readonly map: GeoMap;
21
+ /** The style once loaded, with its source resolved */
22
+ readonly ready: Promise<LoadedStyle>;
23
+ /** Its raster and label layers, removed with it: a layer made is added to them */
24
+ readonly layers: Layer[];
25
+ /** Called when it is destroyed: a layer adds what stops it (its listeners, its timers) */
26
+ readonly cleanup: (() => void)[];
27
+ /** Prefix of the names of its sprites' images among the map's icons */
28
+ readonly iconPrefix: string;
29
+ /** Whether it was destroyed */
30
+ destroyed(): boolean;
31
+ /** Its first raster layer: the ground its labels follow */
32
+ ground(): RasterLayer | undefined;
33
+ /** A message to every worker of its pool */
34
+ broadcast(message: Parameters<BasemapWorkers['broadcast']>[0]): void;
35
+ /** A work asked of the worker of the vector tile `vz/vx/vy` */
36
+ request(vz: number, vx: number, vy: number, work: BasemapWork, signal?: AbortSignal): Promise<BasemapResponse>;
37
+ /** Sends a renderer of these layers to every worker (again under `again`, restyled) */
38
+ renderer(select: (layer: StyleLayer) => boolean, extra: StyleLayer[], again?: string): Promise<Renderer>;
39
+ /** The style's sprites, kept once all of them came */
40
+ loadSprites(sprite: Style['sprite']): Promise<LoadedSprite[]>;
41
+ /** The style's sprites among the map's icons (under `iconPrefix`), added once: the labels' icons, the fills'
42
+ * patterns */
43
+ addSprites(): Promise<void>;
44
+ /** Makes a layer's tiles follow the view, its `requests` aborted when it is destroyed */
45
+ followView(update: () => Promise<unknown>, requests: TileRequests, moving: () => unknown): void;
46
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * What the layers of a `VectorTiles` (its extrusions, streets' wind, trees, vectors and labels) use of it: built by
3
+ * the instance for its layers only, its own fields staying private.
4
+ */
5
+ export {};
6
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../../../src/basemap/vectortiles/context.ts"],"names":[],"mappings":"AAAA;;;GAGG","sourcesContent":["/**\n * What the layers of a `VectorTiles` (its extrusions, streets' wind, trees, vectors and labels) use of it: built by\n * the instance for its layers only, its own fields staying private.\n */\n\nimport type { LoadedSprite } from '../../layers/icons.ts';\nimport type { Layer, RasterLayer } from '../../layers/layers.ts';\nimport type { GeoMap } from '../../map.ts';\nimport type { BasemapResponse, BasemapWork } from '../../workers/basemap.worker.ts';\nimport type { Style, StyleLayer } from '../vectorstyle.ts';\nimport type { BasemapWorkers } from '../workerpool.ts';\nimport type { TileRequests } from './requests.ts';\nimport type { LoadedStyle } from './style.ts';\n\n/** A renderer sent to the workers: its key, the source's deepest level, its style layers */\nexport interface Renderer {\n key: string;\n maxzoom: number;\n layers: StyleLayer[];\n}\n\nexport interface VectorTilesContext {\n readonly map: GeoMap;\n /** The style once loaded, with its source resolved */\n readonly ready: Promise<LoadedStyle>;\n /** Its raster and label layers, removed with it: a layer made is added to them */\n readonly layers: Layer[];\n /** Called when it is destroyed: a layer adds what stops it (its listeners, its timers) */\n readonly cleanup: (() => void)[];\n /** Prefix of the names of its sprites' images among the map's icons */\n readonly iconPrefix: string;\n /** Whether it was destroyed */\n destroyed(): boolean;\n /** Its first raster layer: the ground its labels follow */\n ground(): RasterLayer | undefined;\n /** A message to every worker of its pool */\n broadcast(message: Parameters<BasemapWorkers['broadcast']>[0]): void;\n /** A work asked of the worker of the vector tile `vz/vx/vy` */\n request(vz: number, vx: number, vy: number, work: BasemapWork, signal?: AbortSignal): Promise<BasemapResponse>;\n /** Sends a renderer of these layers to every worker (again under `again`, restyled) */\n renderer(select: (layer: StyleLayer) => boolean, extra: StyleLayer[], again?: string): Promise<Renderer>;\n /** The style's sprites, kept once all of them came */\n loadSprites(sprite: Style['sprite']): Promise<LoadedSprite[]>;\n /** The style's sprites among the map's icons (under `iconPrefix`), added once: the labels' icons, the fills'\n * patterns */\n addSprites(): Promise<void>;\n /** Makes a layer's tiles follow the view, its `requests` aborted when it is destroyed */\n followView(update: () => Promise<unknown>, requests: TileRequests, moving: () => unknown): void;\n}\n"]}
@@ -0,0 +1,15 @@
1
+ /** The extrusions of a vector basemap (`VectorTiles.extrusions`): its buildings raised, tile by tile around the view */
2
+ import type { ExtrusionLayer, ExtrusionOptions } from '../../layers/extrusions.ts';
3
+ import type { RoofMode } from '../../workers/basemap/geometry.ts';
4
+ import type { StyleLayer } from '../vectorstyle.ts';
5
+ import type { VectorTilesContext } from './context.ts';
6
+ /** The options of `VectorTiles.extrusions` */
7
+ export type VectorExtrusionOptions<P> = Omit<ExtrusionOptions<P>, 'data'> & {
8
+ layers?: (layer: StyleLayer) => boolean;
9
+ minZoom?: number;
10
+ maxTiles?: number;
11
+ /** The roofs, as `vectors()` makes them: from the tiles' data (`roof:shape`, `roof:height`; flat without),
12
+ * generated where they have none, or flat (the default, unless `roof` is given) */
13
+ roofs?: RoofMode;
14
+ };
15
+ export declare const extrusions: <P = Record<string, unknown>>(context: VectorTilesContext, options?: VectorExtrusionOptions<P>) => ExtrusionLayer<P>;
@@ -0,0 +1,61 @@
1
+ /** The extrusions of a vector basemap (`VectorTiles.extrusions`): its buildings raised, tile by tile around the view */
2
+ import { isRecord } from "../../util/guards.js";
3
+ import { TileRequests } from "./requests.js";
4
+ import { isBuildings, isPolygonLayer } from "./style.js";
5
+ import { MAX_TILES, nearPriority, STREET_ZOOM, tileKey, tilesAround } from "./tiles.js";
6
+ /** The first of a feature's properties `names` it has */
7
+ const property = (p, names) => isRecord(p) ? names.map((n) => p[n]).find((v) => v !== undefined) : undefined;
8
+ /** The roof style of a `RoofMode`: a feature's `roof:shape` (or `roof_shape`), else flat or generated; its
9
+ * `roof:height` */
10
+ const roofStyle = (mode) => {
11
+ const fallback = mode === 'generated' ? 'generated' : 'flat';
12
+ return {
13
+ roof: (p) => {
14
+ const shape = property(p, ['roof:shape', 'roof_shape']);
15
+ return typeof shape === 'string' && shape !== 'generated' ? shape : fallback;
16
+ },
17
+ roofHeight: (p) => Number(property(p, ['roof:height', 'roof_height']) ?? Number.NaN),
18
+ };
19
+ };
20
+ export const extrusions = (context, options = {}) => {
21
+ const { map } = context;
22
+ const { layers = isBuildings, minZoom = STREET_ZOOM, maxTiles = MAX_TILES, roofs = 'flat', ...style } = options;
23
+ const layer = map.addExtrusions(roofs === 'flat' ? style : { ...roofStyle(roofs), ...style });
24
+ const renderer = context.renderer(isPolygonLayer(layers), []);
25
+ renderer.catch(() => undefined);
26
+ context.layers.push(layer);
27
+ /** The tiles held or asked for, by key */
28
+ const requests = new TileRequests();
29
+ const update = async () => {
30
+ if (context.destroyed())
31
+ return;
32
+ const { key, maxzoom } = await renderer;
33
+ const camera = map.getCamera();
34
+ const z = maxzoom;
35
+ const wanted = camera.zoom < minZoom ? [] : tilesAround(map, camera, z, maxTiles);
36
+ const keys = new Set(wanted.map(([x, y]) => tileKey(z, x, y)));
37
+ // Out of view: dropped, or no longer asked for
38
+ requests.keys().forEach((k) => {
39
+ if (keys.has(k))
40
+ return;
41
+ requests.drop(k);
42
+ layer.removeTile(k);
43
+ });
44
+ wanted.forEach(([x, y], index) => {
45
+ const k = tileKey(z, x, y);
46
+ if (requests.has(k))
47
+ return;
48
+ const work = { type: 'polygons', key, z, x, y, priority: nearPriority(index) };
49
+ requests.ask(k, (signal) => context.request(z, x, y, work, signal), (r) => {
50
+ if (!('polygons' in r))
51
+ return;
52
+ const tile = r.polygons;
53
+ // The features' properties as the style reads them
54
+ layer.setTile(k, { arrays: tile.arrays, properties: tile.properties });
55
+ });
56
+ });
57
+ };
58
+ context.followView(update, requests, update);
59
+ return layer;
60
+ };
61
+ //# sourceMappingURL=extrusions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extrusions.js","sourceRoot":"","sources":["../../../src/basemap/vectortiles/extrusions.ts"],"names":[],"mappings":"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","sourcesContent":["/** The extrusions of a vector basemap (`VectorTiles.extrusions`): its buildings raised, tile by tile around the view */\n\nimport type { ExtrusionLayer, ExtrusionOptions } from '../../layers/extrusions.ts';\nimport { isRecord } from '../../util/guards.ts';\nimport type { RoofMode } from '../../workers/basemap/geometry.ts';\nimport type { BasemapWork, TilePolygons } from '../../workers/basemap.worker.ts';\nimport type { StyleLayer } from '../vectorstyle.ts';\nimport type { VectorTilesContext } from './context.ts';\nimport { TileRequests } from './requests.ts';\nimport { isBuildings, isPolygonLayer } from './style.ts';\nimport { MAX_TILES, nearPriority, STREET_ZOOM, tileKey, tilesAround } from './tiles.ts';\n\n/** The options of `VectorTiles.extrusions` */\nexport type VectorExtrusionOptions<P> = Omit<ExtrusionOptions<P>, 'data'> & {\n layers?: (layer: StyleLayer) => boolean;\n minZoom?: number;\n maxTiles?: number;\n /** The roofs, as `vectors()` makes them: from the tiles' data (`roof:shape`, `roof:height`; flat without),\n * generated where they have none, or flat (the default, unless `roof` is given) */\n roofs?: RoofMode;\n};\n\n/** The first of a feature's properties `names` it has */\nconst property = (p: unknown, names: readonly string[]): unknown =>\n isRecord(p) ? names.map((n) => p[n]).find((v) => v !== undefined) : undefined;\n\n/** The roof style of a `RoofMode`: a feature's `roof:shape` (or `roof_shape`), else flat or generated; its\n * `roof:height` */\nconst roofStyle = <P>(mode: RoofMode): Pick<ExtrusionOptions<P>, 'roof' | 'roofHeight'> => {\n const fallback = mode === 'generated' ? 'generated' : 'flat';\n\n return {\n roof: (p) => {\n const shape = property(p, ['roof:shape', 'roof_shape']);\n\n return typeof shape === 'string' && shape !== 'generated' ? shape : fallback;\n },\n roofHeight: (p) => Number(property(p, ['roof:height', 'roof_height']) ?? Number.NaN),\n };\n};\n\nexport const extrusions = <P = Record<string, unknown>>(\n context: VectorTilesContext,\n options: VectorExtrusionOptions<P> = {},\n): ExtrusionLayer<P> => {\n const { map } = context;\n const { layers = isBuildings, minZoom = STREET_ZOOM, maxTiles = MAX_TILES, roofs = 'flat', ...style } = options;\n const layer = map.addExtrusions<P>(roofs === 'flat' ? style : { ...roofStyle<P>(roofs), ...style });\n const renderer = context.renderer(isPolygonLayer(layers), []);\n\n renderer.catch(() => undefined);\n context.layers.push(layer);\n\n /** The tiles held or asked for, by key */\n const requests = new TileRequests();\n const update = async () => {\n if (context.destroyed()) return;\n\n const { key, maxzoom } = await renderer;\n const camera = map.getCamera();\n const z = maxzoom;\n const wanted = camera.zoom < minZoom ? [] : tilesAround(map, camera, z, maxTiles);\n const keys = new Set(wanted.map(([x, y]) => tileKey(z, x, y)));\n\n // Out of view: dropped, or no longer asked for\n requests.keys().forEach((k) => {\n if (keys.has(k)) return;\n requests.drop(k);\n layer.removeTile(k);\n });\n wanted.forEach(([x, y], index) => {\n const k = tileKey(z, x, y);\n\n if (requests.has(k)) return;\n\n const work: BasemapWork = { type: 'polygons', key, z, x, y, priority: nearPriority(index) };\n\n requests.ask(\n k,\n (signal) => context.request(z, x, y, work, signal),\n (r) => {\n if (!('polygons' in r)) return;\n\n const tile: TilePolygons = r.polygons;\n\n // The features' properties as the style reads them\n layer.setTile(k, { arrays: tile.arrays, properties: tile.properties as P[] });\n },\n );\n });\n };\n context.followView(update, requests, update);\n\n return layer;\n};\n"]}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * The labels of a vector basemap (`VectorTiles.labels`): the label candidates the workers list for each vector tile in
3
+ * view, shaped in the page once per tile, placed by the engine in a symbol layer of their own.
4
+ */
5
+ import type { Feature, Size } from '../../layers/features.ts';
6
+ import type { SymbolLayer } from '../../layers/symbols.ts';
7
+ import type { Json, LabelCandidate, StyleLayer } from '../vectorstyle.ts';
8
+ import type { VectorTilesContext } from './context.ts';
9
+ /** Text or icon size of a style (a number, legacy stops, or the stops of a zoom expression made by the worker) as a
10
+ * size for the symbol layer */
11
+ export declare const size: (value: Json, fallback?: number) => Size;
12
+ /** The labels of a vector tile: features by style layer (its index in the style), the higher layers first */
13
+ type TileLabels = [layer: number, features: Feature<LabelCandidate>[]][];
14
+ /** A tile's label candidates as features, by style layer */
15
+ export declare const tileLabels: (candidates: readonly LabelCandidate[]) => TileLabels;
16
+ /** The options of `VectorTiles.labels` */
17
+ export interface LabelsOptions {
18
+ layers?: (layer: StyleLayer) => boolean;
19
+ maxTiles?: number;
20
+ /** Hidden by the solids before them (the buildings, the trees, the models) and lifted onto the roofs (default: when
21
+ * these tiles raise buildings; `true` for buildings raised by others, a `<VectorGeometry>` of the same style) */
22
+ occluded?: boolean;
23
+ }
24
+ export declare const labels: (context: VectorTilesContext, options?: LabelsOptions) => SymbolLayer<LabelCandidate>;
25
+ export {};
@@ -0,0 +1,250 @@
1
+ /**
2
+ * The labels of a vector basemap (`VectorTiles.labels`): the label candidates the workers list for each vector tile in
3
+ * view, shaped in the page once per tile, placed by the engine in a symbol layer of their own.
4
+ */
5
+ import { ExtrusionLayer } from "../../layers/extrusions.js";
6
+ import { VectorLayer } from "../../layers/vectors.js";
7
+ import { isRecord } from "../../util/guards.js";
8
+ import { Lru } from "../../util/lru.js";
9
+ import { styleFonts } from "./style.js";
10
+ import { MAX_TILES, nearPriority, parseTileKey, tileKey, tilesAround, tileTriples } from "./tiles.js";
11
+ /** Pause before the labels of tiles that failed are asked for again, when no move brings an update (ms) */
12
+ const RETRY_LABELS_MS = 3000;
13
+ /** Milliseconds between two updates of the labels while the camera moves */
14
+ const LABELS_MOVE_REFRESH_MS = 250;
15
+ /** Vector tiles' labels kept, shaped, the least recently used dropped past it */
16
+ const CACHED_TILES = 256;
17
+ /** Text or icon size of a style (a number, legacy stops, or the stops of a zoom expression made by the worker) as a
18
+ * size for the symbol layer */
19
+ export const size = (value, fallback = 16) => {
20
+ const stop = (s) => {
21
+ const [z, v] = Array.isArray(s) ? s : [];
22
+ return [Number(z), Number(v)];
23
+ };
24
+ if (typeof value === 'number')
25
+ return value;
26
+ if (Array.isArray(value) && value.length > 0 && value.every(Array.isArray))
27
+ return value.map(stop);
28
+ if (isRecord(value) && Array.isArray(value.stops))
29
+ return value.stops.map(stop);
30
+ return fallback;
31
+ };
32
+ /** The anchors of the symbol layer */
33
+ const ANCHORS = new Set([
34
+ 'center',
35
+ 'top',
36
+ 'bottom',
37
+ 'left',
38
+ 'right',
39
+ 'top-left',
40
+ 'top-right',
41
+ 'bottom-left',
42
+ 'bottom-right',
43
+ ]);
44
+ const isAnchor = (value) => value !== undefined && ANCHORS.has(value);
45
+ /** A style's anchor (`text-anchor`, `icon-anchor`) for the symbol layer: `center` if it is none */
46
+ const anchor = (value) => (isAnchor(value) ? value : 'center');
47
+ /** Where a candidate is labelled: the line around a text that follows it, a short line along a line's direction (its
48
+ * middle the anchor), or its point */
49
+ const geometryOf = (p) => {
50
+ const { path, along } = p;
51
+ if (path) {
52
+ return {
53
+ type: 'LineString',
54
+ coordinates: Array.from({ length: path.length / 2 }, (_, i) => [path[i * 2] ?? 0, path[i * 2 + 1] ?? 0]),
55
+ };
56
+ }
57
+ if (along) {
58
+ return {
59
+ type: 'LineString',
60
+ coordinates: [
61
+ [p.lon - along[0] / 2, p.lat - along[1] / 2],
62
+ [p.lon + along[0] / 2, p.lat + along[1] / 2],
63
+ ],
64
+ };
65
+ }
66
+ return { type: 'Point', coordinates: [p.lon, p.lat] };
67
+ };
68
+ /** A tile's label candidates as features, by style layer */
69
+ export const tileLabels = (candidates) => {
70
+ const byLayer = new Map();
71
+ candidates.forEach((p) => {
72
+ const feature = {
73
+ type: 'Feature',
74
+ properties: p,
75
+ geometry: geometryOf(p),
76
+ };
77
+ const list = byLayer.get(p.layer);
78
+ if (list)
79
+ list.push(feature);
80
+ else
81
+ byLayer.set(p.layer, [feature]);
82
+ });
83
+ return [...byLayer].sort((a, b) => b[0] - a[0]);
84
+ };
85
+ /** The key of a vector tile's labels: `z/x/y`, then `@zoom` when they are those of a deeper zoom than the tile's */
86
+ const labelsKey = (z, x, y, zoom) => `${tileKey(z, x, y)}${zoom > z ? `@${zoom}` : ''}`;
87
+ /** Whether a layer of the basemap raises its buildings: its extrusions, or a style layer `fill-extrusion` of its
88
+ * vectors shown */
89
+ const raisesBuildings = (layer) => layer instanceof ExtrusionLayer ||
90
+ (layer instanceof VectorLayer && layer.layers.some((l) => l.kind === 'extrusion' && l.visible));
91
+ export const labels = (context, options = {}) => {
92
+ const { map } = context;
93
+ const { layers = () => true, maxTiles = MAX_TILES } = options;
94
+ const symbol = map.addSymbols({
95
+ data: [],
96
+ text: (p) => p.text,
97
+ font: (p) => p.font,
98
+ size: (p) => size(p.size),
99
+ color: (p) => p.color,
100
+ haloColor: (p) => p.haloColor,
101
+ haloWidth: (p) => p.haloWidth,
102
+ anchor: (p) => anchor(p.anchor),
103
+ offset: (p) => p.offset,
104
+ maxWidth: (p) => p.maxWidth,
105
+ lineHeight: (p) => p.lineHeight,
106
+ overlap: (p) => p.overlap,
107
+ placement: (p) => (p.textAlong ? 'line' : 'point'),
108
+ textOptional: (p) => p.textOptional === true,
109
+ icon: (p) => (p.icon ? context.iconPrefix + p.icon.image : ''),
110
+ iconSize: (p) => size(p.icon?.size ?? 1, 1),
111
+ iconAnchor: (p) => anchor(p.icon?.anchor),
112
+ iconOffset: (p) => p.icon?.offset ?? [0, 0],
113
+ iconRotate: (p) => p.icon?.rotate ?? 0,
114
+ iconRotationAlignment: (p) => p.icon?.alignment ?? 'viewport',
115
+ iconOverlap: (p) => p.icon?.overlap ?? false,
116
+ iconOptional: (p) => p.icon?.optional ?? false,
117
+ iconTextFit: (p) => p.icon?.fit ?? 'none',
118
+ iconTextFitPadding: (p) => p.icon?.padding ?? [0, 0, 0, 0],
119
+ iconColor: (p) => p.icon?.color ?? '#000',
120
+ iconOpacity: (p) => p.icon?.opacity ?? 1,
121
+ });
122
+ map.keepOnTop(symbol);
123
+ context.layers.push(symbol);
124
+ const renderer = context.ready.then(async ({ style }) => {
125
+ // The style's fonts from its own glyphs
126
+ if (style.glyphs)
127
+ map.glyphs.register(style.glyphs, styleFonts(style));
128
+ // Its icons from its sprites, before the first labels
129
+ const icons = style.layers.some((l) => l.type === 'symbol' && l.layout?.['icon-image'] !== undefined);
130
+ if (icons)
131
+ await context.addSprites();
132
+ // With the buildings: a label in one stands on its roof
133
+ return context.renderer((l) => (l.type === 'symbol' && layers(l)) || l.type === 'fill-extrusion', []);
134
+ });
135
+ renderer.catch(() => undefined);
136
+ /** The labels of the tiles, as features by style layer, the higher layers first: each list is given to the symbol
137
+ * layer as a group, shaped once while the tile is kept */
138
+ const tiles = new Lru(CACHED_TILES);
139
+ let generation = 0;
140
+ /** The tiles whose labels are shown */
141
+ let shown = '';
142
+ /** The update asked again after tiles failed */
143
+ let retry = 0;
144
+ /** The labels hidden by the buildings the basemap raises, those in one standing on its roof */
145
+ let occluded = false;
146
+ const update = async () => {
147
+ if (context.destroyed())
148
+ return;
149
+ const { key, maxzoom } = await renderer;
150
+ const others = context.layers.filter((l) => l !== symbol);
151
+ // Its vectors' style layers known first
152
+ await Promise.all(others.map((l) => (l instanceof VectorLayer ? l.styled : undefined)));
153
+ const raised = options.occluded ?? others.some(raisesBuildings);
154
+ if (raised !== occluded) {
155
+ occluded = raised;
156
+ symbol.setStyle({ occluded, height: occluded ? (p) => p.height ?? 0 : 0 });
157
+ }
158
+ const run = ++generation;
159
+ const camera = map.getCamera();
160
+ const zoom = Math.max(0, Math.min(Math.floor(camera.zoom), 24));
161
+ const ground = context.ground();
162
+ const groundTiles = ground && map.hasLayer(ground) ? tileTriples(map.engine.wantedTiles(ground.id)) : [];
163
+ const keys = [];
164
+ if (groundTiles.length > 0) {
165
+ // The ground's tiles, the nearest first: a tile so many levels coarser than the finest in view has its
166
+ // labels of a zoom as many under the view's, past the source's deepest level those of that level at the zoom
167
+ const top = groundTiles.reduce((t, [z]) => Math.max(t, z), 0);
168
+ groundTiles.forEach(([z, x, y]) => {
169
+ if (keys.length >= maxTiles)
170
+ return;
171
+ const at = Math.max(0, zoom - (top - z));
172
+ const tz = Math.min(at, maxzoom, z);
173
+ const shift = 2 ** (z - tz);
174
+ const key = labelsKey(tz, Math.floor(x / shift), Math.floor(y / shift), at);
175
+ if (!keys.includes(key))
176
+ keys.push(key);
177
+ });
178
+ }
179
+ else {
180
+ const z = Math.min(zoom, maxzoom);
181
+ const wanted = tilesAround(map, camera, z, maxTiles);
182
+ // Past the source's deepest level, its tiles' labels at the zoom of the view (the layers of the deeper zooms)
183
+ keys.push(...wanted.map(([x, y]) => labelsKey(z, x, y, zoom)));
184
+ }
185
+ // The same tiles as the labels shown: nothing to do
186
+ const set = keys.join(' ');
187
+ if (set === shown)
188
+ return;
189
+ const lists = await Promise.all(keys.map((k, index) => {
190
+ const cached = tiles.get(k);
191
+ if (cached)
192
+ return cached;
193
+ const [tile = '', deeper] = k.split('@');
194
+ const [tz, x, y] = parseTileKey(tile);
195
+ // The nearest tiles first: the others wait for the raster tiles
196
+ const work = {
197
+ type: 'labels',
198
+ key,
199
+ z: tz,
200
+ x,
201
+ y,
202
+ priority: nearPriority(index),
203
+ ...(deeper === undefined ? {} : { zoom: Number(deeper) }),
204
+ };
205
+ const p = context.request(tz, x, y, work).then((r) => tileLabels('labels' in r ? r.labels : []), () => {
206
+ // A network error is not kept: the tile is asked for again on the next update
207
+ tiles.delete(k);
208
+ return [];
209
+ });
210
+ // A bounded cache of tiles' labels, the least recently used dropped
211
+ tiles.set(k, p);
212
+ return p;
213
+ }));
214
+ if (run !== generation || context.destroyed())
215
+ return;
216
+ // A tile that failed is missing: the next update asks for it again, after a pause if no move brings one
217
+ shown = keys.every((k) => tiles.has(k)) ? set : '';
218
+ if (shown === '' && !retry) {
219
+ retry = window.setTimeout(() => {
220
+ retry = 0;
221
+ refresh();
222
+ }, RETRY_LABELS_MS);
223
+ }
224
+ // The labels of the higher style layers first: they win the collisions; for each style layer, the tiles nearest
225
+ // to the center first
226
+ const layerIds = [...new Set(lists.flatMap((list) => list.map(([layer]) => layer)))].sort((a, b) => b - a);
227
+ const groups = layerIds.flatMap((layer) => lists.map((list) => list.find(([l]) => l === layer)?.[1]).filter((group) => group !== undefined));
228
+ symbol.setGroups(groups);
229
+ };
230
+ // The style failed to load: reported by `ready`
231
+ const refresh = () => {
232
+ update().catch(() => undefined);
233
+ };
234
+ let timer = 0;
235
+ const schedule = () => {
236
+ if (timer)
237
+ return;
238
+ timer = window.setTimeout(() => {
239
+ timer = 0;
240
+ refresh();
241
+ }, LABELS_MOVE_REFRESH_MS);
242
+ };
243
+ context.cleanup.push(map.on('move', schedule), map.on('moveend', refresh), () => {
244
+ clearTimeout(timer);
245
+ clearTimeout(retry);
246
+ });
247
+ refresh();
248
+ return symbol;
249
+ };
250
+ //# sourceMappingURL=labels.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"labels.js","sourceRoot":"","sources":["../../../src/basemap/vectortiles/labels.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The labels of a vector basemap (`VectorTiles.labels`): the label candidates the workers list for each vector tile in\n * view, shaped in the page once per tile, placed by the engine in a symbol layer of their own.\n */\n\nimport { ExtrusionLayer } from '../../layers/extrusions.ts';\nimport type { Feature, Size } from '../../layers/features.ts';\nimport type { Layer } from '../../layers/layers.ts';\nimport type { SymbolLayer } from '../../layers/symbols.ts';\nimport { VectorLayer } from '../../layers/vectors.ts';\nimport type { Anchor } from '../../ui/overlay.ts';\nimport { isRecord } from '../../util/guards.ts';\nimport { Lru } from '../../util/lru.ts';\nimport type { BasemapWork } from '../../workers/basemap.worker.ts';\nimport type { Json, LabelCandidate, StyleLayer } from '../vectorstyle.ts';\nimport type { VectorTilesContext } from './context.ts';\nimport { styleFonts } from './style.ts';\nimport { MAX_TILES, nearPriority, parseTileKey, tileKey, tilesAround, tileTriples } from './tiles.ts';\n\n/** Pause before the labels of tiles that failed are asked for again, when no move brings an update (ms) */\nconst RETRY_LABELS_MS = 3000;\n\n/** Milliseconds between two updates of the labels while the camera moves */\nconst LABELS_MOVE_REFRESH_MS = 250;\n\n/** Vector tiles' labels kept, shaped, the least recently used dropped past it */\nconst CACHED_TILES = 256;\n\n/** Text or icon size of a style (a number, legacy stops, or the stops of a zoom expression made by the worker) as a\n * size for the symbol layer */\nexport const size = (value: Json, fallback = 16): Size => {\n const stop = (s: Json): [number, number] => {\n const [z, v] = Array.isArray(s) ? s : [];\n\n return [Number(z), Number(v)];\n };\n\n if (typeof value === 'number') return value;\n if (Array.isArray(value) && value.length > 0 && value.every(Array.isArray)) return value.map(stop);\n if (isRecord(value) && Array.isArray(value.stops)) return value.stops.map(stop);\n\n return fallback;\n};\n\n/** The anchors of the symbol layer */\nconst ANCHORS: ReadonlySet<string> = new Set<Anchor>([\n 'center',\n 'top',\n 'bottom',\n 'left',\n 'right',\n 'top-left',\n 'top-right',\n 'bottom-left',\n 'bottom-right',\n]);\n\nconst isAnchor = (value: string | undefined): value is Anchor => value !== undefined && ANCHORS.has(value);\n\n/** A style's anchor (`text-anchor`, `icon-anchor`) for the symbol layer: `center` if it is none */\nconst anchor = (value: string | undefined): Anchor => (isAnchor(value) ? value : 'center');\n\n/** The labels of a vector tile: features by style layer (its index in the style), the higher layers first */\ntype TileLabels = [layer: number, features: Feature<LabelCandidate>[]][];\n\n/** Where a candidate is labelled: the line around a text that follows it, a short line along a line's direction (its\n * middle the anchor), or its point */\nconst geometryOf = (p: LabelCandidate): Feature<LabelCandidate>['geometry'] => {\n const { path, along } = p;\n\n if (path) {\n return {\n type: 'LineString',\n coordinates: Array.from({ length: path.length / 2 }, (_, i) => [path[i * 2] ?? 0, path[i * 2 + 1] ?? 0]),\n };\n }\n if (along) {\n return {\n type: 'LineString',\n coordinates: [\n [p.lon - along[0] / 2, p.lat - along[1] / 2],\n [p.lon + along[0] / 2, p.lat + along[1] / 2],\n ],\n };\n }\n\n return { type: 'Point', coordinates: [p.lon, p.lat] };\n};\n\n/** A tile's label candidates as features, by style layer */\nexport const tileLabels = (candidates: readonly LabelCandidate[]): TileLabels => {\n const byLayer = new Map<number, Feature<LabelCandidate>[]>();\n\n candidates.forEach((p) => {\n const feature: Feature<LabelCandidate> = {\n type: 'Feature',\n properties: p,\n geometry: geometryOf(p),\n };\n const list = byLayer.get(p.layer);\n\n if (list) list.push(feature);\n else byLayer.set(p.layer, [feature]);\n });\n\n return [...byLayer].sort((a, b) => b[0] - a[0]);\n};\n\n/** The key of a vector tile's labels: `z/x/y`, then `@zoom` when they are those of a deeper zoom than the tile's */\nconst labelsKey = (z: number, x: number, y: number, zoom: number): string =>\n `${tileKey(z, x, y)}${zoom > z ? `@${zoom}` : ''}`;\n\n/** Whether a layer of the basemap raises its buildings: its extrusions, or a style layer `fill-extrusion` of its\n * vectors shown */\nconst raisesBuildings = (layer: Layer): boolean =>\n layer instanceof ExtrusionLayer ||\n (layer instanceof VectorLayer && layer.layers.some((l) => l.kind === 'extrusion' && l.visible));\n\n/** The options of `VectorTiles.labels` */\nexport interface LabelsOptions {\n layers?: (layer: StyleLayer) => boolean;\n maxTiles?: number;\n /** Hidden by the solids before them (the buildings, the trees, the models) and lifted onto the roofs (default: when\n * these tiles raise buildings; `true` for buildings raised by others, a `<VectorGeometry>` of the same style) */\n occluded?: boolean;\n}\n\nexport const labels = (context: VectorTilesContext, options: LabelsOptions = {}): SymbolLayer<LabelCandidate> => {\n const { map } = context;\n const { layers = () => true, maxTiles = MAX_TILES } = options;\n const symbol = map.addSymbols<LabelCandidate>({\n data: [],\n text: (p) => p.text,\n font: (p) => p.font,\n size: (p) => size(p.size),\n color: (p) => p.color,\n haloColor: (p) => p.haloColor,\n haloWidth: (p) => p.haloWidth,\n anchor: (p) => anchor(p.anchor),\n offset: (p) => p.offset,\n maxWidth: (p) => p.maxWidth,\n lineHeight: (p) => p.lineHeight,\n overlap: (p) => p.overlap,\n placement: (p) => (p.textAlong ? 'line' : 'point'),\n textOptional: (p) => p.textOptional === true,\n icon: (p) => (p.icon ? context.iconPrefix + p.icon.image : ''),\n iconSize: (p) => size(p.icon?.size ?? 1, 1),\n iconAnchor: (p) => anchor(p.icon?.anchor),\n iconOffset: (p) => p.icon?.offset ?? [0, 0],\n iconRotate: (p) => p.icon?.rotate ?? 0,\n iconRotationAlignment: (p) => p.icon?.alignment ?? 'viewport',\n iconOverlap: (p) => p.icon?.overlap ?? false,\n iconOptional: (p) => p.icon?.optional ?? false,\n iconTextFit: (p) => p.icon?.fit ?? 'none',\n iconTextFitPadding: (p) => p.icon?.padding ?? [0, 0, 0, 0],\n iconColor: (p) => p.icon?.color ?? '#000',\n iconOpacity: (p) => p.icon?.opacity ?? 1,\n });\n\n map.keepOnTop(symbol);\n context.layers.push(symbol);\n\n const renderer = context.ready.then(async ({ style }) => {\n // The style's fonts from its own glyphs\n if (style.glyphs) map.glyphs.register(style.glyphs, styleFonts(style));\n\n // Its icons from its sprites, before the first labels\n const icons = style.layers.some((l) => l.type === 'symbol' && l.layout?.['icon-image'] !== undefined);\n\n if (icons) await context.addSprites();\n\n // With the buildings: a label in one stands on its roof\n return context.renderer((l) => (l.type === 'symbol' && layers(l)) || l.type === 'fill-extrusion', []);\n });\n\n renderer.catch(() => undefined);\n\n /** The labels of the tiles, as features by style layer, the higher layers first: each list is given to the symbol\n * layer as a group, shaped once while the tile is kept */\n const tiles = new Lru<string, Promise<TileLabels>>(CACHED_TILES);\n let generation = 0;\n /** The tiles whose labels are shown */\n let shown = '';\n /** The update asked again after tiles failed */\n let retry = 0;\n /** The labels hidden by the buildings the basemap raises, those in one standing on its roof */\n let occluded = false;\n const update = async () => {\n if (context.destroyed()) return;\n\n const { key, maxzoom } = await renderer;\n const others = context.layers.filter((l) => l !== symbol);\n\n // Its vectors' style layers known first\n await Promise.all(others.map((l) => (l instanceof VectorLayer ? l.styled : undefined)));\n\n const raised = options.occluded ?? others.some(raisesBuildings);\n\n if (raised !== occluded) {\n occluded = raised;\n symbol.setStyle({ occluded, height: occluded ? (p) => p.height ?? 0 : 0 });\n }\n\n const run = ++generation;\n const camera = map.getCamera();\n const zoom = Math.max(0, Math.min(Math.floor(camera.zoom), 24));\n const ground = context.ground();\n const groundTiles = ground && map.hasLayer(ground) ? tileTriples(map.engine.wantedTiles(ground.id)) : [];\n const keys: string[] = [];\n\n if (groundTiles.length > 0) {\n // The ground's tiles, the nearest first: a tile so many levels coarser than the finest in view has its\n // labels of a zoom as many under the view's, past the source's deepest level those of that level at the zoom\n const top = groundTiles.reduce((t, [z]) => Math.max(t, z), 0);\n\n groundTiles.forEach(([z, x, y]) => {\n if (keys.length >= maxTiles) return;\n\n const at = Math.max(0, zoom - (top - z));\n const tz = Math.min(at, maxzoom, z);\n const shift = 2 ** (z - tz);\n const key = labelsKey(tz, Math.floor(x / shift), Math.floor(y / shift), at);\n\n if (!keys.includes(key)) keys.push(key);\n });\n } else {\n const z = Math.min(zoom, maxzoom);\n const wanted = tilesAround(map, camera, z, maxTiles);\n\n // Past the source's deepest level, its tiles' labels at the zoom of the view (the layers of the deeper zooms)\n keys.push(...wanted.map(([x, y]) => labelsKey(z, x, y, zoom)));\n }\n\n // The same tiles as the labels shown: nothing to do\n const set = keys.join(' ');\n\n if (set === shown) return;\n\n const lists = await Promise.all(\n keys.map((k, index) => {\n const cached = tiles.get(k);\n\n if (cached) return cached;\n\n const [tile = '', deeper] = k.split('@');\n const [tz, x, y] = parseTileKey(tile);\n // The nearest tiles first: the others wait for the raster tiles\n const work: BasemapWork = {\n type: 'labels',\n key,\n z: tz,\n x,\n y,\n priority: nearPriority(index),\n ...(deeper === undefined ? {} : { zoom: Number(deeper) }),\n };\n const p = context.request(tz, x, y, work).then(\n (r) => tileLabels('labels' in r ? r.labels : []),\n () => {\n // A network error is not kept: the tile is asked for again on the next update\n tiles.delete(k);\n\n return [];\n },\n );\n\n // A bounded cache of tiles' labels, the least recently used dropped\n tiles.set(k, p);\n\n return p;\n }),\n );\n\n if (run !== generation || context.destroyed()) return;\n // A tile that failed is missing: the next update asks for it again, after a pause if no move brings one\n shown = keys.every((k) => tiles.has(k)) ? set : '';\n if (shown === '' && !retry) {\n retry = window.setTimeout(() => {\n retry = 0;\n refresh();\n }, RETRY_LABELS_MS);\n }\n\n // The labels of the higher style layers first: they win the collisions; for each style layer, the tiles nearest\n // to the center first\n const layerIds = [...new Set(lists.flatMap((list) => list.map(([layer]) => layer)))].sort((a, b) => b - a);\n const groups = layerIds.flatMap((layer) =>\n lists.map((list) => list.find(([l]) => l === layer)?.[1]).filter((group) => group !== undefined),\n );\n\n symbol.setGroups(groups);\n };\n // The style failed to load: reported by `ready`\n const refresh = () => {\n update().catch(() => undefined);\n };\n let timer = 0;\n const schedule = () => {\n if (timer) return;\n timer = window.setTimeout(() => {\n timer = 0;\n refresh();\n }, LABELS_MOVE_REFRESH_MS);\n };\n\n context.cleanup.push(map.on('move', schedule), map.on('moveend', refresh), () => {\n clearTimeout(timer);\n clearTimeout(retry);\n });\n refresh();\n\n return symbol;\n};\n"]}
@@ -0,0 +1,23 @@
1
+ /** The street lamps of a vector basemap (`VectorTiles.lamps`): 3D models placed by the workers along its streets, lit at
2
+ * night */
3
+ import type { Layer, ModelLayer } from '../../layers/layers.ts';
4
+ import type { StyleLayer } from '../vectorstyle.ts';
5
+ import type { VectorTilesContext } from './context.ts';
6
+ /** The options of `VectorTiles.lamps` */
7
+ export interface LampsOptions {
8
+ /** The line layers of the streets lit (default: the roads, as the streets glowing at night) */
9
+ layers?: (layer: StyleLayer) => boolean;
10
+ /** Metres between two lamps along a street (default 30, every other one on its other side) */
11
+ spacing?: number;
12
+ /** From this zoom on (default 16) */
13
+ minZoom?: number;
14
+ maxTiles?: number;
15
+ /** Lamps in a tile at most (default 20000) */
16
+ maxLamps?: number;
17
+ /** Lamps drawn at most (default 15000) */
18
+ maxInstances?: number;
19
+ /** The color of their light at night (CSS; default a warm white) */
20
+ light?: string;
21
+ before?: Layer;
22
+ }
23
+ export declare const lamps: (context: VectorTilesContext, options?: LampsOptions) => ModelLayer;
@@ -0,0 +1,90 @@
1
+ /** The street lamps of a vector basemap (`VectorTiles.lamps`): 3D models placed by the workers along its streets, lit at
2
+ * night */
3
+ import { LAMP_HEAD, lampModel } from "../furniture.js";
4
+ import { TREE_NUMBERS } from "../trees.js";
5
+ import { TileRequests } from "./requests.js";
6
+ import { nearPriority, tileKey, tilesAround } from "./tiles.js";
7
+ import { followWhenMoved, groundUnderCamera, METRES_PER_DEGREE } from "./trees.js";
8
+ import { isStreet } from "./vectors.js";
9
+ /** Metres around the ground under the camera where every lamp is drawn at zoom 17 (twice as far a zoom level out);
10
+ * past them, fewer and fewer */
11
+ const LAMPS_NEAR_METRES = 250;
12
+ export const lamps = (context, options = {}) => {
13
+ const { map } = context;
14
+ const { layers = isStreet, spacing = 30, minZoom = 16, maxTiles = 9, maxLamps = 20000, maxInstances = 15000, light = '#ffd9a0', } = options;
15
+ const layer = map.addModels({
16
+ data: lampModel(),
17
+ instances: [],
18
+ ...(options.before ? { before: options.before } : {}),
19
+ });
20
+ const renderer = context.renderer((l) => l.type === 'line' && layers(l), []);
21
+ layer.setGlow(LAMP_HEAD, light);
22
+ renderer.catch(() => undefined);
23
+ context.layers.push(layer);
24
+ const requests = new TileRequests();
25
+ const found = new Map();
26
+ /** The lamps drawn: all of them near the ground under the camera, past it those of a rank under (near / distance)²,
27
+ * at most `maxInstances` (past it, the rank kept lowered in proportion: no sort) */
28
+ const send = () => {
29
+ const { center, zoom } = map.getCamera();
30
+ const cos = Math.cos((center[1] * Math.PI) / 180);
31
+ const [ex, ey, back] = groundUnderCamera(map);
32
+ const near = LAMPS_NEAR_METRES * 2 ** (17 - zoom) + back;
33
+ const each = (keep) => {
34
+ found.forEach((t) => {
35
+ for (let i = 0; i < t.length / TREE_NUMBERS; i += 1) {
36
+ const dx = ((t[i * TREE_NUMBERS] ?? 0) - center[0]) * METRES_PER_DEGREE * cos - ex;
37
+ const dy = ((t[i * TREE_NUMBERS + 1] ?? 0) - center[1]) * METRES_PER_DEGREE - ey;
38
+ const score = (t[i * TREE_NUMBERS + 4] ?? 0) * Math.max(1, (dx * dx + dy * dy) / (near * near));
39
+ if (score < 1)
40
+ keep(t, i, score);
41
+ }
42
+ });
43
+ };
44
+ let count = 0;
45
+ each(() => {
46
+ count += 1;
47
+ });
48
+ const limit = Math.min(1, maxInstances / Math.max(count, 1));
49
+ const out = [];
50
+ each((t, i, score) => {
51
+ const at = i * TREE_NUMBERS;
52
+ if (score < limit)
53
+ out.push(t[at] ?? 0, t[at + 1] ?? 0, 0, t[at + 3] ?? 0, 0, 0, t[at + 2] ?? 1);
54
+ });
55
+ layer.setInstanceNumbers(Float64Array.from(out));
56
+ };
57
+ const update = async () => {
58
+ if (context.destroyed())
59
+ return;
60
+ const { key, maxzoom } = await renderer;
61
+ const camera = map.getCamera();
62
+ const z = maxzoom;
63
+ const wanted = camera.zoom < minZoom ? [] : tilesAround(map, camera, z, maxTiles);
64
+ const keys = new Set(wanted.map(([x, y]) => tileKey(z, x, y)));
65
+ // Out of view: dropped, or no longer asked for
66
+ requests
67
+ .keys()
68
+ .filter((k) => !keys.has(k))
69
+ .forEach((k) => {
70
+ requests.drop(k);
71
+ found.delete(k);
72
+ });
73
+ send();
74
+ wanted.forEach(([x, y], index) => {
75
+ const k = tileKey(z, x, y);
76
+ if (requests.has(k))
77
+ return;
78
+ const work = { type: 'lamps', key, z, x, y, spacing, maxLamps, priority: nearPriority(index) };
79
+ requests.ask(k, (signal) => context.request(z, x, y, work, signal), (r) => {
80
+ if (!('lamps' in r))
81
+ return;
82
+ found.set(k, r.lamps);
83
+ send();
84
+ });
85
+ });
86
+ };
87
+ context.followView(update, requests, followWhenMoved(map, (zoom) => LAMPS_NEAR_METRES * 2 ** (17 - zoom), send));
88
+ return layer;
89
+ };
90
+ //# sourceMappingURL=lamps.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lamps.js","sourceRoot":"","sources":["../../../src/basemap/vectortiles/lamps.ts"],"names":[],"mappings":"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","sourcesContent":["/** The street lamps of a vector basemap (`VectorTiles.lamps`): 3D models placed by the workers along its streets, lit at\n * night */\n\nimport type { Layer, ModelLayer } from '../../layers/layers.ts';\nimport type { BasemapWork } from '../../workers/basemap.worker.ts';\nimport { LAMP_HEAD, lampModel } from '../furniture.ts';\nimport { TREE_NUMBERS } from '../trees.ts';\nimport type { StyleLayer } from '../vectorstyle.ts';\nimport type { VectorTilesContext } from './context.ts';\nimport { TileRequests } from './requests.ts';\nimport { nearPriority, tileKey, tilesAround } from './tiles.ts';\nimport { followWhenMoved, groundUnderCamera, METRES_PER_DEGREE } from './trees.ts';\nimport { isStreet } from './vectors.ts';\n\n/** Metres around the ground under the camera where every lamp is drawn at zoom 17 (twice as far a zoom level out);\n * past them, fewer and fewer */\nconst LAMPS_NEAR_METRES = 250;\n\n/** The options of `VectorTiles.lamps` */\nexport interface LampsOptions {\n /** The line layers of the streets lit (default: the roads, as the streets glowing at night) */\n layers?: (layer: StyleLayer) => boolean;\n /** Metres between two lamps along a street (default 30, every other one on its other side) */\n spacing?: number;\n /** From this zoom on (default 16) */\n minZoom?: number;\n maxTiles?: number;\n /** Lamps in a tile at most (default 20000) */\n maxLamps?: number;\n /** Lamps drawn at most (default 15000) */\n maxInstances?: number;\n /** The color of their light at night (CSS; default a warm white) */\n light?: string;\n before?: Layer;\n}\n\nexport const lamps = (context: VectorTilesContext, options: LampsOptions = {}): ModelLayer => {\n const { map } = context;\n const {\n layers = isStreet,\n spacing = 30,\n minZoom = 16,\n maxTiles = 9,\n maxLamps = 20000,\n maxInstances = 15000,\n light = '#ffd9a0',\n } = options;\n const layer = map.addModels({\n data: lampModel(),\n instances: [],\n ...(options.before ? { before: options.before } : {}),\n });\n const renderer = context.renderer((l) => l.type === 'line' && layers(l), []);\n\n layer.setGlow(LAMP_HEAD, light);\n renderer.catch(() => undefined);\n context.layers.push(layer);\n\n const requests = new TileRequests();\n const found = new Map<string, Float64Array>();\n /** The lamps drawn: all of them near the ground under the camera, past it those of a rank under (near / distance)²,\n * at most `maxInstances` (past it, the rank kept lowered in proportion: no sort) */\n const send = () => {\n const { center, zoom } = map.getCamera();\n const cos = Math.cos((center[1] * Math.PI) / 180);\n const [ex, ey, back] = groundUnderCamera(map);\n const near = LAMPS_NEAR_METRES * 2 ** (17 - zoom) + back;\n const each = (keep: (t: Float64Array, i: number, score: number) => void) => {\n found.forEach((t) => {\n for (let i = 0; i < t.length / TREE_NUMBERS; i += 1) {\n const dx = ((t[i * TREE_NUMBERS] ?? 0) - center[0]) * METRES_PER_DEGREE * cos - ex;\n const dy = ((t[i * TREE_NUMBERS + 1] ?? 0) - center[1]) * METRES_PER_DEGREE - ey;\n const score = (t[i * TREE_NUMBERS + 4] ?? 0) * Math.max(1, (dx * dx + dy * dy) / (near * near));\n\n if (score < 1) keep(t, i, score);\n }\n });\n };\n let count = 0;\n\n each(() => {\n count += 1;\n });\n\n const limit = Math.min(1, maxInstances / Math.max(count, 1));\n const out: number[] = [];\n\n each((t, i, score) => {\n const at = i * TREE_NUMBERS;\n\n if (score < limit) out.push(t[at] ?? 0, t[at + 1] ?? 0, 0, t[at + 3] ?? 0, 0, 0, t[at + 2] ?? 1);\n });\n layer.setInstanceNumbers(Float64Array.from(out));\n };\n const update = async () => {\n if (context.destroyed()) return;\n\n const { key, maxzoom } = await renderer;\n const camera = map.getCamera();\n const z = maxzoom;\n const wanted = camera.zoom < minZoom ? [] : tilesAround(map, camera, z, maxTiles);\n const keys = new Set(wanted.map(([x, y]) => tileKey(z, x, y)));\n\n // Out of view: dropped, or no longer asked for\n requests\n .keys()\n .filter((k) => !keys.has(k))\n .forEach((k) => {\n requests.drop(k);\n found.delete(k);\n });\n send();\n wanted.forEach(([x, y], index) => {\n const k = tileKey(z, x, y);\n\n if (requests.has(k)) return;\n\n const work: BasemapWork = { type: 'lamps', key, z, x, y, spacing, maxLamps, priority: nearPriority(index) };\n\n requests.ask(\n k,\n (signal) => context.request(z, x, y, work, signal),\n (r) => {\n if (!('lamps' in r)) return;\n found.set(k, r.lamps);\n send();\n },\n );\n });\n };\n\n context.followView(\n update,\n requests,\n followWhenMoved(map, (zoom) => LAMPS_NEAR_METRES * 2 ** (17 - zoom), send),\n );\n\n return layer;\n};\n"]}