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);
+    }
+
+    
+}