data.ts 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566
  1. /**
  2. * Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author David Sehnal <david.sehnal@gmail.com>
  5. */
  6. import msgpackDecode from '../../mol-io/common/msgpack/decode';
  7. import { PluginContext } from '../../mol-plugin/context';
  8. import { Task } from '../../mol-task';
  9. import { inflate } from '../../mol-util/zip/zip';
  10. export interface G3dHeader {
  11. magic: 'G3D',
  12. version: number,
  13. genome: string,
  14. name: string,
  15. offsets: { [resolution: string]: { offset: number, size: number } },
  16. resolutions: number[]
  17. }
  18. export type G3dDataBlock = {
  19. header: G3dHeader,
  20. resolution: number,
  21. data: {
  22. [haplotype: string]: {
  23. [ch: string]: {
  24. start: number[]
  25. x: number[],
  26. y: number[],
  27. z: number[],
  28. }
  29. }
  30. }
  31. }
  32. const HEADER_SIZE = 64000;
  33. export async function getG3dHeader(ctx: PluginContext, urlOrData: string | Uint8Array): Promise<G3dHeader> {
  34. const data: Uint8Array = await getRawData(ctx, urlOrData, { offset: 0, size: HEADER_SIZE });
  35. let last = data.length - 1;
  36. for (; last >= 0; last--) {
  37. if (data[last] !== 0) break;
  38. }
  39. const header = msgpackDecode(data.slice(0, last + 1));
  40. return header;
  41. }
  42. export async function getG3dDataBlock(ctx: PluginContext, header: G3dHeader, urlOrData: string | Uint8Array, resolution: number): Promise<G3dDataBlock> {
  43. if (!header.offsets[resolution]) throw new Error(`Resolution ${resolution} not available.`);
  44. const data = await getRawData(ctx, urlOrData, header.offsets[resolution]);
  45. const unzipped = await ctx.runTask(Task.create('Unzip', ctx => inflate(ctx, data)));
  46. return {
  47. header,
  48. resolution,
  49. data: msgpackDecode(unzipped)
  50. };
  51. }
  52. async function getRawData(ctx: PluginContext, urlOrData: string | Uint8Array, range: { offset: number, size: number }) {
  53. if (typeof urlOrData === 'string') {
  54. return await ctx.runTask(ctx.fetch({ url: urlOrData, headers: [['Range', `bytes=${range.offset}-${range.offset + range.size - 1}`]], type: 'binary' }));
  55. } else {
  56. return urlOrData.slice(range.offset, range.offset + range.size);
  57. }
  58. }