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