OpenMS
Loading...
Searching...
No Matches
Annotation1DPeakItem.h
Go to the documentation of this file.
1// Copyright (c) 2002-present, OpenMS Inc. -- EKU Tuebingen, ETH Zurich, and FU Berlin
2// SPDX-License-Identifier: BSD-3-Clause
3//
4// --------------------------------------------------------------------------
5// $Maintainer: Johannes Veit $
6// $Authors: Johannes Junker $
7// --------------------------------------------------------------------------
8
9#pragma once
10
12
17
18#include <QtGui/QColor>
19#include <QtGui/QFontMetricsF>
20
21namespace OpenMS
22{
23
27 template <class DataPoint> // e.g. Peak1D
29 public Annotation1DItem
30 {
31public:
33 Annotation1DPeakItem(const DataPoint& peak_position, const QString& text, const QColor& color) :
34 Annotation1DItem(text), peak_position_(peak_position), position_(peak_position), color_(color)
35 {
36 }
37
40
42 ~Annotation1DPeakItem() override = default;
43
44 // Docu in base class
45 void draw(Plot1DCanvas* const canvas, QPainter& painter, bool flipped = false) override
46 {
47 painter.save();
48
49 painter.setPen(color_);
50
51 QPoint position_widget, peak_position_widget;
52
53 // translate units to pixel coordinates
54 canvas->dataToWidget(canvas->getMapper().map(position_), position_widget, flipped);
55 canvas->dataToWidget(canvas->getMapper().map(peak_position_), peak_position_widget, flipped);
56
57 // pre-compute bounding box of text_item
58 const QFontMetricsF fm(QApplication::font());
59 const auto prebox = fm.boundingRect(QRectF(position_widget.x(), position_widget.y(), 0, 0), Qt::AlignCenter, getText());
60 // Shift position of the widget/text, so it sits 'on top' of the peak
61 // We can only do that there, since we do not know the state of 'flipped' in general
62 // Compute the delta in data-units, NOT pixels, since the shift (up/down, or even left/right) depends on state of 'flipped' and axis
63 const auto deltaXY_in_units = canvas->widgetToDataDistance(prebox.width(), prebox.height()).abs(); // abs() to make sure y axis is not negative
64 const auto delta_gravity_in_units = canvas->getGravitator().swap().gravitateZero(deltaXY_in_units); // only keep gravity dim
65 // recompute 'position_widget', shifting the text up by 1/2 box
66 canvas->dataToWidget(canvas->getMapper().map(position_) + delta_gravity_in_units / 2, position_widget, flipped);
67 // re-compute bounding box of text_item on with new position!
68 bounding_box_ = fm.boundingRect(QRectF(position_widget.x(), position_widget.y(), 0, 0), Qt::AlignCenter, getText());
69
70
71 // draw connection line between anchor point and current position if pixel coordinates differ significantly
72 if ((position_widget - peak_position_widget).manhattanLength() > 2)
73 {
74 QPointF border_point = GUIHelpers::intersectionPoint(bounding_box_, peak_position_widget);
75 if (bounding_box_.center() != border_point)
76 {
77 painter.save();
78 painter.setPen(Qt::DashLine);
79 painter.drawLine(peak_position_widget, border_point);
80 painter.restore();
81 }
82 }
83
84 // some pretty printing
85 QString text = text_;
86 if (!text.contains("<\\")) // don't process HTML strings again
87 {
88 // extract ion index
89 {
90 QRegularExpression reg_exp(R"(([abcdwxyz])(\d+))");
91 QRegularExpressionMatch match = reg_exp.match(text);
92 if (text.indexOf(reg_exp) == 0) // only process if at the beginning of the string
93 {
94 text.replace(reg_exp, "\\1<sub>\\2</sub>");
95 }
96 else // protein-protein XL specific ion names
97 { // e.g. "[alpha|ci$y1]"
98 QRegularExpression reg_exp_xlms(R"((ci|xi)[$][abcxyz](\d+))");
99 auto match_pos = text.indexOf(reg_exp_xlms);
100 if ((match_pos == 6) || (match_pos == 7))
101 {
102 // set the match_pos to the position of the ion index
103 match_pos += 3; // skip "ci$" or "xi$"
104 ++match_pos; // skip the ion type (=captured(1))
105 QString charge_str = match.captured(2);
106 // put sub html tag around number
107 text = text.left(match_pos) + QString("<sub>") + charge_str + QString("</sub>") + text.right(text.size() - match_pos - charge_str.size());
108 }
109 }
110 }
111
112 // common losses
113 text.replace("H2O1", "H<sub>2</sub>O"); // mind the order with H2O substitution
114 text.replace("H2O", "H<sub>2</sub>O");
115 text.replace("NH3", "NH<sub>3</sub>");
116 text.replace("H3N1", "NH<sub>3</sub>");
117 text.replace("C1H4O1S1", "H<sub>4</sub>COS"); // methionine sulfoxide loss
118
119 // nucleotide XL related losses
120 text.replace("H3PO4", "H<sub>3</sub>PO<sub>4</sub>");
121 text.replace("HPO3", "HPO<sub>3</sub>");
122 text.replace("C3O", "C<sub>3</sub>O");
123
124 // charge format: +z
125 QRegularExpression charge_rx(R"([\+|\-](\d+)$)");
126 int match_pos = text.indexOf(charge_rx);
127 if (match_pos > 0)
128 {
129 text = text.left(match_pos) + QString("<sup>") + text[match_pos] // + or -
130 + charge_rx.match(text).captured(1) + QString("</sup>"); // charge
131 }
132
133 // charge format: z+
134 charge_rx = QRegularExpression(R"((\d+)[\+|\-]$)");
135 match_pos = text.indexOf(charge_rx);
136 if (match_pos > 0)
137 {
138 auto charge_match = charge_rx.match(text).captured(1);
139 text = text.left(match_pos) + QString("<sup>") + charge_match // charge
140 + text[match_pos + charge_match.size()] + QString("</sup>"); // + or -
141 }
142
143 text.replace(QRegularExpression(R"(\+\+$)"), "<sup>2+</sup>");
144 text.replace(QRegularExpression(R"(\+$)"), "");
145 text.replace(QRegularExpression(R"(\-\-$)"), "<sup>2-</sup>");
146 text.replace(QRegularExpression(R"(\-$)"), "");
147 }
148
149 text = "<font color=\"" + color_.name() + "\">" + text + "</font>";
150
151 // draw html text
152 {
153 QTextDocument td;
154 td.setHtml(text);
155 painter.save();
156 double w = td.size().width();
157 double h = td.size().height();
158 painter.translate(position_widget.x() - w / 2, position_widget.y() - h / 2);
159 td.drawContents(&painter);
160 painter.restore();
161 }
162
163 if (selected_)
164 {
165 drawBoundingBox_(painter);
166 }
167
168 painter.restore();
169 }
170
171 // Docu in base class
172 void move(const PointXYType delta, const Gravitator& /*gr*/, const DimMapper<2>& dim_mapper) override
173 {
174 auto pos_xy = dim_mapper.map(position_);
175 pos_xy += delta;
176 dim_mapper.fromXY(pos_xy, position_);
177 }
178
180 void setPosition(const DataPoint& position)
181 {
182 position_ = position;
183 }
184
186 const DataPoint& getPosition() const
187 {
188 return position_;
189 }
190
192 const DataPoint& getPeakPosition() const
193 {
194 return peak_position_;
195 }
196
197 // Docu in base class
198 void ensureWithinDataRange(Plot1DCanvas* const canvas, const int layer_index) override
199 {
200 canvas->pushIntoDataRange(position_, layer_index);
201 }
202
204 void setColor(const QColor& color)
205 {
206 color_ = color;
207 }
208
210 const QColor& getColor() const
211 {
212 return color_;
213 }
214
217 {
218 // add new fragment annotation
219 QString peak_anno = this->getText().trimmed();
220
221 // check for newlines in the label and only continue with the first line for charge determination
222 peak_anno.remove('\r');
223 QStringList lines = peak_anno.split('\n', Qt::SkipEmptyParts);
224 if (lines.size() > 1)
225 {
226 peak_anno = lines[0];
227 }
228
229 // regular expression for a charge at the end of the annotation
230 QRegularExpression reg_exp(R"(([\+|\-]\d+)$)");
231
232 // read charge and text from annotation item string
233 // we support two notations for the charge suffix: '+2' or '++'
234 // cut and convert the trailing + or - to a proper charge
235 int match_pos = peak_anno.indexOf(reg_exp);
236 int tmp_charge(0);
237 if (match_pos >= 0)
238 {
239 tmp_charge = reg_exp.match(peak_anno).captured(1).toInt();
240 peak_anno = peak_anno.left(match_pos);
241 }
242 else
243 {
244 // count number of + and - in suffix (e.g., to support "++" as charge 2 annotation)
245 int plus(0), minus(0);
246
247 for (int p = (int)peak_anno.size() - 1; p >= 0; --p)
248 {
249 if (peak_anno[p] == '+')
250 {
251 ++plus;
252 continue;
253 }
254 else if (peak_anno[p] == '-')
255 {
256 ++minus;
257 continue;
258 }
259 else // not '+' or '-'?
260 {
261 if (plus > 0 && minus == 0) // found pluses?
262 {
263 tmp_charge = plus;
264 peak_anno = peak_anno.left(peak_anno.size() - plus);
265 break;
266 }
267 else if (minus > 0 && plus == 0) // found minuses?
268 {
269 tmp_charge = -minus;
270 peak_anno = peak_anno.left(peak_anno.size() - minus);
271 break;
272 }
273 break;
274 }
275 }
276 }
277
279 fa.charge = tmp_charge;
280 fa.mz = this->getPeakPosition().getMZ();
281 fa.intensity = this->getPeakPosition().getIntensity();
282 if (lines.size() > 1)
283 {
284 peak_anno.append("\n").append(lines[1]);
285 }
286 fa.annotation = fromQString(peak_anno);
287
288 return fa;
289 }
290
291 // Docu in base class
292 Annotation1DItem* clone() const override
293 {
294 return new Annotation1DPeakItem(*this);
295 }
296
297 protected:
299 DataPoint peak_position_;
300
302 DataPoint position_;
303
305 QColor color_;
306 };
307} // namespace OpenMS
An abstract class acting as an interface for the different 1D annotation items.
Definition Annotation1DItem.h:36
QRectF bounding_box_
The current bounding box of this item on the canvas where it has last been drawn.
Definition Annotation1DItem.h:83
QString text_
The displayed text.
Definition Annotation1DItem.h:89
void drawBoundingBox_(QPainter &painter)
Draws the bounding_box_.
bool selected_
Determines whether this item is currently selected on the canvas.
Definition Annotation1DItem.h:86
const QString & getText() const
Returns the text of the item.
A peak annotation item.
Definition Annotation1DPeakItem.h:30
~Annotation1DPeakItem() override=default
Destructor.
const DataPoint & getPeakPosition() const
Returns the position of the annotated peak.
Definition Annotation1DPeakItem.h:192
Annotation1DItem * clone() const override
Creates a copy of the item on the heap and returns a pointer.
Definition Annotation1DPeakItem.h:292
void draw(Plot1DCanvas *const canvas, QPainter &painter, bool flipped=false) override
Draws the item on painter.
Definition Annotation1DPeakItem.h:45
const DataPoint & getPosition() const
Returns the position of the label (peak)
Definition Annotation1DPeakItem.h:186
QColor color_
The color of the label.
Definition Annotation1DPeakItem.h:305
Annotation1DPeakItem(const Annotation1DPeakItem &rhs)=default
Copy constructor.
void ensureWithinDataRange(Plot1DCanvas *const canvas, const int layer_index) override
Ensures that the item has coordinates within the visible area of the canvas.
Definition Annotation1DPeakItem.h:198
PeptideHit::PeakAnnotation toPeakAnnotation() const
Convert the 'text()' to a Peptide::PeakAnnotation.
Definition Annotation1DPeakItem.h:216
DataPoint peak_position_
The position of the anchor (e.g. the Peak1D)
Definition Annotation1DPeakItem.h:299
void setColor(const QColor &color)
Set the color of the label.
Definition Annotation1DPeakItem.h:204
Annotation1DPeakItem(const DataPoint &peak_position, const QString &text, const QColor &color)
Constructor.
Definition Annotation1DPeakItem.h:33
void setPosition(const DataPoint &position)
Sets the position of the label.
Definition Annotation1DPeakItem.h:180
void move(const PointXYType delta, const Gravitator &, const DimMapper< 2 > &dim_mapper) override
Moves the item on the drawing canvas; behavior depends on item type and is implemented in the subclas...
Definition Annotation1DPeakItem.h:172
const QColor & getColor() const
Returns the color of the label.
Definition Annotation1DPeakItem.h:210
DataPoint position_
The position of the label (e.g. the Peak1D)
Definition Annotation1DPeakItem.h:302
DPosition & abs() noexcept
Make all dimension values positive.
Definition DPosition.h:113
Allows dynamical switching (at runtime) between a dimension (RT, m/z, int, IM, etc) and X,...
Definition DimMapper.h:662
void fromXY(const DRange< N_DIM > &in, RangeManager< Ranges... > &output) const
Definition DimMapper.h:752
Point map(const T &data) const
convert an OpenMS datatype (such as Feature) to an N_DIM-dimensional point
Definition DimMapper.h:716
Manipulates X or Y component of points in the X-Y plane, by assuming one axis (either X or Y axis) ha...
Definition Plot1DCanvas.h:42
QPoint gravitateZero(QPoint p) const
Definition Plot1DCanvas.h:204
Gravitator swap() const
Swap gravity axis (from X to Y, or vice versa)
Definition Plot1DCanvas.h:108
Canvas for visualization of one or several spectra.
Definition Plot1DCanvas.h:295
void dataToWidget(const DPosition< 2 > &peak, QPoint &point, bool flipped=false)
For convenience - calls dataToWidget.
void pushIntoDataRange(T &data_point, const int layer_index)
Pushes a data point back into the valid data range of the current layer area. Useful for annotation i...
Definition Plot1DCanvas.h:434
const Gravitator & getGravitator() const
Get gravity manipulation object to apply gravity to points.
Definition Plot1DCanvas.h:504
PointXYType widgetToDataDistance(double x, double y)
compute distance in data coordinates (unit axis as shown) when moving x/y pixel in chart/widget coord...
Definition Plot1DCanvas.h:412
const DimMapper< 2 > & getMapper() const
Get Mapper to translate between values for axis (X/Y) and units (m/z, RT, intensity,...
QPointF intersectionPoint(const QRectF &rect, const QPointF &p)
Find the point on a rectangle where a ray/line from a point p to its center would intersect at.
Main OpenMS namespace.
Definition openswathalgo/include/OpenMS/OPENSWATHALGO/DATAACCESS/ISpectrumAccess.h:19
String fromQString(const QString &s)
Construct OpenMS::String from QString (replaces String(const QString&))
Definition Qt5Port.h:45
Contains annotations of a peak.
Definition PeptideHit.h:87
double intensity
Definition PeptideHit.h:91
double mz
Definition PeptideHit.h:90
String annotation
Definition PeptideHit.h:88
int charge
Definition PeptideHit.h:89