簡介
通常在項目中需要用到地圖的時候我們會選擇百度、高德等地圖;但有的時候客戶會要求提供基于手繪圖的定位、導游導覽功能,百度-瓦片圖層、高德-圖片覆蓋物提供了相應的解決方案,我們可以把手繪圖作為瓦片、覆蓋物放在地圖上,但是這種方案有其局限性:必須依托百度、高德地圖本身才能查看手繪圖。
接下來我將為大家帶來另外一種解決方案,純手繪圖導游導覽、定位,先看效果圖:
NAMapKit
NAMapKit是一個開源的手繪圖框架,支持縮放、地圖標記、高清地圖切片瀏覽、本地手繪圖、在線手繪圖功能。
NAMapKit Demo
Popup Menu
默認情況下NAMapKit的標記點彈出框不夠nice,所以我在github上fork了NAMapKit,并實現了與City Guides by National Geographic這款App中類似的功能:
Fork NAMapKit on github
標記點
普通點標記
普通點的標記可以直接用尺子在手繪圖上量一下,拿到目標點的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);
}