codec2_talkie/codec2talkie/src/main/java/com/radio/codec2talkie/maps/MapStations.java

216 wiersze
8.4 KiB
Java

package com.radio.codec2talkie.maps;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.util.DisplayMetrics;
import android.util.Log;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.ViewModelProvider;
import androidx.lifecycle.ViewModelStoreOwner;
import com.radio.codec2talkie.R;
import com.radio.codec2talkie.protocol.aprs.tools.AprsSymbolTable;
import com.radio.codec2talkie.protocol.position.Position;
import com.radio.codec2talkie.storage.position.PositionItemViewModel;
import com.radio.codec2talkie.storage.station.StationItem;
import com.radio.codec2talkie.storage.station.StationItemViewModel;
import com.radio.codec2talkie.tools.DateTools;
import com.radio.codec2talkie.tools.UnitTools;
import org.osmdroid.util.GeoPoint;
import org.osmdroid.views.MapView;
import org.osmdroid.views.overlay.Marker;
import org.osmdroid.views.overlay.Polygon;
import org.osmdroid.views.overlay.infowindow.MarkerInfoWindow;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
public class MapStations {
private static final String TAG = MapTrack.class.getSimpleName();
private final Context _context;
private final AprsSymbolTable _aprsSymbolTable;
private final PositionItemViewModel _positionItemViewModel;
private final ViewModelStoreOwner _owner;
private final MapView _mapView;
private final MarkerInfoWindow _infoWindow;
private LiveData<List<StationItem>> _stationItemLiveData;
private final StationItemViewModel _stationItemViewModel;
private final HashMap<String, Marker> _objectOverlayItems = new HashMap<>();
private final HashMap<String, Polygon> _objectOverlayRangeCircles = new HashMap<>();
private boolean _showCircles = false;
private boolean _showMoving = false;
private final MapTrack _activeTrack;
public MapStations(Context context, MapView mapView, ViewModelStoreOwner owner) {
_context = context;
_owner = owner;
_mapView = mapView;
_positionItemViewModel = new ViewModelProvider(_owner).get(PositionItemViewModel.class);
_aprsSymbolTable = AprsSymbolTable.getInstance(context);
_infoWindow = new MarkerInfoWindow(R.layout.bonuspack_bubble, _mapView);
_activeTrack = new MapTrack(_context, _mapView, _owner);
_stationItemViewModel = new ViewModelProvider(_owner).get(StationItemViewModel.class);
loadStations(_showMoving);
}
private void loadStations(boolean movingOnly) {
if (_stationItemLiveData != null)
_stationItemLiveData.removeObservers((LifecycleOwner) _owner);
removePositionMarkers();
_stationItemLiveData = _stationItemViewModel.getAllStationItems(movingOnly);
// FIXME, room livedata sends all list if one item changed event with distinctUntilChanged
_stationItemLiveData.observe((LifecycleOwner) _owner, allStations -> {
Log.i(TAG, "add stations " + allStations.size());
for (StationItem station : allStations) {
//Log.i(TAG, "new position " + station.getSrcCallsign() + ">" +
// station.getDstCallsign() + " " + station.getLatitude() + " " + station.getLongitude());
// do not add items without coordinate
if (station.getMaidenHead() == null) continue;
if (addStationPositionIcon(station)) {
addRangeCircle(station);
}
}
});
}
private void removePositionMarkers() {
for (Marker marker : _objectOverlayItems.values()) {
marker.remove(_mapView);
}
_objectOverlayItems.clear();
for (Polygon circle : _objectOverlayRangeCircles.values()) {
_mapView.getOverlays().remove(circle);
}
_objectOverlayRangeCircles.clear();
}
public void showMovingStations(boolean isMoving) {
_showMoving = isMoving;
loadStations(_showMoving);
}
public void showRangeCircles(boolean isVisible) {
_showCircles = isVisible;
for (Polygon polygon : _objectOverlayRangeCircles.values()) {
polygon.setVisible(isVisible);
}
}
private void addRangeCircle(StationItem group) {
if (group.getRangeMiles() == 0) return;
String callsign = group.getSrcCallsign();
Polygon polygon = null;
if (_objectOverlayRangeCircles.containsKey(callsign)) {
polygon = _objectOverlayRangeCircles.get(callsign);
assert polygon != null;
}
if (polygon == null) {
polygon = new Polygon();
polygon.setVisible(_showCircles);
Paint p = polygon.getOutlinePaint();
p.setStrokeWidth(1);
_mapView.getOverlayManager().add(0, polygon);
_objectOverlayRangeCircles.put(callsign, polygon);
}
ArrayList<GeoPoint> circlePoints = new ArrayList<>();
for (float f = 0; f < 360; f += 6) {
circlePoints.add(new GeoPoint(group.getLatitude(), group.getLongitude()).destinationPoint(1000 * UnitTools.milesToKilometers(group.getRangeMiles()), f));
}
polygon.setPoints(circlePoints);
}
private boolean addStationPositionIcon(StationItem group) {
String callsign = group.getSrcCallsign();
Marker marker = null;
String newTitle = DateTools.epochToIso8601(group.getTimestampEpoch()) + " " + callsign;
String newSnippet = getStatus(group);
// find old marker
if (_objectOverlayItems.containsKey(callsign)) {
marker = _objectOverlayItems.get(callsign);
assert marker != null;
// skip if unchanged
GeoPoint oldPosition = marker.getPosition();
if (oldPosition.getLatitude() == group.getLatitude() &&
oldPosition.getLongitude() == group.getLongitude() &&
marker.getTitle().equals(newTitle) &&
marker.getSnippet().equals(newSnippet)) {
return false;
}
}
// create new marker
if (marker == null) {
Bitmap bitmapInfoIcon = _aprsSymbolTable.bitmapFromSymbol(group.getSymbolCode(), true);
if (bitmapInfoIcon == null) return false;
// add marker
BitmapDrawable drawableText = group.drawLabelWithIcon(_context, 12);
BitmapDrawable drawableInfoIcon = new BitmapDrawable(_context.getResources(), bitmapInfoIcon);
marker = new Marker(_mapView);
marker.setId(callsign);
if (drawableText == null)
Log.e(TAG, "Cannot load icon for " + callsign);
else
marker.setIcon(drawableText);
marker.setImage(drawableInfoIcon);
marker.setOnMarkerClickListener((monitoredStationMarker, mapView) -> {
GeoPoint markerPoint = monitoredStationMarker.getPosition();
_infoWindow.open(monitoredStationMarker, new GeoPoint(markerPoint.getLatitude(), markerPoint.getLongitude()), 0, -64);
_activeTrack.drawForStationMarker(monitoredStationMarker);
return false;
});
_mapView.getOverlays().add(marker);
_objectOverlayItems.put(callsign, marker);
}
marker.setPosition(new GeoPoint(group.getLatitude(), group.getLongitude()));
marker.setTitle(newTitle);
marker.setSnippet(newSnippet);
return true;
}
private String getStatus(StationItem station) {
double range = UnitTools.milesToKilometers(station.getRangeMiles());
return String.format(Locale.US, "%s %s<br>%s %f %f<br>%03d° %03dkm/h %04dm %.2fkm<br>%s %s",
station.getDstCallsign(),
station.getDigipath(),
station.getMaidenHead(), station.getLatitude(), station.getLongitude(),
(int)station.getBearingDegrees(),
UnitTools.metersPerSecondToKilometersPerHour((int)station.getSpeedMetersPerSecond()),
(int)station.getAltitudeMeters(),
range == 0 ? UnitTools.milesToKilometers(Position.DEFAULT_RANGE_MILES): range,
station.getStatus(),
station.getComment());
}
}