Statistics
| Revision:

svn-gvsig-desktop / trunk / org.gvsig.desktop / org.gvsig.desktop.plugin / org.gvsig.editing.app / org.gvsig.editing.app.mainplugin / src / main / java / org / gvsig / editing / gui / cad / tools / SplitGeometryCADTool.java @ 40557

History | View | Annotate | Download (22.6 KB)

1
/**
2
 * gvSIG. Desktop Geographic Information System.
3
 *
4
 * Copyright (C) 2007-2013 gvSIG Association.
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 3
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
 * For any additional information, do not hesitate to contact us
22
 * at info AT gvsig.com, or visit our website www.gvsig.com.
23
 */
24
package org.gvsig.editing.gui.cad.tools;
25

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

    
35
import javax.swing.JOptionPane;
36

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

    
49
import org.slf4j.Logger;
50
import org.slf4j.LoggerFactory;
51

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

    
86
import statemap.State;
87
import statemap.StateUndefinedException;
88

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

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

    
108
    /**
109
     * finite state machine for this CAD tool
110
     */
111
    protected SplitGeometryCADToolContext _fsm;
112

    
113
    /**
114
     * Flag to mark if the digitized line has been finished.
115
     */
116
    protected boolean digitizingFinished = false;
117

    
118
    /**
119
     * Collection of digitized geometries
120
     */
121
    protected List<Point2D> clickedPoints;
122

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

    
136
    public boolean isDigitizingFinished() {
137
        return digitizingFinished;
138
    }
139

    
140
    public String toString() {
141
        return SplitGeometryCADTool.SPLIT_GEOMETRY_TOOL_NAME;
142
    }
143

    
144
    public void finishDigitizedLine() {
145
    }
146

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

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

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

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

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

    
230
                    // for(int j = 0; j < gc.getNumGeometries(); j++){
231
                    // Geometry g = gc.getGeometryN(j);
232
                    org.gvsig.fmap.geom.Geometry fmapGeo =
233
                        Converter.jtsToGeometry(gc0);
234

    
235
                    // if (j==0){
236
                    EditableFeature eFeature = feature.getEditable();
237
                    eFeature.setGeometry(store.getDefaultFeatureType()
238
                        .getDefaultGeometryAttributeName(), fmapGeo);
239
                    
240
                    ed_man.updateFeature(store, eFeature);
241

    
242
                    // }else{
243
                    GeometryCollection gc1 =
244
                        createMulti(geoms1, gc.getFactory());
245
                    fmapGeo = Converter.jtsToGeometry(gc1);
246

    
247
                    super.insertGeometry(fmapGeo,feature);
248
//                    EditableFeature newFeature =
249
//                        store.createNewFeature(store.getDefaultFeatureType(),
250
//                            feature);
251
//                    newFeature.setGeometry(store.getDefaultFeatureType()
252
//                        .getDefaultGeometryAttributeName(), fmapGeo);
253
//                    store.insert(newFeature);
254
//                    
255
//                    SpatialCache spatialCache =
256
//                        ((FLyrVect) vle.getLayer()).getSpatialCache();
257
//                    Envelope envelope = fmapGeo.getEnvelope();
258
//                    spatialCache.insert(envelope, fmapGeo);
259

    
260
                    // }
261
                    // }//for j
262
                }// if splitGeo
263

    
264
            }
265

    
266
            store.endEditingGroup();
267

    
268
            getCadToolAdapter().getMapControl().getMapContext()
269
                .endAtomicEvent();
270
        } catch (Exception ex) {
271
            PluginServices.getLogger().error("Error splitting geom", ex);
272
        } finally {
273
            DisposeUtils.dispose(selected);
274
        }
275
    }
276

    
277
    /**
278
     * @return
279
     */
280
    private Component getCurrentWindow() {
281
        
282
        IWindow iw = PluginServices.getMDIManager().getActiveWindow();
283
        if (iw instanceof Component) {
284
            return (Component) iw;
285
        } else {
286
            return null;
287
        }
288
    }
289

    
290
    private GeometryCollection createMulti(ArrayList<Geometry> geoms,
291
        GeometryFactory factory) {
292
        if (geoms.size() == 0)
293
            return null;
294
        if (geoms.get(0) instanceof Polygon) {
295
            return new MultiPolygon((Polygon[]) geoms.toArray(new Polygon[0]),
296
                factory);
297
        } else
298
            if (geoms.get(0) instanceof LineString) {
299
                return new MultiLineString(
300
                    (LineString[]) geoms.toArray(new LineString[0]), factory);
301
            } else
302
                if (geoms.get(0) instanceof Point) {
303
                    return new MultiPoint(
304
                        (Point[]) geoms.toArray(new Point[0]), factory);
305
                }
306
        return null;
307
    }
308

    
309
    private Geometry[] createSplitGroups(Geometry splitGeo,
310
        LineString splittingLs) {
311
        try {
312
            Geometry[] geomsJTS = new Geometry[2];
313
            Geometry r = splitGeo.getEnvelope();
314
            Geometry splitG = SplitStrategy.splitOp(r, splittingLs);
315
            if (splitG instanceof GeometryCollection
316
                && ((GeometryCollection) splitG).getNumGeometries() > 1) {
317
                GeometryCollection gc = (GeometryCollection) splitG;
318
                for (int j = 0; j < gc.getNumGeometries(); j++) {
319
                    geomsJTS[j] = gc.getGeometryN(j);
320
                }
321
            }
322
            return geomsJTS;
323
        } catch (Exception e) {
324
            logger.info("Warning: Found split line which does not leave bbox. User will be prompted.");
325
        }
326
        return null;
327
    }
328

    
329
    public void end() {
330
        getCadToolAdapter().refreshEditedLayer();
331
        init();
332
    }
333

    
334
    public void addOption(String s) {
335
        State actualState = _fsm.getPreviousState();
336
        String status = actualState.getName();
337
        if (s.equals(PluginServices.getText(this, "cancel"))) {
338
            init();
339
            return;
340
        }
341
        if (status.equals("TopologicalEdition.FirstPoint")) {
342
            return;
343
        }
344
        init();
345

    
346
    }
347

    
348
    public void addPoint(double x, double y, InputEvent event) {
349

    
350
        State actualState = _fsm.getPreviousState();
351
        String status = actualState.getName();
352
        if (status.equals("SplitGeometry.FirstPoint")) {
353
            clickedPoints = new ArrayList<Point2D>();
354
            clickedPoints.add(new Point2D.Double(x, y));
355
        } else
356
            if (status.equals("SplitGeometry.DigitizingLine")) {
357
                clickedPoints.add(new Point2D.Double(x, y));
358
                if (event != null && ((MouseEvent) event).getClickCount() == 2) {
359
                    digitizingFinished = true;
360
                    finishDigitizedLine();
361
                    splitSelectedGeometryWithDigitizedLine();
362
                    end();
363
                }
364
            }
365
    }
366

    
367
    public void addValue(double d) {
368
    }
369

    
370
    /**
371
     * Draws a polyline with the clicked digitized points in the specified
372
     * graphics.
373
     * 
374
     * @param g2
375
     *            graphics on to draw the polyline
376
     * @param x
377
     *            last x mouse pointer position
378
     * @param y
379
     *            last y mouse pointer position
380
     */
381
    protected void drawPolyLine(MapControlDrawer renderer, double x, double y) {
382
        GeneralPathX gpx =
383
            new GeneralPathX(GeneralPathX.WIND_EVEN_ODD, clickedPoints.size());
384
        Point2D firstPoint = clickedPoints.get(0);
385
        gpx.moveTo(firstPoint.getX(), firstPoint.getY());
386
        for (int i = 1; i < clickedPoints.size(); i++) {
387
            Point2D clickedPoint = clickedPoints.get(i);
388
            gpx.lineTo(clickedPoint.getX(), clickedPoint.getY());
389

    
390
        }
391
        gpx.lineTo(x, y);
392
        org.gvsig.fmap.geom.Geometry geom;
393
        try {
394
            geom =
395
                GeometryLocator.getGeometryManager().createCurve(gpx,
396
                    SUBTYPES.GEOM2D);
397
            renderer.draw(geom, mapControlManager.getGeometrySelectionSymbol());
398
        } catch (LocatorException e) {
399
            e.printStackTrace();
400
        } catch (CreateGeometryException e) {
401
            e.printStackTrace();
402
        }
403
    }
404

    
405
    private void drawRectangleOfSplit(MapControlDrawer renderer)
406
        throws GeometryOperationNotSupportedException,
407
        GeometryOperationException, DataException,
408
        CreateGeometryException {
409
        VectorialLayerEdited vle = getVLE();
410
        FeatureStore store = vle.getFeatureStore();
411
        DisposableIterator selected = null;
412

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

    
441
    /**
442
     * @return
443
     */
444
    private ISymbol getRectangleOfSplitSymbol() {
445
        
446
        if (rectSymbol == null) {
447
            SymbologyManager sm = SymbologyLocator.getSymbologyManager();
448
            ISimpleFillSymbol resp = sm.createSimpleFillSymbol();
449
            resp.setColor(Color.RED);
450
            resp.setFillColor(new Color(0,0,0, 100));
451
            rectSymbol = resp;
452
        }
453
        return rectSymbol;
454
    }
455

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

    
484
            // draw selection
485
            Image imgSel = getVLE().getSelectionImage();
486
            renderer.drawImage(imgSel, 0, 0);
487
        } catch (Exception e) {
488
            
489
            logger.info("Error while drawing split tool.", e);
490
            ApplicationLocator.getManager().message(
491
                Messages.getText("_Drawing_error") + e.getMessage(),
492
                JOptionPane.ERROR_MESSAGE);
493
        }
494
    }
495

    
496
    public String getName() {
497
        return PluginServices.getText(this, "split_geometry_shell");
498
    }
499

    
500
    public void transition(double x, double y, InputEvent event) {
501
        try {
502
            _fsm.addPoint(x, y, event);
503
        } catch (Exception e) {
504
            init();
505
        }
506

    
507
    }
508

    
509
    public void transition(double d) {
510
        _fsm.addValue(d);
511
    }
512

    
513
    public void transition(String s) throws CommandException {
514
        if (!super.changeCommand(s)) {
515
            _fsm.addOption(s);
516
        }
517
    }
518

    
519
    @Override
520
    public boolean isApplicable(GeometryType geometryType) {
521
        return true;
522
    }
523

    
524
    @Override
525
    protected int[] getSupportedGeometryTypes() {
526
        return null;
527
    }
528

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

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

    
598
        double area_aib = a_inters_b.getArea();
599
        
600
        if (area_aib == 0) {
601
            return false;
602
        }
603
        
604
        return area_aib > (0.5d * area_a);
605
    }
606
    
607
    /**
608
     * 
609
     * @param a assumed to be a (multi)linestring
610
     * @param b the (possibly) containing polygon
611
     * @return
612
     */
613
    private static boolean lineIsRatherInside(Geometry a, Geometry b) {
614
        
615
        if (a == null || b == null || a.isEmpty() || b.isEmpty()) {
616
            return false;
617
        }
618
        
619
        double len_a = a.getLength();
620
        
621
        if (len_a == 0) {
622
            return false;
623
        }
624
        
625
        Geometry a_inters_b = null;
626
        
627
        a_inters_b = a.intersection(b);
628
        if (a_inters_b == null || a_inters_b.isEmpty()) {
629
            return false;
630
        }
631

    
632
        double len_aib = a_inters_b.getLength();
633
        
634
        if (len_aib == 0) {
635
            return false;
636
        }
637
        
638
        return len_aib > (0.5d * len_a);
639
    }
640
}