Add use of WCS units to center and zoom functions#116
Conversation
…stom number format; added basic test framework and simple example tests in preparation for tests of these functions
I-Chenn
left a comment
There was a problem hiding this comment.
The new added functions work without any problem in my attempt of test.
|
I wasn't entirely happy with the way that the parsing of the different formats was arranged internally, so I did a bit of refactoring to clean up the design and make the separation and code reuse more logical. The user-facing API is unchanged. While updating the tests I found some with duplicate names which I think never ran, and fixed them. |
kswang1029
left a comment
There was a problem hiding this comment.
@confluence I just had some initial code comments. Will proceed user testing.
| """ | ||
| self.call_action("overlayStore.global.setSystem", system) | ||
|
|
||
| def coordinate_system(self): |
There was a problem hiding this comment.
shall we make it clear between native coordinate system (as defined in header) and "rendered" coordinate system?
There was a problem hiding this comment.
This is mostly unambiguous because it's a function on the session, not the image (and will probably soon move to a separate image viewer class). But we can explain this in more detail in the docstring.
| class AngularSizeString: | ||
| """Parses angular sizes.""" | ||
|
|
||
| NORMALIZED_UNIT = { |
There was a problem hiding this comment.
I suggest we also support
- milliarcsec (10^-3 arcsec for VLA/ALMA etc)
- microarcsec (10^-6 arcsec for VLBI, EHT etc)
There was a problem hiding this comment.
This is doable; I'll need to do a bit of refactoring to normalize these to one of the units accepted by the frontend. Are there any other aliases of these unit names that you would like to support?
There was a problem hiding this comment.
I added these (together with the full words, and mas and µas). I also added uas, since I know that u has sometimes been used as an ASCII approximation for µ. It does turn up in search results, but possibly most of these results are just badly OCRed academic papers. If nobody actually uses this, I can take it out.
| """Parses angular sizes.""" | ||
|
|
||
| NORMALIZED_UNIT = { | ||
| "arcmin": "'", |
There was a problem hiding this comment.
we can consider to also support arcsecond and arcminute. Sometimes, we also use "asec" for arcsec and "amin" for arcmin.
There was a problem hiding this comment.
These are all very easy to add.
There was a problem hiding this comment.
I added these, together with "arcseconds" and "arcminutes" ("degrees" is also supported, but only these full words can be plural, not any of the abbreviations).
| } | ||
|
|
||
| @classmethod | ||
| def valid(cls, value): |
There was a problem hiding this comment.
wonder if we need to validate value range? For example, for "hms" convention, 0 <= h < 24, 0 <= m < 60, and 0 <= s < 60. For "dms", -90 <= d <= 90,0 <= m < 60, and 0 <= s < 60. For "d" as "longitude", 0 <= d < 360 and as "latitude" -90 <= d <= 90
There was a problem hiding this comment.
Also doable. We would need to be aware of both the coordinate system and which coordinate is which, so this probably needs to be done inside the set_center function.
As far as I can tell, the frontend currently doesn't do any validation beyond the format -- it attempts to transform whatever values are provided and applies the result if it's valid.
There was a problem hiding this comment.
Actually, for HMS and DMS we can do a more specific check in the util class. We'll just need the system and the axes for the degree ranges.
There was a problem hiding this comment.
- Should the latitude range for degrees not be
-90 <= d <= 90? - For HMS and DMS, I assume that e.g. H can only be 24 if M and S are zero (i.e. 24:12:34.5 = not valid). Is that correct?
- What if the user sets DMS as the custom number format for a longitude axis? I understand that this is not the convention, but nothing is preventing it. Should we then validate that
0 <= d < 360, as for decimal degrees? - Am I correct in assuming that longitude = right ascension = x axis and latitude = declination = y axis in all coordinate systems except for ecliptic in which case latitude = x axis and right ascension = y axis? And does it still make sense for the user to provide the coordinates in (x, y) order in the latter case?
There was a problem hiding this comment.
- Should the latitude range for degrees not be
-90 <= d <= 90?
When the coordinate system is galactic or ecliptic, we express the axis as Galactic/Ecliptic longitude and Galactic/Ecliptic latitude. Mostly, we use decimal degrees as the expression of longitude and latitude. For longitude, the range is 0 deg <= longitude < 360 deg, and for latitude, the range is -90 deg <= latitude <= 90 deg.
- For HMS and DMS, I assume that e.g. H can only be 24 if M and S are zero (i.e. 24:12:34.5 = not valid). Is that correct?
Correct, the HMS (hour-minute-second) range is like 0h:0m:0s, 0h:0m:1s, ..., 23h59m59.999999....s. The DMS (degree-arcmin-arcsec) range is -90d:0m:0s, ... -89d0m0s, ..., +90d0m0s. These are all sexagesimal.
- What if the user sets DMS as the custom number format for a longitude axis? I understand that this is not the convention, but nothing is preventing it. Should we then validate that
0 <= d < 360, as for decimal degrees?
Usually for ICRS, FK5 and FK4 (ie equatorial system), we use the HMS-DMS convention (sexagesimal). But it is totally fine to convert sexagesimal format to decimal degree format. For GALACTIC and ECLIPTIC, we use decimal degrees convention. Rarely we use sexagesimal format for these two systems (but it is valid to do such conversion).
- Am I correct in assuming that longitude = right ascension = x axis and latitude = declination = y axis in all coordinate systems except for ecliptic in which case latitude = x axis and right ascension = y axis? And does it still make sense for the user to provide the coordinates in (x, y) order in the latter case?
AST has its logic on where to map the longitude/latitude or RA/DEC axes to the canvas xy axes if the field has extra rotation. Assuming there is no extra field rotation, for ICRS, FK5 and FK4 systems, North is up and East is left. Then x is RA and y is DEC. For GALACTIC and ECLIPTIC systems without extra field rotation, North is up and East is left too. So x is longitude and y is latitude. The tricky part is, if we select GALACTIC grid to render from a native FK5 system (or vice versa), we will see the grid is rotated (hence we may see unexpected longitude/latitude to x/y mapping due to AST).
There was a problem hiding this comment.
Following up on our previous conversation, I also checked what happens in the GUI if you select HMS as the format for latitude. This appears to be valid, and negative values are converted to the expected negative HMS values, which suggests that in this case the range should be -6:00:00 to 6:00:00.
There was a problem hiding this comment.
Hmmm I would suggest we fix this behavior of the frontend. 🤔 showing HMS for latitude is not right.
There was a problem hiding this comment.
That may be tricky. The preferences are for the X and Y axis, and as we discussed, which axis is which depends on how AST decides to display that image.
There was a problem hiding this comment.
Fortunately I was overthinking this (I misread this part of your reply). I see now that the orientation of X and Y also changes, so X is always longitude and Y is always latitude. That should make it easier to implement the ranges (and also to disable specific custom number options in the frontend).
There was a problem hiding this comment.
I also misread the HMS range -- I see that 24 is excluded from the hour range.
| mock_call_action.assert_called_with("setCenter", float(x), float(y)) | ||
|
|
||
|
|
||
| @pytest.mark.parametrize("x,y,x_fmt,y_fmt,x_norm,y_norm", [ |
There was a problem hiding this comment.
might be good to add test cases like (30d, -30d) where the latitude coordinate is a negative number. Need unit tests for this case too.
There was a problem hiding this comment.
This would probably be in the unit test for set_center.
|
@confluence here is one issue I spotted. If we have two images loaded (not matched) and they both have equatorial coordinate system defined as the native one, then if we try to set_center using GALACTIC coordinate to the non active image, the result will be incorrect. Example script is shown below (let me know if you need the two test images I used). Screen.Recording.2023-05-31.at.12.34.18.movIf we make image active first before set_center, it works. However, the axis labels are not rendered correctly. Screen.Recording.2023-05-31.at.12.36.01.mov |
|
I'm temporarily moving this back to draft so that I can push some incomplete changes while I'm refactoring. I will move it back out once it's ready for review again. |
…if used separately. Updated tests.
| # Image coordinates | ||
| x_value = PixelValue.as_float(str(x)) | ||
| y_value = PixelValue.as_float(str(y)) | ||
| if 0 <= x_value < self.width and 0 <= y_value < self.height: |
There was a problem hiding this comment.
This is minor but I just realized, in CARTA we refer the center of the pixel at the bottom-left corner as (0, 0). This means, for example in x direction we have 100 pixels, the valid range should be -0.5 <= x < 99.5 (or -0.5 <= x <= 99.5?).
There was a problem hiding this comment.
I decided to make the range symmetrical -- if the user can center the bottom-left corner, they should also be able to center the other corners.
Does it make sense to apply a range limit at all? The user can specify WCS coordinates which are outside the image area, right? Should they not be able to do the same here?
There was a problem hiding this comment.
Right, world coordinate can be outside the image. So guess we can remove the range check for consistency and potentially enable some use cases.
| self.call_action("zoomToSizeX", PixelValue.as_float(size)) | ||
| else: | ||
| if not self.valid_wcs: | ||
| raise ValueError("Cannot parse angular size. This image does not contain valid WCS information.") |
There was a problem hiding this comment.
IIRC, if the image is not have a valid WCS, we render the xy axes as "linear". So maybe we can add a hint to use image coordinate (ie pixel) as the size input.
| self.call_action("zoomToSizeXWcs", str(AngularSize.from_string(size))) | ||
|
|
||
| @validate(Size()) | ||
| def zoom_to_size_y(self, size): |
There was a problem hiding this comment.
Wonder if we can combine zoom_to_size_x and zoom_to_size_y as zoom_to_size and have an argument as "direction" (or "dimension" or "axis"?) for x or y? This would simplify the code repeatition here.
There was a problem hiding this comment.
Good idea. I already added a SpatialAxis enum for the set_center range validation, so this is a simple change.
… of coordinates to middle of pixel
…s, for consistency with WCS coordinates
|
As discussed, I will merge this as-is so that we can start using the testing framework, and create a new issue for the inactive image problem. |
This PR implements #56, #57, and #109.
Image.set_centernow accepts either world coordinates or image coordinates.set_centerdoes have an optional parameter to set the system (for consistency with the dropdown in the GUI dialog), and I have added high-level functions toSessionto make setting or clearing a custom number format more user-friendly.Image.set_zoomhas been renamed toImage.set_zoom_level.Image.zoom_to_size_xandImage.zoom_to_size_yare used to fit the specified dimension to the size provided, and accept both pixel sizes and angular sizes."for arcsec,'for arcmin, anddegfor degrees). A number or numeric string with no units is assumed to be in arcsec (for consistency with the frontend).pytest. Once this is merged, tests for existing high-level functions can be filled in.CoordinateSystemenum values have been corrected to match the frontend.Stringvalidation class now has an integer parameter for arbitrary regex flags instead of a boolean parameter for ignoring the case. Existing code was using the parameter like this already (it worked by accident!).I made some decisions about the allowed / disallowed input formats which are up for discussion -- these can be changed relatively easily. For example:
'and", but being allowed before multi-character unit words""is a valid coordinate which is evaluated as"::".To be completed: