/* Copyright (C) 2001, 2007 United States Government as represented by the Administrator of the National Aeronautics and Space Administration. All Rights Reserved. */ package gov.nasa.worldwind.applications.sar; import gov.nasa.worldwind.formats.gpx.GpxReader; import gov.nasa.worldwind.formats.gpx.GpxWriter; import gov.nasa.worldwind.formats.nmea.NmeaReader; import gov.nasa.worldwind.formats.nmea.NmeaWriter; import gov.nasa.worldwind.formats.csv.CSVReader; import gov.nasa.worldwind.formats.csv.CSVWriter; import gov.nasa.worldwind.geom.*; import gov.nasa.worldwind.tracks.*; import org.xml.sax.SAXException; import javax.xml.parsers.ParserConfigurationException; import java.awt.*; import java.beans.*; import java.io.*; import java.util.*; /** * @author tag * @version $Id: SARTrack.java 5261 2008-05-01 20:37:35Z dcollins $ */ public class SARTrack implements Iterable<Position> { public static final int FORMAT_GPX = 1; public static final int FORMAT_CSV = 2; public static final int FORMAT_NMEA = 3; private static int nextColor = 0; private static Color[] colors = new Color[] { Color.RED, Color.GREEN, Color.BLUE, Color.YELLOW, Color.CYAN, Color.MAGENTA, Color.ORANGE, Color.PINK, Color.WHITE }; private static Color nextColor() { return colors[nextColor++ % colors.length]; } // Meta-track properties. private File file = null; private String name = null; private int format = 0; private long lastSaveTime = 0L; private long lastModifiedTime = 0L; // Track properties. private double offset = 0; private Color color = nextColor(); private ArrayList<SARPosition> positions; private PropertyChangeSupport propChangeSupport = new PropertyChangeSupport(this); public static SARTrack fromFile(String filePath) throws IOException { File file = new File(filePath); if (!file.exists()) return null; // TODO: issue message SARTrack track = null; int format = 0; do { format++; try { if (format == FORMAT_GPX) track = readGPX(filePath); else if (format == FORMAT_CSV) track = readCSV(filePath); else if (format == FORMAT_NMEA) track = readNMEA(filePath); } catch (IllegalArgumentException e) { //noinspection UnnecessaryContinue continue; } } while (track == null && format <= 3); if (track != null) { track.setFile(file); track.setFormat(format); track.setName(file.getName()); } return track; } public static void toFile(SARTrack track, String filePath, int format) throws IOException { if (track == null) throw new IllegalArgumentException("track is null"); if (filePath == null) throw new IllegalArgumentException("filePath is null"); if (format == FORMAT_GPX) writeGPX(track, filePath); else if (format == FORMAT_CSV) writeCSV(track, filePath); else if (format == FORMAT_NMEA) writeNMEA(track, filePath); // If no format is specified, then do nothing. } private SARTrack() { } public SARTrack(String name) { this.name = name; this.positions = new ArrayList<SARPosition>(); } public File getFile() { return this.file; } public void setFile(File file) { this.file = file; } public String getName() { return name; } public void setName(String name) { this.name = name; this.firePropertyChange(TrackController.TRACK_NAME, null, this); } public int getFormat() { return format; } public void setFormat(int format) { this.format = format; } public long getLastSaveTime() { return this.lastSaveTime; } public long getLastModifiedTime() { return this.lastModifiedTime; } public boolean isDirty() { return this.lastModifiedTime == 0L || this.lastSaveTime == 0L || (this.lastModifiedTime > this.lastSaveTime); } public void markDirty() { this.lastModifiedTime = System.currentTimeMillis(); this.firePropertyChange(TrackController.TRACK_DIRTY_BIT, null, this); } public void clearDirtyBit() { long time = System.currentTimeMillis(); this.lastSaveTime = time; this.lastModifiedTime = time; this.firePropertyChange(TrackController.TRACK_DIRTY_BIT, null, this); } public Color getColor() { return color; } public void setColor(Color color) { this.color = color; } public int size() { return this.positions.size(); } public SARPosition get(int index) { return this.positions.size() > index ? this.positions.get(index) : null; } public void set(int index, SARPosition position) { if (position == null) return; if (index >= this.positions.size()) this.positions.add(position); else this.positions.set(index, position); this.markDirty(); this.firePropertyChange(TrackController.TRACK_MODIFY, null, this); } public double getOffset() { return offset; } public void setOffset(double offset) { this.offset = offset; } public Iterator<Position> iterator() { return new Iterator<Position>() { private Iterator<SARPosition> iter = SARTrack.this.positions.iterator(); public boolean hasNext() { return this.iter.hasNext(); } public Position next() { return this.iter.next(); } public void remove() { throw new UnsupportedOperationException("Remove operation not supported for SARTrack iterator"); } }; } public void removePosition(int index) { if (index < 0 || index >= this.positions.size()) return; this.positions.remove(index); this.markDirty(); this.firePropertyChange(TrackController.TRACK_MODIFY, null, this); } public void removePositions(int[] positionNumbers) { Arrays.sort(positionNumbers); for (int i = positionNumbers.length - 1; i >= 0; i--) { if (positionNumbers[i] < 0 || positionNumbers[i] >= this.positions.size()) continue; this.positions.remove(positionNumbers[i]); } this.markDirty(); this.firePropertyChange(TrackController.TRACK_MODIFY, null, this); } public void appendPosition(SARPosition position) { if (position == null) return; this.positions.add(position); this.markDirty(); this.firePropertyChange(TrackController.TRACK_MODIFY, null, this); } public void insertPosition(int index, SARPosition position) { if (position == null || index < 0) return; this.positions.add(index, position); this.markDirty(); this.firePropertyChange(TrackController.TRACK_MODIFY, null, this); } public void setPosition(int index, SARPosition position) { if (position == null || index < 0) return; this.positions.set(index, position); this.markDirty(); this.firePropertyChange(TrackController.TRACK_MODIFY, null, this); } private static SARTrack readNMEA(String filePath) throws IOException { NmeaReader reader = new NmeaReader(); reader.readFile(filePath); TrackPointIterator tpi = new TrackPointIteratorImpl(reader.getTracks()); return makeTrackFromTrackPointIterator(tpi); } private static SARTrack readGPX(String filePath) throws IOException { try { GpxReader reader = new GpxReader(); reader.readFile(filePath); TrackPointIterator tpi = new TrackPointIteratorImpl(reader.getTracks()); return makeTrackFromTrackPointIterator(tpi); } catch (ParserConfigurationException e) { throw new IllegalArgumentException(e); } catch (SAXException e) { throw new IllegalArgumentException(e); } } private static SARTrack readCSV(String filePath) throws IOException { CSVReader reader = new CSVReader(); reader.readFile(filePath); TrackPointIterator tpi = new TrackPointIteratorImpl(reader.getTracks()); return makeTrackFromTrackPointIterator(tpi); } private static void writeNMEA(SARTrack track, String filePath) throws IOException { NmeaWriter writer = new NmeaWriter(filePath); Track trk = makeTrackFromSARTrack(track); writer.writeTrack(trk); writer.close(); } private static void writeGPX(SARTrack track, String filePath) throws IOException { try { GpxWriter writer = new GpxWriter(filePath); Track trk = makeTrackFromSARTrack(track); writer.writeTrack(trk); writer.close(); } catch (ParserConfigurationException e) { throw new IllegalArgumentException(e); } catch (javax.xml.transform.TransformerException e) { throw new IllegalArgumentException(e); } } private static void writeCSV(SARTrack track, String filePath) throws IOException { CSVWriter writer = new CSVWriter(filePath); Track trk = makeTrackFromSARTrack(track); writer.writeTrack(trk); writer.close(); } private static SARTrack makeTrackFromTrackPointIterator(TrackPointIterator tpi) throws IOException { ArrayList<SARPosition> positions = new ArrayList<SARPosition>(); while (tpi.hasNext()) { TrackPoint tp = tpi.next(); SARPosition sp = new SARPosition( Angle.fromDegrees(tp.getLatitude()), Angle.fromDegrees(tp.getLongitude()), tp.getElevation()); positions.add(sp); } SARTrack st = new SARTrack(); st.positions = positions; return st; } private static Track makeTrackFromSARTrack(SARTrack sarTrack) { return new TrackWrapper(sarTrack); } private static class TrackWrapper implements Track, TrackSegment { private final SARTrack sarTrack; private final ArrayList<TrackSegment> segments = new ArrayList<TrackSegment>(); public TrackWrapper(SARTrack sarTrack) { this.sarTrack = sarTrack; this.segments.add(this); } public java.util.List<TrackSegment> getSegments() { return this.segments; } public String getName() { return this.sarTrack.getName(); } public int getNumPoints() { return this.sarTrack.size(); } public java.util.List<TrackPoint> getPoints() { ArrayList<TrackPoint> trkPoints = new ArrayList<TrackPoint>(); for (SARPosition sarPos : this.sarTrack.positions) trkPoints.add(sarPos != null ? new TrackPointWrapper(sarPos) : null); return trkPoints; } } private static class TrackPointWrapper implements TrackPoint { private final SARPosition sarPosition; public TrackPointWrapper(SARPosition sarPosition) { this.sarPosition = sarPosition; } public double getLatitude() { return this.sarPosition.getLatitude().degrees; } public void setLatitude(double latitude) { } public double getLongitude() { return this.sarPosition.getLongitude().degrees; } public void setLongitude(double longitude) { } public double getElevation() { return this.sarPosition.getElevation(); } public void setElevation(double elevation) { } public String getTime() { return null; } public void setTime(String time) { } public Position getPosition() { return this.sarPosition; } public void setPosition(Position position) { } } public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) { this.propChangeSupport.addPropertyChangeListener(propertyName, listener); } public void addPropertyChangeListener(PropertyChangeListener listener) { this.propChangeSupport.addPropertyChangeListener(listener); } public void firePropertyChange(String propertyName, Object oldValue, Object newValue) { this.propChangeSupport.firePropertyChange(propertyName, oldValue, newValue); } @Override public String toString() { return this.name; } }