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");

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的,你是怎么使用的,可以再详细告知!

Comments are closed.