From 9424acdcfc99a67d2318fae8d690a25fd789c88c Mon Sep 17 00:00:00 2001
From: Tor-Einar Skog <tor-einar.skog@nibio.no>
Date: Mon, 18 Dec 2017 15:35:44 +0100
Subject: [PATCH] First non-failing version of the MetnoThreddsDataParser

---
 nbactions.xml                                 |   4 -
 pom.xml                                       |  29 +-
 .../logic/service/WeatherProxyService.java    |   5 +-
 .../metnothredds/MetNoThreddsDataParser.java  | 121 ++++++-
 .../TooMuchDataToAskForException.java         |  40 +++
 .../images/station_icon_status_winter.png     | Bin 0 -> 1228 bytes
 .../images/station_icon_status_xmas.png       | Bin 0 -> 540 bytes
 .../vips/logic/test/mock/DataMatrix.java      |  35 ++
 .../nibio/vips/logic/test/mock/MockModel.java | 226 +++++++++++++
 .../MetNoThreddsDataParserTest.java           | 310 +++++++++++++++++-
 10 files changed, 738 insertions(+), 32 deletions(-)
 create mode 100644 src/main/java/no/nibio/vips/util/weather/metnothredds/TooMuchDataToAskForException.java
 create mode 100644 src/main/webapp/public/images/station_icon_status_winter.png
 create mode 100644 src/main/webapp/public/images/station_icon_status_xmas.png
 create mode 100644 src/test/java/no/nibio/vips/logic/test/mock/DataMatrix.java
 create mode 100644 src/test/java/no/nibio/vips/logic/test/mock/MockModel.java

diff --git a/nbactions.xml b/nbactions.xml
index b86e6dae..8b8525cb 100755
--- a/nbactions.xml
+++ b/nbactions.xml
@@ -46,10 +46,6 @@
                 <no.nibio.vips.logic.weather.FIELDCLIMATE_API_CLIENT_SECRET>aa8f4b62b72986bac7c84be78836c2c6</no.nibio.vips.logic.weather.FIELDCLIMATE_API_CLIENT_SECRET>
                 <no.nibio.vips.logic.weather.METNOTHREDDS_TMP_FILE_PATH>/home/treinar/prosjekter/vips/projects/2017_SpotIT/Task 3.2/</no.nibio.vips.logic.weather.METNOTHREDDS_TMP_FILE_PATH>
                 
-                
-                
-                
-                
             </properties>
         </action>
     </actions>
diff --git a/pom.xml b/pom.xml
index 117d5281..4cfa5b0b 100755
--- a/pom.xml
+++ b/pom.xml
@@ -19,6 +19,11 @@
         <id>jitpack.io</id>
         <url>https://jitpack.io</url>
     </repository>
+    <repository>
+    <id>bedatadriven</id>
+    <name>bedatadriven public repo</name>
+    <url>https://nexus.bedatadriven.com/content/groups/public/</url>
+  </repository>
     <repository>
         <id>unidata-releases</id>
         <name>Unidata Releases</name>
@@ -225,7 +230,21 @@
 	<version>9.4-1211</version>
         <scope>provided</scope>
 </dependency-->
-
+<dependency>
+    <groupId>org.openjdk.jol</groupId>
+    <artifactId>jol-core</artifactId>
+    <version>0.9</version>
+</dependency>
+<dependency>
+            <groupId>org.geotools</groupId>
+            <artifactId>gt-api</artifactId>
+            <version>17.2</version>
+        </dependency>
+        <dependency>
+        <groupId>org.geotools</groupId>
+        <artifactId>gt-epsg-hsql</artifactId>
+        <version>17.2</version>
+    </dependency>
   </dependencies>
 
   <build>
@@ -304,6 +323,14 @@
           </webResources>
         </configuration>
       </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <version>2.10</version>
+        <configuration>
+            <argLine>-Xmx6048m</argLine>
+        </configuration>
+    </plugin>
     </plugins>
   </build>
   
diff --git a/src/main/java/no/nibio/vips/logic/service/WeatherProxyService.java b/src/main/java/no/nibio/vips/logic/service/WeatherProxyService.java
index 1728e80f..792acb49 100755
--- a/src/main/java/no/nibio/vips/logic/service/WeatherProxyService.java
+++ b/src/main/java/no/nibio/vips/logic/service/WeatherProxyService.java
@@ -52,6 +52,7 @@ import no.nibio.vips.util.weather.USPestDataParser;
 import no.nibio.vips.util.weather.YrWeatherForecastProvider;
 import no.nibio.vips.util.weather.dnmipointweb.DMIPointWebDataParser;
 import no.nibio.vips.util.weather.metnothredds.MetNoThreddsDataParser;
+import no.nibio.vips.util.weather.metnothredds.TooMuchDataToAskForException;
 import org.jboss.resteasy.annotations.GZIP;
 
 /**
@@ -383,9 +384,9 @@ public class WeatherProxyService {
             List<PointWeatherObservationList> retVal = dp.getGridData(gf.toGeometry(envelope), 10.0, startDate, endDate, Arrays.asList(elementMeasurementTypes));
             return Response.ok().entity(retVal).build();
         }
-        catch(ParseException pe)
+        catch(ParseException | TooMuchDataToAskForException e)
         {
-            return Response.status(Response.Status.BAD_REQUEST).entity(pe.getMessage()).build();
+            return Response.status(Response.Status.BAD_REQUEST).entity(e.getMessage()).build();
         }
     }
 }
diff --git a/src/main/java/no/nibio/vips/util/weather/metnothredds/MetNoThreddsDataParser.java b/src/main/java/no/nibio/vips/util/weather/metnothredds/MetNoThreddsDataParser.java
index 723d84d5..5688971d 100644
--- a/src/main/java/no/nibio/vips/util/weather/metnothredds/MetNoThreddsDataParser.java
+++ b/src/main/java/no/nibio/vips/util/weather/metnothredds/MetNoThreddsDataParser.java
@@ -41,9 +41,15 @@ import java.util.Optional;
 import java.util.Properties;
 import java.util.Set;
 import java.util.stream.Collectors;
+import javax.measure.Measure;
+import javax.measure.quantity.Area;
+import javax.measure.unit.SI;
+import javax.measure.unit.Unit;
 import no.nibio.vips.entity.PointWeatherObservationList;
 import no.nibio.vips.entity.WeatherObservation;
+import no.nibio.vips.gis.GISUtil;
 import no.nibio.vips.gis.SimpleWGS84Coordinate;
+import no.nibio.vips.util.SystemUtil;
 import no.nibio.vips.util.WeatherElements;
 import no.nibio.vips.util.WeatherUtil;
 import org.apache.commons.io.FileUtils;
@@ -73,6 +79,9 @@ public class MetNoThreddsDataParser {
     private final SimpleDateFormat pathFormat;
     private final SimpleDateFormat fileTimeStampFormat;
     
+    // Approximate number of memory bytes per observation
+    // See the test class for more info
+    private final int AVERAGE_OBS_OBJ_SIZE = 40; 
     
     private final WeatherUtil weatherUtil;
     
@@ -93,8 +102,69 @@ public class MetNoThreddsDataParser {
         this.fileTimeStampFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
     }
     
-    public List<PointWeatherObservationList> getGridData(Geometry geometry, Double resolution, Date dateFrom, Date dateTo, List<String> VIPSWeatherParameters)
+    /**
+     * If you want a lot of data, split it into smaller, manageable parts
+     * @param geometry
+     * @param resolution
+     * @return 
+     */
+    public Geometry[] getManageableGeometries(Geometry geometry, Double resolution, Date dateFrom, Date dateTo, List<String> VIPSWeatherParameters)
+    {
+        Long presumableFreeMemory = SystemUtil.getPresumableFreeMemory();
+        Integer divideBy = 1;
+        while(this.getMemoryNeededForRequest(this.getGeometryFraction(geometry, divideBy, 0, 0), resolution, dateFrom, dateTo, VIPSWeatherParameters) > presumableFreeMemory * 0.3)
+        {
+            divideBy++;
+        }
+        
+        Geometry[] retVal = new Geometry[divideBy * divideBy];
+        for(int x=0;x<divideBy;x++)
+        {
+            for(int y=0;y<divideBy;y++)
+            {
+                retVal[x*divideBy + y] = this.getGeometryFraction(geometry, divideBy, x,y);
+            }
+        }
+        //System.out.println("divideBy=" + divideBy);
+        return retVal;
+    }
+    
+    public Geometry getGeometryFraction(Geometry geometry, Integer divideBy, Integer x, Integer y)
+    {
+        Polygon p = (Polygon) geometry.getEnvelope();
+        Double lonLength = (this.getEast(p) - this.getWest(p)) / divideBy;
+        Double latLength = (this.getNorth(p) - this.getSouth(p)) / divideBy;
+        
+        //Double newNorth = ((this.getNorth(p) - this.getSouth(p))/divideBy) + this.getSouth(p);
+        //Double newEast = (this.getEast(p) - this.getWest(p)/divideBy) + this.getWest(p);
+        return new GISUtil().createRectangleFromBounds(
+                this.getSouth(p) + (latLength * (y +1)), 
+                this.getSouth(p) + (latLength * y), 
+                this.getWest(p) + (lonLength * (x +1)), 
+                this.getWest(p) + (lonLength * x)
+        );
+    }
+    
+    /**
+     * 
+     * @param geometry
+     * @param resolution
+     * @param dateFrom
+     * @param dateTo
+     * @param VIPSWeatherParameters
+     * @return
+     * @throws TooMuchDataToAskForException 
+     */
+    public List<PointWeatherObservationList> getGridData(Geometry geometry, Double resolution, Date dateFrom, Date dateTo, List<String> VIPSWeatherParameters) throws TooMuchDataToAskForException
     {
+        Long memoryNeededForRequest = this.getMemoryNeededForRequest(geometry, resolution, dateFrom, dateTo, VIPSWeatherParameters);
+        Long presumableFreeMemory = SystemUtil.getPresumableFreeMemory();
+        //System.out.println("memoryNeeded=" + memoryNeededForRequest + ", free=" + presumableFreeMemory);
+        if(memoryNeededForRequest > presumableFreeMemory * 0.6)
+        {
+            throw new TooMuchDataToAskForException("The request is estimated to require " + memoryNeededForRequest + " bytes of memory, this is too much. Please ask for less!");
+        }
+        
         Set<String> metParamNames = VIPSWeatherParameters.stream().map(
                 param -> paramInfo.getProperty(param)
         ).collect(Collectors.toSet());
@@ -104,8 +174,8 @@ public class MetNoThreddsDataParser {
         Collections.sort(archiveDates);
         //new GeometryFactory().
         Polygon gridBounds =  (Polygon) geometry.getEnvelope();
-        System.out.println("geometryType=" + gridBounds.getGeometryType());
-        System.out.println("gridBounds=" + gridBounds);
+        //System.out.println("geometryType=" + gridBounds.getGeometryType());
+        //System.out.println("gridBounds=" + gridBounds);
          
         Map<Coordinate, Map<Long, WeatherObservation>> tempRetVal = new HashMap<>();
         archiveDates.stream().forEach(archiveDate -> {
@@ -121,7 +191,7 @@ public class MetNoThreddsDataParser {
                         "&north=" + this.getNorth(gridBounds) + "&west=" + this.getWest(gridBounds) + "&east=" + this.getEast(gridBounds) + "&south=" + this.getSouth(gridBounds) +
                         "&var=" + String.join(",", metParamNames) +
                         "&addLatLon=true");
-                System.out.println("URL: " + threddsURL.toString());
+                //System.out.println("URL: " + threddsURL.toString());
                 Long timestamp = new Date().getTime();
                 tmp = new File(this.TMP_FILE_PATH + timestamp + ".nc");
                 FileUtils.copyURLToFile(threddsURL, tmp);
@@ -156,14 +226,14 @@ public class MetNoThreddsDataParser {
                 for(String metParamName:metParamNames)
                 {
                     String VIPSParamName = this.findVIPSParamNameFromMetParamName(metParamName);
-
+                    //System.out.println("metParamName=" + metParamName);
                     Variable var = ncFile.findVariable(metParamName);
                     // All variables have the dims [time, height, y, x]. All 
                     // variables represent only one height, and can therefore be reduced 
                     // from 4 to three dimensions
                     // Assuming all weather parameters are of type "float"
 
-                    ArrayFloat.D3 varArray = (ArrayFloat.D3) var.read().reduce();
+                    ArrayFloat.D3 varArray = (ArrayFloat.D3) var.read().reduce(1); // 1 is the height parameter
                     // We always skip the two first timestamps, since the model needs 
                     // a couple of hours to spin up
                     for(int i=2;i<timeMeasured.length;i++)
@@ -203,9 +273,9 @@ public class MetNoThreddsDataParser {
                 }
                 
             }
-            catch(IOException ioe)
+            catch(IOException | ClassCastException ex)
             {
-                
+                ex.printStackTrace();
             }
             finally {
                 if(ncFile != null)
@@ -254,7 +324,7 @@ public class MetNoThreddsDataParser {
                         this.fileTimeStampFormat.format(archiveDate) +
                         NETCDF_EXT + STANDARD_PARAMS + "&longitude=" + longitude + "&latitude=" + latitude
                         + "&var=" + String.join(",", metParamNames));
-                System.out.println("URL: " + threddsURL.toString());
+                //System.out.println("URL: " + threddsURL.toString());
                 Long timestamp = new Date().getTime();
                 tmp = new File(this.TMP_FILE_PATH + timestamp + ".nc");
                 FileUtils.copyURLToFile(threddsURL, tmp);
@@ -455,4 +525,37 @@ public class MetNoThreddsDataParser {
     {
         return envelope.getCoordinates()[2].x;
     }
+    
+    /**
+     * Calculating roughly the number of grid points within an envelope
+     * @param envelope
+     * @param resolution km-resolution of the grid points
+     * @return 
+     */
+    public Long getNumberOfGridPointsInEnvelope(Polygon envelope, Double resolution)
+    {
+        GISUtil gisUtil = new GISUtil();
+        
+        Measure<Double,Area> m = gisUtil.calcArea(envelope);
+        //System.out.println("measure m = " + m);
+        Unit<Area> sq_km = (Unit<Area>) SI.KILOMETER.pow(2);
+        //System.out.println("Area=" + m.to(sq_km).getValue());
+        Double side = Math.sqrt(m.to(sq_km).getValue());
+        
+        
+        return Math.round((1 + side / resolution) * (1 + side / resolution));
+    }
+    
+    public Long getNumberOfWeatherObservationsRequested(Geometry geometry, Double resolution, Date dateFrom, Date dateTo, List<String> VIPSWeatherParameters)
+    {
+        Polygon gridBounds =  (Polygon) geometry.getEnvelope();
+        Long gridPoints = this.getNumberOfGridPointsInEnvelope(gridBounds, resolution);
+        Long hoursInPeriod = Math.round((dateTo.getTime() - dateFrom.getTime()) / (3600.0 * 1000));        
+        return gridPoints * hoursInPeriod * VIPSWeatherParameters.size();
+    }
+    
+    public Long getMemoryNeededForRequest(Geometry geometry, Double resolution, Date dateFrom, Date dateTo, List<String> VIPSWeatherParameters)
+    {
+        return this.getNumberOfWeatherObservationsRequested(geometry, resolution, dateFrom, dateTo, VIPSWeatherParameters) * this.AVERAGE_OBS_OBJ_SIZE;
+    }
 }
diff --git a/src/main/java/no/nibio/vips/util/weather/metnothredds/TooMuchDataToAskForException.java b/src/main/java/no/nibio/vips/util/weather/metnothredds/TooMuchDataToAskForException.java
new file mode 100644
index 00000000..3031f9f7
--- /dev/null
+++ b/src/main/java/no/nibio/vips/util/weather/metnothredds/TooMuchDataToAskForException.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2017 NIBIO <http://www.nibio.no/>. 
+ * 
+ * This file is part of VIPSLogic.
+ * VIPSLogic is free software: you can redistribute it and/or modify
+ * it under the terms of the NIBIO Open Source License as published by 
+ * NIBIO, either version 1 of the License, or (at your option) any
+ * later version.
+ * 
+ * VIPSLogic is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * NIBIO Open Source License for more details.
+ * 
+ * You should have received a copy of the NIBIO Open Source License
+ * along with VIPSLogic.  If not, see <http://www.nibio.no/licenses/>.
+ * 
+ */
+
+package no.nibio.vips.util.weather.metnothredds;
+
+/**
+ * This is simply a message telling that you're asking for too much data,
+ * putting the server at risk of running out of resources, most notably memory.
+ * @copyright 2017 <a href="http://www.nibio.no/">NIBIO</a>
+ * @author Tor-Einar Skog <tor-einar.skog@nibio.no>
+ */
+public class TooMuchDataToAskForException extends Exception {
+    
+    public TooMuchDataToAskForException()
+    {
+        super();
+    }
+    
+    public TooMuchDataToAskForException(String msg)
+    {
+        super(msg);
+    }
+
+}
diff --git a/src/main/webapp/public/images/station_icon_status_winter.png b/src/main/webapp/public/images/station_icon_status_winter.png
new file mode 100644
index 0000000000000000000000000000000000000000..f2de71d2b1f989688c6f3865dcaba5e8eaedaab2
GIT binary patch
literal 1228
zcmeAS@N?(olHy`uVBq!ia0y~yU=U(pU=ZP8V_;xtn4as!z`($k<n8Xl@E-&h>|H*Y
zfq{Xuz$3Dlfq`2Xgc%uT&5>YWU|=ut^mS!_$j!xP#`ssoUYdb{CDPNyF~s6@Ye=p~
zbh?P0`N_RIubIqBS)C`<9(_`wODVYJq)V$(N7;|VN#SM!I*-(UG+McAd33{dQPP#h
zAI@x6P4^93x}yKg2sZrZaV4{;cc+o^B11K;Sx0N`l-oyjUn;Bpx3~Sd&7A4Q^VpmD
z9oB{|c2FqTw`I$h!s_bX?`v(&?d}qB?G)iU>desa;zfpshlfIqo&5go+rK9U2T%S{
z->T776)e(y^w+6VUfn?}|I9x9l<D-Q9X@K-I+~grj~qMJw{JE3?zOx2?5WAy7$Mfz
z-(TO?-5q`I%o&!J3))I;jbW=>dwYAAOqd`L>f+*Z<Io|eko5HB+zbq_U%m{y@wP17
z+xxV?*=*j}m>2^?L&Nxx@bLEI$By+qefF&G?9-yel$4aT($cS)@$vU>Ub+;dU}Pku
zrKKgOqONYQ?)gdBK!WGQ`|pc4Zrpfy@#4h{hYgl*KYs1?*N#L9bA|$quBN6Yrp}u=
zZajw<T)cR(c>VQm9Wm~vwzjsMUbo_7n{=Ew7XJNPyKT)HpOVjaw{A7vvVHq=J$-$C
zCWaMj*Zw{Aut1?jASqHTb8A$1aq(wiInPPfJcoDG+QoA?3MfqV+W70&ui%v-zjoiv
zt9P8F@}a_Jjsl0GYv8LtfBv`&b+Rxp#OR67IGbkeD6n~k-}2&=loSs!aq-4wFYTtK
zzF)d*S)KmmlMmj!;W_ds{C1huTm`Z2g9!!=aqIuL9)Ik5?D%neKOdi#lP5jxuid@-
zmqk(K?wva_FW$c|XJWW=^{VRgCr|!NoHR*k$KI<iUuJF&Tm5(2^5yC53=6hwG4axy
zZeMIQ_x}fuCV@{s|6I6mAwZD9f{~#>M%_Z@T=el%k3Tm3tg$Qne5~-#yZ{ZJj}<nK
zH}2fI6Brd0HREJT)6btjD|rro*t2Jk$ie`Q>({U66ciY+GaPvQaf6CyQI46kkB`rv
z&c_ugWo6%<2L=W%2wQ!3hSS1|hQ~h=0s}89Hz_b2Hn{MzB*8>#{$-Q3t5>c}WH?}L
zVj`j_z%eB<GSdHPk!6Ae8#_C@fr*L9tM9+-8?IfuCjR>M>+~0<-@bjj)7sklJ>LJk
z=}ey!DMqoP;^Lnf8CI=Y6{N4PFX+U<%EY*3)25<ZH*VZuxPANfiL}j%Q@y@^`tU*E
zqDgJRYlV|<-{x-Fy7lS4m7=1et&wZPQjBI^`0>Nyana5T*RF{@;^pW6|Ke*^`4q3E
z$FjCotzWn993z7a-|^{MQ(x)N^l7t^^S^N5z=2H%6Dn*hEMDknXf$ltV33iW-EA(_
zd)|QO_m{p*Q>n98-@RLR>-~5C^wiY9TGLOzpHY^U#&+%6wS9sdEc-oGCV4a;EZDzz
z@#9y%J$HU&m6e4>L`MFc>!I?Bho7Il)#>A<tgTGGm+!4xx9;(mFC_w<F23sO>hmv}
z_}=N*BdTnkk(v4P#M?4&%`T%^XVb(O?mT_^RMFGZ^P`HV5sTxFSFc~c|B@izb!+9y
rmG$e_ub1ua?!M@?^wNL3d-b<wt4nYFcuJ3ffq}u()z4*}Q$iB}A$v%Z

literal 0
HcmV?d00001

diff --git a/src/main/webapp/public/images/station_icon_status_xmas.png b/src/main/webapp/public/images/station_icon_status_xmas.png
new file mode 100644
index 0000000000000000000000000000000000000000..059c00b378efaca99c5d723f541319c6b1cfc06c
GIT binary patch
literal 540
zcmeAS@N?(olHy`uVBq!ia0y~yVBlw9U=ZP8V_;wiI-V}Xz`($k<n8Xl@E-&h>|H*Y
zfq{Xuz$3Dlfq`2Xgc%uT&5>YWU|=ut^mS!_$j!yaW;HqU!5Iby#+9Bfjv*GOdoSB&
zh&qa}KFAkV{?RSL{gqW$L@{CoV|Y%BdXrKF$3YP(>9?+LHuvR9Il3r?iA8DL;E0Oo
zn5xq>*Xf9xSyQk5k3Zr`8$TD^c;l?Ev%CEHoSDVv@6670JMlaIbpdy9*Qd?XPVjV1
z2!HWF-E2$lgsl^<JrG@&tg82bd7H+mI4;>JrJZ&Ub=3?-!{=$AiVF&AjuGbk8Tsa5
zMM#+pduxr_I{pc=pCntJocDg?-g<S%8jbVYPp`XMJO9b!9}=CS&!(OFr=4nk&D^Y}
zNAB1L)1&;?Pjk1_NNxWpEi7@=FQ!*+%~8+0=Ym~-TG>{z=N+3Ywo!I<lAZMX-S00t
zE82%l&Yu3fP?O26K-@h#DqT=q^y%VRF}LR@#xwuOc5u&oef@6vZ$>`zhTR`jPl`XP
zjymyP!^_#JPF3+<hg<F8?j`TCyNYyfW(1vh&-=PeYtFGxEHz6mI#ichFQ1hCr%O2U
zN_;tg`XB3O1%845GPXTWlR4a2VY|3+k(SNDTGz<0&lRK%PRrUHwlPawWZAbNr(ug=
rPEY@f_W!E&hosjWu&&8x{>ykkH2-|K<GSAr3=9mOu6{1-oD!M<K3?J>

literal 0
HcmV?d00001

diff --git a/src/test/java/no/nibio/vips/logic/test/mock/DataMatrix.java b/src/test/java/no/nibio/vips/logic/test/mock/DataMatrix.java
new file mode 100644
index 00000000..ec6bd70a
--- /dev/null
+++ b/src/test/java/no/nibio/vips/logic/test/mock/DataMatrix.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2017 NIBIO <http://www.nibio.no/>. 
+ * 
+ * This file is part of ZymoseptoriaSimpleRiskGridModel.
+ * ZymoseptoriaSimpleRiskGridModel is free software: you can redistribute it and/or modify
+ * it under the terms of the NIBIO Open Source License as published by 
+ * NIBIO, either version 1 of the License, or (at your option) any
+ * later version.
+ * 
+ * ZymoseptoriaSimpleRiskGridModel is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * NIBIO Open Source License for more details.
+ * 
+ * You should have received a copy of the NIBIO Open Source License
+ * along with ZymoseptoriaSimpleRiskGridModel.  If not, see <http://www.nibio.no/licenses/>.
+ * 
+ */
+
+package no.nibio.vips.logic.test.mock;
+
+import no.nibio.vips.util.DateMap;
+
+/**
+ * @copyright 2017 <a href="http://www.nibio.no/">NIBIO</a>
+ * @author Tor-Einar Skog <tor-einar.skog@nibio.no>
+ */
+public class DataMatrix extends DateMap {
+    public final static String UM = "UM";
+    public final static String BT = "BT";
+    public final static String RR = "RR";
+    public final static String TM = "TM";
+    public final static String WET_HOUR_SUM = "WHS";
+
+}
diff --git a/src/test/java/no/nibio/vips/logic/test/mock/MockModel.java b/src/test/java/no/nibio/vips/logic/test/mock/MockModel.java
new file mode 100644
index 00000000..140a7d04
--- /dev/null
+++ b/src/test/java/no/nibio/vips/logic/test/mock/MockModel.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright (c) 2017 NIBIO <http://www.nibio.no/>. 
+ * 
+ * This file is part of VIPSLogic.
+ * VIPSLogic is free software: you can redistribute it and/or modify
+ * it under the terms of the NIBIO Open Source License as published by 
+ * NIBIO, either version 1 of the License, or (at your option) any
+ * later version.
+ * 
+ * VIPSLogic is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * NIBIO Open Source License for more details.
+ * 
+ * You should have received a copy of the NIBIO Open Source License
+ * along with VIPSLogic.  If not, see <http://www.nibio.no/licenses/>.
+ * 
+ */
+
+package no.nibio.vips.logic.test.mock;
+
+import com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.geom.GeometryFactory;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.TimeZone;
+import no.nibio.vips.entity.ModelConfiguration;
+import no.nibio.vips.entity.PointWeatherObservationList;
+import no.nibio.vips.entity.Result;
+import no.nibio.vips.entity.ResultImpl;
+import no.nibio.vips.model.ConfigValidationException;
+import no.nibio.vips.model.Model;
+import no.nibio.vips.model.ModelExcecutionException;
+import no.nibio.vips.model.ModelId;
+import no.nibio.vips.util.WeatherElements;
+import no.nibio.vips.util.WeatherUtil;
+
+/**
+ * 
+ * This is made for testing purposes!
+ * @copyright 2017 <a href="http://www.nibio.no/">NIBIO</a>
+ * @author Tor-Einar Skog <tor-einar.skog@nibio.no>
+ */
+public class MockModel implements Model{
+    
+    // The minimum number of hours accepted for a daily aggregate
+    private final Integer MINIMUM_HOURLY_VALUES = 16;
+    
+    private List<PointWeatherObservationList> pointWeatherObservationList;
+    private TimeZone timeZone;
+    
+    public final static ModelId MODEL_ID = new ModelId("MOCKMODEL");
+
+    @Override
+    public List<Result> getResult() throws ModelExcecutionException {
+        if(this.pointWeatherObservationList != null)
+        {
+            return this.getGridResult();
+        }
+        
+        throw new UnsupportedOperationException("For now, only grid data are supported");
+    }
+    
+    /**
+     * A simple wet hour sum calculation for the grid data
+     * @return
+     * @throws ModelExcecutionException 
+     */
+    public List<Result> getGridResult() throws ModelExcecutionException{
+        Map<Date, Map<Coordinate, Integer>> resultMap = new HashMap<>();
+        
+        // Calculate the model for each point
+        WeatherUtil wUtil = new WeatherUtil();
+        GeometryFactory geometryFactory = new GeometryFactory();
+        this.pointWeatherObservationList.stream().forEach(oList->{
+            //System.out.println("oList coords=" + oList.getCoordinate());
+            // We are going to calculate number of wet (risky) hours for a day 
+            // including yesterday and tomorrow (72 hours in total) 
+            // Integer[0] = number of input data observation hours (0->24), Integer[1] number of wet hours
+            Map<Date, Integer[]> wetHours = new HashMap<>();
+            DataMatrix pointMatrix = new DataMatrix();
+            oList.getObservations().stream().forEach(obs -> {
+                    pointMatrix.setParamDoubleValueForDate(obs.getTimeMeasured(), obs.getElementMeasurementTypeId(), obs.getValue());
+                }
+            );
+            pointMatrix.getSortedDateKeys().stream().forEach(timestamp -> {
+                Date dayStamp = wUtil.normalizeToExactDate(timestamp, timeZone);
+                Integer[] wetHoursForDay = wetHours.get(dayStamp);
+                if(wetHoursForDay == null)
+                {
+                    wetHoursForDay = new Integer[2];
+                    wetHoursForDay[0] = 0;
+                    wetHoursForDay[1] = 0;
+                }
+                Double UM = pointMatrix.getParamDoubleValueForDate(timestamp, WeatherElements.RELATIVE_HUMIDITY_MEAN);
+                Double TM = pointMatrix.getParamDoubleValueForDate(timestamp, WeatherElements.TEMPERATURE_MEAN);
+                if(UM != null && TM != null)
+                {
+                    wetHoursForDay[0]++; // We add up the number of hours we actually have weather data for
+                }
+                Double BT = pointMatrix.getParamDoubleValueForDate(timestamp, WeatherElements.LEAF_WETNESS);
+                Double RR = pointMatrix.getParamDoubleValueForDate(timestamp, WeatherElements.PRECIPITATION);
+                if(TM >= 10.0)
+                {
+                    wetHoursForDay[1] += (BT != null && RR != null) ? 
+                                                (UM >= 88.0 && BT >= 30.0 && RR > 0.2) ? 1 : 0
+                                            : UM >= 88.0 ? 1 : 0;
+                }
+                wetHours.put(dayStamp, wetHoursForDay);
+            });
+            
+            //wetHours.keySet().stream().sorted().forEach(dayStamp -> System.out.println(dayStamp + ": " + wetHours.get(dayStamp)[0] + "/" + wetHours.get(dayStamp)[1]));
+            List<Date> dayStamps = new ArrayList(wetHours.keySet());
+            Collections.sort(dayStamps);
+            for(int i=1;i<dayStamps.size()-1; i++)
+            {
+                // TODO: Check that each day has enough hourly values as aggregate base
+                if(
+                    wetHours.get(dayStamps.get(i-1))[0] >= this.MINIMUM_HOURLY_VALUES
+                    && wetHours.get(dayStamps.get(i))[0] >= this.MINIMUM_HOURLY_VALUES
+                    && wetHours.get(dayStamps.get(i+1))[0] >= this.MINIMUM_HOURLY_VALUES
+                )
+                {
+                    // Wohoo, we have a result!
+                    Integer wetHourSumForDayAndPoint = 
+                            wetHours.get(dayStamps.get(i-1))[1]
+                            + wetHours.get(dayStamps.get(i))[1]
+                            + wetHours.get(dayStamps.get(i+1))[1];
+                    Map<Coordinate, Integer> pointAndDateResult = resultMap.get(dayStamps.get(i));
+                    if(pointAndDateResult == null)
+                    {
+                        pointAndDateResult = new HashMap<>();
+                    }
+                    pointAndDateResult.put(oList.getCoordinate(), wetHourSumForDayAndPoint);
+                    resultMap.put(dayStamps.get(i), pointAndDateResult);
+                }
+            }
+        });
+        
+        List<Result> retVal = new ArrayList<>();
+        resultMap.entrySet().stream().forEach(timeEntry -> {
+            timeEntry.getValue().entrySet().stream().forEach(pointEntry -> {
+                Result r = new ResultImpl();
+                r.setValidTimeStart(timeEntry.getKey());
+                Coordinate c = new Coordinate(pointEntry.getKey().x, pointEntry.getKey().y);
+                r.setValidGeometry(geometryFactory.createPoint(c));
+                r.setValue(MockModel.MODEL_ID.toString(), DataMatrix.WET_HOUR_SUM, pointEntry.getValue().toString());
+                retVal.add(r);
+            });
+        });
+        
+        // Iterate through dates
+        return retVal;
+    }
+
+    @Override
+    public ModelId getModelId() {
+        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+    }
+
+    @Override
+    public String getModelName() {
+        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+    }
+
+    @Override
+    public String getModelName(String language) {
+        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+    }
+
+    @Override
+    public String getLicense() {
+        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+    }
+
+    @Override
+    public String getCopyright() {
+        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+    }
+
+    @Override
+    public String getModelDescription() {
+        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+    }
+
+    @Override
+    public String getModelDescription(String language) {
+        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+    }
+
+    @Override
+    public String getWarningStatusInterpretation() {
+        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+    }
+
+    @Override
+    public String getWarningStatusInterpretation(String language) {
+        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+    }
+
+    @Override
+    public String getModelUsage() {
+        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+    }
+
+    @Override
+    public String getModelUsage(String language) {
+        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+    }
+
+    @Override
+    public String getSampleConfig() {
+        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+    }
+
+    @Override
+    public void setConfiguration(ModelConfiguration config) throws ConfigValidationException {
+        pointWeatherObservationList = (List<PointWeatherObservationList>) config.getConfigParameter("multiPointWeatherObservations");
+        this.timeZone = TimeZone.getTimeZone((String)config.getConfigParameter("timeZone"));
+    }
+
+}
diff --git a/src/test/java/no/nibio/vips/util/weather/metnothredds/MetNoThreddsDataParserTest.java b/src/test/java/no/nibio/vips/util/weather/metnothredds/MetNoThreddsDataParserTest.java
index bc433129..f92eb57d 100644
--- a/src/test/java/no/nibio/vips/util/weather/metnothredds/MetNoThreddsDataParserTest.java
+++ b/src/test/java/no/nibio/vips/util/weather/metnothredds/MetNoThreddsDataParserTest.java
@@ -18,22 +18,37 @@
  */
 package no.nibio.vips.util.weather.metnothredds;
 
+import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.geom.Geometry;
 import com.vividsolutions.jts.geom.GeometryFactory;
 import com.vividsolutions.jts.geom.Polygon;
 import java.io.File;
+import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.PrintWriter;
 import java.text.SimpleDateFormat;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Calendar;
 import java.util.Collections;
 import java.util.Date;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.TimeZone;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import no.nibio.vips.entity.ModelConfiguration;
 import no.nibio.vips.entity.PointWeatherObservationList;
+import no.nibio.vips.entity.Result;
 import no.nibio.vips.entity.WeatherObservation;
+import no.nibio.vips.logic.test.mock.MockModel;
+import no.nibio.vips.model.ConfigValidationException;
+import no.nibio.vips.model.ModelExcecutionException;
+import no.nibio.vips.util.SystemUtil;
 import no.nibio.vips.util.WeatherUtil;
 import org.junit.After;
 import org.junit.AfterClass;
@@ -41,6 +56,10 @@ import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.Test;
 import static org.junit.Assert.*;
+import org.openjdk.jol.info.GraphLayout;
+import org.wololo.geojson.Feature;
+import org.wololo.geojson.FeatureCollection;
+import org.wololo.jts2geojson.GeoJSONWriter;
 
 /**
  *
@@ -66,38 +85,297 @@ public class MetNoThreddsDataParserTest {
     @After
     public void tearDown() {
     }
-
-    //@Test
-    public void testGetGridData()
+    
+    @Test
+    public void testGetGridDataInPractice()
     {
-        System.out.println("testGetGridData");
+        System.out.println("testGetGridDataInPractice");
         Coordinate[] coords = new Coordinate[5];
-        coords[0] = new Coordinate(10.31,59.91);
-        coords[1] = new Coordinate(11.07,59.91);
+        /*coords[0] = new Coordinate(8.31,63.91);
+        coords[1] = new Coordinate(11.07,63.91);
         coords[2] = new Coordinate(11.07,59.52);
-        coords[3] = new Coordinate(10.31,59.52);
-        coords[4] = new Coordinate(10.31,59.91);
+        coords[3] = new Coordinate(8.31,59.52);
+        coords[4] = new Coordinate(8.31,63.91);*/
+        coords[0] = new Coordinate(-14.4103,71.8152);
+        coords[1] = new Coordinate(48.7212,71.8152);
+        coords[2] = new Coordinate(48.7212,51.7262);
+        coords[3] = new Coordinate(-14.4103,51.7262);
+        coords[4] = new Coordinate(-14.4103,71.8152);
         GeometryFactory gFac = new GeometryFactory();
         Polygon pol = gFac.createPolygon(coords);
-        
-        List<String> weatherParameters = Arrays.asList("TM","RR", "Q0", "TX","TN","UM");
+
+        List<String> weatherParameters = Arrays.asList("TM","RR","Q0","TX","TN","UM");
         MetNoThreddsDataParser instance = new MetNoThreddsDataParser();
-        
+
         Date start = new Date();
         Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("Europe/Oslo"));
         cal.setTime(new Date());
-        //cal.add(Calendar.DATE, -2);
+        cal.add(Calendar.DATE, -25);
         cal.set(Calendar.HOUR_OF_DAY, 5);
         Date dateFrom = new WeatherUtil().normalizeToExactHour(cal.getTime(), TimeZone.getDefault());
-        cal.add(Calendar.DATE, 5);
+        cal.add(Calendar.DATE, 27);
         Date dateTo = cal.getTime();
+
+        List<PointWeatherObservationList> result = null;
         
+        List<Result> modelResult = new ArrayList<>();
+        try
+        {
+            result = instance.getGridData(pol, 10.0, dateFrom, dateTo, weatherParameters);
+        }
+        catch(TooMuchDataToAskForException ex)
+        {
+            Geometry[] manageableGeometries = instance.getManageableGeometries(pol,10.0, dateFrom, dateTo, weatherParameters);
+            System.out.println("The original polygon has been split into " + manageableGeometries.length + " parts");
+            int counter = 0;
+            for(Geometry manageableGeometry:manageableGeometries)
+            {
+                counter++;
+                try
+                {
+                        result = instance.getGridData(manageableGeometry, 10.0, dateFrom, dateTo, weatherParameters);
+                        System.out.println("Batch #" + counter + " size=" + result.size());
+                        ModelConfiguration config = new ModelConfiguration();
+                        config.setConfigParameter("multiPointWeatherObservations", result);
+                        config.setConfigParameter("timeZone","Europe/Oslo");
+                        MockModel mockModel = new MockModel();
+                        mockModel.setConfiguration(config);
+                        modelResult = mockModel.getResult();
+                        // Simulate that we continue with the next iteration
+                        // In a real setting, we'd need to store the modelResults in some
+                        // permanent storage like files or DB (most likely)
+                        result = null; config = null; mockModel = null;
+                        System.gc();
+                }
+                catch(TooMuchDataToAskForException | ConfigValidationException | ModelExcecutionException ex2)
+                {    
+                    fail(ex2.getMessage());
+                }
+            }
+        }
+        assertNull(result);
+        System.out.println("Antall punkter=" + result.size());
         
-        List<PointWeatherObservationList> result = instance.getGridData(pol, 10.0, dateFrom, dateTo, weatherParameters);
-        assertNotNull(result);
         Long timeSpent = new Date().getTime() - start.getTime();
         System.out.println("Time spent=" + (new Double(timeSpent)/1000) + " seconds");
-        result.stream().forEach(mp->System.out.println(mp));
+        /*result.stream().forEach(mp->{
+            System.out.println(mp);
+            //System.out.println(GraphLayout.parseInstance(mp).toPrintable());
+        });
+        */
+        /*PointWeatherObservationList pwol = result.get(0);
+        WeatherObservation wo = pwol.getObservations().get(0);
+        System.out.println(ClassLayout.parseInstance(wo).toPrintable());
+        System.out.println(GraphLayout.parseInstance(wo).toFootprint());*/
+        System.out.println("Total memory used:\n" + GraphLayout.parseInstance(result).toFootprint());
+    }
+
+    //@Test
+    public void testGetGridData()
+    {
+        try {
+            System.out.println("testGetGridData");
+            Coordinate[] coords = new Coordinate[5];
+            /*coords[0] = new Coordinate(8.31,63.91);
+            coords[1] = new Coordinate(11.07,63.91);
+            coords[2] = new Coordinate(11.07,59.52);
+            coords[3] = new Coordinate(8.31,59.52);
+            coords[4] = new Coordinate(8.31,63.91);*/
+            coords[0] = new Coordinate(-14.4103,71.8152);
+            coords[1] = new Coordinate(48.7212,71.8152);
+            coords[2] = new Coordinate(48.7212,51.7262);
+            coords[3] = new Coordinate(-14.4103,51.7262);
+            coords[4] = new Coordinate(-14.4103,71.8152);
+            GeometryFactory gFac = new GeometryFactory();
+            Polygon pol = gFac.createPolygon(coords);
+            
+            List<String> weatherParameters = Arrays.asList("TM","RR","Q0","TX","TN","UM");
+            MetNoThreddsDataParser instance = new MetNoThreddsDataParser();
+            
+            Date start = new Date();
+            Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("Europe/Oslo"));
+            cal.setTime(new Date());
+            cal.add(Calendar.DATE, -25);
+            cal.set(Calendar.HOUR_OF_DAY, 5);
+            Date dateFrom = new WeatherUtil().normalizeToExactHour(cal.getTime(), TimeZone.getDefault());
+            cal.add(Calendar.DATE, 27);
+            Date dateTo = cal.getTime();
+            
+            
+            List<PointWeatherObservationList> result = instance.getGridData(pol, 10.0, dateFrom, dateTo, weatherParameters);
+            System.out.println("Antall punkter=" + result.size());
+            assertNotNull(result);
+            Long timeSpent = new Date().getTime() - start.getTime();
+            System.out.println("Time spent=" + (new Double(timeSpent)/1000) + " seconds");
+            /*result.stream().forEach(mp->{
+                System.out.println(mp);
+                //System.out.println(GraphLayout.parseInstance(mp).toPrintable());
+            });
+            */
+            /*PointWeatherObservationList pwol = result.get(0);
+            WeatherObservation wo = pwol.getObservations().get(0);
+            System.out.println(ClassLayout.parseInstance(wo).toPrintable());
+            System.out.println(GraphLayout.parseInstance(wo).toFootprint());*/
+            System.out.println("Total memory used:\n" + GraphLayout.parseInstance(result).toFootprint());
+        } catch (TooMuchDataToAskForException ex) {
+            ex.printStackTrace();
+            fail();
+        }
+    }
+    
+    //@Test
+    public void testGetManageableGeometries()
+    {
+            Double resolution = 10.0;
+            System.out.println("testGetManageableGeometries");
+            Coordinate[] coords = new Coordinate[5];
+            /*coords[0] = new Coordinate(8.31,63.91);
+            coords[1] = new Coordinate(11.07,63.91);
+            coords[2] = new Coordinate(11.07,59.52);
+            coords[3] = new Coordinate(8.31,59.52);
+            coords[4] = new Coordinate(8.31,63.91);*/
+            coords[0] = new Coordinate(-14.4103,71.8152);
+            coords[1] = new Coordinate(48.7212,71.8152);
+            coords[2] = new Coordinate(48.7212,51.7262);
+            coords[3] = new Coordinate(-14.4103,51.7262);
+            coords[4] = new Coordinate(-14.4103,71.8152);
+            GeometryFactory gFac = new GeometryFactory();
+            Polygon pol = gFac.createPolygon(coords);
+            
+            
+            List<String> weatherParameters = Arrays.asList("TM","RR", "Q0", "TX","TN","UM");
+            MetNoThreddsDataParser instance = new MetNoThreddsDataParser();
+            
+            Date start = new Date();
+            Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("Europe/Oslo"));
+            cal.setTime(new Date());
+            cal.add(Calendar.DATE, -50);
+            cal.set(Calendar.HOUR_OF_DAY, 5);
+            Date dateFrom = new WeatherUtil().normalizeToExactHour(cal.getTime(), TimeZone.getDefault());
+            cal.add(Calendar.DATE, 52);
+            Date dateTo = cal.getTime();
+            
+            
+            Geometry[] result = instance.getManageableGeometries(pol, resolution, dateFrom, dateTo, weatherParameters);
+            List<Feature> features = new ArrayList<>();
+            GeoJSONWriter writer = new GeoJSONWriter();
+            Map<String, Object> props = new HashMap<>();
+            for(Geometry g:result)
+            {
+                System.out.println(g.toText());
+                org.wololo.geojson.Geometry gj = writer.write(g);
+                features.add(new Feature(gj, props));
+            }
+            FeatureCollection coll = new FeatureCollection(features.toArray(new Feature[0]));
+            try(PrintWriter out = new PrintWriter("/home/treinar/prosjekter/vips/projects/2017_SpotIT/grid_model/subgeomtest.geojson")){
+                out.println(coll.toString());
+            }
+            catch(FileNotFoundException ex)
+            {
+                ex.printStackTrace();
+            }
+            try
+            {
+                ObjectMapper mp = new ObjectMapper();
+                for(Geometry g:result)
+                {
+                    List<PointWeatherObservationList> wData = instance.getGridData(g, 10.0, dateFrom, dateTo, weatherParameters);
+                    System.out.println(mp.writeValueAsString(wData));
+                }
+            }
+            catch(TooMuchDataToAskForException| JsonProcessingException ex)
+            {
+                ex.printStackTrace();
+            }
+            
+            
+    }
+    
+    //@Test
+    public void testGetNumberOfWeatherObservationsRequested(){
+        System.out.println("getNumberOfWeatherObservationsRequested");
+        Coordinate[] coords = new Coordinate[5];
+           coords[0] = new Coordinate(8.31,63.91);
+            coords[1] = new Coordinate(11.07,63.91);
+            coords[2] = new Coordinate(11.07,59.52);
+            coords[3] = new Coordinate(8.31,59.52);
+            coords[4] = new Coordinate(8.31,63.91);
+            GeometryFactory gFac = new GeometryFactory();
+            Polygon pol = gFac.createPolygon(coords);
+            pol = (Polygon) pol.getEnvelope();
+            
+            List<String> weatherParameters = Arrays.asList("TM","RR", "Q0", "TX","TN","UM");
+            MetNoThreddsDataParser instance = new MetNoThreddsDataParser();
+            
+
+            Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("Europe/Oslo"));
+            cal.setTime(new Date());
+            cal.add(Calendar.DATE, -25);
+            cal.set(Calendar.HOUR_OF_DAY, 5);
+            Date dateFrom = new WeatherUtil().normalizeToExactHour(cal.getTime(), TimeZone.getDefault());
+            cal.add(Calendar.DATE, 27);
+            Date dateTo = cal.getTime();
+            
+            
+            Long result = instance.getNumberOfWeatherObservationsRequested(pol, 10.0, dateFrom, dateTo, weatherParameters);
+            System.out.println("Weather observations requested=" + result);
+    }
+    
+    //@Test
+    public void testGetMemoryNeededForRequest(){
+        System.out.println("getMemoryNeededForRequest");
+        Coordinate[] coords = new Coordinate[5];
+           /*coords[0] = new Coordinate(8.31,63.91);
+            coords[1] = new Coordinate(11.07,63.91);
+            coords[2] = new Coordinate(11.07,59.52);
+            coords[3] = new Coordinate(8.31,59.52);
+            coords[4] = new Coordinate(8.31,63.91);*/
+           coords[0] = new Coordinate(-14.4103,71.8152);
+            coords[1] = new Coordinate(48.7212,71.8152);
+            coords[2] = new Coordinate(48.7212,51.7262);
+            coords[3] = new Coordinate(-14.4103,51.7262);
+            coords[4] = new Coordinate(-14.4103,71.8152);
+            GeometryFactory gFac = new GeometryFactory();
+            Polygon pol = gFac.createPolygon(coords);
+            pol = (Polygon) pol.getEnvelope();
+            
+            List<String> weatherParameters = Arrays.asList("TM","RR", "Q0", "TX","TN","UM");
+            MetNoThreddsDataParser instance = new MetNoThreddsDataParser();
+            
+            Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("Europe/Oslo"));
+            cal.setTime(new Date());
+            cal.add(Calendar.DATE, -25);
+            cal.set(Calendar.HOUR_OF_DAY, 5);
+            Date dateFrom = new WeatherUtil().normalizeToExactHour(cal.getTime(), TimeZone.getDefault());
+            cal.add(Calendar.DATE, 27);
+            Date dateTo = cal.getTime();
+            
+            
+            Long result = instance.getMemoryNeededForRequest(pol, 10.0, dateFrom, dateTo, weatherParameters);
+            System.out.println("Memory needed for request=" + result + " bytes");
+            
+            System.out.println("presumableFreeMemory=" + SystemUtil.getPresumableFreeMemory());
+    }
+    
+    //@Test
+    public void testGetNumberOfGridPointsInEnvelope()//(Polygon envelope, Double resolution)
+    {
+        System.out.println("getNumberOfGridPointsInEnvelope");
+        Coordinate[] coords = new Coordinate[5];
+            coords[0] = new Coordinate(8.31,63.91);
+            coords[1] = new Coordinate(11.07,63.91);
+            coords[2] = new Coordinate(11.07,59.52);
+            coords[3] = new Coordinate(8.31,59.52);
+            coords[4] = new Coordinate(8.31,63.91);
+            GeometryFactory gFac = new GeometryFactory();
+            Polygon pol = gFac.createPolygon(coords);
+            pol = (Polygon) pol.getEnvelope();
+            
+            MetNoThreddsDataParser instance = new MetNoThreddsDataParser();
+            
+            Long result = instance.getNumberOfGridPointsInEnvelope(pol, 10.0);
+            
+            System.out.println("Number of grid points in envelope=" + result);
     }
     
     /**
-- 
GitLab