geoai-py 0.3.6__py2.py3-none-any.whl → 0.4.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 CHANGED
@@ -2,34 +2,96 @@
2
2
 
3
3
  __author__ = """Qiusheng Wu"""
4
4
  __email__ = "giswqs@gmail.com"
5
- __version__ = "0.3.6"
5
+ __version__ = "0.4.1"
6
6
 
7
7
 
8
8
  import os
9
9
  import sys
10
10
 
11
11
 
12
- def set_proj_lib_path():
13
- """Set the PROJ_LIB environment variable based on the current conda environment."""
12
+ def set_proj_lib_path(verbose=False):
13
+ """
14
+ Set the PROJ_LIB and GDAL_DATA environment variables based on the current conda environment.
15
+
16
+ This function attempts to locate and set the correct paths for PROJ_LIB and GDAL_DATA
17
+ by checking multiple possible locations within the conda environment structure.
18
+
19
+ Args:
20
+ verbose (bool): If True, print additional information during the process.
21
+
22
+ Returns:
23
+ bool: True if both paths were set successfully, False otherwise.
24
+ """
14
25
  try:
15
26
  # Get conda environment path
16
27
  conda_env_path = os.environ.get("CONDA_PREFIX") or sys.prefix
17
28
 
29
+ # Define possible paths for PROJ_LIB
30
+ possible_proj_paths = [
31
+ os.path.join(conda_env_path, "share", "proj"),
32
+ os.path.join(conda_env_path, "Library", "share", "proj"),
33
+ os.path.join(conda_env_path, "Library", "share"),
34
+ ]
35
+
36
+ # Define possible paths for GDAL_DATA
37
+ possible_gdal_paths = [
38
+ os.path.join(conda_env_path, "share", "gdal"),
39
+ os.path.join(conda_env_path, "Library", "share", "gdal"),
40
+ os.path.join(conda_env_path, "Library", "data", "gdal"),
41
+ os.path.join(conda_env_path, "Library", "share"),
42
+ ]
43
+
18
44
  # Set PROJ_LIB environment variable
19
- proj_path = os.path.join(conda_env_path, "share", "proj")
20
- gdal_path = os.path.join(conda_env_path, "share", "gdal")
21
-
22
- # Check if the directory exists before setting
23
- if os.path.exists(proj_path):
24
- os.environ["PROJ_LIB"] = proj_path
25
- if os.path.exists(gdal_path):
26
- os.environ["GDAL_DATA"] = gdal_path
45
+ proj_set = False
46
+ for proj_path in possible_proj_paths:
47
+ if os.path.exists(proj_path) and os.path.isdir(proj_path):
48
+ # Verify it contains projection data
49
+ if os.path.exists(os.path.join(proj_path, "proj.db")):
50
+ os.environ["PROJ_LIB"] = proj_path
51
+ if verbose:
52
+ print(f"PROJ_LIB set to: {proj_path}")
53
+ proj_set = True
54
+ break
55
+
56
+ # Set GDAL_DATA environment variable
57
+ gdal_set = False
58
+ for gdal_path in possible_gdal_paths:
59
+ if os.path.exists(gdal_path) and os.path.isdir(gdal_path):
60
+ # Verify it contains the header.dxf file or other critical GDAL files
61
+ if os.path.exists(
62
+ os.path.join(gdal_path, "header.dxf")
63
+ ) or os.path.exists(os.path.join(gdal_path, "gcs.csv")):
64
+ os.environ["GDAL_DATA"] = gdal_path
65
+ if verbose:
66
+ print(f"GDAL_DATA set to: {gdal_path}")
67
+ gdal_set = True
68
+ break
69
+
70
+ # If paths still not found, try a last-resort approach
71
+ if not proj_set or not gdal_set:
72
+ # Try a deep search in the conda environment
73
+ for root, dirs, files in os.walk(conda_env_path):
74
+ if not gdal_set and "header.dxf" in files:
75
+ os.environ["GDAL_DATA"] = root
76
+ if verbose:
77
+ print(f"GDAL_DATA set to: {root} (deep search)")
78
+ gdal_set = True
79
+
80
+ if not proj_set and "proj.db" in files:
81
+ os.environ["PROJ_LIB"] = root
82
+ if verbose:
83
+ print(f"PROJ_LIB set to: {root} (deep search)")
84
+ proj_set = True
85
+
86
+ if proj_set and gdal_set:
87
+ break
88
+
27
89
  except Exception as e:
28
- print(e)
90
+ print(f"Error setting projection library paths: {e}")
29
91
  return
30
92
 
31
93
 
32
- if "google.colab" not in sys.modules:
33
- set_proj_lib_path()
94
+ # if ("google.colab" not in sys.modules) and (sys.platform != "windows"):
95
+ # set_proj_lib_path()
34
96
 
35
97
  from .geoai import *
geoai/download.py CHANGED
@@ -1,18 +1,19 @@
1
1
  """This module provides functions to download data, including NAIP imagery and building data from Overture Maps."""
2
2
 
3
+ import logging
3
4
  import os
4
- from typing import List, Tuple, Optional, Dict, Any
5
- import rioxarray
6
- import numpy as np
5
+ import subprocess
6
+ from typing import Any, Dict, List, Optional, Tuple
7
+
8
+ import geopandas as gpd
7
9
  import matplotlib.pyplot as plt
8
- from pystac_client import Client
10
+ import numpy as np
9
11
  import planetary_computer as pc
10
- import geopandas as gpd
12
+ import requests
13
+ import rioxarray
14
+ from pystac_client import Client
11
15
  from shapely.geometry import box
12
16
  from tqdm import tqdm
13
- import requests
14
- import subprocess
15
- import logging
16
17
 
17
18
  # Configure logging
18
19
  logging.basicConfig(
geoai/extract.py CHANGED
@@ -14,16 +14,15 @@ import torch
14
14
  from huggingface_hub import hf_hub_download
15
15
  from rasterio.windows import Window
16
16
  from shapely.geometry import Polygon, box
17
- from tqdm import tqdm
18
17
  from torchvision.models.detection import (
19
- maskrcnn_resnet50_fpn,
20
18
  fasterrcnn_resnet50_fpn_v2,
19
+ maskrcnn_resnet50_fpn,
21
20
  )
21
+ from tqdm import tqdm
22
22
 
23
23
  # Local Imports
24
24
  from .utils import get_raster_stats
25
25
 
26
-
27
26
  try:
28
27
  from torchgeo.datasets import NonGeoDataset
29
28
  except ImportError as e:
@@ -270,7 +269,9 @@ class ObjectDetector:
270
269
  Object extraction using Mask R-CNN with TorchGeo.
271
270
  """
272
271
 
273
- def __init__(self, model_path=None, repo_id=None, model=None, device=None):
272
+ def __init__(
273
+ self, model_path=None, repo_id=None, model=None, num_classes=2, device=None
274
+ ):
274
275
  """
275
276
  Initialize the object extractor.
276
277
 
@@ -278,6 +279,7 @@ class ObjectDetector:
278
279
  model_path: Path to the .pth model file.
279
280
  repo_id: Hugging Face repository ID for model download.
280
281
  model: Pre-initialized model object (optional).
282
+ num_classes: Number of classes for detection (default: 2).
281
283
  device: Device to use for inference ('cuda:0', 'cpu', etc.).
282
284
  """
283
285
  # Set device
@@ -297,7 +299,7 @@ class ObjectDetector:
297
299
  self.simplify_tolerance = 1.0 # Tolerance for polygon simplification
298
300
 
299
301
  # Initialize model
300
- self.model = self.initialize_model(model)
302
+ self.model = self.initialize_model(model, num_classes=num_classes)
301
303
 
302
304
  # Download model if needed
303
305
  if model_path is None or (not os.path.exists(model_path)):
@@ -342,11 +344,12 @@ class ObjectDetector:
342
344
  print("Please specify a local model path or ensure internet connectivity.")
343
345
  raise
344
346
 
345
- def initialize_model(self, model):
347
+ def initialize_model(self, model, num_classes=2):
346
348
  """Initialize a deep learning model for object detection.
347
349
 
348
350
  Args:
349
351
  model (torch.nn.Module): A pre-initialized model object.
352
+ num_classes (int): Number of classes for detection.
350
353
 
351
354
  Returns:
352
355
  torch.nn.Module: A deep learning model for object detection.
@@ -361,7 +364,7 @@ class ObjectDetector:
361
364
  model = maskrcnn_resnet50_fpn(
362
365
  weights=None,
363
366
  progress=False,
364
- num_classes=2, # Background + object
367
+ num_classes=num_classes, # Background + object
365
368
  weights_backbone=None,
366
369
  # These parameters ensure consistent normalization
367
370
  image_mean=image_mean,
@@ -1306,13 +1309,14 @@ class ObjectDetector:
1306
1309
  Returns:
1307
1310
  GeoDataFrame with regularized objects
1308
1311
  """
1312
+ import math
1313
+
1314
+ import cv2
1315
+ import geopandas as gpd
1309
1316
  import numpy as np
1310
- from shapely.geometry import Polygon, MultiPolygon, box
1311
1317
  from shapely.affinity import rotate, translate
1312
- import geopandas as gpd
1313
- import math
1318
+ from shapely.geometry import MultiPolygon, Polygon, box
1314
1319
  from tqdm import tqdm
1315
- import cv2
1316
1320
 
1317
1321
  def get_angle(p1, p2, p3):
1318
1322
  """Calculate angle between three points in degrees (0-180)"""
@@ -2112,7 +2116,7 @@ class ObjectDetector:
2112
2116
  output_path=None,
2113
2117
  confidence_threshold=0.5,
2114
2118
  min_object_area=100,
2115
- max_object_size=None,
2119
+ max_object_area=None,
2116
2120
  **kwargs,
2117
2121
  ):
2118
2122
  """
@@ -2123,7 +2127,7 @@ class ObjectDetector:
2123
2127
  output_path: Path for output GeoJSON.
2124
2128
  confidence_threshold: Minimum confidence score (0.0-1.0). Default: 0.5
2125
2129
  min_object_area: Minimum area in pixels to keep an object. Default: 100
2126
- max_object_size: Maximum area in pixels to keep an object. Default: None
2130
+ max_object_area: Maximum area in pixels to keep an object. Default: None
2127
2131
  **kwargs: Additional parameters
2128
2132
 
2129
2133
  Returns:
@@ -2147,8 +2151,9 @@ class ObjectDetector:
2147
2151
  print(f"Found {num_features} connected components")
2148
2152
 
2149
2153
  # Process each component
2150
- car_polygons = []
2151
- car_confidences = []
2154
+ polygons = []
2155
+ confidences = []
2156
+ pixels = []
2152
2157
 
2153
2158
  # Add progress bar
2154
2159
  for label in tqdm(range(1, num_features + 1), desc="Processing components"):
@@ -2179,8 +2184,8 @@ class ObjectDetector:
2179
2184
  if area < min_object_area:
2180
2185
  continue
2181
2186
 
2182
- if max_object_size is not None:
2183
- if area > max_object_size:
2187
+ if max_object_area is not None:
2188
+ if area > max_object_area:
2184
2189
  continue
2185
2190
 
2186
2191
  # Get minimum area rectangle
@@ -2197,16 +2202,18 @@ class ObjectDetector:
2197
2202
  poly = Polygon(geo_points)
2198
2203
 
2199
2204
  # Add to lists
2200
- car_polygons.append(poly)
2201
- car_confidences.append(confidence)
2205
+ polygons.append(poly)
2206
+ confidences.append(confidence)
2207
+ pixels.append(area)
2202
2208
 
2203
2209
  # Create GeoDataFrame
2204
- if car_polygons:
2210
+ if polygons:
2205
2211
  gdf = gpd.GeoDataFrame(
2206
2212
  {
2207
- "geometry": car_polygons,
2208
- "confidence": car_confidences,
2209
- "class": [1] * len(car_polygons),
2213
+ "geometry": polygons,
2214
+ "confidence": confidences,
2215
+ "class": [1] * len(polygons),
2216
+ "pixels": pixels,
2210
2217
  },
2211
2218
  crs=crs,
2212
2219
  )
@@ -2218,7 +2225,7 @@ class ObjectDetector:
2218
2225
 
2219
2226
  return gdf
2220
2227
  else:
2221
- print("No valid car polygons found")
2228
+ print("No valid polygons found")
2222
2229
  return None
2223
2230
 
2224
2231
 
@@ -2356,3 +2363,37 @@ class SolarPanelDetector(ObjectDetector):
2356
2363
  super().__init__(
2357
2364
  model_path=model_path, repo_id=repo_id, model=model, device=device
2358
2365
  )
2366
+
2367
+
2368
+ class ParkingSplotDetector(ObjectDetector):
2369
+ """
2370
+ Car detection using a pre-trained Mask R-CNN model.
2371
+
2372
+ This class extends the `ObjectDetector` class with additional methods for car detection.
2373
+ """
2374
+
2375
+ def __init__(
2376
+ self,
2377
+ model_path="parking_spot_detection.pth",
2378
+ repo_id=None,
2379
+ model=None,
2380
+ num_classes=3,
2381
+ device=None,
2382
+ ):
2383
+ """
2384
+ Initialize the object extractor.
2385
+
2386
+ Args:
2387
+ model_path: Path to the .pth model file.
2388
+ repo_id: Repo ID for loading models from the Hub.
2389
+ model: Custom model to use for inference.
2390
+ num_classes: Number of classes for the model. Default: 3
2391
+ device: Device to use for inference ('cuda:0', 'cpu', etc.).
2392
+ """
2393
+ super().__init__(
2394
+ model_path=model_path,
2395
+ repo_id=repo_id,
2396
+ model=model,
2397
+ num_classes=num_classes,
2398
+ device=device,
2399
+ )
geoai/geoai.py CHANGED
@@ -1,5 +1,7 @@
1
1
  """Main module."""
2
2
 
3
- from .utils import *
4
3
  from .extract import *
4
+ from .hf import *
5
5
  from .segment import *
6
+ from .utils import *
7
+ from .train import train_MaskRCNN_model, object_detection