autumnplot-gl 2.2.3 → 3.0.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 +65 -8
- package/dist/110.autumnplot-gl.js +2 -0
- package/dist/110.autumnplot-gl.js.map +1 -0
- package/dist/autumnplot-gl.js +3 -0
- package/dist/autumnplot-gl.js.LICENSE.txt +12 -0
- package/dist/autumnplot-gl.js.map +1 -0
- package/dist/marchingsquares.wasm +0 -0
- package/lib/AutumnTypes.d.ts +14 -13
- package/lib/Barbs.d.ts +8 -3
- package/lib/Barbs.js +14 -1
- package/lib/BillboardCollection.d.ts +11 -7
- package/lib/BillboardCollection.js +52 -30
- package/lib/ColorBar.js +51 -7
- package/lib/Colormap.d.ts +14 -3
- package/lib/Colormap.js +63 -12
- package/lib/Contour.d.ts +73 -16
- package/lib/Contour.js +175 -146
- package/lib/ContourCreator.d.ts +18 -0
- package/lib/ContourCreator.js +59 -0
- package/lib/Fill.d.ts +17 -5
- package/lib/Fill.js +60 -37
- package/lib/Grid.d.ts +167 -0
- package/lib/Grid.js +339 -0
- package/lib/Hodographs.d.ts +13 -5
- package/lib/Hodographs.js +52 -67
- package/lib/Map.d.ts +14 -3
- package/lib/Map.js +9 -0
- package/lib/Paintball.d.ts +9 -3
- package/lib/Paintball.js +28 -10
- package/lib/PlotComponent.d.ts +5 -5
- package/lib/PlotLayer.d.ts +12 -12
- package/lib/PlotLayer.js +16 -14
- package/lib/PlotLayer.worker.d.ts +2 -2
- package/lib/PlotLayer.worker.js +102 -66
- package/lib/PolylineCollection.d.ts +20 -9
- package/lib/PolylineCollection.js +158 -32
- package/lib/RawField.d.ts +11 -167
- package/lib/RawField.js +37 -383
- package/lib/TextCollection.d.ts +31 -0
- package/lib/TextCollection.js +295 -0
- package/lib/cpp/marchingsquares.d.ts +6 -0
- package/lib/cpp/marchingsquares.js +4152 -0
- package/lib/cpp/marchingsquares.wasm +0 -0
- package/lib/cpp/marchingsquares_embind.d.ts +47 -0
- package/lib/index.d.ts +13 -6
- package/lib/index.js +12 -3
- package/lib/utils.d.ts +5 -3
- package/lib/utils.js +17 -6
- package/package.json +14 -9
|
Binary file
|
package/lib/AutumnTypes.d.ts
CHANGED
|
@@ -19,21 +19,22 @@ interface BillboardSpec {
|
|
|
19
19
|
BB_MAG_WRAP: number;
|
|
20
20
|
BB_MAG_MAX: number;
|
|
21
21
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
type LineData = {
|
|
23
|
+
vertices: [number, number][];
|
|
24
|
+
offsets?: [number, number][];
|
|
25
|
+
data?: number[];
|
|
26
|
+
zoom?: number;
|
|
27
|
+
};
|
|
28
|
+
type Polyline = {
|
|
25
29
|
extrusion: Float32Array;
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
origin: [number, number];
|
|
32
|
-
zoom: number;
|
|
33
|
-
texcoords: [number, number][];
|
|
34
|
-
}
|
|
30
|
+
vertices: Float32Array;
|
|
31
|
+
offsets?: Float32Array;
|
|
32
|
+
data?: Float32Array;
|
|
33
|
+
zoom?: Float32Array;
|
|
34
|
+
};
|
|
35
35
|
type WebGLAnyRenderingContext = WebGLRenderingContext | WebGL2RenderingContext;
|
|
36
36
|
declare function isWebGL2Ctx(gl: WebGLAnyRenderingContext): gl is WebGL2RenderingContext;
|
|
37
37
|
type TypedArray = Float16Array | Float32Array;
|
|
38
|
+
type ContourData = Record<number, [number, number][][]>;
|
|
38
39
|
export { isWebGL2Ctx };
|
|
39
|
-
export type { WindProfile, BillboardSpec,
|
|
40
|
+
export type { WindProfile, BillboardSpec, Polyline, LineData, WebGLAnyRenderingContext, TypedArray, ContourData };
|
package/lib/Barbs.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { PlotComponent } from "./PlotComponent";
|
|
2
2
|
import { RawVectorField } from "./RawField";
|
|
3
|
-
import {
|
|
3
|
+
import { MapLikeType } from "./Map";
|
|
4
4
|
import { TypedArray, WebGLAnyRenderingContext } from "./AutumnTypes";
|
|
5
5
|
interface BarbsOptions {
|
|
6
6
|
/**
|
|
@@ -23,9 +23,9 @@ interface BarbsOptions {
|
|
|
23
23
|
* const vector_field = new RawVectorField(grid, u_data, v_data);
|
|
24
24
|
* const barbs = new Barbs(vector_field, {color: '#000000', thin_fac: 16});
|
|
25
25
|
*/
|
|
26
|
-
declare class Barbs<ArrayType extends TypedArray> extends PlotComponent {
|
|
26
|
+
declare class Barbs<ArrayType extends TypedArray, MapType extends MapLikeType> extends PlotComponent<MapType> {
|
|
27
27
|
/** The vector field */
|
|
28
|
-
private
|
|
28
|
+
private fields;
|
|
29
29
|
readonly color: [number, number, number];
|
|
30
30
|
readonly thin_fac: number;
|
|
31
31
|
private gl_elems;
|
|
@@ -35,6 +35,11 @@ declare class Barbs<ArrayType extends TypedArray> extends PlotComponent {
|
|
|
35
35
|
* @param opts - Options for creating the wind barbs
|
|
36
36
|
*/
|
|
37
37
|
constructor(fields: RawVectorField<ArrayType>, opts: BarbsOptions);
|
|
38
|
+
/**
|
|
39
|
+
* Update the field displayed as barbs
|
|
40
|
+
* @param fields - The new field to display as barbs
|
|
41
|
+
*/
|
|
42
|
+
updateField(fields: RawVectorField<ArrayType>): Promise<void>;
|
|
38
43
|
/**
|
|
39
44
|
* @internal
|
|
40
45
|
* Add the barb field to a map
|
package/lib/Barbs.js
CHANGED
|
@@ -119,6 +119,17 @@ class Barbs extends PlotComponent {
|
|
|
119
119
|
this.thin_fac = opts.thin_fac || 1;
|
|
120
120
|
this.gl_elems = null;
|
|
121
121
|
}
|
|
122
|
+
/**
|
|
123
|
+
* Update the field displayed as barbs
|
|
124
|
+
* @param fields - The new field to display as barbs
|
|
125
|
+
*/
|
|
126
|
+
async updateField(fields) {
|
|
127
|
+
this.fields = fields;
|
|
128
|
+
if (this.gl_elems === null)
|
|
129
|
+
return;
|
|
130
|
+
this.gl_elems.barb_billboards.updateField(fields);
|
|
131
|
+
this.gl_elems.map.triggerRepaint();
|
|
132
|
+
}
|
|
122
133
|
/**
|
|
123
134
|
* @internal
|
|
124
135
|
* Add the barb field to a map
|
|
@@ -131,10 +142,12 @@ class Barbs extends PlotComponent {
|
|
|
131
142
|
BARB_TEXTURE = _createBarbTexture();
|
|
132
143
|
}
|
|
133
144
|
const barb_image = { format: gl.RGBA, type: gl.UNSIGNED_BYTE, image: BARB_TEXTURE, mag_filter: gl.NEAREST };
|
|
134
|
-
const barb_billboards = new BillboardCollection(
|
|
145
|
+
const barb_billboards = new BillboardCollection(this.fields, this.thin_fac, map_max_zoom, barb_image, BARB_DIMS, this.color, 0.1);
|
|
146
|
+
await barb_billboards.setup(gl);
|
|
135
147
|
this.gl_elems = {
|
|
136
148
|
map: map, barb_billboards: barb_billboards
|
|
137
149
|
};
|
|
150
|
+
this.updateField(this.fields);
|
|
138
151
|
}
|
|
139
152
|
/**
|
|
140
153
|
* @internal
|
|
@@ -2,16 +2,20 @@ import { BillboardSpec, TypedArray, WebGLAnyRenderingContext } from "./AutumnTyp
|
|
|
2
2
|
import { RawVectorField } from "./RawField";
|
|
3
3
|
import { WGLTextureSpec } from "autumn-wgl";
|
|
4
4
|
declare class BillboardCollection<ArrayType extends TypedArray> {
|
|
5
|
+
private field;
|
|
5
6
|
readonly spec: BillboardSpec;
|
|
6
7
|
readonly color: [number, number, number];
|
|
7
8
|
readonly size_multiplier: number;
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
private
|
|
12
|
-
private
|
|
13
|
-
private readonly
|
|
14
|
-
|
|
9
|
+
readonly thin_fac: number;
|
|
10
|
+
readonly max_zoom: number;
|
|
11
|
+
readonly billboard_image: WGLTextureSpec;
|
|
12
|
+
private gl_elems;
|
|
13
|
+
private wind_textures;
|
|
14
|
+
private readonly trim_inaccessible;
|
|
15
|
+
private show_field;
|
|
16
|
+
constructor(field: RawVectorField<ArrayType>, thin_fac: number, max_zoom: number, billboard_image: WGLTextureSpec, billboard_spec: BillboardSpec, billboard_color: [number, number, number], billboard_size_mult: number);
|
|
17
|
+
updateField(field: RawVectorField<ArrayType>): void;
|
|
18
|
+
setup(gl: WebGLAnyRenderingContext): Promise<void>;
|
|
15
19
|
render(gl: WebGLAnyRenderingContext, matrix: number[] | Float32Array, [map_width, map_height]: [number, number], map_zoom: number, map_bearing: number, map_pitch: number): void;
|
|
16
20
|
}
|
|
17
21
|
export { BillboardCollection };
|
|
@@ -17,6 +17,7 @@ uniform highp float u_bb_mag_wrap;
|
|
|
17
17
|
|
|
18
18
|
uniform sampler2D u_u_sampler;
|
|
19
19
|
uniform sampler2D u_v_sampler;
|
|
20
|
+
uniform sampler2D u_rot_sampler;
|
|
20
21
|
|
|
21
22
|
varying highp vec2 v_tex_coord;
|
|
22
23
|
|
|
@@ -58,6 +59,7 @@ void main() {
|
|
|
58
59
|
|
|
59
60
|
highp float u = texture2D(u_u_sampler, a_tex_coord).r;
|
|
60
61
|
highp float v = texture2D(u_v_sampler, a_tex_coord).r;
|
|
62
|
+
highp float rot = texture2D(u_rot_sampler, a_tex_coord).r;
|
|
61
63
|
|
|
62
64
|
lowp float bb_aspect = u_bb_width / u_bb_height;
|
|
63
65
|
lowp float ang = (abs(u) < 1e-6 && abs(v) < 1e-6) ? 0. : atan(v, u) - 3.141592654 / 2.0;
|
|
@@ -88,7 +90,7 @@ void main() {
|
|
|
88
90
|
texcoord = tex_loc + vec2(u_bb_width, u_bb_height);
|
|
89
91
|
}
|
|
90
92
|
|
|
91
|
-
mat4 barb_rotation = rotationZMatrix(ang + radians(u_map_bearing));
|
|
93
|
+
mat4 barb_rotation = rotationZMatrix(ang + radians(u_map_bearing) - rot);
|
|
92
94
|
mat4 map_stretch_matrix = scalingMatrix(1.0, 1. / u_map_aspect, 1.0);
|
|
93
95
|
offset = map_stretch_matrix * barb_rotation * offset;
|
|
94
96
|
}
|
|
@@ -105,58 +107,78 @@ void main() {
|
|
|
105
107
|
lowp vec4 tex_color = texture2D(u_sampler, v_tex_coord);
|
|
106
108
|
gl_FragColor = vec4(u_bb_color, tex_color.a);
|
|
107
109
|
}`
|
|
110
|
+
class BillboardCollectionGLElems {
|
|
111
|
+
}
|
|
108
112
|
class BillboardCollection {
|
|
109
|
-
constructor(
|
|
113
|
+
constructor(field, thin_fac, max_zoom, billboard_image, billboard_spec, billboard_color, billboard_size_mult) {
|
|
114
|
+
this.field = field;
|
|
110
115
|
this.spec = billboard_spec;
|
|
111
116
|
this.color = billboard_color;
|
|
112
117
|
this.size_multiplier = billboard_size_mult;
|
|
113
|
-
this.
|
|
114
|
-
this.
|
|
115
|
-
this.
|
|
118
|
+
this.thin_fac = thin_fac;
|
|
119
|
+
this.max_zoom = max_zoom;
|
|
120
|
+
this.billboard_image = billboard_image;
|
|
121
|
+
this.gl_elems = null;
|
|
122
|
+
this.wind_textures = null;
|
|
116
123
|
const n_density_tiers = Math.log2(thin_fac);
|
|
117
124
|
const n_inaccessible_tiers = Math.max(n_density_tiers + 1 - max_zoom, 0);
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
const {
|
|
125
|
+
this.trim_inaccessible = Math.pow(2, n_inaccessible_tiers);
|
|
126
|
+
this.show_field = true;
|
|
127
|
+
}
|
|
128
|
+
updateField(field) {
|
|
129
|
+
this.field = field;
|
|
130
|
+
if (this.gl_elems === null)
|
|
131
|
+
return;
|
|
132
|
+
const gl = this.gl_elems.gl;
|
|
133
|
+
const data = this.field.getThinnedField(this.trim_inaccessible, this.trim_inaccessible);
|
|
134
|
+
const { u: u_thin, v: v_thin } = data.getTextureData();
|
|
135
|
+
this.show_field = u_thin !== null;
|
|
136
|
+
const { format, type, row_alignment } = getGLFormatTypeAlignment(gl, !(u_thin instanceof Float32Array));
|
|
128
137
|
const u_image = { 'format': format, 'type': type,
|
|
129
|
-
'width':
|
|
138
|
+
'width': data.grid.ni, 'height': data.grid.nj, 'image': u_thin,
|
|
130
139
|
'mag_filter': gl.NEAREST, 'row_alignment': row_alignment,
|
|
131
140
|
};
|
|
132
141
|
const v_image = { 'format': format, 'type': type,
|
|
133
|
-
'width':
|
|
142
|
+
'width': data.grid.ni, 'height': data.grid.nj, 'image': v_thin,
|
|
134
143
|
'mag_filter': gl.NEAREST, 'row_alignment': row_alignment,
|
|
135
144
|
};
|
|
136
|
-
this.
|
|
137
|
-
|
|
138
|
-
|
|
145
|
+
if (this.wind_textures === null) {
|
|
146
|
+
this.wind_textures = { u: new WGLTexture(gl, u_image), v: new WGLTexture(gl, v_image) };
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
this.wind_textures.u.setImageData(u_image);
|
|
150
|
+
this.wind_textures.v.setImageData(v_image);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
async setup(gl) {
|
|
154
|
+
const program = new WGLProgram(gl, billboard_vertex_shader_src, billboard_fragment_shader_src);
|
|
155
|
+
const thinned_field = this.field.getThinnedField(this.trim_inaccessible, this.trim_inaccessible);
|
|
156
|
+
const { vertices, texcoords } = await thinned_field.grid.getWGLBillboardBuffers(gl, this.thin_fac / this.trim_inaccessible, this.max_zoom);
|
|
157
|
+
const { rotation: proj_rotation_tex } = thinned_field.grid.getVectorRotationTexture(gl);
|
|
158
|
+
const texture = new WGLTexture(gl, this.billboard_image);
|
|
159
|
+
this.gl_elems = { gl: gl, program: program, vertices: vertices, texcoords: texcoords, texture: texture, proj_rot_texture: proj_rotation_tex };
|
|
139
160
|
}
|
|
140
161
|
render(gl, matrix, [map_width, map_height], map_zoom, map_bearing, map_pitch) {
|
|
141
|
-
if (this.
|
|
162
|
+
if (this.gl_elems === null || this.wind_textures === null || !this.show_field)
|
|
142
163
|
return;
|
|
143
164
|
if (matrix instanceof Float32Array)
|
|
144
165
|
matrix = [...matrix];
|
|
166
|
+
const gl_elems = this.gl_elems;
|
|
145
167
|
const bb_size = this.spec.BB_HEIGHT * (map_height / map_width) * this.size_multiplier;
|
|
146
168
|
const bb_width = this.spec.BB_WIDTH / this.spec.BB_TEX_WIDTH;
|
|
147
169
|
const bb_height = this.spec.BB_HEIGHT / this.spec.BB_TEX_HEIGHT;
|
|
148
|
-
|
|
170
|
+
gl_elems.program.use({ 'a_pos': gl_elems.vertices, 'a_tex_coord': gl_elems.texcoords }, { 'u_bb_size': bb_size, 'u_bb_width': bb_width, 'u_bb_height': bb_height,
|
|
149
171
|
'u_bb_mag_bin_size': this.spec.BB_MAG_BIN_SIZE, 'u_bb_mag_wrap': this.spec.BB_MAG_WRAP, 'u_offset': 0,
|
|
150
|
-
'u_bb_color': this.color, 'u_matrix': matrix, 'u_map_aspect': map_height / map_width, 'u_zoom': map_zoom, 'u_map_bearing': map_bearing }, { 'u_sampler':
|
|
172
|
+
'u_bb_color': this.color, 'u_matrix': matrix, 'u_map_aspect': map_height / map_width, 'u_zoom': map_zoom, 'u_map_bearing': map_bearing }, { 'u_sampler': gl_elems.texture, 'u_u_sampler': this.wind_textures.u, 'u_v_sampler': this.wind_textures.v, 'u_rot_sampler': gl_elems.proj_rot_texture });
|
|
151
173
|
gl.enable(gl.BLEND);
|
|
152
174
|
gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
175
|
+
gl_elems.program.draw();
|
|
176
|
+
gl_elems.program.setUniforms({ 'u_offset': -2 });
|
|
177
|
+
gl_elems.program.draw();
|
|
178
|
+
gl_elems.program.setUniforms({ 'u_offset': -1 });
|
|
179
|
+
gl_elems.program.draw();
|
|
180
|
+
gl_elems.program.setUniforms({ 'u_offset': 1 });
|
|
181
|
+
gl_elems.program.draw();
|
|
160
182
|
}
|
|
161
183
|
}
|
|
162
184
|
export { BillboardCollection };
|
package/lib/ColorBar.js
CHANGED
|
@@ -40,12 +40,13 @@ function makeColorBar(colormap, opts) {
|
|
|
40
40
|
};
|
|
41
41
|
const chars_left = getNChar(ticks[0]);
|
|
42
42
|
const chars_right = getNChar(ticks[ticks.length - 1]);
|
|
43
|
+
const need_overflow = colormap.underflow_color !== null || colormap.overflow_color !== null;
|
|
43
44
|
const bar_long_size = 600;
|
|
44
45
|
const bar_cross_size = bar_long_size / 9;
|
|
45
|
-
const bar_long_pad = orientation == 'horizontal' ? Math.max(chars_left, chars_right) * 6 : 8;
|
|
46
|
+
const bar_long_pad = Math.max(orientation == 'horizontal' ? Math.max(chars_left, chars_right) * 6 : 8, need_overflow ? bar_cross_size / (2 * Math.sqrt(3)) : 0);
|
|
46
47
|
const bar_cross_pad = 3;
|
|
47
48
|
const bar_thickness = 10;
|
|
48
|
-
let height, width, bar_left, bar_top, bar_width, bar_height;
|
|
49
|
+
let height, width, bar_left, bar_top, bar_width, bar_height, bar_right, bar_bottom, bar_middle, bar_low_arrow, bar_high_arrow;
|
|
49
50
|
if (orientation == 'vertical') {
|
|
50
51
|
height = bar_long_size;
|
|
51
52
|
width = bar_cross_size;
|
|
@@ -53,6 +54,11 @@ function makeColorBar(colormap, opts) {
|
|
|
53
54
|
bar_top = bar_long_pad;
|
|
54
55
|
bar_width = bar_thickness;
|
|
55
56
|
bar_height = bar_long_size - 2 * bar_long_pad;
|
|
57
|
+
bar_right = bar_left + bar_width;
|
|
58
|
+
bar_middle = bar_left + bar_width / 2;
|
|
59
|
+
bar_bottom = bar_top + bar_height;
|
|
60
|
+
bar_low_arrow = colormap.underflow_color === null ? bar_bottom : bar_bottom + bar_cross_size / (Math.sqrt(3) * 2);
|
|
61
|
+
bar_high_arrow = colormap.overflow_color === null ? bar_top : bar_top - bar_cross_size / (Math.sqrt(3) * 2);
|
|
56
62
|
}
|
|
57
63
|
else {
|
|
58
64
|
width = bar_long_size;
|
|
@@ -61,6 +67,11 @@ function makeColorBar(colormap, opts) {
|
|
|
61
67
|
bar_top = tick_dir == 'bottom' ? bar_cross_pad : bar_cross_size - 6 - bar_cross_pad - bar_thickness;
|
|
62
68
|
bar_height = bar_thickness;
|
|
63
69
|
bar_width = bar_long_size - 2 * bar_long_pad;
|
|
70
|
+
bar_right = bar_left + bar_width;
|
|
71
|
+
bar_middle = bar_top + bar_height / 2;
|
|
72
|
+
bar_bottom = bar_top + bar_height;
|
|
73
|
+
bar_low_arrow = colormap.underflow_color === null ? bar_left : bar_left - bar_cross_size / (Math.sqrt(3) * 2);
|
|
74
|
+
bar_high_arrow = colormap.overflow_color === null ? bar_right : bar_right + bar_cross_size / (Math.sqrt(3) * 2);
|
|
64
75
|
}
|
|
65
76
|
const n_colors = colormap.colors.length;
|
|
66
77
|
const root = createElement('svg', { width: width, height: height });
|
|
@@ -75,6 +86,7 @@ function makeColorBar(colormap, opts) {
|
|
|
75
86
|
{ 'text-anchor': 'middle', transform: `translate(${bar_left}, ${bar_top})` };
|
|
76
87
|
}
|
|
77
88
|
const gticks = createElement('g', gtickattrs, root);
|
|
89
|
+
// Make the colored background
|
|
78
90
|
colormap.colors.forEach((color, icolor) => {
|
|
79
91
|
let attrs = {};
|
|
80
92
|
if (orientation == 'vertical') {
|
|
@@ -87,6 +99,30 @@ function makeColorBar(colormap, opts) {
|
|
|
87
99
|
}
|
|
88
100
|
createElement('rect', { ...attrs, fill: color.color, opacity: color.opacity }, gbar);
|
|
89
101
|
});
|
|
102
|
+
// Make the overflow and underflow triangles
|
|
103
|
+
if (colormap.underflow_color !== null) {
|
|
104
|
+
let point_list;
|
|
105
|
+
if (orientation == 'vertical') {
|
|
106
|
+
point_list = `${bar_right} ${bar_bottom}, ${bar_middle} ${bar_low_arrow}, ${bar_left} ${bar_bottom}, ${bar_right} ${bar_bottom}`;
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
point_list = `${bar_left} ${bar_bottom}, ${bar_low_arrow} ${bar_middle}, ${bar_left} ${bar_top}, ${bar_left} ${bar_bottom}`;
|
|
110
|
+
}
|
|
111
|
+
const underflow_attrs = { points: point_list, fill: colormap.underflow_color.color, opacity: colormap.underflow_color.opacity };
|
|
112
|
+
createElement('polygon', underflow_attrs, gbar);
|
|
113
|
+
}
|
|
114
|
+
if (colormap.overflow_color !== null) {
|
|
115
|
+
let point_list;
|
|
116
|
+
if (orientation == 'vertical') {
|
|
117
|
+
point_list = `${bar_left} ${bar_top}, ${bar_middle} ${bar_high_arrow}, ${bar_right} ${bar_top}, ${bar_left} ${bar_top}`;
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
point_list = `${bar_right} ${bar_top}, ${bar_high_arrow} ${bar_middle}, ${bar_right} ${bar_bottom}, ${bar_right} ${bar_top}`;
|
|
121
|
+
}
|
|
122
|
+
const overflow_attrs = { points: point_list, fill: colormap.overflow_color.color, opacity: colormap.overflow_color.opacity };
|
|
123
|
+
createElement('polygon', overflow_attrs, gbar);
|
|
124
|
+
}
|
|
125
|
+
// Make the ticks marks and labels
|
|
90
126
|
const first_level = colormap.levels[0];
|
|
91
127
|
const last_level = colormap.levels[colormap.levels.length - 1];
|
|
92
128
|
ticks.filter(level => first_level <= level && level <= last_level).forEach(level => {
|
|
@@ -116,16 +152,24 @@ function makeColorBar(colormap, opts) {
|
|
|
116
152
|
const text = createElement('text', { ...textattrs, fill: '#000000', style: `font-family: ${fontface}; font-size: ${tickfontsize}pt` }, gtick);
|
|
117
153
|
text.textContent = level.toString();
|
|
118
154
|
});
|
|
155
|
+
// Draw the outline
|
|
156
|
+
let point_list;
|
|
157
|
+
if (orientation == 'vertical') {
|
|
158
|
+
point_list = `${bar_left} ${bar_top}, ${bar_middle} ${bar_high_arrow}, ${bar_right} ${bar_top}, ${bar_right} ${bar_bottom}, ` +
|
|
159
|
+
`${bar_middle} ${bar_low_arrow}, ${bar_left} ${bar_bottom}, ${bar_left} ${bar_top}`;
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
point_list = `${bar_left} ${bar_top}, ${bar_right} ${bar_top}, ${bar_high_arrow} ${bar_middle}, ${bar_right} ${bar_bottom}, ` +
|
|
163
|
+
`${bar_left} ${bar_bottom}, ${bar_low_arrow} ${bar_middle}, ${bar_left} ${bar_top}`;
|
|
164
|
+
}
|
|
119
165
|
const outline_attrs = {
|
|
120
|
-
|
|
121
|
-
y: bar_top,
|
|
122
|
-
width: bar_width,
|
|
123
|
-
height: bar_height,
|
|
166
|
+
points: point_list,
|
|
124
167
|
stroke: '#000000',
|
|
125
168
|
'stroke-width': 1.5,
|
|
126
169
|
fill: 'none'
|
|
127
170
|
};
|
|
128
|
-
createElement('
|
|
171
|
+
createElement('polygon', outline_attrs, root);
|
|
172
|
+
// Draw the colorbar label
|
|
129
173
|
let labelattrs;
|
|
130
174
|
if (orientation == 'vertical') {
|
|
131
175
|
labelattrs = tick_dir == 'left' ? { transform: `translate(15, ${height / 2}) rotate(-90)` } : { transform: `translate(${width - 6}, ${height / 2}) rotate(-90)` };
|
package/lib/Colormap.d.ts
CHANGED
|
@@ -1,19 +1,29 @@
|
|
|
1
|
+
import { Float16Array } from "@petamoriken/float16";
|
|
1
2
|
interface Color {
|
|
2
3
|
/** The color as a hex color string */
|
|
3
4
|
color: string;
|
|
4
5
|
/** The opacity as a number from 0 to 1 */
|
|
5
6
|
opacity: number;
|
|
6
7
|
}
|
|
8
|
+
interface ColorMapOptions {
|
|
9
|
+
/** The color to use for areas where the value is below the lowest value in the color map */
|
|
10
|
+
overflow_color?: Color | string;
|
|
11
|
+
/** The color to use for areas where the value is above the highest value in the color map */
|
|
12
|
+
underflow_color?: Color | string;
|
|
13
|
+
}
|
|
7
14
|
/** A mapping from values to colors */
|
|
8
15
|
declare class ColorMap {
|
|
9
16
|
readonly levels: number[];
|
|
10
17
|
readonly colors: Color[];
|
|
18
|
+
readonly overflow_color: Color | null;
|
|
19
|
+
readonly underflow_color: Color | null;
|
|
11
20
|
/**
|
|
12
21
|
* Create a color map
|
|
13
22
|
* @param levels - The list of levels. The number of levels should always be one more than the number of colors.
|
|
14
23
|
* @param colors - A list of colors
|
|
24
|
+
* @param opts - Options for the color map
|
|
15
25
|
*/
|
|
16
|
-
constructor(levels: number[], colors: Color[] | string[]);
|
|
26
|
+
constructor(levels: number[], colors: Color[] | string[], opts?: ColorMapOptions);
|
|
17
27
|
/**
|
|
18
28
|
* @returns an array of hex color strings
|
|
19
29
|
*/
|
|
@@ -67,5 +77,6 @@ declare const bluered: (level_min: number, level_max: number, n_colors: number)
|
|
|
67
77
|
* @returns A canvas element containing each color of the color map
|
|
68
78
|
*/
|
|
69
79
|
declare function makeTextureImage(colormap: ColorMap): HTMLCanvasElement;
|
|
70
|
-
|
|
71
|
-
export
|
|
80
|
+
declare function makeIndexMap(colormap: ColorMap): Float16Array;
|
|
81
|
+
export { ColorMap, bluered, redblue, pw_speed500mb, pw_speed850mb, pw_cape, pw_t2m, pw_td2m, nws_storm_clear_refl, makeTextureImage, makeIndexMap };
|
|
82
|
+
export type { Color, ColorMapOptions };
|
package/lib/Colormap.js
CHANGED
|
@@ -5,6 +5,7 @@ const cape_colormap_data = { "levels": [0, 100, 200, 300, 400, 500, 600, 700
|
|
|
5
5
|
const t2m_colormap_data = { "levels": [-60, -59, -58, -57, -56, -55, -54, -53, -52, -51, -50, -49, -48, -47, -46, -45, -44, -43, -42, -41, -40, -39, -38, -37, -36, -35, -34, -33, -32, -31, -30, -29, -28, -27, -26, -25, -24, -23, -22, -21, -20, -19, -18, -17, -16, -15, -14, -13, -12, -11, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120], "colors": [ "#235877", "#2a5f7c", "#316782", "#396e87", "#40768c", "#477d91", "#4e8497", "#558c9c", "#5d93a1", "#649ba6", "#6ba2ac", "#72a9b1", "#79b1b6", "#81b8bb", "#88c0c1", "#8fc7c6", "#96cecb", "#9dd6d0", "#a5ddd6", "#ace5db", "#b3ece0", "#b3ece0", "#b1e7df", "#b0e1dd", "#aedcdc", "#add7da", "#abd2d9", "#aaccd8", "#a8c7d6", "#a7c2d5", "#a5bcd4", "#a4b7d2", "#a2b2d1", "#9fa7ce", "#9ea2cd", "#9c9dcb", "#9c9dcb", "#9b97ca", "#9992c8", "#988dc7", "#9688c6", "#9582c4", "#9278c2", "#9073c0", "#8f6dbf", "#8d68bd", "#8c63bc", "#8a5dbb", "#8958b9", "#8753b8", "#864eb6", "#8448b5", "#8343b4", "#813eb2", "#8038b1", "#7e33b0", "#7d33ae", "#7b29ad", "#7a23ab", "#781eaa", "#a037af", "#a443b3", "#a74fb7", "#ab5cbb", "#af68bf", "#b374c3", "#b680c7", "#ba8dcc", "#be99d0", "#c1a5d4", "#c5b1d8", "#c9bddc", "#cdcae0", "#d0d6e4", "#d4e2e8", "#deecf2", "#d1e2ee", "#c5d9ea", "#b8cfe6", "#acc5e3", "#9fbbdf", "#92b2db", "#86a8d7", "#799ed3", "#6c94cf", "#608bcb", "#5381c7", "#4777c4", "#3a6dc0", "#2d64bc", "#215ab8", "#1450b4", "#0f4455", "#1c4e5a", "#2a585f", "#376363", "#456d68", "#52776d", "#5f8172", "#6d8c77", "#7a967c", "#88a080", "#95aa85", "#a3b58a", "#b0bf8f", "#bdc994", "#cbd399", "#d8de9d", "#e6e8a2", "#f3f2a7", "#f8eea2", "#f0e199", "#e8d591", "#e1c888", "#d9bc80", "#d1af77", "#c9a36f", "#c19666", "#ba8a5e", "#b27d55", "#aa714d", "#a26444", "#9b583c", "#8b3f2b", "#833222", "#7b261a", "#7b261a", "#741911", "#6c0d09", "#640000", "#5f0000", "#630507", "#670a0e", "#6c0f15", "#70141c", "#741824", "#781d2b", "#7d2232", "#812739", "#852c40", "#73372d", "#7a4036", "#80493f", "#875349", "#8e5c52", "#94655b", "#9b6e64", "#a88177", "#af8a80", "#b69389", "#bd9c92", "#c3a69c", "#caafa5", "#d1b8ae", "#d7c1b7", "#decac0", "#e5d4ca", "#ebddd3", "#f2e6dc", "#e8dfd6", "#e0d7cf", "#d8d0c8", "#d0c8c0", "#c8c0b9", "#c0b9b2", "#b7b1ab", "#afa9a4", "#a7a29c", "#9f9a95", "#97938e", "#8f8b87", "#878380", "#7f7c78", "#777471", "#6f6c6a", "#666563", "#5e5d5c", "#565554", "#4e4e4d", "#464646" ] }
|
|
6
6
|
const td2m_colormap_data = { "levels": [-40, -39, -38, -37, -36, -35, -34, -33, -32, -31, -30, -29, -28, -27, -26, -25, -24, -23, -22, -21, -20, -19, -18, -17, -16, -15, -14, -13, -12, -11, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90], "colors": [ "#986d4d", "#966c4c", "#946b4c", "#926a4b", "#90694b", "#8e684a", "#8c664a", "#8a6549", "#886448", "#866348", "#846247", "#826147", "#806046", "#7e5f46", "#7c5e45", "#7a5d44", "#785b44", "#765a43", "#745943", "#725842", "#715742", "#6f5641", "#6d5540", "#6b5440", "#69533f", "#67523f", "#65503e", "#634f3d", "#614e3d", "#5f4d3c", "#5d4c3c", "#5b4b3b", "#594a3b", "#57493a", "#554839", "#534739", "#514538", "#4f4438", "#4d4337", "#4b4237", "#494136", "#4d4334", "#514738", "#564c3c", "#5a5041", "#5e5545", "#625949", "#675e4d", "#6b6251", "#6f6755", "#746b5a", "#78705e", "#7c7462", "#807966", "#857d6a", "#89826f", "#8d8673", "#928b77", "#968f7b", "#9a947f", "#9e9883", "#a39d88", "#a7a18c", "#aba690", "#afaa94", "#b8b39c", "#b8b39c", "#bcb8a1", "#c1bca5", "#c9c5ad", "#c9cab1", "#d2ceb6", "#d2ceb6", "#d6d3ba", "#dfdcc2", "#e3e0c6", "#e7e5ca", "#ebe9cf", "#f0eed3", "#f4f2d7", "#e6f5e6", "#d7f0d7", "#c8eac8", "#b9e5b9", "#aadfaa", "#9bda9b", "#8cd48c", "#7dcf7d", "#6ec96e", "#5fc45f", "#30ae30", "#2ca32c", "#279927", "#238e23", "#1e831e", "#1a791a", "#156e15", "#116311", "#0c590c", "#084e08", "#61a3af", "#5896a0", "#508992", "#477b83", "#3e6e74", "#366166", "#2d5457", "#244648", "#1c393a", "#132c2b", "#66669a", "#605e94", "#59568e", "#534e88", "#4d4682", "#463e7c", "#403676", "#3a2e70", "#33266a", "#2d1e64", "#724071", "#784573", "#7d4b75", "#835076", "#885678", "#8e5b7a", "#93617c", "#99667d", "#9e6c7f", "#a47181" ] }
|
|
7
7
|
const nws_storm_clear_refl_colormap_data = { "levels": [ -15, -14.53608247, -14.07216495, -13.60824742, -13.1443299, -12.68041237, -12.21649485, -11.75257732, -11.28865979, -10.82474227, -10.36082474, -9.89690722, -9.43298969, -8.96907216, -8.50515464, -8.04123711, -7.57731959, -7.11340206, -6.64948454, -6.18556701, -5.72164948, -5.25773196, -4.79381443, -4.32989691, -3.86597938, -3.40206186, -2.93814433, -2.4742268, -2.01030928, -1.54639175, -1.08247423, -0.6185567, -0.15463918, 0.30927835, 0.77319588, 1.2371134, 1.70103093, 2.16494845, 2.62886598, 3.09278351, 3.55670103, 4.02061856, 4.48453608, 4.94845361, 5.41237113, 5.87628866, 6.34020619, 6.80412371, 7.26804124, 7.73195876, 8.19587629, 8.65979381, 9.12371134, 9.58762887, 10.05154639, 10.51546392, 10.97938144, 11.44329897, 11.90721649, 12.37113402, 12.83505155, 13.29896907, 13.7628866, 14.22680412, 14.69072165, 15.15463918, 15.6185567, 16.08247423, 16.54639175, 17.01030928, 17.4742268, 17.93814433, 18.40206186, 18.86597938, 19.32989691, 19.79381443, 20.25773196, 20.72164948, 21.18556701, 21.64948454, 22.11340206, 22.57731959, 23.04123711, 23.50515464, 23.96907216, 24.43298969, 24.89690722, 25.36082474, 25.82474227, 26.28865979, 26.75257732, 27.21649485, 27.68041237, 28.1443299, 28.60824742, 29.07216495, 29.53608247, 30, 30.46391753, 30.92783505, 31.39175258, 31.8556701, 32.31958763, 32.78350515, 33.24742268, 33.71134021, 34.17525773, 34.63917526, 35.10309278, 35.56701031, 36.03092784, 36.49484536, 36.95876289, 37.42268041, 37.88659794, 38.35051546, 38.81443299, 39.27835052, 39.74226804, 40.20618557, 40.67010309, 41.13402062, 41.59793814, 42.06185567, 42.5257732, 42.98969072, 43.45360825, 43.91752577, 44.3814433, 44.84536082, 45.30927835, 45.77319588, 46.2371134, 46.70103093, 47.16494845, 47.62886598, 48.09278351, 48.55670103, 49.02061856, 49.48453608, 49.94845361, 50.41237113, 50.87628866, 51.34020619, 51.80412371, 52.26804124, 52.73195876, 53.19587629, 53.65979381, 54.12371134, 54.58762887, 55.05154639, 55.51546392, 55.97938144, 56.44329897, 56.90721649, 57.37113402, 57.83505155, 58.29896907, 58.7628866, 59.22680412, 59.69072165, 60.15463918, 60.6185567, 61.08247423, 61.54639175, 62.01030928, 62.4742268, 62.93814433, 63.40206186, 63.86597938, 64.32989691, 64.79381443, 65.25773196, 65.72164948, 66.18556701, 66.64948454, 67.11340206, 67.57731959, 68.04123711, 68.50515464, 68.96907216, 69.43298969, 69.89690722, 70.36082474, 70.82474227, 71.28865979, 71.75257732, 72.21649485, 72.68041237, 73.1443299, 73.60824742, 74.07216495, 74.53608247, 75 ], "colors": [ "#959052", "#979356", "#9c9a60", "#a09c64", "#a3a067", "#a8a570", "#aaa875", "#acac79", "#b1b182", "#b5b587", "#b6b88c", "#bcbd93", "#bfc199", "#c1c39c", "#c1c39c", "#c6caa5", "#cacdaa", "#cccfaf", "#cfd1b3", "#cccfb3", "#c8ccb3", "#c6c8b3", "#bfc3b3", "#bfc3b3", "#bcc1b3", "#b8bdb3", "#b5bab3", "#afb5b3", "#acb3b3", "#aaafb3", "#a7aeb3", "#a3aab3", "#a0a8b3", "#9ca5b3", "#9aa1b3", "#97a0b3", "#939cb3", "#909ab3", "#939ab5", "#8c95b3", "#8791b1", "#838eb1", "#808caf", "#7c89af", "#7785ae", "#7482ac", "#7080aa", "#6b7caa", "#6275a8", "#5e72a7", "#5e72a7", "#5b70a5", "#566da3", "#5269a3", "#4f67a1", "#4b64a1", "#4660a0", "#425d9e", "#4260a1", "#4467a5", "#486eaa", "#4975ae", "#4d7cb1", "#4f83b5", "#508aba", "#5491bf", "#5699c3", "#599ec6", "#5ba5ca", "#5daccf", "#60b3d4", "#62bad8", "#64c1db", "#67c8df", "#69cfe4", "#6ed6e8", "#67d6d6", "#60d6c4", "#59d6b3", "#52d6a1", "#4bd690", "#42d67e", "#3bd66d", "#34d65b", "#11d418", "#11d116", "#0fcd16", "#0fc816", "#0fc316", "#0fbf15", "#0fbc15", "#0fb613", "#0eb313", "#0eaf13", "#0eaa13", "#0ca511", "#0ca111", "#0c9e11", "#0c9911", "#0c950f", "#0c900f", "#0a8c0f", "#0a870f", "#0a830e", "#0a800e", "#0a7c0c", "#0a770c", "#08720c", "#086e0c", "#086b0a", "#08660a", "#08620a", "#085d08", "#1c6708", "#317208", "#467c08", "#5b8707", "#6e9107", "#839c05", "#97a805", "#acb105", "#c1bc05", "#d6c603", "#e9d103", "#ffe200", "#ffd800", "#ffd300", "#ffc800", "#ffc300", "#ffba00", "#ffb500", "#ffb000", "#ffac00", "#ffa700", "#ff9e00", "#ff9900", "#ff9300", "#ff8900", "#ff8500", "#ff7f00", "#ff0000", "#f70000", "#f00000", "#e90000", "#e20000", "#db0000", "#d40000", "#cd0000", "#c60000", "#bf0000", "#b80000", "#b10000", "#aa0000", "#a30000", "#9a0000", "#930000", "#8c0000", "#850000", "#7e0000", "#770000", "#700000", "#ffffff", "#fff4ff", "#ffe9ff", "#ffdfff", "#ffd4ff", "#ffc8ff", "#ffbdff", "#ffb3ff", "#ffa8ff", "#ff9cff", "#ff91ff", "#ff75ff", "#fb6bfd", "#f960f9", "#f656f6", "#f24bf4", "#ef3ff0", "#ed36ef", "#e92aeb", "#e61fe8", "#e416e6", "#e10ae2", "#b100ff", "#ac00fb", "#a300f6", "#9a00f4", "#9300ef", "#8700e9", "#8200e8", "#7900e2", "#7200dd", "#6900db", "#6200d6" ] }
|
|
8
|
+
import { Float16Array } from "@petamoriken/float16";
|
|
8
9
|
function isColor(obj) {
|
|
9
10
|
return (typeof obj == 'object') && 'color' in obj && 'opacity' in obj;
|
|
10
11
|
}
|
|
@@ -14,13 +15,18 @@ class ColorMap {
|
|
|
14
15
|
* Create a color map
|
|
15
16
|
* @param levels - The list of levels. The number of levels should always be one more than the number of colors.
|
|
16
17
|
* @param colors - A list of colors
|
|
18
|
+
* @param opts - Options for the color map
|
|
17
19
|
*/
|
|
18
|
-
constructor(levels, colors) {
|
|
20
|
+
constructor(levels, colors, opts) {
|
|
19
21
|
if (levels.length != colors.length + 1) {
|
|
20
22
|
throw `Mismatch between number of levels (${levels.length}) and number of colors (${colors.length}; expected ${levels.length - 1})`;
|
|
21
23
|
}
|
|
24
|
+
const normalizeColor = (c) => isColor(c) ? c : { 'color': c, 'opacity': 1. };
|
|
22
25
|
this.levels = levels;
|
|
23
|
-
this.colors = colors.map(c =>
|
|
26
|
+
this.colors = colors.map(c => normalizeColor(c));
|
|
27
|
+
opts = opts === undefined ? {} : opts;
|
|
28
|
+
this.overflow_color = opts.overflow_color === undefined ? null : normalizeColor(opts.overflow_color);
|
|
29
|
+
this.underflow_color = opts.underflow_color === undefined ? null : normalizeColor(opts.underflow_color);
|
|
24
30
|
}
|
|
25
31
|
/**
|
|
26
32
|
* @returns an array of hex color strings
|
|
@@ -42,19 +48,33 @@ class ColorMap {
|
|
|
42
48
|
withOpacity(func) {
|
|
43
49
|
const new_colors = [];
|
|
44
50
|
const new_levels = [];
|
|
51
|
+
const opts = {};
|
|
45
52
|
for (let ic = 0; ic < this.colors.length; ic++) {
|
|
46
53
|
const color = this.colors[ic];
|
|
47
54
|
const level_lower = this.levels[ic];
|
|
48
55
|
const level_upper = this.levels[ic + 1];
|
|
49
|
-
const
|
|
50
|
-
|
|
56
|
+
const new_opacity = func(level_lower, level_upper);
|
|
57
|
+
const new_color = { color: color.color, opacity: new_opacity };
|
|
58
|
+
if (new_opacity > 0) {
|
|
51
59
|
if (new_levels[new_levels.length - 1] != level_lower)
|
|
52
60
|
new_levels.push(level_lower);
|
|
53
61
|
new_levels.push(level_upper);
|
|
54
62
|
new_colors.push(new_color);
|
|
55
63
|
}
|
|
56
64
|
}
|
|
57
|
-
|
|
65
|
+
if (this.underflow_color !== null) {
|
|
66
|
+
const underflow_opacity = func(this.levels[0], this.levels[0]);
|
|
67
|
+
if (underflow_opacity > 0) {
|
|
68
|
+
opts.underflow_color = { color: this.underflow_color.color, opacity: underflow_opacity };
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (this.overflow_color !== null) {
|
|
72
|
+
const overflow_opacity = func(this.levels[this.levels.length - 1], this.levels[this.levels.length - 1]);
|
|
73
|
+
if (overflow_opacity > 0) {
|
|
74
|
+
opts.overflow_color = { color: this.overflow_color.color, opacity: overflow_opacity };
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return new ColorMap(new_levels, new_colors, opts);
|
|
58
78
|
}
|
|
59
79
|
/**
|
|
60
80
|
* Create a diverging color map using two input colors
|
|
@@ -103,13 +123,24 @@ class ColorMap {
|
|
|
103
123
|
return new ColorMap(levels, stops);
|
|
104
124
|
}
|
|
105
125
|
}
|
|
126
|
+
function buildColormap(cm_data, overflow) {
|
|
127
|
+
const n_colors = cm_data.colors.length;
|
|
128
|
+
const opts = {};
|
|
129
|
+
if (overflow == 'over' || overflow == 'both') {
|
|
130
|
+
opts.overflow_color = cm_data.colors[n_colors - 1];
|
|
131
|
+
}
|
|
132
|
+
if (overflow == 'under' || overflow == 'both') {
|
|
133
|
+
opts.underflow_color = cm_data.colors[0];
|
|
134
|
+
}
|
|
135
|
+
return new ColorMap(cm_data.levels, cm_data.colors, opts);
|
|
136
|
+
}
|
|
106
137
|
// Some built-in colormaps
|
|
107
|
-
const pw_speed500mb =
|
|
108
|
-
const pw_speed850mb =
|
|
109
|
-
const pw_cape =
|
|
110
|
-
const pw_t2m =
|
|
111
|
-
const pw_td2m =
|
|
112
|
-
const nws_storm_clear_refl =
|
|
138
|
+
const pw_speed500mb = buildColormap(spd500_colormap_data, 'over').withOpacity((levl, levu) => Math.min((levu - 20) / 10, 1.));
|
|
139
|
+
const pw_speed850mb = buildColormap(spd850_colormap_data, 'over').withOpacity((levl, levu) => Math.min((levu - 20) / 10, 1.));
|
|
140
|
+
const pw_cape = buildColormap(cape_colormap_data, 'over').withOpacity((levl, levu) => Math.min(levu / 1000., 1.));
|
|
141
|
+
const pw_t2m = buildColormap(t2m_colormap_data, 'both');
|
|
142
|
+
const pw_td2m = buildColormap(td2m_colormap_data, 'both');
|
|
143
|
+
const nws_storm_clear_refl = buildColormap(nws_storm_clear_refl_colormap_data, 'over');
|
|
113
144
|
/**
|
|
114
145
|
* Create a diverging red/blue colormap, where red corresponds to the lowest value and blue corresponds to the highest value
|
|
115
146
|
* @param level_min - The lowest value in the color map
|
|
@@ -149,4 +180,24 @@ function makeTextureImage(colormap) {
|
|
|
149
180
|
});
|
|
150
181
|
return cmap_image;
|
|
151
182
|
}
|
|
152
|
-
|
|
183
|
+
function makeIndexMap(colormap) {
|
|
184
|
+
// Build a texture to account for nonlinear colormaps (basically inverts the relationship between
|
|
185
|
+
// the normalized index and the normalized level)
|
|
186
|
+
const n_nonlin = 101;
|
|
187
|
+
const map_norm = [];
|
|
188
|
+
for (let i = 0; i < n_nonlin; i++) {
|
|
189
|
+
map_norm.push(i / (n_nonlin - 1));
|
|
190
|
+
}
|
|
191
|
+
const levels = colormap.levels;
|
|
192
|
+
const n_lev = levels.length - 1;
|
|
193
|
+
const input_norm = levels.map((lev, ilev) => ilev / n_lev);
|
|
194
|
+
const cmap_norm = levels.map(lev => (lev - levels[0]) / (levels[n_lev] - levels[0]));
|
|
195
|
+
const inv_cmap_norm = map_norm.map(lev => {
|
|
196
|
+
let jlev;
|
|
197
|
+
for (jlev = 0; !(cmap_norm[jlev] <= lev && lev <= cmap_norm[jlev + 1]); jlev++) { }
|
|
198
|
+
const alpha = (lev - cmap_norm[jlev]) / (cmap_norm[jlev + 1] - cmap_norm[jlev]);
|
|
199
|
+
return input_norm[jlev] * (1 - alpha) + input_norm[jlev + 1] * alpha;
|
|
200
|
+
});
|
|
201
|
+
return new Float16Array(inv_cmap_norm);
|
|
202
|
+
}
|
|
203
|
+
export { ColorMap, bluered, redblue, pw_speed500mb, pw_speed850mb, pw_cape, pw_t2m, pw_td2m, nws_storm_clear_refl, makeTextureImage, makeIndexMap };
|