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',
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
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",
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')
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:
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
'alarm',
'str_split_column_ipv6',
'formataddr',
+ 'LatLonFormatError',
+ 'clean_latitude',
+ 'clean_longitude',
]
IPV4_IN_IPV6_PREFIX = '::ffff:'
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:',