iOS之GPX文件读写KissXML

GPX(GPS eXchange Format,GPS交换格式)是一个XML格式,为应用软件设计的通用GPS数据格式,专门用来存储地理信息。

一个GPX文件当中可能包含一些路点(waypoint)及一些轨迹点(trackpoint)。 以全球定位系统(GPS设备)所产生的GPX档为例, 路点可能是各自独立互不相干的重要标记点, 例如照相的地点或用户手动标记的休息站或路口等等;至于GPS设备自动定时记录的则是轨迹点。 有顺序的一串轨迹点构成一个轨迹(track)或者路程(route)。轨迹是一个人曾经走过的记录,可能包含走错的路等等;路程则经常是建议未来用路人可以走的路径。所以,一般来讲,轨迹里的点,包含时间信息,路程里的点,则没有时间信息。点击查看GPX文件内容示例

1、在iOS开发当中,可以通过Xcode创建一个GPX文件,如图:

从上图中,可以很明确的看出GPX文件就是XML文件。

2、可以从其他地方下载GPX文件放进项目工程使用。
这种方法可以用来给模拟器虚拟定位测试使用。Xcode模拟器可以指定当前Location来机进行地理位置测试。

如上图中,红色箭头的Test就是我们刚刚创建的gpx文件。下面红色箭头可以添加GPX文件到项目里面。
然后就可以在模拟器选项里面选择所需要的GPX文件信息。
这里有一篇钉钉模拟器模拟打卡定位的文章

3、下面来说说代码创建GPX文件的做法。
既然GPX就是一个XML的话,那创建GPX就是创建XML,说道代码创建XML文件的话,iOS上可以搜出几个常用的库,具体就不详细说了,这里只结合项目需求来说说自己功能需求对应的GPX文件生成与读取。

iOS SDK 提供了两个xml框架。
NSXMLParser:它是基于objective-c语言的sax解析框架,是iOS SDK默认的xml解析框架,不支持dom模式。
libxml2: 它是基于c语言的xml解析器,被苹果整合在iOS SDK中,支持sax和dom模式。

第三方xml解析框架
tbxml:它是轻量级的dom模式解析库,不支持xml文档验证和xpath,只能读取xml文档,不能写xml文档。
touchxml:它是基于dom模式的解析库,与 tbxml类似,只能读取xml文档,不能写xml文档。
KissXML:它是基于dom模式的解析库,基于touchxml,主要的不同是可以写入xml文档。
Gdataxml:它是基于dom模式的解析库,由google开发,可以读写xml文档,支持xpath查询。

项目里面我使用的是KissXML来进行数据的生成与解析。
项目需求是记录一段运动轨迹,记录下每个定位点的数据信息,包括“经纬度”、“海拔”、“距离”、“速度”、“时间”等相关轨迹信息,用来在地图控件上绘制一条运动轨迹线条。所以在获取定位点信息之后,必须把数据写入GPX文件,上传到服务器保存起来。看下最终生成的GPX定位信息文件:


这个XML根节点为gpx,wpt为gpx的子节点,在wpt子节点的下面就是包含location的定位信息,n个wpt子节点定位信息组成了一个完整的GPX文件。(gpx、wpt等是自己的命名)
所以采用KissXML来生成的话,就如下面代码示例:

- (NSString *)writeXmlDocumentCallbackPath:(YTTrackRoute *)trackRoute
{
    //gpx(root根)
    DDXMLElement *gpxElement = [[DDXMLElement alloc]initWithName:@"gpx"];
    DDXMLNode *gpxUserID = [DDXMLNode attributeWithName:@"userid" stringValue:[[DataManagerObject shareDataManagerObject] getWildtoID]];
    [gpxElement addAttribute:gpxUserID];
    DDXMLNode *gpxUserName = [DDXMLNode attributeWithName:@"nickname" stringValue:[[DataManagerObject shareDataManagerObject] getUserNickName]];
    [gpxElement addAttribute:gpxUserName];
    DDXMLNode *gpxDevice = [DDXMLNode attributeWithName:@"device" stringValue:[UIDevice machineModelName]];
    [gpxElement addAttribute:gpxDevice];
    DDXMLNode *gpxVersion = [DDXMLNode attributeWithName:@"appversion" stringValue:YTAPPVersion];
    [gpxElement addAttribute:gpxVersion];
    DDXMLNode *gpxChannel = [DDXMLNode attributeWithName:@"channel" stringValue:@"wildto"];
    [gpxElement addAttribute:gpxChannel];
    DDXMLNode *gpxClient = [DDXMLNode attributeWithName:@"client" stringValue:YT_IOS_DEVICE];
    [gpxElement addAttribute:gpxClient];
    
    NSArray *points = trackRoute.route_points;
    for (NSInteger i = 0; i < points.count; i ++) {
        @autoreleasepool {
            YTRoutePoint *point = points[i];
            
            //wpt元素(包含经纬度两个属性)
            DDXMLElement *wptElement = [[DDXMLElement alloc]initWithName:@"wpt"];
            DDXMLNode *wptLonAtt = [DDXMLNode attributeWithName:@"lon" stringValue:[NSString stringWithFormat:@"%f",point.lon]];
            [wptElement addAttribute:wptLonAtt];
            DDXMLNode *wptLatAtt = [DDXMLNode attributeWithName:@"lat" stringValue:[NSString stringWithFormat:@"%f",point.lat]];
            [wptElement addAttribute:wptLatAtt];
            
            //速度
            DDXMLElement *speedElement = [[DDXMLElement alloc]initWithName:@"speed" stringValue:[NSString stringWithFormat:@"%f",point.speed]];
            [wptElement addChild:speedElement];
            
            //海拔
            DDXMLElement *altudeElement = [[DDXMLElement alloc]initWithName:@"level" stringValue:[NSString stringWithFormat:@"%f",point.altude]];
            [wptElement addChild:altudeElement];
            
            //时间点(时间戳)
            DDXMLElement *creatTimeElement = [[DDXMLElement alloc]initWithName:@"time" stringValue:[NSString stringWithFormat:@"%ld",(long)point.create_time]];
            [wptElement addChild:creatTimeElement];
            
            //坡度
            DDXMLElement *slopeElement = [[DDXMLElement alloc]initWithName:@"slope" stringValue:[NSString stringWithFormat:@"%f",point.slope]];
            [wptElement addChild:slopeElement];
            
            //当前点经过的总距离
            DDXMLElement *distacneElement = [[DDXMLElement alloc]initWithName:@"distacne" stringValue:[NSString stringWithFormat:@"%.1f",point.distance]];
            [wptElement addChild:distacneElement];
            
            [gpxElement addChild:wptElement];
        }
    }
    
    NSString *xmlString = [@"<?xml version=\"1.0\"?>" stringByAppendingString:[gpxElement XMLString]];
//    DLog(@"xmlString  %@",xmlString);
    
    DDXMLDocument *xmlDocument = [[DDXMLDocument alloc]initWithXMLString:xmlString options:0 error:nil];
    NSString *path = [NSString stringWithFormat:@"%@/%@.gpx",[self userFolderPath],trackRoute.route_app_id];
    DLog(@"路劲 == %@",path);
    
    NSString *result = [NSString stringWithFormat:@"%@",xmlDocument];
    BOOL writed = [result writeToFile:path atomically:YES encoding:NSUTF8StringEncoding error:nil];
    if (writed) {
        return path;
    }
    return nil;
}

有生成就有读取,下面是读取的代码示例:

- (NSArray *)readGPXFileToPoints:(NSString *)filePath route_app_id:(NSString *)route_app_id
{
    //转成XML字符串
    NSString *xmlString = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
    
    DDXMLDocument *xmlDoc = [[DDXMLDocument alloc]initWithXMLString:xmlString options:0 error:nil];
    
    //开始解析
    NSArray *children = [xmlDoc nodesForXPath:@"gpx/wpt" error:nil];
    
    NSMutableArray *points = [NSMutableArray arrayWithCapacity:children.count];
    
    //遍历每个元素
    YTRoutePoint *prevPoint = nil;
    for (DDXMLElement *child in children) {
        @autoreleasepool {
            NSString *lat =[[child attributeForName:@"lat"] stringValue];
            NSString *lon =[[child attributeForName:@"lon"] stringValue];
            
            DDXMLElement *speedEle = [child elementForName:@"speed"];
            DDXMLElement *timeEle = [child elementForName:@"time"];
            DDXMLElement *distacneEle = [child elementForName:@"distacne"];
            DDXMLElement *levelEle = [child elementForName:@"level"];
            DDXMLElement *slopeEle = [child elementForName:@"slope"];
            
            YTRoutePoint *point = [[YTRoutePoint alloc]init];
            point.route_app_id = route_app_id;
            point.lon = [lon doubleValue];
            point.lat = [lat doubleValue];
            point.speed = [[speedEle stringValue] doubleValue];
            point.altude = [[levelEle stringValue] doubleValue];
            point.distance = [[distacneEle stringValue] doubleValue];
            point.slope = [[slopeEle stringValue] doubleValue];
            point.create_time = [[timeEle stringValue] integerValue];
            
            //去重
            if(!prevPoint) {
                [points addObject:point];
                prevPoint = point;
            }
            else {
                if(prevPoint.lat == point.lat && prevPoint.lon == point.lon) {
                    continue;
                }
                [points addObject:point];
                prevPoint = point;
            }
            
        }
    }
    
    return [NSArray arrayWithArray:points];
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容