converter.ts 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. /**
  2. * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author David Sehnal <david.sehnal@gmail.com>
  5. * @author Sebastian Bittrich <sebastian.bittrich@rcsb.org>
  6. */
  7. import { CIF, CifCategory, getCifFieldType, CifField, CifFile } from '../../mol-io/reader/cif';
  8. import { CifWriter, EncodingStrategyHint } from '../../mol-io/writer/cif';
  9. import * as util from 'util';
  10. import * as fs from 'fs';
  11. import * as zlib from 'zlib';
  12. import { Progress, Task, RuntimeContext } from '../../mol-task';
  13. import { classifyFloatArray, classifyIntArray } from '../../mol-io/common/binary-cif';
  14. import { BinaryEncodingProvider } from '../../mol-io/writer/cif/encoder/binary';
  15. import { Category } from '../../mol-io/writer/cif/encoder';
  16. import { ReaderResult } from '../../mol-io/reader/result';
  17. function showProgress(p: Progress) {
  18. process.stdout.write(`\r${new Array(80).join(' ')}`);
  19. process.stdout.write(`\r${Progress.format(p)}`);
  20. }
  21. const readFileAsync = util.promisify(fs.readFile);
  22. const unzipAsync = util.promisify<zlib.InputType, Buffer>(zlib.unzip);
  23. async function readFile(ctx: RuntimeContext, filename: string): Promise<ReaderResult<CifFile>> {
  24. const isGz = /\.gz$/i.test(filename);
  25. if (filename.match(/\.bcif/)) {
  26. let input = await readFileAsync(filename);
  27. if (isGz) input = await unzipAsync(input);
  28. return await CIF.parseBinary(new Uint8Array(input)).runInContext(ctx);
  29. } else {
  30. let str: string;
  31. if (isGz) {
  32. const data = await unzipAsync(await readFileAsync(filename));
  33. str = data.toString('utf8');
  34. } else {
  35. str = await readFileAsync(filename, 'utf8');
  36. }
  37. return await CIF.parseText(str).runInContext(ctx);
  38. }
  39. }
  40. async function getCIF(ctx: RuntimeContext, filename: string) {
  41. const parsed = await readFile(ctx, filename);
  42. if (parsed.isError) {
  43. throw new Error(parsed.toString());
  44. }
  45. return parsed.result;
  46. }
  47. function getCategoryInstanceProvider(cat: CifCategory, fields: CifWriter.Field[]): CifWriter.Category {
  48. return {
  49. name: cat.name,
  50. instance: () => CifWriter.categoryInstance(fields, { data: cat, rowCount: cat.rowCount })
  51. };
  52. }
  53. function classify(name: string, field: CifField): CifWriter.Field {
  54. const type = getCifFieldType(field);
  55. if (type['@type'] === 'str') {
  56. return { name, type: CifWriter.Field.Type.Str, value: field.str, valueKind: field.valueKind };
  57. } else if (type['@type'] === 'float') {
  58. const encoder = classifyFloatArray(field.toFloatArray({ array: Float64Array }));
  59. return CifWriter.Field.float(name, field.float, { valueKind: field.valueKind, encoder, typedArray: Float64Array });
  60. } else {
  61. const encoder = classifyIntArray(field.toIntArray({ array: Int32Array }));
  62. return CifWriter.Field.int(name, field.int, { valueKind: field.valueKind, encoder, typedArray: Int32Array });
  63. }
  64. }
  65. export function convert(path: string, asText = false, hints?: EncodingStrategyHint[], filter?: string) {
  66. return Task.create<Uint8Array>('BinaryCIF', async ctx => {
  67. const encodingProvider: BinaryEncodingProvider = hints
  68. ? CifWriter.createEncodingProviderFromJsonConfig(hints)
  69. : { get: (c, f) => void 0 };
  70. const cif = await getCIF(ctx, path);
  71. const encoder = CifWriter.createEncoder({
  72. binary: !asText,
  73. encoderName: 'mol*/ciftools cif2bcif',
  74. binaryAutoClassifyEncoding: true,
  75. binaryEncodingPovider: encodingProvider
  76. });
  77. if (filter) {
  78. encoder.setFilter(Category.filterOf(filter));
  79. }
  80. let maxProgress = 0;
  81. for (const b of cif.blocks) {
  82. maxProgress += b.categoryNames.length;
  83. for (const c of b.categoryNames) maxProgress += b.categories[c].fieldNames.length;
  84. }
  85. let current = 0;
  86. for (const b of cif.blocks) {
  87. encoder.startDataBlock(b.header);
  88. for (const c of b.categoryNames) {
  89. const cat = b.categories[c];
  90. const fields: CifWriter.Field[] = [];
  91. for (const f of cat.fieldNames) {
  92. fields.push(classify(f, cat.getField(f)!));
  93. current++;
  94. if (ctx.shouldUpdate) await ctx.update({ message: 'Encoding...', current, max: maxProgress });
  95. }
  96. encoder.writeCategory(getCategoryInstanceProvider(b.categories[c], fields));
  97. current++;
  98. if (ctx.shouldUpdate) await ctx.update({ message: 'Encoding...', current, max: maxProgress });
  99. }
  100. }
  101. await ctx.update('Exporting...');
  102. const ret = encoder.getData() as Uint8Array;
  103. await ctx.update('Done.\n');
  104. return ret;
  105. }).run(showProgress, 250);
  106. }