Strength in unity

All map-based Applications have been changed to make use of
MapApplication. Yay. I also fixed equal-area cylindrical to be real. Now
if only Tobler and the conic maps were real.
This commit is contained in:
jkunimune 2017-07-04 09:11:37 -04:00
parent 9590f6e3a8
commit eaeaee9a5e
8 changed files with 207 additions and 268 deletions

View File

@ -24,39 +24,32 @@
package apps;
import java.io.File;
import java.io.IOException;
import java.util.function.DoubleUnaryOperator;
import javax.imageio.ImageIO;
import dialogs.ProgressBarDialog;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.concurrent.Task;
import javafx.embed.swing.SwingFXUtils;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.Node;
import javafx.scene.chart.BarChart;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart.Data;
import javafx.scene.chart.XYChart.Series;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.Separator;
import javafx.scene.control.Tooltip;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.image.PixelWriter;
import javafx.scene.image.WritableImage;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination;
import javafx.scene.input.KeyCombination;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
@ -64,24 +57,32 @@ import javafx.stage.FileChooser;
import javafx.stage.Stage;
import maps.Projection;
import util.Math2;
import util.Procedure;
/**
* An application to analyze the characteristics of map projections
*
* @author Justin Kunimune
*/
public class MapAnalyzer extends Application {
public class MapAnalyzer extends MapApplication {
private static final int CONT_WIDTH = 300;
private static final int IMG_WIDTH = 500;
public static final void main(String[] args) {
launch(args);
}
private static final int CHART_WIDTH = 400;
private static final int ROUGH_SAMP_NUM = 250;
private static final int FINE_SAMP_NUM = 1000;
private static final double GLOBE_RES = .02;
private static final FileChooser.ExtensionFilter[] RASTER_TYPES = {
new FileChooser.ExtensionFilter("PNG", "*.png"),
new FileChooser.ExtensionFilter("JPG", "*.jpg","*.jpeg","*.jpe","*.jfif") };
private static final KeyCombination ctrlS = new KeyCodeCombination(KeyCode.S, KeyCodeCombination.CONTROL_DOWN);
private static final Projection[] PROJ_ARR = { Projection.MERCATOR, Projection.PLATE_CARREE,
Projection.GALL_PETERS, Projection.HOBO_DYER, Projection.BEHRMANN, Projection.LAMBERT_CYLIND,
private static final Projection[] PROJ_ARR = { Projection.MERCATOR, Projection.PLATE_CARREE, Projection.GALL_PETERS,
Projection.HOBO_DYER, Projection.BEHRMANN, Projection.LAMBERT_CYLIND, Projection.E_A_CYLIND,
Projection.GALL, Projection.STEREOGRAPHIC, Projection.POLAR, Projection.E_A_AZIMUTH,
Projection.ORTHOGRAPHIC, Projection.GNOMONIC, Projection.LAMBERT_CONIC, Projection.E_D_CONIC,
Projection.ALBERS, Projection.LEE, Projection.TETRAGRAPH, Projection.SINUSOIDAL, Projection.MOLLWEIDE,
@ -90,198 +91,173 @@ public class MapAnalyzer extends Application {
Projection.EXPERIMENT, Projection.HYPERELLIPOWER, Projection.TETRAPOWER, Projection.TETRAFILLET };
private Stage stage;
private FileChooser saver;
private ComboBox<Projection> projectionChooser;
private Text projectionDesc;
private Button calculate, saveMap, saveCharts;
private Label avgSizeDistort, avgShapeDistort;
private ImageView output;
private VBox charts;
private BarChart<String, Number> sizeChart, shapeChart;
private Button updateBtn;
private Text avgSizeDistort, avgShapeDistort;
private ImageView mapDisplay;
private Region charts;
private BarChart<String, Number> sizeChart;
private BarChart<String, Number> shapeChart;
public static final void main(String[] args) {
launch(args);
public MapAnalyzer() {
super("Map Analyzer");
}
@Override
public void start(Stage primaryStage) {
stage = primaryStage;
stage.setTitle("Map Analyzer");
public void start(Stage root) {
super.start(root);
new Thread(() -> {
calculateAndUpdate();
}).start();
}
@Override
protected Node makeWidgets() {
final Node projectionSelector = buildProjectionSelector(PROJ_ARR, Projection.MERCATOR, Procedure.NONE);
final Node parameterSelector = buildParameterSelector(Procedure.NONE);
final Node textDisplay = buildTextDisplay();
this.updateBtn = buildUpdateButton(this::calculateAndUpdate);
this.updateBtn.setText("Calculate"); //I don't need to follow your darn conventions!
final Button saveMapBtn = buildSaveButton(true, "map", RASTER_TYPES,
Procedure.NONE, this::calculateAndSaveMap);
final Button savePltBtn = buildSaveButton(true, "plots", RASTER_TYPES,
Procedure.NONE, this::calculateAndSavePlot);
final HBox buttons = new HBox(5, updateBtn, saveMapBtn, savePltBtn);
buttons.setAlignment(Pos.CENTER);
final VBox layout = new VBox();
layout.setSpacing(5);
final VBox layout = new VBox(5,
projectionSelector, parameterSelector, new Separator(),
buttons, new Separator(), textDisplay);
layout.setAlignment(Pos.CENTER);
layout.setPrefWidth(CONT_WIDTH);
layout.setPrefWidth(GUI_WIDTH);
Label lbl = new Label("Projection:");
ObservableList<Projection> items = FXCollections.observableArrayList(PROJ_ARR);
projectionChooser = new ComboBox<Projection>(items);
projectionChooser.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {
projectionDesc.setText(projectionChooser.getValue().getDescription());
}
});
projectionChooser.setValue(Projection.MERCATOR);
layout.getChildren().add(new HBox(3, lbl, projectionChooser));
this.mapDisplay = new ImageView();
this.mapDisplay.setFitWidth(IMG_WIDTH);
this.mapDisplay.setFitHeight(IMG_WIDTH);
this.mapDisplay.setPreserveRatio(true);
final StackPane pane = new StackPane(mapDisplay);
pane.setMinWidth(IMG_WIDTH);
projectionDesc = new Text(projectionChooser.getValue().getDescription());
projectionDesc.setWrappingWidth(CONT_WIDTH);
layout.getChildren().add(projectionDesc);
this.charts = buildDistortionHistograms();
calculate = new Button("Calculate");
calculate.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {
calculateMap();
}
});
calculate.setTooltip(new Tooltip(
"Calculate the distortion for this map."));
calculate.setDefaultButton(true);
final HBox gui = new HBox(10, layout, pane, charts);
gui.setAlignment(Pos.CENTER);
StackPane.setMargin(gui, new Insets(10));
saver = new FileChooser();
saver.setInitialDirectory(new File("output"));
saver.setInitialFileName("myMap.jpg");
saver.setTitle("Save Image");
saver.getExtensionFilters().addAll(
new FileChooser.ExtensionFilter("JPG", "*.jpg"),
new FileChooser.ExtensionFilter("PNG", "*.png"));
return gui;
}
private Node buildTextDisplay() {
this.avgSizeDistort = new Text("...");
this.avgShapeDistort = new Text("...");
final Text txt = new Text("Blue areas are dilated, red areas are compressed, and black areas are stretched.");
txt.setWrappingWidth(GUI_WIDTH);
saveMap = new Button("Save Image...");
saveMap.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {
startFinalizingMap();
}
});
saveMap.setTooltip(new Tooltip("Save the distortion graphic."));
stage.addEventHandler(KeyEvent.KEY_RELEASED, new EventHandler<KeyEvent>() { //ctrl-S saves
public void handle(KeyEvent event) {
if (ctrlS.match(event)) saveMap.fire();
}
});
saveCharts = new Button("Save Chart...");
saveCharts.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {
saveImage(charts.snapshot(null, null), null);
}
});
HBox box = new HBox(5, calculate, saveMap, saveCharts);
box.setAlignment(Pos.CENTER);
layout.getChildren().add(box);
layout.getChildren().add(new Separator());
avgSizeDistort = new Label("...");
avgShapeDistort = new Label("...");
lbl = new Label("Blue areas are dilated, red areas are compressed, and black areas are stretched.");
lbl.setWrapText(true);
VBox bxo = new VBox(3,
VBox box = new VBox(3,
new HBox(new Label("Average size distortion: "),avgSizeDistort),
new HBox(new Label("Average shape distortion: "),avgShapeDistort),
lbl);
bxo.setAlignment(Pos.CENTER_LEFT);
layout.getChildren().add(bxo);
output = new ImageView();
output.setFitWidth(IMG_WIDTH);
output.setFitHeight(IMG_WIDTH);
output.setPreserveRatio(true);
sizeChart = new BarChart<String, Number>(new CategoryAxis(), new NumberAxis());
sizeChart.setPrefWidth(CHART_WIDTH);
sizeChart.setPrefHeight(IMG_WIDTH/2);
sizeChart.getXAxis().setLabel("Scale factor");
sizeChart.setBarGap(0);
sizeChart.setCategoryGap(0);
sizeChart.setAnimated(false);
sizeChart.setLegendVisible(false);
shapeChart = new BarChart<String, Number>(new CategoryAxis(), new NumberAxis());
shapeChart.setPrefWidth(CHART_WIDTH);
shapeChart.setPrefHeight(IMG_WIDTH/2);
shapeChart.getXAxis().setLabel("Stretch factor");
shapeChart.setBarGap(0);
shapeChart.setCategoryGap(0);
shapeChart.setAnimated(false);
shapeChart.setLegendVisible(false);
charts = new VBox(sizeChart, shapeChart);
final HBox gui = new HBox(layout, output, charts);
new Thread(() -> {
calculate.fire();
}).start();
gui.setAlignment(Pos.CENTER);
gui.setSpacing(10);
StackPane.setMargin(gui, new Insets(10));
stage.setScene(new Scene(new StackPane(gui)));
stage.show();
txt);
box.setAlignment(Pos.CENTER_LEFT);
return box;
}
private void calculateMap() {
calculate.setDisable(true);
new Thread(new Task<Void>() {
protected Void call() {
try {
Platform.runLater(() -> {
sizeChart.getData().clear();
shapeChart.getData().clear();
avgSizeDistort.setText("...");
avgShapeDistort.setText("...");
});
final Projection proj = projectionChooser.getValue();
final double[] params = proj.getDefaultParameters(); //TODO: get real parameters
final double[][][] distortionM =
proj.calculateDistortion(proj.map(250,params), params);
output.setImage(makeGraphic(distortionM));
final double[][][] distortionG =
proj.calculateDistortion(Projection.globe(0.02), params);
Platform.runLater(() -> {
sizeChart.getData().add(histogram(distortionG[0],
-2,2,14, true));
shapeChart.getData().add(histogram(distortionG[1],
0,1.6,14, false));
avgSizeDistort.setText(format(Math2.stdDev(distortionG[0])));
avgShapeDistort.setText(format(Math2.mean(distortionG[1])));
});
calculate.setDisable(false);
return null;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}).start();
private Region buildDistortionHistograms() {
this.sizeChart = new BarChart<String, Number>(new CategoryAxis(), new NumberAxis());
this.sizeChart.setPrefWidth(CHART_WIDTH);
this.sizeChart.setPrefHeight(IMG_WIDTH/2);
this.sizeChart.getXAxis().setLabel("Scale factor");
this.sizeChart.setBarGap(0);
this.sizeChart.setCategoryGap(0);
this.sizeChart.setAnimated(false);
this.sizeChart.setLegendVisible(false);
this.shapeChart = new BarChart<String, Number>(new CategoryAxis(), new NumberAxis());
this.shapeChart.setPrefWidth(CHART_WIDTH);
this.shapeChart.setPrefHeight(IMG_WIDTH/2);
this.shapeChart.getXAxis().setLabel("Stretch factor");
this.shapeChart.setBarGap(0);
this.shapeChart.setCategoryGap(0);
this.shapeChart.setAnimated(false);
this.shapeChart.setLegendVisible(false);
return new VBox(5, sizeChart, shapeChart);
}
private void startFinalizingMap() {
final Projection p = projectionChooser.getValue();
final double[] params = p.getDefaultParameters(); //TODO: get real parameters
private void calculateAndUpdate() {
Platform.runLater(() -> {
sizeChart.getData().clear();
shapeChart.getData().clear();
avgSizeDistort.setText("...");
avgShapeDistort.setText("...");
});
ProgressBarDialog pBar = new ProgressBarDialog();
pBar.show();
new Thread(() -> {
final double[][][] distortion = p.calculateDistortion(
p.map(1000,params), params, pBar);
Image graphic = makeGraphic(distortion);
Platform.runLater(() -> saveImage(graphic, pBar));
}).start();
final Projection proj = this.getProjection();
final double[] params = this.getParams();
final double[][][] distortionM = proj.calculateDistortion(
proj.map(ROUGH_SAMP_NUM,params), params);
mapDisplay.setImage(makeGraphic(distortionM));
final double[][][] distortionG = proj.calculateDistortion(
Projection.globe(GLOBE_RES), params);
Platform.runLater(() -> {
sizeChart.getData().add(histogram(distortionG[0],
-2, 2, 14, Math::exp));
shapeChart.getData().add(histogram(distortionG[1],
0, 2.8, 14, (x) -> 1/(x*x/2+1-x*Math.sqrt(x*x/4+1))));
avgSizeDistort.setText(format(Math2.stdDev(distortionG[0])));
avgShapeDistort.setText(format(Math2.mean(distortionG[1])));
});
}
private void calculateAndSavePlot(File file, ProgressBarDialog pBar) {
pBar.setProgress(-1);
final String filename = file.getName();
final String extension = filename.substring(filename.lastIndexOf('.')+1);
try {
final WritableImage out = new WritableImage(
(int) charts.getWidth(), (int) charts.getHeight());
Platform.runLater(() -> charts.snapshot(null,out));
while (out.getProgress() < 1) {}
ImageIO.write(SwingFXUtils.fromFXImage(out, null), extension, file); //save
} catch (IOException e) {
final Alert alert = new Alert(AlertType.ERROR);
alert.setHeaderText("Failure!");
alert.setContentText("Could not access "+file.getAbsolutePath()+". It's possible that another program has it open.");
alert.showAndWait();
}
}
private void calculateAndSaveMap(File file, ProgressBarDialog pBar) {
final Projection proj = this.getProjection();
final double[] params = this.getParams();
final double[][][] distortion = proj.calculateDistortion(
proj.map(FINE_SAMP_NUM,params), params, pBar); //calculate
Image graphic = makeGraphic(distortion);
pBar.setProgress(-1);
final String filename = file.getName();
final String extension = filename.substring(filename.lastIndexOf('.')+1);
try {
ImageIO.write(SwingFXUtils.fromFXImage(graphic,null), extension, file); //save
} catch (IOException e) {
final Alert alert = new Alert(AlertType.ERROR);
alert.setHeaderText("Failure!");
alert.setContentText("Could not access "+file.getAbsolutePath()+". It's possible that another program has it open.");
alert.showAndWait();
}
}
@ -317,25 +293,8 @@ public class MapAnalyzer extends Application {
}
private void saveImage(Image img, ProgressBarDialog pBar) { // call from the main thread!
if (pBar != null)
pBar.close();
final File f = saver.showSaveDialog(stage);
if (f != null) {
new Thread(() -> {
try {
saveMap.setDisable(true);
ImageIO.write(SwingFXUtils.fromFXImage(img,null), "png", f);
saveMap.setDisable(false);
} catch (IOException e) {}
}).start();
}
}
private static final Series<String, Number> histogram(double[][] values,
double min, double max, int num, boolean logarithmic) {
double min, double max, int num, DoubleUnaryOperator labeler) {
int[] hist = new int[num+1]; //this array is the histogram values for min, min+dx, ..., max-dx, max
int tot = 0;
for (double[] row: values) {
@ -350,9 +309,7 @@ public class MapAnalyzer extends Application {
}
Series<String, Number> output = new Series<String, Number>();
for (int i = 0; i <= num; i ++) {
double x = i*(max-min)/num+min;
if (logarithmic) x = Math.exp(x);
else x = 1/(1-Math.sin(x)); //this is a bit nonsensical and sketch. Don't worry about it.
double x = labeler.applyAsDouble(i*(max-min)/num+min);
output.getData().add(new Data<String, Number>(
Double.toString(Math.round(100*x)/100.),
(double)hist[i]/tot*100));

View File

@ -54,7 +54,7 @@ import util.Procedure;
*/
public abstract class MapApplication extends Application {
protected static final int CONT_WIDTH = 350;
protected static final int GUI_WIDTH = 350;
protected static final int IMG_WIDTH = 500;
private static final KeyCombination ctrlO = new KeyCodeCombination(KeyCode.O, KeyCodeCombination.CONTROL_DOWN);
@ -156,7 +156,7 @@ public abstract class MapApplication extends Application {
projectionChooser.setValue(defProj);
final Text description = new Text(projectionChooser.getValue().getDescription());
description.setWrappingWidth(CONT_WIDTH);
description.setWrappingWidth(GUI_WIDTH);
projectionChooser.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {
@ -399,7 +399,9 @@ public abstract class MapApplication extends Application {
paramSpinVF.setMin(paramValues[i][0]);
paramSpinVF.setMax(paramValues[i][1]);
paramSpinVF.setValue(paramValues[i][2]);
final Tooltip tt = new Tooltip("Change the "+paramNames[i]+" of the map");
final Tooltip tt = new Tooltip(
"Change the "+paramNames[i]+" of the map (default is "+
paramValues[i][2]+")");
paramSliders[i].setTooltip(tt);
paramSpinners[i].setTooltip(tt);
paramGrid.addRow(i, paramLabels[i], paramSliders[i], paramSpinners[i]);

View File

@ -29,7 +29,6 @@ import javax.imageio.ImageIO;
import dialogs.MapConfigurationDialog;
import dialogs.ProgressBarDialog;
import javafx.application.Platform;
import javafx.embed.swing.SwingFXUtils;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
@ -118,16 +117,15 @@ public class MapDesignerRaster extends MapApplication {
new Separator(), aspectSelector, parameterSelector,
new Separator(), buttons);
layout.setAlignment(Pos.CENTER);
layout.setPrefWidth(CONT_WIDTH);
layout.setPrefWidth(GUI_WIDTH);
this.display = new ImageView();
this.display.setFitWidth(IMG_WIDTH);
this.display.setFitHeight(IMG_WIDTH);
this.display.setPreserveRatio(true);
final HBox gui = new HBox(layout, this.display);
final HBox gui = new HBox(10, layout, this.display);
gui.setAlignment(Pos.CENTER);
gui.setSpacing(10);
StackPane.setMargin(gui, new Insets(10));
return gui;
@ -167,10 +165,9 @@ public class MapDesignerRaster extends MapApplication {
protected void calculateAndSaveMap(File file, ProgressBarDialog pBar) {
Image theMap = map(
configDialog.getDims(), configDialog.getSmoothing(), pBar); //calculate
Platform.runLater(() -> pBar.setProgress(-1));
pBar.setProgress(-1);
final String filename = file.getName();
final String extension;
extension = filename.substring(filename.lastIndexOf('.')+1);
final String extension = filename.substring(filename.lastIndexOf('.')+1);
try {
ImageIO.write(SwingFXUtils.fromFXImage(theMap,null), extension, file); //save
} catch (IOException e) {

View File

@ -117,13 +117,12 @@ public class MapDesignerVector extends MapApplication {
new Separator(), saveBtn);
layout.setAlignment(Pos.CENTER);
layout.setPrefWidth(CONT_WIDTH);
layout.setPrefWidth(GUI_WIDTH);
viewer = new Canvas(IMG_WIDTH, IMG_WIDTH);
final HBox gui = new HBox(layout, viewer);
final HBox gui = new HBox(10, layout, viewer);
gui.setAlignment(Pos.CENTER);
gui.setSpacing(10);
StackPane.setMargin(gui, new Insets(10));
return gui;

View File

@ -45,8 +45,7 @@ import maps.Projection;
* @author Justin Kunimune
*/
public class MapOptimizer extends Application {
private static final Projection[] EXISTING_PROJECTIONS = { Projection.HOBO_DYER, Projection.ROBINSON,
Projection.PLATE_CARREE, Projection.LEE };
private static final double[] WEIGHTS = { .083, .20, .33, .50, .71, 1.0, 1.4, 2.0, 3.0, 5.0, 12. };
@ -73,10 +72,10 @@ public class MapOptimizer extends Application {
PrintStream log = new PrintStream(new File("output/parameters.txt"));
chart.getData().add(analyzeAll(globe, EXISTING_PROJECTIONS));
chart.getData().add(optimizeHyperelliptical(globe, log));
// chart.getData().add(optimizeEACylinder(globe, log));
chart.getData().add(optimizeTetrapower(globe, log));
chart.getData().add(optimizeTetrafillet(globe, log));
chart.getData().add(optimizeFamily(Projection.HYPERELLIPOWER, globe, log));
chart.getData().add(optimizeFamily(Projection.WINKEL_TRIPEL, 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");
@ -108,18 +107,19 @@ public class MapOptimizer extends Application {
private static Series<Number, Number> optimizeFamily(
Projection projectionFam, double[][] bounds, double[][][] points, PrintStream log) { // optimize and plot some maps of a given family maps
System.out.println("Optimizing "+projectionFam.getName());
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
Projection proj, double[][][] points, PrintStream log) { // optimize and plot some maps of a given family maps
System.out.println("Optimizing "+proj.getName());
final double[][] currentBest = new double[WEIGHTS.length][3+proj.getNumParameters()]; //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;
final double[] params = new double[bounds.length];
for (int i = 0; i < params.length; i ++) params[i] = bounds[i][0]; //initialize params
final double[][] bounds = proj.getParameterValues();
final double[] params = new double[proj.getNumParameters()];
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 = projectionFam.avgDistortion(points, params);
double[] distortions = proj.avgDistortion(points, params);
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);
@ -153,26 +153,26 @@ public class MapOptimizer extends Application {
for (int i = 0; i < NUM_DESCENT; i ++) {
if (i > 0)
fr0 = weighDistortion(WEIGHTS[k],
projectionFam.avgDistortion(points, params)); //calculate the distortion here
proj.avgDistortion(points, params)); //calculate the distortion here
System.out.println(Arrays.toString(params)+" -> "+fr0);
for (int j = 0; j < params.length; j ++) {
params[j] += h;
frd[j] = weighDistortion(WEIGHTS[k],
projectionFam.avgDistortion(points, params)); //and the distortion nearby
proj.avgDistortion(points, params)); //and the distortion nearby
params[j] -= h;
}
for (int j = 0; j < params.length; j ++)
params[j] -= (frd[j]-fr0)/h*delX; //use that to approximate the gradient and go in that direction
}
System.arraycopy(params,0, currentBest[k],3, params.length);
System.arraycopy(projectionFam.avgDistortion(points, params), 0,
System.arraycopy(proj.avgDistortion(points, params), 0,
currentBest[k], 1, 2);
}
final Series<Number, Number> output = new Series<Number, Number>();
output.setName(projectionFam.getName());
output.setName(proj.getName());
log.println("We got the best "+projectionFam.getName()+" projections using:");
log.println("We got the best "+proj.getName()+" projections using:");
for (double[] best: currentBest) {
log.print("\t");
for (int i = 0; i < params.length; i ++)
@ -191,27 +191,6 @@ public class MapOptimizer extends Application {
}
private static Series<Number, Number> optimizeHyperelliptical(
double[][][] points, PrintStream log) { //optimize and plot some hyperelliptical maps
return optimizeFamily(Projection.HYPERELLIPOWER,
new double[][] {{2.5,5}, {0.5,1.75}, {1.0,2.0}}, points, log);
}
private static Series<Number, Number> optimizeTetrapower(
double[][][] points, PrintStream log) { //optimize and plot some hyperelliptical maps
return optimizeFamily(Projection.TETRAPOWER,
new double[][] {{0.25,2.25}, {0.25,2.25}, {.25,2.25}}, points, log);
}
private static Series<Number, Number> optimizeTetrafillet(
double[][][] points, PrintStream log) { //optimize and plot some hyperelliptical maps
return optimizeFamily(Projection.TETRAFILLET,
new double[][] {{0.25,2.25}, {0.25,2.25}, {.25,2.25}}, points, log);
}
private static Data<Number, Number> plotDistortion(
double[][][] pts, Projection proj, double[] params) {
double[] distortion = proj.avgDistortion(pts, params);

View File

@ -83,10 +83,13 @@ public class ProgressBarDialog extends Dialog<Void> {
Platform.runLater(() -> {
if (p >= 0)
this.words.setText((Math.round(1000*p)/10.0)+"%");
else
else {
this.bar.setProgress(-1);
this.words.setText("...");
}
});
this.bar.setProgress(p);
if (p >= 0)
this.bar.setProgress(p);
}
}

View File

@ -108,17 +108,18 @@ public enum Projection {
}
},
EA_CYLIND("Equal-area cylindrical", "A generalized equal-area cylindrical projection",
E_A_CYLIND("Equal-area cylindrical", "A generalized equal-area cylindrical projection",
Math.PI, 0b1111, "cylindrical", "equal-area",
new String[]{"Std. parallel"}, new double[][]{{0, 90, 37.5}}) {
new String[]{"Std. parallel"}, new double[][]{{0, 75, 37.5}}) {
public double[] project(double lat, double lon, double[] params) {
return new double[] {lon, Math.sin(lat)/params[0]};
final double a = Math.pow(Math.cos(Math.toRadians(params[0])), 2);
return new double[] {lon, Math.sin(lat)/a};
}
public double[] inverse(double x, double y, double[] params) {
return new double[] { Math.asin(y), x*Math.PI };
}
public double getAspectRatio(double[] params) {
return 1/Math.cos(params[0]);
return Math.PI*Math.pow(Math.cos(Math.toRadians(params[0])), 2);
}
},

View File

@ -119,6 +119,7 @@ public class NumericalAnalysis {
@FunctionalInterface
public interface VectorFunction {
public double evaluate(double x, double y, double[] constants);
}