Google Map程式範例

此程式為動態下載Google Map的程式範例,能隨者使用者(GPS)移動所在位址上網下載Google Map的地圖再用麥卡托投影法轉換後顯示在螢幕畫面上。
gps.PNG
圖2-4
以下為動態的Google Map程式碼:
專案下載:DynamicLoadGoogleMap.rar
主程式: DynamicLoadGoogleMap.java

package DynamicLoadGoogleMap.a;
 
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.Window;
import android.view.WindowManager;
 
public class DynamicLoadGoogleMap extends Activity {
 
    // 主迴圈執行緒
    public Thread loopThread;
 
    // 畫布
    private Surface surface;
 
    // GPS初始化
    private LocationManager localManager;
    private LocationListener locationListener;
 
    // 暫存圖片
    private Bitmap tempMap;
 
    // 地圖圖片
    private Bitmap[] mapBitmap;
 
    // 地圖物件
    private Map[] mapObj;
 
    // 觸控偏移位置
    private int offsetX, offsetY;
 
    // 顯示GPS經緯度
    private Text lngText;
    private Text latText;
 
    // 上一次的觸控位置
    private int mouseDownX;
    private int mouseDownY;
 
    // 迴圈變數
    private int f;
 
    // 程式進入點
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
 
        // 無標題列
        requestWindowFeature(Window.FEATURE_NO_TITLE);
 
        // 全螢幕設定
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN);
 
        // 初始化GPS服務
        localManager = (LocationManager) getSystemService(Activity.LOCATION_SERVICE);
 
        // 初始化事件
        locationListener = new MyLocationListener();
 
        // 事件更新
        localManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0,
                locationListener);
 
        // 取得螢幕大小
        DisplayMetrics dm = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(dm);
        GV.scaleWidth = dm.widthPixels;
        GV.scaleHeight = dm.heightPixels;
        GV.halfWidth = GV.scaleWidth >> 1;
        GV.halfHeight = GV.scaleHeight >> 1;
 
        loopThread = new Thread(new Loop());
        loopThread.setDaemon(true);
 
        surface = new Surface(this);
        setContentView(surface);
    }
 
    // 暫停程式
    @Override
    protected void onPause() {
 
        // 清除GPS更新事件
        localManager.removeUpdates(locationListener);
 
        if (loopThread != null)
            loopThread.interrupt();
 
        super.onPause();
    }
 
    // 觸控事件
    @Override
    public boolean onTouchEvent(MotionEvent event) {
 
        int x = (int)event.getX();
        int y = (int)event.getY();
 
        switch(event.getAction())
        {
            case MotionEvent.ACTION_DOWN:
                mouseDownX = x;
                mouseDownY = y;
 
                break;
            case MotionEvent.ACTION_MOVE:
                int movX = x - mouseDownX;
                int movY = y - mouseDownY;
 
                if (Math.abs(movX - mouseDownX) > 5)
                {
                    offsetX = movX;
                }
 
                if (Math.abs(movY - mouseDownY) > 5)
                {
                    offsetY = movY;
                }
 
                break;
        }
 
        return super.onTouchEvent(event);
    }
 
    // 初始化
    private void Initialize()
    {
//        GV.setLng(118.32198666666666);
//        GV.setLat(24.449396666666669);
 
        lngText = new Text("", 20, 20, Color.YELLOW);
        latText = new Text("", 20, 40, Color.YELLOW);
 
        // 初始化地圖
        mapBitmap = new Bitmap[4];
 
        // 初始化地圖
        mapObj = new Map[mapBitmap.length];
 
        int x,y;
 
        // 設定地圖的起始位置
        for (f = 0;f <mapObj.length;f++)
        {
            x = -GV.halfWidth + GV.scaleWidth * (f % 2);
            y = -GV.halfHeight + GV.scaleHeight * (f / 2);
 
            mapObj[f] = new Map(x, y, GV.lngX + x, GV.latY + y);
            StartLoadMap(f, mapObj[f].lng, mapObj[f].lat);
        }
    }
 
    // 更新經緯度
    private void updateLngLat()
    {        
        // 如果有異動則
        if (offsetX != 0)
        {
            // 更新經緯度
            GV.addLngX(-offsetX);
 
            offsetX = 0;
        }
 
        // 如果有異動則
        if (offsetY != 0)
        {
            // 更新經緯度
            GV.addLatY(-offsetY);
 
            offsetY = 0;
        }
    }
 
    // 更新動作
    private void Update()
    {
        // 更新經緯度
        updateLngLat();
 
        // 取得經緯度的異動位移
        int x = GV.oldLngX - GV.lngX;
        int y = GV.oldLatY - GV.latY;
 
        // 如果經緯度更新
        if (x != 0 || y != 0)
        {
            if (GV.oldLngX != 0 && GV.oldLatY != 0)
            {
                // 移動地圖
                MoveMap(x, y);
            }else
                Initialize();
 
            // 儲存這次的經緯度絕對點
            GV.oldLngX = GV.lngX;
            GV.oldLatY = GV.latY;
        }
 
        lngText.message = "經度:" + GV.lng;
        latText.message = "緯度:" + GV.lat;
    }
 
    // 移動地圖
    private void MoveMap(int X,int y)
    {            
        for (f = 0; f < mapObj.length; f++)
        {
            mapObj[f].x += X;
 
            // 超出範圍重設位置
            if (mapObj[f].x < -GV.scaleWidth)
            {
                mapObj[f].x += (GV.scaleWidth << 1);
                mapObj[f].addLngX(GV.scaleWidth << 1);
 
                // 從網路載入地圖
                StartLoadMap(f, mapObj[f].lng, mapObj[f].lat);
            }
            else if (mapObj[f].x > GV.scaleWidth)
            {
                mapObj[f].x -= (GV.scaleWidth << 1);
                mapObj[f].addLngX(-(GV.scaleWidth << 1));
 
                // 從網路載入地圖
                StartLoadMap(f, mapObj[f].lng, mapObj[f].lat);
            }
        }
 
        for (f = 0; f < mapObj.length; f++)
        {
            mapObj[f].y += y;
 
            // 超出範圍重設位置
            if (mapObj[f].y < -GV.scaleHeight)
            {
                mapObj[f].y += (GV.scaleHeight << 1);
                mapObj[f].addLatY(GV.scaleHeight << 1);
 
                // 從網路載入地圖
                StartLoadMap(f, mapObj[f].lng, mapObj[f].lat);
            }
            else if (mapObj[f].y > GV.scaleHeight)
            {
                mapObj[f].y -= (GV.scaleHeight << 1);
                mapObj[f].addLatY(-(GV.scaleHeight << 1));
 
                // 從網路載入地圖
                StartLoadMap(f, mapObj[f].lng, mapObj[f].lat);
            }
        }
    }
 
    // 以執行緒載入地圖
    private void StartLoadMap(int index, double lng, double lat)
    {
        mapBitmap[index] = null;
 
        Thread loadMapThread = new Thread(new LoadMapFromNet(index, lng, lat));
        loadMapThread.setDaemon(true);
        loadMapThread.start();
    }
 
    // 從網路載入地圖
    class LoadMapFromNet implements Runnable {
 
        private int index;
        private double lng;
        private double lat;
 
        public LoadMapFromNet(int index,double lng,double lat) {
            this.index = index;
            this.lng = lng;
            this.lat = lat;
        }
 
        public void run() {
            tempMap = GetGoogleMap.getMap(lng, lat, GV.scaleWidth, GV.scaleHeight);
            if (tempMap != null)
                mapBitmap[index] = tempMap;
        }
    }
 
    // 更新畫面
    private void Draw()
    {
        // 鎖定canvas
        Canvas canvas = surface.mHolder.lockCanvas(null);
 
        if (canvas != null)
        {
            // 清除畫面
            canvas.drawColor(Color.BLACK);
 
            // 畫出地圖
            for (f = 0; f < mapBitmap.length; f++)
            {
                if (mapBitmap[f] != null)
                    canvas.drawBitmap(mapBitmap[f], mapObj[f].x, mapObj[f].y, null);
            }
 
            // 畫出lng
            canvas.drawText(lngText.message, lngText.x, lngText.y, lngText.paint);
 
            // 畫出lat
            canvas.drawText(latText.message, latText.x, latText.y, latText.paint);
 
            // 解除鎖定
            surface.mHolder.unlockCanvasAndPost(canvas);
        }
    }
 
    // 主迴圈
    public class Loop implements Runnable
    {
        public void run() {
 
            Initialize();
 
            while(!Thread.interrupted())
            {
 
                Update();
                Draw();
 
                try {
                    Thread.sleep(33);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
 
    // GPS監聽者
    class MyLocationListener implements LocationListener 
    {
        // GPS位置事件更新
        public void onLocationChanged(Location loc) {
            if (loc != null){
 
                // 取得經緯度
                GV.setLng(loc.getLongitude());
                GV.setLat(loc.getLatitude());
            }
        }
 
        public void onProviderDisabled(String provider) {
            // TODO Auto-generated method stub
        }
 
        public void onProviderEnabled(String provider) {
            // TODO Auto-generated method stub
        }
 
        public void onStatusChanged(String provider, int status, 
            Bundle extras) {
            // TODO Auto-generated method stub
        }
    }
}

程式: GetGoogleMap.java
22行的URL url = new URL(urlString);為直接連接網路時使用、24行的URL url = new URL("http","proxy0.internal",3128, urlString);為使用代理伺服器時使用。
package DynamicLoadGoogleMap.a;
 
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import java.net.URL;
import java.net.URLConnection;
 
public class GetGoogleMap
{    
    // 取得GoogleMap
    public static Bitmap getMap(double Longitude,double Latitude,int width,int height)
    {
        try
        {
            String key = "ABQIAAAAz2rjkrilGZTguphQT5RQqxQnA14KtgqpIfJzkpdB1-oq1QYeLRTl0Szkt5Yli5Jv9oBOrf85JNpgIw";
//            String key = "ABQIAAAAz2rjkrilGZTguphQT5RQqxQQIe5j7nozdTIq37ll2BjQmE8XsxTUxidRqBWWrDXy2cwQU686YRJCTQ";
            int zoom = 19;
            String size = String.format("%1sx%2s", width, height); 
 
            // 網址
            String urlString = String.format("http://maps.google.com/staticmap?center=%1$s,%2$s&zoom=%3$s&size=%4$s&maptype=hybrid&sensor=false&key=%5$s", Latitude, Longitude, zoom, size,key);
//            URL url = new URL(urlString);
            // use proxy server
            URL url = new URL("http","proxy0.internal",3128, urlString);
 
            // 連線
            URLConnection conn = url.openConnection();
            conn.connect();
 
            // 取得圖片串流
            Bitmap tempBitmap = BitmapFactory.decodeStream(conn.getInputStream());
 
            if (tempBitmap != null)
                return tempBitmap;
            else
                return null;
        }
        catch (Exception ex)
        {
            return null;
        }
    }
}

程式: GV.java
package DynamicLoadGoogleMap.a;
 
// 共用變數
public class GV {
 
    // 經度 
    public static double lng = 0.0;
 
    // 緯度
    public static double lat = 0.0;
 
    // 經度絕對點
    public static int lngX = 0;
 
    // 緯度絕對點
    public static int latY = 0;
 
    // 上一次的經緯度絕對點
    public static int oldLngX = 0;
    public static int oldLatY = 0;
 
    // 螢幕長寬
    public static int scaleWidth = 0;
    public static int scaleHeight = 0;
    public static int halfWidth = 0;
    public static int halfHeight = 0;
 
    // 地圖縮放係數
    public static int zoom = 19;
 
    public static void setLng(double x)
    {
        oldLngX = lngX;
 
        lng = x;
 
        lngX = LngToX(x);
    }
 
    public static void setLat(double y)
    {
        oldLatY = latY;
 
        lat = y;
 
        latY = LatToY(y); 
    }
 
    public static void addLngX(int x)
    {
        setLngX(lngX + x);
    }
 
    public static void addLatY(int y)
    {
        setLatY(latY + y);
    }
 
    public static void setLngX(int x)
    {
        oldLngX = lngX;
 
        lngX = x;
        lng = XToLng(lngX);
    }
 
    public static void setLatY(int y)
    {
        oldLatY = latY;
 
        latY = y;
        lat = YToLat(latY);
    }
 
    // 經度轉成點(point)
    public static int LngToX(double Longitude)
    {
        int numberOfTiles = 1 << zoom << 8;
        return (int)((Longitude + 180.0) * numberOfTiles / 360.0);
    }
 
    // 緯度轉成點(point)
    public static int LatToY(double Latitude)
    {
        int numberOfTiles = 1 << zoom << 8;
        double projection = Math.log(Math.tan(Math.PI / 4 + ((Latitude / 180 * Math.PI) / 2)));
        double y = (projection / Math.PI);
        y = 1.0 - y;
        y = y / 2.0 * numberOfTiles;
 
        return (int)y;
    }
 
    // 點(X)轉成經度
    public static double XToLng(int pointX)
    {
        int numberOfTiles = 1 << zoom << 8;
        return (pointX * (360.0 / numberOfTiles)) - 180.0;
    }
 
    // 點(Y)轉成緯度
    public static double YToLat(int pointY)
    {
        int numberOfTiles = 1 << zoom << 8;
        double Latitude = pointY * (2.0 / numberOfTiles);
        Latitude = 1 - Latitude;
        Latitude = Latitude * Math.PI;
        Latitude = Math.atan(Math.sinh(Latitude)) * 180.0 / Math.PI;
 
        return Latitude;
    }
}

程式: Map.java

package DynamicLoadGoogleMap.a;
 
public class Map {
 
    // 繪圖位置
    public int x;
    public int y;
 
    // 中心點經緯度
    public double lng;
    public double lat;
 
    // 中心點絕對點
    public int lngX;
    public int latY;
 
    public Map(int x, int y, int lngX, int latY) {
        this.x = x;
        this.y = y;
 
        setLngX(lngX);
        setLatY(latY);
    }
 
    public void addLngX(int x)
    {
        setLngX(lngX + x);
    }
 
    public void addLatY(int y)
    {
        setLatY(latY + y);
    }
 
    public void setLngX(int x)
    {
        lngX = x;
        lng = GV.XToLng(lngX);
    }
 
    public void setLatY(int y)
    {
        latY = y;
        lat = GV.YToLat(latY);
    }
}

程式: Surface.java

package DynamicLoadGoogleMap.a;
 
import android.view.SurfaceHolder;
import android.view.SurfaceView;
 
public class Surface extends SurfaceView implements SurfaceHolder.Callback{
    public SurfaceHolder mHolder;
    private DynamicLoadGoogleMap context;
 
    public Surface(DynamicLoadGoogleMap context) {
        super(context);
 
        this.context = context;
 
        mHolder = getHolder();
        mHolder.addCallback(this);
    }
 
    public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
        // TODO Auto-generated method stub
 
    }
    public void surfaceCreated(SurfaceHolder arg0) {
        context.loopThread.start();
    }
 
    public void surfaceDestroyed(SurfaceHolder arg0) {
        // TODO Auto-generated method stub
 
    }
}

程式: Text.java

package DynamicLoadGoogleMap.a;
 
import android.graphics.Paint;
import android.graphics.Typeface;
 
public class Text {
    public int x;
    public int y;
    public Paint paint;
    public String message;
 
    public Text(String message, int x, int y, int color) {
        this.message = message;
        this.x = x;
        this.y = y;
        paint = new Paint();
        paint.setTextSize(12);
        paint.setColor(color);
        paint.setTypeface(Typeface.MONOSPACE);
    }
}

AndroidManifest.xml為使用權限:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="DynamicLoadGoogleMap.a"
      android:versionCode="1"
      android:versionName="1.0">
    <application android:icon="@drawable/icon" android:label="@string/app_name" android:debuggable="true">
        <activity android:name=".DynamicLoadGoogleMap"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
 
    </application>
    <uses-sdk android:minSdkVersion="4" />
 
    <!-- GPS權限設定 -->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
 
    <!-- 網路權限 -->
    <uses-permission android:name="android.permission.INTERNET" />
</manifest>

程式影片解說