在研究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);
}
}