ラボプロダクト

独自画像をAndroid Maps APIで表示する方法(GMap Image Cutter編)

2011/09/26



Mapion Android Maps APIで独自画像を表示する方法を紹介します。
デモアプリの「独自地図画像を指定する」の実現方法です。

※独自画像を実装するためにはmaps API 2.5以上が推奨です(それ以前のVerはスクロール遅いなど問題あり)

以下の画像(sa.png 1280x800)をタイル化してMaps APIで表示します。
sa.png

手順

1.タイルを作成する

画像を分割するのは手間なので、GMap Image Cutterというツールを使います

GMap Image Cutterを起動する
my01.png


sa.pngを開く
redokuji2_1.png


Createを押す(以下は縮尺3つ分)
redokuji2_2.png


sa-tilesとsa.htmlが出来上がる(必要なのはsa-tiles内のタイルです)
redokuji2_3.png


2.res/drawable-nodpiにタイルをコピーする

my05.png


3.Mapのサブクラスを作る

今回は縮尺3つなのでinitメソッドでratiosに3つ追加(縮尺が増えればさらに8,16,32...と縮尺分追加していく)
package jp.co.mapion.android.mymap;

import java.lang.reflect.Field;
import java.util.HashMap;

import jp.co.mapion.android.maps.GeoPoint;
import jp.co.mapion.android.maps.Map;
import jp.co.mapion.android.maps.Tile;
import android.graphics.Point;

public class MyMap extends Map {

	protected int tileWidth = 256;
	protected int tileHeight = 256;

	protected HashMap<Integer, Float> ratios = new HashMap<Integer, Float>();

	private HashMap<String, Integer> tileNameMap = new HashMap<String, Integer>();

	private String key;

	private int noimage;

	public MapionTownMap2(String key, int noimage) {
		this.key = key;
		this.noimage = noimage;
		init();
	}

	protected void init() {
		ratios.put(1, 4.0f);
		ratios.put(2, 2.0f);
		ratios.put(3, 1.0f);
	}

	@Override
	protected int getMaxZoomLevel() {
		return ratios.size();
	}

	@Override
	protected void setup() {
		setCenter(new GeoPoint(0, 0));
		setZoom(1);
	}

	@Override
	protected int getTileWidth() {
		return tileWidth;
	}

	@Override
	protected int getTileHeight() {
		return tileHeight;
	}

	@Override
	protected Point geoToPixel(GeoPoint geo) {
		int x = (int) (geo.getLongitudeE6() / getRatio());
		int y = (int) (geo.getLatitudeE6() / getRatio());
		return new Point(x, y);
	}

	@Override
	protected GeoPoint pixelToGeo(Point pixel) {
		int lat = (int) (pixel.y * getRatio());
		int lon = (int) (pixel.x * getRatio());
		return new GeoPoint(lat, lon);
	}

	@Override
	protected GeoPoint getOrigin() {
		double originRatio = ratios.get(1);
		int lat = (int) ((tileHeight / 2) * originRatio);
		int lon = (int) (-(tileWidth / 2) * originRatio);
		return new GeoPoint(lat, lon);
	}

	@Override
	protected String getURL(Tile tile) {
		int x = (int) tile.getX();
		int y = (int) tile.getY();
		if (x < 0 || y < 0) {
			return noMap();
		}
		if (isOut(tile)) {
			return noMap();
		}
		int gzoom = getZoom() - 1;
		double pow = Math.pow(2, gzoom);
		StringBuilder tileName = new StringBuilder();
		tileName.append("t");
		for (int i = 0; i < gzoom; i++) {
			pow = pow / 2;
			if (y < pow) {
				if (x < pow) {
					tileName.append("q");
				} else {
					tileName.append("r");
					x -= pow;
				}
			} else {
				if (x < pow) {
					tileName.append("t");
					y -= pow;
				} else {
					tileName.append("s");
					x -= pow;
					y -= pow;
				}
			}
		}
		return String.valueOf(getResourceInt(tileName.toString()));
	}

	@Override
	protected String getKey() {
		return key;
	}

	private double getRatio() {
		return ratios.get(getZoom());
	}

	private int getResourceInt(String name) {
		if (tileNameMap.containsKey(name)) {
			return tileNameMap.get(name);
		}
		try {
			Field field = R.drawable.class.getDeclaredField(name);
			int tileId = field.getInt(R.drawable.class);
			tileNameMap.put(name, tileId);
			return tileId;
		} catch (Exception e) {
		}
		return -1;
	}

	private String noMap() {
		return String.valueOf(noimage);
	}

	private boolean isOut(Tile tile) {
		int max = 1 << (getZoom() - 1);
		if (tile.getX() >= max || tile.getY() >= max) {
			return true;
		}
		return false;
	}
}


4. Activityを作る

package jp.co.mapion.android.mymap;

import jp.co.mapion.android.maps.Map;
import jp.co.mapion.android.maps.MapActivity;
import jp.co.mapion.android.maps.MapView;
import android.os.Bundle;
import android.view.Window;

public class MapionTownActivity extends MapActivity {
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		
		MapView mapView = new MapView(this, new MapionTownMap("APIキー", R.drawable.nashi));
		mapView.setClickable(true);
		mapView.setBuiltInZoomControls(true);
		setContentView(mapView);
	}
}

これで完成です。
APIの機能が全て使えるのでアイコンやテキストなどをOverlayしたり、回転させたりできます。
例ではローカルに画像を置いていますが、サーバに置くことも可能です。