isard 0.0.0-stage → 0.2.1

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 (84) hide show
  1. package/CHANGELOG.md +176 -0
  2. package/LICENSE +21 -0
  3. package/README.md +150 -2
  4. package/dist/basemap.worker.d.ts +76 -0
  5. package/dist/basemap.worker.js +4 -0
  6. package/dist/basemap.worker.js.map +13 -0
  7. package/dist/basemaps.d.ts +48 -0
  8. package/dist/basemaps.js +97 -0
  9. package/dist/basemaps.js.map +1 -0
  10. package/dist/color.d.ts +8 -0
  11. package/dist/color.js +98 -0
  12. package/dist/color.js.map +1 -0
  13. package/dist/controls.d.ts +43 -0
  14. package/dist/controls.js +115 -0
  15. package/dist/controls.js.map +1 -0
  16. package/dist/decoder.worker.js +6 -0
  17. package/dist/decoder.worker.js.map +12 -0
  18. package/dist/decoders.d.ts +27 -0
  19. package/dist/decoders.js +109 -0
  20. package/dist/decoders.js.map +1 -0
  21. package/dist/equirectangular.d.ts +35 -0
  22. package/dist/equirectangular.js +143 -0
  23. package/dist/equirectangular.js.map +1 -0
  24. package/dist/extrusions.d.ts +79 -0
  25. package/dist/extrusions.js +224 -0
  26. package/dist/extrusions.js.map +1 -0
  27. package/dist/features.d.ts +159 -0
  28. package/dist/features.js +387 -0
  29. package/dist/features.js.map +1 -0
  30. package/dist/geoplateforme.d.ts +35 -0
  31. package/dist/geoplateforme.js +56 -0
  32. package/dist/geoplateforme.js.map +1 -0
  33. package/dist/glyphs.d.ts +104 -0
  34. package/dist/glyphs.js +383 -0
  35. package/dist/glyphs.js.map +1 -0
  36. package/dist/index.d.ts +34 -0
  37. package/dist/index.js +25 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/interaction.d.ts +53 -0
  40. package/dist/interaction.js +259 -0
  41. package/dist/interaction.js.map +1 -0
  42. package/dist/isard_bg.wasm +0 -0
  43. package/dist/isard_core_bg.wasm +0 -0
  44. package/dist/layers.d.ts +103 -0
  45. package/dist/layers.js +290 -0
  46. package/dist/layers.js.map +1 -0
  47. package/dist/loader.d.ts +30 -0
  48. package/dist/loader.js +102 -0
  49. package/dist/loader.js.map +1 -0
  50. package/dist/map.d.ts +211 -0
  51. package/dist/map.js +1222 -0
  52. package/dist/map.js.map +1 -0
  53. package/dist/mvt.d.ts +19 -0
  54. package/dist/mvt.js +231 -0
  55. package/dist/mvt.js.map +1 -0
  56. package/dist/overlay.d.ts +118 -0
  57. package/dist/overlay.js +334 -0
  58. package/dist/overlay.js.map +1 -0
  59. package/dist/protocol.d.ts +30 -0
  60. package/dist/protocol.js +2 -0
  61. package/dist/protocol.js.map +1 -0
  62. package/dist/style.d.ts +7 -0
  63. package/dist/style.js +130 -0
  64. package/dist/style.js.map +1 -0
  65. package/dist/symbols.d.ts +61 -0
  66. package/dist/symbols.js +238 -0
  67. package/dist/symbols.js.map +1 -0
  68. package/dist/terrain.d.ts +14 -0
  69. package/dist/terrain.js +56 -0
  70. package/dist/terrain.js.map +1 -0
  71. package/dist/types.d.ts +312 -0
  72. package/dist/types.js +2 -0
  73. package/dist/types.js.map +1 -0
  74. package/dist/vectorstyle.d.ts +83 -0
  75. package/dist/vectorstyle.js +518 -0
  76. package/dist/vectorstyle.js.map +1 -0
  77. package/dist/vectortiles.d.ts +124 -0
  78. package/dist/vectortiles.js +523 -0
  79. package/dist/vectortiles.js.map +1 -0
  80. package/dist/wasm/isard.d.ts +612 -0
  81. package/dist/wasm/isard.js +2623 -0
  82. package/dist/wasm/isard_core.d.ts +469 -0
  83. package/dist/wasm/isard_core.js +2385 -0
  84. package/package.json +49 -4
package/dist/style.js ADDED
@@ -0,0 +1,130 @@
1
+ /**
2
+ * Styles of the DOM overlays (markers, popups, controls), injected once per document so the package needs no CSS
3
+ * file. Colors are custom properties, defaults on the root with no specificity: an application restyles them on any
4
+ * element around the map (its container's class may be replaced by a framework: nothing depends on it).
5
+ */
6
+ const CSS = `
7
+ :where(:root) {
8
+ --isard-ctrl-bg: #fff;
9
+ --isard-ctrl-fg: #333;
10
+ --isard-ctrl-hover: rgb(0 0 0 / 0.05);
11
+ --isard-ctrl-active: #1d2433;
12
+ --isard-ctrl-active-fg: #fff;
13
+ --isard-ctrl-border: #ddd;
14
+ --isard-ctrl-shadow: 0 0 0 2px rgb(0 0 0 / 0.1);
15
+ --isard-popup-bg: #fff;
16
+ --isard-popup-fg: #1d2433;
17
+ --isard-marker-color: #3a6fd8;
18
+ --isard-attrib-bg: rgb(255 255 255 / 0.5);
19
+ --isard-attrib-fg: rgb(0 0 0 / 0.75);
20
+ --isard-focus: #3a6fd8;
21
+ }
22
+ .isard-canvas:focus { outline: none; }
23
+ .isard-canvas:focus-visible { outline: 2px solid var(--isard-focus); outline-offset: -2px; }
24
+ .isard-overlay { position: absolute; inset: 0; overflow: hidden; pointer-events: none; z-index: 1; }
25
+ .isard-ctrl-top-left, .isard-ctrl-top-right, .isard-ctrl-bottom-left, .isard-ctrl-bottom-right {
26
+ position: absolute; pointer-events: none; z-index: 2;
27
+ }
28
+ .isard-ctrl-top-left { top: 0; left: 0; }
29
+ .isard-ctrl-top-right { top: 0; right: 0; }
30
+ .isard-ctrl-bottom-left { bottom: 0; left: 0; }
31
+ .isard-ctrl-bottom-right { bottom: 0; right: 0; }
32
+ .isard-ctrl { clear: both; pointer-events: auto; transform: translate(0, 0); }
33
+ .isard-ctrl-top-left .isard-ctrl { float: left; margin: 10px 0 0 10px; }
34
+ .isard-ctrl-top-right .isard-ctrl { float: right; margin: 10px 10px 0 0; }
35
+ .isard-ctrl-bottom-left .isard-ctrl { float: left; margin: 0 0 10px 10px; }
36
+ .isard-ctrl-bottom-right .isard-ctrl { float: right; margin: 0 10px 10px 0; }
37
+ .isard-ctrl-group { background: var(--isard-ctrl-bg); border-radius: 4px; box-shadow: var(--isard-ctrl-shadow); }
38
+ .isard-ctrl-group button {
39
+ display: block; width: 29px; height: 29px; padding: 0; margin: 0; border: 0; box-sizing: border-box;
40
+ background: transparent; color: var(--isard-ctrl-fg); cursor: pointer; font: inherit;
41
+ }
42
+ .isard-ctrl-group button + button { border-top: 1px solid var(--isard-ctrl-border); }
43
+ .isard-ctrl-group button:first-child { border-radius: 4px 4px 0 0; }
44
+ .isard-ctrl-group button:last-child { border-radius: 0 0 4px 4px; }
45
+ .isard-ctrl-group button:only-child { border-radius: inherit; }
46
+ .isard-ctrl-group button:hover { background-color: var(--isard-ctrl-hover); }
47
+ .isard-ctrl-group button:focus-visible { outline: 2px solid var(--isard-ctrl-active); outline-offset: -2px; }
48
+ .isard-ctrl-group button.active { background: var(--isard-ctrl-active); color: var(--isard-ctrl-active-fg); }
49
+ .isard-ctrl-group button svg { display: block; margin: auto; pointer-events: none; }
50
+ .isard-ctrl-compass svg { transition: transform 0.05s; }
51
+ .isard-marker { position: absolute; top: 0; left: 0; pointer-events: auto; will-change: transform; }
52
+ .isard-marker-draggable { cursor: grab; touch-action: none; }
53
+ .isard-marker-dragging { cursor: grabbing; }
54
+ .isard-marker-pin { display: block; color: var(--isard-marker-color); filter: drop-shadow(0 1px 2px rgb(0 0 0 / 0.35)); }
55
+ .isard-popup {
56
+ position: absolute; top: 0; left: 0; display: flex; pointer-events: none; will-change: transform; z-index: 1;
57
+ color: var(--isard-popup-fg);
58
+ }
59
+ .isard-popup-anchor-top, .isard-popup-anchor-top-left, .isard-popup-anchor-top-right { flex-direction: column; }
60
+ .isard-popup-anchor-bottom, .isard-popup-anchor-bottom-left, .isard-popup-anchor-bottom-right {
61
+ flex-direction: column-reverse;
62
+ }
63
+ .isard-popup-anchor-left { flex-direction: row; }
64
+ .isard-popup-anchor-right { flex-direction: row-reverse; }
65
+ .isard-popup-tip { width: 0; height: 0; border: 10px solid transparent; z-index: 1; }
66
+ .isard-popup-anchor-top .isard-popup-tip { align-self: center; border-top: none; border-bottom-color: var(--isard-popup-bg); }
67
+ .isard-popup-anchor-top-left .isard-popup-tip {
68
+ align-self: flex-start; border-top: none; border-left: none; border-bottom-color: var(--isard-popup-bg);
69
+ }
70
+ .isard-popup-anchor-top-right .isard-popup-tip {
71
+ align-self: flex-end; border-top: none; border-right: none; border-bottom-color: var(--isard-popup-bg);
72
+ }
73
+ .isard-popup-anchor-bottom .isard-popup-tip { align-self: center; border-bottom: none; border-top-color: var(--isard-popup-bg); }
74
+ .isard-popup-anchor-bottom-left .isard-popup-tip {
75
+ align-self: flex-start; border-bottom: none; border-left: none; border-top-color: var(--isard-popup-bg);
76
+ }
77
+ .isard-popup-anchor-bottom-right .isard-popup-tip {
78
+ align-self: flex-end; border-bottom: none; border-right: none; border-top-color: var(--isard-popup-bg);
79
+ }
80
+ .isard-popup-anchor-left .isard-popup-tip { align-self: center; border-left: none; border-right-color: var(--isard-popup-bg); }
81
+ .isard-popup-anchor-right .isard-popup-tip { align-self: center; border-right: none; border-left-color: var(--isard-popup-bg); }
82
+ .isard-popup-content {
83
+ position: relative; padding: 12px 14px; border-radius: 4px; background: var(--isard-popup-bg);
84
+ box-shadow: 0 1px 4px rgb(0 0 0 / 0.2); pointer-events: auto; overflow-wrap: break-word;
85
+ }
86
+ .isard-popup-has-close .isard-popup-content { padding-right: 26px; }
87
+ .isard-popup-anchor-top-left .isard-popup-content { border-top-left-radius: 0; }
88
+ .isard-popup-anchor-top-right .isard-popup-content { border-top-right-radius: 0; }
89
+ .isard-popup-anchor-bottom-left .isard-popup-content { border-bottom-left-radius: 0; }
90
+ .isard-popup-anchor-bottom-right .isard-popup-content { border-bottom-right-radius: 0; }
91
+ .isard-popup-close {
92
+ position: absolute; top: 0; right: 0; width: 22px; height: 22px; padding: 0; border: 0; border-radius: 0 4px 0 4px;
93
+ background: transparent; color: inherit; font: 18px/22px system-ui, sans-serif; cursor: pointer;
94
+ }
95
+ .isard-popup-close:hover { background: rgb(0 0 0 / 0.05); }
96
+ .isard-popup-close:focus-visible, .isard-attrib-button:focus-visible, .isard-attrib a:focus-visible {
97
+ outline: 2px solid var(--isard-focus); outline-offset: -2px;
98
+ }
99
+ .isard-attrib {
100
+ display: flex; align-items: center; gap: 4px; padding: 0 5px; border-radius: 4px; background: var(--isard-attrib-bg);
101
+ color: var(--isard-attrib-fg); font: 12px/20px system-ui, sans-serif;
102
+ }
103
+ .isard-ctrl-bottom-right .isard-ctrl.isard-attrib, .isard-ctrl-bottom-left .isard-ctrl.isard-attrib { margin-bottom: 0; }
104
+ .isard-ctrl-bottom-right .isard-ctrl.isard-attrib { margin-right: 0; border-radius: 4px 0 0 0; }
105
+ .isard-ctrl-bottom-left .isard-ctrl.isard-attrib { margin-left: 0; border-radius: 0 4px 0 0; }
106
+ .isard-attrib a { color: inherit; text-decoration: none; }
107
+ .isard-attrib a:hover { text-decoration: underline; }
108
+ .isard-attrib-button {
109
+ display: none; width: 24px; height: 24px; padding: 0; border: 0; border-radius: 12px; flex: none;
110
+ background: var(--isard-ctrl-bg); color: var(--isard-ctrl-fg); cursor: pointer;
111
+ }
112
+ .isard-attrib-button svg { display: block; margin: auto; }
113
+ .isard-ctrl.isard-attrib.isard-attrib-compact { min-height: 24px; padding: 2px; border-radius: 12px; margin: 0 10px 10px; }
114
+ .isard-attrib-compact .isard-attrib-button { display: block; }
115
+ .isard-attrib-compact .isard-attrib-inner { display: none; padding: 0 6px 0 2px; }
116
+ .isard-attrib-compact.isard-attrib-open { background: var(--isard-ctrl-bg); }
117
+ .isard-attrib-compact.isard-attrib-open .isard-attrib-inner { display: block; }
118
+ `;
119
+ const styled = new WeakSet();
120
+ /** Adds the overlay styles to a document, once; first in the head so application rules win at equal specificity */
121
+ export function injectStyle(doc) {
122
+ if (styled.has(doc))
123
+ return;
124
+ styled.add(doc);
125
+ const style = doc.createElement('style');
126
+ style.dataset.isard = '';
127
+ style.textContent = CSS;
128
+ doc.head.prepend(style);
129
+ }
130
+ //# sourceMappingURL=style.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"style.js","sourceRoot":"","sources":["../src/style.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,GAAG,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgHX,CAAC;AAEF,MAAM,MAAM,GAAG,IAAI,OAAO,EAAY,CAAC;AAEvC,mHAAmH;AACnH,MAAM,UAAU,WAAW,CAAC,GAAa;IACvC,IAAI,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC;QAAE,OAAO;IAC5B,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAChB,MAAM,KAAK,GAAG,GAAG,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;IACzC,KAAK,CAAC,OAAO,CAAC,KAAK,GAAG,EAAE,CAAC;IACzB,KAAK,CAAC,WAAW,GAAG,GAAG,CAAC;IACxB,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC1B,CAAC","sourcesContent":["/**\n * Styles of the DOM overlays (markers, popups, controls), injected once per document so the package needs no CSS\n * file. Colors are custom properties, defaults on the root with no specificity: an application restyles them on any\n * element around the map (its container's class may be replaced by a framework: nothing depends on it).\n */\n\nconst CSS = `\n:where(:root) {\n --isard-ctrl-bg: #fff;\n --isard-ctrl-fg: #333;\n --isard-ctrl-hover: rgb(0 0 0 / 0.05);\n --isard-ctrl-active: #1d2433;\n --isard-ctrl-active-fg: #fff;\n --isard-ctrl-border: #ddd;\n --isard-ctrl-shadow: 0 0 0 2px rgb(0 0 0 / 0.1);\n --isard-popup-bg: #fff;\n --isard-popup-fg: #1d2433;\n --isard-marker-color: #3a6fd8;\n --isard-attrib-bg: rgb(255 255 255 / 0.5);\n --isard-attrib-fg: rgb(0 0 0 / 0.75);\n --isard-focus: #3a6fd8;\n}\n.isard-canvas:focus { outline: none; }\n.isard-canvas:focus-visible { outline: 2px solid var(--isard-focus); outline-offset: -2px; }\n.isard-overlay { position: absolute; inset: 0; overflow: hidden; pointer-events: none; z-index: 1; }\n.isard-ctrl-top-left, .isard-ctrl-top-right, .isard-ctrl-bottom-left, .isard-ctrl-bottom-right {\n position: absolute; pointer-events: none; z-index: 2;\n}\n.isard-ctrl-top-left { top: 0; left: 0; }\n.isard-ctrl-top-right { top: 0; right: 0; }\n.isard-ctrl-bottom-left { bottom: 0; left: 0; }\n.isard-ctrl-bottom-right { bottom: 0; right: 0; }\n.isard-ctrl { clear: both; pointer-events: auto; transform: translate(0, 0); }\n.isard-ctrl-top-left .isard-ctrl { float: left; margin: 10px 0 0 10px; }\n.isard-ctrl-top-right .isard-ctrl { float: right; margin: 10px 10px 0 0; }\n.isard-ctrl-bottom-left .isard-ctrl { float: left; margin: 0 0 10px 10px; }\n.isard-ctrl-bottom-right .isard-ctrl { float: right; margin: 0 10px 10px 0; }\n.isard-ctrl-group { background: var(--isard-ctrl-bg); border-radius: 4px; box-shadow: var(--isard-ctrl-shadow); }\n.isard-ctrl-group button {\n display: block; width: 29px; height: 29px; padding: 0; margin: 0; border: 0; box-sizing: border-box;\n background: transparent; color: var(--isard-ctrl-fg); cursor: pointer; font: inherit;\n}\n.isard-ctrl-group button + button { border-top: 1px solid var(--isard-ctrl-border); }\n.isard-ctrl-group button:first-child { border-radius: 4px 4px 0 0; }\n.isard-ctrl-group button:last-child { border-radius: 0 0 4px 4px; }\n.isard-ctrl-group button:only-child { border-radius: inherit; }\n.isard-ctrl-group button:hover { background-color: var(--isard-ctrl-hover); }\n.isard-ctrl-group button:focus-visible { outline: 2px solid var(--isard-ctrl-active); outline-offset: -2px; }\n.isard-ctrl-group button.active { background: var(--isard-ctrl-active); color: var(--isard-ctrl-active-fg); }\n.isard-ctrl-group button svg { display: block; margin: auto; pointer-events: none; }\n.isard-ctrl-compass svg { transition: transform 0.05s; }\n.isard-marker { position: absolute; top: 0; left: 0; pointer-events: auto; will-change: transform; }\n.isard-marker-draggable { cursor: grab; touch-action: none; }\n.isard-marker-dragging { cursor: grabbing; }\n.isard-marker-pin { display: block; color: var(--isard-marker-color); filter: drop-shadow(0 1px 2px rgb(0 0 0 / 0.35)); }\n.isard-popup {\n position: absolute; top: 0; left: 0; display: flex; pointer-events: none; will-change: transform; z-index: 1;\n color: var(--isard-popup-fg);\n}\n.isard-popup-anchor-top, .isard-popup-anchor-top-left, .isard-popup-anchor-top-right { flex-direction: column; }\n.isard-popup-anchor-bottom, .isard-popup-anchor-bottom-left, .isard-popup-anchor-bottom-right {\n flex-direction: column-reverse;\n}\n.isard-popup-anchor-left { flex-direction: row; }\n.isard-popup-anchor-right { flex-direction: row-reverse; }\n.isard-popup-tip { width: 0; height: 0; border: 10px solid transparent; z-index: 1; }\n.isard-popup-anchor-top .isard-popup-tip { align-self: center; border-top: none; border-bottom-color: var(--isard-popup-bg); }\n.isard-popup-anchor-top-left .isard-popup-tip {\n align-self: flex-start; border-top: none; border-left: none; border-bottom-color: var(--isard-popup-bg);\n}\n.isard-popup-anchor-top-right .isard-popup-tip {\n align-self: flex-end; border-top: none; border-right: none; border-bottom-color: var(--isard-popup-bg);\n}\n.isard-popup-anchor-bottom .isard-popup-tip { align-self: center; border-bottom: none; border-top-color: var(--isard-popup-bg); }\n.isard-popup-anchor-bottom-left .isard-popup-tip {\n align-self: flex-start; border-bottom: none; border-left: none; border-top-color: var(--isard-popup-bg);\n}\n.isard-popup-anchor-bottom-right .isard-popup-tip {\n align-self: flex-end; border-bottom: none; border-right: none; border-top-color: var(--isard-popup-bg);\n}\n.isard-popup-anchor-left .isard-popup-tip { align-self: center; border-left: none; border-right-color: var(--isard-popup-bg); }\n.isard-popup-anchor-right .isard-popup-tip { align-self: center; border-right: none; border-left-color: var(--isard-popup-bg); }\n.isard-popup-content {\n position: relative; padding: 12px 14px; border-radius: 4px; background: var(--isard-popup-bg);\n box-shadow: 0 1px 4px rgb(0 0 0 / 0.2); pointer-events: auto; overflow-wrap: break-word;\n}\n.isard-popup-has-close .isard-popup-content { padding-right: 26px; }\n.isard-popup-anchor-top-left .isard-popup-content { border-top-left-radius: 0; }\n.isard-popup-anchor-top-right .isard-popup-content { border-top-right-radius: 0; }\n.isard-popup-anchor-bottom-left .isard-popup-content { border-bottom-left-radius: 0; }\n.isard-popup-anchor-bottom-right .isard-popup-content { border-bottom-right-radius: 0; }\n.isard-popup-close {\n position: absolute; top: 0; right: 0; width: 22px; height: 22px; padding: 0; border: 0; border-radius: 0 4px 0 4px;\n background: transparent; color: inherit; font: 18px/22px system-ui, sans-serif; cursor: pointer;\n}\n.isard-popup-close:hover { background: rgb(0 0 0 / 0.05); }\n.isard-popup-close:focus-visible, .isard-attrib-button:focus-visible, .isard-attrib a:focus-visible {\n outline: 2px solid var(--isard-focus); outline-offset: -2px;\n}\n.isard-attrib {\n display: flex; align-items: center; gap: 4px; padding: 0 5px; border-radius: 4px; background: var(--isard-attrib-bg);\n color: var(--isard-attrib-fg); font: 12px/20px system-ui, sans-serif;\n}\n.isard-ctrl-bottom-right .isard-ctrl.isard-attrib, .isard-ctrl-bottom-left .isard-ctrl.isard-attrib { margin-bottom: 0; }\n.isard-ctrl-bottom-right .isard-ctrl.isard-attrib { margin-right: 0; border-radius: 4px 0 0 0; }\n.isard-ctrl-bottom-left .isard-ctrl.isard-attrib { margin-left: 0; border-radius: 0 4px 0 0; }\n.isard-attrib a { color: inherit; text-decoration: none; }\n.isard-attrib a:hover { text-decoration: underline; }\n.isard-attrib-button {\n display: none; width: 24px; height: 24px; padding: 0; border: 0; border-radius: 12px; flex: none;\n background: var(--isard-ctrl-bg); color: var(--isard-ctrl-fg); cursor: pointer;\n}\n.isard-attrib-button svg { display: block; margin: auto; }\n.isard-ctrl.isard-attrib.isard-attrib-compact { min-height: 24px; padding: 2px; border-radius: 12px; margin: 0 10px 10px; }\n.isard-attrib-compact .isard-attrib-button { display: block; }\n.isard-attrib-compact .isard-attrib-inner { display: none; padding: 0 6px 0 2px; }\n.isard-attrib-compact.isard-attrib-open { background: var(--isard-ctrl-bg); }\n.isard-attrib-compact.isard-attrib-open .isard-attrib-inner { display: block; }\n`;\n\nconst styled = new WeakSet<Document>();\n\n/** Adds the overlay styles to a document, once; first in the head so application rules win at equal specificity */\nexport function injectStyle(doc: Document): void {\n if (styled.has(doc)) return;\n styled.add(doc);\n const style = doc.createElement('style');\n style.dataset.isard = '';\n style.textContent = CSS;\n doc.head.prepend(style);\n}\n"]}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Text labels from GeoJSON: at points, at the middle of lines, at the center of polygons. Shaped on the page, placed by
3
+ * the engine every frame with collisions shared by all symbol layers (the top layer first, each layer in data order),
4
+ * drawn with a halo, standing on the relief.
5
+ */
6
+ import { type Feature, type FeatureData, type FeatureLayerStyle, type Placement, type Properties, type Size, type Styled } from './features.ts';
7
+ import { type Glyphs, type TextPart } from './glyphs.ts';
8
+ import { Layer } from './layers.ts';
9
+ import type { Anchor } from './overlay.ts';
10
+ import type { LayerHost } from './types.ts';
11
+ export interface SymbolStyle<P = Properties> extends Omit<FeatureLayerStyle<P>, 'ramp'> {
12
+ /** The text: a string (`\n` breaks lines) or runs with their own scale and font; empty: no label */
13
+ text: Styled<string | TextPart[], P>;
14
+ /** A font of the map's glyphs (default `Noto Sans Regular`; also Bold and Italic with the default glyphs) */
15
+ font: Styled<string, P>;
16
+ /** CSS pixels, or zoom stops (default 12) */
17
+ size: Styled<Size, P>;
18
+ /** `'point'` (default): horizontal text; `'line'`: along the line at its middle, kept upright */
19
+ placement: 'point' | 'line';
20
+ color: Styled<string, P>;
21
+ haloColor: Styled<string, P>;
22
+ /** CSS pixels (default 0) */
23
+ haloWidth: Styled<number, P>;
24
+ /** Side of the text at the anchor (default `'center'`) */
25
+ anchor: Styled<Anchor, P>;
26
+ /** Shift, ems (default [0, 0]) */
27
+ offset: Styled<[x: number, y: number], P>;
28
+ /** Wrapping width, ems (default 10) */
29
+ maxWidth: Styled<number, P>;
30
+ /** Ems (default 1.2) */
31
+ lineHeight: Styled<number, P>;
32
+ /** Shown even where it overlaps other labels, and hiding none (MapLibre's allow-overlap and ignore-placement)
33
+ * (default false) */
34
+ overlap: Styled<boolean, P>;
35
+ }
36
+ export interface SymbolOptions<P = Properties> extends Partial<SymbolStyle<P>>, Placement {
37
+ data: FeatureData<P>;
38
+ }
39
+ /** The map's glyphs, for a layer */
40
+ export interface GlyphHost extends LayerHost {
41
+ readonly glyphs: Glyphs;
42
+ }
43
+ export declare class SymbolLayer<P = Properties> extends Layer {
44
+ private readonly host;
45
+ private style;
46
+ private list;
47
+ /** Bumped by each change: a shaping that finished after a newer one began is dropped */
48
+ private generation;
49
+ private labels;
50
+ get features(): readonly Feature<P>[];
51
+ setData(data: FeatureData<P>): void;
52
+ setStyle(style: Partial<SymbolStyle<P>>): void;
53
+ setFilter(filter: ((properties: P, index: number) => boolean) | undefined): void;
54
+ setVisible(visible: boolean): void;
55
+ /** Shapes the labels (loading their glyphs first) and sends them */
56
+ private update;
57
+ private build;
58
+ private send;
59
+ private sendLayer;
60
+ }
61
+ export declare function symbolStyle<P>(options: SymbolOptions<P>): SymbolStyle<P>;
@@ -0,0 +1,238 @@
1
+ /**
2
+ * Text labels from GeoJSON: at points, at the middle of lines, at the center of polygons. Shaped on the page, placed by
3
+ * the engine every frame with collisions shared by all symbol layers (the top layer first, each layer in data order),
4
+ * drawn with a halo, standing on the relief.
5
+ */
6
+ import { evaluate, featuresOf, parseColor, stops, } from "./features.js";
7
+ import { codes, DEFAULT_FONT, shape } from "./glyphs.js";
8
+ import { Layer } from "./layers.js";
9
+ /** Where a geometry is labelled: its points, the middle of its longest line (with the direction of the line there),
10
+ * the centroid of its largest polygon */
11
+ function anchors(geometry) {
12
+ return points(geometry).map((p) => (p.length > 2 ? { at: [p[0], p[1]], along: [p[2] ?? 0, p[3] ?? 0] } : { at: p }));
13
+ }
14
+ /** Anchors as positions, a line's carrying the direction (lon, lat, dlon, dlat) */
15
+ function points(geometry) {
16
+ if (!geometry)
17
+ return [];
18
+ switch (geometry.type) {
19
+ case 'Point':
20
+ return [geometry.coordinates];
21
+ case 'MultiPoint':
22
+ return geometry.coordinates;
23
+ case 'LineString':
24
+ case 'MultiLineString': {
25
+ const lines = geometry.type === 'LineString' ? [geometry.coordinates] : geometry.coordinates;
26
+ const length = (l) => l.slice(1).reduce((s, p, i) => s + Math.hypot(p[0] - (l[i]?.[0] ?? 0), p[1] - (l[i]?.[1] ?? 0)), 0);
27
+ const line = lines.reduce((a, b) => (length(b) > length(a) ? b : a), lines[0] ?? []);
28
+ let rest = length(line) / 2;
29
+ for (let i = 1; i < line.length; i += 1) {
30
+ const [a, b] = [line[i - 1], line[i]];
31
+ if (!a || !b)
32
+ continue;
33
+ const d = Math.hypot(b[0] - a[0], b[1] - a[1]);
34
+ if (d >= rest && d > 0) {
35
+ return [[a[0] + ((b[0] - a[0]) * rest) / d, a[1] + ((b[1] - a[1]) * rest) / d, b[0] - a[0], b[1] - a[1]]];
36
+ }
37
+ rest -= d;
38
+ }
39
+ return line[0] ? [line[0]] : [];
40
+ }
41
+ case 'Polygon':
42
+ case 'MultiPolygon': {
43
+ const polygons = geometry.type === 'Polygon' ? [geometry.coordinates] : geometry.coordinates;
44
+ let best;
45
+ let bestArea = 0;
46
+ for (const p of polygons) {
47
+ const ring = p[0] ?? [];
48
+ let [a, cx, cy] = [0, 0, 0];
49
+ for (let i = 0; i + 1 < ring.length; i += 1) {
50
+ const [x0, y0] = ring[i] ?? [0, 0];
51
+ const [x1, y1] = ring[i + 1] ?? [0, 0];
52
+ const cross = x0 * y1 - x1 * y0;
53
+ a += cross;
54
+ cx += (x0 + x1) * cross;
55
+ cy += (y0 + y1) * cross;
56
+ }
57
+ if (Math.abs(a) > bestArea) {
58
+ bestArea = Math.abs(a);
59
+ best = [cx / (3 * a), cy / (3 * a)];
60
+ }
61
+ }
62
+ return best ? [best] : [];
63
+ }
64
+ case 'GeometryCollection':
65
+ return geometry.geometries.flatMap(points);
66
+ }
67
+ }
68
+ const MAX_LAT = 85.051128779806589;
69
+ const merc = ([lon, lat]) => {
70
+ const phi = (Math.max(-MAX_LAT, Math.min(MAX_LAT, lat)) * Math.PI) / 180;
71
+ return [(lon + 180) / 360, 0.5 - Math.log(Math.tan(Math.PI / 4 + phi / 2)) / (2 * Math.PI)];
72
+ };
73
+ export class SymbolLayer extends Layer {
74
+ host;
75
+ style;
76
+ list;
77
+ /** Bumped by each change: a shaping that finished after a newer one began is dropped */
78
+ generation = 0;
79
+ labels;
80
+ /** @internal */
81
+ constructor(host, data, style) {
82
+ super(host);
83
+ this.host = host;
84
+ this.style = style;
85
+ this.list = featuresOf(data);
86
+ }
87
+ get features() {
88
+ return this.list;
89
+ }
90
+ setData(data) {
91
+ this.list = featuresOf(data);
92
+ void this.update();
93
+ }
94
+ setStyle(style) {
95
+ this.style = { ...this.style, ...style };
96
+ void this.update();
97
+ }
98
+ setFilter(filter) {
99
+ this.setStyle({ filter });
100
+ }
101
+ setVisible(visible) {
102
+ this.style = { ...this.style, visible };
103
+ this.sendLayer(this.map.engine, this.id);
104
+ this.map.requestRender();
105
+ }
106
+ /** Shapes the labels (loading their glyphs first) and sends them */
107
+ async update() {
108
+ const generation = ++this.generation;
109
+ const { text, font, filter } = this.style;
110
+ const parts = this.list.map((f, i) => {
111
+ if (filter && !filter(f.properties, i))
112
+ return [];
113
+ const t = evaluate(text, f.properties, i);
114
+ const own = evaluate(font, f.properties, i);
115
+ // Each run in its font, the feature's by default
116
+ return (typeof t === 'string' ? [{ text: t }] : t).map((p) => ({ ...p, font: p.font ?? own }));
117
+ });
118
+ const byFont = new Map();
119
+ for (const p of parts.flat()) {
120
+ const key = p.font ?? DEFAULT_FONT;
121
+ const set = byFont.get(key) ?? new Set();
122
+ for (const code of codes([p]))
123
+ set.add(code);
124
+ byFont.set(key, set);
125
+ }
126
+ const loaded = new Map();
127
+ await Promise.all([...byFont].map(async ([f, set]) => {
128
+ loaded.set(f, await this.host.glyphs.load(f, set));
129
+ }));
130
+ if (generation !== this.generation)
131
+ return;
132
+ this.labels = this.build(parts, loaded);
133
+ this.send(this.map.engine, this.id);
134
+ this.map.requestRender();
135
+ }
136
+ build(parts, loaded) {
137
+ const { font, size, anchor, offset, maxWidth, lineHeight, color, haloColor, haloWidth, overlap, placement } = this.style;
138
+ const anchorList = [];
139
+ const boxes = [];
140
+ const styles = [];
141
+ const quads = [];
142
+ const feature = [];
143
+ this.list.forEach((f, i) => {
144
+ const text = parts[i];
145
+ if (!text?.length || text.every((p) => !p.text))
146
+ return;
147
+ // Shaped at its largest size; the engine scales by the zoom stops
148
+ const s0 = evaluate(size, f.properties, i);
149
+ const shaped = typeof s0 === 'number' ? s0 : Math.max(...s0.map(([, v]) => v), 1);
150
+ const scale = typeof s0 === 'number' ? 1 : s0.map(([z, v]) => [z, v / shaped]);
151
+ const s = shape(text, (name) => loaded.get(name), {
152
+ font: evaluate(font, f.properties, i),
153
+ size: shaped,
154
+ maxWidth: evaluate(maxWidth, f.properties, i),
155
+ lineHeight: evaluate(lineHeight, f.properties, i),
156
+ anchor: evaluate(anchor, f.properties, i),
157
+ offset: evaluate(offset, f.properties, i),
158
+ });
159
+ if (!s.quads.length)
160
+ return;
161
+ const halo = evaluate(haloWidth, f.properties, i);
162
+ const fill = parseColor(evaluate(color, f.properties, i));
163
+ const haloRgba = parseColor(evaluate(haloColor, f.properties, i));
164
+ for (const { at, along } of anchors(f.geometry)) {
165
+ const label = feature.length;
166
+ feature.push(i);
167
+ const [x, y] = merc(at);
168
+ anchorList.push(x, y);
169
+ // Along the line: its direction in Mercator (y towards the south)
170
+ let direction = [0, 0];
171
+ if (placement === 'line' && along) {
172
+ const [x1, y1] = merc([at[0] + along[0], at[1] + along[1]]);
173
+ direction = [x1 - x, y1 - y];
174
+ }
175
+ // The collision box: the ink, with the halo and a little room
176
+ const pad = halo + 1;
177
+ boxes.push(s.box[0] - pad, s.box[1] - pad, s.box[2] + pad, s.box[3] + pad);
178
+ styles.push(...fill, ...haloRgba, halo, evaluate(overlap, f.properties, i) ? 1 : 0, ...direction, ...stops(scale));
179
+ for (const q of s.quads)
180
+ quads.push(label, ...q);
181
+ }
182
+ });
183
+ return {
184
+ anchors: Float64Array.from(anchorList),
185
+ boxes: Float32Array.from(boxes),
186
+ styles: Float32Array.from(styles),
187
+ quads: Float32Array.from(quads),
188
+ feature,
189
+ };
190
+ }
191
+ send(engine, id) {
192
+ const l = this.labels;
193
+ if (l)
194
+ engine.setSymbols(id, l.anchors, l.boxes, l.styles, l.quads);
195
+ this.sendLayer(engine, id);
196
+ }
197
+ sendLayer(engine, id) {
198
+ const { opacity, minZoom, maxZoom, visible } = this.style;
199
+ // The sizes are per label; the layer's own scale stays 1
200
+ engine.setSymbolLayer(id, Float32Array.from(stops(1)), Float32Array.from(stops(opacity)), minZoom, maxZoom, visible);
201
+ }
202
+ /** @internal */
203
+ create(engine) {
204
+ const id = engine.addSymbols();
205
+ if (this.labels)
206
+ this.send(engine, id);
207
+ else {
208
+ this.sendLayer(engine, id);
209
+ // The first shaping, once the id is known
210
+ queueMicrotask(() => void this.update());
211
+ }
212
+ return id;
213
+ }
214
+ }
215
+ export function symbolStyle(options) {
216
+ const { data: _, before: __, ...rest } = options;
217
+ return {
218
+ opacity: 1,
219
+ minZoom: 0,
220
+ maxZoom: 24,
221
+ visible: true,
222
+ filter: undefined,
223
+ text: '',
224
+ font: DEFAULT_FONT,
225
+ size: 12,
226
+ color: '#000',
227
+ haloColor: '#fff',
228
+ haloWidth: 0,
229
+ anchor: 'center',
230
+ placement: 'point',
231
+ offset: [0, 0],
232
+ maxWidth: 10,
233
+ lineHeight: 1.2,
234
+ overlap: false,
235
+ ...rest,
236
+ };
237
+ }
238
+ //# sourceMappingURL=symbols.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"symbols.js","sourceRoot":"","sources":["../src/symbols.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Text labels from GeoJSON: at points, at the middle of lines, at the center of polygons. Shaped on the page, placed by\n * the engine every frame with collisions shared by all symbol layers (the top layer first, each layer in data order),\n * drawn with a halo, standing on the relief.\n */\nimport {\n evaluate,\n type Feature,\n type FeatureData,\n type FeatureLayerStyle,\n featuresOf,\n type Geometry,\n type Placement,\n type Position,\n type Properties,\n parseColor,\n type Size,\n type Styled,\n stops,\n} from './features.ts';\nimport { codes, DEFAULT_FONT, type Glyph, type Glyphs, shape, type TextPart } from './glyphs.ts';\nimport { Layer } from './layers.ts';\nimport type { Anchor } from './overlay.ts';\nimport type { LayerHost } from './types.ts';\nimport type { Engine } from './wasm/isard.js';\n\nexport interface SymbolStyle<P = Properties> extends Omit<FeatureLayerStyle<P>, 'ramp'> {\n /** The text: a string (`\\n` breaks lines) or runs with their own scale and font; empty: no label */\n text: Styled<string | TextPart[], P>;\n /** A font of the map's glyphs (default `Noto Sans Regular`; also Bold and Italic with the default glyphs) */\n font: Styled<string, P>;\n /** CSS pixels, or zoom stops (default 12) */\n size: Styled<Size, P>;\n /** `'point'` (default): horizontal text; `'line'`: along the line at its middle, kept upright */\n placement: 'point' | 'line';\n color: Styled<string, P>;\n haloColor: Styled<string, P>;\n /** CSS pixels (default 0) */\n haloWidth: Styled<number, P>;\n /** Side of the text at the anchor (default `'center'`) */\n anchor: Styled<Anchor, P>;\n /** Shift, ems (default [0, 0]) */\n offset: Styled<[x: number, y: number], P>;\n /** Wrapping width, ems (default 10) */\n maxWidth: Styled<number, P>;\n /** Ems (default 1.2) */\n lineHeight: Styled<number, P>;\n /** Shown even where it overlaps other labels, and hiding none (MapLibre's allow-overlap and ignore-placement)\n * (default false) */\n overlap: Styled<boolean, P>;\n}\n\nexport interface SymbolOptions<P = Properties> extends Partial<SymbolStyle<P>>, Placement {\n data: FeatureData<P>;\n}\n\n/** Where a geometry is labelled: its points, the middle of its longest line (with the direction of the line there),\n * the centroid of its largest polygon */\nfunction anchors(geometry: Geometry | null): { at: Position; along?: [number, number] }[] {\n return points(geometry).map((p) => (p.length > 2 ? { at: [p[0], p[1]], along: [p[2] ?? 0, p[3] ?? 0] } : { at: p }));\n}\n\n/** Anchors as positions, a line's carrying the direction (lon, lat, dlon, dlat) */\nfunction points(geometry: Geometry | null): Position[] {\n if (!geometry) return [];\n switch (geometry.type) {\n case 'Point':\n return [geometry.coordinates];\n case 'MultiPoint':\n return geometry.coordinates;\n case 'LineString':\n case 'MultiLineString': {\n const lines = geometry.type === 'LineString' ? [geometry.coordinates] : geometry.coordinates;\n const length = (l: Position[]) =>\n l.slice(1).reduce((s, p, i) => s + Math.hypot(p[0] - (l[i]?.[0] ?? 0), p[1] - (l[i]?.[1] ?? 0)), 0);\n const line = lines.reduce((a, b) => (length(b) > length(a) ? b : a), lines[0] ?? []);\n let rest = length(line) / 2;\n for (let i = 1; i < line.length; i += 1) {\n const [a, b] = [line[i - 1], line[i]];\n if (!a || !b) continue;\n const d = Math.hypot(b[0] - a[0], b[1] - a[1]);\n if (d >= rest && d > 0) {\n return [[a[0] + ((b[0] - a[0]) * rest) / d, a[1] + ((b[1] - a[1]) * rest) / d, b[0] - a[0], b[1] - a[1]]];\n }\n rest -= d;\n }\n return line[0] ? [line[0]] : [];\n }\n case 'Polygon':\n case 'MultiPolygon': {\n const polygons = geometry.type === 'Polygon' ? [geometry.coordinates] : geometry.coordinates;\n let best: Position | undefined;\n let bestArea = 0;\n for (const p of polygons) {\n const ring = p[0] ?? [];\n let [a, cx, cy] = [0, 0, 0];\n for (let i = 0; i + 1 < ring.length; i += 1) {\n const [x0, y0] = ring[i] ?? [0, 0];\n const [x1, y1] = ring[i + 1] ?? [0, 0];\n const cross = x0 * y1 - x1 * y0;\n a += cross;\n cx += (x0 + x1) * cross;\n cy += (y0 + y1) * cross;\n }\n if (Math.abs(a) > bestArea) {\n bestArea = Math.abs(a);\n best = [cx / (3 * a), cy / (3 * a)];\n }\n }\n return best ? [best] : [];\n }\n case 'GeometryCollection':\n return geometry.geometries.flatMap(points);\n }\n}\n\nconst MAX_LAT = 85.051128779806589;\nconst merc = ([lon, lat]: Position): [number, number] => {\n const phi = (Math.max(-MAX_LAT, Math.min(MAX_LAT, lat)) * Math.PI) / 180;\n return [(lon + 180) / 360, 0.5 - Math.log(Math.tan(Math.PI / 4 + phi / 2)) / (2 * Math.PI)];\n};\n\n/** The map's glyphs, for a layer */\nexport interface GlyphHost extends LayerHost {\n readonly glyphs: Glyphs;\n}\n\nexport class SymbolLayer<P = Properties> extends Layer {\n private list: Feature<P>[];\n /** Bumped by each change: a shaping that finished after a newer one began is dropped */\n private generation = 0;\n private labels:\n | { anchors: Float64Array; boxes: Float32Array; styles: Float32Array; quads: Float32Array; feature: number[] }\n | undefined;\n\n /** @internal */\n constructor(\n private readonly host: GlyphHost,\n data: FeatureData<P>,\n private style: SymbolStyle<P>,\n ) {\n super(host);\n this.list = featuresOf(data);\n }\n\n get features(): readonly Feature<P>[] {\n return this.list;\n }\n\n setData(data: FeatureData<P>): void {\n this.list = featuresOf(data);\n void this.update();\n }\n\n setStyle(style: Partial<SymbolStyle<P>>): void {\n this.style = { ...this.style, ...style };\n void this.update();\n }\n\n setFilter(filter: ((properties: P, index: number) => boolean) | undefined): void {\n this.setStyle({ filter } as Partial<SymbolStyle<P>>);\n }\n\n setVisible(visible: boolean): void {\n this.style = { ...this.style, visible };\n this.sendLayer(this.map.engine, this.id);\n this.map.requestRender();\n }\n\n /** Shapes the labels (loading their glyphs first) and sends them */\n private async update(): Promise<void> {\n const generation = ++this.generation;\n const { text, font, filter } = this.style;\n const parts = this.list.map((f, i): TextPart[] => {\n if (filter && !filter(f.properties, i)) return [];\n const t = evaluate(text, f.properties, i);\n const own = evaluate(font, f.properties, i);\n // Each run in its font, the feature's by default\n return (typeof t === 'string' ? [{ text: t }] : t).map((p) => ({ ...p, font: p.font ?? own }));\n });\n const byFont = new Map<string, Set<number>>();\n for (const p of parts.flat()) {\n const key = p.font ?? DEFAULT_FONT;\n const set = byFont.get(key) ?? new Set<number>();\n for (const code of codes([p])) set.add(code);\n byFont.set(key, set);\n }\n const loaded = new Map<string, Map<number, Glyph>>();\n await Promise.all(\n [...byFont].map(async ([f, set]) => {\n loaded.set(f, await this.host.glyphs.load(f, set));\n }),\n );\n if (generation !== this.generation) return;\n this.labels = this.build(parts, loaded);\n this.send(this.map.engine, this.id);\n this.map.requestRender();\n }\n\n private build(parts: TextPart[][], loaded: Map<string, Map<number, Glyph>>) {\n const { font, size, anchor, offset, maxWidth, lineHeight, color, haloColor, haloWidth, overlap, placement } =\n this.style;\n const anchorList: number[] = [];\n const boxes: number[] = [];\n const styles: number[] = [];\n const quads: number[] = [];\n const feature: number[] = [];\n this.list.forEach((f, i) => {\n const text = parts[i];\n if (!text?.length || text.every((p) => !p.text)) return;\n // Shaped at its largest size; the engine scales by the zoom stops\n const s0 = evaluate(size, f.properties, i);\n const shaped = typeof s0 === 'number' ? s0 : Math.max(...s0.map(([, v]) => v), 1);\n const scale: Size = typeof s0 === 'number' ? 1 : s0.map(([z, v]): [number, number] => [z, v / shaped]);\n const s = shape(text, (name) => loaded.get(name), {\n font: evaluate(font, f.properties, i),\n size: shaped,\n maxWidth: evaluate(maxWidth, f.properties, i),\n lineHeight: evaluate(lineHeight, f.properties, i),\n anchor: evaluate(anchor, f.properties, i),\n offset: evaluate(offset, f.properties, i),\n });\n if (!s.quads.length) return;\n const halo = evaluate(haloWidth, f.properties, i);\n const fill = parseColor(evaluate(color, f.properties, i));\n const haloRgba = parseColor(evaluate(haloColor, f.properties, i));\n for (const { at, along } of anchors(f.geometry)) {\n const label = feature.length;\n feature.push(i);\n const [x, y] = merc(at);\n anchorList.push(x, y);\n // Along the line: its direction in Mercator (y towards the south)\n let direction: [number, number] = [0, 0];\n if (placement === 'line' && along) {\n const [x1, y1] = merc([at[0] + along[0], at[1] + along[1]]);\n direction = [x1 - x, y1 - y];\n }\n // The collision box: the ink, with the halo and a little room\n const pad = halo + 1;\n boxes.push(s.box[0] - pad, s.box[1] - pad, s.box[2] + pad, s.box[3] + pad);\n styles.push(\n ...fill,\n ...haloRgba,\n halo,\n evaluate(overlap, f.properties, i) ? 1 : 0,\n ...direction,\n ...stops(scale),\n );\n for (const q of s.quads) quads.push(label, ...q);\n }\n });\n return {\n anchors: Float64Array.from(anchorList),\n boxes: Float32Array.from(boxes),\n styles: Float32Array.from(styles),\n quads: Float32Array.from(quads),\n feature,\n };\n }\n\n private send(engine: Engine, id: number): void {\n const l = this.labels;\n if (l) engine.setSymbols(id, l.anchors, l.boxes, l.styles, l.quads);\n this.sendLayer(engine, id);\n }\n\n private sendLayer(engine: Engine, id: number): void {\n const { opacity, minZoom, maxZoom, visible } = this.style;\n // The sizes are per label; the layer's own scale stays 1\n engine.setSymbolLayer(\n id,\n Float32Array.from(stops(1)),\n Float32Array.from(stops(opacity)),\n minZoom,\n maxZoom,\n visible,\n );\n }\n\n /** @internal */\n protected create(engine: Engine): number {\n const id = engine.addSymbols();\n if (this.labels) this.send(engine, id);\n else {\n this.sendLayer(engine, id);\n // The first shaping, once the id is known\n queueMicrotask(() => void this.update());\n }\n return id;\n }\n}\n\nexport function symbolStyle<P>(options: SymbolOptions<P>): SymbolStyle<P> {\n const { data: _, before: __, ...rest } = options;\n return {\n opacity: 1,\n minZoom: 0,\n maxZoom: 24,\n visible: true,\n filter: undefined,\n text: '',\n font: DEFAULT_FONT,\n size: 12,\n color: '#000',\n haloColor: '#fff',\n haloWidth: 0,\n anchor: 'center',\n placement: 'point',\n offset: [0, 0],\n maxWidth: 10,\n lineHeight: 1.2,\n overlap: false,\n ...rest,\n };\n}\n"]}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Altitudes encoded in the colors of PNG tiles, decoded in the page for the relief: Terrarium (Mapzen, the Terrain
3
+ * Tiles of AWS) and Terrain-RGB (Mapbox, MapTiler). The engine reads raw floats only; a loader gives it the altitudes
4
+ * of each tile.
5
+ */
6
+ import type { TerrainEncoding } from './types.ts';
7
+ /** Altitudes (metres, rows from the north) of RGBA pixels in an encoding; below `floor` raised to it */
8
+ export declare function decodeHeights(rgba: Uint8ClampedArray | Uint8Array, encoding: Exclude<TerrainEncoding, 'float32'>, floor?: number): Float32Array;
9
+ /**
10
+ * A loader of the engine's relief for PNG tiles of `url` ({z}, {x}, {y}) in an encoding: 256 × 256 altitudes, null
11
+ * where the server has no tile; a network or server error rejects (retried). `floor`: the lowest altitude kept (the
12
+ * sea floor of a worldwide model flattened at sea level)
13
+ */
14
+ export declare function encodedTerrainLoader(url: string, encoding: Exclude<TerrainEncoding, 'float32'>, floor?: number): (z: number, x: number, y: number, signal: AbortSignal) => Promise<Float32Array | null>;
@@ -0,0 +1,56 @@
1
+ const SIZE = 256;
2
+ /** Altitudes (metres, rows from the north) of RGBA pixels in an encoding; below `floor` raised to it */
3
+ export function decodeHeights(rgba, encoding, floor = Number.NEGATIVE_INFINITY) {
4
+ const n = rgba.length >> 2;
5
+ const out = new Float32Array(n);
6
+ // A loop per encoding, without allocations: 65 536 pixels a tile
7
+ if (encoding === 'terrarium') {
8
+ for (let i = 0, j = 0; i < n; i += 1, j += 4) {
9
+ const h = rgba[j] * 256 + rgba[j + 1] + rgba[j + 2] / 256 - 32768;
10
+ out[i] = h > floor ? h : floor;
11
+ }
12
+ }
13
+ else {
14
+ for (let i = 0, j = 0; i < n; i += 1, j += 4) {
15
+ const h = -10000 + (rgba[j] * 65536 + rgba[j + 1] * 256 + rgba[j + 2]) * 0.1;
16
+ out[i] = h > floor ? h : floor;
17
+ }
18
+ }
19
+ return out;
20
+ }
21
+ /**
22
+ * A loader of the engine's relief for PNG tiles of `url` ({z}, {x}, {y}) in an encoding: 256 × 256 altitudes, null
23
+ * where the server has no tile; a network or server error rejects (retried). `floor`: the lowest altitude kept (the
24
+ * sea floor of a worldwide model flattened at sea level)
25
+ */
26
+ export function encodedTerrainLoader(url, encoding, floor) {
27
+ let canvas;
28
+ return async (z, x, y, signal) => {
29
+ const tile = url.replaceAll('{z}', String(z)).replaceAll('{x}', String(x)).replaceAll('{y}', String(y));
30
+ const response = await fetch(tile, { signal });
31
+ if (response.status === 404 || response.status === 204 || response.status === 410)
32
+ return null;
33
+ if (!response.ok)
34
+ throw new Error(`${response.url}: ${response.status}`);
35
+ // The colors as stored: no color management, no premultiplied alpha
36
+ const bitmap = await createImageBitmap(await response.blob(), {
37
+ premultiplyAlpha: 'none',
38
+ colorSpaceConversion: 'none',
39
+ });
40
+ try {
41
+ canvas ??= new OffscreenCanvas(SIZE, SIZE);
42
+ const g = canvas.getContext('2d', { willReadFrequently: true });
43
+ if (!g)
44
+ throw new Error('no 2D context');
45
+ // Another size is resampled to the nearest pixel: an interpolated color is no altitude
46
+ g.imageSmoothingEnabled = false;
47
+ g.clearRect(0, 0, SIZE, SIZE);
48
+ g.drawImage(bitmap, 0, 0, SIZE, SIZE);
49
+ return decodeHeights(g.getImageData(0, 0, SIZE, SIZE).data, encoding, floor);
50
+ }
51
+ finally {
52
+ bitmap.close();
53
+ }
54
+ };
55
+ }
56
+ //# sourceMappingURL=terrain.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"terrain.js","sourceRoot":"","sources":["../src/terrain.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Altitudes encoded in the colors of PNG tiles, decoded in the page for the relief: Terrarium (Mapzen, the Terrain\n * Tiles of AWS) and Terrain-RGB (Mapbox, MapTiler). The engine reads raw floats only; a loader gives it the altitudes\n * of each tile.\n */\nimport type { TerrainEncoding } from './types.ts';\n\nconst SIZE = 256;\n\n/** Altitudes (metres, rows from the north) of RGBA pixels in an encoding; below `floor` raised to it */\nexport function decodeHeights(\n rgba: Uint8ClampedArray | Uint8Array,\n encoding: Exclude<TerrainEncoding, 'float32'>,\n floor = Number.NEGATIVE_INFINITY,\n): Float32Array {\n const n = rgba.length >> 2;\n const out = new Float32Array(n);\n // A loop per encoding, without allocations: 65 536 pixels a tile\n if (encoding === 'terrarium') {\n for (let i = 0, j = 0; i < n; i += 1, j += 4) {\n const h = (rgba[j] as number) * 256 + (rgba[j + 1] as number) + (rgba[j + 2] as number) / 256 - 32768;\n out[i] = h > floor ? h : floor;\n }\n } else {\n for (let i = 0, j = 0; i < n; i += 1, j += 4) {\n const h = -10000 + ((rgba[j] as number) * 65536 + (rgba[j + 1] as number) * 256 + (rgba[j + 2] as number)) * 0.1;\n out[i] = h > floor ? h : floor;\n }\n }\n return out;\n}\n\n/**\n * A loader of the engine's relief for PNG tiles of `url` ({z}, {x}, {y}) in an encoding: 256 × 256 altitudes, null\n * where the server has no tile; a network or server error rejects (retried). `floor`: the lowest altitude kept (the\n * sea floor of a worldwide model flattened at sea level)\n */\nexport function encodedTerrainLoader(\n url: string,\n encoding: Exclude<TerrainEncoding, 'float32'>,\n floor?: number,\n): (z: number, x: number, y: number, signal: AbortSignal) => Promise<Float32Array | null> {\n let canvas: OffscreenCanvas | undefined;\n return async (z, x, y, signal) => {\n const tile = url.replaceAll('{z}', String(z)).replaceAll('{x}', String(x)).replaceAll('{y}', String(y));\n const response = await fetch(tile, { signal });\n if (response.status === 404 || response.status === 204 || response.status === 410) return null;\n if (!response.ok) throw new Error(`${response.url}: ${response.status}`);\n // The colors as stored: no color management, no premultiplied alpha\n const bitmap = await createImageBitmap(await response.blob(), {\n premultiplyAlpha: 'none',\n colorSpaceConversion: 'none',\n });\n try {\n canvas ??= new OffscreenCanvas(SIZE, SIZE);\n const g = canvas.getContext('2d', { willReadFrequently: true });\n if (!g) throw new Error('no 2D context');\n // Another size is resampled to the nearest pixel: an interpolated color is no altitude\n g.imageSmoothingEnabled = false;\n g.clearRect(0, 0, SIZE, SIZE);\n g.drawImage(bitmap, 0, 0, SIZE, SIZE);\n return decodeHeights(g.getImageData(0, 0, SIZE, SIZE).data, encoding, floor);\n } finally {\n bitmap.close();\n }\n };\n}\n"]}