(gui) - added location/background (rest) - added location
This commit is contained in:
parent
b6cc2ad86e
commit
28f14a9b66
13 changed files with 417 additions and 1 deletions
|
@ -77,6 +77,8 @@ public class Controller implements Initializable {
|
|||
private MobilityDialog mobilityDialog = new MobilityDialog(this);
|
||||
private ChartDialog chartDialog = new ChartDialog(this);
|
||||
private NodeTypesDialog nodeTypesDialog = new NodeTypesDialog(this);
|
||||
private BackgroundDialog backgroundDialog = new BackgroundDialog(this);
|
||||
private LocationDialog locationDialog = new LocationDialog(this);
|
||||
|
||||
public Controller() {
|
||||
// load configuration
|
||||
|
@ -140,6 +142,8 @@ public class Controller implements Initializable {
|
|||
configDialog.setOwner(window);
|
||||
mobilityDialog.setOwner(window);
|
||||
nodeTypesDialog.setOwner(window);
|
||||
backgroundDialog.setOwner(window);
|
||||
locationDialog.setOwner(window);
|
||||
}
|
||||
|
||||
@FXML
|
||||
|
@ -147,6 +151,16 @@ public class Controller implements Initializable {
|
|||
nodeTypesDialog.showDialog();
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void onOptionsMenuBackground(ActionEvent event) {
|
||||
backgroundDialog.showDialog();
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void onOptionsMenuLocation(ActionEvent event) {
|
||||
locationDialog.showDialog();
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void onHelpMenuWebsite(ActionEvent event) {
|
||||
application.getHostServices().showDocument("https://github.com/coreemu/core");
|
||||
|
|
|
@ -79,4 +79,8 @@ public interface ICoreClient {
|
|||
MobilityConfig getMobilityConfig(CoreNode node) throws IOException;
|
||||
|
||||
boolean mobilityAction(CoreNode node, String action) throws IOException;
|
||||
|
||||
LocationConfig getLocationConfig() throws IOException;
|
||||
|
||||
boolean setLocationConfig(LocationConfig config) throws IOException;
|
||||
}
|
||||
|
|
|
@ -309,6 +309,18 @@ public class CoreRestClient implements ICoreClient {
|
|||
return WebUtils.putJson(url, config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocationConfig getLocationConfig() throws IOException {
|
||||
String url = getUrl(String.format("sessions/%s/location", sessionId));
|
||||
return WebUtils.getJson(url, LocationConfig.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setLocationConfig(LocationConfig config) throws IOException {
|
||||
String url = getUrl(String.format("sessions/%s/location", sessionId));
|
||||
return WebUtils.putJson(url, config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean createNode(CoreNode node) throws IOException {
|
||||
String url = getUrl(String.format("sessions/%s/nodes", sessionId));
|
||||
|
|
10
corefx/src/main/java/com/core/data/Location.java
Normal file
10
corefx/src/main/java/com/core/data/Location.java
Normal file
|
@ -0,0 +1,10 @@
|
|||
package com.core.data;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class Location {
|
||||
private Double latitude = 0.0;
|
||||
private Double longitude = 0.0;
|
||||
private Double altitude = 0.0;
|
||||
}
|
10
corefx/src/main/java/com/core/data/LocationConfig.java
Normal file
10
corefx/src/main/java/com/core/data/LocationConfig.java
Normal file
|
@ -0,0 +1,10 @@
|
|||
package com.core.data;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class LocationConfig {
|
||||
private Position position = new Position();
|
||||
private Location location = new Location();
|
||||
private Double scale;
|
||||
}
|
51
corefx/src/main/java/com/core/graph/BackgroundPaintable.java
Normal file
51
corefx/src/main/java/com/core/graph/BackgroundPaintable.java
Normal file
|
@ -0,0 +1,51 @@
|
|||
package com.core.graph;
|
||||
|
||||
import edu.uci.ics.jung.visualization.Layer;
|
||||
import edu.uci.ics.jung.visualization.VisualizationViewer;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
public class BackgroundPaintable<V, E> implements VisualizationViewer.Paintable {
|
||||
private final ImageIcon imageIcon;
|
||||
private final VisualizationViewer<V, E> vv;
|
||||
private final String imagePath;
|
||||
|
||||
public BackgroundPaintable(String imagePath, VisualizationViewer<V, E> vv) throws IOException {
|
||||
this.imagePath = imagePath;
|
||||
Image image = ImageIO.read(Paths.get(imagePath).toFile());
|
||||
imageIcon = new ImageIcon(image);
|
||||
this.vv = vv;
|
||||
}
|
||||
|
||||
public String getImage() {
|
||||
return imagePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paint(Graphics g) {
|
||||
Graphics2D g2d = (Graphics2D) g;
|
||||
AffineTransform oldXform = g2d.getTransform();
|
||||
AffineTransform lat =
|
||||
vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT).getTransform();
|
||||
AffineTransform vat =
|
||||
vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW).getTransform();
|
||||
AffineTransform at = new AffineTransform();
|
||||
at.concatenate(g2d.getTransform());
|
||||
at.concatenate(vat);
|
||||
at.concatenate(lat);
|
||||
g2d.setTransform(at);
|
||||
g.drawImage(imageIcon.getImage(), 0, 0,
|
||||
imageIcon.getIconWidth(), imageIcon.getIconHeight(), vv);
|
||||
g2d.setTransform(oldXform);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useTransform() {
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -55,6 +55,7 @@ public class NetworkGraph {
|
|||
private Supplier<CoreLink> linkFactory = () -> new CoreLink(linkId++);
|
||||
private CorePopupGraphMousePlugin customPopupPlugin;
|
||||
private CoreAnnotatingGraphMousePlugin<CoreNode, CoreLink> customAnnotatingPlugin;
|
||||
private BackgroundPaintable<CoreNode, CoreLink> backgroundPaintable;
|
||||
|
||||
public NetworkGraph(Controller controller) {
|
||||
this.controller = controller;
|
||||
|
@ -164,6 +165,24 @@ public class NetworkGraph {
|
|||
});
|
||||
}
|
||||
|
||||
public void setBackground(String imagePath) {
|
||||
try {
|
||||
backgroundPaintable = new BackgroundPaintable<>(imagePath, graphViewer);
|
||||
graphViewer.addPreRenderPaintable(backgroundPaintable);
|
||||
graphViewer.repaint();
|
||||
} catch (IOException ex) {
|
||||
logger.error("error setting background", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeBackground() {
|
||||
if (backgroundPaintable != null) {
|
||||
graphViewer.removePreRenderPaintable(backgroundPaintable);
|
||||
graphViewer.repaint();
|
||||
backgroundPaintable = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void setMode(ModalGraphMouse.Mode mode) {
|
||||
graphMouse.setMode(mode);
|
||||
}
|
||||
|
|
86
corefx/src/main/java/com/core/ui/BackgroundDialog.java
Normal file
86
corefx/src/main/java/com/core/ui/BackgroundDialog.java
Normal file
|
@ -0,0 +1,86 @@
|
|||
package com.core.ui;
|
||||
|
||||
import com.core.Controller;
|
||||
import com.core.data.CoreLink;
|
||||
import com.core.data.CoreNode;
|
||||
import com.core.graph.BackgroundPaintable;
|
||||
import com.jfoenix.controls.JFXButton;
|
||||
import com.jfoenix.controls.JFXTextField;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.stage.FileChooser;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
public class BackgroundDialog extends StageDialog {
|
||||
private static final Logger logger = LogManager.getLogger();
|
||||
|
||||
@FXML
|
||||
private ImageView imageView;
|
||||
|
||||
@FXML
|
||||
private JFXTextField fileTextField;
|
||||
|
||||
@FXML
|
||||
private JFXButton fileButton;
|
||||
|
||||
private JFXButton saveButton;
|
||||
private JFXButton clearButton;
|
||||
|
||||
|
||||
public BackgroundDialog(Controller controller) {
|
||||
super(controller, "/fxml/background_dialog.fxml");
|
||||
setTitle("Background Configuration");
|
||||
saveButton = createButton("Save");
|
||||
saveButton.setOnAction(event -> {
|
||||
controller.getNetworkGraph().setBackground(fileTextField.getText());
|
||||
close();
|
||||
});
|
||||
clearButton = createButton("Clear");
|
||||
clearButton.setOnAction(event -> {
|
||||
controller.getNetworkGraph().removeBackground();
|
||||
close();
|
||||
});
|
||||
addCancelButton();
|
||||
|
||||
HBox parent = (HBox) imageView.getParent();
|
||||
imageView.fitHeightProperty().bind(parent.heightProperty());
|
||||
|
||||
fileButton.setOnAction(event -> {
|
||||
FileChooser fileChooser = new FileChooser();
|
||||
fileChooser.setTitle("Select Background");
|
||||
fileChooser.setInitialDirectory(new File(System.getProperty("user.home")));
|
||||
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("PNG", "*.png"));
|
||||
File file = fileChooser.showOpenDialog(getStage());
|
||||
if (file != null) {
|
||||
String uri = file.toURI().toString();
|
||||
imageView.setImage(new Image(uri));
|
||||
fileTextField.setText(file.getPath());
|
||||
saveButton.setDisable(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void showDialog() {
|
||||
BackgroundPaintable<CoreNode, CoreLink> backgroundPaintable = getController().getNetworkGraph()
|
||||
.getBackgroundPaintable();
|
||||
saveButton.setDisable(true);
|
||||
fileTextField.setText(null);
|
||||
imageView.setImage(null);
|
||||
if (backgroundPaintable == null) {
|
||||
clearButton.setDisable(true);
|
||||
} else {
|
||||
String imagePath = backgroundPaintable.getImage();
|
||||
fileTextField.setText(imagePath);
|
||||
imageView.setImage(new Image(Paths.get(imagePath).toUri().toString()));
|
||||
clearButton.setDisable(false);
|
||||
}
|
||||
|
||||
show();
|
||||
}
|
||||
}
|
85
corefx/src/main/java/com/core/ui/LocationDialog.java
Normal file
85
corefx/src/main/java/com/core/ui/LocationDialog.java
Normal file
|
@ -0,0 +1,85 @@
|
|||
package com.core.ui;
|
||||
|
||||
import com.core.Controller;
|
||||
import com.core.data.LocationConfig;
|
||||
import com.jfoenix.controls.JFXButton;
|
||||
import com.jfoenix.controls.JFXTextField;
|
||||
import com.jfoenix.validation.DoubleValidator;
|
||||
import javafx.fxml.FXML;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class LocationDialog extends StageDialog {
|
||||
private static final Logger logger = LogManager.getLogger();
|
||||
|
||||
@FXML
|
||||
private JFXTextField scaleTextField;
|
||||
|
||||
@FXML
|
||||
private JFXTextField xTextField;
|
||||
|
||||
@FXML
|
||||
private JFXTextField yTextField;
|
||||
|
||||
@FXML
|
||||
private JFXTextField latTextField;
|
||||
|
||||
@FXML
|
||||
private JFXTextField lonTextField;
|
||||
|
||||
@FXML
|
||||
private JFXTextField altTextField;
|
||||
|
||||
private JFXButton saveButton;
|
||||
|
||||
public LocationDialog(Controller controller) {
|
||||
super(controller, "/fxml/location_dialog.fxml");
|
||||
setTitle("Location Configuration");
|
||||
saveButton = createButton("Save");
|
||||
saveButton.setOnAction(event -> {
|
||||
boolean result = scaleTextField.validate();
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
|
||||
LocationConfig config = new LocationConfig();
|
||||
config.setScale(getDouble(scaleTextField));
|
||||
config.getPosition().setX(getDouble(xTextField));
|
||||
config.getPosition().setY(getDouble(yTextField));
|
||||
config.getLocation().setLatitude(getDouble(latTextField));
|
||||
config.getLocation().setLongitude(getDouble(lonTextField));
|
||||
config.getLocation().setAltitude(getDouble(altTextField));
|
||||
try {
|
||||
getCoreClient().setLocationConfig(config);
|
||||
close();
|
||||
} catch (IOException ex) {
|
||||
Toast.error("error setting location config", ex);
|
||||
}
|
||||
});
|
||||
addCancelButton();
|
||||
|
||||
DoubleValidator validator = new DoubleValidator();
|
||||
scaleTextField.getValidators().add(validator);
|
||||
}
|
||||
|
||||
public Double getDouble(JFXTextField textField) {
|
||||
return Double.parseDouble(textField.getText());
|
||||
}
|
||||
|
||||
public void showDialog() {
|
||||
try {
|
||||
LocationConfig config = getCoreClient().getLocationConfig();
|
||||
scaleTextField.setText(config.getScale().toString());
|
||||
xTextField.setText(config.getPosition().getX().toString());
|
||||
yTextField.setText(config.getPosition().getY().toString());
|
||||
latTextField.setText(config.getLocation().getLatitude().toString());
|
||||
lonTextField.setText(config.getLocation().getLongitude().toString());
|
||||
altTextField.setText(config.getLocation().getAltitude().toString());
|
||||
show();
|
||||
} catch (IOException ex) {
|
||||
Toast.error("error getting location config", ex);
|
||||
}
|
||||
}
|
||||
}
|
35
corefx/src/main/resources/fxml/background_dialog.fxml
Normal file
35
corefx/src/main/resources/fxml/background_dialog.fxml
Normal file
|
@ -0,0 +1,35 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import com.jfoenix.controls.JFXButton?>
|
||||
<?import com.jfoenix.controls.JFXTextField?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.image.ImageView?>
|
||||
<?import javafx.scene.layout.ColumnConstraints?>
|
||||
<?import javafx.scene.layout.GridPane?>
|
||||
<?import javafx.scene.layout.HBox?>
|
||||
<?import javafx.scene.layout.RowConstraints?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
|
||||
<VBox maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" spacing="10.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1">
|
||||
<children>
|
||||
<Label text="Image File" />
|
||||
<GridPane hgap="10.0" vgap="10.0">
|
||||
<columnConstraints>
|
||||
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" percentWidth="15.0" prefWidth="100.0" />
|
||||
</columnConstraints>
|
||||
<rowConstraints>
|
||||
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||
</rowConstraints>
|
||||
<children>
|
||||
<JFXTextField fx:id="fileTextField" disable="true" />
|
||||
<JFXButton fx:id="fileButton" maxWidth="1.7976931348623157E308" styleClass="core-button" text="File" GridPane.columnIndex="1" />
|
||||
</children>
|
||||
</GridPane>
|
||||
<HBox alignment="CENTER" VBox.vgrow="ALWAYS">
|
||||
<children>
|
||||
<ImageView fx:id="imageView" fitHeight="150.0" fitWidth="654.0" pickOnBounds="true" preserveRatio="true" />
|
||||
</children>
|
||||
</HBox>
|
||||
</children>
|
||||
</VBox>
|
56
corefx/src/main/resources/fxml/location_dialog.fxml
Normal file
56
corefx/src/main/resources/fxml/location_dialog.fxml
Normal file
|
@ -0,0 +1,56 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import com.jfoenix.controls.JFXTextField?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.layout.ColumnConstraints?>
|
||||
<?import javafx.scene.layout.GridPane?>
|
||||
<?import javafx.scene.layout.RowConstraints?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
|
||||
|
||||
<VBox maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" spacing="10.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1">
|
||||
<children>
|
||||
<Label text="Scale (100 pixels / Unit)" />
|
||||
<JFXTextField fx:id="scaleTextField" />
|
||||
<Label text="Reference Point" />
|
||||
<GridPane hgap="10.0" vgap="10.0">
|
||||
<columnConstraints>
|
||||
<ColumnConstraints minWidth="10.0" />
|
||||
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||
<ColumnConstraints minWidth="10.0" />
|
||||
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||
</columnConstraints>
|
||||
<rowConstraints>
|
||||
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||
</rowConstraints>
|
||||
<children>
|
||||
<Label text="X" GridPane.halignment="CENTER" />
|
||||
<Label text="Y" GridPane.columnIndex="2" GridPane.halignment="CENTER" />
|
||||
<JFXTextField GridPane.columnIndex="1" fx:id="xTextField" />
|
||||
<JFXTextField fx:id="yTextField" GridPane.columnIndex="3" />
|
||||
</children>
|
||||
</GridPane>
|
||||
<Label text="Reference Location (Maps to Point)" />
|
||||
<GridPane hgap="10.0" vgap="10.0">
|
||||
<columnConstraints>
|
||||
<ColumnConstraints minWidth="10.0" />
|
||||
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||
<ColumnConstraints minWidth="10.0" />
|
||||
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||
<ColumnConstraints minWidth="10.0" />
|
||||
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||
</columnConstraints>
|
||||
<rowConstraints>
|
||||
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||
</rowConstraints>
|
||||
<children>
|
||||
<Label text="Latitude" GridPane.halignment="CENTER" />
|
||||
<Label text="Longitude" GridPane.columnIndex="2" GridPane.halignment="CENTER" />
|
||||
<Label text="Altitude (meters)" GridPane.columnIndex="4" GridPane.halignment="CENTER" />
|
||||
<JFXTextField fx:id="latTextField" GridPane.columnIndex="1" />
|
||||
<JFXTextField fx:id="lonTextField" GridPane.columnIndex="3" />
|
||||
<JFXTextField fx:id="altTextField" GridPane.columnIndex="5" />
|
||||
</children>
|
||||
</GridPane>
|
||||
</children>
|
||||
</VBox>
|
|
@ -32,7 +32,9 @@
|
|||
</Menu>
|
||||
<Menu mnemonicParsing="false" text="Options">
|
||||
<items>
|
||||
<MenuItem mnemonicParsing="false" onAction="#onOptionsMenuNodeTypes" text="Node Configuration" />
|
||||
<MenuItem mnemonicParsing="false" onAction="#onOptionsMenuNodeTypes" text="Nodes" />
|
||||
<MenuItem mnemonicParsing="false" onAction="#onOptionsMenuLocation" text="Location" />
|
||||
<MenuItem mnemonicParsing="false" onAction="#onOptionsMenuBackground" text="Background" />
|
||||
</items>
|
||||
</Menu>
|
||||
<Menu mnemonicParsing="false" text="Help">
|
||||
|
|
|
@ -101,6 +101,38 @@ def set_session_options(session_id):
|
|||
return jsonify()
|
||||
|
||||
|
||||
@api.route("/sessions/<int:session_id>/location", methods=["PUT"])
|
||||
def set_session_location(session_id):
|
||||
session = core_utils.get_session(coreemu, session_id)
|
||||
data = request.get_json() or {}
|
||||
position = data["position"]
|
||||
location = data["location"]
|
||||
session.location.refxyz = (position["x"], position["y"], position["z"])
|
||||
session.location.setrefgeo(location["latitude"], location["longitude"], location["altitude"])
|
||||
session.location.refscale = data["scale"]
|
||||
return jsonify()
|
||||
|
||||
|
||||
@api.route("/sessions/<int:session_id>/location")
|
||||
def get_session_location(session_id):
|
||||
session = core_utils.get_session(coreemu, session_id)
|
||||
x, y, z = session.location.refxyz
|
||||
lat, lon, alt = session.location.refgeo
|
||||
return jsonify({
|
||||
"position": {
|
||||
"x": x,
|
||||
"y": y,
|
||||
"z": z,
|
||||
},
|
||||
"location": {
|
||||
"latitude": lat,
|
||||
"longitude": lon,
|
||||
"altitude": alt
|
||||
},
|
||||
"scale": session.location.refscale
|
||||
})
|
||||
|
||||
|
||||
@api.route("/sessions/<int:session_id>")
|
||||
def get_session(session_id):
|
||||
session = core_utils.get_session(coreemu, session_id)
|
||||
|
|
Loading…
Reference in a new issue