绿蚁新醅酒,红泥小火炉。
晚来天欲雪,能饮一杯无?
百度地图
1、注册地图api key
1 | // 在程序启动代理方法注册baidu map api key 百度开发者平台获取 |
2、初始化地图
- 添加地图的初始化配置
1 | - (void)setupBaiduMapView |
- 开启定位服务回调,更新用户位置信息
1 | -(void)didUpdateBMKUserLocation:(BMKUserLocation *)userLocation |
3、显示自定义大头针
- 3.1、从服务器获取当前设备的相关信息转为模型数据(XSDeviceLocation *locationDetails)
3.2、根据当前显示的地图类型设置对应的坐标信息大头针
- 我的做法在XSDeviceLocation模型里面提供了对应地图的获取属性,在属性getter方法中进行服务器的坐标转换(处理国内返回火星坐标、国外返回标准坐标)
- 如获取百度坐标locationCoordinateBaidu、获取谷歌坐标locationCoordinateGoogle
根据类型设置坐标数据
1
2
3
4
5
6- (void)setupPetLocationCoordinate:(CLLocationCoordinate2D)coordinate mapType:(XSDisplayMapType)mapType
{
// 坐标过滤等其他操作
// ...
[self setupBaiduMapCoordinate:coordinate];
}
3.3、根据设备坐标的相关配置
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- (void)setupBaiduMapCoordinate:(CLLocationCoordinate2D)coordinate
{
[self.baiduMapView setCenterCoordinate:coordinate animated:YES];
// 添加大头针模型数据
self.pointAnnotation.annotationType = XSAnnotationTypeLocationPoint;
self.pointAnnotation.locationDetailsModel = self.locationDetails;
self.pointAnnotation.coordinate = coordinate;
[self.baiduMapView addAnnotation:self.pointAnnotation];
// 添加精度圈
!self.baiduAccuracyCircle ? : [self.baiduMapView removeOverlay:self.baiduAccuracyCircle]; // 移除添加的精度圈
if (self.locationDetails.GpsType == XSLocationModeWifiAndLbs || self.locationDetails.GpsType == XSLocationModeLbs) { // wifi 显示精度圈
self.baiduAccuracyCircle = [BMKCircle circleWithCenterCoordinate:coordinate radius:self.locationDetails.Radius];
[self.baiduMapView addOverlay:self.baiduAccuracyCircle];
}
// 解析地址
BMKReverseGeoCodeOption *reverseGeocodeSearchOption = [[BMKReverseGeoCodeOption alloc] init];
reverseGeocodeSearchOption.reverseGeoPoint = coordinate;
if (![self.geoCodeSearch reverseGeoCode:reverseGeocodeSearchOption]) {
XSLog(@"百度反地理编码获取地址失败");
}
// 计算地图区域缩放
CLLocationDistance horizontalScreenDistance = [self horizontalScreenDistance];
if (self.locationDetails.Radius * 2 < horizontalScreenDistance || self.baiduMapView.zoomLevel <= kMapsMinZoomLevel) {
return;
}
BMKCoordinateRegion region = BMKCoordinateRegionMakeWithDistance(coordinate, self.locationDetails.Radius * 2 + self.locationDetails.Radius / 2, self.locationDetails.Radius * 2 + self.locationDetails.Radius / 2);
[self.baiduMapView setRegion:region animated:YES];
}自定义大头针模型XSPointAnnotation继承自BMKPointAnnotation
- 扩充两个属性:
- annotationType :枚举,显示的大头针类型(切换图标)
- locationDetailsModel :当前大头针的模型数据,点击显示气泡详情的数据
添加精度圈后精度半径过大可能超出屏幕影响用户体验,解决方法如下:
- 根据屏幕最左和最后两个点,转换出地图上对应的坐标点并计算出距离d
- 根据当前精度圈半径 * 2 和 d 比较,如果超出屏幕就重新设置地图显示区域
计算屏幕距离核心代码
1
2
3
4
5CLLocationCoordinate2D leftCoor = [self.baiduMapView convertPoint:CGPointMake(0, XSScreenH) toCoordinateFromView:self.view];
CLLocationCoordinate2D rightCoor = [self.baiduMapView convertPoint:CGPointMake(XSScreenW, XSScreenH) toCoordinateFromView:self.view];
BMKMapPoint pointLeft = BMKMapPointForCoordinate(leftCoor);
BMKMapPoint pointRight = BMKMapPointForCoordinate(rightCoor);
return BMKMetersBetweenMapPoints(pointLeft, pointRight);
3.4、配置信息的回调(真正开始处理显示位置大头针、精度圈、点击显示的弹框泡泡)
1 | // 大头针及大头针弹框 |
4、多个坐标线显示在屏幕范围内
- 绘制多个轨迹点和绘制线比较简单,直接参考官方文档
- 在历史轨迹界面,可能希望显示的所有轨迹点都在屏幕范围内
1 | - (void)baiduMapViewFitAnnotationsWithCoordinates:(CLLocationCoordinate2D *)coords count:(NSUInteger)count |
谷歌地图
1、注册地图api key
1 | // 在程序启动代理方法注册google map api key 谷歌开发者平台获取 |
2、初始化地图
1 | - (void)setupGoogleMapView |
3、显示自定义大头针
- 3.1、从服务器获取当前设备的相关信息转为模型数据(XSDeviceLocation *locationDetails)
3.2、根据当前显示的地图类型设置对应的坐标信息大头针
- 我的做法在XSDeviceLocation模型里面提供了对应地图的获取属性,在属性getter方法中进行服务器的坐标转换(处理国内返回火星坐标、国外返回标准坐标)
- 如获取百度坐标locationCoordinateBaidu、获取谷歌坐标locationCoordinateGoogle
根据类型设置坐标数据
1
2
3
4
5
6- (void)setupPetLocationCoordinate:(CLLocationCoordinate2D)coordinate mapType:(XSDisplayMapType)mapType
{
// 坐标过滤等其他操作
// ...
[self setupGoogleMapCoordinate:coordinate];
}
3.3、根据设备坐标的相关配置
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- (void)setupGoogleMapCoordinate:(CLLocationCoordinate2D)coordinate
{
[self.googleMapView animateToLocation:coordinate];
// Creates a marker in the center of the map.
self.marker.markerType = XSMarkerTypeDefaultLocation;
self.marker.position = coordinate;
self.marker.appearAnimation = YES;
self.marker.tracksInfoWindowChanges = YES;
self.marker.icon = [UIImage imageNamed:self.locationDetails.deviceBindingTypeImageName];
self.marker.map = self.googleMapView;
self.marker.locationDetailsModel = self.locationDetails; // 传递模型数据
// 点击大头针显示的自定义气泡控件 懒加载
self.petAnnotationView.locationDetails = self.locationDetails;
self.petAnnotationView.isHiddenNoNeeds = NO;
[[GMSGeocoder geocoder] reverseGeocodeCoordinate:coordinate completionHandler:^(GMSReverseGeocodeResponse * _Nullable response, NSError * _Nullable error) {
//FIXME: 错误信息处理
// 获取第一个位置信息
GMSAddress *addressModel = response.firstResult;
NSString *firstString = addressModel.lines.firstObject;
NSString *lastString = addressModel.lines.lastObject;
NSString *address = [NSString stringWithFormat:@"%@, %@", firstString, lastString];
self.locationDetails.deviceAddress = address;
self.marker.locationDetailsModel = self.locationDetails; // 传递模型数据
self.petAnnotationView.locationDetails = self.locationDetails;
}];
// 添加精度圈
self.googleAccuracycircle.map = nil; // 清空已存在的
if (self.locationDetails.GpsType == XSLocationModeWifiAndLbs) { // wifi 显示精度圈
self.googleAccuracycircle = [GMSCircle circleWithPosition:coordinate radius:self.locationDetails.Radius];
self.googleAccuracycircle.fillColor = [[UIColor colorWithHexString:kAccuracyCircleWifiAndLbsFillColor] colorWithAlphaComponent:0.15];
self.googleAccuracycircle.strokeColor = [[UIColor colorWithHexString:kAccuracyCircleWifiAndLbsStrokeColor] colorWithAlphaComponent:0.15];
self.googleAccuracycircle.strokeWidth = 1;
self.googleAccuracycircle.map = self.googleMapView;
} else if (self.locationDetails.GpsType == XSLocationModeLbs) {
self.googleAccuracycircle = [GMSCircle circleWithPosition:coordinate radius:self.locationDetails.Radius];
self.googleAccuracycircle.fillColor = [[UIColor colorWithHexString:kAccuracyCircleLbsFillColor] colorWithAlphaComponent:0.15];
self.googleAccuracycircle.strokeColor = [[UIColor colorWithHexString:kAccuracyCircleLbsStrokeColor] colorWithAlphaComponent:0.15];
self.googleAccuracycircle.strokeWidth = 1;
self.googleAccuracycircle.map = self.googleMapView;
}
// 计算地图区域缩放
CLLocationDistance horizontalScreenDistance = [self horizontalScreenDistance];
if (self.locationDetails.Radius * 2 < horizontalScreenDistance || self.googleMapView.camera.zoom <= kMapsMinZoomLevel) {
return;
}
int zoomLevel = self.googleMapView.camera.zoom;
// double radius = self.locationDetails.Radius + self.locationDetails.Radius / 2;
double radius = self.locationDetails.Radius / 2;
double scale = radius / 500;
zoomLevel = (int) (16 - log(scale) / log(2));
zoomLevel--;
[CATransaction begin];
[CATransaction setValue:[NSNumber numberWithFloat:XSAnimationDuration] forKey:kCATransactionAnimationDuration];
GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:coordinate.latitude longitude:coordinate.longitude zoom:zoomLevel];
[self.googleMapView animateToCameraPosition:camera];
[CATransaction commit];
}自定义大头针模型XSMarker继承自GMSMarker
- 扩充两个属性:
- markerType :枚举,显示的大头针类型(切换图标)
- locationDetailsModel :当前大头针的模型数据,点击显示气泡详情的数据
添加精度圈后精度半径过大可能超出屏幕影响用户体验,解决方法如下:
- 根据屏幕最左和最后两个点,转换出地图上对应的坐标点并计算出距离d
- 根据当前精度圈半径 * 2 和 d 比较,如果超出屏幕就重新设置地图显示区域
计算屏幕距离核心代码
1
2
3CLLocationCoordinate2D leftCoor = [self.googleMapView.projection coordinateForPoint:CGPointMake(0, XSScreenH)];
CLLocationCoordinate2D rightCoor = [self.googleMapView.projection coordinateForPoint:CGPointMake(XSScreenW, XSScreenH)];
return GMSGeometryDistance(leftCoor, rightCoor);
3.4、点击大头针显示气泡view的回调
1 | - (UIView *)mapView:(GMSMapView *)mapView markerInfoWindow:(GMSMarker *)marker |
4、多个坐标线显示在屏幕范围内
- 绘制多个轨迹点和绘制线比较简单,直接参考官方文档
- 在历史轨迹界面,可能希望显示的所有轨迹点都在屏幕范围内
1 | - (void)googleMapViewFitAnnotationsWithCoordinates:(NSArray *)coordsModels count:(NSUInteger)count |
Maps 导航
- 检测是否安装对应的地图
- 常用的4个地图的 URL Scheme:
1.苹果自带地图(不需要检测,所以不需要URL Scheme)
2.百度地图 :baidumap://
3.高德地图 :iosamap://
4.谷歌地图 :comgooglemaps://
- 在IOS9之后,苹果进一步完善了安全机制,必须在plist里面设置url scheme白名单,不然无法打开对应的应用
- 添加白名单:
在 info.plist 文件里面,添加一个字段:LSApplicationQueriesSchemes,类型为数组
然后在这个数组里面再添加我们所需要的地图 URL Scheme :
1
2
3
4 > baidumap // 百度
> iosamap // 高德
> comgooglemaps // 谷歌
>
- 应用内部调用google maps地图发起导航功能
1、x-source=%@&x-success=%@跟高德一样 这里分别代表APP的名称和URL Scheme
2、saddr=这里留空则表示从当前位置触发。
1 | if ([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"comgooglemaps://"]]) { |
- 应用内部调用百度地图发起导航功能
1,origin=, 这个是不能被修改的,不然无法把出发位置设置为当前位置
2,destination = latlng:%f,%f|name = 目的地这里面的 name 的字段不能省略,否则导航会失败,而后面的文字则可以随意,赋个你的目的地的值给他就可以了。
3,coord_type = gcj02coord_type 允许的值为 bd09ll、gcj02、wgs84,如果你 APP 的地图 SDK 用的是百度地图 SDK,请填 bd09ll,否则就填gcj02,wgs84的话基本是用不上了
1 | if ([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"baidumap://"]]) { |
- 应用内部调用高德地图发起导航功能
1、sourceApplication=%@&backScheme=%@sourceApplication代表你自己APP的名称 会在之后跳回的时候显示出来 所以必须填写 backScheme是你APP的URL Scheme 不填是跳不回来的哟
2、dev=0这里填0就行了,跟上面的gcj02一个意思 1代表wgs84 也用不上
1 | if ([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"iosamap://"]]) { |
- 应用内部调用苹果地图发起导航功能
1 | CLLocationCoordinate2D loc = CLLocationCoordinate2DMake([self.model.latitude floatValue], [self.model.longitude floatValue]); |