Difference between revisions of "Distance"

From DarkWiki
Jump to: navigation, search
(Created page with "==Java source code== <source lang="java"> package pa.common; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonValue; import o...")
 
(Java source code)
 
Line 2: Line 2:
  
 
<source lang="java">
 
<source lang="java">
package pa.common;
 
  
 
import com.fasterxml.jackson.annotation.JsonCreator;
 
import com.fasterxml.jackson.annotation.JsonCreator;

Latest revision as of 09:16, 16 June 2017

Java source code

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
import org.springframework.data.geo.Metrics;

import java.io.Serializable;
import java.text.DecimalFormat;

/**
 * Represents a measurement of distance. When this object is serialised to/from JSON, it is done so using the
 * string form.
 */
public class Distance implements Comparable<Distance>, Serializable {

    private static final String NUMBER_FORMAT = "0.####";
    private double value = 0;
    private Units units = Units.M;

    /**
     * Create a zero-metre distance.
     */
    public Distance() {
    }

    /**
     * Create a distance.
     *
     * @param value The number of units.
     * @param units The units to use.
     */
    public Distance(double value, Units units) {
        setValue(value);
        setUnits(units);
    }

    /**
     * Convert a spring org.springframework.data.geo.Distance into a distance. If Spring's implementation changes
     * and new metrics are introduced, this method will need to be updated.
     *
     * @param springDistance The object to convert.
     * @return A new distance.
     * @throws IllegalArgumentException if the given spring distance could not be converted.
     */
    public static Distance fromSpringDistance(org.springframework.data.geo.Distance springDistance) {
        if (springDistance.getMetric().equals(Metrics.MILES)) {
            return new Distance(springDistance.getValue(), Units.MI);
        } else if (springDistance.getMetric().equals(Metrics.KILOMETERS)) {
            return new Distance(springDistance.getValue(), Units.KM);
        } else {
            throw new IllegalArgumentException("Unsupported Metric of org.springframework.data.geo.Distance: " +
                springDistance.getMetric());
        }
    }

    /**
     * Parse the input and create a distance from it. If no units are provided, metres are assumed. This is the
     * method that Jackson will use to create Distance objects from a JSON value.
     *
     * @param text The textual version of a distance.
     * @return A new distance object.
     * @throws IllegalArgumentException if the given string cannot be parsed.
     */
    @JsonCreator
    public static Distance parse(String text) {
        return parse(text, Units.M);
    }

    /**
     * Parse the input and create a distance from it. If no units are provided, the default units are assumed.
     *
     * @param text         The textual version of a distance.
     * @param defaultUnits The units to use if none is provided.
     * @return A new distance object.
     * @throws IllegalArgumentException if the given string cannot be parsed.
     */
    public static Distance parse(String text, Units defaultUnits) {
        Units foundUnits = null;
        if (text.endsWith(Units.KM.getSymbol())) foundUnits = Units.KM;
        else if (text.endsWith(Units.M.getSymbol())) foundUnits = Units.M;
        else if (text.endsWith(Units.MI.getSymbol())) foundUnits = Units.MI;
        if (foundUnits != null) {
            int index = text.lastIndexOf(foundUnits.getSymbol());
            String amountString = text.substring(0, index);
            try {
                return new Distance(Double.parseDouble(amountString), foundUnits);
            } catch (NumberFormatException e) {
                throw new IllegalArgumentException("No a valid distance: '" + text + "'");
            }
        } else {
            try {
                return new Distance(Double.parseDouble(text), defaultUnits);
            } catch (NumberFormatException e) {
                throw new IllegalArgumentException("No a valid distance: '" + text + "'");
            }
        }
    }

    /**
     * Convert one distance value into another.
     *
     * @param in   The value to convert.
     * @param from The origin units.
     * @param to   The destination units.
     * @return The converted value.
     */
    static double convert(double in, Units from, Units to) {
        return in * from.getScaleFactor() / to.getScaleFactor();
    }

    /**
     * Get the distance in the current units.
     *
     * @return The distance in the current units.
     */
    public double getValue() {
        return value;
    }

    /**
     * Set the distance in the current units.
     *
     * @param value The distance in the current units.
     */
    public void setValue(double value) {
        this.value = value;
    }

    /**
     * Get the units used by this distance.
     *
     * @return The units used by this distance.
     */
    public Units getUnits() {
        return units;
    }

    /**
     * Set the units used by this distance (without affecting the value).
     *
     * @param units The units used by this distance.
     */
    public void setUnits(Units units) {
        this.units = units;
    }

    /**
     * Convert this distance into one with different units.
     *
     * @param newUnits The desired units.
     * @return The new distance, in the desired units.
     */
    public Distance convertTo(Units newUnits) {
        return new Distance(convert(this.value, this.units, newUnits), newUnits);
    }

    /**
     * Add a distance to this one.
     *
     * @param distance The distance to add.
     * @return A new distance, representing the sum of the distances, in the units of the original.
     */
    public Distance add(Distance distance) {
        return new Distance(this.getInternalValue() + distance.getInternalValue(), Units.M)
            .convertTo(this.units);
    }

    /**
     * Format this distance as a string. A space will be inserted between the value and the unit symbol. This method
     * is called when serializing this object to a JSON value.
     *
     * @return The textual version of this distance.
     */
    @JsonValue
    public String toString() {
        return new DecimalFormat(NUMBER_FORMAT).format(getValue()) + " " + getUnits().getSymbol();
    }

    /**
     * Convert this distance into one of Spring's org.springframework.data.geo.Distance. At this point, Spring only
     * supports kilometres and miles. If any other units are used, the appropriate calculation will be made and the
     * Spring object will be in the KM metric.
     *
     * @return The distance in Spring's representation.
     */
    public org.springframework.data.geo.Distance toSpringDistance() {
        switch (units) {
            case KM:
                return new org.springframework.data.geo.Distance(
                    this.getValue(),
                    Metrics.KILOMETERS);
            case MI:
                return new org.springframework.data.geo.Distance(
                    this.getValue(),
                    Metrics.MILES);
            default:
                return new org.springframework.data.geo.Distance(
                    this.getInternalValue() / Units.KM.getScaleFactor(),
                    Metrics.KILOMETERS);
        }
    }

    /**
     * Get the value (in metres) used to compare.
     *
     * @return The number of metres this distance represents.
     */
    double getInternalValue() {
        if (units == Units.M) {
            return value;
        } else {
            return convert(value, units, Units.M);
        }
    }

    /**
     * Compare two distances.
     *
     * @param o The distance to compare.
     * @return Zero if they're the same, -1 if this value is smaller than the argument, +1 if larger.
     */
    @Override
    public int compareTo(Distance o) {
        if (o == null) {
            return 1;
        } else {
            double lhs = getInternalValue();
            double rhs = o.getInternalValue();
            if (lhs < rhs) {
                return -1;
            } else if (lhs > rhs) {
                return 1;
            } else {
                return 0;
            }
        }
    }

    /**
     * Compare two distances for equality.
     *
     * @param obj The object to check.
     * @return True if they're the same, false otherwise.
     */
    @Override
    public boolean equals(Object obj) {
        if (obj == this) {
            return true;
        } else if (obj instanceof Distance) {
            return this.getInternalValue() == ((Distance) obj).getInternalValue();
        } else {
            return false;
        }
    }

    /*
     * (non-Javadoc)
     * @see java.lang.Object#hashCode()
     */
    @Override
    public int hashCode() {
        int result = 17;
        result ^= 31 * Double.hashCode(value);
        result ^= 31 * units.hashCode();
        return result;
    }

    /**
     * The units of distance. By default, metres are assumed.
     */
    public enum Units {
        /**
         * Metres
         */
        M("m", 1),
        /**
         * Kilometres
         */
        KM("km", 1000),
        /**
         * Miles
         */
        MI("mi", 1609.34);

        private double scaleFactor;
        private String symbol;

        Units(String symbol, double scaleFactor) {
            this.symbol = symbol;
            this.scaleFactor = scaleFactor;
        }

        /**
         * Get the number of metres represented by this unit.
         *
         * @return The number of metres represented by this unit.
         */
        public double getScaleFactor() {
            return scaleFactor;
        }

        /**
         * Get the standard symbol for this unit.
         *
         * @return The standard symbol for this unit.
         */
        public String getSymbol() {
            return symbol;
        }
    }

}