class RadarDisplay(object): _increms = {'left': -1, 'right': 1} def do_save_results(self): return self._do_save def __init__(self, volume, tracks, falarms, polygons, radarFiles): """ Create an interactive display for creating track data from radar data. """ minLat, minLon, maxLat, maxLon, volTimes = ConsistentDomain(radarFiles) self.fig = plt.figure() self.ax = self.fig.gca() self.radarData = RadarCache(radarFiles, 4) self._increm_funcs = { 'left': self.radarData.prev, 'right': self.radarData.next } self._im = None self._curr_selection = None self._alphaScale = 1.0 self.curr_lasso = None self.volTimes = volTimes self.state = StateManager(volTimes) self.state.load_features(tracks, falarms, volume, polygons) for feats in self.state._features.values(): for feat in feats: for obj in feat.objects.values(): if obj is not None: obj.set_visible(False) self.ax.add_artist(obj) for track in self.state._tracks: track.obj.set_visible(True) self.ax.add_artist(track.obj) self.frameIndex = 0 self._show_features = False data = self.radarData.next() radarName = data['station'] if radarName == 'NWRT': radarName = 'PAR' radarSite = radarsites.ByName(radarName)[0] lons, lats = np.meshgrid(data['lons'], data['lats']) self.xs, self.ys = LonLat2Cart(radarSite['LON'], radarSite['LAT'], lons, lats) self.update_frame() self.ax.set_xlim(self.xs.min(), self.xs.max()) self.ax.set_ylim(self.ys.min(), self.ys.max()) self.ax.set_xlabel('X (km)') self.ax.set_ylabel('Y (km)') self.ax.set_aspect('equal') self.fig.canvas.mpl_connect('key_press_event', self.process_key) #self.fig.canvas.mpl_connect('button_release_event', # self.process_click) self.fig.canvas.mpl_connect('button_press_event', self.onpress) #self.fig.canvas.mpl_connect('pick_event', self.onpick) #self._savefeats_cid = self.fig.canvas.mpl_connect('close_event', # self.onclose) self._do_save = True # Start in outline mode self._mode = 'o' # Need to remove some keys... rcParams['keymap.fullscreen'] = [] rcParams['keymap.home'].remove('r') rcParams['keymap.home'].remove('h') rcParams['keymap.back'].remove('left') rcParams['keymap.forward'].remove('right') rcParams['keymap.forward'].remove('v') rcParams['keymap.zoom'] = [] rcParams['keymap.save'] = [] print "Welcome to Track Maker! (press 'h' for menu of options)" def onpick(self, event): """ Track picker handler """ pass #def onclose(self, event) : # """ # Trigger a saving of data (Note: this isn't a saving to files, # but to the track lists and volume lists). # """ # self.save_features() def onlasso(self, verts): """ Creation of the contour polygon, which selects the initial region for watershed clustering. """ newPoly = Polygon(verts, lw=2, fc='gray', hatch='/', zorder=1, ec=Feature.orig_colors['contour'], alpha=Feature.orig_alphas['contour'], picker=None) self.state.add_feature(Feature(self.frameIndex, contour=newPoly)) self.fig.canvas.draw_idle() self.fig.canvas.widgetlock.release(self.curr_lasso) del self.curr_lasso self.curr_lasso = None self.ax.add_artist(newPoly) def onpress(self, event): """ Button-press handler """ if self._mode == 'o': # Outline mode if self.fig.canvas.widgetlock.locked(): return if event.inaxes is not self.ax: return self.curr_lasso = Lasso(event.inaxes, (event.xdata, event.ydata), self.onlasso) # Set a lock on drawing the lasso until finished self.fig.canvas.widgetlock(self.curr_lasso) elif self._mode == 's': # Selection mode if event.inaxes is not self.ax: return curr_select = None for feat in self.state._features[self.frameIndex]: if feat.contains(event): curr_select = feat prev_select = self._curr_selection if curr_select is not None: if prev_select is not None: # TODO: Make a tracking association # TODO: May need some mapping of frameIndex to frameNum # This is only valid if I am able to see the previous # selection. This logic also makes it impossible to # have feat1 be the same object as feat2 if (prev_select.get_visible() and self.frameIndex != prev_select.frame): # We are making associations across frames! tmp = self.state.associate_features( prev_select, curr_select) if tmp is not None: self.ax.add_artist(tmp.obj) prev_select.deselect() if prev_select is curr_select: self._curr_selection = None else: self._curr_selection = curr_select curr_select.select() self.fig.canvas.draw_idle() def process_key(self, event): """ Key-press handler """ if event.key in RadarDisplay._increms: if (0 <= (self.frameIndex + RadarDisplay._increms[event.key]) <= (len(self.radarData) - 1)): lastFrame = self.frameIndex self.frameIndex += RadarDisplay._increms[event.key] # Update the radar data self._increm_funcs[event.key]() #if self._curr_selection is not None : # self._curr_selection.deselect() # self._curr_selection = None # Update the frame self.update_frame(lastFrame, hold_recluster=True) elif event.key == '+': self._alphaScale = min(100.0, self._alphaScale * 2) self.update_frame(hold_recluster=True) elif event.key == '-': self._alphaScale = max(0.001, self._alphaScale / 2.0) self.update_frame(hold_recluster=True) elif event.key == 'r': # Recalculate ellipsoids self.update_frame(force_recluster=True, hold_recluster=False) elif event.key == 'c': # Completely remove the features for this frame if (self._curr_selection is not None and self._curr_selection.frame == self.frameIndex): self._curr_selection.deselect() self._curr_selection = None self.state.clear_frame(self.frameIndex) self.update_frame() elif event.key == 'd': # Delete the currently selected artist (if in the current frame) if (self._curr_selection is not None and self._curr_selection.frame == self.frameIndex): self._curr_selection.deselect() self._curr_selection.remove() self.state.remove_feature(self._curr_selection) self._curr_selection = None self.fig.canvas.draw_idle() elif event.key == 's': # set mode to "selection mode" self._mode = 's' # Just in case the canvas is still locked. if self.curr_lasso is not None: self.fig.canvas.widgetlock.release(self.curr_lasso) del self.curr_lasso self.curr_lasso = None print "Selection Mode" elif event.key == 'o': # set mode to "outline mode" self._mode = 'o' print "Outline Mode" elif event.key == 'f': # Show/Hide all identified features across time self._show_features = (not self._show_features) print "Show features:", self._show_features self.update_frame(hold_recluster=True) elif event.key == 'v': # Toogle save self._do_save = (not self._do_save) print "Do Save:", self._do_save #if not self._do_save : # if self._savefeats_cid is not None : # self.fig.canvas.mpl_disconnect(self._savefeats_cid) # self._savefeats_cid = None #else : # if self._savefeats_cid is None : # self._savefeats_cid = self.fig.canvas.mpl_connect( # "close_event", self.onclose) #elif event.key == 'V' : # # Save features to memory NOW! # print "Converting to track and volume objects, NOW!" # self.save_features() elif event.key == 'h': # Print helpful Menu print dedent(""" Track Maker =========== Key Action ------ ----------------------------- h Show this helpful menu right Step forward by one frame left Step back by one frame +, - Rescale transparency as a function of time o Outline mode s Selection mode r (re)cluster this frame c clear this frame of existing features d delete the currently selected feature s show/hide all features across all time v toggle saving features upon closing figure (this is useful if you detect an error and do not want to save bad data). Current Values -------------- Current Frame: %d of %d Current Mode: %s Do save upon figure close: %s Show all features: %s """ % (self.frameIndex + 1, len(self.radarData), self._mode, self._do_save, self._show_features)) def _clear_frame(self, frame=None): if frame is None: frame = self.frameIndex # Set the frame's features to invisible for feat in self.state._features[frame]: feat.set_visible(False) # Also reset their alpha values feat.set_alpha(1.0) #feat.set_picker(None) def get_clusters(self): dataset = self.radarData.curr() data = dataset['vals'][0] flat_data = data[data >= -40] clustLabels = np.empty(data.shape, dtype=int) clustLabels[:] = -1 if np.nanmin(flat_data) == np.nanmax(flat_data): # can't cluster data with no change return clustLabels, 0 bad_data = (np.isnan(data) | (data <= 0.0)) bins = np.linspace(np.nanmin(flat_data), np.nanmax(flat_data), 2**8) data_digitized = np.digitize(data.flat, bins) data_digitized.shape = data.shape data_digitized = data_digitized.astype('uint8') markers = np.zeros(data.shape, dtype=int) for index, feat in enumerate(self.state._features[self.frameIndex]): if 'contour' in feat.objects: contr = feat.objects['contour'] res = points_inside_poly(zip(self.xs.flat, self.ys.flat), contr.get_xy()) res.shape = self.xs.shape markers[res] = index + 1 # No contour available? Then fall back to just a point elif 'center' in feat.objects: cent = feat.objects['center'] gridx, gridy = self._xy2grid(cent.center[0], cent.center[1]) markers[gridy, gridx] = index + 1 # TODO: work from an ellipse, if it exists? else: raise ValueError("Empty feature?") markers[bad_data] = -1 ndimg.watershed_ift(data_digitized, markers, output=clustLabels) clustCnt = len(self.state._features[self.frameIndex]) cents = ndimg.center_of_mass(data**2, clustLabels, range(1, clustCnt + 1)) ellipses = FitEllipses(clustLabels, range(1, clustCnt + 1), self.xs, self.ys) for center, ellip, feat in zip(cents, ellipses, self.state._features[self.frameIndex]): # Remove any other objects that may exist before adding # new objects to the feature. feat.cleanup(['contour']) if ellip is None: continue cent_indx = tuple(np.floor(center).astype(int).tolist()) # TODO: clean this up! newPoint = self.state._new_point(self.xs[cent_indx], self.ys[cent_indx]) self.ax.add_artist(ellip) self.ax.add_artist(newPoint) feat.objects['center'] = newPoint feat.objects['ellip'] = ellip if feat.track is not None: feat.track.update_frame(self.frameIndex) #print "clust count:", clustCnt return clustLabels, clustCnt def _xy2grid(self, x, y): return (self.xs[0].searchsorted(x), self.ys[:, 0].searchsorted(y)) def update_frame(self, lastFrame=None, force_recluster=False, hold_recluster=False): """ Redraw the current frame. Calculate clusters if needed. *lastFrame* int (None) If specified, make this frame's features invisible. *force_recluster* boolean (False) If True, do a recluster, even if it seems like it isn't needed. Can be over-ridden by *hold_recluster*. *hold_recluster* boolean (False) If True, then don't do a recluster, even if needed or *force_recluster* is True. """ if lastFrame is not None: self._clear_frame(lastFrame) data = self.radarData.curr() # Display current frame's radar image if self._im is None: self._im = MakeReflectPPI(data['vals'][0], self.ys, self.xs, meth='pcmesh', ax=self.ax, colorbar=False, axis_labels=False, zorder=0, mask=False) else: self._im.set_array(data['vals'][0, :-1, :-1].flatten()) if force_recluster or any( [('center' not in feat.objects) for feat in self.state._features[self.frameIndex]]): if not hold_recluster: clustLabels, clustCnt = self.get_clusters() # Set features for this frame to visible for feat in self.state._features[self.frameIndex]: feat.set_visible(True) # Return alpha back to normal feat.set_alpha(1.0) # Put it on top feat.set_zorder(len(self.radarData)) #feat.set_picker(True) # Show the other features if self._show_features: # How much alpha should change for each frame from frameIndex # The closer to self.frameIndex, the more opaque alphaIncrem = 1.0 / len(self.radarData) for frameIndex, features in self.state._features.iteritems(): if frameIndex == self.frameIndex: continue framesFrom = np.abs(self.frameIndex - frameIndex) timeAlpha = ((1.0 - (framesFrom * alphaIncrem))**(1.0 / self._alphaScale)) zorder = len(self.radarData) - framesFrom for feat in features: feat.set_visible(True) feat.set_alpha(timeAlpha) feat.set_zorder(zorder) else: # Make sure that these items are hidden for frameIndex, features in self.state._features.iteritems(): if frameIndex != self.frameIndex: for feat in features: feat.set_visible(False) # Return alpha to normal feat.set_alpha(1.0) if self.volTimes[self.frameIndex] is None: theDateTime = datetime.utcfromtimestamp(data['scan_time']) self.volTimes[self.frameIndex] = theDateTime else: theDateTime = self.volTimes[self.frameIndex] self.ax.set_title(theDateTime.strftime("%Y/%m/%d %H:%M:%S")) self.fig.canvas.draw_idle()
def main(args): if args.bw_mode: BW_mode() if len(args.trackFiles) == 0: print "WARNING: No trackFiles given!" if args.trackTitles is None: args.trackTitles = args.trackFiles else: if len(args.trackTitles) != len(args.trackFiles): raise ValueError("The number of TITLEs do not match the" " number of TRACKFILEs.") if args.statName is not None and args.statLonLat is None: statData = ByName(args.statName)[0] args.statLonLat = (statData['LON'], statData['LAT']) if args.layout is None: args.layout = (1, len(args.trackFiles)) if args.figsize is None: args.figsize = plt.figaspect(float(args.layout[0]) / args.layout[1]) trackerData = [ FilterMHTTracks(*ReadTracks(trackFile)) for trackFile in args.trackFiles ] polyData = [ _load_verts(f, tracks + falarms) for f, (tracks, falarms) in zip(polyfiles, trackerData) ] if args.statLonLat is not None: for aTracker in trackerData: CoordinateTransform(aTracker[0] + aTracker[1], args.statLonLat[0], args.statLonLat[1]) for polys in polyData: CoordinateTrans_lists(polys, args.statLonLat[0], args.statLonLat[1]) if args.simTagFiles is None: args.simTagFiles = [None] multiTags = [ ReadSimTagFile(fname) if fname is not None else None for fname in args.simTagFiles ] if len(trackerData) > len(multiTags): # Very rudimentary broadcasting of multiTags to match trackerData tagMult = max(int(len(trackerData) // len(multiTags)), 1) multiTags = multiTags * tagMult theFig = plt.figure(figsize=args.figsize) grid = AxesGrid(theFig, 111, nrows_ncols=args.layout, aspect=False, share_all=True, axes_pad=0.45) showMap = (args.statLonLat is not None and args.displayMap) # Can only do this if all other data being displayed will be in # lon/lat coordinates if args.radarFile is not None and args.statLonLat is not None: if len(args.radarFile) > 1 and args.endFrame is not None: args.radarFile = args.radarFile[args.endFrame] else: args.radarFile = args.radarFile[-1] data = LoadRastRadar(args.radarFile) for ax in grid: MakeReflectPPI(data['vals'][0], data['lats'], data['lons'], meth='pcmesh', ax=ax, colorbar=False, axis_labels=False, zorder=0, alpha=0.6) MakeTrackPlots(grid, trackerData, args.trackTitles, showMap, endFrame=args.endFrame, tail=args.tail, fade=args.fade, multiTags=multiTags, tag_filters=args.filters) for ax, verts in zip(grid, polyData): _to_polygons(verts[args.endFrame:args.endFrame + 1], ax) if args.xlims is not None and np.prod(grid.get_geometry()) > 0: grid[0].set_xlim(args.xlims) if args.ylims is not None and np.prod(grid.get_geometry()) > 0: grid[0].set_ylim(args.ylims) if args.saveImgFile is not None: theFig.savefig(args.saveImgFile) if args.doShow: plt.show()
def update_frame(self, lastFrame=None, force_recluster=False, hold_recluster=False): """ Redraw the current frame. Calculate clusters if needed. *lastFrame* int (None) If specified, make this frame's features invisible. *force_recluster* boolean (False) If True, do a recluster, even if it seems like it isn't needed. Can be over-ridden by *hold_recluster*. *hold_recluster* boolean (False) If True, then don't do a recluster, even if needed or *force_recluster* is True. """ if lastFrame is not None: self._clear_frame(lastFrame) data = self.radarData.curr() # Display current frame's radar image if self._im is None: self._im = MakeReflectPPI(data['vals'][0], self.ys, self.xs, meth='pcmesh', ax=self.ax, colorbar=False, axis_labels=False, zorder=0, mask=False) else: self._im.set_array(data['vals'][0, :-1, :-1].flatten()) if force_recluster or any( [('center' not in feat.objects) for feat in self.state._features[self.frameIndex]]): if not hold_recluster: clustLabels, clustCnt = self.get_clusters() # Set features for this frame to visible for feat in self.state._features[self.frameIndex]: feat.set_visible(True) # Return alpha back to normal feat.set_alpha(1.0) # Put it on top feat.set_zorder(len(self.radarData)) #feat.set_picker(True) # Show the other features if self._show_features: # How much alpha should change for each frame from frameIndex # The closer to self.frameIndex, the more opaque alphaIncrem = 1.0 / len(self.radarData) for frameIndex, features in self.state._features.iteritems(): if frameIndex == self.frameIndex: continue framesFrom = np.abs(self.frameIndex - frameIndex) timeAlpha = ((1.0 - (framesFrom * alphaIncrem))**(1.0 / self._alphaScale)) zorder = len(self.radarData) - framesFrom for feat in features: feat.set_visible(True) feat.set_alpha(timeAlpha) feat.set_zorder(zorder) else: # Make sure that these items are hidden for frameIndex, features in self.state._features.iteritems(): if frameIndex != self.frameIndex: for feat in features: feat.set_visible(False) # Return alpha to normal feat.set_alpha(1.0) if self.volTimes[self.frameIndex] is None: theDateTime = datetime.utcfromtimestamp(data['scan_time']) self.volTimes[self.frameIndex] = theDateTime else: theDateTime = self.volTimes[self.frameIndex] self.ax.set_title(theDateTime.strftime("%Y/%m/%d %H:%M:%S")) self.fig.canvas.draw_idle()
def main(args): import os.path import matplotlib.pyplot as plt from mpl_toolkits.axes_grid1 import AxesGrid if args.bw_mode: BW_mode() # from TrackPlot module # FIXME: Currently, the code allows for trackFiles to be listed as well # as providing a simulation (which trackfiles are automatically # grabbed). Both situations can not be handled right now, though. trackFiles = [] trackTitles = [] if args.simName is not None: dirName = os.path.join(args.directory, args.simName) simParams = ParamUtils.ReadSimulationParams( os.path.join(dirName, "simParams.conf")) if args.trackRuns is not None: simParams['trackers'] = ExpandTrackRuns(simParams['trackers'], args.trackRuns) trackFiles = [ os.path.join(dirName, simParams['result_file'] + '_' + aTracker) for aTracker in simParams['trackers'] ] if args.trackTitles is None: trackTitles = simParams['trackers'] else: trackTitles = args.trackTitles if args.truthTrackFile is None: args.truthTrackFile = os.path.join(dirName, simParams['noisyTrackFile']) if args.simTagFile is None: args.simTagFile = os.path.join(dirName, simParams['simTagFile']) trackFiles += args.trackFiles if args.trackTitles is None: trackTitles += args.trackFiles else: trackTitles += args.trackTitles if len(trackFiles) == 0: print "WARNING: No trackFiles given or found!" if len(trackTitles) != len(trackFiles): raise ValueError("The number of TITLEs do not match the" " number of TRACKFILEs.") if args.statName is not None and args.statLonLat is None: statData = ByName(args.statName)[0] args.statLonLat = (statData['LON'], statData['LAT']) if args.layout is None: args.layout = (1, len(trackFiles)) if args.figsize is None: args.figsize = plt.figaspect(float(args.layout[0]) / args.layout[1]) polyfiles = args.polys trackerData = [ FilterMHTTracks(*ReadTracks(trackFile)) for trackFile in trackFiles ] polyData = [ _load_verts(f, tracks + falarms) for f, (tracks, falarms) in zip(polyfiles, trackerData) ] keeperIDs = None if args.simTagFile is not None: simTags = ParamUtils.ReadSimTagFile(args.simTagFile) keeperIDs = ParamUtils.process_tag_filters(simTags, args.filters) if args.statLonLat is not None: for aTracker in trackerData: CoordinateTransform(aTracker[0] + aTracker[1], args.statLonLat[0], args.statLonLat[1]) for polys in polyData: CoordinateTrans_lists(polys, args.statLonLat[0], args.statLonLat[1]) theFig = plt.figure(figsize=args.figsize) grid = AxesGrid(theFig, 111, nrows_ncols=args.layout, aspect=False, share_all=True, axes_pad=0.35) if args.truthTrackFile is not None: (true_tracks, true_falarms) = FilterMHTTracks(*ReadTracks(args.truthTrackFile)) if args.statLonLat is not None: CoordinateTransform(true_tracks + true_falarms, args.statLonLat[0], args.statLonLat[1]) true_AssocSegs = CreateSegments(true_tracks) true_FAlarmSegs = CreateSegments(true_falarms) if keeperIDs is not None: true_AssocSegs = FilterSegments(keeperIDs, true_AssocSegs) true_FAlarmSegs = FilterSegments(keeperIDs, true_FAlarmSegs) (xLims, yLims, frameLims) = DomainFromTracks(true_tracks + true_falarms) else: true_AssocSegs = None true_FAlarmSegs = None stackedTracks = [] for aTracker in trackerData: stackedTracks += aTracker[0] + aTracker[1] (xLims, yLims, frameLims) = DomainFromTracks(stackedTracks) endFrame = args.endFrame tail = args.tail if endFrame is None: endFrame = frameLims[1] if tail is None: tail = endFrame - frameLims[0] startFrame = endFrame - tail showMap = (args.statLonLat is not None and args.displayMap) if args.radarFile is not None and args.statLonLat is not None: if len(args.radarFile) > 1 and args.endFrame is not None: args.radarFile = args.radarFile[args.endFrame] else: args.radarFile = args.radarFile[-1] raddata = LoadRastRadar(args.radarFile) else: raddata = None if showMap: bmap = Basemap(projection='cyl', resolution='i', suppress_ticks=False, llcrnrlat=yLims[0], llcrnrlon=xLims[0], urcrnrlat=yLims[1], urcrnrlon=xLims[1]) for index, (tracks, falarms) in enumerate(trackerData): curAxis = grid[index] if raddata is not None: MakeReflectPPI(raddata['vals'][0], raddata['lats'], raddata['lons'], meth='pcmesh', ax=curAxis, colorbar=False, axis_labels=False, zorder=0, alpha=0.6) if showMap: PlotMapLayers(bmap, mapLayers, curAxis) if true_AssocSegs is not None and true_FAlarmSegs is not None: trackAssocSegs = CreateSegments(tracks) trackFAlarmSegs = CreateSegments(falarms) if keeperIDs is not None: trackAssocSegs = FilterSegments(keeperIDs, trackAssocSegs) trackFAlarmSegs = FilterSegments(keeperIDs, trackFAlarmSegs) truthtable = CompareSegments(true_AssocSegs, true_FAlarmSegs, trackAssocSegs, trackFAlarmSegs) PlotSegments(truthtable, (startFrame, endFrame), axis=curAxis, fade=args.fade) else: if keeperIDs is not None: filtFunc = lambda trk: FilterTrack(trk, cornerIDs=keeperIDs) tracks = map(filtFunc, tracks) falarms = map(filtFunc, falarms) CleanupTracks(tracks, falarms) PlotPlainTracks(tracks, falarms, startFrame, endFrame, axis=curAxis, fade=args.fade) #curAxis.set_xlim(xLims) #curAxis.set_ylim(yLims) #curAxis.set_aspect("equal", 'datalim') #curAxis.set_aspect("equal") curAxis.set_title(trackTitles[index]) if not showMap: curAxis.set_xlabel("X") curAxis.set_ylabel("Y") else: curAxis.set_xlabel("Longitude") curAxis.set_ylabel("Latitude") for ax, verts in zip(grid, polyData): _to_polygons(verts[endFrame:endFrame + 1], ax) if args.xlims is not None and np.prod(grid.get_geometry()) > 0: grid[0].set_xlim(args.xlims) if args.ylims is not None and np.prod(grid.get_geometry()) > 0: grid[0].set_ylim(args.ylims) if args.saveImgFile is not None: theFig.savefig(args.saveImgFile, bbox_inches='tight') if args.doShow: plt.show()
class RadarDisplay(object) : _increms = {'left': -1, 'right': 1} def do_save_results(self) : return self._do_save def __init__(self, volume, tracks, falarms, polygons, radarFiles) : """ Create an interactive display for creating track data from radar data. """ minLat, minLon, maxLat, maxLon, volTimes = ConsistentDomain(radarFiles) self.fig = plt.figure() self.ax = self.fig.gca() self.radarData = RadarCache(radarFiles, 4) self._increm_funcs = {'left': self.radarData.prev, 'right': self.radarData.next} self._im = None self._curr_selection = None self._alphaScale = 1.0 self.curr_lasso = None self.volTimes = volTimes self.state = StateManager(volTimes) self.state.load_features(tracks, falarms, volume, polygons) for feats in self.state._features.values() : for feat in feats : for obj in feat.objects.values() : if obj is not None : obj.set_visible(False) self.ax.add_artist(obj) for track in self.state._tracks : track.obj.set_visible(True) self.ax.add_artist(track.obj) self.frameIndex = 0 self._show_features = False data = self.radarData.next() radarName = data['station'] if radarName == 'NWRT' : radarName = 'PAR' radarSite = radarsites.ByName(radarName)[0] lons, lats = np.meshgrid(data['lons'], data['lats']) self.xs, self.ys = LonLat2Cart(radarSite['LON'], radarSite['LAT'], lons, lats) self.update_frame() self.ax.set_xlim(self.xs.min(), self.xs.max()) self.ax.set_ylim(self.ys.min(), self.ys.max()) self.ax.set_xlabel('X (km)') self.ax.set_ylabel('Y (km)') self.ax.set_aspect('equal') self.fig.canvas.mpl_connect('key_press_event', self.process_key) #self.fig.canvas.mpl_connect('button_release_event', # self.process_click) self.fig.canvas.mpl_connect('button_press_event', self.onpress) #self.fig.canvas.mpl_connect('pick_event', self.onpick) #self._savefeats_cid = self.fig.canvas.mpl_connect('close_event', # self.onclose) self._do_save = True # Start in outline mode self._mode = 'o' # Need to remove some keys... rcParams['keymap.fullscreen'] = [] rcParams['keymap.home'].remove('r') rcParams['keymap.home'].remove('h') rcParams['keymap.back'].remove('left') rcParams['keymap.forward'].remove('right') rcParams['keymap.forward'].remove('v') rcParams['keymap.zoom'] = [] rcParams['keymap.save'] = [] print "Welcome to Track Maker! (press 'h' for menu of options)" def onpick(self, event) : """ Track picker handler """ pass #def onclose(self, event) : # """ # Trigger a saving of data (Note: this isn't a saving to files, # but to the track lists and volume lists). # """ # self.save_features() def onlasso(self, verts) : """ Creation of the contour polygon, which selects the initial region for watershed clustering. """ newPoly = Polygon(verts, lw=2, fc='gray', hatch='/', zorder=1, ec=Feature.orig_colors['contour'], alpha=Feature.orig_alphas['contour'], picker=None) self.state.add_feature(Feature(self.frameIndex, contour=newPoly)) self.fig.canvas.draw_idle() self.fig.canvas.widgetlock.release(self.curr_lasso) del self.curr_lasso self.curr_lasso = None self.ax.add_artist(newPoly) def onpress(self, event) : """ Button-press handler """ if self._mode == 'o' : # Outline mode if self.fig.canvas.widgetlock.locked() : return if event.inaxes is not self.ax : return self.curr_lasso = Lasso(event.inaxes, (event.xdata, event.ydata), self.onlasso) # Set a lock on drawing the lasso until finished self.fig.canvas.widgetlock(self.curr_lasso) elif self._mode == 's': # Selection mode if event.inaxes is not self.ax : return curr_select = None for feat in self.state._features[self.frameIndex] : if feat.contains(event) : curr_select = feat prev_select = self._curr_selection if curr_select is not None : if prev_select is not None : # TODO: Make a tracking association # TODO: May need some mapping of frameIndex to frameNum # This is only valid if I am able to see the previous # selection. This logic also makes it impossible to # have feat1 be the same object as feat2 if (prev_select.get_visible() and self.frameIndex != prev_select.frame) : # We are making associations across frames! tmp = self.state.associate_features(prev_select, curr_select) if tmp is not None : self.ax.add_artist(tmp.obj) prev_select.deselect() if prev_select is curr_select : self._curr_selection = None else : self._curr_selection = curr_select curr_select.select() self.fig.canvas.draw_idle() def process_key(self, event) : """ Key-press handler """ if event.key in RadarDisplay._increms : if (0 <= (self.frameIndex + RadarDisplay._increms[event.key]) <= (len(self.radarData) - 1)) : lastFrame = self.frameIndex self.frameIndex += RadarDisplay._increms[event.key] # Update the radar data self._increm_funcs[event.key]() #if self._curr_selection is not None : # self._curr_selection.deselect() # self._curr_selection = None # Update the frame self.update_frame(lastFrame, hold_recluster=True) elif event.key == '+' : self._alphaScale = min(100.0, self._alphaScale * 2) self.update_frame(hold_recluster=True) elif event.key == '-' : self._alphaScale = max(0.001, self._alphaScale / 2.0) self.update_frame(hold_recluster=True) elif event.key == 'r' : # Recalculate ellipsoids self.update_frame(force_recluster=True, hold_recluster=False) elif event.key == 'c' : # Completely remove the features for this frame if (self._curr_selection is not None and self._curr_selection.frame == self.frameIndex) : self._curr_selection.deselect() self._curr_selection = None self.state.clear_frame(self.frameIndex) self.update_frame() elif event.key == 'd' : # Delete the currently selected artist (if in the current frame) if (self._curr_selection is not None and self._curr_selection.frame == self.frameIndex) : self._curr_selection.deselect() self._curr_selection.remove() self.state.remove_feature(self._curr_selection) self._curr_selection = None self.fig.canvas.draw_idle() elif event.key == 's' : # set mode to "selection mode" self._mode = 's' # Just in case the canvas is still locked. if self.curr_lasso is not None : self.fig.canvas.widgetlock.release(self.curr_lasso) del self.curr_lasso self.curr_lasso = None print "Selection Mode" elif event.key == 'o' : # set mode to "outline mode" self._mode = 'o' print "Outline Mode" elif event.key == 'f' : # Show/Hide all identified features across time self._show_features = (not self._show_features) print "Show features:", self._show_features self.update_frame(hold_recluster=True) elif event.key == 'v' : # Toogle save self._do_save = (not self._do_save) print "Do Save:", self._do_save #if not self._do_save : # if self._savefeats_cid is not None : # self.fig.canvas.mpl_disconnect(self._savefeats_cid) # self._savefeats_cid = None #else : # if self._savefeats_cid is None : # self._savefeats_cid = self.fig.canvas.mpl_connect( # "close_event", self.onclose) #elif event.key == 'V' : # # Save features to memory NOW! # print "Converting to track and volume objects, NOW!" # self.save_features() elif event.key == 'h' : # Print helpful Menu print dedent(""" Track Maker =========== Key Action ------ ----------------------------- h Show this helpful menu right Step forward by one frame left Step back by one frame +, - Rescale transparency as a function of time o Outline mode s Selection mode r (re)cluster this frame c clear this frame of existing features d delete the currently selected feature s show/hide all features across all time v toggle saving features upon closing figure (this is useful if you detect an error and do not want to save bad data). Current Values -------------- Current Frame: %d of %d Current Mode: %s Do save upon figure close: %s Show all features: %s """ % (self.frameIndex + 1, len(self.radarData), self._mode, self._do_save, self._show_features)) def _clear_frame(self, frame=None) : if frame is None : frame = self.frameIndex # Set the frame's features to invisible for feat in self.state._features[frame] : feat.set_visible(False) # Also reset their alpha values feat.set_alpha(1.0) #feat.set_picker(None) def get_clusters(self) : dataset = self.radarData.curr() data = dataset['vals'][0] flat_data = data[data >= -40] clustLabels = np.empty(data.shape, dtype=int) clustLabels[:] = -1 if np.nanmin(flat_data) == np.nanmax(flat_data) : # can't cluster data with no change return clustLabels, 0 bad_data = (np.isnan(data) | (data <= 0.0)) bins = np.linspace(np.nanmin(flat_data), np.nanmax(flat_data), 2**8) data_digitized = np.digitize(data.flat, bins) data_digitized.shape = data.shape data_digitized = data_digitized.astype('uint8') markers = np.zeros(data.shape, dtype=int) for index, feat in enumerate(self.state._features[self.frameIndex]) : if 'contour' in feat.objects : contr = feat.objects['contour'] res = points_inside_poly(zip(self.xs.flat, self.ys.flat), contr.get_xy()) res.shape = self.xs.shape markers[res] = index + 1 # No contour available? Then fall back to just a point elif 'center' in feat.objects : cent = feat.objects['center'] gridx, gridy = self._xy2grid(cent.center[0], cent.center[1]) markers[gridy, gridx] = index + 1 # TODO: work from an ellipse, if it exists? else : raise ValueError("Empty feature?") markers[bad_data] = -1 ndimg.watershed_ift(data_digitized, markers, output=clustLabels) clustCnt = len(self.state._features[self.frameIndex]) cents = ndimg.center_of_mass(data**2, clustLabels, range(1, clustCnt + 1)) ellipses = FitEllipses(clustLabels, range(1, clustCnt + 1), self.xs, self.ys) for center, ellip, feat in zip(cents, ellipses, self.state._features[self.frameIndex]) : # Remove any other objects that may exist before adding # new objects to the feature. feat.cleanup(['contour']) if ellip is None : continue cent_indx = tuple(np.floor(center).astype(int).tolist()) # TODO: clean this up! newPoint = self.state._new_point(self.xs[cent_indx], self.ys[cent_indx]) self.ax.add_artist(ellip) self.ax.add_artist(newPoint) feat.objects['center'] = newPoint feat.objects['ellip'] = ellip if feat.track is not None : feat.track.update_frame(self.frameIndex) #print "clust count:", clustCnt return clustLabels, clustCnt def _xy2grid(self, x, y) : return (self.xs[0].searchsorted(x), self.ys[:, 0].searchsorted(y)) def update_frame(self, lastFrame=None, force_recluster=False, hold_recluster=False) : """ Redraw the current frame. Calculate clusters if needed. *lastFrame* int (None) If specified, make this frame's features invisible. *force_recluster* boolean (False) If True, do a recluster, even if it seems like it isn't needed. Can be over-ridden by *hold_recluster*. *hold_recluster* boolean (False) If True, then don't do a recluster, even if needed or *force_recluster* is True. """ if lastFrame is not None : self._clear_frame(lastFrame) data = self.radarData.curr() # Display current frame's radar image if self._im is None : self._im = MakeReflectPPI(data['vals'][0], self.ys, self.xs, meth='pcmesh', ax=self.ax, colorbar=False, axis_labels=False, zorder=0, mask=False) else : self._im.set_array(data['vals'][0, :-1, :-1].flatten()) if force_recluster or any([('center' not in feat.objects) for feat in self.state._features[self.frameIndex]]) : if not hold_recluster : clustLabels, clustCnt = self.get_clusters() # Set features for this frame to visible for feat in self.state._features[self.frameIndex] : feat.set_visible(True) # Return alpha back to normal feat.set_alpha(1.0) # Put it on top feat.set_zorder(len(self.radarData)) #feat.set_picker(True) # Show the other features if self._show_features : # How much alpha should change for each frame from frameIndex # The closer to self.frameIndex, the more opaque alphaIncrem = 1.0 / len(self.radarData) for frameIndex, features in self.state._features.iteritems() : if frameIndex == self.frameIndex : continue framesFrom = np.abs(self.frameIndex - frameIndex) timeAlpha = ((1.0 - (framesFrom * alphaIncrem)) ** (1.0/self._alphaScale)) zorder = len(self.radarData) - framesFrom for feat in features : feat.set_visible(True) feat.set_alpha(timeAlpha) feat.set_zorder(zorder) else : # Make sure that these items are hidden for frameIndex, features in self.state._features.iteritems() : if frameIndex != self.frameIndex : for feat in features : feat.set_visible(False) # Return alpha to normal feat.set_alpha(1.0) if self.volTimes[self.frameIndex] is None : theDateTime = datetime.utcfromtimestamp(data['scan_time']) self.volTimes[self.frameIndex] = theDateTime else : theDateTime = self.volTimes[self.frameIndex] self.ax.set_title(theDateTime.strftime("%Y/%m/%d %H:%M:%S")) self.fig.canvas.draw_idle()
def update_frame(self, lastFrame=None, force_recluster=False, hold_recluster=False) : """ Redraw the current frame. Calculate clusters if needed. *lastFrame* int (None) If specified, make this frame's features invisible. *force_recluster* boolean (False) If True, do a recluster, even if it seems like it isn't needed. Can be over-ridden by *hold_recluster*. *hold_recluster* boolean (False) If True, then don't do a recluster, even if needed or *force_recluster* is True. """ if lastFrame is not None : self._clear_frame(lastFrame) data = self.radarData.curr() # Display current frame's radar image if self._im is None : self._im = MakeReflectPPI(data['vals'][0], self.ys, self.xs, meth='pcmesh', ax=self.ax, colorbar=False, axis_labels=False, zorder=0, mask=False) else : self._im.set_array(data['vals'][0, :-1, :-1].flatten()) if force_recluster or any([('center' not in feat.objects) for feat in self.state._features[self.frameIndex]]) : if not hold_recluster : clustLabels, clustCnt = self.get_clusters() # Set features for this frame to visible for feat in self.state._features[self.frameIndex] : feat.set_visible(True) # Return alpha back to normal feat.set_alpha(1.0) # Put it on top feat.set_zorder(len(self.radarData)) #feat.set_picker(True) # Show the other features if self._show_features : # How much alpha should change for each frame from frameIndex # The closer to self.frameIndex, the more opaque alphaIncrem = 1.0 / len(self.radarData) for frameIndex, features in self.state._features.iteritems() : if frameIndex == self.frameIndex : continue framesFrom = np.abs(self.frameIndex - frameIndex) timeAlpha = ((1.0 - (framesFrom * alphaIncrem)) ** (1.0/self._alphaScale)) zorder = len(self.radarData) - framesFrom for feat in features : feat.set_visible(True) feat.set_alpha(timeAlpha) feat.set_zorder(zorder) else : # Make sure that these items are hidden for frameIndex, features in self.state._features.iteritems() : if frameIndex != self.frameIndex : for feat in features : feat.set_visible(False) # Return alpha to normal feat.set_alpha(1.0) if self.volTimes[self.frameIndex] is None : theDateTime = datetime.utcfromtimestamp(data['scan_time']) self.volTimes[self.frameIndex] = theDateTime else : theDateTime = self.volTimes[self.frameIndex] self.ax.set_title(theDateTime.strftime("%Y/%m/%d %H:%M:%S")) self.fig.canvas.draw_idle()
def main(args): if args.bw_mode: BW_mode() if len(args.trackFiles) == 0: print "WARNING: No trackFiles given!" if len(args.truthTrackFile) == 0: print "WARNING: No truth trackFiles given!" if args.trackTitles is None: args.trackTitles = args.trackFiles else: if len(args.trackTitles) != len(args.trackFiles): raise ValueError("The number of TITLEs does not match the number" " of TRACKFILEs") if args.statName is not None and args.statLonLat is None: statData = ByName(args.statName)[0] args.statLonLat = (statData['LON'], statData['LAT']) if args.layout is None: args.layout = (1, max(len(args.trackFiles), len(args.truthTrackFile))) if args.figsize is None: args.figsize = plt.figaspect(float(args.layout[0]) / args.layout[1]) if args.simTagFiles is None: args.simTagFiles = [] trackerData = [ FilterMHTTracks(*ReadTracks(trackFile)) for trackFile in args.trackFiles ] truthData = [ FilterMHTTracks(*ReadTracks(trackFile)) for trackFile in args.truthTrackFile ] multiTags = [ReadSimTagFile(fname) for fname in args.simTagFiles] if len(multiTags) == 0: multiTags = [None] if args.statLonLat is not None: for aTracker in trackerData + truthData: CoordinateTransform(aTracker[0] + aTracker[1], args.statLonLat[0], args.statLonLat[1]) if len(trackerData) != len(truthData): # Basic broadcasting needed! if len(truthData) > len(trackerData): # Need to extend track data to match with the number of truth sets if len(truthData) % len(trackerData) != 0: raise ValueError("Can't extend TRACKFILE list to match with" " the TRUTHFILE list!") else: # Need to extend truth sets to match with the number of track data if len(trackerData) % len(truthData) != 0: raise ValueError("Can't extend TRUTHFILE list to match with" " the TRACKFILE list!") trkMult = max(int(len(truthData) // len(trackerData)), 1) trthMult = max(int(len(trackerData) // len(truthData)), 1) trackerData = trackerData * trkMult truthData = truthData * trthMult tagMult = max(int(len(truthData) // len(multiTags)), 1) multiTags = multiTags * tagMult args.trackTitles = args.trackTitles * trkMult theFig = plt.figure(figsize=args.figsize) grid = AxesGrid(theFig, 111, nrows_ncols=args.layout, aspect=False, share_all=True, axes_pad=0.45) showMap = (args.statLonLat is not None and args.displayMap) if args.radarFile is not None and args.statLonLat is not None: if len(args.radarFile) > 1 and args.endFrame is not None: args.radarFile = args.radarFile[args.endFrame] else: args.radarFile = args.radarFile[-1] data = LoadRastRadar(args.radarFile) for ax in grid: MakeReflectPPI(data['vals'][0], data['lats'], data['lons'], meth='pcmesh', ax=ax, colorbar=False, axis_labels=False, zorder=0, alpha=0.6) MakeComparePlots(grid, trackerData, truthData, args.trackTitles, showMap, endFrame=args.endFrame, tail=args.tail, fade=args.fade, multiTags=multiTags, tag_filters=args.filters) if args.xlims is not None and np.prod(grid.get_geometry()) > 0: grid[0].set_xlim(args.xlims) if args.ylims is not None and np.prod(grid.get_geometry()) > 0: grid[0].set_ylim(args.ylims) if args.saveImgFile is not None: theFig.savefig(args.saveImgFile) if args.doShow: plt.show()
def ClusterMap(clusters, vals, indicesToShow, radarBG_alpha=0.15, dimmerBox_alpha=0.25, zorder=0, axis=None, **kwargs): """ Plot the clusters of the radar reflectivities in a really cool way. vals must be indexed in [lat, lon] orientation radarBG_alpha - (float) How opaque should the radar reflectivities be? Set to zero if you don't want any radar background. dimmerBox_alpha - (float) How opaque should the dimmer box be? Set to zero if you don't want any dimmer box zorder - (float) The zorder for the plot. Note that this plot will internally use zorders from *zorder* to *zorder* + 1 """ if axis is None: axis = plt.gca() print 'producing clust map' boxBoundx = None boxBoundy = None holdStatus = axis.ishold() if holdStatus: boxBoundx = axis.get_xlim() boxBoundy = axis.get_ylim() else: boxBoundx = (min(clusters['lonAxis']), max(clusters['lonAxis'])) boxBoundy = (min(clusters['latAxis']), max(clusters['latAxis'])) bbBoxX = (boxBoundx[0], boxBoundx[0], boxBoundx[1], boxBoundx[1]) bbBoxY = (boxBoundy[0], boxBoundy[1], boxBoundy[1], boxBoundy[0]) bbBox = zip(bbBoxX, bbBoxY) (lons, lats) = np.meshgrid(clusters['lonAxis'], clusters['latAxis']) domainMask = (lats < boxBoundy[0]) | (lats > boxBoundy[1]) | \ (lons < boxBoundx[0]) | (lons > boxBoundx[1]) | \ np.isnan(vals) # Create a transform object for the drop shadow. dx, dy = 2 / 72., -2 / 72. #white_offset = axis.transData + transforms.ScaledTranslation(dx, dy, axis.figure.dpi_scale_trans) black_offset = axis.transData + \ transforms.ScaledTranslation(dx, dy, axis.figure.dpi_scale_trans) # elementCount determines how many things are going to be # displayed in this plotting function. To start, there # will be len(indicesToShow) clusters to plot, each of # those are plotted with three elements (white contour, # black contour, and a patch). # Then there is the possible radar background. # Lastly, there is the possible dimmer box. # Note: this can be an over-estimate many clusters might be # outside the bounding box. elementCount = ((2 * len(indicesToShow)) + (radarBG_alpha > 0.0) + (dimmerBox_alpha > 0.0)) # Create an array of zorder values to use for the components # of the ClusterMap. zorders = np.linspace(zorder, zorder + 1, num=elementCount, endpoint=False) zorderIndex = 0 # ------ Radar Background -------------- # Don't bother plotting the BG if alpha is zero. if radarBG_alpha > 0.0: MakeReflectPPI(vals, lats, lons, mask=domainMask, colorbar=False, axis_labels=False, ax=axis, zorder=zorders[zorderIndex], alpha=radarBG_alpha, meth='im', **kwargs) zorderIndex += 1 axis.hold(True) # --------Dimmer box-------- # Don't bother plotting the dimmer box if it is zero. if dimmerBox_alpha > 0.0: axis.fill(bbBoxX, bbBoxY, 'black', zorder=zorders[zorderIndex], alpha=dimmerBox_alpha) zorderIndex += 1 axis.hold(True) # -------- Clusters ------------ clustMask = np.empty(vals.shape, dtype=bool) #clustVals = np.empty_like(vals) clustMembers = [ np.nonzero(clusters['clusterIndicies'] == clustIndx) for clustIndx in indicesToShow ] # Need to draw each contour separately to guarrantee that the # cluster has a closed loop for itself (i.e. - not merged with # a neighboring cluster). print "ClustCnt:", len(indicesToShow) # Plot the outlines of the clusters by initializing an # array with NaNs, and then assigning ones to the pixels # for a particular cluster. Then plot the contour of this # array, forcing it to use only one contour level. # The clusters are done separately so that clusters that # touch or even share a few pixels are still distinguishable. for index, goodMembers in enumerate(clustMembers): # If the cluster does not exist within the bounding box, # don't bother rendering it. if not np.any(~domainMask[clusters['members_LatLoc'][goodMembers], clusters['members_LonLoc'][goodMembers]]): continue clustMask.fill(True) # Finding all members of the cluster clustMask[clusters['members_LatLoc'][goodMembers], clusters['members_LonLoc'][goodMembers]] = False # Plotting the cluster (white line, black line, then # reflectivity patch). This could probably be improved # black outline outline = axis.contour(lons, lats, clustMask, [0, 1], colors='k', linewidths=2.5, zorder=zorders[zorderIndex + 0]) SetContourZorder(outline, zorders[zorderIndex + 1]) SetContourTransform(outline, black_offset) # white highlight... save time by re-using contour data. #for aPath in outline.collections[0].get_paths() : # axis.plot(*aPath.vertices.T, c='k', lw=2.5, zorder=zorders[zorderIndex], # transform=white_offset) MakeReflectPPI(vals, lats, lons, mask=clustMask, ax=axis, zorder=zorders[zorderIndex + 1], axis_labels=False, colorbar=False, meth='im', **kwargs) zorderIndex += 2 # Return axis to whatever hold status it had before. axis.hold(holdStatus) print 'map produced'