jbrowse-plugin-mafviewer 1.0.1 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/jbrowse-plugin-mafviewer.umd.development.js +216 -104
- package/dist/jbrowse-plugin-mafviewer.umd.development.js.map +1 -1
- package/dist/jbrowse-plugin-mafviewer.umd.production.min.js +1 -1
- package/dist/jbrowse-plugin-mafviewer.umd.production.min.js.map +1 -1
- package/package.json +1 -1
- package/src/BigMafAdapter/configSchema.ts +2 -1
- package/src/LinearMafDisplay/components/ColorLegend.tsx +25 -36
- package/src/LinearMafDisplay/components/SetRowHeight.tsx +76 -0
- package/src/LinearMafDisplay/components/YScaleBars.tsx +2 -2
- package/src/LinearMafDisplay/configSchema.ts +2 -3
- package/src/LinearMafDisplay/renderSvg.tsx +13 -4
- package/src/LinearMafDisplay/stateModel.ts +61 -13
- package/src/LinearMafRenderer/LinearMafRenderer.ts +101 -46
- package/src/MafTabixAdapter/MafTabixAdapter.ts +19 -17
- package/src/MafTabixAdapter/configSchema.ts +2 -1
- package/src/index.ts +1 -1
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
(function (global, factory) {
|
|
2
|
-
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@jbrowse/core/Plugin'), require('@jbrowse/core/pluggableElementTypes'), require('@jbrowse/core/configuration'), require('@jbrowse/core/data_adapters/BaseAdapter'), require('mobx-state-tree'), require('@jbrowse/core/util'), require('@jbrowse/core/util/rxjs'), require('react'), require('mobx-react'), require('@
|
|
3
|
-
typeof define === 'function' && define.amd ? define(['exports', '@jbrowse/core/Plugin', '@jbrowse/core/pluggableElementTypes', '@jbrowse/core/configuration', '@jbrowse/core/data_adapters/BaseAdapter', 'mobx-state-tree', '@jbrowse/core/util', '@jbrowse/core/util/rxjs', 'react', 'mobx-react', '@
|
|
4
|
-
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.JBrowsePluginMafViewer = {}, global.JBrowseExports["@jbrowse/core/Plugin"], global.JBrowseExports["@jbrowse/core/pluggableElementTypes"], global.JBrowseExports["@jbrowse/core/configuration"], global.JBrowseExports["@jbrowse/core/data_adapters/BaseAdapter"], global.JBrowseExports["mobx-state-tree"], global.JBrowseExports["@jbrowse/core/util"], global.JBrowseExports["@jbrowse/core/util/rxjs"], global.JBrowseExports.react, global.JBrowseExports["mobx-react"], global.JBrowseExports["@
|
|
5
|
-
})(this, (function (exports, Plugin, pluggableElementTypes, configuration, BaseAdapter, mobxStateTree, util, rxjs, React, mobxReact,
|
|
2
|
+
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@jbrowse/core/Plugin'), require('@jbrowse/core/pluggableElementTypes'), require('@jbrowse/core/configuration'), require('@jbrowse/core/data_adapters/BaseAdapter'), require('mobx-state-tree'), require('@jbrowse/core/util'), require('@jbrowse/core/util/rxjs'), require('react'), require('mobx-react'), require('@mui/material'), require('@jbrowse/core/ui'), require('tss-react/mui')) :
|
|
3
|
+
typeof define === 'function' && define.amd ? define(['exports', '@jbrowse/core/Plugin', '@jbrowse/core/pluggableElementTypes', '@jbrowse/core/configuration', '@jbrowse/core/data_adapters/BaseAdapter', 'mobx-state-tree', '@jbrowse/core/util', '@jbrowse/core/util/rxjs', 'react', 'mobx-react', '@mui/material', '@jbrowse/core/ui', 'tss-react/mui'], factory) :
|
|
4
|
+
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.JBrowsePluginMafViewer = {}, global.JBrowseExports["@jbrowse/core/Plugin"], global.JBrowseExports["@jbrowse/core/pluggableElementTypes"], global.JBrowseExports["@jbrowse/core/configuration"], global.JBrowseExports["@jbrowse/core/data_adapters/BaseAdapter"], global.JBrowseExports["mobx-state-tree"], global.JBrowseExports["@jbrowse/core/util"], global.JBrowseExports["@jbrowse/core/util/rxjs"], global.JBrowseExports.react, global.JBrowseExports["mobx-react"], global.JBrowseExports["@mui/material"], global.JBrowseExports["@jbrowse/core/ui"], global.JBrowseExports["tss-react/mui"]));
|
|
5
|
+
})(this, (function (exports, Plugin, pluggableElementTypes, configuration, BaseAdapter, mobxStateTree, util, rxjs, React, mobxReact, material, ui, mui) { 'use strict';
|
|
6
6
|
|
|
7
|
-
var version = "1.0.
|
|
7
|
+
var version = "1.0.3";
|
|
8
8
|
|
|
9
9
|
const configSchema$2 = configuration.ConfigurationSchema('BigMafAdapter', {
|
|
10
10
|
/**
|
|
11
11
|
* #slot
|
|
12
12
|
*/
|
|
13
13
|
samples: {
|
|
14
|
-
type: '
|
|
14
|
+
type: 'frozen',
|
|
15
|
+
description: 'string[] or {id:string,label:string,color?:string}[]',
|
|
15
16
|
defaultValue: [],
|
|
16
17
|
},
|
|
17
18
|
/**
|
|
@@ -737,29 +738,54 @@
|
|
|
737
738
|
|
|
738
739
|
function configSchemaF(pluginManager) {
|
|
739
740
|
const LinearGenomePlugin = pluginManager.getPlugin('LinearGenomeViewPlugin');
|
|
740
|
-
|
|
741
|
-
const { linearBasicDisplayConfigSchemaFactory } = LinearGenomePlugin.exports;
|
|
741
|
+
const { baseLinearDisplayConfigSchema } = LinearGenomePlugin.exports;
|
|
742
742
|
return configuration.ConfigurationSchema('LinearMafDisplay', {
|
|
743
743
|
/**
|
|
744
744
|
* #slot
|
|
745
745
|
*/
|
|
746
746
|
renderer: pluginManager.pluggableConfigSchemaType('renderer'),
|
|
747
747
|
}, {
|
|
748
|
-
baseConfiguration:
|
|
748
|
+
baseConfiguration: baseLinearDisplayConfigSchema,
|
|
749
749
|
explicitlyTyped: true,
|
|
750
750
|
});
|
|
751
751
|
}
|
|
752
752
|
|
|
753
|
+
const useStyles$1 = mui.makeStyles()({
|
|
754
|
+
root: {
|
|
755
|
+
width: 500,
|
|
756
|
+
},
|
|
757
|
+
});
|
|
758
|
+
const SetRowHeightDialog = mobxReact.observer(function (props) {
|
|
759
|
+
const { model, handleClose } = props;
|
|
760
|
+
const { classes } = useStyles$1();
|
|
761
|
+
const [rowHeight, setRowHeight] = React.useState(`${model.rowHeight}`);
|
|
762
|
+
const [rowProportion, setRowProportion] = React.useState(`${model.rowProportion}`);
|
|
763
|
+
return (React.createElement(ui.Dialog, { open: true, onClose: handleClose, title: "Filter options" },
|
|
764
|
+
React.createElement(material.DialogContent, { className: classes.root },
|
|
765
|
+
React.createElement(material.Typography, null, "Set row height and the proportion of the row height to use for drawing each row"),
|
|
766
|
+
React.createElement(material.TextField, { value: rowHeight, onChange: event => setRowHeight(event.target.value), placeholder: "Enter row height" }),
|
|
767
|
+
React.createElement(material.TextField, { value: rowProportion, onChange: event => setRowProportion(event.target.value), placeholder: "Enter row proportion" }),
|
|
768
|
+
React.createElement(material.DialogActions, null,
|
|
769
|
+
React.createElement(material.Button, { variant: "contained", color: "primary", type: "submit", autoFocus: true, onClick: () => {
|
|
770
|
+
model.setRowProportion(+rowProportion);
|
|
771
|
+
model.setRowHeight(+rowHeight);
|
|
772
|
+
handleClose();
|
|
773
|
+
} }, "Submit"),
|
|
774
|
+
React.createElement(material.Button, { variant: "contained", color: "secondary", onClick: () => handleClose() }, "Cancel")))));
|
|
775
|
+
});
|
|
776
|
+
|
|
777
|
+
function isStrs(array) {
|
|
778
|
+
return typeof array[0] === 'string';
|
|
779
|
+
}
|
|
753
780
|
/**
|
|
754
781
|
* #stateModel LinearMafDisplay
|
|
755
782
|
* extends LinearBasicDisplay
|
|
756
783
|
*/
|
|
757
784
|
function stateModelFactory(configSchema, pluginManager) {
|
|
758
785
|
const LinearGenomePlugin = pluginManager.getPlugin('LinearGenomeViewPlugin');
|
|
759
|
-
|
|
760
|
-
const { linearBasicDisplayModelFactory } = LinearGenomePlugin.exports;
|
|
786
|
+
const { BaseLinearDisplay } = LinearGenomePlugin.exports;
|
|
761
787
|
return mobxStateTree.types
|
|
762
|
-
.compose('LinearMafDisplay',
|
|
788
|
+
.compose('LinearMafDisplay', BaseLinearDisplay, mobxStateTree.types.model({
|
|
763
789
|
/**
|
|
764
790
|
* #property
|
|
765
791
|
*/
|
|
@@ -768,23 +794,44 @@
|
|
|
768
794
|
* #property
|
|
769
795
|
*/
|
|
770
796
|
configuration: configuration.ConfigurationReference(configSchema),
|
|
797
|
+
/**
|
|
798
|
+
* #property
|
|
799
|
+
*/
|
|
800
|
+
rowHeight: 15,
|
|
801
|
+
/**
|
|
802
|
+
* #property
|
|
803
|
+
*/
|
|
804
|
+
rowProportion: 0.8,
|
|
771
805
|
}))
|
|
772
806
|
.volatile(() => ({
|
|
773
807
|
prefersOffset: true,
|
|
774
808
|
}))
|
|
775
|
-
.
|
|
809
|
+
.actions(self => ({
|
|
776
810
|
/**
|
|
777
|
-
* #
|
|
811
|
+
* #action
|
|
778
812
|
*/
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
return r.map(elt => ({ name: elt, color: undefined }));
|
|
813
|
+
setRowHeight(n) {
|
|
814
|
+
self.rowHeight = n;
|
|
782
815
|
},
|
|
816
|
+
/**
|
|
817
|
+
* #action
|
|
818
|
+
*/
|
|
819
|
+
setRowProportion(n) {
|
|
820
|
+
self.rowProportion = n;
|
|
821
|
+
},
|
|
822
|
+
}))
|
|
823
|
+
.views(self => ({
|
|
783
824
|
/**
|
|
784
825
|
* #getter
|
|
785
826
|
*/
|
|
786
|
-
get
|
|
787
|
-
|
|
827
|
+
get samples() {
|
|
828
|
+
const r = self.adapterConfig.samples;
|
|
829
|
+
if (isStrs(r)) {
|
|
830
|
+
return r.map(elt => ({ id: elt, label: elt, color: undefined }));
|
|
831
|
+
}
|
|
832
|
+
else {
|
|
833
|
+
return r;
|
|
834
|
+
}
|
|
788
835
|
},
|
|
789
836
|
/**
|
|
790
837
|
* #getter
|
|
@@ -805,7 +852,7 @@
|
|
|
805
852
|
},
|
|
806
853
|
}))
|
|
807
854
|
.views(self => {
|
|
808
|
-
const { renderProps: superRenderProps } = self;
|
|
855
|
+
const { trackMenuItems: superTrackMenuItems, renderProps: superRenderProps, } = self;
|
|
809
856
|
return {
|
|
810
857
|
/**
|
|
811
858
|
* #method
|
|
@@ -813,10 +860,29 @@
|
|
|
813
860
|
renderProps() {
|
|
814
861
|
return {
|
|
815
862
|
...superRenderProps(),
|
|
863
|
+
config: self.rendererConfig,
|
|
816
864
|
samples: self.samples,
|
|
817
865
|
rowHeight: self.rowHeight,
|
|
866
|
+
rowProportion: self.rowProportion,
|
|
818
867
|
};
|
|
819
868
|
},
|
|
869
|
+
/**
|
|
870
|
+
* #method
|
|
871
|
+
*/
|
|
872
|
+
trackMenuItems() {
|
|
873
|
+
return [
|
|
874
|
+
...superTrackMenuItems(),
|
|
875
|
+
{
|
|
876
|
+
label: 'Set row height',
|
|
877
|
+
onClick: () => {
|
|
878
|
+
util.getSession(self).queueDialog(handleClose => [
|
|
879
|
+
SetRowHeightDialog,
|
|
880
|
+
{ model: self, handleClose },
|
|
881
|
+
]);
|
|
882
|
+
},
|
|
883
|
+
},
|
|
884
|
+
];
|
|
885
|
+
},
|
|
820
886
|
};
|
|
821
887
|
})
|
|
822
888
|
.actions(self => {
|
|
@@ -827,7 +893,6 @@
|
|
|
827
893
|
*/
|
|
828
894
|
async renderSvg(opts) {
|
|
829
895
|
const { renderSvg } = await Promise.resolve().then(function () { return renderSvg$1; });
|
|
830
|
-
// @ts-expect-error
|
|
831
896
|
return renderSvg(self, opts, superRenderSvg);
|
|
832
897
|
},
|
|
833
898
|
};
|
|
@@ -841,18 +906,14 @@
|
|
|
841
906
|
|
|
842
907
|
const ColorLegend = mobxReact.observer(function ({ model, labelWidth, }) {
|
|
843
908
|
const { samples, rowHeight } = model;
|
|
844
|
-
const svgFontSize = Math.min(rowHeight,
|
|
845
|
-
const canDisplayLabel = rowHeight
|
|
846
|
-
const
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
source.color ? (React.createElement(RectBg, { y: idx * rowHeight + 1, x: extraOffset, width: colorBoxWidth, height: boxHeight, color: source.color })) : null,
|
|
853
|
-
React.createElement(RectBg, { y: idx * rowHeight + 1, x: extraOffset, width: legendWidth, height: boxHeight }),
|
|
854
|
-
canDisplayLabel ? (React.createElement("text", { y: idx * rowHeight + 13, x: extraOffset + colorBoxWidth + 2, fontSize: svgFontSize }, source.name)) : null));
|
|
855
|
-
}))) : null;
|
|
909
|
+
const svgFontSize = Math.min(rowHeight, 10);
|
|
910
|
+
const canDisplayLabel = rowHeight >= 10;
|
|
911
|
+
const boxHeight = Math.min(20, rowHeight);
|
|
912
|
+
return samples ? (React.createElement(React.Fragment, null,
|
|
913
|
+
samples.map((sample, idx) => (React.createElement(RectBg, { key: `${sample.id}-${idx}`, y: idx * rowHeight + 1, x: 0, width: labelWidth + 5, height: boxHeight, color: sample.color }))),
|
|
914
|
+
canDisplayLabel
|
|
915
|
+
? samples.map((sample, idx) => (React.createElement("text", { key: `${sample.id}-${idx}`, y: idx * rowHeight + 14, x: 2, fontSize: svgFontSize }, sample.label)))
|
|
916
|
+
: null)) : null;
|
|
856
917
|
});
|
|
857
918
|
|
|
858
919
|
const Wrapper = mobxReact.observer(function ({ children, model, exportSVG, }) {
|
|
@@ -875,10 +936,10 @@
|
|
|
875
936
|
const { model } = props;
|
|
876
937
|
const { rowHeight, samples } = model;
|
|
877
938
|
const svgFontSize = Math.min(rowHeight, 12);
|
|
878
|
-
const canDisplayLabel = rowHeight
|
|
939
|
+
const canDisplayLabel = rowHeight >= 10;
|
|
879
940
|
const minWidth = 20;
|
|
880
941
|
const labelWidth = Math.max(...(samples
|
|
881
|
-
.map(s => util.measureText(s, svgFontSize))
|
|
942
|
+
.map(s => util.measureText(s.label, svgFontSize))
|
|
882
943
|
.map(width => (canDisplayLabel ? width : minWidth)) || [0]));
|
|
883
944
|
return (React.createElement(Wrapper, { ...props },
|
|
884
945
|
React.createElement(ColorLegend, { model: model, labelWidth: labelWidth })));
|
|
@@ -917,20 +978,6 @@
|
|
|
917
978
|
explicitlyTyped: true,
|
|
918
979
|
});
|
|
919
980
|
|
|
920
|
-
function getCorrectionFactor(scale) {
|
|
921
|
-
if (scale >= 1) {
|
|
922
|
-
return 0.6;
|
|
923
|
-
}
|
|
924
|
-
else if (scale >= 0.2) {
|
|
925
|
-
return 0.05;
|
|
926
|
-
}
|
|
927
|
-
else if (scale >= 0.02) {
|
|
928
|
-
return 0.03;
|
|
929
|
-
}
|
|
930
|
-
else {
|
|
931
|
-
return 0.02;
|
|
932
|
-
}
|
|
933
|
-
}
|
|
934
981
|
function getContrastBaseMap(theme) {
|
|
935
982
|
return Object.fromEntries(Object.entries(getColorBaseMap(theme)).map(([key, value]) => [
|
|
936
983
|
key,
|
|
@@ -947,16 +994,18 @@
|
|
|
947
994
|
};
|
|
948
995
|
}
|
|
949
996
|
function makeImageData({ ctx, renderArgs, }) {
|
|
950
|
-
const { regions, bpPerPx, rowHeight, theme: configTheme, samples, } = renderArgs;
|
|
997
|
+
const { regions, bpPerPx, rowHeight, theme: configTheme, samples, rowProportion, } = renderArgs;
|
|
951
998
|
const [region] = regions;
|
|
952
999
|
const features = renderArgs.features;
|
|
953
1000
|
const h = rowHeight;
|
|
954
1001
|
const theme = ui.createJBrowseTheme(configTheme);
|
|
955
1002
|
const colorForBase = getColorBaseMap(theme);
|
|
956
1003
|
const contrastForBase = getContrastBaseMap(theme);
|
|
957
|
-
const sampleToRowMap = new Map(samples.map((s, i) => [s.
|
|
1004
|
+
const sampleToRowMap = new Map(samples.map((s, i) => [s.id, i]));
|
|
958
1005
|
const scale = 1 / bpPerPx;
|
|
959
|
-
const
|
|
1006
|
+
const f = 0.4;
|
|
1007
|
+
const h2 = h * rowProportion;
|
|
1008
|
+
const offset = h2 / 2;
|
|
960
1009
|
// sample as alignments
|
|
961
1010
|
ctx.font = 'bold 10px Courier New,monospace';
|
|
962
1011
|
for (const feature of features.values()) {
|
|
@@ -966,63 +1015,117 @@
|
|
|
966
1015
|
for (const [sample, val] of Object.entries(vals)) {
|
|
967
1016
|
const origAlignment = val.data;
|
|
968
1017
|
const alignment = origAlignment.toLowerCase();
|
|
969
|
-
// gaps
|
|
970
|
-
ctx.beginPath();
|
|
971
|
-
ctx.fillStyle = 'black';
|
|
972
|
-
const offset0 = (5 / 12) * h;
|
|
973
|
-
const h6 = h / 6;
|
|
974
1018
|
const row = sampleToRowMap.get(sample);
|
|
975
1019
|
if (row === undefined) {
|
|
976
1020
|
throw new Error(`unknown sample encountered: ${sample}`);
|
|
977
1021
|
}
|
|
978
1022
|
const t = h * row;
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
1023
|
+
// gaps
|
|
1024
|
+
ctx.beginPath();
|
|
1025
|
+
ctx.fillStyle = 'black';
|
|
1026
|
+
for (let i = 0, o = 0; i < alignment.length; i++) {
|
|
1027
|
+
if (seq[i] !== '-') {
|
|
1028
|
+
if (alignment[i] === '-') {
|
|
1029
|
+
const l = leftPx + scale * o;
|
|
1030
|
+
ctx.moveTo(l, t + h2);
|
|
1031
|
+
ctx.lineTo(l + scale + f, t + h2);
|
|
1032
|
+
}
|
|
1033
|
+
o++;
|
|
983
1034
|
}
|
|
984
1035
|
}
|
|
985
|
-
ctx.
|
|
986
|
-
const offset = (1 / 4) * h;
|
|
987
|
-
const h2 = h / 2;
|
|
1036
|
+
ctx.stroke();
|
|
988
1037
|
// matches
|
|
989
1038
|
ctx.beginPath();
|
|
990
1039
|
ctx.fillStyle = 'lightgrey';
|
|
991
|
-
for (let i = 0; i < alignment.length; i++) {
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
1040
|
+
for (let i = 0, o = 0; i < alignment.length; i++) {
|
|
1041
|
+
if (seq[i] !== '-') {
|
|
1042
|
+
const c = alignment[i];
|
|
1043
|
+
const l = leftPx + scale * o;
|
|
1044
|
+
if (seq[i] === c && c !== '-') {
|
|
1045
|
+
ctx.rect(l, offset + t, scale + f, h2);
|
|
1046
|
+
}
|
|
1047
|
+
o++;
|
|
996
1048
|
}
|
|
997
1049
|
}
|
|
998
1050
|
ctx.fill();
|
|
999
1051
|
// mismatches
|
|
1000
|
-
for (let i = 0; i < alignment.length; i++) {
|
|
1052
|
+
for (let i = 0, o = 0; i < alignment.length; i++) {
|
|
1001
1053
|
const c = alignment[i];
|
|
1002
|
-
if (seq[i] !==
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1054
|
+
if (seq[i] !== '-') {
|
|
1055
|
+
if (seq[i] !== c && c !== '-') {
|
|
1056
|
+
const l = leftPx + scale * o;
|
|
1057
|
+
ctx.fillStyle =
|
|
1058
|
+
colorForBase[c] ?? 'purple';
|
|
1059
|
+
ctx.fillRect(l, offset + t, scale + f, h2);
|
|
1060
|
+
}
|
|
1061
|
+
o++;
|
|
1007
1062
|
}
|
|
1008
1063
|
}
|
|
1009
1064
|
// font
|
|
1010
1065
|
const charSize = { w: 10 };
|
|
1011
1066
|
if (scale >= charSize.w) {
|
|
1012
|
-
for (let i = 0; i < alignment.length; i++) {
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1067
|
+
for (let i = 0, o = 0; i < alignment.length; i++) {
|
|
1068
|
+
if (seq[i] !== '-') {
|
|
1069
|
+
const l = leftPx + scale * o;
|
|
1070
|
+
const offset = (scale - charSize.w) / 2 + 1;
|
|
1071
|
+
const c = alignment[i];
|
|
1072
|
+
if (seq[i] !== c && c !== '-') {
|
|
1073
|
+
ctx.fillStyle = contrastForBase[c] ?? 'black';
|
|
1074
|
+
ctx.fillText(origAlignment[i], l + offset, h2 + t + 3);
|
|
1075
|
+
}
|
|
1076
|
+
o++;
|
|
1019
1077
|
}
|
|
1020
1078
|
}
|
|
1021
1079
|
}
|
|
1022
1080
|
}
|
|
1023
1081
|
}
|
|
1082
|
+
// second pass for insertions, has slightly improved look since the
|
|
1083
|
+
// insertions are always 'on top' of the other features
|
|
1084
|
+
for (const feature of features.values()) {
|
|
1085
|
+
const [leftPx] = util.featureSpanPx(feature, region, bpPerPx);
|
|
1086
|
+
const vals = feature.get('alignments');
|
|
1087
|
+
const seq = feature.get('seq').toLowerCase();
|
|
1088
|
+
for (const [sample, val] of Object.entries(vals)) {
|
|
1089
|
+
const origAlignment = val.data;
|
|
1090
|
+
const alignment = origAlignment.toLowerCase();
|
|
1091
|
+
const row = sampleToRowMap.get(sample);
|
|
1092
|
+
if (row === undefined) {
|
|
1093
|
+
throw new Error(`unknown sample encountered: ${sample}`);
|
|
1094
|
+
}
|
|
1095
|
+
const t = h * row;
|
|
1096
|
+
ctx.beginPath();
|
|
1097
|
+
ctx.fillStyle = 'purple';
|
|
1098
|
+
for (let i = 0, o = 0; i < alignment.length; i++) {
|
|
1099
|
+
let ins = '';
|
|
1100
|
+
while (seq[i] === '-') {
|
|
1101
|
+
if (alignment[i] !== '-') {
|
|
1102
|
+
ins += alignment[i];
|
|
1103
|
+
}
|
|
1104
|
+
i++;
|
|
1105
|
+
}
|
|
1106
|
+
if (ins.length) {
|
|
1107
|
+
const l = leftPx + scale * o - 2;
|
|
1108
|
+
ctx.rect(l, offset + t, 2, h2);
|
|
1109
|
+
ctx.rect(l - 2, offset + t, 6, 1);
|
|
1110
|
+
ctx.rect(l - 2, offset + t + h2, 6, 1);
|
|
1111
|
+
}
|
|
1112
|
+
o++;
|
|
1113
|
+
}
|
|
1114
|
+
ctx.fill();
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1024
1117
|
}
|
|
1025
1118
|
class LinearMafRenderer extends pluggableElementTypes.FeatureRendererType {
|
|
1119
|
+
getExpandedRegion(region) {
|
|
1120
|
+
const { start, end } = region;
|
|
1121
|
+
const bpExpansion = 1;
|
|
1122
|
+
return {
|
|
1123
|
+
// xref https://github.com/mobxjs/mobx-state-tree/issues/1524 for Omit
|
|
1124
|
+
...region,
|
|
1125
|
+
start: Math.floor(Math.max(start - bpExpansion, 0)),
|
|
1126
|
+
end: Math.ceil(end + bpExpansion),
|
|
1127
|
+
};
|
|
1128
|
+
}
|
|
1026
1129
|
async render(renderProps) {
|
|
1027
1130
|
const { regions, bpPerPx, samples, rowHeight } = renderProps;
|
|
1028
1131
|
const [region] = regions;
|
|
@@ -1069,7 +1172,8 @@
|
|
|
1069
1172
|
* #slot
|
|
1070
1173
|
*/
|
|
1071
1174
|
samples: {
|
|
1072
|
-
type: '
|
|
1175
|
+
type: 'frozen',
|
|
1176
|
+
description: 'string[] or {id:string,label:string,color?:string}[]',
|
|
1073
1177
|
defaultValue: [],
|
|
1074
1178
|
},
|
|
1075
1179
|
/**
|
|
@@ -1124,6 +1228,10 @@
|
|
|
1124
1228
|
const { adapter } = await this.setup();
|
|
1125
1229
|
return adapter.getRefNames();
|
|
1126
1230
|
}
|
|
1231
|
+
async getHeader() {
|
|
1232
|
+
const { adapter } = await this.setup();
|
|
1233
|
+
return adapter.getHeader();
|
|
1234
|
+
}
|
|
1127
1235
|
getFeatures(query) {
|
|
1128
1236
|
return rxjs.ObservableCreate(async (observer) => {
|
|
1129
1237
|
const { adapter } = await this.setup();
|
|
@@ -1131,31 +1239,29 @@
|
|
|
1131
1239
|
for (const feature of features) {
|
|
1132
1240
|
const data = feature.get('field5').split(',');
|
|
1133
1241
|
const alignments = {};
|
|
1134
|
-
const main = data[0];
|
|
1135
|
-
const aln = main.split(':')[5];
|
|
1136
1242
|
const alns = data.map(elt => elt.split(':')[5]);
|
|
1137
|
-
const
|
|
1243
|
+
// const aln = alns[0]
|
|
1244
|
+
// const alns2 = data.map(() => '')
|
|
1138
1245
|
// remove extraneous data in other alignments
|
|
1139
1246
|
// reason being: cannot represent missing data in main species that are in others)
|
|
1140
|
-
for (let i = 0
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
}
|
|
1247
|
+
// for (let i = 0; i < aln.length; i++) {
|
|
1248
|
+
// if (aln[i] !== '-') {
|
|
1249
|
+
// for (let j = 0; j < data.length; j++) {
|
|
1250
|
+
// alns2[j] += alns[j][i]
|
|
1251
|
+
// }
|
|
1252
|
+
// }
|
|
1253
|
+
// }
|
|
1147
1254
|
for (let j = 0; j < data.length; j++) {
|
|
1148
1255
|
const elt = data[j];
|
|
1149
1256
|
const ad = elt.split(':');
|
|
1150
|
-
const org = ad[0].split('.')
|
|
1151
|
-
const chr = ad[0].split('.')[1];
|
|
1257
|
+
const [org, chr] = ad[0].split('.');
|
|
1152
1258
|
alignments[org] = {
|
|
1153
|
-
chr
|
|
1259
|
+
chr,
|
|
1154
1260
|
start: +ad[1],
|
|
1155
1261
|
srcSize: +ad[2],
|
|
1156
1262
|
strand: ad[3] === '-' ? -1 : 1,
|
|
1157
1263
|
unknown: +ad[4],
|
|
1158
|
-
data:
|
|
1264
|
+
data: alns[j],
|
|
1159
1265
|
};
|
|
1160
1266
|
}
|
|
1161
1267
|
observer.next(new util.SimpleFeature({
|
|
@@ -1166,8 +1272,8 @@
|
|
|
1166
1272
|
refName: feature.get('refName'),
|
|
1167
1273
|
name: feature.get('name'),
|
|
1168
1274
|
score: feature.get('score'),
|
|
1169
|
-
alignments
|
|
1170
|
-
seq:
|
|
1275
|
+
alignments,
|
|
1276
|
+
seq: alns[0],
|
|
1171
1277
|
},
|
|
1172
1278
|
}));
|
|
1173
1279
|
}
|
|
@@ -1286,15 +1392,21 @@
|
|
|
1286
1392
|
MafTabixAdapterF(pluginManager);
|
|
1287
1393
|
MafAddTrackWorkflowF(pluginManager);
|
|
1288
1394
|
}
|
|
1289
|
-
configure(
|
|
1395
|
+
configure(_pluginManager) { }
|
|
1290
1396
|
}
|
|
1291
1397
|
|
|
1292
1398
|
async function renderSvg(self, opts, superRenderSvg) {
|
|
1293
|
-
const {
|
|
1399
|
+
const { height } = self;
|
|
1400
|
+
const { offsetPx, width } = util.getContainingView(self);
|
|
1401
|
+
const clipid = `mafclip-${self.id}`;
|
|
1294
1402
|
return (React.createElement(React.Fragment, null,
|
|
1295
|
-
React.createElement("
|
|
1296
|
-
|
|
1297
|
-
|
|
1403
|
+
React.createElement("defs", null,
|
|
1404
|
+
React.createElement("clipPath", { id: clipid },
|
|
1405
|
+
React.createElement("rect", { x: 0, y: 0, width: width, height: height }))),
|
|
1406
|
+
React.createElement("g", { clipPath: `url(#${clipid})` },
|
|
1407
|
+
React.createElement("g", { id: "snpcov" }, await superRenderSvg(opts)),
|
|
1408
|
+
React.createElement("g", { transform: `translate(${Math.max(-offsetPx, 0)})` },
|
|
1409
|
+
React.createElement(YScaleBars, { model: self, orientation: "left", exportSVG: true })))));
|
|
1298
1410
|
}
|
|
1299
1411
|
|
|
1300
1412
|
var renderSvg$1 = {
|