All Downloads are FREE. Search and download functionalities are using the official Maven repository.

fact.hexmap.ui.components.cameradisplay.FactHexMapDisplay Maven / Gradle / Ivy

Go to download

The FACT telescope is a Cherenkov telescope on the island of La Palma. This set of tools is dedicated to read, parse and process raw data produced by the FACT telescope.

The newest version!
/**
 * 
 */
package fact.hexmap.ui.components.cameradisplay;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.geom.AffineTransform;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;

import javax.swing.JPanel;

import org.apache.commons.math3.util.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import stream.Data;

import com.google.common.eventbus.Subscribe;

import fact.Utils;
import fact.hexmap.CameraPixel;
import fact.hexmap.FactCameraPixel;
import fact.hexmap.FactPixelMapping;
import fact.hexmap.ui.Bus;
import fact.hexmap.ui.SliceObserver;
import fact.hexmap.ui.colormapping.ColorMapping;
import fact.hexmap.ui.colormapping.GrayScaleColorMapping;
import fact.hexmap.ui.events.SliceChangedEvent;
import fact.hexmap.ui.overlays.CameraMapOverlay;

/**
 * This implements a PixelMap to draw a grid of hexagons as seen in the camera
 * of the fact telescope The hexagons are equally spaced and sized. Orientated
 * with one edge on the bottom. Also has a colorbar next to it.
 * 
 */
public class FactHexMapDisplay extends JPanel implements PixelMapDisplay,
		SliceObserver, MouseListener {
	/** The unique class ID */
	private static final long serialVersionUID = -4015808725138908874L;

	static Logger log = LoggerFactory.getLogger(FactHexMapDisplay.class);
	private final double radius;

	FactHexTile tiles[];

	// initialize the hexagonal grid.
	int canvasWidth;
	int canvasHeight;

	int rows = 0, cols = 0;

	Set selectedPixels = new LinkedHashSet();

	public double[][] sliceValues = new double[1440][1024];
	int currentSlice = 0;

	// the data and key which to display a a hexmap
	private Data dataItem;

	// store the smallest and largest value in the data. We need this to map
	// values to colors in the display
	private double minValueInData;
	private double maxValueInData;

	// the colormap of this display and the scale next to the map
	private ColorMapping colormap = new GrayScaleColorMapping();

	final private FactPixelMapping pixelMapping;
	private ArrayList overlays = new ArrayList<>();
	private Set> overlayKeys = new HashSet<>();

	// formater to display doubles nicely
	DecimalFormat fmt = new DecimalFormat("#.##");

	// a default key
	public String defaultKey;
	private boolean patchSelectionMode;

	private boolean drawScaleNumbers = true;

	private boolean includeScale = false;

	private int offsetX = 0;
	private int offsetY = 0;

	/**
	 * A Hexagon in this case is defined by the passed radius. The radius of the
	 * circle that fits into the hexagon can be calculated by sqrt(3)/2 *
	 * (outter radius)
	 * 
	 * @param radius
	 *            the radius of the circle the hexagon should fit into
	 */
	public FactHexMapDisplay(double radius, int canvasWidth, int canvasHeight,
			boolean mouseAction) {

		Bus.eventBus.register(this);

		this.radius = radius;
		this.pixelMapping = FactPixelMapping.getInstance();
		this.canvasHeight = canvasHeight;
		this.canvasWidth = canvasWidth;
		this.rows = pixelMapping.getNumberRows();
		this.cols = pixelMapping.getNumberCols();

		tiles = new FactHexTile[pixelMapping.getNumberOfPixel()];
		for (int i = 0; i < tiles.length; i++) {
			FactHexTile t = new FactHexTile(pixelMapping.getPixelFromId(i),
					radius);
			tiles[i] = t;
		}

		if (mouseAction) {
			// add the mosuelistener so we can react to mouse clicks on the
			// hexmap
			this.addMouseListener(this);
		}
	}

	public FactHexMapDisplay(double radius, int canvasWidth, int canvasHeight) {
		this(radius, canvasWidth, canvasHeight, true);
	}

	@Override
	public Tile[] getTiles() {
		return tiles;
	}

	@Override
	@Subscribe
	public void handleSliceChangeEvent(SliceChangedEvent ev) {
		// log.debug("Hexmap Selecting slice: {}", ev.currentSlice);
		this.currentSlice = ev.currentSlice;
		this.repaint();
	}

	@Subscribe
	public void handleEventChange(Pair itemKeyPair) {
		log.debug("hexmap got a new item");

		String key;
		if (defaultKey != null) {
			key = defaultKey;
		} else {
			key = itemKeyPair.getSecond();
		}

		this.dataItem = itemKeyPair.getFirst();
		minValueInData = 0;
		maxValueInData = 0;
		overlays = updateOverlays(overlayKeys, dataItem);
		if (dataItem.containsKey(key)) {
			updateMapDisplay(dataItem, key);
		} else {
			log.error("The key: " + key
					+ " was not found in the data item. Nothing to display.");
		}

	}

	public void setOverlayItemsToDisplay(Set> items) {
		overlayKeys = items;
		overlays = updateOverlays(items, dataItem);
		this.repaint();
	}

	private ArrayList updateOverlays(
			Set> items, Data dataItem) {
		ArrayList overlays = new ArrayList<>();
		for (Pair s : items) {
			CameraMapOverlay overlay = (CameraMapOverlay) dataItem.get(s
					.getKey());
			if (overlay != null) {
				overlay.setColor(s.getValue());
				overlays.add(overlay);
			}
		}
		
		class customComparator implements Comparator {
		    public int compare(CameraMapOverlay object1, CameraMapOverlay object2) {            	
		        return object1.getDrawRank() - object2.getDrawRank();
		    }
		}
		// Sortierung in der richtigen Reihenfolge
		// um ueberdeckungen zu vermeiden 
		// von niedrig nach hoch
		Collections.sort(overlays, new customComparator());	
		
		return overlays;
	}

	private void updateMapDisplay(Data item, String key) {
		if (item == null) {
			log.error("Dataitem was null in cameraWindow");
		}
		try {
			double[] data = (double[]) item.get(key);
			this.sliceValues = Utils.sortPixels(data, 1440);
			for (double[] slices : sliceValues) {
				for (double v : slices) {
					minValueInData = Math.min(minValueInData, v);
					maxValueInData = Math.max(maxValueInData, v);
				}
			}
			this.repaint();
		} catch (ClassCastException e) {
			log.error("The viewer can only display data of type double[]");
		}
	}

	@Override
	public void setColorMap(ColorMapping m) {
		this.colormap = m;
		this.repaint();
	}

	/**
	 * @see javax.swing.JComponent#paint(java.awt.Graphics)
	 */
	@Override
	public void paint(Graphics g) {
		paint(g, false);
	}

	// The paint method.
	public void paint(Graphics g, boolean transparentBackground) {
		// super.paint(g);
		g.setColor(this.getBackground());
		if (transparentBackground) {
			g.setColor(new Color(255, 255, 255, 0));
		}
		g.fillRect(0, 0, this.getWidth(), this.getHeight());
		int xOffset = getWidth() / 2 + offsetX;
		int yOffset = getHeight() / 2 + offsetY;
		if (g instanceof Graphics2D) {
			Graphics2D g2 = (Graphics2D) g;
			// g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
			// RenderingHints.VALUE_ANTIALIAS_ON);

			// draw a grid with lines every 25 pixel in a dark grey color
			g2.setStroke(new BasicStroke(1.0f));
			g2.setColor(Color.DARK_GRAY);
			// drawGrid(g2, 25);

			// now draw the actual camera pixel
			// translate to center of canvas
			g2.translate(xOffset, yOffset);
			// rotate 90 degrees counter clockwise
			g2.rotate(-Math.PI / 2);
			// and draw tiles
			for (Tile tile : tiles) {
				CameraPixel p = tile.getCameraPixel();
				int slice = currentSlice;
				if (currentSlice >= sliceValues[tile.getCameraPixel().id].length) {
					slice = sliceValues[tile.getCameraPixel().id].length - 1;
				}
				double value = sliceValues[tile.getCameraPixel().id][slice];
				tile.setFillColor(this.colormap.getColorFromValue(value,
						minValueInData, maxValueInData));
				if (selectedPixels.contains(p)) {
					tile.setBorderColor(Color.RED);
				} else {
					tile.setBorderColor(Color.BLACK);
				}
				tile.paint(g);
			}

			// draw all overlays
			for (CameraMapOverlay o : overlays) {
				o.paint(g2, this);
			}
			// g2.setStroke(new BasicStroke(1.0f));
			// g2.setColor(Color.WHITE);
			// // undo the rotation
			g2.rotate(Math.PI / 2);
			// to draw the grid translate back
			g2.translate(-xOffset, -yOffset);

			// draw cross across screen to indicate center ofcomponent

			// Line2D line = new Line2D.Double(0,0, getWidth(),getHeight());
			// g2.draw(line);
			//
			// line = new Line2D.Double(getWidth(),0,0,getHeight());
			// g2.draw(line);

			if (includeScale) {
				g2.translate(this.canvasWidth - 40, 0);
				paintScale(g2, 40);
				g2.translate(-this.canvasWidth + 40, 0);
			}
		}

	}

	private void paintScale(Graphics2D g2, int width) {
		// draw the gradient according to the values returned by the current
		// colormap
		for (int i = 0; i < this.getHeight(); i++) {
			double range = Math.abs(maxValueInData - minValueInData);
			double value = minValueInData + (((double) i) / this.getHeight())
					* range;
			Color c = this.colormap.getColorFromValue(value, minValueInData,
					maxValueInData);
			g2.setColor(c);
			g2.drawLine(20, this.getHeight() - i, width, this.getHeight() - i);
			// draw a number next to the colorbar each 64 pixel
			if (i > 0 && (i % 70) == 0) {
				g2.setColor(Color.GRAY);
				g2.drawString(fmt.format(value), -25, this.getHeight() - i);
			}
		}
		// now draw some numbers next to it

		if (drawScaleNumbers) {
			g2.setColor(Color.WHITE);
			g2.drawString(fmt.format(minValueInData), -25, this.getHeight() - 5);
			g2.drawString(fmt.format(maxValueInData), -25, 10);
		}
	}

	@Override
	public void mouseClicked(MouseEvent arg0) {
		if (arg0.getButton() == MouseEvent.BUTTON1) {
			// since we transformed the geometry while painting the polygons
			// above we now have to transform
			// the coordinates of the mouse pointer.
			Point p = arg0.getPoint();
			p.translate(-getWidth() / 2, -getHeight() / 2);
			AffineTransform rotateInstance = AffineTransform
					.getRotateInstance(Math.PI / 2);
			rotateInstance.transform(p, p);

			// In case we want to select wholes patches at a time we save the id
			// of all selected patches in here
			Set selectedPatches = new HashSet<>();

			for (Tile cell : tiles) {
				if (cell.contains(p)) {
					FactCameraPixel selectedPixel = (FactCameraPixel) cell
							.getCameraPixel();

					// getting the patch by dividing chid by 9 since there are
					// 1440/9 = 160 patches
					Integer patch = selectedPixel.chid / 9;

					boolean shiftDown = arg0.isShiftDown();

					// in case shift is being pressed and we clicked a pixel
					// thats already been selected we
					// have to remove it
					if (shiftDown && selectedPixels.contains(selectedPixel)) {
						selectedPixels.remove(selectedPixel);
						// in case we are in patchselection mode we have to
						// unselected the patch belongin to
						// pixel clicked
						selectedPatches.remove(patch);
						if (patchSelectionMode) {
							Iterator it = selectedPixels
									.iterator();
							while (it.hasNext()) {
								FactCameraPixel pt = it.next();
								if (pt.chid / 9 == patch) {
									it.remove();
								}
							}
						}
					} else {
						if (!shiftDown) {
							selectedPixels.clear();
							selectedPatches.clear();
						}
						selectedPixels.add(selectedPixel);
						selectedPatches.add(patch);
					}
					break;
				}
			}
			// in patch selectionmode add all the pixels with the right patchid
			// to the selectionset
			if (patchSelectionMode) {
				for (Tile cell : tiles) {
					FactCameraPixel pixel = (FactCameraPixel) cell
							.getCameraPixel();
					Integer patch = pixel.chid / 9;
					if (selectedPatches.contains(patch)) {
						selectedPixels.add(pixel);
					}
				}
			}
		}
		this.repaint();
		Bus.eventBus.post(selectedPixels);

	}

	@Override
	public void mousePressed(MouseEvent e) {
	}

	@Override
	public void mouseReleased(MouseEvent e) {
	}

	@Override
	public void mouseEntered(MouseEvent e) {
	}

	@Override
	public void mouseExited(MouseEvent e) {
	}

	// The update method. to update.
	@Override
	public void update(Graphics g) {
		super.paint(g);
		this.paint(g);
	}

	// --------- swing overrides------------
	/**
	 * @see javax.swing.JComponent#getMinimumSize()
	 */
	@Override
	public Dimension getMinimumSize() {
		return new Dimension(getWidth(), getHeight()); // getHeight(),
	}

	/**
	 * @see javax.swing.JComponent#getHeight()
	 */
	@Override
	public int getHeight() {
		return this.canvasHeight;
		// return (int) (this.cellHeight * (rows) );
	}

	/**
	 * @see javax.swing.JComponent#getWidth()
	 */
	@Override
	public int getWidth() {
		return this.canvasWidth;
		// System.out.println("ads " + (int) (this.cellWidth * (cols) + 100) );
		// return (int) (this.cellWidth * (cols) );
	}

	/**
	 * @see javax.swing.JComponent#getMaximumSize()
	 */
	@Override
	public Dimension getMaximumSize() {
		return getMinimumSize();
	}

	/**
	 * @see javax.swing.JComponent#getPreferredSize()
	 */
	@Override
	public Dimension getPreferredSize() {
		return getMinimumSize();
	}

	// ------Getter and Setter----------------
	@Override
	public int getNumberOfTiles() {
		return tiles.length;
	}

	public double getTileRadiusInPixels() {
		return radius;
	}

	public void setPatchSelectionMode(boolean patchSelectionMode) {
		this.patchSelectionMode = patchSelectionMode;
	}

	public boolean isPatchSelectionMode() {
		return patchSelectionMode;
	}

	/**
	 * @return the drawScaleNumbers
	 */
	public boolean isDrawScaleNumbers() {
		return drawScaleNumbers;
	}

	/**
	 * @param drawScaleNumbers
	 *            the drawScaleNumbers to set
	 */
	public void setDrawScaleNumbers(boolean drawScaleNumbers) {
		this.drawScaleNumbers = drawScaleNumbers;
	}

	/**
	 * @return the includeScale
	 */
	public boolean isIncludeScale() {
		return includeScale;
	}

	/**
	 * @param includeScale
	 *            the includeScale to set
	 */
	public void setIncludeScale(boolean includeScale) {
		this.includeScale = includeScale;
	}

	/**
	 * @return the offsetX
	 */
	public int getOffsetX() {
		return offsetX;
	}

	/**
	 * @param offsetX
	 *            the offsetX to set
	 */
	public void setOffsetX(int offsetX) {
		this.offsetX = offsetX;
	}

	/**
	 * @return the offsetY
	 */
	public int getOffsetY() {
		return offsetY;
	}

	/**
	 * @param offsetY
	 *            the offsetY to set
	 */
	public void setOffsetY(int offsetY) {
		this.offsetY = offsetY;
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy