altos/telefireone-v2.0: Remove build of ao_product.h from Makefile
[fw/altos] / altoslib / AltosMapLoader.java
1 /*
2  * Copyright © 2015 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  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
17  */
18
19 package org.altusmetrum.altoslib_13;
20
21 import java.io.*;
22 import java.util.*;
23 import java.util.concurrent.*;
24 import java.text.*;
25 import java.lang.Math;
26 import java.net.URL;
27 import java.net.URLConnection;
28
29 public class AltosMapLoader extends Thread implements AltosMapStoreListener {
30         AltosMapLoaderListener  listener;
31
32         double  latitude, longitude;
33         int     min_z;
34         int     max_z;
35         int     cur_z;
36         int     all_types;
37         int     cur_type;
38         double  radius;
39         int     scale;
40
41         int     tiles_loaded_layer;
42         int     tiles_loaded_total;
43         int     tiles_this_layer;
44         int     tiles_total;
45         int     layers_total;
46         int     layers_loaded;
47
48         private static final int        MAX_LOADING = 200;
49
50         private Semaphore       loading = new Semaphore(MAX_LOADING);
51
52         boolean abort;
53
54         int tile_radius(int zoom) {
55                 double  delta_lon = AltosMapTransform.lon_from_distance(latitude, radius);
56
57                 AltosMapTransform t = new AltosMapTransform(256, 256, zoom + AltosMap.default_zoom, new AltosLatLon(latitude, longitude));
58
59                 AltosPointDouble        center = t.point(new AltosLatLon(latitude, longitude));
60                 AltosPointDouble        edge = t.point(new AltosLatLon(latitude, longitude + delta_lon));
61
62                 int tile_radius = (int) Math.ceil(Math.abs(center.x - edge.x) / AltosMap.px_size);
63
64                 return tile_radius;
65         }
66
67         int tiles_per_layer(int zoom) {
68                 int     tile_radius = tile_radius(zoom);
69                 return (tile_radius * 2 + 1) * (tile_radius * 2 + 1);
70         }
71
72         private boolean do_load() {
73                 tiles_this_layer = tiles_per_layer(cur_z);
74                 tiles_loaded_layer = 0;
75                 listener.debug("tiles_this_layer %d (zoom %d)\n", tiles_this_layer, cur_z);
76
77                 int load_radius = tile_radius(cur_z);
78                 int zoom = cur_z + AltosMap.default_zoom;
79                 int maptype = cur_type;
80                 AltosLatLon load_centre = new AltosLatLon(latitude, longitude);
81                 AltosMapTransform transform = new AltosMapTransform(256, 256, zoom, load_centre);
82
83                 AltosPointInt   upper_left;
84                 AltosPointInt   lower_right;
85
86                 AltosPointInt centre = AltosMap.floor(transform.point(load_centre));
87
88                 upper_left = new AltosPointInt(centre.x - load_radius * AltosMap.px_size,
89                                                centre.y - load_radius * AltosMap.px_size);
90                 lower_right = new AltosPointInt(centre.x + load_radius * AltosMap.px_size,
91                                                 centre.y + load_radius * AltosMap.px_size);
92
93
94                 for (int y = (int) upper_left.y; y <= lower_right.y; y += AltosMap.px_size) {
95                         for (int x = (int) upper_left.x; x <= lower_right.x; x += AltosMap.px_size) {
96                                 try {
97                                         loading.acquire();
98                                 } catch (InterruptedException ie) {
99                                         return false;
100                                 }
101                                 AltosPointInt   point = new AltosPointInt(x, y);
102                                 AltosLatLon     ul = transform.lat_lon(point);
103                                 AltosLatLon     center = transform.lat_lon(new AltosPointDouble(x + AltosMap.px_size/2, y + AltosMap.px_size/2));
104                                 AltosMapStore   store = AltosMapStore.get(center, zoom, maptype, AltosMap.px_size, scale);
105                                 listener.debug("load state %s url %s\n", AltosMapTile.status_name(store.status()), store.url);
106                                 store.add_listener(this);
107                                 if (abort)
108                                         return false;
109                         }
110                 }
111                 return true;
112         }
113
114         private int next_type(int start) {
115                 int next_type;
116                 for (next_type = start;
117                      next_type <= AltosMap.maptype_terrain && (all_types & (1 << next_type)) == 0;
118                      next_type++)
119                         ;
120                 return next_type;
121         }
122
123         private boolean next_load() {
124                 int next_type = next_type(cur_type + 1);
125
126                 if (next_type > AltosMap.maptype_terrain) {
127                         if (cur_z == max_z) {
128                                 return false;
129                         } else {
130                                 cur_z++;
131                         }
132                         next_type = next_type(0);
133                 }
134                 cur_type = next_type;
135                 return true;
136         }
137
138         public void run() {
139
140                 cur_z = min_z;
141                 int ntype = 0;
142
143                 for (int t = AltosMap.maptype_hybrid; t <= AltosMap.maptype_terrain; t++)
144                         if ((all_types & (1 << t)) != 0)
145                                 ntype++;
146                 if (ntype == 0) {
147                         all_types = (1 << AltosMap.maptype_hybrid);
148                         ntype = 1;
149                 }
150
151                 cur_type = next_type(0);
152
153                 tiles_total = 0;
154                 for (int z = min_z; z <= max_z; z++)
155                         tiles_total += tiles_per_layer(z) * ntype;
156
157                 layers_total = (max_z - min_z + 1) * ntype;
158                 layers_loaded = 0;
159                 tiles_loaded_total = 0;
160
161                 listener.debug("total tiles %d layers %d\n", tiles_total, layers_total);
162
163                 listener.loader_start(tiles_total);
164                 do {
165                         if (!do_load())
166                                 break;
167                 } while (next_load());
168                 if (abort)
169                         listener.loader_done(tiles_total);
170         }
171
172         public synchronized void notify_store(AltosMapStore store, int status) {
173                 boolean do_next = false;
174                 if (status == AltosMapTile.fetching)
175                         return;
176
177                 loading.release();
178
179                 store.remove_listener(this);
180
181                 if (layers_loaded >= layers_total)
182                         return;
183
184                 ++tiles_loaded_total;
185                 ++tiles_loaded_layer;
186
187                 listener.debug("AltosMapLoader.notify_store status %d total %d of %d layer %d of %d\n",
188                                status, tiles_loaded_total, tiles_total, tiles_loaded_layer, tiles_this_layer);
189
190                 if (tiles_loaded_layer == tiles_this_layer) {
191                         ++layers_loaded;
192                         listener.debug("%d layers loaded\n", layers_loaded);
193                         do_next = true;
194                 }
195
196                 if (tiles_loaded_total == tiles_total)
197                         listener.loader_done(tiles_total);
198                 else
199                         listener.loader_notify(tiles_loaded_total,
200                                                tiles_total, store.file.toString());
201         }
202
203         public void abort() {
204                 this.abort = true;
205         }
206
207         public AltosMapLoader(AltosMapLoaderListener listener,
208                               double latitude, double longitude, int min_z, int max_z, double radius, int all_types, int scale) {
209                 listener.debug("lat %f lon %f min_z %d max_z %d radius %f all_types %d\n",
210                                latitude, longitude, min_z, max_z, radius, all_types);
211                 this.listener = listener;
212                 this.latitude = latitude;
213                 this.longitude = longitude;
214                 this.min_z = min_z;
215                 this.max_z = max_z;
216                 this.radius = radius;
217 /*
218                 this.all_types = all_types;
219 */
220                 this.all_types = 1 << AltosMap.maptype_hybrid;
221                 this.scale = scale;
222                 this.abort = false;
223                 start();
224         }
225 }