def lengthPoint(gpsTraces, j):
    return tripActivitySeparatorMongo.calDistance(
        gpsTraces[j]["gpsReading"]["location"]["coordinates"], gpsTraces[j + 1]["gpsReading"]["location"]["coordinates"]
    )
def inferModeChain(
    gpsTraces, trip, maxWalkSpeed, maxWalkAcceleration, minSegmentDuration, minSegmentLength, gpsAccuracyThreshold
):

    # Step 1: Label GPS points as walk points or non-walk points
    walkDummy = {}
    i = trip[0]
    while i < trip[1]:
        start, end = i, i
        while end < trip[1] and (
            gpsTraces[end]["gpsReading"]["gpsAccuracy"] > gpsAccuracyThreshold
            or gpsTraces[end + 1]["gpsReading"]["gpsAccuracy"] > gpsAccuracyThreshold
            or gpsTraces[end + 2]["gpsReading"]["gpsAccuracy"] > gpsAccuracyThreshold
        ):
            end += 1
        if start == end:
            features = determineFeatures(gpsTraces, i)
            if features["Acceleration"] <= 945:
                if features["Heading Change"] <= 0.0000:
                    walkDummy[i] = 0
                elif features["Speed"] <= 8.0205:
                    walkDummy[i] = 1
                else:
                    walkDummy[i] = 0
            else:
                walkDummy[i] = 0
            i += 1
        else:
            distance = tripActivitySeparatorMongo.calDistance(
                gpsTraces[start]["gpsReading"]["location"]["coordinates"],
                gpsTraces[end]["gpsReading"]["location"]["coordinates"],
            )
            time = (gpsTraces[end]["epochTime"] - gpsTraces[start]["epochTime"]) / 1000.0
            speed = 2.23694 * (float(distance) / time)
            dummy = int(speed < maxWalkSpeed)
            while i < end:
                walkDummy[i] = dummy
                i += 1
    # print walkDummy
    # print

    # Step 2: Identify walk and non-walk segments as consecutive walk or non-walk points
    modeChains = []
    beginSegment = trip[0]
    currentPoint = trip[0] + 1
    while currentPoint < trip[1]:
        if walkDummy[currentPoint] != walkDummy[beginSegment]:
            modeChains.append([beginSegment, currentPoint, int(walkDummy[beginSegment] != 0)])
            beginSegment = currentPoint
        currentPoint += 1
    modeChains.append([beginSegment, currentPoint, int(walkDummy[beginSegment] != 0)])
    # print modeChains
    # print

    # Step 3: If the time span of a segment is greater than minSegmentDuration milliseconds, label it
    # as certain. If it is less than minSegmentDuration milliseconds, and its backward segment is certain,
    # merge it with the backward segment. If no certain backward segment exists, label the segment as
    # uncertain, and save it as an independent segment.
    newModeChains = []
    for i in range(0, len(modeChains)):
        if gpsTraces[modeChains[i][1]]["epochTime"] - gpsTraces[modeChains[i][0]]["epochTime"] >= minSegmentDuration:
            modeChains[i].append(1)
            newModeChains.append(modeChains[i])
        elif newModeChains and newModeChains[-1][-1] == 1:
            newModeChains[-1][1] = modeChains[i][1]
        else:
            modeChains[i].append(0)
            newModeChains.append(modeChains[i])
    modeChains = newModeChains
    # print modeChains
    # print

    # Step 4: Merge consecutive uncertain segments into a single certain segment. Calculate average
    # speed over segment and compare it against maxWalkSpeed to determine whether walk or non-walk.
    # Check if this segment exceeds minSegmentDuration milliseconds. If it doesn't, and there exists
    # a certain forward segment, merge the new segment with this forward segment.
    newModeChains, i = [modeChains[0][0:-1]], 1
    while i < len(modeChains) and modeChains[i][-1] == 0:
        i += 1
    if i > 1:
        newModeChains[0][1] = modeChains[i - 1][1]
        distance = tripActivitySeparatorMongo.calDistance(
            gpsTraces[newModeChains[0][0]]["gpsReading"]["location"]["coordinates"],
            gpsTraces[newModeChains[0][1]]["gpsReading"]["location"]["coordinates"],
        )
        time = (gpsTraces[newModeChains[0][1]]["epochTime"] - gpsTraces[newModeChains[0][0]]["epochTime"]) / 1000.0
        speed = 2.23694 * (float(distance) / time)
        newModeChains[0][-1] = int(speed < maxWalkSpeed)
    if i < len(modeChains) and modeChains[0][-1] == 0:
        time = gpsTraces[newModeChains[0][1]]["epochTime"] - gpsTraces[newModeChains[0][0]]["epochTime"]
        if time < minSegmentDuration:
            modeChains[i][0] = trip[0]
            newModeChains = []
    while i < len(modeChains):
        newModeChains.append(modeChains[i][:-1])
        i += 1
    modeChains = newModeChains
    # print modeChains
    # print

    # Step 5: Merge consecutive walk segments and consecutive non-walk segments
    newModeChains = [modeChains[0]]
    for i in range(1, len(modeChains)):
        if modeChains[i][2] == newModeChains[-1][2]:
            newModeChains[-1][1] = modeChains[i][1]
        else:
            newModeChains.append(modeChains[i])
    modeChains = newModeChains

    return modeChains
def calInfAccuray(modeChains, gpsTraces):

    timeTotal, timeInferred, distTotal, distInferred = 0, 0, 0, 0
    segTotal, segInferred, segWalkInfNonWalk, segNonWalkInfWalk = 0, 0, 0, 0
    for modeChain in modeChains:
        segTotal += 1
        walk, nonWalk, activity = 0, 0, 0
        for i in range(modeChain[0], modeChain[1]):
            try:
                timeTotal += (gpsTraces[i + 1]["epochTime"] - gpsTraces[i]["epochTime"]) / 1000.0
                distTotal += (
                    tripActivitySeparatorMongo.calDistance(
                        gpsTraces[i]["gpsReading"]["location"]["coordinates"],
                        gpsTraces[i + 1]["gpsReading"]["location"]["coordinates"],
                    )
                    / 1609.34
                )

                if gpsTraces[i]["groundTruth"]["label"] == "Trip" and gpsTraces[i]["groundTruth"]["mode"] == "Walk":
                    walk += 1
                elif gpsTraces[i]["groundTruth"]["label"] == "Trip":
                    nonWalk += 1
                else:
                    activity += 1

                if (
                    modeChain[-1] == 1
                    and gpsTraces[i]["groundTruth"]["label"] == "Trip"
                    and gpsTraces[i]["groundTruth"]["mode"] == "Walk"
                ) or (
                    modeChain[-1] == 0
                    and gpsTraces[i]["groundTruth"]["label"] == "Trip"
                    and gpsTraces[i]["groundTruth"]["mode"] != "Walk"
                ):
                    timeInferred += (gpsTraces[i + 1]["epochTime"] - gpsTraces[i]["epochTime"]) / 1000.0
                    distInferred += (
                        tripActivitySeparatorMongo.calDistance(
                            gpsTraces[i]["gpsReading"]["location"]["coordinates"],
                            gpsTraces[i + 1]["gpsReading"]["location"]["coordinates"],
                        )
                        / 1609.34
                    )
            except:
                print "Unexpected error while calculating inference accuracy:", sys.exc_info()[0]
                pass

        if (max(walk, nonWalk, activity) == walk and modeChain[-1] == 1) or (
            max(walk, nonWalk, activity) == nonWalk and modeChain[-1] == 0
        ):
            segInferred += 1
        elif max(walk, nonWalk, activity) == walk and modeChain[-1] == 0:
            segWalkInfNonWalk += 1
        elif max(walk, nonWalk, activity) == nonWalk and modeChain[-1] == 1:
            segNonWalkInfWalk += 1

    return (
        timeTotal,
        timeInferred,
        distTotal,
        distInferred,
        segTotal,
        segInferred,
        segWalkInfNonWalk,
        segNonWalkInfWalk,
    )