ccfx 0.1.0__py3-none-any.whl → 0.2.0__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.
- ccfx/ccfx.py +168 -5
- ccfx/sqliteConnection.py +22 -12
- {ccfx-0.1.0.dist-info → ccfx-0.2.0.dist-info}/METADATA +3 -2
- ccfx-0.2.0.dist-info/RECORD +11 -0
- {ccfx-0.1.0.dist-info → ccfx-0.2.0.dist-info}/WHEEL +1 -1
- ccfx-0.1.0.dist-info/RECORD +0 -11
- {ccfx-0.1.0.dist-info → ccfx-0.2.0.dist-info}/LICENSE +0 -0
- {ccfx-0.1.0.dist-info → ccfx-0.2.0.dist-info}/top_level.txt +0 -0
ccfx/ccfx.py
CHANGED
@@ -23,7 +23,9 @@ import pickle
|
|
23
23
|
import time
|
24
24
|
from shapely.geometry import box, Point
|
25
25
|
import geopandas, pandas
|
26
|
-
from osgeo import gdal
|
26
|
+
from osgeo import gdal, ogr, osr
|
27
|
+
import py7zr
|
28
|
+
import subprocess
|
27
29
|
|
28
30
|
|
29
31
|
|
@@ -132,9 +134,6 @@ def watchFileCount(path:str="./", extension:str = ".*", interval:float = 0.2, du
|
|
132
134
|
return None
|
133
135
|
|
134
136
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
137
|
def pythonVariable(filename, option, variable=None):
|
139
138
|
'''
|
140
139
|
option: save, load or open
|
@@ -233,6 +232,57 @@ def createPath(pathName, v = False):
|
|
233
232
|
return pathName
|
234
233
|
|
235
234
|
|
235
|
+
def renameNetCDFvariable(input_file: str, output_file: str, old_var_name: str, new_var_name: str, v = False) -> None:
|
236
|
+
"""
|
237
|
+
Renames a variable in a NetCDF file using CDO if it exists.
|
238
|
+
If the variable does not exist, the file is copied without modification.
|
239
|
+
|
240
|
+
:param input_file: Path to the input NetCDF file
|
241
|
+
:param output_file: Path to the output NetCDF file
|
242
|
+
:param old_var_name: Name of the variable to rename
|
243
|
+
:param new_var_name: New name for the variable
|
244
|
+
"""
|
245
|
+
try:
|
246
|
+
# Check if the variable exists in the input file using `cdo showname`
|
247
|
+
result = subprocess.run(
|
248
|
+
["cdo", "showname", input_file],
|
249
|
+
capture_output=True,
|
250
|
+
text=True,
|
251
|
+
check=True
|
252
|
+
)
|
253
|
+
|
254
|
+
# Check if the old variable name is in the output
|
255
|
+
if old_var_name in result.stdout:
|
256
|
+
# Rename the variable using `cdo chname`
|
257
|
+
subprocess.run(
|
258
|
+
["cdo", f"chname,{old_var_name},{new_var_name}", input_file, output_file],
|
259
|
+
check=True
|
260
|
+
)
|
261
|
+
if v: print(f"Variable '{old_var_name}' renamed to '{new_var_name}' in '{output_file}'.")
|
262
|
+
else:
|
263
|
+
# Copy the file without renaming
|
264
|
+
shutil.move(input_file, output_file)
|
265
|
+
if v: print(f"Variable '{old_var_name}' not found; '{input_file}' moved to '{output_file}' without modification.")
|
266
|
+
|
267
|
+
except subprocess.CalledProcessError as e:
|
268
|
+
print(f"Error: {e.stderr}")
|
269
|
+
|
270
|
+
def compressTo7z(input_dir: str, output_file: str):
|
271
|
+
"""
|
272
|
+
Compresses the contents of a directory to a .7z archive with maximum compression.
|
273
|
+
|
274
|
+
:param input_dir: Path to the directory to compress
|
275
|
+
:param output_file: Output .7z file path
|
276
|
+
"""
|
277
|
+
# Create the .7z archive with LZMA2 compression
|
278
|
+
with py7zr.SevenZipFile(output_file, 'w', filters=[{'id': py7zr.FILTER_LZMA2, 'preset': 9}]) as archive:
|
279
|
+
# Add each item in the input directory, avoiding the top-level folder in the archive
|
280
|
+
for root, _, files in os.walk(input_dir):
|
281
|
+
for file in files:
|
282
|
+
file_path = os.path.join(root, file)
|
283
|
+
# Add file to the archive with a relative path to avoid including the 'tmp' folder itself
|
284
|
+
archive.write(file_path, arcname=os.path.relpath(file_path, start=input_dir))
|
285
|
+
|
236
286
|
|
237
287
|
def moveDirectory(srcDir:str, destDir:str, v:bool = False) -> bool:
|
238
288
|
'''
|
@@ -315,6 +365,99 @@ def clipRasterByExtent(inFile: str, outFile: str, bounds: tuple) -> str:
|
|
315
365
|
ds = None
|
316
366
|
return outFile
|
317
367
|
|
368
|
+
def clipVectorByExtent(inFile: str, outFile: str, bounds: tuple) -> str:
|
369
|
+
'''
|
370
|
+
Clips a vector using GeoPandas
|
371
|
+
inFile: input vector path
|
372
|
+
outFile: output path
|
373
|
+
bounds: tuple (minx, miny, maxx, maxy)
|
374
|
+
return: output path
|
375
|
+
'''
|
376
|
+
# Load the vector file as a GeoDataFrame
|
377
|
+
gdf = geopandas.read_file(inFile)
|
378
|
+
bbox = box(bounds[0], bounds[1], bounds[2], bounds[3])
|
379
|
+
clipped = gdf.clip(bbox)
|
380
|
+
clipped.to_file(outFile)
|
381
|
+
|
382
|
+
return outFile
|
383
|
+
|
384
|
+
def reprojectRaster(inFile: str, outFile: str, dstProjection: str, resamplingMethod: str = 'mode') -> str:
|
385
|
+
'''
|
386
|
+
Reprojects a raster to a new projection
|
387
|
+
inFile: input raster path
|
388
|
+
outFile: output raster path
|
389
|
+
dstProjection: target projection in "AUTH:CODE" format (e.g., "EPSG:3395")
|
390
|
+
resamplingMethod: resampling method to use (default is 'mode')
|
391
|
+
return: output path
|
392
|
+
'''
|
393
|
+
# Open the input raster
|
394
|
+
ds = gdal.Open(inFile)
|
395
|
+
|
396
|
+
# Define resampling method
|
397
|
+
resampling_methods = {
|
398
|
+
'nearest': gdal.GRA_NearestNeighbour,
|
399
|
+
'bilinear': gdal.GRA_Bilinear,
|
400
|
+
'cubic': gdal.GRA_Cubic,
|
401
|
+
'cubicspline': gdal.GRA_CubicSpline,
|
402
|
+
'lanczos': gdal.GRA_Lanczos,
|
403
|
+
'average': gdal.GRA_Average,
|
404
|
+
'mode': gdal.GRA_Mode,
|
405
|
+
'max': gdal.GRA_Max,
|
406
|
+
'min': gdal.GRA_Min,
|
407
|
+
'med': gdal.GRA_Med,
|
408
|
+
'q1': gdal.GRA_Q1,
|
409
|
+
'q3': gdal.GRA_Q3
|
410
|
+
}
|
411
|
+
|
412
|
+
resampling = resampling_methods.get(resamplingMethod, gdal.GRA_Mode)
|
413
|
+
gdal.Warp(outFile, ds, dstSRS=dstProjection, resampleAlg=resampling)
|
414
|
+
ds = None
|
415
|
+
|
416
|
+
return outFile
|
417
|
+
|
418
|
+
def rasterizeRaster(inFile: str, outFile: str, targetField: str, targetResolution: float) -> str:
|
419
|
+
'''
|
420
|
+
Rasterizes a vector layer to a raster file
|
421
|
+
inFile: input vector file path
|
422
|
+
outFile: output raster file path
|
423
|
+
targetField: the field in the vector layer to use as the raster value
|
424
|
+
targetResolution: resolution of the output raster (in units of the vector CRS)
|
425
|
+
return: output raster path
|
426
|
+
'''
|
427
|
+
# Open the vector file
|
428
|
+
vector_ds = ogr.Open(inFile)
|
429
|
+
layer = vector_ds.GetLayer()
|
430
|
+
|
431
|
+
# Get the extent of the vector layer
|
432
|
+
x_min, x_max, y_min, y_max = layer.GetExtent()
|
433
|
+
|
434
|
+
# Calculate the raster size based on target resolution
|
435
|
+
x_res = int((x_max - x_min) / targetResolution)
|
436
|
+
y_res = int((y_max - y_min) / targetResolution)
|
437
|
+
|
438
|
+
# Create the raster dataset
|
439
|
+
target_ds = gdal.GetDriverByName('GTiff').Create(outFile, x_res, y_res, 1, gdal.GDT_Int16)
|
440
|
+
target_ds.SetGeoTransform((x_min, targetResolution, 0, y_max, 0, -targetResolution))
|
441
|
+
|
442
|
+
# Set the projection from the vector layer
|
443
|
+
srs = layer.GetSpatialRef()
|
444
|
+
target_ds.SetProjection(srs.ExportToWkt())
|
445
|
+
|
446
|
+
# Set the no-data value to -999
|
447
|
+
band = target_ds.GetRasterBand(1)
|
448
|
+
band.SetNoDataValue(-999)
|
449
|
+
|
450
|
+
# Rasterize the vector layer
|
451
|
+
gdal.RasterizeLayer(target_ds, [1], layer, options=["ATTRIBUTE=" + targetField])
|
452
|
+
|
453
|
+
# Close the datasets
|
454
|
+
band = None
|
455
|
+
target_ds = None
|
456
|
+
vector_ds = None
|
457
|
+
|
458
|
+
return outFile
|
459
|
+
|
460
|
+
|
318
461
|
def getVectorBounds(grid_gdf: geopandas.GeoDataFrame) -> tuple:
|
319
462
|
'''
|
320
463
|
This function gets the bounds of a GeoDataFrame
|
@@ -339,7 +482,6 @@ def getVectorBounds(grid_gdf: geopandas.GeoDataFrame) -> tuple:
|
|
339
482
|
|
340
483
|
return minx, miny, maxx, maxy
|
341
484
|
|
342
|
-
|
343
485
|
def ignoreWarnings(ignore:bool = True, v:bool = False) -> None:
|
344
486
|
'''
|
345
487
|
Ignore warnings
|
@@ -667,6 +809,27 @@ def showProgress(count: int, end: int, message: str, barLength: int = 100) -> No
|
|
667
809
|
if count == end: print()
|
668
810
|
|
669
811
|
|
812
|
+
def listAllFiles(folder, extension="*"):
|
813
|
+
list_of_files = []
|
814
|
+
# Getting the current work directory (cwd)
|
815
|
+
thisdir = folder
|
816
|
+
|
817
|
+
# r=root, d=directories, f = files
|
818
|
+
for r, d, f in os.walk(thisdir):
|
819
|
+
for file in f:
|
820
|
+
if extension == "*":
|
821
|
+
list_of_files.append(os.path.join(r, file))
|
822
|
+
elif "." in extension:
|
823
|
+
if file.endswith(extension[1:]):
|
824
|
+
list_of_files.append(os.path.join(r, file))
|
825
|
+
# print(os.path.join(r, file))
|
826
|
+
else:
|
827
|
+
if file.endswith(extension):
|
828
|
+
list_of_files.append(os.path.join(r, file))
|
829
|
+
# print(os.path.join(r, file))
|
830
|
+
|
831
|
+
return list_of_files
|
832
|
+
|
670
833
|
|
671
834
|
def createPointGeometry(coords: list, proj: str = "EPSG:4326") -> geopandas.GeoDataFrame:
|
672
835
|
'''
|
ccfx/sqliteConnection.py
CHANGED
@@ -33,19 +33,29 @@ class sqliteConnection:
|
|
33
33
|
|
34
34
|
def updateValue(self, table_name, col_name, new_value, col_where1, val_1, v=False):
|
35
35
|
"""
|
36
|
-
|
36
|
+
Updates a single value in a specified table where the condition matches.
|
37
|
+
|
38
|
+
Args:
|
39
|
+
table_name (str): Name of the table to update
|
40
|
+
col_name (str): Name of the column to update
|
41
|
+
new_value: Value to set (can be None)
|
42
|
+
col_where1 (str): Column name for WHERE clause
|
43
|
+
val_1: Value to match in WHERE clause
|
44
|
+
v (bool): Verbose flag for logging
|
37
45
|
"""
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
46
|
+
try:
|
47
|
+
# Use parameterized queries for ALL cases to prevent SQL injection
|
48
|
+
query = f"UPDATE {table_name} SET {col_name} = ? WHERE {col_where1} = ?"
|
49
|
+
self.cursor.execute(query, (new_value, val_1))
|
50
|
+
|
51
|
+
if v:
|
52
|
+
self.report(f"\t -> updated value in {self.db_name.split('/')[-1].split('\\')[-1]} table: {table_name}")
|
53
|
+
|
54
|
+
# Commit the transaction if needed (add self.conn.commit() if not in a transaction)
|
55
|
+
|
56
|
+
except Exception as e:
|
57
|
+
raise Exception(f"Error updating value: {str(e)}")
|
58
|
+
|
49
59
|
|
50
60
|
def createTable(self, table_name, initial_field_name, data_type):
|
51
61
|
'''
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: ccfx
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.2.0
|
4
4
|
Summary: Your package description here
|
5
5
|
Author-email: Celray James CHAWANDA <celray@chawanda.com>
|
6
6
|
License: MIT
|
@@ -20,7 +20,8 @@ Requires-Dist: pandas
|
|
20
20
|
Requires-Dist: xlsxwriter
|
21
21
|
Requires-Dist: pyodbc
|
22
22
|
Requires-Dist: sqlalchemy
|
23
|
-
Requires-Dist: docx
|
23
|
+
Requires-Dist: python-docx
|
24
|
+
Requires-Dist: py7zr
|
24
25
|
|
25
26
|
# ccfx
|
26
27
|
|
@@ -0,0 +1,11 @@
|
|
1
|
+
ccfx/__init__.py,sha256=VmBeF3oj6JTJ_793d4i8PvhyF8_FxaxA1L_FmHWqitc,142
|
2
|
+
ccfx/ccfx.py,sha256=lB6hTCywvbMpHH6lMgw0UJuoiU-7vA22yvttvKFG5Gc,28450
|
3
|
+
ccfx/excel.py,sha256=cQ4TQW49XqbMB3sSS0IOhO3-WArIolEBIrvOvhFyPtI,4757
|
4
|
+
ccfx/mssqlConnection.py,sha256=TwyZXhHHI7zy6BSfH1pszuHVJ5cmndRC5dVxvEtSTks,7904
|
5
|
+
ccfx/sqliteConnection.py,sha256=BsS3jzHSevXLDmtPIFPVuzKqB981su0VpYadTk4xFEQ,11231
|
6
|
+
ccfx/word.py,sha256=AGa64jX5Zl5qotZh5L0QmrsjTnktIBhmj_ByRKZ88vw,3061
|
7
|
+
ccfx-0.2.0.dist-info/LICENSE,sha256=2-M3fBUS3FmrSIrqd3cZDmxXxojWVJtZY-SHSRE6RxM,1098
|
8
|
+
ccfx-0.2.0.dist-info/METADATA,sha256=2GD2kIkcVUo6FG4Th3IEAucmYmhZ4DvdbtGPTI328-c,5419
|
9
|
+
ccfx-0.2.0.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
|
10
|
+
ccfx-0.2.0.dist-info/top_level.txt,sha256=_cSvSA1WX2K8TgoV3iBJUdUZZqMKJbOPLNnKLYSLHaw,5
|
11
|
+
ccfx-0.2.0.dist-info/RECORD,,
|
ccfx-0.1.0.dist-info/RECORD
DELETED
@@ -1,11 +0,0 @@
|
|
1
|
-
ccfx/__init__.py,sha256=VmBeF3oj6JTJ_793d4i8PvhyF8_FxaxA1L_FmHWqitc,142
|
2
|
-
ccfx/ccfx.py,sha256=7vDYc8X1bQGHuF8ZWEH3cWkGDJGlZKrG5kcoeG9IZac,22110
|
3
|
-
ccfx/excel.py,sha256=cQ4TQW49XqbMB3sSS0IOhO3-WArIolEBIrvOvhFyPtI,4757
|
4
|
-
ccfx/mssqlConnection.py,sha256=TwyZXhHHI7zy6BSfH1pszuHVJ5cmndRC5dVxvEtSTks,7904
|
5
|
-
ccfx/sqliteConnection.py,sha256=7xET_tV8uu4LEln4nsWzp3YR5HOma15aaILjrv2F5ZM,10866
|
6
|
-
ccfx/word.py,sha256=AGa64jX5Zl5qotZh5L0QmrsjTnktIBhmj_ByRKZ88vw,3061
|
7
|
-
ccfx-0.1.0.dist-info/LICENSE,sha256=2-M3fBUS3FmrSIrqd3cZDmxXxojWVJtZY-SHSRE6RxM,1098
|
8
|
-
ccfx-0.1.0.dist-info/METADATA,sha256=hZSUkqy3F-ZWJBH8w6pPELTR_1rNSahFTA4G28PZfiw,5390
|
9
|
-
ccfx-0.1.0.dist-info/WHEEL,sha256=OVMc5UfuAQiSplgO0_WdW7vXVGAt9Hdd6qtN4HotdyA,91
|
10
|
-
ccfx-0.1.0.dist-info/top_level.txt,sha256=_cSvSA1WX2K8TgoV3iBJUdUZZqMKJbOPLNnKLYSLHaw,5
|
11
|
-
ccfx-0.1.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|