From d3e6c9f0da4c00ade684c27f1509495eb7588cdf Mon Sep 17 00:00:00 2001 From: Zhang Yunjun Date: Mon, 11 Nov 2024 17:35:37 +0800 Subject: [PATCH 1/2] `gnss`: handle longitude <=-180 or >=360 + objects/gnss.py: add `_set_longitude_range()` to handle the longitude data range - ensure longitude is within (-180, 360) - could specify the data range format, in either [0, 360) or (-180, 180] via `lon_range` arg + call `_set_longitude_range()` in all the child class of GNSS This error is happending for the GNSS_UNR sites in Japan, where the longitude of G073 can be -229, resulting in NaN value for inc/az_angle extration from the geometry file, in the old code. --- src/mintpy/objects/gnss.py | 55 +++++++++++++++++++++++++++++++++++--- 1 file changed, 51 insertions(+), 4 deletions(-) diff --git a/src/mintpy/objects/gnss.py b/src/mintpy/objects/gnss.py index b4de31d27..3c8c10a71 100644 --- a/src/mintpy/objects/gnss.py +++ b/src/mintpy/objects/gnss.py @@ -71,7 +71,8 @@ def search_gnss(SNWE, start_date=None, end_date=None, source='UNR', site_list_fi # ensure that site data formatting is consistent sites['site'] = np.array([site.upper() for site in sites['site']]) - sites['lon'][sites['lon'] > 180] -= 360 # ensure lon values in (-180, 180] + # ensure longitude values in (-180, 180] + sites['lon'] = _set_longitude_range(sites['lon'], lon_range='-180to180') vprint(f'load {len(sites["site"]):d} GNSS sites with fields: {" ".join(sites.keys())}') # limit in space @@ -391,6 +392,42 @@ def get_ESESES_url_prefix(): return url_prefix +def _set_longitude_range(lon, lon_range='-180to180'): + """Set the longitude data range. + + Parameters: lon - float / np.ndarray, longitude in degree + lon_range - str, -180to180 or 0to360 + Returns: lon - float / np.ndarray, longitude in degree + """ + + # ensure data within (-180, 360) + if np.nanmax(lon) >= 360: + if np.isscalar(lon): + lon -= 360 + else: + lon[lon >= 360] -= 360 + elif np.nanmin(lon) <= -180: + if np.isscalar(lon): + lon += 360 + else: + lon[lon <= -180] += 360 + + # range option 1: ensure data within (-180, 180] + if lon_range == '-180to180' and np.nanmax(lon) > 180: + if np.isscalar(lon): + lon -= 360 + else: + lon[lon > 180] -= 360 + + # range option 2: ensure data within [0, 360) + elif lon_range == '0to360' and np.nanmin(lon) < 0: + if np.isscalar(lon): + lon += 360 + else: + lon[lon < 0] += 360 + + return lon + #################################### GNSS-GSI utility functions ##################################### @@ -867,6 +904,9 @@ def get_site_lat_lon(self, print_msg=False) -> (float, float): data = np.loadtxt(self.file, dtype=bytes, skiprows=1, max_rows=10) self.site_lat, self.site_lon = data[0, 20:22].astype(float) + # ensure longitude in the range of (-180, 180] + self.site_lon = _set_longitude_range(self.site_lon, lon_range='-180to180') + return self.site_lat, self.site_lon @@ -987,8 +1027,9 @@ def get_site_lat_lon(self, print_msg=False) -> (float, float): # longitude lon_line = [x for x in lines if x.startswith('# East Longitude')][0].strip('\n') self.site_lon = float(lon_line.split()[-1]) - # ensure longitude in the range of (-180, 180] - self.site_lon -= 0 if self.site_lon <= 180 else 360 + + # ensure longitude in the range of (-180, 180] + self.site_lon = _set_longitude_range(self.site_lon, lon_range='-180to180') return self.site_lat, self.site_lon @@ -1094,6 +1135,8 @@ def get_site_lat_lon(self, print_msg=False) -> (float, float): # format self.site_lat = float(site_lat) self.site_lon = float(site_lon) + # ensure longitude in the range of (-180, 180] + self.site_lon = _set_longitude_range(self.site_lon, lon_range='-180to180') if print_msg == True: print(f'\t{self.site_lat:f}, {self.site_lon:f}') @@ -1185,7 +1228,11 @@ def get_site_lat_lon(self, print_msg=False) -> (str, str): """ sites = read_GENERIC_site_list('GenericList.txt') ind = sites['site'].tolist().index(self.site) - return sites['lat'][ind], sites['lon'][ind] + site_lat, site_lon = sites['lat'][ind], sites['lon'][ind] + # ensure longitude in the range of (-180, 180] + site_lon = _set_longitude_range(site_lon, lon_range='-180to180') + + return site_lat, site_lon def read_displacement(self, start_date=None, end_date=None, print_msg=True, display=False): From be03e45f30103769cdcef5f655a14c6ec3a3b1f0 Mon Sep 17 00:00:00 2001 From: Zhang Yunjun Date: Mon, 11 Nov 2024 18:00:01 +0800 Subject: [PATCH 2/2] rename and move to utils0.py + rename `_set_longitude_range()` to `standardize_longitude()` + move the function from objects/gnss.py to utils/utils0.py --- src/mintpy/objects/gnss.py | 46 +++++--------------------------------- src/mintpy/utils/utils0.py | 24 ++++++++++++++++++++ 2 files changed, 29 insertions(+), 41 deletions(-) diff --git a/src/mintpy/objects/gnss.py b/src/mintpy/objects/gnss.py index 3c8c10a71..89bea2c19 100644 --- a/src/mintpy/objects/gnss.py +++ b/src/mintpy/objects/gnss.py @@ -72,7 +72,7 @@ def search_gnss(SNWE, start_date=None, end_date=None, source='UNR', site_list_fi # ensure that site data formatting is consistent sites['site'] = np.array([site.upper() for site in sites['site']]) # ensure longitude values in (-180, 180] - sites['lon'] = _set_longitude_range(sites['lon'], lon_range='-180to180') + sites['lon'] = ut.standardize_longitude(sites['lon'], limit='-180to180') vprint(f'load {len(sites["site"]):d} GNSS sites with fields: {" ".join(sites.keys())}') # limit in space @@ -392,42 +392,6 @@ def get_ESESES_url_prefix(): return url_prefix -def _set_longitude_range(lon, lon_range='-180to180'): - """Set the longitude data range. - - Parameters: lon - float / np.ndarray, longitude in degree - lon_range - str, -180to180 or 0to360 - Returns: lon - float / np.ndarray, longitude in degree - """ - - # ensure data within (-180, 360) - if np.nanmax(lon) >= 360: - if np.isscalar(lon): - lon -= 360 - else: - lon[lon >= 360] -= 360 - elif np.nanmin(lon) <= -180: - if np.isscalar(lon): - lon += 360 - else: - lon[lon <= -180] += 360 - - # range option 1: ensure data within (-180, 180] - if lon_range == '-180to180' and np.nanmax(lon) > 180: - if np.isscalar(lon): - lon -= 360 - else: - lon[lon > 180] -= 360 - - # range option 2: ensure data within [0, 360) - elif lon_range == '0to360' and np.nanmin(lon) < 0: - if np.isscalar(lon): - lon += 360 - else: - lon[lon < 0] += 360 - - return lon - #################################### GNSS-GSI utility functions ##################################### @@ -905,7 +869,7 @@ def get_site_lat_lon(self, print_msg=False) -> (float, float): data = np.loadtxt(self.file, dtype=bytes, skiprows=1, max_rows=10) self.site_lat, self.site_lon = data[0, 20:22].astype(float) # ensure longitude in the range of (-180, 180] - self.site_lon = _set_longitude_range(self.site_lon, lon_range='-180to180') + self.site_lon = ut.standardize_longitude(self.site_lon, limit='-180to180') return self.site_lat, self.site_lon @@ -1029,7 +993,7 @@ def get_site_lat_lon(self, print_msg=False) -> (float, float): self.site_lon = float(lon_line.split()[-1]) # ensure longitude in the range of (-180, 180] - self.site_lon = _set_longitude_range(self.site_lon, lon_range='-180to180') + self.site_lon = ut.standardize_longitude(self.site_lon, limit='-180to180') return self.site_lat, self.site_lon @@ -1136,7 +1100,7 @@ def get_site_lat_lon(self, print_msg=False) -> (float, float): self.site_lat = float(site_lat) self.site_lon = float(site_lon) # ensure longitude in the range of (-180, 180] - self.site_lon = _set_longitude_range(self.site_lon, lon_range='-180to180') + self.site_lon = ut.standardize_longitude(self.site_lon, limit='-180to180') if print_msg == True: print(f'\t{self.site_lat:f}, {self.site_lon:f}') @@ -1230,7 +1194,7 @@ def get_site_lat_lon(self, print_msg=False) -> (str, str): ind = sites['site'].tolist().index(self.site) site_lat, site_lon = sites['lat'][ind], sites['lon'][ind] # ensure longitude in the range of (-180, 180] - site_lon = _set_longitude_range(site_lon, lon_range='-180to180') + site_lon = ut.standardize_longitude(site_lon, limit='-180to180') return site_lat, site_lon diff --git a/src/mintpy/utils/utils0.py b/src/mintpy/utils/utils0.py index d410a13af..cb7f31594 100644 --- a/src/mintpy/utils/utils0.py +++ b/src/mintpy/utils/utils0.py @@ -267,6 +267,30 @@ def touch(fname_list, times=None): ################################## Coordinate ########################################## +def standardize_longitude(lon, limit='-180to180'): + """Normalize the longitude value range into (-180, 180] or [0, 360). + + Parameters: lon - float / np.ndarray, longitude in degree + limit - str, -180to180 or 0to360 + Returns: lon - float / np.ndarray, longitude in degree + """ + lon = np.asarray(lon) + + # ensure data within (-180, 360) + lon = np.where(lon >= 360, lon - 360, lon) + lon = np.where(lon <= -180, lon + 360, lon) + + # range option 1: ensure data within (-180, 180] + if limit == '-180to180': + lon = np.where(lon > 180, lon - 360, lon) + + # range option 2: ensure data within [0, 360) + elif limit == '0to360' and np.nanmin(lon) < 0: + lon = np.where(lon < 0, lon + 360, lon) + + return float(lon) if np.isscalar(lon) else lon + + def utm_zone2epsg_code(utm_zone): """Convert UTM Zone string to EPSG code.