From 2fcfa325e763c0daafd68c5edd4a40a8ea6bf104 Mon Sep 17 00:00:00 2001 From: Justin Kunimune Date: Wed, 24 May 2017 10:56:45 -1000 Subject: [PATCH] General hyperelliptic things I generalized my formula to make it easier to add new map projection families. --- src/mapAnalyzer/MapOptimizer.java | 82 ++++++++++++++++++++---------- src/vectormaps/MapProjections.java | 4 +- 2 files changed, 56 insertions(+), 30 deletions(-) diff --git a/src/mapAnalyzer/MapOptimizer.java b/src/mapAnalyzer/MapOptimizer.java index 893419b..32d5a20 100644 --- a/src/mapAnalyzer/MapOptimizer.java +++ b/src/mapAnalyzer/MapOptimizer.java @@ -1,5 +1,8 @@ package mapAnalyzer; +import java.util.Arrays; +import java.util.function.BinaryOperator; + import javafx.application.Application; import javafx.scene.Scene; import javafx.scene.chart.NumberAxis; @@ -20,6 +23,7 @@ public class MapOptimizer extends Application { private static final String[] EXISTING_PROJECTIONS = { "Hobo-Dyer", "Winkel Tripel", "Robinson", "Van der Grinten", "Mercator" }; + private static final double[] WEIGHTS = {.22, .50, .86, 1.3, 2.0, 2.67, 3.0, 4.7, 8.0, 18.0 }; private ScatterChart chart; @@ -31,6 +35,8 @@ public class MapOptimizer extends Application { @Override public void start(Stage stage) throws Exception { + final long startTime = System.currentTimeMillis(); + double[][][] globe = MapAnalyzer.globe(0.02); final Series oldMaps = analyzeAll(globe, EXISTING_PROJECTIONS); final Series hyperMaps = optimizeHyperelliptical(globe); @@ -42,6 +48,9 @@ public class MapOptimizer extends Application { chart.getData().add(hyperMaps); chart.getData().add(roundMaps); + System.out.println("Total time elapsed: "+ + (System.currentTimeMillis()-startTime)/1000.); + stage.setTitle("Map Projections"); stage.setScene(new Scene(chart)); stage.show(); @@ -60,47 +69,63 @@ public class MapOptimizer extends Application { } - private static Series optimizeHyperelliptical(double[][][] points) { //optimize and plot some hyperelliptical maps - System.out.println("Hyperelliptical stuff. Aw yeah."); - final double[] weights = {.22, .50, .86, 1, 1.3, 2, 3.0, 4.7, 8.0, 18.0 }; - double[][] currentBest = new double[weights.length][5]; //the 0-3 cols are the min distortions for each weight, the other cols are the values of k and n that caused that - for (int i = 0; i < weights.length; i ++) - currentBest[i][0] = Integer.MAX_VALUE; + private static Series optimizeFamily(BinaryOperator projectionFam, String name, + double[][] bounds, double[][][] points) { // optimize and plot some maps of a given family maps + final double[][] currentBest = new double[WEIGHTS.length][3+bounds.length]; //the 0-3 cols are the min distortions for each weight, the other cols are the values of k and n that caused that + for (int k = 0; k < WEIGHTS.length; k ++) + currentBest[k][0] = Integer.MAX_VALUE; - System.out.println("Iterating k and n:"); - for (double k = 2; k <= 5; k += .25) { - for (double n = 1; n <= 2.75; n += .125) { - System.out.println("("+k+","+n+")"); - final double k0 = k, n0 = n; - double[][][] dist = MapAnalyzer.calculateDistortion(points, - (coords) -> hyperelliptical(coords, k0, n0)); - final double avgSizeD = Stat.stdDev(dist[0]); - final double avgShapeD = Stat.mean(dist[1]); - for (int i = 0; i < weights.length; i ++) { - final double avgDist = avgSizeD + weights[i]*avgShapeD; - if (avgDist < currentBest[i][0]) { - currentBest[i][0] = avgDist; - currentBest[i][1] = avgSizeD; - currentBest[i][2] = avgShapeD; - currentBest[i][3] = k; - currentBest[i][4] = n; - } + final double[] params = new double[bounds.length]; + for (int i = 0; i < params.length; i ++) params[i] = bounds[i][0]; //initialize params + + while (true) { + int i; + for (i = 0; i < params.length; i ++) { //iterate the parameters + if (params[i] < bounds[i][1]) { + for (int j = 0; j < i; j ++) + params[j] = bounds[j][0]; + params[i] += (bounds[i][1]-bounds[i][0])/8; + break; + } + } + if (i == params.length) break; //if you made it through the for loop without breaking (finding a parameter to increment), you're done! + System.out.println(Arrays.toString(params)); + + double[][][] dist = MapAnalyzer.calculateDistortion(points, + (coords) -> projectionFam.apply(coords, params)); + final double avgSizeD = Stat.stdDev(dist[0]); + final double avgShapeD = Stat.mean(dist[1]); + for (int k = 0; k < WEIGHTS.length; k ++) { + final double avgDist = avgSizeD + WEIGHTS[k]*avgShapeD; + if (avgDist < currentBest[k][0]) { + currentBest[k][0] = avgDist; + currentBest[k][1] = avgSizeD; + currentBest[k][2] = avgShapeD; + System.arraycopy(params,0, currentBest[k],3,params.length); } } } final Series output = new Series(); - output.setName("Hyperelliptics"); + output.setName(name); - System.out.println("We got the best hyperelliptic projections using:"); + System.out.println("We got the best "+name+" projections using:"); for (double[] best: currentBest) { - System.out.println("\tk="+best[3]+"; n="+best[4]); + System.out.print("\t"); + for (int i = 0; i < params.length; i ++) + System.out.print("t"+i+"="+best[3+i]+"; "); + System.out.println(); output.getData().add(new Data(best[1], best[2])); } return output; } + private static Series optimizeHyperelliptical(double[][][] points) { //optimize and plot some hyperelliptical maps + return optimizeFamily(MapOptimizer::hyperelliptical, "Hyperelliptic", new double[][] {{2,5}, {.75,1.75}}, points); + } + + private static Series optimizeElliptihypercosine(double[][][] points) { //optimize and plot some elliptical-hypebolic-cosine maps return new Series(); } @@ -114,8 +139,9 @@ public class MapOptimizer extends Application { } - private static double[] hyperelliptical(double[] coords, double k, double n) { //a hyperelliptic map projection with hyperellipse order k and lattitudinal spacind described by x^n/n + private static final double[] hyperelliptical(double[] coords, double[] params) { //a hyperelliptic map projection with hyperellipse order k and lattitudinal spacind described by x^n/n final double lat = coords[0], lon = coords[1]; + final double k = params[0], n = params[1]; return new double[] { Math.pow(1 - Math.pow(Math.abs(lat/(Math.PI/2)), k),1/k)*lon, diff --git a/src/vectormaps/MapProjections.java b/src/vectormaps/MapProjections.java index 18e4a9e..964de4b 100644 --- a/src/vectormaps/MapProjections.java +++ b/src/vectormaps/MapProjections.java @@ -833,8 +833,8 @@ public class MapProjections extends Application { private static double[] hyperelliptic(double lat, double lon) { - final double k = 3; - final double n = 1.5; + final double k = 3.5; + final double n = 1.3125; return new double[] { Math.pow(1 - Math.pow(Math.abs(lat/(Math.PI/2)), k),1/k)*lon, (1-Math.pow(1-Math.abs(lat/(Math.PI/2)), n))/Math.sqrt(n)*Math.signum(lat)*Math.PI/2};