diff --git a/CHANGELOG.md b/CHANGELOG.md index 7157e8a..625b442 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,17 +6,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [1.1.0] - 2019-09-05 + ### Added -- Added `GeostationarySatelliteProjection`/`geos` projection by [@Yaqiang](https://github.com/Yaqiang) -- Registry.getProjections exposes all available projects by [@noberasco](https://github.com/noberasco) -- OSGi compatibility by [@Neutius](https://github.com/Neutius) +- Added `GeostationarySatelliteProjection`/`geos` projection +- Registry.getProjections exposes all available projects +- OSGi compatibility ### Changed -- Parse `geos` (Geostationary Satellite Projection) proj4 strings by [@pomadchin](https://github.com/pomadchin) -- Projection units reported as meters by default by [@bosborn](https://github.com/bosborn) -- BasicCoordinateTransform now thread-safe by [@sebasbaumh](https://github.com/sebasbaumh) -- Improve CRS Caching performance by [@pomadchin](https://github.com/pomadchin) -- CoordinateReferenceSystem.equals considered logical equality by [@pomadchin](https://github.com/pomadchin) +- Parse `geos` (Geostationary Satellite Projection) proj4 strings +- Projection units reported as meters by default +- BasicCoordinateTransform now thread-safe +- Improve CRS Caching performance +- CoordinateReferenceSystem.equals considered logical equality +- Projection.equals considered logical equality ## [1.0.0] - 2019-12-12 diff --git a/pom.xml b/pom.xml index 457f5a2..76c23ba 100755 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.locationtech.proj4j proj4j - 1.0.1-SNAPSHOT + 1.1.0-SNAPSHOT bundle Proj4J https://github.com/locationtech/proj4j @@ -120,7 +120,7 @@ true - + diff --git a/src/main/java/org/locationtech/proj4j/CoordinateReferenceSystem.java b/src/main/java/org/locationtech/proj4j/CoordinateReferenceSystem.java index cc0b993..ab78ab2 100755 --- a/src/main/java/org/locationtech/proj4j/CoordinateReferenceSystem.java +++ b/src/main/java/org/locationtech/proj4j/CoordinateReferenceSystem.java @@ -129,7 +129,8 @@ public boolean equals(Object that) { } if (that instanceof CoordinateReferenceSystem) { CoordinateReferenceSystem cr = (CoordinateReferenceSystem) that; - return name.equals(cr.name) && datum.isEqual(cr.getDatum()) && Arrays.equals(params, cr.params); + // Projection equality contains Ellipsoid and Unit equality + return datum.isEqual(cr.getDatum()) && proj.equals(cr.proj); } return false; } diff --git a/src/main/java/org/locationtech/proj4j/datum/AxisOrder.java b/src/main/java/org/locationtech/proj4j/datum/AxisOrder.java index 9cf8862..8c6da20 100644 --- a/src/main/java/org/locationtech/proj4j/datum/AxisOrder.java +++ b/src/main/java/org/locationtech/proj4j/datum/AxisOrder.java @@ -86,7 +86,7 @@ static Axis fromChar(char c) { public abstract void toENU(double x, ProjCoordinate c); } - public final static AxisOrder ENU = + public final static AxisOrder ENU = new AxisOrder(Axis.Easting, Axis.Northing, Axis.Up); private final Axis x, y, z; diff --git a/src/main/java/org/locationtech/proj4j/proj/AiryProjection.java b/src/main/java/org/locationtech/proj4j/proj/AiryProjection.java index f93b439..a8dd2f3 100644 --- a/src/main/java/org/locationtech/proj4j/proj/AiryProjection.java +++ b/src/main/java/org/locationtech/proj4j/proj/AiryProjection.java @@ -45,7 +45,7 @@ public AiryProjection() { maxLongitude = Math.toRadians(90); initialize(); } - + public ProjCoordinate project(double lplam, double lpphi, ProjCoordinate out) { double sinlam, coslam, cosphi, sinphi, t, s, Krho, cosz; diff --git a/src/main/java/org/locationtech/proj4j/proj/AitoffProjection.java b/src/main/java/org/locationtech/proj4j/proj/AitoffProjection.java index b79a4cc..e3326d1 100644 --- a/src/main/java/org/locationtech/proj4j/proj/AitoffProjection.java +++ b/src/main/java/org/locationtech/proj4j/proj/AitoffProjection.java @@ -23,7 +23,7 @@ public class AitoffProjection extends PseudoCylindricalProjection { - + protected final static int AITOFF = 0; protected final static int WINKEL = 1; @@ -65,7 +65,7 @@ public void initialize() { cosphi1 = 0.636619772367581343; } } - + public boolean hasInverse() { return false; } @@ -74,5 +74,15 @@ public String toString() { return winkel ? "Winkel Tripel" : "Aitoff"; } + @Override + public boolean equals(Object that) { + if (this == that) { + return true; + } + if (that instanceof AitoffProjection) { + AitoffProjection p = (AitoffProjection) that; + return (winkel == p.winkel && super.equals(that)); + } + return false; + } } - diff --git a/src/main/java/org/locationtech/proj4j/proj/AzimuthalProjection.java b/src/main/java/org/locationtech/proj4j/proj/AzimuthalProjection.java index 99ccb78..b2c64b2 100644 --- a/src/main/java/org/locationtech/proj4j/proj/AzimuthalProjection.java +++ b/src/main/java/org/locationtech/proj4j/proj/AzimuthalProjection.java @@ -27,11 +27,11 @@ public abstract class AzimuthalProjection extends Projection { public final static int SOUTH_POLE = 2; public final static int EQUATOR = 3; public final static int OBLIQUE = 4; - + protected int mode; protected double sinphi0, cosphi0; private double mapRadius = 90.0; - + public AzimuthalProjection() { this( Math.toRadians(45.0), Math.toRadians(45.0) ); } @@ -41,7 +41,7 @@ public AzimuthalProjection(double projectionLatitude, double projectionLongitude this.projectionLongitude = projectionLongitude; initialize(); } - + public void initialize() { super.initialize(); if (Math.abs(Math.abs(projectionLatitude) - ProjectionMath.HALFPI) < EPS10) @@ -69,5 +69,20 @@ public double getMapRadius() { return mapRadius; } + @Override + public boolean equals(Object that) { + if (this == that) { + return true; + } + if (that instanceof AzimuthalProjection) { + AzimuthalProjection p = (AzimuthalProjection) that; + return ( + mode == p.mode && + sinphi0 == p.sinphi0 && + cosphi0 == p.cosphi0 && + mapRadius == p.mapRadius && + super.equals(that)); + } + return false; + } } - diff --git a/src/main/java/org/locationtech/proj4j/proj/BipolarProjection.java b/src/main/java/org/locationtech/proj4j/proj/BipolarProjection.java index eb92360..fb8eff8 100644 --- a/src/main/java/org/locationtech/proj4j/proj/BipolarProjection.java +++ b/src/main/java/org/locationtech/proj4j/proj/BipolarProjection.java @@ -54,7 +54,7 @@ public BipolarProjection() { minLongitude = Math.toRadians(-90); maxLongitude = Math.toRadians(90); } - + public ProjCoordinate project(double lplam, double lpphi, ProjCoordinate out) { double cphi, sphi, tphi, t, al, Az, z, Av, cdlam, sdlam, r; boolean tag; @@ -112,8 +112,8 @@ public ProjCoordinate project(double lplam, double lpphi, ProjCoordinate out) { out.y += (tag ? -r : r) * Math.cos(t); if (noskew) { t = out.x; - out.x = -out.x * cAzc - out.y * sAzc; - out.y = -out.y * cAzc + t * sAzc; + out.x = -out.x * cAzc - out.y * sAzc; + out.y = -out.y * cAzc + t * sAzc; } return out; } @@ -125,8 +125,8 @@ public ProjCoordinate projectInverse(double xyx, double xyy, ProjCoordinate out) if (noskew) { t = xyx; - out.x = -xyx * cAzc + xyy * sAzc; - out.y = -xyy * cAzc - t * sAzc; + out.x = -xyx * cAzc + xyy * sAzc; + out.y = -xyy * cAzc - t * sAzc; } if (neg = (xyx < 0.)) { out.y = rhoc - xyy; diff --git a/src/main/java/org/locationtech/proj4j/proj/CentralCylindricalProjection.java b/src/main/java/org/locationtech/proj4j/proj/CentralCylindricalProjection.java index 01e5982..7bcff04 100644 --- a/src/main/java/org/locationtech/proj4j/proj/CentralCylindricalProjection.java +++ b/src/main/java/org/locationtech/proj4j/proj/CentralCylindricalProjection.java @@ -25,15 +25,13 @@ public class CentralCylindricalProjection extends CylindricalProjection { - private double ap; - private final static double EPS10 = 1.e-10; public CentralCylindricalProjection() { minLatitude = Math.toRadians(-80); maxLatitude = Math.toRadians(80); } - + public ProjCoordinate project(double lplam, double lpphi, ProjCoordinate out) { if (Math.abs(Math.abs(lpphi) - ProjectionMath.HALFPI) <= EPS10) throw new ProjectionException("F"); out.x = lplam; diff --git a/src/main/java/org/locationtech/proj4j/proj/CylindricalEqualAreaProjection.java b/src/main/java/org/locationtech/proj4j/proj/CylindricalEqualAreaProjection.java index 5108c63..76b8a96 100644 --- a/src/main/java/org/locationtech/proj4j/proj/CylindricalEqualAreaProjection.java +++ b/src/main/java/org/locationtech/proj4j/proj/CylindricalEqualAreaProjection.java @@ -31,14 +31,14 @@ public class CylindricalEqualAreaProjection extends Projection { public CylindricalEqualAreaProjection() { this(0.0, 0.0, 0.0); } - + public CylindricalEqualAreaProjection(double projectionLatitude, double projectionLongitude, double trueScaleLatitude) { this.projectionLatitude = projectionLatitude; this.projectionLongitude = projectionLongitude; this.trueScaleLatitude = trueScaleLatitude; initialize(); } - + public void initialize() { super.initialize(); double t = trueScaleLatitude; @@ -51,7 +51,7 @@ public void initialize() { qp = ProjectionMath.qsfn(1., e, one_es); } } - + public ProjCoordinate project(double lam, double phi, ProjCoordinate xy) { if (spherical) { xy.x = scaleFactor * lam; @@ -90,4 +90,3 @@ public boolean isRectilinear() { } } - diff --git a/src/main/java/org/locationtech/proj4j/proj/ExtendedTransverseMercatorProjection.java b/src/main/java/org/locationtech/proj4j/proj/ExtendedTransverseMercatorProjection.java index bc17b2f..2099857 100644 --- a/src/main/java/org/locationtech/proj4j/proj/ExtendedTransverseMercatorProjection.java +++ b/src/main/java/org/locationtech/proj4j/proj/ExtendedTransverseMercatorProjection.java @@ -9,19 +9,23 @@ * @see proj_etmerc.c */ public class ExtendedTransverseMercatorProjection extends CylindricalProjection { - + private static final long serialVersionUID = 1L; - - double Qn; /* Merid. quad., scaled to the projection */ - double Zb; /* Radius vector in polar coord. systems */ - double[] cgb = new double[6]; /* Constants for Gauss -> Geo lat */ - double[] cbg = new double[6]; /* Constants for Geo lat -> Gauss */ - double[] utg = new double[6]; /* Constants for transv. merc. -> geo */ + + double Qn; /* Merid. quad., scaled to the projection */ + double Zb; /* Radius vector in polar coord. systems */ + double[] cgb = new double[6]; /* Constants for Gauss -> Geo lat */ + double[] cbg = new double[6]; /* Constants for Geo lat -> Gauss */ + double[] utg = new double[6]; /* Constants for transv. merc. -> geo */ double[] gtu = new double[6]; /* Constants for geo -> transv. merc. */ + /** + * Indicates whether a Southern Hemisphere UTM zone + */ + protected boolean isSouth = false; private static final int PROJ_ETMERC_ORDER = 6; private static final double HUGE_VAL = Double.POSITIVE_INFINITY; - + public ExtendedTransverseMercatorProjection() { ellipsoid = Ellipsoid.GRS80; projectionLatitude = Math.toRadians(0); @@ -30,7 +34,7 @@ public ExtendedTransverseMercatorProjection() { maxLongitude = Math.toRadians(90); initialize(); } - + public ExtendedTransverseMercatorProjection(Ellipsoid ellipsoid, double lon_0, double lat_0, double k, double x_0, double y_0) { setEllipsoid(ellipsoid); projectionLongitude = lon_0; @@ -40,7 +44,17 @@ public ExtendedTransverseMercatorProjection(Ellipsoid ellipsoid, double lon_0, d falseNorthing = y_0; initialize(); } - + + @Override + public void setSouthernHemisphere(boolean isSouth) { + this.isSouth = isSouth; + } + + @Override + public boolean getSouthernHemisphere() { + return isSouth; + } + static double log1py(double x) { /* Compute log(1+x) accurately */ double y = 1 + x; double z = y - 1; @@ -50,26 +64,26 @@ static double log1py(double x) { /* Compute log(1+x) accurately */ * (log(y)/z) introduces little additional error. */ return z == 0 ? x : x * Math.log(y) / z; } - + static double asinhy(double x) { /* Compute asinh(x) accurately */ double y = Math.abs(x); /* Enforce odd parity */ y = log1py(y * (1 + y/(Math.hypot(1.0, y) + 1))); return x < 0 ? -y : y; } - + static double gatg(double[] p1, int len_p1, double B) { double h = 0, h1, h2 = 0; double cos_2B = 2*Math.cos(2*B); - + int p1i; for (p1i = len_p1, h1 = p1[--p1i]; p1i > 0; h2 = h1, h1 = h) { h = -h2 + cos_2B*h1 + p1[--p1i]; } - + return (B + h*Math.sin(2*B)); } - + static double clenS(double[] a, int size, double arg_r, double arg_i, double[] R, double[] I) { double hr, hr1, hr2, hi, hi1, hi2; @@ -115,7 +129,7 @@ static double clens(double[] a, int size, double arg_r) { } return Math.sin (arg_r)*hr; } - + public ProjCoordinate project(double lplam, double lpphi, ProjCoordinate xy) { double sin_Cn, cos_Cn, cos_Ce, sin_Ce; double[] dCn = new double[1]; @@ -171,10 +185,10 @@ public ProjCoordinate projectInverse(double x, double y, ProjCoordinate out) { out.y = gatg (cgb, PROJ_ETMERC_ORDER, Cn); out.x = Ce; } - + return out; } - + public void setUTMZone(int zone) { zone--; projectionLongitude = (zone + .5) * Math.PI / 30. - Math.PI; @@ -184,10 +198,10 @@ public void setUTMZone(int zone) { falseNorthing = isSouth ? 10000000.0 : 0.0; initialize(); } - + public void initialize() { super.initialize(); - + double f, n, np, Z; if (es <= 0) { @@ -270,15 +284,15 @@ public void initialize() { /* i.e. true northing = N - P->Zb */ Zb = - Qn*(Z + clens(gtu, PROJ_ETMERC_ORDER, 2*Z)); } - + public boolean hasInverse() { return true; } - + public boolean isRectilinear() { return false; } - + public Object clone() { ExtendedTransverseMercatorProjection p = (ExtendedTransverseMercatorProjection) super.clone(); if (cgb != null) { diff --git a/src/main/java/org/locationtech/proj4j/proj/GeostationarySatelliteProjection.java b/src/main/java/org/locationtech/proj4j/proj/GeostationarySatelliteProjection.java index f4d6dac..8d8d1ce 100644 --- a/src/main/java/org/locationtech/proj4j/proj/GeostationarySatelliteProjection.java +++ b/src/main/java/org/locationtech/proj4j/proj/GeostationarySatelliteProjection.java @@ -14,12 +14,17 @@ */ public class GeostationarySatelliteProjection extends Projection { + /** + * Height of orbit - Geostationary satellite projection + */ + protected double heightOfOrbit = 35785831.0; + private double _radiusP; private double _radiusP2; private double _radiusPInv2; private double _radiusG; private double _radiusG1; - private double _c; + private double _c; /** * Constructor @@ -43,6 +48,17 @@ public void initialize() { } } + + @Override + public double getHeightOfOrbit(){ + return this.heightOfOrbit; + } + + @Override + public void setHeightOfOrbit(double h){ + this.heightOfOrbit = h; + } + @Override public ProjCoordinate project(double lplam, double lpphi, ProjCoordinate out) { if (spherical) { @@ -187,4 +203,16 @@ public boolean hasInverse() { public String toString() { return "Geostationary Satellite"; } + + @Override + public boolean equals(Object that) { + if (this == that) { + return true; + } + if (that instanceof GeostationarySatelliteProjection) { + GeostationarySatelliteProjection p = (GeostationarySatelliteProjection) that; + return (this.heightOfOrbit == p.heightOfOrbit) && super.equals(that); + } + return false; + } } diff --git a/src/main/java/org/locationtech/proj4j/proj/HammerProjection.java b/src/main/java/org/locationtech/proj4j/proj/HammerProjection.java index b7e3c09..baa2444 100644 --- a/src/main/java/org/locationtech/proj4j/proj/HammerProjection.java +++ b/src/main/java/org/locationtech/proj4j/proj/HammerProjection.java @@ -30,7 +30,7 @@ public class HammerProjection extends PseudoCylindricalProjection { public HammerProjection() { } - + public ProjCoordinate project(double lplam, double lpphi, ProjCoordinate xy) { double cosphi, d; @@ -66,21 +66,35 @@ public boolean isEqualArea() { public void setW( double w ) { this.w = w; } - + public double getW() { return w; } - + public void setM( double m ) { this.m = m; } - + public double getM() { return m; } - + public String toString() { return "Hammer & Eckert-Greifendorff"; } + @Override + public boolean equals(Object that) { + if (this == that) { + return true; + } + if (that instanceof HammerProjection) { + HammerProjection p = (HammerProjection) that; + return ( + m == p.m && + w == p.w && + super.equals(that)); + } + return false; + } } diff --git a/src/main/java/org/locationtech/proj4j/proj/LagrangeProjection.java b/src/main/java/org/locationtech/proj4j/proj/LagrangeProjection.java index fc64dd2..ce91be1 100644 --- a/src/main/java/org/locationtech/proj4j/proj/LagrangeProjection.java +++ b/src/main/java/org/locationtech/proj4j/proj/LagrangeProjection.java @@ -53,7 +53,7 @@ public ProjCoordinate project(double lplam, double lpphi, ProjCoordinate xy) { public void setW( double w ) { this.rw = w; } - + public double getW() { return rw; } @@ -75,7 +75,7 @@ public void initialize() { public boolean isConformal() { return true; } - + public boolean hasInverse() { return false; } @@ -84,4 +84,15 @@ public String toString() { return "Lagrange"; } + @Override + public boolean equals(Object that) { + if (this == that) { + return true; + } + if (that instanceof LagrangeProjection) { + LagrangeProjection p = (LagrangeProjection) that; + return (rw == p.rw) && super.equals(that); + } + return false; + } } diff --git a/src/main/java/org/locationtech/proj4j/proj/MolleweideProjection.java b/src/main/java/org/locationtech/proj4j/proj/MolleweideProjection.java index 9e9eaf2..a1cd92c 100644 --- a/src/main/java/org/locationtech/proj4j/proj/MolleweideProjection.java +++ b/src/main/java/org/locationtech/proj4j/proj/MolleweideProjection.java @@ -36,7 +36,7 @@ public class MolleweideProjection extends PseudoCylindricalProjection { public MolleweideProjection() { this(Math.PI/2); } - + public MolleweideProjection(int type) { this.type = type; switch (type) { @@ -54,11 +54,11 @@ public MolleweideProjection(int type) { break; } } - + public MolleweideProjection(double p) { init(p); } - + public void init(double p) { double r, sp, p2 = p + p; @@ -96,7 +96,7 @@ public ProjCoordinate project(double lplam, double lpphi, ProjCoordinate xy) { public ProjCoordinate projectInverse(double x, double y, ProjCoordinate lp) { double lat, lon; - + lat = Math.asin(y / cy); lon = x / (cx * Math.cos(lat)); lat += lat; @@ -105,7 +105,7 @@ public ProjCoordinate projectInverse(double x, double y, ProjCoordinate lp) { lp.y = lat; return lp; } - + public boolean hasInverse() { return true; } @@ -113,7 +113,7 @@ public boolean hasInverse() { public boolean isEqualArea() { return true; } - + public String toString() { switch (type) { case WAGNER4: @@ -123,4 +123,21 @@ public String toString() { } return "Molleweide"; } + + @Override + public boolean equals(Object that) { + if (this == that) { + return true; + } + if (that instanceof MolleweideProjection) { + MolleweideProjection p = (MolleweideProjection) that; + return ( + type == p.type && + cx == p.cx && + cy == p.cy && + cp == p.cp && + super.equals(that)); + } + return false; + } } diff --git a/src/main/java/org/locationtech/proj4j/proj/ObliqueMercatorProjection.java b/src/main/java/org/locationtech/proj4j/proj/ObliqueMercatorProjection.java index aab85c5..ae94fa3 100644 --- a/src/main/java/org/locationtech/proj4j/proj/ObliqueMercatorProjection.java +++ b/src/main/java/org/locationtech/proj4j/proj/ObliqueMercatorProjection.java @@ -45,7 +45,7 @@ public ObliqueMercatorProjection() { alpha = Math.toRadians(-45);//FIXME initialize(); } - + /** * Set up a projection suitable for State Plane Coordinates. */ @@ -59,7 +59,7 @@ public ObliqueMercatorProjection(Ellipsoid ellipsoid, double lon_0, double lat_0 falseNorthing = y_0; initialize(); } - + public void initialize() { super.initialize(); double con, com, cosphi0, d, f, h, l, sinphi0, p, j; @@ -67,7 +67,7 @@ public void initialize() { //FIXME-setup rot, alpha, longc,lon/lat1/2 rot = true; lamc = lonc; - + // true if alpha provided int azi = Double.isNaN(alpha) ? 0 : 1; if (azi != 0) { // alpha specified @@ -228,4 +228,19 @@ public String toString() { return "Oblique Mercator"; } + @Override + public boolean equals(Object that) { + if (this == that) { + return true; + } + if (that instanceof ObliqueMercatorProjection) { + ObliqueMercatorProjection p = (ObliqueMercatorProjection) that; + return ( + Gamma == p.Gamma && + alpha == p.alpha && + lonc == p.lonc && + super.equals(that)); + } + return false; + } } diff --git a/src/main/java/org/locationtech/proj4j/proj/Projection.java b/src/main/java/org/locationtech/proj4j/proj/Projection.java index 5e820fd..22bbbb9 100644 --- a/src/main/java/org/locationtech/proj4j/proj/Projection.java +++ b/src/main/java/org/locationtech/proj4j/proj/Projection.java @@ -16,6 +16,8 @@ package org.locationtech.proj4j.proj; +import java.util.NoSuchElementException; + import org.locationtech.proj4j.*; import org.locationtech.proj4j.datum.AxisOrder; import org.locationtech.proj4j.datum.Ellipsoid; @@ -102,10 +104,6 @@ public abstract class Projection implements Cloneable, java.io.Serializable { */ protected double falseNorthing = 0; - /** - * Indicates whether a Southern Hemisphere UTM zone - */ - protected boolean isSouth = false; /** * The latitude of true scale. Only used by specific projections. */ @@ -191,11 +189,6 @@ public abstract class Projection implements Cloneable, java.io.Serializable { * northing, vertical (up) */ private AxisOrder axes = AxisOrder.ENU; - - /** - * Height of orbit - Geostationary satellite projection - */ - protected double heightOfOrbit = 35785831.0; // Some useful constants protected final static double EPS10 = 1e-10; @@ -701,12 +694,13 @@ public double getFalseEasting() { return falseEasting; } - public void setSouthernHemisphere(boolean isSouth) - { - this.isSouth = isSouth; + public void setSouthernHemisphere(boolean isSouth) { + throw new NoSuchElementException(); } - public boolean getSouthernHemisphere() { return isSouth; } + public boolean getSouthernHemisphere() { + throw new NoSuchElementException(); + } /** * Set the projection scale factor. This is set to 1 by default. @@ -767,21 +761,21 @@ public void setUnits(Unit unit) public Unit getUnits() { return this.unit != null ? this.unit : Units.METRES; } - + /** * Get height of orbit - Geostationary satellite projection * @return Height of orbit */ public double getHeightOfOrbit(){ - return this.heightOfOrbit; + throw new NoSuchElementException(); } - + /** * Set height of orbit - Geostationary satellite projection * @param h Height of orbit */ public void setHeightOfOrbit(double h){ - this.heightOfOrbit = h; + throw new NoSuchElementException(); } /** @@ -825,4 +819,36 @@ public void setGamma(double gamma) { public Boolean isGeographic() { return false; } + + @Override + public boolean equals(Object that) { + if (this == that) { + return true; + } + if (that instanceof Projection) { + Projection p = (Projection) that; + // Using Double.compare when values can be NaN and should still be equal + return ( + // class represents implementation of project method + this.getClass().equals(that.getClass()) && + ellipsoid.isEqual(p.ellipsoid) && + falseNorthing == p.falseNorthing && + falseEasting == p.falseEasting && + scaleFactor == p.scaleFactor && + fromMetres == p.fromMetres && + trueScaleLatitude == p.trueScaleLatitude && + projectionLatitude == p.projectionLatitude && + projectionLongitude == p.projectionLongitude && + projectionLatitude1 == p.projectionLatitude1 && + projectionLatitude2 == p.projectionLatitude2 && + minLatitude == p.minLatitude && + maxLatitude == p.maxLatitude && + minLongitude == p.minLongitude && + maxLongitude == p.maxLongitude && + axes.equals(p.axes) && + unit.equals(p.unit) && + primeMeridian.equals(p.primeMeridian)); + } + return false; + } } diff --git a/src/main/java/org/locationtech/proj4j/proj/SimpleConicProjection.java b/src/main/java/org/locationtech/proj4j/proj/SimpleConicProjection.java index 4a8dbe7..0b0d4dc 100644 --- a/src/main/java/org/locationtech/proj4j/proj/SimpleConicProjection.java +++ b/src/main/java/org/locationtech/proj4j/proj/SimpleConicProjection.java @@ -44,13 +44,13 @@ public class SimpleConicProjection extends ConicProjection { public SimpleConicProjection() { this( EULER ); } - + public SimpleConicProjection(int type) { this.type = type; minLatitude = Math.toRadians(0); maxLatitude = Math.toRadians(80); } - + public String toString() { return "Simple Conic"; } @@ -157,7 +157,7 @@ public void initialize() { case EULER: n = Math.sin(sig) * Math.sin(del) / del; del *= 0.5; - rho_c = del / (Math.tan(del) * Math.tan(sig)) + sig; + rho_c = del / (Math.tan(del) * Math.tan(sig)) + sig; rho_0 = rho_c - projectionLatitude; break; case PCONIC: @@ -176,4 +176,16 @@ public void initialize() { break; } } + + @Override + public boolean equals(Object that) { + if (this == that) { + return true; + } + if (that instanceof SimpleConicProjection) { + SimpleConicProjection p = (SimpleConicProjection) that; + return (this.type == p.type) && super.equals(that); + } + return false; + } } diff --git a/src/main/java/org/locationtech/proj4j/proj/SineTangentSeriesProjection.java b/src/main/java/org/locationtech/proj4j/proj/SineTangentSeriesProjection.java index ef9b672..7294854 100644 --- a/src/main/java/org/locationtech/proj4j/proj/SineTangentSeriesProjection.java +++ b/src/main/java/org/locationtech/proj4j/proj/SineTangentSeriesProjection.java @@ -36,7 +36,7 @@ protected SineTangentSeriesProjection( double p, double q, boolean mode ) { tan_mode = mode; initialize(); } - + public ProjCoordinate project(double lplam, double lpphi, ProjCoordinate xy) { double c; @@ -56,7 +56,7 @@ public ProjCoordinate project(double lplam, double lpphi, ProjCoordinate xy) { public ProjCoordinate projectInverse(double xyx, double xyy, ProjCoordinate lp) { double c; - + xyy /= C_y; c = Math.cos(lp.y = tan_mode ? Math.atan(xyy) : ProjectionMath.asin(xyy)); lp.y /= C_p; @@ -72,4 +72,20 @@ public boolean hasInverse() { return true; } + @Override + public boolean equals(Object that) { + if (this == that) { + return true; + } + if (that instanceof SineTangentSeriesProjection) { + SineTangentSeriesProjection p = (SineTangentSeriesProjection) that; + return ( + C_x == p.C_x && + C_y == p.C_y && + C_p == p.C_p && + tan_mode == p.tan_mode && + super.equals(that)); + } + return false; + } } diff --git a/src/main/java/org/locationtech/proj4j/proj/StereographicAzimuthalProjection.java b/src/main/java/org/locationtech/proj4j/proj/StereographicAzimuthalProjection.java index 78e01d3..2837a5d 100644 --- a/src/main/java/org/locationtech/proj4j/proj/StereographicAzimuthalProjection.java +++ b/src/main/java/org/locationtech/proj4j/proj/StereographicAzimuthalProjection.java @@ -25,9 +25,9 @@ public class StereographicAzimuthalProjection extends AzimuthalProjection { private final static double TOL = 1.e-8; - + private double akm1; - + public StereographicAzimuthalProjection() { this(Math.toRadians(90.0), Math.toRadians(0.0)); } @@ -36,7 +36,7 @@ public StereographicAzimuthalProjection(double projectionLatitude, double projec super(projectionLatitude, projectionLongitude); initialize(); } - + public void setupUPS(int pole) { projectionLatitude = (pole == SOUTH_POLE) ? -ProjectionMath.HALFPI: ProjectionMath.HALFPI; projectionLongitude = 0.0; @@ -46,7 +46,7 @@ public void setupUPS(int pole) { trueScaleLatitude = ProjectionMath.HALFPI; initialize(); } - + public void initialize() { double t; @@ -245,14 +245,14 @@ public ProjCoordinate projectInverse(double x, double y, ProjCoordinate lp) { } return lp; } - + /** * Returns true if this projection is conformal */ public boolean isConformal() { return true; } - + public boolean hasInverse() { return true; } @@ -266,6 +266,4 @@ private double ssfn(double phit, double sinphi, double eccen) { public String toString() { return "Stereographic Azimuthal"; } - -} - +} \ No newline at end of file diff --git a/src/main/java/org/locationtech/proj4j/proj/TransverseMercatorProjection.java b/src/main/java/org/locationtech/proj4j/proj/TransverseMercatorProjection.java index fdb56d4..44a3d0b 100644 --- a/src/main/java/org/locationtech/proj4j/proj/TransverseMercatorProjection.java +++ b/src/main/java/org/locationtech/proj4j/proj/TransverseMercatorProjection.java @@ -27,7 +27,7 @@ * Transverse Mercator Projection algorithm is taken from the USGS PROJ package. */ public class TransverseMercatorProjection extends CylindricalProjection { - + private final static double FC1 = 1.0; private final static double FC2 = 0.5; private final static double FC3 = 0.16666666666666666666; @@ -37,6 +37,10 @@ public class TransverseMercatorProjection extends CylindricalProjection { private final static double FC7 = 0.02380952380952380952; private final static double FC8 = 0.01785714285714285714; + /** + * Indicates whether a Southern Hemisphere UTM zone + */ + protected boolean isSouth = false; private int utmZone = -1; private double esp; private double ml0; @@ -50,7 +54,7 @@ public TransverseMercatorProjection() { maxLongitude = Math.toRadians(90); initialize(); } - + /** * Set up a projection suitable for State Plane Coordinates. */ @@ -63,14 +67,24 @@ public TransverseMercatorProjection(Ellipsoid ellipsoid, double lon_0, double la falseNorthing = y_0; initialize(); } - + + @Override + public void setSouthernHemisphere(boolean isSouth) { + this.isSouth = isSouth; + } + + @Override + public boolean getSouthernHemisphere() { + return isSouth; + } + public Object clone() { TransverseMercatorProjection p = (TransverseMercatorProjection)super.clone(); if (en != null) p.en = (double[])en.clone(); return p; } - + public boolean isRectilinear() { return false; } @@ -95,7 +109,7 @@ public static int getRowFromNearestParallel(double latitude) { return 24; return (degrees + 80) / 8 + 3; } - + public static int getZoneFromNearestMeridian(double longitude) { int zone = (int)Math.floor((ProjectionMath.normalizeLongitude(longitude) + Math.PI) * 30.0 / Math.PI) + 1; if (zone < 1) @@ -104,7 +118,7 @@ else if (zone > 60) zone = 60; return zone; } - + public void setUTMZone(int zone) { utmZone = zone; zone--; diff --git a/src/main/java/org/locationtech/proj4j/proj/UrmaevFlatPolarSinusoidalProjection.java b/src/main/java/org/locationtech/proj4j/proj/UrmaevFlatPolarSinusoidalProjection.java index 9e2047e..5dede37 100644 --- a/src/main/java/org/locationtech/proj4j/proj/UrmaevFlatPolarSinusoidalProjection.java +++ b/src/main/java/org/locationtech/proj4j/proj/UrmaevFlatPolarSinusoidalProjection.java @@ -33,7 +33,7 @@ public class UrmaevFlatPolarSinusoidalProjection extends Projection { public UrmaevFlatPolarSinusoidalProjection() { } - + public ProjCoordinate project(double lplam, double lpphi, ProjCoordinate out) { out.y = ProjectionMath.asin(n * Math.sin(lpphi)); out.x = C_x * lplam * Math.cos(lpphi); @@ -63,13 +63,24 @@ public void initialize() { // urmfps public void setN( double n ) { this.n = n; } - + public double getN() { return n; } - + public String toString() { return "Urmaev Flat-Polar Sinusoidal"; } + @Override + public boolean equals(Object that) { + if (this == that) { + return true; + } + if (that instanceof UrmaevFlatPolarSinusoidalProjection) { + UrmaevFlatPolarSinusoidalProjection p = (UrmaevFlatPolarSinusoidalProjection) that; + return (n == p.n) && super.equals(that); + } + return false; + } } diff --git a/src/test/java/org/locationtech/proj4j/proj/ProjectionEqualityTest.java b/src/test/java/org/locationtech/proj4j/proj/ProjectionEqualityTest.java new file mode 100644 index 0000000..9860373 --- /dev/null +++ b/src/test/java/org/locationtech/proj4j/proj/ProjectionEqualityTest.java @@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright 2019 Azavea + * + * 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 org.locationtech.proj4j.proj; + +import org.locationtech.proj4j.CRSFactory; +import org.locationtech.proj4j.CoordinateReferenceSystem; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +import org.junit.Test; + +/** + * Tests that Projection equality is semantically correct + */ +public class ProjectionEqualityTest +{ + private static CRSFactory csFactory = new CRSFactory(); + + @Test + public void utmEquality() { + + CoordinateReferenceSystem cs1 = csFactory.createFromName("EPSG:26710"); + CoordinateReferenceSystem cs2 = csFactory.createFromParameters(null, "+proj=utm +zone=10 +datum=NAD27 +units=m +no_defs"); + assertEquals(cs1, cs2); + + CoordinateReferenceSystem cs3 = csFactory.createFromName("EPSG:26711"); + assertNotEquals(cs1, cs3); + } +} diff --git a/src/test/java/org/locationtech/proj4j/proj/ProjectionGridRoundTripper.java b/src/test/java/org/locationtech/proj4j/proj/ProjectionGridRoundTripper.java index faaa96b..773799c 100644 --- a/src/test/java/org/locationtech/proj4j/proj/ProjectionGridRoundTripper.java +++ b/src/test/java/org/locationtech/proj4j/proj/ProjectionGridRoundTripper.java @@ -23,7 +23,7 @@ import org.locationtech.proj4j.proj.Projection; import org.locationtech.proj4j.util.ProjectionUtil; -public class ProjectionGridRoundTripper +public class ProjectionGridRoundTripper { private static final CoordinateTransformFactory ctFactory = new CoordinateTransformFactory(); CRSFactory csFactory = new CRSFactory(); @@ -38,38 +38,36 @@ public class ProjectionGridRoundTripper private boolean debug = false; private int transformCount = 0; private double[] gridExtent; - + public ProjectionGridRoundTripper(CoordinateReferenceSystem cs) { this.cs = cs; - transInverse = ctFactory.createTransform(cs, WGS84); - transForward = ctFactory.createTransform(WGS84, cs); + transInverse = ctFactory.createTransform(cs, WGS84); + transForward = ctFactory.createTransform(WGS84, cs); } - + public void setLevelDebug(boolean debug) { this.debug = debug; } - + public int getTransformCount() { return transformCount; } - + public double[] getExtent() { return gridExtent; } public boolean runGrid(double tolerance) { - boolean isWithinTolerance = true; - gridExtent = gridExtent(cs.getProjection()); double minx = gridExtent[0]; double miny = gridExtent[1]; double maxx = gridExtent[2]; double maxy = gridExtent[3]; - + ProjCoordinate p = new ProjCoordinate(); double dx = (maxx - minx) / gridSize; double dy = (maxy - miny) / gridSize; @@ -82,7 +80,7 @@ public boolean runGrid(double tolerance) p.y = iy == gridSize ? maxy : miny + iy * dy; - + boolean isWithinTol = roundTrip(p, tolerance); if (! isWithinTol) return false; @@ -90,50 +88,50 @@ public boolean runGrid(double tolerance) } return true; } - + ProjCoordinate p2 = new ProjCoordinate(); ProjCoordinate p3 = new ProjCoordinate(); private boolean roundTrip(ProjCoordinate p, double tolerance) { transformCount++; - + transForward.transform(p, p2); transInverse.transform(p2, p3); - - if (debug) + + if (debug) System.out.println(ProjectionUtil.toString(p) + " -> " + ProjectionUtil.toString(p2) + " -> " + ProjectionUtil.toString(p3)); - + double dx = Math.abs(p3.x - p.x); double dy = Math.abs(p3.y - p.y); - + boolean isInTol = dx <= tolerance && dy <= tolerance; - - if (! isInTol) + + if (! isInTol) System.out.println("FAIL: " + ProjectionUtil.toString(p) + " -> " + ProjectionUtil.toString(p2) + " -> " + ProjectionUtil.toString(p3)); - + return isInTol; } - + public static double[] gridExtent(Projection proj) { // scan all lat/lon params to try and determine a reasonable extent - + double lon = proj.getProjectionLongitudeDegrees(); - + double[] latExtent = new double[] {Double.MAX_VALUE, Double.MIN_VALUE }; updateLat(proj.getProjectionLatitudeDegrees(), latExtent); updateLat(proj.getProjectionLatitude1Degrees(), latExtent); updateLat(proj.getProjectionLatitude2Degrees(), latExtent); - + double centrex = lon; double centrey = 0.0; double gridWidth = 10; - + if (latExtent[0] < Double.MAX_VALUE && latExtent[1] > Double.MIN_VALUE) { // got a good candidate - + double dlat = latExtent[1] - latExtent[0]; if (dlat > 0) gridWidth = 2 * dlat; centrey = (latExtent[1] + latExtent[0]) /2; @@ -145,7 +143,7 @@ public static double[] gridExtent(Projection proj) extent[3] = centrey + gridWidth/2; return extent; } - + private static void updateLat(double lat, double[] latExtent) { // 0.0 indicates not set (for most projections?)