/* * Copyright 2004-2010 Information & Software Engineering Group (188/1) * Institute of Software Technology and Interactive Systems * Vienna University of Technology, Austria * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.ifs.tuwien.ac.at/dm/somtoolbox/license.html * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package at.tuwien.ifs.somtoolbox.apps.viewer; import java.awt.Color; import java.awt.event.KeyEvent; import java.awt.geom.Rectangle2D; import java.io.FileNotFoundException; import java.util.logging.Logger; import javax.swing.JFrame; import edu.umd.cs.piccolo.event.PBasicInputEventHandler; import edu.umd.cs.piccolo.event.PInputEvent; import edu.umd.cs.piccolo.event.PInputEventListener; import edu.umd.cs.piccolox.event.PNotificationCenter; import edu.umd.cs.piccolox.event.PSelectionEventHandler; import at.tuwien.ifs.somtoolbox.SOMToolboxException; import at.tuwien.ifs.somtoolbox.apps.viewer.handlers.MyInputDragSequenceEventHandler; import at.tuwien.ifs.somtoolbox.apps.viewer.handlers.MyLabelDragSequenceEventHandler; import at.tuwien.ifs.somtoolbox.apps.viewer.handlers.MyWheelZoomEventHandler; import at.tuwien.ifs.somtoolbox.data.SOMVisualisationData; import at.tuwien.ifs.somtoolbox.data.SharedSOMVisualisationData; import at.tuwien.ifs.somtoolbox.input.SOMLibFileFormatException; import at.tuwien.ifs.somtoolbox.input.SOMLibFormatInputReader; import at.tuwien.ifs.somtoolbox.layers.GrowingLayer; import at.tuwien.ifs.somtoolbox.models.GrowingSOM; import at.tuwien.ifs.somtoolbox.util.FileUtils; import at.tuwien.ifs.somtoolbox.visualization.BackgroundImageVisualizer; import at.tuwien.ifs.somtoolbox.visualization.Visualizations; import at.tuwien.ifs.somtoolbox.visualization.comparison.QuiverPNode; import at.tuwien.ifs.somtoolbox.visualization.comparison.SOMComparison; /** * A specific subclass of {@link GenericPNodeScrollPane} that holds a {@link MapPNode} and handles additionaly label and * input movement events. * * @author Michael Dittenbach * @author Rudolf Mayer * @version $Id: SOMPane.java 3939 2010-11-17 16:06:14Z frank $ */ public class SOMPane extends GenericPNodeScrollPane { private static final long serialVersionUID = 1L; private MyInputDragSequenceEventHandler inputDragHandler; // to select Labels for moving them private MyLabelDragSequenceEventHandler labelDragHandler; private MapPNode map = null; private MapPNode map2 = null; // SOM Comparision private SOMComparison somComparision = null; private boolean shiftArrowsVisibility = false; private QuiverPNode quiver = null; private final int SECOND_MAP_OFFSET = 200; private double secMapXOffset = 0; private double secMapYOffset = 0; // change this for the default location of the second SOM. "right" makes sense for wide-screens, "bottom" might be // better otherwise private String secMapPosition = "right"; /** * Default constructor. */ public SOMPane(JFrame parent, String weightVectorFileName, String unitDescriptionFileName, String mapDescriptionFileName, CommonSOMViewerStateData state) { super(); this.state = state; try { map = new MapPNode(parent, weightVectorFileName, unitDescriptionFileName, mapDescriptionFileName, state); node = map; } catch (FileNotFoundException e) { Logger.getLogger("at.tuwien.ifs.somtoolbox").severe( "Cannot read input file(s): " + e.getMessage() + " - stopping."); System.exit(1); // FIXME: don't use System.exit } catch (SOMLibFileFormatException e) { Logger.getLogger("at.tuwien.ifs.somtoolbox").severe( "Cannot read input file(s): " + e.getMessage() + " - stopping."); System.exit(1); // FIXME: don't use System.exit } init(); } /** * constructor for an already loaded growing som. can be used to create a sompane from a subhierarchy of a grwoing * som */ public SOMPane(JFrame parent, GrowingSOM gsom, GrowingLayer layer, CommonSOMViewerStateData state) { super(); this.state = state; map = new MapPNode(parent, gsom, layer, state); node = map; init(); } @Override protected void init() { super.init(); // add a new listener that will act on mouse wheel events, and will update the MapDetailPanel // we have to register the listener here rather than in MapPNode, as not always, all events will be fired in // MapPNode canvas.addInputEventListener(new PInputEventListener() { @Override public void processEvent(PInputEvent aEvent, int type) { // update zoom display on mouse wheel, but only for main display (MyPCanvas) if (aEvent.isMouseWheelEvent() && aEvent.getSourceSwingEvent().getSource() instanceof MyPCanvas) { state.getMapDetailPanel().updatePanel(aEvent.getTopCamera().getViewScale()); } } }); // connect all keyboard events to the canvas' root pane canvas.getRoot().getDefaultInputManager().setKeyboardFocus(new PBasicInputEventHandler() { @Override public void keyPressed(PInputEvent event) { super.keyPressed(event); if (event.getSourceSwingEvent() instanceof KeyEvent) { KeyEvent keyEvent = (KeyEvent) event.getSourceSwingEvent(); int key = keyEvent.getKeyCode(); // panning keys with the arrow keys & the corner keys on the num-pad if (key == KeyEvent.VK_DOWN || key == KeyEvent.VK_KP_DOWN || key == KeyEvent.VK_END || key == KeyEvent.VK_PAGE_DOWN) { canvas.getCamera().translateView(0, -10); } if (key == KeyEvent.VK_UP || key == KeyEvent.VK_KP_UP || key == KeyEvent.VK_HOME || key == KeyEvent.VK_PAGE_UP) { canvas.getCamera().translateView(0, 10); } if (key == KeyEvent.VK_LEFT || key == KeyEvent.VK_KP_LEFT || key == KeyEvent.VK_HOME || key == KeyEvent.VK_END) { canvas.getCamera().translateView(10, 0); } if (key == KeyEvent.VK_RIGHT || key == KeyEvent.VK_KP_RIGHT || key == KeyEvent.VK_PAGE_UP || key == KeyEvent.VK_PAGE_DOWN) { canvas.getCamera().translateView(-10, 0); } // zooming with +/- keys if (key == KeyEvent.VK_PLUS || key == KeyEvent.VK_ADD) { MyWheelZoomEventHandler.processZoomEvent(event, -1, canvas.getCamera()); } else if (key == KeyEvent.VK_MINUS || key == KeyEvent.VK_SUBTRACT) { MyWheelZoomEventHandler.processZoomEvent(event, 1, canvas.getCamera()); } } else { // this should never happen... Logger.getLogger("at.tuwien.ifs.somtoolbox").severe( "SOMPane.PBasicInputEventHandler: Got an unknown event type: " + event.getSourceSwingEvent()); } } }); // make new arrow container and make it listen to the selection changes // on the first map quiver = new QuiverPNode(this); this.connectSelectionHandlerTo(quiver); // redraw interface if necessary validate(); } /** * initialize both selection handlers and set rectangle selection as default. also initializes handler for selecting * and moving a cluster label. */ @Override public void initSelectionHandlers() { super.initSelectionHandlers(); this.inputDragHandler = new MyInputDragSequenceEventHandler( map.getGsom().getSharedInputObjects().getInputCorrections()); this.labelDragHandler = new MyLabelDragSequenceEventHandler(); } /** set the input selection handler. */ public void setInput() { ((MyPCanvas) canvas).setSelectionEventHandler(this.inputDragHandler); PNotificationCenter.defaultCenter().addListener(this, "selectionChanged", PSelectionEventHandler.SELECTION_CHANGED_NOTIFICATION, this.inputDragHandler); } // Angela /** * Change the selection handler from selecting units to moving labels. Moving labels is of course no selection but * it looks nasty if units get selected while dragging the label around. Therefore the current selection handler * gets disabled. */ public void setLabel() { ((MyPCanvas) canvas).setSelectionEventHandler(this.labelDragHandler); PNotificationCenter.defaultCenter().addListener(this, "selectionChanged", PSelectionEventHandler.SELECTION_CHANGED_NOTIFICATION, this.labelDragHandler); } /** @deprecated use {@link Visualizations} instead */ @Deprecated public BackgroundImageVisualizer[] getVisualizations() { return map.getVisualizations(); } public BackgroundImageVisualizer getCurrentVisualization() { return map.getCurrentVisualization(); } public void setNoVisualization() { map.setNoVisualization(); } public boolean setInitialVisualization(BackgroundImageVisualizer vis, int variant) throws SOMToolboxException { return map.setInitialVisualizationOnStartup(vis, variant); } public boolean setVisualization(BackgroundImageVisualizer vis, int variant) throws SOMToolboxException { return map.setVisualization(vis, variant); } public boolean setVisualization(int vis, int variant) throws SOMToolboxException { return map.setVisualization(vis, variant); } public Color[] getClassLegendColors() { return map.getClassLegendColors(); } public String[] getClassLegendNames() { return map.getClassLegendNames(); } public void updateVisualization() { map.updateVisualization(); } public void updateClassSelection(int[] indices) { updateClassSelection(indices, map); if (map2 != null) { updateClassSelection(indices, map2); } } public void setShowOnlySelectedClasses(boolean selectedClassesOnly) { map.setShowOnlySelectedClasses(selectedClassesOnly); } private void updateClassSelection(int[] indices, MapPNode mapPNode) { mapPNode.updateClassSelection(indices); if (mapPNode.getThematicClassMapVisualizer() != null) { mapPNode.getThematicClassMapVisualizer().invalidateCache(); } } public void setClassColor(int index, Color color) { map.setClassColor(index, color); if (map2 != null) { map2.setClassColor(index, color); } } @Override public void centerAndFitMapToScreen(int animationDuration) { // if the second map is diplayed if (shiftArrowsVisibility && map2 != null) { double x = 0, y = 0, x2, y2, width, height; // take the leftmost point as x if (secMapXOffset < 0) { x = secMapXOffset; } // take the highest point as y if (secMapYOffset < 0) { y = secMapYOffset; } // take the rightmost point as x2 if (map.getWidth() > secMapXOffset + map2.getWidth()) { x2 = map.getWidth(); } else { x2 = secMapXOffset + map2.getWidth(); } // take the lowest point as y2 if (map.getHeight() > secMapYOffset + map2.getHeight()) { y2 = map.getHeight(); } else { y2 = secMapYOffset + map2.getHeight(); } // calculate width and height width = Math.abs(x - x2); height = Math.abs(y - y2); // use coordinates just calculated canvas.getCamera().animateViewToCenterBounds(new Rectangle2D.Double(x, y, width, height), true, animationDuration); } else { super.centerAndFitMapToScreen(animationDuration); } } public MapPNode getMap() { return map; } /** * Is called when the comparison object for comparison between two SOMs must be changed. Either loads new second SOM * and computes new arrows accordingly, or throws away all currently held arrows (if the comparison object is * unloaded). */ public void updateSOMComparison() throws SOMToolboxException { // remove old compareSOMs object somComparision = new SOMComparison(); // remove all old arrows quiver.dropArrows(); if (canvas.getLayer().isAncestorOf(quiver)) { canvas.getLayer().removeChild(quiver); } // remove second map if (map2 != null && canvas.getLayer().isAncestorOf(map2)) { canvas.getLayer().removeChild(map2); } map2 = null; // get the filename for the second SOM String compareFileName = state.secondSOMName; // if the filename's not empty ==> a new set of files was loaded if (!compareFileName.equals("")) { // get prefix of filename (to load the other description files as well) String prefix = FileUtils.extractSOMLibInputPrefix(compareFileName); // contruct new MapPNode for second SOM try { // classInfo = (SOMLibClassInformation) inputObjects.getData(SOMVisualisationData.CLASS_INFO); String classInfoFile = state.inputDataObjects.getObject(SOMVisualisationData.CLASS_INFO).getFileName(); CommonSOMViewerStateData state2 = new CommonSOMViewerStateData(state); state2.inputDataObjects = new SharedSOMVisualisationData(classInfoFile, null, "", "", "", "", ""); // reading input objects, if we have filenames set state2.inputDataObjects.readAvailableData(); map2 = new MapPNode(map.getParentFrame(), prefix + SOMLibFormatInputReader.weightFileNameSuffix, prefix + SOMLibFormatInputReader.unitFileNameSuffix, prefix + SOMLibFormatInputReader.mapFileNameSuffix, state2); this.setSecSOMPosition(); this.updateSecMap(); this.updateQuiver(); } catch (FileNotFoundException e1) { Logger.getLogger("at.tuwien.ifs.somtoolbox").severe( "Cannot read input file(s): " + e1.getMessage() + " - stopping."); } catch (SOMLibFileFormatException e1) { Logger.getLogger("at.tuwien.ifs.somtoolbox").severe( "Cannot read input file(s): " + e1.getMessage() + " - stopping."); } try { // load the SOMs into the compareSOMs object somComparision.loadGSOMs(map.getGsom(), prefix); // compute new arrows quiver.computeArrows(); } catch (SOMToolboxException e1) { // if there was an error loading the new SOMs // throw away all newly created objects map2 = null; somComparision = null; state.secondSOMName = ""; Logger.getLogger("at.tuwien.ifs.somtoolbox").severe(e1.getMessage()); throw e1; } catch (Exception e2) { Logger.getLogger("at.tuwien.ifs.somtoolbox").severe(e2.getMessage()); e2.printStackTrace(); } } } public void useSecSOMOffset() { map2.setOffset(secMapXOffset, secMapYOffset); } /** * Sets the position of the second SOM according to attribute secMapPosition */ public void setSecSOMPosition() { if (secMapPosition.equals("bottom")) { secMapXOffset = (map.getWidth() - map2.getWidth()) / 2; secMapYOffset = map.getHeight() + SECOND_MAP_OFFSET; } else if (secMapPosition.equals("top")) { secMapXOffset = (map.getWidth() - map2.getWidth()) / 2; secMapYOffset = (map2.getHeight() + SECOND_MAP_OFFSET) * -1; } else if (secMapPosition.equals("left")) { secMapXOffset = (map2.getWidth() + SECOND_MAP_OFFSET) * -1; secMapYOffset = (map.getHeight() - map2.getHeight()) / 2; } else if (secMapPosition.equals("right")) { secMapXOffset = map.getWidth() + SECOND_MAP_OFFSET; secMapYOffset = (map.getHeight() - map2.getHeight()) / 2; } else { Logger.getLogger("at.tuwien.ifs.somtoolbox").warning("Invalid Position for Second Map: " + secMapPosition); } this.useSecSOMOffset(); } /** Sets the position of the second SOM according to position and saves the position in attribute secMapPosition */ public void setSecSOMPosition(String position) { this.secMapPosition = position; this.setSecSOMPosition(); } public CommonSOMViewerStateData getState() { return state; } public double getSecMapXOffset() { return secMapXOffset; } public void setSecMapXOffset(double secMapXOffset) { this.secMapXOffset = secMapXOffset; this.useSecSOMOffset(); } public double getSecMapYOffset() { return secMapYOffset; } public void setSecMapYOffset(double secMapYOffset) { this.secMapYOffset = secMapYOffset; this.useSecSOMOffset(); } public SOMComparison getSOMComparision() { return somComparision; } public MapPNode getSecondMap() { return map2; } public QuiverPNode getQuiver() { return quiver; } public boolean isShiftArrowsVisibility() { return shiftArrowsVisibility; } public void setShiftArrowsVisibility(boolean shiftArrowsVisibility) { if (shiftArrowsVisibility != this.shiftArrowsVisibility) { this.shiftArrowsVisibility = shiftArrowsVisibility; this.updateSecMap(); this.updateQuiver(); quiver.updateClusterBorders(); } } private void updateSecMap() { if (map2 != null) { if (shiftArrowsVisibility == true) { if (!canvas.getLayer().isAncestorOf(map2)) { canvas.getLayer().addChild(map2); } } else { if (canvas.getLayer().isAncestorOf(map2)) { canvas.getLayer().removeChild(map2); } } } } private void updateQuiver() { if (quiver != null) { if (shiftArrowsVisibility == true) { if (!canvas.getLayer().isAncestorOf(quiver)) { canvas.getLayer().addChild(quiver); // quiver.moveToBack(); if (getMap().currentVisualizationImage != null) { quiver.moveInFrontOf(getMap().currentVisualizationImage); } } } else { if (canvas.getLayer().isAncestorOf(quiver)) { canvas.getLayer().removeChild(quiver); } } } } }