diff --git a/pom.xml b/pom.xml
index 3abb26e8dc5643119967911f6a6d4fbe178c598a..7d84f0a2a0914c5480df14ac8a21822a30cb9965 100644
--- a/pom.xml
+++ b/pom.xml
@@ -19,18 +19,15 @@
     <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>
+      <dependency>
         <groupId>com.github.bjornharrtell</groupId>
+          <!--groupId>org.wololo</groupId-->
         <artifactId>jts2geojson</artifactId>
-        <version>0.6.0</version>
-    </dependency-->
+        <version>0.9.0</version>
+    </dependency>
       <dependency>
                 <groupId>org.hibernate</groupId>
                 <artifactId>hibernate-spatial</artifactId>
@@ -111,19 +108,19 @@
     <dependency>
       <groupId>com.fasterxml.jackson.core</groupId>
       <artifactId>jackson-annotations</artifactId>
-      <version>2.4.1</version>
+      <version>2.7.4</version>
       <scope>provided</scope>
     </dependency>
     <dependency>
       <groupId>com.fasterxml.jackson.core</groupId>
       <artifactId>jackson-core</artifactId>
-      <version>2.4.1</version>
+      <version>2.7.4</version>
       <scope>provided</scope>
     </dependency>
     <dependency>
       <groupId>com.fasterxml.jackson.core</groupId>
       <artifactId>jackson-databind</artifactId>
-      <version>2.4.1</version>
+      <version>2.7.4</version>
     </dependency>
     <dependency>
       <groupId>log4j</groupId>
@@ -148,11 +145,6 @@
       <version>9.4.1211</version>
       <scope>provided</scope>
     </dependency>
-    <dependency>
-      <groupId>org.wololo</groupId>
-      <artifactId>jts2geojson</artifactId>
-      <version>0.8.0</version>
-    </dependency>
     <dependency>
       <groupId>javax</groupId>
       <artifactId>javaee-web-api</artifactId>
@@ -214,8 +206,8 @@
         <artifactId>maven-compiler-plugin</artifactId>
         <version>2.0.2</version>
         <configuration>
-          <source>1.7</source>
-          <target>1.7</target>
+          <source>1.8</source>
+          <target>1.8</target>
         </configuration>
       </plugin>
     </plugins>
diff --git a/src/main/java/no/nibio/vips/logic/VIPSLogicApplication.java b/src/main/java/no/nibio/vips/logic/VIPSLogicApplication.java
index 24176ef0d6f804807c66f19479c850c63146c883..a7b6950b2bab922a2e4f79d393ff5b4548dee8e8 100644
--- a/src/main/java/no/nibio/vips/logic/VIPSLogicApplication.java
+++ b/src/main/java/no/nibio/vips/logic/VIPSLogicApplication.java
@@ -52,6 +52,7 @@ public class VIPSLogicApplication extends Application
         resources.add(no.nibio.vips.observationdata.ObservationDataService.class);
         resources.add(no.nibio.vips.logic.messaging.sms.SMSHandlingService.class);
         resources.add(no.nibio.vips.logic.modules.applefruitmoth.AppleFruitMothService.class);
+        resources.add(no.nibio.vips.logic.service.ObservationService.class);
         //resources.add(no.nibio.vips.logic.service.JacksonConfig.class);
         //resources.add(no.nibio.vips.coremanager.service.ManagerResourceImpl.class);
     }
@@ -62,15 +63,14 @@ public class VIPSLogicApplication extends Application
      * given list with all resources defined in the project.
      */
     private void addRestResourceClasses(Set<Class<?>> resources) {
-        
         resources.add(no.nibio.vips.logic.messaging.sms.SMSHandlingService.class);
         resources.add(no.nibio.vips.logic.modules.applefruitmoth.AppleFruitMothService.class);
         resources.add(no.nibio.vips.logic.modules.barleynetblotch.BarleyNetBlotchModelService.class);
         resources.add(no.nibio.vips.logic.modules.roughage.RoughageService.class);
         resources.add(no.nibio.vips.logic.service.JacksonConfig.class);
         resources.add(no.nibio.vips.logic.service.LogicService.class);
+        resources.add(no.nibio.vips.logic.service.ObservationService.class);
         resources.add(no.nibio.vips.logic.service.VIPSMobileService.class);
         resources.add(no.nibio.vips.observationdata.ObservationDataService.class);
-        
     }
 }
\ No newline at end of file
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 d99ae3bea75600acd8c0ece4392df3aece2d9f0d..792de65897f57f8f435fa76c44a0eb838c43ea66 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
@@ -19,11 +19,17 @@
 
 package no.nibio.vips.logic.controller.servlet;
 
+import com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.geom.GeometryFactory;
+import com.vividsolutions.jts.geom.Point;
 import java.io.File;
 import java.io.IOException;
+import java.util.Calendar;
 import java.util.Collections;
+import java.util.Date;
 import java.util.List;
 import java.util.Map;
+import java.util.TimeZone;
 import javax.persistence.EntityManager;
 import javax.persistence.PersistenceContext;
 import javax.servlet.ServletContext;
@@ -35,6 +41,8 @@ import no.nibio.vips.logic.entity.CropCategory;
 import no.nibio.vips.logic.entity.Observation;
 import no.nibio.vips.logic.entity.ObservationMethod;
 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.VipsLogicRole;
 import no.nibio.vips.logic.entity.VipsLogicUser;
 import no.nibio.vips.logic.i18n.SessionLocaleUtil;
@@ -72,308 +80,396 @@ public class ObservationController extends HttpServlet {
         VipsLogicUser user = (VipsLogicUser) request.getSession().getAttribute("user");
         // Default: View message list
         // for everyone
-        if(action == null)
+        if(request.getServletPath().endsWith("/observation"))
         {
-            List<Observation> observations = null;
-            Integer statusTypeId = -1; 
-            Integer pestOrganismId = -1;
-            try 
+            if(action == null)
             {
-                statusTypeId = request.getParameter("statusTypeId") != null ? Integer.valueOf(request.getParameter("statusTypeId")) : -1;
-                pestOrganismId = request.getParameter("pestOrganismId") != null ? Integer.valueOf(request.getParameter("pestOrganismId")) : -1;
-            }
-            catch(NumberFormatException nfe) {
-            }
-            
-            
-            if(statusTypeId > 0)
-            {
-                observations = SessionControllerGetter.getObservationBean().getObservations(user.getOrganizationId().getOrganizationId(), statusTypeId);
-            }
-            else if(pestOrganismId > 0)
-            {
-                observations = SessionControllerGetter.getObservationBean().getObservationsOfPest(pestOrganismId);
-            }
-            else
-            {
-                observations = SessionControllerGetter.getObservationBean().getObservations(user.getOrganizationId().getOrganizationId());
+                List<Observation> observations = null;
+                Integer statusTypeId = -1; 
+                Integer pestOrganismId = -1;
+                try 
+                {
+                    statusTypeId = request.getParameter("statusTypeId") != null ? Integer.valueOf(request.getParameter("statusTypeId")) : -1;
+                    pestOrganismId = request.getParameter("pestOrganismId") != null ? Integer.valueOf(request.getParameter("pestOrganismId")) : -1;
+                }
+                catch(NumberFormatException nfe) {
+                }
+
+
+                if(statusTypeId > 0)
+                {
+                    observations = SessionControllerGetter.getObservationBean().getObservations(user.getOrganizationId().getOrganizationId(), statusTypeId);
+                }
+                else if(pestOrganismId > 0)
+                {
+                    observations = SessionControllerGetter.getObservationBean().getObservationsOfPest(pestOrganismId);
+                }
+                else
+                {
+                    observations = SessionControllerGetter.getObservationBean().getObservations(user.getOrganizationId().getOrganizationId());
+                }
+
+                Collections.sort(observations);
+                Collections.reverse(observations);
+                List<Organism> allPests = em.createNamedQuery("Organism.findAllPests").getResultList();
+                request.setAttribute("allPests", SessionControllerGetter.getOrganismBean().sortOrganismsByLocalName(allPests, SessionLocaleUtil.getCurrentLocale(request).getLanguage()));
+                request.setAttribute("hierarchyCategories", SessionControllerGetter.getOrganismBean().getHierarchyCategoryNames(SessionLocaleUtil.getCurrentLocale(request)));
+                request.setAttribute("selectedPestOrganismId", pestOrganismId);
+                request.setAttribute("selectedStatusTypeId", statusTypeId);
+                request.setAttribute("observations", observations);
+                request.setAttribute("userHasObserverPrivilege", SessionControllerGetter.getUserBean().authorizeUser(user, VipsLogicRole.OBSERVER, VipsLogicRole.ORGANIZATION_ADMINISTRATOR, VipsLogicRole.SUPERUSER));
+                request.setAttribute("userIsObservationAuthority", SessionControllerGetter.getUserBean().authorizeUser(user, VipsLogicRole.OBSERVATION_AUTHORITY, VipsLogicRole.ORGANIZATION_ADMINISTRATOR, VipsLogicRole.SUPERUSER));
+                // If this is a redirect from a controller, with a message to be passed on
+                request.setAttribute("messageKey", request.getParameter("messageKey"));
+                request.getRequestDispatcher("/observationList.ftl").forward(request, response);
             }
-            
-            Collections.sort(observations);
-            Collections.reverse(observations);
-            List<Organism> allPests = em.createNamedQuery("Organism.findAllPests").getResultList();
-            request.setAttribute("allPests", SessionControllerGetter.getOrganismBean().sortOrganismsByLocalName(allPests, SessionLocaleUtil.getCurrentLocale(request).getLanguage()));
-            request.setAttribute("hierarchyCategories", SessionControllerGetter.getOrganismBean().getHierarchyCategoryNames(SessionLocaleUtil.getCurrentLocale(request)));
-            request.setAttribute("selectedPestOrganismId", pestOrganismId);
-            request.setAttribute("selectedStatusTypeId", statusTypeId);
-            request.setAttribute("observations", observations);
-            request.setAttribute("userHasObserverPrivilege", SessionControllerGetter.getUserBean().authorizeUser(user, VipsLogicRole.OBSERVER, VipsLogicRole.ORGANIZATION_ADMINISTRATOR, VipsLogicRole.SUPERUSER));
-            request.setAttribute("userIsObservationAuthority", SessionControllerGetter.getUserBean().authorizeUser(user, VipsLogicRole.OBSERVATION_AUTHORITY, VipsLogicRole.ORGANIZATION_ADMINISTRATOR, VipsLogicRole.SUPERUSER));
-            // If this is a redirect from a controller, with a message to be passed on
-            request.setAttribute("messageKey", request.getParameter("messageKey"));
-            request.getRequestDispatcher("/observationList.ftl").forward(request, response);
-        }
-        // Create a new observation
-        // Authorization: ORGANIZATION ADMIN, OBSERVER or SUPERUSER
-        else if(action.equals("newObservationForm"))
-        {
-            if(SessionControllerGetter.getUserBean().authorizeUser(user, VipsLogicRole.OBSERVER, VipsLogicRole.OBSERVATION_AUTHORITY, VipsLogicRole.ORGANIZATION_ADMINISTRATOR, VipsLogicRole.SUPERUSER))
+            // Create a new observation
+            // Authorization: ORGANIZATION ADMIN, OBSERVER or SUPERUSER
+            else if(action.equals("newObservationForm"))
             {
-                try
+                if(SessionControllerGetter.getUserBean().authorizeUser(user, VipsLogicRole.OBSERVER, VipsLogicRole.OBSERVATION_AUTHORITY, VipsLogicRole.ORGANIZATION_ADMINISTRATOR, VipsLogicRole.SUPERUSER))
                 {
-                    Observation observation = new Observation();
-                    request.setAttribute("observation", observation);
-                    request.setAttribute("mapLayers", SessionControllerGetter.getUserBean().getMapLayerJSONForUser(user));
-                    request.setAttribute("defaultMapCenter",user.getOrganizationId().getDefaultMapCenter());
-                    request.setAttribute("defaultMapZoom", user.getOrganizationId().getDefaultMapZoom());
-                    request.setAttribute("locationPointOfInterests", SessionControllerGetter.getPointOfInterestBean().getRelevantPointOfInterestsForUser(user));
-                    List<Organism> allPests = em.createNamedQuery("Organism.findAllPests").getResultList();
-                    request.setAttribute("allPests", SessionControllerGetter.getOrganismBean().sortOrganismsByLocalName(allPests, SessionLocaleUtil.getCurrentLocale(request).getLanguage()));
-                    List<Organism> allCrops = em.createNamedQuery("Organism.findAllCrops").getResultList();
-                    request.setAttribute("allCrops", SessionControllerGetter.getOrganismBean().sortOrganismsByLocalName(allCrops, SessionLocaleUtil.getCurrentLocale(request).getLanguage()));
-                    // Hierarchy categories
-                    request.setAttribute("hierarchyCategories", SessionControllerGetter.getOrganismBean().getHierarchyCategoryNames(SessionLocaleUtil.getCurrentLocale(request)));
-                    request.setAttribute("observationMethods", em.createNamedQuery("ObservationMethod.findAll", ObservationMethod.class).getResultList());
-                    if(SessionControllerGetter.getUserBean().authorizeUser(user, VipsLogicRole.OBSERVATION_AUTHORITY, VipsLogicRole.ORGANIZATION_ADMINISTRATOR, VipsLogicRole.SUPERUSER))
+                    try
                     {
-                        request.setAttribute("statusTypeIds", em.createNamedQuery("ObservationStatusType.findAll").getResultList());
+                        Observation observation = new Observation();
+                        request.setAttribute("observation", observation);
+                        request.setAttribute("mapLayers", SessionControllerGetter.getUserBean().getMapLayerJSONForUser(user));
+                        request.setAttribute("defaultMapCenter",user.getOrganizationId().getDefaultMapCenter());
+                        request.setAttribute("defaultMapZoom", user.getOrganizationId().getDefaultMapZoom());
+                        request.setAttribute("locationPointOfInterests", SessionControllerGetter.getPointOfInterestBean().getRelevantPointOfInterestsForUser(user));
+                        List<Organism> allPests = em.createNamedQuery("Organism.findAllPests").getResultList();
+                        request.setAttribute("allPests", SessionControllerGetter.getOrganismBean().sortOrganismsByLocalName(allPests, SessionLocaleUtil.getCurrentLocale(request).getLanguage()));
+                        List<Organism> allCrops = em.createNamedQuery("Organism.findAllCrops").getResultList();
+                        request.setAttribute("allCrops", SessionControllerGetter.getOrganismBean().sortOrganismsByLocalName(allCrops, SessionLocaleUtil.getCurrentLocale(request).getLanguage()));
+                        // Hierarchy categories
+                        request.setAttribute("hierarchyCategories", SessionControllerGetter.getOrganismBean().getHierarchyCategoryNames(SessionLocaleUtil.getCurrentLocale(request)));
+                        request.setAttribute("observationMethods", em.createNamedQuery("ObservationMethod.findAll", ObservationMethod.class).getResultList());
+                        if(SessionControllerGetter.getUserBean().authorizeUser(user, VipsLogicRole.OBSERVATION_AUTHORITY, VipsLogicRole.ORGANIZATION_ADMINISTRATOR, VipsLogicRole.SUPERUSER))
+                        {
+                            request.setAttribute("statusTypeIds", em.createNamedQuery("ObservationStatusType.findAll").getResultList());
+                        }
+                        request.getRequestDispatcher("/observationForm.ftl").forward(request, response);
+                    }
+                    catch(NullPointerException | NumberFormatException ex)
+                    {
+                        response.sendError(500, ExceptionUtil.getStackTrace(ex));
                     }
-                    request.getRequestDispatcher("/observationForm.ftl").forward(request, response);
                 }
-                catch(NullPointerException | NumberFormatException ex)
+                else
                 {
-                    response.sendError(500, ExceptionUtil.getStackTrace(ex));
+                    response.sendError(403,"Access not authorized"); // HTTP Forbidden
                 }
             }
-            else
+            // Edit an existing observation
+            // Authorization: ORGANIZATION ADMIN, OBSERVER or SUPERUSER
+            else if(action.equals("editObservationForm"))
             {
-                response.sendError(403,"Access not authorized"); // HTTP Forbidden
-            }
-        }
-        // Edit an existing observation
-        // Authorization: ORGANIZATION ADMIN, OBSERVER or SUPERUSER
-        else if(action.equals("editObservationForm"))
-        {
-            if(SessionControllerGetter.getUserBean().authorizeUser(user, VipsLogicRole.OBSERVER, VipsLogicRole.ORGANIZATION_ADMINISTRATOR, VipsLogicRole.SUPERUSER))
-            {
-                try
+                if(SessionControllerGetter.getUserBean().authorizeUser(user, VipsLogicRole.OBSERVER, VipsLogicRole.ORGANIZATION_ADMINISTRATOR, VipsLogicRole.SUPERUSER))
                 {
-                    Integer observationId = Integer.valueOf(request.getParameter("observationId"));
-                    Observation observation = SessionControllerGetter.getObservationBean().getObservation(observationId);//em.find(Observation.class, observationId);
-                    request.setAttribute("observation", observation);
-                    request.setAttribute("mapLayers", SessionControllerGetter.getUserBean().getMapLayerJSONForUser(user));
-                    //System.out.println(observation.getGeoinfo());
-                    request.setAttribute("defaultMapCenter",user.getOrganizationId().getDefaultMapCenter());
-                    request.setAttribute("defaultMapZoom", user.getOrganizationId().getDefaultMapZoom());
-                    List<Organism> allPests = em.createNamedQuery("Organism.findAllPests").getResultList();
-                    request.setAttribute("allPests", SessionControllerGetter.getOrganismBean().sortOrganismsByLocalName(allPests, SessionLocaleUtil.getCurrentLocale(request).getLanguage()));
-                    List<Organism> allCrops = em.createNamedQuery("Organism.findAllCrops").getResultList();
-                    request.setAttribute("allCrops", SessionControllerGetter.getOrganismBean().sortOrganismsByLocalName(allCrops, SessionLocaleUtil.getCurrentLocale(request).getLanguage()));
-                    // Hierarchy categories
-                    request.setAttribute("hierarchyCategories", SessionControllerGetter.getOrganismBean().getHierarchyCategoryNames(SessionLocaleUtil.getCurrentLocale(request)));
-                    request.setAttribute("observationMethods", em.createNamedQuery("ObservationMethod.findAll", ObservationMethod.class).getResultList());
-                    if(SessionControllerGetter.getUserBean().authorizeUser(user, VipsLogicRole.OBSERVATION_AUTHORITY, VipsLogicRole.ORGANIZATION_ADMINISTRATOR, VipsLogicRole.SUPERUSER))
+                    try
                     {
-                        request.setAttribute("statusTypeIds", em.createNamedQuery("ObservationStatusType.findAll").getResultList());
+                        Integer observationId = Integer.valueOf(request.getParameter("observationId"));
+                        Observation observation = SessionControllerGetter.getObservationBean().getObservation(observationId);//em.find(Observation.class, observationId);
+                        request.setAttribute("observation", observation);
+                        request.setAttribute("mapLayers", SessionControllerGetter.getUserBean().getMapLayerJSONForUser(user));
+                        //System.out.println(observation.getGeoinfo());
+                        request.setAttribute("defaultMapCenter",user.getOrganizationId().getDefaultMapCenter());
+                        request.setAttribute("defaultMapZoom", user.getOrganizationId().getDefaultMapZoom());
+                        List<Organism> allPests = em.createNamedQuery("Organism.findAllPests").getResultList();
+                        request.setAttribute("allPests", SessionControllerGetter.getOrganismBean().sortOrganismsByLocalName(allPests, SessionLocaleUtil.getCurrentLocale(request).getLanguage()));
+                        List<Organism> allCrops = em.createNamedQuery("Organism.findAllCrops").getResultList();
+                        request.setAttribute("allCrops", SessionControllerGetter.getOrganismBean().sortOrganismsByLocalName(allCrops, SessionLocaleUtil.getCurrentLocale(request).getLanguage()));
+                        // Hierarchy categories
+                        request.setAttribute("hierarchyCategories", SessionControllerGetter.getOrganismBean().getHierarchyCategoryNames(SessionLocaleUtil.getCurrentLocale(request)));
+                        request.setAttribute("observationMethods", em.createNamedQuery("ObservationMethod.findAll", ObservationMethod.class).getResultList());
+                        if(SessionControllerGetter.getUserBean().authorizeUser(user, VipsLogicRole.OBSERVATION_AUTHORITY, VipsLogicRole.ORGANIZATION_ADMINISTRATOR, VipsLogicRole.SUPERUSER))
+                        {
+                            request.setAttribute("statusTypeIds", em.createNamedQuery("ObservationStatusType.findAll").getResultList());
+                        }
+                        if(request.getParameter("messageKey") != null)
+                        {
+                            request.setAttribute("messageKey",request.getParameter("messageKey"));
+                        }
+                        request.getRequestDispatcher("/observationForm.ftl").forward(request, response);
                     }
-                    if(request.getParameter("messageKey") != null)
+                    catch(NullPointerException | NumberFormatException ex)
                     {
-                        request.setAttribute("messageKey",request.getParameter("messageKey"));
+                        response.sendError(500, ex.getMessage() + ": " + ExceptionUtil.getStackTrace(ex));
                     }
-                    request.getRequestDispatcher("/observationForm.ftl").forward(request, response);
                 }
-                catch(NullPointerException | NumberFormatException ex)
+                else
                 {
-                    response.sendError(500, ex.getMessage() + ": " + ExceptionUtil.getStackTrace(ex));
+                    response.sendError(403,"Access not authorized"); // HTTP Forbidden
                 }
             }
-            else
-            {
-                response.sendError(403,"Access not authorized"); // HTTP Forbidden
-            }
-        }
-        // Store an observation
-        // Authorization: ORGANIZATION ADMIN, OBSERVER or SUPERUSER
-        else if(action.equals("observationFormSubmit"))
-        {
-            if(SessionControllerGetter.getUserBean().authorizeUser(user, VipsLogicRole.OBSERVER, VipsLogicRole.ORGANIZATION_ADMINISTRATOR, VipsLogicRole.SUPERUSER))
+            // Store an observation
+            // Authorization: ORGANIZATION ADMIN, OBSERVER or SUPERUSER
+            else if(action.equals("observationFormSubmit"))
             {
-                try
+                if(SessionControllerGetter.getUserBean().authorizeUser(user, VipsLogicRole.OBSERVER, VipsLogicRole.ORGANIZATION_ADMINISTRATOR, VipsLogicRole.SUPERUSER))
                 {
-                    Map<String,String[]> parameterMap;
-                    List<FileItem> items = null;
-                    if(ServletFileUpload.isMultipartContent(request))
+                    try
                     {
-                        // Create a factory for disk-based file items
-                        DiskFileItemFactory factory = new DiskFileItemFactory();
+                        Map<String,String[]> parameterMap;
+                        List<FileItem> items = null;
+                        if(ServletFileUpload.isMultipartContent(request))
+                        {
+                            // Create a factory for disk-based file items
+                            DiskFileItemFactory factory = new DiskFileItemFactory();
 
-                        // Configure a repository (to ensure a secure temp location is used)
-                        ServletContext servletContext = this.getServletConfig().getServletContext();
-                        File repository = (File) servletContext.getAttribute("javax.servlet.context.tempdir");
-                        factory.setRepository(repository);
+                            // Configure a repository (to ensure a secure temp location is used)
+                            ServletContext servletContext = this.getServletConfig().getServletContext();
+                            File repository = (File) servletContext.getAttribute("javax.servlet.context.tempdir");
+                            factory.setRepository(repository);
 
-                        // Create a new file upload handler
-                        ServletFileUpload upload = new ServletFileUpload(factory);
+                            // Create a new file upload handler
+                            ServletFileUpload upload = new ServletFileUpload(factory);
 
-                        // Parse the request
-                        items = upload.parseRequest(request);
-                        parameterMap = FormUtil.getParameterMap(items,"UTF-8");
-                    }
-                    else
-                    {
-                        parameterMap = request.getParameterMap();
-                    }
-                    FormValidation formValidation = FormValidator.validateForm("observationForm", parameterMap, SessionLocaleUtil.getI18nBundle(request), getServletContext());
-                    Integer observationId = formValidation.getFormField("observationId").getValueAsInteger();
-                    Observation observation = observationId > 0 ? em.find(Observation.class, observationId) : new Observation();
-                    
-                    
-                    
-                    if(formValidation.isValid())
-                    {
-                        // Storing observation
-                        // Only new observations can set the organism
-                        if(observationId <= 0 || user.isSuperUser() || user.isOrganizationAdmin())
+                            // Parse the request
+                            items = upload.parseRequest(request);
+                            parameterMap = FormUtil.getParameterMap(items,"UTF-8");
+                        }
+                        else
                         {
-                            observation.setOrganism(em.find(Organism.class, formValidation.getFormField("organismId").getValueAsInteger()));
-                            observation.setCropOrganism(em.find(Organism.class, formValidation.getFormField("cropOrganismId").getValueAsInteger()));
+                            parameterMap = request.getParameterMap();
                         }
-                        //observation.setDenominator(formValidation.getFormField("denominator").getValueAsInteger());
-                        //observation.setObservationMethodId(em.find(ObservationMethod.class, formValidation.getFormField("observationMethodId").getWebValue()));
-                        observation.setTimeOfObservation(formValidation.getFormField("timeOfObservation").getValueAsTimestamp());
-                        //observation.setObservedValue(formValidation.getFormField("observedValue").getValueAsDouble());
-                        observation.setUserId(user.getUserId());
-                        
-                        observation.setObservationHeading(formValidation.getFormField("observationHeading").getWebValue());
-                        observation.setObservationText(formValidation.getFormField("observationText").getWebValue());
-                        observation.setObservationData(formValidation.getFormField("observationData").getWebValue());
-                        observation.setIsQuantified(formValidation.getFormField("isQuantified").getWebValue() != null);
-                        observation.setBroadcastMessage(formValidation.getFormField("broadcastMessage").getWebValue() != null);
+                        FormValidation formValidation = FormValidator.validateForm("observationForm", parameterMap, SessionLocaleUtil.getI18nBundle(request), getServletContext());
+                        Integer observationId = formValidation.getFormField("observationId").getValueAsInteger();
+                        Observation observation = observationId > 0 ? em.find(Observation.class, observationId) : new Observation();
 
-                        boolean sendNotification = false;    
-                        // Storing approval status
-                        if(SessionControllerGetter.getUserBean().authorizeUser(user, VipsLogicRole.OBSERVATION_AUTHORITY, VipsLogicRole.ORGANIZATION_ADMINISTRATOR, VipsLogicRole.SUPERUSER))
+
+
+                        if(formValidation.isValid())
                         {
-                            Integer statusTypeId = formValidation.getFormField("statusTypeId").getValueAsInteger();
-                            if(
-                                    observation.getStatusTypeId() == null
-                                    || ! observation.getStatusTypeId().equals(statusTypeId)
-                                    )
+                            // Storing observation
+                            // Only new observations can set the organism
+                            if(observationId <= 0 || user.isSuperUser() || user.isOrganizationAdmin())
                             {
-                                observation.setStatusChangedByUserId(user.getUserId());
-                                observation.setStatusChangedTime(SystemTime.getSystemTime());
-                                // Status changed to approved, sending notifications
-                                if(statusTypeId.equals(Observation.STATUS_TYPE_ID_APPROVED) && observation.getBroadcastMessage())
+                                observation.setOrganism(em.find(Organism.class, formValidation.getFormField("organismId").getValueAsInteger()));
+                                observation.setCropOrganism(em.find(Organism.class, formValidation.getFormField("cropOrganismId").getValueAsInteger()));
+                            }
+                            //observation.setDenominator(formValidation.getFormField("denominator").getValueAsInteger());
+                            //observation.setObservationMethodId(em.find(ObservationMethod.class, formValidation.getFormField("observationMethodId").getWebValue()));
+                            observation.setTimeOfObservation(formValidation.getFormField("timeOfObservation").getValueAsTimestamp());
+                            //observation.setObservedValue(formValidation.getFormField("observedValue").getValueAsDouble());
+                            observation.setUserId(user.getUserId());
+
+                            observation.setObservationHeading(formValidation.getFormField("observationHeading").getWebValue());
+                            observation.setObservationText(formValidation.getFormField("observationText").getWebValue());
+                            observation.setObservationData(formValidation.getFormField("observationData").getWebValue());
+                            observation.setIsQuantified(formValidation.getFormField("isQuantified").getWebValue() != null);
+                            observation.setBroadcastMessage(formValidation.getFormField("broadcastMessage").getWebValue() != null);
+
+                            boolean sendNotification = false;    
+                            // Storing approval status
+                            if(SessionControllerGetter.getUserBean().authorizeUser(user, VipsLogicRole.OBSERVATION_AUTHORITY, VipsLogicRole.ORGANIZATION_ADMINISTRATOR, VipsLogicRole.SUPERUSER))
+                            {
+                                Integer statusTypeId = formValidation.getFormField("statusTypeId").getValueAsInteger();
+                                if(
+                                        observation.getStatusTypeId() == null
+                                        || ! observation.getStatusTypeId().equals(statusTypeId)
+                                        )
                                 {
-                                    sendNotification = true;
+                                    observation.setStatusChangedByUserId(user.getUserId());
+                                    observation.setStatusChangedTime(SystemTime.getSystemTime());
+                                    // Status changed to approved, sending notifications
+                                    if(statusTypeId.equals(Observation.STATUS_TYPE_ID_APPROVED) && observation.getBroadcastMessage())
+                                    {
+                                        sendNotification = true;
+                                    }
                                 }
+                                observation.setStatusTypeId(statusTypeId);
+                                observation.setStatusRemarks(formValidation.getFormField("statusRemarks").getWebValue());
                             }
-                            observation.setStatusTypeId(statusTypeId);
-                            observation.setStatusRemarks(formValidation.getFormField("statusRemarks").getWebValue());
-                        }
-                        else if(observation.getStatusTypeId() == null)
-                        {
-                            observation.setStatusTypeId(Observation.STATUS_TYPE_ID_PENDING);
-                        }
-                        //observation.setLocation(formValidation.getFormField("location").getValueAsPointWGS84());
-                        //System.out.println(formValidation.getFormField("geoInfo").getWebValue());
-                        observation.setLocationPointOfInterestId(formValidation.getFormField("locationPointOfInterestId").getValueAsInteger() > 0 ? 
-                                formValidation.getFormField("locationPointOfInterestId").getValueAsInteger() : null);
-                        if(formValidation.getFormField("locationPointOfInterestId").getValueAsInteger() > 0)
-                        {
-                            observation.setLocationPointOfInterestId(formValidation.getFormField("locationPointOfInterestId").getValueAsInteger());
-                            observation.setGeoinfo(null);
-                        }
-                        else
-                        {
-                            observation.setLocationPointOfInterestId(null);
-                            observation.setGeoinfo(formValidation.getFormField("geoInfo").getWebValue());
-                        }
-                        observation = SessionControllerGetter.getObservationBean().storeObservation(observation);
+                            else if(observation.getStatusTypeId() == null)
+                            {
+                                observation.setStatusTypeId(Observation.STATUS_TYPE_ID_PENDING);
+                            }
+                            //observation.setLocation(formValidation.getFormField("location").getValueAsPointWGS84());
+                            //System.out.println(formValidation.getFormField("geoInfo").getWebValue());
+                            observation.setLocationPointOfInterestId(formValidation.getFormField("locationPointOfInterestId").getValueAsInteger() > 0 ? 
+                                    formValidation.getFormField("locationPointOfInterestId").getValueAsInteger() : null);
+                            if(formValidation.getFormField("locationPointOfInterestId").getValueAsInteger() > 0)
+                            {
+                                observation.setLocationPointOfInterestId(formValidation.getFormField("locationPointOfInterestId").getValueAsInteger());
+                                observation.setGeoinfo(null);
+                            }
+                            else
+                            {
+                                observation.setLocationPointOfInterestId(null);
+                                observation.setGeoinfo(formValidation.getFormField("geoInfo").getWebValue());
+                            }
+                            observation = SessionControllerGetter.getObservationBean().storeObservation(observation);
 
-                        // Image handling
-                        // Delete the current illustration
-                        String deleteIllustration = formValidation.getFormField("deleteIllustration").getWebValue();
-                        if(deleteIllustration != null && deleteIllustration.equals("true"))
-                        {
-                            observation = SessionControllerGetter.getObservationBean().deleteObservationIllustration(observation);
-                        }
+                            // Image handling
+                            // Delete the current illustration
+                            String deleteIllustration = formValidation.getFormField("deleteIllustration").getWebValue();
+                            if(deleteIllustration != null && deleteIllustration.equals("true"))
+                            {
+                                observation = SessionControllerGetter.getObservationBean().deleteObservationIllustration(observation);
+                            }
 
-                        // Store the new illustration (replaces former illustration if not already deleted)
-                        if(items != null)
-                        {
-                            for(FileItem item:items)
+                            // Store the new illustration (replaces former illustration if not already deleted)
+                            if(items != null)
                             {
-                                if(!item.isFormField() && item.getSize() > 0)
+                                for(FileItem item:items)
                                 {
-                                    observation = SessionControllerGetter.getObservationBean().storeObservationIllustration(observation, item);
+                                    if(!item.isFormField() && item.getSize() > 0)
+                                    {
+                                        observation = SessionControllerGetter.getObservationBean().storeObservationIllustration(observation, item);
+                                    }
                                 }
                             }
-                        }
 
-                        // All transactions finished, we can send notifications
-                        // if conditions are met
-                        if(sendNotification)
+                            // All transactions finished, we can send notifications
+                            // if conditions are met
+                            if(sendNotification)
+                            {
+                                SessionControllerGetter.getMessagingBean().sendUniversalMessage(observation);
+                            }
+
+                            // Redirect to form
+                            response.sendRedirect(new StringBuilder("http://")
+                                    .append(ServletUtil.getServerName(request))
+                                    .append("/observation?action=editObservationForm&observationId=").append(observation.getObservationId())
+                                    .append("&messageKey=").append("observationStored").toString()
+
+                            );
+                        }
+                        else
                         {
-                            SessionControllerGetter.getMessagingBean().sendUniversalMessage(observation);
+                            // Redirect to form with error messages
+                            request.setAttribute("formValidation", formValidation);
+                            request.setAttribute("observation", observation);
+                            List<Organism> allOrganisms = em.createNamedQuery("Organism.findAll").getResultList();
+                            request.setAttribute("allOrganisms", allOrganisms);
+                            // Hierarchy categories
+                            request.setAttribute("hierarchyCategories", SessionControllerGetter.getOrganismBean().getHierarchyCategoryNames(SessionLocaleUtil.getCurrentLocale(request)));
+                            request.setAttribute("observationMethods", em.createNamedQuery("ObservationMethod.findAll", ObservationMethod.class).getResultList());
+                            request.getRequestDispatcher("/observationForm.ftl").forward(request, response);
                         }
-                        
-                        // Redirect to form
-                        response.sendRedirect(new StringBuilder("http://")
-                                .append(ServletUtil.getServerName(request))
-                                .append("/observation?action=editObservationForm&observationId=").append(observation.getObservationId())
-                                .append("&messageKey=").append("observationStored").toString()
 
-                        );
                     }
-                    else
+                    catch(NullPointerException | NumberFormatException | FormValidationException | FileUploadException ex)
                     {
-                        // Redirect to form with error messages
-                        request.setAttribute("formValidation", formValidation);
-                        request.setAttribute("observation", observation);
-                        List<Organism> allOrganisms = em.createNamedQuery("Organism.findAll").getResultList();
-                        request.setAttribute("allOrganisms", allOrganisms);
-                        // Hierarchy categories
-                        request.setAttribute("hierarchyCategories", SessionControllerGetter.getOrganismBean().getHierarchyCategoryNames(SessionLocaleUtil.getCurrentLocale(request)));
-                        request.setAttribute("observationMethods", em.createNamedQuery("ObservationMethod.findAll", ObservationMethod.class).getResultList());
-                        request.getRequestDispatcher("/observationForm.ftl").forward(request, response);
+                        response.sendError(500, ExceptionUtil.getStackTrace(ex));
+                    }
+                    catch(Exception ex)
+                    {
+                        ex.printStackTrace();
+                        response.sendError(500, ExceptionUtil.getStackTrace(ex));
                     }
-                    
-                }
-                catch(NullPointerException | NumberFormatException | FormValidationException | FileUploadException ex)
-                {
-                    response.sendError(500, ExceptionUtil.getStackTrace(ex));
                 }
-                catch(Exception ex)
+                else
                 {
-                    ex.printStackTrace();
-                    response.sendError(500, ExceptionUtil.getStackTrace(ex));
+                    response.sendError(403,"Access not authorized"); // HTTP Forbidden
                 }
             }
-            else
+            // Authorization: ORGANIZATION ADMIN, OBSERVER or SUPERUSER
+            else if(action.equals("deleteObservation"))
             {
-                response.sendError(403,"Access not authorized"); // HTTP Forbidden
+                if(SessionControllerGetter.getUserBean().authorizeUser(user, VipsLogicRole.OBSERVER, VipsLogicRole.ORGANIZATION_ADMINISTRATOR, VipsLogicRole.SUPERUSER))
+                {
+                    try
+                    {
+                        Integer observationId = Integer.valueOf(request.getParameter("observationId"));
+                        SessionControllerGetter.getObservationBean().deleteObservation(observationId);
+
+                        // Redirect to list
+                        response.sendRedirect(new StringBuilder("http://")
+                                .append(ServletUtil.getServerName(request))
+                                .append("/observation")
+                                .append("?messageKey=").append("observationDeleted").toString()
+                        );
+                    }
+                    catch(NullPointerException | NumberFormatException ex)
+                    {
+                        response.sendError(500, ExceptionUtil.getStackTrace(ex));
+                    }
+                }
+                else
+                {
+                    response.sendError(403,"Access not authorized"); // HTTP Forbidden
+                }
             }
         }
-        // Authorization: ORGANIZATION ADMIN, OBSERVER or SUPERUSER
-        else if(action.equals("deleteObservation"))
+        else if(request.getServletPath().endsWith("/map"))
         {
-            if(SessionControllerGetter.getUserBean().authorizeUser(user, VipsLogicRole.OBSERVER, VipsLogicRole.ORGANIZATION_ADMINISTRATOR, VipsLogicRole.SUPERUSER))
+            // Only for superusers, organizationadmins and observation authorities
+            if(SessionControllerGetter.getUserBean().authorizeUser(user, 
+                    VipsLogicRole.SUPERUSER, VipsLogicRole.ORGANIZATION_ADMINISTRATOR, VipsLogicRole.OBSERVATION_AUTHORITY)
+                    )
             {
                 try
                 {
-                    Integer observationId = Integer.valueOf(request.getParameter("observationId"));
-                    SessionControllerGetter.getObservationBean().deleteObservation(observationId);
-                    
-                    // Redirect to list
-                    response.sendRedirect(new StringBuilder("http://")
-                            .append(ServletUtil.getServerName(request))
-                            .append("/observation")
-                            .append("?messageKey=").append("observationDeleted").toString()
+                    FormValidation formValidation = FormValidator.validateForm("observationMapForm", request, getServletContext());
+                    Organization organization = null;
+                    if(user.isSuperUser()){
+                        Integer organizationId = formValidation.getFormField("organizationId") != null ?
+                                formValidation.getFormField("organizationId").getValueAsInteger()
+                                :user.getOrganizationId().getOrganizationId();
+                        organization = em.find(Organization.class, organizationId);
+                        request.setAttribute("organizations", SessionControllerGetter.getUserBean().getOrganizations());
+                        request.setAttribute("organizationId", organizationId);
 
+                    }
+                    else
+                    {
+                        organization = user.getOrganizationId();
+                        request.setAttribute("organizationId", organization.getOrganizationId());
+                    }
+
+                    if(organization != null)
+                    {
+                        request.setAttribute("defaultMapCenter",organization.getDefaultMapCenter());
+                        request.setAttribute("defaultMapZoom", organization.getDefaultMapZoom());
+                    }
+
+                    // Input control
+                    // from: Default is start of year (jan 1st)
+                    Date from;
+                    Calendar cal = Calendar.getInstance(TimeZone.getDefault());
+                    if(!formValidation.getFormField("from").isEmpty())
+                    {
+                        from = formValidation.getFormField("from").getValueAsDate();
+                    }
+                    else
+                    {
+                        cal.setTime(SystemTime.getSystemTime());
+                        cal.set(cal.get(Calendar.YEAR), Calendar.JANUARY,1,0,0,0);
+                        from = cal.getTime();
+                    }
+                    request.setAttribute("from", from);
+                    // to
+                    Date to;
+                    if(!formValidation.getFormField("to").isEmpty())
+                    {
+                        to = formValidation.getFormField("to").getValueAsDate();
+                    }
+                    else
+                    {
+                        cal.setTime(SystemTime.getSystemTime());
+                        cal.set(cal.get(Calendar.YEAR), Calendar.DECEMBER,31,23,59,59);
+                        to = cal.getTime();
+                    }
+                    request.setAttribute("to", to);
+                    // pestId
+                    request.setAttribute("pestId", formValidation.getFormField("pestId").isEmpty() ? null : 
+                            formValidation.getFormField("pestId").getValueAsInteger()
+                    );
+                    // cropId
+                    request.setAttribute("cropId", formValidation.getFormField("cropId").isEmpty() ? null : 
+                            formValidation.getFormField("cropId").getValueAsInteger()
                     );
+                    // cropCategoryId
+                    request.setAttribute("cropCategoryId", formValidation.getFormField("cropCategoryId").isEmpty() ? null : 
+                            formValidation.getFormField("cropCategoryId").getValueAsInteger()
+                    );
+
+                    request.setAttribute("messageKey", request.getParameter("messageKey"));
+                    request.getRequestDispatcher("/observationMap.ftl").forward(request, response);
                 }
-                catch(NullPointerException | NumberFormatException ex)
+                catch(FormValidationException ex)
                 {
                     response.sendError(500, ExceptionUtil.getStackTrace(ex));
                 }
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 6a89b7373a7f20ac0e3b66f6d2ea17cbfd539880..ac634ac8679b24f734219301bdb6f38e99a5862e 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
@@ -22,6 +22,7 @@ package no.nibio.vips.logic.controller.session;
 import com.vividsolutions.jts.geom.Geometry;
 import java.io.File;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -32,6 +33,7 @@ import javax.ejb.Stateless;
 import javax.persistence.EntityManager;
 import javax.persistence.PersistenceContext;
 import javax.persistence.Query;
+import no.nibio.vips.logic.entity.CropCategory;
 import no.nibio.vips.logic.entity.Gis;
 import no.nibio.vips.logic.entity.Message;
 import no.nibio.vips.logic.entity.MessageIllustration;
@@ -39,6 +41,7 @@ import no.nibio.vips.logic.entity.MessageIllustrationPK;
 import no.nibio.vips.logic.entity.Observation;
 import no.nibio.vips.logic.entity.ObservationIllustration;
 import no.nibio.vips.logic.entity.ObservationIllustrationPK;
+import no.nibio.vips.logic.entity.ObservationStatusType;
 import no.nibio.vips.logic.entity.Organism;
 import no.nibio.vips.logic.entity.Organization;
 import no.nibio.vips.logic.entity.PointOfInterest;
@@ -153,18 +156,16 @@ public class ObservationBean {
         // Then persist the new ones
         if(observation.getGeoinfos() != null && ! observation.getGeoinfos().isEmpty())
         {
-            for(Gis gis : observation.getGeoinfos())
-            {
+            observation.getGeoinfos().stream().forEach((gis) -> {
                 em.persist(gis);
-            }
+            });
             
             Query q = em.createNativeQuery("INSERT INTO public.gis_observation(gis_id,observation_id) VALUES(:gisId,:observationId)")
                 .setParameter("observationId", retVal.getObservationId());
-            for(Gis gis:observation.getGeoinfos())
-            {
+            observation.getGeoinfos().stream().forEach((gis) -> {
                 q.setParameter("gisId", gis.getGisId())
                         .executeUpdate();
-            }
+            });
         }
         /*
         for(Geometry geom:observation.getGeometries())
@@ -299,51 +300,38 @@ public class ObservationBean {
 
     private List<Observation> getObservationsWithLocations(List<Observation> observations) {
          Set<Integer> locationPointOfInterestIds = new HashSet<>();
-         for(Observation o:observations)
-         {
-             if(o.getLocationPointOfInterestId() != null)
-             {
-                 locationPointOfInterestIds.add(o.getLocationPointOfInterestId());
-             }
-         }
+         observations.stream().filter((o) -> (o.getLocationPointOfInterestId() != null)).forEach((o) -> {
+             locationPointOfInterestIds.add(o.getLocationPointOfInterestId());
+        });
+         // Nothing to do?
+        if(locationPointOfInterestIds.isEmpty())
+        {
+            return observations;
+        }
          List<PointOfInterest> pois = SessionControllerGetter.getPointOfInterestBean().getPois(locationPointOfInterestIds);
          Map<Integer, PointOfInterest> mappedPois = new HashMap<>();
-         for(PointOfInterest poi:pois)
-         {
+         pois.stream().forEach((poi) -> {
              mappedPois.put(poi.getPointOfInterestId(), poi);
-         }
-         for(Observation o:observations)
-         {
-             if(o.getLocationPointOfInterestId() != null)
-             {
-                 o.setLocation(mappedPois.get(o.getLocationPointOfInterestId()));
-             }
-         }
+        });
+         observations.stream().filter((o) -> (o.getLocationPointOfInterestId() != null)).forEach((o) -> {
+             o.setLocation(mappedPois.get(o.getLocationPointOfInterestId()));
+        });
          return observations;
     }
 
     private List<Observation> getObservationsWithObservers(List<Observation> observations) {
         Set<Integer> userIds = new HashSet<>();
-         for(Observation o:observations)
-         {
-             if(o.getUserId() != null)
-             {
-                 userIds.add(o.getUserId());
-             }
-         }
+        observations.stream().filter((o) -> (o.getUserId() != null)).forEach((o) -> {
+            userIds.add(o.getUserId());
+        });
          List<VipsLogicUser> users = SessionControllerGetter.getUserBean().getUsers(userIds);
          Map<Integer, VipsLogicUser> mappedUsers = new HashMap<>();
-         for(VipsLogicUser user:users)
-         {
+         users.stream().forEach((user) -> {
              mappedUsers.put(user.getUserId(), user);
-         }
-         for(Observation o:observations)
-         {
-             if(o.getUserId() != null)
-             {
-                 o.setUser(mappedUsers.get(o.getUserId()));
-             }
-         }
+        });
+         observations.stream().filter((o) -> (o.getUserId() != null)).forEach((o) -> {
+             o.setUser(mappedUsers.get(o.getUserId()));
+        });
          return observations;
     }
     
@@ -354,5 +342,97 @@ public class ObservationBean {
                 .getResultList();
     }
 
+    public List<Observation> getFilteredObservations(
+            Integer organizationId, 
+            Integer pestId, 
+            Integer cropId, 
+            Integer cropCategoryId,
+            Date from, 
+            Date to
+    ) 
+    {
+        // The minimum SQL
+        String sql = "SELECT * FROM public.observation \n" +
+                     "WHERE status_type_id = :statusTypeId \n " + 
+                     "AND user_id IN (SELECT user_id FROM public.vips_logic_user WHERE organization_id = :organizationId) \n";
+       
+        Map<String, Object> parameters = new HashMap<>();
+        parameters.put("statusTypeId", ObservationStatusType.STATUS_APPROVED);
+        parameters.put("organizationId", organizationId);
+        
+        // Filter for pest
+        if(pestId != null && pestId > 0)
+        {
+            sql += "AND organism_id = :organismId \n";
+            parameters.put("organismId", pestId);
+        }
+        // Filter either for crop or cropCategoryId
+        if(cropId != null && cropId > 0)
+        {
+            sql += "AND crop_organism_id = :cropOrganismId \n";
+            parameters.put("cropOrganismId", cropId);
+        }
+        else if(cropCategoryId != null && cropCategoryId > 0)
+        {
+            CropCategory cropCategory = em.createNamedQuery("CropCategory.findByCropCategoryId", CropCategory.class)
+                    .setParameter("cropCategoryId", cropCategoryId)
+                    .getSingleResult();
+            List<Integer> cropIds = Arrays.asList(cropCategory.getCropOrganismIds());
+                    
+            sql += "AND crop_organism_id IN (:cropOrganismIds) \n";
+            parameters.put("cropOrganismIds", cropIds);
+        }
+        // Filter for dates
+        if(from != null)
+        {
+            sql += "AND time_of_observation >= :from \n";
+            parameters.put("from", from);
+        }
+        if(to != null)
+        {
+            sql += "AND time_of_observation <= :to \n";
+            parameters.put("to", to);
+        }
+        
+        Query q = em.createNativeQuery(sql, Observation.class);
+        // Setting the parameters one by one
+        parameters.keySet().stream().forEach(
+                (key)->q.setParameter(key, parameters.get(key))
+        );
+        
+        List<Observation> observations = q.getResultList();
+        
+        observations.stream().forEach(
+                (observation)->observation.setUser(em.find(VipsLogicUser.class, observation.getUserId()))
+        );
+        
+        return observations.isEmpty() ?
+                new ArrayList<>()
+                : this.getObservationsWithLocations(this.getObservationsWithGeoInfo(observations));
+    }
+
+    public List<Organism> getObservedPests(Integer organizationId) {
+        Query q = em.createNativeQuery("SELECT DISTINCT organism_id FROM public.observation WHERE user_id IN ("
+                + " SELECT user_id FROM vips_logic_user WHERE organization_id = :organizationId"
+                + ")");
+        List<Integer> pestIds = q.setParameter("organizationId", organizationId).getResultList();
+        return em.createNamedQuery("Organism.findByOrganismIds")
+                .setParameter("organismIds", pestIds)
+                .getResultList();
+        
+    }
+    
+    public List<Organism> getObservedCrops(Integer organizationId) {
+        Query q = em.createNativeQuery("SELECT DISTINCT crop_organism_id FROM public.observation WHERE user_id IN ("
+                + " SELECT user_id FROM vips_logic_user WHERE organization_id = :organizationId"
+                + ")");
+        List<Integer> cropIds = q.setParameter("organizationId", organizationId).getResultList();
+        return em.createNamedQuery("Organism.findByOrganismIds")
+                .setParameter("organismIds", cropIds)
+                .getResultList();
+        
+    }
+
+    
     
 }
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 2da3b8b8095062a8d195ef8ac5dd6fdb0cf80dee..24889460ff7215fcc23179aa8d65879410849b71 100644
--- a/src/main/java/no/nibio/vips/logic/entity/Observation.java
+++ b/src/main/java/no/nibio/vips/logic/entity/Observation.java
@@ -37,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 java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import javax.persistence.CascadeType;
 import javax.persistence.FetchType;
@@ -173,7 +175,9 @@ public class Observation implements Serializable, no.nibio.vips.observation.Obse
     @Override
     public String getGeoinfo()
     {
-        return this.GISUtil.getGeoJSONFromGis(this.geoinfo);
+        Map<String, Object> properties = new HashMap<>();
+        properties.put("observationId", this.getObservationId());
+        return this.GISUtil.getGeoJSONFromGis(this.geoinfo, properties);
     }
     
     /*@Transient
diff --git a/src/main/java/no/nibio/vips/logic/entity/ObservationStatusType.java b/src/main/java/no/nibio/vips/logic/entity/ObservationStatusType.java
index afa92961efcb6a00535f3cc6f992a593c08bb4d0..1d36fe45393199ab75a9c33a08923347f6b136f1 100644
--- a/src/main/java/no/nibio/vips/logic/entity/ObservationStatusType.java
+++ b/src/main/java/no/nibio/vips/logic/entity/ObservationStatusType.java
@@ -43,6 +43,12 @@ import javax.xml.bind.annotation.XmlRootElement;
     @NamedQuery(name = "ObservationStatusType.findByStatusTypeId", query = "SELECT o FROM ObservationStatusType o WHERE o.statusTypeId = :statusTypeId"),
     @NamedQuery(name = "ObservationStatusType.findByStatusTitle", query = "SELECT o FROM ObservationStatusType o WHERE o.statusTitle = :statusTitle")})
 public class ObservationStatusType implements Serializable {
+    
+    public static final Integer STATUS_PENDING = 1;
+    public static final Integer STATUS_REJECTED = 2;
+    public static final Integer STATUS_APPROVED = 3;
+
+    
 
     private static final long serialVersionUID = 1L;
     @Id
diff --git a/src/main/java/no/nibio/vips/logic/entity/PointOfInterest.java b/src/main/java/no/nibio/vips/logic/entity/PointOfInterest.java
index bc73ba0467aa1d2daa8a7f0c4da7a202c8a9f2b4..a4b2784b47ffc2d357e0416031561bb39b0a023f 100644
--- a/src/main/java/no/nibio/vips/logic/entity/PointOfInterest.java
+++ b/src/main/java/no/nibio/vips/logic/entity/PointOfInterest.java
@@ -324,7 +324,9 @@ public class PointOfInterest implements Serializable, Comparable {
     @Transient
     public String getGeoJSON()
     {
-        return this.gisUtil.getGeoJSONFromGeometry(this.getGisGeom());
+        Map<String, Object> properties = new HashMap<>();
+        properties.put("pointOfInterestId", this.getPointOfInterestId());
+        return this.gisUtil.getGeoJSONFromGeometry(this.getGisGeom(), properties);
     }
     
     /**
diff --git a/src/main/java/no/nibio/vips/logic/service/LogicService.java b/src/main/java/no/nibio/vips/logic/service/LogicService.java
index 2f83d85cd3d9bb59f27b26a7fc09df026bb8237e..6178b04672741a8caba1704191e894f8cf830d5f 100644
--- a/src/main/java/no/nibio/vips/logic/service/LogicService.java
+++ b/src/main/java/no/nibio/vips/logic/service/LogicService.java
@@ -635,36 +635,8 @@ public class LogicService {
         return Response.ok().entity(observations).build();
     }
     
-    @GET
-    @Path("observation/{observationId}")
-    @Produces("application/json;charset=UTF-8")
-    public Response getObservation(@PathParam("observationId") Integer observationId){
-        return Response.ok().entity(SessionControllerGetter.getObservationBean().getObservation(observationId)).build();
-    }
     
-    /**
-     * Publicly available observations per organization
-     * @param organizationId
-     * @return APPROVED observations
-     */
-    @GET
-    @Path("observation/list/{organizationId}")
-    @Produces("application/json;charset=UTF-8")
-    public Response getObservations(@PathParam("organizationId") Integer organizationId){
-        return Response.ok().entity(SessionControllerGetter.getObservationBean().getObservations(organizationId, Observation.STATUS_TYPE_ID_APPROVED)).build();
-    }
     
-    /**
-     * Publicly available observations per organization
-     * @param organizationId
-     * @return APPROVED observations
-     */
-    @GET
-    @Path("observation/broadcast/list/{organizationId}")
-    @Produces("application/json;charset=UTF-8")
-    public Response getBroadcastObservations(@PathParam("organizationId") Integer organizationId){
-        return Response.ok().entity(SessionControllerGetter.getObservationBean().getBroadcastObservations(organizationId)).build();
-    }
     
     /**
      * Service available locally for cron jobs. Most useful on test servers
diff --git a/src/main/java/no/nibio/vips/logic/service/ObservationService.java b/src/main/java/no/nibio/vips/logic/service/ObservationService.java
new file mode 100644
index 0000000000000000000000000000000000000000..a59bc42db8b675a77613a8823bccd8880b02bc4a
--- /dev/null
+++ b/src/main/java/no/nibio/vips/logic/service/ObservationService.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2016 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.service;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.List;
+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.logic.entity.Observation;
+import no.nibio.vips.logic.util.Globals;
+import no.nibio.vips.logic.util.SessionControllerGetter;
+
+/**
+ * @copyright 2016 <a href="http://www.nibio.no/">NIBIO</a>
+ * @author Tor-Einar Skog <tor-einar.skog@nibio.no>
+ */
+@Path("rest/observation")
+public class ObservationService {
+    
+    /**
+     * NOTE TO SELF
+     * How to query for observations within a bounding box
+     * Select * from gis where ST_Intersects(
+     *   ST_SetSRID(ST_MakeBox2D(ST_MakePoint(2.9004, 57.7511), ST_MakePoint(32.4316, 71.3851)),4326),
+     *   gis_geom);
+     * First point is SW, last is NE (but could be anything?)
+     * @param organizationId
+     * @param pestId
+     * @param cropId
+     * @param cropCategoryId
+     * @param from
+     * @param to
+     * @return 
+     */
+    @GET
+    @Path("filter/{organizationId}")
+    @Produces("application/json;charset=UTF-8")
+    public Response getFilteredObservations(
+            @PathParam("organizationId") Integer organizationId,
+            @QueryParam("pestId") Integer pestId,
+            @QueryParam("cropId") Integer cropId,
+            @QueryParam("cropCategoryId") Integer cropCategoryId,
+            @QueryParam("from") String fromStr,
+            @QueryParam("to") String toStr
+            
+    )
+    {
+        SimpleDateFormat format = new SimpleDateFormat(Globals.defaultDateFormat);
+        //TODO Set correct timeZone!!!
+        Date from = null;
+        Date to = null;
+        try
+        {
+            from = fromStr != null ? format.parse(fromStr) : null;
+            to = toStr != null ? format.parse(toStr) : null;
+        }
+        catch(ParseException ex){ System.out.println("ERROR");}
+                
+        List<Observation> filteredObservations = SessionControllerGetter.getObservationBean().getFilteredObservations(
+            organizationId,
+            pestId,
+            cropId,
+            cropCategoryId,
+            from,
+            to
+        );
+        return Response.ok().entity(filteredObservations).build();
+    }
+    
+    /**
+     * Get a list of all observed pests for one organization
+     * Practical for building effective select lists
+     * TODO: Should be cached??
+     * @param organizationId
+     * @return 
+     */
+    @GET
+    @Path("pest/{organizationId}")
+    @Produces("application/json;charset=UTF-8")
+    public Response getObservedPests(@PathParam("organizationId") Integer organizationId)
+    {
+        return Response.ok().entity(SessionControllerGetter.getObservationBean().getObservedPests(organizationId)).build();
+    }
+    
+    /**
+     * Get a list of all crop cultures where observations have been made for one organization
+     * Practical for building effective select lists
+     * TODO: Should be cached??
+     * @param organizationId
+     * @return 
+     */
+    @GET
+    @Path("crop/{organizationId}")
+    @Produces("application/json;charset=UTF-8")
+    public Response getObservedCrops(@PathParam("organizationId") Integer organizationId)
+    {
+        return Response.ok().entity(SessionControllerGetter.getObservationBean().getObservedCrops(organizationId)).build();
+    }
+    
+    
+    /**
+     * Publicly available observations per organization
+     * @param organizationId
+     * @return APPROVED observations
+     */
+    @GET
+    @Path("list/{organizationId}")
+    @Produces("application/json;charset=UTF-8")
+    public Response getObservations(@PathParam("organizationId") Integer organizationId){
+        return Response.ok().entity(SessionControllerGetter.getObservationBean().getObservations(organizationId, Observation.STATUS_TYPE_ID_APPROVED)).build();
+    }
+    
+    /**
+     * Publicly available observations per organization
+     * @param organizationId
+     * @return APPROVED observations
+     */
+    @GET
+    @Path("broadcast/list/{organizationId}")
+    @Produces("application/json;charset=UTF-8")
+    public Response getBroadcastObservations(@PathParam("organizationId") Integer organizationId){
+        return Response.ok().entity(SessionControllerGetter.getObservationBean().getBroadcastObservations(organizationId)).build();
+    }
+    
+    @GET
+    @Path("{observationId}")
+    @Produces("application/json;charset=UTF-8")
+    public Response getObservation(@PathParam("observationId") Integer observationId){
+        return Response.ok().entity(SessionControllerGetter.getObservationBean().getObservation(observationId)).build();
+    }
+    
+}
diff --git a/src/main/java/no/nibio/vips/logic/util/GISUtil.java b/src/main/java/no/nibio/vips/logic/util/GISUtil.java
index 0e7fa7ab2497451bfce8663625d288bb17525725..a32327d722d508160a5b43f9fb1f61f6372a9d38 100644
--- a/src/main/java/no/nibio/vips/logic/util/GISUtil.java
+++ b/src/main/java/no/nibio/vips/logic/util/GISUtil.java
@@ -25,8 +25,6 @@ import com.vividsolutions.jts.geom.GeometryFactory;
 import com.vividsolutions.jts.geom.Point;
 import com.vividsolutions.jts.geom.PrecisionModel;
 import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import no.nibio.vips.logic.entity.Gis;
@@ -82,14 +80,13 @@ public class GISUtil {
         return retVal;
     }
     
-    public String getGeoJSONFromGis(List<Gis> geoinfo)
+    public String getGeoJSONFromGis(List<Gis> geoinfo, Map<String, Object> properties)
     {
         if(geoinfo == null || geoinfo.isEmpty())
         {
             return "";
         }
         List<Feature> features = new ArrayList<>();
-        Map<String, Object> properties = new HashMap<>();
         GeoJSONWriter writer = new GeoJSONWriter();
         geoinfo.stream().forEach(
                 (gis)->features.add(new Feature(writer.write(gis.getGisGeom()), properties))
@@ -99,28 +96,26 @@ public class GISUtil {
         return json.toString();
     }
     
-    public String getGeoJSONFromGeometries(List<Geometry> geometries)
+    public String getGeoJSONFromGeometries(List<Geometry> geometries, Map<String, Object> properties)
     {
         if(geometries == null || geometries.isEmpty())
         {
             return "";
         }
         List<Feature> features = new ArrayList<>();
-        Map<String, Object> properties = new HashMap<>();
         GeoJSONWriter writer = new GeoJSONWriter();
         geometries.stream().forEach((geometry)->features.add(new Feature(writer.write(geometry), properties)));
         FeatureCollection json = writer.write(features);
         return json.toString();
     }
     
-    public String getGeoJSONFromGeometry(Geometry geometry)
+    public String getGeoJSONFromGeometry(Geometry geometry,Map<String, Object> properties)
     {
         if(geometry == null)
         {
             return "";
         }
         List<Feature> features = new ArrayList<>();
-        Map<String, Object> properties = new HashMap<>();
         GeoJSONWriter writer = new GeoJSONWriter();
        
         features.add(new Feature(writer.write(geometry), properties));
diff --git a/src/main/java/no/nibio/vips/logic/web/js/JSEnvironment.java b/src/main/java/no/nibio/vips/logic/web/js/JSEnvironment.java
new file mode 100644
index 0000000000000000000000000000000000000000..2db214be6575e06479be9164c85d70315cd17518
--- /dev/null
+++ b/src/main/java/no/nibio/vips/logic/web/js/JSEnvironment.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2016 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.web.js;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import no.nibio.vips.logic.i18n.SessionLocaleUtil;
+
+/**
+ * Provides JavaScript files with environment information
+ * @copyright 2016 <a href="http://www.nibio.no/">NIBIO</a>
+ * @author Tor-Einar Skog <tor-einar.skog@nibio.no>
+ */
+public class JSEnvironment extends HttpServlet {
+   
+    /** 
+     * Processes requests for both HTTP <code>GET</code> and <code>POST</code> methods.
+     * @param request servlet request
+     * @param response servlet response
+     * @throws ServletException if a servlet-specific error occurs
+     * @throws IOException if an I/O error occurs
+     */
+    protected void processRequest(HttpServletRequest request, HttpServletResponse response)
+    throws ServletException, IOException {
+        response.setContentType("application/javascript;charset=UTF-8");
+        try (PrintWriter out = response.getWriter()) {
+            /* TODO output your page here. You may use following sample code. */
+            out.println("var environment = {");
+            out.println("   currentLanguage: \"" + SessionLocaleUtil.getCurrentLocale(request) + "\",");
+            out.println("   defaultLanguage: \"en\"");
+            out.println("};");
+        }
+    } 
+
+    // <editor-fold defaultstate="collapsed" desc="HttpServlet methods. Click on the + sign on the left to edit the code.">
+    /** 
+     * Handles the HTTP <code>GET</code> method.
+     * @param request servlet request
+     * @param response servlet response
+     * @throws ServletException if a servlet-specific error occurs
+     * @throws IOException if an I/O error occurs
+     */
+    @Override
+    protected void doGet(HttpServletRequest request, HttpServletResponse response)
+    throws ServletException, IOException {
+        processRequest(request, response);
+    } 
+
+    /** 
+     * Handles the HTTP <code>POST</code> method.
+     * @param request servlet request
+     * @param response servlet response
+     * @throws ServletException if a servlet-specific error occurs
+     * @throws IOException if an I/O error occurs
+     */
+    @Override
+    protected void doPost(HttpServletRequest request, HttpServletResponse response)
+    throws ServletException, IOException {
+        processRequest(request, response);
+    }
+
+    /** 
+     * Returns a short description of the servlet.
+     * @return a String containing servlet description
+     */
+    @Override
+    public String getServletInfo() {
+        return "Short description";
+    }// </editor-fold>
+
+}
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 319c9a0dc9d04bbeea90775c762a3aedb86d67d5..f1e8e3a3cda982a2de6c60911ee3b433c7a07047 100644
--- a/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts.properties
+++ b/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts.properties
@@ -428,3 +428,4 @@ uppercase=Upper case
 allSystems=All systems
 cropCategoriesFor=Crop categories for
 cropCategoryUpdated=Crop category was updated
+observationMap=Observation map
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 d88dd091035e7a6408cb505eaf8b773eaaad9d3a..7e128fde97fa9d7f3e93809a175147b9ea0f12f2 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
@@ -428,3 +428,4 @@ uppercase=Upper case
 allSystems=All systems
 cropCategoriesFor=Crop categories for
 cropCategoryUpdated=Crop category was updated
+observationMap=Observation map
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 bcdf18e97b76948fdc4ab87608f445e6b5ae3baa..431ac9c44ea84c07d57891b1828412d44a63fca2 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
@@ -427,3 +427,4 @@ uppercase=Upper case
 allSystems=All systems
 cropCategoriesFor=Crop categories for
 cropCategoryUpdated=Crop category was updated
+observationMap=Observation map
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 305abc4f611bbb1190e449b59084aecff27d1994..671364832bc45c0f0258fd66e184f3e86c2a4b6c 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
@@ -428,3 +428,4 @@ uppercase=STOR bokstav
 allSystems=Alle system
 cropCategoriesFor=Kulturkategorier for
 cropCategoryUpdated=Kulturkategorien ble oppdatert
+observationMap=Observasjonskart
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 dacbfef7cc8e04b6ae2ba30282ef7979af8d2093..620d6b1577b96cf9accadf3e63e29463b6b12ed5 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
@@ -428,3 +428,4 @@ uppercase=Upper case
 allSystems=All systems
 cropCategoriesFor=Crop categories for
 cropCategoryUpdated=Crop category was updated
+observationMap=Observation map
diff --git a/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_zh_CN.properties b/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_zh_CN.properties
index 319c9a0dc9d04bbeea90775c762a3aedb86d67d5..f1e8e3a3cda982a2de6c60911ee3b433c7a07047 100644
--- a/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_zh_CN.properties
+++ b/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_zh_CN.properties
@@ -428,3 +428,4 @@ uppercase=Upper case
 allSystems=All systems
 cropCategoriesFor=Crop categories for
 cropCategoryUpdated=Crop category was updated
+observationMap=Observation map
diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml
index a4e31cf71311cf06637080417e944cd3bba7e0a7..d84c500de00bd07f911345f402eccc1c1df5c72b 100644
--- a/src/main/webapp/WEB-INF/web.xml
+++ b/src/main/webapp/WEB-INF/web.xml
@@ -79,6 +79,10 @@
         <servlet-name>CropCategoryController</servlet-name>
         <servlet-class>no.nibio.vips.logic.controller.servlet.CropCategoryController</servlet-class>
     </servlet>
+    <servlet>
+        <servlet-name>JSEnvironment</servlet-name>
+        <servlet-class>no.nibio.vips.logic.web.js.JSEnvironment</servlet-class>
+    </servlet>
     <servlet-mapping>
         <servlet-name>PointOfInterestController</servlet-name>
         <url-pattern>/poi/*</url-pattern>
@@ -126,6 +130,7 @@
     <servlet-mapping>
         <servlet-name>ObservationServlet</servlet-name>
         <url-pattern>/observation</url-pattern>
+        <url-pattern>/observation/map</url-pattern>
     </servlet-mapping>
     <servlet-mapping>
         <servlet-name>NotificationSubscriptionController</servlet-name>
@@ -143,6 +148,10 @@
         <servlet-name>CropCategoryController</servlet-name>
         <url-pattern>/organism/cropcategory</url-pattern>
     </servlet-mapping>
+    <servlet-mapping>
+        <servlet-name>JSEnvironment</servlet-name>
+        <url-pattern>/js/environment.js</url-pattern>
+    </servlet-mapping>
     <welcome-file-list>
         <welcome-file>index.html</welcome-file>
     </welcome-file-list>
@@ -280,4 +289,4 @@
         <error-code>404</error-code>
         <location>/error/404</location>
     </error-page>
-</web-app>
+</web-app>
\ No newline at end of file
diff --git a/src/main/webapp/css/vipslogic.css b/src/main/webapp/css/vipslogic.css
index 2de1c3e04487f57adb3878e627eb429e84159c57..533bbcc693524b5906fd0e1c31fdde00df3bc543 100644
--- a/src/main/webapp/css/vipslogic.css
+++ b/src/main/webapp/css/vipslogic.css
@@ -263,4 +263,9 @@ legend {
 .referenceTranslationText {
     font-size: smaller;
     font-style: italic;
+}
+
+div.popover {
+	min-width: 350px;
+	max-width: 400px !important;
 }
\ No newline at end of file
diff --git a/src/main/webapp/formdefinitions/observationMapForm.json b/src/main/webapp/formdefinitions/observationMapForm.json
new file mode 100644
index 0000000000000000000000000000000000000000..77149662796904cf768c17df4098981180afe0c3
--- /dev/null
+++ b/src/main/webapp/formdefinitions/observationMapForm.json
@@ -0,0 +1,58 @@
+{
+    "_licenseNote": [
+        "Copyright (c) 2016 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/>. "
+    ],
+    "_comment" : "Simplify input check for observation map",
+    "fields": [
+        {
+            "name" : "organizationdId",
+            "dataType" : "INTEGER",
+            "required" : false
+        },
+        {
+            "name" : "pestId",
+            "dataType" : "INTEGER",
+            "fieldType" : "SELECT_SINGLE",
+            "required" : false,
+            "nullValue" : "-1"
+        },
+        {
+            "name" : "cropId",
+            "dataType" : "INTEGER",
+            "fieldType" : "SELECT_SINGLE",
+            "required" : false,
+            "nullValue" : "-1"
+        },
+        {
+            "name" : "cropCategoryId",
+            "dataType" : "INTEGER",
+            "fieldType" : "SELECT_SINGLE",
+            "required" : false,
+            "nullValue" : "-1"
+        },
+        {
+            "name" : "from",
+            "dataType" : "DATE",
+            "required" : false
+        },
+        {
+            "name" : "to",
+            "dataType" : "DATE",
+            "required" : false
+        }
+    ]
+}
diff --git a/src/main/webapp/js/observationMap.js b/src/main/webapp/js/observationMap.js
new file mode 100644
index 0000000000000000000000000000000000000000..5d5955ef52fe41c88f11eb17fd3916fe4a71b724
--- /dev/null
+++ b/src/main/webapp/js/observationMap.js
@@ -0,0 +1,309 @@
+/* 
+ * Copyright (c) 2016 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/>.
+ * 
+ */
+
+var allObservations = []; // Populated asynchronously
+var drawnFeatures = []; // Populated asynchronously
+
+/*
+ * Observation map
+ * @author Tor-Einar Skog <tor-einar.skog@nibio.no>
+ */
+var initMap = function(   
+            center, 
+            zoomLevel, 
+            organizationId,
+            from,
+            to,
+            pestId,
+            cropId,
+            cropCategoryId
+        )
+{
+    // Background layer is OpenStreetMap
+    var backgroundLayer = new ol.layer.Tile({
+                    source: new ol.source.OSM({
+                        attributions: [
+                            new ol.Attribution({
+                              html: mapConstants.MAP_ATTRIBUTION
+                            })
+                          ]
+                    })
+    });
+    
+    // Layer for popup
+    var popOverlay = new ol.Overlay({
+      element: document.getElementById("popover")
+    });
+    
+    // Creating the map
+    var map = new ol.Map({
+                    target: 'observationMap',
+                    layers: [backgroundLayer],
+                    overlays: [popOverlay],
+                    renderer: 'canvas'
+    });
+    
+    var centerPosition = ol.proj.transform(center, 'EPSG:4326', map.getView().getProjection().getCode());
+    
+    // Setting zoom and center for the map (need to do this after creating map. so that we kan transform our
+    // center to correct map projection)
+    var view = new ol.View({
+            center: centerPosition,
+            zoom:zoomLevel
+    });
+    map.setView(view);
+    
+    // Need to build the query string
+    var params = [];
+    
+    if(from !== "")
+    {
+        params.push("from=" + from);
+    }
+    if(to !== "")
+    {
+        params.push("to=" + to);
+    }
+    if(pestId !== null)
+    {
+        params.push("pestId=" + pestId);
+    }
+    if(cropId !== null)
+    {
+        params.push("cropId=" + cropId);
+    }
+    if(cropCategoryId !== null)
+    {
+        params.push("cropCategoryId=" + cropCategoryId);
+    }
+    
+    
+    $.getJSON( "/rest/observation/filter/" + organizationId + (params.length > 0 ? "?" + params.join("&") : ""), function( data ) {
+        allObservations = data;
+        renderObservationTable(data);
+        var geoJSON = {"type":"FeatureCollection","features":[]};
+        for(var i=0;i<data.length;i++)
+        {
+            var observation = data[i];
+            var obsFeatures = null;
+            if(observation.location !== null && observation.location.geoJSON !== null)
+            {
+
+                obsFeatures = JSON.parse(observation.location.geoJSON).features;
+            }
+            else if(observation.geoinfo !== null)
+            {
+                obsFeatures = JSON.parse(observation.geoinfo).features;
+            }
+            
+            for(var j=0; j<obsFeatures.length; j++)
+            {
+                geoJSON.features.push(obsFeatures[j]);
+            }
+        }
+        var features = new ol.Collection();
+        var featureOverlay = new ol.layer.Vector({
+		    source: new ol.source.Vector({
+		      features: features
+		    }),
+		    style: new ol.style.Style({
+		      fill: new ol.style.Fill({
+		        color: 'rgba(255, 0, 255, 0.2)'
+		      }),
+		      stroke: new ol.style.Stroke({
+		        color: '#ff00ff',
+		        width: 2
+		      }),
+		      image: new ol.style.Circle({
+		        radius: 7,
+		        fill: new ol.style.Fill({
+		          color: '#ff00ff'
+		        })
+		      })
+		    })
+		  });
+	
+	
+        var format = new ol.format.GeoJSON();
+        drawnfeatures = format.readFeatures(geoJSON, {
+          dataProjection: 'EPSG:4326',
+          featureProjection: map.getView().getProjection().getCode()
+        });
+        
+        featureOverlay.getSource().addFeatures(drawnfeatures);
+        //console.log(featureOverlay);
+        featureOverlay.setMap(map);
+        if(drawnfeatures.length > 0)
+        {
+            extent = featureOverlay.getSource().getExtent();
+            map.getView().fit(extent, map.getSize());
+        }
+    });
+    
+    // Using Bootstrap's popover plugin. See http://getbootstrap.com/javascript/#popovers
+    var poiDetails = $("#popover");
+    
+    var displayFeatureDetails = function(pixel, coordinate) {
+        var feature = map.forEachFeatureAtPixel(pixel, function(feature,layer){
+           return feature; 
+        });
+
+        if (feature) {
+            
+            // Position the popup, and hiding it
+            // Resetting information from (possible) former popups
+            var geometry = feature.getGeometry();
+            popOverlay.setPosition(ol.extent.getCenter(geometry.getExtent()));
+            // Get the observation that this feature belongs to
+            var observation = getObservation(feature.get("observationId"));
+            //popOverlay.setPosition(geometry.getCoordinates());
+            poiDetails.popover('destroy');
+            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'/>";
+            }
+            // Create the popup, showing it
+            poiDetails.popover({
+                    animation: true,
+                    trigger: 'manual',
+                    html: true,
+                    placement: "auto top",
+                    title: "<a href='/observation?action=editObservationForm&observationId=" + observation.observationId + "' target='new'>" + observation.observationHeading + "</a>",
+                    content: "[" + moment(observation.timeOfObservation).format("YYYY-MM-DD HH:mm ZZ") + "]: " 
+                            + observation.observationText 
+                            + illustrationElm
+            });
+           
+
+          poiDetails.popover('show');
+
+
+        } else {
+            poiDetails.popover('destroy');
+        }
+    };
+    
+    map.on('singleclick', function(evt) {
+            var pixel = map.getEventPixel(evt.originalEvent);
+              displayFeatureDetails(pixel);
+    });
+    
+    
+}
+
+var getObservation = function(observationId)
+{
+    for(var i=0; i<allObservations.length;i++)
+    {
+        if(allObservations[i].observationId == observationId)
+        {
+            return allObservations[i];
+        }
+    }
+    return null;
+}
+
+
+var renderObservationTable = function(data)
+{
+    var tbody = document.getElementById("observationTableBody");
+    var tbodyHTML = [];
+    for(var i=0; i<data.length;i++)
+    {
+        var obs = data[i];
+        tbodyHTML.push("<tr>");
+        tbodyHTML.push("<td>" + moment(obs.timeOfObservation).format("YYYY-MM-DD HH:mm ZZ") + "</td>");
+        tbodyHTML.push("<td>" + getLocalizedOrganismName(obs.organism) + "</td>");
+        tbodyHTML.push("<td>" + getLocalizedOrganismName(obs.cropOrganism) + "</td>");
+        tbodyHTML.push("<td>" + (obs.location != null ? obs.location.name : "") + "</td>");
+        tbodyHTML.push("<td>" + obs.user.firstName + " " + obs.user.lastName + "</td>");
+        tbodyHTML.push("<td>" + obs.observationHeading + "</td>");
+        tbodyHTML.push("<td></td>");
+        tbodyHTML.push("</tr>");
+    }
+    tbody.innerHTML = tbodyHTML.join("\n");
+}
+
+// TODO: Set selected if possible
+var renderOrganismField = function(organismList, fieldId, selectedId)
+{
+    // Sort alphabetically by local name
+    organismList.sort(function(a,b){
+        if (getLocalizedOrganismName(a) < getLocalizedOrganismName(b)) return -1;
+        if (getLocalizedOrganismName(a) > getLocalizedOrganismName(b)) return 1;
+        return 0;
+    });
+    var list = document.getElementById(fieldId);
+    list.options.length=0;
+    list.options[0] = new Option("",""); // For the chosenjs to print data-placeholder 
+    for(var i=0;i<organismList.length;i++)
+    {
+        var organism = organismList[i];
+        var newOption = new Option(getLocalizedOrganismName(organism),organism.organismId);
+        if(organism.organismId === selectedId)
+        {
+            newOption.selected = true;
+        }
+        list.options[list.options.length] = newOption;
+    }
+}
+
+var renderCropCategoryField = function(cropCategoryList, selectedId)
+{
+    // TODO: Sort by local name
+    cropCategoryList.sort(function(a,b){
+        if (getLocalizedCropCategoryName(a) < getLocalizedCropCategoryName(b)) return -1;
+        if (getLocalizedCropCategoryName(a) > getLocalizedCropCategoryName(b)) return 1;
+        return 0;
+    });
+    var list = document.getElementById("cropCategoryList");
+    list.options.length=0;
+    list.options[0] = new Option("",""); // For the chosenjs to print data-placeholder 
+    for(var i=0;i<cropCategoryList.length;i++)
+    {
+        var cropCategory = cropCategoryList[i];
+        var newOption = new Option(getLocalizedCropCategoryName(cropCategory),cropCategory.cropCategoryId);
+        if(cropCategory.cropCategoryId === selectedId)
+        {
+            newOption.selected = true;
+        }
+        list.options[list.options.length] = newOption;
+    }
+}
+
+var initForm = function(organizationId,
+            pestId,
+            cropId,
+            cropCategoryId,
+            postRenderFormActions
+        )
+{
+    $.getJSON( "/rest/observation/pest/" + organizationId , function( pestList ) {
+        renderOrganismField(pestList, "pestList", pestId);
+        $.getJSON( "/rest/observation/crop/" + organizationId , function( cropList ) {
+            renderOrganismField(cropList, "cropList", cropId);
+             $.getJSON( "/rest/organism/cropcategory/" + organizationId , function( cropCategoryList ) {
+                renderCropCategoryField(cropCategoryList, cropCategoryId);
+                postRenderFormActions(); // Activate chosen.js
+            });
+        });
+    });
+}
diff --git a/src/main/webapp/js/util.js b/src/main/webapp/js/util.js
index adebd204fecb46cff0e7516df55a3bc4c5e2e461..92fa45b1db14cfe436f02fe39bacd6afc2b4fc5c 100644
--- a/src/main/webapp/js/util.js
+++ b/src/main/webapp/js/util.js
@@ -34,3 +34,81 @@ var compareSelectListOptions = function(a,b)
 	}
 	return 0;
 }
+
+/**
+ * Depends on the value of currentLanguage and defaultLanguage in /currentLanguage.js
+ * @param organism
+ * @returns {String}
+ */
+function getLocalizedOrganismName(organism)
+{
+	// Fallback in case nothing works
+	if(organism === null)
+	{
+		return gettext("Unnamed");
+	}
+	// Attempting the following languages (in order): current language, default language, English
+	var languages = [environment.currentLanguage, environment.defaultLanguage, "en"];
+	for(var j in languages)
+	{
+		for(var i in organism.organismLocaleSet)
+		{
+			var localeSet = organism.organismLocaleSet[i];
+			//console.log(localeSet);
+			if(localeSet.organismLocalePK.locale == languages[j])
+			{
+				return localeSet.localName;
+			}
+		}
+	}
+	// Then we try the latin name
+	if(organism.latinName !== null 
+			&& organism.latinName !== "")
+	{
+		return organism.latinName;
+	}
+	// Then the trade name
+	if(organism.tradeName !== null
+			&& organism.tradeName !== "")
+	{
+		return organism.tradeName;
+	}
+	// Then we give up
+	return gettext("Unnamed");
+}
+
+/**
+ * Depends on the value of currentLanguage and defaultLanguage in /currentLanguage.js
+ * @param cropCategory
+ * @returns {String}
+ */
+function getLocalizedCropCategoryName(cropCategory)
+{
+	// Fallback in case nothing works
+	if(cropCategory === null)
+	{
+		return "Unnamed";
+	}
+	// Attempting the following languages (in order): current language, default language, English
+	var languages = [environment.currentLanguage, environment.defaultLanguage, "en"];
+	for(var j in languages)
+	{
+		for(var i in cropCategory.cropCategoryLocalSet)
+		{
+			var localeSet = cropCategory.cropCategoryLocalSet[i];
+			if(localeSet.cropCategoryLocalPK.locale.trim() == languages[j].trim())
+			{
+				return localeSet.localName;
+			}
+		}
+	}
+	// Then we try the latin name
+	if(cropCategory.defaultName !== null 
+			&& cropCategory.defaultName !== "")
+	{
+		return cropCategory.defaultName;
+	}
+	// Then we give up
+	return "Unnamed";
+}
+
diff --git a/src/main/webapp/js/weatherStationListMap.js b/src/main/webapp/js/weatherStationListMap.js
index b83affc1b9f8ba77e28076739eac993f8ac6dfc1..7e496be845b246d101036ba649072bda0920943c 100644
--- a/src/main/webapp/js/weatherStationListMap.js
+++ b/src/main/webapp/js/weatherStationListMap.js
@@ -93,7 +93,7 @@ function initMap(center, zoomLevel, organizationId)
               popOverlay.setPosition(geometry.getCoordinates());
               poiDetails.popover('destroy');
               // Create the popup, showing it
-            poiDetails.popover({
+              poiDetails.popover({
                     animation: true,
                     trigger: 'manual',
                     html: true,
diff --git a/src/main/webapp/templates/observationMap.ftl b/src/main/webapp/templates/observationMap.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..ee8f322543d9c4a643dc86b4036b9ec46d2113f1
--- /dev/null
+++ b/src/main/webapp/templates/observationMap.ftl
@@ -0,0 +1,110 @@
+<#-- 
+  Copyright (c) 2016 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/>.
+--><#include "master.ftl">
+<#setting time_zone=user.organizationId.defaultTimeZone!"UTC">
+<#macro page_head>
+        <title>${i18nBundle.observationMap}</title>
+</#macro>
+<#macro custom_css>
+	<link rel="stylesheet" type="text/css" href="/css/3rdparty/ol.css"/>
+        <link rel="stylesheet" type="text/css" href="/css/3rdparty/chosen.min.css"/>
+</#macro>
+<#macro custom_js>
+        <script type="text/javascript" src="/js/3rdparty/ol.js"></script>
+        <script type="text/javascript" src="/js/observationMap.js"></script>
+        <script type="text/javascript" src="/js/3rdparty/moment.min.js"></script>
+        <script type="text/javascript" src="/js/3rdparty/chosen.jquery.min.js"></script>
+        <script type="text/javascript" src="/js/constants.js"></script>
+        <script type="text/javascript" src="/js/util.js"></script>
+        <script type="text/javascript" src="/js/environment.js"></script>
+        <script type="text/javascript">
+		$(document).ready(function() {
+			initMap(
+                            [${(defaultMapCenter.x?c)!"0"}, ${(defaultMapCenter.y?c)!"0"}],
+                            ${defaultMapZoom!"1"},
+                            ${organizationId!user.organizationId.organizationId},
+                            "${from?date}",
+                            "${to?date}",
+                            ${pestId!"null"},
+                            ${cropId!"null"},
+                            ${cropCategoryId!"null"}
+                        );
+                        
+                        
+                        
+                        initForm(${organizationId!user.organizationId.organizationId},
+                            ${pestId!"null"},
+                            ${cropId!"null"},
+                            ${cropCategoryId!"null"},
+                            function() {$(".chosen-select").chosen({allow_single_deselect: true});} // Must do this after select lists have been populated
+                        );
+                        
+                        
+                        
+		});
+        </script>
+</#macro>
+<#macro page_contents>
+<div class="singleBlockContainer">
+        <h1>${i18nBundle.observationMap}</h1>
+        
+        <#if messageKey?has_content>
+		<div class="alert alert-success">${i18nBundle(messageKey)}</div>
+	</#if>
+        <div id="observationMap" class="map">
+            <div id="popover"></div>
+        </div>
+        <form class="form-inline" method="get" action="/observation/map">
+            <div class="form-group">
+                <input class="form-control" type="date" name="from" value="${from?date}"/>
+            </div>
+            -
+            <div class="form-group">
+                <input class="form-control" type="date" name="to" value="${to?date}"/>
+            </div>
+            <div class="form-group">
+                    <select name="pestId" id="pestList" class="form-control chosen-select" data-placeholder="${i18nBundle.pestOrganismId}">
+                    </select>
+            </div>
+            <div class="form-group">
+                    <select name="cropId" id="cropList" class="form-control chosen-select" data-placeholder="${i18nBundle.cropOrganismId}">	
+                    </select>
+            </div>
+            <div class="form-group">
+                    <select name="cropCategoryId" id="cropCategoryList" class="form-control chosen-select" data-placeholder="${i18nBundle.cropCategoryIds}">	
+                    </select>
+            </div>
+            <button type="submit" class="btn btn-default">${i18nBundle.submit}</button>
+        </form>
+        <div class="table-responsive">
+            <table class="table table-striped" id="observationTable">
+                    <thead>
+                            <th>${i18nBundle.timeOfObservation}</th>
+                            <th>${i18nBundle.organism}</th>
+                            <th>${i18nBundle.cropOrganismId}</th>
+                            <th>${i18nBundle.location}</th>
+                            <th>${i18nBundle.observer}</th>
+                            <th>${i18nBundle.heading}</th>
+                            <th></th>
+                    </thead>
+                    <tbody id="observationTableBody">
+                    </tbody>
+            </table>
+        </div>
+</div>
+</#macro>
+<@page_html/>
diff --git a/src/main/webapp/templates/weatherstationList.ftl b/src/main/webapp/templates/weatherstationList.ftl
index c2900b81a3049a1dca51c3900f58523f0ae867e4..bef608bc80c5c2a41346c59068022412a7f60edc 100644
--- a/src/main/webapp/templates/weatherstationList.ftl
+++ b/src/main/webapp/templates/weatherstationList.ftl
@@ -19,7 +19,7 @@
         <title>${i18nBundle.weatherStations}</title>
 </#macro>
 <#macro custom_css>
-	<link rel="stylesheet" type="text/css" href="/css/3rdparty/ol.css"/ >
+	<link rel="stylesheet" type="text/css" href="/css/3rdparty/ol.css"/>
 </#macro>
 <#macro custom_js>
 	<script type="text/javascript" src="/js/3rdparty/ol.js"></script>
diff --git a/src/test/java/no/nibio/vips/logic/util/GISUtilTest.java b/src/test/java/no/nibio/vips/logic/util/GISUtilTest.java
index 9bd5c491fe948b90b23b88c285368bc96755512a..c36f8f6e5cfb1fbc77ce6ac9a22b318b9549aa3b 100644
--- a/src/test/java/no/nibio/vips/logic/util/GISUtilTest.java
+++ b/src/test/java/no/nibio/vips/logic/util/GISUtilTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 NIBIO <http://www.nibio.no/>. 
+ * Copyright (c) 2016 NIBIO <http://www.nibio.no/>. 
  * 
  * This file is part of VIPSLogic.
  * VIPSLogic is free software: you can redistribute it and/or modify
@@ -18,14 +18,18 @@
  */
 package no.nibio.vips.logic.util;
 
-import com.vividsolutions.jts.geom.Geometry;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 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.*;
+import org.wololo.geojson.Feature;
+import org.wololo.geojson.Geometry;
+import org.wololo.geojson.Point;
 
 /**
  *
@@ -58,11 +62,14 @@ public class GISUtilTest {
     @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\":{}}]}";
+        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\":{\"observationId\":12}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[10.704803466796873,59.64831609639066]},\"properties\":{\"observationId\":12}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[10.649871826171871,59.67051458978321],[10.791320800781248,59.67328836837126]]},\"properties\":{\"observationId\":12}}]}";
+        System.out.println("JSON=" + json);
         GISUtil instance = new GISUtil();
         //List<Geometry> expResult = null;
-        List<Geometry> geometries = instance.getGeometriesFromGeoJSON(json);
-        String result = instance.getGeoJSONFromGeometries(geometries);
+        List<com.vividsolutions.jts.geom.Geometry> geometries = instance.getGeometriesFromGeoJSON(json);
+        Map<String, Object> properties = new HashMap<>();
+        properties.put("observationId", 12);
+        String result = instance.getGeoJSONFromGeometries(geometries, properties);
         //System.out.println(result);
         assertEquals(json, result);
     }
diff --git a/src/test/java/no/nibio/vips/util/weather/YrWeatherForecastProviderTest.java b/src/test/java/no/nibio/vips/util/weather/YrWeatherForecastProviderTest.java
index 591d2488fcae9a8566b93619819651afcc198366..f563aa3da7ca121eda47fd73707ee65150d236c2 100644
--- a/src/test/java/no/nibio/vips/util/weather/YrWeatherForecastProviderTest.java
+++ b/src/test/java/no/nibio/vips/util/weather/YrWeatherForecastProviderTest.java
@@ -18,12 +18,15 @@
  */
 package no.nibio.vips.util.weather;
 
+import com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.geom.Point;
 import no.nibio.vips.util.weather.YrWeatherForecastProvider;
 import no.nibio.vips.util.weather.ParseWeatherDataException;
 import java.util.Collections;
 import java.util.List;
 import no.nibio.vips.entity.WeatherObservation;
 import no.nibio.vips.logic.entity.PointOfInterestWeatherStation;
+import no.nibio.vips.logic.util.GISUtil;
 import org.junit.After;
 import org.junit.AfterClass;
 import org.junit.Before;
@@ -64,9 +67,12 @@ public class YrWeatherForecastProviderTest {
         System.out.println("getWeatherForecasts");
         PointOfInterestWeatherStation weatherStation = new PointOfInterestWeatherStation();
         //Testing in Bosnia (Rodoc, Mostar)
-        weatherStation.setAltitude(67.0);
+        GISUtil gisUtil = new GISUtil();
+        Point p = gisUtil.createPointWGS84(new Coordinate(17.8094, 43.301, 67.0));
+        /*weatherStation.setAltitude(67.0);
         weatherStation.setLongitude(17.8094);
-        weatherStation.setLatitude(43.301);
+        weatherStation.setLatitude(43.301);*/
+        weatherStation.setGisGeom(p);
         YrWeatherForecastProvider instance = new YrWeatherForecastProvider();
         try
         {