Initial Commit - Copy from Altus Metrum AltOS
This commit is contained in:
290
altoslib/AltosMapStore.java
Normal file
290
altoslib/AltosMapStore.java
Normal file
@@ -0,0 +1,290 @@
|
||||
/*
|
||||
* 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; 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
|
||||
* 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_14;
|
||||
|
||||
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;
|
||||
|
||||
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) {
|
||||
listeners.remove(listener);
|
||||
}
|
||||
|
||||
private synchronized void notify_listeners(int status) {
|
||||
this.status = status;
|
||||
for (AltosMapStoreListener listener : listeners)
|
||||
listener.notify_store(this, status);
|
||||
}
|
||||
|
||||
private int fetch_url() {
|
||||
URL u;
|
||||
|
||||
try {
|
||||
u = new URL(url);
|
||||
} catch (java.net.MalformedURLException e) {
|
||||
return AltosMapTile.bad_request;
|
||||
}
|
||||
|
||||
byte[] data = null;
|
||||
URLConnection uc = null;
|
||||
|
||||
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();
|
||||
|
||||
if (offset == contentLength)
|
||||
status = AltosMapTile.fetched;
|
||||
else
|
||||
status = 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);
|
||||
if (data != null)
|
||||
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.fetched;
|
||||
}
|
||||
|
||||
static Object fetch_lock = new Object();
|
||||
|
||||
static Object fetcher_lock = new Object();
|
||||
|
||||
static LinkedList<AltosMapStore> waiting = new LinkedList<AltosMapStore>();
|
||||
static LinkedList<AltosMapStore> running = new LinkedList<AltosMapStore>();
|
||||
|
||||
static int concurrent_fetchers() {
|
||||
if (google_maps_api_key == null)
|
||||
return 16;
|
||||
return 128;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
new_status = fetch_url();
|
||||
|
||||
notify_listeners(new_status);
|
||||
} finally {
|
||||
finish_fetcher();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Thread make_fetcher_thread() {
|
||||
return new Thread(new fetcher());
|
||||
}
|
||||
|
||||
private void fetch() {
|
||||
add_fetcher();
|
||||
}
|
||||
|
||||
private AltosMapStore (String url, File file) {
|
||||
this.url = url;
|
||||
this.file = file;
|
||||
|
||||
if (file.exists())
|
||||
status = AltosMapTile.fetched;
|
||||
else {
|
||||
status = AltosMapTile.fetching;
|
||||
fetch();
|
||||
}
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return url.hashCode();
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (o == null)
|
||||
return false;
|
||||
|
||||
if (!(o instanceof AltosMapStore))
|
||||
return false;
|
||||
|
||||
AltosMapStore other = (AltosMapStore) o;
|
||||
return url.equals(other.url);
|
||||
}
|
||||
|
||||
static HashMap<String,AltosMapStore> stores = new HashMap<String,AltosMapStore>();
|
||||
|
||||
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, map_file(center, zoom, maptype, px_size, scale));
|
||||
stores.put(url, store);
|
||||
}
|
||||
}
|
||||
return store;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user