#2340 Update map plotting to address glitchy behavior when selecting entities and histories. (#2341)

Co-authored-by: denny <dennis.sheirer@gmail.com>
This commit is contained in:
Denny Sheirer
2025-12-26 05:40:57 -05:00
committed by GitHub
parent b83f77c7c6
commit a2509554db
6 changed files with 160 additions and 233 deletions

View File

@@ -51,7 +51,6 @@ import javax.swing.JToggleButton;
import javax.swing.SpinnerNumberModel; import javax.swing.SpinnerNumberModel;
import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener; import javax.swing.event.ChangeListener;
import javax.swing.event.TableModelEvent;
/** /**
* Swing map panel. * Swing map panel.
@@ -85,9 +84,9 @@ public class MapPanel extends JPanel implements IPlottableUpdateListener
private JComboBox<Integer> mTrackHistoryLengthComboBox; private JComboBox<Integer> mTrackHistoryLengthComboBox;
private JSpinner mMapZoomSpinner; private JSpinner mMapZoomSpinner;
private SpinnerNumberModel mMapZoomSpinnerModel; private SpinnerNumberModel mMapZoomSpinnerModel;
private TrackHistoryModel mTrackHistoryModel = new TrackHistoryModel();
private JTable mTrackHistoryTable; private JTable mTrackHistoryTable;
private JLabel mSelectedTrackSystemLabel; private JLabel mSelectedTrackSystemLabel;
private final TrackHistoryModel EMPTY_HISTORY = new TrackHistoryModel();
/** /**
* Constructs an instance * Constructs an instance
@@ -138,6 +137,7 @@ public class MapPanel extends JPanel implements IPlottableUpdateListener
cp.add(new JScrollPane(getTrackHistoryTable()), "span 3,wrap"); cp.add(new JScrollPane(getTrackHistoryTable()), "span 3,wrap");
//Uncomment this line to add a debug track generator button to the display
// cp.add(getTrackGeneratorToggle(), "span 3,wrap"); // cp.add(getTrackGeneratorToggle(), "span 3,wrap");
JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
@@ -149,10 +149,10 @@ public class MapPanel extends JPanel implements IPlottableUpdateListener
private void setSelected(PlottableEntityHistory selected) private void setSelected(PlottableEntityHistory selected)
{ {
mTrackHistoryModel.load(selected);
if(selected != null) if(selected != null)
{ {
getTrackHistoryTable().setModel(selected.getTrackHistoryModel());
Identifier system = selected.getIdentifierCollection().getIdentifier(IdentifierClass.CONFIGURATION, Form.SYSTEM, Role.ANY); Identifier system = selected.getIdentifierCollection().getIdentifier(IdentifierClass.CONFIGURATION, Form.SYSTEM, Role.ANY);
if(system != null) if(system != null)
@@ -171,6 +171,7 @@ public class MapPanel extends JPanel implements IPlottableUpdateListener
} }
else else
{ {
getTrackHistoryTable().setModel(EMPTY_HISTORY);
getSelectedTrackSystemLabel().setText(SELECT_A_TRACK); getSelectedTrackSystemLabel().setText(SELECT_A_TRACK);
} }
@@ -217,13 +218,13 @@ public class MapPanel extends JPanel implements IPlottableUpdateListener
{ {
if(mTrackHistoryTable == null) if(mTrackHistoryTable == null)
{ {
mTrackHistoryTable = new JTable(mTrackHistoryModel); mTrackHistoryTable = new JTable(EMPTY_HISTORY);
mTrackHistoryTable.getSelectionModel().addListSelectionListener(e -> mTrackHistoryTable.getSelectionModel().addListSelectionListener(e ->
{ {
if(getCenterOnSelectedCheckBox().isSelected()) if(getCenterOnSelectedCheckBox().isSelected())
{ {
int modelIndex = getTrackHistoryTable().convertRowIndexToModel(getTrackHistoryTable().getSelectedRow()); int modelIndex = getTrackHistoryTable().convertRowIndexToModel(getTrackHistoryTable().getSelectedRow());
TimestampedGeoPosition geo = mTrackHistoryModel.get(modelIndex); TimestampedGeoPosition geo = ((TrackHistoryModel)(getTrackHistoryTable().getModel())).get(modelIndex);
if(geo != null) if(geo != null)
{ {
@@ -469,64 +470,6 @@ public class MapPanel extends JPanel implements IPlottableUpdateListener
{ {
mPlottedTracksTable = new JTable(mMapService.getPlottableEntityModel()); mPlottedTracksTable = new JTable(mMapService.getPlottableEntityModel());
mPlottedTracksTable.setAutoCreateRowSorter(true); mPlottedTracksTable.setAutoCreateRowSorter(true);
mMapService.getPlottableEntityModel().addTableModelListener(e ->
{
//Update the followed entity for DELETE/UPDATE operations
if(mFollowedTrack != null)
{
if(e.getType() == TableModelEvent.DELETE)
{
for(int x = e.getFirstRow(); x <= e.getLastRow(); x++)
{
if(mMapService.getPlottableEntityModel().get(x).equals(mFollowedTrack))
{
follow(null);
return;
}
}
}
else if(e.getType() == TableModelEvent.UPDATE)
{
if(e.getFirstRow() == 0 && e.getLastRow() == Integer.MAX_VALUE)
{
return;
}
for(int x = e.getFirstRow(); x <= e.getLastRow(); x++)
{
PlottableEntityHistory entity = mMapService.getPlottableEntityModel().get(x);
if(entity.equals(getSelected()))
{
mTrackHistoryModel.update();
}
if(entity != null && entity.equals(mFollowedTrack))
{
centerOn(mMapService.getPlottableEntityModel().get(x));
return;
}
}
}
}
if(e.getType() == TableModelEvent.UPDATE && !(e.getFirstRow() == 0 && e.getLastRow() == Integer.MAX_VALUE))
{
for(int x = e.getFirstRow(); x <= e.getLastRow(); x++)
{
PlottableEntityHistory entity = mMapService.getPlottableEntityModel().get(x);
if(entity != null && entity.equals(getSelected()))
{
mTrackHistoryModel.update();
}
}
}
else if(e.getType() == TableModelEvent.DELETE && getSelected() == null)
{
mTrackHistoryModel.load(null);
}
});
//Register selection listener to update button/label states //Register selection listener to update button/label states
mPlottedTracksTable.getSelectionModel().addListSelectionListener(e -> mPlottedTracksTable.getSelectionModel().addListSelectionListener(e ->

View File

@@ -1,6 +1,6 @@
/* /*
* ***************************************************************************** * *****************************************************************************
* Copyright (C) 2014-2024 Dennis Sheirer * Copyright (C) 2014-2025 Dennis Sheirer
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -22,19 +22,15 @@ package io.github.dsheirer.map;
import io.github.dsheirer.identifier.Identifier; import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.identifier.IdentifierCollection; import io.github.dsheirer.identifier.IdentifierCollection;
import io.github.dsheirer.module.decode.event.PlottableDecodeEvent; import io.github.dsheirer.module.decode.event.PlottableDecodeEvent;
import java.util.ArrayList;
import java.util.List;
import org.jdesktop.swingx.mapviewer.GeoPosition;
/** /**
* Plottable entity history with location history. * Plottable entity history with location history.
*/ */
public class PlottableEntityHistory public class PlottableEntityHistory
{ {
public static final int MAX_LOCATION_HISTORY = 10; private final TrackHistoryModel mTrackHistoryModel = new TrackHistoryModel();
private List<TimestampedGeoPosition> mLocationHistory = new ArrayList<>();
private PlottableDecodeEvent mCurrentEvent; private PlottableDecodeEvent mCurrentEvent;
private Identifier mIdentifier; private final Identifier mIdentifier;
/** /**
* Constructs a plottable entity history * Constructs a plottable entity history
@@ -42,28 +38,24 @@ public class PlottableEntityHistory
public PlottableEntityHistory(Identifier identifier, PlottableDecodeEvent event) public PlottableEntityHistory(Identifier identifier, PlottableDecodeEvent event)
{ {
mIdentifier = identifier; mIdentifier = identifier;
mCurrentEvent = event;
add(event); add(event);
} }
/** /**
* Location history for this entity * Location history for this entity
*/ */
public List<TimestampedGeoPosition> getLocationHistory() public TrackHistoryModel getTrackHistoryModel()
{ {
return new ArrayList<>(mLocationHistory); return mTrackHistoryModel;
} }
/** /**
* Latest position for this entity. * Latest position for this history
*/ */
public TimestampedGeoPosition getLatestPosition() public TimestampedGeoPosition getLatestPosition()
{ {
if(mLocationHistory.size() > 0) return mTrackHistoryModel.get(0);
{
return mLocationHistory.get(0);
}
return null;
} }
/** /**
@@ -89,43 +81,9 @@ public class PlottableEntityHistory
{ {
if(event.isValidLocation()) if(event.isValidLocation())
{ {
TimestampedGeoPosition mostRecentPosition = getLatestPosition(); mTrackHistoryModel.add(new TimestampedGeoPosition(event.getLocation(), event.getTimeStart()));
TimestampedGeoPosition latest = new TimestampedGeoPosition(event.getLocation(), event.getTimeStart());
if(isUnique(latest, mostRecentPosition))
{
mCurrentEvent = event;
mLocationHistory.add(0, latest);
while(mLocationHistory.size() > MAX_LOCATION_HISTORY)
{
mLocationHistory.remove(mLocationHistory.size() - 1);
}
}
}
}
/**
* Indicates if the latest time and position is at least 30 seconds newer than the previous position and either the
* latitude or longitude differs by at least 0.00001 degrees.
* @param latest location
* @param previous location
* @return indication of uniqueness.
*/
private boolean isUnique(TimestampedGeoPosition latest, TimestampedGeoPosition previous)
{
if(latest != null && previous == null)
{
return true;
} }
if(latest != null && previous != null) mCurrentEvent = event;
{
return latest.getTimestamp() > (previous.getTimestamp() + 30_000) ||
Math.abs(latest.getLatitude() - previous.getLatitude()) > 0.00001 ||
Math.abs(latest.getLongitude() - previous.getLongitude()) > 0.00001;
}
return false;
} }
} }

View File

@@ -1,6 +1,6 @@
/* /*
* ***************************************************************************** * *****************************************************************************
* Copyright (C) 2014-2024 Dennis Sheirer * Copyright (C) 2014-2025 Dennis Sheirer
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -80,9 +80,9 @@ public class PlottableEntityRenderer
*/ */
public void paintPlottableEntity(Graphics2D g, JXMapViewer viewer, PlottableEntityHistory entity, boolean antiAliasing) public void paintPlottableEntity(Graphics2D g, JXMapViewer viewer, PlottableEntityHistory entity, boolean antiAliasing)
{ {
List<TimestampedGeoPosition> locationHistory = entity.getLocationHistory(); List<TimestampedGeoPosition> locationHistory = entity.getTrackHistoryModel().getTrackHistory();
if(!locationHistory.isEmpty() && locationHistory.get(locationHistory.size() - 1).isValid()) if(!locationHistory.isEmpty() && locationHistory.getLast().isValid())
{ {
Graphics2D graphics = (Graphics2D)g.create(); Graphics2D graphics = (Graphics2D)g.create();
@@ -171,7 +171,7 @@ public class PlottableEntityRenderer
*/ */
private void paintRoute(Graphics2D graphics, JXMapViewer viewer, PlottableEntityHistory entity, Color color) private void paintRoute(Graphics2D graphics, JXMapViewer viewer, PlottableEntityHistory entity, Color color)
{ {
List<TimestampedGeoPosition> locations = entity.getLocationHistory(); List<TimestampedGeoPosition> locations = entity.getTrackHistoryModel().getTrackHistory();
if(!locations.isEmpty()) if(!locations.isEmpty())
{ {

View File

@@ -1,6 +1,6 @@
/* /*
* ***************************************************************************** * *****************************************************************************
* Copyright (C) 2014-2024 Dennis Sheirer * Copyright (C) 2014-2025 Dennis Sheirer
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -28,6 +28,7 @@ import io.github.dsheirer.identifier.configuration.SystemConfigurationIdentifier
import io.github.dsheirer.module.decode.dmr.identifier.DMRRadio; import io.github.dsheirer.module.decode.dmr.identifier.DMRRadio;
import io.github.dsheirer.module.decode.event.DecodeEventType; import io.github.dsheirer.module.decode.event.DecodeEventType;
import io.github.dsheirer.module.decode.event.PlottableDecodeEvent; import io.github.dsheirer.module.decode.event.PlottableDecodeEvent;
import io.github.dsheirer.module.decode.p25.identifier.radio.APCO25RadioIdentifier;
import io.github.dsheirer.protocol.Protocol; import io.github.dsheirer.protocol.Protocol;
import io.github.dsheirer.util.ThreadPool; import io.github.dsheirer.util.ThreadPool;
import java.util.ArrayList; import java.util.ArrayList;
@@ -44,13 +45,9 @@ import org.jdesktop.swingx.mapviewer.GeoPosition;
*/ */
public class TrackGenerator public class TrackGenerator
{ {
private static GeoPosition DEFAULT_START_POSITION = new GeoPosition(43.048, -76.147); private static final GeoPosition DEFAULT_START_POSITION = new GeoPosition(43.048, -76.147);
private static final String ALIAS_LIST_NAME = "DMR Test Alias List"; private final MapService mMapService;
private MapService mMapService; private final List<TrackElementGenerator> mTrackElementGenerators = new ArrayList<>();
private List<TrackElementGenerator> mTrackElementGenerators = new ArrayList<>();
private double mBaseSpeedKPH = 40.0;
private int mTrackCount = 25;
private ScheduledFuture<?> mGeneratorFuture;
/** /**
* Constructs an instance to publish tracks to the specified map service * Constructs an instance to publish tracks to the specified map service
@@ -60,19 +57,27 @@ public class TrackGenerator
{ {
mMapService = mapService; mMapService = mapService;
MutableIdentifierCollection base = new MutableIdentifierCollection();
base.update(SystemConfigurationIdentifier.create("Test System"));
base.update(SiteConfigurationIdentifier.create("Test Site"));
base.update(FrequencyConfigurationIdentifier.create(155000000l));
base.update(AliasListConfigurationIdentifier.create(ALIAS_LIST_NAME));
Random random = new Random(); Random random = new Random();
for(int x = 0; x < mTrackCount; x++) int trackCount = 25;
for(int x = 0; x < trackCount; x++)
{ {
MutableIdentifierCollection mic = new MutableIdentifierCollection(base.getIdentifiers()); MutableIdentifierCollection mic = new MutableIdentifierCollection();
mic.update(DMRRadio.createFrom(x + 1)); mic.update(SystemConfigurationIdentifier.create("Test System"));
double speedKPH = mBaseSpeedKPH + (random.nextDouble() * 15); mic.update(SiteConfigurationIdentifier.create("Test Site"));
mic.update(FrequencyConfigurationIdentifier.create(155000000l));
if(x % 2 == 1)
{
mic.update(AliasListConfigurationIdentifier.create("DMR " + x + " LIST"));
mic.update(DMRRadio.createFrom(x + 1));
}
else
{
mic.update(AliasListConfigurationIdentifier.create("P25 " + x + " LIST"));
mic.update(APCO25RadioIdentifier.createFrom(x + 1));
}
double baseSpeedKPH = 20.0;
double speedKPH = baseSpeedKPH + random.nextDouble(15);
mTrackElementGenerators.add(new TrackElementGenerator(speedKPH, mic)); mTrackElementGenerators.add(new TrackElementGenerator(speedKPH, mic));
} }
} }
@@ -82,9 +87,9 @@ public class TrackGenerator
*/ */
public void start() public void start()
{ {
if(mGeneratorFuture == null) for(TrackElementGenerator generator: mTrackElementGenerators)
{ {
mGeneratorFuture = ThreadPool.SCHEDULED.scheduleAtFixedRate(() -> update(), 0, 1, TimeUnit.SECONDS); generator.start();
} }
} }
@@ -93,59 +98,81 @@ public class TrackGenerator
*/ */
public void stop() public void stop()
{ {
if(mGeneratorFuture != null) for(TrackElementGenerator generator: mTrackElementGenerators)
{ {
mGeneratorFuture.cancel(true); generator.stop();
mGeneratorFuture = null;
}
}
/**
* Updates each of the track generates causing them to dispatch a new decode event to the map service.
*
* Invoke this method once per second.
*/
private void update()
{
for(TrackElementGenerator tg: mTrackElementGenerators)
{
mMapService.receive(tg.update());
} }
} }
/** /**
* Test track element generator * Test track element generator
*/ */
public static class TrackElementGenerator public class TrackElementGenerator
{ {
public static double EARTH_RADIUS_KM = 6378.137; public static double EARTH_RADIUS_KM = 6378.137;
public static double ONE_SECOND = 1.0 / 60.0 / 60.0; //1 hour divided by 60 minutes divided by 60 seconds. public static double ONE_SECOND = 1.0 / 60.0 / 60.0; //1 hour divided by 60 minutes divided by 60 seconds.
private IdentifierCollection mIdentifierCollection; private IdentifierCollection mIdentifierCollection;
private double mSpeedKPH; private double mSpeedKPH;
private GeoPosition mPosition = new GeoPosition(DEFAULT_START_POSITION.getLatitude(), DEFAULT_START_POSITION.getLongitude()); private GeoPosition mPosition;
private Random mRandom = new Random(); private Random mRandom = new Random();
private double mHeading = 360.0 * mRandom.nextDouble(); private double mHeading = 360.0 * mRandom.nextDouble();
private ScheduledFuture<?> mGeneratorFuture;
/** /**
* Constructs an instance * Constructs an instance
* @param trackId for the track
* @param speedKPH speed in KPH * @param speedKPH speed in KPH
* @param identifierCollection for the track
*/ */
public TrackElementGenerator(double speedKPH, IdentifierCollection identifierCollection) public TrackElementGenerator(double speedKPH, IdentifierCollection identifierCollection)
{ {
mSpeedKPH = speedKPH; mSpeedKPH = speedKPH;
mIdentifierCollection = identifierCollection; mIdentifierCollection = identifierCollection;
double latOffset = (1.0 - (mRandom.nextFloat() * 2)) / 800;
double lonOffset = (1.0 - (mRandom.nextFloat() * 2)) / 800;
mPosition = new GeoPosition(DEFAULT_START_POSITION.getLatitude() + latOffset,
DEFAULT_START_POSITION.getLongitude() + lonOffset);
} }
/** /**
* * Starts this track generator
* @param heading 0-360 degrees
*/ */
public PlottableDecodeEvent update() public void start()
{ {
mHeading = mHeading + (15.0 - (mRandom.nextDouble() * 30.0)); if(mGeneratorFuture == null)
mHeading = Math.max(mHeading, 0); {
mHeading = Math.min(mHeading, 360.0); long intervalMS = 500 + (mRandom.nextLong(1000));
mSpeedKPH /= (intervalMS / 1000.0);
System.out.println("Speed: " + mSpeedKPH);
mGeneratorFuture = ThreadPool.SCHEDULED.scheduleAtFixedRate(this::update, 0, intervalMS, TimeUnit.MILLISECONDS);
}
}
/**
* Stops this track generator
*/
public void stop()
{
if(mGeneratorFuture != null)
{
mGeneratorFuture.cancel(true);
mGeneratorFuture = null;
}
}
/**
* Pushes a new update to the map service.
*/
public void update()
{
mHeading = mHeading + (30.0 - (mRandom.nextDouble(60)));
if(mHeading < 0)
{
mHeading += 360;
}
else if(mHeading >= 360)
{
mHeading -= 360;
}
double distanceKM = mSpeedKPH * ONE_SECOND; double distanceKM = mSpeedKPH * ONE_SECOND;
double headingRadians = Math.toRadians(mHeading); double headingRadians = Math.toRadians(mHeading);
@@ -168,7 +195,7 @@ public class TrackGenerator
.protocol(Protocol.DMR) .protocol(Protocol.DMR)
.identifiers(mIdentifierCollection) .identifiers(mIdentifierCollection)
.build(); .build();
return event; mMapService.receive(event);
} }
} }
} }

View File

@@ -1,6 +1,6 @@
/* /*
* ***************************************************************************** * *****************************************************************************
* Copyright (C) 2014-2024 Dennis Sheirer * Copyright (C) 2014-2025 Dennis Sheirer
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -28,15 +28,15 @@ import java.util.List;
import javax.swing.table.AbstractTableModel; import javax.swing.table.AbstractTableModel;
/** /**
* Table model for track history. * Track history table model.
*/ */
public class TrackHistoryModel extends AbstractTableModel public class TrackHistoryModel extends AbstractTableModel
{ {
public static final int MAX_LOCATION_HISTORY = 10;
private static final String[] COLUMNS = new String[]{"Time", "Latitude", "Longitude"}; private static final String[] COLUMNS = new String[]{"Time", "Latitude", "Longitude"};
private SimpleDateFormat mSimpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); private final SimpleDateFormat mSimpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
private DecimalFormat mDegreeFormat = new DecimalFormat("0.00000"); private final DecimalFormat mDegreeFormat = new DecimalFormat("0.00000");
private List<TimestampedGeoPosition> mTimestampedGeoPositions = new ArrayList<>(); private final List<TimestampedGeoPosition> mTimestampedGeoPositions = new ArrayList<>();
private PlottableEntityHistory mPlottableEntityHistory;
/** /**
* Constructs an instance * Constructs an instance
@@ -45,6 +45,65 @@ public class TrackHistoryModel extends AbstractTableModel
{ {
} }
/**
* Complete history for this entity.
*/
public List<TimestampedGeoPosition> getTrackHistory()
{
return Collections.unmodifiableList(mTimestampedGeoPositions);
}
/**
* Adds the position to the history if it is unique and trims the history to MAX_LOCATION_HISTORY size.
* @param latest position to add.
*/
public void add(TimestampedGeoPosition latest)
{
TimestampedGeoPosition mostRecentPosition = null;
if(!mTimestampedGeoPositions.isEmpty())
{
mostRecentPosition = mTimestampedGeoPositions.getFirst();
}
if(isUnique(latest, mostRecentPosition))
{
mTimestampedGeoPositions.addFirst(latest);
fireTableRowsInserted(0, 0);
while(mTimestampedGeoPositions.size() > MAX_LOCATION_HISTORY)
{
int index = mTimestampedGeoPositions.size() - 1;
mTimestampedGeoPositions.removeLast();
fireTableRowsDeleted(index, index);
}
}
}
/**
* Indicates if the latest time and position is at least 30 seconds newer than the previous position and either the
* latitude or longitude differs by at least 0.00001 degrees.
* @param latest location
* @param previous location
* @return indication of uniqueness.
*/
private boolean isUnique(TimestampedGeoPosition latest, TimestampedGeoPosition previous)
{
if(latest != null && previous == null)
{
return true;
}
if(latest != null)
{
return latest.getTimestamp() > (previous.getTimestamp() + 2_000) ||
Math.abs(latest.getLatitude() - previous.getLatitude()) > 0.00001 ||
Math.abs(latest.getLongitude() - previous.getLongitude()) > 0.00001;
}
return false;
}
/** /**
* Get the geo position at the specified index * Get the geo position at the specified index
* @param index to retrieve * @param index to retrieve
@@ -52,7 +111,7 @@ public class TrackHistoryModel extends AbstractTableModel
*/ */
public TimestampedGeoPosition get(int index) public TimestampedGeoPosition get(int index)
{ {
if(index < mTimestampedGeoPositions.size()) if(index >= 0 && index < mTimestampedGeoPositions.size())
{ {
return mTimestampedGeoPositions.get(index); return mTimestampedGeoPositions.get(index);
} }
@@ -60,53 +119,6 @@ public class TrackHistoryModel extends AbstractTableModel
return null; return null;
} }
/**
* Loads the plottable entity history into this model
* @param plottableEntityHistory to load
*/
public void load(PlottableEntityHistory plottableEntityHistory)
{
mPlottableEntityHistory = plottableEntityHistory;
if(mTimestampedGeoPositions.size() > 0)
{
int lastRow = mTimestampedGeoPositions.size() - 1;
mTimestampedGeoPositions.clear();
fireTableRowsDeleted(0, lastRow);
}
if(mPlottableEntityHistory != null)
{
mTimestampedGeoPositions.addAll(mPlottableEntityHistory.getLocationHistory());
if(mTimestampedGeoPositions.size() > 0)
{
fireTableRowsInserted(0, mTimestampedGeoPositions.size() - 1);
}
}
}
/**
* Updates or refreshes the track history from the current plottable entity.
*/
public void update()
{
if(mPlottableEntityHistory != null)
{
List<TimestampedGeoPosition> geos = mPlottableEntityHistory.getLocationHistory();
Collections.reverse(geos);
for(TimestampedGeoPosition geo: geos)
{
if(!mTimestampedGeoPositions.contains(geo))
{
mTimestampedGeoPositions.add(0, geo);
fireTableRowsInserted(0, 0);
}
}
}
}
@Override @Override
public int getRowCount() public int getRowCount()
{ {

View File

@@ -19,7 +19,6 @@
package io.github.dsheirer.module.decode.p25.phase1.message.lc.motorola; package io.github.dsheirer.module.decode.p25.phase1.message.lc.motorola;
import io.github.dsheirer.bits.BinaryMessage;
import io.github.dsheirer.bits.CorrectedBinaryMessage; import io.github.dsheirer.bits.CorrectedBinaryMessage;
import io.github.dsheirer.bits.IntField; import io.github.dsheirer.bits.IntField;
import io.github.dsheirer.identifier.Identifier; import io.github.dsheirer.identifier.Identifier;
@@ -123,16 +122,4 @@ public class LCMotorolaUnitGPS extends LinkControlWord
return mIdentifiers; return mIdentifiers;
} }
static void main()
{
BinaryMessage mick = BinaryMessage.load("000001101001000000000000101100011010110101010011011000101001000001000010");
BinaryMessage newj = BinaryMessage.load("000001101001000000000000001110010000011010111110110010110011100011000010");
LCMotorolaUnitGPS motoMick = new LCMotorolaUnitGPS(new CorrectedBinaryMessage(mick));
LCMotorolaUnitGPS motoNewJ = new LCMotorolaUnitGPS(new CorrectedBinaryMessage(newj));
System.out.println("MICK: " + motoMick);
System.out.println("NEWJ: " + motoNewJ);
}
} }