geoai-py 0.18.2__tar.gz → 0.20.0__tar.gz

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.
Files changed (80) hide show
  1. {geoai_py-0.18.2 → geoai_py-0.20.0}/.gitignore +1 -0
  2. {geoai_py-0.18.2 → geoai_py-0.20.0}/.pre-commit-config.yaml +2 -2
  3. {geoai_py-0.18.2 → geoai_py-0.20.0}/PKG-INFO +3 -1
  4. {geoai_py-0.18.2 → geoai_py-0.20.0}/geoai/__init__.py +23 -1
  5. {geoai_py-0.18.2 → geoai_py-0.20.0}/geoai/agents/__init__.py +1 -0
  6. {geoai_py-0.18.2 → geoai_py-0.20.0}/geoai/agents/geo_agents.py +74 -29
  7. {geoai_py-0.18.2 → geoai_py-0.20.0}/geoai/geoai.py +2 -0
  8. geoai_py-0.20.0/geoai/landcover_train.py +685 -0
  9. geoai_py-0.20.0/geoai/landcover_utils.py +383 -0
  10. geoai_py-0.20.0/geoai/map_widgets.py +730 -0
  11. geoai_py-0.20.0/geoai/moondream.py +990 -0
  12. {geoai_py-0.18.2 → geoai_py-0.20.0}/geoai/tools/__init__.py +11 -0
  13. geoai_py-0.20.0/geoai/tools/sr.py +194 -0
  14. {geoai_py-0.18.2 → geoai_py-0.20.0}/geoai/utils.py +329 -1881
  15. {geoai_py-0.18.2 → geoai_py-0.20.0}/geoai_py.egg-info/PKG-INFO +3 -1
  16. {geoai_py-0.18.2 → geoai_py-0.20.0}/geoai_py.egg-info/SOURCES.txt +21 -0
  17. {geoai_py-0.18.2 → geoai_py-0.20.0}/geoai_py.egg-info/requires.txt +3 -0
  18. {geoai_py-0.18.2 → geoai_py-0.20.0}/mkdocs.yml +4 -0
  19. {geoai_py-0.18.2 → geoai_py-0.20.0}/pyproject.toml +3 -2
  20. geoai_py-0.20.0/qgis_plugin/README.md +188 -0
  21. geoai_py-0.20.0/qgis_plugin/geoai_plugin/__init__.py +21 -0
  22. geoai_py-0.20.0/qgis_plugin/geoai_plugin/dialogs/__init__.py +13 -0
  23. geoai_py-0.20.0/qgis_plugin/geoai_plugin/dialogs/map_tools.py +172 -0
  24. geoai_py-0.20.0/qgis_plugin/geoai_plugin/dialogs/moondream.py +651 -0
  25. geoai_py-0.20.0/qgis_plugin/geoai_plugin/dialogs/samgeo.py +1438 -0
  26. geoai_py-0.20.0/qgis_plugin/geoai_plugin/dialogs/segmentation.py +1450 -0
  27. geoai_py-0.20.0/qgis_plugin/geoai_plugin/geoai_plugin.py +510 -0
  28. geoai_py-0.20.0/qgis_plugin/geoai_plugin/icons/about.svg +5 -0
  29. geoai_py-0.20.0/qgis_plugin/geoai_plugin/icons/gpu.svg +12 -0
  30. geoai_py-0.20.0/qgis_plugin/geoai_plugin/icons/icon.png +0 -0
  31. geoai_py-0.20.0/qgis_plugin/geoai_plugin/icons/moondream.svg +6 -0
  32. geoai_py-0.20.0/qgis_plugin/geoai_plugin/icons/samgeo.png +0 -0
  33. geoai_py-0.20.0/qgis_plugin/geoai_plugin/icons/segment.svg +6 -0
  34. geoai_py-0.20.0/qgis_plugin/geoai_plugin/metadata.txt +42 -0
  35. geoai_py-0.20.0/qgis_plugin/install.py +156 -0
  36. geoai_py-0.20.0/qgis_plugin/install.sh +42 -0
  37. geoai_py-0.18.2/geoai/map_widgets.py +0 -174
  38. {geoai_py-0.18.2 → geoai_py-0.20.0}/.dockerignore +0 -0
  39. {geoai_py-0.18.2 → geoai_py-0.20.0}/.editorconfig +0 -0
  40. {geoai_py-0.18.2 → geoai_py-0.20.0}/CITATION.cff +0 -0
  41. {geoai_py-0.18.2 → geoai_py-0.20.0}/Dockerfile +0 -0
  42. {geoai_py-0.18.2 → geoai_py-0.20.0}/LICENSE +0 -0
  43. {geoai_py-0.18.2 → geoai_py-0.20.0}/MANIFEST.in +0 -0
  44. {geoai_py-0.18.2 → geoai_py-0.20.0}/README.md +0 -0
  45. {geoai_py-0.18.2 → geoai_py-0.20.0}/geoai/agents/catalog_models.py +0 -0
  46. {geoai_py-0.18.2 → geoai_py-0.20.0}/geoai/agents/catalog_tools.py +0 -0
  47. {geoai_py-0.18.2 → geoai_py-0.20.0}/geoai/agents/map_tools.py +0 -0
  48. {geoai_py-0.18.2 → geoai_py-0.20.0}/geoai/agents/stac_models.py +0 -0
  49. {geoai_py-0.18.2 → geoai_py-0.20.0}/geoai/agents/stac_tools.py +0 -0
  50. {geoai_py-0.18.2 → geoai_py-0.20.0}/geoai/change_detection.py +0 -0
  51. {geoai_py-0.18.2 → geoai_py-0.20.0}/geoai/classify.py +0 -0
  52. {geoai_py-0.18.2 → geoai_py-0.20.0}/geoai/detectron2.py +0 -0
  53. {geoai_py-0.18.2 → geoai_py-0.20.0}/geoai/dinov3.py +0 -0
  54. {geoai_py-0.18.2 → geoai_py-0.20.0}/geoai/download.py +0 -0
  55. {geoai_py-0.18.2 → geoai_py-0.20.0}/geoai/extract.py +0 -0
  56. {geoai_py-0.18.2 → geoai_py-0.20.0}/geoai/hf.py +0 -0
  57. {geoai_py-0.18.2 → geoai_py-0.20.0}/geoai/sam.py +0 -0
  58. {geoai_py-0.18.2 → geoai_py-0.20.0}/geoai/segment.py +0 -0
  59. {geoai_py-0.18.2 → geoai_py-0.20.0}/geoai/segmentation.py +0 -0
  60. {geoai_py-0.18.2 → geoai_py-0.20.0}/geoai/timm_segment.py +0 -0
  61. {geoai_py-0.18.2 → geoai_py-0.20.0}/geoai/timm_train.py +0 -0
  62. {geoai_py-0.18.2 → geoai_py-0.20.0}/geoai/tools/cloudmask.py +0 -0
  63. {geoai_py-0.18.2 → geoai_py-0.20.0}/geoai/tools/multiclean.py +0 -0
  64. {geoai_py-0.18.2 → geoai_py-0.20.0}/geoai/train.py +0 -0
  65. {geoai_py-0.18.2 → geoai_py-0.20.0}/geoai_py.egg-info/dependency_links.txt +0 -0
  66. {geoai_py-0.18.2 → geoai_py-0.20.0}/geoai_py.egg-info/entry_points.txt +0 -0
  67. {geoai_py-0.18.2 → geoai_py-0.20.0}/geoai_py.egg-info/top_level.txt +0 -0
  68. {geoai_py-0.18.2 → geoai_py-0.20.0}/pytest.ini +0 -0
  69. {geoai_py-0.18.2 → geoai_py-0.20.0}/requirements.txt +0 -0
  70. {geoai_py-0.18.2 → geoai_py-0.20.0}/requirements_docs.txt +0 -0
  71. {geoai_py-0.18.2 → geoai_py-0.20.0}/setup.cfg +0 -0
  72. {geoai_py-0.18.2 → geoai_py-0.20.0}/tests/__init__.py +0 -0
  73. {geoai_py-0.18.2 → geoai_py-0.20.0}/tests/create_test_data.py +0 -0
  74. {geoai_py-0.18.2 → geoai_py-0.20.0}/tests/test_classify.py +0 -0
  75. {geoai_py-0.18.2 → geoai_py-0.20.0}/tests/test_download.py +0 -0
  76. {geoai_py-0.18.2 → geoai_py-0.20.0}/tests/test_extract.py +0 -0
  77. {geoai_py-0.18.2 → geoai_py-0.20.0}/tests/test_fixtures.py +0 -0
  78. {geoai_py-0.18.2 → geoai_py-0.20.0}/tests/test_geoai.py +0 -0
  79. {geoai_py-0.18.2 → geoai_py-0.20.0}/tests/test_segment.py +0 -0
  80. {geoai_py-0.18.2 → geoai_py-0.20.0}/tests/test_utils.py +0 -0
@@ -11,6 +11,7 @@ AGENTS.md
11
11
  docs/examples/timm_output/
12
12
  docs/examples/timm_seg_output/
13
13
  docs/examples/timm_buildings/
14
+ docs/workshops/**/*.txt
14
15
 
15
16
  # C extensions
16
17
  *.so
@@ -12,7 +12,7 @@ repos:
12
12
  args: ["--maxkb=500"]
13
13
 
14
14
  - repo: https://github.com/psf/black-pre-commit-mirror
15
- rev: 25.11.0
15
+ rev: 25.12.0
16
16
  hooks:
17
17
  - id: black-jupyter
18
18
 
@@ -27,6 +27,6 @@ repos:
27
27
  ]
28
28
 
29
29
  - repo: https://github.com/kynan/nbstripout
30
- rev: 0.8.1
30
+ rev: 0.8.2
31
31
  hooks:
32
32
  - id: nbstripout
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: geoai-py
3
- Version: 0.18.2
3
+ Version: 0.20.0
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
@@ -57,6 +57,8 @@ Requires-Dist: strands-agents-tools; extra == "agents"
57
57
  Requires-Dist: strands-agents[ollama]; extra == "agents"
58
58
  Requires-Dist: strands-agents[anthropic]; extra == "agents"
59
59
  Requires-Dist: strands-agents[openai]; extra == "agents"
60
+ Provides-Extra: sr
61
+ Requires-Dist: opensr-model; extra == "sr"
60
62
  Dynamic: license-file
61
63
 
62
64
  # GeoAI: Artificial Intelligence for Geospatial Data
@@ -2,7 +2,7 @@
2
2
 
3
3
  __author__ = """Qiusheng Wu"""
4
4
  __email__ = "giswqs@gmail.com"
5
- __version__ = "0.18.2"
5
+ __version__ = "0.20.0"
6
6
 
7
7
 
8
8
  import os
@@ -136,3 +136,25 @@ try:
136
136
  except ImportError:
137
137
  # MultiClean not available (missing dependency)
138
138
  pass
139
+
140
+ # Removed redundant cloud mask imports; these are already available via the tools subpackage.
141
+
142
+ try:
143
+ from .tools import super_resolution
144
+ except ImportError:
145
+ # super_resolution not available (missing dependency)
146
+ pass
147
+
148
+ # Moondream Vision Language Model
149
+ try:
150
+ from .moondream import (
151
+ MoondreamGeo,
152
+ moondream_caption,
153
+ moondream_query,
154
+ moondream_detect,
155
+ moondream_point,
156
+ )
157
+ from .map_widgets import moondream_gui
158
+ except ImportError:
159
+ # Moondream not available (missing dependency)
160
+ pass
@@ -6,6 +6,7 @@ from .geo_agents import (
6
6
  create_ollama_model,
7
7
  create_anthropic_model,
8
8
  create_openai_model,
9
+ create_gemini_model,
9
10
  create_bedrock_model,
10
11
  )
11
12
  from .map_tools import MapTools
@@ -17,6 +17,7 @@ from strands.models import BedrockModel
17
17
  from strands.models.anthropic import AnthropicModel
18
18
  from strands.models.ollama import OllamaModel as _OllamaModel
19
19
  from strands.models.openai import OpenAIModel
20
+ from strands.models.gemini import GeminiModel
20
21
 
21
22
  from .catalog_tools import CatalogTools
22
23
  from .map_tools import MapSession, MapTools
@@ -202,6 +203,40 @@ def create_bedrock_model(
202
203
  )
203
204
 
204
205
 
206
+ def create_gemini_model(
207
+ model_id: str = "gemini-2.5-flash",
208
+ api_key: str = None,
209
+ client_args: dict = None,
210
+ **kwargs: Any,
211
+ ) -> GeminiModel:
212
+ """Create a Gemini model.
213
+
214
+ Args:
215
+ model_id: Gemini model ID.
216
+ api_key: Gemini API key.
217
+ client_args: Client arguments for the Gemini model.
218
+ **kwargs: Additional keyword arguments for the Gemini model.
219
+
220
+ Returns:
221
+ GeminiModel: A Gemini model.
222
+ """
223
+
224
+ if api_key is None and (client_args is None or "api_key" not in client_args):
225
+ try:
226
+ api_key = os.getenv("GOOGLE_API_KEY", None)
227
+ if api_key is None:
228
+ raise ValueError("GOOGLE_API_KEY is not set")
229
+ except Exception:
230
+ raise ValueError("GOOGLE_API_KEY is not set")
231
+
232
+ if client_args is None:
233
+ client_args = kwargs.get("client_args", {})
234
+ if "api_key" not in client_args and api_key is not None:
235
+ client_args["api_key"] = api_key
236
+
237
+ return GeminiModel(client_args=client_args, model_id=model_id, **kwargs)
238
+
239
+
205
240
  class GeoAgent(Agent):
206
241
  """Geospatial AI agent with interactive mapping capabilities."""
207
242
 
@@ -229,15 +264,10 @@ class GeoAgent(Agent):
229
264
  model_args = {}
230
265
 
231
266
  # --- save a model factory we can call each turn ---
232
- if model == "llama3.1":
233
- self._model_factory: Callable[[], OllamaModel] = (
234
- lambda: create_ollama_model(
235
- host="http://localhost:11434", model_id=model, **model_args
236
- )
237
- )
238
- elif isinstance(model, str):
239
- self._model_factory: Callable[[], BedrockModel] = (
240
- lambda: create_bedrock_model(model_id=model, **model_args)
267
+ if isinstance(model, str) and (":" in model or model.startswith("llama")):
268
+ # treat ANY "llama..." model id as Ollama
269
+ self._model_factory = lambda m=model: create_ollama_model(
270
+ host="http://localhost:11434", model_id=m, **model_args
241
271
  )
242
272
  elif isinstance(model, OllamaModel):
243
273
  # Extract configuration from existing OllamaModel and create new instances
@@ -267,6 +297,20 @@ class GeoAgent(Agent):
267
297
  model_id=mid, client_args=client_args, **model_args
268
298
  )
269
299
  )
300
+ elif isinstance(model, GeminiModel):
301
+ # Extract configuration from existing GeminiModel and create new instances
302
+ model_id = model.config["model_id"]
303
+ client_args = model.client_args.copy()
304
+ self._model_factory: Callable[[], GeminiModel] = (
305
+ lambda mid=model_id, client_args=client_args: create_gemini_model(
306
+ model_id=mid, client_args=client_args, **model_args
307
+ )
308
+ )
309
+ elif isinstance(model, str):
310
+ # Only Bedrock IDs here, not LLaMA
311
+ self._model_factory = lambda m=model: create_bedrock_model(
312
+ model_id=m, **model_args
313
+ )
270
314
  else:
271
315
  raise ValueError(f"Invalid model: {model}")
272
316
 
@@ -473,7 +517,7 @@ class GeoAgent(Agent):
473
517
  f"<div style='margin:6px 0;padding:6px 8px;border-radius:8px;background:#f7f7f7;'><b>Agent</b>: {_esc(mm)}</div>"
474
518
  )
475
519
  self._ui.log.value = (
476
- "<div>"
520
+ "<div style='height:100%; overflow-y:auto;'>"
477
521
  + (
478
522
  "".join(parts)
479
523
  if parts
@@ -643,16 +687,12 @@ class STACAgent(Agent):
643
687
  model_args = {}
644
688
 
645
689
  # --- save a model factory we can call each turn ---
646
- if model == "llama3.1":
647
- self._model_factory: Callable[[], OllamaModel] = (
648
- lambda: create_ollama_model(
649
- host="http://localhost:11434", model_id=model, **model_args
650
- )
651
- )
652
- elif isinstance(model, str):
653
- self._model_factory: Callable[[], BedrockModel] = (
654
- lambda: create_bedrock_model(model_id=model, **model_args)
690
+ if isinstance(model, str) and (":" in model or model.startswith("llama")):
691
+ # treat ANY "llama..." model id as Ollama
692
+ self._model_factory = lambda m=model: create_ollama_model(
693
+ host="http://localhost:11434", model_id=m, **model_args
655
694
  )
695
+
656
696
  elif isinstance(model, OllamaModel):
657
697
  # Extract configuration from existing OllamaModel and create new instances
658
698
  model_id = model.config["model_id"]
@@ -681,6 +721,11 @@ class STACAgent(Agent):
681
721
  model_id=mid, client_args=client_args, **model_args
682
722
  )
683
723
  )
724
+ elif isinstance(model, str):
725
+ # Only Bedrock IDs here, not LLaMA
726
+ self._model_factory = lambda m=model: create_bedrock_model(
727
+ model_id=m, **model_args
728
+ )
684
729
  else:
685
730
  raise ValueError(f"Invalid model: {model}")
686
731
 
@@ -1092,7 +1137,7 @@ CRITICAL: Return ONLY JSON. NO explanatory text, NO made-up data."""
1092
1137
  f"<div style='margin:6px 0;padding:6px 8px;border-radius:8px;background:#f7f7f7;'><b>Agent</b>: {_esc(mm)}</div>"
1093
1138
  )
1094
1139
  self._ui.log.value = (
1095
- "<div>"
1140
+ "<div style='height:100%; overflow-y:auto;'>"
1096
1141
  + (
1097
1142
  "".join(parts)
1098
1143
  if parts
@@ -1284,15 +1329,10 @@ class CatalogAgent(Agent):
1284
1329
  model_args = {}
1285
1330
 
1286
1331
  # --- save a model factory we can call each turn ---
1287
- if model == "llama3.1":
1288
- self._model_factory: Callable[[], OllamaModel] = (
1289
- lambda: create_ollama_model(
1290
- host="http://localhost:11434", model_id=model, **model_args
1291
- )
1292
- )
1293
- elif isinstance(model, str):
1294
- self._model_factory: Callable[[], BedrockModel] = (
1295
- lambda: create_bedrock_model(model_id=model, **model_args)
1332
+ if isinstance(model, str) and (":" in model or model.startswith("llama")):
1333
+ # treat ANY "llama..." model id as Ollama
1334
+ self._model_factory = lambda m=model: create_ollama_model(
1335
+ host="http://localhost:11434", model_id=m, **model_args
1296
1336
  )
1297
1337
  elif isinstance(model, OllamaModel):
1298
1338
  # Extract configuration from existing OllamaModel and create new instances
@@ -1322,6 +1362,11 @@ class CatalogAgent(Agent):
1322
1362
  model_id=mid, client_args=client_args, **model_args
1323
1363
  )
1324
1364
  )
1365
+ elif isinstance(model, str):
1366
+ # Only Bedrock IDs here, not LLaMA
1367
+ self._model_factory = lambda m=model: create_bedrock_model(
1368
+ model_id=m, **model_args
1369
+ )
1325
1370
  else:
1326
1371
  raise ValueError(f"Invalid model: {model}")
1327
1372
 
@@ -44,6 +44,8 @@ from .train import (
44
44
  train_segmentation_model,
45
45
  )
46
46
  from .utils import *
47
+ from .landcover_utils import *
48
+ from .landcover_train import *
47
49
 
48
50
 
49
51
  class LeafMap(leafmap.Map):