Source Code Cross Referenced for EditUtils.java in  » GIS » udig-1.1 » net » refractions » udig » tools » edit » support » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » GIS » udig 1.1 » net.refractions.udig.tools.edit.support 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /* uDig - User Friendly Desktop Internet GIS client
0002:         * http://udig.refractions.net
0003:         * (C) 2004, Refractions Research Inc.
0004:         *
0005:         * This library is free software; you can redistribute it and/or
0006:         * modify it under the terms of the GNU Lesser General Public
0007:         * License as published by the Free Software Foundation;
0008:         * version 2.1 of the License.
0009:         *
0010:         * This library is distributed in the hope that it will be useful,
0011:         * but WITHOUT ANY WARRANTY; without even the implied warranty of
0012:         * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0013:         * Lesser General Public License for more details.
0014:         */
0015:        package net.refractions.udig.tools.edit.support;
0016:
0017:        import java.awt.geom.PathIterator;
0018:        import java.io.IOException;
0019:        import java.util.ArrayList;
0020:        import java.util.Collection;
0021:        import java.util.Collections;
0022:        import java.util.HashSet;
0023:        import java.util.LinkedList;
0024:        import java.util.List;
0025:        import java.util.Set;
0026:
0027:        import net.refractions.udig.core.IBlockingProvider;
0028:        import net.refractions.udig.mapgraphic.grid.GridMapGraphic;
0029:        import net.refractions.udig.project.IBlackboard;
0030:        import net.refractions.udig.project.ILayer;
0031:        import net.refractions.udig.project.IMap;
0032:        import net.refractions.udig.project.ProjectBlackboardConstants;
0033:        import net.refractions.udig.project.command.AbstractCommand;
0034:        import net.refractions.udig.project.command.UndoableComposite;
0035:        import net.refractions.udig.project.command.UndoableMapCommand;
0036:        import net.refractions.udig.project.ui.AnimationUpdater;
0037:        import net.refractions.udig.project.ui.render.displayAdapter.ViewportPane;
0038:        import net.refractions.udig.project.ui.tool.IToolContext;
0039:        import net.refractions.udig.tools.edit.EditPlugin;
0040:        import net.refractions.udig.tools.edit.EditState;
0041:        import net.refractions.udig.tools.edit.EditToolHandler;
0042:        import net.refractions.udig.tools.edit.animation.SearchBoxAnimation;
0043:        import net.refractions.udig.tools.edit.commands.AddVertexCommand;
0044:        import net.refractions.udig.tools.edit.commands.CreateAndSelectHoleCommand;
0045:        import net.refractions.udig.tools.edit.preferences.PreferenceUtil;
0046:        import net.refractions.udig.ui.ProgressManager;
0047:
0048:        import org.eclipse.core.runtime.IProgressMonitor;
0049:        import org.geotools.data.FeatureSource;
0050:        import org.geotools.feature.Feature;
0051:        import org.geotools.feature.FeatureCollection;
0052:        import org.geotools.feature.FeatureIterator;
0053:        import org.geotools.filter.FidFilter;
0054:        import org.geotools.filter.Filter;
0055:        import org.geotools.filter.FilterFactory;
0056:        import org.geotools.filter.FilterFactoryFinder;
0057:        import org.geotools.filter.FilterType;
0058:        import org.geotools.filter.IllegalFilterException;
0059:        import org.geotools.geometry.jts.JTS;
0060:        import org.opengis.referencing.FactoryException;
0061:        import org.opengis.referencing.operation.MathTransform;
0062:        import org.opengis.referencing.operation.TransformException;
0063:
0064:        import com.vividsolutions.jts.geom.Coordinate;
0065:        import com.vividsolutions.jts.geom.Envelope;
0066:        import com.vividsolutions.jts.geom.Geometry;
0067:        import com.vividsolutions.jts.geom.LinearRing;
0068:
0069:        /**
0070:         * Methods for determining spatial relationships between points. 
0071:         * 
0072:         * @author Jesse
0073:         * @since 1.1.0
0074:         */
0075:        public class EditUtils {
0076:
0077:            static final int TOP = 0x8, BOTTOM = 0x4, RIGHT = 0x2, LEFT = 0x1;
0078:
0079:            public static final int OVER_EDGE = -1;
0080:            public static final int NO_INTERSECTION = -2;
0081:
0082:            private static final String EDIT_FEATURE_BOUNDS = "BOUNDS OF RENDERING FILTER"; //$NON-NLS-1$
0083:
0084:            public static EditUtils instance = new EditUtils();
0085:
0086:            public int overVertext(Coordinate[] coords, Envelope env) {
0087:                if (coords == null)
0088:                    return NO_INTERSECTION;
0089:                for (int i = 0; i < coords.length; i++) {
0090:                    Coordinate coord = coords[i];
0091:                    if (env.contains(coord))
0092:                        return i;
0093:                }
0094:                return OVER_EDGE;
0095:            }
0096:
0097:            /**
0098:             * Return true if the envelope overlaps at least one edge of the shape.  This checks all the
0099:             * Coordinates in the shape so the envelope must be in the "world" space (same projection as coordinates). 
0100:             * 
0101:             * @param shape to search 
0102:             * @param env envelope used to see if it overlaps an edge
0103:             * @return true if the envelope overlaps at least one edge of the shape.
0104:             */
0105:            public boolean overEdgeCoordinatePrecision(PrimitiveShape shape,
0106:                    Envelope env) {
0107:                if (shape.getNumCoords() < 2)
0108:                    return false;
0109:                Coordinate endPoint1 = shape.getCoord(shape.getNumCoords() - 1);
0110:                for (int i = 0; i < shape.getNumCoords(); i++) {
0111:                    Coordinate endPoint2 = shape.getCoord(i);
0112:                    if (overEdge(endPoint1, endPoint2, env))
0113:                        return true;
0114:                    endPoint1 = endPoint2;
0115:                }
0116:                return false;
0117:            }
0118:
0119:            /**
0120:             * Return true if the envelope overlaps at least one edge of the shape.  This only checks the
0121:             * Points in the shape so the envelope must be in pixel space.  This also means that it is not 
0122:             * completely accurate but it is sufficient for interactive purposes.
0123:             * 
0124:             * @param shape to search 
0125:             * @param env envelope used to see if it overlaps an edge
0126:             * @return true if the envelope overlaps at least one edge of the shape.
0127:             */
0128:            public boolean overEdgePixelPrecision(PrimitiveShape shape,
0129:                    Envelope env) {
0130:                if (shape.getNumPoints() < 2)
0131:                    return false;
0132:                Point point1 = shape.getPoint(shape.getNumPoints() - 1);
0133:                for (int i = 0; i < shape.getNumPoints(); i++) {
0134:                    Point point2 = shape.getPoint(i);
0135:
0136:                    Coordinate endPoint1 = new Coordinate(point1.getX(), point1
0137:                            .getY());
0138:                    Coordinate endPoint2 = new Coordinate(point2.getX(), point2
0139:                            .getY());
0140:                    if (overEdge(endPoint1, endPoint2, env))
0141:                        return true;
0142:                    point1 = point2;
0143:                }
0144:                return false;
0145:            }
0146:
0147:            /**
0148:             * Returns true if the envelope overlaps some part of the edge
0149:             *
0150:             * @param endPoint1 one end point of the edge
0151:             * @param endPoint2 the other end point of the edge
0152:             * @param env the reference envelope.
0153:             * @return true if the envelope overlaps some part of the edge
0154:             */
0155:            public boolean overEdge(Coordinate endPoint1, Coordinate endPoint2,
0156:                    Envelope env) {
0157:                boolean accept = false, done = false;
0158:                double x0 = endPoint1.x;
0159:                double y0 = endPoint1.y;
0160:                double x1 = endPoint2.x;
0161:                double y1 = endPoint2.y;
0162:
0163:                double xmin = env.getMinX();
0164:                double ymin = env.getMinY();
0165:                double xmax = env.getMaxX();
0166:                double ymax = env.getMaxY();
0167:
0168:                int outcode0 = compOutCode(x0, y0, xmin, xmax, ymin, ymax);
0169:                int outcode1 = compOutCode(x1, y1, xmin, xmax, ymin, ymax);
0170:                do {
0171:                    if (outcode0 == 0 || outcode1 == 0)
0172:                        accept = done = true;
0173:                    else if ((outcode0 & outcode1) != 0)
0174:                        done = true;
0175:                    else {
0176:                        double x, y;
0177:                        int outcodeOut = outcode0 > 0 ? outcode0 : outcode1;
0178:                        if ((outcodeOut & TOP) > 0) {
0179:                            x = x0 + (x1 - x0) * (ymax - y0) / (y1 - y0);
0180:                            y = ymax;
0181:                        } else if ((outcodeOut & BOTTOM) > 0) {
0182:                            x = x0 + (x1 - x0) * (ymin - y0) / (y1 - y0);
0183:                            y = ymin;
0184:                        } else if ((outcodeOut & RIGHT) > 0) {
0185:                            y = y0 + (y1 - y0) * (xmax - x0) / (x1 - x0);
0186:                            x = xmax;
0187:                        } else {
0188:                            y = y0 + (y1 - y0) * (xmin - x0) / (x1 - x0);
0189:                            x = xmin;
0190:                        }
0191:                        if (outcodeOut == outcode0) {
0192:                            double tmpx = x;
0193:                            double tmpy = y;
0194:                            outcode0 = compOutCode(tmpx, tmpy, xmin, xmax,
0195:                                    ymin, ymax);
0196:                        } else {
0197:                            double tmpx = x;
0198:                            double tmpy = y;
0199:                            outcode1 = compOutCode(tmpx, tmpy, xmin, xmax,
0200:                                    ymin, ymax);
0201:                        }
0202:                    }
0203:                } while (done == false);
0204:                return accept;
0205:            }
0206:
0207:            private int compOutCode(double x, double y, double xmin,
0208:                    double xmax, double ymin, double ymax) {
0209:                int outcode = 0;
0210:                if (y > ymax)
0211:                    outcode |= TOP;
0212:                if (y < ymin)
0213:                    outcode |= BOTTOM;
0214:                if (x > xmax)
0215:                    outcode |= RIGHT;
0216:                if (x < xmin)
0217:                    outcode |= LEFT;
0218:                return outcode;
0219:            }
0220:
0221:            /**
0222:             * Returns the index of the coordinate closest to the click.
0223:             * @param geometry the geometry to search for the closest coordinate;  The default geometry is searched.
0224:             * @param click the closest coordinate in <i>coordinates</i> will be found with respect to
0225:             *        <i>click</i>
0226:             * @param result the first position will be fill with the closest coordinate in geometry.
0227:             * 
0228:             * @return the index of the coordinate closest to the click.
0229:             */
0230:            public int getClosest(Geometry geometry, Coordinate click,
0231:                    Coordinate[] result) {
0232:                Coordinate[] coordinates = geometry.getCoordinates();
0233:                int prev = 0;
0234:                double mindist = Double.MAX_VALUE;
0235:                Coordinate closest = coordinates[coordinates.length - 1];
0236:                double x = click.x - closest.x;
0237:                double y = click.y - closest.y;
0238:                mindist = Math.sqrt(x * x + y * y);
0239:                for (int i = 0; i < coordinates.length; i++) {
0240:                    Coordinate point = coordinates[i];
0241:
0242:                    x = click.x - point.x;
0243:                    y = click.y - point.y;
0244:                    double dist = Math.sqrt(x * x + y * y);
0245:                    if (dist < mindist) {
0246:                        mindist = dist;
0247:                        prev = i;
0248:                        closest = point;
0249:                    }
0250:                }
0251:                result[0] = closest;
0252:                if (geometry instanceof  LinearRing) {
0253:                    return (prev == 0 || prev == coordinates.length) ? coordinates.length - 2
0254:                            : prev;
0255:                }
0256:                return prev != 0 ? prev - 1 : coordinates.length - 2;
0257:            }
0258:
0259:            /**
0260:             * Returns the closest point on the line between <code>vertex1</code> and <code>vertex2</code> to coordinate
0261:             * <code>src</code>
0262:             * <p>
0263:             * All Coordinates must be in the same CRS
0264:             * </p>
0265:             * 
0266:             * @param endPoint1 first vertex of a line.
0267:             * @param endPoint2 second vertex of a line.
0268:             * @param src the closes coordinate is found with respect to src.
0269:             * @return the closest point on the line between <code>vertex1</code> and <code>vertex2</code> to coordinate
0270:             * <code>src</code>
0271:             */
0272:            public Coordinate closestCoordinateOnEdge(Coordinate endPoint1,
0273:                    Coordinate endPoint2, Coordinate src) {
0274:                Coordinate v = new Coordinate();
0275:                v.x = endPoint2.x - endPoint1.x;
0276:                v.y = endPoint2.y - endPoint1.y;
0277:                double d = (v.x * v.x + v.y * v.y);
0278:                if (d == 0)
0279:                    return null;
0280:                double t = ((src.x - endPoint1.x) * v.x + (src.y - endPoint1.y)
0281:                        * v.y)
0282:                        / d;
0283:                if (t < 0 || t > 1 || Double.isInfinite(t) || Double.isNaN(t))
0284:                    return null;
0285:                Coordinate result = new Coordinate();
0286:                result.x = endPoint1.x + t * v.x;
0287:                result.y = endPoint1.y + t * v.y;
0288:                return result;
0289:            }
0290:
0291:            /**
0292:             * Returns the closest point on the line between <code>vertex1</code> and <code>vertex2</code> to coordinate
0293:             * <code>src</code>
0294:             * <p>
0295:             * All Coordinates must be in the same CRS
0296:             * </p>
0297:             * 
0298:             * @param endPoint1 first vertex of a line.
0299:             * @param endPoint2 second vertex of a line.
0300:             * @param src the closes coordinate is found with respect to src.
0301:             * @return the closest point on the line between <code>vertex1</code> and <code>vertex2</code> to coordinate
0302:             * <code>src</code>
0303:             */
0304:            public Point closestPointOnEdge(Point endPoint1, Point endPoint2,
0305:                    Point src) {
0306:                if (endPoint1.equals(src))
0307:                    return src;
0308:                if (endPoint2.equals(src))
0309:                    return src;
0310:
0311:                Point v = Point.valueOf(endPoint2.getX() - endPoint1.getX(),
0312:                        endPoint2.getY() - endPoint1.getY());
0313:                int i = v.getX() * v.getX() + v.getY() * v.getY();
0314:                if (i == 0)
0315:                    return null;
0316:                int j = (src.getX() - endPoint1.getX()) * v.getX();
0317:                int k = (src.getY() - endPoint1.getY()) * v.getY();
0318:                double t = (double) (j + k) / (double) i;
0319:                if (t >= 1)
0320:                    return endPoint2;
0321:                if (t <= 0)
0322:                    return endPoint1;
0323:
0324:                if (Double.isInfinite(t) || Double.isNaN(t))
0325:                    return null;
0326:
0327:                return Point.valueOf((int) (endPoint1.getX() + t * v.getX()),
0328:                        (int) (endPoint1.getY() + t * v.getY()));
0329:            }
0330:
0331:            /**
0332:             * Convenience method; transforms the click from the viewportModel CRS to the layer's CRS.
0333:             * 
0334:             * @param click
0335:             * @return
0336:             */
0337:            public Coordinate getTransformedClick(Coordinate click, ILayer layer) {
0338:
0339:                try {
0340:                    MathTransform transform = layer.mapToLayerTransform();
0341:                    if (transform == null || transform.isIdentity())
0342:                        return click;
0343:                    return JTS.transform(click, new Coordinate(), transform);
0344:                } catch (Exception e1) {
0345:                    // CorePlugin.log(ToolsPlugin.getDefault(), e1);
0346:                    return click;
0347:                }
0348:            }
0349:
0350:            /**
0351:             * Finds and returns the EditGeoms that intersect the point.  This maybe an expensive operation if there
0352:             * are a large number of EditGeoms each with many points.  The calculation is done in screen space, however,
0353:             * so the number of coordinates in the shapes do not matter so much.
0354:             *
0355:             * @param editBlackboard
0356:             * @param point
0357:             * @param treatUnknownAsPolygons
0358:             * @return
0359:             */
0360:            public List<EditGeom> getIntersectingGeom(
0361:                    EditBlackboard editBlackboard, Point point,
0362:                    boolean treatUnknownAsPolygons) {
0363:                List<EditGeom> geoms = editBlackboard.getGeoms();
0364:                List<EditGeom> result = new LinkedList<EditGeom>();
0365:                for (EditGeom geom : geoms) {
0366:                    EditGeomPathIterator iter = EditGeomPathIterator
0367:                            .getPathIterator(geom);
0368:                    iter.setPolygon(treatUnknownAsPolygons);
0369:                    if (iter.toShape().contains(point.getX(), point.getY()))
0370:                        result.add(geom);
0371:                }
0372:                return result;
0373:            }
0374:
0375:            /**
0376:             * Returns the coordinate that is on the grid intersection closest to the coordinate.
0377:             */
0378:            public Coordinate snapToGrid(Point centerPoint, IMap map) {
0379:                List<ILayer> layers = map.getMapLayers();
0380:
0381:                // by default choose something that will work
0382:                ILayer found = layers.get(0);
0383:                GridMapGraphic graphic = new GridMapGraphic();
0384:                for (ILayer layer : layers) {
0385:                    if (layer.hasResource(GridMapGraphic.class)) {
0386:                        found = layer;
0387:                        try {
0388:                            graphic = layer.getResource(GridMapGraphic.class,
0389:                                    ProgressManager.instance().get());
0390:                        } catch (IOException e) {
0391:                            throw (RuntimeException) new RuntimeException()
0392:                                    .initCause(e);
0393:                        }
0394:                        break;
0395:                    }
0396:                }
0397:
0398:                double[] closest;
0399:                try {
0400:                    closest = graphic.closest(centerPoint.getX(), centerPoint
0401:                            .getY(), found);
0402:                } catch (FactoryException e) {
0403:                    EditPlugin.log(null, e);
0404:                    throw (RuntimeException) new RuntimeException()
0405:                            .initCause(e);
0406:                }
0407:                return new Coordinate(closest[0], closest[1], 0);
0408:            }
0409:
0410:            /**
0411:             * Searches all the layers in the map and the EditBlackboard for the closest vertex to center point
0412:             * @param includeVerticesInCurrent indicates whether the vertices of the current feature should be considered.
0413:             * @param stateAfterSearch 
0414:             * @return the Point that is the closest vertex.
0415:             */
0416:            public Coordinate getClosestSnapPoint(EditToolHandler handler,
0417:                    EditBlackboard editBlackboard, Point centerPoint,
0418:                    boolean includeVerticesInCurrent,
0419:                    SnapBehaviour snapBehaviour, EditState stateAfterSearch) {
0420:
0421:                IToolContext context = handler.getContext();
0422:                MinFinder minFinder = new MinFinder(editBlackboard
0423:                        .toCoord(centerPoint));
0424:                SearchBoxAnimation anim = new SearchBoxAnimation(centerPoint,
0425:                        new IsBusyStateProvider(handler));
0426:
0427:                try {
0428:                    handler.setCurrentState(EditState.BUSY);
0429:                    if (snapBehaviour != SnapBehaviour.OFF
0430:                            && snapBehaviour != SnapBehaviour.GRID)
0431:                        AnimationUpdater
0432:                                .runTimer(context.getMapDisplay(), anim);
0433:                    switch (snapBehaviour) {
0434:                    case OFF:
0435:                        return null;
0436:                    case SELECTED:
0437:                        searchSelection(handler, editBlackboard, centerPoint,
0438:                                includeVerticesInCurrent, minFinder);
0439:                        return minFinder.getMinCoord();
0440:                    case CURRENT_LAYER:
0441:                        searchSelection(handler, editBlackboard, centerPoint,
0442:                                includeVerticesInCurrent, minFinder);
0443:                        minFinder.add(searchLayer(handler.getEditLayer(),
0444:                                context, centerPoint));
0445:                        break;
0446:                    case ALL_LAYERS:
0447:                        searchSelection(handler, editBlackboard, centerPoint,
0448:                                includeVerticesInCurrent, minFinder);
0449:                        for (ILayer layer : context.getMapLayers()) {
0450:                            minFinder.add(searchLayer(layer, context,
0451:                                    centerPoint));
0452:                        }
0453:                        break;
0454:                    case GRID:
0455:                        Coordinate worldCoord = snapToGrid(centerPoint, context
0456:                                .getMap());
0457:                        try {
0458:                            return JTS
0459:                                    .transform(
0460:                                            worldCoord,
0461:                                            null,
0462:                                            editBlackboard.pointCoordCalculator.mapToLayer);
0463:                        } catch (TransformException e) {
0464:                            return null;
0465:                        }
0466:                    default:
0467:                        break;
0468:                    }
0469:
0470:                    Coordinate min = minFinder.getMinCoord();
0471:                    try {
0472:                        if (min == null)
0473:                            return null;
0474:                        return JTS.transform(min, new Coordinate(),
0475:                                editBlackboard.pointCoordCalculator.mapToLayer);
0476:                    } catch (Exception e) {
0477:                        EditPlugin.log("", e); //$NON-NLS-1$
0478:                        return null;
0479:                    }
0480:                } finally {
0481:                    if (stateAfterSearch == EditState.BUSY)
0482:                        handler.setCurrentState(EditState.MODIFYING);
0483:                    else
0484:                        handler.setCurrentState(stateAfterSearch);
0485:                    anim.setValid(false);
0486:                }
0487:
0488:            }
0489:
0490:            /**
0491:             * Searches the editblackboard and adds the closest vertex to the minFinder
0492:             */
0493:            private void searchSelection(EditToolHandler handler,
0494:                    EditBlackboard editBlackboard, Point centerPoint,
0495:                    boolean includeVerticesInCurrent, MinFinder minFinder) {
0496:                Point point = editBlackboard.overVertex(centerPoint,
0497:                        PreferenceUtil.instance().getSnappingRadius(), true);
0498:
0499:                // the vertices in the current geometry should only be considered if inlcudeVerticesInCurrent is true
0500:                boolean containsNonCurrentShape = containsNonCurrentShape(
0501:                        point, editBlackboard, handler.getCurrentShape());
0502:                if (point != null
0503:                        && (includeVerticesInCurrent || containsNonCurrentShape))
0504:                    minFinder.add(editBlackboard.toCoord(point));
0505:            }
0506:
0507:            private boolean containsNonCurrentShape(Point p,
0508:                    EditBlackboard editBlackboard, PrimitiveShape currentShape) {
0509:                if (p == null || currentShape == null)
0510:                    return false;
0511:                List<EditGeom> geoms = editBlackboard.getGeoms(p.getX(), p
0512:                        .getY());
0513:                if (geoms.isEmpty())
0514:                    return false;
0515:                if (geoms.size() > 1
0516:                        || geoms.get(0) != currentShape.getEditGeom())
0517:                    return true;
0518:
0519:                return false;
0520:            }
0521:
0522:            /**
0523:             * Searches the layer for coordinates within snapping distance
0524:             *
0525:             * @param layer the layer to search.
0526:             * @param context the context to use for convenience methods
0527:             * @param centerPoint the current centerPoint.
0528:             * @return the closest vertex in the layer within the snapping radius or null.
0529:             */
0530:            private Coordinate searchLayer(ILayer layer, IToolContext context,
0531:                    Point centerPoint) {
0532:                if (!layer.hasResource(FeatureSource.class)
0533:                        || !layer.isApplicable(EditPlugin.ID)
0534:                        || !layer.isVisible())
0535:                    return null;
0536:
0537:                ILayer editLayer = context.getEditManager().getEditLayer();
0538:                Feature editFeature = context.getEditManager().getEditFeature();
0539:                String editFeatureID = null;
0540:                if (editFeature != null)
0541:                    editFeatureID = editFeature.getID();
0542:
0543:                Envelope bbox = context.getBoundingBox(new java.awt.Point(
0544:                        centerPoint.getX(), centerPoint.getY()), PreferenceUtil
0545:                        .instance().getSnappingRadius() * 2);
0546:                try {
0547:                    Coordinate tmp = context.pixelToWorld(centerPoint.getX(),
0548:                            centerPoint.getY());
0549:                    Coordinate layerCenter = JTS.transform(tmp,
0550:                            new Coordinate(), layer.mapToLayerTransform());
0551:                    FeatureCollection features = context.getFeaturesInBbox(
0552:                            layer, bbox);
0553:                    FeatureIterator iter = null;
0554:                    try {
0555:                        Coordinate closest = null;
0556:                        double minDist = Integer.MAX_VALUE;
0557:                        for (iter = features.features(); iter.hasNext();) {
0558:                            Feature feature = iter.next();
0559:                            if (feature.getID().equals(editFeatureID)
0560:                                    && layer == editLayer)
0561:                                continue;
0562:                            Coordinate[] result = new Coordinate[1];
0563:                            EditUtils.instance.getClosest(feature
0564:                                    .getDefaultGeometry(), layerCenter, result);
0565:                            double x = layerCenter.x - result[0].x;
0566:                            double y = layerCenter.y - result[0].y;
0567:                            double distNew = Math.sqrt(x * x + y * y);
0568:
0569:                            if (distNew < minDist) {
0570:                                closest = result[0];
0571:                                minDist = distNew;
0572:                            }
0573:                        }
0574:                        if (closest != null) {
0575:                            Coordinate inMapCoords = new Coordinate();
0576:                            JTS.transform(closest, inMapCoords, layer
0577:                                    .layerToMapTransform());
0578:                            java.awt.Point point = context
0579:                                    .worldToPixel(inMapCoords);
0580:
0581:                            double x = centerPoint.getX() - point.x;
0582:                            double y = centerPoint.getY() - point.y;
0583:                            double distNew = Math.sqrt(x * x + y * y);
0584:                            if (distNew < PreferenceUtil.instance()
0585:                                    .getSnappingRadius())
0586:                                return inMapCoords;
0587:                            else
0588:                                return null;
0589:                        }
0590:                    } finally {
0591:                        if (iter != null) {
0592:                            features.close(iter);
0593:                        }
0594:                    }
0595:                } catch (Exception e) {
0596:                    EditPlugin.log("", e); //$NON-NLS-1$
0597:                }
0598:                return null;
0599:            }
0600:
0601:            /**
0602:             * Keeps track of the point that is the minimum distance to the center point.
0603:             * @author Jesse
0604:             * @since 1.1.0
0605:             */
0606:            public static class MinFinder {
0607:                private Point centerPoint;
0608:                private Point currentMin;
0609:                private double distance;
0610:                private Coordinate centerCoord;
0611:                private Coordinate minCoord;
0612:
0613:                public MinFinder(Point centerPoint) {
0614:                    if (centerPoint == null)
0615:                        throw new NullPointerException(
0616:                                "centerPoint cannot be null"); //$NON-NLS-1$
0617:                    this .centerPoint = centerPoint;
0618:                }
0619:
0620:                public MinFinder(Coordinate coord) {
0621:                    this .centerCoord = coord;
0622:                }
0623:
0624:                public Point getMin() {
0625:                    return currentMin;
0626:                }
0627:
0628:                public Coordinate getMinCoord() {
0629:                    return minCoord;
0630:                }
0631:
0632:                public void add(Point p) {
0633:                    if (p == null || p.equals(centerPoint))
0634:                        return;
0635:                    if (currentMin == null) {
0636:                        currentMin = p;
0637:                        distance = dist(p);
0638:                        return;
0639:                    }
0640:
0641:                    double dist = dist(p);
0642:                    if (dist < distance) {
0643:                        currentMin = p;
0644:                        distance = dist;
0645:                    }
0646:
0647:                }
0648:
0649:                public double dist(Point p) {
0650:                    double x = centerPoint.getX() - p.getX();
0651:                    double y = centerPoint.getY() - p.getY();
0652:                    return Math.sqrt(x * x + y * y);
0653:                }
0654:
0655:                public void add(Coordinate p) {
0656:                    if (p == null || p.equals(centerCoord))
0657:                        return;
0658:                    if (minCoord == null) {
0659:                        minCoord = p;
0660:                        distance = dist(p);
0661:                        return;
0662:                    }
0663:
0664:                    double dist = dist(p);
0665:                    if (dist < distance) {
0666:                        minCoord = p;
0667:                        distance = dist;
0668:                    }
0669:
0670:                }
0671:
0672:                public double dist(Coordinate p) {
0673:                    double x = centerCoord.x - p.x;
0674:                    double y = centerCoord.y - p.y;
0675:                    return Math.sqrt(x * x + y * y);
0676:                }
0677:            }
0678:
0679:            /**
0680:             * Returns the intersection where the two lines meet
0681:             */
0682:            public Coordinate intersectingLines(Coordinate line1P1,
0683:                    Coordinate line1P2, Coordinate line2P1, Coordinate line2P2) {
0684:
0685:                double B1 = line1P1.x - line1P2.x;
0686:                double B2 = line2P1.x - line2P2.x;
0687:                double A1 = line1P2.y - line1P1.y;
0688:                double A2 = line2P2.y - line2P1.y;
0689:                double C1 = A1 * line1P1.x + B1 * line1P1.y;
0690:                double C2 = A2 * line2P1.x + B2 * line2P1.y;
0691:
0692:                double det = A1 * B2 - A2 * B1;
0693:                if (det == 0) {
0694:                    //Lines are parallel
0695:                    return null;
0696:                }
0697:                double x = (B2 * C1 - B1 * C2) / det;
0698:                double y = (A1 * C2 - A2 * C1) / det;
0699:
0700:                boolean onLine1 = Math.min(line1P1.x, line1P2.x) <= x
0701:                        && x <= Math.max(line1P1.x, line1P2.x)
0702:                        && Math.min(line1P1.y, line1P2.y) <= y
0703:                        && y <= Math.max(line1P1.y, line1P2.y);
0704:
0705:                boolean onLine2 = Math.min(line2P1.x, line2P2.x) <= x
0706:                        && x <= Math.max(line2P1.x, line2P2.x)
0707:                        && Math.min(line2P1.y, line2P2.y) <= y
0708:                        && y <= Math.max(line2P1.y, line2P2.y);
0709:
0710:                if (onLine1 && onLine2)
0711:                    return new Coordinate(x, y);
0712:
0713:                return null;
0714:            }
0715:
0716:            /**
0717:             * Returns the intersection where the two lines meet
0718:             */
0719:            public Point intersectingLines(Point line1P1, Point line1P2,
0720:                    Point line2P1, Point line2P2) {
0721:
0722:                int B1 = line1P1.getX() - line1P2.getX();
0723:                int B2 = line2P1.getX() - line2P2.getX();
0724:                int A1 = line1P2.getY() - line1P1.getY();
0725:                int A2 = line2P2.getY() - line2P1.getY();
0726:                int C1 = A1 * line1P1.getX() + B1 * line1P1.getY();
0727:                int C2 = A2 * line2P1.getX() + B2 * line2P1.getY();
0728:
0729:                double det = A1 * B2 - A2 * B1;
0730:                if (det == 0) {
0731:                    //Lines are parallel
0732:                    return null;
0733:                }
0734:                double x = (B2 * C1 - B1 * C2) / det;
0735:                double y = (A1 * C2 - A2 * C1) / det;
0736:
0737:                boolean onLine1 = Math.min(line1P1.getX(), line1P2.getX()) <= x
0738:                        && x <= Math.max(line1P1.getX(), line1P2.getX())
0739:                        && Math.min(line1P1.getY(), line1P2.getY()) <= y
0740:                        && y <= Math.max(line1P1.getY(), line1P2.getY());
0741:
0742:                boolean onLine2 = Math.min(line2P1.getX(), line2P2.getX()) <= x
0743:                        && x <= Math.max(line2P1.getX(), line2P2.getX())
0744:                        && Math.min(line2P1.getY(), line2P2.getY()) <= y
0745:                        && y <= Math.max(line2P1.getY(), line2P2.getY());
0746:
0747:                if (onLine1 && onLine2)
0748:                    return Point.valueOf((int) x, (int) y);
0749:
0750:                return null;
0751:            }
0752:
0753:            /**
0754:             * Reverse the order of the vertices in a Shape.  Used because the holes and shells in polygons have to 
0755:             * be in a particular order. 
0756:             * 
0757:             * @param shape
0758:             */
0759:            public void reverseOrder(PrimitiveShape shape) {
0760:                synchronized (shape.getEditBlackboard()) {
0761:                    shape.getMutator().reverse();
0762:                }
0763:            }
0764:
0765:            /**
0766:             * Appends the points defined in the PathIterator to the shape.  Currently curve segments are not supported
0767:             * and if there is a moveto in the middle of the iterator a hole will be created in the shape.
0768:             * If the GeomType is line or point then an exception will be thrown but otherwise the client code
0769:             * must ensure that the request makes sense.
0770:             * 
0771:             * @param iter The iterator to append
0772:             * @param shape the shape to append to.
0773:             * @return Commands that will append the points to the shape.  Nothing is done until commands are run. 
0774:             */
0775:            public UndoableComposite appendPathToShape(EditToolHandler handler,
0776:                    PathIterator iter, PrimitiveShape shape) {
0777:                EditBlackboard bb = shape.getEditBlackboard();
0778:                IBlockingProvider<PrimitiveShape> currentProvider = new StaticShapeProvider(
0779:                        shape);
0780:                return appendPathToShape(iter, shape.getEditGeom()
0781:                        .getShapeType(), handler, bb, currentProvider);
0782:            }
0783:
0784:            /**
0785:             * Appends the points defined in the PathIterator to the shape.  Currently curve segments are not supported
0786:             * and if there is a move to in the middle of the iterator a hole will be created in the shape.
0787:             * If the GeomType is line or point then an exception will be thrown but otherwise the client code
0788:             * must ensure that the request makes sense.
0789:             * 
0790:             * @param iter The iterator to append
0791:             * @param bb the editblackboard used to add coordinates
0792:             * @param currentProvider2 the shape provider that provides the shape to append the coordinates to
0793:             * @param shapeType the type of geometry that is expected from currentProvider.
0794:             * @return Commands that will append the points to the shape.  Nothing is done until commands are run. 
0795:             */
0796:            public UndoableComposite appendPathToShape(PathIterator iter,
0797:                    ShapeType shapeType, EditToolHandler handler,
0798:                    EditBlackboard bb,
0799:                    IBlockingProvider<PrimitiveShape> currentProvider2) {
0800:                IBlockingProvider<PrimitiveShape> currentProvider = currentProvider2;
0801:
0802:                List<UndoableMapCommand> commands = new ArrayList<UndoableMapCommand>();
0803:                commands.add(new StartBatchingCommand(bb));
0804:                float[] coords = new float[6];
0805:                boolean started = false;
0806:                float[] start = new float[2];
0807:                AddVertexCommand addVertexCommand = null;
0808:                while (!iter.isDone()) {
0809:                    int type = iter.currentSegment(coords);
0810:                    switch (type) {
0811:                    case PathIterator.SEG_MOVETO:
0812:                        if (!started) {
0813:                            started = true;
0814:                        } else {
0815:                            if (shapeType != ShapeType.POLYGON)
0816:                                throw new IllegalArgumentException(
0817:                                        "Holes can not to shapes that are not Polygons.  Current shape is a " + shapeType); //$NON-NLS-1$
0818:                            CreateAndSelectHoleCommand command = new CreateAndSelectHoleCommand(
0819:                                    currentProvider);
0820:                            currentProvider = command.getHoleProvider();
0821:                            commands.add(command);
0822:                        }
0823:                        start[0] = coords[0];
0824:                        start[1] = coords[1];
0825:                        // no break is intentional.  It has to fall through and add a vertext to the shape
0826:                    case PathIterator.SEG_LINETO:
0827:                        addVertexCommand = new AddVertexCommand(handler, bb,
0828:                                currentProvider, Point.valueOf((int) coords[0],
0829:                                        (int) coords[1]), false);
0830:                        addVertexCommand.setShowAnimation(false);
0831:                        commands.add(addVertexCommand);
0832:                        break;
0833:                    case PathIterator.SEG_CLOSE:
0834:                        if (!Point.valueOf((int) coords[0], (int) coords[1])
0835:                                .equals(
0836:                                        Point.valueOf((int) start[0],
0837:                                                (int) start[1]))) {
0838:                            addVertexCommand = new AddVertexCommand(handler,
0839:                                    bb, currentProvider, Point.valueOf(
0840:                                            (int) start[0], (int) start[1]),
0841:                                    false);
0842:                            addVertexCommand.setShowAnimation(false);
0843:                            commands.add(addVertexCommand);
0844:                        }
0845:                        break;
0846:                    default:
0847:                        throw new UnsupportedOperationException("not supported"); //$NON-NLS-1$
0848:
0849:                    }
0850:                    iter.next();
0851:                }
0852:
0853:                if (shapeType == ShapeType.POLYGON
0854:                        && addVertexCommand != null
0855:                        && !addVertexCommand.getPointToAdd().equals(
0856:                                Point.valueOf((int) start[0], (int) start[1]))) {
0857:                    commands.add(new AddVertexCommand(handler, bb,
0858:                            currentProvider, Point.valueOf((int) start[0],
0859:                                    (int) start[1]), false));
0860:                }
0861:
0862:                UndoableComposite undoableComposite = new UndoableComposite(
0863:                        commands);
0864:                undoableComposite.getFinalizerCommands().add(
0865:                        new FireEventsCommand(bb));
0866:                return undoableComposite;
0867:            }
0868:
0869:            private static class StartBatchingCommand extends AbstractCommand
0870:                    implements  UndoableMapCommand {
0871:                private EditBlackboard bb;
0872:
0873:                StartBatchingCommand(EditBlackboard bb) {
0874:                    this .bb = bb;
0875:                }
0876:
0877:                public void run(IProgressMonitor monitor) throws Exception {
0878:                    bb.startBatchingEvents();
0879:                }
0880:
0881:                public String getName() {
0882:                    return null;
0883:                }
0884:
0885:                public void rollback(IProgressMonitor monitor) throws Exception {
0886:                }
0887:
0888:            }
0889:
0890:            private static class FireEventsCommand extends AbstractCommand
0891:                    implements  UndoableMapCommand {
0892:                private EditBlackboard bb;
0893:
0894:                FireEventsCommand(EditBlackboard bb) {
0895:                    this .bb = bb;
0896:                }
0897:
0898:                public void run(IProgressMonitor monitor) throws Exception {
0899:                    bb.fireBatchedEvents();
0900:                }
0901:
0902:                public String getName() {
0903:                    return null;
0904:                }
0905:
0906:                public void rollback(IProgressMonitor monitor) throws Exception {
0907:                }
0908:
0909:            }
0910:
0911:            public static class StaticShapeProvider implements 
0912:                    IBlockingProvider<PrimitiveShape> {
0913:                private PrimitiveShape shape;
0914:
0915:                public StaticShapeProvider(PrimitiveShape shape) {
0916:                    this .shape = shape;
0917:                }
0918:
0919:                public PrimitiveShape get(IProgressMonitor monitor) {
0920:                    return shape;
0921:                }
0922:
0923:            }
0924:
0925:            public static class EditToolHandlerShapeProvider implements 
0926:                    IBlockingProvider<PrimitiveShape> {
0927:
0928:                private EditToolHandler handler;
0929:
0930:                public EditToolHandlerShapeProvider(EditToolHandler handler) {
0931:                    this .handler = handler;
0932:                }
0933:
0934:                public PrimitiveShape get(IProgressMonitor monitor) {
0935:                    return handler.getCurrentShape();
0936:                }
0937:
0938:            }
0939:
0940:            /**
0941:             * Provider for EditGeoms
0942:             * 
0943:             * @author jones
0944:             * @since 1.1.0
0945:             */
0946:            public static class StaticEditGeomProvider implements 
0947:                    IBlockingProvider<EditGeom> {
0948:
0949:                private EditGeom geom;
0950:
0951:                public StaticEditGeomProvider(EditGeom geom) {
0952:                    this .geom = geom;
0953:                }
0954:
0955:                public EditGeom get(IProgressMonitor monitor) {
0956:                    return geom;
0957:                }
0958:
0959:            }
0960:
0961:            public static Coordinate midPointOnLine(Coordinate coord,
0962:                    Coordinate coord2) {
0963:                double x = (coord.x + coord2.x) / 2;
0964:                double y = (coord.y + coord2.y) / 2;
0965:                return new Coordinate(x, y);
0966:            }
0967:
0968:            /**
0969:             * The framework stores the current shape and state on a layer when the currently selected layer changes.  This
0970:             * method clears that cache on the layers passed in.  This should be called when the tool is de-actived and when
0971:             * a cancel and accept is run.  This is so that the shapes can be garbage collected.
0972:             *
0973:             * @param layers
0974:             */
0975:            public void clearLayerStateShapeCache(Collection<ILayer> layers) {
0976:                for (ILayer layer : layers) {
0977:                    layer.getBlackboard().put(
0978:                            EditToolHandler.STORED_CURRENT_SHAPE, null);
0979:                    layer.getBlackboard().put(
0980:                            EditToolHandler.STORED_CURRENT_STATE, null);
0981:                }
0982:            }
0983:
0984:            /**
0985:             * When an edit is canceled the selected layer must be re-rendered because they were hidden by {@link #refreshLayer(ILayer, Feature, Envelope, boolean, boolean)}
0986:             * This method must be called in order to efficiently do that.
0987:             * 
0988:             * @see #refreshLayer(ILayer, Feature, Envelope, boolean, boolean)
0989:             *
0990:             * @param selectedLayer
0991:             */
0992:            public void cancelHideSelection(ILayer selectedLayer) {
0993:                if (selectedLayer == null)
0994:                    return;
0995:
0996:                IBlackboard properties = selectedLayer.getBlackboard();
0997:                if (!PreferenceUtil.instance().hideSelectedLayers()) {
0998:                    properties.put(
0999:                            ProjectBlackboardConstants.MAP__RENDERING_FILTER,
1000:                            null);
1001:                    ((ViewportPane) selectedLayer.getMap().getRenderManager()
1002:                            .getMapDisplay()).repaint();
1003:                    return;
1004:                }
1005:
1006:                Filter filter = (Filter) properties
1007:                        .get(ProjectBlackboardConstants.MAP__RENDERING_FILTER);
1008:                if (filter == null)
1009:                    return;
1010:                properties.put(
1011:                        ProjectBlackboardConstants.MAP__RENDERING_FILTER, null);
1012:
1013:                Envelope env = (Envelope) properties.get(EDIT_FEATURE_BOUNDS);
1014:                properties.put(EDIT_FEATURE_BOUNDS, null);
1015:                selectedLayer.refresh(env);
1016:            }
1017:
1018:            /**
1019:             * Triggers a re-render that hides the features on the {@link EditBlackboard}.
1020:             */
1021:            public void hideSelectedFeatures(EditToolHandler handler,
1022:                    ILayer selectedLayer) {
1023:                Envelope env = new Envelope();
1024:
1025:                Set<String> fids = new HashSet<String>();
1026:                for (EditGeom geom : handler.getEditBlackboard(selectedLayer)
1027:                        .getGeoms()) {
1028:                    if (env.isNull()) {
1029:                        env.init(geom.getShell().getEnvelope());
1030:                    } else {
1031:                        env.expandToInclude(geom.getShell().getEnvelope());
1032:                    }
1033:                    fids.add(geom.getFeatureIDRef().get());
1034:                }
1035:                EditUtils.instance.refreshLayer(selectedLayer, fids, env, true,
1036:                        true);
1037:            }
1038:
1039:            /**
1040:             * Sets the rendering hint on the layer so that the feature is hidden if hidefeature is true.  If hidefeature is false then
1041:             * the hint is reset all features are shown
1042:             * 
1043:             * <p>
1044:             *  {@link #cancelHideSelection(ILayer)} should be called if the edit is canceled. 
1045:             * </p>
1046:             * @see #cancelHideSelection(ILayer)
1047:             *
1048:             * @param selectedLayer
1049:             * @param feature
1050:             * @param refreshBounds the area to refresh (should be the the area of the feature).  May be null to refresh entire area.  Envelope should be in
1051:             * Layer coordinates.
1052:             * @param hidefeature
1053:             */
1054:            public void refreshLayer(ILayer selectedLayer, Feature feature,
1055:                    Envelope refreshBounds, boolean forceRefresh,
1056:                    boolean hidefeature) {
1057:                Set<String> fids = Collections.singleton(feature.getID());
1058:                refreshLayer(selectedLayer, fids, refreshBounds, forceRefresh,
1059:                        hidefeature);
1060:            }
1061:
1062:            /**
1063:             * Sets the rendering hint on the layer so that the feature is hidden if hidefeature is true.  If hidefeature is false then
1064:             * the hint is reset all features are shown
1065:             * 
1066:             * <p>
1067:             *  {@link #cancelHideSelection(ILayer)} should be called if the edit is cancelled. 
1068:             * </p>
1069:             * @see #cancelHideSelection(ILayer)
1070:             *
1071:             * @param selectedLayer the currently selected layer
1072:             * @param fids the Feature Ids of the features that have been selected
1073:             * @param refreshBounds the area to refresh (should be the the area of the features).  May be null to refresh entire area.  Envelope should be in
1074:             * Layer coordinates.
1075:             * @param hidefeature if true then the features are hidden otherwise they will be shown again.
1076:             */
1077:            public void refreshLayer(ILayer selectedLayer, Set<String> fids,
1078:                    Envelope refreshBounds, boolean forceRefresh,
1079:                    boolean hidefeature) {
1080:                if (selectedLayer == null)
1081:                    return;
1082:                IBlackboard properties = selectedLayer.getBlackboard();
1083:                if (!PreferenceUtil.instance().hideSelectedLayers()) {
1084:                    properties.put(
1085:                            ProjectBlackboardConstants.MAP__RENDERING_FILTER,
1086:                            null);
1087:                    ((ViewportPane) selectedLayer.getMap().getRenderManager()
1088:                            .getMapDisplay()).repaint();
1089:                    return;
1090:                }
1091:
1092:                if (!forceRefresh && fids.isEmpty())
1093:                    return;
1094:
1095:                FilterFactory filterFactory = FilterFactoryFinder
1096:                        .createFilterFactory();
1097:                boolean modified = false;
1098:                for (String fid : fids) {
1099:                    if (fid == null)
1100:                        continue;
1101:                    if (hidefeature) {
1102:                        setAffectedArea(refreshBounds, properties);
1103:
1104:                        modified = addFidToExcludeFilter(properties, fid,
1105:                                filterFactory)
1106:                                || modified;
1107:                    } else {
1108:                        //get area to refresh and refresh it
1109:                        modified = removeFidFromExcludeFilter(properties, fid,
1110:                                filterFactory)
1111:                                || modified;
1112:                    }
1113:                }
1114:
1115:                if ((refreshBounds != null || forceRefresh) && modified)
1116:                    selectedLayer.refresh(refreshBounds);
1117:            }
1118:
1119:            private boolean removeFidFromExcludeFilter(IBlackboard properties,
1120:                    String fid, FilterFactory filterFactory) {
1121:                Filter filter = (Filter) properties
1122:                        .get(ProjectBlackboardConstants.MAP__RENDERING_FILTER);
1123:                Filter f = Filter.ALL;
1124:                boolean modified = false;
1125:                if (filter instanceof  FidFilter) {
1126:                    FidFilter fidFilter = (FidFilter) filter;
1127:                    fidFilter.removeFid(fid);
1128:                    f = fidFilter;
1129:                    if (fidFilter.getFids().length == 0)
1130:                        f = Filter.ALL;
1131:
1132:                    modified = true;
1133:                } else {
1134:                    if (filter != null) {
1135:                        f = filterFactory.createFidFilter(fid).not();
1136:                        try {
1137:                            f = filterFactory.createLogicFilter(f, filter,
1138:                                    FilterType.LOGIC_OR);
1139:                        } catch (IllegalFilterException e) {
1140:                            throw (RuntimeException) new RuntimeException()
1141:                                    .initCause(e);
1142:                        }
1143:                        modified = true;
1144:                    }
1145:
1146:                }
1147:                if (f == Filter.ALL)
1148:                    f = null;
1149:                properties.put(
1150:                        ProjectBlackboardConstants.MAP__RENDERING_FILTER, f);
1151:
1152:                return modified;
1153:            }
1154:
1155:            private boolean addFidToExcludeFilter(IBlackboard properties,
1156:                    String fid, FilterFactory filterFactory) {
1157:                //get area to refresh and refresh it
1158:                Filter filter = (Filter) properties
1159:                        .get(ProjectBlackboardConstants.MAP__RENDERING_FILTER);
1160:
1161:                Filter f;
1162:                boolean modified = false;
1163:                if (filter instanceof  FidFilter) {
1164:                    FidFilter fidFilter = (FidFilter) filter;
1165:                    fidFilter.addFid(fid);
1166:                    f = fidFilter;
1167:                    modified = true;
1168:                } else {
1169:                    f = filterFactory.createFidFilter(fid);
1170:
1171:                    if (filter != null) {
1172:                        try {
1173:                            f = filterFactory.createLogicFilter(f, filter,
1174:                                    FilterType.LOGIC_OR);
1175:                        } catch (IllegalFilterException e) {
1176:                            throw (RuntimeException) new RuntimeException()
1177:                                    .initCause(e);
1178:                        }
1179:                    }
1180:                    modified = true;
1181:                }
1182:                properties.put(
1183:                        ProjectBlackboardConstants.MAP__RENDERING_FILTER, f);
1184:
1185:                return modified;
1186:            }
1187:
1188:            /**
1189:             * caches the bounds of all the refresh areas for {@link #cancelHideSelection(ILayer)} so it knows what area to refresh
1190:             *
1191:             * @param refreshBounds
1192:             * @param properties
1193:             */
1194:            private void setAffectedArea(Envelope refreshBounds,
1195:                    IBlackboard properties) {
1196:                Envelope bounds = (Envelope) properties
1197:                        .get(EDIT_FEATURE_BOUNDS);
1198:                if (refreshBounds == null) {
1199:                    bounds = null;
1200:                } else {
1201:                    if (bounds == null)
1202:                        bounds = new Envelope(refreshBounds);
1203:                    else {
1204:                        bounds.expandToInclude(refreshBounds);
1205:                    }
1206:                }
1207:
1208:                properties.put(EDIT_FEATURE_BOUNDS, bounds);
1209:            }
1210:
1211:            /**
1212:             * Returns the Geometry from the collection that the mouse is over/intersects
1213:             *
1214:             * @param geoms Geoms to search through
1215:             * @param location the location 
1216:             * @return the first geom that the location is over/intersects
1217:             */
1218:            public EditGeom getGeomWithMouseOver(Collection<EditGeom> geoms,
1219:                    Point location, boolean treatUnknownAsPolygon) {
1220:                EditGeom over = geoms.iterator().next();
1221:                for (EditGeom geom : geoms) {
1222:                    PrimitiveShapeIterator iter = PrimitiveShapeIterator
1223:                            .getPathIterator(geom.getShell());
1224:                    if (iter.toShape().contains(location.getX(),
1225:                            location.getY())) {
1226:                        over = geom;
1227:                        break;
1228:                    }
1229:                    ClosestEdge edge = geom.getShell().getClosestEdge(location,
1230:                            treatUnknownAsPolygon);
1231:                    if (edge != null
1232:                            && edge.getDistanceToEdge() <= PreferenceUtil
1233:                                    .instance().getVertexRadius()) {
1234:                        over = geom;
1235:                        break;
1236:                    }
1237:                }
1238:                return over;
1239:            }
1240:
1241:            /**
1242:             * Returns true if the shape has a self intersection.   
1243:             * Only checks the points not the coordinates there for it is quicker but less accurate.
1244:             *
1245:             * @param shape shape to test.
1246:             * @return true if the shape has a self intersection.
1247:             */
1248:
1249:            public boolean selfIntersection(PrimitiveShape shape) {
1250:                if (shape.getNumPoints() < 3)
1251:                    return false;
1252:                for (int i = 1; i < shape.getNumPoints(); i++) {
1253:                    Point last = shape.getPoint(i - 1);
1254:                    Point current = shape.getPoint(i);
1255:                    if (intersection(last, current, shape, i, shape
1256:                            .getNumPoints() - 1, false))
1257:                        return true;
1258:                }
1259:
1260:                return false;
1261:            }
1262:
1263:            /**
1264:             * Checks whether the edge from point1 to point2 intersects any edge in the shape from startIndex to the endIndex 
1265:             *
1266:             * @param point1 the first point in the reference edge
1267:             * @param point2 the second point in the reference edge
1268:             * @param shape the shape that is searched for intersections 
1269:             * @param startIndex the index in the shape of the point at which to start searching.  The point indicated will be the
1270:             * first point in the edge.
1271:             * @param endIndex the index to stop the search.  It is the index of the end point of the last edge to compare
1272:             * @return true if there is an intersection between the edge indicated by last,current and the shape
1273:             */
1274:            public boolean intersection(Point point1, Point point2,
1275:                    PrimitiveShape shape, int startIndex, int endIndex) {
1276:                return intersection(point1, point2, shape, startIndex,
1277:                        endIndex, true);
1278:            }
1279:
1280:            /**
1281:             * Checks whether the edge from point1 to point2 intersects any edge in the shape from startIndex to the endIndex 
1282:             *
1283:             * @param point1 the first point in the reference edge
1284:             * @param point2 the second point in the reference edge
1285:             * @param shape the shape that is searched for intersections 
1286:             * @param startIndex the index in the shape of the point at which to start searching.  The point indicated will be the
1287:             * first point in the edge.
1288:             * @param endIndex the index to stop the search.  It is the index of the end point of the last edge to compare
1289:             * @param referenceLineIntersections if true then if a line crosses one of the endpoints of the reference line
1290:             * it is not considered a intersection since it is the connecting point to the rest of the shape.
1291:             * @return true if there is an intersection between the edge indicated by last,current and the shape
1292:             */
1293:            private boolean intersection(Point point1, Point point2,
1294:                    PrimitiveShape shape, int startIndex, int endIndex,
1295:                    boolean referenceLineIntersections) {
1296:
1297:                for (int j = startIndex + 1; j < endIndex + 1; j++) {
1298:                    Point last2 = shape.getPoint(j - 1);
1299:                    Point current2 = shape.getPoint(j);
1300:                    if (last2.equals(point1))
1301:                        continue; // same edge so continue.
1302:
1303:                    if (linesParallel(point1, point2, last2, current2)) {
1304:                        if (sameDirection(point1, point2, last2, current2)
1305:                                && last2.equals(point2)) {
1306:                            return true;
1307:                        } else {
1308:                            // no intersection
1309:                            continue;
1310:                        }
1311:                    }
1312:
1313:                    Point intersectingLines = intersectingLines(point1, point2,
1314:                            last2, current2);
1315:                    if (intersectingLines != null) {
1316:                        Point endPoint2;
1317:                        Point endPoint1;
1318:                        if (referenceLineIntersections) {
1319:                            endPoint1 = point1;
1320:                            endPoint2 = point2;
1321:                        } else {
1322:                            endPoint1 = last2;
1323:                            endPoint2 = current2;
1324:                        }
1325:                        if (!intersectingLines.equals(endPoint1)
1326:                                && !intersectingLines.equals(endPoint2))
1327:                            return true;
1328:                    }
1329:                }
1330:
1331:                return false;
1332:            }
1333:
1334:            private boolean sameDirection(Point last, Point current,
1335:                    Point last2, Point current2) {
1336:                int dx1 = last.getX() - current.getX();
1337:                int dy1 = last.getY() - current.getY();
1338:                int dy2 = last2.getY() - current2.getY();
1339:                int dx2 = last2.getX() - current2.getX();
1340:                double length1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
1341:                double length2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
1342:
1343:                if (dx1 / length1 == dx2 / length2
1344:                        && dy1 / length1 == dy2 / length2) {
1345:                    return false;
1346:                }
1347:                return true;
1348:            }
1349:
1350:            private boolean linesParallel(Point line1P1, Point line1P2,
1351:                    Point line2P1, Point line2P2) {
1352:                int B1 = line1P1.getX() - line1P2.getX();
1353:                int B2 = line2P1.getX() - line2P2.getX();
1354:                int A1 = line1P2.getY() - line1P1.getY();
1355:                int A2 = line2P2.getY() - line2P1.getY();
1356:
1357:                double det = A1 * B2 - A2 * B1;
1358:                if (det == 0) {
1359:                    //Lines are parallel
1360:                    return true;
1361:                }
1362:                return false;
1363:            }
1364:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.