1 // ==========================================================================
2 //                 SeqAn - The Library for Sequence Analysis
3 // ==========================================================================
4 // Copyright (c) 2006-2010, Knut Reinert, FU Berlin
5 // All rights reserved.
6 //
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions are met:
9 //
10 //     * Redistributions of source code must retain the above copyright
11 //       notice, this list of conditions and the following disclaimer.
12 //     * Redistributions in binary form must reproduce the above copyright
13 //       notice, this list of conditions and the following disclaimer in the
14 //       documentation and/or other materials provided with the distribution.
15 //     * Neither the name of Knut Reinert or the FU Berlin nor the names of
16 //       its contributors may be used to endorse or promote products derived
17 //       from this software without specific prior written permission.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 // ARE DISCLAIMED. IN NO EVENT SHALL KNUT REINERT OR THE FU BERLIN BE LIABLE
23 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
29 // DAMAGE.
30 //
31 // ==========================================================================
32 
33 #ifndef SEQAN_HEADER_MISC_SVG_H
34 #define SEQAN_HEADER_MISC_SVG_H
35 
36 namespace SEQAN_NAMESPACE_MAIN
37 {
38 
39 //////////////////////////////////////////////////////////////////////////////
40 // SVG File Context
41 //////////////////////////////////////////////////////////////////////////////
42 
43 struct SVGFile
44 {
45 	std::fstream file;
46 
47 	Pair<int> cursor;
48 	String<CharString> style;
49 
50 	int dpSequence;
51 	int dpMatrix;
52 	int dpTrace;
53 
54 	int text;
55 
56 	int readForward;
57 	int readReverse;
58 	int readText;
59 	int rulerTextTicks;
60 	int rulerTextLabel;
61 
62 	friend inline void svgWriteHeader(SVGFile &svg);
63 	friend inline void svgWriteFooter(SVGFile &svg);
64 
65 	template <typename TFileName>
66 	friend inline bool open(SVGFile &svg, TFileName const &fileName);
67 	friend inline bool close(SVGFile &svg);
68 
SVGFileSVGFile69 	SVGFile():
70 		cursor(0,0)
71 	{
72 		resetStyles();
73 	}
74 
SVGFileSVGFile75 	SVGFile(char const *fileName):
76 		cursor(0,0)
77 	{
78 		resetStyles();
79 		open(*this, fileName);
80 	}
81 
~SVGFileSVGFile82 	~SVGFile()
83 	{
84 		close(*this);
85 	}
86 
87 private:
88 
resetStylesSVGFile89 	inline void resetStyles()
90 	{
91 		clear(style);
92 		readText = dpSequence = length(style);
93 		appendValue(style, "style=\"text-anchor:middle; font-family:Bitstream Vera Sans,Verdana,sans-serif;\" font-size=\"18px\"");
94 		text = length(style);
95 		appendValue(style, "style=\"text-anchor:middle; font-family:Courier New,Verdana,sans-serif; font-weight:bold;\" font-size=\"20px\"");
96 		rulerTextTicks = length(style);
97 		appendValue(style, "style=\"text-anchor:middle; font-family:Verdana,sans-serif;\" font-size=\"6px\"");
98 		rulerTextLabel = length(style);
99 		appendValue(style, "style=\"text-anchor:left; font-family:Verdana,sans-serif; font-weight:bold;\" font-size=\"8px\"");
100 
101 		dpMatrix = length(style);
102 		appendValue(style, "style=\"stroke:lightgray;stroke-width:1;\" marker-end=\"url(#startMarkerNormal)\"");
103 		dpTrace = length(style);
104 		appendValue(style, "style=\"stroke:black;stroke-width:2;\" marker-end=\"url(#startMarkerBold)\"");
105 
106 		readForward = length(style);
107 		appendValue(style, "style=\"stroke:darkblue;stroke-width:3;\"");
108 		appendValue(style, "style=\"stroke:lightblue;stroke-width:1;\"");
109 		appendValue(style, "style=\"stroke:darkblue;stroke-width:3;\" marker-end=\"url(#startMarkerForward)\"");
110 		appendValue(style, "style=\"stroke:lightblue;stroke-width:1;\" marker-end=\"url(#startMarkerForward)\"");
111 		readReverse = length(style);
112 		appendValue(style, "style=\"stroke:darkred;stroke-width:3;\"");
113 		appendValue(style, "style=\"stroke:salmon;stroke-width:1;\"");
114 		appendValue(style, "style=\"stroke:darkred;stroke-width:3;\" marker-end=\"url(#startMarkerReverse)\"");
115 		appendValue(style, "style=\"stroke:salmon;stroke-width:1;\" marker-end=\"url(#startMarkerReverse)\"");
116 	}
117 };
118 
svgWriteHeader(SVGFile & svg)119 inline void svgWriteHeader(SVGFile &svg)
120 {
121 	svg.file << "<?xml version=\"1.0\"?>" << std::endl;
122 	svg.file << "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"" << std::endl;
123 	svg.file << "  \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">" << std::endl;
124 	svg.file << std::endl;
125 	svg.file << "<svg version=\"1.1\"" << std::endl;
126 	svg.file << "    xmlns=\"http://www.w3.org/2000/svg\"" << std::endl;
127 	svg.file << "    xmlns:xlink=\"http://www.w3.org/1999/xlink\">" << std::endl;
128     svg.file << "<defs>" << std::endl;
129 
130 	// trace back arrow markers
131     svg.file << "	<g id=\"arrowMarker\" stroke-linecap=\"round\">" << std::endl;
132     svg.file << "		<line x1=\"-6\" y1=\"1.5\" x2=\"0\" y2=\"0\" />" << std::endl;
133     svg.file << "		<line x1=\"-6\" y1=\"-1.5\" x2=\"0\" y2=\"0\" />" << std::endl;
134     svg.file << "	</g>" << std::endl;
135     svg.file << "	<marker id=\"startMarkerNormal\" markerWidth=\"6\" markerHeight=\"3\" style=\"overflow:visible\" orient=\"auto\" markerUnits=\"userSpaceOnUse\">" << std::endl;
136     svg.file << "		<use xlink:href=\"#arrowMarker\" stroke=\"lightgray\" />" << std::endl;
137     svg.file << "	</marker>" << std::endl;
138     svg.file << "	<marker id=\"startMarkerBold\" markerWidth=\"6\" markerHeight=\"4\" style=\"overflow:visible\" orient=\"auto\" markerUnits=\"userSpaceOnUse\">" << std::endl;
139     svg.file << "		<use xlink:href=\"#arrowMarker\" stroke-width=\"2\" stroke=\"black\" />" << std::endl;
140     svg.file << "	</marker>" << std::endl;
141 
142 	// read mapping arrow markers
143     svg.file << "	<marker id=\"startMarkerForward\" markerWidth=\"10\" markerHeight=\"4\" style=\"overflow:visible\" orient=\"auto\" markerUnits=\"userSpaceOnUse\">" << std::endl;
144     svg.file << "		<polyline points=\"-8,0 -9,-6 3,0 -9,6 -8,0\" fill=\"darkblue\" />" << std::endl;
145     svg.file << "	</marker>" << std::endl;
146     svg.file << "	<marker id=\"startMarkerReverse\" markerWidth=\"10\" markerHeight=\"4\" style=\"overflow:visible\" orient=\"auto\" markerUnits=\"userSpaceOnUse\">" << std::endl;
147     svg.file << "		<polyline points=\"-8,0 -9,-6 3,0 -9,6 -8,0\" fill=\"darkred\" />" << std::endl;
148     svg.file << "	</marker>" << std::endl;
149 
150     svg.file << " </defs>" << std::endl;
151 	svg.file << std::endl;
152 }
153 
svgWriteFooter(SVGFile & svg)154 inline void svgWriteFooter(SVGFile &svg)
155 {
156 	svg.file << std::endl;
157 	svg.file << "</svg>" << std::endl;
158 }
159 
160 
161 template <typename TFileName>
open(SVGFile & svg,TFileName const & fileName)162 inline bool open(SVGFile &svg, TFileName const &fileName)
163 {
164 	svg.file.open(fileName, std::ios_base::out);
165 	if (!svg.file.is_open()) return false;
166 	svgWriteHeader(svg);
167 	return true;
168 }
169 
close(SVGFile & svg)170 inline bool close(SVGFile &svg)
171 {
172 	if (svg.file.is_open())
173 	{
174 		svgWriteFooter(svg);
175 		svg.file.close();
176 	}
177 	return true;
178 }
179 
180 template <typename TChar>
181 inline void
_streamPut(SVGFile & svg,TChar character)182 _streamPut(SVGFile & svg, TChar character)
183 {
184 	if (convert<char>(character) == '\n')
185 	{
186 		++svg.cursor.i2;
187 		svg.cursor.i1 = 0;
188 	} else if (convert<char>(character) == '\t')
189 	{
190 		svg.cursor.i1 = (svg.cursor.i1 & ~7) + 8;
191 	} else
192 	{
193 		if (convert<char>(character) != ' ')
194 		{
195 			svg.file << "<g transform=\"translate(" << svg.cursor.i1*20+10 << ',' << svg.cursor.i2*20+10 << ")\"><text y=\"0.3em\" " << svg.style[svg.text] << '>';
196 			_streamPut(svg.file, convert<char>(character));
197 			svg.file << "</text></g>" << std::endl;
198 		}
199 		++svg.cursor.i1;
200 	}
201 }
202 
203 template <typename TFormatTag, typename TContigGaps, typename TContigName>
_printContig(SVGFile & svg,Tag<TFormatTag> const & format,AlignedReadLayout &,TContigGaps & contigGaps,TContigName const & contigName)204 inline void _printContig(
205 	SVGFile &svg,
206 	Tag<TFormatTag> const &format,
207 	AlignedReadLayout &,
208 	TContigGaps &contigGaps,
209 	TContigName const &contigName)
210 {
211 	typedef typename Iterator<TContigGaps, Standard>::Type TContigIterator;
212 
213 	TContigIterator cit = begin(contigGaps, Standard());
214 	TContigIterator citEnd = end(contigGaps, Standard());
215 	for (__int64 ofs = 0; cit != citEnd; ++cit, ++ofs)
216 	{
217 		if (!isGap(cit))
218 		{
219 			if (ofs == 0)
220 			{
221 				svg.file << "<g transform=\"translate(" << ofs*20+2 << ',' << svg.cursor.i2*20+10 << ")\"><text y=\"0.3em\" " << svg.style[svg.rulerTextLabel] << '>';
222 				svg.file << contigName << "</text></g>" << std::endl;
223 			}
224 
225 			__int64 seqPos = cit.current.seqPos + 1;
226 			if (seqPos % 5 == 0)
227 			{
228 				if (seqPos % 10 == 0)
229 				{
230 					if (ofs >= 5)
231 					{
232 						svg.file << "<g transform=\"translate(" << ofs*20+10 << ',' << svg.cursor.i2*20+10 << ")\"><text y=\"0.3em\" " << svg.style[svg.rulerTextTicks] << '>';
233 						svg.file << seqPos << "</text></g>" << std::endl;
234 					}
235 
236 					svg.file << "<line x1=\"" << ofs*20+10 << "\" x2=\"" << ofs*20+10 << "\" ";
237 					svg.file << "y1=\"" << svg.cursor.i2*20+12 << "\" y2=\"" << svg.cursor.i2*20+15 << "\" ";
238 				} else {
239 					svg.file << "<line x1=\"" << ofs*20+10 << "\" x2=\"" << ofs*20+10 << "\" ";
240 					svg.file << "y1=\"" << svg.cursor.i2*20+12 << "\" y2=\"" << svg.cursor.i2*20+15 << "\" ";
241 				}
242 				svg.file << "stroke-width=\"1\" stroke=\"gray\" />" << std::endl;
243 			}
244 		}
245 	}
246 	_streamPut(svg, '\n');
247 
248 	int savedStyle = svg.text;
249 	svg.text = svg.readText;
250 	write(svg, contigGaps, "", format);
251 	svg.text = savedStyle;
252 }
253 
254 template <typename TFormatTag, typename TContigGaps, typename TReadGaps, typename TAlignedRead, typename TLine>
_printRead(SVGFile & svg,Tag<TFormatTag> const &,AlignedReadLayout & layout,TContigGaps & contigGaps,TReadGaps & readGaps,TAlignedRead & alignedRead,TLine line)255 inline void _printRead(
256 	SVGFile &svg,
257 	Tag<TFormatTag> const &,
258 	AlignedReadLayout &layout,
259 	TContigGaps &contigGaps,
260 	TReadGaps &readGaps,
261 	TAlignedRead &alignedRead,
262 	TLine line)
263 {
264 	typedef typename Iterator<TContigGaps, Standard>::Type TContigIterator;
265 	typedef typename Iterator<TReadGaps, Standard>::Type TIterator;
266 
267 	__int64 x;
268 	__int64 xEnd;
269 	int style, arrow;
270 	const char *first;
271 	const char *second;
272 	if (alignedRead.beginPos < alignedRead.endPos)
273 	{
274 		xEnd = alignedRead.beginPos * 20;
275 		x = xEnd + 5;
276 		first = "<line x1=\"";
277 		second = "\" x2=\"";
278 		style = svg.readForward;
279 		arrow = 0;
280 	} else {
281 		xEnd = alignedRead.endPos * 20;
282 		x = xEnd + 10;
283 		first = "<line x2=\"";
284 		second = "\" x1=\"";
285 		style = svg.readReverse;
286 		arrow = 2;
287 	}
288 	line = svg.cursor.i2 * 20 + 10;
289 
290 	if (length(layout.mateCoords) <= alignedRead.pairMatchId)
291 		resize(layout.mateCoords, alignedRead.pairMatchId + 1, Pair<int>(-1,-1));
292 	else
293 	{
294 		if (layout.mateCoords[alignedRead.pairMatchId].i2 != -1)
295 		{
296 			Pair<__int64> a(alignedRead.beginPos * 20, line);
297 			Pair<__int64> b(layout.mateCoords[alignedRead.pairMatchId]);
298 			if (a.i1 < b.i1)
299 			{
300 				Pair<__int64> tmp = a;
301 				a = b;
302 				b = tmp;
303 			}
304 			__int64 dx = (b.i1 - a.i1);
305 			__int64 dy = (b.i2 - a.i2);
306 
307 			svg.file << "<path d=\"M " << a.i1 << ',' << a.i2;
308 			svg.file << " C " << a.i1+dy/10 << ',' << a.i2-dx/10;
309 			svg.file << ' ' << b.i1+dy/10 << ',' << b.i2-dx/10;
310 			svg.file << ' ' << b.i1 << ',' << b.i2;
311 			svg.file << "\" stroke-width=\"2\" stroke=\"black\" stroke-opacity=\"0.2\" fill=\"none\"/>";
312 		}
313 		else
314 			layout.mateCoords[alignedRead.pairMatchId] = Pair<int>(alignedRead.beginPos * 20, line);
315 	}
316 
317 
318 	TContigIterator cit = begin(contigGaps, Standard()) + _min(alignedRead.beginPos, alignedRead.endPos);
319 	TIterator it = begin(readGaps, Standard());
320 	TIterator itEnd = end(readGaps, Standard());
321 	int lastWasGap = -1;
322 	int inGap;
323 
324 	for (; it != itEnd; ++it, ++cit, xEnd += 20)
325 	{
326 		inGap = isGap(it);
327 		if (lastWasGap != inGap || inGap != static_cast<int>(isGap(cit)) || (!inGap && convert<Dna5>(*cit) != convert<Dna5>(*it)))
328 		{
329 			if (x < xEnd && lastWasGap != -1)
330 			{
331 				svg.file << first << x << "\" y1=\"" << line << second << xEnd;
332 				svg.file << "\" y2=\"" << line << "\" " << svg.style[style + arrow + lastWasGap] << " />" << std::endl;
333 				arrow = 0;
334 			}
335 			lastWasGap = inGap;
336 			x = xEnd;
337 			if (!inGap && convert<Dna5>(*cit) != convert<Dna5>(*it))
338 			{
339 				svg.file << "<g transform=\"translate(" << xEnd + 10 << ',' << line << ")\"><text y=\"0.3em\" " << svg.style[svg.readText] << '>';
340 				_streamPut(svg.file, convert<char>(*it));
341 				svg.file << "</text></g>" << std::endl;
342 				x += 20;
343 				arrow = 0;
344 			}
345 		}
346 	}
347 	if (x < xEnd && lastWasGap != -1)
348 	{
349 		if (alignedRead.beginPos < alignedRead.endPos)
350 		{
351 			arrow = 2;
352 			xEnd -= 10;
353 		} else
354 			xEnd -= 5;
355 		svg.file << first << x << "\" y1=\"" << line << second << xEnd;
356 		svg.file << "\" y2=\"" << line << "\" " << svg.style[style + arrow + lastWasGap] << " />" << std::endl;
357 	}
358 }
359 
360 
361 //////////////////////////////////////////////////////////////////////////////
362 // stream operators
363 //////////////////////////////////////////////////////////////////////////////
364 /*
365 template <typename TSource>
366 inline SVGFile &
367 operator << (SVGFile & target,
368 			 TSource  source)
369 {
370 SEQAN_CHECKPOINT
371 	write(target, source);
372 	return target;
373 }
374 */
375 
376 inline SVGFile &
377 operator << (SVGFile & target, char source)
378 {
379 SEQAN_CHECKPOINT
380 	write(target, source);
381 	return target;
382 }
383 
384 inline SVGFile &
385 operator << (SVGFile & target, char const *source)
386 {
387 SEQAN_CHECKPOINT
388 	write(target, source);
389 	return target;
390 }
391 
392 
393 template <typename TStringSet, typename TTrace, typename TIndexPair>
394 void
_alignNeedlemanWunschMatrix(SVGFile & svg,TStringSet const & str,TTrace const & trace,TIndexPair const &)395 _alignNeedlemanWunschMatrix(SVGFile& svg,
396 							  TStringSet const& str,
397 							  TTrace const& trace,
398 							  TIndexPair const&)
399 {
400 SEQAN_CHECKPOINT
401 	typedef typename Size<TStringSet>::Type TSize;
402 	typedef typename Value<TTrace>::Type TTraceValue;
403 
404 	// TraceBack values
405 	const int Diagonal = 0;
406 	const int Horizontal = 1;
407 	const int Vertical = 2;
408 
409 	// Initialization
410 	TSize numCols = length(str[0]);
411 	TSize numRows = length(str[1]);
412 
413 	// Print trace matrix
414 	for (TSize pos0 = 0; pos0 < numCols; ++pos0)
415 	{
416 		for (TSize pos1 = 0; pos1 < numRows; ++pos1)
417 		{
418 			int tv =(int)trace[pos0*numRows + pos1];
419 				if (tv & (1 << Diagonal))
420 					svg.file << "<line x2=\"" << pos0*20+10 << "\" y2=\"" << pos1*20+10 << "\"" << " x1=\"" << (pos0+1)*20+10 << "\" y1=\"" << (pos1+1)*20+10 << "\" " << svg.style[svg.dpMatrix] << " />" << std::endl;
421 
422 				if (tv & (1 << Horizontal))
423 					svg.file << "<line x2=\"" << pos0*20+10 << "\" y2=\"" << (pos1+1)*20+10 << "\"" << " x1=\"" << (pos0+1)*20+10 << "\" y1=\"" << (pos1+1)*20+10 << "\" " << svg.style[svg.dpMatrix] << " />" << std::endl;
424 
425 				if (tv & (1 << Vertical))
426 					svg.file << "<line x2=\"" << (pos0+1)*20+10 << "\" y2=\"" << pos1*20+10 << "\"" << " x1=\"" << (pos0+1)*20+10 << "\" y1=\"" << (pos1+1)*20+10 << "\" " << svg.style[svg.dpMatrix] << " />" << std::endl;
427 		}
428 	}
429 
430 	// Print sequences
431 	for (TSize pos0 = 0; pos0 < numCols; ++pos0)
432 		svg.file << "<g transform=\"translate(" << pos0*20+20 << ",5)\"><text y=\"0.3em\" " << svg.style[svg.dpSequence] << '>' << str[0][pos0] << "</text></g>" << std::endl;
433 
434 	for (TSize pos1 = 0; pos1 < numRows; ++pos1)
435 		svg.file << "<g transform=\"translate(0," << pos1*20+25 << ")\"><text y=\"0.3em\" " << svg.style[svg.dpSequence] << '>' << str[1][pos1] << "</text></g>" << std::endl;
436 }
437 
438 template <typename TStringSet, typename TId, typename TPos, typename TTraceValue>
439 inline void
_alignTracePrint(SVGFile & svg,TStringSet const &,TId const,TPos pos1,TId const,TPos pos2,TPos const segLen,TTraceValue const tv)440 _alignTracePrint(SVGFile& svg,
441 				   TStringSet const&,
442 				   TId const,
443 				   TPos pos1,
444 				   TId const,
445 				   TPos pos2,
446 				   TPos const segLen,
447 				   TTraceValue const tv)
448 {
449 	// TraceBack values
450 	TTraceValue Diagonal = 0; TTraceValue Horizontal = 1; TTraceValue Vertical = 2;
451 
452 	svg.file << "<line x2=\"" << pos1*20+10 << "\" y2=\"" << pos2*20+10 << "\"";
453 	if (tv == Diagonal) {
454 		pos1 += segLen; pos2 += segLen;
455 	} else if (tv == Horizontal) {
456 		pos1 += segLen;
457 	} else if (tv == Vertical) {
458 		pos2 += segLen;
459 	}
460 	svg.file << " x1=\"" << pos1*20+10 << "\" y1=\"" << pos2*20+10;
461 	svg.file << "\" " << svg.style[svg.dpTrace] << " />" << std::endl;
462 }
463 
464 //////////////////////////////////////////////////////////////////////////////
465 
466 
467 
468 } //namespace SEQAN_NAMESPACE_MAIN
469 
470 #endif //#ifndef SEQAN_HEADER_...
471