diff --git a/LMA/analysis.py b/LMA/analysis.py index d99daf9..a8314b8 100644 --- a/LMA/analysis.py +++ b/LMA/analysis.py @@ -14,6 +14,7 @@ >>> current_events_flashes.targets.add(energy_spectrum_plotter.inlet) """ +from __future__ import absolute_import import numpy as np from stormdrain.pipeline import coroutine from stormdrain.support.matplotlib.artistupdaters import LineArtistUpdater diff --git a/LMA/controller.py b/LMA/controller.py index ebbe4e3..16ac489 100644 --- a/LMA/controller.py +++ b/LMA/controller.py @@ -4,9 +4,11 @@ understood by the lmatools package. """ +from __future__ import absolute_import +from __future__ import print_function import numpy as np -from lmatools.flashsort.autosort.LMAarrayFile import LMAdataFile +from lmatools.io.LMAarrayFile import LMAdataFile from stormdrain.bounds import Bounds, BoundsFilter from stormdrain.data import NamedArrayDataset, indexed @@ -101,7 +103,7 @@ def flash_stat_printer(self, min_points=10): good = (fl['n_points'] >= min_points) N_good = len(fl[good]) area = np.mean(fl['area'][good]) - print template.format(N_good, N, area, min_points) + print(template.format(N_good, N, area, min_points)) def flash_stats_for_dataset(self, d, selection_broadcaster): @@ -163,20 +165,20 @@ def read_hdf5(self, LMAfileHDF): try: import tables except ImportError: - print "couldn't import pytables" + print("couldn't import pytables") return None - from hdf5_lma import HDF5Dataset + from .hdf5_lma import HDF5Dataset # get the HDF5 table name - LMAh5 = tables.openFile(LMAfileHDF, 'r') - table_names = LMAh5.root.events._v_children.keys() + LMAh5 = tables.open_file(LMAfileHDF, 'r') + table_names = list(LMAh5.root.events._v_children.keys()) table_path = '/events/' + table_names[0] LMAh5.close() d = HDF5Dataset(LMAfileHDF, table_path=table_path, mode='a') self.datasets.add(d) if d.flash_table is not None: - print "found flash data" + print("found flash data") return d @@ -196,7 +198,7 @@ def load_hdf5_to_panels(self, panels, LMAfileHDF, scatter_kwargs={}): def load_hdf5_flashes_to_panels(self, panels, hdf5dataset, min_points=10): """ Set up a flash dataset display. The sole argument is usually the HDF5 LMA dataset returned by a call to self.load_hdf5_to_panels """ - from hdf5_lma import HDF5FlashDataset + from .hdf5_lma import HDF5FlashDataset if hdf5dataset.flash_table is not None: point_count_dtype = hdf5dataset.flash_data['n_points'].dtype self.bounds.n_points = (min_points, np.iinfo(point_count_dtype)) diff --git a/LMA/hdf5_lma.py b/LMA/hdf5_lma.py index 2f2a931..7a82428 100644 --- a/LMA/hdf5_lma.py +++ b/LMA/hdf5_lma.py @@ -1,7 +1,10 @@ +from __future__ import absolute_import +from __future__ import print_function import tables from stormdrain.pipeline import coroutine from stormdrain.pubsub import get_exchange +from six.moves import zip class HDF5FlashDataset(object): """ Provides an pipeline source for the flash table part of an HDF5Dataset""" @@ -19,19 +22,19 @@ class HDF5Dataset(object): def __init__(self, h5filename, table_path=None, target=None, mode='r'): self.target = target - self.h5file = tables.openFile(h5filename, mode=mode) - self.table = self.h5file.getNode(table_path) + self.h5file = tables.open_file(h5filename, mode=mode) + self.table = self.h5file.get_node(table_path) self.data = self.table[:] get_exchange('SD_reflow_start').attach(self) flash_table_path = table_path.replace('events', 'flashes') try: - self.flash_table = self.h5file.getNode(flash_table_path) + self.flash_table = self.h5file.get_node(flash_table_path) self.flash_data = self.flash_table[:] except tables.NoSuchNodeError: self.flash_table = None - print "Did not find flash data at {0}".format(flash_table_path) + print("Did not find flash data at {0}".format(flash_table_path)) def update_h5(self, colname, coldata, row_ids): @@ -65,7 +68,7 @@ def update(self, index_name="hdf_row_idx", field_names=None): else: # update everything self.data[indices] = a - print "Did not update HDF5 file" + print("Did not update HDF5 file") def send(self, msg): diff --git a/LMA/live.py b/LMA/live.py index dd2a6e8..1782cb6 100644 --- a/LMA/live.py +++ b/LMA/live.py @@ -34,6 +34,7 @@ """ +from __future__ import absolute_import from datetime import datetime import threading from collections import deque diff --git a/LMA/widgets.py b/LMA/widgets.py index 64ae7a2..0205e26 100644 --- a/LMA/widgets.py +++ b/LMA/widgets.py @@ -1,5 +1,6 @@ from __future__ import print_function +from __future__ import absolute_import from brawl4d.brawl4d import redraw from brawl4d.LMA.controller import LMAController import ipywidgets as widgets @@ -10,180 +11,187 @@ class LMAwidgetController: - def __init__(self, panels, lma_ctrl, scatter_ctrl, charge_lasso, d): #, station_number_selection, charge_lasso_widget, draw_lasso_widget, color_field_widget, animation_time_widget, animate_button, label, LMA_Controlsa, LMA_Controlsb, tools_popup, number_of_stations): - self.panels = panels - self.lma_ctrl = lma_ctrl - self.d = d - self.scatter_ctrl = scatter_ctrl - self.charge_lasso = charge_lasso - - #Max Chi2 Value: - chi2_selection = widgets.BoundedFloatText(description='Max Chi2:', min='0.0', max='1000.0', value='1') + def __init__(self, panels, lma_ctrl, scatter_ctrl, charge_lasso, d): #, station_number_selection, charge_lasso_widget, draw_lasso_widget, color_field_widget, animation_time_widget, animate_button, label, LMA_Controlsa, LMA_Controlsb, tools_popup, number_of_stations): + self.panels = panels + self.lma_ctrl = lma_ctrl + self.d = d + self.scatter_ctrl = scatter_ctrl + self.charge_lasso = charge_lasso + + #Max Chi2 Value: + chi2_selection = widgets.BoundedFloatText(description='Max Chi2:', min='0.0', max='1000.0', value='1') # chi2_selection.layout.width = '30px' - chi2_selection.on_trait_change(self.max_chi2, 'value') - - - #Station Number Selection: - station_number_selection = widgets.Dropdown(description='Number of Stations:', - options=[5, 6, 7, 8, 9, 10, 11, 12], value=7) - station_number_selection.background_color = '#888888' - station_number_selection.color = 'white' - # station_number_selection.set_css({ - # 'background-dropdown': '#888888', - # 'color': 'white', - # }) - station_number_selection.on_trait_change(self.number_of_stations, 'value') - - #Charge Lasso and Draw Button: - # charge_lasso_widget = widgets.RadioButtons(description='Charge Selection:', values=["-1", "0", "1"], value="-1") - charge_lasso_widget = widgets.RadioButtons(description='Charge Selection:', options=["-1", "0", "1"], value='-1') - charge_lasso_widget.on_trait_change(self.change_lasso_charge, 'value') - - draw_lasso_widget = widgets.Button(description='Draw') - draw_lasso_widget.background_color = '#888888' - draw_lasso_widget.color = 'white' - # draw_lasso_widget.set_css({ - # 'background': '#888888', - # 'color': 'white', - # }) - draw_lasso_widget.on_click(self.lasso_button) - - #Color Field Selection: - color_field_widget = widgets.RadioButtons(description='Color By:', options=["chi2", "time", "charge", "power"], value="time") - # color_field_widget = widgets.RadioButtons(description='Color By:', values=["chi2", "time", "charge"], value="time") - color_field_widget.on_trait_change(self.change_color_field, 'value') - - #Animate (Slider and Button) Optional Manual Numerical Input commented out: - animation_time_widget = widgets.IntSlider(description='Animation Time:', min='0', max='30') - animation_time_widget.value = '5' - animate_button = widgets.Button(description="Animate") - animate_button.layout.display = 'flex' - animate_button.layout.align_content = 'flex_end' - animate_button.background_color = '#888888' - animate_button.color = 'white' - # animation_time_widget = widgets.Text(Description='Animation Time') - # animation_time_widget.placeholder = "value" - # animate_button.set_css({ - # 'display': 'flex', - # 'align-content': 'flex-end', - # 'background': '#888888', - # 'color': 'white', - # }) - animate_button.on_click(self.run_animation_button) - - #FOR CONTAINERS AND POPUP - label = widgets.widget_string.Label(value='LMA Tools') - label.font_size = '20px' - label.font_weight = 'bold' - label.layout.align_self = 'center' - label.padding = '15px' - label.background_color = '#d8d8d8' - # label.set_css({ - # 'font-size': '20px', - # 'font-weight': 'bold', - # 'align-self': 'center', - # 'padding': '15px', - # 'background': 'd8d8d8', - # }) - - # LMA_Controlsa = widgets.ContainerWidget() - # LMA_Controlsa.children = [station_number_selection, charge_lasso_widget, draw_lasso_widget, chi2_selection] - # LMA_Controlsa.set_css({ - # 'display': 'flex', - # 'flex-direction': 'column', - # # 'max-width': '300px', - # 'flex-flow': 'row wrap', - # 'align-content': 'flex-start', - # 'padding': '10px', - # 'background': '#e8e8e8', - # 'font-weight': 'bold', - # }) - # LMA_Controlsa.remove_class('vbox') - # LMA_Controlsa.add_class('hbox') - LMA_Controlsa = widgets.HBox(children = [station_number_selection, - charge_lasso_widget, draw_lasso_widget, chi2_selection]) - LMA_Controlsa.layout.align_content = 'flex_start' - LMA_Controlsa.padding = '10px' - LMA_Controlsa.background_color = '#e8e8e8' - LMA_Controlsa.font_weight = 'bold' - - # LMA_Controlsb = widgets.ContainerWidget() - # LMA_Controlsb.children = [color_field_widget, animation_time_widget, animate_button] - # LMA_Controlsb.set_css({ - # 'display': 'flex', - # 'flex-flow': 'wrap', - # 'align-items': 'right', - # 'columns': '1', - # 'padding': '10px', - # 'background': '#e8e8e8', - # 'font-weight': 'bold', - # }) - # LMA_Controlsb.remove_class('vbox') - # LMA_Controlsb.add_class('hbox') - LMA_Controlsb = widgets.HBox(children = [color_field_widget, - animation_time_widget, animate_button]) - LMA_Controlsa.layout.align_content = 'right' - LMA_Controlsa.padding = '10px' - LMA_Controlsa.background_color = '#e8e8e8' - LMA_Controlsa.font_weight = 'bold' - - # tools_popup = widgets.PopupWidget(description='LMA Control Hub') - # tools_popup.set_css({ - # 'font-size': '14px', - # 'align-self': 'center', - # 'padding': '15px', - # 'border': '5px ridge #989898', - # 'background': '#e8e8e8', - - # }) - # tools_popup.children = [label, LMA_Controlsa, LMA_Controlsb] - tools_popup = widgets.Box(description='LMA Control Hub', - children = [label, LMA_Controlsa, LMA_Controlsb]) - tools_popup.font_size = '14px' - tools_popup.layout.align_self = 'center' - tools_popup.padding = '15px' - tools_popup.border = '5px ridge #989898' - tools_popup.background_color = '#e8e8e8' - - - self.chi2_selection = chi2_selection - self.station_number_selection = station_number_selection - self.charge_lasso_widget = charge_lasso_widget - self.draw_lasso_widget = draw_lasso_widget - self.color_field_widget = color_field_widget - self.animation_time_widget = animation_time_widget - self.animate_button = animate_button - self.label = label - self.LMA_Controlsa = LMA_Controlsa - self.LMA_Controlsb = LMA_Controlsb - self.tools_popup = tools_popup + chi2_selection.on_trait_change(self.max_chi2, 'value') + + + #Station Number Selection: + station_number_selection = widgets.Dropdown(description='Minimum Stations:', + options=[5, 6, 7, 8, 9, 10, 11, 12], value=7) + # station_number_selection.background_color = '#888888' + # station_number_selection.color = 'white' + # station_number_selection.set_css({ + # 'background-dropdown': '#888888', + # 'color': 'white', + # }) + station_number_selection.on_trait_change(self.number_of_stations, 'value') + + #Charge Lasso and Draw Button: + # charge_lasso_widget = widgets.RadioButtons(description='Charge Selection:', values=["-1", "0", "1"], value="-1") + charge_lasso_widget = widgets.RadioButtons(description='Charge Selection:', options=["-1", "0", "1"], value='-1') + charge_lasso_widget.on_trait_change(self.change_lasso_charge, 'value') + + draw_lasso_widget = widgets.Button(description='Lasso') + # draw_lasso_widget.background_color = '#888888' + # draw_lasso_widget.color = 'white' + # draw_lasso_widget.set_css({ + # 'background': '#888888', + # 'color': 'white', + # }) + draw_lasso_widget.on_click(self.lasso_button) + + lasso_widget = widgets.VBox([charge_lasso_widget, draw_lasso_widget]) + + #Color Field Selection: + color_field_widget = widgets.RadioButtons(description='Color By:', options=["chi2", "time", "charge", "power"], value="time") + # color_field_widget = widgets.RadioButtons(description='Color By:', values=["chi2", "time", "charge"], value="time") + color_field_widget.on_trait_change(self.change_color_field, 'value') + + #Animate (Slider and Button) Optional Manual Numerical Input commented out: + animation_time_widget = widgets.IntSlider(description='Animation Time:', min='0', max='30') + animation_time_widget.value = '5' + animate_button = widgets.Button(description="Animate") + # animate_button.layout.display = 'flex' + # animate_button.layout.align_content = 'flex-end' + # animate_button.background_color = '#888888' + # animate_button.color = 'white' + # animation_time_widget = widgets.Text(Description='Animation Time') + # animation_time_widget.placeholder = "value" + # animate_button.set_css({ + # 'display': 'flex', + # 'align-content': 'flex-end', + # 'background': '#888888', + # 'color': 'white', + # }) + animate_button.on_click(self.run_animation_button) + + animation_widget = widgets.VBox([animation_time_widget, animate_button]) + + #FOR CONTAINERS AND POPUP + label = widgets.widget_string.Label(value='LMA Tools') + # label.font_size = '20px' + # label.font_weight = 'bold' + label.layout.align_self = 'center' + # label.padding = '15px' + # label.background_color = '#d8d8d8' + # label.set_css({ + # 'font-size': '20px', + # 'font-weight': 'bold', + # 'align-self': 'center', + # 'padding': '15px', + # 'background': 'd8d8d8', + # }) + + # LMA_Controlsa = widgets.ContainerWidget() + # LMA_Controlsa.children = [station_number_selection, charge_lasso_widget, draw_lasso_widget, chi2_selection] + # LMA_Controlsa.set_css({ + # 'display': 'flex', + # 'flex-direction': 'column', + # # 'max-width': '300px', + # 'flex-flow': 'row wrap', + # 'align-content': 'flex-start', + # 'padding': '10px', + # 'background': '#e8e8e8', + # 'font-weight': 'bold', + # }) + # LMA_Controlsa.remove_class('vbox') + # LMA_Controlsa.add_class('hbox') + LMA_Controlsa = widgets.HBox([color_field_widget, + widgets.VBox([station_number_selection, + chi2_selection]) + ]) + # LMA_Controlsa.layout.align_content = 'flex-start' + # LMA_Controlsa.padding = '10px' + # LMA_Controlsa.background_color = '#e8e8e8' + # LMA_Controlsa.font_weight = 'bold' + + # LMA_Controlsb = widgets.ContainerWidget() + # LMA_Controlsb.children = [color_field_widget, animation_time_widget, animate_button] + # LMA_Controlsb.set_css({ + # 'display': 'flex', + # 'flex-flow': 'wrap', + # 'align-items': 'right', + # 'columns': '1', + # 'padding': '10px', + # 'background': '#e8e8e8', + # 'font-weight': 'bold', + # }) + # LMA_Controlsb.remove_class('vbox') + # LMA_Controlsb.add_class('hbox') + LMA_Controlsb = widgets.HBox([animation_widget, + lasso_widget]) + # LMA_Controlsa.layout.align_content = 'right' + # LMA_Controlsa.padding = '10px' + # LMA_Controlsa.background_color = '#e8e8e8' + # LMA_Controlsa.font_weight = 'bold' + + # tools_popup = widgets.PopupWidget(description='LMA Control Hub') + # tools_popup.set_css({ + # 'font-size': '14px', + # 'align-self': 'center', + # 'padding': '15px', + # 'border': '5px ridge #989898', + # 'background': '#e8e8e8', + + # }) + # tools_popup.children = [label, LMA_Controlsa, LMA_Controlsb] + tools_popup = widgets.VBox([label, LMA_Controlsa, LMA_Controlsb], + description='LMA Control Panel', + ) + tools_popup.display = 'flex' + + # tools_popup.align_content = 'stretch' + # tools_popup.font_size = '14px' + # tools_popup.layout.align_self = 'center' + # tools_popup.padding = '15px' + # tools_popup.border = '5px ridge #989898' + # tools_popup.background_color = '#e8e8e8' + + + self.chi2_selection = chi2_selection + self.station_number_selection = station_number_selection + self.charge_lasso_widget = charge_lasso_widget + self.draw_lasso_widget = draw_lasso_widget + self.color_field_widget = color_field_widget + self.animation_time_widget = animation_time_widget + self.animate_button = animate_button + self.label = label + self.LMA_Controlsa = LMA_Controlsa + self.LMA_Controlsb = LMA_Controlsb + self.tools_popup = tools_popup #Where each widget is defined with their respective values: - - def max_chi2(self, name, value): - self.lma_ctrl.bounds.chi2=(0,value) - redraw(self.panels) - - def number_of_stations(self, name, value): - self.lma_ctrl.bounds.stations=(value,99) - redraw(self.panels) - - def change_lasso_charge(self, name, value): - print(value) - self.charge_lasso.charge=value - - def lasso_button(self, on_click): - self.panels.lasso() - - def change_color_field(self, name, the_value): - self.scatter_ctrl.color_field = the_value - redraw(self.panels) - - def run_animation_button(self, anim): - n_seconds = self.animation_time_widget.value - anim=self.scatter_ctrl.animate(n_seconds, repeat=False) - anim.repeat=False - redraw(self.panels) - - - \ No newline at end of file + + def max_chi2(self, name, value): + self.lma_ctrl.bounds.chi2=(0,value) + redraw(self.panels) + + def number_of_stations(self, name, value): + self.lma_ctrl.bounds.stations=(value,99) + redraw(self.panels) + + def change_lasso_charge(self, name, value): + print(value) + self.charge_lasso.charge=value + + def lasso_button(self, on_click): + self.panels.lasso() + + def change_color_field(self, name, the_value): + self.scatter_ctrl.color_field = the_value + redraw(self.panels) + + def run_animation_button(self, anim): + n_seconds = self.animation_time_widget.value + anim=self.scatter_ctrl.animate(n_seconds, repeat=False) + anim.repeat=False + redraw(self.panels) diff --git a/NLDN.py b/NLDN.py index 1453e09..01a067c 100644 --- a/NLDN.py +++ b/NLDN.py @@ -1,3 +1,4 @@ +from __future__ import absolute_import import numpy as np from numpy.lib.recfunctions import append_fields diff --git a/brawl4d.py b/brawl4d.py index b5da2f3..b1ad78d 100644 --- a/brawl4d.py +++ b/brawl4d.py @@ -2,6 +2,8 @@ Balloons, Radar, and Aircraft with Lightning = BRAWL. """ +from __future__ import absolute_import +from __future__ import print_function import datetime import numpy as np @@ -38,9 +40,10 @@ class Panels4D(LinkedPanels): # 1.618 # 89,55,34,21,13,8,5,3,2,1,1,0 + x0 = .025 # shift all panels to right by this amount to leave space for the left axis label dx = .89*0.55 dz = .89*0.21 - mg = .89*0.05 + mg = .89*0.05 # margin dy = dx dt = dx+dz w = mg+dt+mg @@ -49,10 +52,10 @@ class Panels4D(LinkedPanels): # Left, bottom, width, height margin_defaults = { - 'xy':(mg*aspect, mg, dx*aspect, dy), - 'xz':(mg*aspect, mg+dy, dx*aspect, dz), - 'zy':((mg+dx)*aspect, mg, dz*aspect, dy), - 'tz':(mg*aspect, mg+dy+dz+mg, dt*aspect, dz), + 'xy':(mg*aspect+x0, mg, dx*aspect, dy), + 'xz':(mg*aspect+x0, mg+dy, dx*aspect, dz), + 'zy':((mg+dx)*aspect+x0, mg, dz*aspect, dy), + 'tz':(mg*aspect+x0, mg+dy+dz+mg, dt*aspect, dz), } def __init__(self, *args, **kwargs): @@ -128,7 +131,7 @@ def lasso(self): self._active_lasso = PolyLasso(self.figure, self._lasso_callback) lock(self._active_lasso) else: - print "Please deselect other tools to use the lasso." + print("Please deselect other tools to use the lasso.") @@ -181,14 +184,14 @@ def plot_demo_dataset(d, panels): return branch, scatter_outlet_broadcaster -def B4D_startup(show=False, basedate=None, ctr_lat=33.5, ctr_lon=-101.5): +def B4D_startup(show=False, basedate=None, ctr_lat=33.5, ctr_lon=-101.5, figsize=(8.5, 11.0), dpi=80): import matplotlib fontspec = {'family':'Helvetica', 'weight':'bold', 'size':10} matplotlib.rc('font', **fontspec) import matplotlib.pyplot as plt - panel_fig = plt.figure(figsize=(8.5, 11.0)) + panel_fig = plt.figure(figsize=figsize, dpi=dpi) panels = Panels4D(figure=panel_fig, names_4D=('x', 'y', 'z', 'time'), basedate=basedate, ctr_lat=ctr_lat, ctr_lon=ctr_lon) fig_updater = FigureUpdater(panel_fig) diff --git a/docs/conf.py b/docs/conf.py index 5a1c8f6..b4746a8 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -11,6 +11,7 @@ # All configuration values have a default; values that are commented out # serve to show the default. +from __future__ import absolute_import import sys, os # If extensions (or modules to document with autodoc) are in another directory, diff --git a/radar/collection.py b/radar/collection.py index e3a5de2..25efbca 100644 --- a/radar/collection.py +++ b/radar/collection.py @@ -1,6 +1,9 @@ +from __future__ import absolute_import +from __future__ import print_function import pandas import numpy as np import pyart +from six.moves import zip def diagnose_vcp(radar): # swp_start = radar.sweep_start_ray_index['data'] @@ -33,7 +36,7 @@ def data_for_ray_slice(radar, ray_sl, fieldnames=None): data = radar.fields[fieldname]['data'][ray_sl, :] except KeyError: data = None - print("Couldn't find {1} data in {0}".format(radar,fieldname)) + print(("Couldn't find {1} data in {0}".format(radar,fieldname))) data_dict[fieldname] = data return r,az,el,t,data_dict @@ -41,7 +44,7 @@ def data_for_ray_slice(radar, ray_sl, fieldnames=None): def iter_sweep_data(radar, fieldnames): for swp_start, swp_end in zip(radar.sweep_start_ray_index['data'], radar.sweep_end_ray_index['data']): - print swp_start, swp_end + # print swp_start, swp_end ray_sl = slice(swp_start,swp_end+1) yield data_for_ray_slice(radar,ray_sl,fieldnames=fieldnames) @@ -121,8 +124,10 @@ def sweep_for_time_range(self, t0, t1, overlap_idx=0): """ #target = pandas.DataFrame([(t0, t1),], columns=('start', 'end')) + zero_dt = np.zeros(1, dtype=np.timedelta64)[0] - overlap = ((t0 - self.sweep_table['end']) < 0) & ((t1 - self.sweep_table['start']) > 0 ) + overlap = (((t0 - self.sweep_table['end']) < zero_dt) & + ((t1 - self.sweep_table['start']) > zero_dt)) sweeps = self.sweep_table[overlap] if len(sweeps) > 0: selection = np.zeros(len(sweeps), dtype=bool) diff --git a/radar/controller.py b/radar/controller.py index b3007b7..f8fb8e3 100644 --- a/radar/controller.py +++ b/radar/controller.py @@ -47,6 +47,8 @@ """ +from __future__ import absolute_import +from __future__ import print_function class RadarController(object): """ Manage loading radar datasets and creation of displays. """ def __init__(self): @@ -58,7 +60,7 @@ def _pyart_read(self, filename, **kwargs): import pyart return pyart.io.read(filename, **kwargs) except ImportError: - print "Can't read radar: pyart not installed" + print("Can't read radar: pyart not installed") return None def radar_to_panels(self, radar, panels, **kwargs): diff --git a/radar/pipeline.py b/radar/pipeline.py index 8b02b21..58e5301 100644 --- a/radar/pipeline.py +++ b/radar/pipeline.py @@ -1,3 +1,4 @@ +from __future__ import absolute_import from stormdrain.pipeline import coroutine diff --git a/radar/quadmesh_geometry.py b/radar/quadmesh_geometry.py index b840f1c..046293f 100644 --- a/radar/quadmesh_geometry.py +++ b/radar/quadmesh_geometry.py @@ -1,14 +1,10 @@ # Eric Bruning, 11 October 2014. License: BSD (new). # Modified from the vispy Mesh and ModularMesh examples. +from __future__ import absolute_import import numpy as np pi = np.pi -import vispy.app -# from vispy.scene.visuals.modular_mesh import ModularMesh -from vispy.scene.visuals import Mesh -from vispy.geometry import MeshData -from vispy.scene import STTransform, MatrixTransform def synthetic_field(x,y): # Trapp and Doswell (2000, Mon. Weather Rev.) analytic input field @@ -67,12 +63,17 @@ def radar_example_data(): return (x_radar_edge, y_radar_edge, z_radar_edge, all_d) -def mesh_from_quads(x,y,z): +def mesh_from_quads(x,y,z, dup_verts=False): """ x, y, z are M x N arrays giving the edge locations for a quadrilateral mesh of Nq = (M-1) x (N-1) implied quad faces. After conversion to triangles, this is Nf=2*Nq faces. Nv = M x N. + If the keyword argument dup_verts=True, then x,y,z vertices + will be replicated, such that each quad has its own + vertices not shared with other quads. This allows one to specify + colors by vertex if coloring by face is not supported. + returns vertices : ndarray, shape (Nv, 3) - Vertex coordinates. faces : ndarray, shape (Nf, 3) - Indices into the vertex array. @@ -85,77 +86,150 @@ def mesh_from_quads(x,y,z): alternating the triangles' diagonal direction. It would be more amenable GL_TRIANGLE_STRIP. (1) http://dan.lecocq.us/wordpress/2009/12/25/triangle-strip-for-grids-a-construction/ + + """ + + if dup_verts: + n_repeat = 2 + else: + n_repeat = 1 + M, N = x.shape - Nv = M * N + Nv = (M * n_repeat) * (N * n_repeat) Nq = (M-1) * (N-1) Nf = 2*Nq verts = np.empty((Nv, 3), dtype='f4') faces = np.empty((Nf, 3), dtype='i4') - verts[:,0] = x.flat - verts[:,1] = y.flat - verts[:,2] = z.flat + xv = np.repeat(np.repeat(x,n_repeat,axis=0), n_repeat, axis=1) + yv = np.repeat(np.repeat(y,n_repeat,axis=0), n_repeat, axis=1) + zv = np.repeat(np.repeat(z,n_repeat,axis=0), n_repeat, axis=1) + + verts[:,0] = xv.flat + verts[:,1] = yv.flat + verts[:,2] = zv.flat - q_range = np.arange(Nq, dtype='i4') + q_range = np.arange(Nq, dtype='i4')*n_repeat + q_range += (N*n_repeat + 1)*(n_repeat-1) # When the index along M changes, need to skip ahead to avoid connecting # the edges together. - # Should look like (0,0,0,1,1,1,2,2,2,3,3,3) for N-1=3 and M-1=4 - adjust_start = (np.arange(M-1,dtype='int32')[:,None]*np.ones(N-1,dtype='int32')[None,:]).flatten() - q_range+=adjust_start + adjust_start = (np.arange(M-1, dtype='i4')[:,None] * + np.ones(N-1, dtype='i4')[None,:]).flatten() + adjust_adjust = adjust_start * (N*n_repeat+1) + q_range+=adjust_start + adjust_adjust*(n_repeat-1) + + # for N-1=3 and M-1=4 and n_repeat=1, the final values are + # adjust_start = (0,0,0,1,1,1,2,2,2,3,3,3) + # q_range = (0,1,2,4,5,6,8,9,10,12,13,14) + - # even (0,2,4,...) triangles - faces[0::2, 0] = q_range + 1 - faces[0::2, 1] = q_range - faces[0::2, 2] = q_range + N - # odd (1,3,5,...) trianges - faces[1::2, 0] = q_range + N - faces[1::2, 1] = q_range + N + 1 - faces[1::2, 2] = q_range + 1 + # repeated arrays for N-1=4, M-1=3 -- need to redo this block with correct N, M + # xv=array([[0, 0, 1, 1, 2, 2, 3, 3, 4, 4], + # [0, 0, 1, 1, 2, 2, 3, 3, 4, 4], + # [0, 0, 1, 1, 2, 2, 3, 3, 4, 4], + # [0, 0, 1, 1, 2, 2, 3, 3, 4, 4], + # [0, 0, 1, 1, 2, 2, 3, 3, 4, 4], + # [0, 0, 1, 1, 2, 2, 3, 3, 4, 4], + # [0, 0, 1, 1, 2, 2, 3, 3, 4, 4], + # [0, 0, 1, 1, 2, 2, 3, 3, 4, 4]]) - return verts, faces + # yv=array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + # [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + # [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + # [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + # [2, 2, 2, 2, 2, 2, 2, 2, 2, 2], + # [2, 2, 2, 2, 2, 2, 2, 2, 2, 2], + # [3, 3, 3, 3, 3, 3, 3, 3, 3, 3], + # [3, 3, 3, 3, 3, 3, 3, 3, 3, 3]]) + # First quad is 11, 12, 21, 22, which becomes and upper and lower triangle + # (first column of expanded indices below) + # Second quad is 13, 14, 23, 24 + # Third quad is 15, 16, 25, 26 + # Fourth quad is 17, 18, 27, 28 --- skip down two rows now, adding 20=N*n_repeat*n_repeat to the first + # Fifth quad is 31, 32, 41, 42 + # Six quad is 33, 34, 43, 44 + # M=5, N=4 + # each quad becomes and upper and lower triangle (first column of expanded indices below) + # first entry is q_range[0] + (N*n_repeat + 1)*(n_repeat-1) + # First quad is 9, 10, 17, 18 (instead of 0, 1, 4, 5) - second pair increases by N*n_repeat + # Second quad is 11, 12, 19, 20 (instead of 1, 2, 5, 6) - second quad increases by two in each pos'n - q_range*n_repeat and adjust_range*n_repeat + # Third quad is 13, 14, 21, 22 (insted of 2, 3, 6, 7) + + # skip down two rows now, adding 16 = N*n_repeat*n_repeat. This is the number by which to multiply adjust_start. + # Fourth quad is 25, 26, 33, 34 (instead of 4, 5, 8, 9) + # Fifth quad is 27, 28, 35, 36 (instead of 5, 6, 9, 10) + # Sixth quad is 29, 30, 37, 38 (instead of 6, 7, 10, 11) + # print(adjust_start) + # print(q_range ) + # print(q_range + 1 ) + # print(q_range + N*n_repeat ) + # print(q_range + N*n_repeat + 1) + # print(xv) + # print(yv) + # np.repeat(q_range,n_repeat) + + # numbers below are for for N-1=3 and M-1=4 and n_repeat=1 + # even (0,2,4,...) triangles + faces[0::2, 0] = q_range + 1 # (1,2,3,5, 6, 7, 9,10,11,13,14,15) + faces[0::2, 1] = q_range # (0,1,2,4, 5, 6, 8, 9,10,12,13,14) + faces[0::2, 2] = q_range + N*n_repeat # (4,5,6,8, 9,10,12,13,14,16,17,18) + # odd (1,3,5,...) trianges + faces[1::2, 0] = q_range + N*n_repeat # (4,5,6,8, 9,10,12,13,14,16,17,18) + faces[1::2, 1] = q_range + N*n_repeat + 1 # (5,6,7,9,10,11,13,14,15,17,18,19) + faces[1::2, 2] = q_range + 1 # (1,2,3,5, 6, 7, 9,10,11,13,14,15) -class Canvas(vispy.scene.SceneCanvas): - def __init__(self): - self.rotation = MatrixTransform() + return verts, faces - # Generate some data to work with - x,y,z,d = radar_example_data() - print x.shape, y.shape, z.shape - print d.shape, d.min(), d.max() +def tri_colors_from_quadmesh(x,y,z,d, dup_verts=True, cm=None, vmin=0, vmax=1): + verts, faces = mesh_from_quads(x,y,z, dup_verts=dup_verts) + # print(verts.shape, faces.shape) - verts, faces = mesh_from_quads(x,y,z) - face_colors = np.empty((faces.shape[0], 4)) - face_colors[0::2,0] = d.flat - face_colors[0::2,1] = d.flat - face_colors[0::2,2] = d.flat - face_colors[1::2,0] = d.flat - face_colors[1::2,1] = d.flat - face_colors[1::2,2] = d.flat + # Face_colors increases first in range, then in azimuth. + # d[azidx, ridx] + # The triangles receive colors with the lower triangles all in a row, then the upper triangles all in a row, + # which completes all the triangles in range. + # For six azimuths and five ranges, this is [d[0,:], d[0,:]] for the first azimuth, + + squashed = d.flatten() + face_colors = np.empty((faces.shape[0], 4)) + if cm is None: + squashed -= squashed.min() + squashed /= (vmax-vmin) # squashed.max() + face_colors[0::2,0] = squashed + face_colors[0::2,1] = squashed + face_colors[0::2,2] = squashed + face_colors[1::2,0] = squashed + face_colors[1::2,1] = squashed + face_colors[1::2,2] = squashed face_colors[:,3] = 1.0 # transparency - mdata = MeshData(vertices=verts, faces=faces, face_colors=face_colors) - mesh = Mesh(meshdata=mdata) - mesh.transform = vispy.scene.transforms.MatrixTransform() - mesh.transform.scale([1./10, 1./10, 1./10]) + else: + colors = cm.to_rgba(squashed) + face_colors[0::2] = colors + face_colors[1::2] = colors + + if dup_verts: + n_repeat=2 + else: + n_repeat=1 + xv = np.repeat(np.repeat(x,n_repeat,axis=0), n_repeat, axis=1) + dv = np.zeros_like(xv) + dv[1:-1, 1:-1] = np.repeat(np.repeat(d,n_repeat,axis=0), n_repeat, axis=1) + + squashed = dv.flatten() + if cm is None: + vert_colors = np.ones((verts.shape[0], 4)) + squashed -= squashed.min() + squashed /= (vmax-vmin) # squashed.max() + vert_colors[:,0] = squashed + vert_colors[:,1] = squashed + vert_colors[:,2] = squashed + else: + vert_colors = cm.to_rgba(squashed) - vispy.scene.SceneCanvas.__init__(self, keys='interactive') - - self.size = (800, 800) - self.show() - view = self.central_widget.add_view() - # view.set_camera('turntable', mode='ortho', up='z', distance=2) - view.camera='turntable' - view.camera.mode='ortho' - view.camera.up='z' - view.camera.distance=20 - view.add(mesh) - - -if __name__ == '__main__': - win = Canvas() - import sys - if sys.flags.interactive != 1: - vispy.app.run() + colors=vert_colors + + return verts, faces, colors \ No newline at end of file diff --git a/radar/vispy_radar_loop_demo.py b/radar/vispy_radar_loop_demo.py index 9c47125..e11c192 100644 --- a/radar/vispy_radar_loop_demo.py +++ b/radar/vispy_radar_loop_demo.py @@ -1,9 +1,11 @@ +from __future__ import absolute_import +from __future__ import print_function import numpy as np import datetime -from collection import RadarFileCollection +from .collection import RadarFileCollection from pyart.graph.common import sweep_coords_to_cart, corner_to_point -from quadmesh_geometry import mesh_from_quads, radar_example_data +from .quadmesh_geometry import mesh_from_quads, radar_example_data from vispy import gloo import vispy diff --git a/setup.py b/setup.py index d719faf..846ffc7 100644 --- a/setup.py +++ b/setup.py @@ -1,3 +1,4 @@ +from __future__ import absolute_import from distutils.core import setup setup(name='brawl4d',