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