001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.data.gpx;
003
004import java.util.ArrayList;
005import java.util.Collection;
006import java.util.Collections;
007import java.util.List;
008
009import org.openstreetmap.josm.data.Bounds;
010
011/**
012 * A gpx track segment consisting of multiple waypoints.
013 * @since 15496
014 */
015public class GpxTrackSegment extends WithAttributes implements IGpxTrackSegment {
016
017    private final List<WayPoint> wayPoints;
018    private final Bounds bounds;
019    private final double length;
020
021    /**
022     * Constructs a new {@code GpxTrackSegment}.
023     * @param wayPoints list of waypoints
024     */
025    public GpxTrackSegment(Collection<WayPoint> wayPoints) {
026        this.wayPoints = Collections.unmodifiableList(new ArrayList<>(wayPoints));
027        this.bounds = calculateBounds();
028        this.length = calculateLength();
029    }
030
031    private Bounds calculateBounds() {
032        Bounds result = null;
033        for (WayPoint wpt: wayPoints) {
034            if (result == null) {
035                result = new Bounds(wpt.getCoor());
036            } else {
037                result.extend(wpt.getCoor());
038            }
039        }
040        return result;
041    }
042
043    private double calculateLength() {
044        double result = 0.0; // in meters
045        WayPoint last = null;
046        for (WayPoint tpt : wayPoints) {
047            if (last != null) {
048                Double d = last.getCoor().greatCircleDistance(tpt.getCoor());
049                if (!d.isNaN() && !d.isInfinite()) {
050                    result += d;
051                }
052            }
053            last = tpt;
054        }
055        return result;
056    }
057
058    @Override
059    public Bounds getBounds() {
060        return bounds == null ? null : new Bounds(bounds);
061    }
062
063    @Override
064    public Collection<WayPoint> getWayPoints() {
065        return Collections.unmodifiableList(wayPoints);
066    }
067
068    @Override
069    public double length() {
070        return length;
071    }
072
073    @Override
074    public int getUpdateCount() {
075        return 0;
076    }
077
078    @Override
079    public int hashCode() {
080        final int prime = 31;
081        int result = prime + super.hashCode();
082        result = prime * result + ((wayPoints == null) ? 0 : wayPoints.hashCode());
083        return result;
084    }
085
086    @Override
087    public boolean equals(Object obj) {
088        if (this == obj)
089            return true;
090        if (obj == null)
091            return false;
092        if (!super.equals(obj))
093            return false;
094        if (getClass() != obj.getClass())
095            return false;
096        GpxTrackSegment other = (GpxTrackSegment) obj;
097        if (wayPoints == null) {
098            if (other.wayPoints != null)
099                return false;
100        } else if (!wayPoints.equals(other.wayPoints))
101            return false;
102        return true;
103    }
104}