Statistics
| Revision:

root / org.gvsig.toolbox / trunk / org.gvsig.toolbox / org.gvsig.toolbox.algorithm / src / main / java / es / unex / sextante / gridCategorical / tabulateArea / TabulateAreaAlgorithm.java @ 59

History | View | Annotate | Download (12.9 KB)

1
/*******************************************************************************
2
TabulateAreaAlgorithm.java
3
Copyright (C) 2009 ETC-LUSI http://etc-lusi.eionet.europa.eu/
4
 *******************************************************************************/
5
package es.unex.sextante.gridCategorical.tabulateArea;
6

    
7
import java.io.IOException;
8
import java.util.Arrays;
9
import java.util.HashMap;
10
import java.util.HashSet;
11
import java.util.Iterator;
12
import java.util.Map;
13
import java.util.TreeSet;
14

    
15
import es.unex.sextante.core.GeoAlgorithm;
16
import es.unex.sextante.core.OutputFactory;
17
import es.unex.sextante.core.Sextante;
18
import es.unex.sextante.dataObjects.IRasterLayer;
19
import es.unex.sextante.dataObjects.IRecord;
20
import es.unex.sextante.dataObjects.ITable;
21
import es.unex.sextante.exceptions.GeoAlgorithmExecutionException;
22
import es.unex.sextante.exceptions.RepeatedParameterNameException;
23

    
24
/**
25
 * 
26
 * @author Cesar Martinez Izquierdo
27
 */
28
public class TabulateAreaAlgorithm
29
         extends
30
            GeoAlgorithm {
31

    
32
   public static final String GRID          = "GRID";
33
   public static final String GRID2         = "GRID2";
34
   public static final String TABLE         = "TABLE";
35
   public static final String COMPACT       = "COMPACT";
36

    
37
   private int                m_iNX, m_iNY;
38
   private IRasterLayer       m_Window, m_Window2;
39

    
40
   private int                numAreas;                                               // number of areas (or tiles) that the input raster are divided in to process
41

    
42
   // for logging purposes:
43
   private int                absStep;
44
   private int                normStep;
45
   private final String       LOGGING_TEXT1 = Sextante.getText("Tabulating subzones");
46
   private final String       LOGGING_TEXT2 = Sextante.getText("Merging results");
47

    
48
   private final int          MAXTILESIZE   = 8388608;                                // max number of pixels per subArea (it will define the tile size we'll use)
49

    
50

    
51
   //        private final int MAXTILESIZE = 4194304; // max number of pixels per subArea (it will define the tile size we'll use)
52
   //        private final int MAXTILESIZE = 1048576; // max number of pixels per subArea (it will define the tile size we'll use)
53

    
54

    
55
   @Override
56
   public void defineCharacteristics() {
57

    
58
      setName(Sextante.getText("Tabulate_Area"));
59
      setGroup(Sextante.getText("Raster_categories_analysis"));
60
      setUserCanDefineAnalysisExtent(true);
61

    
62
      try {
63
         m_Parameters.addInputRasterLayer(GRID, Sextante.getText("Zones_Grid"), true);
64
         m_Parameters.addInputRasterLayer(GRID2, Sextante.getText("Values_Grid"), true);
65
         m_Parameters.addBoolean(COMPACT, Sextante.getText("Compact_output"), false);
66
         addOutputTable(TABLE, Sextante.getText("Tabulate_Area"));
67
      }
68
      catch (final RepeatedParameterNameException e) {
69
         Sextante.addErrorToLog(e);
70
      }
71

    
72
   }
73

    
74

    
75
   @Override
76
   public boolean processAlgorithm() throws GeoAlgorithmExecutionException {
77
      final long time1 = System.currentTimeMillis();
78
      m_Window = m_Parameters.getParameterValueAsRasterLayer(GRID);
79
      m_Window2 = m_Parameters.getParameterValueAsRasterLayer(GRID2);
80
      final boolean useCompactOutput = m_Parameters.getParameterValueAsBoolean(COMPACT);
81

    
82
      m_Window.setWindowExtent(m_AnalysisExtent);
83
      m_Window.setInterpolationMethod(IRasterLayer.INTERPOLATION_NearestNeighbour);
84
      m_Window2.setWindowExtent(m_AnalysisExtent);
85
      m_Window2.setInterpolationMethod(IRasterLayer.INTERPOLATION_NearestNeighbour);
86

    
87
      m_iNX = m_Window.getNX();
88
      m_iNY = m_Window.getNY();
89
      final double cellArea = m_AnalysisExtent.getCellSize() * m_AnalysisExtent.getCellSize();
90

    
91
      // compute the maximum number of rows per subarea
92
      int maxRowPerArea;
93
      if (MAXTILESIZE > m_iNX) {
94
         maxRowPerArea = (int) Math.floor(MAXTILESIZE / m_iNX);
95
      }
96
      else {
97
         maxRowPerArea = 1;
98
      }
99
      numAreas = (int) Math.ceil((float) m_iNY / (float) maxRowPerArea);
100

    
101
      TabulateSubArea subArea = new TabulateSubArea(this, m_Window, m_Window2);
102

    
103
      // process all the sub-areas
104
      int currY = 0;
105
      SubAreaResult[] result = new SubAreaResult[numAreas];
106
      System.out.println("Num Areas: " + numAreas);
107
      for (int curArea = 0; curArea < numAreas; curArea++) {
108
         final int maxY = Math.min((currY + maxRowPerArea - 1), m_iNY - 1);
109
         result[curArea] = subArea.processArea(0, currY, m_iNX - 1, maxY);
110
         setProgress(LOGGING_TEXT1);
111
         System.gc();
112

    
113
         if ((result[curArea] != null) && !result[curArea].isSuccessful()) {
114
            for (final SubAreaResult element : result) {
115
               try {
116
                  if (result[curArea].getReader() != null) {
117
                     result[curArea].getReader().close();
118
                  }
119
               }
120
               catch (final IOException e) {}
121
            }
122
            return false;
123
         }
124
         currY += maxRowPerArea;
125
      }
126
      subArea = null;
127
      System.gc();
128

    
129
      // we'll assign a global ID for all the zones
130
      // first, we order the zones
131
      final HashSet<Object> globalZonesSet = new HashSet<Object>();
132
      for (int curArea = 0; curArea < numAreas; curArea++) {
133
         if (result[curArea] == null) {
134
            continue;
135
         }
136
         final Map<Object, Integer> currZonesMap = result[curArea].getZones();
137
         final Iterator<Object> it = currZonesMap.keySet().iterator();
138
         while (it.hasNext()) {
139
            final Object o = it.next();
140
            if (!globalZonesSet.contains(o)) {
141
               globalZonesSet.add(o);
142
            }
143
         }
144
      }
145

    
146
      // according to my tests, this is quite faster than using Collections.sort or a TreeMap
147
      Object[] m_ArrayZones = globalZonesSet.toArray(new Object[globalZonesSet.size()]);
148
      Arrays.sort(m_ArrayZones);
149
      final HashMap<Object, Integer> globalZonesMap = new HashMap<Object, Integer>();
150
      for (int i = 0; i < m_ArrayZones.length; i++) {
151
         globalZonesMap.put(m_ArrayZones[i], new Integer(i + 1));
152
      }
153
      m_ArrayZones = null;
154

    
155
      Sextante.addInfoToLog("Total zones: " + globalZonesMap.size());
156
      setProgressText(LOGGING_TEXT2);
157
      setProgress(51, 100);
158

    
159

    
160
      // create final output table
161
      final String sTableName = Sextante.getText("Tabulate_Area");
162

    
163
      String sFields[];
164
      Class types[];
165

    
166
      if (useCompactOutput) {
167
         sFields = new String[3]; // value, zone, area
168
         types = new Class[3];
169
         sFields[0] = "VALUE";
170
         types[0] = Integer.class;
171
         sFields[1] = "ZONE";
172
         types[1] = Integer.class;
173
         sFields[2] = "AREA";
174
         types[2] = Integer.class;
175
      }
176
      else {
177
         sFields = new String[globalZonesMap.size() + 1];
178
         types = new Class[globalZonesMap.size() + 1];
179
         sFields[0] = "VALUE";
180
         types[0] = Integer.class;
181
         final Iterator<Object> it = globalZonesMap.keySet().iterator();
182
         while (it.hasNext()) {
183
            final Object zone = it.next();
184
            final int idx = globalZonesMap.get(zone);
185
            sFields[idx] = zone.toString();
186
            types[idx] = zone.getClass(); // because different drivers might return different classes: Long, Integer...
187
         }
188
      }
189

    
190
      final ITable outputTable = getNewTable(TABLE, sTableName, types, sFields);
191

    
192
      // Merge all the sub-area results in a single table.
193
      // The general strategy here is:
194
      //   - there are several on-disk tables with partial results
195
      //   - each table contains one record per value, and the records are ordered by value
196
      //   - each record contains the value as the first fields, and then one field per zone,
197
      //       which contains the count of the current value in this zone
198
      //   - we will read one record from every table, and store the next available values in a TreeSet ("nextValues")
199
      //   - then we will calculate the total sum for a value (summing the partial results for the value)
200
      //   - after a value is calculated, it is deleted from the "nextValues" TreeSet and the result is written to the final output table
201
      //   - when there are no values available in "nextValues" TreeSet, the algorithm finishes
202
      try {
203
         final TreeSet nextValues = new TreeSet();
204
         for (int curArea = 0; curArea < numAreas; curArea++) {
205
            if (result[curArea] == null) {
206
               continue;
207
            }
208
            if (result[curArea].getReader().hasNext()) {
209
               final IRecord next = result[curArea].getReader().getNext();
210
               if (next != null) {
211
                  nextValues.add(next.getValue(0));
212
               }
213
            }
214
         }
215
         Object[] row; // this will contain the merged row
216
         final int zoneCount = 0;
217
         while (nextValues.size() > 0) {
218
            row = new Object[globalZonesMap.size() + 1];
219
            final Object curValue = nextValues.first();
220
            row[0] = curValue;
221
            for (int k = 1; k < row.length; k++) {
222
               row[k] = 0;
223
            }
224
            for (int curArea = 0; curArea < numAreas; curArea++) {
225
               if (result[curArea] == null) {
226
                  continue;
227
               }
228
               IRecord cur = result[curArea].getReader().getCurrent();
229
               if ((cur != null) && cur.getValue(0).equals(curValue)) {
230
                  final Map<Object, Integer> zonesMap = result[curArea].getZones();
231
                  final Iterator zoneIt = zonesMap.keySet().iterator();
232
                  while (zoneIt.hasNext()) {
233
                     final Object theZone = zoneIt.next();
234
                     final int globalIndex = globalZonesMap.get(theZone);
235
                     final int localIndex = zonesMap.get(theZone);
236
                     row[globalIndex] = new Long(((Number) row[globalIndex]).longValue()
237
                                                 + ((Number) cur.getValue(localIndex + 1)).longValue());
238
                  }
239
                  if (result[curArea].getReader().hasNext()) {
240
                     cur = result[curArea].getReader().getNext();
241
                     if (cur != null) {
242
                        nextValues.add(cur.getValue(0));
243
                     }
244
                  }
245
                  else {
246
                     // The reader has reached the end of the file,
247
                     // so we can safely remove this result to speed up the process
248
                     result[curArea] = null;
249
                     setProgress(LOGGING_TEXT2);
250
                  }
251
               }
252
            }
253
            nextValues.remove(curValue); // this zone has been merged
254
            if (useCompactOutput) {
255
               final Object[] tmpRow = new Object[3];
256
               final Iterator<Object> iter = globalZonesMap.keySet().iterator();
257
               while (iter.hasNext()) {
258
                  final Object zone = iter.next();
259
                  final int globalIdx = globalZonesMap.get(zone);
260
                  final int value = ((Number) row[globalIdx]).intValue();
261
                  if (value > 0) {
262
                     tmpRow[0] = row[0];
263
                     tmpRow[1] = zone;
264
                     final double areaValue = ((Number) row[globalIdx]).longValue() * cellArea;
265
                     tmpRow[2] = new Long(Math.round(areaValue));
266
                     outputTable.addRecord(tmpRow);
267
                  }
268
               }
269
            }
270
            else {
271
               for (int i = 1; i < row.length; i++) {
272
                  final double areaValue = ((Number) row[i]).longValue() * cellArea;
273
                  row[i] = new Long(Math.round(areaValue));
274
               }
275
               outputTable.addRecord(row);
276
            }
277

    
278
         }
279
         final long time2 = System.currentTimeMillis();
280
         setProgress(100, 100);
281
         result = null;
282
         System.gc();
283
         System.out.println("Processing time: " + ((time2 - time1) / 1000.0) + "secs.");
284
         Sextante.addInfoToLog(Sextante.getText("Processing_time") + ((time2 - time1) / 1000.0) + "secs.");
285
         System.out.println("Cell size: " + m_AnalysisExtent.getCellSize());
286
         // not necessary, it is automatically performed by Sextante // outputTable.postProcess();
287
      }
288
      catch (final Exception e) {
289
         Sextante.addErrorToLog(e);
290
         return false;
291
      }
292

    
293
      return !m_Task.isCanceled();
294
   }
295

    
296

    
297
   protected void setProgress(final String text) {
298
      // to avoid redundant logging
299
      absStep++;
300
      final int curStep = absStep * 100 / (2 * numAreas);
301
      if (curStep > normStep) {
302
         normStep = curStep;
303
         setProgressText(text);
304
         setProgress(normStep, 100);
305
      }
306
   }
307

    
308

    
309
   protected boolean isCancelled() {
310
      return m_Task.isCanceled();
311
   }
312

    
313

    
314
   protected OutputFactory getOuputFactory() {
315
      return m_OutputFactory;
316
   }
317

    
318
}