Statistics
| Revision:

root / org.gvsig.toolbox / trunk / org.gvsig.toolbox / org.gvsig.toolbox.algorithm / src / main / java / es / unex / sextante / locate / locateAllocateB / LocateAllocateBAlgorithm.java @ 59

History | View | Annotate | Download (21.3 KB)

1
package es.unex.sextante.locate.locateAllocateB;
2

    
3
import java.text.DecimalFormat;
4
import java.util.ArrayList;
5

    
6
import com.vividsolutions.jts.geom.Coordinate;
7
import com.vividsolutions.jts.geom.GeometryFactory;
8
import com.vividsolutions.jts.geom.LineString;
9

    
10
import es.unex.sextante.additionalInfo.AdditionalInfoNumericalValue;
11
import es.unex.sextante.additionalInfo.AdditionalInfoVectorLayer;
12
import es.unex.sextante.core.GeoAlgorithm;
13
import es.unex.sextante.core.Sextante;
14
import es.unex.sextante.dataObjects.IFeature;
15
import es.unex.sextante.dataObjects.IFeatureIterator;
16
import es.unex.sextante.dataObjects.IRecord;
17
import es.unex.sextante.dataObjects.IRecordsetIterator;
18
import es.unex.sextante.dataObjects.ITable;
19
import es.unex.sextante.dataObjects.IVectorLayer;
20
import es.unex.sextante.docEngines.html.HTMLDoc;
21
import es.unex.sextante.exceptions.GeoAlgorithmExecutionException;
22
import es.unex.sextante.exceptions.OptionalParentParameterException;
23
import es.unex.sextante.exceptions.RepeatedParameterNameException;
24
import es.unex.sextante.exceptions.UndefinedParentParameterNameException;
25
import es.unex.sextante.math.simpleStats.SimpleStats;
26
import es.unex.sextante.outputs.OutputVectorLayer;
27

    
28
public class LocateAllocateBAlgorithm
29
         extends
30
            GeoAlgorithm {
31

    
32
   public static final int    EUCLIDEAN                        = 0;
33

    
34
   public static final int    MINSUM                           = 0;
35
   public static final int    CONSTRAINEDMINSUM                = 1;
36
   public static final int    MINSTDDEV                        = 2;
37
   public static final int    MINIMAX                          = 3;
38
   public static final int    MAXCOV                           = 4;
39
   public static final int    MAXSUM                           = 5;
40
   public static final int    MINCOV                           = 6;
41

    
42
   public static final String MAXDIST                          = "MAXDIST";
43
   public static final String METHOD                           = "METHOD";
44
   public static final String NEWLOCATIONS                     = "NEWLOCATIONS";
45
   public static final String CANDIDATES                       = "CANDIDATES";
46
   public static final String OFFER                            = "OFFER";
47
   public static final String FIELDDEMAND                      = "FIELDDEMAND";
48
   public static final String DEMAND                           = "DEMAND";
49
   public static final String DEMAND_OFFER_DISTANCE_TABLE      = "DEMAND_OFFER_DISTANCE_TABLE";
50
   public static final String DEMAND_CANDIDATES_DISTANCE_TABLE = "DEMAND_CANDIDATES_DISTANCE_TABLE";
51
   public static final String SPIDER                           = "SPIDER";
52
   public static final String RESULT                           = "RESULT";
53

    
54
   private static final int   ITERATIONS                       = 100;
55

    
56
   private ArrayList          m_Demand;
57
   private ArrayList          m_Candidates;
58
   private ArrayList          m_Offer;
59
   private double             m_dDist[][];
60
   private double             m_dMaxDist;
61
   private double             m_dObjective;
62
   private double             m_dBestObjective;
63
   private int                m_iNewLocations;
64
   private int[]              m_iSolution;
65
   private int[]              m_iBestSolution;
66
   private int                m_iOffer;
67
   private int                m_iTotalOffer;
68
   private int                m_iMethod;
69
   private int                m_iDemandField;
70
   private IVectorLayer       m_DemandLayer;
71
   private IVectorLayer       m_OfferLayer;
72
   private IVectorLayer       m_CandidatesLayer;
73
   private ITable             m_DemandOfferDistanceTable;
74
   private ITable             m_DemandCandidatesDistanceTable;
75

    
76

    
77
   @Override
78
   public void defineCharacteristics() {
79

    
80
      final String sMethod[] = { Sextante.getText("Minimum_sum"), Sextante.getText("Minimum_sum_with_restrictions"),
81
               Sextante.getText("Minimum_standard_deviation"), Sextante.getText("Minimize_maximum_distance"),
82
               Sextante.getText("Maximum_coverage"), Sextante.getText("Maximum_sum"), Sextante.getText("Minimum_coverage") };
83

    
84
      setName(Sextante.getText("Location-allocation_table_distance"));
85
      setGroup(Sextante.getText("Location-allocation"));
86
      setUserCanDefineAnalysisExtent(false);
87
      setIsDeterminatedProcess(false);
88
      try {
89
         m_Parameters.addInputVectorLayer(DEMAND, Sextante.getText("Demand_points"), AdditionalInfoVectorLayer.SHAPE_TYPE_POINT,
90
                  true);
91
         m_Parameters.addTableField(FIELDDEMAND, Sextante.getText("Field"), DEMAND);
92
         m_Parameters.addInputVectorLayer(OFFER, Sextante.getText("Preexistent_resources"),
93
                  AdditionalInfoVectorLayer.SHAPE_TYPE_POINT, false);
94
         m_Parameters.addInputVectorLayer(CANDIDATES, Sextante.getText("Candidate_points"),
95
                  AdditionalInfoVectorLayer.SHAPE_TYPE_POINT, true);
96
         m_Parameters.addInputTable(DEMAND_OFFER_DISTANCE_TABLE, Sextante.getText("Demand_offer_distance_table"), true);
97
         m_Parameters.addInputTable(DEMAND_CANDIDATES_DISTANCE_TABLE, Sextante.getText("Demand_candidates_distance_table"), true);
98
         m_Parameters.addSelection(METHOD, Sextante.getText("Method"), sMethod);
99
         m_Parameters.addNumericalValue(NEWLOCATIONS, Sextante.getText("Number_of_resources_to_allocate"),
100
                  AdditionalInfoNumericalValue.NUMERICAL_VALUE_INTEGER, 1, 1, Integer.MAX_VALUE);
101
         m_Parameters.addNumericalValue(MAXDIST, Sextante.getText("Maximum_distance"),
102
                  AdditionalInfoNumericalValue.NUMERICAL_VALUE_DOUBLE, 1, 0, Double.MAX_VALUE);
103
         addOutputVectorLayer(SPIDER, Sextante.getText("Connections"), OutputVectorLayer.SHAPE_TYPE_LINE);
104
         addOutputText(RESULT, Sextante.getText("Statistics"));
105
      }
106
      catch (final RepeatedParameterNameException e) {
107
         Sextante.addErrorToLog(e);
108
      }
109
      catch (final UndefinedParentParameterNameException e) {
110
         Sextante.addErrorToLog(e);
111
      }
112
      catch (final OptionalParentParameterException e) {
113
         Sextante.addErrorToLog(e);
114
      }
115

    
116
   }
117

    
118

    
119
   @Override
120
   public boolean processAlgorithm() throws GeoAlgorithmExecutionException {
121

    
122
      m_DemandCandidatesDistanceTable = m_Parameters.getParameterValueAsTable(DEMAND_CANDIDATES_DISTANCE_TABLE);
123
      m_DemandOfferDistanceTable = m_Parameters.getParameterValueAsTable(DEMAND_OFFER_DISTANCE_TABLE);
124
      m_DemandLayer = m_Parameters.getParameterValueAsVectorLayer(DEMAND);
125
      m_iDemandField = m_Parameters.getParameterValueAsInt(FIELDDEMAND);
126
      m_OfferLayer = m_Parameters.getParameterValueAsVectorLayer(OFFER);
127
      m_CandidatesLayer = m_Parameters.getParameterValueAsVectorLayer(CANDIDATES);
128
      m_iNewLocations = m_Parameters.getParameterValueAsInt(NEWLOCATIONS);
129
      m_iMethod = m_Parameters.getParameterValueAsInt(METHOD);
130
      m_dMaxDist = m_Parameters.getParameterValueAsDouble(MAXDIST);
131

    
132
      if (m_iNewLocations == 0) {
133
         return false;
134
      }
135

    
136
      extractPoints();
137
      calculateDistanceMatrix();
138

    
139
      m_dBestObjective = Double.MAX_VALUE;
140
      for (int i = 0; (i < ITERATIONS) && setProgress(i, ITERATIONS); i++) {
141
         setProgressText(Sextante.getText("Iteration") + " " + Integer.toString(i) + "/" + Integer.toString(ITERATIONS));
142
         runTeitzBart();
143
         if (m_dObjective < m_dBestObjective) {
144
            m_dBestObjective = m_dObjective;
145
            m_iBestSolution = m_iSolution;
146
         }
147
      }
148

    
149
      if (m_Task.isCanceled()) {
150
         return false;
151
      }
152
      else {
153
         if (m_iBestSolution != null) {
154
            createResults();
155
         }
156
         return true;
157
      }
158

    
159
   }
160

    
161

    
162
   private void extractPoints() throws GeoAlgorithmExecutionException {
163

    
164
      double dWeight;
165

    
166
      m_Demand = new ArrayList();
167
      m_Offer = new ArrayList();
168
      m_Candidates = new ArrayList();
169

    
170
      IFeatureIterator iter = m_DemandLayer.iterator();
171
      while (iter.hasNext()) {
172
         final IFeature feature = iter.next();
173
         final Coordinate coord = feature.getGeometry().getCoordinate();
174
         try {
175
            dWeight = Double.parseDouble(feature.getRecord().getValue(m_iDemandField).toString());
176
         }
177
         catch (final Exception e) {
178
            dWeight = 1;
179
         }
180
         m_Demand.add(new DemandPoint(coord.x, coord.y, dWeight));
181
      }
182
      iter.close();
183

    
184
      iter = m_CandidatesLayer.iterator();
185
      while (iter.hasNext()) {
186
         final IFeature feature = iter.next();
187
         final Coordinate coord = feature.getGeometry().getCoordinate();
188
         m_Candidates.add(coord);
189
      }
190
      iter.close();
191

    
192
      if (m_OfferLayer != null) {
193
         iter = m_OfferLayer.iterator();
194
         while (iter.hasNext()) {
195
            final IFeature feature = iter.next();
196
            final Coordinate coord = feature.getGeometry().getCoordinate();
197
            m_Offer.add(coord);
198
         }
199
         iter.close();
200
      }
201

    
202
      if ((m_Candidates.size() < m_iNewLocations) || (m_Demand.size() == 0)) {
203
         throw new GeoAlgorithmExecutionException(Sextante.getText("Invalid_or_insufficient_data"));
204
      }
205

    
206
   }
207

    
208

    
209
   private void calculateDistanceMatrix() throws GeoAlgorithmExecutionException {
210

    
211
      m_iOffer = m_Offer.size();
212
      m_iTotalOffer = m_Offer.size() + m_Candidates.size();
213

    
214
      m_dDist = new double[m_Demand.size()][m_iTotalOffer];
215

    
216
      IRecordsetIterator iter = m_DemandOfferDistanceTable.iterator();
217
      int iDemand = 0;
218
      while (iter.hasNext()) {
219
         final IRecord record = iter.next();
220
         for (int j = 0; j < m_iOffer; j++) {
221
            final String sValue = record.getValue(j).toString();
222
            try {
223
               m_dDist[iDemand][j] = Double.parseDouble(sValue);
224
            }
225
            catch (final Exception e) {
226
               throw new GeoAlgorithmExecutionException(Sextante.getText("Wrong_data_in_table"));
227
            }
228
            iDemand++;
229
         }
230
      }
231
      iter.close();
232

    
233
      iter = m_DemandCandidatesDistanceTable.iterator();
234
      iDemand = 0;
235
      while (iter.hasNext()) {
236
         final IRecord record = iter.next();
237
         for (int j = 0; j < m_Candidates.size(); j++) {
238
            final String sValue = record.getValue(j).toString();
239
            try {
240
               m_dDist[iDemand][j + m_iOffer] = Double.parseDouble(sValue);
241
            }
242
            catch (final Exception e) {
243
               throw new GeoAlgorithmExecutionException(Sextante.getText("Wrong_data_in_table"));
244
            }
245
            iDemand++;
246
         }
247
      }
248
      iter.close();
249

    
250
   }
251

    
252

    
253
   private void runTeitzBart() {
254

    
255
      int i, j;
256
      int iAlternative = 0;
257
      int iOldValue;
258
      int iValueToChange = 0;
259
      int iRandCount, iCount;
260
      double dMin;
261
      boolean bHasImproved;
262
      boolean bIsSelected;
263
      m_iSolution = new int[m_iNewLocations];
264

    
265
      for (i = 0; i < m_iSolution.length; i++) {
266
         m_iSolution[i] = i;
267
      }
268

    
269
      do {
270
         bHasImproved = false;
271
         dMin = getObjectiveFunctionValue();
272
         iCount = 0;
273
         iRandCount = (int) Math.floor(Math.random() * (m_Candidates.size() - m_iSolution.length));
274
         for (i = 0; i < m_Candidates.size(); i++) {
275
            bIsSelected = false;
276
            for (j = 0; j < m_iSolution.length; j++) {
277
               if (m_iSolution[j] == i) {
278
                  bIsSelected = true;
279
                  break;
280
               }
281
            }
282
            if (!bIsSelected) {
283
               if (iRandCount == iCount) {
284
                  iAlternative = i;
285
                  break;
286
               }
287
               else {
288
                  iCount++;
289
               }
290
            }
291
         }
292
         for (i = 0; i < m_iSolution.length; i++) {
293
            iOldValue = m_iSolution[i];
294
            m_iSolution[i] = iAlternative;
295
            m_dObjective = getObjectiveFunctionValue();
296
            if (m_dObjective < dMin) {
297
               bHasImproved = true;
298
               dMin = m_dObjective;
299
               iValueToChange = i;
300
            }
301
            m_iSolution[i] = iOldValue;
302
         }
303
         if (bHasImproved) {
304
            m_iSolution[iValueToChange] = iAlternative;
305
         }
306
      }
307
      while (bHasImproved);
308

    
309
   }
310

    
311

    
312
   private double getObjectiveFunctionValue() {
313

    
314
      switch (m_iMethod) {
315
         case MINSUM:
316
         default:
317
            return getObjectiveFunctionValueMinSum();
318
         case CONSTRAINEDMINSUM:
319
            return getObjectiveFunctionValueConstrainedMinSum();
320
         case MINSTDDEV:
321
            return getObjectiveFunctionValueMinStdDev();
322
         case MAXSUM:
323
            return -getObjectiveFunctionValueMinSum();
324
         case MAXCOV:
325
            return getObjectiveFunctionValueMaxCov();
326
         case MINCOV:
327
            return -getObjectiveFunctionValueMaxCov();
328
         case MINIMAX:
329
            return getObjectiveFunctionValueMinMaxDistance();
330
      }
331

    
332
   }
333

    
334

    
335
   private double getObjectiveFunctionValueMinStdDev() {
336

    
337
      int i, j;
338
      int iPt;
339
      double dMin;
340
      double dSum = 0;
341
      double dVar = 0;
342

    
343

    
344
      for (i = 0; i < m_Demand.size(); i++) {
345
         dMin = Double.MAX_VALUE;
346
         for (j = 0; j < m_iOffer; j++) {
347
            if (m_dDist[i][j] < dMin) {
348
               dMin = m_dDist[i][j];
349
            }
350
         }
351
         for (j = 0; j < m_iSolution.length; j++) {
352
            iPt = m_iSolution[j];
353
            if (m_dDist[i][iPt + m_iOffer] < dMin) {
354
               dMin = m_dDist[i][iPt + m_iOffer];
355
            }
356
         }
357
         //TODO dpt = (DemandPoint) m_Demand.get(i); �NO CONSIDERAMOS PESOS????
358

    
359
         dSum += dMin;
360
         dVar += dMin * dMin;
361

    
362
      }
363

    
364
      dSum /= m_Demand.size();
365
      dVar = dVar / m_Demand.size() - dSum * dSum;
366

    
367
      return dVar;
368

    
369
   }
370

    
371

    
372
   private double getObjectiveFunctionValueConstrainedMinSum() {
373

    
374
      int i, j;
375
      int iPt;
376
      double dValue = 0;
377
      double dMin;
378
      DemandPoint dpt;
379

    
380
      for (i = 0; i < m_Demand.size(); i++) {
381
         dMin = Double.MAX_VALUE;
382
         for (j = 0; j < m_iOffer; j++) {
383
            if (m_dDist[i][j] < dMin) {
384
               dMin = m_dDist[i][j];
385
            }
386
         }
387
         for (j = 0; j < m_iSolution.length; j++) {
388
            iPt = m_iSolution[j];
389
            if (m_dDist[i][iPt + m_iOffer] < dMin) {
390
               dMin = m_dDist[i][iPt + m_iOffer];
391
            }
392
         }
393
         if (dMin < m_dMaxDist) {
394
            dpt = (DemandPoint) m_Demand.get(i);
395
            dValue += (dMin * dpt.weight);
396
         }
397
         else {
398
            dValue = Double.POSITIVE_INFINITY;
399
         }
400

    
401
      }
402

    
403
      return dValue;
404

    
405
   }
406

    
407

    
408
   private double getObjectiveFunctionValueMaxCov() {
409

    
410
      int i, j;
411
      int iPt;
412
      double dValue = 0;
413
      double dDemand;;
414
      DemandPoint dpt;
415

    
416
      for (i = 0; i < m_Demand.size(); i++) {
417
         dpt = (DemandPoint) m_Demand.get(i);
418
         dDemand = dpt.weight;
419
         for (j = 0; j < m_iOffer; j++) {
420
            if (m_dDist[i][j] < m_dMaxDist) {
421
               dDemand = 0;
422
               break;
423
            }
424
         }
425
         if (dDemand != 0) {
426
            for (j = 0; j < m_iSolution.length; j++) {
427
               iPt = m_iSolution[j];
428
               if (m_dDist[i][iPt + m_iOffer] < m_dMaxDist) {
429
                  dDemand = 0;
430
                  break;
431
               }
432
            }
433
         }
434

    
435
         dValue += dDemand;
436
      }
437

    
438
      return dValue;
439

    
440
   }
441

    
442

    
443
   private double getObjectiveFunctionValueMinSum() {
444

    
445
      int i, j;
446
      int iPt;
447
      double dValue = 0;
448
      double dMin;
449
      DemandPoint dpt;
450

    
451
      for (i = 0; i < m_Demand.size(); i++) {
452
         dMin = Double.MAX_VALUE;
453
         for (j = 0; j < m_iOffer; j++) {
454
            if (m_dDist[i][j] < dMin) {
455
               dMin = m_dDist[i][j];
456
            }
457
         }
458
         for (j = 0; j < m_iSolution.length; j++) {
459
            iPt = m_iSolution[j];
460
            if (m_dDist[i][iPt + m_iOffer] < dMin) {
461
               dMin = m_dDist[i][iPt + m_iOffer];
462
            }
463
         }
464
         dpt = (DemandPoint) m_Demand.get(i);
465
         dValue += (dMin * dpt.weight);
466
      }
467

    
468
      return dValue;
469

    
470
   }
471

    
472

    
473
   private double getObjectiveFunctionValueMinMaxDistance() {
474

    
475
      int i, j;
476
      int iPt;
477
      double dMin, dMax = Double.NEGATIVE_INFINITY;
478

    
479
      for (i = 0; i < m_Demand.size(); i++) {
480
         dMin = Double.MAX_VALUE;
481
         for (j = 0; j < m_iOffer; j++) {
482
            if (m_dDist[i][j] < dMin) {
483
               dMin = m_dDist[i][j];
484
            }
485
         }
486
         for (j = 0; j < m_iSolution.length; j++) {
487
            iPt = m_iSolution[j];
488
            if (m_dDist[i][iPt + m_iOffer] < dMin) {
489
               dMin = m_dDist[i][iPt + m_iOffer];
490
            }
491
         }
492
         if (dMax < dMin) {
493
            dMax = dMin;
494
         }
495
      }
496

    
497
      return dMax;
498

    
499
   }
500

    
501

    
502
   private void createResults() throws GeoAlgorithmExecutionException {
503

    
504
      int i, j;
505
      int iPt;
506
      int iID = 1;
507
      double dMin;
508
      double dDist;
509
      DemandPoint dpt;
510
      Coordinate pt = null;
511
      LineString line;
512
      final Coordinate coords[] = new Coordinate[2];
513
      final Object values[] = new Object[8];
514
      String sOfferName = null;
515
      final String[] sNames = { Sextante.getText("ID"), Sextante.getText("Demand_point"), Sextante.getText("X_demand"),
516
               Sextante.getText("Y_demand"), Sextante.getText("Resource"), Sextante.getText("X_resource"),
517
               Sextante.getText("Y_resource"), Sextante.getText("Distance") };
518
      final Class[] types = { Integer.class, String.class, Double.class, Double.class, String.class, Double.class, Double.class,
519
               Double.class };
520
      final SimpleStats stats = new SimpleStats();
521
      final IVectorLayer spiderWebLayer = getNewVectorLayer(SPIDER, Sextante.getText("Conexions"), IVectorLayer.SHAPE_TYPE_LINE,
522
               types, sNames);
523
      final IVectorLayer newPointsLayer = getNewVectorLayer(RESULT, Sextante.getText("Selected_points"),
524
               IVectorLayer.SHAPE_TYPE_POINT, new Class[] { Integer.class }, new String[] { "ID" });
525

    
526
      final GeometryFactory gf = new GeometryFactory();
527

    
528
      for (i = 0; i < m_iBestSolution.length; i++) {
529
         iPt = m_iBestSolution[i];
530
         pt = (Coordinate) m_Candidates.get(iPt);
531
         newPointsLayer.addFeature(gf.createPoint(pt), new Object[] { new Integer(i) });
532
      }
533

    
534

    
535
      for (i = 0; i < m_Demand.size(); i++) {
536
         dpt = (DemandPoint) m_Demand.get(i);
537
         dMin = Double.MAX_VALUE;
538
         for (j = 0; j < m_iOffer; j++) {
539
            if (m_dDist[i][j] < dMin) {
540
               dMin = m_dDist[i][j];
541
               pt = (Coordinate) m_Offer.get(j);
542
               sOfferName = Sextante.getText("Preexistent") + Integer.toString(j);
543
            }
544
         }
545
         for (j = 0; j < m_iBestSolution.length; j++) {
546
            iPt = m_iBestSolution[j];
547
            if (m_dDist[i][iPt + m_iOffer] < dMin) {
548
               dMin = m_dDist[i][iPt + m_iOffer];
549
               pt = (Coordinate) m_Candidates.get(iPt);
550
               sOfferName = Sextante.getText("Candidate") + Integer.toString(iPt);
551
            }
552
         }
553

    
554
         stats.addValue(dMin);
555
         coords[0] = pt;
556
         coords[1] = new Coordinate(dpt.x, dpt.y);
557
         line = gf.createLineString(coords);
558
         values[0] = new Integer(iID++);
559
         values[1] = Integer.toString(i);
560
         values[2] = new Double(dpt.x);
561
         values[3] = new Double(dpt.y);
562
         values[4] = sOfferName;
563
         values[5] = new Double(pt.x);
564
         values[6] = new Double(pt.y);
565
         dDist = Math.sqrt(Math.pow(pt.x - dpt.x, 2) + Math.pow(pt.y - dpt.y, 2));
566
         values[7] = new Double(dDist);
567
         spiderWebLayer.addFeature(line, values);
568

    
569
      }
570

    
571
      final DecimalFormat df = new DecimalFormat("##.###");
572
      final HTMLDoc doc = new HTMLDoc();
573
      doc.open(Sextante.getText("Result"));
574
      doc.addHeader(Sextante.getText("Statistics_of_global_solution"), 2);
575
      doc.startUnorderedList();
576
      doc.addListElement(Sextante.getText("Objective_function") + ": " + df.format(Math.abs(m_dObjective)));
577
      doc.addListElement(Sextante.getText("Mean_distance") + ": " + df.format(stats.getMean()));
578
      doc.addListElement(Sextante.getText("Mean_squared_distance") + ": " + df.format(stats.getRMS()));
579
      doc.addListElement(Sextante.getText("Min_distance") + ": " + df.format(stats.getMin()));
580
      doc.addListElement(Sextante.getText("Maximum_distance") + ": " + df.format(stats.getMax()));
581
      doc.addListElement(Sextante.getText("Variance") + ": " + df.format(stats.getVariance()));
582
      doc.addListElement(Sextante.getText("Sum_of_distances") + ": " + df.format(stats.getSum()));
583
      doc.addListElement(Sextante.getText("Coefficient_of_variation") + ": " + df.format(stats.getCoeffOfVar()));
584
      doc.closeUnorderedList();
585
      doc.close();
586

    
587
      addOutputText(RESULT, Sextante.getText("Statistics"), doc.getHTMLCode());
588

    
589
   }
590

    
591
   private class DemandPoint {
592

    
593
      public double x;
594
      public double y;
595
      public double weight;
596

    
597

    
598
      DemandPoint(final double dX,
599
                  final double dY,
600
                  final double dWeight) {
601

    
602
         x = dX;
603
         y = dY;
604
         weight = dWeight;
605

    
606
      }
607
   }
608
}