Writing out PVL text¶
This documentation explains how you can use pvl.dump()
and
pvl.dumps()
so you can change, add, and/or write out a Python
dict
-like object as PVL text either to a str
or
a file. This documentation assumes that you’ve read about how to
parse PVL text and know how pvl.load()
and
pvl.loads()
work.
The examples primarily use an ISIS Cube image label format, which typically doesn’t conform to PDS 3 standards, so pay attention to the differences between the PVL text that is loaded, versus the PDS 3-compliant PVL text that is dumped.
However, this library can write/alter any PVL compliant label.
Table of Contents
Writing PVL Text to a File¶
The pvl.dump()
function allows you to write out a dict
-like
Python object (typically a pvl.PVLModule
object) to a file as PVL
text.
Simple Use¶
Read a label from a file:
>>> import pvl
>>> pvl_file = 'tests/data/pds3/tiny1.lbl'
>>> label = pvl.load(pvl_file)
>>> print(label)
PVLModule([
('PDS_VERSION_ID', 'PDS3')
])
… then you can change a value:
>>> label['PDS_VERSION_ID'] = 42
>>> print(label)
PVLModule([
('PDS_VERSION_ID', 42)
])
… then add keys to the label
object:
>>> label['New_Key'] = 'New_Value'
>>> print(label)
PVLModule([
('PDS_VERSION_ID', 42)
('New_Key', 'New_Value')
])
… and then write out the PVL text to a file:
>>> pvl.dump(label, 'new.lbl')
54
pvl.dump()
returns the number of characters written to the file.
Changing A Key¶
More complicated parameter value change.
Load some PVL text from a file:
>>> import pvl
>>> img = 'tests/data/pattern.cub'
>>> label = pvl.load(img)
>>> print(label['IsisCube']['Core']['Format'])
Tile
… then change key ‘Format’ to ‘Changed_Value’:
>>> label['IsisCube']['Core']['Format'] = 'Changed_Value'
… then writing out file with new value:
>>> new_file = 'new.lbl'
>>> pvl.dump(label, new_file)
494
If you then try to show the changed value in the file, you’ll get an error:
>>> new_label = pvl.load(new_file)
>>> print(new_label['IsisCube']['Core']['Format'])
Traceback (most recent call last):
...
KeyError: 'Format'
This is because the default for pvl.dump()
and pvl.dumps()
is to write out
PDS3-Standards-compliant PVL, in which the parameter values (but not the aggregation
block names) are uppercased:
>>> print(new_label['IsisCube']['Core'].keys())
KeysView(['STARTBYTE', 'FORMAT', 'TILESAMPLES', 'TILELINES', 'Dimensions', 'Pixels'])
>>> print(new_label['IsisCube']['Core']['FORMAT'])
Changed_Value
Clean up:
>>> import os
>>> os.remove(new_file)
Yes, this case difference is weird, yes, this means that you need
to be aware of the case of different keys in your pvl.PVLModule
objects.
Writing PVL Text to a String¶
The pvl.dumps()
function allows you to convert a dict
-like
Python object (typically a pvl.PVLModule
object) to a Python
str
object which contains the PVL text.
Simple Use¶
Get started, as above:
>>> import pvl
>>> pvl_file = 'tests/data/pds3/tiny1.lbl'
>>> label = pvl.load(pvl_file)
>>> print(label)
PVLModule([
('PDS_VERSION_ID', 'PDS3')
])
… then change a value, and add keys:
>>> label['PDS_VERSION_ID'] = 42
>>> label['New_Param'] = 'New_Value'
>>> print(label)
PVLModule([
('PDS_VERSION_ID', 42)
('New_Param', 'New_Value')
])
… then write to a string:
>>> print(pvl.dumps(label))
PDS_VERSION_ID = 42
NEW_PARAM = New_Value
END
Here we can see the effects of the PDS3LabelEncoder in the default
behavior of pvl.dumps()
: it uppercases the parameters, and
puts a blank line after the END statement. If we were to use the PVLEncoder,
you can see different behavior:
>>> print(pvl.dumps(label, encoder=pvl.encoder.PVLEncoder()))
PDS_VERSION_ID = 42;
New_Param = New_Value;
END;
Adding A Key¶
More complicated:
>>> import pvl
>>> pvl_file = 'tests/data/pds3/group1.lbl'
>>> label = pvl.load(pvl_file)
>>> print(label)
PVLModule([
('PDS_VERSION_ID', 'PDS3')
('IMAGE',
{'CHECKSUM': 25549531,
'MAXIMUM': 255,
'STANDARD_DEVIATION': 16.97019})
('SHUTTER_TIMES', PVLGroup([
('START', 1234567)
('STOP', 2123232)
]))
])
… then add a new key and value to a sub group:
>>> label['New_Key'] = 'New_Value'
>>> label['IMAGE']['New_SubKey'] = 'New_SubValue'
>>> print(label)
PVLModule([
('PDS_VERSION_ID', 'PDS3')
('IMAGE',
{'CHECKSUM': 25549531,
'MAXIMUM': 255,
'New_SubKey': 'New_SubValue',
'STANDARD_DEVIATION': 16.97019})
('SHUTTER_TIMES', PVLGroup([
('START', 1234567)
('STOP', 2123232)
]))
('New_Key', 'New_Value')
])
… then when we dump, the default is to write PDS3 Labels, so the parameters are uppercased:
>>> print(pvl.dumps(label))
PDS_VERSION_ID = PDS3
OBJECT = IMAGE
MAXIMUM = 255
STANDARD_DEVIATION = 16.97019
CHECKSUM = 25549531
NEW_SUBKEY = New_SubValue
END_OBJECT = IMAGE
GROUP = SHUTTER_TIMES
START = 1234567
STOP = 2123232
END_GROUP = SHUTTER_TIMES
NEW_KEY = New_Value
END
Example with an ISIS cube file¶
>>> import pvl
>>> img = 'tests/data/pattern.cub'
>>> label = pvl.load(img)
>>> label['New_Key'] = 'New_Value'
>>> label_string = pvl.dumps(label)
>>> print(label_string)
OBJECT = IsisCube
OBJECT = Core
STARTBYTE = 65537
FORMAT = Tile
TILESAMPLES = 128
TILELINES = 128
GROUP = Dimensions
SAMPLES = 90
LINES = 90
BANDS = 1
END_GROUP = Dimensions
GROUP = Pixels
TYPE = Real
BYTEORDER = Lsb
BASE = 0.0
MULTIPLIER = 1.0
END_GROUP = Pixels
END_OBJECT = Core
END_OBJECT = IsisCube
OBJECT = Label
BYTES = 65536
END_OBJECT = Label
NEW_KEY = New_Value
END
PVL text for ISIS program consumption¶
There are a number of ISIS programs that take PVL text files as a
way of allowing users to provide more detailed inputs. To write
PVL text that is readable by ISIS, you can use the
pvl.encoder.ISISEncoder
. Here’s an example of creating a map file
used by the ISIS program cam2map
. Since cam2map
needs the
‘Mapping’ aggregation to be a PVL Group, you must use the
pvl.PVLGroup
object to assign to ‘Mapping’ rather than
just a dict-like (which gets encoded as a PVL Object by default).
You’d normally use pvl.dump()
to write to a file, but we use
pvl.dumps()
here to show what you’d get:
>>> import pvl
>>> subsc_lat = 10
>>> subsc_lon = 10
>>> map_pvl = {'Mapping': pvl.PVLGroup({'ProjectionName': 'Orthographic',
... 'CenterLatitude': subsc_lat,
... 'CenterLongitude': subsc_lon})}
>>> print(pvl.dumps(map_pvl, encoder=pvl.encoder.ISISEncoder()))
Group = Mapping
ProjectionName = Orthographic
CenterLatitude = 10
CenterLongitude = 10
End_Group = Mapping
END
Pre-1.0 pvl dump behavior¶
If you don’t like the new default behavior of writing out PDS3 Label Compliant PVL text, then just using an encoder with some different settings will get you the old style:
>>> import pvl
>>> img = 'tests/data/pattern.cub'
>>> label = pvl.load(img)
>>> print(pvl.dumps(label))
OBJECT = IsisCube
OBJECT = Core
STARTBYTE = 65537
FORMAT = Tile
TILESAMPLES = 128
TILELINES = 128
GROUP = Dimensions
SAMPLES = 90
LINES = 90
BANDS = 1
END_GROUP = Dimensions
GROUP = Pixels
TYPE = Real
BYTEORDER = Lsb
BASE = 0.0
MULTIPLIER = 1.0
END_GROUP = Pixels
END_OBJECT = Core
END_OBJECT = IsisCube
OBJECT = Label
BYTES = 65536
END_OBJECT = Label
END
>>> print(pvl.dumps(label, encoder=pvl.PVLEncoder(end_delimiter=False)))
...
BEGIN_OBJECT = IsisCube
BEGIN_OBJECT = Core
StartByte = 65537
Format = Tile
TileSamples = 128
TileLines = 128
BEGIN_GROUP = Dimensions
Samples = 90
Lines = 90
Bands = 1
END_GROUP = Dimensions
BEGIN_GROUP = Pixels
Type = Real
ByteOrder = Lsb
Base = 0.0
Multiplier = 1.0
END_GROUP = Pixels
END_OBJECT = Core
END_OBJECT = IsisCube
BEGIN_OBJECT = Label
Bytes = 65536
END_OBJECT = Label
END
… of course, to really get the true old behavior, you should also use the carriage return/newline combination line endings, and encode the string as a bytearray, since that is the Python type that the pre-1.0 library produced:
>>> print(pvl.dumps(label, encoder=pvl.PVLEncoder(end_delimiter=False,
... newline='\r\n')).encode())
b'BEGIN_OBJECT = IsisCube\r\n BEGIN_OBJECT = Core\r\n StartByte = 65537\r\n Format = Tile\r\n TileSamples = 128\r\n TileLines = 128\r\n BEGIN_GROUP = Dimensions\r\n Samples = 90\r\n Lines = 90\r\n Bands = 1\r\n END_GROUP = Dimensions\r\n BEGIN_GROUP = Pixels\r\n Type = Real\r\n ByteOrder = Lsb\r\n Base = 0.0\r\n Multiplier = 1.0\r\n END_GROUP = Pixels\r\n END_OBJECT = Core\r\nEND_OBJECT = IsisCube\r\nBEGIN_OBJECT = Label\r\n Bytes = 65536\r\nEND_OBJECT = Label\r\nEND'