Source code for wwt_data_formats.layers
# -*- mode: python; coding: utf-8 -*-
# Copyright 2020 the .NET Foundation
# Licensed under the MIT License.
"""Serialized information about graphical "layers" in the WWT engine.
Not implemented:
- GreatCircleRouteLayer
- GroundOverlayLayer
- KmlLayer
- Object3d
- OrbitLayer
- SpreadSheetLayer
- TimeSeriesLayer
- VoTableLayer
- WmsLayer
"""
from __future__ import absolute_import, division, print_function
__all__ = '''
ImageSetLayer
Layer
LayerContainerReader
LayerContainerXml
'''.split()
from traitlets import Bool, Float, Instance, List, Unicode, Union
from . import LockedXmlTraits, XmlSer
from .filecabinet import FileCabinetReader
from .imageset import ImageSet
[docs]
class LayerContainerReader(object):
"""A collection of layers and reference frames."""
_reader = None
_info = None
def __init__(self, stream):
self._reader = FileCabinetReader(stream)
for fn in self._reader.filenames():
if fn.endswith('.wwtxml') and '\\' not in fn:
b = self._reader.read_file(fn)
text = b.decode('utf-8-sig')
self._info = LayerContainerXml.from_text(text)
if self._info is None:
raise Exception('found no ".wwtxml" file in WWTL file cabinet')
[docs]
def close(self):
self._reader.close()
self._reader = None
[docs]
@classmethod
def from_file(cls, path):
"""Deserialize layers from a WWTL "file cabinet" file.
Parameters
----------
path : string
The path of the file-cabinet file.
Returns
-------
An initialized instance of the class.
"""
f = open(path, 'rb')
return cls(f)
@property
def layers(self):
return self._info.layers
[docs]
def read_layer_file(self, layer, extension):
"""Read a data file associated with a layer in this container.
Parameters
----------
layer : :class:`Layer`
One of the layer objects found in this container.
extension : string
The file extension of the associated data file, including
a leading period. For instance, ".txt".
Returns
-------
The contents of the data file as a :class:`bytes` object.
"""
# In most examples I've seen, the layer file is in a subdirectory
# grouped by the layer's UUID. But in one example (David Weigel
# LMC WWTL file, June 2020), it's not in the subdirectory.
fn = self._info.id + '\\' + layer.id + extension
if fn in self._reader.filenames():
return self._reader.read_file(fn)
return self._reader.read_file(layer.id + extension)
[docs]
class LayerContainerXml(LockedXmlTraits):
"""The XML serialization of the layer collection information."""
id = Unicode('').tag(xml=XmlSer.attr('ID'))
"""A UUID-format randomized identifier for this layer collection.
Data files associated with this layer collection that come in the WWTL
cabinet file will be placed in a subdirectory whose name is this ID.
"""
# ignore ReferenceFrames for now
layers = List(
trait = Union([
Instance('wwt_data_formats.layers.ImageSetLayer', args=()),
]),
default_value = ()
).tag(xml=XmlSer.wrapped_inner_list('Layers'))
"A list of the layers stored in this container."
def _tag_name(self):
return 'LayerContainer'
[docs]
class Layer(LockedXmlTraits):
"""Generic parent class for serializable WWT layers."""
id = Unicode('').tag(xml=XmlSer.attr('Id'))
"""A UUID-format randomized identifier for this layer collection.
Data files associated with this layer collection that come in the WWTL
cabinet file will be placed in a subdirectory whose name is this ID.
"""
layer_type = Unicode('').tag(xml=XmlSer.attr('Type'))
"A textual representation of this layer's type."
name = Unicode('').tag(xml=XmlSer.attr('Name'))
"A user-facing name for this layer."
reference_frame = Unicode('').tag(xml=XmlSer.attr('ReferenceFrame'))
"The name of the reference frame relative to which this layer is positioned."
opacity = Float(1.0).tag(xml=XmlSer.attr('Opacity'))
# Skipping: Color, StartTime, Endtime, FadeSpan, FadeType
def _tag_name(self):
return 'Layer'
def _layertype_name(self):
raise NotImplementedError()
def _check_elem_is_this_type(self, elem):
return elem.attrib.get('Type', '') == self._layertype_name()
[docs]
class ImageSetLayer(Layer):
extension = Unicode('').tag(xml=XmlSer.attr('Extension'))
"""The filename extension for the underlying image if this is a SkyImage
layer. This includes the period, e.g. ".tif"."""
override_default = Bool(False).tag(xml=XmlSer.attr('OverrideDefault'))
image_set = Instance(ImageSet).tag(xml=XmlSer.inner('ImageSet'))
# Skipping: ScaleType, MinValue, MaxValue
def _layertype_name(self):
return 'TerraViewer.ImageSetLayer'