Skip to content
Snippets Groups Projects
Commit 9d548bdc authored by Tor-Einar Skog's avatar Tor-Einar Skog
Browse files

Add weather data sources (historic and forecast)

parent 9c820098
No related branches found
No related tags found
2 merge requests!13Saddlegallmidge form idec 372,!12feat: Add test page (spatial) with mapserver layer in openlayers map
...@@ -40,10 +40,27 @@ async function getModelInputSchema(dssId,modelId) { ...@@ -40,10 +40,27 @@ async function getModelInputSchema(dssId,modelId) {
return await response.json(); return await response.json();
} }
async function getWeatherDatasource(weatherDatasourceId) /*async function getWeatherDatasource(weatherDatasourceId)
{ {
const response = await fetch(ipmdWeatherApiURL + "rest/weatherdatasource/" + weatherDatasourceId); const response = await fetch(ipmdWeatherApiURL + "rest/weatherdatasource/" + weatherDatasourceId);
return await response.json(); 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() async function getWeatherDatasources()
...@@ -61,10 +78,22 @@ function getWeatherStationList(weatherDatasource){ ...@@ -61,10 +78,22 @@ function getWeatherStationList(weatherDatasource){
let station = {"id":feature.id, "name":feature.properties.name} let station = {"id":feature.id, "name":feature.properties.name}
stationList.push(station); stationList.push(station);
} }
stationList.sort((a, b) => {
return (a.name < b.name) ? -1 : (a.name > b.name) ? 1 : 0;
});
return stationList; 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 const response = await fetch(endpoint
+ "?timeStart=" + dateStart + "?timeStart=" + dateStart
+ "&timeEnd=" + dateEnd + "&timeEnd=" + dateEnd
...@@ -75,6 +104,18 @@ async function getLocationWeatherData(endpoint, weatherStationId, parameters, in ...@@ -75,6 +104,18 @@ async function getLocationWeatherData(endpoint, weatherStationId, parameters, in
return await response.json(); 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
+ "&parameters=" + parameters.join(",")
);
return await response.json();
}
async function runModel(endpoint, inputData) async function runModel(endpoint, inputData)
{ {
const response = await fetch(endpoint, { const response = await fetch(endpoint, {
...@@ -112,6 +153,71 @@ function getWeatherParameter(weatherParameterId){ ...@@ -112,6 +153,71 @@ function getWeatherParameter(weatherParameterId){
return null; 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 = [ const weatherParameterList = [
{ {
"id": 1001, "id": 1001,
......
...@@ -27,10 +27,32 @@ ...@@ -27,10 +27,32 @@
<div class="singleBlockContainer"> <div class="singleBlockContainer">
<h1>{% trans "Saddle gall midge" %}</h1> <h1>{% trans "Saddle gall midge" %}</h1>
<div id="inputForm"></div> <div id="inputForm"></div>
<div id="weatherStations" style="display: none;"> <div id="weatherDataForm" style="display: none;">
<h2>Weather stations</h2> <h2>Weather data</h2>
<select class="form-control" name="weatherStationId" id="weatherStationId"></select> <fieldset id="historicDatasourceFields">
</div> <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> <button class="btn btn-primary" type="button" onclick="submitData();">Submit</button>
<div style="aspect-ratio: 2;"> <div style="aspect-ratio: 2;">
<canvas id="resultChart""></canvas> <canvas id="resultChart""></canvas>
...@@ -49,13 +71,14 @@ ...@@ -49,13 +71,14 @@
<script type="text/javascript"> <script type="text/javascript">
// Page globals // Page globals
var modelMetaData = undefined; var modelMetaData = undefined;
var weatherDatasource = undefined; var currentWeatherDatasource = undefined;
var currentForecastWeatherDatasource = undefined;
var weatherDatasources = undefined; var weatherDatasources = undefined;
var editor = undefined; var editor = undefined;
const selectList = document.getElementById("weatherStationId"); var selectList = document.getElementById("weatherStationId");
var weatherData = undefined; var weatherData = undefined;
async function initPage() { async function initPage() {
modelMetaData = await getModelMetadata("adas.dss","HAPDMA"); modelMetaData = await getModelMetadata("adas.dss","HAPDMA");
document.getElementById("modelDescription").innerHTML= await modelMetaData["description"]; document.getElementById("modelDescription").innerHTML= await modelMetaData["description"];
...@@ -71,32 +94,90 @@ ...@@ -71,32 +94,90 @@
let fullSchema = JSON.parse(modelMetaData["execution"]["input_schema"]); let fullSchema = JSON.parse(modelMetaData["execution"]["input_schema"]);
if(fullSchema["properties"]["weatherData"] !== undefined) if(fullSchema["properties"]["weatherData"] !== undefined)
{ {
//console.info("Adding weather stations");
// Pull weather stations from web service, render list // Pull weather data sources from web service, render lists (historic and forecast sources, some are both)
weatherDatasource = await getWeatherDatasource("no.nibio.lmt");
weatherDatasources = await getWeatherDatasources(); weatherDatasources = await getWeatherDatasources();
console.info(weatherDatasources); console.info(weatherDatasources);
stationList = await getWeatherStationList(weatherDatasource); weatherDatasources.sort((a, b) => {
stationList.sort((a, b) => {
return (a.name < b.name) ? -1 : (a.name > b.name) ? 1 : 0; return (a.name < b.name) ? -1 : (a.name > b.name) ? 1 : 0;
}); });
let weatherDatasourceList = document.getElementById("weatherDatasourceList");
for(let i=0;i<stationList.length; i++) 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!! // TODO: Remove this auto-mock!!
editor.getValue().optionalData.startDate="2023-08-01"; editor.getValue().optionalData.startDate="2023-08-01";
editor.getValue().optionalData.endDate="2023-08-10"; editor.getValue().optionalData.endDate="2023-08-10";
submitData(); document.getElementById("longitude").value="11.781989";
document.getElementById("latitude").value="59.680468";
//submitData();
} }
initPage(); 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(){ async function submitData(){
console.info("submitData!"); console.info("submitData!");
...@@ -115,24 +196,92 @@ ...@@ -115,24 +196,92 @@
if(fullSchema["properties"]["weatherData"] !== undefined) if(fullSchema["properties"]["weatherData"] !== undefined)
{ {
console.info("Need to get weather data!"); 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( weatherData = await getStationWeatherData(
weatherDatasource.endpoint, getWeatherDatasourceEndpoint(currentWeatherDatasource),
weatherStationId, weatherStationId,
(function (){ getPragmaticWeatherParameterList(
let parameterList = [] function (){
modelMetaData.input.weather_parameters.forEach(function(weatherParameter){ let parameterList = []
parameterList.push(weatherParameter.parameter_code) modelMetaData.input.weather_parameters.forEach(function(weatherParameter){
}) parameterList.push(weatherParameter.parameter_code)
return parameterList; })
}()), return parameterList;
3600, }(),
inputData.optionalData.startDate, currentWeatherDatasource.parameters.common
inputData.optionalData.endDate, ),
); 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; inputData["weatherData"] = weatherData;
console.info(weatherData); //console.info(weatherData);
} }
// Ready to call server? // Ready to call server?
//console.info(JSON.stringify(inputData)); //console.info(JSON.stringify(inputData));
...@@ -204,6 +353,8 @@ ...@@ -204,6 +353,8 @@
} }
// Mock result!!! Waiting for ADAS to fix CORS issue // Mock result!!! Waiting for ADAS to fix CORS issue
const mockResult={ const mockResult={
"timeStart": "2023-06-30T22:00:00+00:00", "timeStart": "2023-06-30T22:00:00+00:00",
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment