altoslib: Change map loading to lat=&lon= from center=
[fw/altos] / altoslib / AltosMapStore.java
index 88412593e31513f2389b11662e92d59d3bb732ac..7e0f3fb8a7cc2f246307f7ceda5bb33adf5f33ef 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_7;
+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) {
@@ -50,6 +103,7 @@ public class AltosMapStore {
        static Object   forbidden_lock = new Object();
        static long     forbidden_time;
        static boolean  forbidden_set;
+       public static int forbidden_response;
 
        private int fetch_url() {
                URL u;
@@ -75,6 +129,7 @@ public class AltosMapStore {
                                        synchronized (forbidden_lock) {
                                                forbidden_time = System.nanoTime();
                                                forbidden_set = true;
+                                               forbidden_response = response;
                                                return AltosMapTile.forbidden;
                                        }
                                }
@@ -110,7 +165,7 @@ public class AltosMapStore {
                                file.delete();
                        return AltosMapTile.bad_request;
                }
-               return AltosMapTile.success;
+               return AltosMapTile.fetched;
        }
 
        static Object   fetch_lock = new Object();
@@ -118,49 +173,69 @@ public class AltosMapStore {
        static final long       forbidden_interval = 60l * 1000l * 1000l * 1000l;
        static final long       google_maps_ratelimit_ms = 1200;
 
-       class loader implements Runnable {
+       static Object   fetcher_lock = new Object();
 
-               public void run() {
-                       if (file.exists()) {
-                               notify_listeners(AltosMapTile.success);
-                               return;
-                       }
+       static LinkedList<AltosMapStore> waiting = new LinkedList<AltosMapStore>();
+       static LinkedList<AltosMapStore> running = new LinkedList<AltosMapStore>();
+
+       static final int concurrent_fetchers = 128;
 
-                       synchronized(forbidden_lock) {
-                               if (forbidden_set && (System.nanoTime() - forbidden_time) < forbidden_interval) {
-                                       notify_listeners(AltosMapTile.forbidden);
+       static void start_fetchers() {
+               while (!waiting.isEmpty() && running.size() < concurrent_fetchers) {
+                       AltosMapStore   s = waiting.remove();
+                       running.add(s);
+                       Thread lt = s.make_fetcher_thread();
+                       lt.start();
+               }
+       }
+
+       void finish_fetcher() {
+               synchronized(fetcher_lock) {
+                       running.remove(this);
+                       start_fetchers();
+               }
+       }
+
+       void add_fetcher() {
+               synchronized(fetcher_lock) {
+                       waiting.add(this);
+                       start_fetchers();
+               }
+       }
+
+       class fetcher implements Runnable {
+
+               public void run() {
+                       try {
+                               if (file.exists()) {
+                                       notify_listeners(AltosMapTile.fetched);
                                        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();
-                                                       }
-                                               }
+                               synchronized(forbidden_lock) {
+                                       if (forbidden_set && (System.nanoTime() - forbidden_time) < forbidden_interval) {
+                                               notify_listeners(AltosMapTile.forbidden);
+                                               return;
                                        }
                                }
-                       } else {
+
+                               int new_status;
+
                                new_status = fetch_url();
+
+                               notify_listeners(new_status);
+                       } finally {
+                               finish_fetcher();
                        }
-                       notify_listeners(new_status);
                }
        }
 
-       private void load() {
-               loader  l = new loader();
-               Thread  lt = new Thread(l);
-               lt.start();
+       private Thread make_fetcher_thread() {
+               return new Thread(new fetcher());
+       }
+
+       private void fetch() {
+               add_fetcher();
        }
 
        private AltosMapStore (String url, File file) {
@@ -168,10 +243,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();
                }
        }
 
@@ -192,16 +267,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;
        }
+
 }