diff --git a/ipmd/static/ipmd/js/ipmdlib.js b/ipmd/static/ipmd/js/ipmdlib.js
index 052fcdad141eedbf98fe91c68f18ffa7e4075315..1637f2bd54d6bd2f146e958834738dd15438ee9f 100644
--- a/ipmd/static/ipmd/js/ipmdlib.js
+++ b/ipmd/static/ipmd/js/ipmdlib.js
@@ -40,10 +40,27 @@ async function getModelInputSchema(dssId,modelId) {
return await response.json();
}
-async function getWeatherDatasource(weatherDatasourceId)
+/*async function getWeatherDatasource(weatherDatasourceId)
{
const response = await fetch(ipmdWeatherApiURL + "rest/weatherdatasource/" + weatherDatasourceId);
return await response.json();
+}*/
+
+function getWeatherDatasourceEndpoint(weatherDatasource)
+{
+ return weatherDatasource.endpoint.replace("{WEATHER_API_URL}",ipmdWeatherApiURL);
+}
+
+function getWeatherDatasource(weatherDatasources, weatherDatasourceId)
+{
+ for(let i=0;i<weatherDatasources.length;i++)
+ {
+ if(weatherDatasources[i].id == weatherDatasourceId)
+ {
+ return weatherDatasources[i];
+ }
+ }
+ return null;
}
async function getWeatherDatasources()
@@ -61,10 +78,22 @@ function getWeatherStationList(weatherDatasource){
let station = {"id":feature.id, "name":feature.properties.name}
stationList.push(station);
}
+ stationList.sort((a, b) => {
+ return (a.name < b.name) ? -1 : (a.name > b.name) ? 1 : 0;
+ });
return stationList;
}
-async function getLocationWeatherData(endpoint, weatherStationId, parameters, interval, dateStart, dateEnd){
+function renderWeatherStationSelectList(stationList, selectList){
+
+ selectList.add(new Option("-- Please select weather station --", -1));
+ for(let i=0;i<stationList.length; i++)
+ {
+ selectList.add(new Option(stationList[i].name, stationList[i].id));
+ }
+}
+
+async function getStationWeatherData(endpoint, weatherStationId, parameters, interval, dateStart, dateEnd){
const response = await fetch(endpoint
+ "?timeStart=" + dateStart
+ "&timeEnd=" + dateEnd
@@ -75,6 +104,18 @@ async function getLocationWeatherData(endpoint, weatherStationId, parameters, in
return await response.json();
}
+async function getLocationWeatherData(endpoint, longitude, latitude, parameters, interval, dateStart, dateEnd){
+ const response = await fetch(endpoint
+ + "?timeStart=" + dateStart
+ + "&timeEnd=" + dateEnd
+ + "&interval=" + interval
+ + "&longitude=" + longitude
+ + "&latitude=" + latitude
+ + "¶meters=" + parameters.join(",")
+ );
+ return await response.json();
+}
+
async function runModel(endpoint, inputData)
{
const response = await fetch(endpoint, {
@@ -112,6 +153,71 @@ function getWeatherParameter(weatherParameterId){
return null;
}
+function getWeatherStationCoordinate(weatherDatasource, weatherStationId)
+{
+ geoJson = JSON.parse(weatherDatasource.spatial.geoJSON);
+ let stationList = [];
+ for(let i=0;i<geoJson.features.length;i++)
+ {
+ let feature = geoJson.features[i];
+ if(feature.id == weatherStationId)
+ {
+ // TODO: Don't assume feature is a point
+ return feature.geometry.coordinates;
+ }
+ }
+
+ return null;
+}
+
+function getPragmaticWeatherParameterList(requestedParameters, availableParameters)
+{
+ let completeList = [];
+ for(let i=0;i<requestedParameters.length;i++)
+ {
+ completeList = completeList.concat(requestedParameters[i], fallbackParams[requestedParameters[i]]);
+ }
+ console.info(completeList);
+ console.info(availableParameters);
+
+ return completeList.filter(param => availableParameters.includes(param));
+
+}
+
+/**
+ * Merges the two datasets. Keeping the primaryData if both sets have data for same time and parameter
+ * If a merge operation is not possible, primaryData is returned unchanged
+ * @param {Object} primaryData
+ * @param {Object} secondaryData
+ */
+function mergeWeatherData(primaryData, secondaryData)
+{
+
+ // Where to start in primaryData?
+ let mergedData = {
+ timeStart: null,
+ timeEnd: null,
+ interval: primaryData.interval,
+ weatherParameters: primaryData.weatherParameters
+ };
+
+ console.info("primaryData.timeStart=" + primaryData.timeStart);
+ console.info("mergedData.timeStart=" + mergedData.timeStart);
+
+}
+
+const fallbackParams = {
+ 1001: [1002],
+ 1002: [1001],
+ 3001: [3002],
+ 3002: [3001],
+ 4002: [4003,4012,4013],
+ 4003: [4002,4013,4012],
+ 4012: [4013,4002,4003],
+ 4013: [4012,4003,4002]
+}
+
+
const weatherParameterList = [
{
"id": 1001,
diff --git a/ipmd/templates/ipmd/saddlegallmidgeform.html b/ipmd/templates/ipmd/saddlegallmidgeform.html
index 6692467ee82e1486e91f55893e0cf0e3874a8dee..3985488860c6b58433e160d386abc42510d64f8b 100644
--- a/ipmd/templates/ipmd/saddlegallmidgeform.html
+++ b/ipmd/templates/ipmd/saddlegallmidgeform.html
@@ -27,10 +27,32 @@
<div class="singleBlockContainer">
<h1>{% trans "Saddle gall midge" %}</h1>
<div id="inputForm"></div>
- <div id="weatherStations" style="display: none;">
- <h2>Weather stations</h2>
- <select class="form-control" name="weatherStationId" id="weatherStationId"></select>
- </div>
+ <div id="weatherDataForm" style="display: none;">
+ <h2>Weather data</h2>
+ <fieldset id="historicDatasourceFields">
+ <legend>Weather datasource (historic)</legend>
+ <select class="form-control" name="weatherDatasourceId" id="weatherDatasourceList" onchange="handleWeatherDatasourceSelected(this);"></select>
+ <div id="historicSourceInfo" style="display: none;"></div>
+ </fieldset>
+ <fieldset id="stationFields" style="display: none;">
+ <select class="form-control" name="weatherStationId" id="weatherStationId" onchange="handleWeatherStationSelected(this);" ></select>
+ </fieldset>
+ <fieldset id="locationFields">
+ <legend>Location (decimal degrees)</legend>
+ <div class="form-group">
+ <label for="latitude">Latitude</label>
+ <input type="number" class="form-control" id="latitude" name="latitude" placeholder="Latitude">
+ </div>
+ <div class="form-group">
+ <label for="longitude">Longitude</label>
+ <input type="number" class="form-control" id="longitude" name="longitude" placeholder="Longitude">
+ </div>
+ </fieldset>
+ <fieldset id="forecastDatasourceFields">
+ <legend>Weather forecasts datasource</legend>
+ <select class="form-control" name="forecastWeatherDatasourceId" id="forecastWeatherDatasourceList" onchange="handleForecastSourceSelected(this);"></select>
+ <div id="forecastSourceInfo" style="display: none;"></div>
+ </fieldset>
<button class="btn btn-primary" type="button" onclick="submitData();">Submit</button>
<div style="aspect-ratio: 2;">
<canvas id="resultChart""></canvas>
@@ -49,13 +71,14 @@
<script type="text/javascript">
// Page globals
var modelMetaData = undefined;
- var weatherDatasource = undefined;
+ var currentWeatherDatasource = undefined;
+ var currentForecastWeatherDatasource = undefined;
var weatherDatasources = undefined;
var editor = undefined;
- const selectList = document.getElementById("weatherStationId");
-
+ var selectList = document.getElementById("weatherStationId");
+
var weatherData = undefined;
-
+
async function initPage() {
modelMetaData = await getModelMetadata("adas.dss","HAPDMA");
document.getElementById("modelDescription").innerHTML= await modelMetaData["description"];
@@ -71,32 +94,90 @@
let fullSchema = JSON.parse(modelMetaData["execution"]["input_schema"]);
if(fullSchema["properties"]["weatherData"] !== undefined)
{
- //console.info("Adding weather stations");
- // Pull weather stations from web service, render list
- weatherDatasource = await getWeatherDatasource("no.nibio.lmt");
+
+ // Pull weather data sources from web service, render lists (historic and forecast sources, some are both)
weatherDatasources = await getWeatherDatasources();
console.info(weatherDatasources);
- stationList = await getWeatherStationList(weatherDatasource);
- stationList.sort((a, b) => {
+ weatherDatasources.sort((a, b) => {
return (a.name < b.name) ? -1 : (a.name > b.name) ? 1 : 0;
});
-
- for(let i=0;i<stationList.length; i++)
+ let weatherDatasourceList = document.getElementById("weatherDatasourceList");
+ weatherDatasourceList.add(new Option("Please select a weather datasource", "-1"));
+ for(let i=0;i<weatherDatasources.length; i++)
{
- selectList.add(new Option(stationList[i].name, stationList[i].id))
+ if(weatherDatasources[i].temporal.historic != null && weatherDatasources[i].temporal.historic.start != null)
+ {
+ weatherDatasourceList.add(new Option(weatherDatasources[i].name, weatherDatasources[i].id));
+ }
+ }
+ let forecastWeatherDatasourceList = document.getElementById("forecastWeatherDatasourceList");
+ forecastWeatherDatasourceList.add(new Option("Please select a weather forecast datasource", "-1"));
+ for(let i=0;i<weatherDatasources.length; i++)
+ {
+ if(weatherDatasources[i].temporal.forecast != null && weatherDatasources[i].temporal.forecast > 0)
+ {
+ forecastWeatherDatasourceList.add(new Option(weatherDatasources[i].name, weatherDatasources[i].id));
+ }
}
- document.getElementById("weatherStations").style.display = "block";
- console.info(weatherDatasource);
+
+ document.getElementById("weatherDataForm").style.display = "block";
+
}
// TODO: Remove this auto-mock!!
editor.getValue().optionalData.startDate="2023-08-01";
editor.getValue().optionalData.endDate="2023-08-10";
- submitData();
+ document.getElementById("longitude").value="11.781989";
+ document.getElementById("latitude").value="59.680468";
+ //submitData();
}
initPage();
+ function handleWeatherDatasourceSelected(weatherDatasourceList){
+ currentWeatherDatasource = getWeatherDatasource(
+ weatherDatasources,
+ weatherDatasourceList.options[weatherDatasourceList.selectedIndex].value
+ );
+ let sourceInfo = document.getElementById("historicSourceInfo");
+ sourceInfo.innerHTML = currentWeatherDatasource.description;
+ sourceInfo.style.display="block";
+ if(currentWeatherDatasource.access_type == "stations")
+ {
+ // Display the weather stations
+ renderWeatherStationSelectList(getWeatherStationList(currentWeatherDatasource), selectList);
+ document.getElementById("stationFields").style.display = "block";
+ }
+ else
+ {
+ // Location based API
+ // Display lat/lon input fields
+ document.getElementById("stationFields").style.display = "none";
+ // TODO: Add map?
+
+ }
+ console.info(currentWeatherDatasource);
+ }
+
+ function handleForecastSourceSelected(forecastSourceSelectList)
+ {
+ currentForecastWeatherDatasource = getWeatherDatasource(
+ weatherDatasources,
+ forecastWeatherDatasourceList.options[forecastWeatherDatasourceList.selectedIndex].value
+ );
+ let sourceInfo = document.getElementById("historicSourceInfo");
+ sourceInfo.innerHTML = currentWeatherDatasource.description;
+ sourceInfo.style.display="block";
+ }
+
+ function handleWeatherStationSelected(weatherStationSelectList)
+ {
+ let weatherStationId = selectList.options[selectList.selectedIndex].value;
+ let stationCoordinate = getWeatherStationCoordinate(currentWeatherDatasource, weatherStationId);
+ console.info(stationCoordinate);
+ document.getElementById("longitude").value = stationCoordinate[0];
+ document.getElementById("latitude").value = stationCoordinate[1];
+ }
async function submitData(){
console.info("submitData!");
@@ -115,24 +196,92 @@
if(fullSchema["properties"]["weatherData"] !== undefined)
{
console.info("Need to get weather data!");
- let weatherStationId = selectList.options[selectList.selectedIndex].value;
+ let forecastData = undefined;
+ // 1. Historic weather data
+ if(currentWeatherDatasource != undefined)
+ {
+ if(currentWeatherDatasource.access_type == "stations")
+ {
+ let weatherStationId = selectList.options[selectList.selectedIndex].value;
- weatherData = await getLocationWeatherData(
- weatherDatasource.endpoint,
- weatherStationId,
- (function (){
- let parameterList = []
- modelMetaData.input.weather_parameters.forEach(function(weatherParameter){
- parameterList.push(weatherParameter.parameter_code)
- })
- return parameterList;
- }()),
- 3600,
- inputData.optionalData.startDate,
- inputData.optionalData.endDate,
- );
+ weatherData = await getStationWeatherData(
+ getWeatherDatasourceEndpoint(currentWeatherDatasource),
+ weatherStationId,
+ getPragmaticWeatherParameterList(
+ function (){
+ let parameterList = []
+ modelMetaData.input.weather_parameters.forEach(function(weatherParameter){
+ parameterList.push(weatherParameter.parameter_code)
+ })
+ return parameterList;
+ }(),
+ currentWeatherDatasource.parameters.common
+ ),
+ 3600,
+ inputData.optionalData.startDate,
+ inputData.optionalData.endDate,
+ );
+
+ }
+ else
+ {
+ weatherData = await getLocationWeatherData(
+ getWeatherDatasourceEndpoint(currentWeatherDatasource),
+ document.getElementById("longitude").value,
+ document.getElementById("latitude").value,
+ getPragmaticWeatherParameterList(
+ function (){
+ let parameterList = []
+ modelMetaData.input.weather_parameters.forEach(function(weatherParameter){
+ parameterList.push(weatherParameter.parameter_code)
+ })
+ return parameterList;
+ }(),
+ currentWeatherDatasource.parameters.common
+ ),
+ 3600,
+ inputData.optionalData.startDate,
+ inputData.optionalData.endDate,
+ );
+ }
+ }
+
+ // 2. Forecast weather data
+ if(currentForecastWeatherDatasource != undefined)
+ {
+ forecastData = await getLocationWeatherData(
+ getWeatherDatasourceEndpoint(currentForecastWeatherDatasource),
+ document.getElementById("longitude").value,
+ document.getElementById("latitude").value,
+ getPragmaticWeatherParameterList(
+ function (){
+ let parameterList = []
+ modelMetaData.input.weather_parameters.forEach(function(weatherParameter){
+ parameterList.push(weatherParameter.parameter_code)
+ })
+ return parameterList;
+ }(),
+ currentForecastWeatherDatasource.parameters.common
+ ),
+ 3600,
+ inputData.optionalData.startDate,
+ inputData.optionalData.endDate,
+ );
+ // Merge if both historic and forecast data have been collected
+ if(weatherData == undefined)
+ {
+ weatherData = forecastData;
+ }
+ else
+ {
+ // Is NULL until method is implemented
+ weatherData = mergeWeatherData(weatherData, forecastData);
+ }
+ }
+
+
inputData["weatherData"] = weatherData;
- console.info(weatherData);
+ //console.info(weatherData);
}
// Ready to call server?
//console.info(JSON.stringify(inputData));
@@ -204,6 +353,8 @@
}
+
+
// Mock result!!! Waiting for ADAS to fix CORS issue
const mockResult={
"timeStart": "2023-06-30T22:00:00+00:00",