Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@yunjunz
Copy link
Member

@yunjunz yunjunz commented Jul 5, 2025

Description of proposed changes

  • utils.readfile.read_gmtsar_prm(): support reading GMTSAR config file, which may have empty key values or may not have some keys as in the PRM file

  • prep_gmtsar.extract_gmtsar_metadata(): support grabing A/RLOOKS from the config file if available, which is usually named as config.{SAT}.txt, where SAT can be ERS, ENVI, ALOS, ALOS_SLC, ALOS2, ALOS2_SCAN, S1_STRIP, S1_TOPS, CSK_RAW, CSK_SLC, CSG, TSX, RS2, GF3, LT1, with help from @Xiaohua-Eric-Xu.

  • docs/dir_structure.md: add config.tops.txt file to the example directory

  • docs/demo_dataset.md: update the zenodo link for the new example dataset (with the config.tops.txt file added)

Reminders

  • Pass Pre-commit check (green)
  • Pass Codacy code review (green)
  • Pass Circle CI test (green)
  • Make sure that your code follows our style. Use the other functions/files as a basis.
  • If modifying functionality, describe changes to function behavior and arguments in a comment below the function declaration.

Summary by Sourcery

Add robust GMTSAR config file support for metadata extraction, improve parameter parsing resilience, adjust radar look direction handling, and refresh relevant documentation examples.

New Features:

  • Support reading GMTSAR configuration files in read_gmtsar_prm to handle empty or missing keys
  • Extract A/RLOOKS and RLOOKS from GMTSAR config files in prep_gmtsar.extract_gmtsar_metadata

Enhancements:

  • Ignore lines with empty keys when parsing GMTSAR parameter files
  • Conditionally compute CENTER_LINE_UTC and PLATFORM only if related keys are present
  • Assume right-looking radar for azimuth angle in asc_desc2horz_vert

Documentation:

  • Add config.tops.txt to example directory structure documentation
  • Update demo dataset Zenodo link and include config.tops.txt in docs

+ utils.readfile.read_gmtsar_prm(): support reading GMTSAR config file, which may have empty key values or may not have some keys as in the PRM file

+ prep_gmtsar.extract_gmtsar_metadata(): support grabing A/RLOOKS from the config.tops.txt file if available

+ docs/dir_structure.md: add config.tops.txt file to the example directory

+ docs/demo_dataset.md: update the zenodo link for the new example dataset (with the config.tops.txt file added)
@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Jul 5, 2025

Reviewer's Guide

This PR enhances GMTSAR parameter handling by improving the parser to skip empty entries and conditionally map attributes, augments metadata extraction to pull processing looks from an external config file (with template fallback) while updating pixel sizing, adds targeted debug logging throughout prep_gmtsar, tweaks the heading-to-azimuth conversion for right-looking geometry, and brings documentation up to date with the new config file and demo dataset link.

Sequence diagram for extracting A/RLOOKS from config or template in GMTSAR metadata extraction

sequenceDiagram
    participant User
    participant prep_gmtsar.extract_gmtsar_metadata
    participant readfile.read_gmtsar_prm
    participant readfile.read_template
    participant config_file
    participant template_file

    User->>prep_gmtsar.extract_gmtsar_metadata: Call with meta_file, unw_file, template_file
    prep_gmtsar.extract_gmtsar_metadata->>readfile.read_gmtsar_prm: Read meta_file
    readfile.read_gmtsar_prm-->>prep_gmtsar.extract_gmtsar_metadata: Return meta dict
    prep_gmtsar.extract_gmtsar_metadata->>config_file: Check if config.tops.txt exists
    alt config file exists
        prep_gmtsar.extract_gmtsar_metadata->>readfile.read_gmtsar_prm: Read config_file
        readfile.read_gmtsar_prm-->>prep_gmtsar.extract_gmtsar_metadata: Return config dict
        prep_gmtsar.extract_gmtsar_metadata->>prep_gmtsar.extract_gmtsar_metadata: Calculate ALOOKS/RLOOKS from config
    else config file missing
        prep_gmtsar.extract_gmtsar_metadata->>readfile.read_template: Read template_file
        readfile.read_template-->>prep_gmtsar.extract_gmtsar_metadata: Return template dict
        prep_gmtsar.extract_gmtsar_metadata->>prep_gmtsar.extract_gmtsar_metadata: Get ALOOKS/RLOOKS from template
    end
    prep_gmtsar.extract_gmtsar_metadata->>prep_gmtsar.extract_gmtsar_metadata: Update pixel sizes
    prep_gmtsar.extract_gmtsar_metadata-->>User: Return updated metadata
Loading

Class diagram for updated GMTSAR parameter reading and metadata extraction

classDiagram
    class readfile {
        +read_gmtsar_prm(fname, delimiter='=')
        +read_template(template_file)
    }
    class prep_gmtsar {
        +extract_gmtsar_metadata(meta_file, unw_file, template_file, ...)
    }
    readfile <.. prep_gmtsar : uses
    class read_gmtsar_prm {
        +skips empty key values
        +conditionally maps SC_clock_start/stop and SC_identity
    }
    readfile ..> read_gmtsar_prm : calls
    class extract_gmtsar_metadata {
        +reads ALOOKS/RLOOKS from config or template
        +updates AZIMUTH/RANGE_PIXEL_SIZE
    }
    prep_gmtsar ..> extract_gmtsar_metadata : main logic
Loading

File-Level Changes

Change Details Files
Enhanced GMTSAR parameter file parsing to handle missing or empty entries and conditionally map attributes
  • Ignore lines with empty keys in read_gmtsar_prm
  • Only compute CENTER_LINE_UTC if SC_clock_start/stop exist
  • Only map PLATFORM when SC_identity is present
src/mintpy/utils/readfile.py
Extended extract_gmtsar_metadata to read A/RLOOKS from GMTSAR config file with fallback to template
  • Detect and read config.tops.txt for filter_wavelength
  • Compute ALOOKS/RLOOKS from filter_wavelength and pixel sizes
  • Fallback to template values or raise error if missing
  • Update AZIMUTH/RANGE_PIXEL_SIZE by multiplies of A/RLOOKS
src/mintpy/prep_gmtsar.py
Added informative debug prints in prep_gmtsar metadata extraction
  • Log when grabbing LAT/LON corner refs
  • Log when reading SLANT_RANGE_DISTANCE and INCIDENCE_ANGLE
  • Log metadata sources for A/RLOOKS and HEADING
  • Log coordinate step grabs from unw_file
src/mintpy/prep_gmtsar.py
Adjusted azimuth angle computation to assume right-looking geometry
  • Pass look_direction='right' to heading2azimuth_angle
  • Clarify log output to note right-looking radar
src/mintpy/asc_desc2horz_vert.py
Updated documentation for GMTSAR config integration and dataset references
  • Add config.tops.txt to example directory structure
  • Refresh demo_dataset.md to point to new Zenodo link
docs/dir_structure.md
docs/demo_dataset.md

Possibly linked issues

  • Updates for version 1.1Β #1: PR modifies prep_gmtsar metadata reading code, related to the issue's file handling error in the same code area.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @yunjunz - I've reviewed your changes and they look great!

Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments

### Comment 1
<location> `src/mintpy/prep_gmtsar.py:93` </location>
<code_context>
         return prm_dict

     # read corners lat/lon info
+    print(f'grab LAT/LON_REF1/2/3/4 from file: {geo_file}')
     ds = gdal.Open(geo_file, gdal.GA_ReadOnly)
     transform = ds.GetGeoTransform()
</code_context>

<issue_to_address>
Consider replacing print statements with a logging framework.

A logging framework provides better control over message levels and output, reducing clutter in standard output.

Suggested implementation:

```python
        return prm_dict

    import logging
    logging.basicConfig(level=logging.INFO)
    # read corners lat/lon info
    logging.info(f'grab LAT/LON_REF1/2/3/4 from file: {geo_file}')
    ds = gdal.Open(geo_file, gdal.GA_ReadOnly)
    transform = ds.GetGeoTransform()

```

```python
    # read width from rdr_file
    logging.info(f'grab SLANT_RANGE_DISTANCE, INCIDENCE_ANGLE from file: {geo_file}')
    ds = gdal.Open(geo_file, gdal.GA_ReadOnly)
    width = ds.RasterXSize

```
</issue_to_address>

### Comment 2
<location> `src/mintpy/prep_gmtsar.py:142` </location>
<code_context>
         raise ValueError(f'No radar-coord files found in {ifg_dir} with suffix: {fbases}')

     # read width from rdr_file
+    print(f'grab SLANT_RANGE_DISTANCE, INCIDENCE_ANGLE from file: {geo_file}')
     ds = gdal.Open(geo_file, gdal.GA_ReadOnly)
     width = ds.RasterXSize
</code_context>

<issue_to_address>
Prefer logging over print for status messages.

Using a logging library enhances maintainability and lets users control output verbosity.

Suggested implementation:

```python
    import logging
    logging.info(f'grab LAT/LON_REF1/2/3/4 from file: {geo_file}')

```

```python
    logging.info(f'grab SLANT_RANGE_DISTANCE, INCIDENCE_ANGLE from file: {geo_file}')

```

```python
    logging.info(f'extract metadata from metadata file: {meta_file}')

```

If `import logging` is already present at the top of the file, you can remove the new import statement from the first change above. 
You may also want to ensure that logging is configured somewhere in your application, e.g., with `logging.basicConfig(level=logging.INFO)`, if not already done.
</issue_to_address>

### Comment 3
<location> `src/mintpy/prep_gmtsar.py:195` </location>
<code_context>
     # 1. read *.PRM file
-    #prm_file = get_prm_files(ifg_dir)[0]
+    # prm_file = get_prm_files(ifg_dir)[0]
+    print(f'extract metadata from metadata file: {meta_file}')
     meta = readfile.read_gmtsar_prm(meta_file)
     meta['PROCESSOR'] = 'gmtsar'
</code_context>

<issue_to_address>
Replace print with logging for better output control.

Logging offers more flexibility and can be easily managed or redirected in production environments.

Suggested implementation:

```python
    # 1. read *.PRM file
    # prm_file = get_prm_files(ifg_dir)[0]
    import logging
    logging.info(f'extract metadata from metadata file: {meta_file}')
    meta = readfile.read_gmtsar_prm(meta_file)
    meta['PROCESSOR'] = 'gmtsar'

```

If the `logging` module is already imported at the top of the file, you should remove the duplicate import from this block. Also, ensure that logging is configured somewhere in your application (e.g., with `logging.basicConfig(level=logging.INFO)`), otherwise you may not see any output.
</issue_to_address>

### Comment 4
<location> `src/mintpy/prep_gmtsar.py:210` </location>
<code_context>
+        # calc a/rlooks
+        # Note from Xiaohua Xu: GMTSAR is applying Gaussian filtering, instead of multilooing,
+        # thus, the equivalent value is ~0.3 times the mainlobe response of the boxcar filtering.
+        print(f'grab A/RLOOKS from config file: {config_file}')
+        meta['ALOOKS'] = np.rint(0.3 * filter_wvl / meta['AZIMUTH_PIXEL_SIZE']).astype(int)
+        meta['RLOOKS'] = np.rint(0.3 * filter_wvl / meta['RANGE_PIXEL_SIZE']).astype(int)
+
+    else:
</code_context>

<issue_to_address>
Consider using logging instead of print for configuration status.

Using logging allows users to adjust message verbosity and output location, which is beneficial in complex workflows.

Suggested implementation:

```python
        logging.info(f'grab A/RLOOKS from config file: {config_file}')

```

```python
        logging.warning(f'No config file found in {config_file}!')

```

```python
                logging.info(f'grab {key} from template file: {template_file}')

```

```python
    import logging
    template = readfile.read_template(template_file)

```
</issue_to_address>

### Comment 5
<location> `src/mintpy/prep_gmtsar.py:215` </location>
<code_context>
+        meta['RLOOKS'] = np.rint(0.3 * filter_wvl / meta['RANGE_PIXEL_SIZE']).astype(int)
+
+    else:
+        print(f'WARNING: No config file found in {config_file}!')
+        # read from template file
+        for key in ['ALOOKS', 'RLOOKS']:
</code_context>

<issue_to_address>
Use logging.warning for warnings instead of print.

This allows warnings to be managed and filtered more effectively.

Suggested implementation:

```python
        import logging
        logging.info(f'grab A/RLOOKS from config file: {config_file}')

```

```python
        import logging
        logging.warning(f'No config file found in {config_file}!')

```

```python
                logging.info(f'grab {key} from template file: {template_file}')

```
</issue_to_address>

### Comment 6
<location> `docs/demo_dataset.md:61` </location>
<code_context>

 + Area: San Francisco Bay, California, USA
-+ Data: Sentinel-1 A/B descending track 42 during December 2014 - June 2024 (333 acquisitoins; [Zenodo](https://zenodo.org/records/12773014))
++ Data: Sentinel-1 A/B descending track 42 during December 2014 - June 2024 (333 acquisitoins; [Zenodo](https://zenodo.org/records/15814132))
 + Size: ~2.3 GB

</code_context>

<issue_to_address>
Typo: 'acquisitoins' should be 'acquisitions'.

Please update the typo to 'acquisitions'.
</issue_to_address>

<suggested_fix>
<<<<<<< SEARCH
+ Data: Sentinel-1 A/B descending track 42 during December 2014 - June 2024 (333 acquisitoins; [Zenodo](https://zenodo.org/records/15814132))
=======
+ Data: Sentinel-1 A/B descending track 42 during December 2014 - June 2024 (333 acquisitions; [Zenodo](https://zenodo.org/records/15814132))
>>>>>>> REPLACE

</suggested_fix>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click πŸ‘ or πŸ‘Ž on each comment and I'll use the feedback to improve your reviews.

return prm_dict

# read corners lat/lon info
print(f'grab LAT/LON_REF1/2/3/4 from file: {geo_file}')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Consider replacing print statements with a logging framework.

A logging framework provides better control over message levels and output, reducing clutter in standard output.

Suggested implementation:

        return prm_dict

    import logging
    logging.basicConfig(level=logging.INFO)
    # read corners lat/lon info
    logging.info(f'grab LAT/LON_REF1/2/3/4 from file: {geo_file}')
    ds = gdal.Open(geo_file, gdal.GA_ReadOnly)
    transform = ds.GetGeoTransform()
    # read width from rdr_file
    logging.info(f'grab SLANT_RANGE_DISTANCE, INCIDENCE_ANGLE from file: {geo_file}')
    ds = gdal.Open(geo_file, gdal.GA_ReadOnly)
    width = ds.RasterXSize

raise ValueError(f'No radar-coord files found in {ifg_dir} with suffix: {fbases}')

# read width from rdr_file
print(f'grab SLANT_RANGE_DISTANCE, INCIDENCE_ANGLE from file: {geo_file}')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Prefer logging over print for status messages.

Using a logging library enhances maintainability and lets users control output verbosity.

Suggested implementation:

    import logging
    logging.info(f'grab LAT/LON_REF1/2/3/4 from file: {geo_file}')
    logging.info(f'grab SLANT_RANGE_DISTANCE, INCIDENCE_ANGLE from file: {geo_file}')
    logging.info(f'extract metadata from metadata file: {meta_file}')

If import logging is already present at the top of the file, you can remove the new import statement from the first change above.
You may also want to ensure that logging is configured somewhere in your application, e.g., with logging.basicConfig(level=logging.INFO), if not already done.

# 1. read *.PRM file
#prm_file = get_prm_files(ifg_dir)[0]
# prm_file = get_prm_files(ifg_dir)[0]
print(f'extract metadata from metadata file: {meta_file}')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Replace print with logging for better output control.

Logging offers more flexibility and can be easily managed or redirected in production environments.

Suggested implementation:

    # 1. read *.PRM file
    # prm_file = get_prm_files(ifg_dir)[0]
    import logging
    logging.info(f'extract metadata from metadata file: {meta_file}')
    meta = readfile.read_gmtsar_prm(meta_file)
    meta['PROCESSOR'] = 'gmtsar'

If the logging module is already imported at the top of the file, you should remove the duplicate import from this block. Also, ensure that logging is configured somewhere in your application (e.g., with logging.basicConfig(level=logging.INFO)), otherwise you may not see any output.

Comment on lines +210 to +212
print(f'grab A/RLOOKS from config file: {config_file}')
meta['ALOOKS'] = np.rint(0.3 * filter_wvl / meta['AZIMUTH_PIXEL_SIZE']).astype(int)
meta['RLOOKS'] = np.rint(0.3 * filter_wvl / meta['RANGE_PIXEL_SIZE']).astype(int)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Consider using logging instead of print for configuration status.

Using logging allows users to adjust message verbosity and output location, which is beneficial in complex workflows.

Suggested implementation:

        logging.info(f'grab A/RLOOKS from config file: {config_file}')
        logging.warning(f'No config file found in {config_file}!')
                logging.info(f'grab {key} from template file: {template_file}')
    import logging
    template = readfile.read_template(template_file)

meta['RLOOKS'] = np.rint(0.3 * filter_wvl / meta['RANGE_PIXEL_SIZE']).astype(int)

else:
print(f'WARNING: No config file found in {config_file}!')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Use logging.warning for warnings instead of print.

This allows warnings to be managed and filtered more effectively.

Suggested implementation:

        import logging
        logging.info(f'grab A/RLOOKS from config file: {config_file}')
        import logging
        logging.warning(f'No config file found in {config_file}!')
                logging.info(f'grab {key} from template file: {template_file}')

yunjunz and others added 2 commits July 5, 2025 23:48
Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
@yunjunz yunjunz merged commit 3b0005e into insarlab:main Jul 5, 2025
7 checks passed
@yunjunz yunjunz deleted the gmtsar_config branch July 5, 2025 16:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant