本地环境:win10,visual studio 2022 community
目录
- 前言
- 问题描述
- 实现效果
- 解决思路
- 实现要点
- ABI文件的组织方式
- svg绘制问题
- 变色碱基值
- 动态设置svg图像宽度
前言
本文是在已有的代码基础上进行的开发,前期已经实现:
- ABI文件的解析
- 峰图的简单绘制
- svg绘图
- svg图像导出
对于1,主要用到之前重写的struct包,另外加一些适应ABI格式的修改,c#重写的struct参见:
c#-用c#重写python的struct包-简化版_c# struct pack-CSDN博客
https://blog.csdn.net/pxy7896/article/details/120055568
对于2,之前也写过一个很粗糙的python版,没什么用,后面会着重写绘制思路:
python-读取abi文件并绘制峰图_python abi如何获取-CSDN博客
https://blog.csdn.net/pxy7896/article/details/120562689
对于3和4,参考:
SVG 教程 | 菜鸟教程
https://www.runoob.com/svg/svg-tutorial.html
Blazor入门-简单svg绘制+导出图像_blazor 画图-CSDN博客
https://blog.csdn.net/pxy7896/article/details/139003443
问题描述
在生物学和测序领域中,ABI文件(ABI format)通常指的是Applied Biosystems(ABI)公司开发的测序数据文件格式,通常的文件扩展名为.ab1
。这是一种用于存储DNA序列或蛋白质序列测序结果的标准格式。
ABI文件包含了测序仪器产生的原始测序数据,包括碱基序列、测序质量值、电泳图谱等信息。这些数据对于进行基因组测序、DNA序列分析以及生物信息学研究非常重要。通常,科研人员会使用特定的软件或工具来处理和分析ABI文件,以获取目标DNA或蛋白质序列的信息。
(以上来自AI问答)
我现在接到的任务就是开发一个可以解析abi文件后绘制峰图的小工具。绘制效果应如下图所示:(其中,字体、字号、颜色、间距、峰的宽度和高度等可以定制)
此外,还有如下要求:
- 上方箭头处是目标点,这个位置是由客户指定的,此处需要绘制一个向下的箭头;
- 不需要展示全部序列,只需要截取目标点周围的一段序列,因此序列的start和end也是由客户指定的。
实现效果
只截取了一部分,如下图所示:
如果想更像上面的图,那对于比较“矮”的点可以用更小的缩放系数。目前我用的是同一个缩放系数,因为客户要的精度不高。
解决思路
目前我是用blazor写的,所以可以分为三个部分:
- 前台获取目标点数值和上传
.ab1
文件,传递给后台解析数据和计算路径,然后回传结果并在前台显示出来 - 解析ABI格式。这里需要了解这种文件的组织方式,以及byte到字符串、数值的转化方法
- 计算路径。这里计算的是
<svg>
对象中各种子对象的路径数值,比如<path>
的d
,<text>
的x
和y
。
这里我们先从最终结果入手,思考:如果我们要画出这样的图,需要哪些信息?
我是这样拆解的:
-
顶端的彩色字符序列:这个可以用
<text>
实现,需要计算x
(y
相对来说是固定的,给一个固定值即可)。对于x
,它应该是每个波峰顶点的横坐标再减去字符宽度的一半,这样才能保持居中。 -
一个红色的指示箭头:这个可以用
<path>
实现,需要计算d
。因为是箭头,只需要计算箭头的几个顶点,用简单的直线(Line,L)首尾连接即可。我这里用的方法是先确认顶边中心的坐标,然后按逆时针方向计算其他的点,连接起来。如下图所示:
-
四条彩色曲线,代表不同碱基的测序数值。这个可以用Line或者贝塞尔曲线实现,也是要计算
d
。我本来是考虑用贝塞尔曲线的,因为波峰的值很好取得(后面解析ABI文件会说),那么我可以将每个波峰的值看作Q点,Q和baseline的中点看作M点,如下图所示:
那么整个曲线的d属性的格式就是:M 起点x 起点y Q Qx0 Qy0 Mx0 My0 Qx1 Qy2 Mx1 My1…比如
<path d="M 40 100 Q 50 52 60 100 Q 70 50 80 100 Q 90 20 100 100" fill="none" stroke="blue" />
绘制的效果是:
但是我很快发现这样不行。。因为就算我在波峰两侧多取了两个样点,画出来的图还是假假的。。实验部门说不行,看起来太假了,像是这样:
幸运的是,我发现客户要求的范围其实相当窄,所以完全可以不取样,就用原始数据画,所以最终选择了用直线L,连接所有数据点。
实现要点
ABI文件的组织方式
参见:
https://projects.nfstc.org/workshops/resources/articles/ABIF_File_Format.pdf
只要用我之前写过的struct包解析,注意对应的tag就没啥问题。这里不再展开。
svg绘制问题
变色碱基值
这个比较简单,给前端传递对象时,带一个List<Ab1Char>
,这个Ab1Char
包含字符和x坐标,在前端循环时,根据字符判断颜色:
@foreach (Ab1Char a in ab1.xxx)
{@if (a.ch == "A") {color = "green";}...// 一些判断
}<g><text x="@a.x" y="30" fill="@color">@a.ch</text>
</g>
动态设置svg图像宽度
这个问题主要出现在导出svg图像时,发现图像右侧有一些空白:
造成这个现象的原因是,我的<path>
啥的都是放在一个白色的<rect>
上面的(为了实现白底效果),所以,需要动态地设置<rect>
和<svg>
的宽度,或者在导出图像时,将一个宽度变量传入函数。
目前我的解法是在计算<path>
的d属性时,同步计算最大宽度值,这个值是要带给前端的,在导出操作时也要带这个值,并且修改导出图像的js函数:
export function exportSvgToImage2(svgElement, format, width) {if (format == null)return;var svgXml = new XMLSerializer().serializeToString(svgElement);var utf8 = unescape(encodeURIComponent(svgXml));var imageUrl = "data:image/svg+xml;base64," + btoa(utf8);var canvas = document.createElement("canvas");canvas.width = width; // 修改这一句,不再用clientWidth,改用指定值canvas.height = svgElement.clientHeight;var ctx = canvas.getContext("2d");var img = new Image();img.onload = function () {ctx.drawImage(img, 0, 0);var a = document.createElement("a");a.download = "exported_image." + format;a.href = canvas.toDataURL("image/" + format);a.click();};img.src = imageUrl;
}