简介
通常在项目中需要用到地图的时候我们会选择百度、高德等地图;但有的时候客户会要求提供基于手绘图的定位、导游导览功能,百度-瓦片图层、高德-图片覆盖物提供了相应的解决方案,我们可以把手绘图作为瓦片、覆盖物放在地图上,但是这种方案有其局限性:必须依托百度、高德地图本身才能查看手绘图。
接下来我将为大家带来另外一种解决方案,纯手绘图导游导览、定位,先看效果图:
NAMapKit
NAMapKit是一个开源的手绘图框架,支持缩放、地图标记、高清地图切片浏览、本地手绘图、在线手绘图功能。
Popup Menu
默认情况下NAMapKit的标记点弹出框不够nice,所以我在github上fork了NAMapKit,并实现了与City Guides by National Geographic这款App中类似的功能:
标记点
普通点标记
普通点的标记可以直接用尺子在手绘图上量一下,拿到目标点的CGPoint(x,y),之后把这个point作为NAPinAnnotation
添加到地图上,这样就实现了打点的功能。
当前位置打点
要实现当前位置打点,就得将经纬度坐标转换成图片的平面坐标,以百度地图来说,它提供了以下转换函数:
// BMKGeometry.h
/**
*将经纬度坐标转换为投影后的直角地理坐标
*@param coordinate 待转换的经纬度坐标
*@return 转换后的直角地理坐标
*/
UIKIT_EXTERN BMKMapPoint BMKMapPointForCoordinate(CLLocationCoordinate2D coordinate);
/**
*将投影后的直角地理坐标转换为经纬度坐标
*@param mapPoint 投影后的直角地理坐标
*@return 转换后的经纬度坐标
*/
UIKIT_EXTERN CLLocationCoordinate2D BMKCoordinateForMapPoint(BMKMapPoint mapPoint);
这里有一个问题:以一个城市为例,在百度地图16、17、18、19等缩放级别下都可以绘制这个城市,但在16、17、18、19级别下画出来的手绘图的尺寸(像素)肯定是不一样的!那么百度地图的直角坐标是以什么为标准呢?
- 以百度地图18级缩放级别为模板绘出的手绘图,其与百度地图的直角地理坐标是1:1的关系(zoomRate)
- 你可以在百度地图上找一条直线,看这条直线在16、17、18、19级下长度分别是多少,然后以18级的长度为基准,可以计算出每个缩放等级下的比例值(zoomRate)
所以,如果确定某个经纬度点肯定在当前手绘图中,则可以利用如下公式将经纬度坐标转换成手绘图的平面坐标:
// 如果手绘图是以百度地图18级为参照画出来的,则zoomRate为1
#define zoomRate 1
// 手绘图左上角点的经纬度坐标转换成百度地图的直角坐标
BMKMapPoint leftTopCoor = BMKMapPointForCoordinate(CLLocationCoordinate2DMake(lat, lng))
/**
* 将经纬度坐标转换成手绘图的平面(像素)坐标
*/
- (CGPoint)locationCoordToCgPoint:(CLLocationCoordinate2D)coor
{
BMKMapPoint point = BMKMapPointForCoordinate(coor);
return CGPointMake((point.x - leftTopCoor.x) * zoomRate, (point.y - leftTopCoor.y) * zoomRate);
}
/**
* 将手绘图的平面(像素)坐标转换成经纬度坐标
*/
- (CLLocationCoordinate2D)cgPointToLocationCoord:(CGPoint)point
{
BMKMapPoint mapPoint;
mapPoint.x = point.x / zoomRate + leftTopCoor.x;
mapPoint.y = point.y / zoomRate + leftTopCoor.y;
return BMKCoordinateForMapPoint(mapPoint);
}
判断给定经纬度是否在当前手绘图视野范围内
// 其中imageWidth、imageHeight为手绘图的像素大小(px单位)
- (BOOL)isLocationInImage:(CLLocationCoordinate2D)coor
{
CGPoint point = [self locationCoordToCgPoint:coor];
return !(point.x < 0 || point.y < 0 || point.x > imageWidth || point.y > imageHeight);
}