Example #1
0
def polygon_map(area, source):
    from ipyleaflet import Map, DrawControl

    center, zoom = centroid(area, source)

    m = Map(center=center, zoom=zoom)

    draw_control = DrawControl()
    draw_control.polygon = {
        "shapeOptions": {
            "fillColor": "#6be5c3",
            "color": "#6be5c3",
            "fillOpacity": 1.0
        },
        "drawError": {
            "color": "#dd253b",
            "message": "Oups!"
        },
        "allowIntersection": False
    }

    polygon_map.feature_collection = {
        'type': 'FeatureCollection',
        'features': []
    }

    def handle_draw(self, action, geo_json):
        """Do something with the GeoJSON when it's drawn on the map"""
        polygon_map.feature_collection['features'].append(geo_json)

    draw_control.on_draw(handle_draw)

    m.add_control(draw_control)

    return m
Example #2
0
def run():
    global m,center
#    center = list(reversed(poly.centroid().coordinates().getInfo()))
    center = [51.0,6.4]
    osm = basemap_to_tiles(basemaps.OpenStreetMap.Mapnik)
    ews = basemap_to_tiles(basemaps.Esri.WorldStreetMap)
    ewi = basemap_to_tiles(basemaps.Esri.WorldImagery)
    
    dc = DrawControl(polyline={},circlemarker={})
    dc.rectangle = {"shapeOptions": {"fillColor": "#0000ff","color": "#0000ff","fillOpacity": 0.05}}
    dc.polygon = {"shapeOptions": {"fillColor": "#0000ff","color": "#0000ff","fillOpacity": 0.05}}
    dc.on_draw(handle_draw)
    
    lc = LayersControl(position='topright')
    fs = FullScreenControl(position='topleft')
    mc = MeasureControl(position='topright',primary_length_unit = 'kilometers')

    m = Map(center=center, zoom=11, layout={'height':'500px'},layers=(ewi,ews,osm),controls=(mc,dc,lc,fs))   
#    m = Map(center=center, zoom=11, layout={'height':'500px'},controls=(lc,dc,fs,mc,sm_control)) 

    with w_out:
        w_out.clear_output()
        print('Algorithm output')
    display(m) 
    return box
Example #3
0
    def __init__(self, bbox, zoom=8, resolution=10):
        center = (bbox.min_y + bbox.max_y) / 2, (bbox.min_x + bbox.max_x) / 2
        self.map = Map(center=center, zoom=zoom, scroll_wheel_zoom=True)

        self.resolution = resolution

        control = DrawControl()

        control.rectangle = {
            "shapeOptions": {
                "fillColor": "#fabd14",
                "color": "#fa6814",
                "fillOpacity": 0.2
            }
        }

        #Disable the rest of draw options
        control.polyline = {}
        control.circle = {}
        control.circlemarker = {}
        control.polygon = {}
        control.edit = False
        control.remove = False

        control.on_draw(self._handle_draw)

        self.map.add_control(control)

        self.bbox = None
        self.size = None
        self.rectangle = None
        self.add_rectangle(bbox.min_x, bbox.min_y, bbox.max_x, bbox.max_y)
Example #4
0
class Grass2Leaflet(object):
    def __init__(self, grassimg):
        self.grassimg = grassimg
        self.draw_control = None
        self.zoom = 15
        self.center = self.centermap()
        self.m = Map(default_tiles=TileLayer(opacity=1.0),
                     center=self.center,
                     zoom=self.zoom)

    def centermap(self):
        centerlat = []
        centerlon = []
        for i in self.grassimg:
            centerlat.append(self.grassimg[i]['C'][0])
            centerlon.append(self.grassimg[i]['C'][1])
        center = (sum(centerlat) / float(len(centerlat)),
                  sum(centerlon) / float(len(centerlon)))
        return center

    def imgoverlays(self):
        self.leafletimg = OrderedDict()
        for i in self.grassimg:
            layer = ImageOverlay(url=self.grassimg[i]['raster'],
                                 bounds=(self.grassimg[i]['LL'],
                                         self.grassimg[i]['UR']))
            self.leafletimg[i] = layer

    def render(self, draw_control=None):
        self.imgoverlays()
        self.dc = None
        options = ['None']
        self.m.add_layer(self.leafletimg[list(self.grassimg.keys())[-1]])
        if len(self.grassimg) >= 2:
            self.maplist = widgets.Dropdown(
                options=options + list(self.grassimg.keys()),
                value=list(self.grassimg.keys())[-1],
                description='Select Layer:',
            )
            self.maplist.observe(self.on_value_change, names='value')
            display(self.maplist)
        if draw_control:
            self.dc = DrawControl()
            self.dc.on_draw(handle_draw)
            self.m.add_control(self.dc)
        display(self.m)
        return {'map': self.m, 'drawer': self.dc}

    def on_value_change(self, layername):
        self.m.clear_layers()
        self.m.add_layer(TileLayer(opacity=1.0))
        if self.maplist.value != 'None':
            self.m.add_layer(self.leafletimg[layername['new']])

    def main(self):
        self.imgoverlays()
        self.render()
Example #5
0
    def __create_draw_control(self):
        dc = DrawControl(rectangle={'shapeOptions': {'color': '#0000FF'}}, circle={}, circlemarker={}, polyline={},
                         marker={}, polygon={})
        dc.edit = False
        dc.remove = False

        # handle drawing and deletion of annotations and corresponding classes
        def handle_draw(target, action, geo_json):
            # print(target)
            # print(action)
            # print(geo_json)
            if action == 'created' and geo_json['geometry']['type'] == 'Polygon':
                coordinates = geo_json['geometry']['coordinates'][0]
                #print(coordinates)
                #print(self.__map)
                #print(coordinates)
                hs = [c[1] for c in coordinates]
                ws = [c[0] for c in coordinates]
                min_h, max_h = min(hs), max(hs)
                min_w, max_w = min(ws), max(ws)
                
                # coordinates only inside image
                hh, ww, offset_h, offset_w = self.__img_coords[2:]
                max_h = max(0, min(hh + offset_h, max_h))
                max_w = max(0, min(ww + offset_w, max_w))
                min_h = max(offset_h, min(hh + offset_h, min_h))
                min_w = max(offset_w, min(ww + offset_w, min_w))
                
                
                # remove draw
                dc.clear()

                if max_h - min_h < 1 or max_w - min_w < 1:
                    print(labels_str.warn_skip_wrong )
                    return

                # print(min_h, max_h, min_w, max_w)
                # create rectangle layer and remove drawn geojson
                rectangle = self.__create_rectangle(((min_h, min_w), (max_h, max_w)))
                self.__current_image_annotations.append(rectangle)
                self.__current_class_for_ann.append(None)
                self.__map.add_layer(rectangle)
                # automatically select last annotation
                self.__selected_ann = len(self.__current_image_annotations)-1
                self.__reset_colors_bboxes()
                self.__selected_class = None
                self.__reset_classes_btns()
                self.__delete_button.disabled = False
                # print('Adding ann at index:',len(self.current_image_annotations)-1,
                # ' with class', self.current_class_for_ann[-1])

        dc.on_draw(handle_draw)
        self.__map.add_control(dc)
    def __init__(self, center, zoom):
        self.map = Map(center=center,
                       zoom=zoom,
                       scroll_wheel_zoom=True,
                       interpolation='nearest')
        self.bbox = []
        self.point_coords = []
        self.figure = None
        self.figure_widget = None
        feature_collection = {'type': 'FeatureCollection', 'features': []}

        draw = DrawControl(
            circlemarker={},
            polyline={},
            polygon={},
            marker={"shapeOptions": {
                "original": {},
                "editing": {},
            }},
            rectangle={"shapeOptions": {
                "original": {},
                "editing": {},
            }})

        self.map.add_control(draw)

        def handle_draw(target, action, geo_json):
            feature_collection['features'] = []
            feature_collection['features'].append(geo_json)
            if feature_collection['features'][0]['geometry'][
                    'type'] == 'Point':
                self.point_coords = feature_collection['features'][0][
                    'geometry']['coordinates']
            else:
                coords = feature_collection['features'][0]['geometry'][
                    'coordinates'][0]
                polygon = shapely.geometry.Polygon(coords)
                self.bbox = polygon.bounds

        layers_control = LayersControl(position='topright')
        self.map.add_control(layers_control)
        self.map.add_control(FullScreenControl())
        self.map.add_layer(basemap_to_tiles(basemaps.Esri.WorldImagery))
        draw.on_draw(handle_draw)
Example #7
0
class App():
    """ """

    settings = {"enabled_grid": "B"}

    def __init__(self, session=None):

        self.session = session
        self.use_grid = self.settings["enabled_grid"]

        # generate map grid polygon layers
        self.grid_layers = LayerGroup()
        self.grid_dict = {}

        for feat in above_grid["features"]:
            level = feat["properties"]["grid_level"]
            if level == self.use_grid:
                Cell_object = Cell(feat)
                #Cell_object.layer.on_click()

                grid_id = Cell_object.id
                self.grid_dict[grid_id] = Cell_object
                self.grid_layers.add_layer(self.grid_dict[grid_id].layer)

        # make an attribute that will hold selected layer
        self.selected_layer = LayerGroup()

        self.map = Map(layers=(
            esri,
            self.grid_layers,
            self.selected_layer,
        ),
                       center=(65, -100),
                       zoom=3,
                       width="auto",
                       height="auto",
                       scroll_wheel_zoom=True)

        # map draw controls
        self.draw_control = DrawControl()
        self.draw_control.polyline = {}
        self.draw_control.circle = {}
        self.draw_control.circlemarker = {}
        self.draw_control.remove = False
        self.draw_control.edit = False
        self.draw_control.polygon = {**draw_style}
        self.draw_control.rectangle = {**draw_style}
        self.draw_control.on_draw(self.update_selected_cells)
        self.map.add_control(self.draw_control)

        # output display
        self.output = Output(layout=Layout(width="auto", height="auto"))

        # make the widget layout
        self.ui = VBox(
            [
                #header,
                #HBox([instruct, geojson_text]),
                self.map,
                self.output
            ],
            layout=Layout(width="auto"))

        # display ui
        display(self.ui)

    def update_selected_cells(self, *args, **kwargs):
        """ """
        # clear all draw and selection layers
        self.draw_control.clear()

        # --------------------------------------------------------------------
        # update active cells and make a big merged polgyon for selection

        # make shapely geom from geojson
        drawn_json = kwargs["geo_json"]
        shapely_geom = shape(drawn_json["geometry"])
        cells = self.grid_dict

        # iterate over cells and collect intersecting cells
        on = []
        for id, cell in cells.items():
            if shapely_geom.intersects(cell.shape):
                on.append(cell.shape)

        # this is blatant abuse of try/except; fix it
        try:
            # get the union of all of the cells that are toggled on
            union = cascaded_union(on)
            centroid = union.centroid

            # make layer that represents selected cells and add to selected_layer
            self.selected_layer.clear_layers()
            x, y = union.exterior.coords.xy
            self.selected_layer.add_layer(Polygon(locations=list(zip(y, x))))
            self.map.center = (centroid.y, centroid.x)

            # --------------------------------------------------------------
            # find all CMR collections that intersect with merged cells geom

            selected = []
            for index, collection in above_results_df.iterrows():
                box = collection.boxes
                shapely_box = CMR_box_to_Shapely_box(box[0])

                # intersect: use shapely_geom if strictly using drawn poly
                intersect_bool = shapely_box.intersects(union)
                if intersect_bool:
                    selected.append(index)

            self.coll = above_results_df.iloc[selected]

            self.tab = qgrid.show_grid(
                self.coll[["dataset_id", "time_start", "time_end", "boxes"]],
                grid_options={
                    'forceFitColumns': False,
                    'minColumnWidth': "0",
                    'maxColumnWidth': "400"
                },
                show_toolbar=False)

            self.output.clear_output()
            with self.output:
                display(self.tab)
                #display(self.coll[[
                #    "dataset_id", "time_start", "time_end", "boxes"]])

        except:
            pass
Example #8
0
def sel_params_form(processing_parameters: ProcessingParameters,
                    identifier='identifier',
                    name='name',
                    mock=False):
    debug_view = get_debug_view()

    fetch_inputs_func = fetch_inputs
    if mock:

        @debug_view.capture(clear_output=True)
        def fetch_inputs_mock(input_request: InputRequest,
                              message_func) -> ProcessingRequest:
            debug_view.value = ''
            time.sleep(2)
            input_identifiers = {
                input_type: [f'iid-{i}' for i in range(10)]
                for input_type in input_request.input_types
            }
            processing_request_data = input_request.as_dict()
            processing_request_data.update(
                dict(inputIdentifiers=input_identifiers))
            return ProcessingRequest(processing_request_data)

        fetch_inputs_func = fetch_inputs_mock

    form_item_layout = widgets.Layout(
        display='flex',
        flex_flow='row',
        justify_content='space-between',
    )
    var_checks_layout = widgets.Layout(
        display='flex',
        flex_flow='row',
        justify_content='center',
    )

    variable_names = []
    for variable_id in processing_parameters.variables.ids:
        variable_names.append(
            processing_parameters.variables.get(variable_id).name)
    forward_model_names = []
    for model_id in processing_parameters.forward_models.ids:
        forward_model_names.append(
            processing_parameters.forward_models.get(model_id).name)
    variable_boxes_dict = _get_checkboxes_dict(
        processing_parameters.variables.ids, variable_names)
    forward_model_boxes_dict = _get_checkboxes_dict(
        processing_parameters.forward_models.ids, forward_model_names)
    request_validation = widgets.HTML(
        value=html_element('h3',
                           att=dict(style='color:red'),
                           value='No variable or forward model selected'))
    non_disabled_forward_models = []
    available_forward_models_per_type = {}
    forward_models_per_variable = {}
    forward_model_select_buttons = {}
    for fm_id in processing_parameters.forward_models.ids:
        non_disabled_forward_models.append(fm_id)
        fm = processing_parameters.forward_models.get(fm_id)
        if not fm.input_type in available_forward_models_per_type:
            available_forward_models_per_type[fm.input_type] = []
        available_forward_models_per_type[fm.input_type].append(fm_id)
        for variable in fm.variables:
            if not variable in forward_models_per_variable:
                forward_models_per_variable[variable] = []
            forward_models_per_variable[variable].append(fm_id)
    selected_forward_models = []
    selected_variables = []
    selected_forward_model_per_type = {}
    for it in processing_parameters.input_types.ids:
        selected_forward_model_per_type[it] = None

    def _fm_variables(fm_id: str):
        return processing_parameters.forward_models.get(fm_id).variables

    def _fm_input_type(fm_id: str):
        return processing_parameters.forward_models.get(fm_id).input_type

    def _recommend(id: str):
        if id in processing_parameters.variables.ids:
            _recommend_box(variable_boxes_dict[id])
        elif id in processing_parameters.forward_models.ids:
            if id not in non_disabled_forward_models:
                non_disabled_forward_models.append(id)
            _recommend_box(forward_model_boxes_dict[id])

    def _recommend_box(box: LabeledCheckbox):
        box.enabled = True
        box.color = "green"
        box.font_weight = "bold"

    def _disable(id: str):
        if id in processing_parameters.variables.ids:
            _disable_box(variable_boxes_dict[id])
        elif id in processing_parameters.forward_models.ids:
            if id in non_disabled_forward_models:
                non_disabled_forward_models.remove(id)
            _disable_box(forward_model_boxes_dict[id])

    def _disable_box(box: LabeledCheckbox):
        box.enabled = False
        box.font_weight = "normal"

    def _display_normally(id: str):
        if id in processing_parameters.variables.ids:
            _display_normally_box(variable_boxes_dict[id])
        elif id in processing_parameters.forward_models.ids:
            if id not in non_disabled_forward_models:
                non_disabled_forward_models.append(id)
            _display_normally_box(forward_model_boxes_dict[id])

    def _display_normally_box(box: LabeledCheckbox):
        box.enabled = True
        box.color = "black"
        box.font_weight = "bold"

    def _request_status() -> str:
        if len(selected_variables) == 0 and len(selected_forward_models) == 0:
            return 'No variable or forward model selected'
        elif len(selected_variables) == 0:
            return 'No variable selected'
        elif len(selected_forward_models) == 0:
            return 'No forward model selected'
        else:
            for selected_variable in selected_variables:
                forward_model_available = False
                for variable_fm in forward_models_per_variable[
                        selected_variable]:
                    if variable_fm in selected_forward_models:
                        forward_model_available = True
                        break
                if not forward_model_available:
                    return f"Variable '{selected_variable}' cannot be derived with any of the selected forward models."
            for selected_forward_model in selected_forward_models:
                at_least_one_variable_selected = False
                for variable in processing_parameters.forward_models.get(
                        selected_forward_model).variables:
                    if variable in selected_variables:
                        at_least_one_variable_selected = True
                        break
                if not at_least_one_variable_selected:
                    return f"Selection is valid, " \
                           f"but no variable is selected for forward model '{selected_forward_model}'."
            return 'Selection is valid'

    def _validate_selection():
        color = 'red'
        request_status = _request_status()
        if request_status.startswith('Selection is valid'):
            if request_status == 'Selection is valid':
                color = 'green'
            else:
                color = 'orange'
            request_status = _format_request_status(request_status)
        request_validation.value = html_element(
            'h3', att=dict(style=f'color:{color}'), value=request_status)

    def _format_request_status(request_status: str) -> str:
        if not request_status.startswith('Selection is valid'):
            return request_status
        forward_model_lines = []
        for forward_model_id in selected_forward_models:
            fm_selected_variables = []
            fm_variables = processing_parameters.forward_models.get(
                forward_model_id).variables
            for fm_variable in fm_variables:
                if fm_variable in selected_variables:
                    fm_selected_variables.append(f"'{fm_variable}'")
            if len(fm_selected_variables) > 0:
                fm_selected_variables = ', '.join(fm_selected_variables)
                forward_model_lines.append(
                    f"With model '{forward_model_id}': Compute {fm_selected_variables}"
                )
            else:
                forward_model_lines.append(
                    f"Model '{forward_model_id}' "
                    f"is selected, but does not compute any variables")
        forward_model_lines = '<br>'.join(forward_model_lines)
        message = f"{request_status}:<br>{forward_model_lines}"
        return message

    def _handle_variable_selection(change: dict):
        if change['name'] is not '_property_lock':
            return
        variable_id = change['owner'].label_text
        if change['new']['selected']:
            selected_variables.append(variable_id)
        else:
            selected_variables.remove(variable_id)
        _update_forward_models_after_variable_change(variable_id, True)
        _validate_selection()

    def _update_forward_models_after_variable_change(
            variable_id: str, examine_secondary_models: bool = False):
        already_examined_types = []
        for potential_forward_model in forward_models_per_variable[
                variable_id]:
            fm_type = _fm_input_type(potential_forward_model)
            if (selected_forward_model_per_type[fm_type]
                ) is not None or fm_type in already_examined_types:
                continue
            _validate_forward_models_of_type(fm_type)
            _validate_variables_of_forward_models_of_type(fm_type)
            if examine_secondary_models:
                _validate_forward_models_of_variables_of_forward_models_of_type(
                    fm_type)
            already_examined_types.append(fm_type)

    def _validate_forward_models_of_type(fm_type: str):
        for fm_of_same_type in available_forward_models_per_type[fm_type]:
            if selected_forward_model_per_type[fm_type] == fm_of_same_type:
                _recommend(fm_of_same_type)
                continue
            if selected_forward_model_per_type[fm_type] is not None:
                _disable(fm_of_same_type)
                continue
            fm_of_same_type_variables = _fm_variables(fm_of_same_type)
            at_least_one_variable_selected = False
            for fm_of_same_type_variable in fm_of_same_type_variables:
                if fm_of_same_type_variable in selected_variables:
                    afms_for_st_variable = _available_forward_models_for_variable(
                        fm_of_same_type_variable)
                    if len(afms_for_st_variable) == 1 and _fm_input_type(
                            afms_for_st_variable[0]) == fm_type:
                        _recommend(fm_of_same_type)
                        for other_fm_of_same_type in available_forward_models_per_type[
                                fm_type]:
                            if other_fm_of_same_type != fm_of_same_type:
                                _disable(other_fm_of_same_type)
                        return
                    at_least_one_variable_selected = True
            if at_least_one_variable_selected:
                _recommend(fm_of_same_type)
            else:
                _display_normally(fm_of_same_type)

    def _validate_variables_of_forward_models_of_type(fm_type: str):
        for fm_of_same_type in available_forward_models_per_type[fm_type]:
            fm_of_same_type_variables = _fm_variables(fm_of_same_type)
            for fm_of_same_type_variable in fm_of_same_type_variables:
                _validate_variable(fm_of_same_type_variable)

    def _validate_forward_models_of_variables_of_forward_models_of_type(
            fm_type: str):
        for fm_of_same_type in available_forward_models_per_type[fm_type]:
            fm_of_same_type_variables = _fm_variables(fm_of_same_type)
            for fm_of_same_type_variable in fm_of_same_type_variables:
                afms_for_same_type_variable = _available_forward_models_for_variable(
                    fm_of_same_type_variable)
                if _fm_input_type(afms_for_same_type_variable[0]) != fm_type:
                    _validate_forward_models_of_type(
                        _fm_input_type(afms_for_same_type_variable[0]))
                    _validate_variables_of_forward_models_of_type(
                        _fm_input_type(afms_for_same_type_variable[0]))

    def _available_forward_models_for_variable(variable_id: str) -> List[str]:
        available_forward_models_for_variable = []
        for model_id in forward_models_per_variable[variable_id]:
            if model_id in non_disabled_forward_models:
                available_forward_models_for_variable.append(model_id)
        return available_forward_models_for_variable

    def _validate_variable(variable: str):
        if variable in selected_variables:
            _recommend(variable)
            return
        available_forward_models_for_variable = _available_forward_models_for_variable(
            variable)
        if len(available_forward_models_for_variable) == 0:
            _disable(variable)
            return
        for available_forward_model_for_variable in available_forward_models_for_variable:
            if available_forward_model_for_variable in selected_forward_models:
                _recommend(variable)
                return
        _display_normally(variable)

    @debug_view.capture(clear_output=True)
    def _handle_forward_model_selection(change: dict):
        if change['name'] is not '_property_lock':
            return
        if 'selected' not in change['new']:
            return
        selected_fm_id = change['owner'].label_text
        selected_fm_it = _fm_input_type(selected_fm_id)
        if change['new']['selected']:
            selected_forward_models.append(selected_fm_id)
            selected_forward_model_per_type[selected_fm_it] = selected_fm_id
            forward_model_select_buttons[selected_fm_id].disabled = False
        else:
            selected_forward_models.remove(selected_fm_id)
            selected_forward_model_per_type[selected_fm_it] = None
            forward_model_select_buttons[selected_fm_id].disabled = True
        _validate_forward_models_of_type(selected_fm_it)
        _validate_variables_of_forward_models_of_type(selected_fm_it)
        _validate_selection()
        _update_preprocessing_states()
        _setup_user_priors()

    def _clear_variable_selection(b):
        for variable_id in variable_boxes_dict:
            if variable_id in selected_variables:
                selected_variables.remove(variable_id)
                variable_boxes_dict[variable_id].selected = False
                _validate_variable(variable_id)
                _update_forward_models_after_variable_change(variable_id)
        _validate_selection()

    def _clear_forward_model_selection(b):
        affected_input_types = []
        for forward_model_id in forward_model_boxes_dict:
            if forward_model_id in selected_forward_models:
                selected_forward_models.remove(forward_model_id)
                forward_model_type = _fm_input_type(forward_model_id)
                selected_forward_model_per_type[forward_model_type] = None
                if forward_model_type not in affected_input_types:
                    affected_input_types.append(forward_model_type)
                forward_model_boxes_dict[forward_model_id].selected = False
        for input_type in affected_input_types:
            _validate_forward_models_of_type(input_type)
            _validate_variables_of_forward_models_of_type(input_type)
        _update_preprocessing_states()
        _validate_selection()
        _setup_user_priors()

    def _select_all_variables_for_forward_model(forward_model_id: str):
        fm_variables = _fm_variables(forward_model_id)
        for variable_id in fm_variables:
            if variable_id not in selected_variables:
                selected_variables.append(variable_id)
                variable_boxes_dict[variable_id].selected = True
                _update_forward_models_after_variable_change(variable_id)
        _validate_selection()

    # noinspection PyTypeChecker
    variables_box = _wrap_variable_checkboxes_in_widget(
        variable_boxes_dict.values(), 4, _handle_variable_selection)
    clear_variable_selection_button = widgets.Button(
        description="Clear Variable Selection",
        layout=widgets.Layout(left='60%', width='35%'))
    clear_variable_selection_button.on_click(_clear_variable_selection)
    forward_model_variables = {}
    for fm_id in processing_parameters.forward_models.ids:
        forward_model_variables[
            fm_id] = processing_parameters.forward_models.get(fm_id).variables
        forward_model_select_buttons[fm_id] = _get_select_button(
            _select_all_variables_for_forward_model, fm_id)

    # noinspection PyTypeChecker
    forward_models_box = _wrap_forward_model_checkboxes_in_widget(
        forward_model_boxes_dict.values(), forward_model_select_buttons,
        _handle_forward_model_selection, forward_model_variables)
    clear_model_selection_button = widgets.Button(
        description="Clear Forward Model Selection",
        layout=widgets.Layout(left='60%', width='35%'))
    clear_model_selection_button.on_click(_clear_forward_model_selection)

    user_priors_box = widgets.Box(children=[],
                                  layout=widgets.Layout(overflow='hidden',
                                                        display='flex'))
    user_priors_component = widgets.VBox(children=[
        widgets.HTML(
            value=html_element('h2', value='User Priors')), user_priors_box
    ])
    user_priors_dict = {}

    def _handle_user_prior_change(user_prior_dict):
        for user_prior in user_prior_dict:
            user_priors_dict[user_prior] = user_prior_dict[user_prior]

    @debug_view.capture(clear_output=True)
    def _setup_user_priors():
        possible_user_priors = []
        s2_output_variables = []
        if selected_forward_model_per_type['Sentinel-2'] is not None:
            selected_forward_model = \
                processing_parameters.forward_models.get(selected_forward_model_per_type['Sentinel-2'])
            for prior in selected_forward_model.requiredPriors:
                if prior not in possible_user_priors:
                    possible_user_priors.append(prior)
            s2_output_variables = selected_forward_model.variables
        if selected_forward_model_per_type['Sentinel-1'] is not None:
            selected_forward_model = \
                processing_parameters.forward_models.get(selected_forward_model_per_type['Sentinel-1'])
            for prior in selected_forward_model.requiredPriors:
                if prior not in possible_user_priors and prior not in s2_output_variables:
                    possible_user_priors.append(prior)
        user_prior_components = []
        for possible_user_prior_id in possible_user_priors:
            prior = processing_parameters.variables.get(possible_user_prior_id)
            if not prior.may_be_user_prior:
                continue
            mu = None
            unc = None
            if possible_user_prior_id in user_priors_dict:
                if 'mu' in user_priors_dict[possible_user_prior_id]:
                    mu = user_priors_dict[possible_user_prior_id]['mu']
                if 'unc' in user_priors_dict[possible_user_prior_id]:
                    unc = user_priors_dict[possible_user_prior_id]['unc']
            user_prior_components.append(
                user_prior_component(prior.id, prior.unit,
                                     _handle_user_prior_change, mu, unc))
        user_priors_box.children = [
            _wrap_user_priors_in_widget(user_prior_components)
        ]

    @debug_view.capture(clear_output=True)
    def _must_preprocess(it: str) -> bool:
        must_preprocess = it in selected_forward_model_per_type and selected_forward_model_per_type[
            it] is not None
        if not must_preprocess:
            for post_processor_name in post_processor_checkbox_dict:
                if post_processor_checkbox_dict[post_processor_name].selected:
                    post_processor = processing_parameters.post_processors.get(
                        post_processor_name)
                    if it in post_processor.input_types:
                        must_preprocess = True
                        break
        return must_preprocess

    def _update_preprocessing_states():
        preprocess_s1_temporal_filter.disabled = not _must_preprocess(
            'Sentinel-1')
        preprocess_s2_only_roi_checkbox.enabled = _must_preprocess(
            'Sentinel-2')

    preprocess_s1_temporal_filter = widgets.BoundedIntText(value=5,
                                                           min=2,
                                                           max=15,
                                                           step=1,
                                                           disabled=True)
    preprocess_s2_only_roi_checkbox = LabeledCheckbox(
        selected=False,
        label_text='Only preprocess Region of Interest',
        tooltip='Only preprocess Region of Interest',
        enabled=False,
        layout=widgets.Layout(display='flex', width='30%'))

    global _NUM_REQUESTS
    _NUM_REQUESTS += 1
    request_name = widgets.Text(name)
    python_var_name = widgets.Text(identifier)

    start_date = widgets.DatePicker(
        value=datetime.datetime(year=2018, month=5, day=10))
    end_date = widgets.DatePicker(
        value=datetime.datetime(year=2018, month=5, day=15))

    time_steps = Spinner(value=5, min=1)

    time_steps_unit = widgets.Dropdown(options=['days', 'weeks'],
                                       value='days',
                                       disabled=False)

    map_background_layer = basemap_to_tiles(basemaps.OpenStreetMap.Mapnik)
    geometry_layer = GeoJSON()
    leaflet_map = Map(layers=(map_background_layer, geometry_layer),
                      center=[52., 10.],
                      zoom=4)
    draw_control = DrawControl()
    draw_control.polyline = {}
    draw_control.polygon = {}
    draw_control.circlemarker = {}
    draw_control.rectangle = {'shapeOptions': {}}
    draw_control.edit = False
    draw_control.remove = False

    @debug_view.capture(clear_output=True)
    def _handle_draw(self, action, geo_json):
        self.clear()
        leaflet_map.remove_layer(leaflet_map.layers[1])
        geometry_layer = GeoJSON(data=geo_json['geometry'])
        leaflet_map.add_layer(geometry_layer)

        roi_shape = shape(leaflet_map.layers[1].data)
        roi_area.value = roi_shape.wkt
        roi_validation.value = html_element(
            'h3',
            att=dict(style=f'color:green'),
            value='Region of Interest defined.')

    draw_control.on_draw(_handle_draw)
    leaflet_map.add_control(draw_control)

    roi_area = widgets.Textarea(
        layout=widgets.Layout(flex='1 10 90%', align_items='stretch'))
    roi_map_button = widgets.Button(description="Map region",
                                    layout=widgets.Layout(flex='0 1 10%'))

    def _update_roi_status_for_error(message: str):
        roi_data = leaflet_map.layers[1].data
        if not roi_data:
            roi_validation.value = html_element('h3',
                                                att=dict(style=f'color:red'),
                                                value=message)
            return
        roi_validation.value = html_element(
            'h3',
            att=dict(style=f'color:orange'),
            value=f'{message} Keep previously defined Region of Interest.')

    selected_indicators = []

    indicator_check_boxes = []
    for indicator_id in processing_parameters.indicators.ids:
        indicator = processing_parameters.indicators.get(indicator_id)
        indicator_check_box = LabeledCheckbox(selected=False,
                                              label_text=indicator_id,
                                              tooltip=indicator.description,
                                              enabled=True)
        indicator_check_boxes.append(indicator_check_box)

    @debug_view.capture(clear_output=True)
    def _handle_indicator_selection(change: dict):
        if change['name'] is not '_property_lock' or not 'selected' in change[
                'new']:
            return
        indicator_id = change['owner'].label_text
        if change['new']['selected']:
            selected_indicators.append(indicator_id)
        else:
            selected_indicators.remove(indicator_id)
        for inner_post_processor_name in post_processor_checkbox_dict:
            inner_post_processor = processing_parameters.post_processors.get(
                inner_post_processor_name)
            post_processor_selected = False
            for post_processor_indicator in inner_post_processor.indicators:
                if post_processor_indicator in selected_indicators:
                    post_processor_selected = True
                    break
            if post_processor_checkbox_dict[
                    inner_post_processor_name].selected != post_processor_selected:
                post_processor_checkbox_dict[
                    inner_post_processor_name].selected = post_processor_selected
        _update_preprocessing_states()

    indicators_box = _wrap_variable_checkboxes_in_widget(
        indicator_check_boxes, 3, _handle_indicator_selection)

    post_processor_checkbox_dict = {}
    post_processor_checkboxes = []
    for post_processor_name in processing_parameters.post_processors.names:
        post_processor = processing_parameters.post_processors.get(
            post_processor_name)
        post_processor_checkbox = LabeledCheckbox(
            selected=False,
            label_text=post_processor_name,
            tooltip=post_processor.description,
            enabled=False)
        post_processor_checkboxes.append(post_processor_checkbox)
        post_processor_checkbox_dict[
            post_processor_name] = post_processor_checkbox

    post_processors_box = _wrap_variable_checkboxes_in_widget(
        post_processor_checkboxes, 2, None)

    @debug_view.capture(clear_output=True)
    def _handle_roi_map_button_clicked(*args, **kwargs):
        try:
            geom = loads(roi_area.value)
            if type(geom) is not Polygon:
                _update_roi_status_for_error(
                    'User-provided Region of Interest is not of type Polygon.')
                return
            geojson_feature = geojson.Feature(geometry=geom, properties={})
            draw_control.clear()
            leaflet_map.remove_layer(leaflet_map.layers[1])
            geometry_layer = GeoJSON(data=geojson_feature['geometry'])
            leaflet_map.add_layer(geometry_layer)
            center_lon, center_lat = geom.centroid.coords.xy
            leaflet_map.center = [center_lat[0], center_lon[0]]

            @debug_view.capture(clear_output=False)
            def _adjust_zoom_level(event):
                if event['name'] == 'bounds' and leaflet_map.zoom < 18:
                    southwest, northeast = leaflet_map.bounds
                    map_bounds = Polygon([(southwest[1], southwest[0]),
                                          (southwest[1], northeast[0]),
                                          (northeast[1], northeast[0]),
                                          (northeast[1], southwest[0]),
                                          (southwest[1], southwest[0])])
                    if map_bounds.covers(geom):
                        leaflet_map.zoom = leaflet_map.zoom + 1
                    elif leaflet_map.zoom > 1:
                        leaflet_map.zoom = leaflet_map.zoom - 1
                        leaflet_map.unobserve(_adjust_zoom_level)

            leaflet_map.zoom = 1
            leaflet_map.observe(_adjust_zoom_level)
            if geom.is_valid:
                roi_validation.value = html_element(
                    'h3',
                    att=dict(style=f'color:green'),
                    value='Region of Interest defined.')
            else:
                roi_validation.value = html_element(
                    'h3',
                    att=dict(style=f'color:orange'),
                    value='User-provided Region of Interest is invalid.')
        except WKTReadingError:
            _update_roi_status_for_error(
                'User-provided Region of Interest cannot be read.')

    roi_map_button.on_click(_handle_roi_map_button_clicked)
    spatial_resolution = Spinner(value=100, min=1, step=1)
    roi_validation = widgets.HTML(
        value=html_element('h3',
                           att=dict(style='color:red'),
                           value='No region of interest defined'))

    info = InfoComponent()

    def new_input_request():
        request_status = _request_status()
        if request_status != 'Selection is valid':
            info.output_error(request_status)
            return
        input_types = []
        for input_type in selected_forward_model_per_type:
            if selected_forward_model_per_type[input_type] is not None:
                input_types.append(input_type)
        roi_data = leaflet_map.layers[1].data
        if not roi_data:
            info.output_error('Error: No Region of Interest specified')
            return
        roi = shape(roi_data)
        if not roi.is_valid:
            info.output_error('Error: Region of Interest is invalid')
            return
        request_models = []
        required_priors = []
        for model_id in selected_forward_models:
            request_model = processing_parameters.forward_models.get(model_id)
            request_variables = []
            for variable_id in request_model.variables:
                if variable_id in selected_variables:
                    request_variables.append(variable_id)
            request_model_dict = dict(
                name=model_id,
                type=request_model.type,
                modelDataType=request_model.input_type,
                requiredPriors=request_model.requiredPriors,
                outputParameters=request_variables)
            for required_model_prior in request_model.requiredPriors:
                if not required_model_prior in required_priors:
                    required_priors.append(required_model_prior)
            request_models.append(request_model_dict)
        user_priors_for_request_list = []
        for user_prior in user_priors_dict:
            if user_prior in required_priors:
                user_priors_for_request_dict = {'name': user_prior}
                if 'mu' in user_priors_dict:
                    user_priors_for_request_dict['mu'] = user_priors_dict['mu']
                if 'unc' in user_priors_dict:
                    user_priors_for_request_dict['unc'] = user_priors_dict[
                        'unc']
                user_priors_for_request_list.append(
                    user_priors_for_request_dict)
        temporalFilter = None
        if not preprocess_s1_temporal_filter.disabled:
            temporalFilter = preprocess_s1_temporal_filter.value
        computeOnlyRoi = None
        if preprocess_s2_only_roi_checkbox.enabled:
            computeOnlyRoi = preprocess_s2_only_roi_checkbox.selected
        postProcessors = []
        if len(selected_indicators) > 0:
            postProcessors = []
            for post_processor_name in processing_parameters.post_processors.names:
                post_processor = processing_parameters.post_processors.get(
                    post_processor_name)
                selected_pp_indicators = []
                for indicator in post_processor.indicators:
                    if indicator in selected_indicators:
                        selected_pp_indicators.append(indicator)
                if len(selected_pp_indicators) > 0:
                    post_processor_dict = dict(
                        name=post_processor_name,
                        type=post_processor.type,
                        inputTypes=post_processor.input_types,
                        indicatorNames=selected_pp_indicators,
                        variableNames=selected_variables)
                    postProcessors.append(post_processor_dict)

        return InputRequest(
            dict(name=request_name.value,
                 timeRange=[
                     datetime.datetime.strftime(start_date.value, "%Y-%m-%d"),
                     datetime.datetime.strftime(end_date.value, "%Y-%m-%d")
                 ],
                 timeStep=time_steps.value,
                 timeStepUnit=time_steps_unit.value,
                 roi=f"{roi.wkt}",
                 spatialResolution=spatial_resolution.value,
                 inputTypes=input_types,
                 forwardModels=request_models,
                 userPriors=user_priors_for_request_list,
                 s1TemporalFilter=temporalFilter,
                 s2ComputeRoi=computeOnlyRoi,
                 postProcessors=postProcessors))

    # noinspection PyUnusedLocal
    @debug_view.capture(clear_output=True)
    def handle_new_button_clicked(*args, **kwargs):
        req_var_name = python_var_name.value or 'req'
        if req_var_name and not req_var_name.isidentifier():
            info.output_error(
                f'Error: invalid Python identifier: {req_var_name}')
            return

        inputs_request = new_input_request()
        if inputs_request is None:
            return
        info.output_message('Fetching results...')

        processing_request = fetch_inputs_func(inputs_request,
                                               info.message_func)

        if processing_request is None:
            return
        input_identifiers = processing_request.inputs
        data_rows = []
        for input_type, input_ids in input_identifiers.as_dict().items():
            data_rows.append([input_type, len(input_ids)])

        result_html = html_table(
            data_rows, header_row=['Input Type', 'Number of inputs found'])

        # insert shall variable_id whose value is processing_request
        # users can later call the GUI with that object to edit it
        if req_var_name:
            shell = IPython.get_ipython()
            shell.push({req_var_name: processing_request}, interactive=True)
            var_name_html = html_element(
                'p',
                value=f'Note: a new processing request has been '
                f'stored in variable <code>{req_var_name}</code>.')
            result_html = html_element('div',
                                       value=result_html + var_name_html)
        info.output_html(result_html)

    # noinspection PyUnusedLocal
    @debug_view.capture(clear_output=True)
    def handle_submit_button_clicked(*args, **kwargs):
        req_var_name = python_var_name.value or 'job'
        if req_var_name and not req_var_name.isidentifier():
            info.output_error(
                f'Error: invalid Python identifier: {req_var_name}')
            return

        inputs_request = new_input_request()

        info.output_message('Submitting processing request...')

        job = submit_processing_request(inputs_request,
                                        message_func=info.message_func,
                                        mock=mock)
        if job is not None:
            shell = IPython.get_ipython()
            shell.push({req_var_name: job}, interactive=True)
            result_html = html_element(
                'p',
                value=f'Note: a new job is currently being executed and is '
                f'stored in variable_id <code>{req_var_name}</code>.')
            info.output_html(result_html)

    # TODO: make GUI form look nice
    new_button = widgets.Button(description="New Request", icon="search")
    new_button.on_click(handle_new_button_clicked)
    submit_button = widgets.Button(description="Submit Request", icon="upload")
    submit_button.on_click(handle_submit_button_clicked)

    form_items = [
        widgets.Box(
            [widgets.HTML(value=html_element('h2', value='Output Variables'))],
            layout=form_item_layout),
        widgets.Box([variables_box], layout=var_checks_layout),
        widgets.Box([clear_variable_selection_button],
                    layout=form_item_layout),
        widgets.Box(
            [widgets.HTML(value=html_element('h2', value='Forward Models'))],
            layout=form_item_layout),
        widgets.Box([forward_models_box], layout=var_checks_layout),
        widgets.Box([clear_model_selection_button], layout=form_item_layout),
        widgets.Box([request_validation], layout=form_item_layout),
        widgets.Box([
            widgets.HTML(
                value=html_element('h2', value='Sentinel-1 Pre-Processing'))
        ],
                    layout=form_item_layout),
        widgets.Box([
            widgets.Label(value='Temporal Filter'),
            preprocess_s1_temporal_filter
        ],
                    layout=form_item_layout),
        widgets.Box([
            widgets.HTML(
                value=html_element('h2', value='Sentinel-2 Pre-Processing'))
        ],
                    layout=form_item_layout), preprocess_s2_only_roi_checkbox,
        widgets.Box([user_priors_component], layout=form_item_layout),
        widgets.Box([
            widgets.HTML(
                value=html_element('h2', value='Time Period of Interest'))
        ],
                    layout=form_item_layout),
        widgets.Box([widgets.Label(value='Start date'), start_date],
                    layout=form_item_layout),
        widgets.Box([widgets.Label(value='End date'), end_date],
                    layout=form_item_layout),
        widgets.Box([
            widgets.Label(value='Time steps'),
            widgets.Box([time_steps, time_steps_unit])
        ],
                    layout=form_item_layout),
        widgets.Box([
            widgets.HTML(value=html_element('h2', value='Region of Interest'))
        ],
                    layout=form_item_layout),
        widgets.Box([roi_area, roi_map_button], layout=form_item_layout),
        widgets.Box([leaflet_map], layout=form_item_layout),
        widgets.Box(
            [widgets.Label(value='Resolution (m)'), spatial_resolution],
            layout=form_item_layout),
        widgets.Box([roi_validation], layout=form_item_layout),
        widgets.Box(
            [widgets.HTML(value=html_element('h2', value='Post-Processing'))],
            layout=form_item_layout),
        widgets.Label(value='Indicators', layout=form_item_layout),
        widgets.Box([indicators_box], layout=var_checks_layout),
        widgets.Label(value='Post Processors', layout=form_item_layout),
        widgets.Box([post_processors_box], layout=var_checks_layout),
        widgets.Box([widgets.Label(value='Request/job name'), request_name],
                    layout=form_item_layout),
        widgets.Box(
            [widgets.Label(value='Python identifier'), python_var_name],
            layout=form_item_layout),
        widgets.Box([
            widgets.Label(value=''),
            widgets.Box([new_button, submit_button])
        ],
                    layout=form_item_layout),
        widgets.Box([info.as_widget()], layout=form_item_layout)
    ]
    form = widgets.Box(form_items,
                       layout=widgets.Layout(display='flex',
                                             flex_flow='column',
                                             border='solid 1px lightgray',
                                             align_items='stretch',
                                             width='100%'))

    return form
Example #9
0
        'fillOpacity': 0.1
    }
}
control.rectangle['shapeOptions'].update({
    'color': '#ffffff',
    'weight': 4,
    'opacity': 0.5,
    'stroke': True
})

# remove default polyline and circlemarker
control.polyline = {}
control.circlemarker = {}

# specify clear function
control.on_draw(rid)

# add draw control
chart.add_control(control)

# force a rectange onto the map
force(south, north, west, east)

# display chart
chart

# ### Setting the Date Range

# Set the date range below.  Leaving the beginning date blank will default to Earth Day 1995.  Leaving the ending date blank will default to today's date.

# In[103]:
Example #10
0
def clickablemap(center=[48.790153, 2.327395],
                 zoom=13,
                 layout=ipywidgets.Layout(width='100%', height='500px')):
    # look at: http://leaflet.github.io/Leaflet.draw/docs/examples/basic.html

    import json

    from ipyleaflet import (Map, Rectangle, Polygon, TileLayer, ImageOverlay,
                            DrawControl, GeoJSON)

    #%matplotlib inline
    #   %matplotlib notebook

    # google tileserver
    # https://stackoverflow.com/questions/9394190/leaflet-map-api-with-google-satellite-layer
    mosaicsTilesURL = 'https://mt1.google.com/vt/lyrs=s,h&x={x}&y={y}&z={z}'  # Hybrid: s,h; Satellite: s; Streets: m; Terrain: p;

    # Map Settings
    # Define colors
    colors = {'blue': "#009da5"}
    # Define initial map center lat/long
    #center = [48.790153, 2.327395]
    # Define initial map zoom level
    #zoom = 13
    # Create the map
    m = Map(center=center, zoom=zoom, scroll_wheel_zoom=True, layout=layout)

    # using custom basemap
    m.clear_layers()
    m.add_layer(TileLayer(url=mosaicsTilesURL))

    # Define the draw tool type options
    polygon = {'shapeOptions': {'color': colors['blue']}}
    rectangle = {'shapeOptions': {'color': colors['blue']}}

    ## Create the draw controls
    ## @see https://github.com/ellisonbg/ipyleaflet/blob/master/ipyleaflet/leaflet.py#L293
    #dc = DrawControl(
    #    polygon = polygon,
    #    rectangle = rectangle
    #)
    dc = DrawControl(
        polygon={'shapeOptions': {
            'color': '#0000FF'
        }},
        polyline={'shapeOptions': {
            'color': '#0000FF'
        }},
        circle={'shapeOptions': {
            'color': '#0000FF'
        }},
        rectangle={'shapeOptions': {
            'color': '#0000FF'
        }},
    )

    # Initialize an action counter variable
    m.actionCount = 0
    m.AOIs = []

    # Register the draw controls handler
    def handle_draw(self, action, geo_json):
        # Increment the action counter
        #global actionCount
        m.actionCount += 1
        # Remove the `style` property from the GeoJSON
        geo_json['properties'] = {}
        # Convert geo_json output to a string and prettify (indent & replace ' with ")
        geojsonStr = json.dumps(geo_json, indent=2).replace("'", '"')
        m.AOIs.append(json.loads(geojsonStr))

    # Attach the draw handler to the draw controls `on_draw` event
    dc.on_draw(handle_draw)
    m.add_control(dc)

    # add a custom function to create and add a Rectangle layer
    # (LESS USEFUL THAN add_geojson)
    def add_rect(*args, **kwargs):
        r = Rectangle(*args, **kwargs)
        return m.add_layer(r)

    m.add_rectangle = add_rect

    # add a custom function to create and add a Polygon layer
    def add_geojson(*args, **kwargs):
        # ugly workaround to call without data=aoi
        if 'data' not in kwargs:
            kwargs['data'] = args[0]
            args2 = [i for i in args[1:-1]]
        else:
            args2 = args

        r = GeoJSON(*args2, **kwargs)
        return m.add_layer(r)

    m.add_GeoJSON = add_geojson

    # Display
    return m
Example #11
0
class LeafletMap(HasTraits):
    """A xarray.DataArray extension for tiled map plotting, based on (ipy)leaflet.
    """

    map_ready = Bool(False)

    @observe('map_ready')
    def _map_ready_changed(self, change):
        self._start()


    def __init__(self, da):
        self._da = da
        self._da_selected = None

    def plot(self,
             m,
             x_dim='x',
             y_dim='y',
             fit_bounds=True,
             rgb_dim=None,
             transform0=None,
             transform1=passthrough,
             transform2=coarsen(),
             transform3=passthrough,
             colormap=None,
             persist=True,
             dynamic=False,
             tile_dir=None,
             tile_height=256,
             tile_width=256,
             resampling=Resampling.nearest,
             debug_output=None):
        """Display an array as an interactive map.

        Assumes that the pixels are given on a regular grid
        (fixed spacing in x and y).

        Parameters
        ----------
        m : ipyleaflet.Map
            The map on while to show the layer.
        y_dim : str, optional
            Name of the y dimension/coordinate
            (default: 'y').
        x_dim : str, optional
            Name of the x dimension/coordinate
            (default: 'x').
        rgb_dim : str, optional
            Name of the RGB dimension/coordinate
            (default: None).
        transform0 : function, optional
            Transformation over the whole DataArray.
        transform1 : function, optional
            Transformation over the visible DataArray.
        transform2 : function, optional
            Transformation over a tile before reprojection.
        transform3 : function, optional
            Transformation over a tile before saving to PNG.
        colormap : function, optional
            The colormap function to use for the tile PNG
            (default: matplotlib.pyplot.cm.inferno).
        persist : bool, optional
            Whether to keep the tile files (True) or not (False).
        dynamic : bool, optional
            Whether the map is dynamic (True) or not (False). If True then the
            tiles will refreshed each time the map is dragged or zoomed.
        tile_dir : str, optional
            The path to the tile directory (must be absolute).
        tile_height : int, optional
            The heiht of a tile in pixels (default: 256).
        tile_width : int, optional
            The width of a tile in pixels (default: 256).
        resampling : int, optional
            The resampling method to use, see rasterio.warp.reproject
            (default: Resampling.nearest).

        Returns
        -------
        l : ipyleaflet.LocalTileLayer
            A handler to the layer that is added to the map.
        """

        if debug_output is None:
            self.debug_output = Output()
        else:
            self.debug_output = debug_output

        with self.debug_output:
            if 'proj4def' in m.crs:
                # it's a custom projection
                if dynamic:
                    raise RuntimeError('Dynamic maps are only supported for Web Mercator (EPSG:3857), not {}'.format(m.crs))
                self.dst_crs = m.crs['proj4def']
                self.web_mercator = False
                self.custom_proj = True
            elif m.crs['name'].startswith('EPSG'):
                epsg = m.crs['name'][4:]
                if dynamic and epsg != '3857':
                    raise RuntimeError('Dynamic maps are only supported for Web Mercator (EPSG:3857), not {}'.format(m.crs))
                self.dst_crs = 'EPSG:' + epsg
                self.web_mercator = epsg == '3857'
                self.custom_proj = False
            else:
                raise RuntimeError('Unsupported map projection: {}'.format(m.crs))

            self.nodata = self._da.rio.nodata
            var_dims = self._da.dims
            expected_dims = [y_dim, x_dim]
            if rgb_dim is not None:
                expected_dims.append(rgb_dim)
            if set(var_dims) != set(expected_dims):
                raise ValueError(
                    "Invalid dimensions in DataArray: "
                    "should include only {}, found {}."
                    .format(tuple(expected_dims), var_dims)
                )

            if rgb_dim is not None and colormap is not None:
                raise ValueError(
                    "Cannot have a RGB dimension and a "
                    "colormap at the same time."
                )
            elif rgb_dim is None:
                if colormap is None:
                    colormap = plt.cm.inferno
                if transform0 is None:
                    transform0 = normalize
            else:
                # there is a RGB dimension
                if transform0 is None:
                    transform0 = passthrough

            self.resampling = resampling
            self.tile_dir = tile_dir
            self.persist = persist
            self.attrs = self._da.attrs
            self.m = m
            self.dynamic = dynamic
            self.tile_width = tile_width
            self.tile_height = tile_height
            self.transform0 = transform0
            self.transform1 = transform1
            self.transform2 = transform2
            self.transform3 = transform3
            self.colormap = colormap
            if self.dynamic:
                self.persist = False
                self.tile_dir = None

            self._da = self._da.rename({y_dim: 'y', x_dim: 'x'})
            if rgb_dim is None:
                self.is_rgb = False
            else:
                self.is_rgb = True
                self._da = self._da.rename({rgb_dim: 'rgb'})

            # ensure latitudes are descending
            if np.any(np.diff(self._da.y.values) >= 0):
                self._da = self._da.sel(y=slice(None, None, -1))

            # infer grid specifications (assume a rectangular grid)
            y = self._da.y.values
            x = self._da.x.values

            self.x_left = float(x.min())
            self.x_right = float(x.max())
            self.y_lower = float(y.min())
            self.y_upper = float(y.max())

            self.dx = float((self.x_right - self.x_left) / (x.size - 1))
            self.dy = float((self.y_upper - self.y_lower) / (y.size - 1))

            if fit_bounds:
                asyncio.ensure_future(self.async_fit_bounds())
            else:
                asyncio.ensure_future(self.async_wait_for_bounds())

            self.l = LocalTileLayer()
            if self._da.name is not None:
                self.l.name = self._da.name

            self._da_notransform = self._da

            self.spinner = Spinner()
            self.spinner.radius = 5
            self.spinner.length = 3
            self.spinner.width = 5
            self.spinner.lines = 8
            self.spinner.color = '#000000'
            self.spinner.layout.height = '30px'
            self.spinner.layout.width = '30px'
            self.spinner_control = WidgetControl(widget=self.spinner, position='bottomright')

            return self.l


    def select(self, draw_control=None):
        with self.debug_output:
            if draw_control is None:
                self._draw_control = DrawControl()
                self._draw_control.polygon = {}
                self._draw_control.polyline = {}
                self._draw_control.circlemarker = {}
                self._draw_control.rectangle = {
                    'shapeOptions': {
                        'fillOpacity': 0.5
                    }
                }
            else:
                self._draw_control = draw_control
            self._draw_control.on_draw(self._get_selection)
            self.m.add_control(self._draw_control)


    def unselect(self):
        with self.debug_output:
            self.m.remove_control(self._draw_control)


    def get_selection(self):
        return self._da_selected


    def _get_selection(self, *args, **kwargs):
        with self.debug_output:
            if self._draw_control.last_draw['geometry'] is not None:
                lonlat = self._draw_control.last_draw['geometry']['coordinates'][0]
                lats = [ll[1] for ll in lonlat]
                lons = [ll[0] for ll in lonlat]
                lt0, lt1 = min(lats), max(lats)
                ln0, ln1 = min(lons), max(lons)
                self._da_selected = self._da_notransform.sel(y=slice(lt1, lt0), x=slice(ln0, ln1))


    def _start(self):
        with self.debug_output:
            self.m.add_control(self.spinner_control)
            self._da, self.transform0_args = get_transform(self.transform0(self._da, debug_output=self.debug_output))

            self.url = self.m.window_url
            if self.url.endswith('/lab'):
                # we are in JupyterLab
                self.base_url = self.url[:-4]
            else:
                if '/notebooks/' in self.url:
                    # we are in classical Notebook
                    i = self.url.rfind('/notebooks/')
                    self.base_url = self.url[:i]
                elif '/voila/' in self.url:
                    # we are in Voila
                    i = self.url.rfind('/voila/')
                    self.base_url = self.url[:i]

            if self.tile_dir is None:
                self.tile_temp_dir = TemporaryDirectory(prefix='xarray_leaflet_')
                self.tile_path = self.tile_temp_dir.name
            else:
                self.tile_path = self.tile_dir
            self.url = self.base_url + '/xarray_leaflet' + self.tile_path + '/{z}/{x}/{y}.png'
            self.l.path = self.url

            self.m.remove_control(self.spinner_control)
            self._get_tiles()
            self.m.observe(self._get_tiles, names='pixel_bounds')
            if not self.dynamic:
                self.m.add_layer(self.l)


    def _get_tiles(self, change=None):
        with self.debug_output:
            self.m.add_control(self.spinner_control)
            if self.dynamic:
                self.tile_temp_dir.cleanup()
                self.tile_temp_dir = TemporaryDirectory(prefix='xarray_leaflet_')
                new_tile_path = self.tile_temp_dir.name
                new_url = self.base_url + '/xarray_leaflet' + new_tile_path + '/{z}/{x}/{y}.png'
                if self.l in self.m.layers:
                    self.m.remove_layer(self.l)

            (left, top), (right, bottom) = self.m.pixel_bounds
            (south, west), (north, east) = self.m.bounds
            z = int(self.m.zoom)  # TODO: support non-integer zoom levels?
            if self.custom_proj:
                resolution = self.m.crs['resolutions'][z]

            if self.web_mercator:
                tiles = list(mercantile.tiles(west, south, east, north, z))
            else:
                x0, x1 = int(left) // self.tile_width, int(right) // self.tile_width + 1
                y0, y1 = int(top) // self.tile_height, int(bottom) // self.tile_height + 1
                tiles = [mercantile.Tile(x, y, z) for x in range(x0, x1) for y in range(y0, y1)]

            if self.dynamic:
                # dynamic maps are redrawn at each interaction with the map
                # so we can take exactly the corresponding slice in the original data
                da_visible = self._da.sel(y=slice(north, south), x=slice(west, east))
            elif self.web_mercator:
                # for static web mercator maps we can't redraw a tile once it has been (partly) displayed,
                # so we must slice the original data on tile boundaries
                bbox = get_bbox_tiles(tiles)
                # take one more source data point to avoid glitches
                da_visible = self._da.sel(y=slice(bbox.north + self.dy, bbox.south - self.dy), x=slice(bbox.west - self.dx, bbox.east + self.dx))
            else:
                # it's a custom projection or not web mercator, the visible tiles don't translate easily
                # to a slice of the original data, so we keep everything
                # TODO: slice the data for EPSG3395, EPSG4326, Earth, Base and Simple
                da_visible = self._da

            # check if we have some data to show
            if 0 not in da_visible.shape:
                da_visible, transform1_args = get_transform(self.transform1(da_visible, *self.transform0_args, debug_output=self.debug_output))

            if self.dynamic:
                self.tile_path = new_tile_path
                self.url = new_url

            for tile in tiles:
                x, y, z = tile
                path = f'{self.tile_path}/{z}/{x}/{y}.png'
                # if static map, check if we already have the tile
                # if dynamic map, new tiles are always created
                if self.dynamic or not os.path.exists(path):
                    if self.web_mercator:
                        bbox = mercantile.bounds(tile)
                        xy_bbox = mercantile.xy_bounds(tile)
                        x_pix = (xy_bbox.right - xy_bbox.left) / self.tile_width
                        y_pix = (xy_bbox.top - xy_bbox.bottom) / self.tile_height
                        # take one more source data point to avoid glitches
                        da_tile = da_visible.sel(y=slice(bbox.north + self.dy, bbox.south - self.dy), x=slice(bbox.west - self.dx, bbox.east + self.dx))
                    else:
                        da_tile = da_visible
                    # check if we have data for this tile
                    if 0 in da_tile.shape:
                        write_image(path, None, self.persist)
                    else:
                        da_tile.attrs = self.attrs
                        da_tile, transform2_args = get_transform(self.transform2(da_tile, tile_width=self.tile_width, tile_height=self.tile_height, debug_output=self.debug_output), *transform1_args)
                        # reproject each RGB component if needed
                        # TODO: must be doable with xarray.apply_ufunc
                        if self.is_rgb:
                            das = [da_tile.isel(rgb=i) for i in range(3)]
                        else:
                            das = [da_tile]
                        for i in range(len(das)):
                            das[i] = das[i].rio.write_nodata(self.nodata)
                            if self.custom_proj:
                                das[i] = reproject_custom(das[i], self.dst_crs, x, y, z, resolution, resolution, self.tile_width, self.tile_height, self.resampling)
                            else:
                                das[i] = reproject_not_custom(das[i], self.dst_crs, xy_bbox.left, xy_bbox.top, x_pix, y_pix, self.tile_width, self.tile_height, self.resampling)
                            das[i], transform3_args = get_transform(self.transform3(das[i], *transform2_args, debug_output=self.debug_output))
                        if self.is_rgb:
                            alpha = np.where(das[0]==self._da.rio.nodata, 0, 255)
                            das.append(alpha)
                            da_tile = np.stack(das, axis=2)
                            write_image(path, da_tile, self.persist)
                        else:
                            da_tile = self.colormap(das[0])
                            write_image(path, da_tile*255, self.persist)

            if self.dynamic:
                self.l.path = self.url
                self.m.add_layer(self.l)
                self.l.redraw()

            self.m.remove_control(self.spinner_control)


    async def async_wait_for_bounds(self):
        with self.debug_output:
            if len(self.m.bounds) == 0:
                await wait_for_change(self.m, 'bounds')
            self.map_ready = True


    async def async_fit_bounds(self):
        with self.debug_output:
            center = self.y_lower + (self.y_upper - self.y_lower) / 2, self.x_left + (self.x_right - self.x_left) / 2
            if center != self.m.center:
                self.m.center = center
                await wait_for_change(self.m, 'bounds')
            zoomed_out = False
            # zoom out
            while True:
                if self.m.zoom <= 1:
                    break
                print('Zooming out')
                (south, west), (north, east) = self.m.bounds
                if south > self.y_lower or north < self.y_upper or west > self.x_left or east < self.x_right:
                    self.m.zoom = self.m.zoom - 1
                    await wait_for_change(self.m, 'bounds')
                    zoomed_out = True
                else:
                    break
            if not zoomed_out:
                # zoom in
                while True:
                    print('Zooming in')
                    (south, west), (north, east) = self.m.bounds
                    if south < self.y_lower and north > self.y_upper and west < self.x_left and east > self.x_right:
                        self.m.zoom = self.m.zoom + 1
                        await wait_for_change(self.m, 'bounds')
                    else:
                        self.m.zoom = self.m.zoom - 1
                        await wait_for_change(self.m, 'bounds')
                        break
            self.map_ready = True
Example #12
0
class Grass2Leaflet(object):
    def __init__(self, grassimg):
        self.grassimg = grassimg
        self.draw_control = None
        self.zoom = 15
        self.center = self.centermap()
        self.m = Map(default_tiles=TileLayer(opacity=1.0),
                     center=self.center,
                     zoom=self.zoom)

    def centermap(self):
        centerlat = []
        centerlon = []
        for i in self.grassimg:
            centerlat.append(self.grassimg[i]['C'][0])
            centerlon.append(self.grassimg[i]['C'][1])
        center = (sum(centerlat) / float(len(centerlat)),
                  sum(centerlon) / float(len(centerlon)))
        return center

    def imgoverlays(self):
        self.leafletimg = OrderedDict()
        for i in self.grassimg:
            layer = ImageOverlay(url=self.grassimg[i]['raster'],
                                 bounds=(self.grassimg[i]['LL'],
                                         self.grassimg[i]['UR']))
            self.leafletimg[i] = layer

    def render(self, draw_control=None, caption=None):
        self.out = widgets.Output()
        self.imgoverlays()
        self.dc = None
        options = ['None']
        self.m.add_layer(self.leafletimg[list(self.grassimg.keys())[-1]])
        if len(self.grassimg) >= 2:
            self.maplist = widgets.Dropdown(
                options=options + list(self.grassimg.keys()),
                value=list(self.grassimg.keys())[-1],
                description='Select Layer:',
            )
            self.maplist.observe(self.on_value_change, names='value')
            display(self.maplist)
        if draw_control:
            self.dc = DrawControl()
            self.dc.on_draw(handle_draw)
            self.m.add_control(self.dc)
        display(self.m)
        self.lastdraw = widgets.Button(
            description='Print last draw',
            disabled=False,
            button_style='',  # 'success', 'info', 'warning', 'danger' or ''
            tooltip='Print last draw',
            #icon='check'
        )
        self.lastdraw.on_click(self.on_button_clicked1)
        if not draw_control:
            self.lastdraw.disabled = True
        display(widgets.HBox([self.lastdraw, self.out]))
        if caption:
            display(
                HTML("<center><b>Figure %s:</b> %s<center>" %
                     (caption[0], caption[1])))
        return {'map': self.m, 'drawer': self.dc}

    def on_value_change(self, layername):
        self.m.clear_layers()
        self.m.add_layer(TileLayer(opacity=1.0))
        if self.maplist.value != 'None':
            self.m.add_layer(self.leafletimg[layername['new']])

    def on_button_clicked1(self, b):
        with self.out:
            clear_output()
            display(self.dc.last_draw)
            new_draw = GeoJSON(data=self.dc.last_draw)
            self.m.add_layer(new_draw)
            #display(dir(self.dc.layer))

    def handle_draw_output(self, json):
        display(json.dumps(geo_json))

    def main(self):
        self.imgoverlays()
        self.render()
Example #13
0
    def button_clicked(change):
        if change["new"] == "Apply":

            if len(color.value) != 7:
                color.value = "#3388ff"
            draw_control = DrawControl(
                marker={"shapeOptions": {"color": color.value}},
                rectangle={"shapeOptions": {"color": color.value}},
                polygon={"shapeOptions": {"color": color.value}},
                circlemarker={},
                polyline={},
                edit=False,
                remove=False,
            )

            controls = []
            old_draw_control = None
            for control in m.controls:
                if isinstance(control, DrawControl):
                    controls.append(draw_control)
                    old_draw_control = control

                else:
                    controls.append(control)

            m.controls = tuple(controls)
            old_draw_control.close()
            m.draw_control = draw_control

            train_props = {}

            if prop_text1.value != "" and value_text1.value != "":
                train_props[prop_text1.value] = int(value_text1.value)
            if prop_text2.value != "" and value_text2.value != "":
                train_props[prop_text2.value] = value_text2.value
            if color.value != "":
                train_props["color"] = color.value

            # Handles draw events
            def handle_draw(target, action, geo_json):
                from .geemap import ee_tile_layer

                try:
                    geom = geojson_to_ee(geo_json, False)
                    m.user_roi = geom

                    if len(train_props) > 0:
                        feature = ee.Feature(geom, train_props)
                    else:
                        feature = ee.Feature(geom)
                    m.draw_last_json = geo_json
                    m.draw_last_feature = feature
                    if action == "deleted" and len(m.draw_features) > 0:
                        m.draw_features.remove(feature)
                        m.draw_count -= 1
                    else:
                        m.draw_features.append(feature)
                        m.draw_count += 1
                    collection = ee.FeatureCollection(m.draw_features)
                    m.user_rois = collection
                    ee_draw_layer = ee_tile_layer(
                        collection, {"color": "blue"}, "Drawn Features", False, 0.5
                    )
                    draw_layer_index = m.find_layer_index("Drawn Features")

                    if draw_layer_index == -1:
                        m.add_layer(ee_draw_layer)
                        m.draw_layer = ee_draw_layer
                    else:
                        m.substitute_layer(m.draw_layer, ee_draw_layer)
                        m.draw_layer = ee_draw_layer

                except Exception as e:
                    m.draw_count = 0
                    m.draw_features = []
                    m.draw_last_feature = None
                    m.draw_layer = None
                    m.user_roi = None
                    m.roi_start = False
                    m.roi_end = False
                    print("There was an error creating Earth Engine Feature.")
                    raise Exception(e)

            draw_control.on_draw(handle_draw)

        elif change["new"] == "Clear":
            prop_text1.value = ""
            value_text1.value = ""
            prop_text2.value = ""
            value_text2.value = ""
            color.value = "#3388ff"
        elif change["new"] == "Close":
            m.toolbar_reset()
            if m.training_ctrl is not None and m.training_ctrl in m.controls:
                m.remove_control(m.training_ctrl)
            full_widget.close()
        buttons.value = None
def run_agriculture_app(ds):
    """
    Plots an interactive map of the agriculture case-study area and allows
    the user to draw polygons. This returns a plot of the average NDVI value
    in the polygon area.
    Last modified: May 2019
    Author: Caitlin Adams (FrontierSI)

    inputs
    ds - data set containing combined, masked data from Sentinel-2a and -2b.
    Must also have an attribute containing the NDVI value for each pixel
    """
    # Suppress warnings
    warnings.filterwarnings('ignore')

    # Update plotting functionality through rcParams
    mpl.rcParams.update({'figure.autolayout': True})

    # Define the bounding box that will be overlayed on the interactive map
    # The bounds are hard-coded to match those from the loaded data
    geom_obj = {
        "type": "Feature",
        "properties": {
            "style": {
                "stroke": True,
                "color": 'red',
                "weight": 4,
                "opacity": 0.8,
                "fill": True,
                "fillColor": False,
                "fillOpacity": 0,
                "showArea": True,
                "clickable": True
            }
        },
        "geometry": {
            "type": "Polygon",
            "coordinates": [
                [
                    [152.395805, -24.995971],
                    [152.395805, -24.974997],
                    [152.429994, -24.974997],
                    [152.429994, -24.995971],
                    [152.395805, -24.995971]
                ]
            ]
        }
    }

    # Create a map geometry from the geom_obj dictionary
    # center specifies where the background map view should focus on
    # zoom specifies how zoomed in the background map should be
    loadeddata_geometry = ogr.CreateGeometryFromJson(str(geom_obj['geometry']))
    loadeddata_center = [
        loadeddata_geometry.Centroid().GetY(),
        loadeddata_geometry.Centroid().GetX()
    ]
    loadeddata_zoom = 14

    # define the study area map
    studyarea_map = Map(
        center=loadeddata_center,
        zoom=loadeddata_zoom,
        basemap=basemaps.Esri.WorldImagery
    )

    # define the drawing controls
    studyarea_drawctrl = DrawControl(
        polygon={"shapeOptions": {"fillOpacity": 0}},
        marker={},
        circle={},
        circlemarker={},
        polyline={},
    )

    # add drawing controls and data bound geometry to the map
    studyarea_map.add_control(studyarea_drawctrl)
    studyarea_map.add_layer(GeoJSON(data=geom_obj))

    # Index to count drawn polygons
    polygon_number = 0

    # Define widgets to interact with
    instruction = widgets.Output(layout={'border': '1px solid black'})
    with instruction:
        print("Draw a polygon within the red box to view a plot of "
              "average NDVI over time in that area.")

    info = widgets.Output(layout={'border': '1px solid black'})
    with info:
        print("Plot status:")

    fig_display = widgets.Output(layout=widgets.Layout(
        width="50%",  # proportion of horizontal space taken by plot
    ))

    with fig_display:
        plt.ioff()
        fig, ax = plt.subplots(figsize=(8, 6))
        ax.set_ylim([-1, 1])

    colour_list = plt.rcParams['axes.prop_cycle'].by_key()['color']

    # Function to execute each time something is drawn on the map
    def handle_draw(self, action, geo_json):
        nonlocal polygon_number

        # Execute behaviour based on what the user draws
        if geo_json['geometry']['type'] == 'Polygon':

            info.clear_output(wait=True)  # wait=True reduces flicker effect
            with info:
                print("Plot status: polygon sucessfully added to plot.")

            # Convert the drawn geometry to pixel coordinates
            geom_selectedarea = transform_from_wgs_poly(
                geo_json['geometry'],
                EPSGa=3577  # hard-coded to be same as case-study data
            )

            # Construct a mask to only select pixels within the drawn polygon
            mask = rasterio.features.geometry_mask(
                [geom_selectedarea for geoms in [geom_selectedarea]],
                out_shape=ds.geobox.shape,
                transform=ds.geobox.affine,
                all_touched=False,
                invert=True
            )

            masked_ds = ds.ndvi.where(mask)
            masked_ds_mean = masked_ds.mean(dim=['x', 'y'], skipna=True)
            colour = colour_list[polygon_number % len(colour_list)]

            # Add a layer to the map to make the most recently drawn polygon
            # the same colour as the line on the plot
            studyarea_map.add_layer(
                GeoJSON(
                    data=geo_json,
                    style={
                        'color': colour,
                        'opacity': 1,
                        'weight': 4.5,
                        'fillOpacity': 0.0
                    }
                )
            )

            # add new data to the plot
            xr.plot.plot(
                masked_ds_mean,
                marker='*',
                color=colour,
                ax=ax
            )

            # reset titles back to custom
            ax.set_title("Average NDVI from Sentinel-2")
            ax.set_xlabel("Date")
            ax.set_ylabel("NDVI")

            # refresh display
            fig_display.clear_output(wait=True)  # wait=True reduces flicker effect
            with fig_display:
                display(fig)

            # Iterate the polygon number before drawing another polygon
            polygon_number = polygon_number + 1

        else:
            info.clear_output(wait=True)
            with info:
                print("Plot status: this drawing tool is not currently "
                      "supported. Please use the polygon tool.")

    # call to say activate handle_draw function on draw
    studyarea_drawctrl.on_draw(handle_draw)

    with fig_display:
        # TODO: update with user friendly something
        display(widgets.HTML(""))

    # Construct UI:
    #  +-----------------------+
    #  | instruction           |
    #  +-----------+-----------+
    #  |  map      |  plot     |
    #  |           |           |
    #  +-----------+-----------+
    #  | info                  |
    #  +-----------------------+
    ui = widgets.VBox([instruction,
                       widgets.HBox([studyarea_map, fig_display]),
                       info])
    display(ui)
Example #15
0
class Dashboard:
    def __init__(self, m, notif, dem2d, dem3d):
        self.map = m
        self.notif = notif
        self.dem2d = dem2d
        self.dem3d = dem3d
        self.tile_dir = 'dem_tiles'
        self.spin = Spinner()
        self.spin.class_name = 'dem3d_spinner'

    def start(self):
        self.draw_control = DrawControl()
        self.draw_control.polygon = {}
        self.draw_control.polyline = {}
        self.draw_control.circlemarker = {}
        self.draw_control.rectangle = {'shapeOptions': {'fillOpacity': 0.5}}
        self.draw_control.on_draw(self.show_dem)
        self.map.add_control(self.draw_control)

    def show_dem(self, *args, **kwargs):
        self.dem2d.clear_output()
        self.dem3d.clear_output()
        with self.dem2d:
            display(self.spin)
        with self.dem3d:
            display(self.spin)
        lonlat = self.draw_control.last_draw['geometry']['coordinates'][0]
        lats = [ll[1] for ll in lonlat]
        lons = [ll[0] for ll in lonlat]
        lt0, lt1 = min(lats), max(lats)
        ln0, ln1 = min(lons), max(lons)
        os.makedirs(self.tile_dir, exist_ok=True)
        with open(self.tile_dir + '.json') as f:
            tiles = json.loads(f.read())
        lat = lat0 = floor(lt0 / 5) * 5
        lon = lon0 = floor(ln0 / 5) * 5
        lat1 = ceil(lt1 / 5) * 5
        lon1 = ceil(ln1 / 5) * 5
        ny = int(round((lat1 - lat0) / (5 / 6000)))
        nx = int(round((lon1 - lon0) / (5 / 6000)))
        zarr_path = os.path.join(self.tile_dir + '.zarr')
        if os.path.exists(zarr_path):
            shutil.rmtree(zarr_path)
        z = zarr.open(zarr_path,
                      mode='w',
                      shape=(ny, nx),
                      chunks=(6000, 6000),
                      dtype='float32')
        done = False
        tasks = []
        with concurrent.futures.ThreadPoolExecutor() as executor:
            while not done:
                tasks.append(
                    executor.submit(self.create_zarr_chunk, lat, lon, tiles,
                                    lat0, lat1, lon0, lon1, z))
                lon += 5
                if lon >= ln1:
                    lon = lon0
                    lat += 5
                    if lat >= lt1:
                        done = True
            for task in tasks:
                task.result()
        y = np.arange(lat0, lat1, 5 / 6000)
        x = np.arange(lon0, lon1, 5 / 6000)
        dem = xr.DataArray(z, coords=[y, x], dims=['y',
                                                   'x']).sel(y=slice(lt0, lt1),
                                                             x=slice(ln0, ln1))
        self.download_link(dem)
        self.show_dem2d(dem)
        self.show_dem3d(dem)

    def download_link(self, dem):
        lon, lat = np.meshgrid(np.radians(dem.x),
                               np.radians(dem.y),
                               sparse=True)
        r = 6371e3
        f = np.where(np.isnan(dem.values), 0, dem.values) + r

        nr, nc = len(dem.y), len(dem.x)

        self.vertices = np.empty((nr, nc, 3), dtype='float32')
        self.vertices[:, :, 0] = f * np.cos(lat) * np.cos(lon)
        self.vertices[:, :, 1] = f * np.cos(lat) * np.sin(lon)
        self.vertices[:, :, 2] = f * np.sin(lat)

        self.vertices = self.vertices.reshape(nr * nc, 3)

        # rotate so that z goes through the middle of the selection
        r1 = R.from_rotvec(np.radians(np.array([0, 0,
                                                -(np.mean(dem.x) + 90)])))
        r2 = R.from_rotvec(np.radians(np.array([np.mean(dem.y) - 90, 0, 0])))
        self.vertices = r2.apply(r1.apply(self.vertices))
        self.vertices[:, 2] -= r
        self.alt = self.vertices[:, 2].reshape(nr, nc)

        self.notif.clear_output()
        np.savez('dem.npz', dem=self.vertices)
        local_file = FileLink('dem.npz',
                              result_html_prefix="Click here to download: ")
        with self.notif:
            display(local_file)

    def show_dem2d(self, dem):
        fig = dem.plot.imshow(vmin=np.nanmin(dem), vmax=np.nanmax(dem))
        self.dem2d.clear_output()
        with self.dem2d:
            display(fig)
            plt.show()

    def show_dem3d(self, dem):
        nr, nc = len(dem.y), len(dem.x)
        triangle_indices = np.empty((nr - 1, nc - 1, 2, 3), dtype='uint32')
        r = np.arange(nr * nc, dtype='uint32').reshape(nr, nc)
        triangle_indices[:, :, 0, 0] = r[:-1, :-1]
        triangle_indices[:, :, 1, 0] = r[:-1, 1:]
        triangle_indices[:, :, 0, 1] = r[:-1, 1:]
        triangle_indices[:, :, 1, 1] = r[1:, 1:]
        triangle_indices[:, :, :, 2] = r[1:, :-1, None]
        triangle_indices.shape = (-1, 3)

        height_component = Component(name='value', array=self.alt)

        mesh = PolyMesh(vertices=self.vertices,
                        triangle_indices=triangle_indices,
                        data={'height': [height_component]})

        colored_mesh = IsoColor(mesh,
                                input='height',
                                min=np.min(self.alt),
                                max=np.max(self.alt))
        warped_mesh = Warp(colored_mesh,
                           input=(0, 0, ('height', 'value')),
                           factor=0)
        warp_slider = FloatSlider(min=0,
                                  max=10,
                                  value=0,
                                  description='Vertical exaggeration',
                                  style={'description_width': 'initial'})

        jslink((warped_mesh, 'factor'), (warp_slider, 'value'))

        self.dem3d.clear_output()
        with self.dem3d:
            display(VBox((Scene([warped_mesh]), warp_slider)))

    def create_zarr_chunk(self, lat, lon, tiles, lat0, lat1, lon0, lon1, z):
        if lat < 0:
            fname = 's'
        else:
            fname = 'n'
        fname += str(abs(lat)).zfill(2)
        if lon < 0:
            fname += 'w'
        else:
            fname += 'e'
        fname += str(abs(lon)).zfill(3)
        fname += '_con_grid.zip'
        url = ''
        for continent in tiles:
            if fname in tiles[continent][1]:
                url = tiles[continent][0] + fname
                break
        if url:
            filename = os.path.basename(url)
            name = filename[:filename.find('_grid')]
            adffile = os.path.join(self.tile_dir, name, name, 'w001001.adf')
            zipfname = os.path.join(self.tile_dir, filename)

            if not os.path.exists(adffile):
                r = requests.get(url, stream=True)
                with open(zipfname, 'wb') as f:
                    total_length = int(r.headers.get('content-length'))
                    self.notif.clear_output()
                    with self.notif:
                        for chunk in tqdm(r.iter_content(chunk_size=1024),
                                          total=ceil(total_length / 1024)):
                            if chunk:
                                f.write(chunk)
                                f.flush()
                zip = zipfile.ZipFile(zipfname)
                zip.extractall(self.tile_dir)

            dem = gdal.Open(adffile)
            geo = dem.GetGeoTransform()
            ySize, xSize = dem.RasterYSize, dem.RasterXSize
            dem = dem.ReadAsArray()
            # data is padded into a 6000x6000 array (some tiles may be smaller)
            array_5x5 = np.full((6000, 6000), -32768, dtype='int16')
            y0 = int(round((geo[3] - lat - 5) / geo[5]))
            y1 = 6000 - int(round((lat - (geo[3] + geo[5] * ySize)) / geo[5]))
            x0 = int(round((geo[0] - lon) / geo[1]))
            x1 = 6000 - int(
                round(((lon + 5) - (geo[0] + geo[1] * xSize)) / geo[1]))
            array_5x5[y0:y1, x0:x1] = dem
            array_5x5 = np.where(array_5x5 == -32768, np.nan,
                                 array_5x5.astype('float32'))
            y0 = z.shape[0] + (lat - lat1) // 5 * 6000
            y1 = y0 + 6000
            x0 = (lon - lon0) // 5 * 6000
            x1 = x0 + 6000
            z[y0:y1, x0:x1] = array_5x5[::-1]
Example #16
0
def run_miningrehab_app(ds):
    """
    Plots an interactive map of the mining case-study area and allows
    the user to draw polygons. This returns plots of the fractional cover value
    of bare soil, green vegetation and brown vegetation in the polygon area.
    Last modified: January 2020

    inputs
    ds - data set containing masked Fractional Cover data from Landsat 8
    """

    # Suppress warnings
    warnings.filterwarnings("ignore")

    # Update plotting functionality through rcParams
    mpl.rcParams.update({"figure.autolayout": True})

    # Define the bounding box that will be overlayed on the interactive map
    # The bounds are hard-coded to match those from the loaded data
    geom_obj = {
        "type": "Feature",
        "properties": {
            "style": {
                "stroke": True,
                "color": "red",
                "weight": 4,
                "opacity": 0.8,
                "fill": True,
                "fillColor": False,
                "fillOpacity": 0,
                "showArea": True,
                "clickable": True,
            }
        },
        "geometry": {
            "type":
            "Polygon",
            "coordinates": [[
                [116.630731, -34.434517],
                [116.630731, -34.426512],
                [116.648123, -34.426512],
                [116.648123, -34.434517],
                [116.630731, -34.434517],
            ]],
        },
    }

    # Create a map geometry from the geom_obj dictionary
    # center specifies where the background map view should focus on
    # zoom specifies how zoomed in the background map should be
    loadeddata_geometry = ogr.CreateGeometryFromJson(str(geom_obj["geometry"]))
    loadeddata_center = [
        loadeddata_geometry.Centroid().GetY(),
        loadeddata_geometry.Centroid().GetX(),
    ]
    loadeddata_zoom = 15

    # define the study area map
    studyarea_map = Map(
        layout=widgets.Layout(width="480px", height="600px"),
        center=loadeddata_center,
        zoom=loadeddata_zoom,
        basemap=basemaps.Esri.WorldImagery,
    )

    # define the drawing controls
    studyarea_drawctrl = DrawControl(
        polygon={"shapeOptions": {
            "fillOpacity": 0
        }},
        marker={},
        circle={},
        circlemarker={},
        polyline={},
    )

    # add drawing controls and data bound geometry to the map
    studyarea_map.add_control(studyarea_drawctrl)
    studyarea_map.add_layer(GeoJSON(data=geom_obj))

    # Index to count drawn polygons
    polygon_number = 0

    # Define widgets to interact with
    instruction = widgets.Output(layout={"border": "1px solid black"})
    with instruction:
        print("Draw a polygon within the red box to view plots of "
              "the fractional cover values of bare, green and "
              "non-green cover for the area over time.")

    info = widgets.Output(layout={"border": "1px solid black"})
    with info:
        print("Plot status:")

    fig_display = widgets.Output(layout=widgets.Layout(
        width="50%"  # proportion of horizontal space taken by plot
    ))

    with fig_display:
        plt.ioff()
        fig, ax = plt.subplots(3, 1, figsize=(9, 9))

        for axis in ax:
            axis.set_ylim([0, 1])

    colour_list = plt.rcParams["axes.prop_cycle"].by_key()["color"]

    # Function to execute each time something is drawn on the map
    def handle_draw(self, action, geo_json):
        nonlocal polygon_number

        #         info.clear_output(wait=True)  # wait=True reduces flicker effect
        #         with info:
        #             print("Plot status: entered handle draw")

        # Execute behaviour based on what the user draws
        if geo_json["geometry"]["type"] == "Polygon":

            # Convert the drawn geometry to pixel coordinates
            geom_selectedarea = transform_geojson_wgs_to_epsg(
                geo_json,
                EPSG=3577  # hard-coded to be same as case-study data
            )

            # Construct a mask to only select pixels within the drawn polygon
            mask = rasterio.features.geometry_mask(
                [geom_selectedarea for geoms in [geom_selectedarea]],
                out_shape=ds.geobox.shape,
                transform=ds.geobox.affine,
                all_touched=False,
                invert=True,
            )

            masked_ds = ds.where(mask)
            masked_ds_mean = masked_ds.mean(dim=["x", "y"], skipna=True)

            colour = colour_list[polygon_number % len(colour_list)]

            # Add a layer to the map to make the most recently drawn polygon
            # the same colour as the line on the plot
            studyarea_map.add_layer(
                GeoJSON(
                    data=geo_json,
                    style={
                        "color": colour,
                        "opacity": 1,
                        "weight": 4.5,
                        "fillOpacity": 0.0,
                    },
                ))

            # Add Fractional cover plots to app
            masked_ds_mean.BS.interpolate_na(
                dim="time", method="nearest").plot.line("-", ax=ax[0])
            masked_ds_mean.PV.interpolate_na(
                dim="time", method="nearest").plot.line("-", ax=ax[1])
            masked_ds_mean.NPV.interpolate_na(
                dim="time", method="nearest").plot.line("-", ax=ax[2])

            # reset titles back to custom
            ax[0].set_ylabel("Bare cover")
            ax[1].set_ylabel("Green cover")
            ax[2].set_ylabel("Non-green cover")

            # refresh display
            fig_display.clear_output(
                wait=True)  # wait=True reduces flicker effect
            with fig_display:
                display(fig)

            # Update plot info
            info.clear_output(wait=True)  # wait=True reduces flicker effect
            with info:
                print("Plot status: polygon added to plot")

            # Iterate the polygon number before drawing another polygon
            polygon_number = polygon_number + 1

        else:
            info.clear_output(wait=True)
            with info:
                print("Plot status: this drawing tool is not currently "
                      "supported. Please use the polygon tool.")

    # call to say activate handle_draw function on draw
    studyarea_drawctrl.on_draw(handle_draw)

    with fig_display:
        # TODO: update with user friendly something
        display(widgets.HTML(""))

    # Construct UI:
    #  +-----------------------+
    #  | instruction           |
    #  +-----------+-----------+
    #  |  map      |  plot     |
    #  |           |           |
    #  +-----------+-----------+
    #  | info                  |
    #  +-----------------------+
    ui = widgets.VBox(
        [instruction,
         widgets.HBox([studyarea_map, fig_display]), info])
    display(ui)
Example #17
0
def run_valuation_app():
    """
    Description of function to come
    """
    # Suppress warnings
    warnings.filterwarnings('ignore')

    # Update plotting functionality through rcParams
    mpl.rcParams.update({'figure.autolayout': True})

    #Start the datacube
    dc = datacube.Datacube(app='dem')

    # Set the parameters to load the backgroun map
    # center specifies where the background map view should focus on
    # zoom specifies how zoomed in the background map should be
    loadeddata_center = [-33.419474, 149.577299]
    loadeddata_zoom = 12

    # define the background map
    studyarea_map = Map(center=loadeddata_center,
                        zoom=loadeddata_zoom,
                        basemap=basemaps.OpenStreetMap.Mapnik)

    # define the drawing controls
    studyarea_drawctrl = DrawControl(
        polygon={"shapeOptions": {
            "fillOpacity": 0
        }},
        marker={},
        circle={},
        circlemarker={},
        polyline={},
    )

    # add drawing controls and data bound geometry to the map
    studyarea_map.add_control(studyarea_drawctrl)
    #studyarea_map.add_layer(GeoJSON(data=geom_obj))

    # Index to count drawn polygons
    polygon_number = 0

    # Define widgets to interact with
    instruction = widgets.Output(layout={'border': '1px solid black'})
    with instruction:
        print("Draw a polygon within the red box to estimate "
              "the number of pixels in the polygon.")

    info = widgets.Output(layout={'border': '1px solid black'})
    with info:
        print("Plot status:")

    fig_display = widgets.Output(layout=widgets.Layout(
        width="50%",  # proportion of horizontal space taken by plot
    ))

    with fig_display:
        plt.ioff()
        fig, ax = plt.subplots(figsize=(8, 6))

    colour_list = plt.rcParams['axes.prop_cycle'].by_key()['color']

    # Function to execute each time something is drawn on the map
    def handle_draw(self, action, geo_json):
        nonlocal polygon_number

        # Execute behaviour based on what the user draws
        if geo_json['geometry']['type'] == 'Polygon':

            # Convert the drawn geometry to pixel coordinates
            geom_selectedarea_flat, geom_selectedarea = transform_from_wgs_poly(
                geo_json['geometry'],
                EPSGa=3577  # hard-coded to be same as case-study data
            )

            geom_envelope = geom_selectedarea_flat.GetEnvelope()
            minX, maxX, minY, maxY = geom_envelope

            # Insert dc.load for dem and dlcdnsw
            x_range = (minX, maxX)
            y_range = (minY, maxY)
            inputcrs = "EPSG:3577"
            dem_res = (-5, 5)
            # dlcd_res is unused as the same res must be used when loading
            # multiple products (to support the cross count process). The
            # smallest cell size is used as this will provide the greatest
            # accuracy.
            dlcd_res = (-100, 100)  # unused

            slope_cat = get_slope_raster(dc, x_range, y_range, inputcrs,
                                         dem_res, geom_selectedarea)
            dlcd = get_dlcd_raster(dc, x_range, y_range, inputcrs, dem_res,
                                   geom_selectedarea)

            stacked = xr.merge([dlcd, slope_cat])
            cross_counts = unique_counts(stacked)

            slope_cat_count = slope_cat.to_dataframe(
            ).slope_category.value_counts().rename('counts').to_frame()
            slope_cat_count.index.name = 'index'
            slope_cat_table = pd.read_csv('slope_cat.csv')
            slope_coverage = pd.merge(slope_cat_count,
                                      slope_cat_table,
                                      how="left",
                                      left_on=['index'],
                                      right_on=['id'])

            # Compute the total number of pixels in the masked data set
            pix_dlcd = dlcd.count().compute().item()

            # Convert dlcd to pandas and get value counts for each class
            pd_dlcd = dlcd.to_dataframe()
            pd_dlcd_classcount = pd_dlcd.dlcd.value_counts().reset_index(
                name='counts')

            # Convert slope_cat to pandas and get value counts for each category
            # pd_slope_cat_count = slope_cat.to_dataframe().band1.value_counts()

            # Load DLCD land cover look-up table
            dlcd_lookup = pd.read_csv("dlcd.csv")

            # Join dlcd counts against landcover look up table
            pd_dlcd_coverage = pd.merge(pd_dlcd_classcount,
                                        dlcd_lookup,
                                        how="left",
                                        left_on=['index'],
                                        right_on=['id'])

            # Format the counts table to keep necessary items
            pd_dlcd_coverage['area(km^2)'] = pd_dlcd_coverage['counts'] * (
                dem_res[1] / 1000.)**2
            pd_dlcd_coverage['percentage_area'] = pd_dlcd_coverage[
                'counts'] / pd_dlcd_coverage['counts'].sum() * 100

            pd_dlcd_output = pd_dlcd_coverage[[
                'Name', 'area(km^2)', 'percentage_area'
            ]]

            # manipulate cross counts into format suitable for presentation as
            # a table
            pd_cross_counts = cross_counts.to_dataframe()
            pd_cross_counts.sort_values(by='count',
                                        ascending=False,
                                        inplace=True)
            # join DLCD lookup table for DLCD class names
            pd_cross_counts = pd.merge(pd_cross_counts,
                                       dlcd_lookup,
                                       how='left',
                                       left_on=['dlcd'],
                                       right_on=['id'])
            pd_cross_counts = pd_cross_counts.rename(columns={
                'dlcd': 'dlcd_id',
                'Name': 'DLCD'
            })
            # join slope category definition table for slope class names
            pd_cross_counts = pd.merge(pd_cross_counts,
                                       slope_cat_table,
                                       how='left',
                                       left_on=['slope_category'],
                                       right_on=['id'])
            pd_cross_counts['slope'] = (pd_cross_counts[[
                'label', 'range'
            ]].apply(lambda x: '{} {}'.format(x[0], x[1]), axis=1))

            # Format the counts table to keep necessary items
            pd_cross_counts['area(km^2)'] = (pd_cross_counts['count'] *
                                             (dem_res[1] / 1000.)**2)
            pd_cross_counts['percentage_area'] = (
                pd_cross_counts['count'] / pd_cross_counts['count'].sum() *
                100)

            pd_cross_counts_output = (pd_cross_counts[[
                'DLCD', 'slope', 'area(km^2)', 'percentage_area'
            ]])

            # display(pd_cross_counts)

            colour = colour_list[polygon_number % len(colour_list)]

            # Add a layer to the map to make the most recently drawn polygon
            # the same colour as the line on the plot
            studyarea_map.add_layer(
                GeoJSON(data=geo_json,
                        style={
                            'color': colour,
                            'opacity': 1,
                            'weight': 4.5,
                            'fillOpacity': 0.0
                        }))

            # Make the DLDC Summary plot
            ax.clear()
            pd_dlcd_output.plot.bar(x='Name',
                                    y='percentage_area',
                                    rot=45,
                                    ax=ax,
                                    legend=False,
                                    color=colour)
            ax.set_xlabel("Land Cover Class")
            ax.set_ylabel("Percentage Coverage Of Polygon")

            # refresh display
            fig_display.clear_output(
                wait=True)  # wait=True reduces flicker effect
            with fig_display:
                display(fig)

            info.clear_output(wait=True)  # wait=True reduces flicker effect
            with info:
                # use to_string function to avoid truncation of results
                print(pd_cross_counts_output.to_string())

            # Iterate the polygon number before drawing another polygon
            polygon_number = polygon_number + 1

        else:
            info.clear_output(wait=True)
            with info:
                print("Plot status: this drawing tool is not currently "
                      "supported. Please use the polygon tool.")

    # call to say activate handle_draw function on draw
    studyarea_drawctrl.on_draw(handle_draw)

    with fig_display:
        # TODO: update with user friendly something
        display(widgets.HTML(""))

    # Construct UI:
    #  +-----------------------+
    #  | instruction           |
    #  +-----------+-----------+
    #  |  map                  |
    #  |                       |
    #  +-----------+-----------+
    #  | info                  |
    #  +-----------------------+
    ui = widgets.VBox(
        [instruction,
         widgets.HBox([studyarea_map, fig_display]), info])
    display(ui)
Example #18
0
def run_crophealth_app(ds, lat, lon, buffer):
    """
    Plots an interactive map of the crop health case-study area and allows
    the user to draw polygons. This returns a plot of the average NDVI value
    in the polygon area.
    Last modified: January 2020
    
    Parameters
    ----------
    ds: xarray.Dataset 
        data set containing combined, masked data
        Masked values are set to 'nan'
    lat: float
        The central latitude corresponding to the area of loaded ds
    lon: float
        The central longitude corresponding to the area of loaded ds
    buffer:
         The number of square degrees to load around the central latitude and longitude. 
         For reasonable loading times, set this as `0.1` or lower.
    """
    
    # Suppress warnings
    warnings.filterwarnings('ignore')

    # Update plotting functionality through rcParams
    mpl.rcParams.update({'figure.autolayout': True})
    
    # Define polygon bounds   
    latitude = (lat - buffer, lat + buffer)
    longitude = (lon - buffer, lon + buffer)

    # Define the bounding box that will be overlayed on the interactive map
    # The bounds are hard-coded to match those from the loaded data
    geom_obj = {
        "type": "Feature",
        "properties": {
            "style": {
                "stroke": True,
                "color": 'red',
                "weight": 4,
                "opacity": 0.8,
                "fill": True,
                "fillColor": False,
                "fillOpacity": 0,
                "showArea": True,
                "clickable": True
            }
        },
        "geometry": {
            "type": "Polygon",
            "coordinates": [
                [
                    [
                        longitude[0],
                        latitude[0]
                    ],
                    [
                        longitude[1],
                        latitude[0]
                    ],
                    [
                        longitude[1],
                        latitude[1]
                    ],
                    [
                        longitude[0],
                        latitude[1]
                    ],
                    [
                        longitude[0],
                        latitude[0]
                    ]
                ]
            ]
        }
    }
    
    # Create a map geometry from the geom_obj dictionary
    # center specifies where the background map view should focus on
    # zoom specifies how zoomed in the background map should be
    loadeddata_geometry = ogr.CreateGeometryFromJson(str(geom_obj['geometry']))
    loadeddata_center = [
        loadeddata_geometry.Centroid().GetY(),
        loadeddata_geometry.Centroid().GetX()
    ]
    loadeddata_zoom = 16

    # define the study area map
    studyarea_map = Map(
        center=loadeddata_center,
        zoom=loadeddata_zoom,
        basemap=basemaps.Esri.WorldImagery
    )

    # define the drawing controls
    studyarea_drawctrl = DrawControl(
        polygon={"shapeOptions": {"fillOpacity": 0}},
        marker={},
        circle={},
        circlemarker={},
        polyline={},
    )

    # add drawing controls and data bound geometry to the map
    studyarea_map.add_control(studyarea_drawctrl)
    studyarea_map.add_layer(GeoJSON(data=geom_obj))

    # Index to count drawn polygons
    polygon_number = 0

    # Define widgets to interact with
    instruction = widgets.Output(layout={'border': '1px solid black'})
    with instruction:
        print("Draw a polygon within the red box to view a plot of "
              "average NDVI over time in that area.")

    info = widgets.Output(layout={'border': '1px solid black'})
    with info:
        print("Plot status:")

    fig_display = widgets.Output(layout=widgets.Layout(
        width="50%",  # proportion of horizontal space taken by plot
    ))

    with fig_display:
        plt.ioff()
        fig, ax = plt.subplots(figsize=(8, 6))
        ax.set_ylim([0, 1])

    colour_list = plt.rcParams['axes.prop_cycle'].by_key()['color']

    # Function to execute each time something is drawn on the map
    def handle_draw(self, action, geo_json):
        nonlocal polygon_number

        # Execute behaviour based on what the user draws
        if geo_json['geometry']['type'] == 'Polygon':

            info.clear_output(wait=True)  # wait=True reduces flicker effect
            
            # Save geojson polygon to io temporary file to be rasterized later
            jsonData = json.dumps(geo_json)
            binaryData = jsonData.encode()
            io = BytesIO(binaryData)
            io.seek(0)
            
            # Read the polygon as a geopandas dataframe
            gdf = gpd.read_file(io)
            gdf.crs = "EPSG:4326"

            # Convert the drawn geometry to pixel coordinates
            xr_poly = xr_rasterize(gdf, ds.NDVI.isel(time=0), crs='EPSG:6933')

            # Construct a mask to only select pixels within the drawn polygon
            masked_ds = ds.NDVI.where(xr_poly)
            
            masked_ds_mean = masked_ds.mean(dim=['x', 'y'], skipna=True)
            colour = colour_list[polygon_number % len(colour_list)]

            # Add a layer to the map to make the most recently drawn polygon
            # the same colour as the line on the plot
            studyarea_map.add_layer(
                GeoJSON(
                    data=geo_json,
                    style={
                        'color': colour,
                        'opacity': 1,
                        'weight': 4.5,
                        'fillOpacity': 0.0
                    }
                )
            )

            # add new data to the plot
            xr.plot.plot(
                masked_ds_mean,
                marker='*',
                color=colour,
                ax=ax
            )

            # reset titles back to custom
            ax.set_title("Average NDVI from Landsat 8")
            ax.set_xlabel("Date")
            ax.set_ylabel("NDVI")

            # refresh display
            fig_display.clear_output(wait=True)  # wait=True reduces flicker effect
            with fig_display:
                display(fig)
                
            with info:
                print("Plot status: polygon sucessfully added to plot.")

            # Iterate the polygon number before drawing another polygon
            polygon_number = polygon_number + 1

        else:
            info.clear_output(wait=True)
            with info:
                print("Plot status: this drawing tool is not currently "
                      "supported. Please use the polygon tool.")

    # call to say activate handle_draw function on draw
    studyarea_drawctrl.on_draw(handle_draw)

    with fig_display:
        # TODO: update with user friendly something
        display(widgets.HTML(""))

    # Construct UI:
    #  +-----------------------+
    #  | instruction           |
    #  +-----------+-----------+
    #  |  map      |  plot     |
    #  |           |           |
    #  +-----------+-----------+
    #  | info                  |
    #  +-----------------------+
    ui = widgets.VBox([instruction,
                       widgets.HBox([studyarea_map, fig_display]),
                       info])
    display(ui)
Example #19
0
tiffFiles_path = os.path.join(orig_path, "tiffFiles")
os.chdir(tiffFiles_path)
jsonFile = cityName + ".geojson"
geojson_exists = False
if (os.path.exists(os.path.join(os.getcwd(), jsonFile))):
    actionCount += 1
    with open(os.path.join(os.getcwd(), jsonFile)) as f:
        AOIs[actionCount] = json.load(f)
    print("GEOJSON ALREADY CREATED - MOVE TO NEXT CELL\n")
    geojson_exists = True
else:
    # Attach the draw handler to the draw controls `on_draw` event
    print("Draw area of interest on map and a geojson will be generated\n")
    os.chdir(orig_path)
    dc.on_draw(handle_draw)
    m.add_control(dc)
    geojson_exists = False
m

# # Querying the Planet API.
# * First we'll grab our geojson area of interest (AOI) and use it to construct a query.
# * We'll then build a search to search that area looking for PSScene3Band
# * We have lots of products: RapidEye, PlanetScope (PS) 3 and 4 band, LandSat, and Sentinel are all possible.
# * Once we have our query, we'll do the search. We will then iterate over the results, slurp up the data, and put them in a pandas data frame for easy sorting.
# * We'll print the first few so we're sure it works.

# In[73]:

import datetime
os.chdir(orig_path)
Example #20
0
def draw_map(routes):
    tiles = create_dummy()
    m = Map(layers=(basemap_to_tiles(tiles, "2017-04-08"), ),
            zoom=12,
            layout=Layout(width='100%', height='600px'))

    tiles30 = create_dummy()
    tiles30[
        "url"] = 'https://wmts.geo.admin.ch/1.0.0/ch.swisstopo.hangneigung-ueber_30/default/current/3857/{z}/{x}/{y}.png'
    tiles["opacity"] = .0
    layer = basemap_to_tiles(tiles30, "adad")

    layer.opacity = 0.2
    m.add_layer(layer)

    tiles30 = create_dummy()
    tiles30[
        "url"] = 'https://wmts.geo.admin.ch/1.0.0/ch.swisstopo-karto.skitouren/default/current/3857/{z}/{x}/{y}.png'
    tiles["opacity"] = .0
    layer = basemap_to_tiles(tiles30, "adad")

    layer.opacity = 0.9
    m.add_layer(layer)

    # layer = ipyl.GeoJSON(data=geojson, hover_style={'fillColor': 'red'})

    # def hover_handler(event=None, id=None, properties=None):
    #    label.value = properties['geounit']

    # layer.on_hover(hover_handler)
    draw_control = DrawControl()
    draw_control.polyline = {
        "shapeOptions": {
            "color": "#6bc2e5",
            "weight": 8,
            "opacity": 1.0
        }
    }
    draw_control.polygon = {
        "shapeOptions": {
            "fillColor": "#6be5c3",
            "color": "#6be5c3",
            "fillOpacity": 1.0
        },
        "drawError": {
            "color": "#dd253b",
            "message": "Oups!"
        },
        "allowIntersection": False
    }
    draw_control.circle = {
        "shapeOptions": {
            "fillColor": "#efed69",
            "color": "#efed69",
            "fillOpacity": 1.0
        }
    }
    draw_control.rectangle = {
        "shapeOptions": {
            "fillColor": "#fca45d",
            "color": "#fca45d",
            "fillOpacity": 1.0
        }
    }

    def on_draw(x, geo_json, action):
        route = routes[0]
        cs_ch = []
        for ps in geo_json["geometry"]["coordinates"]:
            p = Point(ps[0], ps[1])
            p = transform(wgs2ch, p)
            cs_ch.append([p.x, p.y])

        geo_json["geometry"]["coordinates"] = cs_ch

        route.geo_path = geo_json

    draw_control.on_draw(on_draw)

    m.add_control(draw_control)

    for route in routes:

        if route.geo_path is not None:
            geo_json = copy.deepcopy(route.geo_path)
            cs_ch = []
            for ps in geo_json["geometry"]["coordinates"]:
                p = Point(ps[0], ps[1])
                p = transform(ch2wgs, p)
                cs_ch.append([p.x, p.y])

            geo_json["geometry"]["coordinates"] = cs_ch

            geo_json = GeoJSON(data=geo_json)
            m.add_layer(geo_json)

        points = []
        labels = []
        points += [transform(ch2wgs, w.point) for w in route.get_waypoints()]
        labels += [route.get_name() for p in route.get_waypoints()]
        for p, label in zip(points, labels):
            marker = CircleMarker()
            marker.location = (p.y, p.x)
            marker.color = "blue"
            marker.title = label

            m.add_layer(marker)

        points = []
        labels = []
        points += [
            transform(ch2wgs, stop.point) for stop in route.get_pt_stops()
        ]
        labels += [route.get_name() for p in route.get_pt_stops()]
        for p, label in zip(points, labels):
            marker = CircleMarker()
            marker.location = (p.y, p.x)
            marker.color = "red"
            marker.title = label

            m.add_layer(marker)
    p = points[0]
    m.center = (p.y, p.x)
    return m