Supporting Types#
WISER uses a number of types internally to wrap various kinds of information.
These types often hold NumPy ndarray objects or other such details for the
actual data, and also provide metadata about the information being managed.
These types are important for most kinds of plugins, and are documented here.
Raster Data Sets#
Imaging spectroscopy cubes are represented with the RasterDataSet class.
This class provides access to both the data and metadata of a spectral image
cube.
- class RasterDataSet(impl: wiser.raster.dataset_impl.RasterDataImpl, data_cache: wiser.raster.data_cache.DataCache = None)#
Bases:
wiser.raster.serializable.SerializableA 2D raster data-set for imaging spectroscopy, possibly with many bands of data for each pixel.
This class is not deep copyable. If you try to deep copy it, you will get an reference to the same object.
Initialization
- _compute_has_wavelengths()#
- get_cache() Optional[wiser.raster.data_cache.DataCache]#
- set_dirty(dirty: bool = True)#
- is_dirty() bool#
- get_id() Optional[int]#
Returns a numeric ID for referring to the data set within the application. A value of
Noneindicates that the data-set has not yet been assigned an ID.
- set_id(id: int) None#
Sets a unique numeric ID for referring to the data set within WISER.
- get_name() Optional[str]#
- set_name(name: Optional[str]) None#
- get_description() Optional[str]#
Returns a string description of the dataset that might be specified in the raster file’s metadata. A missing description is indicated by the
Nonevalue.
- set_description(description: Optional[str]) None#
Sets the string description of the dataset.
- get_format()#
Returns a string describing the type of raster data file that backs this dataset. The file-type string will be specific to the kind of loader used to load the dataset.
- get_filepaths()#
Returns the paths and filenames of all files associated with this raster dataset. This will be an empty list (not
None) if the data is in-memory only, e.g. because it wasn’t yet saved.
- get_width()#
Returns the number of pixels per row in the raster data.
- get_height()#
Returns the number of rows of data in the raster data.
- num_bands()#
Returns the number of spectral bands in the raster data.
- get_shape() Tuple[int, int, int]#
Returns the shape of the raster data set. This is always in the order
(num_bands, height, width).
- get_elem_type() numpy.dtype#
Returns the element-type of the raster data set.
- get_band_memory_size() int#
Returns the approximate size of a band of this dataset. It’s approximate because this doesn’t account for compression
- get_memory_size() int#
Returns the approximate size of this dataset. It’s approximate because this doesn’t account for compression
- get_band_unit() Optional[astropy.units.Unit]#
Returns the units used for all bands’ wavelengths, or
Noneif bands do not specify units.
- band_list() List[Dict[str, Any]]#
Returns a description of all bands in the data set. The description is formulated as a list of dictionaries, where each dictionary provides details about the band. The list of dictionaries is in the same order as the bands in the raster dataset, so that the dictionary at index i in the list describes band i.
Dictionaries may (but are not required to) contain these keys:
‘index’ - the integer index of the band (always present)
‘description’ - the string description of the band
‘wavelength’ - a value-with-units for the spectral wavelength of the band. astropy.units is used to represent the values-with-units.
‘wavelength_str’ - the string version of the band’s wavelength
‘wavelength_units’ - the string version of the band’s wavelength-units value
Note that since both lists and dictionaries are mutable, care must be taken not to mutate the return-value of this method, as it will affect the data-set’s internal state.
- set_band_list(band_list: List[Dict[str, Any]])#
This should ONLY be used for datasets that have NumPyRasterDataImpl as their impl type. This can be VERY destructive to metadata for other impl dataset types. Use with caution!
- Args:
- band_list (List[Dict[str, Any]]): A list of band-info dicts.
Each dict should contain at least one of these keys:
index: the integer index of the band (always present)description: the string description of the bandwavelength: a value-with-units for the spectral wavelength of the band (astropy.units is used to represent values-with-units)wavelength_str: the string version of the band’s wavelengthwavelength_units: the string version of the band’s wavelength units
- set_band_descriptions(band_descriptions: List[str])#
This sets just the band description for all the bands in the dataset based on what is in band_descriptions list.
- set_band_unit(unit: astropy.units.Unit)#
This should ONLY be used for datasets that have NumPyRasterDataImpl as their impl type. This can be destructive to metadata for other impl dataset types. Use with caution!
- _set_wkt(wkt_spatial_reference: Optional[str]) None#
Test-only helper for setting the dataset WKT spatial reference. This should only be used in tests.
- _set_geo_transform(geo_transform: Tuple) None#
Test-only helper for setting the dataset geo transform. This should only be used in tests.
- _set_spatial_reference(spatial_ref: Optional[osgeo.osr.SpatialReference]) None#
Test-only helper for setting the dataset spatial reference. This should only be used in tests.
- has_wavelengths()#
Returns
Trueif all bands specify a wavelength (or some other unit that can be converted to wavelength); otherwise, returnsFalse.
- get_wavelengths() Optional[List[astropy.units.Quantity]]#
Returns the wavelengths of the dataset if it has wavelengths.
- default_display_bands() Optional[wiser.raster.dataset.DisplayBands]#
Returns a tuple of integer indexes, specifying the default bands for display. If the list has 3 values, these are displayed using the red, green and blue channels of an image. If the list has 1 value, the band is displayed as grayscale.
If the raster data specifies no default bands, the return value is
None.
- set_default_display_bands(bands: Optional[wiser.raster.dataset.DisplayBands]) None#
- get_data_ignore_value() Optional[wiser.raster.dataset.Number]#
Returns the number that indicates a value to be ignored in the dataset. If this value is unknown or unspecified in the data,
Noneis returned.
- set_data_ignore_value(ignore_value: Optional[wiser.raster.dataset.Number]) None#
- get_bad_bands() Optional[List[int]]#
Returns a “bad band list” as a list of 0 or 1 integer values, with the same number of elements as the total number of bands in the dataset. A value of 0 means the band is “bad,” and a value of 1 means the band is “good.”
The returned list is a copy of the internal list; mutation on the returned list will not affect the raster data set.
- set_bad_bands(bad_bands: Optional[List[int]])#
- get_image_data(filter_data_ignore_value=True)#
Returns a numpy 3D array of the entire image cube.
The numpy array is configured such that the pixel (x, y) values of band b are at element array[b][y][x].
If the data-set has a “data ignore value” and filter_data_ignore_value is also set to True, the array will be filtered such that any element with the “data ignore value” will be filtered to NaN. Note that this filtering will impact performance.
- get_image_data_subset(x: int, y: int, band: int, dx: int, dy: int, dband: int, filter_data_ignore_value=True)#
Returns a 3D numpy array of values specified starting at x, y, and band and going until x+dx, y+dy, band+dband. The d variables are exclusive.
Data returned is in format arr[b][y][x]
- get_band_data(band_index: int, filter_data_ignore_value=True) Union[numpy.ndarray, numpy.ma.masked_array]#
Returns a numpy 2D array of the specified band’s data. The first band is at index 0.
The numpy array is configured such that the pixel (x, y) values are at element array[y][x].
If the data-set has a “data ignore value” and filter_data_ignore_value is also set to True, the array will be filtered such that any element with the “data ignore value” will be filtered to NaN. Note that this filtering will impact performance.
- get_band_data_normalized(band_index: int, band_min=None, band_max=None, filter_data_ignore_value=True) Union[numpy.ndarray, numpy.ma.masked_array]#
Returns a numpy 2D array of the specified band’s data. The first band is at index 0.
The numpy array is configured such that the pixel (x, y) values are at element array[y][x].
If the data-set has a “data ignore value” and filter_data_ignore_value is also set to True, the array will be filtered such that any element with the “data ignore value” will be filtered to NaN. Note that this filtering will impact performance.
- sample_band_data(band_index: int, sample_factor: int, filter_data_ignore_value=True) Union[numpy.ndarray, numpy.ma.masked_array]#
Returns a numpy 2D array of the specified band’s data. The first band is at index 0.
The numpy array is configured such that the pixel (x, y) values are at element array[y][x].
If the data-set has a “data ignore value” and filter_data_ignore_value is also set to True, the array will be filtered such that any element with the “data ignore value” will be filtered to NaN. Note that this filtering will impact performance.
- get_multiple_band_data(band_list: List[int], filter_data_ignore_value=True)#
Returns a numpy 3D array of the specified images band data for all pixels in those bands. The numpy array is configured such that the pixel (x, y) for band b values are at element array[b][y][x]. If the data-set has a “data ignore value” and filter_data_ignore_value is also set to True, the array will be filtered such that any element with the “data ignore value” will be filtered to NaN. Note that this filtering will impact performance.
- get_band_stats(band_index: int, band: Union[numpy.ndarray, numpy.ma.masked_array] = None)#
Returns statistics of the specified band’s data, wrapped in a
BandStatsobject.
- get_all_bands_at(x: int, y: int, filter_bad_values=True)#
Returns a numpy 1D array of the values of all bands at the specified (x, y) coordinate in the raster data.
If filter_bad_values is set to True, bands that are marked as “bad” in the metadata will be set to NaN, and bands with the “data ignore value” will also be set to NaN.
- get_all_bands_at_rect(x: int, y: int, dx: int, dy: int, filter_bad_values=True)#
Returns a numpy 2D array of the values of all bands at the specified rectangle in the raster data. If filter_bad_values is set to True, bands that are marked as “bad” in the metadata will be set to NaN, and bands with the “data ignore value” will also be set to NaN.
- get_geo_transform() Tuple#
Returns the geographic transform for this dataset as a 6-tuple of floats. The geographic transform is used to map pixel coordinates to linear geographic coordinates, and is always an affine transformation. To map linear geographic coordinates into angular geographic coordinates, see the
get_spatial_ref()method.This value is always present; if the underlying data file doesn’t specify a geographic transform then an identity transformation is returned.
See https://gdal.org/tutorials/geotransforms_tut.html for more details on how to interpret this value.
- get_wkt_spatial_reference() Optional[str]#
- get_spatial_ref() Optional[osgeo.osr.SpatialReference]#
Returns the GDAL spatial reference system used for this dataset, or
Noneif the dataset doesn’t have a spatial reference system.
- has_geographic_info() bool#
- cache_band_stats(index, arr: numpy.ndarray)#
Stores the band stats in this dataset’s cache for band stats
- to_geographic_coords(pixel_coord: Tuple[int, int]) Optional[Tuple[float, float]]#
- to_angular_coords(pixel_coord: Tuple[int, int]) Optional[Tuple[float, float]]#
- geo_to_pixel_coords(geo_coords: Tuple[float, float]) Optional[Tuple[int, int]]#
- geo_to_pixel_coords_exact(geo_coords: Tuple[float, float]) Optional[Tuple[int, int]]#
- is_pixel_in_image_bounds(pixel: Tuple[int, int]) bool#
Checks to see if the pixel is in the bounds of the image.
The 0th index of pixel corresponds to the width (x-coordinate) and the 1st index corresponds to the height (y-coordinate). The coordinate (0, 0) is the top left most valid pixel.
- Args:
pixel: The pixel that we want to know is inbounds or not
- Returns:
True if the pixel is within the bounds of the image, False otherwise.
- is_spatial_coord_in_spatial_bounds(spatial_coord: Tuple[float, float]) bool#
Checks to see if the spatial coordinate is in the spatial bounds of the image.
The 0th index of spatial_coord corresponds to the x coordinate in spatial terms, and the 1st index corresponds to the y coordinate. The spatial extent of the image is determined using self._geo_transform (as returned by GDAL’s GetGeoTransform) along with the image dimensions from self.get_width() (x direction) and self.get_height() (y direction).
- Args:
spatial_coord: The spatial coordinate that we want to know is inbounds or not
- Returns:
True if the spatial coordinate is within the spatial bounds of the image, False otherwise.
- determine_link_state(dataset: wiser.raster.dataset.RasterDataSet) wiser.raster.dataset.GeographicLinkState#
Tests to see if the passed in dataset is compatible to link with the current dataset
- Args:
- dataset (RasterDataSet):
The dataset that we want to determine our dataset’s link state with
- Returns:
GeographicLinkState: 0 is no link, 1 is pixel link, 2 is spatial link
- is_metadata_same(other: wiser.raster.dataset.RasterDataSet) None#
- copy_metadata_from(dataset: wiser.raster.dataset.RasterDataSet) None#
- copy_spatial_metadata(source: wiser.raster.dataset.SpatialMetadata) None#
Copy the spatial metadata from the SpatialMetadata object.
The spatial metadata includes the geographical transform, and the spatial reference system, if the raster has one. Any mutable values are deep-copied so that changes to the source’s information do not affect this object.
- get_spatial_metadata() wiser.raster.dataset.SpatialMetadata#
- get_spectral_metadata() wiser.raster.dataset.SpectralMetadata#
- copy_spectral_metadata(source: wiser.raster.dataset.SpectralMetadata) None#
- show_edit_dataset_dialog(app)#
Creates an edit dataset dialog menu. Should have a label at the top that says none of the changes persist on disk. THey only persist for this session.
Should have a section under this with a label
- update_band_info(wavelengths: List[astropy.units.Quantity])#
Updates the band information for this dataset. Updates the units and the _band_info field. These changes do not persist across sessions.
- get_band_info()#
- get_save_state()#
- set_save_state(save_state: wiser.raster.dataset_impl.SaveState)#
- get_impl()#
- get_subdataset_name() str#
- delete_underlying_dataset()#
- static deserialize_into_class(serializedForm: wiser.raster.serializable.SerializedForm) wiser.raster.dataset.RasterDataSet#
We need to properly open up the dataset, if it is a subdataset, then we need to properly open that subdataset.
- Args:
- serialize_value (Union[str, np.ndarray]):
A string that represents the file path to the dataset, or a numpy array that represents the data in the dataset.
- metadata (Dict):
A dictionary that represents the metadata needed to recreate this object.
- Returns:
RasterDataSet: Takes the passed in parameters and reconstructs a dataset ojbect.
- copy_serialized_metadata_from(metadata: Dict) None#
Copies the metadata from the metadata dictionary into this object. This is useful when reconstructing RasterDataSet objects meta data in another process. This is needed because the user can change the in memory copy of the RasterDataSet object and so if we reconstruct this object just from the impl dataset, we would not get this changed metadata.
- get_serialized_form() wiser.raster.serializable.SerializedForm#
Gives a tuple that represents all of the data needed to recreate this object. The first element is this class, so we can get the deserialize_into_class function The second element is a string that represents the file path to the dataset, or a numpy array that represents the data in the dataset. The third element is a dictionary that represents the metadata needed to recreate this object.
- __hash__()#
I understand that the documentation here: https://docs.python.org/3/glossary.html#term-hashable States that ‘A hash should remain unchanged throughout the lifetime of the object’, however, for this object, we want the hash to change if the data ignore value changed, so cache’s that used this dataset will use the ‘new’ hashed dataset and cause computations to be redone with the new data ignore value.
- __eq__(other) bool#
- __deepcopy__(memo)#
This class is not deep copyable. If you try to deep copy it, you will get an reference to the same object.
Making a Data Set from a NumPy Array#
A data-set may be constructed from a NumPy array using the WISER data loader. See the section Loading Raster Data into WISER for more details.
Raster Data Bands#
A single band of a raster data set may be represented by the RasterDataBand
class. This class is a simple wrapper of a
wiser.raster.RasterDataSet object, that also includes the index of the
referenced band.
- class RasterDataBand(dataset: wiser.raster.dataset.RasterDataSet, band_index: int)#
Bases:
wiser.raster.dataset.RasterBand,wiser.raster.serializable.SerializableA helper class to represent a single band of a raster data set. This is a simple wrapper around class:RasterDataSet that also tracks a single band.
Initialization
- get_band_info()#
- get_band_index() int#
Return the 0-based index of the band in the backing data set.
- get_data(filter_data_ignore_value: bool = True) numpy.ndarray#
Returns a numpy 2D array of this band’s data.
The numpy array is configured such that the pixel (x, y) values are at element
array[y][x].If the data-set has a “data ignore value” and filter_data_ignore_value is also set to True, the array will be filtered such that any element with the “data ignore value” will be filtered to NaN. Note that this filtering will impact performance.
- get_stats() wiser.raster.dataset.BandStats#
Returns statistics of this band’s data, wrapped in a class:BandStats object.
- is_metadata_same(other: wiser.raster.dataset.RasterDataBand) bool#
- static deserialize_into_class(serializedForm: wiser.raster.serializable.SerializedForm) wiser.raster.dataset.RasterDataBand#
- get_serialized_form() wiser.raster.serializable.SerializedForm#
Gives a tuple that represents all of the data needed to recreate this object. The first element is this class, so we can get the deserialize_into_class function The second element is a string that represents the file path to the dataset, or a numpy array that represents the data in the dataset. The third element is a dictionary that represents the metadata needed to recreate this object.
Spectra#
WISER has a rather complex class hierarchy to represent spectra, as they may
come from pixels in a spectral image cube, they may be from a spectral library,
they may be calculated from the area in a Region of Interest or the area around
a pixel in an image, and so forth. The base-type of all these different kinds
of spectra is the Spectrum class:
- class Spectrum#
Bases:
abc.ABC,wiser.raster.serializable.SerializableThe base class for representing spectra of interest to the user of the application.
Initialization
- get_id() Optional[int]#
- set_id(id: int) None#
- abstractmethod get_name() str#
- abstractmethod set_name(name: str)#
- abstractmethod get_source_name() str#
Returns the name of the spectrum’s source.
- num_bands() int#
Returns the number of spectral bands in the spectrum.
- get_bad_bands() numpy.ndarray#
Returns a boolean numpy array indicating which bands are bad (1 for bands to keep, 0 for bands to removed). If no bad bands are defined, returns None.
- get_shape() Tuple[int]#
Returns the shape of the spectrum. This is always simply
(num_bands).
- get_elem_type() numpy.dtype#
Returns the element-type of the spectrum.
- abstractmethod has_wavelengths() bool#
Returns True if this spectrum has wavelength units for all bands, False otherwise.
- abstractmethod get_wavelengths() List[astropy.units.Quantity]#
Returns a list of wavelength values corresponding to each band. The individual values are astropy values-with-units.
- abstractmethod get_wavelength_units() Optional[astropy.units.Unit]#
Returns the astropy unit corresponding to the wavelength.
- abstractmethod get_spectrum() numpy.ndarray#
Return the spectrum data as a 1D NumPy array.
- get_color() Optional[str]#
- set_color(color: str) None#
- is_editable() bool#
- is_discardable() bool#
- get_serialized_form() wiser.raster.serializable.SerializedForm#
This should return all of the information needed to recreate this object. The first element is this class, so we can get the deserialize_into_class function The second element is a string that represents the file path to the dataset, or a numpy array that represents the data in the dataset. The third element is a dictionary that represents the metadata needed to recreate this object.
- get_spectral_metadata() wiser.raster.dataset.SpectralMetadata#
- static deserialize_into_class(serializedForm: wiser.raster.serializable.SerializedForm) wiser.raster.spectrum.NumPyArraySpectrum#
This should recreate the object from the serialized form that is obtained from the get_serialized_form method.
Making a Spectrum from a NumPy Array#
A spectrum may be constructed from a NumPy array using the
NumPyArraySpectrum subclass:
- class NumPyArraySpectrum(arr: numpy.ndarray, name: Optional[str] = None, source_name: Optional[str] = None, wavelengths: Optional[List[astropy.units.Quantity]] = None, editable=True, discardable=True)#
Bases:
wiser.raster.spectrum.SpectrumThis class represents a spectrum that wraps a simple 1D NumPy array. This is generally used for computed spectra.
Initialization
- get_name() Optional[str]#
Returns the current name of the spectrum, or
Noneif no name has been assigned.
- set_name(name: Optional[str])#
Sets the name of the spectrum.
Nonemay be specified if the spectrum is to be unnamed.
- get_source_name() Optional[str]#
Returns the name of the spectrum’s source, or
Noneif no source name has been specified.
- set_source_name(name: str)#
- get_elem_type() numpy.dtype#
Returns the element-type of the spectrum.
- num_bands() int#
Returns the number of spectral bands in the spectrum.
- set_bad_bands(bad_bands: numpy.ndarray)#
Sets the bad bands array for this spectrum. 1 is keep, 0 is ignore.
- get_bad_bands()#
- has_wavelengths() bool#
Returns True if this spectrum has wavelength units for all bands, False otherwise.
- get_wavelengths() List[astropy.units.Quantity]#
Returns a list of wavelength values corresponding to each band. The individual values are astropy values-with-units. If the spectrum doesn’t have wavelengths, it returns a list from 0 to num_bands() - 1
- set_wavelengths(wavelengths: Optional[List[astropy.units.Quantity]])#
Sets the wavelength values that correspond to each band. The argument is a list of astropy values-with-units. Alternately, this method may be used to clear the wavelength information, by passing in
Noneas the argument.
- get_wavelength_units() Optional[astropy.units.Unit]#
- copy_spectral_metadata(source: wiser.raster.dataset.SpectralMetadata)#
- get_spectrum() numpy.ndarray#
Return the spectrum data as a 1D NumPy array.
- is_editable()#
- is_discardable()#
- __eq__(other: wiser.raster.spectrum.NumPyArraySpectrum) bool#
Region Of Interest#
A region of interest that is draw on the screen is represented by the Region Of Interest
class.
- class RegionOfInterest(name: Optional[str] = None, color: str = 'yellow')#
Represents a Region of Interest (abbreviated “ROI”) in the data being analyzed. The Region of Interest may specify multiple selections of various types, indicating the actual area comprising the ROI. Various other attributes may be specified as well, such as the color that the ROI is drawn in.
Initialization
- get_id() Optional[int]#
- set_id(id: int) None#
- __str__()#
- get_name()#
- set_name(name)#
- get_color() str#
Returns the color of the ROI as a string.
- set_color(color: str) None#
- get_description() Optional[str]#
- set_description(description: Optional[str]) None#
- get_selections() List[wiser.raster.selection.Selection]#
- add_selection(selection: wiser.raster.selection.Selection) None#
- del_selection(sel_index: int) None#
- get_metadata()#
- get_all_pixels() Set[Tuple[int, int]]#
Return a Python set containing the coordinates of all pixels that are a part of this Region of Interest. Each pixel coordinate will only appear once, even if the pixel appears within multiple selections in the ROI.
- get_bounding_box() PySide2.QtCore.QRect#
- pprint()#
DynamicInputDialog#
A utility for plugin developers to use to easily collect user input through a GUI.
- class DynamicInputDialog(dialog_title: Optional[str] = None, description: Optional[str] = None, parent=None)#
Bases:
PySide2.QtWidgets.QDialogThis class lets plugin creators make a dialog with inputs that they can set!
You make use of this class by using the function create_input_dialog
Initialization
- create_input_dialog(inputs: List[Tuple[str, str, wiser.gui.ui_library.DynamicInputType, Optional[List[Any]]]]) Optional[Dict[str, Any]]#
Create and execute a dynamic input dialog.
- Args:
inputs: A list of tuples defining the requested inputs:
[ ("<display name>", "<return_key>", DynamicInputType, [<combo_items> or None]), ... ] For ``DynamicInputType.COMBO_BOX``, provide a 4th element: a list of items for the combo box. No other ``DynamicInputType`` values need this element.- Returns:
Optional[Dict]:
{"<return_key>": value, ...}on Accept, orNoneon Cancel/Reject.
- _on_combo_changed(key: str, combo: PySide2.QtWidgets.QComboBox) None#
- _on_float_changed_no_units(key: str, text: str) None#
- _on_float_units_changed(key: str, line_edit: PySide2.QtWidgets.QLineEdit, unit_combo: PySide2.QtWidgets.QComboBox) None#
DynamicInputType#
TableDisplayWidget#
A utility for plugin developers to easily display information in a table.
- class TableDisplayWidget(parent=None)#
Bases:
PySide2.QtWidgets.QWidgetInitialization
- closed = 'Signal(...)'#
- create_table(header: List[str], rows: List[List[Any]], title: Optional[str] = None, description: Optional[str] = None)#
Creates a GUI item that has a description at the top and a QTableWidget with the specified header and the given rows. The table widget is only for display, so no interaction can be done.
- Args:
- header (List[str]):
A list of each of the column header names in order
- rows (List[List[Any]]):
Each of the rows to put into the table in order. The elements in the outer list correspond to rows in the table. The elements in the inner list correspond to columns for that row.
- description (str, optional):
Optional text placed above the table.
- title (str, optional):
Optional title displayed above the description.
- closeEvent(event)#
MatplotlibDisplayWidget#
A utility for plugin developers to easily display matplotlib plots.
- class MatplotlibDisplayWidget(parent=None)#
Bases:
PySide2.QtWidgets.QWidgetInitialization
- closed = 'Signal(...)'#
- create_plot(figure: matplotlib.figure.Figure, axes: matplotlib.axes.Axes, window_title: Optional[str] = None, description: Optional[str] = None)#
Creates a QWidget that containts the figure and axes passed in. You can optionally set the window title and provide a description.
- closeEvent(event)#