*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
+ * the Free Software Foundation; either version 2 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
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_12;
import java.awt.*;
import java.awt.event.*;
+import java.awt.image.*;
import javax.swing.*;
import java.io.*;
import java.lang.Math;
import java.awt.geom.*;
import java.util.*;
import java.util.concurrent.*;
-import org.altusmetrum.altoslib_6.*;
+import javax.imageio.*;
+import org.altusmetrum.altoslib_12.*;
-public class AltosUIMap extends JComponent implements AltosFlightDisplay, AltosUIMapZoomListener {
+public class AltosUIMap extends JComponent implements AltosFlightDisplay, AltosMapInterface {
- static final int px_size = 512;
+ AltosMap map;
+ Graphics2D g;
+ Font tile_font;
+ Font line_font;
- static final int maptype_hybrid = 0;
- static final int maptype_roadmap = 1;
- static final int maptype_satellite = 2;
- static final int maptype_terrain = 3;
- static final int maptype_default = maptype_hybrid;
+ static Point2D.Double point2d(AltosPointDouble pt) {
+ return new Point2D.Double(pt.x, pt.y);
+ }
- static final String[] maptype_names = {
- "hybrid",
- "roadmap",
- "satellite",
- "terrain"
- };
+ static final AltosPointDouble point_double(Point pt) {
+ return new AltosPointDouble(pt.x, pt.y);
+ }
- public static final String[] maptype_labels = {
- "Hybrid",
- "Roadmap",
- "Satellite",
- "Terrain"
- };
+ class MapMark extends AltosMapMark {
+ public void paint(AltosMapTransform t) {
+ AltosPointDouble pt = t.screen(lat_lon);
+
+ g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
+ RenderingHints.VALUE_ANTIALIAS_ON);
+ g.setStroke(new BasicStroke(stroke_width, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
+
+ if (0 <= state && state < AltosUIMap.stateColors.length)
+ g.setColor(AltosUIMap.stateColors[state]);
+ else
+ g.setColor(AltosUIMap.stateColors[AltosLib.ao_flight_invalid]);
+
+ g.drawOval((int)pt.x-5, (int)pt.y-5, 10, 10);
+ g.drawOval((int)pt.x-20, (int)pt.y-20, 40, 40);
+ g.drawOval((int)pt.x-35, (int)pt.y-35, 70, 70);
+ }
+
+ MapMark(double lat, double lon, int state) {
+ super(lat, lon, state);
+ }
+ }
+
+ class MapView extends JComponent implements MouseMotionListener, MouseListener, ComponentListener, MouseWheelListener {
+
+ private VolatileImage create_back_buffer() {
+ return getGraphicsConfiguration().createCompatibleVolatileImage(getWidth(), getHeight());
+ }
+
+ private void do_paint(Graphics my_g) {
+ g = (Graphics2D) my_g;
+
+ map.paint();
+ }
+
+ public void paint(Graphics my_g) {
+ VolatileImage back_buffer = create_back_buffer();
+
+ Graphics2D top_g = (Graphics2D) my_g;
+
+ do {
+ GraphicsConfiguration gc = getGraphicsConfiguration();
+ int code = back_buffer.validate(gc);
+ if (code == VolatileImage.IMAGE_INCOMPATIBLE)
+ back_buffer = create_back_buffer();
+
+ Graphics g_back = back_buffer.getGraphics();
+ g_back.setClip(top_g.getClip());
+ do_paint(g_back);
+ g_back.dispose();
+
+ top_g.drawImage(back_buffer, 0, 0, this);
+ } while (back_buffer.contentsLost());
+ back_buffer.flush();
+ }
+
+ public void repaint(AltosRectangle damage) {
+ repaint(damage.x, damage.y, damage.width, damage.height);
+ }
+
+ private boolean is_drag_event(MouseEvent e) {
+ return e.getModifiers() == InputEvent.BUTTON1_MASK;
+ }
+
+ /* MouseMotionListener methods */
+
+ public void mouseDragged(MouseEvent e) {
+ map.touch_continue(e.getPoint().x, e.getPoint().y, is_drag_event(e));
+ }
+
+ public void mouseMoved(MouseEvent e) {
+ }
+
+ /* MouseListener methods */
+ public void mouseClicked(MouseEvent e) {
+ }
+
+ public void mouseEntered(MouseEvent e) {
+ }
+
+ public void mouseExited(MouseEvent e) {
+ }
+
+ public void mousePressed(MouseEvent e) {
+ map.touch_start(e.getPoint().x, e.getPoint().y, is_drag_event(e));
+ }
+
+ public void mouseReleased(MouseEvent e) {
+ }
+
+ /* MouseWheelListener methods */
+
+ public void mouseWheelMoved(MouseWheelEvent e) {
+ int zoom_change = e.getWheelRotation();
+
+ map.set_zoom_centre(map.get_zoom() - zoom_change, new AltosPointInt(e.getPoint().x, e.getPoint().y));
+ }
+
+ /* ComponentListener methods */
+
+ public void componentHidden(ComponentEvent e) {
+ }
+
+ public void componentMoved(ComponentEvent e) {
+ }
+
+ public void componentResized(ComponentEvent e) {
+ map.set_transform();
+ }
+
+ public void componentShown(ComponentEvent e) {
+ map.set_transform();
+ }
+
+ MapView() {
+ addComponentListener(this);
+ addMouseMotionListener(this);
+ addMouseListener(this);
+ addMouseWheelListener(this);
+ }
+ }
+
+ class MapLine extends AltosMapLine {
+
+ public void paint(AltosMapTransform t) {
+
+ if (start == null || end == null)
+ return;
+
+ g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
+
+ Line2D.Double line = new Line2D.Double(point2d(t.screen(start)),
+ point2d(t.screen(end)));
+
+ g.setColor(Color.WHITE);
+ g.setStroke(new BasicStroke(stroke_width+4, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
+ g.draw(line);
+
+ g.setColor(Color.BLUE);
+ g.setStroke(new BasicStroke(stroke_width, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
+ g.draw(line);
+
+ String message = line_dist();
+ Rectangle2D bounds;
+ bounds = line_font.getStringBounds(message, g.getFontRenderContext());
+
+ float x = (float) line.x1;
+ float y = (float) line.y1 + (float) bounds.getHeight() / 2.0f;
+
+ if (line.x1 < line.x2) {
+ x -= (float) bounds.getWidth() + 2.0f;
+ } else {
+ x += 2.0f;
+ }
+
+ g.setFont(line_font);
+ g.setColor(Color.WHITE);
+ for (int dy = -2; dy <= 2; dy += 2)
+ for (int dx = -2; dx <= 2; dx += 2)
+ g.drawString(message, x + dx, y + dy);
+ g.setColor(Color.BLUE);
+ g.drawString(message, x, y);
+ }
+
+ public MapLine() {
+ }
+ }
+
+ class MapPath extends AltosMapPath {
+ public void paint(AltosMapTransform t) {
+ Point2D.Double prev = null;
+
+ g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
+ RenderingHints.VALUE_ANTIALIAS_ON);
+ g.setStroke(new BasicStroke(stroke_width, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
+
+ for (AltosMapPathPoint point : points) {
+ Point2D.Double cur = point2d(t.screen(point.lat_lon));
+ if (prev != null) {
+ Line2D.Double line = new Line2D.Double (prev, cur);
+ Rectangle bounds = line.getBounds();
+
+ if (g.hitClip(bounds.x, bounds.y, bounds.width, bounds.height)) {
+ if (0 <= point.state && point.state < AltosUIMap.stateColors.length)
+ g.setColor(AltosUIMap.stateColors[point.state]);
+ else
+ g.setColor(AltosUIMap.stateColors[AltosLib.ao_flight_invalid]);
+
+ g.draw(line);
+ }
+ }
+ prev = cur;
+ }
+ }
+ }
+
+ class MapTile extends AltosMapTile {
+ public MapTile(AltosMapCache cache, AltosLatLon upper_left, AltosLatLon center, int zoom, int maptype, int px_size, int scale) {
+ super(cache, upper_left, center, zoom, maptype, px_size, scale);
+ }
+
+ public void paint(AltosMapTransform t) {
+
+ AltosPointDouble point_double = t.screen(upper_left);
+ Point point = new Point((int) (point_double.x + 0.5),
+ (int) (point_double.y + 0.5));
+
+ if (!g.hitClip(point.x, point.y, px_size, px_size))
+ return;
+
+ AltosImage altos_image = get_image();
+ AltosUIImage ui_image = (AltosUIImage) altos_image;
+ Image image = null;
+
+ if (ui_image != null)
+ image = ui_image.image;
+
+ if (image != null) {
+ g.drawImage(image, point.x, point.y, null);
+ } else {
+ g.setColor(Color.GRAY);
+ g.fillRect(point.x, point.y, px_size, px_size);
+
+ if (t.has_location()) {
+ String message = null;
+ switch (status) {
+ case AltosMapTile.fetching:
+ message = "Fetching...";
+ break;
+ case AltosMapTile.bad_request:
+ message = "Internal error";
+ break;
+ case AltosMapTile.failed:
+ message = "Network error, check connection";
+ break;
+ case AltosMapTile.forbidden:
+ message = "Too many requests, try later";
+ break;
+ }
+ if (message != null && tile_font != null) {
+ g.setFont(tile_font);
+ g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
+ Rectangle2D bounds = tile_font.getStringBounds(message, g.getFontRenderContext());
+
+ float x = px_size / 2.0f;
+ float y = px_size / 2.0f;
+ x = x - (float) bounds.getWidth() / 2.0f;
+ y = y + (float) bounds.getHeight() / 2.0f;
+ g.setColor(Color.BLACK);
+ g.drawString(message, (float) point_double.x + x, (float) point_double.y + y);
+ }
+ }
+ }
+ }
+ }
public static final Color stateColors[] = {
Color.WHITE, // startup
Color.CYAN, // stateless
};
- public void reset() {
- // nothing
+ /* AltosMapInterface functions */
+
+ public AltosMapPath new_path() {
+ return new MapPath();
}
- public void font_size_changed(int font_size) {
- view.set_font();
+ public AltosMapLine new_line() {
+ return new MapLine();
}
- public void units_changed(boolean imperial_units) {
- view.set_units();
+ public AltosImage load_image(File file) throws Exception {
+ return new AltosUIImage(ImageIO.read(file));
}
- JLabel zoom_label;
+ public AltosMapMark new_mark(double lat, double lon, int state) {
+ return new MapMark(lat, lon, state);
+ }
- private void set_zoom_label() {
- zoom_label.setText(String.format("Zoom %d", view.zoom() - view.default_zoom));
+ public AltosMapTile new_tile(AltosMapCache cache, AltosLatLon upper_left, AltosLatLon center, int zoom, int maptype, int px_size, int scale) {
+ return new MapTile(cache, upper_left, center, zoom, maptype, px_size, scale);
}
- public void zoom_changed(int zoom) {
- set_zoom_label();
+ public int width() {
+ return view.getWidth();
}
- public void set_zoom(int zoom) {
- view.set_zoom(zoom);
+ public int height() {
+ return view.getHeight();
}
- public int get_zoom() {
- return view.zoom();
+ public void repaint() {
+ view.repaint();
}
- public void set_maptype(int type) {
- view.set_maptype(type);
- maptype_combo.setSelectedIndex(type);
+ public void repaint(AltosRectangle damage) {
+ view.repaint(damage);
}
- public void show(AltosState state, AltosListenerState listener_state) {
- view.show(state, listener_state);
+ public void set_zoom_label(String label) {
+ zoom_label.setText(label);
}
- public void centre(double lat, double lon) {
- view.centre(lat, lon);
+ public void select_object(AltosLatLon latlon) {
+ debug("select at %f,%f\n", latlon.lat, latlon.lon);
}
- public void centre(AltosState state) {
- if (!state.gps.locked && state.gps.nsat < 4)
- return;
- centre(state.gps.lat, state.gps.lon);
+ public void debug(String format, Object ... arguments) {
+ if (AltosUIPreferences.serial_debug())
+ System.out.printf(format, arguments);
}
- public void add_mark(double lat, double lon, int state) {
- view.add_mark(lat, lon, state);
+
+ /* AltosFlightDisplay interface */
+
+ public void set_font() {
+ tile_font = AltosUILib.value_font;
+ line_font = AltosUILib.status_font;
}
- public void clear_marks() {
- view.clear_marks();
+ public void font_size_changed(int font_size) {
+ set_font();
+ repaint();
}
- AltosUIMapView view;
+ public void units_changed(boolean imperial_units) {
+ repaint();
+ }
- private GridBagLayout layout = new GridBagLayout();
+ JLabel zoom_label;
- JComboBox<String> maptype_combo;
+ public void set_maptype(int type) {
+ map.set_maptype(type);
+ maptype_combo.setSelectedIndex(type);
+ }
+
+ /* AltosUIMapPreload functions */
+
+ public void set_zoom(int zoom) {
+ map.set_zoom(zoom);
+ }
- public void set_load_params(double lat, double lon, int radius, AltosUIMapTileListener listener) {
- view.set_load_params(lat, lon, radius, listener);
+ public void add_mark(double lat, double lon, int status) {
+ map.add_mark(lat, lon, status);
}
- public boolean all_fetched() {
- return view.all_fetched();
+ public void clear_marks() {
+ map.clear_marks();
}
- public static void prefetch_maps(double lat, double lon) {
+ /* AltosFlightDisplay interface */
+ public void reset() {
+ // nothing
+ }
+
+ public void show(AltosState state, AltosListenerState listener_state) {
+ map.show(state, listener_state);
+ }
+
+ public void show(AltosGPS gps, int state) {
+ map.show(gps, state);
}
public String getName() {
return "Map";
}
+ /* AltosGraphUI interface */
+ public void centre(AltosState state) {
+ map.centre(state);
+ }
+
+ public void centre(AltosGPS gps) {
+ map.centre(gps);
+ }
+
+ /* internal layout bits */
+ private GridBagLayout layout = new GridBagLayout();
+
+ JComboBox<String> maptype_combo;
+
+ MapView view;
+
public AltosUIMap() {
- view = new AltosUIMapView();
+ set_font();
+
+ view = new MapView();
view.setPreferredSize(new Dimension(500,500));
view.setVisible(true);
view.setEnabled(true);
- view.add_zoom_listener(this);
GridBagLayout my_layout = new GridBagLayout();
int y = 0;
zoom_label = new JLabel("", JLabel.CENTER);
- set_zoom_label();
c = new GridBagConstraints();
c.anchor = GridBagConstraints.CENTER;
JButton zoom_reset = new JButton("0");
zoom_reset.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
- set_zoom(view.default_zoom);
+ map.set_zoom(map.default_zoom);
}
});
JButton zoom_in = new JButton("+");
zoom_in.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
- set_zoom(get_zoom() + 1);
+ map.set_zoom(map.get_zoom() + 1);
}
});
JButton zoom_out = new JButton("-");
zoom_out.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
- set_zoom(get_zoom() - 1);
+ map.set_zoom(map.get_zoom() - 1);
}
});
c = new GridBagConstraints();
c.weighty = 0;
add(zoom_out, c);
- maptype_combo = new JComboBox<String>(maptype_labels);
+ maptype_combo = new JComboBox<String>(map.maptype_labels);
maptype_combo.setEditable(false);
maptype_combo.setMaximumRowCount(maptype_combo.getItemCount());
maptype_combo.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) {
- view.set_maptype(maptype_combo.getSelectedIndex());
+ map.set_maptype(maptype_combo.getSelectedIndex());
}
});
c.weightx = 0;
c.weighty = 0;
add(maptype_combo, c);
+
+ map = new AltosMap(this);
}
}