altos/telefireone-v2.0: Remove build of ao_product.h from Makefile
[fw/altos] / altoslib / AltosMapStore.java
index c06384339eaf55f23580809d9528389597f86619..2f85fb8db49602828ead9ca853796f3fc9fd9a06 100644 (file)
@@ -3,7 +3,8 @@
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -15,7 +16,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_9;
+package org.altusmetrum.altoslib_13;
 
 import java.io.*;
 import java.net.*;
@@ -28,13 +29,65 @@ public class AltosMapStore {
 
        int                                     status;
 
-       public int status() {
+       private static File map_file(AltosLatLon center, int zoom, int maptype, int px_size, int scale) {
+               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.%s",
+                                             chlat, lat, chlon, lon, maptype_string, zoom, scale == 1 ? "" : String.format("-%d", scale), format_string));
+       }
+
+       public static String google_maps_api_key = null;
+
+       private static String google_map_url(AltosLatLon center, int zoom, int maptype, int px_size, int scale, String format_string) {
+               return String.format("http://maps.google.com/maps/api/staticmap?center=%.6f,%.6f&zoom=%d&size=%dx%d&scale=%d&sensor=false&maptype=%s&format=%s&key=%s",
+                                    center.lat, center.lon, zoom, px_size, px_size, scale,
+                                    AltosMap.maptype_names[maptype], format_string, google_maps_api_key);
+       }
+
+       private static String altos_map_url(AltosLatLon center, int zoom, int maptype, int px_size, int scale, String format_string) {
+               return String.format("https://maps.altusmetrum.org/cgi-bin/altos-map?lat=%.6f&lon=%.6f&zoom=%d",
+                                    center.lat, center.lon, zoom);
+       }
+
+       private static String map_url(AltosLatLon center, int zoom, int maptype, int px_size, int scale) {
+               String format_string;
+
+               if (maptype == AltosMap.maptype_hybrid || maptype == AltosMap.maptype_satellite || maptype == AltosMap.maptype_terrain)
+                       format_string = "jpg";
+               else
+                       format_string = "png32";
+
+               for (int s = 1; s < scale; s <<= 1)
+                       zoom--;
+
+               px_size /= scale;
+
+               if (google_maps_api_key != null)
+                       return google_map_url(center, zoom, maptype, px_size, scale, format_string);
+               else
+                       return altos_map_url(center, zoom, maptype, px_size, scale, format_string);
+       }
+
+       public synchronized int status() {
                return status;
        }
 
        public synchronized void add_listener(AltosMapStoreListener listener) {
                if (!listeners.contains(listener))
                        listeners.add(listener);
+               listener.notify_store(this, status);
        }
 
        public synchronized void remove_listener(AltosMapStoreListener listener) {
@@ -47,10 +100,6 @@ public class AltosMapStore {
                        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;
 
@@ -60,47 +109,64 @@ public class AltosMapStore {
                        return AltosMapTile.bad_request;
                }
 
-               byte[] data;
+               byte[] data = null;
                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;
+
+               int status = AltosMapTile.failed;
+               int tries = 0;
+
+               while (tries < 10 && status != AltosMapTile.fetched) {
+                       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:
                                                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();
+                               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;
+                               if (offset == contentLength)
+                                       status = AltosMapTile.fetched;
+                               else
+                                       status = AltosMapTile.failed;
 
-               } catch (IOException e) {
-                       return AltosMapTile.failed;
+                       } catch (IOException e) {
+                               status = AltosMapTile.failed;
+                       }
+
+                       if (status != AltosMapTile.fetched) {
+                               try {
+                                       Thread.sleep(100);
+                               } catch (InterruptedException ie) {
+                               }
+                               tries++;
+                               System.out.printf("Fetch failed, retrying %d\n", tries);
+                       }
                }
 
+               if (status != AltosMapTile.fetched)
+                       return status;
+
                try {
                        FileOutputStream out = new FileOutputStream(file);
-                       out.write(data);
+                       if (data != null)
+                               out.write(data);
                        out.flush();
                        out.close();
                } catch (FileNotFoundException e) {
@@ -110,93 +176,71 @@ public class AltosMapStore {
                                file.delete();
                        return AltosMapTile.bad_request;
                }
-               return AltosMapTile.success;
+               return AltosMapTile.fetched;
        }
 
        static Object   fetch_lock = new Object();
 
-       static final long       forbidden_interval = 60l * 1000l * 1000l * 1000l;
-       static final long       google_maps_ratelimit_ms = 1200;
-
-       static Object   loader_lock = new Object();
+       static Object   fetcher_lock = new Object();
 
        static LinkedList<AltosMapStore> waiting = new LinkedList<AltosMapStore>();
        static LinkedList<AltosMapStore> running = new LinkedList<AltosMapStore>();
 
-       static final int concurrent_loaders = 128;
+       static int concurrent_fetchers() {
+               if (google_maps_api_key == null)
+                       return 16;
+               return 128;
+       }
 
-       static void start_loaders() {
-               while (!waiting.isEmpty() && running.size() < concurrent_loaders) {
+       static void start_fetchers() {
+               while (!waiting.isEmpty() && running.size() < concurrent_fetchers()) {
                        AltosMapStore   s = waiting.remove();
                        running.add(s);
-                       Thread lt = s.make_loader_thread();
+                       Thread lt = s.make_fetcher_thread();
                        lt.start();
                }
        }
 
-       void finish_loader() {
-               synchronized(loader_lock) {
+       void finish_fetcher() {
+               synchronized(fetcher_lock) {
                        running.remove(this);
-                       start_loaders();
+                       start_fetchers();
                }
        }
 
-       void add_loader() {
-               synchronized(loader_lock) {
+       void add_fetcher() {
+               synchronized(fetcher_lock) {
                        waiting.add(this);
-                       start_loaders();
+                       start_fetchers();
                }
        }
 
-       class loader implements Runnable {
+       class fetcher implements Runnable {
 
                public void run() {
                        try {
                                if (file.exists()) {
-                                       notify_listeners(AltosMapTile.success);
+                                       notify_listeners(AltosMapTile.fetched);
                                        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();
-                               }
+                               new_status = fetch_url();
+
                                notify_listeners(new_status);
                        } finally {
-                               finish_loader();
+                               finish_fetcher();
                        }
                }
        }
 
-       private Thread make_loader_thread() {
-               return new Thread(new loader());
+       private Thread make_fetcher_thread() {
+               return new Thread(new fetcher());
        }
 
-       private void load() {
-               add_loader();
+       private void fetch() {
+               add_fetcher();
        }
 
        private AltosMapStore (String url, File file) {
@@ -204,10 +248,10 @@ public class AltosMapStore {
                this.file = file;
 
                if (file.exists())
-                       status = AltosMapTile.success;
+                       status = AltosMapTile.fetched;
                else {
-                       status = AltosMapTile.loading;
-                       load();
+                       status = AltosMapTile.fetching;
+                       fetch();
                }
        }
 
@@ -228,16 +272,19 @@ public class AltosMapStore {
 
        static HashMap<String,AltosMapStore> stores = new HashMap<String,AltosMapStore>();
 
-       public static AltosMapStore get(String url, File file) {
+       public static AltosMapStore get(AltosLatLon center, int zoom, int maptype, int px_size, int scale) {
+               String url = map_url(center, zoom, maptype, px_size, scale);
+
                AltosMapStore   store;
                synchronized(stores) {
                        if (stores.containsKey(url)) {
                                store = stores.get(url);
                        } else {
-                               store = new AltosMapStore(url, file);
+                               store = new AltosMapStore(url, map_file(center, zoom, maptype, px_size, scale));
                                stores.put(url, store);
                        }
                }
                return store;
        }
+
 }