android:layout_height="match_parent"
android:orientation="vertical" >
- <LinearLayout
+ <LinearLayout
android:id="@+id/map"
- android:orientation="horizontal"
- android:layout_width="fill_parent"
- android:layout_height="0dp"
- android:layout_weight="1">
-
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1">
</LinearLayout>
<LinearLayout
android:textAppearance="?android:attr/textAppearanceSmall" />
</RelativeLayout>
</LinearLayout>
-</LinearLayout>
\ No newline at end of file
+</LinearLayout>
android:layout_height="match_parent"
android:orientation="vertical" >
- <org.altusmetrum.AltosDroid.AltosMapView android:id="@+id/map_view_offline"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- />
+ <FrameLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:baselineAligned="true"
+ android:orientation="horizontal"
+ android:layout_weight="1">
+ <LinearLayout
+ android:id="@+id/map_online"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1">
+ </LinearLayout>
+ <org.altusmetrum.AltosDroid.AltosMapOffline android:id="@+id/map_view_offline"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ />
+ </FrameLayout>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
return tab_view;
}
- public void set_map_source() {
+ public void set_map_source(int source) {
+ for (AltosDroidTab mTab : mTabs)
+ mTab.set_map_source(source);
}
@Override
int source = AltosDroidPreferences.map_source();
int new_source = source == AltosDroidPreferences.MAP_SOURCE_ONLINE ? AltosDroidPreferences.MAP_SOURCE_OFFLINE : AltosDroidPreferences.MAP_SOURCE_ONLINE;
AltosDroidPreferences.set_map_source(new_source);
- set_map_source();
+ set_map_source(new_source);
return true;
case R.id.select_tracker:
if (serials != null) {
--- /dev/null
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * 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);
+}
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);
--- /dev/null
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * 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<Integer,Rocket> rockets = new HashMap<Integer,Rocket>();
+
+ 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);
+ }
+}
--- /dev/null
+/*
+ * Copyright © 2013 Mike Beattie <mike@ethernal.org>
+ *
+ * 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<Integer,RocketOnline> rockets = new HashMap<Integer,RocketOnline>();
+ 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;
+ }
+}
+++ /dev/null
-/*
- * Copyright © 2015 Keith Packard <keithp@keithp.com>
- *
- * 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);
- }
-}
setupMap();
}
};
-
}
@Override
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;
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<Integer,Rocket> rockets = new HashMap<Integer,Rocket>();
-
- /* 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
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));
}
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);
}
}
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() {