FIRMS — API reference#
NASA FIRMS active-fire data source subpackage — earthlens.firms.
Background, usage, and credentials are covered under the other pages in
this section; this page is the rendered API.
earthlens.firms
#
NASA FIRMS active-fire backend.
Thin wrapper over the NASA FIRMS (Fire Information for Resource
Management System) area CSV API that returns near-real-time and archival
active-fire detections from MODIS (C6.1) and VIIRS (S-NPP / NOAA-20 /
NOAA-21) as a pyramids
:class:~pyramids.feature.collection.FeatureCollection of fire-pixel
points (CRS EPSG:4326).
This is a vector backend: the result is a table of geolocated fire
detections, not a gridded array, so :data:FIRMS.OUTPUT_KIND is
"vector" and the :class:earthlens.earthlens.EarthLens facade rejects
an aggregate= argument for it.
FIRMS needs a free MAP_KEY (no SDK): the only dependencies are
requests + pandas, both core, so there is no [firms] extra to
install — the key lives in :class:FirmsAuth, not a dependency.
Sensor selection: for this backend variables is a list[str] of FIRMS
sensor codes — variables=["VIIRS_SNPP_NRT"],
variables=["MODIS_NRT", "VIIRS_SNPP_NRT"] — not data-variable
names. This is an intentional, documented overload (the facade makes
variables a required argument). The detection filters
(min_confidence=, day_night=) arrive as explicit keyword arguments.
Public surface (re-exported from this package):
- :class:
FIRMS— the backend; instantiate with a date range, a bbox, andvariables=[sensor_code, ...], then call :meth:FIRMS.download. - :class:
Catalog— pydantic-backed loader for the bundledfirms_data_catalog.yamlsensor dispatch table. - :class:
Sensor/ :class:SensorColumn— one sensor's row and one of its CSV columns. - :class:
FirmsAuth/ :class:FirmsCredentials—MAP_KEYresolution. - :class:
AuthenticationError— raised when no usableMAP_KEYresolves. - :func:
csv_to_fc/ :func:empty_fc— the FIRMS CSV → FeatureCollection mapper and its empty-result counterpart. - :data:
CATALOG_PATH— path to the bundled sensor YAML; monkey-patchable in tests.
Examples:
-
List the registered FIRMS sensor codes:
AuthenticationError
#
Bases: AuthenticationError
Raised when no usable FIRMS MAP_KEY can be resolved.
Carries a message that names a fix: pass api_key= to
EarthLens(...).authenticate(), set the FIRMS_MAP_KEY environment
variable, or request a free key at
firms.modaps.eosdis.nasa.gov/api/map_key/. A subclass of the
cross-backend :class:earthlens.base.AuthenticationError so callers
can catch every backend's auth failure with one except clause.
Source code in src/earthlens/firms/auth.py
Catalog
#
Bases: AbstractCatalog
Sensor catalog for the NASA FIRMS backend.
Reads the bundled firms_data_catalog.yaml (shipped as package
data) and exposes its sensors: block as a map of :class:Sensor
rows, keyed by FIRMS source code under the inherited :attr:datasets
field. Instantiate with no arguments (Catalog());
:func:model_post_init loads and validates the YAML in one pass.
Resolve a sensor with :meth:get_sensor (a thin alias over
:meth:~earthlens.base.AbstractCatalog.get_dataset) and a single
column with :meth:get_column.
There is no available_* index — the listed sensors are the whole
FIRMS universe (a deliberate deviation from the ECMWF/GEE catalogs,
shared with GDACS/FDSN).
Attributes:
| Name | Type | Description |
|---|---|---|
datasets |
dict[str, Sensor]
|
Map from the FIRMS source code to its :class: |
Examples:
- List sensor codes and resolve one:
>>> from earthlens.firms import Catalog >>> cat = Catalog() >>> cat.codes() # doctest: +NORMALIZE_WHITESPACE ['GOES_NRT', 'LANDSAT_NRT', 'MODIS_NRT', 'MODIS_SP', 'VIIRS_NOAA20_NRT', 'VIIRS_NOAA20_SP', 'VIIRS_NOAA21_NRT', 'VIIRS_SNPP_NRT', 'VIIRS_SNPP_SP'] >>> cat.get_sensor("MODIS_NRT").family 'MODIS' >>> "MODIS_NRT" in cat True - An unknown code raises with a did-you-mean hint:
Source code in src/earthlens/firms/catalog.py
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 | |
codes()
#
Return the registered FIRMS sensor codes, sorted.
Returns:
| Type | Description |
|---|---|
list[str]
|
list[str]: The sensor codes ( |
get_catalog()
#
Return the sensor map (satisfies the abstract contract).
Returns:
| Type | Description |
|---|---|
dict[str, Sensor]
|
dict[str, Sensor]: Same object as :attr: |
get_column(code, column)
#
Return one column's metadata for a (sensor, column) pair.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
code
|
str
|
A FIRMS source code as it appears in :attr: |
required |
column
|
str
|
A CSV column name declared under that sensor. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
SensorColumn |
SensorColumn
|
The matching column metadata. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
KeyError
|
If |
Examples:
- Read a column's units:
Source code in src/earthlens/firms/catalog.py
get_sensor(code)
#
Return the :class:Sensor for code, with a did-you-mean hint.
Thin alias over
:meth:~earthlens.base.AbstractCatalog.get_dataset.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
code
|
str
|
A FIRMS source code ( |
required |
Returns:
| Name | Type | Description |
|---|---|---|
Sensor |
Sensor
|
The matching sensor row. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Source code in src/earthlens/firms/catalog.py
get_variable(code, column)
#
Leaf accessor for the shared two-arg get_variable contract.
Alias of :meth:get_column so the FIRMS leaf is reachable under
the same get_variable(dataset_key, variable_name) verb the
other two-level catalogs use.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
code
|
str
|
A FIRMS source code. |
required |
column
|
str
|
A CSV column name declared under that sensor. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
SensorColumn |
SensorColumn
|
The matching column metadata. |
Source code in src/earthlens/firms/catalog.py
load(catalog_path=None)
classmethod
#
Read the FIRMS sensor catalog from disk.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
catalog_path
|
Path | None
|
Path to the catalog YAML. Defaults to the
module-level :data: |
None
|
Returns:
| Type | Description |
|---|---|
Catalog
|
A fully-populated :class: |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the file has no |
Source code in src/earthlens/firms/catalog.py
model_post_init(__context)
#
Auto-load the bundled catalog when no sensors were supplied.
Catalog() with no args reads :data:CATALOG_PATH; passing
datasets=... skips the disk read (used in tests).
Raises:
| Type | Description |
|---|---|
ValueError
|
Propagated from :meth: |
Source code in src/earthlens/firms/catalog.py
FIRMS
#
Bases: AbstractDataSource
NASA FIRMS active-fire backend (vector point-feature output).
Wraps the FIRMS area CSV API so a user can pull a space/time/sensor
window of fire detections through the same download() shape every
other earthlens backend uses. Windows longer than the FIRMS 5-day
per-request cap are chunked, and each (sensor, ≤5-day chunk) is
one CSV GET; the rows are mapped to a
:class:~pyramids.feature.collection.FeatureCollection.
FIRMS needs a free MAP_KEY. Supply it to :meth:authenticate as
api_key=, or set the FIRMS_MAP_KEY environment variable and let
authenticate() / download() resolve it. Credentials are not a
constructor argument — the constructor describes only what to fetch.
Attributes:
| Name | Type | Description |
|---|---|---|
OUTPUT_KIND |
OutputKind
|
|
Source code in src/earthlens/firms/backend.py
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 | |
__init__(start, end, variables, lat_lim, lon_lim, temporal_resolution='all', path='', fmt='%Y-%m-%d', min_confidence=None, day_night=None, file_format='gpkg', timeout=60.0)
#
Initialise a FIRMS backend instance.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
start
|
str
|
Inclusive start of the detection window, as a string
parsed with |
required |
end
|
str
|
Inclusive end of the detection window. |
required |
variables
|
list[str]
|
List of FIRMS sensor codes to query
( |
required |
lat_lim
|
list[float]
|
|
required |
lon_lim
|
list[float]
|
|
required |
temporal_resolution
|
str
|
FIRMS chunks by ≤5-day windows
internally, not by a daily/monthly cadence, so this is
the sentinel |
'all'
|
path
|
Path | str
|
Output directory for the written vector file. Created by the parent class if absent. |
''
|
fmt
|
str
|
|
'%Y-%m-%d'
|
min_confidence
|
float | None
|
Optional 0-100 lower bound applied
client-side on the normalised |
None
|
day_night
|
str | None
|
Optional |
None
|
file_format
|
FileFormat
|
Output vector format — |
'gpkg'
|
timeout
|
float
|
Per-request timeout in seconds for each CSV GET. |
60.0
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
TypeError
|
If |
Source code in src/earthlens/firms/backend.py
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 | |
authenticate(api_key=None)
#
Resolve the FIRMS MAP_KEY and arm the backend for download.
The explicit, fail-fast credential step. Pass api_key= to use a
key directly; omit it (or pass None) to read the FIRMS_MAP_KEY
environment variable. Either way the resolved key is held for the
subsequent :meth:download. Calling it again with a different
api_key re-arms with the new key. download() calls this with
no argument on your behalf if you never do, so an explicit call is
only needed to pass a key directly or to validate up front.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
api_key
|
str | None
|
The FIRMS |
None
|
Returns:
| Type | Description |
|---|---|
FIRMS
|
The backend instance, so it chains |
FIRMS
|
|
Raises:
| Type | Description |
|---|---|
AuthenticationError
|
If |
Examples:
- Arm the backend with an explicit key and read it back:
>>> import tempfile >>> from earthlens.firms import FIRMS >>> backend = FIRMS( ... start="2024-08-01", end="2024-08-01", ... variables=["VIIRS_SNPP_NRT"], ... lat_lim=[33.0, 35.0], lon_lim=[-119.0, -117.0], ... path=tempfile.mkdtemp(), ... ) >>> backend.authenticate(api_key="demo-key").client.api_key 'demo-key' - A fresh backend is unauthenticated until the key resolves:
>>> import tempfile >>> from earthlens.firms import FIRMS >>> backend = FIRMS( ... start="2024-08-01", end="2024-08-01", ... variables=["VIIRS_SNPP_NRT"], ... lat_lim=[33.0, 35.0], lon_lim=[-119.0, -117.0], ... path=tempfile.mkdtemp(), ... ) >>> backend.client.is_authenticated() False >>> backend.authenticate(api_key="abc123").client.is_authenticated() True
Source code in src/earthlens/firms/backend.py
download(progress_bar=True, aggregate=None)
#
Query FIRMS and return the matched detections.
Runs the cheap :meth:_search (sensor validation + chunk
planning) then the throttled :meth:_fetch (one CSV GET per
chunk), concatenates the per-chunk collections into one
FeatureCollection, writes it to one vector file under path, and
returns it. An empty result returns — and writes nothing for — a
schema-correct empty FeatureCollection.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
progress_bar
|
bool
|
Show a per-chunk progress bar. Defaults to
|
True
|
aggregate
|
AggregationConfig | None
|
Must be |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
FeatureCollection |
FeatureCollection
|
The matched detections, CRS |
Raises:
| Type | Description |
|---|---|
NotImplementedError
|
If |
Source code in src/earthlens/firms/backend.py
FirmsAuth
#
Bases: AbstractAuth[FirmsCredentials]
Resolve and hold the FIRMS MAP_KEY.
Implements the :class:earthlens.base.AbstractAuth contract for a
single-secret backend. Construction does not touch the environment;
:meth:configure performs the resolution and is idempotent. After a
successful configure(), the key is available via the
:attr:api_key property for the backend to drop into the request
URL.
The class is a context manager (inherited from
:class:AbstractAuth): with FirmsAuth(creds) as auth: ... calls
configure() on enter and the default no-op close() on exit —
there is no per-instance resource to release.
Attributes:
| Name | Type | Description |
|---|---|---|
_creds |
The :class: |
Examples:
- Resolve an explicit key:
Source code in src/earthlens/firms/auth.py
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 | |
api_key
property
#
The resolved MAP_KEY; valid only after :meth:configure.
Returns:
| Name | Type | Description |
|---|---|---|
str |
str
|
The FIRMS |
Raises:
| Type | Description |
|---|---|
AuthenticationError
|
When read before :meth: |
__init__(credentials)
#
Store credentials; does not resolve the key yet.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
credentials
|
FirmsCredentials
|
The :class: |
required |
Source code in src/earthlens/firms/auth.py
configure()
#
Resolve the MAP_KEY so subsequent requests can authenticate.
Idempotent — short-circuits when :meth:is_authenticated
already returns True. On the first call, resolves the key in
this order: the explicit api_key on the credentials, then the
FIRMS_MAP_KEY environment variable.
Raises:
| Type | Description |
|---|---|
AuthenticationError
|
When neither source supplies a key. The
message names the |
Source code in src/earthlens/firms/auth.py
is_authenticated()
#
Return True once :meth:configure has resolved a key.
Cheap predicate — does not call the network. A return of True
means a usable key is held by this instance.
Returns:
| Name | Type | Description |
|---|---|---|
bool |
bool
|
|
Source code in src/earthlens/firms/auth.py
FirmsCredentials
#
Bases: BaseModel
Frozen value object holding the FIRMS MAP_KEY.
The key is optional at construction time: None means "resolve from
the FIRMS_MAP_KEY environment variable at
:meth:FirmsAuth.configure time". The real "is there a usable key?"
gate is :meth:FirmsAuth.configure, not this model.
Attributes:
| Name | Type | Description |
|---|---|---|
api_key |
SecretStr | None
|
The FIRMS |
Examples:
- Build from an explicit key; the secret is hidden in
repr: - The key is optional — rely on the environment instead:
Source code in src/earthlens/firms/auth.py
Sensor
#
Bases: BaseModel
One FIRMS sensor's dispatch row (the "dataset" analog).
The FIRMS source code is the parent key in :attr:Catalog.datasets
and is repeated here as :attr:code so a :class:Sensor carries its
own identity when passed around outside the catalog.
Attributes:
| Name | Type | Description |
|---|---|---|
code |
str
|
FIRMS source code ( |
name |
str
|
Human-readable sensor name used in logs and docs. |
family |
Literal['MODIS', 'VIIRS', 'GOES', 'LANDSAT']
|
|
resolution_m |
int
|
Nominal nadir pixel size in metres (375 for VIIRS, 1000 for MODIS). |
temporal |
Temporal
|
The sensor's coverage window and quality tier. |
columns |
dict[str, SensorColumn]
|
Per-column metadata keyed by CSV column name. |
Examples:
- Inspect a sensor's resolution and a column:
Source code in src/earthlens/firms/catalog.py
SensorColumn
#
Bases: BaseModel
One FIRMS CSV column's metadata (the "variable" analog).
A frozen value object describing a single column a sensor emits in its area-CSV response. Mirrors the ECMWF / GEE per-variable row, but minimal: FIRMS CSV columns carry no request-shaping parameters, only descriptive metadata.
Attributes:
| Name | Type | Description |
|---|---|---|
units |
str
|
Physical unit of the column ( |
long_name |
str
|
Human-readable description used in docs and logs. |
Examples:
- Build a column row directly:
Source code in src/earthlens/firms/catalog.py
csv_to_fc(df, sensor, family, min_confidence=None, day_night=None)
#
Normalise one sensor's FIRMS CSV frame into a FeatureCollection.
One row per detection, columns per :data:ATTRIBUTE_COLUMNS plus a
geometry column of Point(longitude, latitude). The MODIS/VIIRS
confidence and brightness schemas are unified (G4), acq_date +
integer-HHMM acq_time are combined into a tz-aware UTC
acq_datetime, and the optional min_confidence / day_night
filters are applied client-side (FIRMS offers no server-side
equivalent). An empty input frame returns an empty FeatureCollection
with the same columns/dtypes (see :func:empty_fc).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
df
|
DataFrame
|
The decoded FIRMS area CSV for one sensor/chunk. |
required |
sensor
|
str
|
The FIRMS sensor code; recorded in the |
required |
family
|
str
|
|
required |
min_confidence
|
float | None
|
Optional 0-100 lower bound on the normalised
|
None
|
day_night
|
str | None
|
Optional |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
FeatureCollection |
FeatureCollection
|
One feature per surviving detection, CRS
|
Examples:
- Map a one-row VIIRS frame; the
ltoken becomes 25 %:>>> import pandas as pd >>> from earthlens.firms.events import csv_to_fc >>> df = pd.DataFrame( ... { ... "latitude": [34.0], ... "longitude": [-118.0], ... "acq_date": ["2024-08-01"], ... "acq_time": [1325], ... "satellite": ["N"], ... "confidence": ["l"], ... "bright_ti4": [320.0], ... "frp": [12.5], ... "daynight": ["D"], ... } ... ) >>> fc = csv_to_fc(df, "VIIRS_SNPP_NRT", "VIIRS") >>> float(fc["confidence_pct"].iloc[0]) 25.0 >>> fc["acq_datetime"].iloc[0].strftime("%Y-%m-%d %H:%M") '2024-08-01 13:25' >>> fc.crs.to_epsg() 4326
Source code in src/earthlens/firms/events.py
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 | |
empty_fc()
#
Return an empty FeatureCollection with the canonical schema.
Used for an empty CSV, an out-of-coverage window, or a request whose filters dropped every row, so callers always get the same columns and dtypes back regardless of hit count.
Returns:
| Name | Type | Description |
|---|---|---|
FeatureCollection |
FeatureCollection
|
Zero rows, the :data: |
Examples:
- The schema is present even with no rows:
Source code in src/earthlens/firms/events.py
earthlens.firms.backend
#
Backend that queries the NASA FIRMS active-fire CSV API over HTTPS.
FIRMS(AbstractDataSource) fetches active-fire detections — one point
per fire pixel, with brightness, confidence, and fire-radiative-power —
from the NASA FIRMS (Fire Information for Resource Management System)
area CSV endpoint, across MODIS (C6.1) and VIIRS (S-NPP / NOAA-20 /
NOAA-21) sensors. The rows for a [start, end] window over a bbox come
back as CSV, which :mod:earthlens.firms.events maps to a pyramids
:class:~pyramids.feature.collection.FeatureCollection of fire-pixel
points.
This is a vector backend: the on-the-wire result is a table of
geolocated detections, not a gridded array, so OUTPUT_KIND = "vector"
and the :class:earthlens.earthlens.EarthLens facade rejects an
aggregate= argument (there is no meaningful gridded reduction of a
detection table). download() returns the in-memory FeatureCollection
and, as a side effect, writes it to one vector file under path.
FIRMS needs a free MAP_KEY — pass it to :meth:FIRMS.authenticate
as api_key=, or set FIRMS_MAP_KEY and let authenticate() /
download() read it from the environment. It is not a constructor
argument: the constructor describes only what to fetch. There is no SDK
and no [firms] extra — the only dependencies are requests +
pandas, both core. Sensor selection follows the vector-backend reading
of variables (see the package docstring): variables is a list[str]
of FIRMS sensor codes (["VIIRS_SNPP_NRT"],
["MODIS_NRT", "VIIRS_SNPP_NRT"]); the detection filters ride as
explicit min_confidence= / day_night= keyword arguments. The
temporal window is chunked internally into ≤5-day requests (the FIRMS
per-request cap), so temporal_resolution carries the sentinel "all".
FIRMS
#
Bases: AbstractDataSource
NASA FIRMS active-fire backend (vector point-feature output).
Wraps the FIRMS area CSV API so a user can pull a space/time/sensor
window of fire detections through the same download() shape every
other earthlens backend uses. Windows longer than the FIRMS 5-day
per-request cap are chunked, and each (sensor, ≤5-day chunk) is
one CSV GET; the rows are mapped to a
:class:~pyramids.feature.collection.FeatureCollection.
FIRMS needs a free MAP_KEY. Supply it to :meth:authenticate as
api_key=, or set the FIRMS_MAP_KEY environment variable and let
authenticate() / download() resolve it. Credentials are not a
constructor argument — the constructor describes only what to fetch.
Attributes:
| Name | Type | Description |
|---|---|---|
OUTPUT_KIND |
OutputKind
|
|
Source code in src/earthlens/firms/backend.py
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 | |
__init__(start, end, variables, lat_lim, lon_lim, temporal_resolution='all', path='', fmt='%Y-%m-%d', min_confidence=None, day_night=None, file_format='gpkg', timeout=60.0)
#
Initialise a FIRMS backend instance.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
start
|
str
|
Inclusive start of the detection window, as a string
parsed with |
required |
end
|
str
|
Inclusive end of the detection window. |
required |
variables
|
list[str]
|
List of FIRMS sensor codes to query
( |
required |
lat_lim
|
list[float]
|
|
required |
lon_lim
|
list[float]
|
|
required |
temporal_resolution
|
str
|
FIRMS chunks by ≤5-day windows
internally, not by a daily/monthly cadence, so this is
the sentinel |
'all'
|
path
|
Path | str
|
Output directory for the written vector file. Created by the parent class if absent. |
''
|
fmt
|
str
|
|
'%Y-%m-%d'
|
min_confidence
|
float | None
|
Optional 0-100 lower bound applied
client-side on the normalised |
None
|
day_night
|
str | None
|
Optional |
None
|
file_format
|
FileFormat
|
Output vector format — |
'gpkg'
|
timeout
|
float
|
Per-request timeout in seconds for each CSV GET. |
60.0
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
TypeError
|
If |
Source code in src/earthlens/firms/backend.py
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 | |
authenticate(api_key=None)
#
Resolve the FIRMS MAP_KEY and arm the backend for download.
The explicit, fail-fast credential step. Pass api_key= to use a
key directly; omit it (or pass None) to read the FIRMS_MAP_KEY
environment variable. Either way the resolved key is held for the
subsequent :meth:download. Calling it again with a different
api_key re-arms with the new key. download() calls this with
no argument on your behalf if you never do, so an explicit call is
only needed to pass a key directly or to validate up front.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
api_key
|
str | None
|
The FIRMS |
None
|
Returns:
| Type | Description |
|---|---|
FIRMS
|
The backend instance, so it chains |
FIRMS
|
|
Raises:
| Type | Description |
|---|---|
AuthenticationError
|
If |
Examples:
- Arm the backend with an explicit key and read it back:
>>> import tempfile >>> from earthlens.firms import FIRMS >>> backend = FIRMS( ... start="2024-08-01", end="2024-08-01", ... variables=["VIIRS_SNPP_NRT"], ... lat_lim=[33.0, 35.0], lon_lim=[-119.0, -117.0], ... path=tempfile.mkdtemp(), ... ) >>> backend.authenticate(api_key="demo-key").client.api_key 'demo-key' - A fresh backend is unauthenticated until the key resolves:
>>> import tempfile >>> from earthlens.firms import FIRMS >>> backend = FIRMS( ... start="2024-08-01", end="2024-08-01", ... variables=["VIIRS_SNPP_NRT"], ... lat_lim=[33.0, 35.0], lon_lim=[-119.0, -117.0], ... path=tempfile.mkdtemp(), ... ) >>> backend.client.is_authenticated() False >>> backend.authenticate(api_key="abc123").client.is_authenticated() True
Source code in src/earthlens/firms/backend.py
download(progress_bar=True, aggregate=None)
#
Query FIRMS and return the matched detections.
Runs the cheap :meth:_search (sensor validation + chunk
planning) then the throttled :meth:_fetch (one CSV GET per
chunk), concatenates the per-chunk collections into one
FeatureCollection, writes it to one vector file under path, and
returns it. An empty result returns — and writes nothing for — a
schema-correct empty FeatureCollection.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
progress_bar
|
bool
|
Show a per-chunk progress bar. Defaults to
|
True
|
aggregate
|
AggregationConfig | None
|
Must be |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
FeatureCollection |
FeatureCollection
|
The matched detections, CRS |
Raises:
| Type | Description |
|---|---|
NotImplementedError
|
If |
Source code in src/earthlens/firms/backend.py
earthlens.firms.events
#
Map a FIRMS area-CSV response into a pyramids FeatureCollection.
This module is the only place in the FIRMS backend that touches a GIS
vector container, so per the pyramids policy it keeps all geometry/CRS
handling inside pyramids primitives: earthlens assembles the normalised
attribute rows, builds a Point geometry column from longitude /
latitude, and hands the whole thing to
:class:pyramids.feature.collection.FeatureCollection (a
geopandas.GeoDataFrame subclass) tagged EPSG:4326.
The canonical detection schema lives here as :data:ATTRIBUTE_COLUMNS
(attribute columns + dtypes) plus the geometry column. Both the
populated path (:func:csv_to_fc) and the empty path (:func:empty_fc)
produce a FeatureCollection with exactly these columns and dtypes, so a
downstream to_file never chokes on a schema mismatch between a hit and
a miss.
Two FIRMS data-shape wrinkles are absorbed here:
- Confidence differs by sensor family (
G4). MODIS reports a numeric 0-100confidence; VIIRS reports a categoricall/n/htoken. The mapper keeps the raw value inconfidenceand derives a uniformconfidence_pctfloat — VIIRSl/n/hmap to 25/60/90, MODIS passes through. The rawconfidenceis always rendered as a string so the column dtype stays stable across families (categoricall/n/hand numeric85coexist); numeric consumers should read the floatconfidence_pctrather than re-parsingconfidence. A singlebrightness_kcolumn is filled frombrightness(MODIS) orbright_ti4(VIIRS), whichever the sensor provides. acq_timeis an unpadded integer HHMM (e.g.5= 00:05,1325= 13:25), so it is split into hours/minutes and added toacq_daterather than string-concatenated.
Columns are read defensively (.get / column-presence checks) so a
sensor missing a column degrades to NaN/None rather than raising.
concat(collections)
#
Concatenate per-chunk collections into one, schema-stable.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
collections
|
list[FeatureCollection]
|
The per- |
required |
Returns:
| Name | Type | Description |
|---|---|---|
FeatureCollection |
FeatureCollection
|
Their row-wise union, CRS |
Examples:
- Concatenating only empty collections returns an empty one:
Source code in src/earthlens/firms/events.py
csv_to_fc(df, sensor, family, min_confidence=None, day_night=None)
#
Normalise one sensor's FIRMS CSV frame into a FeatureCollection.
One row per detection, columns per :data:ATTRIBUTE_COLUMNS plus a
geometry column of Point(longitude, latitude). The MODIS/VIIRS
confidence and brightness schemas are unified (G4), acq_date +
integer-HHMM acq_time are combined into a tz-aware UTC
acq_datetime, and the optional min_confidence / day_night
filters are applied client-side (FIRMS offers no server-side
equivalent). An empty input frame returns an empty FeatureCollection
with the same columns/dtypes (see :func:empty_fc).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
df
|
DataFrame
|
The decoded FIRMS area CSV for one sensor/chunk. |
required |
sensor
|
str
|
The FIRMS sensor code; recorded in the |
required |
family
|
str
|
|
required |
min_confidence
|
float | None
|
Optional 0-100 lower bound on the normalised
|
None
|
day_night
|
str | None
|
Optional |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
FeatureCollection |
FeatureCollection
|
One feature per surviving detection, CRS
|
Examples:
- Map a one-row VIIRS frame; the
ltoken becomes 25 %:>>> import pandas as pd >>> from earthlens.firms.events import csv_to_fc >>> df = pd.DataFrame( ... { ... "latitude": [34.0], ... "longitude": [-118.0], ... "acq_date": ["2024-08-01"], ... "acq_time": [1325], ... "satellite": ["N"], ... "confidence": ["l"], ... "bright_ti4": [320.0], ... "frp": [12.5], ... "daynight": ["D"], ... } ... ) >>> fc = csv_to_fc(df, "VIIRS_SNPP_NRT", "VIIRS") >>> float(fc["confidence_pct"].iloc[0]) 25.0 >>> fc["acq_datetime"].iloc[0].strftime("%Y-%m-%d %H:%M") '2024-08-01 13:25' >>> fc.crs.to_epsg() 4326
Source code in src/earthlens/firms/events.py
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 | |
empty_fc()
#
Return an empty FeatureCollection with the canonical schema.
Used for an empty CSV, an out-of-coverage window, or a request whose filters dropped every row, so callers always get the same columns and dtypes back regardless of hit count.
Returns:
| Name | Type | Description |
|---|---|---|
FeatureCollection |
FeatureCollection
|
Zero rows, the :data: |
Examples:
- The schema is present even with no rows:
Source code in src/earthlens/firms/events.py
earthlens.firms.catalog
#
Sensor dispatch table for the NASA FIRMS active-fire backend.
FIRMS is a fixed set of active-fire sensors queried through one area CSV
endpoint, not a curated dataset catalogue, so this "catalog" is small: a
handful of rows mapping a FIRMS source code ("VIIRS_SNPP_NRT",
"MODIS_NRT", …) to a little metadata. It follows the ECMWF / GEE
convention where a sensor plays the "dataset" role and its CSV columns
play the "variable" role — a :class:Sensor nests a columns: map of
:class:SensorColumn rows.
Like gdacs_data_catalog.yaml / fdsn_data_catalog.yaml there is no
available_* index: the listed sensors are the whole FIRMS universe,
so an "available vs curated" split would just duplicate the map. Unlike
those two, FIRMS does ship a probe + audit pair (tools/firms/)
because the per-sensor CSV column schema varies by sensor family (MODIS
reports a numeric confidence; VIIRS reports a categorical l/n/h),
which is exactly the kind of drift a probe pins down.
:class:Catalog is a thin :class:earthlens.base.AbstractCatalog
subclass that loads the bundled firms_data_catalog.yaml and exposes
each row as a :class:Sensor, keyed by code under the inherited
datasets field — which is what gives it the cat["MODIS_NRT"] /
"MODIS_NRT" in cat / len(cat) dict-like surface and the did-you-mean
error for free. :data:CATALOG_PATH is the path to the bundled YAML and
is monkey-patchable in tests.
Catalog
#
Bases: AbstractCatalog
Sensor catalog for the NASA FIRMS backend.
Reads the bundled firms_data_catalog.yaml (shipped as package
data) and exposes its sensors: block as a map of :class:Sensor
rows, keyed by FIRMS source code under the inherited :attr:datasets
field. Instantiate with no arguments (Catalog());
:func:model_post_init loads and validates the YAML in one pass.
Resolve a sensor with :meth:get_sensor (a thin alias over
:meth:~earthlens.base.AbstractCatalog.get_dataset) and a single
column with :meth:get_column.
There is no available_* index — the listed sensors are the whole
FIRMS universe (a deliberate deviation from the ECMWF/GEE catalogs,
shared with GDACS/FDSN).
Attributes:
| Name | Type | Description |
|---|---|---|
datasets |
dict[str, Sensor]
|
Map from the FIRMS source code to its :class: |
Examples:
- List sensor codes and resolve one:
>>> from earthlens.firms import Catalog >>> cat = Catalog() >>> cat.codes() # doctest: +NORMALIZE_WHITESPACE ['GOES_NRT', 'LANDSAT_NRT', 'MODIS_NRT', 'MODIS_SP', 'VIIRS_NOAA20_NRT', 'VIIRS_NOAA20_SP', 'VIIRS_NOAA21_NRT', 'VIIRS_SNPP_NRT', 'VIIRS_SNPP_SP'] >>> cat.get_sensor("MODIS_NRT").family 'MODIS' >>> "MODIS_NRT" in cat True - An unknown code raises with a did-you-mean hint:
Source code in src/earthlens/firms/catalog.py
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 | |
codes()
#
Return the registered FIRMS sensor codes, sorted.
Returns:
| Type | Description |
|---|---|
list[str]
|
list[str]: The sensor codes ( |
get_catalog()
#
Return the sensor map (satisfies the abstract contract).
Returns:
| Type | Description |
|---|---|
dict[str, Sensor]
|
dict[str, Sensor]: Same object as :attr: |
get_column(code, column)
#
Return one column's metadata for a (sensor, column) pair.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
code
|
str
|
A FIRMS source code as it appears in :attr: |
required |
column
|
str
|
A CSV column name declared under that sensor. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
SensorColumn |
SensorColumn
|
The matching column metadata. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
KeyError
|
If |
Examples:
- Read a column's units:
Source code in src/earthlens/firms/catalog.py
get_sensor(code)
#
Return the :class:Sensor for code, with a did-you-mean hint.
Thin alias over
:meth:~earthlens.base.AbstractCatalog.get_dataset.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
code
|
str
|
A FIRMS source code ( |
required |
Returns:
| Name | Type | Description |
|---|---|---|
Sensor |
Sensor
|
The matching sensor row. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Source code in src/earthlens/firms/catalog.py
get_variable(code, column)
#
Leaf accessor for the shared two-arg get_variable contract.
Alias of :meth:get_column so the FIRMS leaf is reachable under
the same get_variable(dataset_key, variable_name) verb the
other two-level catalogs use.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
code
|
str
|
A FIRMS source code. |
required |
column
|
str
|
A CSV column name declared under that sensor. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
SensorColumn |
SensorColumn
|
The matching column metadata. |
Source code in src/earthlens/firms/catalog.py
load(catalog_path=None)
classmethod
#
Read the FIRMS sensor catalog from disk.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
catalog_path
|
Path | None
|
Path to the catalog YAML. Defaults to the
module-level :data: |
None
|
Returns:
| Type | Description |
|---|---|
Catalog
|
A fully-populated :class: |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the file has no |
Source code in src/earthlens/firms/catalog.py
model_post_init(__context)
#
Auto-load the bundled catalog when no sensors were supplied.
Catalog() with no args reads :data:CATALOG_PATH; passing
datasets=... skips the disk read (used in tests).
Raises:
| Type | Description |
|---|---|
ValueError
|
Propagated from :meth: |
Source code in src/earthlens/firms/catalog.py
Sensor
#
Bases: BaseModel
One FIRMS sensor's dispatch row (the "dataset" analog).
The FIRMS source code is the parent key in :attr:Catalog.datasets
and is repeated here as :attr:code so a :class:Sensor carries its
own identity when passed around outside the catalog.
Attributes:
| Name | Type | Description |
|---|---|---|
code |
str
|
FIRMS source code ( |
name |
str
|
Human-readable sensor name used in logs and docs. |
family |
Literal['MODIS', 'VIIRS', 'GOES', 'LANDSAT']
|
|
resolution_m |
int
|
Nominal nadir pixel size in metres (375 for VIIRS, 1000 for MODIS). |
temporal |
Temporal
|
The sensor's coverage window and quality tier. |
columns |
dict[str, SensorColumn]
|
Per-column metadata keyed by CSV column name. |
Examples:
- Inspect a sensor's resolution and a column:
Source code in src/earthlens/firms/catalog.py
SensorColumn
#
Bases: BaseModel
One FIRMS CSV column's metadata (the "variable" analog).
A frozen value object describing a single column a sensor emits in its area-CSV response. Mirrors the ECMWF / GEE per-variable row, but minimal: FIRMS CSV columns carry no request-shaping parameters, only descriptive metadata.
Attributes:
| Name | Type | Description |
|---|---|---|
units |
str
|
Physical unit of the column ( |
long_name |
str
|
Human-readable description used in docs and logs. |
Examples:
- Build a column row directly:
Source code in src/earthlens/firms/catalog.py
Temporal
#
Bases: BaseModel
A sensor's coverage window and quality tier.
Attributes:
| Name | Type | Description |
|---|---|---|
start |
date | None
|
First date the sensor has data for, or |
end |
date | None
|
Last date covered, or |
quality |
Literal['NRT', 'SP']
|
|
Examples:
- An ongoing NRT sensor:
Source code in src/earthlens/firms/catalog.py
earthlens.firms.auth
#
Credentials and MAP_KEY resolution for the NASA FIRMS backend.
Hosts :class:FirmsAuth, an :class:earthlens.base.AbstractAuth
subclass that resolves a single FIRMS MAP_KEY from, in priority
order, an explicit api_key= argument or the FIRMS_MAP_KEY
environment variable. FIRMS requires a (free) key on every request;
there is no username/password and no saved config-file dance, so this is
the same single-secret shape as :class:earthlens.openaq.OpenaqAuth —
mirrored on the CmemsAuth resolution chain but without the toolbox
login.
Unlike OpenAQ (which attaches its key as an X-API-Key header via a
dedicated client), FIRMS sends the MAP_KEY as a path segment in
the request URL, so there is no separate client module: the resolved key
is read back via the :attr:FirmsAuth.api_key property and dropped into
the URL by :class:earthlens.firms.FIRMS directly.
The shape:
- :class:
FirmsCredentialsis a frozen pydantic value object carrying the optional key as a :class:pydantic.SecretStr. - :class:
FirmsAuthbinds those credentials and resolves the key in :meth:FirmsAuth.configure— explicit key first, then theFIRMS_MAP_KEYenv var, then a clear :class:AuthenticationErrornaming the free-registration URL (never an interactive prompt). configure()is idempotent — a second call after :meth:FirmsAuth.is_authenticatedreturnsTrueshort-circuits, so it is safe to call from long-lived workers.
AuthenticationError
#
Bases: AuthenticationError
Raised when no usable FIRMS MAP_KEY can be resolved.
Carries a message that names a fix: pass api_key= to
EarthLens(...).authenticate(), set the FIRMS_MAP_KEY environment
variable, or request a free key at
firms.modaps.eosdis.nasa.gov/api/map_key/. A subclass of the
cross-backend :class:earthlens.base.AuthenticationError so callers
can catch every backend's auth failure with one except clause.
Source code in src/earthlens/firms/auth.py
FirmsAuth
#
Bases: AbstractAuth[FirmsCredentials]
Resolve and hold the FIRMS MAP_KEY.
Implements the :class:earthlens.base.AbstractAuth contract for a
single-secret backend. Construction does not touch the environment;
:meth:configure performs the resolution and is idempotent. After a
successful configure(), the key is available via the
:attr:api_key property for the backend to drop into the request
URL.
The class is a context manager (inherited from
:class:AbstractAuth): with FirmsAuth(creds) as auth: ... calls
configure() on enter and the default no-op close() on exit —
there is no per-instance resource to release.
Attributes:
| Name | Type | Description |
|---|---|---|
_creds |
The :class: |
Examples:
- Resolve an explicit key:
Source code in src/earthlens/firms/auth.py
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 | |
api_key
property
#
The resolved MAP_KEY; valid only after :meth:configure.
Returns:
| Name | Type | Description |
|---|---|---|
str |
str
|
The FIRMS |
Raises:
| Type | Description |
|---|---|
AuthenticationError
|
When read before :meth: |
__init__(credentials)
#
Store credentials; does not resolve the key yet.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
credentials
|
FirmsCredentials
|
The :class: |
required |
Source code in src/earthlens/firms/auth.py
configure()
#
Resolve the MAP_KEY so subsequent requests can authenticate.
Idempotent — short-circuits when :meth:is_authenticated
already returns True. On the first call, resolves the key in
this order: the explicit api_key on the credentials, then the
FIRMS_MAP_KEY environment variable.
Raises:
| Type | Description |
|---|---|
AuthenticationError
|
When neither source supplies a key. The
message names the |
Source code in src/earthlens/firms/auth.py
is_authenticated()
#
Return True once :meth:configure has resolved a key.
Cheap predicate — does not call the network. A return of True
means a usable key is held by this instance.
Returns:
| Name | Type | Description |
|---|---|---|
bool |
bool
|
|
Source code in src/earthlens/firms/auth.py
FirmsCredentials
#
Bases: BaseModel
Frozen value object holding the FIRMS MAP_KEY.
The key is optional at construction time: None means "resolve from
the FIRMS_MAP_KEY environment variable at
:meth:FirmsAuth.configure time". The real "is there a usable key?"
gate is :meth:FirmsAuth.configure, not this model.
Attributes:
| Name | Type | Description |
|---|---|---|
api_key |
SecretStr | None
|
The FIRMS |
Examples:
- Build from an explicit key; the secret is hidden in
repr: - The key is optional — rely on the environment instead: