hydroanomaly 0.7.3__py3-none-any.whl → 1.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.
- hydroanomaly/__init__.py +11 -4
- hydroanomaly/ml.py +170 -0
- hydroanomaly/sentinel_bands.py +51 -1
- hydroanomaly/visualize.py +22 -22
- {hydroanomaly-0.7.3.dist-info → hydroanomaly-1.2.0.dist-info}/METADATA +2 -2
- hydroanomaly-1.2.0.dist-info/RECORD +10 -0
- hydroanomaly-0.7.3.dist-info/RECORD +0 -9
- {hydroanomaly-0.7.3.dist-info → hydroanomaly-1.2.0.dist-info}/WHEEL +0 -0
- {hydroanomaly-0.7.3.dist-info → hydroanomaly-1.2.0.dist-info}/licenses/LICENSE +0 -0
- {hydroanomaly-0.7.3.dist-info → hydroanomaly-1.2.0.dist-info}/top_level.txt +0 -0
hydroanomaly/__init__.py
CHANGED
@@ -5,17 +5,20 @@ A simple Python package with just 3 modules:
|
|
5
5
|
1. USGS turbidity data retrieval (returns data and site coordinates)
|
6
6
|
2. Sentinel satellite bands retrieval
|
7
7
|
3. Time series visualization
|
8
|
+
4. Machine learning anomaly detection (One-Class SVM and Isolation Forest)
|
8
9
|
|
9
10
|
That's it - nothing else!
|
10
11
|
"""
|
11
12
|
|
12
|
-
__version__ = "
|
13
|
+
__version__ = "1.2.0"
|
13
14
|
__author__ = "Ehsan Kahrizi (Ehsan.kahrizi@usu.edu)"
|
14
15
|
|
15
16
|
# Import the 3 simple modules
|
16
17
|
from .usgs_turbidity import get_turbidity, get_usgs_turbidity
|
17
|
-
from .sentinel_bands import get_sentinel_bands, get_satellite_data, get_sentinel, get_sentinel_bands_gee
|
18
|
+
from .sentinel_bands import get_sentinel_bands, get_satellite_data, get_sentinel, get_sentinel_bands_gee, show_sentinel_ndwi_map
|
18
19
|
from .visualize import plot_timeseries, plot_turbidity, plot_sentinel, plot_comparison, plot, visualize
|
20
|
+
from .ml import run_oneclass_svm, run_isolation_forest
|
21
|
+
|
19
22
|
|
20
23
|
# Export everything
|
21
24
|
__all__ = [
|
@@ -28,7 +31,7 @@ __all__ = [
|
|
28
31
|
'get_sentinel_bands',
|
29
32
|
'get_satellite_data',
|
30
33
|
'get_sentinel',
|
31
|
-
|
34
|
+
'show_sentinel_ndwi_map',
|
32
35
|
|
33
36
|
# Visualization functions
|
34
37
|
'plot_timeseries',
|
@@ -36,7 +39,11 @@ __all__ = [
|
|
36
39
|
'plot_sentinel',
|
37
40
|
'plot_comparison',
|
38
41
|
'plot',
|
39
|
-
'visualize'
|
42
|
+
'visualize',
|
43
|
+
|
44
|
+
# Machine learning functions
|
45
|
+
'run_oneclass_svm',
|
46
|
+
'run_isolation_forest'
|
40
47
|
]
|
41
48
|
|
42
49
|
print(f"HydroAnomaly v{__version__} - Simple Water Data Package")
|
hydroanomaly/ml.py
ADDED
@@ -0,0 +1,170 @@
|
|
1
|
+
import numpy as np
|
2
|
+
import pandas as pd
|
3
|
+
from sklearn.preprocessing import StandardScaler
|
4
|
+
from sklearn.svm import OneClassSVM
|
5
|
+
from sklearn.ensemble import IsolationForest
|
6
|
+
from sklearn.metrics import f1_score, recall_score, precision_score
|
7
|
+
import matplotlib.pyplot as plt
|
8
|
+
|
9
|
+
# ============= Helper Functions =========================================================================
|
10
|
+
def match_nearest(row, usgs):
|
11
|
+
target_time = row['date']
|
12
|
+
same_day = usgs[usgs['date'] == target_time.date()]
|
13
|
+
if same_day.empty:
|
14
|
+
return np.nan
|
15
|
+
delta = (same_day['datetime'] - target_time).abs()
|
16
|
+
return same_day.loc[delta.idxmin(), 'Turbidity']
|
17
|
+
|
18
|
+
|
19
|
+
|
20
|
+
# ============= Preprocessing and Feature Engineering ========================================================
|
21
|
+
def preprocess_data(sentinel, usgs):
|
22
|
+
# Add matched turbidity
|
23
|
+
sentinel['Turbidity'] = sentinel.apply(lambda row: match_nearest(row, usgs), axis=1)
|
24
|
+
df = sentinel.dropna(subset=['Turbidity'])
|
25
|
+
|
26
|
+
# Water pixels filtering
|
27
|
+
if 'SCL' in df.columns and (df['SCL'] == 6).sum() > 0:
|
28
|
+
df = df[df['SCL'] == 6].drop_duplicates(subset=['B2', 'B3', 'B4'])
|
29
|
+
|
30
|
+
# Feature engineering
|
31
|
+
bands = ['B2','B3','B4','B5','B6','B7','B8','B8A','B9','B11','B12']
|
32
|
+
df['NDVI'] = (df['B8'] - df['B4']) / (df['B8'] + df['B4'])
|
33
|
+
df['NDWI'] = (df['B3'] - df['B8']) / (df['B3'] + df['B8'])
|
34
|
+
df['NDSI'] = (df['B3'] - df['B11']) / (df['B3'] + df['B11'])
|
35
|
+
|
36
|
+
df = df.sort_values('date').reset_index(drop=True)
|
37
|
+
df['Turbidity_diff1'] = df['Turbidity'].diff()
|
38
|
+
df['Turbidity_diff2'] = df['Turbidity_diff1'].diff()
|
39
|
+
thresh = 2 * df['Turbidity_diff2'].std()
|
40
|
+
df['spike'] = (df['Turbidity_diff2'].abs() > thresh).astype(int)
|
41
|
+
df = df.dropna()
|
42
|
+
|
43
|
+
# Class label
|
44
|
+
df['Classe'] = (df['Turbidity'] > 20).astype(int)
|
45
|
+
return df, bands
|
46
|
+
|
47
|
+
|
48
|
+
# ============= Anomaly Detection Methods ================================================================
|
49
|
+
def run_oneclass_svm(sentinel, usgs, plot=True):
|
50
|
+
"""
|
51
|
+
Apply One-Class SVM anomaly detection on Sentinel/USGS data.
|
52
|
+
Returns: DataFrame with predictions, and best model parameters.
|
53
|
+
"""
|
54
|
+
df, bands = preprocess_data(sentinel, usgs)
|
55
|
+
features = bands + ['NDVI','NDWI','NDSI','Turbidity_diff1','Turbidity_diff2','spike']
|
56
|
+
X = df[features].fillna(df[features].mean()).values
|
57
|
+
y = df['Classe'].values
|
58
|
+
|
59
|
+
scaler = StandardScaler()
|
60
|
+
X_scaled = scaler.fit_transform(X)
|
61
|
+
|
62
|
+
X_class0 = X_scaled[y == 0]
|
63
|
+
X_class1 = X_scaled[y == 1]
|
64
|
+
|
65
|
+
train_size = max(1, int(0.8 * len(X_class0)))
|
66
|
+
X_train = X_class0[:train_size]
|
67
|
+
X_test = np.vstack([X_class0[train_size:], X_class1])
|
68
|
+
y_test = np.array([0]*(len(X_class0)-train_size) + [1]*len(X_class1))
|
69
|
+
|
70
|
+
best_f1 = -1
|
71
|
+
best_model, best_y_pred, best_params = None, None, None
|
72
|
+
for gamma in ['auto', 'scale']:
|
73
|
+
for nu in [0.01, 0.05, 0.1, 0.2]:
|
74
|
+
model = OneClassSVM(kernel='rbf', gamma=gamma, nu=nu)
|
75
|
+
model.fit(X_train)
|
76
|
+
y_pred = np.where(model.predict(X_test) == 1, 0, 1)
|
77
|
+
if len(np.unique(y_pred)) > 1:
|
78
|
+
f1 = f1_score(y_test, y_pred)
|
79
|
+
if f1 > best_f1:
|
80
|
+
best_f1 = f1
|
81
|
+
best_model = model
|
82
|
+
best_y_pred = y_pred
|
83
|
+
best_params = {'gamma': gamma, 'nu': nu}
|
84
|
+
|
85
|
+
if best_f1 > -1:
|
86
|
+
df_out = df.iloc[-len(y_test):].copy()
|
87
|
+
df_out['predicted'] = best_y_pred
|
88
|
+
if plot:
|
89
|
+
plt.figure(figsize=(15,6))
|
90
|
+
plt.plot(df_out['date'], df_out['Turbidity'], label='Turbidity', color='blue')
|
91
|
+
plt.scatter(df_out[df_out['Classe']==1]['date'], df_out[df_out['Classe']==1]['Turbidity'],
|
92
|
+
color='red', marker='x', label='True Anomaly', s=100)
|
93
|
+
plt.scatter(df_out[df_out['predicted']==1]['date'], df_out[df_out['predicted']==1]['Turbidity'],
|
94
|
+
edgecolors='orange', facecolors='none', marker='o', label='Predicted Anomaly', s=80)
|
95
|
+
plt.title("True vs Predicted Anomalies (OneClassSVM)")
|
96
|
+
plt.xlabel("Date")
|
97
|
+
plt.ylabel("Turbidity")
|
98
|
+
plt.legend()
|
99
|
+
plt.grid(True)
|
100
|
+
plt.tight_layout()
|
101
|
+
plt.show()
|
102
|
+
return df_out, best_params, best_f1
|
103
|
+
else:
|
104
|
+
print("Could not find a good model. Try different hyperparameters.")
|
105
|
+
return None, None, None
|
106
|
+
|
107
|
+
|
108
|
+
# ============= Isolation Forest Method ================================================================
|
109
|
+
def run_isolation_forest(sentinel, usgs, plot=True):
|
110
|
+
"""
|
111
|
+
Apply Isolation Forest anomaly detection on Sentinel/USGS data.
|
112
|
+
Returns: DataFrame with predictions, and best model parameters.
|
113
|
+
"""
|
114
|
+
df, bands = preprocess_data(sentinel, usgs)
|
115
|
+
features = bands + ['NDVI','NDWI','NDSI','Turbidity_diff1','Turbidity_diff2','spike']
|
116
|
+
X = df[features].fillna(df[features].mean()).values
|
117
|
+
y = df['Classe'].values
|
118
|
+
|
119
|
+
scaler = StandardScaler()
|
120
|
+
X_scaled = scaler.fit_transform(X)
|
121
|
+
|
122
|
+
X_class0 = X_scaled[y == 0]
|
123
|
+
X_class1 = X_scaled[y == 1]
|
124
|
+
|
125
|
+
train_size = max(1, int(0.8 * len(X_class0)))
|
126
|
+
X_train = X_class0[:train_size]
|
127
|
+
X_test = np.vstack([X_class0[train_size:], X_class1])
|
128
|
+
y_test = np.array([0]*(len(X_class0)-train_size) + [1]*len(X_class1))
|
129
|
+
|
130
|
+
best_f1 = -1
|
131
|
+
best_model, best_y_pred, best_params = None, None, None
|
132
|
+
for contamination in [0.01, 0.05, 0.1, 0.15, 0.2, 0.3]:
|
133
|
+
model = IsolationForest(
|
134
|
+
n_estimators=100,
|
135
|
+
contamination=contamination,
|
136
|
+
max_samples='auto',
|
137
|
+
bootstrap=True,
|
138
|
+
random_state=42
|
139
|
+
)
|
140
|
+
model.fit(X_train)
|
141
|
+
y_pred = np.where(model.predict(X_test) == 1, 0, 1)
|
142
|
+
if len(np.unique(y_pred)) > 1:
|
143
|
+
f1 = f1_score(y_test, y_pred)
|
144
|
+
if f1 > best_f1:
|
145
|
+
best_f1 = f1
|
146
|
+
best_model = model
|
147
|
+
best_y_pred = y_pred
|
148
|
+
best_params = {'contamination': contamination}
|
149
|
+
|
150
|
+
if best_f1 > -1:
|
151
|
+
df_out = df.iloc[-len(y_test):].copy()
|
152
|
+
df_out['predicted'] = best_y_pred
|
153
|
+
if plot:
|
154
|
+
plt.figure(figsize=(15,6))
|
155
|
+
plt.plot(df_out['date'], df_out['Turbidity'], label='Turbidity', color='blue')
|
156
|
+
plt.scatter(df_out[df_out['Classe']==1]['date'], df_out[df_out['Classe']==1]['Turbidity'],
|
157
|
+
color='red', marker='x', label='True Anomaly', s=100)
|
158
|
+
plt.scatter(df_out[df_out['predicted']==1]['date'], df_out[df_out['predicted']==1]['Turbidity'],
|
159
|
+
edgecolors='orange', facecolors='none', marker='o', label='Predicted Anomaly', s=80)
|
160
|
+
plt.title("True vs Predicted Anomalies (Isolation Forest)")
|
161
|
+
plt.xlabel("Date")
|
162
|
+
plt.ylabel("Turbidity")
|
163
|
+
plt.legend()
|
164
|
+
plt.grid(True)
|
165
|
+
plt.tight_layout()
|
166
|
+
plt.show()
|
167
|
+
return df_out, best_params, best_f1
|
168
|
+
else:
|
169
|
+
print("Could not find a good model. Try different hyperparameters.")
|
170
|
+
return None, None, None
|
hydroanomaly/sentinel_bands.py
CHANGED
@@ -4,13 +4,15 @@ Sentinel-2 Satellite Data Retrieval using Google Earth Engine (GEE)
|
|
4
4
|
This module provides a function to retrieve Sentinel-2 satellite band data
|
5
5
|
for a specified location and time period, with masking and cloud filtering.
|
6
6
|
"""
|
7
|
-
import ee
|
7
|
+
import ee
|
8
|
+
import geemap
|
8
9
|
import pandas as pd
|
9
10
|
import numpy as np
|
10
11
|
from datetime import datetime, timedelta
|
11
12
|
import requests
|
12
13
|
import warnings
|
13
14
|
|
15
|
+
# Retrive data from Google Earth Engine ========================================================
|
14
16
|
def get_sentinel_bands_gee(
|
15
17
|
latitude: float,
|
16
18
|
longitude: float,
|
@@ -98,6 +100,54 @@ def get_sentinel_bands_gee(
|
|
98
100
|
df = df.set_index('date')
|
99
101
|
return df
|
100
102
|
|
103
|
+
|
104
|
+
# Showing map with NDWI (Normalized Difference Water Index) ========================================================
|
105
|
+
def show_sentinel_ndwi_map(
|
106
|
+
latitude: float,
|
107
|
+
longitude: float,
|
108
|
+
start_date: str,
|
109
|
+
end_date: str,
|
110
|
+
buffer_meters: int = 20,
|
111
|
+
cloudy_pixel_percentage: int = 20,
|
112
|
+
zoom: int = 15
|
113
|
+
):
|
114
|
+
"""
|
115
|
+
Display an interactive map showing the NDWI, point, and buffer.
|
116
|
+
|
117
|
+
Args:
|
118
|
+
latitude (float): Latitude of the center point.
|
119
|
+
longitude (float): Longitude of the center point.
|
120
|
+
start_date (str): Start date as "YYYY-MM-DD".
|
121
|
+
end_date (str): End date as "YYYY-MM-DD".
|
122
|
+
buffer_meters (int): Buffer radius in meters.
|
123
|
+
cloudy_pixel_percentage (int): Max allowed cloud percentage.
|
124
|
+
zoom (int): Zoom level for map.
|
125
|
+
"""
|
126
|
+
point = ee.Geometry.Point([longitude, latitude])
|
127
|
+
buffer = point.buffer(buffer_meters)
|
128
|
+
|
129
|
+
image = (ee.ImageCollection("COPERNICUS/S2_SR_HARMONIZED")
|
130
|
+
.filterBounds(buffer)
|
131
|
+
.filterDate(start_date, end_date)
|
132
|
+
.filter(ee.Filter.lt("CLOUDY_PIXEL_PERCENTAGE", cloudy_pixel_percentage))
|
133
|
+
.median())
|
134
|
+
|
135
|
+
ndwi = image.normalizedDifference(["B3", "B8"]).rename("NDWI")
|
136
|
+
|
137
|
+
ndwi_vis = {
|
138
|
+
'min': 0,
|
139
|
+
'max': 1,
|
140
|
+
'palette': ['white', 'cyan', 'blue']}
|
141
|
+
|
142
|
+
Map = geemap.Map()
|
143
|
+
Map.centerObject(buffer, zoom=zoom)
|
144
|
+
Map.addLayer(ndwi, ndwi_vis, "NDWI (Water)")
|
145
|
+
Map.addLayer(point, {'color': 'yellow'}, 'Point')
|
146
|
+
Map.addLayer(buffer, {'color': 'red'}, 'Buffer')
|
147
|
+
Map.add_colorbar(ndwi_vis, label="NDWI", layer_name="NDWI (Water)")
|
148
|
+
return Map
|
149
|
+
|
150
|
+
|
101
151
|
# Aliases for user convenience
|
102
152
|
get_sentinel_bands = get_sentinel_bands_gee
|
103
153
|
get_satellite_data = get_sentinel_bands_gee
|
hydroanomaly/visualize.py
CHANGED
@@ -11,7 +11,7 @@ import pandas as pd
|
|
11
11
|
import numpy as np
|
12
12
|
from datetime import datetime
|
13
13
|
|
14
|
-
|
14
|
+
# ==============================================================================================
|
15
15
|
def plot_timeseries(data: pd.DataFrame, title: str = "Time Series Data", save_file: str = None) -> None:
|
16
16
|
"""
|
17
17
|
Create a simple time series plot.
|
@@ -26,10 +26,10 @@ def plot_timeseries(data: pd.DataFrame, title: str = "Time Series Data", save_fi
|
|
26
26
|
"""
|
27
27
|
|
28
28
|
if data.empty:
|
29
|
-
print("
|
29
|
+
print("No data to plot")
|
30
30
|
return
|
31
31
|
|
32
|
-
print(f"
|
32
|
+
print(f"Creating plot: {title}")
|
33
33
|
|
34
34
|
# Create figure
|
35
35
|
plt.figure(figsize=(12, 6))
|
@@ -59,12 +59,12 @@ def plot_timeseries(data: pd.DataFrame, title: str = "Time Series Data", save_fi
|
|
59
59
|
# Save if requested
|
60
60
|
if save_file:
|
61
61
|
plt.savefig(save_file, dpi=300, bbox_inches='tight')
|
62
|
-
print(f"
|
62
|
+
print(f"Plot saved as {save_file}")
|
63
63
|
|
64
64
|
plt.show()
|
65
|
-
print("
|
66
|
-
|
65
|
+
print("Plot created successfully!")
|
67
66
|
|
67
|
+
# ==============================================================================================
|
68
68
|
def plot_turbidity(turbidity_data: pd.DataFrame, save_file: str = None) -> None:
|
69
69
|
"""
|
70
70
|
Create a turbidity-specific plot with appropriate formatting.
|
@@ -75,10 +75,10 @@ def plot_turbidity(turbidity_data: pd.DataFrame, save_file: str = None) -> None:
|
|
75
75
|
"""
|
76
76
|
|
77
77
|
if turbidity_data.empty:
|
78
|
-
print("
|
78
|
+
print("No turbidity data to plot")
|
79
79
|
return
|
80
80
|
|
81
|
-
print("
|
81
|
+
print("Creating turbidity plot")
|
82
82
|
|
83
83
|
plt.figure(figsize=(12, 6))
|
84
84
|
|
@@ -92,7 +92,7 @@ def plot_turbidity(turbidity_data: pd.DataFrame, save_file: str = None) -> None:
|
|
92
92
|
plt.axhline(y=25, color='red', linestyle='--', alpha=0.7, label='High (25 NTU)')
|
93
93
|
|
94
94
|
# Format plot
|
95
|
-
plt.title('
|
95
|
+
plt.title('Turbidity Time Series', fontsize=14, fontweight='bold', pad=20)
|
96
96
|
plt.xlabel('Date', fontsize=12)
|
97
97
|
plt.ylabel('Turbidity (NTU)', fontsize=12)
|
98
98
|
plt.grid(True, alpha=0.3)
|
@@ -107,12 +107,12 @@ def plot_turbidity(turbidity_data: pd.DataFrame, save_file: str = None) -> None:
|
|
107
107
|
# Save if requested
|
108
108
|
if save_file:
|
109
109
|
plt.savefig(save_file, dpi=300, bbox_inches='tight')
|
110
|
-
print(f"
|
110
|
+
print(f"Turbidity plot saved as {save_file}")
|
111
111
|
|
112
112
|
plt.show()
|
113
|
-
print("
|
114
|
-
|
113
|
+
print("Turbidity plot created!")
|
115
114
|
|
115
|
+
# ==============================================================================================
|
116
116
|
def plot_sentinel(sentinel_data: pd.DataFrame, save_file: str = None) -> None:
|
117
117
|
"""
|
118
118
|
Create a Sentinel satellite data plot.
|
@@ -123,10 +123,10 @@ def plot_sentinel(sentinel_data: pd.DataFrame, save_file: str = None) -> None:
|
|
123
123
|
"""
|
124
124
|
|
125
125
|
if sentinel_data.empty:
|
126
|
-
print("
|
126
|
+
print("No Sentinel data to plot")
|
127
127
|
return
|
128
128
|
|
129
|
-
print("
|
129
|
+
print("Creating Sentinel bands plot")
|
130
130
|
|
131
131
|
plt.figure(figsize=(12, 8))
|
132
132
|
|
@@ -146,7 +146,7 @@ def plot_sentinel(sentinel_data: pd.DataFrame, save_file: str = None) -> None:
|
|
146
146
|
label=column, color=color, linewidth=2, marker='o', markersize=4)
|
147
147
|
|
148
148
|
# Format plot
|
149
|
-
plt.title('
|
149
|
+
plt.title('Sentinel Satellite Data', fontsize=14, fontweight='bold', pad=20)
|
150
150
|
plt.xlabel('Date', fontsize=12)
|
151
151
|
plt.ylabel('Digital Number / Index Value', fontsize=12)
|
152
152
|
plt.grid(True, alpha=0.3)
|
@@ -161,12 +161,12 @@ def plot_sentinel(sentinel_data: pd.DataFrame, save_file: str = None) -> None:
|
|
161
161
|
# Save if requested
|
162
162
|
if save_file:
|
163
163
|
plt.savefig(save_file, dpi=300, bbox_inches='tight')
|
164
|
-
print(f"
|
164
|
+
print(f"Sentinel plot saved as {save_file}")
|
165
165
|
|
166
166
|
plt.show()
|
167
|
-
print("
|
168
|
-
|
167
|
+
print("Sentinel plot created!")
|
169
168
|
|
169
|
+
# ==============================================================================================
|
170
170
|
def plot_comparison(data1: pd.DataFrame, data2: pd.DataFrame,
|
171
171
|
label1: str = "Dataset 1", label2: str = "Dataset 2",
|
172
172
|
title: str = "Data Comparison", save_file: str = None) -> None:
|
@@ -183,10 +183,10 @@ def plot_comparison(data1: pd.DataFrame, data2: pd.DataFrame,
|
|
183
183
|
"""
|
184
184
|
|
185
185
|
if data1.empty and data2.empty:
|
186
|
-
print("
|
186
|
+
print("No data to plot")
|
187
187
|
return
|
188
188
|
|
189
|
-
print(f"
|
189
|
+
print(f"Creating comparison plot: {title}")
|
190
190
|
|
191
191
|
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 10), sharex=True)
|
192
192
|
|
@@ -215,10 +215,10 @@ def plot_comparison(data1: pd.DataFrame, data2: pd.DataFrame,
|
|
215
215
|
# Save if requested
|
216
216
|
if save_file:
|
217
217
|
plt.savefig(save_file, dpi=300, bbox_inches='tight')
|
218
|
-
print(f"
|
218
|
+
print(f"Comparison plot saved as {save_file}")
|
219
219
|
|
220
220
|
plt.show()
|
221
|
-
print("
|
221
|
+
print("Comparison plot created!")
|
222
222
|
|
223
223
|
|
224
224
|
# Simple aliases
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: hydroanomaly
|
3
|
-
Version:
|
3
|
+
Version: 1.2.0
|
4
4
|
Summary: A Python package for hydro anomaly detection with simple USGS data retrieval
|
5
5
|
Author-email: Ehsan Kahrizi <ehsan.kahrizi@usu.edu>
|
6
6
|
License: MIT License
|
@@ -28,7 +28,7 @@ License: MIT License
|
|
28
28
|
Project-URL: Homepage, https://github.com/yourusername/hydroanomaly
|
29
29
|
Project-URL: Bug Reports, https://github.com/yourusername/hydroanomaly/issues
|
30
30
|
Project-URL: Source, https://github.com/yourusername/hydroanomaly
|
31
|
-
Keywords: python,package,
|
31
|
+
Keywords: python,package,hydrology,anomaly detection,remote sensing
|
32
32
|
Classifier: Programming Language :: Python :: 3
|
33
33
|
Classifier: Operating System :: OS Independent
|
34
34
|
Requires-Python: >=3.6
|
@@ -0,0 +1,10 @@
|
|
1
|
+
hydroanomaly/__init__.py,sha256=IGPsOv-xfQGQpr_HxhrcZXjvVVkAfMBxh97e6S8Rfac,1664
|
2
|
+
hydroanomaly/ml.py,sha256=vqfnmGijjxGgtqJ2rzOmnMMrrVAVlYOPe1AnuX4EuG4,7018
|
3
|
+
hydroanomaly/sentinel_bands.py,sha256=Y6RAunVJDYLs13WemSSQNEu07GqmhR64fC2mLPxwh2k,5371
|
4
|
+
hydroanomaly/usgs_turbidity.py,sha256=k0cXRXpTe1YgjfR0Htw77SLD8hM--43jiEiJwx1vRg0,5664
|
5
|
+
hydroanomaly/visualize.py,sha256=d_Ou1sTr648TdAW-94NXwNbLPL4rvYVYb5pw4Xux3aE,7228
|
6
|
+
hydroanomaly-1.2.0.dist-info/licenses/LICENSE,sha256=OphKV48tcMv6ep-7j-8T6nycykPT0g8ZlMJ9zbGvdPs,1066
|
7
|
+
hydroanomaly-1.2.0.dist-info/METADATA,sha256=bh51WnUxEbk3azZY2IN9a9Io1Pav7knU90qMiiiTGDU,12981
|
8
|
+
hydroanomaly-1.2.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
9
|
+
hydroanomaly-1.2.0.dist-info/top_level.txt,sha256=t-5Lc-eTLlkxIhR_N1Cpp6_YZafKS3xLLk9D2CtbE7o,13
|
10
|
+
hydroanomaly-1.2.0.dist-info/RECORD,,
|
@@ -1,9 +0,0 @@
|
|
1
|
-
hydroanomaly/__init__.py,sha256=blfdU8RYTU_JTMgD_OUEx6YNEZW_Xi6Nn6vqgOdgbt8,1388
|
2
|
-
hydroanomaly/sentinel_bands.py,sha256=LL_ChqpN6d_aJ0nq0YPWkL_vp9ns5bkSRp0UcKYe3o8,3637
|
3
|
-
hydroanomaly/usgs_turbidity.py,sha256=k0cXRXpTe1YgjfR0Htw77SLD8hM--43jiEiJwx1vRg0,5664
|
4
|
-
hydroanomaly/visualize.py,sha256=gkLgI3agx291jK5o08nYEbEpGpr6cD-6aAKn2Ha2Lqk,6937
|
5
|
-
hydroanomaly-0.7.3.dist-info/licenses/LICENSE,sha256=OphKV48tcMv6ep-7j-8T6nycykPT0g8ZlMJ9zbGvdPs,1066
|
6
|
-
hydroanomaly-0.7.3.dist-info/METADATA,sha256=yRZiu7Pea2bWa42QUcfkxTTtgBRRD2AOAPLsThwqXSw,12962
|
7
|
-
hydroanomaly-0.7.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
8
|
-
hydroanomaly-0.7.3.dist-info/top_level.txt,sha256=t-5Lc-eTLlkxIhR_N1Cpp6_YZafKS3xLLk9D2CtbE7o,13
|
9
|
-
hydroanomaly-0.7.3.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|