/*************************************************** * * cismet GmbH, Saarbruecken, Germany * * ... and it just works. * ****************************************************/ /* * SimpleMoveListener.java * * Created on 10. M\u00E4rz 2005, 10:10 */ package de.cismet.cismap.commons.gui.piccolo.eventlistener; import com.vividsolutions.jts.geom.*; import com.vividsolutions.jts.geom.util.AffineTransformation; import edu.umd.cs.piccolo.PNode; import edu.umd.cs.piccolo.event.PBasicInputEventHandler; import edu.umd.cs.piccolo.event.PInputEvent; import edu.umd.cs.piccolo.nodes.PPath; import edu.umd.cs.piccolo.util.PBounds; import edu.umd.cs.piccolox.event.PNotificationCenter; import edu.umd.cs.piccolox.util.PLocator; import java.awt.Color; import java.awt.Cursor; import java.awt.EventQueue; import java.awt.Frame; import java.awt.geom.Line2D; import java.awt.geom.Point2D; import java.util.Collection; import java.util.LinkedList; import de.cismet.cismap.commons.BoundingBox; import de.cismet.cismap.commons.features.DefaultFeatureCollection; import de.cismet.cismap.commons.features.Feature; import de.cismet.cismap.commons.features.Highlightable; import de.cismet.cismap.commons.features.PureNewFeature; import de.cismet.cismap.commons.features.RequestForNonreflectingFeature; import de.cismet.cismap.commons.features.RequestForUnaddableHandles; import de.cismet.cismap.commons.gui.MappingComponent; import de.cismet.cismap.commons.gui.piccolo.AddHandleDialog; import de.cismet.cismap.commons.gui.piccolo.PFeature; import de.cismet.cismap.commons.gui.piccolo.PHandle; import de.cismet.cismap.commons.gui.piccolo.eventlistener.actions.HandleDeleteAction; import de.cismet.cismap.commons.interaction.CismapBroker; import de.cismet.cismap.commons.tools.PFeatureTools; import de.cismet.math.geometry.StaticGeometryFunctions; import de.cismet.tools.gui.StaticSwingTools; /** * DOCUMENT ME! * * @author hell/nh * @version $Revision$, $Date$ */ public class SimpleMoveListener extends PBasicInputEventHandler { //~ Static fields/initializers --------------------------------------------- public static final String COORDINATES_CHANGED = "COORDINATES_CHANGED"; // NOI18N private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(SimpleMoveListener.class); private static Color COLOR_ADD_HANDLE = new Color(255, 0, 0, 150); private static Color COLOR_REFLECT_HANDLE = new Color(205, 133, 0, 150); //~ Instance fields -------------------------------------------------------- Highlightable highlighted = null; Object handleHighlightingStuff = new Object(); private final MappingComponent mappingComponent; private PFeature underlyingObject = null; private int entityPosition; private int ringPosition; private int coordPosition = 0; private double xCoord = -1.0d; private double yCoord = -1.0d; private float handleX = Float.MIN_VALUE; private float handleY = Float.MIN_VALUE; private PHandle newPointHandle = null; private PFeature pFeature = null; private PLocator locator = null; private PNode pointerAnnotation = new PNode(); private PPath snapRect = PPath.createRectangle(0.0f, 0.0f, 20.0f, 20.0f); private boolean annotationNodeVisible = false; private double underlyingObjectHalo = 0.0d; //~ Constructors ----------------------------------------------------------- /** * Creates a new instance of SimpleMoveListener. * * @param mc DOCUMENT ME! */ public SimpleMoveListener(final MappingComponent mc) { super(); this.mappingComponent = mc; mc.getCamera().addChild(pointerAnnotation); mc.getCamera().addChild(snapRect); snapRect.setStroke(null); snapRect.setTransparency(0.2f); snapRect.setVisible(false); } //~ Methods ---------------------------------------------------------------- @Override public void mouseMoved(final PInputEvent event) { final Runnable t = new Runnable() { @Override public void run() { try { underlyingObject = null; final Object o; if (underlyingObjectHalo > 0.0d) { o = PFeatureTools.getFirstValidObjectUnderPointer2( event, new Class[] { PFeature.class }, underlyingObjectHalo); } else { o = PFeatureTools.getFirstValidObjectUnderPointer( event, new Class[] { PFeature.class }); } if (o instanceof PFeature) { underlyingObject = (PFeature)o; } if (underlyingObject != null) { if (mappingComponent.getInteractionMode().equals(MappingComponent.SELECT) && ((!(underlyingObject.getFeature() instanceof RequestForUnaddableHandles) && mappingComponent.getHandleInteractionMode().equals( MappingComponent.ADD_HANDLE)) || (!(underlyingObject.getFeature() instanceof RequestForNonreflectingFeature) && mappingComponent.getHandleInteractionMode().equals( MappingComponent.REFLECT_POLYGON)))) { if ((mappingComponent.getFeatureCollection() instanceof DefaultFeatureCollection) && (((DefaultFeatureCollection)mappingComponent.getFeatureCollection()) .getSelectedFeatures().size() == 1)) { if (!newPointHandleExists()) { createNewPointHandle(); } if (!(underlyingObject.getFeature() instanceof RequestForUnaddableHandles) && mappingComponent.getHandleInteractionMode().equals( MappingComponent.ADD_HANDLE)) { newPointHandle.setPaint(COLOR_ADD_HANDLE); } else if (!(underlyingObject.getFeature() instanceof RequestForNonreflectingFeature) && mappingComponent.getHandleInteractionMode().equals( MappingComponent.REFLECT_POLYGON)) { newPointHandle.setPaint(COLOR_REFLECT_HANDLE); } if (event.isAltDown()) { handleX = (float)event.getPosition().getX(); handleY = (float)event.getPosition().getY(); } else { final Collection sel = ((DefaultFeatureCollection)mappingComponent.getFeatureCollection()) .getSelectedFeatures(); final Point2D trigger = event.getPosition(); final Point2D[] neighbours = getNearestNeighbours(trigger, sel); final Point2D p0 = neighbours[0]; final Point2D p1 = neighbours[1]; if ((p0 != null) && (p1 != null)) { if (event.isShiftDown()) { final Point2D p0i1 = new Point2D.Double(p0.getX(), p1.getY()); final Point2D p1i0 = new Point2D.Double(p1.getX(), p0.getY()); final Line2D lOrig = new Line2D.Double(p0, p1); final Line2D lInversed = new Line2D.Double(p0i1, p1i0); final Point2D erg = StaticGeometryFunctions.createIntersectionPoint( lOrig, lInversed); handleX = (float)erg.getX(); handleY = (float)erg.getY(); } else { final Point2D erg = StaticGeometryFunctions.createPointOnLine( p0, p1, trigger); handleX = (float)erg.getX(); handleY = (float)erg.getY(); } } boolean found = false; for (final Object po : mappingComponent.getHandleLayer().getChildrenReference()) { if ((po instanceof PHandle) && ((PHandle)po == newPointHandle)) { found = true; } } if (!found) { // EventQueue.invokeLater(new Runnable() { // public void run() { mappingComponent.getHandleLayer().addChild(newPointHandle); LOG.info("tempor\u00E4res Handle eingef\u00FCgt"); // NOI18N // } // }); } newPointHandle.relocateHandle(); } } } } xCoord = mappingComponent.getWtst() .getSourceX(event.getPosition().getX() - mappingComponent.getClip_offset_x()); yCoord = mappingComponent.getWtst() .getSourceY(event.getPosition().getY() - mappingComponent.getClip_offset_y()); refreshPointerAnnotation(event); postCoordinateChanged(); try { mappingComponent.getSnapHandleLayer().removeAllChildren(); } catch (Exception e) { if (LOG.isDebugEnabled()) { LOG.debug("Fehler beim entfernen der SnappingVisualisierung", e); // NOI18N } } if (mappingComponent.isVisualizeSnappingEnabled()) { final Point2D nearestPoint = PFeatureTools.getNearestPointInArea( mappingComponent, event.getCanvasPosition(), mappingComponent.isSnappingOnLineEnabled(), true); if (nearestPoint != null) { mappingComponent.getCamera().viewToLocal(nearestPoint); final PPath show = PPath.createEllipse((float)(nearestPoint.getX() - 3), (float)(nearestPoint.getY() - 3), (float)(6), (float)(6)); show.setPaint(new Color(0, 0, 0)); mappingComponent.getSnapHandleLayer().addChild(show); } if (mappingComponent.isVisualizeSnappingRectEnabled()) { snapRect.setVisible(true); snapRect.setPathToRectangle((int)event.getCanvasPosition().getX() - (mappingComponent.getSnappingRectSize() / 2), (int)event.getCanvasPosition().getY() - (mappingComponent.getSnappingRectSize() / 2), mappingComponent.getSnappingRectSize(), mappingComponent.getSnappingRectSize()); } else { snapRect.setVisible(false); } } } catch (Exception e) { LOG.info("Fehler beim Moven \u00FCber die Karte ", e); // NOI18N } handleHighlightingStuff(event); } }; EventQueue.invokeLater(t); } /** * DOCUMENT ME! * * @return DOCUMENT ME! */ private boolean newPointHandleExists() { return (locator != null) && (newPointHandle != null); } /** * DOCUMENT ME! */ private void createNewPointHandle() { if (LOG.isDebugEnabled()) { LOG.debug("create newPointHandle and Locator"); // NOI18N } locator = new PLocator() { @Override public double locateX() { return handleX; } @Override public double locateY() { return handleY; } }; newPointHandle = new PHandle(locator, mappingComponent) { @Override public void handleClicked(final PInputEvent e) { SimpleMoveListener.this.mouseClicked(e); // super.handleClicked(pInputEvent); } }; } /** * Sucht in der \u00FCbergebenen PFeature-Collection nach den zwei der Mausposition am n\u00E4chsten gelegenen * Eckpunkte EINES PFeatures. * * @param trigger Mausposition * @param sel Collection aller selektierter PFeatures * * @return Point2D-Array mit den zwei gefundenen Punkten */ private Point2D[] getNearestNeighbours(final Point2D trigger, final Collection sel) { Point2D start = null; Point2D end = null; double dist = Double.POSITIVE_INFINITY; for (final Object o : sel) { if (o instanceof Feature) { final Feature feature = (Feature)o; final PFeature pfeature = (PFeature)mappingComponent.getPFeatureHM().get(feature); if (pfeature != null) { final Geometry geometry = pfeature.getFeature().getGeometry(); if ((geometry instanceof Polygon) || (geometry instanceof LineString) || (geometry instanceof MultiPolygon)) { for (int entityIndex = 0; entityIndex < pfeature.getNumOfEntities(); entityIndex++) { for (int ringIndex = 0; ringIndex < pfeature.getNumOfRings(entityIndex); ringIndex++) { final float[] xp = pfeature.getXp(entityIndex, ringIndex); final float[] yp = pfeature.getYp(entityIndex, ringIndex); for (int i = 0; i < (xp.length - 1); i++) { final Point2D tmpStart = new Point2D.Double(xp[i], yp[i]); final Point2D tmpEnd = new Point2D.Double(xp[i + 1], yp[i + 1]); final double tmpDist = StaticGeometryFunctions.distanceToLine( tmpStart, tmpEnd, trigger); if (tmpDist < dist) { dist = tmpDist; start = tmpStart; end = tmpEnd; this.pFeature = pfeature; this.entityPosition = entityIndex; this.ringPosition = ringIndex; this.coordPosition = i + 1; } } } } } } } } final Point2D[] erg = { start, end }; return erg; } /** * DOCUMENT ME! * * @param event DOCUMENT ME! */ private void handleHighlightingStuff(final PInputEvent event) { synchronized (handleHighlightingStuff) { final Object o = PFeatureTools.getFirstValidObjectUnderPointer( event, new Class[] { PFeature.class, PHandle.class }); try { final PNode n = (PNode)o; setMouseCursorAccordingToMode(n); if (n instanceof Highlightable) { if (highlighted != null) { highlighted.setHighlighting(false); } ((Highlightable)n).setHighlighting(true); highlighted = (Highlightable)n; } else if (highlighted != null) { highlighted.setHighlighting(false); } } catch (Exception e) { LOG.warn("Fehler beim Highlighten", e); // NOI18N } } } /** * DOCUMENT ME! */ private void postCoordinateChanged() { final PNotificationCenter pn = PNotificationCenter.defaultCenter(); pn.postNotification(COORDINATES_CHANGED, this); } /** * Falls die \u00FCbergebene PNode ein PHandle ist wir der Cursor entsprechend dem in der MappingComponent gesetzen * Modus gesetzt. * * @param n PNode-Instanz */ private void setMouseCursorAccordingToMode(final PNode n) { Cursor c = null; if (n instanceof PHandle) { c = mappingComponent.getCursor(mappingComponent.getHandleInteractionMode()); } else { c = mappingComponent.getCursor(mappingComponent.getInteractionMode()); } if ((c != null) && (mappingComponent.getCursor() != c)) { mappingComponent.setCursor(c); } } @Override public void mouseClicked(final PInputEvent event) { try { if ((event.getClickCount() == 2) && mappingComponent.getInteractionMode().equals(MappingComponent.SELECT) && ((pFeature != null) && pFeature.isSelected())) { // Selektiertes Feature holen final Collection sel = ((DefaultFeatureCollection)mappingComponent.getFeatureCollection()) .getSelectedFeatures(); if (!(pFeature.getFeature() instanceof RequestForUnaddableHandles) && mappingComponent.getHandleInteractionMode().equals(MappingComponent.ADD_HANDLE)) { // markiertes Handel auf der Linie holen final Point2D newPoint = new Point2D.Float(handleX, handleY); final Point2D[] neighbours = getNearestNeighbours(newPoint, sel); final Point2D p0 = neighbours[0]; final Point2D p1 = neighbours[1]; if ((p0 != null) && (p1 != null)) { // CTRL-Taste beim Klicken gedrückt if (event.isControlDown()) { // welcher Nachbar ist weiter Links/Rechts? Point2D leftNeighbour; Point2D rightNeighbour; if (p0.getX() < p1.getX()) { // Nachbar "0" ist weiter Links leftNeighbour = p0; rightNeighbour = p1; } else if (p1.getX() < p0.getX()) { // Nachbar "1" ist weiter Links leftNeighbour = p1; rightNeighbour = p0; } else // Nachbar "0" und "1" liegen genau übereinander { if (p0.getY() <= p1.getY()) { // Nachbar "0" ist weiter oben (wird als weiter Links interpretiert) leftNeighbour = p0; rightNeighbour = p1; } else { // Nachbar "1" ist weiter oben (wird als weiter Links interpretiert) leftNeighbour = p1; rightNeighbour = p0; } } // Abstand zum linken Nachbar berechnen Double distanceLeft = leftNeighbour.distance(newPoint); if (LOG.isDebugEnabled()) { LOG.debug("distanceLeft: " + distanceLeft); // NOI18N } // Gesamt-Abstand berechnen final Double distanceTotal = leftNeighbour.distance(rightNeighbour); if (LOG.isDebugEnabled()) { LOG.debug("distanceTotal: " + distanceTotal); // NOI18N } // Dialog modal aufrufen und Werte übergeben final AddHandleDialog dialog = AddHandleDialog.getInstance(); dialog.setDistanceTotal(distanceTotal); dialog.setDistanceToLeft(distanceLeft); // Dialog zentrieren und sichtbar machen StaticSwingTools.showDialog(dialog); // wenn der Dialog mit OK geschlossen wurde if (dialog.getReturnStatus() == AddHandleDialog.STATUS_OK) { // ist der gewählte Punkt näher an Links if (dialog.getDistanceToLeft() < dialog.getDistanceToRight()) { // Abstand von Links aus berechnen distanceLeft = dialog.getDistanceToLeft(); } else { // ist der gewählte Punkt nächer an rechts // Abstand von Rechts aus berechnen distanceLeft = distanceTotal - dialog.getDistanceToRight(); } // Abstand kann nicht größer als Gesamt-Abstand sein distanceLeft = (distanceLeft > distanceTotal) ? distanceTotal : distanceLeft; // ist die Gesamtlänge ungleich 0 (division durch null verhindern) if (distanceTotal != 0) { // Handle-Koordinaten anhand des Abstands berechnen handleX = (float)(leftNeighbour.getX() + ((rightNeighbour.getX() - leftNeighbour.getX()) * (distanceLeft / distanceTotal))); handleY = (float)(leftNeighbour.getY() + ((rightNeighbour.getY() - leftNeighbour.getY()) * (distanceLeft / distanceTotal))); } else { // ist die Gesamtlänge 0 // Handle-Koordinaten sind gleich die des linken Nachbarn handleX = (float)leftNeighbour.getX(); handleY = (float)leftNeighbour.getY(); } } else { // wenn der Dialog nicht mit OK geschlossen wurde // nichts tun super.mouseClicked(event); return; } } } pFeature.insertCoordinate(entityPosition, ringPosition, coordPosition, handleX, handleY); } else if (!(pFeature.getFeature() instanceof RequestForNonreflectingFeature) && mappingComponent.getHandleInteractionMode().equals(MappingComponent.REFLECT_POLYGON)) { reflectFeature(pFeature, entityPosition, ringPosition, coordPosition - 1, coordPosition); } mappingComponent.getMemRedo().clear(); resetAfterClick(); } } catch (Exception ex) { LOG.error("Fehler beim Anlegen von neuer Koordinate und Handle", ex); // NOI18N } super.mouseClicked(event); } /** * DOCUMENT ME! * * @param pf DOCUMENT ME! * @param entityPosition DOCUMENT ME! * @param ringPosition DOCUMENT ME! * @param leftCoordPosition DOCUMENT ME! * @param rightCoordPosition DOCUMENT ME! */ private void reflectFeature(final PFeature pf, final int entityPosition, final int ringPosition, final int leftCoordPosition, final int rightCoordPosition) { final Geometry origGeom = pf.getFeature().getGeometry(); final Coordinate[] coordArr = pf.getCoordArr(entityPosition, ringPosition); final Geometry reflectGeom = AffineTransformation.reflectionInstance( coordArr[leftCoordPosition].x, coordArr[leftCoordPosition].y, coordArr[rightCoordPosition].x, coordArr[rightCoordPosition].y) .transform(origGeom); final PureNewFeature reflectFeature = new PureNewFeature(reflectGeom); final PureNewFeature.geomTypes geomType; if (pf.getFeature() instanceof PureNewFeature) { final PureNewFeature origPureNewFeature = (PureNewFeature)pf.getFeature(); geomType = origPureNewFeature.getGeometryType(); } else if (reflectGeom instanceof MultiPolygon) { geomType = PureNewFeature.geomTypes.MULTIPOLYGON; } else if (reflectGeom instanceof Polygon) { geomType = PureNewFeature.geomTypes.POLYGON; } else if (reflectGeom instanceof LineString) { geomType = PureNewFeature.geomTypes.LINESTRING; } else if (reflectGeom instanceof Point) { geomType = PureNewFeature.geomTypes.POINT; } else { geomType = PureNewFeature.geomTypes.UNKNOWN; } reflectFeature.setGeometryType(geomType); reflectFeature.setEditable(true); mappingComponent.getFeatureCollection().addFeature(reflectFeature); mappingComponent.getFeatureCollection().holdFeature(reflectFeature); } /** * Setzt den Listener nach einem erfolgreichen Einf\u00FCgen eines neuen Punkts wieder auf den Anfangszustand * zur\u00FCck. */ private void resetAfterClick() { newPointHandle = null; handleX = Float.MIN_VALUE; handleY = Float.MIN_VALUE; coordPosition = 0; pFeature = null; } @Override public void mouseDragged(final PInputEvent event) { super.mouseDragged(event); refreshPointerAnnotation(event); } @Override public void mouseEntered(final PInputEvent event) { super.mouseEntered(event); if (pointerAnnotation != null) { refreshPointerAnnotation(event); pointerAnnotation.setVisible(annotationNodeVisible); } } @Override public void mouseExited(final PInputEvent event) { super.mouseExited(event); if (pointerAnnotation != null) { pointerAnnotation.setVisible(false); } } /** * DOCUMENT ME! * * @param event DOCUMENT ME! */ private void refreshPointerAnnotation(final PInputEvent event) { if (pointerAnnotation != null) { pointerAnnotation.setOffset(event.getCanvasPosition().getX() + 20.0d, event.getCanvasPosition().getY() + 20.0d); } } /** * DOCUMENT ME! * * @return DOCUMENT ME! */ public double getXCoord() { return xCoord; } /** * DOCUMENT ME! * * @return DOCUMENT ME! */ public double getYCoord() { return yCoord; } /** * DOCUMENT ME! * * @return DOCUMENT ME! */ public double getCurrentOGCScale() { return mappingComponent.getCurrentOGCScale(); } /** * DOCUMENT ME! * * @return DOCUMENT ME! */ public PFeature getUnderlyingPFeature() { return underlyingObject; } /** * DOCUMENT ME! * * @return DOCUMENT ME! */ public boolean isAnnotationNodeVisible() { return annotationNodeVisible; } /** * DOCUMENT ME! * * @param annotationNodeVisible DOCUMENT ME! */ public void setAnnotationNodeVisible(final boolean annotationNodeVisible) { this.annotationNodeVisible = annotationNodeVisible; if (this.pointerAnnotation != null) { try { mappingComponent.getCamera().removeChild(this.pointerAnnotation); } catch (final Exception ex) { if (LOG.isDebugEnabled()) { LOG.debug("no child to remove", ex); } } if (annotationNodeVisible) { mappingComponent.getCamera().addChild(pointerAnnotation); pointerAnnotation.setVisible(true); } } } /** * DOCUMENT ME! * * @return DOCUMENT ME! */ public PNode getPointerAnnotation() { return pointerAnnotation; } /** * DOCUMENT ME! * * @param pointerAnnotation DOCUMENT ME! */ public void setPointerAnnotation(final PNode pointerAnnotation) { setAnnotationNodeVisible(false); this.pointerAnnotation = pointerAnnotation; } /** * DOCUMENT ME! * * @param underlyingObjectHalo DOCUMENT ME! */ public void setUnderlyingObjectHalo(final double underlyingObjectHalo) { this.underlyingObjectHalo = underlyingObjectHalo; } /** * DOCUMENT ME! * * @return DOCUMENT ME! */ public double getUnderlyingObjectHalo() { return underlyingObjectHalo; } }