You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
igc2kmz/igc2kmz/util.py

315 lines
9.4 KiB
Python

# igc2kmz utility functions
# Copyright (C) 2008 Tom Payne
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import __builtin__
import datetime
import itertools
import math
import sys
class Bounds(object):
def __init__(self, value):
if isinstance(value, list):
self.min = value[0]
self.max = value[0]
for i in xrange(1, len(value)):
if value[i] < self.min:
self.min = value[i]
elif value[i] > self.max:
self.max = value[i]
elif isinstance(value, tuple):
self.min, self.max = value
else:
self.min = value
self.max = value
def __repr__(self):
return 'Bounds((%(min)s, %(max)s))' % self.__dict__
def update(self, value):
if isinstance(value, Bounds):
if value.min < self.min:
self.min = value.min
if value.max > self.max:
self.max = value.max
else:
if value < self.min:
self.min = value
if value > self.max:
self.max = value
def tuple(self):
return (self.min, self.max)
class BoundsSet(object):
def update(self, other):
for key, value in other.__dict__.items():
if hasattr(self, key):
getattr(self, key).update(value)
else:
setattr(self, key, value)
class OpenStruct(object):
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
def __iter__(self):
return self.__dict__.iteritems()
def __repr__(self):
return 'OpenStruct(%s)' % ', '.join('%s=%s' % (key, repr(value))
for key, value in self)
def runs(seq):
generator = enumerate(seq)
try:
start, current = generator.next()
except StopIteration:
return
index = 0
for index, element in generator:
if element != current:
yield slice(start, index)
start, current = index, element
yield slice(start, index + 1)
def runs_where(seq):
generator = enumerate(seq)
try:
start, current = generator.next()
except StopIteration:
return
index = 0
for index, element in generator:
if element != current:
if current:
yield slice(start, index)
start, current = index, element
if current:
yield slice(start, index + 1)
def condense(seq, t, delta):
try:
sl = seq.next()
start, stop = sl.start, sl.stop
except StopIteration:
return
for sl in seq:
if t[sl.start] - t[stop] < delta:
stop = sl.stop
else:
yield slice(start, stop)
start, stop = sl.start, sl.stop
yield slice(start, stop)
def douglas_peucker(x, y, epsilon):
"""
Implement the Douglas-Peucker line simplification algorithm.
TODO: implement http://www.cs.ubc.ca/cgi-bin/tr/1992/TR-92-07.ps
"""
indexes = set([0])
stack = [(0, len(x) - 1)]
while stack:
left, right = stack.pop()
indexes.add(right)
kx, ky = y[left] - y[right], x[right] - x[left]
c = x[left] * y[right] - x[right] * y[left]
pivot = left + 1
max_dist = abs(kx * x[pivot] + ky * y[pivot] + c)
for i in xrange(left + 2, right):
dist = abs(kx * x[i] + ky * y[i] + c)
if dist > max_dist:
max_dist = dist
pivot = i
max_dist /= math.sqrt((x[right] - x[left]) ** 2
+ (y[right] - y[left]) ** 2)
if max_dist > epsilon:
indexes.add(pivot)
stack.append((left, pivot))
stack.append((pivot, right))
return sorted(indexes)
def incr_douglas_peucker(x, y, epsilon, max_indexes=sys.maxint):
indexes = set([0])
queue = [(0, len(x) - 1)]
i = 0
while i < len(queue):
left, right = queue[i]
i += 1
indexes.add(right)
if len(indexes) == max_indexes:
break
kx, ky = y[left] - y[right], x[right] - x[left]
c = x[left] * y[right] - x[right] * y[left]
pivot = left + 1
max_dist = abs(kx * x[pivot] + ky * y[pivot] + c)
for j in xrange(left + 2, right):
dist = abs(kx * x[j] + ky * y[j] + c)
if dist > max_dist:
max_dist = dist
pivot = j
max_dist /= math.sqrt((x[right] - x[left]) ** 2
+ (y[right] - y[left]) ** 2)
if max_dist > epsilon:
indexes.add(pivot)
if len(indexes) == max_indexes:
break
queue.append((left, pivot))
queue.append((pivot, right))
return sorted(indexes)
def bsearch(seq, value, cmp=__builtin__.cmp):
left, right = 0, len(seq)
while left <= right:
middle = (left + right) / 2
direction = cmp(value, seq[middle])
if direction < 0:
right = middle - 1
elif direction == 0:
return middle
else:
left = middle + 1
return None
def find_first_ge(seq, value, cmp=__builtin__.cmp):
left = 0
right = len(seq)
while left < right:
middle = (left + right) / 2
direction = cmp(value, seq[middle])
if direction <= 0:
right = middle
else:
left = middle + 1
if left == len(seq):
return None
else:
return right
def pairwise(iterable):
"""s -> (s0,s1), (s1,s2), (s2,s3), ..."""
a, b = itertools.tee(iterable)
for elem in b:
break
return itertools.izip(a, b)
def salient(seq, epsilon=0):
def helper(start, stop):
if stop - start < 2:
return
delta = 0
left, right = start, stop
if seq[start] <= seq[stop]:
max_index = start
for i in xrange(start + 1, stop + 1):
if seq[i] > seq[max_index]:
max_index = i
elif seq[max_index] - seq[i] > delta:
left, right = max_index, i
delta = seq[max_index] - seq[i]
if seq[start] >= seq[stop]:
min_index = start
for i in xrange(start + 1, stop + 1):
if seq[i] < seq[min_index]:
min_index = i
elif seq[i] - seq[min_index] > delta:
left, right = min_index, i
delta = seq[i] - seq[min_index]
if delta >= epsilon and (left != start or right != stop):
result.add(left)
result.add(right)
helper(start, left)
helper(left, right)
helper(right, stop)
result = set()
if len(seq):
result.add(0)
result.add(len(seq) - 1)
helper(0, len(seq) - 1)
return sorted(result)
def salient2(seq, epsilons):
def helper(start, stop):
if stop - start < 2:
return
delta = 0
left, right = start, stop
if seq[start] <= seq[stop]:
max_index = start
for i in xrange(start + 1, stop + 1):
if seq[i] > seq[max_index]:
max_index = i
elif seq[max_index] - seq[i] > delta:
left, right = max_index, i
delta = seq[max_index] - seq[i]
if seq[start] >= seq[stop]:
min_index = start
for i in xrange(start + 1, stop + 1):
if seq[i] < seq[min_index]:
min_index = i
elif seq[i] - seq[min_index] > delta:
left, right = min_index, i
delta = seq[i] - seq[min_index]
if delta >= epsilons[-1] and (left != start or right != stop):
for i, epsilon in enumerate(epsilons):
if delta < epsilon:
continue
if not left in result or result[left] > i:
result[left] = i
if not right in result or result[right] > i:
result[right] = i
helper(start, left)
helper(left, right)
helper(right, stop)
result = {}
if len(seq):
result[0] = 0
result[len(seq) - 1] = 0
helper(0, len(seq) - 1)
return result.items()
def datetime_floor(dt, delta):
if delta.seconds >= 3600:
return dt.replace(minute=0, second=0) \
- datetime.timedelta(0, 3600 * (dt.hour
% int(delta.seconds / 3600)))
elif delta.seconds >= 60:
return dt.replace(second=0) \
- datetime.timedelta(0, 60 * (dt.minute
% int(delta.seconds / 60)))
elif delta.seconds >= 1:
return dt - datetime.timedelta(0, dt.second % delta.seconds)
else:
return dt