diff --git a/applefruitmoth/static/applefruitmoth/js/applefruitmoth.js b/applefruitmoth/static/applefruitmoth/js/applefruitmoth.js index 0d15fe62bb462b13467558f0b0118c1099f9b32b..863948924f7fa1d3cf010349f537c84bbc3beff2 100644 --- a/applefruitmoth/static/applefruitmoth/js/applefruitmoth.js +++ b/applefruitmoth/static/applefruitmoth/js/applefruitmoth.js @@ -1,115 +1,211 @@ -document.addEventListener('DOMContentLoaded', setBasis); -document.title="Rognebærmøllvarsling"; - - - -var stations = new Array(); -var serverUri = "https://" + settings.vipsCoremanagerServerName; -var runUri= serverUri + "/models/DAYDEGREES/run"; +class ApplefruitMoth +{ + serverUri = "https://" + settings.vipsCoremanagerServerName; + runUri= this.serverUri + "/models/DAYDEGREES/run"; + weatherStationDataURL = "https://lmt.nibio.no/services/rest/vips/getdata/forecastfallback"; + gridDataURL = "https://weather.vips.nibio.no/rest/grid/openmeteo/"; + TIMEZONE_OSLO = "Europe/Oslo"; + DATE_FORMAT = "YYYY-MM-DD"; + WEATHER_DATASOURCE_TYPE_COORDINATES = "coordinates"; + WEATHER_DATASOURCE_TYPE_WEATHERSTATION = "weatherstation"; + + basisTemp = "0"; + + constructor() { + this.weatherStationIds = []; + this.weatherStations = []; + } -var emptyChart; + renderWeatherstations = async function () { + console.info("Load weatherstations - start") + try { + const weatherStations = await window.lmtPoiMap(); + this.weatherStations = weatherStations; + this.weatherStationIds = weatherStations.map(ws => ws.id); + const select = document.getElementById('weatherStationId'); + // Add an option for each weather station + this.weatherStations.forEach(station => { + const option = document.createElement('option'); + option.value = station.id; + option.textContent = station.name; + select.appendChild(option); + }); + console.info("Load weatherstations - finished") + } catch (error) { + console.error('Error:', error); + } + } + getSelectedWeatherstation() + { + let wsSelect = document.getElementById("weatherStationId"); + return wsSelect.options[wsSelect.selectedIndex].value != "-1" ? wsSelect.options[wsSelect.selectedIndex].value : undefined; + } + + getSelectedCoordinate() + { + let longitude = parseFloat(document.getElementById("longitude").value); + let latitude = parseFloat(document.getElementById("latitude").value); + return (Number.isNaN(longitude) || Number.isNaN(latitude) ? undefined : [longitude, latitude]); + } -function setBasis () { - - let path = "https://lmt.nibio.no/services/rest/weatherstation/mapweatherstations"; - - $.getJSON(path, function( mapStations ) { - - allStations = mapStations; + getSelectedDates() + { + let startDate = document.getElementById("startdate").value; + let endDate = document.getElementById("enddate").value; - allStations.sort(function(a,b) { - return a.name.toString().localeCompare(b.name.toString()); - }); - - for(var i=0;i<allStations.length;i++){ - stations[i]=([mapStations[i].weatherstationId, mapStations[i].name]); + if(!startDate) + { + let date = new Date(); + date.setDate(date.getDate() - 7); + date.setDate(date.getDate()); + startDate = date.toISOString().split('T')[0]; + document.getElementById("startdate").value = startDate; } - for (let i = 0; i < stations.length; i++ ) { - let option = document.createElement("option"); - option.text = stations[i][1]; - option.value = stations[i][0]; - document.getElementById("weatherstation").appendChild(option); + if(!endDate){ + let date = new Date(); + date.setDate(date.getDate() + 9); + endDate = date.toISOString().split('T')[0]; + + document.getElementById("enddate").value = endDate; } - emptyChart = true; + return [startDate, endDate]; - setStartText(); - displayResults(emptyChart); - }); - -} + } + + + getSelectedWeatherdataSourceType() + { + return document.getElementById("coordinates").checked ? this.WEATHER_DATASOURCE_TYPE_COORDINATES : document.getElementById("weatherstation").checked ? this.WEATHER_DATASOURCE_TYPE_WEATHERSTATION : undefined; + } + + /** + * Calls the dayDegreemodel in the VIPS forecasting system + * Displays data when results are returned + */ + runModel () { + + let selectedDates = this.getSelectedDates(); + let selectedWeatherStationId = this.getSelectedWeatherstation(); + let selectedCoordinate = this.getSelectedCoordinate(); + + if(selectedWeatherStationId == undefined && selectedCoordinate == undefined) + { + return; + } + + let stationName; + + let startDate = selectedDates[0]; + let endDate = selectedDates[1]; -function getBasis () { + + try + { + let dailyDataResponse = undefined; + + if(selectedWeatherStationId != undefined && this.getSelectedWeatherdataSourceType() == this.WEATHER_DATASOURCE_TYPE_WEATHERSTATION) + { + stationName = this.weatherStations.find(ws => ws.id === Number(selectedWeatherStationId)).name; + // Get the hourly data for the past X days + dailyDataResponse = fetch( + this.weatherStationDataURL + "?weatherStationId=" + selectedWeatherStationId + + "&elementMeasurementTypes[]=TM" + + "&timeZone=" + this.TIMEZONE_OSLO + + "&startDate=" + startDate + + "&endDate=" + endDate + + "&logIntervalId=2" + ); + + } + else if(this.getSelectedWeatherdataSourceType() == this.WEATHER_DATASOURCE_TYPE_COORDINATES && selectedCoordinate != undefined) + { + stationName = 'valgte koordinater'; + dailyDataResponse = fetch( + this.gridDataURL + "?longitude=" + selectedCoordinate[0] + "&latitude=" + selectedCoordinate[1] + + "&elementMeasurementTypes[]=TM" + + "&timeZone=" + document.getElementById("timezone").value + + "&startDate=" + startDate + + "&endDate=" + endDate + + "&logIntervalId=2" + ); + } + else + { + console.error("Location not set. Aborting.") + return; + } + dailyDataResponse + .then(response => { + if (!response.ok) { + throw new Error("Response status: " + response.status); + } + return response.json(); + }) + .then(dailyData => { + this.getDaydegrees(dailyData, startDate, endDate, stationName); + }) + .catch(error => console.error('Error:', error)); + + } + catch(error) + { + console.error(error.message); + } + } - var basisTemp = "0"; - var startTime = document.getElementById('startdate').value; - var endTime = document.getElementById('enddate').value; - var weatherStation = document.getElementById('weatherstation').value; + getDaydegrees (data, start, end, name) { + + let modelConfig = { + "loginInfo":{ + "username":"testuser", + "password":"testpass" + }, + "modelId":"DAYDEGREES", + "configParameters":{ + "basisTemp": this.basisTemp, + "observations": data + } + }; - var temperature = []; + let request = $.ajax({ + type:"POST", + url: this.runUri, + data: JSON.stringify(modelConfig), + dataType: "json", + contentType: "application/json; charset=utf-8", + }) + .done((data, textStatus, jqXHR) => { + this.displayResults(data, start, end, name); + }) + .fail((jqXHR, textStatus,errorThrown ) => { + alert( "Request failed: " + errorThrown ); + }) + ; + } - weatherstationId = weatherStation; - - if (startTime > endTime) { - alert("Datoen er ikke gyldig"); - return false; - } - - emptyChart = false; - - $.getJSON("https://lmt.nibio.no/services/rest/vips/getdata/forecastfallback?weatherStationId="+weatherStation+"&elementMeasurementTypes[]=TM&logInterval=1d&startDate="+startTime+"&startTime=00&endDate="+endTime+"&endTime=23&timeZone=Europe/Oslo", function( observations ) { - $.getJSON("https://lmt.nibio.no/services/rest/weatherstation/getstation?weatherStationId="+weatherStation, function( weatherstation ) { - allObservations = weatherstation; - var name = String(weatherstation.name); - console.info(observations); - runModel(basisTemp, observations, name, startTime, endTime); - }); - }); - -} - -/** - * Calls the dayDegreemodel in the VIPS forecasting system - * Displays data when results are returned - */ -function runModel (basisTemp, data, name, start, end) { - var modelConfig = { - "loginInfo":{ - "username":"testuser", - "password":"testpass" - }, - "modelId":"DAYDEGREES", - "configParameters":{ - "basisTemp": basisTemp, - "observations": data - } - }; + setStartText () { + + document.getElementById("textInfo").innerText = ""; + + let infotext = document.createElement("p"); + infotext = "<p>Varmesum, med basistemperatur 0 °C, vert rekna ut frå middeltemperaturen 2 m over bakken for den valde vêrstasjonen. </p>"; + + document.getElementById("textInfo").innerHTML=infotext; + } + - var request = $.ajax({ - type:"POST", - url: runUri, - data: JSON.stringify(modelConfig), - dataType: "json", - contentType: "application/json; charset=utf-8", - }) - .done(function(data, textStatus, jqXHR) { - displayResults(data, basisTemp, name, start, end); - }) - .fail(function( jqXHR, textStatus,errorThrown ) { - alert( "Request failed: " + errorThrown ); - }) - ; -} -function displayResults(data, basisTemp, name, start, end) { + + displayResults (data, start, end, name) { let date = new Array(); let series = []; @@ -117,11 +213,12 @@ function displayResults(data, basisTemp, name, start, end) { let temperature = new Array(); let forecast = new Array(); - + let emptyChart = false; + if(!emptyChart) { - for (var i = 0; i < data.length; i++){ + for (let i = 0; i < data.length; i++){ - var localTime = new Date(data[i].validTimeStart); + let localTime = new Date(data[i].validTimeStart); const offset = localTime.getTimezoneOffset() localTime= new Date(localTime.getTime() - (offset*60*1000)) @@ -135,6 +232,7 @@ function displayResults(data, basisTemp, name, start, end) { localTime = localTime.toISOString().split('T')[0]; time.push(localTime); } + // Fill the gap between temperature and forecast let index = temperature.length-1; forecast[index]=temperature[index]; @@ -152,8 +250,8 @@ function displayResults(data, basisTemp, name, start, end) { document.getElementById("textInfo").innerText = ""; let infotext = document.createElement("p"); - var text1 = "<p>Varmesum, med basistemperatur 0 °C, rekna ut frå middeltemperaturen 2 m over bakken for den valde vêrstasjonen. </p>"; - var text2 = "<p><b>" + lastValue + "</b> døgngrader\n\n</p>"; + let text1 = "<p>Varmesum, med basistemperatur 0 °C, rekna ut frå middeltemperaturen 2 m over bakken for den valde vêrstasjonen. </p>"; + let text2 = "<p><b>" + lastValue + "</b> døgngrader\n\n</p>"; infotext = text1 + text2; @@ -163,24 +261,20 @@ function displayResults(data, basisTemp, name, start, end) { for (var i = 0; i < 10; i++){ time.push(null); forecast.push(null); - name = 'Alvdal'; + name = '-'; start = '-'; end = '-'; } } - - - - var chartExist = Chart.getChart("daydegreesum"); + let chartExist = Chart.getChart("daydegreesum"); if(chartExist != undefined){ chartExist.destroy(); } - var ctx = document.getElementById("daydegreesum").getContext('2d'); + let ctx = document.getElementById("daydegreesum").getContext('2d'); - series[0] = { label: "Døgngrader", data: temperature, @@ -203,16 +297,16 @@ function displayResults(data, basisTemp, name, start, end) { spanGaps: true }; - var data = { + let dataset = { labels: time, datasets: series }; - var text = String("Varmesum for " + name + " i perioden " + start + " til " + end ); + let text = String("Varmesum for " + name + " i perioden " + start + " til " + end ); new Chart(ctx, { type: 'line', - data: data, + data: dataset, options: { scales: { x: { @@ -328,19 +422,9 @@ function displayResults(data, basisTemp, name, start, end) { } } }); - - -} - -function setStartText (){ - - document.getElementById("textInfo").innerText = ""; - - let infotext = document.createElement("p"); - infotext = "<p>Varmesum, med basistemperatur 0 °C, vert rekna ut frå middeltemperaturen 2 m over bakken for den valde vêrstasjonen. </p>"; - - document.getElementById("textInfo").innerHTML=infotext; + + } } - +export default ApplefruitMoth; diff --git a/applefruitmoth/templates/applefruitmoth/index.html b/applefruitmoth/templates/applefruitmoth/index.html index 6f930f78517c951742f0bf511d1ad965c988661e..5416c0da15edc8e72f5d402959db9aa0042edc06 100755 --- a/applefruitmoth/templates/applefruitmoth/index.html +++ b/applefruitmoth/templates/applefruitmoth/index.html @@ -23,6 +23,244 @@ {% endcomment %} {% load i18n %} {% block title%}{% trans "Apple fruit moth forecasting" %}{%endblock%} + + +{% block customJS %} +<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.7.1/chart.min.js"></script> +<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-annotation@1.0.2"></script> +<script type="text/javascript" src="{% static "js/3rdparty/ol.js" %}"></script> +<script type="text/javascript" src="{% url "javascript-catalog" %}"></script> +<script type="text/javascript" src="{% url "views.settings_js" %}"></script> +<script type="text/javascript" src="{% static "js/util.js" %}"></script> +<script type="text/javascript" src="{% static "applefruitmoth/js/map.js" %}"></script> +<script type="text/javascript" src="{% static "applefruitmoth/js/applefruitmoth.js" %}"></script> +<script src="{% static "js/3rdparty/moment-with-locales.min.js" %}"></script> +<script type="text/javascript"> + + $(document).ready(function() { + + + initMap("appleFruitMothForecastMap","{{settings.MAP_ATTRIBUTION|safe}}"); + + }); +</script> +<script type="module"> + const modulePath = `${settings.vipslogicProtocol}://${settings.vipslogicServerName}/js/mapModal.js`; + + import MapModal from 'https://logic.testvips.nibio.no/js/mapModal.js'; + + const selectWeatherstationElement = document.getElementById("weatherStationId"); + + + + window.lmtPoiMap = function() { + return fetch("https://lmt.nibio.no/services/rest/weatherstation/mapweatherstations") + .then(response => response.json()) + .then(mapStations => { + mapStations.sort((a, b) => a.name.toString().localeCompare(b.name.toString())); + + return mapStations.map(station => { + // If they don't match, return the id as weatherstationId + return { "id": station.weatherstationId, "name": station.name }; + })}) + .catch(error => console.error('Error:', error)); + + } + + window.storeUserSettings = function() { + let userSettings = {}; + userSettingsFields.forEach((fieldId) => { + userSettings[`{{ form_id }}.${fieldId}`] = document.getElementById(fieldId).value; + }); + userSettingsRadios.forEach((radioName) => { + document.getElementsByName(radioName).forEach((radioElement) => { + if(radioElement.checked) + { + userSettings[`{{ form_id }}.${radioName}`] = radioElement.value; + } + }); + + }); + storeLocalSettings(userSettings); + console.info("Store current user settings", userSettings); + } + + + import ApplefruitMoth from "../static/applefruitmoth/js/applefruitmoth.js"; + const applefruitMoth = new ApplefruitMoth(); + + const inputLatitudeElement = document.getElementById("latitude"); + const inputLongitudeElement = document.getElementById("longitude"); + + const userSettingsFields = ["latitude","longitude","timezone","weatherStationId"]; + const userSettingsRadios = ["weatherdataType","radioDays"]; + + let poiIdList = [] + let selectedPoint = null; + let selectedFeature = undefined; + + let startDate; + let endDate; + + 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); + runModel() + } + + window.loadUserSettings = () => { + let userSettings = getLocalSettings(getNameSpaced("{{ form_id }}",userSettingsFields.concat(userSettingsRadios)), false); + console.info("User settings available when page is loaded", userSettings) + + // Settings found, render form and run model + if(Object.keys(userSettings).length > 0) + { + userSettingsFields.forEach((fieldId) =>{ + document.getElementById(fieldId).value = userSettings[`{{ form_id }}.${fieldId}`]; + }); + userSettingsRadios.forEach((radioName) => { + let radioValue = userSettings[`{{ form_id }}.${radioName}`]; + document.getElementsByName(radioName).forEach((radioElement) => { + radioElement.checked = (radioElement.value == radioValue); + }); + }); + + if(document.getElementById("coordinates").checked) + { + displayCoordinatesInput(); + getTimezoneForPoint(inputLatitudeElement.value, inputLongitudeElement.value); + } + else + { + displayWeatherstationInput(); + } + } + } + + window.onload = async(event) => { + startDate = document.getElementById("startdate"); + endDate = document.getElementById("endDate"); + await applefruitMoth.renderWeatherstations(); + loadUserSettings() + runModel(); + } + + // We need to do it this way to keep the "this" reference of the class + window.runModel = function () { storeUserSettings(); applefruitMoth.runModel(); }; + + window.openCoordinatesMap = () => { + if (inputLatitudeElement.value && inputLongitudeElement.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 pois + + import(modulePath).then((module) => { + const MapModal = module.default; + const coordinatesMapInstance = new MapModal('coordinates-map', selectedFeature, 'nb', isPoiMap, allowNewPoints, selectCoordinates); + coordinatesMapInstance.openModal(selectedPoint); + }).catch((error) => { + console.error('Error loading module MapModal for coordinatesMap', error); + }); + + } + + 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; + } + } + runModel(); + } + + window.openPoiMap = () => { + fetch("https://lmt.nibio.no/services/rest/weatherstation/ipmdecisions", { + method: 'GET' + }) + .then(response => response.json()) + .then(geoJson => { + // FILTER with only Ids from the Cydia station list + let filteredFeatures = geoJson["features"] + .filter(feature => applefruitMoth.weatherStationIds.indexOf(feature.id) >= 0) + .map(feature => { + feature["properties"]["pointOfInterestName"] = feature["properties"]["name"]; + feature["properties"]["pointOfInterestId"] = feature["id"]; + feature["properties"]["pointOfInterestTypeId"] = 1; // Type = Weather station + return feature; + }); + geoJson["features"] = filteredFeatures; + const isPoiMap = true; // Map should enable selection of pois + const allowNewPoints = false; // User should not be able to create new pois + + import(modulePath).then((module) => { + const MapModal = module.default; + const poiMapInstance = new MapModal('poi-map', geoJson, 'nb', isPoiMap, allowNewPoints, selectPoi); + const selectedPoiId = applefruitMoth.getSelectedWeatherstation(); + poiMapInstance.openModal(selectedPoiId); + }).catch((error) => { + console.error('Error loading module MapModal for poiMap', error); + }); + }) + .catch(error => { + console.error('Unable to retrieve weatherstation geojson', error); + }); + } + + window.displayWeatherstationInput = function () { + document.getElementById("weatherstation").checked = true; + document.getElementById('input-weatherstation').style.display="block"; + document.getElementById('input-coordinates').style.display="none"; + } + + window.displayCoordinatesInput = function () { + document.getElementById("coordinates").checked = true; + document.getElementById('input-weatherstation').style.display="none"; + document.getElementById('input-coordinates').style.display="block"; + selectWeatherstationElement.selectedIndex = 0; + } + + const getTimezoneForPoint = (latitude, longitude) => { + getLocationInformation(latitude, longitude).then(locationInfo => { + document.getElementById("timezone").value = locationInfo.timezone; + document.getElementById("gridPointInfo").innerHTML = `<b>{% trans "Location name" %}</b> ${locationInfo.location}<br> + <b>{% trans "Latitude" %}</b> ${locationInfo.latitude}<br> + <b>{% trans "Longitude" %}</b> ${locationInfo.longitude}<br> + <b>{% trans "Timezone" %}</b> ${locationInfo.timezone}` + }); + } + + +</script> +{% endblock %} + + {% block content %} <h1>{% trans "Apple fruit moth forecasting" %}</h1> <div class="singleBlockContainer"> @@ -76,31 +314,57 @@ <div class="row"><br><br> <div style="font-size:75%;" id="setBasis"></div> - <div class="col-md-3"> - <div class="form-group"> - <label for="weatherstation">Målestasjon</label> - <select id="weatherstation" class="form-control"></select> + <div class="col-md-4"> + + <div class="form-group"> + <h4>Jeg vil bruke værdata</h4> + <div class="radio"> + <label> + <input type="radio" name="weatherdataType" id="coordinates" value="coordinates" onchange="displayCoordinatesInput();"> + fra et punkt i kartet + </label> + <div id="input-coordinates" class="form-inline" style="margin-top: 10px; display: none;""> + <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();" style="margin-left: 5px;"><i class="fa fa-map-marker fa-lg"></i> Velg i kart</button> + </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" class="form-inline" style="margin-top: 10px; display: none;"> + <select name="weatherStationId" id="weatherStationId" class="form-control" style="width: 60%;" onchange="runModel()"> + <option value="-1">-- Velg værstasjon --</option> + </select> + <button type="button" class="btn btn-primary" onclick="openPoiMap()" style="margin-left: 5px;"><i class="fa fa-map-marker fa-lg"></i> Velg i kart</button> + </div> + <div id="poi-map" class="map-modal"></div> + </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> + + + <br> <div class="form-group"> <label for="startdate">Dato for full blom i rogn</label> - <input type="date" id="startdate" class="form-control"> + <input type="date" id="startdate" class="form-control" onchange="runModel()"> </div> <div class="form-group"> <label for="enddate">Sluttdato, med prognose 9 døgn fram i tid</label> - <input type="date" value=moment().add(7,'days').format('YYYY-MM-DD'); id="enddate" class="form-control"> + <input type="date" id="enddate" class="form-control" onchange="runModel()"> </div> - - <br> - <div class="form-group"> - <button type="button" class="btn btn-primary" onclick="getBasis(this)">Beregn</button> - <br><br><br> - </div> - </div> - <div class="col-md-9"> + </div> + + <div class="col-md-8"> <div class="col-md-2"></div> <div class="col-md-9"> <br> @@ -108,9 +372,8 @@ <img id="image" src="{% static 'applefruitmoth/applefruitmoth.png' %}" style="width:100%; height:auto;"> <p style="padding-left: 10px;padding-top: 5px;font-style: italic;">Foto: Sverre Kobro og E. Fløistad, NIBIO</p> </div> - <div class="col-md-1"></div> </div> - + <div class="col-md-1"></div> </div> <br> </div> @@ -130,25 +393,32 @@ {% endblock %} {% block customCSS %} + +<!--[if lte IE 9]> + <style type="text/css">#oldIEWarning{display: block !important;}</style> +<![endif]--> +<link type="text/css" rel="stylesheet" href="{{settings.VIPSLOGIC_PROTOCOL}}://{{settings.VIPSLOGIC_SERVER_NAME}}/css/3rdparty/leaflet.css" /> +<link type="text/css" rel="stylesheet" href="{{settings.VIPSLOGIC_PROTOCOL}}://{{settings.VIPSLOGIC_SERVER_NAME}}/css/mapModal.css" /> <link rel="stylesheet" href="{% static "css/3rdparty/ol.css" %}" type="text/css"> + +<style> + input#latitude, input#longitude { + width: 30%; + display: inline-block; + margin: 10px 10px 10px 0; + } + select#weatherStationId { + width: 60%; + display: inline-block; + margin: 10px 10px 10px 0; + } + .main-label { + font-size: 1.8rem; + font-weight: 500 !important; + } + .space { + margin-top: 40px; + } +</style> + {% endblock %} -{% block customJS %} -<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.7.1/chart.min.js"></script> -<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-annotation@1.0.2"></script> -<script type="text/javascript" src="{% static "js/3rdparty/ol.js" %}"></script> -<script type="text/javascript" src="{% url "javascript-catalog" %}"></script> -<script type="text/javascript" src="{% url "views.settings_js" %}"></script> -<script type="text/javascript" src="{% static "js/util.js" %}"></script> -<script type="text/javascript" src="{% static "applefruitmoth/js/map.js" %}"></script> -<script type="text/javascript" src="{% static "applefruitmoth/js/applefruitmoth.js" %}"></script> -<script src="{% static "js/3rdparty/moment-with-locales.min.js" %}"></script> -<script type="text/javascript"> - - $(document).ready(function() { - - - initMap("appleFruitMothForecastMap","{{settings.MAP_ATTRIBUTION|safe}}"); - - }); -</script> -{% endblock %} \ No newline at end of file