mirror of
https://github.com/csharpee/Map-Projections.git
synced 2025-12-16 00:00:06 -05:00
This code does not work
But I wanted to commit it because I spent a bunch of time on this SVG parser that apparently already exists in JavaFX.
This commit is contained in:
parent
bb5a192509
commit
a9c84a7881
1248
input/basic.svg
Normal file
1248
input/basic.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 1.4 MiB |
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 931 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 558 KiB After Width: | Height: | Size: 276 KiB |
@ -130,7 +130,7 @@ public class MapProjections extends Application {
|
|||||||
changeInput = new Button("Choose input...");
|
changeInput = new Button("Choose input...");
|
||||||
changeInput.setOnAction(new EventHandler<ActionEvent>() {
|
changeInput.setOnAction(new EventHandler<ActionEvent>() {
|
||||||
public void handle(ActionEvent event) {
|
public void handle(ActionEvent event) {
|
||||||
setInput();
|
chooseInput();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
changeInput.setTooltip(new Tooltip(
|
changeInput.setTooltip(new Tooltip(
|
||||||
@ -241,13 +241,10 @@ public class MapProjections extends Application {
|
|||||||
output.setFitHeight(IMG_WIDTH);
|
output.setFitHeight(IMG_WIDTH);
|
||||||
output.setPreserveRatio(true);
|
output.setPreserveRatio(true);
|
||||||
|
|
||||||
try {
|
new Thread(() -> {
|
||||||
input = new Image("file:input/basic.jpg");
|
setInput("basic.jpg", "input/basic.jpg");
|
||||||
inputLabel.setText("basic.jpg");
|
update.fire();
|
||||||
update.getOnAction().handle(null);
|
}).start();
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
update.setDisable(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
final HBox gui = new HBox(layout, output);
|
final HBox gui = new HBox(layout, output);
|
||||||
gui.setAlignment(Pos.CENTER);
|
gui.setAlignment(Pos.CENTER);
|
||||||
@ -258,25 +255,35 @@ public class MapProjections extends Application {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void setInput() {
|
private void chooseInput() {
|
||||||
final File f = inputChooser.showOpenDialog(stage);
|
final File f = inputChooser.showOpenDialog(stage);
|
||||||
if (f != null) {
|
if (f != null) {
|
||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
changeInput.setDisable(true);
|
setInput(f.getName(), f.getAbsolutePath());
|
||||||
update.setDisable(true);
|
|
||||||
saveMap.setDisable(true);
|
|
||||||
|
|
||||||
input = new Image("file:"+f.getAbsolutePath());
|
|
||||||
inputLabel.setText(f.getName());
|
|
||||||
|
|
||||||
changeInput.setDisable(false);
|
|
||||||
update.setDisable(false);
|
|
||||||
saveMap.setDisable(false);
|
|
||||||
}).start();
|
}).start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void setInput(String name, String filename) {
|
||||||
|
changeInput.setDisable(true);
|
||||||
|
update.setDisable(true);
|
||||||
|
saveMap.setDisable(true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
input = new Image("file:"+filename);
|
||||||
|
inputLabel.setText(name);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
inputLabel.setText("Error loading "+name+"! It may be corrupt or nonexistent."); //TODO: Better error handling?
|
||||||
|
System.err.println(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
changeInput.setDisable(false);
|
||||||
|
update.setDisable(false);
|
||||||
|
saveMap.setDisable(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private void setAxisByPreset(String preset) {
|
private void setAxisByPreset(String preset) {
|
||||||
if (preset.equals("Antipode")) {
|
if (preset.equals("Antipode")) {
|
||||||
latSlider.setValue(-latSlider.getValue());
|
latSlider.setValue(-latSlider.getValue());
|
||||||
|
|||||||
841
src/vectormaps/MapProjections.java
Normal file
841
src/vectormaps/MapProjections.java
Normal file
@ -0,0 +1,841 @@
|
|||||||
|
package vectormaps;
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.BufferedWriter;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileReader;
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import org.apache.commons.math3.complex.Complex;
|
||||||
|
|
||||||
|
import javafx.application.Application;
|
||||||
|
import javafx.application.Platform;
|
||||||
|
import javafx.collections.FXCollections;
|
||||||
|
import javafx.collections.ObservableList;
|
||||||
|
import javafx.concurrent.Task;
|
||||||
|
import javafx.event.ActionEvent;
|
||||||
|
import javafx.event.EventHandler;
|
||||||
|
import javafx.geometry.Insets;
|
||||||
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.scene.Scene;
|
||||||
|
import javafx.scene.canvas.Canvas;
|
||||||
|
import javafx.scene.canvas.GraphicsContext;
|
||||||
|
import javafx.scene.control.Alert;
|
||||||
|
import javafx.scene.control.Button;
|
||||||
|
import javafx.scene.control.ComboBox;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.control.MenuButton;
|
||||||
|
import javafx.scene.control.MenuItem;
|
||||||
|
import javafx.scene.control.Separator;
|
||||||
|
import javafx.scene.control.Slider;
|
||||||
|
import javafx.scene.control.Tooltip;
|
||||||
|
import javafx.scene.input.KeyCode;
|
||||||
|
import javafx.scene.input.KeyCodeCombination;
|
||||||
|
import javafx.scene.input.KeyCombination;
|
||||||
|
import javafx.scene.input.KeyEvent;
|
||||||
|
import javafx.scene.layout.GridPane;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
|
import javafx.scene.layout.Priority;
|
||||||
|
import javafx.scene.layout.StackPane;
|
||||||
|
import javafx.scene.layout.VBox;
|
||||||
|
import javafx.scene.text.Text;
|
||||||
|
import javafx.stage.FileChooser;
|
||||||
|
import javafx.stage.Stage;
|
||||||
|
import rastermaps.ProgressBarDialog;
|
||||||
|
import util.WinkelTripel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An application to make vector oblique aspects of map projections
|
||||||
|
*
|
||||||
|
* @author Justin Kunimune
|
||||||
|
*/
|
||||||
|
public class MapProjections extends Application {
|
||||||
|
|
||||||
|
private static final int CONT_WIDTH = 300;
|
||||||
|
private static final int IMG_WIDTH = 500;
|
||||||
|
|
||||||
|
|
||||||
|
private static final KeyCombination ctrlO = new KeyCodeCombination(KeyCode.O, KeyCodeCombination.CONTROL_DOWN);
|
||||||
|
private static final KeyCombination ctrlS = new KeyCodeCombination(KeyCode.S, KeyCodeCombination.CONTROL_DOWN);
|
||||||
|
|
||||||
|
|
||||||
|
private static final String[] PROJ_ARR = { "Equirectangular", "Mercator", "Gall Stereographic",
|
||||||
|
"Cylindrical Equal-Area", "Polar", "Stereographic", "Azimuthal Equal-Area", "Orthographic", "Gnomonic",
|
||||||
|
"Lambert Conical", "Winkel Tripel", "Van der Grinten", "Mollweide", "Aitoff", "Hammer", "Sinusoidal",
|
||||||
|
"Pierce Quincuncial", "Guyou", "TetraGraph", "Magnifier", "Experimental" };
|
||||||
|
private static final String[] DESC = { "An equidistant cylindrical map", "A conformal cylindrical map",
|
||||||
|
"A compromising cylindrical map", "An equal-area cylindrical map", "An equidistant azimuthal map",
|
||||||
|
"A conformal azimuthal map", "An equal-area azimuthal map",
|
||||||
|
"Represents earth viewed from an infinite distance",
|
||||||
|
"Every straight line on the map is a straight line on the sphere", "A conformal conical map",
|
||||||
|
"The compromise map used by National Geographic (caution: very slow)", "A circular compromise map",
|
||||||
|
"An equal-area map shaped like an ellipse", "An equal-area map shaped like an elipse",
|
||||||
|
"An equal-area map shaped like an elipse", "An equal-area map shaped like a sinusoid",
|
||||||
|
"A conformal square map that uses complex math",
|
||||||
|
"A reorganized version of Pierce Quincuncial and actually the best map ever",
|
||||||
|
"A compromising knockoff of the AuthaGraph projection",
|
||||||
|
"A novelty map that swells the center to disproportionate scale",
|
||||||
|
"What happens when you apply a complex differentiable function to a stereographic projection?" };
|
||||||
|
|
||||||
|
private static final String[] AXES = { "Standard", "Transverse", "Center of Mass", "Jerusalem", "Point Nemo",
|
||||||
|
"Longest Line", "Longest Line Transverse", "Cylindrical", "Conical", "Quincuncial", "Antipode", "Random" };
|
||||||
|
private static final double[] DEF_LATS = { 90, 0, 29.9792, 31.7833, 48.8767, -28.5217, -46.4883, -35, -10, 60 };
|
||||||
|
private static final double[] DEF_LONS = { 0, 0, 31.1344, 35.216, 56.6067, 141.451, 16.5305, -13.6064, 65, -6 };
|
||||||
|
private static final double[] DEF_THTS = { 0, 0, -32, -35, -45, 161.5, 137, 145, -150, -10 };
|
||||||
|
private static final int DEF_MAX_VTX = 1000;
|
||||||
|
|
||||||
|
|
||||||
|
private Stage stage;
|
||||||
|
private FileChooser inputChooser, saver;
|
||||||
|
private Text inputLabel;
|
||||||
|
private Button changeInput;
|
||||||
|
private ComboBox<String> projectionChooser;
|
||||||
|
private Text projectionDesc;
|
||||||
|
private Slider latSlider, lonSlider, thtSlider;
|
||||||
|
private Button update;
|
||||||
|
private Button saveMap;
|
||||||
|
private List<String> format;
|
||||||
|
private List<List<double[]>> input;
|
||||||
|
private double minX, maxX, minY, maxY;
|
||||||
|
private int numVtx;
|
||||||
|
private Canvas viewer;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
launch(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start(Stage primaryStage) {
|
||||||
|
stage = primaryStage;
|
||||||
|
stage.setTitle("Map Designer");
|
||||||
|
|
||||||
|
final VBox layout = new VBox();
|
||||||
|
layout.setSpacing(5);
|
||||||
|
layout.setAlignment(Pos.CENTER);
|
||||||
|
layout.setPrefWidth(CONT_WIDTH);
|
||||||
|
|
||||||
|
Label lbl = new Label("Current input:");
|
||||||
|
inputLabel = new Text("None");
|
||||||
|
layout.getChildren().add(new HBox(3, lbl, inputLabel));
|
||||||
|
|
||||||
|
inputChooser = new FileChooser();
|
||||||
|
inputChooser.setInitialDirectory(new File("input"));
|
||||||
|
inputChooser.setTitle("Choose an input map");
|
||||||
|
inputChooser.getExtensionFilters().addAll(
|
||||||
|
new FileChooser.ExtensionFilter("SVG", "*.svg"));
|
||||||
|
|
||||||
|
changeInput = new Button("Choose input...");
|
||||||
|
changeInput.setOnAction(new EventHandler<ActionEvent>() {
|
||||||
|
public void handle(ActionEvent event) {
|
||||||
|
chooseInput();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
changeInput.setTooltip(new Tooltip(
|
||||||
|
"Change the input image"));
|
||||||
|
stage.addEventHandler(KeyEvent.KEY_RELEASED, new EventHandler<KeyEvent>() { // ctrl-O opens
|
||||||
|
public void handle(KeyEvent event) {
|
||||||
|
if (ctrlO.match(event)) changeInput.fire();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
layout.getChildren().add(changeInput);
|
||||||
|
|
||||||
|
layout.getChildren().add(new Separator());
|
||||||
|
|
||||||
|
lbl = new Label("Projection:");
|
||||||
|
ObservableList<String> items = FXCollections.observableArrayList(PROJ_ARR);
|
||||||
|
projectionChooser = new ComboBox<String>(items);
|
||||||
|
projectionChooser.setOnAction(new EventHandler<ActionEvent>() {
|
||||||
|
public void handle(ActionEvent event) {
|
||||||
|
for (int i = 0; i < PROJ_ARR.length; i ++) {
|
||||||
|
if (PROJ_ARR[i].equals(projectionChooser.getValue())) {
|
||||||
|
projectionDesc.setText(DESC[i]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
projectionChooser.setPrefWidth(210);
|
||||||
|
projectionChooser.setValue(PROJ_ARR[1]);
|
||||||
|
layout.getChildren().add(new HBox(3, lbl, projectionChooser));
|
||||||
|
|
||||||
|
projectionDesc = new Text(DESC[1]);
|
||||||
|
projectionDesc.setWrappingWidth(CONT_WIDTH);
|
||||||
|
layout.getChildren().add(projectionDesc);
|
||||||
|
|
||||||
|
layout.getChildren().add(new Separator());
|
||||||
|
|
||||||
|
final MenuButton defAxes = new MenuButton("Aspect Presets");
|
||||||
|
for (String preset: AXES) {
|
||||||
|
MenuItem m = new MenuItem(preset);
|
||||||
|
m.setOnAction(new EventHandler<ActionEvent>() {
|
||||||
|
public void handle(ActionEvent event) {
|
||||||
|
setAxisByPreset(((MenuItem) event.getSource()).getText());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
defAxes.getItems().add(m);
|
||||||
|
}
|
||||||
|
defAxes.setTooltip(new Tooltip(
|
||||||
|
"Set the aspect sliders based on a preset"));
|
||||||
|
layout.getChildren().add(defAxes);
|
||||||
|
|
||||||
|
latSlider = new Slider(-90, 90, 90);
|
||||||
|
lonSlider = new Slider(-180,180,0);
|
||||||
|
thtSlider = new Slider(-180,180,0);
|
||||||
|
Tooltip aspTlTp = new Tooltip("Change the aspect of the map");
|
||||||
|
latSlider.setTooltip(aspTlTp);
|
||||||
|
lonSlider.setTooltip(aspTlTp);
|
||||||
|
thtSlider.setTooltip(aspTlTp);
|
||||||
|
|
||||||
|
GridPane grid = new GridPane();
|
||||||
|
grid.addRow(0, new Text("Latitude:"), latSlider);
|
||||||
|
grid.addRow(1, new Text("Longitude:"), lonSlider);
|
||||||
|
grid.addRow(2, new Text("Orientation:"), thtSlider);
|
||||||
|
GridPane.setHgrow(latSlider, Priority.ALWAYS);
|
||||||
|
GridPane.setHgrow(lonSlider, Priority.ALWAYS);
|
||||||
|
GridPane.setHgrow(thtSlider, Priority.ALWAYS);
|
||||||
|
//GridPane.setFillWidth(latSlider, Boolean.valueOf(true));
|
||||||
|
layout.getChildren().add(grid);
|
||||||
|
|
||||||
|
layout.getChildren().add(new Separator());
|
||||||
|
|
||||||
|
update = new Button("Update Map");
|
||||||
|
update.setOnAction(new EventHandler<ActionEvent>() {
|
||||||
|
public void handle(ActionEvent event) {
|
||||||
|
updateMap();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
update.setTooltip(new Tooltip(
|
||||||
|
"Update the current map with your parameters."));
|
||||||
|
update.setDefaultButton(true);
|
||||||
|
|
||||||
|
saver = new FileChooser();
|
||||||
|
saver.setInitialDirectory(new File("output"));
|
||||||
|
saver.setInitialFileName("myMap.svg");
|
||||||
|
saver.setTitle("Save Map");
|
||||||
|
saver.getExtensionFilters().addAll(
|
||||||
|
new FileChooser.ExtensionFilter("SVG", "*.svg"));
|
||||||
|
|
||||||
|
saveMap = new Button("Save Map...");
|
||||||
|
saveMap.setOnAction(new EventHandler<ActionEvent>() {
|
||||||
|
public void handle(ActionEvent event) {
|
||||||
|
startFinalizingMap();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
saveMap.setTooltip(new Tooltip("Save the map with current settings."));
|
||||||
|
stage.addEventHandler(KeyEvent.KEY_RELEASED, new EventHandler<KeyEvent>() { // ctrl-S saves
|
||||||
|
public void handle(KeyEvent event) {
|
||||||
|
if (ctrlS.match(event)) saveMap.fire();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
HBox box = new HBox(5, update, saveMap);
|
||||||
|
box.setAlignment(Pos.CENTER);
|
||||||
|
layout.getChildren().add(box);
|
||||||
|
|
||||||
|
viewer = new Canvas(IMG_WIDTH, IMG_WIDTH);
|
||||||
|
//viewer.setFitWidth(IMG_WIDTH);
|
||||||
|
//viewer.setFitHeight(IMG_WIDTH);
|
||||||
|
//viewer.setPreserveRatio(true);
|
||||||
|
|
||||||
|
new Thread(() -> {
|
||||||
|
setInput("basic.svg", "input/basic.svg");
|
||||||
|
update.fire();
|
||||||
|
}).start();
|
||||||
|
|
||||||
|
final HBox gui = new HBox(layout, viewer);
|
||||||
|
gui.setAlignment(Pos.CENTER);
|
||||||
|
gui.setSpacing(10);
|
||||||
|
StackPane.setMargin(gui, new Insets(10));
|
||||||
|
stage.setScene(new Scene(new StackPane(gui)));
|
||||||
|
stage.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void chooseInput() {
|
||||||
|
final File f = inputChooser.showOpenDialog(stage);
|
||||||
|
if (f != null) {
|
||||||
|
new Thread(() -> {
|
||||||
|
setInput(f.getName(), f.getAbsolutePath());
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void setInput(String name, String filename) {
|
||||||
|
changeInput.setDisable(true);
|
||||||
|
update.setDisable(true);
|
||||||
|
saveMap.setDisable(true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
input = loadSVG(filename);
|
||||||
|
inputLabel.setText(name);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
final Alert alert = new Alert(Alert.AlertType.ERROR);//TODO: move this code to raster
|
||||||
|
alert.setHeaderText("Unreadable file!");
|
||||||
|
alert.setContentText("We couldn't read "+filename+". It may be corrupt or an unreadable format.");
|
||||||
|
} catch (IOException e) {
|
||||||
|
final Alert alert = new Alert(Alert.AlertType.ERROR);//TODO: move this code to raster
|
||||||
|
alert.setHeaderText("File not found!");
|
||||||
|
alert.setContentText("Couldn't find "+filename+".");
|
||||||
|
}
|
||||||
|
|
||||||
|
changeInput.setDisable(false);
|
||||||
|
update.setDisable(false);
|
||||||
|
saveMap.setDisable(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private List<List<double[]>> loadSVG(String filename) throws IOException { // this method is just awful.
|
||||||
|
System.out.println("loading "+filename);
|
||||||
|
input = new ArrayList<List<double[]>>();
|
||||||
|
format = new ArrayList<String>();
|
||||||
|
BufferedReader in = new BufferedReader(new FileReader(filename));
|
||||||
|
String formatStuff = "";
|
||||||
|
int c = in.read();
|
||||||
|
|
||||||
|
do {
|
||||||
|
formatStuff += (char)c;
|
||||||
|
if (formatStuff.length() >= 4 &&
|
||||||
|
formatStuff.substring(formatStuff.length()-4).equals("d=\"M")) {
|
||||||
|
format.add(formatStuff);
|
||||||
|
formatStuff = "";
|
||||||
|
List<double[]> currentShape = new ArrayList<double[]>();
|
||||||
|
do {//TODO:figure out where to call readLine (probs at the end of loops)
|
||||||
|
if (c == 'Z') {
|
||||||
|
input.add(currentShape);
|
||||||
|
currentShape = new ArrayList<double[]>();
|
||||||
|
format.add("");
|
||||||
|
c = in.read();
|
||||||
|
}
|
||||||
|
else if (isDigit((char) c)) {
|
||||||
|
String num = Character.toString((char)c);
|
||||||
|
while (isDigit((char) (c = in.read())))
|
||||||
|
num += (char) c;
|
||||||
|
double x = Double.parseDouble(num);
|
||||||
|
if (x < minX) minX = x;
|
||||||
|
if (x > maxX) maxX = x;
|
||||||
|
c = in.read();
|
||||||
|
num = Character.toString((char)c);
|
||||||
|
while (isDigit((char) (c = in.read())))
|
||||||
|
num += (char) c;
|
||||||
|
double y = Double.parseDouble(num);
|
||||||
|
if (y < minY) minY = y;
|
||||||
|
if (y > maxY) maxX = y;
|
||||||
|
currentShape.add(new double[] {x, y});
|
||||||
|
}
|
||||||
|
else if (c == ' ' || c == ',' || c == '\n' ||
|
||||||
|
c == 'M' || c == 'L' || c == 'B') {
|
||||||
|
c = in.read();
|
||||||
|
} //XXX This is jank; I should spiff it up once it works
|
||||||
|
else {
|
||||||
|
System.err.print("I don't know how to interpret "+(char)c);
|
||||||
|
c = in.read();
|
||||||
|
}
|
||||||
|
} while (c != '"');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
c = in.read();
|
||||||
|
}
|
||||||
|
} while (c >= 0);
|
||||||
|
|
||||||
|
format.add(formatStuff);
|
||||||
|
in.close();
|
||||||
|
System.out.println("loaded.");
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void setAxisByPreset(String preset) {
|
||||||
|
if (preset.equals("Antipode")) {
|
||||||
|
latSlider.setValue(-latSlider.getValue());
|
||||||
|
lonSlider.setValue((lonSlider.getValue()+360)%360-180);
|
||||||
|
thtSlider.setValue(-thtSlider.getValue());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (preset.equals("Random")) {
|
||||||
|
latSlider.setValue(Math.toDegrees(Math.asin(Math.random()*2-1)));
|
||||||
|
lonSlider.setValue(Math.random()*360-180);
|
||||||
|
thtSlider.setValue(Math.random()*360-180);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < AXES.length; i ++) {
|
||||||
|
if (AXES[i].equals(preset)) {
|
||||||
|
latSlider.setValue(DEF_LATS[i]);
|
||||||
|
lonSlider.setValue(DEF_LONS[i]);
|
||||||
|
thtSlider.setValue(DEF_THTS[i]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void updateMap() {
|
||||||
|
update.setDisable(true);
|
||||||
|
new Thread(new Task<Void>() {
|
||||||
|
protected Void call() {
|
||||||
|
drawImage(map(), viewer);
|
||||||
|
update.setDisable(false);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void startFinalizingMap() {
|
||||||
|
int p = 0;
|
||||||
|
while (p < PROJ_ARR.length &&
|
||||||
|
!PROJ_ARR[p].equals(projectionChooser.getValue()))
|
||||||
|
p ++;
|
||||||
|
|
||||||
|
ProgressBarDialog pBar = new ProgressBarDialog();
|
||||||
|
pBar.show();
|
||||||
|
new Thread(() -> {
|
||||||
|
List<List<double[]>> map = map(0, pBar);
|
||||||
|
Platform.runLater(() -> saveToSVG(map, pBar));
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void drawImage(List<List<double[]>> img, Canvas c) {
|
||||||
|
GraphicsContext g = c.getGraphicsContext2D();
|
||||||
|
/*for (List<double[]> closedCurve: img) {
|
||||||
|
double[] p0 = null;
|
||||||
|
for (int i = 0; i <= closedCurve.size(); i ++) {
|
||||||
|
double[] p1 = closedCurve.get(i%closedCurve.size());
|
||||||
|
if (p0 != null) {
|
||||||
|
g.lineTo(p1[0], p1[1]);
|
||||||
|
}
|
||||||
|
p0 = p1;
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
g.appendSVGPath("M 50 50 L 150 50 L 100 150 z");
|
||||||
|
g.stroke();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void saveToSVG(List<List<double[]>> curves, ProgressBarDialog pBar) { // call from the main thread!
|
||||||
|
pBar.close();
|
||||||
|
final File f = saver.showSaveDialog(stage);
|
||||||
|
try {
|
||||||
|
BufferedWriter out = new BufferedWriter(new FileWriter(f));
|
||||||
|
|
||||||
|
for (int i = 0; i < curves.size(); i ++) {
|
||||||
|
out.write(format.get(i));
|
||||||
|
String curveCode = "M";
|
||||||
|
for (double[] p: curves.get(i))
|
||||||
|
curveCode += p[0]+" "+p[1]+"L";
|
||||||
|
out.write(curveCode.substring(0,curveCode.length()-2)+"Z");
|
||||||
|
pBar.setProgress((double)i/curves.size());
|
||||||
|
}
|
||||||
|
out.write(format.get(curves.size()));
|
||||||
|
out.close();
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
Alert alert = new Alert(Alert.AlertType.ERROR);
|
||||||
|
alert.setHeaderText("File not found!");
|
||||||
|
alert.setContentText("Could not find "+f+".");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private List<List<double[]>> map() {
|
||||||
|
return map(DEF_MAX_VTX, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<List<double[]>> map(int maxVtx, ProgressBarDialog pbar) {
|
||||||
|
final String proj = projectionChooser.getValue();
|
||||||
|
final double[] pole = {Math.toRadians(latSlider.getValue()),
|
||||||
|
Math.toRadians(lonSlider.getValue()),
|
||||||
|
Math.toRadians(thtSlider.getValue())};
|
||||||
|
int step = maxVtx==0 ? 1 : numVtx/maxVtx+1;
|
||||||
|
List<List<double[]>> output = new LinkedList<List<double[]>>();
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
for (List<double[]> curve0: input) {
|
||||||
|
if (curve0.size() < step*3) continue;
|
||||||
|
|
||||||
|
List<double[]> curve1 = new ArrayList<double[]>(curve0.size()/step);
|
||||||
|
for (int j = 0; j < curve0.size(); j += step) {
|
||||||
|
double[] coords = convCoords(curve0.get(j));
|
||||||
|
curve1.add(project(obliquify(pole, coords), proj));
|
||||||
|
}
|
||||||
|
output.add(curve1);
|
||||||
|
|
||||||
|
if (pbar != null) {
|
||||||
|
i ++;
|
||||||
|
pbar.setProgress((double)i/input.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static double[] project(double[] latLon, String p) {
|
||||||
|
double lat = latLon[0];
|
||||||
|
double lon = latLon[1];
|
||||||
|
if (p.equals("Pierce Quincuncial"))
|
||||||
|
return quincuncial(lat, lon);
|
||||||
|
else if (p.equals("Equirectangular"))
|
||||||
|
return equirectangular(lat, lon);
|
||||||
|
else if (p.equals("Mercator"))
|
||||||
|
return mercator(lat, lon);
|
||||||
|
else if (p.equals("Polar"))
|
||||||
|
return polar(lat, lon);
|
||||||
|
else if (p.equals("Gall Stereographic"))
|
||||||
|
return gall(lat, lon);
|
||||||
|
else if (p.equals("Sinusoidal"))
|
||||||
|
return sinusoidal(lat, lon);
|
||||||
|
else if (p.equals("Stereographic"))
|
||||||
|
return stereographic(lat, lon);
|
||||||
|
else if (p.equals("Gnomonic"))
|
||||||
|
return gnomonic(lat, lon);
|
||||||
|
else if (p.equals("Orthographic"))
|
||||||
|
return orthographic(lat, lon);
|
||||||
|
else if (p.equals("Cylindrical Equal-Area"))
|
||||||
|
return eaCylindrical(lat, lon);
|
||||||
|
else if (p.equals("Lambert Conical"))
|
||||||
|
return lambert(lat, lon);
|
||||||
|
else if (p.equals("Azimuthal Equal-Area"))
|
||||||
|
return eaAzimuth(lat, lon);
|
||||||
|
else if (p.equals("Guyou"))
|
||||||
|
return quinshift(lat, lon);
|
||||||
|
else if (p.equals("Mollweide"))
|
||||||
|
return mollweide(lat, lon);
|
||||||
|
else if (p.equals("Winkel Tripel"))
|
||||||
|
return winkel_tripel(lat, lon);
|
||||||
|
else if (p.equals("Van der Grinten"))
|
||||||
|
return grinten(lat, lon);
|
||||||
|
else if (p.equals("Magnifier"))
|
||||||
|
return magnus(lat, lon);
|
||||||
|
else if (p.equals("Aitoff"))
|
||||||
|
return aitoff(lat, lon);
|
||||||
|
else if (p.equals("Hammer"))
|
||||||
|
return hammer(lat, lon);
|
||||||
|
//else if (p.equals("TetraGraph"))
|
||||||
|
// return tetragraph(lat, lon);
|
||||||
|
else if (p.equals("Experimental"))
|
||||||
|
return experiment(lat, lon);
|
||||||
|
else
|
||||||
|
throw new IllegalArgumentException(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private double[] convCoords(double[] coords) { // changes svg coordinates to radians
|
||||||
|
final double NORTHMOST = 1.460;
|
||||||
|
final double SOUTHMOST = -1.475;
|
||||||
|
final double EASTMOST = -Math.PI;
|
||||||
|
final double WESTMOST = Math.PI;
|
||||||
|
return new double[] {linMap(coords[1], minY,maxY, SOUTHMOST,NORTHMOST),
|
||||||
|
linMap(coords[0], minX,maxX, EASTMOST,WESTMOST)};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static final double[] obliquify(double[] pole, double[] coords) {
|
||||||
|
final double lat0 = pole[0];
|
||||||
|
final double lon0 = pole[1];
|
||||||
|
final double tht0 = pole[2];
|
||||||
|
double lat1 = coords[0];
|
||||||
|
double lon1 = coords[1];
|
||||||
|
lon1 += tht0;
|
||||||
|
double latf = Math.asin(Math.sin(lat0)*Math.sin(lat1) - Math.cos(lat0)*Math.cos(lon1)*Math.cos(lat1));
|
||||||
|
double lonf;
|
||||||
|
double innerFunc = Math.sin(lat1)/Math.cos(lat0)/Math.cos(latf) - Math.tan(lat0)*Math.tan(latf);
|
||||||
|
if (lat0 == Math.PI / 2) // accounts for special case when lat0 = pi/2
|
||||||
|
lonf = lon1+lon0;
|
||||||
|
else if (lat0 == -Math.PI / 2) // accounts for special case when lat0 = -pi/2
|
||||||
|
lonf = -lon1+lon0 + Math.PI;
|
||||||
|
else if (Math.abs(innerFunc) > 1) { // accounts for special case when cos(lat1) = --> 0
|
||||||
|
if ((lon1 == 0 && lat1 < -lat0) || (lon1 != 0 && lat1 < lat0))
|
||||||
|
lonf = lon0 + Math.PI;
|
||||||
|
else
|
||||||
|
lonf = lon0;
|
||||||
|
}
|
||||||
|
else if (Math.sin(lon1) > 0)
|
||||||
|
lonf = lon0 +
|
||||||
|
Math.acos(innerFunc);
|
||||||
|
else
|
||||||
|
lonf = lon0 -
|
||||||
|
Math.acos(innerFunc);
|
||||||
|
|
||||||
|
double thtf = 0;
|
||||||
|
if (pole.length >= 3)
|
||||||
|
thtf += pole[2];
|
||||||
|
|
||||||
|
double[] output = {latf, lonf, thtf};
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static double[] quincuncial(double lat, double lon) { // a tessalatable square map
|
||||||
|
double[] output = new double[2];
|
||||||
|
|
||||||
|
final double wMag = Math.tan(lat/2+Math.PI/4);
|
||||||
|
final Complex w = new Complex(wMag*Math.cos(lon), wMag*Math.sin(lon));
|
||||||
|
final Complex k = new Complex(Math.sqrt(0.5));
|
||||||
|
Complex z = F(w.acos(),k);
|
||||||
|
if (z.isInfinite() || z.isNaN() || z.abs() > 10)
|
||||||
|
z = new Complex(0);
|
||||||
|
|
||||||
|
output[0] = -z.getReal();
|
||||||
|
output[1] = z.getImaginary();
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double[] experiment(double lat, double lon) { // just some random complex plane stuff
|
||||||
|
double[] output = new double[2];
|
||||||
|
|
||||||
|
final double p = Math.tan(lat/2+Math.PI/4);
|
||||||
|
final Complex w = new Complex(p*Math.cos(lon), p*Math.sin(lon));
|
||||||
|
Complex z = w;
|
||||||
|
if (z.isInfinite() || z.isNaN() || z.abs() > 10)
|
||||||
|
z = new Complex(0);
|
||||||
|
|
||||||
|
output[0] = -z.getReal();
|
||||||
|
output[1] = z.getImaginary();
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double[] equirectangular(double lat, double lon) { // a linear scale
|
||||||
|
return new double[] {lon, lat};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double[] mercator(double lat, double lon) { // a popular shape-preserving map
|
||||||
|
return new double[] {lon, Math.sinh(Math.tan(lat))};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double[] polar(double lat, double lon) { // the projection used on the UN flag
|
||||||
|
final double r = Math.PI/2 - lat;
|
||||||
|
return new double[] {r*Math.cos(lon), r*Math.sin(lon)};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double[] gall(double lat, double lon) { // a compromise map, similar to mercator
|
||||||
|
return new double[] {lon, Math.tan(lat/2)};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double[] sinusoidal(double lat, double lon) { // a map shaped like a sinusoid
|
||||||
|
return new double[] {Math.cos(lat)*lon, lat};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double[] stereographic(double lat, double lon) { // a shape-preserving infinite map
|
||||||
|
final double r = 1/Math.tan(lat/2 + Math.PI/4);
|
||||||
|
return new double[] {r*Math.cos(lon), r*Math.sin(lon)};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double[] gnomonic(double lat, double lon) { // map where straight lines are straight
|
||||||
|
final double r = Math.tan(Math.PI/4 - lat);
|
||||||
|
return new double[] {r*Math.cos(lon), r*Math.sin(lon)};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double[] orthographic(double lat, double lon) { // a map that mimics the view from space
|
||||||
|
final double r = Math.cos(lat);
|
||||||
|
return new double[] {r*Math.cos(lon), r*Math.sin(lon)};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double[] eaCylindrical(double lat, double lon) { // an equal-area cylindrical map
|
||||||
|
return new double[] {lon, Math.sin(lat)};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double[] lambert(double lat, double lon) { // a conical projection
|
||||||
|
final double r = 1/Math.pow(Math.tan(lat/2 + Math.PI/4), 2);
|
||||||
|
return new double[] {r*Math.cos(2*lon), r*Math.sin(2*lon)};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double[] eaAzimuth(double lat, double lon) { // the lambert azimuthal equal area projection
|
||||||
|
final double r = Math.cos(lat/2);
|
||||||
|
return new double[] {r*Math.cos(lon), r*Math.sin(lon)};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double[] quinshift(double lat, double lon) { // a tessalatable rectangle map
|
||||||
|
double[] output = new double[2];
|
||||||
|
|
||||||
|
if (lat >= 0) {
|
||||||
|
final double wMag = Math.tan(-lat/2+Math.PI/4);
|
||||||
|
final Complex w = new Complex(wMag*Math.cos(lon), wMag*Math.sin(lon));
|
||||||
|
final Complex k = new Complex(Math.sqrt(0.5));
|
||||||
|
Complex z = F(w.acos(),k);
|
||||||
|
|
||||||
|
output[0] = -z.getImaginary()*1000;
|
||||||
|
output[1] = z.getReal()*1000;
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
final double wMag = Math.tan(lat/2+Math.PI/4);
|
||||||
|
final Complex w = new Complex(wMag*Math.cos(lon), wMag*Math.sin(lon));
|
||||||
|
final Complex k = new Complex(Math.sqrt(0.5));
|
||||||
|
Complex z = F(w.acos(),k);
|
||||||
|
|
||||||
|
output[0] = -z.getReal();
|
||||||
|
output[1] = z.getImaginary();
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double[] mollweide(double lat, double lon) {
|
||||||
|
double tht = lat;
|
||||||
|
for (int i = 0; i < 10; i ++)
|
||||||
|
tht -= (2*tht+Math.sin(2*tht)-Math.PI*Math.sin(lat))/
|
||||||
|
(2+2*Math.cos(2*tht));
|
||||||
|
return new double[] {
|
||||||
|
2/Math.PI*lon*Math.cos(tht),
|
||||||
|
Math.sin(tht)};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double[] winkel_tripel(double lat, double lon) {
|
||||||
|
return new double[] {WinkelTripel.X(lat,lon), WinkelTripel.Y(lat,lon)};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double[] grinten(double lat, double lon) {
|
||||||
|
final double t = Math.asin(Math.abs(2*lat/Math.PI));
|
||||||
|
|
||||||
|
if (lat == 0) // special case 1: equator
|
||||||
|
return new double[] {lon, 0};
|
||||||
|
if (lon == 0 || lat >= Math.PI/2 || lat <= -Math.PI/2) // special case 3: meridian
|
||||||
|
return new double[] {0, Math.signum(lat)*Math.PI*Math.tan(t/2)};
|
||||||
|
|
||||||
|
final double A = Math.abs(Math.PI/lon - lon/Math.PI)/2;
|
||||||
|
final double G = Math.cos(t)/(Math.sin(t)+Math.cos(t)-1);
|
||||||
|
final double P = G*(2/Math.sin(t) - 1);
|
||||||
|
final double Q = A*A + G;
|
||||||
|
|
||||||
|
return new double[] {
|
||||||
|
Math.signum(lon)*(A*(G-P*P)+Math.sqrt(A*A*(G-P*P)*(G-P*P)-(P*P+A*A)*(G*G-P*P)))/(P*P+A*A),
|
||||||
|
Math.signum(lat)*(P*Q-A*Math.sqrt((A*A+1)*(P*P+A*A)-Q*Q))/(P*P+A*A)};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double[] magnus(double lat, double lon) { // a novelty map that magnifies the center profusely
|
||||||
|
final double p = .5 - lat/Math.PI;
|
||||||
|
final double r = 0.7 + 0.3*p - Math.pow(1-p,5);
|
||||||
|
return new double[] {r*Math.cos(lon), r*Math.sin(lon)};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double[] hammer(double lat, double lon) { // similar to Mollweide, but moves distortion from the poles to the edges
|
||||||
|
return new double[] {
|
||||||
|
2*Math.cos(lat)*Math.sin(lon/2)/Math.sqrt(1+Math.cos(lat)*Math.cos(lon/2)),
|
||||||
|
Math.sin(lat)/Math.sqrt(1+Math.cos(lat)*Math.cos(lon/2))};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double[] aitoff(double lat, double lon) { // similar to Mollweide, but moves distortion from the poles to the edges
|
||||||
|
final double a = Math.acos(Math.cos(lat)*Math.cos(lon/2));
|
||||||
|
return new double[] {
|
||||||
|
2*Math.cos(lat)*Math.sin(lon/2)*a/Math.sin(a),
|
||||||
|
Math.sin(lat)*a/Math.sin(a)};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*private static double[] tetragraph(double lat, double lon) { // a tetrahedral compromise
|
||||||
|
final double[] faceCenter = new double[3];
|
||||||
|
final double rot, localX, localY;
|
||||||
|
if (y < x-1) {
|
||||||
|
faceCenter[0] = -Math.PI/2;
|
||||||
|
faceCenter[1] = 0;
|
||||||
|
rot = -Math.PI/2;
|
||||||
|
localX = Math.sqrt(3)*(x-2/3.0);
|
||||||
|
localY = y+1;
|
||||||
|
}
|
||||||
|
else if (y < -x-1) {
|
||||||
|
faceCenter[0] = -Math.PI/2;
|
||||||
|
faceCenter[1] = 0;
|
||||||
|
rot = Math.PI/2;
|
||||||
|
localX = Math.sqrt(3)*(x+2/3.0);
|
||||||
|
localY = y+1;
|
||||||
|
}
|
||||||
|
else if (y > -x+1) {
|
||||||
|
faceCenter[0] = Math.PI/2-Math.asin(Math.sqrt(8)/3);
|
||||||
|
faceCenter[1] = Math.PI;
|
||||||
|
rot = -Math.PI/2;
|
||||||
|
localX = Math.sqrt(3)*(x-2/3.0);
|
||||||
|
localY = y-1;
|
||||||
|
}
|
||||||
|
else if (y > x+1) {
|
||||||
|
faceCenter[0] = Math.PI/2-Math.asin(Math.sqrt(8)/3);
|
||||||
|
faceCenter[1] = Math.PI;
|
||||||
|
rot = Math.PI/2;
|
||||||
|
localX = Math.sqrt(3)*(x+2/3.0);
|
||||||
|
localY = y-1;
|
||||||
|
}
|
||||||
|
else if (x < 0) {
|
||||||
|
faceCenter[0] = Math.PI/2-Math.asin(Math.sqrt(8)/3);
|
||||||
|
faceCenter[1] = -Math.PI/3;
|
||||||
|
rot = Math.PI/6;
|
||||||
|
localX = Math.sqrt(3)*(x+1/3.0);
|
||||||
|
localY = y;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
faceCenter[0] = Math.PI/2-Math.asin(Math.sqrt(8)/3);
|
||||||
|
faceCenter[1] = Math.PI/3;
|
||||||
|
rot = -Math.PI/6;
|
||||||
|
localX = Math.sqrt(3)*(x-1/3.0);
|
||||||
|
localY = y;
|
||||||
|
}
|
||||||
|
faceCenter[2] = 0;
|
||||||
|
|
||||||
|
final double t = Math.atan2(localY, localX) + rot;
|
||||||
|
final double t0 = Math.floor((t+Math.PI/2)/(2*Math.PI/3)+0.5)*(2*Math.PI/3) - Math.PI/2;
|
||||||
|
final double dt = t-t0;
|
||||||
|
|
||||||
|
double[] triCoords = {
|
||||||
|
Math.PI/2 - Math.atan(Math.tan(1.654*Math.hypot(localX, localY)*Math.cos(dt))/Math.cos(dt)),
|
||||||
|
Math.PI/2 + t0 + dt};
|
||||||
|
return obliquify(faceCenter, triCoords);
|
||||||
|
}*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static final int blend(int[] colors) {
|
||||||
|
int a_tot = 0;
|
||||||
|
int r_tot = 0;
|
||||||
|
int g_tot = 0;
|
||||||
|
int b_tot = 0;
|
||||||
|
for (int argb: colors) {
|
||||||
|
double a = ((argb >> 24) & 0xFF);
|
||||||
|
a_tot += a;
|
||||||
|
r_tot += a*((argb >> 16) & 0xFF);
|
||||||
|
g_tot += a*((argb >> 8) & 0xFF);
|
||||||
|
b_tot += a*((argb >> 0) & 0xFF);
|
||||||
|
}
|
||||||
|
if (a_tot == 0) return 0;
|
||||||
|
else
|
||||||
|
return (a_tot/colors.length<<24) +
|
||||||
|
(r_tot/a_tot<<16) + (g_tot/a_tot<<8) + (b_tot/a_tot);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static final Complex F(Complex phi, final Complex k) { // series solution to incomplete elliptic integral of the first kind
|
||||||
|
Complex sum = new Complex(0);
|
||||||
|
Complex i_n = phi;
|
||||||
|
|
||||||
|
for (int n = 0; n < 100; n++) {
|
||||||
|
if (n > 0)
|
||||||
|
i_n = i_n.multiply((2.0 * n - 1) / (2.0 * n))
|
||||||
|
.subtract(phi.cos().multiply(phi.sin().pow(2.0 * n - 1)).divide(2.0 * n));
|
||||||
|
sum = sum.add(i_n.multiply(Math.abs(combine(-.5, n))).multiply(k.pow(2.0 * n)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static final double combine(double n, int k) {
|
||||||
|
double output = 1;
|
||||||
|
for (int i = k; i > 0; i --) {
|
||||||
|
output *= (n+i-k)/i;
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static final double linMap(double x, double a0, double a1, double b0, double b1) {
|
||||||
|
return (x-a0)*(b1-b0)/(a1-a0) + b0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static final boolean isDigit(char c) {
|
||||||
|
return c >= '-' && c <= '9';
|
||||||
|
}
|
||||||
|
}
|
||||||
68
src/vectormaps/ProgressBarDialog.java
Normal file
68
src/vectormaps/ProgressBarDialog.java
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
package vectormaps;
|
||||||
|
import javafx.application.Platform;
|
||||||
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.scene.control.Button;
|
||||||
|
import javafx.scene.control.ButtonType;
|
||||||
|
import javafx.scene.control.Dialog;
|
||||||
|
import javafx.scene.control.DialogPane;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.control.ProgressBar;
|
||||||
|
import javafx.scene.control.Tooltip;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
|
import javafx.scene.layout.Priority;
|
||||||
|
|
||||||
|
public class ProgressBarDialog extends Dialog<Void> {
|
||||||
|
|
||||||
|
public final double DEF_SIZE = 1000;
|
||||||
|
|
||||||
|
|
||||||
|
private final HBox box;
|
||||||
|
private final ProgressBar bar;
|
||||||
|
private final Label words;
|
||||||
|
|
||||||
|
public ProgressBarDialog() {
|
||||||
|
DialogPane pane = this.getDialogPane();
|
||||||
|
|
||||||
|
this.bar = new ProgressBar();
|
||||||
|
this.bar.setPrefWidth(300);
|
||||||
|
this.bar.setTooltip(new Tooltip("Oh, did you want a cancel button? Well, TOO BAD."));
|
||||||
|
|
||||||
|
this.words = new Label();
|
||||||
|
this.words.setMinWidth(40);
|
||||||
|
this.words.setAlignment(Pos.BASELINE_RIGHT);
|
||||||
|
|
||||||
|
this.box = new HBox(5);
|
||||||
|
|
||||||
|
pane.contentTextProperty().addListener((arg0) -> { // set it to refresh the gui when... the content texts?
|
||||||
|
this.resetBar();
|
||||||
|
});
|
||||||
|
this.setTitle(""); // set the words on it
|
||||||
|
pane.setHeaderText("Please wait.");
|
||||||
|
pane.getButtonTypes().addAll(new ButtonType[] { ButtonType.CLOSE });
|
||||||
|
((Button) pane.lookupButton(ButtonType.CLOSE)).setText("Run in background"); // you can't close it
|
||||||
|
this.resetBar();
|
||||||
|
|
||||||
|
this.setResultConverter((btn) -> { // set the return value
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void resetBar() {
|
||||||
|
this.bar.setProgress(0);
|
||||||
|
this.words.setText("0.0%");
|
||||||
|
this.box.getChildren().clear();
|
||||||
|
this.box.getChildren().add(this.bar);
|
||||||
|
this.box.getChildren().add(this.words);
|
||||||
|
HBox.setHgrow(this.bar, Priority.ALWAYS);
|
||||||
|
this.getDialogPane().setContent(this.box);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void setProgress(double p) {
|
||||||
|
this.bar.setProgress(p);
|
||||||
|
Platform.runLater(() ->
|
||||||
|
this.words.setText((Math.round(1000*p)/10.0)+"%"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user