From 18fe64cf2648568dd0bde5acd7b627f1ddb6917e Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 22 Jun 2015 18:26:34 -0700 Subject: [PATCH] altosdroid: Display online/offline maps in same tab Make the map portion switchable between online and offline maps, leaving the rest of the tab alone. Signed-off-by: Keith Packard --- altosdroid/res/layout/tab_map.xml | 13 +- altosdroid/res/layout/tab_map_offline.xml | 24 +- .../altusmetrum/AltosDroid/AltosDroid.java | 6 +- .../AltosDroid/AltosDroidMapInterface.java | 33 ++ .../altusmetrum/AltosDroid/AltosDroidTab.java | 3 + .../AltosDroid/AltosMapOffline.java | 469 ++++++++++++++++++ .../AltosDroid/AltosMapOnline.java | 283 +++++++++++ .../altusmetrum/AltosDroid/AltosMapView.java | 168 ------- .../org/altusmetrum/AltosDroid/TabMap.java | 1 - .../altusmetrum/AltosDroid/TabMapOffline.java | 381 +++----------- 10 files changed, 884 insertions(+), 497 deletions(-) create mode 100644 altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidMapInterface.java create mode 100644 altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOffline.java create mode 100644 altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOnline.java delete mode 100644 altosdroid/src/org/altusmetrum/AltosDroid/AltosMapView.java diff --git a/altosdroid/res/layout/tab_map.xml b/altosdroid/res/layout/tab_map.xml index f611ae48..b4c97f91 100644 --- a/altosdroid/res/layout/tab_map.xml +++ b/altosdroid/res/layout/tab_map.xml @@ -20,13 +20,12 @@ android:layout_height="match_parent" android:orientation="vertical" > - - + android:orientation="horizontal" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:layout_weight="1"> - \ No newline at end of file + diff --git a/altosdroid/res/layout/tab_map_offline.xml b/altosdroid/res/layout/tab_map_offline.xml index e6f134d8..1b0f28a7 100644 --- a/altosdroid/res/layout/tab_map_offline.xml +++ b/altosdroid/res/layout/tab_map_offline.xml @@ -20,11 +20,25 @@ android:layout_height="match_parent" android:orientation="vertical" > - + + + + + + * + * 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; version 2 of the License. + * + * 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. + */ + +package org.altusmetrum.AltosDroid; + +import java.util.*; +import java.io.*; +import android.location.Location; +import org.altusmetrum.altoslib_7.*; + +public interface AltosDroidMapInterface { + public void onCreateView(int map_type); + + public void set_visible(boolean visible); + + public void center(double lat, double lon, double accuracy); + + public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver); +} diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidTab.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidTab.java index 6a71021c..f75035d4 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidTab.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidTab.java @@ -42,6 +42,9 @@ public abstract class AltosDroidTab extends Fragment implements AltosUnitsListen public void set_map_type(int map_type) { } + public void set_map_source(int map_source) { + } + public void units_changed(boolean imperial_units) { if (!isHidden()) show(last_telem_state, last_state, last_from_receiver, last_receiver); diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOffline.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOffline.java new file mode 100644 index 00000000..3ff6ff25 --- /dev/null +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOffline.java @@ -0,0 +1,469 @@ +/* + * Copyright © 2015 Keith Packard + * + * 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; version 2 of the License. + * + * 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. + */ + +package org.altusmetrum.AltosDroid; + +import java.util.*; +import java.io.*; + +import org.altusmetrum.altoslib_7.*; + +import android.app.Activity; +import android.graphics.*; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentTransaction; +import android.view.*; +import android.widget.*; +import android.location.Location; +import android.content.*; +import android.util.*; + +class Rocket implements Comparable { + AltosLatLon position; + String name; + long last_packet; + AltosMapOffline map_offline; + + void paint() { + map_offline.draw_bitmap(position, map_offline.rocket_bitmap, map_offline.rocket_off_x, map_offline.rocket_off_y); + map_offline.draw_text(position, name, 0, 3*map_offline.rocket_bitmap.getHeight()/4); + } + + void set_position(AltosLatLon position, long last_packet) { + this.position = position; + this.last_packet = last_packet; + } + + Rocket(String name, AltosMapOffline map_offline) { + this.name = name; + this.map_offline = map_offline; + } + + public int compareTo(Object o) { + Rocket other = (Rocket) o; + + long diff = last_packet - other.last_packet; + + if (diff > 0) + return 1; + if (diff < 0) + return -1; + return 0; + } +} + +public class AltosMapOffline extends View implements ScaleGestureDetector.OnScaleGestureListener, AltosMapInterface, AltosDroidMapInterface { + ScaleGestureDetector scale_detector; + boolean scaling; + AltosMap map; + + AltosLatLon here; + AltosLatLon pad; + + Canvas canvas; + Paint paint; + + Bitmap pad_bitmap; + int pad_off_x, pad_off_y; + Bitmap rocket_bitmap; + int rocket_off_x, rocket_off_y; + Bitmap here_bitmap; + int here_off_x, here_off_y; + + static final int WHITE = 0xffffffff; + static final int RED = 0xffff0000; + static final int PINK = 0xffff8080; + static final int YELLOW= 0xffffff00; + static final int CYAN = 0xff00ffff; + static final int BLUE = 0xff0000ff; + static final int BLACK = 0xff000000; + + public static final int stateColors[] = { + WHITE, // startup + WHITE, // idle + WHITE, // pad + RED, // boost + PINK, // fast + YELLOW, // coast + CYAN, // drogue + BLUE, // main + BLACK, // landed + BLACK, // invalid + CYAN, // stateless + }; + + /* AltosMapInterface */ + public void debug(String format, Object ... arguments) { + AltosDebug.debug(format, arguments); + } + + class MapTile extends AltosMapTile { + public void paint(AltosMapTransform t) { + AltosPointInt pt = new AltosPointInt(t.screen(upper_left)); + + if (canvas.quickReject(pt.x, pt.y, pt.x + px_size, pt.y + px_size, Canvas.EdgeType.AA)) + return; + + AltosImage altos_image = cache.get(this, store, px_size, px_size); + + MapImage map_image = (MapImage) altos_image; + + Bitmap bitmap = null; + + if (map_image != null) + bitmap = map_image.bitmap; + + if (bitmap != null) { + canvas.drawBitmap(bitmap, pt.x, pt.y, paint); + } else { + paint.setColor(0xff808080); + canvas.drawRect(pt.x, pt.y, pt.x + px_size, pt.y + px_size, paint); + if (t.has_location()) { + String message = null; + switch (status) { + case AltosMapTile.loading: + message = "Loading..."; + break; + case AltosMapTile.bad_request: + message = "Internal error"; + break; + case AltosMapTile.failed: + message = "Network error, check connection"; + break; + case AltosMapTile.forbidden: + message = "Too many requests, try later"; + break; + } + if (message != null) { + Rect bounds = new Rect(); + paint.getTextBounds(message, 0, message.length(), bounds); + + int width = bounds.right - bounds.left; + int height = bounds.bottom - bounds.top; + + float x = pt.x + px_size / 2.0f; + float y = pt.y + px_size / 2.0f; + x = x - width / 2.0f; + y = y + height / 2.0f; + paint.setColor(0xff000000); + canvas.drawText(message, 0, message.length(), x, y, paint); + } + } + } + } + + public MapTile(AltosMapTileListener listener, AltosLatLon upper_left, AltosLatLon center, int zoom, int maptype, int px_size) { + super(listener, upper_left, center, zoom, maptype, px_size, 2); + } + + } + + public AltosMapTile new_tile(AltosMapTileListener listener, AltosLatLon upper_left, AltosLatLon center, int zoom, int maptype, int px_size) { + return new MapTile(listener, upper_left, center, zoom, maptype, px_size); + } + + public AltosMapPath new_path() { + return null; + } + + public AltosMapLine new_line() { + return null; + } + + class MapImage implements AltosImage { + public Bitmap bitmap; + + public void flush() { + if (bitmap != null) { + bitmap.recycle(); + bitmap = null; + } + } + + public MapImage(File file) { + bitmap = BitmapFactory.decodeFile(file.getPath()); + } + } + + public AltosImage load_image(File file) throws Exception { + return new MapImage(file); + } + + class MapMark extends AltosMapMark { + public void paint(AltosMapTransform t) { + } + + MapMark(double lat, double lon, int state) { + super(lat, lon, state); + } + } + + public AltosMapMark new_mark(double lat, double lon, int state) { + return new MapMark(lat, lon, state); + } + + public int width() { + return getWidth(); + } + + public int height() { + return getHeight(); + } + + public void repaint() { + postInvalidate(); + } + + public void repaint(AltosRectangle damage) { + postInvalidate(damage.x, damage.y, damage.x + damage.width, damage.y + damage.height); + } + + public void set_zoom_label(String label) { + } + + class Line { + AltosLatLon a, b; + + void paint() { + if (a != null && b != null) { + AltosPointDouble a_screen = map.transform.screen(a); + AltosPointDouble b_screen = map.transform.screen(b); + paint.setColor(0xff8080ff); + canvas.drawLine((float) a_screen.x, (float) a_screen.y, + (float) b_screen.x, (float) b_screen.y, + paint); + } + } + + void set_a(AltosLatLon a) { + this.a = a; + } + + void set_b(AltosLatLon b) { + this.b = b; + } + + Line() { + } + } + + Line line = new Line(); + + int stroke_width = 20; + + void draw_text(AltosLatLon lat_lon, String text, int off_x, int off_y) { + if (lat_lon != null && map != null && map.transform != null) { + AltosPointInt pt = new AltosPointInt(map.transform.screen(lat_lon)); + + Rect bounds = new Rect(); + paint.getTextBounds(text, 0, text.length(), bounds); + + int width = bounds.right - bounds.left; + int height = bounds.bottom - bounds.top; + + float x = pt.x; + float y = pt.y; + x = x - width / 2.0f - off_x; + y = y + height / 2.0f - off_y; + paint.setColor(0xff000000); + canvas.drawText(text, 0, text.length(), x, y, paint); + } + } + + HashMap rockets = new HashMap(); + + void draw_bitmap(AltosLatLon lat_lon, Bitmap bitmap, int off_x, int off_y) { + if (lat_lon != null && map != null && map.transform != null) { + AltosPointInt pt = new AltosPointInt(map.transform.screen(lat_lon)); + + canvas.drawBitmap(bitmap, pt.x - off_x, pt.y - off_y, paint); + } + } + + private void draw_positions() { + line.set_a(map.last_position); + line.set_b(here); + line.paint(); + draw_bitmap(pad, pad_bitmap, pad_off_x, pad_off_y); + + Rocket[] rocket_array = rockets.values().toArray(new Rocket[0]); + + Arrays.sort(rocket_array); + for (Rocket rocket : rocket_array) + rocket.paint(); + draw_bitmap(here, here_bitmap, here_off_x, here_off_y); + } + + @Override public void invalidate() { + Rect r = new Rect(); + getDrawingRect(r); + super.invalidate(); + } + + @Override public void invalidate(int l, int t, int r, int b) { + Rect rect = new Rect(); + getDrawingRect(rect); + super.invalidate(); + } + + @Override + protected void onDraw(Canvas view_canvas) { + debug("onDraw"); + if (map == null) { + debug("MapView draw without map\n"); + return; + } + canvas = view_canvas; + paint = new Paint(Paint.ANTI_ALIAS_FLAG); + paint.setStrokeWidth(stroke_width); + paint.setStrokeCap(Paint.Cap.ROUND); + paint.setStrokeJoin(Paint.Join.ROUND); + paint.setTextSize(40); + map.paint(); + draw_positions(); + canvas = null; + } + + public boolean onScale(ScaleGestureDetector detector) { + float f = detector.getScaleFactor(); + + if (f <= 0.8) { + map.set_zoom_centre(map.get_zoom() - 1, new AltosPointInt((int) detector.getFocusX(), (int) detector.getFocusY())); + return true; + } + if (f >= 1.2) { + map.set_zoom_centre(map.get_zoom() + 1, new AltosPointInt((int) detector.getFocusX(), (int) detector.getFocusY())); + return true; + } + return false; + } + + public boolean onScaleBegin(ScaleGestureDetector detector) { + return true; + } + + public void onScaleEnd(ScaleGestureDetector detector) { + } + + @Override + public boolean dispatchTouchEvent(MotionEvent event) { + scale_detector.onTouchEvent(event); + + if (scale_detector.isInProgress()) { + scaling = true; + } + + if (scaling) { + if (event.getAction() == MotionEvent.ACTION_UP) { + scaling = false; + } + return true; + } + + if (event.getAction() == MotionEvent.ACTION_DOWN) { + map.touch_start((int) event.getX(), (int) event.getY(), true); + } else if (event.getAction() == MotionEvent.ACTION_MOVE) { + map.touch_continue((int) event.getX(), (int) event.getY(), true); + } + return true; + } + + double mapAccuracy; + + public void center(double lat, double lon, double accuracy) { + if (mapAccuracy < 0 || accuracy < mapAccuracy/10) { + if (map != null) + map.maybe_centre(lat, lon); + mapAccuracy = accuracy; + } + } + + public void set_visible(boolean visible) { + if (visible) + setVisibility(VISIBLE); + else + setVisibility(GONE); + } + + public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver) { + if (state != null) { + map.show(state, null); + if (state.pad_lat != AltosLib.MISSING && pad == null) + pad = new AltosLatLon(state.pad_lat, state.pad_lon); + } + + if (telem_state != null) { + Integer[] old_serial = rockets.keySet().toArray(new Integer[0]); + Integer[] new_serial = telem_state.states.keySet().toArray(new Integer[0]); + + /* remove deleted keys */ + for (int serial : old_serial) { + if (!telem_state.states.containsKey(serial)) + rockets.remove(serial); + } + + /* set remaining keys */ + + for (int serial : new_serial) { + Rocket rocket; + AltosState t_state = telem_state.states.get(serial); + if (rockets.containsKey(serial)) + rocket = rockets.get(serial); + else { + rocket = new Rocket(String.format("%d", serial), this); + rockets.put(serial, rocket); + } + if (t_state.gps != null) + rocket.set_position(new AltosLatLon(t_state.gps.lat, t_state.gps.lon), t_state.received_time); + } + } + if (receiver != null) { + here = new AltosLatLon(receiver.getLatitude(), receiver.getLongitude()); + } + } + + public void onCreateView(int map_type) { + map = new AltosMap(this); + map.set_maptype(map_type); + + pad_bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.pad); + /* arrow at the bottom of the launchpad image */ + pad_off_x = pad_bitmap.getWidth() / 2; + pad_off_y = pad_bitmap.getHeight(); + + rocket_bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.rocket); + /* arrow at the bottom of the rocket image */ + rocket_off_x = rocket_bitmap.getWidth() / 2; + rocket_off_y = rocket_bitmap.getHeight(); + + here_bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_maps_indicator_current_position); + /* Center of the dot */ + here_off_x = here_bitmap.getWidth() / 2; + here_off_y = here_bitmap.getHeight() / 2; + } + + public void set_map_type(int map_type) { + if (map != null) + map.set_maptype(map_type); + } + + public AltosMapOffline(Context context, AttributeSet attrs) { + super(context, attrs); + scale_detector = new ScaleGestureDetector(context, this); + } +} diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOnline.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOnline.java new file mode 100644 index 00000000..9503a0bd --- /dev/null +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOnline.java @@ -0,0 +1,283 @@ +/* + * Copyright © 2013 Mike Beattie + * + * 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; version 2 of the License. + * + * 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. + */ + +package org.altusmetrum.AltosDroid; + +import java.util.*; + +import org.altusmetrum.altoslib_7.*; + +import com.google.android.gms.maps.CameraUpdateFactory; +import com.google.android.gms.maps.GoogleMap; +import com.google.android.gms.maps.SupportMapFragment; +import com.google.android.gms.maps.model.BitmapDescriptorFactory; +import com.google.android.gms.maps.model.LatLng; +import com.google.android.gms.maps.model.Marker; +import com.google.android.gms.maps.model.MarkerOptions; +import com.google.android.gms.maps.model.Polyline; +import com.google.android.gms.maps.model.PolylineOptions; + +import android.app.Activity; +import android.graphics.Color; +import android.graphics.*; +import android.os.Bundle; +import android.support.v4.app.Fragment; +//import android.support.v4.app.FragmentTransaction; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; +import android.location.Location; +import android.content.*; + +class RocketOnline implements Comparable { + Marker marker; + long last_packet; + + void set_position(AltosLatLon position, long last_packet) { + marker.setPosition(new LatLng(position.lat, position.lon)); + this.last_packet = last_packet; + } + + private Bitmap rocket_bitmap(Context context, String text) { + + /* From: http://mapicons.nicolasmollet.com/markers/industry/military/missile-2/ + */ + Bitmap orig_bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.rocket); + Bitmap bitmap = orig_bitmap.copy(Bitmap.Config.ARGB_8888, true); + + Canvas canvas = new Canvas(bitmap); + Paint paint = new Paint(); + paint.setTextSize(40); + paint.setColor(0xff000000); + + Rect bounds = new Rect(); + paint.getTextBounds(text, 0, text.length(), bounds); + + int width = bounds.right - bounds.left; + int height = bounds.bottom - bounds.top; + + float x = bitmap.getWidth() / 2.0f - width / 2.0f; + float y = bitmap.getHeight() / 2.0f - height / 2.0f; + + canvas.drawText(text, 0, text.length(), x, y, paint); + return bitmap; + } + + public void remove() { + marker.remove(); + } + + public int compareTo(Object o) { + RocketOnline other = (RocketOnline) o; + + long diff = last_packet - other.last_packet; + + if (diff > 0) + return 1; + if (diff < 0) + return -1; + return 0; + } + + RocketOnline(Context context, String name, GoogleMap map, double lat, double lon, long last_packet) { + this.marker = map.addMarker(new MarkerOptions() + .icon(BitmapDescriptorFactory.fromBitmap(rocket_bitmap(context, name))) + .position(new LatLng(lat, lon)) + .visible(true)); + this.last_packet = last_packet; + } +} + +public class AltosMapOnline implements AltosDroidMapInterface { + public SupportMapFragment mMapFragment; + private GoogleMap mMap; + private boolean mapLoaded = false; + Context context; + + private HashMap rockets = new HashMap(); + private Marker mPadMarker; + private boolean pad_set; + private Polyline mPolyline; + + private View map_view; + + private double mapAccuracy = -1; + + private AltosLatLon my_position = null; + private AltosLatLon target_position = null; + + public void onCreateView(final int map_type) { + mMapFragment = new SupportMapFragment() { + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + setupMap(map_type); + } + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + map_view = super.onCreateView(inflater, container, savedInstanceState); + return map_view; + } + @Override + public void onDestroyView() { + super.onDestroyView(); + map_view = null; + } + }; + } + +// public void onActivityCreated() { +// getChildFragmentManager().beginTransaction().add(R.id.map, mMapFragment).commit(); +// } + + public void setupMap(int map_type) { + mMap = mMapFragment.getMap(); + if (mMap != null) { + set_map_type(map_type); + mMap.setMyLocationEnabled(true); + mMap.getUiSettings().setTiltGesturesEnabled(false); + mMap.getUiSettings().setZoomControlsEnabled(false); + + mPadMarker = mMap.addMarker( + new MarkerOptions().icon(BitmapDescriptorFactory.fromResource(R.drawable.pad)) + .position(new LatLng(0,0)) + .visible(false) + ); + + mPolyline = mMap.addPolyline( + new PolylineOptions().add(new LatLng(0,0), new LatLng(0,0)) + .width(20) + .color(Color.BLUE) + .visible(false) + ); + + mapLoaded = true; + } + } + + public void center(double lat, double lon, double accuracy) { + if (mMap == null) + return; + + if (mapAccuracy < 0 || accuracy < mapAccuracy/10) { + mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(lat, lon),14)); + mapAccuracy = accuracy; + } + } + + private void set_rocket(int serial, AltosState state) { + RocketOnline rocket; + + if (state.gps == null || state.gps.lat == AltosLib.MISSING) + return; + + if (mMap == null) + return; + + if (rockets.containsKey(serial)) { + rocket = rockets.get(serial); + rocket.set_position(new AltosLatLon(state.gps.lat, state.gps.lon), state.received_time); + } else { + rocket = new RocketOnline(context, + String.format("%d", serial), + mMap, state.gps.lat, state.gps.lon, + state.received_time); + rockets.put(serial, rocket); + } + } + + private void remove_rocket(int serial) { + RocketOnline rocket = rockets.get(serial); + rocket.remove(); + rockets.remove(serial); + } + + public void set_visible(boolean visible) { + if (map_view == null) + return; + if (visible) + map_view.setVisibility(View.VISIBLE); + else + map_view.setVisibility(View.GONE); + } + + public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver) { + + if (telem_state != null) { + for (int serial : rockets.keySet()) { + if (!telem_state.states.containsKey(serial)) + remove_rocket(serial); + } + + for (int serial : telem_state.states.keySet()) { + set_rocket(serial, telem_state.states.get(serial)); + } + } + + if (state != null) { + if (mapLoaded) { + if (!pad_set && state.pad_lat != AltosLib.MISSING) { + pad_set = true; + mPadMarker.setPosition(new LatLng(state.pad_lat, state.pad_lon)); + mPadMarker.setVisible(true); + } + } + if (state.gps != null) { + + target_position = new AltosLatLon(state.gps.lat, state.gps.lon); + if (state.gps.locked && state.gps.nsat >= 4) + center (state.gps.lat, state.gps.lon, 10); + } + } + + if (receiver != null) { + double accuracy; + + if (receiver.hasAccuracy()) + accuracy = receiver.getAccuracy(); + else + accuracy = 1000; + + my_position = new AltosLatLon(receiver.getLatitude(), receiver.getLongitude()); + center (my_position.lat, my_position.lon, accuracy); + } + + if (my_position != null && target_position != null) { + mPolyline.setPoints(Arrays.asList(new LatLng(my_position.lat, my_position.lon), new LatLng(target_position.lat, target_position.lon))); + mPolyline.setVisible(true); + } + + } + + public void set_map_type(int map_type) { + if (mMap != null) { + if (map_type == AltosMap.maptype_hybrid) + mMap.setMapType(GoogleMap.MAP_TYPE_HYBRID); + else if (map_type == AltosMap.maptype_satellite) + mMap.setMapType(GoogleMap.MAP_TYPE_SATELLITE); + else if (map_type == AltosMap.maptype_terrain) + mMap.setMapType(GoogleMap.MAP_TYPE_TERRAIN); + else + mMap.setMapType(GoogleMap.MAP_TYPE_NORMAL); + } + } + + public AltosMapOnline(Context context) { + this.context = context; + } +} diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapView.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapView.java deleted file mode 100644 index 65cc0b95..00000000 --- a/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapView.java +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright © 2015 Keith Packard - * - * 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; version 2 of the License. - * - * 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. - */ - -package org.altusmetrum.AltosDroid; - -import java.util.*; -import java.io.*; - -import org.altusmetrum.altoslib_7.*; - -import android.app.Activity; -import android.graphics.*; -import android.os.Bundle; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentTransaction; -import android.view.*; -import android.widget.*; -import android.location.Location; -import android.content.*; -import android.util.*; - -public class AltosMapView extends View implements ScaleGestureDetector.OnScaleGestureListener { - ScaleGestureDetector scale_detector; - boolean scaling; - TabMapOffline tab; - - class Line { - AltosLatLon a, b; - - void paint() { - if (a != null && b != null) { - AltosPointDouble a_screen = tab.map.transform.screen(a); - AltosPointDouble b_screen = tab.map.transform.screen(b); - tab.paint.setColor(0xff8080ff); - tab.canvas.drawLine((float) a_screen.x, (float) a_screen.y, - (float) b_screen.x, (float) b_screen.y, - tab.paint); - } - } - - void set_a(AltosLatLon a) { - this.a = a; - } - - void set_b(AltosLatLon b) { - this.b = b; - } - - Line() { - } - } - - Line line = new Line(); - - private void draw_bitmap(AltosLatLon lat_lon, Bitmap bitmap, int off_x, int off_y) { - if (lat_lon != null && tab.map != null && tab.map.transform != null) { - AltosPointInt pt = new AltosPointInt(tab.map.transform.screen(lat_lon)); - - tab.canvas.drawBitmap(bitmap, pt.x - off_x, pt.y - off_y, tab.paint); - } - } - - private void draw_positions() { - line.set_a(tab.map.last_position); - line.set_b(tab.here); - line.paint(); - draw_bitmap(tab.pad, tab.pad_bitmap, tab.pad_off_x, tab.pad_off_y); - - Rocket[] rockets = tab.rockets.values().toArray(new Rocket[0]); - - Arrays.sort(rockets); - for (Rocket rocket : rockets) - rocket.paint(); - draw_bitmap(tab.here, tab.here_bitmap, tab.here_off_x, tab.here_off_y); - } - - @Override public void invalidate() { - Rect r = new Rect(); - getDrawingRect(r); - super.invalidate(); - } - - @Override public void invalidate(int l, int t, int r, int b) { - Rect rect = new Rect(); - getDrawingRect(rect); - super.invalidate(); - } - - @Override - protected void onDraw(Canvas view_canvas) { - tab.canvas = view_canvas; - tab.paint = new Paint(Paint.ANTI_ALIAS_FLAG); - tab.paint.setStrokeWidth(tab.stroke_width); - tab.paint.setStrokeCap(Paint.Cap.ROUND); - tab.paint.setStrokeJoin(Paint.Join.ROUND); - tab.paint.setTextSize(40); - tab.map.paint(); - draw_positions(); - tab.canvas = null; - } - - public boolean onScale(ScaleGestureDetector detector) { - float f = detector.getScaleFactor(); - - if (f <= 0.8) { - tab.map.set_zoom_centre(tab.map.get_zoom() - 1, new AltosPointInt((int) detector.getFocusX(), (int) detector.getFocusY())); - return true; - } - if (f >= 1.2) { - tab.map.set_zoom_centre(tab.map.get_zoom() + 1, new AltosPointInt((int) detector.getFocusX(), (int) detector.getFocusY())); - return true; - } - return false; - } - - public boolean onScaleBegin(ScaleGestureDetector detector) { - return true; - } - - public void onScaleEnd(ScaleGestureDetector detector) { - } - - @Override - public boolean dispatchTouchEvent(MotionEvent event) { - scale_detector.onTouchEvent(event); - - if (scale_detector.isInProgress()) { - scaling = true; - } - - if (scaling) { - if (event.getAction() == MotionEvent.ACTION_UP) { - scaling = false; - } - return true; - } - - if (event.getAction() == MotionEvent.ACTION_DOWN) { - tab.map.touch_start((int) event.getX(), (int) event.getY(), true); - } else if (event.getAction() == MotionEvent.ACTION_MOVE) { - tab.map.touch_continue((int) event.getX(), (int) event.getY(), true); - } - return true; - } - - public void set_tab(TabMapOffline tab) { - this.tab = tab; - } - - public AltosMapView(Context context, AttributeSet attrs) { - super(context, attrs); - scale_detector = new ScaleGestureDetector(context, this); - } -} diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TabMap.java b/altosdroid/src/org/altusmetrum/AltosDroid/TabMap.java index d5d1d262..ee52d637 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TabMap.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TabMap.java @@ -112,7 +112,6 @@ public class TabMap extends AltosDroidTab { setupMap(); } }; - } @Override diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TabMapOffline.java b/altosdroid/src/org/altusmetrum/AltosDroid/TabMapOffline.java index 296e2122..26e04c83 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TabMapOffline.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TabMapOffline.java @@ -32,58 +32,9 @@ import android.widget.*; import android.location.Location; import android.content.*; -class Rocket implements Comparable { - AltosLatLon position; - String name; - long last_packet; - TabMapOffline tab; - - void paint() { - tab.draw_bitmap(position, tab.rocket_bitmap, tab.rocket_off_x, tab.rocket_off_y); - tab.draw_text(position, name, 0, 3*tab.rocket_bitmap.getHeight()/4); - } - - void set_position(AltosLatLon position, long last_packet) { - this.position = position; - this.last_packet = last_packet; - } - - Rocket(String name, TabMapOffline tab) { - this.name = name; - this.tab = tab; - } - - public int compareTo(Object o) { - Rocket other = (Rocket) o; - - long diff = last_packet - other.last_packet; - - if (diff > 0) - return 1; - if (diff < 0) - return -1; - return 0; - } -} - -public class TabMapOffline extends AltosDroidTab implements AltosMapInterface { - - AltosMap map; +public class TabMapOffline extends AltosDroidTab { AltosLatLon here; - AltosLatLon pad; - - Canvas canvas; - Paint paint; - - Bitmap pad_bitmap; - int pad_off_x, pad_off_y; - Bitmap rocket_bitmap; - int rocket_off_x, rocket_off_y; - Bitmap here_bitmap; - int here_off_x, here_off_y; - - private boolean pad_set; private TextView mDistanceView; private TextView mBearingView; @@ -91,221 +42,14 @@ public class TabMapOffline extends AltosDroidTab implements AltosMapInterface { private TextView mTargetLongitudeView; private TextView mReceiverLatitudeView; private TextView mReceiverLongitudeView; - private AltosMapView map_view; - - private double mapAccuracy = -1; - - int stroke_width = 20; - - - void draw_text(AltosLatLon lat_lon, String text, int off_x, int off_y) { - if (lat_lon != null && map != null && map.transform != null) { - AltosPointInt pt = new AltosPointInt(map.transform.screen(lat_lon)); - - Rect bounds = new Rect(); - paint.getTextBounds(text, 0, text.length(), bounds); - - int width = bounds.right - bounds.left; - int height = bounds.bottom - bounds.top; - - float x = pt.x; - float y = pt.y; - x = x - width / 2.0f - off_x; - y = y + height / 2.0f - off_y; - paint.setColor(0xff000000); - canvas.drawText(text, 0, text.length(), x, y, paint); - } - } - - void draw_bitmap(AltosLatLon lat_lon, Bitmap bitmap, int off_x, int off_y) { - if (lat_lon != null && map != null && map.transform != null) { - AltosPointInt pt = new AltosPointInt(map.transform.screen(lat_lon)); - - canvas.drawBitmap(bitmap, pt.x - off_x, pt.y - off_y, paint); - } - } - - HashMap rockets = new HashMap(); - - /* AltosMapInterface */ - - static final int WHITE = 0xffffffff; - static final int RED = 0xffff0000; - static final int PINK = 0xffff8080; - static final int YELLOW= 0xffffff00; - static final int CYAN = 0xff00ffff; - static final int BLUE = 0xff0000ff; - static final int BLACK = 0xff000000; - - public static final int stateColors[] = { - WHITE, // startup - WHITE, // idle - WHITE, // pad - RED, // boost - PINK, // fast - YELLOW, // coast - CYAN, // drogue - BLUE, // main - BLACK, // landed - BLACK, // invalid - CYAN, // stateless - }; - - public AltosMapPath new_path() { - return null; - } - - public AltosMapLine new_line() { - return null; - } - - class MapImage implements AltosImage { - public Bitmap bitmap; - - public void flush() { - if (bitmap != null) { - bitmap.recycle(); - bitmap = null; - } - } - - public MapImage(File file) { - bitmap = BitmapFactory.decodeFile(file.getPath()); - } - } - - public AltosImage load_image(File file) throws Exception { - return new MapImage(file); - } - - class MapMark extends AltosMapMark { - public void paint(AltosMapTransform t) { - } - - MapMark(double lat, double lon, int state) { - super(lat, lon, state); - } - } - - public AltosMapMark new_mark(double lat, double lon, int state) { - return new MapMark(lat, lon, state); - } - - class MapTile extends AltosMapTile { - public void paint(AltosMapTransform t) { - AltosPointInt pt = new AltosPointInt(t.screen(upper_left)); - - if (canvas.quickReject(pt.x, pt.y, pt.x + px_size, pt.y + px_size, Canvas.EdgeType.AA)) - return; - - AltosImage altos_image = cache.get(this, store, px_size, px_size); - - MapImage map_image = (MapImage) altos_image; - - Bitmap bitmap = null; - - if (map_image != null) - bitmap = map_image.bitmap; - - if (bitmap != null) { - canvas.drawBitmap(bitmap, pt.x, pt.y, paint); - } else { - paint.setColor(0xff808080); - canvas.drawRect(pt.x, pt.y, pt.x + px_size, pt.y + px_size, paint); - if (t.has_location()) { - String message = null; - switch (status) { - case AltosMapTile.loading: - message = "Loading..."; - break; - case AltosMapTile.bad_request: - message = "Internal error"; - break; - case AltosMapTile.failed: - message = "Network error, check connection"; - break; - case AltosMapTile.forbidden: - message = "Too many requests, try later"; - break; - } - if (message != null) { - Rect bounds = new Rect(); - paint.getTextBounds(message, 0, message.length(), bounds); - - int width = bounds.right - bounds.left; - int height = bounds.bottom - bounds.top; - - float x = pt.x + px_size / 2.0f; - float y = pt.y + px_size / 2.0f; - x = x - width / 2.0f; - y = y + height / 2.0f; - paint.setColor(0xff000000); - canvas.drawText(message, 0, message.length(), x, y, paint); - } - } - } - } - - public MapTile(AltosMapTileListener listener, AltosLatLon upper_left, AltosLatLon center, int zoom, int maptype, int px_size) { - super(listener, upper_left, center, zoom, maptype, px_size, 2); - } - - } - - public AltosMapTile new_tile(AltosMapTileListener listener, AltosLatLon upper_left, AltosLatLon center, int zoom, int maptype, int px_size) { - return new MapTile(listener, upper_left, center, zoom, maptype, px_size); - } - - public int width() { - if (map_view != null) - return map_view.getWidth(); - return 500; - } - - public int height() { - if (map_view != null) - return map_view.getHeight(); - return 500; - } - - public void repaint() { - if (map_view != null) - map_view.postInvalidate(); - } - - public void repaint(AltosRectangle damage) { - if (map_view != null) - map_view.postInvalidate(damage.x, damage.y, damage.x + damage.width, damage.y + damage.height); - } - - public void set_zoom_label(String label) { - } - - public void debug(String format, Object ... arguments) { - AltosDebug.debug(format, arguments); - } + private AltosMapOffline map_offline; + private AltosMapOnline map_online; + private View view; + private int map_source; @Override public void onAttach(Activity activity) { super.onAttach(activity); - - map = new AltosMap(this); - map.set_maptype(altos_droid.map_type); - - pad_bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.pad); - /* arrow at the bottom of the launchpad image */ - pad_off_x = pad_bitmap.getWidth() / 2; - pad_off_y = pad_bitmap.getHeight(); - - rocket_bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.rocket); - /* arrow at the bottom of the rocket image */ - rocket_off_x = rocket_bitmap.getWidth() / 2; - rocket_off_y = rocket_bitmap.getHeight(); - - here_bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_maps_indicator_current_position); - /* Center of the dot */ - here_off_x = here_bitmap.getWidth() / 2; - here_off_y = here_bitmap.getHeight() / 2; } @Override @@ -313,42 +57,54 @@ public class TabMapOffline extends AltosDroidTab implements AltosMapInterface { super.onCreate(savedInstanceState); } + private void make_offline_map() { + } + + private void make_online_map() { + map_online = new AltosMapOnline(view.getContext()); + map_online.onCreateView(altos_droid.map_type); + } + @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View v = inflater.inflate(R.layout.tab_map_offline, container, false); - - map_view = (AltosMapView)v.findViewById(R.id.map_view_offline); - map_view.set_tab(this); - mDistanceView = (TextView)v.findViewById(R.id.distance_value_offline); - mBearingView = (TextView)v.findViewById(R.id.bearing_value_offline); - mTargetLatitudeView = (TextView)v.findViewById(R.id.target_lat_value_offline); - mTargetLongitudeView = (TextView)v.findViewById(R.id.target_lon_value_offline); - mReceiverLatitudeView = (TextView)v.findViewById(R.id.receiver_lat_value_offline); - mReceiverLongitudeView = (TextView)v.findViewById(R.id.receiver_lon_value_offline); - return v; + view = inflater.inflate(R.layout.tab_map_offline, container, false); + int map_source = AltosDroidPreferences.map_source(); + + mDistanceView = (TextView)view.findViewById(R.id.distance_value_offline); + mBearingView = (TextView)view.findViewById(R.id.bearing_value_offline); + mTargetLatitudeView = (TextView)view.findViewById(R.id.target_lat_value_offline); + mTargetLongitudeView = (TextView)view.findViewById(R.id.target_lon_value_offline); + mReceiverLatitudeView = (TextView)view.findViewById(R.id.receiver_lat_value_offline); + mReceiverLongitudeView = (TextView)view.findViewById(R.id.receiver_lon_value_offline); + map_offline = (AltosMapOffline)view.findViewById(R.id.map_view_offline); + map_offline.onCreateView(altos_droid.map_type); + map_online = new AltosMapOnline(view.getContext()); + map_online.onCreateView(altos_droid.map_type); + set_map_source(AltosDroidPreferences.map_source()); + return view; } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); + if (map_online != null) + getChildFragmentManager().beginTransaction().add(R.id.map_online, map_online.mMapFragment).commit(); } @Override public void onDestroyView() { super.onDestroyView(); - } + public String tab_name() { return "offmap"; } + private void center(double lat, double lon, double accuracy) { - if (mapAccuracy < 0 || accuracy < mapAccuracy/10) { - if (map != null) - map.maybe_centre(lat, lon); - mapAccuracy = accuracy; - } + if (map_offline != null) + map_offline.center(lat, lon, accuracy); + if (map_online != null) + map_online.center(lat, lon, accuracy); } - public String tab_name() { return "offmap"; } - public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver) { if (from_receiver != null) { mBearingView.setText(String.format("%3.0f°", from_receiver.bearing)); @@ -356,40 +112,9 @@ public class TabMapOffline extends AltosDroidTab implements AltosMapInterface { } if (state != null) { - map.show(state, null); if (state.gps != null) { mTargetLatitudeView.setText(AltosDroid.pos(state.gps.lat, "N", "S")); mTargetLongitudeView.setText(AltosDroid.pos(state.gps.lon, "E", "W")); - if (state.gps.locked && state.gps.nsat >= 4) - center (state.gps.lat, state.gps.lon, 10); - } - if (state.pad_lat != AltosLib.MISSING && pad == null) - pad = new AltosLatLon(state.pad_lat, state.pad_lon); - } - - if (telem_state != null) { - Integer[] old_serial = rockets.keySet().toArray(new Integer[0]); - Integer[] new_serial = telem_state.states.keySet().toArray(new Integer[0]); - - /* remove deleted keys */ - for (int serial : old_serial) { - if (!telem_state.states.containsKey(serial)) - rockets.remove(serial); - } - - /* set remaining keys */ - - for (int serial : new_serial) { - Rocket rocket; - AltosState t_state = telem_state.states.get(serial); - if (rockets.containsKey(serial)) - rocket = rockets.get(serial); - else { - rocket = new Rocket(String.format("%d", serial), this); - rockets.put(serial, rocket); - } - if (t_state.gps != null) - rocket.set_position(new AltosLatLon(t_state.gps.lat, t_state.gps.lon), t_state.received_time); } } @@ -405,13 +130,41 @@ public class TabMapOffline extends AltosDroidTab implements AltosMapInterface { mReceiverLongitudeView.setText(AltosDroid.pos(here.lon, "E", "W")); center (here.lat, here.lon, accuracy); } - + if (map_source == AltosDroidPreferences.MAP_SOURCE_OFFLINE) { + if (map_offline != null) + map_offline.show(telem_state, state, from_receiver, receiver); + } else { + if (map_online != null) + map_online.show(telem_state, state, from_receiver, receiver); + } } @Override public void set_map_type(int map_type) { - if (map != null) - map.set_maptype(map_type); + if (map_offline != null) + map_offline.set_map_type(map_type); + if (map_online != null) + map_online.set_map_type(map_type); + } + + @Override + public void set_map_source(int map_source) { + this.map_source = map_source; + if (map_source == AltosDroidPreferences.MAP_SOURCE_OFFLINE) { + if (map_online != null) + map_online.set_visible(false); + if (map_offline != null) { + map_offline.set_visible(true); + map_offline.show(last_telem_state, last_state, last_from_receiver, last_receiver); + } + } else { + if (map_offline != null) + map_offline.set_visible(false); + if (map_online != null) { + map_online.set_visible(true); + map_online.show(last_telem_state, last_state, last_from_receiver, last_receiver); + } + } } public TabMapOffline() { -- 2.30.2