OpenMS  3.0.0
Annotation1DPeakItem.h
Go to the documentation of this file.
1 // --------------------------------------------------------------------------
2 // OpenMS -- Open-Source Mass Spectrometry
3 // --------------------------------------------------------------------------
4 // Copyright The OpenMS Team -- Eberhard Karls University Tuebingen,
5 // ETH Zurich, and Freie Universitaet Berlin 2002-2022.
6 //
7 // This software is released under a three-clause BSD license:
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above copyright
11 // notice, this list of conditions and the following disclaimer in the
12 // documentation and/or other materials provided with the distribution.
13 // * Neither the name of any author or any participating institution
14 // may be used to endorse or promote products derived from this software
15 // without specific prior written permission.
16 // For a full list of authors, refer to the file AUTHORS.
17 // --------------------------------------------------------------------------
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 // ARE DISCLAIMED. IN NO EVENT SHALL ANY OF THE AUTHORS OR THE CONTRIBUTING
22 // INSTITUTIONS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
25 // OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26 // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
27 // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
28 // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 //
30 // --------------------------------------------------------------------------
31 // $Maintainer: Johannes Veit $
32 // $Authors: Johannes Junker $
33 // --------------------------------------------------------------------------
34 
35 #pragma once
36 
38 
42 
43 #include <QtGui/QColor>
44 
45 namespace OpenMS
46 {
47 
51  template <class DataPoint> // e.g. Peak1D
53  public Annotation1DItem
54  {
55 public:
57  Annotation1DPeakItem(const DataPoint& peak_position, const QString& text, const QColor& color) :
58  Annotation1DItem(text), peak_position_(peak_position), position_(peak_position), color_(color)
59  {
60  }
61 
63  Annotation1DPeakItem(const Annotation1DPeakItem& rhs) = default;
64 
66  ~Annotation1DPeakItem() override = default;
67 
68  // Docu in base class
69  void draw(Plot1DCanvas* const canvas, QPainter& painter, bool flipped = false) override
70  {
71  painter.save();
72 
73  painter.setPen(color_);
74 
75  QPoint position_widget, peak_position_widget;
76 
77  // translate units to pixel coordinates
78  canvas->dataToWidget(canvas->getMapper().map(position_), position_widget, flipped);
79  canvas->dataToWidget(canvas->getMapper().map(peak_position_), peak_position_widget, flipped);
80 
81  // compute bounding box of text_item on the specified painter
82  bounding_box_ = QApplication::fontMetrics().boundingRect(position_widget.x(), position_widget.y(), 0, 0, Qt::AlignCenter, getText());
83 
84  // draw connection line between anchor point and current position if pixel coordinates differ significantly
85  if ((position_widget - peak_position_widget).manhattanLength() > 2)
86  {
87  QPointF border_point = GUIHelpers::intersectionPoint(bounding_box_, peak_position_widget);
88  if (bounding_box_.center() != border_point)
89  {
90  painter.save();
91  painter.setPen(Qt::DashLine);
92  painter.drawLine(peak_position_widget, border_point);
93  painter.restore();
94  }
95  }
96 
97  // some pretty printing
98  QString text = text_;
99  if (!text.contains(R"(<\)")) // don't process HTML strings again
100  {
101  // extract ion index
102  {
103  QRegExp reg_exp(R"([abcdwxyz](\d+))");
104  int match_pos = reg_exp.indexIn(text);
105 
106  if (match_pos == 0)
107  {
108  QString index_str = reg_exp.cap(1);
109 
110  // put sub html tag around number
111  text = text[match_pos] + QString("<sub>") + index_str + QString("</sub>") + text.right(text.size() - match_pos - index_str.size() - 1);
112  }
113  else // protein-protein XL specific ion names
114  {
115  QRegExp reg_exp_xlms(R"((ci|xi)[$][abcxyz](\d+))");
116  match_pos = reg_exp_xlms.indexIn(text);
117  if ((match_pos == 6) || (match_pos == 7))
118  {
119  // set the match_pos to the position of the ion index
120  match_pos += 3;
121  QString index_str = reg_exp.cap(1);
122 
123  // put sub html tag around number
124  text = text.left(match_pos) + text[match_pos] + QString("<sub>") + index_str + QString("</sub>") + text.right(text.size() - match_pos - index_str.size() - 1);
125  }
126  }
127  }
128 
129  // common losses
130  text.replace("H2O1", "H<sub>2</sub>O"); // mind the order with H2O substitution
131  text.replace("H2O", "H<sub>2</sub>O");
132  text.replace("NH3", "NH<sub>3</sub>");
133  text.replace("H3N1", "NH<sub>3</sub>");
134  text.replace("C1H4O1S1", "H<sub>4</sub>COS"); // methionine sulfoxide loss
135 
136  // nucleotide XL related losses
137  text.replace("H3PO4", "H<sub>3</sub>PO<sub>4</sub>");
138  text.replace("HPO3", "HPO<sub>3</sub>");
139  text.replace("C3O", "C<sub>3</sub>O");
140 
141  // charge format: +z
142  QRegExp charge_rx(R"([\+|\-](\d+)$)");
143  int match_pos = charge_rx.indexIn(text);
144  if (match_pos > 0)
145  {
146  text = text.left(match_pos) + QString("<sup>") + text[match_pos] // + or -
147  + charge_rx.cap(1) + QString("</sup>"); // charge
148  }
149 
150  // charge format: z+
151  charge_rx = QRegExp(R"((\d+)[\+|\-]$)");
152  match_pos = charge_rx.indexIn(text);
153  if (match_pos > 0)
154  {
155  text = text.left(match_pos) + QString("<sup>") + charge_rx.cap(1) // charge
156  + text[match_pos + charge_rx.cap(1).size()] + QString("</sup>"); // + or -
157  }
158 
159  text.replace(QRegExp(R"(\+\+$)"), "<sup>2+</sup>");
160  text.replace(QRegExp(R"(\+$)"), "");
161  text.replace(QRegExp(R"(\-\-$)"), "<sup>2-</sup>");
162  text.replace(QRegExp(R"(\-$)"), "");
163  }
164 
165  text = "<font color=\"" + color_.name() + "\">" + text + "</font>";
166  QTextDocument td;
167  td.setHtml(text);
168 
169  // draw html text
170  painter.save();
171  double w = td.size().width();
172  double h = td.size().height();
173  painter.translate(position_widget.x() - w / 2, position_widget.y() - h / 2);
174  td.drawContents(&painter);
175  painter.restore();
176 
177  if (selected_)
178  {
179  drawBoundingBox_(painter);
180  }
181 
182  painter.restore();
183  }
184 
185  // Docu in base class
186  void move(const PointXYType delta, const Gravitator& /*gr*/, const DimMapper<2>& dim_mapper) override
187  {
188  auto pos_xy = dim_mapper.map(position_);
189  pos_xy += delta;
190  dim_mapper.fromXY(pos_xy, position_);
191  }
192 
194  void setPosition(const DataPoint& position)
195  {
196  position_ = position;
197  }
198 
200  const DataPoint& getPosition() const
201  {
202  return position_;
203  }
204 
206  const DataPoint& getPeakPosition() const
207  {
208  return peak_position_;
209  }
210 
211  // Docu in base class
212  void ensureWithinDataRange(Plot1DCanvas* const canvas, const int layer_index) override
213  {
214  canvas->pushIntoDataRange(position_, layer_index);
215  }
216 
218  void setColor(const QColor& color)
219  {
220  color_ = color;
221  }
222 
224  const QColor& getColor() const
225  {
226  return color_;
227  }
228 
231  {
232  // add new fragment annotation
233  QString peak_anno = this->getText().trimmed();
234 
235  // regular expression for a charge at the end of the annotation
236  QRegExp reg_exp(R"(([\+|\-]\d+)$)");
237 
238  // check for newlines in the label and only continue with the first line for charge determination
239  QStringList lines = peak_anno.split(QRegExp("[\r\n]"), QString::SkipEmptyParts);
240  if (lines.size() > 1)
241  {
242  peak_anno = lines[0];
243  }
244 
245  // read charge and text from annotation item string
246  // we support two notations for the charge suffix: '+2' or '++'
247  // cut and convert the trailing + or - to a proper charge
248  int match_pos = reg_exp.indexIn(peak_anno);
249  int tmp_charge(0);
250  if (match_pos >= 0)
251  {
252  tmp_charge = reg_exp.cap(1).toInt();
253  peak_anno = peak_anno.left(match_pos);
254  }
255  else
256  {
257  // count number of + and - in suffix (e.g., to support "++" as charge 2 annotation)
258  int plus(0), minus(0);
259 
260  for (int p = (int)peak_anno.size() - 1; p >= 0; --p)
261  {
262  if (peak_anno[p] == '+')
263  {
264  ++plus;
265  continue;
266  }
267  else if (peak_anno[p] == '-')
268  {
269  ++minus;
270  continue;
271  }
272  else // not '+' or '-'?
273  {
274  if (plus > 0 && minus == 0) // found pluses?
275  {
276  tmp_charge = plus;
277  peak_anno = peak_anno.left(peak_anno.size() - plus);
278  break;
279  }
280  else if (minus > 0 && plus == 0) // found minuses?
281  {
282  tmp_charge = -minus;
283  peak_anno = peak_anno.left(peak_anno.size() - minus);
284  break;
285  }
286  break;
287  }
288  }
289  }
290 
292  fa.charge = tmp_charge;
293  fa.mz = this->getPeakPosition().getMZ();
294  fa.intensity = this->getPeakPosition().getIntensity();
295  if (lines.size() > 1)
296  {
297  peak_anno.append("\n").append(lines[1]);
298  }
299  fa.annotation = peak_anno;
300 
301  return fa;
302  }
303 
304  // Docu in base class
305  Annotation1DItem* clone() const override
306  {
307  return new Annotation1DPeakItem(*this);
308  }
309 
310  protected:
312  DataPoint peak_position_;
313 
315  DataPoint position_;
316 
318  QColor color_;
319  };
320 } // namespace OpenMS
An abstract class acting as an interface for the different 1D annotation items.
Definition: Annotation1DItem.h:61
const DataPoint & getPeakPosition() const
Returns the position of the annotated peak.
Definition: Annotation1DPeakItem.h:206
bool selected_
Determines whether this item is currently selected on the canvas.
Definition: Annotation1DItem.h:112
~Annotation1DPeakItem() override=default
Destructor.
void draw(Plot1DCanvas *const canvas, QPainter &painter, bool flipped=false) override
Draws the item on painter.
Definition: Annotation1DPeakItem.h:69
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...
void drawBoundingBox_(QPainter &painter)
Draws the bounding_box_.
Canvas for visualization of one or several spectra.
Definition: Plot1DCanvas.h:319
const QString & getText() const
Returns the text of the item.
Annotation1DItem * clone() const override
Creates a copy of the item on the heap and returns a pointer.
Definition: Annotation1DPeakItem.h:305
void setPosition(const DataPoint &position)
Sets the position of the label.
Definition: Annotation1DPeakItem.h:194
double mz
Definition: PeptideHit.h:87
Main OpenMS namespace.
Definition: FeatureDeconvolution.h:47
QColor color_
The color of the label.
Definition: Annotation1DPeakItem.h:318
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:186
String annotation
Definition: PeptideHit.h:85
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:452
Point map(const T &data) const
convert an OpenMS datatype (such as Feature) to an N_DIM-dimensional point
Definition: DimMapper.h:680
void setColor(const QColor &color)
Set the color of the label.
Definition: Annotation1DPeakItem.h:218
DataPoint peak_position_
The position of the anchor (e.g. the Peak1D)
Definition: Annotation1DPeakItem.h:312
QRectF bounding_box_
The current bounding box of this item on the canvas where it has last been drawn. ...
Definition: Annotation1DItem.h:109
const double h
Definition: Constants.h:162
const DimMapper< 2 > & getMapper() const
Get Mapper to translate between values for axis (X/Y) and units (m/z, RT, intensity, ...)
PeptideHit::PeakAnnotation toPeakAnnotation() const
Convert the &#39;text()&#39; to a Peptide::PeakAnnotation.
Definition: Annotation1DPeakItem.h:230
double intensity
Definition: PeptideHit.h:88
const QColor & getColor() const
Returns the color of the label.
Definition: Annotation1DPeakItem.h:224
DataPoint position_
The position of the label (e.g. the Peak1D)
Definition: Annotation1DPeakItem.h:315
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:212
int charge
Definition: PeptideHit.h:86
void fromXY(const DRange< N_DIM > &in, RangeManager< Ranges... > &output) const
Definition: DimMapper.h:716
Contains annotations of a peak.
Definition: PeptideHit.h:83
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:67
Annotation1DPeakItem(const DataPoint &peak_position, const QString &text, const QColor &color)
Constructor.
Definition: Annotation1DPeakItem.h:57
A peak annotation item.
Definition: Annotation1DPeakItem.h:52
void dataToWidget(const DPosition< 2 > &peak, QPoint &point, bool flipped=false)
For convenience - calls dataToWidget.
QString text_
The displayed text.
Definition: Annotation1DItem.h:115
const DataPoint & getPosition() const
Returns the position of the label (peak)
Definition: Annotation1DPeakItem.h:200