2 * Copyright © 2013 Mike Beattie <mike@ethernal.org>
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; version 2 of the License.
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License for more details.
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
18 package org.altusmetrum.AltosDroid;
23 import org.altusmetrum.altoslib_7.*;
25 import android.app.Activity;
26 import android.graphics.*;
27 import android.os.Bundle;
28 import android.support.v4.app.Fragment;
29 import android.support.v4.app.FragmentTransaction;
30 import android.view.*;
31 import android.widget.*;
32 import android.location.Location;
33 import android.content.*;
41 tab.draw_bitmap(position, tab.rocket_bitmap, tab.rocket_off_x, tab.rocket_off_y);
42 tab.draw_text(position, name, 0, 3*tab.rocket_bitmap.getHeight()/4);
45 void set_position(AltosLatLon position) {
46 this.position = position;
49 Rocket(String name, TabMapOffline tab) {
55 public class TabMapOffline extends AltosDroidTab implements AltosMapInterface {
57 AltosDroid mAltosDroid;
68 int pad_off_x, pad_off_y;
70 int rocket_off_x, rocket_off_y;
72 int here_off_x, here_off_y;
74 private boolean pad_set;
76 private TextView mDistanceView;
77 private TextView mBearingView;
78 private TextView mTargetLatitudeView;
79 private TextView mTargetLongitudeView;
80 private TextView mReceiverLatitudeView;
81 private TextView mReceiverLongitudeView;
82 private AltosMapView map_view;
84 private double mapAccuracy = -1;
86 int stroke_width = 20;
89 void draw_text(AltosLatLon lat_lon, String text, int off_x, int off_y) {
90 if (lat_lon != null && map != null && map.transform != null) {
91 AltosPointInt pt = new AltosPointInt(map.transform.screen(lat_lon));
93 Rect bounds = new Rect();
94 paint.getTextBounds(text, 0, text.length(), bounds);
96 int width = bounds.right - bounds.left;
97 int height = bounds.bottom - bounds.top;
101 x = x - width / 2.0f - off_x;
102 y = y + height / 2.0f - off_y;
103 paint.setColor(0xff000000);
104 canvas.drawText(text, 0, text.length(), x, y, paint);
108 void draw_bitmap(AltosLatLon lat_lon, Bitmap bitmap, int off_x, int off_y) {
109 if (lat_lon != null && map != null && map.transform != null) {
110 AltosPointInt pt = new AltosPointInt(map.transform.screen(lat_lon));
112 canvas.drawBitmap(bitmap, pt.x - off_x, pt.y - off_y, paint);
116 HashMap<Integer,Rocket> rockets = new HashMap<Integer,Rocket>();
118 /* AltosMapInterface */
120 static final int WHITE = 0xffffffff;
121 static final int RED = 0xffff0000;
122 static final int PINK = 0xffff8080;
123 static final int YELLOW= 0xffffff00;
124 static final int CYAN = 0xff00ffff;
125 static final int BLUE = 0xff0000ff;
126 static final int BLACK = 0xff000000;
128 public static final int stateColors[] = {
142 class MapPath extends AltosMapPath {
144 boolean line_in(AltosPointDouble a, AltosPointDouble b) {
145 final Rect bounds = canvas.getClipBounds();
146 int left = (int) Math.floor (Math.min((float) a.x, (float) b.x) - stroke_width / 2.0f);
147 int right = (int) Math.ceil(Math.max((float) a.x, (float) b.x) + stroke_width / 2.0f);
148 int top = (int) Math.floor(Math.min((float) a.y, (float) b.y) - stroke_width / 2.0f);
149 int bottom = (int) Math.ceil(Math.max((float) a.y, (float) b.y) + stroke_width / 2.0f);
151 return left < bounds.right && bounds.left < right &&
152 top < bounds.bottom && bounds.top < bottom;
155 public void paint(AltosMapTransform t) {
156 AltosPointDouble prev = null;
157 int cur_color = paint.getColor();
159 for (AltosMapPathPoint point : points) {
160 AltosPointDouble cur = t.screen(point.lat_lon);
162 if (prev != null && line_in(prev, cur)) {
164 if (0 <= point.state && point.state < stateColors.length)
165 color = stateColors[point.state];
167 color = stateColors[AltosLib.ao_flight_invalid];
168 if (color != cur_color) {
169 paint.setColor(color);
172 canvas.drawLine((float) prev.x, (float) prev.y, (float) cur.x, (float) cur.y, paint);
179 stroke_width = TabMapOffline.this.stroke_width;
183 public AltosMapPath new_path() {
187 class MapLine extends AltosMapLine {
188 public void paint(AltosMapTransform t) {
195 public AltosMapLine new_line() {
199 class MapImage implements AltosImage {
200 public Bitmap bitmap;
202 public void flush() {
203 if (bitmap != null) {
209 public MapImage(File file) {
210 bitmap = BitmapFactory.decodeFile(file.getPath());
214 public AltosImage load_image(File file) throws Exception {
215 return new MapImage(file);
218 class MapMark extends AltosMapMark {
219 public void paint(AltosMapTransform t) {
222 MapMark(double lat, double lon, int state) {
223 super(lat, lon, state);
227 public AltosMapMark new_mark(double lat, double lon, int state) {
228 return new MapMark(lat, lon, state);
231 class MapTile extends AltosMapTile {
232 public void paint(AltosMapTransform t) {
233 AltosPointInt pt = new AltosPointInt(t.screen(upper_left));
235 if (canvas.quickReject(pt.x, pt.y, pt.x + px_size, pt.y + px_size, Canvas.EdgeType.AA))
238 AltosImage altos_image = cache.get(this, store, px_size, px_size);
240 MapImage map_image = (MapImage) altos_image;
242 Bitmap bitmap = null;
244 if (map_image != null)
245 bitmap = map_image.bitmap;
247 if (bitmap != null) {
248 canvas.drawBitmap(bitmap, pt.x, pt.y, paint);
250 paint.setColor(0xff808080);
251 canvas.drawRect(pt.x, pt.y, pt.x + px_size, pt.y + px_size, paint);
252 if (t.has_location()) {
253 String message = null;
255 case AltosMapTile.loading:
256 message = "Loading...";
258 case AltosMapTile.bad_request:
259 message = "Internal error";
261 case AltosMapTile.failed:
262 message = "Network error, check connection";
264 case AltosMapTile.forbidden:
265 message = "Too many requests, try later";
268 if (message != null) {
269 Rect bounds = new Rect();
270 paint.getTextBounds(message, 0, message.length(), bounds);
272 int width = bounds.right - bounds.left;
273 int height = bounds.bottom - bounds.top;
275 float x = pt.x + px_size / 2.0f;
276 float y = pt.y + px_size / 2.0f;
277 x = x - width / 2.0f;
278 y = y + height / 2.0f;
279 paint.setColor(0xff000000);
280 canvas.drawText(message, 0, message.length(), x, y, paint);
286 public MapTile(AltosMapTileListener listener, AltosLatLon upper_left, AltosLatLon center, int zoom, int maptype, int px_size) {
287 super(listener, upper_left, center, zoom, maptype, px_size, 2);
292 public AltosMapTile new_tile(AltosMapTileListener listener, AltosLatLon upper_left, AltosLatLon center, int zoom, int maptype, int px_size) {
293 return new MapTile(listener, upper_left, center, zoom, maptype, px_size);
297 if (map_view != null)
298 return map_view.getWidth();
302 public int height() {
303 if (map_view != null)
304 return map_view.getHeight();
308 public void repaint() {
309 if (map_view != null)
310 map_view.postInvalidate();
313 public void repaint(AltosRectangle damage) {
314 if (map_view != null)
315 map_view.postInvalidate(damage.x, damage.y, damage.x + damage.width, damage.y + damage.height);
318 public void set_zoom_label(String label) {
321 public void debug(String format, Object ... arguments) {
322 AltosDebug.debug(format, arguments);
326 public void onAttach(Activity activity) {
327 super.onAttach(activity);
328 mAltosDroid = (AltosDroid) activity;
329 mAltosDroid.registerTab(this);
331 map = new AltosMap(this);
332 map.set_maptype(mAltosDroid.map_type);
334 pad_bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.pad);
335 /* arrow at the bottom of the launchpad image */
336 pad_off_x = pad_bitmap.getWidth() / 2;
337 pad_off_y = pad_bitmap.getHeight();
339 rocket_bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.rocket);
340 /* arrow at the bottom of the rocket image */
341 rocket_off_x = rocket_bitmap.getWidth() / 2;
342 rocket_off_y = rocket_bitmap.getHeight();
344 here_bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_maps_indicator_current_position);
345 /* Center of the dot */
346 here_off_x = here_bitmap.getWidth() / 2;
347 here_off_y = here_bitmap.getHeight() / 2;
351 public void onDetach() {
357 public void onCreate(Bundle savedInstanceState) {
358 super.onCreate(savedInstanceState);
362 public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
363 View v = inflater.inflate(R.layout.tab_map_offline, container, false);
365 map_view = (AltosMapView)v.findViewById(R.id.map_view_offline);
366 map_view.set_tab(this);
367 mDistanceView = (TextView)v.findViewById(R.id.distance_value_offline);
368 mBearingView = (TextView)v.findViewById(R.id.bearing_value_offline);
369 mTargetLatitudeView = (TextView)v.findViewById(R.id.target_lat_value_offline);
370 mTargetLongitudeView = (TextView)v.findViewById(R.id.target_lon_value_offline);
371 mReceiverLatitudeView = (TextView)v.findViewById(R.id.receiver_lat_value_offline);
372 mReceiverLongitudeView = (TextView)v.findViewById(R.id.receiver_lon_value_offline);
377 public void onActivityCreated(Bundle savedInstanceState) {
378 super.onActivityCreated(savedInstanceState);
382 public void onDestroyView() {
383 super.onDestroyView();
385 mAltosDroid.unregisterTab(this);
388 private void center(double lat, double lon, double accuracy) {
389 if (mapAccuracy < 0 || accuracy < mapAccuracy/10) {
391 map.maybe_centre(lat, lon);
392 mapAccuracy = accuracy;
396 public String tab_name() { return "offmap"; }
398 public void show(AltosState state, AltosGreatCircle from_receiver, Location receiver) {
399 if (from_receiver != null) {
400 mBearingView.setText(String.format("%3.0f°", from_receiver.bearing));
401 set_value(mDistanceView, AltosConvert.distance, 6, from_receiver.distance);
405 map.show(state, null);
406 if (state.gps != null) {
407 mTargetLatitudeView.setText(AltosDroid.pos(state.gps.lat, "N", "S"));
408 mTargetLongitudeView.setText(AltosDroid.pos(state.gps.lon, "E", "W"));
409 if (state.gps.locked && state.gps.nsat >= 4)
410 center (state.gps.lat, state.gps.lon, 10);
412 if (state.pad_lat != AltosLib.MISSING && pad == null)
413 pad = new AltosLatLon(state.pad_lat, state.pad_lon);
415 int serial = state.serial;
416 if (serial == AltosLib.MISSING)
419 Rocket rocket = null;
421 if (state.gps != null && state.gps.locked) {
422 if (!rockets.containsKey(serial)) {
423 rocket = new Rocket(String.format("%d", serial), this);
424 rockets.put(serial, rocket);
426 rocket = rockets.get(serial);
427 rocket.set_position(new AltosLatLon(state.gps.lat, state.gps.lon));
431 if (receiver != null) {
434 here = new AltosLatLon(receiver.getLatitude(), receiver.getLongitude());
435 if (receiver.hasAccuracy())
436 accuracy = receiver.getAccuracy();
439 mReceiverLatitudeView.setText(AltosDroid.pos(here.lat, "N", "S"));
440 mReceiverLongitudeView.setText(AltosDroid.pos(here.lon, "E", "W"));
441 center (here.lat, here.lon, accuracy);
447 public void set_map_type(int map_type) {
449 map.set_maptype(map_type);
452 public TabMapOffline() {