Esempio n. 1
0
 def load_data(path, itime, ipressure):
     step = 100
     with netCDF4.Dataset(path) as dataset:
         var = dataset.variables["x_wind"]
         if len(var.dimensions) == 4:
             values = var[itime, ipressure]
         else:
             values = var[itime]
         u = values[::step, ::step]
         var = dataset.variables["y_wind"]
         if len(var.dimensions) == 4:
             values = var[itime, ipressure]
         else:
             values = var[itime]
         v = values[::step, ::step]
         for d in var.dimensions:
             if "longitude" in d:
                 lons = dataset.variables[d][::step]
             if "latitude" in d:
                 lats = dataset.variables[d][::step]
     gx, _ = geo.web_mercator(lons, np.zeros(len(lons), dtype="d"))
     _, gy = geo.web_mercator(np.zeros(len(lats), dtype="d"), lats)
     x, y = np.meshgrid(gx, gy)
     u = convert_units(u, 'm s-1', 'knots')
     v = convert_units(v, 'm s-1', 'knots')
     return {
         "x": x.flatten(),
         "y": y.flatten(),
         "u": u.flatten(),
         "v": v.flatten()
     }
Esempio n. 2
0
    def load_centre_points(path):
        """Holds a centre point, future point and future movement line"""
        with open(path) as stream:
            rdt = json.load(stream)

        # Create an empty dictionary
        mydict = dict(
                x1=[], y1=[], x2=[], y2=[], xs=[], ys=[],
                Arrowxs=[],
                Arrowys=[],
                LonG=[],
                LatG=[],
                NumIdCell=[],
                NumIdBirth=[],
                MvtSpeed=[],
                MvtDirection=[])

        # Loop through features
        for i, feature in enumerate(rdt["features"]):
            # Append data from the feature properties to the dictionary
            mykeys = [k for k in mydict.keys() if not (('x' in k) or ('y' in k))]
            for k in mykeys:
                try:
                    thisdata, units = descale_rdt(k, feature['properties'][k])
                    mydict[k].append(thisdata)
                except:
                    mydict[k].append(None)
            # Takes the trajectory lat/lons, reprojects and puts them in a list within a list
            lon = feature['properties']['LonG']
            lat = feature['properties']['LatG']
            x1, y1 = geo.web_mercator(lon, lat)
            mydict['x1'].extend(x1)
            mydict['y1'].extend(y1)

            # Now calculate future point and line
            try:
                speed = float(feature['properties']['MvtSpeed'])
            except ValueError:
                speed = 0
            try:
                direction = float(feature['properties']['MvtDirection'])
            except ValueError:
                direction = 0
            lon2, lat2 = calc_dst_point(lon, lat, speed, direction)
            x2, y2 = geo.web_mercator(lon2, lat2)
            mydict['x2'].extend(x2)
            mydict['y2'].extend(y2)
            mydict['xs'].append([x1, x2])
            mydict['ys'].append([y1, y2])

            # Now calculate arrow polygon
            x3d, y3d, x4d, y4d = get_arrow_poly(lon2, lat2, speed, direction)
            [x3, x4], [y3, y4] = geo.web_mercator([x3d, x4d], [y3d, y4d])

            mydict['Arrowxs'].append([x2[0], x3, x4])
            mydict['Arrowys'].append([y2[0], y3, y4])
        return mydict
Esempio n. 3
0
    def load_tail_lines_json(path):
        """Load tail line data from file

        :returns: dict representation suitable for ColumnDataSource
        """
        with open(path) as stream:
            rdt = json.load(stream)

        # Create an empty dictionary
        mydict = get_empty_feature_dict("Tail_Lines")

        # Loop through features
        for i, feature in enumerate(rdt["features"]):
            # Append data from the feature properties to the dictionary
            for k in mydict.keys():
                try:
                    thisdata, units = descale_rdt(k, feature["properties"][k])
                    mydict[k].append(thisdata)
                except:
                    # Do nothing at the moment with the xs and ys
                    if not k in ["xs", "ys"]:
                        mydict[k].append(None)
                    else:
                        continue
            # Takes the trajectory lat/lons, reprojects and
            # puts them in a list within a list
            lons = feature["properties"]["LonTrajCellCG"]
            lats = feature["properties"]["LatTrajCellCG"]
            xs, ys = geo.web_mercator(lons, lats)
            mydict["xs"].append(xs)
            mydict["ys"].append(ys)

        return mydict
Esempio n. 4
0
def xs_ys(lines):
    xs, ys = [], []
    for lons, lats in lines:
        x, y = geo.web_mercator(lons, lats)
        xs.append(x)
        ys.append(y)
    return {"xs": xs, "ys": ys}
Esempio n. 5
0
def xs_ys(lines):
    """Map to Web Mercator projection and bokeh multi_line structure"""
    xs, ys = [], []
    for lons, lats in lines:
        x, y = geo.web_mercator(lons, lats)
        xs.append(x)
        ys.append(y)
    return {"xs": xs, "ys": ys}
Esempio n. 6
0
def make_arrow(mydict, lon, lat, speed, direction):

    lon2, lat2 = calc_dst_point(lon, lat, speed, direction)
    x1, y1 = geo.web_mercator(lon, lat)
    x2, y2 = geo.web_mercator(lon2, lat2)
    mydict["x2"].extend(x2)
    mydict["y2"].extend(y2)
    mydict["xs"].append([x1, x2])
    mydict["ys"].append([y1, y2])

    # Now calculate arrow polygon
    x3d, y3d, x4d, y4d = get_arrow_poly(lon2, lat2, speed, direction)
    [x3, x4], [y3, y4] = geo.web_mercator([x3d, x4d], [y3d, y4d])

    mydict["Arrowxs"].append([x2[0], x3, x4])
    mydict["Arrowys"].append([y2[0], y3, y4])

    return mydict
Esempio n. 7
0
 def render(self, valid_date):
     frame = self.loader.load_date(valid_date)
     x, y = geo.web_mercator(frame.longitude, frame.latitude)
     self.source.data = {
         "x": x,
         "y": y,
         "date": frame.date,
         "longitude": frame.longitude,
         "latitude": frame.latitude,
         "flash_type": frame.flash_type,
     }
Esempio n. 8
0
def web_mercator(lons, lats):
    """Similar to forest.geo.web_mercator but preserves array shape"""
    if isinstance(lons, list):
        lons = np.asarray(lons)
    if isinstance(lats, list):
        lats = np.asarray(lats)
    if (lons.ndim == 1):
        gx, _ = geo.web_mercator(
            lons,
            np.zeros(len(lons), dtype="d"))
        _, gy = geo.web_mercator(
            np.zeros(len(lats), dtype="d"),
            lats)
        return gx, gy
    elif (lons.ndim == 2) and (lats.ndim == 2):
        gx, gy = geo.web_mercator(lons, lats)
        gx = gx.reshape(lons.shape)
        gx = np.ma.masked_invalid(gx)
        gy = gy.reshape(lats.shape)
        gy = np.ma.masked_invalid(gy)
        return gx, gy
    else:
        raise Exception("Either 1D or 2D lons/lats")
Esempio n. 9
0
    def load_polygon_json(path):
        """Load GeoJSON string representation of Polygons from file

        :returns: GeoJSON str
        """

        with open(path) as stream:
            rdt = json.load(stream)

        # Convert units from the netcdf / geojson data file units into something more readable (e.g. could be Kelvin to degrees C or Pa to hPa)
        unitsToRescale = {"Pa": {"scale": 100, "offset": 0, "Units": "hPa"}}
        # Get text labels instead of numbers for certain fields
        fieldsToLookup = [
            "PhaseLife",
            "SeverityType",
            "SeverityIntensity",
            "ConvType",
            "CType",
        ]

        copy = dict(rdt)
        for i, feature in enumerate(rdt["features"]):
            coordinates = feature["geometry"]["coordinates"][0]
            lons, lats = np.asarray(coordinates).T
            x, y = geo.web_mercator(lons, lats)
            c = np.array([x, y]).T.tolist()
            copy["features"][i]["geometry"]["coordinates"][0] = c

            for k in feature["properties"].keys():

                # Might be easier to have a units lookup instead of this ...
                deldata, myunits = descale_rdt(k, feature["properties"][k])
                if myunits in unitsToRescale.keys():
                    try:
                        mydict = unitsToRescale.get(myunits)
                        scale, offset, units = mydict.values()
                        conv_data = (feature["properties"][k] / scale) + offset
                        copy["features"][i]["properties"][k] = conv_data
                    except:
                        continue

                if k in fieldsToLookup:
                    try:
                        copy["features"][i]["properties"][k] = fieldValueLUT(
                            k, feature["properties"][k])
                    except:
                        continue
        return json.dumps(copy)
Esempio n. 10
0
    def load_tail_points(path):
        with open(path) as stream:
            rdt = json.load(stream)

        # Create an empty dictionary
        mydict = dict(
                x=[], y=[],
                LonTrajCellCG=[],
                LatTrajCellCG=[],
                NumIdCell=[],
                NumIdBirth=[],
                DTimeTraj=[],
                BTempTraj=[],
                BTminTraj=[],
                BaseAreaTraj=[],
                TopAreaTraj=[],
                CoolingRateTraj=[],
                ExpanRateTraj=[],
                SpeedTraj=[],
                DirTraj=[])

        # Loop through features
        for i, feature in enumerate(rdt["features"]):
            # Append data from the feature properties to the dictionary
            # First though, how many points do we have in the trajectory tail?
            npts = len(feature['properties']['LonTrajCellCG'])
            mykeys = [k for k in mydict.keys() if k not in ['x', 'y']]
            for k in mykeys:
                # print(k, type(feature['properties'][k]), sep=':')
                try:
                    if not isinstance(feature['properties'][k], list):
                        datalist = [i for i in itertools.repeat(feature['properties'][k],npts)]
                        thisdata, units = descale_rdt(k, datalist)
                        mydict[k].extend(datalist)
                    else:
                        thisdata, units = descale_rdt(k, feature['properties'][k])
                        mydict[k].extend(thisdata)
                except:
                    datalist = [i for i in itertools.repeat(None, npts)]
                    mydict[k].extend(datalist)
            # Takes the trajectory lat/lons, reprojects and puts them in a list within a list
            lons = feature['properties']['LonTrajCellCG']
            lats = feature['properties']['LatTrajCellCG']
            x, y = geo.web_mercator(lons, lats)
            mydict['x'].extend(x)
            mydict['y'].extend(y)
        return mydict
Esempio n. 11
0
    def load_tail_lines(path):
        """Load tail line data from file

        :returns: dict representation suitable for ColumnDataSource
        """
        with open(path) as stream:
            rdt = json.load(stream)

        # Create an empty dictionary
        mydict = dict(
                xs=[], ys=[],
                LonTrajCellCG=[],
                LatTrajCellCG=[],
                NumIdCell=[],
                NumIdBirth=[],
                DTimeTraj=[],
                BTempTraj=[],
                BTminTraj=[],
                BaseAreaTraj=[],
                TopAreaTraj=[],
                CoolingRateTraj=[],
                ExpanRateTraj=[],
                SpeedTraj=[],
                DirTraj=[])

        # Loop through features
        for i, feature in enumerate(rdt["features"]):
            # Append data from the feature properties to the dictionary
            for k in mydict.keys():
                try:
                    thisdata, units = descale_rdt(k, feature['properties'][k])
                    mydict[k].append(thisdata)
                except:
                    # Do nothing at the moment with the xs and ys
                    if not k in ['xs', 'ys']:
                        mydict[k].append(None)
                    else:
                        continue
            # Takes the trajectory lat/lons, reprojects and
            # puts them in a list within a list
            lons = feature['properties']['LonTrajCellCG']
            lats = feature['properties']['LatTrajCellCG']
            xs, ys = geo.web_mercator(lons, lats)
            mydict['xs'].append(xs)
            mydict['ys'].append(ys)
        return mydict
Esempio n. 12
0
    def load_centre_points_json(path):
        """Holds a centre point, future point and future movement line"""

        with open(path) as stream:
            rdt = json.load(stream)

        # Create an empty dictionary
        mydict = get_empty_feature_dict("Centre_Point")

        # Loop through features
        for i, feature in enumerate(rdt["features"]):
            # Append data from the feature properties to the dictionary
            mykeys = [
                k for k in mydict.keys() if not (("x" in k) or ("y" in k))
            ]
            for k in mykeys:
                try:
                    thisdata, units = descale_rdt(k, feature["properties"][k])
                    mydict[k].append(thisdata)
                except:
                    mydict[k].append(None)
            # Takes the trajectory lat/lons, reprojects and puts them in a list within a list
            lon = feature["properties"]["LonG"]
            lat = feature["properties"]["LatG"]
            x1, y1 = geo.web_mercator(lon, lat)
            mydict["x1"].extend(x1)
            mydict["y1"].extend(y1)

            # Now calculate future point and line
            try:
                speed = float(feature["properties"]["MvtSpeed"])
            except ValueError:
                speed = 0
            try:
                direction = float(feature["properties"]["MvtDirection"])
            except ValueError:
                direction = 0

            mydict = make_arrow(mydict, lon, lat, speed, direction)

        return mydict
Esempio n. 13
0
def figure():
    lon_range = (0, 30)
    lat_range = (0, 30)
    x_range, y_range = geo.web_mercator(lon_range, lat_range)
    figure = bokeh.plotting.figure(x_range=x_range,
                                   y_range=y_range,
                                   x_axis_type="mercator",
                                   y_axis_type="mercator",
                                   active_scroll="wheel_zoom")
    figure.axis.visible = False
    figure.toolbar.logo = None
    figure.toolbar_location = None
    figure.min_border = 3
    tile = bokeh.models.WMTSTileSource(
        url="https://maps.wikimedia.org/osm-intl/{Z}/{X}/{Y}.png",
        attribution="")
    figure.add_tile(tile)

    legend = bokeh.models.Legend()
    figure.add_layout(legend)
    return figure
Esempio n. 14
0
 def scatter(self, state):
     """Scatter plot of flash position colored by time since flash"""
     valid_time = _to_datetime(state.valid_time)
     paths = self.locator.find(valid_time)
     frame = self.loader.load(paths)
     frame = self.select_date(frame, valid_time)
     frame = frame[:400]  # Limit points
     frame["time_since_flash"] = self.since_flash(frame["date"], valid_time)
     if len(frame) == 0:
         return self.empty_image
     x, y = geo.web_mercator(frame.longitude, frame.latitude)
     self.color_mapper.low = np.min(frame.time_since_flash)
     self.color_mapper.high = np.max(frame.time_since_flash)
     self.sources["scatter"].data = {
         "x": x,
         "y": y,
         "date": frame.date,
         "longitude": frame.longitude,
         "latitude": frame.latitude,
         "flash_type": frame.flash_type,
         "time_since_flash": frame.time_since_flash,
     }
Esempio n. 15
0
    def load_tail_points_json(path):
        with open(path) as stream:
            rdt = json.load(stream)

        # Create an empty dictionary
        mydict = get_empty_feature_dict("Tail_Points")

        # Loop through features
        for i, feature in enumerate(rdt["features"]):
            # Append data from the feature properties to the dictionary
            # First though, how many points do we have in the trajectory tail?
            npts = len(feature["properties"]["LonTrajCellCG"])
            mykeys = [k for k in mydict.keys() if k not in ["x", "y"]]
            for k in mykeys:
                # print(k, type(feature['properties'][k]), sep=':')
                try:
                    if not isinstance(feature["properties"][k], list):
                        datalist = [
                            i for i in itertools.repeat(
                                feature["properties"][k], npts)
                        ]
                        thisdata, units = descale_rdt(k, datalist)
                        mydict[k].extend(datalist)
                    else:
                        thisdata, units = descale_rdt(k,
                                                      feature["properties"][k])
                        mydict[k].extend(thisdata)
                except:
                    datalist = [i for i in itertools.repeat(None, npts)]
                    mydict[k].extend(datalist)
            # Takes the trajectory lat/lons, reprojects and puts them in a list within a list
            lons = feature["properties"]["LonTrajCellCG"]
            lats = feature["properties"]["LatTrajCellCG"]
            x, y = geo.web_mercator(lons, lats)
            mydict["x"].extend(x)
            mydict["y"].extend(y)
        return mydict
Esempio n. 16
0
def main(argv=None):

    args = parse_args.parse_args(argv)
    data.AUTO_SHUTDOWN = args.auto_shutdown

    if len(args.files) > 0:
        if args.config_file is not None:
            raise Exception(
                '--config-file and [FILE [FILE ...]] not compatible')
        config = cfg.from_files(args.files, args.file_type)
    else:
        config = cfg.Config.load(args.config_file,
                                 variables=cfg.combine_variables(
                                     os.environ, args.variables))

    # Feature toggles
    if "feature" in config.plugins:
        features = plugin.call(config.plugins["feature"].entry_point)
    else:
        features = config.features
    data.FEATURE_FLAGS = features

    # Full screen map
    viewport = config.default_viewport
    x_range, y_range = geo.web_mercator(viewport.lon_range, viewport.lat_range)
    figure = bokeh.plotting.figure(
        x_range=x_range,
        y_range=y_range,
        #output_backend = "svg",
        x_axis_type="mercator",
        y_axis_type="mercator",
        active_scroll="wheel_zoom")

    figures = [figure]
    for _ in range(2):
        f = bokeh.plotting.figure(x_range=figure.x_range,
                                  y_range=figure.y_range,
                                  x_axis_type="mercator",
                                  y_axis_type="mercator",
                                  active_scroll="wheel_zoom")
        figures.append(f)

    for f in figures:
        f.axis.visible = False
        f.toolbar.logo = None
        f.toolbar_location = None
        f.min_border = 0

    figure_row = layers.FigureRow(figures)

    color_mapper = bokeh.models.LinearColorMapper(
        low=0, high=1, palette=bokeh.palettes.Plasma[256])

    # Convert config to datasets
    datasets = {}
    datasets_by_pattern = {}
    label_to_pattern = {}
    for group in config.file_groups:
        settings = {
            "label": group.label,
            "pattern": group.pattern,
            "locator": group.locator,
            "database_path": group.database_path,
            "directory": group.directory
        }
        dataset = drivers.get_dataset(group.file_type, settings)
        datasets[group.label] = dataset
        datasets_by_pattern[group.pattern] = dataset
        label_to_pattern[group.label] = group.pattern

    # Lakes
    for figure in figures:
        add_feature(figure, data.LAKES, color="lightblue")

    features = []
    for figure in figures:
        render2 = add_feature(figure, data.LAKES, color="lightblue")
        features += [
            add_feature(figure, data.COASTLINES),
            add_feature(figure, data.BORDERS)
        ]

    # Disputed borders
    for figure in figures:
        add_feature(figure, data.DISPUTED, color="red")

    toggle = bokeh.models.CheckboxGroup(labels=["Coastlines"],
                                        active=[0],
                                        width=135)

    def on_change(attr, old, new):
        if len(new) == 1:
            for feature in features:
                feature.visible = True
        else:
            for feature in features:
                feature.visible = False

    toggle.on_change("active", on_change)

    dropdown = bokeh.models.Dropdown(label="Color",
                                     menu=[("Black", "black"),
                                           ("White", "white")],
                                     width=50)
    autolabel(dropdown)

    def on_change(event):
        for feature in features:
            feature.glyph.line_color = new

    dropdown.on_click(on_change)

    layers_ui = layers.LayersUI()

    div = bokeh.models.Div(text="", width=10)
    border_row = bokeh.layouts.row(bokeh.layouts.column(toggle),
                                   bokeh.layouts.column(div),
                                   bokeh.layouts.column(dropdown))

    # Add optional sub-navigators
    sub_navigators = {
        key: dataset.navigator()
        for key, dataset in datasets_by_pattern.items()
        if hasattr(dataset, "navigator")
    }
    navigator = navigate.Navigator(sub_navigators)

    # Pre-select menu choices (if any)
    initial_state = {}
    for pattern, _ in sub_navigators.items():
        initial_state = db.initial_state(navigator, pattern=pattern)
        break

    middlewares = [
        keys.navigate,
        db.InverseCoordinate("pressure"),
        db.next_previous,
        db.Controls(navigator),  # TODO: Deprecate this middleware
        colors.palettes,
        colors.middleware(),
        presets.Middleware(presets.proxy_storage(config.presets_file)),
        presets.middleware,
        layers.middleware,
        navigator,
        mws.echo,
    ]
    store = redux.Store(forest.reducer,
                        initial_state=initial_state,
                        middlewares=middlewares)

    # Colorbar user interface
    colorbar_ui = forest.components.ColorbarUI()
    colorbar_ui.connect(store)

    # Add time user interface
    time_ui = forest.components.TimeUI()
    time_ui.connect(store)

    # Connect MapView orchestration to store
    opacity_slider = forest.layers.OpacitySlider()
    source_limits = colors.SourceLimits().connect(store)
    factory_class = forest.layers.factory(color_mapper, figures, source_limits,
                                          opacity_slider)
    gallery = forest.layers.Gallery.from_datasets(datasets, factory_class)
    gallery.connect(store)

    # Connect layers controls
    layers_ui.add_subscriber(store.dispatch)
    layers_ui.connect(store)

    # Connect tools controls

    display_names = {
        "time_series": "Display Time Series",
        "profile": "Display Profile",
        "barc": "BARC Toolkit"
    }
    available_features = {
        k: display_names[k]
        for k in display_names.keys() if data.FEATURE_FLAGS[k]
    }

    tools_panel = tools.ToolsPanel(available_features)
    tools_panel.connect(store)

    #barc_toolbar=bokeh.models.tools.Toolbar(tools=barc_tools,logo=None)
    if data.FEATURE_FLAGS["BARC"]:
        barc = BARC(figures)
        tools_panel.layout.children.extend(barc.ToolBar())

    # Navbar components
    navbar = Navbar(show_diagram_button=len(available_features) > 0)
    navbar.connect(store)

    # Connect tap listener
    tap_listener = screen.TapListener()
    tap_listener.connect(store)

    # Connect figure controls/views
    figure_ui = layers.FigureUI()
    figure_ui.add_subscriber(store.dispatch)
    figure_row.connect(store)

    # Tiling picker
    if config.use_web_map_tiles:
        tile_picker = forest.components.TilePicker()
        for figure in figures:
            tile_picker.add_figure(figure)
        tile_picker.connect(store)

    # Connect color palette controls
    colors.ColorMapperView(color_mapper).connect(store)
    color_palette = colors.ColorPalette().connect(store)

    # Connect limit controllers to store
    user_limits = colors.UserLimits().connect(store)

    # Preset
    preset_ui = presets.PresetUI().connect(store)

    # Connect navigation controls
    controls = db.ControlView()
    controls.connect(store)

    # Add support for a modal dialogue
    if data.FEATURE_FLAGS["multiple_colorbars"]:
        view = forest.components.modal.Tabbed()
    else:
        view = forest.components.modal.Default()
    modal = forest.components.Modal(view=view)
    modal.connect(store)

    # Set default time series visibility
    store.dispatch(tools.on_toggle_tool("time_series", False))

    # Set default profile visibility
    store.dispatch(tools.on_toggle_tool("profile", False))

    # Set top-level navigation
    store.dispatch(db.set_value("patterns", config.patterns))

    # Pre-select first map_view layer
    for label, dataset in datasets.items():
        pattern = label_to_pattern[label]
        for variable in navigator.variables(pattern):
            spec = {
                "label": label,
                "dataset": label,
                "variable": variable,
                "active": [0]
            }
            store.dispatch(forest.layers.save_layer(0, spec))
            break
        break

    # Set variable dimensions (needed by modal dialogue)
    for label, dataset in datasets.items():
        pattern = label_to_pattern[label]
        values = navigator.variables(pattern)
        store.dispatch(dimension.set_variables(label, values))

    # Select web map tiling
    if config.use_web_map_tiles:
        store.dispatch(tiles.set_tile(tiles.STAMEN_TERRAIN))
        store.dispatch(tiles.set_label_visible(True))

    # Organise controls/settings
    layouts = {}
    layouts["controls"] = [
        bokeh.models.Div(text="Layout:"), figure_ui.layout,
        bokeh.models.Div(text="Navigate:"), controls.layout,
        bokeh.models.Div(text="Compare:"), layers_ui.layout
    ]
    layouts["settings"] = [
        border_row,
        opacity_slider.layout,
        preset_ui.layout,
        color_palette.layout,
        user_limits.layout,
        bokeh.models.Div(text="Tiles:"),
    ]
    if config.use_web_map_tiles:
        layouts["settings"].append(tile_picker.layout)

    tabs = bokeh.models.Tabs(tabs=[
        bokeh.models.Panel(child=bokeh.layouts.column(*layouts["controls"]),
                           title="Control"),
        bokeh.models.Panel(child=bokeh.layouts.column(*layouts["settings"]),
                           title="Settings")
    ])

    tool_figures = {}
    if data.FEATURE_FLAGS["time_series"]:
        # Series sub-figure widget
        series_figure = bokeh.plotting.figure(plot_width=400,
                                              plot_height=200,
                                              x_axis_type="datetime",
                                              toolbar_location=None,
                                              border_fill_alpha=0)
        series_figure.toolbar.logo = None

        series_view = series.SeriesView.from_groups(series_figure,
                                                    config.file_groups)
        series_view.add_subscriber(store.dispatch)
        series_args = (rx.Stream().listen_to(store).map(
            series.select_args).filter(lambda x: x is not None).distinct())
        series_args.map(lambda a: series_view.render(*a))
        series_args.map(print)  # Note: map(print) creates None stream

        tool_figures["series_figure"] = series_figure

    if data.FEATURE_FLAGS["profile"]:
        # Profile sub-figure widget
        profile_figure = bokeh.plotting.figure(plot_width=300,
                                               plot_height=450,
                                               toolbar_location=None,
                                               border_fill_alpha=0)
        profile_figure.toolbar.logo = None
        profile_figure.y_range.flipped = True

        profile_view = profile.ProfileView.from_groups(profile_figure,
                                                       config.file_groups)
        profile_view.add_subscriber(store.dispatch)
        profile_args = (rx.Stream().listen_to(store).map(
            profile.select_args).filter(lambda x: x is not None).distinct())
        profile_args.map(lambda a: profile_view.render(*a))
        profile_args.map(print)  # Note: map(print) creates None stream

        tool_figures["profile_figure"] = profile_figure

    tool_layout = tools.ToolLayout(**tool_figures)
    tool_layout.connect(store)

    for f in figures:
        f.on_event(bokeh.events.Tap, tap_listener.update_xy)
        marker = screen.MarkDraw(f).connect(store)

    control_root = bokeh.layouts.column(tabs, name="controls")

    # Add key press support
    key_press = keys.KeyPress()
    key_press.add_subscriber(store.dispatch)

    document = bokeh.plotting.curdoc()
    document.title = "FOREST"
    document.add_root(control_root)
    document.add_root(
        bokeh.layouts.column(tools_panel.layout,
                             tool_layout.layout,
                             width=400,
                             name="series"))
    document.add_root(bokeh.layouts.row(time_ui.layout, name="time"))
    for root in navbar.roots:
        document.add_root(root)
    document.add_root(colorbar_ui.layout)
    document.add_root(figure_row.layout)
    document.add_root(key_press.hidden_button)
    document.add_root(modal.layout)
Esempio n. 17
0
    def image(self, state):
        """Image colored by time since flash or flash density"""
        valid_time = _to_datetime(state.valid_time)

        # 15 minute/1 hour slice of data?
        window = dt.timedelta(minutes=60)  # 1 hour window
        paths = self.locator.find_period(valid_time, window)
        frame = self.loader.load(paths)
        frame = self.select_date(frame, valid_time, window)

        # Filter intra-cloud/cloud-ground rows
        if "intra-cloud" in state.variable.lower():
            frame = frame[frame["flash_type"] == "IC"]
        elif "cloud-ground" in state.variable.lower():
            frame = frame[frame["flash_type"] == "CG"]

        # EarthNetworks validity box (not needed if tiling algorithm)
        longitude_range = (26, 40)
        latitude_range = (-12, 4)
        x_range, y_range = geo.web_mercator(longitude_range, latitude_range)

        x, y = geo.web_mercator(frame["longitude"], frame["latitude"])
        frame["x"] = x
        frame["y"] = y
        pixels = 256
        canvas = datashader.Canvas(
            plot_width=pixels,
            plot_height=pixels,
            x_range=x_range,
            y_range=y_range,
        )

        if "density" in state.variable.lower():
            # N flashes per pixel
            agg = canvas.points(frame, "x", "y", datashader.count())
        else:
            frame["since_flash"] = self.since_flash(frame["date"], valid_time)
            agg = canvas.points(frame, "x", "y", datashader.max("since_flash"))

        # Note: DataArray objects are not JSON serializable, .values is the
        #       same data cast as a numpy array
        x = agg.x.values.min()
        y = agg.y.values.min()
        dw = agg.x.values.max() - x
        dh = agg.y.values.max() - y
        image = np.ma.masked_array(
            agg.values.astype(np.float), mask=np.isnan(agg.values)
        )
        if "density" in state.variable.lower():
            image[image == 0] = np.ma.masked  # Remove pixels with no data

        # Update color_mapper
        color_mapper = self.color_mappers["image"]
        if "density" in state.variable.lower():
            color_mapper.palette = bokeh.palettes.all_palettes["Spectral"][8]
            color_mapper.low = 0
            color_mapper.high = agg.values.max()
        else:
            color_mapper.palette = bokeh.palettes.all_palettes["RdGy"][8]
            color_mapper.low = 0
            color_mapper.high = 60 * 60  # 1 hour

        # Update tooltips
        for hover_tool in self.hover_tools["image"]:
            hover_tool.tooltips = self.tooltips(state.variable)
            hover_tool.formatters = self.formatters(state.variable)

        if "density" in state.variable.lower():
            units = "events"
        else:
            units = "seconds"

        data = {
            "x": [x],
            "y": [y],
            "dw": [dw],
            "dh": [dh],
            "image": [image],
        }
        meta_data = {
            "variable": [state.variable],
            "date": [valid_time],
            "units": [units],
            "window": [window.total_seconds()],
        }
        data.update(meta_data)
        self.sources["image"].data = data
Esempio n. 18
0
def main(argv=None):
    config = configure(argv=argv)

    # Feature toggles
    if "feature" in config.plugins:
        features = plugin.call(config.plugins["feature"].entry_point)
    else:
        features = config.features
    data.FEATURE_FLAGS = features

    # Full screen map
    viewport = config.default_viewport
    x_range, y_range = geo.web_mercator(viewport.lon_range, viewport.lat_range)

    figure = map_figure(x_range, y_range)
    figures = [figure]
    for _ in range(2):
        f = map_figure(figure.x_range, figure.y_range)
        figures.append(f)

    figure_row = layers.FigureRow(figures)

    color_mapper = bokeh.models.LinearColorMapper(
        low=0, high=1, palette=bokeh.palettes.Plasma[256])

    # Convert config to datasets
    datasets = {}
    datasets_by_pattern = {}
    label_to_pattern = {}
    for group, dataset in zip(config.file_groups, config.datasets):
        datasets[group.label] = dataset
        datasets_by_pattern[group.pattern] = dataset
        label_to_pattern[group.label] = group.pattern

    # print('\n\n\n\n', datasets, '\n\n\n\n')
    '''# Lakes
    for figure in figures:
        add_feature(figure, data.LAKES, color="lightblue")

    features = []
    for figure in figures:
        render2 = add_feature(figure, data.LAKES, color="lightblue")
        features += [
            add_feature(figure, data.COASTLINES),
            add_feature(figure, data.BORDERS)]

    # Disputed borders
    for figure in figures:
        add_feature(figure, data.DISPUTED, color="red")
    '''

    toggle = bokeh.models.CheckboxGroup(labels=["Coastlines"],
                                        active=[0],
                                        width=135)

    def on_change(attr, old, new):
        if len(new) == 1:
            for feature in features:
                feature.visible = True
        else:
            for feature in features:
                feature.visible = False

    toggle.on_change("active", on_change)

    dropdown = bokeh.models.Dropdown(label="Color",
                                     menu=[("Black", "black"),
                                           ("White", "white")],
                                     width=50)
    autolabel(dropdown)

    def on_change(event):
        for feature in features:
            feature.glyph.line_color = new

    dropdown.on_click(on_change)

    layers_ui = layers.LayersUI()

    # Add optional sub-navigators
    sub_navigators = {
        key: dataset.navigator()
        for key, dataset in datasets_by_pattern.items()
        if hasattr(dataset, "navigator")
    }
    navigator = navigate.Navigator(sub_navigators)

    middlewares = [
        keys.navigate,
        db.InverseCoordinate("pressure"),
        db.next_previous,
        db.Controls(navigator),  # TODO: Deprecate this middleware
        colors.palettes,
        colors.middleware(),
        presets.Middleware(presets.proxy_storage(config.presets_file)),
        presets.middleware,
        layers.middleware,
        navigator,
        mws.echo,
    ]
    store = redux.Store(forest.reducer, middlewares=middlewares)

    app = forest.app.Application()
    app.add_component(forest.components.title.Title())

    # Coastlines, borders, lakes and disputed borders
    view = forest.components.borders.View()
    for figure in figures:
        view.add_figure(figure)
    view.connect(store)
    border_ui = forest.components.borders.UI()
    border_ui.connect(store)

    # Colorbar user interface
    component = forest.components.ColorbarUI()
    app.add_component(component)

    # Add time user interface
    if config.defaults.timeui:
        component = forest.components.TimeUI()
        component.layout = bokeh.layouts.row(component.layout, name="time")
        app.add_component(component)

    # Connect MapView orchestration to store
    opacity_slider = forest.layers.OpacitySlider()
    source_limits = colors.SourceLimits().connect(store)
    factory_class = forest.layers.factory(color_mapper, figures, source_limits,
                                          opacity_slider)
    gallery = forest.gallery.Gallery.map_view(datasets, factory_class)
    gallery.connect(store)

    # Connect layers controls
    layers_ui.add_subscriber(store.dispatch)
    layers_ui.connect(store)

    # Connect tools controls

    display_names = {
        "time_series": "Display Time Series",
        "profile": "Display Profile",
        "barc": "BARC Toolkit"
    }
    available_features = {
        k: display_names[k]
        for k in display_names.keys() if data.FEATURE_FLAGS[k]
    }

    tools_panel = tools.ToolsPanel(available_features)
    tools_panel.connect(store)

    #barc_toolbar=bokeh.models.tools.Toolbar(tools=barc_tools,logo=None)
    if data.FEATURE_FLAGS["BARC"]:
        barc = BARC(figures)
        tools_panel.layout.children.append(barc.ToolBar())

    # Navbar components
    navbar = Navbar(show_diagram_button=len(available_features) > 0)
    navbar.connect(store)

    # Connect tap listener
    tap_listener = screen.TapListener()
    tap_listener.connect(store)

    # Connect figure controls/views
    if config.defaults.figures.ui:
        figure_ui = layers.FigureUI(config.defaults.figures.maximum)
        figure_ui.connect(store)
    figure_row.connect(store)

    # Tiling picker
    if config.use_web_map_tiles:
        tile_picker = forest.components.TilePicker()
        for figure in figures:
            tile_picker.add_figure(figure)

        tile_picker.connect(store)

    if not data.FEATURE_FLAGS["multiple_colorbars"]:
        # Connect color palette controls
        colors.ColorMapperView(color_mapper).connect(store)
        color_palette = colors.ColorPalette().connect(store)

        # Connect limit controllers to store
        user_limits = colors.UserLimits().connect(store)

    # Preset
    if config.defaults.presetui:
        preset_ui = presets.PresetUI().connect(store)

    # Connect navigation controls
    controls = db.ControlView()
    controls.connect(store)

    # Add support for a modal dialogue
    if data.FEATURE_FLAGS["multiple_colorbars"]:
        view = forest.components.modal.Tabbed()
    else:
        view = forest.components.modal.Default()
    modal = forest.components.Modal(view=view)
    modal.connect(store)

    # Connect components to Store
    app.connect(store)

    # Set initial state
    store.dispatch(forest.actions.set_state(config.state).to_dict())

    # Pre-select menu choices (if any)
    for pattern, _ in sub_navigators.items():
        state = db.initial_state(navigator, pattern=pattern)
        store.dispatch(forest.actions.update_state(state).to_dict())
        break

    # Set default time series visibility
    store.dispatch(tools.on_toggle_tool("time_series", False))

    # Set default profile visibility
    store.dispatch(tools.on_toggle_tool("profile", False))

    # Set top-level navigation
    store.dispatch(db.set_value("patterns", config.patterns))

    # Pre-select first map_view layer
    for label, dataset in datasets.items():
        pattern = label_to_pattern[label]
        for variable in navigator.variables(pattern):
            spec = {
                "label": label,
                "dataset": label,
                "variable": variable,
                "active": [0]
            }
            store.dispatch(forest.layers.save_layer(0, spec))
            break
        break

    # Set variable dimensions (needed by modal dialogue)
    for label, dataset in datasets.items():
        pattern = label_to_pattern[label]
        values = navigator.variables(pattern)
        store.dispatch(dimension.set_variables(label, values))

    # Organise controls/settings
    layouts = {}
    layouts["controls"] = []
    if config.defaults.figures.ui:
        layouts["controls"] += [
            bokeh.models.Div(text="Layout:"), figure_ui.layout
        ]
    layouts["controls"] += [
        bokeh.models.Div(text="Navigate:"), controls.layout,
        bokeh.models.Div(text="Compare:"), layers_ui.layout
    ]

    layouts["settings"] = [
        bokeh.models.Div(text="Borders, coastlines and lakes:"),
        border_ui.layout,
        opacity_slider.layout,
    ]
    if not data.FEATURE_FLAGS["multiple_colorbars"]:
        layouts["settings"].append(color_palette.layout)
        layouts["settings"].append(user_limits.layout)
    if config.defaults.presetui:
        layouts["settings"].append(preset_ui.layout)
    if config.use_web_map_tiles:
        layouts["settings"].append(bokeh.models.Div(text="Tiles:"))
        layouts["settings"].append(tile_picker.layout)

    tabs = bokeh.models.Tabs(tabs=[
        bokeh.models.Panel(child=bokeh.layouts.column(*layouts["controls"]),
                           title="Control"),
        bokeh.models.Panel(child=bokeh.layouts.column(*layouts["settings"]),
                           title="Settings")
    ])

    tool_figures = {}
    if data.FEATURE_FLAGS["time_series"]:
        # Series sub-figure widget
        series_figure = bokeh.plotting.figure(plot_width=400,
                                              plot_height=200,
                                              x_axis_type="datetime",
                                              toolbar_location=None,
                                              border_fill_alpha=0)
        series_figure.toolbar.logo = None

        gallery = forest.gallery.Gallery.series_view(datasets, series_figure)
        gallery.connect(store)

        tool_figures["series_figure"] = series_figure

    if data.FEATURE_FLAGS["profile"]:
        # Profile sub-figure widget
        profile_figure = bokeh.plotting.figure(plot_width=300,
                                               plot_height=450,
                                               toolbar_location=None,
                                               border_fill_alpha=0)
        profile_figure.toolbar.logo = None
        profile_figure.y_range.flipped = True

        gallery = forest.gallery.Gallery.profile_view(datasets, profile_figure)
        gallery.connect(store)

        tool_figures["profile_figure"] = profile_figure

    tool_layout = tools.ToolLayout(**tool_figures)
    tool_layout.connect(store)

    for f in figures:
        f.on_event(bokeh.events.Tap, tap_listener.update_xy)
        marker = screen.MarkDraw(f).connect(store)

    control_root = bokeh.layouts.column(tabs, name="controls")

    # Add key press support
    key_press = keys.KeyPress()
    key_press.add_subscriber(store.dispatch)

    # Add HTML ready support
    obj = html_ready.HTMLReady(key_press.hidden_button)
    obj.connect(store)

    document = bokeh.plotting.curdoc()
    document.title = "FOREST"
    document.add_root(control_root)
    document.add_root(
        bokeh.layouts.column(tools_panel.layout,
                             tool_layout.layout,
                             width=400,
                             name="series"))
    for root in navbar.roots:
        document.add_root(root)
    for root in app.roots:
        document.add_root(root)
    document.add_root(figure_row.layout)
    document.add_root(key_press.hidden_button)
    document.add_root(modal.layout)
Esempio n. 19
0
def main(argv=None):

    args = parse_args.parse_args(argv)
    data.AUTO_SHUTDOWN = args.auto_shutdown

    if len(args.files) > 0:
        if args.config_file is not None:
            raise Exception(
                '--config-file and [FILE [FILE ...]] not compatible')
        config = cfg.from_files(args.files, args.file_type)
    else:
        config = cfg.Config.load(args.config_file,
                                 variables=cfg.combine_variables(
                                     os.environ, args.variables))

    # Feature toggles
    if "feature" in config.plugins:
        features = plugin.call(config.plugins["feature"].entry_point)
    else:
        features = config.features
    data.FEATURE_FLAGS = features

    # Full screen map
    viewport = config.default_viewport
    x_range, y_range = geo.web_mercator(viewport.lon_range, viewport.lat_range)
    figure = map_figure(x_range, y_range)
    figures = [figure]
    for _ in range(2):
        f = map_figure(figure.x_range, figure.y_range)
        figures.append(f)

    figure_row = layers.FigureRow(figures)

    color_mapper = bokeh.models.LinearColorMapper(
        low=0, high=1, palette=bokeh.palettes.Plasma[256])

    # Convert config to datasets
    datasets = {}
    datasets_by_pattern = {}
    label_to_pattern = {}
    for group in config.file_groups:
        settings = {
            "label": group.label,
            "pattern": group.pattern,
            "locator": group.locator,
            "database_path": group.database_path,
            "directory": group.directory
        }
        dataset = drivers.get_dataset(group.file_type, settings)
        datasets[group.label] = dataset
        datasets_by_pattern[group.pattern] = dataset
        label_to_pattern[group.label] = group.pattern

    layers_ui = layers.LayersUI()

    # Add optional sub-navigators
    sub_navigators = {
        key: dataset.navigator()
        for key, dataset in datasets_by_pattern.items()
        if hasattr(dataset, "navigator")
    }
    navigator = navigate.Navigator(sub_navigators)

    middlewares = [
        keys.navigate,
        db.InverseCoordinate("pressure"),
        db.next_previous,
        db.Controls(navigator),  # TODO: Deprecate this middleware
        colors.palettes,
        colors.middleware(),
        presets.Middleware(presets.proxy_storage(config.presets_file)),
        presets.middleware,
        layers.middleware,
        navigator,
        mws.echo,
    ]
    store = redux.Store(forest.reducer, middlewares=middlewares)

    app = forest.app.Application()
    app.add_component(forest.components.title.Title())

    # Coastlines, borders, lakes and disputed borders
    view = forest.components.borders.View()
    for figure in figures:
        view.add_figure(figure)
    view.connect(store)
    border_ui = forest.components.borders.UI()
    border_ui.connect(store)

    # Colorbar user interface
    component = forest.components.ColorbarUI()
    app.add_component(component)

    # Add time user interface
    if config.defaults.timeui:
        component = forest.components.TimeUI()
        component.layout = bokeh.layouts.row(component.layout, name="time")
        app.add_component(component)

    # Connect MapView orchestration to store
    opacity_slider = forest.layers.OpacitySlider()
    source_limits = colors.SourceLimits().connect(store)
    factory_class = forest.layers.factory(color_mapper, figures, source_limits,
                                          opacity_slider)
    gallery = forest.layers.Gallery.from_datasets(datasets, factory_class)
    gallery.connect(store)

    # Connect layers controls
    layers_ui.add_subscriber(store.dispatch)
    layers_ui.connect(store)

    # Connect tools controls

    display_names = {
        "time_series": "Display Time Series",
        "profile": "Display Profile"
    }
    available_features = {
        k: display_names[k]
        for k in display_names.keys() if data.FEATURE_FLAGS[k]
    }

    tools_panel = tools.ToolsPanel(available_features)
    tools_panel.connect(store)

    # Navbar components
    navbar = Navbar(show_diagram_button=len(available_features) > 0)
    navbar.connect(store)

    # Connect tap listener
    tap_listener = screen.TapListener()
    tap_listener.connect(store)

    # Connect figure controls/views
    if config.defaults.figures.ui:
        figure_ui = layers.FigureUI(config.defaults.figures.maximum)
        figure_ui.connect(store)
    figure_row.connect(store)

    # Tiling picker
    if config.use_web_map_tiles:
        tile_picker = forest.components.TilePicker()
        for figure in figures:
            tile_picker.add_figure(figure)
        tile_picker.connect(store)

    # Connect color palette controls
    colors.ColorMapperView(color_mapper).connect(store)
    color_palette = colors.ColorPalette().connect(store)

    # Connect limit controllers to store
    user_limits = colors.UserLimits().connect(store)

    # Preset
    if config.defaults.presetui:
        preset_ui = presets.PresetUI().connect(store)

    # Connect navigation controls
    controls = db.ControlView()
    controls.connect(store)

    # Add support for a modal dialogue
    if data.FEATURE_FLAGS["multiple_colorbars"]:
        view = forest.components.modal.Tabbed()
    else:
        view = forest.components.modal.Default()
    modal = forest.components.Modal(view=view)
    modal.connect(store)

    # Connect components to Store
    app.connect(store)

    # Set initial state
    store.dispatch(forest.actions.set_state(config.state).to_dict())

    # Pre-select menu choices (if any)
    for pattern, _ in sub_navigators.items():
        state = db.initial_state(navigator, pattern=pattern)
        store.dispatch(forest.actions.update_state(state).to_dict())
        break

    # Set default time series visibility
    store.dispatch(tools.on_toggle_tool("time_series", False))

    # Set default profile visibility
    store.dispatch(tools.on_toggle_tool("profile", False))

    # Set top-level navigation
    store.dispatch(db.set_value("patterns", config.patterns))

    # Pre-select first map_view layer
    for label, dataset in datasets.items():
        pattern = label_to_pattern[label]
        for variable in navigator.variables(pattern):
            spec = {
                "label": label,
                "dataset": label,
                "variable": variable,
                "active": [0]
            }
            store.dispatch(forest.layers.save_layer(0, spec))
            break
        break

    # Set variable dimensions (needed by modal dialogue)
    for label, dataset in datasets.items():
        pattern = label_to_pattern[label]
        values = navigator.variables(pattern)
        store.dispatch(dimension.set_variables(label, values))

    # Organise controls/settings
    layouts = {}
    layouts["controls"] = []
    if config.defaults.figures.ui:
        layouts["controls"] += [
            bokeh.models.Div(text="Layout:"), figure_ui.layout
        ]
    layouts["controls"] += [
        bokeh.models.Div(text="Navigate:"), controls.layout,
        bokeh.models.Div(text="Compare:"), layers_ui.layout
    ]

    layouts["settings"] = [
        border_ui.layout,
        opacity_slider.layout,
        color_palette.layout,
        user_limits.layout,
        bokeh.models.Div(text="Tiles:"),
    ]
    if config.defaults.presetui:
        layouts["settings"].insert(2, preset_ui.layout)
    if config.use_web_map_tiles:
        layouts["settings"].append(tile_picker.layout)

    tabs = bokeh.models.Tabs(tabs=[
        bokeh.models.Panel(child=bokeh.layouts.column(*layouts["controls"]),
                           title="Control"),
        bokeh.models.Panel(child=bokeh.layouts.column(*layouts["settings"]),
                           title="Settings")
    ])

    tool_figures = {}
    if data.FEATURE_FLAGS["time_series"]:
        # Series sub-figure widget
        series_figure = bokeh.plotting.figure(plot_width=400,
                                              plot_height=200,
                                              x_axis_type="datetime",
                                              toolbar_location=None,
                                              border_fill_alpha=0)
        series_figure.toolbar.logo = None

        series_view = series.SeriesView.from_groups(series_figure,
                                                    config.file_groups)
        series_view.add_subscriber(store.dispatch)
        series_args = (rx.Stream().listen_to(store).map(
            series.select_args).filter(lambda x: x is not None).distinct())
        series_args.map(lambda a: series_view.render(*a))
        series_args.map(print)  # Note: map(print) creates None stream

        tool_figures["series_figure"] = series_figure

    if data.FEATURE_FLAGS["profile"]:
        # Profile sub-figure widget
        profile_figure = bokeh.plotting.figure(plot_width=300,
                                               plot_height=450,
                                               toolbar_location=None,
                                               border_fill_alpha=0)
        profile_figure.toolbar.logo = None
        profile_figure.y_range.flipped = True

        profile_view = profile.ProfileView.from_groups(profile_figure,
                                                       config.file_groups)
        profile_view.add_subscriber(store.dispatch)
        profile_args = (rx.Stream().listen_to(store).map(
            profile.select_args).filter(lambda x: x is not None).distinct())
        profile_args.map(lambda a: profile_view.render(*a))
        profile_args.map(print)  # Note: map(print) creates None stream

        tool_figures["profile_figure"] = profile_figure

    tool_layout = tools.ToolLayout(**tool_figures)
    tool_layout.connect(store)

    for f in figures:
        f.on_event(bokeh.events.Tap, tap_listener.update_xy)
        marker = screen.MarkDraw(f).connect(store)

    control_root = bokeh.layouts.column(tabs, name="controls")

    # Add key press support
    key_press = keys.KeyPress()
    key_press.add_subscriber(store.dispatch)

    # Add HTML ready support
    obj = html_ready.HTMLReady(key_press.hidden_button)
    obj.connect(store)

    document = bokeh.plotting.curdoc()
    document.title = "FOREST"
    document.add_root(control_root)
    document.add_root(
        bokeh.layouts.column(tools_panel.layout,
                             tool_layout.layout,
                             width=400,
                             name="series"))
    for root in navbar.roots:
        document.add_root(root)
    for root in app.roots:
        document.add_root(root)
    document.add_root(figure_row.layout)
    document.add_root(key_press.hidden_button)
    document.add_root(modal.layout)
Esempio n. 20
0
def main(argv=None):
    args = parse_args.parse_args(argv)
    if len(args.files) > 0:
        config = cfg.from_files(args.files, args.file_type)
    else:
        config = cfg.load_config(args.config_file)

    database = None
    if args.database is not None:
        if args.database != ':memory:':
            assert os.path.exists(args.database), "{} must exist".format(
                args.database)
        database = db.Database.connect(args.database)

    # Full screen map
    lon_range = (90, 140)
    lat_range = (-23.5, 23.5)
    x_range, y_range = geo.web_mercator(lon_range, lat_range)
    figure = bokeh.plotting.figure(x_range=x_range,
                                   y_range=y_range,
                                   x_axis_type="mercator",
                                   y_axis_type="mercator",
                                   active_scroll="wheel_zoom")
    tile = bokeh.models.WMTSTileSource(
        url="https://maps.wikimedia.org/osm-intl/{Z}/{X}/{Y}.png",
        attribution="")

    figures = [figure]
    for _ in range(2):
        f = bokeh.plotting.figure(x_range=figure.x_range,
                                  y_range=figure.y_range,
                                  x_axis_type="mercator",
                                  y_axis_type="mercator",
                                  active_scroll="wheel_zoom")
        figures.append(f)

    for f in figures:
        f.axis.visible = False
        f.toolbar.logo = None
        f.toolbar_location = None
        f.min_border = 0
        f.add_tile(tile)

    figure_row = bokeh.layouts.row(*figures, sizing_mode="stretch_both")
    figure_row.children = [figures[0]]  # Trick to keep correct sizing modes

    figure_drop = bokeh.models.Dropdown(label="Figure",
                                        menu=[(str(i), str(i))
                                              for i in [1, 2, 3]])

    def on_change(attr, old, new):
        if int(new) == 1:
            figure_row.children = [figures[0]]
        elif int(new) == 2:
            figure_row.children = [figures[0], figures[1]]
        elif int(new) == 3:
            figure_row.children = [figures[0], figures[1], figures[2]]

    figure_drop.on_change("value", on_change)

    color_mapper = bokeh.models.LinearColorMapper(
        low=0, high=1, palette=bokeh.palettes.Plasma[256])
    for figure in figures:
        colorbar = bokeh.models.ColorBar(color_mapper=color_mapper,
                                         orientation="horizontal",
                                         background_fill_alpha=0.,
                                         location="bottom_center",
                                         major_tick_line_color="black",
                                         bar_line_color="black")
        figure.add_layout(colorbar, 'center')

    # Database/File system loader(s)
    for group in config.file_groups:
        if group.label not in data.LOADERS:
            if group.locator == "database":
                loader = load.Loader.group_args(group, args, database=database)
            else:
                loader = load.Loader.group_args(group, args)
            data.add_loader(group.label, loader)

    renderers = {}
    viewers = {}
    for name, loader in data.LOADERS.items():
        if isinstance(loader, rdt.Loader):
            viewer = rdt.View(loader)
        elif isinstance(loader, earth_networks.Loader):
            viewer = earth_networks.View(loader)
        elif isinstance(loader, data.GPM):
            viewer = view.GPMView(loader, color_mapper)
        elif isinstance(loader, satellite.EIDA50):
            viewer = view.EIDA50(loader, color_mapper)
        else:
            viewer = view.UMView(loader, color_mapper)
        viewers[name] = viewer
        renderers[name] = [viewer.add_figure(f) for f in figures]

    artist = Artist(viewers, renderers)
    renderers = []
    for _, r in artist.renderers.items():
        renderers += r

    image_sources = []
    for name, viewer in artist.viewers.items():
        if isinstance(viewer, (view.UMView, view.GPMView, view.EIDA50)):
            image_sources.append(viewer.source)

    # Lakes
    for figure in figures:
        add_feature(figure, data.LAKES, color="lightblue")

    features = []
    for figure in figures:
        features += [
            add_feature(figure, data.COASTLINES),
            add_feature(figure, data.BORDERS)
        ]

    # Disputed borders
    for figure in figures:
        add_feature(figure, data.DISPUTED, color="red")

    toggle = bokeh.models.CheckboxButtonGroup(labels=["Coastlines"],
                                              active=[0],
                                              width=135)

    def on_change(attr, old, new):
        if len(new) == 1:
            for feature in features:
                feature.visible = True
        else:
            for feature in features:
                feature.visible = False

    toggle.on_change("active", on_change)

    dropdown = bokeh.models.Dropdown(label="Color",
                                     menu=[("Black", "black"),
                                           ("White", "white")],
                                     width=50)
    autolabel(dropdown)

    def on_change(attr, old, new):
        for feature in features:
            feature.glyph.line_color = new

    dropdown.on_change("value", on_change)

    slider = bokeh.models.Slider(start=0,
                                 end=1,
                                 step=0.1,
                                 value=1.0,
                                 show_value=False)

    def is_image(renderer):
        return isinstance(getattr(renderer, 'glyph', None), bokeh.models.Image)

    image_renderers = [r for r in renderers if is_image(r)]
    custom_js = bokeh.models.CustomJS(args=dict(renderers=image_renderers),
                                      code="""
            renderers.forEach(function (r) {
                r.glyph.global_alpha = cb_obj.value
            })
            """)
    slider.js_on_change("value", custom_js)

    colors_controls = colors.Controls(color_mapper, "Plasma", 256)

    mapper_limits = MapperLimits(image_sources, color_mapper)

    menu = []
    for k, _ in config.patterns:
        menu.append((k, k))

    image_controls = images.Controls(menu)

    def on_change(attr, old, new):
        if int(new) == 1:
            image_controls.labels = ["Show"]
        elif int(new) == 2:
            image_controls.labels = ["L", "R"]
        elif int(new) == 3:
            image_controls.labels = ["L", "C", "R"]

    figure_drop.on_change("value", on_change)

    image_controls.subscribe(artist.on_visible)

    div = bokeh.models.Div(text="", width=10)
    border_row = bokeh.layouts.row(bokeh.layouts.column(toggle),
                                   bokeh.layouts.column(div),
                                   bokeh.layouts.column(dropdown))

    # Pre-select first layer
    for name, _ in config.patterns:
        image_controls.select(name)
        break

    navigator = navigate.Navigator(config, database)

    # Pre-select menu choices (if any)
    initial_state = {}
    for _, pattern in config.patterns:
        initial_state = db.initial_state(navigator, pattern=pattern)
        break
    middlewares = [
        db.Log(verbose=True), keys.navigate,
        db.InverseCoordinate("pressure"), db.next_previous,
        db.Controls(navigator),
        db.Converter({
            "valid_times": db.stamps,
            "inital_times": db.stamps
        })
    ]
    store = redux.Store(db.reducer,
                        initial_state=initial_state,
                        middlewares=middlewares)
    controls = db.ControlView()
    controls.subscribe(store.dispatch)
    store.subscribe(controls.render)
    old_states = (db.Stream().listen_to(store).map(lambda x: db.State(**x)))
    old_states.subscribe(artist.on_state)

    # Ensure all listeners are pointing to the current state
    store.notify(store.state)
    store.dispatch(db.set_value("patterns", config.patterns))

    tabs = bokeh.models.Tabs(tabs=[
        bokeh.models.Panel(child=bokeh.layouts.column(
            bokeh.models.Div(text="Navigate:"), controls.layout,
            bokeh.models.Div(text="Compare:"), bokeh.layouts.row(figure_drop),
            image_controls.column),
                           title="Control"),
        bokeh.models.Panel(child=bokeh.layouts.column(
            border_row,
            bokeh.layouts.row(slider),
            colors_controls.layout,
            bokeh.layouts.row(mapper_limits.low_input),
            bokeh.layouts.row(mapper_limits.high_input),
            bokeh.layouts.row(mapper_limits.checkbox),
        ),
                           title="Settings")
    ])

    # Series sub-figure widget
    series_figure = bokeh.plotting.figure(plot_width=400,
                                          plot_height=200,
                                          x_axis_type="datetime",
                                          toolbar_location=None,
                                          border_fill_alpha=0)
    series_figure.toolbar.logo = None
    series_row = bokeh.layouts.row(series_figure, name="series")

    def place_marker(figure, source):
        figure.circle(x="x", y="y", color="red", source=source)

        def cb(event):
            source.data = {"x": [event.x], "y": [event.y]}

        return cb

    marker_source = bokeh.models.ColumnDataSource({"x": [], "y": []})
    series = Series.from_groups(series_figure,
                                config.file_groups,
                                directory=args.directory)
    old_states.subscribe(series.on_state)
    for f in figures:
        f.on_event(bokeh.events.Tap, series.on_tap)
        f.on_event(bokeh.events.Tap, place_marker(f, marker_source))

    # Minimise controls to ease navigation
    compact_button = bokeh.models.Button(label="Compact")
    compact_minus = bokeh.models.Button(label="-", width=50)
    compact_plus = bokeh.models.Button(label="+", width=50)
    compact_navigation = bokeh.layouts.column(
        compact_button,
        bokeh.layouts.row(compact_minus, compact_plus, width=100))
    control_root = bokeh.layouts.column(compact_button, tabs, name="controls")

    display = "large"

    def on_compact():
        nonlocal display
        if display == "large":
            control_root.height = 100
            control_root.width = 120
            compact_button.width = 100
            compact_button.label = "Expand"
            control_root.children = [compact_navigation]
            display = "compact"
        else:
            control_root.height = 500
            control_root.width = 300
            compact_button.width = 300
            compact_button.label = "Compact"
            control_root.children = [compact_button, tabs]
            display = "large"

    compact_button.on_click(on_compact)

    # Add key press support
    key_press = keys.KeyPress()
    key_press.subscribe(store.dispatch)

    document = bokeh.plotting.curdoc()
    document.title = "FOREST"
    document.add_root(control_root)
    document.add_root(series_row)
    document.add_root(figure_row)
    document.add_root(key_press.hidden_button)
Esempio n. 21
0
def getRDT(path, lev, type):
    """
    Gets RDT data from the netcdf output of the NWCSAF software
    :param path: Full path and filename of the netcdf file
    :param lev: [0,1] Level number. 0 = bottom of the cloud, 1 = top of the cloud (heights vary between clouds)
    :param type: ['All', 'Centre_Point', 'Polygon', 'Tail_Points', 'Tail_Lines', 'Gridded']
    :return: geojson feature collection object for plotting
    """

    # Load the netcdf data
    ncds = nc.Dataset(path)

    # Get variable names that have either 'nlevel' or 'recNUM' dimensions
    varlev = []  # 'nlevel' dimension
    varrec = []  # 'recNUM' dimension
    vartraj = []  # 'nbpttraj' Trajectory points for each object
    for ncv in ncds.variables.keys():
        var_dim = ncds.variables[ncv].dimensions
        if "nlevel" in var_dim:
            varlev.append(ncv)
        if ("recNUM" in var_dim) and (ncds.dimensions["recNUM"].size
                                      == ncds.variables[ncv].size):
            varrec.append(ncv)
        if "nbpttraj" in var_dim:
            vartraj.append(ncv)
    allvars = varlev.copy()
    allvars.extend(varrec)

    # How many cloud levels have we got? It should only be 2 (cloud top and bottom)
    dimsize = len(ma.getdata(ncds.variables[varlev[0]][0, :]))

    # Convert units from the netcdf
    unitsToRescale = {"Pa": "hPa"}  # , 'K': 'degC'}
    # Get text labels instead of numbers for certain fields
    fieldsToLookup = [
        "PhaseLife",
        "SeverityType",
        "SeverityIntensity",
        "ConvType",
        "CType",
    ]

    # Check the lev is within the dimsize
    if not lev in np.arange(dimsize):
        return "Please enter a valid level number (0 or 1)"
    else:
        list_to_return = []

    if type in ["Polygon", "All"]:

        # Create list of features to populate
        features = []
        fieldsToLookup = [
            "PhaseLife",
            "SeverityType",
            "SeverityIntensity",
            "ConvType",
            "CType",
        ]
        for i in np.arange(ncds.dimensions["recNUM"].size):

            # do something
            ypolcoords_ll = getDataOnly(ncds.variables["LatContour"][i,
                                                                     lev, :])
            xpolcoords_ll = getDataOnly(ncds.variables["LonContour"][i,
                                                                     lev, :])

            xpolcoords, ypolcoords = geo.web_mercator(xpolcoords_ll,
                                                      ypolcoords_ll)

            this_poly = Polygon([[(float(coord[0]), float(coord[1]))
                                  for coord in zip(xpolcoords, ypolcoords)]])

            # Get the properties for this polygon
            pol_props = {}
            for var in allvars:

                if var in varlev:
                    thisdata = ncds.variables[var][i, lev]
                else:
                    thisdata = ncds.variables[var][i]

                thisdata_scaled = convert_values(thisdata, ncds.variables[var])
                datatype = ncds.variables[var].datatype

                if var in fieldsToLookup:
                    try:
                        thisdata_scaled = fieldValueLUT(var, int(thisdata))
                        datatype = "string"
                    except:
                        continue

                update_json(pol_props, var, thisdata_scaled, datatype)

            features.append(Feature(geometry=this_poly, properties=pol_props))

        feature_collection = FeatureCollection(features)

        list_to_return.append(json.dumps(feature_collection))

    if type in ["Tail_Lines", "All"]:

        # Create an empty dictionary
        mydict_tl = get_empty_feature_dict("Tail_Lines")

        # Loop through features
        for i in np.arange(ncds.dimensions["recNUM"].size):

            # Loop through all items in mydict_tl
            for k in mydict_tl.keys():
                try:
                    thisdata, units = descale_rdt(
                        k, getDataOnly(ncds.variables[k][:]))
                    mydict_tl[k].append(thisdata)
                except:
                    # Do nothing at the moment with the xs and ys
                    if not k in ["xs", "ys"]:
                        mydict_tl[k].append(None)
                    else:
                        continue

            lats = getDataOnly(ncds.variables["LatTrajCellCG"][i, :])
            lons = getDataOnly(ncds.variables["LonTrajCellCG"][i, :])

            xs, ys = geo.web_mercator(lons, lats)
            mydict_tl["xs"].append(xs)
            mydict_tl["ys"].append(ys)

        list_to_return.append(mydict_tl)

    if type in ["Tail_Points", "All"]:

        # Create an empty dictionary
        mydict_tp = get_empty_feature_dict("Tail_Points")

        for i in np.arange(ncds.dimensions["recNUM"].size):

            # do something
            npts = len(getDataOnly(ncds.variables["LonTrajCellCG"][i]))
            mykeys = [k for k in mydict_tp.keys() if k not in ["x", "y"]]
            for k in mykeys:

                thisdata = getDataOnly(ncds.variables[k][i]).tolist()

                try:
                    if len(thisdata) == 1:
                        thisdata = thisdata[0]
                except:
                    pass

                if isinstance(thisdata, list) and (npts == len(thisdata)):
                    datalist, units = descale_rdt(k, thisdata)
                    mydict_tp[k].extend(datalist)
                else:
                    # Repeat the data value npts times
                    datalist = [i for i in itertools.repeat(thisdata, npts)]
                    thisdata, units = descale_rdt(k, datalist)
                    mydict_tp[k].extend(datalist)

                # Some records don't have any data. Mostly happens for ExpanRateTraj
                if len(thisdata) == 0:
                    datalist = [i for i in itertools.repeat("-", npts)]
                    mydict_tp[k].extend(datalist)

            lats = getDataOnly(ncds.variables["LatTrajCellCG"][i, :])
            lons = getDataOnly(ncds.variables["LonTrajCellCG"][i, :])
            x, y = geo.web_mercator(lons, lats)
            mydict_tp["x"].extend(x)
            mydict_tp["y"].extend(y)

        list_to_return.append(mydict_tp)

    # Do specific things for each feature type
    if type in ["Centre_Point", "All"]:

        # Create an empty dictionary
        mydict_cp = get_empty_feature_dict("Centre_Point")

        for i in np.arange(ncds.dimensions["recNUM"].size):

            # Get the Point features
            lat = ncds.variables["LatG"][i, lev]
            lon = ncds.variables["LonG"][i, lev]
            x1, y1 = geo.web_mercator(lon, lat)
            mydict_cp["x1"].extend(x1)
            mydict_cp["y1"].extend(y1)

            mykeys = [
                k for k in mydict_cp.keys() if not (("x" in k) or ("y" in k))
            ]
            for k in mykeys:
                try:
                    if k in varlev:
                        thisdata = ncds.variables[k][i, lev]
                    else:
                        thisdata = ncds.variables[k][i]

                    thisdata, units = descale_rdt(
                        k, thisdata)  # May not need to do this
                    mydict_cp[k].append(thisdata)
                except:
                    mydict_cp[k].append(None)

            # Now calculate future point and line
            try:
                speed = float(ncds.variables["MvtSpeed"][i])
            except ValueError:
                speed = 0
            try:
                direction = float(ncds.variables["MvtDirection"][i])
            except ValueError:
                direction = 0

            mydict_cp = make_arrow(mydict_cp, lon, lat, speed, direction)

        list_to_return.append(mydict_cp)

    if len(list_to_return) == 1:
        return list_to_return[0]
    elif len(list_to_return) > 1:
        return tuple(list_to_return)
    else:
        return "Nothing to return"