pycontrails 0.54.0__cp313-cp313-macosx_10_13_x86_64.whl → 0.54.2__cp313-cp313-macosx_10_13_x86_64.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.

Potentially problematic release.


This version of pycontrails might be problematic. Click here for more details.

pycontrails/_version.py CHANGED
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '0.54.0'
16
- __version_tuple__ = version_tuple = (0, 54, 0)
15
+ __version__ = version = '0.54.2'
16
+ __version_tuple__ = version_tuple = (0, 54, 2)
@@ -4,14 +4,20 @@ from __future__ import annotations
4
4
 
5
5
  import abc
6
6
  import dataclasses
7
+ import sys
7
8
  import warnings
8
9
  from typing import Any, Generic, NoReturn, overload
9
10
 
11
+ if sys.version_info >= (3, 12):
12
+ from typing import override
13
+ else:
14
+ from typing_extensions import override
15
+
10
16
  import numpy as np
11
17
  import numpy.typing as npt
12
- from overrides import overrides
13
18
 
14
19
  from pycontrails.core import flight, fuel
20
+ from pycontrails.core.fleet import Fleet
15
21
  from pycontrails.core.flight import Flight
16
22
  from pycontrails.core.met import MetDataset
17
23
  from pycontrails.core.models import Model, ModelParams, interpolate_met
@@ -76,6 +82,10 @@ class AircraftPerformance(Model):
76
82
 
77
83
  source: Flight
78
84
 
85
+ @abc.abstractmethod
86
+ @overload
87
+ def eval(self, source: Fleet, **params: Any) -> Fleet: ...
88
+
79
89
  @abc.abstractmethod
80
90
  @overload
81
91
  def eval(self, source: Flight, **params: Any) -> Flight: ...
@@ -118,7 +128,7 @@ class AircraftPerformance(Model):
118
128
  Flight trajectory with aircraft performance data.
119
129
  """
120
130
 
121
- @overrides
131
+ @override
122
132
  def set_source_met(self, *args: Any, **kwargs: Any) -> None:
123
133
  fill_with_isa = self.params["fill_low_altitude_with_isa_temperature"]
124
134
  if fill_with_isa and (self.met is None or "air_temperature" not in self.met):
@@ -467,10 +477,11 @@ class AircraftPerformance(Model):
467
477
  tas[cond] = self.source.segment_groundspeed()[cond]
468
478
  return tas
469
479
 
470
- met_incomplete = (
471
- self.met is None or "eastward_wind" not in self.met or "northward_wind" not in self.met
480
+ wind_available = ("eastward_wind" in self.source and "northward_wind" in self.source) or (
481
+ self.met is not None and "eastward_wind" in self.met and "northward_wind" in self.met
472
482
  )
473
- if met_incomplete:
483
+
484
+ if not wind_available:
474
485
  if fill_with_groundspeed:
475
486
  tas = self.source.segment_groundspeed()
476
487
  self.source["true_airspeed"] = tas
pycontrails/core/cache.py CHANGED
@@ -7,14 +7,18 @@ import logging
7
7
  import os
8
8
  import pathlib
9
9
  import shutil
10
+ import sys
10
11
  import warnings
11
12
  from abc import ABC, abstractmethod
12
13
  from collections.abc import Sequence
13
14
  from typing import TYPE_CHECKING, Any
14
15
 
15
- logger = logging.getLogger(__name__)
16
+ if sys.version_info >= (3, 12):
17
+ from typing import override
18
+ else:
19
+ from typing_extensions import override
16
20
 
17
- from overrides import overrides
21
+ logger = logging.getLogger(__name__)
18
22
 
19
23
  from pycontrails.utils import dependencies
20
24
 
@@ -203,20 +207,20 @@ class DiskCacheStore(CacheStore):
203
207
  return f"DiskCacheStore: {self.cache_dir}"
204
208
 
205
209
  @property
206
- @overrides
210
+ @override
207
211
  def size(self) -> float:
208
212
  disk_path = pathlib.Path(self.cache_dir)
209
213
  size = sum(f.stat().st_size for f in disk_path.rglob("*") if f.is_file())
210
214
  logger.debug("Disk cache size %s bytes", size)
211
215
  return size / 1e6
212
216
 
213
- @overrides
217
+ @override
214
218
  def listdir(self, path: str = "") -> list[str]:
215
219
  path = self.path(path)
216
220
  iter_ = pathlib.Path(path).iterdir()
217
221
  return sorted(str(f.relative_to(path)) for f in iter_)
218
222
 
219
- @overrides
223
+ @override
220
224
  def path(self, cache_path: str) -> str:
221
225
  if cache_path.startswith(self.cache_dir):
222
226
  disk_path = pathlib.Path(cache_path)
@@ -228,7 +232,7 @@ class DiskCacheStore(CacheStore):
228
232
 
229
233
  return str(disk_path)
230
234
 
231
- @overrides
235
+ @override
232
236
  def exists(self, cache_path: str) -> bool:
233
237
  disk_path = pathlib.Path(self.path(cache_path))
234
238
  return disk_path.exists()
@@ -551,7 +555,7 @@ class GCPCacheStore(CacheStore):
551
555
  return self._client
552
556
 
553
557
  @property
554
- @overrides
558
+ @override
555
559
  def size(self) -> float:
556
560
  # get list of blobs below this path
557
561
  blobs = self._bucket.list_blobs(prefix=self.cache_dir)
@@ -559,7 +563,7 @@ class GCPCacheStore(CacheStore):
559
563
  logger.debug("GCP cache size %s bytes", size)
560
564
  return size / 1e6
561
565
 
562
- @overrides
566
+ @override
563
567
  def listdir(self, path: str = "") -> list[str]:
564
568
  # I don't necessarily think we want to implement this .... it might be
565
569
  # very slow if the bucket is large. BUT, it won't be slower than the size
@@ -572,7 +576,7 @@ class GCPCacheStore(CacheStore):
572
576
  "list files in the local disk cache."
573
577
  )
574
578
 
575
- @overrides
579
+ @override
576
580
  def path(self, cache_path: str) -> str:
577
581
  if cache_path.startswith(self.cache_dir):
578
582
  return cache_path
@@ -604,7 +608,7 @@ class GCPCacheStore(CacheStore):
604
608
  bucket_path = self.path(cache_path)
605
609
  return f"gs://{self.bucket}/{bucket_path}"
606
610
 
607
- @overrides
611
+ @override
608
612
  def exists(self, cache_path: str) -> bool:
609
613
  # see if file is in the mirror disk cache
610
614
  if self._disk_cache.exists(cache_path):
pycontrails/core/fleet.py CHANGED
@@ -2,14 +2,24 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import sys
5
6
  import warnings
6
7
  from collections.abc import Iterable
7
8
  from typing import Any, NoReturn
8
9
 
10
+ if sys.version_info >= (3, 11):
11
+ from typing import Self
12
+ else:
13
+ from typing_extensions import Self
14
+
15
+ if sys.version_info >= (3, 12):
16
+ from typing import override
17
+ else:
18
+ from typing_extensions import override
19
+
9
20
  import numpy as np
10
21
  import numpy.typing as npt
11
22
  import pandas as pd
12
- from overrides import overrides
13
23
 
14
24
  from pycontrails.core.flight import Flight
15
25
  from pycontrails.core.fuel import Fuel, JetA
@@ -121,13 +131,13 @@ class Fleet(Flight):
121
131
 
122
132
  return final_waypoints, fl_attrs
123
133
 
124
- @overrides
134
+ @override
125
135
  def copy(self, **kwargs: Any) -> Fleet:
126
136
  kwargs.setdefault("fuel", self.fuel)
127
137
  kwargs.setdefault("fl_attrs", self.fl_attrs)
128
138
  return super().copy(**kwargs)
129
139
 
130
- @overrides
140
+ @override
131
141
  def filter(self, mask: npt.NDArray[np.bool_], copy: bool = True, **kwargs: Any) -> Fleet:
132
142
  kwargs.setdefault("fuel", self.fuel)
133
143
 
@@ -137,7 +147,7 @@ class Fleet(Flight):
137
147
 
138
148
  return super().filter(mask, copy=copy, **kwargs)
139
149
 
140
- @overrides
150
+ @override
141
151
  def sort(self, by: str | list[str]) -> NoReturn:
142
152
  msg = (
143
153
  "Fleet.sort is not implemented. A Fleet instance must be sorted "
@@ -154,7 +164,7 @@ class Fleet(Flight):
154
164
  broadcast_numeric: bool = True,
155
165
  copy: bool = True,
156
166
  attrs: dict[str, Any] | None = None,
157
- ) -> Fleet:
167
+ ) -> Self:
158
168
  """Instantiate a :class:`Fleet` instance from an iterable of :class:`Flight`.
159
169
 
160
170
  .. versionchanged:: 0.49.3
@@ -196,17 +206,15 @@ class Fleet(Flight):
196
206
 
197
207
  fl_attrs: dict[str, Any] = {}
198
208
 
199
- # Pluck from the first flight to get fuel, data_keys, and crs
209
+ # Pluck from the first flight to get fuel and data_keys
200
210
  fuel = seq[0].fuel
201
211
  data_keys = set(seq[0]) # convert to a new instance to because we mutate seq[0]
202
- crs = seq[0].attrs["crs"]
203
212
 
204
213
  for fl in seq:
205
214
  _validate_fl(
206
215
  fl,
207
216
  fl_attrs=fl_attrs,
208
217
  data_keys=data_keys,
209
- crs=crs,
210
218
  fuel=fuel,
211
219
  broadcast_numeric=broadcast_numeric,
212
220
  )
@@ -316,44 +324,39 @@ class Fleet(Flight):
316
324
  # use case isn't seen.
317
325
  return np.concatenate(tas)
318
326
 
319
- @overrides
327
+ @override
320
328
  def segment_groundspeed(self, *args: Any, **kwargs: Any) -> npt.NDArray[np.float64]:
321
- # Implement if we have a usecase for this.
322
- # Because the super() method uses a smoothing pattern, it will not reliably
323
- # work on Fleet.
324
- raise NotImplementedError
329
+ fls = self.to_flight_list(copy=False)
330
+ gs = [fl.segment_groundspeed(*args, **kwargs) for fl in fls]
331
+ return np.concatenate(gs)
325
332
 
326
- @overrides
333
+ @override
327
334
  def resample_and_fill(self, *args: Any, **kwargs: Any) -> Fleet:
328
335
  flights = self.to_flight_list(copy=False)
329
336
  flights = [fl.resample_and_fill(*args, **kwargs) for fl in flights]
330
337
  return type(self).from_seq(flights, copy=False, broadcast_numeric=False, attrs=self.attrs)
331
338
 
332
- @overrides
339
+ @override
333
340
  def segment_length(self) -> npt.NDArray[np.float64]:
334
341
  return np.where(self.final_waypoints, np.nan, super().segment_length())
335
342
 
336
343
  @property
337
- @overrides
344
+ @override
338
345
  def max_distance_gap(self) -> float:
339
- if self.attrs["crs"] != "EPSG:4326":
340
- msg = "Only implemented for EPSG:4326 CRS."
341
- raise NotImplementedError(msg)
342
-
343
346
  return np.nanmax(self.segment_length()).item()
344
347
 
345
- @overrides
348
+ @override
346
349
  def segment_azimuth(self) -> npt.NDArray[np.float64]:
347
350
  return np.where(self.final_waypoints, np.nan, super().segment_azimuth())
348
351
 
349
- @overrides
352
+ @override
350
353
  def segment_angle(self) -> tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]:
351
354
  sin_a, cos_a = super().segment_angle()
352
355
  sin_a[self.final_waypoints] = np.nan
353
356
  cos_a[self.final_waypoints] = np.nan
354
357
  return sin_a, cos_a
355
358
 
356
- @overrides
359
+ @override
357
360
  def clean_and_resample(
358
361
  self,
359
362
  freq: str = "1min",
@@ -400,7 +403,6 @@ def _validate_fl(
400
403
  *,
401
404
  fl_attrs: dict[str, Any],
402
405
  data_keys: set[str],
403
- crs: str,
404
406
  fuel: Fuel,
405
407
  broadcast_numeric: bool,
406
408
  ) -> None:
@@ -419,8 +421,6 @@ def _validate_fl(
419
421
  Set of data keys expected in each flight.
420
422
  fuel : Fuel
421
423
  Fuel used all flights
422
- crs : str
423
- CRS to use all flights
424
424
  broadcast_numeric : bool
425
425
  If True, broadcast numeric attributes to data variables.
426
426
 
@@ -429,7 +429,7 @@ def _validate_fl(
429
429
  KeyError
430
430
  ``fl`` does not have a ``flight_id`` key in :attr:`attrs`.
431
431
  ValueError
432
- If ``flight_id`` is duplicated or incompatible CRS found.
432
+ If ``flight_id`` is duplicated or if ``fuel`` or ``data_keys`` are inconsistent.
433
433
  """
434
434
  flight_id = _extract_flight_id(fl)
435
435
 
@@ -446,13 +446,6 @@ def _validate_fl(
446
446
  "The 'fuel' attributes must be consistent between flights in a Fleet."
447
447
  )
448
448
  raise ValueError(msg)
449
- if fl.attrs["crs"] != crs:
450
- msg = (
451
- f"CRS on Flight {flight_id} ({fl.attrs['crs']}) "
452
- f"is not inconsistent with previous flights ({crs}). "
453
- "The 'crs' attributes must be consistent between flights in a Fleet."
454
- )
455
- raise ValueError(msg)
456
449
  if fl.data.keys() != data_keys:
457
450
  msg = (
458
451
  f"Data keys on Flight {flight_id} ({fl.data.keys()}) "
@@ -4,14 +4,20 @@ from __future__ import annotations
4
4
 
5
5
  import enum
6
6
  import logging
7
+ import sys
7
8
  import warnings
8
9
  from typing import TYPE_CHECKING, Any, NoReturn, TypeVar
9
10
 
11
+ if sys.version_info >= (3, 12):
12
+ from typing import override
13
+ else:
14
+ from typing_extensions import override
15
+
16
+
10
17
  import numpy as np
11
18
  import numpy.typing as npt
12
19
  import pandas as pd
13
20
  import scipy.signal
14
- from overrides import overrides
15
21
 
16
22
  from pycontrails.core.fuel import Fuel, JetA
17
23
  from pycontrails.core.vector import AttrDict, GeoVectorDataset, VectorDataDict, VectorDataset
@@ -75,9 +81,6 @@ class Flight(GeoVectorDataset):
75
81
  Expect altitude in [:math:`m`].
76
82
  Expect pressure level (`level`) in [:math:`hPa`].
77
83
 
78
- Use the attribute :attr:`attrs["crs"]` to specify coordinate reference system
79
- using `PROJ <https://proj.org/>`_ or `EPSG <https://epsg.org/home.html>`_ syntax.
80
-
81
84
  Parameters
82
85
  ----------
83
86
  data : dict[str, np.ndarray] | pd.DataFrame | VectorDataDict | VectorDataset | None
@@ -159,7 +162,7 @@ class Flight(GeoVectorDataset):
159
162
  ... })
160
163
  >>> fl = Flight(data=df, flight_id=123) # specify a flight_id by keyword
161
164
  >>> fl
162
- Flight [4 keys x 500 length, 2 attributes]
165
+ Flight [4 keys x 500 length, 1 attributes]
163
166
  Keys: longitude, latitude, altitude, time
164
167
  Attributes:
165
168
  time [2021-01-01 10:00:00, 2021-01-01 15:00:00]
@@ -167,7 +170,6 @@ class Flight(GeoVectorDataset):
167
170
  latitude [10.0, 40.0]
168
171
  altitude [10500.0, 10500.0]
169
172
  flight_id 123
170
- crs EPSG:4326
171
173
 
172
174
  >>> # Create `Flight` from keywords
173
175
  >>> fl = Flight(
@@ -177,14 +179,13 @@ class Flight(GeoVectorDataset):
177
179
  ... time=pd.date_range('2021-01-01T12', '2021-01-01T14', periods=200),
178
180
  ... )
179
181
  >>> fl
180
- Flight [4 keys x 200 length, 1 attributes]
182
+ Flight [4 keys x 200 length, 0 attributes]
181
183
  Keys: longitude, latitude, time, altitude
182
184
  Attributes:
183
185
  time [2021-01-01 12:00:00, 2021-01-01 14:00:00]
184
186
  longitude [20.0, 30.0]
185
187
  latitude [30.0, 40.0]
186
188
  altitude [11000.0, 11000.0]
187
- crs EPSG:4326
188
189
 
189
190
  >>> # Access the underlying data as DataFrame
190
191
  >>> fl.dataframe.head()
@@ -281,19 +282,19 @@ class Flight(GeoVectorDataset):
281
282
  "'drop_duplicated_times=True' or call the 'resample_and_fill' method."
282
283
  )
283
284
 
284
- @overrides
285
+ @override
285
286
  def copy(self: FlightType, **kwargs: Any) -> FlightType:
286
287
  kwargs.setdefault("fuel", self.fuel)
287
288
  return super().copy(**kwargs)
288
289
 
289
- @overrides
290
+ @override
290
291
  def filter(
291
292
  self: FlightType, mask: npt.NDArray[np.bool_], copy: bool = True, **kwargs: Any
292
293
  ) -> FlightType:
293
294
  kwargs.setdefault("fuel", self.fuel)
294
295
  return super().filter(mask, copy=copy, **kwargs)
295
296
 
296
- @overrides
297
+ @override
297
298
  def sort(self, by: str | list[str]) -> NoReturn:
298
299
  msg = (
299
300
  "Flight.sort is not implemented. A Flight instance is automatically sorted "
@@ -369,11 +370,6 @@ class Flight(GeoVectorDataset):
369
370
  float
370
371
  Maximum distance between waypoints, [:math:`m`]
371
372
 
372
- Raises
373
- ------
374
- NotImplementedError
375
- Raises when attr:`attrs["crs"]` is not EPSG:4326
376
-
377
373
  Examples
378
374
  --------
379
375
  >>> import numpy as np
@@ -386,9 +382,6 @@ class Flight(GeoVectorDataset):
386
382
  >>> fl.max_distance_gap
387
383
  np.float64(7391.27...)
388
384
  """
389
- if self.attrs["crs"] != "EPSG:4326":
390
- raise NotImplementedError("Only implemented for EPSG:4326 CRS.")
391
-
392
385
  return self.segment_length()[:-1].max()
393
386
 
394
387
  @property
@@ -400,11 +393,6 @@ class Flight(GeoVectorDataset):
400
393
  float
401
394
  Total flight length, [:math:`m`]
402
395
 
403
- Raises
404
- ------
405
- NotImplementedError
406
- Raises when attr:`attrs["crs"]` is not EPSG:4326
407
-
408
396
  Examples
409
397
  --------
410
398
  >>> import numpy as np
@@ -417,9 +405,6 @@ class Flight(GeoVectorDataset):
417
405
  >>> fl.length
418
406
  np.float64(1436924.67...)
419
407
  """
420
- if self.attrs["crs"] != "EPSG:4326":
421
- raise NotImplementedError("Only implemented for EPSG:4326 CRS.")
422
-
423
408
  # drop off the nan
424
409
  return np.nansum(self.segment_length()[:-1])
425
410
 
@@ -461,11 +446,6 @@ class Flight(GeoVectorDataset):
461
446
  npt.NDArray[np.float64]
462
447
  Array of great circle distances in [:math:`m`] between waypoints
463
448
 
464
- Raises
465
- ------
466
- NotImplementedError
467
- Raises when attr:`attrs["crs"]` is not EPSG:4326
468
-
469
449
  Examples
470
450
  --------
471
451
  >>> from pycontrails import Flight
@@ -484,9 +464,6 @@ class Flight(GeoVectorDataset):
484
464
  :func:`segment_haversine`
485
465
  :meth:`segment_length`
486
466
  """
487
- if self.attrs["crs"] != "EPSG:4326":
488
- raise NotImplementedError("Only implemented for EPSG:4326 CRS.")
489
-
490
467
  return geo.segment_haversine(self["longitude"], self["latitude"])
491
468
 
492
469
  def segment_length(self) -> npt.NDArray[np.float64]:
@@ -500,11 +477,6 @@ class Flight(GeoVectorDataset):
500
477
  npt.NDArray[np.float64]
501
478
  Array of distances in [:math:`m`] between waypoints
502
479
 
503
- Raises
504
- ------
505
- NotImplementedError
506
- Raises when attr:`attrs["crs"]` is not EPSG:4326
507
-
508
480
  Examples
509
481
  --------
510
482
  >>> from pycontrails import Flight
@@ -522,9 +494,6 @@ class Flight(GeoVectorDataset):
522
494
  --------
523
495
  :func:`segment_length`
524
496
  """
525
- if self.attrs["crs"] != "EPSG:4326":
526
- raise NotImplementedError("Only implemented for EPSG:4326 CRS.")
527
-
528
497
  return geo.segment_length(self["longitude"], self["latitude"], self.altitude)
529
498
 
530
499
  def segment_angle(self) -> tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]:
@@ -1128,7 +1097,7 @@ class Flight(GeoVectorDataset):
1128
1097
 
1129
1098
  Notes
1130
1099
  -----
1131
- Algorithm is derived from :meth:`traffic.core.flight.Flight.filter`.
1100
+ Algorithm is derived from :meth:`traffic.core.Flight.filter`.
1132
1101
 
1133
1102
  The `traffic
1134
1103
  <https://traffic-viz.github.io/api_reference/traffic.core.flight.html#traffic.core.Flight.filter>`_
@@ -1304,15 +1273,7 @@ class Flight(GeoVectorDataset):
1304
1273
  pd.DataFrame | None
1305
1274
  Generated waypoints to be merged into underlying :attr:`data`.
1306
1275
  Return `None` if no new waypoints are created.
1307
-
1308
- Raises
1309
- ------
1310
- NotImplementedError
1311
- Raises when attr:`attrs["crs"]` is not EPSG:4326
1312
1276
  """
1313
- if self.attrs["crs"] != "EPSG:4326":
1314
- raise NotImplementedError("Only implemented for EPSG:4326 CRS.")
1315
-
1316
1277
  # Omit the final nan and ensure index + 1 (below) is well defined
1317
1278
  segs = self.segment_haversine()[:-1]
1318
1279
 
@@ -1431,7 +1392,7 @@ class Flight(GeoVectorDataset):
1431
1392
  if key is not None and key not in self.dataframe.columns:
1432
1393
  raise KeyError(f"Column {key} does not exist in data.")
1433
1394
 
1434
- jump_indices = _antimeridian_index(pd.Series(self["longitude"]), self.attrs["crs"])
1395
+ jump_indices = _antimeridian_index(pd.Series(self["longitude"]))
1435
1396
 
1436
1397
  def _group_to_feature(group: pd.DataFrame) -> dict[str, str | dict[str, Any]]:
1437
1398
  # assigns a different value to each group of consecutive indices
@@ -1464,19 +1425,21 @@ class Flight(GeoVectorDataset):
1464
1425
  return {"type": "FeatureCollection", "features": features}
1465
1426
 
1466
1427
  def to_traffic(self) -> traffic.core.Flight:
1467
- """Convert Flight instance to :class:`traffic.core.Flight` instance.
1468
-
1469
- See https://traffic-viz.github.io/traffic.core.flight.html#traffic.core.Flight
1428
+ """Convert to :class:`traffic.core.Flight`instance.
1470
1429
 
1471
1430
  Returns
1472
1431
  -------
1473
- :class:`traffic.core.Flight`
1474
- `traffic.core.Flight` instance
1432
+ traffic.core.Flight
1433
+ traffic flight instance
1475
1434
 
1476
1435
  Raises
1477
1436
  ------
1478
1437
  ModuleNotFoundError
1479
1438
  `traffic` package not installed
1439
+
1440
+ See Also
1441
+ --------
1442
+ :class:`traffic.core.Flight`
1480
1443
  """
1481
1444
  try:
1482
1445
  import traffic.core
@@ -1515,8 +1478,6 @@ class Flight(GeoVectorDataset):
1515
1478
  ------
1516
1479
  KeyError
1517
1480
  :attr:`data` does not contain column ``key``
1518
- NotImplementedError
1519
- Raised when ``attrs["crs"]`` is not EPSG:4326
1520
1481
 
1521
1482
  Examples
1522
1483
  --------
@@ -1557,8 +1518,6 @@ class Flight(GeoVectorDataset):
1557
1518
  """
1558
1519
  if key not in self.data:
1559
1520
  raise KeyError(f"Column {key} does not exist in data.")
1560
- if self.attrs["crs"] != "EPSG:4326":
1561
- raise NotImplementedError("Only implemented for EPSG:4326 CRS.")
1562
1521
 
1563
1522
  # The column of interest may contain floating point values less than 1.
1564
1523
  # In this case, if the default threshold is not changed, warn the user that the behavior
@@ -1668,40 +1627,23 @@ def _return_linestring(data: dict[str, npt.NDArray[np.float64]]) -> list[list[fl
1668
1627
  return [list(p) for p in points]
1669
1628
 
1670
1629
 
1671
- def _antimeridian_index(longitude: pd.Series, crs: str = "EPSG:4326") -> list[int]:
1630
+ def _antimeridian_index(longitude: pd.Series) -> list[int]:
1672
1631
  """Return indices after flight crosses antimeridian, or an empty list if flight does not cross.
1673
1632
 
1633
+ This function assumes EPSG:4326 coordinates.
1634
+
1674
1635
  Parameters
1675
1636
  ----------
1676
1637
  longitude : pd.Series
1677
1638
  longitude values with an integer index
1678
- crs : str, optional
1679
- Coordinate Reference system for longitude specified in EPSG format.
1680
- Currently only supports "EPSG:4326" and "EPSG:3857".
1681
1639
 
1682
1640
  Returns
1683
1641
  -------
1684
1642
  list[int]
1685
1643
  Indices after jump, or empty list of flight does not cross antimeridian.
1686
-
1687
- Raises
1688
- ------
1689
- ValueError
1690
- CRS is not supported.
1691
1644
  """
1692
- # WGS84
1693
- if crs in ["EPSG:4326"]:
1694
- l1 = (-180.0, -90.0)
1695
- l2 = (90.0, 180.0)
1696
-
1697
- # pseudo mercator
1698
- elif crs in ["EPSG:3857"]:
1699
- # values calculated through pyproj.Transformer
1700
- l1 = (-20037508.342789244, -10018754.171394622)
1701
- l2 = (10018754.171394622, 20037508.342789244)
1702
-
1703
- else:
1704
- raise ValueError("CRS must be one of EPSG:4326 or EPSG:3857")
1645
+ l1 = (-180.0, -90.0)
1646
+ l2 = (90.0, 180.0)
1705
1647
 
1706
1648
  # TODO: When nans exist, this method *may* not find the meridian
1707
1649
  if np.any(np.isnan(longitude)):
@@ -1711,9 +1653,7 @@ def _antimeridian_index(longitude: pd.Series, crs: str = "EPSG:4326") -> list[in
1711
1653
  s2 = longitude.between(*l2)
1712
1654
  jump12 = longitude[s1 & s2.shift()]
1713
1655
  jump21 = longitude[s1.shift() & s2]
1714
- jump_index = pd.concat([jump12, jump21]).index.to_list()
1715
-
1716
- return jump_index
1656
+ return pd.concat([jump12, jump21]).index.to_list()
1717
1657
 
1718
1658
 
1719
1659
  def _sg_filter(
@@ -2045,18 +1985,18 @@ def filter_altitude(
2045
1985
 
2046
1986
  Notes
2047
1987
  -----
2048
- Algorithm is derived from :meth:`traffic.core.flight.Flight.filter`.
1988
+ Algorithm is derived from :meth:`traffic.core.Flight.filter`.
2049
1989
 
2050
- The `traffic
1990
+ The `traffic filter algorithm
2051
1991
  <https://traffic-viz.github.io/api_reference/traffic.core.flight.html#traffic.core.Flight.filter>`_
2052
- algorithm also computes thresholds on sliding windows
1992
+ also computes thresholds on sliding windows
2053
1993
  and replaces unacceptable values with NaNs.
2054
1994
 
2055
1995
  Errors may raised if the ``kernel_size`` is too large.
2056
1996
 
2057
1997
  See Also
2058
1998
  --------
2059
- :meth:`traffic.core.flight.Flight.filter`
1999
+ :meth:`traffic.core.Flight.filter`
2060
2000
  :func:`scipy.signal.medfilt`
2061
2001
  """
2062
2002
  if not len(altitude_ft):