Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
2343c39
Some prototype functions; work in progress
confluence Jul 27, 2023
fb4b511
some initial functions
confluence Aug 10, 2023
d91fc83
Merge remote-tracking branch 'origin/dev' into confluence/regions
confluence Aug 10, 2023
35453e7
Add region module to docs
confluence Aug 10, 2023
e1a3689
COmpleted and revised some functionality
confluence Aug 15, 2023
f596a3e
syntax fix and formatting pass
confluence Aug 15, 2023
6f9b40f
refactor
confluence Aug 15, 2023
88aa8e2
Bumped version; moved version string to text file to be read dynamica…
confluence Aug 15, 2023
cf1f2d3
Fixed some bugs; partially tested adding new regions
confluence Aug 15, 2023
201aa36
Tested and fixed region export and import. Added functions for deleti…
confluence Aug 15, 2023
5ec4720
fixed ls test; fixed add_* validation; removed rotation from most add…
confluence Aug 16, 2023
59b1c47
Added more region properties
confluence Aug 16, 2023
2fb783b
Removed string representation of pixel units. Bare numbers are now in…
confluence Aug 16, 2023
4d5a9a0
Merge branch 'confluence/simplify-pixel-units' into confluence/regions
confluence Aug 16, 2023
02f7e79
Merge remote-tracking branch 'origin/dev' into confluence/regions
confluence Aug 17, 2023
846f712
Initial WCS support (untested)
confluence Aug 17, 2023
8fdbff8
Fixed some bugs; added WCS functions for specific region types
confluence Aug 28, 2023
90c4e92
Added some docstrings and revised some parameters / validation
confluence Aug 28, 2023
b4481fd
Added most of the missing docstrings.
confluence Aug 29, 2023
39002cf
Added missing docstrings. Made various API changes to the region clas…
confluence Aug 31, 2023
36de180
Merge remote-tracking branch 'origin/dev' into confluence/regions
confluence Aug 31, 2023
f53c80d
Modified angular size units to allow negative sizes; fixed missing an…
confluence Sep 4, 2023
9575d74
formatting pass
confluence Sep 5, 2023
a90cee6
Halfway through region tests
confluence Oct 19, 2023
cdb4d03
More type-specific tests added; fixed minor bugs.
confluence Oct 21, 2023
4a4aa99
added more specific tests; reordered inheritance to allow correct use…
confluence Oct 22, 2023
b454ef3
Added tests for point annotation and fixed bug
confluence Oct 23, 2023
59347f1
Finished specific region class tests; refactored set_size code
confluence Oct 23, 2023
babe063
Added tests for conversion functions in image class; fixed bug
confluence Oct 23, 2023
dae85e3
Added tests for Point utility class
confluence Oct 23, 2023
ed271f3
Added tests for Point validator
confluence Oct 23, 2023
ab065d2
Added tests for angular size arcsec conversion
confluence Oct 23, 2023
36ce986
Simplified region class attributes. Added some docstrings to class at…
confluence Oct 23, 2023
2fa5800
Merge remote-tracking branch 'origin/dev' into confluence/regions
confluence Oct 24, 2023
6e2eb24
removed angle correction which should be unnecessary for atan2
confluence Oct 27, 2023
34f08b3
negate size returned by frontend for endpoint regions instead of nega…
confluence Oct 27, 2023
2ad608e
Fixed tests for swapped size, and renamed fixtures to make later merg…
confluence Oct 27, 2023
00d539f
Merge remote-tracking branch 'origin/dev' into confluence/regions
confluence Oct 27, 2023
67d0d96
Fixed style issues found by updated style check.
confluence Oct 27, 2023
8a5f4f1
Refactored shared fixtures
confluence Oct 27, 2023
ab12401
Merge remote-tracking branch 'origin/dev' into confluence/regions
confluence Oct 27, 2023
4bba199
Add regions to subobject test
confluence Oct 27, 2023
a24f7ab
Updated patched line-like region functionality to match latest dev fr…
confluence Dec 7, 2023
46bda2a
Merge remote-tracking branch 'origin/dev' into confluence/regions
confluence Feb 27, 2024
4a00477
Merge remote-tracking branch 'origin/dev' into confluence/regions
confluence Apr 3, 2024
57ac980
Merge remote-tracking branch 'origin/dev' into confluence/regions
confluence May 30, 2024
25c4fc7
Merge remote-tracking branch 'origin/dev' into confluence/regions
confluence Jun 18, 2024
4b7bc5a
formatting pass and removal of unneeded import
confluence Jun 18, 2024
6cf0339
Merge remote-tracking branch 'origin/dev' into confluence/regions
confluence Oct 7, 2024
8dde217
fixing docs requirements
confluence Oct 7, 2024
60255fa
Removed custom workarounds for issues now fixed in the frontend. Stil…
confluence Oct 30, 2025
7b9ae2d
added alternative functions for creating new rectangles and ellipses
confluence Oct 31, 2025
833b6b6
Use magnitudes of sizes only in set_size; implement set_size and set_…
confluence Nov 10, 2025
fd1c657
added property to scale regions by a numeric factor
confluence Nov 10, 2025
035954d
split size properties out into mixin and update tests; disable annulu…
confluence Nov 11, 2025
57a4e24
Added polygon approximation for ellipses; tests TBD
confluence Nov 12, 2025
cb8b011
Fixed ellipse polygon approximation, added approximations (no oversam…
confluence Nov 14, 2025
581d259
Added oversampling for rectangle and line polygons / polylines. Overr…
confluence Nov 17, 2025
5ad7843
Also copy the line style to the polygon/polyline. Added to_poly* wrap…
confluence Nov 18, 2025
51a1808
Added unit tests for translate / rotate functions
confluence Nov 24, 2025
d9054f7
Added unit tests for polygon/line conversions
confluence Nov 24, 2025
673efdd
reduce depth of table of contents
confluence Nov 25, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions carta/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,89 @@ class GridMode(StrEnum):
FIXED = "fixed"


class FileType(IntEnum):
"""File types corresponding to the protobuf enum."""
CASA = 0
CRTF = 1
DS9_REG = 2
FITS = 3
HDF5 = 4
MIRIAD = 5
UNKNOWN = 6


class RegionType(IntEnum):
"""Region types corresponding to the protobuf enum."""

def __init__(self, value):
self.is_annotation = self.name.startswith("ANN")
self.label = f"{self.name[3:].title()} - Ann" if self.is_annotation else self.name.title()

POINT = 0
LINE = 1
POLYLINE = 2
RECTANGLE = 3
ELLIPSE = 4
# ANNULUS = 5 is not actually implemented
POLYGON = 6
ANNPOINT = 7
ANNLINE = 8
ANNPOLYLINE = 9
ANNRECTANGLE = 10
ANNELLIPSE = 11
ANNPOLYGON = 12
ANNVECTOR = 13
ANNRULER = 14
ANNTEXT = 15
ANNCOMPASS = 16


class CoordinateType(IntEnum):
"""Coordinate types corresponding to the protobuf enum."""
PIXEL = 0
WORLD = 1


class PointShape(IntEnum):
"""Point annotation shapes corresponding to the protobuf enum."""
SQUARE = 0
BOX = 1
CIRCLE = 2
CIRCLE_LINED = 3
DIAMOND = 4
DIAMOND_LINED = 5
CROSS = 6
X = 7


class TextPosition(IntEnum):
"""Text annotation positions corresponding to the protobuf enum."""
CENTER = 0
UPPER_LEFT = 1
UPPER_RIGHT = 2
LOWER_LEFT = 3
LOWER_RIGHT = 4
TOP = 5
BOTTOM = 6
LEFT = 7
RIGHT = 8


class AnnotationFontStyle(StrEnum):
"""Font styles which may be used in annotations."""
NORMAL = "Normal"
BOLD = "Bold"
ITALIC = "Italic"
BOLD_ITALIC = "Italic Bold"


class AnnotationFont(StrEnum):
"""Fonts which may be used in annotations."""
HELVETICA = "Helvetica"
TIMES = "Times"
COURIER = "Courier"


class FontFamily(IntEnum):
"""Font family used in WCS overlay components."""
SANS_SERIF = 0
Expand Down
116 changes: 114 additions & 2 deletions carta/image.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,18 @@
Image objects should not be instantiated directly, and should only be created through methods on the :obj:`carta.session.Session` object.
"""


from .constants import Polarization, SpatialAxis, SpectralSystem, SpectralType, SpectralUnit
from .util import Macro, cached, BasePathMixin
from .util import Macro, cached, BasePathMixin, Point as Pt
from .units import AngularSize, WorldCoordinate
from .validation import validate, Number, Constant, Boolean, Evaluate, Attr, Attrs, OneOf, Size, Coordinate, NoneOr
from .validation import validate, Number, Constant, Boolean, Evaluate, Attr, Attrs, OneOf, Size, Coordinate, NoneOr, IterableOf, Point
from .metadata import parse_header

from .raster import Raster
from .contours import Contours
from .vector_overlay import VectorOverlay
from .wcs_overlay import ImageWCSOverlay
from .region import RegionSet


class Image(BasePathMixin):
Expand Down Expand Up @@ -41,6 +43,8 @@ class Image(BasePathMixin):
Sub-object with functions related to the vector overlay.
wcs : :obj:`carta.wcs_overlay.ImageWCSOverlay`
Sub-object with functions related to the WCS overlay.
regions : :obj:`carta.region.RegionSet` object
Functions for manipulating regions associated with this image.
"""

def __init__(self, session, image_id):
Expand All @@ -55,6 +59,7 @@ def __init__(self, session, image_id):
self.contours = Contours(self)
self.vectors = VectorOverlay(self)
self.wcs = ImageWCSOverlay(self)
self.regions = RegionSet(self)

@classmethod
def new(cls, session, directory, file_name, hdu, append, image_arithmetic, make_active=True, update_directory=False):
Expand Down Expand Up @@ -530,6 +535,113 @@ def set_spectral_coordinate(self, spectral_type, spectral_unit=None):
spectral_coordinate_string = description if spectral_unit is None else f"{description} ({spectral_unit})"
self.call_action("setSpectralCoordinate", spectral_coordinate_string)

# COORDINATE AND SIZE CONVERSIONS

@validate(IterableOf(Point.WorldCoordinatePoint()))
def from_world_coordinate_points(self, points):
"""Convert world coordinate points to image coordinate points.

The points must have string values which can be parsed as world coordinates using the current globally set coordinate system (and any custom number formats).

Parameters
----------
points : {0}
Points with string values which are valid world coordinates.

Returns
-------
iterable of numeric points
Points with numeric values which are image coordinates.
"""
points = [Pt(*p) for p in points]
converted_points = self.call_action("getImagePosFromWCS", points)
return [Pt(**p).as_tuple() for p in converted_points]

@validate(IterableOf(Point.NumericPoint()))
def to_world_coordinate_points(self, points):
"""Convert image coordinate points to world coordinate points.

The points must be numeric.

Parameters
----------
points : {0}
Points with numeric values which are valid image coordinates.

Returns
-------
iterable of string coordinate points
Points with string values which are world coordinates.
"""
points = [Pt(*p) for p in points]
converted_points = self.call_action("getWCSFromImagePos", points)
return [Pt(**p).as_tuple() for p in converted_points]

@validate(Size.AngularSize(), Constant(SpatialAxis))
def from_angular_size(self, size, axis):
"""Convert angular size to pixel size.

Parameters
----------
size : {0}
The angular size.
axis : {1}
The axis.

Returns
-------
float
The pixel size.
"""
arcsec = AngularSize.from_string(size).arcsec
if axis == SpatialAxis.X:
return self.call_action("getImageXValueFromArcsec", arcsec)
if axis == SpatialAxis.Y:
return self.call_action("getImageYValueFromArcsec", arcsec)

@validate(IterableOf(Point.AngularSizePoint()))
def from_angular_size_points(self, points):
"""Convert angular size points to pixel size points.

The points must have string values which can be parsed as angular sizes.

Parameters
----------
points : {0}
Points with string values which are valid angular sizes.

Returns
-------
iterable of numeric points
Points with numeric values which are pixel sizes.
"""
converted_points = []
for x, y in points:
converted_points.append((self.from_angular_size(x, SpatialAxis.X), self.from_angular_size(y, SpatialAxis.Y)))
return converted_points

@validate(IterableOf(Point.NumericPoint()))
def to_angular_size_points(self, points):
"""Convert pixel size points to angular size points.

The points must be numeric.

Parameters
----------
points : {0}
Points with numeric values which are valid image coordinates.

Returns
-------
iterable of angular size points
Points with string values which are angular sizes.
"""
converted_points = []
for p in points:
converted = self.call_action("getWcsSizeInArcsec", Pt(*p))
converted_points.append(Pt(**converted).as_tuple())
return converted_points

# CLOSE

def close(self):
Expand Down
Loading
Loading