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

Merge branch 'feature/vipsutv-757-byggbrunflekk' into feature/vipsutv-801-eplevikler

parents 058cf760 9df20902
Branches
Tags
No related merge requests found
......@@ -165,6 +165,10 @@ MAP_WARNING_LEGEND_PLACEMENT = 4
# The attribution text that appears in a corner of the map
MAP_ATTRIBUTION = "&copy; <a href='http://www.openstreetmap.org'>OpenStreetMap</a> contributors"
# Geoapify is used for getting information (timezone etc) about a given point (latitude, longitude)
# Register at https://www.geoapify.com, and create an API key in order to be able to use the API
GEOAPIFY_API_KEY='api-key'
# The message tags to use on the front page
FRONTPAGE_MESSAGE_TAG_IDS = [1,2,3]
......
......@@ -82,9 +82,6 @@ STATICFILES_FINDERS = (
# 'django.contrib.staticfiles.finders.DefaultStorageFinder',
)
MIDDLEWARE = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
......
......@@ -204,6 +204,18 @@ function getLocalSettings(keys, allOrNothing)
return (allOrNothing && keys.length != Object.keys(retVal).length) ? null : retVal;
}
/**
* Namespace all strings in given array
*/
function getNameSpaced(nameSpace, anArray) {
var retVal = [];
for(var i = 0; i<anArray.length;i++)
{
retVal.push(nameSpace + "." + anArray[i]);
}
return retVal;
};
/**
* Converts a string like this "6,3,23,1,100" => [6,3,23,1,100]
* @param csvString
......@@ -421,3 +433,28 @@ function getLocalizedCropCategoryName(cropCategory)
// Then we give up
return gettext("Unnamed");
}
async function getLocationInformation(latitude, longitude) {
let location = "Unknown"
let timezone = "Europe/Oslo"
try {
const response = await fetch(`https://api.geoapify.com/v1/geocode/reverse?lat=${latitude}&lon=${longitude}&format=json&apiKey=${settings.geoapifyApiKey}`);
const respJson = await response.json()
if(respJson.results && respJson.results.length > 0) {
location = respJson.results[0].city ? respJson.results[0].city : respJson.results[0].formatted;
timezone = respJson.results[0].timezone.name;
} else {
console.error("Unable to get location information for for lat=" + latitude + " lon=" + longitude)
}
} catch (error) {
console.error("Error fetching location information", error)
}
return {
latitude: latitude,
longitude: longitude,
location: location,
timezone: timezone
}
}
......@@ -43,6 +43,8 @@ var settings = {
userIsIE: {{user_is_ie|yesno:"true,false"}},
geoapifyApiKey: "{{settings.GEOAPIFY_API_KEY}}",
highchartsGlobalOptions: {
lang: {
shortMonths: [gettext("Jan"),gettext("Feb"),gettext("Mar"),gettext("Apr"),gettext("May"),gettext("Jun"),gettext("Jul"),gettext("Aug"),gettext("Sep"),gettext("Oct"),gettext("Nov"),gettext("Dec")],
......
......@@ -18,18 +18,28 @@
],
"_comment" : "Structure of the septoriaHumidityForm and how to validate it",
"fields": [
{
"name" : "latitude",
"dataType" : "DOUBLE",
"required" : false
},
{
"name" : "longitude",
"dataType" : "DOUBLE",
"required" : false
},
{
"name" : "organizationId_countryCode",
"dataType" : "STRING",
"fieldType" : "SELECT_SINGLE",
"required" : true,
"required" : false,
"nullValue" : "None"
},
{
"name" : "weatherStationId",
"dataType" : "STRING",
"fieldType" : "SELECT_SINGLE",
"required" : true,
"required" : false,
"nullValue" : ""
},
{
......
......@@ -32,6 +32,7 @@
<link type="text/css" rel="stylesheet" href="https://logic.testvips.nibio.no/css/mapModal.css" />
<style>
/* Added when integrating map, should perhaps be moved to main css file. */
input#latitude, input#longitude {
width: 30%;
display: inline-block;
......@@ -42,12 +43,15 @@
display: inline-block;
margin: 10px 10px 10px 0;
}
.main-label {
font-size: 1.8rem;
font-weight: 500 !important;
fieldset {
margin-bottom: 30px;
}
.space {
margin-top: 40px;
.btn-map {
margin-top: 5px;
margin-bottom: 20px;
}
#gridPointInfo {
margin: 10px 0;
}
</style>
{% endblock %}
......@@ -63,99 +67,112 @@
<div class="row">
<div class="col-md-12 mb-3">
<p class="lead">
<a href="https://www.vips-landbruk.no/forecasts/models/BARLEYNETB/">Elens modell for byggbrunflekk</a> lager en tilvekstkurve for byggbrunflekk med værdata som grunnlag.
Du kan velge å kjøre modellen med værdata for et vilkårlig punkt i kartet, eller fra en værstasjon. Det er i tillegg nødvendig å legge inn informasjon om angrep av sjukdommen, samt
sådato, sort, vekstskifte og jordarbeiding. Dersom du har en privat værstasjon som er registrert i Vips, vil denne være tilgjengelig i skjemaet etter <a href="https://logic.vips.nibio.no/login">innlogging</a>.
<a href="https://www.vips-landbruk.no/forecasts/models/BARLEYNETB/">Elens modell for byggbrunflekk</a> lager en utviklingskurve for byggbrunflekk med værdata som grunnlag.
Du kan velge å kjøre modellen med værdata for et vilkårlig punkt i kartet, eller fra en værstasjon. Det er i tillegg nødvendig å legge inn informasjon om nivået på angrep av sjukdommen, samt
sådato, sort, vekstskifte og jordarbeiding. Dersom du har en <a href="https://www.vips-landbruk.no/information/27/">privat værstasjon</a> som er registrert i VIPS, vil denne være tilgjengelig i skjemaet etter <a href="https://logic.vips.nibio.no/login">innlogging</a>.
</p>
</div>
</div>
<div class="row">
<div class="col-md-6">
<input type="hidden" name="timeZone" value="Europe/Oslo"/>
<div class="form-group">
<label class="main-label">Jeg vil bruke værdata</label>
<div class="radio">
<label>
<input type="radio" name="weatherdataType" id="coordinates" value="coordinates" checked onchange="displayCoordinatesInput()">
for et spesifikt punkt (koordinater)
</label>
<div id="input-coordinates">
<input type="text" class="form-control" name="latitude" id="latitude" placeholder="Breddegrad" aria-label="Breddegrad">
<input type="text" class="form-control" name="longitude" id="longitude" placeholder="Lengdegrad" aria-label="Lengdegrad">
<button type="button" class="btn btn-primary" onclick="openCoordinatesMap()"><i class="fa fa-map-marker fa-lg"></i> Velg i kart</button>
<fieldset>
<legend>Jeg vil bruke værdata</legend>
<div class="form-group">
<div class="radio">
<label>
<input type="radio" name="weatherDataSourceType" id="grid" value="grid" onchange="displayCoordinatesInput()">
for et punkt i kartet
</label>
<div id="input-coordinates">
<input type="hidden" class="form-control" name="latitude" id="latitude" placeholder="Breddegrad" aria-label="Breddegrad">
<input type="hidden" class="form-control" name="longitude" id="longitude" placeholder="Lengdegrad" aria-label="Lengdegrad">
<input type="hidden" class="form-control" name="timezone" id="timezone" placeholder="Tidssone" aria-label="Tidssone">
<div id="gridPointInfo"></div>
<button type="button" class="btn btn-primary btn-map" onclick="openCoordinatesMap()"><i class="fa fa-map-marker fa-lg"></i>&nbsp;&nbsp;Velg i kart</button>
</div>
<div id="coordinates-map" class="map-modal"></div>
</div>
<div id="coordinates-map" class="map-modal"></div>
</div>
<div class="radio">
<label>
<input type="radio" name="weatherdataType" id="weatherstation" value="weatherstation" onchange="displayWeatherstationInput()">
fra en værstasjon
</label>
<div id="input-weatherstation" style="display: none;">
<select name="weatherStationId" id="weatherStationId" class="form-control" onblur="validateField(this);"></select>
<button type="button" class="btn btn-primary" onclick="openPoiMap()"><i class="fa fa-map-marker fa-lg"></i> Velg i kart</button>
<div class="radio">
<label>
<input type="radio" name="weatherDataSourceType" id="weatherstation" value="weatherstation" onchange="displayWeatherstationInput()">
fra en værstasjon
</label>
<div id="input-weatherstation" style="display: none;">
<select name="weatherStationId" id="weatherStationId" class="form-control">
<option value="">Velg værstasjon</option>
</select>
<button type="button" class="btn btn-primary btn-map" onclick="openPoiMap()"><i class="fa fa-map-marker fa-lg"></i>&nbsp;&nbsp;Velg i kart</button>
</div>
<div id="poi-map" class="map-modal"></div>
</div>
<div id="poi-map" class="map-modal"></div>
<span class="help-block" id="{{ form_id }}_latitude_validation"></span>
<span class="help-block" id="{{ form_id }}_longitude_validation"></span>
<span class="help-block" id="{{ form_id }}_weatherStationId_validation"></span>
</div>
</fieldset>
<fieldset>
<legend>Jeg dyrker</legend>
<div class="form-group">
<select name="cropOrganismId" id="cropOrganismId" class="form-control" onblur="validateField(this);">
</select>
<span class="help-block" id="{{ form_id }}_cropOrganismId_validation"></span>
</div>
<div class="form-group">
<label for="sowingDate">{% trans "Sowing date" %}</label>
<input type="date" id="sowingDate" name="sowingDate" class="form-control" max="{{max_sowing_date|date:'Y-m-d'}}" onblur="validateField(this);" placeholder="Sådato"/>
<span class="help-block" id="{{ form_id }}_sowingDate_validation"></span>
</div>
<div class="form-group">
<label for="sameCropAsLastSeason">Vekstskifte</label><br>
<input type="checkbox" id="sameCropAsLastSeason" name="sameCropAsLastSeason"/> Jeg dyrket bygg på samme skifte i fjor<br/>
<span class="help-block" id="{{ form_id }}_sameCropAsLastSeason_validation"></span>
</div>
<span class="help-block" id="{{ form_id }}_latitude_validation"></span>
<span class="help-block" id="{{ form_id }}_weatherStationId_validation"></span>
<span class="help-block" id="{{ form_id }}_longitude_validation"></span>
</div>
<div class="form-group space">
<label class="main-label" for="cropOrganismId">Jeg har dyrket</label>
<select name="cropOrganismId" id="cropOrganismId" class="form-control" onblur="validateField(this);">
</select>
<span class="help-block" id="{{ form_id }}_cropOrganismId_validation"></span>
</div>
<div class="form-group">
<label for="sowingDate">{% trans "Sowing date" %}</label>
<input type="date" id="sowingDate" name="sowingDate" class="form-control" max="{{max_sowing_date|date:'Y-m-d'}}" onblur="validateField(this);" placeholder="Sådato"/>
<span class="help-block" id="{{ form_id }}_sowingDate_validation"></span>
</div>
<div class="form-group">
<label for="sameCropAsLastSeason">Vekstskifte</label><br>
<input type="checkbox" id="sameCropAsLastSeason" name="sameCropAsLastSeason"/> Jeg dyrket bygg på samme skifte i fjor<br/>
<span class="help-block" id="{{ form_id }}_sameCropAsLastSeason_validation"></span>
</div>
<div class="form-group">
<label for="plowed">Jordarbeiding</label><br>
<input type="checkbox" id="plowed" name="plowed"/> Jeg har pløyd<br/>
<span class="help-block" id="{{ form_id }}_plowed_validation"></span>
</div>
<div class="form-group">
<label for="plowed">Jordarbeiding</label><br>
<input type="checkbox" id="plowed" name="plowed"/> Jeg har pløyd<br/>
<span class="help-block" id="{{ form_id }}_plowed_validation"></span>
</div>
</fieldset>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="observationDate" class="main-label" for="observationDate">Observasjonsdato</label>
<input type="date" id="observationDate" name="observationDate" class="form-control"/>
<span class="help-block" id="{{ form_id }}_observationDate_validation"></span>
</div>
<div class="form-group">
<label for="observationValue">{% trans "&#37; Infected leaves" %}</label>
<input type="number" id="observationValue" name="observationValue" class="form-control" min="0" max="100"/>
<span class="help-block" id="{{ form_id }}_observationValue_validation"></span>
</div>
<div class="form-group space">
<label for="sprayingDate" class="main-label">Sprøytedato</label>
<input type="date" id="sprayingDate" name="sprayingDate" class="form-control"/>
<span class="help-block" id="{{ form_id }}_sprayingDate_validation"></span>
</div>
<div class="form-group">
<label for="preparationId">med preparat</label>
<select name="preparationId" id="preparationId" class="form-control">
</select>
<span class="help-block" id="{{ form_id }}_preparationId_validation"></span>
</div>
<div class="form-group">
<label for="preparationDose">og preparatdose (ml/daa)</label>
<input type="number" id="preparationDose" name="preparationDose" class="form-control"/>
<span class="help-block" id="{{ form_id }}_preparationDose_validation"></span>
</div>
<fieldset>
<legend>Observasjon</legend>
<div class="form-group">
<p><i>Legg inn dato og omfang dersom du har observert symptomer</i></p>
<input type="date" id="observationDate" name="observationDate" class="form-control"/>
<span class="help-block" id="{{ form_id }}_observationDate_validation"></span>
</div>
<div class="form-group">
<label for="observationValue">% blad med symptomer</label>
<input type="number" id="observationValue" name="observationValue" class="form-control" min="0" max="100"/>
<span class="help-block" id="{{ form_id }}_observationValue_validation"></span>
</div>
</fieldset>
<fieldset>
<legend>Sprøyting</legend>
<div class="form-group">
<p><i>Legg inn dato og detaljer for eventuell siste sprøyting</i></p>
<input type="date" id="sprayingDate" name="sprayingDate" class="form-control"/>
<span class="help-block" id="{{ form_id }}_sprayingDate_validation"></span>
</div>
<div class="form-group">
<label for="preparationId">med preparat</label>
<select name="preparationId" id="preparationId" class="form-control">
</select>
<span class="help-block" id="{{ form_id }}_preparationId_validation"></span>
</div>
<div class="form-group">
<label for="preparationDose">og preparatdose (ml/daa)</label>
<input type="number" id="preparationDose" name="preparationDose" class="form-control"/>
<span class="help-block" id="{{ form_id }}_preparationDose_validation"></span>
</div>
</fieldset>
</div>
</div>
<div class="row">
<div class="col-md-12 form-group">
<button type="button" class="btn btn-primary pull-right" onclick="if(validateForm(document.getElementById('{{ form_id }}')) & validateFormExtra()){runModel();}">{% trans "Run model" %}</button>
<button type="button" class="btn btn-primary pull-right" onclick="if(validateForm(document.getElementById('{{ form_id }}')) & validateFormExtra()){storeUserSettings();runModel();}">{% trans "Run model" %}</button>
</div>
</div>
......@@ -195,14 +212,34 @@
import MapModal from 'https://logic.testvips.nibio.no/js/mapModal.js';
//import MapModal from settings.vipslogicProtocol + "://" + settings.vipslogicServerName + "/js/mapModal.js"
const theForm = document.getElementById("{{ form_id }}");
const inputLatitudeElement = document.getElementById("latitude");
const inputLongitudeElement = document.getElementById("longitude");
const radioGrid = document.getElementById("grid")
const radioWeatherstation = document.getElementById("weatherstation")
const selectWeatherstationElement = document.getElementById("weatherStationId");
let poiIdList = []
let selectedPoint = null;
let selectedFeature = undefined;
const formFields = [
"weatherDataSourceType",
"latitude",
"longitude",
"timezone",
"weatherStationId",
"cropOrganismId",
"sowingDate",
"sameCropAsLastSeason",
"plowed",
"observationDate",
"observationValue",
"sprayingDate",
"preparationId",
"preparationDose"
];
function getSelectedPoiId() {
const value = selectWeatherstationElement.value;
const parsedValue = parseInt(value, 10);
......@@ -216,6 +253,7 @@
if(selectedLatitude && selectedLongitude) {
inputLatitudeElement.value = selectedLatitude;
inputLongitudeElement.value = selectedLongitude;
getTimezoneForPoint(selectedLatitude, selectedLongitude);
}
}
......@@ -231,7 +269,7 @@
window.openCoordinatesMap = () => {
if (inputLatitudeElement.value && inputLongitudeElement.value) {
selectedPoint = 1;
selectedPoint = -1;
selectedFeature = {
"type": "FeatureCollection", "features": [
{
......@@ -250,11 +288,9 @@
selectedFeature = undefined;
}
// TODO Open map with currently selected language! (not 'nb')
const isPoiMap = false; // Map should enable selection of coordinates (not pois)
const allowNewPoints = true; // User should be able to select new pois
const coordinatesMapInstance = new MapModal('coordinates-map', selectedFeature, 'nb', isPoiMap, allowNewPoints, selectCoordinates);
const allowNewPoints = true; // User should be able to select new coordinates
const coordinatesMapInstance = new MapModal('coordinates-map', selectedFeature, settings.currentLanguage, isPoiMap, allowNewPoints, selectCoordinates);
coordinatesMapInstance.openModal(selectedPoint);
}
......@@ -271,7 +307,7 @@
.then(geoJson => {
const isPoiMap = true; // Map should enable selection of pois
const allowNewPoints = false; // User should not be able to create new pois
const poiMapInstance = new MapModal('poi-map', geoJson, 'nb', isPoiMap, allowNewPoints, selectPoi);
const poiMapInstance = new MapModal('poi-map', geoJson, settings.currentLanguage, isPoiMap, allowNewPoints, selectPoi);
const selectedPoiId = getSelectedPoiId();
poiMapInstance.openModal(selectedPoiId);
})
......@@ -281,36 +317,56 @@
}
window.displayWeatherstationInput = () => {
document.getElementById("weatherstation").checked = true;
radioWeatherstation.checked = true;
document.getElementById('input-weatherstation').style.display="block";
document.getElementById('input-coordinates').style.display="none";
inputLatitudeElement.value = ""
inputLongitudeElement.value = ""
}
window.displayCoordinatesInput = (id) => {
document.getElementById("coordinates").checked = true;
window.displayCoordinatesInput = () => {
radioGrid.checked = true;
document.getElementById('input-weatherstation').style.display="none";
document.getElementById('input-coordinates').style.display="block";
selectWeatherstationElement.selectedIndex = 0;
getTimezoneForSelectedPoint()
}
const getTimezoneForPoint = (latitude, longitude) => {
getLocationInformation(latitude, longitude).then(locationInfo => {
theForm["timezone"].value = locationInfo.timezone;
document.getElementById("gridPointInfo").innerHTML = `<b>Sted</b> ${locationInfo.location}<br>
<b>Breddegrad</b> ${locationInfo.latitude}<br>
<b>Lengdegrad</b> ${locationInfo.longitude}<br>
<b>Tidssone</b> ${locationInfo.timezone}`
});
}
const getTimezoneForSelectedPoint = () => {
const lat = theForm["latitude"].value;
const lon = theForm["longitude"].value;
if(lat && lon) {
getTimezoneForPoint(lat, lon)
} else {
console.info("Latitude and longitude not set in form, cannot get timezone information")
}
}
window.validateFormExtra = () => {
var theForm = document.getElementById("{{ form_id }}");
// Location: Either weatherStationId or latitude/longitude must be set
const selectedWeatherdataType = theForm.querySelector('input[name="weatherdataType"]:checked').value;
if( selectedWeatherdataType === "coordinates") {
const selectedWeatherdataType = theForm.querySelector('input[name="weatherDataSourceType"]:checked').value;
if( selectedWeatherdataType === "grid") {
const trimmedLat = theForm["latitude"].value.trim()
const trimmedLon = theForm["longitude"].value.trim()
if(trimmedLat === "" || trimmedLon === "" || isNaN(Number(trimmedLat)) || isNaN(Number(trimmedLon))) {
alert("Mangler gyldig punkt");
return false;
}
}
if (selectedWeatherdataType === "weatherstation") {
} else if (selectedWeatherdataType === "weatherstation") {
if(theForm["weatherStationId"].options[theForm["weatherStationId"].selectedIndex].value == "-1") {
alert("Mangler værstasjon")
return false;
}
} else {
alert("Mangler type værdatakilde")
return false;
}
// Observation: Either no fields or all fields must be set
......@@ -331,7 +387,35 @@
return true;
}
var VIPSOrganizationId = {{vips_organization_id}};
window.storeUserSettings = () => {
var settingsDict = {}
for(var i in formFields) {
const inputElement = theForm[formFields[i]];
const key = "{{ form_id }}" + "." + formFields[i];
// Need to add the formId as namespace to avoid confusion in Local Storage
if(inputElement.type === "checkbox") {
settingsDict[key] = inputElement.checked
} else {
settingsDict[key] = inputElement.value;
}
}
storeLocalSettings(settingsDict);
};
window.renderUserSettings = (userSettings) => {
for(const key in userSettings){
const fieldName = key.substring("{{form_id}}.".length);
const inputElement = theForm[fieldName];
const theValue = userSettings[key];
if (inputElement.type && inputElement.type === "checkbox") {
inputElement.checked = theValue === "true" || theValue === true;
} else {
inputElement.value = theValue;
}
}
};
const VIPSOrganizationId = {{vips_organization_id}};
window.runModel = () => {
document.getElementById("results").style.display="none";
document.getElementById("errorMessageContainer").style.display="none";
......@@ -434,9 +518,8 @@
};
function initWeatherStations(){
// Fetching information asynchronously from server
var request = $.ajax({
async function initWeatherStations(){
return $.ajax({
type:"GET",
url: settings.vipslogicProtocol + "://" + settings.vipslogicServerName + "/rest/poi/organization/" + VIPSOrganizationId,
statusCode:{
......@@ -462,14 +545,13 @@
function initCrops()
{
// Fetching information asynchronously from server
var request = $.ajax({
return $.ajax({
type:"GET",
url: settings.vipslogicProtocol + "://" + settings.vipslogicServerName + "/rest/barleynetblotchmodel/barleyvarieties/" + VIPSOrganizationId,
statusCode:{
200: function(data,textStatus, jqXHR){
// Building result HTML
var cropHTML=["<option value=\"-1\">-- {% trans "Select crop" %} --</option>"];
var cropHTML=["<option value=\"-1\">-- Velg sort --</option>"];
data.sort(compareOrganisms);
for(var i in data)
{
......@@ -490,8 +572,7 @@
function initPreparations()
{
// Fetching information asynchronously from server
var request = $.ajax({
return $.ajax({
type:"GET",
url: settings.vipslogicProtocol + "://" + settings.vipslogicServerName + "/rest/barleynetblotchmodel/preparations/" + VIPSOrganizationId,
statusCode:{
......@@ -522,18 +603,33 @@
document.getElementById("errorMessage").innerHTML = "<h1>Error</h1><pre>" + message + "</pre>";
document.getElementById("errorMessageContainer").style.display="block";
};
$(document).ready(function() {
if(settings.userIsIE)
{
alert("{% trans "WARNING: We suspect you are using Internet Explorer to view this site. VIPS is not designed to work with Internet Explorer, you may experience errors and missing features. Please use a different browser, like Microsoft Edge or Google Chrome." %}");
}
initWeatherStations();
initCrops();
initPreparations();
// Init form validation
loadFormDefinition("{{ form_id }}","/static/cerealblotchmodels/formdefinitions/");
displayCoordinatesInput();
// Make sure all promises have returned before continuing
Promise.all([initWeatherStations(), initCrops(), initPreparations()]).then(function() {
loadFormDefinition("{{ form_id }}","/static/cerealblotchmodels/formdefinitions/");
var userSettings = getLocalSettings(getNameSpaced("{{ form_id }}",formFields), false);
if(!isDictEmpty(userSettings)) {
renderUserSettings(userSettings);
if(theForm["weatherDataSourceType"].value == "grid") {
displayCoordinatesInput();
} else if(theForm["weatherDataSourceType"].value == "weatherstation") {
displayWeatherstationInput();
} else {
displayCoordinatesInput();
}
} else {
displayCoordinatesInput();
}
}).catch(function(error) {
console.error("Error initializing data:", error);
});
});
</script>
......
......@@ -23,32 +23,82 @@
{% endcomment %}
{% load i18n %}
{% block title %}{% trans "Septoria humidity model" %}{% endblock %}
{% block customCSS %}
<link type="text/css" rel="stylesheet" href="https://logic.testvips.nibio.no/css/3rdparty/leaflet.css" />
<link type="text/css" rel="stylesheet" href="https://logic.testvips.nibio.no/css/mapModal.css" />
<style>
/* Added when integrating map, should perhaps be moved to main css file. */
input#latitude, input#longitude {
margin: 10px 10px 10px 0;
}
select#organizationId_countryCode, select#weatherStationId {
margin: 5px;
}
.main-label {
font-size: 1.8rem;
font-weight: 500 !important;
}
.space {
margin-top: 40px;
}
.radio {
margin-top: 0;
margin-bottom: 20px;
}
#gridPointInfo {
margin: 10px 0;
}
</style>
{% endblock %}
{% block content %}
<div class="singleBlockContainer">
<h1>{% trans "Septoria humidity model" %}</h1>
<p>{% trans "Fuktmodellen er et beslutningsstøtteverktøy, utviklet av <a href='https://www.seges.dk/'>SEGES</a>, Danmark, for å kunne vurdere risiko for angrep av hvetebladprikk i høsthvete under danske forhold. <a href='/forecasts/models/SEPTORIAHU/' target='new'>Les mer</a>, og se <a href='https://vimeo.com/818734601' target='new'>informasjonsvideo</a>" %}</p>
<p class="lead">{% trans "Fuktmodellen er et beslutningsstøtteverktøy, utviklet av <a href='https://www.seges.dk/'>SEGES</a>, Danmark, for å kunne vurdere risiko for angrep av hvetebladprikk i høsthvete under danske forhold. <a href='/forecasts/models/SEPTORIAHU/' target='new'>Les mer</a>, og se <a href='https://vimeo.com/818734601' target='new'>informasjonsvideo</a>" %}</p>
<form role="form" id="{{ form_id }}">
<div class="row">
<div class="col-md-12">
<h2>{% trans "Background data" %}</h2>
</div>
</div>
<div class="row">
<div class="col-md-3">
<div class="form-group">
<label for="organizationId_countryCode">{% trans "Country" %}</label>
<select name="organizationId_countryCode" id="organizationId_countryCode" class="form-control" onchange="updateWeatherDataSources(this.options[this.options.selectedIndex].value);">
<option value="None">{% trans "Please select" %}</option>
</select>
<span class="help-block" id="{{ form_id }}_organizationId_countryCode_validation"></span>
</div>
<div class="form-group">
<label for="weatherStationId">{% trans "Weather station" %}</label>
<select name="weatherStationId" id="weatherStationId" class="form-control">
<option value="">{% trans "Please select" %}</option>
</select>
<span class="help-block" id="{{ form_id }}_weatherStationId_validation"></span>
</div>
<fieldset>
<legend>Værdata</legend>
<div class="form-group">
<div class="radio">
<label>
<input type="radio" name="weatherDataSourceType" id="grid" value="grid" onchange="displayCoordinatesInput()">
for et punkt i kartet
</label>
<div id="input-coordinates">
<input type="hidden" class="form-control" name="latitude" id="latitude" placeholder="Breddegrad" aria-label="Breddegrad">
<input type="hidden" class="form-control" name="longitude" id="longitude" placeholder="Lengdegrad" aria-label="Lengdegrad">
<input type="hidden" class="form-control" name="timezone" id="timezone" placeholder="Tidssone" aria-label="Tidssone">
<div id="gridPointInfo"></div>
<button type="button" class="btn btn-primary" onclick="openCoordinatesMap()"><i class="fa fa-map-marker fa-lg"></i>&nbsp;&nbsp;Velg i kart</button>
</div>
<div id="coordinates-map" class="map-modal"></div>
</div>
<div class="radio">
<label>
<input type="radio" name="weatherDataSourceType" id="weatherstation" value="weatherstation" onchange="displayWeatherstationInput()">
fra en værstasjon
</label>
<div id="input-weatherstation" style="display: none;">
<select name="organizationId_countryCode" id="organizationId_countryCode" class="form-control" onchange="updateWeatherDataSources(this.options[this.options.selectedIndex].value);">
<option value="None">Velg land</option>
</select>
<select name="weatherStationId" id="weatherStationId" class="form-control" disabled>
<option value="">Velg værstasjon</option>
</select>
<button disabled type="button" id="poi-map-button" class="btn btn-primary" onclick="openPoiMap()"><i class="fa fa-map-marker fa-lg"></i>&nbsp;&nbsp;Velg i kart</button>
</div>
<div id="poi-map" class="map-modal"></div>
</div>
<span class="help-block" id="{{ form_id }}_organizationId_countryCode_validation"></span>
<span class="help-block" id="{{ form_id }}_weatherStationId_validation"></span>
<span class="help-block" id="{{ form_id }}_latitude_validation"></span>
<span class="help-block" id="{{ form_id }}_longitude_validation"></span>
</div>
</fieldset>
<fieldset>
<legend>{% trans "Sprayings" %}</legend>
<div class="form-group">
......@@ -62,8 +112,6 @@
<span class="help-block" id="{{ form_id }}_dateSpraying2_validation"></span>
</div>
</fieldset>
</div>
<div class="col-md-3">
<fieldset>
......@@ -94,7 +142,9 @@
<span class="help-block" id="{{ form_id }}_dateGs75_validation"></span>
</div>
</fieldset>
{% trans "Show advanced settings" %} <input type="checkbox" onclick="toggleAdvancedColumns(this);" autocomplete="off"/>
<div class="pull-right">
{% trans "Show advanced settings" %} <input type="checkbox" onclick="toggleAdvancedColumns(this);" autocomplete="off"/>
</div>
</div>
<div class="col-md-3">
......@@ -149,8 +199,8 @@
</div>
</div>
<div class="row">
<div class="col-md-12 form-group">
<button type="button" class="btn btn-primary" onclick="if(validateForm(document.getElementById('{{ form_id }}'))){storeUserSettings();runModel();}">{% trans "Run model" %}</button>
<div class="col-md-6 form-group">
<button type="button" class="btn btn-primary pull-right" onclick="if(validateForm(document.getElementById('{{ form_id }}')) & validateFormExtra()){storeUserSettings();runModel();}">{% trans "Run model" %}</button>
</div>
</div>
</form>
......@@ -179,11 +229,139 @@
<script type="text/javascript" src="{% static "js/util.js" %}"></script>
<script type="text/javascript" src="{% static "js/validateForm.js" %}"></script>
<script type="text/javascript" src="{% static "forecasts/js/forecasts.js" %}"></script>
<script type="text/javascript">
<script type="module">
import MapModal from 'https://logic.testvips.nibio.no/js/mapModal.js';
//import MapModal from settings.vipslogicProtocol + "://" + settings.vipslogicServerName + "/js/mapModal.js"
const theForm = document.getElementById("{{ form_id }}");
const inputLatitudeElement = document.getElementById("latitude");
const inputLongitudeElement = document.getElementById("longitude");
const selectWeatherstationElement = document.getElementById("weatherStationId");
const openPoiMapButton = document.getElementById("poi-map-button");
let poiIdList = []
let selectedPoint = null;
let selectedFeature = undefined;
function getSelectedPoiId() {
const value = selectWeatherstationElement.value;
const parsedValue = parseInt(value, 10);
return (!isNaN(parsedValue) && parsedValue > 0) ? parsedValue : undefined;
}
function selectCoordinates(coordinatesData) {
const selectedLatitude = coordinatesData ? coordinatesData.latitude : undefined;
const selectedLongitude = coordinatesData ? coordinatesData.longitude : undefined;
if(selectedLatitude && selectedLongitude) {
inputLatitudeElement.value = selectedLatitude;
inputLongitudeElement.value = selectedLongitude;
getTimezoneForPoint(selectedLatitude, selectedLongitude);
}
}
function selectPoi(poiData) {
const selectedId = poiData ? poiData.pointOfInterestId : undefined;
if (selectedId) {
const optionIndex = Array.from(selectWeatherstationElement.options).findIndex(option => option.value == selectedId);
if (optionIndex !== -1) {
selectWeatherstationElement.selectedIndex = optionIndex;
}
}
}
window.openCoordinatesMap = () => {
if (inputLatitudeElement.value && inputLongitudeElement.value) {
console.info(`Open map for lat=${parseFloat(inputLatitudeElement.value)} lon=${parseFloat(inputLatitudeElement.value)}`)
selectedPoint = -1;
selectedFeature = {
"type": "FeatureCollection", "features": [
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [parseFloat(inputLongitudeElement.value), parseFloat(inputLatitudeElement.value)]
},
"properties": {
"pointOfInterestId": selectedPoint,
}
}]
};
} else {
selectedPoint = undefined;
selectedFeature = undefined;
}
const isPoiMap = false; // Map should enable selection of coordinates (not pois)
const allowNewPoints = true; // User should be able to select new coordinates
const coordinatesMapInstance = new MapModal('coordinates-map', selectedFeature, settings.currentLanguage, isPoiMap, allowNewPoints, selectCoordinates);
coordinatesMapInstance.openModal(selectedPoint);
}
window.openPoiMap = () => {
fetch(settings.vipslogicProtocol + "://" + settings.vipslogicServerName + "/rest/poi/geojson", {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify(poiIdList)
})
.then(response => response.json())
.then(geoJson => {
const isPoiMap = true; // Map should enable selection of pois
const allowNewPoints = false; // User should not be able to create new pois
const poiMapInstance = new MapModal('poi-map', geoJson, settings.currentLanguage, isPoiMap, allowNewPoints, selectPoi);
const selectedPoiId = getSelectedPoiId();
poiMapInstance.openModal(selectedPoiId);
})
.catch(error => {
console.error('Unable to retrieve weatherstation geojson', error);
});
}
window.displayWeatherstationInput = () => {
document.getElementById("weatherstation").checked = true;
document.getElementById('input-weatherstation').style.display="block";
document.getElementById('input-coordinates').style.display="none";
}
window.displayCoordinatesInput = (id) => {
document.getElementById("grid").checked = true;
document.getElementById('input-weatherstation').style.display="none";
document.getElementById('input-coordinates').style.display="block";
getTimezoneForSelectedPoint()
}
const getTimezoneForPoint = (latitude, longitude) => {
getLocationInformation(latitude, longitude).then(locationInfo => {
theForm["timezone"].value = locationInfo.timezone;
document.getElementById("gridPointInfo").innerHTML = `<b>Sted</b> ${locationInfo.location}<br>
<b>Breddegrad</b> ${locationInfo.latitude}<br>
<b>Lengdegrad</b> ${locationInfo.longitude}<br>
<b>Tidssone</b> ${locationInfo.timezone}`
});
}
const getTimezoneForSelectedPoint = () => {
const lat = theForm["latitude"].value;
const lon = theForm["longitude"].value;
if(lat && lon) {
getTimezoneForPoint(lat, lon)
} else {
console.info("Latitude and longitude not set in form, cannot get timezone information")
}
}
var danishPostCodesUTM;
var organizations;
var allowedCountryCodes = ["NO","DK","SE","FI","LT"];
var formFields = [
"weatherDataSourceType",
"latitude",
"longitude",
"timezone",
"organizationId_countryCode",
"weatherStationId",
"dateSpraying1",
......@@ -217,18 +395,46 @@
var userSettings = getLocalSettings(getNameSpaced("{{ form_id }}",formFields), false);
if(!isDictEmpty(userSettings)) {
renderUserSettings(userSettings);
if(theForm["weatherDataSourceType"].value == "grid") {
displayCoordinatesInput();
} else if(theForm["weatherDataSourceType"].value == "weatherstation") {
displayWeatherstationInput();
} else {
displayCoordinatesInput();
}
}
else
{
displayCoordinatesInput();
updateGSDates();
}
});
});
});
window.validateFormExtra = () => {
// Location: Either weatherStationId or latitude/longitude must be set
const selectedWeatherdataType = theForm.querySelector('input[name="weatherDataSourceType"]:checked').value;
if( selectedWeatherdataType === "grid") {
const trimmedLat = theForm["latitude"].value.trim()
const trimmedLon = theForm["longitude"].value.trim()
if(trimmedLat === "" || trimmedLon === "" || isNaN(Number(trimmedLat)) || isNaN(Number(trimmedLon))) {
alert("Mangler gyldig punkt");
return false;
}
}
else if (selectedWeatherdataType === "weatherstation") {
if(theForm["weatherStationId"].options[theForm["weatherStationId"].selectedIndex].value == "-1") {
alert("Mangler værstasjon");
return false;
}
} else {
alert("Mangler type værdatakilde")
return false;
}
return true;
}
var initDanishPostCodesUTM = function(callback){
$.ajax({
type:"GET",
......@@ -279,13 +485,13 @@
}
});
};
var updateWeatherDataSources = function(organizationId_countryCode){
var selectList = document.getElementById("weatherStationId");
selectList.options.length = 1; // Erase all former options
window.updateWeatherDataSources = (organizationId_countryCode) => {
selectWeatherstationElement.options.length = 1; // Erase all former options
if(organizationId_countryCode === "None")
{
if(organizationId_countryCode === "None"){
selectWeatherstationElement.disabled = true;
openPoiMapButton.disabled = true;
return;
}
......@@ -301,12 +507,13 @@
var city = postCodeEl.getElementsByTagName("CityName")[0].firstChild.nodeValue;
var opt = new Option(city,UTM32v);
//console.info(opt);
selectList.options[selectList.options.length] = opt;
selectWeatherstationElement.options[selectWeatherstationElement.options.length] = opt;
}
renderUserSetting(selectList);
renderUserSetting(selectWeatherstationElement);
}
else
{
poiIdList = []
$.ajax({
type:"GET",
url: settings.vipslogicProtocol + "://" + settings.vipslogicServerName + "/rest/poi/organization/" + organizationId,
......@@ -318,6 +525,7 @@
for(var i in data)
{
var ws = data[i];
poiIdList.push(ws["pointOfInterestId"]);
wsHTML.push("<option value=\"" + ws["pointOfInterestId"] + "\">" + ws["name"] + "</option>");
}
var wsSelect = document.getElementById("weatherStationId");
......@@ -331,9 +539,11 @@
}
});
}
selectWeatherstationElement.disabled = false;
openPoiMapButton.disabled = false;
};
var updateGSDates = function(){
window.updateGSDates = () => {
var dateGs31 = document.getElementById("dateGs31");
var date3rdUpperLeafEmergingdateGs31 = document.getElementById("date3rdUpperLeafEmerging");
var date2ndUpperLeafEmerging = document.getElementById("date2ndUpperLeafEmerging");
......@@ -353,7 +563,7 @@
dateGs75.value = currentDate.format("YYYY-MM-DD");
}
var runModel = function(){
window.runModel = () => {
document.getElementById("chartContainer").style.display="none"; // Hide chart
document.getElementById("warningStatusInterpretation").style.display="none";
// Insert please wait message
......@@ -385,8 +595,7 @@
"SEPTORIAHU.HPHPP" : "{% trans "Humid period hour outside protection period" %}"
};
var renderResults = function(data,textStatus, jqXHR)
{
window.renderResults = (data,textStatus, jqXHR) => {
data.sort(compareForecastResults).reverse();
// First attempt: A table!
var headingLine = "<tr><td style=\"font-weight: bold;\">{% trans "Time" %}</td>";
......@@ -449,11 +658,12 @@
renderForecastChart("chartContainer", "{% trans "Septoria humidity model" %}", warningStatusPlotBandData, data);
}
var handleAjaxError = function(jqXHR,textStatus,errorThrown){
window.handleAjaxError = (jqXHR,textStatus,errorThrown) => {
document.getElementById("resultsTable").innerHTML=""
alert(textStatus);
};
var renderUserSettings = function(userSettings){
window.renderUserSettings = (userSettings) => {
// Strip namespace from form field
var theForm = document.getElementById('{{ form_id }}');
for(var i in userSettings){
......@@ -464,7 +674,7 @@
}
};
var renderUserSetting = function(formField)
window.renderUserSetting = (formField) =>
{
var localStorageKey = "{{form_id}}." + formField.name;
......@@ -475,16 +685,7 @@
}
}
var getNameSpaced = function(nameSpace, anArray){
var retVal = [];
for(var i = 0; i<anArray.length;i++)
{
retVal.push(nameSpace + "." + anArray[i]);
}
return retVal;
};
var storeUserSettings = function(){
window.storeUserSettings = () => {
var theForm = document.getElementById('{{ form_id }}');
var settingsDict = {}
for(var i in formFields)
......@@ -495,7 +696,7 @@
storeLocalSettings(settingsDict);
};
var toggleAdvancedColumns = function(theCheckBox){
window.toggleAdvancedColumns = (theCheckBox) => {
var col1 = document.getElementById("septoriaHumidityAdvancedColumn1");
var col2 = document.getElementById("septoriaHumidityAdvancedColumn2");
col1.style.visibility = theCheckBox.checked ? "visible" : "hidden";
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment