rsplot 0.3.1.dev3__tar.gz → 0.3.2.dev4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. {rsplot-0.3.1.dev3 → rsplot-0.3.2.dev4}/PKG-INFO +215 -1
  2. rsplot-0.3.2.dev4/README.md +463 -0
  3. {rsplot-0.3.1.dev3 → rsplot-0.3.2.dev4}/pyproject.toml +1 -0
  4. {rsplot-0.3.1.dev3 → rsplot-0.3.2.dev4}/src/rsplot/cli.py +48 -10
  5. {rsplot-0.3.1.dev3 → rsplot-0.3.2.dev4}/src/rsplot/commands/common.py +64 -1
  6. {rsplot-0.3.1.dev3 → rsplot-0.3.2.dev4}/src/rsplot/commands/forecast.py +24 -5
  7. {rsplot-0.3.1.dev3 → rsplot-0.3.2.dev4}/src/rsplot/commands/overlay.py +172 -43
  8. {rsplot-0.3.1.dev3 → rsplot-0.3.2.dev4}/src/rsplot/commands/raster.py +168 -44
  9. {rsplot-0.3.1.dev3 → rsplot-0.3.2.dev4}/src/rsplot/commands/recent.py +29 -7
  10. {rsplot-0.3.1.dev3 → rsplot-0.3.2.dev4}/src/rsplot/commands/reconstruct.py +27 -5
  11. {rsplot-0.3.1.dev3 → rsplot-0.3.2.dev4}/src/rsplot/config.py +3 -0
  12. {rsplot-0.3.1.dev3 → rsplot-0.3.2.dev4}/src/rsplot/geo/boundaries.py +105 -2
  13. rsplot-0.3.2.dev4/src/rsplot/geo/footprints.py +202 -0
  14. rsplot-0.3.2.dev4/src/rsplot/geo/processing.py +202 -0
  15. {rsplot-0.3.1.dev3 → rsplot-0.3.2.dev4}/src/rsplot/plotting/map_frame.py +120 -4
  16. {rsplot-0.3.1.dev3 → rsplot-0.3.2.dev4}/src/rsplot/plotting/overlay.py +5 -0
  17. {rsplot-0.3.1.dev3 → rsplot-0.3.2.dev4}/src/rsplot/plotting/raster.py +5 -0
  18. {rsplot-0.3.1.dev3 → rsplot-0.3.2.dev4}/src/rsplot/readers/__init__.py +38 -4
  19. {rsplot-0.3.1.dev3 → rsplot-0.3.2.dev4}/src/rsplot/readers/base.py +16 -1
  20. rsplot-0.3.2.dev4/src/rsplot/readers/gems.py +426 -0
  21. {rsplot-0.3.1.dev3 → rsplot-0.3.2.dev4}/src/rsplot/recent.py +59 -16
  22. {rsplot-0.3.1.dev3 → rsplot-0.3.2.dev4}/src/rsplot/result_builders/common.py +81 -0
  23. {rsplot-0.3.1.dev3 → rsplot-0.3.2.dev4}/src/rsplot/result_builders/forecast.py +7 -0
  24. {rsplot-0.3.1.dev3 → rsplot-0.3.2.dev4}/src/rsplot/result_builders/overlay.py +32 -2
  25. {rsplot-0.3.1.dev3 → rsplot-0.3.2.dev4}/src/rsplot/result_builders/raster.py +32 -0
  26. {rsplot-0.3.1.dev3 → rsplot-0.3.2.dev4}/src/rsplot/result_builders/reconstruct.py +7 -0
  27. {rsplot-0.3.1.dev3 → rsplot-0.3.2.dev4}/src/rsplot/temporal.py +139 -16
  28. rsplot-0.3.1.dev3/README.md +0 -250
  29. rsplot-0.3.1.dev3/src/rsplot/geo/processing.py +0 -120
  30. {rsplot-0.3.1.dev3 → rsplot-0.3.2.dev4}/.gitignore +0 -0
  31. {rsplot-0.3.1.dev3 → rsplot-0.3.2.dev4}/LICENSE +0 -0
  32. {rsplot-0.3.1.dev3 → rsplot-0.3.2.dev4}/src/rsplot/__init__.py +0 -0
  33. {rsplot-0.3.1.dev3 → rsplot-0.3.2.dev4}/src/rsplot/__main__.py +0 -0
  34. {rsplot-0.3.1.dev3 → rsplot-0.3.2.dev4}/src/rsplot/commands/__init__.py +0 -0
  35. {rsplot-0.3.1.dev3 → rsplot-0.3.2.dev4}/src/rsplot/commands/fnr.py +0 -0
  36. {rsplot-0.3.1.dev3 → rsplot-0.3.2.dev4}/src/rsplot/commands/meteo.py +0 -0
  37. {rsplot-0.3.1.dev3 → rsplot-0.3.2.dev4}/src/rsplot/commands/station.py +0 -0
  38. {rsplot-0.3.1.dev3 → rsplot-0.3.2.dev4}/src/rsplot/commands/station_trend.py +0 -0
  39. {rsplot-0.3.1.dev3 → rsplot-0.3.2.dev4}/src/rsplot/fnr.py +0 -0
  40. {rsplot-0.3.1.dev3 → rsplot-0.3.2.dev4}/src/rsplot/geo/__init__.py +0 -0
  41. {rsplot-0.3.1.dev3 → rsplot-0.3.2.dev4}/src/rsplot/geo/gridding.py +0 -0
  42. {rsplot-0.3.1.dev3 → rsplot-0.3.2.dev4}/src/rsplot/plotting/__init__.py +0 -0
  43. {rsplot-0.3.1.dev3 → rsplot-0.3.2.dev4}/src/rsplot/plotting/colormaps.py +0 -0
  44. {rsplot-0.3.1.dev3 → rsplot-0.3.2.dev4}/src/rsplot/plotting/fnr.py +0 -0
  45. {rsplot-0.3.1.dev3 → rsplot-0.3.2.dev4}/src/rsplot/plotting/meteo.py +0 -0
  46. {rsplot-0.3.1.dev3 → rsplot-0.3.2.dev4}/src/rsplot/plotting/station.py +0 -0
  47. {rsplot-0.3.1.dev3 → rsplot-0.3.2.dev4}/src/rsplot/plotting/station_trend.py +0 -0
  48. {rsplot-0.3.1.dev3 → rsplot-0.3.2.dev4}/src/rsplot/plotting/styles.py +0 -0
  49. {rsplot-0.3.1.dev3 → rsplot-0.3.2.dev4}/src/rsplot/plotting/wind.py +0 -0
  50. {rsplot-0.3.1.dev3 → rsplot-0.3.2.dev4}/src/rsplot/readers/_tropomi.py +0 -0
  51. {rsplot-0.3.1.dev3 → rsplot-0.3.2.dev4}/src/rsplot/readers/era5.py +0 -0
  52. {rsplot-0.3.1.dev3 → rsplot-0.3.2.dev4}/src/rsplot/readers/era5_wind.py +0 -0
  53. {rsplot-0.3.1.dev3 → rsplot-0.3.2.dev4}/src/rsplot/readers/forecast.py +0 -0
  54. {rsplot-0.3.1.dev3 → rsplot-0.3.2.dev4}/src/rsplot/readers/guokong.py +0 -0
  55. {rsplot-0.3.1.dev3 → rsplot-0.3.2.dev4}/src/rsplot/readers/reconstruct.py +0 -0
  56. {rsplot-0.3.1.dev3 → rsplot-0.3.2.dev4}/src/rsplot/readers/tropomi_hcho.py +0 -0
  57. {rsplot-0.3.1.dev3 → rsplot-0.3.2.dev4}/src/rsplot/readers/tropomi_no2.py +0 -0
  58. {rsplot-0.3.1.dev3 → rsplot-0.3.2.dev4}/src/rsplot/readers/tropomi_o3.py +0 -0
  59. {rsplot-0.3.1.dev3 → rsplot-0.3.2.dev4}/src/rsplot/readers/tropomi_o3pr.py +0 -0
  60. {rsplot-0.3.1.dev3 → rsplot-0.3.2.dev4}/src/rsplot/result_builders/__init__.py +0 -0
  61. {rsplot-0.3.1.dev3 → rsplot-0.3.2.dev4}/src/rsplot/result_builders/fnr.py +0 -0
  62. {rsplot-0.3.1.dev3 → rsplot-0.3.2.dev4}/src/rsplot/result_builders/meteo.py +0 -0
  63. {rsplot-0.3.1.dev3 → rsplot-0.3.2.dev4}/src/rsplot/result_builders/station.py +0 -0
  64. {rsplot-0.3.1.dev3 → rsplot-0.3.2.dev4}/src/rsplot/result_builders/station_trend.py +0 -0
  65. {rsplot-0.3.1.dev3 → rsplot-0.3.2.dev4}/src/rsplot/results.py +0 -0
  66. {rsplot-0.3.1.dev3 → rsplot-0.3.2.dev4}/src/rsplot/stats.py +0 -0
  67. {rsplot-0.3.1.dev3 → rsplot-0.3.2.dev4}/src/rsplot/tiles/__init__.py +0 -0
  68. {rsplot-0.3.1.dev3 → rsplot-0.3.2.dev4}/src/rsplot/tiles/tianditu.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: rsplot
3
- Version: 0.3.1.dev3
3
+ Version: 0.3.2.dev4
4
4
  Summary: CLI tool for plotting Remote Sensing data, designed for Agent.
5
5
  Project-URL: Repository, https://git.lug.ustc.edu.cn/yaoyhu/rsplot
6
6
  Project-URL: GitHub, https://github.com/yaoyhu/rsplot
@@ -217,6 +217,7 @@ Requires-Dist: geopandas>=0.13
217
217
  Requires-Dist: matplotlib>=3.7
218
218
  Requires-Dist: netcdf4>=1.7.3
219
219
  Requires-Dist: numpy>=1.24
220
+ Requires-Dist: pyproj>=3.1
220
221
  Requires-Dist: rich>=13.0.0
221
222
  Requires-Dist: scipy>=1.10
222
223
  Requires-Dist: shapely>=2.0
@@ -436,6 +437,219 @@ writes a JSON sidecar with the same base name. Reconstructed-grid JSON records
436
437
  both the China-time input and matched UTC archive hour, source file, native and
437
438
  output resolution, regional statistics, administrative summaries, and hotspots.
438
439
 
440
+ ## Select multiple regions without configuration
441
+
442
+ `raster` and `overlay` accept a quoted, comma-separated region argument.
443
+ Use English or Chinese commas; surrounding whitespace and embedded line
444
+ breaks are ignored in a list. Province, city, and qualified county/township
445
+ names can be mixed. County and township names still require their city,
446
+ e.g. `中山市/东区街道`. Other commands retain their single-region input.
447
+
448
+ ```bash
449
+ # One map covering three selected townships.
450
+ uv run rsplot raster \
451
+ "中山市/东区街道,中山市/南区街道,中山市/石岐街道" 20260907 \
452
+ --sensor gems -p o3pr --cf 1 --res 0.075 --smooth 0 \
453
+ --basemap none -o ./logs/zhongshan/three_streets_raster.png
454
+
455
+ # The same daily satellite field with observations at 19:00 China time.
456
+ uv run rsplot overlay \
457
+ "中山市/东区街道,中山市/南区街道,中山市/石岐街道" 20260907 \
458
+ --sensor gems -p o3pr --station-datetime 2026090719 \
459
+ --cf 1 --res 0.075 --smooth 0 --basemap none \
460
+ -o ./logs/zhongshan/three_streets_overlay.png
461
+ ```
462
+
463
+ These examples use your existing product/station data paths. Optional
464
+ `--data-dir` for raster or `--raster-dir` for overlay overrides the satellite
465
+ root; no named region group needs to be added to configuration.
466
+
467
+ Every member is resolved before reading data. Repeated aliases are removed
468
+ in first-occurrence order; empty items and unknown names fail with an explicit
469
+ error instead of silently omitting regions. The map reads and processes the
470
+ combined extent once, but colors only the union of the selected geometries,
471
+ preserving holes and gaps between disconnected regions. Each member retains
472
+ its boundary and label. Defaults use the coarsest selected level; combined
473
+ maps use automatic geographic ticks. Explicit plot options still take
474
+ precedence. Far-apart selections share one map with intervening whitespace.
475
+
476
+ Combined maps use `联合区域(N个)` as their default name. The JSON adds
477
+ `region.members` with canonical qualified names, administrative levels and
478
+ extents. Existing overall statistics operate on the geometric union, without
479
+ double-counting parent/child overlaps. `member_stats` summarizes each selected
480
+ member using that same statistical grid; for overlay these lists are under
481
+ `raster.member_stats` and `station.member_stats`. A member with no valid data
482
+ remains in the list with zero valid counts and null numeric summaries.
483
+ Members may overlap, so their counts must not be added to recover the union
484
+ count. Grid centers exactly on a member boundary are excluded from that
485
+ member's statistics, even if they lie inside the combined region. Station
486
+ selection retains the existing boundary-tolerance rule. `spatial_summary`
487
+ continues to describe subdivisions, independently of selected-member stats.
488
+ Single-region inputs retain their existing output contract.
489
+
490
+ Run the isolated multi-region regressions:
491
+
492
+ ```bash
493
+ uv run python -m unittest discover -s tests -p 'test_multi_regions.py' -v
494
+ ```
495
+
496
+ ## Raster boundaries and regional statistics
497
+
498
+ `raster`, `overlay`, `forecast` (including station overlays), and
499
+ `reconstruct` prepare separate display and statistics grids:
500
+
501
+ - **Display:** retain valid grid cells that intersect the administrative
502
+ geometry, even when their centers are outside. Clip the colored layer to
503
+ the actual boundary, including holes and disconnected islands. Display
504
+ smoothing uses surrounding values but preserves the input valid-cell mask.
505
+ - **Statistics:** select grid centers **strictly inside** the geometry;
506
+ centers exactly on the boundary are excluded. Each valid selected cell has
507
+ equal weight. Coverage is the valid selected-cell count divided by the
508
+ total selected-cell count, not the percentage of colored land area.
509
+ Existing product-specific gap-filling and statistical smoothing behavior
510
+ remains unchanged; GEMS statistics remain unfilled and unsmoothed.
511
+
512
+ The PNG sidecar adds `boundary_policy` with the selection and weighting
513
+ rules, the displayed intersecting-cell count, and the count whose centers
514
+ are outside. For `overlay`, this block is nested under `raster`.
515
+ A small region can display overlapping cells while having no interior grid
516
+ centers: its statistics then report zero valid cells and null summaries.
517
+ This change does not increase the satellite's native resolution or fill
518
+ missing observations.
519
+
520
+ Run boundary-selection and actual rendering regressions (including holes
521
+ and both PlateCarree and Mercator projections):
522
+
523
+ ```bash
524
+ uv run python -m unittest discover -s tests -p 'test_boundary_display.py' -v
525
+ ```
526
+
527
+ ## GEMS satellite products
528
+
529
+ Use `--sensor gems` with `raster`, `recent`, and `overlay`. Omitting
530
+ `--sensor` retains TROPOMI behavior. GEMS FNR and satellite trend curves are
531
+ not included in this release.
532
+
533
+ Configure the three archive roots in `[paths.data_dirs]`:
534
+
535
+ ```toml
536
+ gems_no2 = "/path/to/GEMS/NO2"
537
+ gems_hcho = "/path/to/GEMS/HCHO"
538
+ gems_o3 = "/path/to/GEMS/O3"
539
+ ```
540
+
541
+ Alternatively set `RSPLOT_DATA_DIR_GEMS_NO2`, `RSPLOT_DATA_DIR_GEMS_HCHO`,
542
+ and `RSPLOT_DATA_DIR_GEMS_O3`. Each root contains `YYYYMM/DD/*.nc`.
543
+ `--data-dir` (`--raster-dir` for overlay) overrides the configured root.
544
+
545
+ | Product | GEMS variable | Display unit |
546
+ | --- | --- | --- |
547
+ | `no2` | `Data Fields/ColumnAmountNO2Trop` | 10¹⁵ molecules/cm² |
548
+ | `hcho` | `Data Fields/ColumnAmount` | 10¹⁵ molecules/cm² |
549
+ | `o3` | `product/total_ozone_column` | DU |
550
+ | `o3pr` | `product/troposphere_ozone_column` | DU |
551
+
552
+ Both ozone quantities use the O3P files in `gems_o3`; `o3pr` reads the
553
+ provided tropospheric column without reintegrating the profile. Neither
554
+ quantity represents surface ozone concentration.
555
+
556
+ GEMS dates and scan requests use **Asia/Shanghai**. `YYYYMMDDHHMM` selects
557
+ an exact nominal scan; `YYYYMMDD` computes the day's valid-scan mean.
558
+ For example, `202609010845` selects the `20260901_0045` UTC archive scan.
559
+ The filename is the nominal scan time, not a simultaneous observation time
560
+ for every pixel: GEMS scans begin at :45 UTC and take about 30 minutes
561
+ ([NESC operation schedule](https://nesc.nier.go.kr/en/html/satellite/operation.do)).
562
+ Missing scan requests report available local times instead of selecting a
563
+ replacement. Duplicate files for one nominal scan are rejected; use a root
564
+ containing one archive version.
565
+
566
+ ```bash
567
+ # Exact scan, daily mean, and multi-day mean.
568
+ rsplot raster 安徽 202609011145 --sensor gems -p no2 \
569
+ --basemap none --output /tmp/gems_no2_scan.png
570
+ rsplot raster 安徽 20260901 --sensor gems -p hcho \
571
+ --basemap none --output /tmp/gems_hcho_daily.png
572
+ rsplot raster 安徽 20260901-20260902 --sensor gems -p o3pr \
573
+ --basemap none --output /tmp/gems_o3pr_window.png
574
+
575
+ # Recent daily regional statistics (JSON only).
576
+ rsplot recent 安徽 20260902 --days 2 --source raster --sensor gems -p no2 \
577
+ --output /tmp/gems_no2_recent.json
578
+
579
+ # 11:45 China-time scan with observations at the nearest hour, 12:00.
580
+ rsplot overlay 安徽 202609011145 --sensor gems -p no2 \
581
+ --basemap none --output /tmp/gems_no2_overlay.png
582
+ # Use --station-datetime YYYYMMDDHH for an intentional time override.
583
+ ```
584
+
585
+ Each scan is first remapped by pixel/target overlap area in the equal-area
586
+ EPSG:6933 projection, onto a **globally aligned 0.05° analysis grid**. This is
587
+ a numerical analysis spacing, not native satellite resolution. Daily means
588
+ weight valid scans equally at each grid cell; multi-day means then weight
589
+ valid days equally. A daily cell needs at least one valid scan. Window `--n-min`
590
+ continues to count **days**, using the existing product defaults, and is not
591
+ available for a single scan. These are means of available daytime scans,
592
+ not full 24-hour means. Empty scans and failed files do not contribute zeros.
593
+
594
+ For GEMS `raster` and `overlay`, **`--res` controls only the display grid**.
595
+ Display cells receive area-weighted analysis values and are clipped to the
596
+ fixed analysis grid's valid-cell union and the administrative boundary.
597
+ Changing `--res` can change displayed averages, but cannot move the valid
598
+ support or change the scientific statistics. `--smooth` is applied on the
599
+ fixed analysis grid for display only, preserving its valid mask. GEMS
600
+ `recent` uses the same 0.05° analysis spacing and ignores `--res`.
601
+ TROPOMI keeps its existing resolution behavior.
602
+
603
+ When available, GEMS pixel boundaries use file `CornerLongitude` and
604
+ `CornerLatitude` arrays. The checked O3P and NO2 files have centers only;
605
+ their quadrilateral boundaries are **estimates from adjacent unfiltered
606
+ native centers**, with linear extrapolation at the outer scan edge. Missing
607
+ geolocation is not interpolated, and rejected retrievals do not contribute
608
+ values. This is not a recovery of official footprints or extra observations.
609
+ Analysis cells need positive-area overlap with valid footprints; their
610
+ coverage percentage counts valid analysis centers, not precisely observed
611
+ land area. Subcell gaps remain below the 0.05° analysis spacing.
612
+
613
+ Sidecars record `footprint_source` per scan, `spatial_aggregation`,
614
+ `area_crs`, `analysis_resolution_deg`, `display_resolution_deg`, and
615
+ `display_support`. The compatibility `swath.resolution_deg` describes the
616
+ analysis grid. Statistics differ from the previous center-point-binning
617
+ implementation, but remain identical across display resolutions.
618
+
619
+ The initial GEMS filter retains final quality flag **0**, finite coordinates
620
+ and columns, SZA/VZA below 70°, and a nonnegative cloud measure no greater
621
+ than `--cf` (default 0.5). The cloud measures differ: NO2 uses `CloudFraction`,
622
+ HCHO uses `CloudRadianceFraction`, and O3P uses `effective_cloud_fraction`.
623
+ `--qa` applies only to TROPOMI and is rejected for GEMS. Quality-filtered
624
+ negative columns are retained; HCHO gap-filled flag classes are excluded.
625
+ This explicit filter is recorded with each file's version and variable in
626
+ JSON; quality flag meanings must not be transferred between products.
627
+
628
+ GEMS JSON statistics, spatial summaries, and hotspots use the **unfilled,
629
+ unsmoothed** regional grid. Smoothing affects the map only and cannot create
630
+ new valid cells. Sidecars include `sensor`, `quantity`, the requested local
631
+ time, nominal UTC/local scan times, actual observation time ranges, filter
632
+ settings, file failures, and scan-count summaries. Window metadata retains
633
+ per-day diagnostics. Existing TROPOMI fields remain compatible; the boundary
634
+ policy block described above is additive.
635
+
636
+ GEMS `overlay` accepts an exact scan or a daily mean. A daily mean requires
637
+ an explicit `--station-datetime YYYYMMDDHH`; its title and JSON distinguish
638
+ the daily satellite mean from the hourly station observation, without a
639
+ fictitious scan-time offset. Exact scans match the nearest station hour
640
+ (at most 30 minutes, ties forward). A missing station hour is an error.
641
+ `--station-datetime` records a manual override and its time difference.
642
+ NO2 pairs with NO2; O3/O3PR pair with O3; HCHO requires an explicit `--var`.
643
+ Satellite columns and station concentrations retain independent colorbars
644
+ and units; the map shows spatial juxtaposition, not a column-to-surface
645
+ conversion.
646
+
647
+ Run the self-contained GEMS regressions without adding pytest:
648
+
649
+ ```bash
650
+ uv run python -m unittest discover -s tests -p 'test_gems.py' -v
651
+ ```
652
+
439
653
  ## License
440
654
 
441
655
  This project is distributed under the license in `LICENSE`.