麥卡托投影法程式範例

此程式為把地球從圓形轉換成圓柱再把圓柱攤開成平面,把位置的經緯度轉換成X、Y軸後顯示在畫面上,下圖為執行結果。

%E9%BA%A5%E5%8D%A1%E6%89%98%E6%8A%95%E5%BD%B1%E6%B3%95%E7%A8%8B%E5%BC%8F%E5%9C%96.PNG
圖一:執行結果

以下為麥卡托投影法程式碼:
專案下載:MercatorProjectionDemo.rar
主程式:MercatorProjectionDemo.java

package ccc.MercatorProjectionDemo;
 
import gmap.MercatorProjection;
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
 
import android.graphics.drawable.Drawable;
import android.os.Bundle;
 
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
 
import android.widget.TextView;
 
public class MercatorProjectionDemo extends Activity {
 
    // 畫布
    GmapView view;
    TextView show;
 
    // 中心點經緯度
    double centerLat = 0;
    double centerLng = 0;
    int zoom = 0;
 
    // 指標的位置
    float pointX=128;
    float pointY=128;
 
    // 地圖影像
    Drawable mapImg;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mapImg = getResources().getDrawable(R.drawable.map);
 
        view = new GmapView(this);
        view.setOnTouchListener(new View.OnTouchListener() {
 
            public boolean onTouch(View v, MotionEvent event) {
                float eX=event.getX();
                float eY=event.getY();
                if(eX<256&&eY<256){
                pointX=eX;
                pointY=eY;
                showXYZoom();
                view.invalidate();
                }
                return false;
 
            }
        });
        setContentView(view);
 
    }
 
    // 顯示text
    private void show(String text) {
        this.setTitle(text);
    }
 
    // 顯示經緯度與縮放大小
    private void showLngLatZoom() {
        int centerX = MercatorProjection.LngToX(centerLng, zoom);
        int centerY = MercatorProjection.LatToY(centerLat, zoom);
        int px = (int) (centerX + pointX - 128);
        int py = (int) (centerY + pointY - 128);
        double lng = MercatorProjection.XToLng(px, zoom);
        double lat = MercatorProjection.YToLat(py, zoom);
        show("lng=" + String.format("%.8f",lng)  + " lat=" + String.format("%.8f",lat)+" zoom="+zoom+"\n");
 
    }    
 
    // 顯示X,Y,縮放大小
    private void showXYZoom() {
        int centerX = MercatorProjection.LngToX(centerLng, zoom);
        int centerY = MercatorProjection.LatToY(centerLat, zoom);
        int px = (int) (centerX + pointX - 128);
        int py = (int) (centerY + pointY - 128);
        show("X=" + px  + " Y=" + py+" zoom="+zoom+"\n");
 
    }
 
    // 鍵盤事件
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        int centerX = MercatorProjection.LngToX(centerLng, zoom);
        int centerY = MercatorProjection.LatToY(centerLat, zoom);
        int edgeLength = 256 * (1 << zoom);
 
        if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) {
            centerX-=10;
            if(centerX<0)
                centerX = 0;
        }
        if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {
            centerX+=10;
            if(centerX>edgeLength)
                centerX = edgeLength;
        }
        if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
            centerY -= 10;
            if(centerY<0)
                centerY=0;
        }
        if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
            centerY += 10;
            if(centerY>edgeLength)
                centerY=edgeLength;
        }
        centerLng = MercatorProjection.XToLng(centerX, zoom);
        centerLat = MercatorProjection.YToLat(centerY, zoom);
        pointX=128;
        pointY=128;
        showLngLatZoom();
        view.invalidate();
        return super.onKeyDown(keyCode, event);
    }
 
    // 選單放大跟縮小索引
    protected static final int MENU_ZoomIn = Menu.FIRST;
    protected static final int MENU_ZoomOut = Menu.FIRST + 1;
 
    // 選單初始化
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        menu.add(0, MENU_ZoomIn, 0, "放大");
        menu.add(0, MENU_ZoomOut, 0, "縮小");
        return super.onCreateOptionsMenu(menu);
    };
 
    // 選單項目選擇
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
 
        switch (item.getItemId()) {
        case MENU_ZoomIn:
            if (zoom < 20)
                ++zoom;
            break;
        case MENU_ZoomOut:
            if (zoom > 0)
                --zoom;
            break;
        }
 
        pointX=128;
        pointY=128;
        showLngLatZoom();
        view.invalidate();
        return super.onOptionsItemSelected(item);
    }
 
    // 畫布
    class GmapView extends View {
 
        public GmapView(Context context) {
            super(context);
 
        }
 
        @Override
        protected void onDraw(Canvas canvas) {
            Paint p = new Paint();
            p.setARGB(255, 0, 0, 0);
            p.setStrokeWidth(1);
            int edgeLength = 256 * (1 << zoom);
            int centerX = MercatorProjection.LngToX(centerLng, zoom);
            int centerY = MercatorProjection.LatToY(centerLat, zoom);
            Rect showRect = new Rect(0, 0, edgeLength, edgeLength);
            int offsetX=-centerX+128;
            int offsetY=-centerY+128;
            showRect.offset(offsetX ,offsetY);
            mapImg.setBounds(showRect);
            mapImg.draw(canvas);
 
            canvas.drawLine(pointX-10, pointY,pointX+10, pointY, p);
            canvas.drawLine(pointX, pointY-10,pointX, pointY+10, p);
            super.onDraw(canvas);
        }
 
    }
}

程式:MercatorProjection.java

package gmap;
 
public class MercatorProjection {
       // 經度轉成點(point)
    public static int LngToX(double Longitude,int zoom)
    {
        int numberOfTiles = 1 << zoom << 8;
        return (int)((Longitude + 180.0) * numberOfTiles / 360.0);
    }
 
    // 緯度轉成點(point)
    public static int LatToY(double Latitude,int zoom)
    {
        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 zoom)
    {
        int numberOfTiles = 1 << zoom << 8;
        return (pointX * (360.0 / numberOfTiles)) - 180.0;
    }
 
    // 點(Y)轉成緯度
    public static double YToLat(int pointY,int zoom)
    {
        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;
    }
}

AndroidManifest.xml為使用權限:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="ccc.MercatorProjectionDemo"
      android:versionCode="1"
      android:versionName="1.0">
    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".MercatorProjectionDemo"
                  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" />
 
</manifest>