001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.data.projection;
003
004import org.openstreetmap.josm.data.coor.EastNorth;
005import org.openstreetmap.josm.data.coor.LatLon;
006import org.openstreetmap.josm.data.projection.datum.Datum;
007import org.openstreetmap.josm.data.projection.proj.Proj;
008
009/**
010 * Implementation of the Projection interface that represents a coordinate reference system and delegates
011 * the real projection and datum conversion to other classes.
012 *
013 * It handles false easting and northing, central meridian and general scale factor before calling the
014 * delegate projection.
015 *
016 * Forwards lat/lon values to the real projection in units of radians.
017 *
018 * The fields are named after Proj.4 parameters.
019 *
020 * Subclasses of AbstractProjection must set ellps and proj to a non-null value.
021 * In addition, either datum or nadgrid has to be initialized to some value.
022 */
023abstract public class AbstractProjection implements Projection {
024
025    protected Ellipsoid ellps;
026    protected Datum datum;
027    protected Proj proj;
028    protected double x_0 = 0.0;     /* false easting (in meters) */
029    protected double y_0 = 0.0;     /* false northing (in meters) */
030    protected double lon_0 = 0.0;   /* central meridian */
031    protected double k_0 = 1.0;     /* general scale factor */
032
033    public final Ellipsoid getEllipsoid() {
034        return ellps;
035    }
036
037    public final Datum getDatum() {
038        return datum;
039    }
040
041    /**
042     * Replies the projection (in the narrow sense)
043     * @return The projection object
044     */
045    public final Proj getProj() {
046        return proj;
047    }
048
049    public final double getFalseEasting() {
050        return x_0;
051    }
052
053    public final double getFalseNorthing() {
054        return y_0;
055    }
056
057    public final double getCentralMeridian() {
058        return lon_0;
059    }
060
061    public final double getScaleFactor() {
062        return k_0;
063    }
064
065    @Override
066    public EastNorth latlon2eastNorth(LatLon ll) {
067        ll = datum.fromWGS84(ll);
068        double[] en = proj.project(Math.toRadians(ll.lat()), Math.toRadians(ll.lon() - lon_0));
069        return new EastNorth(ellps.a * k_0 * en[0] + x_0, ellps.a * k_0 * en[1] + y_0);
070    }
071
072    @Override
073    public LatLon eastNorth2latlon(EastNorth en) {
074        double[] latlon_rad = proj.invproject((en.east() - x_0) / ellps.a / k_0, (en.north() - y_0) / ellps.a / k_0);
075        LatLon ll = new LatLon(Math.toDegrees(latlon_rad[0]), Math.toDegrees(latlon_rad[1]) + lon_0);
076        return datum.toWGS84(ll);
077    }
078
079    @Override
080    public double getDefaultZoomInPPD() {
081        // this will set the map scaler to about 1000 m
082        return 10;
083    }
084
085    /**
086     * @return The EPSG Code of this CRS, null if it doesn't have one.
087     */
088    public abstract Integer getEpsgCode();
089
090    /**
091     * Default implementation of toCode().
092     * Should be overridden, if there is no EPSG code for this CRS.
093     */
094    @Override
095    public String toCode() {
096        return "EPSG:" + getEpsgCode();
097    }
098
099    protected static final double convertMinuteSecond(double minute, double second) {
100        return (minute/60.0) + (second/3600.0);
101    }
102
103    protected static final double convertDegreeMinuteSecond(double degree, double minute, double second) {
104        return degree + (minute/60.0) + (second/3600.0);
105    }
106}