1. 程式人生 > >OpenStreetMap(二):如何使用osmdroid庫

OpenStreetMap(二):如何使用osmdroid庫

"Hello osmdroid World"

osmdroid's MapView is basically a replacement for Google's MapView class. First of all, create your Android project, and follow HowToMaven if you're using Maven, or follow HowToGradle if you're using Gradle/Android Studio. This will help you get the binaries for osmdroid included in your project.

Manifest

In most cases, you will have to set the following authorizations in your AndroidManifest.xml:

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> 
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name
="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

Android 6.0+ devices require you have to check for "dangerous" permissions at runtime.


osmdroid requires the following dangerous permissions:
WRITE_EXTERNAL_STORAGE and ACCESS_COARSE_LOCATION/ACCESS_FINE_LOCATION.
See OpenStreetMapViewer's implementation or Google Documentation on Permissions

Layout

Create a "src/main/res/layouts/main.xml" layout like this one. With Android Studio, it probably created one already called. The default is "src/main/res/layouts/activity_main.xml":

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:orientation="vertical" 
        android:layout_width="fill_parent"
        android:layout_height="fill_parent">
        <org.osmdroid.views.MapView android:id="@+id/map"
                android:layout_width="fill_parent" 
                android:layout_height="fill_parent" />
</LinearLayout>

Main Activity

We now create the main activity (MainActivity.java):

import org.osmdroid.tileprovider.tilesource.TileSourceFactory;
import org.osmdroid.views.MapView;

public class MainActivity extends Activity {
    @Override public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //important! set your user agent to prevent getting banned from the osm servers  
    org.osmdroid.tileprovider.constants.OpenStreetMapTileProviderConstants.setUserAgentValue(BuildConfig.APPLICATION_ID);

        MapView map = (MapView) findViewById(R.id.map);
        map.setTileSource(TileSourceFactory.MAPNIK);
    }
}

And that's enough to give it a try, and see the world map.

Then we add default zoom buttons, and ability to zoom with 2 fingers (multi-touch)

        map.setBuiltInZoomControls(true);
        map.setMultiTouchControls(true);

We can move the map on a default view point. For this, we need access to the map controller:

        IMapController mapController = map.getController();
        mapController.setZoom(9);
        GeoPoint startPoint = new GeoPoint(48.8583, 2.2944);
        mapController.setCenter(startPoint);

Advanced tutorial

The best example of how to use the osmdroid library is our OpenStreetMapViewer sample project. It contains a basic osmdroid application plus a few special-use examples. It is recommended you use this project as an example for building your application.

Adding a MapView

You can add a MapView to your xml layout using:

<org.osmdroid.views.MapView
    android:id="@+id/mapview"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tilesource="Mapnik" />

This will allow you to configure the tile source imagery for your MapView but not much else.

However, for more control over your MapView, you will want to create a MapViewprogrammatically.

@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    mMapView = new MapView(inflater.getContext(), 256, getContext());
    return mMapView;
}

Images for Buttons and whatnot

For osmdroid 4.3 and older, there's a number of resources that the map uses for various user interface helpers, such as zoom in/out buttons, the device's current location when GPS is available and more. These resources are loaded via the "ResourceProxy". The idea is that you can either bring your own images or borrow the ones from osmdroid. If you're borrowing, then you'll want to grab the files located here and add them to your project "src/main/res/drawable".

For osmdroid 5.0 and 5.1, the drawables are included with the AAR package. The resource proxy is still present and used so you can override values and images as needed.

For osmdroid 5.2 and up, the resource proxy is removed from the API set and replaced with Android context.

Create a custom Resource Proxy

Applies only to versions prior to 5.2

As mentioned above, the Resource Proxy is a bit of a strange animal that osmdroid uses to load some images for user interface controls. If you're using any of the built-in controls that need images (zoom in/out, person icon, etc) you'll either need to provide your own images, borrow the images from osmdroid's example app, or provide your own implementation of Resource Proxy.

The example osmdroid app includes an example of this called CustomResourceProxy (included with > 4.3 osmdroid). All it does is change the my location drawable (person) to an alternate image. The example is below.

public class CustomResourceProxy extends DefaultResourceProxyImpl {

     private final Context mContext;
     public CustomResourceProxy(Context pContext) {
          super(pContext);
        mContext = pContext;
     }

     @Override
    public Bitmap getBitmap(final bitmap pResId) {
        switch (pResId){
               case person:
                    //your image goes here!!!
                    return BitmapFactory.decodeResource(mContext.getResources(),org.osmdroid.example.R.drawable.sfgpuci);
          }
          return super.getBitmap(pResId);
    }

    @Override
    public Drawable getDrawable(final bitmap pResId) {
        switch (pResId){
               case person:
                    return mContext.getResources().getDrawable(org.osmdroid.example.R.drawable.sfgpuci);
          }
          return super.getDrawable(pResId);
    }
}

Then you can use your instance using the following snippet.

mResourceProxy = new CustomResourceProxy(getApplicationContext());
final RelativeLayout rl = new RelativeLayout(this);
this.mOsmv = new MapView(this,mResourceProxy);

In order to see any difference with our example (changes the person icon), we'll need to get a location fix and add it to the map layers.

this.mLocationOverlay = new MyLocationNewOverlay(new GpsMyLocationProvider(this), mOsmv, mResourceProxy);
this.mLocationOverlay.enableMyLocation();
this.mOsmv.getOverlays().add(mLocationOverlay);
this.mOsmv.setMultiTouchControls(true);

Map Overlays

How to add the My Location overlay

this.mLocationOverlay = new MyLocationNewOverlay(new GpsMyLocationProvider(context),mMapView);
this.mLocationOverlay.enableMyLocation();
mMapView.getOverlays().add(this.mLocationOverlay);

How to add a compass overlay

this.mCompassOverlay = new CompassOverlay(context, new InternalCompassOrientationProvider(context), mMapView);
this.mCompassOverlay.enableCompass();
mMapView.getOverlays().add(this.mCompassOverlay);

How to enable rotation gestures

mRotationGestureOverlay = new RotationGestureOverlay(context, mMapView);
mRotationGestureOverlay.setEnabled(true);
mMapView.setMultiTouchControls(true);
mMapView.getOverlays().add(this.mRotationGestureOverlay);

How to add Map Scale bar overlay

mScaleBarOverlay = new ScaleBarOverlay(context);
mScaleBarOverlay.setCentred(true);
//play around with these values to get the location on screen in the right place for your applicatio
mScaleBarOverlay.setScaleBarOffset(dm.widthPixels / 2, 10);
mMapView.getOverlays().add(this.mScaleBarOverlay);

How to add the built-in Minimap

Note: do not use when rotation is enabled! (Keep reading for a work around)

mMinimapOverlay = new MinimapOverlay(context, mMapView.getTileRequestCompleteHandler());
mMinimapOverlay.setWidth(dm.widthPixels / 5);
mMinimapOverlay.setHeight(dm.heightPixels / 5);
//optionally, you can set the minimap to a different tile source
//mMinimapOverlay.setTileSource(....);
mMapView.getOverlays().add(this.mMinimapOverlay);

Pro tip: If you want the minimap to stay put when rotation is enabled, create a second map view in your layout file, then wire up a change listener on the main map and use that to set the location on the minimap. For the reverse, you need to do the same process, however you have to filter map motion events to prevent infinite looping. There's an example on how to sync the views within the example application.

How do I place icons on the map with a click listener?

//your items
ArrayList<OverlayItem> items = new ArrayList<OverlayItem>();
items.add(new OverlayItem("Title", "Description", new GeoPoint(0.0d,0.0d))); // Lat/Lon decimal degrees

//the overlay
ItemizedOverlayWithFocus<OverlayItem> mOverlay = new ItemizedOverlayWithFocus<OverlayItem>(items,
    new ItemizedIconOverlay.OnItemGestureListener<OverlayItem>() {
    @Override
    public boolean onItemSingleTapUp(final int index, final OverlayItem item) {
    //do something
        return true;
    }
    @Override
    public boolean onItemLongPress(final int index, final OverlayItem item) {
        return false;
    }
});
mOverlay.setFocusItemsOnTap(true);

mMapView.getOverlays().add(mOverlay);

How many icons can I put on the map?

The answer is greatly dependent on what hardware the osmdroid based app is ran on. A Samsung S5 (no endorsement intended) ran just fine at 3k icons and was noticeably choppy at 6k icons. Your mileage may vary. X86 Android running on modern hardware will perform great at even higher numbers. However it's recommended to limit the amount of stuff you're rendering, if at all possible.

If you're also drawing paths, lines, polygons, etc, then this also changes the equation. Drawing multipoint graphics is computationally more expensive and thus negatively affects performance under higher loads. To mitigate performance issues with multipoint graphics, one strategy would be to reduce the amount of points handed off to the map engine when at a higher zoom level (numerically lower), then increase the fidelity as the user zoom's in. In effect, you would be clipping the visible data at the map view bounds so that the map view only "knows" about what's in screen and doesn't have to loop through all 10k icons that you want on the map. Although you can give the map view all 10k objects, but every time the map moves or zooms, it will iterate over all 10k items to calculate where to draw them (if at all). Using this mechanism paired with map motion listeners and a database query that supports geographic bounds, you can support a rich experience for users with lots of data and still have reasonable performance.

Reusing drawables for icons will help with memory usage too.

Map Sources, Imagery and Tile sets.

試譯文(部分連結檢視原文) 你好osmdroid世界(基礎教程) osmdroid的MapView基本上是一個替代谷歌的MapView類。首先,建立你的Android專案,並遵循HowToMaven如果你使用Maven,或遵循HowToGradle如果你使用Gradle / AndroidStudio。這將幫助你得到osmdroid的二進位制檔案包含在您的專案。 1.Manifest 在大多數情況下,您將需要在androidmanifest.xml獲取許可權:
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

Android 6.0 +裝置要求你必須在執行時檢查“危險”許可權。 osmdroid需要以下危險的許可權:
WRITE_EXTERNAL_STORAGE and ACCESS_COARSE_LOCATION/ACCESS_FINE_LOCATION.

看到OpenStreetMapViewer實現或谷歌文件的許可權 2.Layout 建立一個“src/main/res/layouts/main.xml”佈局。在Android Studio中,它可能已經建立好了。預設值是“src/main/res/layouts/activity_main.xml”:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:orientation="vertical"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent">
        <org.osmdroid.views.MapView android:id="@+id/map"
                android:layout_width="fill_parent"
                android:layout_height="fill_parent" />
</LinearLayout>

3.Main Activity 我們現在建立的主要活動(MainActivity.java):
import org.osmdroid.tileprovider.tilesource.TileSourceFactory;
import org.osmdroid.views.MapView;

public class MainActivity extends Activity {
    @Override public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

     //重要!設定你的使用者代理,以防止被osm伺服器禁止(在這裡設定沒有成功)    
    org.osmdroid.tileprovider.constants.OpenStreetMapTileProviderConstants.setUserAgentValue(BuildConfig.APPLICATION_ID);

        MapView map = (MapView) findViewById(R.id.map);
        map.setTileSource(TileSourceFactory.MAPNIK);
    }
}

足以試一試,看看世界地圖。 然後我們新增預設縮放按鈕,與2手指放大的能力(多點觸控)
map.setBuiltInZoomControls(true);
map.setMultiTouchControls(true);

我們可以移動地圖預設的焦點。為此,我們需要訪問地圖控制器:
IMapController mapController = map.getController();
mapController.setZoom(9);
GeoPoint startPoint = new GeoPoint(48.8583, 2.2944);
mapController.setCenter(startPoint);

高階教程 最好的例子 - 如何使用osmdroid圖書館,是我們OpenStreetMapViewer示例專案。 它包含一個基本的osmdroid應用程式加上一些特殊用途的例子。建議你使用這個專案作為一個例子來構建您的應用程式。 Adding a MapView 你可以新增一個MapView xml佈局使用:
<org.osmdroid.views.MapView
    android:id="@+id/mapview"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tilesource="Mapnik" />

這將允許您配置的瓷磚源影象MapView別無他法。 然而,對於更多的控制你的MapView,您將希望以程式設計方式建立一個MapView。
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    mMapView = new MapView(inflater.getContext(), 256, getContext());
    return mMapView;
}

Images for Buttons and whatnot(不可名狀的東西) osmdroid 4.3及以上,有很多資源地圖可以使用各種使用者介面幫手,比如放大/縮小按鈕,裝置的當前位置,當GPS是可用的和更多。這些資源是通過“ResourceProxy”載入。我們的想法是,你可以把你自己的圖片或從osmdroid借的。如果是你借的,那麼你會想在這裡抓住檔案,並將它們新增到您的專案“src/main/res/drawable”。 osmdroid 5.0和5.1的畫板是包含在AAR包。使用的資源代理仍然存在,所以你可以根據需要覆蓋值和影象。 osmdroid 5.2,資源代理從API集刪除,取而代之的是Android Context。 Create a custom Resource Proxy (自定義資源代理) 只適用於5.2版本之前 正如上面提到的,資源代理是有點奇怪的動物,osmdroid使用載入使用者介面控制元件的一些圖片。如果您正在使用任何內建的控制需要影象(放大/縮小,人圖示等)你要麼需要提供自己的影象,從osmdroid借影象的示例應用程式,或提供自己的資源代理的實現。 osmdroid的示例應用程式包含一個名為CustomResourceProxy的例子(> 4.3 osmdroid附帶)。它所做的是改變我的位置使其可拉的(人),另一種形象。下面的例子是
public class CustomResourceProxy extends DefaultResourceProxyImpl {

    private final Context mContext;
    public CustomResourceProxy(Context pContext) {
          super(pContext);
        mContext = pContext;
    }

    @Override
    public Bitmap getBitmap(final bitmap pResId) {
        switch (pResId){
              case person:
                    //your image goes here!!!
                    return BitmapFactory.decodeResource(mContext.getResources(),org.osmdroid.example.R.drawable.sfgpuci);
          }
          return super.getBitmap(pResId);
    }

    @Override
    public Drawable getDrawable(final bitmap pResId) {
        switch (pResId){
              case person:
                    return mContext.getResources().getDrawable(org.osmdroid.example.R.drawable.sfgpuci);
          }
          return super.getDrawable(pResId);
    }
}

然後您可以結合您的例項使用以下程式碼片段。
mResourceProxy = new CustomResourceProxy(getApplicationContext());
final RelativeLayout rl = new RelativeLayout(this);
this.mOsmv = new MapView(this,mResourceProxy);

為了看到任何與我們的示例(更改圖示)不同的人,我們需要得到一個位置修正,並將其新增到地圖圖層。
this.mLocationOverlay = new MyLocationNewOverlay(new GpsMyLocationProvider(this), mOsmv, mResourceProxy);
this.mLocationOverlay.enableMyLocation();
this.mOsmv.getOverlays().add(mLocationOverlay);
this.mOsmv.setMultiTouchControls(true);

Map Overlays(地圖覆蓋物) How to add the My Location overlay
this.mLocationOverlay = new MyLocationNewOverlay(new GpsMyLocationProvider(context),mMapView);
this.mLocationOverlay.enableMyLocation();
mMapView.getOverlays().add(this.mLocationOverlay);

How to add a compass(指南針) overlay
this.mCompassOverlay = new CompassOverlay(context, new InternalCompassOrientationProvider(context), mMapView);
this.mCompassOverlay.enableCompass();
mMapView.getOverlays().add(this.mCompassOverlay);

How to enable rotation gestures(如何啟用旋轉的手勢)
mRotationGestureOverlay = new RotationGestureOverlay(context, mMapView);
mRotationGestureOverlay.setEnabled(true);
mMapView.setMultiTouchControls(true);
mMapView.getOverlays().add(this.mRotationGestureOverlay);

How to add Map Scale bar overlay(如何新增地圖比例尺疊加)
mScaleBarOverlay = new ScaleBarOverlay(context);
mScaleBarOverlay.setCentred(true);
//play around with these values to get the location on screen in the right place for your applicatio
mScaleBarOverlay.setScaleBarOffset(dm.widthPixels / 2, 10);
mMapView.getOverlays().add(this.mScaleBarOverlay);


How to add the built-in Minimap(如何新增內建的迷你地圖) 注意:不要在啟用旋轉時使用!(繼續閱讀解決)
mMinimapOverlay = new MinimapOverlay(context, mMapView.getTileRequestCompleteHandler());
mMinimapOverlay.setWidth(dm.widthPixels / 5);
mMinimapOverlay.setHeight(dm.heightPixels / 5);
//optionally, you can set the minimap to a different tile source
//mMinimapOverlay.setTileSource(....);
mMapView.getOverlays().add(this.mMinimapOverlay);

專家提示:如果你想啟用時,小地圖上留在原地旋轉,建立第二個地圖檢視佈局檔案,然後更改偵聽器連線在主圖和用它來設定小地圖上的位置。相反的,你需要做相同的過程,然而你必須過濾運動事件對映到防止無限迴圈。有一個例子如何同步示例應用程式中的檢視。 How do I place icons on the map with a click listener?(我怎麼把圖示在地圖上點選監聽器)
//your items
ArrayList<OverlayItem> items = new ArrayList<OverlayItem>();
items.add(new OverlayItem("Title", "Description", new GeoPoint(0.0d,0.0d))); // Lat/Lon decimal degrees

//the overlay
ItemizedOverlayWithFocus<OverlayItem> mOverlay = new ItemizedOverlayWithFocus<OverlayItem>(items,
    new ItemizedIconOverlay.OnItemGestureListener<OverlayItem>() {
    @Override
    public boolean onItemSingleTapUp(final int index, final OverlayItem item) {
    //do something
        return true;
    }
    @Override
    public boolean onItemLongPress(final int index, final OverlayItem item) {
        return false;
    }
});
mOverlay.setFocusItemsOnTap(true);

mMapView.getOverlays().add(mOverlay);


How many icons can I put on the map?(我可以在地圖上放多少圖示) 答案是極大地依賴於硬體osmdroid基礎應用上跑。三星S5(沒有認可計劃)跑很好在3 k圖示和明顯震盪6 k圖示。你的情況可能不同。Android上執行X86現代硬體將執行在更高的數字。不過如果可能的話,建議限制你呈現的東西。 如果你也畫路徑、線、多邊形等,那麼這也改變了方程。畫多點圖形計算更加昂貴,因此消極地影響效能在高負載的情況下。減輕與多點圖形效能問題,一種策略是減少點交給地圖引擎在一個更高的縮放級別(數值較低),然後增加忠誠使用者放大的。實際上,你會剪輯在地圖檢視可見的資料範圍,這樣地圖檢視只在螢幕上,“知道”的不需要遍歷所有10 k圖示在地圖上你想要的。雖然你可以給所有10 k物件地圖檢視,但是每次移動或縮放地圖時,它會遍歷所有10 k項計算在哪裡畫(如果有的話)。使用此機制配合地圖運動聽眾和一個數據庫查詢,支援地理界限,可以支援一個豐富的經驗為使用者提供大量的資料,還有合理的效能。 重用畫板的圖示也會幫助記憶體使用。 Map Sources, Imagery and Tile sets(地圖資源,影象和瓷磚集) See https://github.com/osmdroid/osmdroid/wiki/Map-Sources