diff --git a/src/main/java/no/nibio/vips/logic/controller/servlet/OrganizationController.java b/src/main/java/no/nibio/vips/logic/controller/servlet/OrganizationController.java
index b10c713a6ce7bf20ec28db65a7ac945cc21fe658..ed2e9fd72e7dd666418d13a765304fdcdd98597b 100644
--- a/src/main/java/no/nibio/vips/logic/controller/servlet/OrganizationController.java
+++ b/src/main/java/no/nibio/vips/logic/controller/servlet/OrganizationController.java
@@ -29,6 +29,7 @@ import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import no.nibio.vips.gis.GISUtil;
 import no.nibio.vips.gis.LonLatStringFormatException;
+import no.nibio.vips.logic.controller.session.PointOfInterestBean;
 import no.nibio.vips.logic.controller.session.UserBean;
 import no.nibio.vips.logic.entity.Organization;
 import no.nibio.vips.logic.entity.VipsLogicUser;
@@ -46,6 +47,9 @@ public class OrganizationController extends HttpServlet {
     
     @EJB
     UserBean userBean;
+
+    @EJB
+    PointOfInterestBean poiBean;
    
     /** 
      * Processes requests for both HTTP <code>GET</code> and <code>POST</code> methods.
@@ -101,6 +105,7 @@ public class OrganizationController extends HttpServlet {
             request.setAttribute("countries", userBean.getCountries());
             request.setAttribute("timeZones", TimeZone.getAvailableIDs());
             request.setAttribute("organization", organization);
+            request.setAttribute("gridWeatherDataSources", poiBean.getGridWeatherStationDataSources());
             request.getRequestDispatcher("/organizationForm.ftl").forward(request, response);
         }
         else if(action.equals("organizationFormSubmit"))
@@ -142,6 +147,11 @@ public class OrganizationController extends HttpServlet {
                                     : new GISUtil().getJtsPointFromString(formValidation.getFormField("defaultMapCenter").getWebValue())
                     );
                     
+                    organization.setDefaultGridWeatherStationDataSource(
+                        formValidation.getFormField("defaultGridWeatherStationDataSourceId").getValueAsInteger() < 0 ?
+                        null
+                        : poiBean.getWeatherStationDataSource(formValidation.getFormField("defaultGridWeatherStationDataSourceId").getValueAsInteger())
+                    );
                     organization = userBean.storeOrganization(organization);
                     
                     response.sendRedirect(
diff --git a/src/main/java/no/nibio/vips/logic/controller/servlet/WeatherStationDataSourceController.java b/src/main/java/no/nibio/vips/logic/controller/servlet/WeatherStationDataSourceController.java
new file mode 100644
index 0000000000000000000000000000000000000000..766d208f92d7924d4d5be151e124a313b34a2b05
--- /dev/null
+++ b/src/main/java/no/nibio/vips/logic/controller/servlet/WeatherStationDataSourceController.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright (c) 2024 NIBIO <http://www.nibio.no/>. 
+ *
+ * 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
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+package no.nibio.vips.logic.controller.servlet;
+
+import java.io.IOException;
+
+import java.util.List;
+
+import javax.ejb.EJB;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import no.nibio.vips.gis.LonLatStringFormatException;
+import no.nibio.vips.logic.controller.session.PointOfInterestBean;
+import no.nibio.vips.logic.entity.VipsLogicUser;
+import no.nibio.vips.logic.entity.WeatherStationDataSource;
+import no.nibio.vips.logic.util.Globals;
+import no.nibio.vips.logic.util.SystemTime;
+import no.nibio.vips.util.ServletUtil;
+import no.nibio.web.forms.FormValidation;
+import no.nibio.web.forms.FormValidationException;
+import no.nibio.web.forms.FormValidator;;
+
+/**
+ * Handles actions regarding listing and modifying weather station data sources
+ * 
+ * @copyright 2024 <a href="http://www.nibio.no/">NIBIO</a>
+ * @author Tor-Einar Skog <tor-einar.skog@nibio.no>
+ */
+public class WeatherStationDataSourceController  extends HttpServlet{
+
+    private static Logger LOGGER = LoggerFactory.getLogger(WeatherStationDataSourceController.class);
+
+    @EJB
+    PointOfInterestBean poiBean;
+
+    /** 
+     * Processes requests for both HTTP <code>GET</code> and <code>POST</code> methods.
+     * @param request servlet request
+     * @param response servlet response
+     * @throws ServletException if a servlet-specific error occurs
+     * @throws IOException if an I/O error occurs
+     */
+    protected void processRequest(HttpServletRequest request, HttpServletResponse response)
+    throws ServletException, IOException {
+
+        response.setContentType("text/html;charset=UTF-8");
+        
+        VipsLogicUser user = (VipsLogicUser) request.getSession().getAttribute("user");
+        // Basic authorization
+        if(!user.isSuperUser() && ! user.isOrganizationAdmin()){
+            response.sendError(403,"Access not authorized");
+            return;
+        }
+        String action = request.getParameter("action");
+
+        if(action == null)
+        {
+            List<WeatherStationDataSource> wsDataSources = poiBean.getWeatherStationDataSources();
+            request.setAttribute("messageKey", request.getParameter("messageKey") != null ? request.getParameter("messageKey") : null);
+            request.setAttribute("weatherStationDataSources", wsDataSources);
+            request.getRequestDispatcher("/weatherStationDataSourceList.ftl").forward(request, response);
+        }
+        else if(action.equals("editWeatherStationDataSource") || action.equals("newWeatherStationDataSource"))
+        {
+            WeatherStationDataSource weatherStationDataSource = null;
+            try 
+            { 
+                weatherStationDataSource = poiBean.getWeatherStationDataSource(Integer.valueOf(request.getParameter("weatherStationDataSourceId")));
+            }
+            catch(NullPointerException | NumberFormatException ex){}
+
+            if(weatherStationDataSource == null)
+            {
+                LOGGER.debug("Could not find weather data source with ID = " + request.getParameter("weatherStationDataSourceId"));
+                weatherStationDataSource = new WeatherStationDataSource();
+            }
+            
+            request.setAttribute("messageKey", request.getParameter("messageKey") != null ? request.getParameter("messageKey") : null);
+            request.setAttribute("weatherStationDataSource", weatherStationDataSource);
+            request.getRequestDispatcher("/weatherStationDataSourceForm.ftl").forward(request, response);
+        }
+        else if(action.equals("weatherStationDataSourceFormSubmit"))
+        {
+            try
+            {
+                // Check that the provided id is either -1 or it already exists
+                Integer weatherStationDataSourceId = null;
+                WeatherStationDataSource weatherStationDataSource;
+                try
+                {
+                    weatherStationDataSourceId = Integer.valueOf(request.getParameter("weatherStationDataSourceId"));
+                }
+                catch(NullPointerException | NumberFormatException ex)
+                {
+                    response.sendError(400,"Wrong format of id for weather station data source");
+                }
+                if(weatherStationDataSourceId < 0)
+                {
+                    weatherStationDataSource = new WeatherStationDataSource();
+                }
+                else 
+                {
+                    weatherStationDataSource = poiBean.getWeatherStationDataSource(weatherStationDataSourceId);
+                    if(weatherStationDataSource == null)
+                    {
+                        response.sendError(404,"Could not find weather station data source with id=" + weatherStationDataSourceId);
+                    }
+                }
+                FormValidation formValidation = FormValidator.validateForm("weatherStationDataSourceForm", request, getServletContext());
+                if(formValidation.isValid())
+                {
+                    weatherStationDataSource.setName(formValidation.getFormField("name").getWebValue());
+                    weatherStationDataSource.setDefaultDescription(formValidation.getFormField("defaultDescription").getWebValue());
+                    weatherStationDataSource.setUri(formValidation.getFormField("uri").getWebValue());
+                    weatherStationDataSource.setDatafetchUriExpression(formValidation.getFormField("datafetchUriExpression").getWebValue());
+                    weatherStationDataSource.setInfoUriExpression(formValidation.getFormField("infoUriExpression").getWebValue());
+                    weatherStationDataSource.setIsGrid(formValidation.getFormField("isGrid").getWebValue() != null);
+                    weatherStationDataSource = poiBean.storeWeatherStationDataSource(weatherStationDataSource);
+
+                    response.sendRedirect(
+                            Globals.PROTOCOL + "://" + ServletUtil.getServerName(request)
+                            + "/weatherstationdatasource?action=editWeatherStationDataSource&weatherStationDataSourceId=" + weatherStationDataSource.getWeatherStationDataSourceId()
+                            + "&messageKey=weatherStationDataSourceStored" 
+                    );
+                }
+                else
+                {
+                    request.setAttribute("formValidation", formValidation);
+                    request.setAttribute("weatherStationDataSource", weatherStationDataSource);
+                    LOGGER.debug("Form NOT valid. Dispatching");
+                    request.getRequestDispatcher("/weatherStationDataSourceForm.ftl").forward(request, response);
+                }
+            }
+            catch(FormValidationException ex)
+            {
+                ex.printStackTrace();
+                response.sendError(500, ex.getClass().toString() + ": " + ex.getMessage());
+            }
+        }
+        else if(action.equals("deleteWeatherStationDataSource"))
+        {
+
+            try
+            {
+                Integer weatherStationDataSourceId = Integer.valueOf(request.getParameter("weatherStationDataSourceId"));
+                WeatherStationDataSource weatherStationDataSource = poiBean.getWeatherStationDataSource(weatherStationDataSourceId);
+                if(weatherStationDataSource == null)
+                {
+                    response.sendError(404,"Could not find weather station data source with id=" + weatherStationDataSourceId);
+                }
+                // Check: Can it be deleted? Not if organizations or weatherStations refer to it
+                if(poiBean.isweatherStationDataSourceDeleteable(weatherStationDataSource))
+                {
+                    poiBean.deleteWeatherStationDataSource(weatherStationDataSource);
+                    // Redirect to list from which the deleted item has been removed
+                    response.sendRedirect(
+                            Globals.PROTOCOL + "://" + ServletUtil.getServerName(request)
+                            + "/weatherstationdatasource?messageKey=weatherStationDataSourceDeleted"
+                    );
+                }
+                else 
+                {
+                    response.sendError(403,weatherStationDataSource.getName() + " is in use by one or more organizations, so it cannot be deleted. Hit the browser's back button return to your form.");
+                }
+            }
+            catch(NullPointerException | NumberFormatException ex)
+            {
+                response.sendError(400,"Wrong format of id for weather station data source");
+            }
+
+            
+        }
+    }
+
+    // <editor-fold defaultstate="collapsed" desc="HttpServlet methods. Click on the + sign on the left to edit the code.">
+    /** 
+     * Handles the HTTP <code>GET</code> method.
+     * @param request servlet request
+     * @param response servlet response
+     * @throws ServletException if a servlet-specific error occurs
+     * @throws IOException if an I/O error occurs
+     */
+    @Override
+    protected void doGet(HttpServletRequest request, HttpServletResponse response)
+    throws ServletException, IOException {
+        processRequest(request, response);
+    } 
+
+    /** 
+     * Handles the HTTP <code>POST</code> method.
+     * @param request servlet request
+     * @param response servlet response
+     * @throws ServletException if a servlet-specific error occurs
+     * @throws IOException if an I/O error occurs
+     */
+    @Override
+    protected void doPost(HttpServletRequest request, HttpServletResponse response)
+    throws ServletException, IOException {
+        processRequest(request, response);
+    }
+
+    /** 
+     * Returns a short description of the servlet.
+     * @return a String containing servlet description
+     */
+    @Override
+    public String getServletInfo() {
+        return "Short description";
+    }// </editor-fold>
+}
diff --git a/src/main/java/no/nibio/vips/logic/controller/session/PointOfInterestBean.java b/src/main/java/no/nibio/vips/logic/controller/session/PointOfInterestBean.java
index 7c82e925fe6a7c595ecd996b5fbbcab09b7f0182..22229cf81bc468edeed0512b3f2a87923bec3c42 100755
--- a/src/main/java/no/nibio/vips/logic/controller/session/PointOfInterestBean.java
+++ b/src/main/java/no/nibio/vips/logic/controller/session/PointOfInterestBean.java
@@ -263,10 +263,54 @@ public class PointOfInterestBean {
         return kml;
     }
 
+    public WeatherStationDataSource getWeatherStationDataSource(Integer weatherStationDataSourceId)
+    {
+        return em.find(WeatherStationDataSource.class, weatherStationDataSourceId);
+    }
+
     public List<WeatherStationDataSource> getWeatherStationDataSources() {
         return em.createNamedQuery("WeatherStationDataSource.findAll").getResultList();
     }
 
+    public List<WeatherStationDataSource> getGridWeatherStationDataSources() {
+        return em.createNamedQuery("WeatherStationDataSource.findGridSources").getResultList();
+    }
+
+    /**
+     * Checks if the weather station data source can be deleted from the system. Criteria:
+     * <ul>
+     * <li>Not referenced from public.point_of_interest_weather_station</li>
+     * <li>Not referenced from public.organization</li>
+     * </ul> 
+     * @param weatherStationDataSource
+     * @return
+     */
+    public Boolean isweatherStationDataSourceDeleteable(WeatherStationDataSource weatherStationDataSource)
+    {
+        Query poiRefQuery = em.createQuery("SELECT COUNT(*) FROM PointOfInterestWeatherStation poiws where poiws.weatherStationDataSourceId = :weatherStationDataSourceId");
+        Long weatherStationReferences = (Long) poiRefQuery.setParameter("weatherStationDataSourceId", weatherStationDataSource).getSingleResult();
+        if(weatherStationReferences > 0)
+        {
+            return false;
+        }
+
+        Query orgRefQuery = em.createQuery("SELECT COUNT(*) FROM Organization o where o.defaultGridWeatherStationDataSource = :weatherStationDataSourceId");
+        Long organizationReferences = (Long) orgRefQuery.setParameter("weatherStationDataSourceId", weatherStationDataSource).getSingleResult();
+        return organizationReferences == 0;
+    }
+
+    public void deleteWeatherStationDataSource(WeatherStationDataSource weatherStationDataSource)
+    {
+        WeatherStationDataSource sourceToDelete = em.find(WeatherStationDataSource.class, weatherStationDataSource.getWeatherStationDataSourceId());
+        em.remove(sourceToDelete);
+    }
+
+    public WeatherStationDataSource storeWeatherStationDataSource(WeatherStationDataSource weatherStationDataSource)
+    {
+        weatherStationDataSource = em.merge(weatherStationDataSource);
+        return weatherStationDataSource;
+    }
+
     public PointOfInterestWeatherStation storeWeatherStation(PointOfInterestWeatherStation weatherStation) {
         weatherStation = em.merge(weatherStation);
         return weatherStation;
diff --git a/src/main/java/no/nibio/vips/logic/entity/ForecastConfiguration.java b/src/main/java/no/nibio/vips/logic/entity/ForecastConfiguration.java
index 735eebd16da0098531a68965e87403fe1994bbb1..7be6c11a3b2a35155dc61cf1421ca573a8c7dfba 100755
--- a/src/main/java/no/nibio/vips/logic/entity/ForecastConfiguration.java
+++ b/src/main/java/no/nibio/vips/logic/entity/ForecastConfiguration.java
@@ -257,6 +257,29 @@ public class ForecastConfiguration implements Serializable, Comparable {
      * @return the weatherStationPointOfInterestId
      */
     public PointOfInterest getWeatherStationPointOfInterestId() {
+        if( this.getUseGridWeatherData() && this.getVipsLogicUserId().getOrganizationId().getDefaultGridWeatherStationDataSource() != null)
+        {
+            // Create a "weather station" with coordinates from the location
+            // And the default grid weather data source for the current organization (get location owner's organization)
+            PointOfInterestWeatherStation gridStation = new PointOfInterestWeatherStation();
+            gridStation.setLatitude(this.getLocationPointOfInterestId().getLatitude());
+            gridStation.setLongitude(this.getLocationPointOfInterestId().getLongitude());
+            gridStation.setName("GRID-punkt for " + this.getLocationPointOfInterestId().getName()); // TODO Translate!!!
+            gridStation.setTimeZone(this.getLocationPointOfInterestId().getTimeZone());
+            gridStation.setWeatherStationDataSourceId(this.getVipsLogicUserId().getOrganizationId().getDefaultGridWeatherStationDataSource());
+            gridStation.setUser(this.getVipsLogicUserId());
+            gridStation.setWeatherForecastProviderId(null);
+            gridStation.setWeatherStationRemoteId(gridStation.getLongitude() + "_" + gridStation.getLatitude());
+            gridStation.setGisGeom(this.getLocationPointOfInterestId().getGisGeom());
+            gridStation.setAltitude(this.getLocationPointOfInterestId().getAltitude());
+            gridStation.setCountryCode(this.getLocationPointOfInterestId().getCountryCode());
+            gridStation.setIsForecastLocation(true);
+            gridStation.setPointOfInterestTypeId(PointOfInterestType.POINT_OF_INTEREST_TYPE_WEATHER_STATION);
+            gridStation.setUser(this.getVipsLogicUserId());
+            gridStation.setProperties(this.getLocationPointOfInterestId().getProperties());
+            return gridStation;
+        }
+
         return weatherStationPointOfInterestId;
     }
 
diff --git a/src/main/java/no/nibio/vips/logic/entity/Organization.java b/src/main/java/no/nibio/vips/logic/entity/Organization.java
index 4e5d1bd8be98829b6d203075f4f563e9e030607b..4e5d7e67b0154adde1a7b7ca33c8fba2bd6b438f 100755
--- a/src/main/java/no/nibio/vips/logic/entity/Organization.java
+++ b/src/main/java/no/nibio/vips/logic/entity/Organization.java
@@ -111,6 +111,21 @@ public class Organization implements Serializable {
     @OneToOne
     @JsonIgnore
     private VipsLogicUser archiveUser; 
+    
+    @JoinColumn(name = "default_grid_weather_station_data_source_id", referencedColumnName = "weather_station_data_source_id")
+    @ManyToOne
+    private WeatherStationDataSource defaultGridWeatherStationDataSource;
+
+    public WeatherStationDataSource getDefaultGridWeatherStationDataSource() {
+        return defaultGridWeatherStationDataSource;
+    }
+
+    public void setDefaultGridWeatherStationDataSource(WeatherStationDataSource defaultGridWeatherStationDataSource) {
+        this.defaultGridWeatherStationDataSource = defaultGridWeatherStationDataSource;
+    }
+
+    
+
 
     public Organization() {
     }
diff --git a/src/main/java/no/nibio/vips/logic/entity/WeatherStationDataSource.java b/src/main/java/no/nibio/vips/logic/entity/WeatherStationDataSource.java
index 9fe247f205036fd792eebe65ab0e6342a2625510..b31bd3aad0c4a63cf73652253fc942660688f974 100755
--- a/src/main/java/no/nibio/vips/logic/entity/WeatherStationDataSource.java
+++ b/src/main/java/no/nibio/vips/logic/entity/WeatherStationDataSource.java
@@ -40,6 +40,7 @@ import javax.xml.bind.annotation.XmlRootElement;
 @XmlRootElement
 @NamedQueries({
     @NamedQuery(name = "WeatherStationDataSource.findAll", query = "SELECT w FROM WeatherStationDataSource w"),
+    @NamedQuery(name = "WeatherStationDataSource.findGridSources", query = "SELECT w FROM WeatherStationDataSource w WHERE w.isGrid IS TRUE"),
     @NamedQuery(name = "WeatherStationDataSource.findByWeatherStationDataSourceId", query = "SELECT w FROM WeatherStationDataSource w WHERE w.weatherStationDataSourceId = :weatherStationDataSourceId"),
     @NamedQuery(name = "WeatherStationDataSource.findByName", query = "SELECT w FROM WeatherStationDataSource w WHERE w.name = :name"),
     @NamedQuery(name = "WeatherStationDataSource.findByDefaultDescription", query = "SELECT w FROM WeatherStationDataSource w WHERE w.defaultDescription = :defaultDescription"),
@@ -68,6 +69,18 @@ public class WeatherStationDataSource implements Serializable {
     @Size(max = 1024)
     @Column(name = "info_uri_expression")
     private String infoUriExpression;
+    @Column(name = "is_grid")
+    private Boolean isGrid;
+
+   
+
+    public Boolean getIsGrid() {
+        return isGrid;
+    }
+
+    public void setIsGrid(Boolean isGrid) {
+        this.isGrid = isGrid;
+    }
 
     public WeatherStationDataSource() {
     }
diff --git a/src/main/java/no/nibio/vips/logic/scheduling/VipsLogicTaskFactory.java b/src/main/java/no/nibio/vips/logic/scheduling/VipsLogicTaskFactory.java
index 66ae454af9550f24ce0552d5de6bb17d9cd2e87f..bf4bb80ee5e27f560c1ae23298fdc6b4385bc7c5 100755
--- a/src/main/java/no/nibio/vips/logic/scheduling/VipsLogicTaskFactory.java
+++ b/src/main/java/no/nibio/vips/logic/scheduling/VipsLogicTaskFactory.java
@@ -49,7 +49,7 @@ public class VipsLogicTaskFactory {
     public static final int RUN_FORECAST_CONFIGURATIONS_BY_ID_TASK = 9;
 
     
-    private final static int[] ALL_TASK_IDS = {1,2,3,4,5,6,7,8,9};
+    private final static int[] ALL_TASK_IDS = {1,2,3,4,5,6,7,9};
     
     private final static int[] ORGANIZATION_ADMIN_TASK_IDS = {7};
     
diff --git a/src/main/java/no/nibio/vips/logic/scheduling/model/preprocessor/AppleScabModelPreprocessor.java b/src/main/java/no/nibio/vips/logic/scheduling/model/preprocessor/AppleScabModelPreprocessor.java
index 374785eb0666c660d5b7d2800a67beb629064f17..230ffcb73ab8db055e90f5b57b72005caf1551c1 100755
--- a/src/main/java/no/nibio/vips/logic/scheduling/model/preprocessor/AppleScabModelPreprocessor.java
+++ b/src/main/java/no/nibio/vips/logic/scheduling/model/preprocessor/AppleScabModelPreprocessor.java
@@ -27,6 +27,10 @@ import java.util.Date;
 import java.util.HashSet;
 import java.util.List;
 import java.util.TimeZone;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 import no.nibio.vips.entity.ModelConfiguration;
 import no.nibio.vips.entity.WeatherObservation;
 import no.nibio.vips.logic.entity.ForecastConfiguration;
@@ -48,17 +52,13 @@ import no.nibio.vips.util.weather.WeatherDataSourceUtil;
  * @author Tor-Einar Skog <tor-einar.skog@nibio.no>
  */
 public class AppleScabModelPreprocessor extends ModelRunPreprocessor{
-    
-    private final boolean DEBUG = false;
+    private static Logger LOGGER = LoggerFactory.getLogger(AppleScabModelPreprocessor.class);
     public final static String APPLESCABM_START_DATE_ASCOSPORE_MATURITY = "APPLESCABM_START_DATE_ASCOSPORE_MATURITY";
 
     @Override
     public ModelConfiguration getModelConfiguration(ForecastConfiguration configuration) throws PreprocessorException
     {
-        if(DEBUG)
-        {
-            System.out.println("getModelConfiguration");
-        }
+        LOGGER.debug("AppleScabModelPreprocessor.getModelConfiguration() called");
         //configuration.getDateStart();
         PointOfInterestWeatherStation weatherStation = (PointOfInterestWeatherStation) configuration.getWeatherStationPointOfInterestId();
         // What timezone is the calculation for
@@ -92,10 +92,8 @@ public class AppleScabModelPreprocessor extends ModelRunPreprocessor{
         
         // Use Jackson to parse JSON from server
         // Weather data collections
-        if(DEBUG)
-        {
-            System.out.println("Getting weather data at " + new Date().toString());
-        }
+        LOGGER.debug("Getting weather data at " + new Date().toString());
+
         WeatherDataSourceUtil wdsUtil = new WeatherDataSourceUtil();
         List<WeatherObservation> observations;
         try {
@@ -135,22 +133,17 @@ public class AppleScabModelPreprocessor extends ModelRunPreprocessor{
             // We do nothing
         }
         
-        
-        if(DEBUG)
-        {
-            System.out.println("Finished getting weather data at " + new Date().toString());
-        }
+        LOGGER.debug("Finished getting weather data at " + new Date().toString());
+
         try {
             observations = validateAndSanitizeObservations(observations, startDateAscosporeMaturity);
         } catch (ConfigValidationException | WeatherObservationListException ex) {
             //ex.printStackTrace();
             throw new PreprocessorException(ex.getMessage());
         } 
-        if(DEBUG)
-        {        
-            System.out.println("Observations=" + observations.toString());
-        }
 
+        //LOGGER.debug("Observations=" + observations.toString());
+        
         
         // Create the complete model configuration object
         ModelConfiguration retVal = new ModelConfiguration();
@@ -176,11 +169,7 @@ public class AppleScabModelPreprocessor extends ModelRunPreprocessor{
      * @throws WeatherObservationListException 
      */
     private List<WeatherObservation> validateAndSanitizeObservations(List<WeatherObservation> observations, Date firstTimeStamp) throws ConfigValidationException, WeatherObservationListException {
-        if(DEBUG)
-        {
-            System.out.println("validateAndSanitizeObservations");
-        }
-        
+
         WeatherUtil wUtil = new WeatherUtil();
         
         // First we remove all duplicates
@@ -241,14 +230,7 @@ public class AppleScabModelPreprocessor extends ModelRunPreprocessor{
         
         // Problems with weather observations
         
-        // Holes in series
-        if(DEBUG)
-        {
-            System.out.println("checkForAndFixHourlyTimeSeriesHoles");
-            //System.out.println(wUtil.dumpWeatherObservationList(RR));
-        }
-
-        
+       
         
         // Unequal length of lists
         if  (        
@@ -257,6 +239,7 @@ public class AppleScabModelPreprocessor extends ModelRunPreprocessor{
                 || RR.size() != TM.size()
             )
         {
+            LOGGER.debug("Unequal lists lengt: RR=" + RR.size() + ", TM=" + TM.size() + ", BT=" + BT.size());
             UM = wUtil.fixHourlyValuesForParameters(
                     UM, 
                     new HashSet(Arrays.asList("UM")), 
diff --git a/src/main/java/no/nibio/vips/logic/scheduling/tasks/RunAllForecastConfigurationsTask.java b/src/main/java/no/nibio/vips/logic/scheduling/tasks/RunAllForecastConfigurationsTask.java
index 7a5cf687a4e6fa2e3e9c1de4827fe9b4c94b3953..b09788237b49bb869d77879b7201ccc9d72a1ca8 100755
--- a/src/main/java/no/nibio/vips/logic/scheduling/tasks/RunAllForecastConfigurationsTask.java
+++ b/src/main/java/no/nibio/vips/logic/scheduling/tasks/RunAllForecastConfigurationsTask.java
@@ -24,6 +24,10 @@ import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import javax.ejb.EJB;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 import no.nibio.vips.i18n.I18nImpl;
 import no.nibio.vips.logic.controller.session.ForecastBean;
 import no.nibio.vips.logic.controller.session.PointOfInterestBean;
@@ -46,6 +50,8 @@ import no.nibio.web.forms.FormField;
  * @author Tor-Einar Skog <tor-einar.skog@nibio.no>
  */
 public class RunAllForecastConfigurationsTask extends VipsLogicTask{
+
+    private static Logger LOGGER = LoggerFactory.getLogger(RunAllForecastConfigurationsTask.class);
     
     private I18nImpl i18n;
     //private boolean DEBUG=true;
@@ -91,24 +97,30 @@ public class RunAllForecastConfigurationsTask extends VipsLogicTask{
                 noForecastConfigurationsFound = false;
                 for(ForecastConfiguration forecastConfiguration:currentForecastConfigurations)
                 {
-                    if(
-                            weatherStationPointOfInterestId == null
-                            || weatherStationPointOfInterestId <= 0
-                            || (forecastConfiguration.getWeatherStationPointOfInterestId() != null && forecastConfiguration.getWeatherStationPointOfInterestId().getPointOfInterestId().equals(weatherStationPointOfInterestId))
+                    if(forecastConfiguration.getUseGridWeatherData() && forecastConfiguration.getWeatherStationPointOfInterestId() == null)
+                    {
+                        errorMessage.append(
+                            SchedulingUtil.createSchedulingMessageHTML(
+                                "Error with forecast #" + forecastConfiguration.getForecastConfigurationId()  + " (" + forecastConfiguration.getLocationPointOfInterestId().getName() + " - " + modelInformationMap.get(forecastConfiguration.getModelId()).getDefaultName() + ")", 
+                                "The forecast is configured to use gridded weather data, but the organization " + forecastConfiguration.getVipsLogicUserId().getOrganizationId().getOrganizationName() + " has not set its gridded weather data source. Please contact the system administrator.", 
+                                SchedulingUtil.MESSAGE_STATUS_WARNING
                             )
+                        );
+                        totalNumberofForecastConfigurations++;
+                    }
+                    else if(
+                        weatherStationPointOfInterestId == null
+                        || weatherStationPointOfInterestId <= 0
+                        || (forecastConfiguration.getWeatherStationPointOfInterestId() != null && forecastConfiguration.getWeatherStationPointOfInterestId().getPointOfInterestId().equals(weatherStationPointOfInterestId))
+                    )
+
                     {
                         try 
                         {
                             totalNumberofForecastConfigurations++;
-                            //System.out.println("Running forecast #" + forecastConfiguration.getForecastConfigurationId());
+                            LOGGER.debug("Running forecast #" + forecastConfiguration.getForecastConfigurationId());
                             SessionControllerGetter.getForecastBean().runForecast(forecastConfiguration);
-                            /*
-                            if(DEBUG && totalNumberofForecastConfigurations == 2)
-                            {
-                                throw new RunModelException("This is a test!!!");
-                            }*/
                             numberOfCompletedForecastConfigurations++;
-                            //System.out.println("All went well");
                         }
                         catch (PreprocessorException | RunModelException ex) 
                         {
@@ -119,38 +131,32 @@ public class RunAllForecastConfigurationsTask extends VipsLogicTask{
                                             ex.getMessage(), 
                                             SchedulingUtil.MESSAGE_STATUS_DANGER)
                                     );
-                            //System.out.println("###########################   Error caught: " + errorMessage);
-                            //System.out.println("numberOfCompletedForecastConfigurations=" + numberOfCompletedForecastConfigurations);
-                            //System.out.println("totalNumberofForecastConfigurations=" + totalNumberofForecastConfigurations);
-                            //continue;
                         }
 
                     }
                     if(totalNumberofForecastConfigurations > 0)
                     {
                         noForecastConfigurationsFound = false;
-                        double completeness = (double) numberOfCompletedForecastConfigurations/totalNumberofForecastConfigurations;
-                        tec.setCompleteness(completeness);
+                        tec.setCompleteness(Double.valueOf(numberOfCompletedForecastConfigurations)/Double.valueOf(totalNumberofForecastConfigurations));
                     }
                     else
                     {
                         noForecastConfigurationsFound = true;
-                        //System.out.println("noForecastConfigurationsFound == true!!");
+
                     }
-                    //System.out.println("Current completeness=" + tec.getTaskExecutor().getCompleteness());
                 }
             }
             
         }
+
         if(noForecastConfigurationsFound)
         {
                tec.setCompleteness(1.0);
                tec.setStatusMessage("No current forecast configurations were found");
         }
         
-        //System.out.println("Total completeness=" + tec.getTaskExecutor().getCompleteness());
         
-        if(tec.getTaskExecutor().getCompleteness() != 1.0)
+        if(tec.getTaskExecutor().getCompleteness() != 1.0 || ! errorMessage.isEmpty())
         {
             //System.out.println("Error detected, RuntimeException thrown just after this");
             tec.setStatusMessage(errorMessage.toString());
diff --git a/src/main/resources/db/migration/V19__Add_Organization_Property_Default_Grid_DataSource.sql b/src/main/resources/db/migration/V19__Add_Organization_Property_Default_Grid_DataSource.sql
new file mode 100644
index 0000000000000000000000000000000000000000..b254b3e45832acef2b0e85af03c170362322435b
--- /dev/null
+++ b/src/main/resources/db/migration/V19__Add_Organization_Property_Default_Grid_DataSource.sql
@@ -0,0 +1,3 @@
+-- Adding this property when adding support for gridded weather datasources in VIPS
+ALTER TABLE organization
+ADD COLUMN default_grid_weather_station_data_source_id INTEGER REFERENCES weather_station_data_source(weather_station_data_source_id) DEFAULT NULL;
\ No newline at end of file
diff --git a/src/main/resources/db/migration/V20__Add_Datasource_Property_IsGrid.sql b/src/main/resources/db/migration/V20__Add_Datasource_Property_IsGrid.sql
new file mode 100644
index 0000000000000000000000000000000000000000..468128cbcf53a0e66d5b8373095debb8491592b7
--- /dev/null
+++ b/src/main/resources/db/migration/V20__Add_Datasource_Property_IsGrid.sql
@@ -0,0 +1,3 @@
+-- Adding this property when adding support for gridded weather datasources in VIPS
+ALTER TABLE weather_station_data_source
+ADD COLUMN is_grid BOOLEAN DEFAULT FALSE;
\ No newline at end of file
diff --git a/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts.properties b/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts.properties
index e7caec3ae87ef744e17f826821f0ca3da4b91607..d1b535a4cc9b8bfe6cb4e23143f9a04c0edefe1f 100755
--- a/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts.properties
+++ b/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts.properties
@@ -1056,3 +1056,12 @@ thresholdDSVMax=DSV threshold for high infection risk
 thresholdDSVTempMin=Minimum temperature for DSV calculation
 useGridWeatherData=Use grid weather data
 doNotUse=Do not use
+defaultGridWeatherStationDataSource=Gridded weather data source
+weatherStationDataSources=Weather station data sources
+newWeatherStationDataSource=New weather (station) data source
+editWeatherStationDataSource=Edit weather (station) data source
+datafetchUriExpression=URI template for requesting data
+infoUriExpression=Template for request for station information
+isGridWeatherDataSource=This is a grid based weather data source
+weatherStationDataSourceStored=Weather (station) data source was successfully stored
+weatherStationDataSourceDeleted=The weather (station) data source was successfully deleted
diff --git a/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_bs.properties b/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_bs.properties
index b632979d4bef2c6f99f1b0a2908e54e7ceee5671..69fb5f1d3cda2371e2ac60f20c77b441c431eb66 100755
--- a/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_bs.properties
+++ b/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_bs.properties
@@ -1048,3 +1048,12 @@ thresholdDSVMax=DSV threshold for high infection risk
 thresholdDSVTempMin=Minimum temperature for DSV calculation
 useGridWeatherData=Use grid weather data
 doNotUse=Do not use
+defaultGridWeatherStationDataSource=Gridded weather data source
+weatherStationDataSources=Weather station data sources
+newWeatherStationDataSource=New weather (station) data source
+editWeatherStationDataSource=Edit weather (station) data source
+datafetchUriExpression=URI template for requesting data
+infoUriExpression=Template for request for station information
+isGridWeatherDataSource=This is a grid based weather data source
+weatherStationDataSourceStored=Weather (station) data source was successfully stored
+weatherStationDataSourceDeleted=The weather (station) data source was successfully deleted
diff --git a/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_hr.properties b/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_hr.properties
index 286433a39e854e88ffbc69e3174ea1a676fa5e55..fcde6d89ad112ea828d6f8663fcb3e1def2c91a2 100755
--- a/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_hr.properties
+++ b/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_hr.properties
@@ -1046,3 +1046,12 @@ thresholdDSVMax=DSV threshold for high infection risk
 thresholdDSVTempMin=Minimum temperature for DSV calculation
 useGridWeatherData=Use grid weather data
 doNotUse=Do not use
+defaultGridWeatherStationDataSource=Gridded weather data source
+weatherStationDataSources=Weather station data sources
+newWeatherStationDataSource=New weather (station) data source
+editWeatherStationDataSource=Edit weather (station) data source
+datafetchUriExpression=URI template for requesting data
+infoUriExpression=Template for request for station information
+isGridWeatherDataSource=This is a grid based weather data source
+weatherStationDataSourceStored=Weather (station) data source was successfully stored
+weatherStationDataSourceDeleted=The weather (station) data source was successfully deleted
diff --git a/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_nb.properties b/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_nb.properties
index 1d3db33c3426184e4df91b2ccd1dca2d3e632b81..bc7b59b08ae5e993f8616d0b36d54d476f6b67e5 100755
--- a/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_nb.properties
+++ b/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_nb.properties
@@ -1056,3 +1056,12 @@ thresholdDSVMax=DSV-terskel for h\u00f8y infeksjonsrisiko
 thresholdDSVTempMin=Minimumstemperatur for beregning av DSV
 useGridWeatherData=Bruk v\u00e6rdata fra rutenett
 doNotUse=Ikke bruk
+defaultGridWeatherStationDataSource=GRID-basert v\u00e6rdatakilde
+weatherStationDataSources=V\u00e6r(stasjons)datakilder
+newWeatherStationDataSource=Ny v\u00e6r(stasjons)datakilde
+editWeatherStationDataSource=Rediger v\u00e6r(stasjons)datakilde
+datafetchUriExpression=URI-mal for v\u00e6rdataforesp\u00f8rsel
+infoUriExpression=Mal for henvendelse om stasjonsinformasjon
+isGridWeatherDataSource=Er en grid-basert v\u00e6rdatakilde
+weatherStationDataSourceStored=V\u00e6r(stasjons)datakilden ble lagret
+weatherStationDataSourceDeleted=V\u00e6r(stasjons)datakilden ble slettet
diff --git a/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_sr.properties b/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_sr.properties
index 044f7b23e28adf500929451368da17dfde0b3908..7eb7473abecd9c43cc4a7c7829f13ae9cb3c4052 100755
--- a/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_sr.properties
+++ b/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_sr.properties
@@ -1048,3 +1048,12 @@ thresholdDSVMax=DSV threshold for high infection risk
 thresholdDSVTempMin=Minimum temperature for DSV calculation
 useGridWeatherData=Use grid weather data
 doNotUse=Do not use
+defaultGridWeatherStationDataSource=Gridded weather data source
+weatherStationDataSources=Weather station data sources
+newWeatherStationDataSource=New weather (station) data source
+editWeatherStationDataSource=Edit weather (station) data source
+datafetchUriExpression=URI template for requesting data
+infoUriExpression=Template for request for station information
+isGridWeatherDataSource=This is a grid based weather data source
+weatherStationDataSourceStored=Weather (station) data source was successfully stored
+weatherStationDataSourceDeleted=The weather (station) data source was successfully deleted
diff --git a/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_zh_CN.properties b/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_zh_CN.properties
index 66a5fd1c01511447b57bb0470e725877b76641f8..8e0f57063b6cf713ffe34b4cfc16e42d4e119cfc 100755
--- a/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_zh_CN.properties
+++ b/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_zh_CN.properties
@@ -1042,3 +1042,12 @@ thresholdDSVMax=DSV threshold for high infection risk
 thresholdDSVTempMin=Minimum temperature for DSV calculation
 useGridWeatherData=Use grid weather data
 doNotUse=Do not use
+defaultGridWeatherStationDataSource=Gridded weather data source
+weatherStationDataSources=Weather station data sources
+newWeatherStationDataSource=New weather (station) data source
+editWeatherStationDataSource=Edit weather (station) data source
+datafetchUriExpression=URI template for requesting data
+infoUriExpression=Template for request for station information
+isGridWeatherDataSource=This is a grid based weather data source
+weatherStationDataSourceStored=Weather (station) data source was successfully stored
+weatherStationDataSourceDeleted=The weather (station) data source was successfully deleted
diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml
index 62d85f54fb23655a8bafb80e6f02925b270da5a8..2f2ca6f05c3225d9c37a4a9b10fd4695bd726ee3 100755
--- a/src/main/webapp/WEB-INF/web.xml
+++ b/src/main/webapp/WEB-INF/web.xml
@@ -79,6 +79,10 @@
         <servlet-name>CropCategoryController</servlet-name>
         <servlet-class>no.nibio.vips.logic.controller.servlet.CropCategoryController</servlet-class>
     </servlet>
+    <servlet>
+        <servlet-name>WeatherStationDataSourceController</servlet-name>
+        <servlet-class>no.nibio.vips.logic.controller.servlet.WeatherStationDataSourceController</servlet-class>
+    </servlet>
     <servlet>
         <servlet-name>JSEnvironment</servlet-name>
         <servlet-class>no.nibio.vips.logic.web.js.JSEnvironment</servlet-class>
@@ -156,6 +160,10 @@
         <servlet-name>CropCategoryController</servlet-name>
         <url-pattern>/organism/cropcategory</url-pattern>
     </servlet-mapping>
+    <servlet-mapping>
+        <servlet-name>WeatherStationDataSourceController</servlet-name>
+        <url-pattern>/weatherstationdatasource</url-pattern>
+    </servlet-mapping>
     <servlet-mapping>
         <servlet-name>JSEnvironment</servlet-name>
         <url-pattern>/js/environment.js</url-pattern>
diff --git a/src/main/webapp/formdefinitions/organizationForm.json b/src/main/webapp/formdefinitions/organizationForm.json
index acc63d9e460de6b61f08b83b7870926d11bfce3f..4ff9c999cf3cdd406e5b6db626f46b8564ebdf61 100644
--- a/src/main/webapp/formdefinitions/organizationForm.json
+++ b/src/main/webapp/formdefinitions/organizationForm.json
@@ -95,6 +95,12 @@
             "name" : "defaultMapCenter",
             "dataType" : "STRING",
             "required" : false
+        },
+        {
+            "name": "defaultGridWeatherStationDataSourceId",
+            "dataType": "INTEGER",
+            "fieldType": "SELECT_SINGLE",
+            "required": false
         }
         
     ]
diff --git a/src/main/webapp/formdefinitions/weatherStationDataSourceForm.json b/src/main/webapp/formdefinitions/weatherStationDataSourceForm.json
new file mode 100644
index 0000000000000000000000000000000000000000..35cf480345f1f784dba1cd19c2b555b553245fbe
--- /dev/null
+++ b/src/main/webapp/formdefinitions/weatherStationDataSourceForm.json
@@ -0,0 +1,59 @@
+{
+    "_licenseNote": [
+       "Copyright (c) 2024 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",
+        "the Free Software Foundation, either version 3 of the License, or",
+        "(at your option) any later version.",
+        
+        "This program 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",
+        "GNU Affero General Public License for more details.",
+ 
+        "You should have received a copy of the GNU Affero General Public License",
+        "along with this program.  If not, see <https://www.gnu.org/licenses/>."
+    ],
+    "_comment" : "Structure of the organizationForm and how to validate it",
+    "fields": [
+        {
+            "name" : "weatherStationDataSourceId",
+            "dataType" : "INTEGER",
+            "required" : true
+        },
+        {
+            "name" : "name",
+            "dataType" : "STRING",
+            "required" : true
+        },
+        {
+            "name" : "defaultDescription",
+            "dataType" : "STRING",
+            "required" : false
+        },
+        {
+            "name" : "uri",
+            "dataType" : "STRING",
+            "required" : false
+        },
+        {
+            "name" : "datafetchUriExpression",
+            "dataType" : "STRING",
+            "required" : true
+        },
+        {
+            "name" : "infoUriExpression",
+            "dataType" : "STRING",
+            "required" : false
+        },
+        {
+            "name" : "isGrid",
+            "dataType" : "STRING",
+            "required" : false
+        }
+        
+    ]
+}
diff --git a/src/main/webapp/templates/forecastConfigurationForm.ftl b/src/main/webapp/templates/forecastConfigurationForm.ftl
index d1037a361da27fa37e7d337816b9d9f56b4ce0bd..c574fa7f0d5b5c9069bdfdc4945925cb955d5bb3 100755
--- a/src/main/webapp/templates/forecastConfigurationForm.ftl
+++ b/src/main/webapp/templates/forecastConfigurationForm.ftl
@@ -15,6 +15,7 @@
     You should have received a copy of the GNU Affero General Public License
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 --><#include "master.ftl">
+<#assign isGridForecastSupported = (user.organizationId.defaultGridWeatherStationDataSource?has_content)>
 <#macro page_head>
     <title>${i18nBundle.viewForecastConfiguration}</title>
 </#macro>
@@ -316,7 +317,9 @@
           };
 
           // Setting weather station select list state correct on page load
-          handleUseGridWeatherDataClicked(document.getElementById("useGridWeatherData")<#if forecastConfiguration.weatherStationPointOfInterestId?has_content>,${forecastConfiguration.weatherStationPointOfInterestId.pointOfInterestId}</#if>);
+          <#if isGridForecastSupported>
+          handleUseGridWeatherDataClicked(document.getElementById("useGridWeatherData")<#if forecastConfiguration.weatherStationPointOfInterestId?has_content && forecastConfiguration.weatherStationPointOfInterestId.pointOfInterestId?has_content>,${forecastConfiguration.weatherStationPointOfInterestId.pointOfInterestId}</#if>);
+          </#if>
 
 	</script>
 </#macro>
@@ -421,23 +424,30 @@
 	  <div class="form-group">
 	    <label for="weatherStationPointOfInterestId">${i18nBundle.weatherStationPointOfInterestId}</label>
 	    <select class="form-control" id="weatherStationPointOfInterestId" name="weatherStationPointOfInterestId" onblur="if(!document.getElementById('useGridWeatherData').checked) {validateField(this);};">
-	    	<option value="-2">${i18nBundle.doNotUse} ${i18nBundle.weatherStationPointOfInterestId?lower_case}</option>
+	    	<#if isGridForecastSupported>
+          <option value="-2">${i18nBundle.doNotUse} ${i18nBundle.weatherStationPointOfInterestId?lower_case}</option>
+        </#if>
         <option value="-1"<#if !forecastConfiguration.weatherStationPointOfInterestId?has_content && !forecastConfiguration.useGridWeatherData> selected="selected"</#if>>${i18nBundle.pleaseSelect} ${i18nBundle.weatherStationPointOfInterestId?lower_case}</option>
 	    	<#list weatherStationPointOfInterests?sort_by("name") as poi>
-	    	<option value="${poi.pointOfInterestId}"<#if forecastConfiguration.weatherStationPointOfInterestId?has_content && poi.pointOfInterestId == forecastConfiguration.weatherStationPointOfInterestId.pointOfInterestId> selected="selected"</#if>>${poi.name}</option>
+	    	<option value="${poi.pointOfInterestId}"<#if 
+          forecastConfiguration.weatherStationPointOfInterestId?has_content 
+          && forecastConfiguration.weatherStationPointOfInterestId.pointOfInterestId?has_content 
+          && poi.pointOfInterestId == forecastConfiguration.weatherStationPointOfInterestId.pointOfInterestId> selected="selected"</#if>>${poi.name}</option>
 	    	</#list>
 	    </select>
 	    <span class="help-block" id="${formId}_weatherStationPointOfInterestId_validation"></span>
 	  </div>
-    <input type="hidden" id="weatherStationPointOfInterestIdHidden" name="weatherStationPointOfInterestIdDisabled" value="-2" disabled="disabled"/>
-    <div class="form-group">
-		  <div class="checkbox">
-			<label>
-				<input type="checkbox" id="useGridWeatherData" name="useGridWeatherData"<#if forecastConfiguration.useGridWeatherData?has_content && forecastConfiguration.useGridWeatherData == true> checked="checked"</#if> onclick="handleUseGridWeatherDataClicked(this);"/>
-			</label>
-			${i18nBundle.useGridWeatherData}
-      <span class="help-block" id="${formId}_useGridWeatherData_validation"></span>
-		</div>
+    <#if isGridForecastSupported>
+      <input type="hidden" id="weatherStationPointOfInterestIdHidden" name="weatherStationPointOfInterestIdDisabled" value="-2" disabled="disabled"/>
+      <div class="form-group">
+        <div class="checkbox">
+        <label>
+          <input type="checkbox" id="useGridWeatherData" name="useGridWeatherData"<#if forecastConfiguration.useGridWeatherData?has_content && forecastConfiguration.useGridWeatherData == true> checked="checked"</#if> onclick="handleUseGridWeatherDataClicked(this);"/>
+        </label>
+        ${i18nBundle.useGridWeatherData}
+        <span class="help-block" id="${formId}_useGridWeatherData_validation"></span>
+      </div>
+    </#if>
 	  <#else>
 	  <input type="hidden" name="multipleNew" value="true"/>
 	  <div class="form-group">
diff --git a/src/main/webapp/templates/master.ftl b/src/main/webapp/templates/master.ftl
index 0027c08c231fb3c53e0a7141a1e240a62efbf4d2..712e6aafac85f7a6ec85871ea3f78bac317a28ec 100755
--- a/src/main/webapp/templates/master.ftl
+++ b/src/main/webapp/templates/master.ftl
@@ -59,6 +59,9 @@
 		  <#else>
 		  	<li><a href="/user?action=viewUser&userId=${user.userId}">${i18nBundle.myAccount}</a></li>
 		  </#if>
+		  <#if user.isOrganizationAdmin() || user.isSuperUser()>
+		  	<li><a href="/weatherstationdatasource">${i18nBundle.weatherStationDataSources}</a></li>
+		  </#if>
 		  <li><a href="/poi">${i18nBundle.pois}</a></li>
 		  <#if user.isOrganizationAdmin() || user.isSuperUser() || user.isMessageAuthor()>
 		  	<li><a href="/message">${i18nBundle.messages}</a></li>
diff --git a/src/main/webapp/templates/organizationForm.ftl b/src/main/webapp/templates/organizationForm.ftl
index 50ac1afc38f3741305c4cec59726a0e8d09ee2c6..cb907e210e7a0fffae2d438bd39fc540d2b57633 100644
--- a/src/main/webapp/templates/organizationForm.ftl
+++ b/src/main/webapp/templates/organizationForm.ftl
@@ -119,7 +119,7 @@
 			    <input type="text" class="form-control" name="vipswebUrl" placeholder="${i18nBundle.vipswebUrl}" value="${(organization.vipswebUrl)!""}" onblur="validateField(this); "/>
 			    <span class="help-block" id="${formId}_vipswebUrl_validation"></span>
 			  </div>
-                          <div class="form-group">
+                <div class="form-group">
 			    <label for="archiveUserId">${i18nBundle.archiveUserId}</label>
 			    <select class="form-control" name="archiveUserId" onblur="validateField(this);">
 			    	<option value="-1">${i18nBundle.pleaseSelect} ${i18nBundle.archiveUserId?lower_case}
@@ -131,21 +131,33 @@
 			     </select>
 			     <span class="help-block" id="${formId}_archiveUserId_validation"></span>
 			  </div>
-                          <div class="form-group">
+                <div class="form-group">
 			    <label for="defaultVipsCoreUserId">${i18nBundle.defaultVipsCoreUserId}</label>
 			    <input type="number" class="form-control" name="defaultVipsCoreUserId" placeholder="${i18nBundle.defaultVipsCoreUserId}" value="${(organization.defaultVipsCoreUserId)!""}" onblur="validateField(this); "/>
 			    <span class="help-block" id="${formId}_defaultVipsCoreUserId_validation"></span>
 			  </div>
-                           <div class="form-group">
+                <div class="form-group">
 			    <label for="defaultMapZoom">${i18nBundle.defaultMapZoom}</label>
 			    <input type="number" class="form-control" name="defaultMapZoom" placeholder="${i18nBundle.defaultMapZoom}" value="${(organization.defaultMapZoom)!""}" onblur="validateField(this); "/>
 			    <span class="help-block" id="${formId}_defaultMapZoom_validation"></span>
 			  </div>
-                          <div class="form-group">
+                <div class="form-group">
 			    <label for="defaultMapCenter">${i18nBundle.defaultMapCenter} (WGS84 longitude,latitude)</label>
 			    <input type="text" class="form-control" name="defaultMapCenter" placeholder="${i18nBundle.defaultMapCenter}" value="${(defaultMapCenterLon?c)!""},${(defaultMapCenterLat?c)!""}" onblur="validateField(this); "/>
 			    <span class="help-block" id="${formId}_defaultMapCenter_validation"></span>
 			  </div>
+			  <div class="form-group">
+			    <label for="defaultGridWeatherStationDataSourceId">${i18nBundle.defaultGridWeatherStationDataSource}</label>
+			    <select class="form-control" name="defaultGridWeatherStationDataSourceId" onblur="validateField(this);">
+			    	<option value="-1">${i18nBundle.pleaseSelect} ${i18nBundle.defaultGridWeatherStationDataSource?lower_case}
+				<#list gridWeatherDataSources as dSource>
+					<option value="${dSource.weatherStationDataSourceId}"
+						<#if organization.defaultGridWeatherStationDataSource?has_content && organization.defaultGridWeatherStationDataSource.weatherStationDataSourceId == dSource.weatherStationDataSourceId>selected="selected"</#if>
+					>${dSource.name}</option>
+				</#list>
+			     </select>
+			     <span class="help-block" id="${formId}_defaultGridWeatherStationDataSourceId_validation"></span>
+			  </div>
 			  <button type="submit" class="btn btn-default">${i18nBundle.submit}</button>
 			  <#if organization.organizationId?has_content>
 			  <button type="button" class="btn btn-danger" onclick="if(confirm('${i18nBundle.confirmDelete}')){alert('Sorry, but this functionality has not yet been implemented');}">${i18nBundle.delete}</button>
diff --git a/src/main/webapp/templates/weatherStationDataSourceForm.ftl b/src/main/webapp/templates/weatherStationDataSourceForm.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..d386d502e3a02eaea22d83ad0139cc8254c53674
--- /dev/null
+++ b/src/main/webapp/templates/weatherStationDataSourceForm.ftl
@@ -0,0 +1,97 @@
+<#-- 
+  Copyright (c) 2024 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
+  the Free Software Foundation, either version 3 of the License, or
+  (at your option) any later version.
+
+  This program 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
+  GNU Affero General Public License for more details.
+
+  You should have received a copy of the GNU Affero General Public License
+  along with this program.  If not, see <https://www.gnu.org/licenses/>.
+--><#include "master.ftl">
+<#assign formId = "weatherStationDataSourceForm">
+<#macro page_head>
+        <title><#if weatherStationDataSource.weatherStationDataSourceId?has_content>${i18nBundle.editWeatherStationDataSource} ${weatherStationDataSource.name}<#else>${i18nBundle.newWeatherStationDataSource}</#if></title>
+</#macro>
+<#macro custom_css>
+	<link rel="stylesheet" type="text/css" href="/css/3rdparty/ol.css"/ >
+</#macro>
+<#macro custom_js>
+	<script type="text/javascript" src="/js/constants.js"></script>
+	<script type="text/javascript" src="/js/resourcebundle.js"></script>
+	<script type="text/javascript" src="/js/validateForm.js"></script>
+	<script type="text/javascript">
+		$(document).ready(function() {
+			// Load main form definition (for validation)
+			loadFormDefinition("${formId}");
+			
+		});
+        function handleWeatherStationDataSourceDelete(weatherStationDataSourceId){  
+                window.location.href="/weatherstationdatasource?action=deleteWeatherStationDataSource&weatherStationDataSourceId=" + weatherStationDataSourceId;
+        }
+                
+	</script>
+</#macro>
+<#macro page_contents>
+<div class="singleBlockContainer">
+	<p><a href="/weatherstationdatasource" class="btn btn-default back" role="button">${i18nBundle.back}</a></p>
+    <h1><#if weatherStationDataSource.weatherStationDataSourceId?has_content>${i18nBundle.editWeatherStationDataSource} ${weatherStationDataSource.name}<#else>${i18nBundle.newWeatherStationDataSource}</#if></h1>
+    <div id="errorMsgEl" class="alert alert-danger" <#if !formValidation?has_content> style="display:none;"</#if>>
+		<#if formValidation?has_content>${formValidation.validationMessages?replace("\n", "<br>")}</#if>
+	</div>
+    <#if messageKey?has_content>
+		<div class="alert alert-success">${i18nBundle(messageKey)}</div>
+	</#if>
+    <div class="row">
+		<div class="col-md-12">
+			<form id="${formId}" role="form" action="/weatherstationdatasource?action=weatherStationDataSourceFormSubmit" method="POST" onsubmit="return validateForm(this);">
+                <input type="hidden" name="weatherStationDataSourceId" value="${weatherStationDataSource.weatherStationDataSourceId!"-1"}"/>
+                <div class="form-group">
+                    <label for="name">${i18nBundle.name}</label>
+                    <input type="text" class="form-control" name="name" placeholder="${i18nBundle.name}" value="${(weatherStationDataSource.name)!""}" onblur="validateField(this); "/>
+			        <span class="help-block" id="${formId}_name_validation"></span>
+			    </div>
+                <div class="form-group">
+                    <label for="defaultDescription">${i18nBundle.description}</label>
+                    <input type="text" class="form-control" name="defaultDescription" placeholder="${i18nBundle.description}" value="${(weatherStationDataSource.defaultDescription)!""}" onblur="validateField(this); "/>
+			        <span class="help-block" id="${formId}_defaultDescription_validation"></span>
+			    </div>
+                <div class="form-group">
+                    <label for="uri">URI</label>
+                    <input type="url" class="form-control" name="uri" placeholder="URI" value="${(weatherStationDataSource.uri)!""}" onblur="validateField(this); "/>
+			        <span class="help-block" id="${formId}_uri_validation"></span>
+			    </div>
+                <div class="form-group">
+                    <label for="datafetchUriExpression">${i18nBundle.datafetchUriExpression}</label>
+                    <input type="url" class="form-control" name="datafetchUriExpression" placeholder="${i18nBundle.datafetchUriExpression}" value="${(weatherStationDataSource.datafetchUriExpression)!""}" onblur="validateField(this); "/>
+			        <span class="help-block" id="${formId}_datafetchUriExpression_validation"></span>
+			    </div>
+                <div class="form-group">
+                    <label for="infoUriExpression">${i18nBundle.infoUriExpression}</label>
+                    <input type="url" class="form-control" name="infoUriExpression" placeholder="${i18nBundle.infoUriExpression}" value="${(weatherStationDataSource.infoUriExpression)!""}" onblur="validateField(this); "/>
+			        <span class="help-block" id="${formId}_infoUriExpression_validation"></span>
+			    </div>
+                <div class="form-group">
+                <div class="checkbox">
+                    <label>
+                    <input type="checkbox" id="isGrid" name="isGrid" value="true"<#if weatherStationDataSource.isGrid?has_content && weatherStationDataSource.isGrid == true> checked="checked"</#if>/>
+                    </label>
+                    ${i18nBundle.isGridWeatherDataSource}
+                    <span class="help-block" id="${formId}_isGrid_validation"></span>
+                </div>
+                <button type="submit" class="btn btn-default">${i18nBundle.submit}</button>
+			  <#if weatherStationDataSource.weatherStationDataSourceId?has_content>
+			  <button type="button" class="btn btn-danger" onclick="if(confirm('${i18nBundle.confirmDelete}')){handleWeatherStationDataSourceDelete(${weatherStationDataSource.weatherStationDataSourceId});}">${i18nBundle.delete}</button>
+			  </#if>
+            </form>
+        </div>
+    </div>
+</div>
+</#macro>
+<@page_html/>
\ No newline at end of file
diff --git a/src/main/webapp/templates/weatherStationDataSourceList.ftl b/src/main/webapp/templates/weatherStationDataSourceList.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..82c6b0eb62b9c386636027ee68973a20de718fd0
--- /dev/null
+++ b/src/main/webapp/templates/weatherStationDataSourceList.ftl
@@ -0,0 +1,39 @@
+<#-- 
+    Copyright (c) 2024 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
+  the Free Software Foundation, either version 3 of the License, or
+  (at your option) any later version.
+
+  This program 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
+  GNU Affero General Public License for more details.
+
+  You should have received a copy of the GNU Affero General Public License
+  along with this program.  If not, see <https://www.gnu.org/licenses/>.
+--><#include "master.ftl">
+<#macro page_head>
+        <title>${i18nBundle.weatherStationDataSources}</title>
+</#macro>
+<#macro custom_css>
+</#macro>
+<#macro custom_js>
+</#macro>
+<#macro page_contents>
+<div class="singleBlockContainer">
+<#if messageKey?has_content>
+        <div class="alert alert-success">${i18nBundle(messageKey)}</div>
+</#if>
+<h1>${i18nBundle.weatherStationDataSources}</h1>
+<ul>
+<#list weatherStationDataSources as dSource>
+<li><a href="/weatherstationdatasource?action=editWeatherStationDataSource&weatherStationDataSourceId=${dSource.weatherStationDataSourceId}">${dSource.name}</a></li>
+</#list>
+</ul>
+<p><a href="/weatherstationdatasource?action=newWeatherStationDataSource" class="btn btn-default back" role="button">${i18nBundle.newWeatherStationDataSource}</a></p>
+</div>
+</#macro>
+<@page_html/>