From 2e4dd379fcb38ea610e0b0d06c4d9628db0deb5d Mon Sep 17 00:00:00 2001 From: Tor-Einar Skog <tor-einar.skog@nibio.no> Date: Wed, 19 Dec 2018 10:48:27 +0100 Subject: [PATCH] Adding Leaf Blotch Model Specific stuff --- .../logic/controller/session/MessageBean.java | 2 +- .../controller/session/ObservationBean.java | 4 +- .../wheatleafblotch/ResistanceFactor.java | 121 +++++++ .../WheatLeafBlotchModelService.java | 324 ++++++++++++++++++ ...heatLeafBlotchPreparationEffectFactor.java | 113 ++++++ src/main/webapp/js/observationMap.js | 4 +- 6 files changed, 563 insertions(+), 5 deletions(-) create mode 100644 src/main/java/no/nibio/vips/logic/modules/wheatleafblotch/ResistanceFactor.java create mode 100644 src/main/java/no/nibio/vips/logic/modules/wheatleafblotch/WheatLeafBlotchModelService.java create mode 100644 src/main/java/no/nibio/vips/logic/modules/wheatleafblotch/WheatLeafBlotchPreparationEffectFactor.java diff --git a/src/main/java/no/nibio/vips/logic/controller/session/MessageBean.java b/src/main/java/no/nibio/vips/logic/controller/session/MessageBean.java index 64b0bce0..e0d08001 100755 --- a/src/main/java/no/nibio/vips/logic/controller/session/MessageBean.java +++ b/src/main/java/no/nibio/vips/logic/controller/session/MessageBean.java @@ -434,6 +434,6 @@ public class MessageBean { return new ArrayList<>(); } } - + } diff --git a/src/main/java/no/nibio/vips/logic/controller/session/ObservationBean.java b/src/main/java/no/nibio/vips/logic/controller/session/ObservationBean.java index 84296265..09683391 100755 --- a/src/main/java/no/nibio/vips/logic/controller/session/ObservationBean.java +++ b/src/main/java/no/nibio/vips/logic/controller/session/ObservationBean.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014 NIBIO <http://www.nibio.no/>. + * Copyright (c) 2018 NIBIO <http://www.nibio.no/>. * * This file is part of VIPSLogic. * VIPSLogic is free software: you can redistribute it and/or modify @@ -58,7 +58,7 @@ import org.wololo.geojson.FeatureCollection; import org.wololo.geojson.GeoJSONFactory; /** - * @copyright 2014-2015 <a href="http://www.nibio.no/">NIBIO</a> + * @copyright 2014-2018 <a href="http://www.nibio.no/">NIBIO</a> * @author Tor-Einar Skog <tor-einar.skog@nibio.no> */ @Stateless diff --git a/src/main/java/no/nibio/vips/logic/modules/wheatleafblotch/ResistanceFactor.java b/src/main/java/no/nibio/vips/logic/modules/wheatleafblotch/ResistanceFactor.java new file mode 100644 index 00000000..175f13c9 --- /dev/null +++ b/src/main/java/no/nibio/vips/logic/modules/wheatleafblotch/ResistanceFactor.java @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2015 NIBIO <http://www.nibio.no/>. + * + * This file is part of VIPSLogic. + * VIPSLogic is free software: you can redistribute it and/or modify + * it under the terms of the NIBIO Open Source License as published by + * NIBIO, either version 1 of the License, or (at your option) any + * later version. + * + * VIPSLogic is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * NIBIO Open Source License for more details. + * + * You should have received a copy of the NIBIO Open Source License + * along with VIPSLogic. If not, see <http://www.nibio.no/licenses/>. + * + */ + +package no.nibio.vips.logic.modules.wheatleafblotch; + +import java.io.Serializable; +import java.math.BigDecimal; +import javax.persistence.Basic; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.NamedQueries; +import javax.persistence.NamedQuery; +import javax.persistence.Table; +import javax.validation.constraints.NotNull; +import javax.xml.bind.annotation.XmlRootElement; + +/** + * @copyright 2018 <a href="http://www.nibio.no/">NIBIO</a> + * @author Tor-Einar Skog <tor-einar.skog@nibio.no> + */ +@Entity +@Table(name = "resistance_factor", schema = "wheatleafb") +@XmlRootElement +@NamedQueries({ + @NamedQuery(name = "ResistanceFactor.findAll", query = "SELECT f FROM Factors f"), + @NamedQuery(name = "ResistanceFactor.findByOrganizationId", query = "SELECT f FROM ResistanceFactor f where f.organizationId IN (SELECT o.organizationId FROM Organization o WHERE o = :organizationId OR o.parentOrganizationId = :organizationId)"), + @NamedQuery(name = "ResistanceFactor.findByCropOrganismId", query = "SELECT f FROM ResistanceFactor f WHERE f.cropOrganismId = :cropOrganismId"), + @NamedQuery(name = "ResistanceFactor.findByResistanceFactor", query = "SELECT f FROM ResistanceFactor f WHERE f.resistanceFactor = :resistanceFactor") +}) +public class ResistanceFactor implements Serializable { + private static final long serialVersionUID = 1L; + @Id + @Basic(optional = false) + @NotNull + @Column(name = "crop_organism_id") + private Integer cropOrganismId; + @Column(name = "organization_id") + private Integer organizationId; + @Column(name = "resistance_factor") + private BigDecimal resistanceFactor; + + public ResistanceFactor() { + } + + public ResistanceFactor(Integer cropOrganismId) { + this.cropOrganismId = cropOrganismId; + } + + public Integer getCropOrganismId() { + return cropOrganismId; + } + + public void setCropOrganismId(Integer cropOrganismId) { + this.cropOrganismId = cropOrganismId; + } + + public BigDecimal getResistanceFactor() { + return resistanceFactor; + } + + public void setResistanceFactor(BigDecimal resistanceFactor) { + this.resistanceFactor = resistanceFactor; + } + + @Override + public int hashCode() { + int hash = 0; + hash += (cropOrganismId != null ? cropOrganismId.hashCode() : 0); + return hash; + } + + @Override + public boolean equals(Object object) { + // TODO: Warning - this method won't work in the case the id fields are not set + if (!(object instanceof ResistanceFactor)) { + return false; + } + ResistanceFactor other = (ResistanceFactor) object; + if ((this.cropOrganismId == null && other.cropOrganismId != null) || (this.cropOrganismId != null && !this.cropOrganismId.equals(other.cropOrganismId))) { + return false; + } + return true; + } + + @Override + public String toString() { + return "no.nibio.vips.logic.modules.barleynetblotch.Factors[ cropOrganismId=" + cropOrganismId + " ]"; + } + + /** + * @return the organizationId + */ + public Integer getOrganizationId() { + return organizationId; + } + + /** + * @param organizationId the organizationId to set + */ + public void setOrganizationId(Integer organizationId) { + this.organizationId = organizationId; + } + +} diff --git a/src/main/java/no/nibio/vips/logic/modules/wheatleafblotch/WheatLeafBlotchModelService.java b/src/main/java/no/nibio/vips/logic/modules/wheatleafblotch/WheatLeafBlotchModelService.java new file mode 100644 index 00000000..fcdb61a8 --- /dev/null +++ b/src/main/java/no/nibio/vips/logic/modules/wheatleafblotch/WheatLeafBlotchModelService.java @@ -0,0 +1,324 @@ +/* + * Copyright (c) 2015 NIBIO <http://www.nibio.no/>. + * + * This file is part of VIPSLogic. + * VIPSLogic is free software: you can redistribute it and/or modify + * it under the terms of the NIBIO Open Source License as published by + * NIBIO, either version 1 of the License, or (at your option) any + * later version. + * + * VIPSLogic is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * NIBIO Open Source License for more details. + * + * You should have received a copy of the NIBIO Open Source License + * along with VIPSLogic. If not, see <http://www.nibio.no/licenses/>. + * + */ + +package no.nibio.vips.logic.modules.wheatleafblotch; + +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 javax.persistence.EntityManager; +import javax.persistence.NoResultException; +import javax.persistence.PersistenceContext; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Response; +import no.nibio.vips.entity.ModelConfiguration; +import no.nibio.vips.entity.Result; +import no.nibio.vips.entity.WeatherObservation; +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.util.RunModelException; +import no.nibio.vips.logic.util.SessionControllerGetter; +import no.nibio.vips.logic.util.SystemTime; +import no.nibio.vips.observation.ObservationImpl; +import no.nibio.vips.pestmanagement.SprayingImpl; +import no.nibio.vips.util.ParseRESTParamUtil; +import no.nibio.vips.util.WeatherElements; +import no.nibio.vips.util.weather.WeatherDataSourceException; +import no.nibio.vips.util.weather.WeatherDataSourceUtil; + +/** + * @copyright 2018 <a href="http://www.nibio.no/">NIBIO</a> + * @author Tor-Einar Skog <tor-einar.skog@nibio.no> + */ +@Path("rest/wheatleafblotchmodel") +@Facet("restricted") +public class WheatLeafBlotchModelService { + private final static String VIPSCOREMANAGER_URL = System.getProperty("no.nibio.vips.logic.VIPSCOREMANAGER_URL"); + + @PersistenceContext(unitName="VIPSLogic-PU") + EntityManager em; + + @GET + @Path("wheatvarieties/{organizationId}") + @Produces("application/json;charset=UTF-8") + public Response getWheatVarieties(@PathParam("organizationId") Integer organizationId) + { + try + { + Organization o = em.find(Organization.class, organizationId); + List<ResistanceFactor> resistanceFactors = em.createNamedQuery("ResistanceFactor.findByOrganizationId") + .setParameter("organizationId", o).getResultList(); + + List<Organism> wheatTypes = em.createNativeQuery("SELECT * FROM public.organism WHERE parent_organism_id IN (" + + "SELECT organism_id FROM public.organism WHERE latin_name='Triticum'" + + ")", Organism.class).getResultList(); + + List<Integer> varietyIds = new ArrayList<>(); + for(ResistanceFactor f:resistanceFactors) + { + varietyIds.add(f.getCropOrganismId()); + } + + List<Organism> wheatVarieties = new ArrayList<>(); + + if(varietyIds.size() > 0) + { + wheatVarieties = em.createNamedQuery("Organism.findByOrganismIds") + .setParameter("organismIds", varietyIds).getResultList(); + for(Organism variety:wheatVarieties) + { + for(ResistanceFactor f:resistanceFactors) + { + if(f.getCropOrganismId().equals(variety.getOrganismId())) + { + variety.getExtraProperties().put("factors", f); + } + } + for(Organism type:wheatTypes) + { + if(variety.getParentOrganismId().equals(type.getOrganismId())) + { + variety.getExtraProperties().put("type", type); + } + } + } + } + return Response.ok().entity(wheatVarieties).build(); + } + catch(NoResultException ex) + { + return Response.ok().entity(ex.getMessage()).build(); + } + + } + + @GET + @Path("preparations/{organizationId}") + @Produces("application/json;charset=UTF-8") + public Response getPreparations(@PathParam("organizationId") Integer organizationId) + { + try + { + // TODO: Get all organization children as well + // Must get the preparations first + List<Preparation> preparations = em.createNativeQuery( + "SELECT * FROM preparation " + + "WHERE organization_id = :organizationId " + + "AND preparation_id IN (" + + "SELECT preparation_id FROM wheatleafb.wheatleafblotch_preparation_effect_factor" + + ")", + Preparation.class) + .setParameter("organizationId", organizationId) + .getResultList(); + return Response.ok().entity(preparations).build(); + } + catch(NoResultException ex) + { + return Response.ok().entity(ex.getMessage()).build(); + } + } + + @GET + @Path("runmodel/{organizationId}") + @Produces("application/json;charset=UTF-8") + public Response runModel( + @PathParam("organizationId") Integer organizationId, + @QueryParam("wheatType") Integer wheatType, + @QueryParam("timeZone") String timeZoneStr, + @QueryParam("weatherStationId") Integer weatherStationId, + @QueryParam("sowingDate") String sowingDateStr, + @QueryParam("cropId") Integer cropOrganismId, + @QueryParam("observationDate") String observationDateStr, + @QueryParam("observationValue") String observationValueStr, + @QueryParam("sameCropAsLastSeason") String sameCropAsLastSeasonStr, + @QueryParam("plowed") String plowedStr, + @QueryParam("sprayingDate") String sprayingDateStr, + @QueryParam("preparationId1") String preparationId1Str, + @QueryParam("preparationDose1") String preparationDose1Str, + @QueryParam("preparationId2") String preparationId2Str, + @QueryParam("preparationDose2") String preparationDose2Str, + @QueryParam("preparationId3") String preparationId3Str, + @QueryParam("preparationDose3") String preparationDose3Str + ) + { + // Some parsing needed... + ParseRESTParamUtil parseUtil = new ParseRESTParamUtil(); + TimeZone timeZone = TimeZone.getTimeZone(timeZoneStr); + Date sowingDate = parseUtil.parseISODate(sowingDateStr, timeZone); + Date observationDate = parseUtil.parseISODateTime(observationDateStr, timeZone); + // In case it's just a date string + if(observationDate == null) + { + observationDate = parseUtil.parseISODate(observationDateStr, timeZone); + } + Double observationValue = parseUtil.parseDouble(observationValueStr); + Boolean sameCropAsLastSeason = parseUtil.parseCheckbox(sameCropAsLastSeasonStr); + Boolean plowed = parseUtil.parseCheckbox(plowedStr); + Date sprayingDate = parseUtil.parseISODate(sprayingDateStr, timeZone); + Integer preparationId1 = parseUtil.parseInteger(preparationId1Str); + Double preparationDose1 = parseUtil.parseDouble(preparationDose1Str); + Integer preparationId2 = parseUtil.parseInteger(preparationId2Str); + Double preparationDose2 = parseUtil.parseDouble(preparationDose2Str); + Integer preparationId3 = parseUtil.parseInteger(preparationId3Str); + Double preparationDose3 = parseUtil.parseDouble(preparationDose3Str); + + // Build model configuration + ModelConfiguration config = new ModelConfiguration(); + config.setModelId("WLEAFBLTCH"); + // Get weather data from weather station + PointOfInterestWeatherStation weatherStation = em.find(PointOfInterestWeatherStation.class, weatherStationId); + WeatherDataSourceUtil wsdUtil = new WeatherDataSourceUtil(); + + String[] parameterList; + + // End date for weather data depends on season + // We try to add 5 months to the sowing date. If that's in the future, + // We add 10 days to today + Date dateOfFirstWeatherData; + Date dateOfLastWeatherData; + Calendar cal = Calendar.getInstance(timeZone); + cal.setTime(sowingDate); + // If winter wheat, the sowing date is after August 1st. We get weather + // data from March 1st the following year. And we need soil temperature + if(cal.get(Calendar.MONTH) > Calendar.JULY) + { + cal.set(Calendar.YEAR, cal.get(Calendar.YEAR) + 1); + cal.set(Calendar.MONTH, Calendar.MARCH); + cal.set(Calendar.DATE, 1); + parameterList = new String[]{ + WeatherElements.TEMPERATURE_MEAN, + WeatherElements.PRECIPITATION, + WeatherElements.SOIL_TEMPERATURE_10CM_MEAN + }; + } + else + { + cal.add(Calendar.MONTH, -1); + parameterList = new String[]{ + WeatherElements.TEMPERATURE_MEAN, + WeatherElements.PRECIPITATION + }; + } + dateOfFirstWeatherData = cal.getTime(); + cal.add(Calendar.MONTH, 6); + Date fiveMonthsAfterSowingDate = cal.getTime(); + if(fiveMonthsAfterSowingDate.after(SystemTime.getSystemTime())) + { + cal.setTime(SystemTime.getSystemTime()); + cal.add(Calendar.DATE, 10); + dateOfLastWeatherData = cal.getTime(); + } + else + { + dateOfLastWeatherData = fiveMonthsAfterSowingDate; + } + + List<WeatherObservation> observations; + try { + observations = wsdUtil.getWeatherObservations( + weatherStation, + WeatherObservation.LOG_INTERVAL_ID_1H, + parameterList, + dateOfFirstWeatherData, + 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(); + } + + // Mandatory parameters + config.setConfigParameter("wheatType", wheatType); + config.setConfigParameter("observations", observations); + config.setConfigParameter("timeZone", timeZone.getID()); + config.setConfigParameter("sowingDate", sowingDateStr); + config.setConfigParameter("plowed", plowed); + config.setConfigParameter("previousSeasonCropType", sameCropAsLastSeason ? 1 : 2 ); + // Optional parameters + if(observationDate != null && observationValue != null) + { + ObservationImpl observation = new ObservationImpl(); + observation.setName("Septoria tritici"); + observation.setObservationData("{\"percentInfectedLeaves\": " + observationValue + "}"); + observation.setTimeOfObservation(observationDate); + config.setConfigParameter("observation", observation); + } + if(sprayingDate != null && preparationId1 != null && preparationDose1 != null) + { + SprayingImpl spraying = new SprayingImpl(); + spraying.setSprayingDate(sprayingDate); + WheatLeafBlotchPreparationEffectFactor preparationEffectFactor1 = em.createNamedQuery("WheatLeafBlotchPreparationEffectFactor.findByPreparationId", WheatLeafBlotchPreparationEffectFactor.class) + .setParameter("preparationId", preparationId1).getSingleResult(); + WheatLeafBlotchPreparationEffectFactor preparationEffectFactor2 = null; + WheatLeafBlotchPreparationEffectFactor preparationEffectFactor3 = null; + Integer denominator = 1; + if(preparationId2 != null && preparationDose2 != null) + { + preparationEffectFactor2 = em.createNamedQuery("WheatLeafBlotchPreparationEffectFactor.findByPreparationId", WheatLeafBlotchPreparationEffectFactor.class) + .setParameter("preparationId", preparationId2).getSingleResult(); + denominator++; + } + if(preparationId3 != null && preparationDose3 != null) + { + preparationEffectFactor3 = em.createNamedQuery("WheatLeafBlotchPreparationEffectFactor.findByPreparationId", WheatLeafBlotchPreparationEffectFactor.class) + .setParameter("preparationId", preparationId3).getSingleResult(); + denominator++; + } + spraying.setSprayingEffectFactor( + (1 - (preparationDose1 * preparationEffectFactor1.getFactorValue() / preparationEffectFactor1.getFullDosisMlDaa()) + + (preparationEffectFactor2 != null ? (1 - (preparationDose2 * preparationEffectFactor2.getFactorValue() / preparationEffectFactor2.getFullDosisMlDaa())) : 0.0 ) + + (preparationEffectFactor3 != null ? (1 - (preparationDose3 * preparationEffectFactor3.getFactorValue() / preparationEffectFactor3.getFullDosisMlDaa())) : 0.0) + ) / denominator + ); + //System.out.println("sprayingEffectFactor=" + spraying.getSprayingEffectFactor()); + config.setConfigParameter("spraying", spraying); + } + + + // Must get the VIPSCore user id for this organization + Organization org = em.find(Organization.class, organizationId); + Integer VIPSCoreUserId = org.getDefaultVipsCoreUserId(); + + List<Result> results; + try + { + results = SessionControllerGetter.getForecastBean().runForecast(config, VIPSCoreUserId); + } + catch(RunModelException ex) + { + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(ex.getMessage()).build(); + } + return Response.ok().entity(results).build(); + } + + +} diff --git a/src/main/java/no/nibio/vips/logic/modules/wheatleafblotch/WheatLeafBlotchPreparationEffectFactor.java b/src/main/java/no/nibio/vips/logic/modules/wheatleafblotch/WheatLeafBlotchPreparationEffectFactor.java new file mode 100644 index 00000000..d0e53006 --- /dev/null +++ b/src/main/java/no/nibio/vips/logic/modules/wheatleafblotch/WheatLeafBlotchPreparationEffectFactor.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2015 NIBIO <http://www.nibio.no/>. + * + * This file is part of VIPSLogic. + * VIPSLogic is free software: you can redistribute it and/or modify + * it under the terms of the NIBIO Open Source License as published by + * NIBIO, either version 1 of the License, or (at your option) any + * later version. + * + * VIPSLogic is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * NIBIO Open Source License for more details. + * + * You should have received a copy of the NIBIO Open Source License + * along with VIPSLogic. If not, see <http://www.nibio.no/licenses/>. + * + */ + +package no.nibio.vips.logic.modules.wheatleafblotch; + +import java.io.Serializable; +import javax.persistence.Basic; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.NamedQueries; +import javax.persistence.NamedQuery; +import javax.persistence.Table; +import javax.validation.constraints.NotNull; +import javax.xml.bind.annotation.XmlRootElement; + +/** + * @copyright 2018 <a href="http://www.nibio.no/">NIBIO</a> + * @author Tor-Einar Skog <tor-einar.skog@nibio.no> + */ +@Entity +@Table(name = "wheatleafblotch_preparation_effect_factor", schema = "wheatleafb") +@XmlRootElement +@NamedQueries({ + @NamedQuery(name = "WheatLeafBlotchPreparationEffectFactor.findAll", query = "SELECT p FROM WheatLeafBlotchPreparationEffectFactor p"), + @NamedQuery(name = "WheatLeafBlotchPreparationEffectFactor.findByPreparationId", query = "SELECT p FROM WheatLeafBlotchPreparationEffectFactor p WHERE p.preparationId = :preparationId"), + @NamedQuery(name = "WheatLeafBlotchPreparationEffectFactor.findByFullDosisMlDaa", query = "SELECT p FROM WheatLeafBlotchPreparationEffectFactor p WHERE p.fullDosisMlDaa = :fullDosisMlDaa"), + @NamedQuery(name = "WheatLeafBlotchPreparationEffectFactor.findByFactorValue", query = "SELECT p FROM WheatLeafBlotchPreparationEffectFactor p WHERE p.factorValue = :factorValue")}) +public class WheatLeafBlotchPreparationEffectFactor implements Serializable { + private static final long serialVersionUID = 1L; + @Id + @Basic(optional = false) + @NotNull + @Column(name = "preparation_id") + private Integer preparationId; + @Column(name = "full_dosis_ml_daa") + private Double fullDosisMlDaa; + @Column(name = "factor_value") + private Double factorValue; + + public WheatLeafBlotchPreparationEffectFactor() { + } + + public WheatLeafBlotchPreparationEffectFactor(Integer preparationId) { + this.preparationId = preparationId; + } + + public Integer getPreparationId() { + return preparationId; + } + + public void setPreparationId(Integer preparationId) { + this.preparationId = preparationId; + } + + public Double getFullDosisMlDaa() { + return fullDosisMlDaa; + } + + public void setFullDosisMlDaa(Double fullDosisMlDaa) { + this.fullDosisMlDaa = fullDosisMlDaa; + } + + public Double getFactorValue() { + return factorValue; + } + + public void setFactorValue(Double factorValue) { + this.factorValue = factorValue; + } + + @Override + public int hashCode() { + int hash = 0; + hash += (preparationId != null ? preparationId.hashCode() : 0); + return hash; + } + + @Override + public boolean equals(Object object) { + // TODO: Warning - this method won't work in the case the id fields are not set + if (!(object instanceof WheatLeafBlotchPreparationEffectFactor)) { + return false; + } + WheatLeafBlotchPreparationEffectFactor other = (WheatLeafBlotchPreparationEffectFactor) object; + if ((this.preparationId == null && other.preparationId != null) || (this.preparationId != null && !this.preparationId.equals(other.preparationId))) { + return false; + } + return true; + } + + @Override + public String toString() { + return "no.nibio.vips.logic.modules.barleynetblotch.PreparationEffectFactor[ preparationId=" + preparationId + " ]"; + } + +} diff --git a/src/main/webapp/js/observationMap.js b/src/main/webapp/js/observationMap.js index 5a132bb7..fa6cd64a 100755 --- a/src/main/webapp/js/observationMap.js +++ b/src/main/webapp/js/observationMap.js @@ -198,8 +198,8 @@ var initMap = function( var illustrationElm = ""; if(observation.observationIllustrationSet.length == 1) { - var illustration = observation.observationIllustrationSet[0]; - illustrationElm = "<img src='/static/images/observations/" + observation.organismId + "/" + illustration.observationIllustrationPK.fileName + "' class='img-responsive'/>"; + var illustration = observation.observationIllustrationSet[0]; + illustrationElm = "<img src='/static/images/observations/" + observation.organismId + "/" + illustration.observationIllustrationPK.fileName + "' class='img-responsive'/>"; } // Create the popup, showing it poiDetails.popover({ -- GitLab