All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.tomtom.speedtools.geometry.GeoLine Maven / Gradle / Ivy

Go to download

Contains a set of geometry (and geographical) classes, which work with any latitude and longitude, without glitches around the poles or Fiji.

There is a newer version: 3.4.4
Show newest version
/*
 * Copyright (C) 2015. TomTom International BV (http://tomtom.com).
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.tomtom.speedtools.geometry;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;

import com.tomtom.speedtools.utils.MathUtils;

/**
 * Geometric line between two points, from west to east (or, for vertical vectors east=west). If west > east, the
 * vector is wrapped along the long side of Earth.
 *
 * Constructor: {@link #GeoLine}
 */
@Immutable
public final class GeoLine extends GeoObject {

    @Nonnull
    private final GeoPoint southWest;
    @Nonnull
    private final GeoPoint northEast;

    /**
     * Create a line. A line is always defined from west to east. Note that if the west.longitude > east.longitude
     * that the line is wrapped along the long side of the Earth.
     *
     * @param southWest Start point, west side.
     * @param northEast End point, east side, although east and west may be same longitude.
     */
    public GeoLine(
            @Nonnull final GeoPoint southWest,
            @Nonnull final GeoPoint northEast) {
        super();
        assert southWest != null;
        assert northEast != null;
        final GeoPoint lowerLeft = (southWest.getLat() <= northEast.getLat()) ?
                southWest : southWest.withLat(northEast.getLat());
        final GeoPoint upperRight = (southWest.getLat() <= northEast.getLat()) ?
                northEast : northEast.withLat(southWest.getLat());
        this.southWest = lowerLeft;
        this.northEast = upperRight;
        assert this.southWest.getLat() <= this.northEast.getLat() : "SW above NE: " + this;
    }

    /**
     * Default constructor, used for deserialization only.
     */
    @SuppressWarnings({"ConstantConditions", "UnusedDeclaration"})
    @Deprecated
    private GeoLine() {
        super();
        southWest = null;
        northEast = null;
    }

    /**
     * Return origin == southWest.
     *
     * @return Origin.
     */
    @Nonnull
    @Override
    public GeoPoint getOrigin() {
        return southWest;
    }

    /**
     * Return center == Mid point of (northEast + southWest).
     *
     * @return Center.
     */
    @Nonnull
    @Override
    public GeoPoint getCenter() {
        final double lat = (southWest.getLat() + northEast.getLat()) / 2.0;
        final double lon = (((northEast.getLon() >= southWest.getLon()) ?
                (northEast.getLon()) : ((northEast.getLon() + 360.0))) +
                southWest.getLon()) / 2.0;
        return new GeoPoint(lat, lon);
    }

    /**
     * Get start point.
     *
     * @return Start point.
     */
    @Nonnull
    public GeoPoint getSouthWest() {
        return southWest;
    }

    /**
     * Get end point.
     *
     * @return End point.
     */
    @Nonnull
    public GeoPoint getNorthEast() {
        return northEast;
    }

    /**
     * Setter for {@link #getSouthWest()}.
     */
    @Nonnull
    public GeoLine withSouthWest(@Nonnull final GeoPoint southWest) {
        return new GeoLine(southWest, northEast);
    }

    /**
     * Setter for {@link #getNorthEast()}.
     */
    @Nonnull
    public GeoLine withNorthEast(@Nonnull final GeoPoint northEast) {
        return new GeoLine(southWest, northEast);
    }

    @Nonnull
    @Override
    public GeoLine translate(@Nonnull final GeoVector vector) {
        return new GeoLine(southWest.translate(vector), northEast.translate(vector));
    }

    @Nonnull
    @Override
    public GeoLine moveTo(@Nonnull final GeoPoint origin) {
        final GeoPoint p1 = origin;
        final GeoPoint p2 = origin.translate(new GeoVector(getNorthing(), getEasting()));
        return new GeoLine(p1, p2);
    }

    /**
     * Return degrees northing.
     *
     * @return Degrees northing, [0, 180].
     */
    public double getNorthing() {
        final double northing = northEast.getLat() - southWest.getLat();
        assert MathUtils.isBetween(northing, 0, 180) : "Northing not in [0, 180]: " + northing + ", " + this;
        return northing;
    }

    /**
     * Return degrees easting.
     *
     * @return Degrees easting, [0, 360).
     */
    public double getEasting() {
        final double easting;

        if (northEast.getLon() >= southWest.getLon()) {
            easting = northEast.getLon() - southWest.getLon();
        } else {
            easting = 360.0 + (northEast.getLon() - southWest.getLon());
        }
        assert (0.0 <= easting) && (easting < 360.0) : "Easting not in [0, 360): " + easting + ", " + this;
        return easting;
    }

    /**
     * Return length of line in meters (approximation).
     *
     * @return Length in meters.
     */
    public double getLengthMeters() {
        return Geo.distanceInMeters(southWest, northEast);
    }

    /**
     * Return whether the vector is wrapped around the long or short side of Earth. The 'normal case' is that vectors
     * and rectangles are wrapped along the short side indeed.
     *
     * @return True if wrapped around long side, false if wrapped along short side.
     */
    public boolean isWrappedOnLongSide() {
        return southWest.getLon() > northEast.getLon();
    }

    /**
     * Return the shortest GeoLine for two points, which is never wrapped around the long side of the Earth.
     *
     * @param from From.
     * @param to   To.
     * @return Shortest connection.
     */
    @Nonnull
    public static GeoLine getShortestLine(
            @Nonnull final GeoPoint from,
            @Nonnull final GeoPoint to) {
        assert from != null;
        assert to != null;
        GeoLine shortest = new GeoLine(from, to);
        if (shortest.isWrappedOnLongSide()) {
            shortest = new GeoLine(to, from);
        }
        return shortest;
    }

    @Override
    public boolean canEqual(@Nonnull final Object obj) {
        return obj instanceof GeoLine;
    }

    @Override
    public boolean equals(@Nullable final Object obj) {
        boolean eq;
        if (this == obj) {
            eq = true;
        } else if ((obj != null) && (obj instanceof GeoLine)) {
            final GeoLine that = (GeoLine) obj;
            eq = that.canEqual(this);
            // Top-level entity, so don't: super.equals(that)
            eq = eq && southWest.equals(that.southWest);
            eq = eq && northEast.equals(that.northEast);
        } else {
            eq = false;
        }

        return eq;
    }

    @Override
    public int hashCode() {
        return hashCodeSuper(southWest, northEast);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy