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

com.gluonhq.maps.MapView Maven / Gradle / Ivy

There is a newer version: 2.0.0-ea+6
Show newest version
/*
 * Copyright (c) 2016, Gluon
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see .
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL GLUON BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package com.gluonhq.maps;

import com.gluonhq.charm.down.Platform;
import java.util.LinkedList;
import java.util.List;

import com.gluonhq.impl.maps.BaseMap;
import javafx.animation.Animation.Status;
import javafx.animation.Interpolator;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.scene.layout.Region;
import javafx.scene.shape.Rectangle;
import javafx.util.Duration;

/**
 *
 * This is the top UI element of the map component. The center location and the
 * zoom level of the map can be altered by input events (mouse/touch/gestures)
 * or by calling the methods setCenter and setZoom.
 */
public class MapView extends Region {

    private final BaseMap baseMap;
    private Timeline t;
    private final List layers = new LinkedList<>();
    private Rectangle clip;
    private MapPoint centerPoint = null;
    private boolean zooming = false;
    
    /**
     * Create a MapView component.
     */
    public MapView() {
        baseMap = new BaseMap();
        getChildren().add(baseMap);
        registerInputListeners();

        baseMap.centerLat().addListener(o -> markDirty());
        baseMap.centerLon().addListener(o -> markDirty());
        clip = new Rectangle();
        this.setClip(clip);
        this.layoutBoundsProperty().addListener(e -> {
            // in case our assigned space changes, AND in case we are requested
            // to center at a specific point, we need to re-center.
            if (centerPoint != null) {
                // we will set the center to a slightly different location first, in order 
                // to trigger the invalidationListeners.
                setCenter(centerPoint.getLatitude()+.00001, centerPoint.getLongitude()+.00001);
                setCenter(centerPoint);
            }
        });
    }

    
    private void registerInputListeners() {
        setOnMousePressed(t -> {
            if (zooming) return;
            baseMap.x0 = t.getX();
            baseMap.y0 = t.getY();
            centerPoint = null; // once the user starts moving, we don't track the center anymore.
        });
        setOnMouseDragged(t -> {
            if (zooming) return;
            baseMap.moveX(baseMap.x0 - t.getX());
            baseMap.moveY(baseMap.y0 - t.getY());
            baseMap.x0 = t.getX();
            baseMap.y0 = t.getY();
        });
        setOnZoomStarted(t -> zooming = true);
        setOnZoomFinished(t -> zooming = false);
        setOnZoom(t -> baseMap.zoom(t.getZoomFactor() - 1, t.getX(), t.getY()));
        if (Platform.isDesktop()) {
            setOnScroll(t -> baseMap.zoom(t.getDeltaY() > 1 ? .1 : -.1, t.getX(), t.getY()));
        }
    }

    /**
     * Request the map to set its zoom level to the specified value. The map
     * considers this request, but it does not guarantee the zoom level will be
     * set to the provided value
     *
     * @param zoom the requested zoom level
     */
    public void setZoom(double zoom) {
        baseMap.setZoom(zoom);
    }

    /**
     * Request the map to position itself around the specified center
     *
     * @param mapPoint
     */
    public void setCenter(MapPoint mapPoint) {
        setCenter(mapPoint.getLatitude(), mapPoint.getLongitude());
    }

    /**
     * Request the map to position itself around the specified center
     *
     * @param lat
     * @param lon
     */
    public void setCenter(double lat, double lon) {
        this.centerPoint = new MapPoint(lat, lon);
        baseMap.setCenter(lat, lon);
    }

    /**
     * Add a new layer on top of this map. Layers are displayed in order of
     * addition, with the last added layer to be on top
     *
     * @param child
     */
    public void addLayer(MapLayer child) {
        child.setBaseMap(this.baseMap);
        layers.add(child);
        this.getChildren().add(child);
    }

    /**
     * Removes the specified layer from the map
     *
     * @param child
     */
    public void removeLayer(MapLayer child) {
        layers.remove(child);
        this.getChildren().remove(child);
    }

    /**
     * Wait a bit, then move to the specified mapPoint in seconds time
     *
     * @param waitTime the time to wait before we start moving
     * @param mapPoint the destination of the move
     * @param seconds the time the move should take
     */
    public void flyTo(double waitTime, MapPoint mapPoint, double seconds) {
        if ((t != null) && (t.getStatus() == Status.RUNNING)) {
            t.stop();
        }
        double currentLat = baseMap.centerLat().get();
        double currentLon = baseMap.centerLon().get();
        t = new Timeline(
            new KeyFrame(Duration.ZERO, new KeyValue(baseMap.prefCenterLat(), currentLat), new KeyValue(baseMap.prefCenterLon(), currentLon)),
            new KeyFrame(Duration.seconds(waitTime), new KeyValue(baseMap.prefCenterLat(), currentLat), new KeyValue(baseMap.prefCenterLon(), currentLon)),
            new KeyFrame(Duration.seconds(waitTime + seconds), new KeyValue(baseMap.prefCenterLat(), mapPoint.getLatitude()), new KeyValue(baseMap.prefCenterLon(), mapPoint.getLongitude(), Interpolator.EASE_BOTH))
        );
        t.play();
    }

    private boolean dirty = false;

    protected void markDirty() {
        dirty = true;
        this.setNeedsLayout(true);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void layoutChildren() {
        final double w = getWidth();
        final double h = getHeight();

        if (dirty) {
            for (MapLayer layer : layers) {
                layer.layoutLayer();
            }
        }
        super.layoutChildren();
        dirty = false;

        // we need to get these values or we won't be notified on new changes
        baseMap.centerLon().get();
        baseMap.centerLat().get();

        // update clip
        clip.setWidth(w);
        clip.setHeight(h);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy