Beispiel #1
0
    def __init__(self, root):
        assert (root.tag == self.tagInNs('LgFormat')
                ), 'Wrong root element {:s}'.format(root.tag)
        myName = self.elemID(root)
        # Tracks, create a list of Track.Track objects
        # NOTE: This is not quite like the LIS track buildup where the tracks are single
        # and in order so when we encounter T23 we span tracks T2 and T3 (by ordinal).
        # Here we get a separate track description 'track23' which has the width and grid
        # generator, 'track2' and 'track3' may be blank.
        # I suppose also that the order of tracks in the XML file is not significant but
        # we treat is so here.
        myTrackS = []
        # Make an additional data structure that maps the track name to ordinal
        self._trackNameOrdinalMap = {}
        for t, aT in enumerate(root.findall(self.tagInNs('LgTrack'))):
            l = Coord.Dim(self.float(aT, self.tagInNs('LeftPosition'), 0.0),
                          'in')
            r = Coord.Dim(self.float(aT, self.tagInNs('RightPosition'), 0.0),
                          'in')
            myTrackS.append(
                Track.Track(
                    leftPos=l,
                    rightPos=r,
                    # Figure out track grid function
                    gridGn=self._retGridGen(aT),
                    plotXLines=self.bool(aT, self.tagInNs('IndexLinesVisible'),
                                         True),
                    plotXAlpha=self.bool(aT,
                                         self.tagInNs('IndexNumbersVisible'),
                                         False),
                ))
            trackID = self.elemID(aT)
            if trackID is None:
                raise ExceptionFILMCfgXMLRead(
                    'PhysFilmCfgXMLRead.__init__(): Missing track UniqueId'.
                    format(trackID))
            if trackID in self._trackNameOrdinalMap:
                raise ExceptionFILMCfgXMLRead(
                    'PhysFilmCfgXMLRead.__init__(): Duplicate track UniqueId="{:s}"'
                    .format(trackID))
            # Add the track ordinal
            self._trackNameOrdinalMap[trackID] = t


#        myXPath = self.tagsInNs('LgFormat', 'LgVerticalScale', 'IndexScaler')
#        myElem = root.find(myXPath)
#        myX = self.int(root, self.tagsInNs('LgVerticalScale', 'IndexScaler'))
#        print()
#        print('TRACE:', myXPath, myElem, myX)
        myXScale = self.int(root,
                            self.tagsInNs('LgVerticalScale', 'IndexScaler'),
                            None)
        assert (myXScale is not None)
        super().__init__(
            theName=myName,
            theTracks=myTrackS,
            # This does not seem to be used by the parent class
            theDest=None,
            theX=myXScale,
        )
Beispiel #2
0
 def test_05(self):
     """TestPlotTrack.test_05(): Construct a blank track without failure."""
     myT = Track.Track(leftPos=Coord.Dim(3.2, 'in'),
                       rightPos=Coord.Dim(5.6, 'in'),
                       gridGn=None)
     self.assertEqual(Coord.Dim(3.2, 'in'), myT.left)
     self.assertEqual(Coord.Dim(5.6, 'in'), myT.right)
     self.assertFalse(myT.hasGrid)
Beispiel #3
0
 def test_11(self):
     """TestPlotTrack.test_11(): Construct a linear track with left edge < right edge - fails."""
     try:
         Track.Track(leftPos=Coord.Dim(5.6, 'in'),
                     rightPos=Coord.Dim(3.2, 'in'),
                     gridGn=Track.genLinear10)
         self.fail('Track.ExceptionTotalDepthLISPlotTrack not raised')
     except Track.ExceptionTotalDepthLISPlotTrack:
         pass
Beispiel #4
0
 def test_01(self):
     """TestPlotTrack.test_01(): Construct a linear track without failure and check default values."""
     myT = Track.Track(leftPos=Coord.Dim(3.2, 'in'),
                       rightPos=Coord.Dim(5.6, 'in'),
                       gridGn=Track.genLinear10)
     self.assertEqual(Coord.Dim(3.2, 'in'), myT.left)
     self.assertEqual(Coord.Dim(5.6, 'in'), myT.right)
     self.assertTrue(myT.hasGrid)
     self.assertTrue(myT.plotXLines)
     self.assertFalse(myT.plotXAlpha)
Beispiel #5
0
    def test_03(self):
        """TestPlotTrack.test_03(): Construct a log track and plot it in SVG."""
        myT = Track.Track(
            leftPos=Coord.Dim(3.2, 'in'), 
            rightPos=Coord.Dim(5.6, 'in'),
            gridGn=Track.genLog10Decade2
        )
#        myCnv = Plot.Canvas(Coord.Dim(6, 'in'), Coord.Dim(8, 'in'), Plot.MarginQtrInch)
#        myViewPort = Coord.Box(
#            width=myCnv.width,
#            depth=myCnv.depth,
#        )
        myViewPort = Coord.Box(
            width=Coord.Dim(6, 'in'),
            depth=Coord.Dim(8, 'in'),
        )
        myTl = Coord.Pt(Coord.Dim(0.25, 'in'), Coord.Dim(0.25, 'in'))
        myF = io.StringIO()
        with SVGWriter.SVGWriter(myF, myViewPort) as xS:
            myT.plotSVG(myTl, Coord.Dim(7.5, 'in'), xS)
        # print()
        # print(myF.getvalue())
#        self.maxDiff = None
        self.assertEqual("""<?xml version='1.0' encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg height="8.000in" version="1.1" width="6.000in" xmlns="http://www.w3.org/2000/svg">
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.750" x1="3.450in" x2="3.450in" y1="0.250in" y2="7.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.250" x1="3.811in" x2="3.811in" y1="0.250in" y2="7.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.250" x1="4.023in" x2="4.023in" y1="0.250in" y2="7.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.250" x1="4.172in" x2="4.172in" y1="0.250in" y2="7.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.250" x1="4.289in" x2="4.289in" y1="0.250in" y2="7.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.250" x1="4.384in" x2="4.384in" y1="0.250in" y2="7.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.250" x1="4.464in" x2="4.464in" y1="0.250in" y2="7.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.250" x1="4.534in" x2="4.534in" y1="0.250in" y2="7.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.250" x1="4.595in" x2="4.595in" y1="0.250in" y2="7.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.750" x1="4.650in" x2="4.650in" y1="0.250in" y2="7.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.250" x1="5.011in" x2="5.011in" y1="0.250in" y2="7.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.250" x1="5.223in" x2="5.223in" y1="0.250in" y2="7.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.250" x1="5.372in" x2="5.372in" y1="0.250in" y2="7.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.250" x1="5.489in" x2="5.489in" y1="0.250in" y2="7.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.250" x1="5.584in" x2="5.584in" y1="0.250in" y2="7.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.250" x1="5.664in" x2="5.664in" y1="0.250in" y2="7.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.250" x1="5.734in" x2="5.734in" y1="0.250in" y2="7.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.250" x1="5.795in" x2="5.795in" y1="0.250in" y2="7.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.750" x1="5.850in" x2="5.850in" y1="0.250in" y2="7.750in"/>
</svg>
""",
            myF.getvalue()
        )
Beispiel #6
0
    def test_04(self):
        """TestPlotTrack.test_04(): Construct a lin/log/log track and plot it in SVG."""
        myTracks = [
            Track.Track(leftPos=Coord.Dim(0.0, 'in'),
                        rightPos=Coord.Dim(2.4, 'in'),
                        gridGn=Track.genLinear10),
            Track.Track(leftPos=Coord.Dim(3.2, 'in'),
                        rightPos=Coord.Dim(5.6, 'in'),
                        gridGn=Track.genLog10Decade2Start2),
            Track.Track(leftPos=Coord.Dim(5.6, 'in'),
                        rightPos=Coord.Dim(8, 'in'),
                        gridGn=Track.genLog10Decade2Start2),
        ]
        #        myCnv = Plot.Canvas(Coord.Dim(8.5, 'in'), Coord.Dim(6, 'in'), Plot.MarginQtrInch)
        #        myViewPort = Coord.Box(
        #            width=myCnv.width,
        #            depth=myCnv.depth,
        #        )
        myViewPort = Coord.Box(
            width=Coord.Dim(6, 'in'),
            depth=Coord.Dim(8, 'in'),
        )
        myTl = Coord.Pt(Coord.Dim(0.25, 'in'), Coord.Dim(0.25, 'in'))
        myF = io.StringIO()
        with SVGWriter.SVGWriter(myF, myViewPort) as xS:
            for t in myTracks:
                #xS.comment(str(t))
                t.plotSVG(myTl, Coord.Dim(5.5, 'in'), xS)
        # print()
        # print(myF.getvalue())


#        self.maxDiff = None
        self.assertEqual(
            """<?xml version='1.0' encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg height="8.000in" version="1.1" width="6.000in" xmlns="http://www.w3.org/2000/svg">
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.750" x1="0.250in" x2="0.250in" y1="0.250in" y2="5.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.250" x1="0.490in" x2="0.490in" y1="0.250in" y2="5.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.250" x1="0.730in" x2="0.730in" y1="0.250in" y2="5.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.250" x1="0.970in" x2="0.970in" y1="0.250in" y2="5.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.250" x1="1.210in" x2="1.210in" y1="0.250in" y2="5.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.500" x1="1.450in" x2="1.450in" y1="0.250in" y2="5.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.250" x1="1.690in" x2="1.690in" y1="0.250in" y2="5.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.250" x1="1.930in" x2="1.930in" y1="0.250in" y2="5.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.250" x1="2.170in" x2="2.170in" y1="0.250in" y2="5.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.250" x1="2.410in" x2="2.410in" y1="0.250in" y2="5.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.750" x1="2.650in" x2="2.650in" y1="0.250in" y2="5.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.750" x1="3.450in" x2="3.450in" y1="0.250in" y2="5.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.250" x1="3.661in" x2="3.661in" y1="0.250in" y2="5.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.250" x1="3.811in" x2="3.811in" y1="0.250in" y2="5.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.250" x1="3.928in" x2="3.928in" y1="0.250in" y2="5.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.250" x1="4.023in" x2="4.023in" y1="0.250in" y2="5.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.250" x1="4.103in" x2="4.103in" y1="0.250in" y2="5.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.250" x1="4.172in" x2="4.172in" y1="0.250in" y2="5.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.250" x1="4.234in" x2="4.234in" y1="0.250in" y2="5.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.750" x1="4.289in" x2="4.289in" y1="0.250in" y2="5.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.250" x1="4.650in" x2="4.650in" y1="0.250in" y2="5.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.250" x1="4.861in" x2="4.861in" y1="0.250in" y2="5.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.250" x1="5.011in" x2="5.011in" y1="0.250in" y2="5.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.250" x1="5.128in" x2="5.128in" y1="0.250in" y2="5.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.250" x1="5.223in" x2="5.223in" y1="0.250in" y2="5.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.250" x1="5.303in" x2="5.303in" y1="0.250in" y2="5.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.250" x1="5.372in" x2="5.372in" y1="0.250in" y2="5.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.250" x1="5.434in" x2="5.434in" y1="0.250in" y2="5.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.750" x1="5.489in" x2="5.489in" y1="0.250in" y2="5.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.750" x1="5.850in" x2="5.850in" y1="0.250in" y2="5.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.750" x1="5.850in" x2="5.850in" y1="0.250in" y2="5.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.250" x1="6.061in" x2="6.061in" y1="0.250in" y2="5.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.250" x1="6.211in" x2="6.211in" y1="0.250in" y2="5.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.250" x1="6.328in" x2="6.328in" y1="0.250in" y2="5.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.250" x1="6.423in" x2="6.423in" y1="0.250in" y2="5.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.250" x1="6.503in" x2="6.503in" y1="0.250in" y2="5.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.250" x1="6.572in" x2="6.572in" y1="0.250in" y2="5.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.250" x1="6.634in" x2="6.634in" y1="0.250in" y2="5.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.750" x1="6.689in" x2="6.689in" y1="0.250in" y2="5.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.250" x1="7.050in" x2="7.050in" y1="0.250in" y2="5.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.250" x1="7.261in" x2="7.261in" y1="0.250in" y2="5.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.250" x1="7.411in" x2="7.411in" y1="0.250in" y2="5.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.250" x1="7.528in" x2="7.528in" y1="0.250in" y2="5.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.250" x1="7.623in" x2="7.623in" y1="0.250in" y2="5.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.250" x1="7.703in" x2="7.703in" y1="0.250in" y2="5.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.250" x1="7.772in" x2="7.772in" y1="0.250in" y2="5.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.250" x1="7.834in" x2="7.834in" y1="0.250in" y2="5.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.750" x1="7.889in" x2="7.889in" y1="0.250in" y2="5.750in"/>
  <line stroke="black" stroke-opacity="1.000" stroke-width="0.750" x1="8.250in" x2="8.250in" y1="0.250in" y2="5.750in"/>
</svg>
""", myF.getvalue())
 def test_00(self):
     """TestPlotTrack.test_00(): Construct a lin/log/log track with FEET 1/200 and plot it in SVG."""
     myCnv = Plot.Canvas(Coord.Dim(8.5, 'in'), Coord.Dim(12, 'in'),
                         PlotConstants.MarginQtrInch)
     myViewPort = Coord.Box(
         width=myCnv.width,
         depth=myCnv.depth,
     )
     myTopLeft = Coord.Pt(Coord.Dim(0.25, 'in'), Coord.Dim(0.25, 'in'))
     myF = io.StringIO()
     myTracks = [
         Track.Track(leftPos=Coord.Dim(0.0, 'in'),
                     rightPos=Coord.Dim(2.4, 'in'),
                     gridGn=Track.genLinear10),
         Track.Track(
             leftPos=Coord.Dim(2.4, 'in'),
             rightPos=Coord.Dim(3.2, 'in'),
             gridGn=None,
             plotXAxis=True,
         ),
         Track.Track(leftPos=Coord.Dim(3.2, 'in'),
                     rightPos=Coord.Dim(5.6, 'in'),
                     gridGn=Track.genLog10Decade2Start2),
         Track.Track(leftPos=Coord.Dim(5.6, 'in'),
                     rightPos=Coord.Dim(8, 'in'),
                     gridGn=Track.genLog10Decade2Start2),
     ]
     with SVGWriter.SVGWriter(myF, myViewPort) as xS:
         # Do tracks first
         for t in myTracks:
             #xS.comment(str(t))
             if t.hasGrid:
                 t.plotSVG(
                     myTopLeft,
                     myCnv.depth - myCnv.margins.top - myCnv.margins.bottom,
                     xS,
                 )
         # Now XGrid
         # We are plotting up so xPosStart is at bottom
         myXposStart = myCnv.depth - myCnv.margins.bottom
         myXposStop = myCnv.margins.top
         myXg = XGrid.XGrid(200)
         # Plot depth lines
         for pos, stroke in myXg.genXPosStroke(xFrom=4307.5,
                                               xInc=False,
                                               units=b'FEET'):
             myXpos = myXposStart + pos
             if myXpos < myXposStop:
                 break
             for t in myTracks:
                 if t.hasGrid:
                     with SVGWriter.SVGLine(
                             xS,
                             Coord.Pt(t.left + myCnv.margins.left, myXpos),
                             Coord.Pt(t.right + myCnv.margins.left, myXpos),
                             attrs=Stroke.retSVGAttrsFromStroke(stroke)):
                         pass
         # Plot depth text
         textAttrs = {
             'text-anchor': 'end',
             'dominant-baseline': 'middle',
         }
         for pos, val in myXg.genXPosText(xFrom=4307.5,
                                          xInc=False,
                                          units=b'FEET'):
             myXpos = myXposStart + pos
             if myXpos < myXposStop:
                 break
             for t in myTracks:
                 if t.plotXAxis:
                     myPt = Coord.Pt(
                         t.right + myCnv.margins.left -
                         Coord.Dim(0.05, 'in'), myXpos)
                     with SVGWriter.SVGText(xS, myPt, 'Courier', 16,
                                            textAttrs):
                         xS.characters(str(val))
     print()
     print(myF.getvalue())
Beispiel #8
0
class PhysFilmCfgLISRead(PhysFilmCfg):
    """Tracks from a LIS FILM table, essentially the pair of GCOD and GDEC
    defines the left-to-right layout of the plot.
    
    Grid codes from analysis above:
    b'BBB ',  b'E1E ', b'E20 ', b'E2E ', b'E3E ', b'E40 ', b'E4E ', b'EB0 ', b'EEB ', b'EEE ', b'LLLL'
    
    The DSCA defines the top-to-bottom nature of the plot. 
    DSCA codes from analysis above:
    b'D200', b'D500', b'DM  ', b'S5  '
    
    But we can guess some others...
    """
    # The map of FILM track descriptions [pair of (name, decades)] to internal track representations
    GCOD_GDEC_MAP = {
        # T23 is 4 decades
        (b'E20 ', b'-4--'): [
            Track.Track(leftPos=Coord.Dim(0.0, 'in'),
                        rightPos=Coord.Dim(2.4, 'in'),
                        gridGn=Track.genLinear10),
            Track.Track(
                leftPos=Coord.Dim(2.4, 'in'),
                rightPos=Coord.Dim(3.2, 'in'),
                gridGn=None,
                plotXLines=False,
                plotXAlpha=True,
            ),
            Track.Track(leftPos=Coord.Dim(3.2, 'in'),
                        rightPos=Coord.Dim(5.6, 'in'),
                        gridGn=Track.genLog10Decade2Start2),
            Track.Track(leftPos=Coord.Dim(5.6, 'in'),
                        rightPos=Coord.Dim(8, 'in'),
                        gridGn=Track.genLog10Decade2Start2),
        ],
        (b'E2E ', b'-1--'): [
            Track.Track(leftPos=Coord.Dim(0.0, 'in'),
                        rightPos=Coord.Dim(2.4, 'in'),
                        gridGn=Track.genLinear10),
            Track.Track(
                leftPos=Coord.Dim(2.4, 'in'),
                rightPos=Coord.Dim(3.2, 'in'),
                gridGn=None,
                plotXLines=False,
                plotXAlpha=True,
            ),
            Track.Track(leftPos=Coord.Dim(3.2, 'in'),
                        rightPos=Coord.Dim(5.6, 'in'),
                        gridGn=Track.genLog10Decade1Start2),
            Track.Track(leftPos=Coord.Dim(5.6, 'in'),
                        rightPos=Coord.Dim(8, 'in'),
                        gridGn=Track.genLinear10),
        ],
        (b'E2E ', b'-2--'): [
            Track.Track(leftPos=Coord.Dim(0.0, 'in'),
                        rightPos=Coord.Dim(2.4, 'in'),
                        gridGn=Track.genLinear10),
            Track.Track(
                leftPos=Coord.Dim(2.4, 'in'),
                rightPos=Coord.Dim(3.2, 'in'),
                gridGn=None,
                plotXLines=False,
                plotXAlpha=True,
            ),
            Track.Track(leftPos=Coord.Dim(3.2, 'in'),
                        rightPos=Coord.Dim(5.6, 'in'),
                        gridGn=Track.genLog10Decade2Start2),
            Track.Track(leftPos=Coord.Dim(5.6, 'in'),
                        rightPos=Coord.Dim(8, 'in'),
                        gridGn=Track.genLinear10),
        ],
        (b'E3E ', b'-3--'): [
            Track.Track(leftPos=Coord.Dim(0.0, 'in'),
                        rightPos=Coord.Dim(2.4, 'in'),
                        gridGn=Track.genLinear10),
            Track.Track(
                leftPos=Coord.Dim(2.4, 'in'),
                rightPos=Coord.Dim(3.2, 'in'),
                gridGn=None,
                plotXLines=False,
                plotXAlpha=True,
            ),
            Track.Track(leftPos=Coord.Dim(3.2, 'in'),
                        rightPos=Coord.Dim(5.6, 'in'),
                        gridGn=Track.genLog10Decade3),
            Track.Track(leftPos=Coord.Dim(5.6, 'in'),
                        rightPos=Coord.Dim(8, 'in'),
                        gridGn=Track.genLinear10),
        ],
        (b'E4E ', b'-4--'): [
            Track.Track(leftPos=Coord.Dim(0.0, 'in'),
                        rightPos=Coord.Dim(2.4, 'in'),
                        gridGn=Track.genLinear10),
            Track.Track(
                leftPos=Coord.Dim(2.4, 'in'),
                rightPos=Coord.Dim(3.2, 'in'),
                gridGn=None,
                plotXLines=False,
                plotXAlpha=True,
            ),
            Track.Track(leftPos=Coord.Dim(3.2, 'in'),
                        rightPos=Coord.Dim(5.6, 'in'),
                        gridGn=Track.genLog10Decade4),
            Track.Track(leftPos=Coord.Dim(5.6, 'in'),
                        rightPos=Coord.Dim(8, 'in'),
                        gridGn=Track.genLinear10),
        ],
        (b'EEE ', b'----'): [
            Track.Track(leftPos=Coord.Dim(0.0, 'in'),
                        rightPos=Coord.Dim(2.4, 'in'),
                        gridGn=Track.genLinear10),
            Track.Track(
                leftPos=Coord.Dim(2.4, 'in'),
                rightPos=Coord.Dim(3.2, 'in'),
                gridGn=None,
                plotXLines=False,
                plotXAlpha=True,
            ),
            Track.Track(leftPos=Coord.Dim(3.2, 'in'),
                        rightPos=Coord.Dim(5.6, 'in'),
                        gridGn=Track.genLinear10),
            Track.Track(leftPos=Coord.Dim(5.6, 'in'),
                        rightPos=Coord.Dim(8, 'in'),
                        gridGn=Track.genLinear10),
        ],
        (b'EEB ', b'----'): [
            Track.Track(leftPos=Coord.Dim(0.0, 'in'),
                        rightPos=Coord.Dim(2.4, 'in'),
                        gridGn=Track.genLinear10),
            Track.Track(
                leftPos=Coord.Dim(2.4, 'in'),
                rightPos=Coord.Dim(3.2, 'in'),
                gridGn=None,
                plotXLines=False,
                plotXAlpha=True,
            ),
            Track.Track(leftPos=Coord.Dim(3.2, 'in'),
                        rightPos=Coord.Dim(5.6, 'in'),
                        gridGn=Track.genLinear10),
            Track.Track(leftPos=Coord.Dim(5.6, 'in'),
                        rightPos=Coord.Dim(8, 'in'),
                        gridGn=None),
        ],
        (b'EBE ', b'----'): [
            Track.Track(leftPos=Coord.Dim(0.0, 'in'),
                        rightPos=Coord.Dim(2.4, 'in'),
                        gridGn=Track.genLinear10),
            Track.Track(
                leftPos=Coord.Dim(2.4, 'in'),
                rightPos=Coord.Dim(3.2, 'in'),
                gridGn=None,
                plotXLines=False,
                plotXAlpha=True,
            ),
            Track.Track(leftPos=Coord.Dim(3.2, 'in'),
                        rightPos=Coord.Dim(5.6, 'in'),
                        gridGn=None),
            Track.Track(leftPos=Coord.Dim(5.6, 'in'),
                        rightPos=Coord.Dim(8, 'in'),
                        gridGn=Track.genLinear10),
        ],
        # All blank
        (b'BBB ', b'----'): [
            Track.Track(leftPos=Coord.Dim(0.0, 'in'),
                        rightPos=Coord.Dim(2.4, 'in'),
                        gridGn=None),
            Track.Track(
                leftPos=Coord.Dim(2.4, 'in'),
                rightPos=Coord.Dim(3.2, 'in'),
                gridGn=None,
                plotXLines=False,
                plotXAlpha=True,
            ),
            Track.Track(leftPos=Coord.Dim(3.2, 'in'),
                        rightPos=Coord.Dim(5.6, 'in'),
                        gridGn=None),
            Track.Track(leftPos=Coord.Dim(5.6, 'in'),
                        rightPos=Coord.Dim(8, 'in'),
                        gridGn=None),
        ],
        # Four track. Depth 1in, 4 tracks at 1.75in
        # Alternate could be depth 0.5in, 4 tracks at 1.875in
        (b'LLLL', b'1111'): [
            Track.Track(
                leftPos=Coord.Dim(0.0, 'in'),
                rightPos=Coord.Dim(1.0, 'in'),
                gridGn=None,
                plotXLines=False,
                plotXAlpha=True,
            ),
            Track.Track(leftPos=Coord.Dim(1.0, 'in'),
                        rightPos=Coord.Dim(2.75, 'in'),
                        gridGn=Track.genLinear10),
            Track.Track(leftPos=Coord.Dim(2.75, 'in'),
                        rightPos=Coord.Dim(4.5, 'in'),
                        gridGn=Track.genLinear10),
            Track.Track(leftPos=Coord.Dim(4.5, 'in'),
                        rightPos=Coord.Dim(6.25, 'in'),
                        gridGn=Track.genLinear10),
            Track.Track(leftPos=Coord.Dim(6.25, 'in'),
                        rightPos=Coord.Dim(8, 'in'),
                        gridGn=Track.genLinear10),
        ],
    }
    # Alternate mappings that are deemed 'equivalent'
    GCOD_GDEC_ALT_MAP = {
        # Read from 2953.S13
        (b'EEE ', b'EEE-'):
        GCOD_GDEC_MAP[(b'EEE ', b'----')],
        # Read from 12988.S3
        (b'EEB ', b'EEE-'):
        GCOD_GDEC_MAP[(b'EEB ', b'----')],
        # Read from 200099.S07 - ignore for the moment
        (b'EB0 ', b'----'):
        GCOD_GDEC_MAP[(b'EBE ', b'----')],
        #
        (b'E1E ', b'-4--'):
        GCOD_GDEC_MAP[(b'E4E ', b'-4--')],
        # Read from 300026.S01
        (b'E40 ', b'-4--'):
        GCOD_GDEC_MAP[(b'E20 ', b'-4--')],
    }
    #: X axis scale from a LIS FILM table
    #: DSCA codes from analysis above:
    #: b'D200', b'D500', b'DM  ', b'S5  '
    #: But we can guess some others...
    DSCA_MAP = {
        b'D20 ': 20,
        b'D40 ': 40,
        b'D200': 200,
        b'D240 ': 240,
        b'D500': 500,
        b'DM  ': 1000,
        # 5 inches per 100 feet
        b'S5  ': 12 * 100 // 5,
        # 2 inches per 100 feet
        b'S2  ': 12 * 100 // 2,
    }

    def __init__(self, theRow):
        """Reads a LogiRec.TableRow object and populates a CurveCfg.
        
        Example::
        
            MNEM  GCOD  GDEC  DEST  DSCA
            -----------------------------
            1     E20   -4--  PF1   D200
        """
        myName = Mnem.Mnem(theRow[b'MNEM'].value)
        myTrackS = self._retTracks(theRow[b'GCOD'].value,
                                   theRow[b'GDEC'].value)
        myDest = theRow[b'DEST'].value
        myDscaleKey = theRow[b'DSCA'].value
        super().__init__(theName=myName,
                         theTracks=myTrackS,
                         theDest=myDest,
                         theX=self.DSCA_MAP[myDscaleKey])

    def _retTracks(self, theGCOD, theGDEC):
        """Given a GCOD, GDEC pair this returns a list of Track.Track() objects or None."""
        # Kludge: Replace a trailing ' ' with a '-' so that the key look up works
        # when the '-' is missing.
        if len(theGDEC) == 4 and theGDEC[3:] == b' ':
            theGDEC = theGDEC[:3] + b'-'
        try:
            return self.GCOD_GDEC_MAP[(theGCOD, theGDEC)]
        except KeyError:
            # Try the alternate map
            logging.warning(
                'PhysFilmCfgLISRead._retTracks():'
                ' No key for GCOD={!r:s} GDEC={!r:s}, falling back to alternate.'
                .format(theGCOD, theGDEC))
            try:
                return self.GCOD_GDEC_ALT_MAP[(theGCOD, theGDEC)]
            except KeyError:
                pass
        raise ExceptionFILMCfg(
            'PhysFilmCfgLISRead._retTracks(): No key for GCOD={!r:s} GDEC={!r:s}'
            .format(theGCOD, theGDEC))

    def supportedFilmTracks(self):
        """A list of supported film (name, decade) pairs."""
        return sorted(self.GCOD_GDEC_MAP.keys())