SVG export: insert space to materialize hidden columns + other details

I’m using jalview to generate an (SVG) image for some small parts of a sequence alignment (protein). I’m attaching a PNG rendering of the SVG. I’m using a groovy script to hide most columns of the alignment, and I would like to know several things:

  • is it possible to better materialize the “break” in the sequence numbering ? For now, the only way to see the break is the rather discreet blue triangle and blue line just right of 332G;
  • is it possible to remove the amino acid after the number in the sequence above ?
  • is it possible to insert the number of the last amino acid on the right of each set of visible columns, to help figuring out the numbering ?

Thanks a lot

Vincent

Hi Vincent.

It looks like you have a reference sequence set, but then have hidden the reference sequence - is that correct ? Not having a reference sequence set would remove the letters from the alignment ruler, but then the ruler would count column numbers.

Jalview currently can’t do exactly what you ask for re materialise hidden columns, not showing the residue letter along side reference sequence numbering in the ruler and showing the position numbers on the left and right extent of the ruler - but it is fairly easy to implement them (though enhanced hidden column visibility will need some rigorous testing, since it means adjusting the MSA layout grid).

However, in the situation you’ve shown, we usually recommend using wrap mode if you want to have sequence numbers at the left and right hand side. You could perhaps try to enable that to see if it suits your needs ? In wrap mode Jalview can show sequence numbers at the left and right of each sequence.

Jim

Hi Jim,

First thanks for the prompt reply.

The reference sequence is Tc_CODH2, displayed, although I’m setting that programmatically from the groovy script searching for the sequence name in the viewport .getAlignment().getSequences() and setting it via .getAlignment().setSeqrep(sq). Is that the cause ?

The wrap mode is indeed much better for my needs, thanks !

About the spacing for hidden columns, I’d be happy to give it a try if time allows, can you point me to what needs adjustment and how to make sure I’m not messing up everything ?

Thanks !

Vincent

It is, though normally I would expect to see Tc_CODH2’s ID string shown in bold, which makes me think something isn’t quite right somewhere.

OK - but it isn’t entirely straightforward - there are two parts.
First you’d update the various renderers responsible for drawing the MSA data, ruler, and annotations:

  • jalview.gui.SeqCanvas.drawPanel(Graphics, int, int, int, int, int) contains the render loop for the MSA. It calls jalview.gui.SeqCanvas.draw(Graphics, int, int, int, int, int) for each visible segment.
  • jalview.gui.SeqCanvas.drawWrappedWidth(Graphics, int, int, int, int) - this is the render loop for visible segments of the MSA when it is rendered in Wrapped mode. Called by jalview.gui.SeqCanvas.drawWrappedPanel(Graphics, int, int, int).
  • jalview.gui.ScalePanel.drawScale(Graphics, int, int, int, int) renders the scale above the alignment
  • jalview.renderer.AnnotationRenderer.drawComponent(AwtRenderPanelI, AlignViewportI, Graphics, int, int, int) - renders each column of each alignment annotation row.

In each renderer, you’d need to add a few pixels to the x coordinate to introduce a gap at each hidden region boundary.

Next comes the real work - making sure all the geometry calculations take account of the additional gap introduced by each hidden region boundary. These calculation routines are used to resolve the column position under the mouse, or calculate how many columns of the alignment can fit into a particular number of pixels.

Because the MSA layout is a grid, the calculation is deceptively simple, and pragmatically easier to write again rather than create a central function, so there are lots of column=xpos/charWidth type calculations scattered across the jalview.gui classes. In most classes you’ll be relieved to read that there is a centralised function call, e.g. jalview.gui.SeqPanel.findColumn(MouseEvent) and jalview.gui.AnnotationPanel.getColumnForXPos(int), but I’m 99.9% certain there will be a few cases where the column calculation is being performed implicitly.

In summary, what I would suggest is take a look at each place where jalview.viewmodel.AlignmentViewport.getCharWidth() is called. I used eclipse’s ‘show call hierarchy’ to get a complete list, and list below the places where I would take a look first to see if there’s a geometry calculation of somekind being performed that needs to be adapted to account for additional space between hidden regions:

jalview.gui.SplitFrame.adjustLayout()
jalview.gui.AlignmentPanel.adjustmentValueChanged(AdjustmentEvent)
jalview.gui.AlignmentPanel.AlignmentPanel(…).new ComponentAdapter() {…}.componentResized(ComponentEvent)
jalview.gui.SeqCanvas.draw(Graphics, int, int, int, int, int)
jalview.gui.SequenceRenderer.drawBoxes(SequenceI, int, int, int)
jalview.gui.AnnotationPanel.drawComponent(Graphics, int, int)
jalview.gui.SequenceRenderer.drawCursor(Graphics, char, int, int)
jalview.gui.SeqCanvas.drawCursor(Graphics, int, int, int, int)
jalview.gui.AnnotationPanel.drawCursor(Graphics, SequenceI, int, int, int)
jalview.gui.SeqCanvas.drawHiddenColumnMarkers(Graphics, int, int, int)
jalview.gui.SequenceRenderer.drawHighlightedText(SequenceI, int, int, int, int)
jalview.gui.SeqCanvas.drawMappedPositions(SearchResultsI)
jalview.gui.SeqCanvas.drawMappedPositionsWrapped(SearchResultsI)
jalview.gui.SeqCanvas.drawNorthScale(Graphics, int, int, int)
jalview.gui.SeqCanvas.drawPanel(Graphics, int, int, int, int, int)
jalview.gui.SeqCanvas.drawPartialGroupOutline(Graphics2D, SequenceGroup, int, int, int, int, int)
jalview.gui.ScalePanel.drawScale(Graphics, int, int, int, int)
jalview.gui.SequenceRenderer.drawText(SequenceI, int, int, int)
jalview.gui.SeqCanvas.drawUnwrappedSelection(Graphics2D, SequenceGroup, int, int, int, int, int)
jalview.gui.SeqCanvas.drawVerticalScale(Graphics, int, int, int, boolean)
jalview.gui.SeqCanvas.drawWrappedDecorators(Graphics, int)
jalview.gui.SeqCanvas.drawWrappedSelection(Graphics2D, SequenceGroup, int, int, int)
jalview.gui.SeqCanvas.drawWrappedWidth(Graphics, int, int, int, int)
jalview.gui.SeqCanvas.fastPaint(int, int)
jalview.gui.AnnotationPanel.fastPaint(int)
jalview.gui.SeqCanvas.fastPaintWrappedAddRight(int)
jalview.gui.SeqPanel.findColumn(MouseEvent)
jalview.gui.AlignmentPanel.fontChanged()
jalview.gui.AlignmentPanel.getAlignmentDimension()
jalview.viewmodel.AlignmentViewport.getCharWidth()
jalview.gui.AnnotationPanel.getColumnForXPos(int)
jalview.gui.SeqCanvas.getLabelWidth(FontMetrics)
jalview.gui.SeqCanvas.getWrappedCanvasWidth(int)
jalview.gui.AlignmentPanel.makePNGImageMap(File, String)
jalview.gui.ScalePanel.mouseDragged(MouseEvent)
jalview.gui.SeqPanel.mouseDragged(MouseEvent)
jalview.gui.ScalePanel.mouseMoved(MouseEvent)
jalview.gui.ScalePanel.mousePressed(MouseEvent)
jalview.gui.ScalePanel.mouseReleased(MouseEvent)
jalview.gui.AnnotationPanel.paintComponent(Graphics)
jalview.gui.SeqCanvas.paintComponent(Graphics)
jalview.gui.SequenceRenderer.prepare(Graphics, boolean)
jalview.gui.AlignmentPanel.printUnwrapped(int, int, int, Graphics, Graphics)
jalview.renderer.seqfeatures.FeatureRenderer.renderFeature(Graphics, SequenceI, int, int, Color, int, int, int, boolean)
jalview.renderer.seqfeatures.FeatureRenderer.renderScoreFeature(Graphics, SequenceI, int, int, Color, int, int, int, byte, boolean)
jalview.gui.AlignmentPanel.setScrollValues(int, int)
jalview.gui.SeqCanvas.shiftWrappedAlignment(int)
jalview.gui.ScalePanelTest.testBuildPopupMenu()
jalview.gui.SeqCanvasTest.testCalculateWrappedGeometry_fromScrolled()
jalview.gui.SeqCanvasTest.testCalculateWrappedGeometry_noAnnotations()
jalview.gui.SeqCanvasTest.testCalculateWrappedGeometry_withAnnotations()
jalview.gui.SeqPanelTest.testFindColumn_unwrapped()
jalview.gui.SeqPanelTest.testFindColumn_wrapped()
jalview.gui.SeqPanelTest.testFindMousePosition_unwrapped()
jalview.gui.SeqPanelTest.testFindMousePosition_wrapped_annotations()
jalview.gui.SeqPanelTest.testFindMousePosition_wrapped_noAnnotations()
jalview.gui.SeqPanelTest.testFindMousePosition_wrapped_scaleAbove()
jalview.gui.SeqPanelTest.testFindMousePosition_wrapped_scales_longSequence()
jalview.gui.ScalePanelTest.testSelectColumns_withHidden()
jalview.gui.AlignmentPanel.updateLayout()

I’ve removed calling instances that I’m fairly sure do not contain geometry calculations. I’ve also removed all calling instances from the jalview.appletgui package, which we are not maintaining (and should be dropped from the codebase when we finally get to release Jalview 2.12 later this year). What you may also notice is there are several Test classes in the list above - these variously verify that jalview’s window and layout geometry are behaving correctly and simulate a number of corner cases to ensure - for example - the column under the mouse is actually the one that gets selected when you click the button.

If you are still keen to go ahead then I’d recommend working from the ‘develop’ branch at source.jalview.org/git/jalview.git - since we are pretty close to release of 2.11.3, and it should be much easier to merge your code in. I’ve already created an issue for you: https://issues.jalview.org/browse/JAL-4183

I of course completely understand if you choose not to embark on this adventure just now :slight_smile: If you just need something quickly for non-interactive use, you might find it is enough to patch the renderers, and judiciously increase the alignment width configuration so that the SVGs are not clipped on the right hand side. We’d be happy to take that into the codebase on a branch as a starting point for a more complete implementation !

Jim

Unsure about the reference sequence, I’ll let you know if I ever understand why it shows that way.
I’ll have a look at the grid… I guess the most important point is first to introduce a function called in every place for the drawing and then the extra pixels (or even better, the residue numbers, why not ?) will be easy to get in. I’ll let you know, but this could be fun !