@@ -8,6 +8,15 @@ function render({ model, el }) {
88 document . head . appendChild ( link ) ;
99 }
1010
11+ // Inject mapbox-gl-draw CSS if not already loaded
12+ if ( ! document . getElementById ( "mapbox-gl-draw-css" ) ) {
13+ const link = document . createElement ( "link" ) ;
14+ link . id = "mapbox-gl-draw-css" ;
15+ link . rel = "stylesheet" ;
16+ link . href = "https://www.unpkg.com/@mapbox/mapbox-gl-draw@1.5.0/dist/mapbox-gl-draw.css" ;
17+ document . head . appendChild ( link ) ;
18+ }
19+
1120 function updateModel ( model , map ) {
1221 const viewState = {
1322 center : map . getCenter ( ) ,
@@ -51,6 +60,13 @@ function render({ model, el }) {
5160 const controlRegistry = new Map ( ) ;
5261 let processedCallsCount = 0 ;
5362
63+ // Initialize draw features in model
64+ model . set ( "draw_features_selected" , [ ] ) ;
65+ model . set ( "draw_feature_collection_all" , { type : "FeatureCollection" , features : [ ] } ) ;
66+ model . set ( "draw_features_created" , [ ] ) ;
67+ model . set ( "draw_features_updated" , [ ] ) ;
68+ model . set ( "draw_features_deleted" , [ ] ) ;
69+
5470 map . on ( "click" , function ( e ) {
5571 model . set ( "clicked_latlng" , [ e . lngLat . lng , e . lngLat . lat ] ) ;
5672 model . save_changes ( ) ;
@@ -101,6 +117,7 @@ function render({ model, el }) {
101117 console . log ( "Map loaded" ) ;
102118 model . set ( "loaded" , true ) ;
103119 model . save_changes ( ) ;
120+ map . getCanvas ( ) . style . cursor = 'pointer' ;
104121 } ) ;
105122
106123 // Support JS calls from Python
@@ -123,6 +140,16 @@ function render({ model, el }) {
123140 // Handle removeControl specially
124141 const [ controlType ] = args ;
125142 removeControlFromMap ( map , controlType ) ;
143+ } else if ( method === "addDrawControl" ) {
144+ // Handle addDrawControl specially
145+ const [ options , controls , position , geojson ] = args ;
146+ addDrawControlToMap ( map , options , controls , position , geojson ) ;
147+ } else if ( method === "removeDrawControl" ) {
148+ // Handle removeDrawControl specially
149+ removeDrawControlFromMap ( map ) ;
150+ } else if ( method === "drawFeaturesDeleteAll" ) {
151+ // Handle delete all draw features
152+ deleteAllDrawFeatures ( map ) ;
126153 } else if ( typeof map [ method ] === "function" ) {
127154 try {
128155 map [ method ] ( ...( args || [ ] ) ) ;
@@ -196,16 +223,211 @@ function render({ model, el }) {
196223 }
197224 }
198225
226+ // Function to add draw control to the map
227+ function addDrawControlToMap ( map , options = { } , controls = { } , position = "top-right" , geojson = null ) {
228+ // Check if MapboxDraw is available
229+ if ( typeof MapboxDraw === "undefined" ) {
230+ console . warn ( "MapboxDraw is not loaded. Loading now..." ) ;
231+ loadMapboxDraw ( ( ) => addDrawControlToMap ( map , options , controls , position , geojson ) ) ;
232+ return ;
233+ }
234+
235+ // Patch MapboxDraw constants to work with MapLibre GL
236+ if ( MapboxDraw . constants && MapboxDraw . constants . classes ) {
237+ MapboxDraw . constants . classes . CANVAS = 'maplibregl-canvas' ;
238+ MapboxDraw . constants . classes . CONTROL_BASE = 'maplibregl-ctrl' ;
239+ MapboxDraw . constants . classes . CONTROL_PREFIX = 'maplibregl-ctrl-' ;
240+ MapboxDraw . constants . classes . CONTROL_GROUP = 'maplibregl-ctrl-group' ;
241+ MapboxDraw . constants . classes . ATTRIBUTION = 'maplibregl-ctrl-attrib' ;
242+ }
243+
244+ // Default controls configuration
245+ const defaultControls = {
246+ polygon : true ,
247+ line_string : true ,
248+ point : true ,
249+ trash : true ,
250+ combine_features : false ,
251+ uncombine_features : false
252+ } ;
253+
254+ // Merge provided controls with defaults
255+ const drawControls = { ...defaultControls , ...controls } ;
256+
257+ // Default options
258+ const defaultOptions = {
259+ displayControlsDefault : false ,
260+ controls : {
261+ polygon : drawControls . polygon ,
262+ line_string : drawControls . line_string ,
263+ point : drawControls . point ,
264+ trash : drawControls . trash ,
265+ combine_features : drawControls . combine_features ,
266+ uncombine_features : drawControls . uncombine_features
267+ }
268+ } ;
269+
270+ // Merge provided options with defaults
271+ const drawOptions = { ...defaultOptions , ...options } ;
272+
273+ // Create draw control
274+ const draw = new MapboxDraw ( drawOptions ) ;
275+
276+ try {
277+ // For better control positioning, don't specify position if it's default
278+ if ( position === "top-right" ) {
279+ map . addControl ( draw ) ;
280+ } else {
281+ map . addControl ( draw , position ) ;
282+ }
283+ console . log ( `Added draw control at ${ position } ` ) ;
284+ controlRegistry . set ( "draw" , draw ) ;
285+
286+ // Add initial geojson if provided
287+ if ( geojson ) {
288+ if ( geojson . type === "FeatureCollection" ) {
289+ geojson . features . forEach ( feature => {
290+ draw . add ( feature ) ;
291+ } ) ;
292+ } else if ( geojson . type === "Feature" ) {
293+ draw . add ( geojson ) ;
294+ }
295+ updateDrawFeatures ( map , draw ) ;
296+ }
297+
298+ // Set up draw event handlers
299+ setupDrawEventHandlers ( map , draw ) ;
300+
301+ } catch ( err ) {
302+ console . warn ( "Failed to add draw control:" , err ) ;
303+ }
304+ }
305+
306+ // Function to set up draw event handlers
307+ function setupDrawEventHandlers ( map , draw ) {
308+ map . on ( 'draw.create' , function ( e ) {
309+ console . log ( 'Features created:' , e . features ) ;
310+ model . set ( "draw_features_created" , e . features ) ;
311+ updateDrawFeatures ( map , draw ) ;
312+ model . save_changes ( ) ;
313+ } ) ;
314+
315+ map . on ( 'draw.update' , function ( e ) {
316+ console . log ( 'Features updated:' , e . features ) ;
317+ model . set ( "draw_features_updated" , e . features ) ;
318+ updateDrawFeatures ( map , draw ) ;
319+ model . save_changes ( ) ;
320+ } ) ;
321+
322+ map . on ( 'draw.delete' , function ( e ) {
323+ console . log ( 'Features deleted:' , e . features ) ;
324+ model . set ( "draw_features_deleted" , e . features ) ;
325+ updateDrawFeatures ( map , draw ) ;
326+ model . save_changes ( ) ;
327+ } ) ;
328+
329+ map . on ( 'draw.selectionchange' , function ( e ) {
330+ console . log ( 'Selection changed:' , e . features ) ;
331+ model . set ( "draw_features_selected" , e . features ) ;
332+ model . save_changes ( ) ;
333+ } ) ;
334+ }
335+
336+ // Function to update all draw features in model
337+ function updateDrawFeatures ( map , draw ) {
338+ const allFeatures = draw . getAll ( ) ;
339+ model . set ( "draw_feature_collection_all" , allFeatures ) ;
340+ }
341+
342+ // Function to remove draw control from the map
343+ function removeDrawControlFromMap ( map ) {
344+ const draw = controlRegistry . get ( "draw" ) ;
345+ if ( draw ) {
346+ map . removeControl ( draw ) ;
347+ console . log ( "Removed draw control" ) ;
348+ controlRegistry . delete ( "draw" ) ;
349+
350+ // Clear draw features from model
351+ model . set ( "draw_features_selected" , [ ] ) ;
352+ model . set ( "draw_feature_collection_all" , { type : "FeatureCollection" , features : [ ] } ) ;
353+ model . set ( "draw_features_created" , [ ] ) ;
354+ model . set ( "draw_features_updated" , [ ] ) ;
355+ model . set ( "draw_features_deleted" , [ ] ) ;
356+ model . save_changes ( ) ;
357+ } else {
358+ console . warn ( "Draw control not found" ) ;
359+ }
360+ }
361+
362+ // Function to delete all draw features
363+ function deleteAllDrawFeatures ( map ) {
364+ const draw = controlRegistry . get ( "draw" ) ;
365+ if ( draw ) {
366+ const allFeatures = draw . getAll ( ) ;
367+ if ( allFeatures . features . length > 0 ) {
368+ const featureIds = allFeatures . features . map ( f => f . id ) ;
369+ draw . delete ( featureIds ) ;
370+ console . log ( "Deleted all draw features" ) ;
371+
372+ // Update model
373+ model . set ( "draw_features_deleted" , allFeatures . features ) ;
374+ updateDrawFeatures ( map , draw ) ;
375+ model . save_changes ( ) ;
376+ }
377+ } else {
378+ console . warn ( "Draw control not found" ) ;
379+ }
380+ }
381+
382+ // Function to load MapboxDraw if not available
383+ function loadMapboxDraw ( callback ) {
384+ if ( typeof MapboxDraw !== "undefined" ) {
385+ callback ( ) ;
386+ return ;
387+ }
388+
389+ const script = document . createElement ( "script" ) ;
390+ script . src = "https://www.unpkg.com/@mapbox/mapbox-gl-draw@1.5.0/dist/mapbox-gl-draw.js" ;
391+ script . onload = ( ) => {
392+ // Patch MapboxDraw constants immediately after loading
393+ if ( MapboxDraw . constants && MapboxDraw . constants . classes ) {
394+ MapboxDraw . constants . classes . CANVAS = 'maplibregl-canvas' ;
395+ MapboxDraw . constants . classes . CONTROL_BASE = 'maplibregl-ctrl' ;
396+ MapboxDraw . constants . classes . CONTROL_PREFIX = 'maplibregl-ctrl-' ;
397+ MapboxDraw . constants . classes . CONTROL_GROUP = 'maplibregl-ctrl-group' ;
398+ MapboxDraw . constants . classes . ATTRIBUTION = 'maplibregl-ctrl-attrib' ;
399+ }
400+ callback ( ) ;
401+ } ;
402+ script . onerror = ( ) => {
403+ console . error ( "Failed to load MapboxDraw library" ) ;
404+ } ;
405+ document . body . appendChild ( script ) ;
406+ }
407+
199408 // Resize after layout stabilizes
200409 setTimeout ( ( ) => map . resize ( ) , 100 ) ;
201410 }
202411
412+ // Preload MapboxDraw before map initialization
413+ function preloadMapboxDraw ( ) {
414+ if ( typeof MapboxDraw === "undefined" ) {
415+ loadMapboxDraw ( ( ) => {
416+ console . log ( "MapboxDraw preloaded and ready" ) ;
417+ } ) ;
418+ }
419+ }
420+
203421 if ( typeof maplibregl === "undefined" ) {
204422 const script = document . createElement ( "script" ) ;
205423 script . src = "https://unpkg.com/maplibre-gl@5.5.0/dist/maplibre-gl.js" ;
206- script . onload = initMap ;
424+ script . onload = ( ) => {
425+ preloadMapboxDraw ( ) ;
426+ initMap ( ) ;
427+ } ;
207428 document . body . appendChild ( script ) ;
208429 } else {
430+ preloadMapboxDraw ( ) ;
209431 initMap ( ) ;
210432 }
211433}
0 commit comments