diff --git a/src/main/java/no/nibio/vips/logic/service/GrowthStageService.java b/src/main/java/no/nibio/vips/logic/service/GrowthStageService.java
index a2208740dc5b25f693da6213e4fa811684dfb2e7..7803810bdb328bf803b0ce5db1ce9fb2c2ac410f 100644
--- a/src/main/java/no/nibio/vips/logic/service/GrowthStageService.java
+++ b/src/main/java/no/nibio/vips/logic/service/GrowthStageService.java
@@ -59,7 +59,8 @@ public class GrowthStageService {
     public Response getDateForGrowthStage(
             @PathParam("organismId") Integer organismId,
             @PathParam("growthStages") String growthStagesStr,
-            @QueryParam("location") String location
+            @QueryParam("location") String location,
+            @QueryParam("season") Integer season
     )
     {
         //SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
@@ -72,15 +73,15 @@ public class GrowthStageService {
             
             if(growthStage.equals(32))
             {
-                gsDate.put(growthStage, LocalDate.of(LocalDate.now().getYear(), Month.JUNE,10));
+                gsDate.put(growthStage, LocalDate.of(season != null ? season : LocalDate.now().getYear(), Month.JUNE,10));
             }
             else if(growthStage.equals(71))
             {
-                gsDate.put(growthStage, LocalDate.of(LocalDate.now().getYear(), Month.JULY,25));
+                gsDate.put(growthStage, LocalDate.of(season != null ? season : LocalDate.now().getYear(), Month.JULY,25));
             }
             else
             {
-                gsDate.put(growthStage, LocalDate.of(LocalDate.now().getYear(), Month.JANUARY,1));
+                gsDate.put(growthStage, LocalDate.of(season != null ? season : LocalDate.now().getYear(), Month.JANUARY,1));
             }
             retVal.add(gsDate);
         }
diff --git a/src/main/webapp/public/nordic_septoria_map/nordic_septoria_map.css b/src/main/webapp/public/nordic_septoria_map/nordic_septoria_map.css
index 168d52b6247b038a7b83ff0a9503f5260e50e945..1ce9a56a3eaf8219481f5a076421d73b7471308d 100644
--- a/src/main/webapp/public/nordic_septoria_map/nordic_septoria_map.css
+++ b/src/main/webapp/public/nordic_septoria_map/nordic_septoria_map.css
@@ -64,7 +64,7 @@ along with VIPSLogic.  If not, see <http://www.nibio.no/licenses/>.
     }
 }
 
-#VIPSAttribution, .dateField, #layersField {
+#VIPSAttribution, .dateField, #layersField, #seasonField {
     position: absolute;
     z-index: 1000;
     font-family: Arial, Helvetica, sans-serif;
@@ -89,10 +89,14 @@ along with VIPSLogic.  If not, see <http://www.nibio.no/licenses/>.
     right: 10px;
 }
 
-#layersField {
+#layersField, #seasonField {
     left: 45px;
 }
 
+#seasonField {
+    top: 150px;
+}
+
 #subMap1 .ol-attribution, #subMap2 .ol-attribution, #subMap3 .ol-attribution, #subMap4 .ol-attribution  ,
 #subMap1 .ol-zoom, #subMap2 .ol-zoom, #subMap3 .ol-zoom, #subMap4 .ol-zoom  
 {
diff --git a/src/main/webapp/public/nordic_septoria_map/nordic_septoria_map.js b/src/main/webapp/public/nordic_septoria_map/nordic_septoria_map.js
index 287c64345591076d6fda228ae2e8a78b18fe2cee..7a21f851ca7a400462352565de34adc0e3f3acc8 100644
--- a/src/main/webapp/public/nordic_septoria_map/nordic_septoria_map.js
+++ b/src/main/webapp/public/nordic_septoria_map/nordic_septoria_map.js
@@ -37,16 +37,32 @@ for (var i in views)
 var maps = {mainMap:null, subMap1: null, subMap2: null, subMap3: null, subMap4: null};
 var language = "en";
 
+/**
+ * After all libraries have been loaded (added to the containing page), this
+ * function sets up all the maps and displays the initial view
+ * @returns {undefined}
+ */
 var initMap = function ()
 {
     todayAtMidnight = getTodayAtMidnight();
     var nordicSeptoriaMapContainer = document.getElementById("nordicSeptoriaMapContainer");
-    language = nordicSeptoriaMapContainer.getAttribute("data-language") != null ? nordicSeptoriaMapContainer.getAttribute("data-language") : language;
+    // This is being used by dict, the translation tables
+    language = nordicSeptoriaMapContainer.getAttribute("data-language") !== null ? nordicSeptoriaMapContainer.getAttribute("data-language") : language;
+    // Giving the user the option to choose between the different views
     var viewRadioList = "";
     for (var i in views)
     {
         viewRadioList += "     <input type='radio' name='selectedlayer' " + (views[i] == initialView ? "checked " : "") + "value='" + views[i] + "' onclick='showLayer(this.value);'/> " + geti18nText(views[i]) + "<br/>"
     }
+    // Model results should be calculated by VIPS from 2014 and forward
+    var seasonList = "";
+    var currentSeason = new Date().getFullYear();
+    for(var season=2014;season<=currentSeason;season++)
+    {
+        seasonList += "     <option value='" + season + "'" + (season == currentSeason ? " selected='selected'" : "") + ">" + season + "</option>";
+    }
+    // This HTML is injected into the hosting web page. It contains all of the
+    // maps and the controls
     nordicSeptoriaMapContainer.innerHTML = "<div id='mainMap'>"
                                     + " <div id='popupTooltip_mainMap' class='ol-popup'>"
                                     + "     <a href='#' class='ol-popup-closer' onclick='closeOverlay(this);'></a>"
@@ -61,6 +77,13 @@ var initMap = function ()
                                     + " <div id='layersField'>"
                                     + viewRadioList
                                     + " </div>"
+                                    + " <div id='seasonField'>"
+                                    + " <select id='seasonList' name='season' onchange='changeSeason(this.options[this.options.selectedIndex].value);'>"
+                                    + seasonList
+                                    + " </select>"
+                                    + " <input type='date' id='startDate' name='startDate' min='" + currentSeason + "-01-01' onchange='updateResults();'/> "
+                                    + " <input type='date' id='endDate' name='endDate' max='" + currentSeason + "-12-31' onchange='updateResults();'/>"
+                                    + " </div>"
                                     + " <div id='VIPSAttribution'>" + geti18nText("poweredBy") + " <a href='https://www.vips-landbruk.no/' target='new'><img id='VIPSLogo' src='" + hostName + "/public/nordic_septoria_map/logo_vips.png'/></a></div>"
                                     + "</div>"
                                     + "<div id='subMap1'>" 
@@ -90,7 +113,7 @@ var initMap = function ()
                                     + "<div id='popup'></div>"
                                     + "</div>";
                                     
-    // Initializing all the layers for all maps
+    // Initializing all the layers (one for each model result view) for all maps
     for (var i in views)
     {
         for(var mapName in featureOverlays[views[i]])
@@ -104,7 +127,7 @@ var initMap = function ()
         }
     }
     
-    // Creating the 5 maps
+    // Creating the 5 maps (present day + 4 days ahead)
     for(var mapName in maps)
     {
         var currentMap = currentMap;
@@ -114,9 +137,9 @@ var initMap = function ()
             layers: [
               new ol.layer.Tile({
                 source: new ol.source.OSM()
-              })//,
-              //featureOverlays["WHS"][mapName]
+              })
             ],
+            // This is the cartoon text bubble
             overlays: [new ol.Overlay({
                     element: document.getElementById('popupTooltip_' + mapName),
                     autoPan: true,
@@ -132,6 +155,7 @@ var initMap = function ()
         {
             maps[mapName].addLayer(featureOverlays[views[i]][mapName]);
         }
+        // When clicking on a feature - get the details and display
         maps[mapName].on('singleclick', function(evt) {
             var pixel = evt.map.getEventPixel(evt.originalEvent);
             var coordinate = evt.coordinate;
@@ -139,9 +163,63 @@ var initMap = function ()
 	});
     }
     showLayer(initialView);
+};
+
+var updateResults = function() {
+  console.info("updateResults: TODO");  
+};
+
+/**
+ * 
+ * @returns {undefined}All features are removed. Used at when switching season
+ */
+var clearAll = function()
+{
+    for (var i in views)
+    {
+        for(var mapName in featureOverlays[views[i]])
+        {
+            featureOverlays[views[i]][mapName].getSource().clear();
+        }
+    }
+}
 
+/**
+ * This function does what you think! It clears all current features
+ * and gets/displays results for the new selected season
+ */
+var changeSeason = function(selectedSeason)
+{
+  // Clear layer(s)
+  clearAll();
+  // Set date field limits
+  var startDate = document.getElementById("startDate").value;
+  var endDate = document.getElementById("endDate").value;
+  document.getElementById("startDate").value = 
+          document.getElementById("startDate").value != "" ? 
+            selectedSeason + document.getElementById("startDate").value.substring(4) : 
+            "";
+  document.getElementById("endDate").value = 
+          document.getElementById("endDate").value != "" ? 
+            selectedSeason + document.getElementById("endDate").value.substring(4) : 
+            "";
+  // Get results for this season
+  getResults[getVisibleLayerName()](getCurrentSeason());
+  
 };
 
+/**
+ * Which season is currently in use? Checking the season select list
+ */
+var getCurrentSeason = function()
+{
+    return parseInt(document.getElementById('seasonList').options[document.getElementById('seasonList').options.selectedIndex].value);
+}
+
+/**
+ * Collects features at the point clicked on the map. Displays the feature
+ * properties (differs between views/models) in a cartoon text bubble
+ */
 var displayFeatureDetails = function(map, pixel, coordinate)
 {
     var features = [];
@@ -159,6 +237,9 @@ var displayFeatureDetails = function(map, pixel, coordinate)
     currentOverlay.setPosition(coordinate);
 }
 
+/**
+ * Returns a text representation of the feature, including specific properties
+ */
 var getFeatureDetails = {
     "WHS": function(features) { return "TODO"; },
     "rainyDays": function(features) {
@@ -167,6 +248,9 @@ var getFeatureDetails = {
     }
 };
 
+/**
+ * Closing the cartoon text bubble (clicked feature details)
+ */
 var closeOverlay = function(theCloser)
 {
     var currentOverlay = maps[theCloser.parentNode.id.split("_")[1]].getOverlays().item(0);
@@ -183,13 +267,12 @@ var closeOverlay = function(theCloser)
  */
 function showLayer(layerName)
 {
+    //console.info("Attempting to show " + layerName);
     // Has this layer been initialized already?
-    console.info("Attempting to show " + layerName);
-    
     if(featureOverlays[layerName]["mainMap"].getSource().getFeatures().length == 0)
     {
-         console.info("Layer " + layerName + " is new, need to get data ");
-         getResults[layerName]();
+         //console.info("Layer " + layerName + " is new, need to get data ");
+         getResults[layerName](getCurrentSeason());
     }
     for(var mapName in maps)
     {
@@ -202,8 +285,9 @@ function showLayer(layerName)
 }
 
 /**
- * 
- * @returns {String}
+ * Which layer is currently visible? I can tell you, using my immense powers
+ * of deduction!
+ * @returns {String} The name of the currently visible layer
  */
 var getVisibleLayerName = function()
 {
@@ -220,7 +304,10 @@ var getVisibleLayerName = function()
  * Contains layer specific methods for fetching and displaying the data
  */
 var getResults = {
-    "WHS" : function(){
+    /**
+     * TODO: This is old school
+     */
+    "WHS" : function(season){
         ajax(hostName + "/rest/forecastresults/-1000", function(e){
         //ajax("http://vipslogic-local.no/rest/forecastresults/-1000", function(e){
             var results = JSON.parse(e.target.responseText);
@@ -235,25 +322,22 @@ var getResults = {
             }
             // This is here to fix an apparent bug in having Vector tiles
             // within the CSS grid system
+            // THE FIRST LAYER THAT IS BEING SHOWN ON THE MAP MUST DO THIS. 
+            // AT LEAST ONCE.
             window.dispatchEvent(new Event('resize'));
         });
     },
     "observedDisease": function() { console.info("NOT IMPLEMENTED");},
     "yieldLoss": function() { console.info("NOT IMPLEMENTED");},
-    "rainyDays": function() { 
+    "rainyDays": function(season) { 
         // Retrieve the forecast ids first
-        ajax(hostName + "/rest/forecastconfigurations/model/RAINYDAYSM/2019", function(e){
-            //console.info(e);
+        ajax(hostName + "/rest/forecastconfigurations/model/RAINYDAYSM/" + season, function(e){
             forecastsForSeason = JSON.parse(e.target.responseText);
-            
-            // For each forecast config, get the results for a given period (GS32-GS71 or user selected)
-            // and aggregate
+            // For each forecast config, get the results for a given season 
+            // and aggregate for a given period (GS32-GS71 or user selected)
             for(var i in forecastsForSeason)
             {
-                // This is a closure. It has access to the parent function's variables. 
-                // This is how we keep state in this chain of Ajax calls
-                var getForecastResults = function(e){
-                    
+                ajax(hostName + "/rest/forecastresults/" + forecastsForSeason[i].forecastConfigurationId + "/" + season + "-01-01/" + season + "-12-31", function(e){
                     var forecastResults = JSON.parse(e.target.responseText);
                     //console.info(forecastResults);
                     var currentForecastId = forecastResults[0].forecastConfigurationId;
@@ -266,10 +350,13 @@ var getResults = {
                             break;
                         }
                     }
-                    
-                    var getForecastGSTimeLimit = function(e){
+                    ajax(hostName + "/rest/gs/date/32,71/25/?season=" + season + "&location=" + currentForecast.locationPointOfInterestId.longitude + "," + currentForecast.locationPointOfInterestId.latitude, function(e){
+                        // This callback interprets the data returned from 
+                        // the VIPSLogic GrowthStageService
+                        // It then forwards results and GS dates to displayResults
                         var GSResults = JSON.parse(e.target.responseText);
                         var GS32Date, GS71Date;
+                        
                         for(var i in GSResults)
                         {
                             if(GSResults[i]["32"] != null)
@@ -281,14 +368,18 @@ var getResults = {
                                 GS71Date = moment(GSResults[i]["71"]);
                             }
                         }
+                        
+                        var startDate = document.getElementById("startDate").value != "" ? moment(document.getElementById("startDate").value) : GS32Date;
+                        var endDate = document.getElementById("endDate").value != "" ? moment(document.getElementById("endDate").value) : GS71Date;
+                        //console.info("startDate=" + startDate.format("YYYY-MM-DD"));
                         // We now have everything we need to calculate the sum
                         var rainyDaysSum = 0;
                         for(var i in forecastResults)
                         {
                             var validTimeStart = moment(forecastResults[i].validTimeStart);
                             if(
-                                    validTimeStart.isSameOrAfter(GS32Date) 
-                                    && validTimeStart.isSameOrBefore(GS71Date) 
+                                    validTimeStart.isSameOrAfter(startDate) 
+                                    && validTimeStart.isSameOrBefore(endDate) 
                                     && forecastResults[i].allValues["RAINYDAYSM.RAINY_DAY"] == "true"
                             )
                             {
@@ -296,21 +387,19 @@ var getResults = {
                             }
                         }
                         // We have the sum and location, let's display it on the map!
+                        // THIS IS THE LAST STEP IN THIS CHAIN!
                         displayResults["rainyDays"](rainyDaysSum, GS32Date, GS71Date, currentForecast);
-                    }
-                    ajax(hostName + "/rest/gs/date/32,71/25/?location=" + currentForecast.locationPointOfInterestId.longitude + "," + currentForecast.locationPointOfInterestId.latitude, getForecastGSTimeLimit);
-                };
-                ajax(hostName + "/rest/forecastresults/" + forecastsForSeason[i].forecastConfigurationId + "/2019-05-01/2019-09-13", getForecastResults);
+                    });
+                });
             }
         });
     },
     "HM": function() { console.info("NOT IMPLEMENTED");},
 };
 
-// "observedDisease","yieldLoss","rainyDays","WHS","HM"
-
 var featureZIndex = 10;
-
+// This is used by OpenLayers to define the features' looks
+// The different models have varying thresholds, colors etc
 var getFeatureStyle = {
     "WHS": function(feature)
     {
@@ -339,7 +428,6 @@ var getFeatureStyle = {
     },
     "rainyDays": function(feature)
     {
-        //console.info(feature);
         var rainyDays = parseInt(feature.get("rainyDays"));
         var color = rainyDays < 4 ? "green" : "red";
         
@@ -362,14 +450,17 @@ var getFeatureStyle = {
     }
 };
 
+/**
+ * 
+ * Creating OpenLayers features from the model results from VIPSLogiv and 
+ * displaying them on the map
+ */
 var displayResults = {
     "WHS" : function(results,mapName, date){
         var features = [];
         //var momentDate = moment(date);
         for(var i in results)
         {
-            //console.info(moment(results[i].validTimeStart).format() + "==" + date.format());
-
             if(moment(results[i].validTimeStart).isSame(date)){
                 //console.info(results[i].validGeometry.coordinates);
                 var feature = new ol.Feature({
@@ -380,8 +471,6 @@ var displayResults = {
                 features.push(feature);
             }
         }
-        //var featureSource = new ol.source.Vector({features:features});
-        //console.info(features);
         featureOverlays["WHS"][mapName].getSource().clear();
         featureOverlays["WHS"][mapName].getSource().addFeatures(features);
         
@@ -405,6 +494,13 @@ var displayResults = {
     }
 };
 
+/**
+ * 
+ * @param {type} url
+ * @param {type} callback
+ * @returns {undefined}General XMLHttpRequest utility function. To avoid using JQuery or similarly
+ * bloated framework
+ */
 var ajax = function(url, callback)
 {
     var xhr = new XMLHttpRequest();
@@ -413,6 +509,9 @@ var ajax = function(url, callback)
     xhr.send();
 };
 
+/**
+ * Simple popup with detailed description of each model
+ */
 var showModelInfo = {
     "WHS": function()
     {
@@ -440,6 +539,10 @@ var hideModelInfo = function()
     document.getElementById('popup').style.display="none";
 }
 
+/**
+ * TODO: this is using Europe/Oslo as default. What about Finland/the Baltics?
+ * @returns {getTodayAtMidnight.today}
+ */
 function getTodayAtMidnight()
 {
     var timeZone="Europe/Oslo";
@@ -475,7 +578,7 @@ var getLayerLegend = {
 // All the stuff below is for dynamically loading all JavaScript Libs that are
 // needed to run the application
 
-// After the client document has finished loading, we download OpenLayers and subsequently
+// After the client document has finished loading, we download OpenLayers, MomentJS and subsequently
 // initialize the map.
 document.addEventListener("DOMContentLoaded", function() {
   // Some introspection here