Skip to content

Commit

Permalink
Initial Bullseye logic almost finished
Browse files Browse the repository at this point in the history
  • Loading branch information
liturner committed Dec 26, 2022
1 parent fd90ad9 commit 23f938f
Show file tree
Hide file tree
Showing 20 changed files with 316 additions and 300 deletions.
19 changes: 16 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
[![Download Frederick](https://img.shields.io/sourceforge/dt/frederick.svg)](https://sourceforge.net/projects/frederick/files/latest/download)
[Download Frederick](https://github.com/liturner/frederick/releases)

[![Github All Releases](https://img.shields.io/github/downloads/liturner/frederick/total.svg)]()

[![Download Frederick](https://a.fsdn.com/con/app/sf-download-button)](https://sourceforge.net/projects/frederick/files/latest/download)

# Links

- [Product Site](https://liturner.github.io/frederick/)
- [Sourceforge Project](https://sourceforge.net/projects/frederick/)
- [GitHub Project](https://github.com/liturner/frederick)

# Project Type
Expand All @@ -21,3 +21,16 @@ We aim to provide a set of tools to help with the missions and deployments of th
- Stability of the software is preferred over quantity of features.
- The software should only be configurable where neccassary.
- The software is to be used in emergency situations. Pretty comes second to fast.

# Architechture

The application aims to have a failry minimalistic architechture, while still ensuring that future refactoring is not a nightmare. The GUI is generally seperate from the application, and the individual windows or major components of the application do not communicate directly with one another, but rather through a central command and event system. The primary points of interest are:

- We treat the Application class as a global service provider. It is anyway static, and as we control the initialisation order within it, we can ensure that service order initialisation is handled.
- There is a single Service class which is not tied to the GUI or any major SDK. This is where all actions and events are consumed and produced. Future iterations may see this evolve into multiple services provided by modules. This class hides the data storage and logging from the user interfaces. This is a critical aspect to consider, as Unit Testing must be able to validate that the application allways logs certain actions. (relevant for certification)
- Each window is trated like a plugin which adds a new view on the data. It does not add any new functionality! All functionality is handled by the "Service" class.
- Action Listeners should be limited to the main window frames, which themselves update their sub components. This is purely for kognitive simplicity and may change if not strong enough as a tactic.
- GIS stuff (Geotools) is kept in a single package. The application should never become dependant on such a major SDK.
- GIS Layers are singletons. The application is deliberately static, and adding / removing data to and from the layers is well defined enough that we can centralise the logic in classes which will only have one instance.

It is known that this architechture is not complete. As the application expands it will be updated to be more robust using a strong pattern. For the time being the intention is to spend some time developing the requirements of the application, before settling on a long term architechture.
10 changes: 10 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<artifactId>frederick</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<url>https://liturner.github.io/frederick/</url>

<name>Frederick</name>
<licenses>
Expand Down Expand Up @@ -339,6 +340,15 @@
<artifactId>maven-surefire-report-plugin</artifactId>
</plugin>

<!-- Generate and add Javadoc to the Project Site-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<configuration>
<doclint>all,-missing</doclint>
</configuration>
</plugin>

</plugins>
</reporting>

Expand Down
6 changes: 3 additions & 3 deletions src/main/java/de/turnertech/frederick/Application.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
import de.turnertech.frederick.gui.tray.FrederickTrayIcon;

/**
* Currently the only executable method for this application. Used to analyse
* all balls with all methods.
* The core application class, hosting a few services which the sub modules can
* use to interact.
*/
public class Application {

Expand Down Expand Up @@ -83,7 +83,7 @@ public static void main(String[] args) {
mapFrame = new MapFrame();

// Technically, there is always a depolyment open. This is more a trigger to say "initialisation finished"
database.notifyActionListeners(Database.DEPLOYMENT_OPENED_EVENT);
database.notifyActionListeners(Database.DEPLOYMENT_OPENED_EVENT_ID);

SwingUtilities.invokeLater(() -> {
etbFrame.setVisible(true);
Expand Down
16 changes: 8 additions & 8 deletions src/main/java/de/turnertech/frederick/Database.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,15 @@ public class Database {

public static final String CURRENT_DEPLOYMENT_FILE_NAME = "Current";

public static final int DEPLOYMENT_OPENED_EVENT = "DEPLOYMENT_OPENED_EVENT".hashCode();
public static final int DEPLOYMENT_OPENED_EVENT_ID = "DEPLOYMENT_OPENED_EVENT".hashCode();

public static final Integer DEPLOYMENT_CLOSED_EVENT = "DEPLOYMENT_CLOSED_EVENT".hashCode();
public static final int DEPLOYMENT_CLOSED_EVENT_ID = "DEPLOYMENT_CLOSED_EVENT".hashCode();

public static final Integer DEPLOYMENT_SAVED_EVENT = "DEPLOYMENT_SAVED_EVENT".hashCode();
public static final int DEPLOYMENT_SAVED_EVENT_ID = "DEPLOYMENT_SAVED_EVENT".hashCode();

public static final int DEPLOYMENT_UPDATED_EVENT = "DEPLOYMENT_UPDATED_EVENT".hashCode();
public static final int DEPLOYMENT_UPDATED_EVENT_ID = "DEPLOYMENT_UPDATED_EVENT".hashCode();

public static final Integer DEPLOYMENT_DELETED_EVENT = "DEPLOYMENT_DELETED_EVENT".hashCode();
public static final int DEPLOYMENT_DELETED_EVENT_ID = "DEPLOYMENT_DELETED_EVENT".hashCode();

private final ArrayList<ActionListener> actionListeners = new ArrayList<>();

Expand Down Expand Up @@ -122,7 +122,7 @@ public void saveCurrentDeployment() {
Path pathToDeployment = getPathToDeployment(CURRENT_DEPLOYMENT_FILE_NAME).orElseThrow();
Serialization.serialize(currentDeployment, pathToDeployment.toString());
Logging.LOGGER.info("Saved current deployment");
notifyActionListeners(DEPLOYMENT_SAVED_EVENT);
notifyActionListeners(DEPLOYMENT_SAVED_EVENT_ID);
} catch (NoSuchElementException e) {
Logging.LOGGER.severe("Unable to get path to current deployment! Cannot save current deployment!");
} catch (Exception e) {
Expand Down Expand Up @@ -205,7 +205,7 @@ public void closeDeployment(final String name) {
// This triggers creation of the new empty deployment
getCurrentDeployment();
saveCurrentDeployment();
notifyActionListeners(DEPLOYMENT_CLOSED_EVENT);
notifyActionListeners(DEPLOYMENT_CLOSED_EVENT_ID);
} catch (IOException e) {
Logging.LOGGER.severe("Unable to close the deployment!");
}
Expand All @@ -216,7 +216,7 @@ public void deleteDeployment(final String name) {
Path pathToDeployment = getPathToDeployment(name).orElse(null);
Files.delete(pathToDeployment);
Logging.LOGGER.info(() -> "\"" + Application.CURRENT_USER + "\" hat den Einsatz \"" + name + "\" gelöscht");
notifyActionListeners(DEPLOYMENT_DELETED_EVENT);
notifyActionListeners(DEPLOYMENT_DELETED_EVENT_ID);
} catch (IOException e) {
Logging.LOGGER.severe("Unable to delete the deployment!");
}
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/de/turnertech/frederick/Service.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public void setBullseye(final Bullseye bullseye, final String logPosition) {
database.getCurrentDeployment().setBullseye(bullseye);
EtbEntry etbEntry = new EtbEntry(Date.from(Instant.now()), Application.CURRENT_USER, "Einsatzort festgelegt als " + logPosition);
database.getCurrentDeployment().getEtbEntries().add(etbEntry);
database.notifyActionListeners(Database.DEPLOYMENT_UPDATED_EVENT);
database.notifyActionListeners(Database.DEPLOYMENT_UPDATED_EVENT_ID);
database.saveCurrentDeployment();
}

Expand Down
4 changes: 4 additions & 0 deletions src/main/java/de/turnertech/frederick/data/Deployment.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

/**
* A Data class for serialising the deployments. This class should never have complex
* functionality, rather simple getters and setters for parameters.
*/
@XmlRootElement(name = "deployment")
public class Deployment {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,9 @@ public TableColumnModel getColumnModel() {

@Override
public void actionPerformed(ActionEvent e) {
if(Database.DEPLOYMENT_CLOSED_EVENT.equals(e.getID()) ||
Database.DEPLOYMENT_SAVED_EVENT.equals(e.getID()) ||
Database.DEPLOYMENT_DELETED_EVENT.equals(e.getID())) {
if(Database.DEPLOYMENT_CLOSED_EVENT_ID == e.getID() ||
Database.DEPLOYMENT_SAVED_EVENT_ID == e.getID() ||
Database.DEPLOYMENT_DELETED_EVENT_ID == e.getID()) {
fireTableDataChanged();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ else if (END_COMMAND.equals(e.getActionCommand())) {
Application.getDatabase().closeDeployment(name);
}
else if (EXPORT_COMMAND.equals(e.getActionCommand())) {

JOptionPane.showMessageDialog(this, "Not yet implemented", "Sorry", JOptionPane.INFORMATION_MESSAGE);
}
else if (PRINT_COMMAND.equals(e.getActionCommand())) {
Printing.printDeployment();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ public TableColumnModel getColumnModel() {

@Override
public void actionPerformed(ActionEvent e) {
if(Database.DEPLOYMENT_CLOSED_EVENT.equals(e.getID()) || Database.DEPLOYMENT_UPDATED_EVENT == e.getID()) {
if(Database.DEPLOYMENT_CLOSED_EVENT_ID == e.getID() || Database.DEPLOYMENT_UPDATED_EVENT_ID == e.getID()) {
this.fireTableDataChanged();
}
}
Expand Down
15 changes: 5 additions & 10 deletions src/main/java/de/turnertech/frederick/gui/map/MapBrowserPanel.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,39 +15,34 @@
import org.geotools.swing.JMapPane;

import de.turnertech.frederick.Resources;
import de.turnertech.frederick.gui.map.browser.JMouseCoordinateField;
import de.turnertech.frederick.gui.map.browser.JPropertiesPane;

/**
* A major panel containing usefull information about the map or selected object,
* as well as the ability to add new elements to the map.
*/
public class MapBrowserPanel extends JPanel {

public MapBrowserPanel(JMapPane mapPane) {

this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
this.setPreferredSize(new java.awt.Dimension(400, 0));

//Lay out the text controls and the labels.
JPanel propertiesPane = new JPropertiesPane(mapPane);
propertiesPane.setPreferredSize(new java.awt.Dimension(0, 300));
propertiesPane.setMinimumSize(new Dimension(0, 300));
propertiesPane.setMaximumSize(new Dimension(this.getPreferredSize().width, 300));

propertiesPane.add(new JMouseCoordinateField(mapPane));
this.add(propertiesPane);

// TODO: Create an Element panel with drag and drop elements
DefaultListModel<ImageIcon> listModel = new DefaultListModel<>();
JList<ImageIcon> elementsPane = new JList<>(listModel);
elementsPane.setLayoutOrientation(JList.HORIZONTAL_WRAP);
elementsPane.setVisibleRowCount(-1);

for (int row = 0; row < 100; ++row) {
listModel.addElement(Resources.getHelp24pxIcon());
}

JScrollPane selementsScrollPane = new JScrollPane(elementsPane);
selementsScrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
selementsScrollPane.setMinimumSize(new Dimension(200,200));
selementsScrollPane.setPreferredSize(new Dimension(200, Integer.MAX_VALUE));

JPanel elementsGroup = new JPanel();
elementsGroup.setLayout(new BoxLayout(elementsGroup, BoxLayout.Y_AXIS));
elementsGroup.add(new JTextField());
Expand Down
31 changes: 10 additions & 21 deletions src/main/java/de/turnertech/frederick/gui/map/MapFrame.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Arrays;
Expand All @@ -26,7 +25,6 @@
import org.geotools.styling.SLD;
import org.geotools.styling.Style;
import org.geotools.swing.JMapPane;
import org.geotools.swing.MapLayerTable;
import org.geotools.tile.TileService;
import org.geotools.tile.impl.osm.OSMService;
import org.geotools.tile.util.TileLayer;
Expand All @@ -37,7 +35,8 @@
import de.turnertech.frederick.Database;
import de.turnertech.frederick.Logging;
import de.turnertech.frederick.Resources;
import de.turnertech.frederick.gui.map.feature.Bullseye;
import de.turnertech.frederick.data.Bullseye;
import de.turnertech.frederick.gui.map.feature.BullseyeLayer;
import de.turnertech.frederick.gui.map.tool.ContextMenuTool;
import de.turnertech.frederick.gui.map.tool.PanTool;
import de.turnertech.frederick.gui.map.tool.ScrollTool;
Expand Down Expand Up @@ -66,7 +65,7 @@ public MapFrame() {
}

map.addLayer(layer);
map.addLayer(Bullseye.LAYER);
map.addLayer(BullseyeLayer.instance());
addGrids(map);

this.setTitle("Frederick - Einsatz Karte");
Expand All @@ -80,22 +79,11 @@ public MapFrame() {
mapPane.addMouseListener(new ContextMenuTool(mapPane));
this.add(mapPane, BorderLayout.CENTER);

MapLayerTable mapLayerTable = new MapLayerTable(mapPane);
mapLayerTable.setPreferredSize(new Dimension(200, -1));
//JSplitPane splitPane =
// new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, false, mapLayerTable, mapPane);
//this.add(splitPane, "grow");

mapToolbar = new MapToolbar(mapPane);

this.add(new MapBrowserPanel(mapPane), BorderLayout.LINE_START);
this.add(mapToolbar, BorderLayout.PAGE_START);
this.add(new StatusBar(), BorderLayout.PAGE_END);
//this.setMapContent(map);
//this.enableStatusBar(true);
//this.enableToolBar(true);
//this.enableLayerTable(true);
//this.initComponents();
this.setSize(1024, 768);
this.setDefaultCloseOperation(HIDE_ON_CLOSE);
}
Expand Down Expand Up @@ -138,17 +126,18 @@ private void addGrids(MapContent map) {

@Override
public void actionPerformed(ActionEvent actionEvent) {
if(actionEvent.getID() == Database.DEPLOYMENT_UPDATED_EVENT || actionEvent.getID() == Database.DEPLOYMENT_OPENED_EVENT) {
if(actionEvent.getID() == Database.DEPLOYMENT_UPDATED_EVENT_ID || actionEvent.getID() == Database.DEPLOYMENT_OPENED_EVENT_ID) {
if(Application.getService().getBullseye().isPresent()) {
de.turnertech.frederick.data.Bullseye bullsFromStorage = Application.getService().getBullseye().get();
Bullseye.set(new DirectPosition2D(DefaultGeographicCRS.WGS84, bullsFromStorage.getX(), bullsFromStorage.getY()));
Bullseye bullsFromStorage = Application.getService().getBullseye().get();
BullseyeLayer.instance().set(new DirectPosition2D(DefaultGeographicCRS.WGS84, bullsFromStorage.getX(), bullsFromStorage.getY()));
mapToolbar.setFocusBullseyeActionEnabled(true);
} else {
if(Bullseye.getPosition().isPresent()) {
Bullseye.clear();
}
BullseyeLayer.instance().clear();
mapToolbar.setFocusBullseyeActionEnabled(false);
}
} else if(actionEvent.getID() == Database.DEPLOYMENT_CLOSED_EVENT_ID) {
BullseyeLayer.instance().clear();
mapToolbar.setFocusBullseyeActionEnabled(false);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import org.geotools.swing.action.MapAction;

import de.turnertech.frederick.Logging;
import de.turnertech.frederick.gui.map.feature.Bullseye;
import de.turnertech.frederick.gui.map.feature.BullseyeLayer;

public class FocusBullseyeAction extends MapAction {

Expand All @@ -19,7 +19,7 @@ public FocusBullseyeAction(MapPane mapPane) {

@Override
public void actionPerformed(ActionEvent ev) {
Optional<ReferencedEnvelope> bullseye = Bullseye.getArea(getMapPane());
Optional<ReferencedEnvelope> bullseye = BullseyeLayer.getArea(getMapPane());
if(bullseye.isEmpty()) {
Logging.LOGGER.warning("Bullseye not set. Cannot focus on bullseye untill it is set.");
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@

public class SetBullseyeAction extends AbstractAction {

final MapPane mapPane;
final transient MapPane mapPane;

final Point2D screenPoint;
final transient Point2D screenPoint;

public SetBullseyeAction(final MapPane mapPane, int x, int y) {
super("Bullseye Setzen");
Expand Down Expand Up @@ -50,7 +50,6 @@ public void actionPerformed(ActionEvent e) {
return;
}

// ToDo - Consider centralising this in Application? Maybe using events? Maybe this is fine as it is?
de.turnertech.frederick.data.Bullseye dataToStore = new de.turnertech.frederick.data.Bullseye(crs84Position.getX(), crs84Position.getY());
Application.getService().setBullseye(dataToStore, MapHelper.format(crs84Position));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package de.turnertech.frederick.gui.map.browser;

import java.awt.Dimension;
import java.awt.GridBagLayout;

import javax.swing.BorderFactory;
Expand All @@ -11,11 +12,15 @@ public class JPropertiesPane extends JPanel {

public JPropertiesPane(JMapPane mapPane) {
super(new GridBagLayout());

this.setPreferredSize(new java.awt.Dimension(0, 300));
this.setMinimumSize(new Dimension(0, 300));
this.setBorder(
BorderFactory.createCompoundBorder(
BorderFactory.createTitledBorder("Eigenschaften"),
BorderFactory.createEmptyBorder(5,5,5,5)));
BorderFactory.createCompoundBorder(
BorderFactory.createTitledBorder("Eigenschaften"),
BorderFactory.createEmptyBorder(5,5,5,5))
);

this.add(new JMouseCoordinateField(mapPane));
}

}
Loading

0 comments on commit 23f938f

Please sign in to comment.