Ejemplo n.º 1
0
class Dial(ValueIndicator):
    """
    A Dial represents a value in some range as a position on an
    annular dial. It is similar to a Gauge but more minimal visually.
    """

    annulus_width = param.Number(default=0.2,
                                 doc="""
      Width of the radial annulus as a fraction of the total.""")

    bounds = param.Range(default=(0, 100),
                         doc="""
      The upper and lower bound of the dial.""")

    colors = param.List(default=None,
                        doc="""
      Color thresholds for the Dial, specified as a list of tuples
      of the fractional threshold and the color to switch to.""")

    default_color = param.String(default='lightblue',
                                 doc="""
      Color of the radial annulus if not color thresholds are supplied.""")

    end_angle = param.Number(default=25,
                             doc="""
      Angle at which the dial ends.""")

    format = param.String(default='{value}%',
                          doc="""
      Formatting string for the value indicator and lower/upper bounds.""")

    height = param.Integer(default=250, bounds=(1, None))

    nan_format = param.String(default='-',
                              doc="""
      How to format nan values.""")

    needle_color = param.String(default='black',
                                doc="""
      Color of the Dial needle.""")

    needle_width = param.Number(default=0.1,
                                doc="""
      Radial width of the needle.""")

    start_angle = param.Number(default=-205,
                               doc="""
      Angle at which the dial starts.""")

    tick_size = param.String(default=None,
                             doc="""
      Font size of the Dial min/max labels.""")

    title_size = param.String(default=None,
                              doc="""
      Font size of the Dial title.""")

    unfilled_color = param.String(default='whitesmoke',
                                  doc="""
      Color of the unfilled region of the Dial.""")

    value_size = param.String(default=None,
                              doc="""
      Font size of the Dial value label.""")

    value = param.Number(default=25,
                         allow_None=True,
                         doc="""
      Value to indicate on the dial a value within the declared bounds.""")

    width = param.Integer(default=250, bounds=(1, None))

    _manual_params = [
        'value', 'start_angle', 'end_angle', 'bounds', 'annulus_width',
        'format', 'background', 'needle_width', 'tick_size', 'title_size',
        'value_size', 'colors', 'default_color', 'unfilled_color', 'height',
        'width', 'nan_format', 'needle_color'
    ]

    _data_params = _manual_params

    _rename = {'background': 'background_fill_color'}

    def __init__(self, **params):
        super().__init__(**params)
        self._update_value_bounds()

    @param.depends('bounds', watch=True)
    def _update_value_bounds(self):
        self.param.value.bounds = self.bounds

    def _get_data(self):
        vmin, vmax = self.bounds
        value = self.value
        if value is None:
            value = float('nan')
        fraction = (value - vmin) / (vmax - vmin)
        start = (np.radians(360 - self.start_angle) - pi % (2 * pi)) + pi
        end = (np.radians(360 - self.end_angle) - pi % (2 * pi)) + pi
        distance = (abs(end - start) % (pi * 2))
        if end > start:
            distance = (pi * 2) - distance
        radial_fraction = distance * fraction
        angle = start if np.isnan(fraction) else (start - radial_fraction)
        inner_radius = 1 - self.annulus_width

        color = self.default_color
        for val, clr in (self.colors or [])[::-1]:
            if fraction <= val:
                color = clr

        annulus_data = {
            'starts': np.array([start, angle]),
            'ends': np.array([angle, end]),
            'color': [color, self.unfilled_color],
            'radius': np.array([inner_radius, inner_radius])
        }

        x0s, y0s, x1s, y1s, clrs = [], [], [], [], []
        colors = self.colors or []
        for (val, _), (_, clr) in zip(colors[:-1], colors[1:]):
            tangle = start - (distance * val)
            if (vmin + val * (vmax - vmin)) <= value:
                continue
            x0, y0 = np.cos(tangle), np.sin(tangle)
            x1, y1 = x0 * inner_radius, y0 * inner_radius
            x0s.append(x0)
            y0s.append(y0)
            x1s.append(x1)
            y1s.append(y1)
            clrs.append(clr)

        threshold_data = {
            'x0': x0s,
            'y0': y0s,
            'x1': x1s,
            'y1': y1s,
            'color': clrs
        }

        center_radius = 1 - self.annulus_width / 2.
        x, y = np.cos(angle) * center_radius, np.sin(angle) * center_radius
        needle_start = pi + angle - (self.needle_width / 2.)
        needle_end = pi + angle + (self.needle_width / 2.)
        needle_data = {
            'x': np.array([x]),
            'y': np.array([y]),
            'start': np.array([needle_start]),
            'end': np.array([needle_end]),
            'radius': np.array([center_radius])
        }

        value = self.format.format(value=value).replace('nan', self.nan_format)
        min_value = self.format.format(value=vmin)
        max_value = self.format.format(value=vmax)
        tminx, tminy = np.cos(start) * center_radius, np.sin(
            start) * center_radius
        tmaxx, tmaxy = np.cos(end) * center_radius, np.sin(end) * center_radius
        tmin_angle, tmax_angle = start + pi, end + pi % pi
        scale = (self.height / 400)
        title_size = self.title_size if self.title_size else '%spt' % (scale *
                                                                       32)
        value_size = self.value_size if self.value_size else '%spt' % (scale *
                                                                       48)
        tick_size = self.tick_size if self.tick_size else '%spt' % (scale * 18)

        text_data = {
            'x': np.array([0, 0, tminx, tmaxx]),
            'y': np.array([-.2, -.5, tminy, tmaxy]),
            'text': [self.name, value, min_value, max_value],
            'rot': np.array([0, 0, tmin_angle, tmax_angle]),
            'size': [title_size, value_size, tick_size, tick_size],
            'color': ['black', color, 'black', 'black']
        }
        return annulus_data, needle_data, threshold_data, text_data

    def _get_model(self, doc, root=None, parent=None, comm=None):
        params = self._process_param_change(self._init_params())
        model = figure(x_range=(-1, 1),
                       y_range=(-1, 1),
                       tools=[],
                       outline_line_color=None,
                       toolbar_location=None,
                       width=self.width,
                       height=self.height,
                       **params)
        model.xaxis.visible = False
        model.yaxis.visible = False
        model.grid.visible = False

        annulus, needle, threshold, text = self._get_data()

        # Draw annulus
        annulus_source = ColumnDataSource(data=annulus, name='annulus_source')
        model.annular_wedge(x=0,
                            y=0,
                            inner_radius='radius',
                            outer_radius=1,
                            start_angle='starts',
                            end_angle='ends',
                            line_color='gray',
                            color='color',
                            direction='clock',
                            source=annulus_source)

        # Draw needle
        needle_source = ColumnDataSource(data=needle, name='needle_source')
        model.wedge(x='x',
                    y='y',
                    radius='radius',
                    start_angle='start',
                    end_angle='end',
                    fill_color=self.needle_color,
                    line_color=self.needle_color,
                    source=needle_source,
                    name='needle_renderer')

        # Draw thresholds
        threshold_source = ColumnDataSource(data=threshold,
                                            name='threshold_source')
        model.segment(x0='x0',
                      x1='x1',
                      y0='y0',
                      y1='y1',
                      line_color='color',
                      source=threshold_source,
                      line_width=2)

        # Draw labels
        text_source = ColumnDataSource(data=text, name='label_source')
        model.text(x='x',
                   y='y',
                   text='text',
                   font_size='size',
                   text_align='center',
                   text_color='color',
                   source=text_source,
                   text_baseline='top',
                   angle='rot')

        if root is None:
            root = model
        self._models[root.ref['id']] = (model, parent)
        return model

    def _manual_update(self, events, model, doc, root, parent, comm):
        update_data = False
        for event in events:
            if event.name in ('width', 'height'):
                model.update(**{event.name: event.new})
            if event.name in self._data_params:
                update_data = True
            elif event.name == 'needle_color':
                needle_r = model.select(name='needle_renderer')
                needle_r.glyph.line_color = event.new
                needle_r.glyph.fill_color = event.new
        if not update_data:
            return
        annulus, needle, threshold, labels = self._get_data()
        model.select(name='annulus_source').data.update(annulus)
        model.select(name='needle_source').data.update(needle)
        model.select(name='threshold_source').data.update(threshold)
        model.select(name='label_source').data.update(labels)
Ejemplo n.º 2
0
class ChannelViewer(BaseViewer):

    cmap = param.Selector(list(available_cmaps.keys()), doc='Colormap name')
    # actual colormap, needed to handle named colormaps and instances of colormap
    _cmap = param.Parameter(next(iter(available_cmaps.values())))
    intensity_bounds = param.Range(
        doc=
        'Image clipping bounds. If not specified, imgae (min,max) will be used'
    )
    slider_limits = param.NumericTuple(
        (0, 2**16 - 1), doc='(min,max) values for intensity slider')
    opacity = param.Number(1.,
                           bounds=(0., 1.),
                           step=0.01,
                           doc='Channel opacity',
                           instantiate=True)
    bitdepth = param.Selector(
        default=8,
        objects=[8, 16, 'int8', 'int16'],
        doc=
        'bitdepth of the rasterized image. 16 bits is useful for labels with object above 255'
    )
    raster_aggregator = param.String(
        'default',
        doc=
        'Aggreation method to downsample the image. e.g. use "first" for labels'
    )

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._set_intensity_bounds_limits(self.slider_limits)
        self._watch_selected_cmap()

    @param.depends('cmap', watch=True)
    def _watch_selected_cmap(self):
        self._cmap = available_cmaps[self.cmap]

    def _set_intensity_bounds_limits(self, limits):
        '''Updates the bounds of intensity slider and change current value if out of bound'''

        if self.intensity_bounds and self.intensity_bounds[1] > limits[1]:
            self.intensity_bounds = (self.intensity_bounds[0], limits[1])

        elif self.intensity_bounds and self.intensity_bounds[0] < limits[0]:
            self.intensity_bounds = (limits[0], self.intensity_bounds[1])

        self.param.intensity_bounds.bounds = limits

    @param.depends('intensity_bounds')
    def _update_intensity_bounds(self, elem):

        xs = elem.dimension_values(0, expanded=False)
        ys = elem.dimension_values(1, expanded=False)
        img = elem.dimension_values(2, flat=False)
        dtype = {
            8: np.uint8,
            16: np.uint16,
            'int8': np.int8,
            'int16': np.int16
        }[self.bitdepth]

        bounds = self.intensity_bounds
        if bounds is None:
            bounds = (img.min(), img.max())

        img = rescale_intensity(img, in_range=bounds,
                                out_range=dtype).astype(dtype)

        return elem.clone((xs, ys, img))

    @param.depends()
    def _set_aspect_ratio(self, elem):
        # hack to avoid bug with rasterize + invert_yaxis + aspect='equal'
        # manually set data_aspect=1

        options = elem.opts.get().options
        frame_w = options.get('frame_width', None)
        frame_h = options.get('frame_height', None)

        if frame_w and frame_h:
            # already set
            return elem

        wstart, wstop = elem.dimension_values(0, expanded=False)[[0, -1]]
        hstart, hstop = elem.dimension_values(1, expanded=False)[[0, -1]]

        w = wstop - wstart
        h = hstop - hstart

        if frame_w:
            return elem.opts(
                opts.Image(frame_height=int(round(frame_w / w * h))))
        elif frame_h:
            return elem.opts(
                opts.Image(frame_width=int(round(frame_h / h * w))))
        else:
            return elem

    def _call(self, dmap):
        dmap = dmap.apply(lambda ds: ds.to(hv.Image, group='channel'))
        dmap = rasterize(dmap, aggregator=self.raster_aggregator, expand=False)
        dmap = dmap.apply(self._set_aspect_ratio)
        dmap = dmap.apply(self._update_intensity_bounds)
        return dmap.apply.opts(cmap=self.param._cmap, alpha=self.param.opacity)

    def widgets(self):
        return pn.WidgetBox(self.param.cmap, self.param.intensity_bounds,
                            self.param.opacity)
Ejemplo n.º 3
0
class Gauge(ValueIndicator):
    """
    A Gauge represents a value in some range as a position on
    speedometer or gauge. It is similar to a Dial but visually a lot
    busier.
    """

    annulus_width = param.Integer(default=10,
                                  doc="""
      Width of the gauge annulus.""")

    bounds = param.Range(default=(0, 100),
                         doc="""
      The upper and lower bound of the dial.""")

    colors = param.List(default=None,
                        doc="""
      Color thresholds for the Gauge, specified as a list of tuples
      of the fractional threshold and the color to switch to.""")

    custom_opts = param.Dict(doc="""
      Additional options to pass to the ECharts Gauge definition.""")

    height = param.Integer(default=300, bounds=(0, None))

    end_angle = param.Number(default=-45,
                             doc="""
      Angle at which the gauge ends.""")

    format = param.String(default='{value}%',
                          doc="""
      Formatting string for the value indicator.""")

    num_splits = param.Integer(default=10,
                               doc="""
      Number of splits along the gauge.""")

    show_ticks = param.Boolean(default=True,
                               doc="""
      Whether to show ticks along the dials.""")

    show_labels = param.Boolean(default=True,
                                doc="""
      Whether to show tick labels along the dials.""")

    start_angle = param.Number(default=225,
                               doc="""
      Angle at which the gauge starts.""")

    tooltip_format = param.String(default='{b} : {c}%',
                                  doc="""
      Formatting string for the hover tooltip.""")

    title_size = param.Integer(default=18,
                               doc="""
      Size of title font.""")

    value = param.Number(default=25,
                         doc="""
      Value to indicate on the gauge a value within the declared bounds.""")

    width = param.Integer(default=300, bounds=(0, None))

    _rename = {}

    _source_transforms = {
        'annulus_width': None,
        'bounds': None,
        'colors': None,
        'custom_opts': None,
        'end_angle': None,
        'format': None,
        'num_splits': None,
        'show_ticks': None,
        'show_labels': None,
        'start_angle': None,
        'tooltip_format': None,
        'title_size': None,
        'value': None
    }

    @property
    def _widget_type(self):
        if 'panel.models.echarts' not in sys.modules:
            from ..models.echarts import ECharts
        else:
            ECharts = getattr(sys.modules['panel.models.echarts'], 'ECharts')
        return ECharts

    def __init__(self, **params):
        super().__init__(**params)
        self._update_value_bounds()

    @param.depends('bounds', watch=True)
    def _update_value_bounds(self):
        self.param.value.bounds = self.bounds

    def _process_param_change(self, msg):
        msg = super()._process_param_change(msg)
        vmin, vmax = msg.pop('bounds', self.bounds)
        msg['data'] = {
            'tooltip': {
                'formatter': msg.pop('tooltip_format', self.tooltip_format)
            },
            'series': [{
                'name':
                'Gauge',
                'type':
                'gauge',
                'axisTick': {
                    'show': msg.pop('show_ticks', self.show_ticks)
                },
                'axisLabel': {
                    'show': msg.pop('show_labels', self.show_labels)
                },
                'title': {
                    'fontWeight': 'bold',
                    'fontSize': msg.pop('title_size', self.title_size)
                },
                'splitLine': {
                    'show': True
                },
                'radius':
                '100%',
                'detail': {
                    'formatter': msg.pop('format', self.format)
                },
                'min':
                vmin,
                'max':
                vmax,
                'startAngle':
                msg.pop('start_angle', self.start_angle),
                'endAngle':
                msg.pop('end_angle', self.end_angle),
                'splitNumber':
                msg.pop('num_splits', self.num_splits),
                'data': [{
                    'value': msg.pop('value', self.value),
                    'name': self.name
                }],
                'axisLine': {
                    'lineStyle': {
                        'width': msg.pop('annulus_width', self.annulus_width),
                    }
                }
            }]
        }
        colors = msg.pop('colors', self.colors)
        if colors:
            msg['data']['series'][0]['axisLine']['lineStyle']['color'] = colors
        custom_opts = msg.pop('custom_opts', self.custom_opts)
        if custom_opts:
            gauge = msg['data']['series'][0]
            for k, v in custom_opts.items():
                if k not in gauge or not isinstance(gauge[k], dict):
                    gauge[k] = v
                else:
                    gauge[k].update(v)
        return msg
Ejemplo n.º 4
0
class SagSwellExplorer(hv.streams.Stream):

    alpha = param.Magnitude(default=0.75,
                            doc="Alpha value for the map opacity")
    plot = param.ObjectSelector(default="Sag", objects=["Sag", "Swell"])
    colormap = param.ObjectSelector(default=cm["fire"], objects=cm.values())
    numEvents = param.Range(default=(1, 300),
                            bounds=(1, 300),
                            doc="""Filter for event count""")
    ByDay = param.Boolean(False, doc="Filter By Day")
    DayNum = param.Integer(
        1,
        bounds=(1, 15))  # Stop at 15 since that's all the data we have loaded
    ByFeeder = param.Boolean(False, doc="Filter By Feeder")
    Feeder = param.ObjectSelector(
        default="28GM012002",
        objects=df.FEEDER_ID.sort_values().unique().tolist())
    BySUB = param.Boolean(False, doc="Filter By SUB")
    Substations = param.ObjectSelector(
        default="28GM", objects=df.SUB.sort_values().unique().tolist())
    maxpix = param.Integer(12)
    threshhold = param.Number(0.6, bounds=(0.1, 1.0))

    def make_view(self, x_range=None, y_range=None, **kwargs):
        #map_tiles = tiles.opts(style=dict(alpha=self.alpha), plot=options)

        points = hv.Points(
            df,
            kdims=['X_CORD', 'Y_CORD'],
            vdims=['EVENT_COUNT', 'EventType', 'SUB', 'day', 'FEEDER_ID'])

        if (self.BySUB & self.ByFeeder & self.ByDay):
            selected = points.select(EventType=self.plot,
                                     EVENT_COUNT=self.numEvents,
                                     SUB=self.Substations,
                                     day=self.DayNum,
                                     FEEDER_ID=self.Feeder)
        elif (self.BySUB & self.ByFeeder & (not self.ByDay)):
            selected = points.select(EventType=self.plot,
                                     EVENT_COUNT=self.numEvents,
                                     SUB=self.Substations,
                                     FEEDER_ID=self.Feeder)
        elif (self.BySUB & (not self.ByFeeder) & self.ByDay):
            selected = points.select(EventType=self.plot,
                                     EVENT_COUNT=self.numEvents,
                                     SUB=self.Substations,
                                     day=self.DayNum)
        elif (self.BySUB & (not self.ByFeeder) & (not self.ByDay)):
            selected = points.select(EventType=self.plot,
                                     EVENT_COUNT=self.numEvents,
                                     SUB=self.Substations)
        elif ((not self.BySUB) & self.ByFeeder & self.ByDay):
            selected = points.select(EventType=self.plot,
                                     EVENT_COUNT=self.numEvents,
                                     day=self.DayNum,
                                     FEEDER_ID=self.Feeder)
        elif ((not self.BySUB) & self.ByFeeder & (not self.ByDay)):
            selected = points.select(EventType=self.plot,
                                     EVENT_COUNT=self.numEvents,
                                     FEEDER_ID=self.Feeder)
        elif ((not self.BySUB) & (not self.ByFeeder) & self.ByDay):
            selected = points.select(EventType=self.plot,
                                     EVENT_COUNT=self.numEvents,
                                     day=self.DayNum)
        else:
            selected = points.select(EventType=self.plot,
                                     EVENT_COUNT=self.numEvents)

        SagSwellPts = datashade(selected,
                                x_sampling=1,
                                y_sampling=1,
                                cmap=self.colormap,
                                dynamic=False,
                                x_range=x_range,
                                y_range=y_range,
                                width=640,
                                height=380)
        dsss = dynspread(SagSwellPts,
                         shape='circle',
                         max_px=self.maxpix,
                         threshold=self.threshhold)
        #return map_tiles * dsss
        return dsss

    def jtdp(self, x_range, y_range, **kwargs):

        pointdec = hv.Points(df,
                             kdims=['X_CORD', 'Y_CORD'],
                             vdims=['EVENT_COUNT', 'FEEDER_ID'])
        selecteddec = pointdec.select(EventType=self.plot,
                                      EVENT_COUNT=self.numEvents)
        dm2 = decimate(
            selecteddec, x_range=x_range, y_range=y_range, dynamic=False
        ).opts(
            style={'Points': dict(alpha=0.0, color='blue', size=self.maxpix)})
        return dm2

    def dec_tab(self, x_range, y_range, bounds, **kwargs):

        #%opts Table [ fig_size=550 width=600 height=380]

        b0 = bounds[0]
        b2 = bounds[2]
        b1 = bounds[1]
        b3 = bounds[3]

        xr = bounds[2] - bounds[0]
        yr = bounds[3] - bounds[1]

        if (not ((xr < 50000) & (yr < 50000))):
            b0 = b2 = b1 = b3 = 0.0
            win32api.MessageBox(0, "SELECTED AREA TOO LARGE! ", 'dec_tab',
                                0x00001000)

        pointdec = hv.Points(df,
                             kdims=['X_CORD', 'Y_CORD'],
                             vdims=[
                                 'EVENT_COUNT', 'EventType', 'SUB', 'day',
                                 'FEEDER_ID', 'XFMR', 'Phase'
                             ])

        if (self.BySUB & self.ByFeeder & self.ByDay):
            selected = pointdec.select(X_CORD=(b0, b2),
                                       Y_CORD=(b1, b3),
                                       EventType=self.plot,
                                       EVENT_COUNT=self.numEvents,
                                       SUB=self.Substations,
                                       day=self.DayNum,
                                       FEEDER_ID=self.Feeder)
        elif (self.BySUB & self.ByFeeder & (not self.ByDay)):
            selected = pointdec.select(X_CORD=(b0, b2),
                                       Y_CORD=(b1, b3),
                                       EventType=self.plot,
                                       EVENT_COUNT=self.numEvents,
                                       SUB=self.Substations,
                                       FEEDER_ID=self.Feeder)
        elif (self.BySUB & (not self.ByFeeder) & self.ByDay):
            selected = pointdec.select(X_CORD=(b0, b2),
                                       Y_CORD=(b1, b3),
                                       EventType=self.plot,
                                       EVENT_COUNT=self.numEvents,
                                       SUB=self.Substations,
                                       day=self.DayNum)
        elif (self.BySUB & (not self.ByFeeder) & (not self.ByDay)):
            selected = pointdec.select(X_CORD=(b0, b2),
                                       Y_CORD=(b1, b3),
                                       EventType=self.plot,
                                       EVENT_COUNT=self.numEvents,
                                       SUB=self.Substations)
        elif ((not self.BySUB) & self.ByFeeder & self.ByDay):
            selected = pointdec.select(X_CORD=(b0, b2),
                                       Y_CORD=(b1, b3),
                                       EventType=self.plot,
                                       EVENT_COUNT=self.numEvents,
                                       day=self.DayNum,
                                       FEEDER_ID=self.Feeder)
        elif ((not self.BySUB) & self.ByFeeder & (not self.ByDay)):
            selected = pointdec.select(X_CORD=(b0, b2),
                                       Y_CORD=(b1, b3),
                                       EventType=self.plot,
                                       EVENT_COUNT=self.numEvents,
                                       FEEDER_ID=self.Feeder)
        elif ((not self.BySUB) & (not self.ByFeeder) & self.ByDay):
            selected = pointdec.select(X_CORD=(b0, b2),
                                       Y_CORD=(b1, b3),
                                       EventType=self.plot,
                                       EVENT_COUNT=self.numEvents,
                                       day=self.DayNum)
        else:
            selected = pointdec.select(X_CORD=(b0, b2),
                                       Y_CORD=(b1, b3),
                                       EventType=self.plot,
                                       EVENT_COUNT=self.numEvents)

        #bp=selected.select( X_CORD=(b0, b2),Y_CORD=(b1, b3)  )
        tab = hv.Table(
            selected,
            kdims=[],
            vdims=['EventType', 'SUB', 'FEEDER_ID', 'XFMR', 'Phase'])

        return tab
Ejemplo n.º 5
0
class MyParamRange(param.Parameterized):
    my_ranges = param.Range(default=(-50, 50))
Ejemplo n.º 6
0
 class Q(param.Parameterized):
     q = param.Range((0, 2), bounds=(0, 1))
Ejemplo n.º 7
0
class LinearGauge(ValueIndicator):
    """
    A LinearGauge represents a value in some range as a position on an
    linear scale. It is similar to a Dial/Gauge but visually more
    compact.

    Reference: https://panel.holoviz.org/reference/indicators/LinearGauge.html

    :Example:

    >>> LinearGauge(value=30, default_color='red', bounds=(0, 100))
    """

    bounds = param.Range(default=(0, 100), doc="""
      The upper and lower bound of the gauge.""")

    default_color = param.String(default='lightblue', doc="""
      Color of the radial annulus if not color thresholds are supplied.""")

    colors = param.Parameter(default=None, doc="""
      Color thresholds for the gauge, specified as a list of tuples
      of the fractional threshold and the color to switch to.""")

    format = param.String(default='{value:.2f}%', doc="""
      Formatting string for the value indicator and lower/upper bounds.""")

    height = param.Integer(default=300, bounds=(1, None))

    horizontal = param.Boolean(default=False, doc="""
      Whether to display the linear gauge horizontally.""")

    nan_format = param.String(default='-', doc="""
      How to format nan values.""")

    needle_color = param.String(default='black', doc="""
      Color of the gauge needle.""")

    show_boundaries = param.Boolean(default=False, doc="""
      Whether to show the boundaries between colored regions.""")

    unfilled_color = param.String(default='whitesmoke', doc="""
      Color of the unfilled region of the LinearGauge.""")

    title_size = param.String(default=None, doc="""
      Font size of the gauge title.""")

    tick_size = param.String(default=None, doc="""
      Font size of the gauge tick labels.""")

    value_size = param.String(default=None, doc="""
      Font size of the gauge value label.""")

    value = param.Number(default=25, allow_None=True, doc="""
      Value to indicate on the dial a value within the declared bounds.""")

    width = param.Integer(default=125, bounds=(1, None))

    _manual_params = [
        'value', 'bounds', 'format', 'title_size', 'value_size',
        'horizontal', 'height', 'colors', 'tick_size',
        'unfilled_color', 'width', 'nan_format', 'needle_color'
    ]

    _data_params = [
        'value', 'bounds', 'format', 'nan_format', 'needle_color',
        'colors'
    ]

    _rerender_params = ['horizontal']

    _rename = {
        'background': 'background_fill_color', 'show_boundaries': None,
        'default_color': None
    }

    _updates = False

    def __init__(self, **params):
        super().__init__(**params)
        self._update_value_bounds()

    @param.depends('bounds', watch=True)
    def _update_value_bounds(self):
        self.param.value.bounds = self.bounds

    @property
    def _color_intervals(self):
        vmin, vmax = self.bounds
        value = self.value
        ncolors = len(self.colors) if self.colors else 1
        interval = (vmax-vmin)
        if math.isfinite(value):
            fraction = value / interval
            idx = round(fraction * (ncolors-1))
        else:
            fraction = 0
            idx = 0
        if not self.colors:
            intervals = [
                (fraction, self.default_color)
            ]
            intervals.append((1, self.unfilled_color))
        elif self.show_boundaries:
            intervals = [
                c if isinstance(c, tuple) else ((i+1)/(ncolors), c)
                for i, c in enumerate(self.colors)
            ]
        else:
            intervals = [
                self.colors[idx] if isinstance(self.colors[0], tuple)
                else (fraction, self.colors[idx])
            ]
            intervals.append((1, self.unfilled_color))
        return intervals

    def _get_data(self):
        vmin, vmax = self.bounds
        value = self.value
        interval = (vmax-vmin)
        colors, values = [], [vmin]
        above = False
        prev = None
        for (v, color) in self._color_intervals:
            val = v*interval
            if val == prev:
                continue
            elif val > value:
                if not above:
                    colors.append(color)
                    values.append(value)
                    above = True
                color = self.unfilled_color
            colors.append(color)
            values.append(val)
            prev = val
        value = self.format.format(value=value).replace('nan', self.nan_format)
        return (
            {'y0': values[:-1], 'y1': values[1:], 'color': colors},
            {'y': [self.value], 'text': [value]}
        )

    def _get_model(self, doc, root=None, parent=None, comm=None):
        params = self._process_param_change(self._init_params())
        model = figure(
            outline_line_color=None, toolbar_location=None, tools=[],
            x_axis_location='above', y_axis_location='right', **params
        )
        model.grid.visible = False
        model.xaxis.major_label_standoff = 2
        model.yaxis.major_label_standoff = 2
        model.xaxis.axis_label_standoff = 2
        model.yaxis.axis_label_standoff = 2
        self._update_name(model)
        self._update_title_size(model)
        self._update_tick_size(model)
        self._update_figure(model)
        self._update_axes(model)
        self._update_renderers(model)
        self._update_bounds(model)
        if root is None:
            root = model
        self._models[root.ref['id']] = (model, parent)
        return model

    def _update_name(self, model):
        model.xaxis.axis_label = self.name
        model.yaxis.axis_label = self.name

    def _update_title_size(self, model):
        title_size = self.title_size or f'{self.width/6}px'
        model.xaxis.axis_label_text_font_size = title_size
        model.yaxis.axis_label_text_font_size = title_size

    def _update_tick_size(self, model):
        tick_size = self.tick_size or f'{self.width/9}px'
        model.xaxis.major_label_text_font_size = tick_size
        model.yaxis.major_label_text_font_size = tick_size

    def _update_renderers(self, model):
        model.renderers = []
        data, needle_data = self._get_data()
        bar_source = ColumnDataSource(data=data, name='bar_source')
        needle_source = ColumnDataSource(data=needle_data, name='needle_source')
        if self.horizontal:
            model.hbar(
                y=0.1, left='y0', right='y1', height=1, color='color',
                source=bar_source
            )
            wedge_params = {'y': 0.5, 'x': 'y', 'angle': np.deg2rad(180)}
            text_params = {
                'y': -0.4, 'x': 0, 'text_align': 'left',
                'text_baseline': 'top'
            }
        else:
            model.vbar(
                x=0.1, bottom='y0', top='y1', width=0.9, color='color',
                source=bar_source
            )
            wedge_params = {'x': 0.5, 'y': 'y', 'angle': np.deg2rad(90)}
            text_params = {
                'x': -0.4, 'y': 0, 'text_align': 'left',
                'text_baseline': 'bottom', 'angle': np.deg2rad(90)
            }
        model.scatter(
            fill_color=self.needle_color, line_color=self.needle_color,
            source=needle_source, name='needle_renderer', marker='triangle',
            size=int(self.width/8), level='overlay', **wedge_params
        )
        value_size = self.value_size or f'{self.width/8}px'
        model.text(
            text='text', source=needle_source, text_font_size=value_size,
            **text_params
        )

    def _update_bounds(self, model):
        if self.horizontal:
            x_range, y_range = tuple(self.bounds), (-0.8, 0.5)
        else:
            x_range, y_range = (-0.8, 0.5), tuple(self.bounds)
        model.x_range.update(start=x_range[0], end=x_range[1])
        model.y_range.update(start=y_range[0], end=y_range[1])

    def _update_axes(self, model):
        vmin, vmax = self.bounds
        interval = (vmax-vmin)
        if self.show_boundaries:
            ticks = [vmin] + [v*interval for (v, _) in self._color_intervals]
        else:
            ticks = [vmin, vmax]
        ticker = FixedTicker(ticks=ticks)
        if self.horizontal:
            model.xaxis.visible = True
            model.xaxis.ticker = ticker
            model.yaxis.visible = False
        else:
            model.xaxis.visible = False
            model.yaxis.visible = True
            model.yaxis.ticker = ticker

    def _update_figure(self, model):
        params = self._process_param_change(self._init_params())
        if self.horizontal:
            params.update(width=self.height, height=self.width)
        else:
            params.update(width=self.width, height=self.height)
        model.update(**params)

    def _manual_update(self, events, model, doc, root, parent, comm):
        update_data = False
        for event in events:
            if event.name in ('width', 'height'):
                self._update_figure(model)
            elif event.name == 'bounds':
                self._update_bounds(model)
                self._update_renderers(model)
            elif event.name in self._data_params:
                update_data = True
            elif event.name == 'needle_color':
                needle_r = model.select(name='needle_renderer')
                needle_r.glyph.line_color = event.new
                needle_r.glyph.fill_color = event.new
            elif event.name == 'horizontal':
                self._update_bounds(model)
                self._update_figure(model)
                self._update_axes(model)
                self._update_renderers(model)
            elif event.name == 'name':
                self._update_name(model)
            elif event.name == 'tick_size':
                self._update_tick_size(model)
            elif event.name == 'title_size':
                self._update_title_size(model)
        if not update_data:
            return
        data, needle_data = self._get_data()
        model.select(name='bar_source').data.update(data)
        model.select(name='needle_source').data.update(needle_data)
Ejemplo n.º 8
0
class MedNumApp(param.Parameterized):
    localisation = param.String(
        default="Toulouse", label=""
    )  # default=["Toulouse"], objects=list(ifrag_cont_df_merged.nom_com.unique()), label='', doc="A string")
    score = param.Range(
        default=(0, 250),
        bounds=(0, 250),
    )  # , name="Score")
    interfaces_num = param.ListSelector(label="")
    infos_num = param.ListSelector(label="")

    comp_admin = param.ListSelector(label="")
    comp_usage_num = param.ListSelector(label="")

    point_ref = param.Selector(
        objects=["Pays", "Région", "Département", "Intercommune", "Commune"],
        label="Point de référence",
    )

    donnees_infra = param.Action(lambda x: x,
                                 doc="""Données Infra-Communales""",
                                 precedence=0.7)
    export_data = param.Action(
        lambda x: x.timestamps.append(dt.datetime.utcnow()),
        doc="""Exporter les résultats""",
        precedence=0.7,
    )
    edit_report = param.Action(
        lambda x: x.timestamps.append(dt.datetime.utcnow()),
        doc="""Editer un rapport""",
        precedence=0.7,
    )
    tiles = StamenTerrain()

    def __init__(self, **params):
        super(MedNumApp, self).__init__(**params)
        self.param.interfaces_num.objects = OPTIONS_INT_NUM
        self.param.infos_num.objects = OPTIONS_X_INFOS

        self.param.comp_admin.objects = OPTIONS_X_COMP_ADMIN
        self.param.comp_usage_num.objects = OPTIONS_X_COMP_USAGE

        indic_w_g_value_1 = {
            "name":
            "indic1_1",
            "indicators": [
                dict(name="accès", main=True, value=85, max_value=100),
                dict(name="information", value=118),
                # dict(name="indic3", value=168),
                dict(name="Interfaces", value=53),
            ],
        }

        indic_w_g_value_2 = {
            "indicators": [
                dict(name="Compétences", main=True, value=135, max_value=180),
                dict(name="indic3_2", value=115),
                dict(name="indic4", value=155),
            ]
        }

        self.indicator_w_gauge_1 = IndicatorsWithGauge(**indic_w_g_value_1)
        self.indicator_w_gauge_2 = IndicatorsWithGauge(**indic_w_g_value_2)
        self.indicator_glob_stats = self.glob_stats()

    def lat_widgets(self):
        score_panel = pn.Column("# Score", self.param.score)
        point_ref_panel = pn.Column(
            "# Point de reference",
            pn.Param(
                self.param.point_ref,
                widgets={
                    "point_ref": pn.widgets.RadioBoxGroup,
                },
            ),
        )
        export_panel = pn.Column("# Aller plus loin", self.param.export_data,
                                 self.param.edit_report)

        localisation_panel = pn.Column("# Localisation",
                                       self.param.localisation)
        spec_interfaces = {
            "interfaces_num": pn.widgets.TreeViewCheckBox,
            "infos_num": pn.widgets.TreeViewCheckBox,
            "comp_admin": pn.widgets.TreeViewCheckBox,
            "comp_usage_num": pn.widgets.TreeViewCheckBox,
        }

        g_params = [
            pn.Param(self.param[p], widgets={p: w})
            for p, w in spec_interfaces.items()
        ]
        pn.Column(*g_params)
        indicateurs = pn.Column("# Indicateurs", *g_params)

        ordered_panel = pn.Column(
            localisation_panel,
            score_panel,
            indicateurs,
            point_ref_panel,
            export_panel,
            width=400,
        )

        return ordered_panel

    def link_ctrl_params_to_indic_params(self):
        try:
            return self.indicator_w_gauge.view
        except:
            return ""

    @pn.depends('score', watch=True)
    def glob_stats(self):
        label = "Score Global"
        score_min, score_max = self.score
        HTML = """
        <h1>{loc}</h1>
        <b>{score_name}</b> | {score_min}->{score_max}<br>

        <b>{pop_name}</b> | {population}
        """.format(loc=self.localisation,
                   score_name=label,
                   score_min=score_min,
                   score_max=score_max,
                   pop_name="Population",
                   population="Beaucoup !!")
        return pn.pane.HTML(HTML)

    def top_panel(self):
        try:
            return pn.Row(self.indicator_glob_stats,
                          self.indicator_w_gauge_1.view,
                          self.indicator_w_gauge_2.view,
                          css_classes=["top-custom"],
                          height=120)
        except:
            return ""

    @param.depends("localisation")  # , interfaces_num)
    def plot(self):

        commune_plot = gv.Polygons(
            ifrag_cont_df_merged[ifrag_cont_df_merged.nom_com ==
                                 self.localisation],
            vdims=vdims,
        )
        return self.tiles * commune_plot.opts(
            color=indice, width=600, height=600, fill_alpha=0.5)

    def view(self):
        self.view = pn.Row(
            self.lat_widgets(),
            pn.Spacer(width=10),
            pn.Column(self.top_panel, pn.Spacer(height=80), self.plot),
        )

    def panel(self):
        return self.lat_widgets()
Ejemplo n.º 9
0
class InterpolateMesh(param.Parameterized):
    """
    Loaded bathymetry scatterset must be in columnar format with column labels being 'x', 'y', 'z'
    """
    map_width = param.Integer(default=800, precedence=-1)
    map_height = param.Integer(default=600, precedence=-1)

    filepath = param.String(default=os.path.join(
        ROOTDIR, 'tests', 'test_files', 'SanDiego', 'SanDiego_bathy.csv'))
    load_data = param.Action(lambda self: self.param.trigger('load_data'),
                             label='Load',
                             precedence=1)

    scatter = param.DataFrame(default=pd.DataFrame(), precedence=-1)
    scatter_projection = param.ClassSelector(default=Projection(),
                                             class_=Projection)

    scatter_toggle = param.Boolean(
        default=False,
        doc='Toggle the visibility of the scatterset',
        label='View scatter',
        precedence=24)
    interp_button = param.Action(
        lambda self: self.param.trigger('interp_button'),
        label='Interpolate',
        precedence=25)

    interpolation_option = param.ObjectSelector(
        default='idw',
        objects=['linear', 'natural_neighbor', 'idw'],
        precedence=1)
    nodal_function = param.ObjectSelector(
        default='constant',
        objects=['constant', 'gradient_plane', 'quadratic'],
        precedence=1.1)
    truncation = param.Boolean(default=False, precedence=2)

    truncation_range = param.Range(default=(-10, 100),
                                   bounds=(None, None),
                                   softbounds=(-40, 150),
                                   label='',
                                   precedence=-1)

    adh_mod = param.ClassSelector(class_=AdhModel)

    cmap_opts = param.ClassSelector(default=ColormapOpts(),
                                    class_=ColormapOpts)
    display_range = param.ClassSelector(default=DisplayRangeOpts(),
                                        class_=DisplayRangeOpts)

    def __init__(self, adh_mod, **params):
        super(InterpolateMesh, self).__init__(adh_mod=adh_mod, **params)
        # set defaults for initialized example
        self.display_range.param.color_range.bounds = (10, 90)
        self.display_range.color_range = (10, 90)
        self.cmap_opts.colormap = cc.rainbow
        self.scatter_projection.set_crs(ccrs.GOOGLE_MERCATOR)
        self.adh_mod.wmts.source = gv.tile_sources.EsriImagery

        # print(self.projection.param.UTM_zone_hemi.constant, self.projection.crs_label)
        self.opts = (opts.Curve(height=self.map_height,
                                width=self.map_width,
                                xaxis=None,
                                line_width=1.50,
                                color='red',
                                tools=['hover']),
                     opts.Path(height=self.map_height,
                               width=self.map_width,
                               line_width=3,
                               color='black'),
                     opts.Image(height=self.map_height,
                                width=self.map_width,
                                cmap=self.cmap_opts.param.colormap,
                                clim=self.display_range.param.color_range,
                                colorbar=True,
                                clipping_colors={
                                    'NaN': 'transparent',
                                    'min': 'transparent'
                                },
                                axiswise=True),
                     opts.RGB(height=self.map_height, width=self.map_width),
                     opts.Points(height=self.map_height,
                                 width=self.map_width,
                                 color_index='z',
                                 cmap=self.cmap_opts.param.colormap,
                                 clim=self.display_range.param.color_range,
                                 size=10,
                                 tools=['hover'],
                                 padding=(0.1, 0.1),
                                 colorbar=True),
                     opts.TriMesh(height=self.map_height,
                                  width=self.map_width,
                                  color_index='z',
                                  cmap=self.cmap_opts.param.colormap,
                                  clim=self.display_range.param.color_range,
                                  tools=['hover'],
                                  padding=(0.1, 0.1),
                                  colorbar=True), opts.VLine(color='black'))
        # opts.defaults(*self.opts)

    @param.depends('load_data', watch=True)
    def _load(self):
        self.scatter = pd.read_csv(self.filepath,
                                   sep=',',
                                   names=['x', 'y', 'z'])
        self.scatter_pts = gv.Points(data=self.scatter,
                                     crs=self.scatter_projection.get_crs(),
                                     vdims=['z'],
                                     kdims=['x', 'y'])
        self.scatter_toggle = True

    @param.depends('truncation', watch=True)
    def _truncation(self):
        if self.truncation:
            self.param.truncation_range.precedence = 2.4
        else:
            self.param.truncation_range.precedence = -1

    @param.depends('interpolation_option', watch=True)
    def _update_nodal_function(self):
        if self.interpolation_option == 'linear':
            self.param.nodal_function.precedence = -1
        else:
            self.param.nodal_function.precedence = 1.1

    @param.depends('interp_button', watch=True)
    def interpolate(self):

        if self.interpolation_option != 'idw':
            interp_object = xmsinterp.interpolate.InterpLinear(
                pts=self.scatter.values)
            if self.interpolation_option == 'natural_neighbor':
                interp_object.interp_to_pt(
                    (0, 0))  # this will force triangle creation
                interp_object.set_use_natural_neighbor(
                    nodal_func_type=self.nodal_function,
                    nd_func_pt_search_opt="nearest_pts")
        else:
            interp_object = xmsinterp.interpolate.InterpIdw(
                pts=self.scatter.values)
            interp_object.set_nodal_function(
                nodal_func_type=self.nodal_function)

        if self.truncation:
            interp_object.set_truncation_max_min(self.truncation_range[1],
                                                 self.truncation_range[0])
        z = interp_object.interp_to_pts(
            self.adh_mod.mesh.mesh_points.data[['x', 'y']].values)

        self.adh_mod.mesh.mesh_points.data['z'] = z
        self.adh_mod.mesh.tri_mesh = gv.TriMesh(
            (self.adh_mod.mesh.tris[['v0', 'v1', 'v2']],
             self.adh_mod.mesh.mesh_points)).apply.opts(*self.opts)

        self.adh_mod.mesh.elevation_toggle = True

    # @param.depends('scatter_toggle', watch=True)
    def view_scatter(self):
        # print('view_scatter')
        if self.scatter_toggle and not self.scatter.empty:

            return self.scatter_pts.apply.opts(
                color_index='z',
                cmap=self.cmap_opts.param.colormap,
                clim=self.display_range.param.color_range,
                colorbar=True,
                marker='o',
                line_color=None)
        else:
            return Curve([])

    @param.depends('adh_mod.mesh.elements_toggle',
                   'adh_mod.mesh.elevation_toggle',
                   'interp_button',
                   'scatter_toggle',
                   watch=True)
    def view_map(self):
        # print('view_map method')

        if self.adh_mod.mesh.elevation_toggle:
            elevation = rasterize(self.adh_mod.mesh.tri_mesh,
                                  aggregator=ds.mean('z'),
                                  precompute=True).apply.opts(
                                      opts.Image(
                                          cmap=self.cmap_opts.colormap,
                                          clim=self.display_range.color_range,
                                          height=self.map_height,
                                          width=self.map_width))
        else:
            elevation = Curve([]).opts(height=self.map_height,
                                       width=self.map_width)

        # return self.adh_mod.mesh.view_bathy() * self.adh_mod.mesh.view_elements(line_color='yellow') * base_map * self.view_scatter()

        return elevation * self.adh_mod.mesh.view_elements(
            line_color='yellow') * hv.DynamicMap(
                self.adh_mod.wmts.view) * self.view_scatter()

    @param.output(adh_mod=AdhModel)
    def output(self):
        return self.adh_mod

    def panel(self):
        load_tab = pn.Column(
            pn.panel(self.param, parameters=['filepath'], show_name=False),
            pn.panel(self.scatter_projection.param, expand_button=False),
            pn.panel(self.param, parameters=['load_data'], show_name=False))

        map_pane = self.view_map

        interp_pane = pn.Column(
            pn.panel(self.param,
                     parameters=['interpolation_option', 'nodal_function'],
                     show_name=False),
            pn.Row(
                pn.panel(self.param,
                         parameters=['truncation', 'truncation_range'],
                         show_name=False), ),
            pn.panel(self.param, parameters=['interp_button'],
                     show_name=False))

        display_tab = pn.Column(
            pn.panel(self.cmap_opts.param,
                     parameters=['colormap'],
                     show_name=False),
            pn.panel(self.display_range.param,
                     parameters=['color_range'],
                     show_name=False),
            pn.panel(self.param,
                     parameters=['scatter_toggle'],
                     show_name=False),
            pn.panel(self.adh_mod.mesh.param,
                     parameters=['elements_toggle', 'elevation_toggle'],
                     show_name=False),
            pn.panel(self.adh_mod.wmts.param,
                     parameters=['source'],
                     expand_button=False,
                     show_name=False))

        tool_panel = pn.Tabs(('Load Data', load_tab), ('Display', display_tab),
                             ('Interpolate', interp_pane))

        return pn.Row(map_pane, tool_panel)
Ejemplo n.º 10
0
class SpatialSubsetter(NullSubsetter):
    latitude_range = param.Range(default=(-90, 90), bounds=(-90, 90))
    longitude_range = param.Range(default=(0, 360), bounds=(0, 360))
Ejemplo n.º 11
0
class ReactiveDashboard(param.Parameterized):
    title = pn.pane.Markdown("# Smart House Search")
    pins = param.List(default=[])
    lat_longs = param.List(default=[])
    # house_df = get_dummy_house_df()
    house_df = house_df_default
    hover = HoverTool(tooltips=TOOLTIPS)
    details_area = pn.pane.Markdown("# Details")
    price_range = get_price_range()
    minimum_price = param.Selector(objects=list(price_range))
    maximum_price = param.Selector(objects=list(price_range),
                                   default=price_range[-1])
    price_slider = param.Range(
        label='Price range',
        default=(options['price_min'], options['price_max']),
        bounds=(0, options['price_max']),
    )
    rooms_slider = param.Range(label='Bedrooms', default=(0, 7), bounds=(0, 7))
    bathrooms_slider = param.Range(label='Bathrooms',
                                   default=(0, 7),
                                   bounds=(0, 7))
    type = param.ListSelector(label='Type of property',
                              default=options['type'],
                              objects=options['type'])
    transit_time = param.Range(label='Transit time [mins]',
                               default=(0, options['transit_time_max']),
                               bounds=(0, options['transit_time_max']))

    map_background = hv.element.tiles.OSM().opts(width=600, height=550)
    stream = hv.streams.Tap(source=map_background, x=np.nan, y=np.nan)

    def get_easting_northing(self):
        self.house_df['easting'] = self.house_df.apply(
            lambda x: lon_lat_to_easting_northing(x['long'], x['lat'])[0],
            axis=1)
        self.house_df['northing'] = self.house_df.apply(
            lambda x: lon_lat_to_easting_northing(x['long'], x['lat'])[1],
            axis=1)

    @pn.depends('price_slider',
                'rooms_slider',
                'bathrooms_slider',
                'pins',
                watch=False)
    def house_plot(self):

        if 'northing' not in self.house_df.columns:
            self.get_easting_northing()

        df_filtered = self.house_df[
            (self.house_df['price'] <= self.price_slider[1])
            & (self.house_df['price'] >= self.price_slider[0])]
        df_filtered = df_filtered[
            (df_filtered['bedrooms'] <= self.rooms_slider[1])
            & (df_filtered['bedrooms'] >= self.rooms_slider[0])]
        df_filtered = df_filtered[
            (df_filtered['bathrooms'] <= self.rooms_slider[1])
            & (df_filtered['bathrooms'] >= self.rooms_slider[0])]
        # Create a bokeh figure and source here:

        # range bounds supplied in web mercator coordinates
        xrange = (df_filtered['easting'].round(decimals=2).min(),
                  df_filtered['easting'].round(decimals=2).max())
        yrange = (df_filtered['northing'].round(decimals=2).min(),
                  df_filtered['northing'].round(decimals=2).max())
        df_source = ColumnDataSource(df_filtered)
        tools = [ResetTool(), PanTool(), WheelZoomTool()]
        p = figure(x_range=xrange,
                   y_range=yrange,
                   x_axis_type="mercator",
                   y_axis_type="mercator",
                   plot_width=600,
                   plot_height=600,
                   tools=tools)
        p.add_tile(OSM_tile_source)
        print(df_filtered.shape)
        circle_renderer = p.circle(
            x='easting',
            y='northing',
            fill_color='midnightblue',
            fill_alpha=0.95,
            line_color='dodgersblue',
            hover_fill_color='firebrick',
            line_alpha=0.91,
            source=df_source,
            size=10,
            # hover_line_color='black',
            line_width=0)
        tool_circle_hover = HoverTool(renderers=[circle_renderer],
                                      tooltips=TOOLTIPS)
        p.add_tools(tool_circle_hover)
        return p

    def filter_df(self):
        if 'northing' not in self.house_df.columns:
            self.get_easting_northing()

        display_df = self.house_df[
            (self.house_df['price'] <= self.maximum_price)
            & (self.house_df['price'] >= self.minimum_price)]
        cols = [
            'lat', 'long', 'easting', 'northing', 'detail_url', 'key',
            'mls_number', 'id', 'photo_url'
        ]
        for col in cols:
            if col in display_df.columns:
                display_df = display_df.drop(columns=col, axis=1)
        display_df['size'] = display_df['size'].apply(
            lambda x: x.split()[0] if x else -999).astype(float)
        display_df = display_df.set_index('address')

        return display_df[[
            'photo', 'price', 'DateSold', 'PriceLastSold', 'Assessment Price',
            'bedrooms', 'bathrooms', 'size', 'lot_size', 'type', 'stories'
        ]]

    @pn.depends("stream", watch=False)
    def distance_df(self, x, y):
        lat = easting_northing_to_lon_lat(x, y)[1]
        long = easting_northing_to_lon_lat(x, y)[0]
        self.lat_longs.append(['enter name', lat, long])
        df = pd.DataFrame(self.lat_longs,
                          columns=['Name', 'Latitude',
                                   'Longitude']).dropna().style.hide_index()
        return pn.widgets.Tabulator(df.data,
                                    pagination='remote',
                                    page_size=10,
                                    sizing_mode='scale_both',
                                    show_index=False)

    @pn.depends("stream", "pins")
    def location(self, x, y):
        if x and y:
            self.pins.append([x, y])
        return self.house_plot

    def panel(self):
        result = bootstrap
        price_slider = pn.widgets.RangeSlider.from_param(
            self.param.price_slider, step=10000, format='0.0a')
        result.sidebar.append(price_slider)
        result.sidebar.append(self.param.rooms_slider)
        result.sidebar.append(self.param.bathrooms_slider)
        result.sidebar.append(self.param.type)
        result.sidebar.append(self.param.transit_time)

        image_format = r'<div> <img src="<%= value %>" height="70" alt="<%= value %>" width="70" style="float: left; margin: 0px 15px 15px 0px;" border="2" ></img> </div>'
        tabulator_formatters = {
            'price': NumberFormatter(format='$0,0'),
            'size': NumberFormatter(format='0,0 sqft'),
            'photo': HTMLTemplateFormatter(template=image_format)
        }

        df_widget = pn.widgets.Tabulator(self.filter_df(),
                                         pagination='remote',
                                         page_size=10,
                                         formatters=tabulator_formatters,
                                         sizing_mode='scale_both')
        df_widget.add_filter(self.param.price_slider, 'price')
        df_widget.add_filter(self.param.rooms_slider, 'bedrooms')

        # df_pins = pn.widgets.Tabulator(self.distance_df(), pagination='remote', page_size=10, sizing_mode='scale_both')

        layout = pn.Row(
            pn.Card(pn.bind(self.location,
                            x=self.stream.param.x,
                            y=self.stream.param.y),
                    title="Map",
                    sizing_mode='stretch_height'),
            pn.Column(
                pn.Card(df_widget,
                        title="Properties",
                        sizing_mode='scale_both')))

        result.sidebar.append(
            pn.Card(pn.bind(self.distance_df,
                            x=self.stream.param.x,
                            y=self.stream.param.y),
                    title="Pins",
                    sizing_mode='scale_both'))

        bootstrap.main.append(layout)
        bootstrap.main.append(pn.Card(self.details_area, title='Details'))

        return result
Ejemplo n.º 12
0
 def test_get_soft_bounds(self):
     q = param.Range((1, 3), bounds=(0, 10), softbounds=(1, 9))
     self.assertEqual(q.get_soft_bounds(), (1, 9))
Ejemplo n.º 13
0
 class Q(param.Parameterized):
     q = param.Range(bounds=(0, 10))
Ejemplo n.º 14
0
 class Q(param.Parameterized):
     q = param.Range(bounds=(0, 10), inclusive_bounds=(False, True))
Ejemplo n.º 15
0
 class Test(param.Parameterized):
     num = param.Range()
Ejemplo n.º 16
0
class ufloat(param.Parameterized):

    # deployment properties
    d_depth = param.Number(500,
                           bounds=(10, 4000),
                           step=10,
                           label='depth [m]',
                           doc="Deployment depth [m]")
    d_T = param.Number(30,
                       bounds=(1, 300),
                       step=1,
                       label='T [days]',
                       doc="Deployment time length [days]")
    d_delta_rho = param.Number(5,
                               bounds=(1, 30),
                               step=.5,
                               label='delta_rho [kg/m3]',
                               doc="Deployment density delta [kg/m^3]")

    # hull properties
    #h_length = param.Number(0.8, bounds=(.1,2.), step=.02,
    #                        label='length [m]', doc="Hull length [m]")
    h_radius = param.Range(default=(1., 30.),
                           bounds=(1., 50.),
                           label='radius [cm]',
                           doc="Hull radius [cm]")
    h_thickness = param.Number(.5,
                               bounds=(.1, 2.),
                               step=.1,
                               label='thickness [cm]',
                               doc="Hull thickness [cm]")
    h_cap_thickness = param.Number(.5,
                                   bounds=(.1, 3.),
                                   step=.1,
                                   label='end cap thickness [cm]',
                                   doc="End cap thickness [cm]")
    #h_density = param.Number(2700, bounds=(1000,3000), step=50.,
    #                         label='density [kg/m3]', doc="Hull density [kg/m^3]")
    h_material = param.Selector(objects=list(_materials),
                                label='size',
                                doc="Material")

    # piston properties
    #p_length = param.Number(.2, bounds=(.1,1.), step=.01,
    #                        label='length',doc="Piston length [m]")
    p_lambda = param.Number(.5,
                            bounds=(.1, 1.),
                            step=.01,
                            label='piston length normalized [1]',
                            doc="Piston length normalized [1]")
    p_density = param.Number(2700,
                             bounds=(1000, 3000),
                             step=50.,
                             label='density [kg/m3]',
                             doc="Piston density [kg/m^3]")
    p_efficiency = param.Number(.1,
                                bounds=(.01, 1.),
                                step=.01,
                                label='efficiency [1]',
                                doc="Piston energetic efficiency [1]")
    p_speed = param.Number(.1,
                           bounds=(.01, 10),
                           step=.01,
                           label='speed [m/h]',
                           doc="Piston speed [m/h]")

    # electronics properties
    e_volume = param.Number(250,
                            bounds=(10, 1000),
                            step=10,
                            label='volume [cm3]',
                            doc="Electronics volume [cm3]")
    e_mass = param.Number(.400,
                          bounds=(.1, 1.),
                          step=.05,
                          label='mass [kg]',
                          doc="Electronics mass [kg]")
    e_c = param.Number(.1,
                       bounds=(.01, 1.),
                       step=.01,
                       label='conssumption [W]',
                       doc="Electronics conssumption [W]")

    # battery selector
    b_cell = param.Selector(
        objects={v['long_name']: k
                 for k, v in _batteries.items()},
        label='size',
        doc="type")
    #b_lithium = param.Boolean(True, label='lithium', doc="Battery type")

    # Option buttons
    o_cap = param.Boolean(True, label='cap', doc="Cap")
    o_compressibility = param.Boolean(True,
                                      label='compressibility',
                                      doc="Compressibility")

    _params = [
        'd_depth',
        'd_T',
        'd_delta_rho',
        'h_radius',
        'h_thickness',
        'h_cap_thickness',  # 'h_length',
        'h_material',  #'h_density',
        'p_lambda',
        'p_density',
        'p_efficiency',
        'p_speed',  #'p_length',
        'e_volume',
        'e_mass',
        'e_c',
        'b_cell',  #'b_lithium',
        'o_cap',
        'o_compressibility',
    ]
    _scales = {
        'd_depth': 1.,
        'd_T': 86400.,
        'd_delta_rho': 1.,
        #'h_length': 1.,
        'h_radius': 1e-2,
        'h_thickness': 1e-2,
        'h_cap_thickness': 1e-2,
        #'h_density': 1,
        'h_material': None,
        'p_lambda': 1.,  #'p_length': 1.,
        'p_density': 1.,
        'p_efficiency': 1.,
        'p_speed': 1 / 3600.,
        'e_volume': 1e-6,
        'e_mass': 1.,
        'e_c': 1.,
        'b_cell': None,
        #'b_lithium': None,
        'o_cap': None,
        'o_compressibility': None,
    }
    _mapping = {p[0]: p for p in _items}

    def get_part(self, item):
        assert item in self._params
        return self._mapping[item.split('_')[0]]

    def get_key_and_part(self, item):
        assert item in self._params
        item_split = item.split('_')
        return '_'.join(item_split[1:]), self._mapping[item_split[0]]

    def __init__(self, **params):
        super(ufloat, self).__init__(**params)

        # will mirror data in dicts:
        self.D = {i: {} for i in _items}
        self.D['total'] = {}
        # hull material
        #self.D['hull'].update(alu_6061)

        # constraints
        self.constraints = {}

        # init figures
        self.renderers = {
            v: {}
            for v in ['volume', 'length', 'mass', 'constraints', 'speed']
        }

        _tools = 'pan,wheel_zoom,box_zoom,hover,crosshair,reset'
        TOOLTIPS = [
            ("(radius, y)", "($x, $y)"),
        ]
        # for linked crosshair:
        # https://stackoverflow.com/questions/37965669/how-do-i-link-the-crosshairtool-in-bokeh-over-several-plots

        f_volume = figure(
            y_axis_label='[liters]',
            tools=_tools,
            tooltips=TOOLTIPS,
        )
        f, r = f_volume, self.renderers['volume']
        for i in ['hull', 'piston', 'battery', 'electronics', 'total']:
            r[i] = f.line([], [],
                          color=_colors[i],
                          line_width=3,
                          legend_label=i)
        f.legend.background_fill_alpha = 0.1
        #
        f_length = figure(y_axis_label='[cm]',
                          tools=_tools,
                          tooltips=TOOLTIPS,
                          x_range=f_volume.x_range)
        f, r = f_length, self.renderers['length']
        for i in ['hull', 'piston']:
            r[i + '_radius'] = f.line(
                [],
                [],
                color=_colors[i],
                line_width=2,
                legend_label=i + ' radius',
            )
            r[i + '_length'] = f.line(
                [],
                [],
                color=_colors[i],
                line_width=4,
                legend_label=i + ' length',
            )
        r['hull_radius_gamma'] = f.line([], [],
                                        color=_colors['hull'],
                                        line_width=3,
                                        line_dash='4 4',
                                        legend_label='h radius/Gamma^{1/2}')
        #f.xaxis.axis_label = 'hull radius [cm]'
        f.legend.background_fill_alpha = 0.1
        #
        f_mass = figure(y_axis_label='[kg]',
                        y_axis_location='right',
                        tools=_tools,
                        tooltips=TOOLTIPS,
                        x_range=f_volume.x_range)
        f, r = f_mass, self.renderers['mass']
        for i in ['battery', 'hull', 'total']:
            r[i] = f.line([], [],
                          color=_colors[i],
                          line_width=3,
                          legend_label=i)
        f.legend.background_fill_alpha = 0.1
        #
        f_constraints = figure(
            y_axis_label='[1]',
            y_axis_location='right',
            tools=_tools,
            tooltips=TOOLTIPS,
            x_range=f_volume.x_range,
            y_range=(0, 5),
        )
        f, r = f_constraints, self.renderers['constraints']
        r['stress'] = line(f, _colors['hull'], 'stress')
        r['buckling'] = line(f,
                             _colors['hull'],
                             'buckling',
                             line_dash='dashed')
        r['compressibility'] = line(f, 'green', '1/compressibility')
        r['piston_length'] = line(f, _colors['piston'], 'piston length')
        r['piston_radius'] = line(f,
                                  _colors['piston'],
                                  'piston radius',
                                  line_dash='dashed')
        f.xaxis.axis_label = 'hull radius [cm]'
        f.add_layout(
            Span(location=1,
                 dimension='width',
                 line_color='black',
                 line_width=1))
        f.legend.background_fill_alpha = 0.1
        #
        f_speed = figure(
            y_axis_label='[cm/s]',
            tools=_tools,
            tooltips=TOOLTIPS,
            x_range=f_volume.x_range,
        )
        f, r = f_speed, self.renderers['speed']
        r['maxspeed'] = line(f, 'black', 'max speed')
        f.legend.background_fill_alpha = 0.1
        #
        self.figures = gridplot(
            [[f_volume, f_mass], [f_length, f_constraints], [f_speed, None]],
            plot_width=500,
            plot_height=300,
        )
        #
        self.update()

    def _update_dict_params(self):
        # push and rescale data in central dict
        for p in self._params:
            _k, _p = self.get_key_and_part(p)
            _d = self.D[_p]
            if p == 'h_radius':
                _d['radius'] = np.linspace(self.h_radius[0], self.h_radius[1],
                                           100) / 1e2
            elif p == 'h_material':
                _d['material'] = self.h_material
                _d.update(_materials[self.h_material])
            elif p == 'o_cap':
                _d['cap'] = int(self.o_cap)
            elif p == 'o_compressibility':
                _d['compressibility'] = int(self.o_compressibility)
            elif self._scales[p]:
                _d[_k] = getattr(self, p) * self._scales[p]

    def _update_battery(self):
        """ J/kg
        alcaline: 1.2V*1Ah=1.2Wh pour 136g
        lithium: 3.6V*9Ah=32.4Wh pour 96g
        """
        b = {**_batteries[self.b_cell]}
        b['volume'] = np.pi * b['radius']**2 * b['length']
        self.D['battery']['cell'] = b
        #
        density = b['weight'] / b['volume']
        V_reference = 3.5  # volts
        V_ratio = b['voltage'] / V_reference  # need to be verified
        edensity = (b['assumed_capacity'] * 3600 / V_ratio / b['weight'])
        self.D['battery']['density'] = density
        self.D['battery']['edensity'] = edensity

    def _update_mechanical_constraints(self, length=True):

        p = self.D['deployment']['pressure']

        # stress
        e = self.D['hull']['thickness']
        a = self.D['hull']['radius'] - e / 2.
        sigma = p * a / e * np.sqrt(1 + 1 / 4)
        # needs to be lower than self.D['hull']['Re']
        # !! should checks that internal radius < thickness > 10
        self.constraints['stress'] = (self.D['hull']['Re'], sigma)

        # buckling pressure
        E = self.D['hull']['E']
        nu = self.D['hull']['nu']
        t = e
        n = 2  # number of lobes
        l = self.D['hull']['length']
        r = self.D['hull']['radius']
        _r = (np.pi * r / (n * l))**2
        q_func = lambda n: (E * t / r / (1 + _r / 2) *
                            (1 / n**2 / (1 + 1 / _r)**2 + (n * t / r)**2 / 12 /
                             (1 - nu**2) * (1 + _r)**2))
        q_prime = np.maximum(q_func(2), q_func(3))
        # needs to be larger than 1.2 p
        self.constraints['buckling'] = (q_prime, 1.2 * p)

    def _update_compressibilities(self):

        p = self.D['deployment']['depth'] * g * rho0
        e = self.D['hull']['thickness']
        a = self.D['hull']['radius'] - e / 2.

        E = self.D['hull']['E']
        nu = self.D['hull']['nu']

        # mechanical compressibility
        gamma = a / e / E * (5 / 2 - 2 * nu)  # 1/Pa
        gamma = gamma * self.D['options'][
            'compressibility']  # set to 0 if option turned off
        self.D['hull']['mechanical_compressibility'] = gamma
        self.constraints['compressibility'] = (gamma_water, gamma)

        # thermal compressibility
        self.D['hull']['thermal_compressibility'] = 3 * self.D['hull']['alpha']

    def _update_line(self, figure_key, item, data, x, scale):
        #_d = self.D[item][data_key]
        _d = data
        if isinstance(_d, float):
            _d = np.ones_like(x) * _d
        (self.renderers[figure_key][item].data_source.data.update({
            'x':
            x * 1e2,
            'y':
            _d * scale,
        }))

    @param.depends(*_params, watch=True)
    def update(self):

        # update data containers
        self._update_dict_params()
        self._update_battery()

        d, h, p, b, e, o = (self.D[i] for i in _items)
        t = self.D['total']

        # solve for volume first
        self._update_compressibilities()
        d['pressure'] = rho0 * g * d['depth']
        # below may not be right for very compressible floats
        Gamma = abs(d['delta_rho'] / rho0 +
                    (gamma_water - h['mechanical_compressibility']) *
                    d['pressure'])
        ## lambda = l_piston/l
        sigma = (np.pi * Gamma / p['lambda'] * d['pressure'] * p['speed'] /
                 p['efficiency'])
        caps_mass = 2. * np.pi * h['cap_thickness'] * h['radius']**2 * h[
            'density']
        caps_mass = caps_mass * o['cap']  # turn off if option not set
        h['volume'] = ( caps_mass
                       +d['T']*sigma/b['edensity']*h['radius']**2
                       +d['T']*e['c']/b['edensity']
                       + e['mass']
                      ) \
                      /( rho0
                         - 2*h['thickness']/h['radius']*h['density']
                         - p['density'] * Gamma
                       )
        # negative volumes are flagged
        h['volume'][np.where(h['volume'] < 0)] = np.NaN

        # propagate volume
        h['length'] = h['volume'] / (np.pi * h['radius']**2)

        # piston radius
        p['length'] = p['lambda'] * h['length']  # if lambda is used
        p['radius'] = np.sqrt(h['length'] / p['length'] * Gamma) * h['radius']

        # piston conssumption
        p['c'] = (rho0 * g * d['depth'] * np.pi * p['radius']**2 * p['speed'] /
                  p['efficiency'])

        # battery mass
        b['mass'] = d['T'] * (e['c'] + p['c']) / b['edensity']
        b['volume'] = b['mass'] / b['density']

        # other parameters
        h['mass'] = (2. * np.pi * h['radius'] * h['thickness'] * h['length'] *
                     h['density'] + caps_mass)
        p['volume'] = np.pi * p['radius']**2 * p['length']
        p['mass'] = p['density'] * p['volume']
        t['mass'] = h['mass'] + p['mass'] + b['mass'] + e['mass']
        t['volume'] = p['volume'] + b['volume'] + e['volume']
        # https://doi.org/10.1016/j.earscirev.2014.06.001
        Cd = 1.
        max_speed = np.sqrt(2 * g * abs(Gamma) * t['mass'] /
                            (rho0 * np.pi * h['radius']**2 * Cd))

        # update constraints
        self.constraints['piston_length'] = (h['length'], p['length'])
        self.constraints['piston_radius'] = (h['radius'], p['radius'])
        self._update_mechanical_constraints()

        # update plots
        for _p in ['hull', 'piston', 'battery', 'electronics', 'total']:
            self._update_line('volume', _p, self.D[_p]['volume'], h['radius'],
                              1e3)
        #
        for _p in [
                'hull',
                'piston',
        ]:
            self._update_line('length', _p + '_radius', self.D[_p]['radius'],
                              h['radius'], 1e2)
            self._update_line('length', _p + '_length', self.D[_p]['length'],
                              h['radius'], 1e2)
        (self.renderers['length']['hull_radius_gamma'].data_source.data.update(
            {
                'x': h['radius'] * 1e2,
                'y': np.sqrt(Gamma) * h['radius'] * 1e2
            }))
        # manually adjust y range
        #_ylim = (0., 3.*np.nanmax(p['radius'])*1e2)
        _ylim = (0., min(100, np.nanmax(h['length']) * 1e2))
        self.figures.children[1].children[2][0].y_range = Range1d(*_ylim)
        #
        for _p in ['hull', 'battery', 'total']:
            self._update_line('mass', _p, self.D[_p]['mass'], h['radius'], 1)
        #
        r = self.renderers['constraints']
        for k, v in self.constraints.items():
            (r[k].data_source.data.update({
                'x': h['radius'] * 1e2,
                'y': v[0] / v[1]
            }))
        #
        (self.renderers['speed']['maxspeed'].data_source.data.update({
            'x':
            h['radius'] * 1e2,
            'y':
            max_speed * 1e2
        }))

    @param.depends(*_params)
    def variables_view(self):
        return self.figures

    def panel(self):
        w = self._widgets_panel()
        return pn.Column(
            pn.Row(w[0], w[1], w[2]),
            pn.Row(w[3], w[4], w[5]),
            self.variables_view,
        )

    def _widgets_panel(self):
        return (
            pn.Column('### {}'.format('Deployment'),
                      pn.panel(self.param.d_depth), pn.panel(self.param.d_T),
                      pn.panel(self.param.d_delta_rho)),
            pn.Column(
                '### {}'.format('Hull'),
                pn.panel(self.param.h_radius),  #pn.panel(self.param.h_length),
                pn.panel(self.param.h_thickness),
                pn.panel(self.param.h_cap_thickness),
                #pn.panel(self.param.h_density),
                pn.panel(self.param.h_material),
            ),
            pn.Column(
                '### {}'.format('Piston'),
                #pn.panel(self.param.p_length),
                pn.panel(self.param.p_lambda),
                pn.panel(self.param.p_density),
                pn.panel(self.param.p_efficiency),
                pn.panel(self.param.p_speed),
            ),
            pn.Column(
                '### {}'.format('Electronics'),
                pn.panel(self.param.e_volume),
                pn.panel(self.param.e_mass),
                pn.panel(self.param.e_c),
            ),
            pn.Column(
                '### {}'.format('Battery'),
                pn.panel(self.param.b_cell),
                #pn.panel(self.param.b_lithium),
            ),
            pn.Column(
                '### {}'.format('Options'),
                pn.panel(self.param.o_cap),
                pn.panel(self.param.o_compressibility),
            ),
        )
Ejemplo n.º 17
0
 class Test(param.Parameterized):
     a = param.Range(default=(0.1, 0.5), bounds=(0, 1.1))
Ejemplo n.º 18
0
class DisplayRangeOpts(param.Parameterized):
    color_range = param.Range(default=(0.0, 10), bounds=(-10, 20))