Skip to content

Method: call()

1: /*
2: * *************************************************************************************************************************************************************
3: *
4: * MapView: a JavaFX map renderer for tile-based servers
5: * http://tidalwave.it/projects/mapview
6: *
7: * Copyright (C) 2024 - 2025 by Tidalwave s.a.s. (http://tidalwave.it)
8: *
9: * *************************************************************************************************************************************************************
10: *
11: * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
12: * You may obtain a copy of the License at
13: *
14: * http://www.apache.org/licenses/LICENSE-2.0
15: *
16: * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
17: * CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
18: *
19: * *************************************************************************************************************************************************************
20: *
21: * git clone https://bitbucket.org/tidalwave/mapview-src
22: * git clone https://github.com/tidalwave-it/mapview-src
23: *
24: * *************************************************************************************************************************************************************
25: */
26: package it.tidalwave.mapview.javafx.example;
27:
28: import jakarta.annotation.Nonnull;
29: import java.util.Collection;
30: import java.util.List;
31: import java.util.concurrent.ExecutionException;
32: import java.util.concurrent.ExecutorService;
33: import java.util.concurrent.Executors;
34: import java.io.IOException;
35: import javafx.concurrent.Task;
36: import javafx.fxml.FXML;
37: import javafx.scene.Node;
38: import javafx.scene.control.Button;
39: import javafx.scene.control.Label;
40: import javafx.scene.control.Slider;
41: import javafx.scene.layout.AnchorPane;
42: import javafx.scene.paint.Color;
43: import javafx.scene.shape.Circle;
44: import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
45: import io.jenetics.jpx.GPX;
46: import io.jenetics.jpx.Track;
47: import io.jenetics.jpx.TrackSegment;
48: import io.jenetics.jpx.WayPoint;
49: import it.tidalwave.mapviewer.MapArea;
50: import it.tidalwave.mapviewer.MapCoordinates;
51: import it.tidalwave.mapviewer.OpenStreetMapTileSource;
52: import it.tidalwave.mapviewer.OpenTopoMapTileSource;
53: import it.tidalwave.mapviewer.TileSource;
54: import it.tidalwave.mapviewer.javafx.MapView;
55: import lombok.extern.slf4j.Slf4j;
56:
57: /***************************************************************************************************************************************************************
58: *
59: * @author Fabrizio Giudici
60: *
61: **************************************************************************************************************************************************************/
62: @Slf4j
63: public class MapViewExampleController
64: {
65: private static final String TRACK_OVERLAY_NAME = "track";
66: private static final MapCoordinates START = MapCoordinates.of(44.4072, 8.9340);
67: private static final double START_ZOOM = 8;
68: private static final MapArea ITALY = MapArea.of(47.115, 18.480, 36.6199, 6.749);
69: private static final MapArea FRANCE = MapArea.of(51.148, 9.560, 2.053, -54.524);
70: private static final MapArea SWITZERLAND = MapArea.of(47.830, 10.442, 45.776, 6.022);
71: private static final MapArea ALEUTIAN = MapArea.of(62.67, -147.38, 46.32, 161.64 );
72: private static final TileSource osm = new OpenStreetMapTileSource();
73: private static final TileSource otm = new OpenTopoMapTileSource();
74:
75: @FXML
76: private AnchorPane apAnchorPane;
77:
78: @FXML
79: private Slider slZoom;
80:
81: @FXML
82: private Button btZoomIn;
83:
84: @FXML
85: private Button btZoomOut;
86:
87: @FXML
88: private Button btReset;
89:
90: @FXML
91: private Button btShowItaly;
92:
93: @FXML
94: private Button btShowFrance;
95:
96: @FXML
97: private Button btShowSwitzerland;
98:
99: @FXML
100: private Button btShowAleutian;
101:
102: @FXML
103: private Button btZeroZero;
104:
105: @FXML
106: private Button btReframe;
107:
108: @FXML
109: private Button btOSM;
110:
111: @FXML
112: private Button btOTM;
113:
114: @FXML
115: private Label lbCoordinates;
116:
117: @FXML
118: private Label lbArea;
119:
120: @FXML
121: private Label lbCenterCoordinates;
122:
123: @FXML
124: private Label lbZoom;
125:
126: private MapView mapView;
127:
128: @Nonnull
129: private final ExecutorService executorService = Executors.newFixedThreadPool(1);
130:
131: /***********************************************************************************************************************************************************
132: *
133: **********************************************************************************************************************************************************/
134: public void initialize()
135: {
136: mapView = new MapView(MapView.options());
137: mapView.setZoom(START_ZOOM);
138: mapView.setCenter(START);
139: mapView.setRecenterOnDoubleClick(true);
140: AnchorPane.setLeftAnchor(mapView, 0.0);
141: AnchorPane.setRightAnchor(mapView, 0.0);
142: AnchorPane.setTopAnchor(mapView, 0.0);
143: AnchorPane.setBottomAnchor(mapView, 0.0);
144: apAnchorPane.getChildren().add(mapView);
145: mapView.setRecenterOnDoubleClick(true);
146: slZoom.minProperty().bind(mapView.minZoomProperty());
147: slZoom.maxProperty().bind(mapView.maxZoomProperty());
148: slZoom.valueProperty().bindBidirectional(mapView.zoomProperty());
149: mapView.centerProperty().addListener((_1, _2, coordinates) -> lbCenterCoordinates.setText(coordinates.toString()));
150: mapView.zoomProperty().addListener((_1, _2, zoom) -> lbZoom.setText(Integer.toString(zoom.intValue())));
151: mapView.areaProperty().addListener((_1, _2, area) -> lbArea.setText(area.toString()));
152: mapView.coordinatesUnderMouseProperty().addListener((_1, _2, coordinates) -> lbCoordinates.setText(coordinates.toString()));
153: btZoomIn.setOnAction(event -> mapView.setZoom(mapView.getZoom() + 1));
154: btZoomOut.setOnAction(event -> mapView.setZoom(mapView.getZoom() - 1));
155: btReset.setOnAction(event -> { mapView.setCenter(START); mapView.setZoom(START_ZOOM); });
156: btShowItaly.setOnAction(event -> mapView.fitArea(ITALY));
157: btShowFrance.setOnAction(event -> mapView.fitArea(FRANCE));
158: btShowSwitzerland.setOnAction(event -> mapView.fitArea(SWITZERLAND));
159: btShowAleutian.setOnAction(event -> mapView.fitArea(ALEUTIAN));
160: btZeroZero.setOnAction(event -> mapView.setCenter(MapCoordinates.of(0, 0)));
161: btOSM.setOnAction(event -> mapView.setTileSource(osm));
162: btOTM.setOnAction(event -> mapView.setTileSource(otm));
163: btReframe.setOnAction(event -> renderTrack());
164: }
165:
166: /***********************************************************************************************************************************************************
167: *
168: **********************************************************************************************************************************************************/
169: @SuppressFBWarnings("RV_RETURN_VALUE_IGNORED_BAD_PRACTICE")
170: public void renderTrack()
171: {
172: record PointsAndArea(List<WayPoint> points, MapArea area) {}
173:
174: final var task = new Task<PointsAndArea>()
175: {
176: @Override @Nonnull
177: protected PointsAndArea call()
178: {
179: final var track = loadTrack();
180: final var points = track.segments().flatMap(TrackSegment::points).toList();
181: final var area = computeFitArea(points);
182: log.info("track with {} points, fit area: {}", points.size(), area);
183: return new PointsAndArea(points, area);
184: }
185: };
186:
187: task.setOnSucceeded(event ->
188: {
189: try
190: {
191: final var pointsAndArea = task.get();
192: mapView.removeOverlay(TRACK_OVERLAY_NAME);
193: mapView.addOverlay(TRACK_OVERLAY_NAME, helper ->
194: helper.addAll(pointsAndArea.points.stream().map(wp -> createPoint(helper, wp)).toList()));
195: mapView.fitArea(pointsAndArea.area);
196: }
197: catch (InterruptedException | ExecutionException e)
198: {
199: log.error("", e);
200: }
201: });
202:
203: executorService.submit(task);
204: }
205:
206: /***********************************************************************************************************************************************************
207: *
208: **********************************************************************************************************************************************************/
209: @Nonnull
210: private static Node createPoint (@Nonnull final MapView.OverlayHelper helper, @Nonnull final WayPoint wp)
211: {
212: final var mapPoint = helper.toMapViewPoint(MapCoordinates.of(wp.getLatitude().doubleValue(), wp.getLongitude().doubleValue()));
213: final var node = new Circle(2.5, Color.RED);
214: node.setVisible(true);
215: node.setTranslateX(mapPoint.x());
216: node.setTranslateY(mapPoint.y());
217: return node;
218: }
219:
220: /***********************************************************************************************************************************************************
221: *
222: **********************************************************************************************************************************************************/
223: @Nonnull
224: private static MapArea computeFitArea (@Nonnull final Collection<WayPoint> points)
225: {
226: // quick and dirty, just for this example
227: var north = -999d;
228: var south = 999d;
229: var east = -999d;
230: var west = 999d;
231:
232: for (final var point : points)
233: {
234: north = Math.max(north, point.getLatitude().doubleValue());
235: south = Math.min(south, point.getLatitude().doubleValue());
236: east = Math.max(east, point.getLongitude().doubleValue());
237: west = Math.min(west, point.getLongitude().doubleValue());
238: }
239:
240: return MapArea.of(north, east, south, west);
241: }
242:
243: /***********************************************************************************************************************************************************
244: *
245: **********************************************************************************************************************************************************/
246: @Nonnull
247: private Track loadTrack()
248: {
249: try (final var is = MapViewExampleController.class.getResourceAsStream("/2014-04-10 1800__20140410_1800.gpx"))
250: {
251: return GPX.Reader.of(GPX.Reader.Mode.LENIENT).read(is).getTracks().getFirst();
252: }
253: catch (IOException e)
254: {
255: log.error(e.toString());
256: throw new RuntimeException(e);
257: }
258: }
259: }