C#-读取测序数据的ABI文件并绘制svg格式峰图

本地环境:win10,visual studio 2022 community


2024/8/6更新:python版

python-读取测序数据的ABI文件并输出png格式峰图-CSDN博客
https://blog.csdn.net/pxy7896/article/details/140955361

前言

本文是在已有的代码基础上进行的开发,前期已经实现:

  1. ABI文件的解析
  2. 峰图的简单绘制
  3. svg绘图
  4. 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文件后绘制峰图的小工具。绘制效果应如下图所示:(其中,字体、字号、颜色、间距、峰的宽度和高度等可以定制)
在这里插入图片描述
此外,还有如下要求:

  1. 上方箭头处是目标点,这个位置是由客户指定的,此处需要绘制一个向下的箭头;
  2. 不需要展示全部序列,只需要截取目标点周围的一段序列,因此序列的start和end也是由客户指定的。

实现效果

只截取了一部分,如下图所示:
在这里插入图片描述
如果想更像上面的图,那对于比较“矮”的点可以用更小的缩放系数。目前我用的是同一个缩放系数,因为客户要的精度不高。

解决思路

目前我是用blazor写的,所以可以分为三个部分:

  1. 前台获取目标点数值和上传.ab1文件,传递给后台解析数据和计算路径,然后回传结果并在前台显示出来
  2. 解析ABI格式。这里需要了解这种文件的组织方式,以及byte到字符串、数值的转化方法
  3. 计算路径。这里计算的是<svg>对象中各种子对象的路径数值,比如<path>d<text>xy

这里我们先从最终结果入手,思考:如果我们要画出这样的图,需要哪些信息?

我是这样拆解的:

  1. 顶端的彩色字符序列:这个可以用<text>实现,需要计算xy相对来说是固定的,给一个固定值即可)。对于x,它应该是每个波峰顶点的横坐标再减去字符宽度的一半,这样才能保持居中。

  2. 一个红色的指示箭头:这个可以用<path>实现,需要计算d。因为是箭头,只需要计算箭头的几个顶点,用简单的直线(Line,L)首尾连接即可。我这里用的方法是先确认顶边中心的坐标,然后按逆时针方向计算其他的点,连接起来。如下图所示:
    在这里插入图片描述

  3. 四条彩色曲线,代表不同碱基的测序数值。这个可以用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;
}
  • 6
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值