Statistics
| Revision:

svn-gvsig-desktop / tags / v2_0_0_Build_2056 / extensions / extEditing / src / org / gvsig / editing / gui / cad / tools / SplitGeometryCADTool.java @ 39004

History | View | Annotate | Download (22.3 KB)

1
/* gvSIG. Geographic Information System of the Valencian Government
2
 *
3
 * Copyright (C) 2007-2008 Infrastructures and Transports Department
4
 * of the Valencian Government (CIT)
5
 * 
6
 * This program is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU General Public License
8
 * as published by the Free Software Foundation; either version 2
9
 * of the License, or (at your option) any later version.
10
 * 
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 * 
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 
19
 * MA  02110-1301, USA.
20
 * 
21
 */
22
package org.gvsig.editing.gui.cad.tools;
23

    
24
import java.awt.Color;
25
import java.awt.Component;
26
import java.awt.Image;
27
import java.awt.event.InputEvent;
28
import java.awt.event.MouseEvent;
29
import java.awt.geom.Point2D;
30
import java.util.ArrayList;
31
import java.util.List;
32

    
33
import javax.swing.JOptionPane;
34

    
35
import com.vividsolutions.jts.geom.Coordinate;
36
import com.vividsolutions.jts.geom.Geometry;
37
import com.vividsolutions.jts.geom.GeometryCollection;
38
import com.vividsolutions.jts.geom.GeometryFactory;
39
import com.vividsolutions.jts.geom.LineString;
40
import com.vividsolutions.jts.geom.MultiLineString;
41
import com.vividsolutions.jts.geom.MultiPoint;
42
import com.vividsolutions.jts.geom.MultiPolygon;
43
import com.vividsolutions.jts.geom.Point;
44
import com.vividsolutions.jts.geom.Polygon;
45
import com.vividsolutions.jts.geom.PrecisionModel;
46

    
47
import org.slf4j.Logger;
48
import org.slf4j.LoggerFactory;
49

    
50
import org.gvsig.andami.PluginServices;
51
import org.gvsig.andami.ui.mdiManager.IWindow;
52
import org.gvsig.app.ApplicationLocator;
53
import org.gvsig.editing.gui.cad.DefaultCADTool;
54
import org.gvsig.editing.gui.cad.exception.CommandException;
55
import org.gvsig.editing.gui.cad.tools.smc.SplitGeometryCADToolContext;
56
import org.gvsig.editing.gui.cad.tools.split.SplitStrategy;
57
import org.gvsig.editing.layers.VectorialLayerEdited;
58
import org.gvsig.fmap.dal.exception.DataException;
59
import org.gvsig.fmap.dal.feature.EditableFeature;
60
import org.gvsig.fmap.dal.feature.Feature;
61
import org.gvsig.fmap.dal.feature.FeatureStore;
62
import org.gvsig.fmap.geom.Geometry.SUBTYPES;
63
import org.gvsig.fmap.geom.GeometryLocator;
64
import org.gvsig.fmap.geom.exception.CreateGeometryException;
65
import org.gvsig.fmap.geom.operation.GeometryOperationException;
66
import org.gvsig.fmap.geom.operation.GeometryOperationNotSupportedException;
67
import org.gvsig.fmap.geom.primitive.Envelope;
68
import org.gvsig.fmap.geom.primitive.GeneralPathX;
69
import org.gvsig.fmap.geom.type.GeometryType;
70
import org.gvsig.fmap.geom.util.Converter;
71
import org.gvsig.fmap.mapcontext.layers.SpatialCache;
72
import org.gvsig.fmap.mapcontext.layers.vectorial.FLyrVect;
73
import org.gvsig.fmap.mapcontext.rendering.symbols.ISymbol;
74
import org.gvsig.fmap.mapcontrol.MapControlDrawer;
75
import org.gvsig.i18n.Messages;
76
import org.gvsig.symbology.SymbologyLocator;
77
import org.gvsig.symbology.SymbologyManager;
78
import org.gvsig.symbology.fmap.mapcontext.rendering.symbol.fill.ISimpleFillSymbol;
79
import org.gvsig.tools.dispose.DisposableIterator;
80
import org.gvsig.tools.dispose.DisposeUtils;
81
import org.gvsig.tools.locator.LocatorException;
82

    
83
import statemap.State;
84
import statemap.StateUndefinedException;
85

    
86
/**
87
 * CAD Tool which splits the selected geometries of a vectorial editing
88
 * layer with a digitized polyline.
89
 * 
90
 * 
91
 * @author Alvaro Zabala
92
 * 
93
 */
94
public class SplitGeometryCADTool extends DefaultCADTool {
95

    
96
    private static Logger logger =
97
        LoggerFactory.getLogger(SplitGeometryCADTool.class);
98
    /**
99
     * String representation of this tool (used for example to active the tool
100
     * in mapcontrol)
101
     */
102
    public static final String SPLIT_GEOMETRY_ACTION_NAME = "layer-modify-split";
103
    public static final String SPLIT_GEOMETRY_TOOL_NAME = "_split_geometry";
104

    
105
    /**
106
     * finite state machine for this CAD tool
107
     */
108
    protected SplitGeometryCADToolContext _fsm;
109

    
110
    /**
111
     * Flag to mark if the digitized line has been finished.
112
     */
113
    protected boolean digitizingFinished = false;
114

    
115
    /**
116
     * Collection of digitized geometries
117
     */
118
    protected List<Point2D> clickedPoints;
119

    
120
    /**
121
     * used to draw helping rectangle (bbox)
122
     */
123
    protected ISymbol rectSymbol = null;
124
    /**
125
     * Initialization method.
126
     */
127
    public void init() {
128
        digitizingFinished = false;
129
        _fsm = new SplitGeometryCADToolContext(this);
130
        setNextTool(SplitGeometryCADTool.SPLIT_GEOMETRY_TOOL_NAME);
131
    }
132

    
133
    public boolean isDigitizingFinished() {
134
        return digitizingFinished;
135
    }
136

    
137
    public String toString() {
138
        return SplitGeometryCADTool.SPLIT_GEOMETRY_TOOL_NAME;
139
    }
140

    
141
    public void finishDigitizedLine() {
142
    }
143

    
144
    public Coordinate[] getPoint2DAsCoordinates(Point2D[] point2d) {
145
        Coordinate[] solution = new Coordinate[point2d.length];
146
        for (int i = 0; i < point2d.length; i++) {
147
            solution[i] = new Coordinate(point2d[i].getX(), point2d[i].getY());
148
        }
149
        return solution;
150
    }
151

    
152
    public void splitSelectedGeometryWithDigitizedLine() {
153
        
154
        // comprobar bucle, probar interseccion?
155
        Point2D[] clickedPts = new Point2D[this.clickedPoints.size()];
156
        clickedPoints.toArray(clickedPts);
157
        Coordinate[] digitizedCoords = getPoint2DAsCoordinates(clickedPts);
158
        LineString splittingLs =
159
            new GeometryFactory(new PrecisionModel(10000))
160
                .createLineString(digitizedCoords);
161
        DisposableIterator selected = null;
162
        try {
163
            VectorialLayerEdited vle = getVLE();
164
            FeatureStore store = vle.getFeatureStore();
165
            selected = store.getFeatureSelection().iterator();
166

    
167
            getCadToolAdapter().getMapControl().getMapContext()
168
                .beginAtomicEvent();
169
            store.beginEditingGroup(getName());
170
            while (selected.hasNext()) {
171
                Feature feature = (Feature) selected.next();
172
                org.gvsig.fmap.geom.Geometry ig = feature.getDefaultGeometry();
173
                Geometry jtsGeo = (Geometry)ig.invokeOperation("toJTS", null);
174
                if (jtsGeo == null)
175
                    return;
176
                Geometry splitGeo = SplitStrategy.splitOp(jtsGeo, splittingLs);
177
                if (splitGeo instanceof GeometryCollection
178
                    && ((GeometryCollection) splitGeo).getNumGeometries() > 1) {
179

    
180
                    GeometryCollection gc = (GeometryCollection) splitGeo;
181
                    ArrayList<Geometry> geoms0 = new ArrayList<Geometry>();
182
                    ArrayList<Geometry> geoms1 = new ArrayList<Geometry>();
183
                    if (gc.getNumGeometries() > 2) {
184
                        Geometry[] splitGroups =
185
                            createSplitGroups(jtsGeo, splittingLs);
186
                        
187
                        if (splitGroups == null) {
188
                            
189
                            String _tit = Messages.getText("split_geometry");
190
                            String _msg = Messages.getText("_Split_line_must_start_and_end_outside_bbox");
191
                            Component parent = getCurrentWindow();
192
                            JOptionPane.showMessageDialog(
193
                                parent,
194
                                _msg,
195
                                _tit,
196
                                JOptionPane.WARNING_MESSAGE);
197
                            // cancel splitting
198
                            break;
199
                        }
200
                        
201
                        if (splitGroups.length == 0) {
202
                            continue;
203
                        }
204

    
205
                        for (int j = 0; j < gc.getNumGeometries(); j++) {
206
                            Geometry g = gc.getGeometryN(j);
207
                            
208
                            if (isRatherInside(g, splitGroups[0])) {
209
                                geoms0.add(g);
210
                            } else {
211
                                geoms1.add(g);
212
                            }
213
                        }
214
                    } else {
215
                        if (gc.getNumGeometries() == 2) {
216
                            geoms0.add(gc.getGeometryN(0));
217
                            geoms1.add(gc.getGeometryN(1));
218
                        } else {
219
                            continue;
220
                        }
221
                    }
222
                    GeometryCollection gc0 =
223
                        createMulti(geoms0, gc.getFactory());
224

    
225
                    // for(int j = 0; j < gc.getNumGeometries(); j++){
226
                    // Geometry g = gc.getGeometryN(j);
227
                    org.gvsig.fmap.geom.Geometry fmapGeo =
228
                        Converter.jtsToGeometry(gc0);
229

    
230
                    // if (j==0){
231
                    EditableFeature eFeature = feature.getEditable();
232
                    eFeature.setGeometry(store.getDefaultFeatureType()
233
                        .getDefaultGeometryAttributeName(), fmapGeo);
234
                    store.update(eFeature);
235
                    // }else{
236
                    GeometryCollection gc1 =
237
                        createMulti(geoms1, gc.getFactory());
238
                    fmapGeo = Converter.jtsToGeometry(gc1);
239

    
240
                    EditableFeature newFeature =
241
                        store.createNewFeature(store.getDefaultFeatureType(),
242
                            feature);
243
                    newFeature.setGeometry(store.getDefaultFeatureType()
244
                        .getDefaultGeometryAttributeName(), fmapGeo);
245
                    store.insert(newFeature);
246
                    SpatialCache spatialCache =
247
                        ((FLyrVect) vle.getLayer()).getSpatialCache();
248
                    Envelope envelope = fmapGeo.getEnvelope();
249
                    spatialCache.insert(envelope, fmapGeo);
250
                    // }
251
                    // }//for j
252
                }// if splitGeo
253

    
254
            }
255

    
256
            store.endEditingGroup();
257

    
258
            getCadToolAdapter().getMapControl().getMapContext()
259
                .endAtomicEvent();
260
        } catch (Exception ex) {
261
            PluginServices.getLogger().error("Error splitting geom", ex);
262
        } finally {
263
            DisposeUtils.dispose(selected);
264
        }
265
    }
266

    
267
    /**
268
     * @return
269
     */
270
    private Component getCurrentWindow() {
271
        
272
        IWindow iw = PluginServices.getMDIManager().getActiveWindow();
273
        if (iw instanceof Component) {
274
            return (Component) iw;
275
        } else {
276
            return null;
277
        }
278
    }
279

    
280
    private GeometryCollection createMulti(ArrayList<Geometry> geoms,
281
        GeometryFactory factory) {
282
        if (geoms.size() == 0)
283
            return null;
284
        if (geoms.get(0) instanceof Polygon) {
285
            return new MultiPolygon((Polygon[]) geoms.toArray(new Polygon[0]),
286
                factory);
287
        } else
288
            if (geoms.get(0) instanceof LineString) {
289
                return new MultiLineString(
290
                    (LineString[]) geoms.toArray(new LineString[0]), factory);
291
            } else
292
                if (geoms.get(0) instanceof Point) {
293
                    return new MultiPoint(
294
                        (Point[]) geoms.toArray(new Point[0]), factory);
295
                }
296
        return null;
297
    }
298

    
299
    private Geometry[] createSplitGroups(Geometry splitGeo,
300
        LineString splittingLs) {
301
        try {
302
            Geometry[] geomsJTS = new Geometry[2];
303
            Geometry r = splitGeo.getEnvelope();
304
            Geometry splitG = SplitStrategy.splitOp(r, splittingLs);
305
            if (splitG instanceof GeometryCollection
306
                && ((GeometryCollection) splitG).getNumGeometries() > 1) {
307
                GeometryCollection gc = (GeometryCollection) splitG;
308
                for (int j = 0; j < gc.getNumGeometries(); j++) {
309
                    geomsJTS[j] = gc.getGeometryN(j);
310
                }
311
            }
312
            return geomsJTS;
313
        } catch (Exception e) {
314
            logger.info("Warning: Found split line which does not leave bbox. User will be prompted.");
315
        }
316
        return null;
317
    }
318

    
319
    public void end() {
320
        getCadToolAdapter().refreshEditedLayer();
321
        init();
322
    }
323

    
324
    public void addOption(String s) {
325
        State actualState = _fsm.getPreviousState();
326
        String status = actualState.getName();
327
        if (s.equals(PluginServices.getText(this, "cancel"))) {
328
            init();
329
            return;
330
        }
331
        if (status.equals("TopologicalEdition.FirstPoint")) {
332
            return;
333
        }
334
        init();
335

    
336
    }
337

    
338
    public void addPoint(double x, double y, InputEvent event) {
339

    
340
        State actualState = _fsm.getPreviousState();
341
        String status = actualState.getName();
342
        if (status.equals("SplitGeometry.FirstPoint")) {
343
            clickedPoints = new ArrayList<Point2D>();
344
            clickedPoints.add(new Point2D.Double(x, y));
345
        } else
346
            if (status.equals("SplitGeometry.DigitizingLine")) {
347
                clickedPoints.add(new Point2D.Double(x, y));
348
                if (event != null && ((MouseEvent) event).getClickCount() == 2) {
349
                    digitizingFinished = true;
350
                    finishDigitizedLine();
351
                    splitSelectedGeometryWithDigitizedLine();
352
                    end();
353
                }
354
            }
355
    }
356

    
357
    public void addValue(double d) {
358
    }
359

    
360
    /**
361
     * Draws a polyline with the clicked digitized points in the specified
362
     * graphics.
363
     * 
364
     * @param g2
365
     *            graphics on to draw the polyline
366
     * @param x
367
     *            last x mouse pointer position
368
     * @param y
369
     *            last y mouse pointer position
370
     */
371
    protected void drawPolyLine(MapControlDrawer renderer, double x, double y) {
372
        GeneralPathX gpx =
373
            new GeneralPathX(GeneralPathX.WIND_EVEN_ODD, clickedPoints.size());
374
        Point2D firstPoint = clickedPoints.get(0);
375
        gpx.moveTo(firstPoint.getX(), firstPoint.getY());
376
        for (int i = 1; i < clickedPoints.size(); i++) {
377
            Point2D clickedPoint = clickedPoints.get(i);
378
            gpx.lineTo(clickedPoint.getX(), clickedPoint.getY());
379

    
380
        }
381
        gpx.lineTo(x, y);
382
        org.gvsig.fmap.geom.Geometry geom;
383
        try {
384
            geom =
385
                GeometryLocator.getGeometryManager().createCurve(gpx,
386
                    SUBTYPES.GEOM2D);
387
            renderer.draw(geom, mapControlManager.getGeometrySelectionSymbol());
388
        } catch (LocatorException e) {
389
            e.printStackTrace();
390
        } catch (CreateGeometryException e) {
391
            e.printStackTrace();
392
        }
393
    }
394

    
395
    private void drawRectangleOfSplit(MapControlDrawer renderer)
396
        throws GeometryOperationNotSupportedException,
397
        GeometryOperationException, DataException,
398
        CreateGeometryException {
399
        VectorialLayerEdited vle = getVLE();
400
        FeatureStore store = vle.getFeatureStore();
401
        DisposableIterator selected = null;
402

    
403
        try {
404
            selected = store.getFeatureSelection().iterator();
405
            while (selected.hasNext()) {
406
                Feature feature = (Feature) selected.next();
407
                org.gvsig.fmap.geom.Geometry ig = feature.getDefaultGeometry();
408
                Geometry jtsG = (Geometry)ig.invokeOperation("toJTS", null);
409
                if (jtsG != null && jtsG instanceof GeometryCollection
410
                    && jtsG.getNumGeometries() > 1) {
411
                    
412
                    /*
413
                    org.gvsig.fmap.geom.Geometry r =
414
                        ig.getEnvelope().getGeometry();
415
                        */
416
                    // get perimeter of envelope to prevent
417
                    // opaque rectangle
418
                    org.gvsig.fmap.geom.Geometry geom =
419
                        GeometryLocator.getGeometryManager().createCurve(
420
                            ig.getEnvelope().getGeometry().getGeneralPath(),
421
                            SUBTYPES.GEOM2D);
422
                    
423
                    renderer.draw(geom, getRectangleOfSplitSymbol());
424
                }
425
            }
426
        } finally {
427
            DisposeUtils.dispose(selected);
428
        }
429
    }
430

    
431
    /**
432
     * @return
433
     */
434
    private ISymbol getRectangleOfSplitSymbol() {
435
        
436
        if (rectSymbol == null) {
437
            SymbologyManager sm = SymbologyLocator.getSymbologyManager();
438
            ISimpleFillSymbol resp = sm.createSimpleFillSymbol();
439
            resp.setColor(Color.RED);
440
            resp.setFillColor(new Color(0,0,0, 100));
441
            rectSymbol = resp;
442
        }
443
        return rectSymbol;
444
    }
445

    
446
    public void drawOperation(MapControlDrawer renderer, double x, double y) {
447
        
448
        // decide whether user line must be drawn
449
        boolean draw_user_poly_line = false;
450
        try {
451
            State currentState = _fsm.getState();
452
            if (currentState != null) {
453
                String status = currentState.getName();
454
                if (status != null && status.equals("SplitGeometry.DigitizingLine")) {
455
                    draw_user_poly_line = true;
456
                }
457
            }        
458
        } catch (StateUndefinedException sue) {
459
            // this happens when state is null
460
            // because the line has been finished and we must not draw it
461
            draw_user_poly_line = false;
462
        }
463
        
464
        // =======================================================
465
        
466
        try {
467
            drawRectangleOfSplit(renderer);
468
            
469
            // possibly draw splitting line
470
            if (draw_user_poly_line) {
471
                drawPolyLine(renderer, x, y);
472
            }
473

    
474
            // draw selection
475
            Image imgSel = getVLE().getSelectionImage();
476
            renderer.drawImage(imgSel, 0, 0);
477
        } catch (Exception e) {
478
            
479
            logger.info("Error while drawing split tool.", e);
480
            ApplicationLocator.getManager().message(
481
                Messages.getText("_Drawing_error") + e.getMessage(),
482
                JOptionPane.ERROR_MESSAGE);
483
        }
484
    }
485

    
486
    public String getName() {
487
        return PluginServices.getText(this, "split_geometry_shell");
488
    }
489

    
490
    public void transition(double x, double y, InputEvent event) {
491
        try {
492
            _fsm.addPoint(x, y, event);
493
        } catch (Exception e) {
494
            init();
495
        }
496

    
497
    }
498

    
499
    public void transition(double d) {
500
        _fsm.addValue(d);
501
    }
502

    
503
    public void transition(String s) throws CommandException {
504
        if (!super.changeCommand(s)) {
505
            _fsm.addOption(s);
506
        }
507
    }
508

    
509
    @Override
510
    public boolean isApplicable(GeometryType geometryType) {
511
        return true;
512
    }
513

    
514
    @Override
515
    protected int[] getSupportedGeometryTypes() {
516
        return null;
517
    }
518

    
519
    /**
520
     * This method decides if geometrya  is inside geometry b 
521
     * by more than 50%. This is useful because it will be
522
     * used to assign each geometry to one of two possible containers
523
     * 
524
     * @param a
525
     * @param b must be a multi(polygon)
526
     * @return
527
     */
528
    private static boolean isRatherInside(Geometry a, Geometry b) {
529
        
530
        if (a instanceof Polygon || a instanceof MultiPolygon) {
531
            return polygonIsRatherInside(a, b);
532
        } else {
533
            if (a instanceof LineString || a instanceof MultiLineString) {
534
                return lineIsRatherInside(a, b);
535
            } else {
536
                if (a instanceof MultiPoint) {
537
                    
538
                    MultiPoint mp = (MultiPoint) a;
539
                    int n = mp.getNumPoints();
540
                    int inn = 0;
541
                    Geometry itemg = null;
542
                    for (int i=0; i<n; i++) {
543
                        itemg = mp.getGeometryN(i);
544
                        if (isRatherInside(itemg, b)) {
545
                            inn++;
546
                        }
547
                    }
548
                    return inn > (n/2);
549
                } else {
550
                    // last case: should be a point
551
                    return b.contains(a);
552
                }
553
            }
554
        }
555
        
556
    }
557

    
558
    
559
    
560
    /**
561
     * This method decides if a polygon is inside another
562
     * by more than 50%. This is useful because it will be
563
     * used to assign each polygon to one of two possible containers
564
     * 
565
     * @param a
566
     * @param b
567
     * @return whether a is 'rather' contained by b
568
     */
569
    private static boolean polygonIsRatherInside(Geometry a, Geometry b) {
570
        
571
        if (a == null || b == null || a.isEmpty() || b.isEmpty()) {
572
            return false;
573
        }
574
        
575
        double area_a = a.getArea();
576
        
577
        if (area_a == 0) {
578
            return false;
579
        }
580
        
581
        Geometry a_inters_b = null;
582
        
583
        a_inters_b = a.intersection(b);
584
        if (a_inters_b == null || a_inters_b.isEmpty()) {
585
            return false;
586
        }
587

    
588
        double area_aib = a_inters_b.getArea();
589
        
590
        if (area_aib == 0) {
591
            return false;
592
        }
593
        
594
        return area_aib > (0.5d * area_a);
595
    }
596
    
597
    /**
598
     * 
599
     * @param a assumed to be a (multi)linestring
600
     * @param b the (possibly) containing polygon
601
     * @return
602
     */
603
    private static boolean lineIsRatherInside(Geometry a, Geometry b) {
604
        
605
        if (a == null || b == null || a.isEmpty() || b.isEmpty()) {
606
            return false;
607
        }
608
        
609
        double len_a = a.getLength();
610
        
611
        if (len_a == 0) {
612
            return false;
613
        }
614
        
615
        Geometry a_inters_b = null;
616
        
617
        a_inters_b = a.intersection(b);
618
        if (a_inters_b == null || a_inters_b.isEmpty()) {
619
            return false;
620
        }
621

    
622
        double len_aib = a_inters_b.getLength();
623
        
624
        if (len_aib == 0) {
625
            return false;
626
        }
627
        
628
        return len_aib > (0.5d * len_a);
629
    }
630
}