index.ts 5.82 KB
Newer Older
Remi  PLANEL's avatar
Remi PLANEL committed
1
import { GeneData, GenomeBrowserData } from "./types";
Remi  PLANEL's avatar
Remi PLANEL committed
2
3
import { select, event } from "d3-selection";
import { scaleLinear } from "d3-scale";
Remi  PLANEL's avatar
Remi PLANEL committed
4
import GenomeBrowser from "./component/genome-browser";
Remi  PLANEL's avatar
Remi PLANEL committed
5
import { color } from "d3";
Remi  PLANEL's avatar
Remi PLANEL committed
6
7
import genomeBrowser from "./component/genome-browser";
import { filter } from "rxjs/operators";
8
import { schemeSet1 } from "d3-scale-chromatic";
9

Remi  PLANEL's avatar
Remi PLANEL committed
10
const width = 1500;
Remi  PLANEL's avatar
Remi PLANEL committed
11
12
const height = 300;
const genomeBrowserComponent = GenomeBrowser();
Remi  PLANEL's avatar
Remi PLANEL committed
13

Remi  PLANEL's avatar
Remi PLANEL committed
14
15
16
17
18
19
20
21
22
23
24
25
const geneData: GeneData[] = [
  {
    name: "gene 1",
    strand: "-",
    begin: 20815,
    end: 21078,
    gene: "insA",
  },
  {
    name: "gene 2",
    strand: "+",
    begin: 21181,
Remi  PLANEL's avatar
Remi PLANEL committed
26
    end: 21400,
Remi  PLANEL's avatar
Remi PLANEL committed
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
    gene: "yaaY",
  },
  {
    name: "gene 3",
    strand: "+",
    begin: 21407,
    end: 22348,
    gene: "ribF"
  },
  {
    name: "gene 4",
    strand: "+",
    begin: 22391,
    end: 25207,
    gene: "ileS"
Remi  PLANEL's avatar
Remi PLANEL committed
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
  },
  {
    name: "gene 4",
    strand: "-",
    begin: 25304,
    end: 25956,
    gene: "foo"
  },
  {
    name: "gene 4",
    strand: "+",
    begin: 26004,
    end: 26329,
    gene: "foo"
  },
  {
    name: "gene 4",
    strand: "+",
    begin: 29004,
    end: 29429,
    gene: "foo"
  },

Remi  PLANEL's avatar
Remi PLANEL committed
65
];
Remi  PLANEL's avatar
Remi PLANEL committed
66
const chromosomeSize = 75000;
Remi  PLANEL's avatar
Remi PLANEL committed
67
68
69
70
71
72
73
const genomeBrowserData: GenomeBrowserData[] = [{
  width: 1500,
  genomeWindow: {
    center: 23000,
    size: 6000
  },
  currentMousePosition: 0,
Remi  PLANEL's avatar
Remi PLANEL committed
74
75
76
77
78
79
80
81
  chromosome: {
    size: chromosomeSize,
    genes: geneData
  },
  axis: {
    global: {
      title: "Genome XXXX (" + chromosomeSize + " bp)",
      interval: [0, chromosomeSize],
82
83
      window: [0, 0],

Remi  PLANEL's avatar
Remi PLANEL committed
84
85
86
87
88
89
    },
    chromosome: {
      title: "Chromosome X ",
      interval: [0, 0]
    }
  }
Remi  PLANEL's avatar
Remi PLANEL committed
90
}];
Remi  PLANEL's avatar
Remi PLANEL committed
91
92
93
94
const svg = select<SVGElement, any>("svg")
  .attr("width", width + 1)
  .attr("height", height);

Remi  PLANEL's avatar
Remi PLANEL committed
95
96
97
98
99

draw();



100
select("#zoom-in").on("click", function () {
Remi  PLANEL's avatar
Remi PLANEL committed
101
102
  genomeBrowserData[0].genomeWindow.size -= 1000;
  draw()
103
104
});
select("#zoom-out").on("click", function () {
Remi  PLANEL's avatar
Remi PLANEL committed
105
106
  genomeBrowserData[0].genomeWindow.size += 1000;
  draw()
107
});
Remi  PLANEL's avatar
Remi PLANEL committed
108

109
110
111
112
/*
    FUNCTIONS
*/
function draw() {
Remi  PLANEL's avatar
Remi PLANEL committed
113
  // 
114
115
116
117
118
119
120
121
122
  const computedGenomeBrowserData: GenomeBrowserData[] = updateGenomeBrowserData(genomeBrowserData);
  svg
    .datum(computedGenomeBrowserData)
    .call(genomeBrowserComponent, width, height);

}

function updateGenomeBrowserData(genomeBrowserData: GenomeBrowserData[]) {
  return genomeBrowserData
Remi  PLANEL's avatar
Remi PLANEL committed
123
124
    .map(function (genomesBrowser: GenomeBrowserData, i) {
      const { width, genomeWindow: { center, size }, chromosome: { genes } } = genomesBrowser;
125
      const genomeWindowBoundaries = getChromosomeInterval(center, size);
Remi  PLANEL's avatar
Remi PLANEL committed
126
127
128
129
130
131
132
133
134
135
136
137
      const xScale = scaleLinear()
        .domain(genomeWindowBoundaries)
        .range([0, width]);
      // Construct clickHandler function that depends of the current window.
      const clickHandler = function ([begin, end]: [number, number]) {
        genomesBrowser.genomeWindow.center = (end + begin) / 2;
        draw();
      };
      // Filter genes in order to display those visible
      const visibleGenes = genes.filter(
        gene => gene.end > genomeWindowBoundaries[0] || gene.begin < genomeWindowBoundaries[1]
      );
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
      // Callback when brushed => modify data and redraw the genome + axis
      const brushedCallback = function (scale: any) {
        if (!event.sourceEvent) return;
        if (event.selection) {
          const { selection: [x1, x2] } = event;
          const window = [scale.invert(x1), scale.invert(x2)];
          genomeBrowserData[i].genomeWindow.center = (window[0] + window[1]) / 2;
          genomeBrowserData[i].genomeWindow.size = window[1] - window[0];
          const newData = updateGenomeBrowserData(genomeBrowserData);
          newData.forEach(function (data) {
            genomeBrowserComponent.updateGenome(data.axis.chromosome, data.chromosome.genes);
          })
        }
      };

Remi  PLANEL's avatar
Remi PLANEL committed
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170

      const dragStartCallback = function (elem: SVGElement) {
        select(elem).classed("active", true);
        genomesBrowser.currentMousePosition = xScale
          .invert(event.x);
      }

      const draggedCallback = function () {
        const mousePosition = xScale
          .invert(event.x);
        const diff = genomesBrowser.currentMousePosition - mousePosition;
        genomesBrowser.currentMousePosition = mousePosition;
        genomeBrowserData[i].genomeWindow.center += diff;
        draw();
      };
      const dragendedCallback = function (elem: SVGElement) {
        select(elem).classed("active", false);
      }
171
      const globalAxisWindow = getChromosomeInterval(center, size);
Remi  PLANEL's avatar
Remi PLANEL committed
172
173
      const newGenomeBrowser = {
        ...genomesBrowser,
Remi  PLANEL's avatar
Remi PLANEL committed
174
175
176
        chromosome: {
          ...genomesBrowser.chromosome,
          genes: visibleGenes.map(function (gene) {
177
            // const fillPalette = schemeSet1(gene.strand);
Remi  PLANEL's avatar
Remi PLANEL committed
178
179
180
181
182
183
184
185
186
187
188
189
            const fill = gene.strand === "+" ? color("darkred") : color("darkblue");
            const stroke = (fill) ? fill.darker(1).toString() : "lighgray"
            return {
              ...gene,
              eventHandler: {
                click: clickHandler
              },
              fill: (fill) ? fill.toString() : "lightgray",
              stroke
            }
          })
        },
Remi  PLANEL's avatar
Remi PLANEL committed
190
        scale: xScale,
Remi  PLANEL's avatar
Remi PLANEL committed
191
192
193
194
        axis: {
          ...genomesBrowser.axis,
          global: {
            ...genomesBrowser.axis.global,
195
196
197
198
            window: globalAxisWindow,
            eventHandler: {
              brushed: brushedCallback
            }
Remi  PLANEL's avatar
Remi PLANEL committed
199
200
201
202
203
204
205
206
          },
          chromosome: {
            ...genomesBrowser.axis.chromosome,
            interval: globalAxisWindow

          }

        },
Remi  PLANEL's avatar
Remi PLANEL committed
207
208
209
210
211
212
213
214
215
216
        eventHandler: {
          dragged: draggedCallback,
          dragstarted: dragStartCallback,
          dragended: dragendedCallback
        }
      };

      return newGenomeBrowser;

    });
Remi  PLANEL's avatar
Remi PLANEL committed
217

Remi  PLANEL's avatar
Remi PLANEL committed
218

219
}
220
221

function getChromosomeInterval(center: number, genomeWindowSize: number): [number, number] {
Remi  PLANEL's avatar
Remi PLANEL committed
222
223
224
225
226
  const halfWindow = genomeWindowSize / 2;
  return [center - halfWindow, center + halfWindow]

}

227