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

org.oscim.map.Map Maven / Gradle / Ivy

/*
 * Copyright 2013 Hannes Janetzek
 * Copyright 2016 Andrey Novikov
 * Copyright 2016 Stephan Leuschner
 * Copyright 2016-2017 devemux86
 * Copyright 2016 Longri
 *
 * This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
 *
 * This program is free software: you can redistribute it and/or modify it under the
 * terms of the GNU Lesser 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License along with
 * this program. If not, see .
 */
package org.oscim.map;

import org.oscim.core.BoundingBox;
import org.oscim.core.Box;
import org.oscim.core.MapPosition;
import org.oscim.event.Event;
import org.oscim.event.EventDispatcher;
import org.oscim.event.EventListener;
import org.oscim.event.Gesture;
import org.oscim.event.MotionEvent;
import org.oscim.layers.AbstractMapEventLayer;
import org.oscim.layers.Layer;
import org.oscim.layers.MapEventLayer;
import org.oscim.layers.MapEventLayer2;
import org.oscim.layers.tile.TileLayer;
import org.oscim.layers.tile.vector.OsmTileLayer;
import org.oscim.layers.tile.vector.VectorTileLayer;
import org.oscim.renderer.MapRenderer;
import org.oscim.theme.IRenderTheme;
import org.oscim.theme.ThemeFile;
import org.oscim.theme.ThemeLoader;
import org.oscim.tiling.TileSource;
import org.oscim.utils.Parameters;
import org.oscim.utils.ThreadUtils;
import org.oscim.utils.async.AsyncExecutor;
import org.oscim.utils.async.TaskQueue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public abstract class Map implements TaskQueue {

    private static final Logger log = LoggerFactory.getLogger(Map.class);

    /**
     * Listener interface for map update notifications.
     * Layers implementing this interface they will be automatically register
     * when the layer is added to the map and unregistered when the layer is
     * removed. Otherwise use map.events.bind(UpdateListener).
     */
    public interface UpdateListener extends EventListener {
        void onMapEvent(Event e, MapPosition mapPosition);
    }

    /**
     * Listener interface for input events.
     * Layers implementing this interface they will be automatically register
     * when the layer is added to the map and unregistered when the layer is
     * removed.
     */

    public interface InputListener extends EventListener {
        void onInputEvent(Event e, MotionEvent motionEvent);
    }

    /**
     * UpdateListener event. Map position has changed.
     */
    public static final Event POSITION_EVENT = new Event();

    /**
     * UpdateListener event. Map was moved by user.
     */
    public static final Event MOVE_EVENT = new Event();

    /**
     * UpdateListener event. Map was scaled by user.
     */
    public static final Event SCALE_EVENT = new Event();

    /**
     * UpdateListener event. Map was rotated by user.
     */
    public static final Event ROTATE_EVENT = new Event();

    /**
     * UpdateListener event. Map was tilted by user.
     */
    public static final Event TILT_EVENT = new Event();

    /**
     * UpdateLister event. Delivered on main-thread when updateMap() was called
     * and no CLEAR_EVENT or POSITION_EVENT was triggered.
     */
    public static final Event UPDATE_EVENT = new Event();

    /**
     * UpdateListerner event. Map state has changed in a way that all layers
     * should clear their state e.g. the theme or the TilesSource has changed.
     * TODO should have an event-source to only clear affected layers.
     */
    public static final Event CLEAR_EVENT = new Event();

    public static final Event ANIM_END = new Event();

    public static final Event ANIM_START = new Event();

    public final EventDispatcher input;
    public final EventDispatcher events;

    private final Layers mLayers;
    private final ViewController mViewport;
    private final AsyncExecutor mAsyncExecutor;

    protected final Animator mAnimator;
    protected final MapPosition mMapPosition;

    protected final AbstractMapEventLayer mEventLayer;

    protected boolean mClearMap = true;

    public Map() {
        ThreadUtils.init();

        mViewport = new ViewController();
        mAnimator = new Animator(this);
        mLayers = new Layers(this);

        input = new EventDispatcher() {

            @Override
            public void tell(InputListener l, Event e, MotionEvent d) {
                l.onInputEvent(e, d);
            }
        };
        events = new EventDispatcher() {

            @Override
            public void tell(UpdateListener l, Event e, MapPosition d) {
                l.onMapEvent(e, d);
            }
        };

        mAsyncExecutor = new AsyncExecutor(4, this);
        mMapPosition = new MapPosition();

        if (Parameters.MAP_EVENT_LAYER2)
            mEventLayer = new MapEventLayer2(this);
        else
            mEventLayer = new MapEventLayer(this);
        mLayers.add(0, mEventLayer);

    }

    public AbstractMapEventLayer getEventLayer() {
        return mEventLayer;
    }

    /**
     * Create OsmTileLayer with given TileSource and
     * set as base map (layer 1)
     * 

* TODO deprecate */ public VectorTileLayer setBaseMap(TileSource tileSource) { VectorTileLayer l = new OsmTileLayer(this); l.setTileSource(tileSource); setBaseMap(l); return l; } public TileLayer setBaseMap(TileLayer tileLayer) { mLayers.add(1, tileLayer); return tileLayer; } /** * Utility function to set theme of base vector-layer and * use map background color from theme. */ public void setTheme(ThemeFile theme) { setTheme(theme, false); } /** * Utility function to set theme of base vector-layer, optionally * to all vector layers and use map background color from theme. */ public void setTheme(ThemeFile theme, boolean allLayers) { setTheme(ThemeLoader.load(theme), allLayers); } public void setTheme(IRenderTheme theme) { setTheme(theme, false); } public void setTheme(IRenderTheme theme, boolean allLayers) { if (theme == null) { throw new IllegalArgumentException("Theme cannot be null."); } boolean themeSet = false; for (Layer layer : mLayers) { if (layer instanceof VectorTileLayer) { ((VectorTileLayer) layer).setRenderTheme(theme); themeSet = true; if (!allLayers) break; } } if (!themeSet) { log.error("No vector layers set"); throw new IllegalStateException(); } MapRenderer.setBackgroundColor(theme.getMapBackground()); clearMap(); } public void destroy() { mLayers.destroy(); mAsyncExecutor.dispose(); } /** * Request call to onUpdate for all layers. This function can * be called from any thread. Request will be handled on main * thread. * * @param redraw pass true to render next frame afterwards */ public abstract void updateMap(boolean redraw); /** * Request to render a frame. Request will be handled on main * thread. Use this for animations in RenderLayers. */ public abstract void render(); /** * Post a runnable to be executed on main-thread */ @Override public abstract boolean post(Runnable action); /** * Post a runnable to be executed on main-thread. Execution is delayed for * at least 'delay' milliseconds. */ public abstract boolean postDelayed(Runnable action, long delay); /** * Post a task to run on a shared worker-thread. Should only use for * tasks running less than a second. */ @Override public void addTask(Runnable task) { mAsyncExecutor.post(task); } /** * Return screen width in pixel. */ public abstract int getWidth(); /** * Return screen height in pixel. */ public abstract int getHeight(); /** * Request to clear all layers before rendering next frame */ public void clearMap() { mClearMap = true; updateMap(true); } /** * Set {@link MapPosition} of {@link Viewport} and trigger a redraw. */ public void setMapPosition(final MapPosition mapPosition) { if (!ThreadUtils.isMainThread()) post(new Runnable() { @Override public void run() { mViewport.setMapPosition(mapPosition); updateMap(true); } }); else { mViewport.setMapPosition(mapPosition); updateMap(true); } } public void setMapPosition(double latitude, double longitude, double scale) { mViewport.setMapPosition(new MapPosition(latitude, longitude, scale)); updateMap(true); } /** * Get current {@link MapPosition} or at possible animation end. * * @param animationEnd map position at animation end (valid with Animator.animateTo methods) * @param mapPosition reuse MapPosition instance * @return true when MapPosition was updated (has changed) */ public boolean getMapPosition(boolean animationEnd, MapPosition mapPosition) { if (animationEnd) { if (animator().isActive()) { mapPosition.copy(animator().getEndPosition()); return true; } } if (!ThreadUtils.isMainThread()) { return mViewport.getSyncMapPosition(mapPosition); } return mViewport.getMapPosition(mapPosition); } /** * Get current {@link MapPosition}. * * @return true when MapPosition was updated (has changed) */ public boolean getMapPosition(MapPosition mapPosition) { return getMapPosition(false, mapPosition); } /** * Get current {@link MapPosition}. Consider using * getViewport.getMapPosition(pos) instead to reuse * MapPosition instance. */ public MapPosition getMapPosition() { MapPosition pos = new MapPosition(); mViewport.getMapPosition(pos); return pos; } public BoundingBox getBoundingBox(int expand) { Box box = new Box(); mViewport.getBBox(box, expand); box.map2mercator(); return new BoundingBox(box.ymin, box.xmin, box.ymax, box.xmax); } /** * @return Viewport instance */ public ViewController viewport() { return mViewport; } /** * @return Layers instance */ public Layers layers() { return mLayers; } /** * @return MapAnimator instance */ public Animator animator() { return mAnimator; } /** * This function is run on main-thread before rendering a frame. *

* For internal use only. Do not call! */ protected void prepareFrame() { ThreadUtils.assertMainThread(); MapPosition pos = mMapPosition; mAnimator.updateAnimation(); boolean changed = mViewport.getMapPosition(pos); boolean sizeChanged = mViewport.sizeChanged(); if (mClearMap) events.fire(CLEAR_EVENT, pos); else if (changed || sizeChanged) events.fire(POSITION_EVENT, pos); else events.fire(UPDATE_EVENT, pos); mClearMap = false; mAnimator.updateAnimation(); mViewport.syncViewport(); } public boolean handleGesture(Gesture g, MotionEvent e) { return mLayers.handleGesture(g, e); } /** * Called on render thread, use synchronized! */ public abstract void beginFrame(); /** * Called on render thread, use synchronized! */ public abstract void doneFrame(boolean needsRedraw); }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy