diff --git a/.classpath b/.classpath index a2f75d1..668b23b 100644 --- a/.classpath +++ b/.classpath @@ -1,69 +1,63 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..d59e09c --- /dev/null +++ b/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,5 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/.settings/org.eclipse.m2e.core.prefs b/.settings/org.eclipse.m2e.core.prefs new file mode 100644 index 0000000..14b697b --- /dev/null +++ b/.settings/org.eclipse.m2e.core.prefs @@ -0,0 +1,4 @@ +activeProfiles= +eclipse.preferences.version=1 +resolveWorkspaceProjects=true +version=1 diff --git a/src/main/org/insa/algo/weakconnectivity/WeaklyConnectedComponentGraphicObserver.java b/src/main/org/insa/algo/weakconnectivity/WeaklyConnectedComponentGraphicObserver.java index c5cfa53..c2657ce 100644 --- a/src/main/org/insa/algo/weakconnectivity/WeaklyConnectedComponentGraphicObserver.java +++ b/src/main/org/insa/algo/weakconnectivity/WeaklyConnectedComponentGraphicObserver.java @@ -7,33 +7,31 @@ import org.insa.drawing.Drawing; import org.insa.graph.Node; public class WeaklyConnectedComponentGraphicObserver implements WeaklyConnectedComponentObserver { - - private static final Color[] COLORS = { - Color.BLUE, Color.ORANGE, Color.GREEN, Color.YELLOW, Color.RED - }; - // Drawing + Graph drawing - private Drawing drawing; - - // Current index color - private int cindex = -1; - - public WeaklyConnectedComponentGraphicObserver(Drawing drawing) { - this.drawing = drawing; - } + private static final Color[] COLORS = { Color.BLUE, Color.ORANGE, Color.GREEN, Color.YELLOW, Color.RED }; - @Override - public void notifyStartComponent(Node curNode) { - cindex = (cindex + 1) % COLORS.length; - } + // Drawing + Graph drawing + private Drawing drawing; - @Override - public void notifyNewNodeInComponent(Node node) { - this.drawing.drawMarker(node.getPoint(), COLORS[cindex]); - } + // Current index color + private int cindex = -1; - @Override - public void notifyEndComponent(ArrayList nodes) { - } + public WeaklyConnectedComponentGraphicObserver(Drawing drawing) { + this.drawing = drawing; + } + + @Override + public void notifyStartComponent(Node curNode) { + cindex = (cindex + 1) % COLORS.length; + } + + @Override + public void notifyNewNodeInComponent(Node node) { + this.drawing.drawPoint(node.getPoint(), 1, COLORS[cindex]); + } + + @Override + public void notifyEndComponent(ArrayList nodes) { + } } diff --git a/src/main/org/insa/base/MainWindow.java b/src/main/org/insa/base/MainWindow.java index 28b5c30..34da2b0 100644 --- a/src/main/org/insa/base/MainWindow.java +++ b/src/main/org/insa/base/MainWindow.java @@ -1,651 +1,625 @@ -package org.insa.base; - -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Component; -import java.awt.Dimension; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.KeyEvent; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.awt.event.WindowAdapter; -import java.awt.event.WindowEvent; -import java.awt.geom.NoninvertibleTransformException; -import java.io.DataInputStream; -import java.io.File; -import java.io.IOException; -import java.io.OutputStream; -import java.io.PrintStream; -import java.time.Duration; -import java.time.Instant; -import java.util.ArrayList; - -import javax.swing.BorderFactory; -import javax.swing.JButton; -import javax.swing.JFileChooser; -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JMenu; -import javax.swing.JMenuBar; -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JSplitPane; -import javax.swing.JTextArea; -import javax.swing.KeyStroke; -import javax.swing.SwingConstants; -import javax.swing.Timer; -import javax.swing.UIManager; -import javax.swing.filechooser.FileNameExtensionFilter; - -import org.insa.algo.shortestpath.BellmanFordAlgorithm; -import org.insa.algo.shortestpath.ShortestPathAlgorithm; -import org.insa.algo.shortestpath.ShortestPathData; -import org.insa.algo.shortestpath.ShortestPathData.Mode; -import org.insa.algo.shortestpath.ShortestPathGraphicObserver; -import org.insa.algo.shortestpath.ShortestPathSolution; -import org.insa.algo.weakconnectivity.WeaklyConnectedComponentGraphicObserver; -import org.insa.algo.weakconnectivity.WeaklyConnectedComponentsAlgorithm; -import org.insa.algo.weakconnectivity.WeaklyConnectedComponentsData; -import org.insa.drawing.BasicDrawing; -import org.insa.drawing.BlackAndWhiteGraphPalette; -import org.insa.drawing.Drawing; -import org.insa.drawing.MapViewDrawing; -import org.insa.graph.Graph; -import org.insa.graph.Node; -import org.insa.graph.Path; -import org.insa.graph.Point; -import org.insa.graph.io.AbstractGraphReader; -import org.insa.graph.io.BinaryGraphReader; -import org.insa.graph.io.BinaryGraphReaderV2; -import org.insa.graph.io.BinaryPathReader; -import org.insa.graph.io.MapMismatchException; -import org.insa.graph.io.Openfile; - -public class MainWindow extends JFrame { - - protected class JOutputStream extends OutputStream { - private JTextArea textArea; - - public JOutputStream(JTextArea textArea) { - this.textArea = textArea; - } - - @Override - public void write(int b) throws IOException { - // redirects data to the text area - textArea.setText(textArea.getText() + String.valueOf((char) b)); - // scrolls the text area to the end of data - textArea.setCaretPosition(textArea.getDocument().getLength()); - // keeps the textArea up to date - textArea.update(textArea.getGraphics()); - } - } - - protected interface CallableWithNodes { - - void call(ArrayList nodes); - - }; - - protected class DrawingClickListener extends MouseAdapter { - - // Enable/Disable. - private boolean enabled = false; - - // List of points. - private ArrayList points = new ArrayList(); - - // Number of points to find before running. - private int nTargetPoints = 0; - - // Callable to call when points are reached. - CallableWithNodes callable = null; - - /** - * @return true if this listener is enabled. - */ - public boolean isEnabled() { - return enabled; - } - - /** - * Enable this listener. - * - * @param nTargetPoints Number of point to found before calling the callable. - */ - public void enable(int nTargetPoints, CallableWithNodes callable) { - this.enabled = true; - this.nTargetPoints = nTargetPoints; - this.points.clear(); - this.callable = callable; - } - - /** - * Disable this listener. - */ - public void disable() { - this.enabled = false; - } - - public void mouseClicked(MouseEvent evt) { - if (!isEnabled()) { - return; - } - Point lonlat; - try { - // TODO: Fix - lonlat = ((BasicDrawing) drawing).getLongitudeLatitude(evt); - } - catch (NoninvertibleTransformException e) { - // Should never happens in "normal" circumstances... - e.printStackTrace(); - return; - } - - Node node = graph.findClosestNode(lonlat); - - drawing.drawMarker(node.getPoint(), Color.BLUE); - points.add(node); - if (points.size() == nTargetPoints) { - callable.call(points); - this.disable(); - } - } - }; - - /** - * - */ - private static final long serialVersionUID = -527660583705140687L; - - /** - * - */ - private static final String WINDOW_TITLE = "BE Graphes INSA"; - - /** - * - */ - private static final int THREAD_TIMER_DELAY = 1000; // in milliseconds - - // Current graph. - private Graph graph; - - // Current loaded path. - private Path currentPath; - - // Drawing and click adapter. - private Drawing drawing; - private DrawingClickListener clickAdapter; - - // Main panel. - private JSplitPane mainPanel; - - // List of item for the top menus. - private JMenuItem openMapItem; - - // List of items that cannot be used without a graph - private ArrayList graphLockItems = new ArrayList(); - - // Label containing the map ID of the current graph. - private JLabel mapIdPanel; - - // Thread information - private Instant threadStartTime; - private Timer threadTimer; - private JPanel threadPanel; - - // Log stream and print stream - private JOutputStream logStream; - - @SuppressWarnings("unused") - private PrintStream printStream; - - // Current running thread - private Thread currentThread; - - public MainWindow() { - super(WINDOW_TITLE); - setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); - setLayout(new BorderLayout()); - setJMenuBar(createMenuBar()); - - addWindowListener(new WindowAdapter() { - public void windowClosing(WindowEvent e) { - int confirmed = JOptionPane.showConfirmDialog(null, - "Are you sure you want to close the application?", "Exit Confirmation", - JOptionPane.YES_NO_OPTION); - - if (confirmed == JOptionPane.YES_OPTION) { - dispose(); - System.exit(0); - } - } - }); - - // Create graph area - mainPanel = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); - - BasicDrawing drawing = new BasicDrawing(); - // MapViewDrawing drawing = new MapViewDrawing(); - - Component drawingComponent = drawing; - this.drawing = drawing; - - // Click adapter - this.clickAdapter = new DrawingClickListener(); - // drawing.addMouseListener(this.clickAdapter); - - JTextArea infoPanel = new JTextArea(); - infoPanel.setMinimumSize(new Dimension(200, 50)); - // infoPanel.setBorder(BorderFactory.createMatteBorder(0, 1, 0, 0, Color.GRAY)); - infoPanel.setBackground(Color.WHITE); - infoPanel.setLineWrap(true); - infoPanel.setEditable(false); - this.logStream = new JOutputStream(infoPanel); - this.printStream = new PrintStream(this.logStream); - - mainPanel.setResizeWeight(0.8); - // sp.setEnabled(false); - mainPanel.setDividerSize(5); - - mainPanel.setBackground(Color.WHITE); - mainPanel.add(drawingComponent); - mainPanel.add(new JScrollPane(infoPanel)); - this.add(mainPanel, BorderLayout.CENTER); - - // Top Panel - this.add(createTopPanel(), BorderLayout.NORTH); - this.add(createStatusBar(), BorderLayout.SOUTH); - } - - private void restartThreadTimer() { - threadStartTime = Instant.now(); - threadTimer.restart(); - } - - private void stopThreadTimer() { - threadTimer.stop(); - } - - /** - * @param runnable - * @param canInterrupt - */ - private void launchThread(Runnable runnable, boolean canInterrupt) { - if (canInterrupt) { - currentThread = new Thread(new Runnable() { - @Override - public void run() { - restartThreadTimer(); - threadPanel.setVisible(true); - runnable.run(); - clearCurrentThread(); - } - }); - } - else { - currentThread = new Thread(runnable); - } - currentThread.start(); - } - - private void launchThread(Runnable runnable) { - launchThread(runnable, true); - } - - private void clearCurrentThread() { - stopThreadTimer(); - threadPanel.setVisible(false); - currentThread = null; - } - - private void launchShortestPathThread(ShortestPathAlgorithm spAlgorithm) { - spAlgorithm.addObserver(new ShortestPathGraphicObserver(drawing)); - // algo.addObserver(new ShortestPathTextObserver(printStream)); - launchThread(new Runnable() { - @Override - public void run() { - ShortestPathSolution solution = spAlgorithm.run(); - if (solution != null && solution.isFeasible()) { - drawing.drawPath(solution.getPath()); - } - } - }); - } - - private JMenuBar createMenuBar() { - - // Open Map item... - openMapItem = new JMenuItem("Open Map... ", KeyEvent.VK_O); - openMapItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, ActionEvent.ALT_MASK)); - openMapItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - JFileChooser chooser = new JFileChooser(); - FileNameExtensionFilter filter = new FileNameExtensionFilter( - "Map & compressed map files", "map", "map2", "mapgr", "map.gz"); - chooser.setCurrentDirectory(new File(System.getProperty("user.dir"))); - chooser.setFileFilter(filter); - if (chooser.showOpenDialog(MainWindow.this) == JFileChooser.APPROVE_OPTION) { - launchThread(new Runnable() { - @Override - public void run() { - String path = chooser.getSelectedFile().getAbsolutePath(); - DataInputStream stream; - try { - stream = Openfile.open(path); - } - catch (IOException e1) { - JOptionPane.showMessageDialog(MainWindow.this, - "Cannot open the selected file."); - return; - } - AbstractGraphReader reader; - if (path.endsWith(".map2") || path.endsWith("mapgr")) { - reader = new BinaryGraphReaderV2(stream); - } - else { - reader = new BinaryGraphReader(stream); - } - try { - graph = reader.read(); - } - catch (Exception exception) { - JOptionPane.showMessageDialog(MainWindow.this, - "Unable to read graph from the selected file."); - exception.printStackTrace(System.out); - return; - } - drawing.clear(); - drawing.drawGraph(graph); - - for (JMenuItem item: graphLockItems) { - item.setEnabled(true); - } - mapIdPanel - .setText("Map ID: 0x" + Integer.toHexString(graph.getMapId())); - } - }, false); - } - } - }); - - // Open Path item... - JMenuItem openPathItem = new JMenuItem("Open Path... ", KeyEvent.VK_P); - openPathItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_P, ActionEvent.ALT_MASK)); - openPathItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - JFileChooser chooser = new JFileChooser(); - FileNameExtensionFilter filter = new FileNameExtensionFilter( - "Path & compressed path files", "path", "path.gz"); - chooser.setCurrentDirectory(new File(System.getProperty("user.dir"))); - chooser.setFileFilter(filter); - if (chooser.showOpenDialog(MainWindow.this) == JFileChooser.APPROVE_OPTION) { - BinaryPathReader reader; - try { - reader = new BinaryPathReader( - Openfile.open(chooser.getSelectedFile().getAbsolutePath())); - } - catch (IOException e1) { - JOptionPane.showMessageDialog(MainWindow.this, - "Cannot open the selected file."); - return; - } - try { - currentPath = reader.readPath(graph); - } - catch (MapMismatchException exception) { - JOptionPane.showMessageDialog(MainWindow.this, - "The selected file does not contain a path for the current graph."); - return; - } - catch (Exception exception) { - JOptionPane.showMessageDialog(MainWindow.this, - "Unable to read path from the selected file."); - return; - } - drawing.drawPath(currentPath); - } - } - }); - graphLockItems.add(openPathItem); - - // Close item - JMenuItem closeItem = new JMenuItem("Quit", KeyEvent.VK_Q); - closeItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q, ActionEvent.ALT_MASK)); - closeItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - MainWindow.this.dispatchEvent( - new WindowEvent(MainWindow.this, WindowEvent.WINDOW_CLOSING)); - } - }); - - // Build the first menu. - JMenu fileMenu = new JMenu("File"); - fileMenu.add(openMapItem); - fileMenu.add(openPathItem); - fileMenu.addSeparator(); - fileMenu.add(closeItem); - - // Second menu - JMenuItem drawGraphItem = new JMenuItem("Redraw", KeyEvent.VK_R); - drawGraphItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R, ActionEvent.ALT_MASK)); - drawGraphItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - launchThread(new Runnable() { - @Override - public void run() { - if (!(drawing instanceof BasicDrawing)) { - BasicDrawing tmp = new BasicDrawing(); - mainPanel.setLeftComponent(tmp); - drawing = tmp; - } - drawing.clear(); - drawing.drawGraph(graph); - } - }); - } - }); - graphLockItems.add(drawGraphItem); - JMenuItem drawGraphBWItem = new JMenuItem("Redraw (B&W)", KeyEvent.VK_B); - drawGraphBWItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_B, ActionEvent.ALT_MASK)); - drawGraphBWItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - launchThread(new Runnable() { - @Override - public void run() { - if (!(drawing instanceof BasicDrawing)) { - BasicDrawing tmp = new BasicDrawing(); - mainPanel.setLeftComponent(tmp); - drawing = tmp; - } - drawing.clear(); - drawing.drawGraph(graph, new BlackAndWhiteGraphPalette()); - } - }); - } - }); - graphLockItems.add(drawGraphBWItem); - JMenuItem drawGraphMapsforgeItem = new JMenuItem("Redraw (Map)", KeyEvent.VK_M); - drawGraphMapsforgeItem - .setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_M, ActionEvent.ALT_MASK)); - drawGraphMapsforgeItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - launchThread(new Runnable() { - @Override - public void run() { - if (!(drawing instanceof MapViewDrawing)) { - MapViewDrawing tmp = new MapViewDrawing(); - mainPanel.setLeftComponent(tmp); - drawing = tmp; - } - drawing.clear(); - drawing.drawGraph(graph, new BlackAndWhiteGraphPalette()); - } - }); - } - }); - graphLockItems.add(drawGraphMapsforgeItem); - - JMenu graphMenu = new JMenu("Graph"); - graphMenu.add(drawGraphItem); - graphMenu.add(drawGraphBWItem); - graphMenu.addSeparator(); - graphMenu.add(drawGraphMapsforgeItem); - - // Algo menu - JMenu algoMenu = new JMenu("Algorithms"); - - // Weakly connected components - JMenuItem wccItem = new JMenuItem("Weakly Connected Components"); - wccItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - WeaklyConnectedComponentsData instance = new WeaklyConnectedComponentsData(graph); - WeaklyConnectedComponentsAlgorithm algo = new WeaklyConnectedComponentsAlgorithm( - instance); - algo.addObserver(new WeaklyConnectedComponentGraphicObserver(drawing)); - // algo.addObserver(new WeaklyConnectedComponentTextObserver(printStream)); - launchThread(new Runnable() { - @Override - public void run() { - algo.run(); - } - }); - } - }); - - // Shortest path - JMenuItem bellmanItem = new JMenuItem("Shortest Path (Bellman-Ford)"); - bellmanItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - int idx = JOptionPane.showOptionDialog(MainWindow.this, "Which mode do you want?", - "Mode selection", JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, - null, Mode.values(), Mode.LENGTH); - - if (idx != -1) { - Mode mode = Mode.values()[idx]; - clickAdapter.enable(2, new CallableWithNodes() { - @Override - public void call(ArrayList nodes) { - launchShortestPathThread(new BellmanFordAlgorithm( - new ShortestPathData(graph, nodes.get(0), nodes.get(1), mode))); - } - }); - } - } - }); - graphLockItems.add(wccItem); - graphLockItems.add(bellmanItem); - - algoMenu.add(wccItem); - algoMenu.addSeparator(); - algoMenu.add(bellmanItem); - // algoMenu.add(djikstraItem); - // algoMenu.add(aStarItem); - - // Create the menu bar. - JMenuBar menuBar = new JMenuBar(); - - menuBar.add(fileMenu); - menuBar.add(graphMenu); - menuBar.add(algoMenu); - - for (JMenuItem item: graphLockItems) { - item.setEnabled(false); - } - - return menuBar; - } - - @SuppressWarnings("deprecation") - private void stopCurrentThread() { - // Should not be used in production code, but here I have no idea how - // to do this properly... Cannot use .interrupt() because it would requires - // the algorithm to watch the ThreadInteruption exception. - currentThread.stop(); - } - - private JPanel createStatusBar() { - // create the status bar panel and shove it down the bottom of the frame - JPanel statusPanel = new JPanel(); - statusPanel.setBorder(BorderFactory.createMatteBorder(1, 0, 0, 0, Color.GRAY)); - statusPanel.setPreferredSize(new Dimension(getWidth(), 38)); - statusPanel.setLayout(new BorderLayout()); - - mapIdPanel = new JLabel(); - mapIdPanel.setHorizontalAlignment(SwingConstants.LEFT); - statusPanel.add(mapIdPanel, BorderLayout.WEST); - - JLabel threadInfo = new JLabel("Thread running... "); - JLabel threadTimerLabel = new JLabel("00:00:00"); - JButton threadButton = new JButton("Stop"); - threadButton.addActionListener(new ActionListener() { - - @Override - public void actionPerformed(ActionEvent e) { - if (currentThread != null && currentThread.isAlive()) { - int confirmed = JOptionPane.showConfirmDialog(null, - "Are you sure you want to kill the running thread?", - "Kill Confirmation", JOptionPane.YES_NO_OPTION); - if (confirmed == JOptionPane.YES_OPTION) { - stopCurrentThread(); - clearCurrentThread(); - threadPanel.setVisible(false); - } - } - } - }); - - threadTimer = new Timer(THREAD_TIMER_DELAY, new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - Duration elapsed = Duration.between(threadStartTime, Instant.now()); - long seconds = elapsed.getSeconds(); - threadTimerLabel.setText(String.format("%02d:%02d:%02d", seconds / 3600, - seconds / 60 % 60, seconds % 60)); - } - }); - threadTimer.setInitialDelay(0); - - threadPanel = new JPanel(); - threadPanel.add(threadInfo); - threadPanel.add(threadTimerLabel); - threadPanel.add(threadButton); - threadPanel.setVisible(false); - statusPanel.add(threadPanel, BorderLayout.EAST); - - return statusPanel; - } - - protected JPanel createTopPanel() { - JPanel topPanel = new JPanel(); - - return topPanel; - } - - public static void main(final String[] args) { - - // Try to set system look and feel. - try { - UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); - } - catch (Exception e) { - } - - MainWindow w = new MainWindow(); - w.setExtendedState(JFrame.MAXIMIZED_BOTH); - w.setVisible(true); - } - -} +package org.insa.base; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.io.DataInputStream; +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; +import java.time.Duration; +import java.time.Instant; +import java.util.ArrayList; + +import javax.swing.BorderFactory; +import javax.swing.JButton; +import javax.swing.JFileChooser; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JSplitPane; +import javax.swing.JTextArea; +import javax.swing.KeyStroke; +import javax.swing.SwingConstants; +import javax.swing.Timer; +import javax.swing.UIManager; +import javax.swing.filechooser.FileNameExtensionFilter; + +import org.insa.algo.shortestpath.BellmanFordAlgorithm; +import org.insa.algo.shortestpath.ShortestPathAlgorithm; +import org.insa.algo.shortestpath.ShortestPathData; +import org.insa.algo.shortestpath.ShortestPathData.Mode; +import org.insa.algo.shortestpath.ShortestPathGraphicObserver; +import org.insa.algo.shortestpath.ShortestPathSolution; +import org.insa.algo.weakconnectivity.WeaklyConnectedComponentGraphicObserver; +import org.insa.algo.weakconnectivity.WeaklyConnectedComponentsAlgorithm; +import org.insa.algo.weakconnectivity.WeaklyConnectedComponentsData; +import org.insa.drawing.BasicDrawing; +import org.insa.drawing.BlackAndWhiteGraphPalette; +import org.insa.drawing.Drawing; +import org.insa.drawing.DrawingClickListener; +import org.insa.drawing.MapViewDrawing; +import org.insa.graph.Graph; +import org.insa.graph.Node; +import org.insa.graph.Path; +import org.insa.graph.Point; +import org.insa.graph.io.AbstractGraphReader; +import org.insa.graph.io.BinaryGraphReader; +import org.insa.graph.io.BinaryGraphReaderV2; +import org.insa.graph.io.BinaryPathReader; +import org.insa.graph.io.MapMismatchException; +import org.insa.graph.io.Openfile; + +public class MainWindow extends JFrame { + + protected class JOutputStream extends OutputStream { + private JTextArea textArea; + + public JOutputStream(JTextArea textArea) { + this.textArea = textArea; + } + + @Override + public void write(int b) throws IOException { + // redirects data to the text area + textArea.setText(textArea.getText() + String.valueOf((char) b)); + // scrolls the text area to the end of data + textArea.setCaretPosition(textArea.getDocument().getLength()); + // keeps the textArea up to date + textArea.update(textArea.getGraphics()); + } + } + + protected interface CallableWithNodes { + + void call(ArrayList nodes); + + }; + + protected class MultiPointsClickListener implements DrawingClickListener { + + // Enable/Disable. + private boolean enabled = false; + + // List of points. + private ArrayList points = new ArrayList(); + + // Number of points to find before running. + private int nTargetPoints = 0; + + // Callable to call when points are reached. + CallableWithNodes callable = null; + + /** + * @return true if this listener is enabled. + */ + public boolean isEnabled() { + return enabled; + } + + /** + * Enable this listener. + * + * @param nTargetPoints + * Number of point to found before calling the callable. + */ + public void enable(int nTargetPoints, CallableWithNodes callable) { + this.enabled = true; + this.nTargetPoints = nTargetPoints; + this.points.clear(); + this.callable = callable; + } + + /** + * Disable this listener. + */ + public void disable() { + this.enabled = false; + } + + @Override + public void mouseClicked(Point lonlat) { + if (!isEnabled()) { + return; + } + Node node = graph.findClosestNode(lonlat); + drawing.drawMarker(node.getPoint(), Color.BLUE); + points.add(node); + System.out.println("Click at " + lonlat + ", " + points.size() + "/" + nTargetPoints + " in array."); + if (points.size() == nTargetPoints) { + callable.call(points); + this.disable(); + } + } + }; + + /** + * + */ + private static final long serialVersionUID = -527660583705140687L; + + /** + * + */ + private static final String WINDOW_TITLE = "BE Graphes INSA"; + + /** + * + */ + private static final int THREAD_TIMER_DELAY = 1000; // in milliseconds + + // Current graph. + private Graph graph; + + // Current loaded path. + private Path currentPath; + + // Drawing and click adapter. + private Drawing drawing; + private MultiPointsClickListener clickAdapter; + + // Main panel. + private JSplitPane mainPanel; + + // List of item for the top menus. + private JMenuItem openMapItem; + + // List of items that cannot be used without a graph + private ArrayList graphLockItems = new ArrayList(); + + // Label containing the map ID of the current graph. + private JLabel mapIdPanel; + + // Thread information + private Instant threadStartTime; + private Timer threadTimer; + private JPanel threadPanel; + + // Log stream and print stream + private JOutputStream logStream; + + @SuppressWarnings("unused") + private PrintStream printStream; + + // Current running thread + private Thread currentThread; + + public MainWindow() { + super(WINDOW_TITLE); + setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); + setLayout(new BorderLayout()); + setJMenuBar(createMenuBar()); + + addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) { + int confirmed = JOptionPane.showConfirmDialog(null, "Are you sure you want to close the application?", + "Exit Confirmation", JOptionPane.YES_NO_OPTION); + + if (confirmed == JOptionPane.YES_OPTION) { + dispose(); + System.exit(0); + } + } + }); + + // Create graph area + mainPanel = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); + + this.drawing = new BasicDrawing(); + + // Click adapter + this.clickAdapter = new MultiPointsClickListener(); + this.drawing.addDrawingClickListener(this.clickAdapter); + + JTextArea infoPanel = new JTextArea(); + infoPanel.setMinimumSize(new Dimension(200, 50)); + infoPanel.setBackground(Color.WHITE); + infoPanel.setLineWrap(true); + infoPanel.setEditable(false); + this.logStream = new JOutputStream(infoPanel); + this.printStream = new PrintStream(this.logStream); + + mainPanel.setResizeWeight(0.8); + // sp.setEnabled(false); + mainPanel.setDividerSize(5); + + mainPanel.setBackground(Color.WHITE); + mainPanel.setLeftComponent((Component) this.drawing); + mainPanel.setRightComponent(new JScrollPane(infoPanel)); + this.add(mainPanel, BorderLayout.CENTER); + + // Top Panel + this.add(createTopPanel(), BorderLayout.NORTH); + this.add(createStatusBar(), BorderLayout.SOUTH); + } + + private void restartThreadTimer() { + threadStartTime = Instant.now(); + threadTimer.restart(); + } + + private void stopThreadTimer() { + threadTimer.stop(); + } + + /** + * @param runnable + * @param canInterrupt + */ + private void launchThread(Runnable runnable, boolean canInterrupt) { + if (canInterrupt) { + currentThread = new Thread(new Runnable() { + @Override + public void run() { + restartThreadTimer(); + threadPanel.setVisible(true); + runnable.run(); + clearCurrentThread(); + } + }); + } + else { + currentThread = new Thread(runnable); + } + currentThread.start(); + } + + private void launchThread(Runnable runnable) { + launchThread(runnable, true); + } + + private void clearCurrentThread() { + stopThreadTimer(); + threadPanel.setVisible(false); + currentThread = null; + } + + private void launchShortestPathThread(ShortestPathAlgorithm spAlgorithm) { + spAlgorithm.addObserver(new ShortestPathGraphicObserver(drawing)); + // algo.addObserver(new ShortestPathTextObserver(printStream)); + launchThread(new Runnable() { + @Override + public void run() { + ShortestPathSolution solution = spAlgorithm.run(); + if (solution != null && solution.isFeasible()) { + drawing.drawPath(solution.getPath()); + } + } + }); + } + + private void updateDrawing(Class newClass) { + + drawing.clear(); + if (drawing == null || !newClass.isInstance(drawing)) { + try { + drawing = newClass.newInstance(); + } + catch (InstantiationException | IllegalAccessException e) { + e.printStackTrace(); + } + drawing.addDrawingClickListener(this.clickAdapter); + } + mainPanel.setLeftComponent((Component) drawing); + } + + private JMenuBar createMenuBar() { + + // Open Map item... + openMapItem = new JMenuItem("Open Map... ", KeyEvent.VK_O); + openMapItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, ActionEvent.ALT_MASK)); + openMapItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + JFileChooser chooser = new JFileChooser(); + FileNameExtensionFilter filter = new FileNameExtensionFilter("Map & compressed map files", "map", + "map2", "mapgr", "map.gz"); + chooser.setCurrentDirectory(new File(System.getProperty("user.dir"))); + chooser.setFileFilter(filter); + if (chooser.showOpenDialog(MainWindow.this) == JFileChooser.APPROVE_OPTION) { + launchThread(new Runnable() { + @Override + public void run() { + String path = chooser.getSelectedFile().getAbsolutePath(); + DataInputStream stream; + try { + stream = Openfile.open(path); + } + catch (IOException e1) { + JOptionPane.showMessageDialog(MainWindow.this, "Cannot open the selected file."); + return; + } + AbstractGraphReader reader; + if (path.endsWith(".map2") || path.endsWith("mapgr")) { + reader = new BinaryGraphReaderV2(stream); + } + else { + reader = new BinaryGraphReader(stream); + } + try { + graph = reader.read(); + } + catch (Exception exception) { + JOptionPane.showMessageDialog(MainWindow.this, + "Unable to read graph from the selected file."); + exception.printStackTrace(System.out); + return; + } + drawing.clear(); + drawing.drawGraph(graph); + + for (JMenuItem item: graphLockItems) { + item.setEnabled(true); + } + mapIdPanel.setText("Map ID: 0x" + Integer.toHexString(graph.getMapId())); + } + }, false); + } + } + }); + + // Open Path item... + JMenuItem openPathItem = new JMenuItem("Open Path... ", KeyEvent.VK_P); + openPathItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_P, ActionEvent.ALT_MASK)); + openPathItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + JFileChooser chooser = new JFileChooser(); + FileNameExtensionFilter filter = new FileNameExtensionFilter("Path & compressed path files", "path", + "path.gz"); + chooser.setCurrentDirectory(new File(System.getProperty("user.dir"))); + chooser.setFileFilter(filter); + if (chooser.showOpenDialog(MainWindow.this) == JFileChooser.APPROVE_OPTION) { + BinaryPathReader reader; + try { + reader = new BinaryPathReader(Openfile.open(chooser.getSelectedFile().getAbsolutePath())); + } + catch (IOException e1) { + JOptionPane.showMessageDialog(MainWindow.this, "Cannot open the selected file."); + return; + } + try { + currentPath = reader.readPath(graph); + } + catch (MapMismatchException exception) { + JOptionPane.showMessageDialog(MainWindow.this, + "The selected file does not contain a path for the current graph."); + return; + } + catch (Exception exception) { + JOptionPane.showMessageDialog(MainWindow.this, "Unable to read path from the selected file."); + return; + } + drawing.drawPath(currentPath); + } + } + }); + graphLockItems.add(openPathItem); + + // Close item + JMenuItem closeItem = new JMenuItem("Quit", KeyEvent.VK_Q); + closeItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q, ActionEvent.ALT_MASK)); + closeItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + MainWindow.this.dispatchEvent(new WindowEvent(MainWindow.this, WindowEvent.WINDOW_CLOSING)); + } + }); + + // Build the first menu. + JMenu fileMenu = new JMenu("File"); + fileMenu.add(openMapItem); + fileMenu.add(openPathItem); + fileMenu.addSeparator(); + fileMenu.add(closeItem); + + // Second menu + JMenuItem drawGraphItem = new JMenuItem("Redraw", KeyEvent.VK_R); + drawGraphItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R, ActionEvent.ALT_MASK)); + drawGraphItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + launchThread(new Runnable() { + @Override + public void run() { + updateDrawing(BasicDrawing.class); + drawing.drawGraph(graph); + } + }); + } + }); + graphLockItems.add(drawGraphItem); + JMenuItem drawGraphBWItem = new JMenuItem("Redraw (B&W)", KeyEvent.VK_B); + drawGraphBWItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_B, ActionEvent.ALT_MASK)); + drawGraphBWItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + launchThread(new Runnable() { + @Override + public void run() { + updateDrawing(BasicDrawing.class); + drawing.drawGraph(graph, new BlackAndWhiteGraphPalette()); + } + }); + } + }); + graphLockItems.add(drawGraphBWItem); + JMenuItem drawGraphMapsforgeItem = new JMenuItem("Redraw (Map)", KeyEvent.VK_M); + drawGraphMapsforgeItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_M, ActionEvent.ALT_MASK)); + drawGraphMapsforgeItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + launchThread(new Runnable() { + @Override + public void run() { + updateDrawing(MapViewDrawing.class); + drawing.drawGraph(graph); + } + }); + } + }); + graphLockItems.add(drawGraphMapsforgeItem); + + JMenu graphMenu = new JMenu("Graph"); + graphMenu.add(drawGraphItem); + graphMenu.add(drawGraphBWItem); + graphMenu.addSeparator(); + graphMenu.add(drawGraphMapsforgeItem); + + // Algo menu + JMenu algoMenu = new JMenu("Algorithms"); + + // Weakly connected components + JMenuItem wccItem = new JMenuItem("Weakly Connected Components"); + wccItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + WeaklyConnectedComponentsData instance = new WeaklyConnectedComponentsData(graph); + WeaklyConnectedComponentsAlgorithm algo = new WeaklyConnectedComponentsAlgorithm(instance); + algo.addObserver(new WeaklyConnectedComponentGraphicObserver(drawing)); + // algo.addObserver(new WeaklyConnectedComponentTextObserver(printStream)); + launchThread(new Runnable() { + @Override + public void run() { + algo.run(); + } + }); + } + }); + + // Shortest path + JMenuItem bellmanItem = new JMenuItem("Shortest Path (Bellman-Ford)"); + bellmanItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + int idx = JOptionPane.showOptionDialog(MainWindow.this, "Which mode do you want?", "Mode selection", + JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null, Mode.values(), Mode.LENGTH); + + if (idx != -1) { + Mode mode = Mode.values()[idx]; + clickAdapter.enable(2, new CallableWithNodes() { + @Override + public void call(ArrayList nodes) { + launchShortestPathThread(new BellmanFordAlgorithm( + new ShortestPathData(graph, nodes.get(0), nodes.get(1), mode))); + } + }); + } + } + }); + graphLockItems.add(wccItem); + graphLockItems.add(bellmanItem); + + algoMenu.add(wccItem); + algoMenu.addSeparator(); + algoMenu.add(bellmanItem); + // algoMenu.add(djikstraItem); + // algoMenu.add(aStarItem); + + // Create the menu bar. + JMenuBar menuBar = new JMenuBar(); + + menuBar.add(fileMenu); + menuBar.add(graphMenu); + menuBar.add(algoMenu); + + for (JMenuItem item: graphLockItems) { + item.setEnabled(false); + } + + return menuBar; + } + + @SuppressWarnings("deprecation") + private void stopCurrentThread() { + // Should not be used in production code, but here I have no idea how + // to do this properly... Cannot use .interrupt() because it would requires + // the algorithm to watch the ThreadInteruption exception. + currentThread.stop(); + } + + private JPanel createStatusBar() { + // create the status bar panel and shove it down the bottom of the frame + JPanel statusPanel = new JPanel(); + statusPanel.setBorder(BorderFactory.createMatteBorder(1, 0, 0, 0, Color.GRAY)); + statusPanel.setPreferredSize(new Dimension(getWidth(), 38)); + statusPanel.setLayout(new BorderLayout()); + + mapIdPanel = new JLabel(); + mapIdPanel.setHorizontalAlignment(SwingConstants.LEFT); + statusPanel.add(mapIdPanel, BorderLayout.WEST); + + JLabel threadInfo = new JLabel("Thread running... "); + JLabel threadTimerLabel = new JLabel("00:00:00"); + JButton threadButton = new JButton("Stop"); + threadButton.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + if (currentThread != null && currentThread.isAlive()) { + int confirmed = JOptionPane.showConfirmDialog(null, + "Are you sure you want to kill the running thread?", "Kill Confirmation", + JOptionPane.YES_NO_OPTION); + if (confirmed == JOptionPane.YES_OPTION) { + stopCurrentThread(); + clearCurrentThread(); + threadPanel.setVisible(false); + } + } + } + }); + + threadTimer = new Timer(THREAD_TIMER_DELAY, new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + Duration elapsed = Duration.between(threadStartTime, Instant.now()); + long seconds = elapsed.getSeconds(); + threadTimerLabel + .setText(String.format("%02d:%02d:%02d", seconds / 3600, seconds / 60 % 60, seconds % 60)); + } + }); + threadTimer.setInitialDelay(0); + + threadPanel = new JPanel(); + threadPanel.add(threadInfo); + threadPanel.add(threadTimerLabel); + threadPanel.add(threadButton); + threadPanel.setVisible(false); + statusPanel.add(threadPanel, BorderLayout.EAST); + + return statusPanel; + } + + protected JPanel createTopPanel() { + JPanel topPanel = new JPanel(); + + return topPanel; + } + + public static void main(final String[] args) { + + // Try to set system look and feel. + try { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } + catch (Exception e) { + } + + MainWindow w = new MainWindow(); + w.setExtendedState(JFrame.MAXIMIZED_BOTH); + w.setVisible(true); + } + +} diff --git a/src/main/org/insa/base/Samples.java b/src/main/org/insa/base/Samples.java deleted file mode 100644 index 720eee8..0000000 --- a/src/main/org/insa/base/Samples.java +++ /dev/null @@ -1,302 +0,0 @@ -/* - * Copyright 2010, 2011, 2012, 2013 mapsforge.org - * Copyright 2014 Christian Pesch - * Copyright 2014 Ludwig M Brinckmann - * Copyright 2014-2018 devemux86 - * Copyright 2017 usrusr - * - * This program is free software: you can redistribute it and/or modify it under the - * terms of the GNU Lesser General Public License as published by the Free Software - * Foundation, either version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A - * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with - * this program. If not, see . - */ -package org.insa.base; - -import java.awt.Dimension; -import java.awt.event.WindowAdapter; -import java.awt.event.WindowEvent; -import java.io.File; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.UUID; -import java.util.prefs.Preferences; - -import javax.swing.JFrame; -import javax.swing.JOptionPane; -import javax.swing.WindowConstants; - -import org.insa.graph.Arc; -import org.insa.graph.Graph; -import org.insa.graph.Path; -import org.insa.graph.io.BinaryGraphReader; -import org.insa.graph.io.BinaryPathReader; -import org.insa.graph.io.Openfile; -import org.mapsforge.core.graphics.Color; -import org.mapsforge.core.graphics.GraphicFactory; -import org.mapsforge.core.graphics.Paint; -import org.mapsforge.core.graphics.Style; -import org.mapsforge.core.model.BoundingBox; -import org.mapsforge.core.model.LatLong; -import org.mapsforge.core.model.MapPosition; -import org.mapsforge.core.model.Point; -import org.mapsforge.core.util.LatLongUtils; -import org.mapsforge.core.util.Parameters; -import org.mapsforge.map.awt.graphics.AwtGraphicFactory; -import org.mapsforge.map.awt.util.AwtUtil; -import org.mapsforge.map.awt.util.JavaPreferences; -import org.mapsforge.map.awt.view.MapView; -import org.mapsforge.map.datastore.MapDataStore; -import org.mapsforge.map.datastore.MultiMapDataStore; -import org.mapsforge.map.layer.Layers; -import org.mapsforge.map.layer.cache.TileCache; -import org.mapsforge.map.layer.debug.TileCoordinatesLayer; -import org.mapsforge.map.layer.debug.TileGridLayer; -import org.mapsforge.map.layer.download.TileDownloadLayer; -import org.mapsforge.map.layer.download.tilesource.OpenStreetMapMapnik; -import org.mapsforge.map.layer.download.tilesource.TileSource; -import org.mapsforge.map.layer.hills.DiffuseLightShadingAlgorithm; -import org.mapsforge.map.layer.hills.HillsRenderConfig; -import org.mapsforge.map.layer.hills.MemoryCachingHgtReaderTileSource; -import org.mapsforge.map.layer.overlay.Polyline; -import org.mapsforge.map.layer.renderer.TileRendererLayer; -import org.mapsforge.map.model.MapViewPosition; -import org.mapsforge.map.model.Model; -import org.mapsforge.map.model.common.PreferencesFacade; -import org.mapsforge.map.reader.MapFile; -import org.mapsforge.map.rendertheme.InternalRenderTheme; - -public final class Samples { - private static final GraphicFactory GRAPHIC_FACTORY = AwtGraphicFactory.INSTANCE; - private static final boolean SHOW_DEBUG_LAYERS = false; - private static final boolean SHOW_RASTER_MAP = false; - - private static final String MESSAGE = "Are you sure you want to exit the application?"; - private static final String TITLE = "Confirm close"; - - /** - * Starts the {@code Samples}. - * - * @param args - * command line args: expects the map files as multiple parameters - * with possible SRTM hgt folder as 1st argument. - * @throws Exception - */ - public static void main(String[] args) throws Exception { - - // Multithreaded map rendering - Parameters.NUMBER_OF_THREADS = 2; - - // Square frame buffer - Parameters.SQUARE_FRAME_BUFFER = false; - - HillsRenderConfig hillsCfg = null; - File demFolder = getDemFolder(args); - if (demFolder != null) { - MemoryCachingHgtReaderTileSource tileSource = new MemoryCachingHgtReaderTileSource( - demFolder, new DiffuseLightShadingAlgorithm(), AwtGraphicFactory.INSTANCE); - tileSource.setEnableInterpolationOverlap(true); - hillsCfg = new HillsRenderConfig(tileSource); - hillsCfg.indexOnThread(); - args = Arrays.copyOfRange(args, 1, args.length); - } - - List mapFiles = getMapFiles(args); - final MapView mapView = createMapView(); - final BoundingBox boundingBox = addLayers(mapView, mapFiles, hillsCfg); - - // addAPath(mapView); - - final PreferencesFacade preferencesFacade = new JavaPreferences( - Preferences.userNodeForPackage(Samples.class)); - - final JFrame frame = new JFrame(); - frame.setTitle("Mapsforge Samples"); - frame.add(mapView); - frame.pack(); - frame.setSize(new Dimension(800, 600)); - frame.setLocationRelativeTo(null); - frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); - frame.addWindowListener(new WindowAdapter() { - @Override - public void windowClosing(WindowEvent e) { - int result = JOptionPane.showConfirmDialog(frame, MESSAGE, TITLE, - JOptionPane.YES_NO_OPTION); - if (result == JOptionPane.YES_OPTION) { - mapView.getModel().save(preferencesFacade); - mapView.destroyAll(); - AwtGraphicFactory.clearResourceMemoryCache(); - frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); - } - } - - @Override - public void windowOpened(WindowEvent e) { - final Model model = mapView.getModel(); - model.init(preferencesFacade); - if (model.mapViewPosition.getZoomLevel() == 0 - || !boundingBox.contains(model.mapViewPosition.getCenter())) { - byte zoomLevel = LatLongUtils.zoomForBounds( - model.mapViewDimension.getDimension(), boundingBox, - model.displayModel.getTileSize()); - model.mapViewPosition.setMapPosition( - new MapPosition(boundingBox.getCenterPoint(), zoomLevel)); - } - } - }); - frame.setVisible(true); - } - - private static void addAPath(MapView mapView) throws Exception { - - Graph gr = (new BinaryGraphReader(Openfile.open("Maps/midip.map"))).read(); - Path path = (new BinaryPathReader(Openfile.open("Paths/chemin_0x400_119963_96676.path"))) - .readPath(gr); - - Paint paintStroke = AwtGraphicFactory.INSTANCE.createPaint(); - paintStroke.setColor(Color.GREEN); - paintStroke.setStrokeWidth(3); - paintStroke.setStyle(Style.STROKE); - - Polyline line = new Polyline(paintStroke, AwtGraphicFactory.INSTANCE); - - for (Arc arc : path.getArcs()) { - ArrayList points = arc.getPoints(); - for (int i = 0; i < points.size(); ++i) { - line.getLatLongs().add( - new LatLong(points.get(i).getLatitude(), points.get(i).getLongitude())); - } - } - - mapView.getLayerManager().getLayers().add(line); - } - - private static BoundingBox addLayers(MapView mapView, List mapFiles, - HillsRenderConfig hillsRenderConfig) { - Layers layers = mapView.getLayerManager().getLayers(); - - int tileSize = SHOW_RASTER_MAP ? 256 : 512; - - // Tile cache - TileCache tileCache = AwtUtil.createTileCache(tileSize, - mapView.getModel().frameBufferModel.getOverdrawFactor(), 1024, - new File(System.getProperty("java.io.tmpdir"), UUID.randomUUID().toString())); - - final BoundingBox boundingBox; - if (SHOW_RASTER_MAP) { - // Raster - mapView.getModel().displayModel.setFixedTileSize(tileSize); - TileSource tileSource = OpenStreetMapMapnik.INSTANCE; - TileDownloadLayer tileDownloadLayer = createTileDownloadLayer(tileCache, - mapView.getModel().mapViewPosition, tileSource); - layers.add(tileDownloadLayer); - tileDownloadLayer.start(); - mapView.setZoomLevelMin(tileSource.getZoomLevelMin()); - mapView.setZoomLevelMax(tileSource.getZoomLevelMax()); - boundingBox = new BoundingBox(LatLongUtils.LATITUDE_MIN, LatLongUtils.LONGITUDE_MIN, - LatLongUtils.LATITUDE_MAX, LatLongUtils.LONGITUDE_MAX); - } - else { - // Vector - mapView.getModel().displayModel.setFixedTileSize(tileSize); - MultiMapDataStore mapDataStore = new MultiMapDataStore( - MultiMapDataStore.DataPolicy.RETURN_ALL); - for (File file : mapFiles) { - mapDataStore.addMapDataStore(new MapFile(file), false, false); - } - TileRendererLayer tileRendererLayer = createTileRendererLayer(tileCache, mapDataStore, - mapView.getModel().mapViewPosition, hillsRenderConfig); - layers.add(tileRendererLayer); - boundingBox = mapDataStore.boundingBox(); - } - - // Debug - if (SHOW_DEBUG_LAYERS) { - layers.add(new TileGridLayer(GRAPHIC_FACTORY, mapView.getModel().displayModel)); - layers.add(new TileCoordinatesLayer(GRAPHIC_FACTORY, mapView.getModel().displayModel)); - } - - return boundingBox; - } - - private static MapView createMapView() { - MapView mapView = new MapView(); - mapView.getMapScaleBar().setVisible(true); - if (SHOW_DEBUG_LAYERS) { - mapView.getFpsCounter().setVisible(true); - } - - return mapView; - } - - @SuppressWarnings("unused") - private static TileDownloadLayer createTileDownloadLayer(TileCache tileCache, - MapViewPosition mapViewPosition, TileSource tileSource) { - return new TileDownloadLayer(tileCache, mapViewPosition, tileSource, GRAPHIC_FACTORY) { - @Override - public boolean onTap(LatLong tapLatLong, Point layerXY, Point tapXY) { - System.out.println("Tap on: " + tapLatLong); - return true; - } - }; - } - - private static TileRendererLayer createTileRendererLayer(TileCache tileCache, - MapDataStore mapDataStore, MapViewPosition mapViewPosition, - HillsRenderConfig hillsRenderConfig) { - TileRendererLayer tileRendererLayer = new TileRendererLayer(tileCache, mapDataStore, - mapViewPosition, false, true, false, GRAPHIC_FACTORY, hillsRenderConfig) { - @Override - public boolean onTap(LatLong tapLatLong, Point layerXY, Point tapXY) { - System.out.println("Tap on: " + tapLatLong); - return true; - } - }; - tileRendererLayer.setXmlRenderTheme(InternalRenderTheme.DEFAULT); - return tileRendererLayer; - } - - private static File getDemFolder(String[] args) { - if (args.length == 0) { - throw new IllegalArgumentException("missing argument: "); - } - - File demFolder = new File(args[0]); - if (demFolder.exists() && demFolder.isDirectory() && demFolder.canRead()) { - return demFolder; - } - return null; - } - - private static List getMapFiles(String[] args) { - if (args.length == 0) { - throw new IllegalArgumentException("missing argument: "); - } - - List result = new ArrayList<>(); - for (String arg : args) { - File mapFile = new File(arg); - if (!mapFile.exists()) { - throw new IllegalArgumentException("file does not exist: " + mapFile); - } - else if (!mapFile.isFile()) { - throw new IllegalArgumentException("not a file: " + mapFile); - } - else if (!mapFile.canRead()) { - throw new IllegalArgumentException("cannot read file: " + mapFile); - } - result.add(mapFile); - } - return result; - } - - private Samples() { - throw new IllegalStateException(); - } -} \ No newline at end of file diff --git a/src/main/org/insa/drawing/BasicDrawing.java b/src/main/org/insa/drawing/BasicDrawing.java index 6b67e84..7320436 100644 --- a/src/main/org/insa/drawing/BasicDrawing.java +++ b/src/main/org/insa/drawing/BasicDrawing.java @@ -5,12 +5,16 @@ import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; +import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; import java.awt.geom.NoninvertibleTransformException; import java.awt.geom.Point2D; -import java.awt.image.*; +import java.awt.image.BufferedImage; import java.util.ArrayList; +import java.util.IdentityHashMap; import java.util.Iterator; +import java.util.Map; import javax.swing.JPanel; @@ -21,294 +25,321 @@ import org.insa.graph.Path; import org.insa.graph.Point; /** - * Cette implementation de la classe Dessin produit vraiment un affichage - * (au contraire de la classe DessinInvisible). + * 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; - - // Default path color. - public static final Color DEFAULT_PATH_COLOR = new Color(255, 0, 255); + /** + * + */ + private static final long serialVersionUID = 96779785877771827L; - // Default palette. - public static final GraphPalette DEFAULT_PALETTE = new BasicGraphPalette(); - - // Default marker width - private static final int DEFAULT_MARKER_WIDTH = 10; - - // - private final Graphics2D gr; + // Default path color. + public static final Color DEFAULT_PATH_COLOR = new Color(255, 0, 255); - private double long1, long2, lat1, lat2; - - // Width and height of the image - private final int width, height; - - private Image image; - private ZoomAndPanListener zoomAndPanListener; - - /** - * Create a new BasicDrawing. - * - */ - public BasicDrawing() { - - this.zoomAndPanListener = new ZoomAndPanListener(this, ZoomAndPanListener.DEFAULT_MIN_ZOOM_LEVEL, 20, 1.2); - this.addMouseListener(zoomAndPanListener); - this.addMouseMotionListener(zoomAndPanListener); - this.addMouseWheelListener(zoomAndPanListener); - - this.width = 2000; - this.height = 1600; - - BufferedImage img = new BufferedImage(this.width, this.height, BufferedImage.TYPE_3BYTE_BGR); - - this.image = img; - this.gr = img.createGraphics(); - - this.zoomAndPanListener.setCoordTransform(this.gr.getTransform()); + // Default palette. + public static final GraphPalette DEFAULT_PALETTE = new BasicGraphPalette(); - this.long1 = -180; - this.long2 = 180; - this.lat1 = -90; - this.lat2 = 90; + // Default marker width + private static final int DEFAULT_MARKER_WIDTH = 10; - this.clear(); - this.repaint(); + // + private final Graphics2D gr; - } + private double long1, long2, lat1, lat2; - @Override - public void paintComponent(Graphics g1) { - Graphics2D g = (Graphics2D)g1; - g.clearRect(0, 0, getWidth(), getHeight()); - g.setTransform(zoomAndPanListener.getCoordTransform()); - g.drawImage(image, 0, 0, this); - } + // Width and height of the image + private final int width, height; - protected void setBB(double long1, double long2, double lat1, double lat2) { + // + private Image image; + private ZoomAndPanListener zoomAndPanListener; - if (long1 > long2 || lat1 > lat2) { - throw new Error("DessinVisible.setBB : mauvaises coordonnees."); - } - - this.long1 = long1; - this.long2 = long2; - this.lat1= lat1; - this.lat2 = lat2; - - double scale = 1 / Math.max(this.width / (double)this.getWidth(), this.height / (double)this.getHeight()); - - this.zoomAndPanListener.getCoordTransform().setToIdentity(); - 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.repaint(); - - } + // Mapping DrawingClickListener -> MouseEventListener + private Map listenerMapping = new IdentityHashMap<>(); - private int projx(double lon) { - return (int)(width * (lon - this.long1) / (this.long2 - this.long1)) ; - } + /** + * Create a new BasicDrawing. + * + */ + public BasicDrawing() { - private int projy(double lat) { - return (int)(height * (1 - (lat - this.lat1) / (this.lat2 - this.lat1))) ; - } - - /** - * Return the longitude and latitude corresponding to the given - * position of the MouseEvent. - * - * @param event - * - * @return - */ - public 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. - double lon = ptDst.getX(); - double lat = ptDst.getY(); - lon = (lon / this.width) * (this.long2 - this.long1) + this.long1; - lat = (1 - lat / this.height) * (this.lat2 - this.lat1) + this.lat1; - - // Return a new point. - return new Point(lon, lat); - } - - protected void setWidth(int width) { - this.gr.setStroke(new BasicStroke(width)); - } + this.zoomAndPanListener = new ZoomAndPanListener(this, ZoomAndPanListener.DEFAULT_MIN_ZOOM_LEVEL, 20, 1.2); + this.addMouseListener(zoomAndPanListener); + this.addMouseMotionListener(zoomAndPanListener); + this.addMouseWheelListener(zoomAndPanListener); - protected void setColor(Color col) { - this.gr.setColor(col); - } - - @Override - public void clear() { - this.gr.setColor(Color.WHITE); - this.gr.fillRect(0, 0, this.width, this.height); - } + this.width = 2000; + this.height = 1600; - @Override - public void drawLine(Point from, Point to) { - int x1 = this.projx(from.getLongitude()) ; - int x2 = this.projx(to.getLongitude()) ; - int y1 = this.projy(from.getLatitude()) ; - int y2 = this.projy(to.getLatitude()) ; + BufferedImage img = new BufferedImage(this.width, this.height, BufferedImage.TYPE_3BYTE_BGR); - gr.drawLine(x1, y1, x2, y2) ; - this.repaint(); - } + this.image = img; + this.gr = img.createGraphics(); - @Override - public void drawLine(Point from, Point to, int width) { - setWidth(width); - drawLine(from, to); - } + this.zoomAndPanListener.setCoordTransform(this.gr.getTransform()); - @Override - public void drawLine(Point from, Point to, int width, Color color) { - setWidth(width); - setColor(color); - drawLine(from, to); - } - - @Override - public void drawMarker(Point point) { - drawPoint(point, DEFAULT_MARKER_WIDTH, this.gr.getColor()); - } + this.long1 = -180; + this.long2 = 180; + this.lat1 = -90; + this.lat2 = 90; + this.clear(); + this.repaint(); - @Override - public void drawMarker(Point point, Color color) { - setColor(color); - drawMarker(point); - } - - @Override - public void drawPoint(Point point, int width, Color color) { - setWidth(width); - setColor(color); - int x = this.projx(point.getLongitude()) - DEFAULT_MARKER_WIDTH / 2; - int y = this.projy(point.getLatitude()) - DEFAULT_MARKER_WIDTH / 2; - gr.fillOval(x, y, DEFAULT_MARKER_WIDTH, DEFAULT_MARKER_WIDTH); - this.repaint(); - } - - /** - * 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. - */ - public void drawArc(Arc arc, GraphPalette palette) { - ArrayList pts = arc.getPoints(); - if (!pts.isEmpty()) { - if (palette != null) { - setColor(palette.getColorForType(arc.getInfo().getType())); - setWidth(palette.getWidthForType(arc.getInfo().getType())); - } - Iterator it1 = pts.iterator(); - Point prev = it1.next(); - while (it1.hasNext()) { - Point curr = it1.next(); - drawLine(prev, curr); - prev = curr; - } - } - } - - /** - * Initialize the drawing for the given graph. - * - * @param graph - */ - public void initialize(Graph graph) { - double minLon = Double.POSITIVE_INFINITY, minLat = Double.POSITIVE_INFINITY, - maxLon = Double.NEGATIVE_INFINITY, maxLat = Double.NEGATIVE_INFINITY; - for (Node node: graph.getNodes()) { - Point pt = node.getPoint(); - if (pt.getLatitude() < minLat) { - minLat = pt.getLatitude(); - } - if (pt.getLatitude() > maxLat) { - maxLat = pt.getLatitude(); - } - if (pt.getLongitude() < minLon) { - minLon = pt.getLongitude(); - } - if (pt.getLongitude() > maxLon) { - maxLon = pt.getLongitude(); - } - } - - double deltaLon = 0.02 * (maxLon - minLon), - deltaLat = 0.02 * (maxLat - minLat); + } - setBB(minLon - deltaLon, maxLon + deltaLon, - minLat - deltaLat, maxLat + deltaLat); - } + @Override + public void paintComponent(Graphics g1) { + Graphics2D g = (Graphics2D) g1; + g.clearRect(0, 0, getWidth(), getHeight()); + g.setTransform(zoomAndPanListener.getCoordTransform()); + g.drawImage(image, 0, 0, this); + } - @Override - public void drawGraph(Graph graph, GraphPalette palette) { - clear(); - initialize(graph); - for (Node node: graph.getNodes()) { - for (Arc arc: node.getSuccessors()) { - drawArc(arc, palette); - } - } - } + protected void setBB(double long1, double long2, double lat1, double lat2) { - @Override - public void drawGraph(Graph graph) { - drawGraph(graph, DEFAULT_PALETTE); - } + if (long1 > long2 || lat1 > lat2) { + throw new Error("DessinVisible.setBB : mauvaises coordonnees."); + } - @Override - public void drawPath(Path path, Color color, boolean markers) { - setColor(color); - setWidth(2); - for (Arc arc: path.getArcs()) { - drawArc(arc, null); - } - if (markers) { - drawMarker(path.getOrigin().getPoint(), color); - drawMarker(path.getDestination().getPoint(), color); - } - } - - @Override - public void drawPath(Path path, Color color) { - drawPath(path, color, true); - } + this.long1 = long1; + this.long2 = long2; + this.lat1 = lat1; + this.lat2 = lat2; - @Override - public void drawPath(Path path) { - drawPath(path, DEFAULT_PATH_COLOR); - } - - @Override - public void drawPath(Path path, boolean markers) { - drawPath(path, DEFAULT_PATH_COLOR, markers); - } - - @SuppressWarnings("unused") - private void putText(Point point, String txt) { - int x = this.projx(point.getLongitude()); - int y = this.projy(point.getLatitude()); - gr.drawString(txt, x, y); - this.repaint(); - } + double scale = 1 / Math.max(this.width / (double) this.getWidth(), this.height / (double) this.getHeight()); + + this.zoomAndPanListener.getCoordTransform().setToIdentity(); + 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.repaint(); + + } + + private int projx(double lon) { + return (int) (width * (lon - this.long1) / (this.long2 - this.long1)); + } + + private int projy(double lat) { + return (int) (height * (1 - (lat - this.lat1) / (this.lat2 - this.lat1))); + } + + /** + * Return the longitude and latitude corresponding to the given position of the + * MouseEvent. + * + * @param event + * + * @return + */ + public 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. + double lon = ptDst.getX(); + double lat = ptDst.getY(); + lon = (lon / this.width) * (this.long2 - this.long1) + this.long1; + lat = (1 - lat / this.height) * (this.lat2 - this.lat1) + this.lat1; + + // Return a new point. + return new Point(lon, lat); + } + + @Override + public void addDrawingClickListener(DrawingClickListener listener) { + MouseListener mListener = new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent evt) { + System.out.println(evt); + try { + listener.mouseClicked(getLongitudeLatitude(evt)); + } + catch (NoninvertibleTransformException e) { + e.printStackTrace(); + } + } + }; + this.addMouseListener(mListener); + this.listenerMapping.put(listener, mListener); + } + + @Override + public void removeDrawingClickListener(DrawingClickListener listener) { + this.removeMouseListener(this.listenerMapping.get(listener)); + } + + protected void setWidth(int width) { + this.gr.setStroke(new BasicStroke(width)); + } + + protected void setColor(Color col) { + this.gr.setColor(col); + } + + @Override + public void clear() { + this.gr.setColor(Color.WHITE); + this.gr.fillRect(0, 0, this.width, this.height); + } + + @Override + public void drawLine(Point from, Point to) { + int x1 = this.projx(from.getLongitude()); + int x2 = this.projx(to.getLongitude()); + int y1 = this.projy(from.getLatitude()); + int y2 = this.projy(to.getLatitude()); + + gr.drawLine(x1, y1, x2, y2); + this.repaint(); + } + + @Override + public void drawLine(Point from, Point to, int width) { + setWidth(width); + drawLine(from, to); + } + + @Override + public void drawLine(Point from, Point to, int width, Color color) { + setWidth(width); + setColor(color); + drawLine(from, to); + } + + @Override + public void drawMarker(Point point) { + drawPoint(point, DEFAULT_MARKER_WIDTH, this.gr.getColor()); + } + + @Override + public void drawMarker(Point point, Color color) { + setColor(color); + drawMarker(point); + } + + @Override + public void drawPoint(Point point, int width, Color color) { + setWidth(width); + setColor(color); + int x = this.projx(point.getLongitude()) - DEFAULT_MARKER_WIDTH / 2; + int y = this.projy(point.getLatitude()) - DEFAULT_MARKER_WIDTH / 2; + gr.fillOval(x, y, DEFAULT_MARKER_WIDTH, DEFAULT_MARKER_WIDTH); + this.repaint(); + } + + /** + * 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. + */ + public void drawArc(Arc arc, GraphPalette palette) { + ArrayList pts = arc.getPoints(); + if (!pts.isEmpty()) { + if (palette != null) { + setColor(palette.getColorForType(arc.getInfo().getType())); + setWidth(palette.getWidthForType(arc.getInfo().getType())); + } + Iterator it1 = pts.iterator(); + Point prev = it1.next(); + while (it1.hasNext()) { + Point curr = it1.next(); + drawLine(prev, curr); + prev = curr; + } + } + } + + /** + * Initialize the drawing for the given graph. + * + * @param graph + */ + public void initialize(Graph graph) { + double minLon = Double.POSITIVE_INFINITY, minLat = Double.POSITIVE_INFINITY, maxLon = Double.NEGATIVE_INFINITY, + maxLat = Double.NEGATIVE_INFINITY; + for (Node node: graph.getNodes()) { + Point pt = node.getPoint(); + if (pt.getLatitude() < minLat) { + minLat = pt.getLatitude(); + } + if (pt.getLatitude() > maxLat) { + maxLat = pt.getLatitude(); + } + if (pt.getLongitude() < minLon) { + minLon = pt.getLongitude(); + } + if (pt.getLongitude() > maxLon) { + maxLon = pt.getLongitude(); + } + } + + double deltaLon = 0.02 * (maxLon - minLon), deltaLat = 0.02 * (maxLat - minLat); + + setBB(minLon - deltaLon, maxLon + deltaLon, minLat - deltaLat, maxLat + deltaLat); + } + + @Override + public void drawGraph(Graph graph, GraphPalette palette) { + clear(); + initialize(graph); + for (Node node: graph.getNodes()) { + for (Arc arc: node.getSuccessors()) { + drawArc(arc, palette); + } + } + } + + @Override + public void drawGraph(Graph graph) { + drawGraph(graph, DEFAULT_PALETTE); + } + + @Override + public void drawPath(Path path, Color color, boolean markers) { + setColor(color); + setWidth(2); + for (Arc arc: path.getArcs()) { + drawArc(arc, null); + } + if (markers) { + drawMarker(path.getOrigin().getPoint(), color); + drawMarker(path.getDestination().getPoint(), color); + } + } + + @Override + public void drawPath(Path path, Color color) { + drawPath(path, color, true); + } + + @Override + public void drawPath(Path path) { + drawPath(path, DEFAULT_PATH_COLOR); + } + + @Override + public void drawPath(Path path, boolean markers) { + drawPath(path, DEFAULT_PATH_COLOR, markers); + } + + @SuppressWarnings("unused") + private void putText(Point point, String txt) { + int x = this.projx(point.getLongitude()); + int y = this.projy(point.getLatitude()); + gr.drawString(txt, x, y); + this.repaint(); + } } diff --git a/src/main/org/insa/drawing/Drawing.java b/src/main/org/insa/drawing/Drawing.java index 0ab4927..79e222f 100644 --- a/src/main/org/insa/drawing/Drawing.java +++ b/src/main/org/insa/drawing/Drawing.java @@ -7,113 +7,128 @@ import org.insa.graph.Path; import org.insa.graph.Point; public interface Drawing { - - /** - * Clear the drawing. - */ - public void clear(); - /** - * Draw a line between the two given points with the default color - * and width. - * - * @param from - * @param to - */ - public void drawLine(Point from, Point to); - - /** - * Draw a line between the two given points with the default color - * and the given width. - * - * @param from - * @param to - * @param width - */ - public void drawLine(Point from, Point to, int width); - - /** - * Draw a line between the two given points with the given color - * and the given width. - * - * @param from - * @param to - * @param width - * @param color - */ - public void drawLine(Point from, Point to, int width, Color color); - - /** - * Draw a marker at the given point with the default color. - * - * @param point - */ - public void drawMarker(Point point); - - /** - * Draw the given point with the given color. - * - * @param point - */ - public void drawMarker(Point point, Color color); - - /** - * Draw a point width the given width and color. Do not use this to mark location, - * use drawMarker. - * - * @param point - * @param width - * @param color - */ - public void drawPoint(Point point, int width, Color color); + /** + * Add a listener to click to this drawing. + * + * @param listener + */ + public void addDrawingClickListener(DrawingClickListener listener); + + /** + * Remove the given listener from the drawing. + * + * @param listener + */ + public void removeDrawingClickListener(DrawingClickListener listener); + + /** + * Clear the drawing. + */ + public void clear(); + + /** + * Draw a line between the two given points with the default color and width. + * + * @param from + * @param to + */ + public void drawLine(Point from, Point to); + + /** + * Draw a line between the two given points with the default color and the given + * width. + * + * @param from + * @param to + * @param width + */ + public void drawLine(Point from, Point to, int width); + + /** + * Draw a line between the two given points with the given color and the given + * width. + * + * @param from + * @param to + * @param width + * @param color + */ + public void drawLine(Point from, Point to, int width, Color color); + + /** + * Draw a marker at the given point with the default color. + * + * @param point + */ + public void drawMarker(Point point); + + /** + * Draw the given point with the given color. + * + * @param point + */ + public void drawMarker(Point point, Color color); + + /** + * Draw a point width the given width and color. Do not use this to mark + * location, use drawMarker. + * + * @param point + * @param width + * @param color + */ + public void drawPoint(Point point, int width, Color color); + + /** + * Draw the given graph using the given palette. + * + * @param graph + * @param palette + */ + public void drawGraph(Graph graph, GraphPalette palette); + + /** + * Draw the given graph using a default palette specific to the implementation. + * + * @param graph + */ + public void drawGraph(Graph graph); + + /** + * Draw a path using the given color. + * + * @param path + * @param color + * @param markers + * Show origin and destination markers. + */ + public void drawPath(Path path, Color color, boolean markers); + + /** + * Draw a path using the given color with markers. + * + * @param path + * @param color + */ + public void drawPath(Path path, Color color); + + /** + * Draw a path using a default color specific to the implementation + * + * + * @param path + * @param markers + * Show origin and destination markers. + */ + public void drawPath(Path path, boolean markers); + + /** + * Draw a path using a default color specific to the implementation + * + * + * @param path + */ + public void drawPath(Path path); - /** - * Draw the given graph using the given palette. - * - * @param graph - * @param palette - */ - public void drawGraph(Graph graph, GraphPalette palette); - - /** - * Draw the given graph using a default palette specific to the implementation. - * - * @param graph - */ - public void drawGraph(Graph graph); - - /** - * Draw a path using the given color. - * - * @param path - * @param color - * @param markers Show origin and destination markers. - */ - public void drawPath(Path path, Color color, boolean markers); - - /** - * Draw a path using the given color with markers. - * - * @param path - * @param color - */ - public void drawPath(Path path, Color color); - - /** - * Draw a path using a default color specific to the implementation - * - * - * @param path - * @param markers Show origin and destination markers. - */ - public void drawPath(Path path, boolean markers); - - /** - * Draw a path using a default color specific to the implementation - * - * - * @param path - */ - public void drawPath(Path path); - } diff --git a/src/main/org/insa/drawing/DrawingClickListener.java b/src/main/org/insa/drawing/DrawingClickListener.java new file mode 100644 index 0000000..35ce18d --- /dev/null +++ b/src/main/org/insa/drawing/DrawingClickListener.java @@ -0,0 +1,14 @@ +package org.insa.drawing; + +import org.insa.graph.Point; + +public interface DrawingClickListener { + + /** + * Event triggered when a click is made on the map. + * + * @param point + */ + public void mouseClicked(Point point); + +} diff --git a/src/main/org/insa/drawing/MapViewDrawing.java b/src/main/org/insa/drawing/MapViewDrawing.java index 5c08be5..a217a44 100644 --- a/src/main/org/insa/drawing/MapViewDrawing.java +++ b/src/main/org/insa/drawing/MapViewDrawing.java @@ -1,272 +1,324 @@ -package org.insa.drawing; - -import java.awt.Color; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseWheelEvent; -import java.io.File; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; - -import javax.swing.JFileChooser; -import javax.swing.filechooser.FileNameExtensionFilter; - -import org.insa.graph.Arc; -import org.insa.graph.Graph; -import org.insa.graph.Path; -import org.insa.graph.Point; -import org.mapsforge.core.graphics.GraphicFactory; -import org.mapsforge.core.graphics.Paint; -import org.mapsforge.core.graphics.Style; -import org.mapsforge.core.model.BoundingBox; -import org.mapsforge.core.model.LatLong; -import org.mapsforge.core.model.MapPosition; -import org.mapsforge.core.util.LatLongUtils; -import org.mapsforge.map.awt.graphics.AwtGraphicFactory; -import org.mapsforge.map.awt.util.AwtUtil; -import org.mapsforge.map.awt.view.MapView; -import org.mapsforge.map.datastore.MapDataStore; -import org.mapsforge.map.layer.Layers; -import org.mapsforge.map.layer.cache.TileCache; -import org.mapsforge.map.layer.hills.HillsRenderConfig; -import org.mapsforge.map.layer.overlay.Marker; -import org.mapsforge.map.layer.overlay.Polyline; -import org.mapsforge.map.layer.renderer.TileRendererLayer; -import org.mapsforge.map.model.DisplayModel; -import org.mapsforge.map.model.MapViewPosition; -import org.mapsforge.map.model.Model; -import org.mapsforge.map.reader.MapFile; -import org.mapsforge.map.rendertheme.InternalRenderTheme; -import org.mapsforge.map.rendertheme.XmlRenderTheme; - -public class MapViewDrawing extends MapView implements Drawing { - - /** - * - */ - private static final long serialVersionUID = 8606967833704938092L; - - // Default path color. - public static final Color DEFAULT_PATH_COLOR = new Color(66, 134, 244); - - // Graphic factory. - private static final GraphicFactory GRAPHIC_FACTORY = AwtGraphicFactory.INSTANCE; - - // Default tile size. - private static final int DEFAULT_TILE_SIZE = 512; - - // Tile size. - int tileSize; - - ArrayList extraLayers; - - public MapViewDrawing() { - getMapScaleBar().setVisible(true); - this.tileSize = DEFAULT_TILE_SIZE; - DisplayModel model = getModel().displayModel; - model.setFixedTileSize(tileSize); - // model.setBackgroundColor(convertColor(Color.WHITE)); - - extraLayers = new ArrayList(); - addMouseWheelListener(new MouseAdapter() { - @Override - public void mouseWheelMoved(MouseWheelEvent e) { - byte zoomLevelDiff = (byte) -e.getWheelRotation(); - for (Paint p: extraLayers) { - p.setStrokeWidth(p.getStrokeWidth() + zoomLevelDiff); - } - } - }); - } - - protected int convertColor(Color color) { - return GRAPHIC_FACTORY.createColor(color.getAlpha(), color.getRed(), color.getGreen(), - color.getBlue()); - } - - private int getStrokeWidth(int width) { - byte zoomLevel = getModel().mapViewPosition.getZoomLevel(); - return width * (2 - (8 - zoomLevel)); - } - - private Paint createPaintStroke(int width, Color color) { - Paint paintStroke = AwtGraphicFactory.INSTANCE.createPaint(); - paintStroke.setStyle(Style.STROKE); - if (width != 0) { - paintStroke.setStrokeWidth(getStrokeWidth(width)); - } - if (color != null) { - paintStroke.setColor(convertColor(color)); - } - return paintStroke; - } - - /** - * - * @param color - * @return - */ - private File getMapsforgeFileFromGraph(Graph graph) { - // TODO: Find a way to change this... - Map idToNames = new HashMap(); - idToNames.put(0x100, "insa"); - idToNames.put(0x110, "paris"); - idToNames.put(0x200, "mayotte"); - idToNames.put(0x250, "newzealand"); - idToNames.put(0x300, "reunion"); - idToNames.put(0x400, "midip"); - idToNames.put(0x410, "morbihan"); - - File file = null; - if (idToNames.containsKey(graph.getMapId())) { - file = new File("Maps/" + idToNames.get(graph.getMapId()) + ".mapfg"); - if (!file.exists()) { - file = new File("Maps/new/" + idToNames.get(graph.getMapId()) + ".mapfg"); - } - } - - if (file == null || !file.exists()) { - JFileChooser fileChooser = new JFileChooser("Maps/"); - fileChooser.setFileFilter(new FileNameExtensionFilter("mapsforge files", "" + "mapfg")); - if (fileChooser.showOpenDialog(this.getParent()) == JFileChooser.APPROVE_OPTION) { - file = fileChooser.getSelectedFile(); - } - } - - return file; - } - - protected LatLong convertPoint(Point point) { - return new LatLong(point.getLatitude(), point.getLongitude()); - } - - private static TileRendererLayer createTileRendererLayer(TileCache tileCache, - MapDataStore mapDataStore, MapViewPosition mapViewPosition, - HillsRenderConfig hillsRenderConfig) { - TileRendererLayer tileRendererLayer = new TileRendererLayer(tileCache, mapDataStore, - mapViewPosition, false, true, false, GRAPHIC_FACTORY, hillsRenderConfig) { - @Override - public boolean onTap(LatLong tapLatLong, org.mapsforge.core.model.Point layerXY, - org.mapsforge.core.model.Point tapXY) { - System.out.println("Tap on: " + tapLatLong); - return true; - } - }; - XmlRenderTheme renderTheme = InternalRenderTheme.DEFAULT; - tileRendererLayer.setXmlRenderTheme(renderTheme); - return tileRendererLayer; - } - - @Override - public void clear() { - getLayerManager().getLayers().clear(); - extraLayers.clear(); - repaint(); - } - - @Override - public void drawLine(Point from, Point to) { - drawLine(from, to, 0, null); - } - - @Override - public void drawLine(Point from, Point to, int width) { - drawLine(from, to, width, null); - } - - @Override - public void drawLine(Point from, Point to, int width, Color color) { - Paint paintStroke = createPaintStroke(width, color); - Polyline line = new Polyline(paintStroke, AwtGraphicFactory.INSTANCE); - line.getLatLongs().add(convertPoint(from)); - line.getLatLongs().add(convertPoint(to)); - getLayerManager().getLayers().add(line); - } - - @Override - public void drawMarker(Point point) { - drawMarker(point, null); - } - - @Override - public void drawMarker(Point point, Color color) { - Marker marker = new Marker(convertPoint(point), GRAPHIC_FACTORY.createBitmap(10, 20), 1, 2); - getLayerManager().getLayers().add(marker); - } - - @Override - public void drawPoint(Point point, int width, Color color) { - // TODO: Maybe do something? - } - - @Override - public void drawGraph(Graph graph, GraphPalette palette) { - - File graphFile = getMapsforgeFileFromGraph(graph); - - // Tile cache - TileCache tileCache = AwtUtil.createTileCache(tileSize, - getModel().frameBufferModel.getOverdrawFactor(), 1024, - new File(System.getProperty("java.io.tmpdir"), UUID.randomUUID().toString())); - - // Layers - Layers layers = getLayerManager().getLayers(); - - MapDataStore mapDataStore = new MapFile(graphFile); - TileRendererLayer tileRendererLayer = createTileRendererLayer(tileCache, mapDataStore, - getModel().mapViewPosition, null); - layers.add(tileRendererLayer); - BoundingBox boundingBox = mapDataStore.boundingBox(); - - final Model model = getModel(); - if (model.mapViewPosition.getZoomLevel() == 0 - || !boundingBox.contains(model.mapViewPosition.getCenter())) { - byte zoomLevel = LatLongUtils.zoomForBounds(model.mapViewDimension.getDimension(), - boundingBox, model.displayModel.getTileSize()); - model.mapViewPosition - .setMapPosition(new MapPosition(boundingBox.getCenterPoint(), zoomLevel)); - model.mapViewPosition.setZoomLevelMin(zoomLevel); - } - } - - @Override - public void drawGraph(Graph graph) { - drawGraph(graph, null); - } - - @Override - public void drawPath(Path path, Color color, boolean markers) { - Paint paintStroke = createPaintStroke(1, DEFAULT_PATH_COLOR); - Polyline line = new Polyline(paintStroke, AwtGraphicFactory.INSTANCE); - for (Arc arc: path.getArcs()) { - ArrayList points = arc.getPoints(); - for (int i = 0; i < points.size(); ++i) { - line.getLatLongs().add( - new LatLong(points.get(i).getLatitude(), points.get(i).getLongitude())); - } - } - getLayerManager().getLayers().add(line); - extraLayers.add(paintStroke); - if (markers) { - drawMarker(path.getOrigin().getPoint()); - drawMarker(path.getDestination().getPoint()); - } - } - - @Override - public void drawPath(Path path, Color color) { - drawPath(path, color, true); - } - - @Override - public void drawPath(Path path) { - drawPath(path, DEFAULT_PATH_COLOR, true); - } - - @Override - public void drawPath(Path path, boolean markers) { - drawPath(path, DEFAULT_PATH_COLOR, markers); - } - -} +package org.insa.drawing; + +import java.awt.Color; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseWheelEvent; +import java.io.File; +import java.io.FileNotFoundException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +import javax.swing.JFileChooser; +import javax.swing.filechooser.FileNameExtensionFilter; + +import org.insa.drawing.utils.MarkerUtils; +import org.insa.graph.Arc; +import org.insa.graph.Graph; +import org.insa.graph.Path; +import org.insa.graph.Point; +import org.mapsforge.core.graphics.Bitmap; +import org.mapsforge.core.graphics.GraphicFactory; +import org.mapsforge.core.graphics.Paint; +import org.mapsforge.core.graphics.Style; +import org.mapsforge.core.model.BoundingBox; +import org.mapsforge.core.model.LatLong; +import org.mapsforge.core.model.MapPosition; +import org.mapsforge.core.util.LatLongUtils; +import org.mapsforge.map.awt.graphics.AwtGraphicFactory; +import org.mapsforge.map.awt.util.AwtUtil; +import org.mapsforge.map.awt.view.MapView; +import org.mapsforge.map.datastore.MapDataStore; +import org.mapsforge.map.layer.Layers; +import org.mapsforge.map.layer.cache.TileCache; +import org.mapsforge.map.layer.hills.HillsRenderConfig; +import org.mapsforge.map.layer.overlay.Marker; +import org.mapsforge.map.layer.overlay.Polyline; +import org.mapsforge.map.layer.renderer.TileRendererLayer; +import org.mapsforge.map.model.DisplayModel; +import org.mapsforge.map.model.MapViewPosition; +import org.mapsforge.map.model.Model; +import org.mapsforge.map.reader.MapFile; +import org.mapsforge.map.rendertheme.ExternalRenderTheme; +import org.mapsforge.map.rendertheme.XmlRenderTheme; + +public class MapViewDrawing extends MapView implements Drawing { + + /** + * + */ + private static final long serialVersionUID = 8606967833704938092L; + + // Default path color. + public static final Color DEFAULT_PATH_COLOR = new Color(66, 134, 244); + + // Graphic factory. + private static final GraphicFactory GRAPHIC_FACTORY = AwtGraphicFactory.INSTANCE; + + // Default tile size. + private static final int DEFAULT_TILE_SIZE = 512; + + // List of listeners. + private ArrayList drawingClickListeners = new ArrayList<>(); + + // Tile size. + int tileSize; + + // Extra layers... + private static class FixedStrokeWidthLayer { + public Paint paint; + public int width; + + public FixedStrokeWidthLayer(Paint paint, int width) { + this.paint = paint; + this.width = width; + } + + }; + + ArrayList extraLayers = new ArrayList<>(); + + public MapViewDrawing() { + getMapScaleBar().setVisible(true); + this.tileSize = DEFAULT_TILE_SIZE; + DisplayModel model = getModel().displayModel; + model.setFixedTileSize(tileSize); + + addMouseWheelListener(new MouseAdapter() { + + @Override + public void mouseWheelMoved(MouseWheelEvent e) { + for (FixedStrokeWidthLayer f: extraLayers) { + f.paint.setStrokeWidth(getStrokeWidth(f.width)); + } + } + }); + + } + + /** + * @param color + * @return + */ + protected int convertColor(Color color) { + return GRAPHIC_FACTORY.createColor(color.getAlpha(), color.getRed(), color.getGreen(), color.getBlue()); + } + + /** + * @param width + * @return + */ + private int getStrokeWidth(int width) { + byte zoomLevel = getModel().mapViewPosition.getZoomLevel(); + int mul = 2; + if (zoomLevel < 8) { + mul = 1; + } + else { + mul += 2 * (zoomLevel - 8) / 3; + } + return width * mul; + } + + /** + * @param width + * @param color + * @return + */ + private Paint createPaintStroke(int width, Color color) { + Paint paintStroke = AwtGraphicFactory.INSTANCE.createPaint(); + paintStroke.setStyle(Style.STROKE); + if (width != 0) { + paintStroke.setStrokeWidth(getStrokeWidth(width)); + } + if (color != null) { + paintStroke.setColor(convertColor(color)); + } + return paintStroke; + } + + /** + * + * @param color + * @return + */ + private File getMapsforgeFileFromGraph(Graph graph) { + // TODO: Find a way to change this... + Map idToNames = new HashMap(); + idToNames.put(0x100, "insa"); + idToNames.put(0x110, "paris"); + idToNames.put(0x200, "mayotte"); + idToNames.put(0x250, "newzealand"); + idToNames.put(0x300, "reunion"); + idToNames.put(0x400, "midip"); + idToNames.put(0x410, "morbihan"); + + File file = null; + if (idToNames.containsKey(graph.getMapId())) { + file = new File("Maps/" + idToNames.get(graph.getMapId()) + ".mapfg"); + if (!file.exists()) { + file = new File("Maps/new/" + idToNames.get(graph.getMapId()) + ".mapfg"); + } + } + + if (file == null || !file.exists()) { + JFileChooser fileChooser = new JFileChooser("Maps/"); + fileChooser.setFileFilter(new FileNameExtensionFilter("mapsforge files", "" + "mapfg")); + if (fileChooser.showOpenDialog(this.getParent()) == JFileChooser.APPROVE_OPTION) { + file = fileChooser.getSelectedFile(); + } + } + + return file; + } + + protected LatLong convertPoint(Point point) { + return new LatLong(point.getLatitude(), point.getLongitude()); + } + + private TileRendererLayer createTileRendererLayer(TileCache tileCache, MapDataStore mapDataStore, + MapViewPosition mapViewPosition, HillsRenderConfig hillsRenderConfig) { + TileRendererLayer tileRendererLayer = new TileRendererLayer(tileCache, mapDataStore, mapViewPosition, false, + true, false, GRAPHIC_FACTORY, hillsRenderConfig) { + @Override + public boolean onTap(LatLong tapLatLong, org.mapsforge.core.model.Point layerXY, + org.mapsforge.core.model.Point tapXY) { + System.out.println("Tap on: " + tapLatLong); + Point pt = new Point(tapLatLong.getLongitude(), tapLatLong.getLatitude()); + for (DrawingClickListener listener: MapViewDrawing.this.drawingClickListeners) { + listener.mouseClicked(pt); + } + return true; + } + }; + XmlRenderTheme renderTheme = null; + try { + renderTheme = new ExternalRenderTheme("resources/assets/custom-theme.xml"); + } + catch (FileNotFoundException e) { + e.printStackTrace(); + } + tileRendererLayer.setXmlRenderTheme(renderTheme); + return tileRendererLayer; + } + + @Override + public void addDrawingClickListener(DrawingClickListener listener) { + this.drawingClickListeners.add(listener); + } + + @Override + public void removeDrawingClickListener(DrawingClickListener listener) { + this.drawingClickListeners.remove(listener); + } + + @Override + public void clear() { + getLayerManager().getLayers().clear(); + extraLayers.clear(); + repaint(); + } + + @Override + public void drawLine(Point from, Point to) { + drawLine(from, to, 0, null); + } + + @Override + public void drawLine(Point from, Point to, int width) { + drawLine(from, to, width, null); + } + + @Override + public void drawLine(Point from, Point to, int width, Color color) { + Paint paintStroke = createPaintStroke(width, color); + Polyline line = new Polyline(paintStroke, AwtGraphicFactory.INSTANCE); + line.getLatLongs().add(convertPoint(from)); + line.getLatLongs().add(convertPoint(to)); + getLayerManager().getLayers().add(line); + } + + @Override + public void drawMarker(Point point) { + drawMarker(point, Color.GREEN); + } + + @Override + public void drawMarker(Point point, Color color) { + Bitmap bitmap = MarkerUtils.getMarkerForColor(color); + Marker marker = new Marker(convertPoint(point), bitmap, 0, -bitmap.getHeight() / 2); + getLayerManager().getLayers().add(marker); + } + + @Override + public void drawPoint(Point point, int width, Color color) { + // TODO: + } + + @Override + public void drawGraph(Graph graph, GraphPalette palette) { + + File graphFile = getMapsforgeFileFromGraph(graph); + + // Tile cache + TileCache tileCache = AwtUtil.createTileCache(tileSize, getModel().frameBufferModel.getOverdrawFactor(), 1024, + new File(System.getProperty("java.io.tmpdir"), UUID.randomUUID().toString())); + + // Layers + Layers layers = getLayerManager().getLayers(); + + MapDataStore mapDataStore = new MapFile(graphFile); + TileRendererLayer tileRendererLayer = createTileRendererLayer(tileCache, mapDataStore, + getModel().mapViewPosition, null); + layers.add(tileRendererLayer); + BoundingBox boundingBox = mapDataStore.boundingBox(); + + final Model model = getModel(); + if (model.mapViewPosition.getZoomLevel() == 0 || !boundingBox.contains(model.mapViewPosition.getCenter())) { + byte zoomLevel = LatLongUtils.zoomForBounds(model.mapViewDimension.getDimension(), boundingBox, + model.displayModel.getTileSize()); + model.mapViewPosition.setMapPosition(new MapPosition(boundingBox.getCenterPoint(), zoomLevel)); + model.mapViewPosition.setZoomLevelMin(zoomLevel); + } + } + + @Override + public void drawGraph(Graph graph) { + drawGraph(graph, null); + } + + @Override + public void drawPath(Path path, Color color, boolean markers) { + Paint paintStroke = createPaintStroke(1, DEFAULT_PATH_COLOR); + Polyline line = new Polyline(paintStroke, AwtGraphicFactory.INSTANCE); + for (Arc arc: path.getArcs()) { + ArrayList points = arc.getPoints(); + for (int i = 0; i < points.size(); ++i) { + line.getLatLongs().add(new LatLong(points.get(i).getLatitude(), points.get(i).getLongitude())); + } + } + getLayerManager().getLayers().add(line); + extraLayers.add(new FixedStrokeWidthLayer(paintStroke, 1)); + if (markers) { + drawMarker(path.getOrigin().getPoint(), DEFAULT_PATH_COLOR); + drawMarker(path.getDestination().getPoint(), DEFAULT_PATH_COLOR); + } + } + + @Override + public void drawPath(Path path, Color color) { + drawPath(path, color, true); + } + + @Override + public void drawPath(Path path) { + drawPath(path, DEFAULT_PATH_COLOR, true); + } + + @Override + public void drawPath(Path path, boolean markers) { + drawPath(path, DEFAULT_PATH_COLOR, markers); + } + +} diff --git a/src/main/org/insa/drawing/utils/MarkerUtils.java b/src/main/org/insa/drawing/utils/MarkerUtils.java new file mode 100644 index 0000000..74f8fb2 --- /dev/null +++ b/src/main/org/insa/drawing/utils/MarkerUtils.java @@ -0,0 +1,118 @@ +package org.insa.drawing.utils; + +import java.awt.Color; +import java.awt.image.BufferedImage; +import java.util.HashMap; +import java.util.Map; + +import org.mapsforge.core.graphics.Bitmap; +import org.mapsforge.map.awt.graphics.AwtBitmap; + +public class MarkerUtils { + + // Marker + private static Map MARKER_BITMAPS = new HashMap(); + + /** + * Return a color mapping with the given color as the main color. + * + * @param color + * @return + */ + protected static Color[] getColorMapping(Color color) { + return new Color[] { new Color(0, 0, 0, 0), color, Color.BLACK }; + } + + /** + * Create a Bitmap representing a marker of the given color. + * + * @param color + * @return + */ + public static Bitmap getMarkerForColor(Color color) { + if (MARKER_BITMAPS.containsKey(color)) { + return MARKER_BITMAPS.get(color); + } + + // create image + BufferedImage image = new BufferedImage(MARKER_MASK[0].length, MARKER_MASK.length, BufferedImage.TYPE_INT_ARGB); + + Color[] map = getColorMapping(color); + for (int i = 0; i < image.getHeight(); ++i) { + for (int j = 0; j < image.getWidth(); ++j) { + image.setRGB(j, i, map[MARKER_MASK[i][j]].getRGB()); + } + } + + // Create Bitmap, add it to map and return it. + Bitmap bitmap = new AwtBitmap(image); + MARKER_BITMAPS.put(color, bitmap); + return bitmap; + } + + // Mask + private static byte[][] MARKER_MASK = new byte[][] { + // @formatter:off + {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0}, + {0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0}, + {0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0}, + {0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0}, + {0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0}, + {0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0}, + {0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0}, + {0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0}, + {0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0}, + {0,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,0}, + {1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1}, + {1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1}, + {1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1}, + {1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1}, + {1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1}, + {1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1}, + {1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1}, + {0,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,0}, + {0,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,0}, + {0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0}, + {0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0}, + {0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0}, + {0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0}, + {0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0}, + {0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0}, + {0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0}, + {0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0}, + {0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} + }; +}