package com.bbn.openmap.proj.coords;
import java.awt.geom.Point2D;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Properties;
import java.util.TreeMap;
import com.bbn.openmap.proj.Ellipsoid;
import com.bbn.openmap.proj.GeoProj;
import com.bbn.openmap.proj.LLXYLoader;
import com.bbn.openmap.proj.LambertConformalLoader;
import com.bbn.openmap.proj.MercatorLoader;
import com.bbn.openmap.proj.Planet;
import com.bbn.openmap.proj.ProjectionFactory;
import com.bbn.openmap.proj.ProjectionLoader;
import com.bbn.openmap.proj.UTMProjectionLoader;
public class CoordinateReferenceSystem {
private String code;
private GeoCoordTransformation coordTransform;
private ProjectionLoader projLoader;
private BoundingBox boundingBox;
private String projLoaderClassName;
private Ellipsoid ellipsoid = Ellipsoid.WGS_84;
private Properties defaultProjectionParameters = new Properties();
private AxisOrder axisOrder;
protected static final Map<String, CoordinateReferenceSystem> crss =
Collections.synchronizedMap(new TreeMap<String, CoordinateReferenceSystem>());
static {
// unprojected wgs84
addCrs(new CoordinateReferenceSystem("EPSG:4326", LatLonGCT.INSTANCE, LLXYLoader.class, Ellipsoid.WGS_84, null, null,
AxisOrder.northBeforeEast));
addCrs(new CoordinateReferenceSystem("CRS:84", LatLonGCT.INSTANCE, LLXYLoader.class, Ellipsoid.WGS_84));
// unprojected ED50
addCrs(new CoordinateReferenceSystem("EPSG:4230", new DatumShiftGCT(Ellipsoid.INTERNATIONAL), LLXYLoader.class,
Ellipsoid.INTERNATIONAL));
// Spherical Mercator for overlaying with Google Maps
// http://trac.openlayers.org/wiki/SphericalMercator
addCrs(new CoordinateReferenceSystem("EPSG:900913", new MercatorMeterGCT(Planet.wgs84_earthEquatorialRadiusMeters_D,
Planet.wgs84_earthEquatorialRadiusMeters_D),
MercatorLoader.class, Ellipsoid.WGS_84));
addCrs(new CoordinateReferenceSystem("EPSG:3857", new MercatorMeterGCT(Planet.wgs84_earthEquatorialRadiusMeters_D,
Planet.wgs84_earthEquatorialRadiusMeters_D),
MercatorLoader.class, Ellipsoid.WGS_84));
addUtms();
// Estonian Coordinate System of 1997 - EPSG:3301
// http://spatialreference.org/ref/epsg/3301/
// bounding box values from a national WMS from Estonian
addLcc("EPSG:3301", Ellipsoid.GRS_1980, 59.33333333333334, 58d, 57.51755393055556d, 24d, 500000, 6375000,
new BoundingBox(300000, 6.3e+06, 800000, 6.7e+06), AxisOrder.northBeforeEast);
// SWEREF 99 TM (EPSG:3006)
// http://spatialreference.org/ref/epsg/3006/
addUtm("EPSG:3006", 33, 'N', Ellipsoid.GRS_1980,
new BoundingBox(218128.7031d, 6126002.9379d, 1083427.2970d, 7692850.9468d), AxisOrder.northBeforeEast);
// ETRS89 / ETRS-TM35FIN
// http://spatialreference.org/ref/epsg/3067/
addUtm("EPSG:3067", 35, 'N', Ellipsoid.GRS_1980, new BoundingBox(50199.4814d, 6582464.0358d, 761274.6247d, 7799839.8902d));
}
private static void addLcc(String code, Ellipsoid ellps, double sp1, double sp2, double refLat, double centMeri,
double falseEast, double falseNorth, BoundingBox bbox, AxisOrder axisOrder) {
Properties props = new Properties();
props.put(LambertConformalLoader.StandardParallelOneProperty, Double.toString(sp1));
props.put(LambertConformalLoader.StandardParallelTwoProperty, Double.toString(sp2));
props.put(LambertConformalLoader.ReferenceLatitudeProperty, Double.toString(refLat));
props.put(LambertConformalLoader.CentralMeridianProperty, Double.toString(centMeri));
props.put(LambertConformalLoader.FalseEastingProperty, Double.toString(falseEast));
props.put(LambertConformalLoader.FalseNorthingProperty, Double.toString(falseNorth));
props.put(ProjectionFactory.DATUM, ellps);
props.put(ProjectionFactory.CENTER, new LatLonPoint.Double(refLat, centMeri));
addCrs(new CoordinateReferenceSystem(code, new LambertConformalGCT(props), LambertConformalLoader.class, ellps, props,
bbox, axisOrder));
}
public static void addCrs(CoordinateReferenceSystem crs) {
crss.put(crs.getCode(), crs);
}
private static void addUtms() {
for (int zone = 1; zone <= 60; zone++) {
String zoneCode = String.valueOf(zone);
while (zoneCode.length() < 2) {
zoneCode = "0" + zoneCode;
}
// wgs84 utm
addUtm("EPSG:326" + zoneCode, zone, 'N', Ellipsoid.WGS_84, null);
addUtm("EPSG:327" + zoneCode, zone, 'S', Ellipsoid.WGS_84, null);
// ed50 utm
if ((zone >= 28) && (zone <= 38)) {
addUtm("EPSG:230" + zoneCode, zone, 'N', Ellipsoid.INTERNATIONAL, null);
}
// ETRS89 utm
if ((zone >= 28) && (zone <= 38)) {
addUtm("EPSG:258" + zoneCode, zone, 'N', Ellipsoid.GRS_1980, null);
}
}
}
private static void addUtm(String epsg, int zone_number, char zone_letter, Ellipsoid ellps, BoundingBox bbox) {
addUtm(epsg, zone_number, zone_letter, ellps, bbox, AxisOrder.eastBeforeNorth);
}
private static void addUtm(String epsg, int zone_number, char zone_letter, Ellipsoid ellps, BoundingBox bbox,
AxisOrder axisOrder) {
// some properties for the projection loader
Properties projProps = new Properties();
projProps.put(UTMProjectionLoader.ZONE_NUMBER, Integer.toString(zone_number));
projProps.put(UTMProjectionLoader.ZONE_LETTER, Character.toString(zone_letter));
UTMGCT utmgct = new UTMGCT(zone_number, zone_letter);
utmgct.setEllipsoid(ellps);
GeoCoordTransformation gct = utmgct;
// add datum shift for non-wgs84/grs80
if (!((ellps == Ellipsoid.WGS_84) || (ellps == Ellipsoid.GRS_1980))) {
DatumShiftGCT egct = new DatumShiftGCT(ellps);
gct = new MultiGCT(new GeoCoordTransformation[] {
egct,
utmgct
});
}
addCrs(new CoordinateReferenceSystem(epsg, gct, UTMProjectionLoader.class, ellps, projProps, bbox, axisOrder));
}
public CoordinateReferenceSystem(String code, GeoCoordTransformation coordConverter, Class<?> projLoaderClass,
Ellipsoid ellipsoid) {
this.code = code;
this.coordTransform = coordConverter;
this.projLoaderClassName = projLoaderClass.getName();
this.ellipsoid = ellipsoid;
this.axisOrder = AxisOrder.eastBeforeNorth;
defaultProjectionParameters.put(ProjectionFactory.DATUM, ellipsoid);
}
public CoordinateReferenceSystem(String code, GeoCoordTransformation coordConverter, Class<?> projLoaderClass,
Ellipsoid ellipsoid, Properties projectionParameters, BoundingBox boundingBox,
AxisOrder axisOrder) {
this(code, coordConverter, projLoaderClass, ellipsoid);
if (projectionParameters != null) {
defaultProjectionParameters.putAll(projectionParameters);
}
this.boundingBox = boundingBox;
this.axisOrder = axisOrder;
}
public static CoordinateReferenceSystem getForCode(String code) {
CoordinateReferenceSystem crs = crss.get(code);
// TODO: handle extra parameters like
// AUTO2:42003,0.3048006096012192,-100,45. See ISO/DIS 19128 wms v1.3.0
// chapter 6.7.3.4
// TODO: clone to simplify transformator by not being thread safe?
return crs;
}
public static Collection<String> getCodes() {
return crss.keySet();
}
protected ProjectionLoader projectionLoader() {
if (projLoader != null) {
return projLoader;
}
try {
Class<?> cl = Class.forName(projLoaderClassName);
Object o = cl.newInstance();
projLoader = (ProjectionLoader) o;
return projLoader;
} catch (ClassNotFoundException e) {
throw new IllegalStateException(e.getMessage());
} catch (InstantiationException e) {
throw new IllegalStateException(e.getMessage());
} catch (IllegalAccessException e) {
throw new IllegalStateException(e.getMessage());
}
}
public GeoProj createProjection(Properties overrideProjectionParameters) {
Properties projectionParameters = new Properties();
projectionParameters.putAll(defaultProjectionParameters);
projectionParameters.putAll(overrideProjectionParameters);
return (GeoProj) projectionLoader().create(projectionParameters);
}
/**
* Return a EPSG code like "EPSG:4326"
*
* @return EPSG code
*/
public String getCode() {
return code;
}
/**
* Return the bounding box of this coordinate system or null if the bounding
* box is not defined.
*/
public BoundingBox getBoundingBox() {
return boundingBox;
}
public AxisOrder getAxisOrder() {
return axisOrder;
}
public void prepareProjection(GeoProj proj) {
// TODO: do we need this??
proj.setPlanetRadius(ellipsoid.radius);
}
/**
* Convert the given (projected) coordinate in the CRS to a LatLonPoint
* without respect for axis order.
*
* @param x
* @param y
* @return LatLonPoint from inverse projected x, y coordinate
*/
public LatLonPoint inverse(double x, double y) {
return coordTransform.inverse(x, y);
}
/**
* Convert the given (projected) coordinate in the CRS to a LatLonPoint. If
* the useAxisOrder parameter is true, then the
* {@link CoordinateReferenceSystem}s {@link AxisOrder} will be used.
*
* @param x
* @param y
* @return LatLonPoint from inverse projected x, y coordinate
*/
public LatLonPoint inverse(double x, double y, boolean useAxisOrder) {
if (useAxisOrder && (getAxisOrder() == AxisOrder.northBeforeEast)) {
return coordTransform.inverse(y, x);
}
return coordTransform.inverse(x, y);
}
public Point2D forward(double lat, double lon) {
return coordTransform.forward(lat, lon);
}
public Point2D forward(double lat, double lon, boolean useAxisOrder) {
Point2D p = coordTransform.forward(lat, lon);
if (useAxisOrder && (getAxisOrder() == AxisOrder.northBeforeEast)) {
double x = p.getX();
double y = p.getY();
p.setLocation(y, x);
}
return p;
}
public int hashCode() {
return getCode().hashCode();
}
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
CoordinateReferenceSystem o = (CoordinateReferenceSystem) obj;
return getCode().equals(o.getCode());
}
}