package ai.cogmission.fxmaps.ui; import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; import javafx.application.Platform; import javafx.beans.property.BooleanProperty; import javafx.beans.property.IntegerProperty; import javafx.beans.property.ObjectProperty; import javafx.beans.value.ObservableValue; import javafx.geometry.Point2D; import javafx.scene.Node; import javafx.scene.control.ContextMenu; import javafx.scene.control.Label; import javafx.scene.control.MenuItem; import javafx.scene.layout.Border; import javafx.scene.layout.BorderPane; import javafx.scene.layout.BorderStroke; import javafx.scene.layout.BorderStrokeStyle; import javafx.scene.layout.BorderWidths; import javafx.scene.layout.Pane; import javafx.scene.layout.StackPane; import javafx.scene.paint.Color; import javafx.scene.text.Font; import javafx.scene.text.FontWeight; import javafx.scene.web.WebView; import javafx.stage.PopupWindow.AnchorLocation; import javafx.stage.Window; import netscape.javascript.JSObject; import ai.cogmission.fxmaps.event.MapEventHandler; import ai.cogmission.fxmaps.event.MapEventType; import ai.cogmission.fxmaps.event.MapInitializedListener; import ai.cogmission.fxmaps.event.MapReadyListener; import ai.cogmission.fxmaps.model.DirectionsRoute; import ai.cogmission.fxmaps.model.LatLon; import ai.cogmission.fxmaps.model.Location; import ai.cogmission.fxmaps.model.Locator; import ai.cogmission.fxmaps.model.MapObject; import ai.cogmission.fxmaps.model.MapOptions; import ai.cogmission.fxmaps.model.MapShape; import ai.cogmission.fxmaps.model.MapShapeOptions; import ai.cogmission.fxmaps.model.MapStore; import ai.cogmission.fxmaps.model.MapType; import ai.cogmission.fxmaps.model.Marker; import ai.cogmission.fxmaps.model.MarkerOptions; import ai.cogmission.fxmaps.model.MarkerType; import ai.cogmission.fxmaps.model.PersistentMap; import ai.cogmission.fxmaps.model.Polyline; import ai.cogmission.fxmaps.model.PolylineOptions; import ai.cogmission.fxmaps.model.Route; import ai.cogmission.fxmaps.model.Waypoint; import com.lynden.gmapsfx.GoogleMapView; import com.lynden.gmapsfx.javascript.object.GoogleMap; import com.lynden.gmapsfx.javascript.object.LatLong; /** * Undecorated {@link Pane} extension which is specialized to contain a * map view and an optional {@link DirectionsPane}. * <p> * To create a MapPane: * <br><br> * 1. Map map = Map.create("My Map Name"); * <br>--or--<br> * 2. MapPane map = Map.create("My Map Name"); * <br><br> * * To call JavaFX Node methods on the {@link MapPane} (such as setting width and height), use option 2. * * @author cogmission * */ public class MapPane extends StackPane implements Map { private static final String DEFAULT_OVERLAY_MESSAGE = "Click \"Create / Select Map\" To:\n\n" + "\t" + '\u2022' + " Create a new map\n\n \t\t-- or -- \n\n" + "\t" + '\u2022' + " Select a loaded map\n\n \t\t-- or -- \n\n" + "\t" + '\u2022' + " Load a GPX File"; private BorderPane contentPane = new BorderPane(); private static final MapOptions DEFAULT_MAP_OPTIONS = getDefaultMapOptions(); private final MapEventHandler DEFAULT_MAPEVENT_HANDLER = getDefaultMapEventHandler(); private MapStore MAP_STORE; private boolean defaultMapEventHandlerInstalled = true; private PolylineOptions DEFAULT_POLYLINE_OPTIONS; protected GoogleMapView mapComponent; protected GoogleMap googleMap; protected MapOptions userMapOptions; protected DirectionsPane directionsPane; protected Route currentRoute; protected List<MapReadyListener> readyListeners = new ArrayList<>(); protected boolean overlayVisible; protected ContextMenu contextMenu; protected MapObject currMapObj; protected StackPane dimmer; protected Label dimmerMessage; protected Map.Mode currentMode = Map.Mode.NORMAL; /** * Constructs a new {@code MapPane} */ MapPane() { setPrefWidth(1000); setPrefHeight(780); getChildren().add(contentPane); mapComponent = new GoogleMapView(); contentPane.setCenter(mapComponent); contextMenu = getContextMenu(); contextMenu.hideOnEscapeProperty().set(true); mapComponent.getWebView().setOnMousePressed(e -> { if(e.isPrimaryButtonDown()) { contextMenu.hide(); refresh(); } }); directionsPane = new DirectionsPane(); directionsPane.setPrefWidth(200); setDirectionsVisible(false); } public ContextMenu getContextMenu() { if(contextMenu == null) { ContextMenu menu = new ContextMenu(); MenuItem deleteItem = new MenuItem("Delete Object..."); deleteItem.setOnAction(e -> { if(currMapObj instanceof Waypoint){ Waypoint wp = ((Waypoint)currMapObj); Route editedRoute = getRouteForWaypoint(wp); eraseRoute(editedRoute); removeWaypoint(wp); displayRoute(editedRoute); MAP_STORE.store(); }else if((currMapObj instanceof MapShape)) { Polyline p = (Polyline)currMapObj; Route editedRoute = getRouteForLine(p); Waypoint editedWaypoint = getWaypointForLine(editedRoute, p); eraseRoute(editedRoute); removeWaypoint(editedWaypoint); displayRoute(editedRoute); MAP_STORE.store(); } }); menu.getItems().add(deleteItem); menu.setAnchorLocation(AnchorLocation.WINDOW_TOP_LEFT); contextMenu = menu; } return contextMenu; } /** * Creates and initializes child components to prepare the map * for immediate use. when the {@link Map} is initialized it is * given two call backs which when called, indicate that the map * is ready for use. * * Before this method is called, any desired {@link MapOptions} must * be set on the map before calling {@code #initialize()} */ @Override public void initialize() { // first complete ui initialization configureOverlay(); mapComponent.addMapInializedListener(this); } @Override public void addMap(String mapName) { if(mapName == null) return; MAP_STORE.addMap(mapName); MAP_STORE.getMap(mapName).setMapOptions(DEFAULT_MAP_OPTIONS); } /** * Returns the {@link MapOptions} set on this {@code Map} * * @return this {@code Map}'s {@link MapOptions} */ public MapOptions getMapOptions() { return this.userMapOptions; } /** * Specifies the {@link MapOptions} to use. <em>Note</em> this must * be set prior to calling {@link #initialize()} * * @param mapOptions the {@code MapOptions} to use. */ @Override public void setMapOptions(MapOptions options) { this.userMapOptions = options; } /** * Returns this map's persistent store. */ @Override public MapStore getMapStore() { return MAP_STORE; } /** * Sets the map mode to one of {@link Mode} * @param mode the mode to set */ public void setMode(Mode mode) { currentMode = mode; if(mode == Mode.ADD_WAYPOINTS) { setBorder(new Border(new BorderStroke(Color.GREEN, BorderStrokeStyle.SOLID, null, new BorderWidths(5)))); }else{ setBorder(null); } } /** * Makes the right {@link DirectionsPane} visible or invisible. * @param b */ @Override public void setDirectionsVisible(boolean b) { if(b) { if(contentPane.getRight() == null) { contentPane.setRight(directionsPane); } }else{ contentPane.setRight(null); } } /** * {@inheritDoc} */ @Override public void mapInitialized() { mapComponent.addMapReadyListener(() -> { // This call will fail unless the map is completely ready. // // NOTE: Leaving this in for documentation on how to go from lat/lon // to pixel coordinates. //checkCenter(center); // Locates the user's aprox. location and centers the map there. try { centerMapOnLocal(); MAP_STORE = MapStore.load(MapStore.DEFAULT_STORE_PATH); DEFAULT_POLYLINE_OPTIONS = getDefaultPolylineOptions(); for(MapReadyListener li : readyListeners) { li.mapReady(); } }catch(Exception e) { e.printStackTrace(); } }); createGoogleMap(); /** See {@link #removeDefaultMapEventHandler()} */ if(defaultMapEventHandlerInstalled) { addMapEventHandler(MapEventType.CLICK, DEFAULT_MAPEVENT_HANDLER); } } /** * Removes the default handler which: * <ol> * <li>Checks to see if "routeSimulationMode" is true (see {@link #setRouteSimulationMode(boolean)}) * <li> if routeSimulationMode is true, and the user left-clicked the map, a Waypoint will be added * to either a route named "temp" or if {@link #setCurrentRoute} has been called with a valid Route, * it will be added to that current {@link Route} * <li> if routeSimulationMode is false, nothing happens. * </ol> * * <em>WARNING: Must be called before {@link Map#initialize()} is called or * else this has no effect</em> */ @Override public void removeDefaultMapEventHandler() { defaultMapEventHandlerInstalled = false; } /** * Sets the current {@link Route} to which {@link #addNewWaypoint(Waypoint)} will add a waypoint. * Routes may be created by calling {@link Map#createRoute(String)} with a unique name. * * @param r the {@code Route} make current. */ @Override public void setCurrentRoute(Route route) { this.currentRoute = route; } /** * Returns the current {@link Route} to which {@link #addNewWaypoint(Waypoint)} will add a waypoint. * Routes may be created by calling {@link Map#createRoute(String)} with a unique name. * * @returns the {@code Route} which is current current. */ @Override public Route getCurrentRoute() { return currentRoute; } /** * Adds a {@link Node} acting as a toolbar * @param n a toolbar */ public void addToolBar(Node n) { contentPane.setTop(n); } /** * Returns a mutable {@link IntegerProperty} used to display * and change the zoom factor. * * @return zoom {@link IntegerProperty} */ @Override public IntegerProperty zoomProperty() { return googleMap.zoomProperty(); } /** * Demonstrates how to go from lat/lon to pixel coordinates. * @param center */ @SuppressWarnings("unused") private void checkCenter(LatLon center) { System.out.println("Testing fromLatLngToPoint using: " + center); Point2D p = googleMap.fromLatLngToPoint(center.toLatLong()); System.out.println("Testing fromLatLngToPoint result: " + p); System.out.println("Testing fromLatLngToPoint expected: " + mapComponent.getWidth()/2 + ", " + mapComponent.getHeight()/2); System.out.println("type = "+ MarkerType.BROWN.iconPath()); } @Override public void centerMapOnLocal() { try { String ip = Locator.getIp(); Location l = Locator.getIPLocation(ip); googleMap.setCenter(new LatLong(l.getLatitude(), l.getLongitude())); }catch(Exception e) { e.printStackTrace(); } } /** * Sets the center location of the map to the specified lat/lon * coordinates. * * @param ll the lat/lon coordinates around which to center the map. */ public void setCenter(LatLon ll) { googleMap.setCenter(ll.toLatLong()); } @Override public void addMapInializedListener(MapInitializedListener listener) { // TODO Auto-generated method stub } @Override public void removeMapInitializedListener(MapInitializedListener listener) { // TODO Auto-generated method stub } /** * Adds the specified {@link MapReadyListener} to the list of listeners * notified when the map becomes fully engageable. * * @param listener the {@code MapReadyListener} to add */ @Override public void addMapReadyListener(MapReadyListener listener) { if(!readyListeners.contains(listener)) { readyListeners.add(listener); } } @Override public void removeReadyListener(MapReadyListener listener) { // TODO Auto-generated method stub } /** * Displays a {@link Marker} on the {@code Map}, as opposed to adding * a {@link Waypoint} which adds a {@code Marker} and a connecting * line from any previous {@link Marker} along a given {@link Route}. * * @param marker the marker to add * @see Wayoint */ @Override public void displayMarker(Marker marker) { googleMap.addMarker(marker.convert()); String name = marker.convert().getVariableName(); System.out.println("marker name = " + name); Object obj = googleMap.getJSObject().getMember(name); System.out.println("marker obj = " + obj); System.out.println("icon = " + marker.convert().getJSObject().getMember("icon")); } private void setCurrentMapObject(MapObject o) { this.currMapObj = o; } /** * Removes the specified {@link Marker} from the {@code Map}, as opposed to removing * a {@link Waypoint} which removes a {@code Marker} and a connecting * line from any previous {@link Marker} along a given {@link Route}. * * This method only removes the marker from the display, it does nothing to the route. * * @param marker the marker to remove * @see Waypoint */ @Override public void eraseMarker(Marker marker) { googleMap.removeMarker(marker.convert()); } /** * Creates a {@link Waypoint} which is a combination of a * {@link LatLon} and a {@link Marker}. * * @param latLon the latitude/longitude position of the waypoint * @return the newly created {@code Waypoint} */ @Override public Waypoint createWaypoint(LatLon latLon) { MarkerOptions opts = new MarkerOptions() .position(latLon) .title("Waypoint") .icon(MarkerType.GREEN.nextPath()) .visible(true); return new Waypoint(latLon, new Marker(opts)); } /** * Adds a {@link Waypoint} to the map connecting it to any * previously added {@code Waypoint}s by a connecting line, * as opposed to adding a {@link Marker} which doesn't add * a line. The specified Waypoint is also added to the * currently focused route. * * @param waypoint the {@link Waypoint} to be added. * @see #displayMarker(Marker) */ @Override public void addNewWaypoint(Waypoint waypoint) { displayWaypoint(waypoint); currentRoute.addWaypoint(waypoint); if(currentRoute.size() > 1) { Polyline poly = connectLastWaypoint(waypoint, null); displayShape(poly); } MAP_STORE.store(); } /** * Adds a {@link Waypoint} to the map connecting it to any * previously added {@code Waypoint}s by a connecting line, * as opposed to adding a {@link Marker} which doesn't add * a line. The specified Waypoint is also added to the * currently focused route. * * @param waypoint the {@link Waypoint} to be added. * @param polylineOptions the subclass of {@link MapShapeOptions} containing desired * properties of the rendering operation. * @see #displayMarker(Marker) * @see #addNewWaypoint(Waypoint) */ @Override public <T extends MapShapeOptions<T>>void addNewWaypoint(Waypoint waypoint, T polylineOptions) { currentRoute.addWaypoint(waypoint); displayWaypoint(waypoint); if(currentRoute.size() > 1) { Polyline poly = connectLastWaypoint(waypoint, polylineOptions); displayShape(poly); } MAP_STORE.store(); } /** * Adds a {@link Waypoint} from the {@link MapStore} when the specified * Waypoint is already part of a {@link Route} and already has connecting leg lines. * @param waypoint the Waypoint to add */ @Override public void displayWaypoint(Waypoint waypoint) { displayMarker(waypoint.getMarker()); addObjectEventHandler(waypoint.getMarker(), MapEventType.RIGHTCLICK, (JSObject o) -> { setCurrentMapObject(waypoint); setCurrentRoute(getRouteForWaypoint(waypoint)); String id = waypoint.getMarker().getMarkerOptions().getIcon(); id = id.substring(id.lastIndexOf("M"), id.lastIndexOf(".")); contextMenu.getItems().get(0).setText("Clear " + id); LatLong cxtLL = new LatLong((JSObject) o.getMember("latLng")); Point2D p = googleMap.fromLatLngToPoint(cxtLL); Window w = MapPane.this.getScene().getWindow(); contextMenu.show( mapComponent.getWebView(), w.getX() + p.getX() + 10, w.getY() + p.getY()); }); } /** * Returns the {@link Polyline} which connects the specified {@link Waypoint} * @param lastWaypoint the newly added waypoint * @param polylineOptions the line options to use for rendering * @return the connecting Polyline */ private <T extends MapShapeOptions<T>> Polyline connectLastWaypoint(Waypoint lastWaypoint, T polylineOptions) { List<LatLon> l = new ArrayList<>(); l.add(currentRoute.getWaypoint(currentRoute.size() - 2).getLatLon()); l.add(currentRoute.getWaypoint(currentRoute.size() - 1).getLatLon()); Polyline poly = new Polyline(polylineOptions == null ? PolylineOptions.copy(DEFAULT_POLYLINE_OPTIONS).path(l) : (PolylineOptions)polylineOptions); lastWaypoint.setConnection(poly); currentRoute.addLine(poly); return poly; } /** * Removes the {@link Waypoint} from the map and its connecting line. * @param waypoint * @see #displayMarker(Marker) */ @Override public void removeWaypoint(Waypoint waypoint) { currentRoute.removeWaypoint(waypoint); } /** * Adds the specified {@link MapShape} to this {@code Map} * * @param shape the {@code MapShape} to add */ @Override public void displayShape(MapShape shape) { addLineMouseListener(getWaypointForLine(currentRoute, (Polyline)shape), (Polyline)shape); googleMap.addMapShape(shape.convert()); } /** * Removes the specified {@link MapShape} from this {@code Map} * @param shape the {@code MapShape} to remove */ @Override public void eraseShape(MapShape shape) { googleMap.removeMapShape(shape.convert()); } /** * Adds a {@link Route} to this {@code Map} * @param route the route to add */ @Override public void addRoute(Route route) { PersistentMap currentMap = MAP_STORE.getMap(MAP_STORE.getSelectedMapName()); if(currentMap.getRoute(route.getName()) == null) { MAP_STORE.getMap(MAP_STORE.getSelectedMapName()).addRoute(route); MAP_STORE.store(); } } /** * Removes the specified {@link Route} from this {@code Map} * * @param route the route to remove */ @Override public void removeRoute(Route route) { MAP_STORE.getMap(MAP_STORE.getSelectedMapName()).removeRoute(route); MAP_STORE.store(); } /** * Clears the specified {@link Route} of all its contents * (i.e. Lines and Markers) * * @param route the route to be cleared */ @Override public void clearRoute(Route route) { eraseRoute(route); route.removeAllWaypoints(); MAP_STORE.store(); } /** * Non-destructive clearing of all map objects. The {@code Map} * and all its {@link Route}s will still contain their content, * but the display will be cleared. * * @param route the {@link Route} to erase */ public void eraseRoute(Route route) { for(Waypoint w : route.getWaypoints()) { googleMap.removeMarker(w.getMarker().convert()); } for(Polyline line : route.getLines()) { googleMap.removeMapShape(line.convert()); } } /** * Returns the {@link Route} with the specified name. * @param name the name of the route to return * @return the specified route */ @Override public Route getRoute(String name) { return MAP_STORE.getMap(MAP_STORE.getSelectedMapName()).getRoute(name); } /** * Selects the specified {@link Route}, designating it * to be the currently focused route. * * @param route the {@code Route} to select. */ @Override public void selectRoute(Route route) { currentRoute = route; } /** * Displays the list of {@link Route}s on this {@code Map} * * @param routes the list of routes to display */ @Override public void displayRoutes(List<Route> routes) { for(Route r : routes) { currentRoute = r; displayRoute(r); } refresh(); } /** * Displays the specified {@link Route} on the map * * @param route the route to display */ @Override public void displayRoute(Route route) { for(Waypoint wp : route.getWaypoints()) { if(route.getInterimMarkersVisible() || (!route.getInterimMarkersVisible() && (wp.equals(route.getOrigin()) || wp.equals(route.getDestination())))) { displayWaypoint(wp); } } for(Polyline p : route.getLines()) { Waypoint wp = getWaypointForLine(route, p); wp.setConnection(p); displayShape(p); } } /** * Adds the listener that invokes the context menu for lines. * * @param wp the {@link Waypoint} which owns the specified {@link Polyline} * @param p the Polyline to add the listener to. */ private void addLineMouseListener(Waypoint wp, Polyline p) { if(wp != null) { System.out.println("Adding listener for waypoint: " + wp.getMarker().getMarkerOptions().getIcon()); addObjectEventHandler((MapObject)p, MapEventType.RIGHTCLICK, (JSObject o) -> { setCurrentMapObject(p); setCurrentRoute(getRouteForLine((Polyline)p)); String id = wp.getMarker().getMarkerOptions().getIcon(); id = id.substring(id.lastIndexOf("M"), id.lastIndexOf(".")); id = id.substring(0, id.length() - 1) + " " + id.substring(id.length() - 1); contextMenu.getItems().get(0).setText("Clear \"" + id + "\"'s connection"); LatLong cxtLL = new LatLong((JSObject) o.getMember("latLng")); Point2D pt = googleMap.fromLatLngToPoint(cxtLL); Window w = MapPane.this.getScene().getWindow(); contextMenu.show( mapComponent.getWebView(), w.getX() + pt.getX() + 10, w.getY() + pt.getY()); }); } } /** * <p> * Finds the Waypoint with the same path as the specified line's path. * There are copies of Waypoints and Lines which are equal but do not * have the same underlying peer (JSObject). Therefore, picking lines * on a map which aren't really displayed will result in context menu * actions that don't invoke anything. * </p><p> * This method ensures that we reference the Waypoint and Polyline * which are in the {@link Route}'s list of waypoint and lines which * are the ones that are rendered. * </p> * @param route The owning Route * @param line the Polyline to be rendered * @return the owning {@link Waypoint} */ @Override public Waypoint getWaypointForLine(Route route, Polyline line) { for(Waypoint wp : route.getWaypoints()) { if(wp.getConnection() != null && wp.getConnection().getOptions().getPath().equals(line.getOptions().getPath())) { return wp; } } return null; } /** * Returns the {@link Polyline} which is the line really rendered * for the specified {@link Waypoint}'s connection. * * @param route The owning Route * @param wp the Waypoint to be rendered * @return */ @Override public Polyline getLineForWaypoint(Route route, Waypoint wp) { for(Polyline line : route.getLines()) { if(wp.getConnection() != null && wp.getConnection().getOptions().getPath().equals(line.getOptions().getPath())) { return line; } } return null; } /** * Returns the {@link Route} which contains the specified {@link Waypoint} * @param wp the Waypoint whose owning Route will be returned * @return the Route which contains the specified Waypoint */ @Override public Route getRouteForWaypoint(Waypoint wp) { Optional<Route> or = Optional.of(MAP_STORE.getMap(MAP_STORE.getSelectedMapName()).getRoutes().stream() .filter(r -> r.getWaypoints().stream().anyMatch(w -> w.getLatLon().equals(wp.getLatLon()))) .collect(Collectors.toList()).get(0)); return or.isPresent() ? or.get() : null; } /** * Returns the {@link Route} which contains the specified {@link Polyline} * @param p the Polyline whose owning Route will be returned * @return the Route which contains the specified Polyline */ @Override public Route getRouteForLine(Polyline p) { Optional<Route> or = Optional.of(MAP_STORE.getMap(MAP_STORE.getSelectedMapName()).getRoutes().stream() .filter(r -> r.getLines().stream().anyMatch(l -> l.getOptions().getPath().equals(p.getOptions().getPath()))) .collect(Collectors.toList()).get(0)); return or.isPresent() ? or.get() : null; } /** * Redraws the map */ @Override public void refresh() { googleMap.setZoom(googleMap.getZoom() + 1); googleMap.setZoom(googleMap.getZoom() - 1); } /** * Removes all displayed {@link Route}s from this {@code Map} */ @Override public void clearMap() { currentRoute = null; if(MAP_STORE.getSelectedMapName() == null || MAP_STORE.getMap(MAP_STORE.getSelectedMapName()) == null) { return; } clearMap(MAP_STORE.getSelectedMapName()); } /** * Removes all displayed {@link Route}s from the map specified by mapName * * @param mapName the name of the map to remove */ public void clearMap(String mapName) { for(Route r : MAP_STORE.getMap(MAP_STORE.getSelectedMapName()).getRoutes()) { clearRoute(r); } } /** * Non-destructively erases all displayed content from the map * display */ public void eraseMap() { if(MAP_STORE.getMap(MAP_STORE.getSelectedMapName()) == null) { return; } for(Route r : MAP_STORE.getMap(MAP_STORE.getSelectedMapName()).getRoutes()) { eraseRoute(r); } } /** * Deletes the currently selected map and its persistent storage. * * @param mapName the name of the map to delete */ public void deleteMap(String mapName) { MAP_STORE.deleteMap(mapName); MAP_STORE.store(); } /** * Removes all {@link Waypoint}s and {@link MapObject}s (Lines) * from the {@link Route} specified by name. * * @param name name of the {@link Route} to clear. */ public void clearRoute(String name) { for(Route r : MAP_STORE.getMap(MAP_STORE.getSelectedMapName()).getRoutes()) { if(r.getName().equals(name)) { clearRoute(r); break; } } } @Override public DirectionsRoute getSnappedDirectionsRoute(Route route) { // TODO Auto-generated method stub return null; } /** * Adds an EventHandler which can be notified of JavaScript events * arising from the map within a given {@link WebView} * * @param eventType the Event Type to monitor * @param handler the handler to be notified. */ @Override public void addMapEventHandler(MapEventType eventType, MapEventHandler handler) { googleMap.addUIEventHandler(eventType.convert(), handler); } /** * Adds an EventHandler which can be notified of JavaScript events * arising from the map object within a given {@link WebView} * * @param mapObject the {@link MapObject} event source * @param eventType the Event Type to monitor * @param handler the handler to be notified. */ @Override public void addObjectEventHandler(MapObject mapObject, MapEventType eventType, MapEventHandler handler) { googleMap.addUIEventHandler(mapObject.convert(), eventType.convert(), handler); } @Override public void addMapObject(MapObject mapObject) { // TODO Auto-generated method stub } @Override public BooleanProperty routeSnappedProperty() { // TODO Auto-generated method stub return null; } @Override public ObjectProperty<ObservableValue<MapType>> mapTypeProperty() { // TODO Auto-generated method stub return null; } @Override public ObjectProperty<ObservableValue<LatLon>> clickProperty() { // TODO Auto-generated method stub return null; } @Override public ObjectProperty<ObservableValue<LatLon>> centerMapProperty() { // TODO Auto-generated method stub return null; } /** * Called internally to configure size, position and style of the overlay. */ private void configureOverlay() { dimmer = new StackPane(); dimmer.setManaged(false); dimmerMessage = new Label(DEFAULT_OVERLAY_MESSAGE); dimmerMessage.setFont(Font.font(dimmerMessage.getFont().getFamily(), FontWeight.BOLD, 18)); dimmerMessage.setTextFill(Color.WHITE); dimmer.getChildren().add(dimmerMessage); dimmer.setStyle("-fx-background-color: rgba(0, 0, 0, 0.6);"); getChildren().add(dimmer); layoutBoundsProperty().addListener((v, o, n) -> { Platform.runLater(() -> { if(MapPane.this.getScene().getWindow() == null) return; Point2D mapPoint = contentPane.localToParent(0, 0); double topHeight = contentPane.getTop() == null ? 0 : contentPane.getTop().getLayoutBounds().getHeight(); dimmer.resizeRelocate(mapPoint.getX(), mapPoint.getY() + topHeight, contentPane.getWidth(), contentPane.getHeight() - topHeight); }); }); } /** * Returns the flag which indicates whether the overlay is currently visible * or not. * @return true if visible, false if not */ public boolean isOverlayVisible() { return overlayVisible; } /** * Sets the flag which indicates whether the overlay is currently visible * or not. * @param b true if visible, false if not */ public void setOverlayVisible(boolean b) { dimmer.setVisible(overlayVisible = b); } /** * Sets the overlay message which is the message displayed when * {@link #setOverlayVisible(boolean)} is called with "true". * @param message the message to be displayed. */ public void setOverlayMessage(String message) { dimmerMessage.setText(message); } /** * Sets the string used to set the style of the overlay. * * @param fxmlStyleString Style String such as:<br> * <b>"-fx-background-color: rgba(0,0,0,0.6); * </b></em>(the default)</em>" */ public void setOverlayStyle(String fxmlStyleString) { dimmer.setStyle(fxmlStyleString); } /** * Returns this JavaFX {@link Node} */ @Override public MapPane getNode() { return this; } /** * Return some default {@link PolylineOptions} * * @return the constructed options */ public static PolylineOptions getDefaultPolylineOptions() { return new PolylineOptions() .strokeColor("red") .visible(true) .clickable(true) .strokeWeight(2); } /** * Returns the default {@link MapOptions} * @return the default MapOptions */ public static MapOptions getDefaultMapOptions() { MapOptions options = new MapOptions(); options.mapMarker(true) .zoom(15) .overviewMapControl(false) .panControl(false) .rotateControl(false) .scaleControl(false) .streetViewControl(false) .zoomControl(false) .mapTypeControl(false) .mapType(MapType.ROADMAP); return options; } /** * Returns the default {@link MapEventHandler}. This is the handler that * converts clicks on the map to {@link Waypoint}s added. * * @return the default {@code MapEventHandler} */ private MapEventHandler getDefaultMapEventHandler() { return (JSObject obj) -> { if(currentMode == Mode.ADD_WAYPOINTS) { LatLong ll = new LatLong((JSObject) obj.getMember("latLng")); Waypoint waypoint = createWaypoint(new LatLon(ll.getLatitude(), ll.getLongitude())); addNewWaypoint(waypoint); System.out.println("clicked: " + ll.getLatitude() + ", " + ll.getLongitude()); } }; } /** * Creates the internal {@link GoogleMap} object */ private void createGoogleMap() { googleMap = mapComponent.createMap(userMapOptions == null ? DEFAULT_MAP_OPTIONS.convert() : userMapOptions.convert()); } }