Satellite Data Readers¶
This module provides specialized readers for various optical satellite missions. All these readers implement the GeoData protocol, which means they provide a consistent interface for spatial operations, data access, and manipulation.
These readers make it easy to work with official data formats from different Earth observation missions, and they can be used with all the functions available in the georeader.read module.
Readers available:
Sentinel-2 Reader¶
The Sentinel-2 reader provides functionality for reading Sentinel-2 L1C and L2A products in SAFE format. It supports:
- Direct reading from local files or cloud storage (Google Cloud Storage)
- Windowed reading for efficient memory usage
- Conversion from digital numbers to radiance
- Access to metadata, including viewing geometry and solar angles
Tutorial examples:
- Reading from the public Google bucket
- Exploring image metadata
- Creating mosaics from multiple images
- Converting TOA reflectance to radiance
API Reference¶
Sentinel-2 SAFE Product Reader for L1C and L2A Data.
This module provides readers for Sentinel-2 satellite imagery in the SAFE format, supporting both Level-1C (top-of-atmosphere reflectance) and Level-2A (surface reflectance) products. It handles local files and cloud storage (Google Cloud).
Sentinel-2 Product Levels¶
::
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β SENTINEL-2 PROCESSING LEVELS β
β β
β Level-1C (L1C) Level-2A (L2A) β
β βββββββββββββββββ βββββββββββββββββ β
β β
β βοΈ Sun βοΈ Sun β
β β β β
β βΌ βΌ β
β βββββββββββ βββββββββββ β
β βAtmosphereβ ββ NOT corrected βAtmosphereβ ββ CORRECTED β
β ββββββ¬βββββ ββββββ¬βββββ β
β β β β
β βΌ βΌ β
β βββββββββββ βββββββββββ β
β β Surface β β Surface β β
β βββββββββββ βββββββββββ β
β β β β
β βΌ π°οΈ βΌ π°οΈ β
β β
β TOA Reflectance BOA Reflectance β
β - Includes atmospheric effects - Surface reflectance β
β - Globally available - Atmospheric correction applied β
β - Can convert to radiance - Scene Classification (SCL) β
β - 13 bands (incl. B10 cirrus) - 12 bands (no B10) β
β β
β Use for: Use for: β
β - Radiance-based analysis - Land cover mapping β
β - Custom atmospheric correction - Vegetation indices (NDVI) β
β - Cloud studies (B10) - Change detection β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Spectral Bands¶
::
Band β Central Ξ» β Bandwidth β Resolution β L1C β L2A β Description
ββββββΌββββββββββββΌββββββββββββΌβββββββββββββΌββββββΌββββββΌβββββββββββββββββββββ
B01 β 443 nm β 20 nm β 60m β β β β β Coastal/Aerosol
B02 β 490 nm β 65 nm β 10m β β β β β Blue
B03 β 560 nm β 35 nm β 10m β β β β β Green
B04 β 665 nm β 30 nm β 10m β β β β β Red
B05 β 705 nm β 15 nm β 20m β β β β β Red Edge 1
B06 β 740 nm β 15 nm β 20m β β β β β Red Edge 2
B07 β 783 nm β 20 nm β 20m β β β β β Red Edge 3
B08 β 842 nm β 115 nm β 10m β β β β β NIR
B8A β 865 nm β 20 nm β 20m β β β β β NIR Narrow
B09 β 945 nm β 20 nm β 60m β β β β β Water Vapour
B10 β 1375 nm β 30 nm β 60m β β β β β Cirrus (L1C only)
B11 β 1610 nm β 90 nm β 20m β β β β β SWIR 1
B12 β 2190 nm β 180 nm β 20m β β β β β SWIR 2
Data Access¶
Products can be loaded from:
-
Local SAFE folders::
s2 = S2ImageL2A("/data/S2A_MSIL2A_20240115T...SAFE")
-
Google Cloud Public Bucket (free, no auth)::
path = "gs://gcp-public-data-sentinel-2/tiles/32/T/QM/..." s2 = S2ImageL2A(path)
-
Other cloud storage (via fsspec)::
s2 = S2ImageL2A("s3://bucket/S2A_MSIL2A_...SAFE", requester_pays=True)
Quick Start Examples¶
Load L2A surface reflectance (most common)::
from georeader.readers.S2_SAFE_reader import S2ImageL2A
from shapely.geometry import box
# Define area of interest in WGS84
aoi = box(-3.75, 40.40, -3.65, 40.50) # Madrid area
# Load from Google Cloud public bucket
s2 = S2ImageL2A(
"gs://gcp-public-data-sentinel-2/L2/tiles/30/T/VK/"
"S2A_MSIL2A_20240115T110351_N0510_R094_T30TVK_20240115T144512.SAFE",
polygon=aoi,
out_res=10, # 10m resolution
bands=["B04", "B03", "B02", "B08"] # RGBNIR
)
# Load as GeoTensor
gt = s2.load()
print(f"Shape: {gt.shape}") # (4, H, W)
print(f"CRS: {gt.crs}") # EPSG:32630 (UTM 30N)
Load L1C and convert to radiance::
from georeader.readers.S2_SAFE_reader import S2ImageL1C
s2_l1c = S2ImageL1C("/path/to/S2A_MSIL1C_...SAFE", polygon=aoi)
# Read tile metadata for solar angles
s2_l1c.read_metadata_tl()
# Get solar zenith angle
sza = s2_l1c.mean_sza
# Convert DN to at-sensor radiance (W/mΒ²/sr/Β΅m)
radiance = s2_l1c.DN_to_radiance(bands=["B04", "B03", "B02"])
Classes¶
S2Image Base class with shared functionality (don't use directly) S2ImageL1C Level-1C reader with TOA reflectance and angle accessors S2ImageL2A Level-2A reader with surface reflectance
See Also¶
georeader.reflectance : Radiance β reflectance conversions georeader.readers.ee_image : Load Sentinel-2 via Google Earth Engine
References¶
- ESA Sentinel-2 User Guide: https://sentinel.esa.int/web/sentinel/user-guides/sentinel-2-msi
- Google Cloud Sentinel-2 Bucket: https://cloud.google.com/storage/docs/public-datasets/sentinel-2
- Sentinel-2 Radiometric Resolution: https://sentiwiki.copernicus.eu/web/s2-processing
Authors: Gonzalo Mateo-GarcΓa, Dan Lopez-Puigdollers
S2Image
¶
Base Sentinel-2 image reader for handling Sentinel-2 satellite products. Do Not use this class directly, use S2ImageL1C or S2ImageL2A instead.
This class provides functionality to read and manipulate Sentinel-2 satellite imagery. It handles the specific format and metadata of Sentinel-2 products, supporting operations like loading bands, masks, and converting digital numbers to radiance.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
s2folder
|
str
|
Path to the Sentinel-2 SAFE product folder. |
required |
polygon
|
Optional[Polygon]
|
Polygon defining the area of interest in EPSG:4326. Defaults to None (entire image). |
None
|
granules
|
Optional[Dict[str, str]]
|
Dictionary mapping band names to file paths. Defaults to None (automatically discovered). |
None
|
out_res
|
int
|
Output resolution in meters. Must be one of 10, 20, or 60. Defaults to 10. |
10
|
window_focus
|
Optional[Window]
|
Window to focus on a specific region of the image. Defaults to None (entire image). |
None
|
bands
|
Optional[List[str]]
|
List of bands to read. If None, all available bands will be loaded based on the product type. |
None
|
metadata_msi
|
Optional[str]
|
Path to metadata file. If None, it is assumed to be in the SAFE folder. |
None
|
Attributes:
| Name | Type | Description |
|---|---|---|
mission |
str
|
Mission identifier (e.g., 'S2A', 'S2B'). |
producttype |
str
|
Product type identifier (e.g., 'MSIL1C', 'MSIL2A'). |
pdgs |
str
|
PDGS Processing Baseline number. |
relorbitnum |
str
|
Relative Orbit number. |
tile_number_field |
str
|
Tile Number field. |
product_discriminator |
str
|
Product Discriminator. |
name |
str
|
Base name of the product. |
folder |
str
|
Path to the product folder. |
datetime |
datetime
|
Acquisition datetime. |
metadata_msi |
str
|
Path to the MSI metadata file. |
out_res |
int
|
Output resolution in meters. |
bands |
List[str]
|
List of bands to read. |
dims |
Tuple[str]
|
Names of the dimensions ("band", "y", "x"). |
fill_value_default |
int
|
Default fill value (typically 0). |
band_check |
str
|
Band used as template for reading. |
granule_readers |
Dict[str, RasterioReader]
|
Dictionary of readers for each band. |
window_focus |
Window
|
Current window focus. |
transform |
Affine transform for the window. |
|
crs |
Coordinate reference system. |
|
shape |
Shape of the data (bands, height, width). |
|
bounds |
Bounds of the window. |
|
res |
Tuple[float, float]
|
Resolution of the data. |
Source code in georeader/readers/S2_SAFE_reader.py
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 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 | |
__init__(s2folder, polygon=None, granules=None, out_res=10, window_focus=None, bands=None, metadata_msi=None)
¶
Sentinel-2 image reader class.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
s2folder
|
str
|
name of the SAFE product expects name |
required |
polygon
|
Optional[Polygon]
|
in CRS EPSG:4326 |
None
|
granules
|
Optional[Dict[str, str]]
|
dictionary with granule name and path |
None
|
out_res
|
int
|
output resolution in meters one of 10, 20, 60 (default 10) |
10
|
window_focus
|
Optional[Window]
|
rasterio window to read. All reads will be based on this window |
None
|
bands
|
Optional[List[str]]
|
list of bands to read. If None all bands are read. |
None
|
metadata_msi
|
Optional[str]
|
path to metadata file. If None it is assumed to be in the SAFE folder |
None
|
Source code in georeader/readers/S2_SAFE_reader.py
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 | |
cache_product_to_local_dir(path_dest=None, print_progress=True, format_bands=None)
¶
Copy the product to a local directory and return a new instance of the class with the new path
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path_dest
|
Optional[str]
|
path to the destination folder. If None, the current folder ()".") is used |
None
|
print_progress
|
bool
|
print progress bar. Default True |
True
|
format_bands
|
Optional[str]
|
format of the bands. Default None (keep original format). Options: "COG", "GeoTIFF" |
None
|
Returns:
| Type | Description |
|---|---|
__class__
|
A new instance of the class pointing to the new path |
Source code in georeader/readers/S2_SAFE_reader.py
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 | |
get_reader(band_names, overview_level=None)
¶
Provides a RasterioReader object to read all the bands at the same resolution
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
band_names
|
Union[str, List[str]]
|
List of band names or band. raises assertion error if bands have different resolution. |
required |
overview_level
|
Optional[int]
|
level of the pyramid to read (same as in rasterio) |
None
|
Returns:
| Type | Description |
|---|---|
RasterioReader
|
RasterioReader |
Source code in georeader/readers/S2_SAFE_reader.py
675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 | |
quantification_value()
¶
Returns the quantification value stored in the metadata msi file (this is always: 10_000)
Source code in georeader/readers/S2_SAFE_reader.py
665 666 667 668 669 670 671 672 673 | |
read_from_band_names(band_names)
¶
Read from band names
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
band_names
|
List[str]
|
List of band names |
required |
Returns:
| Type | Description |
|---|---|
__class__
|
Copy of current object with band names set to band_names |
Source code in georeader/readers/S2_SAFE_reader.py
804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 | |
solar_irradiance()
¶
Returns solar irradiance per nanometer: W/mΒ²/nm
Source code in georeader/readers/S2_SAFE_reader.py
642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 | |
S2ImageL1C
¶
Bases: S2Image
Sentinel-2 Level 1C (top of atmosphere reflectance) image reader.
This class extends the base S2Image class to handle Sentinel-2 Level 1C products, which provide calibrated and orthorectified top of atmosphere reflectance data. It also provides methods to access viewing and solar angle information.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
s2folder
|
str
|
Path to the Sentinel-2 SAFE product folder. |
required |
granules
|
Dict[str, str]
|
Dictionary mapping band names to file paths. |
required |
polygon
|
Polygon
|
Polygon defining the area of interest in EPSG:4326. |
required |
out_res
|
int
|
Output resolution in meters. Must be one of 10, 20, or 60. Defaults to 10. |
10
|
window_focus
|
Optional[Window]
|
Window to focus on a specific region of the image. Defaults to None (entire image). |
None
|
bands
|
Optional[List[str]]
|
List of bands to read. If None, all available bands will be loaded. |
None
|
metadata_msi
|
Optional[str]
|
Path to metadata file. If None, it is assumed to be in the SAFE folder. |
None
|
Attributes:
| Name | Type | Description |
|---|---|---|
Additional |
to S2Image attributes
|
|
granule_folder |
str
|
Path to the granule folder. |
msk_clouds_file |
str
|
Path to the cloud mask file. |
metadata_tl |
str
|
Path to the TL metadata file. |
root_metadata_tl |
Root element of the TL metadata XML. |
|
tileId |
str
|
Tile identifier. |
satId |
str
|
Satellite identifier. |
procLevel |
str
|
Processing level. |
dimsByRes |
Dict
|
Dimensions by resolution. |
ulxyByRes |
Dict
|
Upper-left coordinates by resolution. |
tileAnglesNode |
Dict
|
Tile angles node from metadata. |
mean_sza |
float
|
Mean solar zenith angle. |
mean_saa |
float
|
Mean solar azimuth angle. |
mean_vza |
Dict[str, float]
|
Mean viewing zenith angle per band. |
mean_vaa |
Dict[str, float]
|
Mean viewing azimuth angle per band. |
vaa |
Dict[str, GeoTensor]
|
Viewing azimuth angle as GeoTensor per band. |
vza |
Dict[str, GeoTensor]
|
Viewing zenith angle as GeoTensor per band. |
saa |
GeoTensor
|
Solar azimuth angle as GeoTensor. |
sza |
GeoTensor
|
Solar zenith angle as GeoTensor. |
anglesULXY |
Tuple[float, float]
|
Upper-left coordinates of the angle grids. |
Examples:
>>> # Initialize the S2ImageL1C reader with a data path
>>> s2_l1c = S2ImageL1C('/path/to/S2A_MSIL1C_20170717T235959_N0205_R072_T01WCP_20170718T000256.SAFE',
... granules=granules_dict, polygon=aoi_polygon)
>>> # Load all bands
>>> l1c_data = s2_l1c.load()
>>> # Read angle information
>>> s2_l1c.read_metadata_tl()
>>> solar_zenith = s2_l1c.sza
>>> # Convert to radiance
>>> radiance_data = s2_l1c.DN_to_radiance()
Source code in georeader/readers/S2_SAFE_reader.py
1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 | |
cache_product_to_local_dir(path_dest=None, print_progress=True, format_bands=None)
¶
Overrides the parent method to copy the MTD_TL.xml file
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path_dest
|
Optional[str]
|
path to the destination folder. Defaults to None. |
None
|
print_progress
|
bool
|
whether to print progress. Defaults to True. |
True
|
Returns:
| Name | Type | Description |
|---|---|---|
__class__ |
__class__
|
the cached object |
Source code in georeader/readers/S2_SAFE_reader.py
1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 | |
read_metadata_tl()
¶
Read metadata TILE to parse information about the acquisition and properties of GRANULE bands.
It populates the following attributes
- mean_sza
- mean_saa
- mean_vza
- mean_vaa
- vaa
- vza
- saa
- sza
- anglesULXY
- tileId
- satId
- procLevel
- epsg_code
- dimsByRes
- ulxyByRes
- tileAnglesNode
- root_metadata_tl
Source code in georeader/readers/S2_SAFE_reader.py
1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 | |
S2ImageL2A
¶
Bases: S2Image
Sentinel-2 Level 2A (surface reflectance) image reader.
This class extends the base S2Image class to handle Sentinel-2 Level 2A products, which provide surface reflectance data with atmospheric corrections applied.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
s2folder
|
str
|
Path to the Sentinel-2 SAFE product folder. |
required |
granules
|
Dict[str, str]
|
Dictionary mapping band names to file paths. |
required |
polygon
|
Polygon
|
Polygon defining the area of interest in EPSG:4326. |
required |
out_res
|
int
|
Output resolution in meters. Must be one of 10, 20, or 60. Defaults to 10. |
10
|
window_focus
|
Optional[Window]
|
Window to focus on a specific region of the image. Defaults to None (entire image). |
None
|
bands
|
Optional[List[str]]
|
List of bands to read. If None, the default L2A bands (excluding B10) will be loaded. |
None
|
metadata_msi
|
Optional[str]
|
Path to metadata file. If None, it is assumed to be in the SAFE folder. |
None
|
Attributes:
| Name | Type | Description |
|---|---|---|
mission |
str
|
Mission identifier (e.g., 'S2A', 'S2B'). |
producttype |
str
|
Product type identifier (e.g., 'MSIL2A'). |
pdgs |
str
|
PDGS Processing Baseline number. |
relorbitnum |
str
|
Relative Orbit number. |
tile_number_field |
str
|
Tile Number field. |
product_discriminator |
str
|
Product Discriminator. |
name |
str
|
Base name of the product. |
folder |
str
|
Path to the product folder. |
datetime |
datetime
|
Acquisition datetime. |
metadata_msi |
str
|
Path to the MSI metadata file. |
out_res |
int
|
Output resolution in meters. |
bands |
List[str]
|
List of bands to read. |
dims |
Tuple[str]
|
Names of the dimensions ("band", "y", "x"). |
fill_value_default |
int
|
Default fill value (typically 0). |
band_check |
str
|
Band used as template for reading. |
granule_readers |
Dict[str, RasterioReader]
|
Dictionary of readers for each band. |
window_focus |
Window
|
Current window focus. |
Examples:
>>> # Initialize the S2ImageL2A reader with a data path
>>> s2_l2a = S2ImageL2A('/path/to/S2A_MSIL2A_20170717T235959_N0205_R072_T01WCP_20170718T000256.SAFE',
... granules=granules_dict, polygon=aoi_polygon)
>>> # Load all bands
>>> l2a_data = s2_l2a.load()
Source code in georeader/readers/S2_SAFE_reader.py
918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 | |
s2loader(s2folder, out_res=10, bands=None, window_focus=None, granules=None, polygon=None, metadata_msi=None)
¶
Loads a S2ImageL2A or S2ImageL1C depending on the product type
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
s2folder
|
str
|
.SAFE folder. Expected standard ESA naming convention (see s2_name_split fun) |
required |
out_res
|
int
|
default output resolution {10, 20, 60} |
10
|
bands
|
Optional[List[str]]
|
Bands to read. Default to BANDS_S2 or BANDS_S2_L2A depending on the product type |
None
|
window_focus
|
Optional[Window]
|
window to read when creating the object |
None
|
granules
|
Optional[Dict[str, str]]
|
Dict where keys are the band names and values are paths to the band location |
None
|
polygon
|
Optional[Polygon]
|
polygon with the footprint of the object |
None
|
metadata_msi
|
Optional[str]
|
path to metadata file |
None
|
Returns:
| Type | Description |
|---|---|
Union[S2ImageL2A, S2ImageL1C]
|
S2Image reader |
Source code in georeader/readers/S2_SAFE_reader.py
1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 | |
s2_public_bucket_path(s2file, check_exists=False, mode='gcp')
¶
Returns the expected patch in the public bucket of the S2 file
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
s2file
|
str
|
safe file (e.g. S2B_MSIL1C_20220527T030539_N0400_R075_T49SGV_20220527T051042.SAFE) |
required |
check_exists
|
bool
|
check if the file exists in the bucket, This will not work if GOOGLE_APPLICATION_CREDENTIALS and/or GS_USER_PROJECT env variables are not set. Default to False |
False
|
mode
|
str
|
"gcp" or "rest" |
'gcp'
|
Returns:
| Type | Description |
|---|---|
str
|
full path to the file (e.g. gs://gcp-public-data-sentinel-2/tiles/49/S/GV/S2B_MSIL1C_20220527T030539_N0400_R075_T49SGV_20220527T051042.SAFE) |
Source code in georeader/readers/S2_SAFE_reader.py
1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 | |
read_srf(satellite, srf_file=SRF_FILE_DEFAULT, cache=True)
¶
Process the spectral response function file. If the file is not provided it downloads it from https://sentinel.esa.int/web/sentinel/user-guides/sentinel-2-msi/document-library/-/asset_publisher/Wk0TKajiISaR/content/sentinel-2a-spectral-responses
This function requires the fsspec package and pandas and openpyxl for reading excel files.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
satellite
|
str
|
satellite name (S2A, S2B or S2C) |
required |
srf_file
|
str
|
path to the srf file |
SRF_FILE_DEFAULT
|
cache
|
bool
|
if True, the srf is cached for future calls. Default True |
True
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
pd.DataFrame: spectral response function for each of the bands of S2 |
Source code in georeader/readers/S2_SAFE_reader.py
1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 | |
Proba-V Reader¶
The Proba-V reader enables access to Proba-V Level 2A and Level 3 products. It handles:
- Reading TOA reflectance from HDF5 files
- Mask handling for clouds, shadows, and invalid pixels
- Extraction of metadata and acquisition parameters
Tutorial example:
API Reference¶
Proba-V reader
Unnoficial Proba-V reader. This reader is based in the Proba-V user manual: https://publications.vito.be/2017-1333-probav-products-user-manual.pdf
Author: Gonzalo Mateo-GarcΓa
ProbaV
¶
Proba-V reader for handling Proba-V satellite products.
This class provides functionality to read and manipulate Proba-V satellite imagery products. It handles the specific format and metadata of Proba-V HDF5 files, supporting operations like loading radiometry data, masks, and cloud information.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
hdf5_file
|
str
|
Path to the HDF5 file containing the Proba-V product. |
required |
window
|
Optional[Window]
|
Optional window to focus on a specific region of the image. Defaults to None (entire image). |
None
|
level_name
|
str
|
Processing level of the product, either "LEVEL2A" or "LEVEL3". Defaults to "LEVEL3". |
'LEVEL3'
|
Attributes:
| Name | Type | Description |
|---|---|---|
hdf5_file |
str
|
Path to the HDF5 file. |
name |
str
|
Basename of the HDF5 file. |
camera |
str
|
Camera ID (for LEVEL2A products). |
res_name |
str
|
Resolution name identifier (e.g., '100M', '300M', '1KM'). |
version |
str
|
Product version. |
toatoc |
str
|
Indicator of whether data is TOA (top of atmosphere) or TOC (top of canopy). |
real_transform |
Affine
|
Affine transform for the full image. |
real_shape |
Tuple[int, int]
|
Shape of the full image (height, width). |
dtype_radiometry |
Data type for radiometry data (typically np.float32). |
|
dtype_sm |
Data type for SM (status map) data. |
|
metadata |
Dict[str, Any]
|
Dictionary with product metadata. |
window_focus |
Window
|
Current window focus. |
window_data |
Window
|
Window representing the full data extent. |
start_date |
datetime
|
Start acquisition date and time. |
end_date |
datetime
|
End acquisition date and time. |
map_projection_wkt |
str
|
WKT representation of the map projection. |
crs |
Coordinate reference system. |
|
level_name |
str
|
Processing level identifier. |
Examples:
>>> import rasterio.windows
>>> # Initialize the ProbaV reader with a data path
>>> probav_reader = ProbaV('/path/to/probav_product.HDF5')
>>> # Load radiometry data
>>> bands = probav_reader.load_radiometry()
>>> # Get cloud mask
>>> cloud_mask = probav_reader.load_sm_cloud_mask()
>>> # Focus on a specific window
>>> window = rasterio.windows.Window(col_off=100, row_off=100, width=200, height=200)
>>> probav_reader.set_window(window)
Source code in georeader/readers/probav_image_operational.py
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 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 | |
load_mask(boundless=True)
¶
Returns the valid mask (False if the pixel is out of swath or is invalid). This function loads the SM band
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
boundless
|
bool
|
boundless option to load the SM band. Defaults to True. |
True
|
Returns:
| Type | Description |
|---|---|
GeoTensor
|
geotensor.GeoTensor: mask with the same shape as the image |
Source code in georeader/readers/probav_image_operational.py
357 358 359 360 361 362 363 364 365 366 367 368 369 370 | |
load_sm(boundless=True)
¶
Reference of values in SM flags.
From user manual pag 67
* Clear -> 000
* Shadow -> 001
* Undefined -> 010
* Cloud -> 011
* Ice -> 100
* 2**3 sea/land
* 2**4 quality swir (0 bad 1 good)
* 2**5 quality nir
* 2**6 quality red
* 2**7 quality blue
* 2**8 coverage swir (0 no 1 yes)
* 2**9 coverage nir
* 2**10 coverage red
* 2**11 coverage blue
Source code in georeader/readers/probav_image_operational.py
333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 | |
save_bands(img)
¶
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
img
|
ndarray
|
(4, self.real_height, self.real_width, 4) tensor |
required |
Returns:
Source code in georeader/readers/probav_image_operational.py
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 | |
ProbaVRadiometry
¶
Bases: ProbaV
A specialized ProbaV reader class focused on radiometry data.
This class extends the base ProbaV class to provide a simplified interface for working with radiometry bands from Proba-V products.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
hdf5_file
|
str
|
Path to the HDF5 file containing the Proba-V product. |
required |
window
|
Optional[Window]
|
Optional window to focus on a specific region of the image. Defaults to None (entire image). |
None
|
level_name
|
str
|
Processing level of the product. Defaults to "LEVEL2A". |
'LEVEL2A'
|
indexes
|
Optional[List[int]]
|
Optional list of band indices to load. If None, all four bands (0=BLUE, 1=RED, 2=NIR, 3=SWIR) will be loaded. Defaults to None. |
None
|
Attributes:
| Name | Type | Description |
|---|---|---|
dims |
Tuple[str]
|
Names of the dimensions ("band", "y", "x"). |
indexes |
List[int]
|
List of band indices to load. |
dtype |
Data type of the radiometry data. |
|
count |
int
|
Number of bands to be loaded. |
shape |
Tuple[int, int, int]
|
Shape of the data (bands, height, width). |
values |
ndarray
|
The radiometry data values. |
Examples:
>>> # Initialize the ProbaVRadiometry reader with a data path
>>> probav_rad = ProbaVRadiometry('/path/to/probav_product.HDF5')
>>> # Load only RED and NIR bands
>>> probav_rad_rn = ProbaVRadiometry('/path/to/probav_product.HDF5', indexes=[1, 2])
>>> # Get the data as a GeoTensor
>>> geotensor_data = probav_rad.load()
Source code in georeader/readers/probav_image_operational.py
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 | |
ProbaVSM
¶
Bases: ProbaV
A specialized ProbaV reader class focused on Status Map (SM) data.
This class extends the base ProbaV class to provide a simplified interface for working with the status map band from Proba-V products. The SM band contains information about the pixel quality, cloud status, etc.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
hdf5_file
|
str
|
Path to the HDF5 file containing the Proba-V product. |
required |
window
|
Optional[Window]
|
Optional window to focus on a specific region of the image. Defaults to None (entire image). |
None
|
level_name
|
str
|
Processing level of the product. Defaults to "LEVEL2A". |
'LEVEL2A'
|
Attributes:
| Name | Type | Description |
|---|---|---|
dims |
Tuple[str]
|
Names of the dimensions ("y", "x"). |
dtype |
Data type of the SM data. |
|
shape |
Tuple[int, int]
|
Shape of the SM data (height, width). |
values |
ndarray
|
The SM data values. |
Examples:
>>> # Initialize the ProbaVSM reader with a data path
>>> probav_sm = ProbaVSM('/path/to/probav_product.HDF5')
>>> # Get the SM data as a GeoTensor
>>> sm_data = probav_sm.load()
>>> # Extract cloud information
>>> cloud_mask = sm_cloud_mask(sm_data.values)
Source code in georeader/readers/probav_image_operational.py
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 | |
SPOT-VGT Reader¶
The SPOT-VGT reader provides functionality for reading SPOT-VGT products. Features include:
- HDF4 file format support
- Handling of radiometry and quality layers
- Cloud and shadow mask extraction
Note: See the Proba-V tutorial for similar processing workflows as both sensors share similar data structures.
API Reference¶
SPOT VGT reader
Unofficial reader for SPOT VGT products. The reader is based on the user manual: https://docs.terrascope.be/DataProducts/SPOT-VGT/references/SPOT_VGT_PUM_v1.3.pdf
Authors: Dan Lopez-Puigdollers, Gonzalo Mateo-GarcΓa
SpotVGT
¶
SPOT-VGT reader for handling SPOT Vegetation satellite products.
This class provides functionality to read and manipulate SPOT-VGT satellite imagery products. It handles the specific format and metadata of SPOT-VGT HDF4 files, supporting operations like loading radiometry data, masks, and cloud information.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
hdf4_file
|
str
|
Path to the HDF4 file or directory containing the SPOT-VGT product. |
required |
window
|
Optional[Window]
|
Optional window to focus on a specific region of the image. Defaults to None (entire image). |
None
|
Attributes:
| Name | Type | Description |
|---|---|---|
hdf4_file |
str
|
Path to the HDF4 file. |
name |
str
|
Basename of the HDF4 file. |
satelliteID |
str
|
Satellite ID extracted from the filename. |
station |
str
|
Station code extracted from the filename. |
productID |
str
|
Product ID extracted from the filename. |
year, |
month, day (str
|
Date components extracted from the filename. |
segment |
str
|
Segment identifier extracted from the filename. |
version |
str
|
Product version extracted from the filename. |
files |
List[str]
|
List of files in the SPOT-VGT product. |
files_dict |
Dict[str, str]
|
Dictionary mapping band names to file paths. |
metadata |
Dict[str, str]
|
Metadata extracted from the LOG file. |
real_shape |
Tuple[int, int]
|
Shape of the full image (height, width). |
real_transform |
Affine
|
Affine transform for the full image. |
dtype_radiometry |
Data type for radiometry data (typically np.float32). |
|
window_focus |
Window
|
Current window focus. |
window_data |
Window
|
Window representing the full data extent. |
start_date |
datetime
|
Start acquisition date and time. |
end_date |
datetime
|
End acquisition date and time. |
crs |
Coordinate reference system. |
|
toatoc |
str
|
Indicator of whether data is TOA (top of atmosphere). |
res_name |
str
|
Resolution name identifier (e.g., '1KM'). |
level_name |
str
|
Processing level identifier. |
Examples:
>>> import rasterio.windows
>>> # Initialize the SpotVGT reader with a data path
>>> spot_reader = SpotVGT('/path/to/V2KRNP____20140321F146_V003')
>>> # Load radiometry data
>>> bands = spot_reader.load_radiometry()
>>> # Get cloud mask
>>> cloud_mask = spot_reader.load_sm_cloud_mask()
>>> # Focus on a specific window
>>> window = rasterio.windows.Window(col_off=100, row_off=100, width=200, height=200)
>>> spot_reader.set_window(window)
Source code in georeader/readers/spotvgt_image_operational.py
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 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 | |
load_mask(boundless=True)
¶
Returns the valid mask (False if the pixel is out of swath or is invalid). This function loads the SM band
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
boundless
|
bool
|
boundless option to load the SM band. Defaults to True. |
True
|
Returns:
| Type | Description |
|---|---|
GeoTensor
|
geotensor.GeoTensor: mask with the same shape as the image |
Source code in georeader/readers/spotvgt_image_operational.py
283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 | |
load_sm(boundless=True)
¶
Reference of values in SM flags.
From user manual pag 46
* Clear -> 000
* Shadow -> 001
* Undefined -> 010
* Cloud -> 011
* Ice -> 100
* 2**3 sea/land
* 2**4 quality swir (0 bad 1 good)
* 2**5 quality nir
* 2**6 quality red
* 2**7 quality blue
Source code in georeader/readers/spotvgt_image_operational.py
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 | |
PRISMA Reader¶
The PRISMA reader handles data from the Italian Space Agency's hyperspectral mission, specifically working with Level 1B radiance data (not atmospherically corrected). PRISMA provides hyperspectral imaging in the 400-2500 nm spectral range, with a spectral resolution of ~12 nm.
Key features:
- Reading L1B hyperspectral radiance data from HDF5 format files
- Handling separate VNIR (400-1000 nm) and SWIR (1000-2500 nm) spectral ranges
- Georeferencing functionality for non-orthorectified data using provided latitude/longitude coordinates
- On-demand conversion from radiance (mW/mΒ²/sr/nm) to top-of-atmosphere reflectance
- Spectral response function integration for accurate band simulation
- Extraction of RGB previews from specific wavelengths
- Access to satellite and solar geometry information for radiometric calculations
Tutorial examples:
API Reference¶
Module to read PRISMA (PRecursore IperSpettrale della Missione Applicativa) hyperspectral images.
PRISMA is an Italian Space Agency (ASI) Earth observation satellite launched in 2019, carrying a hyperspectral imaging spectrometer that captures data in 239 spectral bands from 400 to 2500 nm with a 30m spatial resolution.
Data Format Overview¶
PRISMA data is distributed in HDF5 format (HE5 extension) with a specific structure:
PRISMA HDF5 File Structure:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β /HDFEOS/SWATHS/PRS_L1_HCO/ β
β βββ Data Fields/ β
β β βββ VNIR_Cube: (bands, crosstrack, downtrack) β
β β β βββ 400-1010 nm, ~66 bands β
β β βββ SWIR_Cube: (bands, crosstrack, downtrack) β
β β βββ 920-2500 nm, ~173 bands β
β βββ Geolocation Fields/ β
β β βββ Latitude_SWIR, Longitude_SWIR β
β β βββ Latitude_VNIR, Longitude_VNIR β
β βββ Attributes (solar/view angles, timing, etc.) β
β β
β /KDP_AUX/ β
β βββ Cw_Vnir_Matrix, Cw_Swir_Matrix (wavelengths) β
β βββ Fwhm_Vnir_Matrix, Fwhm_Swir_Matrix β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Unlike EMIT, PRISMA data is NOT orthorectified. The geolocation arrays provide lat/lon coordinates for each pixel, requiring gridding for visualization.
Dual-Sensor Configuration¶
PRISMA uses two separate sensors for VNIR and SWIR:
VNIR Sensor SWIR Sensor
ββββββββββββββββββββββ ββββββββββββββββββββββ
β 400 - 1010 nm β β 920 - 2500 nm β
β ~66 bands β β ~173 bands β
β ~10 nm sampling β β ~10 nm sampling β
β β β β
β Shared 30m GSD β β Shared 30m GSD β
ββββββββββββββββββββββ ββββββββββββββββββββββ
β β
βββββββββββββ Overlap ββββββββββββββββ
920-1010 nm
The VNIR and SWIR sensors have overlapping wavelength coverage in the 920-1010 nm region, which can be used for cross-calibration.
Radiometric Units¶
- L1 Radiance: mW/(mΒ²Β·srΒ·nm) - milliwatts per square meter per steradian per nanometer (equivalent to W/(mΒ²Β·srΒ·ΞΌm))
- Scale factors and offsets are applied during loading to convert from DN to radiance
Spectral Characteristics¶
- Total bands: ~239 (66 VNIR + 173 SWIR, minus flagged bands)
- Spectral sampling: ~10 nm (varies slightly)
- FWHM: ~10-12 nm
- SNR: >200 for VNIR, >100 for SWIR
Examples¶
Basic usage::
from georeader.readers.prisma import PRISMA
# Load PRISMA image
prisma = PRISMA('/path/to/PRS_L1_STD_*.he5')
# Load specific wavelengths as reflectance
bands = prisma.load_wavelengths([850, 1600, 2200], as_reflectance=True)
# Load RGB composite
rgb = prisma.load_rgb(as_reflectance=True)
# Get georeferenced output (reprojected to UTM)
rgb_geo = prisma.load_rgb(as_reflectance=True, raw=False)
See Also¶
georeader.readers.emit : EMIT hyperspectral reader georeader.readers.enmap : EnMAP hyperspectral reader georeader.griddata : Utilities for gridding non-orthorectified data
References¶
- ASI PRISMA Mission: https://www.asi.it/en/earth-science/prisma/
- PRISMA User Guide: https://prisma.asi.it/
PRISMA
¶
Reader for PRISMA (PRecursore IperSpettrale della Missione Applicativa) hyperspectral images.
This class provides comprehensive functionality to read and manipulate PRISMA satellite imagery products from the Italian Space Agency (ASI). It handles the dual-sensor (VNIR + SWIR) data format, supporting operations like:
- Loading radiance or reflectance data at specific wavelengths
- Automatic handling of VNIR/SWIR sensor selection based on wavelength
- Converting radiance to reflectance using solar irradiance
- Georeferencing raw data to projected coordinate systems
PRISMA Data Model¶
PRISMA stores data in sensor coordinates with separate lat/lon arrays for geolocation. Unlike EMIT's GLT approach, PRISMA requires gridding/interpolation for orthorectification:
Sensor Grid (raw) Geographic Grid (output)
βββββββββββββββββββββββ βββββββββββββββββββββββ
β pushbroom scan β β regular grid β
β βββββ¬ββββ¬ββββ¬ββββ β gridding β βββββ¬ββββ¬ββββ¬ββββ β
β β a β b β c β d β β ββββββββ β β a'β b'β c'β d'β β
β βββββΌββββΌββββΌββββ€ β β βββββΌββββΌββββΌββββ€ β
β β e β f β g β h β β β β e'β f'β g'β h'β β
β βββββ΄ββββ΄ββββ΄ββββ β β βββββ΄ββββ΄ββββ΄ββββ β
β + lat/lon per pixelβ β + affine transform β
βββββββββββββββββββββββ βββββββββββββββββββββββ
Raw methods (raw=True) return sensor coordinates; georeferenced methods (raw=False) apply gridding to regular geographic coordinates.
Dual Sensor Architecture¶
PRISMA has separate VNIR and SWIR sensors with overlapping coverage:
Wavelength Range:
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
400nm 1000nm 2500nm
ββββββββββ VNIR βββββββββββ€
βββββββββββββββββββ SWIR ββββββββββββββββββββ€
ββ overlap ββ
920-1010nm
The class automatically selects the appropriate sensor based on requested wavelengths.
Attributes¶
filename : str Path to the PRISMA HE5 file. lats : np.ndarray Latitude values (H, W) for each pixel in sensor coordinates. lons : np.ndarray Longitude values (H, W) for each pixel in sensor coordinates. attributes_prisma : Dict Dictionary of PRISMA metadata attributes from HDF5 root. nbands_vnir : int Number of valid VNIR bands (excluding flagged bands). vnir_range : Tuple[float, float] Wavelength range (min, max) of VNIR sensor in nm. nbands_swir : int Number of valid SWIR bands (excluding flagged bands). swir_range : Tuple[float, float] Wavelength range (min, max) of SWIR sensor in nm. time_coverage_start : datetime UTC datetime of acquisition start. time_coverage_end : datetime UTC datetime of acquisition end. units : str Radiance units: 'mW/m2/sr/nm'. sza_swir : float Solar zenith angle (degrees) for SWIR sensor. sza_vnir : float Solar zenith angle (degrees) for VNIR sensor. vza_swir : float View zenith angle (degrees) for SWIR sensor. vza_vnir : float View zenith angle (degrees) for VNIR sensor.
Lazy-Loaded Attributes¶
ltoa_swir : np.ndarray
SWIR radiance data (H, W, B), loaded by load_raw(swir_flag=True).
ltoa_vnir : np.ndarray
VNIR radiance data (H, W, B), loaded by load_raw(swir_flag=False).
wavelength_swir : np.ndarray
SWIR wavelengths (H, B) - varies slightly across track.
wavelength_vnir : np.ndarray
VNIR wavelengths (H, B) - varies slightly across track.
fwhm_swir : np.ndarray
SWIR FWHM values (H, B) - varies slightly across track.
fwhm_vnir : np.ndarray
VNIR FWHM values (H, B) - varies slightly across track.
Examples¶
Basic loading::
>>> from georeader.readers.prisma import PRISMA
>>>
>>> prisma = PRISMA('/path/to/PRS_L1_STD_*.he5')
>>> print(prisma) # View metadata summary
>>> print(f"VNIR: {prisma.vnir_range}, SWIR: {prisma.swir_range}")
Loading specific wavelengths::
>>> # Load NDVI bands (Red at 665nm, NIR at 865nm)
>>> bands = prisma.load_wavelengths([665, 865], as_reflectance=True)
>>> print(bands.shape) # (2, H, W) in sensor coordinates
>>>
>>> # Load and georeference to UTM
>>> bands_geo = prisma.load_wavelengths([665, 865], as_reflectance=True,
... raw=False, resolution_dst=30)
>>> print(type(bands_geo)) # GeoTensor with transform and CRS
Loading RGB composite::
>>> # Raw sensor coordinates
>>> rgb_raw = prisma.load_rgb(as_reflectance=True, raw=True)
>>>
>>> # Georeferenced output
>>> rgb_geo = prisma.load_rgb(as_reflectance=True, raw=False)
>>> plt.imshow(np.clip(rgb_geo.values.transpose(1,2,0), 0, 0.3) / 0.3)
Working with raw data::
>>> # Load all SWIR bands
>>> prisma.load_raw(swir_flag=True)
>>> print(prisma.ltoa_swir.shape) # (H, W, ~173)
>>> print(prisma.wavelength_swir.shape) # (H, ~173) - wavelengths vary across track
>>>
>>> # Load all VNIR bands
>>> prisma.load_raw(swir_flag=False)
>>> print(prisma.ltoa_vnir.shape) # (H, W, ~66)
See Also¶
georeader.readers.emit.EMITImage : EMIT hyperspectral reader georeader.readers.enmap.EnMAP : EnMAP hyperspectral reader georeader.griddata : Gridding utilities for non-orthorectified data georeader.reflectance : Radiometric conversion utilities
References¶
- ASI PRISMA Mission: https://www.asi.it/en/earth-science/prisma/
- PRISMA Data Products: https://prisma.asi.it/
Source code in georeader/readers/prisma.py
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 | |
load_raw(swir_flag)
¶
Load the all the data from all the wavelengths for the VNIR or SWIR range.
This function caches the data, wavelegths and FWHM in the attributes of the class:
* ltoa_swir, wavelength_swir, fwhm_swir, vza_swir, sza_swir if swir_flag is True
* ltoa_vnir, wavelength_vnir, fwhm_vnir, vza_vnir, sza_vnir if swir_flag is False
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
swir_flag
|
bool
|
if True it will load the SWIR range, otherwise it will load the VNIR range |
required |
Returns:
| Name | Type | Description |
|---|---|---|
NDArray |
NDArray
|
3D array with the reflectance values (H, W, B) where N and M are the dimensions of the image and B is the number of bands. |
Source code in georeader/readers/prisma.py
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 | |
load_wavelengths(wavelengths, as_reflectance=True, raw=True, resolution_dst=30, dst_crs=None, fill_value_default=-1)
¶
Load the reflectance of the given wavelengths
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
wavelengths
|
Union[float, List[float], NDArray]
|
List of wavelengths to load |
required |
as_reflectance
|
bool
|
return the values as reflectance rather than radiance. Defaults to True.
If False values will have units of W/m^2/SR/um ( |
True
|
raw
|
bool
|
if True it will return the raw values, if False it will return the values reprojected to the specified CRS and resolution. Defaults to True. |
True
|
resolution_dst
|
int
|
if raw is False, it will reproject the values to this resolution. Defaults to 30. |
30
|
dst_crs
|
Optional[Any]
|
if None it will use the corresponding UTM zone. |
None
|
fill_value_default
|
float
|
fill value. Defaults to -1. |
-1
|
Returns:
| Type | Description |
|---|---|
Union[GeoTensor, NDArray]
|
Union[GeoTensor, NDArray]: if raw is True it will return a NDArray with the values, otherwise it will return a GeoTensor
with the reprojected values in its |
Source code in georeader/readers/prisma.py
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 | |
EMIT Reader¶
The EMIT (Earth Surface Mineral Dust Source Investigation) reader provides access to NASA's imaging spectrometer data from the International Space Station. This reader works with Level 1B calibrated radiance data (not atmospherically corrected).
Key features:
- Reading L1B hyperspectral radiance data from NetCDF4 format files
- Working with the 380-2500 nm spectral range with 7.4 nm sampling
- Irregular grid georeferencing through GLT (Geographic Lookup Table)
- Support for the observation geometry information (solar and viewing angles)
- Integration with L2A mask products for cloud and shadow detection
- Quality-aware analysis with cloud, cirrus, and spacecraft flag masks
- Conversion from radiance (ΞΌW/cmΒ²/sr/nm) to top-of-atmosphere reflectance
- Support for downloading data from NASA DAAC portals
- Automatic detection and use of appropriate UTM projection
Tutorial example:
API Reference¶
Module to read EMIT (Earth Surface Mineral Dust Source Investigation) hyperspectral images.
EMIT is a NASA imaging spectrometer aboard the International Space Station that measures reflected solar radiation from Earth's surface in 285 spectral bands from 380 to 2500 nm. This module provides tools to read, georeference, and process EMIT L1B radiance data.
Data Format Overview¶
EMIT data is distributed in NetCDF format with a unique storage layout:
Raw Data Structure (NetCDF file):
βββββββββββββββββββββββββββββββββββββββ
β radiance: (downtrack, crosstrack, bands) β
β βββ Shape: (~1280, ~1242, 285) β
β β
β location/glt_x: (rows, cols) β
β location/glt_y: (rows, cols) β
β βββ Geographic Lookup Table (GLT) β
βββββββββββββββββββββββββββββββββββββββ
The raw data is stored in sensor coordinates (pushbroom scan lines), NOT in geographic coordinates. The GLT provides a mapping from geographic (orthorectified) coordinates back to raw sensor coordinates.
GLT Orthorectification Process¶
The GLT (Geographic Lookup Table) is key to understanding EMIT data:
Geographic Grid (Output) Sensor Grid (Raw Data)
βββββββββββββββββββββββ βββββββββββββββββββββββ
β (0,0) β β radiance array β
β βββββ¬ββββ¬ββββ β GLT β βββββββββββββββββ β
β β a β b β c β β βββββββ β β (5,2) (5,3) β β
β βββββΌββββΌββββ€ β lookup β β (6,1) (6,2) β β
β β d β e β f β β β β ... β β
β βββββ΄ββββ΄ββββ β β βββββββββββββββββ β
β (H,W) β β β
βββββββββββββββββββββββ βββββββββββββββββββββββ
For pixel (row=1, col=2) in geographic grid:
glt_x[1,2] = 5 β raw_col = 5
glt_y[1,2] = 2 β raw_row = 2
value = radiance[2, 5, :] (all bands)
GLT values of 0 indicate invalid/no-data pixels
This approach allows: 1. Efficient storage (no wasted pixels from orthorectification padding) 2. Preservation of original radiometric values (no resampling) 3. Flexible reprojection to any target CRS
Radiometric Units¶
- L1B Radiance: ΞΌW/(cmΒ²Β·srΒ·nm) - microwatts per square centimeter per steradian per nanometer
- FWHM: Full Width at Half Maximum of spectral response in nm
- Wavelengths: Center wavelengths in nm (380-2500 nm range)
Key Classes and Functions¶
- EMITImage: Main class for reading and processing EMIT data
- download_product: Download EMIT products from NASA Earthdata
- get_radiance_link, get_obs_link: Generate download URLs
Requirements¶
Requires xarray: pip install xarray
Authentication for downloads requires NASA Earthdata credentials stored in:
~/.georeader/auth_emit.json with format: {"user": "...", "password": "..."}
Examples¶
Basic usage::
from georeader.readers.emit import EMITImage, download_product
# Download and open EMIT image
link = 'https://data.lpdaac.earthdatacloud.nasa.gov/...'
filepath = download_product(link)
emit = EMITImage(filepath)
# Reproject to UTM (recommended for analysis)
emit_utm = emit.to_crs("UTM")
# Load as reflectance (applies solar irradiance correction)
reflectance = emit_utm.load(as_reflectance=True)
# Load RGB composite
rgb = emit_utm.load_rgb(as_reflectance=True)
# Get cloud mask
cloud_mask = emit.validmask()
References¶
- NASA EMIT Mission: https://earth.jpl.nasa.gov/emit/
- EMIT Data Resources: https://github.com/nasa/EMIT-Data-Resources
- EMIT Utils: https://github.com/emit-sds/emit-utils/
- LP DAAC Data Access: https://lpdaac.usgs.gov/products/emitl1bradv001/
EMITImage
¶
Reader for EMIT L1B (Earth Surface Mineral Dust Source Investigation) hyperspectral images.
This class provides comprehensive functionality to read and manipulate EMIT satellite imagery products from NASA's imaging spectrometer aboard the ISS. It handles the unique GLT-based (Geographic Lookup Table) storage format, supporting operations like:
- Loading radiometry data with automatic orthorectification
- Converting radiance to reflectance using solar irradiance
- Accessing cloud and quality masks
- Extracting viewing and solar geometry angles
- Reprojecting to different coordinate reference systems
EMIT Data Model¶
EMIT stores data in sensor coordinates, not geographic coordinates. The GLT provides a lookup table mapping geographic pixels to sensor pixels:
GLT Orthorectification:
ββββββββββββββββββββββββββββββ ββββββββββββββββββββββββββββ
β Geographic Grid β β Sensor Grid (raw) β
β (orthorectified space) β β (pushbroom scan) β
β βββββ¬ββββ¬ββββ¬ββββ β β βββββ¬ββββ¬ββββ¬ββββ β
β β Β· β a β b β Β· β β GLT β β e β a β b β Β· β β
β βββββΌββββΌββββΌββββ€ β βββ β βββββΌββββΌββββΌββββ€ β
β β c β d β e β f β β β β f β c β d β Β· β β
β βββββ΄ββββ΄ββββ΄ββββ β β βββββ΄ββββ΄ββββ΄ββββ β
β (pixels with data) β β (original acquistion) β
ββββββββββββββββββββββββββββββ ββββββββββββββββββββββββββββ
Β· = no data (GLT value = 0)
For geographic pixel (row, col):
raw_x = glt_x[row, col]
raw_y = glt_y[row, col]
value = radiance[raw_y, raw_x, :]
This approach preserves original radiometric values without interpolation artifacts.
Spectral Characteristics¶
- Wavelength range: 380-2500 nm (VNIR + SWIR)
- Number of bands: 285
- Spectral sampling: ~7.4 nm
- Spatial resolution: 60m at nadir
Attributes¶
filename : str Path to the EMIT NetCDF file. nc_ds : xr.Dataset xarray Dataset handle for the main radiance file. glt : GeoTensor Geographic Lookup Table as a GeoTensor with shape (2, H, W). - glt.values[0]: x-indices into raw radiance (1-based) - glt.values[1]: y-indices into raw radiance (1-based) valid_glt : np.ndarray Boolean mask (H, W) indicating valid GLT entries (data coverage). glt_relative : GeoTensor GLT with indices relative to the data window (0-based). window_raw : rasterio.windows.Window Window defining the subset of raw data to read (optimizes I/O). real_transform : rasterio.Affine Affine transform for the orthorectified (geographic) grid. time_coverage_start : datetime UTC datetime of acquisition start. time_coverage_end : datetime UTC datetime of acquisition end. wavelengths : np.ndarray Center wavelengths (nm) for selected bands. fwhm : np.ndarray Full Width at Half Maximum (nm) for selected bands. band_selection : Union[int, Tuple[int, ...], slice] Current band subset selection. units : str Radiance units from file metadata (typically 'uW/(cm^2 sr nm)'). fill_value_default : float No-data value for radiance data. dims : Tuple[str] Dimension names ("band", "y", "x"). dtype : np.dtype Data type of radiance values.
Lazy-Loaded Properties¶
nc_ds_obs : xr.Dataset
Observation data (viewing/solar angles, path length, elevation).
Auto-downloaded from NASA Earthdata if not present locally.
nc_ds_l2amask : xr.Dataset
L2A quality mask data (clouds, cirrus, water, aggregate flags).
Auto-downloaded from NASA Earthdata if not present locally.
mean_sza : float
Mean solar zenith angle (degrees) across the scene.
mean_vza : float
Mean view zenith angle (degrees) across the scene.
observation_date_correction_factor : float
Earth-Sun distance correction factor for the acquisition date.
Examples¶
Basic loading and reprojection::
>>> from georeader.readers.emit import EMITImage, download_product
>>>
>>> # Download from NASA Earthdata
>>> link = 'https://data.lpdaac.earthdatacloud.nasa.gov/lp-prod-protected/...'
>>> filepath = download_product(link)
>>>
>>> # Open and reproject to UTM
>>> emit = EMITImage(filepath)
>>> emit_utm = emit.to_crs("UTM", resolution_dst_crs=60)
>>>
>>> # Load as reflectance
>>> refl = emit_utm.load(as_reflectance=True)
>>> print(refl.shape) # (285, H, W)
Working with specific wavelengths::
>>> # Select RGB-like bands (640, 550, 460 nm)
>>> emit.set_band_selection([35, 23, 11])
>>> print(emit.wavelengths) # [641.2, 553.1, 462.3]
>>> rgb = emit.load(as_reflectance=True)
>>>
>>> # Or use the convenience method
>>> rgb = emit.load_rgb(as_reflectance=True)
Accessing masks and quality data::
>>> # Get valid (cloud-free) mask
>>> valid_mask = emit.validmask()
>>> print(f"Clear pixels: {emit.percentage_clear:.1f}%")
>>>
>>> # Get specific mask layers
>>> cloud_mask = emit.mask("Cloud flag")
>>> water_mask = emit.water_mask()
Working with viewing geometry::
>>> # Get solar zenith angle
>>> sza = emit.sza() # GeoTensor with SZA values
>>>
>>> # Get mean angles for quick reference
>>> print(f"Mean SZA: {emit.mean_sza:.1f}Β°")
>>> print(f"Mean VZA: {emit.mean_vza:.1f}Β°")
Spatial subsetting::
>>> import rasterio.windows
>>>
>>> # Read a spatial window
>>> window = rasterio.windows.Window(col_off=100, row_off=200, width=500, height=500)
>>> emit_subset = emit.read_from_window(window)
>>> data = emit_subset.load()
See Also¶
georeader.readers.prisma.PRISMA : PRISMA hyperspectral reader georeader.readers.enmap.EnMAP : EnMAP hyperspectral reader georeader.reflectance : Radiometric conversion utilities
References¶
- EMIT L1B Product Guide: https://lpdaac.usgs.gov/products/emitl1bradv001/
- EMIT Data Resources: https://github.com/nasa/EMIT-Data-Resources
- EMIT Algorithms: Green et al. (2020) doi:10.1029/2020JD033451
Source code in georeader/readers/emit.py
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 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 | |
mask_bands
property
¶
Returns the mask bands -> ['Cloud flag', 'Cirrus flag', 'Water flag', 'Spacecraft Flag', 'Dilated Cloud Flag', 'AOD550', 'H2O (g cm-2)', 'Aggregate Flag']
mean_sza
property
¶
Return the mean solar zenith angle
mean_vza
property
¶
Return the mean view zenith angle
nc_ds_l2amask
property
¶
Loads the L2A mask file. In this file we have information about the cloud mask.
This function downloads the L2A mask file if it does not exist from the JPL portal.
It caches the L2A mask file in the object. (self.nc_ds_l2amask)
See https://lpdaac.usgs.gov/products/emitl2arflv001/ for info about the L2A mask file.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
l2amaskfile
|
Optional[str]
|
Path to the L2A mask file. Defaults to None. If none it will download the L2A mask file from the EMIT server. |
required |
nc_ds_obs
property
¶
Loads the observation file. In this file we have information about angles (solar and viewing), elevation and ilumination based on elevation and path length.
This function downloads the observation file if it does not exist from the JPL portal.
It caches the observation file in the object. (self.nc_ds_obs)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
obs_file
|
Optional[str]
|
Path to the observation file. Defaults to None. If none it will download the observation file from the EMIT server. |
required |
observation_bands
property
¶
Returns the observation bands
percentage_clear
property
¶
Return the percentage of clear pixels in the image
Returns:
| Name | Type | Description |
|---|---|---|
float |
float
|
percentage of clear pixels |
shape_raw
property
¶
Return the shape of the raw data in (C, H, W) format
clear_radiance_cache()
¶
Drop the cached radiance window if present.
After this call, the next load_raw() will re-read from disk. The
_cache dict object itself is not replaced β clones built via
__copy__ / read_from_bands / to_crs / read_from_window
share the same dict by reference, so clearing through any clone is
visible to all of them. Intended to be called from EmitProcessor.process
after all per-scene products are computed, to release the ~1.5 GB
radiance array before the next scene is processed.
Source code in georeader/readers/emit.py
1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 | |
footprint(crs=None)
¶
Get the footprint of the image in the given CRS. If no CRS is given, the footprint is returned in the native CRS. This function takes into account the valid_glt mask to compute the footprint.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
crs
|
Optional[str]
|
The CRS to return the footprint in. Defaults to None. If None, the footprint is returned in the native CRS. |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
Polygon |
Polygon
|
The footprint of the image in the given CRS. |
Source code in georeader/readers/emit.py
668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 | |
georreference(data, fill_value_default=None)
¶
Georreference an image in sensor coordinates to coordinates of the current georreferenced object. If you do some processing with the raw data, you can georreference the raw output with this function.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
array
|
raw data (C, H, W) or (H, W). |
required |
Returns:
| Name | Type | Description |
|---|---|---|
GeoTensor |
GeoTensor
|
georreferenced version of data (C, H', W') or (H', W') |
Example
emit_image = EMITImage("path/to/emit_image.nc") emit_image_rgb = emit_image.read_from_bands([35, 23, 11]) data_rgb = emit_image_rgb.load_raw() # (3, H, W) data_rgb_ortho = emit_image.georreference(data_rgb) # (3, H', W')
Source code in georeader/readers/emit.py
1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 | |
invalid_mask_raw(with_buffer=True)
¶
Returns the non georreferenced quality mask. True means that the pixel is not valid.
This mask is computed as the sum of the Cloud flag, Cirrus flag, Spacecraft flag and Dilated Cloud Flag. True means that the pixel is not valid.
From: https://github.com/nasa/EMIT-Data-Resources/blob/main/python/how-tos/How_to_use_EMIT_Quality_data.ipynb and https://github.com/nasa/EMIT-Data-Resources/blob/main/python/modules/emit_tools.py#L277
Source code in georeader/readers/emit.py
798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 | |
load_raw(transpose=True)
¶
Load the raw data, without orthorectification
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
transpose
|
bool
|
Transpose the data if it has 3 dimentsions to (C, H, W) Defaults to True. if False return (H, W, C) |
True
|
Returns:
| Type | Description |
|---|---|
array
|
np.array: raw data (C, H, W) or (H, W) |
Source code in georeader/readers/emit.py
1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 | |
mask(mask_name='cloud_mask')
¶
Return the mask layer with the given name. Mask shall be one of self.mask_bands -> ['Cloud flag', 'Cirrus flag', 'Water flag', 'Spacecraft Flag', 'Dilated Cloud Flag', 'AOD550', 'H2O (g cm-2)', 'Aggregate Flag']
Args: mask_name (str, optional): Name of the mask. Defaults to "cloud_mask".
Returns: GeoTensor: mask
Source code in georeader/readers/emit.py
833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 | |
observation(name)
¶
Returns the observation with the given name
Source code in georeader/readers/emit.py
861 862 863 864 865 866 867 868 | |
set_band_selection(band_selection=None)
¶
Set the band selection. Band selection is absolute w.r.t self.nc_ds['radiance']
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
band_selection
|
Optional[Union[int, Tuple[int, ...], slice]]
|
slicing or selection of the bands. Defaults to None. |
None
|
Example
emit_image.set_band_selection(slice(0, 3)) # will only load the three first bands emit_image.wavelengths # will only return the wavelengths of the three first bands emit_image.load() # will only load the three first bands
Source code in georeader/readers/emit.py
693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 | |
sza()
¶
Return the solar zenith angle as a GeoTensor
Source code in georeader/readers/emit.py
870 871 872 | |
to_crs(crs='UTM', resolution_dst_crs=60)
¶
Reproject the image to a new crs
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
crs
|
Any
|
CRS. |
'UTM'
|
Returns:
| Name | Type | Description |
|---|---|---|
EmitImage |
__class__
|
EMIT image in the new CRS |
Example
emit_image = EMITImage("path/to/emit_image.nc") emit_image_utm = emit_image.to_crs(crs="UTM")
Source code in georeader/readers/emit.py
927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 | |
validmask(with_buffer=True)
¶
Return the validmask mask
Returns:
| Name | Type | Description |
|---|---|---|
GeoTensor |
GeoTensor
|
bool mask. True means that the pixel is valid. |
Source code in georeader/readers/emit.py
784 785 786 787 788 789 790 791 792 793 794 795 796 | |
vza()
¶
Return the view zenith angle as a GeoTensor
Source code in georeader/readers/emit.py
874 875 876 | |
water_mask()
¶
Returns the water mask
Source code in georeader/readers/emit.py
851 852 853 | |
download_product(link_down, filename=None, display_progress_bar=True, auth=None)
¶
Download a product from the EMIT website (https://search.earthdata.nasa.gov/search). It requires that you have an account in the NASA Earthdata portal.
This code is based on this example: https://git.earthdata.nasa.gov/projects/LPDUR/repos/daac_data_download_python/browse
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
link_down
|
str
|
link to the product |
required |
filename
|
Optional[str]
|
filename to save the product |
None
|
display_progress_bar
|
bool
|
display tqdm progress bar |
True
|
auth
|
Optional[Tuple[str, str]]
|
tuple with user and password to download the product. If None, it will try to read the user and password from ~/.georeader/auth_emit.json |
None
|
Example
link_down = 'https://data.lpdaac.earthdatacloud.nasa.gov/lp-prod-protected/EMITL1BRAD.001/EMIT_L1B_RAD_001_20220828T051941_2224004_006/EMIT_L1B_RAD_001_20220828T051941_2224004_006.nc' filename = download_product(link_down)
Source code in georeader/readers/emit.py
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 | |
get_radiance_link(product_path)
¶
Get the link to download a product from the EMIT website. See: https://git.earthdata.nasa.gov/projects/LPDUR/repos/daac_data_download_python/browse
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
product_path
|
str
|
path to the product or filename of the product or product name with or without extension. e.g. 'EMIT_L1B_RAD_001_20220827T060753_2223904_013.nc' |
required |
Example
product_path = 'EMIT_L1B_RAD_001_20220827T060753_2223904_013.nc' link = get_radiance_link(product_path) 'https://data.lpdaac.earthdatacloud.nasa.gov/lp-prod-protected/EMITL1BRAD.001/EMIT_L1B_RAD_001_20220827T060753_2223904_013/EMIT_L1B_RAD_001_20220827T060753_2223904_013.nc'
Source code in georeader/readers/emit.py
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 | |
get_obs_link(product_path)
¶
Get the link to download a product from the EMIT website. See: https://git.earthdata.nasa.gov/projects/LPDUR/repos/daac_data_download_python/browse
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
product_path
|
str
|
path to the product or filename of the product with or without extension. e.g. 'EMIT_L1B_RAD_001_20220827T060753_2223904_013.nc' |
required |
Example
product_path = 'EMIT_L1B_RAD_001_20220827T060753_2223904_013.nc' link = get_radiance_link(product_path) 'https://data.lpdaac.earthdatacloud.nasa.gov/lp-prod-protected/EMITL1BRAD.001/EMIT_L1B_RAD_001_20220827T060753_2223904_013/EMIT_L1B_OBS_001_20220827T060753_2223904_013.nc'
Source code in georeader/readers/emit.py
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 | |
get_ch4enhancement_link(tile)
¶
Get the link to download a product from the EMIT website. See: https://git.earthdata.nasa.gov/projects/LPDUR/repos/daac_data_download_python/browse
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tile
|
str
|
path to the product or filename of the product with or without extension. e.g. 'EMIT_L1B_RAD_001_20220827T060753_2223904_013.nc' |
required |
Example
product_path = 'EMIT_L1B_RAD_001_20220827T060753_2223904_013.nc' link = get_radiance_link(product_path) 'https://data.lpdaac.earthdatacloud.nasa.gov/lp-prod-protected/EMITL2BCH4ENH.001/EMIT_L2B_CH4ENH_001_20220810T064957_2222205_033/EMIT_L2B_CH4ENH_001_20220810T064957_2222205_033.tif'
Source code in georeader/readers/emit.py
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 | |
get_l2amask_link(tile)
¶
Get the link to download a product from the EMIT website (https://search.earthdata.nasa.gov/search)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tile
|
str
|
path to the product or filename of the L1B product with or without extension. e.g. 'EMIT_L1B_RAD_001_20220827T060753_2223904_013.nc' |
required |
Returns:
| Name | Type | Description |
|---|---|---|
str |
str
|
link to the L2A mask product |
Example
tile = 'EMIT_L1B_RAD_001_20220827T060753_2223904_013.nc' link = get_l2amask_link(tile) 'https://data.lpdaac.earthdatacloud.nasa.gov/lp-prod-protected/EMITL2ARFL.001/EMIT_L2A_RFL_001_20220827T060753_2223904_013/EMIT_L2A_MASK_001_20220827T060753_2223904_013.nc'
Source code in georeader/readers/emit.py
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 | |
valid_mask(filename, with_buffer=False, dst_crs='UTM', resolution_dst_crs=60)
¶
Loads the valid mask from the EMIT L2AMASK file.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
filename
|
str
|
path to the L2AMASK file. e.g. EMIT_L2A_MASK_001_20220827T060753_2223904_013.nc |
required |
with_buffer
|
bool
|
If True, the buffer band is used to compute the valid mask. Defaults to False. |
False
|
Returns:
| Name | Type | Description |
|---|---|---|
GeoTensor |
Tuple[GeoTensor, float]
|
valid mask |
Source code in georeader/readers/emit.py
1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 | |
EnMAP Reader¶
The EnMAP (Environmental Mapping and Analysis Program) reader processes data from the German hyperspectral satellite mission. This reader works with Level 1B radiometrically calibrated data (not atmospherically corrected) that contains radiance values in physical units.
Key features:
- Reading L1B hyperspectral radiance data from GeoTIFF format with accompanying XML metadata
- Working with separate VNIR (420-1000 nm) and SWIR (900-2450 nm) spectral ranges
- Support for 228 spectral channels with 6.5 nm (VNIR) and 10 nm (SWIR) sampling
- Integration with Rational Polynomial Coefficients (RPCs) for accurate geometric correction
- Conversion from radiance (mW/mΒ²/sr/nm) to top-of-atmosphere reflectance
- Access to solar illumination and viewing geometry for radiometric calculations
- Support for quality masks
Tutorial example:
API Reference¶
Module to read EnMAP (Environmental Mapping and Analysis Program) hyperspectral images.
EnMAP is a German hyperspectral satellite mission operated by DLR (German Aerospace Center), launched in 2022. It provides high-spectral-resolution data in 224 bands from 420 to 2450 nm with a 30m spatial resolution and 30km swath width.
Data Format Overview¶
EnMAP data is distributed as separate GeoTIFF files with an XML metadata file:
EnMAP Product Structure:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β ENMAP01-____L1B-DT0000000000_20220501T101523Z_001_V010110_... β
β βββ *-METADATA.XML β Main metadata file (input) β
β βββ *-SPECTRAL_IMAGE_VNIR.TIF 420-1000 nm, ~88 bands β
β βββ *-SPECTRAL_IMAGE_SWIR.TIF 900-2450 nm, ~136 bands β
β βββ *-QL_QUALITY_CLOUD.TIF Cloud mask β
β βββ *-QL_QUALITY_CIRRUS.TIF Cirrus mask β
β βββ *-QL_QUALITY_SNOW.TIF Snow mask β
β βββ *-QL_QUALITY_HAZE.TIF Haze mask β
β βββ *-QL_PIXELMASK_*.TIF Per-sensor pixel masks β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Unlike EMIT and PRISMA, EnMAP L1B data is already orthorectified (map-projected) with Rational Polynomial Coefficients (RPCs) stored in the metadata for refined geolocation.
Dual-Sensor Architecture¶
EnMAP uses two pushbroom sensors with overlapping spectral coverage:
VNIR Detector SWIR Detector
ββββββββββββββββββββββ ββββββββββββββββββββββ
β 420 - 1000 nm β β 900 - 2450 nm β
β ~88 bands β β ~136 bands β
β 6.5 nm sampling β β 10 nm sampling β
β Si CCD β β HgCdTe β
ββββββββββββββββββββββ ββββββββββββββββββββββ
β β
βββββββββββββ Overlap ββββββββββββββββ
900-1000 nm
The spectral overlap enables cross-calibration between the two detectors.
Radiometric Processing¶
EnMAP L1B data requires conversion from Digital Numbers (DN) to radiance:
L_Ξ» = DN Γ GAIN + OFFSET [W/(mΒ²Β·srΒ·nm)]
Note: DLR provides gains between 2000-10000 (multiplicative, not divisive)
The reader applies: L = (GAIN Γ DN + OFFSET) Γ 1000 to get mW/(mΒ²Β·srΒ·nm)
Rational Polynomial Coefficients (RPCs)¶
EnMAP includes RPCs for precise geolocation refinement:
Pixel (col, row) βββ RPC Transform βββ Geographic (lon, lat)
RPCs model:
- Satellite orbit and attitude
- Sensor geometry
- Terrain elevation effects (when height_off is set appropriately)
The reader can apply RPCs during loading for refined geolocation.
Product Levels¶
- L1B: At-sensor radiance, sensor geometry
- L2A: Surface reflectance, atmospheric correction applied
This reader is designed for L1B products.
Examples¶
Basic usage::
from georeader.readers.enmap import EnMAP
# Load from metadata XML file
enmap = EnMAP('/path/to/*-METADATA.XML')
# Load specific wavelengths as reflectance
bands = enmap.load_wavelengths([665, 865, 1600], as_reflectance=True)
# Load RGB with RPC-refined geolocation
rgb = enmap.load_rgb(as_reflectance=True, apply_rpcs=True)
# Load quality masks
cloud_mask = enmap.load_product('QL_QUALITY_CLOUD')
See Also¶
georeader.readers.emit : EMIT hyperspectral reader georeader.readers.prisma : PRISMA hyperspectral reader georeader.rasterio_reader : Base reader for GeoTIFF files
References¶
- DLR EnMAP Mission: https://www.enmap.org/
- EnMAP Product Specification: https://www.enmap.org/data_access/
- GFZ enpt Package: https://github.com/GFZ/enpt (metadata parsing reference)
EnMAP
¶
Reader for EnMAP (Environmental Mapping and Analysis Program) hyperspectral images.
This class provides comprehensive functionality to read and manipulate EnMAP satellite imagery products from DLR. It handles the multi-file product structure (separate VNIR/SWIR GeoTIFFs with XML metadata), supporting operations like:
- Loading radiance or reflectance data at specific wavelengths
- Automatic handling of VNIR/SWIR sensor selection based on wavelength
- Converting DN to radiance using gain/offset from metadata
- Converting radiance to reflectance using solar irradiance
- Applying Rational Polynomial Coefficients (RPCs) for refined geolocation
- Loading quality masks (cloud, cirrus, snow, haze)
EnMAP Data Model¶
EnMAP L1B products are orthorectified (map-projected) GeoTIFFs with separate files for VNIR and SWIR bands:
File Structure:
ββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β METADATA.XML βββ wavelengths, FWHM, angles, β
β gain/offset, RPCs β
β β
β SPECTRAL_IMAGE_VNIR.TIF βββ (88, H, W) bands β
β SPECTRAL_IMAGE_SWIR.TIF βββ (136, H, W) bands β
β β
β QL_QUALITY_*.TIF βββ quality masks β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Radiometric Conversion¶
DN to radiance conversion is automatic::
L_Ξ» = (GAIN Γ DN + OFFSET) Γ 1000 [mW/(mΒ²Β·srΒ·nm)]
Note: DLR gains are multiplicative (not divisive as in some sensors)
Spectral Configuration¶
EnMAP has two detectors with overlapping coverage:
Wavelength: 420nm ββββ 1000nm ββββ 2450nm
βββ VNIR βββββ€
βββββ SWIR βββββββββββ€
β overlapβ
900-1000nm
- VNIR: Silicon CCD, ~88 bands, 6.5nm sampling, SNR >500:1
- SWIR: HgCdTe, ~136 bands, 10nm sampling, SNR >150:1
Attributes¶
xml_file : str Path to the EnMAP XML metadata file. by_folder : bool Whether files are organized by folder structure (alternative naming convention). swir_file : str Path to the SWIR GeoTIFF file (derived from xml_file). fs : fsspec.AbstractFileSystem Filesystem for file access (local or cloud storage). vnir : RasterioReader Reader for VNIR spectral image. swir : RasterioReader Reader for SWIR spectral image. wl_center : Dict[str, np.ndarray] Center wavelengths per sensor: {'vnir': [...], 'swir': [...]}. wl_fwhm : Dict[str, np.ndarray] FWHM per sensor: {'vnir': [...], 'swir': [...]}. gain_arr : Dict[str, np.ndarray] Radiometric gains per sensor for DNβradiance conversion. offs_arr : Dict[str, np.ndarray] Radiometric offsets per sensor for DNβradiance conversion. vnir_range : Tuple[float, float] VNIR wavelength range (min, max) including FWHM margins. swir_range : Tuple[float, float] SWIR wavelength range (min, max) including FWHM margins. hsf : float Mean ground elevation (m) from scene metadata. sza : float Solar zenith angle (degrees). saa : float Solar azimuth angle (degrees). vza : float View zenith angle (across-track off-nadir angle, degrees). vaa : float View azimuth angle (scene azimuth, degrees). rpcs_vnir : rasterio.rpc.RPC Rational Polynomial Coefficients for VNIR refined geolocation. rpcs_swir : rasterio.rpc.RPC Rational Polynomial Coefficients for SWIR refined geolocation. time_coverage_start : datetime UTC datetime of acquisition start. time_coverage_end : datetime UTC datetime of acquisition end. units : str Radiance units: 'mW/m2/sr/nm'.
Properties (from underlying readers)¶
shape : Tuple[int, int, int] Full shape (total_bands, height, width). transform : rasterio.Affine Affine geotransform from SWIR file. crs : rasterio.crs.CRS Coordinate reference system from SWIR file. bounds : Tuple[float, float, float, float] Geographic bounds (xmin, ymin, xmax, ymax). res : Tuple[float, float] Pixel resolution (x, y).
Examples¶
Basic loading::
>>> from georeader.readers.enmap import EnMAP
>>>
>>> enmap = EnMAP('/data/ENMAP01-...-METADATA.XML')
>>> print(enmap) # View metadata summary
Loading specific wavelengths::
>>> # Load NDVI bands as reflectance
>>> bands = enmap.load_wavelengths([665, 865], as_reflectance=True)
>>> print(bands.shape) # (2, H, W)
>>>
>>> # Compute NDVI
>>> red, nir = bands.values[0], bands.values[1]
>>> ndvi = (nir - red) / (nir + red + 1e-10)
Loading RGB with RPC refinement::
>>> # Apply RPCs for better geolocation (recommended)
>>> rgb = enmap.load_rgb(as_reflectance=True, apply_rpcs=True)
>>>
>>> # Without RPCs (uses original map projection)
>>> rgb = enmap.load_rgb(as_reflectance=True, apply_rpcs=False)
Loading quality masks::
>>> # Load cloud mask
>>> cloud = enmap.load_product('QL_QUALITY_CLOUD')
>>>
>>> # Available products:
>>> # 'QL_QUALITY_CLOUD', 'QL_QUALITY_CIRRUS', 'QL_QUALITY_SNOW',
>>> # 'QL_QUALITY_HAZE', 'QL_PIXELMASK_VNIR', 'QL_PIXELMASK_SWIR'
Spatial subsetting with window_focus::
>>> from rasterio.windows import Window
>>>
>>> # Focus on a specific region
>>> window = Window(col_off=100, row_off=200, width=500, height=500)
>>> enmap_subset = EnMAP('/path/to/METADATA.XML', window_focus=window)
Cloud storage access::
>>> import gcsfs
>>>
>>> fs = gcsfs.GCSFileSystem()
>>> enmap = EnMAP('gs://bucket/ENMAP-METADATA.XML', fs=fs)
See Also¶
georeader.readers.emit.EMITImage : EMIT hyperspectral reader georeader.readers.prisma.PRISMA : PRISMA hyperspectral reader georeader.rasterio_reader.RasterioReader : Base reader for GeoTIFF georeader.read.read_rpcs : Apply RPC transformations
References¶
- DLR EnMAP Mission: https://www.enmap.org/
- GFZ enpt Package: https://github.com/GFZ/enpt (metadata parser reference)
- EnMAP Product Specification Document
Source code in georeader/readers/enmap.py
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 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 | |
load_rgb(as_reflectance=True, apply_rpcs=True, dst_crs='EPSG:4326', resolution_dst_crs=None)
¶
Load RGB image from VNIR bands. Converts radiance to TOA reflectance if as_reflectance is True
otherwise it will return the radiance values in W/m^2/SR/um == mW/m2/sr/nm (self.units)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
as_reflectance
|
bool
|
Convert radiance to TOA reflectance. Defaults to True. |
True
|
apply_rpcs
|
bool
|
Apply RPCs to the image. Defaults to True. |
True
|
dst_crs
|
str
|
Destination CRS. Defaults to "EPSG:4326". |
'EPSG:4326'
|
resolution_dst_crs
|
Optional[Union[float, Tuple[float, float]]]
|
Resolution of the destination CRS. Defaults to None. |
None
|
Returns: GeoTensor: with the RGB image
Source code in georeader/readers/enmap.py
815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 | |
load_wavelengths(wavelengths, as_reflectance=True)
¶
Load the reflectance of the given wavelengths
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
wavelengths
|
Union[float, List[float], NDArray]
|
List of wavelengths to load |
required |
as_reflectance
|
bool
|
return the values as reflectance rather than radiance.
Defaults to True. If False values will have units of W/m^2/SR/um == mW/m2/sr/nm ( |
True
|
Returns:
| Type | Description |
|---|---|
Union[GeoTensor, NDArray]
|
Union[GeoTensor, NDArray]: GeoTensor with the values in reflectance or radiance units. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If any wavelength is outside the sensor's range. |
Source code in georeader/readers/enmap.py
718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 | |
Carbon Mapper Reader¶
The Carbon Mapper reader provides typed access to the Carbon Mapper STAC catalogue and plume API β atmospheric methane / carbon-dioxide retrievals from the Tanager-1, EMIT, AVIRIS, and GAO instruments. Carbon Mapper publishes:
- L2B scenes (per-pixel CH4 column-matched-filter, RGB, uncertainty, artifact-mask) addressed by
scene_idin thel2b-ch4-mfa-v3aSTAC collection. - L3A per-plume rasters (alpha-banded delineated plume mask) addressed by
plume_idin thel3acollection. - Source records β DBSCAN clusters of plumes detected at the same physical site, addressed by deterministic
source_name.
Key features:
- Token-aware HTTP client (
obtain_token,refresh_token,download_asset) with file-based persistence (CarbonMapperConfig). - Typed query layer (
CMTileItem,CMRawPlume,CMSource, exception hierarchy) β never returns raw dicts. - Lazy raster wrappers (
CMImageRaster,CMPlumeRaster) backed byRasterioReader.CMPlumeRaster.polygon()extracts the authoritative plume polygon from the L3Aplume_tifband-4 alpha mask β the upstream source of truth for plume geometry. - Cross-resolution helpers:
get_tile_for_plume,get_source_for_plume,list_tiles_for_source,list_plumes_for_tile.
Optional install: the reader is gated behind the [carbonmapper] extra to keep the base install minimal:
pip install 'georeader-spaceml[carbonmapper]'
This pulls in pydantic (for CMRawPlume) and requests (for the API client). Azure SDK is intentionally not included β downstream consumers can layer keyvault-backed token loading on top of CarbonMapperConfig.
API Reference¶
High-level typed queries over the Carbon Mapper REST + STAC APIs.
This module is the typed, cross-resolution layer that sits between the
raw HTTP wrappers in :mod:georeader.readers.carbonmapper.download and consumers
(the Phase 2 DailyMonitoringCM ETL, analyst notebooks, future
Partner-feed backfills).
Why this exists¶
:mod:download exposes ~16 low-level endpoint wrappers that return raw
JSON / pandas DataFrames. Every consumer otherwise has to:
- Pick the right endpoint (
/catalog/plume-csvvs/catalog/plumes/annotatedvs STACsearchβ all three have different schemas). - Parse the response into something usable.
- Stitch resources together by hand: plume β
scene_idviarsplit("-", 1)[0],scene_idβ STAC item, plume β source via/catalog/source/plume/name/{plume_id}.
This module lifts those patterns into:
- One function per logical question (not per HTTP endpoint).
- Typed return values (:class:
CMRawPlume, :class:CMTileItem, :class:CMSource) β never raw dicts. - Owned knowledge of the bbox-encoding (
data_model Β§2.1) andsource_namequery-suffix (data_model Β§2.2) quirks.
Failure modes¶
The exception hierarchy is part of the contract:
- :class:
CMPlumeNotFoundβget_plume404. - :class:
CMSourceNotFoundβget_source404. - :class:
CMSceneNotPublishedβget_tile/get_tile_for_plume404 (CM publishes L2B selectively βdata_model Β§5.2). The cross-resolution helper :func:get_tile_for_plumecatches this and returnsNone; the single-resource :func:get_tilere-raises so callers can choose to defer.
Examples¶
"What does CM know about this plume?":
from georeader.readers.carbonmapper.api_queries import get_plume_context plume, tile, source = get_plume_context(token, "tan20251212t185057c20s4001-E") plume.plume_id 'tan20251212t185057c20s4001-E' tile.scene_id if tile else None 'tan20251212t185057c20s4001' source.sector if source else None # may be None if unattributed '1B2'
"All tiles ever observing this chronic emitter":
from georeader.readers.carbonmapper.api_queries import list_tiles_for_source tiles = list_tiles_for_source(token, "CH4_1B2_100m_-104.17525_32.49125") {t.platform for t in tiles}
See also¶
georeader.readers.carbonmapper.download : raw HTTP / JSON wrappers. georeader.readers.carbonmapper.plume.CMRawPlume : typed plume model. georeader.readers.carbonmapper.source.CMSource : typed source model.
CMTileItem
dataclass
¶
Lightweight Carbon Mapper L2B STAC item β API-only, no DB binding.
The DB-bound counterpart is CarbonMapperTile (Phase 1). The
promotion direction (API β DB) lives on the DB side via
CarbonMapperTile.from_cm_tile_item(item, cm_provider=...); this
keeps :mod:api_queries free of any database imports.
Frozen so instances are hashable and safe to use as dict keys when
deduplicating scene_ids in cross-resolution queries.
Attributes¶
scene_id:
STAC item id β equivalent to plume_id.rsplit("-", 1)[0] for
plumes that came from this scene.
collection:
STAC collection id, e.g. "l2b-ch4-mfa-v3a".
datetime:
UTC-aware acquisition time parsed from
properties["datetime"].
platform:
properties["platform"] β "Tanager1", "EMIT", etc.
bbox:
(W, S, E, N) in WGS-84 decimal degrees.
geometry:
Shapely geometry (typically a Polygon) of the scene footprint.
asset_urls:
Mapping of asset name β href URL, e.g.
{"cmf": "https://.../cmf.tif", "rgb": ...}. The L2B CH4
collection consistently exposes cmf, rgb,
uncertainty, and artifact-mask.
properties:
Full properties mapping from the STAC item.
raw:
Original STAC item dict β useful for fields not yet exposed
on the dataclass.
Examples¶
from georeader.readers.carbonmapper.api_queries import CMTileItem tile = CMTileItem.from_stac_item({ ... "id": "tan20251212t185057c20s4001", ... "collection": "l2b-ch4-mfa-v3a", ... "properties": {"datetime": "2025-12-12T18:50:57Z", ... "platform": "Tanager1"}, ... "bbox": [-103.6, 31.4, -103.4, 31.6], ... "geometry": {"type": "Polygon", "coordinates": [ ... [[-103.6, 31.4], [-103.4, 31.4], ... [-103.4, 31.6], [-103.6, 31.6], [-103.6, 31.4]]]}, ... "assets": {"cmf": {"href": "https://cm/.../cmf.tif"}}, ... }) tile.scene_id, tile.platform ('tan20251212t185057c20s4001', 'Tanager1') tile.asset_urls["cmf"] 'https://cm/.../cmf.tif'
Source code in georeader/readers/carbonmapper/api_queries.py
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 | |
from_stac_item(item)
classmethod
¶
Build a :class:CMTileItem from a raw STAC item dict.
Tolerates both string and pre-parsed datetime values for
properties["datetime"] and falls back to utcnow if the
property is missing entirely.
Parameters¶
item:
STAC item dict (Feature shape) as returned by
:func:georeader.readers.carbonmapper.download.stac_get_item or
:func:georeader.readers.carbonmapper.download.stac_search.
Returns¶
CMTileItem
Raises¶
ValueError
If item["bbox"] is missing or not 4-length.
Source code in georeader/readers/carbonmapper/api_queries.py
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 | |
CMAPIError
¶
Bases: Exception
Base for everything raised by :mod:api_queries.
Catch this to handle any expected Carbon Mapper API miss in one
block. requests.HTTPError for non-404 statuses (e.g. 500, 429)
propagates unchanged β those are infra issues, not data issues.
Source code in georeader/readers/carbonmapper/api_queries.py
99 100 101 102 103 104 105 | |
CMPlumeNotFound
¶
Bases: CMAPIError
Raised by :func:get_plume when the plume is unknown to CM.
The unmodified plume_id is preserved on the instance for
logging.
Examples¶
try: ... get_plume(token, "tan-does-not-exist") # doctest: +SKIP ... except CMPlumeNotFound as exc: ... log.warning("missing plume", plume_id=exc.plume_id)
Source code in georeader/readers/carbonmapper/api_queries.py
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 | |
CMSceneNotPublished
¶
Bases: CMAPIError
Raised when STAC has no L2B item for a given scene_id.
Carbon Mapper publishes L2B selectively (data_model.md Β§5.2):
plumes can exist for scenes whose L2B raster has not been (or never
will be) released. The Phase 2 promotion path defers such plumes
rather than failing hard.
The :func:get_tile single-resource fetcher raises this so
callers can pick a strategy; the cross-resolution
:func:get_tile_for_plume catches it and returns None.
Source code in georeader/readers/carbonmapper/api_queries.py
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 | |
CMSourceNotFound
¶
Bases: CMAPIError
Raised by :func:get_source when the source name is unknown.
The (cleaned, query-suffix-stripped) source_name is preserved
on the instance.
Source code in georeader/readers/carbonmapper/api_queries.py
127 128 129 130 131 132 133 134 135 136 | |
get_tile(token, scene_id, *, collection=DEFAULT_L2B_COLLECTION)
¶
Fetch a single L2B STAC item by scene_id.
Wraps GET /stac/collections/{collection}/items/{scene_id}.
Parameters¶
token:
Bearer token (STAC item endpoints accept anonymous reads for
published items, but auth surfaces additional fields).
scene_id:
The L2B scene_id, equal to plume_id.rsplit("-", 1)[0] for
any plume that came from this scene.
collection:
STAC collection β defaults to :data:DEFAULT_L2B_COLLECTION
(CH4 matched-filter v3a). Override for CO2 or earlier versions.
Returns¶
CMTileItem
Raises¶
CMSceneNotPublished When the L2B item has not been published yet (HTTP 404). Re-raised β not caught β so callers can choose to defer.
Examples¶
tile = get_tile(token, "tan20251212t185057c20s4001") # doctest: +SKIP tile.platform, list(tile.asset_urls) ('Tanager1', ['cmf', 'rgb', 'uncertainty', 'artifact-mask'])
Source code in georeader/readers/carbonmapper/api_queries.py
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 | |
get_plume(token, plume_id)
¶
Fetch a single plume by its CM plume_id.
Wraps GET /catalog/plume/{id} and parses the result through
:class:CMRawPlume.
Parameters¶
token:
Carbon Mapper Bearer token. Required for non-public fields.
plume_id:
Either the colloquial name (e.g.
"tan20251212t185057c20s4001-E") or the UUID form.
Returns¶
CMRawPlume
Raises¶
CMPlumeNotFound When the API returns 404. requests.HTTPError For non-404 errors (5xx, 429, etc.).
Examples¶
plume = get_plume(token, "tan20251212t185057c20s4001-E") # doctest: +SKIP plume.plume_id, plume.gas ('tan20251212t185057c20s4001-E', 'CH4')
Source code in georeader/readers/carbonmapper/api_queries.py
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 | |
get_source(token, source_name)
¶
Fetch a single Carbon Mapper source by its canonical name.
Strips the source-name query-string suffix (?plume_gas=...)
automatically (data_model Β§2.2) β pass either the dirty or
clean form.
Parameters¶
token:
Bearer token.
source_name:
Canonical or query-suffixed source name, e.g.
"CH4_1B2_100m_-104.17525_32.49125" or
"CH4_1B2_100m_-104.17525_32.49125?plume_gas=CH4".
Returns¶
CMSource
Raises¶
CMSourceNotFound When the API returns 404.
Examples¶
src = get_source(token, "CH4_1B2_100m_-104.17525_32.49125") # doctest: +SKIP src.sector, src.plume_count ('1B2', 12)
Source code in georeader/readers/carbonmapper/api_queries.py
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 | |
get_tile_for_plume(token, plume_id, *, collection=DEFAULT_L2B_COLLECTION)
¶
Resolve a plume to its parent L2B STAC item.
Derives the parent scene_id via
plume_id.rsplit("-", 1)[0] and looks up the corresponding
STAC item.
Unlike :func:get_tile, this helper catches
:class:CMSceneNotPublished and returns None β appropriate for
consumers (Phase 2 ETL) that want to defer rather than error.
Parameters¶
token:
Bearer token.
plume_id:
Colloquial plume id (with the -{part} suffix).
collection:
STAC collection β defaults to :data:DEFAULT_L2B_COLLECTION.
Returns¶
CMTileItem | None
None when the L2B scene has not been published yet.
Examples¶
tile = get_tile_for_plume(token, "tan20251212t185057c20s4001-E") # doctest: +SKIP tile.scene_id if tile else "deferred" # doctest: +SKIP 'tan20251212t185057c20s4001'
Source code in georeader/readers/carbonmapper/api_queries.py
667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 | |
get_source_for_plume(token, plume_id)
¶
Resolve a plume to its attributed Carbon Mapper source.
Wraps /catalog/source/plume/name/{plume_id} β the by-name
endpoint, which returns the cleaned source_name (preferred over
the UUID-keyed sibling for colloquial plume_id strings).
Returns None when CM has not attributed the plume to a source
(HTTP 404). Other HTTP errors propagate.
Parameters¶
token: Bearer token. plume_id: Colloquial plume id.
Returns¶
CMSource | None
Examples¶
src = get_source_for_plume(token, "tan20251212t185057c20s4001-E") # doctest: +SKIP src.source_name if src else "unattributed" # doctest: +SKIP 'CH4_1B2_100m_-104.0_32.0'
Source code in georeader/readers/carbonmapper/api_queries.py
844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 | |
get_plume_context(token, plume_id)
¶
Single-call fetch of a plume plus its parent tile and source.
The most common notebook / ETL question is "give me everything CM knows about this plume". This helper batches the three independent REST/STAC calls behind a single name and surfaces the contracts as a typed tuple.
Failure modes are asymmetric:
- The plume itself must exist β
CMPlumeNotFoundpropagates. - Tile resolution returns
Nonewhen the scene has not been published to L2B (CMSceneNotPublishedcaught internally). - Source resolution returns
Nonewhen CM has not attributed the plume (404 caught internally).
Parameters¶
token: Bearer token. plume_id: Colloquial plume id.
Returns¶
(CMRawPlume, CMTileItem | None, CMSource | None)
Raises¶
CMPlumeNotFound When the plume itself is unknown.
Examples¶
Notebook exploration:
plume, tile, source = get_plume_context( # doctest: +SKIP ... token, "tan20251212t185057c20s4001-E", ... ) print(f"emission: {plume.emission_auto:.0f} kg/h") # doctest: +SKIP emission: 1240 kg/h if source: # doctest: +SKIP ... print(f"source {source.source_name} sector {source.sector}")
Source code in georeader/readers/carbonmapper/api_queries.py
911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 | |
list_tiles(token, *, bbox=None, datetime_min=None, datetime_max=None, collection=DEFAULT_L2B_COLLECTION, limit=1000)
¶
Materialised list of L2B STAC items matching filters.
Wraps /stac/search (comma-joined STAC bbox encoding).
Parameters¶
token:
Bearer token.
bbox:
(W, S, E, N) WGS-84 spatial filter.
datetime_min, datetime_max:
Optional UTC bounds.
collection:
STAC collection β defaults to :data:DEFAULT_L2B_COLLECTION.
limit:
Max items in this call.
Returns¶
list[CMTileItem]
Examples¶
tiles = list_tiles( # doctest: +SKIP ... token, bbox=(-104.5, 31.0, -101.5, 33.5), limit=10, ... ) {t.platform for t in tiles} # doctest: +SKIP
Source code in georeader/readers/carbonmapper/api_queries.py
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 | |
list_plumes(token, *, bbox=None, sectors=None, instruments=None, datetime_min=None, datetime_max=None, gas=Gas.CH4, limit=1000)
¶
Materialised list of plumes matching filters.
Wraps /catalog/plumes/annotated and converts each row into a
:class:CMRawPlume. The bbox is encoded as repeated keys (REST
style β see :func:georeader.readers.carbonmapper.download._rest_bbox_params).
Parameters¶
token:
Bearer token.
bbox:
(W, S, E, N) WGS-84 spatial filter.
sectors:
IPCC sector codes β e.g. ["1B2", "6A"].
instruments:
Instrument short codes β e.g. ["emi", "tan"] or
:class:Instrument members like [Instrument.EMIT, Instrument.TANAGER].
datetime_min, datetime_max:
Optional UTC bounds β combined into an RFC 3339 interval.
gas:
:data:Gas.CH4 (default). CH4-only for this PR;
Gas.CO2 lands in a follow-up. Typed as
Gas | Literal["CH4"] so plain string call-sites
(gas="CH4") continue to type-check.
limit:
Max rows returned in this call. The API caps at 1 000 per page.
Returns¶
list[CMRawPlume]
Examples¶
Permian methane plumes for Q1 2025 from EMIT and Tanager:
from datetime import datetime, timezone plumes = list_plumes( # doctest: +SKIP ... token, ... bbox=(-104.5, 31.0, -101.5, 33.5), ... instruments=["emi", "tan"], ... datetime_min=datetime(2025, 1, 1, tzinfo=timezone.utc), ... datetime_max=datetime(2025, 4, 1, tzinfo=timezone.utc), ... limit=500, ... ) sum(p.emission_auto or 0 for p in plumes) # doctest: +SKIP 412350.0
Source code in georeader/readers/carbonmapper/api_queries.py
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 | |
list_sources(token, *, bbox=None, sectors=None, gas=Gas.CH4)
¶
List Carbon Mapper sources matching filters.
Wraps the source listing endpoint (REST Catalog). Each item is
parsed via :meth:CMSource.from_geojson_feature, which strips the
source-name query-suffix.
Parameters¶
token:
Bearer token.
bbox:
(W, S, E, N) WGS-84 spatial filter (REST repeated-keys
encoding).
sectors:
IPCC sector codes.
gas:
:data:Gas.CH4 (default). CH4-only for this PR;
Gas.CO2 lands in a follow-up.
Returns¶
list[CMSource]
Examples¶
Top oil & gas sources in the Permian:
sources = list_sources( # doctest: +SKIP ... token, ... bbox=(-104.5, 31.0, -101.5, 33.5), ... sectors=["1B2"], ... ) sorted(sources, key=lambda s: -(s.emission_auto or 0))[:3] # doctest: +SKIP [
, , ]
Source code in georeader/readers/carbonmapper/api_queries.py
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 652 653 654 655 656 657 658 659 | |
list_plumes_for_tile(token, scene_id, *, gas=Gas.CH4)
¶
All plumes attributed to a given L2B scene.
Carbon Mapper plume_ids embed the scene_id β
plume_id = "{scene_id}-{part}" β so we filter the annotated
plumes listing client-side by prefix.
Parameters¶
token:
Bearer token.
scene_id:
L2B scene id, e.g. "tan20251212t185057c20s4001".
gas:
:data:Gas.CH4 (default). CH4-only for this PR;
Gas.CO2 lands in a follow-up.
Returns¶
list[CMRawPlume]
Note¶
The current implementation pulls a 1 000-plume page and filters
in Python. For high-volume scenes that may miss tail rows; pass a
bbox filter or use :func:list_plumes directly when completeness
matters.
Examples¶
plumes = list_plumes_for_tile( # doctest: +SKIP ... token, "tan20251212t185057c20s4001", ... ) [p.plume_id[-1] for p in plumes] # doctest: +SKIP ['A', 'B', 'C', 'E']
Source code in georeader/readers/carbonmapper/api_queries.py
964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 | |
list_plumes_for_source(token, source_name, *, limit=10000)
¶
All plumes attributed to a Carbon Mapper source.
Wraps /catalog/source-plumes-csv/{source_name}. The CSV
endpoint is single-shot (no pagination) β the result is fully
materialised.
Strips the ?... query suffix from source_name automatically
(data_model Β§2.2).
Parameters¶
token: Bearer token. source_name: Canonical or query-suffixed source name. limit: Cap the returned list. Defaults to 10 000 β CM sources rarely exceed a few hundred plumes, so this is just a safety cap.
Returns¶
list[CMRawPlume]
Examples¶
plumes = list_plumes_for_source( # doctest: +SKIP ... token, "CH4_1B2_100m_-104.17525_32.49125", ... ) len(plumes), plumes[0].plume_id[:3] # doctest: +SKIP (47, 'tan')
Source code in georeader/readers/carbonmapper/api_queries.py
1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 | |
list_tiles_for_source(token, source_name, *, collection=DEFAULT_L2B_COLLECTION)
¶
All distinct parent L2B tiles touched by a source's plumes.
Implementation:
- :func:
list_plumes_for_sourceβ every plume attributed to the source. {plume_id.rsplit("-", 1)[0] for ...}β distinct scene_ids.stac_search(ids=[...])β resolve to STAC items.
Useful for tile-level backfill: given a chronic emitter, fetch every L2B scene that ever observed it, regardless of whether plumes were detected on a given pass.
Parameters¶
token:
Bearer token.
source_name:
Canonical or query-suffixed source name.
collection:
STAC collection β defaults to :data:DEFAULT_L2B_COLLECTION.
Returns¶
list[CMTileItem] Empty list if the source has no plumes.
Examples¶
tiles = list_tiles_for_source( # doctest: +SKIP ... token, "CH4_1B2_100m_-104.17525_32.49125", ... ) sorted({t.platform for t in tiles}) # doctest: +SKIP ['EMIT', 'Tanager1']
Source code in georeader/readers/carbonmapper/api_queries.py
1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 | |
plume.py¶
Unified Pydantic model for Carbon Mapper plume records.
Handles payloads from both Carbon Mapper API formats:
- CSV bulk export (
/api/v1/catalog/plume-csv) β providesplume_latitude,plume_longitude,datetime,plume_bounds. - Annotated plume JSON (
/api/v1/catalog/plumes/annotated) β providesgeometry_json,scene_timestamp,validated,has_phme.
All fields except plume_id are optional so that the model can be
constructed from either format without validation errors.
CH4 only for this PR. The catalog model surface is gas-agnostic
(CMRawPlume.gas returns whatever the API gave us), but query
helpers in :mod:api_queries are typed Literal["CH4"] to keep
the supported-product surface explicit. CO2 lands in a follow-up.
Version timeline. Carbon Mapper bumps emission_version per
processing-software release. v3a is the canonical STAC-exposed
version family (in /stac/collections); v3c is the live
processing version of newer plumes β reachable via direct asset
URLs from /catalog/plume/{id} but not registered in STAC.
The :attr:CMRawPlume.version property exposes this so callers can
branch between STAC-item lookup (v3a) and URL-pattern derivation
(v3c) β see :class:~georeader.readers.carbonmapper.image.CMPlumeImage,
which handles both transparently.
This module is the API-side typed view of a Carbon Mapper plume
record. Downstream consumers (e.g. UNEP IMEO MARS) may persist the
record into their own tables / views; field-level docstrings below
mirror the column comments on the
src_carbon_mapper_plumes SQL view in pysat
(UNEP-IMEO-MARS/pysat <https://github.com/UNEP-IMEO-MARS/pysat>_),
so the upstream API and one downstream staging view share a single
source of truth.
CARBONMAPPER_INSTRUMENTS = {'emi': 'EMIT', 'tan': 'Tanager-1', 'ang': 'AVIRIS-NG', 'gao': 'Global Airborne Observatory', 'av3': 'AVIRIS-3'}
module-attribute
¶
CM_INSTRUMENT_TO_SATELLITE = {'tan': 'Tanager1', 'ang': 'AVIRISNG', 'av3': 'AVIRIS3', 'emi': 'EMIT', 'gao': 'GAO'}
module-attribute
¶
CMRawPlume
¶
Bases: BaseModel
Unified Carbon Mapper plume model.
Accepts payloads from both the CSV bulk-export endpoint and the
annotated plume JSON endpoint. Only plume_id is required β all
other fields default to None so either format can be parsed
without errors.
Geometry is built automatically from whichever source is available:
geometry_json(GeoJSON dict) β Point geometries are buffered by 0.001Β° to produce a small polygon.plume_bounds(bounding box) β converted to ashapely.box.
Note that geometry here is not the retrieved plume mask
polygon β it's just the API's reported point/bounds. For the
authoritative plume polygon, use
:meth:~georeader.readers.carbonmapper.rasters.CMPlumeRaster.polygon,
which extracts it from the L3A plume_tif band-4 alpha mask.
Downstream MARS staging-view counterpart¶
UNEP IMEO MARS persists this record into src_plume_staging_hist
and exposes it via the src_carbon_mapper_plumes view (defined
in pysat sql/view01_raw_carbon_mapper_plumes_view.sql
<https://github.com/UNEP-IMEO-MARS/pysat/blob/main/sql/view01_raw_carbon_mapper_plumes_view.sql>_).
Field-level docstrings below mirror that view's
COMMENT ON COLUMN statements.
Mapping reference (CMRawPlume field β SQL view column):
============================ =====================================
CMRawPlume field src_carbon_mapper_plumes column
============================ =====================================
plume_id source_id
datetime_str / tile_date
scene_timestamp
published_at_str published_at
modified_str modified
plume_latitude lat
plume_longitude lon
plume_bounds_raw plume_bounds
wind_source_auto wind_source
wind_speed_avg_auto wind_speed_m_s
wind_speed_std_auto wind_speed_std_m_s
wind_direction_avg_auto wind_direction_deg
wind_direction_std_auto wind_direction_std_deg
emission_auto emission_rate_kg_h
emission_uncertainty_auto emission_rate_uncertainty_kg_h
ipcc_sector sector
con_tif concentration_tif
rgb_tif, rgb_png same names
plume_tif, plume_png same names
============================ =====================================
Source code in georeader/readers/carbonmapper/plume.py
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 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 | |
instrument_name
property
¶
Human-readable instrument name from :data:CARBONMAPPER_INSTRUMENTS.
The lookup is case-insensitive β upstream payloads occasionally
report "GAO" while plume_id prefixes are lowercase, so
the table key is normalised at lookup time rather than relying
on every caller to lowercase first.
observation_datetime
property
¶
Parse observation time from datetime_str or scene_timestamp.
scene_id
property
¶
Parent L2B scene id, derived from plume_id.
Equivalent to plume_id.rsplit('-', 1)[0] β same string used
as the STAC item id in the l2b-ch4-mfa-v3a collection. Use
this to bridge from a plume to its parent scene without an HTTP
round-trip:
raw.scene_id # doctest: +SKIP 'tan20251212t185057c20s4001' tile = api_queries.get_tile(token, raw.scene_id) # doctest: +SKIP
Distinct from :attr:scene_uuid, which is the API's internal
UUID for the scene.
version
property
¶
Processing version ("v3a" / "v3b" / "v3c" / ...).
Re-exposes :attr:emission_version as a more obvious branch
point for STAC-vs-CDN access: v3a plumes are STAC-resident,
v3c plumes are reachable only via the URL-pattern derivation
in :class:~georeader.readers.carbonmapper.image.CMPlumeImage.
Returns None if the upstream payload didn't include
emission_version (older CSV exports).
wind_u
property
¶
Eastward wind component (m/s), meteorological convention.
wind_v
property
¶
Northward wind component (m/s), meteorological convention.
from_raw(raw)
classmethod
¶
Create from a JSON string or dict (CSV row or annotated-plume payload).
Source code in georeader/readers/carbonmapper/plume.py
953 954 955 956 957 958 | |
to_source_dict()
¶
Serialise to a dict suitable for round-tripping through :meth:from_raw.
Source code in georeader/readers/carbonmapper/plume.py
861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 | |
decompose_wind(speed, direction_deg)
¶
Convert wind speed + meteorological direction to (u, v) components.
Meteorological convention: 0Β° = wind from North, 90Β° = wind from East. Returns the eastward (u) and northward (v) wind vector components.
Source code in georeader/readers/carbonmapper/plume.py
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 | |
Typed model for a Carbon Mapper source (DBSCAN cluster of plumes).
A Carbon Mapper source groups all plumes detected at the same
geographic location into a persistent point-source record. Sources are
addressed by a deterministic name of the form
{gas}_{sector}_{footprint_m}m_{lon}_{lat} β e.g.
"CH4_1B2_100m_-104.17525_32.49125".
This module is the API-side typed view of a Carbon Mapper source. Downstream consumers may persist it into their own tables, but this package deliberately does not assume any particular DB schema.
Notable quirks handled here¶
/catalog/sources.geojsonfeatures sometimes returnsource_namewith a stray query-string fragment appended ("...?plume_gas=CH4&bbox=..."). :func:_strip_query_suffixremoves it; :meth:CMSource.from_geojson_featurecalls it always so callers never see the dirty form.- The endpoints return either a GeoJSON Feature (with
properties/geometry) or a flat dict; the higher-level :mod:georeader.readers.carbonmapper.api_queriesnormalises these before invoking :meth:from_geojson_feature.
CMSource
dataclass
¶
Typed view of a Carbon Mapper source (cluster of plumes).
Frozen β instances are immutable and hashable. The raw dict
captures the full upstream properties payload so consumers can
reach for fields not yet exposed on the dataclass without round-
tripping through the API.
Attributes¶
source_name:
Canonical name (no ?... suffix). Stable across CM API
revisions for the same physical site.
gas:
Gas species β typically "CH4" or "CO2".
sector:
IPCC sector code, e.g. "1B2" (Oil & Gas), "6A" (Solid
Waste), "1B1a" (Coal Mining).
point:
Centroid as a Shapely :class:shapely.geometry.Point in WGS-84.
plume_count:
Number of plumes Carbon Mapper has attributed to this source.
persistence:
Carbon Mapper's persistence metric (overpasses-with-detection /
total-overpasses), in [0, 1].
emission_auto:
Persistence-weighted average emission rate in kg/h. None
when CM has not produced an aggregate estimate.
emission_uncertainty_auto:
Companion uncertainty for emission_auto, in kg/h.
first_observation, last_observation:
Earliest and latest detection datetimes (UTC-aware).
raw:
Original properties mapping from the API response.
Examples¶
Parse from a /catalog/sources.geojson feature:
feature = { ... "properties": { ... "source_name": "CH4_1B2_100m_-104.17525_32.49125?plume_gas=CH4", ... "sector": "1B2", "gas": "CH4", ... "plume_count": 12, "persistence": 0.42, ... "emission_auto": 250.0, ... }, ... "geometry": {"type": "Point", ... "coordinates": [-104.17525, 32.49125]}, ... } src = CMSource.from_geojson_feature(feature) src.source_name # query suffix stripped 'CH4_1B2_100m_-104.17525_32.49125' src.point.x, src.point.y (-104.17525, 32.49125) src.plume_count, src.sector (12, '1B2')
Source code in georeader/readers/carbonmapper/source.py
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 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 | |
from_geojson_feature(feature)
classmethod
¶
Parse a /catalog/sources.geojson feature into a CMSource.
Always strips the source_name query-string suffix
(?plume_gas=...) β this is the canonical strip site, so
downstream code can treat CMSource.source_name as clean.
Parameters¶
feature:
GeoJSON Feature dict with at least "geometry" (Point)
and "properties" (with source_name and friends).
Returns¶
CMSource Typed source record with the suffix stripped.
Raises¶
ValueError
If feature["geometry"] does not carry a Point coordinate
pair.
Examples¶
feature = { ... "properties": {"source_name": "x?bbox=1", "sector": "1B2", ... "gas": "CH4", "plume_count": 1, ... "persistence": 0.5}, ... "geometry": {"type": "Point", "coordinates": [-100.0, 30.0]}, ... } CMSource.from_geojson_feature(feature).source_name 'x'
Source code in georeader/readers/carbonmapper/source.py
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 | |
Carbon Mapper L2B scene raster wrapper.
:class:CMImageRaster exposes every loadable L2B scene asset
(cmf / cmf-unortho / uncertainty /
uncertainty-unortho / artifact-mask / rgb / uas) as
lazy properties backed by :class:~georeader.rasterio_reader.RasterioReader
(or plain text for the uas.txt sidecar).
Per-plume L3A products (mask, concentrations, IME-clipped
concentrations, RGB, outline) live in
:mod:~georeader.readers.carbonmapper.image β
:class:~georeader.readers.carbonmapper.image.CMPlumeImage is the
counterpart to this class for plume-level data.
Intentionally NOT wrapped:
- PNG assets (
rgb_pngetc.) β un-georeferenced, not COGs. - Per-plume
con_tiffrom the catalog REST surface β duplicates the column-density crop already provided byCMPlumeImage.
Pure raster wrappers β no DB binding, no blob upload. The DB-bound
classes (CarbonMapperTile, CarbonMapperLocationImage) and the
analyst notebooks consume them.
CM_L2B_BANDS = ('cmf', 'cmf-unortho', 'uncertainty', 'uncertainty-unortho', 'artifact-mask', 'rgb')
module-attribute
¶
DEFAULT_L2B_RGB_COLLECTION = 'l2b-rgb-v3a'
module-attribute
¶
CMImageRaster
dataclass
¶
L2B scene exposed as four georeader-backed rasters.
Lazy: instantiating the dataclass does NOT issue HTTP / blob reads;
access .cmf / .rgb / etc. or call :meth:read_window /
:meth:read_polygon to trigger I/O.
Attributes:
| Name | Type | Description |
|---|---|---|
scene_id |
str
|
CM L2B item id (e.g. |
asset_paths |
Mapping[str, PathLike]
|
Mapping of band name β URL ( |
overview_level |
Optional[int]
|
Forwarded to |
Source code in georeader/readers/carbonmapper/rasters.py
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 | |
artifact_mask
cached
property
¶
Artefact mask (covers ~25% of scene). Flags un-orthorectified
strip pixels and geometric anomalies β not a cloud mask.
None if absent.
cmf
cached
property
¶
CH4 matched-filter retrieval, orthorectified (ppmΒ·m). Always present on L2B-CH4 items.
cmf_unortho
cached
property
¶
CH4 retrieval in raw sensor frame (pre-orthorectification).
None for older collection variants (e.g. mfm-v1) that
don't ship the unortho sibling.
rgb
cached
property
¶
3-band uint8 RGB. None for L2B-CH4 collections (RGB lives
in a separate STAC collection β fetch and pass via
asset_paths or compose via :meth:with_rgb).
uas
cached
property
¶
UAS sensor-metadata sidecar β raw text from uas.txt.
Lazy-fetched on first access (one HTTP GET if the path is a
URL, or a file read for local paths) and cached as a string.
Callers parse the structure as needed; we don't impose a
schema. Returns None if no uas URL/path was supplied.
Auth: rasterio's curl session is configured via the
GDAL_HTTP_HEADERS env var (set by the standard reader
bootstrap). We re-use that header here so a single
Authorization: Bearer <token> setup applies to every
L2B asset, raster or text alike.
uncertainty
cached
property
¶
Companion uncertainty raster aligned with cmf.
uncertainty_unortho
cached
property
¶
Per-pixel uncertainty in raw sensor frame. None for
older collection variants without the unortho sibling.
from_cm_tile_item(item)
classmethod
¶
Build from the lightweight STAC item (Phase 0.2).
STAC asset keys carry file extensions (cmf.tif,
uncertainty.tif, artifact-mask.tif, uas.txt,
*-unortho.tif variants). This method strips the
appropriate extension and retains every key listed in
:data:CM_L2B_BANDS plus uas (the text sidecar).
Source code in georeader/readers/carbonmapper/rasters.py
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 | |
from_local(scene_dir)
classmethod
¶
Build from a downloaded scene directory.
Picks up every L2B asset present (cmf.tif / rgb.tif /
uncertainty.tif / artifact-mask.tif and the
un-orthorectified variants), plus the uas.txt sidecar.
Missing files become absent keys in asset_paths.
Source code in georeader/readers/carbonmapper/rasters.py
402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 | |
from_scene_id(scene_id, *, token, l2b_collection_candidates=DEFAULT_L2B_CH4_COLLECTION_CANDIDATES, rgb_collection_candidates=DEFAULT_L2B_RGB_COLLECTION_CANDIDATES, with_rgb=True, overview_level=None, http_timeout=30.0)
classmethod
¶
Build by deriving L2B asset URLs from the scene_id (URL-pattern).
Bypasses STAC entirely β derives every asset URL by templating
against the verified asset-proxy pattern (see
:func:_l2b_asset_url) and probing the candidate collections
in order. Required for 2026 plumes (v3c/v3d L3A) whose L2B
parent scenes are not in /stac/collections.
Parameters¶
scene_id:
L2B scene id, equal to plume_id.rsplit("-", 1)[0] for
any plume that came from this scene. Must follow the
<inst><YYYYMMDD>t<HHMMSS>... convention so the date
can be parsed.
token:
Bearer token. Required β the asset-proxy URLs return 401
without it.
l2b_collection_candidates:
L2B CH4 collection IDs to probe, in order. First one to
serve a 200/206 on cmf.tif wins. Defaults to
:data:DEFAULT_L2B_CH4_COLLECTION_CANDIDATES β
("l2b-ch4-mfa-v3c", "l2b-ch4-mfa-v3a").
rgb_collection_candidates:
L2B RGB sibling collection IDs probed identically (on
rgb.tif). Defaults to
:data:DEFAULT_L2B_RGB_COLLECTION_CANDIDATES.
with_rgb:
When True (default), probe the RGB sibling collections
and attach the rgb URL on success. When False,
self.rgb will be None.
overview_level:
Forwarded to :class:RasterioReader.
http_timeout:
Per-probe range-GET timeout (seconds).
Returns¶
CMImageRaster
With asset_paths populated for the 6 L2B CH4 assets
(cmf, cmf-unortho, uncertainty,
uncertainty-unortho, artifact-mask, uas) and,
when with_rgb=True, the rgb sibling URL.
Raises¶
CMSceneNotPublished
When every candidate L2B collection 404s for scene_id
β the scene either hasn't been processed yet or only
exists in a collection variant not listed in
l2b_collection_candidates. Catch in ETL paths that
want to defer rather than error.
ValueError
When scene_id doesn't carry an 8-digit date at
positions [3:11].
Examples¶
tile = CMImageRaster.from_scene_id( # doctest: +SKIP ... "tan20260331t181625c77s4001", token=tok, ... ) tile.cmf # doctest: +SKIP
Source code in georeader/readers/carbonmapper/rasters.py
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 | |
read_polygon(polygon, *, crs_polygon='EPSG:4326', bands=CM_L2B_BANDS)
¶
Read a polygon clip from the requested bands.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
polygon
|
BaseGeometry
|
Clip geometry. |
required |
crs_polygon
|
str
|
CRS of |
'EPSG:4326'
|
bands
|
Iterable[str]
|
Subset of band names. Bands whose asset is missing
or whose window has zero overlap return |
CM_L2B_BANDS
|
Returns:
| Name | Type | Description |
|---|---|---|
dict[str, Optional[GeoData]]
|
|
|
dict[str, Optional[GeoData]]
|
|
|
dict[str, Optional[GeoData]]
|
class: |
|
as |
dict[str, Optional[GeoData]]
|
class: |
Source code in georeader/readers/carbonmapper/rasters.py
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 | |
read_window(bounds_4326, *, bands=CM_L2B_BANDS)
¶
Read a WGS-84 bbox window from the requested bands.
Source code in georeader/readers/carbonmapper/rasters.py
567 568 569 570 571 572 573 574 | |
read_window_to_crs(bounds_4326, crs_dst, *, bands=CM_L2B_BANDS)
¶
Read a window then reproject each band to crs_dst.
Reprojection materialises the data β values are
:class:GeoTensor, not lazy readers.
Source code in georeader/readers/carbonmapper/rasters.py
576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 | |
with_rgb(rgb_item)
¶
Return a copy with rgb merged in from a sibling STAC item.
The CH4 (l2b-ch4-mfa-v3a) and RGB (l2b-rgb-v3a) L2B
collections share scene_id and pixel grid, but each STAC
item only exposes its own assets. Fetch both with
:func:api_queries.get_tile (passing collection=...) and
compose them via this method:
ir = CMImageRaster.from_cm_tile_item(ch4_item) ir = ir.with_rgb(rgb_item) ir.rgb is not None True
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Source code in georeader/readers/carbonmapper/rasters.py
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 | |
Rasterize Carbon Mapper sources (point clusters) onto a target grid.
Carbon Mapper sources are point geometries (DBSCAN-clustered plume locations). For training labels, QA overlays, and source-prior features it is useful to project them onto the same grid as an L2B scene as a binary mask. This module provides:
- :func:
rasterize_sourcesβ one-shot function: list of points β :class:~georeader.geotensor.GeoTensormask. - :class:
CMSourceRasterβ lazy wrapper that mirrors :class:~georeader.readers.carbonmapper.rasters.CMImageRastershape (read_polygon/read_window/read_window_to_crs) so callers can compose the source mask with the L2B rasters.
Both delegate the actual burn-in to
:func:georeader.rasterize.rasterize_geopandas_like /
:func:~georeader.rasterize.rasterize_from_geopandas β no custom
rasterio.features call lives in this module.
The Carbon Mapper API does not publish a sources raster β these helpers
build it client-side from :func:list_sources (or any iterable of
:class:~georeader.readers.carbonmapper.source.CMSource).
CMSourceRaster
dataclass
¶
Lazy binary-mask raster of Carbon Mapper sources on a target grid.
Mirrors the read-helper surface of
:class:~georeader.readers.carbonmapper.rasters.CMImageRaster so
callers can compose source masks with L2B reads.
Attributes¶
sources:
Source points to rasterize.
transform, shape, crs:
Target grid spec. Use :meth:from_cmtileitem or
:meth:from_geodata to inherit the spec from an existing
raster.
buffer_m:
Per-point disk radius in metres. 0 β single pixel.
Source code in georeader/readers/carbonmapper/sources_raster.py
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 | |
from_cmtileitem(sources, tile, *, buffer_m=0.0)
classmethod
¶
Build a source raster aligned to an L2B :class:CMTileItem.
Resolves the tile's cmf GeoTIFF header to inherit
(transform, shape, crs). Issues one HEAD/GET-range read.
Source code in georeader/readers/carbonmapper/sources_raster.py
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 | |
from_geodata(sources, template, *, buffer_m=0.0)
classmethod
¶
Build a source raster aligned to an existing :class:GeoData.
Source code in georeader/readers/carbonmapper/sources_raster.py
235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 | |
load()
¶
Rasterize all sources onto the full grid.
Source code in georeader/readers/carbonmapper/sources_raster.py
281 282 283 284 285 286 287 288 289 | |
read_polygon(polygon, *, crs_polygon='EPSG:4326')
¶
Read a polygon clip of the source mask.
Source code in georeader/readers/carbonmapper/sources_raster.py
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 | |
read_window(bounds_4326)
¶
Read a WGS-84 bbox window of the source mask.
Source code in georeader/readers/carbonmapper/sources_raster.py
313 314 315 | |
read_window_to_crs(bounds_4326, crs_dst)
¶
Read a window then reproject the mask to crs_dst.
Source code in georeader/readers/carbonmapper/sources_raster.py
317 318 319 320 321 322 323 324 | |
rasterize_sources(sources, *, transform, shape, crs, buffer_m=0.0)
¶
Rasterize source points onto a target grid as a binary mask.
Each source contributes a value of 1 at its pixel; if
buffer_m > 0 a disk of that radius (in metres) is stamped
instead. Sources falling outside the grid are silently dropped.
Delegates to
:func:georeader.rasterize.rasterize_from_geopandas.
Parameters¶
sources:
Iterable of :class:CMSource, Shapely :class:Point, or
(lon, lat) tuples β all interpreted as WGS-84 lon/lat.
transform:
Affine transform of the target grid.
shape:
(height, width) of the target grid.
crs:
CRS of the target grid. Must be projected when
buffer_m > 0.
buffer_m:
Buffer radius in metres applied around each source point.
0 (default) β all_touched single-pixel stamp per source.
Returns¶
GeoTensor
2D mask of shape with values in {0, 1}.
Raises¶
ValueError
If buffer_m > 0 and crs is geographic, or if shape
is not 2D.
Source code in georeader/readers/carbonmapper/sources_raster.py
87 88 89 90 91 92 93 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 | |
rasterize_sources_like(sources, data_like, *, buffer_m=0.0)
¶
Rasterize sources onto an existing :class:GeoData grid.
Thin wrapper around
:func:georeader.rasterize.rasterize_geopandas_like.
Source code in georeader/readers/carbonmapper/sources_raster.py
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 | |
config.py¶
Lightweight credentials and configuration handler for the Carbon Mapper Data Platform API.
Credentials can be supplied in three ways (checked in priority order):
- Environment variables β set
CARBONMAPPER_TOKEN(access token),CARBONMAPPER_EMAILandCARBONMAPPER_PASSWORD(login credentials). - Config file β a JSON file at one of the well-known paths listed in
:data:
CONFIG_SEARCH_PATHS, or a custom path passed to :meth:CarbonMapperConfig.load. The canonical location matches the sibling readers (emit.py/S2_SAFE_reader.py):~/.georeader/auth_carbonmapper.json. - Explicit arguments β pass
token=directly to API functions indownload.py.
If no config file exists when :meth:CarbonMapperConfig.load is called
without an explicit path and no env-var credentials are set, a
placeholder ~/.georeader/auth_carbonmapper.json is auto-created
with stub values so users have a clear edit target.
Quick start¶
from georeader.readers.carbonmapper.config import CarbonMapperConfig cfg = CarbonMapperConfig.load() token = cfg.get_token() # resolves from env var or file
β or β store credentials in the default config file:¶
cfg.email = "user@example.com" cfg.password = "s3cret" cfg.save() # writes to ~/.georeader/auth_carbonmapper.json
References¶
- API docs : https://api.carbonmapper.org/api/v1/docs
- Registration : https://data.carbonmapper.org
CarbonMapperConfig
¶
Simple credentials and configuration container for the Carbon Mapper API.
Attributes¶
token:
A pre-obtained JWT bearer token. If set, it takes precedence over
email / password when :meth:get_token is called.
email:
Registered Carbon Mapper account e-mail address.
password:
Account password. Stored only in memory or in the config file on
disk β never sent anywhere except the token endpoint.
extra:
Any additional key/value pairs loaded from or saved to the config
file (for forward compatibility).
Examples¶
Load from environment or disk and retrieve a usable token:
cfg = CarbonMapperConfig.load() token = cfg.get_token() # may return None if no credentials found if token: ... data = get_plumes_annotated(plume_gas="CH4", token=token)
Persist credentials to the default config file:
cfg = CarbonMapperConfig(email="user@example.com", password="s3cret") cfg.save()
Reset (delete) the stored config file:
CarbonMapperConfig.reset()
Source code in georeader/readers/carbonmapper/config.py
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 | |
from_env()
classmethod
¶
Build a :class:CarbonMapperConfig purely from environment variables.
Reads :envvar:CARBONMAPPER_TOKEN, :envvar:CARBONMAPPER_EMAIL,
and :envvar:CARBONMAPPER_PASSWORD. Fields that are absent from
the environment are left as None.
Returns¶
CarbonMapperConfig A new config object populated from the environment.
Examples¶
import os os.environ["CARBONMAPPER_TOKEN"] = "eyJ..." cfg = CarbonMapperConfig.from_env() cfg.token 'eyJ...'
Source code in georeader/readers/carbonmapper/config.py
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 | |
from_file(path)
classmethod
¶
Load a :class:CarbonMapperConfig from a specific JSON file.
Parameters¶
path:
Path to a JSON config file containing any combination of the
keys "token", "email", "password", plus any extra
fields.
Returns¶
CarbonMapperConfig Config populated from the file.
Raises¶
FileNotFoundError If path does not exist. json.JSONDecodeError If the file cannot be parsed as JSON.
Examples¶
cfg = CarbonMapperConfig.from_file("~/.georeader/auth_carbonmapper.json")
Source code in georeader/readers/carbonmapper/config.py
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 | |
get_token()
¶
Return the best available bearer token.
If :attr:token is set, it is returned directly. Otherwise
None is returned β callers that need a fresh token should call
:meth:refresh_access_token or
:func:~georeader.readers.carbonmapper.download.obtain_token
with :attr:email and :attr:password.
Returns¶
str or None
A JWT bearer token string, or None if none is configured.
Examples¶
cfg = CarbonMapperConfig.load() token = cfg.get_token() if token is None: ... token = cfg.refresh_access_token()
Source code in georeader/readers/carbonmapper/config.py
444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 | |
has_credentials()
¶
Return True if any usable credentials are present.
A config is considered to have credentials when at least one of the
following is set: :attr:token, or both :attr:email and
:attr:password.
Examples¶
cfg = CarbonMapperConfig(email="u@example.com", password="pw") cfg.has_credentials() True CarbonMapperConfig().has_credentials() False
Source code in georeader/readers/carbonmapper/config.py
505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 | |
load(path=None, *, create_placeholder=True)
classmethod
¶
Load config using the standard resolution order.
Resolution order
~~~~~~~~~~~~~~~~
1. If path is given, load that file.
2. Otherwise search :data:CONFIG_SEARCH_PATHS for the first file
that exists.
3. Overlay environment variables β env values overwrite file values.
4. If still nothing is configured (no file found, no env vars set)
AND create_placeholder is True, write a stub config to
:data:DEFAULT_SAVE_PATH with SET-EMAIL / SET-PASSWORD
placeholders so users have a clear edit target. Matches the
emit.py / S2_SAFE_reader.py behaviour.
Parameters¶
path:
Optional explicit path to a config file. Skips the search
when provided.
create_placeholder:
When True (default), auto-create a stub config file at
:data:DEFAULT_SAVE_PATH if no credentials could be
resolved. Set to False in tests / non-interactive
contexts to keep the filesystem untouched.
Returns¶
CarbonMapperConfig
The resolved config. Fields without a value (from file and
env) are None.
Examples¶
cfg = CarbonMapperConfig.load() print(cfg.email) # None if not configured
cfg = CarbonMapperConfig.load("~/my_project/.carbonmapper.json")
Source code in georeader/readers/carbonmapper/config.py
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 | |
refresh_access_token()
¶
Obtain a fresh JWT access token using stored email/password.
Calls :func:~georeader.readers.carbonmapper.download.obtain_token with the
stored :attr:email and :attr:password, updates :attr:token
in-place, and returns the new access token.
Returns¶
str The new JWT access token.
Raises¶
ValueError If email or password is not set. requests.HTTPError If the Carbon Mapper API rejects the credentials.
Examples¶
cfg = CarbonMapperConfig.load() # ~/.georeader/auth_carbonmapper.json token = cfg.refresh_access_token()
Source code in georeader/readers/carbonmapper/config.py
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 | |
reset(path=None)
classmethod
¶
Delete the stored config file, if it exists.
Parameters¶
path:
Path to the config file to remove. Defaults to
:data:DEFAULT_SAVE_PATH
(~/.georeader/auth_carbonmapper.json).
Examples¶
CarbonMapperConfig.reset() # removes ~/.georeader/auth_carbonmapper.json
Source code in georeader/readers/carbonmapper/config.py
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 | |
save(path=None)
¶
Persist the config to a JSON file.
Parameters¶
path:
Destination file path. Defaults to
:data:DEFAULT_SAVE_PATH
(~/.georeader/auth_carbonmapper.json), matching the
sibling-reader convention (emit, S2). User-level location
outside the working tree so credentials are never
accidentally committed.
Returns¶
Path The resolved path of the file that was written.
Examples¶
cfg = CarbonMapperConfig(email="user@example.com", password="s3cret") saved_path = cfg.save() print(saved_path) /home/user/.georeader/auth_carbonmapper.json
Source code in georeader/readers/carbonmapper/config.py
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 | |
download.py¶
Carbon Mapper Data Platform API client for the marsml pipeline.
Provides typed wrappers around three Carbon Mapper APIs:
1. **REST Catalog API** β plumes, sources, scenes, plume CSV, assets
2. **STAC API** β spatiotemporal search across collections
3. **Asset Download** β GeoTIFF retrievals, RGB imagery, plume PNGs
Authentication¶
Most read endpoints work without a token, but some (scenes, related
plumes, STAC tokens) require a Bearer token. Use
:func:obtain_token or
:meth:~georeader.readers.carbonmapper.config.CarbonMapperConfig.refresh_access_token
to obtain one from credentials in ~/.georeader/auth_carbonmapper.json.
References¶
- API Docs : https://api.carbonmapper.org/api/v1/docs
- STAC Root : https://api.carbonmapper.org/api/v1/stac/
- Registration : https://data.carbonmapper.org
obtain_token(email, password)
¶
Exchange credentials for a JWT access/refresh token pair.
Parameters¶
email: Registered Carbon Mapper account e-mail address. password: Account password.
Returns¶
dict A mapping with at least two keys:
- ``"access"`` β short-lived JWT bearer token (use in API calls).
- ``"refresh"`` β long-lived refresh token (use with :func:`refresh_token`).
Examples¶
tokens = obtain_token("user@example.com", "s3cret") access_token = tokens["access"] data = get_plumes_annotated(plume_gas="CH4", limit=5, token=access_token)
Source code in georeader/readers/carbonmapper/download.py
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 | |
refresh_token(refresh)
¶
Refresh an expired access token using a refresh token.
Parameters¶
refresh:
The "refresh" value previously returned by :func:obtain_token.
Returns¶
dict
A mapping with a new "access" token (and optionally a new
"refresh" token if the server rotates them).
Examples¶
tokens = obtain_token("user@example.com", "s3cret") new_tokens = refresh_token(tokens["refresh"]) access_token = new_tokens["access"]
Source code in georeader/readers/carbonmapper/download.py
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 | |
download_asset(asset_key, dest, token=None)
¶
Download a raster asset (GeoTIFF or PNG) by its storage key.
Parameters¶
asset_key:
The path portion after /catalog/asset/. For example::
l2b-ch4-mf-v1/2016/10/08/ang20161008t211637/ang20161008t211637_l2b-ch4-mf-v1_cmf.tif
Asset keys are available in STAC item ``assets[name]["href"]``
entries and can be derived from the plume ``plume_tif`` /
``con_tif`` / ``rgb_tif`` URLs.
dest: Local file path where the asset will be written. Parent directories are created automatically. token: Optional Bearer token for authenticated access.
Returns¶
Path The resolved path of the downloaded file.
Examples¶
download_asset( ... "l2b-ch4-mf-v1/2016/10/08/ang20161008t211637/ang20161008t211637_l2b-ch4-mf-v1_cmf.tif", ... dest="./retrieval.tif", ... )
.. note::
For plume dicts returned by :func:get_plumes_annotated, prefer
:func:download_plume_assets which handles all assets at once.
Source code in georeader/readers/carbonmapper/download.py
647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 | |
download_plume_assets(plume, dest_dir)
¶
Download all available raster assets for a single plume.
Given a plume dict returned by :func:get_plumes_annotated, download
every non-null asset URL into dest_dir and return a mapping of asset
type to local file path.
Parameters¶
plume:
A single plume dict as returned in
get_plumes_annotated()["items"]. The function inspects the
"plume_png", "plume_tif", "con_tif", "rgb_png",
"rgb_tif", and "plume_rgb_png" keys for download URLs.
dest_dir:
Directory into which assets are downloaded. Created automatically
if it does not already exist.
Returns¶
dict[str, Path]
Mapping of asset type β local :class:~pathlib.Path for each
successfully downloaded asset. Assets that are missing (null)
or whose download fails are omitted. Example::
{
"plume_png": Path("./plumes/emi20240420t101448p07050-A_plume.png"),
"plume_tif": Path("./plumes/emi20240420t101448p07050-A_plume.tif"),
"con_tif": Path("./plumes/emi20240420t101448p07050-A_con.tif"),
"rgb_png": Path("./plumes/emi20240420t101448p07050-A_rgb.png"),
}
Examples¶
.. code-block:: python
result = get_plumes_annotated(plume_gas="CH4", limit=1, qualities=["good"])
plume = result["items"][0]
downloaded = download_plume_assets(plume, "./plume_data/")
for asset_type, path in downloaded.items():
print(asset_type, "β", path)
Source code in georeader/readers/carbonmapper/download.py
695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 | |
stac_search(*, collections=None, bbox=None, datetime_range=None, ids=None, limit=10, token=None)
¶
Cross-collection STAC item search.
Searches across one or more STAC collections using spatial and temporal filters and returns matching items as a GeoJSON FeatureCollection.
Parameters¶
collections:
List of STAC collection IDs to search. If None, all
collections are searched. Example:
["l2b-ch4-mfa-v3", "l4a-combined-ch4-v3a"].
bbox:
Bounding-box spatial filter as
(west_lon, south_lat, east_lon, north_lat) in WGS 84.
datetime_range:
RFC 3339 time interval string, e.g.
"2024-01-01T00:00:00Z/2024-06-01T00:00:00Z".
limit:
Maximum number of items to return.
token:
Optional Bearer token for authenticated requests.
Returns¶
dict A GeoJSON FeatureCollection mapping. Key fields:
- ``"type"`` β ``"FeatureCollection"``.
- ``"features"`` β list of STAC item GeoJSON Features. Each
Feature has:
- ``"id"`` β item ID.
- ``"geometry"`` β GeoJSON geometry of the scene footprint.
- ``"properties"`` β item metadata (datetime, collection, etc.).
- ``"assets"`` β dict of named assets, each with an
``"href"`` download URL and media type.
- ``"context"`` β pagination info (``matched``, ``returned``).
Examples¶
Search CH4 retrievals in the Permian Basin:
result = stac_search( ... collections=["l2b-ch4-mfa-v3"], ... bbox=(-104.5, 31.0, -101.5, 33.5), ... datetime_range="2024-01-01T00:00:00Z/2024-06-01T00:00:00Z", ... limit=5, ... ) for feat in result["features"]: ... print(feat["id"], list(feat["assets"].keys()))
Search across multiple collections simultaneously:
result = stac_search( ... collections=["l4a-combined-ch4-v3a", "l2b-rgb-v3a"], ... bbox=(-104.5, 31.0, -101.5, 33.5), ... limit=3, ... )
Source code in georeader/readers/carbonmapper/download.py
1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 | |
stac_get_items(collection_id, *, limit=10, bbox=None, datetime_range=None, token=None)
¶
Get items from a STAC collection (OGC API Features compliant).
Parameters¶
collection_id:
Identifier of the STAC collection to query, e.g.
"l4a-combined-ch4-v3a" or "l2b-rgb-v3a".
limit:
Maximum number of items to return.
bbox:
Bounding-box spatial filter as
(west_lon, south_lat, east_lon, north_lat) in WGS 84.
datetime_range:
RFC 3339 time interval string.
token:
Optional Bearer token for authenticated requests.
Returns¶
dict
A GeoJSON FeatureCollection. Each Feature has "assets"
containing download links for GeoTIFFs, PNGs, and other raster
products, with "href" and media-type annotations.
Examples¶
items = stac_get_items("l4a-combined-ch4-v3a", limit=5) for feat in items["features"]: ... print(feat["id"], feat.get("properties", {}).get("datetime"))
Source code in georeader/readers/carbonmapper/download.py
811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 | |