autumnplot-gl 4.0.0-beta → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -207
- package/dist/812.autumnplot-gl.js +2 -0
- package/dist/812.autumnplot-gl.js.map +1 -0
- package/dist/983.autumnplot-gl.js +2 -0
- package/dist/983.autumnplot-gl.js.map +1 -0
- package/dist/autumnplot-gl.js +1 -1
- package/dist/autumnplot-gl.js.map +1 -1
- package/dist/marchingsquares.wasm +0 -0
- package/lib/AutumnTypes.d.ts +38 -5
- package/lib/AutumnTypes.js +7 -1
- package/lib/Barbs.d.ts +12 -2
- package/lib/Barbs.js +9 -0
- package/lib/BillboardCollection.d.ts +2 -2
- package/lib/BillboardCollection.js +14 -14
- package/lib/Color.d.ts +1 -0
- package/lib/Color.js +1 -0
- package/lib/ColorBar.d.ts +14 -0
- package/lib/ColorBar.js +15 -8
- package/lib/Colormap.d.ts +9 -1
- package/lib/Colormap.js +24 -1
- package/lib/Contour.d.ts +26 -1
- package/lib/Contour.js +24 -2
- package/lib/ContourCreator.worker.d.ts +25 -0
- package/lib/{ContourCreator.js → ContourCreator.worker.js} +15 -14
- package/lib/Fill.d.ts +31 -11
- package/lib/Fill.js +38 -18
- package/lib/Hodographs.d.ts +19 -3
- package/lib/Hodographs.js +45 -20
- package/lib/Map.d.ts +13 -1
- package/lib/Map.js +62 -8
- package/lib/Paintball.d.ts +14 -5
- package/lib/Paintball.js +96 -46
- package/lib/PlotComponent.d.ts +9 -3
- package/lib/PlotComponent.js +36 -1
- package/lib/PlotLayer.d.ts +2 -2
- package/lib/PlotLayer.js +2 -2
- package/lib/PlotLayer.worker.js +9 -3
- package/lib/RawField.d.ts +223 -27
- package/lib/RawField.js +413 -59
- package/lib/StationPlot.d.ts +78 -11
- package/lib/StationPlot.js +113 -30
- package/lib/TextCollection.d.ts +5 -0
- package/lib/TextCollection.js +82 -9
- package/lib/WasmInterface.d.ts +7 -0
- package/lib/WasmInterface.js +11 -0
- package/lib/WorkerPool.d.ts +8 -0
- package/lib/WorkerPool.js +77 -0
- package/lib/cpp/marchingsquares.js +127 -13
- package/lib/cpp/marchingsquares.wasm +0 -0
- package/lib/cpp/marchingsquares_embind.d.ts +16 -3
- package/lib/grids/AutoZoom.d.ts +21 -0
- package/lib/grids/AutoZoom.js +63 -0
- package/lib/grids/DomainBuffer.d.ts +14 -0
- package/lib/grids/DomainBuffer.js +16 -0
- package/lib/grids/Geostationary.d.ts +35 -0
- package/lib/grids/Geostationary.js +47 -0
- package/lib/grids/Grid.d.ts +36 -0
- package/lib/grids/Grid.js +12 -0
- package/lib/grids/GridCoordinates.d.ts +10 -0
- package/lib/grids/GridCoordinates.js +64 -0
- package/lib/grids/LambertGrid.d.ts +73 -0
- package/lib/grids/LambertGrid.js +92 -0
- package/lib/grids/PlateCarreeGrid.d.ts +46 -0
- package/lib/grids/PlateCarreeGrid.js +55 -0
- package/lib/grids/PlateCarreeRotatedGrid.d.ts +53 -0
- package/lib/grids/PlateCarreeRotatedGrid.js +65 -0
- package/lib/grids/RadarSweepGrid.d.ts +46 -0
- package/lib/grids/RadarSweepGrid.js +74 -0
- package/lib/grids/StructuredGrid.d.ts +49 -0
- package/lib/grids/StructuredGrid.js +103 -0
- package/lib/grids/UnstructuredGrid.d.ts +56 -0
- package/lib/grids/UnstructuredGrid.js +102 -0
- package/lib/index.d.ts +23 -6
- package/lib/index.js +18 -8
- package/lib/utils.d.ts +11 -2
- package/lib/utils.js +63 -1
- package/package.json +4 -3
- package/dist/110.autumnplot-gl.js +0 -2
- package/dist/110.autumnplot-gl.js.map +0 -1
- package/lib/ContourCreator.d.ts +0 -22
- package/lib/Grid.d.ts +0 -263
- package/lib/Grid.js +0 -547
- package/lib/ParticleTracer.d.ts +0 -19
- package/lib/ParticleTracer.js +0 -37
package/lib/StationPlot.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { RenderMethodArg, WebGLAnyRenderingContext } from "./AutumnTypes";
|
|
2
2
|
import { MapLikeType } from "./Map";
|
|
3
3
|
import { PlotComponent } from "./PlotComponent";
|
|
4
|
-
import {
|
|
4
|
+
import { AutoZoomGrid } from "./grids/AutoZoom";
|
|
5
5
|
import { RawObsField } from "./RawField";
|
|
6
|
+
import { ColorMap } from "./Colormap";
|
|
6
7
|
/**
|
|
7
8
|
* Positions around the station plot at which to draw the various elements
|
|
8
9
|
*
|
|
@@ -19,6 +20,7 @@ import { RawObsField } from "./RawField";
|
|
|
19
20
|
* | `'c'` | center |
|
|
20
21
|
*/
|
|
21
22
|
type SPPosition = 'cl' | 'll' | 'lc' | 'lr' | 'cr' | 'ur' | 'uc' | 'ul' | 'c';
|
|
23
|
+
/** Configuration for numerical values on station plots */
|
|
22
24
|
interface SPNumberConfig {
|
|
23
25
|
type: 'number';
|
|
24
26
|
/**
|
|
@@ -30,6 +32,21 @@ interface SPNumberConfig {
|
|
|
30
32
|
* @default '#000000'
|
|
31
33
|
*/
|
|
32
34
|
color?: string;
|
|
35
|
+
/**
|
|
36
|
+
* A colormap to use for coloring numeric values
|
|
37
|
+
* @default null
|
|
38
|
+
*/
|
|
39
|
+
cmap?: ColorMap | null;
|
|
40
|
+
/**
|
|
41
|
+
* Whether to draw a halo (outline) around the number
|
|
42
|
+
* @default true;
|
|
43
|
+
*/
|
|
44
|
+
halo?: boolean;
|
|
45
|
+
/**
|
|
46
|
+
* The color to use for the halo (outline)
|
|
47
|
+
* @default '#ffffff'
|
|
48
|
+
*/
|
|
49
|
+
halo_color?: string;
|
|
33
50
|
/**
|
|
34
51
|
* A function that properly formats the number for display
|
|
35
52
|
* @example (val) => val === null ? '' : val.toFixed(0)
|
|
@@ -38,22 +55,34 @@ interface SPNumberConfig {
|
|
|
38
55
|
*/
|
|
39
56
|
formatter?: (val: number | null) => string;
|
|
40
57
|
}
|
|
58
|
+
/** Configuration for strings on station plots */
|
|
41
59
|
interface SPStringConfig {
|
|
42
60
|
type: 'string';
|
|
43
61
|
/**
|
|
44
|
-
* The position on the station plot at which to place the
|
|
62
|
+
* The position on the station plot at which to place the string
|
|
45
63
|
*/
|
|
46
64
|
pos: SPPosition;
|
|
47
65
|
/**
|
|
48
|
-
* The color to use to draw the
|
|
66
|
+
* The color to use to draw the string
|
|
49
67
|
* @default '#000000'
|
|
50
68
|
*/
|
|
51
69
|
color?: string;
|
|
70
|
+
/**
|
|
71
|
+
* Whether to draw a halo (outline) around the string
|
|
72
|
+
* @default true;
|
|
73
|
+
*/
|
|
74
|
+
halo?: boolean;
|
|
75
|
+
/**
|
|
76
|
+
* The color to use for the halo (outline)
|
|
77
|
+
* @default '#ffffff'
|
|
78
|
+
*/
|
|
79
|
+
halo_color?: string;
|
|
52
80
|
}
|
|
81
|
+
/** Configuration for wind barbs on station plots */
|
|
53
82
|
interface SPBarbConfig {
|
|
54
83
|
type: 'barb';
|
|
55
84
|
/**
|
|
56
|
-
* The color to use to draw the
|
|
85
|
+
* The color to use to draw the barb
|
|
57
86
|
* @default '#000000'
|
|
58
87
|
*/
|
|
59
88
|
color?: string;
|
|
@@ -67,18 +96,30 @@ interface SPBarbConfig {
|
|
|
67
96
|
* Accepted symbol codes for sky cover and present weather symbols
|
|
68
97
|
*/
|
|
69
98
|
type SPSymbol = ('0/8' | '1/8' | '2/8' | '3/8' | '4/8' | '5/8' | '6/8' | '7/8' | '8/8' | 'clr' | 'few' | 'sct' | 'bkn' | 'ovc' | 'obsc' | 'va' | 'fu' | 'hz' | 'du' | 'bldu' | 'sa' | 'blsa' | 'vcblsa' | 'vcbldu' | 'blpy' | 'po' | 'vcpo' | 'vcds' | 'vcss' | 'br' | 'bcbr' | 'bc' | 'mifg' | 'vcts' | 'virga' | 'vcsh' | 'ts' | 'thdr' | 'vctshz' | 'tsfzfg' | 'tsbr' | 'tsdz' | 'vctsup' | '-tsup' | 'tsup' | '+tsup' | 'sq' | 'fc' | '+fc' | 'ds' | 'ss' | 'drsa' | 'drdu' | '+ds' | '+ss' | 'drsn' | '+drsn' | '-blsn' | 'blsn' | '+blsn' | 'vcblsn' | 'vcfg' | 'bcfg' | 'prfg' | 'fg' | 'fzfg' | '-vctsdz' | '-dz' | '-dzbr' | 'vctsdz' | 'dz' | '+vctsdz' | '+dz' | '-fzdz' | '-fzdzsn' | 'fzdz' | '+fzdz' | 'fzdzsn' | '-dzra' | 'dzra' | '+dzra' | '-ra' | '-rabr' | 'ra' | 'rabr' | 'rafg' | 'vcra' | '+ra' | '-fzra' | '-fzrasn' | '-fzrabr' | '-fzrapl' | '-fzrasnpl' | 'tsfzrapl' | '-tsfzra' | 'fzra' | '+fzra' | 'fzrasn' | 'tsfzra' | '-dzsn' | '-rasn' | '-snra' | '-sndz' | 'rasn' | '+rasn' | 'snra' | 'dzsn' | 'sndz' | '+dzsn' | '+sndz' | '-sn' | '-snbr' | 'sn' | '+sn' | '-snsg' | 'sg' | '-sg' | 'ic' | '-fzdzpl' | '-fzdzplsn' | 'fzdzpl' | '-fzraplsn' | 'fzrapl' | '+fzrapl' | '-rapl' | '-rasnpl' | '-raplsn' | '+rapl' | 'rapl' | '-snpl' | 'snpl' | '-pl' | 'pl' | '-plsn' | '-plra' | 'plra' | '-pldz' | '+pl' | 'plsn' | 'plup' | '+plsn' | '-sh' | '-shra' | 'sh' | 'shra' | '+sh' | '+shra' | '-shrasn' | '-shsnra' | '+shrabr' | 'shrasn' | '+shrasn' | 'shsnra' | '+shsnra' | '-shsn' | 'shsn' | '+shsn' | '-gs' | '-shgs' | 'fzraplgs' | '-sngs' | 'gsplsn' | 'gspl' | 'plgssn' | 'gs' | 'shgs' | '+gs' | '+shgs' | '-gr' | '-shgr' | '-sngr' | 'gr' | 'shgr' | '+gr' | '+shgr' | '-tsrasn' | 'tsrasn' | '-tssnra' | 'tssnra' | '-vctsra' | '-tsra' | 'tsra' | '-tsdz' | 'vctsra' | 'tspl' | '-tssn' | '-tspl' | 'tssn' | '-vctssn' | 'vctssn' | 'tsplsn' | 'tssnpl' | '-tssnpl' | '-tsragr' | 'tsrags' | 'tsragr' | 'tsgs' | 'tsgr' | '+tsfzrapl' | '+vctsra' | '+tsra' | '+tsfzra' | '+tssn' | '+tspl' | '+tsplsn' | '+vctssn' | 'tssa' | 'tsds' | 'tsdu' | '+tsgs' | '+tsgr' | '+tsrags' | '+tsragr' | 'in' | '-up' | 'up' | '+up' | '-fzup' | 'fzup' | '+fzup');
|
|
99
|
+
/** Configuration for symbols on station plots */
|
|
70
100
|
interface SPSymbolConfig {
|
|
71
101
|
type: 'symbol';
|
|
72
102
|
/**
|
|
73
|
-
* The position on the station plot at which to place the
|
|
103
|
+
* The position on the station plot at which to place the symbol
|
|
74
104
|
*/
|
|
75
105
|
pos: SPPosition;
|
|
76
106
|
/**
|
|
77
|
-
* The color to use to draw the
|
|
107
|
+
* The color to use to draw the symbol
|
|
78
108
|
* @default '#000000'
|
|
79
109
|
*/
|
|
80
|
-
color?: string;
|
|
110
|
+
color?: string | ((symbol: SPSymbol | null, category: SPSymbolCategory) => string);
|
|
111
|
+
/**
|
|
112
|
+
* Whether to draw a halo (outline) around the string
|
|
113
|
+
* @default true;
|
|
114
|
+
*/
|
|
115
|
+
halo?: boolean;
|
|
116
|
+
/**
|
|
117
|
+
* The color to use for the halo (outline)
|
|
118
|
+
* @default '#ffffff'
|
|
119
|
+
*/
|
|
120
|
+
halo_color?: string;
|
|
81
121
|
}
|
|
122
|
+
/** Configuration for station plot sub-elements */
|
|
82
123
|
type SPConfig = SPNumberConfig | SPStringConfig | SPBarbConfig | SPSymbolConfig;
|
|
83
124
|
/**
|
|
84
125
|
* Configuration for station data plots
|
|
@@ -98,6 +139,7 @@ type SPConfig = SPNumberConfig | SPStringConfig | SPBarbConfig | SPSymbolConfig;
|
|
|
98
139
|
* }
|
|
99
140
|
*/
|
|
100
141
|
type SPDataConfig<ObsFieldName extends string> = Record<ObsFieldName, SPConfig>;
|
|
142
|
+
/** Options for {@link StationPlot} components */
|
|
101
143
|
interface StationPlotOptions<ObsFieldName extends string> {
|
|
102
144
|
config: SPDataConfig<ObsFieldName>;
|
|
103
145
|
/**
|
|
@@ -120,15 +162,40 @@ interface StationPlotOptions<ObsFieldName extends string> {
|
|
|
120
162
|
*/
|
|
121
163
|
font_url_template?: string;
|
|
122
164
|
}
|
|
123
|
-
|
|
165
|
+
type SPSymbolCategory = 'freezing_rain' | 'sleet' | 'snow' | 'rain' | 'blowing_dust' | 'thunder' | 'fog' | 'none';
|
|
166
|
+
/**
|
|
167
|
+
* Station model plots for observed data
|
|
168
|
+
*
|
|
169
|
+
* ## Grid Compatibility
|
|
170
|
+
* - :white_check_mark: `PlateCarreeGrid`
|
|
171
|
+
* - :white_check_mark: `PlateCarreeRotatedGrid`
|
|
172
|
+
* - :white_check_mark: `LambertGrid`
|
|
173
|
+
* - :white_check_mark: `UnstructuredGrid`
|
|
174
|
+
* - :x: `RadarSweepGrid`
|
|
175
|
+
* - :x: `Geostationary`
|
|
176
|
+
*
|
|
177
|
+
* @example
|
|
178
|
+
* // Specify how to set up the station plot
|
|
179
|
+
* const station_plot_locs = {
|
|
180
|
+
* tmpf: {type: 'number', pos: 'ul', color: '#cc0000', formatter: val => val === null ? '' : val.toFixed(0)},
|
|
181
|
+
* dwpf: {type: 'number', pos: 'll', color: '#00aa00', formatter: val => val === null ? '' : val.toFixed(0)},
|
|
182
|
+
* wind: {type: 'barb', pos: 'c'},
|
|
183
|
+
* preswx: {type: 'symbol', pos: 'cl', color: '#ff00ff'},
|
|
184
|
+
* skyc: {type: 'symbol', pos: 'c'},
|
|
185
|
+
* };
|
|
186
|
+
*
|
|
187
|
+
* // Create the station plot
|
|
188
|
+
* const station_plot = new StationPlot(obs_field, {config: station_plot_locs, thin_fac: 8, font_size: 14});
|
|
189
|
+
*/
|
|
190
|
+
declare class StationPlot<GridType extends AutoZoomGrid, MapType extends MapLikeType, ObsFieldName extends string> extends PlotComponent<MapType> {
|
|
124
191
|
private field;
|
|
125
192
|
readonly opts: Required<StationPlotOptions<ObsFieldName>>;
|
|
126
193
|
private gl_elems;
|
|
127
194
|
private text_components;
|
|
128
195
|
/**
|
|
129
|
-
*
|
|
130
|
-
* @param field
|
|
131
|
-
* @param opts
|
|
196
|
+
* Create station plots
|
|
197
|
+
* @param field - A field containing the observed data
|
|
198
|
+
* @param opts - Various options for the station plots
|
|
132
199
|
*/
|
|
133
200
|
constructor(field: RawObsField<GridType, ObsFieldName>, opts: StationPlotOptions<ObsFieldName>);
|
|
134
201
|
/**
|
package/lib/StationPlot.js
CHANGED
|
@@ -76,11 +76,53 @@ function positionToAlignmentAndOffset(pos, off_size) {
|
|
|
76
76
|
}
|
|
77
77
|
return { horizontal_align: ha, vertical_align: va, offset_x: xoff, offset_y: yoff };
|
|
78
78
|
}
|
|
79
|
+
function symbolCategory(symbol) {
|
|
80
|
+
const sym = symbol.toLowerCase();
|
|
81
|
+
if (sym.includes('fzra') || (sym.includes('fz') && sym.includes('ra')))
|
|
82
|
+
return 'freezing_rain';
|
|
83
|
+
if (sym.includes('pl'))
|
|
84
|
+
return 'sleet';
|
|
85
|
+
if (sym.includes('sn') || sym.includes('sg') || sym.includes('gs'))
|
|
86
|
+
return 'snow';
|
|
87
|
+
if (sym.includes('ra') || sym.includes('dz'))
|
|
88
|
+
return 'rain';
|
|
89
|
+
if (sym.includes('du') || sym.includes('ds') || sym.includes('ss') || sym.includes('blsa') || sym.includes('bldu') || sym.includes('blpy'))
|
|
90
|
+
return 'blowing_dust';
|
|
91
|
+
if (sym.includes('ts') || sym.includes('thdr'))
|
|
92
|
+
return 'thunder';
|
|
93
|
+
if (sym.includes('fg'))
|
|
94
|
+
return 'fog';
|
|
95
|
+
return 'none';
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Station model plots for observed data
|
|
99
|
+
*
|
|
100
|
+
* ## Grid Compatibility
|
|
101
|
+
* - :white_check_mark: `PlateCarreeGrid`
|
|
102
|
+
* - :white_check_mark: `PlateCarreeRotatedGrid`
|
|
103
|
+
* - :white_check_mark: `LambertGrid`
|
|
104
|
+
* - :white_check_mark: `UnstructuredGrid`
|
|
105
|
+
* - :x: `RadarSweepGrid`
|
|
106
|
+
* - :x: `Geostationary`
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* // Specify how to set up the station plot
|
|
110
|
+
* const station_plot_locs = {
|
|
111
|
+
* tmpf: {type: 'number', pos: 'ul', color: '#cc0000', formatter: val => val === null ? '' : val.toFixed(0)},
|
|
112
|
+
* dwpf: {type: 'number', pos: 'll', color: '#00aa00', formatter: val => val === null ? '' : val.toFixed(0)},
|
|
113
|
+
* wind: {type: 'barb', pos: 'c'},
|
|
114
|
+
* preswx: {type: 'symbol', pos: 'cl', color: '#ff00ff'},
|
|
115
|
+
* skyc: {type: 'symbol', pos: 'c'},
|
|
116
|
+
* };
|
|
117
|
+
*
|
|
118
|
+
* // Create the station plot
|
|
119
|
+
* const station_plot = new StationPlot(obs_field, {config: station_plot_locs, thin_fac: 8, font_size: 14});
|
|
120
|
+
*/
|
|
79
121
|
class StationPlot extends PlotComponent {
|
|
80
122
|
/**
|
|
81
|
-
*
|
|
82
|
-
* @param field
|
|
83
|
-
* @param opts
|
|
123
|
+
* Create station plots
|
|
124
|
+
* @param field - A field containing the observed data
|
|
125
|
+
* @param opts - Various options for the station plots
|
|
84
126
|
*/
|
|
85
127
|
constructor(field, opts) {
|
|
86
128
|
super();
|
|
@@ -105,31 +147,51 @@ class StationPlot extends PlotComponent {
|
|
|
105
147
|
if (font_url_template === undefined)
|
|
106
148
|
throw "The map style doesn't have any glyph information. Please pass the font_url_template option to StationPlot";
|
|
107
149
|
const font_url = font_url_template.replace('{fontstack}', this.opts.font_face);
|
|
150
|
+
const coords = this.field.grid.getEarthCoords();
|
|
151
|
+
const zoom = this.field.grid.getMinVisibleZoom(this.opts.thin_fac);
|
|
108
152
|
let ibarb = 0;
|
|
109
|
-
|
|
153
|
+
let sub_component_promises = [];
|
|
154
|
+
Object.entries(this.opts.config).forEach(async ([k_, config]) => {
|
|
110
155
|
const k = k_;
|
|
111
156
|
if (config.type == 'number' || config.type == 'string') {
|
|
112
157
|
const pos = config.pos;
|
|
113
|
-
const
|
|
114
|
-
const
|
|
115
|
-
const
|
|
116
|
-
|
|
158
|
+
const color = config.color === undefined ? Color.fromHex('#000000') : Color.normalizeColor(config.color);
|
|
159
|
+
const halo_color = config.halo_color === undefined ? Color.fromHex('#ffffff') : Color.normalizeColor(config.halo_color);
|
|
160
|
+
const halo = config.halo === undefined ? true : config.halo;
|
|
161
|
+
let cmap = null;
|
|
117
162
|
let text_specs;
|
|
118
163
|
if (config.type == 'number') {
|
|
164
|
+
cmap = config.cmap === undefined ? null : config.cmap;
|
|
119
165
|
const comp = this.field.getScalar(k);
|
|
120
166
|
const formatter = config.formatter === undefined ? (val) => val === null ? 'null' : val.toString() : config.formatter;
|
|
121
|
-
text_specs = comp.map((v, i) =>
|
|
167
|
+
text_specs = comp.map((v, i) => {
|
|
168
|
+
const spec = {
|
|
169
|
+
text: formatter(v),
|
|
170
|
+
lat: coords.lats[i],
|
|
171
|
+
lon: coords.lons[i],
|
|
172
|
+
min_zoom: zoom[i],
|
|
173
|
+
};
|
|
174
|
+
if (v !== null)
|
|
175
|
+
spec.data_value = v;
|
|
176
|
+
return spec;
|
|
177
|
+
});
|
|
122
178
|
}
|
|
123
179
|
else {
|
|
124
180
|
const comp = this.field.getStrings(k);
|
|
125
|
-
text_specs = comp.map((v, i) => ({
|
|
181
|
+
text_specs = comp.map((v, i) => ({
|
|
182
|
+
text: v === null ? '' : v,
|
|
183
|
+
lat: coords.lats[i],
|
|
184
|
+
lon: coords.lons[i],
|
|
185
|
+
min_zoom: zoom[i],
|
|
186
|
+
}));
|
|
126
187
|
}
|
|
127
188
|
const tc_opts = {
|
|
128
189
|
...positionToAlignmentAndOffset(pos),
|
|
129
|
-
font_size: this.opts.font_size, halo:
|
|
130
|
-
text_color: color, halo_color:
|
|
190
|
+
font_size: this.opts.font_size, halo: halo,
|
|
191
|
+
text_color: color, halo_color: halo_color,
|
|
192
|
+
cmap: cmap
|
|
131
193
|
};
|
|
132
|
-
|
|
194
|
+
sub_component_promises.push([TextCollection.make(gl, text_specs, font_url, tc_opts)]);
|
|
133
195
|
}
|
|
134
196
|
else if (config.type == 'barb') {
|
|
135
197
|
const comp = this.field.getVector(k);
|
|
@@ -138,28 +200,49 @@ class StationPlot extends PlotComponent {
|
|
|
138
200
|
}
|
|
139
201
|
else if (config.type == 'symbol') {
|
|
140
202
|
const pos = config.pos;
|
|
141
|
-
const
|
|
142
|
-
const
|
|
203
|
+
const color = config.color === undefined ? '#000000' : config.color;
|
|
204
|
+
const halo_color = config.halo_color === undefined ? Color.fromHex('#ffffff') : Color.normalizeColor(config.halo_color);
|
|
205
|
+
const halo = config.halo === undefined ? true : config.halo;
|
|
143
206
|
const comp = this.field.getStrings(k);
|
|
144
|
-
const coords = this.field.grid.getEarthCoords();
|
|
145
|
-
const zoom = this.field.grid.getMinVisibleZoom(this.opts.thin_fac);
|
|
146
207
|
const wxsym_font_url = font_url_template.replace('{fontstack}', 'wx_symbols');
|
|
147
|
-
const
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
208
|
+
const text_spec_data = [];
|
|
209
|
+
comp.forEach((v, i) => {
|
|
210
|
+
const v_cat = v === null ? 'none' : symbolCategory(v);
|
|
211
|
+
const text_color = typeof color === 'string' ? color : color(v, v_cat);
|
|
212
|
+
const text_spec_data_filtered = text_spec_data.filter(tsd => tsd.color == text_color);
|
|
213
|
+
let text_spec_color;
|
|
214
|
+
if (text_spec_data_filtered.length == 0) {
|
|
215
|
+
text_spec_color = { specs: [], color: text_color };
|
|
216
|
+
text_spec_data.push(text_spec_color);
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
text_spec_color = text_spec_data_filtered[0];
|
|
220
|
+
}
|
|
221
|
+
text_spec_color.specs.push({
|
|
222
|
+
text: v === null ? '' : String.fromCharCode(SYMBOLS[v]),
|
|
223
|
+
lat: coords.lats[i],
|
|
224
|
+
lon: coords.lons[i],
|
|
225
|
+
min_zoom: zoom[i],
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
const promises = [];
|
|
229
|
+
text_spec_data.forEach(tsd => {
|
|
230
|
+
const tc_opts = {
|
|
231
|
+
...positionToAlignmentAndOffset(pos),
|
|
232
|
+
font_size: this.opts.font_size, halo: halo,
|
|
233
|
+
text_color: Color.normalizeColor(tsd.color), halo_color: halo_color,
|
|
234
|
+
};
|
|
235
|
+
if (tc_opts.offset_x !== undefined)
|
|
236
|
+
tc_opts.offset_x -= 3;
|
|
237
|
+
promises.push(TextCollection.make(gl, tsd.specs, wxsym_font_url, tc_opts));
|
|
238
|
+
});
|
|
239
|
+
sub_component_promises.push(promises);
|
|
157
240
|
}
|
|
158
241
|
else {
|
|
159
242
|
throw `Unknown station plot configuration type ${config.type}`;
|
|
160
243
|
}
|
|
161
244
|
});
|
|
162
|
-
this.text_components =
|
|
245
|
+
this.text_components = await Promise.all(sub_component_promises.map(p => Promise.all(p)));
|
|
163
246
|
map.triggerRepaint();
|
|
164
247
|
}
|
|
165
248
|
/** @internal */
|
|
@@ -192,12 +275,12 @@ class StationPlot extends PlotComponent {
|
|
|
192
275
|
const map_height = gl_elems.map.getCanvas().height;
|
|
193
276
|
const map_zoom = gl_elems.map.getZoom();
|
|
194
277
|
let itext = 0, ibarb = 0;
|
|
195
|
-
Object.values(this.opts.config).forEach(comp => {
|
|
278
|
+
Object.values(this.opts.config).forEach((comp, idx) => {
|
|
196
279
|
if (comp.type == 'barb') {
|
|
197
280
|
barb_components[ibarb++].render(gl, arg);
|
|
198
281
|
}
|
|
199
282
|
else {
|
|
200
|
-
text_components[itext++].render(gl, arg, [map_width, map_height], map_zoom);
|
|
283
|
+
text_components[itext++].forEach(tc => tc.render(gl, arg, [map_width, map_height], map_zoom));
|
|
201
284
|
}
|
|
202
285
|
});
|
|
203
286
|
}
|
package/lib/TextCollection.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { RenderMethodArg, WebGLAnyRenderingContext } from "./AutumnTypes";
|
|
2
2
|
import { Color } from "./Color";
|
|
3
|
+
import { ColorMap, ColorMapGPUInterface } from "./Colormap";
|
|
3
4
|
import { ShaderProgramManager } from "./ShaderManager";
|
|
4
5
|
import { WGLBuffer, WGLTexture } from "autumn-wgl";
|
|
5
6
|
interface TextSpec {
|
|
@@ -7,6 +8,7 @@ interface TextSpec {
|
|
|
7
8
|
lon: number;
|
|
8
9
|
text: string;
|
|
9
10
|
min_zoom?: number;
|
|
11
|
+
data_value?: number;
|
|
10
12
|
}
|
|
11
13
|
type HorizontalAlign = 'left' | 'center' | 'right';
|
|
12
14
|
type VerticalAlign = 'baseline' | 'middle' | 'top';
|
|
@@ -15,6 +17,7 @@ interface TextCollectionOptions {
|
|
|
15
17
|
vertical_align?: VerticalAlign;
|
|
16
18
|
font_size?: number;
|
|
17
19
|
text_color?: Color;
|
|
20
|
+
cmap?: ColorMap | null;
|
|
18
21
|
halo_color?: Color;
|
|
19
22
|
halo?: boolean;
|
|
20
23
|
offset_x?: number;
|
|
@@ -25,7 +28,9 @@ declare class TextCollection {
|
|
|
25
28
|
readonly anchors: WGLBuffer;
|
|
26
29
|
readonly offsets: WGLBuffer;
|
|
27
30
|
readonly texcoords: WGLBuffer;
|
|
31
|
+
readonly data: WGLBuffer | null;
|
|
28
32
|
readonly texture: WGLTexture;
|
|
33
|
+
readonly cmap_gpu: ColorMapGPUInterface | null;
|
|
29
34
|
readonly opts: Required<TextCollectionOptions>;
|
|
30
35
|
private constructor();
|
|
31
36
|
static make(gl: WebGLAnyRenderingContext, text_locs: TextSpec[], fontstack_url_template: string, opts?: TextCollectionOptions): Promise<TextCollection>;
|
package/lib/TextCollection.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { getRendererData, isWebGL2Ctx } from "./AutumnTypes";
|
|
2
2
|
import { Color } from "./Color";
|
|
3
|
+
import { ColorMap, ColorMapGPUInterface } from "./Colormap";
|
|
3
4
|
import { LngLat } from "./Map";
|
|
4
5
|
import { ShaderProgramManager } from "./ShaderManager";
|
|
5
6
|
import { Cache, normalizeOptions } from "./utils";
|
|
@@ -18,8 +19,16 @@ in vec3 a_pos;
|
|
|
18
19
|
in vec2 a_offset;
|
|
19
20
|
in vec2 a_tex_coord;
|
|
20
21
|
|
|
22
|
+
#ifdef DATA
|
|
23
|
+
in highp float a_value;
|
|
24
|
+
#endif
|
|
25
|
+
|
|
21
26
|
out highp vec2 v_tex_coord;
|
|
22
27
|
|
|
28
|
+
#ifdef DATA
|
|
29
|
+
out highp float v_value;
|
|
30
|
+
#endif
|
|
31
|
+
|
|
23
32
|
mat4 scalingMatrix(float x_scale, float y_scale, float z_scale) {
|
|
24
33
|
return mat4(x_scale, 0.0, 0.0, 0.0,
|
|
25
34
|
0.0, y_scale, 0.0, 0.0,
|
|
@@ -41,14 +50,25 @@ void main() {
|
|
|
41
50
|
|
|
42
51
|
gl_Position = projectTile(a_pos.xy + globe_offset) + u_font_size / 12. * 1.5 * map_stretch_matrix * vec4(offset, 0., 0.);
|
|
43
52
|
v_tex_coord = a_tex_coord;
|
|
53
|
+
|
|
54
|
+
#ifdef DATA
|
|
55
|
+
v_value = a_value;
|
|
56
|
+
#endif
|
|
44
57
|
}`
|
|
45
58
|
const text_fragment_shader_src = `#version 300 es
|
|
46
59
|
|
|
47
60
|
in highp vec2 v_tex_coord;
|
|
61
|
+
|
|
62
|
+
#ifdef DATA
|
|
63
|
+
in highp float v_value;
|
|
64
|
+
#endif
|
|
65
|
+
|
|
48
66
|
uniform sampler2D u_sdf_sampler;
|
|
49
67
|
uniform int u_is_halo;
|
|
50
68
|
|
|
69
|
+
#ifndef DATA
|
|
51
70
|
uniform lowp vec4 u_text_color;
|
|
71
|
+
#endif
|
|
52
72
|
uniform lowp vec4 u_halo_color;
|
|
53
73
|
|
|
54
74
|
#define SDF_FILL 0.75
|
|
@@ -62,7 +82,13 @@ void main() {
|
|
|
62
82
|
lowp float step_width = 0.08;
|
|
63
83
|
lowp float alpha = smoothstep(SDF_FILL - step_width, SDF_FILL + step_width, sdf_val);
|
|
64
84
|
|
|
65
|
-
|
|
85
|
+
#ifdef DATA
|
|
86
|
+
lowp vec4 text_color = apply_colormap(v_value);
|
|
87
|
+
#else
|
|
88
|
+
lowp vec4 text_color = u_text_color;
|
|
89
|
+
#endif
|
|
90
|
+
|
|
91
|
+
lowp vec4 color = u_is_halo == 1 ? u_halo_color : text_color;
|
|
66
92
|
|
|
67
93
|
if (u_is_halo == 1) {
|
|
68
94
|
alpha = min(smoothstep(SDF_HALO - step_width, SDF_HALO + step_width, sdf_val), 1.0 - alpha);
|
|
@@ -154,6 +180,9 @@ function createAtlas(pbf_glyphs) {
|
|
|
154
180
|
const FONT_ATLAS_CACHE = new Cache(async (urls) => {
|
|
155
181
|
const promises = urls.map(async (url) => {
|
|
156
182
|
const resp = await fetch(url);
|
|
183
|
+
if (resp.status != 200) {
|
|
184
|
+
throw `Error ${resp.status} retrieving font pbf from '${url}'`;
|
|
185
|
+
}
|
|
157
186
|
const blob = await resp.blob();
|
|
158
187
|
const data_buffer = await blob.arrayBuffer();
|
|
159
188
|
// Parse the PBF and get the glyph data
|
|
@@ -168,6 +197,7 @@ const text_collection_opt_defaults = {
|
|
|
168
197
|
vertical_align: 'baseline',
|
|
169
198
|
font_size: 12,
|
|
170
199
|
text_color: new Color([0, 0, 0, 1]),
|
|
200
|
+
cmap: null,
|
|
171
201
|
halo_color: new Color([0, 0, 0, 1]),
|
|
172
202
|
halo: false,
|
|
173
203
|
offset_x: 0,
|
|
@@ -176,6 +206,7 @@ const text_collection_opt_defaults = {
|
|
|
176
206
|
class TextCollection {
|
|
177
207
|
constructor(gl, text_locs, font_atlas, opts) {
|
|
178
208
|
this.opts = normalizeOptions(opts, text_collection_opt_defaults);
|
|
209
|
+
const text_color_hex = this.opts.text_color === undefined ? new Color([0, 0, 0, 1]) : this.opts.text_color;
|
|
179
210
|
const is_webgl2 = isWebGL2Ctx(gl);
|
|
180
211
|
const format = is_webgl2 ? gl.R8 : gl.LUMINANCE;
|
|
181
212
|
const type = gl.UNSIGNED_BYTE;
|
|
@@ -193,11 +224,15 @@ class TextCollection {
|
|
|
193
224
|
const anchor_data = new Float32Array(n_verts * 3);
|
|
194
225
|
const offset_data = new Float32Array(n_verts * 2);
|
|
195
226
|
const tc_data = new Float32Array(n_verts * 2);
|
|
196
|
-
|
|
227
|
+
const value_data = new Float32Array(n_verts);
|
|
228
|
+
let i_anch = 0, i_off = 0, i_tc = 0, i_dat = 0;
|
|
229
|
+
let has_data = false;
|
|
197
230
|
text_locs.forEach(loc => {
|
|
198
231
|
const { lat, lon, text } = loc;
|
|
199
232
|
const min_zoom = loc.min_zoom === undefined ? 0 : loc.min_zoom;
|
|
233
|
+
const data_value = loc.data_value === undefined ? NaN : loc.data_value;
|
|
200
234
|
const { x: anchor_x, y: anchor_y } = new LngLat(lon, lat).toMercatorCoord();
|
|
235
|
+
has_data = has_data || loc.data_value !== undefined;
|
|
201
236
|
let x_offset = this.opts.offset_x;
|
|
202
237
|
let y_offset = this.opts.offset_y;
|
|
203
238
|
const init_i_off = i_off;
|
|
@@ -251,6 +286,12 @@ class TextCollection {
|
|
|
251
286
|
tc_data[i_tc++] = glyph_info.atlas_j / font_atlas.atlas_height;
|
|
252
287
|
tc_data[i_tc++] = (glyph_info.atlas_i + glyph_info.width) / font_atlas.atlas_width;
|
|
253
288
|
tc_data[i_tc++] = glyph_info.atlas_j / font_atlas.atlas_height;
|
|
289
|
+
value_data[i_dat++] = data_value;
|
|
290
|
+
value_data[i_dat++] = data_value;
|
|
291
|
+
value_data[i_dat++] = data_value;
|
|
292
|
+
value_data[i_dat++] = data_value;
|
|
293
|
+
value_data[i_dat++] = data_value;
|
|
294
|
+
value_data[i_dat++] = data_value;
|
|
254
295
|
x_offset += glyph_info.advance - glyph_info.left;
|
|
255
296
|
}
|
|
256
297
|
if (this.opts.horizontal_align == 'center') {
|
|
@@ -274,18 +315,40 @@ class TextCollection {
|
|
|
274
315
|
}
|
|
275
316
|
}
|
|
276
317
|
});
|
|
277
|
-
|
|
318
|
+
const shader_defines = [];
|
|
319
|
+
let fragment_src = text_fragment_shader_src;
|
|
320
|
+
if (has_data) {
|
|
321
|
+
shader_defines.push('DATA');
|
|
322
|
+
this.data = new WGLBuffer(gl, value_data, 1, gl.TRIANGLE_STRIP);
|
|
323
|
+
const cmap = this.opts.cmap === null ? new ColorMap([0, 1], [text_color_hex], { overflow_color: text_color_hex, underflow_color: text_color_hex }) : this.opts.cmap;
|
|
324
|
+
this.cmap_gpu = new ColorMapGPUInterface(cmap);
|
|
325
|
+
this.cmap_gpu.setupShaderVariables(gl, gl.NEAREST);
|
|
326
|
+
fragment_src = ColorMapGPUInterface.applyShader(fragment_src);
|
|
327
|
+
}
|
|
328
|
+
else {
|
|
329
|
+
this.data = null;
|
|
330
|
+
this.cmap_gpu = null;
|
|
331
|
+
}
|
|
332
|
+
this.shader_manager = new ShaderProgramManager(text_vertex_shader_src, fragment_src, shader_defines);
|
|
278
333
|
this.anchors = new WGLBuffer(gl, anchor_data, 3, gl.TRIANGLE_STRIP);
|
|
279
334
|
this.offsets = new WGLBuffer(gl, offset_data, 2, gl.TRIANGLE_STRIP);
|
|
280
335
|
this.texcoords = new WGLBuffer(gl, tc_data, 2, gl.TRIANGLE_STRIP);
|
|
336
|
+
this.data = has_data ? new WGLBuffer(gl, value_data, 1, gl.TRIANGLE_STRIP) : null;
|
|
281
337
|
}
|
|
282
338
|
static async make(gl, text_locs, fontstack_url_template, opts) {
|
|
283
339
|
const FONT_GROUP_SIZE = 256;
|
|
284
340
|
const characters = text_locs.map(tl => [...tl.text]).flat().map(c => c.charCodeAt(0)).filter((c, i, ary) => ary.indexOf(c) == i);
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
341
|
+
let stack_start, stack_end;
|
|
342
|
+
if (characters.length > 0) {
|
|
343
|
+
const char_code_min = Math.min(...characters);
|
|
344
|
+
const char_code_max = Math.max(...characters);
|
|
345
|
+
stack_start = Math.floor(char_code_min / FONT_GROUP_SIZE) * FONT_GROUP_SIZE;
|
|
346
|
+
stack_end = Math.floor(char_code_max / FONT_GROUP_SIZE) * FONT_GROUP_SIZE;
|
|
347
|
+
}
|
|
348
|
+
else {
|
|
349
|
+
stack_start = 0;
|
|
350
|
+
stack_end = 0;
|
|
351
|
+
}
|
|
289
352
|
const fontstack_urls = [];
|
|
290
353
|
for (let istack = stack_start; istack <= stack_end; istack += FONT_GROUP_SIZE) {
|
|
291
354
|
fontstack_urls.push(fontstack_url_template.replace('{range}', `${istack}-${istack + FONT_GROUP_SIZE - 1}`));
|
|
@@ -298,13 +361,23 @@ class TextCollection {
|
|
|
298
361
|
render(gl, arg, [map_width, map_height], map_zoom) {
|
|
299
362
|
const render_data = getRendererData(arg);
|
|
300
363
|
const program = this.shader_manager.getShaderProgram(gl, render_data.shaderData);
|
|
364
|
+
const attributes = { 'a_pos': this.anchors, 'a_offset': this.offsets, 'a_tex_coord': this.texcoords };
|
|
301
365
|
const uniforms = {
|
|
302
366
|
'u_map_width': map_width, 'u_map_height': map_height, 'u_map_zoom': map_zoom, 'u_font_size': this.opts.font_size,
|
|
303
|
-
'
|
|
367
|
+
'u_halo_color': this.opts.halo_color.toRGBATuple(), 'u_offset': 0,
|
|
304
368
|
...this.shader_manager.getShaderUniforms(render_data)
|
|
305
369
|
};
|
|
370
|
+
if (this.data !== null) {
|
|
371
|
+
attributes['a_value'] = this.data;
|
|
372
|
+
}
|
|
373
|
+
else {
|
|
374
|
+
uniforms['u_text_color'] = this.opts.text_color.toRGBATuple();
|
|
375
|
+
}
|
|
306
376
|
uniforms['u_is_halo'] = this.opts.halo ? 1 : 0;
|
|
307
|
-
program.use(
|
|
377
|
+
program.use(attributes, uniforms, { 'u_sdf_sampler': this.texture });
|
|
378
|
+
if (this.cmap_gpu !== null) {
|
|
379
|
+
this.cmap_gpu.bindShaderVariables(program);
|
|
380
|
+
}
|
|
308
381
|
gl.enable(gl.BLEND);
|
|
309
382
|
gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
|
|
310
383
|
program.draw();
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { MarchingSquaresModule } from './cpp/marchingsquares';
|
|
2
|
+
import './cpp/marchingsquares.wasm';
|
|
3
|
+
interface InitMSModuleOpts {
|
|
4
|
+
document_script?: string;
|
|
5
|
+
}
|
|
6
|
+
declare function initMSModule(opts: InitMSModuleOpts): Promise<MarchingSquaresModule>;
|
|
7
|
+
export { initMSModule };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Should rename this eventually
|
|
2
|
+
import Module from './cpp/marchingsquares';
|
|
3
|
+
import './cpp/marchingsquares.wasm';
|
|
4
|
+
let msm_promise = null;
|
|
5
|
+
function initMSModule(opts) {
|
|
6
|
+
if (msm_promise === null) {
|
|
7
|
+
msm_promise = Module({ 'locateFile': (fname, dir) => (opts.document_script === undefined ? dir : opts.document_script) + fname });
|
|
8
|
+
}
|
|
9
|
+
return msm_promise;
|
|
10
|
+
}
|
|
11
|
+
export { initMSModule };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import * as Comlink from 'comlink';
|
|
2
|
+
type Promisify<T> = (T extends (...args: infer TArguments) => infer TReturn ? (...args: TArguments) => Promise<TReturn> : unknown);
|
|
3
|
+
type WorkerPool<T> = {
|
|
4
|
+
[K in keyof T]: Promisify<T[K]>;
|
|
5
|
+
};
|
|
6
|
+
declare function createWorkerPool<T>(workers: Worker[], init?: (wkr: Comlink.Remote<T>) => void): WorkerPool<T>;
|
|
7
|
+
export { createWorkerPool };
|
|
8
|
+
export type { WorkerPool };
|