local-api.ts 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. /**
  2. * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * Taken/adapted from DensityServer (https://github.com/dsehnal/DensityServer)
  5. *
  6. * @author David Sehnal <david.sehnal@gmail.com>
  7. */
  8. import * as Api from './api';
  9. import * as Data from './query/data-model';
  10. import * as Coordinate from './algebra/coordinate';
  11. import * as fs from 'fs';
  12. import * as path from 'path';
  13. export interface JobEntry {
  14. source: {
  15. filename: string,
  16. name: string,
  17. id: string
  18. },
  19. query: {
  20. kind: 'box' | 'cell',
  21. space?: 'fractional' | 'cartesian',
  22. bottomLeft?: number[],
  23. topRight?: number[],
  24. }
  25. params: {
  26. /** Determines the detail level as specified in server-config */
  27. detail?: number,
  28. /**
  29. * Determines the sampling level:
  30. * 1: Original data
  31. * 2: Downsampled by factor 1/2
  32. * ...
  33. * N: downsampled 1/2^(N-1)
  34. */
  35. forcedSamplingLevel?: number,
  36. asBinary: boolean,
  37. },
  38. outputFolder: string,
  39. outputFilename?: string
  40. }
  41. export async function run(jobs: JobEntry[]) {
  42. let progress = 0;
  43. let started = getTime();
  44. for (const job of jobs) {
  45. try {
  46. await query(job);
  47. } catch (e) {
  48. console.error(e);
  49. }
  50. progress++;
  51. const elapsed = (getTime() - started) / 1000;
  52. console.log(`[Progress] ${progress}/${jobs.length} in ${elapsed.toFixed(2)}s`);
  53. }
  54. }
  55. function getTime() {
  56. let t = process.hrtime();
  57. return t[0] * 1000 + t[1] / 1000000;
  58. }
  59. async function query(job: JobEntry) {
  60. let box: Data.QueryParamsBox;
  61. if (job.query.kind.toLocaleLowerCase() === 'cell') {
  62. box = { kind: 'Cell' };
  63. } else if (job.query.space === 'fractional') {
  64. box = {
  65. kind: 'Fractional',
  66. a: Coordinate.fractional(job.query.bottomLeft![0], job.query.bottomLeft![1], job.query.bottomLeft![2]),
  67. b: Coordinate.fractional(job.query.topRight![0], job.query.topRight![1], job.query.topRight![2]),
  68. };
  69. } else {
  70. box = {
  71. kind: 'Cartesian',
  72. a: Coordinate.cartesian(job.query.bottomLeft![0], job.query.bottomLeft![1], job.query.bottomLeft![2]),
  73. b: Coordinate.cartesian(job.query.topRight![0], job.query.topRight![1], job.query.topRight![2]),
  74. };
  75. }
  76. const params: Data.QueryParams = {
  77. sourceFilename: job.source.filename,
  78. sourceId: job.source.id,
  79. asBinary: job.params.asBinary,
  80. box,
  81. detail: !job.params.detail ? 0 : job.params.detail,
  82. forcedSamplingLevel: job.params.forcedSamplingLevel
  83. };
  84. if (!fs.existsSync(job.outputFolder)) {
  85. makeDir(job.outputFolder);
  86. }
  87. const filename = path.join(job.outputFolder, job.outputFilename ?? Api.getOutputFilename(job.source.name, job.source.id, params));
  88. const res = () => wrapFile(filename);
  89. await Api.queryBox(params, res);
  90. }
  91. function makeDir(path: string, root?: string): boolean {
  92. let dirs = path.split(/\/|\\/g),
  93. dir = dirs.shift();
  94. root = (root || '') + dir + '/';
  95. try {
  96. fs.mkdirSync(root);
  97. } catch (e) {
  98. if (!fs.statSync(root).isDirectory()) throw new Error(e);
  99. }
  100. return !dirs.length || makeDir(dirs.join('/'), root);
  101. }
  102. function wrapFile(fn: string) {
  103. const w = {
  104. open(this: any) {
  105. if (this.opened) return;
  106. this.file = fs.openSync(fn, 'w');
  107. this.opened = true;
  108. },
  109. writeBinary(this: any, data: Uint8Array) {
  110. this.open();
  111. fs.writeSync(this.file, Buffer.from(data));
  112. return true;
  113. },
  114. writeString(this: any, data: string) {
  115. this.open();
  116. fs.writeSync(this.file, data);
  117. return true;
  118. },
  119. end(this: any) {
  120. if (!this.opened || this.ended) return;
  121. fs.close(this.file, function () { });
  122. this.ended = true;
  123. },
  124. file: 0,
  125. ended: false,
  126. opened: false
  127. };
  128. return w;
  129. }