Reinforced the North-Up Convention

So, it turns out that one of the reasons I was having such trouble with
AuthaGraph was that I had a sign convention where south was positive and
the international date line was zero. So, I fixed that, because that was
awful. It should be easier for me to figure out polyhedric projections
now. Mind you, AuthaGraph doesn't work at all right now. It's just not
even close right now. Don't worry - I'll work on that.
This commit is contained in:
jkunimune 2016-11-21 15:39:18 -05:00
parent 8ea1d446d3
commit b095d00ee6
2 changed files with 78 additions and 60 deletions

Binary file not shown.

View File

@ -18,6 +18,7 @@ import javafx.scene.control.Label;
import javafx.scene.control.MenuButton; import javafx.scene.control.MenuButton;
import javafx.scene.control.MenuItem; import javafx.scene.control.MenuItem;
import javafx.scene.control.Slider; import javafx.scene.control.Slider;
import javafx.scene.control.Spinner;
import javafx.scene.control.Tooltip; import javafx.scene.control.Tooltip;
import javafx.scene.image.Image; import javafx.scene.image.Image;
import javafx.scene.image.ImageView; import javafx.scene.image.ImageView;
@ -57,7 +58,7 @@ public class MapProjections extends Application {
"Represents earth viewed from an infinite distance", "Represents earth viewed from an infinite distance",
"Every straight line on the map is a straight line on the sphere", "A conformal conical map", "Every straight line on the map is a straight line on the sphere", "A conformal conical map",
"The compromise map used by National Geographic (caution: very slow)", "A circular compromise map", "The compromise map used by National Geographic (caution: very slow)", "A circular compromise map",
"An equal-area map shaped like an elipse", "An equal-area map shaped like an elipse", "An equal-area map shaped like an ellipse", "An equal-area map shaped like an elipse",
"An equal-area map shaped like a sinusoid", "BURN LIFE'S HOUSE DOWN!", "An equal-area map shaped like a sinusoid", "BURN LIFE'S HOUSE DOWN!",
"A conformal square map that uses complex math", "A conformal square map that uses complex math",
"A reorganized version of Pierce Quincuncial and actually the best map ever", "A reorganized version of Pierce Quincuncial and actually the best map ever",
@ -66,10 +67,10 @@ public class MapProjections extends Application {
"What happens when you apply a complex differentiable function to a stereographic projection?" }; "What happens when you apply a complex differentiable function to a stereographic projection?" };
private static final String[] AXES = { "Standard", "Transverse", "Center of Mass", "Jerusalem", "Point Nemo", private static final String[] AXES = { "Standard", "Transverse", "Center of Mass", "Jerusalem", "Point Nemo",
"Longest Line", "Longest Line Transverse", "Cylindrical", "Conical", "Quincuncial", "Random" }; "Longest Line", "Longest Line Transverse", "Cylindrical", "Conical", "Quincuncial", "Antipode", "Random" };
private static final double[] DEF_LATS = { 90, 0, 29.9792, 31.7833, 48.8767, -28.5217, -46.4883, -35, -10, 60, Double.NaN }; private static final double[] DEF_LATS = { 90, 0, 29.9792, 31.7833, 48.8767, -28.5217, -46.4883, -35, -10, 60 };
private static final double[] DEF_LONS = { 0, 0, 31.1344, 35.216, 56.6067, 141.451, 16.5305, -13.6064, 65, -6, Double.NaN }; private static final double[] DEF_LONS = { 0, 0, 31.1344, 35.216, 56.6067, 141.451, 16.5305, -13.6064, 65, -6 };
private static final double[] DEF_THTS = { 0, 0, -32, -35, -45, 71.5, 137, 145, -150, -10, Double.NaN }; private static final double[] DEF_THTS = { 0, 0, -32, -35, -45, 161.5, 137, 145, -150, -10 };
private FileChooser inputChooser, saver; private FileChooser inputChooser, saver;
@ -138,14 +139,14 @@ public class MapProjections extends Application {
} }
}); });
projectionChooser.setPrefWidth(200); projectionChooser.setPrefWidth(200);
projectionChooser.setValue("Mercator"); projectionChooser.setValue(PROJ_ARR[1]);
layout.getChildren().add(new HBox(3, lbl, projectionChooser)); layout.getChildren().add(new HBox(3, lbl, projectionChooser));
projectionDesc = new Text("Please choose a projection."); projectionDesc = new Text(DESC[1]);
projectionDesc.setWrappingWidth(CONT_WIDTH); projectionDesc.setWrappingWidth(CONT_WIDTH);
layout.getChildren().add(projectionDesc); layout.getChildren().add(projectionDesc);
final MenuButton defAxes = new MenuButton("Axis Presets"); final MenuButton defAxes = new MenuButton("Aspect Presets");
for (String preset: AXES) { for (String preset: AXES) {
MenuItem m = new MenuItem(preset); MenuItem m = new MenuItem(preset);
m.setOnAction(new EventHandler<ActionEvent>() { m.setOnAction(new EventHandler<ActionEvent>() {
@ -156,12 +157,16 @@ public class MapProjections extends Application {
defAxes.getItems().add(m); defAxes.getItems().add(m);
} }
defAxes.setTooltip(new Tooltip( defAxes.setTooltip(new Tooltip(
"Set the axis sliders based on a preset")); "Set the aspect sliders based on a preset"));
layout.getChildren().add(defAxes); layout.getChildren().add(defAxes);
latSlider = new Slider(-90, 90, 90); latSlider = new Slider(-90, 90, 90);
lonSlider = new Slider(-180,180,0); lonSlider = new Slider(-180,180,0);
thtSlider = new Slider(-180,180,0); thtSlider = new Slider(-180,180,0);
Tooltip aspTlTp = new Tooltip("Change the aspect of the map");
latSlider.setTooltip(aspTlTp);
lonSlider.setTooltip(aspTlTp);
thtSlider.setTooltip(aspTlTp);
GridPane grid = new GridPane(); GridPane grid = new GridPane();
grid.addRow(0, new Text("Latitude:"), latSlider); grid.addRow(0, new Text("Latitude:"), latSlider);
@ -227,6 +232,12 @@ public class MapProjections extends Application {
private void setAxisByPreset(String preset) { private void setAxisByPreset(String preset) {
if (preset.equals("Antipode")) {
latSlider.setValue(-latSlider.getValue());
lonSlider.setValue((lonSlider.getValue()+360)%360-180);
thtSlider.setValue(-thtSlider.getValue());
return;
}
if (preset.equals("Random")) { if (preset.equals("Random")) {
latSlider.setValue(Math.toDegrees(Math.asin(Math.random()*2-1))); latSlider.setValue(Math.toDegrees(Math.asin(Math.random()*2-1)));
lonSlider.setValue(Math.random()*360-180); lonSlider.setValue(Math.random()*360-180);
@ -273,7 +284,7 @@ public class MapProjections extends Application {
final int[] refDims = {(int)width, (int)height}; final int[] refDims = {(int)width, (int)height};
final String p = projectionChooser.getValue(); final String p = projectionChooser.getValue();
final double X = 2.0*x/outputWidth-1; final double X = 2.0*x/outputWidth-1;
final double Y = 2.0*y/outputHeight-1; final double Y = 1-2.0*y/outputHeight;
if (p.equals("Pierce Quincuncial")) if (p.equals("Pierce Quincuncial"))
return quincuncial(pole, X, Y, refDims, input); return quincuncial(pole, X, Y, refDims, input);
else if (p.equals("Equirectangular")) else if (p.equals("Equirectangular"))
@ -327,8 +338,8 @@ public class MapProjections extends Application {
Complex k = new Complex(Math.sqrt(0.5)); // the rest comes from some fancy complex calculus Complex k = new Complex(Math.sqrt(0.5)); // the rest comes from some fancy complex calculus
Complex ans = Jacobi.cn(u, k); Complex ans = Jacobi.cn(u, k);
double p = 2 * Math.atan(ans.abs()); double p = 2 * Math.atan(ans.abs());
double theta = Math.atan2(ans.getRe(), ans.getIm()) + Math.PI; double theta = Math.atan2(ans.getIm(), ans.getRe()) - Math.PI/2;
double lambda = p - Math.PI / 2; double lambda = Math.PI/2 - p;
return getColor(pole, lambda, theta, refDims, ref); return getColor(pole, lambda, theta, refDims, ref);
} }
@ -336,15 +347,15 @@ public class MapProjections extends Application {
private static int experiment(final double[] pole, double x, double y, private static int experiment(final double[] pole, double x, double y,
int[] refDims, Image ref) { // just some random complex plane stuff int[] refDims, Image ref) { // just some random complex plane stuff
Complex z = new Complex(x*3, y*3); Complex z = new Complex(x*3, y*3);
Complex ans = z.neg().exp().minus(1).neg().invert(); Complex ans = z;
double p = 2 * Math.atan(ans.abs()); double p = 2 * Math.atan(ans.abs());
double theta = Math.atan2(ans.getRe(), ans.getIm()) + Math.PI; double theta = Math.atan2(ans.getIm(), ans.getRe()) + Math.PI/2;
double lambda = p - Math.PI / 2; double lambda = Math.PI/2 - p;
return getColor(pole, lambda, theta, refDims, ref); return getColor(pole, lambda, theta, refDims, ref);
} }
private static int equirectangular(final double[] pole, double x, double y, private static int equirectangular(final double[] pole, double x, double y,
int[] refDims, Image ref) { // a basic scale int[] refDims, Image ref) { // a linear scale
return getColor(pole, y*Math.PI/2, x*Math.PI, refDims, ref); return getColor(pole, y*Math.PI/2, x*Math.PI, refDims, ref);
} }
@ -356,9 +367,10 @@ public class MapProjections extends Application {
private static int polar(final double[] pole, double x, double y, private static int polar(final double[] pole, double x, double y,
int[] refDims, Image ref) { // the projection used on the UN flag int[] refDims, Image ref) { // the projection used on the UN flag
double phi = Math.PI * Math.hypot(x, y) - Math.PI/2; double phi = Math.PI/2 - Math.PI * Math.hypot(x, y);
if (Math.abs(phi) < Math.PI/2) if (Math.abs(phi) < Math.PI/2)
return getColor(pole, phi, Math.atan2(x, y), refDims, ref); return getColor(pole, phi, Math.atan2(y, x) + Math.PI/2,
refDims, ref);
else else
return 0; return 0;
} }
@ -376,21 +388,21 @@ public class MapProjections extends Application {
private static int stereographic(final double[] pole, double x, double y, private static int stereographic(final double[] pole, double x, double y,
int[] refDims, Image ref) { // a shape-preserving infinite map int[] refDims, Image ref) { // a shape-preserving infinite map
return getColor(pole, 2*Math.atan(2*Math.hypot(x, y)) - Math.PI/2, return getColor(pole, Math.PI/2 - 2*Math.atan(2*Math.hypot(x, y)),
Math.atan2(x, y), refDims, ref); Math.atan2(y, x) + Math.PI/2, refDims, ref);
} }
private static int gnomonic(final double[] pole, double x, double y, private static int gnomonic(final double[] pole, double x, double y,
int[] refDims, Image ref) { // map where straight lines are straight int[] refDims, Image ref) { // map where straight lines are straight
return getColor(pole, Math.atan(2*Math.hypot(x, y)) - Math.PI/2, return getColor(pole, Math.PI/2 - Math.atan(2*Math.hypot(x, y)),
Math.atan2(x, y), refDims, ref); Math.atan2(y, x) + Math.PI/2, refDims, ref);
} }
private static int orthographic(final double[] pole, double x, double y, private static int orthographic(final double[] pole, double x, double y,
int[] refDims, Image ref) { // a map that mimics the view from space int[] refDims, Image ref) { // a map that mimics the view from space
double R = Math.hypot(x, y); double R = Math.hypot(x, y);
if (R <= 1) if (R <= 1)
return getColor(pole, -Math.acos(R), Math.atan2(x, y), return getColor(pole, Math.acos(R), Math.atan2(y, x) + Math.PI/2,
refDims, ref); refDims, ref);
else else
return 0; return 0;
@ -403,10 +415,10 @@ public class MapProjections extends Application {
private static int lambert(final double[] pole, double x, double y, private static int lambert(final double[] pole, double x, double y,
int[] refDims, Image ref) { // a conical projection int[] refDims, Image ref) { // a conical projection
y = (y+1)/2; y = (y-1)/2;
return getColor(pole, return getColor(pole,
2*Math.atan(Math.pow(1.5*Math.hypot(x, y), 2)) - Math.PI/2, Math.PI/2 - 2*Math.atan(Math.pow(1.5*Math.hypot(x, y), 2)),
2*Math.atan2(x, y), refDims, ref); 2*(Math.atan2(y, x) + Math.PI/2), refDims, ref);
} }
private static int lemons(final double[] pole, double x, double y, private static int lemons(final double[] pole, double x, double y,
@ -427,8 +439,8 @@ public class MapProjections extends Application {
final int[] refDims, Image ref) { // the lambert azimuthal equal area projection final int[] refDims, Image ref) { // the lambert azimuthal equal area projection
double R = Math.hypot(x, y); double R = Math.hypot(x, y);
if (R <= 1) if (R <= 1)
return getColor(pole, Math.asin(2*R*R - 1), Math.atan2(x, y), return getColor(pole, Math.asin(1 - 2*R*R),
refDims, ref); Math.atan2(y, x) + Math.PI/2, refDims, ref);
else else
return 0; return 0;
} }
@ -439,8 +451,8 @@ public class MapProjections extends Application {
Complex k = new Complex(Math.sqrt(0.5)); // the rest comes from some fancy complex calculus Complex k = new Complex(Math.sqrt(0.5)); // the rest comes from some fancy complex calculus
Complex ans = Jacobi.cn(u, k); Complex ans = Jacobi.cn(u, k);
double p = 2 * Math.atan(ans.abs()); double p = 2 * Math.atan(ans.abs());
double theta = Math.atan2(ans.getRe(), ans.getIm()); double theta = Math.atan2(ans.getIm(), ans.getRe());
double lambda = p - Math.PI / 2; double lambda = Math.PI/2 - p;
return getColor(pole, lambda, theta, refDims, ref); return getColor(pole, lambda, theta, refDims, ref);
} }
@ -508,8 +520,8 @@ public class MapProjections extends Application {
int[] refDims, Image ref) { // a novelty map that magnifies the center profusely int[] refDims, Image ref) { // a novelty map that magnifies the center profusely
double R = Math.hypot(x, y); double R = Math.hypot(x, y);
if (R <= 1) if (R <= 1)
return getColor(pole, Math.PI/2 * (R*R*R*1.8 + R*.2 - 1), return getColor(pole, Math.PI/2 * (1 - R*.2 - R*R*R*1.8),
Math.atan2(x, y), refDims, ref); Math.atan2(y, x) + Math.PI/2, refDims, ref);
else else
return 0; return 0;
} }
@ -529,43 +541,46 @@ public class MapProjections extends Application {
final double[] faceCenter = new double[3]; final double[] faceCenter = new double[3];
final double localX, localY; final double localX, localY;
if (y+1 > 4*x && y+1 > -4*x) { if (y+1 > 4*x && y+1 > -4*x) {
faceCenter[0] = Math.PI/2-Math.asin(Math.sqrt(8)/3); //faceCenter[0] = Math.asin(Math.sqrt(8)/3)-Math.PI/2;
faceCenter[1] = Math.PI; faceCenter[0] = -1;
faceCenter[1] = 0;
faceCenter[2] = 0; faceCenter[2] = 0;
localX = 2*x; localX = 2*x;
localY = y-1/3.0; localY = y-1/3.0;
} }
else if (y+1 > 4*(x+1)) { else if (y+1 > 4*(x+1)) {
faceCenter[0] = -Math.PI/2; faceCenter[0] = Math.PI/2;
faceCenter[1] = 0; faceCenter[1] = 0;
faceCenter[2] = 0; faceCenter[2] = 0;
localX = 2*(x+1); localX = 2*(x+1);
localY = y-1/3.0; localY = y-1/3.0;
} }
else if (y+1 > -4*(x-1)) { else if (y+1 > -4*(x-1)) {
faceCenter[0] = -Math.PI/2; faceCenter[0] = Math.PI/2;
faceCenter[1] = 0; faceCenter[1] = 0;
faceCenter[2] = 0; faceCenter[2] = 0;
localX = 2*(x-1); localX = 2*(x-1);
localY = y-1/3.0; localY = y-1/3.0;
} }
else if (x < 0) { else if (x < 0) {
faceCenter[0] = Math.PI/2-Math.asin(Math.sqrt(8)/3); faceCenter[0] = Math.asin(Math.sqrt(8)/3)-Math.PI/2;
faceCenter[1] = Math.PI/3; faceCenter[1] = 4*Math.PI/3;
faceCenter[2] = Math.PI/3; faceCenter[2] = Math.PI/3;
localX = 2*(x+0.5); localX = 2*(x+0.5);
localY = y+1/3.0; localY = y+1/3.0;
} }
else { else {
faceCenter[0] = Math.PI/2-Math.asin(Math.sqrt(8)/3); faceCenter[0] = Math.asin(Math.sqrt(8)/3)-Math.PI/2;
faceCenter[1] =5*Math.PI/3; faceCenter[1] =2*Math.PI/3;
faceCenter[2] = -Math.PI/3; faceCenter[2] = -Math.PI/3;
localX = 2*(x-0.5); localX = 2*(x-0.5);
localY = y+1/3.0; localY = y+1/3.0;
} }
double[] newPole = obliquify(pole, faceCenter); double[] newPole = obliquify(pole, faceCenter);
return gnomonic(newPole, localX, localY, refDims, ref); return getColor(newPole,
Math.PI/2 - Math.atan(2.35*Math.hypot(localX, localY)),
Math.atan2(localY, localX) + Math.PI/2, refDims, ref);
} }
@ -573,8 +588,8 @@ public class MapProjections extends Application {
int[] refDims, Image input) { // returns the color of any coordinate on earth int[] refDims, Image input) { // returns the color of any coordinate on earth
final double[] coords = {lat, lon}; final double[] coords = {lat, lon};
final double[] convCoords = obliquify(pole, coords); final double[] convCoords = obliquify(pole, coords);
double x = convCoords[1] / (2*Math.PI); double x = convCoords[1]/(2*Math.PI) + refDims[0]/2.0;
double y = convCoords[0] * refDims[1] / Math.PI + refDims[1]/2.0; double y = refDims[1]/2.0 - convCoords[0]*refDims[1]/Math.PI;
x = (x - Math.floor(x)) * refDims[0]; x = (x - Math.floor(x)) * refDims[0];
if (y < 0) if (y < 0)
@ -593,29 +608,32 @@ public class MapProjections extends Application {
double lat1 = coords[0]; double lat1 = coords[0];
double lon1 = coords[1]; double lon1 = coords[1];
lon1 += tht0; lon1 += tht0;
double latitude = Math.asin(Math.sin(lat0) * Math.sin(lat1) + Math.cos(lat0) * Math.cos(lon1) * Math.cos(lat1)); double latf = Math.asin(Math.sin(lat0)*Math.sin(lat1) - Math.cos(lat0)*Math.cos(lon1)*Math.cos(lat1));
double longitude; double lonf;
double innerFunc = Math.sin(lat1) / Math.cos(lat0) / Math.cos(latitude) - Math.tan(lat0) * Math.tan(latitude); double innerFunc = Math.sin(lat1)/Math.cos(lat0)/Math.cos(latf) - Math.tan(lat0)*Math.tan(latf);
if (lat0 == Math.PI / 2) // accounts for special case when lat0 = pi/2 if (lat0 == Math.PI / 2) // accounts for special case when lat0 = pi/2
longitude = lon1+lon0 + Math.PI; lonf = lon1+lon0+Math.PI;
else if (lat0 == -Math.PI / 2) // accounts for special case when lat0 = -pi/2 else if (lat0 == -Math.PI / 2) // accounts for special case when lat0 = -pi/2
longitude = -lon1+lon0; lonf = -lon1+lon0;
else if (Math.abs(innerFunc) > 1) { // accounts for special case when else if (Math.abs(innerFunc) > 1) { // accounts for special case when cos(lat1) = --> 0
// cos(lat) = --> 0 if ((lon1 == 0 && lat1 < -lat0) || (lon1 != 0 && lat1 < lat0))
if ((lon1 == Math.PI && lat1 < -lat0) || (lon1 != Math.PI && lat1 < lat0)) lonf = lon0;
longitude = Math.PI + lon0;
else else
longitude = lon0; lonf = lon0 + Math.PI;
} else if (Math.sin(lon1) < 0) } else if (Math.sin(lon1) > 0)
longitude = lon0 + Math lonf = Math.PI + lon0 +
.acos(Math.sin(lat1) / Math.cos(lat0) / Math.cos(latitude) - Math.tan(lat0) * Math.tan(latitude)); Math.acos(Math.sin(lat1) / Math.cos(lat0)/Math.cos(latf) - Math.tan(lat0)*Math.tan(latf));
else else
longitude = lon0 - Math.acos(Math.sin(lat1) / Math.cos(lat0) / Math.cos(latitude) - Math.tan(lat0) * Math.tan(latitude)); lonf = Math.PI + lon0 -
double orientation = (Math.cos(lat0)*Math.sin(lat0)-Math.tan(latitude)*Math.sin(lat1)+Math.tan(latitude)*Math.sin(latitude)*Math.sin(lat0))/Math.cos(lat1); Math.acos(Math.sin(lat1)/Math.cos(lat0)/Math.cos(latf) - Math.tan(lat0)*Math.tan(latf));
double[] output = {latitude, longitude, orientation}; double P = Math.sin(lat0)*Math.cos(latf)-Math.cos(lat0)*Math.sin(latf)*Math.cos(lonf-lon0);
double thtf = Math.acos(P/Math.cos(lat1));
thtf = 0;
if (coords.length >= 3) if (coords.length >= 3)
output[2] += coords[2]; // carry forward some information if necessary thtf += coords[2];
double[] output = {latf, lonf, thtf};
return output; return output;
} }
} }