All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.lynden.gmapsfx.javascript.object.GoogleMap Maven / Gradle / Ivy

/*
 * Copyright 2014 Lynden, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * 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 CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.lynden.gmapsfx.javascript.object;

import com.lynden.gmapsfx.javascript.JavascriptObject;
import com.lynden.gmapsfx.javascript.JavascriptRuntime;
import com.lynden.gmapsfx.javascript.event.EventHandlers;
import com.lynden.gmapsfx.javascript.event.GFXEventHandler;
import com.lynden.gmapsfx.javascript.event.MapStateEventType;
import com.lynden.gmapsfx.javascript.event.StateEventHandler;
import com.lynden.gmapsfx.javascript.event.UIEventHandler;
import com.lynden.gmapsfx.javascript.event.UIEventType;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.value.ObservableValue;
import javafx.geometry.Point2D;
import javafx.util.Callback;
import netscape.javascript.JSObject;

/**
 *
 * @author Rob Terpilowski
 */
public class GoogleMap extends JavascriptObject {

    private boolean userPromptedZoomChange;
    private boolean mapPromptedZoomChange;
    protected MapOptions options;
    protected static String divArg = "document.getElementById('map-canvas')";

    private ReadOnlyObjectWrapper center;
    private IntegerProperty zoom;
    private ReadOnlyObjectWrapper bounds;
    
    private final EventHandlers jsHandlers = new EventHandlers();
    private boolean registeredOnJS;
    
    private Set markers;
    
    public GoogleMap() {
        super(GMapObjectType.MAP, divArg);
    }

    public GoogleMap(MapOptions mapOptions) {
        super(GMapObjectType.MAP, new Object[]{divArg, mapOptions});
    }

    public void setZoom(int zoom) {
        zoomProperty().set(zoom);
    }

    public int getZoom() {
        return zoomProperty().get();
    }

    private int internalGetZoom() {
        return (int) invokeJavascript("getZoom");
    }

    private void internalSetZoom(int zoom) {
        invokeJavascript("setZoom", zoom);
    }
    
    public void showDirectionsPane() {
        JavascriptRuntime.getInstance().execute("showDirections()");
    }
    
    public void hideDirectionsPane() {
        JavascriptRuntime.getInstance().execute("hideDirections()");
    }

    public IntegerProperty zoomProperty() {
        if (zoom == null) {
            zoom = new SimpleIntegerProperty(internalGetZoom());
            addStateEventHandler(MapStateEventType.zoom_changed, () -> {
                if (!userPromptedZoomChange) {
                    mapPromptedZoomChange = true;
                    zoom.set(internalGetZoom());
                    mapPromptedZoomChange = false;
                }
            });
            zoom.addListener((ObservableValue obs, Number o, Number n) -> {
                if (!mapPromptedZoomChange) {
                    userPromptedZoomChange = true;
                    internalSetZoom(n.intValue());
                    userPromptedZoomChange = false;
                }
            });
        }
        return zoom;
    }

//    This method was calling setCenter anyway, so would never have worked.
//    public LatLong getLatLong() {
//        return getProperty("setCenter", LatLong.class);
//    }
    
    public final ReadOnlyObjectProperty centerProperty() {
        if (center == null) {
            center = new ReadOnlyObjectWrapper<>(getCenter());
            addStateEventHandler(MapStateEventType.center_changed, () -> {
                center.set(getCenter());
            });
        }
        return center.getReadOnlyProperty();
    }

    public LatLong getCenter() {
        return new LatLong((JSObject) invokeJavascript("getCenter"));
    }
    
    public void setCenter(LatLong latLong) {
        invokeJavascript("setCenter", latLong);
    }
    
    
    /**
     * Returns the LatLongBounds of the visual area. Note: on zoom changes the
     * bounds are reset after the zoom event is fired, which can cause
     * unexpected results.
     *
     * @return
     */
    public LatLongBounds getBounds() {
        return invokeJavascriptReturnValue("getBounds", LatLongBounds.class);
    }
    
    /** Moves the map to ensure the given bounds fit within the viewport.
     * 

* Note that the Google Maps API will add a buffer around this value, so * assuming you can store this and use it to later restore the view * will give incorrect results. Calling map.fitBounds(map.getBounds()); will * result in the map gradually zooming outward. *

* * @param bounds */ public void fitBounds( LatLongBounds bounds ) { invokeJavascript("fitBounds", bounds ); } public void panToBounds(LatLongBounds bounds) { invokeJavascript("panToBounds", bounds); } /** A property tied to the map, updated when the idle state event is fired. * * @return */ public final ReadOnlyObjectProperty boundsProperty() { if (bounds == null) { bounds = new ReadOnlyObjectWrapper<>(getBounds()); addStateEventHandler(MapStateEventType.idle, () -> { bounds.set(getBounds()); }); } return bounds.getReadOnlyProperty(); } public void setHeading( double heading ) { invokeJavascript("setHeading", heading); } public double getHeading() { return invokeJavascriptReturnValue("getHeading", Double.class ); } /** Adds the supplied marker to the map. * * @param marker */ public void addMarker(Marker marker) { if (markers == null) { markers = new HashSet<>(); } markers.add(marker); marker.setMap(this); } /** Removes the supplied marker from the map. * * @param marker */ public void removeMarker(Marker marker) { if (markers != null && markers.contains(marker)) { markers.remove(marker); } marker.setMap(null); } /** Removes all of the markers from the map. * */ public void clearMarkers() { if (markers != null && ! markers.isEmpty()) { markers.forEach((m) -> { m.setMap(null); }); markers.clear(); } } /** Adds all of the markers in the supplied collection to the map. Existing * markers, if any, are retained. * * @param col */ public void addMarkers(Collection col) { if (markers == null) { markers = new HashSet<>(col); } else { markers.addAll(col); } col.forEach((m) -> { m.setMap(this); }); } public void addMarkers(Collection col, UIEventType type, Callback h) { if (markers == null) { markers = new HashSet<>(col); } else { markers.addAll(col); } col.forEach((m) -> { m.setMap(this); addUIEventHandler(m, type, h.call(m)); }); } /** Removes the markers in the supplied collection from the map. * * @param col */ public void removeMarkers(Collection col) { if (markers != null && ! markers.isEmpty()) { markers.removeAll(col); col.forEach((m) -> { m.setMap(null); }); } } /** Sets the map type. This is equivalent to the javascript method * setMapTypeId. * * @param type */ public void setMapType(MapTypeIdEnum type) { invokeJavascript("setMapTypeId", type); } public void addMapShape(MapShape shape) { shape.setMap(this); } public void removeMapShape(MapShape shape) { shape.setMap(null); } public Projection getProjection() { Object obj = invokeJavascript("getProjection"); return (obj == null) ? null : new Projection((JSObject) obj); } /** * Pans the map by the supplied values. * * @param x delta x value in pixels. * @param y delta y value in pixels. */ public void panBy(double x, double y) { // System.out.println("panBy x: " + x + ", y: " + y); invokeJavascript("panBy", new Object[]{x, y}); } /** * Pans the map to the specified latitude and longitude. * * @param latLong */ public void panTo(LatLong latLong) { invokeJavascript("panTo", latLong); } /** * Returns the screen point for the provided LatLong. Note: Unexpected * results can be obtained if this method is called as a result of a zoom * change, as the zoom event is fired before the bounds are updated, and * bounds need to be used to obtain the answer! *

* One workaround is to only operate off bounds_changed events. * * @param loc * @return */ public Point2D fromLatLngToPoint(LatLong loc) { // System.out.println("GoogleMap.fromLatLngToPoint loc: " + loc); Projection proj = getProjection(); //System.out.println("map.fromLatLngToPoint Projection: " + proj); LatLongBounds llb = getBounds(); // System.out.println("GoogleMap.fromLatLngToPoint Bounds: " + llb); GMapPoint topRight = proj.fromLatLngToPoint(llb.getNorthEast()); // System.out.println("GoogleMap.fromLatLngToPoint topRight: " + topRight); GMapPoint bottomLeft = proj.fromLatLngToPoint(llb.getSouthWest()); // System.out.println("GoogleMap.fromLatLngToPoint bottomLeft: " + bottomLeft); double scale = Math.pow(2, getZoom()); GMapPoint worldPoint = proj.fromLatLngToPoint(loc); // System.out.println("GoogleMap.fromLatLngToPoint worldPoint: " + worldPoint); double x = (worldPoint.getX() - bottomLeft.getX()) * scale; double y = (worldPoint.getY() - topRight.getY()) * scale; // System.out.println("GoogleMap.fromLatLngToPoint x: " + x + " y: " + y); return new Point2D(x, y); } /** * Registers an event handler in the repository shared between Javascript * and Java. * * @param h Event handler to be registered. * @return Callback key that Javascript will use to find this handler. */ private String registerEventHandler(GFXEventHandler h) { //checkInitialized(); if (!registeredOnJS) { JSObject doc = (JSObject) runtime.execute("document"); doc.setMember("jsHandlers", jsHandlers); registeredOnJS = true; } return jsHandlers.registerHandler(h); } /** * Adds a handler for a mouse type event on the map. * * @param type Type of the event to register against. * @param h Handler that will be called when the event occurs. */ public void addUIEventHandler(UIEventType type, UIEventHandler h) { this.addUIEventHandler(this, type, h); } /** * Adds a handler for a mouse type event on the map. * * @param obj The object that the event should be registered on. * @param type Type of the event to register against. * @param h Handler that will be called when the event occurs. */ public void addUIEventHandler(JavascriptObject obj, UIEventType type, UIEventHandler h) { String key = registerEventHandler(h); String mcall = "google.maps.event.addListener(" + obj.getVariableName() + ", '" + type.name() + "', " + "function(event) {document.jsHandlers.handleUIEvent('" + key + "', event);});";//.latLng //System.out.println("addUIEventHandler mcall: " + mcall); runtime.execute(mcall); } /** * Adds a handler for a state type event on the map. *

* We could allow this to handle any state event by adding a parameter * JavascriptObject obj, but we would then need to loosen up the event type * and either accept a String value, or fill an enum with all potential * state events. * * @param type Type of the event to register against. * @param h Handler that will be called when the event occurs. */ public void addStateEventHandler(MapStateEventType type, StateEventHandler h) { String key = registerEventHandler(h); String mcall = "google.maps.event.addListener(" + getVariableName() + ", '" + type.name() + "', " + "function() {document.jsHandlers.handleStateEvent('" + key + "');});"; //System.out.println("addStateEventHandler mcall: " + mcall); runtime.execute(mcall); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy