isard 1.2.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.
Files changed (119) hide show
  1. package/CHANGELOG.md +124 -0
  2. package/README.md +46 -39
  3. package/dist/basemap/basemaps.js +1 -0
  4. package/dist/basemap/basemaps.js.map +1 -1
  5. package/dist/basemap/pmtileslayer.d.ts +33 -0
  6. package/dist/basemap/pmtileslayer.js +97 -0
  7. package/dist/basemap/pmtileslayer.js.map +1 -0
  8. package/dist/basemap/trees.d.ts +26 -0
  9. package/dist/basemap/trees.js +141 -0
  10. package/dist/basemap/trees.js.map +1 -0
  11. package/dist/basemap/vectorstyle.d.ts +10 -0
  12. package/dist/basemap/vectorstyle.js +35 -0
  13. package/dist/basemap/vectorstyle.js.map +1 -1
  14. package/dist/basemap/vectortiles.d.ts +93 -44
  15. package/dist/basemap/vectortiles.js +511 -303
  16. package/dist/basemap/vectortiles.js.map +1 -1
  17. package/dist/basemap/workerpool.d.ts +17 -0
  18. package/dist/basemap/workerpool.js +203 -0
  19. package/dist/basemap/workerpool.js.map +1 -0
  20. package/dist/basemap.worker.js +2 -2
  21. package/dist/basemap.worker.js.map +18 -8
  22. package/dist/bind/layers.js +10 -0
  23. package/dist/bind/layers.js.map +1 -1
  24. package/dist/decoder.worker.js +4 -4
  25. package/dist/decoder.worker.js.map +3 -3
  26. package/dist/engine-loader.d.ts +8 -1
  27. package/dist/engine-loader.js +20 -1
  28. package/dist/engine-loader.js.map +1 -1
  29. package/dist/geoplateforme.d.ts +16 -0
  30. package/dist/geoplateforme.js +23 -0
  31. package/dist/geoplateforme.js.map +1 -1
  32. package/dist/grid-module.d.ts +44 -0
  33. package/dist/grid-module.js +63 -0
  34. package/dist/grid-module.js.map +1 -0
  35. package/dist/grids.d.ts +51 -0
  36. package/dist/grids.js +88 -0
  37. package/dist/grids.js.map +1 -0
  38. package/dist/index.d.ts +14 -4
  39. package/dist/index.js +11 -2
  40. package/dist/index.js.map +1 -1
  41. package/dist/isard_bg.wasm +0 -0
  42. package/dist/isard_core_bg.wasm +0 -0
  43. package/dist/isard_grid_bg.wasm +0 -0
  44. package/dist/isard_tess_bg.wasm +0 -0
  45. package/dist/layers/flow.d.ts +111 -0
  46. package/dist/layers/flow.js +222 -0
  47. package/dist/layers/flow.js.map +1 -0
  48. package/dist/layers/layers.js +9 -0
  49. package/dist/layers/layers.js.map +1 -1
  50. package/dist/layers/symbols.d.ts +2 -0
  51. package/dist/layers/symbols.js +2 -1
  52. package/dist/layers/symbols.js.map +1 -1
  53. package/dist/layers/vectors.d.ts +60 -0
  54. package/dist/layers/vectors.js +130 -0
  55. package/dist/layers/vectors.js.map +1 -0
  56. package/dist/map/frame-loop.d.ts +3 -0
  57. package/dist/map/frame-loop.js +6 -0
  58. package/dist/map/frame-loop.js.map +1 -1
  59. package/dist/map/layer-spec.d.ts +4 -1
  60. package/dist/map/layer-spec.js +2 -0
  61. package/dist/map/layer-spec.js.map +1 -1
  62. package/dist/map/raster-source.js +1 -1
  63. package/dist/map/raster-source.js.map +1 -1
  64. package/dist/map/workers.d.ts +5 -1
  65. package/dist/map/workers.js +36 -1
  66. package/dist/map/workers.js.map +1 -1
  67. package/dist/map.d.ts +35 -6
  68. package/dist/map.js +83 -11
  69. package/dist/map.js.map +1 -1
  70. package/dist/react/IsardMap.js +1 -0
  71. package/dist/react/IsardMap.js.map +1 -1
  72. package/dist/react/Layer.d.ts +2 -0
  73. package/dist/react/Layer.js.map +1 -1
  74. package/dist/react/Swipe.d.ts +11 -4
  75. package/dist/react/Swipe.js +26 -15
  76. package/dist/react/Swipe.js.map +1 -1
  77. package/dist/react/VectorGeometry.d.ts +35 -0
  78. package/dist/react/VectorGeometry.js +82 -0
  79. package/dist/react/VectorGeometry.js.map +1 -0
  80. package/dist/react/context.d.ts +2 -1
  81. package/dist/react/context.js.map +1 -1
  82. package/dist/react/index.d.ts +2 -1
  83. package/dist/react/index.js +2 -1
  84. package/dist/react/index.js.map +1 -1
  85. package/dist/types.d.ts +30 -3
  86. package/dist/types.js.map +1 -1
  87. package/dist/ui/minimap.d.ts +25 -0
  88. package/dist/ui/minimap.js +90 -0
  89. package/dist/ui/minimap.js.map +1 -0
  90. package/dist/ui/scale.d.ts +19 -0
  91. package/dist/ui/scale.js +44 -0
  92. package/dist/ui/scale.js.map +1 -0
  93. package/dist/ui/search.d.ts +25 -0
  94. package/dist/ui/search.js +119 -0
  95. package/dist/ui/search.js.map +1 -0
  96. package/dist/ui/style.js +17 -0
  97. package/dist/ui/style.js.map +1 -1
  98. package/dist/util/sun.d.ts +11 -0
  99. package/dist/util/sun.js +26 -0
  100. package/dist/util/sun.js.map +1 -0
  101. package/dist/wasm/isard.d.ts +124 -7
  102. package/dist/wasm/isard.js +319 -8
  103. package/dist/wasm/isard_core.d.ts +124 -7
  104. package/dist/wasm/isard_core.js +319 -8
  105. package/dist/wasm/isard_grid.d.ts +101 -0
  106. package/dist/wasm/isard_grid.js +375 -0
  107. package/dist/wasm/isard_tess.d.ts +109 -0
  108. package/dist/wasm/isard_tess.js +398 -0
  109. package/dist/workers/basemap/geometry.d.ts +72 -0
  110. package/dist/workers/basemap/labels.d.ts +8 -0
  111. package/dist/workers/basemap/raster.d.ts +10 -0
  112. package/dist/workers/basemap/street.d.ts +19 -0
  113. package/dist/workers/basemap/tiles.d.ts +65 -0
  114. package/dist/workers/basemap/trees.d.ts +19 -0
  115. package/dist/workers/basemap.worker.d.ts +147 -22
  116. package/dist/workers/basemap.worker.js +93 -553
  117. package/dist/workers/basemap.worker.js.map +1 -1
  118. package/package.json +6 -4
  119. package/vite.js +11 -2
package/dist/index.d.ts CHANGED
@@ -14,9 +14,11 @@
14
14
  export { OPENFREEMAP_ATTRIBUTION, TERRAIN_TILES_ATTRIBUTION } from './basemap/basemaps.ts';
15
15
  export type { TileGrid, TileMatrix } from './basemap/grid.ts';
16
16
  export { PMTiles, type PmtilesEntry, type PmtilesHeader, type PmtilesTileType, tileIdToZxy, zxyToTileId, } from './basemap/pmtiles.ts';
17
+ export { basicStyle, type PmtilesOptions } from './basemap/pmtileslayer.ts';
18
+ export { type TreeColors, treeModel } from './basemap/trees.ts';
17
19
  export type { CandidateIcon, CandidateOverlap, Json, LabelCandidate, RasterSource, Style, StyleLayer, } from './basemap/vectorstyle.ts';
18
- export type { PmtilesOptions, VectorTilesOptions } from './basemap/vectortiles.ts';
19
- export { basicStyle, OPENFREEMAP_STYLES, PLAN_STYLES, VectorTiles } from './basemap/vectortiles.ts';
20
+ export type { VectorTilesOptions } from './basemap/vectortiles.ts';
21
+ export { OPENFREEMAP_STYLES, PLAN_STYLES, VectorTiles } from './basemap/vectortiles.ts';
20
22
  export { parseWmtsGrid, wmtsGrid } from './basemap/wmts.ts';
21
23
  export { type Crs, type CrsLike, getCrs, lambertConformalConic, registerCrs, reproject, transverseMercator, } from './data/crs.ts';
22
24
  export { parseGpx } from './data/gpx.ts';
@@ -24,11 +26,14 @@ export { parseKml } from './data/kml.ts';
24
26
  export { parseTopoJson, type TopoGeometry, type Topology } from './data/topojson.ts';
25
27
  export type { DecoderUrls } from './engine/decoders.ts';
26
28
  export { setRequestHeaders } from './engine/requests.ts';
27
- export { BATIMENTS_3D_URL, type BdTopoBuilding, batiments3d, bdtopoBuildings, geoplateformeWfs, geoplateformeWms, geoplateformeWmts, IGN_ATTRIBUTION, IGN_PRESETS, type IgnPreset, } from './geoplateforme.ts';
29
+ export { BATIMENTS_3D_URL, type BdTopoBuilding, batiments3d, bdtopoBuildings, type GeocodedPlace, geoplateformeGeocode, geoplateformeWfs, geoplateformeWms, geoplateformeWmts, IGN_ATTRIBUTION, IGN_PRESETS, type IgnPreset, } from './geoplateforme.ts';
30
+ export { type Grid, windAt, windFields } from './grids.ts';
28
31
  export type { ExtrusionOptions, ExtrusionStyle, PolygonArrays, TileKey } from './layers/extrusions.ts';
29
32
  export { ExtrusionLayer } from './layers/extrusions.ts';
30
33
  export type { CircleOptions, CircleStyle, Feature, FeatureCollection, FeatureData, FeatureLayerStyle, FillOptions, FillStyle, Geometry, LineOptions, LineStyle, Placement, Position, Properties, Ramp, Size, Stops, Styled, } from './layers/features.ts';
31
34
  export { CircleLayer, FeatureLayer, FillLayer, interpolate, LineLayer, parseColor } from './layers/features.ts';
35
+ export type { FlowField, FlowOptions, FlowStyle, TimedField } from './layers/flow.ts';
36
+ export { FlowLayer, flowFieldFromImage, flowFieldsFromImage } from './layers/flow.ts';
32
37
  export type { TextPart } from './layers/glyphs.ts';
33
38
  export { DEFAULT_FONT, GLYPHS_URL } from './layers/glyphs.ts';
34
39
  export type { ImageOptions, Pixels, SpriteIcon } from './layers/icons.ts';
@@ -39,9 +44,14 @@ export type { ClassStyle, PointCloudOptions, PointCloudStyle, PointColor } from
39
44
  export { CLASSIFICATIONS, ELEVATION_RAMP, PointCloudLayer } from './layers/pointclouds.ts';
40
45
  export type { Overlap, SymbolOptions, SymbolStyle } from './layers/symbols.ts';
41
46
  export { SymbolLayer } from './layers/symbols.ts';
47
+ export { type VectorFeature, VectorLayer, type VectorSublayer } from './layers/vectors.ts';
42
48
  export type { HoverPicking } from './map/hover-pick.ts';
43
49
  export { GeoMap, type LayerSpec } from './map.ts';
44
- export type { AnimationOptions, AtmosphereOptions, Basemap, BasemapName, BodyOptions, CelestialBody, FeatureHit, FirstPersonView, FlyToOptions, GeoMapOptions, LoadingMode, LonLat, MapEvents, MapHoverFeatureEvent, MapPointerEvent, NetworkOptions, NetworkStyle, Point, Projection, RasterGrid, RasterOptions, RasterStyle, SwipeOptions, TerrainEncoding, TerrainModel, TerrainOptions, Tiles3dOptions, Tiles3dStyle, ViewshedOptions, } from './types.ts';
50
+ export type { AnimationOptions, AtmosphereOptions, Basemap, BasemapName, BodyOptions, CelestialBody, FeatureHit, FirstPersonView, FlyToOptions, GeoMapOptions, LoadingMode, LonLat, MapEvents, MapHoverFeatureEvent, MapPointerEvent, NetworkOptions, NetworkStyle, Point, Projection, RasterGrid, RasterOptions, RasterStyle, SunOptions, SwipeOptions, TerrainEncoding, TerrainModel, TerrainOptions, Tiles3dOptions, Tiles3dStyle, ViewshedOptions, } from './types.ts';
45
51
  export { AttributionControl, type AttributionOptions, type Control, type ControlPosition, NavigationControl, type NavigationOptions, } from './ui/controls.ts';
52
+ export { MinimapControl, type MinimapOptions } from './ui/minimap.ts';
46
53
  export { type Anchor, Marker, type MarkerEvent, type MarkerEvents, type MarkerOptions, Panel, type PanelOptions, Popup, type PopupEvents, type PopupOptions, } from './ui/overlay.ts';
54
+ export { lengthLabel, roundLength, ScaleControl, type ScaleOptions } from './ui/scale.ts';
55
+ export { SearchControl, type SearchOptions } from './ui/search.ts';
56
+ export { sunPosition } from './util/sun.ts';
47
57
  export type { Camera, NetworkStats, PickHit, PickResult, PointCloudInfo, PointCloudPoint, PointCloudStats, RasterLayerStats, Tiles3dStats, } from './wasm/isard.js';
package/dist/index.js CHANGED
@@ -13,22 +13,31 @@
13
13
  */
14
14
  export { OPENFREEMAP_ATTRIBUTION, TERRAIN_TILES_ATTRIBUTION } from "./basemap/basemaps.js";
15
15
  export { PMTiles, tileIdToZxy, zxyToTileId, } from "./basemap/pmtiles.js";
16
- export { basicStyle, OPENFREEMAP_STYLES, PLAN_STYLES, VectorTiles } from "./basemap/vectortiles.js";
16
+ export { basicStyle } from "./basemap/pmtileslayer.js";
17
+ export { treeModel } from "./basemap/trees.js";
18
+ export { OPENFREEMAP_STYLES, PLAN_STYLES, VectorTiles } from "./basemap/vectortiles.js";
17
19
  export { parseWmtsGrid, wmtsGrid } from "./basemap/wmts.js";
18
20
  export { getCrs, lambertConformalConic, registerCrs, reproject, transverseMercator, } from "./data/crs.js";
19
21
  export { parseGpx } from "./data/gpx.js";
20
22
  export { parseKml } from "./data/kml.js";
21
23
  export { parseTopoJson } from "./data/topojson.js";
22
24
  export { setRequestHeaders } from "./engine/requests.js";
23
- export { BATIMENTS_3D_URL, batiments3d, bdtopoBuildings, geoplateformeWfs, geoplateformeWms, geoplateformeWmts, IGN_ATTRIBUTION, IGN_PRESETS, } from "./geoplateforme.js";
25
+ export { BATIMENTS_3D_URL, batiments3d, bdtopoBuildings, geoplateformeGeocode, geoplateformeWfs, geoplateformeWms, geoplateformeWmts, IGN_ATTRIBUTION, IGN_PRESETS, } from "./geoplateforme.js";
26
+ export { windAt, windFields } from "./grids.js";
24
27
  export { ExtrusionLayer } from "./layers/extrusions.js";
25
28
  export { CircleLayer, FeatureLayer, FillLayer, interpolate, LineLayer, parseColor } from "./layers/features.js";
29
+ export { FlowLayer, flowFieldFromImage, flowFieldsFromImage } from "./layers/flow.js";
26
30
  export { DEFAULT_FONT, GLYPHS_URL } from "./layers/glyphs.js";
27
31
  export { CustomLayer, GroundLayer, Layer, ModelLayer, NetworkLayer, RasterLayer, Tiles3dLayer, } from "./layers/layers.js";
28
32
  export { Panoramas } from "./layers/panoramas.js";
29
33
  export { CLASSIFICATIONS, ELEVATION_RAMP, PointCloudLayer } from "./layers/pointclouds.js";
30
34
  export { SymbolLayer } from "./layers/symbols.js";
35
+ export { VectorLayer } from "./layers/vectors.js";
31
36
  export { GeoMap } from "./map.js";
32
37
  export { AttributionControl, NavigationControl, } from "./ui/controls.js";
38
+ export { MinimapControl } from "./ui/minimap.js";
33
39
  export { Marker, Panel, Popup, } from "./ui/overlay.js";
40
+ export { lengthLabel, roundLength, ScaleControl } from "./ui/scale.js";
41
+ export { SearchControl } from "./ui/search.js";
42
+ export { sunPosition } from "./util/sun.js";
34
43
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,uBAAuB,EAAE,yBAAyB,EAAE,MAAM,uBAAuB,CAAC;AAE3F,OAAO,EACL,OAAO,EAIP,WAAW,EACX,WAAW,GACZ,MAAM,sBAAsB,CAAC;AAW9B,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACpG,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAGL,MAAM,EACN,qBAAqB,EACrB,WAAW,EACX,SAAS,EACT,kBAAkB,GACnB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,aAAa,EAAoC,MAAM,oBAAoB,CAAC;AAErF,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EACL,gBAAgB,EAEhB,WAAW,EACX,eAAe,EACf,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EACjB,eAAe,EACf,WAAW,GAEZ,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAqBxD,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEhH,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAU9D,OAAO,EACL,WAAW,EACX,WAAW,EACX,KAAK,EACL,UAAU,EACV,YAAY,EACZ,WAAW,EACX,YAAY,GACb,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAuC,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAEvF,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAE3F,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAElD,OAAO,EAAE,MAAM,EAAkB,MAAM,UAAU,CAAC;AAgClD,OAAO,EACL,kBAAkB,EAIlB,iBAAiB,GAElB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAEL,MAAM,EAIN,KAAK,EAEL,KAAK,GAGN,MAAM,iBAAiB,CAAC","sourcesContent":["/**\n * isard: a WebGL2 map engine for large, interactive data layers, labels and 3D Tiles.\n *\n * ```ts\n * import { GeoMap } from 'isard';\n * const map = await GeoMap.create(container, { center: [2.4246, 48.8457], zoom: 13 });\n * const network = map.addNetwork({ nodes, edges, max: 1800, contour: 300 });\n * map.on('hover', ({ lonLat }) => lonLat && network.setValues(travelTimes(lonLat)));\n * map.addTiles3d({ url: 'https://…/tileset.json' });\n * map.addControl(new NavigationControl());\n * new Marker().setLonLat([2.4246, 48.8457]).setPopup(new Popup({ offset: [0, -34] }).setText('Here')).addTo(map);\n * ```\n */\nexport { OPENFREEMAP_ATTRIBUTION, TERRAIN_TILES_ATTRIBUTION } from './basemap/basemaps.ts';\nexport type { TileGrid, TileMatrix } from './basemap/grid.ts';\nexport {\n PMTiles,\n type PmtilesEntry,\n type PmtilesHeader,\n type PmtilesTileType,\n tileIdToZxy,\n zxyToTileId,\n} from './basemap/pmtiles.ts';\nexport type {\n CandidateIcon,\n CandidateOverlap,\n Json,\n LabelCandidate,\n RasterSource,\n Style,\n StyleLayer,\n} from './basemap/vectorstyle.ts';\nexport type { PmtilesOptions, VectorTilesOptions } from './basemap/vectortiles.ts';\nexport { basicStyle, OPENFREEMAP_STYLES, PLAN_STYLES, VectorTiles } from './basemap/vectortiles.ts';\nexport { parseWmtsGrid, wmtsGrid } from './basemap/wmts.ts';\nexport {\n type Crs,\n type CrsLike,\n getCrs,\n lambertConformalConic,\n registerCrs,\n reproject,\n transverseMercator,\n} from './data/crs.ts';\nexport { parseGpx } from './data/gpx.ts';\nexport { parseKml } from './data/kml.ts';\nexport { parseTopoJson, type TopoGeometry, type Topology } from './data/topojson.ts';\nexport type { DecoderUrls } from './engine/decoders.ts';\nexport { setRequestHeaders } from './engine/requests.ts';\nexport {\n BATIMENTS_3D_URL,\n type BdTopoBuilding,\n batiments3d,\n bdtopoBuildings,\n geoplateformeWfs,\n geoplateformeWms,\n geoplateformeWmts,\n IGN_ATTRIBUTION,\n IGN_PRESETS,\n type IgnPreset,\n} from './geoplateforme.ts';\nexport type { ExtrusionOptions, ExtrusionStyle, PolygonArrays, TileKey } from './layers/extrusions.ts';\nexport { ExtrusionLayer } from './layers/extrusions.ts';\nexport type {\n CircleOptions,\n CircleStyle,\n Feature,\n FeatureCollection,\n FeatureData,\n FeatureLayerStyle,\n FillOptions,\n FillStyle,\n Geometry,\n LineOptions,\n LineStyle,\n Placement,\n Position,\n Properties,\n Ramp,\n Size,\n Stops,\n Styled,\n} from './layers/features.ts';\nexport { CircleLayer, FeatureLayer, FillLayer, interpolate, LineLayer, parseColor } from './layers/features.ts';\nexport type { TextPart } from './layers/glyphs.ts';\nexport { DEFAULT_FONT, GLYPHS_URL } from './layers/glyphs.ts';\nexport type { ImageOptions, Pixels, SpriteIcon } from './layers/icons.ts';\nexport type {\n CustomFrame,\n CustomLayerInterface,\n GroundOptions,\n GroundTexture,\n ModelInstance,\n ModelOptions,\n} from './layers/layers.ts';\nexport {\n CustomLayer,\n GroundLayer,\n Layer,\n ModelLayer,\n NetworkLayer,\n RasterLayer,\n Tiles3dLayer,\n} from './layers/layers.ts';\nexport { type Panorama, type PanoramaOptions, Panoramas } from './layers/panoramas.ts';\nexport type { ClassStyle, PointCloudOptions, PointCloudStyle, PointColor } from './layers/pointclouds.ts';\nexport { CLASSIFICATIONS, ELEVATION_RAMP, PointCloudLayer } from './layers/pointclouds.ts';\nexport type { Overlap, SymbolOptions, SymbolStyle } from './layers/symbols.ts';\nexport { SymbolLayer } from './layers/symbols.ts';\nexport type { HoverPicking } from './map/hover-pick.ts';\nexport { GeoMap, type LayerSpec } from './map.ts';\nexport type {\n AnimationOptions,\n AtmosphereOptions,\n Basemap,\n BasemapName,\n BodyOptions,\n CelestialBody,\n FeatureHit,\n FirstPersonView,\n FlyToOptions,\n GeoMapOptions,\n LoadingMode,\n LonLat,\n MapEvents,\n MapHoverFeatureEvent,\n MapPointerEvent,\n NetworkOptions,\n NetworkStyle,\n Point,\n Projection,\n RasterGrid,\n RasterOptions,\n RasterStyle,\n SwipeOptions,\n TerrainEncoding,\n TerrainModel,\n TerrainOptions,\n Tiles3dOptions,\n Tiles3dStyle,\n ViewshedOptions,\n} from './types.ts';\nexport {\n AttributionControl,\n type AttributionOptions,\n type Control,\n type ControlPosition,\n NavigationControl,\n type NavigationOptions,\n} from './ui/controls.ts';\nexport {\n type Anchor,\n Marker,\n type MarkerEvent,\n type MarkerEvents,\n type MarkerOptions,\n Panel,\n type PanelOptions,\n Popup,\n type PopupEvents,\n type PopupOptions,\n} from './ui/overlay.ts';\nexport type {\n Camera,\n NetworkStats,\n PickHit,\n PickResult,\n PointCloudInfo,\n PointCloudPoint,\n PointCloudStats,\n RasterLayerStats,\n Tiles3dStats,\n} from './wasm/isard.js';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,uBAAuB,EAAE,yBAAyB,EAAE,MAAM,uBAAuB,CAAC;AAE3F,OAAO,EACL,OAAO,EAIP,WAAW,EACX,WAAW,GACZ,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,UAAU,EAAuB,MAAM,2BAA2B,CAAC;AAC5E,OAAO,EAAmB,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAWhE,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACxF,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAGL,MAAM,EACN,qBAAqB,EACrB,WAAW,EACX,SAAS,EACT,kBAAkB,GACnB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,aAAa,EAAoC,MAAM,oBAAoB,CAAC;AAErF,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EACL,gBAAgB,EAEhB,WAAW,EACX,eAAe,EAEf,oBAAoB,EACpB,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EACjB,eAAe,EACf,WAAW,GAEZ,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAa,MAAM,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE3D,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAqBxD,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEhH,OAAO,EAAE,SAAS,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAEtF,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAU9D,OAAO,EACL,WAAW,EACX,WAAW,EACX,KAAK,EACL,UAAU,EACV,YAAY,EACZ,WAAW,EACX,YAAY,GACb,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAuC,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAEvF,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAE3F,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAsB,WAAW,EAAuB,MAAM,qBAAqB,CAAC;AAE3F,OAAO,EAAE,MAAM,EAAkB,MAAM,UAAU,CAAC;AAiClD,OAAO,EACL,kBAAkB,EAIlB,iBAAiB,GAElB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,cAAc,EAAuB,MAAM,iBAAiB,CAAC;AACtE,OAAO,EAEL,MAAM,EAIN,KAAK,EAEL,KAAK,GAGN,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,YAAY,EAAqB,MAAM,eAAe,CAAC;AAC1F,OAAO,EAAE,aAAa,EAAsB,MAAM,gBAAgB,CAAC;AACnE,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC","sourcesContent":["/**\n * isard: a WebGL2 map engine for large, interactive data layers, labels and 3D Tiles.\n *\n * ```ts\n * import { GeoMap } from 'isard';\n * const map = await GeoMap.create(container, { center: [2.4246, 48.8457], zoom: 13 });\n * const network = map.addNetwork({ nodes, edges, max: 1800, contour: 300 });\n * map.on('hover', ({ lonLat }) => lonLat && network.setValues(travelTimes(lonLat)));\n * map.addTiles3d({ url: 'https://…/tileset.json' });\n * map.addControl(new NavigationControl());\n * new Marker().setLonLat([2.4246, 48.8457]).setPopup(new Popup({ offset: [0, -34] }).setText('Here')).addTo(map);\n * ```\n */\nexport { OPENFREEMAP_ATTRIBUTION, TERRAIN_TILES_ATTRIBUTION } from './basemap/basemaps.ts';\nexport type { TileGrid, TileMatrix } from './basemap/grid.ts';\nexport {\n PMTiles,\n type PmtilesEntry,\n type PmtilesHeader,\n type PmtilesTileType,\n tileIdToZxy,\n zxyToTileId,\n} from './basemap/pmtiles.ts';\nexport { basicStyle, type PmtilesOptions } from './basemap/pmtileslayer.ts';\nexport { type TreeColors, treeModel } from './basemap/trees.ts';\nexport type {\n CandidateIcon,\n CandidateOverlap,\n Json,\n LabelCandidate,\n RasterSource,\n Style,\n StyleLayer,\n} from './basemap/vectorstyle.ts';\nexport type { VectorTilesOptions } from './basemap/vectortiles.ts';\nexport { OPENFREEMAP_STYLES, PLAN_STYLES, VectorTiles } from './basemap/vectortiles.ts';\nexport { parseWmtsGrid, wmtsGrid } from './basemap/wmts.ts';\nexport {\n type Crs,\n type CrsLike,\n getCrs,\n lambertConformalConic,\n registerCrs,\n reproject,\n transverseMercator,\n} from './data/crs.ts';\nexport { parseGpx } from './data/gpx.ts';\nexport { parseKml } from './data/kml.ts';\nexport { parseTopoJson, type TopoGeometry, type Topology } from './data/topojson.ts';\nexport type { DecoderUrls } from './engine/decoders.ts';\nexport { setRequestHeaders } from './engine/requests.ts';\nexport {\n BATIMENTS_3D_URL,\n type BdTopoBuilding,\n batiments3d,\n bdtopoBuildings,\n type GeocodedPlace,\n geoplateformeGeocode,\n geoplateformeWfs,\n geoplateformeWms,\n geoplateformeWmts,\n IGN_ATTRIBUTION,\n IGN_PRESETS,\n type IgnPreset,\n} from './geoplateforme.ts';\nexport { type Grid, windAt, windFields } from './grids.ts';\nexport type { ExtrusionOptions, ExtrusionStyle, PolygonArrays, TileKey } from './layers/extrusions.ts';\nexport { ExtrusionLayer } from './layers/extrusions.ts';\nexport type {\n CircleOptions,\n CircleStyle,\n Feature,\n FeatureCollection,\n FeatureData,\n FeatureLayerStyle,\n FillOptions,\n FillStyle,\n Geometry,\n LineOptions,\n LineStyle,\n Placement,\n Position,\n Properties,\n Ramp,\n Size,\n Stops,\n Styled,\n} from './layers/features.ts';\nexport { CircleLayer, FeatureLayer, FillLayer, interpolate, LineLayer, parseColor } from './layers/features.ts';\nexport type { FlowField, FlowOptions, FlowStyle, TimedField } from './layers/flow.ts';\nexport { FlowLayer, flowFieldFromImage, flowFieldsFromImage } from './layers/flow.ts';\nexport type { TextPart } from './layers/glyphs.ts';\nexport { DEFAULT_FONT, GLYPHS_URL } from './layers/glyphs.ts';\nexport type { ImageOptions, Pixels, SpriteIcon } from './layers/icons.ts';\nexport type {\n CustomFrame,\n CustomLayerInterface,\n GroundOptions,\n GroundTexture,\n ModelInstance,\n ModelOptions,\n} from './layers/layers.ts';\nexport {\n CustomLayer,\n GroundLayer,\n Layer,\n ModelLayer,\n NetworkLayer,\n RasterLayer,\n Tiles3dLayer,\n} from './layers/layers.ts';\nexport { type Panorama, type PanoramaOptions, Panoramas } from './layers/panoramas.ts';\nexport type { ClassStyle, PointCloudOptions, PointCloudStyle, PointColor } from './layers/pointclouds.ts';\nexport { CLASSIFICATIONS, ELEVATION_RAMP, PointCloudLayer } from './layers/pointclouds.ts';\nexport type { Overlap, SymbolOptions, SymbolStyle } from './layers/symbols.ts';\nexport { SymbolLayer } from './layers/symbols.ts';\nexport { type VectorFeature, VectorLayer, type VectorSublayer } from './layers/vectors.ts';\nexport type { HoverPicking } from './map/hover-pick.ts';\nexport { GeoMap, type LayerSpec } from './map.ts';\nexport type {\n AnimationOptions,\n AtmosphereOptions,\n Basemap,\n BasemapName,\n BodyOptions,\n CelestialBody,\n FeatureHit,\n FirstPersonView,\n FlyToOptions,\n GeoMapOptions,\n LoadingMode,\n LonLat,\n MapEvents,\n MapHoverFeatureEvent,\n MapPointerEvent,\n NetworkOptions,\n NetworkStyle,\n Point,\n Projection,\n RasterGrid,\n RasterOptions,\n RasterStyle,\n SunOptions,\n SwipeOptions,\n TerrainEncoding,\n TerrainModel,\n TerrainOptions,\n Tiles3dOptions,\n Tiles3dStyle,\n ViewshedOptions,\n} from './types.ts';\nexport {\n AttributionControl,\n type AttributionOptions,\n type Control,\n type ControlPosition,\n NavigationControl,\n type NavigationOptions,\n} from './ui/controls.ts';\nexport { MinimapControl, type MinimapOptions } from './ui/minimap.ts';\nexport {\n type Anchor,\n Marker,\n type MarkerEvent,\n type MarkerEvents,\n type MarkerOptions,\n Panel,\n type PanelOptions,\n Popup,\n type PopupEvents,\n type PopupOptions,\n} from './ui/overlay.ts';\nexport { lengthLabel, roundLength, ScaleControl, type ScaleOptions } from './ui/scale.ts';\nexport { SearchControl, type SearchOptions } from './ui/search.ts';\nexport { sunPosition } from './util/sun.ts';\nexport type {\n Camera,\n NetworkStats,\n PickHit,\n PickResult,\n PointCloudInfo,\n PointCloudPoint,\n PointCloudStats,\n RasterLayerStats,\n Tiles3dStats,\n} from './wasm/isard.js';\n"]}
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Flow layers: particles carried by a field of velocities (a wind's east and north components on a grid of longitudes
3
+ * and latitudes, m/s), their trails drawn on the globe and the relief and animated, colored by speed: wind maps,
4
+ * currents. The engine moves them (`stepFlow`), one step per animation frame while the layer plays.
5
+ */
6
+ import { type Ramp } from './features.ts';
7
+ import { Layer } from './layers.ts';
8
+ /**
9
+ * A field of velocities: `width` × `height` points, from west to east and from north to south (row after row), the
10
+ * east (`u`) and north (`v`) components at each (m/s; not a number: none there), the corner points at `bounds` (west,
11
+ * south, east, north, degrees). A last column one step short of 360° goes round the world (a global model's grid)
12
+ */
13
+ export interface FlowField {
14
+ width: number;
15
+ height: number;
16
+ u: Float32Array | readonly number[];
17
+ v: Float32Array | readonly number[];
18
+ bounds: [west: number, south: number, east: number, north: number];
19
+ }
20
+ export interface FlowStyle {
21
+ /** The colors of the speeds (m/s), linear between them, up to 8 (default from calm blue to storm red) */
22
+ colors: Ramp;
23
+ /** Width of a trail, CSS pixels (default 1.5) */
24
+ width: number;
25
+ /** 0 to 1 (default 0.9) */
26
+ opacity: number;
27
+ /** Metres over the ground (default 0) */
28
+ height: number;
29
+ /** Speed on screen: pixels a second for 10 m/s, whatever the zoom (default 60) */
30
+ speed: number;
31
+ /** Particles moving for each 100 × 100 CSS pixels of the view, the screen as dense at any zoom (default 60; at most
32
+ * `particles`) */
33
+ density: number;
34
+ }
35
+ /** A field at a time: one of a model's hours */
36
+ export interface TimedField {
37
+ time: Date | number;
38
+ field: FlowField;
39
+ }
40
+ export interface FlowOptions extends Partial<FlowStyle> {
41
+ /** The field; or `fields`, at their times, mixed at `time` */
42
+ field?: FlowField;
43
+ /** Fields at their times, on one grid (a model's hours): the layer's field at `time` the mix of the two around it */
44
+ fields?: readonly TimedField[];
45
+ /** The time the fields are mixed at (default the first one's) */
46
+ time?: Date | number;
47
+ /** Particles moving, rounded up to a multiple of 4096 (default 16384) */
48
+ particles?: number;
49
+ /** Positions of a trail, one each 60th of a second (default 24): longer trails, more to draw */
50
+ trail?: number;
51
+ /** Moving from the start (default true); `play`, `pause` and `step` after */
52
+ playing?: boolean;
53
+ before?: Layer;
54
+ }
55
+ export declare class FlowLayer extends Layer {
56
+ #private;
57
+ /** The fields by time (one at time 0 for a single field), the time shown, the two sent to the engine */
58
+ private fields;
59
+ private time;
60
+ private sent;
61
+ private style;
62
+ private readonly particles;
63
+ private readonly trail;
64
+ /** The animation frame asked for, and the time of the last step (ms) */
65
+ private frame;
66
+ private last;
67
+ /** Whether the particles move */
68
+ get playing(): boolean;
69
+ /** The particles move, a step each animation frame */
70
+ play(): void;
71
+ /** The particles stop where they are (the map no longer drawn for them) */
72
+ pause(): void;
73
+ /** Moving or not (`play`, `pause`) */
74
+ setPlaying(playing: boolean): void;
75
+ /** Moves the particles by `seconds` of the page's time, at once (a still image after so many steps; paused) */
76
+ step(seconds?: number): void;
77
+ /** A new field (on another grid, the trails start again) */
78
+ setField(field: FlowField): void;
79
+ /** New fields at their times (a model's hours), mixed at `time` (default the one shown) */
80
+ setFields(fields: readonly TimedField[], time?: Date | number): void;
81
+ /** The time the fields are mixed at: between two of the model's hours, the field goes linearly from one to the
82
+ * other (before the first and after the last, theirs); the particles go on */
83
+ setTime(time: Date | number): void;
84
+ setStyle(style: Partial<FlowStyle>): void;
85
+ /** One step an animation frame while it plays, by the time since the last */
86
+ private loop;
87
+ /** The two fields around the time and how far between them, the fields sent only when they change */
88
+ private sendField;
89
+ private sendStyle;
90
+ remove(): void;
91
+ }
92
+ /**
93
+ * A field from an image (PNG, as wind maps ship them): the east component in red, the north in green, each scaled
94
+ * from 0-255 to its range (`u`, `v`, m/s), none where the image is transparent; its pixels the grid's points over
95
+ * `bounds`
96
+ */
97
+ export declare const flowFieldFromImage: (url: string | URL, options: {
98
+ bounds: FlowField["bounds"];
99
+ u: [min: number, max: number];
100
+ v: [min: number, max: number];
101
+ }, init?: RequestInit) => Promise<FlowField>;
102
+ /**
103
+ * The fields of an image holding `count` of them one under the other (a model's hours, the first at the top), each
104
+ * read as `flowFieldFromImage` reads one
105
+ */
106
+ export declare const flowFieldsFromImage: (url: string | URL, options: {
107
+ bounds: FlowField["bounds"];
108
+ u: [min: number, max: number];
109
+ v: [min: number, max: number];
110
+ count: number;
111
+ }, init?: RequestInit) => Promise<FlowField[]>;
@@ -0,0 +1,222 @@
1
+ /**
2
+ * Flow layers: particles carried by a field of velocities (a wind's east and north components on a grid of longitudes
3
+ * and latitudes, m/s), their trails drawn on the globe and the relief and animated, colored by speed: wind maps,
4
+ * currents. The engine moves them (`stepFlow`), one step per animation frame while the layer plays.
5
+ */
6
+ import { parseColor } from "./features.js";
7
+ import { Layer } from "./layers.js";
8
+ /** Wind speeds (m/s) from calm to storm */
9
+ const COLORS = [
10
+ [0, '#3a6fb0'],
11
+ [4, '#4fa3c7'],
12
+ [8, '#7fc79a'],
13
+ [12, '#d6e27a'],
14
+ [16, '#f6c14e'],
15
+ [22, '#ef7d3c'],
16
+ [30, '#d2374a'],
17
+ [40, '#8e1f5c'],
18
+ ];
19
+ export class FlowLayer extends Layer {
20
+ /** The fields by time (one at time 0 for a single field), the time shown, the two sent to the engine */
21
+ fields;
22
+ time;
23
+ sent;
24
+ style;
25
+ particles;
26
+ trail;
27
+ #playing;
28
+ /** The animation frame asked for, and the time of the last step (ms) */
29
+ frame = 0;
30
+ last;
31
+ /** @internal */
32
+ constructor(map, options) {
33
+ super(map);
34
+ this.fields = timed(options);
35
+ this.time = options.time === undefined ? (this.fields[0]?.time ?? 0) : Number(options.time);
36
+ this.particles = options.particles ?? 16384;
37
+ this.trail = options.trail ?? 24;
38
+ this.style = {
39
+ colors: options.colors ?? COLORS,
40
+ width: options.width ?? 1.5,
41
+ opacity: options.opacity ?? 0.9,
42
+ height: options.height ?? 0,
43
+ speed: options.speed ?? 60,
44
+ density: options.density ?? 60,
45
+ };
46
+ this.#playing = options.playing ?? true;
47
+ if (this.#playing)
48
+ this.loop();
49
+ }
50
+ /** Whether the particles move */
51
+ get playing() {
52
+ return this.#playing;
53
+ }
54
+ /** The particles move, a step each animation frame */
55
+ play() {
56
+ if (this.#playing)
57
+ return;
58
+ this.#playing = true;
59
+ this.last = undefined;
60
+ this.loop();
61
+ }
62
+ /** The particles stop where they are (the map no longer drawn for them) */
63
+ pause() {
64
+ this.#playing = false;
65
+ cancelAnimationFrame(this.frame);
66
+ this.frame = 0;
67
+ }
68
+ /** Moving or not (`play`, `pause`) */
69
+ setPlaying(playing) {
70
+ if (playing)
71
+ this.play();
72
+ else
73
+ this.pause();
74
+ }
75
+ /** Moves the particles by `seconds` of the page's time, at once (a still image after so many steps; paused) */
76
+ step(seconds = 1 / 60) {
77
+ if (this.id < 0)
78
+ return;
79
+ this.map.engine.stepFlow(this.id, seconds);
80
+ this.map.requestRender();
81
+ }
82
+ /** A new field (on another grid, the trails start again) */
83
+ setField(field) {
84
+ this.setFields([{ time: 0, field }], 0);
85
+ }
86
+ /** New fields at their times (a model's hours), mixed at `time` (default the one shown) */
87
+ setFields(fields, time) {
88
+ this.fields = timed({ fields });
89
+ if (time !== undefined)
90
+ this.time = Number(time);
91
+ this.sent = undefined;
92
+ if (this.id >= 0)
93
+ this.sendField(this.map.engine, this.id);
94
+ this.map.requestRender();
95
+ }
96
+ /** The time the fields are mixed at: between two of the model's hours, the field goes linearly from one to the
97
+ * other (before the first and after the last, theirs); the particles go on */
98
+ setTime(time) {
99
+ this.time = Number(time);
100
+ if (this.id >= 0)
101
+ this.sendField(this.map.engine, this.id);
102
+ this.map.requestRender();
103
+ }
104
+ setStyle(style) {
105
+ this.style = { ...this.style, ...style };
106
+ if (this.id >= 0)
107
+ this.sendStyle(this.map.engine, this.id);
108
+ this.map.requestRender();
109
+ }
110
+ /** One step an animation frame while it plays, by the time since the last */
111
+ loop() {
112
+ const tick = (now) => {
113
+ if (!this.#playing)
114
+ return;
115
+ const seconds = this.last === undefined ? 1 / 60 : (now - this.last) / 1000;
116
+ this.last = now;
117
+ this.step(seconds);
118
+ this.frame = requestAnimationFrame(tick);
119
+ };
120
+ cancelAnimationFrame(this.frame);
121
+ this.frame = requestAnimationFrame(tick);
122
+ }
123
+ /** The two fields around the time and how far between them, the fields sent only when they change */
124
+ sendField(engine, id) {
125
+ const { fields, time } = this;
126
+ const [a, b] = around(fields, time);
127
+ if (!a)
128
+ return;
129
+ if (this.sent?.[0] !== a.field || this.sent?.[1] !== b?.field) {
130
+ const { width, height, u, v, bounds } = a.field;
131
+ engine.setFlowField(id, width, height, Float32Array.from(u), Float32Array.from(v), Float64Array.from(bounds));
132
+ engine.setFlowNext(id, Float32Array.from(b?.field.u ?? []), Float32Array.from(b?.field.v ?? []));
133
+ this.sent = [a.field, b?.field];
134
+ }
135
+ engine.setFlowBlend(id, b ? (time - a.time) / (b.time - a.time) : 0);
136
+ }
137
+ sendStyle(engine, id) {
138
+ const { colors, width, opacity, height, speed, density } = this.style;
139
+ const stops = colors.slice(0, 8);
140
+ engine.setFlowStyle(id, Float32Array.from(stops, ([s]) => s), Float32Array.from(stops.flatMap(([, c]) => parseColor(c))), width, opacity, height, speed, density);
141
+ }
142
+ /** @internal */
143
+ create(engine) {
144
+ const id = engine.addFlow(this.particles, this.trail);
145
+ // A new engine: everything sent again
146
+ this.sent = undefined;
147
+ this.sendField(engine, id);
148
+ this.sendStyle(engine, id);
149
+ return id;
150
+ }
151
+ remove() {
152
+ super.remove();
153
+ this.dispose();
154
+ }
155
+ /** @internal */
156
+ dispose() {
157
+ this.pause();
158
+ }
159
+ }
160
+ /** The fields around a time: the one before and the one after, or the first or last alone outside them */
161
+ const around = (fields, time) => {
162
+ const after = fields.findIndex((f) => f.time > time);
163
+ if (after < 0)
164
+ return [fields.at(-1), undefined];
165
+ if (after === 0)
166
+ return [fields[0], undefined];
167
+ return [fields[after - 1], fields[after]];
168
+ };
169
+ /** The fields of the options by time: `fields` sorted, or `field` alone (at time 0) */
170
+ const timed = ({ field, fields }) => {
171
+ if (fields?.length)
172
+ return fields.map((f) => ({ time: Number(f.time), field: f.field })).sort((a, b) => a.time - b.time);
173
+ if (field)
174
+ return [{ time: 0, field }];
175
+ throw new Error('isard: a flow layer needs a field or fields');
176
+ };
177
+ /**
178
+ * A field from an image (PNG, as wind maps ship them): the east component in red, the north in green, each scaled
179
+ * from 0-255 to its range (`u`, `v`, m/s), none where the image is transparent; its pixels the grid's points over
180
+ * `bounds`
181
+ */
182
+ export const flowFieldFromImage = async (url, options, init) => {
183
+ const [field] = await flowFieldsFromImage(url, { ...options, count: 1 }, init);
184
+ if (!field)
185
+ throw new Error(`isard: ${String(url)}: no field`);
186
+ return field;
187
+ };
188
+ /**
189
+ * The fields of an image holding `count` of them one under the other (a model's hours, the first at the top), each
190
+ * read as `flowFieldFromImage` reads one
191
+ */
192
+ export const flowFieldsFromImage = async (url, options, init) => {
193
+ const response = await fetch(url, init);
194
+ if (!response.ok)
195
+ throw new Error(`isard: ${String(url)}: ${response.status}`);
196
+ const bitmap = await createImageBitmap(await response.blob(), { colorSpaceConversion: 'none' });
197
+ const { width, height } = bitmap;
198
+ const canvas = new OffscreenCanvas(width, height);
199
+ const g = canvas.getContext('2d', { willReadFrequently: true });
200
+ if (!g)
201
+ throw new Error('isard: no 2D canvas to read the field');
202
+ g.drawImage(bitmap, 0, 0);
203
+ bitmap.close();
204
+ const { data } = g.getImageData(0, 0, width, height);
205
+ const [u0, u1] = options.u;
206
+ const [v0, v1] = options.v;
207
+ const count = Math.max(1, Math.floor(options.count));
208
+ const rows = Math.floor(height / count);
209
+ return Array.from({ length: count }, (_, k) => {
210
+ const u = new Float32Array(width * rows);
211
+ const v = new Float32Array(width * rows);
212
+ const first = k * rows * width;
213
+ for (let i = 0; i < width * rows; i++) {
214
+ const at = (first + i) * 4;
215
+ const none = (data[at + 3] ?? 0) < 128;
216
+ u[i] = none ? Number.NaN : u0 + ((data[at] ?? 0) / 255) * (u1 - u0);
217
+ v[i] = none ? Number.NaN : v0 + ((data[at + 1] ?? 0) / 255) * (v1 - v0);
218
+ }
219
+ return { width, height: rows, u, v, bounds: options.bounds };
220
+ });
221
+ };
222
+ //# sourceMappingURL=flow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flow.js","sourceRoot":"","sources":["../../src/layers/flow.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Flow layers: particles carried by a field of velocities (a wind's east and north components on a grid of longitudes\n * and latitudes, m/s), their trails drawn on the globe and the relief and animated, colored by speed: wind maps,\n * currents. The engine moves them (`stepFlow`), one step per animation frame while the layer plays.\n */\n\nimport type { LayerHost } from '../types.ts';\nimport type { Engine } from '../wasm/isard.js';\nimport { parseColor, type Ramp } from './features.ts';\nimport { Layer } from './layers.ts';\n\n/**\n * A field of velocities: `width` × `height` points, from west to east and from north to south (row after row), the\n * east (`u`) and north (`v`) components at each (m/s; not a number: none there), the corner points at `bounds` (west,\n * south, east, north, degrees). A last column one step short of 360° goes round the world (a global model's grid)\n */\nexport interface FlowField {\n width: number;\n height: number;\n u: Float32Array | readonly number[];\n v: Float32Array | readonly number[];\n bounds: [west: number, south: number, east: number, north: number];\n}\n\nexport interface FlowStyle {\n /** The colors of the speeds (m/s), linear between them, up to 8 (default from calm blue to storm red) */\n colors: Ramp;\n /** Width of a trail, CSS pixels (default 1.5) */\n width: number;\n /** 0 to 1 (default 0.9) */\n opacity: number;\n /** Metres over the ground (default 0) */\n height: number;\n /** Speed on screen: pixels a second for 10 m/s, whatever the zoom (default 60) */\n speed: number;\n /** Particles moving for each 100 × 100 CSS pixels of the view, the screen as dense at any zoom (default 60; at most\n * `particles`) */\n density: number;\n}\n\n/** A field at a time: one of a model's hours */\nexport interface TimedField {\n time: Date | number;\n field: FlowField;\n}\n\nexport interface FlowOptions extends Partial<FlowStyle> {\n /** The field; or `fields`, at their times, mixed at `time` */\n field?: FlowField;\n /** Fields at their times, on one grid (a model's hours): the layer's field at `time` the mix of the two around it */\n fields?: readonly TimedField[];\n /** The time the fields are mixed at (default the first one's) */\n time?: Date | number;\n /** Particles moving, rounded up to a multiple of 4096 (default 16384) */\n particles?: number;\n /** Positions of a trail, one each 60th of a second (default 24): longer trails, more to draw */\n trail?: number;\n /** Moving from the start (default true); `play`, `pause` and `step` after */\n playing?: boolean;\n before?: Layer;\n}\n\n/** Wind speeds (m/s) from calm to storm */\nconst COLORS: Ramp = [\n [0, '#3a6fb0'],\n [4, '#4fa3c7'],\n [8, '#7fc79a'],\n [12, '#d6e27a'],\n [16, '#f6c14e'],\n [22, '#ef7d3c'],\n [30, '#d2374a'],\n [40, '#8e1f5c'],\n];\n\nexport class FlowLayer extends Layer {\n /** The fields by time (one at time 0 for a single field), the time shown, the two sent to the engine */\n private fields: { time: number; field: FlowField }[];\n private time: number;\n private sent: [FlowField, FlowField | undefined] | undefined;\n private style: FlowStyle;\n private readonly particles: number;\n private readonly trail: number;\n #playing: boolean;\n /** The animation frame asked for, and the time of the last step (ms) */\n private frame = 0;\n private last: number | undefined;\n\n /** @internal */\n constructor(map: LayerHost, options: FlowOptions) {\n super(map);\n this.fields = timed(options);\n this.time = options.time === undefined ? (this.fields[0]?.time ?? 0) : Number(options.time);\n this.particles = options.particles ?? 16384;\n this.trail = options.trail ?? 24;\n this.style = {\n colors: options.colors ?? COLORS,\n width: options.width ?? 1.5,\n opacity: options.opacity ?? 0.9,\n height: options.height ?? 0,\n speed: options.speed ?? 60,\n density: options.density ?? 60,\n };\n this.#playing = options.playing ?? true;\n if (this.#playing) this.loop();\n }\n\n /** Whether the particles move */\n get playing(): boolean {\n return this.#playing;\n }\n\n /** The particles move, a step each animation frame */\n play(): void {\n if (this.#playing) return;\n this.#playing = true;\n this.last = undefined;\n this.loop();\n }\n\n /** The particles stop where they are (the map no longer drawn for them) */\n pause(): void {\n this.#playing = false;\n cancelAnimationFrame(this.frame);\n this.frame = 0;\n }\n\n /** Moving or not (`play`, `pause`) */\n setPlaying(playing: boolean): void {\n if (playing) this.play();\n else this.pause();\n }\n\n /** Moves the particles by `seconds` of the page's time, at once (a still image after so many steps; paused) */\n step(seconds = 1 / 60): void {\n if (this.id < 0) return;\n this.map.engine.stepFlow(this.id, seconds);\n this.map.requestRender();\n }\n\n /** A new field (on another grid, the trails start again) */\n setField(field: FlowField): void {\n this.setFields([{ time: 0, field }], 0);\n }\n\n /** New fields at their times (a model's hours), mixed at `time` (default the one shown) */\n setFields(fields: readonly TimedField[], time?: Date | number): void {\n this.fields = timed({ fields });\n if (time !== undefined) this.time = Number(time);\n this.sent = undefined;\n if (this.id >= 0) this.sendField(this.map.engine, this.id);\n this.map.requestRender();\n }\n\n /** The time the fields are mixed at: between two of the model's hours, the field goes linearly from one to the\n * other (before the first and after the last, theirs); the particles go on */\n setTime(time: Date | number): void {\n this.time = Number(time);\n if (this.id >= 0) this.sendField(this.map.engine, this.id);\n this.map.requestRender();\n }\n\n setStyle(style: Partial<FlowStyle>): void {\n this.style = { ...this.style, ...style };\n if (this.id >= 0) this.sendStyle(this.map.engine, this.id);\n this.map.requestRender();\n }\n\n /** One step an animation frame while it plays, by the time since the last */\n private loop(): void {\n const tick = (now: number) => {\n if (!this.#playing) return;\n\n const seconds = this.last === undefined ? 1 / 60 : (now - this.last) / 1000;\n\n this.last = now;\n this.step(seconds);\n this.frame = requestAnimationFrame(tick);\n };\n\n cancelAnimationFrame(this.frame);\n this.frame = requestAnimationFrame(tick);\n }\n\n /** The two fields around the time and how far between them, the fields sent only when they change */\n private sendField(engine: Engine, id: number): void {\n const { fields, time } = this;\n const [a, b] = around(fields, time);\n\n if (!a) return;\n if (this.sent?.[0] !== a.field || this.sent?.[1] !== b?.field) {\n const { width, height, u, v, bounds } = a.field;\n\n engine.setFlowField(id, width, height, Float32Array.from(u), Float32Array.from(v), Float64Array.from(bounds));\n engine.setFlowNext(id, Float32Array.from(b?.field.u ?? []), Float32Array.from(b?.field.v ?? []));\n this.sent = [a.field, b?.field];\n }\n engine.setFlowBlend(id, b ? (time - a.time) / (b.time - a.time) : 0);\n }\n\n private sendStyle(engine: Engine, id: number): void {\n const { colors, width, opacity, height, speed, density } = this.style;\n const stops = colors.slice(0, 8);\n\n engine.setFlowStyle(\n id,\n Float32Array.from(stops, ([s]) => s),\n Float32Array.from(stops.flatMap(([, c]) => parseColor(c))),\n width,\n opacity,\n height,\n speed,\n density,\n );\n }\n\n /** @internal */\n protected create(engine: Engine): number {\n const id = engine.addFlow(this.particles, this.trail);\n\n // A new engine: everything sent again\n this.sent = undefined;\n this.sendField(engine, id);\n this.sendStyle(engine, id);\n\n return id;\n }\n\n override remove(): void {\n super.remove();\n this.dispose();\n }\n\n /** @internal */\n override dispose(): void {\n this.pause();\n }\n}\n\n/** The fields around a time: the one before and the one after, or the first or last alone outside them */\nconst around = <T extends { time: number }>(fields: readonly T[], time: number): [T | undefined, T | undefined] => {\n const after = fields.findIndex((f) => f.time > time);\n\n if (after < 0) return [fields.at(-1), undefined];\n if (after === 0) return [fields[0], undefined];\n\n return [fields[after - 1], fields[after]];\n};\n\n/** The fields of the options by time: `fields` sorted, or `field` alone (at time 0) */\nconst timed = ({ field, fields }: Pick<FlowOptions, 'field' | 'fields'>): { time: number; field: FlowField }[] => {\n if (fields?.length)\n return fields.map((f) => ({ time: Number(f.time), field: f.field })).sort((a, b) => a.time - b.time);\n if (field) return [{ time: 0, field }];\n throw new Error('isard: a flow layer needs a field or fields');\n};\n\n/**\n * A field from an image (PNG, as wind maps ship them): the east component in red, the north in green, each scaled\n * from 0-255 to its range (`u`, `v`, m/s), none where the image is transparent; its pixels the grid's points over\n * `bounds`\n */\nexport const flowFieldFromImage = async (\n url: string | URL,\n options: { bounds: FlowField['bounds']; u: [min: number, max: number]; v: [min: number, max: number] },\n init?: RequestInit,\n): Promise<FlowField> => {\n const [field] = await flowFieldsFromImage(url, { ...options, count: 1 }, init);\n\n if (!field) throw new Error(`isard: ${String(url)}: no field`);\n\n return field;\n};\n\n/**\n * The fields of an image holding `count` of them one under the other (a model's hours, the first at the top), each\n * read as `flowFieldFromImage` reads one\n */\nexport const flowFieldsFromImage = async (\n url: string | URL,\n options: {\n bounds: FlowField['bounds'];\n u: [min: number, max: number];\n v: [min: number, max: number];\n count: number;\n },\n init?: RequestInit,\n): Promise<FlowField[]> => {\n const response = await fetch(url, init);\n\n if (!response.ok) throw new Error(`isard: ${String(url)}: ${response.status}`);\n\n const bitmap = await createImageBitmap(await response.blob(), { colorSpaceConversion: 'none' });\n const { width, height } = bitmap;\n const canvas = new OffscreenCanvas(width, height);\n const g = canvas.getContext('2d', { willReadFrequently: true });\n\n if (!g) throw new Error('isard: no 2D canvas to read the field');\n g.drawImage(bitmap, 0, 0);\n bitmap.close();\n\n const { data } = g.getImageData(0, 0, width, height);\n const [u0, u1] = options.u;\n const [v0, v1] = options.v;\n const count = Math.max(1, Math.floor(options.count));\n const rows = Math.floor(height / count);\n\n return Array.from({ length: count }, (_, k) => {\n const u = new Float32Array(width * rows);\n const v = new Float32Array(width * rows);\n const first = k * rows * width;\n\n for (let i = 0; i < width * rows; i++) {\n const at = (first + i) * 4;\n const none = (data[at + 3] ?? 0) < 128;\n\n u[i] = none ? Number.NaN : u0 + ((data[at] ?? 0) / 255) * (u1 - u0);\n v[i] = none ? Number.NaN : v0 + ((data[at + 1] ?? 0) / 255) * (v1 - v0);\n }\n\n return { width, height: rows, u, v, bounds: options.bounds };\n });\n};\n"]}
@@ -462,6 +462,15 @@ export class ModelLayer extends Layer {
462
462
  this.map.engine.setModelInstances(this.id, this.instances, this.ground);
463
463
  this.map.requestRender();
464
464
  }
465
+ /** @internal The instances as the engine takes them (`instanceNumbers`: seven numbers each), above the ground */
466
+ setInstanceNumbers(numbers) {
467
+ this.instances = numbers;
468
+ this.ground = true;
469
+ if (this.id < 0)
470
+ return;
471
+ this.map.engine.setModelInstances(this.id, this.instances, this.ground);
472
+ this.map.requestRender();
473
+ }
465
474
  remove() {
466
475
  super.remove();
467
476
  this.dispose();