ccfx 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.
- ccfx/ccfx.py +223 -6
- ccfx-1.1.1.dist-info/METADATA +595 -0
- {ccfx-1.0.9.dist-info → ccfx-1.1.1.dist-info}/RECORD +6 -6
- ccfx-1.0.9.dist-info/METADATA +0 -181
- {ccfx-1.0.9.dist-info → ccfx-1.1.1.dist-info}/WHEEL +0 -0
- {ccfx-1.0.9.dist-info → ccfx-1.1.1.dist-info}/licenses/LICENSE +0 -0
- {ccfx-1.0.9.dist-info → ccfx-1.1.1.dist-info}/top_level.txt +0 -0
ccfx/ccfx.py
CHANGED
|
@@ -22,7 +22,6 @@ import platform
|
|
|
22
22
|
import zipfile
|
|
23
23
|
import pickle
|
|
24
24
|
import time
|
|
25
|
-
from shapely.geometry import box, Point
|
|
26
25
|
import geopandas, pandas
|
|
27
26
|
from collections import defaultdict
|
|
28
27
|
import py7zr
|
|
@@ -38,6 +37,12 @@ import yt_dlp
|
|
|
38
37
|
from typing import Optional, Any
|
|
39
38
|
from datetime import datetime, timedelta
|
|
40
39
|
from PIL import Image
|
|
40
|
+
import scipy as scipy
|
|
41
|
+
from shapely.geometry import LineString, Polygon, MultiPolygon, box, Point
|
|
42
|
+
from shapely.ops import polygonize, unary_union
|
|
43
|
+
import rasterio
|
|
44
|
+
from rasterio import features
|
|
45
|
+
from rasterio.transform import from_bounds
|
|
41
46
|
|
|
42
47
|
# functions
|
|
43
48
|
def listFiles(path: str, ext: Optional[str] = None) -> list:
|
|
@@ -568,6 +573,14 @@ def correctFisheye(inputFile: str, outputFile: str = '',
|
|
|
568
573
|
subprocess.run(cmd, check=True)
|
|
569
574
|
return outputFile
|
|
570
575
|
|
|
576
|
+
def correctLens(inputFile: str, outputFile: str = '',
|
|
577
|
+
k1: float = -0.1, k2: float = 0.05,
|
|
578
|
+
cx: float = 0.5, cy: float = 0.5,
|
|
579
|
+
crf: int = 20) -> str:
|
|
580
|
+
"""
|
|
581
|
+
Alias for correctFisheye
|
|
582
|
+
"""
|
|
583
|
+
return correctFisheye(inputFile, outputFile, k1, k2, cx, cy, crf)
|
|
571
584
|
|
|
572
585
|
def formatStringBlock(input_str: str, max_chars: int = 70) -> str:
|
|
573
586
|
'''
|
|
@@ -680,6 +693,76 @@ def downloadFile(url: str, save_path: str, exists_action: str = 'resume', num_co
|
|
|
680
693
|
|
|
681
694
|
|
|
682
695
|
|
|
696
|
+
|
|
697
|
+
def createPolygonFromOuterPoints(pointsGdf: geopandas.GeoDataFrame, alpha: float = 1.6, keepHoles: bool = False) -> geopandas.GeoDataFrame:
|
|
698
|
+
"""
|
|
699
|
+
Concave hull (alpha-shape) from points.
|
|
700
|
+
alpha: larger -> tighter/more detailed; too large can fragment.
|
|
701
|
+
keepHoles: keep interior holes if True, otherwise drop them.
|
|
702
|
+
"""
|
|
703
|
+
|
|
704
|
+
pointsGdf = pointsGdf[pointsGdf.geometry.type.eq("Point") & pointsGdf.geometry.notna()]
|
|
705
|
+
if pointsGdf.empty or pointsGdf.geometry.nunique() < 3:
|
|
706
|
+
raise ValueError("need at least three distinct points")
|
|
707
|
+
|
|
708
|
+
# coordinates (N, 2)
|
|
709
|
+
coords = numpy.array([(g.x, g.y) for g in pointsGdf.geometry])
|
|
710
|
+
|
|
711
|
+
tri = scipy.spatial.Delaunay(coords)
|
|
712
|
+
simplices = tri.simplices # (M, 3) indices into coords
|
|
713
|
+
triPts = coords[simplices] # (M, 3, 2)
|
|
714
|
+
|
|
715
|
+
# side lengths
|
|
716
|
+
a = numpy.linalg.norm(triPts[:, 1] - triPts[:, 2], axis=1)
|
|
717
|
+
b = numpy.linalg.norm(triPts[:, 0] - triPts[:, 2], axis=1)
|
|
718
|
+
c = numpy.linalg.norm(triPts[:, 0] - triPts[:, 1], axis=1)
|
|
719
|
+
|
|
720
|
+
s = (a + b + c) / 2.0
|
|
721
|
+
# heron area, guard against tiny/negative due to fp error
|
|
722
|
+
areaSq = numpy.maximum(s * (s - a) * (s - b) * (s - c), 0.0)
|
|
723
|
+
area = numpy.sqrt(areaSq)
|
|
724
|
+
valid = area > 0.0
|
|
725
|
+
if not numpy.any(valid):
|
|
726
|
+
hull = pointsGdf.unary_union.convex_hull
|
|
727
|
+
return geopandas.GeoDataFrame({"name": ["outerBoundary"]}, geometry=[hull], crs=pointsGdf.crs)
|
|
728
|
+
|
|
729
|
+
circumradius = (a * b * c) / (4.0 * area)
|
|
730
|
+
keep = valid & (circumradius < (1.0 / alpha))
|
|
731
|
+
keptSimplices = simplices[keep]
|
|
732
|
+
if keptSimplices.size == 0:
|
|
733
|
+
hull = pointsGdf.unary_union.convex_hull
|
|
734
|
+
return geopandas.GeoDataFrame({"name": ["outerBoundary"]}, geometry=[hull], crs=pointsGdf.crs)
|
|
735
|
+
|
|
736
|
+
# count triangle edges; boundary edges appear exactly once
|
|
737
|
+
edgeCounts: dict[tuple[int, int], int] = {}
|
|
738
|
+
for i0, i1, i2 in keptSimplices:
|
|
739
|
+
for e in ((i0, i1), (i1, i2), (i2, i0)):
|
|
740
|
+
key = (e[0], e[1]) if e[0] < e[1] else (e[1], e[0])
|
|
741
|
+
edgeCounts[key] = edgeCounts.get(key, 0) + 1
|
|
742
|
+
|
|
743
|
+
boundaryLines = [
|
|
744
|
+
LineString([coords[i], coords[j]])
|
|
745
|
+
for (i, j), count in edgeCounts.items()
|
|
746
|
+
if count == 1
|
|
747
|
+
]
|
|
748
|
+
if not boundaryLines:
|
|
749
|
+
hull = pointsGdf.unary_union.convex_hull
|
|
750
|
+
return geopandas.GeoDataFrame({"name": ["outerBoundary"]}, geometry=[hull], crs=pointsGdf.crs)
|
|
751
|
+
|
|
752
|
+
polygons = list(polygonize(boundaryLines))
|
|
753
|
+
if not polygons:
|
|
754
|
+
hull = pointsGdf.unary_union.convex_hull
|
|
755
|
+
return geopandas.GeoDataFrame({"name": ["outerBoundary"]}, geometry=[hull], crs=pointsGdf.crs)
|
|
756
|
+
|
|
757
|
+
merged = unary_union(polygons)
|
|
758
|
+
if isinstance(merged, MultiPolygon):
|
|
759
|
+
merged = max(merged.geoms, key=lambda g: g.area)
|
|
760
|
+
if not keepHoles and isinstance(merged, Polygon):
|
|
761
|
+
merged = Polygon(merged.exterior)
|
|
762
|
+
|
|
763
|
+
return geopandas.GeoDataFrame({"name": ["outerBoundary"]}, geometry=[merged], crs=pointsGdf.crs)
|
|
764
|
+
|
|
765
|
+
|
|
683
766
|
def mergeRasterTiles(tileList:list, outFile:str) -> str:
|
|
684
767
|
'''
|
|
685
768
|
Merge raster tiles into one raster file
|
|
@@ -1030,6 +1113,116 @@ def extractCompressedFile(inputFile: str, outputDir: str, v: bool = False) -> No
|
|
|
1030
1113
|
"""
|
|
1031
1114
|
uncompress(inputFile, outputDir, v)
|
|
1032
1115
|
|
|
1116
|
+
|
|
1117
|
+
|
|
1118
|
+
def rasterizeGDF(gdf: geopandas.GeoDataFrame, valueField: str, outRasterFN: str, resolution: float, isCOG: bool = True, allTouched: bool = False, profileOverrides: dict | None = None) -> str:
|
|
1119
|
+
"""
|
|
1120
|
+
Rasterize a GeoDataFrame to GeoTIFF/COG.
|
|
1121
|
+
|
|
1122
|
+
Parameters
|
|
1123
|
+
----------
|
|
1124
|
+
gdf : geopandas.GeoDataFrame
|
|
1125
|
+
valueField : str
|
|
1126
|
+
Column to burn as pixel values.
|
|
1127
|
+
outRasterFN : str
|
|
1128
|
+
resolution : float
|
|
1129
|
+
Pixel size in CRS units (assumes a projected CRS).
|
|
1130
|
+
isCOG : bool, default True
|
|
1131
|
+
If True, writes Cloud-Optimized GeoTIFF. Otherwise plain GeoTIFF.
|
|
1132
|
+
allTouched : bool, default False
|
|
1133
|
+
Pass-through to rasterio.features.rasterize.
|
|
1134
|
+
profileOverrides : dict | None
|
|
1135
|
+
Extra GDAL profile options (e.g., {"compress": "LZW"}). Overrides defaults.
|
|
1136
|
+
|
|
1137
|
+
Returns
|
|
1138
|
+
-------
|
|
1139
|
+
str
|
|
1140
|
+
The path written to (outRasterFN).
|
|
1141
|
+
"""
|
|
1142
|
+
# basic checks
|
|
1143
|
+
if gdf is None or len(gdf) == 0 or gdf.geometry.isna().all():
|
|
1144
|
+
raise ValueError("gdf is empty or has no valid geometries.")
|
|
1145
|
+
if gdf.crs is None:
|
|
1146
|
+
raise ValueError("gdf must have a defined CRS.")
|
|
1147
|
+
if resolution <= 0:
|
|
1148
|
+
raise ValueError("resolution must be > 0.")
|
|
1149
|
+
if valueField not in gdf.columns:
|
|
1150
|
+
raise ValueError(f"valueField '{valueField}' not found in gdf.")
|
|
1151
|
+
|
|
1152
|
+
# compute raster shape + transform
|
|
1153
|
+
bounds = gdf.total_bounds # (minx, miny, maxx, maxy)
|
|
1154
|
+
width = int(numpy.ceil((bounds[2] - bounds[0]) / float(resolution)))
|
|
1155
|
+
height = int(numpy.ceil((bounds[3] - bounds[1]) / float(resolution)))
|
|
1156
|
+
if width < 1 or height < 1:
|
|
1157
|
+
raise ValueError("computed raster dimensions are invalid (check resolution and bounds).")
|
|
1158
|
+
|
|
1159
|
+
transform = from_bounds(bounds[0], bounds[1], bounds[2], bounds[3], width, height)
|
|
1160
|
+
|
|
1161
|
+
# infer dtype + nodata
|
|
1162
|
+
pandasDtype = gdf[valueField].dtype
|
|
1163
|
+
if numpy.issubdtype(pandasDtype, numpy.floating):
|
|
1164
|
+
dtype = numpy.float32
|
|
1165
|
+
nodata = numpy.nan
|
|
1166
|
+
fillValue = numpy.nan
|
|
1167
|
+
elif numpy.issubdtype(pandasDtype, numpy.bool_):
|
|
1168
|
+
dtype = numpy.uint8
|
|
1169
|
+
nodata = 255 # sentinel for bool raster
|
|
1170
|
+
fillValue = nodata
|
|
1171
|
+
else:
|
|
1172
|
+
dtype = numpy.int32
|
|
1173
|
+
nodata = -9999
|
|
1174
|
+
fillValue = nodata
|
|
1175
|
+
|
|
1176
|
+
# prefill target array
|
|
1177
|
+
raster = numpy.full((height, width), fillValue, dtype=dtype)
|
|
1178
|
+
|
|
1179
|
+
# build shapes generator (ensure python scalars)
|
|
1180
|
+
shapes = ((geom, (None if numpy.isnan(val) else val) if isinstance(val, float) else int(val) if numpy.issubdtype(type(val), numpy.integer) else float(val))
|
|
1181
|
+
for geom, val in zip(gdf.geometry, gdf[valueField]))
|
|
1182
|
+
|
|
1183
|
+
# burn
|
|
1184
|
+
features.rasterize(
|
|
1185
|
+
shapes=shapes,
|
|
1186
|
+
out_shape=raster.shape,
|
|
1187
|
+
transform=transform,
|
|
1188
|
+
fill=fillValue,
|
|
1189
|
+
out=raster,
|
|
1190
|
+
all_touched=allTouched,
|
|
1191
|
+
dtype=dtype
|
|
1192
|
+
)
|
|
1193
|
+
|
|
1194
|
+
# default profile settings
|
|
1195
|
+
profile = {
|
|
1196
|
+
"driver": "COG" if isCOG else "GTiff",
|
|
1197
|
+
"height": raster.shape[0],
|
|
1198
|
+
"width": raster.shape[1],
|
|
1199
|
+
"count": 1,
|
|
1200
|
+
"dtype": raster.dtype,
|
|
1201
|
+
"crs": gdf.crs,
|
|
1202
|
+
"transform": transform,
|
|
1203
|
+
"nodata": nodata,
|
|
1204
|
+
}
|
|
1205
|
+
# sane compression defaults
|
|
1206
|
+
if isCOG:
|
|
1207
|
+
profile.setdefault("compress", "LZW")
|
|
1208
|
+
profile.setdefault("blocksize", 512) # GDAL COG option via rasterio
|
|
1209
|
+
# overviews are handled by the COG driver
|
|
1210
|
+
else:
|
|
1211
|
+
profile.setdefault("compress", "LZW"),
|
|
1212
|
+
profile.setdefault("tiled", True)
|
|
1213
|
+
profile.setdefault("blockxsize", 512)
|
|
1214
|
+
profile.setdefault("blockysize", 512)
|
|
1215
|
+
|
|
1216
|
+
if profileOverrides:
|
|
1217
|
+
profile.update(profileOverrides)
|
|
1218
|
+
|
|
1219
|
+
# write
|
|
1220
|
+
with rasterio.open(outRasterFN, "w", **profile) as dst:
|
|
1221
|
+
dst.write(raster, 1)
|
|
1222
|
+
|
|
1223
|
+
return outRasterFN
|
|
1224
|
+
|
|
1225
|
+
|
|
1033
1226
|
def moveDirectory(srcDir:str, destDir:str, v:bool = False) -> bool:
|
|
1034
1227
|
'''
|
|
1035
1228
|
this function moves all files from srcDir to destDir
|
|
@@ -1656,17 +1849,33 @@ def dualProgress(primaryCount: int, primaryEnd: int,
|
|
|
1656
1849
|
Bars are redrawn entirely each call.
|
|
1657
1850
|
'''
|
|
1658
1851
|
|
|
1852
|
+
darkBlock = '█'
|
|
1853
|
+
denseBlock = '▒'
|
|
1854
|
+
lightBlock = '░'
|
|
1855
|
+
emptyBlock = '-'
|
|
1856
|
+
|
|
1857
|
+
|
|
1858
|
+
|
|
1659
1859
|
primaryPercent = float(primaryCount / primaryEnd * 100) if primaryEnd > 0 else 100
|
|
1660
1860
|
secondaryPercent = float(secondaryCount / secondaryEnd * 100) if secondaryEnd > 0 else 100
|
|
1661
1861
|
|
|
1662
1862
|
filledPrimary = int(barLength * primaryCount / primaryEnd) if primaryEnd > 0 else barLength
|
|
1663
|
-
|
|
1863
|
+
filledShadow = int(barLength * secondaryCount / secondaryEnd) if secondaryEnd > 0 else barLength
|
|
1664
1864
|
|
|
1865
|
+
if filledShadow < filledPrimary:
|
|
1866
|
+
filledPrimary = filledPrimary - filledShadow
|
|
1867
|
+
filledSecondary = 0
|
|
1868
|
+
else:
|
|
1869
|
+
filledShadow = 0
|
|
1870
|
+
filledSecondary = int(barLength * secondaryCount / secondaryEnd) if secondaryEnd > 0 else barLength
|
|
1871
|
+
filledSecondary = filledSecondary - filledPrimary
|
|
1872
|
+
|
|
1873
|
+
shadowSection = filledShadow
|
|
1665
1874
|
startSection = filledPrimary
|
|
1666
1875
|
middleSection = filledSecondary
|
|
1667
|
-
endSection = barLength - startSection - middleSection
|
|
1876
|
+
endSection = barLength - startSection - middleSection - shadowSection
|
|
1668
1877
|
|
|
1669
|
-
bar =
|
|
1878
|
+
bar = denseBlock * shadowSection + darkBlock * startSection + lightBlock * middleSection + emptyBlock * endSection
|
|
1670
1879
|
formattedPrimaryPercent = f'{primaryPercent:03.1f}'
|
|
1671
1880
|
formattedSecondaryPercent = f'{secondaryPercent:03.1f}'
|
|
1672
1881
|
print(f'\r{bar} {formattedPrimaryPercent.rjust(6)}% | {formattedSecondaryPercent.rjust(6)}% | {message} ', end='', flush=True)
|
|
@@ -2076,7 +2285,7 @@ def getTimeseriesStats(data:pandas.DataFrame, observed:Optional[str] = None, sim
|
|
|
2076
2285
|
|
|
2077
2286
|
return stats
|
|
2078
2287
|
|
|
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]:
|
|
2288
|
+
def readSWATPlusOutputs(filePath: str, column: Optional[str] = None, unit: Optional[int] = None, gis_id: Optional[int] = None, name: Optional[str] = None, coerceNumeric: bool = True) -> Optional[pandas.DataFrame]:
|
|
2080
2289
|
'''
|
|
2081
2290
|
Read SWAT+ output files and return a pandas DataFrame with proper date handling
|
|
2082
2291
|
and optional filtering capabilities.
|
|
@@ -2138,8 +2347,15 @@ def readSWATPlusOutputs(filePath: str, column: Optional[str] = None, unit: Optio
|
|
|
2138
2347
|
# Convert all columns to numeric except 'name' (which is string)
|
|
2139
2348
|
for col in df.columns:
|
|
2140
2349
|
if col != 'name':
|
|
2141
|
-
|
|
2350
|
+
if coerceNumeric:
|
|
2351
|
+
df[col] = pandas.to_numeric(df[col], errors='coerce')
|
|
2142
2352
|
|
|
2353
|
+
if not coerceNumeric:
|
|
2354
|
+
# If not coercing to numeric, ensure date columns are numeric
|
|
2355
|
+
for mandatoryCol in ['yr', 'mon', 'day', 'gis_id']:
|
|
2356
|
+
if mandatoryCol in df.columns:
|
|
2357
|
+
df[mandatoryCol] = pandas.to_numeric(df[mandatoryCol], errors='coerce')
|
|
2358
|
+
|
|
2143
2359
|
# Create date column from yr, mon, day
|
|
2144
2360
|
try:
|
|
2145
2361
|
df['date'] = pandas.to_datetime(pandas.DataFrame({'year': df.yr, 'month': df.mon, 'day': df.day}))
|
|
@@ -2179,4 +2395,5 @@ def readSWATPlusOutputs(filePath: str, column: Optional[str] = None, unit: Optio
|
|
|
2179
2395
|
|
|
2180
2396
|
return df
|
|
2181
2397
|
|
|
2398
|
+
|
|
2182
2399
|
ignoreWarnings()
|
|
@@ -0,0 +1,595 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ccfx
|
|
3
|
+
Version: 1.1.1
|
|
4
|
+
Summary: This package simplifies regular common actions for quick prototyping in a user friendly way
|
|
5
|
+
Author-email: Celray James CHAWANDA <celray@chawanda.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/celray/ccfx
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Requires-Python: >=3.10
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Requires-Dist: netCDF4
|
|
14
|
+
Requires-Dist: yt_dlp
|
|
15
|
+
Requires-Dist: gdal
|
|
16
|
+
Requires-Dist: numpy
|
|
17
|
+
Requires-Dist: shapely
|
|
18
|
+
Requires-Dist: geopandas
|
|
19
|
+
Requires-Dist: pandas
|
|
20
|
+
Requires-Dist: xlsxwriter
|
|
21
|
+
Requires-Dist: pyodbc
|
|
22
|
+
Requires-Dist: sqlalchemy
|
|
23
|
+
Requires-Dist: python-docx
|
|
24
|
+
Requires-Dist: py7zr
|
|
25
|
+
Requires-Dist: mutagen
|
|
26
|
+
Requires-Dist: requests
|
|
27
|
+
Requires-Dist: tqdm
|
|
28
|
+
Requires-Dist: pillow
|
|
29
|
+
Requires-Dist: scipy
|
|
30
|
+
Requires-Dist: rasterio
|
|
31
|
+
Requires-Dist: matplotlib
|
|
32
|
+
Dynamic: license-file
|
|
33
|
+
|
|
34
|
+
# ccfx
|
|
35
|
+
|
|
36
|
+
[](https://badge.fury.io/py/ccfx)
|
|
37
|
+
[](https://opensource.org/licenses/MIT)
|
|
38
|
+
[](https://www.python.org/downloads/)
|
|
39
|
+
[](https://github.com/celray/ccfx/stargazers)
|
|
40
|
+
|
|
41
|
+
`ccfx` is a comprehensive Python package designed to streamline file and data management, geospatial analysis, NetCDF file processing, database interactions, document generation, and multimedia handling for rapid prototyping and development workflows.
|
|
42
|
+
|
|
43
|
+
## Table of Contents
|
|
44
|
+
|
|
45
|
+
- [Features](#features)
|
|
46
|
+
- [Installation](#installation)
|
|
47
|
+
- [Quick Start](#quick-start)
|
|
48
|
+
- [Dependencies](#dependencies)
|
|
49
|
+
- [Usage Examples](#usage-examples)
|
|
50
|
+
- [API Reference](#api-reference-selected-functions)
|
|
51
|
+
- [File Management](#file-management-ccfxpy)
|
|
52
|
+
- [Geospatial](#geospatial-ccfxpy)
|
|
53
|
+
- [NetCDF](#netcdf-ccfxpy)
|
|
54
|
+
- [Database](#database-mssqlconnectionpy-sqliteconnectionpy)
|
|
55
|
+
- [Document/Spreadsheet](#documentspreadsheet-wordpy-excelpy)
|
|
56
|
+
- [Multimedia & Web](#multimedia--web-ccfxpy)
|
|
57
|
+
- [Data Analysis & Utilities](#data-analysis--utilities-ccfxpy)
|
|
58
|
+
- [System Requirements](#system-requirements)
|
|
59
|
+
- [Contributing](#contributing)
|
|
60
|
+
- [Changelog](#changelog)
|
|
61
|
+
- [License](#license)
|
|
62
|
+
|
|
63
|
+
## Features
|
|
64
|
+
|
|
65
|
+
1. **File Management**:
|
|
66
|
+
* List, delete, move, copy, and count files/directories.
|
|
67
|
+
* Monitor file count over time.
|
|
68
|
+
* Save, load, and manage Python variables via pickle serialization.
|
|
69
|
+
* Compress directories to `.7z` archives.
|
|
70
|
+
* Read/write text files with encoding support.
|
|
71
|
+
* Download files from URLs with resume and multi-connection support.
|
|
72
|
+
|
|
73
|
+
2. **Geospatial Data Processing**:
|
|
74
|
+
* Read, write, clip (by extent/feature), resample, reproject, merge, and rasterize raster data (GeoTIFF, NetCDF).
|
|
75
|
+
* Read, write, and clip vector data (Shapefile, GeoPackage).
|
|
76
|
+
* Create grids of polygons based on shapefile boundaries.
|
|
77
|
+
* Convert coordinates between Coordinate Reference Systems (CRS).
|
|
78
|
+
* Extract raster values at specific coordinates.
|
|
79
|
+
* Convert point lists to GeoDataFrames.
|
|
80
|
+
* Get vector layer bounds.
|
|
81
|
+
|
|
82
|
+
3. **NetCDF File Handling**:
|
|
83
|
+
* List variables and dimensions.
|
|
84
|
+
* Export NetCDF variables to GeoTIFF format (single or multiple bands).
|
|
85
|
+
* Calculate sum and average maps from NetCDF data across multiple files.
|
|
86
|
+
* Rename variables using CDO (if available).
|
|
87
|
+
|
|
88
|
+
4. **Database Connectivity**:
|
|
89
|
+
* **MS SQL Server**: Connect, list databases/tables, read tables (including spatial data into GeoDataFrames), write DataFrames/GeoDataFrames to tables, drop tables.
|
|
90
|
+
* **SQLite**: Connect, create/rename/drop tables, read tables (as dict, specific columns), insert data (rows, dicts, partial dicts), update values, dump tables to CSV.
|
|
91
|
+
|
|
92
|
+
5. **Document & Spreadsheet Handling**:
|
|
93
|
+
* **Excel**: Create `.xlsx` files, add sheets, write data (including dates), set column widths, add scatter plot charts.
|
|
94
|
+
* **Word**: Create `.docx` files, add headings, paragraphs (with alignment), list items, formatted text (bold/italic), images, page breaks, set margins.
|
|
95
|
+
|
|
96
|
+
6. **Multimedia & Web**:
|
|
97
|
+
* Read and write MP3 metadata (ID3 tags), including album art.
|
|
98
|
+
* Download videos/audio from YouTube using `yt-dlp`.
|
|
99
|
+
|
|
100
|
+
7. **Data Analysis & Utilities**:
|
|
101
|
+
* Calculate timeseries statistics (NSE, KGE, PBIAS, LNSE, R2, RMSE, MAE, MSE, MAPE, alpha, beta) with resampling options.
|
|
102
|
+
* Display dynamic progress bars.
|
|
103
|
+
* Check system platform information.
|
|
104
|
+
* Enable or disable warnings programmatically.
|
|
105
|
+
* Set the working directory.
|
|
106
|
+
|
|
107
|
+
## Installation
|
|
108
|
+
|
|
109
|
+
Install `ccfx` via pip:
|
|
110
|
+
```bash
|
|
111
|
+
pip install ccfx
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
> **Note**: GDAL is a core dependency and may require additional system-level installation steps. See [System Requirements](#system-requirements) for details.
|
|
115
|
+
|
|
116
|
+
## Quick Start
|
|
117
|
+
|
|
118
|
+
Here's a simple example to get you started with `ccfx`:
|
|
119
|
+
|
|
120
|
+
```python
|
|
121
|
+
import ccfx
|
|
122
|
+
|
|
123
|
+
# File management
|
|
124
|
+
files = ccfx.listFiles("/path/to/directory", ext=".txt")
|
|
125
|
+
print(f"Found {len(files)} text files")
|
|
126
|
+
|
|
127
|
+
# Create and save data to Excel
|
|
128
|
+
excel_doc = ccfx.excel("output.xlsx")
|
|
129
|
+
excel_doc.create()
|
|
130
|
+
excel_doc.addSheet("MyData")
|
|
131
|
+
excel_doc.write("A1", "Hello, ccfx!")
|
|
132
|
+
excel_doc.save()
|
|
133
|
+
|
|
134
|
+
# Work with geospatial data
|
|
135
|
+
bounds = (-180, -90, 180, 90) # Global bounds
|
|
136
|
+
ccfx.clipRasterByExtent("input.tif", "clipped.tif", bounds)
|
|
137
|
+
|
|
138
|
+
# Download and process data
|
|
139
|
+
ccfx.downloadFile("https://example.com/data.zip", "./data.zip")
|
|
140
|
+
ccfx.compressTo7z("./my_folder", "archive.7z")
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Dependencies
|
|
144
|
+
|
|
145
|
+
`ccfx` relies on the following libraries (automatically installed with pip):
|
|
146
|
+
|
|
147
|
+
### Core Dependencies
|
|
148
|
+
* **gdal**: For geospatial raster and vector data manipulation.
|
|
149
|
+
* **numpy**: For array processing and numerical operations.
|
|
150
|
+
* **pandas**: For data manipulation and analysis.
|
|
151
|
+
* **geopandas**: Extends pandas to handle geospatial vector data.
|
|
152
|
+
* **shapely**: Provides geometric objects and operations.
|
|
153
|
+
* **netCDF4**: For working with NetCDF files.
|
|
154
|
+
* **rasterio**: Advanced raster I/O and processing.
|
|
155
|
+
|
|
156
|
+
### Document & Database
|
|
157
|
+
* **xlsxwriter**: For creating and writing Excel `.xlsx` files.
|
|
158
|
+
* **python-docx**: Enables creation and manipulation of Word `.docx` documents.
|
|
159
|
+
* **pyodbc**: Enables connectivity to databases through ODBC (e.g., MS SQL Server).
|
|
160
|
+
* **sqlalchemy**: Provides SQL toolkit and ORM features for database access.
|
|
161
|
+
|
|
162
|
+
### Utilities & Multimedia
|
|
163
|
+
* **py7zr**: For creating `.7z` archives.
|
|
164
|
+
* **mutagen**: For reading and writing MP3 metadata (ID3 tags).
|
|
165
|
+
* **requests**: For downloading files via HTTP.
|
|
166
|
+
* **tqdm**: For displaying progress bars.
|
|
167
|
+
* **yt-dlp**: For downloading YouTube content.
|
|
168
|
+
* **pillow**: For image processing.
|
|
169
|
+
* **scipy**: For scientific computing.
|
|
170
|
+
* **matplotlib**: For plotting and visualization.
|
|
171
|
+
|
|
172
|
+
## System Requirements
|
|
173
|
+
|
|
174
|
+
### Python Version
|
|
175
|
+
- **Python 3.10+** is required
|
|
176
|
+
|
|
177
|
+
### GDAL Installation
|
|
178
|
+
GDAL can be challenging to install depending on your operating system:
|
|
179
|
+
|
|
180
|
+
#### Windows
|
|
181
|
+
```bash
|
|
182
|
+
# Using conda (recommended)
|
|
183
|
+
conda install -c conda-forge gdal
|
|
184
|
+
|
|
185
|
+
# Or using pip with pre-compiled binaries
|
|
186
|
+
pip install GDAL
|
|
187
|
+
|
|
188
|
+
# Alternative: If struggling with GDAL installation on Windows
|
|
189
|
+
pip install gdal-installer
|
|
190
|
+
install-gdal
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
#### macOS
|
|
194
|
+
```bash
|
|
195
|
+
# Using Homebrew
|
|
196
|
+
brew install gdal
|
|
197
|
+
pip install gdal
|
|
198
|
+
|
|
199
|
+
# Using conda
|
|
200
|
+
conda install -c conda-forge gdal
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
#### Linux (Ubuntu/Debian)
|
|
204
|
+
```bash
|
|
205
|
+
# Install system dependencies
|
|
206
|
+
sudo apt-get update
|
|
207
|
+
sudo apt-get install gdal-bin libgdal-dev
|
|
208
|
+
|
|
209
|
+
# Install Python bindings
|
|
210
|
+
pip install gdal
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
#### Docker
|
|
214
|
+
For a hassle-free setup, consider using the official GDAL Docker images:
|
|
215
|
+
```bash
|
|
216
|
+
docker pull ghcr.io/osgeo/gdal:ubuntu-small-latest
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## Usage Examples
|
|
220
|
+
|
|
221
|
+
### File Management
|
|
222
|
+
```python
|
|
223
|
+
import ccfx
|
|
224
|
+
|
|
225
|
+
# List all Python files in a directory
|
|
226
|
+
python_files = ccfx.listFiles("/path/to/project", ext=".py")
|
|
227
|
+
|
|
228
|
+
# Create a backup directory and copy files
|
|
229
|
+
ccfx.createPath("/backup/location")
|
|
230
|
+
ccfx.copyDirectory("/source/dir", "/backup/location")
|
|
231
|
+
|
|
232
|
+
# Compress a directory
|
|
233
|
+
ccfx.compressTo7z("/data/folder", "backup.7z")
|
|
234
|
+
|
|
235
|
+
# Download a file with resume capability
|
|
236
|
+
ccfx.downloadFile(
|
|
237
|
+
"https://example.com/largefile.zip",
|
|
238
|
+
"downloaded_file.zip",
|
|
239
|
+
exists_action='resume'
|
|
240
|
+
)
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### Geospatial Data Processing
|
|
244
|
+
```python
|
|
245
|
+
import ccfx
|
|
246
|
+
|
|
247
|
+
# Clip a raster to a specific bounding box
|
|
248
|
+
bounds = (-74.0, 40.7, -73.9, 40.8) # NYC area
|
|
249
|
+
ccfx.clipRasterByExtent("satellite_image.tif", "nyc_clip.tif", bounds)
|
|
250
|
+
|
|
251
|
+
# Resample a raster to different resolution
|
|
252
|
+
ccfx.resampleRaster(
|
|
253
|
+
"high_res.tif",
|
|
254
|
+
"low_res.tif",
|
|
255
|
+
resolution=1000, # 1km resolution
|
|
256
|
+
resamplingMethod='bilinear'
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
# Convert coordinates between projections
|
|
260
|
+
lat, lon = 40.7128, -74.0060 # NYC coordinates
|
|
261
|
+
x, y = ccfx.convertCoordinates(lon, lat, "EPSG:4326", "EPSG:3857")
|
|
262
|
+
|
|
263
|
+
# Extract raster value at specific point
|
|
264
|
+
value = ccfx.extractRasterValue("elevation.tif", 40.7128, -74.0060)
|
|
265
|
+
print(f"Elevation at NYC: {value}")
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### Database Operations
|
|
269
|
+
```python
|
|
270
|
+
import ccfx
|
|
271
|
+
|
|
272
|
+
# SQLite operations
|
|
273
|
+
db = ccfx.sqliteConnection("my_data.db", connect=True)
|
|
274
|
+
db.createTable("users", ["id INTEGER PRIMARY KEY", "name TEXT", "email TEXT"])
|
|
275
|
+
db.insertDict("users", {"name": "John Doe", "email": "john@example.com"})
|
|
276
|
+
users = db.readTableAsDict("users")
|
|
277
|
+
db.closeConnection()
|
|
278
|
+
|
|
279
|
+
# MS SQL Server operations
|
|
280
|
+
mssql = ccfx.mssql_connection("server", "username", "password", "driver")
|
|
281
|
+
mssql.connect()
|
|
282
|
+
databases = mssql.listDatabases()
|
|
283
|
+
df = mssql.readTable("MyDatabase", "MyTable")
|
|
284
|
+
mssql.close()
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
### Document Generation
|
|
288
|
+
```python
|
|
289
|
+
import ccfx
|
|
290
|
+
|
|
291
|
+
# Create Excel spreadsheet
|
|
292
|
+
excel = ccfx.excel("report.xlsx")
|
|
293
|
+
excel.create()
|
|
294
|
+
excel.addSheet("Sales Data")
|
|
295
|
+
excel.write("A1", "Product")
|
|
296
|
+
excel.write("B1", "Revenue")
|
|
297
|
+
excel.writeColumn("A", ["Product A", "Product B", "Product C"], start_row=2)
|
|
298
|
+
excel.writeColumn("B", [1000, 1500, 800], start_row=2)
|
|
299
|
+
excel.save()
|
|
300
|
+
|
|
301
|
+
# Create Word document
|
|
302
|
+
doc = ccfx.word_document("report.docx")
|
|
303
|
+
doc.addHeading("Monthly Report", level=1)
|
|
304
|
+
doc.addParagraph("This report summarizes our monthly performance.")
|
|
305
|
+
doc.addListItem("Revenue increased by 15%")
|
|
306
|
+
doc.addListItem("Customer satisfaction improved")
|
|
307
|
+
doc.save()
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### NetCDF Processing
|
|
311
|
+
```python
|
|
312
|
+
import ccfx
|
|
313
|
+
|
|
314
|
+
# List variables in NetCDF file
|
|
315
|
+
variables = ccfx.netcdfVariablesList("climate_data.nc")
|
|
316
|
+
print("Available variables:", variables)
|
|
317
|
+
|
|
318
|
+
# Export NetCDF variable to GeoTIFF
|
|
319
|
+
ccfx.netcdfExportTif(
|
|
320
|
+
"temperature_data.nc",
|
|
321
|
+
"temperature",
|
|
322
|
+
"temp_map.tif",
|
|
323
|
+
band=1
|
|
324
|
+
)
|
|
325
|
+
|
|
326
|
+
# Calculate average from multiple NetCDF files
|
|
327
|
+
nc_files = ["data_2020.nc", "data_2021.nc", "data_2022.nc"]
|
|
328
|
+
avg_map = ccfx.netcdfAverageMap(nc_files, "precipitation")
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
### Data Analysis
|
|
332
|
+
```python
|
|
333
|
+
import ccfx
|
|
334
|
+
import pandas as pd
|
|
335
|
+
|
|
336
|
+
# Calculate timeseries statistics
|
|
337
|
+
observed_data = pd.read_csv("observed.csv")
|
|
338
|
+
simulated_data = pd.read_csv("simulated.csv")
|
|
339
|
+
|
|
340
|
+
# Combine data
|
|
341
|
+
data = pd.merge(observed_data, simulated_data, on='date')
|
|
342
|
+
|
|
343
|
+
# Calculate Nash-Sutcliffe Efficiency
|
|
344
|
+
stats = ccfx.calculateTimeseriesStats(data, observed='obs', simulated='sim')
|
|
345
|
+
print(f"NSE: {stats['NSE']:.3f}")
|
|
346
|
+
print(f"KGE: {stats['KGE']:.3f}")
|
|
347
|
+
print(f"R²: {stats['R2']:.3f}")
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
## API Reference (Complete Function List)
|
|
351
|
+
|
|
352
|
+
### File & Directory Management (`ccfx.py`)
|
|
353
|
+
|
|
354
|
+
**Basic File Operations:**
|
|
355
|
+
* **`listFiles(path: str, ext: Optional[str] = None) -> list`**: Lists files in a directory, optionally filtering by extension.
|
|
356
|
+
* **`listAllFiles(folder: str, extension: str = "*") -> list`**: Recursively lists all files in a folder and its subfolders.
|
|
357
|
+
* **`listFolders(path: str) -> list`**: Lists all folders in a directory.
|
|
358
|
+
* **`listDirectories(path: str) -> list`**: Alias for listFolders.
|
|
359
|
+
* **`deleteFile(filePath: str, v: bool = False) -> bool`**: Deletes a specified file.
|
|
360
|
+
* **`deletePath(path: str, v: bool = False) -> bool`**: Deletes a directory and its contents.
|
|
361
|
+
* **`createPath(pathName: str, v: bool = False) -> str`**: Creates a directory path if it doesn't exist.
|
|
362
|
+
* **`getExtension(filePath: str) -> str`**: Gets the extension of a file.
|
|
363
|
+
* **`getFileBaseName(filePath: str, extension: bool = True) -> str`**: Gets the base name of a file.
|
|
364
|
+
|
|
365
|
+
**File Copying & Moving:**
|
|
366
|
+
* **`copyFile(source: str, destination: str, v: bool = True) -> None`**: Copies a single file.
|
|
367
|
+
* **`copyDirectory(source: str, destination: str, recursive: bool = True, v: bool = True, filter: list = []) -> None`**: Copies a directory's contents.
|
|
368
|
+
* **`copyFolder(source: str, destination: str, v: bool = True) -> None`**: Alias for copyDirectory.
|
|
369
|
+
* **`moveDirectory(srcDir: str, destDir: str, v: bool = False) -> bool`**: Moves all files from source to destination directory.
|
|
370
|
+
* **`moveDirectoryFiles(srcDir: str, destDir: str, v: bool = False) -> bool`**: Moves files and subdirectories from source to destination.
|
|
371
|
+
|
|
372
|
+
**File I/O Operations:**
|
|
373
|
+
* **`readFrom(filename: str, decode_codec: Optional[str] = None, v: bool = False) -> Any`**: Reads ASCII files.
|
|
374
|
+
* **`readFile(filename: str, decode_codec: Optional[str] = None, v: bool = False) -> Any`**: Alias for readFrom.
|
|
375
|
+
* **`writeTo(filename: str, file_text: Any, encode_codec: Optional[str] = None, v: bool = False) -> bool`**: Writes ASCII files.
|
|
376
|
+
* **`writeToFile(filename: str, file_text: Any, encode_codec: Optional[str] = None, v: bool = False) -> bool`**: Alias for writeTo.
|
|
377
|
+
* **`writeFile(filename: str, file_text: Any, encode_codec: Optional[str] = None, v: bool = False) -> bool`**: Alias for writeTo.
|
|
378
|
+
|
|
379
|
+
**Compression & Archives:**
|
|
380
|
+
* **`compressTo7z(input_dir: str, output_file: str, compressionLevel: int = 4, excludeExt: Optional[list] = None, v: bool = False) -> None`**: Compresses a directory into a .7z file.
|
|
381
|
+
* **`uncompress(inputFile: str, outputDir: str, v: bool = False) -> None`**: Extracts various archive formats (.7z, .zip, .tar, etc.).
|
|
382
|
+
* **`uncompressFile(inputFile: str, outputDir: str, v: bool = False) -> None`**: Alias for uncompress.
|
|
383
|
+
* **`unzipFile(inputFile: str, outputDir: str, v: bool = False) -> None`**: Alias for uncompress.
|
|
384
|
+
* **`extractZip(inputFile: str, outputDir: str, v: bool = False) -> None`**: Alias for uncompress.
|
|
385
|
+
* **`extractCompressedFile(inputFile: str, outputDir: str, v: bool = False) -> None`**: Alias for uncompress.
|
|
386
|
+
|
|
387
|
+
**File Monitoring & Statistics:**
|
|
388
|
+
* **`fileCount(path: str = "./", extension: str = ".*", v: bool = True) -> int`**: Gets the number of files in a directory with a specific extension.
|
|
389
|
+
* **`watchFileCount(path: str = "./", extension: str = ".*", interval: float = 0.2, duration = 3, v: bool = True) -> None`**: Monitors file count over time.
|
|
390
|
+
|
|
391
|
+
**Variable Persistence:**
|
|
392
|
+
* **`pythonVariable(filename: str, option: str, variable: Any = None) -> Any`**: Saves ('dump') or loads ('load') Python variables using pickle.
|
|
393
|
+
|
|
394
|
+
**Download Operations:**
|
|
395
|
+
* **`downloadFile(url: str, save_path: str, exists_action: str = 'resume', num_connections: int = 5, v: bool = False) -> None`**: Downloads files with resume and multi-connection support.
|
|
396
|
+
* **`downloadChunk(url: str, start: int, end: int, path: str) -> None`**: Internal function for chunked downloads.
|
|
397
|
+
|
|
398
|
+
### Geospatial Data Processing (`ccfx.py`)
|
|
399
|
+
|
|
400
|
+
**Raster Operations:**
|
|
401
|
+
* **`clipRasterByExtent(inFile: str, outFile: str, bounds: tuple) -> str`**: Clips a raster using bounding box coordinates.
|
|
402
|
+
* **`clipRasterByVector(inFile: str, outFile: str, vectorFile: str) -> str`**: Clips a raster using a vector file.
|
|
403
|
+
* **`resampleRaster(inFile: str, outFile: str, resolution: float, dstSRS = None, resamplingMethod = 'bilinear', replaceOutput: bool = True, v: bool = True) -> Optional[str]`**: Resamples a raster to a new resolution.
|
|
404
|
+
* **`reprojectRaster(inFile: str, outFile: str, dstProjection: str, resamplingMethod: str = 'mode') -> str`**: Reprojects a raster to a new CRS.
|
|
405
|
+
* **`mergeRasterTiles(tileList: list, outFile: str) -> str`**: Merges multiple raster files into one.
|
|
406
|
+
* **`mergeRasterFiles(tileList: list, outFile: str) -> str`**: Alias for mergeRasterTiles.
|
|
407
|
+
* **`rasterizeRaster(inFile: str, outFile: str, targetField: str, targetResolution: float) -> str`**: Rasterizes a vector layer based on an attribute field.
|
|
408
|
+
* **`rasterizeGDF(gdf: geopandas.GeoDataFrame, valueField: str, outRasterFN: str, resolution: float, isCOG: bool = True, allTouched: bool = False, profileOverrides: dict | None = None) -> str`**: Rasterizes a GeoDataFrame to GeoTIFF/COG.
|
|
409
|
+
* **`tiffWriteArray(array: numpy.ndarray, outputFile: str, geoTransform: tuple = (0, 1, 0, 0, 0, -1), projection: str = 'EPSG:4326', noData: Optional[float] = None, v: bool = False) -> gdal.Dataset`**: Writes a NumPy array to a GeoTIFF file.
|
|
410
|
+
|
|
411
|
+
**Vector Operations:**
|
|
412
|
+
* **`clipVectorByExtent(inFile: str, outFile: str, bounds: tuple) -> str`**: Clips a vector file using bounding box coordinates.
|
|
413
|
+
* **`clipFeatures(inputFeaturePath: str, boundaryFeature: str, outputFeature: str, keepOnlyTypes: Optional[list] = None, v: bool = False) -> geopandas.GeoDataFrame`**: Clips input features by a boundary feature.
|
|
414
|
+
* **`getVectorBounds(grid_gdf: geopandas.GeoDataFrame) -> tuple`**: Gets the bounds of a GeoDataFrame.
|
|
415
|
+
|
|
416
|
+
**Coordinate & Geometry Operations:**
|
|
417
|
+
* **`convertCoordinates(lon: float, lat: float, srcEPSG: str, dstCRS: str) -> tuple`**: Converts coordinates between CRSs.
|
|
418
|
+
* **`extractRasterValue(rasterPath: str, lat: float, lon: float, coordProj: str = 'EPSG:4326') -> Optional[float]`**: Extracts the raster value at a specific point.
|
|
419
|
+
* **`getRasterValue(rasterPath: str, lat: float, lon: float, coordProj: str = 'EPSG:4326') -> Optional[float]`**: Alias for extractRasterValue.
|
|
420
|
+
* **`pointsToGeodataframe(rowList: list, columnNames: list, latIndex: int, lonIndex: int, auth: str = "EPSG", code: str = "4326", outShape: str = "", format: str = "gpkg", v: bool = False, includeLatLon: bool = True) -> geopandas.GeoDataFrame`**: Converts a list of point coordinates to a GeoDataFrame.
|
|
421
|
+
* **`createPointGeometry(coords: list, proj: str = "EPSG:4326") -> geopandas.GeoDataFrame`**: Converts list of coordinate tuples to GeoDataFrame.
|
|
422
|
+
* **`createGrid(topLeft: Optional[list] = None, bottomRight: Optional[list] = None, resolution: Optional[float] = None, inputShape: Optional[str] = None, crs: str = "EPSG:4326", saveVector: Optional[str] = None) -> geopandas.GeoDataFrame`**: Creates a grid of polygons based on shapefile or coordinates.
|
|
423
|
+
* **`createPolygonFromOuterPoints(pointsGdf: geopandas.GeoDataFrame, alpha: float = 1.6, keepHoles: bool = False) -> geopandas.GeoDataFrame`**: Creates concave hull (alpha-shape) from points.
|
|
424
|
+
|
|
425
|
+
### NetCDF File Handling (`ccfx.py`)
|
|
426
|
+
|
|
427
|
+
* **`netcdfVariablesList(ncFile: str) -> list`**: Lists variables in a NetCDF file.
|
|
428
|
+
* **`netcdfVariableDimensions(ncFile: str, variable: str) -> dict`**: Gets dimensions and their sizes for a NetCDF variable.
|
|
429
|
+
* **`netcdfExportTif(ncFile: str, variable: str, outputFile: Optional[str] = None, band: Optional[int] = None, v: bool = True) -> gdal.Dataset`**: Exports a NetCDF variable (optionally a specific band) to GeoTIFF.
|
|
430
|
+
* **`netcdfAverageMap(ncFiles: list, variable: str, band: int = 1) -> numpy.ndarray`**: Calculates the average map from a variable across multiple NetCDF files.
|
|
431
|
+
* **`netcdfSumMaps(ncFiles: list, variable: str, band: int = 1) -> numpy.ndarray`**: Calculates the sum map from a variable across multiple NetCDF files.
|
|
432
|
+
* **`renameNetCDFvariable(input_file: str, output_file: str, old_var_name: str, new_var_name: str, v: bool = False) -> None`**: Renames a variable in a NetCDF file using CDO.
|
|
433
|
+
|
|
434
|
+
### Database Connectivity
|
|
435
|
+
|
|
436
|
+
#### MS SQL Server (`mssqlConnection.py`)
|
|
437
|
+
**Class: `mssqlConnection(server, username, password, driver, trust_server_ssl=True)`**
|
|
438
|
+
* **`__init__(server, username, password, driver, trust_server_ssl=True) -> None`**: Initialize connection parameters.
|
|
439
|
+
* **`connect()`**: Establish connection to server.
|
|
440
|
+
* **`listDatabases() -> list`**: List available databases.
|
|
441
|
+
* **`listTables(db_name=None) -> list`**: List tables in database.
|
|
442
|
+
* **`readTable()`**: Read table data (including spatial data into GeoDataFrames).
|
|
443
|
+
* **`connectDB()`**: Connect to specific database.
|
|
444
|
+
* **`dataframeToSql()`**: Write DataFrame/GeoDataFrame to table.
|
|
445
|
+
* **`dropTable()`**: Remove table.
|
|
446
|
+
* **`close()`**: Close connection.
|
|
447
|
+
|
|
448
|
+
#### SQLite (`sqliteConnection.py`)
|
|
449
|
+
**Class: `sqliteConnection(sqlite_database, connect=False)`**
|
|
450
|
+
* **`__init__(sqlite_database, connect=False) -> None`**: Initialize database connection.
|
|
451
|
+
* **`connect(v=True) -> None`**: Establish connection.
|
|
452
|
+
* **`createTable(table_name, initial_field_name, data_type) -> None`**: Create new table.
|
|
453
|
+
* **`renameTable()`**: Rename existing table.
|
|
454
|
+
* **`deleteTable()`**: Remove table.
|
|
455
|
+
* **`readTableAsDict()`**: Read table data as dictionary.
|
|
456
|
+
* **`insertDict()`**: Insert data from dictionary.
|
|
457
|
+
* **`insertRow()`**: Insert single row.
|
|
458
|
+
* **`updateValue(table_name, col_name, new_value, col_where1, val_1, v=False) -> None`**: Update specific values.
|
|
459
|
+
* **`dumpCSV()`**: Export table to CSV.
|
|
460
|
+
* **`commitChanges()`**: Commit transactions.
|
|
461
|
+
* **`closeConnection()`**: Close connection.
|
|
462
|
+
|
|
463
|
+
### Document & Spreadsheet Generation
|
|
464
|
+
|
|
465
|
+
#### Excel Spreadsheets (`excel.py`)
|
|
466
|
+
**Class: `excel(path)`**
|
|
467
|
+
* **`__init__(path)`**: Initialize Excel document.
|
|
468
|
+
* **`create()`**: Create new workbook.
|
|
469
|
+
* **`addSheet(sheet_name)`**: Add worksheet.
|
|
470
|
+
* **`write(sheet_name, row, column, value)`**: Write data to cells.
|
|
471
|
+
* **`writeDate(sheet_name, row, column, datetime_obj)`**: Write date values.
|
|
472
|
+
* **`setDateFormat(format_string='dd/mm/yyyy')`**: Set date format.
|
|
473
|
+
* **`setColumnWidth(sheet_name, column_names, width=12)`**: Adjust column widths.
|
|
474
|
+
* **`addFigure(sheet_name, x_src_sheet_name, x_start, x_end, y_src_sheet_name, y_start, y_end, position_cell="E2", chart_type='subtype')`**: Insert charts/graphs.
|
|
475
|
+
* **`save()`**: Save workbook.
|
|
476
|
+
* **`open()`**: Open existing file.
|
|
477
|
+
|
|
478
|
+
#### Word Documents (`word.py`)
|
|
479
|
+
**Class: `word_document(path)`**
|
|
480
|
+
* **`__init__(path) -> None`**: Initialize Word document.
|
|
481
|
+
* **`addHeading(heading, level=2)`**: Add document headings.
|
|
482
|
+
* **`addParagraph(text="", alignment='justify')`**: Add text paragraphs.
|
|
483
|
+
* **`addListItem(text="", numbers=False)`**: Add list items.
|
|
484
|
+
* **`addText(text, bold=False, italic=False)`**: Add formatted text.
|
|
485
|
+
* **`addImage(path_to_image, width_=16)`**: Insert images.
|
|
486
|
+
* **`addPageBreak()`**: Insert page breaks.
|
|
487
|
+
* **`setMargins()`**: Configure page margins.
|
|
488
|
+
* **`save()`**: Save document.
|
|
489
|
+
|
|
490
|
+
### Multimedia & Web (`ccfx.py`)
|
|
491
|
+
|
|
492
|
+
**MP3 Metadata:**
|
|
493
|
+
* **`getMp3Metadata(fn: str, imagePath: Optional[str] = None) -> dict`**: Extracts ID3 metadata from an MP3 file.
|
|
494
|
+
* **`setMp3Metadata(fn: str, metadata: dict, imagePath: Optional[str] = None) -> bool`**: Writes ID3 metadata (including album art) to an MP3 file.
|
|
495
|
+
* **`guessMimeType(imagePath: str) -> str`**: Determines MIME type of image files.
|
|
496
|
+
|
|
497
|
+
**YouTube Downloads:**
|
|
498
|
+
* **`downloadYoutubeVideo(url: str, dstDir: str, audioOnly: bool = False, cookiesFile: Optional[str] = None, dstFileName: Optional[str] = None) -> str`**: Downloads video or audio from a YouTube URL.
|
|
499
|
+
* **`parseYoutubePlaylist(playlistUrl: str) -> list[str]`**: Returns list of video URLs from a YouTube playlist.
|
|
500
|
+
* **`parseYoutubeChannelVideos(channelUrl: str, maxItems: Optional[int] = None) -> list[str]`**: Returns list of video URLs from a YouTube channel.
|
|
501
|
+
|
|
502
|
+
**Image Processing:**
|
|
503
|
+
* **`removeImageColour(inPath: str, outPath: str, colour: tuple = (255, 255, 255), tolerance: int = 30) -> None`**: Removes a specific color from an image.
|
|
504
|
+
* **`makeTransparent(inPath: str, outPath: str, colour: tuple = (255, 255, 255), tolerance: int = 30) -> None`**: Makes pixels in an image transparent.
|
|
505
|
+
|
|
506
|
+
**Video Processing:**
|
|
507
|
+
* **`correctFisheye(inputFile: str, outputFile: str = '', k1: float = -0.1, k2: float = 0.05, cx: float = 0.5, cy: float = 0.5, crf: int = 20) -> str`**: Corrects fisheye distortion in videos.
|
|
508
|
+
* **`correctLens(inputFile: str, outputFile: str = '', k1: float = -0.1, k2: float = 0.05, cx: float = 0.5, cy: float = 0.5, crf: int = 20) -> str`**: Alias for correctFisheye.
|
|
509
|
+
|
|
510
|
+
### Data Analysis & Statistics (`ccfx.py`)
|
|
511
|
+
|
|
512
|
+
**Timeseries Analysis:**
|
|
513
|
+
* **`calculateTimeseriesStats(data: pandas.DataFrame, observed: Optional[str] = None, simulated: Optional[str] = None, resample: Optional[str] = None) -> dict`**: Calculates comprehensive statistics between observed and simulated timeseries.
|
|
514
|
+
* **`getNSE(data: pandas.DataFrame, observed: Optional[str] = None, simulated: Optional[str] = None, resample: Optional[str] = None) -> float`**: Calculates Nash-Sutcliffe Efficiency.
|
|
515
|
+
* **`getKGE(data: pandas.DataFrame, observed: Optional[str] = None, simulated: Optional[str] = None, resample: Optional[str] = None) -> float`**: Calculates Kling-Gupta Efficiency.
|
|
516
|
+
* **`getPBIAS(data: pandas.DataFrame, observed: Optional[str] = None, simulated: Optional[str] = None, resample: Optional[str] = None) -> float`**: Calculates Percent Bias.
|
|
517
|
+
* **`getLNSE(data: pandas.DataFrame, observed: Optional[str] = None, simulated: Optional[str] = None, resample: Optional[str] = None) -> float`**: Calculates Log Nash-Sutcliffe Efficiency.
|
|
518
|
+
* **`getR2(data: pandas.DataFrame, observed: Optional[str] = None, simulated: Optional[str] = None, resample: Optional[str] = None) -> float`**: Calculates R-squared.
|
|
519
|
+
* **`getRMSE(data: pandas.DataFrame, observed: Optional[str] = None, simulated: Optional[str] = None, resample: Optional[str] = None) -> float`**: Calculates Root Mean Square Error.
|
|
520
|
+
* **`getMAE(data: pandas.DataFrame, observed: Optional[str] = None, simulated: Optional[str] = None, resample: Optional[str] = None) -> float`**: Calculates Mean Absolute Error.
|
|
521
|
+
* **`getMSE(data: pandas.DataFrame, observed: Optional[str] = None, simulated: Optional[str] = None, resample: Optional[str] = None) -> float`**: Calculates Mean Square Error.
|
|
522
|
+
* **`getTimeseriesStats(data: pandas.DataFrame, observed: Optional[str] = None, simulated: Optional[str] = None, resample: Optional[str] = None) -> dict`**: Alias for calculateTimeseriesStats.
|
|
523
|
+
|
|
524
|
+
**SWAT+ Integration:**
|
|
525
|
+
* **`readSWATPlusOutputs(filePath: str, column: Optional[str] = None, unit: Optional[int] = None, gis_id: Optional[int] = None, name: Optional[str] = None, coerceNumeric: bool = True) -> Optional[pandas.DataFrame]`**: Reads SWAT+ output files with filtering capabilities.
|
|
526
|
+
* **`runSWATPlus(txtinoutDir: str, finalDir: str, executablePath: str = "swatplus", v: bool = True) -> None`**: Runs SWAT+ model with progress monitoring.
|
|
527
|
+
|
|
528
|
+
### Utility Functions (`ccfx.py`)
|
|
529
|
+
|
|
530
|
+
**Progress & Display:**
|
|
531
|
+
* **`progressBar(count: int, total: int, message: str = "") -> None`**: Displays a simple console progress bar.
|
|
532
|
+
* **`showProgress(count: int, end: int, message: str, barLength: int = 100) -> None`**: Displays a detailed console progress bar.
|
|
533
|
+
* **`dualProgress(primaryCount: int, primaryEnd: int, secondaryCount: int, secondaryEnd: int, barLength: int = 40, message: str = '') -> None`**: Displays two progress bars simultaneously.
|
|
534
|
+
|
|
535
|
+
**System & Environment:**
|
|
536
|
+
* **`systemPlatform() -> str`**: Gets the system platform.
|
|
537
|
+
* **`setHomeDir(path: str) -> str`**: Sets the working directory to script location.
|
|
538
|
+
* **`ignoreWarnings(ignore: bool = True, v: bool = False) -> None`**: Suppresses or enables Python warnings.
|
|
539
|
+
|
|
540
|
+
**Text & String Processing:**
|
|
541
|
+
* **`formatStringBlock(input_str: str, max_chars: int = 70) -> str`**: Formats a string into a block of text with maximum characters per line.
|
|
542
|
+
* **`formatTimedelta(delta: timedelta) -> str`**: Formats a timedelta duration to readable format.
|
|
543
|
+
|
|
544
|
+
**Mathematical Utilities:**
|
|
545
|
+
* **`isBetween(number: float, a: float, b: float) -> bool`**: Returns True if number is between a and b.
|
|
546
|
+
|
|
547
|
+
**Notifications:**
|
|
548
|
+
* **`alert(message: str, server: str = "http://ntfy.sh", topic: str = "pythonAlerts", attachment: Optional[str] = None, messageTitle: str = "info", priority: int = None, tags: list = [], printIt: bool = True, v: bool = False) -> bool`**: Sends notifications to external servers.
|
|
549
|
+
|
|
550
|
+
## Contributing
|
|
551
|
+
|
|
552
|
+
Contributions are welcome! Please fork the repository, make your changes, and submit a pull request. Ensure code is well-documented and includes tests where applicable.
|
|
553
|
+
|
|
554
|
+
### Development Setup
|
|
555
|
+
```bash
|
|
556
|
+
git clone https://github.com/celray/ccfx.git
|
|
557
|
+
cd ccfx
|
|
558
|
+
pip install -e .
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
### Version Management
|
|
562
|
+
To automatically update the version in README.md based on `pyproject.toml`:
|
|
563
|
+
```bash
|
|
564
|
+
# Make the script executable (if not already)
|
|
565
|
+
chmod +x updateReadmeVersion.py
|
|
566
|
+
|
|
567
|
+
# Run the version update script
|
|
568
|
+
./updateReadmeVersion.py
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
You can also integrate this into your build process by adding it to your CI/CD pipeline or as a pre-commit hook.
|
|
572
|
+
|
|
573
|
+
### Testing
|
|
574
|
+
Please ensure your changes don't break existing functionality and add tests for new features.
|
|
575
|
+
|
|
576
|
+
## Changelog
|
|
577
|
+
|
|
578
|
+
### Version 1.1.1 (Current)
|
|
579
|
+
- Enhanced geospatial processing capabilities
|
|
580
|
+
- Improved database connectivity options
|
|
581
|
+
- Added comprehensive multimedia support
|
|
582
|
+
- Better error handling and documentation
|
|
583
|
+
|
|
584
|
+
For detailed release notes, visit the [Releases page](https://github.com/celray/ccfx/releases).
|
|
585
|
+
|
|
586
|
+
## License
|
|
587
|
+
|
|
588
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
589
|
+
|
|
590
|
+
---
|
|
591
|
+
|
|
592
|
+
**Author**: Celray James CHAWANDA
|
|
593
|
+
**Email**: celray@chawanda.com
|
|
594
|
+
**GitHub**: [@celray](https://github.com/celray)
|
|
595
|
+
**Package Homepage**: [https://github.com/celray/ccfx](https://github.com/celray/ccfx)
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
ccfx/__init__.py,sha256=UK62VcGS84SJyGVg1bK4FltZj7OkpdoyhoFWeXcKsX0,144
|
|
2
|
-
ccfx/ccfx.py,sha256=
|
|
2
|
+
ccfx/ccfx.py,sha256=H1Kqo7rQuiGkH_pr8KxWMwZVY5tOUuMWoNk29KzMkt0,87188
|
|
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.
|
|
8
|
-
ccfx-1.
|
|
9
|
-
ccfx-1.
|
|
10
|
-
ccfx-1.
|
|
11
|
-
ccfx-1.
|
|
7
|
+
ccfx-1.1.1.dist-info/licenses/LICENSE,sha256=EuxaawJg_OOCLfikkCGgfXPZmxR-x_5PH7_2e9M-3eA,1099
|
|
8
|
+
ccfx-1.1.1.dist-info/METADATA,sha256=xvkQuCHd7Kecmlj6DQjpYLHW9fUc93acFx2EV5ONO08,29271
|
|
9
|
+
ccfx-1.1.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
10
|
+
ccfx-1.1.1.dist-info/top_level.txt,sha256=_cSvSA1WX2K8TgoV3iBJUdUZZqMKJbOPLNnKLYSLHaw,5
|
|
11
|
+
ccfx-1.1.1.dist-info/RECORD,,
|
ccfx-1.0.9.dist-info/METADATA
DELETED
|
@@ -1,181 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: ccfx
|
|
3
|
-
Version: 1.0.9
|
|
4
|
-
Summary: This package simplifies regular common actions for quick prototyping in a user friendly way
|
|
5
|
-
Author-email: Celray James CHAWANDA <celray@chawanda.com>
|
|
6
|
-
License-Expression: MIT
|
|
7
|
-
Project-URL: Homepage, https://github.com/celray/ccfx
|
|
8
|
-
Classifier: Programming Language :: Python :: 3
|
|
9
|
-
Classifier: Operating System :: OS Independent
|
|
10
|
-
Requires-Python: >=3.10
|
|
11
|
-
Description-Content-Type: text/markdown
|
|
12
|
-
License-File: LICENSE
|
|
13
|
-
Requires-Dist: netCDF4
|
|
14
|
-
Requires-Dist: yt_dlp
|
|
15
|
-
Requires-Dist: gdal
|
|
16
|
-
Requires-Dist: numpy
|
|
17
|
-
Requires-Dist: shapely
|
|
18
|
-
Requires-Dist: geopandas
|
|
19
|
-
Requires-Dist: pandas
|
|
20
|
-
Requires-Dist: xlsxwriter
|
|
21
|
-
Requires-Dist: pyodbc
|
|
22
|
-
Requires-Dist: sqlalchemy
|
|
23
|
-
Requires-Dist: python-docx
|
|
24
|
-
Requires-Dist: py7zr
|
|
25
|
-
Requires-Dist: mutagen
|
|
26
|
-
Requires-Dist: requests
|
|
27
|
-
Requires-Dist: tqdm
|
|
28
|
-
Requires-Dist: pillow
|
|
29
|
-
Dynamic: license-file
|
|
30
|
-
|
|
31
|
-
# ccfx
|
|
32
|
-
|
|
33
|
-
`ccfx` is a comprehensive Python package designed to streamline file and data management, geospatial analysis, NetCDF file processing, database interactions, document generation, and multimedia handling for rapid prototyping and development workflows.
|
|
34
|
-
|
|
35
|
-
## Features
|
|
36
|
-
|
|
37
|
-
1. **File Management**:
|
|
38
|
-
* List, delete, move, copy, and count files/directories.
|
|
39
|
-
* Monitor file count over time.
|
|
40
|
-
* Save, load, and manage Python variables via pickle serialization.
|
|
41
|
-
* Compress directories to `.7z` archives.
|
|
42
|
-
* Read/write text files with encoding support.
|
|
43
|
-
* Download files from URLs with resume and multi-connection support.
|
|
44
|
-
|
|
45
|
-
2. **Geospatial Data Processing**:
|
|
46
|
-
* Read, write, clip (by extent/feature), resample, reproject, merge, and rasterize raster data (GeoTIFF, NetCDF).
|
|
47
|
-
* Read, write, and clip vector data (Shapefile, GeoPackage).
|
|
48
|
-
* Create grids of polygons based on shapefile boundaries.
|
|
49
|
-
* Convert coordinates between Coordinate Reference Systems (CRS).
|
|
50
|
-
* Extract raster values at specific coordinates.
|
|
51
|
-
* Convert point lists to GeoDataFrames.
|
|
52
|
-
* Get vector layer bounds.
|
|
53
|
-
|
|
54
|
-
3. **NetCDF File Handling**:
|
|
55
|
-
* List variables and dimensions.
|
|
56
|
-
* Export NetCDF variables to GeoTIFF format (single or multiple bands).
|
|
57
|
-
* Calculate sum and average maps from NetCDF data across multiple files.
|
|
58
|
-
* Rename variables using CDO (if available).
|
|
59
|
-
|
|
60
|
-
4. **Database Connectivity**:
|
|
61
|
-
* **MS SQL Server**: Connect, list databases/tables, read tables (including spatial data into GeoDataFrames), write DataFrames/GeoDataFrames to tables, drop tables.
|
|
62
|
-
* **SQLite**: Connect, create/rename/drop tables, read tables (as dict, specific columns), insert data (rows, dicts, partial dicts), update values, dump tables to CSV.
|
|
63
|
-
|
|
64
|
-
5. **Document & Spreadsheet Handling**:
|
|
65
|
-
* **Excel**: Create `.xlsx` files, add sheets, write data (including dates), set column widths, add scatter plot charts.
|
|
66
|
-
* **Word**: Create `.docx` files, add headings, paragraphs (with alignment), list items, formatted text (bold/italic), images, page breaks, set margins.
|
|
67
|
-
|
|
68
|
-
6. **Multimedia & Web**:
|
|
69
|
-
* Read and write MP3 metadata (ID3 tags), including album art.
|
|
70
|
-
* Download videos/audio from YouTube using `yt-dlp`.
|
|
71
|
-
|
|
72
|
-
7. **Data Analysis & Utilities**:
|
|
73
|
-
* Calculate timeseries statistics (NSE, KGE, PBIAS, LNSE, R2, RMSE, MAE, MSE, MAPE, alpha, beta) with resampling options.
|
|
74
|
-
* Display dynamic progress bars.
|
|
75
|
-
* Check system platform information.
|
|
76
|
-
* Enable or disable warnings programmatically.
|
|
77
|
-
* Set the working directory.
|
|
78
|
-
|
|
79
|
-
## Installation
|
|
80
|
-
|
|
81
|
-
Install `ccfx` via pip:
|
|
82
|
-
```bash
|
|
83
|
-
pip install ccfx
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
## Dependencies
|
|
87
|
-
|
|
88
|
-
`ccfx` relies on the following libraries:
|
|
89
|
-
|
|
90
|
-
* **gdal**: For geospatial raster and vector data manipulation.
|
|
91
|
-
* **numpy**: For array processing and numerical operations.
|
|
92
|
-
* **pandas**: For data manipulation and analysis.
|
|
93
|
-
* **geopandas**: Extends pandas to handle geospatial vector data.
|
|
94
|
-
* **shapely**: Provides geometric objects and operations.
|
|
95
|
-
* **netCDF4**: For working with NetCDF files.
|
|
96
|
-
* **xlsxwriter**: For creating and writing Excel `.xlsx` files.
|
|
97
|
-
* **python-docx**: Enables creation and manipulation of Word `.docx` documents.
|
|
98
|
-
* **pyodbc**: Enables connectivity to databases through ODBC (e.g., MS SQL Server).
|
|
99
|
-
* **sqlalchemy**: Provides SQL toolkit and ORM features for database access (used with MS SQL).
|
|
100
|
-
* **py7zr**: For creating `.7z` archives.
|
|
101
|
-
* **mutagen**: For reading and writing MP3 metadata (ID3 tags).
|
|
102
|
-
* **requests**: For downloading files via HTTP.
|
|
103
|
-
* **tqdm**: For displaying progress bars.
|
|
104
|
-
* **yt-dlp**: For downloading YouTube content.
|
|
105
|
-
* **matplotlib** (Optional, often used with geospatial/data analysis): For plotting.
|
|
106
|
-
|
|
107
|
-
These dependencies should be installed automatically when `ccfx` is installed via pip, but GDAL might require manual installation steps depending on your OS.
|
|
108
|
-
|
|
109
|
-
## API Reference (Selected Functions)
|
|
110
|
-
|
|
111
|
-
### File Management (`ccfx.py`)
|
|
112
|
-
|
|
113
|
-
* **`listFiles(path: str, ext: str = None) -> list`**: Lists files in a directory, optionally filtering by extension.
|
|
114
|
-
* **`deleteFile(filePath: str, v: bool = False) -> bool`**: Deletes a specified file.
|
|
115
|
-
* **`deletePath(path: str, v: bool = False) -> bool`**: Deletes a directory and its contents.
|
|
116
|
-
* **`createPath(pathName, v = False)`**: Creates a directory path if it doesn't exist.
|
|
117
|
-
* **`copyFile(source: str, destination: str, v: bool = True)`**: Copies a single file.
|
|
118
|
-
* **`copyDirectory(source: str, destination: str, recursive=True, v=True, filter=[])`**: Copies a directory's contents.
|
|
119
|
-
* **`moveDirectoryFiles(srcDir: str, destDir: str, v: bool = False) -> bool`**: Moves files and subdirectories from source to destination.
|
|
120
|
-
* **`pythonVariable(filename, option, variable=None)`**: Saves ('dump') or loads ('load') Python variables using pickle.
|
|
121
|
-
* **`compressTo7z(input_dir: str, output_file: str)`**: Compresses a directory into a .7z file.
|
|
122
|
-
* **`downloadFile(url, save_path, exists_action='resume', num_connections=5, v=False)`**: Downloads a file from a URL with advanced options.
|
|
123
|
-
* **`listAllFiles(folder, extension="*")`**: Recursively lists all files in a folder and its subfolders.
|
|
124
|
-
|
|
125
|
-
### Geospatial (`ccfx.py`)
|
|
126
|
-
|
|
127
|
-
* **`createGrid(shapefile_path: str, resolution: float, useDegree: bool = True) -> tuple`**: Generates a grid of polygons based on a shapefile extent.
|
|
128
|
-
* **`clipRasterByExtent(inFile: str, outFile: str, bounds: tuple) -> str`**: Clips a raster using bounding box coordinates.
|
|
129
|
-
* **`clipVectorByExtent(inFile: str, outFile: str, bounds: tuple) -> str`**: Clips a vector file using bounding box coordinates.
|
|
130
|
-
* **`clipFeatures(inputFeaturePath:str, boundaryFeature:str, outputFeature:str, keepOnlyTypes = None, v = False) -> geopandas.GeoDataFrame`**: Clips input features by a boundary feature.
|
|
131
|
-
* **`resampleRaster(inFile:str, outFile:str, resolution:float, dstSRS = None, resamplingMethod = 'bilinear', replaceOutput:bool = True, v:bool = True) -> str`**: Resamples a raster to a new resolution and optionally CRS.
|
|
132
|
-
* **`reprojectRaster(inFile: str, outFile: str, dstProjection: str, resamplingMethod: str = 'mode') -> str`**: Reprojects a raster to a new CRS.
|
|
133
|
-
* **`mergeRasterTiles(tileList:list, outFile:str) -> str`**: Merges multiple raster files into one.
|
|
134
|
-
* **`rasterizeRaster(inFile: str, outFile: str, targetField: str, targetResolution: float) -> str`**: Rasterizes a vector layer based on an attribute field.
|
|
135
|
-
* **`extractRasterValue(rasterPath: str, lat: float, lon: float, coordProj: str = 'EPSG:4326') -> float`**: Extracts the raster value at a specific point.
|
|
136
|
-
* **`convertCoordinates(lon, lat, srcEPSG, dstCRS) -> tuple`**: Converts coordinates between CRSs.
|
|
137
|
-
* **`tiffWriteArray(array: numpy.ndarray, outputFile: str, geoTransform: tuple, projection: str, noData:float = None, v:bool = False) -> gdal.Dataset`**: Writes a NumPy array to a GeoTIFF file.
|
|
138
|
-
* **`pointsToGeodataframe(point_pairs_list, columns = ['latitude', 'longitude'], auth = "EPSG", code = '4326', out_shape = '', format = 'gpkg', v = False, get_geometry_only = False)`**: Converts a list of point coordinates to a GeoDataFrame.
|
|
139
|
-
|
|
140
|
-
### NetCDF (`ccfx.py`)
|
|
141
|
-
|
|
142
|
-
* **`netcdfVariablesList(ncFile: str) -> list`**: Lists variables in a NetCDF file.
|
|
143
|
-
* **`netcdfVariableDimensions(ncFile: str, variable: str) -> dict`**: Gets dimensions and their sizes for a NetCDF variable.
|
|
144
|
-
* **`netcdfExportTif(ncFile: str, variable: str, outputFile: str = None, band: int = None, v:bool = True) -> gdal.Dataset`**: Exports a NetCDF variable (optionally a specific band) to GeoTIFF.
|
|
145
|
-
* **`netcdfAverageMap(ncFiles:list, variable:str, band:int = 1) -> numpy.ndarray`**: Calculates the average map from a variable across multiple NetCDF files.
|
|
146
|
-
* **`netcdfSumMaps(ncFiles:list, variable:str, band:int = 1) -> numpy.ndarray`**: Calculates the sum map from a variable across multiple NetCDF files.
|
|
147
|
-
* **`renameNetCDFvariable(input_file: str, output_file: str, old_var_name: str, new_var_name: str, v = False)`**: Renames a variable in a NetCDF file using CDO.
|
|
148
|
-
|
|
149
|
-
### Database (`mssqlConnection.py`, `sqliteConnection.py`)
|
|
150
|
-
|
|
151
|
-
* **`mssql_connection(server, username, password, driver, ...)`**: Class for MS SQL Server interactions.
|
|
152
|
-
* `connect()`, `listDatabases()`, `listTables()`, `readTable()`, `connectDB()`, `dataframeToSql()`, `dropTable()`, `close()`
|
|
153
|
-
* **`sqliteConnection(sqlite_database, connect=False)`**: Class for SQLite interactions.
|
|
154
|
-
* `connect()`, `createTable()`, `renameTable()`, `deleteTable()`, `readTableAsDict()`, `insertDict()`, `insertRow()`, `updateValue()`, `dumpCSV()`, `commitChanges()`, `closeConnection()`
|
|
155
|
-
|
|
156
|
-
### Document/Spreadsheet (`word.py`, `excel.py`)
|
|
157
|
-
|
|
158
|
-
* **`word_document(path)`**: Class for creating Word documents.
|
|
159
|
-
* `addHeading()`, `addParagraph()`, `addListItem()`, `addText()`, `addImage()`, `addPageBreak()`, `setMargins()`, `save()`
|
|
160
|
-
* **`excel(path)`**: Class for creating Excel spreadsheets.
|
|
161
|
-
* `create()`, `addSheet()`, `write()`, `writeDate()`, `setColumnWidth()`, `addFigure()`, `writeColumn()`, `save()`, `open()`
|
|
162
|
-
|
|
163
|
-
### Multimedia & Web (`ccfx.py`)
|
|
164
|
-
|
|
165
|
-
* **`getMp3Metadata(fn, imagePath=None)`**: Extracts ID3 metadata from an MP3 file.
|
|
166
|
-
* **`setMp3Metadata(fn, metadata, imagePath=None)`**: Writes ID3 metadata (including album art) to an MP3 file.
|
|
167
|
-
* **`downloadYoutubeVideo(url: str, dstDir: str, audioOnly: bool = False, dstFileName: Optional[str] = None ) -> str`**: Downloads video or audio from a YouTube URL.
|
|
168
|
-
|
|
169
|
-
### Data Analysis & Utilities (`ccfx.py`)
|
|
170
|
-
|
|
171
|
-
* **`calculateTimeseriesStats(data:pandas.DataFrame, observed:str = None, simulated:str = None, resample:str = None ) -> dict`**: Calculates various statistics between observed and simulated timeseries. (Wrappers like `getNSE`, `getKGE`, etc., are also available).
|
|
172
|
-
* **`progressBar(count, total, message="")`**: Displays a simple console progress bar.
|
|
173
|
-
* **`showProgress(count: int, end: int, message: str, barLength: int = 100)`**: Displays a more detailed console progress bar.
|
|
174
|
-
* **`ignoreWarnings(ignore:bool = True, v:bool = False)`**: Suppresses or enables Python warnings.
|
|
175
|
-
|
|
176
|
-
## Contributing
|
|
177
|
-
|
|
178
|
-
Contributions are welcome! Please fork the repository, make your changes, and submit a pull request. Ensure code is well-documented and includes tests where applicable.
|
|
179
|
-
|
|
180
|
-
## License
|
|
181
|
-
This project is licensed under the MIT License. See the LICENSE file for details.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|