diff --git a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/App.java b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/App.java index b6813c2fc487069a71bc60d7ce5c4bec0f13e7ba..dc4206406ee5027dabfc27e5a246d4107a255c6e 100644 --- a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/App.java +++ b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/App.java @@ -1,5 +1,9 @@ package tdt4140.gr1800.app.core; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; import java.net.URI; import java.util.ArrayList; import java.util.Collection; @@ -17,7 +21,7 @@ public class App { private Collection<GeoLocations> geoLocations; public Iterable<String> getGeoLocationNames() { - Collection<String> names = new ArrayList<String>(geoLocations.size()); + Collection<String> names = new ArrayList<String>(geoLocations != null ? geoLocations.size() : 0); if (geoLocations != null) { for (GeoLocations geoLocations : geoLocations) { names.add(geoLocations.getName()); @@ -25,7 +29,7 @@ public class App { } return names; } - + public boolean hasGeoLocations(String name) { if (geoLocations != null) { for (GeoLocations geoLocations : geoLocations) { @@ -47,4 +51,46 @@ public class App { } return null; } + + // + + private DocumentStorageImpl<Collection<GeoLocations>, File> documentStorage = new DocumentStorageImpl<Collection<GeoLocations>, File>() { + + @Override + protected Collection<GeoLocations> getDocument() { + return geoLocations; + } + + @Override + protected void setDocument(Collection<GeoLocations> document) { + geoLocations = document; + } + + @Override + protected Collection<GeoLocations> createDocument() { + return new ArrayList<GeoLocations>(); + } + + @Override + protected Collection<GeoLocations> loadDocument(File file) throws IOException { + try { + return geoLocationsLoader.loadLocations(new FileInputStream(file)); + } catch (Exception e) { + throw new IOException(e); + } + } + + @Override + protected void storeDocument(Collection<GeoLocations> document, File file) throws IOException { + try { + geoLocationsLoader.saveLocations(document, new FileOutputStream(file)); + } catch (Exception e) { + throw new IOException(e); + } + } + }; + + public IDocumentStorage<File> getDocumentStorage() { + return documentStorage; + } } diff --git a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/DocumentStorageImpl.java b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/DocumentStorageImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..9314f27249bf693cb90bcbf07e2c9a2c0a851e60 --- /dev/null +++ b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/DocumentStorageImpl.java @@ -0,0 +1,49 @@ +package tdt4140.gr1800.app.core; + +import java.io.IOException; + +public abstract class DocumentStorageImpl<D, L> implements IDocumentStorage<L> { + + private L documentLocation; + + @Override + public L getDocumentLocation() { + return documentLocation; + } + + @Override + public void setDocumentLocation(L documentLocation) { + this.documentLocation = documentLocation; + } + + protected abstract D getDocument(); + protected abstract void setDocument(D document); + + protected abstract D createDocument(); + protected abstract D loadDocument(L storage) throws IOException; + protected abstract void storeDocument(D document, L storage) throws IOException; + + @Override + public void newDocument() { + setDocument(createDocument()); + } + + @Override + public void openDocument(L storage) throws IOException { + setDocument(loadDocument(storage)); + } + + @Override + public void saveDocument() throws IOException { + storeDocument(getDocument(), getDocumentLocation()); + } + + public void saveDocumentAs(L documentLocation) throws IOException { + setDocumentLocation(documentLocation); + saveDocument(); + } + + public void saveCopyAs(L documentLocation) throws IOException { + storeDocument(getDocument(), documentLocation); + } +} diff --git a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/IDocumentStorage.java b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/IDocumentStorage.java new file mode 100644 index 0000000000000000000000000000000000000000..ceef9616362ec82ffd15246c7811942b39ddc35e --- /dev/null +++ b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/IDocumentStorage.java @@ -0,0 +1,12 @@ +package tdt4140.gr1800.app.core; + +import java.io.IOException; + +public interface IDocumentStorage<L> { + public L getDocumentLocation(); + public void setDocumentLocation(L documentLocation); + + public void newDocument(); + public void openDocument(L documentLocation) throws IOException; + public void saveDocument() throws IOException; +} diff --git a/tdt4140-gr1800/app.ui/src/main/java/tdt4140/gr1800/app/ui/FileMenuController.java b/tdt4140-gr1800/app.ui/src/main/java/tdt4140/gr1800/app/ui/FileMenuController.java new file mode 100644 index 0000000000000000000000000000000000000000..f63754c3c4ab28db23b288e2e0711a50f0e504a5 --- /dev/null +++ b/tdt4140-gr1800/app.ui/src/main/java/tdt4140/gr1800/app/ui/FileMenuController.java @@ -0,0 +1,129 @@ +package tdt4140.gr1800.app.ui; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.scene.control.Menu; +import javafx.scene.control.MenuItem; +import javafx.stage.FileChooser; +import tdt4140.gr1800.app.core.IDocumentStorage; + +public class FileMenuController { + + private IDocumentStorage<File> documentStorage; + + public void setDocumentStorage(IDocumentStorage<File> documentStorage) { + this.documentStorage = documentStorage; + } + + private Consumer<IDocumentStorage<File>> onDocumentChanged; + + public void setOnDocumentChanged(Consumer<IDocumentStorage<File>> onDocumentChanged) { + this.onDocumentChanged = onDocumentChanged; + } + + @FXML + public void handleNewAction() { + documentStorage.newDocument(); + fireDocumentChanged(); + } + + private void fireDocumentChanged() { + if (onDocumentChanged != null) { + onDocumentChanged.accept(documentStorage); + } + } + + private List<File> recentFiles = new ArrayList<File>(); + + @FXML + private Menu recentMenu; + + protected void updateRecentMenu(File file) { + recentFiles.remove(file); + recentFiles.add(0, file); + recentMenu.getItems().clear(); + for (File recentFile : recentFiles) { + MenuItem menuItem = new MenuItem(); + menuItem.setText(recentFile.toString()); + menuItem.setOnAction(event -> handleOpenAction(event)); + recentMenu.getItems().add(menuItem); + } + } + + private FileChooser fileChooser; + + protected FileChooser getFileChooser() { + if (fileChooser == null) { + fileChooser = new FileChooser(); + } + return fileChooser; + } + + @FXML + public void handleOpenAction(ActionEvent event) { + File selection = null; + if (event.getSource() instanceof MenuItem) { + File file = new File(((MenuItem) event.getSource()).getText()); + if (file.exists()) { + selection = file; + } + } + if (selection == null) { + FileChooser fileChooser = getFileChooser(); + selection = fileChooser.showOpenDialog(null); + } + if (selection != null) { + try { + documentStorage.openDocument(selection); + updateRecentMenu(selection); + fireDocumentChanged(); + } catch (IOException e) { + // TODO + } + } + } + + @FXML + public void handleSaveAction() { + try { + documentStorage.saveDocument(); + } catch (IOException e) { + // TODO + } + } + + @FXML + public void handleSaveAsAction() { + FileChooser fileChooser = getFileChooser(); + File selection = fileChooser.showSaveDialog(null); + File oldStorage = documentStorage.getDocumentLocation(); + try { + documentStorage.setDocumentLocation(selection); + documentStorage.saveDocument(); + } catch (IOException e) { + // TODO + documentStorage.setDocumentLocation(oldStorage); + } + } + + @FXML + public void handleSaveCopyAsAction() { + FileChooser fileChooser = getFileChooser(); + File selection = fileChooser.showSaveDialog(null); + File oldStorage = documentStorage.getDocumentLocation(); + try { + documentStorage.setDocumentLocation(selection); + documentStorage.saveDocument(); + } catch (IOException e) { + // TODO + } finally { + documentStorage.setDocumentLocation(oldStorage); + } + } +} diff --git a/tdt4140-gr1800/app.ui/src/main/java/tdt4140/gr1800/app/ui/FxAppController.java b/tdt4140-gr1800/app.ui/src/main/java/tdt4140/gr1800/app/ui/FxAppController.java index 753b0caf45c168a803261e534e5a08c26e547fa8..22dc763b0309af78a8b853be3261a04aafdfa2de 100644 --- a/tdt4140-gr1800/app.ui/src/main/java/tdt4140/gr1800/app/ui/FxAppController.java +++ b/tdt4140-gr1800/app.ui/src/main/java/tdt4140/gr1800/app/ui/FxAppController.java @@ -1,48 +1,30 @@ package tdt4140.gr1800.app.ui; -import java.io.File; -import java.net.URI; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; - -import com.lynden.gmapsfx.GoogleMapView; -import com.lynden.gmapsfx.MapComponentInitializedListener; -import com.lynden.gmapsfx.javascript.event.UIEventType; -import com.lynden.gmapsfx.javascript.object.GoogleMap; -import com.lynden.gmapsfx.javascript.object.MVCArray; -import com.lynden.gmapsfx.javascript.object.MapOptions; -import com.lynden.gmapsfx.javascript.object.MapShape; -import com.lynden.gmapsfx.javascript.object.MapTypeIdEnum; -import com.lynden.gmapsfx.javascript.object.Marker; -import com.lynden.gmapsfx.javascript.object.MarkerOptions; -import com.lynden.gmapsfx.shapes.Polyline; -import com.lynden.gmapsfx.shapes.PolylineOptions; +import java.util.Iterator; +import fxmapcontrol.MapBase; +import fxmapcontrol.MapItemsControl; +import fxmapcontrol.MapNode; +import fxmapcontrol.MapTileLayer; import javafx.fxml.FXML; -import javafx.scene.control.Alert; -import javafx.scene.control.Alert.AlertType; import javafx.scene.control.ComboBox; import javafx.scene.control.Slider; -import javafx.scene.control.TextField; -import javafx.stage.DirectoryChooser; -import javafx.stage.FileChooser; import tdt4140.gr1800.app.core.App; import tdt4140.gr1800.app.core.GeoLocations; import tdt4140.gr1800.app.core.LatLong; -public class FxAppController implements MapComponentInitializedListener { +public class FxAppController { @FXML - private TextField geolocationsFileText; + private FileMenuController fileMenuController; @FXML private ComboBox<String> geoLocationsSelector; @FXML - private GoogleMapView mapView; - private GoogleMap map; + private MapBase mapView; + + private MapItemsControl<MapNode> markersParent; @FXML private Slider zoomSlider; @@ -51,133 +33,62 @@ public class FxAppController implements MapComponentInitializedListener { @FXML public void initialize() { - mapView.addMapInializedListener(this); app = new App(); + fileMenuController.setDocumentStorage(app.getDocumentStorage()); + fileMenuController.setOnDocumentChanged(documentStorage -> initMapMarkers()); geoLocationsSelector.getSelectionModel().selectedItemProperty().addListener((stringProperty, oldValue, newValue) -> updateGeoLocations()); - zoomSlider.valueProperty().addListener((doubleProperty, oldValue, newValue) -> map.setZoom((int) zoomSlider.getValue())); + + mapView.getChildren().add(MapTileLayer.getOpenStreetMapLayer()); + mapView.zoomLevelProperty().bind(zoomSlider.valueProperty()); + markersParent = new MapItemsControl<MapNode>(); + mapView.getChildren().add(markersParent); } private Object updateGeoLocations() { return null; } - private Map<GeoLocations, MapShape> shapes = new HashMap<GeoLocations, MapShape>(); - private Map<GeoLocations, Collection<Marker>> markers = new HashMap<GeoLocations, Collection<Marker>>(); - private void initMapMarkers() { - map.clearMarkers(); + markersParent.getItems().clear(); for (String geoLocationName : app.getGeoLocationNames()) { GeoLocations geoLocations = app.getGeoLocations(geoLocationName); - if (geoLocations.isPath()) { - Collection<com.lynden.gmapsfx.javascript.object.LatLong> latLongs = new ArrayList<com.lynden.gmapsfx.javascript.object.LatLong>(); - for (LatLong latLong : geoLocations) { - double lat = latLong.latitude, lon = latLong.longitude; - latLongs.add(new com.lynden.gmapsfx.javascript.object.LatLong(lat, lon)); - } - MVCArray mvc = new MVCArray(latLongs.toArray(new com.lynden.gmapsfx.javascript.object.LatLong[latLongs.size()])); - PolylineOptions options = new PolylineOptions() - .path(mvc) - .strokeColor("red") - .strokeWeight(2); - Polyline polyline = new Polyline(options); - map.addMapShape(polyline); - this.shapes.put(geoLocations, polyline); - } else { - Collection<Marker> markers = new ArrayList<Marker>(); - for (LatLong latLong : geoLocations) { - double lat = latLong.latitude, lon = latLong.longitude; - MarkerOptions options = new MarkerOptions().label(geoLocationName) - .position(new com.lynden.gmapsfx.javascript.object.LatLong(lat, lon)); - Marker marker = new Marker(options); - map.addMarker(marker); - markers.add(marker); + MapMarker lastMarker = null; + for (LatLong latLong : geoLocations) { + MapMarker mapMarker = new MapMarker(latLong); + markersParent.getItems().add(mapMarker); + if (geoLocations.isPath() && lastMarker != null) { + MapPathLine pathLine = new MapPathLine(lastMarker, mapMarker); + markersParent.getItems().add(pathLine); } - this.markers.put(geoLocations, markers); + lastMarker = mapMarker; } geoLocationsSelector.getItems().add(geoLocationName); } - map.setCenter(getCenter(null)); - map.addMouseEventHandler(UIEventType.click, (event) -> { - com.lynden.gmapsfx.javascript.object.LatLong latLong = event.getLatLong(); - System.out.println("Latitude: " + latLong.getLatitude()); - System.out.println("Longitude: " + latLong.getLongitude()); - }); + LatLong center = getCenter(null); System.out.println("Map markers initialized"); } - private com.lynden.gmapsfx.javascript.object.LatLong getCenter(GeoLocations geoLocations) { + private LatLong getCenter(GeoLocations geoLocations) { double latSum = 0.0, lonSum = 0.0; int num = 0; + Iterator<String> names = null; if (geoLocations == null) { - for (String geoLocationName : app.getGeoLocationNames()) { - geoLocations = app.getGeoLocations(geoLocationName); - for (LatLong latLong : geoLocations) { - double lat = latLong.latitude, lon = latLong.longitude; - latSum += lat; - lonSum += lon; - num++; - } + names = app.getGeoLocationNames().iterator(); + } + while (geoLocations != null || (names != null && names.hasNext())) { + if (names != null) { + geoLocations = app.getGeoLocations(names.next()); } - } else { for (LatLong latLong : geoLocations) { double lat = latLong.latitude, lon = latLong.longitude; latSum += lat; lonSum += lon; num++; } - } - return new com.lynden.gmapsfx.javascript.object.LatLong(latSum / num, lonSum / num); - } - - @Override - public void mapInitialized() { - // Set the initial properties of the map. - MapOptions mapOptions = new MapOptions(); - - mapOptions - .mapType(MapTypeIdEnum.ROADMAP) - .center(new com.lynden.gmapsfx.javascript.object.LatLong(63.0, 10.0)) - .overviewMapControl(false) - .panControl(false) - .rotateControl(false) - .scaleControl(false) - .streetViewControl(false) - .zoomControl(false) - .zoom(zoomSlider.getValue()); - map = mapView.createMap(mapOptions); - System.out.println("Map initialized: " + map); - if (app.getGeoLocationNames().iterator().hasNext()) { - initMapMarkers(); - } - } - - @FXML - public void loadGeolocationsFile() { - try { - URI fileUri = new URI(geolocationsFileText.getText()); - app.loadGeoLocations(fileUri); - System.out.println("GeoLocations initialized: " + app.getGeoLocationNames()); - if (map != null) { - initMapMarkers(); + if (names != null) { + geoLocations = null; } - } catch (Exception e) { - Alert alert = new Alert(AlertType.ERROR); - alert.setTitle("Exception when loading from " + geolocationsFileText.getText() + ": " + e.getMessage()); - alert.setContentText(e.getMessage()); - alert.show(); - } - } - - @FXML - public void handleBrowseGeolocationsFile() { - FileChooser fileChooser = new FileChooser(); - fileChooser.setTitle("Select geo-locations file"); - - File file = fileChooser.showOpenDialog(mapView.getScene().getWindow()); - if (file == null) { - file = new File("."); } - geolocationsFileText.setText(file.toURI().toString()); - geolocationsFileText.getOnAction().handle(null); + return new LatLong(latSum / num, lonSum / num); } } diff --git a/tdt4140-gr1800/app.ui/src/main/resources/tdt4140/gr1800/app/ui/FileMenu.fxml b/tdt4140-gr1800/app.ui/src/main/resources/tdt4140/gr1800/app/ui/FileMenu.fxml new file mode 100644 index 0000000000000000000000000000000000000000..9b1209b8feb26c402e0ffb0e15d9a69a63cc97bd --- /dev/null +++ b/tdt4140-gr1800/app.ui/src/main/resources/tdt4140/gr1800/app/ui/FileMenu.fxml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<?import java.lang.*?> +<?import javafx.scene.control.Menu?> +<?import javafx.scene.control.MenuItem?> + +<Menu xmlns:fx="http://javafx.com/fxml" text="File" fx:controller="tdt4140.gr1800.app.ui.FileMenuController"> + <items> + <MenuItem text="New" onAction="#handleNewAction"/> + <MenuItem text="Open..." onAction="#handleOpenAction"/> + <Menu fx:id="recentMenu" text="Open Recent"/> + <MenuItem text="Save" onAction="#handleSaveAction"/> + <MenuItem text="Save As..." onAction="#handleSaveAsAction"/> + <MenuItem text="Save Copy As..." onAction="#handleSaveCopyAsAction"/> + </items> +</Menu>