From: Keith Packard Date: Sat, 6 Oct 2018 23:04:39 +0000 (-0700) Subject: map-server: Add maps proxy server X-Git-Tag: 1.8.7~3^2~31^2~4 X-Git-Url: https://git.gag.com/?p=fw%2Faltos;a=commitdiff_plain;h=3b817a2b854065af23c9ec8e849150e6930f51e9 map-server: Add maps proxy server This creates a map proxy server to handle the new Google Maps API requirements Signed-off-by: Keith Packard --- diff --git a/Makefile.am b/Makefile.am index 4f47417e..89fdd6c9 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS=ao-tools src doc icon altoslib libaltos altosuilib altosui micropeak ao-utils altosdroid telegps +SUBDIRS=ao-tools src doc icon altoslib libaltos altosuilib altosui micropeak ao-utils altosdroid telegps map-server EXTRA_DIST = ChangeLog diff --git a/altoslib/AltosMapStore.java b/altoslib/AltosMapStore.java index 7af439c4..b1cfcbd7 100644 --- a/altoslib/AltosMapStore.java +++ b/altoslib/AltosMapStore.java @@ -68,7 +68,7 @@ public class AltosMapStore { center.lat, center.lon, z, px_size/scale, px_size/scale, AltosMap.maptype_names[maptype], format_string); } - public int status() { + public synchronized int status() { return status; } diff --git a/configure.ac b/configure.ac index a14762b0..0a5a76ec 100644 --- a/configure.ac +++ b/configure.ac @@ -565,6 +565,9 @@ ao-tools/ao-usbtrng/Makefile ao-tools/ao-chaosread/Makefile ao-tools/ao-makebin/Makefile ao-utils/Makefile +map-server/Makefile +map-server/altos-mapd/Makefile +map-server/altos-map/Makefile src/Version ]) diff --git a/map-server/Makefile.am b/map-server/Makefile.am new file mode 100644 index 00000000..f9b8a727 --- /dev/null +++ b/map-server/Makefile.am @@ -0,0 +1 @@ +SUBDIRS=altos-mapd altos-map diff --git a/map-server/altos-map/.gitignore b/map-server/altos-map/.gitignore new file mode 100644 index 00000000..ea012eef --- /dev/null +++ b/map-server/altos-map/.gitignore @@ -0,0 +1,6 @@ +altos-map +altos-map-jdb +altos-map-test +*.jar +*.stamp +classes diff --git a/map-server/altos-map/AltosMap.java b/map-server/altos-map/AltosMap.java new file mode 100644 index 00000000..83bc7cea --- /dev/null +++ b/map-server/altos-map/AltosMap.java @@ -0,0 +1,156 @@ +/* + * Copyright © 2018 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, 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. + */ + +package altosmap; + +import java.net.*; +import java.io.*; +import java.util.*; +import java.text.*; + +import org.altusmetrum.altoslib_13.*; + +public class AltosMap { + + public final static int port = 16717; + + String query_string; + String remote_addr; + + public String reason_string(int code) { + switch (code) { + case 200: + return "OK"; + case 400: + return "Bad Request"; + case 403: + return "Forbidden"; + case 404: + return "Not Found"; + case 408: + return "Request Timeout"; + default: + return "Failure"; + } + } + + public void write_status(int status) { + System.out.printf("Status: %d %s\n", status, reason_string(status)); + } + + public void write_type(String type) { + System.out.printf("Content-Type: %s\n", type); + } + + public void fail(int status, String reason) { + write_status(status); + write_type("text/html"); + System.out.printf("%s\n", reason); + System.exit(1); + } + + public void process() { + query_string = System.getenv("QUERY_STRING"); + + if (query_string == null) + fail(400, "Missing query string"); + + remote_addr = System.getenv("REMOTE_ADDR"); + + if (remote_addr == null) + fail(400, "Missing remote address"); + + String[] queries = query_string.split("&"); + + double lon = AltosLib.MISSING; + double lat = AltosLib.MISSING; + int zoom = AltosLib.MISSING; + + try { + for (String query : queries) { + String[] q = query.split("="); + if (q.length >= 2) { + String name = q[0]; + String value = q[1]; + if (name.equals("lon")) + lon = AltosParse.parse_double_net(value); + else if (name.equals("lat")) + lat = AltosParse.parse_double_net(value); + else if (name.equals("zoom")) + zoom = AltosParse.parse_int(value); + else + fail(400, String.format("Extra query param \"%s\"", query)); + } + } + } catch (ParseException pe) { + fail(400, String.format("Invalid query: %s", pe.toString())); + } + + if (lon == AltosLib.MISSING) + fail(400, "Missing longitude"); + if (lat == AltosLib.MISSING) + fail(400, "Missing latitude"); + if (zoom == AltosLib.MISSING) + fail(400, "Missing zoom"); + + try { + Socket socket = new Socket(InetAddress.getLoopbackAddress(), port); + + AltosJson request = new AltosJson(); + + request.put("lat", lat); + request.put("lon", lon); + request.put("zoom", zoom); + request.put("remote_addr", remote_addr); + + Writer writer = new PrintWriter(socket.getOutputStream()); + request.write(writer); + writer.flush(); + + AltosJson reply = AltosJson.fromInputStream(socket.getInputStream()); + + int status = reply.get_int("status", 400); + + if (status != 200) + fail(status, "Bad cache status"); + + String filename = reply.get_string("filename", null); + try { + File file = new File(filename); + long length = file.length(); + FileInputStream in = new FileInputStream(file); + String content_type = reply.get_string("content_type", null); + System.out.printf("Content-Type: %s\n", content_type); + System.out.printf("Content-Length: %d\n", file.length()); + byte[] buf = new byte[4096]; + int bytes_read; + while ((bytes_read = in.read(buf)) > 0) + System.out.write(buf); + } catch (IOException ie) { + fail(404, String.format("IO Exception: %s", ie.toString())); + } + } catch (Exception e) { + fail(404, String.format("Exception %s", e.toString())); + } + } + + public AltosMap() { + } + + public static void main(final String[] args) { + + new AltosMap().process(); + + } +} diff --git a/map-server/altos-map/Makefile.am b/map-server/altos-map/Makefile.am new file mode 100644 index 00000000..efaae457 --- /dev/null +++ b/map-server/altos-map/Makefile.am @@ -0,0 +1,56 @@ +JAVAROOT=classes +AM_JAVACFLAGS=-target 1.6 -encoding UTF-8 -Xlint:deprecation -Xlint:unchecked -source 6 + +altoslibdir=$(libdir)/altos + +CLASSPATH_ENV=mkdir -p $(JAVAROOT); CLASSPATH="$(JAVAROOT):../../altoslib/*" + +bin_SCRIPTS=altos-map + +altosmap_JAVA = \ + AltosMap.java + +ALTOSLIB_CLASS=\ + altoslib_$(ALTOSLIB_VERSION).jar + +JAR=altosmap.jar + +FATJAR=altosmap-fat.jar + +all-local: classes/altosmap $(JAR) altos-map altos-map-test altos-map-jdb + +classes/altosmap: + mkdir -p classes/altosmap + +$(JAR): classaltosmap.stamp Manifest.txt $(ALTOSLIB_CLASS) + jar cfm $@ Manifest.txt \ + -C classes altosmap + +altosmapdir=$(datadir)/java + +$(FATJAR): classaltosmap.stamp Manifest-fat.txt $(ALTOSLIB_CLASS) + jar cfm $@ Manifest-fat.txt \ + -C classes altosmap + +altos-map: Makefile + echo "#!/bin/sh" > $@ + echo 'exec java -Djava.library.path="$(altoslibdir)" -jar "$(altosmapdir)/altosmap.jar" "$$@"' >> $@ + chmod +x $@ + +altos-map-test: Makefile + echo '#!/bin/sh' > $@ + echo 'dir="$$(dirname $$0)"' >> $@ + echo 'cd "$$dir"' >> $@ + echo 'altosmap="$$(pwd -P)"' >> $@ + echo 'exec java -jar "$$altosmap/altosmap.jar" "$$@"' >> $@ + chmod +x $@ + +altos-map-jdb: Makefile + echo "#!/bin/sh" > $@ + echo 'exec jdb altosmap/AltosMap "$$@"' >> $@ + chmod +x $@ + +$(ALTOSLIB_CLASS): + -rm -f "$@" + $(LN_S) ../../altoslib/"$@" . + diff --git a/map-server/altos-map/Manifest.txt b/map-server/altos-map/Manifest.txt new file mode 100644 index 00000000..1a285b40 --- /dev/null +++ b/map-server/altos-map/Manifest.txt @@ -0,0 +1,2 @@ +Main-Class: altosmap.AltosMap +Class-Path: altoslib_13.jar diff --git a/map-server/altos-map/altos-map-fake b/map-server/altos-map/altos-map-fake new file mode 100755 index 00000000..a78bbd64 --- /dev/null +++ b/map-server/altos-map/altos-map-fake @@ -0,0 +1,6 @@ +#!/bin/sh +# map-N43.799102,W120.586281-hybrid-20.jpg +export QUERY_STRING="lat=43.799102&lon=-120.586281&zoom=20" +export REMOTE_ADDR="127.0.0.1" +./altos-map-test + diff --git a/map-server/altos-mapd/.gitignore b/map-server/altos-mapd/.gitignore new file mode 100644 index 00000000..5f5ce0ae --- /dev/null +++ b/map-server/altos-mapd/.gitignore @@ -0,0 +1,6 @@ +*.stamp +*.jar +altos-mapd +altos-mapd-jdb +altos-mapd-test +classes diff --git a/map-server/altos-mapd/AltosMapd.java b/map-server/altos-mapd/AltosMapd.java new file mode 100644 index 00000000..cfa1ef35 --- /dev/null +++ b/map-server/altos-mapd/AltosMapd.java @@ -0,0 +1,50 @@ +/* + * Copyright © 2018 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, 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. + */ + +package altosmapd; + +import java.net.*; +import java.io.*; + +import org.altusmetrum.altoslib_13.*; + +public class AltosMapd { + + public final static int port = 16717; + + public final static int maptype = AltosMap.maptype_hybrid; + + public final static int px_size = 512; + + public final static int scale = 1; + + public static void main(final String[] args) { + + AltosMapdServer server = new AltosMapdServer(port); + + AltosPreferences.init(new AltosMapdPreferences()); + + AltosPreferences.mapdir = new File("/home/keithp/misc/rockets/flights/maps"); + + for (;;) { + Socket client = server.accept(); + if (client == null) { + System.out.printf("accept failed\n"); + continue; + } + System.out.printf("got client\n"); + new AltosMapdClient(client); + } + } +} diff --git a/map-server/altos-mapd/AltosMapdClient.java b/map-server/altos-mapd/AltosMapdClient.java new file mode 100644 index 00000000..fb0c08e6 --- /dev/null +++ b/map-server/altos-mapd/AltosMapdClient.java @@ -0,0 +1,148 @@ +/* + * Copyright © 2018 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, 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. + */ + +package altosmapd; + +import java.net.*; +import java.util.*; +import java.util.concurrent.*; +import java.io.*; + +import org.altusmetrum.altoslib_13.*; + +public class AltosMapdClient extends Thread implements AltosMapStoreListener { + private Socket socket; + private AltosJson request; + private AltosJson reply; + + private void set_status(int status) { + reply.put("status", status); + } + + private void set_filename(String filename) { + reply.put("filename", filename); + + } + + private void set_content_type(String content_type) { + reply.put("content_type", content_type); + } + + private String content_type(File file) { + String content_type = "application/octet-stream"; + String basename = file.getName(); + if (basename.endsWith(".jpg")) + content_type = "image/jpeg"; + if (basename.endsWith(".png")) + content_type = "image/png"; + return content_type; + } + + private void set_file(File file) { + set_filename(file.getAbsolutePath()); + set_content_type(content_type(file)); + } + + private Semaphore store_ready; + + public void notify_store(AltosMapStore map_store, int status) { + if (status != AltosMapTile.fetching) + store_ready.release(); + } + + public void run() { + reply = new AltosJson(); + try { + request = AltosJson.fromInputStream(socket.getInputStream()); + + double lat = request.get_double("lat", AltosLib.MISSING); + double lon = request.get_double("lon", AltosLib.MISSING); + int zoom = request.get_int("zoom", AltosLib.MISSING); + String addr = request.get_string("remote_addr", null); + + if (lat == AltosLib.MISSING || + lon == AltosLib.MISSING || + zoom == AltosLib.MISSING || + addr == null) + { + set_status(400); + } else { + store_ready = new Semaphore(0); + + System.out.printf("Fetching tile for %g %g %d\n", lat, lon, zoom); + + AltosMapStore map_store = AltosMapStore.get(new AltosLatLon(lat, lon), + zoom, + AltosMapd.maptype, + AltosMapd.px_size, + AltosMapd.scale); + int status; + + if (map_store == null) { + System.out.printf("no store?\n"); + status = AltosMapTile.failed; + } else { + map_store.add_listener(this); + + System.out.printf("Waiting for tile\n"); + + try { + store_ready.acquire(); + } catch (Exception ie) { + } + + status = map_store.status(); + } + + if (status == AltosMapTile.fetched || status == AltosMapTile.loaded) { + set_status(200); + set_file(map_store.file); + } else if (status == AltosMapTile.failed) { + set_status(404); + } else if (status == AltosMapTile.fetching) { + set_status(408); + } else if (status == AltosMapTile.bad_request) { + set_status(400); + } else if (status == AltosMapTile.forbidden) { + set_status(403); + } else { + set_status(400); + } + } + } catch (Exception e) { + System.out.printf("client exception %s\n", e.toString()); + e.printStackTrace(System.out); + set_status(400); + + } finally { + try { + Writer writer = new PrintWriter(socket.getOutputStream()); + reply.write(writer); + writer.write('\n'); + writer.flush(); + } catch (IOException ie) { + } + try { + socket.close(); + } catch (IOException ie) { + } + System.out.printf("client done\n"); + } + } + + public AltosMapdClient(Socket socket) { + this.socket = socket; + start(); + } +} diff --git a/map-server/altos-mapd/AltosMapdPreferences.java b/map-server/altos-mapd/AltosMapdPreferences.java new file mode 100644 index 00000000..fcfe3261 --- /dev/null +++ b/map-server/altos-mapd/AltosMapdPreferences.java @@ -0,0 +1,85 @@ +/* + * Copyright © 2018 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, 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. + */ + +package altosmapd; + +import java.io.*; + +import org.altusmetrum.altoslib_13.*; + +public class AltosMapdPreferences extends AltosPreferencesBackend { + + public String getString(String key, String def) { + return def; + } + public void putString(String key, String value) { + } + + public int getInt(String key, int def) { + return def; + } + + public void putInt(String key, int value) { + } + + public double getDouble(String key, double def) { + return def; + } + + public void putDouble(String key, double value) { + } + + public boolean getBoolean(String key, boolean def) { + return def; + } + + public void putBoolean(String key, boolean value) { + } + + public byte[] getBytes(String key, byte[] def) { + return def; + } + + public void putBytes(String key, byte[] value) { + } + + public boolean nodeExists(String key) { + return false; + } + + public AltosPreferencesBackend node(String key) { + return this; + } + + public String[] keys() { + return null; + } + + public void remove(String key) { + } + + public void flush() { + } + + public File homeDirectory() { + return new File ("."); + } + + public void debug(String format, Object ... arguments) { + System.out.printf(format, arguments); + } + + public AltosMapdPreferences() { + } +} diff --git a/map-server/altos-mapd/AltosMapdServer.java b/map-server/altos-mapd/AltosMapdServer.java new file mode 100644 index 00000000..68b427f0 --- /dev/null +++ b/map-server/altos-mapd/AltosMapdServer.java @@ -0,0 +1,37 @@ +/* + * Copyright © 2018 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, 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. + */ + +package altosmapd; + +import java.net.*; + +public class AltosMapdServer { + ServerSocket socket; + + public Socket accept() { + try { + return socket.accept(); + } catch (Exception e) { + return null; + } + } + + public AltosMapdServer(int port) { + try { + socket = new ServerSocket(port, 5, InetAddress.getLoopbackAddress()); + } catch (Exception e) { + socket = null; + } + } +} diff --git a/map-server/altos-mapd/Makefile.am b/map-server/altos-mapd/Makefile.am new file mode 100644 index 00000000..c099d1c7 --- /dev/null +++ b/map-server/altos-mapd/Makefile.am @@ -0,0 +1,59 @@ +JAVAROOT=classes +AM_JAVACFLAGS=-target 1.6 -encoding UTF-8 -Xlint:deprecation -Xlint:unchecked -source 6 + +altoslibdir=$(libdir)/altos + +CLASSPATH_ENV=mkdir -p $(JAVAROOT); CLASSPATH="$(JAVAROOT):../../altoslib/*" + +bin_SCRIPTS=altos-mapd + +altosmapd_JAVA = \ + AltosMapd.java \ + AltosMapdServer.java \ + AltosMapdClient.java \ + AltosMapdPreferences.java + +ALTOSLIB_CLASS=\ + altoslib_$(ALTOSLIB_VERSION).jar + +JAR=altosmapd.jar + +FATJAR=altosmapd-fat.jar + +all-local: classes/altosmapd $(JAR) altos-mapd altos-mapd-test altos-mapd-jdb + +classes/altosmapd: + mkdir -p classes/altosmapd + +$(JAR): classaltosmapd.stamp Manifest.txt $(ALTOSLIB_CLASS) + jar cfm $@ Manifest.txt \ + -C classes altosmapd + +altosmapddir=$(datadir)/java + +$(FATJAR): classaltosmapd.stamp Manifest-fat.txt $(ALTOSLIB_CLASS) + jar cfm $@ Manifest-fat.txt \ + -C classes altosmapd + +altos-mapd: Makefile + echo "#!/bin/sh" > $@ + echo 'exec java -Djava.library.path="$(altoslibdir)" -jar "$(altosmapddir)/altosmapd.jar" "$$@"' >> $@ + chmod +x $@ + +altos-mapd-test: Makefile + echo '#!/bin/sh' > $@ + echo 'dir="$$(dirname $$0)"' >> $@ + echo 'cd "$$dir"' >> $@ + echo 'altosmapd="$$(pwd -P)"' >> $@ + echo 'exec java -jar "$$altosmapd/altosmapd.jar" "$$@"' >> $@ + chmod +x $@ + +altos-mapd-jdb: Makefile + echo "#!/bin/sh" > $@ + echo 'exec jdb altosmapd/AltosMapd "$$@"' >> $@ + chmod +x $@ + +$(ALTOSLIB_CLASS): + -rm -f "$@" + $(LN_S) ../../altoslib/"$@" . + diff --git a/map-server/altos-mapd/Manifest.txt b/map-server/altos-mapd/Manifest.txt new file mode 100644 index 00000000..42c0313b --- /dev/null +++ b/map-server/altos-mapd/Manifest.txt @@ -0,0 +1,2 @@ +Main-Class: altosmapd.AltosMapd +Class-Path: altoslib_13.jar