@@ -606,23 +606,32 @@ def get_species_statistics(self, species_code: str) -> Dict:
606606 )
607607
608608 def identify_pixel_value (
609- self ,
610- species_code : str ,
611- x : float ,
612- y : float ,
609+ self ,
610+ species_code : str ,
611+ x : float ,
612+ y : float ,
613613 spatial_ref : str = "102100"
614- ) -> float :
614+ ) -> Optional [ float ] :
615615 """
616616 Get biomass value for a species at a specific coordinate.
617-
618- Args:
619- species_code: FIA species code
620- x: X coordinate
621- y: Y coordinate
622- spatial_ref: Spatial reference system (default: Web Mercator)
623-
624- Returns:
625- Biomass value at the location
617+
618+ Parameters
619+ ----------
620+ species_code : str
621+ FIA species code (e.g., "0131").
622+ x : float
623+ X coordinate in the specified spatial reference.
624+ y : float
625+ Y coordinate in the specified spatial reference.
626+ spatial_ref : str, default="102100"
627+ Spatial reference system (default: Web Mercator).
628+
629+ Returns
630+ -------
631+ Optional[float]
632+ Biomass value at the location, np.nan if the pixel is NoData
633+ (e.g., non-forest or outside coverage), or None if the API
634+ response is missing the value key entirely.
626635 """
627636 function_name = self ._get_function_name (species_code )
628637 if not function_name :
@@ -640,19 +649,19 @@ def identify_pixel_value(
640649 'rasterFunction' : function_name
641650 })
642651 }
643-
652+
644653 try :
645654 response = self ._rate_limited_request ("GET" , f"{ self .base_url } /identify" , params = params )
646655 response .raise_for_status ()
647656 result = response .json ()
648-
657+
649658 if 'value' in result :
650659 value = result ['value' ]
651660 if value == 'NoData' or value is None :
652- return 0.0 # No biomass at this location
661+ return float ( 'nan' )
653662 return float (value )
654663 return None
655-
664+
656665 except requests .RequestException as e :
657666 print_error (f"Failed to identify pixel: { e } " )
658667 raise APIConnectionError (
@@ -804,21 +813,44 @@ def _get_function_name(self, species_code: str) -> Optional[str]:
804813 return None
805814
806815 def _calculate_image_size (
807- self ,
808- bbox : Tuple [float , float , float , float ],
816+ self ,
817+ bbox : Tuple [float , float , float , float ],
809818 pixel_size : float
810819 ) -> str :
811- """Calculate image size based on bbox and pixel size."""
820+ """
821+ Calculate image size based on bbox and pixel size.
822+
823+ If the requested dimensions exceed service limits, the image is
824+ clamped and a warning is emitted so users know the effective
825+ resolution is coarser than the requested pixel size.
826+ """
812827 width = int ((bbox [2 ] - bbox [0 ]) / pixel_size )
813828 height = int ((bbox [3 ] - bbox [1 ]) / pixel_size )
814-
829+
815830 # Limit to service maximums
816831 max_width = 15000
817832 max_height = 4100
818-
833+
834+ clamped = False
819835 if width > max_width :
836+ clamped = True
837+ effective_x_res = (bbox [2 ] - bbox [0 ]) / max_width
820838 width = max_width
821839 if height > max_height :
840+ clamped = True
841+ effective_y_res = (bbox [3 ] - bbox [1 ]) / max_height
822842 height = max_height
823-
843+
844+ if clamped :
845+ effective_res = max (
846+ (bbox [2 ] - bbox [0 ]) / width ,
847+ (bbox [3 ] - bbox [1 ]) / height ,
848+ )
849+ print_warning (
850+ f"Requested area exceeds service limits. "
851+ f"Image clamped to { width } x{ height } pixels "
852+ f"(effective resolution ~{ effective_res :.1f} m instead of { pixel_size } m). "
853+ f"Consider tiling the request for full resolution."
854+ )
855+
824856 return f"{ width } ,{ height } "
0 commit comments