/* * -------------------------------------------------------------- * * 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; either version 2 * of the License, or (at your option) any later version. * * 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. * * -------------------------------------------------------------- * * (c) 2006 by Tomo Krajina, aaa@puzz.info * */ package info.puzz.trackprofiler.appobjects; import info.puzz.trackprofiler.Messages; import info.puzz.trackprofiler.TrackProfilerAppContext; import info.puzz.trackprofiler.TrackProfilerException; import info.puzz.trackprofiler.util.Message; import java.util.Collections; import java.util.Comparator; import java.util.Vector; /** * Sadrzi sve podatke o nekom track-u. * * @see info.puzz.trackprofiler.appobjects.TrackLoader */ public class Track { // TODO: Sve ostale podatke iz headera! private String title = ""; //$NON-NLS-1$ private Waypoints waypoints; private Vector/*<TrackPoint>*/ trackPoints = new Vector/*<TrackPoint>*/(); public static void main( String[] args ) { } public Track() { super(); } public Vector/*<TrackPoint>*/ getTrackPoints() { return trackPoints; } public void setTrackPoints( Vector/*<TrackPoints>*/ trackPoints ) { if( trackPoints == null ) { return; } this.trackPoints = trackPoints; this.refreshData(); } public int size() { return trackPoints.size(); } public TrackPoint getPointAt( int i ) { return (TrackPoint) trackPoints.get( i ); } public String getTitle() { return title; } public void setTitle( String title ) { this.title = title; } /** * "Izravnava" track, tj. smanjuje visinske razmake izmedju susjednih * tocaka. */ public void smoothTrack() { if ( this.size() < 3 ) { return; } double[] elevations = getSmoothedElevations(); for ( int i = 0; i < this.size(); i++ ) { this.getPointAt( i ).setElevation( elevations[i] ); } this.refreshData(); // Nakon svega resetiramo ekstreme: // this.extreemes = null; } /** Vraca samo array s "izravnatim" visinama pripadnih tocaka track-a. */ private double[] getSmoothedElevations() { double[] elevations = new double[ this.size() ]; for ( int i = 0; i < this.size(); i++ ) { elevations[i] = this.getPointAt( i ).getElevation(); } return getSmoothedElevations( elevations ); } private double[] getSmoothedElevations( double[] elevations ) { double[] result = new double[ elevations.length ]; for ( int i = 0; i < elevations.length; i++ ) { result[i] = elevations[i]; } // Prvog i zadnjeg posebno: result[0] = ( 0.3 * result[1] + 0.4 * result[0] ) / 0.7; result[elevations.length - 1] = ( 0.3 * result[elevations.length - 2] + 0.4 * result[elevations.length - 1] ) / 0.7; for ( int i = 1; i < this.size() - 1; i++ ) { double elevation = 0.3 * elevations[ i - 1 ] + 0.4 * elevations[ i ] + 0.3 * elevations[ i + 1 ]; result[ i ] = elevation; } return result; } /** * Traži ekstreme nekog track-a. * @see info.puzz.trackprofiler.TrackProfilerAppContext#getComputingSmoothNumber() */ public Vector/*<TrackExtreeme>*/ findExtremes() { Vector/*<TrackExtreeme>*/ result = new Vector/*<TrackExtreeme>*/(); if ( this.size() < 3 ) { return result; } // Prvo cemo nekoliko puta izravnati krivulju tako da ne bi dobili puno // besmislenih malih minimuma: double[] elevations = this.getSmoothedElevations(); for ( int i = 0; i < Math.abs( TrackProfilerAppContext.getInstance().getComputingSmoothNumber() ); i++ ) { elevations = this.getSmoothedElevations( elevations ); } // Ekstrem je tocka za koju vrijedi: // Obje njegove susjedne tocke su nize (ili vise) od njega) // TODO: Napraviti da gleda dvije susjedne sa svake strane! // Prva i zadnja tocka ne mogu biti minimumi. for ( int i = 1; i < elevations.length - 1; i++ ) { double previous = elevations[ i - 1 ]; double _this = elevations[ i ]; double next = elevations[ i + 1 ]; TrackExtreeme te = null; if ( previous < _this && next < _this ) { TrackPoint point = this.getPointAt( i ); te = new TrackExtreeme( point.getLatitude(), point.getLongitude(), point .getElevation() ); te.setMaximum(); result.add( te ); } else if ( previous > _this && next > _this ) { TrackPoint point = this.getPointAt( i ); te = new TrackExtreeme( point.getLatitude(), point.getLongitude(), point .getElevation() ); te.setMinimum(); result.add( te ); } if( te != null ) { Vector positions = this.getWaypointPositions( te ); // Ekstrem moze imati samo jednu poziciju: if( positions.size() > 0 ) { te.addPositionOnTrack( ( (Double) positions.get( 0 ) ).doubleValue() ); } } } return result; } /** * Vraca array s dva elementa; prvi je ukupno uspona, a drugi je ukupno * silazaka. */ public double[] getClimbingAndDownhillSum() throws TrackProfilerException { double up = 0; double down = 0; if ( this.size() < 3 ) { return new double[] { 0, 0 }; } Vector/*<TrackPoint>*/ points = new Vector/*<TrackPoint>*/(); points.add( this.getPointAt( 0 ) ); points.addAll( this.findExtremes() ); points.add( this.getPointAt( this.size() - 1 ) ); for ( int i = 1; i < points.size(); i++ ) { AbstractPoint d1 = (AbstractPoint) points.get( i - 1 ); AbstractPoint d2 = (AbstractPoint) points.get( i ); if ( d1.getElevation() < d2.getElevation() ) { up += d2.getElevation() - d1.getElevation(); } else { down += d1.getElevation() - d2.getElevation(); } } return new double[] { up, down }; } /** @see #getTrack3DLength() */ public double getTrack2DLength() { if( this.size() < 2 ) { return 0; } double result = 0; for( int i = 1; i < this.size(); i++ ) { TrackPoint previous = this.getPointAt( i - 1 ); TrackPoint point = this.getPointAt( i ); result += point.distance2D( previous ); } return result; } /** * Duljina puta, ali mjeri se i pomak u visinu (dakle, ne samo duljina * tlocrta na karti). * * @see #getTrack2DLength() */ public double getTrack3DLength() { if( this.size() < 2 ) { return 0; } double result = 0; for( int i = 1; i < this.size(); i++ ) { TrackPoint previous = this.getPointAt( i - 1 ); TrackPoint point = this.getPointAt( i ); double distance = point.distance2D( previous ); double elevation = point.getElevation() - previous.getElevation(); result += Math.sqrt( distance * distance + elevation * elevation ); } return result; } /** Trazi najvisu tocku. */ public TrackPoint getHighestPoint() throws TrackProfilerException { if( this.size() == 0 ) { // TODO throw new TrackProfilerException( new Message( Messages.TRACK_NOT_LOADED ).toString() ); } TrackPoint point = this.getPointAt( 0 ); for( int i = 0; i < this.size(); i++ ) { TrackPoint current = this.getPointAt( i ); if( current.getElevation() > point.getElevation() ) { point = current; } } return point; } /** Trazi najnizu tocku. */ public TrackPoint getLowestPoint() throws TrackProfilerException { if( this.size() == 0 ) { // TODO throw new TrackProfilerException( new Message( Messages.TRACK_NOT_LOADED ).toString() ); } TrackPoint point = this.getPointAt( 0 ); for( int i = 0; i < this.size(); i++ ) { TrackPoint current = this.getPointAt( i ); if( current.getElevation() < point.getElevation() ) { point = current; } } return point; } /** * Nalazi na koliko metara od pocetka track-a se nalazi waypoint. Za * udaljenost ce vratiti -1 ako je waypoint predaleko od bilo koje tocke * track-a! * * Rezultat je vektor kojemu je svaki clan array od polozaja na tracku * (udaljenost od pocetka) i visine. */ public Vector getWaypointPositionsAndElevations( Waypoint waypoint ) { Vector result = new Vector(); // Sluzi za cuvanje kandidata za najblizu tocku track-a double tempDistanceFromPoint = 100000000; double tempTrackLength = -1; double tempElevation = -1; double trackLength = 0; // Petlja: for ( int i = 0; i < this.size(); i++ ) { TrackPoint tempPoint = this.getPointAt( i ); if ( i == 0 ) { tempDistanceFromPoint = tempPoint.distance2D( waypoint ); tempElevation = tempPoint.getElevation(); } else { trackLength += this.getPointAt( i - 1 ).distance2D( this.getPointAt( i ) ); } double distance = tempPoint.distance2D( waypoint ); if ( distance <= tempDistanceFromPoint && distance <= TrackProfilerAppContext.getInstance().getWaypointMinDistance() ) { tempDistanceFromPoint = distance; tempTrackLength = trackLength; tempElevation = tempPoint.getElevation(); } // Ako smo izasli od minimalne udaljenosti: if( distance > TrackProfilerAppContext.getInstance().getWaypointMinDistance() && tempTrackLength > 0 ) { result.add( new double[] { tempTrackLength, tempElevation } ); System.out.println( "Dodan " + waypoint.getTitle() + " na " + tempTrackLength ); // I resetiramo privremeno varijable: tempDistanceFromPoint = 100000000; tempTrackLength = -1; tempElevation = -1; } } return result; } public Vector getWaypointPositions( Waypoint waypoint ) { Vector positions = getWaypointPositionsAndElevations( waypoint ); Vector result = new Vector(); for( int i = 0; i < positions.size(); i++ ) { double[] p = (double[]) positions.get( i ); result.add( new Double( p[ 0 ] ) ); } return result; } public Waypoints getWaypoints() { if( this.waypoints == null ) { this.waypoints = new Waypoints(); } return waypoints; } public void addWaypoints( Waypoints wpts ) { Waypoints _waypoints = this.getWaypoints(); _waypoints.add( wpts ); this.setWaypoints( _waypoints ); } public void setWaypoints( Waypoints waypoints ) { if( waypoints == null ) { return; } this.waypoints = waypoints; refreshData(); } /** * Izracunava visinu waypointa ovisno o tome gdje se on nalazi na track-u. * Treba pozvati svaki put nakon izravnavanja grafa. */ private void refreshData() { if ( this.waypoints != null ) { // Waypoints _waypoints = new Waypoints(); for ( int i = 0; i < this.waypoints.size(); i++ ) { Waypoint wpt = (Waypoint) this.waypoints.get( i ); Vector positionsElevations = this.getWaypointPositionsAndElevations( wpt ); for( int j = 0; j < positionsElevations.size(); j++ ) { double[] pe = (double[]) positionsElevations.get( j ); if ( pe[0] > 0 ) { wpt.addPositionOnTrack( pe[0] ); wpt.setElevation( pe[1] ); } } } // this.waypoints = _waypoints; } this.calculatePositionsAndAngles(); } // @SuppressWarnings("unchecked") //$NON-NLS-1$ public void sortWaypointsByDistance() { Waypoints waypoints = this.getWaypoints(); Collections.sort( waypoints, new Comparator() { public int compare( Object o1, Object o2 ) { Waypoint w1 = (Waypoint) o1; Waypoint w2 = (Waypoint) o2; if( w1.getFirstPositionOnTrack() == w2.getFirstPositionOnTrack() ) { return 0; } else if( w1.getFirstPositionOnTrack() > w2.getFirstPositionOnTrack() ) { return 1; } return -1; } } ); } private void calculatePositionsAndAngles() { double position = 0; for( int i = 0; i < this.trackPoints.size(); i++ ) { if( i > 0 ) { TrackPoint previous = (TrackPoint) this.trackPoints.get( i - 1); TrackPoint current = (TrackPoint) this.trackPoints.get( i ); position = position + previous.distance2D( current ); current.setPosition( position ); } if( i > 0 && i < this.trackPoints.size() - 2 ) { TrackPoint previous = (TrackPoint) this.trackPoints.get( i - 1); TrackPoint current = (TrackPoint) this.trackPoints.get( i ); TrackPoint next = (TrackPoint) this.trackPoints.get( i + 1 ); current.setAngle( previous.getAngle( next ) ); } } } }