isard 0.2.0 → 0.3.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 (93) hide show
  1. package/CHANGELOG.md +134 -14
  2. package/README.md +2 -2
  3. package/dist/animation.d.ts +18 -0
  4. package/dist/animation.js +84 -0
  5. package/dist/animation.js.map +1 -0
  6. package/dist/basemap.worker.d.ts +26 -1
  7. package/dist/basemap.worker.js +2 -2
  8. package/dist/basemap.worker.js.map +8 -4
  9. package/dist/basemaps.d.ts +5 -3
  10. package/dist/basemaps.js +5 -3
  11. package/dist/basemaps.js.map +1 -1
  12. package/dist/crs.d.ts +72 -0
  13. package/dist/crs.js +243 -0
  14. package/dist/crs.js.map +1 -0
  15. package/dist/decoder.worker.js +4 -4
  16. package/dist/decoder.worker.js.map +4 -4
  17. package/dist/engine-loader.d.ts +27 -0
  18. package/dist/engine-loader.js +112 -0
  19. package/dist/engine-loader.js.map +1 -0
  20. package/dist/extrusions.js +11 -2
  21. package/dist/extrusions.js.map +1 -1
  22. package/dist/features.d.ts +8 -0
  23. package/dist/features.js +17 -10
  24. package/dist/features.js.map +1 -1
  25. package/dist/geo.d.ts +16 -0
  26. package/dist/geo.js +32 -0
  27. package/dist/geo.js.map +1 -0
  28. package/dist/geoplateforme.d.ts +21 -5
  29. package/dist/geoplateforme.js +47 -12
  30. package/dist/geoplateforme.js.map +1 -1
  31. package/dist/glyphs.d.ts +10 -3
  32. package/dist/glyphs.js +14 -2
  33. package/dist/glyphs.js.map +1 -1
  34. package/dist/gpx.d.ts +11 -0
  35. package/dist/gpx.js +74 -0
  36. package/dist/gpx.js.map +1 -0
  37. package/dist/grid.d.ts +81 -0
  38. package/dist/grid.js +270 -0
  39. package/dist/grid.js.map +1 -0
  40. package/dist/index.d.ts +7 -1
  41. package/dist/index.js +5 -0
  42. package/dist/index.js.map +1 -1
  43. package/dist/interaction.d.ts +3 -4
  44. package/dist/interaction.js +27 -7
  45. package/dist/interaction.js.map +1 -1
  46. package/dist/isard_bg.wasm +0 -0
  47. package/dist/isard_core_bg.wasm +0 -0
  48. package/dist/kml.d.ts +12 -0
  49. package/dist/kml.js +159 -0
  50. package/dist/kml.js.map +1 -0
  51. package/dist/layers.d.ts +5 -2
  52. package/dist/layers.js +22 -18
  53. package/dist/layers.js.map +1 -1
  54. package/dist/lru.d.ts +22 -0
  55. package/dist/lru.js +52 -0
  56. package/dist/lru.js.map +1 -0
  57. package/dist/map.d.ts +26 -6
  58. package/dist/map.js +196 -287
  59. package/dist/map.js.map +1 -1
  60. package/dist/resample.d.ts +41 -0
  61. package/dist/resample.js +164 -0
  62. package/dist/resample.js.map +1 -0
  63. package/dist/settings.d.ts +39 -0
  64. package/dist/settings.js +104 -0
  65. package/dist/settings.js.map +1 -0
  66. package/dist/symbols.d.ts +12 -1
  67. package/dist/symbols.js +124 -26
  68. package/dist/symbols.js.map +1 -1
  69. package/dist/terrain.d.ts +19 -7
  70. package/dist/terrain.js +50 -26
  71. package/dist/terrain.js.map +1 -1
  72. package/dist/topojson.d.ts +29 -0
  73. package/dist/topojson.js +66 -0
  74. package/dist/topojson.js.map +1 -0
  75. package/dist/types.d.ts +23 -7
  76. package/dist/types.js.map +1 -1
  77. package/dist/vectortiles.d.ts +13 -3
  78. package/dist/vectortiles.js +85 -46
  79. package/dist/vectortiles.js.map +1 -1
  80. package/dist/wasm/isard.d.ts +14 -7
  81. package/dist/wasm/isard.js +33 -10
  82. package/dist/wasm/isard_core.d.ts +6 -5
  83. package/dist/wasm/isard_core.js +41 -10
  84. package/dist/wmts.d.ts +14 -0
  85. package/dist/wmts.js +76 -0
  86. package/dist/wmts.js.map +1 -0
  87. package/dist/xml.d.ts +26 -0
  88. package/dist/xml.js +109 -0
  89. package/dist/xml.js.map +1 -0
  90. package/package.json +1 -1
  91. package/dist/equirectangular.d.ts +0 -35
  92. package/dist/equirectangular.js +0 -113
  93. package/dist/equirectangular.js.map +0 -1
@@ -1,113 +0,0 @@
1
- /**
2
- * Equirectangular tiles (longitudes and latitudes, two 256 px tiles side by side at level 0, rows from the north)
3
- * reprojected into the Web Mercator tiles the engine draws: a raster loader that fetches the source tiles a Mercator
4
- * tile spans and draws them into it, one row of pixels at a time (along a row the latitude is constant, so each row is
5
- * a horizontal strip of the source, scaled). The planetary services (NASA's Trek, USGS) serve this grid only.
6
- */
7
- /** Side of the source tiles, pixels */
8
- const SOURCE = 256;
9
- /** Source levels added at most towards the poles, where Web Mercator stretches the latitudes */
10
- const MAX_BOOST = 3;
11
- /** Source tiles kept decoded: the neighbours of a Mercator tile share them */
12
- const CACHE = 96;
13
- /** Latitude (degrees) of a Mercator y in [0, 1] (towards the south) */
14
- const latitude = (y) => (Math.atan(Math.sinh(Math.PI * (1 - 2 * y))) * 180) / Math.PI;
15
- /**
16
- * The strips filling Mercator tile `z`/`x`/`y` (`size` pixels a side) from equirectangular tiles of levels 0 to
17
- * `maxLevel`. The source level has the same pixel size along the parallels (one less than `z`: the source is twice as
18
- * wide at level 0), finer towards the poles, where the Mercator pixels shrink along the meridians.
19
- */
20
- export function plan(z, x, y, size, maxLevel) {
21
- const n = 2 ** z;
22
- const center = latitude((y + 0.5) / n);
23
- const boost = Math.min(MAX_BOOST, Math.max(0, Math.round(-Math.log2(Math.cos((center * Math.PI) / 180)))));
24
- const level = Math.max(0, Math.min(maxLevel, z - 1 + boost));
25
- // The source in pixels at that level: 360° across, 180° down
26
- const width = 2 * SOURCE * 2 ** level;
27
- const height = SOURCE * 2 ** level;
28
- const west = (x / n) * width;
29
- const east = ((x + 1) / n) * width;
30
- const scale = size / (east - west);
31
- const strips = [];
32
- for (let row = 0; row < size; row += 1) {
33
- const lat = latitude((y + (row + 0.5) / size) / n);
34
- const v = Math.max(0, Math.min(height - 1e-6, ((90 - lat) / 180) * height));
35
- const ty = Math.floor(v / SOURCE);
36
- // The source row centered on the destination row's latitude, kept inside its tile (no bleeding into the next)
37
- const sy = Math.max(0, Math.min(SOURCE - 1, v - ty * SOURCE - 0.5));
38
- for (let tx = Math.floor(west / SOURCE); tx * SOURCE < east; tx += 1) {
39
- const from = Math.max(west, tx * SOURCE);
40
- const to = Math.min(east, (tx + 1) * SOURCE);
41
- strips.push({
42
- tx,
43
- ty,
44
- sx: from - tx * SOURCE,
45
- sy,
46
- sw: to - from,
47
- dx: (from - west) * scale,
48
- dy: row,
49
- dw: (to - from) * scale,
50
- });
51
- }
52
- }
53
- return { level, strips };
54
- }
55
- /** A tile URL of the source: {z}, {x} and {y} replaced */
56
- const tileUrl = (template, z, x, y) => template.replaceAll('{z}', String(z)).replaceAll('{x}', String(x)).replaceAll('{y}', String(y));
57
- /**
58
- * A raster loader drawing Web Mercator tiles of `size` pixels from the equirectangular tiles of `url` ({z}, {x}, {y};
59
- * levels 0 to `maxLevel`). A missing source tile (404) leaves its part empty; a tile with no source at all is
60
- * missing; other errors reject (retried by the engine).
61
- */
62
- export function equirectangularLoader(url, maxLevel, size) {
63
- // Source tiles by key, oldest first: a request is shared by the tiles that need it, its signal is not theirs
64
- const cache = new Map();
65
- const source = (z, x, y) => {
66
- const key = `${z}/${x}/${y}`;
67
- let tile = cache.get(key);
68
- if (tile) {
69
- cache.delete(key);
70
- }
71
- else {
72
- tile = (async () => {
73
- const response = await fetch(tileUrl(url, z, x, y));
74
- if (response.status === 404 || response.status === 204 || response.status === 410)
75
- return null;
76
- if (!response.ok)
77
- throw new Error(`isard: ${response.status} for an equirectangular tile`);
78
- return createImageBitmap(await response.blob());
79
- })();
80
- // A failure is not kept: the engine's retry fetches again
81
- tile.catch(() => cache.delete(key));
82
- }
83
- cache.set(key, tile);
84
- for (const old of cache.keys()) {
85
- if (cache.size <= CACHE)
86
- break;
87
- cache.delete(old);
88
- }
89
- return tile;
90
- };
91
- return async (z, x, y, signal) => {
92
- const { level, strips } = plan(z, x, y, size, maxLevel);
93
- const keys = [...new Set(strips.map((s) => `${s.tx}/${s.ty}`))];
94
- const images = new Map(await Promise.all(keys.map(async (key) => {
95
- const [tx, ty] = key.split('/').map(Number);
96
- return [key, await source(level, tx, ty)];
97
- })));
98
- signal.throwIfAborted();
99
- if (![...images.values()].some(Boolean))
100
- return null;
101
- const canvas = new OffscreenCanvas(size, size);
102
- const context = canvas.getContext('2d');
103
- if (!context)
104
- throw new Error('isard: no 2D canvas to reproject the tiles');
105
- for (const s of strips) {
106
- const image = images.get(`${s.tx}/${s.ty}`);
107
- if (image)
108
- context.drawImage(image, s.sx, s.sy, s.sw, 1, s.dx, s.dy, s.dw, 1);
109
- }
110
- return canvas.transferToImageBitmap();
111
- };
112
- }
113
- //# sourceMappingURL=equirectangular.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"equirectangular.js","sourceRoot":"","sources":["../src/equirectangular.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Equirectangular tiles (longitudes and latitudes, two 256 px tiles side by side at level 0, rows from the north)\n * reprojected into the Web Mercator tiles the engine draws: a raster loader that fetches the source tiles a Mercator\n * tile spans and draws them into it, one row of pixels at a time (along a row the latitude is constant, so each row is\n * a horizontal strip of the source, scaled). The planetary services (NASA's Trek, USGS) serve this grid only.\n */\n\n/** Side of the source tiles, pixels */\nconst SOURCE = 256;\n/** Source levels added at most towards the poles, where Web Mercator stretches the latitudes */\nconst MAX_BOOST = 3;\n/** Source tiles kept decoded: the neighbours of a Mercator tile share them */\nconst CACHE = 96;\n\n/** One strip drawn: from source tile (`tx`, `ty`) at the plan's level, the source rectangle (`sx`, `sy`, `sw`, one\n * row high) to the destination rectangle (`dx`, `dy`, `dw`, one row high) */\nexport interface Strip {\n tx: number;\n ty: number;\n sx: number;\n sy: number;\n sw: number;\n dx: number;\n dy: number;\n dw: number;\n}\n\n/** Where the pixels of a Mercator tile come from: the source level, and a strip per row and source column */\nexport interface Plan {\n level: number;\n strips: Strip[];\n}\n\n/** Latitude (degrees) of a Mercator y in [0, 1] (towards the south) */\nconst latitude = (y: number) => (Math.atan(Math.sinh(Math.PI * (1 - 2 * y))) * 180) / Math.PI;\n\n/**\n * The strips filling Mercator tile `z`/`x`/`y` (`size` pixels a side) from equirectangular tiles of levels 0 to\n * `maxLevel`. The source level has the same pixel size along the parallels (one less than `z`: the source is twice as\n * wide at level 0), finer towards the poles, where the Mercator pixels shrink along the meridians.\n */\nexport function plan(z: number, x: number, y: number, size: number, maxLevel: number): Plan {\n const n = 2 ** z;\n const center = latitude((y + 0.5) / n);\n const boost = Math.min(MAX_BOOST, Math.max(0, Math.round(-Math.log2(Math.cos((center * Math.PI) / 180)))));\n const level = Math.max(0, Math.min(maxLevel, z - 1 + boost));\n // The source in pixels at that level: 360° across, 180° down\n const width = 2 * SOURCE * 2 ** level;\n const height = SOURCE * 2 ** level;\n const west = (x / n) * width;\n const east = ((x + 1) / n) * width;\n const scale = size / (east - west);\n const strips: Strip[] = [];\n for (let row = 0; row < size; row += 1) {\n const lat = latitude((y + (row + 0.5) / size) / n);\n const v = Math.max(0, Math.min(height - 1e-6, ((90 - lat) / 180) * height));\n const ty = Math.floor(v / SOURCE);\n // The source row centered on the destination row's latitude, kept inside its tile (no bleeding into the next)\n const sy = Math.max(0, Math.min(SOURCE - 1, v - ty * SOURCE - 0.5));\n for (let tx = Math.floor(west / SOURCE); tx * SOURCE < east; tx += 1) {\n const from = Math.max(west, tx * SOURCE);\n const to = Math.min(east, (tx + 1) * SOURCE);\n strips.push({\n tx,\n ty,\n sx: from - tx * SOURCE,\n sy,\n sw: to - from,\n dx: (from - west) * scale,\n dy: row,\n dw: (to - from) * scale,\n });\n }\n }\n return { level, strips };\n}\n\n/** A tile URL of the source: {z}, {x} and {y} replaced */\nconst tileUrl = (template: string, z: number, x: number, y: number) =>\n template.replaceAll('{z}', String(z)).replaceAll('{x}', String(x)).replaceAll('{y}', String(y));\n\n/**\n * A raster loader drawing Web Mercator tiles of `size` pixels from the equirectangular tiles of `url` ({z}, {x}, {y};\n * levels 0 to `maxLevel`). A missing source tile (404) leaves its part empty; a tile with no source at all is\n * missing; other errors reject (retried by the engine).\n */\nexport function equirectangularLoader(\n url: string,\n maxLevel: number,\n size: number,\n): (z: number, x: number, y: number, signal: AbortSignal) => Promise<ImageBitmap | null> {\n // Source tiles by key, oldest first: a request is shared by the tiles that need it, its signal is not theirs\n const cache = new Map<string, Promise<ImageBitmap | null>>();\n const source = (z: number, x: number, y: number): Promise<ImageBitmap | null> => {\n const key = `${z}/${x}/${y}`;\n let tile = cache.get(key);\n if (tile) {\n cache.delete(key);\n } else {\n tile = (async () => {\n const response = await fetch(tileUrl(url, z, x, y));\n if (response.status === 404 || response.status === 204 || response.status === 410) return null;\n if (!response.ok) throw new Error(`isard: ${response.status} for an equirectangular tile`);\n return createImageBitmap(await response.blob());\n })();\n // A failure is not kept: the engine's retry fetches again\n tile.catch(() => cache.delete(key));\n }\n cache.set(key, tile);\n for (const old of cache.keys()) {\n if (cache.size <= CACHE) break;\n cache.delete(old);\n }\n return tile;\n };\n return async (z, x, y, signal) => {\n const { level, strips } = plan(z, x, y, size, maxLevel);\n const keys = [...new Set(strips.map((s) => `${s.tx}/${s.ty}`))];\n const images = new Map(\n await Promise.all(\n keys.map(async (key) => {\n const [tx, ty] = key.split('/').map(Number) as [number, number];\n return [key, await source(level, tx, ty)] as const;\n }),\n ),\n );\n signal.throwIfAborted();\n if (![...images.values()].some(Boolean)) return null;\n const canvas = new OffscreenCanvas(size, size);\n const context = canvas.getContext('2d');\n if (!context) throw new Error('isard: no 2D canvas to reproject the tiles');\n for (const s of strips) {\n const image = images.get(`${s.tx}/${s.ty}`);\n if (image) context.drawImage(image, s.sx, s.sy, s.sw, 1, s.dx, s.dy, s.dw, 1);\n }\n return canvas.transferToImageBitmap();\n };\n}\n"]}