mirror of
https://github.com/csharpee/Map-Projections.git
synced 2025-10-11 00:00:05 -04:00
<3
I added the Bonne projection, which is a generalized form of Sinusoidal and Werner (I don't have Werner). As it turns out, this was one of the go-to map projections back in the days before Albers and Van der Grinten had been invented.
This commit is contained in:
parent
3e819d9fdb
commit
c4df39aa6c
@ -197,9 +197,11 @@ public class Conic {
|
|||||||
lat = -lat;
|
lat = -lat;
|
||||||
lon = -lon;
|
lon = -lon;
|
||||||
}
|
}
|
||||||
final double s = reversed ? -1 : 1;
|
|
||||||
final double r = Math.sqrt(C - 2*n*Math.sin(lat));
|
final double r = Math.sqrt(C - 2*n*Math.sin(lat));
|
||||||
return new double[] { s*r*Math.sin(n*lon), s*(y0 - r*Math.cos(n*lon)) };
|
final double x = r*Math.sin(n*lon);
|
||||||
|
final double y = -r*Math.cos(n*lon);
|
||||||
|
if (reversed) return new double[] {-x,-y};
|
||||||
|
else return new double[] { x, y};
|
||||||
}
|
}
|
||||||
|
|
||||||
public double[] inverse(double x, double y) {
|
public double[] inverse(double x, double y) {
|
||||||
@ -219,7 +221,6 @@ public class Conic {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A base for all conic projections
|
* A base for all conic projections
|
||||||
* @author jkunimune
|
* @author jkunimune
|
||||||
|
@ -233,6 +233,89 @@ public class Misc {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
public static final Projection BONNE =
|
||||||
|
new Projection(
|
||||||
|
"Bonne", "A traditional pseudoconic projection, also known as the Sylvanus projection.",
|
||||||
|
10, 10, 0b1111, Type.PSEUDOCONIC, Property.EQUAL_AREA, 1,
|
||||||
|
new String[] {"Std. Parallel"}, new double[][] {{-90, 90, 45}}) {
|
||||||
|
|
||||||
|
private double r0;
|
||||||
|
private double yC; // the y coordinate of the centers of the parallels
|
||||||
|
private boolean reversed; // if the standard parallel is southern
|
||||||
|
|
||||||
|
public void setParameters(double... params) {
|
||||||
|
double lat0 = Math.toRadians(params[0]);
|
||||||
|
this.reversed = (lat0 < 0);
|
||||||
|
if (reversed)
|
||||||
|
lat0 = -lat0;
|
||||||
|
this.r0 = 1/Math.tan(lat0) + lat0;
|
||||||
|
if (Double.isInfinite(r0)) {
|
||||||
|
this.width = Pseudocylindrical.SINUSOIDAL.getWidth();
|
||||||
|
this.height = Pseudocylindrical.SINUSOIDAL.getHeight();
|
||||||
|
}
|
||||||
|
else { // for such a simple map projection...
|
||||||
|
double argmaxX = NumericalAnalysis.bisectionFind(
|
||||||
|
(p) -> (Math.PI*(1/(r0-p)*Math.cos(p) - Math.sin(p))*Math.cos(Math.PI/(r0-p)*Math.cos(p)) - Math.sin(Math.PI/(r0-p)*Math.cos(p))),
|
||||||
|
-Math.PI/2, 0, 1e-3); // it sure is complicated to find its dimensions!
|
||||||
|
double maxX = (r0 - argmaxX)*Math.sin(Math.PI/(r0 - argmaxX)*Math.cos(argmaxX));
|
||||||
|
this.width = 2*maxX;
|
||||||
|
double argmaxY;
|
||||||
|
try {
|
||||||
|
argmaxY = NumericalAnalysis.bisectionFind(
|
||||||
|
(p) -> (Math.PI*(1/(r0-p)*Math.cos(p) - Math.sin(p))*Math.sin(Math.PI/(r0-p)*Math.cos(p)) + Math.cos(Math.PI/(r0-p)*Math.cos(p))),
|
||||||
|
0, Math.PI/4, 1e-3);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
argmaxY = Math.PI/2;
|
||||||
|
}
|
||||||
|
double maxY = Math.max(-r0 + Math.PI/2,
|
||||||
|
-(r0 - argmaxY)*Math.cos(Math.PI/(r0 - argmaxY)*Math.cos(argmaxY)));
|
||||||
|
this.height = r0 + Math.PI/2 + maxY;
|
||||||
|
this.yC = (r0 + Math.PI/2 - maxY)/2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public double[] project(double lat, double lon) {
|
||||||
|
if (Double.isInfinite(r0))
|
||||||
|
return Pseudocylindrical.SINUSOIDAL.project(lat, lon);
|
||||||
|
if (reversed) {
|
||||||
|
lat = -lat;
|
||||||
|
lon = -lon;
|
||||||
|
}
|
||||||
|
|
||||||
|
double r = r0 - lat;
|
||||||
|
double th = lon*Math.cos(lat)/r;
|
||||||
|
double x = r*Math.sin(th);
|
||||||
|
double y = yC - r*Math.cos(th);
|
||||||
|
|
||||||
|
if (reversed)
|
||||||
|
return new double[] {-x,-y};
|
||||||
|
else
|
||||||
|
return new double[] { x, y};
|
||||||
|
}
|
||||||
|
|
||||||
|
public double[] inverse(double x, double y) {
|
||||||
|
if (Double.isInfinite(r0))
|
||||||
|
return Pseudocylindrical.SINUSOIDAL.inverse(x, y);
|
||||||
|
if (reversed) {
|
||||||
|
x = -x;
|
||||||
|
y = -y;
|
||||||
|
}
|
||||||
|
|
||||||
|
double r = Math.hypot(x, y-yC);
|
||||||
|
if (r < r0 - Math.PI/2 || r > r0 + Math.PI/2)
|
||||||
|
return null;
|
||||||
|
double th = Math.atan2(x, -(y-yC));
|
||||||
|
double lat = r0 - r;
|
||||||
|
double lon = th*r/Math.cos(lat);
|
||||||
|
|
||||||
|
if (reversed)
|
||||||
|
return new double[] {-lat,-lon};
|
||||||
|
else
|
||||||
|
return new double[] { lat, lon};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
public static final Projection T_SHIRT =
|
public static final Projection T_SHIRT =
|
||||||
new Projection(
|
new Projection(
|
||||||
"T-Shirt", "A conformal projection onto a torso.",
|
"T-Shirt", "A conformal projection onto a torso.",
|
||||||
|
@ -560,6 +560,14 @@ public abstract class Projection {
|
|||||||
return this.property;
|
return this.property;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the rating:
|
||||||
|
* 0 if I hate it with a burning passion;
|
||||||
|
* 1 if it is bad and you shouldn't use it;;
|
||||||
|
* 2 if it has its use cases but isn't very good outside of them;
|
||||||
|
* 3 if it is a solid, defensible choice; and
|
||||||
|
* 4 if I love it with a burning passion.
|
||||||
|
*/
|
||||||
public final int getRating() {
|
public final int getRating() {
|
||||||
return this.rating;
|
return this.rating;
|
||||||
}
|
}
|
||||||
@ -612,10 +620,10 @@ public abstract class Projection {
|
|||||||
public static enum Type {
|
public static enum Type {
|
||||||
CYLINDRICAL("Cylindrical"), CONIC("Conic"), AZIMUTHAL("Azimuthal"),
|
CYLINDRICAL("Cylindrical"), CONIC("Conic"), AZIMUTHAL("Azimuthal"),
|
||||||
PSEUDOCYLINDRICAL("Pseudocylindrical"), PSEUDOCONIC("Pseudoconic"),
|
PSEUDOCYLINDRICAL("Pseudocylindrical"), PSEUDOCONIC("Pseudoconic"),
|
||||||
PSEUDOAZIMUTHAL("Pseudoazimuthal"), QUASIAZIMUTHAL("Quasiazimuthal"),
|
PSEUDOAZIMUTHAL("Pseudoazimuthal"),
|
||||||
TETRAHEDRAL("Tetrahedral"), OCTOHEDRAL("Octohedral"),
|
TETRAHEDRAL("Tetrahedral"), OCTOHEDRAL("Octohedral"),
|
||||||
TETRADECAHEDRAL("Truncated Octohedral"), ICOSOHEDRAL("Icosohedral"),
|
TETRADECAHEDRAL("Truncated Octohedral"), ICOSOHEDRAL("Icosohedral"),
|
||||||
POLYNOMIAL("Polynomial"), STREBE("Strebe Blend"), PLANAR("Planar"), OTHER("Other");
|
POLYNOMIAL("Polynomial"), STREBE("Strebe blend"), PLANAR("Planar"), OTHER("Other");
|
||||||
|
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
|
@ -138,6 +138,36 @@ public class NumericalAnalysis {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies a bisection zero-finding scheme to find x such that f(x)=y
|
||||||
|
* @param f The function whose zero must be found
|
||||||
|
* @param xMin The lower bound for the zero
|
||||||
|
* @param xMax The upper bound for the zero
|
||||||
|
* @param tolerence The maximum error in x that this can return
|
||||||
|
* @return The value of x that sets f to zero
|
||||||
|
*/
|
||||||
|
public static final double bisectionFind(DoubleUnaryOperator f,
|
||||||
|
double xMin, double xMax, double tolerance) {
|
||||||
|
double yMin = f.applyAsDouble(xMin);
|
||||||
|
double yMax = f.applyAsDouble(xMax);
|
||||||
|
if ((yMin < 0) == (yMax < 0))
|
||||||
|
throw new IllegalArgumentException("Bisection failed; bounds "+xMin+" and "+xMax+" do not necessarily straddle a zero.");
|
||||||
|
while (Math.abs(xMax - xMin) > tolerance) {
|
||||||
|
double x = (xMax + xMin)/2;
|
||||||
|
double y = f.applyAsDouble(x);
|
||||||
|
if ((y < 0) == (yMin < 0)) {
|
||||||
|
xMin = x;
|
||||||
|
yMin = y;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
xMax = x;
|
||||||
|
yMax = y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (xMax + xMin)/2;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Applies Newton's method in one dimension to solve for x such that f(x)=y
|
* Applies Newton's method in one dimension to solve for x such that f(x)=y
|
||||||
* @param y Desired value for f
|
* @param y Desired value for f
|
||||||
@ -145,7 +175,7 @@ public class NumericalAnalysis {
|
|||||||
* @param f The error in terms of x
|
* @param f The error in terms of x
|
||||||
* @param dfdx The derivative of f with respect to x
|
* @param dfdx The derivative of f with respect to x
|
||||||
* @param tolerance The maximum error that this can return
|
* @param tolerance The maximum error that this can return
|
||||||
* @return The value of x that puts f near 0, or NaN if it does not converge in 5 iterations
|
* @return The value of x that puts f near y, or NaN if it does not converge in 5 iterations
|
||||||
*/
|
*/
|
||||||
public static final double newtonRaphsonApproximation(
|
public static final double newtonRaphsonApproximation(
|
||||||
double y, double x0, DoubleUnaryOperator f, DoubleUnaryOperator dfdx, double tolerance) {
|
double y, double x0, DoubleUnaryOperator f, DoubleUnaryOperator dfdx, double tolerance) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user