altos-mapd: Check nearest portion of tile to launch site
[fw/altos] / map-server / altos-mapd / AltosMapd.java
1 /*
2  * Copyright © 2018 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  */
14
15 package altosmapd;
16
17 import java.net.*;
18 import java.io.*;
19 import java.text.*;
20 import java.util.*;
21 import java.util.concurrent.*;
22
23 import org.altusmetrum.altoslib_13.*;
24
25 public class AltosMapd implements AltosLaunchSiteListener {
26
27         public static int port = 16717;
28
29         public final static int maptype = AltosMap.maptype_hybrid;
30
31         public final static int px_size = 512;
32
33         public final static int scale = 1;
34
35         public static double valid_radius = 17000;      /* 17km */
36
37         public String map_dir = null;
38         public String launch_sites_file = null;
39         public String key_file = null;
40
41         public void usage() {
42                 System.out.printf("usage: altos-mapd [--mapdir <map-directory] [--launch-sites <launch-sites-file>]\n" +
43                                   "                  [--radius <valid-radius-m> [--port <port>] [--key <key-file>]\n");
44                 System.exit(1);
45         }
46
47         private static Semaphore launch_sites_ready;
48
49         private static List<AltosLaunchSite> launch_sites;
50
51         public void notify_launch_sites(List<AltosLaunchSite> sites) {
52                 synchronized (launch_sites_ready) {
53                         if (sites != null) {
54                                 launch_sites = sites;
55                                 launch_sites_ready.release();
56                         }
57                 }
58         }
59
60         private static boolean west_of(double a, double b) {
61                 double  diff = b - a;
62
63                 while (diff >= 360.0)
64                         diff -= 360.0;
65                 while (diff <= -360.0)
66                         diff += 360.0;
67
68                 return diff >= 0;
69         }
70
71         public static boolean check_lat_lon(double lat, double lon, int zoom) {
72                 AltosMapTransform       transform = new AltosMapTransform(px_size, px_size, zoom, new AltosLatLon(lat, lon));
73
74                 AltosLatLon             upper_left = transform.screen_lat_lon(new AltosPointInt(0, 0));
75                 AltosLatLon             lower_right = transform.screen_lat_lon(new AltosPointInt(px_size, px_size));
76
77                 synchronized (launch_sites_ready) {
78                         if (launch_sites == null) {
79                                 try {
80                                         launch_sites_ready.acquire();
81                                 } catch (InterruptedException ie) {
82                                         return false;
83                                 }
84                         }
85                 }
86                 if (launch_sites == null) {
87                         System.out.printf("No launch site data available, refusing all requests\n");
88                         return false;
89                 }
90
91                 for (AltosLaunchSite site : launch_sites) {
92
93                         /* Figure out which point in the tile to
94                          * measure to the site That's one of the edges
95                          * or the site location depend on where the
96                          * site is in relation to the tile
97                          */
98
99                         double          check_lon;
100
101                         if (west_of(site.longitude, upper_left.lon))
102                                 check_lon = upper_left.lon;
103                         else if (west_of(lower_right.lon, site.longitude))
104                                 check_lon = lower_right.lon;
105                         else
106                                 check_lon = site.longitude;
107
108                         double          check_lat;
109
110                         if (site.latitude < lower_right.lat)
111                                 check_lat = lower_right.lat;
112                         else if (upper_left.lat < site.latitude)
113                                 check_lat = upper_left.lat;
114                         else
115                                 check_lat = site.latitude;
116
117                         AltosGreatCircle gc = new AltosGreatCircle(site.latitude, site.longitude,
118                                                                    check_lat, check_lon);
119
120                         if (gc.distance <= valid_radius)
121                                 return true;
122                 }
123
124                 return false;
125         }
126
127         AltosMapdServer server;
128
129         public void process(String[] args) {
130
131                 AltosPreferences.init(new AltosMapdPreferences());
132
133                 int skip = 1;
134                 for (int i = 0; i < args.length; i += skip) {
135                         skip = 1;
136                         if (args[i].equals("--mapdir") && i < args.length-1) {
137                                 map_dir = args[i+1];
138                                 skip = 2;
139                         } else if (args[i].equals("--launch-sites") && i < args.length-1) {
140                                 launch_sites_file = args[i+1];
141                                 skip = 2;
142                         } else if (args[i].equals("--radius") && i < args.length-1) {
143                                 try {
144                                         valid_radius = AltosParse.parse_double_locale(args[i+1]);
145                                 } catch (ParseException pe) {
146                                         usage();
147                                 }
148                                 skip = 2;
149                         } else if (args[i].equals("--port") && i < args.length-1) {
150                                 try {
151                                         port = AltosParse.parse_int(args[i+1]);
152                                 } catch (ParseException pe) {
153                                         usage();
154                                 }
155                                 skip = 2;
156                         } else if (args[i].equals("--key") && i < args.length-1) {
157                                 key_file = args[i+1];
158                                 skip = 2;
159                         } else {
160                                 usage();
161                         }
162                 }
163
164                 if (map_dir == null)
165                         usage();
166
167                 if (key_file != null) {
168                         try {
169                                 BufferedReader key_reader = new BufferedReader(new FileReader(key_file));
170
171                                 String line = key_reader.readLine();
172                                 if (line == null || line.length() != 39) {
173                                         System.out.printf("%s: invalid contents %d \"%s\"\n",
174                                                           key_file, line.length(), line);
175                                         usage();
176                                 }
177                                 key_reader.close();
178                                 AltosMapStore.google_maps_api_key = line;
179                         } catch (Exception e) {
180                                 System.out.printf("%s: %s\n", key_file, e.toString());
181                                 usage();
182                         }
183                 }
184
185                 AltosPreferences.mapdir = new File(map_dir);
186
187                 if (launch_sites_file != null)
188                         AltosLaunchSites.launch_sites_url = "file://" + launch_sites_file;
189
190                 launch_sites_ready = new Semaphore(0);
191
192                 new AltosLaunchSites(this);
193
194                 try {
195                         server = new AltosMapdServer(port);
196                 } catch (IOException ie) {
197                         System.out.printf("Cannot bind to port %d: %s\n", port, ie.toString());
198                         usage();
199                 }
200
201                 for (;;) {
202                         try {
203                                 Socket client = server.accept();
204                                 if (client == null) {
205                                         System.out.printf("accept failed\n");
206                                         continue;
207                                 }
208                                 new AltosMapdClient(client);
209                         } catch (Exception e) {
210                                 System.out.printf("Exception %s\n", e.toString());
211                         }
212                 }
213         }
214
215         public void AltosMapd() {
216         }
217
218         public static void main(final String[] args) {
219                 new AltosMapd().process(args);
220         }
221 }