// License: GPL. For details, see LICENSE file. package org.openstreetmap.josm.plugins.videomapping; import static org.openstreetmap.josm.tools.I18n.tr; import java.awt.Color; import java.awt.Graphics2D; import java.awt.Point; import java.awt.Rectangle; import java.awt.event.MouseEvent; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Collections; import java.util.Date; import java.util.LinkedList; import java.util.List; import javax.swing.Action; import javax.swing.Icon; import javax.swing.ImageIcon; import org.openstreetmap.josm.Main; import org.openstreetmap.josm.data.Bounds; import org.openstreetmap.josm.data.coor.LatLon; import org.openstreetmap.josm.data.gpx.GpxData; import org.openstreetmap.josm.data.gpx.GpxTrack; import org.openstreetmap.josm.data.gpx.GpxTrackSegment; import org.openstreetmap.josm.data.gpx.WayPoint; import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor; import org.openstreetmap.josm.gui.MapView; import org.openstreetmap.josm.gui.dialogs.LayerListDialog; import org.openstreetmap.josm.gui.dialogs.LayerListPopup; import org.openstreetmap.josm.gui.layer.GpxLayer; import org.openstreetmap.josm.gui.layer.Layer; import org.openstreetmap.josm.plugins.videomapping.video.GPSVideoPlayer; //Basic rendering and GPS layer interaction public class VideoPositionLayer extends Layer { private List<WayPoint> gpsTrack; private ImageIcon layerIcon; private DateFormat gpsTimeFormat; private WayPoint iconPosition; private final int GPS_INTERVALL = 1000; private GPSVideoPlayer gpsVideoPlayer; private boolean autoCenter; public VideoPositionLayer(GpxLayer gpsLayer) { super("videolayer"); layerIcon = new ImageIcon("images/videomapping.png"); gpsTrack = importGPSLayer(gpsLayer.data); gpsTimeFormat = new SimpleDateFormat("HH:mm:ss"); iconPosition = gpsTrack.get(0); Main.getLayerManager().addLayer(this); } //make a flat copy private List<WayPoint> importGPSLayer(GpxData gps) { LinkedList<WayPoint> ls = new LinkedList<>(); for (GpxTrack trk : gps.tracks) { for (GpxTrackSegment segment : trk.getSegments()) { ls.addAll(segment.getWayPoints()); } } Collections.sort(ls); //sort basing upon time return ls; } @Override public void paint(Graphics2D g, MapView map, Bounds bound) { paintGpsTrack(g, map); paintSyncedTrack(g, map); paintPositionIcon(g, map); //paintInterpolatedSegment(g); //just a test for my own } private void paintGpsTrack(Graphics2D g, MapView map) { g.setColor(Color.YELLOW); for (WayPoint n: gpsTrack) { Point p = map.getPoint(n.getEastNorth()); g.drawOval(p.x - 2, p.y - 2, 4, 4); } } private void paintSyncedTrack(Graphics2D g, MapView map) { g.setColor(Color.GREEN); for (WayPoint n : gpsTrack) { if (n.attr.containsKey("synced")) { Point p = map.getPoint(n.getEastNorth()); g.drawOval(p.x - 2, p.y - 2, 4, 4); } } } private void paintPositionIcon(Graphics2D g, MapView map) { if (iconPosition != null) { Point p = map.getPoint(iconPosition.getEastNorth()); layerIcon.paintIcon(null, g, p.x-layerIcon.getIconWidth()/2, p.y-layerIcon.getIconHeight()/2); g.drawString(gpsTimeFormat.format(iconPosition.getTime()), p.x-15, p.y-15); } } /* private void paintInterpolatedSegment(Graphics2D g) { g.setColor(Color.CYAN); List<WayPoint>ls=getInterpolatedSegment(iconPosition,5,5); for(WayPoint n: ls) { Point p = Main.map.mapView.getPoint(n.getEastNorth()); g.drawOval(p.x - 2, p.y - 2, 4, 4); } } //just a Demo to show up IPO on a whole segment private List<WayPoint> getInterpolatedSegment(WayPoint center, int before, int after) { LinkedList<WayPoint> ls = new LinkedList<WayPoint>(); if(gpsTrack.indexOf(iconPosition)!=0) { WayPoint prev=gpsTrack.get(gpsTrack.indexOf(iconPosition)-1); for(int i=1;i<=before;i++) { ls.add(interpolate(prev,(float)100f/before*i)); } } for(int i=1;i<=after;i++) { ls.add(interpolate(iconPosition,(float)100f/before*i)); } //test code Date test=getFirstWayPoint().getTime(); test.setHours(14); test.setMinutes(50); test.setSeconds(33); ls.add(getWayPointBefore(new Date(test.getTime()+500))); ls.add(interpolate(new Date(test.getTime()+500))); return ls; } */ //creates a waypoint for the corresponding time public WayPoint interpolate(Date GPSTime) { WayPoint before = getWayPointBefore(GPSTime); if (before == null) { return null; } long diff = GPSTime.getTime() - before.getTime().getTime(); assert diff >= 0; assert diff < GPS_INTERVALL; float perc = ((float) diff/(float) GPS_INTERVALL)*100; return interpolate(before, perc); } public WayPoint getWayPointBefore(Date GPSTime) { assert GPSTime.after(getFirstWayPoint().getTime()) == true; assert GPSTime.before(getLastWayPoint().getTime()) == true; Date first = getFirstWayPoint().getTime(); long diff = GPSTime.getTime()-first.getTime(); //assumes that GPS intervall is constant int id = (int) (diff/GPS_INTERVALL); return 0 <= id && id < gpsTrack.size() ? gpsTrack.get(id) : null; } public WayPoint getFirstWayPoint() { return gpsTrack.isEmpty() ? null : gpsTrack.get(0); } public WayPoint getLastWayPoint() { return gpsTrack.isEmpty() ? null : gpsTrack.get(gpsTrack.size()-1); } //interpolates a waypoint between this and the following waypoint at percent private WayPoint interpolate(WayPoint first, float percent) { assert (percent > 0); assert (percent < 100); double dX, dY; WayPoint leftP, rightP; WayPoint next = gpsTrack.get(gpsTrack.indexOf(first)+1); //determine which point is what leftP = getLeftPoint(first, next); rightP = getRightPoint(first, next); //calc increment percent = percent/100; dX = (rightP.getCoor().lon()-leftP.getCoor().lon())*percent; dY = (rightP.getCoor().lat()-leftP.getCoor().lat())*percent; //move in the right direction if (first == leftP) { return new WayPoint(new LatLon(leftP.getCoor().lat()+dY, leftP.getCoor().lon()+dX)); } else { return new WayPoint(new LatLon(rightP.getCoor().lat()-dY, rightP.getCoor().lon()-dX)); } } private WayPoint getLeftPoint(WayPoint p1, WayPoint p2) { if (p1.getCoor().lon() < p2.getCoor().lon()) return p1; else return p2; } private WayPoint getRightPoint(WayPoint p1, WayPoint p2) { if (p1.getCoor().lon() > p2.getCoor().lon()) return p1; else return p2; } public Date getGPSDate() { return iconPosition.getTime(); } public WayPoint getCurrentWayPoint() { return iconPosition; } public List<WayPoint> getTrack() { return gpsTrack; } public void jump(Date GPSTime) { setIconPosition(getWayPointBefore(GPSTime)); } public void setIconPosition(WayPoint wp) { iconPosition = wp; if (Main.isDisplayingMapView()) { Main.map.mapView.repaint(); if (autoCenter) Main.map.mapView.zoomTo(iconPosition.getCoor()); } } public void mouseReleased(MouseEvent e) { //only leftclicks on our layer if (e.getButton() == MouseEvent.BUTTON1) { WayPoint wp = getNearestWayPoint(e.getPoint()); if (wp != null) { if (gpsVideoPlayer.areAllVideosSynced()) { //we set the video to corresponding position gpsVideoPlayer.jumpTo(wp.getTime()); } setIconPosition(wp); } } } //finds the first waypoint that is nearby the given point private WayPoint getNearestWayPoint(Point mouse) { final int MAX = 10; Point p; Rectangle rect = new Rectangle(mouse.x-MAX/2, mouse.y-MAX/2, MAX, MAX); //iterate through all possible notes for (WayPoint n : gpsTrack) { p = Main.map.mapView.getPoint(n.getEastNorth()); if (rect.contains(p)) { return n; } } return null; } @Override public Icon getIcon() { return layerIcon; } @Override public Object getInfoComponent() { // TODO Auto-generated method stub return null; } @Override public Action[] getMenuEntries() { return new Action[]{ LayerListDialog.getInstance().createActivateLayerAction(this), LayerListDialog.getInstance().createShowHideLayerAction(), LayerListDialog.getInstance().createDeleteLayerAction(), SeparatorLayerAction.INSTANCE, new LayerListPopup.InfoAction(this)}; } @Override public String getToolTipText() { return tr("Shows current position in the video"); } @Override public boolean isMergable(Layer arg0) { return false; } @Override public void mergeFrom(Layer arg0) { } @Override public void visitBoundingBox(BoundingXYVisitor arg0) { } public void setGPSVideoPlayer(GPSVideoPlayer player) { gpsVideoPlayer = player; } public void setAutoCenter(boolean enabled) { autoCenter = enabled; } public void unload() { Main.getLayerManager().removeLayer(this); } }