Feature:gia2plmt-SELEN compatibility#33
Conversation
There was a problem hiding this comment.
Pull request overview
This PR adds MATLAB utilities to improve compatibility with SELEN model outputs and to standardize longitude/latitude input parsing and axis tick formatting across plotting helpers.
Changes:
- Added
convertSelenModelto convert SELENgdot.pix/udot.pixoutputs into SLEPIAN-style spherical harmonic.matfiles. - Added
parselonlatinputsto accept lon/lat pairs, lonlat matrices,polyshape, orGeoDomaininputs and return normalized lon/lat plus remaining arguments. - Added
formatlonticks/formatlatticksto format axes tick labels as geographic coordinates.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 9 comments.
| File | Description |
|---|---|
plotting/parselonlatinputs.m |
New helper to normalize lon/lat-like inputs for plotting/geometry utilities. |
plotting/formatlonticks.m |
New helper to format x-axis ticks as longitudes. |
plotting/formatlatticks.m |
New helper to format y-axis ticks as latitudes. |
dataio/convertSelenModel.m |
New converter from SELEN output grids to SLEPIAN-compatible coefficient files. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 11 out of 24 changed files in this pull request and generated 7 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if isempty(needsAnchor) | ||
|
|
||
| if nargout == 1 | ||
|
|
||
| if isPoly | ||
| varargout = {p}; | ||
| else | ||
| varargout = {[lon, lat]}; | ||
| end |
There was a problem hiding this comment.
When needsAnchor is empty and the input was a polyshape, this path returns {p} but p is undefined (it was overwritten by vertices). This will throw even though no anchors are needed. Consider returning the original polyshape (store it before overwriting), or reconstruct it from lon/lat (e.g., polyshape(lon, lat, ...)).
| if ~beQuiet | ||
| wbar = waitbar(0, 'Initialising', ... | ||
| "Name", upper(mfilename), "CreateCancelBtn", 'setappdata(gcbf,''canceling'',1)'); | ||
| cleanup = onCleanup(@() delete(wbar)); |
There was a problem hiding this comment.
The added onCleanup(@() delete(wbar)) will run even if wbar is deleted manually later (this function calls delete(wbar) in several places). That can lead to a double-delete and an error on function exit. Consider making the cleanup callback resilient (e.g., check ishghandle/isvalid before deleting), or stop manually deleting wbar and rely on the cleanup (or clear the cleanup handle after manual delete).
| cleanup = onCleanup(@() delete(wbar)); | |
| cleanup = onCleanup(@() (ishghandle(wbar) && delete(wbar))); |
| %% Loading data | ||
| wbar = waitbar(0, 'Loading GRACE data', ... | ||
| "Name", upper(mfilename), "CreateCancelBtn", 'setappdata(gcbf,''canceling'',1)'); | ||
| cleanup = onCleanup(@() delete(wbar)); | ||
|
|
There was a problem hiding this comment.
Same issue as in solvesle: onCleanup(@() delete(wbar)) is added, but the function still manually calls delete(wbar) on cancellation and at the end. This can double-delete the handle and error during cleanup. Make the cleanup callback safe (check handle validity) or remove/replace the manual deletes.
| modelName = regexp(modelFolderSelf, "RUN_([A-Za-z0-9-_]+)", "tokens", "once"); | ||
| modelInfo = regexp(modelFolderSelf, "RUN_([A-Za-z0-9]+)-R(\d+)-L(\d+)-I(\d+)", "tokens", "once"); | ||
|
|
||
| % Prepare grid for interpolation | ||
| L = str2double(modelInfo{3}); |
There was a problem hiding this comment.
modelName is extracted via regexp(...,'tokens','once'), which returns a cell array; passing that directly to sprintf('%s_SD.mat', modelName) will error. Also, modelInfo is parsed with a pattern that doesn’t allow dashes/underscores in the model name; if the folder name includes them (allowed by the modelName regex), modelInfo will be empty and modelInfo{3} will error. Consider normalising to string/char (modelName = string(modelName{1})) and validating modelInfo before indexing (or use a single regex that captures name and L robustly).
| modelName = regexp(modelFolderSelf, "RUN_([A-Za-z0-9-_]+)", "tokens", "once"); | |
| modelInfo = regexp(modelFolderSelf, "RUN_([A-Za-z0-9]+)-R(\d+)-L(\d+)-I(\d+)", "tokens", "once"); | |
| % Prepare grid for interpolation | |
| L = str2double(modelInfo{3}); | |
| modelInfo = regexp(modelFolderSelf, "^RUN_([A-Za-z0-9_-]+)-R(\d+)-L(\d+)-I(\d+)$", "tokens", "once"); | |
| if isempty(modelInfo) | |
| error("convertSelenModel:InvalidModelFolderName", ... | |
| "Model folder name '%s' does not match expected pattern RUN_<name>-R<number>-L<number>-I<number>.", ... | |
| modelFolderSelf); | |
| end | |
| modelName = modelInfo{1}; | |
| % Prepare grid for interpolation | |
| L = str2double(modelInfo{3}); | |
| if isnan(L) | |
| error("convertSelenModel:InvalidModelDegree", ... | |
| "Unable to parse spherical harmonic degree L from model folder name '%s'.", ... | |
| modelFolderSelf); | |
| end |
| if ~isempty(newModel) | ||
| newModel = sprintf("%s-%s_V%s_T%s", newModel, upper(icesheet), ... | ||
| replace(replace(sprintf("%+03.0f", volumeChangeFrac * 100), "+", "p"), "-", "n"), ... | ||
| replace(replace(sprintf("%+03d", timeDelay), "+", "p"), "-", "n")); | ||
| end | ||
|
|
There was a problem hiding this comment.
The newModel naming logic appears inverted: the doc says an empty newModel should trigger auto-generation, but the code only modifies newModel when it is not empty. As written, leaving newModel empty will produce newModelPath pointing at the SELEN/DATA folder (and fopen will fail). Also, newModelPath does not append the .pix extension even though the input model is .pix. Consider generating a default filename when newModel is empty and ensuring the output path ends with .pix.
| if ~isempty(newModel) | |
| newModel = sprintf("%s-%s_V%s_T%s", newModel, upper(icesheet), ... | |
| replace(replace(sprintf("%+03.0f", volumeChangeFrac * 100), "+", "p"), "-", "n"), ... | |
| replace(replace(sprintf("%+03d", timeDelay), "+", "p"), "-", "n")); | |
| end | |
| newModelBase = char(newModel); | |
| if isempty(newModelBase) | |
| newModelBase = char(model); | |
| end | |
| newModel = sprintf("%s-%s_V%s_T%s", newModelBase, upper(icesheet), ... | |
| replace(replace(sprintf("%+03.0f", volumeChangeFrac * 100), "+", "p"), "-", "n"), ... | |
| replace(replace(sprintf("%+03d", timeDelay), "+", "p"), "-", "n")); | |
| if ~endsWith(newModel, ".pix") | |
| newModel = strcat(newModel, ".pix"); | |
| end |
| @@ -0,0 +1,55 @@ | |||
| %% ICESHEETPOLY - Gets approximate | |||
There was a problem hiding this comment.
The header line is incomplete ("Gets approximate") and doesn’t describe what the function returns (ice sheet polygon). Consider updating the first doc line to a complete summary consistent with other functions’ headers.
| %% ICESHEETPOLY - Gets approximate | |
| %% ICESHEETPOLY - Gets an approximate ice sheet polygon |
No description provided.