altoslib: Create display-independent map support code
authorKeith Packard <keithp@keithp.com>
Thu, 21 May 2015 20:49:28 +0000 (13:49 -0700)
committerKeith Packard <keithp@keithp.com>
Thu, 21 May 2015 20:49:28 +0000 (13:49 -0700)
This takes the swing-specific map code and creates a generic version.

Signed-off-by: Keith Packard <keithp@keithp.com>
24 files changed:
altoslib/.gitignore
altoslib/AltosFlightDisplay.java [new file with mode: 0644]
altoslib/AltosFontListener.java [new file with mode: 0644]
altoslib/AltosImage.java [new file with mode: 0644]
altoslib/AltosLatLon.java [new file with mode: 0644]
altoslib/AltosMap.java [new file with mode: 0644]
altoslib/AltosMapCache.java [new file with mode: 0644]
altoslib/AltosMapCacheListener.java [new file with mode: 0644]
altoslib/AltosMapInterface.java [new file with mode: 0644]
altoslib/AltosMapLine.java [new file with mode: 0644]
altoslib/AltosMapMark.java [new file with mode: 0644]
altoslib/AltosMapPath.java [new file with mode: 0644]
altoslib/AltosMapRectangle.java [new file with mode: 0644]
altoslib/AltosMapStore.java [new file with mode: 0644]
altoslib/AltosMapStoreListener.java [new file with mode: 0644]
altoslib/AltosMapTile.java [new file with mode: 0644]
altoslib/AltosMapTileListener.java [new file with mode: 0644]
altoslib/AltosMapTransform.java [new file with mode: 0644]
altoslib/AltosMapZoomListener.java [new file with mode: 0644]
altoslib/AltosPointDouble.java [new file with mode: 0644]
altoslib/AltosPointInt.java [new file with mode: 0644]
altoslib/AltosPreferences.java
altoslib/AltosVersion.java.in [new file with mode: 0644]
altoslib/Makefile.am

index ff0fd71..dc8b7e5 100644 (file)
@@ -1,3 +1,4 @@
 bin
 classaltoslib.stamp
 altoslib*.jar
+AltosVersion.java
diff --git a/altoslib/AltosFlightDisplay.java b/altoslib/AltosFlightDisplay.java
new file mode 100644 (file)
index 0000000..157a071
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright © 2010 Keith Packard <keithp@keithp.com>
+ *
+ * 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.altoslib_6;
+
+public interface AltosFlightDisplay extends AltosUnitsListener, AltosFontListener {
+       void reset();
+
+       void show(AltosState state, AltosListenerState listener_state);
+
+       String getName();
+}
diff --git a/altoslib/AltosFontListener.java b/altoslib/AltosFontListener.java
new file mode 100644 (file)
index 0000000..7498813
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * 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.altoslib_6;
+
+public interface AltosFontListener {
+       void font_size_changed(int font_size);
+}
diff --git a/altoslib/AltosImage.java b/altoslib/AltosImage.java
new file mode 100644 (file)
index 0000000..9a9b577
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * 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.altoslib_6;
+
+import java.io.*;
+
+public interface AltosImage {
+       /* Discard storage for image */
+       public abstract void flush();
+}
diff --git a/altoslib/AltosLatLon.java b/altoslib/AltosLatLon.java
new file mode 100644 (file)
index 0000000..f1ecfc9
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright © 2014 Keith Packard <keithp@keithp.com>
+ *
+ * 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.altoslib_6;
+
+public class AltosLatLon {
+       public double   lat;
+       public double   lon;
+
+       public boolean equals(AltosLatLon other) {
+               if (other == null)
+                       return false;
+               return lat == other.lat && lon == other.lon;
+       }
+
+       public AltosLatLon(double lat, double lon) {
+               this.lat = lat;
+               this.lon = lon;
+       }
+}
diff --git a/altoslib/AltosMap.java b/altoslib/AltosMap.java
new file mode 100644 (file)
index 0000000..b42f9f8
--- /dev/null
@@ -0,0 +1,355 @@
+/*
+ * Copyright © 2010 Anthony Towns <aj@erisian.com.au>
+ *
+ * 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.altoslib_6;
+
+import java.io.*;
+import java.lang.*;
+import java.util.*;
+import java.util.concurrent.*;
+
+public class AltosMap implements AltosFlightDisplay, AltosMapTileListener, AltosMapStoreListener {
+
+       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 int default_zoom = 15;
+       static final int min_zoom = 3;
+       static final int max_zoom = 21;
+
+       static final String[] maptype_names = {
+               "hybrid",
+               "roadmap",
+               "satellite",
+               "terrain"
+       };
+
+       public static final String[] maptype_labels = {
+               "Hybrid",
+               "Roadmap",
+               "Satellite",
+               "Terrain"
+       };
+
+       AltosMapInterface       map_interface;
+
+       AltosMapCache           cache;
+
+       LinkedList<AltosMapMark> marks = new LinkedList<AltosMapMark>();
+
+       LinkedList<AltosMapZoomListener> zoom_listeners = new LinkedList<AltosMapZoomListener>();
+
+       public void add_zoom_listener(AltosMapZoomListener listener) {
+               if (!zoom_listeners.contains(listener))
+                       zoom_listeners.add(listener);
+       }
+
+       public void remove_zoom_listener(AltosMapZoomListener listener) {
+               zoom_listeners.remove(listener);
+       }
+
+       boolean         have_boost = false;
+       boolean         have_landed = false;
+
+       ConcurrentHashMap<AltosPointInt,AltosMapTile> tiles = new ConcurrentHashMap<AltosPointInt,AltosMapTile>();
+       int             load_radius;
+       AltosLatLon     load_centre = null;
+       AltosMapTileListener    load_listener;
+
+       int             zoom = AltosMap.default_zoom;
+       int             maptype = AltosMap.maptype_default;
+
+       long            user_input_time;
+
+       /* Milliseconds to wait after user action before auto-scrolling
+        */
+       static final long auto_scroll_delay = 20 * 1000;
+
+       AltosMapTransform       transform;
+       AltosLatLon             centre;
+
+       public void reset() {
+               // nothing
+       }
+
+       /* MapInterface wrapping functions */
+       public void set_units() {
+               map_interface.set_units();
+       }
+
+       public void repaint(AltosMapRectangle damage, int pad) {
+               map_interface.repaint(damage, pad);
+       }
+
+       public void repaint(double x, double y, double w, double h) {
+               map_interface.repaint(x, y, w, h);
+       }
+
+       public void repaint() {
+               map_interface.repaint();
+       }
+
+       public int width() {
+               return map_interface.width();
+       }
+
+       public int height() {
+               return map_interface.height();
+       }
+
+       public AltosPointInt floor(AltosPointDouble point) {
+               return new AltosPointInt ((int) Math.floor(point.x / AltosMap.px_size) * AltosMap.px_size,
+                                             (int) Math.floor(point.y / AltosMap.px_size) * AltosMap.px_size);
+       }
+
+       public AltosPointInt ceil(AltosPointDouble point) {
+               return new AltosPointInt ((int) Math.ceil(point.x / AltosMap.px_size) * AltosMap.px_size,
+                                             (int) Math.ceil(point.y / AltosMap.px_size) * AltosMap.px_size);
+       }
+
+       public void notice_user_input() {
+               user_input_time = System.currentTimeMillis();
+       }
+
+       public boolean recent_user_input() {
+               return (System.currentTimeMillis() - user_input_time) < auto_scroll_delay;
+       }
+
+       public boolean far_from_centre(AltosLatLon lat_lon) {
+
+               if (centre == null || transform == null)
+                       return true;
+
+               AltosPointDouble        screen = transform.screen(lat_lon);
+
+               int             width = width();
+               int             dx = Math.abs ((int) (double) screen.x - width/2);
+
+               if (dx > width / 4)
+                       return true;
+
+               int             height = height();
+               int             dy = Math.abs ((int) (double) screen.y - height/2);
+
+               if (dy > height / 4)
+                       return true;
+
+               return false;
+       }
+
+       public void font_size_changed(int font_size) {
+               map_interface.line.font_size_changed(font_size);
+               for (AltosMapTile tile : tiles.values())
+                       tile.font_size_changed(font_size);
+               repaint();
+       }
+
+       public void units_changed(boolean imperial_units) {
+       }
+
+       private void set_transform() {
+               transform = new AltosMapTransform(width(), height(), zoom, centre);
+               repaint();
+       }
+
+       public boolean set_zoom(int zoom) {
+               if (AltosMap.min_zoom <= zoom && zoom <= AltosMap.max_zoom && zoom != this.zoom) {
+                       this.zoom = zoom;
+                       tiles.clear();
+                       set_transform();
+
+                       for (AltosMapZoomListener listener : zoom_listeners)
+                               listener.zoom_changed(this.zoom);
+
+                       return true;
+               }
+               return false;
+       }
+
+       public int get_zoom() {
+               return zoom;
+       }
+
+       public boolean set_maptype(int maptype) {
+               if (maptype != this.maptype) {
+                       this.maptype = maptype;
+                       tiles.clear();
+                       repaint();
+                       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;
+
+               AltosMapRectangle       damage = map_interface.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, AltosMapPath.stroke_width);
+               maybe_centre(gps.lat, gps.lon);
+       }
+
+       public void centre(AltosLatLon lat_lon) {
+               centre = lat_lon;
+               set_transform();
+       }
+
+       public void centre(double lat, double lon) {
+               centre(new AltosLatLon(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 maybe_centre(double lat, double lon) {
+               AltosLatLon     lat_lon = new AltosLatLon(lat, lon);
+               if (centre == null || (!recent_user_input() && far_from_centre(lat_lon)))
+                       centre(lat_lon);
+       }
+
+       public void add_mark(double lat, double lon, int state) {
+               synchronized(marks) {
+                       marks.add(map_interface.new_mark(lat, lon, state));
+               }
+               repaint();
+       }
+
+       public void clear_marks() {
+               synchronized(marks) {
+                       marks.clear();
+               }
+       }
+
+       private void make_tiles() {
+               AltosPointInt   upper_left;
+               AltosPointInt   lower_right;
+
+               if (load_centre != null) {
+                       AltosPointInt centre = floor(transform.point(load_centre));
+
+                       upper_left = new AltosPointInt(centre.x - load_radius * AltosMap.px_size,
+                                                              centre.y - load_radius * AltosMap.px_size);
+                       lower_right = new AltosPointInt(centre.x + load_radius * AltosMap.px_size,
+                                                               centre.y + load_radius * AltosMap.px_size);
+               } else {
+                       upper_left = floor(transform.screen_point(new AltosPointDouble(0.0, 0.0)));
+                       lower_right = floor(transform.screen_point(new AltosPointDouble(width(), height())));
+               }
+               LinkedList<AltosPointInt> to_remove = new LinkedList<AltosPointInt>();
+
+               for (AltosPointInt 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 (AltosPointInt point : to_remove)
+                       tiles.remove(point);
+
+               cache.set_cache_size((width() / AltosMap.px_size + 2) * (height() / AltosMap.px_size + 2));
+               for (int y = (int) upper_left.y; y <= lower_right.y; y += AltosMap.px_size) {
+                       for (int x = (int) upper_left.x; x <= lower_right.x; x += AltosMap.px_size) {
+                               AltosPointInt point = new AltosPointInt(x, y);
+
+                               if (!tiles.containsKey(point)) {
+                                       AltosLatLon     ul = transform.lat_lon(new AltosPointDouble(x, y));
+                                       AltosLatLon     center = transform.lat_lon(new AltosPointDouble(x + AltosMap.px_size/2, y + AltosMap.px_size/2));
+                                       AltosMapTile tile = new AltosMapTile(this, ul, center, zoom, maptype,
+                                                                            AltosMap.px_size);
+                                       tiles.put(point, tile);
+                               }
+                       }
+               }
+       }
+
+       public void set_load_params(double lat, double lon, int radius, AltosMapTileListener listener) {
+               load_centre = new AltosLatLon(lat, lon);
+               load_radius = radius;
+               load_listener = listener;
+               centre(lat, lon);
+               make_tiles();
+               for (AltosMapTile tile : tiles.values()) {
+                       tile.add_store_listener(this);
+                       if (tile.store_status() != AltosMapTile.loading)
+                               listener.notify_tile(tile, tile.store_status());
+               }
+               repaint();
+       }
+
+       public String getName() {
+               return "Map";
+       }
+
+       /* AltosMapTileListener methods */
+       public synchronized void notify_tile(AltosMapTile tile, int status) {
+               for (AltosPointInt point : tiles.keySet()) {
+                       if (tile == tiles.get(point)) {
+                               AltosPointInt   screen = transform.screen(point);
+                               repaint(screen.x, screen.y, AltosMap.px_size, AltosMap.px_size);
+                       }
+               }
+       }
+
+       /* AltosMapStoreListener methods */
+       public synchronized void notify_store(AltosMapStore store, int status) {
+               if (load_listener != null) {
+                       for (AltosMapTile tile : tiles.values())
+                               if (store.equals(tile.store))
+                                       load_listener.notify_tile(tile, status);
+               }
+       }
+
+       public AltosMap(AltosMapInterface map_interface) {
+               this.map_interface = map_interface;
+               cache = new AltosMapCache(map_interface);
+               centre(0, 0);
+       }
+}
diff --git a/altoslib/AltosMapCache.java b/altoslib/AltosMapCache.java
new file mode 100644 (file)
index 0000000..d94bae6
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+ * Copyright © 2010 Anthony Towns <aj@erisian.com.au>
+ *
+ * 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.altoslib_6;
+
+import java.io.*;
+import java.net.*;
+
+public class AltosMapCache implements AltosMapCacheListener {
+
+       /* An entry in the MapCache */
+       class MapCacheElement implements AltosMapStoreListener {
+
+               AltosMapTile            tile;           /* Notify when image has been loaded */
+               AltosImage              image;
+               AltosMapStore           store;
+               long                    used;
+
+               class loader implements Runnable {
+                       public void run() {
+                               if (image != null)
+                                       tile.notify_image(image);
+                               try {
+                                       image = map_interface.load_image(store.file);
+                               } catch (Exception ex) {
+                               }
+                               if (image == null)
+                                       tile.set_status(AltosMapTile.failed);
+                               else
+                                       tile.set_status(AltosMapTile.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() == AltosMapTile.success;
+               }
+
+               public synchronized void notify_store(AltosMapStore store, int status) {
+                       switch (status) {
+                       case AltosMapTile.loading:
+                               break;
+                       case AltosMapTile.success:
+                               load();
+                               break;
+                       default:
+                               tile.set_status(status);
+                               tile.notify_image(null);
+                       }
+               }
+
+               public MapCacheElement(AltosMapTile tile, AltosMapStore store) throws IOException {
+                       this.tile = tile;
+                       this.image = null;
+                       this.store = store;
+                       this.used = 0;
+
+                       int status = store.status();
+                       switch (status) {
+                       case AltosMapTile.loading:
+                               store.add_listener(this);
+                               break;
+                       case AltosMapTile.success:
+                               load();
+                               break;
+                       default:
+                               tile.set_status(status);
+                               tile.notify_image(null);
+                               break;
+                       }
+               }
+       }
+
+       int                     min_cache_size;         /* configured minimum cache size */
+       int                     cache_size;             /* current cache size */
+       int                     requested_cache_size;   /* cache size computed by application */
+
+       private Object          fetch_lock = new Object();
+       private Object          cache_lock = new Object();
+
+       AltosMapInterface       map_interface;
+
+       MapCacheElement[]       elements = new MapCacheElement[cache_size];
+
+       long                    used;
+
+       public void set_cache_size(int new_size) {
+
+               requested_cache_size = new_size;
+
+               if (new_size < min_cache_size)
+                       new_size = min_cache_size;
+
+               if (new_size == cache_size)
+                       return;
+
+               synchronized(cache_lock) {
+                       MapCacheElement[]       new_elements = new MapCacheElement[new_size];
+
+                       for (int i = 0; i < cache_size; i++) {
+                               if (i < new_size)
+                                       new_elements[i] = elements[i];
+                               else if (elements[i] != null)
+                                       elements[i].flush();
+                       }
+                       elements = new_elements;
+                       cache_size = new_size;
+               }
+       }
+
+       public AltosImage get(AltosMapTile tile, AltosMapStore store, int width, int height) {
+               int             oldest = -1;
+               long            age = used;
+
+               synchronized(cache_lock) {
+                       MapCacheElement element = null;
+                       for (int i = 0; i < cache_size; i++) {
+                               element = elements[i];
+
+                               if (element == null) {
+                                       oldest = i;
+                                       break;
+                               }
+                               if (store.equals(element.store)) {
+                                       element.used = used++;
+                                       return element.image;
+                               }
+                               if (element.used < age) {
+                                       oldest = i;
+                                       age = element.used;
+                               }
+                       }
+
+                       try {
+                               element = new MapCacheElement(tile, store);
+                               element.used = used++;
+                               if (elements[oldest] != null)
+                                       elements[oldest].flush();
+
+                               elements[oldest] = element;
+
+                               if (element.image == null)
+                                       tile.set_status(AltosMapTile.loading);
+                               else
+                                       tile.set_status(AltosMapTile.success);
+
+                               return element.image;
+                       } catch (IOException e) {
+                               tile.set_status(AltosMapTile.failed);
+                               return null;
+                       }
+               }
+       }
+
+       public void map_cache_changed(int map_cache) {
+               min_cache_size = map_cache;
+
+               set_cache_size(requested_cache_size);
+       }
+
+       public void dispose() {
+               AltosPreferences.unregister_map_cache_listener(this);
+
+               for (int i = 0; i < cache_size; i++) {
+                       MapCacheElement element = elements[i];
+
+                       if (element != null)
+                           element.flush();
+               }
+       }
+
+       public AltosMapCache(AltosMapInterface map_interface) {
+               this.map_interface = map_interface;
+               min_cache_size = AltosPreferences.map_cache();
+
+               set_cache_size(0);
+
+               AltosPreferences.register_map_cache_listener(this);
+       }
+}
diff --git a/altoslib/AltosMapCacheListener.java b/altoslib/AltosMapCacheListener.java
new file mode 100644 (file)
index 0000000..49b075d
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright © 2014 Keith Packard <keithp@keithp.com>
+ *
+ * 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.altoslib_6;
+
+public interface AltosMapCacheListener {
+       public void map_cache_changed(int map_cache);
+}
diff --git a/altoslib/AltosMapInterface.java b/altoslib/AltosMapInterface.java
new file mode 100644 (file)
index 0000000..ea5454d
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * 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.altoslib_6;
+
+import java.io.*;
+import java.net.*;
+
+public abstract class AltosMapInterface {
+
+       public AltosMapPath             path;
+       public AltosMapLine             line;
+
+       public abstract AltosImage load_image(File file) throws Exception;
+
+       public abstract AltosMapMark new_mark(double lat, double lon, int state);
+
+       public abstract int width();
+       public abstract int height();
+
+       public abstract void repaint();
+
+       public abstract void repaint(AltosMapRectangle damage, int pad);
+
+       public abstract void repaint(double x, double y, double w, double h);
+
+       public abstract void set_units();
+
+       public AltosMapInterface (AltosMapPath path, AltosMapLine line) {
+               this.path = path;
+               this.line = line;
+       }
+}
diff --git a/altoslib/AltosMapLine.java b/altoslib/AltosMapLine.java
new file mode 100644 (file)
index 0000000..83de47f
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright © 2014 Keith Packard <keithp@keithp.com>
+ *
+ * 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.altoslib_6;
+
+import java.io.*;
+import java.lang.Math;
+import java.util.*;
+import java.util.concurrent.*;
+
+public abstract class AltosMapLine implements AltosFontListener {
+       AltosLatLon     start, end;
+
+       static public int stroke_width = 6;
+
+       public abstract void font_size_changed(int font_size);
+
+       private AltosLatLon lat_lon(AltosPointDouble pt, AltosMapTransform t) {
+               return t.screen_lat_lon(pt);
+       }
+
+       public void dragged(AltosPointDouble pt, AltosMapTransform t) {
+               end = lat_lon(pt, t);
+       }
+
+       public void pressed(AltosPointDouble pt, AltosMapTransform t) {
+               start = lat_lon(pt, 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 abstract void paint(AltosMapTransform t);
+}
diff --git a/altoslib/AltosMapMark.java b/altoslib/AltosMapMark.java
new file mode 100644 (file)
index 0000000..9b77d53
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright © 2014 Keith Packard <keithp@keithp.com>
+ *
+ * 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.altoslib_6;
+
+import java.io.*;
+import java.lang.Math;
+import java.util.*;
+import java.util.concurrent.*;
+
+public abstract class AltosMapMark {
+
+       AltosLatLon     lat_lon;
+       int             state;
+
+       static public int stroke_width = 6;
+
+       public abstract void paint(AltosMapTransform t);
+
+       public AltosMapMark (double lat, double lon, int state) {
+               lat_lon = new AltosLatLon(lat, lon);
+               this.state = state;
+       }
+}
diff --git a/altoslib/AltosMapPath.java b/altoslib/AltosMapPath.java
new file mode 100644 (file)
index 0000000..272a431
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright © 2014 Keith Packard <keithp@keithp.com>
+ *
+ * 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.altoslib_6;
+
+import java.io.*;
+import java.lang.Math;
+import java.util.*;
+import java.util.concurrent.*;
+
+class PathPoint {
+       AltosLatLon     lat_lon;
+       int             state;
+
+       public PathPoint(AltosLatLon 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 abstract class AltosMapPath {
+
+       LinkedList<PathPoint>   points = new LinkedList<PathPoint>();
+       PathPoint               last_point = null;
+
+       static public int stroke_width = 6;
+
+       public abstract void paint(AltosMapTransform t);
+
+       public AltosMapRectangle add(double lat, double lon, int state) {
+               PathPoint               point = new PathPoint(new AltosLatLon (lat, lon), state);
+               AltosMapRectangle       rect = null;
+
+               if (!point.equals(last_point)) {
+                       if (last_point != null)
+                               rect = new AltosMapRectangle(last_point.lat_lon, point.lat_lon);
+                       points.add (point);
+                       last_point = point;
+               }
+               return rect;
+       }
+
+       public void clear () {
+               points = new LinkedList<PathPoint>();
+       }
+}
diff --git a/altoslib/AltosMapRectangle.java b/altoslib/AltosMapRectangle.java
new file mode 100644 (file)
index 0000000..f2f3010
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright © 2014 Keith Packard <keithp@keithp.com>
+ *
+ * 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.altoslib_6;
+
+public class AltosMapRectangle {
+       AltosLatLon     ul, lr;
+
+       public AltosMapRectangle(AltosLatLon a, AltosLatLon 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 AltosLatLon(ul_lat, ul_lon);
+               lr = new AltosLatLon(lr_lat, lr_lon);
+       }
+}
diff --git a/altoslib/AltosMapStore.java b/altoslib/AltosMapStore.java
new file mode 100644 (file)
index 0000000..2f52edb
--- /dev/null
@@ -0,0 +1,196 @@
+/*
+ * Copyright © 2014 Keith Packard <keithp@keithp.com>
+ *
+ * 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.altoslib_6;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+
+public class AltosMapStore {
+       String                                  url;
+       public File                             file;
+       LinkedList<AltosMapStoreListener>       listeners = new LinkedList<AltosMapStoreListener>();
+
+       int                                     status;
+
+       public int status() {
+               return status;
+       }
+
+       public synchronized void add_listener(AltosMapStoreListener listener) {
+               if (!listeners.contains(listener))
+                       listeners.add(listener);
+       }
+
+       public synchronized void remove_listener(AltosMapStoreListener listener) {
+               listeners.remove(listener);
+       }
+
+       private synchronized void notify_listeners(int status) {
+               this.status = status;
+               for (AltosMapStoreListener 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 AltosMapTile.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 AltosMapTile.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 AltosMapTile.failed;
+
+               } catch (IOException e) {
+                       return AltosMapTile.failed;
+               }
+
+               try {
+                       FileOutputStream out = new FileOutputStream(file);
+                       out.write(data);
+                       out.flush();
+                       out.close();
+               } catch (FileNotFoundException e) {
+                       return AltosMapTile.bad_request;
+               } catch (IOException e) {
+                       if (file.exists())
+                               file.delete();
+                       return AltosMapTile.bad_request;
+               }
+               return AltosMapTile.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(AltosMapTile.success);
+                               return;
+                       }
+
+                       synchronized(forbidden_lock) {
+                               if (forbidden_set && (System.nanoTime() - forbidden_time) < forbidden_interval) {
+                                       notify_listeners(AltosMapTile.forbidden);
+                                       return;
+                               }
+                       }
+
+                       int new_status;
+
+                       if (!AltosVersion.has_google_maps_api_key()) {
+                               synchronized (fetch_lock) {
+                                       long startTime = System.nanoTime();
+                                       new_status = fetch_url();
+                                       if (new_status == AltosMapTile.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 AltosMapStore (String url, File file) {
+               this.url = url;
+               this.file = file;
+
+               if (file.exists())
+                       status = AltosMapTile.success;
+               else {
+                       status = AltosMapTile.loading;
+                       load();
+               }
+       }
+
+       public boolean equals(AltosMapStore other) {
+               return url.equals(other.url);
+       }
+
+       static HashMap<String,AltosMapStore> stores = new HashMap<String,AltosMapStore>();
+
+       public static AltosMapStore get(String url, File file) {
+               AltosMapStore   store;
+               synchronized(stores) {
+                       if (stores.containsKey(url)) {
+                               store = stores.get(url);
+                       } else {
+                               store = new AltosMapStore(url, file);
+                               stores.put(url, store);
+                       }
+               }
+               return store;
+       }
+}
diff --git a/altoslib/AltosMapStoreListener.java b/altoslib/AltosMapStoreListener.java
new file mode 100644 (file)
index 0000000..a272e86
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright © 2014 Keith Packard <keithp@keithp.com>
+ *
+ * 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.altoslib_6;
+
+public interface AltosMapStoreListener {
+       abstract void notify_store(AltosMapStore store, int status);
+}
diff --git a/altoslib/AltosMapTile.java b/altoslib/AltosMapTile.java
new file mode 100644 (file)
index 0000000..b5dee7a
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * Copyright © 2010 Anthony Towns <aj@erisian.com.au>
+ *
+ * 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.altoslib_6;
+
+import java.io.*;
+import java.util.*;
+
+public class AltosMapTile implements AltosFontListener {
+       AltosMapTileListener    listener;
+       AltosLatLon     upper_left, center;
+       int             px_size;
+       int             zoom;
+       int             maptype;
+       AltosMapStore   store;
+       AltosMapCache   cache;
+       int             status;
+
+       static public final int success = 0;
+       static public final int loading = 1;
+       static public final int failed = 2;
+       static public final int bad_request = 3;
+       static public final int forbidden = 4;
+
+       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-", AltosMap.maptype_names[maptype]);
+               String format_string;
+               if (maptype == AltosMap.maptype_hybrid || maptype == AltosMap.maptype_satellite || maptype == AltosMap.maptype_terrain)
+                       format_string = "jpg";
+               else
+                       format_string = "png";
+               return new File(AltosPreferences.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 == AltosMap.maptype_hybrid || maptype == AltosMap.maptype_satellite || maptype == AltosMap.maptype_terrain)
+                       format_string = "jpg";
+               else
+                       format_string = "png32";
+
+               if (AltosVersion.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, AltosMap.maptype_names[maptype], format_string, AltosVersion.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, AltosMap.maptype_names[maptype], format_string);
+       }
+
+       public void font_size_changed(int font_size) {
+       }
+
+       public void set_status(int status) {
+               this.status = status;
+               listener.notify_tile(this, status);
+       }
+
+       public void notify_image(AltosImage image) {
+               listener.notify_tile(this, status);
+       }
+
+       public int store_status() {
+               return store.status();
+       }
+
+       public void add_store_listener(AltosMapStoreListener listener) {
+               store.add_listener(listener);
+       }
+
+       public void remove_store_listener(AltosMapStoreListener listener) {
+               store.remove_listener(listener);
+       }
+
+       public AltosMapTile(AltosMapTileListener listener, AltosLatLon upper_left, AltosLatLon center, int zoom, int maptype, int px_size) {
+               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;
+
+               status = AltosMapTile.loading;
+               store = AltosMapStore.get(map_url(), map_file());
+       }
+}
diff --git a/altoslib/AltosMapTileListener.java b/altoslib/AltosMapTileListener.java
new file mode 100644 (file)
index 0000000..8abc8b5
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright © 2014 Keith Packard <keithp@keithp.com>
+ *
+ * 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.altoslib_6;
+
+public interface AltosMapTileListener {
+       abstract public void notify_tile(AltosMapTile tile, int status);
+}
diff --git a/altoslib/AltosMapTransform.java b/altoslib/AltosMapTransform.java
new file mode 100644 (file)
index 0000000..f5fc5eb
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * Copyright © 2014 Keith Packard <keithp@keithp.com>
+ *
+ * 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.altoslib_6;
+
+import java.io.*;
+import java.lang.Math;
+import java.util.*;
+import java.util.concurrent.*;
+
+public class AltosMapTransform {
+
+       double  scale_x, scale_y;
+
+       double  offset_x, offset_y;
+
+       public AltosLatLon lat_lon (AltosPointDouble 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 AltosLatLon(lat,lon);
+       }
+
+       public AltosPointDouble screen_point(AltosPointDouble screen) {
+               return new AltosPointDouble(screen.x + offset_x, screen.y + offset_y);
+       }
+
+       public AltosLatLon screen_lat_lon(AltosPointDouble screen) {
+               return lat_lon(screen_point(screen));
+       }
+
+       public AltosPointDouble point(AltosLatLon 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 AltosPointDouble(x, y);
+       }
+
+       public AltosPointDouble screen(AltosPointDouble point) {
+               return new AltosPointDouble(point.x - offset_x, point.y - offset_y);
+       }
+
+       public AltosPointInt screen(AltosPointInt point) {
+               return new AltosPointInt((int) (point.x - offset_x + 0.5),
+                                        (int) (point.y - offset_y + 0.5));
+       }
+
+//     public Rectangle screen(AltosMapRectangle map_rect) {
+//             AltosPoint2D    ul = screen(map_rect.ul);
+//             AltosPoint2D    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 AltosPointDouble screen(AltosLatLon lat_lon) {
+               return screen(point(lat_lon));
+       }
+
+       private boolean has_location;
+
+       public boolean has_location() {
+               return has_location;
+       }
+
+       public AltosMapTransform(int width, int height, int zoom, AltosLatLon centre_lat_lon) {
+               scale_x = 256/360.0 * Math.pow(2, zoom);
+               scale_y = 256/(2.0*Math.PI) * Math.pow(2, zoom);
+
+               AltosPointDouble 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/altoslib/AltosMapZoomListener.java b/altoslib/AltosMapZoomListener.java
new file mode 100644 (file)
index 0000000..34edb19
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright © 2014 Keith Packard <keithp@keithp.com>
+ *
+ * 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.altoslib_6;
+
+public interface AltosMapZoomListener {
+       abstract public void zoom_changed(int zoom);
+}
diff --git a/altoslib/AltosPointDouble.java b/altoslib/AltosPointDouble.java
new file mode 100644 (file)
index 0000000..96fb916
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * 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.altoslib_6;
+
+public class AltosPointDouble {
+       public double   x, y;
+
+       public boolean equals(AltosPointDouble n) {
+               return n.x == x && n.y == y;
+       }
+
+       public AltosPointDouble(double x, double y) {
+               this.x = x;
+               this.y = y;
+       }
+
+       public AltosPointDouble(int x, int y) {
+               this.x = x;
+               this.y = y;
+       }
+
+       public AltosPointDouble(AltosPointInt p) {
+               this.x = p.x;
+               this.y = p.y;
+       }
+}
diff --git a/altoslib/AltosPointInt.java b/altoslib/AltosPointInt.java
new file mode 100644 (file)
index 0000000..6ee077a
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * 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.altoslib_6;
+
+public class AltosPointInt {
+       public int      x, y;
+
+       public boolean equals(AltosPointInt n) {
+               return n.x == x && n.y == y;
+       }
+
+       public AltosPointInt(int x, int y) {
+               this.x = x;
+               this.y = y;
+       }
+}
index 5aa45d3..9c4cadf 100644 (file)
@@ -118,6 +118,13 @@ public class AltosPreferences {
 
        public final static String      unitsPreference = "IMPERIAL-UNITS";
 
+       /* Maps cache size preference name */
+       final static String mapCachePreference = "MAP-CACHE";
+
+       static LinkedList<AltosMapCacheListener> map_cache_listeners;
+
+       public static int map_cache = 9;
+
        public static AltosFrequency[] load_common_frequencies() {
                AltosFrequency[] frequencies = null;
                boolean existing = false;
@@ -208,6 +215,9 @@ public class AltosPreferences {
                common_frequencies = load_common_frequencies();
 
                AltosConvert.imperial_units = backend.getBoolean(unitsPreference, false);
+
+               map_cache = backend.getInt(mapCachePreference, 9);
+               map_cache_listeners = new LinkedList<AltosMapCacheListener>();
        }
 
        public static void flush_preferences() {
@@ -548,4 +558,33 @@ public class AltosPreferences {
                        units_listeners.remove(l);
                }
        }
+
+
+       public static void register_map_cache_listener(AltosMapCacheListener l) {
+               synchronized(backend) {
+                       map_cache_listeners.add(l);
+               }
+       }
+
+       public static void unregister_map_cache_listener(AltosMapCacheListener l) {
+               synchronized (backend) {
+                       map_cache_listeners.remove(l);
+               }
+       }
+
+       public static void set_map_cache(int new_map_cache) {
+               synchronized(backend) {
+                       map_cache = new_map_cache;
+                       backend.putInt(mapCachePreference, map_cache);
+                       flush_preferences();
+                       for (AltosMapCacheListener l: map_cache_listeners)
+                               l.map_cache_changed(map_cache);
+               }
+       }
+
+       public static int map_cache() {
+               synchronized(backend) {
+                       return map_cache;
+               }
+       }
 }
diff --git a/altoslib/AltosVersion.java.in b/altoslib/AltosVersion.java.in
new file mode 100644 (file)
index 0000000..6bfef18
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * 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.altoslib_6;
+
+public class AltosVersion {
+       public final static String version = "@VERSION@";
+
+       public final static String google_maps_api_key = @GOOGLEKEY@;
+
+       static boolean has_google_maps_api_key() {
+               return google_maps_api_key != null && google_maps_api_key.length() > 1;
+       }
+}
index c640c69..0ee7b93 100644 (file)
@@ -128,7 +128,28 @@ altoslib_JAVA = \
        AltosPyro.java \
        AltosWriter.java \
        AltosQuaternion.java \
-       AltosRotation.java
+       AltosRotation.java \
+       AltosImage.java \
+       AltosLatLon.java \
+       AltosMap.java \
+       AltosMapCache.java \
+       AltosMapCacheListener.java \
+       AltosMapInterface.java \
+       AltosMapLine.java \
+       AltosMapMark.java \
+       AltosMapPath.java \
+       AltosMapRectangle.java \
+       AltosMapStore.java \
+       AltosMapStoreListener.java \
+       AltosMapTile.java \
+       AltosMapTileListener.java \
+       AltosMapTransform.java \
+       AltosMapZoomListener.java \
+       AltosPointDouble.java \
+       AltosPointInt.java \
+       AltosFlightDisplay.java \
+       AltosFontListener.java \
+       AltosVersion.java
 
 JAR=altoslib_$(ALTOSLIB_VERSION).jar