3636basemaps = Box (xyz_to_leaflet (), frozen_box = True )
3737
3838
39+ class MapDrawControl (ipyleaflet .DrawControl , map_widgets .AbstractDrawControl ):
40+ """ "Implements the AbstractDrawControl for the map."""
41+
42+ _roi_start = False
43+ _roi_end = False
44+
45+ def __init__ (self , host_map , ** kwargs ):
46+ """Initialize the map draw control.
47+
48+ Args:
49+ host_map (geemap.Map): The geemap.Map object that the control will be added to.
50+ """
51+ super (MapDrawControl , self ).__init__ (host_map = host_map , ** kwargs )
52+
53+ @property
54+ def user_roi (self ):
55+ """Returns the last drawn geometry.
56+
57+ Returns:
58+ ee.Geometry: Last drawn geometry.
59+ """
60+ return self .last_geometry
61+
62+ @property
63+ def user_rois (self ):
64+ """Returns all drawn geometries as an ee.FeatureCollection.
65+
66+ Returns:
67+ ee.FeatureCollection: All drawn geometries.
68+ """
69+ return self .collection
70+
71+ # NOTE: Overridden for backwards compatibility, where edited geometries are
72+ # added to the layer instead of modified in place. Remove when
73+ # https://github.com/jupyter-widgets/ipyleaflet/issues/1119 is fixed to
74+ # allow geometry edits to be reflected on the tile layer.
75+ def _handle_geometry_edited (self , geo_json ):
76+ return self ._handle_geometry_created (geo_json )
77+
78+ def _get_synced_geojson_from_draw_control (self ):
79+ return [data .copy () for data in self .data ]
80+
81+ def _bind_to_draw_control (self ):
82+ # Handles draw events
83+ def handle_draw (_ , action , geo_json ):
84+ try :
85+ self ._roi_start = True
86+ if action == "created" :
87+ self ._handle_geometry_created (geo_json )
88+ elif action == "edited" :
89+ self ._handle_geometry_edited (geo_json )
90+ elif action == "deleted" :
91+ self ._handle_geometry_deleted (geo_json )
92+ self ._roi_end = True
93+ self ._roi_start = False
94+ except Exception as e :
95+ self .reset (clear_draw_control = False )
96+ self ._roi_start = False
97+ self ._roi_end = False
98+ print ("There was an error creating Earth Engine Feature." )
99+ raise Exception (e )
100+
101+ self .on_draw (handle_draw )
102+ # NOTE: Uncomment the following code once
103+ # https://github.com/jupyter-widgets/ipyleaflet/issues/1119 is fixed
104+ # to allow edited geometries to be reflected instead of added.
105+ # def handle_data_update(_):
106+ # self._sync_geometries()
107+ # self.observe(handle_data_update, 'data')
108+
109+ def _remove_geometry_at_index_on_draw_control (self , index ):
110+ # NOTE: Uncomment the following code once
111+ # https://github.com/jupyter-widgets/ipyleaflet/issues/1119 is fixed to
112+ # remove drawn geometries with `remove_last_drawn()`.
113+ # del self.data[index]
114+ # self.send_state(key='data')
115+ pass
116+
117+ def _clear_draw_control (self ):
118+ return self .clear ()
119+
120+
39121class Map (ipyleaflet .Map ):
40122 """The Map class inherits the ipyleaflet Map class. The arguments you can pass to the Map initialization
41123 can be found at https://ipyleaflet.readthedocs.io/en/latest/map_and_basemaps/map.html.
@@ -46,6 +128,27 @@ class Map(ipyleaflet.Map):
46128 object: ipyleaflet map object.
47129 """
48130
131+ # Map attributes for drawing features
132+ @property
133+ def draw_features (self ):
134+ return self .draw_control .features if self .draw_control else []
135+
136+ @property
137+ def draw_last_feature (self ):
138+ return self .draw_control .last_feature if self .draw_control else None
139+
140+ @property
141+ def draw_layer (self ):
142+ return self .draw_control .layer if self .draw_control else None
143+
144+ @property
145+ def user_roi (self ):
146+ return self .draw_control .user_roi if self .draw_control else None
147+
148+ @property
149+ def user_rois (self ):
150+ return self .draw_control .user_rois if self .draw_control else None
151+
49152 def __init__ (self , ** kwargs ):
50153 """Initialize a map object. The following additional parameters can be passed in addition to the ipyleaflet.Map parameters:
51154
@@ -170,13 +273,6 @@ def __init__(self, **kwargs):
170273 if kwargs .get (control , True ):
171274 self .add_controls (control , position = "bottomright" )
172275
173- # Map attributes for drawing features
174- self .draw_features = []
175- self .draw_last_feature = None
176- self .draw_layer = None
177- self .user_roi = None
178- self .user_rois = None
179-
180276 # Map attributes for layers
181277 self .geojson_layers = []
182278 self .ee_layers = []
@@ -2467,7 +2563,9 @@ def _on_close():
24672563 self .inspector_control .close ()
24682564 self .inspector_control = None
24692565
2470- inspector = map_widgets .Inspector (self , names , visible , decimals , opened , show_close_button )
2566+ inspector = map_widgets .Inspector (
2567+ self , names , visible , decimals , opened , show_close_button
2568+ )
24712569 inspector .on_close = _on_close
24722570 self .inspector_control = ipyleaflet .WidgetControl (
24732571 widget = inspector , position = position
@@ -2500,8 +2598,8 @@ def add_draw_control(self, position="topleft"):
25002598 Args:
25012599 position (str, optional): The position of the draw control. Defaults to "topleft".
25022600 """
2503-
2504- draw_control = ipyleaflet . DrawControl (
2601+ draw_control = MapDrawControl (
2602+ host_map = self ,
25052603 marker = {"shapeOptions" : {"color" : "#3388ff" }},
25062604 rectangle = {"shapeOptions" : {"color" : "#3388ff" }},
25072605 # circle={"shapeOptions": {"color": "#3388ff"}},
@@ -2510,50 +2608,6 @@ def add_draw_control(self, position="topleft"):
25102608 remove = True ,
25112609 position = position ,
25122610 )
2513-
2514- # Handles draw events
2515- def handle_draw (target , action , geo_json ):
2516- try :
2517- self ._roi_start = True
2518- geom = geojson_to_ee (geo_json , False )
2519- self .user_roi = geom
2520- feature = ee .Feature (geom )
2521- self .draw_last_feature = feature
2522- if not hasattr (self , "_draw_count" ):
2523- self ._draw_count = 0
2524- if action == "deleted" and len (self .draw_features ) > 0 :
2525- self .draw_features .remove (feature )
2526- self ._draw_count -= 1
2527- else :
2528- self .draw_features .append (feature )
2529- self ._draw_count += 1
2530- collection = ee .FeatureCollection (self .draw_features )
2531- self .user_rois = collection
2532- ee_draw_layer = EELeafletTileLayer (
2533- collection , {"color" : "blue" }, "Drawn Features" , False , 0.5
2534- )
2535- draw_layer_index = self .find_layer_index ("Drawn Features" )
2536-
2537- if draw_layer_index == - 1 :
2538- self .add (ee_draw_layer )
2539- self .draw_layer = ee_draw_layer
2540- else :
2541- self .substitute_layer (self .draw_layer , ee_draw_layer )
2542- self .draw_layer = ee_draw_layer
2543- self ._roi_end = True
2544- self ._roi_start = False
2545- except Exception as e :
2546- self ._draw_count = 0
2547- self .draw_features = []
2548- self .draw_last_feature = None
2549- self .draw_layer = None
2550- self .user_roi = None
2551- self ._roi_start = False
2552- self ._roi_end = False
2553- print ("There was an error creating Earth Engine Feature." )
2554- raise Exception (e )
2555-
2556- draw_control .on_draw (handle_draw )
25572611 self .add (draw_control )
25582612 self .draw_control = draw_control
25592613
@@ -2585,8 +2639,11 @@ def add_toolbar(self, position="topright", **kwargs):
25852639 """
25862640
25872641 from .toolbar import Toolbar , main_tools , extra_tools
2642+
25882643 self ._toolbar = Toolbar (self , main_tools , extra_tools )
2589- toolbar_control = ipyleaflet .WidgetControl (widget = self ._toolbar , position = position )
2644+ toolbar_control = ipyleaflet .WidgetControl (
2645+ widget = self ._toolbar , position = position
2646+ )
25902647 self .add (toolbar_control )
25912648
25922649 def add_plot_gui (self , position = "topright" , ** kwargs ):
@@ -4060,46 +4117,37 @@ def add_remote_tile(
40604117 else :
40614118 raise Exception ("The source must be a URL." )
40624119
4120+ def remove_draw_control (self ):
4121+ """Removes the draw control from the map"""
4122+ controls = []
4123+ old_draw_control = None
4124+ for control in self .controls :
4125+ if isinstance (control , MapDrawControl ):
4126+ old_draw_control = control
4127+
4128+ else :
4129+ controls .append (control )
4130+
4131+ self .controls = tuple (controls )
4132+ if old_draw_control :
4133+ old_draw_control .close ()
4134+
40634135 def remove_drawn_features (self ):
40644136 """Removes user-drawn geometries from the map"""
4065- if self .draw_layer is not None :
4066- self .remove_layer (self .draw_layer )
4067- self ._draw_count = 0
4068- self .draw_features = []
4069- self .draw_last_feature = None
4070- self .draw_layer = None
4071- self .user_roi = None
4072- self .user_rois = None
4073- self ._chart_values = []
4074- self ._chart_points = []
4075- self ._chart_labels = None
40764137 if self .draw_control is not None :
4077- self .draw_control .clear ()
4138+ self .draw_control .reset ()
40784139
40794140 def remove_last_drawn (self ):
4080- """Removes user-drawn geometries from the map"""
4081- if self .draw_layer is not None :
4082- collection = ee .FeatureCollection (self .draw_features [:- 1 ])
4083- ee_draw_layer = EELeafletTileLayer (
4084- collection , {"color" : "blue" }, "Drawn Features" , True , 0.5
4085- )
4086- if self ._draw_count == 1 :
4141+ """Removes last user-drawn geometry from the map"""
4142+ if self .draw_control is not None :
4143+ if self .draw_control .count == 1 :
40874144 self .remove_drawn_features ()
4088- else :
4089- self .substitute_layer (self .draw_layer , ee_draw_layer )
4090- self .draw_layer = ee_draw_layer
4091- self ._draw_count -= 1
4092- self .draw_features = self .draw_features [:- 1 ]
4093- self .draw_last_feature = self .draw_features [- 1 ]
4094- self .draw_layer = ee_draw_layer
4095- self .user_roi = ee .Feature (
4096- collection .toList (collection .size ()).get (
4097- collection .size ().subtract (1 )
4098- )
4099- ).geometry ()
4100- self .user_rois = collection
4101- self ._chart_values = self ._chart_values [:- 1 ]
4102- self ._chart_points = self ._chart_points [:- 1 ]
4145+ elif self .draw_control .count :
4146+ self .draw_control .remove_geometry (self .draw_control .geometries [- 1 ])
4147+ if hasattr (self , "_chart_values" ):
4148+ self ._chart_values = self ._chart_values [:- 1 ]
4149+ if hasattr (self , "_chart_points" ):
4150+ self ._chart_points = self ._chart_points [:- 1 ]
41034151 # self._chart_labels = None
41044152
41054153 def extract_values_to_points (self , filename ):
0 commit comments