Skip to content

Fix geoman controls option update + snapping option #1247

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
2d9e83c
Add pm_ignore to TypeScript side interface definitions, and add get_o…
mangecoeur Apr 19, 2025
0bc020c
Add super.model_events call to geoJson
mangecoeur Apr 19, 2025
f1a8a77
bump version of geoman
mangecoeur Apr 20, 2025
d3a7a55
Add snap ignore option to layers and geoman control
mangecoeur Apr 20, 2025
628e13b
Split definition of polygon and polyline views to appease TypeScript.
mangecoeur Apr 20, 2025
ea02766
docs
mangecoeur Apr 20, 2025
aa75436
Make the geomancontrol match the Control interface so that it can be …
mangecoeur Apr 21, 2025
eea2055
Force the geoman toolbar to update on options change by removing and …
mangecoeur Apr 22, 2025
6233af9
Remove needles log
mangecoeur Apr 23, 2025
b6bfa33
FIX: get() call should be on model, not options; replace special case…
mangecoeur Apr 26, 2025
92dbbc7
FIX: to implement the Control interface, setting the position should …
mangecoeur Apr 26, 2025
ffc3776
Avoid listening to current mode on change.
mangecoeur Apr 26, 2025
df778c4
Lint
mangecoeur May 5, 2025
29774a2
FIX: bugs in pointToLayer method prevented providing existing Points …
mangecoeur May 5, 2025
6e9251b
Fix: remove listeners before removing controls, since doing it the ot…
mangecoeur May 28, 2025
221c22c
Add pm_ignore to TypeScript side interface definitions, and add get_o…
mangecoeur Apr 19, 2025
eec0d2b
Add super.model_events call to geoJson
mangecoeur Apr 19, 2025
b40c4bb
bump version of geoman
mangecoeur Apr 20, 2025
aef80ec
Add snap ignore option to layers and geoman control
mangecoeur Apr 20, 2025
30765ea
Split definition of polygon and polyline views to appease TypeScript.
mangecoeur Apr 20, 2025
c4e3026
docs
mangecoeur Apr 20, 2025
d43c9bf
Make the geomancontrol match the Control interface so that it can be …
mangecoeur Apr 21, 2025
d945856
Force the geoman toolbar to update on options change by removing and …
mangecoeur Apr 22, 2025
5ea33fd
Remove needles log
mangecoeur Apr 23, 2025
b9dd8a2
FIX: get() call should be on model, not options; replace special case…
mangecoeur Apr 26, 2025
2084774
FIX: to implement the Control interface, setting the position should …
mangecoeur Apr 26, 2025
922908b
Avoid listening to current mode on change.
mangecoeur Apr 26, 2025
6389eef
Lint
mangecoeur May 5, 2025
580e53b
FIX: bugs in pointToLayer method prevented providing existing Points …
mangecoeur May 5, 2025
361c8ec
Fix: remove listeners before removing controls, since doing it the ot…
mangecoeur May 28, 2025
ae1661e
Merge remote-tracking branch 'origin/fix_geoman_controls_option_updat…
mangecoeur Jun 9, 2025
ba8716f
Cleanup GeomanDrawControl.ipynb
mangecoeur Jun 9, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
184 changes: 184 additions & 0 deletions examples/GeomanDrawControl.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"ExecuteTime": {
"end_time": "2025-06-09T15:35:39.559258Z",
"start_time": "2025-06-09T15:35:39.448345Z"
}
},
"outputs": [],
"source": [
"from ipyleaflet import (\n",
" Map,\n",
" Marker,\n",
" TileLayer,\n",
" ImageOverlay,\n",
" Polyline,\n",
" Polygon,\n",
" Rectangle,\n",
" Circle,\n",
" CircleMarker,\n",
" GeoJSON,\n",
" GeomanDrawControl,\n",
")\n",
"\n",
"from traitlets import link"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"ExecuteTime": {
"end_time": "2025-05-15T18:21:31.116235Z",
"start_time": "2025-05-15T18:21:31.114028Z"
}
},
"outputs": [],
"source": [
"import json\n",
"\n",
"with open(\"simple.geo.json\") as f:\n",
" data = json.load(f)"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"ExecuteTime": {
"end_time": "2025-05-15T18:21:31.197905Z",
"start_time": "2025-05-15T18:21:31.195304Z"
}
},
"outputs": [],
"source": [
"d1 = data.copy()\n",
"d2 = data.copy()\n",
"\n",
"d1[\"features\"] = d1[\"features\"][:1]\n",
"d2[\"features\"] = d2[\"features\"][1:]\n"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {
"ExecuteTime": {
"end_time": "2025-05-15T18:21:36.974937Z",
"start_time": "2025-05-15T18:21:36.966638Z"
}
},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "14649585f32d4174a701bdd9d77a8b6a",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"Map(center=[46.475212657477016, 6.3198722284199675], controls=(ZoomControl(options=['position', 'zoom_in_text'…"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"center = [46.475212657477016, 6.3198722284199675]\n",
"zoom = 9\n",
"m = Map(center=center, zoom=zoom, layout=dict(height=\"600px\"))\n",
"\n",
"g1 = GeoJSON(data=d1, pm_ignore=True, snap_ignore=True, \n",
" style={\n",
" 'opacity': 1, 'dashArray': '9', 'fillOpacity': 0.1, 'weight': 1\n",
" },\n",
" )\n",
"\n",
"g2 = GeoJSON(data=d2, pm_ignore=True, snap_ignore=False)\n",
"m.add(g1)\n",
"m.add(g2)\n",
"\n",
"draw_data = data[\"features\"][1]\n",
"draw_data[\"properties\"][\"type\"] = \"circlemarker\"\n",
"dc = GeomanDrawControl(\n",
" marker={},\n",
" circlemarker={},\n",
" polygon={},\n",
" data=[draw_data]\n",
")\n",
"\n",
"def handle_draw(target, action, geo_json):\n",
" print(action)\n",
" print(geo_json)\n",
"\n",
"\n",
"dc.on_draw(handle_draw)\n",
"m.add(dc)\n",
"m"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "14649585f32d4174a701bdd9d77a8b6a",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"Map(bottom=46680.0, center=[46.475212657477016, 6.3198722284199675], controls=(ZoomControl(options=['position'…"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Modifying draw options will update the toolbar\n",
"dc.marker={\"markerStyle\": {\"color\": \"#FF0000\"}}\n",
"dc.rectangle={\"pathOptions\": {\"color\": \"#FF0000\"}}\n",
"dc.circlemarker={\"pathOptions\": {\"color\": \"#FF0000\"}}\n",
"m"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.12"
}
},
"nbformat": 4,
"nbformat_minor": 4
}
3 changes: 3 additions & 0 deletions python/ipyleaflet/ipyleaflet/leaflet.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,8 @@ class Layer(Widget, InteractMixin):
Name of the pane to use for the layer.
pm_ignore: boolean
Make Leaflet-Geoman ignore the layer, so it cannot modify it.
snap_ignore: boolean
Make Leaflet-Geoman snapping ignore the layer, so it is not used as a snap target when editing.
"""

_view_name = Unicode("LeafletLayerView").tag(sync=True)
Expand All @@ -198,6 +200,7 @@ class Layer(Widget, InteractMixin):
subitems = Tuple().tag(trait=Instance(Widget), sync=True, **widget_serialization)

pm_ignore = Bool(True).tag(sync=True, o=True)
snap_ignore = Bool(True).tag(sync=True, o=False)

@validate("subitems")
def _validate_subitems(self, proposal):
Expand Down
2 changes: 1 addition & 1 deletion python/jupyter_leaflet/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
"watch:nbextension": "webpack --watch"
},
"dependencies": {
"@geoman-io/leaflet-geoman-free": "^2.16.0",
"@geoman-io/leaflet-geoman-free": "^2.18.0",
"@jupyter-widgets/base": "^2 || ^3 || ^4 || ^5 || ^6",
"buffer": "^6.0.3",
"crypto-browserify": "^3.12.0",
Expand Down
11 changes: 1 addition & 10 deletions python/jupyter_leaflet/src/Map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -227,23 +227,14 @@ export class LeafletMapView extends LeafletDOMWidgetView {
}

remove_control_view(child_view: LeafletControlView) {
this.obj.removeControl(child_view.obj);
child_view.remove();
this.obj.removeControl(child_view.obj);
}

async add_control_model(child_model: LeafletControlModel) {
const view = await this.create_child_view<LeafletControlView>(child_model, {
map_view: this,
});
// Work around for Geoman creating and adding its own toolbar
// TODO: remove the special case
if (
view instanceof LeafletGeomanDrawControlView &&
!child_model.get('hide_controls')
) {
this.obj.pm.addControls(view.controlOptions);
return view;
}

this.obj.addControl(view.obj);
// Trigger the displayed event of the child view.
Expand Down
Loading
Loading