ocstrack 0.1.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.
@@ -0,0 +1,369 @@
1
+ import os
2
+ import requests
3
+ import logging
4
+ import shutil
5
+ import time
6
+
7
+ import xarray as xr
8
+
9
+ from datetime import datetime, timedelta
10
+ from typing import Union, Optional, List
11
+ from tqdm import tqdm
12
+
13
+ from .urls import URL_TEMPLATES
14
+
15
+
16
+ logging.basicConfig(
17
+ level=logging.WARNING,
18
+ format='%(asctime)s - %(levelname)s - %(message)s',
19
+ handlers=[
20
+ logging.StreamHandler(), # Print logs to the console
21
+ # logging.FileHandler('get_sat.log', mode='w')
22
+ ]
23
+ )
24
+ _logger = logging.getLogger()
25
+
26
+
27
+ def generate_daily_dates(start_date_str: str,
28
+ end_date_str: str) -> List[str]:
29
+ """
30
+ This function generates a list of formated
31
+ dates between the start and end dates.
32
+
33
+ Args:
34
+ start_date_str: String with dates as 'YYYY-MM-DD'
35
+ end_date_str: String with dates as 'YYYY-MM-DD'
36
+
37
+ Returns:
38
+ List of formated dates (daily).
39
+ """
40
+
41
+ start_date = datetime.strptime(start_date_str,
42
+ '%Y-%m-%d')
43
+ end_date = datetime.strptime(end_date_str,
44
+ '%Y-%m-%d')
45
+
46
+ return [(start_date + timedelta(days=i)).strftime('%Y%m%d')
47
+ for i in range((end_date - start_date).days + 1)]
48
+
49
+
50
+ def download_sat_data(dates_str: List[str],
51
+ url_template: str,
52
+ raw_dir: str,
53
+ sat: str,
54
+ retries: int = 1,
55
+ delay: int = 5) -> List[str]:
56
+ """
57
+ This function downloads the satellite data
58
+
59
+ Parameters
60
+ ----------
61
+ dates_str: List with all the dates data will be downloaded for
62
+ url_template: from urls.py
63
+ raw_dir: path to where the raw sat data will be saved
64
+ retries: how many times will it try to download the data
65
+ delay: how long will it wait to try the download again
66
+
67
+ Returns
68
+ -------
69
+ List of paths to the downlaoded satellite files.
70
+ """
71
+ os.makedirs(raw_dir, exist_ok=True)
72
+ raw_files = []
73
+
74
+ for date_str in tqdm(dates_str, desc=f"Downloading {sat} data..."):
75
+ url = f"{url_template}{date_str}.nc"
76
+ filename = os.path.basename(url)
77
+ raw_path = os.path.join(raw_dir, filename)
78
+
79
+ if not os.path.exists(raw_path):
80
+ for attempt in range(retries):
81
+ try:
82
+ with requests.get(url, stream=True) as r:
83
+ r.raise_for_status()
84
+ with open(raw_path, 'wb') as f:
85
+ for chunk in r.iter_content(chunk_size=8192):
86
+ f.write(chunk)
87
+ _logger.info(f"Downloaded {filename}")
88
+ break
89
+ except requests.RequestException as e:
90
+ _logger.warning(f"Attempt {attempt+1}/{retries} \
91
+ failed for {url}: {e}")
92
+ time.sleep(delay)
93
+ else:
94
+ _logger.error(f"Failed to download {filename} after \
95
+ {retries} attempts.")
96
+ else:
97
+ _logger.info(f"File already exists: {filename}")
98
+ raw_files.append(raw_path)
99
+
100
+ return raw_files
101
+
102
+ def crop_by_box(dataset: xr.Dataset,
103
+ lat_min: float,
104
+ lat_max: float,
105
+ lon_min: float,
106
+ lon_max: float) -> xr.Dataset:
107
+ """
108
+ Crops xarray data based on lats and lons
109
+
110
+ Parameters
111
+ ----------
112
+ lat_min: float/int of mininum latitude
113
+ lat_max: float/int of maximum latitude
114
+ lon_min: float/int of mininum longitude
115
+ lon_max: float/int of maximum latitude
116
+ Returns
117
+ -------
118
+ xarray object of the cropped data
119
+
120
+ Notes
121
+ -----
122
+ Satellite data uses the -180 to 180 standard
123
+ If you want to cross the meridian, then pass a lon_min > lon_max
124
+
125
+ """
126
+ # Check if latitude and longitude coordinates are in the dataset
127
+ if 'lat' not in dataset or 'lon' not in dataset:
128
+ raise ValueError("Dataset does not contain lat or lon dimensions")
129
+
130
+ if lon_min < lon_max:
131
+ lon_mask = (dataset.lon >= lon_min) & (dataset.lon <= lon_max)
132
+ else:
133
+ lon_mask = (dataset.lon >= lon_min) | (dataset.lon <= lon_max)
134
+
135
+ lat_mask = (dataset.lat >= lat_min) & (dataset.lat <= lat_max)
136
+ cropped = dataset.where(lat_mask & lon_mask, drop=True)
137
+
138
+ return cropped
139
+
140
+ def crop_by_shape():
141
+ """
142
+ To be implemented
143
+ """
144
+ pass
145
+
146
+ def crop_sat_data(file_paths: List[str],
147
+ cropped_dir: str,
148
+ lat_min: float,
149
+ lat_max: float,
150
+ lon_min: float,
151
+ lon_max: float) -> List[xr.Dataset]:
152
+ """
153
+ Handles the satellite data cropping
154
+ Crops and saves all the satellite data
155
+
156
+ Parameters
157
+ ----------
158
+ lat_min: float/int of mininum latitude
159
+ lat_max: float/int of maximum latitude
160
+ lon_min: float/int of mininum longitude
161
+ lon_max: float/int of maximum latitude
162
+
163
+ Returns
164
+ -------
165
+ xarray object of the cropped data
166
+
167
+ Notes
168
+ -----
169
+ Satellite data uses the -180 to 180 standard
170
+ If you want to change the longitudes, use util.convert_longitude(sat_data.lon,mode=?)
171
+ """
172
+ os.makedirs(cropped_dir, exist_ok=True)
173
+ cropped_datasets = []
174
+
175
+ for file_path in tqdm(file_paths, desc="Cropping"):
176
+ try:
177
+ with xr.open_dataset(file_path) as ds:
178
+ ds.load()
179
+ cropped = crop_by_box(ds, lat_min, lat_max, lon_min, lon_max)
180
+ if cropped.dims and all(size > 0 for size in cropped.sizes.values()):
181
+ out_path = os.path.join(cropped_dir,
182
+ f"cropped_\
183
+ {os.path.basename(file_path)}")
184
+ cropped.to_netcdf(out_path)
185
+ _logger.info(f"Saved {out_path}")
186
+ cropped_datasets.append(cropped)
187
+ else:
188
+ _logger.warning(f"Skipping empty cropped dataset: \
189
+ {file_path}")
190
+ except Exception as e:
191
+ _logger.warning(f"Failed to crop \
192
+ {file_path}: {type(e).__name__} - {e}")
193
+
194
+ return cropped_datasets
195
+
196
+
197
+ def concat_sat_data(datasets: List[xr.Dataset],
198
+ output_path: str,
199
+ sat: str) -> Optional[xr.Dataset]:
200
+ """
201
+ Handles the satellite data concatenation
202
+ Concat and saves all the satellite data on the datasets list
203
+
204
+ Parameters
205
+ ----------
206
+ datasets: List xr satellite data to be concatenated
207
+ output_path: path to where the concatenated data will be saved
208
+ sat: name of the satellite
209
+
210
+ Returns
211
+ ----------
212
+ xarray object of the concatenated data
213
+ """
214
+ if not datasets:
215
+ _logger.warning("No datasets to concatenate.")
216
+ return None
217
+
218
+ try:
219
+ concat_ds = xr.concat(datasets, dim='time')
220
+ concat_ds = concat_ds.assign_coords(source=sat)
221
+ concat_ds.to_netcdf(output_path)
222
+ _logger.info(f"Concatenated dataset saved to {output_path}")
223
+ return concat_ds
224
+ except Exception as e:
225
+ _logger.warning(f"Failed to concatenate datasets: \
226
+ {type(e).__name__} - {e}")
227
+ return None
228
+
229
+
230
+ def get_per_sat(start_date: str,
231
+ end_date: str,
232
+ sat: str,
233
+ output_dir: Union[str, os.PathLike],
234
+ lat_min: Optional[float] = None,
235
+ lat_max: Optional[float] = None,
236
+ lon_min: Optional[float] = None,
237
+ lon_max: Optional[float] = None,
238
+ concat: bool = True,
239
+ clean_raw: bool = False,
240
+ clean_cropped: bool = False) -> Optional[xr.Dataset]:
241
+ """
242
+ Download, crop, and optionally concatenate satellite data.
243
+
244
+ Parameters
245
+ ----------
246
+ start_date: Start date in 'YYYY-MM-DD'
247
+ end_date: End date in 'YYYY-MM-DD'
248
+ sat: Satellite key for URL_TEMPLATES
249
+ output_dir: Directory to save files
250
+ lat_min, lat_max, lon_min, lon_max: Optional cropping bounds
251
+ concat: Save a single concatenated output
252
+ clean_raw: Delete raw files after processing
253
+ clean_cropped: Delete cropped files after processing
254
+
255
+ Returns
256
+ ----------
257
+ xarray.Dataset if concatenated, otherwise None
258
+ """
259
+ try:
260
+ url_template = URL_TEMPLATES[sat]
261
+ except KeyError:
262
+ raise ValueError(f"Unknown satellite key: {sat}")
263
+
264
+ output_dir = os.path.join(output_dir, sat)
265
+ raw_dir = os.path.join(output_dir, "raw")
266
+ cropped_dir = os.path.join(output_dir, "cropped")
267
+ os.makedirs(output_dir, exist_ok=True)
268
+
269
+ cropping_enabled = None not in (lat_min, lat_max, lon_min, lon_max)
270
+ dates_str = generate_daily_dates(start_date, end_date)
271
+
272
+ raw_files = download_sat_data(dates_str, url_template, raw_dir, sat)
273
+
274
+ datasets_to_concat = []
275
+ if cropping_enabled:
276
+ datasets_to_concat = crop_sat_data(raw_files,
277
+ cropped_dir,
278
+ lat_min,
279
+ lat_max,
280
+ lon_min,
281
+ lon_max)
282
+ else:
283
+ for file_path in raw_files:
284
+ try:
285
+ with xr.open_dataset(file_path) as ds:
286
+ ds.load()
287
+ datasets_to_concat.append(ds)
288
+ except Exception as e:
289
+ _logger.warning(f"Failed to load \
290
+ {file_path}: {type(e).__name__} - {e}")
291
+
292
+ final_dataset = None
293
+ if concat and datasets_to_concat:
294
+ concat_filename = f"concat_{'cropped_' if cropping_enabled else ''}{sat}_{start_date}_{end_date}.nc"
295
+ concat_path = os.path.join(output_dir, concat_filename)
296
+ final_dataset = concat_sat_data(datasets_to_concat, concat_path, sat)
297
+
298
+ if clean_raw and os.path.exists(raw_dir):
299
+ shutil.rmtree(raw_dir)
300
+ _logger.info("Raw files removed.")
301
+
302
+ if clean_cropped and os.path.exists(cropped_dir):
303
+ shutil.rmtree(cropped_dir)
304
+ _logger.info("Cropped files removed.")
305
+
306
+ return final_dataset
307
+
308
+ def get_multi_sat(start_date: str,
309
+ end_date: str,
310
+ sat_list: List,
311
+ output_dir: Union[str, os.PathLike],
312
+ lat_min: Optional[float] = None,
313
+ lat_max: Optional[float] = None,
314
+ lon_min: Optional[float] = None,
315
+ lon_max: Optional[float] = None,
316
+ concat: bool = True,
317
+ clean_raw: bool = True,
318
+ clean_cropped: bool = True) -> Optional[xr.Dataset]:
319
+ """
320
+ Run download and processing for multiple satellites.
321
+
322
+ Parameters
323
+ ----------
324
+ start_date: Start date in 'YYYY-MM-DD'
325
+ end_date: End date in 'YYYY-MM-DD'
326
+ sat: Satellite key for URL_TEMPLATES
327
+ output_dir: Directory to save files
328
+ lat_min, lat_max, lon_min, lon_max: Optional cropping bounds
329
+ concat: Save a single concatenated output
330
+ clean_raw: Delete raw files after processing
331
+
332
+ Returns
333
+ ----------
334
+ xarray.Dataset if concatenated, otherwise None
335
+ """
336
+
337
+ all_sat = []
338
+ for sat in sat_list:
339
+ concat_ds = get_per_sat(start_date,
340
+ end_date,
341
+ sat,
342
+ output_dir,
343
+ lat_min,
344
+ lat_max,
345
+ lon_min,
346
+ lon_max,
347
+ concat=concat,
348
+ clean_raw=clean_raw,
349
+ clean_cropped=clean_cropped)
350
+ if concat_ds is not None:
351
+ all_sat.append(concat_ds)
352
+
353
+ if all_sat:
354
+ try:
355
+ multisat_filename = f"multisat_{'cropped_' if lat_min is not None else ''}{start_date}_{end_date}.nc"
356
+ multisat_path = os.path.join(output_dir, multisat_filename)
357
+ all_sat_ds = xr.concat(all_sat, dim='time')
358
+ all_sat_ds.to_netcdf(multisat_path)
359
+ _logger.info(f"Concatenated multisat dataset saved to \
360
+ {multisat_path}")
361
+ return all_sat_ds
362
+ except Exception as e:
363
+ _logger.warning(f"Failed to concatenate all satellite datasets: \
364
+ {type(e).__name__} - {e}")
365
+ else:
366
+ _logger.warning("No satellite datasets were successfully processed.")
367
+
368
+ return None
369
+
@@ -0,0 +1,103 @@
1
+ import xarray as xr
2
+ import numpy as np
3
+
4
+ from typing import Union
5
+
6
+ class SatelliteData:
7
+ """
8
+ Satellite altimetry data handler.
9
+
10
+ Loads a NetCDF file containing satellite-derived variables such as
11
+ significant wave height (SWH), sea level anomaly (SLA), and metadata.
12
+
13
+ Provides accessor properties and time filtering functionality.
14
+
15
+ Methods
16
+ -------
17
+ filter_by_time(start_date, end_date)
18
+ Restrict the dataset to a specific time range
19
+ """
20
+ def __init__(self, filepath: str):
21
+ """
22
+ Initialize the SatelliteData object by loading a NetCDF dataset.
23
+
24
+ Parameters
25
+ ----------
26
+ filepath : str
27
+ Path to the satellite NetCDF file
28
+
29
+ Raises
30
+ ------
31
+ ValueError
32
+ If required variables are missing from the dataset
33
+ """
34
+ self.ds = xr.open_dataset(filepath)
35
+
36
+ # Check essential variables exist
37
+ required_vars = ['time', 'lon', 'lat', 'swh', 'sla', 'source']
38
+ missing = [v for v in required_vars if v not in self.ds.variables]
39
+ if missing:
40
+ raise ValueError(f"Missing required variables in dataset: {missing}")
41
+
42
+ @property
43
+ def time(self):
44
+ return self.ds.time.values
45
+
46
+ @property
47
+ def lon(self):
48
+ return self.ds.lon.values
49
+ @lon.setter
50
+ def lon(self, new_lon: Union[np.ndarray, list]):
51
+ if len(new_lon) != len(self.ds.lon):
52
+ raise ValueError("New longitude array must match existing size.")
53
+ self.ds['lon'] = ('time', np.array(new_lon))
54
+
55
+ @property
56
+ def lat(self):
57
+ return self.ds.lat.values
58
+ @lat.setter
59
+ def lat(self, new_lat: Union[np.ndarray, list]):
60
+ if len(new_lat) != len(self.ds.lat):
61
+ raise ValueError("New latitude array must match existing size.")
62
+ self.ds['lat'] = ('time', np.array(new_lat))
63
+
64
+ @property
65
+ def swh(self):
66
+ return self.ds.swh.values
67
+
68
+ @property
69
+ def sla(self):
70
+ return self.ds.sla.values
71
+
72
+ @property
73
+ def source(self):
74
+ return self.ds.source.values
75
+
76
+ def filter_by_time(self,
77
+ start_date: str,
78
+ end_date: str) -> None:
79
+ """
80
+ Filter the dataset by time range.
81
+
82
+ Parameters
83
+ ----------
84
+ start_date : str
85
+ ISO 8601 string representing the start date
86
+ end_date : str
87
+ ISO 8601 string representing the end date
88
+
89
+ Notes
90
+ -----
91
+ The method converts time variables to datetime64 and ensures the time
92
+ dimension is sorted before filtering.
93
+ """
94
+ # Convert to datetime for safety
95
+ start = np.datetime64(start_date)
96
+ end = np.datetime64(end_date)
97
+
98
+ # Ensure time is datetime64 and sorted
99
+ if not np.issubdtype(self.ds['time'].dtype, np.datetime64):
100
+ self.ds['time'] = xr.decode_cf(self.ds).time
101
+
102
+ self.ds = self.ds.sortby('time') # Ensure sorted time axis
103
+ self.ds = self.ds.sel(time=slice(start, end))
@@ -0,0 +1,11 @@
1
+
2
+ URL_TEMPLATES = {
3
+ 'sentinel3a': 'https://www.star.nesdis.noaa.gov/data/pub0010/lsa/johnk/coastwatch/3a/3a_',
4
+ 'sentinel3b': 'https://www.star.nesdis.noaa.gov/data/pub0010/lsa/johnk/coastwatch/3b/3b_',
5
+ 'sentinel6a': 'https://www.star.nesdis.noaa.gov/data/pub0010/lsa/johnk/coastwatch/6a/6a_',
6
+ 'jason2': 'https://www.star.nesdis.noaa.gov/data/pub0010/lsa/johnk/coastwatch/j2/j2_',
7
+ 'jason3': 'https://www.star.nesdis.noaa.gov/data/pub0010/lsa/johnk/coastwatch/j3/j3_',
8
+ 'cryosat2': 'https://www.star.nesdis.noaa.gov/data/pub0010/lsa/johnk/coastwatch/c2/c2_',
9
+ 'saral': 'https://www.star.nesdis.noaa.gov/data/pub0010/lsa/johnk/coastwatch/sa/sa_',
10
+ 'swot': 'https://www.star.nesdis.noaa.gov/data/pub0010/lsa/johnk/coastwatch/sw/sw_',
11
+ }
ocstrack/__init__.py ADDED
@@ -0,0 +1 @@
1
+
ocstrack/utils.py ADDED
@@ -0,0 +1,47 @@
1
+ import logging
2
+ from typing import Union
3
+
4
+ import numpy as np
5
+
6
+ # Set up logging
7
+ logging.basicConfig(
8
+ level=logging.WARNING,
9
+ format='%(asctime)s - %(levelname)s - %(message)s',
10
+ handlers=[
11
+ logging.StreamHandler(),
12
+ ]
13
+ )
14
+ _logger = logging.getLogger()
15
+
16
+
17
+ def convert_longitude(lon: Union[float, np.ndarray],
18
+ mode: int = 1) -> np.ndarray:
19
+ """
20
+ Convert longitudes between common geographic conventions.
21
+
22
+ Args:
23
+ lon: array-like of longitudes
24
+ mode: conversion mode:
25
+ - 1: Convert from [-180, 180] (Greenwich at 0°) → [0, 360] (Greenwich at 0°)
26
+ - 2: Convert from [0, 360] (Greenwich at 0°) → [-180, 180] (Greenwich at 0°)
27
+ - 3: Convert from [-180, 180] (Greenwich at 0°) → [0, 360] (Greenwich at 180°)
28
+ - 4: Convert from [0, 360] (Greenwich at 0°) → [0, 360] (Greenwich at 180°)
29
+ - 5: Convert from [0, 360] (Greenwich at 180°) → [0, 360] (Greenwich at 0°)
30
+
31
+ Returns:
32
+ np.ndarray of converted longitudes
33
+ """
34
+ _logger.debug("Converting longitude with mode %d", mode)
35
+ lon = np.asarray(lon)
36
+ if mode == 1:
37
+ return lon % 360
38
+ elif mode == 2:
39
+ return np.where(lon > 180, lon - 360, lon)
40
+ elif mode == 3:
41
+ return lon + 180
42
+ elif mode == 4:
43
+ return (lon + 180) % 360
44
+ elif mode == 5:
45
+ return (lon - 180) % 360
46
+ else:
47
+ raise ValueError(f"Invalid mode {mode}. Supported modes: 1, 2, 3, 4, 5")
@@ -0,0 +1,134 @@
1
+ Metadata-Version: 2.4
2
+ Name: ocstrack
3
+ Version: 0.1.0
4
+ Summary: Satellite data download, crop, and collocation with model outputs
5
+ Author-email: Felicio Cassalho <felicio.cassalho@noaa.gov>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/noaa-ocs-modeling/OCSTrack
8
+ Project-URL: Repository, https://github.com/noaa-ocs-modeling/OCSTrack
9
+ Requires-Python: >=3.10
10
+ Description-Content-Type: text/markdown
11
+ License-File: LICENSE.txt
12
+ Requires-Dist: numpy
13
+ Requires-Dist: xarray
14
+ Requires-Dist: scipy
15
+ Requires-Dist: tqdm
16
+ Requires-Dist: requests
17
+ Requires-Dist: netcdf4
18
+ Requires-Dist: h5netcdf
19
+ Dynamic: license-file
20
+
21
+ # OCSTrack
22
+ OCSTrack is an object-oriented Python package for the along-track collocation of satellite data with ocean circulation and wave model outputs.
23
+ It simplifies the process of aligning diverse datasets, making it easier to compare and analyze satellite observations against model simulations.
24
+
25
+ ## Key Features
26
+ ### Satellite Altimetry Data Support
27
+
28
+ Seamlessly integrates with NOAA [CoastalWatch](https://coastwatch.noaa.gov/cwn/products/along-track-significant-wave-height-wind-speed-and-sea-level-anomaly-multiple-altimeters.html) altimetry data, providing access to a wide range of missions:
29
+ * Jason-2
30
+ * Jason-3
31
+ * Sentinel-3A
32
+ * Sentinel-3B
33
+ * Sentinel-6A
34
+ * CryoSat-2
35
+ * SARAL
36
+ * SWOT
37
+
38
+ ### Ocean Model Data Support
39
+ Supports outputs from various ocean circulation and wave models:
40
+ * [SCHISM+WWM](https://github.com/schism-dev/schism)
41
+ * WaveWatch3 (to be implemented)
42
+ * ADCIRC+SWAN (to be implemented)
43
+
44
+
45
+ ## Installation
46
+
47
+ 1. **Create and activate a new conda environment:**
48
+ This command creates an environment named `ocstrack` and installs all dependencies from `conda-forge`.
49
+ ```bash
50
+ conda create -n ocstrack -c conda-forge python=3.10 numpy xarray scipy tqdm requests netcdf4 h5netcdf
51
+ conda activate ocstrack
52
+ ```
53
+
54
+ 2. **Install `ocstrack` from GitHub:**
55
+ Finally, install this package using `pip`.
56
+ ```bash
57
+ pip install "git+[https://github.com/noaa-ocs-modeling/OCSTrack.git](https://github.com/noaa-ocs-modeling/OCSTrack.git)"
58
+ ```
59
+
60
+ ## Usage
61
+ Here's a typical workflow demonstrating how to use OCSTrack to download satellite data, load model outputs, and perform collocation.
62
+ ```
63
+ import numpy as np
64
+ import xarray as xr
65
+ # Assuming ocstrack is installed and available in your environment
66
+ from ocstrack.Model.model import SCHISM
67
+ from ocstrack.Satellite.satellite import SatelliteData
68
+ from ocstrack.Satellite import get_sat
69
+ from ocstrack.Collocation.collocate import Collocate
70
+ from ocstrack.utils import convert_longitude
71
+
72
+
73
+ # 1. Download Satellite Data
74
+ # Specify your desired date range, list of satellites, output directory, and geographical bounding box.
75
+ get_sat.get_multi_sat(start_date="2019-07-30",
76
+ end_date="2019-08-04",
77
+ sat_list=['sentinel3a','sentinel3b','jason2','jason3','cryosat2','saral'],
78
+ output_dir=r"Your/Path/Here/",
79
+ lat_min=49.109,
80
+ lat_max=66.304309,
81
+ lon_min=156.6854,
82
+ lon_max=-156.864,
83
+ )
84
+
85
+ # 2. Define File Paths
86
+ # Set the paths for your downloaded satellite data, model run, and where you want to save the collocated output.
87
+ sat_path = "/path/to/your/multisat_cropped_2019-07-30_2019-08-04.nc"
88
+ model_path = "/path/to/your/model/run/"
89
+ output_path = "/path/to/your/collocated_output.nc"
90
+ s_time,e_time = "2019-08-01", "2019-08-03"
91
+
92
+ # 3. Load Satellite Data
93
+ # Initialize the SatelliteData object with your satellite data file.
94
+ sat_data = SatelliteData(sat_path)
95
+ # It's crucial to ensure longitude conventions match between satellite and model data.
96
+ # Use convert_longitude if needed (mode=1 for converting to 0-360 degrees).
97
+ sat_data.lon = convert_longitude(sat_data.lon, mode=1)
98
+
99
+ # 4. Load Model Data
100
+ # Instantiate the SCHISM model object, specifying the run directory and model variable details.
101
+ model_run = SCHISM(
102
+ rundir=model_path,
103
+ model_dict={'var': 'sigWaveHeight',
104
+ 'startswith': 'out2d_', # File name prefix for 2D outputs
105
+ 'var_type': '2D',
106
+ 'model': 'SCHISM'},
107
+ start_date=np.datetime64(s_time),
108
+ end_date=np.datetime64(e_time)
109
+ )
110
+
111
+ # 5. Perform Collocation
112
+ # Create a Collocate object, providing the loaded model and satellite data.
113
+ coll = Collocate(
114
+ model_run=model_run,
115
+ satellite=sat_data,
116
+ # dist_coast=dist_coast,
117
+ n_nearest=3,
118
+ # search_radius = 3000,
119
+ temporal_interp=True
120
+ )
121
+ ds_coll = coll.run(output_path=output_path) # Execute the collocation and save the results
122
+ ```
123
+
124
+ ## Contributing
125
+ We welcome contributions to OCSTrack! If you have ideas for improvements, new features, or find a bug, please don't hesitate to open an issue or submit a pull request on our GitHub repository. Your input helps make OCSTrack better for everyone.
126
+
127
+ ### Contact
128
+ <sub><sup>Contact: felicio.cassalho@noaa.gov </sup></sub>
129
+
130
+ ![NOAA logo](https://user-images.githubusercontent.com/72229285/216712553-c1e4b2fa-4b6d-4eab-be0f-f7075b6151d1.png)
131
+
132
+
133
+ #### Acknowledgements:
134
+ *OCSTrack was inspired by the MATLAB-based [WW3-tools](https://github.com/NOAA-EMC/WW3-tools) and [wave-tools](https://github.com/NOAA-EMC/WW3-tools) collocation tools developed for WaveWatch3.*
@@ -0,0 +1,18 @@
1
+ ocstrack/__init__.py,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
2
+ ocstrack/utils.py,sha256=js_J3m2CqHDpryNNRRWkROrqiY4PqC86hH7jHVXHy5s,1543
3
+ ocstrack/Collocation/__init__.py,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
4
+ ocstrack/Collocation/collocate.py,sha256=B_niVPz_zB1cUXJcZyoIbxoH0TEOkOevjoogctAP1Y4,17648
5
+ ocstrack/Collocation/output.py,sha256=NT1orNapFx7WCOkqouSiylL_Qz_BRCjPi1xIYB1SloI,2730
6
+ ocstrack/Collocation/spatial.py,sha256=OH6m7QlkI-FnkegZsX6tkNXxvwXMRCDco41SyS12pGE,6657
7
+ ocstrack/Collocation/temporal.py,sha256=BPjXJqxMiBFNSbLV2gGUSSl39HpZDIZiNByk1-j4eAs,4010
8
+ ocstrack/Model/__init__.py,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
9
+ ocstrack/Model/model.py,sha256=vxkxYMbxFfiV_0T-JDqT2D2q3LZmpL_CFMH6vRLwhBM,7887
10
+ ocstrack/Satellite/__init__.py,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
11
+ ocstrack/Satellite/get_sat.py,sha256=2Ut5K5ca_Pf_Pl9GUv-5Tg7OaujseQ2xG8_qsRfUCBA,12830
12
+ ocstrack/Satellite/satellite.py,sha256=O0vcbRrotp3xaTpVdBct-OnEiI5zG2evSB8siEIIxY8,3135
13
+ ocstrack/Satellite/urls.py,sha256=Q1MSanZwTnsjghRO6j9yH8ZGWVo6Y0EWraxOCN4dR6E,790
14
+ ocstrack-0.1.0.dist-info/licenses/LICENSE.txt,sha256=9Ofzc7m5lpUDN-jUGkopOcLZC3cl6brz1QhKInF60yg,7169
15
+ ocstrack-0.1.0.dist-info/METADATA,sha256=Zfp3T6kMOPg1Na7RwHDJ34MCJ-j34L0nWUqBGCyndy4,5657
16
+ ocstrack-0.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
17
+ ocstrack-0.1.0.dist-info/top_level.txt,sha256=II1d5yXek4m6ld-nK6MGUXQT8dpvgrgPRJuYgyIfJhA,9
18
+ ocstrack-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+