Skip to content

Commit 3bb58b2

Browse files
committed
Push selected features to anywidget
1 parent 9a522e5 commit 3bb58b2

File tree

9 files changed

+332
-112
lines changed

9 files changed

+332
-112
lines changed

notebooks/layers/Untitled.ipynb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@
161161
],
162162
"metadata": {
163163
"kernelspec": {
164-
"display_name": ".venv",
164+
"display_name": "Python 3 (ipykernel)",
165165
"language": "python",
166166
"name": "python3"
167167
},

notebooks/selected_features.ipynb

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "code",
5+
"execution_count": 1,
6+
"id": "50a578df-e913-4b3a-abc0-2780bcc9999b",
7+
"metadata": {},
8+
"outputs": [],
9+
"source": [
10+
"import openlayers.express as ox"
11+
]
12+
},
13+
{
14+
"cell_type": "code",
15+
"execution_count": 2,
16+
"id": "818ce264-61cb-4ccc-b74b-9314ccb36895",
17+
"metadata": {},
18+
"outputs": [],
19+
"source": [
20+
"url = \"https://openlayers.org/data/vector/us-states.json\""
21+
]
22+
},
23+
{
24+
"cell_type": "code",
25+
"execution_count": 3,
26+
"id": "647d23eb-0023-47ed-87ed-384ff1c4bab2",
27+
"metadata": {},
28+
"outputs": [],
29+
"source": [
30+
"m = ox.GeoJSONLayer(data=url, webgl=False).to_map()"
31+
]
32+
},
33+
{
34+
"cell_type": "code",
35+
"execution_count": 4,
36+
"id": "3e61cf8e-c4e9-4d28-90d2-a7e0e6c2749f",
37+
"metadata": {},
38+
"outputs": [
39+
{
40+
"data": {
41+
"application/vnd.jupyter.widget-view+json": {
42+
"model_id": "f263d2e8f4a441f686d56f9c35a024a6",
43+
"version_major": 2,
44+
"version_minor": 1
45+
},
46+
"text/plain": [
47+
"MapWidget(calls=[{'method': 'addLayer', 'args': ({'id': '9c642cc5c9', 'source': {'url': 'https://openlayers.or…"
48+
]
49+
},
50+
"execution_count": 4,
51+
"metadata": {},
52+
"output_type": "execute_result"
53+
}
54+
],
55+
"source": [
56+
"m"
57+
]
58+
},
59+
{
60+
"cell_type": "code",
61+
"execution_count": 5,
62+
"id": "36967ec6-bb85-4a64-8e2d-7776a89a23bf",
63+
"metadata": {},
64+
"outputs": [],
65+
"source": [
66+
"m.add_call(\"addSelectFeatures\")"
67+
]
68+
},
69+
{
70+
"cell_type": "code",
71+
"execution_count": 6,
72+
"id": "749ef4e0-c16b-4b5e-a925-026dc5a1349e",
73+
"metadata": {},
74+
"outputs": [],
75+
"source": [
76+
"import geopandas as gpd\n",
77+
"import shapely"
78+
]
79+
},
80+
{
81+
"cell_type": "code",
82+
"execution_count": 7,
83+
"id": "b8cc6d11-8dcb-4620-bdba-78b1c767af98",
84+
"metadata": {},
85+
"outputs": [
86+
{
87+
"data": {
88+
"text/plain": [
89+
"array([<POLYGON ((-97.229 49, -97.097 48.683, -97.163 48.546, -97.13 48.14, -97.053...>,\n",
90+
" <POLYGON ((-91.834 40.61, -91.73 40.615, -91.527 40.412, -91.418 40.38, -91....>,\n",
91+
" <POLYGON ((-83.903 38.769, -83.679 38.632, -83.52 38.704, -83.142 38.627, -8...>,\n",
92+
" <POLYGON ((-90.64 42.51, -88.789 42.494, -87.803 42.494, -87.836 42.302, -87...>,\n",
93+
" <POLYGON ((-107.92 41.004, -105.729 40.998, -104.053 41.004, -102.054 41.004...>],\n",
94+
" dtype=object)"
95+
]
96+
},
97+
"execution_count": 7,
98+
"metadata": {},
99+
"output_type": "execute_result"
100+
}
101+
],
102+
"source": [
103+
"features = shapely.from_geojson(m.features_selected)\n",
104+
"features"
105+
]
106+
},
107+
{
108+
"cell_type": "code",
109+
"execution_count": 19,
110+
"id": "766fe3b7-9ca4-4b05-94e7-7f7563419274",
111+
"metadata": {},
112+
"outputs": [
113+
{
114+
"data": {
115+
"text/html": [
116+
"<div>\n",
117+
"<style scoped>\n",
118+
" .dataframe tbody tr th:only-of-type {\n",
119+
" vertical-align: middle;\n",
120+
" }\n",
121+
"\n",
122+
" .dataframe tbody tr th {\n",
123+
" vertical-align: top;\n",
124+
" }\n",
125+
"\n",
126+
" .dataframe thead th {\n",
127+
" text-align: right;\n",
128+
" }\n",
129+
"</style>\n",
130+
"<table border=\"1\" class=\"dataframe\">\n",
131+
" <thead>\n",
132+
" <tr style=\"text-align: right;\">\n",
133+
" <th></th>\n",
134+
" <th>0</th>\n",
135+
" </tr>\n",
136+
" </thead>\n",
137+
" <tbody>\n",
138+
" <tr>\n",
139+
" <th>0</th>\n",
140+
" <td>POLYGON ((-107.421329 37.000263, -106.868158 3...</td>\n",
141+
" </tr>\n",
142+
" <tr>\n",
143+
" <th>1</th>\n",
144+
" <td>POLYGON ((-101.812942 36.501861, -100.000075 3...</td>\n",
145+
" </tr>\n",
146+
" </tbody>\n",
147+
"</table>\n",
148+
"</div>"
149+
],
150+
"text/plain": [
151+
" 0\n",
152+
"0 POLYGON ((-107.421329 37.000263, -106.868158 3...\n",
153+
"1 POLYGON ((-101.812942 36.501861, -100.000075 3..."
154+
]
155+
},
156+
"execution_count": 19,
157+
"metadata": {},
158+
"output_type": "execute_result"
159+
}
160+
],
161+
"source": [
162+
"gpd.GeoDataFrame(features)"
163+
]
164+
},
165+
{
166+
"cell_type": "code",
167+
"execution_count": 22,
168+
"id": "37c15337-ed98-49b4-b827-9b9e3b268972",
169+
"metadata": {},
170+
"outputs": [],
171+
"source": [
172+
"# m.features_selected"
173+
]
174+
},
175+
{
176+
"cell_type": "code",
177+
"execution_count": null,
178+
"id": "ae248d94-c42e-4a67-a9ea-e5abfcee9679",
179+
"metadata": {},
180+
"outputs": [],
181+
"source": []
182+
}
183+
],
184+
"metadata": {
185+
"kernelspec": {
186+
"display_name": "Python 3 (ipykernel)",
187+
"language": "python",
188+
"name": "python3"
189+
},
190+
"language_info": {
191+
"codemirror_mode": {
192+
"name": "ipython",
193+
"version": 3
194+
},
195+
"file_extension": ".py",
196+
"mimetype": "text/x-python",
197+
"name": "python",
198+
"nbconvert_exporter": "python",
199+
"pygments_lexer": "ipython3",
200+
"version": "3.11.11"
201+
}
202+
},
203+
"nbformat": 4,
204+
"nbformat_minor": 5
205+
}

src/openlayers/anywidget.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ class MapWidget(Map, AnyWidget):
2828
# clicked = traitlets.Dict().tag(sync=True)
2929
view_state = traitlets.Dict().tag(sync=True)
3030
metadata = traitlets.Dict().tag(sync=True)
31+
features_selected = traitlets.List().tag(sync=True)
3132

3233
def __init__(
3334
self,

src/openlayers/express.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from .models.sources import VectorSource, GeoTIFFSource
77
from .models.view import View
88
from .styles import FlatStyle, default_style
9+
from .anywidget import MapWidget
910

1011

1112
class GeoTIFFTileLayer(LayerLike):
@@ -66,7 +67,7 @@ def model(self) -> WebGLVectorLayer | VectorLayer:
6667

6768
def to_map(
6869
self, lon: float = 0, lat: float = 0, zoom: float | int = 0, **kwargs
69-
) -> Map:
70+
) -> Map | MapWidget:
7071
"""Initialize a new `Map` instance and add the layer to it
7172
7273
Args:
@@ -75,7 +76,7 @@ def to_map(
7576
zoom (float | int): The initial zoom level of the map
7677
**kwargs (Any): Arguments that are handed over to the `Map` instance
7778
"""
78-
m = Map(View(center=(lon, lat), zoom=zoom), **kwargs)
79+
m = MapWidget(View(center=(lon, lat), zoom=zoom), **kwargs)
7980
m.add_layer(self)
8081
return m
8182

src/openlayers/js/openlayers.anywidget.js

Lines changed: 53 additions & 53 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/openlayers/js/openlayers.standalone.js

Lines changed: 39 additions & 39 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

srcjs/ipywidget-ts/map.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ const GEOJSON_IDENTIFIER = "@@geojson";
3030

3131
const jsonConverter = new JSONConverter();
3232

33-
// ...
33+
// --- Use [lon, lat] coordinates as input
3434
useGeographic();
3535

3636
// --- Helpers
@@ -241,6 +241,6 @@ export default class MapWidget {
241241
}
242242

243243
addSelectFeatures(): void {
244-
addSelectFeaturesToMap(this._map);
244+
addSelectFeaturesToMap(this._map, this._model);
245245
}
246246
}

srcjs/ipywidget-ts/select-features.ts

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
1-
import { Map } from "ol";
2-
import { FeatureLike } from "ol/Feature";
3-
import Feature from "ol/Feature";
4-
import Style, { StyleLike } from "ol/style/Style";
1+
import type { AnyModel } from "@anywidget/types";
2+
import type { Map } from "ol";
3+
import type Feature from "ol/Feature";
4+
5+
import Style from "ol/style/Style";
56
import Fill from 'ol/style/Fill.js';
67
import Stroke from 'ol/style/Stroke.js';
78
import VectorLayer from "ol/layer/Vector";
89

10+
11+
import { featureToGeoJSON } from "./utils";
12+
913
// TODO: Should be a parameter
1014
const highlightStyle = new Style({
1115
fill: new Fill({
@@ -19,34 +23,37 @@ const highlightStyle = new Style({
1923
})
2024
});
2125

22-
function addSelectFeaturesToMap(map: Map): void {
26+
// TODO: Setting new style only works for 'VectorLayer'
27+
// For 'WebGLVectorLayer' we need to add complete highlight-layer on topt of the current one
28+
function addSelectFeaturesToMap(map: Map, model?: AnyModel): void {
2329
const selected = [] as Feature[];
2430

2531
map.on('singleclick', function (e) {
2632
map.forEachFeatureAtPixel(e.pixel, function (feature, layer) {
2733
const isVectorLayer = layer instanceof VectorLayer;
28-
console.log("isVectorLayer", isVectorLayer);
34+
35+
// console.log("isVectorLayer", isVectorLayer);
2936
const f = feature as Feature;
3037
const selIndex = selected.indexOf(f);
3138
if (selIndex < 0) {
3239
console.log("push");
3340
selected.push(f);
34-
35-
// Does not work for WebGLVectorLayer
3641
if (isVectorLayer)
3742
f.setStyle(highlightStyle);
3843
} else {
3944
console.log("delete");
4045
selected.splice(selIndex, 1);
41-
42-
// Does not work for WebGLVectorLayer
4346
if (isVectorLayer)
4447
f.setStyle();
4548
}
4649
});
47-
48-
const output = selected.map(f => { return f.getProperties(); });
49-
console.log(output);
50+
const output = selected.map(f => featureToGeoJSON(f));
51+
// console.log("model", model);
52+
if (model) {
53+
model.set("features_selected", output);
54+
model.save_changes();
55+
} else
56+
console.log(output);
5057
});
5158
}
5259

srcjs/ipywidget-ts/utils.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
import type { MapBrowserEvent } from "ol";
1+
import type { Feature, MapBrowserEvent } from "ol";
22
import type { FeatureLike } from "ol/Feature";
33
import type { FeatureProps } from ".";
44
import type { View } from "ol";
55

6+
import { GeoJSON } from "ol/format";
7+
68
// import { toLonLat } from "ol/proj";
79

810
// TODO: get 'info' from 'parseView'
@@ -33,4 +35,8 @@ function getFeatureProperties(feature: FeatureLike): FeatureProps {
3335
return props;
3436
}
3537

36-
export { parseClickEvent, getFeatureProperties, parseView }
38+
function featureToGeoJSON(feature: Feature): any {
39+
return new GeoJSON().writeFeature(feature);
40+
}
41+
42+
export { parseClickEvent, getFeatureProperties, parseView, featureToGeoJSON }

0 commit comments

Comments
 (0)