title: Android Osmdroid
date: 2020-03-30 02:21:27.86
updated: 2020-06-08 03:35:43.852
url: https://ritoom.me/archives/osmdroid
categories:
- Code
tags: -
android
Osmdroid地图框架
osmdroid是对Android的MapView的开源替代品,由于Android自带的MapView需要Google提供相关支持,因此采用该框架库去完成加载离线地图与相关地图标绘等操作.
osmdroid支持在线与离线地图包,支持以下
- osmdroid’s flavor of a sqlite database (recommended)
- osmdroid ZIP
- MBTiles
- GEMF
- Mapsforge
- GeoPackage (still under development)
类型的存储格式.
Demo应用
一. 引入osmdroid库
在demo源码app目录下的build.gradle中添加
repositories {
mavenCentral()
}
dependencies {
implementation 'org.osmdroid:osmdroid-android:6.1.5'
implementation 'androidx.preference:preference:1.1.0'
}
注: osmdroid最低支持android版本为8,及Android2.2系统
二. 添加所需权限
在线模式下的地图所需权限
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
离线模式下的地图所需权限
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
如需获取位置信息,需添加一下权限
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
三. 页面设置
在demo中的layout页面目录下的page2_fragment.xml中添加osmdroid重写的MapView控件
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".page2.Page2Fragment">
<!-- MapView控件-->
<org.osmdroid.views.MapView android:id="@+id/map"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</FrameLayout>
对该控件绑定ID
android:id="@+id/map"
三. 控制类绑定初始化地图控件
在demo该页面的onCreateView方法中,初始化MapView变量,装载配置
private MapView map = null;
Configuration.getInstance().load(this, PreferenceManager.getDefaultSharedPreferences(this));
map = view.findViewById(R.id.map);
四. 操作地图
设置地图源
在线地图
map.setTileSource(TileSourceFactory.MAPNIK);
离线地图
map.setTileSource(new XYTileSource(
"4uMaps",
1,
15,
256,
".png",
new String[]{
"http://tileserver.4umaps.eu/"
}));
离线地图需预先放置在Android设备文件目录中的osmdroid目录下
在demo中使用的是osmdroid ZIP格式的离线地图,zip压缩包中的文件夹名称及XYTileSource构造函数的第一个变量,其他变量分别对应
final int aZoomMinLevel //地图最小等级
final int aZoomMaxLevel //地图最大等级
final int aTileSizePixels //Tile尺寸像素
final String aImageFilenameEnding //地图切片文件后缀名
final String[] aBaseUrl //基础url
注: 当使用离线地图时,使用 map.setUseDataConnection(false) 设置不要连接在线数据,以免对离线地图造成影响.
地图基本属性修改
地图设置中心点与地图初始化等级
地图设置需要使用Controller去控制地图元素
通过setZoom方法,设置地图初始化等级
通过GeoPoint新建一个点,再将该点设置为中心点
IMapController mapController = map.getController();
mapController.setZoom(12.5);
GeoPoint startPoint = new GeoPoint(36.6619, 117.020291);
mapController.setCenter(startPoint);
地图设置手势操作
mRotationGestureOverlay = new RotationGestureOverlay(this.getContext(), map);
mRotationGestureOverlay.setEnabled(true);
map.setMultiTouchControls(true);
map.getOverlays().add(this.mRotationGestureOverlay);
地图添加图标
Marker类是osmdroid用来处理图标相关业务的类,可以通过Marker设置图标等各种样式
Marker startMarker = new Marker(map);
startMarker.setIcon(getResources().getDrawable(R.drawable.ic_place_black_24dp));
startMarker.setPosition(startPoint);
startMarker.setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_BOTTOM);
map.getOverlays().add(startMarker);
添加地图事件
地图事件主要包括单机事件与长按事件,新建MapEventsReceiver类后重新其中的方法即可
MapEventsReceiver mapEventsReceiver = new MapEventsReceiver() {
@Override
public boolean singleTapConfirmedHelper(GeoPoint p) {
Marker newMarker = new Marker(map);
newMarker.setPosition(p);
map.getOverlays().add(newMarker);
return false;
}
@Override
public boolean longPressHelper(GeoPoint p) {
return false;
}
};
map.getOverlays().add(new MapEventsOverlay(mapEventsReceiver));
在地图添加图片按钮控件
因为整个页面使用FrameLayout布局,控件如果不刻意去设置,会自动漂浮在页面左上角.
- 页面添加控件
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".page2.Page2Fragment">
<!-- MapView控件-->
<org.osmdroid.views.MapView android:id="@+id/map"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
<!-- 图片按钮控件-->
<ImageButton
android:id="@+id/imageButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@android:drawable/ic_menu_edit" />
</FrameLayout>
- 控制器类初始化该控件,并添加事件
drawButton = view.findViewById(R.id.imageButton);
drawButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (buttenStatus){
mRotationGestureOverlay.setEnabled(false);
map.setMultiTouchControls(false);
buttenStatus = false;
}else {
mRotationGestureOverlay.setEnabled(true);
map.setMultiTouchControls(true);
map.setClickable(true);
buttenStatus = true;
}
}
});
绘制地图
在osmdroid的基本绘制方法中,自身具有添加标点/画线/画圆的API,但是我所需的应用场景是不是通过程序预置好的经纬度来添加图标,而是通过用户在地图视图上进行触摸标绘,因此需要将标绘的方法进行逻辑处理.
通过参考osmdroid的demo,发现demo中是通过自定义View,使用View的onTouchEvent方法监控用户的触摸事件,onDraw方法绘制页面.
以下为demo代码举例
@Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
pts.add(new Point((int)x,(int)y));
switch (event.getAction()){
case MotionEvent.ACTION_DOWN://用户按下事件
touch_start(x,y);
invalidate();
break;
case MotionEvent.ACTION_MOVE://用户触摸移动事件
touch_move(x,y);
invalidate();
break;
case MotionEvent.ACTION_UP://用户松开事件
touch_up(x,y);
invalidate();
break;
}
return true;
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawPath(mPath,mPaint);// 传入绘制的路径与样式参数
}
touch_start()与touch_move()方法负责视图绘制,touch_up()方法则将绘制后相关数据调用osmdroid中的标绘方法将图形添加到视图上.
private void touch_up(float x,float y){
mPath.reset();
canvas.drawPath(mPath,mPaint);
mPath.reset();
if (map != null){
Projection projection = map.getProjection();
ArrayList<GeoPoint> geoPoints = new ArrayList<>();
final Point unrotatedPoint = new Point();
for (int i=0; i < pts.size(); i++) {
projection.unrotateAndScalePoint(pts.get(i).x, pts.get(i).y, unrotatedPoint);
GeoPoint iGeoPoint = (GeoPoint) projection.fromPixels(unrotatedPoint.x, unrotatedPoint.y);
geoPoints.add(iGeoPoint);
}
if (geoPoints.size() > 2){
final int color = Color.argb(100, 100, 100, 100);
final Polyline line = new Polyline(map);
line.setInfoWindow(
new BasicInfoWindow(org.osmdroid.library.R.layout.bonuspack_bubble, map));
line.getOutlinePaint().setColor(color);
line.setPoints(geoPoints);
line.showInfoWindow();
line.getOutlinePaint().setStrokeCap(Paint.Cap.ROUND);
final Paint arrowPaint = new Paint();
arrowPaint.setColor(color);
arrowPaint.setStrokeWidth(10.0f);
arrowPaint.setStyle(Paint.Style.FILL_AND_STROKE);
arrowPaint.setAntiAlias(true);
final Path arrowPath = new Path(); // a simple arrow towards the right
arrowPath.moveTo(- 10, - 10);
arrowPath.lineTo(10, 0);
arrowPath.lineTo(- 10, 10);
arrowPath.close();
map.getOverlayManager().add(line);
}
}
pts.clear();
}