root/trunk/processing/sketches/modest_maps/init.pde

Revision 489, 12.8 kB (checked in by tom, 6 months ago)

moving processing libs around

Line 
1
2 void doMapTest() {
3   println();
4   println("map test");
5   println(); 
6
7   Map m = new Map(new RoadProvider(), new Point(600, 600), new Coordinate(3165, 1313, 13), new Point(-144, -94));
8   Point p = m.locationPoint(new Location(37.804274, -122.262940));
9   println( p.toString().equals("(370.688, 342.438)") );
10   // !!! this is what the python version gives (probably floats vs doubles?)
11   //println( p.toString().equals("(370.724, 342.549)") );
12   Location l = m.pointLocation(p);
13   println( l.toString().equals("(37.804, -122.263)") ); 
14 }
15
16
17
18 class MapCenter {
19
20   Coordinate coordinate;
21   Point point;
22
23   MapCenter(Coordinate coordinate, Point point) {
24     this.coordinate = coordinate;
25     this.point = point;
26   }
27
28 }
29
30 MapCenter calculateMapCenter(AbstractMapProvider provider, Coordinate centerCoord) {
31   // Based on a center coordinate, returns the coordinate
32   // of an initial tile and its point placement, relative to
33   // the map center.
34
35   // initial tile coordinate
36   Coordinate initTileCoord = new Coordinate(floor(centerCoord.row), floor(centerCoord.column), floor(centerCoord.zoom));
37
38   // initial tile position, assuming centered tile well in grid
39   float initX = (initTileCoord.column - centerCoord.column) * provider.tileWidth();
40   float initY = (initTileCoord.row - centerCoord.row) * provider.tileHeight();
41   Point initPoint = new Point(round(initX), round(initY));
42
43   return new MapCenter(initTileCoord, initPoint);
44 }
45
46 MapCenter calculateMapExtent(AbstractMapProvider provider, int width, int height, Location[] locations) {
47
48   float minRow = MAX_FLOAT;
49   float maxRow = -MAX_FLOAT;
50   float minCol = MAX_FLOAT;
51   float maxCol = -MAX_FLOAT;
52   float minZoom = MAX_FLOAT;
53   float maxZoom = -MAX_FLOAT;
54
55   Coordinate[] coordinates = new Coordinate[locations.length];
56   for (int i = 0; i < coordinates.length; i++) {
57     coordinates[i] = provider.locationCoordinate(locations[i]);
58     minRow = min(minRow, coordinates[i].row);
59     maxRow = max(maxRow, coordinates[i].row);
60     minCol = min(minCol, coordinates[i].column);
61     maxCol = max(maxCol, coordinates[i].column);
62     minZoom = min(minZoom, coordinates[i].zoom);
63     maxZoom = max(maxZoom, coordinates[i].zoom);
64   }
65
66   Coordinate TL = new Coordinate(minRow, minCol, minZoom);
67
68   Coordinate BR = new Coordinate(maxRow, maxCol, maxZoom);
69
70   // multiplication factor between horizontal span and map width
71   float hFactor = (BR.column - TL.column) / ((float)width / provider.tileWidth());
72
73   // multiplication factor expressed as base-2 logarithm, for zoom difference
74   float hZoomDiff = log(hFactor) / log(2);
75
76   // possible horizontal zoom to fit geographical extent in map width
77   float hPossibleZoom = TL.zoom - ceil(hZoomDiff);
78
79   // multiplication factor between vertical span and map height
80   float vFactor = (BR.row - TL.row) / ((float)height / provider.tileHeight());
81
82   // multiplication factor expressed as base-2 logarithm, for zoom difference
83   float vZoomDiff = log(vFactor) / log(2);
84
85   // possible vertical zoom to fit geographical extent in map height
86   float vPossibleZoom = TL.zoom - ceil(vZoomDiff);
87
88   // initial zoom to fit extent vertically and horizontally
89   float initZoom = min(hPossibleZoom, vPossibleZoom);
90
91   // additionally, make sure it's not outside the boundaries set by provider limits
92   //initZoom = min(initZoom, provider.outerLimits()[1].zoom)
93   //initZoom = max(initZoom, provider.outerLimits()[0].zoom)
94
95   // coordinate of extent center
96   float centerRow = (TL.row + BR.row) / 2.0;
97   float centerColumn = (TL.column + BR.column) / 2.0;
98   float centerZoom = (TL.zoom + BR.zoom) / 2.0;
99   Coordinate centerCoord = new Coordinate(centerRow, centerColumn, centerZoom).zoomTo(initZoom);
100
101   return calculateMapCenter(provider, centerCoord);
102 }
103
104
105 class TileRequest {
106
107   // how many times to retry a failing tile
108   int MAX_ATTEMPTS = 5;
109
110   boolean done;
111   AbstractMapProvider provider;
112   Coordinate coord;
113   Point  offset;
114
115   PImage imgs[];
116
117   TileRequest(AbstractMapProvider provider, Coordinate coord, Point  offset) {
118     this.done = false;
119     this.provider = provider;
120     this.coord = coord;
121     this.offset = offset;
122   }
123
124   boolean loaded() {
125     return this.done;
126   }
127
128   PImage[] images() {
129     return imgs;
130   }
131
132   void load(boolean verbose) {
133     load(verbose, 1);
134   }
135
136   void load(boolean verbose, int attempt) {
137     if (done) {
138       return;
139     }
140
141     String[] urls = provider.getTileUrls(coord);
142
143     if (verbose) {
144       print("Requesting ");
145       print(join(urls, ", "));
146       println(" - attempt no. " + attempt);// + " in thread', thread.get_ident()
147     }
148
149     this.imgs = new PImage[urls.length];
150
151     // this is the time-consuming part
152     try {
153       for (int i = 0; i < urls.length; i++) {
154         String type = urls[i].startsWith("http://mt") ? "png" : urls[i].startsWith("http://kh") ? "jpg" : null;
155         if (type != null) {
156           imgs[i] = loadImage(urls[i], type);
157         }
158         else {
159           imgs[i] = loadImage(urls[i]);
160         }
161       }
162     }
163     catch (Exception e) {
164       /*               
165        if verbose:
166        print 'Failed', urls, '- attempt no.', attempt, 'in thread', thread.get_ident()
167        
168        if attempt < TileRequest.MAX_ATTEMPTS:
169        time.sleep(1 * attempt)
170        return self.load(lock, verbose, attempt+1)
171        else:
172        imgs = [None for url in urls]
173        */
174     }
175
176     if (verbose) {
177       print("Received ");
178       print(join(urls,", "));
179       println(" - attempt no. " + attempt);// 'in thread', thread.get_ident()
180     }
181
182     /* if lock.acquire():
183      self.imgs = imgs
184      self.done = True
185      lock.release() */
186   }
187
188 }
189
190
191 class TileQueue extends Vector {
192   // List of TileRequest objects, that's sensitive to when they're loaded.
193   boolean pending() {
194     for (int i = 0; i < size(); i++) {
195       if (!((TileRequest)get(i)).loaded()) {
196         return true;
197       }
198     }
199     return false;
200   }
201 }
202
203 class Map {
204
205   AbstractMapProvider provider;
206   Point dimensions;
207   Coordinate coordinate;
208   Point offset;
209
210   Map(AbstractMapProvider provider, Point dimensions, Location location, int zoom) {
211     MapCenter center = calculateMapCenter(provider, provider.locationCoordinate(location).zoomTo(zoom));
212     this.provider = provider;
213     this.dimensions = dimensions;
214     this.coordinate = center.coordinate;
215     this.offset = center.point;
216   }
217
218   Map(AbstractMapProvider provider, Point dimensions, Coordinate coordinate, Point offset) {
219     // Instance of a map intended for drawing to an image.
220     // provider
221     //   Instance of IMapProvider         
222     // dimensions
223     //   Size of output image, instance of Point
224     // coordinate
225     //   Base tile, instance of Coordinate
226     // offset
227     //   Position of base tile relative to map center, instance of Point
228
229     this.provider = provider;
230     this.dimensions = dimensions;
231     this.coordinate = coordinate;
232     this.offset = offset;
233   }
234
235   String toString() {
236     return "Map(" + provider + ", " + dimensions + ", " + coordinate + ", " + offset + ")";
237   }
238
239   Point locationPoint(Location location) {
240     // Return an x, y point on the map image for a given geographical location.
241
242     Point point = new Point(offset.x, offset.y);
243     Coordinate coord = provider.locationCoordinate(location).zoomTo(coordinate.zoom);
244
245     // distance from the known coordinate offset
246     point.x += provider.tileWidth() * (coord.column - coordinate.column);
247     point.y += provider.tileHeight() * (coord.row - coordinate.row);
248
249     // because of the center/corner business
250     point.x += dimensions.x/2.0;
251     point.y += dimensions.y/2.0;
252
253     return point;
254   }
255
256   Location pointLocation(Point point) {
257     // Return a geographical location on the map image for a given x, y point.
258
259     Coordinate hizoomCoord = coordinate.zoomTo(coordinate.MAX_ZOOM);
260
261     // because of the center/corner business
262     point = new Point(point.x - dimensions.x/2.0,
263     point.y - dimensions.y/2.0);
264
265     // distance in tile widths from reference tile to point
266     float xTiles = (point.x - offset.x) / provider.tileWidth();
267     float yTiles = (point.y - offset.y) / provider.tileHeight();
268
269     // distance in rows & columns at maximum zoom
270     float xDistance = xTiles * pow(2, (coordinate.MAX_ZOOM - coordinate.zoom));
271     float yDistance = yTiles * pow(2, (coordinate.MAX_ZOOM - coordinate.zoom));
272
273     // new point coordinate reflecting that distance
274     Coordinate coord = new Coordinate(round(hizoomCoord.row + yDistance), round(hizoomCoord.column + xDistance), hizoomCoord.zoom);
275
276     coord = coord.zoomTo(coordinate.zoom);
277
278     Location location = provider.coordinateLocation(coord);
279
280     return location;
281   }
282
283   PImage draw_bbox(float[] bbox) {
284     return draw_bbox(bbox, 16, false);
285   }
286
287   PImage draw_bbox(float[] bbox, int zoom) {
288     return draw_bbox(bbox, zoom, false);
289   }
290
291   // bbox = new float[] { south, west, north, east };
292   PImage draw_bbox(float[] bbox, int zoom, boolean verbose) {
293
294     Location sw = new Location(bbox[0], bbox[1]);
295     Location ne = new Location(bbox[2], bbox[3]);
296     Location nw = new Location(ne.lat, sw.lon);
297     Location se = new Location(sw.lat, ne.lon);
298
299     Coordinate TL = provider.locationCoordinate(nw).zoomTo(zoom);
300
301     TileQueue tiles = new TileQueue();
302
303     float cur_lon = sw.lon;
304     float cur_lat = ne.lat;       
305     float max_lon = ne.lon;
306     float max_lat = sw.lat;
307
308     float x_off = 0;
309     float y_off = 0;
310     float tile_x = 0;
311     float tile_y = 0;
312
313     Coordinate tileCoord = TL.copy();
314
315     while (cur_lon < max_lon) {
316
317       y_off = 0;
318       tile_y = 0;
319
320       Location loc;
321       while (cur_lat > max_lat) {
322
323         tiles.add(new TileRequest(provider, tileCoord, new Point(x_off, y_off)));
324         y_off += provider.tileHeight();
325
326         tileCoord = tileCoord.down();
327         loc = provider.coordinateLocation(tileCoord);
328         cur_lat = loc.lat;
329
330         tile_y += 1;
331       }
332
333       x_off += provider.tileWidth();
334       cur_lat = ne.lat;
335
336       tile_x += 1;
337       tileCoord = TL.copy().right(tile_x);
338
339       loc = provider.coordinateLocation(tileCoord);
340       cur_lon = loc.lon;
341     }
342
343     int width = (int)floor(provider.tileWidth() * tile_x);
344     int height = (int)floor(provider.tileHeight() * tile_y);
345
346     // Quick, look over there!
347
348     MapCenter center = calculateMapExtent(provider, width, height, new Location[] {
349       new Location(bbox[0], bbox[1]), new Location(bbox[2], bbox[3])     }
350     );
351
352     this.offset = center.point;
353     this.coordinate = center.coordinate;
354     this.dimensions = new Point(width, height);
355
356     return draw();
357   }
358
359   PGraphics draw() {
360     return draw(false);
361   }
362
363   PGraphics draw(boolean verbose) {
364     // Draw map out to a PImage and return it.
365
366     Coordinate coord = coordinate.copy();
367     Point corner = new Point(int(offset.x + dimensions.x/2.0), int(offset.y + dimensions.y/2.0));
368
369     while (corner.x > 0) {
370       corner.x -= provider.tileWidth();
371       coord = coord.left();
372     }
373
374     while (corner.y > 0) {
375       corner.y -= provider.tileHeight();
376       coord = coord.up();
377     }
378
379     TileQueue tiles = new TileQueue();
380
381     Coordinate rowCoord = coord.copy();
382     for (float y = corner.y; y < dimensions.y; y += provider.tileHeight()) {
383       Coordinate tileCoord = rowCoord.copy();
384       for (float x = corner.x; x < dimensions.x; x += provider.tileWidth()) {
385         tiles.add(new TileRequest(provider, tileCoord, new Point(x, y)));
386         tileCoord = tileCoord.right();
387       }
388       rowCoord = rowCoord.down();
389     }
390
391     return render_tiles(tiles, (int)dimensions.x, (int)dimensions.y, verbose);
392
393   }
394
395   PGraphics render_tiles(TileQueue tiles, int img_width, int img_height) {
396     return render_tiles(tiles, img_width, img_height, false);
397   }
398
399   PGraphics render_tiles(TileQueue tiles, int img_width, int img_height, boolean verbose) {
400
401     // lock = thread.allocate_lock()
402
403     for (int i = 0; i < tiles.size(); i++) {
404       TileRequest tile = (TileRequest)tiles.get(i);
405       // request all needed images
406       // thread.start_new_thread(tile.load, (lock, verbose))
407       tile.load(verbose);
408     }
409
410     // if it takes any longer than 20 sec overhead + 10 sec per tile, give up
411     // due = time.time() + 20 + len(tiles) * 10
412
413     //while time.time() < due and tiles.pending():
414     //    # hang around until they are loaded or we run out of time...
415     //    time.sleep(1)
416
417     PGraphics mapImg = createGraphics(img_width, img_height, JAVA2D);
418     mapImg.beginDraw();
419
420     for (int i = 0; i < tiles.size(); i++) {
421       TileRequest tile = (TileRequest)tiles.get(i);
422       for (int j = 0; j < tile.images().length; j++) {
423         PImage img = tile.images()[j];
424         mapImg.image(img, tile.offset.x, tile.offset.y);
425       }
426     }
427
428     mapImg.endDraw();
429
430     return mapImg;
431   }
432
433 }
Note: See TracBrowser for help on using the browser.