mirror of
https://github.com/michaelrausch/Party-Parrots-At-Sea.git
synced 2026-05-09 14:28:43 +00:00
Began fixing bugs with caused by asynchronous listener calls.
#bug
This commit is contained in:
@@ -1,11 +0,0 @@
|
||||
package seng302.visualiser;
|
||||
|
||||
import seng302.model.stream.packets.StreamPacket;
|
||||
|
||||
/**
|
||||
* Functional interface for receiving packets from client socket.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface ClientSocketListener {
|
||||
void newPacket(StreamPacket packet);
|
||||
}
|
||||
@@ -5,14 +5,16 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.Socket;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.zip.CRC32;
|
||||
import java.util.zip.Checksum;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.scene.control.Alert;
|
||||
import javafx.scene.control.Alert.AlertType;
|
||||
import seng302.model.stream.packets.StreamPacket;
|
||||
import seng302.server.messages.BoatActionMessage;
|
||||
import seng302.server.messages.Message;
|
||||
@@ -22,6 +24,21 @@ import seng302.server.messages.Message;
|
||||
* its own thread.
|
||||
*/
|
||||
public class ClientToServerThread implements Runnable {
|
||||
|
||||
/**
|
||||
* Functional interface for receiving packets from client socket.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface ClientSocketListener {
|
||||
void newPacket();
|
||||
}
|
||||
|
||||
private class ByteReadException extends Exception {
|
||||
private ByteReadException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
private static final int LOG_LEVEL = 1;
|
||||
|
||||
private Queue<StreamPacket> streamPackets = new ConcurrentLinkedQueue<>();
|
||||
@@ -34,8 +51,9 @@ public class ClientToServerThread implements Runnable {
|
||||
|
||||
private int clientId;
|
||||
|
||||
private Boolean updateClient = true;
|
||||
// private Boolean updateClient = true;
|
||||
private ByteArrayOutputStream crcBuffer;
|
||||
private boolean socketOpen = true;
|
||||
|
||||
/**
|
||||
* Constructor for ClientToServerThread which takes in ipAddress and portNumber and attempts to
|
||||
@@ -53,7 +71,6 @@ public class ClientToServerThread implements Runnable {
|
||||
socket = new Socket(ipAddress, portNumber);
|
||||
is = socket.getInputStream();
|
||||
os = socket.getOutputStream();
|
||||
|
||||
Integer allocatedID = threeWayHandshake();
|
||||
if (allocatedID != null) {
|
||||
clientId = allocatedID;
|
||||
@@ -90,8 +107,19 @@ public class ClientToServerThread implements Runnable {
|
||||
int sync1;
|
||||
int sync2;
|
||||
// TODO: 14/07/17 wmu16 - Work out how to fix this while loop
|
||||
while(true) { /**REMOVED SOMETHING HERE ClientState.isConnectedToHost() */
|
||||
while(socketOpen) { /**REMOVED SOMETHING HERE ClientState.isConnectedToHost() */
|
||||
System.out.println("socket.isConnected() = " + socket.isConnected());
|
||||
try {
|
||||
//Perform a write if it is time to as delegated by the MainServerThread
|
||||
// if (updateClient) {
|
||||
// // TODO: 13/07/17 wmu16 - Write out game state - some function that would write all appropriate messages to this output stream
|
||||
//// try {
|
||||
//// GameState.outputState(os);
|
||||
//// } catch (IOException e) {
|
||||
//// System.out.println("IO error in server thread upon writing to output stream");
|
||||
//// }
|
||||
// updateClient = false;
|
||||
// }
|
||||
crcBuffer = new ByteArrayOutputStream();
|
||||
sync1 = readByte();
|
||||
sync2 = readByte();
|
||||
@@ -109,29 +137,33 @@ public class ClientToServerThread implements Runnable {
|
||||
long packetCrc = Message.bytesToLong(getBytes(4));
|
||||
if (computedCrc == packetCrc) {
|
||||
// streamPackets.add(new StreamPacket(type, payloadLength, timeStamp, payload));
|
||||
for (ClientSocketListener csl : listeners)
|
||||
csl.newPacket(new StreamPacket(type, payloadLength, timeStamp, payload));
|
||||
// for (ClientSocketListener csl : listeners)
|
||||
// csl.newPacket(new StreamPacket(type, payloadLength, timeStamp, payload));
|
||||
if (streamPackets.size() > 0) {
|
||||
streamPackets.add(new StreamPacket(type, payloadLength, timeStamp, payload));
|
||||
} else {
|
||||
streamPackets.add(new StreamPacket(type, payloadLength, timeStamp, payload));
|
||||
for (ClientSocketListener csl : listeners)
|
||||
csl.newPacket();
|
||||
}
|
||||
} else {
|
||||
clientLog("Packet has been dropped", 1);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
} catch (ByteReadException e) {
|
||||
closeSocket();
|
||||
Platform.runLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Alert alert = new Alert(AlertType.ERROR);
|
||||
alert.setHeaderText("Host has disconnected");
|
||||
alert.setContentText("Cannot find Server");
|
||||
alert.showAndWait();
|
||||
}
|
||||
});
|
||||
clientLog("Disconnected from server", 1);
|
||||
Platform.runLater(() -> {
|
||||
Alert alert = new Alert(AlertType.ERROR);
|
||||
alert.setHeaderText("Host has disconnected");
|
||||
alert.setContentText("Cannot find Server");
|
||||
alert.showAndWait();
|
||||
});
|
||||
clientLog(e.getMessage(), 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// closeSocket();
|
||||
// clientLog("Disconnected from server", 0);
|
||||
closeSocket();
|
||||
clientLog("Closed connection to Server", 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -147,7 +179,6 @@ public class ClientToServerThread implements Runnable {
|
||||
ourSourceID = is.read();
|
||||
} catch (IOException e) {
|
||||
clientLog("Three way handshake failed", 1);
|
||||
|
||||
}
|
||||
if (ourSourceID != null) {
|
||||
try {
|
||||
@@ -174,7 +205,7 @@ public class ClientToServerThread implements Runnable {
|
||||
}
|
||||
|
||||
|
||||
public void closeSocket() {
|
||||
private void closeSocket() {
|
||||
try {
|
||||
socket.close();
|
||||
} catch (IOException e) {
|
||||
@@ -182,6 +213,14 @@ public class ClientToServerThread implements Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
public void setSocketToClose () {
|
||||
socketOpen = false;
|
||||
}
|
||||
|
||||
public Queue<StreamPacket> getPacketQueue () {
|
||||
return streamPackets;
|
||||
}
|
||||
|
||||
public void addStreamObserver (ClientSocketListener streamListener) {
|
||||
listeners.add(streamListener);
|
||||
}
|
||||
@@ -190,7 +229,7 @@ public class ClientToServerThread implements Runnable {
|
||||
listeners.remove(streamListener);
|
||||
}
|
||||
|
||||
private int readByte() throws Exception {
|
||||
private int readByte() throws ByteReadException {
|
||||
int currentByte = -1;
|
||||
try {
|
||||
currentByte = is.read();
|
||||
@@ -199,12 +238,12 @@ public class ClientToServerThread implements Runnable {
|
||||
clientLog("Read byte failed", 1);
|
||||
}
|
||||
if (currentByte == -1) {
|
||||
throw new Exception();
|
||||
throw new ByteReadException("InputStream reach end of stream");
|
||||
}
|
||||
return currentByte;
|
||||
}
|
||||
|
||||
private byte[] getBytes(int n) throws Exception {
|
||||
private byte[] getBytes(int n) throws ByteReadException {
|
||||
byte[] bytes = new byte[n];
|
||||
for (int i = 0; i < n; i++) {
|
||||
bytes[i] = (byte) readByte();
|
||||
@@ -212,7 +251,7 @@ public class ClientToServerThread implements Runnable {
|
||||
return bytes;
|
||||
}
|
||||
|
||||
private void skipBytes(long n) throws Exception {
|
||||
private void skipBytes(long n) throws ByteReadException {
|
||||
for (int i = 0; i < n; i++) {
|
||||
readByte();
|
||||
}
|
||||
|
||||
@@ -3,29 +3,28 @@ package seng302.visualiser;
|
||||
import java.io.IOException;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.TimeZone;
|
||||
import javafx.application.Platform;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.fxml.FXMLLoader;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.layout.Pane;
|
||||
import seng302.gameServer.GameState;
|
||||
import seng302.gameServer.MainServerThread;
|
||||
import seng302.model.Yacht;
|
||||
import seng302.model.RaceState;
|
||||
import seng302.model.Yacht;
|
||||
import seng302.model.mark.Mark;
|
||||
import seng302.model.stream.parser.PositionUpdateData.DeviceType;
|
||||
import seng302.model.stream.parser.MarkRoundingData;
|
||||
import seng302.model.stream.parser.RaceStatusData;
|
||||
import seng302.model.stream.xml.parser.RaceXMLData;
|
||||
import seng302.model.stream.parser.StreamParser;
|
||||
import seng302.model.stream.xml.parser.RegattaXMLData;
|
||||
import seng302.model.stream.xml.parser.XMLParser;
|
||||
import seng302.model.stream.parser.PositionUpdateData;
|
||||
import seng302.model.stream.packets.StreamPacket;
|
||||
import seng302.visualiser.ClientToServerThread;
|
||||
import seng302.model.stream.parser.MarkRoundingData;
|
||||
import seng302.model.stream.parser.PositionUpdateData;
|
||||
import seng302.model.stream.parser.PositionUpdateData.DeviceType;
|
||||
import seng302.model.stream.parser.RaceStatusData;
|
||||
import seng302.utilities.StreamParser;
|
||||
import seng302.model.stream.xml.parser.RaceXMLData;
|
||||
import seng302.model.stream.xml.parser.RegattaXMLData;
|
||||
import seng302.utilities.XMLParser;
|
||||
import seng302.visualiser.controllers.LobbyController;
|
||||
import seng302.visualiser.controllers.LobbyController.CloseStatus;
|
||||
import seng302.visualiser.controllers.RaceViewController;
|
||||
@@ -61,9 +60,10 @@ public class GameClient {
|
||||
}
|
||||
LobbyController lobbyController = loadLobby("/views/LobbyView.fxml");
|
||||
lobbyController.setPlayerListSource(lobbyList);
|
||||
lobbyController.disableReadyButton();
|
||||
lobbyController.setTitle("Connected to host - IP : " + ipAddress + " Port : " + portNumber);
|
||||
lobbyController.addCloseListener((exitCause) -> this.loadStartScreen());
|
||||
socketThread.addStreamObserver(this::parsePacket);
|
||||
socketThread.addStreamObserver(this::parsePackets);
|
||||
}
|
||||
|
||||
public void runAsHost (String ipAddress, Integer portNumber) {
|
||||
@@ -74,13 +74,13 @@ public class GameClient {
|
||||
ioe.printStackTrace();
|
||||
System.out.println("Unable to make local connection to host...");
|
||||
}
|
||||
LobbyController lobbyController = loadLobby("/views/HostLobbyView.fxml");
|
||||
socketThread.addStreamObserver(this::parsePackets);
|
||||
LobbyController lobbyController = loadLobby("/views/LobbyView.fxml");
|
||||
lobbyController.setPlayerListSource(GameState.getObservablePlayers());
|
||||
lobbyController.setTitle("Hosting Lobby - IP : " + ipAddress + " Port : " + portNumber);
|
||||
lobbyController.addCloseListener(exitCause -> {
|
||||
if (exitCause == CloseStatus.READY) {
|
||||
server.startGame();
|
||||
socketThread.addStreamObserver(this::parsePacket);
|
||||
} else if (exitCause == CloseStatus.LEAVE) {
|
||||
loadStartScreen();
|
||||
}
|
||||
@@ -88,11 +88,11 @@ public class GameClient {
|
||||
}
|
||||
|
||||
private void loadStartScreen () {
|
||||
socketThread.closeSocket();
|
||||
socketThread.setSocketToClose();
|
||||
socketThread = null;
|
||||
if (server != null) {
|
||||
// TODO: 26/07/17 cir27 - handle disconnecting
|
||||
server.shutDown();
|
||||
// server.shutDown();
|
||||
server = null;
|
||||
}
|
||||
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/views/StartScreenView.fxml"));
|
||||
@@ -125,68 +125,78 @@ public class GameClient {
|
||||
// if (courseData.getParticipants().contains(id))
|
||||
// racingBoats.put(id, boat);
|
||||
// });
|
||||
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/views/RaceView.fxml"));
|
||||
raceView = fxmlLoader.getController();
|
||||
FXMLLoader fxmlLoader = new FXMLLoader(RaceViewController.class.getResource("/views/RaceView.fxml"));
|
||||
// raceView = fxmlLoader.getController();
|
||||
try {
|
||||
holderPane.getChildren().add(fxmlLoader.load());
|
||||
Node node = fxmlLoader.load();
|
||||
Platform.runLater(() -> {
|
||||
holderPane.getChildren().clear();
|
||||
holderPane.getChildren().add(node);
|
||||
});
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
raceView = fxmlLoader.getController();
|
||||
raceView.loadRace(allBoatsMap, courseData, raceState);
|
||||
}
|
||||
|
||||
private void parsePacket(StreamPacket packet) {
|
||||
switch (packet.getType()) {
|
||||
case RACE_STATUS:
|
||||
processRaceStatusUpdate(StreamParser.extractRaceStatus(packet));
|
||||
break;
|
||||
private void parsePackets() {
|
||||
while (socketThread.getPacketQueue().peek() != null) {
|
||||
StreamPacket packet = socketThread.getPacketQueue().poll();
|
||||
switch (packet.getType()) {
|
||||
case RACE_STATUS:
|
||||
processRaceStatusUpdate(StreamParser.extractRaceStatus(packet));
|
||||
startRaceIfAllDataReceived();
|
||||
break;
|
||||
|
||||
case REGATTA_XML:
|
||||
System.out.println("REGATTA XML");
|
||||
regattaData = XMLParser.parseRegatta(
|
||||
StreamParser.extractXmlMessage(packet)
|
||||
);
|
||||
raceState.setTimeZone(
|
||||
TimeZone.getTimeZone(
|
||||
ZoneId.ofOffset("UTC", ZoneOffset.ofHours(regattaData.getUtcOffset()))
|
||||
)
|
||||
);
|
||||
startRaceIfAllDataReceived();
|
||||
break;
|
||||
case REGATTA_XML:
|
||||
System.out.println("REGATTA XML");
|
||||
regattaData = XMLParser.parseRegatta(
|
||||
StreamParser.extractXmlMessage(packet)
|
||||
);
|
||||
raceState.setTimeZone(
|
||||
TimeZone.getTimeZone(
|
||||
ZoneId.ofOffset("UTC", ZoneOffset.ofHours(regattaData.getUtcOffset()))
|
||||
)
|
||||
);
|
||||
// startRaceIfAllDataReceived();
|
||||
break;
|
||||
|
||||
case RACE_XML:
|
||||
System.out.println("RACE XML");
|
||||
courseData = XMLParser.parseRace(
|
||||
StreamParser.extractXmlMessage(packet)
|
||||
);
|
||||
if (raceView != null) {
|
||||
raceView.updateRaceData(courseData);
|
||||
}
|
||||
startRaceIfAllDataReceived();
|
||||
break;
|
||||
case RACE_XML:
|
||||
System.out.println("RACE XML");
|
||||
courseData = XMLParser.parseRace(
|
||||
StreamParser.extractXmlMessage(packet)
|
||||
);
|
||||
if (raceView != null) {
|
||||
raceView.updateRaceData(courseData);
|
||||
}
|
||||
// startRaceIfAllDataReceived();
|
||||
break;
|
||||
|
||||
case BOAT_XML:
|
||||
System.out.println("BOAT XML");
|
||||
allBoatsMap = XMLParser.parseBoats(
|
||||
StreamParser.extractXmlMessage(packet)
|
||||
);
|
||||
lobbyList.clear();
|
||||
allBoatsMap.forEach((id, boat) -> lobbyList.add(id.toString() + boat.getBoatName()));
|
||||
allBoatsMap.forEach((i, b) -> System.out.println(b.getBoatName()));
|
||||
startRaceIfAllDataReceived();
|
||||
break;
|
||||
case BOAT_XML:
|
||||
System.out.println("BOAT XML");
|
||||
allBoatsMap = XMLParser.parseBoats(
|
||||
StreamParser.extractXmlMessage(packet)
|
||||
);
|
||||
lobbyList.clear();
|
||||
allBoatsMap
|
||||
.forEach((id, boat) -> lobbyList.add(id.toString() + boat.getBoatName()));
|
||||
allBoatsMap.forEach((i, b) -> System.out.println(b.getBoatName()));
|
||||
// startRaceIfAllDataReceived();
|
||||
break;
|
||||
|
||||
case RACE_START_STATUS:
|
||||
raceState.updateState(StreamParser.extractRaceStartStatus(packet));
|
||||
break;
|
||||
case RACE_START_STATUS:
|
||||
raceState.updateState(StreamParser.extractRaceStartStatus(packet));
|
||||
break;
|
||||
|
||||
case BOAT_LOCATION:
|
||||
updatePosition(StreamParser.extractBoatLocation(packet));
|
||||
break;
|
||||
case BOAT_LOCATION:
|
||||
updatePosition(StreamParser.extractBoatLocation(packet));
|
||||
break;
|
||||
|
||||
case MARK_ROUNDING:
|
||||
updateMarkRounding(StreamParser.extractMarkRounding(packet));
|
||||
break;
|
||||
case MARK_ROUNDING:
|
||||
updateMarkRounding(StreamParser.extractMarkRounding(packet));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -243,6 +253,7 @@ public class GameClient {
|
||||
yacht.setEstimateTimeAtFinish(boatData[2]);
|
||||
int legNumber = (int) boatData[3];
|
||||
yacht.setLegNumber(legNumber);
|
||||
yacht.setBoatStatus((int) boatData[4]);
|
||||
if (legNumber != yacht.getLegNumber()) {
|
||||
int placing = 1;
|
||||
for (Yacht otherYacht : allBoatsMap.values()) {
|
||||
@@ -257,7 +268,7 @@ public class GameClient {
|
||||
}
|
||||
|
||||
private void close () {
|
||||
socketThread.closeSocket();
|
||||
socketThread.setSocketToClose();
|
||||
}
|
||||
|
||||
// /** Handle the key-pressed event from the text field. */
|
||||
|
||||
@@ -22,20 +22,18 @@ import javafx.scene.layout.Pane;
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.scene.shape.Polygon;
|
||||
import javafx.scene.text.Text;
|
||||
import seng302.model.Limit;
|
||||
import seng302.visualiser.fxObjects.AnnotationBox;
|
||||
import seng302.visualiser.fxObjects.BoatObject;
|
||||
import seng302.visualiser.fxObjects.MarkObject;
|
||||
import seng302.model.Colors;
|
||||
import seng302.model.Limit;
|
||||
import seng302.model.Yacht;
|
||||
import seng302.model.map.Boundary;
|
||||
import seng302.model.map.CanvasMap;
|
||||
import seng302.model.mark.GateMark;
|
||||
import seng302.model.mark.Mark;
|
||||
import seng302.model.mark.MarkType;
|
||||
import seng302.model.mark.SingleMark;
|
||||
import seng302.utilities.GeoPoint;
|
||||
import seng302.utilities.GeoUtility;
|
||||
import seng302.visualiser.fxObjects.AnnotationBox;
|
||||
import seng302.visualiser.fxObjects.BoatObject;
|
||||
import seng302.visualiser.fxObjects.MarkObject;
|
||||
|
||||
/**
|
||||
* Created by cir27 on 20/07/17.
|
||||
@@ -45,11 +43,11 @@ public class GameView extends Pane {
|
||||
private ObservableList<Node> gameObjects;
|
||||
private ImageView mapImage;
|
||||
|
||||
private final int BUFFER_SIZE = 50;
|
||||
private final int PANEL_WIDTH = 1260; // it should be 1280 but, minors 40 to cancel the bias.
|
||||
private final int PANEL_HEIGHT = 960;
|
||||
private final int CANVAS_WIDTH = 1100;
|
||||
private final int CANVAS_HEIGHT = 920;
|
||||
private double bufferSize = 50;
|
||||
private double panelWidth = 1260; // it should be 1280 but, minors 40 to cancel the bias.
|
||||
private double panelHeight = 960;
|
||||
private double canvasWidth = 1100;
|
||||
private double canvasHeight = 920;
|
||||
private boolean horizontalInversion = false;
|
||||
|
||||
private double distanceScaleFactor;
|
||||
@@ -63,13 +61,14 @@ public class GameView extends Pane {
|
||||
private double metersPerPixelX;
|
||||
private double metersPerPixelY;
|
||||
|
||||
private Map<SingleMark, MarkObject> markObjects = new HashMap<>();
|
||||
private Map<Yacht, BoatObject> boatObjects = new HashMap<>();
|
||||
private List<AnnotationBox> annotations = new ArrayList<>();
|
||||
|
||||
private Text fpsDisplay = new Text();
|
||||
|
||||
private Polygon raceBorder = new Polygon();
|
||||
/* Note that if either of these is null then values for it have not been added and the other
|
||||
should be used as the limits of the map. */
|
||||
private Polygon raceBorder;
|
||||
private Map<SingleMark, MarkObject> markObjects = new HashMap<>();
|
||||
|
||||
//FRAME RATE
|
||||
private Double frameRate = 60.0;
|
||||
@@ -205,9 +204,13 @@ public class GameView extends Pane {
|
||||
* in a compound mark etc..
|
||||
*/
|
||||
public void updateBorder(List<Limit> border) {
|
||||
raceBorder.setStroke(new Color(0.0f, 0.0f, 0.74509807f, 1));
|
||||
raceBorder.setStrokeWidth(3);
|
||||
raceBorder.setFill(new Color(0,0,0,0));
|
||||
if (raceBorder == null) {
|
||||
raceBorder = new Polygon();
|
||||
raceBorder.setStroke(new Color(0.0f, 0.0f, 0.74509807f, 1));
|
||||
raceBorder.setStrokeWidth(3);
|
||||
raceBorder.setFill(new Color(0,0,0,0));
|
||||
findCanvasScaling();
|
||||
}
|
||||
List<Double> boundaryPoints = new ArrayList<>();
|
||||
for (Limit limit : border) {
|
||||
Point2D location = findScaledXY(limit.getLat(), limit.getLng());
|
||||
@@ -393,24 +396,24 @@ public class GameView extends Pane {
|
||||
|
||||
if (scaleDirection == ScaleDirection.HORIZONTAL) {
|
||||
referenceAngle = Math.abs(Mark.calculateHeadingRad(referencePoint, minLonPoint));
|
||||
referencePointX = BUFFER_SIZE + distanceScaleFactor * Math.sin(referenceAngle) * Mark.calculateDistance(referencePoint, minLonPoint);
|
||||
referencePointX = bufferSize + distanceScaleFactor * Math.sin(referenceAngle) * Mark.calculateDistance(referencePoint, minLonPoint);
|
||||
|
||||
referenceAngle = Math.abs(Mark.calculateHeadingRad(referencePoint, maxLatPoint));
|
||||
referencePointY = canvasHeight - (BUFFER_SIZE + BUFFER_SIZE);
|
||||
referencePointY = canvasHeight - (bufferSize + bufferSize);
|
||||
referencePointY -= distanceScaleFactor * Math.cos(referenceAngle) * Mark.calculateDistance(referencePoint, maxLatPoint);
|
||||
referencePointY = referencePointY / 2;
|
||||
referencePointY += BUFFER_SIZE;
|
||||
referencePointY += bufferSize;
|
||||
referencePointY += distanceScaleFactor * Math.cos(referenceAngle) * Mark.calculateDistance(referencePoint, maxLatPoint);
|
||||
} else {
|
||||
referencePointY = canvasHeight - BUFFER_SIZE;
|
||||
referencePointY = canvasHeight - bufferSize;
|
||||
|
||||
referenceAngle = Math.abs(Mark.calculateHeadingRad(referencePoint, minLonPoint));
|
||||
referencePointX = BUFFER_SIZE;
|
||||
referencePointX = bufferSize;
|
||||
referencePointX += distanceScaleFactor * Math.sin(referenceAngle) * Mark.calculateDistance(referencePoint, minLonPoint);
|
||||
referencePointX += ((canvasWidth - (BUFFER_SIZE + BUFFER_SIZE)) - (minLonToMaxLon * distanceScaleFactor)) / 2;
|
||||
referencePointX += ((canvasWidth - (bufferSize + bufferSize)) - (minLonToMaxLon * distanceScaleFactor)) / 2;
|
||||
}
|
||||
if(horizontalInversion) {
|
||||
referencePointX = canvasWidth - BUFFER_SIZE - (referencePointX - BUFFER_SIZE);
|
||||
referencePointX = canvasWidth - bufferSize - (referencePointX - bufferSize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -434,10 +437,10 @@ public class GameView extends Pane {
|
||||
double horiDistance =
|
||||
Math.cos(horiAngle) * Mark.calculateDistance(minLonPoint, maxLonPoint);
|
||||
|
||||
double vertScale = (canvasHeight - (BUFFER_SIZE + BUFFER_SIZE)) / vertDistance;
|
||||
double vertScale = (canvasHeight - (bufferSize + bufferSize)) / vertDistance;
|
||||
|
||||
if ((horiDistance * vertScale) > (canvasWidth - (BUFFER_SIZE + BUFFER_SIZE))) {
|
||||
distanceScaleFactor = (canvasWidth - (BUFFER_SIZE + BUFFER_SIZE)) / horiDistance;
|
||||
if ((horiDistance * vertScale) > (canvasWidth - (bufferSize + bufferSize))) {
|
||||
distanceScaleFactor = (canvasWidth - (bufferSize + bufferSize)) / horiDistance;
|
||||
scaleDirection = ScaleDirection.HORIZONTAL;
|
||||
} else {
|
||||
distanceScaleFactor = vertScale;
|
||||
@@ -479,7 +482,7 @@ public class GameView extends Pane {
|
||||
yAxisLocation += Math.round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference);
|
||||
}
|
||||
if(horizontalInversion) {
|
||||
xAxisLocation = canvasWidth - BUFFER_SIZE - (xAxisLocation - BUFFER_SIZE);
|
||||
xAxisLocation = canvasWidth - bufferSize - (xAxisLocation - bufferSize);
|
||||
}
|
||||
return new Point2D(xAxisLocation, yAxisLocation);
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ import javafx.scene.layout.AnchorPane;
|
||||
import javafx.scene.layout.GridPane;
|
||||
import javafx.scene.layout.Pane;
|
||||
import seng302.model.Yacht;
|
||||
import seng302.model.stream.parser.StreamParser;
|
||||
|
||||
public class FinishScreenViewController implements Initializable {
|
||||
|
||||
|
||||
@@ -2,8 +2,9 @@ package seng302.visualiser.controllers;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.ResourceBundle;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.fxml.FXML;
|
||||
@@ -11,7 +12,6 @@ import javafx.fxml.FXMLLoader;
|
||||
import javafx.fxml.Initializable;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.ListView;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.layout.AnchorPane;
|
||||
import javafx.scene.layout.GridPane;
|
||||
@@ -36,9 +36,6 @@ public class LobbyController implements Initializable {
|
||||
void notify(CloseStatus exitCause);
|
||||
}
|
||||
|
||||
@FXML
|
||||
private ListView<String> competitorsListView;
|
||||
|
||||
@FXML
|
||||
private GridPane lobbyScreen;
|
||||
@FXML
|
||||
@@ -259,10 +256,13 @@ public class LobbyController implements Initializable {
|
||||
@FXML
|
||||
public void leaveLobbyButtonPressed() {
|
||||
// TODO: 10/07/17 wmu16 - Finish function!
|
||||
setContentPane("/views/StartScreenView.fxml");
|
||||
// setContentPane("/views/StartScreenView.fxml");
|
||||
GameState.setCurrentStage(GameStages.CANCELLED);
|
||||
// TODO: 20/07/17 wmu16 - Implement some way of terminating the game
|
||||
// ClientState.setConnectedToHost(false);
|
||||
for (LobbyCloseListener readyListener : lobbyListeners)
|
||||
readyListener.notify(CloseStatus.LEAVE);
|
||||
|
||||
}
|
||||
|
||||
@FXML
|
||||
@@ -309,11 +309,15 @@ public class LobbyController implements Initializable {
|
||||
}
|
||||
|
||||
public void setPlayerListSource (ObservableList<String> players) {
|
||||
if (competitorsListView != null)
|
||||
competitorsListView.setItems(players);
|
||||
if (firstListView != null) {
|
||||
// if (competitorsListView != null)
|
||||
// competitorsListView.setItems(players);
|
||||
// if (firstListView != null) {
|
||||
firstListView.setItems(players);
|
||||
firstImageView.setVisible(false);
|
||||
}
|
||||
// }
|
||||
}
|
||||
|
||||
public void disableReadyButton () {
|
||||
readyButton.setDisable(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
package seng302.visualiser.controllers;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import javafx.animation.KeyFrame;
|
||||
import javafx.animation.Timeline;
|
||||
@@ -9,7 +15,6 @@ import javafx.fxml.FXML;
|
||||
import javafx.fxml.FXMLLoader;
|
||||
import javafx.geometry.Point2D;
|
||||
import javafx.scene.Scene;
|
||||
|
||||
import javafx.scene.chart.LineChart;
|
||||
import javafx.scene.chart.NumberAxis;
|
||||
import javafx.scene.chart.XYChart;
|
||||
@@ -30,7 +35,9 @@ import javafx.stage.Stage;
|
||||
import javafx.stage.StageStyle;
|
||||
import javafx.util.Duration;
|
||||
import javafx.util.StringConverter;
|
||||
import seng302.model.Corner;
|
||||
import seng302.model.RaceState;
|
||||
import seng302.model.Yacht;
|
||||
import seng302.model.mark.Mark;
|
||||
import seng302.model.stream.xml.parser.RaceXMLData;
|
||||
import seng302.visualiser.GameView;
|
||||
import seng302.visualiser.controllers.annotations.Annotation;
|
||||
@@ -38,11 +45,6 @@ import seng302.visualiser.controllers.annotations.ImportantAnnotationController;
|
||||
import seng302.visualiser.controllers.annotations.ImportantAnnotationDelegate;
|
||||
import seng302.visualiser.controllers.annotations.ImportantAnnotationsState;
|
||||
import seng302.visualiser.fxObjects.BoatObject;
|
||||
import seng302.model.*;
|
||||
import seng302.model.mark.Mark;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Controller class that manages the display of a race
|
||||
@@ -92,6 +94,8 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
||||
raceSparkLine.getYAxis().setAutoRanging(false);
|
||||
sparklineYAxis.setTickMarkVisible(false);
|
||||
|
||||
positionVbox.getStylesheets().add(getClass().getResource("/css/master.css").toString());
|
||||
|
||||
selectAnnotationBtn.setOnAction(event -> loadSelectAnnotationView());
|
||||
}
|
||||
|
||||
@@ -189,11 +193,10 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
annotationSlider.setValue(2);
|
||||
annotationSlider.valueProperty().addListener((obs, oldVal, newVal) ->
|
||||
setAnnotations((int) annotationSlider.getValue())
|
||||
);
|
||||
annotationSlider.setValue(2);
|
||||
}
|
||||
|
||||
|
||||
@@ -368,14 +371,15 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
||||
*/
|
||||
private void updateOrder() {
|
||||
positionVbox.getChildren().clear();
|
||||
positionVbox.getChildren().removeAll();
|
||||
positionVbox.getStylesheets().add(getClass().getResource("/css/master.css").toString());
|
||||
// positionVbox.getChildren().removeAll();
|
||||
// positionVbox.getStylesheets().add(getClass().getResource("/css/master.css").toString());
|
||||
|
||||
// list of racing yacht id
|
||||
List<Yacht> sorted = new ArrayList<>(participants.values());
|
||||
sorted.sort(Comparator.comparingInt(Yacht::getPositionInteger));
|
||||
|
||||
for (Yacht yacht : sorted) {
|
||||
// System.out.println("yacht == null " + String.valueOf(yacht == null));
|
||||
if (yacht.getBoatStatus() == 3) { // 3 is finish status
|
||||
Text textToAdd = new Text(yacht.getPositionInteger() + ". " +
|
||||
yacht.getShortName() + " (Finished)");
|
||||
@@ -389,6 +393,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
||||
textToAdd.setStyle("");
|
||||
positionVbox.getChildren().add(textToAdd);
|
||||
}
|
||||
// System.out.println("finished a loop :))))))))))))");
|
||||
}
|
||||
// participants.forEach((id, yacht) ->{
|
||||
// Text textToAdd = new Text(yacht.getPosition() + ". " +
|
||||
|
||||
@@ -1,25 +1,17 @@
|
||||
package seng302.visualiser.controllers;
|
||||
|
||||
import java.net.Inet4Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.NetworkInterface;
|
||||
import java.net.URL;
|
||||
import java.util.Enumeration;
|
||||
import java.util.ResourceBundle;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.fxml.FXMLLoader;
|
||||
import javafx.fxml.Initializable;
|
||||
import javafx.scene.control.Alert;
|
||||
import javafx.scene.control.Alert.AlertType;
|
||||
import javafx.scene.control.TextField;
|
||||
import javafx.scene.layout.AnchorPane;
|
||||
import javafx.scene.layout.GridPane;
|
||||
import javafx.scene.layout.Pane;
|
||||
import seng302.client.ClientState;
|
||||
import seng302.visualiser.ClientToServerThread;
|
||||
import seng302.gameServer.GameState;
|
||||
import seng302.gameServer.MainServerThread;
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import seng302.visualiser.GameClient;
|
||||
|
||||
/**
|
||||
|
||||
+3
-2
@@ -1,5 +1,7 @@
|
||||
package seng302.visualiser.controllers.annotations;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.ResourceBundle;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.fxml.Initializable;
|
||||
import javafx.scene.control.Button;
|
||||
@@ -7,8 +9,7 @@ import javafx.scene.control.CheckBox;
|
||||
import javafx.scene.layout.AnchorPane;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
import java.net.URL;;
|
||||
import java.util.ResourceBundle;
|
||||
;
|
||||
|
||||
public class ImportantAnnotationController implements Initializable {
|
||||
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
package seng302.visualiser.fxObjects;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import javafx.geometry.Point2D;
|
||||
import javafx.scene.CacheHint;
|
||||
import javafx.scene.Group;
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.scene.shape.Circle;
|
||||
import javafx.scene.paint.Paint;
|
||||
import javafx.scene.shape.Line;
|
||||
import javafx.scene.shape.Polygon;
|
||||
import javafx.scene.transform.Rotate;
|
||||
import seng302.model.Yacht;
|
||||
|
||||
/**
|
||||
* BoatGroup is a javafx group that by default contains a graphical objects for representing a 2
|
||||
@@ -64,52 +62,49 @@ public class BoatObject extends Group {
|
||||
* polygon.
|
||||
*/
|
||||
public BoatObject(double... points) {
|
||||
public BoatGroup(Yacht boat, Color color, double... points) {
|
||||
this.colour = colour;
|
||||
destinationSet = false;
|
||||
this.boat = boat;
|
||||
initChildren(color, points);
|
||||
initChildren(points);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the javafx objects that will be the in the group by default.
|
||||
*
|
||||
* @param color The colour of the boat polygon and the trailing line.
|
||||
* @param points An array of co-ordinates x1,y1,x2,y2,x3,y3... that will make up the boat
|
||||
* polygon.
|
||||
*/
|
||||
private void initChildren(Color color, double... points) {
|
||||
this.color = color;
|
||||
private void initChildren(double... points) {
|
||||
// this.colour = color;
|
||||
boatPoly = new Polygon(points);
|
||||
boatPoly.setFill(colour);
|
||||
boatPoly.setFill(this.color);
|
||||
boatPoly.setFill(this.colour);
|
||||
boatPoly.setOnMouseEntered(event -> {
|
||||
boatPoly.setFill(Color.FLORALWHITE);
|
||||
boatPoly.setStroke(Color.RED);
|
||||
});
|
||||
boatPoly.setOnMouseExited(event -> {
|
||||
boatPoly.setFill(colour);
|
||||
boatPoly.setFill(this.color);
|
||||
boatPoly.setFill(this.colour);
|
||||
boatPoly.setStroke(Color.BLACK);
|
||||
});
|
||||
boatPoly.setOnMouseClicked(event -> setIsSelected(!isSelected));
|
||||
boatPoly.setCache(true);
|
||||
boatPoly.setCacheHint(CacheHint.SPEED);
|
||||
|
||||
annotationBox = new AnnotationBox();
|
||||
annotationBox.setFill(colour);
|
||||
boatAnnotations = new BoatAnnotations(boat, this.color);
|
||||
// annotationBox = new AnnotationBox();
|
||||
// annotationBox.setFill(colour);
|
||||
|
||||
leftLayLine = new Line();
|
||||
rightLayline = new Line();
|
||||
|
||||
wake = new Wake(0, -BOAT_HEIGHT);
|
||||
super.getChildren().addAll(boatPoly, annotationBox);
|
||||
super.getChildren().addAll(boatPoly);//, annotationBox);
|
||||
}
|
||||
|
||||
public void setFill (Paint value) {
|
||||
this.colour = value;
|
||||
boatPoly.setFill(colour);
|
||||
annotationBox.setFill(colour);
|
||||
// annotationBox.setFill(colour);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -207,14 +202,14 @@ public class BoatObject extends Group {
|
||||
wake.setRotation(rotation, groundSpeed);
|
||||
// yacht.setVelocity(groundSpeed);
|
||||
lastTimeValid = timeValid;
|
||||
boat.setVelocity(groundSpeed);
|
||||
// boat.setVelocity(groundSpeed);
|
||||
isStopped = false;
|
||||
lastRotation = rotation;
|
||||
boatAnnotations.update();
|
||||
// boatAnnotations.update();
|
||||
|
||||
distanceTravelled += Math.sqrt((dx * dx) + (dy * dy));
|
||||
|
||||
if (distanceTravelled > 10 && isPlayer) {
|
||||
if (distanceTravelled > 10){// && isPlayer) {
|
||||
distanceTravelled = 0d;
|
||||
|
||||
if (lastPoint != null) {
|
||||
@@ -225,7 +220,7 @@ public class BoatObject extends Group {
|
||||
boatPoly.getLayoutY()
|
||||
);
|
||||
l.getStrokeDashArray().setAll(3d, 7d);
|
||||
l.setStroke(boat.getColour());
|
||||
l.setStroke(this.colour);
|
||||
l.setCache(true);
|
||||
l.setCacheHint(CacheHint.SPEED);
|
||||
lineGroup.getChildren().add(l);
|
||||
@@ -280,8 +275,7 @@ public class BoatObject extends Group {
|
||||
|
||||
public void setVisibility (boolean teamName, boolean velocity, boolean estTime, boolean legTime,
|
||||
boolean trail, boolean wake) {
|
||||
public void setVisibility (boolean teamName, boolean velocity, boolean estTime, boolean legTime, boolean trail, boolean wake) {
|
||||
boatAnnotations.setVisible(teamName, velocity, estTime, legTime);
|
||||
// boatAnnotations.setVisible(teamName, velocity, estTime, legTime);
|
||||
this.wake.setVisible(wake);
|
||||
this.lineGroup.setVisible(trail);
|
||||
}
|
||||
@@ -336,23 +330,18 @@ public class BoatObject extends Group {
|
||||
return isStopped;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return boat.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this boat to appear highlighted
|
||||
*/
|
||||
public void setAsPlayer() {
|
||||
boatPoly.getPoints().setAll(
|
||||
-BOAT_WIDTH / 1.75, BOAT_HEIGHT / 1.75,
|
||||
0.0, -BOAT_HEIGHT / 1.75,
|
||||
BOAT_WIDTH / 1.75, BOAT_HEIGHT / 1.75
|
||||
);
|
||||
boatPoly.setStroke(Color.BLACK);
|
||||
boatPoly.setStrokeWidth(3);
|
||||
boatAnnotations.setAsPlayer();
|
||||
isPlayer = true;
|
||||
// boatPoly.getPoints().setAll(
|
||||
// -BOAT_WIDTH / 1.75, BOAT_HEIGHT / 1.75,
|
||||
// 0.0, -BOAT_HEIGHT / 1.75,
|
||||
// BOAT_WIDTH / 1.75, BOAT_HEIGHT / 1.75
|
||||
// );
|
||||
// boatPoly.setStroke(Color.BLACK);
|
||||
// boatPoly.setStrokeWidth(3);
|
||||
// boatAnnotations.setAsPlayer();
|
||||
// isPlayer = true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package seng302.visualiser.map;
|
||||
|
||||
/**
|
||||
* The Boundary class represents a rectangle territorial boundary on a map. It
|
||||
* contains four extremity double values(N, E, S, W). N and S are represented as
|
||||
* latitudes in radians. E and W are represented as longitudes in radians.
|
||||
*
|
||||
* Created by Haoming on 10/5/17
|
||||
*/
|
||||
class Boundary {
|
||||
|
||||
private double northLat, eastLng, southLat, westLng;
|
||||
|
||||
Boundary(double northLat, double eastLng, double southLat, double westLng) {
|
||||
this.northLat = northLat;
|
||||
this.eastLng = eastLng;
|
||||
this.southLat = southLat;
|
||||
this.westLng = westLng;
|
||||
}
|
||||
|
||||
double getCentreLat() {
|
||||
return (northLat + southLat) / 2;
|
||||
}
|
||||
|
||||
double getCentreLng() {
|
||||
return (eastLng + westLng) / 2;
|
||||
}
|
||||
|
||||
double getNorthLat() {
|
||||
return northLat;
|
||||
}
|
||||
|
||||
double getEastLng() {
|
||||
return eastLng;
|
||||
}
|
||||
|
||||
double getSouthLat() {
|
||||
return southLat;
|
||||
}
|
||||
|
||||
double getWestLng() {
|
||||
return westLng;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
package seng302.visualiser.map;
|
||||
|
||||
import java.net.URL;
|
||||
import javafx.geometry.Point2D;
|
||||
import javafx.scene.image.Image;
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import seng302.utilities.GeoPoint;
|
||||
|
||||
/**
|
||||
* CanvasMap retrieves a map image with given geo boundary from Google Map server.
|
||||
* By passing a rectangle like geo boundary, it returns a map image with the
|
||||
* highest resolution. However, due to free quote account usage limit, the maximum
|
||||
* resolution is only 1280 * 1280.
|
||||
*
|
||||
* Created by Haoming on 15/5/2017
|
||||
*/
|
||||
public class CanvasMap {
|
||||
|
||||
private Boundary boundary;
|
||||
private long width, height; // desired image size
|
||||
private int zoom;
|
||||
|
||||
private String KEY = "AIzaSyC-5oOShMCY5Oy_9L7guYMPUPFHDMr37wE";
|
||||
|
||||
CanvasMap(Boundary boundary) {
|
||||
this.boundary = boundary;
|
||||
calculateOptimalMapSize();
|
||||
}
|
||||
|
||||
Image getMapImage() {
|
||||
try {
|
||||
URL url = new URL(getRequest());
|
||||
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
|
||||
|
||||
return new Image(connection.getInputStream());
|
||||
} catch (Exception e) {
|
||||
System.out.println("[CanvasMap] Exception");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private String getRequest() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("https://maps.googleapis.com/maps/api/staticmap?");
|
||||
sb.append(String.format("center=%f,%f", boundary.getCentreLat(), boundary.getCentreLng()));
|
||||
sb.append(String.format("&zoom=%d", zoom));
|
||||
sb.append(String.format("&size=%dx%d&scale=2", width, height));
|
||||
sb.append("&style=feature:all|element:labels|visibility:off"); // hide all labels on map
|
||||
// sb.append(String.format("&markers=%f,%f", boundary.getSouthLat(), boundary.getWestLng()));
|
||||
// sb.append(String.format("&key=%s", KEY));
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private void calculateOptimalMapSize() {
|
||||
for (int z = 20; z > 0; z--) {
|
||||
MapSize mapSize = getMapSize(z, boundary);
|
||||
zoom = z;
|
||||
width = mapSize.width;
|
||||
height = mapSize.height;
|
||||
// if map size is valid, exit the loop as we have the highest resolution
|
||||
if (mapSize.isValid()) break;
|
||||
}
|
||||
}
|
||||
|
||||
private MapSize getMapSize(int zoom, Boundary boundary) {
|
||||
double scale = Math.pow(2, zoom);
|
||||
GeoPoint geoSW = new GeoPoint(boundary.getSouthLat(), boundary.getWestLng());
|
||||
GeoPoint geoNE = new GeoPoint(boundary.getNorthLat(), boundary.getEastLng());
|
||||
Point2D pointSW = MercatorProjection.toMapPoint(geoSW);
|
||||
Point2D pointNE = MercatorProjection.toMapPoint(geoNE);
|
||||
return new MapSize(Math.abs(pointNE.getX() - pointSW.getX()) * scale,
|
||||
Math.abs(pointNE.getY() - pointSW.getY()) * scale);
|
||||
}
|
||||
|
||||
class MapSize {
|
||||
long width, height;
|
||||
|
||||
MapSize(double width, double height) {
|
||||
this.width = Math.round(width);
|
||||
this.height = Math.round(height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Map size is valid when width and height are both less than 640 pixels
|
||||
* @return true if both dimensions are less than 640px
|
||||
*/
|
||||
boolean isValid() {
|
||||
return Math.max(width, height) <= 640;
|
||||
}
|
||||
}
|
||||
|
||||
public long getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
public long getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
public int getZoom() {
|
||||
return zoom;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package seng302.visualiser.map;
|
||||
|
||||
import javafx.geometry.Point2D;
|
||||
import seng302.utilities.GeoPoint;
|
||||
|
||||
/**
|
||||
* An utility class useful to convert between Geo locations and Mercator projection
|
||||
* planar coordinates.
|
||||
* Created by Haoming on 15/5/2017
|
||||
*/
|
||||
public class MercatorProjection {
|
||||
|
||||
private static final double MERCATOR_RANGE = 256;
|
||||
private static final double pixelsPerLngDegree = MERCATOR_RANGE / 360.0;
|
||||
private static final double pixelsPerLngRadian = MERCATOR_RANGE / (2 * Math.PI);
|
||||
|
||||
/**
|
||||
* A help function keeps the value in bound between -0.9999 and 0.9999.
|
||||
* @param value in bound value
|
||||
* @return the value in bound
|
||||
*/
|
||||
private static double bound(double value) {
|
||||
return Math.min(Math.max(value, -0.9999), 0.9999);
|
||||
}
|
||||
|
||||
/**
|
||||
* Projects a Geo Location (lat, lng) on a planar
|
||||
* @param geo GeoPoint (lat, lng) location to be projected
|
||||
* @return the projection Point2D (x, y) on planar
|
||||
*/
|
||||
static Point2D toMapPoint(GeoPoint geo) {
|
||||
double x, y;
|
||||
Point2D origin = new Point2D(MERCATOR_RANGE / 2.0, MERCATOR_RANGE / 2.0);
|
||||
x = (origin.getX() + geo.getLng() * pixelsPerLngDegree);
|
||||
|
||||
// NOTE(appleton): Truncating to 0.9999 effectively limits latitude to
|
||||
// 89.189. This is about a third of a tile past the edge of the world tile.
|
||||
double sinY = bound(Math.sin(Math.toRadians(geo.getLat())));
|
||||
y = origin.getY() + 0.5 * Math.log((1 + sinY) / (1 - sinY)) * (-pixelsPerLngRadian);
|
||||
return new Point2D(x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the planar projection (x, y) back to Geo Location (lat, lng)
|
||||
* @param point Point2D (x, y) to be converted back
|
||||
* @return the original Geo location converted from the given projection point
|
||||
*/
|
||||
public static GeoPoint toMapGeo(Point2D point) {
|
||||
Point2D origin = new Point2D(MERCATOR_RANGE / 2.0, MERCATOR_RANGE / 2.0);
|
||||
double lng = (point.getX() - origin.getX()) / pixelsPerLngDegree;
|
||||
double latRadians = (point.getY() - origin.getY()) / (-pixelsPerLngRadian);
|
||||
double lat = Math.toDegrees(2 * Math.atan(Math.exp(latRadians)) - Math.PI / 2.0);
|
||||
return new GeoPoint(lat, lng);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package seng302.visualiser.map;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.ResourceBundle;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.fxml.Initializable;
|
||||
import javafx.scene.canvas.Canvas;
|
||||
import javafx.scene.canvas.GraphicsContext;
|
||||
|
||||
public class TestMapController implements Initializable{
|
||||
|
||||
@FXML
|
||||
private Canvas mapCanvas;
|
||||
|
||||
@Override
|
||||
public void initialize(URL location, ResourceBundle resources) {
|
||||
GraphicsContext gc = mapCanvas.getGraphicsContext2D();
|
||||
Boundary bound = new Boundary(57.662943, 11.848501, 57.673945, 11.824966);
|
||||
CanvasMap canvasMap = new CanvasMap(bound);
|
||||
gc.drawImage(canvasMap.getMapImage(), 0, 0, canvasMap.getWidth(), canvasMap.getHeight());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user