CB_Locator.Map.MapViewBase.java Source code

Java tutorial

Introduction

Here is the source code for CB_Locator.Map.MapViewBase.java

Source

/* 
 * Copyright (C) 2014 team-cachebox.de
 *
 * Licensed under the : GNU General Public License (GPL);
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.gnu.org/licenses/gpl.html
 *
 * 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 CB_Locator.Map;

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Timer;

import org.slf4j.LoggerFactory;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.math.Matrix4;
import com.badlogic.gdx.math.Vector2;

import CB_Locator.Coordinate;
import CB_Locator.CoordinateGPS;
import CB_Locator.Locator;
import CB_Locator.LocatorSettings;
import CB_Locator.Events.PositionChangedEvent;
import CB_Locator.Events.PositionChangedEventList;
import CB_UI_Base.Events.invalidateTextureEvent;
import CB_UI_Base.Events.invalidateTextureEventList;
import CB_UI_Base.GL_UI.CB_View_Base;
import CB_UI_Base.GL_UI.Fonts;
import CB_UI_Base.GL_UI.GL_View_Base;
import CB_UI_Base.GL_UI.IRunOnGL;
import CB_UI_Base.GL_UI.Sprites;
import CB_UI_Base.GL_UI.Controls.ZoomButtons;
import CB_UI_Base.GL_UI.GL_Listener.GL;
import CB_UI_Base.GL_UI.Main.MainViewBase;
import CB_UI_Base.GL_UI.utils.KineticPan;
import CB_UI_Base.GL_UI.utils.KineticZoom;
import CB_UI_Base.Math.CB_RectF;
import CB_UI_Base.Math.GL_UISizes;
import CB_UI_Base.Math.SizeF;
import CB_UI_Base.graphics.PolygonDrawable;
import CB_Utils.MathUtils;
import CB_Utils.Lists.CB_List;
import CB_Utils.Log.Log;
import CB_Utils.Math.Point;
import CB_Utils.Math.PointD;
import CB_Utils.Math.PointL;
import CB_Utils.Util.FileIO;
import CB_Utils.Util.IChanged;

/**
 * @author ging-buh
 * @author Longri
 * @author arbor95
 */
public abstract class MapViewBase extends CB_View_Base implements PositionChangedEvent, invalidateTextureEvent {
    final static org.slf4j.Logger log = LoggerFactory.getLogger(MapViewBase.class);
    public static int INITIAL_SETTINGS = 1;
    public static int INITIAL_THEME = 2;
    public static int INITIAL_ALL = 7;
    public static int INITIAL_WITH_OUT_ZOOM = 8;
    public static int INITIAL_SETTINGS_WITH_OUT_ZOOM = 9;
    public static final boolean debug = false;

    // ######################################

    public enum MapState {
        FREE, GPS, WP, LOCK, CAR
    }

    private MapState mapState = MapState.FREE;

    protected final int ZoomTime = 1000;
    protected ZoomButtons zoomBtn;
    protected ZoomScale zoomScale;
    protected MapScale mapScale;
    public static MapTileLoader mapTileLoader = new MapTileLoader();
    // protected boolean alignToCompass = false;
    private float mapHeading = 0;
    protected float arrowHeading = 0;
    private final Point lastMovement = new Point(0, 0);
    protected Vector2 myPointOnScreen;
    protected boolean showAccuracyCircle;
    protected int aktZoom;
    protected KineticZoom kineticZoom = null;
    private KineticPan kineticPan = null;
    protected float maxTilesPerScreen = 0;
    long posx = 8745;
    long posy = 5685;
    public PointL screenCenterW = new PointL(0, 0);
    protected PointL screenCenterT = new PointL(0, 0);
    protected int mapIntWidth;
    protected int mapIntHeight;
    protected int drawingWidth;
    protected int drawingHeight;
    long pos20y = 363904;
    long size20 = 256;
    public CoordinateGPS center = new CoordinateGPS(48.0, 12.0);
    protected boolean positionInitialized = false;
    protected OrthographicCamera camera;
    long startTime;
    Timer myTimer;
    boolean useNewInput = true;
    public float ySpeedVersatz = 200;
    protected boolean CarMode = false;
    boolean NightMode = false;
    protected boolean NorthOriented = true;

    protected final Vector2 loVector = new Vector2();
    protected final Vector2 ruVector = new Vector2();
    protected final Descriptor lo = new Descriptor();
    protected final Descriptor ru = new Descriptor();

    protected IChanged themeChangedEventHandler = new IChanged() {

        @Override
        public void isChanged() {
            MapViewBase.this.invalidateTexture();
        }
    };
    // protected LoadedSortedTiles tilesToDraw = new LoadedSortedTiles((short) 10);

    int debugcount = 0;
    protected float iconFactor = 1.5f;
    protected boolean showMapCenterCross;
    protected PolygonDrawable CrossLines = null;
    protected AccuracyDrawable accuracyDrawable = null;
    String str = "";

    protected String mapsForgeThemePath;

    public boolean GetNightMode() {
        return this.NightMode;
    }

    public void SetNightMode(boolean NightMode) {
        this.NightMode = NightMode;
    }

    public boolean GetNorthOriented() {
        return this.NorthOriented;
    }

    public void SetNorthOriented(boolean NorthOriented) {
        this.NorthOriented = NorthOriented;
    }

    @Override
    public void onShow() {
        PositionChangedEventList.Add(this);
        PositionChanged();

        CarMode = (getMapState() == MapState.CAR);
        if (!CarMode) {
            drawingWidth = mapIntWidth;
            drawingHeight = mapIntHeight;
        }

        setVisible();

        int zoom = MapTileLoader.MAX_MAP_ZOOM;
        float tmpZoom = camera.zoom;
        float faktor = 1.5f;
        faktor = faktor / iconFactor;
        while (tmpZoom > faktor) {
            tmpZoom /= 2;
            zoom--;
        }
        aktZoom = zoom;

        calcPixelsPerMeter();

    }

    @Override
    public void onHide() {
        PositionChangedEventList.Remove(this);
        setInvisible();
        onStop();// save last zoom and position
    }

    @Override
    public void dispose() {
        // remove eventHandler
        PositionChangedEventList.Remove(this);
        invalidateTextureEventList.Remove(this);
        super.dispose();
    }

    @Override
    public void onResized(CB_RectF rec) {
        if (rec.getWidth() <= 0 || rec.getHeight() <= 0)
            return;

        // wenn sich die Gre nicht gendert hat, brauchen wir nicht zu machen!
        if (rec.getWidth() == this.mapIntWidth && rec.getHeight() == this.mapIntHeight) {
            // Ausser wenn Camera == null!
            if (camera != null)
                return;
        }
        this.mapIntWidth = (int) rec.getWidth();
        this.mapIntHeight = (int) rec.getHeight(); // Gdx.graphics.getHeight();
        this.drawingWidth = (int) rec.getWidth();
        this.drawingHeight = (int) rec.getHeight();

        camera = new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());

        aktZoom = zoomBtn.getZoom();

        camera.zoom = MapTileLoader.getMapTilePosFactor(aktZoom);

        camera.position.set(0, 0, 0);

        // Log.debug(log, "MapView Size Changed MaxY=" + this.getMaxY());

        requestLayout();

    }

    @Override
    public void onStop() {
        LocatorSettings.MapInitLatitude.setValue(center.getLatitude());
        LocatorSettings.MapInitLongitude.setValue(center.getLongitude());
        LocatorSettings.lastZoomLevel.setValue(zoomBtn.getZoom());
        super.onStop();
    }

    public void setCurrentLayer(Layer newLayer) {
        if (newLayer == null) {
            LocatorSettings.CurrentMapLayer.setValue(new String[0]);
        } else {
            LocatorSettings.CurrentMapLayer.setValue(newLayer.getNames());
        }
        mapTileLoader.setCurrentLayer(newLayer);
        mapTileLoader.clearLoadedTiles();
    }

    public void addToCurrentLayer(Layer layer) {
        Layer curentLayer = mapTileLoader.getCurrentLayer();
        curentLayer.addMapsforgeLayer(layer);
        setCurrentLayer(curentLayer);
    }

    public void SetCurrentOverlayLayer(Layer newLayer) {
        if (newLayer == null) {
            LocatorSettings.CurrentMapOverlayLayer.setValue("");
        } else {
            LocatorSettings.CurrentMapOverlayLayer.setValue(newLayer.Name);
        }
        mapTileLoader.setCurrentOverlayLayer(newLayer);
        mapTileLoader.clearLoadedTiles();
    }

    protected float scale;
    protected int outScreenDraw = 0;
    public static int INITIAL_NEW_SETTINGS = 3;

    @Override
    protected void render(Batch batch) {

        if (LocatorSettings.MoveMapCenterWithSpeed.getValue() && CarMode && Locator.hasSpeed()) {

            double maxSpeed = LocatorSettings.MoveMapCenterMaxSpeed.getValue();

            double percent = Locator.SpeedOverGround() / maxSpeed;

            float diff = (float) ((this.getHeight()) / 3 * percent);
            if (diff > this.getHeight() / 3)
                diff = this.getHeight() / 3;

            ySpeedVersatz = diff;

        } else
            ySpeedVersatz = 0;

        boolean reduceFps = ((kineticZoom != null) || ((kineticPan != null) && (kineticPan.getStarted())));
        if (kineticZoom != null) {
            camera.zoom = kineticZoom.getAktZoom();
            // float tmpZoom = mapTileLoader.convertCameraZommToFloat(camera);
            // aktZoom = (int) tmpZoom;

            int zoom = MapTileLoader.MAX_MAP_ZOOM;
            float tmpZoom = camera.zoom;
            float faktor = 1.5f;
            // faktor = faktor - iconFactor + 1;
            faktor = faktor / iconFactor;
            while (tmpZoom > faktor) {
                tmpZoom /= 2;
                zoom--;
            }
            aktZoom = zoom;

            if (kineticZoom.getFertig()) {
                setZoomScale(zoomBtn.getZoom());
                GL.that.removeRenderView(this);
                kineticZoom = null;
            } else
                reduceFps = false;

            calcPixelsPerMeter();
            if (mapScale != null)
                mapScale.ZoomChanged();
            if (zoomScale != null)
                zoomScale.setZoom(MapTileLoader.convertCameraZommToFloat(camera));

        }

        if ((kineticPan != null) && (kineticPan.getStarted())) {
            long faktor = MapTileLoader.getMapTilePosFactor(aktZoom);
            Point pan = kineticPan.getAktPan();
            // debugString = pan.x + " - " + pan.y;
            // camera.position.add(pan.x * faktor, pan.y * faktor, 0);
            // screenCenterW.x = camera.position.x;
            // screenCenterW.y = camera.position.y;
            screenCenterT.x += pan.x * faktor;
            screenCenterT.y += pan.y * faktor;
            calcCenter();

            if (kineticPan.getFertig()) {
                kineticPan = null;
            } else
                reduceFps = false;
        }

        if (reduceFps) {
            GL.that.removeRenderView(this);
        }

        synchronized (screenCenterT) {
            screenCenterW.x = screenCenterT.x;
            screenCenterW.y = screenCenterT.y;
        }
        loadTiles();
        /*
         * if (alignToCompass) { camera.up.x = 0; camera.up.y = 1; camera.up.z = 0; camera.rotate(-mapHeading, 0, 0, 1); } else {
         * camera.up.x = 0; camera.up.y = 1; camera.up.z = 0; }
         */
        camera.update();

        // synchronized (screenCenterW)
        // {
        renderMapTiles(batch);
        renderSyncronOverlay(batch);
        // }
        // renderDebugInfo(batch);

        renderNonSyncronOverlay(batch);

    }

    protected abstract void renderSyncronOverlay(Batch batch);

    protected abstract void renderNonSyncronOverlay(Batch batch);

    private void renderMapTiles(Batch batch) {
        batch.disableBlending();

        float faktor = camera.zoom;
        float dx = this.thisWorldRec.getCenterPosX() - MainViewBase.mainView.getCenterPosX();
        float dy = this.thisWorldRec.getCenterPosY() - MainViewBase.mainView.getCenterPosY();

        dy -= ySpeedVersatz;

        camera.position.set(0, 0, 0);
        float dxr = dx;
        float dyr = dy;

        if (!this.NorthOriented || CarMode) {
            camera.up.x = 0;
            camera.up.y = 1;
            camera.up.z = 0;
            camera.rotate(-mapHeading, 0, 0, 1);
            double angle = mapHeading * MathUtils.DEG_RAD;
            dxr = (float) (Math.cos(angle) * dx + Math.sin(angle) * dy);
            dyr = (float) (-Math.sin(angle) * dx + Math.cos(angle) * dy);
        } else {
            camera.up.x = 0;
            camera.up.y = 1;
            camera.up.z = 0;
        }
        camera.translate(-dxr * faktor, -dyr * faktor, 0);

        camera.update();

        Matrix4 mat = camera.combined;

        batch.setProjectionMatrix(mat);

        try {
            // das Alter aller Tiles um 1 erhhen
            mapTileLoader.increaseLoadedTilesAge();
        } catch (Exception e) {
            // LogCat announces a java.util.ConcurrentModificationException
        }
        // for (int tmpzoom = zoom; tmpzoom <= zoom; tmpzoom++)
        {
            int tmpzoom = aktZoom;

            int halfMapIntWidth = mapIntWidth / 2;
            int halfMapIntHeight = mapIntHeight / 2;

            int halfDrawingtWidth = drawingWidth / 2;
            int halfDrawingHeight = drawingHeight / 2;

            loVector.set(halfMapIntWidth - halfDrawingtWidth, halfMapIntHeight - halfDrawingHeight - ySpeedVersatz);
            ruVector.set(halfMapIntWidth + halfDrawingtWidth, halfMapIntHeight + halfDrawingHeight + ySpeedVersatz);
            lo.set(screenToDescriptor(loVector, aktZoom, lo));
            ru.set(screenToDescriptor(ruVector, aktZoom, ru));

            for (int i = lo.getX(); i <= ru.getX(); i++) {
                for (int j = lo.getY(); j <= ru.getY(); j++) {
                    Descriptor desc = new Descriptor(i, j, tmpzoom, this.NightMode);
                    boolean canDraw = mapTileLoader.markToDraw(desc);
                    boolean canDrawOverlay = false;
                    if (mapTileLoader.getCurrentOverlayLayer() != null) {
                        canDrawOverlay = mapTileLoader.markToDrawOverlay(desc);
                    }

                    if (!canDraw && tmpzoom == aktZoom) {

                        // create this Tile new
                        desc.Data = this;
                        mapTileLoader.reloadTile(this, desc, aktZoom);

                        // fr den aktuellen Zoom ist kein Tile vorhanden ->
                        // kleinere Zoomfaktoren durchsuchen
                        if (!renderBiggerTiles(batch, i, j, aktZoom)) {
                            // grere Zoomfaktoren noch durchsuchen, ob davon Tiles
                            // vorhanden sind...
                            // dafr mssen aber pro fehlendem Tile mehrere kleine
                            // Tiles gezeichnet werden (4 oder 16 oder 64...)
                            // dieser Aufruf kann auch rekursiv sein...
                            renderSmallerTiles(batch, i, j, aktZoom);
                        }
                    }

                    if (mapTileLoader.getCurrentOverlayLayer() != null) {
                        if (!canDrawOverlay && tmpzoom == aktZoom) {
                            if (!renderBiggerOverlayTiles(batch, i, j, aktZoom))
                                renderSmallerOverlayTiles(batch, i, j, aktZoom);
                        }
                    }
                }
            }
        }

        // FIXME Change to Sorted List, close Texture changing!!
        /*
         * Sort First Symbols then Text!
         * 
         * Sort Symbols with Texture, close Texture changing!
         * 
         * Sort Text with TextType and Size, close Texture changing!
         */
        CB_List<TileGL_RotateDrawables> rotateList = new CB_List<TileGL_RotateDrawables>();

        mapTileLoader.sort();

        synchronized (screenCenterW) {
            for (int i = mapTileLoader.getDrawingSize() - 1; i > -1; i--) {
                TileGL tile = mapTileLoader.getDrawingTile(i);
                if (tile == null)
                    continue;

                // Faktor, mit der dieses MapTile vergrert gezeichnet
                // werden mu
                long posFactor = getscaledMapTilePosFactor(tile);

                long xPos = tile.Descriptor.getX() * posFactor * tile.getWidth() - screenCenterW.x;
                long yPos = -(tile.Descriptor.getY() + 1) * posFactor * tile.getHeight() - screenCenterW.y;
                float xSize = tile.getWidth() * posFactor;
                float ySize = tile.getHeight() * posFactor;

                // Draw Names and Symbols only from Tile with right zoom factor
                boolean addToRotateList = tile.Descriptor.getZoom() == aktZoom;

                tile.draw(batch, xPos, yPos, xSize, ySize, addToRotateList ? rotateList : null);

            }
            batch.enableBlending();

            // FIXME sort rotate List first the Symbols then the Text! sort Text with same Font!
            // Don't change the Texture (improve the Performance)

            for (int i = 0, n = rotateList.size(); i < n; i++) {
                TileGL_RotateDrawables drw = rotateList.get(i);
                if (drw != null)
                    drw.draw(batch, -mapHeading);
            }
            rotateList.truncate(0);
            rotateList = null;

        }
        mapTileLoader.clearDrawingTiles();

        if (mapTileLoader.getCurrentOverlayLayer() != null) {
            synchronized (screenCenterW) {
                for (int i = mapTileLoader.getDrawingSizeOverlay() - 1; i > -1; i--) {
                    TileGL tile = mapTileLoader.getDrawingTileOverlay(i);
                    if (tile == null)
                        continue;

                    // Faktor, mit der dieses MapTile vergrert gezeichnet
                    // werden mu
                    long posFactor = getscaledMapTilePosFactor(tile);

                    long xPos = tile.Descriptor.getX() * posFactor * tile.getWidth() - screenCenterW.x;
                    long yPos = -(tile.Descriptor.getY() + 1) * posFactor * tile.getHeight() - screenCenterW.y;
                    float xSize = tile.getWidth() * posFactor;
                    float ySize = tile.getHeight() * posFactor;
                    tile.draw(batch, xPos, yPos, xSize, ySize, rotateList);

                }
            }
            mapTileLoader.clearDrawingTilesOverlay();
        }

    }

    private long getscaledMapTilePosFactor(TileGL tile) {
        if (tile == null || tile.Descriptor == null)
            return 1;
        long result = 1;
        result = (long) (Math.pow(2.0, MapTileLoader.MAX_MAP_ZOOM - tile.Descriptor.getZoom())
                / tile.getScaleFactor());
        return result;
    }

    protected void renderDebugInfo(Batch batch) {

        CB_RectF r = this.thisWorldRec;

        Gdx.gl.glDisable(GL20.GL_SCISSOR_TEST);

        BitmapFont font = Fonts.getNormal();

        font.setColor(Color.BLACK);

        Matrix4 def = new Matrix4().setToOrtho2D(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
        def.translate(r.getX(), r.getY(), 1);
        batch.setProjectionMatrix(def);

        // str = debugString;
        font.draw(batch, str, 20, 120);

        str = "fps: " + Gdx.graphics.getFramesPerSecond();
        font.draw(batch, str, 20, 100);

        str = String.valueOf(aktZoom) + " - camzoom: " + Math.round(camera.zoom * 100) / 100;
        font.draw(batch, str, 20, 80);

        str = "lTiles: " + mapTileLoader.LoadedTilesSize() + " - qTiles: " + mapTileLoader.QueuedTilesSize();
        font.draw(batch, str, 20, 60);

        str = "lastMove: " + lastMovement.x + " - " + lastMovement.y;
        font.draw(batch, str, 20, 20);
        Gdx.gl.glEnable(GL20.GL_SCISSOR_TEST);
    }

    // FIXME make point and vPoint final and setValues!
    protected void renderPositionMarker(Batch batch) {
        PointD point = Descriptor.ToWorld(
                Descriptor.LongitudeToTileX(MapTileLoader.MAX_MAP_ZOOM, Locator.getLongitude()),
                Descriptor.LatitudeToTileY(MapTileLoader.MAX_MAP_ZOOM, Locator.getLatitude()),
                MapTileLoader.MAX_MAP_ZOOM, MapTileLoader.MAX_MAP_ZOOM);

        Vector2 vPoint = new Vector2((float) point.X, -(float) point.Y);

        myPointOnScreen = worldToScreen(vPoint);

        myPointOnScreen.y -= ySpeedVersatz;

        if (showAccuracyCircle) {

            if (accuracyDrawable == null) {
                accuracyDrawable = new AccuracyDrawable(this.mapIntWidth, this.mapIntWidth);
            }

            float radius = (pixelsPerMeter * Locator.getCoordinate().getAccuracy());

            if (radius > GL_UISizes.PosMarkerSize / 2) {
                accuracyDrawable.draw(batch, myPointOnScreen.x, myPointOnScreen.y, radius);
            }
        }

        boolean lastUsedCompass = Locator.UseMagneticCompass();
        boolean Transparency = LocatorSettings.PositionMarkerTransparent.getValue();

        int arrowId = 0;
        if (lastUsedCompass) {
            arrowId = Transparency ? 1 : 0;
        } else {
            arrowId = Transparency ? 3 : 2;
        }

        if (CarMode)
            arrowId = 15;

        Sprite arrow = Sprites.Arrows.get(arrowId);
        arrow.setRotation(-arrowHeading);
        arrow.setBounds(myPointOnScreen.x - GL_UISizes.halfPosMarkerSize,
                myPointOnScreen.y - GL_UISizes.halfPosMarkerSize, GL_UISizes.PosMarkerSize,
                GL_UISizes.PosMarkerSize);
        arrow.setOrigin(GL_UISizes.halfPosMarkerSize, GL_UISizes.halfPosMarkerSize);
        arrow.draw(batch);

    }

    protected float get_angle(float x1, float y1, float x2, float y2) {
        float opp;
        float adj;
        float ang1;

        // calculate vector differences
        opp = y1 - y2;
        adj = x1 - x2;

        if (x1 == x2 && y1 == y2)
            return (-1);

        // trig function to calculate angle
        if (adj == 0) // to catch vertical co-ord to prevent division by 0
        {
            if (opp >= 0) {
                return (0);
            } else {
                return (180);
            }
        } else {
            ang1 = (float) ((Math.atan(opp / adj)) * MathUtils.RAD_DEG);
            // the angle calculated will range from +90 degrees to -90 degrees
            // so the angle needs to be adjusted if point x1 is less or greater then x2
            if (x1 >= x2) {
                ang1 = 90 - ang1;
            } else {
                ang1 = 270 - ang1;
            }
        }
        return (ang1);
    }

    private boolean renderBiggerTiles(Batch batch, int i, int j, int zoom2) {
        // fr den aktuellen Zoom ist kein Tile vorhanden -> kleinere
        // Zoomfaktoren noch durchsuchen, ob davon Tiles vorhanden sind...
        // von dem gefundenen Tile mu dann nur ein Ausschnitt gezeichnet werden
        int ii = i / 2;
        int jj = j / 2;
        int zoomzoom = zoom2 - 1;

        Descriptor desc = new Descriptor(ii, jj, zoomzoom, this.NightMode);
        boolean canDraw = mapTileLoader.markToDraw(desc);

        if (canDraw) {
            // das Alter der benutzten Tiles nicht auf 0 setzen, da dies
            // eigentlich nicht das richtige Tile ist!!!
            // tile.Age = 0;

            return true;
        } else if ((zoomzoom >= aktZoom - 3) && (zoomzoom >= zoomBtn.getMinZoom())) {
            // fr den aktuellen Zoom ist kein Tile vorhanden -> grere
            // Zoomfaktoren noch durchsuchen, ob davon Tiles vorhanden
            // sind...
            // dafr mssen aber pro fehlendem Tile mehrere kleine Tiles
            // gezeichnet werden (4 oder 16 oder 64...)
            // dieser Aufruf kann auch rekursiv sein...
            renderBiggerTiles(batch, ii, jj, zoomzoom);
        }
        return false;
    }

    private boolean renderBiggerOverlayTiles(Batch batch, int i, int j, int zoom2) {
        // fr den aktuellen Zoom ist kein Tile vorhanden -> kleinere
        // Zoomfaktoren noch durchsuchen, ob davon Tiles vorhanden sind...
        // von dem gefundenen Tile mu dann nur ein Ausschnitt gezeichnet werden
        int ii = i / 2;
        int jj = j / 2;
        int zoomzoom = zoom2 - 1;

        Descriptor desc = new Descriptor(ii, jj, zoomzoom, this.NightMode);
        boolean canDraw = mapTileLoader.markToDrawOverlay(desc);

        if (canDraw) {

            return true;
        } else if ((zoomzoom >= aktZoom - 3) && (zoomzoom >= zoomBtn.getMinZoom())) {
            // fr den aktuellen Zoom ist kein Tile vorhanden -> grere
            // Zoomfaktoren noch durchsuchen, ob davon Tiles vorhanden
            // sind...
            // dafr mssen aber pro fehlendem Tile mehrere kleine Tiles
            // gezeichnet werden (4 oder 16 oder 64...)
            // dieser Aufruf kann auch rekursiv sein...
            renderBiggerOverlayTiles(batch, ii, jj, zoomzoom);
        }
        return false;
    }

    private void renderSmallerTiles(Batch batch, int i, int j, int zoom2) {
        // fr den aktuellen Zoom ist kein Tile vorhanden -> grere
        // Zoomfaktoren noch durchsuchen, ob davon Tiles vorhanden sind...
        // dafr mssen aber pro fehlendem Tile mehrere kleine Tiles gezeichnet
        // werden (4 oder 16 oder 64...)
        int i1 = i * 2;
        int i2 = i * 2 + 1;
        int j1 = j * 2;
        int j2 = j * 2 + 1;
        int zoomzoom = zoom2 + 1;
        for (int ii = i1; ii <= i2; ii++) {
            for (int jj = j1; jj <= j2; jj++) {
                Descriptor desc = new Descriptor(ii, jj, zoomzoom, this.NightMode);
                boolean canDraw = mapTileLoader.markToDraw(desc);
                if (canDraw) {

                } else if ((zoomzoom <= aktZoom + 0) && (zoomzoom <= MapTileLoader.MAX_MAP_ZOOM)) {
                    // fr den aktuellen Zoom ist kein Tile vorhanden -> grere
                    // Zoomfaktoren noch durchsuchen, ob davon Tiles vorhanden
                    // sind...
                    // dafr mssen aber pro fehlendem Tile mehrere kleine Tiles
                    // gezeichnet werden (4 oder 16 oder 64...)
                    // dieser Aufruf kann auch rekursiv sein...
                    renderSmallerTiles(batch, ii, jj, zoomzoom);
                }
            }
        }
    }

    private void renderSmallerOverlayTiles(Batch batch, int i, int j, int zoom2) {
        // fr den aktuellen Zoom ist kein Tile vorhanden -> grere
        // Zoomfaktoren noch durchsuchen, ob davon Tiles vorhanden sind...
        // dafr mssen aber pro fehlendem Tile mehrere kleine Tiles gezeichnet
        // werden (4 oder 16 oder 64...)
        int i1 = i * 2;
        int i2 = i * 2 + 1;
        int j1 = j * 2;
        int j2 = j * 2 + 1;
        int zoomzoom = zoom2 + 1;
        for (int ii = i1; ii <= i2; ii++) {
            for (int jj = j1; jj <= j2; jj++) {
                Descriptor desc = new Descriptor(ii, jj, zoomzoom, this.NightMode);
                boolean canDraw = mapTileLoader.markToDrawOverlay(desc);
                if (canDraw) {

                } else if ((zoomzoom <= aktZoom + 0) && (zoomzoom <= MapTileLoader.MAX_MAP_ZOOM)) {
                    // fr den aktuellen Zoom ist kein Tile vorhanden -> grere
                    // Zoomfaktoren noch durchsuchen, ob davon Tiles vorhanden
                    // sind...
                    // dafr mssen aber pro fehlendem Tile mehrere kleine Tiles
                    // gezeichnet werden (4 oder 16 oder 64...)
                    // dieser Aufruf kann auch rekursiv sein...
                    renderSmallerOverlayTiles(batch, ii, jj, zoomzoom);
                }
            }
        }
    }

    protected void loadTiles() {
        int halfMapIntWidth = mapIntWidth / 2;
        int halfMapIntHeight = mapIntHeight / 2;

        int extensionTop = (int) ((halfMapIntHeight - ySpeedVersatz) * 1.5);
        int extensionBottom = (int) ((halfMapIntHeight + ySpeedVersatz) * 1.5);
        int extensionLeft = (int) (halfMapIntWidth * 1.5);
        int extensionRight = (int) (halfMapIntWidth * 1.5);

        loVector.set(halfMapIntWidth - drawingWidth / 2 - extensionLeft,
                halfMapIntHeight - drawingHeight / 2 - extensionTop);
        ruVector.set(halfMapIntWidth + drawingWidth / 2 + extensionRight,
                halfMapIntHeight + drawingHeight / 2 + extensionBottom);
        lo.set(screenToDescriptor(loVector, aktZoom, lo));
        ru.set(screenToDescriptor(ruVector, aktZoom, ru));

        // check count of Tiles
        boolean CacheisToSmall = true;
        int cacheSize = mapTileLoader.getCacheSize();
        do {
            int x = ru.X - lo.X + 1;
            int y = ru.Y - lo.Y + 1;
            int count = x * y;
            if (count <= cacheSize) {
                CacheisToSmall = false;
            } else {
                lo.X++;
                lo.Y++;
                ru.X--;
                ru.Y--;
            }

        } while (CacheisToSmall);

        mapTileLoader.loadTiles(this, lo, ru, aktZoom);

    }

    public void InitializeMap() {
        zoomBtn.setZoom(LocatorSettings.lastZoomLevel.getValue());
        // Bestimmung der ersten Position auf der Karte
        if (!positionInitialized) {
            double lat = LocatorSettings.MapInitLatitude.getValue();
            double lon = LocatorSettings.MapInitLongitude.getValue();

            // Initialisierungskoordinaten bekannt und knnen bernommen werden
            if (lat != -1000 && lon != -1000) {
                setCenter(new CoordinateGPS(lat, lon));
                positionInitialized = true;
                // setLockPosition(0);
            } else {
                // GPS-Position bekannt?
                if (Locator.Valid()) {
                    setCenter(Locator.getCoordinate());
                    positionInitialized = true;
                } else {
                    setInitialLocation();
                }
            }
        }

        setNewSettings(INITIAL_ALL);

    }

    /**
     * Set the Initial location now. If you wont.
     */
    protected abstract void setInitialLocation();

    protected float lastDynamicZoom = -1;

    public enum InputState {
        Idle, IdleDown, Button, Pan, Zoom, PanAutomatic, ZoomAutomatic
    }

    protected InputState inputState = InputState.Idle;
    private final HashMap<Integer, Point> fingerDown = new LinkedHashMap<Integer, Point>();

    // private static String debugString = "";

    public abstract void setNewSettings(int InitialFlags);

    protected boolean setTheme(String Path) {
        if (Path.length() > 0)
            if (FileIO.FileExists(Path) && FileIO.GetFileExtension(Path).contains("xml")) {
                mapsForgeThemePath = Path;
                return true;
            }
        return false;
    }

    private void setScreenCenter(Vector2 newCenter) {
        synchronized (screenCenterT) {
            screenCenterT.x = (long) newCenter.x;
            screenCenterT.y = (long) (-newCenter.y);
        }
        // if (camera != null) camera.position.set((float) screenCenterW.x, (float) screenCenterW.y, 0);
        GL.that.renderOnce();
    }

    public void setCenter(CoordinateGPS value) {
        synchronized (screenCenterW) {

            if (center == null)
                center = new CoordinateGPS(48.0, 12.0);
            positionInitialized = true;
            center = value;
            PointD point = Descriptor.ToWorld(
                    Descriptor.LongitudeToTileX(MapTileLoader.MAX_MAP_ZOOM, center.getLongitude()),
                    Descriptor.LatitudeToTileY(MapTileLoader.MAX_MAP_ZOOM, center.getLatitude()),
                    MapTileLoader.MAX_MAP_ZOOM, MapTileLoader.MAX_MAP_ZOOM);
            setScreenCenter(new Vector2((float) point.X, (float) point.Y));
        }
    }

    /**
     * liefert die World-Koordinate in Pixel relativ zur Map in der hchsten Auflsung
     */
    protected Vector2 screenToWorld(Vector2 point) {
        // Vector2 result = new Vector2(0, 0);
        try {
            synchronized (screenCenterW) {
                point.x = screenCenterW.x + ((long) point.x - mapIntWidth / 2) * camera.zoom;
                point.y = -screenCenterW.y + ((long) point.y - mapIntHeight / 2) * camera.zoom;
            }
        } catch (Exception e) {
            // wenn hier ein Fehler auftritt, dann geben wir einen Vector 0,0 zurck!
            point.x = 0;
            point.y = 0;
        }
        return point;
    }

    public Vector2 worldToScreen(Vector2 point) {

        Vector2 result = new Vector2(0, 0);
        result.x = ((long) point.x - screenCenterW.x) / camera.zoom + (float) mapIntWidth / 2;
        result.y = -(-(long) point.y + screenCenterW.y) / camera.zoom + (float) mapIntHeight / 2;
        result.add(-(float) mapIntWidth / 2, -(float) mapIntHeight / 2);
        result.rotate(mapHeading);
        result.add((float) mapIntWidth / 2, (float) mapIntHeight / 2);
        return result;

    }

    protected Descriptor screenToDescriptor(Vector2 point, int zoom, Descriptor destDescriptor) {
        // World-Koordinaten in Pixel
        Vector2 world = screenToWorld(point);
        for (int i = MapTileLoader.MAX_MAP_ZOOM; i > zoom; i--) {
            world.x /= 2;
            world.y /= 2;
        }
        world.x /= 256;
        world.y /= 256;
        int x = (int) world.x;
        int y = (int) world.y;
        destDescriptor.set(x, y, zoom, NightMode);
        // Descriptor result = new Descriptor(x, y, zoom, NightMode);
        return destDescriptor;
    }

    @Override
    public void PositionChanged() {
        if (CarMode) {
            // im CarMode keine Netzwerk Koordinaten zulassen
            if (!Locator.isGPSprovided())
                return;
        }
        if (getCenterGps())
            setCenter(Locator.getCoordinate());

        GL.that.renderOnce();
    }

    public float pixelsPerMeter = 0;

    @Override
    public void OrientationChanged() {

        float heading = Locator.getHeading();

        // im CarMode keine Richtungs nderungen unter 20kmh
        if (CarMode && Locator.SpeedOverGround() < 20)
            heading = this.mapHeading;

        if (!this.NorthOriented || CarMode) {
            this.mapHeading = heading;
            this.arrowHeading = 0;

            // da die Map gedreht in die offScreenBmp gezeichnet werden soll,
            // muss der Bereich, der gezeichnet werden soll grer sein, wenn
            // gedreht wird.
            if (heading >= 180)
                heading -= 180;
            if (heading > 90)
                heading = 180 - heading;
            double alpha = heading / 180 * Math.PI;
            double mapHeightCalcBase = mapIntHeight + (ySpeedVersatz * 1.7);
            double mapWidthCalcBase = mapIntWidth + (ySpeedVersatz * 1.7);
            double beta = Math.atan(mapWidthCalcBase / mapHeightCalcBase);
            double gammaW = Math.PI / 2 - alpha - beta;
            // halbe Lnge der Diagonalen
            double diagonal = Math.sqrt(Math.pow(mapWidthCalcBase, 2) + Math.pow(mapHeightCalcBase, 2)) / 2;
            drawingWidth = (int) (Math.cos(gammaW) * diagonal * 2);

            double gammaH = alpha - beta;
            drawingHeight = (int) (Math.cos(gammaH) * diagonal * 2);
        } else {
            this.mapHeading = 0;
            this.arrowHeading = heading;
            drawingWidth = mapIntWidth;
            drawingHeight = mapIntHeight;
        }

        GL.that.renderOnce();
    }

    public void SetAlignToCompass(boolean value) {
        if (!value) {
            drawingWidth = mapIntWidth;
            drawingHeight = mapIntHeight;
        }
        this.NorthOriented = !value;
    }

    /**
     * Returns True, if MapState <br>
     * MapState.GPS<br>
     * MapState.LOCK<br>
     * MapState.CAR<br>
     * 
     * @return
     */
    public boolean getCenterGps() {
        return mapState != MapState.FREE && mapState != MapState.WP;
    }

    public boolean GetAlignToCompass() {
        return !this.NorthOriented;
    }

    public MapViewBase(String Name) {
        super(Name);
    }

    public MapViewBase(float X, float Y, float Width, float Height, String Name) {
        super(X, Y, Width, Height, Name);
    }

    public MapViewBase(float X, float Y, float Width, float Height, GL_View_Base Parent, String Name) {
        super(X, Y, Width, Height, Parent, Name);
    }

    public MapViewBase(CB_RectF rec, String Name) {
        super(rec, Name);
        invalidateTextureEventList.Add(this);
    }

    @Override
    public boolean onTouchDown(int x, int y, int pointer, int button) {

        if (pointer == MOUSE_WHEEL_POINTER_UP || pointer == MOUSE_WHEEL_POINTER_DOWN) {
            lastTouchPos = new Vector2(x, y);
            return true;
        }

        y = mapIntHeight - y;
        // debugString = "touchDown " + x + " - " + y;
        if (inputState == InputState.Idle) {
            fingerDown.clear();
            inputState = InputState.IdleDown;
            fingerDown.put(pointer, new Point(x, y));
        } else {
            fingerDown.put(pointer, new Point(x, y));
            if (fingerDown.size() == 2)
                inputState = InputState.Zoom;
        }

        return true;
    }

    @Override
    public boolean onTouchDragged(int x, int y, int pointer, boolean KineticPan) {

        if (pointer == MOUSE_WHEEL_POINTER_UP || pointer == MOUSE_WHEEL_POINTER_DOWN) {
            // Mouse wheel scrolling => Zoom in/out

            if (lastDynamicZoom == -1)
                lastDynamicZoom = zoomBtn.getZoom();

            float div = lastTouchPos.x - x;

            float zoomValue = div / 100f;

            int maxZoom = LocatorSettings.OsmMaxLevel.getValue();
            int minZoom = LocatorSettings.OsmMinLevel.getValue();
            float dynZoom = (lastDynamicZoom - zoomValue);

            if (dynZoom > maxZoom)
                dynZoom = maxZoom;
            if (dynZoom < minZoom)
                dynZoom = minZoom;

            if (lastDynamicZoom != dynZoom) {

                // Log.debug(log, "Mouse Zoom:" + div + "/" + zoomValue + "/" + dynZoom);

                lastDynamicZoom = dynZoom;
                zoomBtn.setZoom((int) lastDynamicZoom);
                inputState = InputState.Idle;

                kineticZoom = new KineticZoom(camera.zoom, MapTileLoader.getMapTilePosFactor(lastDynamicZoom),
                        System.currentTimeMillis(), System.currentTimeMillis() + ZoomTime);

                // kineticZoom = new KineticZoom(camera.zoom, lastDynamicZoom, System.currentTimeMillis(), System.currentTimeMillis() +
                // 1000);

                GL.that.addRenderView(MapViewBase.this, GL.FRAME_RATE_ACTION);
                GL.that.renderOnce();
                calcPixelsPerMeter();
            }

            return true;
        }

        try {
            y = mapIntHeight - y;
            // debugString = "touchDragged: " + x + " - " + y;
            // debugString = "touchDragged " + inputState.toString();
            if (inputState == InputState.IdleDown) {
                // es wurde 1x gedrckt -> testen, ob ein gewisser Minimum Bereich verschoben wurde
                Point p = fingerDown.get(pointer);
                if (p != null) {
                    // if ((Math.abs(p.x - x) > 10) || (Math.abs(p.y - y) > 10)) // this check is not necessary because this is already
                    // checked in GL.java
                    {
                        inputState = InputState.Pan;
                        // GL_Listener.glListener.addRenderView(this, frameRateAction);
                        GL.that.renderOnce();
                        // xxx startTimer(frameRateAction);
                        // xxx ((GLSurfaceView) MapViewGL.ViewGl).requestRender();
                    }
                    return false;
                }
            }
            if (inputState == InputState.Button) {
                // wenn ein Button gedrckt war -> beim Verschieben nichts machen!!!
                return false;
            }

            if ((inputState == InputState.Pan) && (fingerDown.size() == 1)) {

                if (getMapState() == MapState.CAR || getMapState() == MapState.LOCK) {
                    // fr verschieben gesperrt!
                    return false;
                } else {
                    // auf GPS oder WP ausgerichtet und wird jetzt auf Free gestellt
                    SetMapStateFree();
                }

                // Fadein ZoomButtons!
                zoomBtn.resetFadeOut();

                // GL_Listener.glListener.addRenderView(this, frameRateAction);
                GL.that.renderOnce();
                // debugString = "";
                long faktor = MapTileLoader.getMapTilePosFactor(aktZoom);
                // debugString += faktor;
                Point lastPoint = (Point) fingerDown.values().toArray()[0];
                // debugString += " - " + (lastPoint.x - x) * faktor + " - " + (y - lastPoint.y) * faktor;

                // camera.position.add((lastPoint.x - x) * faktor, (y - lastPoint.y) * faktor, 0);
                // screenCenterW.x = camera.position.x;
                // screenCenterW.y = camera.position.y;
                synchronized (screenCenterT) {
                    double angle = mapHeading * MathUtils.DEG_RAD;
                    int dx = (lastPoint.x - x);
                    int dy = (y - lastPoint.y);
                    int dxr = (int) (Math.cos(angle) * dx + Math.sin(angle) * dy);
                    int dyr = (int) (-Math.sin(angle) * dx + Math.cos(angle) * dy);
                    // debugString = dx + " - " + dy + " - " + dxr + " - " + dyr;

                    // Pan stufenlos anpassen an den aktuell gltigen Zoomfaktor
                    float tmpZoom = camera.zoom;
                    float ffaktor = 1.5f;
                    // ffaktor = ffaktor - iconFactor + 1;
                    ffaktor = ffaktor / iconFactor;
                    while (tmpZoom > ffaktor) {
                        tmpZoom /= 2;
                    }

                    screenCenterT.x += (long) (dxr * faktor * tmpZoom);
                    screenCenterT.y += (long) (dyr * faktor * tmpZoom);
                }
                calcCenter();

                // if (kineticPan == null) kineticPan = new KineticPan();
                // kineticPan.setLast(System.currentTimeMillis(), x, y);

                lastPoint.x = x;
                lastPoint.y = y;
            } else if ((inputState == InputState.Zoom) && (fingerDown.size() == 2)) {
                Point p1 = (Point) fingerDown.values().toArray()[0];
                Point p2 = (Point) fingerDown.values().toArray()[1];
                float originalDistance = (float) Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2));

                if (fingerDown.containsKey(pointer)) {
                    // neue Werte setzen
                    fingerDown.get(pointer).x = x;
                    fingerDown.get(pointer).y = y;
                    p1 = (Point) fingerDown.values().toArray()[0];
                    p2 = (Point) fingerDown.values().toArray()[1];
                }
                float currentDistance = (float) Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2));
                float ratio = originalDistance / currentDistance;
                camera.zoom = camera.zoom * ratio;

                if (camera.zoom < MapTileLoader.getMapTilePosFactor(zoomBtn.getMaxZoom())) {
                    camera.zoom = MapTileLoader.getMapTilePosFactor(zoomBtn.getMaxZoom());
                }
                if (camera.zoom > MapTileLoader.getMapTilePosFactor(zoomBtn.getMinZoom())) {
                    camera.zoom = MapTileLoader.getMapTilePosFactor(zoomBtn.getMinZoom());
                }

                lastDynamicZoom = camera.zoom;

                int zoom = MapTileLoader.MAX_MAP_ZOOM;
                float tmpZoom = camera.zoom;
                float faktor = 1.5f;
                // faktor = faktor - iconFactor + 1;
                faktor = faktor / iconFactor;
                while (tmpZoom > faktor) {
                    tmpZoom /= 2;
                    zoom--;
                }
                aktZoom = zoom;

                calcPixelsPerMeter();
                mapScale.ZoomChanged();
                zoomBtn.setZoom(aktZoom);

                if (!CarMode && zoomScale != null) {
                    zoomScale.setZoom(MapTileLoader.convertCameraZommToFloat(camera));
                    zoomScale.resetFadeOut();
                }

                return false;
            }

            // debugString = "State: " + inputState;
            return true;
        } catch (Exception ex) {
            Log.err(log, "MapView", "-onTouchDragged Error", ex);
        }
        return false;
    }

    protected void SetMapStateFree() {
        setMapState(MapState.FREE);
    }

    protected void setZoomScale(int zoom) {
        if (!CarMode && zoomScale != null)
            zoomScale.setZoom(zoom);
    }

    @Override
    public boolean onTouchUp(int x, int y, int pointer, int button) {

        if (pointer == MOUSE_WHEEL_POINTER_UP || pointer == MOUSE_WHEEL_POINTER_DOWN) {
            return true;
        }

        y = mapIntHeight - y;
        // debugString = "touchUp: " + x + " - " + y;
        // debugString = "touchUp " + inputState.toString();
        if (inputState == InputState.IdleDown) {
            // es wurde gedrckt, aber nich verschoben
            fingerDown.remove(pointer);
            inputState = InputState.Idle;
            // -> Buttons testen

            // auf Button Clicks nur reagieren, wenn aktuell noch kein Finger gedrckt ist!!!
            if (kineticPan != null)
                // bei FingerKlick (wenn Idle) sofort das kinetische Scrollen stoppen
                kineticPan = null;

            inputState = InputState.Idle;
            return false;
        }

        fingerDown.remove(pointer);
        if (fingerDown.size() == 1)
            inputState = InputState.Pan;
        else if (fingerDown.size() == 0) {
            inputState = InputState.Idle;
            // wieder langsam rendern
            GL.that.renderOnce();

            if ((kineticZoom == null) && (kineticPan == null))
                GL.that.removeRenderView(this);

            if (kineticPan != null)
                kineticPan.start();
        }

        // debugString = "State: " + inputState;

        return true;
    }

    public MapViewBase(CB_RectF rec, GL_View_Base Parent, String Name) {
        super(rec, Parent, Name);
    }

    protected void calcCenter() {
        // berechnet anhand des ScreenCenterW die Center-Coordinaten
        PointD point = Descriptor.FromWorld(screenCenterW.x, screenCenterW.y, MapTileLoader.MAX_MAP_ZOOM,
                MapTileLoader.MAX_MAP_ZOOM);

        center = new CoordinateGPS(Descriptor.TileYToLatitude(MapTileLoader.MAX_MAP_ZOOM, -point.Y),
                Descriptor.TileXToLongitude(MapTileLoader.MAX_MAP_ZOOM, point.X));
    }

    public MapViewBase(SizeF size, String Name) {
        super(size, Name);
    }

    protected void calcPixelsPerMeter() {

        float calcZoom = MapTileLoader.convertCameraZommToFloat(camera);

        Coordinate dummy = CoordinateGPS.Project(center.getLatitude(), center.getLongitude(), 90, 1000);
        double l1 = Descriptor.LongitudeToTileX(calcZoom, center.getLongitude());
        double l2 = Descriptor.LongitudeToTileX(calcZoom, dummy.getLongitude());
        double diff = Math.abs(l2 - l1);
        pixelsPerMeter = (float) ((diff * 256) / 1000);

    }

    protected float getPixelsPerMeter(int ZoomLevel) {
        Coordinate dummy = CoordinateGPS.Project(center.getLatitude(), center.getLongitude(), 90, 1000);
        double l1 = Descriptor.LongitudeToTileX(ZoomLevel, center.getLongitude());
        double l2 = Descriptor.LongitudeToTileX(ZoomLevel, dummy.getLongitude());
        double diff = Math.abs(l2 - l1);
        return (float) ((diff * 256) / 1000);
    }

    public abstract void requestLayout();

    @Override
    protected void Initial() {

    }

    public int getAktZoom() {
        return aktZoom;
    }

    @Override
    public String getReceiverName() {
        return "Core.MapView";
    }

    @Override
    protected void SkinIsChanged() {
        setBackground(Sprites.ListBack);
        invalidateTexture();
    }

    @Override
    public void invalidateTexture() {
        setNewSettings(INITIAL_THEME);
        mapTileLoader.clearLoadedTiles();
        mapScale.ZoomChanged();

        GL.that.RunOnGLWithThreadCheck(new IRunOnGL() {
            @Override
            public void run() {
                if (CrossLines != null)
                    CrossLines.dispose();
                CrossLines = null;
            }
        });

    }

    @Override
    public Priority getPriority() {
        return Priority.Normal;
    }

    public MapState getMapState() {
        return mapState;
    }

    public void setMapState(MapState state) {
        if (mapState == state)
            return;

        mapState = state;

        boolean wasCarMode = CarMode;

        if (mapState == MapState.CAR) {
            if (wasCarMode)
                return; // Brauchen wir nicht noch einmal machen!

            // Car mode
            CarMode = true;
            invalidateTexture();
        } else if (mapState == MapState.WP) {
            MapStateChangedToWP();
        } else if (mapState == MapState.LOCK || mapState == MapState.GPS) {
            setCenter(Locator.getCoordinate());
        }

        if (mapState != MapState.CAR) {
            if (!wasCarMode)
                return; // brauchen wir nicht noch einmal machen

            CarMode = false;
            invalidateTexture();
        }

    }

    public abstract void MapStateChangedToWP();

    public void SetZoom(int newZoom) {

        if (zoomBtn != null) {
            if (zoomBtn.getZoom() != newZoom) {
                zoomBtn.setZoom(newZoom);
            }
        }

        setZoomScale(newZoom);
        if (zoomScale != null)
            zoomScale.resetFadeOut();
        inputState = InputState.Idle;

        lastDynamicZoom = newZoom;

        kineticZoom = new KineticZoom(camera.zoom, MapTileLoader.getMapTilePosFactor(newZoom),
                System.currentTimeMillis(), System.currentTimeMillis() + ZoomTime);
        GL.that.addRenderView(MapViewBase.this, GL.FRAME_RATE_ACTION);
        GL.that.renderOnce();
        calcPixelsPerMeter();
    }

    public boolean isCarMode() {
        return CarMode;
    }

}