@@ -225,72 +225,148 @@ def mask_and_clean_dataset(*args):
225225 return data_array
226226
227227
228- def find_longitude_index (longitude , lon_list ): # pylint: disable=too-many-statements
229- """Finds the index of the given longitude in a list of longitudes .
228+ def _normalize_longitude_value (longitude , lon_start , lon_end ):
229+ """Normalize longitude based on grid format [-180, 180] or [0, 360] .
230230
231231 Parameters
232232 ----------
233233 longitude : float
234- The longitude to find in the list.
235- lon_list : list of float
236- The list of longitudes.
234+ The longitude to normalize.
235+ lon_start : float
236+ The first longitude value in the grid.
237+ lon_end : float
238+ The last longitude value in the grid.
237239
238240 Returns
239241 -------
240- tuple
241- A tuple containing the adjusted longitude and its index in the list.
242-
243- Raises
244- ------
245- ValueError
246- If the longitude is not within the range covered by the list.
242+ float
243+ The normalized longitude value.
247244 """
248-
249- def _coord_value (source , index ):
250- return float (source [index ])
251-
252- lon_len = len (lon_list )
253- lon_start = _coord_value (lon_list , 0 )
254- lon_end = _coord_value (lon_list , lon_len - 1 )
255-
256245 # Determine if file uses geographic longitudes in [-180, 180] or [0, 360].
257246 # Do not remap projected x coordinates.
258247 is_geographic_longitude = abs (lon_start ) <= 360 and abs (lon_end ) <= 360
259248 if is_geographic_longitude :
260249 if lon_start < 0 or lon_end < 0 :
261- lon = longitude if longitude < 180 else - 180 + longitude % 180
262- else :
263- lon = longitude % 360
264- else :
265- lon = longitude
250+ return longitude if longitude < 180 else - 180 + longitude % 180
251+ return longitude % 360
252+ return longitude
266253
267- is_ascending = lon_start < lon_end
268254
269- # Binary search to find the insertion index such that index-1 and index
270- # bracket the requested longitude.
255+ def _binary_search_coordinate_index (target_value , coord_list , is_ascending ):
256+ """Find insertion index for target value using binary search.
257+
258+ Parameters
259+ ----------
260+ target_value : float
261+ The coordinate value to locate.
262+ coord_list : list of float
263+ The list of coordinate values.
264+ is_ascending : bool
265+ Whether the coordinate list is in ascending order.
266+
267+ Returns
268+ -------
269+ int
270+ The insertion index such that coord_list[index-1] and coord_list[index]
271+ bracket the target value.
272+ """
271273 low = 0
272- high = lon_len
274+ high = len ( coord_list )
273275 while low < high :
274276 mid = (low + high ) // 2
275- mid_value = _coord_value ( lon_list , mid )
276- if (mid_value < lon ) if is_ascending else (mid_value > lon ):
277+ mid_value = float ( coord_list [ mid ] )
278+ if (mid_value < target_value ) if is_ascending else (mid_value > target_value ):
277279 low = mid + 1
278280 else :
279281 high = mid
280- lon_index = low
281-
282- # Take care of longitude value equal to minimum/maximum longitude in the grid
283- if lon_index == 0 and math .isclose (_coord_value (lon_list , 0 ), lon ):
284- lon_index = 1
285- if lon_index == lon_len and _coord_value (lon_list , lon_index - 1 ) == lon :
286- lon_index -= 1
287- # Check if longitude value is inside the grid
288- if lon_index in (0 , lon_len ):
282+ return low
283+
284+
285+ def _adjust_boundary_coordinate_index (index , coord_list , coord_value ):
286+ """Adjust index for exact matches at grid boundaries.
287+
288+ Parameters
289+ ----------
290+ index : int
291+ The current index from binary search.
292+ coord_list : list of float
293+ The list of coordinate values.
294+ coord_value : float
295+ The coordinate value being matched.
296+
297+ Returns
298+ -------
299+ int
300+ The adjusted index after boundary handling.
301+ """
302+ coord_len = len (coord_list )
303+ if index == 0 and math .isclose (float (coord_list [0 ]), coord_value ):
304+ return 1
305+ if index == coord_len and float (coord_list [coord_len - 1 ]) == coord_value :
306+ return index - 1
307+ return index
308+
309+
310+ def _validate_coordinate_index_in_range (index , coord_len , coord_start , coord_end , coord_name ):
311+ """Validate that coordinate index is within valid interpolation range.
312+
313+ Parameters
314+ ----------
315+ index : int
316+ The coordinate index to validate.
317+ coord_len : int
318+ The length of the coordinate list.
319+ coord_start : float
320+ The first coordinate value in the grid.
321+ coord_end : float
322+ The last coordinate value in the grid.
323+ coord_name : str
324+ The name of the coordinate (e.g., "Longitude", "Latitude").
325+
326+ Raises
327+ ------
328+ ValueError
329+ If the index is out of valid range (0 or coord_len).
330+ """
331+ if index in (0 , coord_len ):
289332 raise ValueError (
290- f"Longitude { lon } not inside region covered by file, which is "
291- f"from { lon_start } to { lon_end } ."
333+ f"{ coord_name } not inside region covered by file, which is "
334+ f"from { coord_start } to { coord_end } ."
292335 )
293336
337+
338+ def find_longitude_index (longitude , lon_list ):
339+ """Finds the index of the given longitude in a list of longitudes.
340+
341+ Parameters
342+ ----------
343+ longitude : float
344+ The longitude to find in the list.
345+ lon_list : list of float
346+ The list of longitudes.
347+
348+ Returns
349+ -------
350+ tuple
351+ A tuple containing the adjusted longitude and its index in the list.
352+
353+ Raises
354+ ------
355+ ValueError
356+ If the longitude is not within the range covered by the list.
357+ """
358+ lon_len = len (lon_list )
359+ lon_start = float (lon_list [0 ])
360+ lon_end = float (lon_list [lon_len - 1 ])
361+
362+ lon = _normalize_longitude_value (longitude , lon_start , lon_end )
363+ is_ascending = lon_start < lon_end
364+
365+ lon_index = _binary_search_coordinate_index (lon , lon_list , is_ascending )
366+ lon_index = _adjust_boundary_coordinate_index (lon_index , lon_list , lon )
367+
368+ _validate_coordinate_index_in_range (lon_index , lon_len , lon_start , lon_end , "Longitude" )
369+
294370 return lon , lon_index
295371
296372
@@ -314,37 +390,18 @@ def find_latitude_index(latitude, lat_list):
314390 ValueError
315391 If the latitude is not within the range covered by the list.
316392 """
317-
318- def _coord_value (source , index ):
319- return float (source [index ])
320-
321393 lat_len = len (lat_list )
322- lat_start = _coord_value (lat_list , 0 )
323- lat_end = _coord_value (lat_list , lat_len - 1 )
394+ lat_start = float (lat_list [ 0 ] )
395+ lat_end = float (lat_list [ lat_len - 1 ] )
324396 is_ascending = lat_start < lat_end
325397
326- low = 0
327- high = lat_len
328- while low < high :
329- mid = (low + high ) // 2
330- mid_value = _coord_value (lat_list , mid )
331- if (mid_value < latitude ) if is_ascending else (mid_value > latitude ):
332- low = mid + 1
333- else :
334- high = mid
335- lat_index = low
336-
337- # Take care of latitude value equal to minimum/maximum latitude in the grid
338- if lat_index == 0 and math .isclose (_coord_value (lat_list , 0 ), latitude ):
339- lat_index = 1
340- if lat_index == lat_len and _coord_value (lat_list , lat_index - 1 ) == latitude :
341- lat_index -= 1
342- # Check if latitude value is inside the grid
343- if lat_index in (0 , lat_len ):
344- raise ValueError (
345- f"Latitude { latitude } not inside region covered by file, "
346- f"which is from { lat_start } to { lat_end } ."
347- )
398+ lat_index = _binary_search_coordinate_index (latitude , lat_list , is_ascending )
399+ lat_index = _adjust_boundary_coordinate_index (lat_index , lat_list , latitude )
400+
401+ _validate_coordinate_index_in_range (
402+ lat_index , lat_len , lat_start , lat_end , "Latitude"
403+ )
404+
348405 return latitude , lat_index
349406
350407
0 commit comments