Map-Projections/src/maps/Snyder.java
2023-03-31 13:24:10 -04:00

121 lines
4.5 KiB
Java

/**
* MIT License
*
* Copyright (c) 2017 Justin Kunimune
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package maps;
import de.jtem.mfc.field.Complex;
import maps.Projection.Property;
import maps.Projection.Type;
/**
* A map optimised specifically for the 50 united states of America.
* Source:
* Snyder, John Parr (1985). "Computer-assisted map projection research". Bulletin. United States
* Geological Survey. 1629: 79&endash;92; 147&endash;51.
*
* @author jkunimune
*/
public class Snyder {
private static final double[] POLE = { Math.PI/4, -2*Math.PI/3, 0 };
private static final double[] A = {
0.05, 0.9842990, 0.0211642, -0.1036018, -0.0329095, 0.0499471, 0.0260460,
0.0007388, 0.0075848, -0.0216473, -0.0225161 }; //the real components of the coefficients
private static final double[] B = {
-0.07, 0., 0.0037608, -0.0575102, -0.0320119, 0.1223335, 0.0899805,
-0.1435792, -0.1334108, 0.0776645, 0.0853673 }; //the imaginary components of the coefficients
private static final double TOLERANCE = 1e-4;
private static final double[] LIMS = {
Math.toRadians(10), Math.toRadians(90), Math.toRadians(-195), Math.toRadians(-50) }; //trims the outside unsightly portions
public static final Projection GS50 =
new Projection(
"GS50", "'MURKA!", 1.6, 1.1, 0b0011, Type.POLYNOMIAL, Property.CONFORMAL, 4,
new String[] {}, new double[][] {}, false) {
public double[] project(double lat, double lon) {
if (lat < LIMS[0]) lat = LIMS[0]; //cut out the farther more unsightly bits
if (lat > LIMS[1]) lat = LIMS[1];
if (lon > 0 && lon-2*Math.PI < LIMS[2]) lon = LIMS[2];
if (lon < 0 && lon > LIMS[3]) lon = LIMS[3];
final double g = Math.sin(lat)*Math.sin(POLE[0]) + Math.cos(lat)*Math.cos(POLE[0])*Math.cos(lon-POLE[1]);
final double s = 2/(1+g);
final Complex z = new Complex(s*Math.cos(lat)*Math.sin(lon-POLE[1]), s*(Math.sin(lat)*Math.cos(POLE[0]) - Math.cos(lat)*Math.sin(POLE[0])*Math.cos(lon-POLE[1])));
final Complex p = f(z);
return new double[] { p.getRe(), p.getIm() };
}
public double[] inverse(double x, double y) {
final Complex p = new Complex(x, y);
Complex z = p; //initial guess
Complex error = f(z).minus(p);
for (int i = 0; error.abs() > TOLERANCE; i ++) {
if (i == 9) return null;
final Complex deriv = fp(z);
z = z.minus(error.divide(deriv));
error = f(z).minus(p);
}
double r = z.abs();
double phi = 2*Math.atan(r/2);
double lat = Math.asin(Math.cos(phi)*Math.sin(POLE[0]) + z.getIm()*Math.sin(phi)*Math.cos(POLE[0])/r);
double lon = POLE[1] + Math.atan(z.getRe()*Math.sin(phi)/(r*Math.cos(POLE[0])*Math.cos(phi)-z.getIm()*Math.sin(POLE[0]*Math.sin(phi))));
if (lat < LIMS[0] || lat > LIMS[1]) return null;
if (lon < LIMS[2] || lon > LIMS[3]) return null;
if (lon < -Math.PI) lon += 2*Math.PI;
return new double[] {lat, lon};
}
@Override
public double[] project(double lat, double lon, double[] pole) {
return super.project(lat, lon, null);
}
@Override
public double[] inverse(double x, double y, double[] pole, boolean crop) {
return super.inverse(x, y, null, crop);
}
};
private static final Complex f(Complex z) {
Complex p = new Complex();
for (int k = A.length-1; k >= 0; k --) {
p = p.times(z).plus(new Complex(A[k], B[k]));
}
return p;
}
private static final Complex fp(Complex z) {
Complex p = new Complex();
for (int k = A.length-1; k > 0; k --) {
p = p.times(z).plus(new Complex(A[k], B[k]).times(k));
}
return p;
}
}