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

Robust datasource map

parent 5ca17b04
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
......@@ -23,7 +23,8 @@
*/
const ipmdDSSApiURL = "https://platform.ipmdecisions.net/api/dss/";
const ipmdWeatherApiURL = "https://platform.ipmdecisions.net/api/wx/";
//const ipmdWeatherApiURL = "https://platform.ipmdecisions.net/api/wx/";
const ipmdWeatherApiURL = "http://ipmdlocal/api/wx/";
async function getModelMetadata(dssId,modelId) {
......@@ -281,10 +282,10 @@ function mergeWeatherData(primaryData, secondaryData)
});
// Calculate dimensions of the merged data set
console.info("timeStart=" + mergedData.timeStart.format("YYYY-MM-DD") + ", timeEnd=" + mergedData.timeEnd.format("YYYY-MM-DD"));
//console.info("timeStart=" + mergedData.timeStart.format("YYYY-MM-DD") + ", timeEnd=" + mergedData.timeEnd.format("YYYY-MM-DD"));
let length = (getUnix(mergedData.timeEnd) - getUnix(mergedData.timeStart)) / mergedData.interval;
let width = mergedData.weatherParameters.length;
console.info(length + "*" + width);
//console.info(length + "*" + width);
let dataSet = Array.from(Array(length), () => new Array(width));
//console.info(dataSet);
// Keeping track of offsets (if dataset does not cover the entire combined period of the merging datasets)
......@@ -293,8 +294,8 @@ function mergeWeatherData(primaryData, secondaryData)
let primaryDataSet = primaryData.locationWeatherData[0].data;
let secondaryDataSet = secondaryData.locationWeatherData[0].data;
console.info("primaryOffset=" + primaryOffset);
console.info(primaryDataSet);
//console.info("primaryOffset=" + primaryOffset);
//console.info(primaryDataSet);
// Finally: Merge the data!
for(let i=0;i<dataSet.length;i++)
......@@ -312,7 +313,7 @@ function mergeWeatherData(primaryData, secondaryData)
}
if(primaryOffset <= i && i-primaryOffset < primaryDataSet.length )
{
console.info(i-primaryOffset);
//console.info(i-primaryOffset);
for(let j=0; j<primaryData.weatherParameters.length;j++)
{
if(!isEmpty(primaryDataSet[i-primaryOffset][j]))
......@@ -331,7 +332,7 @@ function mergeWeatherData(primaryData, secondaryData)
function getUnix(aDate)
{
console.info(typeof aDate);
//console.info(typeof aDate);
if(typeof aDate == "string")
{
aDate = moment(aDate);
......@@ -345,6 +346,111 @@ function isEmpty(val)
return val === undefined || val === null;
}
// Keeping track of maps
var maps = {};
/**
* Requires OpenLayers v. 4
* @param {String} containerId
* @param {JSON} geoJson
*/
async function initDataSourceMap(containerId, geoJson, countryCodeList)
{
let map = maps[containerId];
// Delete whatever was there
if(map != undefined)
{
map.setTarget(undefined);
map = null;
}
//console.info(countryCodeList);
//console.info(geoJson);
// If no data, no map
if((geoJson == undefined || geoJson == null || geoJson.features.length == 0) && (countryCodeList == null || countryCodeList.length == 0))
{
document.getElementById(containerId).innerHTML = "NO GEODATA PROVIDED BY WEATHER DATA SOURCE";
return;
}
else{
document.getElementById(containerId).innerHTML = "";
}
var backgroundLayer = new ol.layer.Tile({
source: new ol.source.OSM({
attributions: [
new ol.Attribution({
html: "TEST"
})
]
})
});
// Creating the map
map = new ol.Map({
target: containerId,
layers: [backgroundLayer],
renderer: 'canvas'
});
maps[containerId] = map;
// Setting zoom and center for the map (need to do this after creating map. so that we can transform our
// center to correct map projection)
var view = new ol.View({
center: ol.proj.transform([10,65], 'EPSG:4326', map.getView().getProjection().getCode()),
zoom: 7,
maxZoom: 7
});
map.setView(view);
let features = new ol.Collection();
let format = new ol.format.GeoJSON();
let drawnFeatures = undefined;
// If we have geoJson available, we display that
if (geoJson != null && geoJson.features.length > 0) {
drawnFeatures = format.readFeatures(geoJson, {
dataProjection: 'EPSG:4326',
featureProjection: map.getView().getProjection().getCode()
});
}
// If not, we have to get geoJson mapped to the countries
else if (countryCodeList != undefined && countryCodeList != null && countryCodeList.length > 0) {
let countryBoundaries = await getCountryBoundaries(countryCodeList);
//console.info(countryBoundaries);
drawnFeatures = await format.readFeatures(countryBoundaries, {
dataProjection: 'EPSG:4326',
featureProjection: map.getView().getProjection().getCode()
});
}
if(drawnFeatures != undefined)
{
//console.info(drawnFeatures);
var featureOverlay = new ol.layer.Vector({
source: new ol.source.Vector({
features: features
})
});
featureOverlay.getSource().addFeatures(drawnFeatures);
featureOverlay.setMap(map);
extent = featureOverlay.getSource().getExtent();
map.getView().fit(extent, map.getSize());
}
}
async function getCountryBoundaries(countryCodeList)
{
const response = await fetch(ipmdWeatherApiURL + "rest/country/" + countryCodeList.join(","));
return await response.json();
}
const fallbackParams = {
1001: [1002],
1002: [1001],
......@@ -357,6 +463,7 @@ const fallbackParams = {
}
const weatherParameterList = [
{
"id": 1001,
......
......@@ -29,30 +29,48 @@
<div id="inputForm"></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 class="row">
<div class="col-md-8">
<fieldset id="historicDatasourceFields">
<legend>Weather datasource (historic)</legend>
<select class="form-control" name="weatherDatasourceId" id="weatherDatasourceList" onchange="handleWeatherDatasourceSelected(this);"></select>
<div id="historicSourceInfoPanel" class="panel panel-default" style="display: none;">
<div class="panel-body" id="historicSourceInfo"></div>
</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>
</div>
<div class="form-group">
<label for="longitude">Longitude</label>
<input type="number" class="form-control" id="longitude" name="longitude" placeholder="Longitude">
<div class="col-md-4">
<div id="historicDatasourceMap"></div>
</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>
</div>
<div class="row">
<div class="col-md-8">
<fieldset id="forecastDatasourceFields">
<legend>Weather forecasts datasource</legend>
<select class="form-control" name="forecastWeatherDatasourceId" id="forecastWeatherDatasourceList" onchange="handleForecastSourceSelected(this);"></select>
<div id="forecastSourceInfoPanel" class="panel panel-default" style="display: none;">
<div class="panel-body" id="forecastSourceInfo"></div>
</div>
</fieldset>
</div>
<div class="col-md-4">
<div id="forecastDatasourceMap"></div>
</div>
</div>
<button class="btn btn-primary" type="button" onclick="submitData();">Submit</button>
<div style="aspect-ratio: 2;">
<canvas id="resultChart""></canvas>
......@@ -62,11 +80,17 @@
<pre id="modelDescription"></pre>
</div>
{% endblock %}
{% block extendCSS %}
<link rel="stylesheet" href="{% static "css/3rdparty/ol.css" %}" type="text/css">
{% endblock%}
{% block customJS %}
<script src="https://cdn.jsdelivr.net/npm/@json-editor/json-editor@latest/dist/jsoneditor.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script type="text/javascript" src="{% static "js/3rdparty/moment.min.js" %}"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-moment"></script>
<script type="text/javascript" src="{% static "js/3rdparty/ol-debug.js" %}"></script>
<script type="text/javascript" src="{% static "ipmd/js/ipmdlib.js" %}"></script>
<script type="text/javascript">
// Page globals
......@@ -83,13 +107,19 @@
modelMetaData = await getModelMetadata("adas.dss","HAPDMA");
document.getElementById("modelDescription").innerHTML= await modelMetaData["description"];
inputFormSchema = await getModelInputSchema("adas.dss","HAPDMA")
console.info(inputFormSchema);
inputFormSchema.title ="Model input data";
//console.info(inputFormSchema);
const element = document.getElementById('inputForm');
// See https://github.com/json-editor/json-editor#options
editor = new JSONEditor(element,
{
ajax: true,
schema: inputFormSchema,
theme: "bootstrap4"
theme: "bootstrap4",
iconlib: "fontawesome4",
disable_edit_json: true,
disable_properties: true
});
let fullSchema = JSON.parse(modelMetaData["execution"]["input_schema"]);
if(fullSchema["properties"]["weatherData"] !== undefined)
......@@ -97,7 +127,7 @@
// Pull weather data sources from web service, render lists (historic and forecast sources, some are both)
weatherDatasources = await getWeatherDatasources();
console.info(weatherDatasources);
//console.info(weatherDatasources);
weatherDatasources.sort((a, b) => {
return (a.name < b.name) ? -1 : (a.name > b.name) ? 1 : 0;
});
......@@ -138,10 +168,21 @@
currentWeatherDatasource = getWeatherDatasource(
weatherDatasources,
weatherDatasourceList.options[weatherDatasourceList.selectedIndex].value
);
);
let sourceInfoPanel = document.getElementById("historicSourceInfoPanel");
if(currentWeatherDatasource == null)
{
sourceInfoPanel.style.display="none";
document.getElementById("stationFields").style.display = "none";
return;
}
// Display map
initDataSourceMap("historicDatasourceMap", JSON.parse(currentWeatherDatasource.spatial.geoJSON), currentWeatherDatasource.spatial.countries);
let sourceInfo = document.getElementById("historicSourceInfo");
sourceInfo.innerHTML = currentWeatherDatasource.description;
sourceInfo.style.display="block";
sourceInfoPanel.style.display="block";
if(currentWeatherDatasource.access_type == "stations")
{
// Display the weather stations
......@@ -156,7 +197,7 @@
// TODO: Add map?
}
console.info(currentWeatherDatasource);
//console.info(currentWeatherDatasource);
}
function handleForecastSourceSelected(forecastSourceSelectList)
......@@ -164,29 +205,36 @@
currentForecastWeatherDatasource = getWeatherDatasource(
weatherDatasources,
forecastWeatherDatasourceList.options[forecastWeatherDatasourceList.selectedIndex].value
);
let sourceInfo = document.getElementById("historicSourceInfo");
sourceInfo.innerHTML = currentWeatherDatasource.description;
sourceInfo.style.display="block";
);
let sourceInfoPanel = document.getElementById("forecastSourceInfoPanel");
if(currentForecastWeatherDatasource == null)
{
sourceInfoPanel.style.display="none";
return;
}
let sourceInfo = document.getElementById("forecastSourceInfo");
sourceInfo.innerHTML = currentForecastWeatherDatasource.description;
sourceInfoPanel.style.display="block";
}
function handleWeatherStationSelected(weatherStationSelectList)
{
let weatherStationId = selectList.options[selectList.selectedIndex].value;
let stationCoordinate = getWeatherStationCoordinate(currentWeatherDatasource, weatherStationId);
console.info(stationCoordinate);
//console.info(stationCoordinate);
document.getElementById("longitude").value = stationCoordinate[0];
document.getElementById("latitude").value = stationCoordinate[1];
}
async function submitData(){
console.info("submitData!");
//console.info("submitData!");
let inputData = editor.getValue();
console.info(inputData);
//console.info(inputData);
// Add hidden parameters
let fullSchema = JSON.parse(modelMetaData["execution"]["input_schema"]);
const hiddenParameters = modelMetaData["execution"]["input_schema_categories"]["hidden"];
console.info(hiddenParameters);
//console.info(hiddenParameters);
for(let i=0;i<hiddenParameters.length;i++)
{
let hiddenParameter = hiddenParameters[i];
......@@ -195,7 +243,7 @@
// Check for weatherData element. Assuming it's at the root node
if(fullSchema["properties"]["weatherData"] !== undefined)
{
console.info("Need to get weather data!");
//console.info("Need to get weather data!");
let forecastData = undefined;
// 1. Historic weather data
if(currentWeatherDatasource != undefined)
......@@ -296,7 +344,7 @@
let chartData = [];
// Generate dates
let dates = getDateArray(result.timeStart, 86400, result.locationResult[0].length);
console.info(dates);
//console.info(dates);
for(let i=0; i< result.resultParameters.length;i++)
{
let dataset = {label:result.resultParameters[i], data: []}
......@@ -341,7 +389,8 @@
html += "</tr></thead>"
html += "<tbody>";
for(let i=0;i< weatherData.locationWeatherData[0].length;i++)
//console.info("Weatherdata length: " + weatherData.locationWeatherData[0].data.length);
for(let i=0;i< weatherData.locationWeatherData[0].data.length;i++)
{
html += "<tr><td>" + moment(dates[i]).format("YYYY-MM-DD HH:mm:ss") + "</td>";
weatherData.locationWeatherData[0].data[i].forEach(function(val){html+="<td>" + val + "</td>";})
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment