isard 1.6.0 → 1.7.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 (123) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/dist/basemap/furniture.d.ts +20 -2
  3. package/dist/basemap/furniture.js +72 -3
  4. package/dist/basemap/furniture.js.map +1 -1
  5. package/dist/basemap/meshes.d.ts +21 -0
  6. package/dist/basemap/meshes.js +159 -0
  7. package/dist/basemap/meshes.js.map +1 -0
  8. package/dist/basemap/placed.d.ts +13 -0
  9. package/dist/basemap/placed.js +14 -0
  10. package/dist/basemap/placed.js.map +1 -0
  11. package/dist/basemap/trees.d.ts +4 -14
  12. package/dist/basemap/trees.js +12 -93
  13. package/dist/basemap/trees.js.map +1 -1
  14. package/dist/basemap/vectortiles/context.d.ts +4 -2
  15. package/dist/basemap/vectortiles/context.js.map +1 -1
  16. package/dist/basemap/vectortiles/extrusions.d.ts +1 -1
  17. package/dist/basemap/vectortiles/extrusions.js +7 -16
  18. package/dist/basemap/vectortiles/extrusions.js.map +1 -1
  19. package/dist/basemap/vectortiles/furniture.d.ts +19 -0
  20. package/dist/basemap/vectortiles/furniture.js +29 -0
  21. package/dist/basemap/vectortiles/furniture.js.map +1 -0
  22. package/dist/basemap/vectortiles/labels.js +8 -1
  23. package/dist/basemap/vectortiles/labels.js.map +1 -1
  24. package/dist/basemap/vectortiles/lamps.js +11 -76
  25. package/dist/basemap/vectortiles/lamps.js.map +1 -1
  26. package/dist/basemap/vectortiles/placed.d.ts +38 -0
  27. package/dist/basemap/vectortiles/placed.js +143 -0
  28. package/dist/basemap/vectortiles/placed.js.map +1 -0
  29. package/dist/basemap/vectortiles/street-wind.js +4 -6
  30. package/dist/basemap/vectortiles/street-wind.js.map +1 -1
  31. package/dist/basemap/vectortiles/trees.d.ts +2 -9
  32. package/dist/basemap/vectortiles/trees.js +34 -165
  33. package/dist/basemap/vectortiles/trees.js.map +1 -1
  34. package/dist/basemap/vectortiles/vectors.d.ts +4 -5
  35. package/dist/basemap/vectortiles/vectors.js +17 -16
  36. package/dist/basemap/vectortiles/vectors.js.map +1 -1
  37. package/dist/basemap/vectortiles.d.ts +14 -4
  38. package/dist/basemap/vectortiles.js +40 -12
  39. package/dist/basemap/vectortiles.js.map +1 -1
  40. package/dist/basemap/wmts.js +2 -2
  41. package/dist/basemap/wmts.js.map +1 -1
  42. package/dist/basemap.worker.js +2 -2
  43. package/dist/basemap.worker.js.map +18 -14
  44. package/dist/data/collada.js +3 -17
  45. package/dist/data/collada.js.map +1 -1
  46. package/dist/data/crs.js +1 -1
  47. package/dist/data/crs.js.map +1 -1
  48. package/dist/data/ifc.js +21 -22
  49. package/dist/data/ifc.js.map +1 -1
  50. package/dist/geoplateforme.d.ts +14 -2
  51. package/dist/geoplateforme.js +24 -2
  52. package/dist/geoplateforme.js.map +1 -1
  53. package/dist/index.d.ts +5 -4
  54. package/dist/index.js +3 -3
  55. package/dist/index.js.map +1 -1
  56. package/dist/isard_bg.wasm +0 -0
  57. package/dist/isard_core_bg.wasm +0 -0
  58. package/dist/isard_grid_bg.wasm +0 -0
  59. package/dist/isard_tess_bg.wasm +0 -0
  60. package/dist/layers/extrusions.js +5 -4
  61. package/dist/layers/extrusions.js.map +1 -1
  62. package/dist/layers/features.d.ts +0 -2
  63. package/dist/layers/features.js +5 -6
  64. package/dist/layers/features.js.map +1 -1
  65. package/dist/layers/layers.d.ts +2 -0
  66. package/dist/layers/layers.js +11 -0
  67. package/dist/layers/layers.js.map +1 -1
  68. package/dist/layers/panoramas.d.ts +32 -10
  69. package/dist/layers/panoramas.js +55 -28
  70. package/dist/layers/panoramas.js.map +1 -1
  71. package/dist/layers/photos.js +5 -6
  72. package/dist/layers/photos.js.map +1 -1
  73. package/dist/layers/trees.js +7 -7
  74. package/dist/layers/trees.js.map +1 -1
  75. package/dist/layers/vectors.d.ts +1 -0
  76. package/dist/layers/vectors.js +23 -8
  77. package/dist/layers/vectors.js.map +1 -1
  78. package/dist/map/frame-loop.d.ts +2 -0
  79. package/dist/map/frame-loop.js +3 -3
  80. package/dist/map/frame-loop.js.map +1 -1
  81. package/dist/map.js +17 -0
  82. package/dist/map.js.map +1 -1
  83. package/dist/panoramax.d.ts +6 -0
  84. package/dist/panoramax.js +29 -3
  85. package/dist/panoramax.js.map +1 -1
  86. package/dist/react/VectorGeometry.d.ts +18 -9
  87. package/dist/react/VectorGeometry.js +27 -13
  88. package/dist/react/VectorGeometry.js.map +1 -1
  89. package/dist/react/VectorTiles.d.ts +65 -0
  90. package/dist/react/VectorTiles.js +112 -0
  91. package/dist/react/VectorTiles.js.map +1 -0
  92. package/dist/react/context.d.ts +9 -0
  93. package/dist/react/context.js +32 -0
  94. package/dist/react/context.js.map +1 -1
  95. package/dist/react/index.d.ts +7 -1
  96. package/dist/react/index.js +7 -1
  97. package/dist/react/index.js.map +1 -1
  98. package/dist/types.d.ts +2 -0
  99. package/dist/types.js.map +1 -1
  100. package/dist/util/geo.d.ts +8 -0
  101. package/dist/util/geo.js +8 -0
  102. package/dist/util/geo.js.map +1 -1
  103. package/dist/util/gltf.d.ts +18 -1
  104. package/dist/util/gltf.js +26 -1
  105. package/dist/util/gltf.js.map +1 -1
  106. package/dist/util/roofs.d.ts +8 -0
  107. package/dist/util/roofs.js +16 -2
  108. package/dist/util/roofs.js.map +1 -1
  109. package/dist/util/style-floats.d.ts +33 -0
  110. package/dist/util/style-floats.js +34 -0
  111. package/dist/util/style-floats.js.map +1 -0
  112. package/dist/util/sun.js +1 -1
  113. package/dist/util/sun.js.map +1 -1
  114. package/dist/workers/basemap/furniture.d.ts +13 -0
  115. package/dist/workers/basemap/geometry.d.ts +2 -5
  116. package/dist/workers/basemap/labels.d.ts +5 -2
  117. package/dist/workers/basemap/lamps.d.ts +3 -3
  118. package/dist/workers/basemap/tiles.d.ts +2 -0
  119. package/dist/workers/basemap/trees.d.ts +3 -0
  120. package/dist/workers/basemap.worker.d.ts +17 -1
  121. package/dist/workers/basemap.worker.js +12 -1
  122. package/dist/workers/basemap.worker.js.map +1 -1
  123. package/package.json +1 -1
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Things placed by the workers in a vector basemap's tiles around the view (trees, street lamps, street furniture),
3
+ * each kind one instanced model layer: all of them near the ground under the camera, fewer and fewer past it (a
4
+ * simplified model there when the kind has one), the tiles following the view.
5
+ */
6
+ import { METRES_PER_DEGREE } from "../../util/geo.js";
7
+ import { PLACED_KIND, PLACED_NUMBERS, PLACED_RANK } from "../placed.js";
8
+ import { TileRequests } from "./requests.js";
9
+ import { nearPriority, tileKey, tilesAround } from "./tiles.js";
10
+ /** The engine's vertical field of view (radians, `camera::FOV`): where the camera stands behind the view's center */
11
+ const FOV = 0.6435011087932844;
12
+ /** While the camera moves, the things chosen again once it has gone this share of the near radius (or turned or
13
+ * zoomed as much): going through the tiles' things at every frame of a drag would hold the main thread */
14
+ const RESEND_SHARE = 0.05;
15
+ /** The ground under the camera: east and north metres from the view's center, and its distance from it (the camera
16
+ * stands behind the center of a tilted view, `FOV` its field of view) */
17
+ const groundUnderCamera = (map) => {
18
+ const { pitch, bearing } = map.getCamera();
19
+ const back = ((0.5 * map.getCanvas().clientHeight) / Math.tan(FOV / 2)) *
20
+ Math.sin((pitch * Math.PI) / 180) *
21
+ (map.metersPerPixel() ?? 0);
22
+ return [-back * Math.sin((bearing * Math.PI) / 180), -back * Math.cos((bearing * Math.PI) / 180), back];
23
+ };
24
+ /** What follows the camera as it moves: `send` called again once it has gone `RESEND_SHARE` of `radius` metres (or
25
+ * turned or zoomed as much), not at every frame of a drag */
26
+ const followWhenMoved = (map, radius, send) => {
27
+ /** The view of the last call: center (metres), zoom, bearing, pitch */
28
+ let sent;
29
+ return () => {
30
+ const { center, zoom, bearing, pitch } = map.getCamera();
31
+ const [x, y] = [
32
+ center[0] * METRES_PER_DEGREE * Math.cos((center[1] * Math.PI) / 180),
33
+ center[1] * METRES_PER_DEGREE,
34
+ ];
35
+ if (sent &&
36
+ Math.hypot(x - sent[0], y - sent[1]) < RESEND_SHARE * radius(zoom) &&
37
+ Math.abs(zoom - sent[2]) < RESEND_SHARE &&
38
+ Math.abs(bearing - sent[3]) < 45 * RESEND_SHARE &&
39
+ Math.abs(pitch - sent[4]) < 45 * RESEND_SHARE)
40
+ return;
41
+ sent = [x, y, zoom, bearing, pitch];
42
+ send();
43
+ };
44
+ };
45
+ /** The layers of the things placed: one per kind, then one per kind's simplified model; the first holds the others
46
+ * (`Layer.companions`) */
47
+ export const placed = (context, options) => {
48
+ const { map } = context;
49
+ const { models, far, minZoom, maxTiles, maxInstances } = options;
50
+ const add = (model) => map.addModels({ ...model, instances: [], ...(options.before ? { before: options.before } : {}) });
51
+ const [model, ...more] = models;
52
+ const layers = [add(model), ...more.map(add), ...(far?.models ?? []).map(add)];
53
+ const renderer = context.renderer(options.layers, []);
54
+ const radius = (zoom) => options.near[0] * 2 ** (options.near[1] - zoom);
55
+ renderer.catch(() => undefined);
56
+ layers[0].companions.push(...layers.slice(1));
57
+ context.layers.push(...layers);
58
+ const requests = new TileRequests();
59
+ const found = new Map();
60
+ /** The things drawn: all of them near the ground under the camera; past it those of a rank under `density` (near /
61
+ * distance)², simplified; at most `maxInstances` near, `far.instances` times as many past (beyond, the rank kept
62
+ * lowered in proportion: no sort) */
63
+ const send = () => {
64
+ const { center, zoom } = map.getCamera();
65
+ const cos = Math.cos((center[1] * Math.PI) / 180);
66
+ const [ex, ey, back] = groundUnderCamera(map);
67
+ const near = radius(zoom) + back;
68
+ const density = far?.density ?? 1;
69
+ /** Each thing that may be drawn: in full (near, or every one without simplified models) or simplified, with its
70
+ * score (the lowest kept first) */
71
+ const visit = (each) => {
72
+ found.forEach((t) => {
73
+ for (let at = 0; at < t.length; at += PLACED_NUMBERS) {
74
+ const dx = ((t[at] ?? 0) - center[0]) * METRES_PER_DEGREE * cos - ex;
75
+ const dy = ((t[at + 1] ?? 0) - center[1]) * METRES_PER_DEGREE - ey;
76
+ const distance = (dx * dx + dy * dy) / (near * near);
77
+ const rank = t[at + PLACED_RANK] ?? 0;
78
+ if (distance <= 1)
79
+ each(t, at, true, rank);
80
+ else if (rank * (distance / density) < 1)
81
+ each(t, at, !far, rank * (distance / density));
82
+ }
83
+ });
84
+ };
85
+ let [fullCount, farCount] = [0, 0];
86
+ visit((_, __, full) => {
87
+ if (full)
88
+ fullCount += 1;
89
+ else
90
+ farCount += 1;
91
+ });
92
+ const limits = [
93
+ Math.min(1, maxInstances / Math.max(fullCount, 1)),
94
+ Math.min(1, (maxInstances * (far?.instances ?? 1)) / Math.max(farCount, 1)),
95
+ ];
96
+ // The engine's numbers: longitude, latitude, altitude, heading, pitch, roll, scale
97
+ const out = layers.map(() => []);
98
+ visit((t, at, full, score) => {
99
+ if (score >= (limits[full ? 0 : 1] ?? 1))
100
+ return;
101
+ const kind = t[at + PLACED_KIND] ?? 0;
102
+ const k = (full ? 0 : models.length) + (kind < models.length ? kind : 0);
103
+ out[k]?.push(t[at] ?? 0, t[at + 1] ?? 0, 0, t[at + 3] ?? 0, 0, 0, t[at + 2] ?? 1);
104
+ });
105
+ layers.forEach((layer, k) => {
106
+ layer.setInstanceNumbers(Float64Array.from(out[k] ?? []));
107
+ });
108
+ };
109
+ const update = async () => {
110
+ if (context.destroyed())
111
+ return;
112
+ const { key, maxzoom: z, layers: list } = await renderer;
113
+ const camera = map.getCamera();
114
+ const wanted = camera.zoom < minZoom ? [] : tilesAround(map, camera, z, maxTiles);
115
+ const keys = new Set(wanted.map(([x, y]) => tileKey(z, x, y)));
116
+ // Out of view: dropped, or no longer asked for
117
+ requests
118
+ .keys()
119
+ .filter((k) => !keys.has(k))
120
+ .forEach((k) => {
121
+ requests.drop(k);
122
+ found.delete(k);
123
+ });
124
+ // Dense around the new center
125
+ send();
126
+ wanted.forEach(([x, y], index) => {
127
+ const k = tileKey(z, x, y);
128
+ if (requests.has(k))
129
+ return;
130
+ requests.ask(k, (signal) => context.request(z, x, y, options.work(key, z, x, y, nearPriority(index), list), signal), (r) => {
131
+ const things = options.read(r);
132
+ if (!things)
133
+ return;
134
+ found.set(k, things);
135
+ send();
136
+ });
137
+ });
138
+ };
139
+ // While the camera moves, the things drawn follow it (dense near it), chosen again once it has gone far enough
140
+ context.followView(layers[0], update, requests, followWhenMoved(map, radius, send));
141
+ return layers;
142
+ };
143
+ //# sourceMappingURL=placed.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"placed.js","sourceRoot":"","sources":["../../../src/basemap/vectortiles/placed.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Things placed by the workers in a vector basemap's tiles around the view (trees, street lamps, street furniture),\n * each kind one instanced model layer: all of them near the ground under the camera, fewer and fewer past it (a\n * simplified model there when the kind has one), the tiles following the view.\n */\n\nimport type { Layer, ModelLayer, ModelOptions } from '../../layers/layers.ts';\nimport type { GeoMap } from '../../map.ts';\nimport { METRES_PER_DEGREE } from '../../util/geo.ts';\nimport type { BasemapResponse, BasemapWork, Priority } from '../../workers/basemap.worker.ts';\nimport { PLACED_KIND, PLACED_NUMBERS, PLACED_RANK } from '../placed.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';\n\n/** The engine's vertical field of view (radians, `camera::FOV`): where the camera stands behind the view's center */\nconst FOV = 0.6435011087932844;\n\n/** While the camera moves, the things chosen again once it has gone this share of the near radius (or turned or\n * zoomed as much): going through the tiles' things at every frame of a drag would hold the main thread */\nconst RESEND_SHARE = 0.05;\n\n/** A model: a glTF binary's URL or bytes */\ntype Model = Pick<ModelOptions, 'url' | 'data'>;\n\ninterface PlacedOptions {\n /** A model per kind (`PLACED_KIND`); a kind past them drawn with the first */\n models: [Model, ...Model[]];\n /** Past `near`, the kinds' simplified models (as many), drawn `density` times as densely as the full ones would\n * be, at most `instances` times `maxInstances` */\n far?: { models: Model[]; density: number; instances: number };\n /** The style layers the renderer sends the workers (the tiles' source and its levels) */\n layers: (layer: StyleLayer) => boolean;\n /** The work asked of the worker of a tile (`layers`: the renderer's), and the things in its answer */\n work: (key: string, z: number, x: number, y: number, priority: Priority, layers: StyleLayer[]) => BasemapWork;\n read: (response: BasemapResponse) => Float64Array | undefined;\n /** Metres around the ground under the camera where every one is drawn at a zoom (twice as far a zoom level out) */\n near: [metres: number, zoom: number];\n minZoom: number;\n maxTiles: number;\n /** Things drawn at most, all kinds together (and `far.instances` times as many simplified) */\n maxInstances: number;\n before?: Layer;\n}\n\n/** The ground under the camera: east and north metres from the view's center, and its distance from it (the camera\n * stands behind the center of a tilted view, `FOV` its field of view) */\nconst groundUnderCamera = (map: GeoMap): [east: number, north: number, distance: number] => {\n const { pitch, bearing } = map.getCamera();\n const back =\n ((0.5 * map.getCanvas().clientHeight) / Math.tan(FOV / 2)) *\n Math.sin((pitch * Math.PI) / 180) *\n (map.metersPerPixel() ?? 0);\n\n return [-back * Math.sin((bearing * Math.PI) / 180), -back * Math.cos((bearing * Math.PI) / 180), back];\n};\n\n/** What follows the camera as it moves: `send` called again once it has gone `RESEND_SHARE` of `radius` metres (or\n * turned or zoomed as much), not at every frame of a drag */\nconst followWhenMoved = (map: GeoMap, radius: (zoom: number) => number, send: () => void): (() => void) => {\n /** The view of the last call: center (metres), zoom, bearing, pitch */\n let sent: [number, number, number, number, number] | undefined;\n\n return () => {\n const { center, zoom, bearing, pitch } = map.getCamera();\n const [x, y] = [\n center[0] * METRES_PER_DEGREE * Math.cos((center[1] * Math.PI) / 180),\n center[1] * METRES_PER_DEGREE,\n ];\n\n if (\n sent &&\n Math.hypot(x - sent[0], y - sent[1]) < RESEND_SHARE * radius(zoom) &&\n Math.abs(zoom - sent[2]) < RESEND_SHARE &&\n Math.abs(bearing - sent[3]) < 45 * RESEND_SHARE &&\n Math.abs(pitch - sent[4]) < 45 * RESEND_SHARE\n )\n return;\n sent = [x, y, zoom, bearing, pitch];\n send();\n };\n};\n\n/** The layers of the things placed: one per kind, then one per kind's simplified model; the first holds the others\n * (`Layer.companions`) */\nexport const placed = (context: VectorTilesContext, options: PlacedOptions): [ModelLayer, ...ModelLayer[]] => {\n const { map } = context;\n const { models, far, minZoom, maxTiles, maxInstances } = options;\n const add = (model: Model) =>\n map.addModels({ ...model, instances: [], ...(options.before ? { before: options.before } : {}) });\n const [model, ...more] = models;\n const layers: [ModelLayer, ...ModelLayer[]] = [add(model), ...more.map(add), ...(far?.models ?? []).map(add)];\n const renderer = context.renderer(options.layers, []);\n const radius = (zoom: number) => options.near[0] * 2 ** (options.near[1] - zoom);\n\n renderer.catch(() => undefined);\n layers[0].companions.push(...layers.slice(1));\n context.layers.push(...layers);\n\n const requests = new TileRequests();\n const found = new Map<string, Float64Array>();\n /** The things drawn: all of them near the ground under the camera; past it those of a rank under `density` (near /\n * distance)², simplified; at most `maxInstances` near, `far.instances` times as many past (beyond, the rank kept\n * 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 = radius(zoom) + back;\n const density = far?.density ?? 1;\n /** Each thing that may be drawn: in full (near, or every one without simplified models) or simplified, with its\n * score (the lowest kept first) */\n const visit = (each: (t: Float64Array, at: number, full: boolean, score: number) => void) => {\n found.forEach((t) => {\n for (let at = 0; at < t.length; at += PLACED_NUMBERS) {\n const dx = ((t[at] ?? 0) - center[0]) * METRES_PER_DEGREE * cos - ex;\n const dy = ((t[at + 1] ?? 0) - center[1]) * METRES_PER_DEGREE - ey;\n const distance = (dx * dx + dy * dy) / (near * near);\n const rank = t[at + PLACED_RANK] ?? 0;\n\n if (distance <= 1) each(t, at, true, rank);\n else if (rank * (distance / density) < 1) each(t, at, !far, rank * (distance / density));\n }\n });\n };\n let [fullCount, farCount] = [0, 0];\n\n visit((_, __, full) => {\n if (full) fullCount += 1;\n else farCount += 1;\n });\n\n const limits = [\n Math.min(1, maxInstances / Math.max(fullCount, 1)),\n Math.min(1, (maxInstances * (far?.instances ?? 1)) / Math.max(farCount, 1)),\n ];\n // The engine's numbers: longitude, latitude, altitude, heading, pitch, roll, scale\n const out: number[][] = layers.map(() => []);\n\n visit((t, at, full, score) => {\n if (score >= (limits[full ? 0 : 1] ?? 1)) return;\n\n const kind = t[at + PLACED_KIND] ?? 0;\n const k = (full ? 0 : models.length) + (kind < models.length ? kind : 0);\n\n out[k]?.push(t[at] ?? 0, t[at + 1] ?? 0, 0, t[at + 3] ?? 0, 0, 0, t[at + 2] ?? 1);\n });\n layers.forEach((layer, k) => {\n layer.setInstanceNumbers(Float64Array.from(out[k] ?? []));\n });\n };\n const update = async () => {\n if (context.destroyed()) return;\n\n const { key, maxzoom: z, layers: list } = await renderer;\n const camera = map.getCamera();\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 // Dense around the new center\n send();\n wanted.forEach(([x, y], index) => {\n const k = tileKey(z, x, y);\n\n if (requests.has(k)) return;\n requests.ask(\n k,\n (signal) => context.request(z, x, y, options.work(key, z, x, y, nearPriority(index), list), signal),\n (r) => {\n const things = options.read(r);\n\n if (!things) return;\n found.set(k, things);\n send();\n },\n );\n });\n };\n\n // While the camera moves, the things drawn follow it (dense near it), chosen again once it has gone far enough\n context.followView(layers[0], update, requests, followWhenMoved(map, radius, send));\n\n return layers;\n};\n"]}
@@ -1,10 +1,8 @@
1
1
  /** The wind through the streets of a vector basemap (`VectorTiles.streetWind`): a flow round its buildings */
2
- import { latitude, longitude, mercatorX, mercatorY } from "../../util/geo.js";
2
+ import { EARTH_METRES, latitude, longitude, mercatorX, mercatorY } from "../../util/geo.js";
3
3
  import { TileRequests } from "./requests.js";
4
4
  import { isBuildings, isPolygonLayer } from "./style.js";
5
5
  import { STREET_ZOOM } from "./tiles.js";
6
- /** Earth's circumference at the equator (metres): the size of the Mercator square, 1 wide, at latitude 0 */
7
- const EARTH_CIRCUMFERENCE_METRES = 40_075_016.686;
8
6
  /** Pause before the first square that failed is asked for again (ms) */
9
7
  const RETRY_FIRST_MS = 1000;
10
8
  /** Longest pause before a square that failed again and again is asked for again (ms) */
@@ -46,7 +44,7 @@ export const streetWind = (context, options = {}) => {
46
44
  const camera = map.getCamera();
47
45
  const [x, y] = [mercatorX(camera.center[0]), mercatorY(camera.center[1])];
48
46
  // Half the square in Mercator, at its latitude
49
- const half = size / 2 / (EARTH_CIRCUMFERENCE_METRES * Math.cos((camera.center[1] * Math.PI) / 180));
47
+ const half = size / 2 / (EARTH_METRES * Math.cos((camera.center[1] * Math.PI) / 180));
50
48
  const square = state.square;
51
49
  // Off street level: no square (its particles gone, asked for again back at street level)
52
50
  if (camera.zoom < minZoom) {
@@ -118,8 +116,8 @@ export const streetWind = (context, options = {}) => {
118
116
  };
119
117
  renderer.catch(() => undefined);
120
118
  context.layers.push(layer);
121
- context.followView(update, requests, () => undefined);
122
- context.cleanup.push(() => {
119
+ context.followView(layer, update, requests, () => undefined);
120
+ context.stopWith(layer, () => {
123
121
  clearTimeout(state.retry);
124
122
  });
125
123
  return Object.assign(layer, {
@@ -1 +1 @@
1
- {"version":3,"file":"street-wind.js","sourceRoot":"","sources":["../../../src/basemap/vectortiles/street-wind.ts"],"names":[],"mappings":"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","sourcesContent":["/** The wind through the streets of a vector basemap (`VectorTiles.streetWind`): a flow round its buildings */\n\nimport type { StreetFlows } from '../../grid-module.ts';\nimport type { FlowField, FlowLayer, FlowOptions } from '../../layers/flow.ts';\nimport { latitude, longitude, mercatorX, mercatorY } from '../../util/geo.ts';\nimport type { BasemapWork } 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 { STREET_ZOOM } from './tiles.ts';\n\n/** Earth's circumference at the equator (metres): the size of the Mercator square, 1 wide, at latitude 0 */\nconst EARTH_CIRCUMFERENCE_METRES = 40_075_016.686;\n\n/** Pause before the first square that failed is asked for again (ms) */\nconst RETRY_FIRST_MS = 1000;\n\n/** Longest pause before a square that failed again and again is asked for again (ms) */\nconst RETRY_MAX_MS = 30_000;\n\n/** The options of `VectorTiles.streetWind` */\nexport type StreetWindOptions = Omit<FlowOptions, 'field' | 'fields' | 'time'> & {\n wind?: [u: number, v: number];\n size?: number;\n cell?: number;\n iterations?: number;\n layers?: (layer: StyleLayer) => boolean;\n minZoom?: number;\n};\n\n/** The layer of `VectorTiles.streetWind` */\nexport type StreetWindLayer = FlowLayer & { setWind(wind: [u: number, v: number]): void; computed: Promise<void> };\n\nexport const streetWind = (context: VectorTilesContext, options: StreetWindOptions = {}): StreetWindLayer => {\n const { map } = context;\n const { size = 1200, cell = 4, iterations = 300, layers = isBuildings, minZoom = STREET_ZOOM, ...flow } = options;\n const renderer = context.renderer(isPolygonLayer(layers), []);\n /** No square: a field of no value at the view's center, where no particle is born nor moves (the map idles) */\n const empty = (): FlowField => {\n const [lon, lat] = map.getCamera().center;\n\n return {\n width: 2,\n height: 2,\n u: [NaN, NaN, NaN, NaN],\n v: [NaN, NaN, NaN, NaN],\n bounds: [lon, lat, lon + 1e-4, lat + 1e-4],\n };\n };\n const layer = map.addFlow({ height: 3, ...flow, field: empty() });\n const state: {\n wind: [number, number];\n /** The square solved (Mercator: west, north, east, south), its grid, the flows of its basis */\n square?: [number, number, number, number] | undefined;\n grid?: { width: number; height: number; bounds: [number, number, number, number] };\n flows?: StreetFlows | undefined;\n /** Bumped by each square asked for: only the last one's answer is taken */\n asked: number;\n /** After a failure, the square asked for again in `wait` ms (doubled each time) */\n retry?: number;\n wait: number;\n } = { wind: options.wind ?? [3, 2], asked: 0, wait: RETRY_FIRST_MS };\n /** The flow of the wind far away: `u` times the flow towards the east plus `v` times the one towards the north,\n * mixed by the engine (the two flows sent once a square, a new wind two numbers) */\n const send = () => {\n const { flows, grid, wind } = state;\n\n if (!flows || !grid) return;\n layer.setBasis({ ...grid, u: flows.eastU, v: flows.eastV }, { ...grid, u: flows.northU, v: flows.northV }, wind);\n };\n /** The square asked for, its only request (under `square`) */\n const requests = new TileRequests();\n const first: { resolve?: () => void } = {};\n const computed = new Promise<void>((resolve) => {\n first.resolve = resolve;\n });\n const update = async () => {\n if (context.destroyed()) return;\n\n const camera = map.getCamera();\n const [x, y] = [mercatorX(camera.center[0]), mercatorY(camera.center[1])];\n // Half the square in Mercator, at its latitude\n const half = size / 2 / (EARTH_CIRCUMFERENCE_METRES * Math.cos((camera.center[1] * Math.PI) / 180));\n const square = state.square;\n\n // Off street level: no square (its particles gone, asked for again back at street level)\n if (camera.zoom < minZoom) {\n if (state.square) {\n state.asked++;\n state.square = undefined;\n state.flows = undefined;\n requests.abortAll();\n layer.setField(empty());\n }\n\n return;\n }\n // Still in the square's middle: nothing to compute\n if (\n square &&\n Math.abs(x - (square[0] + square[2]) / 2) < half / 3 &&\n Math.abs(y - (square[1] + square[3]) / 2) < half / 3\n ) {\n return;\n }\n\n const { key, maxzoom } = await renderer;\n const bounds: [number, number, number, number] = [x - half, y - half, x + half, y + half];\n const n = 2 ** maxzoom;\n const tiles: number[] = [];\n\n for (let ty = Math.floor(bounds[1] * n); ty <= Math.floor(bounds[3] * n); ty++) {\n for (let tx = Math.floor(bounds[0] * n); tx <= Math.floor(bounds[2] * n); tx++) tiles.push(tx, ty);\n }\n\n const sides = Math.max(8, Math.round(size / cell));\n const work: BasemapWork = {\n type: 'street',\n key,\n module: map.gridUrl,\n z: maxzoom,\n tiles,\n bounds,\n width: sides,\n height: sides,\n iterations,\n };\n const asked = ++state.asked;\n\n requests.abortAll();\n\n const signal = requests.start('square');\n\n state.square = bounds;\n\n const answer = await context\n .request(maxzoom, Math.floor(x * n), Math.floor(y * n), work, signal)\n .catch((error: unknown) => ({ error }));\n\n if (asked !== state.asked || signal.aborted) return;\n // Failed (a tile or the grid module not fetched, a worker of an older build): the square asked for again later,\n // a while after each failure\n if (!('street' in answer)) {\n console.warn(\"isard: the streets' wind not computed, tried again\", 'error' in answer ? answer.error : answer);\n state.square = undefined;\n clearTimeout(state.retry);\n state.retry = window.setTimeout(() => {\n update().catch(() => undefined);\n }, state.wait);\n state.wait = Math.min(state.wait * 2, RETRY_MAX_MS);\n\n return;\n }\n state.wait = RETRY_FIRST_MS;\n state.flows = answer.street;\n state.grid = {\n width: sides,\n height: sides,\n bounds: [longitude(bounds[0]), latitude(bounds[3]), longitude(bounds[2]), latitude(bounds[1])],\n };\n send();\n first.resolve?.();\n };\n\n renderer.catch(() => undefined);\n context.layers.push(layer);\n context.followView(update, requests, () => undefined);\n context.cleanup.push(() => {\n clearTimeout(state.retry);\n });\n\n return Object.assign(layer, {\n computed,\n setWind: (wind: [u: number, v: number]) => {\n state.wind = wind;\n layer.setWeights(wind);\n },\n });\n};\n"]}
1
+ {"version":3,"file":"street-wind.js","sourceRoot":"","sources":["../../../src/basemap/vectortiles/street-wind.ts"],"names":[],"mappings":"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","sourcesContent":["/** The wind through the streets of a vector basemap (`VectorTiles.streetWind`): a flow round its buildings */\n\nimport type { StreetFlows } from '../../grid-module.ts';\nimport type { FlowField, FlowLayer, FlowOptions } from '../../layers/flow.ts';\nimport { EARTH_METRES, latitude, longitude, mercatorX, mercatorY } from '../../util/geo.ts';\nimport type { BasemapWork } 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 { STREET_ZOOM } from './tiles.ts';\n\n/** Pause before the first square that failed is asked for again (ms) */\nconst RETRY_FIRST_MS = 1000;\n\n/** Longest pause before a square that failed again and again is asked for again (ms) */\nconst RETRY_MAX_MS = 30_000;\n\n/** The options of `VectorTiles.streetWind` */\nexport type StreetWindOptions = Omit<FlowOptions, 'field' | 'fields' | 'time'> & {\n wind?: [u: number, v: number];\n size?: number;\n cell?: number;\n iterations?: number;\n layers?: (layer: StyleLayer) => boolean;\n minZoom?: number;\n};\n\n/** The layer of `VectorTiles.streetWind` */\nexport type StreetWindLayer = FlowLayer & { setWind(wind: [u: number, v: number]): void; computed: Promise<void> };\n\nexport const streetWind = (context: VectorTilesContext, options: StreetWindOptions = {}): StreetWindLayer => {\n const { map } = context;\n const { size = 1200, cell = 4, iterations = 300, layers = isBuildings, minZoom = STREET_ZOOM, ...flow } = options;\n const renderer = context.renderer(isPolygonLayer(layers), []);\n /** No square: a field of no value at the view's center, where no particle is born nor moves (the map idles) */\n const empty = (): FlowField => {\n const [lon, lat] = map.getCamera().center;\n\n return {\n width: 2,\n height: 2,\n u: [NaN, NaN, NaN, NaN],\n v: [NaN, NaN, NaN, NaN],\n bounds: [lon, lat, lon + 1e-4, lat + 1e-4],\n };\n };\n const layer = map.addFlow({ height: 3, ...flow, field: empty() });\n const state: {\n wind: [number, number];\n /** The square solved (Mercator: west, north, east, south), its grid, the flows of its basis */\n square?: [number, number, number, number] | undefined;\n grid?: { width: number; height: number; bounds: [number, number, number, number] };\n flows?: StreetFlows | undefined;\n /** Bumped by each square asked for: only the last one's answer is taken */\n asked: number;\n /** After a failure, the square asked for again in `wait` ms (doubled each time) */\n retry?: number;\n wait: number;\n } = { wind: options.wind ?? [3, 2], asked: 0, wait: RETRY_FIRST_MS };\n /** The flow of the wind far away: `u` times the flow towards the east plus `v` times the one towards the north,\n * mixed by the engine (the two flows sent once a square, a new wind two numbers) */\n const send = () => {\n const { flows, grid, wind } = state;\n\n if (!flows || !grid) return;\n layer.setBasis({ ...grid, u: flows.eastU, v: flows.eastV }, { ...grid, u: flows.northU, v: flows.northV }, wind);\n };\n /** The square asked for, its only request (under `square`) */\n const requests = new TileRequests();\n const first: { resolve?: () => void } = {};\n const computed = new Promise<void>((resolve) => {\n first.resolve = resolve;\n });\n const update = async () => {\n if (context.destroyed()) return;\n\n const camera = map.getCamera();\n const [x, y] = [mercatorX(camera.center[0]), mercatorY(camera.center[1])];\n // Half the square in Mercator, at its latitude\n const half = size / 2 / (EARTH_METRES * Math.cos((camera.center[1] * Math.PI) / 180));\n const square = state.square;\n\n // Off street level: no square (its particles gone, asked for again back at street level)\n if (camera.zoom < minZoom) {\n if (state.square) {\n state.asked++;\n state.square = undefined;\n state.flows = undefined;\n requests.abortAll();\n layer.setField(empty());\n }\n\n return;\n }\n // Still in the square's middle: nothing to compute\n if (\n square &&\n Math.abs(x - (square[0] + square[2]) / 2) < half / 3 &&\n Math.abs(y - (square[1] + square[3]) / 2) < half / 3\n ) {\n return;\n }\n\n const { key, maxzoom } = await renderer;\n const bounds: [number, number, number, number] = [x - half, y - half, x + half, y + half];\n const n = 2 ** maxzoom;\n const tiles: number[] = [];\n\n for (let ty = Math.floor(bounds[1] * n); ty <= Math.floor(bounds[3] * n); ty++) {\n for (let tx = Math.floor(bounds[0] * n); tx <= Math.floor(bounds[2] * n); tx++) tiles.push(tx, ty);\n }\n\n const sides = Math.max(8, Math.round(size / cell));\n const work: BasemapWork = {\n type: 'street',\n key,\n module: map.gridUrl,\n z: maxzoom,\n tiles,\n bounds,\n width: sides,\n height: sides,\n iterations,\n };\n const asked = ++state.asked;\n\n requests.abortAll();\n\n const signal = requests.start('square');\n\n state.square = bounds;\n\n const answer = await context\n .request(maxzoom, Math.floor(x * n), Math.floor(y * n), work, signal)\n .catch((error: unknown) => ({ error }));\n\n if (asked !== state.asked || signal.aborted) return;\n // Failed (a tile or the grid module not fetched, a worker of an older build): the square asked for again later,\n // a while after each failure\n if (!('street' in answer)) {\n console.warn(\"isard: the streets' wind not computed, tried again\", 'error' in answer ? answer.error : answer);\n state.square = undefined;\n clearTimeout(state.retry);\n state.retry = window.setTimeout(() => {\n update().catch(() => undefined);\n }, state.wait);\n state.wait = Math.min(state.wait * 2, RETRY_MAX_MS);\n\n return;\n }\n state.wait = RETRY_FIRST_MS;\n state.flows = answer.street;\n state.grid = {\n width: sides,\n height: sides,\n bounds: [longitude(bounds[0]), latitude(bounds[3]), longitude(bounds[2]), latitude(bounds[1])],\n };\n send();\n first.resolve?.();\n };\n\n renderer.catch(() => undefined);\n context.layers.push(layer);\n context.followView(layer, update, requests, () => undefined);\n context.stopWith(layer, () => {\n clearTimeout(state.retry);\n });\n\n return Object.assign(layer, {\n computed,\n setWind: (wind: [u: number, v: number]) => {\n state.wind = wind;\n layer.setWeights(wind);\n },\n });\n};\n"]}
@@ -1,11 +1,8 @@
1
1
  /** The trees of a vector basemap (`VectorTiles.trees`): 3D models scattered by the workers over its woods and grass */
2
2
  import type { Layer, ModelLayer } from '../../layers/layers.ts';
3
- import type { GeoMap } from '../../map.ts';
4
3
  import { type TreeColors } from '../trees.ts';
5
4
  import type { StyleLayer } from '../vectorstyle.ts';
6
5
  import type { VectorTilesContext } from './context.ts';
7
- /** Metres in a degree of latitude (and of longitude at the equator), near enough for the trees' distances */
8
- export declare const METRES_PER_DEGREE = 111320;
9
6
  /** The options of `VectorTiles.trees` */
10
7
  export interface TreesOptions {
11
8
  layers?: (layer: StyleLayer) => boolean;
@@ -19,10 +16,6 @@ export interface TreesOptions {
19
16
  data?: ArrayBuffer | ArrayBufferView<ArrayBuffer>;
20
17
  before?: Layer;
21
18
  }
22
- /** The ground under the camera: east and north metres from the view's center, and its distance from it (the camera
23
- * stands behind the center of a tilted view, `FOV` its field of view) */
24
- export declare const groundUnderCamera: (map: GeoMap) => [east: number, north: number, distance: number];
25
- /** What follows the camera as it moves: `send` called again once it has gone `RESEND_SHARE` of `radius` metres (or
26
- * turned or zoomed as much), not at every frame of a drag */
27
- export declare const followWhenMoved: (map: GeoMap, radius: (zoom: number) => number, send: () => void) => (() => void);
19
+ /** The trees' layers: broadleaved in full (the one returned, holding the others), conifers, then both simplified far
20
+ * away; with a model of the page's (`url`, `data`), that one alone, every tree in full */
28
21
  export declare const trees: (context: VectorTilesContext, options?: TreesOptions) => ModelLayer;
@@ -1,178 +1,47 @@
1
1
  /** The trees of a vector basemap (`VectorTiles.trees`): 3D models scattered by the workers over its woods and grass */
2
- import { coniferModel, isWoodland, TREE_NUMBERS, treeModel, treeSpacing } from "../trees.js";
3
- import { TileRequests } from "./requests.js";
4
- import { nearPriority, STREET_ZOOM, tileKey, tilesAround } from "./tiles.js";
2
+ import { coniferModel, isWoodland, treeModel, treeSpacing } from "../trees.js";
3
+ import { placed } from "./placed.js";
4
+ import { STREET_ZOOM } from "./tiles.js";
5
5
  /** Metres around the view's center where every tree is drawn in full at zoom 16 (twice as far a zoom level out);
6
6
  * past them, simplified (`treeModel(…, true)`) */
7
7
  const TREES_NEAR_METRES = 350;
8
- /** The engine's vertical field of view (radians, `camera::FOV`): where the camera stands behind the view's center */
9
- const FOV = 0.6435011087932844;
10
8
  /** How much denser the simplified trees far away stay than the full ones would (four times fewer triangles each): a
11
9
  * tree twice as far kept as often */
12
10
  const FAR_DENSITY = 4;
13
11
  /** The simplified trees drawn at most, for so many full ones (`maxInstances`) */
14
12
  const FAR_INSTANCES = 3;
15
- /** While the camera moves, the trees chosen again once it has gone this share of the full trees' radius (or turned
16
- * or zoomed as much): going through the tiles' trees at every frame of a drag would hold the main thread */
17
- const RESEND_SHARE = 0.05;
18
- /** Metres in a degree of latitude (and of longitude at the equator), near enough for the trees' distances */
19
- export const METRES_PER_DEGREE = 111320;
20
- /** The ground under the camera: east and north metres from the view's center, and its distance from it (the camera
21
- * stands behind the center of a tilted view, `FOV` its field of view) */
22
- export const groundUnderCamera = (map) => {
23
- const { pitch, bearing } = map.getCamera();
24
- const back = ((0.5 * map.getCanvas().clientHeight) / Math.tan(FOV / 2)) *
25
- Math.sin((pitch * Math.PI) / 180) *
26
- (map.metersPerPixel() ?? 0);
27
- return [-back * Math.sin((bearing * Math.PI) / 180), -back * Math.cos((bearing * Math.PI) / 180), back];
28
- };
29
- /** What follows the camera as it moves: `send` called again once it has gone `RESEND_SHARE` of `radius` metres (or
30
- * turned or zoomed as much), not at every frame of a drag */
31
- export const followWhenMoved = (map, radius, send) => {
32
- /** The view of the last call: center (metres), zoom, bearing, pitch */
33
- let sent;
34
- return () => {
35
- const { center, zoom, bearing, pitch } = map.getCamera();
36
- const [x, y] = [
37
- center[0] * METRES_PER_DEGREE * Math.cos((center[1] * Math.PI) / 180),
38
- center[1] * METRES_PER_DEGREE,
39
- ];
40
- if (sent &&
41
- Math.hypot(x - sent[0], y - sent[1]) < RESEND_SHARE * radius(zoom) &&
42
- Math.abs(zoom - sent[2]) < RESEND_SHARE &&
43
- Math.abs(bearing - sent[3]) < 45 * RESEND_SHARE &&
44
- Math.abs(pitch - sent[4]) < 45 * RESEND_SHARE)
45
- return;
46
- sent = [x, y, zoom, bearing, pitch];
47
- send();
48
- };
49
- };
13
+ /** The trees' layers: broadleaved in full (the one returned, holding the others), conifers, then both simplified far
14
+ * away; with a model of the page's (`url`, `data`), that one alone, every tree in full */
50
15
  export const trees = (context, options = {}) => {
51
- const { map } = context;
52
- const { layers = isWoodland, spacing = treeSpacing, minZoom = STREET_ZOOM, maxTiles = 9, maxTrees = 30000, maxInstances = 25000, } = options;
53
- const own = options.url !== undefined || options.data !== undefined;
54
- const model = options.url ? { url: options.url } : { data: options.data ?? treeModel(options.colors) };
55
- const before = options.before ? { before: options.before } : {};
56
- const layer = map.addModels({ ...model, instances: [], ...before });
57
- const add = (data) => map.addModels({ data, instances: [], ...before });
58
- // The other kinds and the simplified trees far away on layers of their own, with it: conifers near, the broadleaved
59
- // and the conifers far away (a model of the page's: every tree, in full)
60
- const others = own
61
- ? undefined
62
- : {
63
- conifers: add(coniferModel(options.colors)),
64
- farBroadleaved: add(treeModel(options.colors, true)),
65
- farConifers: add(coniferModel(options.colors, true)),
66
- };
67
- if (others)
68
- layer.companions.push(others.conifers, others.farBroadleaved, others.farConifers);
69
- const renderer = context.renderer((l) => l.type === 'fill' && layers(l), []);
70
- renderer.catch(() => undefined);
71
- context.layers.push(layer);
72
- /** The tiles held or asked for, by key, and the trees of those that came */
73
- const requests = new TileRequests();
74
- const found = new Map();
75
- /** The trees drawn to the engine (longitude, latitude, altitude, heading, pitch, roll, scale each): all of them in
76
- * full near the camera (as far as `TREES_NEAR_METRES` past the view's center), at most `maxInstances`; past `near`
77
- * metres, simplified, those of a rank under
78
- * `FAR_DENSITY` (near / distance)², at most `FAR_INSTANCES` times as many (a model of the page's: thinned as the
79
- * full ones would be, none simplified) */
80
- const send = () => {
81
- const { center, zoom } = map.getCamera();
82
- const cos = Math.cos((center[1] * Math.PI) / 180);
83
- // The trees near the ground under the camera in full, as far as some way past the center
84
- const [ex, ey, back] = groundUnderCamera(map);
85
- const near = TREES_NEAR_METRES * 2 ** (16 - zoom) + back;
86
- const density = others ? FAR_DENSITY : 1;
87
- /** Each tree of the tiles that may be drawn: in full (near, or every one with a model of the page's) or
88
- * simplified, with its score (the lowest kept first) */
89
- const visit = (each) => {
90
- found.forEach((t) => {
91
- for (let i = 0; i < t.length / TREE_NUMBERS; i += 1) {
92
- const dx = ((t[i * TREE_NUMBERS] ?? 0) - center[0]) * METRES_PER_DEGREE * cos - ex;
93
- const dy = ((t[i * TREE_NUMBERS + 1] ?? 0) - center[1]) * METRES_PER_DEGREE - ey;
94
- const distance = (dx * dx + dy * dy) / (near * near);
95
- const rank = t[i * TREE_NUMBERS + 4] ?? 0;
96
- if (distance <= 1)
97
- each(t, i, true, rank);
98
- else if (rank * (distance / density) < 1)
99
- each(t, i, !others, rank * (distance / density));
100
- }
101
- });
102
- };
103
- // How many would be drawn: past a cap, the score kept lowered in proportion (the ranks are even, no sort needed)
104
- let [fullCount, farCount] = [0, 0];
105
- visit((_, __, full) => {
106
- if (full)
107
- fullCount += 1;
108
- else
109
- farCount += 1;
110
- });
111
- const limits = [
112
- Math.min(1, maxInstances / Math.max(fullCount, 1)),
113
- Math.min(1, (maxInstances * FAR_INSTANCES) / Math.max(farCount, 1)),
114
- ];
115
- // The engine's numbers (longitude, latitude, altitude, heading, pitch, roll, scale): broadleaved and conifers in
116
- // full, then simplified
117
- const out = [[], [], [], []];
118
- visit((t, i, full, score) => {
119
- if (score >= (limits[full ? 0 : 1] ?? 1))
120
- return;
121
- const at = i * TREE_NUMBERS;
122
- const conifer = others && t[at + 5] === 1 ? 1 : 0;
123
- out[(full ? 0 : 2) + conifer]?.push(t[at] ?? 0, t[at + 1] ?? 0, 0, t[at + 3] ?? 0, 0, 0, t[at + 2] ?? 1);
124
- });
125
- const none = new Float64Array();
126
- const [broadleaved = none, conifers = none, farBroadleaved = none, farConifers = none] = out.map((o) => Float64Array.from(o));
127
- layer.setInstanceNumbers(broadleaved);
128
- if (others) {
129
- others.conifers.setInstanceNumbers(conifers);
130
- others.farBroadleaved.setInstanceNumbers(farBroadleaved);
131
- others.farConifers.setInstanceNumbers(farConifers);
132
- }
133
- };
134
- const update = async () => {
135
- if (context.destroyed())
136
- return;
137
- const { key, maxzoom, layers: list } = await renderer;
138
- const camera = map.getCamera();
139
- const z = maxzoom;
140
- const wanted = camera.zoom < minZoom ? [] : tilesAround(map, camera, z, maxTiles);
141
- const keys = new Set(wanted.map(([x, y]) => tileKey(z, x, y)));
142
- const metres = list.map((l) => (typeof spacing === 'number' ? spacing : spacing(l)));
143
- const dropped = requests.keys().filter((k) => !keys.has(k));
144
- // Out of view: dropped, or no longer asked for
145
- dropped.forEach((k) => {
146
- requests.drop(k);
147
- found.delete(k);
148
- });
149
- // Dense around the new center
150
- send();
151
- wanted.forEach(([x, y], index) => {
152
- const k = tileKey(z, x, y);
153
- if (requests.has(k))
154
- return;
155
- const work = {
156
- type: 'trees',
157
- key,
158
- z,
159
- x,
160
- y,
161
- spacing: metres,
162
- maxTrees,
163
- priority: nearPriority(index),
164
- };
165
- requests.ask(k, (signal) => context.request(z, x, y, work, signal), (r) => {
166
- if (!('trees' in r))
167
- return;
168
- found.set(k, r.trees);
169
- send();
170
- });
171
- });
172
- };
173
- // While the camera moves, the trees drawn follow it (dense near it), chosen again once it has gone far enough
174
- const follow = followWhenMoved(map, (zoom) => TREES_NEAR_METRES * 2 ** (16 - zoom), send);
175
- context.followView(update, requests, follow);
16
+ const { layers = isWoodland, spacing = treeSpacing, minZoom = STREET_ZOOM, maxTiles = 9, maxTrees = 30000, maxInstances = 25000, colors, } = options;
17
+ const own = options.url !== undefined ? { url: options.url } : options.data && { data: options.data };
18
+ const [layer] = placed(context, {
19
+ models: own ? [own] : [{ data: treeModel(colors) }, { data: coniferModel(colors) }],
20
+ ...(!own && {
21
+ far: {
22
+ models: [{ data: treeModel(colors, true) }, { data: coniferModel(colors, true) }],
23
+ density: FAR_DENSITY,
24
+ instances: FAR_INSTANCES,
25
+ },
26
+ }),
27
+ layers: (l) => l.type === 'fill' && layers(l),
28
+ work: (key, z, x, y, priority, list) => ({
29
+ type: 'trees',
30
+ key,
31
+ z,
32
+ x,
33
+ y,
34
+ spacing: list.map((l) => (typeof spacing === 'number' ? spacing : spacing(l))),
35
+ maxTrees,
36
+ priority,
37
+ }),
38
+ read: (r) => ('trees' in r ? r.trees : undefined),
39
+ near: [TREES_NEAR_METRES, 16],
40
+ minZoom,
41
+ maxTiles,
42
+ maxInstances,
43
+ ...(options.before && { before: options.before }),
44
+ });
176
45
  return layer;
177
46
  };
178
47
  //# sourceMappingURL=trees.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"trees.js","sourceRoot":"","sources":["../../../src/basemap/vectortiles/trees.ts"],"names":[],"mappings":"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","sourcesContent":["/** The trees of a vector basemap (`VectorTiles.trees`): 3D models scattered by the workers over its woods and grass */\n\nimport type { Layer, ModelLayer } from '../../layers/layers.ts';\nimport type { GeoMap } from '../../map.ts';\nimport type { BasemapWork } from '../../workers/basemap.worker.ts';\nimport { coniferModel, isWoodland, TREE_NUMBERS, type TreeColors, treeModel, treeSpacing } from '../trees.ts';\nimport type { StyleLayer } from '../vectorstyle.ts';\nimport type { VectorTilesContext } from './context.ts';\nimport { TileRequests } from './requests.ts';\nimport { nearPriority, STREET_ZOOM, tileKey, tilesAround } from './tiles.ts';\n\n/** Metres around the view's center where every tree is drawn in full at zoom 16 (twice as far a zoom level out);\n * past them, simplified (`treeModel(…, true)`) */\nconst TREES_NEAR_METRES = 350;\n\n/** The engine's vertical field of view (radians, `camera::FOV`): where the camera stands behind the view's center */\nconst FOV = 0.6435011087932844;\n\n/** How much denser the simplified trees far away stay than the full ones would (four times fewer triangles each): a\n * tree twice as far kept as often */\nconst FAR_DENSITY = 4;\n\n/** The simplified trees drawn at most, for so many full ones (`maxInstances`) */\nconst FAR_INSTANCES = 3;\n\n/** While the camera moves, the trees chosen again once it has gone this share of the full trees' radius (or turned\n * or zoomed as much): going through the tiles' trees at every frame of a drag would hold the main thread */\nconst RESEND_SHARE = 0.05;\n\n/** Metres in a degree of latitude (and of longitude at the equator), near enough for the trees' distances */\nexport const METRES_PER_DEGREE = 111320;\n\n/** The options of `VectorTiles.trees` */\nexport interface TreesOptions {\n layers?: (layer: StyleLayer) => boolean;\n spacing?: number | ((layer: StyleLayer) => number);\n minZoom?: number;\n maxTiles?: number;\n maxTrees?: number;\n maxInstances?: number;\n colors?: Partial<TreeColors>;\n url?: string;\n data?: ArrayBuffer | ArrayBufferView<ArrayBuffer>;\n before?: Layer;\n}\n\n/** The ground under the camera: east and north metres from the view's center, and its distance from it (the camera\n * stands behind the center of a tilted view, `FOV` its field of view) */\nexport const groundUnderCamera = (map: GeoMap): [east: number, north: number, distance: number] => {\n const { pitch, bearing } = map.getCamera();\n const back =\n ((0.5 * map.getCanvas().clientHeight) / Math.tan(FOV / 2)) *\n Math.sin((pitch * Math.PI) / 180) *\n (map.metersPerPixel() ?? 0);\n\n return [-back * Math.sin((bearing * Math.PI) / 180), -back * Math.cos((bearing * Math.PI) / 180), back];\n};\n\n/** What follows the camera as it moves: `send` called again once it has gone `RESEND_SHARE` of `radius` metres (or\n * turned or zoomed as much), not at every frame of a drag */\nexport const followWhenMoved = (map: GeoMap, radius: (zoom: number) => number, send: () => void): (() => void) => {\n /** The view of the last call: center (metres), zoom, bearing, pitch */\n let sent: [number, number, number, number, number] | undefined;\n\n return () => {\n const { center, zoom, bearing, pitch } = map.getCamera();\n const [x, y] = [\n center[0] * METRES_PER_DEGREE * Math.cos((center[1] * Math.PI) / 180),\n center[1] * METRES_PER_DEGREE,\n ];\n\n if (\n sent &&\n Math.hypot(x - sent[0], y - sent[1]) < RESEND_SHARE * radius(zoom) &&\n Math.abs(zoom - sent[2]) < RESEND_SHARE &&\n Math.abs(bearing - sent[3]) < 45 * RESEND_SHARE &&\n Math.abs(pitch - sent[4]) < 45 * RESEND_SHARE\n )\n return;\n sent = [x, y, zoom, bearing, pitch];\n send();\n };\n};\n\nexport const trees = (context: VectorTilesContext, options: TreesOptions = {}): ModelLayer => {\n const { map } = context;\n const {\n layers = isWoodland,\n spacing = treeSpacing,\n minZoom = STREET_ZOOM,\n maxTiles = 9,\n maxTrees = 30000,\n maxInstances = 25000,\n } = options;\n const own = options.url !== undefined || options.data !== undefined;\n const model = options.url ? { url: options.url } : { data: options.data ?? treeModel(options.colors) };\n const before = options.before ? { before: options.before } : {};\n const layer = map.addModels({ ...model, instances: [], ...before });\n const add = (data: Uint8Array<ArrayBuffer>) => map.addModels({ data, instances: [], ...before });\n // The other kinds and the simplified trees far away on layers of their own, with it: conifers near, the broadleaved\n // and the conifers far away (a model of the page's: every tree, in full)\n const others = own\n ? undefined\n : {\n conifers: add(coniferModel(options.colors)),\n farBroadleaved: add(treeModel(options.colors, true)),\n farConifers: add(coniferModel(options.colors, true)),\n };\n\n if (others) layer.companions.push(others.conifers, others.farBroadleaved, others.farConifers);\n const renderer = context.renderer((l) => l.type === 'fill' && layers(l), []);\n\n renderer.catch(() => undefined);\n context.layers.push(layer);\n\n /** The tiles held or asked for, by key, and the trees of those that came */\n const requests = new TileRequests();\n const found = new Map<string, Float64Array>();\n /** The trees drawn to the engine (longitude, latitude, altitude, heading, pitch, roll, scale each): all of them in\n * full near the camera (as far as `TREES_NEAR_METRES` past the view's center), at most `maxInstances`; past `near`\n * metres, simplified, those of a rank under\n * `FAR_DENSITY` (near / distance)², at most `FAR_INSTANCES` times as many (a model of the page's: thinned as the\n * full ones would be, none simplified) */\n const send = () => {\n const { center, zoom } = map.getCamera();\n const cos = Math.cos((center[1] * Math.PI) / 180);\n // The trees near the ground under the camera in full, as far as some way past the center\n const [ex, ey, back] = groundUnderCamera(map);\n const near = TREES_NEAR_METRES * 2 ** (16 - zoom) + back;\n const density = others ? FAR_DENSITY : 1;\n /** Each tree of the tiles that may be drawn: in full (near, or every one with a model of the page's) or\n * simplified, with its score (the lowest kept first) */\n const visit = (each: (t: Float64Array, i: number, full: boolean, 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 distance = (dx * dx + dy * dy) / (near * near);\n const rank = t[i * TREE_NUMBERS + 4] ?? 0;\n\n if (distance <= 1) each(t, i, true, rank);\n else if (rank * (distance / density) < 1) each(t, i, !others, rank * (distance / density));\n }\n });\n };\n // How many would be drawn: past a cap, the score kept lowered in proportion (the ranks are even, no sort needed)\n let [fullCount, farCount] = [0, 0];\n\n visit((_, __, full) => {\n if (full) fullCount += 1;\n else farCount += 1;\n });\n\n const limits = [\n Math.min(1, maxInstances / Math.max(fullCount, 1)),\n Math.min(1, (maxInstances * FAR_INSTANCES) / Math.max(farCount, 1)),\n ];\n // The engine's numbers (longitude, latitude, altitude, heading, pitch, roll, scale): broadleaved and conifers in\n // full, then simplified\n const out: number[][] = [[], [], [], []];\n\n visit((t, i, full, score) => {\n if (score >= (limits[full ? 0 : 1] ?? 1)) return;\n\n const at = i * TREE_NUMBERS;\n const conifer = others && t[at + 5] === 1 ? 1 : 0;\n\n out[(full ? 0 : 2) + conifer]?.push(t[at] ?? 0, t[at + 1] ?? 0, 0, t[at + 3] ?? 0, 0, 0, t[at + 2] ?? 1);\n });\n\n const none = new Float64Array();\n const [broadleaved = none, conifers = none, farBroadleaved = none, farConifers = none] = out.map((o) =>\n Float64Array.from(o),\n );\n\n layer.setInstanceNumbers(broadleaved);\n if (others) {\n others.conifers.setInstanceNumbers(conifers);\n others.farBroadleaved.setInstanceNumbers(farBroadleaved);\n others.farConifers.setInstanceNumbers(farConifers);\n }\n };\n const update = async () => {\n if (context.destroyed()) return;\n\n const { key, maxzoom, layers: list } = 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 const metres = list.map((l) => (typeof spacing === 'number' ? spacing : spacing(l)));\n const dropped = requests.keys().filter((k) => !keys.has(k));\n\n // Out of view: dropped, or no longer asked for\n dropped.forEach((k) => {\n requests.drop(k);\n found.delete(k);\n });\n // Dense around the new center\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 = {\n type: 'trees',\n key,\n z,\n x,\n y,\n spacing: metres,\n maxTrees,\n priority: nearPriority(index),\n };\n\n requests.ask(\n k,\n (signal) => context.request(z, x, y, work, signal),\n (r) => {\n if (!('trees' in r)) return;\n found.set(k, r.trees);\n send();\n },\n );\n });\n };\n // While the camera moves, the trees drawn follow it (dense near it), chosen again once it has gone far enough\n const follow = followWhenMoved(map, (zoom) => TREES_NEAR_METRES * 2 ** (16 - zoom), send);\n\n context.followView(update, requests, follow);\n\n return layer;\n};\n"]}
1
+ {"version":3,"file":"trees.js","sourceRoot":"","sources":["../../../src/basemap/vectortiles/trees.ts"],"names":[],"mappings":"AAAA,uHAAuH;AAGvH,OAAO,EAAE,YAAY,EAAE,UAAU,EAAmB,SAAS,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAGhG,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzC;kDACkD;AAClD,MAAM,iBAAiB,GAAG,GAAG,CAAC;AAE9B;qCACqC;AACrC,MAAM,WAAW,GAAG,CAAC,CAAC;AAEtB,iFAAiF;AACjF,MAAM,aAAa,GAAG,CAAC,CAAC;AAgBxB;0FAC0F;AAC1F,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,OAA2B,EAAE,UAAwB,EAAE,EAAc,EAAE;IAC3F,MAAM,EACJ,MAAM,GAAG,UAAU,EACnB,OAAO,GAAG,WAAW,EACrB,OAAO,GAAG,WAAW,EACrB,QAAQ,GAAG,CAAC,EACZ,QAAQ,GAAG,KAAK,EAChB,YAAY,GAAG,KAAK,EACpB,MAAM,GACP,GAAG,OAAO,CAAC;IACZ,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC;IACtG,MAAM,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC,OAAO,EAAE;QAC9B,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC;QACnF,GAAG,CAAC,CAAC,GAAG,IAAI;YACV,GAAG,EAAE;gBACH,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,SAAS,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,CAAC;gBACjF,OAAO,EAAE,WAAW;gBACpB,SAAS,EAAE,aAAa;aACzB;SACF,CAAC;QACF,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,MAAM,CAAC,CAAC,CAAC;QAC7C,IAAI,EAAE,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;YACvC,IAAI,EAAE,OAAO;YACb,GAAG;YACH,CAAC;YACD,CAAC;YACD,CAAC;YACD,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;YAC9E,QAAQ;YACR,QAAQ;SACT,CAAC;QACF,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;QACjD,IAAI,EAAE,CAAC,iBAAiB,EAAE,EAAE,CAAC;QAC7B,OAAO;QACP,QAAQ;QACR,YAAY;QACZ,GAAG,CAAC,OAAO,CAAC,MAAM,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC;KAClD,CAAC,CAAC;IAEH,OAAO,KAAK,CAAC;AACf,CAAC,CAAC","sourcesContent":["/** The trees of a vector basemap (`VectorTiles.trees`): 3D models scattered by the workers over its woods and grass */\n\nimport type { Layer, ModelLayer } from '../../layers/layers.ts';\nimport { coniferModel, isWoodland, type TreeColors, treeModel, treeSpacing } from '../trees.ts';\nimport type { StyleLayer } from '../vectorstyle.ts';\nimport type { VectorTilesContext } from './context.ts';\nimport { placed } from './placed.ts';\nimport { STREET_ZOOM } from './tiles.ts';\n\n/** Metres around the view's center where every tree is drawn in full at zoom 16 (twice as far a zoom level out);\n * past them, simplified (`treeModel(…, true)`) */\nconst TREES_NEAR_METRES = 350;\n\n/** How much denser the simplified trees far away stay than the full ones would (four times fewer triangles each): a\n * tree twice as far kept as often */\nconst FAR_DENSITY = 4;\n\n/** The simplified trees drawn at most, for so many full ones (`maxInstances`) */\nconst FAR_INSTANCES = 3;\n\n/** The options of `VectorTiles.trees` */\nexport interface TreesOptions {\n layers?: (layer: StyleLayer) => boolean;\n spacing?: number | ((layer: StyleLayer) => number);\n minZoom?: number;\n maxTiles?: number;\n maxTrees?: number;\n maxInstances?: number;\n colors?: Partial<TreeColors>;\n url?: string;\n data?: ArrayBuffer | ArrayBufferView<ArrayBuffer>;\n before?: Layer;\n}\n\n/** The trees' layers: broadleaved in full (the one returned, holding the others), conifers, then both simplified far\n * away; with a model of the page's (`url`, `data`), that one alone, every tree in full */\nexport const trees = (context: VectorTilesContext, options: TreesOptions = {}): ModelLayer => {\n const {\n layers = isWoodland,\n spacing = treeSpacing,\n minZoom = STREET_ZOOM,\n maxTiles = 9,\n maxTrees = 30000,\n maxInstances = 25000,\n colors,\n } = options;\n const own = options.url !== undefined ? { url: options.url } : options.data && { data: options.data };\n const [layer] = placed(context, {\n models: own ? [own] : [{ data: treeModel(colors) }, { data: coniferModel(colors) }],\n ...(!own && {\n far: {\n models: [{ data: treeModel(colors, true) }, { data: coniferModel(colors, true) }],\n density: FAR_DENSITY,\n instances: FAR_INSTANCES,\n },\n }),\n layers: (l) => l.type === 'fill' && layers(l),\n work: (key, z, x, y, priority, list) => ({\n type: 'trees',\n key,\n z,\n x,\n y,\n spacing: list.map((l) => (typeof spacing === 'number' ? spacing : spacing(l))),\n maxTrees,\n priority,\n }),\n read: (r) => ('trees' in r ? r.trees : undefined),\n near: [TREES_NEAR_METRES, 16],\n minZoom,\n maxTiles,\n maxInstances,\n ...(options.before && { before: options.before }),\n });\n\n return layer;\n};\n"]}
@@ -5,7 +5,7 @@
5
5
  import type { Icons } from '../../layers/icons.ts';
6
6
  import type { Layer } from '../../layers/layers.ts';
7
7
  import type { VectorLayer } from '../../layers/vectors.ts';
8
- import type { RoofMode } from '../../workers/basemap/geometry.ts';
8
+ import type { RoofMode } from '../../util/roofs.ts';
9
9
  import { type Json, type StyleLayer } from '../vectorstyle.ts';
10
10
  import type { VectorTilesContext } from './context.ts';
11
11
  /**
@@ -16,10 +16,9 @@ export declare const dashes: (value: Json | undefined, zoom: number) => [number,
16
16
  /** Whether a style layer draws streets, which glow at night: its id names a road (OpenMapTiles' styles, PLAN IGN's),
17
17
  * not its casing, a railway, a path or a tunnel */
18
18
  export declare const isStreet: (layer: StyleLayer) => boolean;
19
- /** A tile's styles with their patterns placed: each feature's pattern, as its index past one into `names` (float 28
20
- * for a fill, 29 for a line or a building: floats the engine does not keep), replaced by its image's place in the icon
21
- * atlas (texels) and pixel ratio, at 12 and 16 for a fill, 4 and 27 for a line or a building; none if the atlas has no
22
- * such image; the atlas uploaded */
19
+ /** A tile's styles with their patterns placed: each feature's pattern, as its index past one into `names`
20
+ * (`STYLE.fillPattern`, `STYLE.linePattern`), replaced by its image's place in the icon atlas (texels) and pixel
21
+ * ratio; none if the atlas has no such image; the atlas uploaded */
23
22
  export declare const placePatterns: (styles: Float32Array, names: readonly string[] | undefined, icons: Icons, prefix: string) => Float32Array;
24
23
  /** A `fill-translate` or `line-translate` at the camera's zoom (CSS pixels; none if it is not two numbers) */
25
24
  export declare const translation: (value: Json | undefined, zoom: number) => [number, number];