GIS的学习(四十一)osmdroid基于微软bing整合实现地图

                  在研究Osmdroid和Google整合的过程中,发现使用mapforget有一些问题,于是采用扩展图层源的方式
  扩展osmdroid的图源支持 Bing地图的 具体的实现参考BingMapTileSource类:

在osmdroid的第三方lib中,有支持微软地图的图层源的代码具体实现如下:

package com.etrip.osmdroid.tilesource.bing.imagerymetadata;

import org.json.JSONArray;
import org.json.JSONObject;

/**
 * ImageryMetaData storage. Class used to decode valid ImageryMetaData.
 *
 */
public class ImageryMetaData {

	// Useful fields found in ImageryMetaData response
	private final static String STATUS_CODE="statusCode";
	private final static String AUTH_RESULT_CODE="authenticationResultCode";
	private final static String AUTH_RESULT_CODE_VALID="ValidCredentials";
	private final static String RESOURCE_SETS="resourceSets";
	private final static String ESTIMATED_TOTAL="estimatedTotal";
	private final static String RESOURCE = "resources";

	/**
	 * Parse a JSON string containing ImageryMetaData response
	 * @param a_jsonContent	the JSON content string
	 * @return	ImageryMetaDataResource object containing parsed information
	 * @throws Exception
	 */
	static public ImageryMetaDataResource getInstanceFromJSON(final String a_jsonContent) throws Exception
	{

		if(a_jsonContent==null) {
			throw new Exception("JSON to parse is null");
		}

		/// response code should be 200 and authorization should be valid (valid BingMap key)
		final JSONObject jsonResult = new JSONObject(a_jsonContent);
		final int statusCode = jsonResult.getInt(STATUS_CODE);
		if(statusCode!=200) {
			throw new Exception("Status code = "+statusCode);
		}

		if(AUTH_RESULT_CODE_VALID.compareToIgnoreCase(jsonResult.getString(AUTH_RESULT_CODE))!=0) {
			throw new Exception("authentication result code = "+jsonResult.getString(AUTH_RESULT_CODE));
		}

		// get first valid resource information
		final JSONArray resultsSet = jsonResult.getJSONArray(RESOURCE_SETS);
		if(resultsSet==null || resultsSet.length()<1) {
			throw new Exception("No results set found in json response");
		}

		if(resultsSet.getJSONObject(0).getInt(ESTIMATED_TOTAL)<=0) {
			throw new Exception("No resource found in json response");
		}

		final JSONObject resource = resultsSet.getJSONObject(0).getJSONArray(RESOURCE).getJSONObject(0);

		return ImageryMetaDataResource.getInstanceFromJSON(resource);
	}

}
package com.etrip.osmdroid.tilesource.bing.imagerymetadata;

import org.json.JSONArray;
import org.json.JSONObject;

/**
 * ImageryMetaData storage. Class used to parse and store useful ImageryMetaData fields.
 *
 */
public class ImageryMetaDataResource {

	// Useful fields
	private final static String IMAGE_WIDTH = "imageWidth";
	private final static String IMAGE_HEIGHT = "imageHeight";
	private final static String IMAGE_URL = "imageUrl";
	private final static String IMAGE_URL_SUBDOMAINS = "imageUrlSubdomains";
	private final static String ZOOM_MIN = "ZoomMin";
	private final static String ZOOM_MAX = "ZoomMax";

	/** image height in pixels (256 as default value) **/
	public int m_imageHeight=256;
	/** image width in pixels (256 as default value) **/
	public int m_imageWidth=256;
	/** image url pattern **/
	public String m_imageUrl;
	/** list of available sub domains. Can be null. **/
	public String[] m_imageUrlSubdomains;
	/** maximum zoom level (22 as default value for BingMap) **/
	public int m_zoomMax=22;
	/** minimum zoom level (1 as default value for BingMap) **/
	public int m_zoomMin=1;
	/** whether this imagery has been initialised */
	public boolean m_isInitialised = false;

	// counter used to manage next available sub domain
	private int m_subdomainsCounter = 0;

	/**
	 * Get an instance with default values.
	 * @return
	 */
	static public ImageryMetaDataResource getDefaultInstance() {
		return new ImageryMetaDataResource();
	}

	/**
	 * Parse a JSON string containing resource field of a ImageryMetaData response
	 * @param a_jsonObject	the JSON content string
	 * @return	ImageryMetaDataResource object containing parsed information
	 * @throws Exception
	 */
	static public ImageryMetaDataResource getInstanceFromJSON(final JSONObject a_jsonObject) throws Exception
	{
		final ImageryMetaDataResource result = new ImageryMetaDataResource();

		if(a_jsonObject==null) {
			throw new Exception("JSON to parse is null");
		}

		if(a_jsonObject.has(IMAGE_HEIGHT)) {
			result.m_imageHeight = a_jsonObject.getInt(IMAGE_HEIGHT);
		}
		if(a_jsonObject.has(IMAGE_WIDTH)) {
			result.m_imageWidth = a_jsonObject.getInt(IMAGE_WIDTH);
		}
		if(a_jsonObject.has(ZOOM_MIN)) {
			result.m_zoomMin = a_jsonObject.getInt(ZOOM_MIN);
		}
		if(a_jsonObject.has(ZOOM_MAX)) {
			result.m_zoomMax = a_jsonObject.getInt(ZOOM_MAX);
		}
		result.m_imageUrl = a_jsonObject.getString(IMAGE_URL);
		if(result.m_imageUrl!=null && result.m_imageUrl.matches(".*?\\{.*?\\}.*?")) {
			result.m_imageUrl = result.m_imageUrl.replaceAll("\\{.*?\\}", "%s");
		}

		final JSONArray subdomains = a_jsonObject.getJSONArray(IMAGE_URL_SUBDOMAINS);
		if(subdomains!=null && subdomains.length()>=1)
		{
			result.m_imageUrlSubdomains = new String[subdomains.length()];
			for(int i=0;i<subdomains.length();i++)
			{
				result.m_imageUrlSubdomains[i] = subdomains.getString(i);
			}

		}

		result.m_isInitialised = true;

		return result;
	}

	/**
	 * When several subdomains are available, get subdomain pointed by internal cycle counter on subdomains and increment this counter
	 * @return	the subdomain string associated to current counter value.
	 */
	public synchronized String getSubDomain()
	{
		if(m_imageUrlSubdomains==null || m_imageUrlSubdomains.length<=0) {
			return null;
		}

		final String result = m_imageUrlSubdomains[m_subdomainsCounter];
		if(m_subdomainsCounter<m_imageUrlSubdomains.length-1) {
			m_subdomainsCounter++;
		} else {
			m_subdomainsCounter=0;
		}

		return result;
	}
}
package com.etrip.osmdroid.tilesource.bing;

import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.Locale;

import microsoft.mappoint.TileSystem;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.DefaultHttpClient;
import org.osmdroid.ResourceProxy;
import org.osmdroid.tileprovider.MapTile;
import org.osmdroid.tileprovider.tilesource.IStyledTileSource;
import org.osmdroid.tileprovider.tilesource.QuadTreeTileSource;
import org.osmdroid.tileprovider.util.StreamUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;

import com.etrip.osmdroid.tilesource.bing.imagerymetadata.ImageryMetaData;
import com.etrip.osmdroid.tilesource.bing.imagerymetadata.ImageryMetaDataResource;

/**
 * BingMap tile source used with OSMDroid<br>
 *
 * This class builds the Bing REST services url to be requested to get a tile image.<br>
 *
 * Before to be used, the static method {@link retrieveBingKey} must be invoked.<br>
 *
 * See {@link http://msdn.microsoft.com/en-us/library/ff701721.aspx} for details on the Bing API.
 */
public class BingMapTileSource extends QuadTreeTileSource implements IStyledTileSource<String> {

	private static final Logger logger = LoggerFactory.getLogger(BingMapTileSource.class);

	/** the meta data key in the manifest */
	private static final String BING_KEY = "BING_KEY";

	//Constant used for imagerySet parameter
	/** Aerial imagery mode **/
	public static final String IMAGERYSET_AERIAL = "Aerial";
	/** Aerial imagery with road overlay mode **/
	public static final String IMAGERYSET_AERIALWITHLABELS = "AerialWithLabels";
	/** Roads imagery mode **/
	public static final String IMAGERYSET_ROAD = "Road";

	// Bing Map REST services return jpeg images
	private static final String FILENAME_ENDING =".jpeg";

	// URL used to get imageryData. It is requested in order to get tiles url patterns
	private static final String BASE_URL_PATTERN = "http://dev.virtualearth.net/REST/V1/Imagery/Metadata/%s?mapVersion=v1&output=json&key=%s";

	/** Bing Map key set by user.
	 * See {@link http://msdn.microsoft.com/en-us/library/ff428642.aspx}
	 */
	private static String mBingMapKey = "";

	private String mStyle = IMAGERYSET_ROAD;

	// object storing imagery meta data
	private ImageryMetaDataResource mImageryData = ImageryMetaDataResource.getDefaultInstance();

	// local used for set BingMap REST culture parameter
	private String mLocale;
	// baseURl used for OnlineTileSourceBase override
	private String mBaseUrl;
	// tile's image resolved url pattern
	private String mUrl;

	/**
	 * Constructor.<br> <b>Warning, the static method {@link retrieveBingKey} should have been invoked once before constructor invocation</b>
	 * @param aLocale	The language used with BingMap REST service to retrieve tiles.<br> If null, the system default locale is used.
	 */
	public BingMapTileSource(final String aLocale) {
		super("BingMap", ResourceProxy.string.bing, -1, -1, -1, FILENAME_ENDING, (String)null);
		mLocale = aLocale;
		if(mLocale==null) {
			mLocale=Locale.getDefault().getLanguage()+"-"+Locale.getDefault().getCountry();
		}
	}

	/**
	 * Read the API key from the manifest.<br>
	 * This method should be invoked before class instantiation.<br>
	 */
	public static void retrieveBingKey(final Context aContext) {

		// get the key from the manifest
		final PackageManager pm = aContext.getPackageManager();
		try {
			final ApplicationInfo info = pm.getApplicationInfo(aContext.getPackageName(),
					PackageManager.GET_META_DATA);
			if (info.metaData == null) {
				logger.info("Bing key not found in manifest");
			} else {
				final String key = info.metaData.getString(BING_KEY);
				if (key == null) {
					logger.info("Bing key not found in manifest");
				} else {
					if (DEBUGMODE) {
						logger.debug("Bing key: " + key);
					}
					mBingMapKey = key.trim();
				}
			}
		} catch (final NameNotFoundException e) {
			logger.info("Bing key not found in manifest", e);
		}
	}

	public static String getBingKey() {
		return mBingMapKey;
	}

	/*-------------- overrides OnlineTileSourceBase ---------------------*/

	@Override
	protected String getBaseUrl() {
		if (!mImageryData.m_isInitialised) {
			initMetaData();
		}
		return mBaseUrl;
	}

	@Override
	public String getTileURLString(final MapTile pTile)
	{
		if (!mImageryData.m_isInitialised) {
			initMetaData();
		}
		return String.format(mUrl,quadTree(pTile));
	}

	/**
	 * get minimum zoom level
	 * @return minimum zoom level supported by Bing Map for current map view mode
	 */
	@Override
	public int getMinimumZoomLevel() {
		return mImageryData.m_zoomMin;
	}
	/**
	 * get maximum zoom level
	 * @return maximum zoom level supported by Bing Map for current map view mode
	 */
	@Override
	public int getMaximumZoomLevel() {
		return mImageryData.m_zoomMax;
	}
	/**
	 * get tile size in pixel
	 * @return tile size in pixel supported by Bing Map for current map view mode
	 */
	@Override
	public int getTileSizePixels() {
		return mImageryData.m_imageHeight;
	}

	/**
	 * get the base path used for caching purpose
	 * @return a base path built on name given as constructor parameter and current style name
	 */
	@Override
	public String pathBase() {
		return mName + mStyle;
	}

	/*--------------- IStyledTileSource --------------------*/

	@Override
	/**
	 * Set the map style.
	 * @param aStyle The map style.<br>
	 * Should be one of {@link IMAGERYSET_AERIAL}, {@link IMAGERYSET_AERIALWITHLABELS} or {@link IMAGERYSET_ROAD}
	 */
	public void setStyle(final String pStyle) {
		if(!pStyle.equals(mStyle)) {
			// flag to re-read imagery data
			synchronized (mStyle) {
				mUrl = null;
				mBaseUrl = null;
				mImageryData.m_isInitialised = false;
			}
		}
		mStyle = pStyle;
	}

	@Override
	public String getStyle() {
		return mStyle;
	}

	private ImageryMetaDataResource initMetaData() {
		if (!mImageryData.m_isInitialised) {
			synchronized (this) {
				if (!mImageryData.m_isInitialised) {
					final ImageryMetaDataResource imageryData = getMetaData();
					if (imageryData != null) {
						mImageryData = imageryData;
						TileSystem.setTileSize(getTileSizePixels());
						updateBaseUrl();
					}
				}
			}
		}
		return mImageryData;
	}

	/**
	 * Gets the imagery meta from the REST service, or null if it fails
	 */
	private ImageryMetaDataResource getMetaData()
	{
		logger.trace("getMetaData");

		final HttpClient client = new DefaultHttpClient();
		final HttpUriRequest head = new HttpGet(String.format(BASE_URL_PATTERN, mStyle, mBingMapKey));
		logger.debug("make request "+head.getURI().toString());
		try {
		    final HttpResponse response = client.execute(head);

		    final HttpEntity entity = response.getEntity();

		    if (entity == null) {
				logger.error("Cannot get response for url "+head.getURI().toString());
				return null;
			}

		    final InputStream in = entity.getContent();
		    final ByteArrayOutputStream dataStream = new ByteArrayOutputStream();
		    final BufferedOutputStream out = new BufferedOutputStream(dataStream, StreamUtils.IO_BUFFER_SIZE);
			StreamUtils.copy(in, out);
			out.flush();

			return ImageryMetaData.getInstanceFromJSON(dataStream.toString());

		} catch(final Exception e) {
			logger.error("Error getting imagery meta data", e);
		} finally {
			client.getConnectionManager().shutdown();
			logger.trace("end getMetaData");
		}
		return null;
	}

	/**
	 * Resolves url patterns to update urls with current map view mode and available sub domain.<br>
	 * When several subdomains are available, change current sub domain in a cycle manner
	 */
	protected void updateBaseUrl()
	{
		logger.trace("updateBaseUrl");
		final String subDomain = mImageryData.getSubDomain();
		final int idx = mImageryData.m_imageUrl.lastIndexOf("/");
		if(idx>0) {
			mBaseUrl = mImageryData.m_imageUrl.substring(0,idx);
		} else {
			mBaseUrl = mImageryData.m_imageUrl;
		}

		mUrl = mImageryData.m_imageUrl;
		if(subDomain!=null)
		{
			mBaseUrl = String.format(mBaseUrl, subDomain);
			mUrl = String.format(mUrl, subDomain,"%s",mLocale);
		}
		logger.debug("updated url = "+mUrl);
		logger.trace("end updateBaseUrl");
	}

}

具体使用调用如下:

package com.etrip.osmdroid;

import org.osmdroid.tileprovider.tilesource.ITileSource;
import org.osmdroid.util.GeoPoint;
import org.osmdroid.views.MapController;
import org.osmdroid.views.MapView;

import android.app.Activity;
import android.os.Bundle;

import com.etrip.osmdroid.tilesource.bing.BingMapTileSource;

/**
 * 
 *       在研究Osmdroid和Google整合的过程中,发现使用mapforget有一些问题,于是采用扩展图层源的方式
 * 扩展osmdroid的图源支持
 * Bing地图的
 * 具体的实现参考BingMapTileSource类:
 * 
 * 
 * 
 * 
 * @author longgangbai
 */
public class MainActivity extends Activity {

	private MapController mapController;
	private MapView mapView;
	/** Called when the activity is first created. */
	@Override
	public void onCreate(final Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		mapView = (MapView) findViewById(R.id.map);
		mapView.setBuiltInZoomControls(true);
		mapController = mapView.getController();
		mapController.setZoom(15);
		GeoPoint geopoint=new GeoPoint(39.92605, 116.42616); 
		ITileSource tileSource=new BingMapTileSource(null);
		//mapView.setTileSource(new BingMapTileSource("Baidu Maps", null, 1, 20, 256, ".png", new String[]{"http://api.map.baidu.com/staticimage?"},39.92605,116.42616)); 
		mapController.setCenter(geopoint);	
	}
}

猜你喜欢

转载自topmanopensource.iteye.com/blog/1697225