//
//  MASmoothPathTool.m
//  iOS-path-smooth
//
//  Created by shaobin on 2017/10/12.
//  Copyright © 2017年 autonavi. All rights reserved.
//

#import "MASmoothPathTool.h"
#import <MAMapKit/MAMapKit.h>

@implementation MALonLatPoint
@end

@implementation MASmoothPathTool
{
    double lastLocation_x; //上次位置
    double currentLocation_x;//这次位置
    double lastLocation_y; //上次位置
    double currentLocation_y;//这次位置
    double estimate_x; //修正后数据
    double estimate_y; //修正后数据
    double pdelt_x; //自预估偏差
    double pdelt_y; //自预估偏差
    double mdelt_x; //上次模型偏差
    double mdelt_y; //上次模型偏差
    double gauss_x; //高斯噪音偏差
    double gauss_y; //高斯噪音偏差
    double kalmanGain_x; //卡尔曼增益
    double kalmanGain_y; //卡尔曼增益
    
    double m_R;
    double m_Q;
}

- (id)init {
    self = [super init];
    
    if(self) {
        self.intensity = 3;
        self.threshHold = 0.3f;
        self.noiseThreshhold = 10;
    }
    
    return self;
}
/**
 * 轨迹平滑优化
 * @param originlist 原始轨迹list,list.size大于2
 * @return 优化后轨迹list
 */
- (NSArray<MALonLatPoint*>*)pathOptimize:(NSArray<MALonLatPoint*>*)originlist {
    
    NSArray<MALonLatPoint*>* list = [self removeNoisePoint:originlist];//去噪
    NSArray<MALonLatPoint*>* afterList = [self kalmanFilterPath:list intensity:self.intensity];//滤波
    NSArray<MALonLatPoint*>* pathoptimizeList = [self reducerVerticalThreshold:afterList threshHold:self.threshHold];//抽稀
    return pathoptimizeList;
}

/**
 * 轨迹线路滤波
 * @param originlist 原始轨迹list,list.size大于2
 * @return 滤波处理后的轨迹list
 */
- (NSArray<MALonLatPoint*>*)kalmanFilterPath:(NSArray<MALonLatPoint*>*)originlist {
    return [self kalmanFilterPath:originlist intensity:self.intensity];
}


/**
 * 轨迹去噪，删除垂距大于20m的点
 * @param originlist 原始轨迹list,list.size大于2
 * @return 去燥后的list
 */
- (NSArray<MALonLatPoint*>*)removeNoisePoint:(NSArray<MALonLatPoint*>*)originlist{
    return [self reduceNoisePoint:originlist threshHold:self.noiseThreshhold];
}

/**
 * 单点滤波
 * @param lastLoc 上次定位点坐标
 * @param curLoc 本次定位点坐标
 * @return 滤波后本次定位点坐标值
 */
- (MALonLatPoint*)kalmanFilterPoint:(MALonLatPoint*)lastLoc curLoc:(MALonLatPoint*)curLoc {
    return [self kalmanFilterPoint:lastLoc curLoc:curLoc intensity:self.intensity];
}

/**
 * 轨迹抽稀
 * @param inPoints 待抽稀的轨迹list，至少包含两个点，删除垂距小于mThreshhold的点
 * @return 抽稀后的轨迹list
 */
- (NSArray<MALonLatPoint*>*)reducerVerticalThreshold:(NSArray<MALonLatPoint*>*)inPoints {
    return [self reducerVerticalThreshold:inPoints threshHold:self.threshHold];
}

/********************************************************************************************************/
/**
 * 轨迹线路滤波
 * @param originlist 原始轨迹list,list.size大于2
 * @param intensity 滤波强度（1—5）
 * @return 滤波后的list
 */
- (NSArray<MALonLatPoint*>*)kalmanFilterPath:(NSArray<MALonLatPoint*>*)originlist intensity:(int)intensity {
    if (!originlist || originlist.count <= 2) {
        return nil;
    }
    
    NSMutableArray<MALonLatPoint*>* kalmanFilterList = [NSMutableArray array];
    
    [self initial];//初始化滤波参数
    
    MALonLatPoint* point = nil;
    MALonLatPoint* lastLoc = [[MALonLatPoint alloc] init];
    lastLoc.lat = [originlist objectAtIndex:0].lat;
    lastLoc.lon = [originlist objectAtIndex:0].lon;
    [kalmanFilterList addObject:lastLoc];
    
    for (int i = 1; i < originlist.count; i++) {
        MALonLatPoint* curLoc = [originlist objectAtIndex:i];
        point = [self kalmanFilterPoint:lastLoc curLoc:curLoc intensity:intensity];
        if (point) {
            [kalmanFilterList addObject:point];
            lastLoc = point;
        }
    }
    return kalmanFilterList;
}

/**
 * 单点滤波
 * @param lastLoc 上次定位点坐标
 * @param curLoc 本次定位点坐标
 * @param intensity 滤波强度（1—5）
 * @return 滤波后本次定位点坐标值
 */
- (MALonLatPoint*)kalmanFilterPoint:(MALonLatPoint*)lastLoc curLoc:(MALonLatPoint*)curLoc intensity:(int)intensity {
    if (!lastLoc || !curLoc){
        return nil;
    }
    
    if (pdelt_x == 0 || pdelt_y == 0 ){
        [self initial];
    }
    
    MALonLatPoint* point = nil;
    if (intensity < 1){
        intensity = 1;
    } else if (intensity > 5){
        intensity = 5;
    }
    for (int j = 0; j < intensity; j++){
        point = [self kalmanFilter:lastLoc.lon value_x:curLoc.lon oldValue_y:lastLoc.lat value_y:curLoc.lat];
        curLoc = point;
    }
    return point;
}


/***************************卡尔曼滤波开始********************************/

//初始模型
- (void)initial {
    pdelt_x =  0.001;
    pdelt_y =  0.001;
    //        mdelt_x = 0;
    //        mdelt_y = 0;
    mdelt_x =  5.698402909980532E-4;
    mdelt_y =  5.698402909980532E-4;
}

- (MALonLatPoint*)kalmanFilter:(double)oldValue_x value_x:(double)value_x oldValue_y:(double)oldValue_y value_y:(double)value_y{
    lastLocation_x = oldValue_x;
    currentLocation_x= value_x;
    
    gauss_x = sqrt(pdelt_x * pdelt_x + mdelt_x * mdelt_x)+m_Q;     //计算高斯噪音偏差
    kalmanGain_x = sqrt((gauss_x * gauss_x)/(gauss_x * gauss_x + pdelt_x * pdelt_x)) +m_R; //计算卡尔曼增益
    estimate_x = kalmanGain_x * (currentLocation_x - lastLocation_x) + lastLocation_x;    //修正定位点
    mdelt_x = sqrt((1-kalmanGain_x) * gauss_x *gauss_x);      //修正模型偏差
    
    lastLocation_y = oldValue_y;
    currentLocation_y = value_y;
    gauss_y = sqrt(pdelt_y * pdelt_y + mdelt_y * mdelt_y)+m_Q;     //计算高斯噪音偏差
    kalmanGain_y = sqrt((gauss_y * gauss_y)/(gauss_y * gauss_y + pdelt_y * pdelt_y)) +m_R; //计算卡尔曼增益
    estimate_y = kalmanGain_y * (currentLocation_y - lastLocation_y) + lastLocation_y;    //修正定位点
    mdelt_y = sqrt((1-kalmanGain_y) * gauss_y * gauss_y);      //修正模型偏差
    
    MALonLatPoint *point = [[MALonLatPoint alloc] init];
    point.lon = estimate_x;
    point.lat = estimate_y;
    
    return point;
}
/***************************卡尔曼滤波结束**********************************/

/***************************抽稀算法*************************************/
- (NSArray<MALonLatPoint*>*)reducerVerticalThreshold:(NSArray<MALonLatPoint*>*)inPoints threshHold:(float)threshHold {
    if(inPoints.count < 2) {
        return inPoints;
    }
    
    NSMutableArray *ret = [NSMutableArray arrayWithCapacity:inPoints.count];
    
    for(int i = 0; i < inPoints.count; ++i) {
        MALonLatPoint *pre = ret.lastObject;
        MALonLatPoint *cur = [inPoints objectAtIndex:i];
        
        
        if (!pre || i == inPoints.count - 1) {
            [ret addObject:[inPoints objectAtIndex:i]];
            continue;
        }
        
        MALonLatPoint *next = [inPoints objectAtIndex:(i + 1)];
        
        MAMapPoint curP = MAMapPointForCoordinate(CLLocationCoordinate2DMake(cur.lat, cur.lon));
        MAMapPoint prevP = MAMapPointForCoordinate(CLLocationCoordinate2DMake(pre.lat, pre.lon));
        MAMapPoint nextP = MAMapPointForCoordinate(CLLocationCoordinate2DMake(next.lat, next.lon));
        double distance = [self calculateDistanceFromPoint:curP lineBegin:prevP lineEnd:nextP];
        if (distance >= threshHold) {
            [ret addObject:cur];
        }
    }
    
    return ret;
}

- (MALonLatPoint*)getLastLocation:(NSArray<MALonLatPoint*>*)oneGraspList {
    if (!oneGraspList || oneGraspList.count == 0) {
        return nil;
    }
    NSInteger locListSize = oneGraspList.count;
    MALonLatPoint* lastLocation = [oneGraspList objectAtIndex:(locListSize - 1)];
    return lastLocation;
}
   
/**
 * 计算当前点到线的垂线距离
 * @param pt 当前点
 * @param begin 线的起点
 * @param end 线的终点
 *
 */
- (double)calculateDistanceFromPoint:(MAMapPoint)pt
                           lineBegin:(MAMapPoint)begin
                             lineEnd:(MAMapPoint)end {
    
    MAMapPoint mappedPoint;
    double dx = begin.x - end.x;
    double dy = begin.y - end.y;
    if(fabs(dx) < 0.00000001 && fabs(dy) < 0.00000001 ) {
        mappedPoint = begin;
    } else {
        double u = (pt.x - begin.x)*(begin.x - end.x) +
        (pt.y - begin.y)*(begin.y - end.y);
        u = u/((dx*dx)+(dy*dy));
        
        mappedPoint.x = begin.x + u*dx;
        mappedPoint.y = begin.y + u*dy;
    }
    
    return MAMetersBetweenMapPoints(pt, mappedPoint);
}
/***************************抽稀算法结束*********************************/
   
- (NSArray<MALonLatPoint*>*)reduceNoisePoint:(NSArray<MALonLatPoint*>*)inPoints threshHold:(float)threshHold {
    if (!inPoints) {
        return nil;
    }
    if (inPoints.count <= 2) {
        return inPoints;
    }
    
    NSMutableArray<MALonLatPoint*>* ret = [NSMutableArray array];
    for (int i = 0; i < inPoints.count; i++) {
        MALonLatPoint* pre = [self getLastLocation:ret];
        MALonLatPoint* cur = [inPoints objectAtIndex:i];
        if (!pre || i == inPoints.count - 1) {
            [ret addObject:cur];
            continue;
        }
        MALonLatPoint* next = [inPoints objectAtIndex:(i + 1)];
        
        MAMapPoint curP = MAMapPointForCoordinate(CLLocationCoordinate2DMake(cur.lat, cur.lon));
        MAMapPoint prevP = MAMapPointForCoordinate(CLLocationCoordinate2DMake(pre.lat, pre.lon));
        MAMapPoint nextP = MAMapPointForCoordinate(CLLocationCoordinate2DMake(next.lat, next.lon));
        double distance = [self calculateDistanceFromPoint:curP lineBegin:prevP lineEnd:nextP];
        if (distance < threshHold){
            [ret addObject:cur];
        }
    }
    return ret;
}


@end
