Source code for IMTreatment.core.temporalpoints

# -*- coding: utf-8 -*-
#!/bin/env python3

# Copyright (C) 2003-2007 Gaby Launay

# Author: Gaby Launay  <gaby.launay@tutanota.com>
# URL: https://framagit.org/gabylaunay/IMTreatment
# Version: 1.0

# This file is part of IMTreatment.

# IMTreatment 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.

# IMTreatment 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/>.

from .. import plotlib as pplt
import numpy as np
import unum
import copy
from ..utils import make_unit
from ..utils.types import ARRAYTYPES, INTEGERTYPES, NUMBERTYPES, STRINGTYPES
from . import points as pts


[docs]class TemporalPoints(object): """ Class representing a set of time evolving points. """ def __init__(self): self.point_sets = [] self.__times = np.array([], dtype=float) self.__unit_times = make_unit("") self.unit_x = make_unit('') self.unit_y = make_unit('') self.unit_times = make_unit('') def __iter__(self): for i in np.arange(len(self.point_sets)): yield self.times[i], self.point_sets[i] def __len__(self): return len(self.point_sets) @property def unit_x(self): return self.__unit_x @unit_x.setter def unit_x(self, value): self.__unit_x = value for pt in self.point_sets: pt.unit_x = value @property def unit_y(self): return self.__unit_y @unit_y.setter def unit_y(self, value): self.__unit_y = value for pt in self.point_sets: pt.unit_y = value @property def times(self): return self.__times @times.setter def times(self, values): if not isinstance(values, ARRAYTYPES): raise TypeError() if len(self.point_sets) != len(values): raise ValueError("New number of time ({}) do not corespond to " "the number of fields ({})" .format(len(values), len(self.point_sets))) self.__times = values @times.deleter def times(self): raise Exception("Nope, can't do that") @property def dt(self): return self.times[1] - self.times[0] @property def unit_times(self): return self.__unit_times @unit_times.setter def unit_times(self, new_unit_times): if isinstance(new_unit_times, unum.Unum): if new_unit_times.asNumber() == 1: self.__unit_times = new_unit_times else: raise ValueError() elif isinstance(new_unit_times, STRINGTYPES): self.__unit_times == make_unit(new_unit_times) else: raise TypeError() @unit_times.deleter def unit_times(self): raise Exception("Nope, can't do that")
[docs] def scale(self, scalex=None, scaley=None, scalev=None, scalet=None, inplace=False): """ Scale the point sets. Parameters ---------- scalex, scaley, scalev : numbers or Unum objects Scale for the axis and the values. inplace : boolean . """ if inplace: tmp_ps = self else: tmp_ps = self.copy() for i in range(len(self.point_sets)): tmp_ps.point_sets[i].scale(scalex=scalex, scaley=scaley, scalev=scalev, inplace=True) # scale the time if scalet is None: pass elif isinstance(scalet, NUMBERTYPES): tmp_ps.times *= scalet elif isinstance(scalet, unum.Unum): new_unit = tmp_ps.unit_times*scalet fact = new_unit.asNumber() new_unit /= fact tmp_ps.unit_times = new_unit tmp_ps.times *= fact else: raise TypeError() # returning return tmp_ps
[docs] def change_unit(self, axe, new_unit): """ Change the unit of an axe. Parameters ---------- axe : string 'y' for changing the profile y axis unit 'x' for changing the profile x axis unit 'values' for changing values unit 'time' for changing time unit new_unit : Unum.unit object or string The new unit. """ if isinstance(new_unit, STRINGTYPES): new_unit = make_unit(new_unit) if not isinstance(new_unit, unum.Unum): raise TypeError() if not isinstance(axe, STRINGTYPES): raise TypeError() if axe in ['x', 'y', 'values']: for pt in self.point_sets: pt.change_unit(axe, new_unit) elif axe == 'time': old_unit = self.unit_times new_unit = old_unit.asUnit(new_unit) fact = new_unit.asNumber() self.times *= fact self.unit_times = new_unit/fact else: raise ValueError()
[docs] def add_pts(self, pt, time=0., unit_times=""): """ Add a Pointsobject to the existing point set. Parameters ---------- pt : Point object The points to add. time : number time associated to the field. unit_time : Unum object time unit. """ if not isinstance(pt, pts.Points): raise TypeError() if not isinstance(time, NUMBERTYPES): raise TypeError("'time' should be a number, not {}" .format(type(time))) if isinstance(unit_times, unum.Unum): if unit_times.asNumber() != 1: raise ValueError() elif isinstance(unit_times, STRINGTYPES): unit_times = make_unit(unit_times) else: raise TypeError() # if this is the first field if len(self.point_sets) == 0: self.unit_x = pt.unit_x self.unit_y = pt.unit_y self.unit_times = unit_times self.__times = np.asarray([time], dtype=float) # if not else: # storing time time = (time*self.unit_times/unit_times).asNumber() self.__times = np.append(self.__times, time) # use default constructor self.point_sets.append(pt) # sorting the field with time self.__sort_field_by_time()
[docs] def remove_pts(self, indice): """ Remove points of the existing point set. Parameters ---------- indice : integer or list of integers Point indice(s) to remove. """ if isinstance(indice, INTEGERTYPES): indice = [indice] for i in indice: self.__times = np.delete(self.times, i) self.point_sets = np.delete(self.point_sets, i)
[docs] def crop(self, intervx=None, intervy=None, intervv=None, intervt=None, full_output=False, ind=False, inplace=False): """ Return a croped field in respect with given intervals. Parameters ---------- intervx : array, optional interval wanted along x intervy : array, optional interval wanted along y intervv : array, optional interval wanted along v intervt : array, optional interval wanted along time full_output : boolean, optional If 'True', cutting indices are also returned inplace : boolean, optional If 'True', fields are croped in place. """ # check parameters if intervt is not None: if not isinstance(intervt, ARRAYTYPES): raise TypeError() intervt = np.array(intervt, dtype=float) if intervt.shape != (2, ): raise ValueError() # get wanted times if intervt is not None: if not ind: if intervt[0] < self.times[0]: ind1 = 0 elif intervt[0] > self.times[-1]: raise ValueError() else: ind1 = np.where(intervt[0] <= self.times)[0][0] if intervt[1] > self.times[-1]: ind2 = len(self.times) - 1 elif intervt[1] < self.times[0]: raise ValueError() else: ind2 = np.where(intervt[1] >= self.times)[0][-1] intervt = [ind1, ind2] # crop if inplace: croppts = self else: croppts = self.copy() # temporal if intervt is not None: croppts.point_sets = croppts.point_sets[intervt[0]:intervt[1] + 1] croppts.times = croppts.times[intervt[0]:intervt[1] + 1] # spatial for pt in self.point_sets: pt.crop(intervx=intervx, intervy=intervy, intervv=intervv, inplace=True) # returning return croppts
[docs] def smooth(self, tos='gaussian', size=None, inplace=False, **kw): """ Return a smoothed points field set. Parameters ---------- tos : string, optional Type of smoothing, can be 'uniform', 'gaussian' (default) or 'lowpass'. size : number, optional radius of the smoothing for 'uniform', radius of the smoothing for 'gaussian', cut off frequency for 'lowpass' Default are 3 for 'uniform', 1 for 'gaussian' and 0.1 for ' lowpass'. inplace : boolean If 'False', return a smoothed points field else, smooth in place. kw : dic Additional parameters for ndimage methods (See ndimage documentation) """ if inplace: tmp_pts = self else: tmp_pts = self.copy() for pt in tmp_pts.point_sets: pt.smooth(tos=tos, size=size, inplace=True, **kw) return tmp_pts
[docs] def copy(self): """ Return a copy of the velocityfields """ return copy.deepcopy(self)
def __sort_field_by_time(self): if len(self.point_sets) in [0, 1]: return None ind_sort = np.argsort(self.times) self.times = self.times[ind_sort] self.point_sets = [self.point_sets[i] for i in ind_sort]
[docs] def display(self, sharecb=True, buffer_size=100, cpkw={}): """ Create a windows to display temporals point sets, controlled by buttons. Parameters ---------- sharecb: boolean Do all the vector field serie has to share the same colorbar or not. buffer_size: number Number of displays to keep in memory (faster, but use memory). cpkw: dict Drawing argument for points Display control --------------- The display can be controlled using the button, but also the keyboard: space, right arrow or + : next field backspace, left arrow or - : previous field up arrow : last field down arrow : first field number + enter : goto a specific frame p : play the animated fields number + i : set the animation increment number + t : set the animation time interval (ms) q : close s : save an image """ # get data x = [list(pt.xy[:, 0]) for pt in self.point_sets] y = [list(pt.xy[:, 1]) for pt in self.point_sets] # check if there is data if len(np.concatenate(x)) == 0: return None # default args args = {'kind': 'scatter', 'marker': 'o', 'color': 'k'} if 'kind' in list(cpkw.keys()): if cpkw['kind'] != 'plot': args = {} args.update(cpkw) db = pplt.Displayer(x, y, buffer_size=buffer_size, **args) # plot if len(self.times) == 1: db.draw(0, rescale=False) else: pplt.ButtonManager(db) return db