ccfx 1.0.7__py3-none-any.whl → 1.0.9__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 +112 -59
- {ccfx-1.0.7.dist-info → ccfx-1.0.9.dist-info}/METADATA +1 -1
- {ccfx-1.0.7.dist-info → ccfx-1.0.9.dist-info}/RECORD +6 -6
- {ccfx-1.0.7.dist-info → ccfx-1.0.9.dist-info}/WHEEL +0 -0
- {ccfx-1.0.7.dist-info → ccfx-1.0.9.dist-info}/licenses/LICENSE +0 -0
- {ccfx-1.0.7.dist-info → ccfx-1.0.9.dist-info}/top_level.txt +0 -0
ccfx/ccfx.py
CHANGED
@@ -35,7 +35,7 @@ import math
|
|
35
35
|
import requests
|
36
36
|
from tqdm import tqdm
|
37
37
|
import yt_dlp
|
38
|
-
from typing import Optional
|
38
|
+
from typing import Optional, Any
|
39
39
|
from datetime import datetime, timedelta
|
40
40
|
from PIL import Image
|
41
41
|
|
@@ -72,7 +72,7 @@ def getExtension(filePath:str) -> str:
|
|
72
72
|
return os.path.splitext(filePath)[1].lstrip('.')
|
73
73
|
|
74
74
|
|
75
|
-
def getMp3Metadata(fn, imagePath=None):
|
75
|
+
def getMp3Metadata(fn: str, imagePath: Optional[str] = None) -> dict:
|
76
76
|
'''
|
77
77
|
This function takes a path to mp3 and returns a dictionary with
|
78
78
|
the following keys:
|
@@ -127,7 +127,7 @@ def getMp3Metadata(fn, imagePath=None):
|
|
127
127
|
return metadata
|
128
128
|
|
129
129
|
|
130
|
-
def guessMimeType(imagePath):
|
130
|
+
def guessMimeType(imagePath: str) -> str:
|
131
131
|
ext = os.path.splitext(imagePath.lower())[1]
|
132
132
|
if ext in ['.jpg', '.jpeg']:
|
133
133
|
return 'image/jpeg'
|
@@ -240,7 +240,7 @@ def parseYoutubeChannelVideos(channelUrl: str, maxItems: Optional[int] = None) -
|
|
240
240
|
return [f"https://www.youtube.com/watch?v={e['id']}" for e in entries if e.get("id")]
|
241
241
|
|
242
242
|
|
243
|
-
def runSWATPlus(txtinoutDir: str, finalDir: str, executablePath: str = "swatplus", v: bool = True):
|
243
|
+
def runSWATPlus(txtinoutDir: str, finalDir: str, executablePath: str = "swatplus", v: bool = True) -> None:
|
244
244
|
os.chdir(txtinoutDir)
|
245
245
|
|
246
246
|
if not v:
|
@@ -339,7 +339,7 @@ def formatTimedelta(delta: timedelta) -> str:
|
|
339
339
|
return f"{time_fmt}"
|
340
340
|
|
341
341
|
|
342
|
-
def setMp3Metadata(fn, metadata, imagePath=None):
|
342
|
+
def setMp3Metadata(fn: str, metadata: dict, imagePath: Optional[str] = None) -> bool:
|
343
343
|
'''
|
344
344
|
This function takes a path to an mp3 and a metadata dictionary,
|
345
345
|
then writes that metadata to the file's ID3 tags.
|
@@ -434,7 +434,7 @@ def deleteFile(filePath:str, v:bool = False) -> bool:
|
|
434
434
|
|
435
435
|
return deleted
|
436
436
|
|
437
|
-
def removeImageColour(inPath:str, outPath:str, colour:tuple = (255, 255, 255), tolerance:int = 30):
|
437
|
+
def removeImageColour(inPath:str, outPath:str, colour:tuple = (255, 255, 255), tolerance:int = 30) -> None:
|
438
438
|
'''
|
439
439
|
Remove a specific color from an image.
|
440
440
|
colour: RGB tuple, e.g., (255, 0, 0) for red
|
@@ -457,7 +457,7 @@ def removeImageColour(inPath:str, outPath:str, colour:tuple = (255, 255, 255), t
|
|
457
457
|
img.putdata(new_data)
|
458
458
|
img.save(outPath)
|
459
459
|
|
460
|
-
def makeTransparent(inPath:str, outPath:str, colour:tuple = (255, 255, 255), tolerance:int = 30):
|
460
|
+
def makeTransparent(inPath:str, outPath:str, colour:tuple = (255, 255, 255), tolerance:int = 30) -> None:
|
461
461
|
'''
|
462
462
|
Make some pixels in an image transparent.
|
463
463
|
'''
|
@@ -479,9 +479,9 @@ def alert(message:str, server:str = "http://ntfy.sh", topic:str = "pythonAlerts"
|
|
479
479
|
return: True if the alert was sent successfully, False otherwise
|
480
480
|
'''
|
481
481
|
print(message) if printIt else None; header_data = {}
|
482
|
-
if not messageTitle is None: header_data["Title"] = messageTitle
|
483
|
-
if not priority is None: header_data["Priority"] = priority
|
484
|
-
if not len(tags) == 0: header_data["Tags"] = ",".join(tags)
|
482
|
+
if not messageTitle is None: header_data["Title"] = str(messageTitle).replace("\r"," ").replace("\n"," ")
|
483
|
+
if not priority is None: header_data["Priority"] = str(int(priority))
|
484
|
+
if not len(tags) == 0: header_data["Tags"] = ",".join(map(str, tags))
|
485
485
|
|
486
486
|
try:
|
487
487
|
if v: print(f"sending alert to {server}/{topic}")
|
@@ -517,9 +517,10 @@ def deletePath(path:str, v:bool = False) -> bool:
|
|
517
517
|
if v:
|
518
518
|
print(f'! {path} does not exist')
|
519
519
|
deleted = False
|
520
|
+
return deleted
|
520
521
|
|
521
522
|
|
522
|
-
def downloadChunk(url, start, end, path):
|
523
|
+
def downloadChunk(url: str, start: int, end: int, path: str) -> None:
|
523
524
|
headers = {'Range': f'bytes={start}-{end}'}
|
524
525
|
response = requests.get(url, headers=headers, stream=True)
|
525
526
|
with open(path, 'wb') as f:
|
@@ -528,7 +529,47 @@ def downloadChunk(url, start, end, path):
|
|
528
529
|
f.write(chunk)
|
529
530
|
|
530
531
|
|
531
|
-
def
|
532
|
+
def correctFisheye(inputFile: str, outputFile: str = '',
|
533
|
+
k1: float = -0.1, k2: float = 0.05,
|
534
|
+
cx: float = 0.5, cy: float = 0.5,
|
535
|
+
crf: int = 20) -> str:
|
536
|
+
"""
|
537
|
+
Correct fisheye distortion in a video and save as MP4.
|
538
|
+
|
539
|
+
Args:
|
540
|
+
inputFile (str): Path to the input video (any format).
|
541
|
+
outputFile (str, optional): Path for the corrected MP4. If None, auto-creates.
|
542
|
+
k1, k2 (float): Lens distortion coefficients.
|
543
|
+
cx, cy (float): Optical center (0.5 = image center).
|
544
|
+
crf (int): Constant Rate Factor (lower = better quality, larger file).
|
545
|
+
|
546
|
+
Returns:
|
547
|
+
str: Path to the corrected MP4 file.
|
548
|
+
"""
|
549
|
+
if not os.path.exists(inputFile):
|
550
|
+
raise FileNotFoundError(f"Input file not found: {inputFile}")
|
551
|
+
|
552
|
+
# Default output path
|
553
|
+
if outputFile == '':
|
554
|
+
base, _ = os.path.splitext(inputFile)
|
555
|
+
outputFile = f"{base}_corrected.mp4"
|
556
|
+
|
557
|
+
cmd = [
|
558
|
+
"ffmpeg", "-hide_banner", "-loglevel", "error", "-stats",
|
559
|
+
"-i", inputFile,
|
560
|
+
"-vf", f"lenscorrection=cx={cx}:cy={cy}:k1={k1}:k2={k2}",
|
561
|
+
"-c:v", "libx264", "-preset", "slow", f"-crf", str(crf),
|
562
|
+
"-pix_fmt", "yuv420p",
|
563
|
+
"-c:a", "aac", "-b:a", "192k",
|
564
|
+
"-movflags", "+faststart",
|
565
|
+
outputFile
|
566
|
+
]
|
567
|
+
|
568
|
+
subprocess.run(cmd, check=True)
|
569
|
+
return outputFile
|
570
|
+
|
571
|
+
|
572
|
+
def formatStringBlock(input_str: str, max_chars: int = 70) -> str:
|
532
573
|
'''
|
533
574
|
This function takes a string and formats it into a block of text
|
534
575
|
with a maximum number of characters per line.
|
@@ -558,8 +599,7 @@ def formatStringBlock(input_str, max_chars=70):
|
|
558
599
|
|
559
600
|
|
560
601
|
|
561
|
-
|
562
|
-
def downloadFile(url, save_path, exists_action='resume', num_connections=5, v=False):
|
602
|
+
def downloadFile(url: str, save_path: str, exists_action: str = 'resume', num_connections: int = 5, v: bool = False) -> None:
|
563
603
|
if v:
|
564
604
|
print(f"\ndownloading {url}")
|
565
605
|
fname = getFileBaseName(url, extension=True)
|
@@ -573,13 +613,17 @@ def downloadFile(url, save_path, exists_action='resume', num_connections=5, v=Fa
|
|
573
613
|
if os.path.exists(save_fname):
|
574
614
|
if exists_action == 'skip':
|
575
615
|
if v:
|
576
|
-
print(f"
|
616
|
+
print(f"file exists, skipping: {save_fname}")
|
577
617
|
return
|
578
618
|
elif exists_action == 'overwrite':
|
579
619
|
os.remove(save_fname)
|
580
620
|
# 'resume' is handled below
|
581
621
|
|
582
|
-
# Get file size
|
622
|
+
# Get file size (suppress urllib3 warnings when v=False)
|
623
|
+
import urllib3
|
624
|
+
if not v:
|
625
|
+
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
626
|
+
|
583
627
|
response = requests.head(url)
|
584
628
|
file_size = int(response.headers.get('content-length', 0))
|
585
629
|
|
@@ -589,7 +633,7 @@ def downloadFile(url, save_path, exists_action='resume', num_connections=5, v=Fa
|
|
589
633
|
initial_pos = os.path.getsize(save_fname)
|
590
634
|
if initial_pos >= file_size:
|
591
635
|
if v:
|
592
|
-
print(f"
|
636
|
+
print(f"file already completed: {save_fname}")
|
593
637
|
return
|
594
638
|
|
595
639
|
# Calculate chunk sizes
|
@@ -609,14 +653,22 @@ def downloadFile(url, save_path, exists_action='resume', num_connections=5, v=Fa
|
|
609
653
|
executor.submit(downloadChunk, url, start, end, temp_files[i])
|
610
654
|
)
|
611
655
|
|
612
|
-
# Wait for all downloads to complete with progress bar
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
656
|
+
# Wait for all downloads to complete with progress bar (conditionally show progress)
|
657
|
+
if v:
|
658
|
+
with tqdm(total=file_size-initial_pos, initial=initial_pos, unit='B',
|
659
|
+
unit_scale=True, desc=fname) as pbar:
|
660
|
+
completed = initial_pos
|
661
|
+
while completed < file_size:
|
662
|
+
current = sum(os.path.getsize(f) for f in temp_files if os.path.exists(f))
|
663
|
+
pbar.update(current - completed)
|
664
|
+
completed = current
|
665
|
+
else:
|
666
|
+
# Wait silently without progress bar
|
667
|
+
while True:
|
617
668
|
current = sum(os.path.getsize(f) for f in temp_files if os.path.exists(f))
|
618
|
-
|
619
|
-
|
669
|
+
if current >= file_size - initial_pos:
|
670
|
+
break
|
671
|
+
time.sleep(0.1)
|
620
672
|
|
621
673
|
# Merge chunks
|
622
674
|
with open(save_fname, 'ab' if initial_pos > 0 else 'wb') as outfile:
|
@@ -627,6 +679,7 @@ def downloadFile(url, save_path, exists_action='resume', num_connections=5, v=Fa
|
|
627
679
|
os.remove(temp_file)
|
628
680
|
|
629
681
|
|
682
|
+
|
630
683
|
def mergeRasterTiles(tileList:list, outFile:str) -> str:
|
631
684
|
'''
|
632
685
|
Merge raster tiles into one raster file
|
@@ -649,7 +702,7 @@ def systemPlatform() -> str:
|
|
649
702
|
'''
|
650
703
|
return platform.system()
|
651
704
|
|
652
|
-
def progressBar(count, total, message=""):
|
705
|
+
def progressBar(count: int, total: int, message: str = "") -> None:
|
653
706
|
percent = int(count / total * 100)
|
654
707
|
filled = int(percent / 2)
|
655
708
|
bar = '█' * filled + '░' * (50 - filled)
|
@@ -669,7 +722,7 @@ def fileCount(path:str = "./", extension:str = ".*", v:bool = True) -> int:
|
|
669
722
|
print(f'> there are {count} {extension if not extension ==".*" else ""} files in {path}')
|
670
723
|
return count
|
671
724
|
|
672
|
-
def resampleRaster(inFile:str, outFile:str, resolution:float, dstSRS = None, resamplingMethod = 'bilinear', replaceOutput:bool = True, v:bool = True) -> str:
|
725
|
+
def resampleRaster(inFile:str, outFile:str, resolution:float, dstSRS = None, resamplingMethod = 'bilinear', replaceOutput:bool = True, v:bool = True) -> Optional[str]:
|
673
726
|
'''
|
674
727
|
Resample a raster file
|
675
728
|
inFile: input raster file
|
@@ -740,7 +793,7 @@ def watchFileCount(path:str="./", extension:str = ".*", interval:float = 0.2, du
|
|
740
793
|
return None
|
741
794
|
|
742
795
|
|
743
|
-
def pythonVariable(filename, option, variable=None):
|
796
|
+
def pythonVariable(filename: str, option: str, variable: Any = None) -> Any:
|
744
797
|
'''
|
745
798
|
option: save, load or open
|
746
799
|
|
@@ -774,7 +827,7 @@ def listFolders(path:str) -> list:
|
|
774
827
|
else:
|
775
828
|
return []
|
776
829
|
|
777
|
-
def readFrom(filename, decode_codec = None, v=False):
|
830
|
+
def readFrom(filename: str, decode_codec: Optional[str] = None, v: bool = False) -> Any:
|
778
831
|
'''
|
779
832
|
a function to read ascii files
|
780
833
|
'''
|
@@ -793,16 +846,16 @@ def readFrom(filename, decode_codec = None, v=False):
|
|
793
846
|
|
794
847
|
|
795
848
|
def pointsToGeodataframe(
|
796
|
-
rowList,
|
797
|
-
columnNames,
|
798
|
-
latIndex,
|
799
|
-
lonIndex,
|
800
|
-
auth = "EPSG",
|
801
|
-
code = "4326",
|
802
|
-
outShape = "",
|
803
|
-
format = "gpkg",
|
804
|
-
v = False,
|
805
|
-
includeLatLon = True ) -> geopandas.GeoDataFrame:
|
849
|
+
rowList: list,
|
850
|
+
columnNames: list,
|
851
|
+
latIndex: int,
|
852
|
+
lonIndex: int,
|
853
|
+
auth: str = "EPSG",
|
854
|
+
code: str = "4326",
|
855
|
+
outShape: str = "",
|
856
|
+
format: str = "gpkg",
|
857
|
+
v: bool = False,
|
858
|
+
includeLatLon: bool = True ) -> geopandas.GeoDataFrame:
|
806
859
|
df = pandas.DataFrame(rowList, columns = columnNames)
|
807
860
|
geometry = [
|
808
861
|
Point(row[lonIndex], row[latIndex]) for row in rowList
|
@@ -824,10 +877,10 @@ def pointsToGeodataframe(
|
|
824
877
|
return gdf
|
825
878
|
|
826
879
|
|
827
|
-
def readFile(filename, decode_codec = None, v=False):
|
880
|
+
def readFile(filename: str, decode_codec: Optional[str] = None, v: bool = False) -> Any:
|
828
881
|
return readFrom(filename, decode_codec, v)
|
829
882
|
|
830
|
-
def writeTo(filename, file_text, encode_codec = None, v=False) -> bool:
|
883
|
+
def writeTo(filename: str, file_text: Any, encode_codec: Optional[str] = None, v: bool = False) -> bool:
|
831
884
|
'''
|
832
885
|
a function to write ascii files
|
833
886
|
'''
|
@@ -846,13 +899,13 @@ def writeTo(filename, file_text, encode_codec = None, v=False) -> bool:
|
|
846
899
|
if v: print("\t> wrote {0}".format(getFileBaseName(filename, extension=True)))
|
847
900
|
return True
|
848
901
|
|
849
|
-
def writeToFile(filename, file_text, encode_codec = None, v=False) -> bool:
|
902
|
+
def writeToFile(filename: str, file_text: Any, encode_codec: Optional[str] = None, v: bool = False) -> bool:
|
850
903
|
return writeTo(filename, file_text, encode_codec, v)
|
851
904
|
|
852
|
-
def writeFile(filename, file_text, encode_codec = None, v=False) -> bool:
|
905
|
+
def writeFile(filename: str, file_text: Any, encode_codec: Optional[str] = None, v: bool = False) -> bool:
|
853
906
|
return writeTo(filename, file_text, encode_codec, v)
|
854
907
|
|
855
|
-
def createPath(pathName, v = False):
|
908
|
+
def createPath(pathName: str, v: bool = False) -> str:
|
856
909
|
'''
|
857
910
|
this function creates a directory if it does not exist
|
858
911
|
pathName: the path to create
|
@@ -871,7 +924,7 @@ def createPath(pathName, v = False):
|
|
871
924
|
return pathName
|
872
925
|
|
873
926
|
|
874
|
-
def renameNetCDFvariable(input_file: str, output_file: str, old_var_name: str, new_var_name: str, v = False) -> None:
|
927
|
+
def renameNetCDFvariable(input_file: str, output_file: str, old_var_name: str, new_var_name: str, v: bool = False) -> None:
|
875
928
|
"""
|
876
929
|
Renames a variable in a NetCDF file using CDO if it exists.
|
877
930
|
If the variable does not exist, the file is copied without modification.
|
@@ -907,7 +960,7 @@ def renameNetCDFvariable(input_file: str, output_file: str, old_var_name: str, n
|
|
907
960
|
print(f"Error: {e.stderr}")
|
908
961
|
|
909
962
|
|
910
|
-
def compressTo7z(input_dir: str, output_file: str, compressionLevel: int = 4, excludeExt: list = None, v: bool = False) -> None:
|
963
|
+
def compressTo7z(input_dir: str, output_file: str, compressionLevel: int = 4, excludeExt: Optional[list] = None, v: bool = False) -> None:
|
911
964
|
"""
|
912
965
|
Compresses the contents of a directory to a .7z archive with maximum compression.
|
913
966
|
|
@@ -1210,8 +1263,8 @@ def ignoreWarnings(ignore:bool = True, v:bool = False) -> None:
|
|
1210
1263
|
return None
|
1211
1264
|
|
1212
1265
|
|
1213
|
-
def createGrid(topLeft: list = None, bottomRight: list = None, resolution: float = None,
|
1214
|
-
inputShape: str = None, crs: str = "EPSG:4326", saveVector: str = None) -> geopandas.GeoDataFrame:
|
1266
|
+
def createGrid(topLeft: Optional[list] = None, bottomRight: Optional[list] = None, resolution: Optional[float] = None,
|
1267
|
+
inputShape: Optional[str] = None, crs: str = "EPSG:4326", saveVector: Optional[str] = None) -> geopandas.GeoDataFrame:
|
1215
1268
|
'''
|
1216
1269
|
This function creates a grid of polygons based on either a shapefile or corner coordinates
|
1217
1270
|
|
@@ -1341,7 +1394,7 @@ def netcdfVariableDimensions(ncFile: str, variable: str) -> dict:
|
|
1341
1394
|
|
1342
1395
|
return bands_info
|
1343
1396
|
|
1344
|
-
def netcdfExportTif(ncFile: str, variable: str, outputFile: Optional[str] = None, band: int = None, v:bool = True) -> gdal.Dataset:
|
1397
|
+
def netcdfExportTif(ncFile: str, variable: str, outputFile: Optional[str] = None, band: Optional[int] = None, v:bool = True) -> gdal.Dataset:
|
1345
1398
|
'''
|
1346
1399
|
Export a variable from a NetCDF file to a GeoTiff file
|
1347
1400
|
ncFile: NetCDF file
|
@@ -1401,8 +1454,8 @@ def netcdfSumMaps(ncFiles:list, variable:str, band:int = 1) -> numpy.ndarray:
|
|
1401
1454
|
def tiffWriteArray(array: numpy.ndarray, outputFile: str,
|
1402
1455
|
geoTransform: tuple = (0, 1, 0, 0, 0, -1),
|
1403
1456
|
projection: str = 'EPSG:4326',
|
1404
|
-
noData:float = None,
|
1405
|
-
v:bool = False) -> gdal.Dataset:
|
1457
|
+
noData: Optional[float] = None,
|
1458
|
+
v: bool = False) -> gdal.Dataset:
|
1406
1459
|
'''
|
1407
1460
|
Write a numpy array to a GeoTIFF file
|
1408
1461
|
array : numpy array to write
|
@@ -1448,7 +1501,7 @@ def copyFile(source:str, destination:str, v:bool = True) -> None:
|
|
1448
1501
|
if v: print(f'> {source} copied to \t - {destination}')
|
1449
1502
|
|
1450
1503
|
|
1451
|
-
def copyDirectory(source:str, destination:str, recursive = True, v:bool = True, filter = []) -> None:
|
1504
|
+
def copyDirectory(source:str, destination:str, recursive: bool = True, v:bool = True, filter: list = []) -> None:
|
1452
1505
|
'''
|
1453
1506
|
Copy a directory from source to destination
|
1454
1507
|
source: source directory
|
@@ -1499,7 +1552,7 @@ def copyFolder(source:str, destination:str, v:bool = True) -> None:
|
|
1499
1552
|
copyDirectory(source, destination, v=v)
|
1500
1553
|
|
1501
1554
|
|
1502
|
-
def convertCoordinates(lon, lat, srcEPSG, dstCRS) -> tuple:
|
1555
|
+
def convertCoordinates(lon: float, lat: float, srcEPSG: str, dstCRS: str) -> tuple:
|
1503
1556
|
"""
|
1504
1557
|
this function converts coordinates from one CRS to another
|
1505
1558
|
|
@@ -1516,7 +1569,7 @@ def convertCoordinates(lon, lat, srcEPSG, dstCRS) -> tuple:
|
|
1516
1569
|
return (new_lon, new_lat)
|
1517
1570
|
|
1518
1571
|
|
1519
|
-
def extractRasterValue(rasterPath: str, lat: float, lon: float, coordProj: str = 'EPSG:4326') -> float:
|
1572
|
+
def extractRasterValue(rasterPath: str, lat: float, lon: float, coordProj: str = 'EPSG:4326') -> Optional[float]:
|
1520
1573
|
"""
|
1521
1574
|
Extract raster value at given coordinates.
|
1522
1575
|
|
@@ -1561,7 +1614,7 @@ def extractRasterValue(rasterPath: str, lat: float, lon: float, coordProj: str =
|
|
1561
1614
|
return float(value)
|
1562
1615
|
|
1563
1616
|
|
1564
|
-
def getRasterValue(rasterPath: str, lat: float, lon: float, coordProj: str = 'EPSG:4326') -> float:
|
1617
|
+
def getRasterValue(rasterPath: str, lat: float, lon: float, coordProj: str = 'EPSG:4326') -> Optional[float]:
|
1565
1618
|
'''
|
1566
1619
|
this function is a wrapper for extractRasterValue
|
1567
1620
|
'''
|
@@ -1588,9 +1641,9 @@ def showProgress(count: int, end: int, message: str, barLength: int = 100) -> No
|
|
1588
1641
|
percentStr = f'{percent:03.1f}'
|
1589
1642
|
filled = int(barLength * count / end)
|
1590
1643
|
bar = '█' * filled + '░' * (barLength - filled)
|
1591
|
-
print(f'\r{bar} {percentStr}% [{count}/{end}]
|
1644
|
+
print(f'\r{message} |{bar}| {percentStr}% [{count}/{end}]', end='', flush=True)
|
1592
1645
|
if count == end:
|
1593
|
-
print(f'\r{bar} {percentStr}% [{count}/{end}] ', end='', flush=True)
|
1646
|
+
print(f'\r{message} |{bar}| {percentStr}% [{count}/{end}] ', end='', flush=True)
|
1594
1647
|
print()
|
1595
1648
|
|
1596
1649
|
|
@@ -1621,7 +1674,7 @@ def dualProgress(primaryCount: int, primaryEnd: int,
|
|
1621
1674
|
print(f'\r{bar} {formattedPrimaryPercent.rjust(6)}% | {formattedSecondaryPercent.rjust(6)}% ', end='', flush=True)
|
1622
1675
|
|
1623
1676
|
|
1624
|
-
def listAllFiles(folder, extension="*"):
|
1677
|
+
def listAllFiles(folder: str, extension: str = "*") -> list:
|
1625
1678
|
list_of_files = []
|
1626
1679
|
# Getting the current work directory (cwd)
|
1627
1680
|
thisdir = folder
|
@@ -1643,7 +1696,7 @@ def listAllFiles(folder, extension="*"):
|
|
1643
1696
|
return list_of_files
|
1644
1697
|
|
1645
1698
|
|
1646
|
-
def clipFeatures(inputFeaturePath:str, boundaryFeature:str, outputFeature:str, keepOnlyTypes = None, v = False) -> geopandas.GeoDataFrame:
|
1699
|
+
def clipFeatures(inputFeaturePath:str, boundaryFeature:str, outputFeature:str, keepOnlyTypes: Optional[list] = None, v: bool = False) -> geopandas.GeoDataFrame:
|
1647
1700
|
'''
|
1648
1701
|
keepOnlyTypes = ['MultiPolygon', 'Polygon', 'Point', etc]
|
1649
1702
|
|
@@ -2023,7 +2076,7 @@ def getTimeseriesStats(data:pandas.DataFrame, observed:Optional[str] = None, sim
|
|
2023
2076
|
|
2024
2077
|
return stats
|
2025
2078
|
|
2026
|
-
def readSWATPlusOutputs(filePath: str, column: Optional[str] = None, unit: Optional[int] = None, gis_id: Optional[int] = None, name: Optional[str] = None):
|
2079
|
+
def readSWATPlusOutputs(filePath: str, column: Optional[str] = None, unit: Optional[int] = None, gis_id: Optional[int] = None, name: Optional[str] = None) -> Optional[pandas.DataFrame]:
|
2027
2080
|
'''
|
2028
2081
|
Read SWAT+ output files and return a pandas DataFrame with proper date handling
|
2029
2082
|
and optional filtering capabilities.
|
@@ -1,11 +1,11 @@
|
|
1
1
|
ccfx/__init__.py,sha256=UK62VcGS84SJyGVg1bK4FltZj7OkpdoyhoFWeXcKsX0,144
|
2
|
-
ccfx/ccfx.py,sha256=
|
2
|
+
ccfx/ccfx.py,sha256=doZ9tSJWRBAC3TKd6joThjDpivc6ef3bd1U5FuOVVh0,78988
|
3
3
|
ccfx/excel.py,sha256=vm_cm4huKKx4_Nstr5neJzhBLmoZjg8qxjzz4hcF5hg,4754
|
4
4
|
ccfx/mssqlConnection.py,sha256=C3HxzgZHmHy_de9EbMaXzR8NrkJxwHc8a00qzxQu_gs,8984
|
5
5
|
ccfx/sqliteConnection.py,sha256=pOT9BBEAcm2kmoS0yBkUi4m9srQVe62J4xG5bnddvis,16207
|
6
6
|
ccfx/word.py,sha256=AGa64jX5Zl5qotZh5L0QmrsjTnktIBhmj_ByRKZ88vw,3061
|
7
|
-
ccfx-1.0.
|
8
|
-
ccfx-1.0.
|
9
|
-
ccfx-1.0.
|
10
|
-
ccfx-1.0.
|
11
|
-
ccfx-1.0.
|
7
|
+
ccfx-1.0.9.dist-info/licenses/LICENSE,sha256=EuxaawJg_OOCLfikkCGgfXPZmxR-x_5PH7_2e9M-3eA,1099
|
8
|
+
ccfx-1.0.9.dist-info/METADATA,sha256=asJO3MTzoPwEhrQRcacRz1_GOIlD45X0tFu9tjb9-QE,11282
|
9
|
+
ccfx-1.0.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
10
|
+
ccfx-1.0.9.dist-info/top_level.txt,sha256=_cSvSA1WX2K8TgoV3iBJUdUZZqMKJbOPLNnKLYSLHaw,5
|
11
|
+
ccfx-1.0.9.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|