{"id":28218255,"url":"https://github.com/aburrell/pyintensityfeatures","last_synced_at":"2026-02-17T06:32:50.741Z","repository":{"id":284436048,"uuid":"954923667","full_name":"aburrell/pyIntensityFeatures","owner":"aburrell","description":"Identify features, such as auroral luminosity boundaries, in imager data","archived":false,"fork":false,"pushed_at":"2025-03-28T14:36:03.000Z","size":1391,"stargazers_count":1,"open_issues_count":14,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-09-23T13:55:35.858Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/aburrell.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2025-03-25T20:34:41.000Z","updated_at":"2025-04-06T03:33:16.000Z","dependencies_parsed_at":"2025-03-25T22:39:26.543Z","dependency_job_id":null,"html_url":"https://github.com/aburrell/pyIntensityFeatures","commit_stats":null,"previous_names":["aburrell/pyintensityfeatures"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/aburrell/pyIntensityFeatures","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aburrell%2FpyIntensityFeatures","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aburrell%2FpyIntensityFeatures/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aburrell%2FpyIntensityFeatures/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aburrell%2FpyIntensityFeatures/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/aburrell","download_url":"https://codeload.github.com/aburrell/pyIntensityFeatures/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aburrell%2FpyIntensityFeatures/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29535977,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-17T05:00:25.817Z","status":"ssl_error","status_checked_at":"2026-02-17T04:57:16.126Z","response_time":100,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-05-18T01:10:42.097Z","updated_at":"2026-02-17T06:32:50.720Z","avatar_url":"https://github.com/aburrell.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Documentation Status](https://readthedocs.org/projects/pyIntensityFeatures/badge/?version=latest)](http://pyIntensityFeatures.readthedocs.io/en/latest/?badge=latest)\n[DOI]\n[PyPI version]\n[![Test Status](https://github.com/aburrell/pyIntensityFeatures/actions/workflows/main.yml/badge.svg)](https://github.com/aburrell/pyIntensityFeatures/actions/workflows/main.yml)\n[![Coverage Status](https://coveralls.io/repos/github/aburrell/pyIntensityFeatures/badge.svg?branch=main)](https://coveralls.io/github/aburrell/pyIntensityFeatures?branch=main)\n\n\u003ch1\u003e \u003cimg width=\"128\" height=\"128\" src=\"https://raw.githubusercontent.com/aburrell/pyIntensityFeatures/refs/heads/main/docs/figures/pyIntensityFeatures_logo.png\" alt=\"Snakes marking the boundaries of the auroral oval\" title=\"pyIntensityFeatures logo\" style=\"float:left;\"\u003e\nOverview \u003c/h1\u003e\n\npyIntensityFeatures is a Python module that identifies features in imager\nintensity data.  It currently supports the identification of the Auroral\nLuminosity Boundaries (ALBs).  This package is designed to be data source\nagnostic and uses a fitting method based on Longden et al. (2009) to identify\nthe poleward and equatorward ALBs. Current support focuses on satellite-based\nimagers.\n\n# Dependencies\n\nThe required dependecies encompass:\n  * aacgmv2\n  * numpy\n  * pandas\n  * scipy\n  * xarray\n\nOptional dependencies include:\n  * apexpy\n\nTesting is performed using the python module, unittest.\n\n# Installation\n\nInstallation is now available through pypi\n\n```\n    $ pip install pyIntensityFeatures\n```\n\nYou may also checkout the repository and install it yourself:\n\n```\n    $ git clone git://github.com/aburrell/pyIntensityFeatures.git;\n```\n\nChange directories into the repository folder and build the wheel.\n\n```\n    $ cd pyIntensityFeatures/\n    $ python -m build .\n    $ pip install .\n```\n\nTo run the unit tests,\n\n```\n    $ python -m unittest discover\n```\n\n# Example\n\nThis example uses pysatNASA to obtain GUVI sdr-imaging data. You can use this\nas a template to create the necessary supporting functions to identify ALBs\nin your own data set. In iPython, run:\n\n```\nimport datetime as dt\nimport pyIntensityFeatures\nimport pysat\nfrom pysatNASA.instruments import timed_guvi\n```\n\nObtain the data from which you want to get boundaries.\n\n```\nstime = dt.datetime(2005, 1, 1)\nguvi = pysat.Instrument(inst_module=timed_guvi, tag='sdr-imaging',\n                        inst_id='high_res')\nguvi.download(start=stime)\n```\n\nWe need to create a cleaning function to select only good intensity data using\nthe supplied DQI flag. Because GUVI images the ionosphere at several\nwavelenghts and the satellite slice-detection function expects a 2D array for\nintensity, we also need to create a function that will add a single-channel\nintensity variable to the pysat GUVI Instrument.\n\n```\ndef create_channel_intensity(guvi, channel='LBHlong'):\n    \"\"\"Create a new variable for the single-channel auroral intensity.\n\n    Parameters\n    ----------\n    guvi : pysat.Instrument\n        pysat Instrument for the GUVI data.\n    channel : str\n        Desired channel name. (defualt='LBHlong')\n\n    \"\"\"\n    int_var = 'DISK_RECTIFIED_INTENSITY_DAY_AURORAL'\n    chan_var = 'nchan'\n\n    # Get the channel index\n    ichan = list(guvi[chan_var].values).index(channel)\n\n    # Create the new intensity variable\n    new_var = int_var.replace('DISK_RECTIFIED', channel)\n    guvi[new_var] = guvi[int_var][:, :, ichan]\n    return\n\n\ndef clean_guvi(guvi, int_var):\n    \"\"\"Get a mask for clean GUVI auroral intensity.\n\n    Parameters\n    ----------\n    guvi : pysat.Instrument\n        pysat Instrument for the GUVI data.\n    int_var : str\n        Intensity variable name\n\n    Returns\n    -------\n    clean_mask : array-like\n        2D array with a mask denoting the good data by the DQI auroral flag.\n\n    \"\"\"\n    flag_var = 'DQI_DAY_AURORAL'\n\n    # Get the clean mask from the flag\n    clean_mask = np.full(shape=guvi[int_var].values.shape, fill_value=False)\n    clean_mask = guvi[flag_var].values \u003c= 0\n\n    # The pyIntensityFeatures.instruments.satellites function requires time as\n    # the first dimension\n    return clean_mask.transpose()\n```\n\npysat allows methods that update or alter the instrument to be run when loading\nthe data.  Attach the `create_channel_intensity` function and load the\ndesired day.\n\n```\nguvi.custom_attach(create_channel_intensity, kwargs={'channel': 'LBHlong'})\nguvi.load(date=stime)\n```\n\nThen initialise an AuroralBounds class object. The `transpose` kwarg is set\nto `True` to reshape the GUVI longitude, latitude, and intensity data such that\nthe time dimension is first.\n\n```\nckwargs = {'int_var': 'LBHlong_INTENSITY_DAY_AURORAL'}\nguvi_alb = pyIntensityFeatures.AuroralBounds(guvi, 'time_auroral',\n                                            'PIERCEPOINT_DAY_LONGITUDE_AURORAL',\n                                            'PIERCEPOINT_DAY_LATITUDE_AURORAL',\n                                            'LBHlong_INTENSITY_DAY_AURORAL',\n                                            110.0, hemisphere=1,\n                                            opt_coords={'channel': 'LBHlong'},\n                                            transpose=True,\n                                            clean_func=clean_guvi,\n                                            clean_kwargs=ckwargs)\nprint(guvi_alb)\n```\n\nThe output should be as follows, with the start and end times set to reflect\nthe times at which data was provided:\n\n```\nAuroral Boundary object\n=======================\nInstrument Data: pysat Instrument object\n-----------------------\nPlatform: 'timed'\nName: 'guvi'\nTag: 'sdr-imaging'\nInstrument id: 'high_res'\n\nData Processing\n---------------\nCleaning Level: 'clean'\nData Padding: None\nCustom Functions: 1 applied\n    0: \u003cfunction create_channel_intensity\u003e\n     : Kwargs={'channel': 'LBHlong'}\n\nLocal File Statistics\n---------------------\nNumber of files: 711\nDate Range: 01 January 2005 --- 01 March 2005\n\nLoaded Data Statistics\n----------------------\nDate: 01 January 2005\nDOY: 001\nTime range: 01 January 2005 00:12:38 --- 01 January 2005 23:59:59\nNumber of Times: 23989\nNumber of variables: 70\n\nVariable Names:\nORBIT_DAY  LATITUDE_DAY  LONGITUDE_DAY\n              ...\nnCross  nCrossDayAur  LBHlong_INTENSITY_DAY_AURORAL \n\npysat Meta object\n-----------------\nTracking 8 metadata values\nMetadata for 90 standard variables\nMetadata for 40 global attributes\n\n\nData Variables\n--------------\nTime: time_auroral\nGeo Lon: PIERCEPOINT_DAY_LONGITUDE_AURORAL\nGeo Lat: PIERCEPOINT_DAY_LATITUDE_AURORAL\nIntensity: LBHlong_INTENSITY_DAY_AURORAL\nTranspose: {'PIERCEPOINT_DAY_LONGITUDE_AURORAL': True,\n            'PIERCEPOINT_DAY_LATITUDE_AURORAL': True,\n            'LBHlong_INTENSITY_DAY_AURORAL': True}\n\nCoordinate Attributes\n---------------------\nHemisphere: 1\nAltitude: 110.00 km\nOptional coords: {'channel': 'LBHlong', 'hemisphere': 1}\nStart time: 2005-01-01T00:12:38.575362000\nEnd time: 2005-01-01T23:59:59.722748000\n\nInstrument Functions\n--------------------\nSlicing: functools.partial(\u003cfunction get_auroral_slice\u003e, transpose=True)\nCleaning: functools.partial(\u003cfunction clean_guvi\u003e,\n                            int_var='LBHlong_INTENSITY_DAY_AURORAL')\n```\n\nNow, get the ALBs for the loaded data using the method defaults. This will\nupdate the `boundaries` attribute from `None` to a filled or empty xarray\nDataset. We will update the end time to be shorter than the amount of loaded\ndata to limit the processing time.\n\n```\nguvi_alb.etime = dt.datetime(2005, 1, 1, 0, 25)\nguvi_alb.set_boundaries()\nprint(guvi_alb.boundaries)\n```\n\nThis will yield one slice of intensity data in the Northern hemisphere with\nALBs:\n\n```\n\u003cxarray.Dataset\u003e\nDimensions:         (sweep_start: 1, mlt: 48, coeff: 12, lat: 31, sweep_end: 1)\nCoordinates:\n  * sweep_start     (sweep_start) datetime64[ns] 2005-01-01T00:24:35.572131\n  * sweep_end       (sweep_end) datetime64[ns] 2005-01-01T00:49:11.295639\n  * mlt             (mlt) float64 0.25 0.75 1.25 1.75 ... 22.75 23.25 23.75\n    channel         \u003cU7 'LBHlong'\n    hemisphere      int64 1\n  * lat             (lat) float64 59.5 60.5 61.5 62.5 ... 86.5 87.5 88.5 89.5\nDimensions without coordinates: coeff\nData variables:\n    eq_bounds       (sweep_start, mlt) float64 63.5 64.03 nan ... nan 62.98 nan\n    eq_uncert       (sweep_start, mlt) float64 0.741 0.8169 nan ... 2.219 nan\n    po_bounds       (sweep_start, mlt) float64 73.96 76.12 nan ... nan 71.24 nan\n    po_uncert       (sweep_start, mlt) float64 0.97 2.213 nan ... nan 0.6226 nan\n    eq_params       (sweep_start, mlt, coeff) float64 67.87 -2.033 ... nan nan\n    po_params       (sweep_start, mlt, coeff) float64 67.87 -2.033 ... nan nan\n    mean_intensity  (sweep_start, lat, mlt) float64 0.2494 0.8067 ... nan nan\n    std_intensity   (sweep_start, lat, mlt) float64 10.72 9.089 ... nan nan\n    num_intensity   (sweep_start, lat, mlt) float64 10.72 9.089 ... nan nan\nAttributes:\n    min_mlat_base:  59.0\n    mag_method:     ALLOWTRACE\n    mlat_inc:       1.0\n    mlt_inc:        0.5\n    un_threshold:   1.25\n    lt_out_bin:     5.0\n    max_iqr:        1.5\n```\n\nYou can then save the data as a NetCDF using the built-in xarray Dataset method.\nAll the kwargs from the `set_boundaries` method have been added as attributes to\nensure reproducibility of the identified boundaries.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faburrell%2Fpyintensityfeatures","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faburrell%2Fpyintensityfeatures","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faburrell%2Fpyintensityfeatures/lists"}