OpenStreetMap(二):如何使用osmdroid库 - 原文+译文

赵英范
2023-12-01
"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 / Android Studio。这将帮助你得到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

 类似资料: