@jbrowse/plugin-linear-genome-view
Version:
JBrowse 2 linear genome view
132 lines (131 loc) • 4.8 kB
JavaScript
import { assembleLocString, parseLocString } from '@jbrowse/core/util';
export function chooseGridPitch(scale, minMajorPitchPx, minMinorPitchPx) {
scale = Math.abs(scale);
const minMajorPitchBp = minMajorPitchPx * scale;
const majorMagnitude = Number.parseInt(Number(minMajorPitchBp).toExponential().split(/e/i)[1], 10);
let majorPitch = 10 ** majorMagnitude;
while (majorPitch < minMajorPitchBp) {
majorPitch *= 2;
if (majorPitch >= minMajorPitchBp) {
break;
}
majorPitch *= 2.5;
}
majorPitch = Math.max(majorPitch, 5);
const majorPitchPx = majorPitch / scale;
let minorPitch = 0;
if (!(majorPitch % 10) && majorPitchPx / 10 >= minMinorPitchPx) {
minorPitch = majorPitch / 10;
}
else if (!(majorPitch % 5) && majorPitchPx / 5 >= minMinorPitchPx) {
minorPitch = majorPitch / 5;
}
else if (!(majorPitch % 2) && majorPitchPx / 2 >= minMinorPitchPx) {
minorPitch = majorPitch / 2;
}
return { majorPitch, minorPitch };
}
export function makeTicks(start, end, bpPerPx, emitMajor = true, emitMinor = true) {
const gridPitch = chooseGridPitch(bpPerPx, 60, 15);
let minBase = start;
let maxBase = end;
if (bpPerPx < 0) {
;
[minBase, maxBase] = [maxBase, minBase];
}
minBase -= Math.abs(20 * bpPerPx) - 1;
maxBase += Math.abs(20 * bpPerPx) + 1;
const iterPitch = gridPitch.minorPitch || gridPitch.majorPitch;
let index = 0;
const ticks = [];
for (let base = Math.floor(minBase / iterPitch) * iterPitch; base < Math.ceil(maxBase / iterPitch) * iterPitch + 1; base += iterPitch) {
if (emitMinor && base % (gridPitch.majorPitch * 2)) {
ticks.push({ type: 'minor', base: base - 1, index });
index += 1;
}
else if (emitMajor && !(base % (gridPitch.majorPitch * 2))) {
ticks.push({ type: 'major', base: base - 1, index });
index += 1;
}
}
return ticks;
}
export async function generateLocations({ regions, assemblyManager, assemblyName, grow, }) {
return Promise.all(regions.map(async (region) => {
const asmName = region.assemblyName || assemblyName;
if (!asmName) {
throw new Error('no assembly provided');
}
const asm = await assemblyManager.waitForAssembly(asmName);
const { refName } = region;
if (!asm) {
throw new Error(`assembly ${asmName} not found`);
}
const { regions } = asm;
if (!regions) {
throw new Error(`regions not loaded yet for ${asmName}`);
}
const canonicalRefName = asm.getCanonicalRefName(region.refName);
if (!canonicalRefName) {
throw new Error(`Could not find refName ${refName} in ${asm.name}`);
}
const parentRegion = regions.find(r => r.refName === canonicalRefName);
if (!parentRegion) {
throw new Error(`Could not find refName ${refName} in ${asmName}`);
}
const { start, end } = region;
if (grow && start && end) {
const len = end - start;
const margin = len * grow;
return {
...region,
start: Math.max(0, start - margin),
end: end + margin,
assemblyName: asmName,
parentRegion,
};
}
else {
return {
...region,
assemblyName: asmName,
parentRegion,
};
}
}));
}
export function parseLocStrings(input, assemblyName, isValidRefName) {
const inputs = input
.split(/(\s+)/)
.map(f => f.trim())
.filter(f => !!f);
try {
return inputs.map(loc => parseLocString(loc, ref => isValidRefName(ref, assemblyName)));
}
catch (e) {
const [refName, start, end] = inputs;
if (/Unknown reference sequence/.exec(`${e}`) &&
Number.isInteger(+start) &&
Number.isInteger(+end)) {
return [
parseLocString(`${refName}:${start}..${end}`, ref => isValidRefName(ref, assemblyName)),
];
}
throw e;
}
}
export function calculateVisibleLocStrings(contentBlocks) {
if (!contentBlocks.length) {
return '';
}
else {
const isSingleAssemblyName = contentBlocks.every(b => b.assemblyName === contentBlocks[0].assemblyName);
const locs = contentBlocks.map(block => assembleLocString({
...block,
start: Math.round(block.start),
end: Math.round(block.end),
assemblyName: isSingleAssemblyName ? undefined : block.assemblyName,
}));
return locs.join(' ');
}
}