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 @ 41595

History | View | Annotate | Download (22.5 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.GeneralPathX;
71
import org.gvsig.fmap.geom.type.GeometryType;
72
import org.gvsig.fmap.geom.util.UtilFunctions;
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
        IEditionManager ed_man = this.getEditionManager();
155
        
156
        // comprobar bucle, probar interseccion?
157
        Point2D[] clickedPts = new Point2D[this.clickedPoints.size()];
158
        clickedPoints.toArray(clickedPts);
159
        Coordinate[] digitizedCoords = getPoint2DAsCoordinates(clickedPts);
160
        LineString splittingLs =
161
            new GeometryFactory(new PrecisionModel(10000))
162
                .createLineString(digitizedCoords);
163
        DisposableIterator selected = null;
164
        try {
165
            VectorialLayerEdited vle = getVLE();
166
            FeatureStore store = vle.getFeatureStore();
167
            selected = store.getFeatureSelection().iterator();
168

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

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

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

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

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

    
239
                    // }else{
240
                    GeometryCollection gc1 =
241
                        createMulti(geoms1, gc.getFactory());
242
                    fmapGeo = UtilFunctions.jtsToGeometry(gc1);
243

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

    
257
                    // }
258
                    // }//for j
259
                }// if splitGeo
260

    
261
            }
262

    
263
            store.endEditingGroup();
264

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

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

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

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

    
326
    public void end() {
327
        getCadToolAdapter().refreshEditedLayer();
328
        init();
329
    }
330

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

    
343
    }
344

    
345
    public void addPoint(double x, double y, InputEvent event) {
346

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

    
364
    public void addValue(double d) {
365
    }
366

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

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

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

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

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

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

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

    
493
    public String getName() {
494
        return PluginServices.getText(this, "split_geometry_shell");
495
    }
496

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

    
504
    }
505

    
506
    public void transition(double d) {
507
        _fsm.addValue(d);
508
    }
509

    
510
    public void transition(String s) throws CommandException {
511
        if (!super.changeCommand(s)) {
512
            _fsm.addOption(s);
513
        }
514
    }
515

    
516
    @Override
517
    public boolean isApplicable(GeometryType geometryType) {
518
        return true;
519
    }
520

    
521
    @Override
522
    protected int[] getSupportedGeometryTypes() {
523
        return null;
524
    }
525

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

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

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

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