hydroanomaly 0.6.0__tar.gz → 0.7.0__tar.gz

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.
@@ -1,11 +1,30 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hydroanomaly
3
- Version: 0.6.0
3
+ Version: 0.7.0
4
4
  Summary: A Python package for hydro anomaly detection with simple USGS data retrieval
5
- Home-page: https://github.com/yourusername/hydroanomaly
6
- Author: Your Name
7
- Author-email: Your Name <your.email@example.com>
8
- License-Expression: MIT
5
+ Author-email: Ehsan Kahrizi <ehsan.kahrizi@usu.edu>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2025 Your Name
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
9
28
  Project-URL: Homepage, https://github.com/yourusername/hydroanomaly
10
29
  Project-URL: Bug Reports, https://github.com/yourusername/hydroanomaly/issues
11
30
  Project-URL: Source, https://github.com/yourusername/hydroanomaly
@@ -30,10 +49,7 @@ Requires-Dist: pytest>=6.0; extra == "dev"
30
49
  Requires-Dist: black>=21.0; extra == "dev"
31
50
  Requires-Dist: flake8>=3.8; extra == "dev"
32
51
  Requires-Dist: mypy>=0.800; extra == "dev"
33
- Dynamic: author
34
- Dynamic: home-page
35
52
  Dynamic: license-file
36
- Dynamic: requires-python
37
53
 
38
54
  # HydroAnomaly
39
55
 
@@ -2,15 +2,15 @@
2
2
  HydroAnomaly: Simple Water Data Analysis Package
3
3
 
4
4
  A simple Python package with just 3 modules:
5
- 1. USGS turbidity data retrieval
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
8
 
9
9
  That's it - nothing else!
10
10
  """
11
11
 
12
- __version__ = "0.6.0"
13
- __author__ = "HydroAnomaly Team"
12
+ __version__ = "0.7.0"
13
+ __author__ = "Ehsan Kahrizi (Ehsan.kahrizi@usu.edu)"
14
14
 
15
15
  # Import the 3 simple modules
16
16
  from .usgs_turbidity import get_turbidity, get_usgs_turbidity
@@ -38,9 +38,9 @@ __all__ = [
38
38
  'visualize'
39
39
  ]
40
40
 
41
- print(f"🌊 HydroAnomaly v{__version__} - Simple Water Data Package")
42
- print("📚 Available functions:")
43
- print(" • get_turbidity() - Get USGS turbidity data")
41
+ print(f"HydroAnomaly v{__version__} - Simple Water Data Package")
42
+ print("Available functions:")
43
+ print(" • get_turbidity() - Get USGS turbidity data and site coordinates")
44
44
  print(" • get_sentinel_bands() - Get satellite data")
45
45
  print(" • plot_timeseries() - Visualize data")
46
- print("💡 Try: help(hydroanomaly.get_turbidity) for examples")
46
+ print("Try: help(hydroanomaly.get_turbidity) for examples")
@@ -0,0 +1,104 @@
1
+ """
2
+ Sentinel-2 Satellite Data Retrieval using Google Earth Engine (GEE)
3
+
4
+ This module provides a function to retrieve Sentinel-2 satellite band data
5
+ for a specified location and time period, with masking and cloud filtering.
6
+ """
7
+
8
+ import pandas as pd
9
+ import numpy as np
10
+ from datetime import datetime, timedelta
11
+ import requests
12
+ import warnings
13
+
14
+ def get_sentinel_bands_gee(
15
+ latitude: float,
16
+ longitude: float,
17
+ start_date: str,
18
+ end_date: str,
19
+ bands: list = None,
20
+ buffer_meters: int = 20,
21
+ cloudy_pixel_percentage: int = 20,
22
+ masks_to_apply: list = None
23
+ ) -> pd.DataFrame:
24
+ """
25
+ Retrieve Sentinel-2 bands from Google Earth Engine, applying custom masking.
26
+
27
+ Args:
28
+ latitude (float): Latitude of center point.
29
+ longitude (float): Longitude of center point.
30
+ start_date (str): Start date as "YYYY-MM-DD".
31
+ end_date (str): End date as "YYYY-MM-DD".
32
+ bands (list): List of bands to retrieve (default is common Sentinel-2 bands).
33
+ buffer_meters (int): Buffer size around the point, in meters.
34
+ cloudy_pixel_percentage (int): Maximum allowed cloud percentage for each image.
35
+ masks_to_apply (list): Masking strategies (e.g., ["water", "no_cloud_shadow", ...]).
36
+
37
+ Returns:
38
+ pd.DataFrame: DataFrame with band reflectance values per date.
39
+
40
+ Example:
41
+ >>> import ee
42
+ >>> ee.Authenticate()
43
+ >>> ee.Initialize()
44
+ >>> df = get_sentinel_bands_gee(29.77, -95.06, "2021-01-01", "2021-12-31")
45
+ >>> print(df.head())
46
+ """
47
+ if bands is None:
48
+ bands = ['B2','B3','B4','B8','SCL']
49
+ if masks_to_apply is None:
50
+ masks_to_apply = ["water", "no_cloud_shadow", "no_clouds", "no_snow_ice", "no_saturated"]
51
+
52
+ point = ee.Geometry.Point([longitude, latitude])
53
+ buffered_point = point.buffer(buffer_meters)
54
+
55
+ s2 = (ee.ImageCollection('COPERNICUS/S2_SR_HARMONIZED')
56
+ .filterBounds(buffered_point)
57
+ .filterDate(start_date, end_date)
58
+ .filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', cloudy_pixel_percentage))
59
+ .select(bands))
60
+
61
+ def dynamic_scl_mask(image):
62
+ scl = image.select('SCL')
63
+ mask = ee.Image.constant(1)
64
+ if "water" in masks_to_apply:
65
+ mask = mask.And(scl.eq(6))
66
+ if "no_cloud_shadow" in masks_to_apply:
67
+ mask = mask.And(scl.neq(3))
68
+ if "no_clouds" in masks_to_apply:
69
+ cloud_mask = scl.neq(8).And(scl.neq(9)).And(scl.neq(10))
70
+ mask = mask.And(cloud_mask)
71
+ if "no_snow_ice" in masks_to_apply:
72
+ mask = mask.And(scl.neq(11))
73
+ if "no_saturated" in masks_to_apply:
74
+ mask = mask.And(scl.neq(1))
75
+ return image.updateMask(mask)
76
+
77
+ s2_masked = s2.map(dynamic_scl_mask)
78
+
79
+ def extract_features(image):
80
+ date = image.date().format('YYYY-MM-dd HH:mm:ss')
81
+ values = image.reduceRegion(
82
+ reducer=ee.Reducer.mean(),
83
+ geometry=buffered_point,
84
+ scale=20,
85
+ maxPixels=1e8
86
+ )
87
+ return ee.Feature(None, values.set('date', date))
88
+
89
+ features = s2_masked.map(extract_features)
90
+ fc = ee.FeatureCollection(features).filter(ee.Filter.notNull(['B2']))
91
+
92
+ data = fc.getInfo()
93
+ rows = [f['properties'] for f in data['features']]
94
+ df = pd.DataFrame(rows)
95
+ if not df.empty:
96
+ df['date'] = pd.to_datetime(df['date'])
97
+ df = df.sort_values('date')
98
+ df = df.set_index('date')
99
+ return df
100
+
101
+ # Aliases for user convenience
102
+ get_sentinel_bands = get_sentinel_bands_gee
103
+ get_satellite_data = get_sentinel_bands_gee
104
+ get_sentinel = get_sentinel_bands_gee
@@ -11,7 +11,7 @@ from io import StringIO
11
11
  from datetime import datetime
12
12
  import numpy as np
13
13
 
14
-
14
+ # Function for retrive data ---------------------------------------------------------------------------------------------------------------------------------------------------------------------
15
15
  def get_turbidity(site_number: str, start_date: str, end_date: str) -> pd.DataFrame:
16
16
  """
17
17
  Get turbidity data from a USGS station.
@@ -22,50 +22,75 @@ def get_turbidity(site_number: str, start_date: str, end_date: str) -> pd.DataFr
22
22
  end_date (str): End date as "YYYY-MM-DD"
23
23
 
24
24
  Returns:
25
- pd.DataFrame: Time series data with datetime index and turbidity values
25
+ tuple: (pd.DataFrame, (latitude, longitude)) or (empty DataFrame, (None, None)) if not found.
26
+ * Note: pd.DataFrame: Time series data with datetime index and turbidity values
26
27
 
27
28
  Example:
28
29
  >>> data = get_turbidity("294643095035200", "2023-01-01", "2023-12-31")
29
30
  >>> print(f"Got {len(data)} turbidity measurements")
30
31
  """
32
+
33
+ # --- Validate inputs ---------------------------------------------------------------------------------------------------------------------------------------------------------------------
34
+ print(f"Getting turbidity data for site {site_number}")
35
+ print(f"Date range: {start_date} to {end_date}")
36
+
37
+ # --- Retrieve site metadata (lat/lon) ---------------------------------------------------------------------------------------------------------------------------------------------------------------------
38
+ site_url = (
39
+ f"https://waterservices.usgs.gov/nwis/site/"
40
+ f"?sites={site_number}"
41
+ f"&format=rdb")
42
+ try:
43
+ site_resp = requests.get(site_url, timeout=15)
44
+ if site_resp.status_code != 200:
45
+ print(f"Could not get site metadata: {site_resp.status_code}")
46
+ lat, lon = None, None
47
+ else:
48
+ df_meta = pd.read_csv(StringIO(site_resp.text), sep="\t", comment="#")
49
+ df_meta = df_meta.dropna(axis=1, how="all")
50
+ lat, lon = None, None
51
+ if not df_meta.empty:
52
+ lat = float(df_meta["dec_lat_va"].iloc[0]) if "dec_lat_va" in df_meta.columns else None
53
+ lon = float(df_meta["dec_long_va"].iloc[0]) if "dec_long_va" in df_meta.columns else None
54
+ except Exception as e:
55
+ print(f"Error getting site coordinates: {e}")
56
+ lat, lon = None, None
31
57
 
32
- print(f"🌊 Getting turbidity data for site {site_number}")
33
- print(f"📅 Date range: {start_date} to {end_date}")
34
58
 
35
- # Build USGS API URL for turbidity (parameter code 63680)
59
+ # --- Retrieve turbidity data (Build USGS API URL for turbidity (parameter code 63680))------------------------------------------------------------------------------------------------------------------------------------------------------------------
36
60
  url = (
37
61
  f"https://waterservices.usgs.gov/nwis/iv/"
38
62
  f"?sites={site_number}"
39
63
  f"&parameterCd=63680" # Turbidity parameter code
40
64
  f"&startDT={start_date}"
41
65
  f"&endDT={end_date}"
42
- f"&format=rdb"
43
- )
66
+ f"&format=rdb")
44
67
 
45
68
  try:
46
69
  # Get data from USGS
47
70
  response = requests.get(url, timeout=30)
48
71
 
49
72
  if response.status_code != 200:
50
- print(f" API Error: {response.status_code}")
51
- return _create_synthetic_turbidity(start_date, end_date)
73
+ print(f"No data found: API returned status {response.status_code}.")
74
+ print("Data for the specified site or parameters does not exist.")
75
+ return pd.DataFrame(), (lat, lon)
52
76
 
53
77
  # Parse the response
54
78
  data = _parse_usgs_response(response.text)
55
79
 
56
80
  if len(data) == 0:
57
- print("⚠️ No real data found. Creating synthetic data...")
58
- return _create_synthetic_turbidity(start_date, end_date)
81
+ print("No data found for the specified parameters or date range.")
82
+ return pd.DataFrame(), (lat, lon)
59
83
 
60
- print(f"Retrieved {len(data)} turbidity measurements")
61
- return data
84
+ print(f"Retrieved {len(data)} turbidity measurements")
85
+ return data, (lat, lon)
62
86
 
63
87
  except Exception as e:
64
- print(f"Error: {e}")
65
- print("🔄 Creating synthetic turbidity data...")
66
- return _create_synthetic_turbidity(start_date, end_date)
88
+ print(f"Error: {e}")
89
+ print("Data for the specified site or parameters does not exist.")
90
+ return pd.DataFrame(), (lat, lon)
67
91
 
68
92
 
93
+ # Function for parse and cleaning Turbidity Time Series from USGS API Respons as DataFrame ---------------------------------------------------------------------------------------------------------------------------------------------------------------------
69
94
  def _parse_usgs_response(content: str) -> pd.DataFrame:
70
95
  """Parse USGS response and extract turbidity data."""
71
96
 
@@ -107,6 +132,8 @@ def _parse_usgs_response(content: str) -> pd.DataFrame:
107
132
  return pd.DataFrame()
108
133
 
109
134
 
135
+ """
136
+ # Generate synthetic data if data not exist in USGS ---------------------------------------------------------------------------------------------------------------------------------------------------------------------
110
137
  def _create_synthetic_turbidity(start_date: str, end_date: str) -> pd.DataFrame:
111
138
  """Create realistic synthetic turbidity data."""
112
139
 
@@ -141,10 +168,10 @@ def _create_synthetic_turbidity(start_date: str, end_date: str) -> pd.DataFrame:
141
168
  'turbidity': synthetic_values
142
169
  }, index=date_range)
143
170
 
144
- print(f"📊 Created {len(synthetic_data)} synthetic turbidity measurements")
171
+ print(f"Created {len(synthetic_data)} synthetic turbidity measurements")
145
172
 
146
173
  return synthetic_data
147
-
174
+ """
148
175
 
149
176
  # Simple alias for backwards compatibility
150
177
  get_usgs_turbidity = get_turbidity
@@ -1,11 +1,30 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hydroanomaly
3
- Version: 0.6.0
3
+ Version: 0.7.0
4
4
  Summary: A Python package for hydro anomaly detection with simple USGS data retrieval
5
- Home-page: https://github.com/yourusername/hydroanomaly
6
- Author: Your Name
7
- Author-email: Your Name <your.email@example.com>
8
- License-Expression: MIT
5
+ Author-email: Ehsan Kahrizi <ehsan.kahrizi@usu.edu>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2025 Your Name
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
9
28
  Project-URL: Homepage, https://github.com/yourusername/hydroanomaly
10
29
  Project-URL: Bug Reports, https://github.com/yourusername/hydroanomaly/issues
11
30
  Project-URL: Source, https://github.com/yourusername/hydroanomaly
@@ -30,10 +49,7 @@ Requires-Dist: pytest>=6.0; extra == "dev"
30
49
  Requires-Dist: black>=21.0; extra == "dev"
31
50
  Requires-Dist: flake8>=3.8; extra == "dev"
32
51
  Requires-Dist: mypy>=0.800; extra == "dev"
33
- Dynamic: author
34
- Dynamic: home-page
35
52
  Dynamic: license-file
36
- Dynamic: requires-python
37
53
 
38
54
  # HydroAnomaly
39
55
 
@@ -1,7 +1,6 @@
1
1
  LICENSE
2
2
  README.md
3
3
  pyproject.toml
4
- setup.py
5
4
  hydroanomaly/__init__.py
6
5
  hydroanomaly/sentinel_bands.py
7
6
  hydroanomaly/usgs_turbidity.py
@@ -4,13 +4,15 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "hydroanomaly"
7
- version = "0.6.0"
7
+ version = "0.7.0"
8
8
  authors = [
9
- {name = "Your Name", email = "your.email@example.com"},
9
+ {name = "Ehsan Kahrizi", email = "ehsan.kahrizi@usu.edu"},
10
10
  ]
11
+
12
+
11
13
  description = "A Python package for hydro anomaly detection with simple USGS data retrieval"
12
14
  readme = "README.md"
13
- license = "MIT"
15
+ license = {file = "LICENSE"}
14
16
  requires-python = ">=3.6"
15
17
  classifiers = [
16
18
  "Programming Language :: Python :: 3",
@@ -1,157 +0,0 @@
1
- """
2
- Simple Sentinel Satellite Data Retrieval
3
-
4
- This module provides one simple function to get Sentinel satellite bands.
5
- That's it - nothing else!
6
- """
7
-
8
- import pandas as pd
9
- import numpy as np
10
- from datetime import datetime, timedelta
11
- import requests
12
- import warnings
13
-
14
-
15
- def get_sentinel_bands(latitude: float, longitude: float, start_date: str, end_date: str, bands: list = None) -> pd.DataFrame:
16
- """
17
- Get Sentinel satellite band data for a location.
18
-
19
- Args:
20
- latitude (float): Latitude coordinate (e.g., 30.2672)
21
- longitude (float): Longitude coordinate (e.g., -97.7431)
22
- start_date (str): Start date as "YYYY-MM-DD"
23
- end_date (str): End date as "YYYY-MM-DD"
24
- bands (list): List of bands to retrieve (default: ['B2', 'B3', 'B4', 'B8'])
25
-
26
- Returns:
27
- pd.DataFrame: Time series data with datetime index and band values
28
-
29
- Example:
30
- >>> data = get_sentinel_bands(30.2672, -97.7431, "2023-01-01", "2023-12-31")
31
- >>> print(f"Got {len(data)} satellite observations")
32
- """
33
-
34
- if bands is None:
35
- bands = ['B2', 'B3', 'B4', 'B8'] # Blue, Green, Red, NIR
36
-
37
- print(f"🛰️ Getting Sentinel data for location ({latitude}, {longitude})")
38
- print(f"📅 Date range: {start_date} to {end_date}")
39
- print(f"📡 Bands: {', '.join(bands)}")
40
-
41
- try:
42
- # Try to get real Sentinel data (this would normally use Google Earth Engine or similar)
43
- # For now, we'll create realistic synthetic data
44
- print("⚠️ Creating synthetic Sentinel data (real API integration would go here)")
45
- return _create_synthetic_sentinel(latitude, longitude, start_date, end_date, bands)
46
-
47
- except Exception as e:
48
- print(f"❌ Error: {e}")
49
- print("🔄 Creating synthetic Sentinel data...")
50
- return _create_synthetic_sentinel(latitude, longitude, start_date, end_date, bands)
51
-
52
-
53
- def _create_synthetic_sentinel(lat: float, lon: float, start_date: str, end_date: str, bands: list) -> pd.DataFrame:
54
- """Create realistic synthetic Sentinel satellite data."""
55
-
56
- # Generate dates (Sentinel-2 has ~5-day revisit time)
57
- start_dt = datetime.strptime(start_date, "%Y-%m-%d")
58
- end_dt = datetime.strptime(end_date, "%Y-%m-%d")
59
-
60
- # Create observation dates (every 5-10 days, accounting for clouds)
61
- observation_dates = []
62
- current_date = start_dt
63
-
64
- while current_date <= end_dt:
65
- # Add some randomness to simulate real satellite schedule
66
- if np.random.random() > 0.3: # 70% chance of successful observation
67
- observation_dates.append(current_date)
68
-
69
- # Next observation in 5-10 days
70
- days_to_add = np.random.randint(5, 11)
71
- current_date += timedelta(days=days_to_add)
72
-
73
- if len(observation_dates) == 0:
74
- print("⚠️ No observation dates generated")
75
- return pd.DataFrame()
76
-
77
- # Generate realistic band values
78
- data_dict = {'datetime': observation_dates}
79
-
80
- for band in bands:
81
- if band == 'B2': # Blue (450-520nm)
82
- base_value = 1200
83
- variation = 300
84
- elif band == 'B3': # Green (520-600nm)
85
- base_value = 1400
86
- variation = 350
87
- elif band == 'B4': # Red (630-690nm)
88
- base_value = 1300
89
- variation = 400
90
- elif band == 'B8': # NIR (760-900nm)
91
- base_value = 2800
92
- variation = 800
93
- else: # Generic band
94
- base_value = 1500
95
- variation = 400
96
-
97
- # Generate values with seasonal and random variation
98
- band_values = []
99
- for i, date in enumerate(observation_dates):
100
- # Seasonal variation (higher vegetation in summer)
101
- day_of_year = date.timetuple().tm_yday
102
- seasonal_factor = np.sin(2 * np.pi * day_of_year / 365) * 0.2
103
-
104
- # Random variation
105
- noise = np.random.normal(0, variation * 0.3)
106
-
107
- # Calculate final value
108
- value = base_value * (1 + seasonal_factor) + noise
109
- value = max(0, int(value)) # Ensure positive integer values
110
-
111
- band_values.append(value)
112
-
113
- data_dict[band] = band_values
114
-
115
- # Create DataFrame
116
- result = pd.DataFrame(data_dict)
117
- result['datetime'] = pd.to_datetime(result['datetime'])
118
- result = result.set_index('datetime')
119
-
120
- print(f"📊 Created {len(result)} synthetic Sentinel observations")
121
- print(f"📡 Bands included: {', '.join(bands)}")
122
-
123
- return result
124
-
125
-
126
- def calculate_ndvi(sentinel_data: pd.DataFrame) -> pd.DataFrame:
127
- """
128
- Calculate NDVI from Sentinel data.
129
-
130
- Args:
131
- sentinel_data (pd.DataFrame): DataFrame with B4 (Red) and B8 (NIR) bands
132
-
133
- Returns:
134
- pd.DataFrame: DataFrame with NDVI values
135
- """
136
-
137
- if 'B4' not in sentinel_data.columns or 'B8' not in sentinel_data.columns:
138
- print("❌ Need B4 (Red) and B8 (NIR) bands to calculate NDVI")
139
- return pd.DataFrame()
140
-
141
- # Calculate NDVI = (NIR - Red) / (NIR + Red)
142
- red = sentinel_data['B4']
143
- nir = sentinel_data['B8']
144
-
145
- ndvi = (nir - red) / (nir + red)
146
-
147
- result = pd.DataFrame({'NDVI': ndvi}, index=sentinel_data.index)
148
-
149
- print(f"📊 Calculated NDVI for {len(result)} observations")
150
- print(f"🌱 NDVI range: {ndvi.min():.3f} to {ndvi.max():.3f}")
151
-
152
- return result
153
-
154
-
155
- # Simple aliases
156
- get_satellite_data = get_sentinel_bands
157
- get_sentinel = get_sentinel_bands
@@ -1,31 +0,0 @@
1
- from setuptools import setup, find_packages
2
-
3
- setup(
4
- name="hydroanomaly",
5
- version="0.5.0",
6
- author="Your Name",
7
- author_email="your.email@example.com",
8
- description="A Python package for hydro anomaly detection with simple USGS data retrieval",
9
- long_description=open("README.md").read(),
10
- long_description_content_type="text/markdown",
11
- url="https://github.com/yourusername/hydroanomaly",
12
- packages=find_packages(),
13
- classifiers=[
14
- "Programming Language :: Python :: 3",
15
- "License :: OSI Approved :: MIT License",
16
- "Operating System :: OS Independent",
17
- ],
18
- python_requires=">=3.6",
19
- install_requires=[
20
- "pandas>=1.3.0",
21
- "numpy>=1.20.0",
22
- "requests>=2.25.1",
23
- "matplotlib>=3.3.0",
24
- "seaborn>=0.11.0",
25
- "earthengine-api>=0.1.300",
26
- "geemap>=0.20.0",
27
- "tqdm>=4.60.0",
28
- "scipy>=1.7.0",
29
- "scikit-learn>=1.0.0",
30
- ],
31
- )
File without changes
File without changes
File without changes