@@ -455,36 +455,36 @@ def __repr__(self) -> str:
455
455
456
456
@staticmethod
457
457
def _repr_value (value : object ) -> str :
458
- """Creates a representation string for different types of data.
459
-
460
- :param value: data in any type
461
- :return: representation string
462
- """
458
+ """Creates a representation string for different types of data."""
463
459
if isinstance (value , np .ndarray ):
464
460
return f"{ EOPatch ._repr_value_class (value )} (shape={ value .shape } , dtype={ value .dtype } )"
465
461
466
462
if isinstance (value , gpd .GeoDataFrame ):
467
463
crs = CRS (value .crs ).ogc_string () if value .crs else value .crs
468
464
return f"{ EOPatch ._repr_value_class (value )} (columns={ list (value )} , length={ len (value )} , crs={ crs } )"
469
465
466
+ repr_str = str (value )
467
+ if len (repr_str ) <= MAX_DATA_REPR_LEN :
468
+ return repr_str
469
+
470
470
if isinstance (value , (list , tuple , dict )) and value :
471
- repr_str = str (value )
472
- if len (repr_str ) <= MAX_DATA_REPR_LEN :
473
- return repr_str
471
+ lb , rb = ("[" , "]" ) if isinstance (value , list ) else ("(" , ")" ) if isinstance (value , tuple ) else ("{" , "}" )
474
472
475
- l_bracket , r_bracket = ("[" , "]" ) if isinstance (value , list ) else ("(" , ")" )
476
- if isinstance (value , (list , tuple )) and len (value ) > 2 :
477
- repr_str = f"{ l_bracket } { value [0 ]!r} , ..., { value [- 1 ]!r} { r_bracket } "
473
+ if isinstance (value , dict ): # generate representation of first element or (key, value) pair
474
+ some_key = next (iter (value ))
475
+ repr_of_el = f"{ EOPatch ._repr_value (some_key )} : { EOPatch ._repr_value (value [some_key ])} "
476
+ else :
477
+ repr_of_el = EOPatch ._repr_value (value [0 ])
478
478
479
- if len (repr_str ) > MAX_DATA_REPR_LEN and isinstance ( value , ( list , tuple )) and len ( value ) > 1 :
480
- repr_str = f"{ l_bracket } { value [ 0 ]!r } , ... { r_bracket } "
479
+ many_elements_visual = ", ..." if len (value ) > 1 else "" # add ellipsis if there are multiple elements
480
+ repr_str = f"{ lb } { repr_of_el } { many_elements_visual } { rb } "
481
481
482
482
if len (repr_str ) > MAX_DATA_REPR_LEN :
483
483
repr_str = str (type (value ))
484
484
485
- return f"{ repr_str } , length={ len (value )} "
485
+ return f"{ repr_str } < length={ len (value )} > "
486
486
487
- return repr ( value )
487
+ return str ( type ( value ) )
488
488
489
489
@staticmethod
490
490
def _repr_value_class (value : object ) -> str :
@@ -726,6 +726,7 @@ def merge(
726
726
self , * eopatches , features = features , time_dependent_op = time_dependent_op , timeless_op = timeless_op
727
727
)
728
728
729
+ @deprecated_function (EODeprecationWarning , "Please use the method `temporal_subset` instead." )
729
730
def consolidate_timestamps (self , timestamps : list [dt .datetime ]) -> set [dt .datetime ]:
730
731
"""Removes all frames from the EOPatch with a date not found in the provided timestamps list.
731
732
@@ -750,6 +751,45 @@ def consolidate_timestamps(self, timestamps: list[dt.datetime]) -> set[dt.dateti
750
751
751
752
return remove_from_patch
752
753
754
+ def temporal_subset (
755
+ self , timestamps : Iterable [dt .datetime ] | Iterable [int ] | Callable [[list [dt .datetime ]], Iterable [bool ]]
756
+ ) -> EOPatch :
757
+ """Returns an EOPatch that only contains data for the temporal subset corresponding to `timestamps`.
758
+
759
+ For array-based data appropriate temporal slices are extracted. For vector data a filtration is performed.
760
+
761
+ :param timestamps: Parameter that defines the temporal subset. Can be a collection of timestamps, a
762
+ collection of timestamp indices. It is possible to also provide a callable that maps a list of timestamps
763
+ to a sequence of booleans, which determine if a given timestamp is included in the subset or not.
764
+ """
765
+ timestamp_indices = self ._parse_temporal_subset_input (timestamps )
766
+ new_timestamps = [ts for i , ts in enumerate (self .get_timestamps ()) if i in timestamp_indices ]
767
+ new_patch = EOPatch (bbox = self .bbox , timestamps = new_timestamps )
768
+
769
+ for ftype , fname in self .get_features ():
770
+ if ftype .is_timeless () or ftype .is_meta ():
771
+ new_patch [ftype , fname ] = self [ftype , fname ]
772
+ elif ftype .is_vector ():
773
+ gdf : gpd .GeoDataFrame = self [ftype , fname ]
774
+ new_patch [ftype , fname ] = gdf [gdf [TIMESTAMP_COLUMN ].isin (new_timestamps )]
775
+ else :
776
+ new_patch [ftype , fname ] = self [ftype , fname ][timestamp_indices ]
777
+
778
+ return new_patch
779
+
780
+ def _parse_temporal_subset_input (
781
+ self , timestamps : Iterable [dt .datetime ] | Iterable [int ] | Callable [[list [dt .datetime ]], Iterable [bool ]]
782
+ ) -> list [int ]:
783
+ """Parses input into a list of timestamp indices. Also adds implicit support for strings via `parse_time`."""
784
+ if callable (timestamps ):
785
+ accepted_timestamps = timestamps (self .get_timestamps ())
786
+ return [i for i , accepted in enumerate (accepted_timestamps ) if accepted ]
787
+ ts_or_idx = list (timestamps )
788
+ if all (isinstance (ts , int ) for ts in ts_or_idx ):
789
+ return ts_or_idx # type: ignore[return-value]
790
+ parsed_timestamps = {parse_time (ts , force_datetime = True ) for ts in ts_or_idx } # type: ignore[call-overload]
791
+ return [i for i , ts in enumerate (self .get_timestamps ()) if ts in parsed_timestamps ]
792
+
753
793
def plot (
754
794
self ,
755
795
feature : Feature ,
0 commit comments