Note: For updates to third-party libraries, see the IDL Release Notes inside the IDL installation.

New Features


BZIP2 Support


IDL can now compress and uncompress files in the BZIP2 compression format. See BZIP_COMPRESS and BZIP_UNCOMPRESS for details.

Embedded Python for IDL and ENVI


IDL now ships with a fully-featured Python distribution. This embedded Python includes a range of useful packages such as numpy, pandas, regex, urllib3, h5py, and the envipyengine. You can easily install new packages into a user-specific installation folder.

The new embedded Python removes any need to configure the IDL-Python bridge. When you execute your first Python command, IDL will automatically load the embedded Python. For example:

IDL> pd = Python.Import('pandas')
% Python is embedded version 3.13.
% Loaded DLM: PYTHON313.
% PYTHON_INIT: C:\Program Files\NV5\IDL92\bin\bin.x86_64\idl-python313.
IDL> dates = pd.date_range("20130101", periods=3)
IDL> df = pd.dataframe(randomu(seed, 4, 3), index = dates, columns = ["A", "B", "C", "D"])
IDL> print, df
A         B         C         D
2013-01-01  0.647128  0.954983  0.171173  0.664392
2013-01-02  0.251179  0.635682  0.986357  0.125033
2013-01-03  0.003626  0.229533  0.639266  0.297249

The embedded Python also comes with a new PyUtils class which lets you easily add or remove packages using familiar pip commands. For example, you can install the Beautiful Soup package, which is useful for parsing HTML and XML files:

IDL> PyUtils.PipInstall, 'beautifulsoup4'
Looking in indexes: https://pypi.python.org/simple
Collecting beautifulsoup4
Installing collected packages: beautifulsoup4
Successfully installed beautifulsoup4-4.13.3
Package beautifulsoup4 successfully installed in C:\Users\myusername\.idl\idl\python_idlx_x.

Now that it is installed we can try out the package:

IDL> bs4 = Python.Import('bs4')
IDL> myhtml = '<html><title>IDL is great</title></html>'
IDL> soup = bs4.BeautifulSoup(myhtml, 'html.parser')
IDL> print, soup.title.string
IDL is great

IDL 9.2 still ships with support for other Python versions in case you prefer your own installation. The PyUtils class can be used to access other compatible installations. For example, if you wanted to use your own install of Python 3.13, you can call the new PyUtils.Load to pick the appropriate Python bridge library. For example:

IDL> PyUtils.Load, 'python313'  ; your Python is 3.13

See Python Bridge and the PyUtils Class for details.

FILE_HASH


IDL has a new FILE_HASH function that lets you compute the cryptographic hash for a file or array of files. Cryptographic hashes are useful for ensuring that a file has all of the expected bytes and has not been tampered with or damaged. The new function can compute the MD5, SHA-1, SHA-2 (256 bit) and SHA-2 (512 bit) hashes. For example:

IDL> file = filepath('hdf5_test.h5', subdir=['examples', 'data'])
IDL> print, file_hash(file)
69121a6aa9bc4eed8bdbd668f98d5503
IDL> print, file_hash(file, /SHA256)
74e416059b19e63581f9d8d03266081ed6a9a9a18fa44c2e41709ff7a7c20dba

See FILE_HASH for details.

HttpRequest PATCH


The HttpRequest class (introduced in IDL 9.0) has a new ::Patch method that lets you make PATCH requests to a server. Unlike POST or PUT, a PATCH request allows you to update existing resources on the server.

For example, to upload a file helloworld.txt that contains the text "Hello World":

IDL> data = hash("name", "patched file", "file", "@helloworld.txt")
IDL> response = HttpRequest.Patch('https://httpbin.org/patch', multipart = data)
IDL> response.status
200
IDL> response.json()
{
  "args": {
  },
  "data": "",
  "files": {
    "file": "Hello World\r\n"
  },
  "form": {
    "name": "patched file"
  },
  ...
}

See HttpRequest for details.

Serial Port Communication


Using the new embedded Python bridge, IDL now supports communication with serial ports via the pyserial package.

For example, you can install the Python package:

pyutils.pipinstall, 'pyserial'

Next, plug your serial device into your computer. Then, in IDL, you can import the pyserial library and get a list of the available ports:

serial = Python.import('serial')
ports = Python.import('serial.tools.list_ports')
com_ports = ports.comports()
foreach p, com_ports do print, p.device

On Windows this will print out ports such as COM2 or COM3. On Linux and Mac the port names will look like /dev/ttyUSB0.

Once you have identified the correct port, you can then connect to it and begin communicating.

For example, here we connect an Arduino to a Windows machine and send a string back and forth:

serial = Python.import('serial')
arduino = serial.Serial(port="COM3", baudrate=9600, timeout=0.1)
wait, 2 ; give time to connect
!null = arduino.write(byte("hello"))
wait, 0.05
data = arduino.readline()
print, data
!null = arduino.close()

For the complete example see Serial Port Communication via Python Bridge.

Windows Command Line


On Windows, the IDL command line has been completely rewritten. The new command line has the following additional features:

  • Better performance when outputting large streams of data.

  • Colored text for the IDL or ENVI prompt, as well as informational messages or error messages.

Tip: To use the new Windows command line, you can either run the idl.exe application directly, or you can add the IDL bin directory to your system path and run idl from a Windows or bash shell.

Tab Completion

Pressing the TAB key in the new Windows command line will perform tab completion, first using IDL's command history (if there are any matching commands), or if there are no matches, then IDL tries to match file names on your local system.

For an example of file path completion, imagine you have two folder hierarchies on your system:

dir1/
  subdir/
    myimage.jpeg
dir2/

At the IDL command line, you could type:

IDL> read_jpeg, "./dir

If you press the tab key, IDL will print:

dir1/  dir2/

If you continue by typing a 1/, followed by another TAB key, IDL will fill in the rest of the subfolder path:

IDL> read_jpeg, "./dir1/subdir/

Hitting TAB again will then complete the file path:

IDL> read_jpeg, ".\dir1\subdir\myimage.jpeg

If there had been multiple files in that subdir, then IDL would have printed out all of the files, and you could have continued to type and hit TAB to narrow down the choices.

Updates


ASDF File Format Enhancements


IDL's support for ASDF (Advanced Scientific Data Format) has been enhanced with the following features:

  • Handles files with mixed endian byte orders

  • Supports reading files with strides

  • Supports reading streamed files

  • Supports reading and writing structured arrays

  • Supports reading and writing the ASCII data type

  • Support for BZIP2 compression

For more details see ASDF_PARSE and ASDF_WRITE.

CREATE_STRUCT has a new PRESERVE_CASE keyword


By default, when you create an IDL structure using either the curly braces { } or the CREATE_STRUCT function, the tag names are automatically converted to uppercase. In CREATE_STRUCT, you can use the new PRESERVE_CASE keyword to keep lowercase characters unchanged. For example:

IDL> a = create_struct('tag1', 1.0, 'TaG2', 2, /preserve_case)
IDL> help, a
  ** Structure <b5161400>, 2 tags, length=8, data length=6, refs=1:
  tag1            FLOAT           1.00000
  TaG2            INT              2
   
IDL> print, tag_names(a)
tag1 TaG2

Notes:

  1. In IDL, you can refer to tag names in structures using any combination of upper or lowercase letters; all matches for tag names are case insensitive. This applies consistently, even when working with arrays of structures that have mixed-case tag names. For example:
    IDL> a = create_struct('tag1', 1.0, 'TaG2', 2, /preserve_case)
    IDL> print, a.TAG1, a.tag1, a.TAG2, a.tag2
    1.00000    1.00000    2    2
    IDL> arr = replicate(a, 10)
    IDL> arr[5] = {TAG1: 2.0, TAG2: 3}
    IDL> print, tag_names(arr[5])
    tag1 TaG2

  2. Structures created using curly braces { } always have uppercase tags, regardless of the actual case used in your IDL code. This behavior is unchanged. For example:
    IDL> b = {tag1: 3.14, Tag2: 5}

    IDL> help, b

    ** Structure <83e80910>, 2 tags, length=8, data length=6, refs=1:

    TAG1   FLOAT 3.14000

    TAG2  INT   5

  3. Since tag matching is case insensitive, you cannot create a structure with two tag names that are identical except for case. For example, trying to create a structure with tag names "tag1" ang "TAG1" will throw an error.

HttpRequest has a simpler API for Downloading and Uploading Files


The HttpRequest class (introduced in IDL 9.0) allows you to make GET, POST, PUT, and DELETE requests to an HTTP or HTTPS server. In IDL 9.2 it is now much simpler to download and upload files.

First, the Get, Post, and Put methods have a new FILENAME keyword. Setting this keyword will download the response to a file instead of returning the data as a byte array or string. This allows you to download huge files from a URL directly to a file on your local file system. For example:

response = HttpRequest.Get('https://httpbin.org/', FILENAME='c:/temp/result.txt')
print, response.status_code
print, file_lines('c:/temp/result.txt')

Next, for POST and PUT, when uploading a file as part of a multipart form, you can now specify the file using a shortcut "@filepath" instead of creating an IDL structure. For example:

multipart = hash("name", "David Stern", "resume", "@c:/myplainresume.txt")
a = HttpRequest.Post(url, multipart=multipart)

See HttpRequest for details on these new features.

IDL Package Manager (IPM) support for IDL/ENVI Repository Portals, new API, and Publish


The IDL Package Manager (IPM) has been rewritten to use static methods. This provides an API that is cleaner and is consistent with other IDL classes such as HttpRequest.

The IPM also has a new Publish feature, which lets you upload IDL packages to a remote server.

In addition, a new IDL/ENVI Repository reader has been added. This allows you to create, upload, and install IDL or ENVI packages from within IDL. For example, if your server URL is myrepository.com, with port 8000, and your package name is ExtractAttributes, then the following code would create the package, upload it to the server, and then download and install it:

url = 'http://myrepository.com:8000/'
packagename = 'ExtractAttributes'
dir = getenv('HOME') + '/' + packagename
 
IPM.Create, dir, $
  name = packagename, $
  display_name = "Extract Attributes", $
  description = "Extracts the attributes table of a shapefile.", $
  tags = ["Vector"], $
  version = '1.0.0', $
  author = "David Stern", $
  dependencies = [{ $
    name: "DumpVectorTable", $
    version: ">=1.0.0"}, $
    { $
    name: "ReadShapefile", $
    version: "<2.0"} $
    ], $
  url = url

We can now add all of our required files, and then upload the newly-created package to the server:

IPM.Publish, dir, destination = 'http://myrepository.com:8000/'

To test your package, you can now install it:

IPM.Install, 'http://myrepository.com:8000/', name='ExtractAttributes', version='1.0.0'

IDL prints:

Package: ExtractAttributes, Version: 1.0.0 installed
Package: DumpVectorTable, Version: 1.2.0 installed
Package: ReadShapefile, Version: 1.9.0 installed

For details see IPM.

ISA has a new STRICT_ARRAY keyword


The ISA function currently has a /ARRAY keyword. This is useful for checking if a variable is indexable, like a regular IDL array, a list, or a hash.

ISA now has a new STRICT_ARRAY keyword that returns true only if the variable is truly an IDL array or structure. For example, a hash or list will return false, but an object array of hashes or lists will return true.

See ISA for details.

Physical Constants


The !CONST system variable contains values for the most common physical constants.

In 2019, the SI base units for the kilogram, ampere, kelvin, and mole were redefined in terms of natural physical constants instead of human artifacts. This was done by setting exact numerical values for the Planck constant (h), the elementary electric charge (e), the Boltzmann constant (kB), and the Avogadro constant (NA). Because of these redefinitions, the following constants have new values:

Name

Description

Old Value

New Value

alpha

Fine structure constant

7.2973525698 x 10-3

7.2973525643 x 10-3

e

Elementary charge

1.602176565 x 10-19 C

1.602176634 x 10-19 C

eps0

Vacuum electric permittivity

8.854187817 x 10-12 F/m

8.8541878188 x 10-12 F/m

F

Faraday constant

96485.3365 C/mol

96485.33212 C/mol

G

Gravitation constant

6.67428 x 10-11 m3/kg/s2

6.67430 x 10-11 m3/kg/s2

h

Planck constant

6.62606957 x 10-34 J s

6.62607015 x 10-34 J s

hbar

h/(2π)

1.054571726 x 10-34 J s

1.054571817 x 10-34 J s

k

Boltzmann constant

1.3806488 x 10-23 J/K

1.380649 x 10-23 J/K

me

Electron mass

9.10938291 x 10-31 kg

9.1093837139 x 10-31 kg

mn

Neutron mass

1.674927351 x 10-27 kg

1.67492750056 x 10-27 kg

mp

Proton mass

1.672621777 x 10-27 kg

1.67262192595 x 10-27 kg

mu0

Vacuum magnetic permeability

1.2566370614 x 10-6 N/A2

1.25663706127 x 10-6 N/A2

n0

Loschmidt constant

2.6867805 x 1025 m-3

2.686780111 x 1025 m-3

Na

Avogadro constant

6.02214129 x 1023 mol-1

6.02214076 x 1023 mol-1

parsec

Parsec distance

3.0856775814671912 x 1016 m

3.0856775814913673 x 1016 m

R

Molar gas constant

8.3144621 J/mol/K

8.3144626181532395 J/mol/K

re

Classical electron radius

2.8179403267 x 10-15 m

2.8179403205 x 10-15 m

rydberg

Rydberg constant

10973731.568539 m-1

10973731.568157 m-1

sigma

Stefan-Boltzmann constant

5.670373 x 10-8 W/m2/K4

5.670374419 x 10-8 W/m2/K4

u

Atomic mass constant

1.660538921 x 10-27 kg

1.66053906892 x 10-27 kg

Vm

Molar volume, ideal gas at STP

22.413968 x 10-3 m3/mol

22.41396954 x 10-3 m3/mol

While most of these values can be verified with suitable lab equipment, we are anxiously awaiting the return of our plucky IDL interns from their voyage to measure the new parsec distance.

See !CONST system variable for details.

Python::GetAttr method


The IDL-to-Python bridge has a new static method, Python::GetAttr. The Python::GetAttr static method retrieves an attribute from a Python object. The GetAttr method is useful if you want to specify your attribute as a string, or if you want to use the WRAP keyword to return the attribute as a Python object instead of converting to an IDL type.

Example


Create a Pandas DataFrame and then retrieve the "shape" attribute, which gives the length of each dimension:

np = python.import('numpy')
pd = python.import('pandas')
arr = (np.random).randn(6,4)
dates = pd.date_range("20130101", periods=6)
df = pd.dataframe(arr, index = dates, columns = ["A", "B", "C", "D"])
print, `shape = ${df.shape}`   ; use dot notation
shape = Python.getattr(df, 'shape', /wrap)
help, shape
print, shape

IDL prints:

shape = [6,4]
SHAPE           PYTHON  <ID=837>  <class 'tuple'>
(6, 4)

Note that in the call to GetAttr, we have used the WRAP keyword to return the Python tuple without conversion. See Python::GetAttr for details.

Shapefiles with huge integer attributes


Prior to IDL 9.2, if you had an ESRI shapefile with an integer attribute, the attribute values were always read into a 32-bit integer. This agrees with the ESRI shapefile specification, which limits integers to 32-bits. However, certain shapefiles can contain integers which are outside of the possible range (–2147483648 to 2147483647). In these cases, IDL will now automatically return these attribute fields as IDL type DOUBLE. Note that if you then write these attribute values out to a new shapefile, the type will automatically be Double, which conforms to the ESRI specification and should also allow these files to be successfully used with other tools.

SORT function has a new STABLE keyword


By default, when you call the SORT function on an array with identical elements, the returned indices are in an arbitrary order. The SORT function now has a new STABLE keyword which will preserve the order of identical elements:

a = [4.0, 3.5, 7.0, 1.0, 1.1, 7.0, 7.0] ; the 7.0 values are identical
indices = sort(a)
indices_stable = sort(a, /stable)
print, `${indices}`
print, `${indices_stable}`

IDL prints:

[3, 4, 1, 0, 5, 6, 2] ; note the arbitrary order of the indices 5,6,2
[3, 4, 1, 0, 2, 5, 6] ; the values equal to 7.0 are now in order 2,5,6

Similarly, if you use the ::Sort method on an IDL variable, you can now specify the STABLE keyword to return the values in the INDICES keyword in the correct order:

a = [4.0, 3.5, 7.0, 1.0, 1.1, 7.0, 7.0]
astable = a.sort(indices = indices_stable, /stable)
print, `${indices_stable}` ; the results are the same as above

See the SORT function and the IDL_Variable::Sort method for details.

IDL Extension for VS Code


The IDL for VSCode extension is available for free within Visual Studio Code. The extension can be easily downloaded and installed from the VS Code extensions page. The extension has the following new features:

  • Full debugging support, including halting execution.

  • An IDL Tutorial, consisting of a series of interactive IDL Notebooks that walk you through the IDL language.

For details visit the IDL for VSCode page.

MAKE_RT Enhancements


MAKE_RT has been optimized to produce a much smaller distribution. There are three new keywords:

  • BROWSER - by default MAKE_RT will not include the files needed for the IDL browser widget. Set this keyword to include those files.

  • PYTHON - by default MAKE_RT will not include the embedded Python in the IDL bin folder. Set this keyword to include those files.

  • VERBOSE - set this keyword to print out the full filepath for each copied file.

MAKE_RT now makes use of CLI_PROGRESS to display a progress bar:

IDL> make_rt,'helloWorld','C:\Users\<User>\IDLWorkspace\make_rt_copy_trgt'

IDL prints:

8.0% [###-------------------------------------] Filtering files
61.3% [########################----------------] Copying files
etc...

See MAKE_RT for details on all of these enhancements.

Trailing Commas in Array and Structure Creation


Now, when creating arrays, a trailing comma at the end of a sequence of values is ignored. For example:

a = [ $
  [10:19], $
  [20:29], $
  [30:39], $ ; this trailing comma is now ignored
]

You can easily add a fourth row:

a = [ $
  [10:19], $
  [20:29], $
  [30:39], $
  [40:49], $ ; new row, did not have to touch the third row
]

Similarly, for structures:

a = { $
  field1: 1, $
  field2: "2", $
  field3: 3.0, $ ; this trailing comma is now ignored
}

You can easily add a fourth row:

a = { $
  field1: 1, $
  field2: "2", $
  field3: 3.0, $
  field4: "four", $ ; new row, did not have to touch the third row
}

This feature makes it simpler to modify existing arrays or structures in code by just adding in another row without worrying about a missing comma. Having a trailing comma (or not) is completely optional and makes no difference to the created array or structure or its contained data.

WIDGET_INFO function has a new SCREENSHOT keyword


The SCREENSHOT keyword makes WIDGET_INFO return an [M, N, 3] byte image of the specified widget. Virtually all widgets can be captured, including top-level bases, sub-bases, browser widgets, and others. The only widgets that cannot be specified are menu bars, menus, menu items, and tree widget nodes. Images can be used in automated testing, report generation, etc.

See the WIDGET_INFO function for details.

WIDGET_TABLE has better handling for scroll bars and selection events


The WIDGET_TABLE function now adds scroll bars only when necessary, otherwise scroll bars are suppressed. You can now set X_SCROLL_SIZE = 0 or Y_SCROLL_SIZE = 0 to suppress scroll bars in the horizontal or vertical direction.

WIDGET_TABLE has a new WHEEL_EVENTS keyword, which enables the generation of wheel events whenever the mouse wheel is used to scroll the table widget.

WIDGET_TABLE has a new EXTEND_SELECTION keyword, which changes the selection event values when a row or column header is clicked. This allows you to distinguish between events where the user clicks inside the table versus when the user click on a row or column header.

WIDGET_TABLE now allows you to set both the foreground and background colors for the column and row headers. Header cells are indexed by using –1.

See WIDGET_TABLE for details.

WIDGET_TABLE can now repeat Format strings across rows or columns


The WIDGET_TABLE function has a new REPEAT_FORMAT keyword. If this keyword is set, and the FORMAT keyword is set to a vector or array that is smaller than the table size, then that format array is repeated across the additional rows or columns.

By setting FORMAT to a vector of strings, along with the REPEAT_FORMAT keyword, you can easily set the format for the entire table, where each row of the table will have the same formats.

See WIDGET_TABLE for details.

FILE_POLL_INPUT now works with Windows pipes


The FILE_POLL_INPUT function now supports Windows pipes, in addition to Windows sockets and Unix file descriptors. One use case for this occurs when IDL has SPAWN'd a child process that reads and writes to stdin and stdout with something like:

spawn, command, unit = pipe, /noshell

Now, IDL can check whether the child process has data available for reading:

result = file_poll_input(pipe, timeout = double)

This enables IDL applications to avoid blocking reads and thereby remain active. If the result is 1 then the IDL application can safely read (at least one byte) without blocking for data that may never come or arrive at some unknown, distant time.

See FILE_POLL_INPUT for details.

FFT on arm64 Macs has an updated implementation


Arm64 Mac IDL now ships with the Arm Performance Libraries version 25.04.  This provides performance improvements for one dimensional data.

See Also


See What's New (Previous IDL Releases) for an archive of What's New information.