Permalink
Cannot retrieve contributors at this time
Fetching contributors…
| /* | |
| * Copyright (C) 2014 Jonathan Hudson <jh+mwptools@daria.co.uk> | |
| * | |
| * 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. | |
| */ | |
| using Gtk; | |
| using Clutter; | |
| using Champlain; | |
| using GtkChamplain; | |
| public struct MapSource | |
| { | |
| string id; | |
| string name; | |
| int min_zoom; | |
| int max_zoom; | |
| int tile_size; | |
| string proj; | |
| string licence; | |
| string licence_uri; | |
| string uri_format; | |
| Champlain.MapSourceDesc desc; | |
| } | |
| public class MwpMapSource : Champlain.MapSourceDesc | |
| { | |
| public MwpMapSource (string id, | |
| string name, | |
| string license, | |
| string license_uri, | |
| int minzoom, | |
| int maxzoom, | |
| int tile_size, | |
| Champlain.MapProjection projection, | |
| string uri_format) | |
| { | |
| /* the 0.12 vapi appears not to support projection | |
| * as a property | |
| */ | |
| Object(id: id, name: name, license: license, license_uri: license_uri, | |
| min_zoom_level: minzoom, max_zoom_level: maxzoom, | |
| tile_size: tile_size, | |
| uri_format: uri_format, | |
| data: (void *)projection, | |
| constructor: (void*)my_construct); | |
| } | |
| static Champlain.MapSource my_construct (Champlain.MapSourceDesc d) | |
| { | |
| var renderer = new Champlain.ImageRenderer(); | |
| Champlain.MapProjection proj = (Champlain.MapProjection)d.get_data(); | |
| var source = new Champlain.NetworkTileSource.full( | |
| d.get_id(), | |
| d.get_name(), | |
| d.get_license(), | |
| d.get_license_uri(), | |
| d.get_min_zoom_level(), | |
| d.get_max_zoom_level(), | |
| d.get_tile_size(), | |
| proj, | |
| d.get_uri_format(), | |
| renderer); | |
| return source; | |
| } | |
| } | |
| public class SoupProxy : Soup.Server | |
| { | |
| private string basename; | |
| private string extname; | |
| public static Pid cpid = 0; | |
| public SoupProxy(string uri) | |
| { | |
| var parts = uri.split("#"); | |
| if(parts.length == 3 && parts[1] == "Q") | |
| { | |
| basename = parts[0]; | |
| extname = parts[2]; | |
| this.add_handler (null, default_handler); | |
| } | |
| else | |
| { | |
| MWPLog.message("Invalid quadkeys URI (%s)\n", uri); | |
| Posix.exit(255); | |
| } | |
| } | |
| ~SoupProxy() | |
| { | |
| } | |
| private string quadkey(int iz, int ix, int iy) | |
| { | |
| StringBuilder sb = new StringBuilder (); | |
| for (var i = iz - 1; i >= 0; i--) | |
| { | |
| char digit = '0'; | |
| if ((ix & (1 << i)) != 0) | |
| digit += 1; | |
| if ((iy & (1 << i)) != 0) | |
| digit += 2; | |
| sb.append_unichar(digit); | |
| } | |
| return sb.str; | |
| } | |
| private string rewrite_path(string p) | |
| { | |
| var parts = p.split("/"); | |
| var np = parts.length-3; | |
| var fn = parts[np+2].split("."); | |
| var iz = int.parse(parts[np]); | |
| var ix = int.parse(parts[np+1]); | |
| var iy = int.parse(fn[0]); | |
| var q = quadkey(iz, ix, iy); | |
| StringBuilder sb = new StringBuilder(); | |
| sb.append(basename); | |
| sb.append(q); | |
| sb.append(extname); | |
| return sb.str; | |
| } | |
| private void default_handler (Soup.Server server, | |
| Soup.Message msg, string path, | |
| GLib.HashTable? query, | |
| Soup.ClientContext client) | |
| { | |
| if (msg.method == "HEAD") | |
| { | |
| bool ok = false; | |
| Posix.Stat st; | |
| var parts = path.split("/"); | |
| var np = parts.length; | |
| var fnstr = GLib.Path.build_filename( | |
| Environment.get_home_dir(), | |
| ".cache/champlain", | |
| JsonMapDef.id, | |
| parts[np-3], | |
| parts[np-2], | |
| parts[np-1]); | |
| if(Posix.stat(fnstr, out st) == 0) | |
| { | |
| ok = true; | |
| var dt = new DateTime.from_unix_utc(st.st_mtime); | |
| var dstr = dt.format("%a, %d %b %Y %H:%M:%S %Z"); | |
| msg.response_headers.append("Content-Type","image/png"); | |
| msg.response_headers.append("Accept-Ranges", "bytes"); | |
| msg.response_headers.append("Last-Modified", dstr); | |
| msg.response_headers.append("Content-Length", | |
| st.st_size.to_string()); | |
| msg.set_status(200); | |
| } | |
| if(!ok) | |
| { | |
| msg.set_status(404); | |
| } | |
| } | |
| else if (msg.method == "GET") | |
| { | |
| var xpath = rewrite_path(path); | |
| var session = new Soup.Session (); | |
| var message = new Soup.Message ("GET", xpath); | |
| session.send_message (message); | |
| if(message.status_code == 200) | |
| { | |
| msg.set_response ("image/png", Soup.MemoryUse.COPY, | |
| message.response_body.data); | |
| } | |
| msg.set_status(message.status_code); | |
| } | |
| else | |
| { | |
| msg.set_status(404); | |
| } | |
| msg.response_headers.append("Server", "qk-proxy/1.0"); | |
| } | |
| } | |
| public class JsonMapDef : Object | |
| { | |
| private static Regex rx = null; | |
| public static int port = 0; | |
| public static string id = null; | |
| private static bool check_proxy(string s) | |
| { | |
| MatchInfo mi = null; | |
| bool found = false; | |
| if(rx == null) | |
| { | |
| try { | |
| rx = new Regex("localhost:(?<port>\\d+)\\/quadkey-proxy\\/"); | |
| } catch {} | |
| } | |
| if((port == 0) && rx.match(s,0,out mi)) | |
| { | |
| var p = mi.fetch_named("port"); | |
| if(p != null) | |
| { | |
| port = int.parse(p); | |
| found = true; | |
| } | |
| } | |
| return found; | |
| } | |
| public static MapSource[] read_json_sources(string fn) | |
| { | |
| MapSource[] sources = {}; | |
| try { | |
| var parser = new Json.Parser (); | |
| parser.load_from_file (fn); | |
| var root_object = parser.get_root ().get_object (); | |
| foreach (var node in | |
| root_object.get_array_member ("sources").get_elements ()) | |
| { | |
| var s = MapSource(); | |
| var item = node.get_object (); | |
| s.name = item.get_string_member ("name"); | |
| s.id = item.get_string_member ("id"); | |
| s.min_zoom = (int)item.get_int_member ("min_zoom"); | |
| s.max_zoom = (int) item.get_int_member ("max_zoom"); | |
| s.tile_size = (int)item.get_int_member("tile_size"); | |
| s.proj = item.get_string_member("projection"); | |
| s.uri_format = item.get_string_member("uri_format"); | |
| s.licence = item.get_string_member("license"); | |
| s.licence_uri = item.get_string_member("license_uri"); | |
| if (check_proxy(s.uri_format)) | |
| id = s.id; | |
| sources += s; | |
| } | |
| } | |
| catch (Error e) { | |
| MWPLog.message ("I guess something is not working...\n"); | |
| } | |
| return sources; | |
| } | |
| public static void run_proxy(string uri) | |
| { | |
| #if BADSOUP | |
| string[] spawn_args = {"qproxy", port.to_string(), uri}; | |
| try { | |
| Process.spawn_async ("/", | |
| spawn_args, | |
| null, | |
| SpawnFlags.SEARCH_PATH | SpawnFlags.STDERR_TO_DEV_NULL, | |
| null, | |
| out SoupProxy.cpid); | |
| MWPLog.message("Starting external proxy process\n"); | |
| } catch { | |
| MWPLog.message("Failed to start external proxy process\n"); | |
| } | |
| #else | |
| var pt = JsonMapDef.port; | |
| MWPLog.message("Starting proxy thread\n"); | |
| new Thread<int>("proxy",() => { | |
| var sp = new SoupProxy(uri); | |
| try { | |
| sp.listen_all(pt, 0); | |
| } catch { | |
| return 42; | |
| } | |
| return 0; | |
| }); | |
| #endif | |
| } | |
| } |