diff --git a/pom.xml b/pom.xml
index d6bb97210b869193e1649933490888507e068ebb..dc4ab6601e363e42382df5858cbb61401495ade9 100755
--- a/pom.xml
+++ b/pom.xml
@@ -52,12 +52,6 @@
             <artifactId>flyway-core</artifactId>
             <version>4.2.0</version>
         </dependency>
-        <dependency>
-            <!--groupId>com.github.bjornharrtell</groupId-->
-            <groupId>org.wololo</groupId>
-            <artifactId>jts2geojson</artifactId>
-            <version>0.12.0</version>
-        </dependency>
         <dependency>
             <groupId>org.hibernate</groupId>
             <artifactId>hibernate-spatial</artifactId>
@@ -88,6 +82,12 @@
             <version>3.6.3.Final</version>
             <scope>provided</scope>
         </dependency>
+        <dependency>
+            <groupId>org.jboss.resteasy</groupId>
+            <artifactId>resteasy-jackson2-provider</artifactId>
+            <version>3.6.3.Final</version>
+            <scope>provided</scope>
+        </dependency>
         <dependency>
             <groupId>no.nibio.vips.common</groupId>
             <artifactId>VIPSCommon</artifactId>
@@ -198,6 +198,11 @@
             <version>1.16.1</version>
             <type>jar</type>
         </dependency>
+        <dependency>
+            <groupId>org.wololo</groupId>
+            <artifactId>jts2geojson</artifactId>
+            <version>0.14.1</version>
+        </dependency>
         <dependency>
             <groupId>javax</groupId>
             <artifactId>javaee-web-api</artifactId>
diff --git a/src/main/java/no/nibio/vips/logic/controller/servlet/ObservationController.java b/src/main/java/no/nibio/vips/logic/controller/servlet/ObservationController.java
index 755bc567f09b2ef4f9f034ed9bfb204a365f7438..84fc06f83aeb5251fc18679a1c41eaf6b9649afc 100755
--- a/src/main/java/no/nibio/vips/logic/controller/servlet/ObservationController.java
+++ b/src/main/java/no/nibio/vips/logic/controller/servlet/ObservationController.java
@@ -46,6 +46,7 @@ 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.OrganizationGroup;
+import no.nibio.vips.logic.entity.PolygonService;
 import no.nibio.vips.logic.entity.VipsLogicRole;
 import no.nibio.vips.logic.entity.VipsLogicUser;
 import no.nibio.vips.logic.i18n.SessionLocaleUtil;
@@ -234,7 +235,7 @@ public class ObservationController extends HttpServlet {
                 
                 Collections.sort(observations);
                 Collections.reverse(observations);
-                observations.forEach(o->System.out.println(o));
+                //observations.forEach(o->System.out.println(o));
                 List<Organism> allPests = em.createNamedQuery("Organism.findAllPests").getResultList();
                 request.setAttribute("shortcuts", shortcuts);
                 request.setAttribute("allPests", SessionControllerGetter.getOrganismBean().sortOrganismsByLocalName(allPests, SessionLocaleUtil.getCurrentLocale(request).getLanguage()));
@@ -282,7 +283,12 @@ public class ObservationController extends HttpServlet {
                             allCrops = em.createNamedQuery("Organism.findAllCrops").getResultList();
                         }
                         
+                        // Get the polygonServices
+                        List<PolygonService> polygonServices = SessionControllerGetter.getObservationBean().getPolygonServicesForOrganization(user.getOrganization_id());
+                        
                         request.setAttribute("observation", observation);
+                        request.setAttribute("polygonServices", polygonServices);
+                        request.setAttribute("locationVisibilityFormValue", ObservationController.LOCATION_VISIBILITY_FORM_VALUE_PUBLIC);
                         request.setAttribute("shortcut", request.getParameter("observationFormShortcutId") != null ?
                                     em.find(ObservationFormShortcut.class, Integer.valueOf(request.getParameter("observationFormShortcutId")))
                                     : null
@@ -328,7 +334,10 @@ public class ObservationController extends HttpServlet {
                     {
                         Integer observationId = Integer.valueOf(request.getParameter("observationId"));
                         Observation observation = SessionControllerGetter.getObservationBean().getObservation(observationId);//em.find(Observation.class, observationId);
+                        List<PolygonService> polygonServices = SessionControllerGetter.getObservationBean().getPolygonServicesForOrganization(user.getOrganization_id());
+                        request.setAttribute("locationVisibilityFormValue", this.getLocationVisibilityFormValue(observation));
                         request.setAttribute("observation", observation);
+                        request.setAttribute("polygonServices", polygonServices);
                         request.setAttribute("noBroadcast", request.getParameter("noBroadcast") != null);
                         request.setAttribute("mapLayers", SessionControllerGetter.getUserBean().getMapLayerJSONForUser(user));
                         //System.out.println(observation.getGeoinfo());
@@ -442,7 +451,7 @@ public class ObservationController extends HttpServlet {
                                             : formValidation.getFormField("observationData").getWebValue()
                             );
                             observation.setIsQuantified(formValidation.getFormField("isQuantified").getWebValue() != null);
-                            observation.setLocationIsPrivate(formValidation.getFormField("locationIsPrivate").getWebValue() != null);
+                            this.setObservationLocationVisibility(observation, formValidation.getFormField("locationVisibility").getWebValue());
                             observation.setBroadcastMessage(formValidation.getFormField("broadcastMessage").getWebValue() != null);
 
                             boolean sendNotification = false;    
@@ -512,7 +521,9 @@ public class ObservationController extends HttpServlet {
                             
                             // All transactions finished, we can send notifications
                             // if conditions are met
-                            if(sendNotification)
+                            if(sendNotification && !
+                                    (System.getProperty("DISABLE_MESSAGING_SYSTEM") != null && System.getProperty("DISABLE_MESSAGING_SYSTEM").equals("true"))
+                                    )
                             {
                                 SessionControllerGetter.getMessagingBean().sendUniversalMessage(observation);
                             }
@@ -670,6 +681,39 @@ public class ObservationController extends HttpServlet {
             }
         }
     } 
+    
+    public static final String LOCATION_VISIBILITY_FORM_VALUE_PRIVATE = "private";
+    public static final String LOCATION_VISIBILITY_FORM_VALUE_PUBLIC = "public";
+    public static final String LOCATION_VISIBILITY_FORM_VALUE_MASK_PREFIX = "mask_";
+    
+    private String getLocationVisibilityFormValue(Observation observation)
+    {
+        // Private is private, no matter what
+        if(observation.getLocationIsPrivate())
+        {
+            return ObservationController.LOCATION_VISIBILITY_FORM_VALUE_PRIVATE;
+        }
+        // Public can either be completely public or masked by a polygon layer
+        // e.g. county borders
+        if(observation.getPolygonService() == null)
+        {
+            return ObservationController.LOCATION_VISIBILITY_FORM_VALUE_PUBLIC;
+        }
+        return ObservationController.LOCATION_VISIBILITY_FORM_VALUE_MASK_PREFIX + observation.getPolygonService().getPolygonServiceId();
+    }
+    
+    private void setObservationLocationVisibility(Observation observation, String formValue)
+    {
+        // Private is private, no matter what
+        observation.setLocationIsPrivate(formValue.equals(ObservationController.LOCATION_VISIBILITY_FORM_VALUE_PRIVATE));
+        observation.setPolygonService(
+                // If private or public, set no polygon service
+                formValue.equals(ObservationController.LOCATION_VISIBILITY_FORM_VALUE_PRIVATE) || formValue.equals(ObservationController.LOCATION_VISIBILITY_FORM_VALUE_PUBLIC)
+                ? null
+                // Otherwise, set selected polygon service
+                : em.find(PolygonService.class, Integer.valueOf(formValue.split("_")[1]))              
+        );
+    }
 
     // <editor-fold defaultstate="collapsed" desc="HttpServlet methods. Click on the + sign on the left to edit the code.">
     /** 
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 eefdc2053b7d72cc9e604cb0dc37444e786e3d05..253d85b352e8efa899724c0fdc88e187ffe75bec 100755
--- a/src/main/java/no/nibio/vips/logic/controller/session/ObservationBean.java
+++ b/src/main/java/no/nibio/vips/logic/controller/session/ObservationBean.java
@@ -49,6 +49,7 @@ 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;
+import no.nibio.vips.logic.entity.PolygonService;
 import no.nibio.vips.logic.entity.VipsLogicUser;
 import no.nibio.vips.logic.util.SessionControllerGetter;
 import no.nibio.vips.logic.util.SystemTime;
@@ -464,7 +465,7 @@ public class ObservationBean {
                 .getResultList();
     }
 
-    private List<Observation> getObservationsWithLocations(List<Observation> observations) {
+    public List<Observation> getObservationsWithLocations(List<Observation> observations) {
          Set<Integer> locationPointOfInterestIds = new HashSet<>();
          observations.stream().filter((o) -> (o.getLocationPointOfInterestId() != null)).forEach((o) -> {
              locationPointOfInterestIds.add(o.getLocationPointOfInterestId());
@@ -607,7 +608,7 @@ public class ObservationBean {
         List<Observation> retVal = new ArrayList<>();
         if(! observations.isEmpty())
         {
-            //start = new Date();
+            //Date start = new Date();
             retVal = this.getObservationsWithGeoInfo(observations);
             //System.out.println("Finding geoinfo took " + (new Date().getTime() - start.getTime()) + " milliseconds");
             //start = new Date();
@@ -742,8 +743,10 @@ public class ObservationBean {
                 
     }
 
-    
+    public List<PolygonService> getPolygonServicesForOrganization(Integer organizationId) {
+        return em.createNativeQuery("SELECT * FROM polygon_service p WHERE p.polygon_service_id IN (SELECT polygon_service_id FROM public.organization_polygon_service WHERE organization_id=:organizationId)", PolygonService.class)
+                .setParameter("organizationId", organizationId)
+                .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 314c78198eb9093acceebbd5588d5c23e868c32d..321dfbc3c0a652ff564c95b4651f7b11a7e977d9 100755
--- a/src/main/java/no/nibio/vips/logic/entity/Observation.java
+++ b/src/main/java/no/nibio/vips/logic/entity/Observation.java
@@ -104,6 +104,7 @@ public class Observation implements Serializable, no.nibio.vips.observation.Obse
     private Boolean isQuantified;
     private Boolean broadcastMessage;
     private Boolean locationIsPrivate;
+    private PolygonService polygonService;
     
     private String observationDataSchema;
     
@@ -306,6 +307,17 @@ public class Observation implements Serializable, no.nibio.vips.observation.Obse
         this.organism = organism;
     }
     
+    @JoinColumn(name = "polygon_service_id", referencedColumnName = "polygon_service_id")
+    @ManyToOne
+    public PolygonService getPolygonService(){
+        return this.polygonService;
+    }
+    
+    public void setPolygonService(PolygonService polygonService)
+    {
+        this.polygonService = polygonService;
+    }
+    
     /*
     @Override
     @Transient
@@ -610,6 +622,11 @@ public class Observation implements Serializable, no.nibio.vips.observation.Obse
         this.lastEditedByUser = lastEditedByUser;
     }
 
+    /**
+     * Simplifies the public JSON object
+     * @param locale
+     * @return 
+     */
     public ObservationListItem getListItem(String locale)
     {
         // If geoInfo is from POI, need to add observationId
@@ -624,7 +641,10 @@ public class Observation implements Serializable, no.nibio.vips.observation.Obse
                 this.getOrganism().getLocalName(locale),
                 this.getCropOrganismId(),
                 this.getCropOrganism().getLocalName(locale),
-                this.location != null ? this.location.getGeoJSON() : this.getGeoinfo(),
+                // Specific geoInfo trumps location. This is to be interpreted 
+                // as that the observation has been geographically masked by
+                // choice of the observer
+                this.location != null && this.geoinfo == null ? this.location.getGeoJSON() : this.getGeoinfo(),
                 this.getObservationHeading(),
                 this.getBroadcastMessage(),
                 this.getLocationIsPrivate()
diff --git a/src/main/java/no/nibio/vips/logic/entity/PolygonService.java b/src/main/java/no/nibio/vips/logic/entity/PolygonService.java
new file mode 100644
index 0000000000000000000000000000000000000000..609bd3a7d9bd124c2f8f088a41c5c0e582c0365b
--- /dev/null
+++ b/src/main/java/no/nibio/vips/logic/entity/PolygonService.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2019 NIBIO <http://www.nibio.no/>. 
+ * 
+ * This file is part of VIPSLogic.
+ * VIPSLogic is free software: you can redistribute it and/or modify
+ * it under the terms of the NIBIO Open Source License as published by 
+ * NIBIO, either version 1 of the License, or (at your option) any
+ * later version.
+ * 
+ * VIPSLogic is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * NIBIO Open Source License for more details.
+ * 
+ * You should have received a copy of the NIBIO Open Source License
+ * along with VIPSLogic.  If not, see <http://www.nibio.no/licenses/>.
+ * 
+ */
+
+package no.nibio.vips.logic.entity;
+
+import java.io.Serializable;
+import javax.persistence.Basic;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.Table;
+import javax.validation.constraints.Size;
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * @copyright 2019 <a href="http://www.nibio.no/">NIBIO</a>
+ * @author Tor-Einar Skog <tor-einar.skog@nibio.no>
+ */
+@Entity
+@Table(name = "polygon_service")
+@XmlRootElement
+@NamedQueries({
+    @NamedQuery(name = "PolygonService.findAll", query = "SELECT p FROM PolygonService p"),
+    @NamedQuery(name = "PolygonService.findByPolygonServiceId", query = "SELECT p FROM PolygonService p WHERE p.polygonServiceId = :polygonServiceId"),
+    @NamedQuery(name = "PolygonService.findByPolygonServiceName", query = "SELECT p FROM PolygonService p WHERE p.polygonServiceName = :polygonServiceName"),
+    @NamedQuery(name = "PolygonService.findByDescription", query = "SELECT p FROM PolygonService p WHERE p.description = :description"),
+    @NamedQuery(name = "PolygonService.findByGisSearchUrlTemplate", query = "SELECT p FROM PolygonService p WHERE p.gisSearchUrlTemplate = :gisSearchUrlTemplate"),
+    @NamedQuery(name = "PolygonService.findByEpsg", query = "SELECT p FROM PolygonService p WHERE p.epsg = :epsg")})
+public class PolygonService implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    @Basic(optional = false)
+    @Column(name = "polygon_service_id")
+    private Integer polygonServiceId;
+    @Size(max = 255)
+    @Column(name = "polygon_service_name")
+    private String polygonServiceName;
+    @Size(max = 2147483647)
+    @Column(name = "description")
+    private String description;
+    @Size(max = 1024)
+    @Column(name = "gis_search_url_template")
+    private String gisSearchUrlTemplate;
+    @Column(name = "epsg")
+    private Integer epsg;
+
+    public PolygonService() {
+    }
+
+    public PolygonService(Integer polygonServiceId) {
+        this.polygonServiceId = polygonServiceId;
+    }
+
+    public Integer getPolygonServiceId() {
+        return polygonServiceId;
+    }
+
+    public void setPolygonServiceId(Integer polygonServiceId) {
+        this.polygonServiceId = polygonServiceId;
+    }
+
+    public String getPolygonServiceName() {
+        return polygonServiceName;
+    }
+
+    public void setPolygonServiceName(String polygonServiceName) {
+        this.polygonServiceName = polygonServiceName;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public String getGisSearchUrlTemplate() {
+        return gisSearchUrlTemplate;
+    }
+
+    public void setGisSearchUrlTemplate(String gisSearchUrlTemplate) {
+        this.gisSearchUrlTemplate = gisSearchUrlTemplate;
+    }
+
+    public Integer getEpsg() {
+        return epsg;
+    }
+
+    public void setEpsg(Integer epsg) {
+        this.epsg = epsg;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 0;
+        hash += (polygonServiceId != null ? polygonServiceId.hashCode() : 0);
+        return hash;
+    }
+
+    @Override
+    public boolean equals(Object object) {
+        // TODO: Warning - this method won't work in the case the id fields are not set
+        if (!(object instanceof PolygonService)) {
+            return false;
+        }
+        PolygonService other = (PolygonService) object;
+        if ((this.polygonServiceId == null && other.polygonServiceId != null) || (this.polygonServiceId != null && !this.polygonServiceId.equals(other.polygonServiceId))) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return "no.nibio.vips.logic.entity.PolygonService[ polygonServiceId=" + polygonServiceId + " ]";
+    }
+
+}
diff --git a/src/main/java/no/nibio/vips/logic/entity/rest/ObservationListItem.java b/src/main/java/no/nibio/vips/logic/entity/rest/ObservationListItem.java
index 7193b73a1c70fb53d791206fec8a2dc9ca4c9e32..41e9b94094b7ab2998f1f77f5c7c8f120feb4df0 100644
--- a/src/main/java/no/nibio/vips/logic/entity/rest/ObservationListItem.java
+++ b/src/main/java/no/nibio/vips/logic/entity/rest/ObservationListItem.java
@@ -22,6 +22,7 @@ package no.nibio.vips.logic.entity.rest;
 import java.util.Date;
 
 /**
+ * A JSON friendly, public representation of the Observation
  * @copyright 2018 <a href="http://www.nibio.no/">NIBIO</a>
  * @author Tor-Einar Skog <tor-einar.skog@nibio.no>
  */
diff --git a/src/main/java/no/nibio/vips/logic/entity/rest/PointMappingRequest.java b/src/main/java/no/nibio/vips/logic/entity/rest/PointMappingRequest.java
new file mode 100644
index 0000000000000000000000000000000000000000..3a5750ad1179630147694f74dab86fd16739d9df
--- /dev/null
+++ b/src/main/java/no/nibio/vips/logic/entity/rest/PointMappingRequest.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2019 NIBIO <http://www.nibio.no/>. 
+ * 
+ * This file is part of VIPSLogic.
+ * VIPSLogic is free software: you can redistribute it and/or modify
+ * it under the terms of the NIBIO Open Source License as published by 
+ * NIBIO, either version 1 of the License, or (at your option) any
+ * later version.
+ * 
+ * VIPSLogic is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * NIBIO Open Source License for more details.
+ * 
+ * You should have received a copy of the NIBIO Open Source License
+ * along with VIPSLogic.  If not, see <http://www.nibio.no/licenses/>.
+ * 
+ */
+
+package no.nibio.vips.logic.entity.rest;
+
+import java.io.Serializable;
+import java.util.List;
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * @copyright 2019 <a href="http://www.nibio.no/">NIBIO</a>
+ * @author Tor-Einar Skog <tor-einar.skog@nibio.no>
+ */
+@XmlRootElement
+public class PointMappingRequest implements Serializable{
+    private List<ReferencedPoint> points;
+
+    /**
+     * @return the points
+     */
+    public List<ReferencedPoint> getPoints() {
+        return points;
+    }
+
+    /**
+     * @param points the points to set
+     */
+    public void setPoints(List<ReferencedPoint> points) {
+        this.points = points;
+    }
+    
+    
+}
diff --git a/src/main/java/no/nibio/vips/logic/entity/rest/PointMappingResponse.java b/src/main/java/no/nibio/vips/logic/entity/rest/PointMappingResponse.java
new file mode 100644
index 0000000000000000000000000000000000000000..820b483570667d02feb0cb0a8824d97c3775f072
--- /dev/null
+++ b/src/main/java/no/nibio/vips/logic/entity/rest/PointMappingResponse.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2019 NIBIO <http://www.nibio.no/>. 
+ * 
+ * This file is part of VIPSLogic.
+ * VIPSLogic is free software: you can redistribute it and/or modify
+ * it under the terms of the NIBIO Open Source License as published by 
+ * NIBIO, either version 1 of the License, or (at your option) any
+ * later version.
+ * 
+ * VIPSLogic is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * NIBIO Open Source License for more details.
+ * 
+ * You should have received a copy of the NIBIO Open Source License
+ * along with VIPSLogic.  If not, see <http://www.nibio.no/licenses/>.
+ * 
+ */
+
+package no.nibio.vips.logic.entity.rest;
+
+import java.util.Map;
+import org.wololo.geojson.FeatureCollection;
+
+
+
+
+/**
+ * @copyright 2019 <a href="http://www.nibio.no/">NIBIO</a>
+ * @author Tor-Einar Skog <tor-einar.skog@nibio.no>
+ */
+public class PointMappingResponse {
+    private Map[] mapping;
+    private FeatureCollection featureCollection;
+
+    /**
+     * @return the mapping
+     */
+    public Map[] getMapping() {
+        return mapping;
+    }
+
+    /**
+     * @param mapping the mapping to set
+     */
+    public void setMapping(Map[] mapping) {
+        this.mapping = mapping;
+    }
+
+    /**
+     * @return the featureCollection
+     */
+    public FeatureCollection getFeatureCollection() {
+        return featureCollection;
+    }
+
+    /**
+     * @param featureCollection the featureCollection to set
+     */
+    public void setFeatureCollection(FeatureCollection featureCollection) {
+        this.featureCollection = featureCollection;
+    }
+}
diff --git a/src/main/java/no/nibio/vips/logic/entity/rest/ReferencedPoint.java b/src/main/java/no/nibio/vips/logic/entity/rest/ReferencedPoint.java
new file mode 100644
index 0000000000000000000000000000000000000000..0fb89cf6595d3cc7766ce7eb2ef149b204276582
--- /dev/null
+++ b/src/main/java/no/nibio/vips/logic/entity/rest/ReferencedPoint.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2019 NIBIO <http://www.nibio.no/>. 
+ * 
+ * This file is part of VIPSLogic.
+ * VIPSLogic is free software: you can redistribute it and/or modify
+ * it under the terms of the NIBIO Open Source License as published by 
+ * NIBIO, either version 1 of the License, or (at your option) any
+ * later version.
+ * 
+ * VIPSLogic is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * NIBIO Open Source License for more details.
+ * 
+ * You should have received a copy of the NIBIO Open Source License
+ * along with VIPSLogic.  If not, see <http://www.nibio.no/licenses/>.
+ * 
+ */
+
+package no.nibio.vips.logic.entity.rest;
+
+/**
+ * @copyright 2019 <a href="http://www.nibio.no/">NIBIO</a>
+ * @author Tor-Einar Skog <tor-einar.skog@nibio.no>
+ */
+public class ReferencedPoint {
+    private String id;
+    private Double lon;
+    private Double lat;
+
+    /**
+     * @return the id
+     */
+    public String getId() {
+        return id;
+    }
+
+    /**
+     * @param id the id to set
+     */
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    /**
+     * @return the lon
+     */
+    public Double getLon() {
+        return lon;
+    }
+
+    /**
+     * @param lon the lon to set
+     */
+    public void setLon(Double lon) {
+        this.lon = lon;
+    }
+
+    /**
+     * @return the lat
+     */
+    public Double getLat() {
+        return lat;
+    }
+
+    /**
+     * @param lat the lat to set
+     */
+    public void setLat(Double lat) {
+        this.lat = lat;
+    }
+}
diff --git a/src/main/java/no/nibio/vips/logic/service/ObservationService.java b/src/main/java/no/nibio/vips/logic/service/ObservationService.java
index 7e84c3295bcc402fd6f6566800f4554e0f5bfe64..e2805e4cd64081db98f98e0fc8235a2015d6dbc4 100755
--- a/src/main/java/no/nibio/vips/logic/service/ObservationService.java
+++ b/src/main/java/no/nibio/vips/logic/service/ObservationService.java
@@ -19,14 +19,22 @@
 
 package no.nibio.vips.logic.service;
 
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
 import com.webcohesion.enunciate.metadata.Facet;
 import java.io.IOException;
 import java.net.URI;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
+import java.util.ArrayList;
 import java.util.Date;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.UUID;
+import java.util.function.Predicate;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 import java.util.stream.Collectors;
 import javax.servlet.http.HttpServletRequest;
 import javax.ws.rs.Consumes;
@@ -37,15 +45,28 @@ import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
 import javax.ws.rs.QueryParam;
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
 import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
+import no.nibio.vips.logic.entity.Gis;
 import no.nibio.vips.logic.entity.Observation;
+import no.nibio.vips.logic.entity.PolygonService;
 import no.nibio.vips.logic.entity.VipsLogicRole;
 import no.nibio.vips.logic.entity.VipsLogicUser;
+import no.nibio.vips.logic.entity.rest.PointMappingRequest;
+import no.nibio.vips.logic.entity.rest.PointMappingResponse;
+import no.nibio.vips.logic.entity.rest.ReferencedPoint;
 import no.nibio.vips.logic.util.GISEntityUtil;
 import no.nibio.vips.logic.util.Globals;
 import no.nibio.vips.logic.util.SessionControllerGetter;
 import org.jboss.resteasy.annotations.GZIP;
+import org.wololo.geojson.Feature;
+import org.wololo.geojson.FeatureCollection;
+import org.wololo.geojson.Polygon;
 
 /**
  * @copyright 2016 <a href="http://www.nibio.no/">NIBIO</a>
@@ -131,6 +152,7 @@ public class ObservationService {
         {
             user = SessionControllerGetter.getUserBean().findVipsLogicUser(UUID.fromString(userUUID));
         }
+        //System.out.println("getFilteredObservationListItems");
         return Response.ok().entity(
                 getFilteredObservationsFromBackend(
                         organizationId,
@@ -154,6 +176,7 @@ public class ObservationService {
             String toStr
     )
     {
+        //System.out.println("getFilteredObservationsFromBackend");
         SimpleDateFormat format = new SimpleDateFormat(Globals.defaultDateFormat);
         //TODO Set correct timeZone!!!
         Date from = null;
@@ -306,8 +329,44 @@ public class ObservationService {
     @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();
+    public Response getObservation(
+            @PathParam("observationId") Integer observationId,
+            @QueryParam("userUUID") String userUUID
+    ){
+        // Observation needs to be masked here as well, or does it create trouble for VIPSLogic observation admin?
+        Observation o = SessionControllerGetter.getObservationBean().getObservation(observationId);
+        VipsLogicUser user = (VipsLogicUser) httpServletRequest.getSession().getAttribute("user");
+        if(user == null && userUUID != null)
+        {
+            user = SessionControllerGetter.getUserBean().findVipsLogicUser(UUID.fromString(userUUID));
+        }
+        // Modification of location information:
+        // 1) If location is private, only the owner or super users/org admins may view them
+        // slett all geoInfo
+        if(user == null || (! user.isSuperUser() && ! user.isOrganizationAdmin()))
+        {
+            // Hide completely for all users except super and orgadmin
+            if(o.getLocationIsPrivate() && (user == null || ! o.getUserId().equals(user.getUserId()))) 
+            {
+                o.setGeoinfos(null);
+            }
+            // This means the user wants to hide the exact location,
+            // so mask for all users except super and orgadmin
+            else if(o.getPolygonService() != null) 
+            {
+                //System.out.println("Masking observation");
+                List<Observation> intermediary = new ArrayList<>();
+                intermediary.add(o);
+                intermediary = this.maskObservations(o.getPolygonService(), 
+                        SessionControllerGetter.getObservationBean().getObservationsWithLocations(
+                                SessionControllerGetter.getObservationBean().getObservationsWithGeoInfo(intermediary)
+                        )
+                    );
+                o = intermediary.get(0);
+            }
+        }
+        
+        return Response.ok().entity(o).build();
     }
     
     /**
@@ -397,20 +456,116 @@ public class ObservationService {
 
     private List<Observation> getFilteredObservationsFromBackend(Integer organizationId, Integer pestId, Integer cropId, List<Integer> cropCategoryId, String fromStr, String toStr, VipsLogicUser user) {
         List<Observation> filteredObservations = this.getFilteredObservationsFromBackend(organizationId, pestId, cropId, cropCategoryId, fromStr, toStr);
-        // If user is not logged in, return only the publicly available observations
-        if(user == null)
+        //filteredObservations.forEach(o->System.out.println(o.getObservationId()));
+        // If superuser or orgadmin: Return everything, unchanged, uncensored
+        if(user != null && (user.isSuperUser() || user.isOrganizationAdmin()))
         {
-            return filteredObservations.stream().filter(obs->obs.getBroadcastMessage()).collect(Collectors.toList());
+            return filteredObservations;
         }
-        // Else: If superuser: Return everything
-        if(user.isSuperUser() || user.isOrganizationAdmin())
+        List<Observation> retVal = filteredObservations.stream().filter(obs->obs.getBroadcastMessage()).collect(Collectors.toList());
+        //retVal.forEach(o->System.out.println(o.getObservationId()));
+        retVal = this.maskObservations(retVal);
+        //retVal.forEach(o->System.out.println(o.getObservationId()));
+        // If user is not logged in, return only the publicly available observations
+        if(user == null)
         {
-            return filteredObservations;
+            return retVal;
         }
         // Else: This is a registered user without special privileges. Show public observations + user's own
-        List<Observation> retVal = filteredObservations.stream().filter(obs->obs.getBroadcastMessage()).collect(Collectors.toList());
         retVal.addAll(SessionControllerGetter.getObservationBean().getObservationsForUser(user));
         return retVal;
     }
+
+    /**
+     * Runs through the observations, check each to see if it should be masked
+     * (for privacy reasons) through a polygon service
+     * @param observations
+     * @return 
+     */
+    private List<Observation> maskObservations(List<Observation> observations) {
+        //System.out.println("maskObservations(List<Observation> observations)");
+        
+        // Placing all observations with a polygon service in the correct bucket.
+        Map<PolygonService, List<Observation>> registeredPolygonServicesInObservationList = new HashMap<>();
+        observations.stream().filter((obs) -> (!obs.getLocationIsPrivate() && obs.getPolygonService() != null)).forEachOrdered((obs) -> {
+            List<Observation> obsWithPolyServ = registeredPolygonServicesInObservationList.getOrDefault(obs.getPolygonService(), new ArrayList<>());
+            obsWithPolyServ.add(obs);
+            registeredPolygonServicesInObservationList.put(obs.getPolygonService(), obsWithPolyServ);
+        });
+        
+        // No buckets filled = No masking needed, return list unmodified
+        if(registeredPolygonServicesInObservationList.isEmpty())
+        {
+            return observations; 
+        }
+        else
+        {
+            // Loop through, mask
+            Map<Integer,Observation> maskedObservations = new HashMap<>();
+            registeredPolygonServicesInObservationList.keySet().forEach((pService) -> {
+                this.maskObservations(pService, registeredPolygonServicesInObservationList.get(pService))
+                        .forEach(o->maskedObservations.put(o.getObservationId(), o));
+            });
+            
+            // Adding the rest of the observations (the ones that don't need masking)
+            observations.stream().filter(o->maskedObservations.get(o.getObservationId())==null).forEach(o->maskedObservations.put(o.getObservationId(), o));
+            return new ArrayList(maskedObservations.values());
+        }
+    }
+    
+    private List<Observation> maskObservations(PolygonService polygonService, List<Observation> observations)
+    {
+        //observations.forEach(o->System.out.println(o.getObservationId()));
+        Client client = ClientBuilder.newClient();
+        WebTarget target = client.target(polygonService.getGisSearchUrlTemplate());
+        List<ReferencedPoint> points = observations.stream()
+                .filter(obs -> (obs.getGeoinfos()!=null && !obs.getGeoinfos().isEmpty()) || obs.getLocation() != null)
+                .map(obs->{
+                    ReferencedPoint rp = new ReferencedPoint();
+                    rp.setId(String.valueOf(obs.getObservationId()));
+                    if(obs.getGeoinfos() != null)
+                    {
+                        rp.setLon(obs.getGeoinfos().get(0).getGisGeom().getCoordinate().x);
+                        rp.setLat(obs.getGeoinfos().get(0).getGisGeom().getCoordinate().y);
+                    }
+                    else
+                    {
+                        rp.setLon(obs.getLocation().getLongitude());
+                        rp.setLat(obs.getLocation().getLatitude());
+                    }
+                    return rp;
+        }).collect(Collectors.toList());
+        /*System.out.println("maskobservations - target.request() about to be called");
+        ObjectMapper oMapper = new ObjectMapper();
+        try {
+            System.out.println(oMapper.writeValueAsString(Entity.entity(points.toArray(new ReferencedPoint[points.size()]), MediaType.APPLICATION_JSON)));
+        } catch (JsonProcessingException ex) {
+            Logger.getLogger(ObservationService.class.getName()).log(Level.SEVERE, null, ex);
+        }*/
+        PointMappingResponse response = target.request(MediaType.APPLICATION_JSON)
+                .post(Entity.entity(points.toArray(new ReferencedPoint[points.size()]), MediaType.APPLICATION_JSON), PointMappingResponse.class);
+        // We need to loop through the observations and find corresponding featurecollections and replace those
+        Map<Integer, Feature> indexedPolygons = new HashMap<>();
+        for(Feature feature:response.getFeatureCollection().getFeatures())
+        {
+            indexedPolygons.put((Integer)feature.getProperties().get("id"), feature);
+        }
+        GISEntityUtil gisEntityUtil = new GISEntityUtil();
+        for(Map mapping:response.getMapping())
+        {
+            Integer observationId = Integer.valueOf((String) mapping.get("id"));
+            Integer borderId = (Integer) mapping.get("borderid");
+            observations.stream().filter((o) -> (o.getObservationId().equals(observationId))).forEachOrdered((o) -> {
+                Gis polygon = gisEntityUtil.getGisFromFeature(indexedPolygons.get(borderId));
+                List<Gis> gis = new ArrayList<>();
+                gis.add(polygon);
+                o.setGeoinfos(gis);
+                o.setLocation(null);
+                o.setLocationPointOfInterestId(null);
+            });
+        }
+        
+        return observations;
+    }
     
 }
diff --git a/src/main/java/no/nibio/vips/logic/util/GISEntityUtil.java b/src/main/java/no/nibio/vips/logic/util/GISEntityUtil.java
index 6b9160c76ffb3e4230a69889ebe84473d3b7d142..a1e95eb46d43e7adab4e72bab32d549a0898858a 100755
--- a/src/main/java/no/nibio/vips/logic/util/GISEntityUtil.java
+++ b/src/main/java/no/nibio/vips/logic/util/GISEntityUtil.java
@@ -82,6 +82,14 @@ public class GISEntityUtil {
         return retVal;
     }
 
+    public Gis getGisFromFeature(Feature feature)
+    {
+        GeoJSONReader reader = new GeoJSONReader();
+        Gis gis = new Gis();
+        gis.setGisGeom(reader.read(feature.getGeometry()));
+        gis.getGisGeom().setSRID(GISUtil.DEFAULT_SRID);
+        return gis;
+    }
     
     /**
      * Converts a list of observations to a FeatureCollection
diff --git a/src/main/resources/db/migration/V5__Add_PolygonService.sql b/src/main/resources/db/migration/V5__Add_PolygonService.sql
new file mode 100644
index 0000000000000000000000000000000000000000..cba55e4dfdd66c50dd6f03f8225c415daa638082
--- /dev/null
+++ b/src/main/resources/db/migration/V5__Add_PolygonService.sql
@@ -0,0 +1,47 @@
+/* 
+ * Copyright (c) 2019 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/>.
+ * 
+ */
+
+/**
+ * Author:  Tor-Einar Skog <tor-einar.skog@nibio.no>
+ * Created: Aug 22, 2019
+ */
+
+-- ALTER TABLE public.observation
+--    DROP COLUMN polygon_service_id;
+
+-- DROP TABLE public.organization_polygon_service;
+
+-- DROP TABLE public.polygon_service;
+
+CREATE TABLE public.polygon_service (
+    polygon_service_id SERIAL PRIMARY KEY,
+    polygon_service_name VARCHAR(255),
+    description TEXT,
+    gis_search_url_template VARCHAR(1024),
+    EPSG INTEGER
+);
+
+CREATE TABLE public.organization_polygon_service (
+    organization_id INTEGER REFERENCES public.organization(organization_id),
+    polygon_service_id INTEGER REFERENCES public.polygon_service(polygon_service_id),
+    CONSTRAINT organization_polygon_service_pkey PRIMARY KEY (organization_id, polygon_service_id)
+);
+
+ALTER TABLE public.observation 
+    ADD COLUMN polygon_service_id INTEGER REFERENCES public.polygon_service(polygon_service_id);
diff --git a/src/main/webapp/formdefinitions/observationForm.json b/src/main/webapp/formdefinitions/observationForm.json
index 71b1eba94543beb8f7dbb8ea574a6c7842f203e8..652dd4e9b5e55d82104b62dcb438b15b484230c0 100755
--- a/src/main/webapp/formdefinitions/observationForm.json
+++ b/src/main/webapp/formdefinitions/observationForm.json
@@ -58,9 +58,10 @@
             "required" : false
         },
         {
-            "name" : "locationIsPrivate",
+            "name" : "locationVisibility",
             "dataType" : "STRING",
-            "required" : false
+            "fieldType" : "RADIO",
+            "required" : true
         },
         {
             "name" : "organizationGroupId",
diff --git a/src/main/webapp/js/validateForm.js b/src/main/webapp/js/validateForm.js
index 66e1763c6c08e6354c351b41e3c901216bdb1016..3fa7365ff1fb573ac887fb1aad9974474990874c 100755
--- a/src/main/webapp/js/validateForm.js
+++ b/src/main/webapp/js/validateForm.js
@@ -39,6 +39,7 @@ var dataTypes = {
 var fieldTypes= {
     TYPE_SELECT_SINGLE: "SELECT_SINGLE",
     TYPE_SELECT_MULTIPLE: "SELECT_MULTIPLE",
+    TYPE_RADIO: "RADIO",
     TYPE_INPUT: "INPUT"
 };
 
@@ -64,7 +65,7 @@ var emailReg = /^([\w-\.]+@([\w-]+\.)+[\w-]{2,4})?$/;
 
 /**
  * Validation function for all forms
- * @copyright 2013 <a href="http://www.nibio.no/">NIBIO</a>
+ * @copyright 2013-2019 <a href="http://www.nibio.no/">NIBIO</a>
  * @author Tor-Einar Skog <tor-einar.skog@nibio.no>
  * 
  * It depends on the following:
@@ -101,7 +102,6 @@ function validateForm(theForm, formDefinitionKey)
             continue;
         }
         if(!validateFieldActual(theForm[fieldDefinition.name], theForm, formDefinitionKey)){
-            //alert("Validation failed for " + fieldDefinition.name);
             isValid = false;
         }
     }
@@ -241,9 +241,16 @@ function getValidationOutputEl(fieldEl, theForm)
  */
 function validateFieldActual(fieldEl, theForm, formDefinitionKey)
 {
-    
     var webValue = fieldEl.value;
-    var fieldDefinition = getFieldDefinition(fieldEl.name, formDefinitions[formDefinitionKey !== null ? formDefinitionKey : theForm.id]);
+    // Determine the fieldName.
+    // If it's a RadioNodeList, get the name of the first radio element item
+    // Otherwise (AFAIK), it's the name of the field
+    var fieldName = fieldEl.name;
+    if(Object.prototype.toString.call(fieldEl).indexOf("RadioNodeList") >= 0)
+    {
+        fieldName = fieldEl.item(0).name;
+    }
+    var fieldDefinition = getFieldDefinition(fieldName, formDefinitions[formDefinitionKey !== null ? formDefinitionKey : theForm.id]);
     // Empty?
     if(webValue === null || webValue === "")
     {
@@ -258,7 +265,6 @@ function validateFieldActual(fieldEl, theForm, formDefinitionKey)
             return true;
         }
     }
-    
     // Single select field - check for nullValue
     if(fieldDefinition.fieldType === fieldTypes.TYPE_SELECT_SINGLE)
     {
@@ -302,6 +308,21 @@ function validateFieldActual(fieldEl, theForm, formDefinitionKey)
             return true;
         }
     }
+    
+    // Radio buttons
+    if(fieldDefinition.fieldType === fieldTypes.TYPE_RADIO)
+    {
+        if(fieldDefinition.required && fieldEl.value === "")
+        {
+            invalidizeField(fieldEl, theForm, getI18nMsg("fieldIsRequired",null));
+            return false;
+        }
+        else
+        {
+            validizeField(fieldEl, theForm);
+            return true;
+        }
+    }
 
     // STRINGS
     // Strings are checked for length
@@ -526,8 +547,8 @@ function evaluatePassword(passwordEl)
  */
 function invalidizeField(inputEl, theForm, message)
 {
-    // Make sure we don't try to manipulate hidden fields
-    if(inputEl.type === "hidden")
+    // Make sure we don't try to manipulate hidden fields or radio node lists
+    if(inputEl.type === "hidden" || Object.prototype.toString.call(inputEl).indexOf("RadioNodeList") >= 0)
     {
         alert(message);
         return;
@@ -544,8 +565,8 @@ function invalidizeField(inputEl, theForm, message)
  */
 function validizeField(inputEl, theForm)
 {
-    // Make sure we don't try to manipulate hidden fields
-    if(inputEl.type === "hidden")
+    // Make sure we don't try to manipulate hidden fields or radio node lists
+    if(inputEl.type === "hidden" || Object.prototype.toString.call(inputEl).indexOf("RadioNodeList") >= 0)
     {
         return;
     }
diff --git a/src/main/webapp/templates/observationForm.ftl b/src/main/webapp/templates/observationForm.ftl
index 44d3661a834920900a014347931891ada0e8563a..07ec5674597e9b1ec2cc7311eca1cd8a324eca03 100755
--- a/src/main/webapp/templates/observationForm.ftl
+++ b/src/main/webapp/templates/observationForm.ftl
@@ -600,15 +600,39 @@
 			     </select>
 			     <span class="help-block" id="${formId}_locationPointOfInterestId_validation"></span>
 			  </div>
-                          <div class="form-group">
-			  	<div class="checkbox">
-                                        <#if editAccess!="W" && observation.locationIsPrivate?has_content && observation.locationIsPrivate == true><input type="hidden" name="locationIsPrivate" value="true"/></#if>
-					<label>
-						<input type="checkbox" name="locationIsPrivate"<#if !observation.locationIsPrivate?has_content || observation.locationIsPrivate == false><#else>checked="checked"</#if> <#if editAccess!="W">disabled="disabled"</#if>/>
-					</label>
-					${i18nBundle.locationIsPrivate}
-			  	</div>
-			  </div>
+                          <#if editAccess!="W" && observation.locationIsPrivate?has_content && observation.locationIsPrivate == true>
+                                <input type="hidden" name="locationVisibility" value="private"/>
+                          <#else>
+                                <div class="form-group">
+                                    <div class="radio">
+                                              <label>
+                                                      <input type="radio" name="locationVisibility" value="public"
+                                                             <#if locationVisibilityFormValue == "public">checked="checked"</#if>
+                                                             />
+                                              </label>
+                                              ${i18nBundle.locationIsPublic}
+                                      </div>
+                                      <div class="radio">
+
+                                              <label>
+                                                      <input type="radio" name="locationVisibility" value="private"
+                                                             <#if locationVisibilityFormValue == "private">checked="checked"</#if>
+                                                             />
+                                              </label>
+                                              ${i18nBundle.locationIsPrivate}
+                                      </div>
+                                      <#list polygonServices as polygonService>
+                                          <div class="radio">
+                                                <label>
+                                                  <input type="radio" name="locationVisibility" value="mask_${polygonService.polygonServiceId}"
+                                                         <#if locationVisibilityFormValue == "mask_" + polygonService.polygonServiceId>checked="checked"</#if>
+                                                         />
+                                                  ${i18nBundle.maskObservationWith} ${polygonService.polygonServiceName}
+                                                </label>
+                                        </div>
+                                      </#list>
+                                </div>
+                          </#if>
                           <div class="form-group">
                                 <label for="organizationGroupId">${i18nBundle.availableFor} ${i18nBundle.organizationGroupList?lower_case}</label>
                                 <select class="form-control chosen-select" name="organizationGroupId" multiple="multiple" data-placeholder="${i18nBundle.all}"  <#if editAccess!="W">readonly="readonly"</#if>>
diff --git a/src/test/java/no/nibio/vips/logic/entity/rest/PointMappingRequestTest.java b/src/test/java/no/nibio/vips/logic/entity/rest/PointMappingRequestTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..79d8899f586e8be8b69dd960e6b60ef319621f14
--- /dev/null
+++ b/src/test/java/no/nibio/vips/logic/entity/rest/PointMappingRequestTest.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2019 NIBIO <http://www.nibio.no/>. 
+ * 
+ * This file is part of VIPSLogic.
+ * VIPSLogic is free software: you can redistribute it and/or modify
+ * it under the terms of the NIBIO Open Source License as published by 
+ * NIBIO, either version 1 of the License, or (at your option) any
+ * later version.
+ * 
+ * VIPSLogic is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * NIBIO Open Source License for more details.
+ * 
+ * You should have received a copy of the NIBIO Open Source License
+ * along with VIPSLogic.  If not, see <http://www.nibio.no/licenses/>.
+ * 
+ */
+package no.nibio.vips.logic.entity.rest;
+
+import java.util.ArrayList;
+import java.util.List;
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.MediaType;
+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.Polygon;
+
+/**
+ *
+ * @author treinar
+ */
+public class PointMappingRequestTest {
+    
+    public PointMappingRequestTest() {
+    }
+    
+    @BeforeClass
+    public static void setUpClass() {
+    }
+    
+    @AfterClass
+    public static void tearDownClass() {
+    }
+    
+    @Before
+    public void setUp() {
+    }
+    
+    @After
+    public void tearDown() {
+    }
+
+    /**
+     * Test of getPoints method, of class PointMappingRequest.
+     */
+    //@Test
+    public void testGetPoints() {
+        System.out.println("getPoints");
+        PointMappingRequest instance = new PointMappingRequest();
+        List<ReferencedPoint> expResult = null;
+        List<ReferencedPoint> result = instance.getPoints();
+        assertEquals(expResult, result);
+        // TODO review the generated test code and remove the default call to fail.
+        fail("The test case is a prototype.");
+    }
+
+    /**
+     * Test of setPoints method, of class PointMappingRequest.
+     */
+    //@Test
+    public void testSetPoints() {
+        System.out.println("setPoints");
+        List<ReferencedPoint> points = null;
+        PointMappingRequest instance = new PointMappingRequest();
+        instance.setPoints(points);
+        // TODO review the generated test code and remove the default call to fail.
+        fail("The test case is a prototype.");
+    }
+    
+    @Test
+    public void testClient() {
+        System.out.println("testClient");
+        List<ReferencedPoint> points = new ArrayList<>();
+        ReferencedPoint r1 = new ReferencedPoint();
+        r1.setId("test1");
+        r1.setLon(11.9028835076959);
+        r1.setLat(64.455583583917);
+        ReferencedPoint r2 = new ReferencedPoint();
+        r2.setId("test2");
+        r2.setLon(12.588734241028);
+        r2.setLat(64.5142545939044);
+        
+        points.add(r1);
+        points.add(r2);
+        
+        ReferencedPoint[] instance = points.toArray(new ReferencedPoint[points.size()]);
+        
+        Client client = ClientBuilder.newClient();
+        WebTarget target = client.target("http://proxy1utv.int.nibio.no/municipality_cache_ws/pointmapping/1/4326");
+
+        //PointMappingResponse response = 
+        PointMappingResponse response =    target.request(MediaType.APPLICATION_JSON)
+                .post(Entity.entity(instance, MediaType.APPLICATION_JSON), PointMappingResponse.class);//.toString();
+        
+        //System.out.println(((Polygon)response.getFeatureCollection().getFeatures()[0].getGeometry()).getCoordinates()[0].length);
+        
+        assertNotNull(response);
+        //System.out.println(Entity.entity(instance, MediaType.APPLICATION_JSON));
+        
+        //System.out.println(target.request(MediaType.APPLICATION_JSON)
+        //        .buildPost(Entity.entity(instance, MediaType.APPLICATION_JSON).));
+
+                
+        //System.out.println(response.getStatus());
+        
+           
+    }
+}