UNPKG

@jbrowse/plugin-linear-genome-view

Version:

JBrowse 2 linear genome view

132 lines (131 loc) 4.8 kB
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(' '); } }