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/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: {'GeoTIFF' if is_geotiff else 'Regular image'} ({input_ext})"
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] = "folium",
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")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: geoai-py
3
- Version: 0.10.0
3
+ Version: 0.11.1
4
4
  Summary: A Python package for using Artificial Intelligence (AI) with geospatial data
5
5
  Author-email: Qiusheng Wu <giswqs@gmail.com>
6
6
  License: MIT License
@@ -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,,
@@ -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,,