geoai-py 0.10.0__py2.py3-none-any.whl → 0.11.1__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- geoai/__init__.py +2 -1
- geoai/dinov3.py +1146 -0
- geoai/geoai.py +11 -0
- geoai/map_widgets.py +174 -0
- geoai/train.py +8 -2
- geoai/utils.py +13 -1
- {geoai_py-0.10.0.dist-info → geoai_py-0.11.1.dist-info}/METADATA +1 -1
- geoai_py-0.11.1.dist-info/RECORD +21 -0
- geoai_py-0.10.0.dist-info/RECORD +0 -19
- {geoai_py-0.10.0.dist-info → geoai_py-0.11.1.dist-info}/WHEEL +0 -0
- {geoai_py-0.10.0.dist-info → geoai_py-0.11.1.dist-info}/entry_points.txt +0 -0
- {geoai_py-0.10.0.dist-info → geoai_py-0.11.1.dist-info}/licenses/LICENSE +0 -0
- {geoai_py-0.10.0.dist-info → geoai_py-0.11.1.dist-info}/top_level.txt +0 -0
geoai/geoai.py
CHANGED
@@ -41,6 +41,7 @@ from .train import (
|
|
41
41
|
train_segmentation_model,
|
42
42
|
)
|
43
43
|
from .utils import *
|
44
|
+
from .map_widgets import DINOv3GUI
|
44
45
|
|
45
46
|
|
46
47
|
class Map(leafmap.Map):
|
@@ -50,6 +51,16 @@ class Map(leafmap.Map):
|
|
50
51
|
"""Initialize the Map class."""
|
51
52
|
super().__init__(*args, **kwargs)
|
52
53
|
|
54
|
+
def add_dinov3_gui(
|
55
|
+
self,
|
56
|
+
raster: str,
|
57
|
+
processor: "DINOv3GeoProcessor",
|
58
|
+
features: torch.Tensor,
|
59
|
+
**kwargs: Any,
|
60
|
+
) -> None:
|
61
|
+
"""Add a DINOv3 GUI to the map."""
|
62
|
+
return DINOv3GUI(raster, processor, features, host_map=self, **kwargs)
|
63
|
+
|
53
64
|
|
54
65
|
class MapLibre(maplibregl.Map):
|
55
66
|
"""A subclass of maplibregl.Map for GeoAI applications."""
|
geoai/map_widgets.py
ADDED
@@ -0,0 +1,174 @@
|
|
1
|
+
"""Interactive widget for GeoAI."""
|
2
|
+
|
3
|
+
import ipywidgets as widgets
|
4
|
+
from .utils import dict_to_image, dict_to_rioxarray
|
5
|
+
|
6
|
+
|
7
|
+
class DINOv3GUI(widgets.VBox):
|
8
|
+
"""Interactive widget for DINOv3."""
|
9
|
+
|
10
|
+
def __init__(
|
11
|
+
self,
|
12
|
+
raster: str,
|
13
|
+
processor=None,
|
14
|
+
features=None,
|
15
|
+
host_map=None,
|
16
|
+
position="topright",
|
17
|
+
colormap_options=None,
|
18
|
+
raster_args=None,
|
19
|
+
):
|
20
|
+
"""Initialize the DINOv3 GUI.
|
21
|
+
|
22
|
+
Args:
|
23
|
+
raster (str): The path to the raster image.
|
24
|
+
processor (DINOv3GeoProcessor): The DINOv3 processor.
|
25
|
+
features (torch.Tensor): The features of the raster image.
|
26
|
+
host_map (Map): The host map.
|
27
|
+
position (str): The position of the widget.
|
28
|
+
colormap_options (list): The colormap options.
|
29
|
+
raster_args (dict): The raster arguments.
|
30
|
+
|
31
|
+
Returns:
|
32
|
+
None
|
33
|
+
|
34
|
+
Example:
|
35
|
+
>>> processor = DINOv3GeoProcessor()
|
36
|
+
>>> features, h_patches, w_patches = processor.extract_features(raster)
|
37
|
+
>>> gui = DINOv3GUI(raster, processor, features, host_map=m)
|
38
|
+
"""
|
39
|
+
if raster_args is None:
|
40
|
+
raster_args = {}
|
41
|
+
|
42
|
+
if "layer_name" not in raster_args:
|
43
|
+
raster_args["layer_name"] = "Raster"
|
44
|
+
|
45
|
+
if colormap_options is None:
|
46
|
+
colormap_options = [
|
47
|
+
"jet",
|
48
|
+
"viridis",
|
49
|
+
"plasma",
|
50
|
+
"inferno",
|
51
|
+
"magma",
|
52
|
+
"cividis",
|
53
|
+
]
|
54
|
+
|
55
|
+
main_widget = widgets.VBox(layout=widgets.Layout(width="230px"))
|
56
|
+
style = {"description_width": "initial"}
|
57
|
+
layout = widgets.Layout(width="95%", padding="0px 5px 0px 5px")
|
58
|
+
|
59
|
+
interpolation_checkbox = widgets.Checkbox(
|
60
|
+
value=True,
|
61
|
+
description="Use interpolation",
|
62
|
+
style=style,
|
63
|
+
layout=layout,
|
64
|
+
)
|
65
|
+
|
66
|
+
threshold_slider = widgets.FloatSlider(
|
67
|
+
value=0.7,
|
68
|
+
min=0,
|
69
|
+
max=1,
|
70
|
+
step=0.01,
|
71
|
+
description="Threshold",
|
72
|
+
style=style,
|
73
|
+
layout=layout,
|
74
|
+
)
|
75
|
+
|
76
|
+
opacity_slider = widgets.FloatSlider(
|
77
|
+
value=0.5,
|
78
|
+
min=0,
|
79
|
+
max=1,
|
80
|
+
step=0.01,
|
81
|
+
description="Opacity",
|
82
|
+
style=style,
|
83
|
+
layout=layout,
|
84
|
+
)
|
85
|
+
colormap_dropdown = widgets.Dropdown(
|
86
|
+
options=colormap_options,
|
87
|
+
value="jet",
|
88
|
+
description="Colormap",
|
89
|
+
style=style,
|
90
|
+
layout=layout,
|
91
|
+
)
|
92
|
+
layer_name_input = widgets.Text(
|
93
|
+
value="Similarity",
|
94
|
+
description="Layer name",
|
95
|
+
style=style,
|
96
|
+
layout=layout,
|
97
|
+
)
|
98
|
+
|
99
|
+
save_button = widgets.Button(
|
100
|
+
description="Save",
|
101
|
+
)
|
102
|
+
|
103
|
+
reset_button = widgets.Button(
|
104
|
+
description="Reset",
|
105
|
+
)
|
106
|
+
|
107
|
+
output = widgets.Output()
|
108
|
+
|
109
|
+
main_widget.children = [
|
110
|
+
interpolation_checkbox,
|
111
|
+
threshold_slider,
|
112
|
+
opacity_slider,
|
113
|
+
colormap_dropdown,
|
114
|
+
layer_name_input,
|
115
|
+
widgets.HBox([save_button, reset_button]),
|
116
|
+
output,
|
117
|
+
]
|
118
|
+
|
119
|
+
if host_map is not None:
|
120
|
+
|
121
|
+
host_map.add_widget(main_widget, add_header=True, position=position)
|
122
|
+
|
123
|
+
if raster is not None:
|
124
|
+
host_map.add_raster(raster, **raster_args)
|
125
|
+
|
126
|
+
def handle_map_interaction(**kwargs):
|
127
|
+
try:
|
128
|
+
if kwargs.get("type") == "click":
|
129
|
+
latlon = kwargs.get("coordinates")
|
130
|
+
with output:
|
131
|
+
output.clear_output()
|
132
|
+
|
133
|
+
results = processor.compute_similarity(
|
134
|
+
source=raster,
|
135
|
+
features=features,
|
136
|
+
query_coords=latlon[::-1],
|
137
|
+
output_dir="dinov3_results",
|
138
|
+
use_interpolation=interpolation_checkbox.value,
|
139
|
+
coord_crs="EPSG:4326",
|
140
|
+
)
|
141
|
+
array = results["image_dict"]["image"]
|
142
|
+
binary_array = array > threshold_slider.value
|
143
|
+
image = dict_to_image(results["image_dict"])
|
144
|
+
binary_image = dict_to_image(
|
145
|
+
{
|
146
|
+
"image": binary_array,
|
147
|
+
"crs": results["image_dict"]["crs"],
|
148
|
+
"bounds": results["image_dict"]["bounds"],
|
149
|
+
}
|
150
|
+
)
|
151
|
+
host_map.add_raster(
|
152
|
+
image,
|
153
|
+
colormap=colormap_dropdown.value,
|
154
|
+
opacity=opacity_slider.value,
|
155
|
+
layer_name=layer_name_input.value,
|
156
|
+
zoom_to_layer=False,
|
157
|
+
overwrite=True,
|
158
|
+
)
|
159
|
+
host_map.add_raster(
|
160
|
+
binary_image,
|
161
|
+
colormap="jet",
|
162
|
+
nodata=0,
|
163
|
+
opacity=opacity_slider.value,
|
164
|
+
layer_name="Foreground",
|
165
|
+
zoom_to_layer=False,
|
166
|
+
overwrite=True,
|
167
|
+
visible=False,
|
168
|
+
)
|
169
|
+
except Exception as e:
|
170
|
+
with output:
|
171
|
+
print(e)
|
172
|
+
|
173
|
+
host_map.on_interaction(handle_map_interaction)
|
174
|
+
host_map.default_style = {"cursor": "crosshair"}
|
geoai/train.py
CHANGED
@@ -3163,11 +3163,17 @@ def semantic_segmentation(
|
|
3163
3163
|
|
3164
3164
|
# Detect file format based on extension
|
3165
3165
|
input_ext = os.path.splitext(input_path)[1].lower()
|
3166
|
-
is_geotiff = input_ext in [".tif", ".tiff"]
|
3166
|
+
is_geotiff = input_ext in [".tif", ".tiff", ".jp2", ".img"]
|
3167
|
+
formats = {
|
3168
|
+
".tif": "GeoTIFF",
|
3169
|
+
".tiff": "GeoTIFF",
|
3170
|
+
".jp2": "JP2OpenJPEG",
|
3171
|
+
".img": "IMG",
|
3172
|
+
}
|
3167
3173
|
|
3168
3174
|
if not quiet:
|
3169
3175
|
print(
|
3170
|
-
f"Input file format: {
|
3176
|
+
f"Input file format: {formats[input_ext] if is_geotiff else 'Regular image'} ({input_ext})"
|
3171
3177
|
)
|
3172
3178
|
|
3173
3179
|
# Load model
|
geoai/utils.py
CHANGED
@@ -64,7 +64,7 @@ def view_raster(
|
|
64
64
|
client_args: Optional[Dict] = {"cors_all": False},
|
65
65
|
basemap: Optional[str] = "OpenStreetMap",
|
66
66
|
basemap_args: Optional[Dict] = None,
|
67
|
-
backend: Optional[str] = "
|
67
|
+
backend: Optional[str] = "ipyleaflet",
|
68
68
|
**kwargs: Any,
|
69
69
|
) -> Any:
|
70
70
|
"""
|
@@ -87,6 +87,7 @@ def view_raster(
|
|
87
87
|
client_args (Optional[Dict], optional): Additional arguments for the client. Defaults to {"cors_all": False}.
|
88
88
|
basemap (Optional[str], optional): The basemap to use. Defaults to "OpenStreetMap".
|
89
89
|
basemap_args (Optional[Dict], optional): Additional arguments for the basemap. Defaults to None.
|
90
|
+
backend (Optional[str], optional): The backend to use. Defaults to "ipyleaflet".
|
90
91
|
**kwargs (Any): Additional keyword arguments.
|
91
92
|
|
92
93
|
Returns:
|
@@ -396,12 +397,18 @@ def dict_to_rioxarray(data_dict: Dict) -> xr.DataArray:
|
|
396
397
|
"""
|
397
398
|
|
398
399
|
from affine import Affine
|
400
|
+
from collections import namedtuple
|
401
|
+
|
402
|
+
BoundingBox = namedtuple("BoundingBox", ["minx", "maxx", "miny", "maxy"])
|
399
403
|
|
400
404
|
# Extract components from the dictionary
|
401
405
|
crs = data_dict["crs"]
|
402
406
|
bounds = data_dict["bounds"]
|
403
407
|
image_tensor = data_dict["image"]
|
404
408
|
|
409
|
+
if hasattr(bounds, "left"):
|
410
|
+
bounds = BoundingBox(bounds.left, bounds.right, bounds.bottom, bounds.top)
|
411
|
+
|
405
412
|
# Convert tensor to numpy array if needed
|
406
413
|
if hasattr(image_tensor, "numpy"):
|
407
414
|
# For PyTorch tensors
|
@@ -2620,6 +2627,11 @@ def export_geotiff_tiles(
|
|
2620
2627
|
create_overview (bool): Whether to create an overview image of all tiles
|
2621
2628
|
skip_empty_tiles (bool): If True, skip tiles with no features
|
2622
2629
|
"""
|
2630
|
+
|
2631
|
+
import logging
|
2632
|
+
|
2633
|
+
logging.getLogger("rasterio").setLevel(logging.ERROR)
|
2634
|
+
|
2623
2635
|
# Create output directories
|
2624
2636
|
os.makedirs(out_folder, exist_ok=True)
|
2625
2637
|
image_dir = os.path.join(out_folder, "images")
|
@@ -0,0 +1,21 @@
|
|
1
|
+
geoai/__init__.py,sha256=bf-BQ8_LOIWMsCC_0MMLRSBoeskwphJeLXAqyAHtuG8,3851
|
2
|
+
geoai/change_detection.py,sha256=XkJjMEU1nD8uX3-nQy7NEmz8cukVeSaRxKJHlrv8xPM,59636
|
3
|
+
geoai/classify.py,sha256=0DcComVR6vKU4qWtH2oHVeXc7ZTcV0mFvdXRtlNmolo,35637
|
4
|
+
geoai/detectron2.py,sha256=dOOFM9M9-6PV8q2A4-mnIPrz7yTo-MpEvDiAW34nl0w,14610
|
5
|
+
geoai/dinov3.py,sha256=pr7yFyw9yXGT95QkGO3Cmu__22rMyZJr3lHIPiLQ83U,40114
|
6
|
+
geoai/download.py,sha256=B0EwpQFndJknOKmwRfEEnnCJhplOAwcLyNzFuA6FjZs,47633
|
7
|
+
geoai/extract.py,sha256=595MBcSaFx-gQLIEv5g3oEM90QA5In4L59GPVgBOlQc,122092
|
8
|
+
geoai/geoai.py,sha256=UK3HLsg3u0hJQfSQ29avWm6iKcKW35GmQ0f6jDp0ywg,10110
|
9
|
+
geoai/hf.py,sha256=HbfJfpO6XnANKhmFOBvpwULiC65TeMlnLNtyQHHmlKA,17248
|
10
|
+
geoai/map_widgets.py,sha256=8S0WCAeH8f1jswtBJHzV_lGaO92er8P58GxxotbKUng,5862
|
11
|
+
geoai/sam.py,sha256=O6S-kGiFn7YEcFbfWFItZZQOhnsm6-GlunxQLY0daEs,34345
|
12
|
+
geoai/segment.py,sha256=yBGTxA-ti8lBpk7WVaBOp6yP23HkaulKJQk88acrmZ0,43788
|
13
|
+
geoai/segmentation.py,sha256=7yEzBSKCyHW1dNssoK0rdvhxi2IXsIQIFSga817KdI4,11535
|
14
|
+
geoai/train.py,sha256=r9eioaBpc2eg6hckkGVI3aGhQZffKas_UVRj-AWruu8,136049
|
15
|
+
geoai/utils.py,sha256=Vg8vNuIZ2PvmwPGAIZHN_1dHw4jC9ddjM_GmdkFN1KA,300899
|
16
|
+
geoai_py-0.11.1.dist-info/licenses/LICENSE,sha256=vN2L5U7cZ6ZkOHFmc8WiGlsogWsZc5dllMeNxnKVOZg,1070
|
17
|
+
geoai_py-0.11.1.dist-info/METADATA,sha256=7RYVHCX_BzOQXPiXg9hsejDQM4lUQaLKvO-22xLq0ow,6764
|
18
|
+
geoai_py-0.11.1.dist-info/WHEEL,sha256=JNWh1Fm1UdwIQV075glCn4MVuCRs0sotJIq-J6rbxCU,109
|
19
|
+
geoai_py-0.11.1.dist-info/entry_points.txt,sha256=uGp3Az3HURIsRHP9v-ys0hIbUuBBNUfXv6VbYHIXeg4,41
|
20
|
+
geoai_py-0.11.1.dist-info/top_level.txt,sha256=1YkCUWu-ii-0qIex7kbwAvfei-gos9ycyDyUCJPNWHY,6
|
21
|
+
geoai_py-0.11.1.dist-info/RECORD,,
|
geoai_py-0.10.0.dist-info/RECORD
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
geoai/__init__.py,sha256=NaZb3vKclD1g50Cp45BvKY-dLnYC8ilz1J6wyRAgPY4,3766
|
2
|
-
geoai/change_detection.py,sha256=XkJjMEU1nD8uX3-nQy7NEmz8cukVeSaRxKJHlrv8xPM,59636
|
3
|
-
geoai/classify.py,sha256=0DcComVR6vKU4qWtH2oHVeXc7ZTcV0mFvdXRtlNmolo,35637
|
4
|
-
geoai/detectron2.py,sha256=dOOFM9M9-6PV8q2A4-mnIPrz7yTo-MpEvDiAW34nl0w,14610
|
5
|
-
geoai/download.py,sha256=B0EwpQFndJknOKmwRfEEnnCJhplOAwcLyNzFuA6FjZs,47633
|
6
|
-
geoai/extract.py,sha256=595MBcSaFx-gQLIEv5g3oEM90QA5In4L59GPVgBOlQc,122092
|
7
|
-
geoai/geoai.py,sha256=ZPr7hyJhOnwPO9c-nVJVaOUqMRZ77UpK95TFjKzDt0A,9782
|
8
|
-
geoai/hf.py,sha256=HbfJfpO6XnANKhmFOBvpwULiC65TeMlnLNtyQHHmlKA,17248
|
9
|
-
geoai/sam.py,sha256=O6S-kGiFn7YEcFbfWFItZZQOhnsm6-GlunxQLY0daEs,34345
|
10
|
-
geoai/segment.py,sha256=yBGTxA-ti8lBpk7WVaBOp6yP23HkaulKJQk88acrmZ0,43788
|
11
|
-
geoai/segmentation.py,sha256=7yEzBSKCyHW1dNssoK0rdvhxi2IXsIQIFSga817KdI4,11535
|
12
|
-
geoai/train.py,sha256=tf29H_1GojczQnBzJpNPYXYfo1dDT7W1EivMlpXiEjM,135893
|
13
|
-
geoai/utils.py,sha256=c171Gpk6WF5c2l4Hk9NYBGwSifGf-AuBKnA_SlnoTuA,300492
|
14
|
-
geoai_py-0.10.0.dist-info/licenses/LICENSE,sha256=vN2L5U7cZ6ZkOHFmc8WiGlsogWsZc5dllMeNxnKVOZg,1070
|
15
|
-
geoai_py-0.10.0.dist-info/METADATA,sha256=fwxfmIApQl0bmwZmVj-A8X0vvPfELJOCp0QllFfNxyE,6764
|
16
|
-
geoai_py-0.10.0.dist-info/WHEEL,sha256=JNWh1Fm1UdwIQV075glCn4MVuCRs0sotJIq-J6rbxCU,109
|
17
|
-
geoai_py-0.10.0.dist-info/entry_points.txt,sha256=uGp3Az3HURIsRHP9v-ys0hIbUuBBNUfXv6VbYHIXeg4,41
|
18
|
-
geoai_py-0.10.0.dist-info/top_level.txt,sha256=1YkCUWu-ii-0qIex7kbwAvfei-gos9ycyDyUCJPNWHY,6
|
19
|
-
geoai_py-0.10.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|