123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675 |
- package org.insa.graphics.drawing.components;
-
- import java.awt.BasicStroke;
- import java.awt.Color;
- import java.awt.Graphics;
- import java.awt.Graphics2D;
- import java.awt.Image;
- import java.awt.event.ActionEvent;
- import java.awt.event.ActionListener;
- import java.awt.event.MouseAdapter;
- import java.awt.event.MouseEvent;
- import java.awt.geom.AffineTransform;
- import java.awt.geom.NoninvertibleTransformException;
- import java.awt.geom.Point2D;
- import java.awt.image.BufferedImage;
- import java.io.IOException;
- import java.util.ArrayList;
- import java.util.Collections;
- import java.util.Iterator;
- import java.util.List;
-
- import javax.swing.JPanel;
-
- import org.insa.graph.Arc;
- import org.insa.graph.Graph;
- import org.insa.graph.GraphStatistics.BoundingBox;
- import org.insa.graph.Node;
- import org.insa.graph.Path;
- import org.insa.graph.Point;
- import org.insa.graphics.drawing.BasicGraphPalette;
- import org.insa.graphics.drawing.Drawing;
- import org.insa.graphics.drawing.DrawingClickListener;
- import org.insa.graphics.drawing.GraphPalette;
- import org.insa.graphics.drawing.MercatorProjection;
- import org.insa.graphics.drawing.overlays.MarkerOverlay;
- import org.insa.graphics.drawing.overlays.MarkerUtils;
- import org.insa.graphics.drawing.overlays.Overlay;
- import org.insa.graphics.drawing.overlays.PathOverlay;
- import org.insa.graphics.drawing.overlays.PointSetOverlay;
-
- /**
- * Cette implementation de la classe Dessin produit vraiment un affichage (au
- * contraire de la classe DessinInvisible).
- */
-
- public class BasicDrawing extends JPanel implements Drawing {
-
- /**
- *
- */
- private static final long serialVersionUID = 96779785877771827L;
-
- private abstract class BasicOverlay implements Overlay {
-
- // Visible?
- protected boolean visible;
-
- // Color
- protected Color color;
-
- public BasicOverlay(Color color) {
- this.visible = true;
- this.color = color;
- }
-
- @Override
- public void setColor(Color color) {
- this.color = color;
- }
-
- @Override
- public Color getColor() {
- return this.color;
- }
-
- @Override
- public void setVisible(boolean visible) {
- this.visible = visible;
- BasicDrawing.this.repaint();
- }
-
- @Override
- public boolean isVisible() {
- return this.visible;
- }
-
- @Override
- public void delete() {
- synchronized (overlays) {
- BasicDrawing.this.overlays.remove(this);
- }
- BasicDrawing.this.repaint();
- }
-
- /**
- * Draw the given overlay.
- */
- public void draw(Graphics2D g) {
- if (this.visible) {
- drawImpl(g);
- }
- }
-
- public abstract void drawImpl(Graphics2D g);
-
- public void redraw() {
- BasicDrawing.this.repaint();
- }
-
- };
-
- private class BasicMarkerOverlay extends BasicOverlay implements MarkerOverlay {
-
- // Marker width and height
- public static final int MARKER_WIDTH = 30, MARKER_HEIGHT = 60;
-
- // Point of the marker.
- private Point point;
-
- // Image to draw
- private Image image;
-
- public BasicMarkerOverlay(Point point, Color color) {
- super(color);
- this.point = point;
- this.color = color;
- this.image = MarkerUtils.getMarkerForColor(color);
- }
-
- @Override
- public Point getPoint() {
- return point;
- }
-
- @Override
- public void setColor(Color color) {
- super.setColor(color);
- this.image = MarkerUtils.getMarkerForColor(color);
- }
-
- @Override
- public void moveTo(Point point) {
- this.point = point;
- BasicDrawing.this.repaint();
- }
-
- @Override
- public void drawImpl(Graphics2D graphics) {
-
- int px = projection.longitudeToPixelX(getPoint().getLongitude());
- int py = projection.latitudeToPixelY(getPoint().getLatitude());
-
- graphics.drawImage(this.image, px - MARKER_WIDTH / 2, py - MARKER_HEIGHT, MARKER_WIDTH,
- MARKER_HEIGHT, BasicDrawing.this);
- }
-
- };
-
- private class BasicPathOverlay extends BasicOverlay implements PathOverlay {
-
- // List of points
- private final List<Point> points;
-
- // Origin / Destination markers.
- private BasicMarkerOverlay origin, destination;
-
- public BasicPathOverlay(List<Point> points, Color color, BasicMarkerOverlay origin,
- BasicMarkerOverlay destination) {
- super(color);
- this.points = points;
- this.origin = origin;
- this.destination = destination;
- this.color = color;
- }
-
- @Override
- public void setColor(Color color) {
- super.setColor(color);
- this.origin.setColor(color);
- this.destination.setColor(color);
- }
-
- @Override
- public void drawImpl(Graphics2D graphics) {
-
- if (!points.isEmpty()) {
-
- graphics.setStroke(new BasicStroke(2));
- graphics.setColor(getColor());
-
- Iterator<Point> itPoint = points.iterator();
- Point prev = itPoint.next();
-
- while (itPoint.hasNext()) {
- Point curr = itPoint.next();
-
- int x1 = projection.longitudeToPixelX(prev.getLongitude());
- int x2 = projection.longitudeToPixelX(curr.getLongitude());
- int y1 = projection.latitudeToPixelY(prev.getLatitude());
- int y2 = projection.latitudeToPixelY(curr.getLatitude());
-
- graphics.drawLine(x1, y1, x2, y2);
-
- prev = curr;
- }
-
- }
-
- if (this.origin != null) {
- this.origin.draw(graphics);
- }
- if (this.destination != null) {
- this.destination.draw(graphics);
- }
- }
-
- };
-
- private class BasicPointSetOverlay extends BasicOverlay implements PointSetOverlay {
-
- // Default point width
- private static final int DEFAULT_POINT_WIDTH = 5;
-
- // Image for path / points
- private final BufferedImage image;
- private final Graphics2D graphics;
-
- private int width = DEFAULT_POINT_WIDTH;
-
- public BasicPointSetOverlay() {
- super(Color.BLACK);
- this.image = new BufferedImage(BasicDrawing.this.width, BasicDrawing.this.height,
- BufferedImage.TYPE_4BYTE_ABGR);
- this.graphics = image.createGraphics();
- this.graphics.setBackground(new Color(0, 0, 0, 0));
- }
-
- @Override
- public void setColor(Color color) {
- super.setColor(color);
- this.graphics.setColor(color);
- }
-
- @Override
- public void setWidth(int width) {
- this.width = Math.max(2, width);
- }
-
- @Override
- public void setWidthAndColor(int width, Color color) {
- setWidth(width);
- setColor(color);
- }
-
- @Override
- public void addPoint(Point point) {
- int x = projection.longitudeToPixelX(point.getLongitude()) - this.width / 2;
- int y = projection.latitudeToPixelY(point.getLatitude()) - this.width / 2;
- this.graphics.fillOval(x, y, this.width, this.width);
- BasicDrawing.this.repaint();
- }
-
- @Override
- public void addPoint(Point point, int width) {
- setWidth(width);
- addPoint(point);
- }
-
- @Override
- public void addPoint(Point point, Color color) {
- setColor(color);
- addPoint(point);
- }
-
- @Override
- public void addPoint(Point point, int width, Color color) {
- setWidth(width);
- setColor(color);
- addPoint(point);
- }
-
- @Override
- public void drawImpl(Graphics2D g) {
- g.drawImage(this.image, 0, 0, BasicDrawing.this);
- }
-
- }
-
- // Default path color.
- public static final Color DEFAULT_PATH_COLOR = new Color(66, 134, 244);
-
- // Default palette.
- public static final GraphPalette DEFAULT_PALETTE = new BasicGraphPalette();
-
- // Maximum width for the drawing (in pixels).
- private static final int MAXIMUM_DRAWING_WIDTH = 2000;
-
- private MercatorProjection projection;
-
- // Width and height of the image
- private int width, height;
-
- // Zoom controls
- private MapZoomControls zoomControls;
- private ZoomAndPanListener zoomAndPanListener;
-
- //
- private Image graphImage = null;
- private Graphics2D graphGraphics = null;
-
- // List of image for markers
- private List<BasicOverlay> overlays = Collections
- .synchronizedList(new ArrayList<BasicOverlay>());
-
- // Mapping DrawingClickListener -> MouseEventListener
- private List<DrawingClickListener> drawingClickListeners = new ArrayList<>();
-
- /**
- * Create a new BasicDrawing.
- *
- */
- public BasicDrawing() {
- setLayout(null);
- this.setBackground(new Color(245, 245, 245));
- this.zoomAndPanListener = new ZoomAndPanListener(this,
- ZoomAndPanListener.DEFAULT_MIN_ZOOM_LEVEL, 20, 1.2);
-
- // Try...
- try {
- this.zoomControls = new MapZoomControls(this, 0,
- ZoomAndPanListener.DEFAULT_MIN_ZOOM_LEVEL, 20);
- this.zoomControls.addZoomInListener(new ActionListener() {
- @Override
- public void actionPerformed(ActionEvent e) {
- zoomAndPanListener.zoomIn();
- }
- });
- this.zoomControls.addZoomOutListener(new ActionListener() {
- @Override
- public void actionPerformed(ActionEvent e) {
- zoomAndPanListener.zoomOut();
- }
- });
- }
- catch (IOException e) {
- e.printStackTrace();
- }
-
- this.addMouseListener(new MouseAdapter() {
- @Override
- public void mouseClicked(MouseEvent evt) {
- if (zoomControls.contains(evt.getPoint())) {
- return;
- }
- Point lonlat = null;
- try {
- lonlat = getLongitudeLatitude(evt);
- }
- catch (NoninvertibleTransformException e) {
- return;
- }
- for (DrawingClickListener listener: drawingClickListeners) {
- listener.mouseClicked(lonlat);
- }
- }
- });
- }
-
- @Override
- public void paintComponent(Graphics g1) {
- super.paintComponent(g1);
- Graphics2D g = (Graphics2D) g1;
- AffineTransform sTransform = g.getTransform();
- g.setColor(this.getBackground());
- g.fillRect(0, 0, getWidth(), getHeight());
- g.setTransform(zoomAndPanListener.getCoordTransform());
-
- if (graphImage != null) {
- // Draw graph
- g.drawImage(graphImage, 0, 0, this);
- }
-
- // Draw markers
- synchronized (overlays) {
- for (BasicOverlay overlay: overlays) {
- overlay.draw(g);
- }
- }
-
- g.setTransform(sTransform);
- if (this.zoomControls != null) {
- this.zoomControls.setZoomLevel(this.zoomAndPanListener.getZoomLevel());
- this.zoomControls.draw(g, getWidth() - this.zoomControls.getWidth() - 20,
- this.getHeight() - this.zoomControls.getHeight() - 10, this);
- }
-
- }
-
- /*
- * (non-Javadoc)
- * @see org.insa.graphics.drawing.Drawing#clear()
- */
- @Override
- public void clear() {
- if (this.graphGraphics != null) {
- this.graphGraphics.clearRect(0, 0, this.width, this.height);
- }
- synchronized (overlays) {
- this.overlays.clear();
- }
- this.repaint();
- }
-
- /*
- * (non-Javadoc)
- * @see org.insa.graphics.drawing.Drawing#clearOverlays()
- */
- @Override
- public void clearOverlays() {
- synchronized (overlays) {
- this.overlays.clear();
- }
- this.repaint();
- }
-
- /**
- * @return The current ZoomAndPanListener associated with this drawing.
- */
- public ZoomAndPanListener getZoomAndPanListener() {
- return this.zoomAndPanListener;
- }
-
- /**
- * Return the longitude and latitude corresponding to the given position of the
- * MouseEvent.
- *
- * @param event MouseEvent from which longitude/latitude should be retrieved.
- *
- * @return Point representing the projection of the MouseEvent position in the
- * graph/map.
- *
- * @throws NoninvertibleTransformException if the actual transformation is
- * invalid.
- */
- protected Point getLongitudeLatitude(MouseEvent event) throws NoninvertibleTransformException {
- // Get the point using the inverse transform of the Zoom/Pan object, this gives
- // us
- // a point within the drawing box (between [0, 0] and [width, height]).
- Point2D ptDst = this.zoomAndPanListener.getCoordTransform()
- .inverseTransform(event.getPoint(), null);
-
- // Inverse the "projection" on x/y to get longitude and latitude.
- return new Point(projection.pixelXToLongitude(ptDst.getX()),
- projection.pixelYToLatitude(ptDst.getY()));
- }
-
- /*
- * (non-Javadoc)
- * @see
- * org.insa.graphics.drawing.Drawing#addDrawingClickListener(org.insa.graphics.
- * drawing.DrawingClickListener)
- */
- @Override
- public void addDrawingClickListener(DrawingClickListener listener) {
- this.drawingClickListeners.add(listener);
- }
-
- /*
- * (non-Javadoc)
- * @see org.insa.graphics.drawing.Drawing#removeDrawingClickListener(org.insa.
- * graphics.drawing.DrawingClickListener)
- */
- @Override
- public void removeDrawingClickListener(DrawingClickListener listener) {
- this.drawingClickListeners.remove(listener);
- }
-
- public BasicMarkerOverlay createMarker(Point point, Color color) {
- return new BasicMarkerOverlay(point, color);
- }
-
- @Override
- public MarkerOverlay drawMarker(Point point, Color color) {
- BasicMarkerOverlay marker = createMarker(point, color);
- synchronized (overlays) {
- this.overlays.add(marker);
- }
- this.repaint();
- return marker;
- }
-
- @Override
- public PointSetOverlay createPointSetOverlay() {
- BasicPointSetOverlay ps = new BasicPointSetOverlay();
- synchronized (overlays) {
- this.overlays.add(ps);
- }
- return ps;
- }
-
- @Override
- public PointSetOverlay createPointSetOverlay(int width, Color color) {
- PointSetOverlay ps = createPointSetOverlay();
- ps.setWidthAndColor(width, color);
- return ps;
- }
-
- /**
- * Draw the given arc.
- *
- * @param arc Arc to draw.
- * @param palette Palette to use to retrieve color and width for arc, or null to
- * use current settings.
- */
- protected void drawArc(Arc arc, GraphPalette palette, boolean repaint) {
- List<Point> pts = arc.getPoints();
- if (!pts.isEmpty()) {
- if (palette != null) {
- this.graphGraphics.setColor(palette.getColorForArc(arc));
- this.graphGraphics.setStroke(new BasicStroke(palette.getWidthForArc(arc)));
- }
- Iterator<Point> it1 = pts.iterator();
- Point prev = it1.next();
- while (it1.hasNext()) {
- Point curr = it1.next();
-
- int x1 = projection.longitudeToPixelX(prev.getLongitude());
- int x2 = projection.longitudeToPixelX(curr.getLongitude());
- int y1 = projection.latitudeToPixelY(prev.getLatitude());
- int y2 = projection.latitudeToPixelY(curr.getLatitude());
-
- graphGraphics.drawLine(x1, y1, x2, y2);
- prev = curr;
- }
- }
- if (repaint) {
- this.repaint();
- }
- }
-
- /**
- * Initialize the drawing for the given graph.
- *
- * @param graph
- */
- protected void initialize(Graph graph) {
-
- // Clear everything.
- this.clear();
-
- BoundingBox box = graph.getGraphInformation().getBoundingBox();
-
- // Find minimum/maximum longitude and latitude.
- float minLon = box.getTopLeftPoint().getLongitude(),
- maxLon = box.getBottomRightPoint().getLongitude(),
- minLat = box.getBottomRightPoint().getLatitude(),
- maxLat = box.getTopLeftPoint().getLatitude();
-
- // Add a little delta to avoid drawing on the edge...
- float diffLon = maxLon - minLon, diffLat = maxLat - minLat;
- float deltaLon = 0.01f * diffLon, deltaLat = 0.01f * diffLat;
-
- // Create the projection and retrieve width and height for the box.
- projection = new MercatorProjection(box.extend(deltaLon, deltaLat, deltaLon, deltaLat),
- MAXIMUM_DRAWING_WIDTH);
- this.width = (int) projection.getImageWidth();
- this.height = (int) projection.getImageHeight();
-
- // Create the image
- BufferedImage img = new BufferedImage(this.width, this.height,
- BufferedImage.TYPE_3BYTE_BGR);
- this.graphImage = img;
- this.graphGraphics = img.createGraphics();
- this.graphGraphics.setBackground(this.getBackground());
- this.graphGraphics.clearRect(0, 0, this.width, this.height);
-
- // Set the zoom and pan listener
-
- double scale = 1 / Math.max(this.width / (double) this.getWidth(),
- this.height / (double) this.getHeight());
-
- this.zoomAndPanListener.setCoordTransform(this.graphGraphics.getTransform());
- this.zoomAndPanListener.getCoordTransform().translate(
- (this.getWidth() - this.width * scale) / 2,
- (this.getHeight() - this.height * scale) / 2);
- this.zoomAndPanListener.getCoordTransform().scale(scale, scale);
- this.zoomAndPanListener.setZoomLevel(0);
- this.zoomControls.setZoomLevel(0);
-
- // Repaint
- this.repaint();
- }
-
- @Override
- public void drawGraph(Graph graph, GraphPalette palette) {
- int repaintModulo = graph.getNodes().size() / 100;
-
- // Initialize the buffered image
-
- this.initialize(graph);
-
- // Remove zoom and pan listener
- this.removeMouseListener(zoomAndPanListener);
- this.removeMouseMotionListener(zoomAndPanListener);
- this.removeMouseWheelListener(zoomAndPanListener);
-
- for (Node node: graph.getNodes()) {
- for (Arc arc: node.getSuccessors()) {
- // Draw arcs only if there are one-way arcs or if origin is lower than
- // destination, avoid drawing two-ways arc twice.
- if (arc.getRoadInformation().isOneWay()
- || arc.getOrigin().compareTo(arc.getDestination()) < 0) {
- drawArc(arc, palette, false);
- }
- }
- if (node.getId() % repaintModulo == 0) {
- this.repaint();
- }
- }
- this.repaint();
-
- // Re-add zoom and pan listener
- this.addMouseListener(zoomAndPanListener);
- this.addMouseMotionListener(zoomAndPanListener);
- this.addMouseWheelListener(zoomAndPanListener);
- }
-
- @Override
- public void drawGraph(Graph graph) {
- drawGraph(graph, DEFAULT_PALETTE);
- }
-
- @Override
- public PathOverlay drawPath(Path path, Color color, boolean markers) {
- List<Point> points = new ArrayList<Point>();
- if (!path.isEmpty()) {
- points.add(path.getOrigin().getPoint());
- for (Arc arc: path.getArcs()) {
- Iterator<Point> itPoint = arc.getPoints().iterator();
- // Discard origin each time
- itPoint.next();
- while (itPoint.hasNext()) {
- points.add(itPoint.next());
- }
- }
- }
- BasicMarkerOverlay origin = null, destination = null;
- if (markers && !path.isEmpty()) {
- origin = createMarker(path.getOrigin().getPoint(), color);
- destination = createMarker(path.getDestination().getPoint(), color);
- }
- BasicPathOverlay overlay = new BasicPathOverlay(points, color, origin, destination);
- synchronized (overlays) {
- this.overlays.add(overlay);
- }
- this.repaint();
- return overlay;
- }
-
- @Override
- public PathOverlay drawPath(Path path, Color color) {
- return drawPath(path, color, true);
- }
-
- @Override
- public PathOverlay drawPath(Path path) {
- return drawPath(path, DEFAULT_PATH_COLOR);
- }
-
- @Override
- public PathOverlay drawPath(Path path, boolean markers) {
- return drawPath(path, DEFAULT_PATH_COLOR, markers);
- }
-
- }
|