0

I want to calculate the average speed from two point objects. A point object contains a timestamp, Longitude, Latitude, and elevation.

I know that I need to compute the amount of time that has passed between measurements for the first and last points on the track. I was hoping to use the ChronoUnit type for this — specifically, the between method, which I believe can be called on ChronoUnit.SECONDS to measure time intervals in units of seconds.

What I have so far (basically a stub so the UnitTests run):

  // Average speed method
    public double averageSpeed() {
        double avgSpeed = 0;
        if (track.size () < 4) {
            throw new GPSException ("Not enough points to compute");
        } else {
            return avgSpeed;
        }
    }

Points are read from a .csv file, an example of which can be seen here:

Time,Longitude,Latitude,Elevation
2016-02-17T09:34:53Z,-1.536369,53.796796,35.0
2016-02-17T09:35:14Z,-1.536506,53.796819,35.1
2016-02-17T09:35:21Z,-1.536657,53.796798,35.2
2016-02-17T09:35:28Z,-1.536817,53.796783,35.3
2016-02-17T09:35:31Z,-1.536892,53.796711,35.4
2016-02-17T09:35:34Z,-1.536967,53.796623,35.5

My question is I want to know how to calculate and return averageSpeed() using the ChronoUnit type for this — specifically, the between method to measure time intervals in units of seconds.

3 Answers3

0

Maybe load each row into an instance of a class like this and use the distanceFrom() and secondsUntil() methods to help you compute speed.

public static class Observation {
    private ZonedDateTime zdt;
    private double lon;
    private double lat;
    private double ele;

    public ZonedDateTime getZdt() {
        return zdt;
    }

    public void setZdt(ZonedDateTime zdt) {
        this.zdt = zdt;
    }

    public double getLon() {
        return lon;
    }

    public void setLon(double lon) {
        this.lon = lon;
    }

    public double getLat() {
        return lat;
    }

    public void setLat(double lat) {
        this.lat = lat;
    }

    public double getEle() {
        return ele;
    }

    public void setEle(double ele) {
        this.ele = ele;
    }

    // https://stackoverflow.com/questions/3694380/calculating-distance-between-two-points-using-latitude-longitude
    private static double distance(double lat1, double lat2, double lon1, double lon2, double el1, double el2) {
        final int R = 6371; // Radius of the earth

        double latDistance = Math.toRadians(lat2 - lat1);
        double lonDistance = Math.toRadians(lon2 - lon1);
        double a = Math.sin(latDistance / 2) * Math.sin(latDistance / 2) + Math.cos(Math.toRadians(lat1))
                * Math.cos(Math.toRadians(lat2)) * Math.sin(lonDistance / 2) * Math.sin(lonDistance / 2);
        double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
        double distance = R * c * 1000; // convert to meters

        double height = el1 - el2;

        distance = Math.pow(distance, 2) + Math.pow(height, 2);

        return Math.sqrt(distance);
    }

    public double distanceFrom(Observation that) {
        return distance(this.lat, that.lat, this.lon, that.lon, this.ele, that.ele);
    }

    /**
     * Returns the number of seconds from _this_ to _that_
     */
    public long secondsUntil(Observation that) {
        return Duration.between(this.zdt, that.zdt)
                       .getSeconds();
    }
}
Not a JD
  • 1,864
  • 6
  • 14
0

As you may well know, the average speed is the distance travelled divided by the time taken.

For calculating the total distance you need to calculate the distance between pairwise points and sum up.

The time is easier because it’s one-dimensional. You just need the time between the first and the last point. For example:

    Instant firstTime = Instant.parse("2016-02-17T09:34:53Z");
    Instant lastTime = Instant.parse("2016-02-17T09:35:34Z");
    long totalTime = ChronoUnit.SECONDS.between(firstTime, lastTime);
    System.out.println("Total time: " + totalTime + " seconds");

Total time: 41 seconds

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
0

Thanks for the help guys. It helped me come up with my own solution which works perfectly:

 // Average speed method calling totalDistance() to compute
  public double averageSpeed() {
    double tDistance = totalDistance();

    if (track.size() == 0) {
      throw new GPSException("Track is empty.");
    }
    if (track.size() == 1) {
      throw new GPSException("Track only contains a single point.");
    }
    ZonedDateTime t1 = track.get(0).getTime();
    ZonedDateTime t2 = track.get(track.size() - 1).getTime();

    double time = ChronoUnit.SECONDS.between(t1, t2);
    double avgSpeed = tDistance / time;
    return avgSpeed;
  }
}
  • Thanks for sharing the solution you ended up with. `ZonedDateTime` works but is overkill for UTC times like yours. Just use `Instant`. – Ole V.V. Mar 16 '19 at 19:39