J2ME Google 地圖 API

這一篇是我在J2ME Google Maps API的中文翻譯,網址在J2ME Google 地圖 API
為了備份,並轉貼在此:

這裡有一個簡單的函式庫來查詢Google地圖,它有下面的功能:

  • 地理編碼定址到其地理座標
  • 擷取給定尺寸、格式及畫面遠近的靜態圖片

這個API有一個實例,你可以在這裡檢查:Java ME Google Maps API sample MIDlet

取得你自己的Google地圖API Key

注意:用免費的Google地圖API Key的程式使用會違反Google的條款和條件 (10.8節),假如你想要使用Google地圖API在上面的範例中,你應該購買企業許可證。

要使用下面的程式碼,你應該取得你自己的Google地圖API Key,假如你沒有API key,你只能照著下面的操作:如何在手機應用程式中使用Google地圖資料

使用代理伺服器來存取Google地圖服務

注意:這個主題(代理的使用)可能不需要,我們仍然探討一下..
當你註冊取得Google地圖API key時,你可以輸入位址,這個位址使用那個key能夠存取地圖服務,因此,你應該設定代理伺服器在那個位址上,這樣就能從你的行動用戶端收到HTTP請求,轉發給Google地圖服務,收回Google的回饋反應。

在下面的程式碼裡你應該發送下面的請求:

原始碼:GoogleMaps 類別

import java.io.ByteArrayOutputStream; 
import java.io.DataOutputStream; 
import java.io.IOException; 
import java.io.InputStream; 
import java.util.Vector; 
import javax.microedition.io.Connector; 
import javax.microedition.io.HttpConnection; 
import javax.microedition.lcdui.Image; 

public class GoogleMaps { 
    private static final String URL_UNRESERVED =  
        "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +  
        "abcdefghijklmnopqrstuvwxyz" + 
        "0123456789-_.~"; 
    private static final char[] HEX = "0123456789ABCDEF".toCharArray(); 

    // these 2 properties will be used with map scrolling methods. You can remove them if not needed 
    public static final int offset = 268435456; 
    public static final double radius = offset / Math.PI; 

    private String apiKey = null; 

    public GoogleMaps(String key) { 
        apiKey = key; 
    } 

    public double[] geocodeAddress(String address) throws Exception { 
        byte[] res = loadHttpFile(getGeocodeUrl(address)); 
        String[] data = split(new String(res, 0, res.length), ','); 

        if (data[0].compareTo("200") != 0) { 
            int errorCode = Integer.parseInt(data[0]); 
            throw new Exception("Google Maps Exception: " + getGeocodeError(errorCode)); 
        } 

        return new double[] { 
                Double.parseDouble(data[2]), Double.parseDouble(data[3]) 
        }; 
    } 

    public Image retrieveStaticImage(int width, int height, double lat, double lng, int zoom, 
            String format) throws IOException { 
        byte[] imageData = loadHttpFile(getMapUrl(width, height, lng, lat, zoom, format)); 

        return Image.createImage(imageData, 0, imageData.length); 
    } 

    private static String getGeocodeError(int errorCode) { 
        switch (errorCode) { 
        case 400: 
            return "Bad request"; 
        case 500: 
            return "Server error"; 
        case 601: 
            return "Missing query"; 
        case 602: 
            return "Unknown address"; 
        case 603: 
            return "Unavailable address"; 
        case 604: 
            return "Unknown directions"; 
        case 610: 
            return "Bad API key"; 
        case 620: 
            return "Too many queries"; 
        default: 
            return "Generic error"; 
        } 
    } 

    private String getGeocodeUrl(String address) { 
        return "http://maps.google.com/maps/geo?q=" + urlEncode(address) + "&output=csv&key=" 
                + apiKey; 
    } 

    private String getMapUrl(int width, int height, double lng, double lat, int zoom, String format) { 
        return "http://maps.google.com/staticmap?center=" + lat + "," + lng + "&format=" 
                + format + "&zoom=" + zoom + "&size=" + width + "x" + height + "&key=" + apiKey; 
    } 

    private static String urlEncode(String str) { 
        StringBuffer buf = new StringBuffer(); 
        byte[] bytes = null; 
        try { 
            ByteArrayOutputStream bos = new ByteArrayOutputStream(); 
            DataOutputStream dos = new DataOutputStream(bos); 
            dos.writeUTF(str); 
            bytes = bos.toByteArray(); 
        } catch (IOException e) { 
            // ignore 
        } 
        for (int i = 2; i < bytes.length; i++) { 
            byte b = bytes[i]; 
            if (URL_UNRESERVED.indexOf(b) >= 0) { 
                buf.append((char) b); 
            } else { 
                buf.append('%').append(HEX[(b >> 4) & 0x0f]).append(HEX[b & 0x0f]); 
            } 
        } 
        return buf.toString(); 
    } 

    private static byte[] loadHttpFile(String url) throws IOException { 
        byte[] byteBuffer; 

        HttpConnection hc = (HttpConnection) Connector.open(url); 
        try { 
            hc.setRequestMethod(HttpConnection.GET); 
            InputStream is = hc.openInputStream(); 
            try { 
                int len = (int) hc.getLength(); 
                if (len > 0) { 
                    byteBuffer = new byte[len]; 
                    int done = 0; 
                    while (done < len) { 
                        done += is.read(byteBuffer, done, len - done); 
                    } 
                } else { 
                    ByteArrayOutputStream bos = new ByteArrayOutputStream(); 
                    byte[] buffer = new byte[512]; 
                    int count; 
                    while ( (count = is.read(buffer)) >= 0 ) { 
                        bos.write(buffer, 0, count); 
                    } 
                    byteBuffer = bos.toByteArray(); 
                } 
            } finally { 
                is.close(); 
            } 
        } finally { 
            hc.close(); 
        } 

        return byteBuffer; 
    } 

    private static String[] split(String s, int chr) { 
        Vector res = new Vector(); 

        int curr; 
        int prev = 0; 

        while ( (curr = s.indexOf(chr, prev)) >= 0 ) { 
            res.addElement(s.substring(prev, curr)); 
            prev = curr + 1; 
        } 
        res.addElement(s.substring(prev)); 

        String[] splitted = new String[res.size()]; 
        res.copyInto(splitted); 

        return splitted; 
    } 
}

地圖捲動的使用方法

假如你需要捲動你的地圖,你將需要對你的靜態圖片計算一個新的中心,下面的adjust()方法傳回新的地圖中心的經緯度,接受以下的參數:

  • 目前中心的經度緯度座標
  • 新地圖中心的deltaXdeltaY像素值
  • 新地圖畫面遠近程度

原來的程式碼是用JavaScript寫的,可以在:http://www.polyarc.us/adjust.js下載

注意:要使用下面的方法,你必須將MicroFloat函式庫可在這裡下載:MicroFloat網站含括到你的專暗李

public double[] adjust(double lat, double lng, int deltaX, int deltaY, int z)
{
	return new double[]{
		XToL(LToX(lng) + (deltaX<<(21-z))),
		YToL(LToY(lat) + (deltaY<<(21-z)))
	};
}
double LToX(double x)
{
	return round(offset + radius * x * Math.PI / 180);
}

double LToY(double y)
{
	return round(
		offset - radius * 
		Double.longBitsToDouble(MicroDouble.log(
			Double.doubleToLongBits(
			(1 + Math.sin(y * Math.PI / 180))
			/
			(1 - Math.sin(y * Math.PI / 180))
			)
		)) / 2);
}

double XToL(double x)
{
	return ((round(x) - offset) / radius) * 180 / Math.PI;
}

double YToL(double y)
{
	return (Math.PI / 2 - 2 * Double.longBitsToDouble(
				MicroDouble.atan(
					MicroDouble.exp(Double.doubleToLongBits((round(y)-offset)/radius))
				)
			)) * 180 / Math.PI;
}
double round(double num)
{
	double floor = Math.floor(num);

	if(num - floor >= 0.5)
		return Math.ceil(num);
	else
		return floor;
}

原始碼:範例使用

Image:J2me_google_maps.jpg
要使用這個類別,首先先用你的API金鑰:

GoogleMaps gMap = new GoogleMaps("API_KEY");

要地理編碼一個地址,你可以使用geocodeAddress()方法:

double[] lanLng = gMap.geocodeAddress("Leicester Square, London");

要擷取地圖圖像:

Image map = gMap.retrieveStaticImage(320, 240, 51.510605, -0.130728, 8, "png32");
Print Friendly, PDF & Email

2 則留言

  1. 我去過這個nokia的網站
    那你知道關於定位的寫法嗎
    事實上這部分我很不懂
    gps或基地台地位之類的事
    但是利用J2ME的location類別
    我在SE的手機(z770i)上run不起來
    我確定手機有支援JSR-179
    所以地圖部分現在也只能用預設的經緯度
    這樣太不方便了
    能請教一下你嗎

  2. Author

    我用NetBeans 6.7在我的Nokia 5800上用http://wiki.forum.nokia.com/index.php/Java_ME_Location_API
    這裡的範例可以run,但是我的手機衛星訊號很弱,無法定位,但是程式是可以run的,你是怎麼使用的,可以再詳細告知!

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

這個網站採用 Akismet 服務減少垃圾留言。進一步瞭解 Akismet 如何處理網站訪客的留言資料