modules/data/datasources/dwd.js

/**
 * Deutscher Wetterdienst (DWD): Open Data Server for Germany.
 * Provides access to ICON model data, MOSMIX forecasts, and Radar data.
 * For more information, visit: https://opendata.dwd.de/
 *
 * @type {Object}
 * @name DWD
 * @memberof datasources
 */

export default {
    // Access to ICON Global model data
    "icon-global": {
        methods: {
            type: "grib2",
            method: "GET",
        },
    },

    // Access to ICON-EU (Europe) model data
    "icon-eu": {
        methods: {
            type: "grib2",
            method: "GET",
        },
    },

    // Access to ICON-D2 (Germany high-resolution) model data
    "icon-d2": {
        methods: {
            type: "grib2",
            method: "GET",
        },
    },

    // MOSMIX Forecasts (Statistical optimization of model output)
    "mosmix": {
        methods: {
            type: "kmz", // Returns KMZ (zipped KML)
            method: "GET",
        },
    },

    // Radar Data (Composite)
    "radar-composite": {
        methods: {
            type: "binary",
            method: "GET",
        },
    },

    requirements: {
        needProxy: true,
        requireskey: false,
    },

    info: {
        returnFormats: "grib2, netcdf, kmz, binary",
        MoreInfo: "https://opendata.dwd.de/readme.txt",
        About: "Deutscher Wetterdienst (DWD) Open Data Server. Provides raw model data (ICON), MOSMIX forecasts, and Radar composites.",
    },

    "endpoint-info": {
        "icon-global": {
            paramFormat: {
                run: "String - Model run time (00, 06, 12, 18)",
                parameter: "String - Meteorological parameter (lowercase for directory). Available: alb_rad, alhfl_s, apab_s, ashfl_s, asob_s, asob_s_cs, asob_t, aswdifd_s, aswdifu_s, aswdir_s, athb_s, athb_t, aumfl_s, avmfl_s, cape_con, cape_ml, ceiling, cin_ml, clc, clch, clcl, clcm, clct, clct_mod, cldepth, depth_lk, fi, fr_lake, fr_land, h_ice, h_snow, hbas_con, hhl, hsurf, htop_con, htop_dc, hzerocl, lai, lpi_con_max, mh, omega, p, plcov, pmsl, ps, qc, qi, qv, qv_2m, qv_s, rain_con, rain_gsp, relhum, relhum_2m, rho_snow, rlat, rlon, rootdp, runoff_g, runoff_s, snow_con, snow_gsp, snowlmt, soiltyp, synmsg_bt_cl_ir10.8, synmsg_bt_cl_wv6.2, t, t_2m, t_g, t_ice, t_snow, t_so, tch, tcm, td_2m, tke, tmax_2m, tmin_2m, tot_prec, tqc, tqi, tqr, tqs, tqv, u, u_10m, v, v_10m, vis, vmax_10m, w, w_snow, w_so, w_so_ice, ww, z0",
                parameterUpper: "String - Meteorological parameter (uppercase for filename). e.g. T_2M for t_2m",
                time: "String - Forecast hour (000 to 180)",
                date: "String - Date in YYYYMMDD format",
            },
            infoSource: "https://opendata.dwd.de/weather/nwp/icon/grib/",
            example: {
                run: "00",
                parameter: "t_2m",
                parameterUpper: "T_2M",
                time: "000",
                date: "20231025",
                exampleRequest: "https://opendata.dwd.de/weather/nwp/icon/grib/00/t_2m/icon_global_icosahedral_single-level_2023102500_000_T_2M.grib2.bz2"
            }
        },
        "icon-eu": {
            paramFormat: {
                run: "String - Model run time (00, 06, 12, 18)",
                parameter: "String - Meteorological parameter (e.g., 't_2m')",
                parameterUpper: "String - Meteorological parameter (uppercase for filename). e.g. T_2M for t_2m",
                time: "String - Forecast hour (000 to 120)",
                date: "String - Date in YYYYMMDD format",
            },
            infoSource: "https://opendata.dwd.de/weather/nwp/icon-eu/grib/",
            example: {
                run: "06",
                parameter: "tot_prec",
                parameterUpper: "TOT_PREC",
                time: "001",
                date: "20231025",
                exampleRequest: "https://opendata.dwd.de/weather/nwp/icon-eu/grib/06/tot_prec/icon-eu_europe_regular-lat-lon_single-level_2023102506_001_TOT_PREC.grib2.bz2"
            }
        },
        "icon-d2": {
            paramFormat: {
                run: "String - Model run time (00, 03, 06, ...)",
                parameter: "String - Meteorological parameter (e.g., 't_2m')",
                time: "String - Forecast hour (000 to 048)",
                date: "String - Date in YYYYMMDD format",
            },
            infoSource: "https://opendata.dwd.de/weather/nwp/icon-d2/grib/",
            example: {
                run: "12",
                parameter: "t_2m",
                time: "002",
                date: "20231025",
                exampleRequest: "https://opendata.dwd.de/weather/nwp/icon-d2/grib/12/t_2m/icon-d2_germany_regular-lat-lon_single-level_2023102512_002_2d_t_2m.grib2.bz2"
            }
        },
        "mosmix": {
            paramFormat: {
                station: "String - Station ID (e.g., '10382' for Berlin-Tegel)",
            },
            infoSource: "https://opendata.dwd.de/weather/local_forecasts/mos/",
            example: {
                station: "10382",
                exampleRequest: "https://opendata.dwd.de/weather/local_forecasts/mos/MOSMIX_L/single_stations/10382/kml/MOSMIX_L_LATEST_10382.kmz"
            }
        },
        "radar-composite": {
            paramFormat: {
                product: "String - Radar product code (wn, hx, rs, rv, dmax, hg, pg, vii, hymecng)",
                date: "String/Date - Date object or ISO string (e.g. '2025-12-06T04:10:00Z')",
            },
            infoSource: "https://opendata.dwd.de/weather/radar/composite/",
            example: {
                product: "wn",
                date: "2025-12-06T04:10:00Z",
                exampleRequest: "https://opendata.dwd.de/weather/radar/composite/wn/composite_wn_20251206_0410.tar"
            },
            example_hx: {
                product: "hx",
                date: "2025-12-06T04:15:00Z",
                exampleRequest: "https://opendata.dwd.de/weather/radar/composite/hx/composite_hx_20251206_0415-hd5"
            },
            example_pg: {
                product: "pg",
                date: "2025-12-06T04:15:00Z",
                exampleRequest: "https://opendata.dwd.de/weather/radar/composite/pg/PAAH21EDZW060415.buf"
            }
        }
    },

    /**
     * Generates the dynamic endpoint based on the data type and arguments.
     * @param {Object} args - Arguments for the request
     * @param {string} dataType - Type of data requested
     * @returns {string} The constructed URL
     */
    sourceType: (args, dataType) => {
        const baseUrl = "https://opendata.dwd.de/weather";

        if (dataType === "radar-composite") {
            const product = args.product || "wn";
            const date = args.date ? new Date(args.date) : new Date();

            // Helper to pad numbers
            const pad = (n) => String(n).padStart(2, "0");

            const year = date.getUTCFullYear();
            const yy = String(year).slice(2);
            const month = pad(date.getUTCMonth() + 1);
            const day = pad(date.getUTCDate());
            const hour = pad(date.getUTCHours());

            // Round minutes to nearest 5 for radar products
            let minutes = date.getUTCMinutes();
            minutes = Math.floor(minutes / 5) * 5;
            const minute = pad(minutes);

            const yyyymmdd = `${year}${month}${day}`;
            const yymmdd = `${yy}${month}${day}`;
            const hhmm = `${hour}${minute}`;
            const yymmddhhmm = `${yymmdd}${hhmm}`;

            // Product configurations
            const configs = {
                wn: { name: "wn", suffix: ".tar", pattern: "composite" },
                hx: { name: "hx", suffix: "-hd5", pattern: "composite" },
                rs: { name: "rs", suffix: ".tar", pattern: "composite" },
                rv: { prefix: "DE1200_RV", suffix: ".tar.bz2", pattern: "short" },
                dmax: { name: "dmax", suffix: "-hd5", pattern: "composite" },
                hg: { prefix: "HG", suffix: "_000.bz2", pattern: "short" },
                vii: { name: "VII", suffix: "-hd5", pattern: "composite" },
                hymecng: { name: "HymecNG", suffix: "_000-hd5", pattern: "composite" },
                pg: { prefix: "PAAH21EDZW", suffix: ".buf", pattern: "pg" }
            };

            const config = configs[product];
            if (!config) {
                throw new Error(`Unknown radar product: ${product}`);
            }

            let filename = "";
            if (config.pattern === "composite") {
                filename = `composite_${config.name}_${yyyymmdd}_${hhmm}${config.suffix}`;
            } else if (config.pattern === "short") {
                filename = `${config.prefix}${yymmddhhmm}${config.suffix}`;
            } else if (config.pattern === "pg") {
                filename = `${config.prefix}${day}${hhmm}${config.suffix}`;
            }

            return `${baseUrl}/radar/composite/${product}/${filename}`;

        } else if (dataType.startsWith("icon")) {
            const run = args.run || "00";
            const parameter = args.parameter || "t_2m";
            const parameterUpper = args.parameterUpper || parameter.toUpperCase();
            const time = args.time || "000";

            // Handle date: default to today if not provided
            let dateStr = args.date;
            if (!dateStr) {
                const now = new Date();
                const y = now.getUTCFullYear();
                const m = String(now.getUTCMonth() + 1).padStart(2, "0");
                const d = String(now.getUTCDate()).padStart(2, "0");
                dateStr = `${y}${m}${d}`;
            }

            if (dataType === "icon-global") {
                return `${baseUrl}/nwp/icon/grib/${run}/${parameter}/icon_global_icosahedral_single-level_${dateStr}${run}_${time}_${parameterUpper}.grib2.bz2`;
            } else if (dataType === "icon-eu") {
                return `${baseUrl}/nwp/icon-eu/grib/${run}/${parameter}/icon-eu_europe_regular-lat-lon_single-level_${dateStr}${run}_${time}_${parameterUpper}.grib2.bz2`;
            } else if (dataType === "icon-d2") {
                return `${baseUrl}/nwp/icon-d2/grib/${run}/${parameter}/icon-d2_germany_regular-lat-lon_single-level_${dateStr}${run}_${time}_2d_${parameter}.grib2.bz2`;
            }

        } else if (dataType === "mosmix") {
            const station = args.station;
            if (!station) throw new Error("Station ID is required for MOSMIX data.");
            return `${baseUrl}/local_forecasts/mos/MOSMIX_L/single_stations/${station}/kml/MOSMIX_L_LATEST_${station}.kmz`;
        }

        throw new Error(`Unsupported data type: ${dataType}`);
    }
};