mirror of
https://github.com/csharpee/Map-Projections.git
synced 2025-12-10 00:00:19 -05:00
Complexity is Key
I got it to work perfectly! The output is a beautiful Peirce Quincuncial projection. My main problem before was that this projection requires a complex function called cn(u,k), and every single definition on the internet was exceedingly vague. Luckily, I found a package online that could calculate cn for me, which meant that I didn't need to do any crazy integration or calculus whatsoever.
This commit is contained in:
parent
c4d6a85f21
commit
fe8a778365
@ -2,6 +2,7 @@
|
||||
<classpath>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
|
||||
<classpathentry kind="lib" path="C:/Users/jkunimune/workspace/commons-math3-3.5/commons-math3-3.5.jar"/>
|
||||
<classpathentry kind="lib" path="C:/Users/jkunimune/workspace/ellipticFunctions/ellipticFunctions.jar"/>
|
||||
<classpathentry kind="lib" path="C:/Users/jkunimune/workspace/ellipticFunctions/mfc.jar"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
|
||||
Binary file not shown.
BIN
bin/Number.class
Normal file
BIN
bin/Number.class
Normal file
Binary file not shown.
BIN
input/color.png
BIN
input/color.png
Binary file not shown.
|
Before Width: | Height: | Size: 418 KiB After Width: | Height: | Size: 418 KiB |
BIN
output/myMap.jpg
BIN
output/myMap.jpg
Binary file not shown.
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 101 KiB |
@ -5,7 +5,8 @@ import java.util.Scanner;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
import org.apache.commons.math3.complex.Complex;
|
||||
import ellipticFunctions.Jacobi;
|
||||
import mfc.field.Complex;
|
||||
|
||||
/**
|
||||
*
|
||||
@ -16,10 +17,10 @@ import org.apache.commons.math3.complex.Complex;
|
||||
*
|
||||
*/
|
||||
public class MapProjections {
|
||||
private static final int QUINCUNCIAL = 3;
|
||||
private static final int QUINCUNCIAL = 4;
|
||||
private static final int EQUIRECTANGULAR = 1;
|
||||
private static final int MERCATOR = 2;
|
||||
private static final int POLAR = 4;
|
||||
private static final int POLAR = 3;
|
||||
|
||||
|
||||
|
||||
@ -78,37 +79,13 @@ public class MapProjections {
|
||||
/* PROJECTION METHODS: Return RGB at a given pixel based on a reference map and a unique projection method */
|
||||
public static int quincuncial(final double lat0, final double lon0, final double orientation,
|
||||
final int width, final int height, int x, int y, BufferedImage ref) { // a tessalatable square map
|
||||
final double dx = .01;
|
||||
|
||||
double phi, lambda; // the correct phi and lambda combination is the one where func goes to 0.
|
||||
double phiMin = -Math.PI/2;
|
||||
double phiMax = Math.PI/2;
|
||||
double lamMin = -Math.PI;
|
||||
double lamMax = Math.PI;
|
||||
|
||||
double[] error = new double[3];
|
||||
int i = 0;
|
||||
do {
|
||||
phi = (phiMax+phiMin)/2.0;
|
||||
lambda = (lamMax+lamMin)/2.0;
|
||||
|
||||
error[0] = func(phi, lambda, 2.0*x/width-1, 2.0*y/height-1);
|
||||
error[1] = func(phi+dx, lambda, 2.0*x/width-1, 2.0*y/height-1);
|
||||
error[2] = func(phi, lambda+dx, 2.0*x/width-1, 2.0*y/height-1);
|
||||
|
||||
if (error[0] > error[1]) // phi is too small
|
||||
phiMin = phi;
|
||||
else // phi is too big
|
||||
phiMax = phi;
|
||||
if (error[0] > error[2]) // lambda is too small
|
||||
lamMin = lambda;
|
||||
else // lambda is too big
|
||||
lamMax = lambda;
|
||||
|
||||
i ++;
|
||||
} while (phiMax-phiMin > Math.PI/ref.getHeight() && lamMax-lamMin > 2*Math.PI/ref.getWidth() && i < 256);
|
||||
|
||||
return getColor(phi,lambda,ref);
|
||||
Complex u = new Complex(3.7116*x/width, 3.7116*y/height-1.8558); // don't ask me where 3.7116 come from because I have no idea
|
||||
Complex k = new Complex(Math.sqrt(0.5)); // the rest comes from some fancy complex calculus stuff
|
||||
Complex ans = Jacobi.cn(u, k);
|
||||
double p = 2*Math.atan(ans.abs());
|
||||
double theta = Math.atan2(ans.getRe(), ans.getIm());
|
||||
double lambda = p-Math.PI/2;
|
||||
return getColor(lambda,theta,ref);
|
||||
}
|
||||
|
||||
|
||||
@ -182,17 +159,17 @@ public class MapProjections {
|
||||
/* PRECONDITION: -1 <= x,y <= 1 */
|
||||
private static double func(double p, double l, double x, double y) { // a super complicated function that calculates stuff for peirce quincuncial
|
||||
final double rt2 = Math.sqrt(2);
|
||||
Complex X = new Complex(rt2*Math.tan(p/2)*Math.cos(l), rt2*Math.tan(p/2)*Math.sin(l));
|
||||
Complex Z = new Complex(x,y); // the point on the map
|
||||
Number X = new Number(rt2*Math.tan(p/2), l);
|
||||
Number Z = new Number(x,y,true); // the point on the map
|
||||
// X*sqrt(-X^2/2 + 1)/2
|
||||
Complex radical1 = X.multiply(X.multiply(X).divide(-2).add(1).pow(.5)).divide(2);
|
||||
Number radical1 = X.times(X.sqrd().over(-2).plus(1)).over(2);
|
||||
// asin(X/sqrt(2))/sqrt(2)
|
||||
Complex arcSin = X.divide(rt2).asin().divide(rt2);
|
||||
Number arcSin = Number.asin(X.over(rt2)).over(rt2);
|
||||
// X*sqrt(-X^2/4 + 1)
|
||||
Complex radical2 = X.multiply(X.multiply(X).divide(-4).add(1).pow(.5));
|
||||
// 2*Z + pi*sqrt(2)/8
|
||||
Complex finalBit = Z.multiply(2).subtract(Math.PI*rt2/8);
|
||||
System.out.println("F("+X+","+Z+") = "+(radical1.subtract(arcSin).subtract(radical2).subtract(finalBit)).abs());
|
||||
return (radical1.subtract(arcSin).subtract(radical2).subtract(finalBit)).abs();
|
||||
Number radical2 = X.times(Number.sqrt(X.sqrd().over(-4).plus(1)));
|
||||
// 2*Z - pi*sqrt(2)/8
|
||||
Number finalBit = Z.times(2).minus(Math.PI*rt2/8);
|
||||
//System.out.println("F("+X+","+Z+") = "+(radical1.subtract(arcSin).subtract(radical2).subtract(finalBit)).abs());
|
||||
return Number.abs(radical1.minus(radical2).minus(arcSin).minus(finalBit));
|
||||
}
|
||||
}
|
||||
112
src/Number.java
Normal file
112
src/Number.java
Normal file
@ -0,0 +1,112 @@
|
||||
public class Number {
|
||||
private double r;
|
||||
private double theta;
|
||||
|
||||
public static final Number ONE = new Number(1,0);
|
||||
|
||||
|
||||
|
||||
|
||||
public Number(double mag, double ang) {
|
||||
r = mag;
|
||||
theta = ang;
|
||||
}
|
||||
|
||||
|
||||
public Number(double x, double y, boolean cartesian) {
|
||||
r = Math.hypot(x, y);
|
||||
theta = Math.atan2(y, x);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public Number plus(Number that) {
|
||||
return new Number (this.x()+that.x(), this.y()+that.y(), true);
|
||||
}
|
||||
|
||||
|
||||
public Number plus(double x) {
|
||||
return new Number (this.x()+x, this.y(), true);
|
||||
}
|
||||
|
||||
|
||||
public Number minus(Number that) {
|
||||
return new Number (this.x()-that.x(), this.y()-that.y(), true);
|
||||
}
|
||||
|
||||
|
||||
public Number minus(double x) {
|
||||
return new Number (this.x()-x, this.y(), true);
|
||||
}
|
||||
|
||||
|
||||
public Number times(Number that) {
|
||||
return new Number (this.r()*that.r(), this.theta()+that.theta());
|
||||
}
|
||||
|
||||
|
||||
public Number times(double x) {
|
||||
return new Number (r*x, theta);
|
||||
}
|
||||
|
||||
|
||||
public Number over(double x) {
|
||||
return new Number (r/x, theta);
|
||||
}
|
||||
|
||||
|
||||
public Number sqrd() { // squares it
|
||||
return new Number (r*r, theta*2);
|
||||
}
|
||||
|
||||
|
||||
public Number timesI() { // multiplies it by i
|
||||
return new Number (-y(), x());
|
||||
}
|
||||
|
||||
|
||||
public Number overI() { // multiplies by negative I
|
||||
return new Number (y(), -x());
|
||||
}
|
||||
|
||||
|
||||
public static Number sqrt(Number z) { // sqrts it
|
||||
return new Number (Math.sqrt(z.r()), z.theta()/2);
|
||||
}
|
||||
|
||||
|
||||
public static Number ln(Number z) {
|
||||
return new Number (Math.log(z.r()), z.theta(), true);
|
||||
}
|
||||
|
||||
|
||||
public static Number asin(Number z) {
|
||||
return Number.ln(z.timesI().plus(Number.sqrt(Number.ONE.minus(z.sqrd())))).overI();
|
||||
}
|
||||
|
||||
|
||||
public static double abs(Number z) {
|
||||
return z.r();
|
||||
}
|
||||
|
||||
|
||||
public double r() {
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
public double theta() {
|
||||
return theta;
|
||||
}
|
||||
|
||||
|
||||
public double x() {
|
||||
return r*Math.cos(theta);
|
||||
}
|
||||
|
||||
|
||||
public double y() {
|
||||
return r*Math.sin(theta);
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user