diff --git a/src/main/java/no/nibio/vips/logic/modules/barleynetblotch/BarleyNetBlotchModelService.java b/src/main/java/no/nibio/vips/logic/modules/barleynetblotch/BarleyNetBlotchModelService.java index 58b9c26ed68b2b13de307c5f059fac87327f9f3c..493f96c287ff253dfee0380009ef3395b27b2aa3 100755 --- a/src/main/java/no/nibio/vips/logic/modules/barleynetblotch/BarleyNetBlotchModelService.java +++ b/src/main/java/no/nibio/vips/logic/modules/barleynetblotch/BarleyNetBlotchModelService.java @@ -19,11 +19,8 @@ package no.nibio.vips.logic.modules.barleynetblotch; import com.webcohesion.enunciate.metadata.Facet; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Date; -import java.util.List; -import java.util.TimeZone; + +import java.util.*; import javax.ejb.EJB; import javax.persistence.EntityManager; import javax.persistence.NoResultException; @@ -38,10 +35,7 @@ import no.nibio.vips.entity.ModelConfiguration; import no.nibio.vips.entity.Result; import no.nibio.vips.entity.WeatherObservation; import no.nibio.vips.logic.controller.session.ForecastBean; -import no.nibio.vips.logic.entity.Organism; -import no.nibio.vips.logic.entity.Organization; -import no.nibio.vips.logic.entity.PointOfInterestWeatherStation; -import no.nibio.vips.logic.entity.Preparation; +import no.nibio.vips.logic.entity.*; import no.nibio.vips.logic.util.RunModelException; import no.nibio.vips.logic.util.SystemTime; import no.nibio.vips.observation.ObservationImpl; @@ -140,6 +134,9 @@ public class BarleyNetBlotchModelService { public Response runModel( @PathParam("organizationId") Integer organizationId, @QueryParam("timeZone") String timeZoneStr, + @QueryParam("weatherdataType") String weatherdataType, + @QueryParam("latitude") Double latitude, + @QueryParam("longitude") Double longitude, @QueryParam("weatherStationId") Integer weatherStationId, @QueryParam("sowingDate") String sowingDateStr, @QueryParam("cropId") Integer cropOrganismId, @@ -173,47 +170,57 @@ public class BarleyNetBlotchModelService { ModelConfiguration config = new ModelConfiguration(); config.setModelId("BARLEYNETB"); // Get weather data from weather station - PointOfInterestWeatherStation weatherStation = em.find(PointOfInterestWeatherStation.class, weatherStationId); - WeatherDataSourceUtil wsdUtil = new WeatherDataSourceUtil(); - - // End date for weather data depends on season - // We try to add 5 months to the sowing date. If thats in the future, - // We add 10 days to today - Date dateOfLastWeatherData; - Calendar cal = Calendar.getInstance(timeZone); - cal.setTime(sowingDate); - cal.add(Calendar.MONTH, 5); - Date fiveMonthsAfterSowingDate = cal.getTime(); - if(fiveMonthsAfterSowingDate.after(SystemTime.getSystemTime())) - { - cal.setTime(SystemTime.getSystemTime()); - cal.add(Calendar.DATE, 10); - dateOfLastWeatherData = cal.getTime(); - } - else - { - dateOfLastWeatherData = fiveMonthsAfterSowingDate; - } + WeatherDataSourceUtil wsdUtil = new WeatherDataSourceUtil(); + Date endDateForWeatherData = calculateEndDateForWeatherData(timeZone, sowingDate); List<WeatherObservation> observations; - try { - observations = wsdUtil.getWeatherObservations( + + if("weatherstation".equals(weatherdataType)) { + PointOfInterestWeatherStation weatherStation = + em.find(PointOfInterestWeatherStation.class, weatherStationId); + try { + observations = wsdUtil.getWeatherObservations( weatherStation, WeatherObservation.LOG_INTERVAL_ID_1H, - new String[]{ + new String[] { WeatherElements.TEMPERATURE_MEAN, WeatherElements.PRECIPITATION }, - sowingDate, - dateOfLastWeatherData - ); - } catch (WeatherDataSourceException ex) { - return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(ex.getMessage()).build(); - } - - if(observations == null || observations.isEmpty()) - { - return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity("Could not find weather data for weather station with id=" + weatherStationId).build(); + sowingDate, + endDateForWeatherData + ); + } catch (WeatherDataSourceException ex) { + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(ex.getMessage()).build(); + } + if (observations == null || observations.isEmpty()) { + return Response.status(Response.Status.INTERNAL_SERVER_ERROR) + .entity("Could not find weather data for weather station with id=" + weatherStationId).build(); + } + } else { + PointOfInterest coordinates = new PointOfInterest(); + coordinates.setLatitude(latitude); + coordinates.setLongitude(longitude); + try { + observations = wsdUtil.getWeatherObservations( + "https://weather.vips.nibio.no/rest/grid/openmeteo/" + coordinates.getLongitude() + "_" + coordinates.getLatitude(), + WeatherObservation.LOG_INTERVAL_ID_1H, + new String[] { + WeatherElements.TEMPERATURE_MEAN, + WeatherElements.PRECIPITATION + }, + sowingDate, + endDateForWeatherData, + timeZone, + Boolean.FALSE, + new HashSet<>(Collections.singletonList(WeatherObservation.LOG_INTERVAL_ID_1H)) + ); + } catch (WeatherDataSourceException ex) { + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(ex.getMessage()).build(); + } + if (observations == null || observations.isEmpty()) { + return Response.status(Response.Status.INTERNAL_SERVER_ERROR) + .entity("Could not find weather data for weather station with id=" + weatherStationId).build(); + } } // Mandatory parameters @@ -257,6 +264,32 @@ public class BarleyNetBlotchModelService { } return Response.ok().entity(results).build(); } - - + + /** + * End date for weather data depends on season. We try to add 5 months to the sowing date. + * If the resulting date is in the future, we add 10 days to today. + * + * @param timeZone The current timezone + * @param sowingDate The sowing date + * @return The end date for weather data + */ + private static Date calculateEndDateForWeatherData(TimeZone timeZone, Date sowingDate) { + Date dateOfLastWeatherData; + Calendar cal = Calendar.getInstance(timeZone); + cal.setTime(sowingDate); + cal.add(Calendar.MONTH, 5); + Date fiveMonthsAfterSowingDate = cal.getTime(); + if(fiveMonthsAfterSowingDate.after(SystemTime.getSystemTime())) + { + cal.setTime(SystemTime.getSystemTime()); + cal.add(Calendar.DATE, 10); + dateOfLastWeatherData = cal.getTime(); + } + else + { + dateOfLastWeatherData = fiveMonthsAfterSowingDate; + } + return dateOfLastWeatherData; + } + } diff --git a/src/main/webapp/js/mapModal.js b/src/main/webapp/js/mapModal.js index 37dfec3049c75cc10eb17d9f177ced4329024c4a..ad34e2b08dda801a77bd90498c757882ab8faeab 100644 --- a/src/main/webapp/js/mapModal.js +++ b/src/main/webapp/js/mapModal.js @@ -35,7 +35,9 @@ class MapModal { static TRANSLATIONS = { nb: { selectLocation: 'Velg sted', + selectCoordinates: 'Velg punkt', createNewLocation: 'Opprett nytt sted', + selectNewCoordinates: 'Velg nytt punkt', name: 'Navn', latitude: 'Breddegrad', longitude: 'Lengdegrad', @@ -54,7 +56,9 @@ class MapModal { }, en: { selectLocation: 'Select location', + selectCoordinates: 'Select point', createNewLocation: 'Create New Location', + selectNewCoordinates: 'Select new point', name: 'Name', latitude: 'Latitude', longitude: 'Longitude', @@ -86,11 +90,10 @@ class MapModal { this.mapContainerId = mapModalId + "-container"; this.mapContainerElement = this.addMapContainer(this.mapModalElement, this.mapContainerId); - // The variable below should instead be: this.selectPoi or this.selectCoordinates - // this.includeTypeInformation = Object.keys(typeNameMap).length === 7; - this.includeTypeInformation = true; + this.geoJsonData = geoJsonData && geoJsonData !== "{}" ? geoJsonData : { features: []}; + // Filter out invalid features + this.geoJsonData.features = this.geoJsonData.features.filter(feature => feature.geometry.type === "Point" && !isNaN(feature.geometry.coordinates[0]) && !isNaN(feature.geometry.coordinates[1])); - this.geoJsonData = geoJsonData; if (language in MapModal.TRANSLATIONS) { this.translations = MapModal.TRANSLATIONS[language]; } else { @@ -182,9 +185,8 @@ class MapModal { this.map.addControl(this.closeMapControl); // Add points to the map if given - if (this.geoJsonData.features) { + if (this.geoJsonData && this.geoJsonData.features) { geoJSON(this.geoJsonData, { - filter: (feature) => feature.geometry.type === "Point", pointToLayer: (feature, latlng) => { return circleMarker(latlng, this.styleOfPointMarker(feature.properties.pointOfInterestTypeId)); }, @@ -218,7 +220,7 @@ class MapModal { } // Function called when point is hidden (by deselecting its location type in legend box) - // If point is already selected, the popup and info panel is removed. + // If point is already selected, the popup is removed. unbindActionToPoint(layer) { if (this.selectedExistingPointMarker === layer) { layer.closePopup(); @@ -238,6 +240,10 @@ class MapModal { selectPointById(pointOfInterestId) { const selectedFeature = this.getFeatureById(pointOfInterestId); const selectedLayer = this.getLayerById(pointOfInterestId); + if(!selectedFeature || !selectedLayer) { + console.error("Unable to display selected point " + pointOfInterestId, this.geoJsonData.features) + return + } this.displaySelectedPoint(selectedFeature, selectedLayer, true); selectedLayer.openPopup(); } @@ -394,12 +400,19 @@ class MapModal { const type = this.translations['poiType' + feature.properties.pointOfInterestTypeId]; const latitude = feature.geometry.coordinates[1].toFixed(this.coordinatePrecision); const longitude = feature.geometry.coordinates[0].toFixed(this.coordinatePrecision); - const buttonLabel = this.translations.selectLocation; - popupElement.innerHTML = `<h4>${name}</h4> - <b>${this.translations.latitude}</b> ${latitude}<br> - <b>${this.translations.longitude}</b> ${longitude}<br> - <b>${this.translations.type}</b> ${type}<br><br> - <button id="submit-button" class="btn btn-primary">${buttonLabel}</button>` + const buttonLabel = this.isPoiMap ? this.translations.selectLocation : this.translations.selectCoordinates; + + if(this.isPoiMap) { + popupElement.innerHTML = `<h4>${name}</h4> + <b>${this.translations.latitude}</b> ${latitude}<br> + <b>${this.translations.longitude}</b> ${longitude}<br> + <b>${this.translations.type}</b> ${type}<br><br> + <button id="submit-button" class="btn btn-primary">${buttonLabel}</button>` + } else { + popupElement.innerHTML = `<b>${this.translations.latitude}</b> ${latitude}<br> + <b>${this.translations.longitude}</b> ${longitude}<br><br> + <button id="submit-button" class="btn btn-primary">${buttonLabel}</button>` + } const buttonElement = popupElement.querySelector("#submit-button"); buttonElement.addEventListener('click', () => { this.confirmSelection(feature); @@ -438,7 +451,7 @@ class MapModal { form.innerHTML = ` <div class="panel-heading"> - <h4 class="panel-title">${this.translations.createNewLocation}</h4> + <h4 class="panel-title">${this.isPoiMap ? this.translations.createNewLocation : this.translations.selectNewCoordinates}</h4> <span id="map-poi-close-button" style="position: absolute; top: 5px; right: 10px; cursor: pointer; font-size: 18px;">×</span> </div> <div class="panel-body"> @@ -463,7 +476,7 @@ class MapModal { </select> </div> <div class="form-group text-right"> - <button id="map-poi-submit-button" class="btn btn-primary">${this.translations.selectLocation}</button> + <button id="map-poi-submit-button" class="btn btn-primary">${this.isPoiMap ? this.translations.selectLocation : this.translations.selectCoordinates}</button> </div> </div>`;