mirror of
https://github.com/csharpee/Map-Projections.git
synced 2025-12-30 00:01:30 -05:00
A Whole New WAAAAAAAAAAAAAAAterman!
I've used that reference before, haven't I? In any case, I have successfully transfered Waterman to a new Octohedral class that is far more generalisable than the last one. I now intend to start moving Cahill-Keyes, and coding in Cahill-Rus, Cahill-Concialdi, and the detachment of Antarctica. The end goal: more and better octohedral maps in my collection.
This commit is contained in:
parent
b50e63d19d
commit
9d85e98492
@ -81,12 +81,12 @@ import maps.Cylindrical;
|
||||
import maps.Lenticular;
|
||||
import maps.Misc;
|
||||
import maps.MyProjections;
|
||||
import maps.Octohedral;
|
||||
import maps.Polyhedral;
|
||||
import maps.Projection;
|
||||
import maps.Pseudocylindrical;
|
||||
import maps.Snyder;
|
||||
import maps.Tobler;
|
||||
import maps.Waterman;
|
||||
import maps.WinkelTripel;
|
||||
import utils.Flag;
|
||||
import utils.Math2;
|
||||
@ -114,7 +114,7 @@ public abstract class MapApplication extends Application {
|
||||
private static final KeyCombination CTRL_ENTER = new KeyCodeCombination(KeyCode.ENTER, KeyCodeCombination.CONTROL_DOWN);
|
||||
|
||||
|
||||
public static final Projection[] FEATURED_PROJECTIONS = { Cylindrical.MERCATOR,
|
||||
public static final Projection[] FEATURED_PROJECTIONS = { Octohedral.WATERMAN, Cylindrical.MERCATOR,
|
||||
Cylindrical.EQUIRECTANGULAR, Cylindrical.EQUAL_AREA, Cylindrical.GALL_STEREOGRAPHIC,
|
||||
Azimuthal.STEREOGRAPHIC, Azimuthal.POLAR, Azimuthal.EQUAL_AREA, Azimuthal.GNOMONIC,
|
||||
Azimuthal.PERSPECTIVE, Conic.LAMBERT, Conic.EQUIDISTANT, Conic.ALBERS,
|
||||
@ -135,7 +135,7 @@ public abstract class MapApplication extends Application {
|
||||
{ Conic.ALBERS, Conic.LAMBERT, Conic.EQUIDISTANT },
|
||||
{ Polyhedral.AUTHAGRAPH, CahillKeyes.M_MAP, Polyhedral.DYMAXION,
|
||||
Polyhedral.LEE_TETRAHEDRAL_RECTANGULAR, Polyhedral.LEE_TETRAHEDRAL_TRIANGULAR,
|
||||
Waterman.BUTTERFLY },
|
||||
Octohedral.WATERMAN },
|
||||
{ Pseudocylindrical.ECKERT_IV, Pseudocylindrical.KAVRAYSKIY_VII,
|
||||
Pseudocylindrical.MOLLWEIDE, Arbitrary.NATURAL_EARTH, Arbitrary.ROBINSON,
|
||||
Pseudocylindrical.SINUSOIDAL, Tobler.TOBLER },
|
||||
|
||||
@ -30,19 +30,19 @@ import java.io.PrintStream;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
import maps.Arbitrary;
|
||||
import maps.Azimuthal;
|
||||
import maps.Conic;
|
||||
import maps.Cylindrical;
|
||||
import maps.Lenticular;
|
||||
import maps.Misc;
|
||||
import maps.MyProjections;
|
||||
import maps.Octohedral;
|
||||
import maps.Polyhedral;
|
||||
import maps.Projection;
|
||||
import maps.Pseudocylindrical;
|
||||
import maps.Arbitrary;
|
||||
import maps.Snyder;
|
||||
import maps.Polyhedral;
|
||||
import maps.Tobler;
|
||||
import maps.Waterman;
|
||||
import maps.WinkelTripel;
|
||||
import utils.ImageUtils;
|
||||
import utils.PixelMap;
|
||||
@ -66,7 +66,7 @@ public class MapExplainer {
|
||||
Polyhedral.LEE_TETRAHEDRAL_RECTANGULAR, Polyhedral.AUTHAGRAPH,
|
||||
Pseudocylindrical.SINUSOIDAL, Pseudocylindrical.MOLLWEIDE, Tobler.TOBLER,
|
||||
Lenticular.HAMMER, Lenticular.AITOFF, Lenticular.VAN_DER_GRINTEN, Arbitrary.ROBINSON,
|
||||
WinkelTripel.WINKEL_TRIPEL, Waterman.BUTTERFLY, Misc.PEIRCE_QUINCUNCIAL.transverse(),
|
||||
WinkelTripel.WINKEL_TRIPEL, Octohedral.WATERMAN, Misc.PEIRCE_QUINCUNCIAL.transverse(),
|
||||
Snyder.GS50, Misc.TWO_POINT_EQUIDISTANT, Misc.HAMMER_RETROAZIMUTHAL, Misc.FLAT_EARTH },
|
||||
|
||||
{ MyProjections.PSEUDOSTEREOGRAPHIC, Polyhedral.TETRAGRAPH, Polyhedral.AUTHAPOWER,
|
||||
|
||||
198
src/maps/Octohedral.java
Normal file
198
src/maps/Octohedral.java
Normal file
@ -0,0 +1,198 @@
|
||||
/**
|
||||
* 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 maps.Projection.Property;
|
||||
|
||||
/**
|
||||
* A class of maps that use octohedral octants. Very similar to Polyhedral, but much faster since
|
||||
* it takes advantage of the fact that everything is orthogonal.
|
||||
*
|
||||
* @author jkunimune
|
||||
*/
|
||||
public class Octohedral {
|
||||
|
||||
public static final Projection WATERMAN = new OctohedralProjection(
|
||||
"Waterman Butterfly", "A simple Cahill-esque octohedral map arrangement, with Antarctica left on.",
|
||||
2*Math.sqrt(3), (Math.sqrt(3)-1)/2, 0b1010, Property.COMPROMISE, 3,
|
||||
Configuration.BUTTERFLY) {
|
||||
|
||||
protected double[] faceProject(double lat, double lon) {
|
||||
return Waterman.faceProject(lat, lon);
|
||||
}
|
||||
|
||||
protected double[] faceInverse(double x, double y) {
|
||||
return Waterman.faceInverse(x, y);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
private static abstract class OctohedralProjection extends Projection {
|
||||
|
||||
private final double size;
|
||||
private Configuration config;
|
||||
|
||||
|
||||
public OctohedralProjection(String name, String desc, double altitude, double cutSize,
|
||||
int fisc, Property property, int rating, Configuration config) {
|
||||
super(name, desc,
|
||||
config.fullWidth*altitude-config.cutWidth*cutSize,
|
||||
config.fullHeight*altitude-config.cutHeight*cutSize, fisc,
|
||||
(cutSize == 0) ? Type.OCTOHEDRAL : Type.TETRADECAHEDRAL, property, rating);
|
||||
this.size = altitude;
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
|
||||
protected abstract double[] faceProject(double lat, double lon);
|
||||
|
||||
protected abstract double[] faceInverse(double x, double y);
|
||||
|
||||
|
||||
public double[] project(double lat, double lon) {
|
||||
double[] octant = config.project(lat, lon); //octant properties
|
||||
double x0 = octant[0]*size, y0 = octant[1]*size, tht0 = octant[2], lon0 = octant[3];
|
||||
|
||||
double[] coords = this.faceProject(Math.abs(lat), Math.abs(lon-lon0));
|
||||
double xMj = coords[0], yMj = coords[1]; //relative octant coordinates (Mj stands for "Mary Jo Graca")
|
||||
|
||||
if (lat < 0) //reflect the southern hemisphere over the equator
|
||||
xMj = 2*size - xMj;
|
||||
if (lon-lon0 < 0)
|
||||
yMj = -yMj;
|
||||
|
||||
return new double[] {
|
||||
x0 + Math.sin(tht0)*xMj + Math.cos(tht0)*yMj,
|
||||
y0 - Math.cos(tht0)*xMj + Math.sin(tht0)*yMj + config.fullHeight*size/2 };
|
||||
}
|
||||
|
||||
|
||||
public double[] inverse(double x, double y) {
|
||||
y = y - config.fullHeight*size/2; //measure from extrapolated top of map, not centre
|
||||
double[] octant = config.inverse(x/size, y/size);
|
||||
if (octant == null) return null;
|
||||
double lon0 = octant[0], x0 = size*octant[1], y0 = size*octant[2], tht0 = octant[3];
|
||||
|
||||
double xMj = Math.sin(tht0)*(x-x0) - Math.cos(tht0)*(y-y0);
|
||||
double yMj = Math.cos(tht0)*(x-x0) + Math.sin(tht0)*(y-y0);
|
||||
|
||||
double[] coords = this.faceInverse(Math.min(xMj, 2*size-xMj), Math.abs(yMj));
|
||||
if (coords == null) return null;
|
||||
double lat = coords[0], lon = coords[1];
|
||||
|
||||
return new double[] { Math.signum(size-xMj)*lat, Math.signum(yMj)*lon + lon0 };
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
private enum Configuration {
|
||||
|
||||
BUTTERFLY(4, 2, 4/Math.sqrt(3), Math.sqrt(3)) { //the classic four octants splayed out in a nice butterfly shape, with Antarctica divided and attached
|
||||
|
||||
private final double Y_OFFSET = -1/Math.sqrt(3);
|
||||
|
||||
public double[] project(double lat, double lon) {
|
||||
if (Math.abs(lon) > Math.PI && lat < 0) {
|
||||
double sign = Math.signum(lon);
|
||||
return new double[] {sign, 2/Math.sqrt(3), sign*Math.PI/6, sign*Math.PI};
|
||||
}
|
||||
double centralMerid = Math.floor(lon/(Math.PI/2))*Math.PI/2 + Math.PI/4;
|
||||
return new double[] { 0, Y_OFFSET, centralMerid*2/3., centralMerid };
|
||||
}
|
||||
|
||||
public double[] inverse(double x, double y) {
|
||||
if (y > (1-Math.abs(x))/Math.sqrt(3)) {
|
||||
double sign = Math.signum(x);
|
||||
return new double[] { sign*5*Math.PI/4, sign, 2/Math.sqrt(3), sign*Math.PI/6 };
|
||||
}
|
||||
double tht = Math.atan2(x, -y+Y_OFFSET);
|
||||
if (Math.abs(tht) > 5*Math.PI/6)
|
||||
return null;
|
||||
double centralAngle = Math.floor(tht/(Math.PI/3))*Math.PI/3 + Math.PI/6;
|
||||
return new double[] { centralAngle*3/2., 0, Y_OFFSET, centralAngle };
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
M_PROFILE(4, 0, Math.sqrt(3), Math.sqrt(3)) {
|
||||
|
||||
public double[] project(double lat, double lon) {
|
||||
// TODO: Implement this
|
||||
return null;
|
||||
}
|
||||
|
||||
public double[] inverse(double x, double y) {
|
||||
// TODO: Implement this
|
||||
return null;
|
||||
} //The more compact zigzag configuration with Antarctica divided and attached
|
||||
|
||||
},
|
||||
|
||||
|
||||
M_W_S_POLE(4, 0, 3.5/Math.sqrt(3), 1.5*Math.sqrt(3)) {
|
||||
|
||||
public double[] project(double lat, double lon) {
|
||||
// TODO: Implement this
|
||||
return null;
|
||||
}
|
||||
|
||||
public double[] inverse(double x, double y) {
|
||||
// TODO: Implement this
|
||||
return null;
|
||||
} //Keyes's current configuration, with Antarctica reassembled in the center
|
||||
},
|
||||
|
||||
|
||||
BAT_SHAPE(2*Math.sqrt(3), 0, 2, 0) {
|
||||
|
||||
public double[] project(double lat, double lon) {
|
||||
// TODO: Implement this
|
||||
return null;
|
||||
}
|
||||
|
||||
public double[] inverse(double x, double y) {
|
||||
// TODO: Implement this
|
||||
return null;
|
||||
} //Luca Concialdi's obscure "Bat" arrangement that I liked. I don't think it's the best map possible as Luca does, but I do think it's quite neat
|
||||
};
|
||||
|
||||
|
||||
public final double fullWidth, cutWidth, fullHeight, cutHeight;
|
||||
|
||||
private Configuration(double fullWidth, double cutWidth,
|
||||
double fullHeight, double cutHeight) {
|
||||
this.fullWidth = fullWidth;
|
||||
this.cutWidth = cutWidth;
|
||||
this.fullHeight = fullHeight;
|
||||
this.cutHeight = cutHeight;
|
||||
}
|
||||
|
||||
public abstract double[] project(double lat, double lon); //calculate the x, y, rotation, and central meridian for this quadrant
|
||||
public abstract double[] inverse(double x, double y); //calculate the central meridian, x, y, and rotation for this quadrant
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,6 +1,4 @@
|
||||
package maps;
|
||||
import maps.Projection.Property;
|
||||
import maps.Projection.Type;
|
||||
import utils.Math2;
|
||||
|
||||
/**
|
||||
@ -28,8 +26,7 @@ import utils.Math2;
|
||||
*/
|
||||
|
||||
/**
|
||||
* A class devoted to the Waterman butterfly, because it doesn't really fit into Polyhedral.java or
|
||||
* CahillKeyes.java, and Misc.java has too many projections already.
|
||||
* A class of methods pertaining to the Waterman projection.
|
||||
* http://www.watermanpolyhedron.com/methodp.html
|
||||
*
|
||||
* @author jkunimune
|
||||
@ -40,58 +37,12 @@ public class Waterman {
|
||||
{(Math.sqrt(3)-1)/2, .5*Math.sqrt(3), 1.5*Math.sqrt(3), Double.NaN};
|
||||
private static final double[] dYdL = {0, 2/Math.PI, 6/Math.PI, (4+Math.sqrt(8))/Math.PI};
|
||||
|
||||
private static final double lossX = (Math.sqrt(3)-1)/2; //horizontal dimension interruption loss
|
||||
private static final double lossY = lossX*Math.sqrt(3)/2; //vertical dimension interruption loss
|
||||
private static final double lonDiag = Math.PI/(4+Math.sqrt(8));
|
||||
private static final double sin15 = (Math.sqrt(3)-1)/Math.sqrt(8);
|
||||
private static final double cos15 = (Math.sqrt(3)+1)/Math.sqrt(8);
|
||||
|
||||
|
||||
public static final Projection BUTTERFLY =
|
||||
new Projection(
|
||||
"Waterman", "An aesthetically pleasing octohedral map arrangement.",
|
||||
8*Math.sqrt(3)-2*lossX, 8-2*lossY, 0b1010,
|
||||
Type.TETRADECAHEDRAL, Property.COMPROMISE, 3) {
|
||||
|
||||
public double[] project(double lat, double lon) {
|
||||
double centralLon = Math.floor(lon/(Math.PI/2))*Math.PI/2 + Math.PI/4; //get the octant longitude
|
||||
if (lon == Math.PI) centralLon = 3*Math.PI/4; //a hack to fix a minor issue with the IDL
|
||||
|
||||
double[] mjCoords = faceProject(Math.abs(lat), Math.abs(lon - centralLon));
|
||||
double mjX = mjCoords[0]; //"mj" stands for "Mary Jo Graca". I know she had nothing to do with the Waterman projection, but it means the same thing here as it does in Cahill-Keyes
|
||||
double mjY = mjCoords[1];
|
||||
|
||||
if (lat < 0) //if it's in the southern hemisphere
|
||||
mjX = 4*Math.sqrt(3) - mjX; //flip it around
|
||||
if (lon < centralLon) //if the relative longitude is negative
|
||||
mjY = -mjY; //flip it the other way
|
||||
return new double[] {
|
||||
mjX*Math.sin(centralLon*2/3) + mjY*Math.cos(centralLon*2/3),
|
||||
-mjX*Math.cos(centralLon*2/3) + mjY*Math.sin(centralLon*2/3) + 2 };
|
||||
}
|
||||
|
||||
public double[] inverse(double x, double y) {
|
||||
y -= 2;
|
||||
double tht = Math.atan2(x, -y);
|
||||
if (Math.abs(tht) > 5*Math.PI/6) return null; //only show a little bit of extra
|
||||
|
||||
double quadrAngle = (Math.floor(tht/(Math.PI/3))+2)*Math.PI/3; //the angle of the centre of the quadrant, measured widdershins from -y
|
||||
double centralLon = quadrAngle*1.5 - 3*Math.PI/4; //the central meridian of this quadrant
|
||||
double mjX = -x*Math.cos(quadrAngle) - y*Math.sin(quadrAngle);
|
||||
double mjY = x*Math.sin(quadrAngle) - y*Math.cos(quadrAngle);
|
||||
|
||||
double[] relCoords = faceInverse(Math.min(mjX, 4*Math.sqrt(3)-mjX), Math.abs(mjY));
|
||||
if (relCoords == null)
|
||||
return null;
|
||||
|
||||
if (mjY < 0) relCoords[1] *= -1; //the left half of the octant gets shifted west
|
||||
if (mjX > 2*Math.sqrt(3)) relCoords[0] *= -1; //the outer rim of the map is the southern hemisphere
|
||||
return new double[] { relCoords[0], relCoords[1] + centralLon };
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
private static double[] faceProject(double lat, double lon) {
|
||||
public static double[] faceProject(double lat, double lon) {
|
||||
double[] yELD = jointHeights(lon);
|
||||
double desirLength = Math2.linInterp(lat, Math.PI/2, 0, 0, totalLength(xELD, yELD));
|
||||
|
||||
@ -107,7 +58,7 @@ public class Waterman {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static double[] faceInverse(double x, double y) {
|
||||
public static double[] faceInverse(double x, double y) {
|
||||
if (y > x-(Math.sqrt(3)-1)/2 || y > x/Math.sqrt(3) || y > (2-Math.sqrt(3))*(x+3) ||
|
||||
y > (7+4*Math.sqrt(3))-(2+Math.sqrt(3))*x || x > 2*Math.sqrt(3)) //this describes the footprint of the octant
|
||||
return null;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user