Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • akselsa/course-material
  • it1901/course-material
2 results
Show changes
Showing
with 0 additions and 933 deletions
package simpleex.json;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
import simpleex.core.LatLong;
public class LatLongSerializer extends JsonSerializer<LatLong> {
public static final String LONGITUDE_FIELD_NAME = "longitude";
public static final String LATITUDE_FIELD_NAME = "latitude";
@Override
public void serialize(final LatLong latLon, final JsonGenerator jsonGen,
final SerializerProvider provider) throws IOException {
jsonGen.writeStartObject();
jsonGen.writeFieldName(LATITUDE_FIELD_NAME);
jsonGen.writeNumber(latLon.getLatitude());
jsonGen.writeFieldName(LONGITUDE_FIELD_NAME);
jsonGen.writeNumber(latLon.getLongitude());
jsonGen.writeEndObject();
}
}
package simpleex.json;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import simpleex.core.LatLong;
import simpleex.core.LatLongs;
public class LatLongsDeserializer extends JsonDeserializer<LatLongs> {
private final LatLongDeserializer latLongDeserializer = new LatLongDeserializer();
@Override
public LatLongs deserialize(final JsonParser jsonParser,
final DeserializationContext deserContext) throws IOException, JsonProcessingException {
final JsonNode jsonNode = jsonParser.getCodec().readTree(jsonParser);
if (jsonNode instanceof ArrayNode) {
final ArrayNode latLongsArray = (ArrayNode) jsonNode;
final Collection<LatLong> latLongs = new ArrayList<>(latLongsArray.size());
for (final JsonNode latLongNode : latLongsArray) {
final LatLong latLong = latLongDeserializer.deserialize(latLongNode);
latLongs.add(latLong);
}
return new LatLongs(latLongs);
}
return null;
}
}
package simpleex.json;
import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.module.SimpleDeserializers;
import com.fasterxml.jackson.databind.module.SimpleSerializers;
import simpleex.core.LatLong;
import simpleex.core.LatLongs;
public class LatLongsModule extends Module {
@Override
public String getModuleName() {
return "LatLongsModule";
}
@Override
public Version version() {
return Version.unknownVersion();
}
private final SimpleSerializers serializers = new SimpleSerializers();
private final SimpleDeserializers deserializers = new SimpleDeserializers();
public LatLongsModule() {
serializers.addSerializer(LatLong.class, new LatLongSerializer());
serializers.addSerializer(LatLongs.class, new LatLongsSerializer());
deserializers.addDeserializer(LatLong.class, new LatLongDeserializer());
deserializers.addDeserializer(LatLongs.class, new LatLongsDeserializer());
}
@Override
public void setupModule(final SetupContext context) {
context.addSerializers(serializers);
context.addDeserializers(deserializers);
}
}
package simpleex.json;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
import simpleex.core.LatLong;
import simpleex.core.LatLongs;
public class LatLongsSerializer extends JsonSerializer<LatLongs> {
@Override
public void serialize(final LatLongs latLongs, final JsonGenerator jsonGen,
final SerializerProvider provider) throws IOException {
jsonGen.writeStartArray(latLongs.getLatLongCount());
for (final LatLong latLong : latLongs) {
jsonGen.writeObject(latLong);
}
jsonGen.writeEndArray();
}
}
# Kildekode for persistenslaget
Persistenslaget bruker [Jackson-biblioteket](https://github.com/FasterXML/jackson) for å serialisere objekter til [JSON](https://www.json.org).
For hver [domeneklasse](../core/README.md) finnes tilsvarende klasser for serialisering (konvertering av domeneobjekter til tekststrøm) og deserialisering (parsing av tekststrøm og opprettelse av tilsvarende domeneobjekter). I tillegg finnes det en klasse, **JacksonConfigurator**, som inneholder diverse metoder for å lage og konfigurere **ObjectMapper**- og **SimpleModule**-objektene som trengs for å serialisere og deserialisere objekter.
package simpleex.ui;
import javafx.event.EventHandler;
import javafx.geometry.Point2D;
import javafx.scene.Node;
import javafx.scene.input.MouseEvent;
public class DraggableNodeController {
private final NodeDraggedHandler nodeDraggedHandler;
public DraggableNodeController() {
this(null);
}
public DraggableNodeController(final NodeDraggedHandler nodeDraggedHandler) {
this.nodeDraggedHandler = (nodeDraggedHandler != null ? nodeDraggedHandler : (node, x, y) -> {});
}
private boolean immediate = false;
public void setImmediate(final boolean immediate) {
this.immediate = immediate;
}
private Node currentNode = null;
private Point2D startPoint = null;
private Point2D startTranslate = null;
private final EventHandler<MouseEvent> mousePressedHandler = this::mousePressed;
private final EventHandler<MouseEvent> mouseDraggedHandler = this::mouseDragged;
private final EventHandler<MouseEvent> mouseReleasedHandler = this::mouseReleased;
public void attach(final Node node) {
node.setOnMousePressed(mousePressedHandler);
node.setOnMouseDragged(mouseDraggedHandler);
node.setOnMouseReleased(mouseReleasedHandler);
}
public void detach(final Node node) {
node.setOnMousePressed(null);
node.setOnMouseDragged(null);
node.setOnMouseReleased(null);
}
private void mousePressed(final MouseEvent mouseEvent) {
if (currentNode == null && mouseEvent.getSource() instanceof Node) {
currentNode = (Node) mouseEvent.getSource();
startPoint = new Point2D(mouseEvent.getSceneX(), mouseEvent.getSceneY());
startTranslate = new Point2D(currentNode.getTranslateX(), currentNode.getTranslateY());
mouseEvent.consume();
}
}
private void mouseDragged(final MouseEvent mouseEvent) {
if (currentNode != null && currentNode == mouseEvent.getSource()) {
final double dx = mouseEvent.getSceneX() - startPoint.getX();
final double dy = mouseEvent.getSceneY() - startPoint.getY();
updateNode(dx, dy);
}
}
protected void updateNode(final double dx, final double dy) {
if (immediate) {
nodeDraggedHandler.nodeDragged(currentNode, dx, dy);
startPoint = startPoint.add(dx, dy);
} else if (currentNode != null) {
currentNode.setTranslateX(startTranslate.getX() + dx);
currentNode.setTranslateY(startTranslate.getY() + dy);
}
}
private void mouseReleased(final MouseEvent mouseEvent) {
if (currentNode != null && currentNode == mouseEvent.getSource()) {
final double dx = mouseEvent.getSceneX() - startPoint.getX();
final double dy = mouseEvent.getSceneY() - startPoint.getY();
if (!immediate) {
currentNode.setTranslateX(startTranslate.getX());
currentNode.setTranslateY(startTranslate.getY());
}
final Node node = currentNode;
currentNode = null;
nodeDraggedHandler.nodeDragged(node, dx, dy);
}
}
public interface NodeDraggedHandler {
public void nodeDragged(Node currentNode2, double dx, double dy);
}
}
package simpleex.ui;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import simpleex.core.LatLongs;
public class FxApp extends Application {
@Override
public void start(final Stage stage) throws Exception {
final FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("FxApp.fxml"));
final Parent root = fxmlLoader.load();
final FxAppController controller = fxmlLoader.getController();
controller.setLatLongs(new LatLongs(63.1, 11.2, 63.2, 11.0));
final Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
public static void main(final String[] args) {
// only needed on ios
System.setProperty("os.target", "ios");
System.setProperty("os.name", "iOS");
System.setProperty("glass.platform", "ios");
System.setProperty("targetos.name", "iOS");
launch(args);
}
}
package simpleex.ui;
import com.fasterxml.jackson.databind.ObjectMapper;
import fxmapcontrol.Location;
import fxmapcontrol.MapBase;
import fxmapcontrol.MapItemsControl;
import fxmapcontrol.MapNode;
import fxmapcontrol.MapProjection;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import javafx.collections.FXCollections;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.geometry.Point2D;
import javafx.scene.Node;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.ButtonType;
import javafx.scene.control.ListView;
import javafx.scene.control.Slider;
import javafx.stage.FileChooser;
import simpleex.core.LatLong;
import simpleex.core.LatLongs;
import simpleex.json.LatLongsModule;
/*
@startuml
class FxAppController
class LatLongs
class BorderPane
class "ListView<LatLong>" as ListView
class "fxmapcontrol.MapBase" as MapBase
BorderPane *--> ListView: "left"
BorderPane *--> MapBase: "center"
FxAppController --> LatLongs: "latLongs"
FxAppController --> MapBase: "mapView"
FxAppController --> ListView: "locationListView"
@enduml
*/
public class FxAppController {
private LatLongs latLongs;
public FxAppController() {
latLongs = new LatLongs();
}
public LatLongs getLatLongs() {
return latLongs;
}
// to make it testable
public void setLatLongs(final LatLongs latLongs) {
this.latLongs = latLongs;
updateLocationViewList(0);
}
// @FXML
// private FileMenuController fileMenuController;
@FXML
private ListView<LatLong> locationListView;
@FXML
private MapBase mapView;
private MapItemsControl<MapNode> markersParent;
private MapMarker marker = null;
private DraggableNodeController draggableMapController = null;
private DraggableNodeController draggableMarkerController = null;
@FXML
private Slider zoomSlider;
@FXML
private void initialize() {
// map stuff
// mapView.getChildren().add(MapTileLayer.getOpenStreetMapLayer());
zoomSlider.valueProperty()
.addListener((prop, oldValue, newValue) -> mapView.setZoomLevel(zoomSlider.getValue()));
zoomSlider.setValue(8);
markersParent = new MapItemsControl<MapNode>();
mapView.getChildren().add(markersParent);
draggableMapController = new DraggableNodeController(this::handleMapDragged);
draggableMapController.setImmediate(true);
draggableMapController.attach(mapView);
draggableMarkerController = new DraggableNodeController(this::handleMarkerDragged);
// the location list
locationListView.getSelectionModel().selectedIndexProperty()
.addListener((prop, oldValue, newValue) -> updateMapMarker(true));
}
private void handleMapDragged(final Node node, final double dx, final double dy) {
final MapProjection projection = mapView.getProjection();
final Point2D point = projection.locationToViewportPoint(mapView.getCenter());
final Location newCenter = projection.viewportPointToLocation(point.add(-dx, -dy));
mapView.setCenter(newCenter);
}
private void handleMarkerDragged(final Node node, final double dx, final double dy) {
final MapProjection projection = mapView.getProjection();
final Point2D point = projection.locationToViewportPoint(marker.getLocation());
final Location newLocation = projection.viewportPointToLocation(point.add(dx, dy));
getLatLongs().setLatLong(locationListView.getSelectionModel().getSelectedIndex(),
location2LatLong(newLocation));
updateLocationViewListSelection(false);
}
private LatLong location2LatLong(final Location newLocation) {
return new LatLong(newLocation.getLatitude(), newLocation.getLongitude());
}
private void updateMapMarker(final boolean centerOnMarker) {
final int num = locationListView.getSelectionModel().getSelectedIndex();
if (num < 0 || num >= getLatLongs().getLatLongCount()) {
markersParent.getItems().clear();
if (draggableMarkerController != null) {
draggableMarkerController.detach(marker);
}
marker = null;
} else {
final LatLong latLong = getLatLongs().getLatLong(num);
if (marker == null) {
marker = new MapMarker(latLong);
markersParent.getItems().add(marker);
if (draggableMarkerController != null) {
draggableMarkerController.attach(marker);
}
} else {
marker.setLocation(latLong);
}
if (centerOnMarker) {
mapView.setCenter(marker.getLocation());
}
}
}
@FXML
private void handleAddLocation() {
final Location center = mapView.getCenter();
final int pos = getLatLongs().addLatLong(location2LatLong(center));
updateLocationViewList(pos);
}
private void updateLocationViewListSelection(final Boolean updateMapMarker) {
final int selectedIndex = locationListView.getSelectionModel().getSelectedIndex();
updateLocationViewListItem(selectedIndex);
if (updateMapMarker != null) {
updateMapMarker(updateMapMarker);
}
}
private void updateLocationViewListItem(final int index) {
locationListView.getItems().set(index, getLatLongs().getLatLong(index));
}
private void updateLocationViewList(int selectedIndex) {
final LatLong[] latLongs = new LatLong[getLatLongs().getLatLongCount()];
for (int i = 0; i < latLongs.length; i++) {
latLongs[i] = getLatLongs().getLatLong(i);
}
final int oldSelectionIndex = locationListView.getSelectionModel().getSelectedIndex();
locationListView.setItems(FXCollections.observableArrayList(latLongs));
if (selectedIndex < 0 || selectedIndex >= latLongs.length) {
selectedIndex = oldSelectionIndex;
}
if (selectedIndex >= 0 && selectedIndex < getLatLongs().getLatLongCount()) {
locationListView.getSelectionModel().select(selectedIndex);
}
}
// File menu items
private FileChooser fileChooser;
private FileChooser getFileChooser() {
if (fileChooser == null) {
fileChooser = new FileChooser();
}
return fileChooser;
}
@FXML
void handleOpenAction(final ActionEvent event) {
final FileChooser fileChooser = getFileChooser();
final File selection = fileChooser.showOpenDialog(null);
if (selection != null) {
try (InputStream input = new FileInputStream(selection) ){
setLatLongs(getObjectMapper().readValue(input, LatLongs.class));
} catch (final IOException e) {
showExceptionDialog("Oops, problem when opening " + selection, e);
}
}
}
private ObjectMapper objectMapper;
public ObjectMapper getObjectMapper() {
if (objectMapper == null) {
objectMapper = new ObjectMapper();
objectMapper.registerModule(new LatLongsModule());
}
return objectMapper;
}
private void showExceptionDialog(final String message) {
final Alert alert = new Alert(AlertType.ERROR, message, ButtonType.CLOSE);
alert.showAndWait();
}
private void showExceptionDialog(final String message, final Exception e) {
showExceptionDialog(message + ": " + e.getLocalizedMessage());
}
private void showSaveExceptionDialog(final File location, final Exception e) {
showExceptionDialog("Oops, problem saving to " + location, e);
}
@FXML
void handleSaveAction() {
final FileChooser fileChooser = getFileChooser();
final File selection = fileChooser.showSaveDialog(null);
if (selection != null ) {
try (OutputStream outputStream = new FileOutputStream(selection, false)) {
getObjectMapper().writeValue(outputStream, getLatLongs());
} catch (final IOException e) {
showSaveExceptionDialog(selection, e);
}
}
}
}
package simpleex.ui;
import fxmapcontrol.Location;
import fxmapcontrol.MapItem;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import simpleex.core.LatLong;
public class MapMarker extends MapItem<LatLong> {
public MapMarker(final LatLong latLong) {
setLocation(latLong);
final Circle circle = new Circle();
circle.setRadius(5);
circle.setFill(Color.BLUE);
getChildren().add(circle);
}
public final void setLocation(final LatLong latLong) {
setLocation(new Location(latLong.getLatitude(), latLong.getLongitude()));
}
}
# Kildekode for brukergrensesnittet
Brukergrensesnittet er laget med JavaFX og FXML.
Brukergrensesnittet er enkelt og består av en liste og et kart. Lista viser innholdet i et **LatLongs**-objekt, altså en samling **LatLong**-objekter. Når man velger et element, så vises det elementet som et punkt på kartet. En kan flytte punktet, forskyve kartet, zoome inn og ut og opprette nye **LatLong**-objekter.
Kartet implementeres av ([FxMapControl](https://github.com/ClemensFischer/FX-Map-Control), og siden biblioteket ikke er tilgjengelig i et sentral repository, så har vi bygget og lagt ved jar-filen.
I tillegg kan en lagre og lese inn **LatLongs**-objektet fra **Open...**- og **Save...**-aksjoner i en **File**-meny.
```plantuml
class FxAppController
class LatLongs
class BorderPane
class "ListView<LatLong>" as ListView
class "fxmapcontrol.MapBase" as MapBase
BorderPane *--> ListView: "left"
BorderPane *--> MapBase: "center"
FxAppController --> LatLongs: "latLongs"
FxAppController --> MapBase: "mapView"
FxAppController --> ListView: "locationListView"
```
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.ListView?>
<?import javafx.scene.control.Slider?>
<?import fxmapcontrol.MapBase?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.control.Label?>
<?import fxmapcontrol.MapTileLayer?>
<?import fxmapcontrol.TileSource?>
<?import javafx.scene.control.MenuBar?>
<?import javafx.scene.control.Menu?>
<?import javafx.scene.control.MenuItem?>
<?import javafx.scene.control.SeparatorMenuItem?>
<BorderPane xmlns:fx="http://javafx.com/fxml"
fx:controller="simpleex.ui.FxAppController"
prefHeight="750" prefWidth="1000">
<top>
<VBox>
<MenuBar >
<menus>
<Menu text="File">
<items>
<MenuItem text="Open..." accelerator="Meta+O" onAction="#handleOpenAction"/>
<MenuItem text="Save" accelerator="Meta+S" onAction="#handleSaveAction"/>
</items>
</Menu>
</menus>
</MenuBar>
</VBox>
</top>
<left>
<VBox fillWidth="true">
<ListView fx:id="locationListView"/>
<Button text="Add location" onAction="#handleAddLocation"/>
</VBox>
</left>
<center>
<VBox>
<MapBase fx:id="mapView">
<MapTileLayer name="OpenStreetMap" minZoomLevel="0" maxZoomLevel="17">
<tileSource>
<TileSource urlFormat="http://a.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png"/>
<!--
<TileSource urlFormat="https://{c}.tile.openstreetmap.org/{z}/{x}/{y}.png"/>
<TileSource urlFormat="http://opencache.statkart.no/gatekeeper/gk/gk.open_gmaps?layers=topo2&amp;zoom={z}&amp;x={x}&amp;y={y}"/>
<TileSource urlFormat="http://mt1.google.com/vt/lyrs=m@129&amp;hl=en&amp;s=Galileo&amp;z={z}&amp;x={x}&amp;y={y}"/>
-->
</tileSource>
</MapTileLayer>
</MapBase>
<Slider fx:id="zoomSlider" min="1" max="20" value="9"/>
</VBox>
</center>
</BorderPane>
# Ressurser for brukergrensesnittet
Brukergrensesnittet er laget med JavaFX og FXML, og her ligger FXML-fila som brukes.
package simpleex.core;
import java.util.HashMap;
import java.util.Map;
import org.junit.Assert;
import org.junit.Test;
public class LatLongTest {
@Test
public void testToString() {
final LatLong latLong = new LatLong(63.0, 10.0);
Assert.assertEquals(Double.toString(63) + "," + Double.toString(10), latLong.toString());
}
@Test
public void testValueOf() {
testLatLong(LatLong.valueOf("63.0, 10.0"), 63.0, 10.0);
testLatLong(LatLong.valueOf("63.0, 10.0", ","), 63.0, 10.0);
testLatLong(LatLong.valueOf("63.0; 10.0", ";"), 63.0, 10.0);
try {
testLatLong(LatLong.valueOf("63.0; 10.0", ","), 63.0, 10.0);
Assert.fail("Should throw IllegalArgumentException");
} catch (final IllegalArgumentException e) {
// ok
} catch (final Exception e) {
Assert.fail("Should throw IllegalArgumentException");
}
}
private void testLatLong(final LatLong latLong, final double lat, final double lon) {
Assert.assertEquals(lat, latLong.getLatitude(), 0.0);
Assert.assertEquals(lon, latLong.getLongitude(), 0.0);
}
@Test
public void testEquals() {
Assert.assertTrue(new LatLong(63.0, 10.0).equals(new LatLong(63.0, 10.0)));
Assert.assertFalse(new LatLong(10.0, 63.0).equals(new LatLong(63.0, 10.0)));
Assert.assertFalse(new LatLong(10.0, 63.0).equals(null));
}
@Test
public void testHashCode() {
final Map<LatLong, String> map = new HashMap<>();
map.put(new LatLong(63.0, 10.0), "first");
map.put(new LatLong(63.0, 10.0), "second");
Assert.assertEquals(1, map.size());
Assert.assertEquals("second", map.get(new LatLong(63.0, 10.0)));
}
@Test
public void testDistance() {
final LatLong trd = new LatLong(63.4217137055, 10.4221522734);
final LatLong str = new LatLong(63.0339713594, 10.2946225585);
checkDistance(trd.distance(trd), 0.0, 10.0);
checkDistance(str.distance(str), 0.0, 10.0);
checkDistance(trd.distance(str), 43000.0, 45000.0);
checkDistance(str.distance(trd), 43000.0, 45000.0);
checkDistance(LatLong.distance(trd, trd), 0.0, 10.0);
checkDistance(LatLong.distance(str, str), 0.0, 10.0);
checkDistance(LatLong.distance(trd, str), 43000.0, 45000.0);
checkDistance(LatLong.distance(str, trd), 43000.0, 45000.0);
}
private void checkDistance(final double d, final double lower, final double upper) {
Assert.assertTrue(d + " isn't between " + lower + " and " + upper, d <= upper && d >= lower);
}
}
package simpleex.core;
import java.util.Arrays;
import java.util.Iterator;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
public class LatLongsTest {
private LatLongs latLongs;
@Before
public void setUp() {
latLongs = new LatLongs();
}
@Test
public void testEmptyConstructor() {
Assert.assertEquals(0, latLongs.getLatLongCount());
}
private static void checkLatLongs(final LatLongs latLongs1, final LatLong... latLongs2) {
Assert.assertEquals(latLongs2.length, latLongs1.getLatLongCount());
final Iterator<LatLong> it = latLongs1.iterator();
for (int i = 0; i < latLongs2.length; i++) {
Assert.assertTrue(it.hasNext());
Assert.assertEquals(latLongs2[i], latLongs1.getLatLong(i));
Assert.assertEquals(latLongs2[i], it.next());
}
Assert.assertFalse(it.hasNext());
}
@Test
public void testConstructor() {
checkLatLongs(new LatLongs(63.0, 10.3, 63.1, 10.2), new LatLong(63.0, 10.3),
new LatLong(63.1, 10.2));
checkLatLongs(new LatLongs(new LatLong(63.0, 10.3), new LatLong(63.1, 10.2)),
new LatLong(63.0, 10.3), new LatLong(63.1, 10.2));
checkLatLongs(new LatLongs(Arrays.asList(new LatLong(63.1, 10.2), new LatLong(63.1, 10.1))),
new LatLong(63.1, 10.2), new LatLong(63.1, 10.1));
}
@Test
public void testAddLatLong() {
latLongs.addLatLong(new LatLong(63.0, 10.3));
Assert.assertEquals(1, latLongs.getLatLongCount());
latLongs.addLatLong(new LatLong(63.1, 10.2));
Assert.assertEquals(2, latLongs.getLatLongCount());
}
@Test
public void testAddLatLongs() {
final LatLongs lls1 = new LatLongs();
lls1.addLatLongs(63.0, 10.3, 63.1, 10.2);
checkLatLongs(lls1, new LatLong(63.0, 10.3), new LatLong(63.1, 10.2));
final LatLongs lls2 = new LatLongs();
lls2.addLatLongs(new LatLong(63.0, 10.3), new LatLong(63.1, 10.2));
checkLatLongs(lls2, new LatLong(63.0, 10.3), new LatLong(63.1, 10.2));
final LatLongs lls3 = new LatLongs();
lls3.addLatLongs(Arrays.asList(new LatLong(63.1, 10.2), new LatLong(63.1, 10.1)));
checkLatLongs(lls3, new LatLong(63.1, 10.2), new LatLong(63.1, 10.1));
}
}
# Testkode for domenelaget
package simpleex.json;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Assert;
import org.junit.Test;
import simpleex.core.LatLong;
import simpleex.core.LatLongs;
public class LatLongsJsonTest {
private final ObjectMapper objectMapper = new ObjectMapper();
{
objectMapper.registerModule(new LatLongsModule());
}
protected void assertEqualsIgnoreWhitespace(final String expected, final String actual)
throws Exception {
Assert.assertEquals(expected, actual.replaceAll("\\s+", ""));
}
@Test
public void testLatLongsSerialization() throws Exception {
final String actualJson = objectMapper.writeValueAsString(new LatLongs(new LatLong(63.1, 12.3), new LatLong(63.0, 12.4)));
final String expectedJson = "[{\"latitude\":63.1,\"longitude\":12.3},{\"latitude\":63.0,\"longitude\":12.4}]";
assertEqualsIgnoreWhitespace(expectedJson, actualJson);
}
@Test
public void testLatLongsDeserialization() throws Exception {
final String json = "[{\"latitude\":63.1,\"longitude\":12.3}, [63.0,12.4]]";
final LatLongs latLongs = objectMapper.readValue(json, LatLongs.class);
Assert.assertEquals(2, latLongs.getLatLongCount());
Assert.assertEquals(new LatLong(63.1, 12.3), latLongs.getLatLong(0));
Assert.assertEquals(new LatLong(63.0, 12.4), latLongs.getLatLong(1));
}
}
# Testkode for persistenslaget
package simpleex.ui;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import fxmapcontrol.Location;
import fxmapcontrol.MapBase;
import java.util.ArrayList;
import java.util.List;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.testfx.framework.junit.ApplicationTest;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ListView;
import javafx.stage.Stage;
import simpleex.core.LatLong;
import simpleex.core.LatLongs;
public class FxAppTest extends ApplicationTest {
@BeforeClass
public static void headless() {
if (Boolean.valueOf(System.getProperty("gitlab-ci", "false"))) {
System.setProperty("prism.verbose", "true"); // optional
System.setProperty("java.awt.headless", "true");
System.setProperty("testfx.robot", "glass");
System.setProperty("testfx.headless", "true");
System.setProperty("glass.platform", "Monocle");
System.setProperty("monocle.platform", "Headless");
System.setProperty("prism.order", "sw");
System.setProperty("prism.text", "t2k");
System.setProperty("testfx.setup.timeout", "2500");
}
}
private FxAppController controller;
private LatLongs latLongs;
@Override
public void start(final Stage stage) throws Exception {
final FXMLLoader loader = new FXMLLoader(getClass().getResource("FxApp.fxml"));
final Parent root = loader.load();
this.controller = loader.getController();
setUpLatLongs();
final Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
private List<LatLong> latLongList;
private void setUpLatLongs() {
// test data
latLongList = new ArrayList<>(List.of(new LatLong(63.1, 11.2), new LatLong(63.2, 11.0)));
// "mocked" (faked) LatLongs object with very specific and limited behavior
latLongs = mock(LatLongs.class);
// get nth LatLong object
when(latLongs.getLatLong(anyInt()))
.then(invocation -> latLongList.get(invocation.getArgument(0)));
// get the number of LatLong objects
when(latLongs.getLatLongCount()).then(invocation -> latLongList.size());
// iterator for LatLong objects
when(latLongs.iterator()).then(invocation -> latLongList.iterator());
controller.setLatLongs(latLongs);
}
@Test
public void testController() {
Assert.assertNotNull(this.controller);
}
@Test
public void testLocationListView() {
final ListView<?> locationListView = lookup("#locationListView").query();
// list contains equals elements in same order
Assert.assertEquals(latLongList, locationListView.getItems());
// first list element is auto-selected
Assert.assertEquals(0, locationListView.getSelectionModel().getSelectedIndex());
}
@Test
public void testMapView() {
final MapBase mapView = lookup("#mapView").query();
// center of map view is approx. the first LatLong object
final Location center = mapView.getCenter();
final double epsilon = 0.000001; // round-off error
Assert.assertEquals(latLongList.get(0).getLatitude(), center.getLatitude(), epsilon);
Assert.assertEquals(latLongList.get(0).getLongitude(), center.getLongitude(), epsilon);
}
@Test
public void testAddLocation() {
// needs map center
final Location center = ((MapBase) lookup("#mapView").query()).getCenter();
// add behavior for add
final LatLong latLong = new LatLong(center.getLatitude(), center.getLongitude());
when(latLongs.addLatLong(latLong)).thenReturn(2); // add center
// make test less sensitive to exact button text
final Button addLocButton = lookup(node -> node instanceof Button
&& ((Button) node).getText().toLowerCase().startsWith("add loc")).query();
clickOn(addLocButton);
}
}
# Testkode for brukergrensesnittet
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.ListView?>
<?import javafx.scene.control.Slider?>
<?import fxmapcontrol.MapBase?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.control.Label?>
<?import fxmapcontrol.MapTileLayer?>
<?import fxmapcontrol.TileSource?>
<?import javafx.scene.control.MenuBar?>
<?import javafx.scene.control.Menu?>
<?import javafx.scene.control.MenuItem?>
<?import javafx.scene.control.SeparatorMenuItem?>
<BorderPane xmlns:fx="http://javafx.com/fxml"
fx:controller="simpleex.ui.FxAppController"
prefHeight="750" prefWidth="1000">
<top>
<VBox>
<MenuBar >
<menus>
<Menu text="File">
<items>
<MenuItem text="Open..." accelerator="Meta+O" onAction="#handleOpenAction"/>
<MenuItem text="Save" accelerator="Meta+S" onAction="#handleSaveAction"/>
</items>
</Menu>
</menus>
</MenuBar>
</VBox>
</top>
<left>
<VBox fillWidth="true">
<ListView fx:id="locationListView"/>
<Button text="Add location" onAction="#handleAddLocation"/>
</VBox>
</left>
<center>
<VBox>
<MapBase fx:id="mapView">
<MapTileLayer name="OpenStreetMap" minZoomLevel="0" maxZoomLevel="17">
<tileSource>
<TileSource urlFormat="http://a.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png"/>
<!--
<TileSource urlFormat="https://{c}.tile.openstreetmap.org/{z}/{x}/{y}.png"/>
<TileSource urlFormat="http://opencache.statkart.no/gatekeeper/gk/gk.open_gmaps?layers=topo2&amp;zoom={z}&amp;x={x}&amp;y={y}"/>
<TileSource urlFormat="http://mt1.google.com/vt/lyrs=m@129&amp;hl=en&amp;s=Galileo&amp;z={z}&amp;x={x}&amp;y={y}"/>
-->
</tileSource>
</MapTileLayer>
</MapBase>
<Slider fx:id="zoomSlider" min="1" max="20" value="9"/>
</VBox>
</center>
</BorderPane>