-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' of github.com:geo-engine/geoengine-python into re…
…sultdescriptor-with-time
- Loading branch information
Showing
21 changed files
with
2,052 additions
and
71 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,201 @@ | ||
''' | ||
Labeling tools for smaller ML use cases. | ||
''' | ||
|
||
import os | ||
from typing import Tuple, Callable, Optional, Mapping, TypedDict, List | ||
import matplotlib.pyplot as plt | ||
from matplotlib.backend_bases import MouseButton, MouseEvent | ||
from matplotlib.backend_tools import Cursors | ||
from matplotlib.patches import Circle | ||
import ipywidgets as widgets | ||
from IPython.display import display | ||
import geopandas as gpd | ||
from shapely.geometry import Point | ||
import pandas as pd | ||
|
||
|
||
class ClassValue(TypedDict): | ||
value: int | ||
color: str | ||
|
||
|
||
class PointLabelingTool(widgets.VBox): | ||
''' | ||
Create points labels by overlaying them over a background. | ||
Select a class and click to add one. | ||
''' | ||
|
||
points: gpd.GeoDataFrame | ||
crs: str | ||
filename: str | ||
class_column: str | ||
selected_class_value: int | ||
classes: Mapping[str, ClassValue] | ||
color_map: Mapping[int, str] | ||
|
||
fig: plt.Figure | ||
ax: plt.Axes | ||
plt_fg: Optional[pd.plotting.PlotAccessor] | ||
legend_handles: List[Circle] | ||
|
||
def __init__(self, | ||
*, | ||
filename: str, | ||
class_column: str, | ||
classes: Mapping[str, ClassValue], | ||
crs: str, | ||
background: Callable[[plt.Axes], None], | ||
figsize: Optional[Tuple[int, int]] = None) -> None: | ||
super().__init__() | ||
|
||
self.filename = filename | ||
self.crs = crs | ||
|
||
self.class_column = class_column | ||
self.classes = classes | ||
|
||
self.color_map = {c['value']: c['color'] for c in self.classes.values()} | ||
self.selected_class_value = next(iter(self.classes.values()))['value'] | ||
|
||
self.points = self.__make_gdf(None) | ||
|
||
if os.path.exists(filename): | ||
self.points = pd.concat( | ||
[ | ||
self.points, | ||
gpd.read_file(filename).set_crs(self.crs, allow_override=True), | ||
], | ||
ignore_index=True, | ||
) | ||
|
||
self.children = [ | ||
self.__create_selection(background), | ||
self.__create_plot(background, figsize), | ||
] | ||
|
||
self.__plot_and_save(background) | ||
|
||
def __make_gdf(self, entry: Optional[Tuple[Point, int]]) -> gpd.GeoDataFrame: | ||
''' | ||
Create a GeoDataFrame from a given entry. | ||
If `entry` is None, an empty GeoDataFrame is created. | ||
''' | ||
|
||
data: dict[str, list] = {'geometry': [], self.class_column: []} | ||
|
||
if entry: | ||
geom, class_value = entry | ||
data['geometry'].append(geom) | ||
data[self.class_column].append(class_value) | ||
|
||
return gpd.GeoDataFrame( | ||
data=data, | ||
crs=self.crs, | ||
geometry='geometry', | ||
) | ||
|
||
def __create_plot(self, | ||
background: Callable[[plt.Axes], None], | ||
figsize: Optional[Tuple[int, int]]) -> widgets.Output: | ||
""" | ||
Creates a plot with a specified background and figure size, | ||
and sets up an interactive widget for labeling points. | ||
""" | ||
|
||
output = widgets.Output() | ||
|
||
with output, plt.ioff(): | ||
self.fig, self.ax = plt.subplots(figsize=figsize, constrained_layout=True) | ||
|
||
self.fig.canvas.toolbar_visible = False | ||
self.fig.canvas.header_visible = False | ||
self.fig.canvas.footer_visible = False | ||
|
||
display(self.fig.canvas) | ||
|
||
background(self.ax) | ||
|
||
def on_click(event: MouseEvent): | ||
if self.fig.canvas.widgetlock.locked(): | ||
return # Don't do anything if the zoom/pan tools have been enabled. | ||
if event.button is not MouseButton.LEFT: | ||
return | ||
if not event.inaxes: | ||
return | ||
|
||
self.points.loc[len(self.points)] = [ | ||
Point(event.xdata, event.ydata), | ||
self.selected_class_value, | ||
] | ||
self.points.set_geometry(col='geometry', inplace=True) | ||
|
||
self.__plot_and_save(background) | ||
|
||
self.fig.canvas.mpl_connect('button_press_event', on_click) | ||
|
||
self.legend_handles = [ | ||
Circle((0.5, 0.5), 1, label=name, facecolor=v['color']) for (name, v) in self.classes.items() | ||
] | ||
|
||
return output | ||
|
||
def __create_selection(self, background: Callable[[plt.Axes], None]) -> widgets.HBox: | ||
""" | ||
Creates a selection interface for labeling water bodies. | ||
This method generates a horizontal box layout containing toggle buttons for class selection | ||
and an undo button. The class buttons allow the user to select a class from the available | ||
options, while the undo button removes the last point added to the selection. | ||
""" | ||
|
||
class_buttons = widgets.ToggleButtons( | ||
options=[(c, v['value']) for (c, v) in self.classes.items()], | ||
description='Class:', | ||
button_style='info', | ||
) | ||
|
||
undo_button = widgets.Button( | ||
icon='undo', | ||
button_style="warning", | ||
) | ||
|
||
def on_undo(_event): | ||
self.points.drop(self.points.tail(1).index, inplace=True) | ||
|
||
self.__plot_and_save(background) | ||
|
||
undo_button.on_click(on_undo) | ||
|
||
def set_selected_class_value(change) -> None: | ||
self.selected_class_value = change['new'] | ||
|
||
class_buttons.observe(set_selected_class_value, names=["value"]) | ||
|
||
return widgets.HBox([class_buttons, undo_button]) | ||
|
||
def __plot_and_save(self, | ||
background: Callable[[plt.Axes], None]) -> None: | ||
""" | ||
Plots the points on the given background and saves the plot to a file. | ||
""" | ||
|
||
self.ax.clear() | ||
|
||
background(self.ax) | ||
|
||
if len(self.points) == 0: | ||
return | ||
|
||
colors = self.points[self.class_column].map(self.color_map) | ||
self.plt_fg = self.points.plot(ax=self.ax, c=colors) | ||
|
||
self.points.to_file(self.filename) | ||
|
||
self.ax.cursor_to_use = Cursors.POINTER | ||
|
||
self.ax.legend( | ||
handles=self.legend_handles, | ||
loc='center left', | ||
bbox_to_anchor=(1, 0.5), | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
{ | ||
"type": "FeatureCollection", | ||
"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::32632" } }, | ||
"features": [ | ||
{ "type": "Feature", "properties": { "water": 1.0 }, "geometry": { "type": "Point", "coordinates": [ 359492.047223033325281, 5649059.958522516302764 ] } }, | ||
{ "type": "Feature", "properties": { "water": 1.0 }, "geometry": { "type": "Point", "coordinates": [ 359436.58185506798327, 5648255.710687017999589 ] } }, | ||
{ "type": "Feature", "properties": { "water": 1.0 }, "geometry": { "type": "Point", "coordinates": [ 361045.077526063658297, 5639935.90549221355468 ] } }, | ||
{ "type": "Feature", "properties": { "water": 1.0 }, "geometry": { "type": "Point", "coordinates": [ 359381.116487102583051, 5640102.301596108824015 ] } }, | ||
{ "type": "Feature", "properties": { "water": 1.0 }, "geometry": { "type": "Point", "coordinates": [ 357966.749603985692374, 5641599.866531174629927 ] } }, | ||
{ "type": "Feature", "properties": { "water": 1.0 }, "geometry": { "type": "Point", "coordinates": [ 357328.89787238399731, 5643374.758306065574288 ] } }, | ||
{ "type": "Feature", "properties": { "water": 1.0 }, "geometry": { "type": "Point", "coordinates": [ 357134.769084505212959, 5644983.253977061249316 ] } }, | ||
{ "type": "Feature", "properties": { "water": 1.0 }, "geometry": { "type": "Point", "coordinates": [ 357134.769084505212959, 5645676.571076628752053 ] } }, | ||
{ "type": "Feature", "properties": { "water": 1.0 }, "geometry": { "type": "Point", "coordinates": [ 357606.224712210823782, 5646453.086228143423796 ] } }, | ||
{ "type": "Feature", "properties": { "water": 1.0 }, "geometry": { "type": "Point", "coordinates": [ 358382.739863725961186, 5647118.670643728226423 ] } }, | ||
{ "type": "Feature", "properties": { "water": 1.0 }, "geometry": { "type": "Point", "coordinates": [ 358992.858911345014349, 5647395.997483555227518 ] } }, | ||
{ "type": "Feature", "properties": { "water": 1.0 }, "geometry": { "type": "Point", "coordinates": [ 357467.561292297381442, 5642903.30267836060375 ] } }, | ||
{ "type": "Feature", "properties": { "water": 1.0 }, "geometry": { "type": "Point", "coordinates": [ 357245.699820435955189, 5643763.015881823375821 ] } }, | ||
{ "type": "Feature", "properties": { "water": 1.0 }, "geometry": { "type": "Point", "coordinates": [ 359076.056963293056469, 5649531.414150222204626 ] } }, | ||
{ "type": "Feature", "properties": { "water": 1.0 }, "geometry": { "type": "Point", "coordinates": [ 358770.997439483529888, 5649753.275622082874179 ] } }, | ||
{ "type": "Feature", "properties": { "water": 1.0 }, "geometry": { "type": "Point", "coordinates": [ 360213.097006583120674, 5639963.63817619625479 ] } }, | ||
{ "type": "Feature", "properties": { "water": 1.0 }, "geometry": { "type": "Point", "coordinates": [ 358465.937915674003307, 5640851.084063641726971 ] } }, | ||
{ "type": "Feature", "properties": { "water": 1.0 }, "geometry": { "type": "Point", "coordinates": [ 358650.151550401293207, 5648719.950428013689816 ] } }, | ||
{ "type": "Feature", "properties": { "water": 1.0 }, "geometry": { "type": "Point", "coordinates": [ 358511.599955826473888, 5648917.881277405656874 ] } }, | ||
{ "type": "Feature", "properties": { "water": 1.0 }, "geometry": { "type": "Point", "coordinates": [ 358326.864496393478476, 5649425.903790846467018 ] } }, | ||
{ "type": "Feature", "properties": { "water": 1.0 }, "geometry": { "type": "Point", "coordinates": [ 358128.933647000929341, 5649089.421346879564226 ] } }, | ||
{ "type": "Feature", "properties": { "water": 1.0 }, "geometry": { "type": "Point", "coordinates": [ 357858.428152831096668, 5649683.213895057328045 ] } }, | ||
{ "type": "Feature", "properties": { "water": 1.0 }, "geometry": { "type": "Point", "coordinates": [ 357614.313438580313232, 5649815.167794652283192 ] } }, | ||
{ "type": "Feature", "properties": { "water": 1.0 }, "geometry": { "type": "Point", "coordinates": [ 358148.726731940172613, 5649531.466910522431135 ] } }, | ||
{ "type": "Feature", "properties": { "water": 1.0 }, "geometry": { "type": "Point", "coordinates": [ 359488.058812829724047, 5648667.168868175707757 ] } }, | ||
{ "type": "Feature", "properties": { "water": 1.0 }, "geometry": { "type": "Point", "coordinates": [ 359138.380978902918287, 5647651.123841293156147 ] } }, | ||
{ "type": "Feature", "properties": { "water": 0.0 }, "geometry": { "type": "Point", "coordinates": [ 358905.963186536508147, 5648231.670999898575246 ] } }, | ||
{ "type": "Feature", "properties": { "water": 0.0 }, "geometry": { "type": "Point", "coordinates": [ 356869.274874848197214, 5647952.125545353628695 ] } }, | ||
{ "type": "Feature", "properties": { "water": 0.0 }, "geometry": { "type": "Point", "coordinates": [ 353501.417731991037726, 5647779.073597301729023 ] } }, | ||
{ "type": "Feature", "properties": { "water": 0.0 }, "geometry": { "type": "Point", "coordinates": [ 354632.911238484550267, 5648457.96970119792968 ] } }, | ||
{ "type": "Feature", "properties": { "water": 0.0 }, "geometry": { "type": "Point", "coordinates": [ 355365.05409562739078, 5649389.787883015349507 ] } }, | ||
{ "type": "Feature", "properties": { "water": 0.0 }, "geometry": { "type": "Point", "coordinates": [ 358866.028121601440944, 5646168.359311587177217 ] } }, | ||
{ "type": "Feature", "properties": { "water": 0.0 }, "geometry": { "type": "Point", "coordinates": [ 361421.872277445625514, 5646514.46320769097656 ] } }, | ||
{ "type": "Feature", "properties": { "water": 0.0 }, "geometry": { "type": "Point", "coordinates": [ 359771.222926796239335, 5642787.190480418503284 ] } }, | ||
{ "type": "Feature", "properties": { "water": 0.0 }, "geometry": { "type": "Point", "coordinates": [ 358067.326822900155094, 5645396.28138950932771 ] } }, | ||
{ "type": "Feature", "properties": { "water": 0.0 }, "geometry": { "type": "Point", "coordinates": [ 355404.989160562457982, 5645076.800870028324425 ] } }, | ||
{ "type": "Feature", "properties": { "water": 0.0 }, "geometry": { "type": "Point", "coordinates": [ 356682.911238484550267, 5645782.320350548252463 ] } }, | ||
{ "type": "Feature", "properties": { "water": 0.0 }, "geometry": { "type": "Point", "coordinates": [ 356043.950199523533229, 5647512.839831067249179 ] } }, | ||
{ "type": "Feature", "properties": { "water": 0.0 }, "geometry": { "type": "Point", "coordinates": [ 353914.080069653398823, 5646155.047623275779188 ] } }, | ||
{ "type": "Feature", "properties": { "water": 0.0 }, "geometry": { "type": "Point", "coordinates": [ 352316.677472250768915, 5645662.515155742876232 ] } }, | ||
{ "type": "Feature", "properties": { "water": 0.0 }, "geometry": { "type": "Point", "coordinates": [ 352662.781368354684673, 5646115.112558340653777 ] } }, | ||
{ "type": "Feature", "properties": { "water": 0.0 }, "geometry": { "type": "Point", "coordinates": [ 352369.924225497525185, 5647952.125545353628695 ] } }, | ||
{ "type": "Feature", "properties": { "water": 0.0 }, "geometry": { "type": "Point", "coordinates": [ 352263.430719004012644, 5649363.164506392553449 ] } }, | ||
{ "type": "Feature", "properties": { "water": 0.0 }, "geometry": { "type": "Point", "coordinates": [ 352103.69045926380204, 5640524.203467431478202 ] } }, | ||
{ "type": "Feature", "properties": { "water": 0.0 }, "geometry": { "type": "Point", "coordinates": [ 353754.339809913130011, 5641003.424246652051806 ] } }, | ||
{ "type": "Feature", "properties": { "water": 0.0 }, "geometry": { "type": "Point", "coordinates": [ 354872.521628094953485, 5640670.632038859650493 ] } }, | ||
{ "type": "Feature", "properties": { "water": 0.0 }, "geometry": { "type": "Point", "coordinates": [ 354979.015134588466026, 5641229.722947950474918 ] } }, | ||
{ "type": "Feature", "properties": { "water": 0.0 }, "geometry": { "type": "Point", "coordinates": [ 353967.326822900155094, 5642733.943727171979845 ] } }, | ||
{ "type": "Feature", "properties": { "water": 0.0 }, "geometry": { "type": "Point", "coordinates": [ 353661.157991731306538, 5645023.554116781800985 ] } }, | ||
{ "type": "Feature", "properties": { "water": 0.0 }, "geometry": { "type": "Point", "coordinates": [ 353315.05409562739078, 5643958.619051846675575 ] } }, | ||
{ "type": "Feature", "properties": { "water": 0.0 }, "geometry": { "type": "Point", "coordinates": [ 355751.093056666373741, 5643905.372298600152135 ] } }, | ||
{ "type": "Feature", "properties": { "water": 0.0 }, "geometry": { "type": "Point", "coordinates": [ 357215.378770952112973, 5640883.619051846675575 ] } }, | ||
{ "type": "Feature", "properties": { "water": 0.0 }, "geometry": { "type": "Point", "coordinates": [ 356416.677472250768915, 5641708.943727171979845 ] } }, | ||
{ "type": "Feature", "properties": { "water": 0.0 }, "geometry": { "type": "Point", "coordinates": [ 359678.041108614415862, 5644943.683986911550164 ] } }, | ||
{ "type": "Feature", "properties": { "water": 0.0 }, "geometry": { "type": "Point", "coordinates": [ 361155.638511211844161, 5644730.696973924525082 ] } }, | ||
{ "type": "Feature", "properties": { "water": 0.0 }, "geometry": { "type": "Point", "coordinates": [ 359039.080069653398823, 5643279.722947950474918 ] } } | ||
] | ||
} |
Oops, something went wrong.