diff --git a/pom.xml b/pom.xml index 00f0708b6a8a797a808388fa0861573ba25e76d2..731ccfc951c5fc345dee53d5a75e9c164836f067 100644 --- a/pom.xml +++ b/pom.xml @@ -9,17 +9,28 @@ <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> + <geotools.version>13.4</geotools.version> </properties> <name>VIPSLogic</name> <url>http://maven.apache.org</url> <repositories> + <repository> + <id>jitpack.io</id> + <url>https://jitpack.io</url> +</repository> <repository> <id>Hibernate Spatial repo</id> <url>http://www.hibernatespatial.org/repository</url> </repository> + </repositories> <dependencies> + <dependency> + <groupId>com.github.bjornharrtell</groupId> + <artifactId>jts2geojson</artifactId> + <version>0.6.0</version> + </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-spatial</artifactId> @@ -115,6 +126,12 @@ <artifactId>jackson-databind</artifactId> <version>2.4.1</version> </dependency> + <dependency> + <groupId>log4j</groupId> + <artifactId>log4j</artifactId> + <version>1.2.14</version> + <type>jar</type> + </dependency> <dependency> <groupId>javax</groupId> <artifactId>javaee-web-api</artifactId> diff --git a/src/main/java/no/nibio/vips/logic/controller/servlet/ObservationController.java b/src/main/java/no/nibio/vips/logic/controller/servlet/ObservationController.java index 239b18744c124a0becba1a9da96fbaffa21f75f8..8002b1a29e51b31d914e0dd38bd7675caa5ab4bd 100644 --- a/src/main/java/no/nibio/vips/logic/controller/servlet/ObservationController.java +++ b/src/main/java/no/nibio/vips/logic/controller/servlet/ObservationController.java @@ -108,8 +108,11 @@ public class ObservationController extends HttpServlet { try { Integer observationId = Integer.valueOf(request.getParameter("observationId")); - Observation observation = em.find(Observation.class, observationId); + Observation observation = SessionControllerGetter.getObservationBean().getObservation(observationId);//em.find(Observation.class, observationId); request.setAttribute("observation", observation); + //System.out.println(observation.getGeoinfo()); + request.setAttribute("defaultMapCenter",user.getOrganizationId().getDefaultMapCenter()); + request.setAttribute("defaultMapZoom", user.getOrganizationId().getDefaultMapZoom()); List<Organism> allOrganisms = em.createNamedQuery("Organism.findAll").getResultList(); request.setAttribute("allOrganisms", allOrganisms); // Hierarchy categories @@ -123,7 +126,7 @@ public class ObservationController extends HttpServlet { } catch(NullPointerException | NumberFormatException ex) { - response.sendError(500, ExceptionUtil.getStackTrace(ex)); + response.sendError(500, ex.getMessage() + ": " + ExceptionUtil.getStackTrace(ex)); } } else @@ -151,7 +154,9 @@ public class ObservationController extends HttpServlet { observation.setTimeOfObservation(formValidation.getFormField("timeOfObservation").getValueAsTimestamp()); observation.setObservedValue(formValidation.getFormField("observedValue").getValueAsDouble()); observation.setUserId(user.getUserId()); - observation.setLocation(formValidation.getFormField("location").getValueAsPointWGS84()); + //observation.setLocation(formValidation.getFormField("location").getValueAsPointWGS84()); + //System.out.println(formValidation.getFormField("geoInfo").getWebValue()); + observation.setGeoinfo(formValidation.getFormField("geoInfo").getWebValue()); observation = SessionControllerGetter.getObservationBean().storeObservation(observation); // Redirect to form 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 05a157f78233dfdfd0dffee4db07c817eb856268..b03d18b7aa87e1a05f52f3ebf5aa7f6bcc789900 100644 --- a/src/main/java/no/nibio/vips/logic/controller/session/ObservationBean.java +++ b/src/main/java/no/nibio/vips/logic/controller/session/ObservationBean.java @@ -19,16 +19,20 @@ package no.nibio.vips.logic.controller.session; +import com.vividsolutions.jts.geom.Geometry; +import java.util.ArrayList; import java.util.List; import javax.ejb.Stateless; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; +import javax.persistence.Query; +import no.nibio.vips.logic.entity.Gis; import no.nibio.vips.logic.entity.Observation; import no.nibio.vips.logic.entity.Organization; import no.nibio.vips.logic.entity.VipsLogicUser; /** - * @copyright 2014 <a href="http://www.nibio.no/">NIBIO</a> + * @copyright 2014-2015 <a href="http://www.nibio.no/">NIBIO</a> * @author Tor-Einar Skog <tor-einar.skog@nibio.no> */ @Stateless @@ -43,11 +47,93 @@ public class ObservationBean { .setParameter("organizationId", organization) .getResultList(); - return em.createNamedQuery("Observation.findAll").getResultList(); + List<Observation> retVal = this.getObservationsWithGeoInfo(em.createNamedQuery("Observation.findAll").getResultList()); + + return retVal; + } + + public Observation getObservation(Integer observationId) + { + Observation retVal = em.find(Observation.class, observationId); + if(retVal != null) + { + retVal.setGeoinfos(this.getGeoinfoForObservation(retVal)); + } + return retVal; + } + + public List<Gis> getGeoinfoForObservation(Observation obs) + { + List<Integer> gisIds = em.createNativeQuery("SELECT gis_id FROM public.gis_observation WHERE observation_id = :observationId") + .setParameter("observationId", obs.getObservationId()) + .getResultList(); + + if(gisIds != null && ! gisIds.isEmpty()) + { + return em.createNamedQuery("Gis.findByGisIds",Gis.class) + .setParameter("gisIds", gisIds) + .getResultList(); + } + else + { + return null; + } + } + + public List<Observation> getObservationsWithGeoInfo(List<Observation> observations) + { + Query q1 = em.createNamedQuery("Gis.findByGisIds",Gis.class); + + Query q2 = em.createNativeQuery("SELECT gis_id FROM public.gis_observation WHERE observation_id = :observationId"); + + for(Observation obs:observations) + { + List<Integer> gisIds = q2.setParameter("observationId", obs.getObservationId()).getResultList(); + if(!gisIds.isEmpty()) + { + obs.setGeoinfos(q1.setParameter("gisIds", gisIds).getResultList()); + } + // + } + return observations; } public Observation storeObservation(Observation observation) { - return em.merge(observation); + Observation retVal = em.merge(observation); + // Store geoinfo + // First delete all old observations + em.createNativeQuery("DELETE FROM public.gis where gis_id IN (SELECT gis_id FROM public.gis_observation WHERE observation_id=:observationId)") + .setParameter("observationId", observation.getObservationId()) + .executeUpdate(); + em.createNativeQuery("DELETE FROM public.gis_observation WHERE observation_id=:observationId") + .setParameter("observationId", observation.getObservationId()) + .executeUpdate(); + // Then persist the new ones + if(observation.getGeoinfos() != null && ! observation.getGeoinfos().isEmpty()) + { + for(Gis gis : observation.getGeoinfos()) + { + em.persist(gis); + } + + Query q = em.createNativeQuery("INSERT INTO public.gis_observation(gis_id,observation_id) VALUES(:gisId,:observationId)") + .setParameter("observationId", observation.getObservationId()); + for(Gis gis:observation.getGeoinfos()) + { + q.setParameter("gisId", gis.getGisId()) + .executeUpdate(); + } + } + /* + for(Geometry geom:observation.getGeometries()) + { + Gis gis = new Gis(); + gis.setGisGeom(geom); + em.persist(gis); + gises.add(gis); + }*/ + + return retVal; } public void deleteObservation(Integer observationId) { diff --git a/src/main/java/no/nibio/vips/logic/entity/Gis.java b/src/main/java/no/nibio/vips/logic/entity/Gis.java new file mode 100644 index 0000000000000000000000000000000000000000..75a7079af75caa37c7a44a77b341ea65a9ef7229 --- /dev/null +++ b/src/main/java/no/nibio/vips/logic/entity/Gis.java @@ -0,0 +1,110 @@ +/* + * 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.entity; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.vividsolutions.jts.geom.Geometry; +import java.io.Serializable; +import javax.persistence.Basic; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.NamedQueries; +import javax.persistence.NamedQuery; +import javax.persistence.Table; +import javax.xml.bind.annotation.XmlRootElement; +import org.hibernate.annotations.Type; + +/** + * @copyright 2015 <a href="http://www.nibio.no/">NIBIO</a> + * @author Tor-Einar Skog <tor-einar.skog@nibio.no> + */ +@Entity +@Table(name = "gis") +@XmlRootElement +@NamedQueries({ + @NamedQuery(name = "Gis.findAll", query = "SELECT g FROM Gis g"), + @NamedQuery(name = "Gis.findByGisId", query = "SELECT g FROM Gis g WHERE g.gisId = :gisId"), + @NamedQuery(name = "Gis.findByGisIds", query = "SELECT g FROM Gis g WHERE g.gisId IN(:gisIds)") +}) +public class Gis implements Serializable { + private static final long serialVersionUID = 1L; + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Basic(optional = false) + @Column(name = "gis_id") + private Integer gisId; + @JsonIgnore + @Type(type = "org.hibernate.spatial.GeometryType") + @Column(name = "gis_geom", columnDefinition = "Geometry") + private Geometry gisGeom; + + public Gis() { + } + + public Gis(Integer gisId) { + this.gisId = gisId; + } + + public Integer getGisId() { + return gisId; + } + + public void setGisId(Integer gisId) { + this.gisId = gisId; + } + + + public Geometry getGisGeom() { + return gisGeom; + } + + public void setGisGeom(Geometry gisGeom) { + this.gisGeom = gisGeom; + } + + @Override + public int hashCode() { + int hash = 0; + hash += (gisId != null ? gisId.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 Gis)) { + return false; + } + Gis other = (Gis) object; + if ((this.gisId == null && other.gisId != null) || (this.gisId != null && !this.gisId.equals(other.gisId))) { + return false; + } + return true; + } + + @Override + public String toString() { + return "no.nibio.vips.logic.entity.Gis[ gisId=" + gisId + " ]"; + } + +} diff --git a/src/main/java/no/nibio/vips/logic/entity/Observation.java b/src/main/java/no/nibio/vips/logic/entity/Observation.java index 6076869702e416f18c7ccdd512c0924f05aa643c..a83286c4cd4db00e9f8768f6deeb98c605975308 100644 --- a/src/main/java/no/nibio/vips/logic/entity/Observation.java +++ b/src/main/java/no/nibio/vips/logic/entity/Observation.java @@ -18,8 +18,6 @@ */ package no.nibio.vips.logic.entity; -import com.vividsolutions.jts.geom.Coordinate; -import com.vividsolutions.jts.geom.Point; import java.io.Serializable; import java.util.Date; import javax.persistence.Basic; @@ -39,7 +37,9 @@ import javax.persistence.Transient; import javax.validation.constraints.NotNull; import javax.xml.bind.annotation.XmlRootElement; import com.fasterxml.jackson.annotation.JsonIgnore; -import org.hibernate.annotations.Type; +import com.vividsolutions.jts.geom.Geometry; +import java.util.List; +import no.nibio.vips.logic.util.GISUtil; /** * @copyright 2014 <a href="http://www.nibio.no/">NIBIO</a> @@ -63,12 +63,16 @@ public class Observation implements Serializable, no.nibio.vips.observation.Obse private Date timeOfObservation; private Organism organism; private Integer userId; - private Point location; + //private Point location; + private List<Gis> geoinfo; private Double observedValue; private Integer denominator; private ObservationMethod observationMethodId; + + private GISUtil GISUtil; public Observation() { + this.GISUtil = new GISUtil(); } public Observation(Integer observationId) { @@ -97,6 +101,7 @@ public class Observation implements Serializable, no.nibio.vips.observation.Obse @Basic(optional = false) @Column(name = "time_of_observation") @Temporal(TemporalType.TIMESTAMP) + @Override public Date getTimeOfObservation() { return timeOfObservation; } @@ -105,8 +110,9 @@ public class Observation implements Serializable, no.nibio.vips.observation.Obse this.timeOfObservation = timeOfObservation; } + // Using PostGIS + Hibernate-spatial + Java Topology Suite to make this work - @JsonIgnore + /*@JsonIgnore @Type(type = "org.hibernate.spatial.GeometryType") @Column(name = "location", columnDefinition = "Geometry") public Point getLocation() { @@ -115,17 +121,43 @@ public class Observation implements Serializable, no.nibio.vips.observation.Obse public void setLocation(Point location) { this.location = location; + }*/ + + public void setGeoinfos(List<Gis> geoinfo) + { + this.geoinfo = geoinfo; + } + + @JsonIgnore + @Transient + public List<Gis> getGeoinfos() + { + return this.geoinfo; + } + + + public void setGeoinfo(String json) + { + this.setGeoinfos(this.GISUtil.getGisFromGeoJSON(json)); } @Transient + @Override + public String getGeoinfo() + { + return this.GISUtil.getGeoJSONFromGis(this.geoinfo); + } + + /*@Transient public Coordinate getLocationCoordinate() { return this.location.getCoordinate(); - } + }*/ @NotNull @Basic(optional = false) @Column(name = "observed_value") + @Override public Double getObservedValue() { return observedValue; } @@ -135,6 +167,7 @@ public class Observation implements Serializable, no.nibio.vips.observation.Obse } @Column(name = "denominator") + @Override public Integer getDenominator() { return denominator; } @@ -215,6 +248,7 @@ public class Observation implements Serializable, no.nibio.vips.observation.Obse this.organism = organism; } + /* @Override @Transient public Double getLatitude() { @@ -226,7 +260,8 @@ public class Observation implements Serializable, no.nibio.vips.observation.Obse public Double getLongitude() { return this.getLocationCoordinate().x; } - + */ + @Override @Transient public String getName() { diff --git a/src/main/java/no/nibio/vips/logic/util/GISUtil.java b/src/main/java/no/nibio/vips/logic/util/GISUtil.java new file mode 100644 index 0000000000000000000000000000000000000000..138ae03b8730a8c67d2139d7cb471e9f071a0fb0 --- /dev/null +++ b/src/main/java/no/nibio/vips/logic/util/GISUtil.java @@ -0,0 +1,110 @@ +/* + * 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.util; + +import com.vividsolutions.jts.geom.Geometry; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import no.nibio.vips.logic.entity.Gis; +import org.wololo.geojson.Feature; +import org.wololo.geojson.FeatureCollection; +import org.wololo.geojson.GeoJSONFactory; +import org.wololo.jts2geojson.GeoJSONReader; +import org.wololo.jts2geojson.GeoJSONWriter; + +/** + * @copyright 2015 <a href="http://www.nibio.no/">NIBIO</a> + * @author Tor-Einar Skog <tor-einar.skog@nibio.no> + */ +public class GISUtil { + + /** + * Parses all features from GeoJSON string, returns as JTS Geometry objects + * @param json + * @return + */ + public List<Geometry> getGeometriesFromGeoJSON(String json) + { + FeatureCollection featureCollection = (FeatureCollection) GeoJSONFactory.create(json); + GeoJSONReader reader = new GeoJSONReader(); + List<Geometry> retVal = new ArrayList<>(); + for(Feature feature: featureCollection.getFeatures()) + { + retVal.add(reader.read(feature.getGeometry())); + } + return retVal; + } + + public List<Gis> getGisFromGeoJSON(String json) + { + GeoJSONReader reader = new GeoJSONReader(); + List<Gis> retVal = new ArrayList<>(); + try + { + FeatureCollection featureCollection = (FeatureCollection) GeoJSONFactory.create(json); + for(Feature feature: featureCollection.getFeatures()) + { + Gis gis = new Gis(); + gis.setGisGeom(reader.read(feature.getGeometry())); + gis.getGisGeom().setSRID(4326); + retVal.add(gis); + } + }catch(NullPointerException ex) {}; + return retVal; + } + + public String getGeoJSONFromGis(List<Gis> geoinfo) + { + if(geoinfo == null || geoinfo.isEmpty()) + { + return ""; + } + List<Feature> features = new ArrayList<>(); + Map<String, Object> properties = new HashMap<>(); + GeoJSONWriter writer = new GeoJSONWriter(); + for(Gis gis:geoinfo) + { + features.add(new Feature(writer.write(gis.getGisGeom()), properties)); + } + + FeatureCollection json = writer.write(features); + return json.toString(); + } + + public String getGeoJSONFromGeometries(List<Geometry> geometries) + { + if(geometries == null || geometries.isEmpty()) + { + return ""; + } + List<Feature> features = new ArrayList<>(); + Map<String, Object> properties = new HashMap<>(); + GeoJSONWriter writer = new GeoJSONWriter(); + for(Geometry geometry:geometries) + { + features.add(new Feature(writer.write(geometry), properties)); + } + + FeatureCollection json = writer.write(features); + return json.toString(); + } +} diff --git a/src/main/java/no/nibio/web/forms/FormField.java b/src/main/java/no/nibio/web/forms/FormField.java index e5346d840ee88bf801660787a8596a8d54e710e7..888ceb1a6e361ac0c747e01e64f23a683421f22d 100644 --- a/src/main/java/no/nibio/web/forms/FormField.java +++ b/src/main/java/no/nibio/web/forms/FormField.java @@ -51,6 +51,7 @@ public class FormField { public final static String DATA_TYPE_PASSWORD = "PASSWORD"; public final static String DATA_TYPE_EMAIL = "EMAIL"; public final static String DATA_TYPE_POINT_WGS84 = "POINT_WGS84"; + public final static String DATA_TYPE_GEOJSON = "GEOJSON"; public final static String FIELD_TYPE_INPUT = "INPUT"; public final static String FIELD_TYPE_HIDDEN = "HIDDEN"; diff --git a/src/main/java/no/nibio/web/forms/FormValidator.java b/src/main/java/no/nibio/web/forms/FormValidator.java index 0e141352f3d3583a324642f13f91d085f119f626..508ab252119453c9b4c5910b118fc8e4e56291a1 100644 --- a/src/main/java/no/nibio/web/forms/FormValidator.java +++ b/src/main/java/no/nibio/web/forms/FormValidator.java @@ -318,6 +318,15 @@ public class FormValidator { } } } + // GEOJSON + if(field.getDataType().equals(FormField.DATA_TYPE_GEOJSON)) + { + if((field.getWebValue().equals("{}") || field.getWebValue().equals(field.getNullValue())) && field.isRequired()) + { + field.setValid(false); + field.setValidationMessage(resourceBundle.getString("fieldIsRequired")); + } + } // SELECT FIELDS // SINGLE SELECT diff --git a/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts.properties b/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts.properties index 0d69b3a6c9b1230dadc50963bab808f2fe74d324..3e5c09e293b8c3bf098743ccee15f2f347418d7d 100644 --- a/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts.properties +++ b/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts.properties @@ -286,3 +286,4 @@ allCrops=All crops rememberLogin=Remember login task_DeleteAllExpiredUserUuidsTask_name=Delete all expired user UUIDs task_DeleteAllExpiredUserUuidsTask_description=Cleaning up the database of UUIDs, which is used for remembering client logins +mapDataIsRequired=Map data is required diff --git a/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_bs.properties b/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_bs.properties index ddf8b72285bc61538ed5c5f91a6b8393b951a770..7ae7ba080e9570038d97c8c612dd54b33f7e1481 100644 --- a/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_bs.properties +++ b/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_bs.properties @@ -286,3 +286,4 @@ allCrops=All crops rememberLogin=Remember login task_DeleteAllExpiredUserUuidsTask_name=Delete all expired user UUIDs task_DeleteAllExpiredUserUuidsTask_description=Cleaning up the database of UUIDs, which is used for remembering client logins +mapDataIsRequired=Map data is required diff --git a/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_hr.properties b/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_hr.properties index 5c746b23b42375288a890e2219dd9552fa4b77d1..8b56087dd472d1b317cc8e47fbe30eb7694b663f 100644 --- a/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_hr.properties +++ b/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_hr.properties @@ -285,3 +285,4 @@ allCrops=All crops rememberLogin=Remember login task_DeleteAllExpiredUserUuidsTask_name=Delete all expired user UUIDs task_DeleteAllExpiredUserUuidsTask_description=Cleaning up the database of UUIDs, which is used for remembering client logins +mapDataIsRequired=Map data is required diff --git a/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_nb.properties b/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_nb.properties index 82ee56fd99c812a83b261fd4d89d4394548af80d..13b3239f3a9ae4c6163d8c9dc66c1a088c39eeaa 100644 --- a/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_nb.properties +++ b/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_nb.properties @@ -286,3 +286,4 @@ allCrops=Alle kulturer rememberLogin=Husk innlogging task_DeleteAllExpiredUserUuidsTask_name=Slett alle utdaterte brukerUUIDer task_DeleteAllExpiredUserUuidsTask_description=Rydder opp i tabellen over brukerUUIDer, som brukes til \u00e5 huske klientinnlogginger +mapDataIsRequired=Kartdata m\u00e5 registreres diff --git a/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_sr.properties b/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_sr.properties index c72dcabeb50b7a601dac735c960bda8d5ff60b7a..9d905c280aacdc8a8f4acb1ceff760c8a3408e81 100644 --- a/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_sr.properties +++ b/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_sr.properties @@ -286,3 +286,4 @@ allCrops=All crops rememberLogin=Remember login task_DeleteAllExpiredUserUuidsTask_name=Delete all expired user UUIDs task_DeleteAllExpiredUserUuidsTask_description=Cleaning up the database of UUIDs, which is used for remembering client logins +mapDataIsRequired=Map data is required diff --git a/src/main/webapp/formdefinitions/observationForm.json b/src/main/webapp/formdefinitions/observationForm.json index 2050ff30a1974c848db3031361af907522ce444b..ba490d9baed297a79c40710829df4747ef73621e 100644 --- a/src/main/webapp/formdefinitions/observationForm.json +++ b/src/main/webapp/formdefinitions/observationForm.json @@ -38,9 +38,11 @@ "required" : true }, { - "name" : "location", - "dataType" : "POINT_WGS84", - "required" : true + "name": "geoInfo", + "dataType" : "GEOJSON", + "fieldType" : "HIDDEN", + "required" : true, + "nullValue" : "{\"type\":\"FeatureCollection\",\"features\":[]}" }, { "name" : "observedValue", diff --git a/src/main/webapp/js/observationFormMap.js b/src/main/webapp/js/observationFormMap.js index 9205aed0f502906e35fd7e34f7f5271f854bd6ab..bb269380137f7bf283fac32d1f9e3cb4686672aa 100755 --- a/src/main/webapp/js/observationFormMap.js +++ b/src/main/webapp/js/observationFormMap.js @@ -21,14 +21,16 @@ * Logic for registering location of an observation using an OpenLayers v3 map */ +var featureOverlay; + /** * * @param {ol.Coordinate} center - coordinates for the map's center (WGS84) * @param {int} zoomLevel - the zoom level (1-15, 1 is world wide view, 15 is greatest zoom) * @param {boolean} displayMarker - show observation marker in center location + * @param {string} drawnObjs - GeoJSON with geometries to display * @returns {void} */ - function initMap(center, zoomLevel, displayMarker, drawnObjs) { @@ -55,16 +57,6 @@ function initMap(center, zoomLevel, displayMarker, drawnObjs) { }) }); - - - - - - - - - - var fylke_layer = new ol.layer.Vector({ title: 'Fylkesgrenser', type: 'overlay', @@ -153,7 +145,7 @@ function initMap(center, zoomLevel, displayMarker, drawnObjs) { // This collection is passed to the modify and also the draw // interaction, so that both can add or modify features. var features = new ol.Collection(); - var featureOverlay = new ol.layer.Vector({ + featureOverlay = new ol.layer.Vector({ source: new ol.source.Vector({ features: features }), @@ -348,4 +340,23 @@ function initMap(center, zoomLevel, displayMarker, drawnObjs) { }, 500, function() {}); }); } +} + +function getFeatures() +{ + var features = featureOverlay.getSource().getFeatures(); + // create an object to write features on a output KML file + + //var desimalDegrees = features[0].getGeometry().clone().transform('EPSG:4326','EPSG:3857').getCoordinates(); +//debugger; + + var format = new ol.format.GeoJSON(); + // write features to GeoJSON format using projection EPSG:4326 + + var result = format.writeFeatures(features, { + dataProjection: 'EPSG:4326', + featureProjection: 'EPSG:3857' + }); + + return result; } \ No newline at end of file diff --git a/src/main/webapp/js/util.js b/src/main/webapp/js/util.js new file mode 100644 index 0000000000000000000000000000000000000000..f86837a8bc52d5e8abece528208ac616782a3086 --- /dev/null +++ b/src/main/webapp/js/util.js @@ -0,0 +1,23 @@ +/* + * 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/>. + * + */ + +function setFieldValue(theForm, fieldName, value) +{ + theForm[fieldName] = value; +} diff --git a/src/main/webapp/js/validateForm.js b/src/main/webapp/js/validateForm.js index 64d59eaadb993295c2432dacc51795669375cea1..a9315c10d91c48ddd2e3507c2016df0ab38cd23a 100644 --- a/src/main/webapp/js/validateForm.js +++ b/src/main/webapp/js/validateForm.js @@ -31,9 +31,8 @@ var dataTypes = { TYPE_TIMESTAMP: "TIMESTAMP", TYPE_PASSWORD: "PASSWORD", TYPE_EMAIL: "EMAIL", - TYPE_POINT_WGS84: "POINT_WGS84" - - + TYPE_POINT_WGS84: "POINT_WGS84", + TYPE_GEOJSON: "GEOJSON" }; var fieldTypes= { @@ -420,6 +419,17 @@ function validateFieldActual(fieldEl, theForm, formDefinitionKey) validizeField(fieldEl, theForm); return true; } + if(fieldDefinition.dataType === dataTypes.TYPE_GEOJSON) + { + // For now: Check that it's valid JSON?? + if((webValue === "{}" || webValue === fieldDefinition.nullValue) && fieldDefinition.required === true) + { + alert(getI18nMsg("mapDataIsRequired")); + return false; + } + validizeField(fieldEl, theForm); + return true; + } // Passwords are evaluated in evaluatePassword if(fieldDefinition.dataType === dataTypes.TYPE_PASSWORD) @@ -511,6 +521,7 @@ function invalidizeField(inputEl, theForm, message) // Make sure we don't try to manipulate hidden fields if(inputEl.type === "hidden") { + alert(message); return; } styleInvalid(theForm[inputEl.name]); diff --git a/src/main/webapp/templates/observationForm.ftl b/src/main/webapp/templates/observationForm.ftl index b94734a90244d15ea9d0603dbde42d3134a514e5..f1cbe9a61b4492a03a00d01533b37428684912bb 100755 --- a/src/main/webapp/templates/observationForm.ftl +++ b/src/main/webapp/templates/observationForm.ftl @@ -55,7 +55,8 @@ <#if observation.location?has_content> initMap([${(observation.location.x?c)!""},${(observation.location.y?c)!""}],10,true); <#else> - initMap([${defaultMapCenter.x?c},${defaultMapCenter.y?c}],${defaultMapZoom},false); + var geoInfo = <#if observation.geoinfo?has_content>${observation.geoinfo}<#else>{}</#if>; + initMap([${defaultMapCenter.x?c},${defaultMapCenter.y?c}],${defaultMapZoom},false, geoInfo); </#if> }); </script> @@ -72,7 +73,8 @@ <div class="row"> <div class="col-md-6"> <#assign formId = "observationForm"> - <form id="${formId}" role="form" action="/observation?action=observationFormSubmit" method="POST" onsubmit="return validateForm(this);"> + <form id="${formId}" role="form" action="/observation?action=observationFormSubmit" method="POST" onsubmit="this['geoInfo'].value=getFeatures();return validateForm(this);"> + <input type="hidden" name="geoInfo" value=""/> <input type="hidden" name="observationId" value="${observation.observationId!"-1"}"/> <div class="form-group"> <label for="organismId">${i18nBundle.organism}</label> @@ -86,11 +88,11 @@ </select> <span class="help-block" id="${formId}_organismId_validation"></span> </div> - <div class="form-group"> + <!--div class="form-group"> <label for="location">${i18nBundle.location}</label> <input type="text" class="form-control" readonly="readonly" id="location" name="location" placeholder="${i18nBundle.location}" value="${(observation.location.x?c)!""},${(observation.location.y?c)!""}" onblur="validateField(this);" /> <span class="help-block" id="${formId}_location_validation"></span> - </div> + </div--> <div class="form-group"> <label for="timeOfObservation">${i18nBundle.timeOfObservation}</label> <input type="datetime" class="form-control" name="timeOfObservation" placeholder="${i18nBundle.timeOfObservation}" value="${(observation.timeOfObservation?string("yyyy-MM-dd HH:mmZ"))!.now?string("yyyy-MM-dd HH:mmZ")}" onblur="validateField(this);" /> diff --git a/src/test/java/no/nibio/vips/logic/entity/ObservationTest.java b/src/test/java/no/nibio/vips/logic/entity/ObservationTest.java new file mode 100644 index 0000000000000000000000000000000000000000..b3fc6529922d8eee10d3063045ce7a67ed928c08 --- /dev/null +++ b/src/test/java/no/nibio/vips/logic/entity/ObservationTest.java @@ -0,0 +1,84 @@ +/* + * 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.entity; + +import com.vividsolutions.jts.geom.Coordinate; +import com.vividsolutions.jts.geom.GeometryCollection; +import com.vividsolutions.jts.geom.Point; +import java.util.Date; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import static org.junit.Assert.*; + +/** + * + * @author treinar + */ +public class ObservationTest { + + public ObservationTest() { + } + + @BeforeClass + public static void setUpClass() { + } + + @AfterClass + public static void tearDownClass() { + } + + @Before + public void setUp() { + } + + @After + public void tearDown() { + } + + + /** + * Test of setGeoinfoFromJson method, of class Observation. + */ + //@Test + public void testSetGeoinfoFromJson() { + System.out.println("setGeoinfoFromJson"); + String geoinfo = "{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[[10.669097900390623,59.753628151319106],[10.612792968749998,59.70309199431276],[10.726776123046873,59.705863076677105],[10.669097900390623,59.753628151319106]]]},\"properties\":null},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[10.704803466796873,59.64831609639066]},\"properties\":null},{\"type\":\"Feature\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[10.649871826171871,59.67051458978321],[10.791320800781248,59.67328836837126]]},\"properties\":null}]}"; + Observation instance = new Observation(); + //instance.setGeoinfoFromJson(geoinfo); + //System.out.println(instance.getGeoinfoFromJson()); + } + + /** + * Test of getGeoinfoFromJson method, of class Observation. + */ + //@Test + public void testGetGeoinfoFromJson() { + System.out.println("getGeoinfoFromJson"); + Observation instance = new Observation(); + String expResult = ""; + //String result = instance.getGeoinfoFromJson(); + //assertEquals(expResult, result); + + } + + +} diff --git a/src/test/java/no/nibio/vips/logic/util/GISUtilTest.java b/src/test/java/no/nibio/vips/logic/util/GISUtilTest.java new file mode 100644 index 0000000000000000000000000000000000000000..9bd5c491fe948b90b23b88c285368bc96755512a --- /dev/null +++ b/src/test/java/no/nibio/vips/logic/util/GISUtilTest.java @@ -0,0 +1,71 @@ +/* + * 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.util; + +import com.vividsolutions.jts.geom.Geometry; +import java.util.List; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import static org.junit.Assert.*; + +/** + * + * @author treinar + */ +public class GISUtilTest { + + public GISUtilTest() { + } + + @BeforeClass + public static void setUpClass() { + } + + @AfterClass + public static void tearDownClass() { + } + + @Before + public void setUp() { + } + + @After + public void tearDown() { + } + + /** + * Test of getGeometriesFromGeoJSON method, of class GISUtil. + */ + @Test + public void testRoundtrip() { + System.out.println("testRoundtrip"); + String json = "{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[[10.669097900390623,59.753628151319106],[10.612792968749998,59.70309199431276],[10.726776123046873,59.705863076677105],[10.669097900390623,59.753628151319106]]]},\"properties\":{}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[10.704803466796873,59.64831609639066]},\"properties\":{}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[10.649871826171871,59.67051458978321],[10.791320800781248,59.67328836837126]]},\"properties\":{}}]}"; + GISUtil instance = new GISUtil(); + //List<Geometry> expResult = null; + List<Geometry> geometries = instance.getGeometriesFromGeoJSON(json); + String result = instance.getGeoJSONFromGeometries(geometries); + //System.out.println(result); + assertEquals(json, result); + } + + +}