/*************************************************** * * cismet GmbH, Saarbruecken, Germany * * ... and it just works. * ****************************************************/ package de.cismet.cismap.commons.gui.piccolo.eventlistener; import com.vividsolutions.jts.geom.Coordinate; 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.piccolox.event.PNotificationCenter; import java.awt.Color; import java.awt.geom.Point2D; import java.lang.reflect.Constructor; import java.util.Arrays; import java.util.List; import de.cismet.cismap.commons.WorldToScreenTransform; import de.cismet.cismap.commons.features.DefaultFeatureCollection; import de.cismet.cismap.commons.features.Feature; import de.cismet.cismap.commons.features.FeatureCollectionEvent; import de.cismet.cismap.commons.features.FeatureCollectionListener; import de.cismet.cismap.commons.features.PureNewFeature; import de.cismet.cismap.commons.features.StyledFeature; import de.cismet.cismap.commons.gui.MappingComponent; import de.cismet.cismap.commons.gui.piccolo.EllipsePHandle; import de.cismet.cismap.commons.gui.piccolo.FixedWidthStroke; import de.cismet.cismap.commons.gui.piccolo.eventlistener.actions.FeatureDeleteAction; import de.cismet.cismap.commons.tools.PFeatureTools; import de.cismet.tools.collections.TypeSafeCollections; /** * DOCUMENT ME! * * @author thorsten.hell@cismet.de * @author srichter * @version $Revision$, $Date$ */ public class MessenGeometryListener extends PBasicInputEventHandler implements FeatureCollectionListener { //~ Static fields/initializers --------------------------------------------- public static final String LINESTRING = "LINESTRING"; public static final String POINT = "POINT"; public static final String POLYGON = "POLYGON"; public static final String RECTANGLE = "BOUNDING_BOX"; public static final String ELLIPSE = "ELLIPSE"; private static final int NUMOF_ELLIPSE_EDGES = 36; public static final String GEOMETRY_CREATED_NOTIFICATION = "GEOMETRY_CREATED_NOTIFICATION"; private static final Color PAINT_COLOR = new Color(255, 0, 255, 45); //~ Instance fields -------------------------------------------------------- protected Point2D startPoint; protected PPath tempFeature; protected MappingComponent mc; protected boolean inProgress; private final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(this.getClass()); private final List<Point2D> points = TypeSafeCollections.newArrayList(); private final List<Point2D> undoPoints = TypeSafeCollections.newArrayList(); private int numOfEllipseEdges; private SimpleMoveListener moveListener; private String mode = POLYGON; private Class<? extends PureNewFeature> geometryFeatureClass = null; private final PBasicInputEventHandler zoomDelegate; private Feature latestCreation = null; //~ Constructors ----------------------------------------------------------- /** * Creates a new MessenGeometryListener object. * * @param mc DOCUMENT ME! */ public MessenGeometryListener(final MappingComponent mc) { this(mc, PureNewFeature.class); } /** * Creates a new instance of CreateGeometryListener. * * @param mc DOCUMENT ME! * @param geometryFeatureClass DOCUMENT ME! */ protected MessenGeometryListener(final MappingComponent mc, final Class geometryFeatureClass) { setGeometryFeatureClass(geometryFeatureClass); zoomDelegate = new RubberBandZoomListener(); this.mc = mc; moveListener = (SimpleMoveListener)mc.getInputListener(MappingComponent.MOTION); undoPoints.clear(); // srichter: fehlerpotential! this referenz eines nicht fertig initialisieren Objekts wieder nach aussen // geliefert! loesungsvorschlag: createInstance-methode, welche den aufruf nach dem erzeugen ausfuehrt. mc.getFeatureCollection().addFeatureCollectionListener(this); } //~ Methods ---------------------------------------------------------------- /** * DOCUMENT ME! * * @param m DOCUMENT ME! * * @throws IllegalArgumentException DOCUMENT ME! */ public void setMode(final String m) throws IllegalArgumentException { if (m.equals(LINESTRING) || m.equals(POINT) || m.equals(POLYGON) || m.equals(ELLIPSE) || m.equals(RECTANGLE)) { this.mode = m; mc.getTmpFeatureLayer().removeAllChildren(); inProgress = false; } else { throw new IllegalArgumentException("Mode:" + m + " is not a valid Mode in this Listener."); } } /** * DOCUMENT ME! * * @param pInputEvent DOCUMENT ME! */ @Override public void mouseMoved(final edu.umd.cs.piccolo.event.PInputEvent pInputEvent) { super.mouseMoved(pInputEvent); if (moveListener != null) { moveListener.mouseMoved(pInputEvent); } else { log.warn("Movelistener zur Abstimmung der Mauszeiger nicht gefunden."); } if (inProgress) { // && (!isInMode(POINT))) { Point2D point = null; if (mc.isSnappingEnabled()) { final boolean vertexRequired = mc.isSnappingOnLineEnabled(); point = PFeatureTools.getNearestPointInArea( mc, pInputEvent.getCanvasPosition(), vertexRequired, true); } if (point == null) { point = pInputEvent.getPosition(); } updatePolygon(point); } } /** * DOCUMENT ME! * * @param pInputEvent DOCUMENT ME! */ @Override public void mousePressed(final edu.umd.cs.piccolo.event.PInputEvent pInputEvent) { super.mouseClicked(pInputEvent); if (mc.isReadOnly()) { ((DefaultFeatureCollection)(mc.getFeatureCollection())).removeFeaturesByInstance(Feature.class); } if (isInMode(POINT)) { if (pInputEvent.isLeftMouseButton()) { Point2D point = null; if (mc.isSnappingEnabled()) { final boolean vertexRequired = mc.isSnappingOnLineEnabled(); point = PFeatureTools.getNearestPointInArea( mc, pInputEvent.getCanvasPosition(), vertexRequired, true); } if (point == null) { point = pInputEvent.getPosition(); } try { final Constructor<? extends PureNewFeature> c = geometryFeatureClass.getConstructor( Point2D.class, WorldToScreenTransform.class); final PureNewFeature pnf = c.newInstance(point, mc.getWtst()); applyCurrentStyle(pnf); pnf.setGeometryType(PureNewFeature.geomTypes.POINT); finishGeometry(pnf); } catch (Throwable t) { log.error("Fehler beim Erzeugen der Geometrie: " + geometryFeatureClass, t); } } } else if (isInMode(RECTANGLE)) { if (!inProgress) { tempFeature = initTempFeature(true); mc.getTmpFeatureLayer().addChild(tempFeature); startPoint = pInputEvent.getPosition(); } } else if (isInMode(ELLIPSE)) { if (!inProgress) { tempFeature = initTempFeature(true); mc.getTmpFeatureLayer().addChild(tempFeature); startPoint = pInputEvent.getPosition(); } } else if (isInMode(POLYGON) || isInMode(LINESTRING)) { if (pInputEvent.getClickCount() == 1) { Point2D point = null; undoPoints.clear(); if (mc.isSnappingEnabled()) { final boolean vertexRequired = mc.isSnappingOnLineEnabled(); point = PFeatureTools.getNearestPointInArea( mc, pInputEvent.getCanvasPosition(), vertexRequired, true); } if (point == null) { point = pInputEvent.getPosition(); } if (!inProgress) { if (isInMode(POLYGON)) { tempFeature = initTempFeature(true); } else { tempFeature = initTempFeature(false); } mc.getTmpFeatureLayer().addChild(tempFeature); // Polygon erzeugen points.clear(); // Ersten Punkt anlegen startPoint = point; points.add(startPoint); if (latestCreation != null) { mc.getFeatureCollection().removeFeature(latestCreation); } inProgress = true; } else { // Zus\u00E4tzlichen Punkt anlegen points.add(point); updatePolygon(null); } } else if (pInputEvent.getClickCount() == 2) { // Anlegen des neuen PFeatures try { final Constructor<? extends PureNewFeature> c = geometryFeatureClass.getConstructor( Point2D[].class, WorldToScreenTransform.class); final Point2D[] p = getFinalPoints(null); if (log.isDebugEnabled()) { log.debug("Anzahl Punkte:" + p.length + " (" + Arrays.deepToString(p) + ")"); } final PureNewFeature pnf = c.newInstance(p, mc.getWtst()); applyCurrentStyle(pnf); if (isInMode(POLYGON)) { pnf.setGeometryType(PureNewFeature.geomTypes.POLYGON); } else if (isInMode(LINESTRING)) { pnf.setGeometryType(PureNewFeature.geomTypes.LINESTRING); } finishGeometry(pnf); } catch (Throwable t) { log.error("Fehler beim Erzeugen der Geometrie: " + geometryFeatureClass, t); } inProgress = false; } } } /** * DOCUMENT ME! * * @param arg0 DOCUMENT ME! */ @Override public void mouseReleased(final PInputEvent arg0) { super.mouseReleased(arg0); if (isInMode(RECTANGLE) || isInMode(ELLIPSE)) { if (inProgress) { try { final Constructor<? extends PureNewFeature> c = geometryFeatureClass.getConstructor( Point2D[].class, WorldToScreenTransform.class); final Point2D[] p = getFinalPoints(null); if (log.isDebugEnabled()) { log.debug("Anzahl Punkte:" + p.length + " (" + Arrays.deepToString(p) + ")"); } final PureNewFeature pnf = c.newInstance(p, mc.getWtst()); applyCurrentStyle(pnf); if (isInMode(RECTANGLE)) { pnf.setGeometryType(PureNewFeature.geomTypes.RECTANGLE); } else { pnf.setGeometryType(PureNewFeature.geomTypes.ELLIPSE); } finishGeometry(pnf); } catch (Throwable ex) { log.error("", ex); } inProgress = false; } } } /** * DOCUMENT ME! * * @param event DOCUMENT ME! */ @Override public void keyPressed(final edu.umd.cs.piccolo.event.PInputEvent event) { if (inProgress) { if (!event.isControlDown() && (points.size() > 0)) { // Strg nicht gedr\u00FCckt undoPoints.add(points.get(points.size() - 1)); points.remove(points.size() - 1); // keine Punkte mehr vorhanden? Stoppe erstellen if (points.size() == 0) { startPoint = null; mc.getTmpFeatureLayer().removeAllChildren(); inProgress = false; } if (log.isDebugEnabled()) { log.debug("Backspace gedr\u00FCckt: letzter eingef\u00FCgter Punkt gel\u00F6scht."); } updatePolygon(null); } else if (event.isControlDown()) { // Strg gedr\u00FCckt if (!undoPoints.isEmpty()) { points.add(undoPoints.remove(undoPoints.size() - 1)); if (log.isDebugEnabled()) { log.debug("Backspace + STRG gedr\u00FCckt: letzter gel\u00F6schter Punkt wiederhergestellt."); } updatePolygon(null); } } } else if (!inProgress && points.isEmpty() && event.isControlDown()) { if (log.isDebugEnabled()) { log.debug("Versuche Polygon und Startpunkt wiederherzustellen"); } tempFeature = initTempFeature(true); mc.getTmpFeatureLayer().addChild(tempFeature); // Ersten Punkt anlegen startPoint = undoPoints.remove(undoPoints.size() - 1); points.add(startPoint); inProgress = true; } } /** * DOCUMENT ME! * * @param m DOCUMENT ME! * @param f DOCUMENT ME! */ private void createAction(final MappingComponent m, final PureNewFeature f) { mc.getMemUndo().addAction(new FeatureDeleteAction(m, f)); mc.getMemRedo().clear(); } /** * DOCUMENT ME! * * @return DOCUMENT ME! */ protected Color getFillingColor() { return PAINT_COLOR; } /** * DOCUMENT ME! * * @param toProcess DOCUMENT ME! */ private void applyCurrentStyle(final Feature toProcess) { if (toProcess instanceof StyledFeature) { final StyledFeature sf = (StyledFeature)toProcess; sf.setFillingPaint(PAINT_COLOR); sf.setLinePaint(PAINT_COLOR.darker()); sf.setTransparency((PAINT_COLOR.getTransparency() / 255.0f)); } } /** * DOCUMENT ME! * * @param lastPoint DOCUMENT ME! */ protected void updatePolygon(final Point2D lastPoint) { final Point2D[] p = getPoints(lastPoint); Feature pnf = null; try { final Constructor c = geometryFeatureClass.getConstructor(Point2D[].class, WorldToScreenTransform.class); pnf = (Feature)c.newInstance(p, mc.getWtst()); applyCurrentStyle(pnf); } catch (Throwable t) { log.error("Fehler beim Erzeugen der Geometrie", t); } // pnf=new PureNewFeature(p,mc.getWtst()); final List<Feature> v = TypeSafeCollections.newArrayList(1); v.add(pnf); if (log.isDebugEnabled()) { log.debug("hinzugefĆ¼gt:" + pnf); } ((DefaultFeatureCollection)mc.getFeatureCollection()).fireFeaturesChanged(v); tempFeature.setPathToPolyline(p); tempFeature.repaint(); } /** * DOCUMENT ME! * * @param lastPoint DOCUMENT ME! * * @return DOCUMENT ME! */ protected Point2D[] getFinalPoints(final Point2D lastPoint) { return getPoints(true, lastPoint); } /** * DOCUMENT ME! * * @param lastPoint DOCUMENT ME! * * @return DOCUMENT ME! */ protected Point2D[] getPoints(final Point2D lastPoint) { return getPoints(false, lastPoint); } /** * DOCUMENT ME! * * @param isFinal DOCUMENT ME! * @param lastPoint DOCUMENT ME! * * @return DOCUMENT ME! */ protected Point2D[] getPoints(final boolean isFinal, final Point2D lastPoint) { int plus; boolean movin = false; try { if (lastPoint != null) { plus = 2; movin = true; } else { plus = 1; movin = false; } if (!isInMode(POLYGON) || (isInMode(POLYGON) && (points.size() == 2) && !movin)) { plus--; } if (isFinal && isInMode(POLYGON) && (points.size() == 2) && !movin) { // bei polygonen mit nur 2 punkten // wird eine boundingbox angelegt final Point2D[] p = new Point2D[5]; p[0] = points.get(0); p[2] = points.get(1); p[1] = new Point2D.Double(p[0].getX(), p[2].getY()); p[3] = new Point2D.Double(p[2].getX(), p[0].getY()); p[4] = p[0]; return p; } final Point2D[] p = new Point2D[points.size() + plus]; for (int i = 0; i < points.size(); ++i) { p[i] = points.get(i); } if (movin) { if (log.isDebugEnabled()) { log.debug("movin"); } p[points.size()] = lastPoint; if (isInMode(POLYGON)) { // close it p[points.size() + 1] = startPoint; } } else { if (log.isDebugEnabled()) { log.debug("not movin"); } if ((points.size() > 2) && isInMode(POLYGON)) { // close it p[points.size()] = startPoint; } } return p; } catch (Exception e) { log.warn("Fehler in getPoints()", e); return new Point2D[0]; } } /** * DOCUMENT ME! * * @param mode DOCUMENT ME! * * @return DOCUMENT ME! */ public boolean isInMode(final String mode) { return (this.mode.equals(mode)); } /** * DOCUMENT ME! * * @return DOCUMENT ME! */ public String getMode() { return mode; } /** * DOCUMENT ME! * * @return DOCUMENT ME! */ public Class getGeometryFeatureClass() { return geometryFeatureClass; } /** * DOCUMENT ME! * * @param geometryFeatureClass DOCUMENT ME! */ public void setGeometryFeatureClass(final Class<? extends PureNewFeature> geometryFeatureClass) { this.geometryFeatureClass = geometryFeatureClass; } /** * DOCUMENT ME! * * @param newFeature DOCUMENT ME! */ private void postGeometryCreatedNotificaton(final PureNewFeature newFeature) { final PNotificationCenter pn = PNotificationCenter.defaultCenter(); pn.postNotification(GEOMETRY_CREATED_NOTIFICATION, newFeature); } /** * DOCUMENT ME! * * @param fce DOCUMENT ME! */ @Override public void allFeaturesRemoved(final FeatureCollectionEvent fce) { } /** * DOCUMENT ME! * * @param pInputEvent DOCUMENT ME! */ @Override public void mouseDragged(final PInputEvent pInputEvent) { super.mouseDragged(pInputEvent); if (isInMode(RECTANGLE)) { inProgress = true; // 4 Punkte, und der erste Punkt nochmal als letzter Punkt points.clear(); points.add(startPoint); points.add(new Point2D.Double(startPoint.getX(), pInputEvent.getPosition().getY())); points.add(pInputEvent.getPosition()); points.add(new Point2D.Double(pInputEvent.getPosition().getX(), startPoint.getY())); points.add(startPoint); updatePolygon(null); } else if (isInMode(ELLIPSE)) { inProgress = true; final Point2D dragPoint = pInputEvent.getPosition(); if (log.isDebugEnabled()) { log.debug("pInputEvent.getModifiers() = " + pInputEvent.getModifiers()); } final double a = startPoint.getX() - dragPoint.getX(); final double b = startPoint.getY() - dragPoint.getY(); final double startX = startPoint.getX(); final double startY = startPoint.getY(); final Coordinate[] coordArr = EllipsePHandle.createEllipseCoordinates( getNumOfEllipseEdges(), a, b, pInputEvent.isControlDown(), pInputEvent.isShiftDown()); points.clear(); for (int i = 0; i < coordArr.length; i++) { points.add(new Point2D.Double(startX - coordArr[i].x, startY - coordArr[i].y)); } updatePolygon(null); } } /** * DOCUMENT ME! */ @Override public void featureCollectionChanged() { } /** * DOCUMENT ME! * * @param fce DOCUMENT ME! */ @Override public void featureReconsiderationRequested(final FeatureCollectionEvent fce) { } /** * DOCUMENT ME! * * @param fce DOCUMENT ME! */ @Override public void featureSelectionChanged(final FeatureCollectionEvent fce) { } /** * DOCUMENT ME! * * @param fce DOCUMENT ME! */ @Override public void featuresAdded(final FeatureCollectionEvent fce) { if (log.isDebugEnabled()) { log.debug("Features added to map"); } for (final Feature curFeature : fce.getEventFeatures()) { if (curFeature instanceof PureNewFeature) { if (log.isDebugEnabled()) { log.debug("Added Feature is PureNewFeature. PostingGeometryCreateNotification"); } postGeometryCreatedNotificaton((PureNewFeature)curFeature); createAction(mc, (PureNewFeature)curFeature); } } } /** * DOCUMENT ME! * * @param fce DOCUMENT ME! */ @Override public void featuresChanged(final FeatureCollectionEvent fce) { } /** * DOCUMENT ME! * * @param fce DOCUMENT ME! */ @Override public void featuresRemoved(final FeatureCollectionEvent fce) { } /** * DOCUMENT ME! * * @param newFeature DOCUMENT ME! */ protected void finishGeometry(final PureNewFeature newFeature) { latestCreation = newFeature; mc.getTmpFeatureLayer().removeAllChildren(); newFeature.setEditable(true); mc.getFeatureCollection().addFeature(newFeature); } /** * DOCUMENT ME! * * @param numOfEllipseEdges DOCUMENT ME! */ public void setNumOfEllipseEdges(int numOfEllipseEdges) { if (numOfEllipseEdges <= 2) { numOfEllipseEdges = NUMOF_ELLIPSE_EDGES; } this.numOfEllipseEdges = numOfEllipseEdges; } /** * DOCUMENT ME! * * @return DOCUMENT ME! */ public int getNumOfEllipseEdges() { return numOfEllipseEdges; } /** * DOCUMENT ME! * * @param filled DOCUMENT ME! * * @return DOCUMENT ME! */ private PPath initTempFeature(final boolean filled) { tempFeature = new PPath(); tempFeature.setStroke(new FixedWidthStroke()); final Color fillingColor = getFillingColor(); tempFeature.setStrokePaint(fillingColor.darker()); if (filled) { tempFeature.setPaint(fillingColor); } else { tempFeature.setPaint(null); } return tempFeature; } /** * DOCUMENT ME! * * @param evt DOCUMENT ME! */ @Override public void mouseWheelRotated(final PInputEvent evt) { // delegate zoom event zoomDelegate.mouseWheelRotated(evt); // trigger full repaint mouseMoved(evt); } }