iOS

高德地图sdk分析

高德地图sdk是目前国内使用比较多的地图类sdk(还有百度地图也不错),目前项目中也用到了,但是相关封装还是不够清晰。为了做好这一层封装,首先需要把高德地图sdk研究一下。

高德地图有各种语言版本的sdk,其中iOS的sdk就包含基本sdk、云图sdk,导航sdk。后两个sdk暂时用不到,而基本sdk中又包含2d(栅格地图)和3d(矢量地图)两个版本的库以及搜索的库。今天要分析的就是2d和搜索的framework,最新的sdk版本是2.4.0。

高德地图sdk是目前国内使用比较多的地图类sdk(还有百度地图也不错),目前项目中也用到了,但是相关封装还是不够清晰。为了做好这一层封装,首先需要把高德地图sdk研究一下。

高德地图有各种语言版本的sdk,其中iOS的sdk就包含基本sdk、云图sdk,导航sdk。后两个sdk暂时用不到,而基本sdk中又包含2d(栅格地图)和3d(矢量地图)两个版本的库以及搜索的库。今天要分析的就是2d和搜索的framework,最新的sdk版本是2.4.0。

几个核心类说明

######MAMapView
地图视图类,sdk中最重要的一个类,用来负责地图的展示,该类直接从UIView继承而来。

1、属性visibleMapRect

1
self.mapView.visibleMapRect = MAMapRectMake(220880104, 101476980, 272496, 466656);

它定义了地图的可见范围,如下图所示:
mamap_2.png
我们如果改变visibleMapRect的大小,地图就会显示不同的区域。

2、属性mapType
地图类型,有两种类型: 普通地图和卫星地图,我们经常用到的就是普通地图

1
2
3
4
5
typedef NS_ENUM(NSInteger, MAMapType)
{
MAMapTypeStandard, // 普通地图
MAMapTypeSatellite // 卫星地图
};

3、属性showTraffic
是否展示路况拥堵信息,默认是不展示的,一般也用不到。

4、属性scrollEnabled和zoomEnabled
MAMapView虽然不是UIScrollView,但自身也实现了滚动和缩放功能(UIWebView同理)。这两个属性可以控制缩放和滚动的开关,默认都是打开的。

5、属性logoCenter和logoSize
高德的logo位置和大小,这个logo是需要显示出来的,不能隐藏不能遮挡(尊重知识产权)

6、属性showsCompass、compassOrigin、compassSize
地图的罗盘是否展示,展示的位置和大小

7、属性showsScale、scaleOrigin、scaleSize
地图的比例尺是否展示,展示的位置和大小

8、属性centerCoordinate
地图中心点的经纬度,改变它不会影响缩放比例。

9、属性showsUserLocation、userLocation、userTrackingMode
是否显示用户位置,用户位置的数据、用户追踪的模式。

10、属性annotations
地图上的标注,显示对应位置有什么东西,比较常用。

11、属性overlays
地图上的覆盖图形,能够在地图指定位置绘制自定义的图层,比较常用。

12、属性distanceFilter、desiredAccuracy、headingFilter
最小更新距离,定位精度,最小更新角度。

13、协议,MAMapViewDelegate,作为一个自定义的视图,当其数据对应数据发生变化时,肯定会有一些函数回调来改变自身。这些方法都被集中在MAMapViewDelegate中。一共有18个方法,大致可以分为4类:地图区域改变、用户位置发生变化、标注的创建和事件响应已经覆盖层的一些逻辑。个人觉得这个协议太大了,不符合接口分离原则 。

######AMapSearchAPI
AMapSearchAPI是一个NSObject,代表高德地图的搜索功能。

1、属性timeOut。
搜索请求超时时间,默认是20秒。

2、查询接口,不同的查询类型有不同的查询接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//POI查询接口函数,即根据 POI 参数选项进行 POI 查询。
- (void)AMapPlaceSearch:(AMapPlaceSearchRequest *)request;

//路径规划查询接口。
- (void)AMapNavigationSearch:(AMapNavigationSearchRequest *)request;

//输入提示查询接口。
- (void)AMapInputTipsSearch:(AMapInputTipsSearchRequest *)request;

//地址编码查询接口。
- (void)AMapGeocodeSearch:(AMapGeocodeSearchRequest *)request;

//逆地址编码查询接口。
- (void)AMapReGoecodeSearch:(AMapReGeocodeSearchRequest *)request;

//公交线路查询接口。
- (void)AMapBusLineSearch:(AMapBusLineSearchRequest *)request;

//公交车站查询接口。
- (void)AMapBusStopSearch:(AMapBusStopSearchRequest *)request;

3、协议AMapSearchDelegate,包含对各种请求接口成功失败的回调。

可以实现的功能

######自定义用户坐标点
如果起用了定位,如下的方法会被调用到,因为自身打点也是一个Annotation,但这个Annotation的类型是MAUserLocation。然后通过设置MAUserLocationRepresentation,就可以将用户坐标点进行自定义。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- (void)mapView:(MAMapView *)mapView didAddAnnotationViews:(NSArray *)views
{
MAAnnotationView *view = views[0];

// 放到该方法中用以保证userlocation的annotationView已经添加到地图上了。
if ([view.annotation isKindOfClass:[MAUserLocation class]])
{
MAUserLocationRepresentation *pre = [[MAUserLocationRepresentation alloc] init];
pre.fillColor = [UIColor colorWithRed:0.9 green:0.1 blue:0.1 alpha:0.3];
pre.strokeColor = [UIColor colorWithRed:0.1 green:0.1 blue:0.9 alpha:1.0];
pre.image = [UIImage imageNamed:@"location.png"];
pre.lineWidth = 3;
pre.lineDashPattern = @[@6, @3];

[self.mapView updateUserLocationRepresentation:pre];

view.calloutOffset = CGPointMake(0, 0);
}
}

######用户手势
缩放和滚动有属性直接控制,单击双击也能够支持,但是为了和AnnotationView进行区分,需要用下面的方法进行过滤。

1
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch

######添加浮层
浮层对象是遵循MAOverlay协议的对象,可以是圆、折线和多边形。在高德sdk中,MACircle、MAPolyline、MAPolygon都是浮层对象。
然后通过

1
- (void)addOverlays:(NSArray *)overlays;

将浮层对象添加到MapView中。

######自定义浮层
可以自定义浮层对象,只要符合MAOverlay协议即可。然后在绘制图层的代理方法中

1
- (MAOverlayRenderer *)mapView:(MAMapView *)mapView viewForOverlay:(id <MAOverlay>)overlay

子类话一个MAOverlayRenderer并返回,该子类只要重写方法

1
- (void)drawMapRect:(MAMapRect)mapRect zoomScale:(MAZoomScale)zoomScale inContext:(CGContextRef)context

该方法就如果UIView的drawRect,可以随心所欲的在上面画任何东西。

Overlay可以直接贴图,使用MAGroundOverlay类。虽然这个功能用自定义Overlay的方式也能实现,但比较麻烦。用MAGroundOverlay就很简单。

Overlay还支持大地曲线:MAGeodesicPolyline,图片tiles:MATileOverlay。

######打点标记
Annotaion就是在地图上标记一个位置,然后展示出来。展示的视图就是用MAAnnotationView。
像MapView插入Annotation的方法是:

1
- (void)addAnnotation:(id <MAAnnotation>)annotation;

类似UITableView,MAAnnotationView的创建实在代理方法中实现的。

1
- (MAAnnotationView *)mapView:(MAMapView *)mapView viewForAnnotation:(id<MAAnnotation>)annotation

MAAnnotationView可以自定义,可以有更好的显示效果;Annotation还支持动画,让显示的点动起来。

###搜索接口

######POI搜索
POI(Point of Interest),信息点,一家餐馆就是一个信息点。POI搜索可以根据ID、关键字、中心点搜索周边、指定范围搜索等搜索方式来满足不同的需求。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
/* 根据ID来搜索POI. */
- (void)searchPoiByID
{
AMapPlaceSearchRequest *request = [[AMapPlaceSearchRequest alloc] init];
// B000A80WBJ hotel
// B00141IEZK dining
// B000A876EH cinema
// B000A7O1CU scenic
request.searchType = AMapSearchType_PlaceID;
request.uid = @"B000A07060";
request.requireExtension = YES;

[self.search AMapPlaceSearch:request];

}

/* 根据关键字来搜索POI. */
- (void)searchPoiByKeyword
{
AMapPlaceSearchRequest *request = [[AMapPlaceSearchRequest alloc] init];

request.searchType = AMapSearchType_PlaceKeyword;
request.keywords = @"肯德基";
request.city = @[@"010"];
request.requireExtension = YES;
[self.search AMapPlaceSearch:request];
}

/* 根据中心点坐标来搜周边的POI. */
- (void)searchPoiByCenterCoordinate
{
AMapPlaceSearchRequest *request = [[AMapPlaceSearchRequest alloc] init];

request.searchType = AMapSearchType_PlaceAround;
request.location = [AMapGeoPoint locationWithLatitude:39.990459 longitude:116.481476];
request.keywords = @"餐饮";
/* 按照距离排序. */
request.sortrule = 1;
request.requireExtension = YES;

/* 添加搜索结果过滤 */
AMapPlaceSearchFilter *filter = [[AMapPlaceSearchFilter alloc] init];
filter.costFilter = @[@"100", @"200"];
filter.requireFilter = AMapRequireGroupbuy;
request.searchFilter = filter;

[self.search AMapPlaceSearch:request];
}

/* 在指定的范围内搜索POI. */
- (void)searchPoiByPolygon
{
NSArray *points = [NSArray arrayWithObjects:
[AMapGeoPoint locationWithLatitude:39.990459 longitude:116.481476],
[AMapGeoPoint locationWithLatitude:39.890459 longitude:116.581476],
nil];
AMapGeoPolygon *polygon = [AMapGeoPolygon polygonWithPoints:points];

AMapPlaceSearchRequest *request = [[AMapPlaceSearchRequest alloc] init];

request.searchType = AMapSearchType_PlacePolygon;
request.polygon = polygon;
request.keywords = @"Apple";
request.requireExtension = YES;

[self.search AMapPlaceSearch:request];
}

######地理编码
输入提示查询

1
2
3
AMapInputTipsSearchRequest *tips = [[AMapInputTipsSearchRequest alloc] init];
tips.keywords = @"关键字";
[self.search AMapInputTipsSearch:tips];

输入提示回调

1
2
3
4
5
6
- (void)onInputTipsSearchDone:(AMapInputTipsSearchRequest *)request response:(AMapInputTipsSearchResponse *)response
{
[self.tips setArray:response.tips];

[self.displayController.searchResultsTableView reloadData];
}

搜索地理编码

1
2
3
4
5
6
7
8
9
AMapGeocodeSearchRequest *geo = [[AMapGeocodeSearchRequest alloc] init];
geo.address = key;

if (adcode.length > 0)
{
geo.city = @[adcode];
}

[self.search AMapGeocodeSearch:geo];

地理编码回调,得到一个地点的经纬度信息

1
2
3
- (void)onGeocodeSearchDone:(AMapGeocodeSearchRequest *)request response:(AMapGeocodeSearchResponse *)response
{
}

######逆地理编码
逆地理编码与地理编码正好相反,是通过经纬度来查询文字描述的位置信息。
发起逆地理编码查询

1
2
3
4
5
6
AMapReGeocodeSearchRequest *regeo = [[AMapReGeocodeSearchRequest alloc] init];

regeo.location = [AMapGeoPoint locationWithLatitude:coordinate.latitude longitude:coordinate.longitude];
regeo.requireExtension = YES;

[self.search AMapReGoecodeSearch:regeo];

逆地理编码回调

1
2
3
4
5
6
7
8
9
10
11
- (void)onReGeocodeSearchDone:(AMapReGeocodeSearchRequest *)request response:(AMapReGeocodeSearchResponse *)response
{
if (response.regeocode != nil)
{
CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(request.location.latitude, request.location.longitude);
ReGeocodeAnnotation *reGeocodeAnnotation = [[ReGeocodeAnnotation alloc] initWithCoordinate:coordinate
reGeocode:response.regeocode];

[self.mapView addAnnotation:reGeocodeAnnotation];
}
}

除了以上的搜索,还有导航、搜索公交路线、公交站,这些再一般的应用中不会用到,就不介绍了。