import { WxAPIControl } from '@metoceanapi/wxtiles-common/controls/WxAPIControl'; import { WxInfoControl } from '@metoceanapi/wxtiles-common/controls/WxInfoControl'; import { WxLegendControl } from '@metoceanapi/wxtiles-common/controls/WxLegendControl'; import { WxStyleEditorControl } from '@metoceanapi/wxtiles-common/controls/WxStyleEditorControl'; import { WxTimeControl } from '@metoceanapi/wxtiles-common/controls/WxTimeControl'; import { WXLOG, type WxColorStyleWeak } from '@metoceanapi/wxtiles-common/utils/wxtools'; import { WxAPI, type WxTileSource } from '../src/index'; import { addControl, addLayer, addRaster, flyTo, initFrameWork, position, removeLayer, setURL } from './frwrkdeps'; const OPACITY = 0.8; // this is universal function for Leaflet and Mapbox. // Functions below are just framework specific wrappers for this universal function // start() is the fully interchangable function for Leaflet and Mapbox export async function start() { const map = await initFrameWork(); const headers = new Headers({ 'x-api-key': 'YHtP155Q1gxENEeESBsmDF' }); addRaster(map, 'baseS', 'baseL', 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', 3); addRaster(map, 'baseS', 'baseL', 'https://tiles.metoceanapi.com/base-lines/{z}/{x}/{y}', 5, null, headers); const dataServerURL = 'https://tilesdev.metoceanapi.com/data/'; const wxapi = new WxAPI({ dataServerURL, maskURL: 'auto', maskChannel: 'R', qtreeURL: 'auto', requestInit: { headers: headers }, }); const filter = 'global'; let datasetName = 'gfs.global'; let variable = 'air.temperature.at-2m'; // get datasetName from URL const urlParams = window.location.toString().split('##')[1]; const params = urlParams?.split('/'); datasetName = params?.[0] || datasetName; if (params?.[1]) variable = params[1]; let time = params?.[2] || ''; const zoom = (params && Number.parseFloat(params[3])) || 0; const lng = (params && Number.parseFloat(params[4])) || 0; const lat = (params && Number.parseFloat(params[5])) || 0; const bearing = (params && Number.parseFloat(params[6])) || 0; const pitch = (params && Number.parseFloat(params[7])) || 0; if (params?.length > 8) params[8] = params.slice(8).join('/'); const str = params?.[8] && params[8]; const sth = { style: {} as WxColorStyleWeak }; try { // get style from URL sth.style = str && { ...JSON.parse(decodeURI(str)) }; // reset levels if change units } catch (e) { /* ignore errors silently */ console.log("Can't parse style from URL"); } params && flyTo(map, zoom, lng, lat, bearing, pitch); // if no params stay at default position set in initFrameWork() map.on('zoomend', () => setURL(map, time, datasetName, variable, sth.style)); map.on('moveend', () => setURL(map, time, datasetName, variable, sth.style)); let wxsourceLayer: WxTileSource | undefined; const legendControl = new WxLegendControl(); addControl(map, legendControl, 'top-right'); const frameworkOptions = { id: 'wxsource', opacity: OPACITY, attribution: 'WxTiles DOCS', }; const apiControl = new WxAPIControl(wxapi, datasetName, variable, { filter }); addControl(map, apiControl, 'top-left'); apiControl.onchange = async (_datasetName, _variable, resetStyleAndFlyTo = true): Promise => { datasetName = _datasetName; variable = _variable; WXLOG('apiControl.onchange datasetName=', datasetName, 'variable=', variable); // remove existing source and layer removeLayer(map, frameworkOptions.id, wxsourceLayer); // if (resetStyleAndFlyTo) { sth.style = {}; } wxsourceLayer = undefined; const wxdatasetManager = await wxapi.createDatasetManager(datasetName); const boundaries = wxdatasetManager.getBoundaries(); if (boundaries && resetStyleAndFlyTo) { const { east, west, north, south } = boundaries.boundariesnorm; const zoom = Math.round(Math.log((360 * 360) / Math.max((east - west + 360) % 360, north - south) / 360) / Math.LN2); // from https://stackoverflow.com/questions/6048975/google-maps-v3-how-to-calculate-the-zoom-level-for-a-given-bounds flyTo(map, zoom, (east + west) / 2, (north + south) / 2, 0, 0); } const meta = wxdatasetManager.getVariableCurrentMeta(variable); if (meta?.units === 'RGB') { const times = wxdatasetManager.getAllTimes(); addRaster( map, frameworkOptions.id, 'wxtiles', wxdatasetManager.createURI(variable, times[0]), wxdatasetManager.getMaxZoom(), wxdatasetManager.getBoundaries()?.boundariesnorm, headers, ); timeControl.setTimes(times); legendControl.clear(); } else { wxsourceLayer = wxdatasetManager.createSourceLayer({ variable, time, wxstyle: sth.style }, frameworkOptions); wxsourceLayer.setCoarseLevel(0); await addLayer(map, 'wxtiles', wxsourceLayer); // wxsource.startAnimation(); const styleCopy = wxsourceLayer.getCurrentStyleObjectCopy(); legendControl.drawLegend(styleCopy); // first draw legend with current style styleCopy.levels = sth.style?.levels; // no need to show defaults it in the editor and URL styleCopy.colors = sth.style?.colors; // no need to show defaults it in the editor and URL await customStyleEditorControl.onchange?.(styleCopy, true); timeControl.updateSource(wxsourceLayer); } }; const timeControl = new WxTimeControl(50); addControl(map, timeControl, 'top-left'); timeControl.onchange = (time_) => { time = time_; setURL(map, time_, datasetName, variable, sth.style); infoControl.update(wxsourceLayer, map); }; const customStyleEditorControl = new WxStyleEditorControl(); addControl(map, customStyleEditorControl, 'top-right'); customStyleEditorControl.onchange = async (style, nonnativecall) => { WXLOG('customStyleEditorControl.onchange'); if (!wxsourceLayer) return; nonnativecall || (await wxsourceLayer.updateCurrentStyleObject(style)); // if called manually, do not update wxsource's style const nstyle = wxsourceLayer.getCurrentStyleObjectCopy(); legendControl.drawLegend(nstyle); nstyle.levels = style?.levels; // keep levels empty if they are not defined nstyle.colors = style?.colors; // keep colors empty if they are not defined customStyleEditorControl.setStyle(nstyle); // if called from wxsource, update customStyleEditorControl sth.style = nstyle; setURL(map, time, datasetName, variable, sth.style); }; const infoControl = new WxInfoControl(); addControl(map, infoControl, 'bottom-left'); map.on('mousemove', (e) => infoControl.update(wxsourceLayer, map, position(e))); await apiControl.onchange(datasetName, variable, false); // initial load }