geoai-py 0.4.3__py2.py3-none-any.whl → 0.5.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 +6 -1
- geoai/classify.py +933 -0
- geoai/download.py +119 -80
- geoai/geoai.py +93 -1
- geoai/train.py +115 -6
- geoai/utils.py +196 -0
- {geoai_py-0.4.3.dist-info → geoai_py-0.5.1.dist-info}/METADATA +9 -1
- geoai_py-0.5.1.dist-info/RECORD +16 -0
- {geoai_py-0.4.3.dist-info → geoai_py-0.5.1.dist-info}/WHEEL +1 -1
- geoai_py-0.4.3.dist-info/RECORD +0 -15
- {geoai_py-0.4.3.dist-info → geoai_py-0.5.1.dist-info}/entry_points.txt +0 -0
- {geoai_py-0.4.3.dist-info → geoai_py-0.5.1.dist-info}/licenses/LICENSE +0 -0
- {geoai_py-0.4.3.dist-info → geoai_py-0.5.1.dist-info}/top_level.txt +0 -0
geoai/utils.py
CHANGED
|
@@ -6053,3 +6053,199 @@ def try_common_architectures(state_dict):
|
|
|
6053
6053
|
|
|
6054
6054
|
except Exception as e:
|
|
6055
6055
|
print(f"- {name}: Failed to load - {str(e)}")
|
|
6056
|
+
|
|
6057
|
+
|
|
6058
|
+
def mosaic_geotiffs(input_dir, output_file, mask_file=None):
|
|
6059
|
+
"""Create a mosaic from all GeoTIFF files as a Cloud Optimized GeoTIFF (COG).
|
|
6060
|
+
|
|
6061
|
+
This function identifies all GeoTIFF files in the specified directory,
|
|
6062
|
+
creates a seamless mosaic with proper handling of nodata values, and saves
|
|
6063
|
+
as a Cloud Optimized GeoTIFF format. If a mask file is provided, the output
|
|
6064
|
+
will be clipped to the extent of the mask.
|
|
6065
|
+
|
|
6066
|
+
Args:
|
|
6067
|
+
input_dir (str): Path to the directory containing GeoTIFF files.
|
|
6068
|
+
output_file (str): Path to the output Cloud Optimized GeoTIFF file.
|
|
6069
|
+
mask_file (str, optional): Path to a mask file to clip the output.
|
|
6070
|
+
If provided, the output will be clipped to the extent of this mask.
|
|
6071
|
+
Defaults to None.
|
|
6072
|
+
|
|
6073
|
+
Returns:
|
|
6074
|
+
bool: True if the mosaic was created successfully, False otherwise.
|
|
6075
|
+
|
|
6076
|
+
Examples:
|
|
6077
|
+
>>> mosaic_geotiffs('naip', 'merged_naip.tif')
|
|
6078
|
+
True
|
|
6079
|
+
>>> mosaic_geotiffs('naip', 'merged_naip.tif', 'boundary.tif')
|
|
6080
|
+
True
|
|
6081
|
+
"""
|
|
6082
|
+
import glob
|
|
6083
|
+
from osgeo import gdal
|
|
6084
|
+
|
|
6085
|
+
gdal.UseExceptions()
|
|
6086
|
+
# Get all tif files in the directory
|
|
6087
|
+
tif_files = glob.glob(os.path.join(input_dir, "*.tif"))
|
|
6088
|
+
|
|
6089
|
+
if not tif_files:
|
|
6090
|
+
print("No GeoTIFF files found in the specified directory.")
|
|
6091
|
+
return False
|
|
6092
|
+
|
|
6093
|
+
# Analyze the first input file to determine compression and nodata settings
|
|
6094
|
+
ds = gdal.Open(tif_files[0])
|
|
6095
|
+
if ds is None:
|
|
6096
|
+
print(f"Unable to open {tif_files[0]}")
|
|
6097
|
+
return False
|
|
6098
|
+
|
|
6099
|
+
# Get driver metadata from the first file
|
|
6100
|
+
driver = ds.GetDriver()
|
|
6101
|
+
creation_options = []
|
|
6102
|
+
|
|
6103
|
+
# Check compression type
|
|
6104
|
+
metadata = ds.GetMetadata("IMAGE_STRUCTURE")
|
|
6105
|
+
if "COMPRESSION" in metadata:
|
|
6106
|
+
compression = metadata["COMPRESSION"]
|
|
6107
|
+
creation_options.append(f"COMPRESS={compression}")
|
|
6108
|
+
else:
|
|
6109
|
+
# Default compression if none detected
|
|
6110
|
+
creation_options.append("COMPRESS=LZW")
|
|
6111
|
+
|
|
6112
|
+
# Add COG-specific creation options
|
|
6113
|
+
creation_options.extend(["TILED=YES", "BLOCKXSIZE=512", "BLOCKYSIZE=512"])
|
|
6114
|
+
|
|
6115
|
+
# Check for nodata value in the first band of the first file
|
|
6116
|
+
band = ds.GetRasterBand(1)
|
|
6117
|
+
has_nodata = band.GetNoDataValue() is not None
|
|
6118
|
+
nodata_value = band.GetNoDataValue() if has_nodata else None
|
|
6119
|
+
|
|
6120
|
+
# Close the dataset
|
|
6121
|
+
ds = None
|
|
6122
|
+
|
|
6123
|
+
# Create a temporary VRT (Virtual Dataset)
|
|
6124
|
+
vrt_path = os.path.join(input_dir, "temp_mosaic.vrt")
|
|
6125
|
+
|
|
6126
|
+
# Build VRT from input files with proper nodata handling
|
|
6127
|
+
vrt_options = gdal.BuildVRTOptions(
|
|
6128
|
+
resampleAlg="nearest",
|
|
6129
|
+
srcNodata=nodata_value if has_nodata else None,
|
|
6130
|
+
VRTNodata=nodata_value if has_nodata else None,
|
|
6131
|
+
)
|
|
6132
|
+
vrt_dataset = gdal.BuildVRT(vrt_path, tif_files, options=vrt_options)
|
|
6133
|
+
|
|
6134
|
+
# Close the VRT dataset to flush it to disk
|
|
6135
|
+
vrt_dataset = None
|
|
6136
|
+
|
|
6137
|
+
# Create temp mosaic
|
|
6138
|
+
temp_mosaic = output_file + ".temp.tif"
|
|
6139
|
+
|
|
6140
|
+
# Convert VRT to GeoTIFF with the same compression as input
|
|
6141
|
+
translate_options = gdal.TranslateOptions(
|
|
6142
|
+
format="GTiff",
|
|
6143
|
+
creationOptions=creation_options,
|
|
6144
|
+
noData=nodata_value if has_nodata else None,
|
|
6145
|
+
)
|
|
6146
|
+
gdal.Translate(temp_mosaic, vrt_path, options=translate_options)
|
|
6147
|
+
|
|
6148
|
+
# Apply mask if provided
|
|
6149
|
+
if mask_file and os.path.exists(mask_file):
|
|
6150
|
+
print(f"Clipping mosaic to mask: {mask_file}")
|
|
6151
|
+
|
|
6152
|
+
# Create a temporary clipped file
|
|
6153
|
+
clipped_mosaic = output_file + ".clipped.tif"
|
|
6154
|
+
|
|
6155
|
+
# Open mask file
|
|
6156
|
+
mask_ds = gdal.Open(mask_file)
|
|
6157
|
+
if mask_ds is None:
|
|
6158
|
+
print(f"Unable to open mask file: {mask_file}")
|
|
6159
|
+
# Continue without clipping
|
|
6160
|
+
else:
|
|
6161
|
+
# Get mask extent
|
|
6162
|
+
mask_geotransform = mask_ds.GetGeoTransform()
|
|
6163
|
+
mask_projection = mask_ds.GetProjection()
|
|
6164
|
+
mask_ulx = mask_geotransform[0]
|
|
6165
|
+
mask_uly = mask_geotransform[3]
|
|
6166
|
+
mask_lrx = mask_ulx + (mask_geotransform[1] * mask_ds.RasterXSize)
|
|
6167
|
+
mask_lry = mask_uly + (mask_geotransform[5] * mask_ds.RasterYSize)
|
|
6168
|
+
|
|
6169
|
+
# Close mask dataset
|
|
6170
|
+
mask_ds = None
|
|
6171
|
+
|
|
6172
|
+
# Use warp options to clip
|
|
6173
|
+
warp_options = gdal.WarpOptions(
|
|
6174
|
+
format="GTiff",
|
|
6175
|
+
outputBounds=[mask_ulx, mask_lry, mask_lrx, mask_uly],
|
|
6176
|
+
dstSRS=mask_projection,
|
|
6177
|
+
creationOptions=creation_options,
|
|
6178
|
+
srcNodata=nodata_value if has_nodata else None,
|
|
6179
|
+
dstNodata=nodata_value if has_nodata else None,
|
|
6180
|
+
)
|
|
6181
|
+
|
|
6182
|
+
# Apply clipping
|
|
6183
|
+
gdal.Warp(clipped_mosaic, temp_mosaic, options=warp_options)
|
|
6184
|
+
|
|
6185
|
+
# Remove the unclipped temp mosaic and use the clipped one
|
|
6186
|
+
os.remove(temp_mosaic)
|
|
6187
|
+
temp_mosaic = clipped_mosaic
|
|
6188
|
+
|
|
6189
|
+
# Create internal overviews for the temp mosaic
|
|
6190
|
+
ds = gdal.Open(temp_mosaic, gdal.GA_Update)
|
|
6191
|
+
overview_list = [2, 4, 8, 16, 32]
|
|
6192
|
+
ds.BuildOverviews("NEAREST", overview_list)
|
|
6193
|
+
ds = None # Close the dataset to ensure overviews are written
|
|
6194
|
+
|
|
6195
|
+
# Convert the temp mosaic to a proper COG
|
|
6196
|
+
cog_options = gdal.TranslateOptions(
|
|
6197
|
+
format="GTiff",
|
|
6198
|
+
creationOptions=[
|
|
6199
|
+
"TILED=YES",
|
|
6200
|
+
"COPY_SRC_OVERVIEWS=YES",
|
|
6201
|
+
"COMPRESS=DEFLATE",
|
|
6202
|
+
"PREDICTOR=2",
|
|
6203
|
+
"BLOCKXSIZE=512",
|
|
6204
|
+
"BLOCKYSIZE=512",
|
|
6205
|
+
],
|
|
6206
|
+
noData=nodata_value if has_nodata else None,
|
|
6207
|
+
)
|
|
6208
|
+
gdal.Translate(output_file, temp_mosaic, options=cog_options)
|
|
6209
|
+
|
|
6210
|
+
# Clean up temporary files
|
|
6211
|
+
if os.path.exists(vrt_path):
|
|
6212
|
+
os.remove(vrt_path)
|
|
6213
|
+
if os.path.exists(temp_mosaic):
|
|
6214
|
+
os.remove(temp_mosaic)
|
|
6215
|
+
|
|
6216
|
+
print(f"Cloud Optimized GeoTIFF mosaic created successfully: {output_file}")
|
|
6217
|
+
return True
|
|
6218
|
+
|
|
6219
|
+
|
|
6220
|
+
def download_model_from_hf(model_path, repo_id=None):
|
|
6221
|
+
"""
|
|
6222
|
+
Download the object detection model from Hugging Face.
|
|
6223
|
+
|
|
6224
|
+
Args:
|
|
6225
|
+
model_path: Path to the model file.
|
|
6226
|
+
repo_id: Hugging Face repository ID.
|
|
6227
|
+
|
|
6228
|
+
Returns:
|
|
6229
|
+
Path to the downloaded model file
|
|
6230
|
+
"""
|
|
6231
|
+
from huggingface_hub import hf_hub_download
|
|
6232
|
+
|
|
6233
|
+
try:
|
|
6234
|
+
|
|
6235
|
+
# Define the repository ID and model filename
|
|
6236
|
+
if repo_id is None:
|
|
6237
|
+
print(
|
|
6238
|
+
"Repo is is not specified, using default Hugging Face repo_id: giswqs/geoai"
|
|
6239
|
+
)
|
|
6240
|
+
repo_id = "giswqs/geoai"
|
|
6241
|
+
|
|
6242
|
+
# Download the model
|
|
6243
|
+
model_path = hf_hub_download(repo_id=repo_id, filename=model_path)
|
|
6244
|
+
print(f"Model downloaded to: {model_path}")
|
|
6245
|
+
|
|
6246
|
+
return model_path
|
|
6247
|
+
|
|
6248
|
+
except Exception as e:
|
|
6249
|
+
print(f"Error downloading model from Hugging Face: {e}")
|
|
6250
|
+
print("Please specify a local model path or ensure internet connectivity.")
|
|
6251
|
+
raise
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: geoai-py
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.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
|
|
@@ -25,6 +25,7 @@ Requires-Dist: jupyter-server-proxy
|
|
|
25
25
|
Requires-Dist: leafmap
|
|
26
26
|
Requires-Dist: localtileserver
|
|
27
27
|
Requires-Dist: mapclassify
|
|
28
|
+
Requires-Dist: maplibre
|
|
28
29
|
Requires-Dist: overturemaps
|
|
29
30
|
Requires-Dist: planetary-computer
|
|
30
31
|
Requires-Dist: pystac-client
|
|
@@ -143,3 +144,10 @@ We welcome contributions of all kinds! See our [contributing guide](https://geoa
|
|
|
143
144
|
## 📄 License
|
|
144
145
|
|
|
145
146
|
GeoAI is free and open source software, licensed under the MIT License.
|
|
147
|
+
|
|
148
|
+
## Acknowledgments
|
|
149
|
+
|
|
150
|
+
We gratefully acknowledge the support of the following organizations:
|
|
151
|
+
|
|
152
|
+
- [NASA](https://www.nasa.gov): This research is partially supported by the National Aeronautics and Space Administration (NASA) through Grant No. 80NSSC22K1742, awarded under the [Open Source Tools, Frameworks, and Libraries Program](https://bit.ly/3RVBRcQ).
|
|
153
|
+
- [AmericaView](https://americaview.org): This work is also partially supported by the U.S. Geological Survey through Grant/Cooperative Agreement No. G23AP00683 (GY23-GY27) in collaboration with AmericaView.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
geoai/__init__.py,sha256=pJWIhWx8tlrmE1KCdmmSNJFEyvGyzOBBtYWdtQanGCU,3765
|
|
2
|
+
geoai/classify.py,sha256=_e-193QzAx3pIxUflPIsIs1qZevQx5ADu7i3bOL1G70,35055
|
|
3
|
+
geoai/download.py,sha256=lJ1GsJOZsKc2i6_dQyPV-XXIXmlADOpmSBo-wha4DEU,40892
|
|
4
|
+
geoai/extract.py,sha256=GocJufMmrwEWxNBL1J91EXXHL8AKcO8m_lmtUF5AKPw,119102
|
|
5
|
+
geoai/geoai.py,sha256=QGKDAenLPo1CgKZLsMFDzW7CLH1Ao2sYaWhFZG3eTpk,4896
|
|
6
|
+
geoai/hf.py,sha256=mLKGxEAS5eHkxZLwuLpYc1o7e3-7QIXdBv-QUY-RkFk,17072
|
|
7
|
+
geoai/segment.py,sha256=g3YW17ftr--CKq6VB32TJEPY8owGQ7uQ0sg_tUT2ooE,13681
|
|
8
|
+
geoai/segmentation.py,sha256=AtPzCvguHAEeuyXafa4bzMFATvltEYcah1B8ZMfkM_s,11373
|
|
9
|
+
geoai/train.py,sha256=mQXat2yuddT-2rME4xnX_m3SkY23E_-zdxLnBIKxw8o,44091
|
|
10
|
+
geoai/utils.py,sha256=5BZTL9QlJGEs9uw5w6i_aZ4s8SH_FGvb6ZFlIyEHEZI,239703
|
|
11
|
+
geoai_py-0.5.1.dist-info/licenses/LICENSE,sha256=vN2L5U7cZ6ZkOHFmc8WiGlsogWsZc5dllMeNxnKVOZg,1070
|
|
12
|
+
geoai_py-0.5.1.dist-info/METADATA,sha256=9_A_dN9Vnl5C4Q_i7N8OdgTTo1Ke4wunve3ChsUvQnA,6637
|
|
13
|
+
geoai_py-0.5.1.dist-info/WHEEL,sha256=MAQBAzGbXNI3bUmkDsiV_duv8i-gcdnLzw7cfUFwqhU,109
|
|
14
|
+
geoai_py-0.5.1.dist-info/entry_points.txt,sha256=uGp3Az3HURIsRHP9v-ys0hIbUuBBNUfXv6VbYHIXeg4,41
|
|
15
|
+
geoai_py-0.5.1.dist-info/top_level.txt,sha256=1YkCUWu-ii-0qIex7kbwAvfei-gos9ycyDyUCJPNWHY,6
|
|
16
|
+
geoai_py-0.5.1.dist-info/RECORD,,
|
geoai_py-0.4.3.dist-info/RECORD
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
geoai/__init__.py,sha256=b12E2HztHEPaaKMVKpk6GPjC7ElUMxOW9pYKl4VZkmE,3592
|
|
2
|
-
geoai/download.py,sha256=BvCEpcBwZlEOWieixsdyvQDiE0CXRjU7oayLmy5_Dgs,40110
|
|
3
|
-
geoai/extract.py,sha256=GocJufMmrwEWxNBL1J91EXXHL8AKcO8m_lmtUF5AKPw,119102
|
|
4
|
-
geoai/geoai.py,sha256=BqKdWzNruDdGqwqoyTaJzUq4lKGj-RDBZlSO3t3-GxQ,626
|
|
5
|
-
geoai/hf.py,sha256=mLKGxEAS5eHkxZLwuLpYc1o7e3-7QIXdBv-QUY-RkFk,17072
|
|
6
|
-
geoai/segment.py,sha256=g3YW17ftr--CKq6VB32TJEPY8owGQ7uQ0sg_tUT2ooE,13681
|
|
7
|
-
geoai/segmentation.py,sha256=AtPzCvguHAEeuyXafa4bzMFATvltEYcah1B8ZMfkM_s,11373
|
|
8
|
-
geoai/train.py,sha256=-l2j1leTxDnFDLaBslu1q6CobXjm3LEdiQwUWOU8P6M,40088
|
|
9
|
-
geoai/utils.py,sha256=Wg9jbMBKUZSGUmU8Vkp6v19QcDNg5KmcyZxuHqJvgnc,233016
|
|
10
|
-
geoai_py-0.4.3.dist-info/licenses/LICENSE,sha256=vN2L5U7cZ6ZkOHFmc8WiGlsogWsZc5dllMeNxnKVOZg,1070
|
|
11
|
-
geoai_py-0.4.3.dist-info/METADATA,sha256=geDmJ-1zHImsOdcj4gypgq8JqSy8MznnxAnICwh0EbA,6049
|
|
12
|
-
geoai_py-0.4.3.dist-info/WHEEL,sha256=aoLN90hLOL0c0qxXMxWYUM3HA3WmFGZQqEJHX1V_OJE,109
|
|
13
|
-
geoai_py-0.4.3.dist-info/entry_points.txt,sha256=uGp3Az3HURIsRHP9v-ys0hIbUuBBNUfXv6VbYHIXeg4,41
|
|
14
|
-
geoai_py-0.4.3.dist-info/top_level.txt,sha256=1YkCUWu-ii-0qIex7kbwAvfei-gos9ycyDyUCJPNWHY,6
|
|
15
|
-
geoai_py-0.4.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|