mirror of
https://github.com/csharpee/Map-Projections.git
synced 2025-12-09 00:00:11 -05:00
An amalgamation beyond what Tobler ever intended
I just added a standard parallel slider to Tobler. I think it's much better this way. It'll give the optimizer more room to play. I think this is a good thing.
This commit is contained in:
parent
8f9af45190
commit
18d2d8266f
Binary file not shown.
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 10 KiB |
BIN
output/mymap.png
BIN
output/mymap.png
Binary file not shown.
|
Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 376 KiB |
@ -1,52 +1,13 @@
|
||||
We got the best Tobler projections using:
|
||||
t0=0.20689580721572343; t1=1.0518739973059956; (0.2278882297188225, 0.7351995813152631)
|
||||
t0=-0.013873876034131705; t1=1.1964036325935448; (0.10329633230643703, 0.8159488460892629)
|
||||
t0=0.07848706830962371; t1=1.250712275634557; (0.19906825204375778, 0.6909385426069267)
|
||||
t0=-0.16247243342371043; t1=2.663854628635912; (0.3764474762459828, 0.39497938818029654)
|
||||
t0=-0.12574252228336746; t1=2.68730952077553; (0.38377128187389, 0.38934754071673555)
|
||||
t0=-0.07960733577894175; t1=2.7154408517069104; (0.3922805350432534, 0.38301260966955725)
|
||||
t0=-0.02312390412484433; t1=2.747964441000864; (0.40181996234881234, 0.376245876308442)
|
||||
t0=0.22979176258322553; t1=2.7429178312596534; (0.4307734747048179, 0.35886916600298147)
|
||||
t0=0.2986403319843731; t1=2.7680744355593454; (0.4390973632047321, 0.3549598860878104)
|
||||
t0=0.4897643027102489; t1=2.731577724739168; (0.4570064132077315, 0.3489329746625129)
|
||||
t0=0.576706686336081; t1=2.727168425147056; (0.4652189203666443, 0.3475514461065558)
|
||||
|
||||
We got the best Hyperellipower projections using:
|
||||
t0=7.000030714857738; t1=1.7501736136613912; t2=1.2424301333532883; (0.05789920062472334, 0.5401983547787428)
|
||||
t0=2.9689994981677543; t1=1.5546706682635625; t2=1.202488975475673; (0.04998147976348523, 0.4916068765524706)
|
||||
t0=2.976171401808167; t1=1.5161903399231447; t2=1.1782665110594825; (0.06530293128069009, 0.4761748256585911)
|
||||
t0=2.9861817083085525; t1=1.4593728821289664; t2=1.1537120674283408; (0.09184394727824656, 0.45451796394264804)
|
||||
t0=2.9991221156792065; t1=1.3843375298085148; t2=1.1329489918084068; (0.12960786131042348, 0.4268390954046906)
|
||||
t0=3.01752604900096; t1=1.2792897036902051; t2=1.1175589617802562; (0.18431069667203306, 0.3888711249418354)
|
||||
t0=4.980007215738542; t1=1.1761278423590138; t2=1.1389472742347184; (0.2951326973404997, 0.32344075041891057)
|
||||
t0=4.980064919080647; t1=0.9783263517000584; t2=1.1487412229145555; (0.39837308894141954, 0.2505546973545707)
|
||||
t0=4.9866040560903; t1=0.6584785807029476; t2=1.2323013996369525; (0.5663045417767244, 0.13457680020557403)
|
||||
t0=5.006169745214724; t1=0.5618372179572724; t2=1.333381969495644; (0.6173994095669276, 0.10739463432525206)
|
||||
t0=5.077803445908131; t1=0.519820523555758; t2=1.375324645883218; (0.6404971339042698, 0.09702292792121305)
|
||||
|
||||
We got the best Tetrapower projections using:
|
||||
t0=1.9742544935127513; t1=0.3165825132944997; t2=1.7104482063781243; (0.12685011883033045, 0.6998556205587817)
|
||||
t0=1.962981467437631; t1=0.33675183192641267; t2=1.6361592952928836; (0.1209476582717595, 0.6640588882232411)
|
||||
t0=1.9480908277857543; t1=0.35849753079300584; t2=1.5582492283949376; (0.12325154012287963, 0.6256441904705873)
|
||||
t0=1.9234978011011936; t1=0.3847357740932864; t2=1.4604806712420046; (0.13799869866070721, 0.5762718610462976)
|
||||
t0=0.6124388919820642; t1=1.917086087357923; t2=0.9484044889912777; (0.41309531025079965, 0.2327393768086944)
|
||||
t0=1.23401828454997; t1=1.876873726139273; t2=1.0828451736039355; (0.2637145876836078, 0.31014464183255075)
|
||||
t0=0.9572598729729318; t1=1.7817153953686002; t2=1.0224033818379272; (0.32826397272609387, 0.24202139324489627)
|
||||
t0=0.8587884543704014; t1=1.6710977448846762; t2=0.9949103223977627; (0.3522709832093798, 0.21319182309649015)
|
||||
t0=0.9435163850418924; t1=1.55144565212313; t2=0.9983573057631698; (0.33448713206948405, 0.21021946056729113)
|
||||
t0=0.835223125904033; t1=1.3334457018926003; t2=0.9615459374484878; (0.36632736621112577, 0.1690016499137683)
|
||||
t0=0.7695503630650578; t1=1.0466006829884087; t2=0.7748410161473593; (0.4180555104706251, 0.19100158435703396)
|
||||
|
||||
We got the best Tetrafillet projections using:
|
||||
t0=1.697301652074613; t1=0.29231443192365125; t2=2.0833144221215747; (0.14923380124858912, 0.4443773760510159)
|
||||
t0=1.6502750223731497; t1=0.2914626537478813; t2=2.066348607583602; (0.1388325981140953, 0.43085162515261666)
|
||||
t0=1.600743232418834; t1=0.29090527877531613; t2=2.047318416001321; (0.12942585741109294, 0.41660202879376756)
|
||||
t0=0.7919132930728487; t1=0.42770091877145067; t2=1.9993410514340235; (0.2116646805720328, 0.2949663045438834)
|
||||
t0=0.8080673553412518; t1=0.44850426928591514; t2=1.934057053185013; (0.20930284601445454, 0.2806330954457315)
|
||||
t0=0.8235072145657272; t1=0.4737730344356592; t2=1.8515915590155538; (0.20896755329277447, 0.2637740962243038)
|
||||
t0=0.8337917661788428; t1=0.5030437079595327; t2=1.7520032593115307; (0.21265929624368232, 0.2452639646963364)
|
||||
t0=0.5603843826957053; t1=1.6235135259584208; t2=1.668817985780958; (0.2694541626361741, 0.2673610849383188)
|
||||
t0=0.5452329420102817; t1=1.471056278853867; t2=1.5625466788876228; (0.2730604696794315, 0.23924324368133712)
|
||||
t0=0.5584240611794797; t1=1.2338383431980802; t2=1.4331167922737151; (0.2772746999471374, 0.2084441050746002)
|
||||
t0=0.6263498318543057; t1=0.8844338247687276; t2=1.3078296521667596; (0.285312021308777, 0.1878269571175769)
|
||||
t0=0.25022057521781305; t1=4.49997342476493; (4.0724237830145623E-4, 0.5079777001726435)
|
||||
t0=0.2505753087520266; t1=4.499956546953281; (4.064604422025349E-4, 0.5079765807787102)
|
||||
t0=0.2508838353035556; t1=4.4999316499660384; (4.0573108482153483E-4, 0.5079755362982413)
|
||||
t0=0.2508314520322239; t1=4.499728417979239; (4.0450171936633287E-4, 0.5079758925954643)
|
||||
t0=0.2500693069768243; t1=4.499328429617362; (4.0750703627578564E-4, 0.5079785011865504)
|
||||
t0=0.2600667982838587; t1=4.5008562930364775; (3.8249122842821693E-4, 0.5079640977617719)
|
||||
t0=0.2558237748512546; t1=4.4998740643492035; (3.9365452439070936E-4, 0.5079694226751023)
|
||||
t0=0.24889877394521065; t1=4.498227902031633; (4.100009439557956E-4, 0.507979411007307)
|
||||
t0=0.259316034943474; t1=4.4995552660836395; (3.8333968917550053E-4, 0.5079662063081046)
|
||||
t0=0.2759279673286983; t1=4.5009128718891915; (3.3514923608814645E-4, 0.5079628635806102)
|
||||
t0=0.2554293245455028; t1=4.492372958335096; (3.945025957463458E-4, 0.5079708674912415)
|
||||
|
||||
|
||||
@ -126,8 +126,10 @@ public class MapDesignerRaster extends MapApplication {
|
||||
this.display.setFitWidth(IMG_WIDTH);
|
||||
this.display.setFitHeight(IMG_WIDTH);
|
||||
this.display.setPreserveRatio(true);
|
||||
final StackPane pane = new StackPane(display);
|
||||
pane.setMinWidth(IMG_WIDTH);
|
||||
|
||||
final HBox gui = new HBox(10, layout, this.display);
|
||||
final HBox gui = new HBox(10, layout, pane);
|
||||
gui.setAlignment(Pos.CENTER);
|
||||
StackPane.setMargin(gui, new Insets(10));
|
||||
|
||||
|
||||
@ -47,7 +47,7 @@ import maps.Projection;
|
||||
public class MapOptimizer extends Application {
|
||||
|
||||
private static final Projection[] EXISTING_PROJECTIONS = { Projection.HOBO_DYER, Projection.ROBINSON,
|
||||
Projection.PLATE_CARREE, Projection.LEE };
|
||||
Projection.PLATE_CARREE, Projection.PEIRCE_QUINCUNCIAL };
|
||||
private static final double[] WEIGHTS = { .083, .20, .33, .50, .71, 1.0, 1.4, 2.0, 3.0, 5.0, 12. };
|
||||
private static final int NUM_DESCENT = 40;
|
||||
private LineChart<Number, Number> chart;
|
||||
@ -68,15 +68,15 @@ public class MapOptimizer extends Application {
|
||||
new NumberAxis("Shape distortion", 0, 1, 0.2));
|
||||
chart.setCreateSymbols(true);
|
||||
chart.setAxisSortingPolicy(SortingPolicy.NONE);
|
||||
double[][][] globe = Projection.globe(0.02);
|
||||
double[][][] globe = Projection.globe(0.01);
|
||||
PrintStream log = new PrintStream(new File("output/parameters.txt"));
|
||||
|
||||
chart.getData().add(analyzeAll(globe, EXISTING_PROJECTIONS));
|
||||
// chart.getData().add(optimizeFamily(Projection.WINKEL_TRIPEL, globe, log));
|
||||
chart.getData().add(optimizeFamily(Projection.TOBLER, globe, log));
|
||||
chart.getData().add(optimizeFamily(Projection.HYPERELLIPOWER, globe, log));
|
||||
chart.getData().add(optimizeFamily(Projection.TETRAPOWER, globe, log));
|
||||
chart.getData().add(optimizeFamily(Projection.TETRAFILLET, globe, log));
|
||||
// chart.getData().add(optimizeFamily(Projection.HYPERELLIPOWER, globe, log));
|
||||
// chart.getData().add(optimizeFamily(Projection.TETRAPOWER, globe, log));
|
||||
// chart.getData().add(optimizeFamily(Projection.TETRAFILLET, globe, log));
|
||||
|
||||
System.out.println("Total time elapsed: "+
|
||||
(System.currentTimeMillis()-startTime)/1000.+"s");
|
||||
@ -119,11 +119,10 @@ public class MapOptimizer extends Application {
|
||||
for (int i = 0; i < params.length; i ++) params[i] = bounds[i][0]; //initialize params
|
||||
|
||||
while (true) { //start with brute force
|
||||
System.out.println(Arrays.toString(params));
|
||||
double[] distortions = proj.avgDistortion(points, params);
|
||||
System.out.println(Arrays.toString(params)+": "+Arrays.toString(distortions));
|
||||
for (int k = 0; k < WEIGHTS.length; k ++) {
|
||||
final double avgDist = Math.pow(distortions[0],1.5) +
|
||||
WEIGHTS[k]*Math.pow(distortions[1],1.5);
|
||||
final double avgDist = weighDistortion(WEIGHTS[k], distortions);
|
||||
if (avgDist < currentBest[k][0]) {
|
||||
currentBest[k][0] = avgDist;
|
||||
currentBest[k][1] = distortions[0];
|
||||
@ -134,10 +133,11 @@ public class MapOptimizer extends Application {
|
||||
|
||||
int i;
|
||||
for (i = 0; i < params.length; i ++) { //iterate the parameters
|
||||
if (params[i] < bounds[i][1]+1e-5) {
|
||||
final double step = (bounds[i][1]-bounds[i][0])/Math.floor(Math.pow(16, 1./params.length));
|
||||
if (params[i]+step < bounds[i][1]+1e-5) {
|
||||
for (int j = 0; j < i; j ++)
|
||||
params[j] = bounds[j][0];
|
||||
params[i] += (bounds[i][1]-bounds[i][0])/Math.floor(Math.pow(16, 1./params.length));
|
||||
params[i] += step;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -65,7 +65,7 @@ public enum Projection {
|
||||
}
|
||||
},
|
||||
|
||||
PLATE_CARREE("Plate Carrée", 2., 0b1111, "cylindrical", "equidistant") {
|
||||
PLATE_CARREE("Plate Carrée", 2., 0b1111, "cylindrical", "equidistant", null, "focused on the equator") {
|
||||
public double[] project(double lat, double lon, double[] params) {
|
||||
return new double[] {lon, lat};
|
||||
}
|
||||
@ -74,6 +74,20 @@ public enum Projection {
|
||||
}
|
||||
},
|
||||
|
||||
EQUIRECTANGULAR("Equirectangular", "A linear mapping from longitude and latitude to x and y",
|
||||
2, 0b1111, "cylindrical", "equidistant",
|
||||
new String[]{"Std. parallel"}, new double[][]{{0, 85, 0}}) {
|
||||
public double[] project(double lat, double lon, double[] params) {
|
||||
return new double[] {lon, lat/Math.cos(Math.toRadians(params[0]))};
|
||||
}
|
||||
public double[] inverse(double x, double y, double[] params) {
|
||||
return new double[] {y*Math.PI/2, x*Math.PI};
|
||||
}
|
||||
public double getAspectRatio(double[] params) {
|
||||
return Math.PI*Math.pow(Math.cos(Math.toRadians(params[0])), 2);
|
||||
}
|
||||
},
|
||||
|
||||
GALL_PETERS("Gall-Peters", 1.571, 0b1111, "cylindrical", "equal-area", "somewhat controversial") {
|
||||
public double[] project(double lat, double lon, double[] params) {
|
||||
return new double[] {lon, Math.sin(lat)*Math.PI/1.571};
|
||||
@ -83,7 +97,8 @@ public enum Projection {
|
||||
}
|
||||
},
|
||||
|
||||
HOBO_DYER("Hobo-Dyer", 1.977, 0b1111, "cylindrical", "equal-area", "with least distortion at 37.5°") {
|
||||
HOBO_DYER("Hobo-Dyer", 1.977, 0b1111, "cylindrical", "equal-area",
|
||||
null, "with least distortion at 37.5°") {
|
||||
public double[] project(double lat, double lon, double[] params) {
|
||||
return new double[] {lon, Math.sin(lat)*Math.PI/1.977};
|
||||
}
|
||||
@ -92,7 +107,8 @@ public enum Projection {
|
||||
}
|
||||
},
|
||||
|
||||
BEHRMANN("Behrmann", 2.356, 0b1111, null, "cylindrical", "equal-area", "with least distortion at 30°") {
|
||||
BEHRMANN("Behrmann", 2.356, 0b1111, "cylindrical", "equal-area",
|
||||
null, "with least distortion at 30°") {
|
||||
public double[] project(double lat, double lon, double[] params) {
|
||||
return new double[] {lon, Math.sin(lat)*Math.PI/2.356};
|
||||
}
|
||||
@ -398,46 +414,45 @@ public enum Projection {
|
||||
},
|
||||
|
||||
TOBLER("Tobler", "An equal-area projection shaped like a hyperellipse (in case you're wondering about gamma, it's calculated automatically)",
|
||||
2., 0b1001, "pseudocylindrical", "equal-area",new String[]{"alpha","K"},
|
||||
new double[][] {{0,1,0}, {1,8,2.5}}) {
|
||||
2., 0b1001, "pseudocylindrical", "equal-area",new String[]{"Std. Parallel","alpha","K"},
|
||||
new double[][] {{0,85,37.5}, {0,1,0}, {1,8,2.5}}) {
|
||||
private double[] Z; //Z[i] = sin(phi) when y = i/(Z.length-1)
|
||||
private double[] lastParams;
|
||||
private void generateZ(int n, double[] params) {
|
||||
final double e = NumericalAnalysis.simpsonIntegrate(0, 1,
|
||||
Tobler::hyperEllipse, .0001, params);
|
||||
Z = NumericalAnalysis.simpsonODESolve(1, n,
|
||||
Tobler::dZdY, Math.min(1./n,.0001),
|
||||
params[0], params[1], e);
|
||||
}
|
||||
public List<List<double[]>> transform(List<List<double[]>> curves, int step,
|
||||
double[] params, double[] pole, DoubleConsumer tracker) {
|
||||
generateZ(10000, params);
|
||||
return super.transform(curves, step, params, pole, tracker); //sneak z in where params used to be
|
||||
e, params[1], params[2]);
|
||||
lastParams = params.clone();
|
||||
}
|
||||
public double[] project(double lat, double lon, double[] params) {
|
||||
if (Z == null) generateZ(10000, params);
|
||||
if (Z == null || !Arrays.equals(params, lastParams))
|
||||
generateZ(10000, params);
|
||||
final double z0 = Math.abs(Math.sin(lat));
|
||||
final int i = Arrays.binarySearch(Z, z0);
|
||||
final double y;
|
||||
if (i >= 0)
|
||||
y = i/(Z.length-1.);
|
||||
else if (-i-1 >= Z.length)
|
||||
y = Z[Z.length-1];
|
||||
else
|
||||
y = Math2.linInterp(z0, Z[-i-2], Z[-i-1], -i-2, -i-1)/
|
||||
(Z.length-1.);
|
||||
final double ar = Math.pow(Math.cos(Math.toRadians(params[0])),2);
|
||||
return new double[] {
|
||||
Tobler.X(y, lon, params), Math.PI/2*y*Math.signum(lat) };
|
||||
}
|
||||
public double[][][] map(double w, double h, double[] params, double[] pole, DoubleConsumer tracker) { //generate a matrix of coordinates based on a map projection
|
||||
final int n = (int) h; //just so I don't forget
|
||||
generateZ(n, params);
|
||||
final double[][][] output = super.map(w, h, params, pole, tracker);
|
||||
Z = null; // set this to null so no one else accidentally uses it
|
||||
return output;
|
||||
Tobler.X(y, lon, params), y*Math.signum(lat)/ar };
|
||||
}
|
||||
public double[] inverse(double x, double y, double[] params) {
|
||||
if (Z == null || !Arrays.equals(params, lastParams))
|
||||
generateZ(10000, params);
|
||||
return new double[] {
|
||||
Math.asin(Z[(int)Math.round(Math.abs(y)*(Z.length-1))])*Math.signum(y),
|
||||
Tobler.lam(x,y,params) };
|
||||
}
|
||||
public double getAspectRatio(double[] params) {
|
||||
return Math.pow(Math.cos(Math.toRadians(params[0])),2) * Math.PI;
|
||||
}
|
||||
},
|
||||
|
||||
VAN_DER_GRINTEN("Van der Grinten", "A circular compromise map that is popular for some reason",
|
||||
|
||||
@ -31,26 +31,26 @@ package maps;
|
||||
public class Tobler {
|
||||
|
||||
public static final double lam(double x, double y, double[] params) {
|
||||
final double a = params[0];
|
||||
final double a = params[1];
|
||||
return Math.PI * x / Math.abs(a + (1-a)*hyperEllipse(y, params));
|
||||
}
|
||||
|
||||
|
||||
public static final double X(double y, double lam, double[] params) {
|
||||
final double a = params[0];
|
||||
final double a = params[1];
|
||||
return lam * Math.abs(a + (1-a)*hyperEllipse(y, params));
|
||||
}
|
||||
|
||||
|
||||
public static final double dZdY(double y, double[] params) {
|
||||
final double a = params[0], e = params[2];
|
||||
final double a = params[1], e = params[0];
|
||||
return Math.abs((a + (1-a)*hyperEllipse(y, params))/
|
||||
(a + (1-a)*e));
|
||||
}
|
||||
|
||||
|
||||
public static final double hyperEllipse(double y, double[] params) {
|
||||
final double k = params[1];
|
||||
final double k = params[2];
|
||||
return Math.pow(1 - Math.pow(Math.abs(y),k), 1/k);
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user