From 0ba570e8fb3c0c865edba784b0fa7c005e867cbb Mon Sep 17 00:00:00 2001 From: =?utf8?q?Jean-Michel=20Nirgal=20Vourg=C3=A8re?= Date: Wed, 19 Jan 2011 18:11:21 +0000 Subject: [PATCH] Added filter_far_from filter in ais.common extract tool --- bin/common.py | 25 +++++++++- bin/ntools.py | 125 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 148 insertions(+), 2 deletions(-) diff --git a/bin/common.py b/bin/common.py index a8d9f46..f747813 100755 --- a/bin/common.py +++ b/bin/common.py @@ -13,7 +13,7 @@ import csv from ais.ntools import * from ais.db import * from ais.area import load_area_from_kml_polygon -from ais.earth3d import dist3_latlong_ais +from ais.earth3d import dist3_latlong_ais, dist3_xyz, latlon_to_xyz_deg, latlon_to_xyz_ais __all__ = [ 'DB_STARTDATE', 'DBPATH', @@ -1756,6 +1756,13 @@ def filter_area(nmea, area): return False return True +def filter_far_from(nmea, miles, lat, lon): + ''' + Returns true if position is farther than miles from (lat, lon) + ''' + return dist3_xyz(latlon_to_xyz_deg(lat, lon), latlon_to_xyz_ais(nmea.latitude, nmea.longitude)) >= miles + + def filter_knownposition(nmea): """ Returns false if position is not fully known @@ -1857,6 +1864,9 @@ def main(): parser.add_option('--filter-area', action='store', type='str', dest='area_file', metavar="FILE.KML", help="only process a specific area as defined in a kml polygon file.") + parser.add_option('--filter-farfrom', + action='store', dest='far_from', nargs=3, metavar='MILES LAT LONG', + help="only show ships farther than MILES miles from LAT,LONG") parser.add_option('--filter-destination', action='store', type='str', dest='filter_destination', metavar="DESTINATION", @@ -1954,6 +1964,7 @@ def main(): if options.sdt_start or options.granularity is not None or options.max_count: # time period is enabled if options.sdt_start: + # remove non digit characters options.sdt_start = "".join([ c for c in options.sdt_start if c.isdigit()]) if len(options.sdt_start)==14: dt_start = datetime.strptime(options.sdt_start, '%Y%m%d%H%M%S') @@ -1963,7 +1974,7 @@ def main(): print >> sys.stderr, "Invalid format for --start option" sys.exit(1) else: - dt_start = dt_end - timedelta(1) + dt_start = dt_end - timedelta(1) # One day if options.granularity is None: options.granularity = 600 else: @@ -1990,6 +2001,16 @@ def main(): area = load_area_from_kml_polygon(options.area_file) filters.append(lambda nmea: filter_area(nmea, area)) + if options.far_from: + miles = float(options.far_from[0]) + try: + lat = clean_latitude(unicode(options.far_from[1], 'utf-8')) + lon = clean_longitude(unicode(options.far_from[2], 'utf-8')) + except LatLonFormatError as err: + print >> sys.stderr, err.args + sys.exit(1) + filters.append(lambda nmea: filter_far_from(nmea, miles, lat, lon)) + if options.type_list: def filter_type(nmea): return nmea.type in options.type_list diff --git a/bin/ntools.py b/bin/ntools.py index 1a672ed..c60fc8d 100644 --- a/bin/ntools.py +++ b/bin/ntools.py @@ -18,6 +18,9 @@ __all__ = [ 'alarm', 'str_split_column_ipv6', 'formataddr', + 'LatLonFormatError', + 'clean_latitude', + 'clean_longitude', ] IPV4_IN_IPV6_PREFIX = '::ffff:' @@ -131,6 +134,128 @@ def formataddr(addr): return addr + + +class LatLonFormatError(ValueError): + pass + + +def clean_latitude(data): + ''' + Convert a string latitude into a float. + Raises LatLonFormatError on error. + ''' + data = data.replace(u"''", u'"') # common mistake + data = data.replace(u' ', u'') # remove spaces + sides = u'SN' + if not data: + return None + tmp, side = data[:-1], data[-1] + if side == sides[0]: + side = -1 + elif side == sides[1]: + side = 1 + else: + raise LatLonFormatError(u'Last character must be either %s or %s.', sides[0], sides[1]) + spl = tmp.split(u'°') + if len(spl) == 1: + raise LatLonFormatError(u'You need to use the ° character.') + d, tmp = spl + try: + d = float(d) + except ValueError: + raise LatLonFormatError(u'Degrees must be an number. It\'s %s.', d) + spl = tmp.split(u"'", 1) + if len(spl) == 1: + # no ' sign: ok only if there is nothing but the side after ° + # we don't accept seconds if there is no minutes: + # It might be an entry mistake + tmp = spl[0] + if len(tmp) == 0: + m = s = 0 + else: + raise LatLonFormatError(u'You must use the \' character between ° and %s.', data[-1]) + else: + m, tmp = spl + try: + m = float(m) + except ValueError: + raise LatLonFormatError(u'Minutes must be an number. It\'s %s.', m) + if len(tmp) == 0: + s = 0 + else: + if tmp[-1] != '"': + raise LatLonFormatError(u'You must use the " character between seconds and %s.', data[-1]) + s = tmp[:-1] + try: + s = float(s) + except ValueError: + raise LatLonFormatError(u'Seconds must be an number. It\'s %s.', s) + data = side * ( d + m / 60 + s / 3600) + + if data < -90 or data > 90: + raise LatLonFormatError(u'%s in not in -90..90 range', data) + return data + + + +def clean_longitude(data): + ''' + Convert a string latitude into a float. + Raises LatLonFormatError on error. + ''' + data = data.replace(u"''", u'"') # common mistake + data = data.replace(u' ', u'') # remove spaces + sides = u'WE' + if not data: + return None + tmp, side = data[:-1], data[-1] + if side == sides[0]: + side = -1 + elif side == sides[1]: + side = 1 + else: + raise LatLonFormatError(u'Last character must be either %s or %s.', sides[0], sides[1]) + spl = tmp.split(u'°') + if len(spl) == 1: + raise LatLonFormatError(u'You need to use the ° character.') + d, tmp = spl + try: + d = float(d) + except ValueError: + raise LatLonFormatError(u'Degrees must be an number. It\'s %s.', d) + spl = tmp.split(u"'", 1) + if len(spl) == 1: + # no ' sign: ok only if there is nothing but the side after ° + # we don't accept seconds if there is no minutes: + # It might be an entry mistake + tmp = spl[0] + if len(tmp) == 0: + m = s = 0 + else: + raise LatLonFormatError(u'You must use the \' character between ° and %s.', data[-1]) + else: + m, tmp = spl + try: + m = float(m) + except ValueError: + raise LatLonFormatError(u'Minutes must be an number. It\'s %s.', m) + if len(tmp) == 0: + s = 0 + else: + if tmp[-1] != '"': + raise LatLonFormatError(u'You must use the " character between seconds and %s.', data[-1]) + s = tmp[:-1] + try: + s = float(s) + except ValueError: + raise LatLonFormatError(u'Seconds must be an number. It\'s %s.', s) + data = side * ( d + m / 60 + s / 3600) + + if data < -180 or data > 180: + raise LatLonFormatError(u'%s in not in -180..180 range', data) + return data + #if __name__ == '__main__': # for test in ('12:34:56:78', # ':12::56:', -- 2.30.2