mirror of
https://github.com/csharpee/Map-Projections.git
synced 2025-12-07 00:00:07 -05:00
add Solid Earth projection
there was also a bug where the Liquid Earth projection threw an error if you tried to use a map with points on the pole or antimeridian. but that's fixd now. I also fixed a minor issue where it said that the Equal Earth projection had a closed-form inverse when it in fact does not. I also renamed the liquid earth mesh files.
This commit is contained in:
parent
08ca187e7b
commit
4bd6f3c2c9
BIN
res/Solid_Earth_mesh_tris.npy
Normal file
BIN
res/Solid_Earth_mesh_tris.npy
Normal file
Binary file not shown.
BIN
res/Solid_Earth_mesh_verts_initial.npy
Normal file
BIN
res/Solid_Earth_mesh_verts_initial.npy
Normal file
Binary file not shown.
BIN
res/Solid_Earth_mesh_verts_transformed.npy
Normal file
BIN
res/Solid_Earth_mesh_verts_transformed.npy
Normal file
Binary file not shown.
@ -75,7 +75,7 @@ import maps.EqualEarth;
|
||||
import maps.Gyorffy;
|
||||
import maps.HammerRetroazimuthal;
|
||||
import maps.Lenticular;
|
||||
import maps.Liquid;
|
||||
import maps.Sargent;
|
||||
import maps.Misc;
|
||||
import maps.Octahedral;
|
||||
import maps.Polyhedral;
|
||||
@ -161,7 +161,7 @@ public abstract class MapApplication extends Application {
|
||||
Lenticular.HAMMER, Lenticular.LAGRANGE, Lenticular.STREBE_95,
|
||||
Lenticular.VAN_DER_GRINTEN, Lenticular.WAGNER_VIII, WinkelTripel.WINKEL_TRIPEL },
|
||||
{ Elastic.ELASTIC_I, Elastic.ELASTIC_II, Elastic.ELASTIC_III,
|
||||
Liquid.LIQUID_EARTH,
|
||||
Sargent.LIQUID_EARTH, Sargent.SOLID_EARTH,
|
||||
Danseiji.DANSEIJI_N, Danseiji.DANSEIJI_I, Danseiji.DANSEIJI_II, Danseiji.DANSEIJI_III,
|
||||
Danseiji.DANSEIJI_IV, Danseiji.DANSEIJI_V, Danseiji.DANSEIJI_VI },
|
||||
{ Misc.BONNE, Misc.CASSINI, Snyder.GS50, Misc.GUYOU, HammerRetroazimuthal.FULL,
|
||||
|
||||
@ -49,8 +49,8 @@ public class EqualEarth {
|
||||
|
||||
|
||||
public static final Projection EQUAL_EARTH = new Projection(
|
||||
"Equal Earth", "B. Savric, T. Patterson, and B. Jenny", "An equal-area pseudocylindrical projection specifically designed to woo Gall-Peters supporters away from that horrid thing",
|
||||
null, true, true, true, true, Type.PSEUDOCYLINDRICAL, Property.EQUAL_AREA, 3) {
|
||||
"Equal Earth", "B. Savric, Tom Patterson, and B. Jenny", "An equal-area pseudocylindrical projection specifically designed to woo Gall-Peters supporters away from that horrid thing",
|
||||
null, true, true, true, false, Type.PSEUDOCYLINDRICAL, Property.EQUAL_AREA, 3) {
|
||||
|
||||
public double[] project(double lat, double lon) {
|
||||
double th = asin(B*sin(lat));
|
||||
@ -69,6 +69,31 @@ public class EqualEarth {
|
||||
};
|
||||
|
||||
|
||||
public static final Projection SARGENT_EQUIVALENT_EARTH = new Projection(
|
||||
"Equivalent Earth", "R. Sargent", "An equal-area pseudocylindrical projection based on the Equal Earth projection and the cylindrical equal area projection, used as the base of the Solid Earth projection",
|
||||
null, true, true, true, false, Type.PSEUDOCYLINDRICAL, Property.EQUAL_AREA, 2) {
|
||||
|
||||
public double[] project(double lat, double lon) {
|
||||
double th = asin(B*sin(lat));
|
||||
return new double[] {
|
||||
lon/(0.3*B*poly8(th)/cos(th) + 0.7*1.4),
|
||||
0.3*poly9(th) + 0.7*1.4*sin(th)/B };
|
||||
}
|
||||
|
||||
public double[] inverse(double x, double y) {
|
||||
double th = NumericalAnalysis.newtonRaphsonApproximation(
|
||||
y, y/Y_SCALE,
|
||||
(double th_) -> 0.3*poly9(th_) + 0.7*1.4*sin(th_)/B,
|
||||
(double th_) -> 0.3*poly8(th_) + 0.7*1.4*cos(th_)/B, 1e-6);
|
||||
return new double[] { asin(sin(th)/B), x*(0.3*B*poly8(th)/cos(th) + 0.7*1.4) };
|
||||
}
|
||||
|
||||
public void initialize(double... params) throws IllegalArgumentException {
|
||||
this.shape = Shape.meridianEnvelope(this);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
private static double poly9(double x) {
|
||||
return A[3]*pow(x,9) + A[2]*pow(x,7) + A[1]*pow(x,3) + A[0]*x;
|
||||
|
||||
@ -606,9 +606,6 @@ public class Misc {
|
||||
// for polar latitudes, switch to an azimuthal projection centered on the pole
|
||||
else {
|
||||
double sign = signum(φ0);
|
||||
if (φ0 < -φMax) {
|
||||
System.out.println(sign);
|
||||
}
|
||||
return new double[] {
|
||||
sign*(PI/2 - hypot(x - sign*xPole, y - sign*yPole)),
|
||||
sign*coerceAngle(θPole - atan2(xPole - sign*x, yPole - sign*y)) };
|
||||
|
||||
@ -56,16 +56,19 @@ import static utils.Math2.coerceAngle;
|
||||
*
|
||||
* @author Justin Kunimune
|
||||
*/
|
||||
public class Liquid {
|
||||
|
||||
public static final LiquidProjection LIQUID_EARTH = new LiquidProjection(
|
||||
public class Sargent {
|
||||
|
||||
public static final SargentProjection LIQUID_EARTH = new SargentProjection(
|
||||
"Liquid Earth", "An equal-area map in the shape of the Equal Earth projection, optimized around the continents",
|
||||
EqualEarth.EQUAL_EARTH, toRadians(11), true, "liquid");
|
||||
|
||||
|
||||
private static class LiquidProjection extends Projection {
|
||||
EqualEarth.EQUAL_EARTH, toRadians(11), true);
|
||||
|
||||
public static final SargentProjection SOLID_EARTH = new SargentProjection(
|
||||
"Solid Earth", "A map optimised to show off the continents by compressing the oceans",
|
||||
EqualEarth.SARGENT_EQUIVALENT_EARTH, toRadians(11), true);
|
||||
|
||||
|
||||
private static class SargentProjection extends Projection {
|
||||
|
||||
private final String filename; // the data filename
|
||||
private final Projection baseProjection;
|
||||
private final double centralMeridian;
|
||||
private int[][] triangles;
|
||||
@ -77,14 +80,13 @@ public class Liquid {
|
||||
private List<int[]>[][] lookupTableTransformed;
|
||||
|
||||
|
||||
public LiquidProjection(
|
||||
public SargentProjection(
|
||||
String title, String description, Projection baseProjection,
|
||||
double centralMeridian, boolean based_on_land, String filename) {
|
||||
double centralMeridian, boolean based_on_land) {
|
||||
super(title, "Robert C. Sargent", description, null,
|
||||
baseProjection.isContinuous(), baseProjection.isComprehensive(), true, true,
|
||||
Type.OTHER, baseProjection.getProperty(), 4,
|
||||
new String[0], new double[0][], !based_on_land);
|
||||
this.filename = filename;
|
||||
this.baseProjection = baseProjection;
|
||||
this.centralMeridian = centralMeridian;
|
||||
}
|
||||
@ -142,13 +144,17 @@ public class Liquid {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* find the triangle (represented as an array of three vertex indices) that contains the given point, using a
|
||||
* table of cached triangle locations to do so efficiently.
|
||||
*/
|
||||
private static int[] findContainingTriangle(
|
||||
double[] фBins, double[] λBins, List<int[]>[][] lookupTable,
|
||||
Vector[] vertices, double ф, double λ) {
|
||||
λ = coerceAngle(λ);
|
||||
Vector point = new Vector(cos(ф)*cos(λ), cos(ф)*sin(λ), sin(ф));
|
||||
int i = (int) floor((ф - фBins[0])/(фBins[1] - фBins[0]));
|
||||
int j = (int) floor((λ - λBins[0])/(λBins[1] - λBins[0]));
|
||||
int i = min((int) floor((ф - фBins[0])/(фBins[1] - фBins[0])), фBins.length - 2);
|
||||
int j = min((int) floor((λ - λBins[0])/(λBins[1] - λBins[0])), λBins.length - 2);
|
||||
triangleLoop:
|
||||
for (int[] triangle: lookupTable[i][j]) {
|
||||
for (int k = 0; k < 3; k ++) {
|
||||
@ -216,14 +222,15 @@ public class Liquid {
|
||||
return;
|
||||
|
||||
// load the files
|
||||
String filename = "res/" + this.getName().replace(" ", "_") + "_mesh";
|
||||
double[][] verticesInitial, verticesTransformed;
|
||||
int[][] triangles;
|
||||
try {
|
||||
verticesInitial = readNumpyFloatArray("res/" + this.filename + "_vertices_initial.npy");
|
||||
verticesTransformed = readNumpyFloatArray("res/" + this.filename + "_vertices_transformed.npy");
|
||||
triangles = readNumpyIntArray("res/" + this.filename + "_triangles.npy");
|
||||
verticesInitial = readNumpyFloatArray(filename + "_verts_initial.npy");
|
||||
verticesTransformed = readNumpyFloatArray(filename + "_verts_transformed.npy");
|
||||
triangles = readNumpyIntArray(filename + "_tris.npy");
|
||||
} catch (IOException e) {
|
||||
throw new IllegalArgumentException("couldn't read the config file for this Liquid Projection: " + e);
|
||||
throw new IllegalArgumentException("couldn't read the config file for this Sargent Projection: " + e);
|
||||
}
|
||||
if (verticesInitial.length != verticesTransformed.length)
|
||||
throw new IllegalArgumentException("the initial vertices file and the transformed vertices files don't match.");
|
||||
@ -251,6 +258,15 @@ public class Liquid {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* build a table that will allow us to efficiently determine which triangles might contain a given pixel.
|
||||
* the output will be a table with фBins.length - 1 rows and λBins.length - 1 collums. each entry i,j will be
|
||||
* a list of triangles that contact at least one point in the range
|
||||
* фBins[i] <= ф <= фBins[i + 1] && λBins[j] <= λ <= λBins[j]
|
||||
* thus, if you need to find the triangle that contains a given point, you can find a cell of the table that
|
||||
* contains that point, and then you only need to check the contained triangles rather than every triangle in
|
||||
* the mesh.
|
||||
*/
|
||||
private static List<int[]>[][] cacheTriangleLocations(
|
||||
int[][] triangles, Vector[] vertices, double[] фBins, double[] λBins) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Loading…
x
Reference in New Issue
Block a user