Statistics
| Revision:

root / trunk / extensions / extGeoProcessing / src / com / iver / cit / gvsig / geoprocess / impl / dissolve / fmap / FeatureDissolver.java @ 6497

History | View | Annotate | Download (18.8 KB)

1 5918 azabala
/*
2
 * Created on 12-may-2006
3
 *
4
 * gvSIG. Sistema de Informaci?n Geogr?fica de la Generalitat Valenciana
5
 *
6
 * Copyright (C) 2004 IVER T.I. and Generalitat Valenciana.
7
 *
8
 * This program is free software; you can redistribute it and/or
9
 * modify it under the terms of the GNU General Public License
10
 * as published by the Free Software Foundation; either version 2
11
 * of the License, or (at your option) any later version.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU General Public License
19
 * along with this program; if not, write to the Free Software
20
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,USA.
21
 *
22
 * For more information, contact:
23
 *
24
 *  Generalitat Valenciana
25
 *   Conselleria d'Infraestructures i Transport
26
 *   Av. Blasco Ib??ez, 50
27
 *   46010 VALENCIA
28
 *   SPAIN
29
 *
30
 *      +34 963862235
31
 *   gvsig@gva.es
32
 *      www.gvsig.gva.es
33
 *
34
 *    or
35
 *
36
 *   IVER T.I. S.A
37
 *   Salamanca 50
38
 *   46005 Valencia
39
 *   Spain
40
 *
41
 *   +34 963163400
42
 *   dac@iver.es
43
 */
44
/* CVS MESSAGES:
45
 *
46
 * $Id$
47
 * $Log$
48
 * Revision 1.1  2006-06-20 18:20:45  azabala
49
 * first version in cvs
50
 *
51
 * Revision 1.2  2006/06/02 18:21:28  azabala
52
 * *** empty log message ***
53
 *
54
 * Revision 1.1  2006/05/24 21:11:14  azabala
55
 * primera version en cvs despues de refactoring orientado a crear un framework extensible de geoprocessing
56
 *
57
 *
58
 */
59
package com.iver.cit.gvsig.geoprocess.impl.dissolve.fmap;
60
61
import java.awt.geom.Rectangle2D;
62
import java.io.IOException;
63
import java.util.ArrayList;
64
import java.util.Enumeration;
65
import java.util.Iterator;
66
import java.util.List;
67
import java.util.Map;
68
import java.util.Stack;
69
70
import org.cresques.cts.ICoordTrans;
71
72
import com.iver.cit.gvsig.fmap.DriverException;
73
import com.iver.cit.gvsig.fmap.core.IFeature;
74
import com.iver.cit.gvsig.fmap.core.IGeometry;
75
import com.iver.cit.gvsig.fmap.core.v02.FConverter;
76
import com.iver.cit.gvsig.fmap.drivers.DriverIOException;
77
import com.iver.cit.gvsig.fmap.edition.EditionException;
78
import com.iver.cit.gvsig.fmap.layers.FBitSet;
79
import com.iver.cit.gvsig.fmap.layers.FLayer;
80
import com.iver.cit.gvsig.fmap.layers.FLyrVect;
81
import com.iver.cit.gvsig.fmap.layers.ReadableVectorial;
82
import com.iver.cit.gvsig.fmap.layers.SelectableDataSource;
83
import com.iver.cit.gvsig.fmap.layers.layerOperations.AlphanumericData;
84
import com.iver.cit.gvsig.fmap.layers.layerOperations.VectorialData;
85
import com.iver.cit.gvsig.fmap.operations.strategies.FeatureVisitor;
86
import com.iver.cit.gvsig.fmap.operations.strategies.Strategy;
87
import com.iver.cit.gvsig.fmap.operations.strategies.StrategyManager;
88
import com.iver.cit.gvsig.fmap.operations.strategies.VisitException;
89
import com.iver.cit.gvsig.geoprocess.core.fmap.FeatureProcessor;
90
import com.iver.cit.gvsig.geoprocess.core.fmap.GeoprocessException;
91
import com.iver.utiles.swing.threads.Cancellable;
92
import com.iver.utiles.swing.threads.CancellableMonitorable;
93
import com.vividsolutions.jts.geom.Geometry;
94
import com.vividsolutions.jts.geom.GeometryCollection;
95
import com.vividsolutions.jts.geom.GeometryFactory;
96
97
/**
98
 * <p>
99
 * Dissolve features in base of a given dissolve criteria.
100
 * </p>
101
 * By 'dissolving' we understand the union of the geometry of many features
102
 *
103
 * @author azabala
104
 *
105
 */
106
public class FeatureDissolver {
107
108
        public static final int ALPHANUMERIC_DISSOLVE = 0;
109
110
        public static final int SPATIAL_DISSOLVE = 1;
111
112
        private int dissolveType = ALPHANUMERIC_DISSOLVE;
113
114
        /**
115
         * Fetches attributes of disolved layer features
116
         */
117
        protected SelectableDataSource recordset;
118
119
        protected ICoordTrans ct;
120
121
        /**
122
         * Is used to do spatial querys (looking for adjacent polygons to visited
123
         * feature geometry
124
         */
125
        protected FLyrVect dissolvedLayer;
126
127
        /**
128
         * It marks all features that have already been dissolved (to avoid process
129
         * them in subsecuent steps)
130
         *
131
         */
132
        protected FBitSet dissolvedGeometries;
133
134
        /**
135
         * Relates a numerical field name with its sumarization functions
136
         */
137
        protected Map numericField_sumarizeFunction;
138
139
        /**
140
         * Processes results of dissolve operations (save them in a file, or cache
141
         * them in memory, etc)
142
         */
143
        protected FeatureProcessor featureProcessor;
144
145
        /**
146
         * Index of the result
147
         */
148
        protected int fid = 0;
149
150
        /**
151
         * It decides if two features must be dissolved, and builds the feature
152
         * resulting of the dissolution
153
         */
154
        protected IDissolveCriteria dissolveCriteria;
155
156
        /**
157
         *
158
         * @param processor
159
         * @param layer
160
         * @throws GeoprocessException
161
         */
162
        public FeatureDissolver(FeatureProcessor processor, FLyrVect layer,
163
                        Map numericField_sumFunction, IDissolveCriteria criteria, int dissolveType)
164
                        throws GeoprocessException {
165
166
                this.featureProcessor = processor;
167
                this.numericField_sumarizeFunction = numericField_sumFunction;
168
                this.dissolveCriteria = criteria;
169
                this.dissolveType = dissolveType;
170
                dissolvedGeometries = new FBitSet();
171
172
                if (layer instanceof AlphanumericData && layer instanceof VectorialData) {
173
                        try {
174
                                dissolvedLayer = (FLyrVect) layer;
175
                                recordset = ((AlphanumericData) layer).getRecordset();
176
                                ct = dissolvedLayer.getCoordTrans();
177
                                featureProcessor.start();
178
                        } catch (com.iver.cit.gvsig.fmap.DriverException e) {
179
                                throw new GeoprocessException(
180
                                                "Error al acceder al recordset de la capa "
181
                                                                + layer.getName(), e);
182
                        } catch (EditionException e) {
183
                                throw new GeoprocessException(
184
                                                "Error al preparar donde se van a escribir los resultados del dissolve de la capa "
185
                                                                + layer.getName(), e);
186
                        }
187
                } else {
188
                        throw new GeoprocessException(
189
                                        "La capa a dissolver debe ser VectorialData y AlphanumericData");
190
                }
191
        }
192
193
        public void setDissolveCriteria(IDissolveCriteria criteria) {
194
                this.dissolveCriteria = criteria;
195
        }
196
197
        public void setDissolvedAttributesInfo(Map numericField_sumFunction) {
198
                this.numericField_sumarizeFunction = numericField_sumFunction;
199
        }
200
201
        public int getNumProcessedGeometries() {
202
                return dissolvedGeometries.cardinality();
203
        }
204
205
        public void dissolve(CancellableMonitorable cancel) throws GeoprocessException {
206
                try {
207
                        ReadableVectorial va = dissolvedLayer.getSource();
208
                        va.start();
209
                        for (int i = 0; i < va.getShapeCount(); i++) {// for each geometry
210
//                                if (cancel != null) {
211
//                                        cancel.reportStep();
212
//                                }
213
                                if (verifyCancelation(cancel, va)) {
214
                                        this.featureProcessor.finish();
215
                                        return;
216
                                }
217
218
219
                                //Ver si podemos optimizar esto de forma que solo
220
                                //se procesasen los elementos no marcados en el bitset
221
                                //(bitset de acceso aleatorio)
222
223
                                if (!dissolvedGeometries.get(i)) {
224
                                        System.out.println("dissolviendo "+i);
225
                                        // if we havent processed this element yet
226
                                        try {
227
                                                if(dissolveType == SPATIAL_DISSOLVE)
228
                                                        process( i, va, cancel);
229
                                                else if(dissolveType == ALPHANUMERIC_DISSOLVE)
230
                                                        processAlphanumeric(i, va, cancel);
231
                                        } catch (DriverException e) {
232
                                                throw new GeoprocessException(
233
                                                                "Error accediendo a datos durante dissolve", e);
234
                                        } catch (VisitException e) {
235
                                                throw new GeoprocessException(
236
                                                                "Error procesando datos durante dissolve", e);
237
                                        } catch (IOException e) {
238
                                                throw new GeoprocessException(
239
                                                                "Error de I/O durante dissolve", e);
240
                                        }
241
                                }// if
242
                        }// for
243
                        va.stop();
244
                        this.featureProcessor.finish();
245
                } catch (DriverIOException e) {
246
                        e.printStackTrace();
247
                }
248
        }
249
250
        /**
251
         * Verifies cancelation events, and return a boolean flag if processes must
252
         * be stopped for this cancelations events.
253
         *
254
         * @param cancel
255
         * @param va
256
         * @param visitor
257
         * @return
258
         * @throws DriverIOException
259
         */
260
        protected boolean verifyCancelation(Cancellable cancel, ReadableVectorial va) {
261
                if (cancel != null) {
262
                        if (cancel.isCanceled()) {
263
                                try {
264
                                        va.stop();
265
                                } finally {
266
                                        return true;
267
                                }
268
                        }
269
                }
270
                return false;
271
        }
272
273
        class DissolveVisitor implements FeatureVisitor {
274
275
                IDissolveCriteria criteria;
276
                int index1;
277
                Stack stack;
278
                List geometries;
279
                ReadableVectorial va;
280
                FunctionSummarizer sumarizer;
281
                CancellableMonitorable cancel;
282
283
                DissolveVisitor(IDissolveCriteria criteria, int index1, Stack stack,
284
                                List geometries, ReadableVectorial va,
285
                                FunctionSummarizer sumarizer, CancellableMonitorable cancel) {
286
                        this.criteria = criteria;
287
                        this.index1 = index1;
288
                        this.stack = stack;
289
                        this.geometries = geometries;
290
                        this.va = va;
291
                        this.sumarizer = sumarizer;
292
                        this.cancel = cancel;
293
                }
294
295
                public void visit(IGeometry g, int index2) throws VisitException {
296
                        if(g == null)
297
                                return;
298
                        if (verifyCancelation(cancel, va)) {
299
                                // TODO Revisar si hay problemas por llamar a finish
300
                                // varias veces
301
                                featureProcessor.finish();
302
                                return;
303
                        }
304
305
                        if(index1 == index2){
306
                                //we dont want dissolve a feature with itself
307
                                return;
308
                        }
309
310
                        if (dissolvedGeometries.get(index2)) {
311
                                // Esta geometria ya ha sido procesada
312
                                return;
313
                        }
314
315
                        if (criteria.verifyIfDissolve(index1, index2)) {
316
                                //Redise?ar esto (es para que valga tanto para dissolves
317
                                //espaciales como alfanum?ricos
318
                                if(stack != null)
319
                                        stack.push(new Integer(index2));
320
                                try {
321
                                        if(criteria instanceof ISpatialDissolveCriteria){
322
                                                //Para ver el criterio de disolucion ya se ha
323
                                                //leido la geometria
324
                                                geometries.add(
325
                                                                ((ISpatialDissolveCriteria)criteria).
326
                                                                getSecondGeometry().toJTSGeometry());
327
                                        }else{
328
                                                IGeometry g2 = va.getShape(index2);
329
                                                if(ct != null)
330
                                                        g2.reProject(ct);
331
                                                geometries.add(g2.toJTSGeometry());
332
                                        }
333
                                        sumarizer.applySumarizeFunction(index2);
334
                                } catch (DriverIOException e) {
335
                                        throw new VisitException(
336
                                                        "Error durante lectura de geometria en dissolve", e);
337
                                } catch (com.hardcode.gdbms.engine.data.driver.DriverException e) {
338
                                        throw new VisitException(
339
                                                        "Error durante lectura de geometria en dissolve", e);
340
                                }
341
                                //Esto se debe hacer externamente
342
                                if(stack == null)
343
                                        dissolvedGeometries.set(index2);
344
                        }
345
                }// visit
346
347
                public String getProcessDescription() {
348
                        return "";
349
                }
350
351
                public void stop(FLayer layer) {
352
                }
353
354
                public boolean start(FLayer layer) {
355
                        return true;
356
                }
357
        }
358
359
        /**
360
         * @param index1
361
         * @param va
362
         * @param cancel
363
         * @throws DriverException
364
         * @throws VisitException
365
         * @throws IOException
366
         * @throws DriverIOException
367
         */
368
        public void processAlphanumeric(int index1,
369
                                                        ReadableVectorial va,
370
                                                        CancellableMonitorable cancel)
371
                                                        throws DriverException, VisitException, IOException,
372
                                                        DriverIOException{
373
374
                dissolvedGeometries.set(index1);
375
                Strategy strategy = StrategyManager.getStrategy(dissolvedLayer);
376
                ArrayList geometries = new ArrayList();
377
                //we add the 'seed' feature geometry
378
                IGeometry g1 = va.getShape(index1);
379
                if(g1 == null)
380
                        return;
381
                if(ct != null)
382
                        g1.reProject(ct);
383
                geometries.add(g1.toJTSGeometry());
384
                if(dissolveCriteria instanceof ISpatialDissolveCriteria){
385
                        ((ISpatialDissolveCriteria)dissolveCriteria).setCoordTrans(ct);
386
                        ((ISpatialDissolveCriteria)dissolveCriteria).setFirstGeometry(g1);
387
                }
388
                FunctionSummarizer sumarizer = new FunctionSummarizer(
389
                                numericField_sumarizeFunction, recordset);
390
                DissolveVisitor visitor = new DissolveVisitor(dissolveCriteria, index1, null,
391
                                geometries, va, sumarizer, cancel);
392
                strategy.process(visitor);
393
                IGeometry newGeometry = FConverter.jts_to_igeometry(union2(geometries));
394
                List sumarizedValues = sumarizer.getValues();
395
                IFeature dissolvedFeature = null;
396
                if(sumarizedValues != null || sumarizedValues.size() != 0){
397
                        dissolvedFeature =  dissolveCriteria.
398
                                                        getFeatureBuilder().
399
                                                        createFeature(newGeometry,
400
                                                                                sumarizedValues,
401
                                                                                                fid,
402
                                                                                                index1);
403
                }else{
404
                        dissolvedFeature = dissolveCriteria.
405
                                                                getFeatureBuilder().
406
                                                                createFeature(newGeometry,
407
                                                                                index1,
408
                                                                                fid);
409
                }
410
                fid++;
411
                featureProcessor.processFeature(dissolvedFeature);
412
                dissolveCriteria.clear();
413
        }
414
415
416
        class SpatialDissolveVisitor implements FeatureVisitor {
417
418
                IDissolveCriteria criteria;
419
                Geometry geom1;
420
                StackEntry entry1;
421
                Stack stack;
422
                List geometries;
423
                ReadableVectorial va;
424
                FunctionSummarizer sumarizer;
425
                CancellableMonitorable cancel;
426
427
                SpatialDissolveVisitor(IDissolveCriteria criteria, Geometry geo1, StackEntry entry1,
428
                                Stack stack,
429
                                List geometries, ReadableVectorial va,
430
                                FunctionSummarizer sumarizer, CancellableMonitorable cancel) {
431
                        this.criteria = criteria;
432
                        this.geom1 = geo1;
433
                        this.entry1 = entry1;
434
                        this.stack = stack;
435
                        this.geometries = geometries;
436
                        this.va = va;
437
                        this.sumarizer = sumarizer;
438
                        this.cancel = cancel;
439
                }
440
441
                void setSeed(StackEntry entry, Geometry jtsGeo){
442
                        this.entry1 = entry;
443
                        this.geom1 = jtsGeo;
444
                }
445
446
447
448
                public void visit(IGeometry g, int index2) throws VisitException {
449
                        if(g == null)
450
                                return;
451
                        if (verifyCancelation(cancel, va)) {
452
                                // TODO Revisar si hay problemas por llamar a finish
453
                                // varias veces
454
                                featureProcessor.finish();
455
                                return;
456
                        }
457
458
                        if(entry1.index == index2){
459
                                //we dont want dissolve a feature with itself
460
                                return;
461
                        }
462
463
                        if (dissolvedGeometries.get(index2)) {
464
                                // Esta geometria ya ha sido procesada
465
                                return;
466
                        }
467
                        if(criteria instanceof ISpatialDissolveCriteria){
468
                                ((ISpatialDissolveCriteria)criteria).setSecondGeometry(g);
469
                        }
470
                        if (criteria.verifyIfDissolve(entry1.index, index2)) {
471
                                StackEntry entry2 = new StackEntry();
472
                                entry2.g = g;
473
                                entry2.index = index2;
474
                                stack.push(entry2);
475
                                System.out.println("Anado "+index2+ " al stack");
476
                                dumpStack(stack);
477
                                if(criteria instanceof ISpatialDissolveCriteria)
478
                                {
479
                                        ISpatialDissolveCriteria c = (ISpatialDissolveCriteria)criteria;
480
                                        geometries.add(c.getSecondJts());
481
                                }else
482
                                        geometries.add(g.toJTSGeometry());
483
                                try {
484
                                        sumarizer.applySumarizeFunction(index2);
485
                                } catch (com.hardcode.gdbms.engine.data.driver.DriverException e) {
486
                                        throw new VisitException("Error al aplicar la funcion de sumarizacion en dissolve", e);
487
                                }
488
                                dissolvedGeometries.set(index2);
489
                        }
490
                }// visit
491
492
                public String getProcessDescription() {
493
                        return "";
494
                }
495
496
                public void stop(FLayer layer) {
497
                }
498
499
                public boolean start(FLayer layer) {
500
                        return true;
501
                }
502
        }
503
504
        class StackEntry{
505
                public IGeometry g;
506
                public int index;
507
        }
508
        /**
509
         * Processes the given feature looking for features to dissolve with. The
510
         * criteria to decide if dissolve two features is given by
511
         * IDissolveCriteria.
512
         *
513
         * @param criteria
514
         *            decides if dissolve two features
515
         * @param index1
516
         *            index of feature we are processing
517
         * @param va
518
         *            it reads geometry of features
519
         * @param cancel
520
         *            listen cancelations
521
         * @throws VisitException
522
         * @throws DriverException
523
         * @throws IOException
524
         * @throws DriverIOException
525
         */
526
        public void process(int index1,
527
                        ReadableVectorial va,
528
                        CancellableMonitorable cancel)
529
                        throws DriverException, VisitException, IOException,
530
                        DriverIOException {
531
532
                if(dissolvedGeometries.get(index1))
533
                        return;
534
                Strategy strategy = StrategyManager.getStrategy(dissolvedLayer);
535
                IGeometry g1 = va.getShape(index1);
536
                if(g1 == null)
537
                        return;
538
                if(ct != null)
539
                        g1.reProject(ct);
540
                StackEntry entry = new StackEntry();
541
                entry.g = g1;
542
                entry.index = index1;
543
                Stack stack = new Stack();//it saves FMap geometries
544
                stack.push(entry);
545
                ArrayList geometries = new ArrayList();
546
547
                Geometry jtsGeo = g1.toJTSGeometry();
548
                geometries.add(jtsGeo);//it saves jts geometries
549
550
                if(dissolveCriteria instanceof ISpatialDissolveCriteria){
551
                        ((ISpatialDissolveCriteria)dissolveCriteria).setCoordTrans(ct);
552
                        ((ISpatialDissolveCriteria)dissolveCriteria).setFirstGeometry(g1);
553
                }
554
                FunctionSummarizer sumarizer = new FunctionSummarizer(
555
                                numericField_sumarizeFunction, recordset);
556
                SpatialDissolveVisitor visitor = new SpatialDissolveVisitor(dissolveCriteria,
557
                                                                                                                                                        jtsGeo,
558
                                                                                                                                                        entry,
559
                                                                                                                                                        stack,
560
                                                                                                                                                        geometries,
561
                                                                                                                                                        va,
562
                                                                                                                                                        sumarizer,
563
                                                                                                                                                        cancel);
564
565
                while (stack.size() != 0) {
566
                        dumpStack(stack);
567
                        StackEntry sEntry = (StackEntry) stack.pop();
568
                        dissolvedGeometries.set(sEntry.index);
569
570
                        /*//TODO
571
                         * Revisar si no deberiamos hacer
572
                         * ct.getInverted().convert(rect);
573
                         *
574
                         * */
575
                        Rectangle2D rect = sEntry.g.getBounds2D();
576
                        if(ct != null)
577
                                rect = ct.convert(rect);
578
                        double xmin = rect.getMinX();
579
                        double ymin = rect.getMinY();
580
                        double xmax = rect.getMaxX();
581
                        double ymax = rect.getMaxY();
582
                        double magnify = 15d;
583
                        Rectangle2D query = new Rectangle2D.Double(xmin - magnify, ymin
584
                                        - magnify, (xmax - xmin) + magnify, (ymax - ymin) + magnify);
585
                        Geometry jts = sEntry.g.toJTSGeometry();
586
                        visitor.setSeed(sEntry, jts);
587
                        strategy.process(visitor, query);
588
                }// while
589
590
591
                IGeometry newGeometry = FConverter.jts_to_igeometry(union2(geometries));
592
                List sumarizedValues = sumarizer.getValues();
593
                IFeature dissolvedFeature = null;
594
                if(sumarizedValues != null || sumarizedValues.size() != 0){
595
                        dissolvedFeature =  dissolveCriteria.
596
                                                                getFeatureBuilder().
597
                                                                createFeature(newGeometry,
598
                                                                                        sumarizedValues,
599
                                                                                                        fid,
600
                                                                                                        index1);
601
                }else{
602
                        dissolvedFeature = dissolveCriteria.
603
                                                                getFeatureBuilder().
604
                                                                createFeature(newGeometry,
605
                                                                                index1,
606
                                                                                fid);
607
                }
608
                fid++;
609
                featureProcessor.processFeature(dissolvedFeature);
610
                dissolveCriteria.clear();
611
        }
612
613
        private void dumpStack(Stack stack) {
614
                Enumeration e = stack.elements();
615
                System.out.println("#####Elementos por procesar");
616
                while(e.hasMoreElements()){
617
                        System.out.println("#######-     "+((StackEntry)e.nextElement()).index);
618
                }
619
        }
620
621
        /**
622
         * Returns the union of all geometries of the list
623
         *
624
         * @param geometries
625
         * @return
626
         */
627
        protected Geometry union(List geometries) {
628
                Geometry union = null;
629
                GeometryFactory fact = ((Geometry)geometries.
630
                                                                        get(0)).getFactory();
631
                Iterator geomIt = geometries.iterator();
632
                while(geomIt.hasNext()){
633
                        Geometry g = (Geometry) geomIt.next();
634
                        if(union == null)
635
                                union = g;
636
                        else{
637
                                Geometry[] geomArray = {union, g};
638
                                GeometryCollection gCol =
639
                                        fact.
640
                                        createGeometryCollection(geomArray);
641
                                union = gCol.buffer(0d);
642
                        }
643
                }
644
                return union;
645
        }
646
647
        protected Geometry union3(List geometries) {
648
                long t0 = System.currentTimeMillis();
649
                                Geometry union = null;
650
                                Iterator geomIt = geometries.iterator();
651
                                while(geomIt.hasNext()){
652
                                        Geometry g = (Geometry) geomIt.next();
653
                                        if(union == null)
654
                                                union = g;
655
                                        else{
656
                                                union = union.union(g);
657
                                        }
658
                                }
659
                long t1 = System.currentTimeMillis();
660
                System.out.println((t1-t0)+ " en procesar union 3");
661
                                return union;
662
                        }
663
664
        protected Geometry union2(List geometries){
665
                        Geometry union = null;
666
                        Geometry[] geom = new Geometry[geometries.size()];
667
                        geometries.toArray(geom);
668
                        GeometryFactory fact = geom[0].getFactory();
669
                    Geometry geomColl = fact.createGeometryCollection(geom);
670
                    union = geomColl.buffer(0);
671
                    return union;
672
673
        }
674
}