geomind-ai 1.0.9__py3-none-any.whl → 1.1.1__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.
geomind/__init__.py CHANGED
@@ -3,7 +3,7 @@ GeoMind - Geospatial AI Agent
3
3
 
4
4
  """
5
5
 
6
- __version__ = "1.0.9"
6
+ __version__ = "1.1.0"
7
7
  __author__ = "Harsh Shinde, Rajat Shinde"
8
8
 
9
9
  from .agent import GeoMindAgent
geomind/agent.py CHANGED
@@ -245,7 +245,7 @@ class GeoMindAgent:
245
245
  Initialize the GeoMind agent.
246
246
 
247
247
  Args:
248
- model: Model name (default: xiaomi/mimo-v2-flash:free)
248
+ model: Model name (default: nvidia/nemotron-3-nano-30b-a3b:free)
249
249
  api_key: OpenRouter API key (required).
250
250
  """
251
251
  self.provider = "openrouter"
@@ -263,7 +263,7 @@ class GeoMindAgent:
263
263
  "3. Create .env file with: OPENROUTER_API_KEY=YOUR_KEY"
264
264
  )
265
265
 
266
- print(f"🚀 GeoMind Agent initialized with {self.model_name}")
266
+ print(f"GeoMind Agent initialized with {self.model_name}")
267
267
 
268
268
  # Create OpenAI-compatible client
269
269
  self.client = OpenAI(base_url=self.base_url, api_key=self.api_key)
@@ -292,10 +292,19 @@ Key information:
292
292
  - Bands available: B01-B12 at 10m, 20m, or 60m resolution
293
293
  - Current date: {datetime.now().strftime('%Y-%m-%d')}
294
294
 
295
+ IMPORTANT - Zarr URL usage:
296
+ - STAC search results include both SR_10m (base URL) and individual band assets (B02_10m, B03_10m, B04_10m, B08_10m)
297
+ - EITHER type of URL works for create_rgb_composite and calculate_ndvi:
298
+ * SR_10m URL: Points to .../measurements/reflectance/r10m (contains all bands as subdirectories)
299
+ * Individual band URLs: Point directly to specific bands like .../r10m/b02
300
+ - Prefer using SR_10m URL as it's simpler and works for all bands
301
+ - The processing functions automatically handle the correct path structure
302
+
295
303
  When users ask for imagery:
296
304
  1. First use get_bbox_from_location or list_recent_imagery to search
297
- 2. Present the results clearly with key metadata
305
+ 2. Present the results clearly with key metadata (ID, date, cloud cover)
298
306
  3. Offer to create visualizations if data is found
307
+ 4. For visualizations, use the SR_10m asset URL from search results
299
308
 
300
309
  Always explain what you're doing and interpret results in a helpful way."""
301
310
 
@@ -312,7 +321,7 @@ Always explain what you're doing and interpret results in a helpful way."""
312
321
 
313
322
  def _execute_function(self, name: str, args: dict) -> dict:
314
323
  """Execute a function call and return the result."""
315
- print(f" 🔧 Executing: {name}({args})")
324
+ print(f" Executing: {name}({args})")
316
325
 
317
326
  if name not in TOOL_FUNCTIONS:
318
327
  return {"error": f"Unknown function: {name}"}
@@ -328,8 +337,8 @@ Always explain what you're doing and interpret results in a helpful way."""
328
337
  Send a message to the agent and get a response.
329
338
  """
330
339
  if verbose:
331
- print(f"\n💬 User: {message}")
332
- print("🤔 Processing...")
340
+ print(f"\nUser: {message}")
341
+ print("Processing...")
333
342
 
334
343
  # Add user message to history
335
344
  self.history.append({"role": "user", "content": message})
@@ -395,7 +404,7 @@ Always explain what you're doing and interpret results in a helpful way."""
395
404
  self.history.append({"role": "assistant", "content": final_text})
396
405
 
397
406
  if verbose:
398
- print(f"\n🌍 GeoMind: {final_text}")
407
+ print(f"\nGeoMind: {final_text}")
399
408
 
400
409
  return final_text
401
410
 
@@ -404,7 +413,7 @@ Always explain what you're doing and interpret results in a helpful way."""
404
413
  def reset(self):
405
414
  """Reset the chat session."""
406
415
  self.history = []
407
- print("🔄 Chat session reset")
416
+ print("Chat session reset")
408
417
 
409
418
 
410
419
  def main(model: Optional[str] = None):
@@ -412,7 +421,7 @@ def main(model: Optional[str] = None):
412
421
  import sys
413
422
 
414
423
  print("=" * 60)
415
- print("🌍 GeoMind - Geospatial AI Agent")
424
+ print("GeoMind - Geospatial AI Agent")
416
425
  print("=" * 60)
417
426
  print("Powered by OpenRouter | Sentinel-2 Imagery")
418
427
  print("Type 'quit' or 'exit' to end the session")
@@ -422,22 +431,22 @@ def main(model: Optional[str] = None):
422
431
  try:
423
432
  agent = GeoMindAgent(model=model)
424
433
  except ValueError as e:
425
- print(f"\n❌ Error: {e}")
434
+ print(f"\nError: {e}")
426
435
  sys.exit(1)
427
436
  except Exception as e:
428
- print(f"\n❌ Error: {e}")
437
+ print(f"\nError: {e}")
429
438
  print("\nPlease check your API key and internet connection.")
430
439
  sys.exit(1)
431
440
 
432
441
  while True:
433
442
  try:
434
- user_input = input("\n💬 You: ").strip()
443
+ user_input = input("\nYou: ").strip()
435
444
 
436
445
  if not user_input:
437
446
  continue
438
447
 
439
448
  if user_input.lower() in ["quit", "exit", "q"]:
440
- print("\n👋 Goodbye!")
449
+ print("\nGoodbye!")
441
450
  break
442
451
 
443
452
  if user_input.lower() == "reset":
@@ -447,10 +456,10 @@ def main(model: Optional[str] = None):
447
456
  agent.chat(user_input)
448
457
 
449
458
  except KeyboardInterrupt:
450
- print("\n\n👋 Goodbye!")
459
+ print("\n\nGoodbye!")
451
460
  break
452
461
  except Exception as e:
453
- print(f"\n❌ Error: {e}")
462
+ print(f"\nError: {e}")
454
463
 
455
464
 
456
465
  if __name__ == "__main__":
geomind/cli.py CHANGED
@@ -7,6 +7,10 @@ import os
7
7
  import argparse
8
8
  from pathlib import Path
9
9
  from typing import Optional
10
+ import subprocess
11
+ import platform
12
+ import threading
13
+ import time
10
14
 
11
15
  from .agent import GeoMindAgent
12
16
 
@@ -36,6 +40,130 @@ def save_api_key(api_key: str) -> bool:
36
40
  return False
37
41
 
38
42
 
43
+ def display_recent_images():
44
+ """Display recently created images if any exist."""
45
+ outputs_dir = Path("outputs")
46
+ if not outputs_dir.exists():
47
+ return
48
+
49
+ # Get recent image files (created in last few seconds)
50
+ import time
51
+ recent_threshold = time.time() - 30 # 30 seconds ago
52
+
53
+ recent_images = []
54
+ for ext in ['*.png', '*.jpg', '*.jpeg', '*.tiff']:
55
+ for img_file in outputs_dir.glob(ext):
56
+ if img_file.stat().st_mtime > recent_threshold:
57
+ recent_images.append(img_file)
58
+
59
+ if recent_images:
60
+ print("\n" + "="*60)
61
+ print("Generated Images:")
62
+ for img in recent_images:
63
+ print(f" • {img.name} ({img.stat().st_size // 1024}KB)")
64
+
65
+ # Try to open the most recent image
66
+ if recent_images:
67
+ latest_image = max(recent_images, key=lambda x: x.stat().st_mtime)
68
+ open_image_viewer(latest_image)
69
+ print("="*60)
70
+
71
+
72
+ def open_image_viewer(image_path: Path):
73
+ """Open image in default viewer."""
74
+ try:
75
+ system = platform.system()
76
+ if system == "Windows":
77
+ os.startfile(str(image_path))
78
+ elif system == "Darwin": # macOS
79
+ subprocess.run(["open", str(image_path)], check=False)
80
+ else: # Linux
81
+ subprocess.run(["xdg-open", str(image_path)], check=False)
82
+ print(f" -> Opened {image_path.name} in default viewer")
83
+ except Exception:
84
+ print(f" -> Saved to: {image_path}")
85
+
86
+
87
+ def format_response_box(title: str, content: str, color_code: str = "\033[94m") -> str:
88
+ """Format response in an attractive box."""
89
+ RESET = "\033[0m"
90
+ lines = content.split('\n')
91
+ max_width = max(len(line) for line in lines) if lines else 0
92
+ max_width = max(max_width, len(title) + 4)
93
+ width = min(max_width + 4, 80)
94
+
95
+ box = f"{color_code}"
96
+ box += "┌" + "─" * (width - 2) + "┐\n"
97
+ box += f"│ {title:<{width-4}} │\n"
98
+ box += "├" + "─" * (width - 2) + "┤\n"
99
+
100
+ for line in lines:
101
+ if line.strip():
102
+ box += f"│ {line:<{width-4}} │\n"
103
+ else:
104
+ box += f"│{' ' * (width-2)}│\n"
105
+
106
+ box += "└" + "─" * (width - 2) + "┘"
107
+ box += RESET
108
+ return box
109
+
110
+
111
+ class ThinkingIndicator:
112
+ """Claude Code style thinking animation."""
113
+
114
+ def __init__(self):
115
+ self.is_thinking = False
116
+ self.thread = None
117
+ self.frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']
118
+ self.thinking_messages = [
119
+ "Thinking",
120
+ "Analyzing satellite data",
121
+ "Processing request",
122
+ "Searching imagery"
123
+ ]
124
+
125
+ def start(self):
126
+ """Start the thinking animation."""
127
+ self.is_thinking = True
128
+ self.thread = threading.Thread(target=self._animate)
129
+ self.thread.daemon = True
130
+ self.thread.start()
131
+
132
+ def stop(self):
133
+ """Stop the thinking animation."""
134
+ self.is_thinking = False
135
+ if self.thread:
136
+ self.thread.join(timeout=0.1)
137
+ # Clear the line
138
+ print("\r" + " " * 60 + "\r", end="", flush=True)
139
+
140
+ def _animate(self):
141
+ """Run the thinking animation."""
142
+ frame_idx = 0
143
+ message_idx = 0
144
+ message_counter = 0
145
+
146
+ # Colors like Claude Code
147
+ DIM = '\033[2m'
148
+ RESET = '\033[0m'
149
+
150
+ while self.is_thinking:
151
+ spinner = self.frames[frame_idx % len(self.frames)]
152
+
153
+ # Cycle through thinking messages every 30 frames (3 seconds)
154
+ if message_counter % 30 == 0:
155
+ message_idx = (message_idx + 1) % len(self.thinking_messages)
156
+
157
+ message = self.thinking_messages[message_idx]
158
+
159
+ # Show thinking with shimmer effect like Claude Code
160
+ print(f"\r{DIM}{spinner} {message}...{RESET}", end="", flush=True)
161
+
162
+ time.sleep(0.1)
163
+ frame_idx += 1
164
+ message_counter += 1
165
+
166
+
39
167
  def main():
40
168
  """Main CLI entry point for the geomind package."""
41
169
  parser = argparse.ArgumentParser(
@@ -60,7 +188,7 @@ Examples:
60
188
 
61
189
  Environment Variables:
62
190
  OPENROUTER_API_KEY Your OpenRouter API key
63
- OPENROUTER_MODEL Model to use (default: xiaomi/mimo-v2-flash:free)
191
+ OPENROUTER_MODEL Model to use (default: nvidia/nemotron-3-nano-30b-a3b:free)
64
192
  OPENROUTER_API_URL API endpoint (default: https://openrouter.ai/api/v1)
65
193
  """,
66
194
  )
@@ -93,9 +221,9 @@ Environment Variables:
93
221
  if args.clear_key:
94
222
  if CONFIG_FILE.exists():
95
223
  CONFIG_FILE.unlink()
96
- print("Saved API key cleared.")
224
+ print("Saved API key cleared.")
97
225
  else:
98
- print("ℹ️ No saved API key found.")
226
+ print("No saved API key found.")
99
227
  sys.exit(0)
100
228
 
101
229
  if args.version:
@@ -112,7 +240,7 @@ Environment Variables:
112
240
 
113
241
  api_key = args.api_key or OPENROUTER_API_KEY or get_saved_api_key()
114
242
  if not api_key:
115
- print(" No API key found. Run 'geomind' first to set up.")
243
+ print("Error: No API key found. Run 'geomind' first to set up.")
116
244
  sys.exit(1)
117
245
  agent = GeoMindAgent(model=args.model, api_key=api_key)
118
246
  agent.chat(args.query)
@@ -120,27 +248,64 @@ Environment Variables:
120
248
  # Interactive mode
121
249
  run_interactive(model=args.model, api_key=args.api_key)
122
250
  except ValueError as e:
123
- print(f"\n❌ Error: {e}")
251
+ print(f"\nError: {e}")
124
252
  sys.exit(1)
125
253
  except KeyboardInterrupt:
126
- print("\n\n👋 Goodbye!")
254
+ print("\n\nGoodbye!")
127
255
  sys.exit(0)
128
256
  except Exception as e:
129
- print(f"\n❌ Unexpected error: {e}")
257
+ print(f"\nUnexpected error: {e}")
130
258
  sys.exit(1)
131
259
 
132
260
 
261
+ def print_banner():
262
+ from . import __version__
263
+
264
+ # ANSI color codes
265
+ BOLD = '\033[1m'
266
+ DIM = '\033[2m'
267
+ RESET = '\033[0m'
268
+
269
+ banner = f"""
270
+ ┌──────────────────────────────────────────────────────────────────────┐
271
+ │ {BOLD}>_ GeoMind{RESET} (v{__version__}) │
272
+ │ │
273
+ │ model: nvidia/nemotron-3-nano-30b-a3b:free │
274
+ │ docs: https://harshshinde0.github.io/GeoMind │
275
+ │ authors: Harsh Shinde, Rajat Shinde │
276
+ │ official: https://harshshinde0.github.io/GeoMind │
277
+ │ │
278
+ │ Type "?" for help, "quit" to exit. │
279
+ └──────────────────────────────────────────────────────────────────────┘
280
+ """
281
+ print(banner)
282
+ print()
283
+
284
+
285
+ def print_help():
286
+ """Print interactive session help."""
287
+ help_text = """
288
+ Interactive Commands:
289
+ help, ? Show this help
290
+ reset Reset conversation
291
+ exit, quit, q Exit GeoMind
292
+
293
+ Query Examples:
294
+ > Find recent Sentinel-2 imagery of Paris
295
+ > Show me NDVI data for the Amazon rainforest
296
+ > Search for images with less than 10% cloud cover in London
297
+ > Get satellite data for coordinates 40.7128, -74.0060
298
+
299
+ For CLI options, run: geomind --help
300
+ """
301
+ print(help_text)
302
+
303
+
133
304
  def run_interactive(model: Optional[str] = None, api_key: Optional[str] = None):
134
305
  """Run interactive CLI mode."""
135
306
  from . import __version__
136
307
 
137
- print("=" * 60)
138
- print("🌍 GeoMind - Geospatial AI Agent")
139
- print("=" * 60)
140
- print(f"Version: {__version__} | Authors: Harsh Shinde, Rajat Shinde")
141
- print("Type 'quit' or 'exit' to end the session")
142
- print("Type 'reset' to start a new conversation")
143
- print("Type 'geomind --help' for more options")
308
+ print_banner()
144
309
 
145
310
  # Check for API key in order: argument > env > saved file
146
311
  from .config import OPENROUTER_API_KEY
@@ -155,44 +320,72 @@ def run_interactive(model: Optional[str] = None, api_key: Optional[str] = None):
155
320
  api_key = get_saved_api_key()
156
321
 
157
322
  if not api_key:
158
- print("\n🔑 OpenRouter API key required (FREE)")
323
+ print("\nOpenRouter API key required (FREE)")
159
324
  print(" Get yours at: https://openrouter.ai/settings/keys\n")
160
325
  api_key = input(" Enter your API key: ").strip()
161
326
 
162
327
  if not api_key:
163
- print("\n❌ No API key provided. Exiting.")
328
+ print("\nNo API key provided. Exiting.")
164
329
  return
165
330
 
166
331
  # Save the key for future use
167
332
  if save_api_key(api_key):
168
- print(" API key saved! You won't need to enter it again.\n")
333
+ print(" API key saved! You won't need to enter it again.\n")
169
334
  else:
170
- print(" ⚠️ Could not save API key. You'll need to enter it next time.\n")
335
+ print(" Warning: Could not save API key. You'll need to enter it next time.\n")
171
336
 
172
337
  agent = GeoMindAgent(model=model, api_key=api_key)
173
338
 
339
+ # Claude Code style color scheme
340
+ CYAN = '\033[96m'
341
+ DIM = '\033[2m'
342
+ BOLD = '\033[1m'
343
+ RESET = '\033[0m'
344
+
174
345
  while True:
175
346
  try:
176
- user_input = input("\n💬 You: ").strip()
347
+ # Simple prompt like Claude Code
348
+ user_input = input(f"\n{CYAN}>{RESET} ").strip()
177
349
 
178
350
  if not user_input:
179
351
  continue
180
352
 
181
353
  if user_input.lower() in ["quit", "exit", "q"]:
182
- print("\n👋 Goodbye!")
354
+ print(f"\n{DIM}Goodbye!{RESET}")
183
355
  break
184
356
 
185
357
  if user_input.lower() == "reset":
186
358
  agent.reset()
359
+ print(f"{DIM}Started new conversation{RESET}")
187
360
  continue
188
361
 
189
- agent.chat(user_input)
362
+ if user_input.lower() in ["help", "?"]:
363
+ print_help()
364
+ continue
365
+
366
+ # Start thinking animation
367
+ thinking = ThinkingIndicator()
368
+ thinking.start()
369
+
370
+ try:
371
+ # Get response from agent
372
+ response = agent.chat(user_input, verbose=False)
373
+
374
+ # Stop thinking animation
375
+ thinking.stop()
376
+
377
+ # Display response cleanly like Claude Code
378
+ print(f"\n{response}")
379
+
380
+ except Exception as chat_error:
381
+ thinking.stop()
382
+ raise chat_error
190
383
 
191
384
  except KeyboardInterrupt:
192
- print("\n\n👋 Goodbye!")
385
+ print(f"\n\n{DIM}Goodbye!{RESET}")
193
386
  break
194
387
  except Exception as e:
195
- print(f"\nError: {e}")
388
+ print(f"\n{DIM}Error: {e}{RESET}")
196
389
 
197
390
 
198
391
  if __name__ == "__main__":
geomind/config.py CHANGED
@@ -52,4 +52,4 @@ GEOCODER_USER_AGENT = "geomind_agent_v0.1"
52
52
  # Get your free API key at: https://openrouter.ai/settings/keys
53
53
  OPENROUTER_API_KEY = os.getenv("OPENROUTER_API_KEY", "")
54
54
  OPENROUTER_API_URL = os.getenv("OPENROUTER_API_URL", "https://openrouter.ai/api/v1")
55
- OPENROUTER_MODEL = os.getenv("OPENROUTER_MODEL", "xiaomi/mimo-v2-flash:free")
55
+ OPENROUTER_MODEL = os.getenv("OPENROUTER_MODEL", "nvidia/nemotron-3-nano-30b-a3b:free")
@@ -106,7 +106,7 @@ def create_rgb_composite(
106
106
  Uses B04 (Red), B03 (Green), B02 (Blue) bands.
107
107
 
108
108
  Args:
109
- zarr_url: URL to the SR_10m Zarr asset
109
+ zarr_url: URL to the SR_10m Zarr asset or individual band asset URL
110
110
  output_path: Optional path to save the image
111
111
  subset_size: Size to subset the image (for faster processing)
112
112
 
@@ -117,15 +117,23 @@ def create_rgb_composite(
117
117
  import xarray as xr
118
118
  import zarr
119
119
 
120
- # Open the Zarr store
121
- # The SR_10m asset contains b02, b03, b04, b08
122
- store = zarr.open(zarr_url, mode="r")
123
-
124
- # Read the bands
125
- # Note: Band names are lowercase in the Zarr structure
126
- red = np.array(store["b04"])
127
- green = np.array(store["b03"])
128
- blue = np.array(store["b02"])
120
+ # Determine if this is a band-specific URL or base SR_10m URL
121
+ # Band-specific URLs end with /b02, /b03, /b04, etc.
122
+ # Base SR_10m URLs end with /r10m
123
+ is_band_url = zarr_url.rstrip('/').split('/')[-1].startswith('b')
124
+
125
+ if is_band_url:
126
+ # Individual band URL provided - need to construct URLs for each band
127
+ base_url = '/'.join(zarr_url.rstrip('/').split('/')[:-1])
128
+ red = np.array(zarr.open(f"{base_url}/b04", mode="r"))
129
+ green = np.array(zarr.open(f"{base_url}/b03", mode="r"))
130
+ blue = np.array(zarr.open(f"{base_url}/b02", mode="r"))
131
+ else:
132
+ # Base SR_10m URL - bands are subdirectories
133
+ base_url = zarr_url.rstrip('/')
134
+ red = np.array(zarr.open(f"{base_url}/b04", mode="r"))
135
+ green = np.array(zarr.open(f"{base_url}/b03", mode="r"))
136
+ blue = np.array(zarr.open(f"{base_url}/b02", mode="r"))
129
137
 
130
138
  # Subset if requested (for faster processing)
131
139
  if subset_size and red.shape[0] > subset_size:
@@ -197,7 +205,7 @@ def calculate_ndvi(
197
205
  Uses B08 (NIR) and B04 (Red) bands.
198
206
 
199
207
  Args:
200
- zarr_url: URL to the SR_10m Zarr asset
208
+ zarr_url: URL to the SR_10m Zarr asset or individual band asset URL
201
209
  output_path: Optional path to save the NDVI image
202
210
  subset_size: Size to subset the image
203
211
 
@@ -208,12 +216,19 @@ def calculate_ndvi(
208
216
  import zarr
209
217
  from matplotlib.colors import LinearSegmentedColormap
210
218
 
211
- # Open the Zarr store
212
- store = zarr.open(zarr_url, mode="r")
213
-
214
- # Read the bands
215
- nir = np.array(store["b08"]) # NIR
216
- red = np.array(store["b04"]) # Red
219
+ # Determine if this is a band-specific URL or base SR_10m URL
220
+ is_band_url = zarr_url.rstrip('/').split('/')[-1].startswith('b')
221
+
222
+ if is_band_url:
223
+ # Individual band URL provided
224
+ base_url = '/'.join(zarr_url.rstrip('/').split('/')[:-1])
225
+ nir = np.array(zarr.open(f"{base_url}/b08", mode="r"))
226
+ red = np.array(zarr.open(f"{base_url}/b04", mode="r"))
227
+ else:
228
+ # Base SR_10m URL
229
+ base_url = zarr_url.rstrip('/')
230
+ nir = np.array(zarr.open(f"{base_url}/b08", mode="r"))
231
+ red = np.array(zarr.open(f"{base_url}/b04", mode="r"))
217
232
 
218
233
  # Subset if requested
219
234
  if subset_size and nir.shape[0] > subset_size:
@@ -25,6 +25,24 @@ def _format_item(item) -> dict:
25
25
  """Format a STAC item into a simplified dictionary."""
26
26
  props = item.properties
27
27
 
28
+ # Extract individual band assets for direct access
29
+ assets = {}
30
+ for key, asset in item.assets.items():
31
+ if key in ["SR_10m", "SR_20m", "SR_60m", "TCI_10m", "product"]:
32
+ assets[key] = {
33
+ "title": asset.title,
34
+ "href": asset.href,
35
+ "type": asset.media_type,
36
+ }
37
+ # Include individual 10m band assets for direct access
38
+ elif key in ["B02_10m", "B03_10m", "B04_10m", "B08_10m"]:
39
+ assets[key] = {
40
+ "title": asset.title,
41
+ "href": asset.href,
42
+ "type": asset.media_type,
43
+ "band": key.split("_")[0].lower(), # Extract band name (b02, b03, b04, b08)
44
+ }
45
+
28
46
  return {
29
47
  "id": item.id,
30
48
  "datetime": props.get("datetime"),
@@ -32,15 +50,7 @@ def _format_item(item) -> dict:
32
50
  "platform": props.get("platform"),
33
51
  "bbox": item.bbox,
34
52
  "geometry": item.geometry,
35
- "assets": {
36
- key: {
37
- "title": asset.title,
38
- "href": asset.href,
39
- "type": asset.media_type,
40
- }
41
- for key, asset in item.assets.items()
42
- if key in ["SR_10m", "SR_20m", "SR_60m", "TCI_10m", "product"]
43
- },
53
+ "assets": assets,
44
54
  "stac_url": f"{STAC_API_URL}/collections/{STAC_COLLECTION}/items/{item.id}",
45
55
  }
46
56
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: geomind-ai
3
- Version: 1.0.9
3
+ Version: 1.1.1
4
4
  Summary: AI agent for geospatial analysis with Sentinel-2 satellite imagery
5
5
  Author: Harsh Shinde, Rajat Shinde
6
6
  License-Expression: MIT
@@ -0,0 +1,14 @@
1
+ geomind/__init__.py,sha256=MZ0Zr2vGCJ816ilSApbwhA6iEfCwEBk40etbvIGfpqs,165
2
+ geomind/agent.py,sha256=-TF2VqOyTAJDrBPMbPKFoUwAHahBUUYOGtzqAKoNiUs,16226
3
+ geomind/cli.py,sha256=ipuMF0ZPseHHNKDD5VUMUG4fJDNmG_RQXNvAvT8Gbww,12085
4
+ geomind/config.py,sha256=7zPr0OKvK2SqQArUbjMvf5GVLQsmHcbn7e-BsSB1yv0,2030
5
+ geomind/tools/__init__.py,sha256=8iumGwIFHh8Bj1VJNgZtmKnEBqCy6_cRkzYENDUH7x4,720
6
+ geomind/tools/geocoding.py,sha256=hiLpzHpkJP6IgWAUtZMnHL6qpkWcYWVLpGe0yfYxXv8,3007
7
+ geomind/tools/processing.py,sha256=hb_Y9JV6o8XxS8_HI9DuW-hjzkv8S5ZdBpm45NpWwXQ,11287
8
+ geomind/tools/stac_search.py,sha256=3W9iZRObY6EkDn9K06Sp9ME-fYmubZRjPNDKDLEBUmM,6898
9
+ geomind_ai-1.1.1.dist-info/licenses/LICENSE,sha256=aveu0ERm7I3NnIu8rtpKdvd0eyRpmktXKU0PBABtSN0,1069
10
+ geomind_ai-1.1.1.dist-info/METADATA,sha256=3QKoizXhvfTGs6Bi73RdRFtx748Wzb7jGtbe39DObGw,2405
11
+ geomind_ai-1.1.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
12
+ geomind_ai-1.1.1.dist-info/entry_points.txt,sha256=2nPR3faYKl0-1epccvzMJ2xdi-Q1Vt7aOSvA84oIWnw,45
13
+ geomind_ai-1.1.1.dist-info/top_level.txt,sha256=rjKWNSNRhq4R9xJoZGsG-eAaH7BmTVNvfrrbcaJMIIs,8
14
+ geomind_ai-1.1.1.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.9.0)
2
+ Generator: setuptools (80.10.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,14 +0,0 @@
1
- geomind/__init__.py,sha256=Ts7erVUqshA1chdhAW-PSX82JK4klooPOe8PcJtkH-0,165
2
- geomind/agent.py,sha256=mc9Qnn247qXYKkPLTUPJWLF7cV-GedWuJq4jqNklCdU,15651
3
- geomind/cli.py,sha256=MJvXohvc4QJokdchZBy0BHVlOiimoXSdT74_caX5EVU,5604
4
- geomind/config.py,sha256=Zv2IlY8fJcxkv9KviLkyak3IvCSoCqEfi6BfftHcqFA,2020
5
- geomind/tools/__init__.py,sha256=8iumGwIFHh8Bj1VJNgZtmKnEBqCy6_cRkzYENDUH7x4,720
6
- geomind/tools/geocoding.py,sha256=hiLpzHpkJP6IgWAUtZMnHL6qpkWcYWVLpGe0yfYxXv8,3007
7
- geomind/tools/processing.py,sha256=mGu20uvpAVil2w2BEqj-0zYhKhCFuZHr1-3-vq0VzqM,10152
8
- geomind/tools/stac_search.py,sha256=V6230l4aHjedPWXu-3Cjmfc6diSFh5zsycewUko0W8k,6452
9
- geomind_ai-1.0.9.dist-info/licenses/LICENSE,sha256=aveu0ERm7I3NnIu8rtpKdvd0eyRpmktXKU0PBABtSN0,1069
10
- geomind_ai-1.0.9.dist-info/METADATA,sha256=K2Sh5I1SloXn0noO8zdj5h_m9CHg3uqzGtQ9qpfsC9Y,2405
11
- geomind_ai-1.0.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
12
- geomind_ai-1.0.9.dist-info/entry_points.txt,sha256=2nPR3faYKl0-1epccvzMJ2xdi-Q1Vt7aOSvA84oIWnw,45
13
- geomind_ai-1.0.9.dist-info/top_level.txt,sha256=rjKWNSNRhq4R9xJoZGsG-eAaH7BmTVNvfrrbcaJMIIs,8
14
- geomind_ai-1.0.9.dist-info/RECORD,,