Package com.mapmidlet.tile.ui

Source Code of com.mapmidlet.tile.ui.TileCanvas

/*
* 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; version 3 of the License.
*
* 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.
*
* Author: Damian Waradzyn
*/
package com.mapmidlet.tile.ui;

import henson.midp.Float11;

import java.util.*;

import javax.microedition.lcdui.*;
import javax.microedition.lcdui.game.GameCanvas;

import com.mapmidlet.CloudGps;
import com.mapmidlet.gps.GpsState;
import com.mapmidlet.gps.GpsState.Satellite;
import com.mapmidlet.misc.IOTool;
import com.mapmidlet.options.Options;
import com.mapmidlet.projection.*;
import com.mapmidlet.routing.*;
import com.mapmidlet.routing.Route.RouteDirection;
import com.mapmidlet.tile.provider.*;

/**
* This canvas shows the map and some controls on the screen. This class is also
* responsible for updating various variables (tile management, reading and
* interpolating GPS position and so on) and repainting constantly. Besides that
* it handles interaction with user - keys and touchscreen.
*
* @author Damian Waradzyn
*/
public class TileCanvas extends GameCanvas implements Runnable {

    private final double MAX_SCROLL_SPEED = 5.0;
    private final double SCROLL_SPEED_DELTA = MAX_SCROLL_SPEED / 10.0;

    private AbstractTileFactory tileFactory;

    private Tile[][][] tiles = null;
    private int currentTilesIdx = 1;
    public int latitude;
    public int longitude;
    private int zoom;
    public double x, y;
    double dx = 0.0, dy = 0.0;
    public int horizontalTilesCount;
    public int verticalTilesCount;
    private boolean followGps;
    private boolean followMarker;
    public int tileSize;
    private ScreenMarker draggedMarker;
    private boolean dragging = false;

    boolean doubleBuffered;
    private Image buffer = null;
    private boolean running = false;

    private final Options options = Options.getInstance();

    public boolean doZoomIn, doZoomOut, doScreenSizeChanged, doChangeFollowMode, doCycleRouteEnds, firePressed;
    private GpsState gpsState = new GpsState();

    private ScreenCoordinate gpsCurrent;

    public TileCanvas() {
        super(false);
        doubleBuffered = isDoubleBuffered();
        if (!doubleBuffered) {
            buffer = Image.createImage(getMapWidth(), getMapHeight());
        }
    }

    long lastRouteCalcMilis = -1;

    public void run() {
        while (true) {
            try {
                if (running) {
                    if (doScreenSizeChanged) {
                        createTiles();
                        doScreenSizeChanged = false;
                    }
                    if (doZoomIn) {
                        zoomIn();
                        doZoomIn = false;
                    }
                    if (doZoomOut) {
                        zoomOut();
                        doZoomOut = false;
                    }
                    if (options.routeEndIndex < 0) {
                        followMarker = false;
                    }
                    processMarkerDragging();
                    updateCoordinates();

                    if (firePressed) {
                        if (options.routeEndIndex >= 0) {
                            ScreenMarker marker = (ScreenMarker) options.routeEnds.elementAt(options.routeEndIndex);
                            marker.worldCoordinate = ProjectionTool.toWorldCoordinate(marker.tileCoordinate, tileSize);
                            marker.raised = false;
                            followMarker = false;
                            options.routeEndIndex = -1;
                        }
                        firePressed = false;
                    }
                    if (doCycleRouteEnds) {
                        doCycleRouteEnds = false;
                        cycleRouteEnds();
                    }

                    gpsState = new GpsState(CloudGps.gpsState);

                    // if (gpsState.state < GpsState.CONNECTED_FIX) {
                    // doChangeFollowMode = false;
                    // }
                    Navigation.navigate(gpsState);

                    gpsState.performCalculations();

                    if (doChangeFollowMode) {
                        changeFollowGps();
                        doChangeFollowMode = false;
                    }

                    if (gpsState.state >= GpsState.CONNECTED_FIX) {
                        calculateGpsCurrent();
                    }

                    if (followMarker && options.automaticRouteCalc
                            && System.currentTimeMillis() - lastRouteCalcMilis > 10000) {
                        ScreenMarker marker = (ScreenMarker) options.routeEnds.elementAt(options.routeEndIndex);
                        if (marker.tileCoordinate != null) {

                            marker.worldCoordinate = ProjectionTool.toWorldCoordinate(marker.tileCoordinate, tileSize);
                            CloudGps.calculateRoute(false);
                            lastRouteCalcMilis = System.currentTimeMillis();
                        }
                    }

                    repaint();
                    Thread.sleep(20);
                } else {
                    Thread.yield();
                }
            } catch (Throwable e) {
                CloudGps.setError(e);
            }
        }
    }

    private void cycleRouteEnds() {
        ScreenMarker marker;
        if (options.routeEndIndex >= 0) {
            marker = (ScreenMarker) options.routeEnds.elementAt(options.routeEndIndex);
            marker.raiseChangeMilis = System.currentTimeMillis();
            marker.worldCoordinate = ProjectionTool.toWorldCoordinate(marker.tileCoordinate, tileSize);
            marker.raised = false;
        }
        options.routeEndIndex++;
        if (options.routeEndIndex > 1) {
            options.routeEndIndex = -1;
            followMarker = false;
        } else {
            marker = (ScreenMarker) options.routeEnds.elementAt(options.routeEndIndex);
            marker.visible = true;
            marker.raised = true;
            marker.raiseChangeMilis = System.currentTimeMillis();
            followMarker = true;
            if (Double.isNaN(marker.worldCoordinate.latitude)) {
                marker.worldCoordinate = getCurrentCenter();
            }
        }
    }

    protected void keyPressed(int keyCode) {
        // System.out.println(getKeyName(keyCode));
        if (keyCode == KEY_STAR) {
            doZoomOut = true;
        } else if (keyCode == KEY_POUND) {
            doZoomIn = true;
        } else if (keyCode == KEY_NUM0) {
            doCycleRouteEnds = true;
        } else if (keyCode == KEY_NUM2) {
            doChangeFollowMode = true;
        } else if (keyCode == KEY_NUM1) {
            decreaseReplaySpeed();
        } else if (keyCode == KEY_NUM3) {
            increaseReplaySpeed();
        } else if (keyCode == FIRE || getKeyName(keyCode).equals("SELECT")) {
            firePressed = true;
        }
    }

    protected void keyRepeated(int keyCode) {
        keyPressed(keyCode);
    }

    private void calculateGpsCurrent() {
        gpsCurrent = toScreenCoordinate(gpsState.tileCoordinate, x, y);

        if (gpsState.prevTileCoordinate != null) {
            ScreenCoordinate gpsPrevious = toScreenCoordinate(gpsState.prevTileCoordinate, x, y);
            if (gpsState.coordinateUpdateMilis - gpsState.prevCoordinateUpdateMilis != 0) {
                double factor = (System.currentTimeMillis() - gpsState.coordinateUpdateMilis)
                        / (double) (gpsState.coordinateUpdateMilis - gpsState.prevCoordinateUpdateMilis);
                if (factor > 1.0) {
                    factor = 1.0;
                }
                gpsCurrent.x = (int) (gpsPrevious.x + factor * (gpsCurrent.x - gpsPrevious.x));
                gpsCurrent.y = (int) (gpsPrevious.y + factor * (gpsCurrent.y - gpsPrevious.y));
            }
        }
    }

    private void updateTiles(int horizontalShift, int verticalShift) {
        // Manual image deallocation - helps some KVMs with memory management.
        deallocateTiles(horizontalShift, verticalShift);

        for (int i = 0; i < tiles.length; i++) {
            for (int j = 0; j < tiles[i].length; j++) {
                int newPosX = i - horizontalShift;
                int newPosY = j - verticalShift;
                if (newPosX >= 0 && newPosX < horizontalTilesCount && newPosY >= 0 && newPosY < verticalTilesCount) {

                    tiles[i][j][1 - currentTilesIdx] = tiles[newPosX][newPosY][currentTilesIdx];
                } else {
                    Tile newTile = tileFactory.createTile(tiles[i][j][currentTilesIdx], horizontalShift, verticalShift);
                    tiles[i][j][1 - currentTilesIdx] = newTile;
                }
            }
        }
        currentTilesIdx = 1 - currentTilesIdx;
    }

    private void deallocateTiles(int horizontalShift, int verticalShift) {
        for (int i = getLowerDeallocateBound(horizontalTilesCount, horizontalShift); i < getUpperDeallocateBound(
                horizontalTilesCount, horizontalShift); i++) {
            for (int j = 0; j < verticalTilesCount; j++) {
                tiles[i][j][currentTilesIdx].deallocate();
            }
        }
        for (int i = 0; i < horizontalTilesCount; i++) {
            for (int j = getLowerDeallocateBound(verticalTilesCount, verticalShift); j < getUpperDeallocateBound(
                    verticalTilesCount, verticalShift); j++) {
                tiles[i][j][currentTilesIdx].deallocate();
            }
        }
    }

    private int getLowerDeallocateBound(int size, int shift) {
        return shift < 0 ? 0 : (size - shift) < 0 ? 0 : (size - shift);
    }

    private int getUpperDeallocateBound(int size, int shift) {
        return shift < 0 ? (-shift < size ? -shift : size) : size;
    }

    private void updateCoordinates() {
        dx *= 0.95;
        dy *= 0.95;

        if (pressedTime > 0 && System.currentTimeMillis() - pressedTime > 250) {
            dx *= 0.25;
            dy *= 0.25;
        }

        if (getKeyStates() != 0) {
            followGps = false;
            if ((getKeyStates() & LEFT_PRESSED) != 0) {
                dx += SCROLL_SPEED_DELTA;
            }
            if ((getKeyStates() & RIGHT_PRESSED) != 0) {
                dx -= SCROLL_SPEED_DELTA;
            }
            if ((getKeyStates() & UP_PRESSED) != 0) {
                dy += SCROLL_SPEED_DELTA;
            }
            if ((getKeyStates() & DOWN_PRESSED) != 0) {
                dy -= SCROLL_SPEED_DELTA;
            }

            if (dx > MAX_SCROLL_SPEED) {
                dx = MAX_SCROLL_SPEED;
            }
            if (dx < -MAX_SCROLL_SPEED) {
                dx = -MAX_SCROLL_SPEED;
            }
            if (dy > MAX_SCROLL_SPEED) {
                dy = MAX_SCROLL_SPEED;
            }
            if (dy < -MAX_SCROLL_SPEED) {
                dy = -MAX_SCROLL_SPEED;
            }
        }

        if (followGps || followMarker) {
            computeFollowDeltas();
        }

        if (followMarker) {
            ScreenMarker routeEnd = (ScreenMarker) options.routeEnds.elementAt(options.routeEndIndex);
            if (routeEnd.screenCoordinate != null && routeEnd.screenCoordinate.x == getMapWidth() / 2
                    && routeEnd.screenCoordinate.y == getMapHeight() / 2) {

                routeEnd.tileCoordinate.x -= dx;
                routeEnd.tileCoordinate.y -= dy;
            }
        }

        if (Math.abs(dx) > 0.01 || Math.abs(dy) > 0.01) {
            x += dx;
            y += dy;
        } else {
            dx = 0.0;
            dy = 0.0;
        }

        // Counter force disallowing moving off the map.
        /*
         * int mapSize = tileSize << zoom; if ((latitude) * tileSize + x < 0) {
         * double cfx = ((-(latitude + 1)) * tileSize + x) / 4.0; if (cfx > 0.1)
         * { x -= cfx; System.out.println("cfx = " + cfx); } } else if
         * ((latitude) * tileSize + x > mapSize) { x += 5; }
         *
         * if ((longitude) * tileSize + y < 0) { double cfy = ((-(longitude +
         * 1)) * tileSize + y) / 4.0; if (cfy > 0.1) { y -= cfy;
         * System.out.println("cfy = " + cfy); } }
         */

        for (int i = 0; i < tiles.length; i++) {
            for (int j = 0; j < tiles[i].length; j++) {
                if (tiles[i][j][currentTilesIdx] != null && tiles[i][j][currentTilesIdx].state == Tile.STATE_EMPTY
                        && isVisible(i, j)) {
                    tiles[i][j][currentTilesIdx].load();
                }
            }
        }

        int horizontalShift = roundDown(x / tileSize);
        int verticalShift = roundDown(y / tileSize);

        if (horizontalShift != 0 || verticalShift != 0) {
            x -= horizontalShift * tileSize;
            y -= verticalShift * tileSize;

            latitude -= horizontalShift;
            longitude -= verticalShift;
            updateTiles(horizontalShift, verticalShift);
        }
    }

    private int roundDown(double x) {
        return x < 0 ? (int) (x - 1.0) : (int) x;
    }

    private void createTiles() {
        int verticalTilesCount = this.verticalTilesCount;
        int horizontalTilesCount = this.horizontalTilesCount;

        this.verticalTilesCount = 0;
        this.horizontalTilesCount = 0;

        if (tiles != null) {
            for (int i = 0; i < horizontalTilesCount; i++) {
                for (int j = 0; j < verticalTilesCount; j++) {
                    tiles[i][j][currentTilesIdx].deallocate();
                }
            }
            tiles = null;
        }
        horizontalTilesCount = getTilesCount(getMapWidth(), tileSize);
        verticalTilesCount = getTilesCount(getMapHeight(), tileSize);
        // System.gc();

        tiles = new Tile[horizontalTilesCount][verticalTilesCount][2];

        for (int i = 0; i < horizontalTilesCount; i++) {
            for (int j = 0; j < verticalTilesCount; j++) {
                tiles[i][j][0] = tiles[i][j][1] = tileFactory.createTile(zoom, latitude + i, longitude + j);

                // lazy load
                if (isVisible(i, j)) {
                    tiles[i][j][0].load();
                }
            }
        }
        this.verticalTilesCount = verticalTilesCount;
        this.horizontalTilesCount = horizontalTilesCount;
    }

    private boolean isVisible(int i, int j) {
        int tilePositionX = getTilePosition(x, i, tileSize);
        int tilePositionY = getTilePosition(y, j, tileSize);

        return !(tilePositionX >= getMapWidth() || tilePositionY >= getMapHeight() || tilePositionX <= -tileSize || tilePositionY <= -tileSize);
    }

    public int getMapHeight() {
        return getHeight() - 40;
    }

    public int getMapWidth() {
        return getWidth();
    }

    private int getTilesCount(int screenSize, int tileSize) {
        if (screenSize <= tileSize) {
            return 2;
        }
        return (int) ((double) (screenSize) / (double) tileSize) + 2;
    }

    public void paint(Graphics screen) {
        try {
            Graphics g;

            Tile[][][] tiles = this.tiles;
            if (!doubleBuffered) {
                g = buffer.getGraphics();
            } else {
                g = screen;
            }
            g.setFont(options.skin.mainFont);

            double x = this.x;
            double y = this.y;

            int panelHeight = options.skin.panelBackground.getHeight();

            int tmpIdx = currentTilesIdx;
            Tile[][] tiles2 = new Tile[tiles.length][tiles[0].length];

            for (int i = 0; i < horizontalTilesCount; i++) {
                for (int j = 0; j < verticalTilesCount; j++) {
                    tiles2[i][j] = tiles[i][j][tmpIdx];
                }
            }

            for (int i = 0; i < horizontalTilesCount; i++) {
                for (int j = 0; j < verticalTilesCount; j++) {
                    Tile tile = tiles2[i][j];

                    if (tile != null) {

                        g.translate(getTilePosition(x, i, tileSize) - g.getTranslateX(),
                                getTilePosition(y, j, tileSize) - g.getTranslateY() + panelHeight);
                        tile.paint(g, getMapWidth(), getMapHeight());

                        if (options.debugMode) {
                            g.setColor(20, 20, 250);
                            g.drawString(tile.getLatitude() + "," + tile.getLongitude(), 0, 0, Graphics.LEFT
                                    | Graphics.TOP);
                            g.drawRect(0, 0, tileSize, tileSize);
                        }
                    } else {
                        Tile.paintEmpty(g, tileSize, tileSize);
                    }
                }
            }
            g.translate(-g.getTranslateX(), -g.getTranslateY() + panelHeight);
            drawRoute(g, x, y);
            drawMarkers(g, x, y);
            drawNav(g);

            if (gpsState.state >= GpsState.CONNECTED_FIX) {
                String posImgPrefix = (options.navContext.directionIdx < 0) ? "position" : "snapped_pos";
                Image posImage = options.skin
                        .getImage(posImgPrefix + (System.currentTimeMillis() % 800) / 200 + ".png");
                g.drawImage(posImage, gpsCurrent.x, gpsCurrent.y, Graphics.HCENTER | Graphics.VCENTER);
            }

            drawScale(g);

            g.setColor(0, 0, 0);
            g.drawLine(getMapWidth() / 2, getMapHeight() / 2 - 15, getMapWidth() / 2, getMapHeight() / 2 + 15);
            g.drawLine(getMapWidth() / 2 - 15, getMapHeight() / 2, getMapWidth() / 2 + 15, getMapHeight() / 2);

            if (options.debugMode) {
                if (gpsState.state >= GpsState.CONNECTED_FIX) {
                    g.setColor(0, 0, 0);
                    ScreenCoordinate tmp = toScreenCoordinate(ProjectionTool.toTileCoordinate(gpsState.worldCoordinate,
                            zoom, tileSize), x, y);
                    g.drawRect(tmp.x, tmp.y, 1, 1);
                }
            }

            g.translate(-g.getTranslateX(), -g.getTranslateY());
            g.drawImage(options.skin.panelBackground, 0, 0, Graphics.LEFT | Graphics.TOP);

            drawStatusString(g, 1, 1, "Downloaded " + (options.downloaded / 1024) + " kB", Graphics.LEFT | Graphics.TOP);

            if (options.gpsEnabled || options.replayMode) {
                StringBuffer sb = new StringBuffer(96);
                int state = gpsState.state;
                if (state == GpsState.CONNECTING) {
                    sb.append("GPS: Connecting...");
                } else if (state == GpsState.CONNECTED_NO_FIX) {
                    sb.append("GPS: No fix");
                } else if (state == GpsState.CONNECTED_FIX) {
                    if (gpsState.activeSats != null) {
                        sb.append("GPS: " + gpsState.activeSats.size() + " active sats");
                    }
                } else {
                    sb.append("GPS: Connection error");
                }
                drawStatusString(g, 1, 15, sb.toString(), Graphics.LEFT | Graphics.TOP);

                int speedX = options.skin.mainFont.stringWidth("Downloaded 99999 kB") + 30;
                if (gpsState.state >= GpsState.CONNECTED_FIX && gpsState.groundSpeed >= 0) {
                    drawStatusString(g, speedX, 1, getRoundedNumber(gpsState.groundSpeed), Graphics.RIGHT
                            | Graphics.TOP);
                    drawStatusString(g, speedX, 15, "km/h", Graphics.RIGHT | Graphics.TOP);
                }

                int altX = speedX + options.skin.mainFont.stringWidth("AMSL") + 10;
                if (gpsState.state >= GpsState.CONNECTED_FIX) {
                    drawStatusString(g, altX, 1, gpsState.altitude + " m", Graphics.HCENTER | Graphics.TOP);
                    drawStatusString(g, altX, 15, "AMSL", Graphics.HCENTER | Graphics.TOP);

                }

                int satsInView = gpsState.satellitesInView;
                if (satsInView > 0) {
                    int barWidth = 12;
                    int barPanelX = getWidth() - (satsInView * barWidth);
                    if (barPanelX < altX + 30) {
                        satsInView = (getWidth() - (altX + 30)) / 12;
                        barPanelX = getWidth() - (satsInView * barWidth);
                    }
                    Vector satellites = gpsState.satellites;
                    Hashtable activeSats = gpsState.activeSats;

                    for (int i = 0; i < satsInView; i++) {
                        int barX = barPanelX + i * barWidth;
                        g.setColor(0, 0, 0);
                        g.drawRect(barX, 1, barWidth - 3, 25);
                        if (satellites != null && i < satellites.size()) {
                            Satellite sat = (Satellite) satellites.elementAt(i);

                            if (activeSats != null && activeSats.contains(sat.prn)) {
                                g.setColor(200, 200, 200);
                            } else {
                                g.setColor(40, 40, 40);
                            }
                            g.drawString(sat.prn, barX, 26, Graphics.LEFT | Graphics.TOP);

                            int barHeight = sat.snr / 2;
                            if (barHeight > 25) {
                                barHeight = 25;
                            }

                            g.fillRect(barX + 1, 26 - barHeight, barWidth - 4, barHeight);
                        }
                    }
                }

                if (options.replayMode) {
                    g.drawImage(options.skin.getImage("decrease_speed.png"), getMapWidth() / 2 - 80, 50, Graphics.TOP
                            | Graphics.LEFT);

                    g.drawImage(options.skin.getImage("increase_speed.png"), getMapWidth() / 2 + 32, 50, Graphics.TOP
                            | Graphics.LEFT);
                    g.setColor(40, 40, 40);
                    g.fillRect(getMapWidth() / 2 - 32, 50, 64, 48);
                    g.setColor(250, 250, 250);
                    g.drawString(options.replaySpeed + "x", getMapWidth() / 2, 55, Graphics.HCENTER | Graphics.TOP);
                    g.drawString(getRoundedNumber(gpsState.replyPosition * 100.0 / (double) gpsState.replySize) + "%",
                            getMapWidth() / 2, 75, Graphics.HCENTER | Graphics.TOP);
                }
            }

            if (hasPointerEvents()) {
                g.drawImage(options.skin.getImage("zoom_out.png"), 10, getHeight() - 58, Graphics.TOP | Graphics.LEFT);
                g.drawImage(options.skin.getImage("zoom_in.png"), getMapWidth() - 58, getHeight() - 58, Graphics.TOP
                        | Graphics.LEFT);
                if (gpsState.state >= GpsState.CONNECTED_FIX) {
                    g.drawImage(options.skin.getImage(followGps ? "my_pos_enabled.png" : "my_pos_disabled.png"),
                            getMapWidth() - 116, getHeight() - 58, Graphics.TOP | Graphics.LEFT);
                }
            }

            if (!doubleBuffered) {
                screen.drawImage(buffer, 0, 0, Graphics.LEFT | Graphics.TOP);
                flushGraphics();
            }
        } catch (Throwable e) {
            CloudGps.setError(e);
        }
    }

    private void drawStatusString(Graphics g, int x, int y, String string, int anchor) {
        g.setColor(50, 50, 50);
        g.drawString(string, x, y, anchor);
        g.setColor(250, 250, 250);
        g.drawString(string, x + 1, y + 1, anchor);
    }

    private void drawNav(Graphics g) {
        String img = null;
        if (options.routingStatus == Options.ROUTING_ERROR) {
            img = "nav_calc_error.png";
        } else if (options.routingStatus == Options.ROUTING_CALCULATING) {
            img = "nav_calc.png";
        } else if (gpsState.state >= GpsState.CONNECTED_FIX) {
            if (options.route != null) {
                if (options.navContext.directionIdx == -1) {
                    img = "nav_offroad.png";
                } else {
                    RouteDirection direction = (RouteDirection) options.route.routeDirections
                            .elementAt(options.navContext.directionIdx);
                    img = "turn" + (direction.turn == null ? "C" : direction.turn) + ".png";
                    g.drawString("[" + options.navContext.directionIdx + "] " + direction.text, 10, 70, Graphics.TOP
                            | Graphics.LEFT);

                    int distance = (int) ProjectionTool.getDistance((WorldCoordinate) options.route.waypoints
                            .elementAt(direction.offset), gpsState.worldCoordinate) / 10;
                    distance *= 10;
                    String str;
                    if (distance > 1000) {
                        str = getRoundedNumber(distance / 1000) + " km";
                    } else if (distance == 0) {
                        str = "<10 m";
                    } else {
                        str = distance + " m";
                    }
                    g.drawString(str, 10, 55, Graphics.TOP | Graphics.LEFT);
                }
            }
        }
        if (img != null) {
            g.drawImage(options.skin.getImage(img), 0, 0, Graphics.TOP | Graphics.LEFT);
        }
    }

    private void drawRoute(Graphics g, double x, double y) {
        OptimizedRoute optimizedRoute = options.navContext.optimizedRoute;

        if (optimizedRoute != null) {
            Vector segments = optimizedRoute.visibleSegments;
            Vector dirs = optimizedRoute.visibleSegmentDirections;

            if (segments != null && dirs != null) {
                ScreenCoordinate s1, s2;
                Integer prevdir = null;

                g.setColor(0, 0, 255);

                for (int i = 0; i < segments.size(); i += 2) {
                    s1 = toScreenCoordinate((TileCoordinate) segments.elementAt(i), x, y);
                    s2 = toScreenCoordinate((TileCoordinate) segments.elementAt(i + 1), x, y);

                    Integer dir = (Integer) dirs.elementAt(i / 2);
                    if (options.debugMode) {
                        if (prevdir != null && !dir.equals(prevdir)) {
                            g.setColor(255 - g.getRedComponent(), 0, 255 - g.getBlueComponent());
                        }
                        int dirx = (s1.x + s2.x) / 2, diry = (s1.y + s2.y) / 2;
                        if (Math.abs(s1.x - s2.x) > Math.abs(s1.y - s2.y)) {
                            diry += 6;
                        } else {
                            dirx += 6;
                        }
                        g.drawString("" + dir, dirx, diry, Graphics.TOP | Graphics.HCENTER);
                        prevdir = dir;
                    }
                    // g.drawLine(s1.x, s1.y, s2.x, s2.y);
                    boldLine(g, s1.x, s1.y, s2.x, s2.y);
                }
            }
        }
    }

    private void boldLine(Graphics g, int x1, int y1, int x2, int y2) {
        if (Math.abs(x1 - x2) > Math.abs(y1 - y2)) {
            g.drawLine(x1, y1 - 1, x2, y2 - 1);
            g.drawLine(x1, y1, x2, y2);
            g.drawLine(x1, y1 + 1, x2, y2 + 1);
        } else {
            g.drawLine(x1 - 1, y1, x2 - 1, y2);
            g.drawLine(x1, y1, x2, y2);
            g.drawLine(x1 + 1, y1, x2 + 1, y2);
        }
    }

    private void drawMarkers(Graphics g, double x, double y) {
        if (options.markers != null && !options.markers.isEmpty()) {
            Image shadow = options.skin.getImage("shadow.png");
            Image marker = options.skin.getImage("search_result.png");

            // Calculate visibility for markers and draw shadows first.
            for (int i = 0; i < options.markers.size(); i++) {
                ScreenMarker res = (ScreenMarker) options.markers.elementAt(i);
                if (res.visible) {
                    if (res.tileCoordinate == null || res.tileCoordinate.zoom != zoom) {
                        if (res.tileCoordinate != null) {
                            res.worldCoordinate = ProjectionTool.toWorldCoordinate(res.tileCoordinate, tileSize);
                        }

                        res.tileCoordinate = ProjectionTool.toTileCoordinate(res.worldCoordinate, zoom, tileSize);
                    }

                    res.screenCoordinate = toScreenCoordinate(res.tileCoordinate, x, y);

                    if (res.screenCoordinate.x > -marker.getWidth() / 2
                            && res.screenCoordinate.x < getMapWidth() + marker.getWidth() / 2
                            && res.screenCoordinate.y > 0
                            && res.screenCoordinate.y < getMapHeight() + marker.getHeight()) {
                        g.drawImage(shadow, res.screenCoordinate.x, res.screenCoordinate.y, Graphics.BOTTOM
                                | Graphics.LEFT);
                    } else {
                        // Marker is not visible.
                        res.screenCoordinate = null;
                    }
                }
            }

            // Update raise level and draw all markers.
            for (int i = 0; i < options.markers.size(); i++) {
                ScreenMarker res = (ScreenMarker) options.markers.elementAt(i);
                if (res.visible) {
                    if (res.raised && res.raiseLevel < 20) {
                        res.raiseLevel++;
                    } else if (!res.raised && res.raiseLevel > 0) {
                        res.raiseLevel--;
                    }

                    marker = options.skin.getImage(res.iconName);
                    if (res.screenCoordinate != null) {
                        g.drawImage(marker, res.screenCoordinate.x, res.screenCoordinate.y - res.raiseLevel,
                                Graphics.HCENTER | Graphics.BOTTOM);
                    }
                }
            }
        }
    }

    private void drawScale(Graphics g) {
        double gr = ProjectionTool.getGroundResolution(getCurrentCenter(), tileSize, zoom);
        double log10 = Float11.log10(gr);
        double norm = gr / Float11.pow(10, Math.floor(log10) + 1);
        double scaleWidth;
        String scaleText;
        if (norm < 0.15) {
            scaleWidth = 10;
        } else if (norm < 0.3) {
            scaleWidth = 20;
        } else if (norm < 0.7) {
            scaleWidth = 50;
        } else {
            scaleWidth = 100;
        }

        int scale = ((int) scaleWidth * (int) Float11.pow(10, Math.floor(log10) + 1));
        if (scale >= 1000) {
            scaleText = (scale / 1000) + " km";
        } else {
            scaleText = scale + " m";
        }
        scaleWidth = scaleWidth / norm;
        g.setColor(50, 50, 200);

        int x1 = getMapWidth() / 2 - (int) scaleWidth / 2;
        int y = getMapHeight() - 15;

        int x2 = x1 + (int) scaleWidth;

        g.drawLine(x1, y, x2, y);
        g.drawLine(x1, y, x1, y - 5);
        g.drawLine(x2, y, x2, y - 5);
        g.drawLine(getMapWidth() / 2, y, getMapWidth() / 2, y - 2);
        g.drawString(scaleText, x1 + (int) (scaleWidth / 2), y - 4, Graphics.HCENTER | Graphics.BASELINE);
    }

    private String getRoundedNumber(double number) {
        int i = (int) Math.floor(number);
        int frac = (int) ((number - i) * 100.0);
        return i + "." + (frac < 10 ? "0" : "") + frac;
    }

    private int getTilePosition(double screenTranslation, int tileNum, int tileSize) {
        return (int) screenTranslation + (tileNum - 1) * tileSize;
    }

    public ScreenCoordinate toScreenCoordinate(TileCoordinate c, double x, double y) {
        ScreenCoordinate result = new ScreenCoordinate();
        result.x = (int) ((c.latitude - latitude - 1) * tileSize + x + c.x);
        result.y = (int) ((c.longitude - longitude - 1) * tileSize + y + c.y);
        return result;
    }

    int lastX = -1, lastY = -1;
    private long pressedTime, markerPressedTime;

    protected void pointerPressed(int x, int y) {
        int width = getWidth();
        int height = getHeight();

        if (x >= 10 && x <= 58 && y >= height - 58 && y <= height - 10) {
            doZoomOut = true;
        } else if (x >= width - 58 && x <= width - 10 && y >= height - 58 && y <= height - 10) {
            doZoomIn = true;
        } else if (gpsState.state >= GpsState.CONNECTED_FIX && x >= width - 116 && x <= width - 68 && y >= height - 58
                && y <= height - 10) {
            doChangeFollowMode = true;
        } else if (Options.getInstance().replayMode && x >= getMapWidth() / 2 - 64 && x <= getMapWidth() / 2 - 16
                && y >= 50 && y <= 98) {
            decreaseReplaySpeed();
        } else if (Options.getInstance().replayMode && x >= getMapWidth() / 2 + 16 && x <= getMapWidth() / 2 + 64
                && y >= 50 && y <= 98) {
            increaseReplaySpeed();
        }

        else {
            followGps = false;
            lastX = x;
            lastY = y;
            pressedTime = System.currentTimeMillis();
        }
    }

    private void processMarkerDragging() {
        dragging = false;

        if (lastX >= 0 && draggedMarker != null && draggedMarker.raiseLevel >= 20) {
            draggedMarker.tileCoordinate.x += lastX - draggedMarker.screenCoordinate.x;
            draggedMarker.tileCoordinate.y += lastY - draggedMarker.screenCoordinate.y;
            draggedMarker.worldCoordinate = ProjectionTool.toWorldCoordinate(draggedMarker.tileCoordinate, tileSize);
            dragging = true;
            return;
        } else if (options.markers != null) {
            for (int i = 0; i < options.markers.size(); i++) {
                ScreenMarker marker = (ScreenMarker) options.markers.elementAt(i);

                if (marker.visible && marker.screenCoordinate != null
                        && Math.abs((lastX - marker.screenCoordinate.x)) < 15
                        && Math.abs((lastY - marker.screenCoordinate.y - 18)) < 20) {
                    dragging = true;
                    if (draggedMarker != marker) {
                        if (draggedMarker != null) {
                            changeDraggedIcon(draggedMarker, false);
                            draggedMarker.raised = false;
                        }
                        markerPressedTime = System.currentTimeMillis();
                        draggedMarker = marker;
                        changeDraggedIcon(draggedMarker, true);
                    } else {
                        if (System.currentTimeMillis() - markerPressedTime > 300) {
                            draggedMarker.raised = true;
                        }
                    }
                    break;
                }
            }
        }
        if (!dragging && draggedMarker != null) {
            changeDraggedIcon(draggedMarker, false);
            draggedMarker.raised = false;
            draggedMarker = null;
        }
    }

    private void changeDraggedIcon(ScreenMarker marker, boolean addRemove) {
        if (addRemove) {// add dragged suffix
            if (!marker.iconName.endsWith("_dragged.png")) {
                marker.iconName = marker.iconName.substring(0, marker.iconName.length() - 4) + "_dragged.png";
            }
        } else { // remove suffix
            if (marker.iconName.endsWith("_dragged.png")) {
                marker.iconName = marker.iconName.substring(0, marker.iconName.length() - 12) + ".png";
            }

        }
    }

    private void changeFollowGps() {
        followGps = !followGps;
        if (followGps) {
            followMarker = false;
            options.routeEndIndex = -1;
            // todo raisemilis
            computeFollowDeltas();
        }
    }

    private void computeFollowDeltas() {
        if (followGps && gpsState.state >= GpsState.CONNECTED_FIX) {
            double factor = 2 + Math.sqrt(options.replaySpeed) / 5.0;
            dx = (getMapWidth() / 2 - gpsCurrent.x) * factor / 25.0;
            dy = (getMapHeight() / 2 - gpsCurrent.y) * factor / 25.0;
        }
        if (followMarker) {
            ScreenMarker marker = (ScreenMarker) options.routeEnds.elementAt(options.routeEndIndex);
            ScreenCoordinate follow = marker.screenCoordinate;
            if (follow == null) {
                if (marker.tileCoordinate == null) {
                    marker.tileCoordinate = ProjectionTool.toTileCoordinate(marker.worldCoordinate, zoom, tileSize);
                }
                follow = toScreenCoordinate(marker.tileCoordinate, x, y);
            }

            if (follow.x != getMapWidth() / 2 || follow.y != getMapHeight() / 2) {
                dx = (getMapWidth() / 2 - follow.x) / 5.0;
                dy = (getMapHeight() / 2 - follow.y) / 5.0;

            }
        }
    }

    protected void pointerDragged(int x, int y) {
        if (lastX >= 0 && lastY >= 0) {
            if (!dragging) {
                dx += (x - lastX) / 4.0;
                dy += (y - lastY) / 4.0;
            }
            lastX = x;
            lastY = y;
            pressedTime = System.currentTimeMillis();
        }
    }

    protected void pointerReleased(int x, int y) {
        lastX = -1;
        lastY = -1;
        pressedTime = -1;
    }

    public void reload() {
        for (int i = 0; i < horizontalTilesCount; i++) {
            for (int j = 0; j < verticalTilesCount; j++) {
                if (isVisible(i, j)) {
                    tiles[i][j][currentTilesIdx].load();
                }
            }
        }
    }

    protected void sizeChanged(int w, int h) {
        doScreenSizeChanged = true;
    }

    public void setTileFactory(AbstractTileFactory tileFactory) {
        setRunning(false);
        this.tileFactory = tileFactory;
        this.tileSize = tileFactory.getTileSize();
        if (zoom > tileFactory.getMaxZoom()) {
            setZoomAndCenter(tileFactory.getMaxZoom(), getCurrentCenter());
        }

        IOTool.ensureDirExist(tileFactory.getDirectoryName());
        createTiles();
        setRunning(true);
    }

    public void setRunning(boolean running) {
        this.running = running;
    }

    public void zoomIn() {
        if (zoom < tileFactory.getMaxZoom()) {
            int newZoom = zoom + 1;
            setZoomAndCenter(newZoom, getCurrentCenter());
        }
    }

    public void zoomOut() {
        if (zoom > 0) {
            int newZoom = zoom - 1;
            setZoomAndCenter(newZoom, getCurrentCenter());
        }
    }

    public WorldCoordinate getCurrentCenter() {
        TileCoordinate center = new TileCoordinate();
        center.zoom = zoom;
        center.latitude = (int) latitude;
        center.longitude = (int) longitude;
        center.x = (tileSize - x) + getMapWidth() / 2;
        center.y = (tileSize - y) + getMapHeight() / 2;

        adjustShifts(center);

        return ProjectionTool.toWorldCoordinate(center, tileSize);
    }

    private void adjustShifts(TileCoordinate c) {
        int horizontalShift = roundDown(c.x / (double) tileSize);
        int verticalShift = roundDown(c.y / (double) tileSize);

        c.x -= horizontalShift * tileSize;
        c.y -= verticalShift * tileSize;

        c.latitude += horizontalShift;
        c.longitude += verticalShift;
    }

    public void setZoomAndCenter(int newZoom, WorldCoordinate center) {
        if (newZoom < 0 || newZoom > tileFactory.getMaxZoom()) {
            throw new RuntimeException("Illegal zoom value in setZoomAndCenter(): " + newZoom);
        }
        boolean oldFollowMode = followGps;
        zoom = newZoom;
        options.zoom = zoom;

        TileCoordinate tileCoordinate = ProjectionTool.toTileCoordinate(center, newZoom, tileSize);
        tileCoordinate.x -= getMapWidth() / 2;
        tileCoordinate.y -= getMapHeight() / 2;

        adjustShifts(tileCoordinate);

        latitude = tileCoordinate.latitude;
        longitude = tileCoordinate.longitude;
        x = tileSize - tileCoordinate.x;
        y = tileSize - tileCoordinate.y;
        dx = 0;
        dy = 0;

        createTiles();
        followGps = oldFollowMode;
    }

    private void decreaseReplaySpeed() {
        if (Options.getInstance().replaySpeed > 1) {
            Options.getInstance().replaySpeed /= 2;
        }
    }

    private void increaseReplaySpeed() {
        if (Options.getInstance().replaySpeed < 256) {
            Options.getInstance().replaySpeed *= 2;
        }
    }

    public void showSearchResults() {
        if (options.markers != null && !options.markers.isEmpty()) {
            ScreenMarker res = (ScreenMarker) options.markers.elementAt(0);
            double minx = res.worldCoordinate.longitude;
            double maxx = res.worldCoordinate.longitude;
            double miny = res.worldCoordinate.latitude;
            double maxy = res.worldCoordinate.latitude;

            if (options.markers.size() > 1) {
                for (int i = 1; i < options.markers.size(); i++) {
                    res = (ScreenMarker) options.markers.elementAt(i);
                    if (res.worldCoordinate.longitude < minx) {
                        minx = res.worldCoordinate.longitude;
                    }
                    if (res.worldCoordinate.longitude > maxx) {
                        maxx = res.worldCoordinate.longitude;
                    }
                    if (res.worldCoordinate.latitude < miny) {
                        miny = res.worldCoordinate.latitude;
                    }
                    if (res.worldCoordinate.latitude > maxy) {
                        maxy = res.worldCoordinate.latitude;
                    }
                }
            } else {
                setZoomAndCenter(zoom, res.worldCoordinate);
                return;
            }

            // Naive way to find optimal zoom showing all search results.
            int newZoom;

            WorldCoordinate upperLeft = new WorldCoordinate();
            upperLeft.latitude = miny;
            upperLeft.longitude = minx;

            WorldCoordinate lowerLeft = new WorldCoordinate();
            lowerLeft.latitude = maxy;
            lowerLeft.longitude = minx;

            WorldCoordinate lowerRight = new WorldCoordinate();
            lowerRight.latitude = maxy;
            lowerRight.longitude = maxx;

            for (newZoom = 1; newZoom < tileFactory.getMaxZoom(); newZoom++) {
                TileCoordinate upperLeftTile = ProjectionTool.toTileCoordinate(upperLeft, newZoom, tileSize);
                TileCoordinate lowerLeftTile = ProjectionTool.toTileCoordinate(lowerLeft, newZoom, tileSize);
                TileCoordinate lowerRightTile = ProjectionTool.toTileCoordinate(lowerRight, newZoom, tileSize);

                double scrMinx = lowerLeftTile.latitude * tileSize + lowerLeftTile.x;
                double scrMaxx = lowerRightTile.latitude * tileSize + lowerRightTile.x;

                double scrMiny = upperLeftTile.longitude * tileSize + upperLeftTile.y;
                double scrMaxy = lowerLeftTile.longitude * tileSize + lowerLeftTile.y;

                if (Math.abs(scrMinx - scrMaxx) > getMapWidth() * 0.9) {
                    break;
                }
                if (Math.abs(scrMiny - scrMaxy) > getMapHeight() * 0.9) {
                    break;
                }
            }

            WorldCoordinate center = new WorldCoordinate();
            center.longitude = (maxx + minx) / 2.0;
            center.latitude = (maxy + miny) / 2.0;
            setZoomAndCenter(newZoom - 1, center);
        }
    }
}
TOP

Related Classes of com.mapmidlet.tile.ui.TileCanvas

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.