Statistics
| Revision:

root / org.gvsig.toolbox / trunk / org.gvsig.toolbox / org.gvsig.toolbox.algorithm / src / main / java / es / unex / sextante / gridAnalysis / supervisedClassification / SupervisedClassificationAlgorithm.java @ 59

History | View | Annotate | Download (18.7 KB)

1
package es.unex.sextante.gridAnalysis.supervisedClassification;
2

    
3
import java.awt.geom.Rectangle2D;
4
import java.util.ArrayList;
5
import java.util.Arrays;
6
import java.util.HashMap;
7
import java.util.Iterator;
8
import java.util.Set;
9

    
10
import com.vividsolutions.jts.geom.Coordinate;
11
import com.vividsolutions.jts.geom.Envelope;
12
import com.vividsolutions.jts.geom.Geometry;
13

    
14
import es.unex.sextante.additionalInfo.AdditionalInfoMultipleInput;
15
import es.unex.sextante.additionalInfo.AdditionalInfoVectorLayer;
16
import es.unex.sextante.core.AnalysisExtent;
17
import es.unex.sextante.core.GeoAlgorithm;
18
import es.unex.sextante.core.Sextante;
19
import es.unex.sextante.dataObjects.IFeature;
20
import es.unex.sextante.dataObjects.IFeatureIterator;
21
import es.unex.sextante.dataObjects.IRasterLayer;
22
import es.unex.sextante.dataObjects.ITable;
23
import es.unex.sextante.dataObjects.IVectorLayer;
24
import es.unex.sextante.exceptions.GeoAlgorithmExecutionException;
25
import es.unex.sextante.exceptions.IteratorException;
26
import es.unex.sextante.exceptions.OptionalParentParameterException;
27
import es.unex.sextante.exceptions.RepeatedParameterNameException;
28
import es.unex.sextante.exceptions.UndefinedParentParameterNameException;
29
import es.unex.sextante.exceptions.UnsupportedOutputChannelException;
30
import es.unex.sextante.math.simpleStats.SimpleStats;
31
import es.unex.sextante.parameters.RasterLayerAndBand;
32

    
33
public class SupervisedClassificationAlgorithm
34
         extends
35
            GeoAlgorithm {
36

    
37
   public static final String INPUT                 = "INPUT";
38
   public static final String POLYGONS              = "POLYGONS";
39
   public static final String FIELD                 = "FIELD";
40
   public static final String METHOD                = "METHOD";
41
   public static final String CLASSIFICATION        = "CLASSIFICATION";
42
   public static final String CLASSES               = "CLASSES";
43

    
44
   public static final int    METHOD_PARALELLPIPED  = 0;
45
   public static final int    METHOD_MIN_DISTANCE   = 1;
46
   public static final int    METHOD_MAX_LIKELIHOOD = 2;
47

    
48
   private int                m_iMinX, m_iMinY;
49
   private int                m_iField;
50
   private IRasterLayer[]     m_Window;
51
   private IRasterLayer       m_Output;
52
   private ArrayList          m_Bands;
53
   private IVectorLayer       m_Polygons;
54
   private ITable             m_Table;
55
   private HashMap            m_Classes;
56
   private int[]              m_iBands;
57

    
58

    
59
   @Override
60
   public void defineCharacteristics() {
61

    
62
      final String sMethod[] = { Sextante.getText("Parallelepiped"), Sextante.getText("Minimum_distance"),
63
               Sextante.getText("Maximum_likelihood") };
64

    
65
      setName(Sextante.getText("Supervised_classification"));
66
      setGroup(Sextante.getText("Raster_layer_analysis"));
67
      setUserCanDefineAnalysisExtent(true);
68

    
69
      try {
70
         m_Parameters.addMultipleInput(INPUT, Sextante.getText("Bands"), AdditionalInfoMultipleInput.DATA_TYPE_BAND, true);
71
         m_Parameters.addInputVectorLayer(POLYGONS, Sextante.getText("Polygons"), AdditionalInfoVectorLayer.SHAPE_TYPE_POLYGON,
72
                  true);
73
         m_Parameters.addTableField(FIELD, Sextante.getText("Field_with_class_value"), "POLYGONS");
74
         m_Parameters.addSelection(METHOD, Sextante.getText("Method"), sMethod);
75
         addOutputRasterLayer(CLASSIFICATION, Sextante.getText("Classification"));
76
         addOutputTable(CLASSES, Sextante.getText("Classes"));
77
      }
78
      catch (final RepeatedParameterNameException e) {
79
         Sextante.addErrorToLog(e);
80
      }
81
      catch (final OptionalParentParameterException e) {
82
         Sextante.addErrorToLog(e);
83
      }
84
      catch (final UndefinedParentParameterNameException e) {
85
         Sextante.addErrorToLog(e);
86
      }
87

    
88
   }
89

    
90

    
91
   @Override
92
   public boolean processAlgorithm() throws GeoAlgorithmExecutionException {
93

    
94
      int i;
95
      AnalysisExtent ge;
96

    
97
      final int iMethod = m_Parameters.getParameterValueAsInt(METHOD);
98
      m_iField = m_Parameters.getParameterValueAsInt(FIELD);
99
      m_Bands = m_Parameters.getParameterValueAsArrayList(INPUT);
100
      m_Polygons = m_Parameters.getParameterValueAsVectorLayer(POLYGONS);
101

    
102
      if (m_Bands.size() == 0) {
103
         throw new GeoAlgorithmExecutionException("At least one band is needed to run this algorithm");
104
      }
105

    
106
      m_Classes = new HashMap();
107

    
108
      getClassInformation();
109

    
110
      if (m_Task.isCanceled()) {
111
         return false;
112
      }
113

    
114
      m_Output = getNewRasterLayer(CLASSIFICATION, Sextante.getText("Classification"), IRasterLayer.RASTER_DATA_TYPE_SHORT);
115
      m_Output.setNoDataValue(-1);
116
      ge = m_Output.getWindowGridExtent();
117

    
118
      for (i = 0; i < m_Window.length; i++) {
119
         m_Window[i].setWindowExtent(ge);
120
      }
121

    
122
      switch (iMethod) {
123
         case 0:
124
            doParalellpiped();
125
         case 1:
126
         default:
127
            doMinimumDistance();
128
         case 2:
129
            doMaximumLikelihood();
130
      }
131

    
132
      return !m_Task.isCanceled();
133

    
134
   }
135

    
136

    
137
   private void getClassInformation() throws UnsupportedOutputChannelException {
138

    
139
      int i, j;
140
      int iGrid;
141
      Object classID;
142
      Set set;
143
      Iterator iter;
144
      RasterLayerAndBand band;
145
      ArrayList stats;
146
      String sClass;
147
      final String sField[] = new String[1 + m_Bands.size() * 2];
148
      final Class types[] = new Class[1 + m_Bands.size() * 2];
149
      final Object values[] = new Object[m_Bands.size() * 2 + 1];
150
      SimpleStats substats;
151

    
152
      sField[0] = Sextante.getText("Name");
153
      types[0] = String.class;
154
      m_Window = new IRasterLayer[m_Bands.size()];
155
      for (i = 0; i < m_Bands.size(); i++) {
156
         band = (RasterLayerAndBand) m_Bands.get(i);
157
         m_iBands = new int[m_Bands.size()];
158
         m_iBands[i] = band.getBand();
159
         m_Window[i] = band.getRasterLayer();
160
         final AnalysisExtent extent = getAdjustedGridExtent(i);
161
         m_Window[i].setWindowExtent(extent);
162
         sField[i * 2 + 1] = band.getRasterLayer().getName() + "|" + Integer.toString(band.getBand());
163
         sField[i * 2 + 2] = band.getRasterLayer().getName() + "|" + Integer.toString(band.getBand());
164
         types[i * 2 + 1] = Double.class;
165
         types[i * 2 + 2] = Double.class;
166
      }
167

    
168

    
169
      setProgressText(Sextante.getText("Calculating_spectral_signatures"));
170
      i = 0;
171
      final int iShapeCount = m_Polygons.getShapesCount();
172
      final IFeatureIterator featureIter = m_Polygons.iterator();
173
      while (featureIter.hasNext() && setProgress(i, iShapeCount)) {
174
         IFeature feature;
175
         try {
176
            feature = featureIter.next();
177
            final Geometry geometry = feature.getGeometry();
178
            final Object[] record = feature.getRecord().getValues();
179
            sClass = record[m_iField].toString();
180
            if (m_Classes.containsKey(sClass)) {
181
               stats = (ArrayList) m_Classes.get(sClass);
182
            }
183
            else {
184
               stats = new ArrayList();
185
               for (j = 0; j < m_Bands.size(); j++) {
186
                  stats.add(new SimpleStats());
187
               }
188
               m_Classes.put(sClass, stats);
189
            }
190
            doPolygon(geometry, stats);
191
            i++;
192
         }
193
         catch (final IteratorException e) {
194
            //we ignore wrong features
195
         }
196
      }
197
      featureIter.close();
198

    
199
      if (m_Task.isCanceled()) {
200
         return;
201
      }
202

    
203
      m_Table = getNewTable(CLASSES, Sextante.getText("Classes"), types, sField);
204

    
205
      set = m_Classes.keySet();
206
      iter = set.iterator();
207

    
208
      while (iter.hasNext()) {
209
         classID = iter.next();
210
         stats = (ArrayList) m_Classes.get(classID);
211
         values[0] = new String(classID.toString());
212
         for (iGrid = 0; iGrid < m_Bands.size(); iGrid++) {
213
            substats = (SimpleStats) stats.get(iGrid);
214
            values[1 + iGrid * 2] = new Double(substats.getMean());
215
            values[2 + iGrid * 2] = new Double(substats.getStdDev());
216
         }
217
         m_Table.addRecord(values);
218
      }
219

    
220
   }
221

    
222

    
223
   private void doPolygon(final Geometry geom,
224
                          final ArrayList stats) {
225

    
226
      for (int i = 0; i < geom.getNumGeometries(); i++) {
227
         final Geometry part = geom.getGeometryN(i);
228
         doPolygonPart(part, stats);
229
      }
230

    
231
   }
232

    
233

    
234
   private boolean getCrossing(final Coordinate crossing,
235
                               final Coordinate a1,
236
                               final Coordinate a2,
237
                               final Coordinate b1,
238
                               final Coordinate b2) {
239

    
240
      double lambda, div, a_dx, a_dy, b_dx, b_dy;
241

    
242
      a_dx = a2.x - a1.x;
243
      a_dy = a2.y - a1.y;
244

    
245
      b_dx = b2.x - b1.x;
246
      b_dy = b2.y - b1.y;
247

    
248
      if ((div = a_dx * b_dy - b_dx * a_dy) != 0.0) {
249
         lambda = ((b1.x - a1.x) * b_dy - b_dx * (b1.y - a1.y)) / div;
250

    
251
         crossing.x = a1.x + lambda * a_dx;
252
         crossing.y = a1.y + lambda * a_dy;
253

    
254
         return true;
255

    
256
      }
257

    
258
      return false;
259

    
260
   }
261

    
262

    
263
   private AnalysisExtent getAdjustedGridExtent(final int iLayer) {
264

    
265
      double iMaxX, iMaxY;
266
      double dMinX, dMinY;
267
      double dMinX2, dMinY2, dMaxX2, dMaxY2;
268
      double dCellSize;
269
      final AnalysisExtent ge = new AnalysisExtent();
270

    
271
      final Rectangle2D rect = m_Polygons.getFullExtent();
272
      dMinX = m_Window[iLayer].getLayerGridExtent().getXMin();
273
      dMinY = m_Window[iLayer].getLayerGridExtent().getYMin();
274
      dCellSize = m_Window[iLayer].getLayerGridExtent().getCellSize();
275

    
276
      m_iMinX = (int) Math.floor((rect.getMinX() - dMinX) / dCellSize);
277
      iMaxX = Math.ceil((rect.getMaxX() - dMinX) / dCellSize);
278
      m_iMinY = (int) Math.floor((rect.getMinY() - dMinY) / dCellSize);
279
      iMaxY = Math.ceil((rect.getMaxY() - dMinY) / dCellSize);
280

    
281
      dMinX2 = dMinX + m_iMinX * dCellSize;
282
      dMinY2 = dMinY + m_iMinY * dCellSize;
283
      dMaxX2 = dMinX + iMaxX * dCellSize;
284
      dMaxY2 = dMinY + iMaxY * dCellSize;
285

    
286
      ge.setCellSize(dCellSize);
287
      ge.setXRange(dMinX2, dMaxX2, true);
288
      ge.setYRange(dMinY2, dMaxY2, true);
289

    
290
      return ge;
291

    
292
   }
293

    
294

    
295
   private void doPolygonPart(final Geometry geom,
296
                              final ArrayList stats) {
297

    
298
      boolean bFill;
299
      boolean bCrossing[];
300
      int iNX, iNY;
301
      int i;
302
      int x, y, ix, xStart, xStop;
303
      int iPoint;
304
      double yPos;
305
      double dValue;
306
      AnalysisExtent ge;
307
      SimpleStats substats;
308
      Coordinate pLeft, pRight, pa, pb, p;
309

    
310
      final Envelope extent = geom.getEnvelopeInternal();
311

    
312
      final Coordinate[] points = geom.getCoordinates();
313

    
314
      for (i = 0; i < m_Window.length; i++) {
315
         p = new Coordinate();
316
         substats = (SimpleStats) stats.get(i);
317
         ge = m_Window[i].getWindowGridExtent();
318
         iNX = ge.getNX();
319
         iNY = ge.getNY();
320
         bCrossing = new boolean[iNX];
321

    
322
         xStart = (int) ((extent.getMinX() - ge.getXMin()) / ge.getCellSize()) - 1;
323
         if (xStart < 0) {
324
            xStart = 0;
325
         }
326

    
327
         xStop = (int) ((extent.getMaxX() - ge.getXMin()) / ge.getCellSize()) + 1;
328
         if (xStop >= iNX) {
329
            xStop = iNX - 1;
330
         }
331

    
332
         for (y = 0, yPos = ge.getYMax(); y < iNY; y++, yPos -= ge.getCellSize()) {
333
            if ((yPos >= extent.getMinY()) && (yPos <= extent.getMaxY())) {
334
               Arrays.fill(bCrossing, false);
335
               pLeft = new Coordinate(ge.getXMin() - 1.0, yPos);
336
               pRight = new Coordinate(ge.getXMax() + 1.0, yPos);
337

    
338
               pb = points[points.length - 1];
339

    
340
               for (iPoint = 0; iPoint < points.length; iPoint++) {
341
                  pa = pb;
342
                  pb = points[iPoint];
343

    
344
                  if ((((pa.y <= yPos) && (yPos < pb.y)) || ((pa.y > yPos) && (yPos >= pb.y)))) {
345
                     getCrossing(p, pa, pb, pLeft, pRight);
346

    
347
                     ix = (int) ((p.x - ge.getXMin()) / ge.getCellSize() + 1.0);
348

    
349
                     if (ix < 0) {
350
                        ix = 0;
351
                     }
352
                     else if (ix >= iNX) {
353
                        ix = iNX - 1;
354
                     }
355

    
356
                     bCrossing[ix] = !bCrossing[ix];
357
                  }
358
               }
359

    
360
               for (x = xStart, bFill = false; x <= xStop; x++) {
361
                  if (bCrossing[x]) {
362
                     bFill = !bFill;
363
                  }
364
                  if (bFill) {
365
                     dValue = m_Window[i].getCellValueAsDouble(x /*+ m_iMinX - 1*/, y /*+ m_iMinY - 1*/);
366
                     if (!m_Window[i].isNoDataValue(dValue)) {
367
                        substats.addValue(dValue);
368
                     }
369
                  }
370
               }
371
            }
372
         }
373
      }
374

    
375
   }
376

    
377

    
378
   private void doParalellpiped() {
379

    
380
      int iNX, iNY;
381
      int x, y;
382
      int iMatchingClass = 0;
383
      int iClass, iGrid;
384
      final double dMean[][] = new double[m_Classes.size()][m_Window.length];
385
      final double dStdDev[][] = new double[m_Classes.size()][m_Window.length];
386
      double dValue;
387
      ArrayList stats;
388
      SimpleStats substats;
389
      Set set;
390
      Iterator iter;
391

    
392
      iNX = m_Output.getWindowGridExtent().getNX();
393
      iNY = m_Output.getWindowGridExtent().getNY();
394

    
395
      set = m_Classes.keySet();
396
      iter = set.iterator();
397
      iClass = 0;
398
      while (iter.hasNext()) {
399
         stats = (ArrayList) m_Classes.get(iter.next());
400
         for (iGrid = 0; iGrid < m_Window.length; iGrid++) {
401
            substats = ((SimpleStats) stats.get(iGrid));
402
            dMean[iClass][iGrid] = substats.getMean();
403
            dStdDev[iClass][iGrid] = Math.sqrt(substats.getVariance());
404
         }
405
         iClass++;
406
      }
407

    
408
      for (y = 0; y < iNY; y++) {
409
         for (x = 0; x < iNX; x++) {
410
            for (iClass = 0; iClass < m_Classes.size(); iClass++) {
411
               iMatchingClass = iClass;
412
               for (iGrid = 0; iGrid < m_Window.length; iGrid++) {
413
                  dValue = m_Window[iGrid].getCellValueAsDouble(x, y);
414
                  if (!m_Window[iGrid].isNoDataValue(dValue)) {
415
                     if (Math.abs(m_Window[iGrid].getCellValueAsDouble(x, y) - dMean[iClass][iGrid]) > dStdDev[iClass][iGrid]) {
416
                        iMatchingClass = -1;
417
                        break;
418
                     }
419
                  }
420
                  else {
421
                     break;
422
                  }
423
               }
424
               if (iMatchingClass != -1) {
425
                  break;
426
               }
427
            }
428
            if (iMatchingClass != -1) {
429
               m_Output.setCellValue(x, y, iMatchingClass + 1);
430
            }
431
            else {
432
               m_Output.setNoData(x, y);
433
            }
434
         }
435
      }
436

    
437
   }
438

    
439

    
440
   private void doMinimumDistance() {
441

    
442
      int iNX, iNY;
443
      int x, y;
444
      int iClass, iGrid, iMin = 0;
445
      final double dMean[][] = new double[m_Classes.size()][m_Window.length];
446
      double dMin, d, e;
447
      double dValue;
448
      ArrayList stats;
449
      Set set;
450
      Iterator iter;
451

    
452
      iNX = m_Output.getWindowGridExtent().getNX();
453
      iNY = m_Output.getWindowGridExtent().getNY();
454

    
455
      set = m_Classes.keySet();
456
      iter = set.iterator();
457
      iClass = 0;
458
      while (iter.hasNext()) {
459
         stats = (ArrayList) m_Classes.get(iter.next());
460
         for (iGrid = 0; iGrid < m_Window.length; iGrid++) {
461
            dMean[iClass][iGrid] = ((SimpleStats) stats.get(iGrid)).getMean();
462
         }
463
         iClass++;
464
      }
465

    
466
      for (y = 0; y < iNY; y++) {
467
         for (x = 0; x < iNX; x++) {
468
            for (iClass = 0, dMin = -1.0; iClass < m_Classes.size(); iClass++) {
469
               for (iGrid = 0, d = 0.0; iGrid < m_Window.length; iGrid++) {
470
                  dValue = m_Window[iGrid].getCellValueAsDouble(x, y);
471
                  if (!m_Window[iGrid].isNoDataValue(dValue)) {
472
                     e = m_Window[iGrid].getCellValueAsDouble(x, y) - dMean[iClass][iGrid];
473
                     d += e * e;
474
                     if ((dMin < 0.0) || (dMin > d)) {
475
                        dMin = d;
476
                        iMin = iClass;
477
                     }
478
                  }
479
                  else {
480
                     dMin = -1;
481
                  }
482
               }
483
            }
484

    
485
            if (dMin >= 0.0) {
486
               m_Output.setCellValue(x, y, iMin + 1);
487
            }
488
            else {
489
               m_Output.setNoData(x, y);
490
            }
491
         }
492
      }
493

    
494
   }
495

    
496

    
497
   private void doMaximumLikelihood() {
498

    
499
      int iNX, iNY;
500
      int x, y;
501
      int iClass, iGrid, iMax = 0;
502
      final double dMean[][] = new double[m_Classes.size()][m_Window.length];
503
      final double dStdDev[][] = new double[m_Classes.size()][m_Window.length];
504
      final double dK[][] = new double[m_Classes.size()][m_Window.length];
505
      double dMax, d, e;
506
      double dValue;
507
      ArrayList stats;
508
      SimpleStats substats;
509
      Set set;
510
      Iterator iter;
511

    
512
      iNX = m_Output.getWindowGridExtent().getNX();
513
      iNY = m_Output.getWindowGridExtent().getNY();
514

    
515
      set = m_Classes.keySet();
516
      iter = set.iterator();
517
      iClass = 0;
518
      while (iter.hasNext()) {
519
         stats = (ArrayList) m_Classes.get(iter.next());
520
         for (iGrid = 0; iGrid < m_Window.length; iGrid++) {
521
            substats = ((SimpleStats) stats.get(iGrid));
522
            dMean[iClass][iGrid] = substats.getMean();
523
            dStdDev[iClass][iGrid] = Math.sqrt(substats.getVariance());
524
            dK[iClass][iGrid] = 1.0 / (dStdDev[iClass][iGrid] * Math.sqrt(2.0 * Math.PI));
525
         }
526
         iClass++;
527
      }
528

    
529
      for (y = 0; y < iNY; y++) {
530
         for (x = 0; x < iNX; x++) {
531
            for (iClass = 0, dMax = 0.0; iClass < m_Classes.size(); iClass++) {
532
               for (iGrid = 0, d = 0.0; iGrid < m_Window.length; iGrid++) {
533
                  dValue = m_Window[iGrid].getCellValueAsDouble(x, y);
534
                  if (!m_Window[iGrid].isNoDataValue(dValue)) {
535
                     e = (m_Window[iGrid].getCellValueAsDouble(x, y) - dMean[iClass][iGrid]) / dStdDev[iClass][iGrid];
536
                     e = dK[iClass][iGrid] * Math.exp(-0.5 * e * e);
537
                     d += e * e;
538
                     if (dMax < d) {
539
                        dMax = d;
540
                        iMax = iClass;
541
                     }
542
                  }
543
                  else {
544
                     dMax = -1;
545
                  }
546
               }
547
            }
548

    
549
            if (dMax > 0.0) {
550
               m_Output.setCellValue(x, y, iMax + 1);
551
            }
552
            else {
553
               m_Output.setNoData(x, y);
554
            }
555
         }
556
      }
557

    
558
   }
559

    
560

    
561
}