001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.data.projection; 003 004import org.openstreetmap.josm.data.Bounds; 005import org.openstreetmap.josm.data.ProjectionBounds; 006import org.openstreetmap.josm.data.coor.EastNorth; 007import org.openstreetmap.josm.data.coor.LatLon; 008import org.openstreetmap.josm.data.projection.datum.Datum; 009import org.openstreetmap.josm.data.projection.proj.Proj; 010 011/** 012 * Implementation of the Projection interface that represents a coordinate reference system and delegates 013 * the real projection and datum conversion to other classes. 014 * 015 * It handles false easting and northing, central meridian and general scale factor before calling the 016 * delegate projection. 017 * 018 * Forwards lat/lon values to the real projection in units of radians. 019 * 020 * The fields are named after Proj.4 parameters. 021 * 022 * Subclasses of AbstractProjection must set ellps and proj to a non-null value. 023 * In addition, either datum or nadgrid has to be initialized to some value. 024 */ 025public abstract class AbstractProjection implements Projection { 026 027 protected Ellipsoid ellps; 028 protected Datum datum; 029 protected Proj proj; 030 protected double x0; /* false easting (in meters) */ 031 protected double y0; /* false northing (in meters) */ 032 protected double lon0; /* central meridian */ 033 protected double pm; /* prime meridian */ 034 protected double k0 = 1.0; /* general scale factor */ 035 036 private volatile ProjectionBounds projectionBoundsBox; 037 038 public final Ellipsoid getEllipsoid() { 039 return ellps; 040 } 041 042 public final Datum getDatum() { 043 return datum; 044 } 045 046 /** 047 * Replies the projection (in the narrow sense) 048 * @return The projection object 049 */ 050 public final Proj getProj() { 051 return proj; 052 } 053 054 public final double getFalseEasting() { 055 return x0; 056 } 057 058 public final double getFalseNorthing() { 059 return y0; 060 } 061 062 public final double getCentralMeridian() { 063 return lon0; 064 } 065 066 public final double getScaleFactor() { 067 return k0; 068 } 069 070 @Override 071 public EastNorth latlon2eastNorth(LatLon ll) { 072 ll = datum.fromWGS84(ll); 073 double[] en = proj.project(Math.toRadians(ll.lat()), Math.toRadians(ll.lon() - lon0 - pm)); 074 return new EastNorth(ellps.a * k0 * en[0] + x0, ellps.a * k0 * en[1] + y0); 075 } 076 077 @Override 078 public LatLon eastNorth2latlon(EastNorth en) { 079 double[] latlon_rad = proj.invproject((en.east() - x0) / ellps.a / k0, (en.north() - y0) / ellps.a / k0); 080 LatLon ll = new LatLon(Math.toDegrees(latlon_rad[0]), Math.toDegrees(latlon_rad[1]) + lon0 + pm); 081 return datum.toWGS84(ll); 082 } 083 084 @Override 085 public double getDefaultZoomInPPD() { 086 // this will set the map scaler to about 1000 m 087 return 10; 088 } 089 090 /** 091 * @return The EPSG Code of this CRS, null if it doesn't have one. 092 */ 093 public abstract Integer getEpsgCode(); 094 095 /** 096 * Default implementation of toCode(). 097 * Should be overridden, if there is no EPSG code for this CRS. 098 */ 099 @Override 100 public String toCode() { 101 return "EPSG:" + getEpsgCode(); 102 } 103 104 protected static final double convertMinuteSecond(double minute, double second) { 105 return (minute/60.0) + (second/3600.0); 106 } 107 108 protected static final double convertDegreeMinuteSecond(double degree, double minute, double second) { 109 return degree + (minute/60.0) + (second/3600.0); 110 } 111 112 @Override 113 public final ProjectionBounds getWorldBoundsBoxEastNorth() { 114 ProjectionBounds result = projectionBoundsBox; 115 if (result == null) { 116 synchronized (this) { 117 result = projectionBoundsBox; 118 if (result == null) { 119 Bounds b = getWorldBoundsLatLon(); 120 // add 4 corners 121 result = new ProjectionBounds(latlon2eastNorth(b.getMin())); 122 result.extend(latlon2eastNorth(b.getMax())); 123 result.extend(latlon2eastNorth(new LatLon(b.getMinLat(), b.getMaxLon()))); 124 result.extend(latlon2eastNorth(new LatLon(b.getMaxLat(), b.getMinLon()))); 125 // and trace along the outline 126 double dLon = (b.getMaxLon() - b.getMinLon()) / 1000; 127 double dLat = (b.getMaxLat() - b.getMinLat()) / 1000; 128 for (double lon = b.getMinLon(); lon < b.getMaxLon(); lon += dLon) { 129 result.extend(latlon2eastNorth(new LatLon(b.getMinLat(), lon))); 130 result.extend(latlon2eastNorth(new LatLon(b.getMaxLat(), lon))); 131 } 132 for (double lat = b.getMinLat(); lat < b.getMaxLat(); lat += dLat) { 133 result.extend(latlon2eastNorth(new LatLon(lat, b.getMinLon()))); 134 result.extend(latlon2eastNorth(new LatLon(lat, b.getMaxLon()))); 135 } 136 projectionBoundsBox = result; 137 } 138 } 139 } 140 return projectionBoundsBox; 141 } 142}