Skip to content
Snippets Groups Projects
Commit 3000c395 authored by Hallvard Trætteberg's avatar Hallvard Trætteberg
Browse files

Imports and uses FxMapControl. Issue #10.

parent 4e8e8e36
No related branches found
No related tags found
No related merge requests found
Showing with 421 additions and 36 deletions
/*
* FX Map Control - https://github.com/ClemensFischer/FX-Map-Control
* © 2017 Clemens Fischer
*/
package fxmapcontrol;
import javafx.event.Event;
import javafx.event.EventType;
/**
* Fired by MapBase when the viewport changes.
*/
public class ViewportChangedEvent extends Event {
public static final EventType<ViewportChangedEvent> VIEWPORT_CHANGED
= new EventType(Event.ANY, "VIEWPORT_CHANGED");
private final boolean projectionChanged;
private final double longitudeOffset;
public ViewportChangedEvent(boolean projectionChanged, double longitudeOffset) {
super(VIEWPORT_CHANGED);
this.projectionChanged = projectionChanged;
this.longitudeOffset = longitudeOffset;
}
/**
* Indicates if the map projection has changed, i.e. if a TileLayer or MapImageLayer should be
* immediately updated, or MapPath Data in cartesian map coordinates should be recalculated.
*/
public final boolean getProjectionChanged() {
return projectionChanged;
}
/**
* Offset of the map center longitude value from the previous viewport.
* Used to detect if the map center has moved across 180° longitude.
*/
public final double getLongitudeOffset() {
return longitudeOffset;
}
}
/*
* FX Map Control - https://github.com/ClemensFischer/FX-Map-Control
* © 2017 Clemens Fischer
*/
package fxmapcontrol;
import javafx.geometry.Point2D;
/**
* Transforms geographic coordinates to cartesian coordinates according to the Web Mercator
* Projection. Longitude values are transformed linearly to X values in meters, by multiplying with
* METERS_PER_DEGREE. Latitude values in the interval [-maxLatitude .. maxLatitude] are transformed
* to Y values in meters in the interval [-R*pi .. R*pi], R=WGS84_EQUATORIAL_RADIUS.
*/
public class WebMercatorProjection extends MapProjection {
public static final double MAX_LATITUDE = yToLatitude(180.);
public WebMercatorProjection() {
this("EPSG:3857");
}
public WebMercatorProjection(String crsId) {
this.crsId = crsId;
}
@Override
public boolean isWebMercator() {
return true;
}
@Override
public boolean isNormalCylindrical() {
return true;
}
@Override
public boolean isAzimuthal() {
return false;
}
@Override
public double maxLatitude() {
return MAX_LATITUDE;
}
@Override
public Point2D getMapScale(Location location) {
double scale = viewportScale / Math.cos(location.getLatitude() * Math.PI / 180d);
return new Point2D(scale, scale);
}
@Override
public Point2D locationToPoint(Location location) {
return new Point2D(
METERS_PER_DEGREE * location.getLongitude(),
METERS_PER_DEGREE * latitudeToY(location.getLatitude()));
}
@Override
public Location pointToLocation(Point2D point) {
return new Location(
yToLatitude(point.getY() / METERS_PER_DEGREE),
point.getX() / METERS_PER_DEGREE);
}
@Override
public double getViewportScale(double zoomLevel) {
return super.getViewportScale(zoomLevel) / METERS_PER_DEGREE;
}
public static double latitudeToY(double latitude) {
return latitude <= -90 ? Double.NEGATIVE_INFINITY
: latitude >= 90 ? Double.POSITIVE_INFINITY
: Math.log(Math.tan(latitude * Math.PI / 360 + Math.PI / 4)) / Math.PI * 180;
}
public static double yToLatitude(double y) {
return Math.atan(Math.sinh(y * Math.PI / 180)) / Math.PI * 180;
}
}
/*
* FX Map Control - https://github.com/ClemensFischer/FX-Map-Control
* © 2016 Clemens Fischer
*/
package fxmapcontrol;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.beans.InvalidationListener;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
/**
* Map image overlay. Fills the entire viewport with map images provided by a Web Map Service (WMS).
* The base request URL is specified by the serviceUrl property.
*/
public class WmsImageLayer extends MapImageLayer {
private final StringProperty serviceUrlProperty = new SimpleStringProperty(this, "serviceUrl");
private final StringProperty versionProperty = new SimpleStringProperty(this, "version", "1.3.0");
private final StringProperty layersProperty = new SimpleStringProperty(this, "layers");
private final StringProperty stylesProperty = new SimpleStringProperty(this, "styles");
private final StringProperty parametersProperty = new SimpleStringProperty(this, "parameters");
private final BooleanProperty transparentProperty = new SimpleBooleanProperty(this, "transparent");
public WmsImageLayer(String serviceUrl, String layers) {
this();
setServiceUrl(serviceUrl);
setLayers(layers);
}
public WmsImageLayer() {
InvalidationListener listener = observable -> updateImage();
serviceUrlProperty.addListener(listener);
versionProperty.addListener(listener);
layersProperty.addListener(listener);
stylesProperty.addListener(listener);
parametersProperty.addListener(listener);
transparentProperty.addListener(listener);
}
public final StringProperty serviceUrlProperty() {
return serviceUrlProperty;
}
public final String getServiceUrl() {
return serviceUrlProperty.get();
}
public final void setServiceUrl(String serviceUrl) {
serviceUrlProperty.set(serviceUrl);
}
public final StringProperty versionProperty() {
return versionProperty;
}
public final String getVersion() {
return versionProperty.get();
}
public final void setVersion(String version) {
versionProperty.set(version);
}
public final StringProperty layersProperty() {
return layersProperty;
}
public final String getLayers() {
return layersProperty.get();
}
public final void setLayers(String layers) {
layersProperty.set(layers);
}
public final StringProperty stylesProperty() {
return stylesProperty;
}
public final String getStyles() {
return stylesProperty.get();
}
public final void setStyles(String styles) {
stylesProperty.set(styles);
}
public final StringProperty parametersProperty() {
return parametersProperty;
}
public final String getParameters() {
return parametersProperty.get();
}
public final void setParameters(String parameters) {
parametersProperty.set(parameters);
}
public final BooleanProperty transparentProperty() {
return transparentProperty;
}
public final boolean isTransparent() {
return transparentProperty.get();
}
public final void setTransparent(boolean transparent) {
transparentProperty.set(transparent);
}
public ObservableList<String> getAllLayers() {
ObservableList<String> layers = FXCollections.observableArrayList();
String url = getServiceUrl();
if (url != null && !url.isEmpty()) {
try {
url += "?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetCapabilities";
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
DocumentBuilder docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document document;
try (InputStream inputStream = connection.getInputStream()) {
document = docBuilder.parse(inputStream);
}
NodeList layerNodes = document.getDocumentElement().getElementsByTagName("Layer");
if (layerNodes.getLength() > 0) {
Element rootLayer = (Element) layerNodes.item(0);
layerNodes = rootLayer.getElementsByTagName("Layer");
for (int i = 0; i < layerNodes.getLength(); i++) {
Node layerNode = layerNodes.item(i);
if (layerNode.getNodeType() == Node.ELEMENT_NODE) {
NodeList nameNodes = ((Element) layerNode).getElementsByTagName("Name");
if (nameNodes.getLength() > 0) {
layers.add(((Element) nameNodes.item(0)).getTextContent());
}
}
}
}
}
} catch (IOException | ParserConfigurationException | SAXException | DOMException ex) {
Logger.getLogger(WmsImageLayer.class.getName()).log(Level.SEVERE, null, ex);
}
}
return layers;
}
@Override
protected boolean updateImage(MapBoundingBox boundingBox) {
String serviceUrl = getServiceUrl();
if (serviceUrl == null || serviceUrl.isEmpty()) {
return false;
}
String version = getVersion() != null ? getVersion() : "1.3.0";
String queryParameters = getMap().getProjection().wmsQueryParameters(boundingBox, version);
if (queryParameters == null || queryParameters.isEmpty()) {
return false;
}
String imageUrl = serviceUrl
+ "?SERVICE=WMS"
+ "&VERSION=" + version
+ "&REQUEST=GetMap"
+ "&LAYERS=" + (getLayers() != null ? getLayers() : "")
+ "&STYLES=" + (getStyles() != null ? getStyles() : "")
+ "&" + queryParameters
+ "&FORMAT=image/png"
+ "&TRANSPARENT=" + (isTransparent() ? "TRUE" : "FALSE");
if (getParameters() != null) {
imageUrl += "&" + getParameters();
}
imageUrl = imageUrl.replace(" ", "%20");
updateImage(imageUrl);
return true;
}
}
......@@ -16,14 +16,18 @@
<version>0.0.1-SNAPSHOT</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.lynden/GMapsFX -->
<!-- https://mvnrepository.com/artifact/com.lynden/GMapsFX
<dependency>
<groupId>com.lynden</groupId>
<artifactId>GMapsFX</artifactId>
<version>2.12.0</version>
</dependency>
<!-- <dependency> <groupId>fischer.clemens</groupId> <artifactId>fx-map-control</artifactId>
<version>1.0</version> </dependency> -->
-->
<dependency>
<groupId>fischer.clemens</groupId>
<artifactId>fx-map-control</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>junit</groupId>
......
package tdt4140.gr1800.app.ui;
import fxmapcontrol.Location;
import fxmapcontrol.MapItem;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.scene.shape.Circle;
import tdt4140.gr1800.app.core.LatLong;
public class MapMarker extends MapItem<LatLong> {
public MapMarker(LatLong latLong) {
setLocation(new Location(latLong.latitude, latLong.longitude));
final Circle circle = new Circle();
circle.setRadius(5);
circle.setFill(getMarkerColor(false));
getChildren().add(circle);
selectedProperty().addListener((booleanProperty, oldValue, newValue) -> {
circle.setFill(getMarkerColor(newValue));
});
}
protected Paint getMarkerColor(boolean selected) {
return selected ? Color.BLUE : Color.GREEN;
}
}
package tdt4140.gr1800.app.ui;
import fxmapcontrol.MapItem;
import fxmapcontrol.MapNode;
import javafx.scene.paint.Color;
import javafx.scene.shape.Line;
import tdt4140.gr1800.app.core.LatLong;
public class MapPathLine extends MapItem<LatLong> {
public MapPathLine(MapNode start, MapNode end) {
setLocation(start.getLocation());
final Line line = new Line();
line.setStrokeWidth(3);
line.setStroke(Color.GREEN);
end.translateXProperty().addListener((prop, oldValue, newValue) -> {
line.setEndX(end.getTranslateX() - getTranslateX());
});
end.translateYProperty().addListener((prop, oldValue, newValue) -> {
line.setEndY(end.getTranslateY() - getTranslateY());
});
getChildren().add(line);
}
}
......@@ -7,22 +7,22 @@
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import com.lynden.gmapsfx.GoogleMapView?>
<?import fxmapcontrol.Map?>
<?import javafx.scene.control.ComboBox?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.control.Button?>
<BorderPane xmlns:fx="http://javafx.com/fxml"
fx:controller="tdt4140.gr1800.app.ui.FxAppController"
<BorderPane xmlns:fx="http://javafx.com/fxml" fx:controller="tdt4140.gr1800.app.ui.FxAppController"
prefHeight="750" prefWidth="1000">
<top>
<VBox>
<HBox>
<TextField fx:id="geolocationsFileText" promptText="Geo-locations file" onAction="#loadGeolocationsFile"/>
<Button text="Browse..." onAction="#handleBrowseGeolocationsFile"/>
</HBox>
<MenuBar>
<menus>
<fx:include fx:id="fileMenu" source="FileMenu.fxml"/>
</menus>
</MenuBar>
<ComboBox fx:id="geoLocationsSelector">
<BorderPane.margin>
<Insets left="5" right="5" top="5" bottom="5" />
......@@ -31,8 +31,8 @@
</VBox>
</top>
<center>
<GoogleMapView fx:id="mapView">
</GoogleMapView>
<Map fx:id="mapView" center="63,10" maxZoomLevel="15">
</Map>
</center>
<bottom>
<Slider fx:id="zoomSlider" min="1" max="20" value="9">
......
......@@ -4,8 +4,7 @@ import org.junit.Assert;
import org.junit.Test;
import org.testfx.framework.junit.ApplicationTest;
import com.lynden.gmapsfx.GoogleMapView;
import fxmapcontrol.MapBase;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.scene.Parent;
......@@ -28,6 +27,6 @@ public class FxAppTest extends ApplicationTest {
@Test
public void testMapExists() {
Node map = lookup("#mapView").query();
Assert.assertTrue(map instanceof GoogleMapView);
Assert.assertTrue(map instanceof MapBase);
}
}
......@@ -13,7 +13,7 @@
<modules>
<module>app.core</module>
<module>FxMapControl</module>
<module>app.ui</module>
</modules>
</project>
\ No newline at end of file
......@@ -14,6 +14,5 @@
<modules>
<module>app.core</module>
<module>app.ui</module>
</modules>
</project>
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment