From: Keith Packard Date: Thu, 12 Jun 2014 01:46:47 +0000 (-0700) Subject: altosuilib: Rewrite map GUI bits X-Git-Tag: 1.4~77 X-Git-Url: https://git.gag.com/?p=fw%2Faltos;a=commitdiff_plain;h=db2443fdbf65b65703217174303027c439124a83 altosuilib: Rewrite map GUI bits Use a single large Canvas and draw images on top by hand. Signed-off-by: Keith Packard --- diff --git a/altosui/AltosFlightUI.java b/altosui/AltosFlightUI.java index baa18686..f2bd70a0 100644 --- a/altosui/AltosFlightUI.java +++ b/altosui/AltosFlightUI.java @@ -37,7 +37,7 @@ public class AltosFlightUI extends AltosUIFrame implements AltosFlightDisplay { AltosDescent descent; AltosLanded landed; AltosCompanionInfo companion; - AltosSiteMap sitemap; + AltosUIMap sitemap; boolean has_map; boolean has_companion; boolean has_state; @@ -310,7 +310,7 @@ public class AltosFlightUI extends AltosUIFrame implements AltosFlightDisplay { has_companion = false; has_state = false; - sitemap = new AltosSiteMap(); + sitemap = new AltosUIMap(); has_map = false; /* Make the tabbed pane use the rest of the window space */ diff --git a/altosui/AltosGraphUI.java b/altosui/AltosGraphUI.java index 9e8a1939..0df92eae 100644 --- a/altosui/AltosGraphUI.java +++ b/altosui/AltosGraphUI.java @@ -34,7 +34,7 @@ public class AltosGraphUI extends AltosUIFrame JTabbedPane pane; AltosGraph graph; AltosUIEnable enable; - AltosSiteMap map; + AltosUIMap map; AltosState state; AltosGraphDataSet graphDataSet; AltosFlightStats stats; @@ -46,7 +46,7 @@ public class AltosGraphUI extends AltosUIFrame for (AltosState state : states) { if (state.gps != null && state.gps.locked && state.gps.nsat >= 4) { if (map == null) - map = new AltosSiteMap(); + map = new AltosUIMap(); map.show(state, null); has_gps = true; } diff --git a/altosui/AltosUI.java b/altosui/AltosUI.java index 302f623f..6137487c 100644 --- a/altosui/AltosUI.java +++ b/altosui/AltosUI.java @@ -280,7 +280,7 @@ public class AltosUI extends AltosUIFrame { } void LoadMaps() { - new AltosSiteMapPreload(AltosUI.this); + new AltosUIMapPreload(AltosUI.this); } void LaunchController() { @@ -578,7 +578,7 @@ public class AltosUI extends AltosUIFrame { } else { double lat = Double.parseDouble(args[i+1]); double lon = Double.parseDouble(args[i+2]); - AltosSiteMap.prefetchMaps(lat, lon); +// AltosSiteMap.prefetchMaps(lat, lon); i += 2; } } else if (args[i].equals("--replay")) diff --git a/altosuilib/AltosUILatLon.java b/altosuilib/AltosUILatLon.java new file mode 100644 index 00000000..688dd58b --- /dev/null +++ b/altosuilib/AltosUILatLon.java @@ -0,0 +1,44 @@ +/* + * Copyright © 2014 Keith Packard + * + * 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. + * + * 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package org.altusmetrum.altosuilib_2; + +import java.awt.*; +import java.awt.event.*; +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_4.*; + +public class AltosUILatLon { + public double lat; + public double lon; + + public boolean equals(AltosUILatLon other) { + if (other == null) + return false; + return lat == other.lat && lon == other.lon; + } + + public AltosUILatLon(double lat, double lon) { + this.lat = lat; + this.lon = lon; + } +} diff --git a/altosuilib/AltosUIMap.java b/altosuilib/AltosUIMap.java new file mode 100644 index 00000000..fa974d36 --- /dev/null +++ b/altosuilib/AltosUIMap.java @@ -0,0 +1,246 @@ +/* + * Copyright © 2010 Anthony Towns + * + * 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. + * + * 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package org.altusmetrum.altosuilib_2; + +import java.awt.*; +import java.awt.event.*; +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_4.*; + +public class AltosUIMap extends JComponent implements AltosFlightDisplay, AltosUIMapZoomListener { + + static final int px_size = 512; + + 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 final String[] maptype_names = { + "hybrid", + "roadmap", + "satellite", + "terrain" + }; + + public static final String[] maptype_labels = { + "Hybrid", + "Roadmap", + "Satellite", + "Terrain" + }; + + public static final Color stateColors[] = { + Color.WHITE, // startup + Color.WHITE, // idle + Color.WHITE, // pad + Color.RED, // boost + Color.PINK, // fast + Color.YELLOW, // coast + Color.CYAN, // drogue + Color.BLUE, // main + Color.BLACK, // landed + Color.BLACK, // invalid + Color.CYAN, // stateless + }; + + public void reset() { + // nothing + } + + public void font_size_changed(int font_size) { + view.set_font(); + } + + public void units_changed(boolean imperial_units) { + repaint(); + } + + JLabel zoom_label; + + private void set_zoom_label() { + zoom_label.setText(String.format("Zoom %d", view.zoom() - view.default_zoom)); + } + + public void zoom_changed(int zoom) { + set_zoom_label(); + } + + public void set_zoom(int zoom) { + view.set_zoom(zoom); + } + + public int get_zoom() { + return view.zoom(); + } + + public void set_maptype(int type) { + view.set_maptype(type); + maptype_combo.setSelectedIndex(type); + } + + public void show(AltosState state, AltosListenerState listener_state) { + view.show(state, listener_state); + } + + public void centre(double lat, double lon) { + view.centre(lat, lon); + } + + public void centre(AltosState state) { + if (!state.gps.locked && state.gps.nsat < 4) + return; + centre(state.gps.lat, state.gps.lon); + } + + public void add_mark(double lat, double lon, int state) { + view.add_mark(lat, lon, state); + } + + public void clear_marks() { + view.clear_marks(); + } + + AltosUIMapView view; + + private GridBagLayout layout = new GridBagLayout(); + + JComboBox maptype_combo; + + public void set_load_params(double lat, double lon, int radius, AltosUIMapTileListener listener) { + view.set_load_params(lat, lon, radius, listener); + } + + public boolean all_fetched() { + return view.all_fetched(); + } + + public static void prefetch_maps(double lat, double lon) { + } + + public AltosUIMap() { + + view = new AltosUIMapView(); + + view.setPreferredSize(new Dimension(500,500)); + view.setVisible(true); + view.setEnabled(true); + view.add_zoom_listener(this); + + GridBagLayout my_layout = new GridBagLayout(); + + setLayout(my_layout); + + GridBagConstraints c = new GridBagConstraints(); + c.anchor = GridBagConstraints.CENTER; + c.fill = GridBagConstraints.BOTH; + c.gridx = 0; + c.gridy = 0; + c.gridwidth = 1; + c.gridheight = 10; + c.weightx = 1; + c.weighty = 1; + add(view, c); + + int y = 0; + + zoom_label = new JLabel("", JLabel.CENTER); + set_zoom_label(); + + c = new GridBagConstraints(); + c.anchor = GridBagConstraints.CENTER; + c.fill = GridBagConstraints.HORIZONTAL; + c.gridx = 1; + c.gridy = y++; + c.weightx = 0; + c.weighty = 0; + add(zoom_label, c); + + JButton zoom_reset = new JButton("0"); + zoom_reset.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + set_zoom(view.default_zoom); + } + }); + + c = new GridBagConstraints(); + c.anchor = GridBagConstraints.CENTER; + c.fill = GridBagConstraints.HORIZONTAL; + c.gridx = 1; + c.gridy = y++; + c.weightx = 0; + c.weighty = 0; + add(zoom_reset, c); + + JButton zoom_in = new JButton("+"); + zoom_in.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + set_zoom(get_zoom() + 1); + } + }); + + c = new GridBagConstraints(); + c.anchor = GridBagConstraints.CENTER; + c.fill = GridBagConstraints.HORIZONTAL; + c.gridx = 1; + c.gridy = y++; + c.weightx = 0; + c.weighty = 0; + add(zoom_in, c); + + JButton zoom_out = new JButton("-"); + zoom_out.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + set_zoom(get_zoom() - 1); + } + }); + c = new GridBagConstraints(); + c.anchor = GridBagConstraints.CENTER; + c.fill = GridBagConstraints.HORIZONTAL; + c.gridx = 1; + c.gridy = y++; + c.weightx = 0; + c.weighty = 0; + add(zoom_out, c); + + maptype_combo = new JComboBox(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()); + } + }); + + c = new GridBagConstraints(); + c.anchor = GridBagConstraints.CENTER; + c.fill = GridBagConstraints.HORIZONTAL; + c.gridx = 1; + c.gridy = y++; + c.weightx = 0; + c.weighty = 0; + add(maptype_combo, c); + } +} diff --git a/altosuilib/AltosUIMapCache.java b/altosuilib/AltosUIMapCache.java new file mode 100644 index 00000000..e849da79 --- /dev/null +++ b/altosuilib/AltosUIMapCache.java @@ -0,0 +1,112 @@ +/* + * Copyright © 2010 Anthony Towns + * + * 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. + * + * 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package org.altusmetrum.altosuilib_2; + +import javax.swing.*; +import javax.imageio.ImageIO; +import java.awt.image.*; +import java.awt.*; +import java.io.*; +import java.net.*; + +public class AltosUIMapCache { + static final int success = 0; + static final int loading = 1; + static final int failed = 2; + static final int bad_request = 3; + static final int forbidden = 4; + + static private Object fetch_lock = new Object(); + + static final int min_cache_size = 9; + static final int max_cache_size = 24; + + static int cache_size = min_cache_size; + + static AltosUIMapImage[] images = new AltosUIMapImage[cache_size]; + + static Object cache_lock = new Object(); + + public static void set_cache_size(int new_size) { + if (new_size < min_cache_size) + new_size = min_cache_size; + if (new_size > max_cache_size) + new_size = max_cache_size; + if (new_size == cache_size) + return; + + synchronized(cache_lock) { + AltosUIMapImage[] new_images = new AltosUIMapImage[new_size]; + + for (int i = 0; i < cache_size; i++) { + if (i < new_size) + new_images[i] = images[i]; + else if (images[i] != null) + images[i].flush(); + } + images = new_images; + cache_size = new_size; + } + } + + static long used; + + public static Image get(AltosUIMapTile tile, AltosUIMapStore store, int width, int height) { + int oldest = -1; + long age = used; + + synchronized(cache_lock) { + AltosUIMapImage image = null; + for (int i = 0; i < cache_size; i++) { + image = images[i]; + + if (image == null) { + oldest = i; + break; + } + if (store.equals(image.store)) { + image.used = used++; + return image.image; + } + if (image.used < age) { + oldest = i; + age = image.used; + } + } + + try { + image = new AltosUIMapImage(tile, store); + image.used = used++; + if (images[oldest] != null) + images[oldest].flush(); + + images[oldest] = image; + + if (image.image == null) + tile.set_status(loading); + else + tile.set_status(success); + + return image.image; + } catch (IOException e) { + tile.set_status(failed); + return null; + } + } + } +} diff --git a/altosuilib/AltosUIMapImage.java b/altosuilib/AltosUIMapImage.java new file mode 100644 index 00000000..3819d079 --- /dev/null +++ b/altosuilib/AltosUIMapImage.java @@ -0,0 +1,113 @@ +/* + * Copyright © 2010 Anthony Towns + * + * 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. + * + * 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package org.altusmetrum.altosuilib_2; + +import javax.swing.*; +import javax.imageio.ImageIO; +import java.awt.image.*; +import java.awt.*; +import java.io.*; +import java.net.*; + +public class AltosUIMapImage implements AltosUIMapStoreListener { + static final long google_maps_ratelimit_ms = 1200; + // Google limits static map queries to 50 per minute per IP, so + // each query should take at least 1.2 seconds. + + static final int success = 0; + static final int loading = 1; + static final int failed = 2; + static final int bad_request = 3; + static final int forbidden = 4; + + static long forbidden_time; + static boolean forbidden_set = false; + static final long forbidden_interval = 60l * 1000l * 1000l * 1000l; + + AltosUIMapTile tile; /* Notify when image has been loaded */ + Image image; + AltosUIMapStore store; + long used; + + class loader implements Runnable { + public void run() { + if (image != null) + tile.notify_image(image); + try { + image = ImageIO.read(store.file); + } catch (Exception ex) { + } + if (image == null) + tile.set_status(failed); + else + tile.set_status(success); + tile.notify_image(image); + } + } + + private void load() { + loader l = new loader(); + Thread lt = new Thread(l); + lt.start(); + } + + public void flush() { + if (image != null) { + image.flush(); + image = null; + } + } + + public boolean has_map() { + return store.status() == AltosUIMapStore.success; + } + + public synchronized void notify_store(AltosUIMapStore store, int status) { + switch (status) { + case AltosUIMapStore.loading: + break; + case AltosUIMapStore.success: + load(); + break; + default: + tile.set_status(status); + tile.notify_image(null); + } + } + + public AltosUIMapImage(AltosUIMapTile tile, AltosUIMapStore store) throws IOException { + this.tile = tile; + this.image = null; + this.store = store; + this.used = 0; + + int status = store.status(); + switch (status) { + case AltosUIMapStore.loading: + store.add_listener(this); + break; + case AltosUIMapStore.success: + load(); + break; + default: + tile.set_status(status); + tile.notify_image(null); + break; + } + } +} diff --git a/altosuilib/AltosUIMapLine.java b/altosuilib/AltosUIMapLine.java new file mode 100644 index 00000000..e09a2d9f --- /dev/null +++ b/altosuilib/AltosUIMapLine.java @@ -0,0 +1,116 @@ +/* + * Copyright © 2014 Keith Packard + * + * 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. + * + * 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package org.altusmetrum.altosuilib_2; + +import java.awt.*; +import java.awt.event.*; +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_4.*; + +public class AltosUIMapLine { + AltosUILatLon start, end; + + private Font font = null; + + public void set_font(Font font) { + this.font = font; + } + + private AltosUILatLon lat_lon(MouseEvent e, AltosUIMapTransform t) { + return t.screen_lat_lon(e.getPoint()); + } + + public void dragged(MouseEvent e, AltosUIMapTransform t) { + end = lat_lon(e, t); + } + + public void pressed(MouseEvent e, AltosUIMapTransform t) { + start = lat_lon(e, t); + end = null; + } + + private String line_dist() { + String format; + AltosGreatCircle g = new AltosGreatCircle(start.lat, start.lon, + end.lat, end.lon); + double distance = g.distance; + + if (AltosConvert.imperial_units) { + distance = AltosConvert.meters_to_feet(distance); + if (distance < 10000) { + format = "%4.0fft"; + } else { + distance /= 5280; + if (distance < 10) + format = "%5.3fmi"; + else if (distance < 100) + format = "%5.2fmi"; + else if (distance < 1000) + format = "%5.1fmi"; + else + format = "%5.0fmi"; + } + } else { + if (distance < 10000) { + format = "%4.0fm"; + } else { + distance /= 1000; + if (distance < 100) + format = "%5.2fkm"; + else if (distance < 1000) + format = "%5.1fkm"; + else + format = "%5.0fkm"; + } + } + return String.format(format, distance); + } + + public void paint(Graphics2D g, AltosUIMapTransform t) { + g.setColor(Color.BLUE); + + if (start == null || end == null) + return; + + Line2D.Double line = new Line2D.Double(t.screen(start), + t.screen(end)); + + g.draw(line); + + String message = line_dist(); + g.setFont(font); + g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + Rectangle2D bounds; + bounds = 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.drawString(message, x, y); + } +} diff --git a/altosuilib/AltosUIMapMark.java b/altosuilib/AltosUIMapMark.java new file mode 100644 index 00000000..8c640e5f --- /dev/null +++ b/altosuilib/AltosUIMapMark.java @@ -0,0 +1,59 @@ +/* + * Copyright © 2014 Keith Packard + * + * 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. + * + * 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package org.altusmetrum.altosuilib_2; + +import java.awt.*; +import java.awt.event.*; +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_4.*; + +public class AltosUIMapMark { + + AltosUILatLon lat_lon; + int state; + + static public int stroke_width = 6; + + public void paint(Graphics2D g, AltosUIMapTransform t) { + + Point2D.Double 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); + } + + public AltosUIMapMark (double lat, double lon, int state) { + lat_lon = new AltosUILatLon(lat, lon); + this.state = state; + } +} diff --git a/altosuilib/AltosUIMapPath.java b/altosuilib/AltosUIMapPath.java new file mode 100644 index 00000000..ff17be67 --- /dev/null +++ b/altosuilib/AltosUIMapPath.java @@ -0,0 +1,96 @@ +/* + * Copyright © 2014 Keith Packard + * + * 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. + * + * 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package org.altusmetrum.altosuilib_2; + +import java.awt.*; +import java.awt.event.*; +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_4.*; + +class PathPoint { + AltosUILatLon lat_lon; + int state; + + public PathPoint(AltosUILatLon lat_lon, int state) { + this.lat_lon = lat_lon; + this.state = state; + } + + public boolean equals(PathPoint other) { + if (other == null) + return false; + + return lat_lon.equals(other.lat_lon) && state == other.state; + } +} + +public class AltosUIMapPath { + + LinkedList points = new LinkedList(); + PathPoint last_point = null; + + static public int stroke_width = 6; + + public void paint(Graphics2D g, AltosUIMapTransform 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 (PathPoint point : points) { + Point2D.Double cur = 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; + } + } + + public AltosUIMapRectangle add(double lat, double lon, int state) { + PathPoint point = new PathPoint(new AltosUILatLon (lat, lon), state); + AltosUIMapRectangle rect = null; + + if (!point.equals(last_point)) { + if (last_point != null) + rect = new AltosUIMapRectangle(last_point.lat_lon, point.lat_lon); + points.add (point); + last_point = point; + } + return rect; + } + + public void clear () { + points = new LinkedList(); + } +} diff --git a/altosuilib/AltosUIMapPreload.java b/altosuilib/AltosUIMapPreload.java new file mode 100644 index 00000000..d702dddf --- /dev/null +++ b/altosuilib/AltosUIMapPreload.java @@ -0,0 +1,608 @@ +/* + * Copyright © 2011 Keith Packard + * + * 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. + * + * 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package org.altusmetrum.altosuilib_2; + +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import java.io.*; +import java.util.*; +import java.text.*; +import java.lang.Math; +import java.net.URL; +import java.net.URLConnection; +import org.altusmetrum.altoslib_4.*; + +class AltosUIMapPos extends Box { + AltosUIFrame owner; + JLabel label; + JComboBox hemi; + JTextField deg; + JLabel deg_label; + JTextField min; + JLabel min_label; + + public void set_value(double new_value) { + double d, m; + int h; + + h = 0; + if (new_value < 0) { + h = 1; + new_value = -new_value; + } + d = Math.floor(new_value); + deg.setText(String.format("%3.0f", d)); + m = (new_value - d) * 60.0; + min.setText(String.format("%7.4f", m)); + hemi.setSelectedIndex(h); + } + + public double get_value() throws NumberFormatException { + int h = hemi.getSelectedIndex(); + String d_t = deg.getText(); + String m_t = min.getText(); + double d, m, v; + try { + d = Double.parseDouble(d_t); + } catch (NumberFormatException ne) { + JOptionPane.showMessageDialog(owner, + String.format("Invalid degrees \"%s\"", + d_t), + "Invalid number", + JOptionPane.ERROR_MESSAGE); + throw ne; + } + try { + if (m_t.equals("")) + m = 0; + else + m = Double.parseDouble(m_t); + } catch (NumberFormatException ne) { + JOptionPane.showMessageDialog(owner, + String.format("Invalid minutes \"%s\"", + m_t), + "Invalid number", + JOptionPane.ERROR_MESSAGE); + throw ne; + } + v = d + m/60.0; + if (h == 1) + v = -v; + return v; + } + + public AltosUIMapPos(AltosUIFrame in_owner, + String label_value, + String[] hemi_names, + double default_value) { + super(BoxLayout.X_AXIS); + owner = in_owner; + label = new JLabel(label_value); + hemi = new JComboBox(hemi_names); + hemi.setEditable(false); + deg = new JTextField(5); + deg.setMinimumSize(deg.getPreferredSize()); + deg.setHorizontalAlignment(JTextField.RIGHT); + deg_label = new JLabel("°"); + min = new JTextField(9); + min.setMinimumSize(min.getPreferredSize()); + min_label = new JLabel("'"); + set_value(default_value); + add(label); + add(Box.createRigidArea(new Dimension(5, 0))); + add(hemi); + add(Box.createRigidArea(new Dimension(5, 0))); + add(deg); + add(Box.createRigidArea(new Dimension(5, 0))); + add(deg_label); + add(Box.createRigidArea(new Dimension(5, 0))); + add(min); + add(Box.createRigidArea(new Dimension(5, 0))); + add(min_label); + } +} + +class AltosUISite { + String name; + double latitude; + double longitude; + + public String toString() { + return name; + } + + public AltosUISite(String in_name, double in_latitude, double in_longitude) { + name = in_name; + latitude = in_latitude; + longitude = in_longitude; + } + + public AltosUISite(String line) throws ParseException { + String[] elements = line.split(":"); + + if (elements.length < 3) + throw new ParseException(String.format("Invalid site line %s", line), 0); + + name = elements[0]; + + try { + latitude = Double.parseDouble(elements[1]); + longitude = Double.parseDouble(elements[2]); + } catch (NumberFormatException ne) { + throw new ParseException(String.format("Invalid site line %s", line), 0); + } + } +} + +class AltosUISites extends Thread { + AltosUIMapPreload preload; + URL url; + LinkedList sites; + + void notify_complete() { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + preload.set_sites(); + } + }); + } + + void add(AltosUISite site) { + sites.add(site); + } + + void add(String line) { + try { + add(new AltosUISite(line)); + } catch (ParseException pe) { + } + } + + public void run() { + try { + URLConnection uc = url.openConnection(); + //int length = uc.getContentLength(); + + InputStreamReader in_stream = new InputStreamReader(uc.getInputStream(), AltosLib.unicode_set); + BufferedReader in = new BufferedReader(in_stream); + + for (;;) { + String line = in.readLine(); + if (line == null) + break; + add(line); + } + } catch (IOException e) { + } finally { + notify_complete(); + } + } + + public AltosUISites(AltosUIMapPreload in_preload) { + sites = new LinkedList(); + preload = in_preload; + try { + url = new URL(AltosLib.launch_sites_url); + } catch (java.net.MalformedURLException e) { + notify_complete(); + } + start(); + } +} + +public class AltosUIMapPreload extends AltosUIFrame implements ActionListener, ItemListener, AltosUIMapTileListener { + AltosUIFrame owner; + AltosUIMap map; + + AltosUIMapPos lat; + AltosUIMapPos lon; + + JProgressBar pbar; + int pbar_max; + int pbar_cur; + + AltosUISites sites; + JLabel site_list_label; + JComboBox site_list; + + JToggleButton load_button; + boolean loading; + JButton close_button; + + JCheckBox[] maptypes = new JCheckBox[AltosUIMap.maptype_terrain - AltosUIMap.maptype_hybrid + 1]; + + JComboBox min_zoom; + JComboBox max_zoom; + JComboBox radius; + + Integer[] zooms = { -12, -11, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6 }; + Integer[] radii = { 1, 2, 3, 4, 5 }; + + static final String[] lat_hemi_names = { "N", "S" }; + static final String[] lon_hemi_names = { "E", "W" }; + + class updatePbar implements Runnable { + String s; + + public updatePbar(String in_s) { + s = in_s; + } + + public void run() { + int n = ++pbar_cur; + + pbar.setMaximum(pbar_max); + pbar.setValue(n); + pbar.setString(s); + } + } + + double latitude, longitude; + int min_z; + int max_z; + int cur_z; + int all_types; + int cur_type; + int r; + + int tiles_per_layer; + int tiles_loaded; + int layers_total; + int layers_loaded; + + + private void do_load() { + tiles_loaded = 0; + map.set_zoom(cur_z + AltosUIMapView.default_zoom); + map.set_maptype(cur_type); + map.set_load_params(latitude, longitude, r, this); + } + + private int next_type(int start) { + int next_type; + for (next_type = start; + next_type <= AltosUIMap.maptype_terrain && (all_types & (1 << next_type)) == 0; + next_type++) + ; + return next_type; + } + + private void next_load() { + int next_type = next_type(cur_type + 1); + + if (next_type > AltosUIMap.maptype_terrain) { + if (cur_z == max_z) { + return; + } else { + cur_z++; + } + next_type = next_type(0); + } + cur_type = next_type; + do_load(); + } + + private void start_load() { + cur_z = min_z; + int ntype = 0; + all_types = 0; + for (int t = AltosUIMap.maptype_hybrid; t <= AltosUIMap.maptype_terrain; t++) + if (maptypes[t].isSelected()) { + all_types |= (1 << t); + ntype++; + } + if (ntype == 0) { + all_types |= (1 << AltosUIMap.maptype_hybrid); + ntype = 1; + } + + cur_type = next_type(0); + tiles_per_layer = (r * 2 + 1) * (r * 2 + 1); + layers_total = (max_z - min_z + 1) * ntype; + layers_loaded = 0; + pbar_max = layers_total * tiles_per_layer; + pbar_cur = 0; + + map.clear_marks(); + map.add_mark(latitude,longitude, AltosLib.ao_flight_boost); + do_load(); + } + + /* AltosUIMapTileListener methods */ + + public void notify_tile(AltosUIMapTile tile, int status) { + if (status == AltosUIMapStore.loading) + return; + + SwingUtilities.invokeLater(new updatePbar(tile.store.file.toString())); + ++tiles_loaded; + if (tiles_loaded == tiles_per_layer) { + ++layers_loaded; + if (layers_loaded == layers_total) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + pbar.setValue(0); + pbar.setString(""); + load_button.setSelected(false); + loading = false; + } + }); + } else { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + next_load(); + } + }); + } + } + } + + public void set_sites() { + int i = 1; + for (AltosUISite site : sites.sites) { + site_list.insertItemAt(site, i); + i++; + } + } + + public void itemStateChanged(ItemEvent e) { + int state = e.getStateChange(); + + if (state == ItemEvent.SELECTED) { + Object o = e.getItem(); + if (o instanceof AltosUISite) { + AltosUISite site = (AltosUISite) o; + lat.set_value(site.latitude); + lon.set_value(site.longitude); + } + } + } + + public void actionPerformed(ActionEvent e) { + String cmd = e.getActionCommand(); + + if (cmd.equals("close")) + setVisible(false); + + if (cmd.equals("load")) { + if (!loading) { + try { + latitude = lat.get_value(); + longitude = lon.get_value(); + min_z = (Integer) min_zoom.getSelectedItem(); + max_z = (Integer) max_zoom.getSelectedItem(); + if (max_z < min_z) + max_z = min_z; + r = (Integer) radius.getSelectedItem(); + loading = true; + } catch (NumberFormatException ne) { + load_button.setSelected(false); + } + start_load(); + } + } + } + + public AltosUIMapPreload(AltosUIFrame in_owner) { + System.out.printf("start creating preload ui\n"); + + owner = in_owner; + + Container pane = getContentPane(); + GridBagConstraints c = new GridBagConstraints(); + Insets i = new Insets(4,4,4,4); + + setTitle("AltOS Load Maps"); + + pane.setLayout(new GridBagLayout()); + + map = new AltosUIMap(); + + c.fill = GridBagConstraints.BOTH; + c.anchor = GridBagConstraints.CENTER; + c.insets = i; + c.weightx = 1; + c.weighty = 1; + + c.gridx = 0; + c.gridy = 0; + c.gridwidth = 10; + c.anchor = GridBagConstraints.CENTER; + + pane.add(map, c); + + pbar = new JProgressBar(); + pbar.setMinimum(0); + pbar.setMaximum(1); + pbar.setValue(0); + pbar.setString(""); + pbar.setStringPainted(true); + + c.fill = GridBagConstraints.HORIZONTAL; + c.anchor = GridBagConstraints.CENTER; + c.insets = i; + c.weightx = 1; + c.weighty = 0; + + c.gridx = 0; + c.gridy = 1; + c.gridwidth = 10; + + pane.add(pbar, c); + + site_list_label = new JLabel ("Known Launch Sites:"); + + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.CENTER; + c.insets = i; + c.weightx = 1; + c.weighty = 0; + + c.gridx = 0; + c.gridy = 2; + c.gridwidth = 1; + + pane.add(site_list_label, c); + + site_list = new JComboBox(new AltosUISite[] { new AltosUISite("Site List", 0, 0) }); + site_list.addItemListener(this); + + sites = new AltosUISites(this); + + c.fill = GridBagConstraints.HORIZONTAL; + c.anchor = GridBagConstraints.CENTER; + c.insets = i; + c.weightx = 1; + c.weighty = 0; + + c.gridx = 1; + c.gridy = 2; + c.gridwidth = 1; + + pane.add(site_list, c); + + lat = new AltosUIMapPos(owner, + "Latitude:", + lat_hemi_names, + 37.167833333); + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.CENTER; + c.insets = i; + c.weightx = 0; + c.weighty = 0; + + c.gridx = 0; + c.gridy = 3; + c.gridwidth = 1; + c.anchor = GridBagConstraints.CENTER; + + pane.add(lat, c); + + lon = new AltosUIMapPos(owner, + "Longitude:", + lon_hemi_names, + -97.73975); + + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.CENTER; + c.insets = i; + c.weightx = 0; + c.weighty = 0; + + c.gridx = 1; + c.gridy = 3; + c.gridwidth = 1; + c.anchor = GridBagConstraints.CENTER; + + pane.add(lon, c); + + load_button = new JToggleButton("Load Map"); + load_button.addActionListener(this); + load_button.setActionCommand("load"); + + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.CENTER; + c.insets = i; + c.weightx = 1; + c.weighty = 0; + + c.gridx = 0; + c.gridy = 4; + c.gridwidth = 1; + c.anchor = GridBagConstraints.CENTER; + + pane.add(load_button, c); + + close_button = new JButton("Close"); + close_button.addActionListener(this); + close_button.setActionCommand("close"); + + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.CENTER; + c.insets = i; + c.weightx = 1; + c.weighty = 0; + + c.gridx = 1; + c.gridy = 4; + c.gridwidth = 1; + c.anchor = GridBagConstraints.CENTER; + + pane.add(close_button, c); + + JLabel types_label = new JLabel("Map Types"); + c.gridx = 2; + c.gridwidth = 2; + c.gridy = 2; + pane.add(types_label, c); + + c.gridwidth = 1; + + for (int type = AltosUIMap.maptype_hybrid; type <= AltosUIMap.maptype_terrain; type++) { + maptypes[type] = new JCheckBox(AltosUIMap.maptype_labels[type], + type == AltosUIMap.maptype_hybrid); + c.gridx = 2 + (type >> 1); + c.fill = GridBagConstraints.HORIZONTAL; + c.gridy = (type & 1) + 3; + pane.add(maptypes[type], c); + } + + JLabel min_zoom_label = new JLabel("Minimum Zoom"); + c.gridx = 4; + c.gridy = 2; + pane.add(min_zoom_label, c); + + min_zoom = new JComboBox(zooms); + min_zoom.setSelectedItem(zooms[10]); + min_zoom.setEditable(false); + c.gridx = 5; + c.gridy = 2; + pane.add(min_zoom, c); + + JLabel max_zoom_label = new JLabel("Maximum Zoom"); + c.gridx = 4; + c.gridy = 3; + pane.add(max_zoom_label, c); + + max_zoom = new JComboBox(zooms); + max_zoom.setSelectedItem(zooms[14]); + max_zoom.setEditable(false); + c.gridx = 5; + c.gridy = 3; + pane.add(max_zoom, c); + + JLabel radius_label = new JLabel("Tile Radius"); + c.gridx = 4; + c.gridy = 4; + pane.add(radius_label, c); + + radius = new JComboBox(radii); + radius.setSelectedItem(radii[4]); + radius.setEditable(true); + c.gridx = 5; + c.gridy = 4; + pane.add(radius, c); + + pack(); + setLocationRelativeTo(owner); + setVisible(true); + + System.out.printf("done creating preload ui\n"); + } +} diff --git a/altosuilib/AltosUIMapRectangle.java b/altosuilib/AltosUIMapRectangle.java new file mode 100644 index 00000000..8a5b16e1 --- /dev/null +++ b/altosuilib/AltosUIMapRectangle.java @@ -0,0 +1,45 @@ +/* + * Copyright © 2014 Keith Packard + * + * 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. + * + * 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package org.altusmetrum.altosuilib_2; + +public class AltosUIMapRectangle { + AltosUILatLon ul, lr; + + public AltosUIMapRectangle(AltosUILatLon a, AltosUILatLon b) { + double ul_lat, ul_lon; + double lr_lat, lr_lon; + + if (a.lat > b.lat) { + ul_lat = a.lat; + lr_lat = b.lat; + } else { + ul_lat = b.lat; + lr_lat = a.lat; + } + if (a.lon < b.lon) { + ul_lon = a.lon; + lr_lon = b.lon; + } else { + ul_lon = b.lon; + lr_lon = a.lon; + } + + ul = new AltosUILatLon(ul_lat, ul_lon); + lr = new AltosUILatLon(lr_lat, lr_lon); + } +} diff --git a/altosuilib/AltosUIMapStore.java b/altosuilib/AltosUIMapStore.java new file mode 100644 index 00000000..4cecb54f --- /dev/null +++ b/altosuilib/AltosUIMapStore.java @@ -0,0 +1,203 @@ +/* + * Copyright © 2014 Keith Packard + * + * 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. + * + * 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package org.altusmetrum.altosuilib_2; + +import java.io.*; +import java.net.*; +import java.util.*; + +public class AltosUIMapStore { + String url; + File file; + LinkedList listeners = new LinkedList(); + + static final int success = 0; + static final int loading = 1; + static final int failed = 2; + static final int bad_request = 3; + static final int forbidden = 4; + + int status; + + public int status() { + return status; + } + + public synchronized void add_listener(AltosUIMapStoreListener listener) { + if (!listeners.contains(listener)) + listeners.add(listener); + } + + public synchronized void remove_listener(AltosUIMapStoreListener listener) { + listeners.remove(listener); + } + + private synchronized void notify_listeners(int status) { + this.status = status; + for (AltosUIMapStoreListener listener : listeners) + listener.notify_store(this, status); + } + + static Object forbidden_lock = new Object(); + static long forbidden_time; + static boolean forbidden_set; + + private int fetch_url() { + URL u; + + try { + u = new URL(url); + } catch (java.net.MalformedURLException e) { + return bad_request; + } + + byte[] data; + URLConnection uc = null; + try { + uc = u.openConnection(); + String type = uc.getContentType(); + int contentLength = uc.getContentLength(); + if (uc instanceof HttpURLConnection) { + int response = ((HttpURLConnection) uc).getResponseCode(); + switch (response) { + case HttpURLConnection.HTTP_FORBIDDEN: + case HttpURLConnection.HTTP_PAYMENT_REQUIRED: + case HttpURLConnection.HTTP_UNAUTHORIZED: + synchronized (forbidden_lock) { + forbidden_time = System.nanoTime(); + forbidden_set = true; + return forbidden; + } + } + } + InputStream in = new BufferedInputStream(uc.getInputStream()); + int bytesRead = 0; + int offset = 0; + data = new byte[contentLength]; + while (offset < contentLength) { + bytesRead = in.read(data, offset, data.length - offset); + if (bytesRead == -1) + break; + offset += bytesRead; + } + in.close(); + + if (offset != contentLength) + return failed; + + } catch (IOException e) { + return failed; + } + + try { + FileOutputStream out = new FileOutputStream(file); + out.write(data); + out.flush(); + out.close(); + } catch (FileNotFoundException e) { + return bad_request; + } catch (IOException e) { + if (file.exists()) + file.delete(); + return bad_request; + } + return success; + } + + static Object fetch_lock = new Object(); + + static final long forbidden_interval = 60l * 1000l * 1000l * 1000l; + static final long google_maps_ratelimit_ms = 1200; + + class loader implements Runnable { + + public void run() { + if (file.exists()) { + notify_listeners(success); + return; + } + + synchronized(forbidden_lock) { + if (forbidden_set && (System.nanoTime() - forbidden_time) < forbidden_interval) { + notify_listeners(forbidden); + return; + } + } + + int new_status; + + if (!AltosUIVersion.has_google_maps_api_key()) { + synchronized (fetch_lock) { + long startTime = System.nanoTime(); + new_status = fetch_url(); + if (new_status == success) { + long duration_ms = (System.nanoTime() - startTime) / 1000000; + if (duration_ms < google_maps_ratelimit_ms) { + try { + Thread.sleep(google_maps_ratelimit_ms - duration_ms); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + } + } + } else { + new_status = fetch_url(); + } + notify_listeners(new_status); + } + } + + private void load() { + loader l = new loader(); + Thread lt = new Thread(l); + lt.start(); + } + + private AltosUIMapStore (String url, File file) { + this.url = url; + this.file = file; + + if (file.exists()) + status = success; + else { + status = loading; + load(); + } + } + + public boolean equals(AltosUIMapStore other) { + return url.equals(other.url); + } + + static HashMap stores = new HashMap(); + + public static AltosUIMapStore get(String url, File file) { + AltosUIMapStore store; + synchronized(stores) { + if (stores.containsKey(url)) { + store = stores.get(url); + } else { + store = new AltosUIMapStore(url, file); + stores.put(url, store); + } + } + return store; + } + +} diff --git a/altosuilib/AltosUIMapStoreListener.java b/altosuilib/AltosUIMapStoreListener.java new file mode 100644 index 00000000..91aff00c --- /dev/null +++ b/altosuilib/AltosUIMapStoreListener.java @@ -0,0 +1,22 @@ +/* + * Copyright © 2014 Keith Packard + * + * 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. + * + * 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package org.altusmetrum.altosuilib_2; + +public interface AltosUIMapStoreListener { + abstract void notify_store(AltosUIMapStore store, int status); +} diff --git a/altosuilib/AltosUIMapTile.java b/altosuilib/AltosUIMapTile.java new file mode 100644 index 00000000..6fbcdb4b --- /dev/null +++ b/altosuilib/AltosUIMapTile.java @@ -0,0 +1,190 @@ +/* + * Copyright © 2010 Anthony Towns + * + * 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. + * + * 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package org.altusmetrum.altosuilib_2; + +import java.awt.*; +import java.awt.image.*; +import javax.swing.*; +import javax.imageio.*; +import java.awt.geom.*; +import java.io.*; +import java.util.*; +import java.awt.RenderingHints.*; +import org.altusmetrum.altoslib_4.*; + +public class AltosUIMapTile { + AltosUIMapTileListener listener; + AltosUILatLon upper_left, center; + int px_size; + int zoom; + int maptype; + AltosUIMapStore store; + int status; + + private File map_file() { + double lat = center.lat; + double lon = center.lon; + char chlat = lat < 0 ? 'S' : 'N'; + char chlon = lon < 0 ? 'W' : 'E'; + + if (lat < 0) lat = -lat; + if (lon < 0) lon = -lon; + String maptype_string = String.format("%s-", AltosUIMap.maptype_names[maptype]); + String format_string; + if (maptype == AltosUIMap.maptype_hybrid || maptype == AltosUIMap.maptype_satellite || maptype == AltosUIMap.maptype_terrain) + format_string = "jpg"; + else + format_string = "png"; + return new File(AltosUIPreferences.mapdir(), + String.format("map-%c%.6f,%c%.6f-%s%d.%s", + chlat, lat, chlon, lon, maptype_string, zoom, format_string)); + } + + private String map_url() { + String format_string; + if (maptype == AltosUIMap.maptype_hybrid || maptype == AltosUIMap.maptype_satellite || maptype == AltosUIMap.maptype_terrain) + format_string = "jpg"; + else + format_string = "png32"; + + if (AltosUIVersion.has_google_maps_api_key()) + return String.format("http://maps.google.com/maps/api/staticmap?center=%.6f,%.6f&zoom=%d&size=%dx%d&sensor=false&maptype=%s&format=%s&key=%s", + center.lat, center.lon, zoom, px_size, px_size, AltosUIMap.maptype_names[maptype], format_string, AltosUIVersion.google_maps_api_key); + else + return String.format("http://maps.google.com/maps/api/staticmap?center=%.6f,%.6f&zoom=%d&size=%dx%d&sensor=false&maptype=%s&format=%s", + center.lat, center.lon, zoom, px_size, px_size, AltosUIMap.maptype_names[maptype], format_string); + } + private Font font = null; + + public void set_font(Font font) { + this.font = font; + } + + int painting_serial; + int painted_serial; + + Image image; + + public void paint_graphics(Graphics2D g2d, AltosUIMapTransform t, int serial) { + if (serial < painted_serial) + return; + + Point2D.Double point_double = t.screen(upper_left); + Point point = new Point((int) (point_double.x + 0.5), + (int) (point_double.y + 0.5)); + + painted_serial = serial; + + if (!g2d.hitClip(point.x, point.y, px_size, px_size)) + return; + + if (image != null) { + g2d.drawImage(image, point.x, point.y, null); + image = null; + } else { + g2d.setColor(Color.GRAY); + g2d.fillRect(point.x, point.y, px_size, px_size); + + if (t.has_location()) { + String message = null; + switch (status) { + case AltosUIMapCache.loading: + message = "Loading..."; + break; + case AltosUIMapCache.bad_request: + message = "Internal error"; + break; + case AltosUIMapCache.failed: + message = "Network error, check connection"; + break; + case AltosUIMapCache.forbidden: + message = "Too many requests, try later"; + break; + } + if (message != null && font != null) { + g2d.setFont(font); + g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + Rectangle2D bounds = font.getStringBounds(message, g2d.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; + g2d.setColor(Color.BLACK); + g2d.drawString(message, (float) point_double.x + x, (float) point_double.y + y); + } + } + } + } + + public void set_status(int status) { + this.status = status; + listener.notify_tile(this, status); + } + + public void notify_image(Image image) { + listener.notify_tile(this, status); + } + + public void paint(Graphics g, AltosUIMapTransform t) { + Graphics2D g2d = (Graphics2D) g; + boolean queued = false; + + Point2D.Double point = t.screen(upper_left); + + if (!g.hitClip((int) (point.x + 0.5), (int) (point.y + 0.5), px_size, px_size)) + return; + + ++painting_serial; + + if (image == null && t.has_location()) + image = AltosUIMapCache.get(this, store, px_size, px_size); + + paint_graphics(g2d, t, painting_serial); + } + + public int store_status() { + return store.status(); + } + + public void add_store_listener(AltosUIMapStoreListener listener) { + store.add_listener(listener); + } + + public void remove_store_listener(AltosUIMapStoreListener listener) { + store.remove_listener(listener); + } + + public AltosUIMapTile(AltosUIMapTileListener listener, AltosUILatLon upper_left, AltosUILatLon center, int zoom, int maptype, int px_size, Font font) { + this.listener = listener; + this.upper_left = upper_left; + + while (center.lon < -180.0) + center.lon += 360.0; + while (center.lon > 180.0) + center.lon -= 360.0; + + this.center = center; + this.zoom = zoom; + this.maptype = maptype; + this.px_size = px_size; + this.font = font; + status = AltosUIMapCache.loading; + store = AltosUIMapStore.get(map_url(), map_file()); + } +} diff --git a/altosuilib/AltosUIMapTileListener.java b/altosuilib/AltosUIMapTileListener.java new file mode 100644 index 00000000..4cc3ff2f --- /dev/null +++ b/altosuilib/AltosUIMapTileListener.java @@ -0,0 +1,22 @@ +/* + * Copyright © 2014 Keith Packard + * + * 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. + * + * 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package org.altusmetrum.altosuilib_2; + +public interface AltosUIMapTileListener { + abstract public void notify_tile(AltosUIMapTile tile, int status); +} diff --git a/altosuilib/AltosUIMapTransform.java b/altosuilib/AltosUIMapTransform.java new file mode 100644 index 00000000..e6f1ffe3 --- /dev/null +++ b/altosuilib/AltosUIMapTransform.java @@ -0,0 +1,106 @@ +/* + * Copyright © 2014 Keith Packard + * + * 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. + * + * 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package org.altusmetrum.altosuilib_2; + +import java.awt.*; +import java.awt.event.*; +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_4.*; + +public class AltosUIMapTransform { + + double scale_x, scale_y; + + double offset_x, offset_y; + + public AltosUILatLon lat_lon (Point2D.Double point) { + double lat, lon; + double rads; + + lon = point.x/scale_x; + rads = 2 * Math.atan(Math.exp(-point.y/scale_y)); + lat = Math.toDegrees(rads - Math.PI/2); + + return new AltosUILatLon(lat,lon); + } + + public Point2D.Double screen_point(Point screen) { + return new Point2D.Double(screen.x + offset_x, screen.y + offset_y); + } + + public AltosUILatLon screen_lat_lon(Point screen) { + return lat_lon(screen_point(screen)); + } + + public Point2D.Double point(AltosUILatLon lat_lon) { + double x, y; + double e; + + x = lat_lon.lon * scale_x; + + e = Math.sin(Math.toRadians(lat_lon.lat)); + e = Math.max(e,-(1-1.0E-15)); + e = Math.min(e, 1-1.0E-15 ); + + y = 0.5*Math.log((1+e)/(1-e))*-scale_y; + + return new Point2D.Double(x, y); + } + + public Point2D.Double screen(Point2D.Double point) { + return new Point2D.Double(point.x - offset_x, point.y - offset_y); + } + + public Point screen(Point point) { + return new Point((int) (point.x - offset_x + 0.5), + (int) (point.y - offset_y + 0.5)); + } + + public Rectangle screen(AltosUIMapRectangle map_rect) { + Point2D.Double ul = screen(map_rect.ul); + Point2D.Double lr = screen(map_rect.lr); + + return new Rectangle((int) ul.x, (int) ul.y, (int) (lr.x - ul.x), (int) (lr.y - ul.y)); + } + + public Point2D.Double screen(AltosUILatLon lat_lon) { + return screen(point(lat_lon)); + } + + private boolean has_location; + + public boolean has_location() { + return has_location; + } + + public AltosUIMapTransform(int width, int height, int zoom, AltosUILatLon centre_lat_lon) { + scale_x = 256/360.0 * Math.pow(2, zoom); + scale_y = 256/(2.0*Math.PI) * Math.pow(2, zoom); + + Point2D.Double centre_pt = point(centre_lat_lon); + + has_location = (centre_lat_lon.lat != 0 || centre_lat_lon.lon != 0); + offset_x = centre_pt.x - width / 2.0; + offset_y = centre_pt.y - height / 2.0; + } +} diff --git a/altosuilib/AltosUIMapView.java b/altosuilib/AltosUIMapView.java new file mode 100644 index 00000000..c558118b --- /dev/null +++ b/altosuilib/AltosUIMapView.java @@ -0,0 +1,462 @@ +/* + * Copyright © 2014 Keith Packard + * + * 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. + * + * 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package org.altusmetrum.altosuilib_2; + +import java.awt.*; +import java.awt.event.*; +import java.awt.image.*; +import javax.swing.*; +import java.io.*; +import java.lang.*; +import java.awt.geom.*; +import java.util.*; +import java.util.concurrent.*; +import org.altusmetrum.altoslib_4.*; + +public class AltosUIMapView extends Canvas implements MouseMotionListener, MouseListener, MouseWheelListener, ComponentListener, AltosUIMapTileListener, AltosUIMapStoreListener { + + AltosUIMapPath path = new AltosUIMapPath(); + + AltosUIMapLine line = new AltosUIMapLine(); + + LinkedList marks = new LinkedList(); + + LinkedList zoom_listeners = new LinkedList(); + + boolean have_boost = false; + boolean have_landed = false; + + ConcurrentHashMap tiles = new ConcurrentHashMap(); + + static final int default_zoom = 15; + static final int min_zoom = 3; + static final int max_zoom = 21; + static final int px_size = 512; + + int load_radius; + AltosUILatLon load_centre = null; + AltosUIMapTileListener load_listener; + + int zoom = default_zoom; + int maptype = AltosUIMap.maptype_default; + + long user_input_time; + + /* Milliseconds to wait after user action before auto-scrolling + */ + static final long auto_scroll_delay = 20 * 1000; + + AltosUIMapTransform transform; + AltosUILatLon centre; + + public void set_font() { + line.set_font(AltosUILib.value_font); + for (AltosUIMapTile tile : tiles.values()) + tile.set_font(AltosUILib.value_font); + } + + private boolean is_drag_event(MouseEvent e) { + return e.getModifiers() == InputEvent.BUTTON1_MASK; + } + + Point drag_start; + + private void drag(MouseEvent e) { + if (drag_start == null) + return; + + int dx = e.getPoint().x - drag_start.x; + int dy = e.getPoint().y - drag_start.y; + + AltosUILatLon new_centre = transform.screen_lat_lon(new Point(getWidth() / 2 - dx, getHeight() / 2 - dy)); + centre (new_centre.lat, new_centre.lon); + drag_start = e.getPoint(); + } + + private void drag_start(MouseEvent e) { + drag_start = e.getPoint(); + } + + private void notice_user_input() { + user_input_time = System.currentTimeMillis(); + } + + private boolean recent_user_input() { + return (System.currentTimeMillis() - user_input_time) < auto_scroll_delay; + } + + /* MouseMotionListener methods */ + + public void mouseDragged(MouseEvent e) { + notice_user_input(); + if (is_drag_event(e)) + drag(e); + else { + line.dragged(e, transform); + repaint(); + } + } + + 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) { + notice_user_input(); + if (is_drag_event(e)) + drag_start(e); + else + line.pressed(e, transform); + } + + public void mouseReleased(MouseEvent e) { + } + + /* MouseWheelListener methods */ + + public void mouseWheelMoved(MouseWheelEvent e) { + int zoom_change = e.getWheelRotation(); + + notice_user_input(); + AltosUILatLon mouse_lat_lon = transform.screen_lat_lon(e.getPoint()); + set_zoom(zoom() - zoom_change); + + Point2D.Double new_mouse = transform.screen(mouse_lat_lon); + + int dx = getWidth()/2 - e.getPoint().x; + int dy = getHeight()/2 - e.getPoint().y; + + AltosUILatLon new_centre = transform.screen_lat_lon(new Point((int) new_mouse.x + dx, (int) new_mouse.y + dy)); + + centre(new_centre.lat, new_centre.lon); + } + + /* ComponentListener methods */ + + public void componentHidden(ComponentEvent e) { + } + + public void componentMoved(ComponentEvent e) { + } + + public void componentResized(ComponentEvent e) { + set_transform(); + } + + public void componentShown(ComponentEvent e) { + set_transform(); + } + + public void repaint(Rectangle r, int pad) { + repaint(r.x - pad, r.y - pad, r.width + pad*2, r.height + pad*2); + } + + public void repaint(AltosUIMapRectangle rect, int pad) { + repaint (transform.screen(rect), pad); + } + + private boolean far_from_centre(AltosUILatLon lat_lon) { + + if (centre == null || transform == null) + return true; + + Point2D.Double screen = transform.screen(lat_lon); + + int width = getWidth(); + int dx = Math.abs ((int) screen.x - width/2); + + if (dx > width / 4) + return true; + + int height = getHeight(); + int dy = Math.abs ((int) screen.y - height/2); + + if (dy > height / 4) + return true; + + return false; + } + + public void show(AltosState state, AltosListenerState listener_state) { + + /* If insufficient gps data, nothing to update + */ + AltosGPS gps = state.gps; + + if (gps == null) + return; + + if (!gps.locked && gps.nsat < 4) + return; + + AltosUIMapRectangle damage = path.add(gps.lat, gps.lon, state.state); + + switch (state.state) { + case AltosLib.ao_flight_boost: + if (!have_boost) { + add_mark(gps.lat, gps.lon, state.state); + have_boost = true; + } + break; + case AltosLib.ao_flight_landed: + if (!have_landed) { + add_mark(gps.lat, gps.lon, state.state); + have_landed = true; + } + break; + } + + if (damage != null) + repaint(damage, AltosUIMapPath.stroke_width); + maybe_centre(gps.lat, gps.lon); + } + + private void set_transform() { + Rectangle bounds = getBounds(); + + transform = new AltosUIMapTransform(bounds.width, bounds.height, zoom, centre); + repaint(); + } + + public boolean set_zoom(int zoom) { + if (min_zoom <= zoom && zoom <= max_zoom && zoom != this.zoom) { + this.zoom = zoom; + tiles.clear(); + set_transform(); + + for (AltosUIMapZoomListener listener : zoom_listeners) + listener.zoom_changed(this.zoom); + + return true; + } + return false; + } + + public void add_zoom_listener(AltosUIMapZoomListener listener) { + if (!zoom_listeners.contains(listener)) + zoom_listeners.add(listener); + } + + public void remove_zoom_listener(AltosUIMapZoomListener listener) { + zoom_listeners.remove(listener); + } + + public void set_load_params(double lat, double lon, int radius, AltosUIMapTileListener listener) { + load_centre = new AltosUILatLon(lat, lon); + load_radius = radius; + load_listener = listener; + centre(lat, lon); + make_tiles(); + for (AltosUIMapTile tile : tiles.values()) { + tile.add_store_listener(this); + if (tile.store_status() != AltosUIMapStore.loading) + listener.notify_tile(tile, tile.store_status()); + } + repaint(); + } + + public boolean all_fetched() { + for (AltosUIMapTile tile : tiles.values()) { + if (tile.store_status() == AltosUIMapStore.loading) + return false; + } + return true; + } + + public boolean set_maptype(int maptype) { + if (maptype != this.maptype) { + this.maptype = maptype; + tiles.clear(); + repaint(); + return true; + } + return false; + } + + public int get_maptype() { + return maptype; + } + + public int zoom() { + return zoom; + } + + public void centre(AltosUILatLon lat_lon) { + centre = lat_lon; + set_transform(); + } + + public void centre(double lat, double lon) { + centre(new AltosUILatLon(lat, lon)); + } + + public void maybe_centre(double lat, double lon) { + AltosUILatLon lat_lon = new AltosUILatLon(lat, lon); + if (centre == null || (!recent_user_input() && far_from_centre(lat_lon))) + centre(lat_lon); + } + + private VolatileImage create_back_buffer() { + return getGraphicsConfiguration().createCompatibleVolatileImage(getWidth(), getHeight()); + } + + private Point floor(Point2D.Double point) { + return new Point ((int) Math.floor(point.x / px_size) * px_size, + (int) Math.floor(point.y / px_size) * px_size); + } + + private Point ceil(Point2D.Double point) { + return new Point ((int) Math.ceil(point.x / px_size) * px_size, + (int) Math.ceil(point.y / px_size) * px_size); + } + + private void make_tiles() { + Point upper_left; + Point lower_right; + + if (load_centre != null) { + Point centre = floor(transform.point(load_centre)); + + upper_left = new Point(centre.x - load_radius * px_size, + centre.y - load_radius * px_size); + lower_right = new Point(centre.x + load_radius * px_size, + centre.y + load_radius * px_size); + } else { + upper_left = floor(transform.screen_point(new Point(0, 0))); + lower_right = floor(transform.screen_point(new Point(getWidth(), getHeight()))); + } + LinkedList to_remove = new LinkedList(); + + for (Point point : tiles.keySet()) { + if (point.x < upper_left.x || lower_right.x < point.x || + point.y < upper_left.y || lower_right.y < point.y) { + to_remove.add(point); + } + } + + for (Point point : to_remove) + tiles.remove(point); + + AltosUIMapCache.set_cache_size(((lower_right.y - upper_left.y) / px_size + 1) * ((lower_right.x - upper_left.x) / px_size + 1)); + for (int y = upper_left.y; y <= lower_right.y; y += px_size) { + for (int x = upper_left.x; x <= lower_right.x; x += px_size) { + Point point = new Point(x, y); + + if (!tiles.containsKey(point)) { + AltosUILatLon ul = transform.lat_lon(new Point2D.Double(x, y)); + AltosUILatLon center = transform.lat_lon(new Point2D.Double(x + px_size/2, y + px_size/2)); + AltosUIMapTile tile = new AltosUIMapTile(this, ul, center, zoom, maptype, + px_size, AltosUILib.value_font); + tiles.put(point, tile); + } + } + } + } + + /* AltosUIMapTileListener methods */ + public void notify_tile(AltosUIMapTile tile, int status) { + for (Point point : tiles.keySet()) { + if (tile == tiles.get(point)) { + Point screen = transform.screen(point); + repaint(screen.x, screen.y, px_size, px_size); + } + } + } + + /* AltosUIMapStoreListener methods */ + public void notify_store(AltosUIMapStore store, int status) { + if (load_listener != null) { + for (AltosUIMapTile tile : tiles.values()) + if (store.equals(tile.store)) + load_listener.notify_tile(tile, status); + } + } + + private void do_paint(Graphics g) { + Graphics2D g2d = (Graphics2D) g; + + make_tiles(); + + for (AltosUIMapTile tile : tiles.values()) + tile.paint(g2d, transform); + + synchronized(marks) { + for (AltosUIMapMark mark : marks) + mark.paint(g2d, transform); + } + + path.paint(g2d, transform); + + line.paint(g2d, transform); + } + + public void paint(Graphics g) { + + VolatileImage back_buffer = create_back_buffer(); + 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(g.getClip()); + do_paint(g_back); + g_back.dispose(); + + g.drawImage(back_buffer, 0, 0, this); + } while (back_buffer.contentsLost()); + back_buffer.flush(); + } + + public void update(Graphics g) { + paint(g); + } + + public void add_mark(double lat, double lon, int state) { + synchronized(marks) { + marks.add(new AltosUIMapMark(lat, lon, state)); + } + repaint(); + } + + public void clear_marks() { + synchronized(marks) { + marks.clear(); + } + } + + public AltosUIMapView() { + centre(0, 0); + + addComponentListener(this); + addMouseMotionListener(this); + addMouseListener(this); + addMouseWheelListener(this); + set_font(); + } +} diff --git a/altosuilib/AltosUIMapZoomListener.java b/altosuilib/AltosUIMapZoomListener.java new file mode 100644 index 00000000..02e8bb51 --- /dev/null +++ b/altosuilib/AltosUIMapZoomListener.java @@ -0,0 +1,22 @@ +/* + * Copyright © 2014 Keith Packard + * + * 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. + * + * 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package org.altusmetrum.altosuilib_2; + +public interface AltosUIMapZoomListener { + abstract public void zoom_changed(int zoom); +} diff --git a/altosuilib/Makefile.am b/altosuilib/Makefile.am index b90669ce..466cfd99 100644 --- a/altosuilib/Makefile.am +++ b/altosuilib/Makefile.am @@ -33,11 +33,6 @@ altosuilib_JAVA = \ AltosUISeries.java \ AltosUIVersion.java \ AltosUSBDevice.java \ - AltosSiteMap.java \ - AltosSiteMapCache.java \ - AltosSiteMapPreload.java \ - AltosSiteMapTile.java \ - AltosSiteMapImage.java \ AltosVoice.java \ AltosDisplayThread.java \ AltosDeviceUIDialog.java \ @@ -65,8 +60,23 @@ altosuilib_JAVA = \ AltosBTDevice.java \ AltosBTDeviceIterator.java \ AltosBTManage.java \ - AltosBTKnown.java - + AltosBTKnown.java \ + AltosUIMap.java \ + AltosUIMapView.java \ + AltosUIMapLine.java \ + AltosUIMapMark.java \ + AltosUIMapPath.java \ + AltosUIMapTile.java \ + AltosUIMapCache.java \ + AltosUIMapImage.java \ + AltosUIMapTransform.java \ + AltosUIMapRectangle.java \ + AltosUIMapZoomListener.java \ + AltosUIMapTileListener.java \ + AltosUIMapPreload.java \ + AltosUIMapStore.java \ + AltosUIMapStoreListener.java \ + AltosUILatLon.java JAR=altosuilib_$(ALTOSUILIB_VERSION).jar diff --git a/telegps/TeleGPS.java b/telegps/TeleGPS.java index c61b245e..2503d53e 100644 --- a/telegps/TeleGPS.java +++ b/telegps/TeleGPS.java @@ -67,7 +67,7 @@ public class TeleGPS JTabbedPane pane; - AltosSiteMap sitemap; + AltosUIMap map; TeleGPSInfo gps_info; AltosInfoTable info_table; @@ -167,7 +167,7 @@ public class TeleGPS } void load_maps() { - new AltosSiteMapPreload(this); + new AltosUIMapPreload(this); } void disconnect() { @@ -438,9 +438,9 @@ public class TeleGPS c.gridwidth = 2; bag.add(pane, c); - sitemap = new AltosSiteMap(); - pane.add("Site Map", sitemap); - displays.add(sitemap); + map = new AltosUIMap(); + pane.add("Map", map); + displays.add(map); gps_info = new TeleGPSInfo(); pane.add("Info", gps_info); @@ -578,7 +578,7 @@ public class TeleGPS } else { double lat = Double.parseDouble(args[i+1]); double lon = Double.parseDouble(args[i+2]); - AltosSiteMap.prefetchMaps(lat, lon); + AltosUIMap.prefetch_maps(lat, lon); i += 2; } } else if (args[i].equals("--replay")) diff --git a/telegps/TeleGPSGraphUI.java b/telegps/TeleGPSGraphUI.java index b7fc4caa..fbc9657e 100644 --- a/telegps/TeleGPSGraphUI.java +++ b/telegps/TeleGPSGraphUI.java @@ -38,7 +38,7 @@ public class TeleGPSGraphUI extends AltosUIFrame JTabbedPane pane; AltosGraph graph; AltosUIEnable enable; - AltosSiteMap map; + AltosUIMap map; AltosState state; AltosFlightStats stats; AltosGraphDataSet graphDataSet; @@ -69,7 +69,7 @@ public class TeleGPSGraphUI extends AltosUIFrame graph = new AltosGraph(enable, stats, graphDataSet); statsTable = new AltosFlightStatsTable(stats); - map = new AltosSiteMap(); + map = new AltosUIMap(); pane.add("Flight Graph", graph.panel); pane.add("Configure Graph", enable);