isard 1.3.0 → 1.4.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.
- package/CHANGELOG.md +36 -0
- package/README.md +2 -0
- package/dist/basemap/basemaps.js +1 -0
- package/dist/basemap/basemaps.js.map +1 -1
- package/dist/basemap/vectortiles.d.ts +26 -0
- package/dist/basemap/vectortiles.js +125 -1
- package/dist/basemap/vectortiles.js.map +1 -1
- package/dist/basemap/workerpool.js +9 -0
- package/dist/basemap/workerpool.js.map +1 -1
- package/dist/basemap.worker.js +2 -2
- package/dist/basemap.worker.js.map +7 -4
- package/dist/bind/layers.js +10 -0
- package/dist/bind/layers.js.map +1 -1
- package/dist/decoder.worker.js +3 -3
- package/dist/decoder.worker.js.map +3 -3
- package/dist/engine-loader.d.ts +4 -1
- package/dist/engine-loader.js +9 -0
- package/dist/engine-loader.js.map +1 -1
- package/dist/grid-module.d.ts +44 -0
- package/dist/grid-module.js +63 -0
- package/dist/grid-module.js.map +1 -0
- package/dist/grids.d.ts +51 -0
- package/dist/grids.js +88 -0
- package/dist/grids.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/isard_bg.wasm +0 -0
- package/dist/isard_core_bg.wasm +0 -0
- package/dist/isard_grid_bg.wasm +0 -0
- package/dist/layers/flow.d.ts +111 -0
- package/dist/layers/flow.js +222 -0
- package/dist/layers/flow.js.map +1 -0
- package/dist/layers/symbols.d.ts +2 -0
- package/dist/layers/symbols.js +2 -1
- package/dist/layers/symbols.js.map +1 -1
- package/dist/map/layer-spec.d.ts +4 -1
- package/dist/map/layer-spec.js +2 -0
- package/dist/map/layer-spec.js.map +1 -1
- package/dist/map/workers.d.ts +4 -0
- package/dist/map/workers.js +35 -0
- package/dist/map/workers.js.map +1 -1
- package/dist/map.d.ts +14 -0
- package/dist/map.js +19 -1
- package/dist/map.js.map +1 -1
- package/dist/react/Layer.d.ts +2 -0
- package/dist/react/Layer.js.map +1 -1
- package/dist/types.d.ts +7 -3
- package/dist/types.js.map +1 -1
- package/dist/wasm/isard.d.ts +42 -0
- package/dist/wasm/isard.js +91 -1
- package/dist/wasm/isard_core.d.ts +42 -0
- package/dist/wasm/isard_core.js +91 -1
- package/dist/wasm/isard_grid.d.ts +101 -0
- package/dist/wasm/isard_grid.js +375 -0
- package/dist/workers/basemap/street.d.ts +19 -0
- package/dist/workers/basemap.worker.d.ts +21 -0
- package/dist/workers/basemap.worker.js +23 -4
- package/dist/workers/basemap.worker.js.map +1 -1
- package/package.json +2 -1
- package/vite.js +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,42 @@
|
|
|
3
3
|
Every notable change of `isard` (published as `@ign-junn/isard` up to 0.1.1). The format follows [Keep a Changelog](https://keepachangelog.com), the
|
|
4
4
|
versions [Semantic Versioning](https://semver.org) (before 1.0.0, a minor version may change an API).
|
|
5
5
|
|
|
6
|
+
## [1.4.0] - 2026-10-24
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
|
|
10
|
+
- Flows: `map.addFlow({ field, colors, width, speed, density, particles, trail, playing })` (and `<Layer
|
|
11
|
+
type="flow">`), particles carried by a field of velocities (a wind, a current: its east and north components on a
|
|
12
|
+
grid of longitudes and latitudes, m/s), their trails animated on the globe and the relief and colored by speed;
|
|
13
|
+
born in the view and as dense on screen at any zoom, moved by the engine in steps of a 60th of a second, their last
|
|
14
|
+
positions kept on the GPU and drawn in 3D; `play`, `pause`, `step`, `setField`; `flowFieldFromImage` reads a field
|
|
15
|
+
from an image. The example Wind (NOAA's GFS over the world), in TypeScript and React. About 4.3 KB gzipped more for
|
|
16
|
+
the engine.
|
|
17
|
+
- A model's hours in a flow layer: `fields` at their times and `setTime` (a field between two hours their linear mix,
|
|
18
|
+
the particles going on), `flowFieldsFromImage` for the fields of one image; the example Wind through a day (GFS every
|
|
19
|
+
3 hours) with a clock.
|
|
20
|
+
- A model's files read in the workers: `map.readGrids(url | bytes)` reads GRIB2 (regular latitude/longitude grids,
|
|
21
|
+
packed simply, in groups or in groups of spatial differences, NOAA's GFS as it comes, with or without a bitmap) and
|
|
22
|
+
classic NetCDF (the CF conventions: scale, offset, fill values, times "since" a date) into `Grid`s, by a module of its
|
|
23
|
+
own (`isard_grid_bg.wasm`, the crate `isard-grid`, 58 KB gzipped, loaded with the first file; `gridWasmUrl`);
|
|
24
|
+
`windFields` pairs their wind's components by time into a flow layer's `fields`. The example Wind reads the GFS's
|
|
25
|
+
GRIB2 file.
|
|
26
|
+
- The wind through a city's streets: `vectorTiles().streetWind({ wind, size, cell, height })`, particles at street
|
|
27
|
+
level round the style's buildings (hidden by those in front of them), the flow a potential flow round their
|
|
28
|
+
footprints computed in a worker by the grid module, `setWind` turning it at once (the flows of an east and a north
|
|
29
|
+
wind combined); `windAt(fields, time, lonLat)`, a model's wind at a place and time. The example Wind in the streets
|
|
30
|
+
(the GFS's wind around the Louvre at the hour of a clock), in TypeScript and React. A flow layer's particles are
|
|
31
|
+
born only where its field has a value.
|
|
32
|
+
- `'osm-dark'`, OpenFreeMap's Dark style, and its `dark` and `fiord` styles in `OPENFREEMAP_STYLES`.
|
|
33
|
+
- Labels over the ground: `addSymbols({ height })` (and `<Layer type="symbols" height>`), metres over the ground (the
|
|
34
|
+
relief under them), on a building's floor or roof; placed and drawn there.
|
|
35
|
+
|
|
36
|
+
### Changed
|
|
37
|
+
|
|
38
|
+
- Inside a shopping mall: its five floors stacked as they stand rather than one at a time, each floor, the walls and the
|
|
39
|
+
names hidden on demand; its escalators and stairs climbing between floors, its lifts, toilets, information desks and
|
|
40
|
+
entrances from OpenStreetMap; the shops' names at their floor.
|
|
41
|
+
|
|
6
42
|
## [1.3.0] - 2026-10-23
|
|
7
43
|
|
|
8
44
|
### Added
|
package/README.md
CHANGED
|
@@ -44,6 +44,8 @@ General-purpose map libraries spend their time on things these maps do not need.
|
|
|
44
44
|
its `fill-extrusion` layers, sharp at any zoom (its tiles cut again past the source's deepest level), 3D trees in
|
|
45
45
|
its woods, the sun's shadows at any hour, the night with lit windows and glowing streets; indoor data floor by floor
|
|
46
46
|
(a shopping mall, its shops raised and named).
|
|
47
|
+
- 🌬️ **Wind and currents.** Particles carried by a field of velocities (a weather model's wind, an ocean's current),
|
|
48
|
+
their trails animated on the globe and the relief, colored by speed, as dense on screen at any zoom.
|
|
47
49
|
- 🏙️ **Buildings and 3D Tiles.** Buildings colored by a value or by a network's travel times, photomeshes and city
|
|
48
50
|
models in 3D Tiles (Draco, meshopt, KTX2).
|
|
49
51
|
- ☁️ **Point clouds.** LiDAR in COPC read by range requests (the IGN's LiDAR HD, reprojected from Lambert-93 onto the
|
package/dist/basemap/basemaps.js
CHANGED
|
@@ -75,6 +75,7 @@ export const SEA = {
|
|
|
75
75
|
'osm-positron': [0.761, 0.784, 0.792],
|
|
76
76
|
'osm-liberty': [0.62, 0.741, 1],
|
|
77
77
|
'osm-bright': [0.682, 0.812, 0.886],
|
|
78
|
+
'osm-dark': [0.106, 0.106, 0.114],
|
|
78
79
|
'ign-plan': [0.667, 0.835, 0.914],
|
|
79
80
|
'ign-plan-grey': [0.792, 0.792, 0.792],
|
|
80
81
|
'ign-plan-vector': [0.667, 0.835, 0.914],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"basemaps.js","sourceRoot":"","sources":["../../src/basemap/basemaps.ts"],"names":[],"mappings":"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","sourcesContent":["/** The basemaps by name: OpenFreeMap's vector styles (OpenStreetMap data, worldwide) and the Géoplateforme's (IGN,\n * France), the credits of the sources, and the relief of each body */\nimport { geoplateformeWmts, IGN_ATTRIBUTION } from '../geoplateforme.ts';\nimport type { BasemapName, RasterOptions } from '../types.ts';\n\nexport { IGN_ATTRIBUTION };\n\nconst PLAN = 'GEOGRAPHICALGRIDSYSTEMS.PLANIGNV2';\n\n/** The raster basemaps of the Géoplateforme (WMTS, Web Mercator), crediting the IGN */\nexport const BASEMAPS: Record<'ign-plan' | 'ign-plan-grey' | 'ign-ortho', RasterOptions> = {\n 'ign-plan': geoplateformeWmts(PLAN),\n 'ign-plan-grey': geoplateformeWmts(PLAN, { saturation: 0 }),\n 'ign-ortho': geoplateformeWmts('ORTHOIMAGERY.ORTHOPHOTOS', { format: 'image/jpeg' }),\n};\n\n/** The former names of the Géoplateforme basemaps (before 0.5), and their names now: an error tells it */\nexport const RENAMED_BASEMAPS: Record<string, BasemapName> = {\n plan: 'ign-plan',\n 'plan-grey': 'ign-plan-grey',\n ortho: 'ign-ortho',\n 'plan-vector': 'ign-plan-vector',\n 'plan-grey-vector': 'ign-plan-grey-vector',\n};\n\n/** Credits of OpenFreeMap's basemaps, as its TileJSON gives them */\nexport const OPENFREEMAP_ATTRIBUTION =\n '<a href=\"https://openfreemap.org\">OpenFreeMap</a> <a href=\"https://www.openmaptiles.org/\">© OpenMapTiles</a> ' +\n 'Data from <a href=\"https://www.openstreetmap.org/copyright\">OpenStreetMap</a>';\n\n/** Credits of the Terrain Tiles of AWS's open data registry (Mapzen's Terrarium tiles) and of their sources */\nexport const TERRAIN_TILES_ATTRIBUTION =\n '<a href=\"https://github.com/tilezen/joerd/blob/master/docs/attribution.md\">Terrain Tiles</a> (Mapzen, AWS)';\n\n/** IGN hillshade, grey on transparent, zooms 6 to 17 */\nexport const HILLSHADE: RasterOptions = geoplateformeWmts('IGNF_ELEVATION.ELEVATIONGRIDCOVERAGE.SHADOW', {\n minZoom: 6,\n maxZoom: 17,\n});\n\n/** IGN terrain model (RGE ALTI and its fill-ins, France), raw altitudes in metres from the Géoplateforme WMS */\nexport const TERRAIN_URL =\n 'https://data.geopf.fr/wms-r/wms?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetMap&LAYERS=ELEVATION.ELEVATIONGRIDCOVERAGE.HIGHRES' +\n '&STYLES=&CRS=EPSG:3857&BBOX={bbox}&WIDTH=256&HEIGHT=256&FORMAT=image/x-bil;bits=32';\n\n/** The relief of the Moon and Mars by default: NASA's Trek image services (ArcGIS), raw 32-bit altitudes in metres\n * asked for in the body's Mercator, with their credits. The Moon: LOLA's global model, 128 pixels per degree (237 m);\n * Mars: MOLA's, 128 and 64 pixels per degree merged (463 m), above the areoid */\nexport const BODY_TERRAINS = {\n moon: {\n url: 'https://trek.nasa.gov/moon/trekarcgis/rest/services/LRO_LOLA_DEM_Global_128ppd_v04/ImageServer',\n maxZoom: 8,\n attribution: '<a href=\"https://trek.nasa.gov/moon/\">NASA Moon Trek</a>: LOLA terrain model, NASA/GSFC/MIT',\n },\n mars: {\n url: 'https://trek.nasa.gov/mars/trekarcgis/rest/services/mola128_mola64_merge_90Nto90S_SimpleC_clon0/ImageServer',\n maxZoom: 8,\n attribution: '<a href=\"https://trek.nasa.gov/mars/\">NASA Mars Trek</a>: MOLA terrain model, NASA/JPL/GSFC',\n },\n} as const;\n\n/** An ArcGIS image service (a URL ending in `/ImageServer`) */\nexport const IMAGE_SERVER = /\\/ImageServer\\/?$/;\n\n/**\n * The `exportImage` template of an ArcGIS image service for the relief: 256 × 256 raw 32-bit floats (`bsq`, followed\n * by its validity mask) of the box `{bbox}` in the Mercator of the body. On the Earth, Web Mercator (EPSG:3857);\n * elsewhere, a Mercator on the sphere of `radius` metres (Web Mercator's own formulas), given in WKT.\n */\nexport const imageServerTerrain = (url: string, earth: boolean, radius: number): string => {\n const crs = earth\n ? '3857'\n : encodeURIComponent(\n JSON.stringify({\n wkt:\n `PROJCS[\"Mercator\",GEOGCS[\"GCS\",DATUM[\"D\",SPHEROID[\"S\",${radius},0]],PRIMEM[\"Reference_Meridian\",0],` +\n 'UNIT[\"Degree\",0.0174532925199433]],PROJECTION[\"Mercator\"],PARAMETER[\"False_Easting\",0],' +\n 'PARAMETER[\"False_Northing\",0],PARAMETER[\"Central_Meridian\",0],PARAMETER[\"Standard_Parallel_1\",0],' +\n 'UNIT[\"Meter\",1]]',\n }),\n );\n\n return (\n `${url.replace(/\\/$/, '')}/exportImage?f=image&format=bsq&pixelType=F32&size=256,256` +\n `&bboxSR=${crs}&imageSR=${crs}&bbox={bbox}`\n );\n};\n\n/** The Earth's relief for `terrain: true`: the Terrain Tiles of AWS's open data registry, worldwide (SRTM, EU-DEM,\n * GMTED… merged by Mapzen), Terrarium PNGs of 256 px to level 14, altitudes above the mean sea level */\nexport const TERRAIN_TILES = {\n url: 'https://s3.amazonaws.com/elevation-tiles-prod/terrarium/{z}/{x}/{y}.png',\n maxZoom: 14,\n} as const;\n\n/** Sea color of each basemap, for the globe under the tiles (polar caps), RGB 0-1 */\nexport const SEA: Record<BasemapName, [r: number, g: number, b: number]> = {\n 'osm-positron': [0.761, 0.784, 0.792],\n 'osm-liberty': [0.62, 0.741, 1],\n 'osm-bright': [0.682, 0.812, 0.886],\n 'ign-plan': [0.667, 0.835, 0.914],\n 'ign-plan-grey': [0.792, 0.792, 0.792],\n 'ign-plan-vector': [0.667, 0.835, 0.914],\n 'ign-plan-grey-vector': [0.792, 0.792, 0.792],\n 'ign-ortho': [0.039, 0.231, 0.349],\n none: [0.85, 0.85, 0.83],\n};\n"]}
|
|
1
|
+
{"version":3,"file":"basemaps.js","sourceRoot":"","sources":["../../src/basemap/basemaps.ts"],"names":[],"mappings":"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","sourcesContent":["/** The basemaps by name: OpenFreeMap's vector styles (OpenStreetMap data, worldwide) and the Géoplateforme's (IGN,\n * France), the credits of the sources, and the relief of each body */\nimport { geoplateformeWmts, IGN_ATTRIBUTION } from '../geoplateforme.ts';\nimport type { BasemapName, RasterOptions } from '../types.ts';\n\nexport { IGN_ATTRIBUTION };\n\nconst PLAN = 'GEOGRAPHICALGRIDSYSTEMS.PLANIGNV2';\n\n/** The raster basemaps of the Géoplateforme (WMTS, Web Mercator), crediting the IGN */\nexport const BASEMAPS: Record<'ign-plan' | 'ign-plan-grey' | 'ign-ortho', RasterOptions> = {\n 'ign-plan': geoplateformeWmts(PLAN),\n 'ign-plan-grey': geoplateformeWmts(PLAN, { saturation: 0 }),\n 'ign-ortho': geoplateformeWmts('ORTHOIMAGERY.ORTHOPHOTOS', { format: 'image/jpeg' }),\n};\n\n/** The former names of the Géoplateforme basemaps (before 0.5), and their names now: an error tells it */\nexport const RENAMED_BASEMAPS: Record<string, BasemapName> = {\n plan: 'ign-plan',\n 'plan-grey': 'ign-plan-grey',\n ortho: 'ign-ortho',\n 'plan-vector': 'ign-plan-vector',\n 'plan-grey-vector': 'ign-plan-grey-vector',\n};\n\n/** Credits of OpenFreeMap's basemaps, as its TileJSON gives them */\nexport const OPENFREEMAP_ATTRIBUTION =\n '<a href=\"https://openfreemap.org\">OpenFreeMap</a> <a href=\"https://www.openmaptiles.org/\">© OpenMapTiles</a> ' +\n 'Data from <a href=\"https://www.openstreetmap.org/copyright\">OpenStreetMap</a>';\n\n/** Credits of the Terrain Tiles of AWS's open data registry (Mapzen's Terrarium tiles) and of their sources */\nexport const TERRAIN_TILES_ATTRIBUTION =\n '<a href=\"https://github.com/tilezen/joerd/blob/master/docs/attribution.md\">Terrain Tiles</a> (Mapzen, AWS)';\n\n/** IGN hillshade, grey on transparent, zooms 6 to 17 */\nexport const HILLSHADE: RasterOptions = geoplateformeWmts('IGNF_ELEVATION.ELEVATIONGRIDCOVERAGE.SHADOW', {\n minZoom: 6,\n maxZoom: 17,\n});\n\n/** IGN terrain model (RGE ALTI and its fill-ins, France), raw altitudes in metres from the Géoplateforme WMS */\nexport const TERRAIN_URL =\n 'https://data.geopf.fr/wms-r/wms?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetMap&LAYERS=ELEVATION.ELEVATIONGRIDCOVERAGE.HIGHRES' +\n '&STYLES=&CRS=EPSG:3857&BBOX={bbox}&WIDTH=256&HEIGHT=256&FORMAT=image/x-bil;bits=32';\n\n/** The relief of the Moon and Mars by default: NASA's Trek image services (ArcGIS), raw 32-bit altitudes in metres\n * asked for in the body's Mercator, with their credits. The Moon: LOLA's global model, 128 pixels per degree (237 m);\n * Mars: MOLA's, 128 and 64 pixels per degree merged (463 m), above the areoid */\nexport const BODY_TERRAINS = {\n moon: {\n url: 'https://trek.nasa.gov/moon/trekarcgis/rest/services/LRO_LOLA_DEM_Global_128ppd_v04/ImageServer',\n maxZoom: 8,\n attribution: '<a href=\"https://trek.nasa.gov/moon/\">NASA Moon Trek</a>: LOLA terrain model, NASA/GSFC/MIT',\n },\n mars: {\n url: 'https://trek.nasa.gov/mars/trekarcgis/rest/services/mola128_mola64_merge_90Nto90S_SimpleC_clon0/ImageServer',\n maxZoom: 8,\n attribution: '<a href=\"https://trek.nasa.gov/mars/\">NASA Mars Trek</a>: MOLA terrain model, NASA/JPL/GSFC',\n },\n} as const;\n\n/** An ArcGIS image service (a URL ending in `/ImageServer`) */\nexport const IMAGE_SERVER = /\\/ImageServer\\/?$/;\n\n/**\n * The `exportImage` template of an ArcGIS image service for the relief: 256 × 256 raw 32-bit floats (`bsq`, followed\n * by its validity mask) of the box `{bbox}` in the Mercator of the body. On the Earth, Web Mercator (EPSG:3857);\n * elsewhere, a Mercator on the sphere of `radius` metres (Web Mercator's own formulas), given in WKT.\n */\nexport const imageServerTerrain = (url: string, earth: boolean, radius: number): string => {\n const crs = earth\n ? '3857'\n : encodeURIComponent(\n JSON.stringify({\n wkt:\n `PROJCS[\"Mercator\",GEOGCS[\"GCS\",DATUM[\"D\",SPHEROID[\"S\",${radius},0]],PRIMEM[\"Reference_Meridian\",0],` +\n 'UNIT[\"Degree\",0.0174532925199433]],PROJECTION[\"Mercator\"],PARAMETER[\"False_Easting\",0],' +\n 'PARAMETER[\"False_Northing\",0],PARAMETER[\"Central_Meridian\",0],PARAMETER[\"Standard_Parallel_1\",0],' +\n 'UNIT[\"Meter\",1]]',\n }),\n );\n\n return (\n `${url.replace(/\\/$/, '')}/exportImage?f=image&format=bsq&pixelType=F32&size=256,256` +\n `&bboxSR=${crs}&imageSR=${crs}&bbox={bbox}`\n );\n};\n\n/** The Earth's relief for `terrain: true`: the Terrain Tiles of AWS's open data registry, worldwide (SRTM, EU-DEM,\n * GMTED… merged by Mapzen), Terrarium PNGs of 256 px to level 14, altitudes above the mean sea level */\nexport const TERRAIN_TILES = {\n url: 'https://s3.amazonaws.com/elevation-tiles-prod/terrarium/{z}/{x}/{y}.png',\n maxZoom: 14,\n} as const;\n\n/** Sea color of each basemap, for the globe under the tiles (polar caps), RGB 0-1 */\nexport const SEA: Record<BasemapName, [r: number, g: number, b: number]> = {\n 'osm-positron': [0.761, 0.784, 0.792],\n 'osm-liberty': [0.62, 0.741, 1],\n 'osm-bright': [0.682, 0.812, 0.886],\n 'osm-dark': [0.106, 0.106, 0.114],\n 'ign-plan': [0.667, 0.835, 0.914],\n 'ign-plan-grey': [0.792, 0.792, 0.792],\n 'ign-plan-vector': [0.667, 0.835, 0.914],\n 'ign-plan-grey-vector': [0.792, 0.792, 0.792],\n 'ign-ortho': [0.039, 0.231, 0.349],\n none: [0.85, 0.85, 0.83],\n};\n"]}
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import type { ExtrusionLayer, ExtrusionOptions } from '../layers/extrusions.ts';
|
|
12
12
|
import type { Feature, Size } from '../layers/features.ts';
|
|
13
|
+
import type { FlowLayer, FlowOptions } from '../layers/flow.ts';
|
|
13
14
|
import type { Layer, ModelLayer, RasterLayer } from '../layers/layers.ts';
|
|
14
15
|
import type { SymbolLayer } from '../layers/symbols.ts';
|
|
15
16
|
import type { VectorLayer } from '../layers/vectors.ts';
|
|
@@ -21,6 +22,8 @@ export declare const OPENFREEMAP_STYLES: {
|
|
|
21
22
|
readonly positron: "https://tiles.openfreemap.org/styles/positron";
|
|
22
23
|
readonly liberty: "https://tiles.openfreemap.org/styles/liberty";
|
|
23
24
|
readonly bright: "https://tiles.openfreemap.org/styles/bright";
|
|
25
|
+
readonly dark: "https://tiles.openfreemap.org/styles/dark";
|
|
26
|
+
readonly fiord: "https://tiles.openfreemap.org/styles/fiord";
|
|
24
27
|
};
|
|
25
28
|
/** PLAN.IGN vector styles of the Géoplateforme (France) */
|
|
26
29
|
export declare const PLAN_STYLES: {
|
|
@@ -32,6 +35,7 @@ export declare const VECTOR_BASEMAPS: {
|
|
|
32
35
|
readonly 'osm-positron': "https://tiles.openfreemap.org/styles/positron";
|
|
33
36
|
readonly 'osm-liberty': "https://tiles.openfreemap.org/styles/liberty";
|
|
34
37
|
readonly 'osm-bright': "https://tiles.openfreemap.org/styles/bright";
|
|
38
|
+
readonly 'osm-dark': "https://tiles.openfreemap.org/styles/dark";
|
|
35
39
|
readonly 'ign-plan-vector': "https://data.geopf.fr/annexes/ressources/vectorTiles/styles/PLAN.IGN/standard.json";
|
|
36
40
|
readonly 'ign-plan-grey-vector': "https://data.geopf.fr/annexes/ressources/vectorTiles/styles/PLAN.IGN/gris.json";
|
|
37
41
|
};
|
|
@@ -123,6 +127,28 @@ export declare class VectorTiles {
|
|
|
123
127
|
minZoom?: number;
|
|
124
128
|
maxTiles?: number;
|
|
125
129
|
}): ExtrusionLayer<P>;
|
|
130
|
+
/**
|
|
131
|
+
* The wind through the streets around the view, as particles at street level (`height`, default 3 metres) between
|
|
132
|
+
* the style's buildings (`layers`, default its `building` and `bati_surf` layers): their footprints laid on a grid of
|
|
133
|
+
* `cell` metres (default 4) over a square of `size` metres (default 1200) around the view's center, the flow of the
|
|
134
|
+
* air round them a potential flow (it goes round the blocks and faster through the narrow streets; no wake behind
|
|
135
|
+
* them), computed in a worker by the grid module from street level (`minZoom`, default 15) and again when the view
|
|
136
|
+
* leaves the square's middle. `wind` is the wind far away (east and north, m/s, default a light breeze from the
|
|
137
|
+
* south-west); `setWind` changes it at once (the flow is linear in it: nothing computed again), from a model's hour
|
|
138
|
+
* (`windAt`) for instance; `computed` resolves once the first square's flow is there. The other options are the flow
|
|
139
|
+
* layer's (`addFlow`)
|
|
140
|
+
*/
|
|
141
|
+
streetWind(options?: Omit<FlowOptions, 'field' | 'fields' | 'time'> & {
|
|
142
|
+
wind?: [u: number, v: number];
|
|
143
|
+
size?: number;
|
|
144
|
+
cell?: number;
|
|
145
|
+
iterations?: number;
|
|
146
|
+
layers?: (layer: StyleLayer) => boolean;
|
|
147
|
+
minZoom?: number;
|
|
148
|
+
}): FlowLayer & {
|
|
149
|
+
setWind(wind: [u: number, v: number]): void;
|
|
150
|
+
computed: Promise<void>;
|
|
151
|
+
};
|
|
126
152
|
/**
|
|
127
153
|
* Trees on the style's woods, forests and grass (or the fill layers `layers` keeps) as 3D models, from street level
|
|
128
154
|
* (`minZoom`, default 15): the workers scatter them over the polygons of the vector tiles around the view, `spacing`
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
import { withHeaders } from "../engine/requests.js";
|
|
12
12
|
import { IGN_ATTRIBUTION } from "../geoplateforme.js";
|
|
13
13
|
import { loadSprites, readPixels } from "../layers/icons.js";
|
|
14
|
-
import { mercatorX, mercatorY } from "../util/geo.js";
|
|
14
|
+
import { latitude, longitude, mercatorX, mercatorY } from "../util/geo.js";
|
|
15
15
|
import { isRecord } from "../util/guards.js";
|
|
16
16
|
import { Lru } from "../util/lru.js";
|
|
17
17
|
import { OPENFREEMAP_ATTRIBUTION } from "./basemaps.js";
|
|
@@ -24,6 +24,8 @@ export const OPENFREEMAP_STYLES = {
|
|
|
24
24
|
positron: 'https://tiles.openfreemap.org/styles/positron',
|
|
25
25
|
liberty: 'https://tiles.openfreemap.org/styles/liberty',
|
|
26
26
|
bright: 'https://tiles.openfreemap.org/styles/bright',
|
|
27
|
+
dark: 'https://tiles.openfreemap.org/styles/dark',
|
|
28
|
+
fiord: 'https://tiles.openfreemap.org/styles/fiord',
|
|
27
29
|
};
|
|
28
30
|
/** PLAN.IGN vector styles of the Géoplateforme (France) */
|
|
29
31
|
export const PLAN_STYLES = {
|
|
@@ -35,6 +37,7 @@ export const VECTOR_BASEMAPS = {
|
|
|
35
37
|
'osm-positron': OPENFREEMAP_STYLES.positron,
|
|
36
38
|
'osm-liberty': OPENFREEMAP_STYLES.liberty,
|
|
37
39
|
'osm-bright': OPENFREEMAP_STYLES.bright,
|
|
40
|
+
'osm-dark': OPENFREEMAP_STYLES.dark,
|
|
38
41
|
'ign-plan-vector': PLAN_STYLES.standard,
|
|
39
42
|
'ign-plan-grey-vector': PLAN_STYLES.grey,
|
|
40
43
|
};
|
|
@@ -360,6 +363,127 @@ export class VectorTiles {
|
|
|
360
363
|
this.followView(update, held, update);
|
|
361
364
|
return extrusions;
|
|
362
365
|
}
|
|
366
|
+
/**
|
|
367
|
+
* The wind through the streets around the view, as particles at street level (`height`, default 3 metres) between
|
|
368
|
+
* the style's buildings (`layers`, default its `building` and `bati_surf` layers): their footprints laid on a grid of
|
|
369
|
+
* `cell` metres (default 4) over a square of `size` metres (default 1200) around the view's center, the flow of the
|
|
370
|
+
* air round them a potential flow (it goes round the blocks and faster through the narrow streets; no wake behind
|
|
371
|
+
* them), computed in a worker by the grid module from street level (`minZoom`, default 15) and again when the view
|
|
372
|
+
* leaves the square's middle. `wind` is the wind far away (east and north, m/s, default a light breeze from the
|
|
373
|
+
* south-west); `setWind` changes it at once (the flow is linear in it: nothing computed again), from a model's hour
|
|
374
|
+
* (`windAt`) for instance; `computed` resolves once the first square's flow is there. The other options are the flow
|
|
375
|
+
* layer's (`addFlow`)
|
|
376
|
+
*/
|
|
377
|
+
streetWind(options = {}) {
|
|
378
|
+
const { size = 1200, cell = 4, iterations = 300, layers = isBuildings, minZoom = 15, ...flow } = options;
|
|
379
|
+
const renderer = this.renderer((l) => (l.type === 'fill' || l.type === 'fill-extrusion') && layers(l), []);
|
|
380
|
+
const [lon, lat] = this.map.getCamera().center;
|
|
381
|
+
// Nothing yet: a field of no value, where no particle is born
|
|
382
|
+
const empty = { width: 2, height: 2, u: [NaN, NaN, NaN, NaN], v: [NaN, NaN, NaN, NaN] };
|
|
383
|
+
const layer = this.map.addFlow({
|
|
384
|
+
height: 3,
|
|
385
|
+
...flow,
|
|
386
|
+
field: { ...empty, bounds: [lon, lat, lon + 1e-4, lat + 1e-4] },
|
|
387
|
+
});
|
|
388
|
+
const state = { wind: options.wind ?? [3, 2], asked: 0, wait: 1000 };
|
|
389
|
+
/** The flow of the wind far away: `u` times the flow towards the east plus `v` times the one towards the north */
|
|
390
|
+
const send = () => {
|
|
391
|
+
const { flows, grid, wind } = state;
|
|
392
|
+
if (!flows || !grid)
|
|
393
|
+
return;
|
|
394
|
+
const [wu, wv] = wind;
|
|
395
|
+
const u = flows.eastU.map((e, i) => wu * e + wv * (flows.northU[i] ?? 0));
|
|
396
|
+
const v = flows.eastV.map((e, i) => wu * e + wv * (flows.northV[i] ?? 0));
|
|
397
|
+
layer.setField({ ...grid, u, v });
|
|
398
|
+
};
|
|
399
|
+
const held = new Map();
|
|
400
|
+
const first = {};
|
|
401
|
+
const computed = new Promise((resolve) => {
|
|
402
|
+
first.resolve = resolve;
|
|
403
|
+
});
|
|
404
|
+
const update = async () => {
|
|
405
|
+
if (this.destroyed)
|
|
406
|
+
return;
|
|
407
|
+
const camera = this.map.getCamera();
|
|
408
|
+
const [x, y] = [mercatorX(camera.center[0]), mercatorY(camera.center[1])];
|
|
409
|
+
// Half the square in Mercator, at its latitude
|
|
410
|
+
const half = size / 2 / (40_075_016.686 * Math.cos((camera.center[1] * Math.PI) / 180));
|
|
411
|
+
const square = state.square;
|
|
412
|
+
// Off street level, or still in the square's middle: nothing to compute
|
|
413
|
+
if (camera.zoom < minZoom)
|
|
414
|
+
return;
|
|
415
|
+
if (square &&
|
|
416
|
+
Math.abs(x - (square[0] + square[2]) / 2) < half / 3 &&
|
|
417
|
+
Math.abs(y - (square[1] + square[3]) / 2) < half / 3) {
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
const { key, maxzoom } = await renderer;
|
|
421
|
+
const bounds = [x - half, y - half, x + half, y + half];
|
|
422
|
+
const n = 2 ** maxzoom;
|
|
423
|
+
const tiles = [];
|
|
424
|
+
for (let ty = Math.floor(bounds[1] * n); ty <= Math.floor(bounds[3] * n); ty++) {
|
|
425
|
+
for (let tx = Math.floor(bounds[0] * n); tx <= Math.floor(bounds[2] * n); tx++)
|
|
426
|
+
tiles.push(tx, ty);
|
|
427
|
+
}
|
|
428
|
+
const sides = Math.max(8, Math.round(size / cell));
|
|
429
|
+
const work = {
|
|
430
|
+
type: 'street',
|
|
431
|
+
key,
|
|
432
|
+
module: this.map.gridUrl,
|
|
433
|
+
z: maxzoom,
|
|
434
|
+
tiles,
|
|
435
|
+
bounds,
|
|
436
|
+
width: sides,
|
|
437
|
+
height: sides,
|
|
438
|
+
iterations,
|
|
439
|
+
};
|
|
440
|
+
const asked = ++state.asked;
|
|
441
|
+
held.forEach((c) => {
|
|
442
|
+
c.abort();
|
|
443
|
+
});
|
|
444
|
+
held.clear();
|
|
445
|
+
const controller = new AbortController();
|
|
446
|
+
held.set('square', controller);
|
|
447
|
+
state.square = bounds;
|
|
448
|
+
const answer = await this.request(maxzoom, Math.floor(x * n), Math.floor(y * n), work, controller.signal).catch((error) => ({ error }));
|
|
449
|
+
if (asked !== state.asked || controller.signal.aborted)
|
|
450
|
+
return;
|
|
451
|
+
// Failed (a tile or the grid module not fetched, a worker of an older build): the square asked for again later,
|
|
452
|
+
// a while after each failure
|
|
453
|
+
if (!('street' in answer)) {
|
|
454
|
+
console.warn("isard: the streets' wind not computed, tried again", 'error' in answer ? answer.error : answer);
|
|
455
|
+
state.square = undefined;
|
|
456
|
+
clearTimeout(state.retry);
|
|
457
|
+
state.retry = window.setTimeout(() => {
|
|
458
|
+
update().catch(() => undefined);
|
|
459
|
+
}, state.wait);
|
|
460
|
+
state.wait = Math.min(state.wait * 2, 30_000);
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
state.wait = 1000;
|
|
464
|
+
state.flows = answer.street;
|
|
465
|
+
state.grid = {
|
|
466
|
+
width: sides,
|
|
467
|
+
height: sides,
|
|
468
|
+
bounds: [longitude(bounds[0]), latitude(bounds[3]), longitude(bounds[2]), latitude(bounds[1])],
|
|
469
|
+
};
|
|
470
|
+
send();
|
|
471
|
+
first.resolve?.();
|
|
472
|
+
};
|
|
473
|
+
renderer.catch(() => undefined);
|
|
474
|
+
this.layers.push(layer);
|
|
475
|
+
this.followView(update, held, () => undefined);
|
|
476
|
+
this.cleanup.push(() => {
|
|
477
|
+
clearTimeout(state.retry);
|
|
478
|
+
});
|
|
479
|
+
return Object.assign(layer, {
|
|
480
|
+
computed,
|
|
481
|
+
setWind: (wind) => {
|
|
482
|
+
state.wind = wind;
|
|
483
|
+
send();
|
|
484
|
+
},
|
|
485
|
+
});
|
|
486
|
+
}
|
|
363
487
|
/**
|
|
364
488
|
* Trees on the style's woods, forests and grass (or the fill layers `layers` keeps) as 3D models, from street level
|
|
365
489
|
* (`minZoom`, default 15): the workers scatter them over the polygons of the vector tiles around the view, `spacing`
|