def create_fitswcs(inp, input_frame=None): if isinstance(inp, DataModel): wcsinfo = wcsinfo_from_model(inp) wavetable = None spatial_axes, spectral_axes, unknown = gwutils.get_axes(wcsinfo) if spectral_axes: sp_axis = spectral_axes[0] if wcsinfo['CTYPE'][sp_axis] == 'WAVE-TAB': wavetable = inp.wavetable transform = fitswcs_transform_from_model(wcsinfo, wavetable=wavetable) output_frame = frame_from_model(wcsinfo) else: raise TypeError( "Input is expected to be a DataModel instance or a FITS file.") if input_frame is None: wcsaxes = wcsinfo['WCSAXES'] if wcsaxes == 2: input_frame = cf.Frame2D(name="detector") elif wcsaxes == 3: input_frame = cf.CoordinateFrame( name="detector", naxes=3, axes_order=(0, 1, 2), unit=(u.pix, u.pix, u.pix), axes_type=["SPATIAL", "SPATIAL", "SPECTRAL"], axes_names=('x', 'y', 'z'), axis_physical_types=None) else: raise TypeError( f"WCSAXES is expected to be 2 or 3, instead it is {wcsaxes}") pipeline = [(input_frame, transform), (output_frame, None)] wcsobj = wcs.WCS(pipeline) return wcsobj
def fitswcs_transform_from_model(wcsinfo, wavetable=None): """ Create a WCS object using from datamodel.meta.wcsinfo. Transforms assume 0-based coordinates. Parameters ---------- wcsinfo : dict-like ``~jwst.meta.wcsinfo`` structure. Return ------ transform : `~astropy.modeling.core.Model` WCS forward transform - from pixel to world coordinates. """ spatial_axes, spectral_axes, unknown = gwutils.get_axes(wcsinfo) transform = gwutils.make_fitswcs_transform(wcsinfo) if spectral_axes: sp_axis = spectral_axes[0] if wavetable is None: # Subtract one from CRPIX which is 1-based. spectral_transform = astmodels.Shift(-(wcsinfo['CRPIX'][sp_axis] - 1)) | \ astmodels.Scale(wcsinfo['CDELT'][sp_axis]) | \ astmodels.Shift(wcsinfo['CRVAL'][sp_axis]) else: # Wave dimension is an array that needs to be converted to a table waves = wavetable['wavelength'].flatten() spectral_transform = astmodels.Tabular1D(lookup_table=waves) transform = transform & spectral_transform return transform
def fitswcs_transform_from_model(wcsinfo): """ Create a WCS object using from datamodel.meta.wcsinfo. Transforms assume 0-based coordinates. Parameters ---------- wcsinfo : dict-like ``~jwst.meta.wcsinfo`` structure. Return ------ transform : `~astropy.modeling.core.Model` WCS forward transform - from pixel to world coordinates. """ spatial_axes, spectral_axes, unknown = gwutils.get_axes(wcsinfo) #sp_axis = spectral_axes[0] transform = gwutils.make_fitswcs_transform(wcsinfo) #if wcsinfo['WCSAXES'] == 3: if spectral_axes: sp_axis = spectral_axes[0] # Subtract one from CRPIX which is 1-based. spectral_transform = astmodels.Shift(-(wcsinfo['CRPIX'][sp_axis] - 1)) | \ astmodels.Scale(wcsinfo['CDELT'][sp_axis]) | \ astmodels.Shift(wcsinfo['CRVAL'][sp_axis]) transform = transform & spectral_transform return transform
def fitswcs_transform_from_model(wcsinfo, wavetable=None): """ Create a WCS object using from datamodel.meta.wcsinfo. Transforms assume 0-based coordinates. Parameters ---------- wcsinfo : dict-like ``~jwst.meta.wcsinfo`` structure. Return ------ transform : `~astropy.modeling.core.Model` WCS forward transform - from pixel to world coordinates. """ spatial_axes, spectral_axes, unknown = gwutils.get_axes(wcsinfo) #sp_axis = spectral_axes[0] transform = gwutils.make_fitswcs_transform(wcsinfo) if spectral_axes: sp_axis = spectral_axes[0] if wavetable is None : # Subtract one from CRPIX which is 1-based. spectral_transform = astmodels.Shift(-(wcsinfo['CRPIX'][sp_axis] - 1)) | \ astmodels.Scale(wcsinfo['CDELT'][sp_axis]) | \ astmodels.Shift(wcsinfo['CRVAL'][sp_axis]) else : # Wave dimension is an array that needs to be converted to a table waves = wavetable['wavelength'].flatten() spectral_transform = astmodels.Tabular1D(lookup_table=waves) transform = transform & spectral_transform return transform
def frame_from_model(wcsinfo): """ Initialize a coordinate frame based on values in model.meta.wcsinfo. Parameters ---------- wcsinfo : `~stdatamodels.DataModel` or dict Either one of the JWST data models or a dict with model.meta.wcsinfo. Returns ------- frame : `~coordinate_frames.CoordinateFrame` """ if isinstance(wcsinfo, DataModel): wcsinfo = wcsinfo_from_model(wcsinfo) wcsaxes = wcsinfo['WCSAXES'] celestial_axes, spectral_axes, other = gwutils.get_axes(wcsinfo) cunit = wcsinfo['CUNIT'] frames = [] if celestial_axes: ref_frame = coords.ICRS() celestial = cf.CelestialFrame(name='sky', axes_order=tuple(celestial_axes), reference_frame=ref_frame, unit=cunit[celestial_axes], axes_names=('RA', 'DEC')) frames.append(celestial) if spectral_axes: spec = cf.SpectralFrame(name='spectral', axes_order=tuple(spectral_axes), unit=cunit[spectral_axes], axes_names=('wavelength', )) frames.append(spec) if other: # Make sure these are strings and not np.str_ objects. axes_names = tuple([str(name) for name in wcsinfo['CTYPE'][other]]) name = "_".join(wcsinfo['CTYPE'][other]) spatial = cf.Frame2D(name=name, axes_order=tuple(other), unit=cunit[other], axes_names=axes_names) frames.append(spatial) if wcsaxes == 2: return frames[0] elif wcsaxes == 3: world = cf.CompositeFrame(frames, name='world') return world else: raise ValueError("WCSAXES can be 2 or 3, got {0}".format(wcsaxes))
def fitswcs_transform_from_model(wcsinfo): """ Create a fits wcs from a datamodel.meta.wcsinfo. """ spatial_axes, spectral_axes = gwutils.get_axes(wcsinfo) transform = gwutils.make_fitswcs_transform(wcsinfo) if wcsinfo['WCSAXES'] == 3: spectral_transform = astmodels.Shift(-wcsinfo['CRPIX'][spectral_axes]) | \ astmodels.Scale(wcsinfo['CDELT'][spectral_axes]) | \ astmodels.Shift(wcsinfo['CRVAL'][spectral_axes]) transform = transform & spectral_transform return transform
def frame_from_model(wcsinfo): wcsaxes = wcsinfo['WCSAXES'] spatial_axes, spectral_axes = gwutils.get_axes(wcsinfo) cunit = wcsinfo['CUNIT'] ref_frame = coords.ICRS() sky = cf.CelestialFrame(name='sky', axes_order=tuple(spatial_axes), reference_frame=ref_frame, unit=cunit[spatial_axes], axes_names=('RA', 'DEC')) if wcsaxes == 2: return sky elif wcsaxes == 3: spec = cf.SpectralFrame(name='spectral', axes_order=tuple(spectral_axes), unit=cunit[spectral_axes[0]], axes_names=('wavelength',)) world = cf.CompositeFrame([sky, spec], name='world') return world else: raise ValueError("WCSAXES can be 2 or 3, git {0}".format(wcsaxes))
def frame_from_model(wcsinfo): """ Initialize a coordinate frame based on values in model.meta.wcsinfo. Parameters ---------- wcsinfo : `~jwst.datamodels.model_base.DataModel` or dict Either one of the JWST data moels or a dict with model.meta.wcsinfo. Returns ------- frame : `~coordinate_frames.CoordinateFrame` """ if isinstance(wcsinfo, DataModel): wcsinfo = wcsinfo_from_model(wcsinfo) wcsaxes = wcsinfo['WCSAXES'] celestial_axes, spectral_axes, other = gwutils.get_axes(wcsinfo) cunit = wcsinfo['CUNIT'] frames = [] if celestial_axes: ref_frame = coords.ICRS() celestial = cf.CelestialFrame(name='sky', axes_order=tuple(celestial_axes), reference_frame=ref_frame, unit=cunit[celestial_axes], axes_names=('RA', 'DEC')) frames.append(celestial) if spectral_axes: spec = cf.SpectralFrame(name='spectral', axes_order=tuple(spectral_axes), unit=cunit[spectral_axes], axes_names=('wavelength',)) frames.append(spec) if other: # Make sure these are strings and not np.str_ objects. axes_names = tuple([str(name) for name in wcsinfo['CTYPE'][other]]) name = "_".join(wcsinfo['CTYPE'][other]) spatial = cf.Frame2D(name=name, axes_order=tuple(other), unit=cunit[other], axes_names=axes_names) frames.append(spatial) if wcsaxes == 2: return frames[0] elif wcsaxes == 3: world = cf.CompositeFrame(frames, name='world') return world else: raise ValueError("WCSAXES can be 2 or 3, got {0}".format(wcsaxes))
def frame_from_model(wcsinfo): wcsaxes = wcsinfo['WCSAXES'] spatial_axes, spectral_axes = gwutils.get_axes(wcsinfo) cunit = wcsinfo['CUNIT'] ref_frame = coords.ICRS() sky = cf.CelestialFrame(name='sky', axes_order=tuple(spatial_axes), reference_frame=ref_frame, unit=cunit[spatial_axes], axes_names=('RA', 'DEC')) if wcsaxes == 2: return sky elif wcsaxes == 3: spec = cf.SpectralFrame(name='spectral', axes_order=tuple(spectral_axes), unit=cunit[spectral_axes[0]], axes_names=('wavelength', )) world = cf.CompositeFrame([sky, spec], name='world') return world else: raise ValueError("WCSAXES can be 2 or 3, got {0}".format(wcsaxes))
def create_fitswcs(inp, input_frame=None): if isinstance(inp, DataModel): wcsinfo = wcsinfo_from_model(inp) wavetable = None spatial_axes, spectral_axes, unknown = gwutils.get_axes(wcsinfo) sp_axis = spectral_axes[0] if wcsinfo['CTYPE'][sp_axis] == 'WAVE-TAB': wavetable = inp.wavetable transform = fitswcs_transform_from_model(wcsinfo, wavetable) output_frame = frame_from_model(wcsinfo) #elif isinstance(inp, str): #transform = create_fitswcs_transform(inp) #output_frame = frame_from_fits(inp) else: raise TypeError( "Input is expected to be a DataModel instance or a FITS file.") if input_frame is None: input_frame = "detector" pipeline = [(input_frame, transform), (output_frame, None)] wcsobj = wcs.WCS(pipeline) return wcsobj
def create_fitswcs(inp, input_frame=None): if isinstance(inp, DataModel): wcsinfo = wcsinfo_from_model(inp) wavetable = None spatial_axes, spectral_axes, unknown = gwutils.get_axes(wcsinfo) sp_axis = spectral_axes[0] if wcsinfo['CTYPE'][sp_axis] == 'WAVE-TAB': wavetable = inp.wavetable transform = fitswcs_transform_from_model(wcsinfo, wavetable) output_frame = frame_from_model(wcsinfo) #elif isinstance(inp, str): #transform = create_fitswcs_transform(inp) #output_frame = frame_from_fits(inp) else: raise TypeError("Input is expected to be a DataModel instance or a FITS file.") if input_frame is None: input_frame = "detector" pipeline = [(input_frame, transform), (output_frame, None)] wcsobj = wcs.WCS(pipeline) return wcsobj
def wcs_from_footprints(dmodels, refmodel=None, transform=None, bounding_box=None, domain=None): """ Create a WCS from a list of input data models. A fiducial point in the output coordinate frame is created from the footprints of all WCS objects. For a spatial frame this is the center of the union of the footprints. For a spectral frame the fiducial is in the beginning of the footprint range. If ``refmodel`` is None, the first WCS object in the list is considered a reference. The output coordinate frame and projection (for celestial frames) is taken from ``refmodel``. If ``transform`` is not suplied, a compound transform is created using CDELTs and PC. If ``bounding_box`` is not supplied, the bounding_box of the new WCS is computed from bounding_box of all input WCSs. Parameters ---------- dmodels : list of `~jwst.datamodels.DataModel` A list of data models. refmodel : `~jwst.datamodels.DataModel`, optional This model's WCS is used as a reference. WCS. The output coordinate frame, the projection and a scaling and rotation transform is created from it. If not supplied the first model in the list is used as ``refmodel``. transform : `~astropy.modeling.core.Model`, optional A transform, passed to :meth:`~gwcs.wcstools.wcs_from_fiducial` If not supplied Scaling | Rotation is computed from ``refmodel``. bounding_box : tuple, optional Bounding_box of the new WCS. If not supplied it is computed from the bounding_box of all inputs. """ if domain is not None: warnings.warning( "'domain' was deprecated in 0.8 and will be removed from next" "version. Use 'bounding_box' instead.") bb = _domain_to_bounding_box(domain) else: bb = bounding_box wcslist = [im.meta.wcs for im in dmodels] if not isiterable(wcslist): raise ValueError( "Expected 'wcslist' to be an iterable of WCS objects.") if not all([isinstance(w, WCS) for w in wcslist]): raise TypeError( "All items in wcslist are to be instances of gwcs.WCS.") if refmodel is None: refmodel = dmodels[0] else: if not isinstance(refmodel, DataModel): raise TypeError( "Expected refmodel to be an instance of DataModel.") fiducial = compute_fiducial(wcslist, bb) prj = astmodels.Pix2Sky_TAN() if transform is None: transform = [] wcsinfo = pointing.wcsinfo_from_model(refmodel) sky_axes, spec, other = gwutils.get_axes(wcsinfo) rotation = astmodels.AffineTransformation2D(wcsinfo['PC']) transform.append(rotation) if sky_axes: cdelt1, cdelt2 = wcsinfo['CDELT'][sky_axes] scale = np.sqrt(np.abs(cdelt1 * cdelt2)) scales = astmodels.Scale(scale) & astmodels.Scale(scale) transform.append(scales) if transform: transform = functools.reduce(lambda x, y: x | y, transform) out_frame = refmodel.meta.wcs.output_frame wnew = wcs_from_fiducial(fiducial, coordinate_frame=out_frame, projection=prj, transform=transform) footprints = [w.footprint().T for w in wcslist] domain_bounds = np.hstack( [wnew.backward_transform(*f) for f in footprints]) for axs in domain_bounds: axs -= axs.min() bounding_box = [] for axis in out_frame.axes_order: axis_min, axis_max = domain_bounds[axis].min( ), domain_bounds[axis].max() bounding_box.append((axis_min, axis_max)) bounding_box = tuple(bounding_box) ax1, ax2 = np.array(bounding_box)[sky_axes] offset1 = (ax1[1] - ax1[0]) / 2 offset2 = (ax2[1] - ax2[0]) / 2 offsets = astmodels.Shift(-offset1) & astmodels.Shift(-offset2) wnew.insert_transform('detector', offsets, after=True) wnew.bounding_box = bounding_box return wnew
def wcs_from_footprints(dmodels, refmodel=None, transform=None, bounding_box=None, pscale_ratio=None): """ Create a WCS from a list of input data models. A fiducial point in the output coordinate frame is created from the footprints of all WCS objects. For a spatial frame this is the center of the union of the footprints. For a spectral frame the fiducial is in the beginning of the footprint range. If ``refmodel`` is None, the first WCS object in the list is considered a reference. The output coordinate frame and projection (for celestial frames) is taken from ``refmodel``. If ``transform`` is not suplied, a compound transform is created using CDELTs and PC. If ``bounding_box`` is not supplied, the bounding_box of the new WCS is computed from bounding_box of all input WCSs. Parameters ---------- dmodels : list of `~jwst.datamodels.DataModel` A list of data models. refmodel : `~jwst.datamodels.DataModel`, optional This model's WCS is used as a reference. WCS. The output coordinate frame, the projection and a scaling and rotation transform is created from it. If not supplied the first model in the list is used as ``refmodel``. transform : `~astropy.modeling.core.Model`, optional A transform, passed to :meth:`~gwcs.wcstools.wcs_from_fiducial` If not supplied Scaling | Rotation is computed from ``refmodel``. bounding_box : tuple, optional Bounding_box of the new WCS. If not supplied it is computed from the bounding_box of all inputs. pscale_ratio : float, optional Ratio of input to output pixel scale. """ bb = bounding_box wcslist = [im.meta.wcs for im in dmodels] if not isiterable(wcslist): raise ValueError("Expected 'wcslist' to be an iterable of WCS objects.") if not all([isinstance(w, WCS) for w in wcslist]): raise TypeError("All items in wcslist are to be instances of gwcs.WCS.") if refmodel is None: refmodel = dmodels[0] else: if not isinstance(refmodel, DataModel): raise TypeError("Expected refmodel to be an instance of DataModel.") fiducial = compute_fiducial(wcslist, bb) ref_fiducial = compute_fiducial([refmodel.meta.wcs]) prj = astmodels.Pix2Sky_TAN() if transform is None: transform = [] wcsinfo = pointing.wcsinfo_from_model(refmodel) sky_axes, spec, other = gwutils.get_axes(wcsinfo) # Need to put the rotation matrix (List[float, float, float, float]) # returned from calc_rotation_matrix into the correct shape for # constructing the transformation roll_ref = np.deg2rad(refmodel.meta.wcsinfo.roll_ref) v3yangle = np.deg2rad(refmodel.meta.wcsinfo.v3yangle) vparity = refmodel.meta.wcsinfo.vparity pc = np.reshape( calc_rotation_matrix(roll_ref, v3yangle, vparity=vparity), (2, 2) ) rotation = astmodels.AffineTransformation2D(pc) transform.append(rotation) if sky_axes: scale = compute_scale(refmodel.meta.wcs, ref_fiducial, pscale_ratio=pscale_ratio) transform.append(astmodels.Scale(scale) & astmodels.Scale(scale)) if transform: transform = functools.reduce(lambda x, y: x | y, transform) out_frame = refmodel.meta.wcs.output_frame input_frame = dmodels[0].meta.wcs.input_frame wnew = wcs_from_fiducial(fiducial, coordinate_frame=out_frame, projection=prj, transform=transform, input_frame=input_frame) footprints = [w.footprint().T for w in wcslist] domain_bounds = np.hstack([wnew.backward_transform(*f) for f in footprints]) for axs in domain_bounds: axs -= (axs.min() + .5) output_bounding_box = [] for axis in out_frame.axes_order: axis_min, axis_max = domain_bounds[axis].min(), domain_bounds[axis].max() output_bounding_box.append((axis_min, axis_max)) output_bounding_box = tuple(output_bounding_box) ax1, ax2 = np.array(output_bounding_box)[sky_axes] offset1 = (ax1[1] - ax1[0]) / 2 offset2 = (ax2[1] - ax2[0]) / 2 offsets = astmodels.Shift(-offset1) & astmodels.Shift(-offset2) wnew.insert_transform('detector', offsets, after=True) wnew.bounding_box = output_bounding_box return wnew
def wcs_from_footprints(dmodels, refmodel=None, transform=None, domain=None): """ Create a WCS from a list of input data models. A fiducial point in the output coordinate frame is created from the footprints of all WCS objects. For a spatial frame this is the center of the union of the footprints. For a spectral frame the fiducial is in the beginning of the footprint range. If ``refmodel`` is None, the first WCS object in the list is considered a reference. The output coordinate frame and projection (for celestial frames) is taken from ``refmodel``. If ``transform`` is not suplied, a compound transform is created using CDELTs and PC. If ``domain`` is not supplied, the domain of the new WCS is computed from the domains of all input WCSs. Parameters ---------- dmodels : list of `~jwst.datamodels.DataModel` A list of data models. refmodel : `~jwst.datamodels.DataModel`, optional This model's WCS is used as a reference. WCS. The output coordinate frame, the projection and a scaling and rotation transform is created from it. If not supplied the first model in the list is used as ``refmodel``. transform : `~astropy.modeling.core.Model`, optional A transform, passed to :class_method:`~gwcs.WCS.wcs_from_fiducial` If not supplied Scaling | Rotation is computed from ``refmodel``. domain : list of dicts, optional Domain of the new WCS. If not supplied it is computed from the domain of all inputs. """ wcslist = [im.meta.wcs for im in dmodels] if not isiterable(wcslist): raise ValueError( "Expected 'wcslist' to be an iterable of WCS objects.") if not all([isinstance(w, WCS) for w in wcslist]): raise TypeError( "All items in wcslist are to be instances of gwcs.WCS.") if refmodel is None: refmodel = dmodels[0] else: if not isinstance(refmodel, DataModel): raise TypeError( "Expected refmodel to be an instance of DataModel.") fiducial = compute_fiducial(wcslist, domain) prj = astmodels.Pix2Sky_TAN() if transform is None: transform = [] wcsinfo = pointing.wcsinfo_from_model(refmodel) sky_axes, _ = gwutils.get_axes(wcsinfo) rotation = astmodels.AffineTransformation2D(np.array(wcsinfo['PC'])) transform.append(rotation) if sky_axes: scale = wcsinfo['CDELT'][sky_axes].mean() scales = astmodels.Scale(scale) & astmodels.Scale(scale) transform.append(scales) if transform: transform = functools.reduce(lambda x, y: x | y, transform) out_frame = refmodel.meta.wcs.output_frame wnew = wcs_from_fiducial(fiducial, coordinate_frame=out_frame, projection=prj, transform=transform) footprints = [w.footprint() for w in wcslist] domain_bounds = np.hstack( [wnew.backward_transform(*f) for f in footprints]) for axs in domain_bounds: axs -= axs.min() domain = [] for axis in out_frame.axes_order: axis_min, axis_max = domain_bounds[axis].min( ), domain_bounds[axis].max() domain.append({ 'lower': axis_min, 'upper': axis_max, 'includes_lower': True, 'includes_upper': True }) #ax1, ax2 = domain[sky_axes] # change when domain is a bounding_box ax1, ax2 = domain offset1 = (ax1['upper'] - ax1['lower']) / 2 offset2 = (ax2['upper'] - ax2['lower']) / 2 offsets = astmodels.Shift(-offset1) & astmodels.Shift(-offset2) wnew.insert_transform('detector', offsets, after=True) wnew.domain = domain return wnew
def wcs_from_footprints(dmodels, refmodel=None, transform=None, bounding_box=None, domain=None): """ Create a WCS from a list of input data models. A fiducial point in the output coordinate frame is created from the footprints of all WCS objects. For a spatial frame this is the center of the union of the footprints. For a spectral frame the fiducial is in the beginning of the footprint range. If ``refmodel`` is None, the first WCS object in the list is considered a reference. The output coordinate frame and projection (for celestial frames) is taken from ``refmodel``. If ``transform`` is not suplied, a compound transform is created using CDELTs and PC. If ``bounding_box`` is not supplied, the bounding_box of the new WCS is computed from bounding_box of all input WCSs. Parameters ---------- dmodels : list of `~jwst.datamodels.DataModel` A list of data models. refmodel : `~jwst.datamodels.DataModel`, optional This model's WCS is used as a reference. WCS. The output coordinate frame, the projection and a scaling and rotation transform is created from it. If not supplied the first model in the list is used as ``refmodel``. transform : `~astropy.modeling.core.Model`, optional A transform, passed to :meth:`~gwcs.wcstools.wcs_from_fiducial` If not supplied Scaling | Rotation is computed from ``refmodel``. bounding_box : tuple, optional Bounding_box of the new WCS. If not supplied it is computed from the bounding_box of all inputs. """ if domain is not None: warnings.warning("'domain' was deprecated in 0.8 and will be removed from next" "version. Use 'bounding_box' instead.") bb = _domain_to_bounding_box(domain) else: bb = bounding_box wcslist = [im.meta.wcs for im in dmodels] if not isiterable(wcslist): raise ValueError("Expected 'wcslist' to be an iterable of WCS objects.") if not all([isinstance(w, WCS) for w in wcslist]): raise TypeError("All items in wcslist are to be instances of gwcs.WCS.") if refmodel is None: refmodel = dmodels[0] else: if not isinstance(refmodel, DataModel): raise TypeError("Expected refmodel to be an instance of DataModel.") fiducial = compute_fiducial(wcslist, bb) prj = astmodels.Pix2Sky_TAN() if transform is None: transform = [] wcsinfo = pointing.wcsinfo_from_model(refmodel) sky_axes, spec, other = gwutils.get_axes(wcsinfo) rotation = astmodels.AffineTransformation2D(wcsinfo['PC']) transform.append(rotation) if sky_axes: cdelt1, cdelt2 = wcsinfo['CDELT'][sky_axes] scale = np.sqrt(np.abs(cdelt1 * cdelt2)) scales = astmodels.Scale(scale) & astmodels.Scale(scale) transform.append(scales) if transform: transform = functools.reduce(lambda x, y: x | y, transform) out_frame = refmodel.meta.wcs.output_frame wnew = wcs_from_fiducial(fiducial, coordinate_frame=out_frame, projection=prj, transform=transform) footprints = [w.footprint().T for w in wcslist] domain_bounds = np.hstack([wnew.backward_transform(*f) for f in footprints]) for axs in domain_bounds: axs -= axs.min() bounding_box = [] for axis in out_frame.axes_order: axis_min, axis_max = domain_bounds[axis].min(), domain_bounds[axis].max() bounding_box.append((axis_min, axis_max)) bounding_box = tuple(bounding_box) ax1, ax2 = np.array(bounding_box)[sky_axes] offset1 = (ax1[1] - ax1[0]) / 2 offset2 = (ax2[1] - ax2[0]) / 2 offsets = astmodels.Shift(-offset1) & astmodels.Shift(-offset2) wnew.insert_transform('detector', offsets, after=True) wnew.bounding_box = bounding_box return wnew
def wcs_from_footprints(dmodels, refmodel=None, transform=None, bounding_box=None, pscale_ratio=None, pscale=None, rotation=None, shape=None, crpix=None, crval=None): """ Create a WCS from a list of input data models. A fiducial point in the output coordinate frame is created from the footprints of all WCS objects. For a spatial frame this is the center of the union of the footprints. For a spectral frame the fiducial is in the beginning of the footprint range. If ``refmodel`` is None, the first WCS object in the list is considered a reference. The output coordinate frame and projection (for celestial frames) is taken from ``refmodel``. If ``transform`` is not supplied, a compound transform is created using CDELTs and PC. If ``bounding_box`` is not supplied, the bounding_box of the new WCS is computed from bounding_box of all input WCSs. Parameters ---------- dmodels : list of `~jwst.datamodels.DataModel` A list of data models. refmodel : `~jwst.datamodels.DataModel`, optional This model's WCS is used as a reference. WCS. The output coordinate frame, the projection and a scaling and rotation transform is created from it. If not supplied the first model in the list is used as ``refmodel``. transform : `~astropy.modeling.core.Model`, optional A transform, passed to :meth:`~gwcs.wcstools.wcs_from_fiducial` If not supplied Scaling | Rotation is computed from ``refmodel``. bounding_box : tuple, optional Bounding_box of the new WCS. If not supplied it is computed from the bounding_box of all inputs. pscale_ratio : float, None, optional Ratio of input to output pixel scale. Ignored when either ``transform`` or ``pscale`` are provided. pscale : float, None, optional Absolute pixel scale in degrees. When provided, overrides ``pscale_ratio``. Ignored when ``transform`` is provided. rotation : float, None, optional Position angle of output image’s Y-axis relative to North. A value of 0.0 would orient the final output image to be North up. The default of `None` specifies that the images will not be rotated, but will instead be resampled in the default orientation for the camera with the x and y axes of the resampled image corresponding approximately to the detector axes. Ignored when ``transform`` is provided. shape : tuple of int, None, optional Shape of the image (data array) using ``numpy.ndarray`` convention (``ny`` first and ``nx`` second). This value will be assigned to ``pixel_shape`` and ``array_shape`` properties of the returned WCS object. crpix : tuple of float, None, optional Position of the reference pixel in the image array. If ``crpix`` is not specified, it will be set to the center of the bounding box of the returned WCS object. crval : tuple of float, None, optional Right ascension and declination of the reference pixel. Automatically computed if not provided. """ bb = bounding_box wcslist = [im.meta.wcs for im in dmodels] if not isiterable(wcslist): raise ValueError( "Expected 'wcslist' to be an iterable of WCS objects.") if not all([isinstance(w, WCS) for w in wcslist]): raise TypeError( "All items in wcslist are to be instances of gwcs.WCS.") if refmodel is None: refmodel = dmodels[0] else: if not isinstance(refmodel, DataModel): raise TypeError( "Expected refmodel to be an instance of DataModel.") fiducial = compute_fiducial(wcslist, bb) if crval is not None: # overwrite spatial axes with user-provided CRVAL: i = 0 for k, axt in enumerate(wcslist[0].output_frame.axes_type): if axt == 'SPATIAL': fiducial[k] = crval[i] i += 1 ref_fiducial = np.array( [refmodel.meta.wcsinfo.ra_ref, refmodel.meta.wcsinfo.dec_ref]) prj = astmodels.Pix2Sky_TAN() if transform is None: transform = [] wcsinfo = pointing.wcsinfo_from_model(refmodel) sky_axes, spec, other = gwutils.get_axes(wcsinfo) # Need to put the rotation matrix (List[float, float, float, float]) # returned from calc_rotation_matrix into the correct shape for # constructing the transformation v3yangle = np.deg2rad(refmodel.meta.wcsinfo.v3yangle) vparity = refmodel.meta.wcsinfo.vparity if rotation is None: roll_ref = np.deg2rad(refmodel.meta.wcsinfo.roll_ref) else: roll_ref = np.deg2rad(rotation) + (vparity * v3yangle) pc = np.reshape( calc_rotation_matrix(roll_ref, v3yangle, vparity=vparity), (2, 2)) rotation = astmodels.AffineTransformation2D(pc, name='pc_rotation_matrix') transform.append(rotation) if sky_axes: if not pscale: pscale = compute_scale(refmodel.meta.wcs, ref_fiducial, pscale_ratio=pscale_ratio) transform.append( astmodels.Scale(pscale, name='cdelt1') & astmodels.Scale(pscale, name='cdelt2')) if transform: transform = functools.reduce(lambda x, y: x | y, transform) out_frame = refmodel.meta.wcs.output_frame input_frame = refmodel.meta.wcs.input_frame wnew = wcs_from_fiducial(fiducial, coordinate_frame=out_frame, projection=prj, transform=transform, input_frame=input_frame) footprints = [w.footprint().T for w in wcslist] domain_bounds = np.hstack( [wnew.backward_transform(*f) for f in footprints]) axis_min_values = np.min(domain_bounds, axis=1) domain_bounds = (domain_bounds.T - axis_min_values).T output_bounding_box = [] for axis in out_frame.axes_order: axis_min, axis_max = domain_bounds[axis].min( ), domain_bounds[axis].max() output_bounding_box.append((axis_min, axis_max)) output_bounding_box = tuple(output_bounding_box) if crpix is None: offset1, offset2 = wnew.backward_transform(*fiducial) offset1 -= axis_min_values[0] offset2 -= axis_min_values[1] else: offset1, offset2 = crpix offsets = astmodels.Shift(-offset1, name='crpix1') & astmodels.Shift( -offset2, name='crpix2') wnew.insert_transform('detector', offsets, after=True) wnew.bounding_box = output_bounding_box if shape is None: shape = [ int(axs[1] - axs[0] + 0.5) for axs in output_bounding_box[::-1] ] wnew.pixel_shape = shape[::-1] wnew.array_shape = shape return wnew