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
}