diff --git a/src/main/java/no/nibio/vips/logic/controller/servlet/ForecastConfigurationController.java b/src/main/java/no/nibio/vips/logic/controller/servlet/ForecastConfigurationController.java
index b7277f97d8fac27234bf423a10618e03372d33e9..044fc0d7fb006498aef4d770579ee95aea7ad2f4 100755
--- a/src/main/java/no/nibio/vips/logic/controller/servlet/ForecastConfigurationController.java
+++ b/src/main/java/no/nibio/vips/logic/controller/servlet/ForecastConfigurationController.java
@@ -18,6 +18,7 @@
 
 package no.nibio.vips.logic.controller.servlet;
 
+import com.fasterxml.jackson.databind.ObjectMapper;
 import com.ibm.icu.util.Calendar;
 import java.io.IOException;
 import java.text.ParseException;
@@ -37,12 +38,9 @@ import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import no.nibio.vips.logic.controller.session.ForecastBean;
 import no.nibio.vips.logic.controller.session.OrganismBean;
+import no.nibio.vips.logic.controller.session.PointOfInterestBean;
 import no.nibio.vips.logic.controller.session.UserBean;
-import no.nibio.vips.logic.entity.ForecastConfiguration;
-import no.nibio.vips.logic.entity.ModelInformation;
-import no.nibio.vips.logic.entity.Organization;
-import no.nibio.vips.logic.entity.VipsLogicRole;
-import no.nibio.vips.logic.entity.VipsLogicUser;
+import no.nibio.vips.logic.entity.*;
 import no.nibio.vips.logic.i18n.SessionLocaleUtil;
 import no.nibio.vips.logic.util.Globals;
 import no.nibio.vips.logic.util.SystemTime;
@@ -52,6 +50,9 @@ import no.nibio.web.forms.FormField;
 import no.nibio.web.forms.FormValidation;
 import no.nibio.web.forms.FormValidationException;
 import no.nibio.web.forms.FormValidator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.wololo.geojson.GeoJSON;
 
 /**
  * Handles form configuration actions
@@ -59,7 +60,7 @@ import no.nibio.web.forms.FormValidator;
  * @author Tor-Einar Skog <tor-einar.skog@nibio.no>
  */
 public class ForecastConfigurationController extends HttpServlet {
-    
+
     @PersistenceContext(unitName="VIPSLogic-PU")
     EntityManager em;
     
@@ -72,6 +73,9 @@ public class ForecastConfigurationController extends HttpServlet {
     @EJB
     OrganismBean organismBean;
 
+    @EJB
+    PointOfInterestBean pointOfInterestBean;
+
     /**
      * Processes requests for both HTTP <code>GET</code> and <code>POST</code>
      * methods.
@@ -203,7 +207,7 @@ public class ForecastConfigurationController extends HttpServlet {
                     if(forecastConfiguration == null)
                     {
                         forecastConfiguration = new ForecastConfiguration();
-                        multipleNew =   request.getParameter("multipleNew") != null 
+                        multipleNew =   request.getParameter("multipleNew") != null
                                         && request.getParameter("multipleNew").equals("true");
                     }
                     // Only superusers can view and edit forecasts from other organizations
@@ -221,14 +225,22 @@ public class ForecastConfigurationController extends HttpServlet {
                         // TODO: More intelligent selection of locations, weather stations and users
                         if(user.isSuperUser())
                         {
-                            request.setAttribute("locationPointOfInterests", em.createNamedQuery("PointOfInterest.findAll").getResultList());
-                            request.setAttribute("weatherStationPointOfInterests", em.createNamedQuery("PointOfInterestWeatherStation.findAllByActivity").setParameter("active", Boolean.TRUE).getResultList());
+                            List<PointOfInterest> poiList = em.createNamedQuery("PointOfInterest.findAll").getResultList();
+                            request.setAttribute("locationPointOfInterests", poiList);
+                            request.setAttribute("locationPointOfInterestsGeoJson", convertToGeoJson(poiList));
+                            List<PointOfInterest> weatherStationPoiList = em.createNamedQuery("PointOfInterestWeatherStation.findAllByActivity").setParameter("active", Boolean.TRUE).getResultList();
+                            request.setAttribute("weatherStationPointOfInterests", weatherStationPoiList);
+                            request.setAttribute("weatherStationPointOfInterestsGeoJson", convertToGeoJson(weatherStationPoiList));
                             request.setAttribute("vipsLogicUsers", em.createNamedQuery("VipsLogicUser.findAll").getResultList());
                         }
                         else
                         {
-                            request.setAttribute("locationPointOfInterests", em.createNamedQuery("PointOfInterest.findByOrganizationId").setParameter("organizationId", user.getOrganizationId()).getResultList());
-                            request.setAttribute("weatherStationPointOfInterests", em.createNamedQuery("PointOfInterestWeatherStation.findByActivityAndOrganizationId").setParameter("active", Boolean.TRUE).setParameter("organizationId", user.getOrganizationId()).getResultList());
+                            List<PointOfInterest> poiList = em.createNamedQuery("PointOfInterest.findByOrganizationId").setParameter("organizationId", user.getOrganizationId()).getResultList();
+                            request.setAttribute("locationPointOfInterests", poiList);
+                            request.setAttribute("locationPointOfInterestsGeoJson", convertToGeoJson(poiList));
+                            List<PointOfInterest> weatherStationPoiList = em.createNamedQuery("PointOfInterestWeatherStation.findByActivityAndOrganizationId").setParameter("active", Boolean.TRUE).setParameter("organizationId", user.getOrganizationId()).getResultList();
+                            request.setAttribute("weatherStationPointOfInterests", weatherStationPoiList);
+                            request.setAttribute("weatherStationPointOfInterestsGeoJson", convertToGeoJson(weatherStationPoiList));
                             request.setAttribute("vipsLogicUsers", em.createNamedQuery("VipsLogicUser.findByOrganizationId").setParameter("organizationId", user.getOrganizationId()).getResultList());
                         }
                         request.setAttribute("forecastConfiguration", forecastConfiguration);
@@ -244,7 +256,7 @@ public class ForecastConfigurationController extends HttpServlet {
                         request.setAttribute("modelInformations", forecastBean.getBatchableModels());
                         request.setAttribute("messageKey", request.getParameter("messageKey"));
                         request.getRequestDispatcher("/forecastConfigurationForm.ftl").forward(request, response);
-                       
+
                     }
                 }
                 catch(NullPointerException | NumberFormatException ex)
@@ -264,13 +276,17 @@ public class ForecastConfigurationController extends HttpServlet {
                         forecastConfiguration.setIsPrivate(Boolean.TRUE);
                     }
                     request.setAttribute("forecastConfiguration", forecastConfiguration);
-                    request.setAttribute("locationPointOfInterests", em.createNamedQuery("PointOfInterest.findByOrganizationId").setParameter("organizationId", user.getOrganizationId()).getResultList());
-                    request.setAttribute("weatherStationPointOfInterests", em.createNamedQuery("PointOfInterestWeatherStation.findByActivityAndOrganizationId").setParameter("active", Boolean.TRUE).setParameter("organizationId", user.getOrganizationId()).getResultList());
+                    List<PointOfInterest> poiList = em.createNamedQuery("PointOfInterest.findByOrganizationId").setParameter("organizationId", user.getOrganizationId()).getResultList();
+                    request.setAttribute("locationPointOfInterests", poiList);
+                    request.setAttribute("locationPointOfInterestsGeoJson", convertToGeoJson(poiList));
+                    List<PointOfInterest> weatherStationPoiList = em.createNamedQuery("PointOfInterestWeatherStation.findByActivityAndOrganizationId").setParameter("active", Boolean.TRUE).setParameter("organizationId", user.getOrganizationId()).getResultList();
+                    request.setAttribute("weatherStationPointOfInterests", weatherStationPoiList);
+                    request.setAttribute("weatherStationPointOfInterestsGeoJson", convertToGeoJson(weatherStationPoiList));
                     request.setAttribute("forecastConfiguration", forecastConfiguration);
                     request.setAttribute("formId","forecastConfigurationForm");
                     request.getSession().setAttribute("availableTimeZones", SystemTime.getAvailableTimeZones());
                     request.getSession().setAttribute("defaultTimeZoneId", user.getOrganizationId().getDefaultTimeZone());
-                    
+
                     request.setAttribute("allCrops", em.createNamedQuery("Organism.findAllCrops").getResultList());
                     request.setAttribute("allPests", em.createNamedQuery("Organism.findAllPests").getResultList());
                     // Hierarchy categories
@@ -279,13 +295,13 @@ public class ForecastConfigurationController extends HttpServlet {
                     request.setAttribute("messageKey", request.getParameter("messageKey"));
                     request.getRequestDispatcher("/forecastConfigurationForm.ftl").forward(request, response);
                 }
-                else 
+                else
                 {
                     response.sendError(403,"Access not authorized");
                 }
             }
         }
-        
+
         // Store forecast configuration(s)
         // Authorization: SUPERUSERS and ORGANIZATION ADMINS
         else if(action.equals("forecastConfigurationFormSubmit"))
@@ -310,8 +326,8 @@ public class ForecastConfigurationController extends HttpServlet {
                 }
                 else
                 {
-                    String formId = request.getParameter("multipleNew") != null && request.getParameter("multipleNew").equals("true") ? 
-                            "forecastConfigurationMultipleNewForm" 
+                    String formId = request.getParameter("multipleNew") != null && request.getParameter("multipleNew").equals("true") ?
+                            "forecastConfigurationMultipleNewForm"
                             :"forecastConfigurationForm";
                     FormValidation formValidation = FormValidator.validateForm(formId,request,getServletContext());
                     // Also validation the model specific fields
@@ -344,7 +360,7 @@ public class ForecastConfigurationController extends HttpServlet {
                             for(String optionVal:formValidation.getFormField("weatherStationPointOfInterestIds").getWebValues())
                             {
                                 Integer weatherStationPointOfInterestId = Integer.valueOf(optionVal);
-                                forecastBean.storeNewMultipleForecastConfiguration(weatherStationPointOfInterestId, formValidation.getFormFields(), modelFieldFormValidation.getFormFields());    
+                                forecastBean.storeNewMultipleForecastConfiguration(weatherStationPointOfInterestId, formValidation.getFormFields(), modelFieldFormValidation.getFormFields());
                             }
                             request.setAttribute("messageKey", request.getParameter("multipleForecastConfigurationsCreated"));
                             response.sendRedirect(new StringBuilder(Globals.PROTOCOL + "://").append(ServletUtil.getServerName(request)).append("/forecastConfiguration?messageKey=").append("multipleForecastConfigurationsCreated").toString());
@@ -357,8 +373,12 @@ public class ForecastConfigurationController extends HttpServlet {
                         // We must get date formats!
                         Map<String, FormField> formFields = FormValidator.getFormFields("forecastConfigurationForm",getServletContext());
                         // TODO: More intelligent selection of locations, weather stations and users
-                        request.setAttribute("locationPointOfInterests", em.createNamedQuery("PointOfInterest.findAll").getResultList());
-                        request.setAttribute("weatherStationPointOfInterests", em.createNamedQuery("PointOfInterestWeatherStation.findAll").getResultList());
+                        List<PointOfInterest> poiList = em.createNamedQuery("PointOfInterest.findAll").getResultList();
+                        request.setAttribute("locationPointOfInterests", poiList);
+                        request.setAttribute("locationPointOfInterestsGeoJson", convertToGeoJson(poiList));
+                        List<PointOfInterest> weatherStationPoiList = em.createNamedQuery("PointOfInterestWeatherStation.findAll").getResultList();
+                        request.setAttribute("weatherStationPointOfInterests", weatherStationPoiList);
+                        request.setAttribute("weatherStationPointOfInterestsGeoJson", convertToGeoJson(weatherStationPoiList));
                         request.setAttribute("vipsLogicUsers", em.createNamedQuery("VipsLogicUser.findAll").getResultList());
                         request.setAttribute("dateStart_dateFormat", formFields.get("dateStart").getDateFormat());
                         request.setAttribute("dateEnd_dateFormat", formFields.get("dateEnd").getDateFormat());
@@ -403,13 +423,13 @@ public class ForecastConfigurationController extends HttpServlet {
                 try
                 {
                     forecastBean.deleteForecastConfiguration(forecastConfigurationId);
-                    response.sendRedirect(new StringBuilder(Globals.PROTOCOL + "://").append(ServletUtil.getServerName(request)).append("/forecastConfiguration?").append("&messageKey=").append("forecastConfigurationDeleted").toString());                       
+                    response.sendRedirect(new StringBuilder(Globals.PROTOCOL + "://").append(ServletUtil.getServerName(request)).append("/forecastConfiguration?").append("&messageKey=").append("forecastConfigurationDeleted").toString());
                 }
                 catch(NullPointerException | NumberFormatException ex)
                 {
                     response.sendError(500, "Invalid forecast configurationId " + request.getParameter("forecastConfigurationId"));
                 }
-                
+
             }
             else
             {
@@ -418,6 +438,11 @@ public class ForecastConfigurationController extends HttpServlet {
         }
     }
 
+    private GeoJSON convertToGeoJson(List<PointOfInterest> poiList) {
+        return pointOfInterestBean.getPoisAsGeoJson(poiList);
+    }
+
+
     // <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.
diff --git a/src/main/java/no/nibio/vips/logic/service/POIService.java b/src/main/java/no/nibio/vips/logic/service/POIService.java
index 19132a3b9146e71697b99567cb723a0acd7140b9..399cb26acf2078c0d0c4e9da7b48020e75605359 100644
--- a/src/main/java/no/nibio/vips/logic/service/POIService.java
+++ b/src/main/java/no/nibio/vips/logic/service/POIService.java
@@ -37,7 +37,9 @@ import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.Status;
 
+import com.fasterxml.jackson.core.JsonProcessingException;
 import no.nibio.vips.logic.controller.session.PointOfInterestBean;
+import no.nibio.vips.logic.entity.helpers.PointOfInterestFactory;
 import org.jboss.resteasy.spi.HttpRequest;
 import org.locationtech.jts.geom.Coordinate;
 import org.locationtech.jts.geom.Point;
@@ -102,7 +104,70 @@ public class POIService {
         PointOfInterest retVal = SessionControllerGetter.getPointOfInterestBean().getPointOfInterest(pointOfInterestId);
         return Response.ok().entity(retVal).build();
     }
-    
+
+    @POST
+    @Consumes("application/json;charset=UTF-8")
+    @Produces("application/json;charset=UTF-8")
+    public Response postPoi(String poiJson) {
+        // TODO Fix authentication
+        ObjectMapper oM = new ObjectMapper();
+        Map<Object, Object> poiMap;
+        try {
+            poiMap = oM.readValue(poiJson, new TypeReference<HashMap<Object, Object>>() {});
+        } catch (JsonProcessingException e) {
+            LOGGER.error(e.getMessage(), e);
+            return Response.status(Status.BAD_REQUEST).entity("Unable to parse input").build();
+        }
+
+        Integer poiUserId = poiMap.get("userId") != null ? Integer.parseInt(poiMap.get("userId").toString()) : null;
+
+        VipsLogicUser user = SessionControllerGetter.getUserBean().getVipsLogicUser(poiUserId);
+        if (user == null) {
+            LOGGER.error("No user found for userId={}", poiUserId);
+            return Response.status(Status.UNAUTHORIZED).build();
+        }
+        LOGGER.error("Remember to check for roles as well, if necessary!");
+
+        PointOfInterestBean poiBean = SessionControllerGetter.getPointOfInterestBean();
+
+        Integer poiTypeId = poiMap.get("typeId") != null ? Integer.parseInt(poiMap.get("typeId").toString()) : null;
+        if(poiTypeId == null) {
+            return Response.status(Status.BAD_REQUEST).entity("Point of interest type is required").build();
+        }
+        String poiName = poiMap.get("name") != null ? poiMap.get("name").toString() : null;
+        Double poiLongitude = poiMap.get("longitude") != null ? Double.valueOf(poiMap.get("longitude").toString()): null;
+        Double poiLatitude = poiMap.get("latitude") != null ? Double.valueOf(poiMap.get("latitude").toString()): null;
+        Double poiAltitude = poiMap.get("altitude") != null ? Double.valueOf(poiMap.get("altitude").toString()): null;
+
+        PointOfInterest poiToSave = PointOfInterestFactory.getPointOfInterest(poiTypeId);
+        poiToSave.setName(poiName);
+        poiToSave.setLongitude(poiLongitude);
+        poiToSave.setLatitude(poiLatitude);
+        poiToSave.setAltitude(poiAltitude);
+        poiToSave.setLastEditedTime(new Date());
+        poiToSave.setUser(user);
+        poiToSave.setCountryCode(user.getOrganizationId().getCountryCode());
+
+        if (poiLongitude != null && poiLatitude != null && poiAltitude != null) {
+            GISUtil gisUtil = new GISUtil();
+            Coordinate coordinate = new Coordinate(poiLongitude, poiLatitude, poiAltitude);
+            Point p3d = gisUtil.createPointWGS84(coordinate);
+            poiToSave.setGisGeom(p3d);
+        }
+
+        poiToSave = poiBean.storePoi(poiToSave);
+
+        if (poiToSave != null) {
+            return Response.status(Response.Status.CREATED)
+                    .entity(poiToSave)
+                    .build();
+        } else {
+            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
+                    .entity("Failed to create PointOfInterest")
+                    .build();
+        }
+    }
+
     /**
      * @param organizationId Id of the organization in question
      * @param poiTypesStr Comma separated list of poiTypes
diff --git a/src/main/webapp/css/mapModal.css b/src/main/webapp/css/mapModal.css
new file mode 100644
index 0000000000000000000000000000000000000000..66a5bfee7ce7ddca9991631c717af2c43c2deb42
--- /dev/null
+++ b/src/main/webapp/css/mapModal.css
@@ -0,0 +1,53 @@
+.modal {
+    display: none;
+    position: fixed;
+    z-index: 1000;
+    left: 0;
+    top: 0;
+    width: 100%;
+    height: 100%;
+    overflow: hidden;
+    background-color: rgba(0, 0, 0, 0.9);
+}
+
+.modal-content {
+    position: relative;
+    height: 100%;
+    width: 100%;
+    background-color: #fefefe;
+}
+
+#selectedPointInfo {
+    font-family: Arial, sans-serif;
+    font-size: 12px;
+    box-shadow: 0 0 15px rgba(0, 0, 0, 0.2);
+    width: 150px;
+    position: absolute;
+    bottom: 20px;
+    left: 20px;
+    background: white;
+    padding: 10px;
+    border-radius: 5px;
+    z-index: 1100;
+}
+
+#confirmButton {
+    margin-top: 10px;
+}
+
+.leaflet-container {
+    cursor: default !important;  /* Force the arrow cursor */
+}
+
+.leaflet-container {
+    font-size: 0.9rem; /* Make font-size of popup larger */
+}
+
+#pointForm {
+    z-index: 1200;
+    position: absolute;
+}
+
+#poiPopup {
+    width: 100px;
+}
\ No newline at end of file
diff --git a/src/main/webapp/js/mapModal.js b/src/main/webapp/js/mapModal.js
new file mode 100644
index 0000000000000000000000000000000000000000..47f23cf1ce15231dc8df4d495af7f6039598b557
--- /dev/null
+++ b/src/main/webapp/js/mapModal.js
@@ -0,0 +1,305 @@
+// mapModal.js
+import {
+    map,
+    tileLayer,
+    marker,
+    geoJSON,
+    circleMarker,
+    DomEvent
+} from 'https://unpkg.com/leaflet/dist/leaflet-src.esm.js';
+
+/**
+ * Uses css classes from bootstrap 3.4.1
+ *
+ */
+class MapModal {
+
+    constructor(mapContainerId, typeNameMap, geoJsonData, allowNewPoints = false, callback = null) {
+        this.mapContainerId = mapContainerId;
+        this.typeNameMap = typeNameMap;
+        this.geojsonData = geoJsonData;
+        this.allowNewPoints = allowNewPoints;
+        this.map = null;
+        this.isMapInitialized = false;
+        this.selectedPointLayer = null;
+        this.createdPointLayer = null;
+        this.createdPoints = [];
+        this.callback = callback;
+
+        this.typeColorMap = {
+            0: "#B3CDE0",  // Light blue
+            1: "#FBB4AE",  // Soft pink
+            2: "#CCEBC5",  // Light green
+            3: "#DECBE4",  // Lavender
+            5: "#FED9A6",  // Light peach
+            6: "#FFFFCC",  // Pale yellow
+            7: "#E5D8BD"   // Beige
+        };
+
+    }
+
+    initMap() {
+        if (!this.isMapInitialized) {
+            // Initialize the map centered on Norway
+            this.map = map(this.mapContainerId).setView([63.4226, 10.3951], 5);
+            // Add a tile layer
+            tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
+                maxZoom: 19
+            }).addTo(this.map);
+
+            this.createSelectedPointInfo();
+
+            // Add points to the map
+            geoJSON(this.geojsonData, {
+                filter: (feature) => feature.geometry.type === "Point",
+                pointToLayer: (feature, latlng) => {
+                    const color = this.typeColorMap[feature.properties.pointOfInterestTypeId] || "#3498DB";
+                    return circleMarker(latlng, {
+                        radius: 8,  // Size of the marker
+                        fillColor: color,
+                        color: "#FFFFFF",  // Border color
+                        weight: 2,  // Border thickness
+                        opacity: 1,
+                        fillOpacity: 0.8
+                    });
+                },
+                onEachFeature: (feature, layer) => {
+                    layer.bindPopup(this.popupContent(feature));
+                    layer.on('click', () => this.selectPoint(feature, layer));
+                }
+            }).addTo(this.map);
+
+            // Enable adding new points if allowed
+            if (this.allowNewPoints) {
+                console.info("Enable point creation")
+                this.enablePointCreation();
+            }
+            this.isMapInitialized = true;
+        }
+    }
+
+    createSelectedPointInfo() {
+        console.debug("Create information panel for selected point, initially hidden");
+        const selectedPointInfoHtml = `
+        <div id="selectedPointInfo" style="display: none;">
+            <div id="infoMessage"></div>
+            <button id="confirmButton" class="btn btn-primary">OK</button>
+        </div>
+    `   ;
+        document.getElementById(this.mapContainerId).insertAdjacentHTML('beforeend', selectedPointInfoHtml);
+    }
+
+    updateSelectedPointInfo(feature) {
+        const name = feature.properties.pointOfInterestName;
+        const type = this.typeNameMap[feature.properties.pointOfInterestTypeId];
+        const coordinates = feature.geometry.coordinates[1].toFixed(5) + ", " + feature.geometry.coordinates[0].toFixed(5);
+
+        const selectedPointInfo = document.getElementById('selectedPointInfo');
+        const infoMessage = document.getElementById('infoMessage');
+
+        infoMessage.innerHTML = `<strong>${name}</strong><br>${coordinates}<br>${type}`;
+        selectedPointInfo.style.display = 'block';
+
+        const confirmButton = document.getElementById('confirmButton');
+        confirmButton.style.display = 'block';
+        confirmButton.onclick = () => {
+            this.confirmSelection(feature);
+        };
+    }
+
+    selectPoint(feature, layer) {
+        // Deselect previously selected point, if any
+        if (this.selectedPointLayer) {
+            const color = this.typeColorMap[feature.properties.pointOfInterestTypeId] || "#3498DB";
+            this.selectedPointLayer.setStyle({
+                radius: 8,  // Size of the marker
+                fillColor: color,
+                color: "#FFFFFF",  // Border color
+                weight: 2,  // Border thickness
+                opacity: 1,
+                fillOpacity: 0.8
+            });
+        }
+
+        // Highlight the new selected point
+        layer.setStyle({
+            fillColor: 'red',
+            color: '#f00'
+        });
+
+        this.updateSelectedPointInfo(feature)
+        this.selectedPointLayer = layer;
+    }
+
+    confirmSelection(feature) {
+        console.info("Save pointData", feature);
+        const pointData = {
+            id: feature.properties.pointOfInterestId,
+            name: feature.properties.pointOfInterestName,
+            typeId: feature.properties.pointOfInterestTypeId,
+            altitude: feature.properties.pointOfInterestAltitude,
+            longitude: feature.geometry.coordinates[0],
+            latitude: feature.geometry.coordinates[1]
+        };
+        if (typeof this.callback === 'function') {
+            console.info("Callback!", pointData);
+            this.callback(pointData);
+        }
+        this.closeModal();
+    }
+
+    enablePointCreation() {
+        this.map.on('click', (e) => {
+            console.log("Map clicked, enabling point creation...");
+            const latlng = e.latlng;
+
+            // If a form already exists, remove it
+            this.closeNewPointFormIfOpen();
+
+            // Calculate the pixel position from the map's click event
+            const containerPoint = this.map.latLngToContainerPoint(latlng);
+
+            // Show form for creating a new point
+            const formHtml = `
+            <div id="pointForm" class="panel panel-default"  style="top: ${containerPoint.y}px; left: ${containerPoint.x}px;">
+                <div class="panel-heading">
+                    <h3 class="panel-title">Create new point</h3>
+                </div>
+                <div class="panel-body">
+                    <div class="form-group">
+                        <label for="name">Name:</label>
+                        <input type="text" class="form-control" id="name" name="name">
+                    </div>
+                    <div class="form-group">
+                        <label for="altitude">Altitude:</label>
+                        <input type="text" class="form-control" id="altitude" name="altitude">
+                    </div>
+                    <div class="form-group">
+                        <label for="poiTypeSelect">Type:</label>
+                        <select class="form-control" id="poiTypeSelect" name="poiTypeSelect"></select>
+                    </div>
+                    <div class="form-group text-right">
+                        <button id="savePointButton" class="btn btn-primary">Save</button>
+                    </div>                
+                </div>
+            </div>`;
+            document.getElementById(this.mapContainerId).insertAdjacentHTML('beforeend', formHtml);
+
+            const typeSelectElement = document.getElementById("poiTypeSelect");
+            typeSelectElement.innerHTML = '';
+            for (const [id, name] of Object.entries(this.typeNameMap)) {
+                const option = document.createElement('option');
+                option.value = id;
+                option.textContent = name;
+                typeSelectElement.appendChild(option);
+            }
+
+            const formElement = document.getElementById('pointForm');
+            DomEvent.disableClickPropagation(formElement);
+
+            // Add event listener to close the form if clicked outside
+            document.addEventListener('click', this.handleClickOutsidePointForm.bind(this), true);
+
+            document.getElementById('savePointButton').addEventListener('click', () => {
+                this.savePoint(latlng.lat, latlng.lng);
+            });
+        });
+    }
+
+    handleClickOutsidePointForm(event) {
+        const formElement = document.getElementById('pointForm');
+
+        // If the clicked element is not inside the form, close the form
+        if (formElement && !formElement.contains(event.target)) {
+            this.closeNewPointFormIfOpen();
+        }
+    }
+
+    closeNewPointFormIfOpen() {
+        const formElement = document.getElementById('pointForm');
+        if (formElement) {
+            formElement.remove();
+        }
+
+        // Remove the event listener after closing the form
+        document.removeEventListener('click', this.handleClickOutsidePointForm.bind(this), true);
+    }
+
+    savePoint(lat, lng) {
+        const nameElement = document.getElementById('name');
+        const altitudeElement = document.getElementById('altitude');
+        const poiTypeSelectElement = document.getElementById("poiTypeSelect")
+
+        if (nameElement && poiTypeSelectElement) {
+            const poiName = nameElement.value;
+            const poiAltitude = altitudeElement.value;
+            const poiType = poiTypeSelectElement.value;
+
+            if (this.createdPointLayer) {
+                this.map.removeLayer(this.createdPointLayer);
+            }
+
+            const newPoint = {
+                "type": "Feature",
+                "geometry": {
+                    "type": "Point",
+                    "coordinates": [lng, lat]
+                },
+                "properties": {
+                    "pointOfInterestName": poiName,
+                    "pointOfInterestAltitude": poiAltitude,
+                    "pointOfInterestTypeId": poiType
+                }
+            };
+            if (this.createdPoints.length > 0) {
+                this.createdPoints.pop(); // Remove the last created point from the list
+            }
+            this.createdPoints.push(newPoint);
+            this.createdPointLayer = geoJSON(newPoint, {
+                pointToLayer: (feature, latlng) => {
+                    return circleMarker(latlng, {
+                        radius: 8,
+                        fillColor: 'green',
+                        color: '#000',
+                        weight: 1,
+                        opacity: 1,
+                        fillOpacity: 0.8
+                    });
+                },
+                onEachFeature: (feature, layer) => {
+                    layer.bindPopup(this.popupContent(feature));
+                    layer.on('click', () => this.selectPoint(feature, layer));
+                }
+            }).addTo(this.map);
+
+            this.createdPointLayer.eachLayer((layer) => {
+                this.selectPoint(newPoint, layer);
+            });
+
+            const formElement = document.getElementById('pointForm');
+            if (formElement) {
+                formElement.remove();
+            }
+        }
+    }
+
+    popupContent(feature) {
+        const localizedTypeName = this.typeNameMap[feature.properties.pointOfInterestTypeId];
+        const coordinates = feature.geometry.coordinates[1].toFixed(5) + ", " + feature.geometry.coordinates[0].toFixed(5);
+        return `<div id="poiPopup">
+                <strong>${feature.properties.pointOfInterestName}</strong><br>${coordinates}<br>${localizedTypeName}
+                </div>`;
+    }
+
+    openModal(points) {
+        document.getElementById('mapModal').style.display = 'block';
+        this.initMap();
+    }
+
+    closeModal() {
+        document.getElementById('mapModal').style.display = 'none';
+    }
+}
+
+// Export the module
+export default MapModal;
\ No newline at end of file
diff --git a/src/main/webapp/templates/forecastConfigurationForm.ftl b/src/main/webapp/templates/forecastConfigurationForm.ftl
index c4656b5809c21741a48ddec2aac42bd9940a25c8..b07241b2160a992bc804d84452b922d128f1015e 100755
--- a/src/main/webapp/templates/forecastConfigurationForm.ftl
+++ b/src/main/webapp/templates/forecastConfigurationForm.ftl
@@ -1,6 +1,6 @@
-<#-- 
-    Copyright (c) 2016 NIBIO <http://www.nibio.no/>. 
-  
+<#--
+    Copyright (c) 2016 NIBIO <http://www.nibio.no/>.
+
   This file is part of VIPSLogic.
   This program is free software: you can redistribute it and/or modify
     it under the terms of the GNU Affero General Public License as published by
@@ -16,21 +16,78 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 --><#include "master.ftl">
 <#macro page_head>
-        <title>${i18nBundle.viewForecastConfiguration}</title>
+    <title>${i18nBundle.viewForecastConfiguration}</title>
 </#macro>
 <#macro custom_js>
-	<script src="/js/resourcebundle.js"></script>
-	<script src="/js/forecastConfigurationForm.js"></script>
-	<script src="/js/validateForm.js"></script>
-	<script src="//code.jquery.com/ui/1.10.3/jquery-ui.min.js"></script>
-	<link href="//code.jquery.com/ui/1.10.3/themes/redmond/jquery-ui.css" rel="stylesheet" />
-	<script type="text/javascript" src="/js/3rdparty/modernizr_custom.js"></script>
-	<script type="text/javascript" src="/js/3rdparty/moment.min.js"></script>
+    <script src="/js/resourcebundle.js"></script>
+    <script src="/js/forecastConfigurationForm.js"></script>
+    <script src="/js/validateForm.js"></script>
+    <script src="//code.jquery.com/ui/1.10.3/jquery-ui.min.js"></script>
+    <link href="//code.jquery.com/ui/1.10.3/themes/redmond/jquery-ui.css" rel="stylesheet" />
+    <link rel="stylesheet" href="https://unpkg.com/leaflet/dist/leaflet.css" />
+    <link rel="stylesheet" href="/css/mapModal.css" />
+    <script type="text/javascript" src="/js/3rdparty/modernizr_custom.js"></script>
+    <script type="text/javascript" src="/js/3rdparty/moment.min.js"></script>
     <script type="text/javascript" src="/js/environment.js"></script>
     <script type="text/javascript" src="/js/util.js"></script>
     <script type="text/javascript" src="/js/3rdparty/chosen.jquery.min.js"></script>
     <script type="text/javascript">
         $(".chosen-select").chosen();
+    </script>
+    <script type="module">
+        import MapModal from '/js/mapModal.js';
+        function callbackUpdateLocationPointOfInterest(pointData) {
+            const selectBox = document.querySelector('select[name="locationPointOfInterestId"]');
+            if(pointData.id) {
+                let optionFound = false;
+                for (let i = 0; i < selectBox.options.length; i++) {
+                    if (selectBox.options[i].value == pointData.id) {
+                        selectBox.selectedIndex = i; // Select the matching option
+                        optionFound = true;
+                        break;
+                    }
+                }
+                if (!optionFound) {
+                    console.error("No matching option found for feature.id:", pointData.id);
+                }
+            } else {
+                const userId = ${user.userId};
+                const params = {
+                    'name': pointData.name,
+                    'typeId': pointData.typeId,
+                    'longitude': pointData.longitude,
+                    'latitude': pointData.latitude,
+                    'altitude': pointData.altitude,
+                    'userId': userId,
+                }
+                $.ajax({
+                    url: "/rest/poi",
+                    type: "POST",
+                    contentType: "application/json",
+                    data: JSON.stringify(params),
+                    success: function(response) {
+                        console.info("Success:", response);
+
+                    },
+                    error: function(jqXHR, textStatus, errorThrown) {
+                        console.error("Error:", textStatus, errorThrown);
+                    }
+                });
+            }
+        }
+        const typeNameMap = {
+            2: "${i18nBundle["pointOfInterestType_2"]}",
+            3: "${i18nBundle["pointOfInterestType_3"]}",
+            5: "${i18nBundle["pointOfInterestType_5"]}"
+        };
+
+        const poiGeoJson = JSON.parse('${locationPointOfInterestsGeoJson?json_string}');
+        const stationGeoJson = JSON.parse('${weatherStationPointOfInterestsGeoJson?json_string}')
+        const mapModalInstance = new MapModal('mapContainer', typeNameMap, poiGeoJson, true, callbackUpdateLocationPointOfInterest);
+        window.mapModalInstance = mapModalInstance;
+
+        window.openModal = () => window.mapModalInstance && window.mapModalInstance.openModal();
+        window.closeModal = () => window.mapModalInstance && window.mapModalInstance.closeModal();
     </script>
 	<script type="text/javascript">
             $(document).ready(function() {
@@ -278,6 +335,14 @@
 	    	<option value="${poi.pointOfInterestId}"<#if forecastConfiguration.locationPointOfInterestId?has_content && poi.pointOfInterestId == forecastConfiguration.locationPointOfInterestId.pointOfInterestId> selected="selected"</#if>>${poi.name}</option>
 	    	</#list>
 	    </select>
+          <button onclick="openModal()">Open Map</button>
+          <div id="mapModal" class="modal">
+              <div class="modal-content">
+                  <span class="close-button" onclick="closeModal()">&times;</span>
+                  <div id="mapContainer" style="height: 100vh; width: 100%; position: relative;">
+                  </div>
+              </div>
+          </div>
 	    <span class="help-block" id="${formId}_locationPointOfInterestId_validation"></span>
 	  </div>
 	  <div class="form-group">