ThinkGeo.com    |     Documentation    |     Premium Support

IsoLines performance and Threading

We have been using the Isolines (GridIsoLineLayer, GridFeatureLayer) features to plot data we generate from simulations.  The simplest plot use a 5 x 5 grid cell count and the isolines generate in seconds (or less).  We have noticed a significant dropoff as the grid cell count increases. For example with a 50 x 50  grid the plots generate in around a minute.  At 100 x 100 it may take several minutes.  The largest I have worked on personnally was about 800 x 800 and it took around 3 hours and 20 minutes.  Our user have tried some larger plots but I do not have a time for the plotting operation.  


 Our question is whether it is possible to generate the isolines in a secondary thread so that the GUI of our application (and the window containing the MapSuite map can continue to be responsive?  As it is right now, when we call the method to generate the GridIsoLineLayer and GridFeatureLayer and then call the winformMap.Refresh to render it on the map, the thread that controls the GUI of our application has to wait until the isoline generation completes.  This causes our GUI to have partially repainted or unresponive repainting of the GUI.    It seems that the actual guts of the the GridIsoLineLayer (the interpolation calculations) are not being performed until I call the winformMap.Refresh.   Since this also update the MapSuite map window, I have not tried it in a separate thread because we have had issues in the past with multiple threads accesing the GUI of our application.  Is there a model for the code that will allow us to generate the isolines as a background or secondary thread and then render the output to the map in the thread that controls the GUI windows?


Richard



Richard,
 
I guess you must have had a huge number of reference points which makes it so slow to generate a big grid cell matrix. I tried generating a 800*800 cell matrix in the DisplayIsoLine code community sample (wiki.thinkgeo.com/wiki/File:…111115.zip), and it takes only 46 seconds. My test machine is with Q8200 CPU and 8G Memory. 
 
There are 2 steps to generate IsoLines in GridIsoLineFeatureLayer, first it generates a gridCellMatrix and second, it generate IsoLines based on that Matrix. We provide static methods for both of the 2 steps which you can put in a separate thread. The first method is in GridFeatureSource class:

public static GridCell[,] GenerateGridMatrix(GridDefinition gridDefinition, GridInterpolationModel gridInterpolationModel)

 
The second static methods in IsoLineLayer accept the generated gridMatrix and create IsoLineFeatures.  

Collection<Feature> GetIsoFeatures(GridCell[,] gridMatrix, IEnumerable<double> isoLineLevels, string dataValueColumnName, IsoLineType isoLineType)

 
You can put them in another thread, generate the IsoLines features and load them when needed by adding them to an InmemoryFeatureLayer maybe. This gives you a responsive UI. 
 
Thanks,
 
Ben

 



Ben, 
  
 Thanks for the response. 
  
 Yes, I guess we do have a lot of reference points.  The reference points we are using are generated by a simulation.  The simulation is already interpolating a value for each cell in the matrix of values.  Therefore, if the grid size is 5 cells by 5 cells, we should have 25 values one per cell.  If we are using a 100 by 100 grid size we would have 10000 reference points.  On the 800 by 800 grid, we had 640,000 reference points.  We do sometime have cells with the "NoDataValue" defined but these are usually a small percentage at the most.  I would love to see it generate the isolines in 46 seconds but that is not the experience we are getting. 
  
 Since my original post, I have been trying to isolate the area that is taking the most time and it does appear to be the call we make to GridFeatureSource.GenerateGrid rather than the call to winformsMap1.Refresh.   The refresh is the second longest running call is the process from what I have seen lately.  I believe GridFeatureSource.GenerateGrid and the static GridFeatureSource.GenerateGridMatrix you mentioned above must be equivalent.  I currently do not use GetIsoFeatures.  The Isolines sample that I modeled my code after was serializing the output of the GenerateGrid to a temporary output file on disk. I then use the GridIsoLIneLayer constructor with an argument to the temporary file path.  Would there be a big speed advantage in using GetIsoFeatures over the constructor using the serialized file?  Since I do not call GetIsoFeatures directly is it being called indirectly from some other call I make such as the constructor for GridIsoLineLayer or on the refresh? 
  
 Thanks  
 Richard 


Richard, 
  
 You are right the GridFeatureSource.GenerateGrid() is taking the most time. As you’ve already serialized the grid to a file and load it back directly in GridIsoLineLayer’s constructor, this most time-consuming part has been skipped. GridIsoLineLayer internally calls the static GetIsoFeatures() to get the IsoLine Features, which is usually running fast especially compared with the GenerateGrid(), but you may still getting performance benefits by running GetIsoFeatures() first, serialize the return features, and deserialize those features when needed. This skips the GridCells->IsoLine calculation but spend time on Serialization/Deserialization, so the performance benefit depends on the number of grid cells and isoLines. 
  
 Since the original post, you certainly don’t want to put GenerateGrid() to your main UI thread, and probably you can also move GetIsoFeatures() to another thread and just load the returning features in the main thread when ready.  In this way the UI thread just simply load features and move all the dirty work away. 
  
 Thanks, 
  
 Ben 


Ben, 
  
 We have several additional questions that we need answers to.  Do you have any description of how the InverseDistanceWeighterGridInterpolationModel class works to interpolate the data?  Is the output of the GenerateGrid and GenerateGridMatrix always a rectangluar array?  The output of the GenerateGridMatrix is shown as GridCell[,].  Is this a jagged array or are the dimensions fully populated into a rectangular array?   Is the purpose of the GenerateGrid and GenerateGridMatrix to take a randomly distributing set of input data points and use interpolation to output a rectangluar array of a single data point per grid cell?  How does the interpolation handle the empty (NoDataValue) grid cells?  
  
 And one last question from a programmer.  The GridIsoLineLayer class has four constructors. Three of them take a fileNamePath as an input argument.  The fourth is the default constructor with no input arguments.  Is there a way to use data loaded in memory as the input to the GirdIsoLineLayer and is there any sample code for this? 
  
 Thanks for your help 
  
 Richard

 Richard,



You are right. The returned GridCell[,] is always a rectangular array, will never be a jagged array. The purpose of the GenerateGrid and GenerateGridMatrix is to take a randomly distributing set of input data points and use interpolation to output a rectangluar array of a single data point per grid cell. The key logic in InverseDistanceWeightedGridInterpolationModel is as following, you can see it’s pretty simple.



 protected override double InterpolateCore(RectangleShape cellExtent, GridDefinition gridDefinition)
        {
            double cellCenterX = cellExtent.GetCenterPoint().X;
            double cellCenterY = cellExtent.GetCenterPoint().Y;
            double cellValue = gridDefinition.NoDataValue;
            double Zx1 = 0;
            double Zx2 = 0;
 
            foreach (PointShape closePoint in gridDefinition.DataPoints.Keys)
            {
                double distance = Math.Pow((cellCenterX - closePoint.X), 2) + Math.Pow((cellCenterY - closePoint.Y), 2);
 
                if (distance < searchRadius)
                {
                    double alpha = Math.Pow((1 / distance), power / 2);
 
                    Zx1 = Zx1 + alpha * (gridDefinition.DataPoints[closePoint]);
                    Zx2 = Zx2 + alpha;
                }
            }
 
            if (Zx1 != 0 && Zx2 != 0)
            {
                cellValue = (Zx1 / Zx2);
            }
            return cellValue;
        }


If a cell value cannot be properly calculated while generating the grid cell matrix, it will be assigned as NoDataValue (-9999) by default.  If passing a matrix with NoDataValue to a GridIsoLineLayer for example, the noDataValue will first be replaced by some other value by calling GridFeatureSource.ReplaceNoDataValue with a given GridInterpolationModel.  You can also call it manually to replace the NoDataValue before using the matrix to generate IsoLines. (The ReplaceNoDataValue method is now only available in the DEV Branch (5.5.*.0)).


To create a GridIsoLineLayer completely in Memory, you can use the StreamLoading event.  Please check this post for detail, and you can also check the HowDoI sample to see how to use StreamLoading event. 


gis.thinkgeo.com/Support/Dis...fault.aspx


Thanks,


Ben