2014-07-09

之前工作在一家智能设备的公司,做过一个亲友定位监控系统,类似现在比较流行的360儿童手环。所以这里简单介绍定位与地图。

1 定位服务 iOS设备提供三种不同定位途径,蜂窝式移动电话基站定位;WiFi定位,通过查询一个WiFi路由器的地理位置信息,比较省电;GPS卫星定位,通过3~4颗卫星定位,最为准确,但是耗电量大。iOS系统如果能够接收GPS信息,那么设备优先采用GPS,其次是WiFi,最后是基站,开发人员不能选择哪种定位方式。

定位服务使用CoreLocation框架,主要使用CLLocationMananger、CLLocationManangerDelegate和CLLocation三个类,CLLocationMananger是定位服务管理类,获取设备的位置信息,CLLocationManangerDelegate是代理协议,CLLocation封装了位置信息。

这里要注意,CLLocationManangerDelegate 的locationManager:didUpdateToLocation:fromLocation:方法得到的坐标是火星坐标,这个原因你懂得,所以需要转换成真实的地理坐标。我使用的是一个第三方的CSqlite类,有一个转换坐标的数据库,你调用就可以转换为正确坐标了。

得到经纬度后,要进行地理位置信息反编码,使用CLGeocoder类实现,将地理坐标转换为地理文字描述信息,这些文字描述信息被封装在CLPlacemark类中。

当然给定地理信息的文字描述,也可以进行地理信息编码查询,转换为地理坐标,也是采用CLGeocoder类。

判断一个坐标点是否在一个无规则的多边形内

//    在范围内返回1,不在返回0
-(int)mutableBoundConrtolAction:(NSMutableArray *)arrSome:(CLLocationCoordinate2D )myCoordinate4{
    int n=arrSome.count;
    float vertx[n];
    float verty[n];
    for (int i=0; i<arrSome.count; i++) {
//MyPoint类存储的是经度和纬度
        vertx[i]=((MyPoint *)(arrSome[i])).x;
        verty[i]=((MyPoint *)(arrSome[i])).y;
    }
    if (arrSome.count==0) {

        return 1;
    }
    BOOL i=pnpoly(arrSome.count, vertx, verty, myCoordinate4.latitude, myCoordinate4.longitude);


    if (i) {
        return 1;
    }else{
        return 0;
    }


    return 1;
}
//多边形由边界的坐标点所构成的数组组成,参数格式 该数组的count,  多边形边界点x坐标 的组成的数组,多边形边界点y坐标 的组成的数组,需要判断的点的x坐标,需要判断的点的y坐标
BOOL pnpoly (int nvert, float *vertx, float *verty, float testx, float testy) {
    int i, j;
    BOOL c=NO;
    for (i = 0, j = nvert-1; i < nvert; j = i++) {

        if ( ( (verty[i]>testy) != (verty[j]>testy) ) &&
            (testx < (vertx[j]-vertx[i]) * (testy-verty[i]) / (verty[j]-verty[i]) + vertx[i]) )
            c = !c;
    }
    return c;
}

2 系统地图

地图我目前用过系统、百度以及高德,开发人员使用都是差不多的,下面的代码涉及的类都是高德地图api提供的类。

我之前做项目,使用高德地图,做到后期,项目会出现闪退,后来查出是地图区域内存的问题,然后重新布局了地图区域,使得每一个地图区域能够及时销毁,虽然闪退周期明显延长,但是还是存在,这里不知道是何原因,说来惭愧。

设置地图区域

-(void)SetMapRegion:(CLLocationCoordinate2D)myCoordinate
{
    MACoordinateRegion theRegion = { {0.0, 0.0 }, { 0.0, 0.0 } };
    theRegion.center=myCoordinate;
    [self.m_map setScrollEnabled:YES];
    theRegion.span.longitudeDelta = 0.01f;
    theRegion.span.latitudeDelta = 0.01f;
    [self.m_map setRegion:theRegion animated:YES];
}

平移地图,上下左右

-(void)panMap:(NSString *)direction{
    CLLocationCoordinate2D changeCoordinate=self.m_map.centerCoordinate;
    CGPoint changePoint=[self.m_map convertCoordinate:changeCoordinate toPointToView:self.m_map];

    if ([direction isEqualToString:@"up"]) {
        changePoint.y=changePoint.y+50;

    }else if ([direction isEqualToString:@"down"]) {
        changePoint.y=changePoint.y-50;
    }else if ([direction isEqualToString:@"left"]) {
        changePoint.x=changePoint.x-50;
    }else if ([direction isEqualToString:@"right"]) {
        changePoint.x=changePoint.x+50;
    }
    changeCoordinate=[self.m_map convertPoint:changePoint toCoordinateFromView:self.m_map];
    [self.m_map setCenterCoordinate:changeCoordinate animated:YES];
}

判断某一个坐标点是否在当前地图区域内

-(void)isAtCurrentRegion:(CLLocationCoordinate2D)coordiante{

    CGPoint point=[self.m_map convertCoordinate:coordiante toPointToView:self.view];
    if ((point.x<0)||(point.y<0)||(point.x>WScreen)||(point.y>HScreen)) {
//        如果不在 设置该点为地图中心点
        [self SetMapRegion:coordiante];
    }


}

在地图上添加标注

系统地图使用MapKit框架,核心是MKMapView类,显示地图只要添加MKMapView实例就可以了。如果要实现在地图上添加标注点,第以是触发添加动作,第二实现MKMapViewDelegate的mapView:viewForAnnotation:完成添加标注。

高德地图实现的原理也是一样的,高德地图使用的是MAMapKit框架。对于annotation,一般会自定义一个继承NSobject并且实现了maannotation协议的类,然后使用mapview的addAnnotation:方法就可以。MKReverseGeocoder类可以实现coordinate的反编码,这里需要实现它的代理,把得到的地理文字描述信息赋给annotation。这里需要实现代理的mapView:viewForAnnotation:方法,一个标注其实就是一个MAAnnotationView,标注有点类似tableviewcell,这里也有重用机制。实现代理的mapView:annotationView:calloutAccessoryControlTapped:方法可以响应leftCalloutAccessoryView或者rightCalloutAccessoryView的点击事件,不过这个accessory view必须继承自UIControl。 在地图上绘制线条和多边形

MAPolyline类定义一个由多个点相连的多段线,点与点之间尾部想连但第一点与最后一个点不相连, 通常MAPolyline是MAPolylineView的model,它提供了两个方法polylineWithPoints:count:、polylineWithCoordinates:count:用来添加线条,然后再通过map view的addOverlay:方法把Polyline实例添加进去,最后实现mapviewdelegate的mapView:viewForOverlay:方法就可以了。注意如果一开始添加的不是coordinate,而是point,可以通过map view的convertPoint:toCoordinateFromView:方法进行转换。

MAPolygon类定义的就是一个不规则的由多个点组成的闭合多边形,点与点之间按顺序尾部相连, 第一个点与最后一个点相连, 通常MAPolygon是MAPolygonView的model,首先需要添加坐标点的数组,可以使用polygonWithCoordinates:count:方法或者polygonWithPoints:count:方法,然后把这个polygon通过addOverlay:方法添加到map view上就可以了。然后可以在mapviewdelegate里面的mapView:viewForOverlay:方法里面给MAPolygonView的属性赋值,这样一个完整的多边形就出来了。

不管是高德地图还是百度地图等第三方,都会有一个mapsearchkit,这是一个用于查询的框架,有兴趣的朋友可以多加研究。