Skip to content

Commit aa18946

Browse files
feat: Support Gaussian grids like O1280 for IFS HRES
1 parent 49a5775 commit aa18946

File tree

3 files changed

+76
-11
lines changed

3 files changed

+76
-11
lines changed

examples/temperature.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
});
4747

4848
// s3 storage structure: /data_spatial/{domain}/{modelRun /year/month/day/time}Z/{selectedTime year-month-day-time.om}?variable={variable}
49-
const omUrl = `https://map-tiles.open-meteo.com/data_spatial/dwd_icon/2025/10/15/1200Z/2025-10-15T1400.om?variable=temperature_2m`;
49+
const omUrl = `https://map-tiles.open-meteo.com/data_spatial/ecmwf_ifs/2025/10/20/0000Z/2025-10-20T1200.om?variable=temperature_2m`;
5050

5151
map.on('load', () => {
5252
map.addSource('omFileSource', {

src/utils/domains.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,27 @@ export const domainOptions: Array<Domain> = [
377377
model_interval: 6,
378378
windUVComponents: true
379379
},
380+
{
381+
value: 'ecmwf_ifs',
382+
label: 'ECMWF IFS HRES',
383+
grid: {
384+
nx: 1440, // 6599680
385+
ny: 721,
386+
latMin: -90,
387+
lonMin: -180,
388+
dx: 360 / 1440,
389+
dy: 180 / (721 - 1),
390+
zoom: 3.2,
391+
gaussianGridLatitudeLines: 1280,
392+
center: function () {
393+
this.center = getCenterPoint(this);
394+
return this;
395+
}
396+
},
397+
time_interval: 1,
398+
model_interval: 3,
399+
windUVComponents: true
400+
},
380401

381402
// GEM
382403
{

src/worker.ts

Lines changed: 54 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -172,16 +172,60 @@ self.onmessage = async (message) => {
172172
const ind = j + i * TILE_SIZE;
173173
const lon = tile2lon(x + j / TILE_SIZE, z);
174174

175-
const { index, xFraction, yFraction } = getIndexAndFractions(
176-
lat,
177-
lon,
178-
domain,
179-
projectionGrid,
180-
ranges,
181-
[latMin, lonMin, latMax, lonMax]
182-
);
183-
184-
let px = interpolator(values as Float32Array, index, xFraction, yFraction, ranges);
175+
let px = NaN
176+
if (domain.grid.gaussianGridLatitudeLines) {
177+
const latitudeLines = domain.grid.gaussianGridLatitudeLines
178+
const dy = 180 / (2 * latitudeLines + 0.5);
179+
const count = 6599680;
180+
181+
const nearestNeighbor = false
182+
if (nearestNeighbor) {
183+
// nearest neighbor
184+
const y = (Math.round(latitudeLines - 1 - (lat - dy / 2) / dy) + 2 * latitudeLines) % (2 * latitudeLines);
185+
const nx = y < latitudeLines ? 20 + y * 4 : (2 * latitudeLines - y - 1) * 4 + 20;
186+
const dx = 360 / nx;
187+
const x = (Math.floor(lon / dx) + nx) % nx;
188+
const integral = y < latitudeLines ? 2 * y * y + 18 * y : count - (2 * (2 * latitudeLines - y) * (2 * latitudeLines - y) + 18 * (2 * latitudeLines - y));
189+
const index = integral + x
190+
px = values[index]
191+
} else {
192+
// linear interpolation
193+
const yLower = (Math.floor(latitudeLines - 1 - (lat - dy / 2) / dy) + 2 * latitudeLines) % (2 * latitudeLines);
194+
const yFraction = (latitudeLines - 1 - (lat - dy / 2) / dy) % 1
195+
const yUpper = yLower + 1;
196+
const nxLower = yLower < latitudeLines ? 20 + yLower * 4 : (2 * latitudeLines - yLower - 1) * 4 + 20;
197+
const nxUpper = yUpper < latitudeLines ? 20 + yUpper * 4 : (2 * latitudeLines - yUpper - 1) * 4 + 20;
198+
const dxLower = 360 / nxLower;
199+
const dxUpper = 360 / nxUpper;
200+
const xLower0 = (Math.floor(lon / dxLower) + nxLower) % nxLower;
201+
const xUpper0 = (Math.floor(lon / dxUpper) + nxUpper) % nxUpper;
202+
const integralLower = yLower < latitudeLines ? 2 * yLower * yLower + 18 * yLower : count - (2 * (2 * latitudeLines - yLower) * (2 * latitudeLines - yLower) + 18 * (2 * latitudeLines - yLower));
203+
const integralUpper = yUpper < latitudeLines ? 2 * yUpper * yUpper + 18 * yUpper : count - (2 * (2 * latitudeLines - yUpper) * (2 * latitudeLines - yUpper) + 18 * (2 * latitudeLines - yUpper));
204+
const indexLower = integralLower + xLower0;
205+
const indexUpper = integralUpper + xUpper0;
206+
const xFractionLower = (lon / dxLower) % 1
207+
const xFractionUpper = (lon / dxUpper) % 1
208+
const p0 = values[indexLower]
209+
const p1 = values[indexLower+1]
210+
const p2 = values[indexUpper]
211+
const p3 = values[indexUpper+1]
212+
px = p0 * (1 - xFractionLower) * (1 - yFraction) +
213+
p1 * xFractionLower * (1 - yFraction) +
214+
p2 * (1 - xFractionUpper) * yFraction +
215+
p3 * xFractionUpper * yFraction
216+
}
217+
} else {
218+
const { index, xFraction, yFraction } = getIndexAndFractions(
219+
lat,
220+
lon,
221+
domain,
222+
projectionGrid,
223+
ranges,
224+
[latMin, lonMin, latMax, lonMax]
225+
);
226+
227+
px = interpolator(values as Float32Array, index, xFraction, yFraction, ranges);
228+
}
185229

186230
if (hideZero.includes(variable.value)) {
187231
if (px < 0.25) {

0 commit comments

Comments
 (0)