map-server: Add maps proxy server
authorKeith Packard <keithp@keithp.com>
Sat, 6 Oct 2018 23:04:39 +0000 (16:04 -0700)
committerKeith Packard <keithp@keithp.com>
Sat, 6 Oct 2018 23:04:39 +0000 (16:04 -0700)
This creates a map proxy server to handle the new Google Maps API requirements

Signed-off-by: Keith Packard <keithp@keithp.com>
16 files changed:
Makefile.am
altoslib/AltosMapStore.java
configure.ac
map-server/Makefile.am [new file with mode: 0644]
map-server/altos-map/.gitignore [new file with mode: 0644]
map-server/altos-map/AltosMap.java [new file with mode: 0644]
map-server/altos-map/Makefile.am [new file with mode: 0644]
map-server/altos-map/Manifest.txt [new file with mode: 0644]
map-server/altos-map/altos-map-fake [new file with mode: 0755]
map-server/altos-mapd/.gitignore [new file with mode: 0644]
map-server/altos-mapd/AltosMapd.java [new file with mode: 0644]
map-server/altos-mapd/AltosMapdClient.java [new file with mode: 0644]
map-server/altos-mapd/AltosMapdPreferences.java [new file with mode: 0644]
map-server/altos-mapd/AltosMapdServer.java [new file with mode: 0644]
map-server/altos-mapd/Makefile.am [new file with mode: 0644]
map-server/altos-mapd/Manifest.txt [new file with mode: 0644]

index 4f47417eccf00c9bc5846d97a4847339ab366664..89fdd6c9ce8e9290aa320cd489971877a5610427 100644 (file)
@@ -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
 
index 7af439c41507f88ae6a13e6438bbe300fd01e68a..b1cfcbd79c7460badbd1ab33c89c24818ac9b0b3 100644 (file)
@@ -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;
        }
 
index a14762b00b2a57b05841cf262316db6b549d23ac..0a5a76ecd8a4fb8462274d8a93ebea5873f464f0 100644 (file)
@@ -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 (file)
index 0000000..f9b8a72
--- /dev/null
@@ -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 (file)
index 0000000..ea012ee
--- /dev/null
@@ -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 (file)
index 0000000..83bc7ce
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * Copyright © 2018 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.
+ */
+
+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("<body>%s</body>\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 (file)
index 0000000..efaae45
--- /dev/null
@@ -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 (file)
index 0000000..1a285b4
--- /dev/null
@@ -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 (executable)
index 0000000..a78bbd6
--- /dev/null
@@ -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 (file)
index 0000000..5f5ce0a
--- /dev/null
@@ -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 (file)
index 0000000..cfa1ef3
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright © 2018 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.
+ */
+
+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 (file)
index 0000000..fb0c08e
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * Copyright © 2018 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.
+ */
+
+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 (file)
index 0000000..fcfe326
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright © 2018 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.
+ */
+
+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 (file)
index 0000000..68b427f
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright © 2018 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.
+ */
+
+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 (file)
index 0000000..c099d1c
--- /dev/null
@@ -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 (file)
index 0000000..42c0313
--- /dev/null
@@ -0,0 +1,2 @@
+Main-Class: altosmapd.AltosMapd
+Class-Path: altoslib_13.jar