#!/usr/bin/python

# find_when_gps_is_here.py
#       --copyright--                   Copyright 2009 (C) Tranzoa, Co. All rights reserved.    Warranty: You're free and on your own here. This code is not necessarily up-to-date or of public quality.
#       --url--                         http://www.tranzoa.net/tzpython/
#       --email--                       pycode is the name to send to. tranzoa.com is the place to send to.
#       --bodstamps--
#       April 30, 2009          bar
#       --eodstamps--
##      \file
#
#
#       Find all instances of being at a particular point in GPS files.
#
#


import  latlon
import  tz_gps


MIN_DURATION    =  30           # seconds
MAX_DISTANCE    =  50           # yards
MAX_SPEED       =   5           # mph

AWAY_DURATION   = MIN_DURATION * 120




def find_points(trks, point, max_distance   = ((MAX_DISTANCE * 3) / latlon.feetPerMeter) / latlon.metersPerNauticalMile,
                             min_duration   = MIN_DURATION,
                             away_duration  = AWAY_DURATION,
                             max_speed      = (MAX_SPEED * 5.280) / latlon.feetPerMeter
               ) :
    """
        Find all the points in the given tracks that are near enough to the given point,
        returning a big point for each encounter.

        !!!!    This really should examine the points that are not at the location.
                For instance, if a point at the location is at the end of a track and at the beginning of the next track, then, really, they should be considered the same encounter.
    """

    trks        = tz_gps.sorted_tracks_by_when(trks)
    trks        = tz_gps.remove_duplicate_tracks(trks)


    def _under_spd(p) :
        return((p.speed == None) or (p.speed < max_speed))
    points      = tz_gps.get_nearby_tracks_points(trks, point, max_distance = max_distance, flt_rtn = _under_spd, flat = True)
    # print len(points), "points", len(trks[0].points), max_distance, max_speed, min_duration

    npoints     = []
    if  points  :
        points.sort(lambda a, b : cmp(a.point.when, b.point.when))

        pi      = 0
        while pi < len(points) :
            p   = points[pi].point
            pt  = t = p.when
            bp  = tz_gps.a_big_point(p)
            pi += 1
            fp  = (min_duration == 0) or False
            while pi < len(points) :
                p   = points[pi].point
                if  p.when - pt >= away_duration :      # note: must find at least two points at the location
                    break

                if  p.when -  t >=  min_duration :
                    fp  = True                          # ok, we've been at the location long enough, now let's just suck in the rest of 'em for this encounter

                bp.include_point(p)
                pt  = p.when
                pi += 1

            if  fp :
                bp.distance = bp.distance_from(point)
                npoints.append(bp)
            pass
        pass

    return(npoints)






help_str        = """
%s  (options) lat_lon gps_file(s)...

Options:

    --min_duration  hh:mm:ss    Minimum time spent at location.     (default: %u:%02u:%02u)
    --away_duration hh:mm:ss    Time away to make new location.     (default: %u:%02u:%02u)
    --max_distance  yards       Maximum distance from the location. (default: %u yards)
    --max_speed     mph         Maximum speed in MPH.               (default: %u MPH)

    --cache         file_name   Cache results from the files.

I find all the times in GPS files that the the given lat_lon is found.

"""


#
#
#   Main program
#
#
if  __name__ == '__main__' :

    import  cPickle
    import  glob
    import  os
    import  sys

    import  TZCommandLineAtFile
    import  replace_file
    import  tzlib
    import  tz_parse_time


    program_name    = sys.argv.pop(0)
    TZCommandLineAtFile.expand_at_sign_command_line_files(sys.argv)


    max_distance    = MAX_DISTANCE
    min_duration    = MIN_DURATION
    away_duration   = AWAY_DURATION
    max_speed       = MAX_SPEED


    cache_file_name = None
    cache           = {}


    while True :
        oi  = tzlib.array_find(sys.argv, [ "--help", "-h", "-?", "/h", "/H", "/?" ] )
        if  oi < 0 :    break
        del sys.argv[oi]
        print help_str % ( program_name,
                           (min_duration / 3600), (min_duration / 60) % 60, min_duration % 60,
                           (away_duration / 3600), (away_duration / 60) % 60, away_duration % 60,
                           max_distance,
                           max_speed,
                         )
        sys.exit(254)


    while True :
        oi  = tzlib.array_find(sys.argv, [ "--cache_file", "--cache", "-c" ] )
        if  oi < 0 :    break
        del sys.argv[oi]
        cache_file_name = sys.argv.pop(oi)
        if  os.path.isfile(cache_file_name) :
            fi          = open(cache_file_name, "rb")
            cache.update(cPickle.load(fi))
            fi.close();
        pass



    while True :
        oi  = tzlib.array_find(sys.argv, [ "--max_distance", "--max-distance", "--maxdistance", "--distance", "-d"] )
        if  oi < 0 :    break
        del sys.argv[oi]
        max_distance    = float(sys.argv.pop(oi))


    while True :
        oi  = tzlib.array_find(sys.argv, [ "--min_duration", "--min-duration", "--minduration", "--duration", "--time", "-t" ] )
        if  oi < 0 :    break
        del sys.argv[oi]
        min_duration   = tz_parse_time.parse_time_zone(sys.argv.pop(oi))
        if  min_duration == None :
            print "I cannot understand the minimum duration!"
            sys.exit(102)
        pass


    while True :
        oi  = tzlib.array_find(sys.argv, [ "--away_duration", "--away-duration", "--awayduration", "--away", "-a" ] )
        if  oi < 0 :    break
        del sys.argv[oi]
        away_duration   = tz_parse_time.parse_time_zone(sys.argv.pop(oi))
        if  away_duration == None :
            print "I cannot understand the away duration!"
            sys.exit(104)
        pass


    while True :
        oi  = tzlib.array_find(sys.argv, [ "--max_speed", "--max-speed", "--maxspeed", "--speed", "-s" ] )
        if  oi < 0 :    break
        del sys.argv[oi]
        max_speed   = float(sys.argv.pop(oi))


    max_distance    = ((max_distance * 3.0  ) / latlon.feetPerMeter) / latlon.metersPerNauticalMile
    max_speed       =  (max_speed    * 5.280) / latlon.feetPerMeter


    if  len(sys.argv) < 2 :
        print "Please tell me a lat,lon point and input .gpx/.kmz/.nmea file(s)!"
        sys.exit(101)


    lls         = sys.argv.pop(0)
    ( lat, lon) = latlon.parse_lat_lon(lls)
    if  lat    == None :
        print "I cannot understand %s!" % lls
        sys.exit(103)
    npoint      = tz_gps.a_point(lat = lat, lon = lon)


    fnames      = {}
    while len(sys.argv) >= 1 :
        fn      = sys.argv.pop(0)
        fns     = glob.glob(fn)
        if  not fns :
            print "Found no file named %s!" % ( fn )
            sys.exit(106)

        for fn in fns :
            fnames[fn]  = True
        pass


    fnames      = fnames.keys()
    fnames.sort()
    for fn in fnames :
        crc                 = None
        if  cache_file_name :
            crc             = tzlib.blkcrc32(0xffffFFFF, tzlib.read_whole_binary_file(fn))

        if  (crc != None) and (crc in cache) :
            print "Cache-skipping %s!"   % fn
        else :
            trks            = tz_gps.tracks_from_file(fn)
            points          = find_points(trks, npoint, max_distance = max_distance, min_duration = min_duration, away_duration= away_duration, max_speed = max_speed)

            if  not points  :
                print "No points in %s!" % fn
                cache[crc]  = fn
            else :
                print   "File: %s" % fn
                for p in points :
                    print p, (p.distance * latlon.metersPerNauticalMile * latlon.feetPerMeter) / 3.0, len(p.points)
                pass
            pass
        pass

    if  cache_file_name :
        tfn         = cache_file_name + ".tmp"
        fo          = open(tfn, "wb")
        cPickle.dump(cache, fo)
        fo.close()
        replace_file.replace_file(cache_file_name, tfn, cache_file_name + ".bak")

    pass


#
#
# eof

