1 /*
2 * Copyright 2012, 2013 Thomas Schöps
3 * Copyright 2014-2019 Kai Pastor
4 *
5 * This file is part of OpenOrienteering.
6 *
7 * OpenOrienteering is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * OpenOrienteering is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with OpenOrienteering. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21
22 #ifndef OPENORIENTEERING_MAP_VIEW_H
23 #define OPENORIENTEERING_MAP_VIEW_H
24
25 #include <vector>
26
27 #include <QtGlobal>
28 #include <QFlags>
29 #include <QObject>
30 #include <QPoint>
31 #include <QPointF>
32 #include <QRectF>
33 #include <QTransform>
34
35 #include "map_coord.h"
36
37 class QLatin1String;
38 class QRectF;
39 class QXmlStreamReader;
40 class QXmlStreamWriter;
41
42 namespace OpenOrienteering {
43
44 class Map;
45 class Template;
46
47
48 /**
49 * Contains the visibility information for a template (or the map).
50 */
51 class TemplateVisibility
52 {
53 public:
54 /** Opacity from 0.0 (invisible) to 1.0 (opaque) */
55 qreal opacity;
56
57 /** Visibility flag */
58 bool visible;
59
60 /** Returns true when the template is visible but not opaque. */
61 bool hasAlpha() const;
62 };
63
64 bool operator==(TemplateVisibility lhs, TemplateVisibility rhs);
65
66 bool operator!=(TemplateVisibility lhs, TemplateVisibility rhs);
67
68
69 /**
70 * Stores view position, zoom, rotation and grid / template visibilities
71 * to define a view onto a map.
72 *
73 * These parameters define the view coordinates with origin at the view's center,
74 * measured in pixels. The class provides methods to convert between view
75 * coordinates and map coordinates (defined as millimeter on map paper).
76 */
77 class MapView : public QObject
78 {
79 Q_OBJECT
80 Q_FLAGS(ChangeFlags VisibilityFeature)
81
82 public:
83 enum ChangeFlag
84 {
85 NoChange = 0,
86 CenterChange = 1,
87 ZoomChange = 2,
88 RotationChange = 4,
89 };
90
91 Q_DECLARE_FLAGS(ChangeFlags, ChangeFlag)
92
93 enum VisibilityFeature
94 {
95 MultipleFeatures = 0,
96 GridVisible = 1,
97 OverprintingEnabled = 2,
98 AllTemplatesHidden = 4,
99 TemplateVisible = 8,
100 MapVisible = 16,
101 };
102
103
104 /**
105 * Creates a default view looking at the origin.
106 *
107 * The parameter map must not be null.
108 */
109 MapView(QObject* parent, Map* map);
110
111 /** Creates a default view looking at the origin.
112 *
113 * The map takes ownership of the map view. It must not be null.
114 */
115 MapView(Map* map);
116
117 /** Destroys the map view. */
118 ~MapView() override;
119
120
121 /**
122 * Saves the map view state to an XML stream.
123 * @param xml The XML output stream.
124 * @param element_name The name of the element which will be written.
125 * @param template_details Save template visibilities (default: true)
126 */
127 void save(QXmlStreamWriter& xml, const QLatin1String& element_name, bool template_details = true) const;
128
129 /** Loads the map view state from the current element of an xml stream. */
130 void load(QXmlStreamReader& xml);
131
132 /**
133 * Redraws all map widgets completely.
134 *
135 * Note that this calls QWidget::update() which does not cause an immediate
136 * repaint; instead it schedules a paint event.
137 *
138 * Completely repainting widgets can be slow.
139 * Try to do partial updates instead, if possible.
140 */
141 void updateAllMapWidgets();
142
143
144 /** Converts the point (with origin at the center of the view) to map coordinates */
145 MapCoord viewToMap(const QPointF& point) const;
146
147 /** Converts the point (with origin at the center of the view) to map coordinates */
148 MapCoordF viewToMapF(const QPointF& point) const;
149
150
151 /// Converts map coordinates to view coordinates (with origin at the center of the view)
152 QPointF mapToView(const MapCoord& coords) const;
153
154 /// Converts map coordinates to view coordinates (with origin at the center of the view)
155 QPointF mapToView(const QPointF& coords) const;
156
157
158 /**
159 * Converts a length from native map coordinates to the current length in view pixels.
160 */
161 qreal lengthToPixel(qreal length) const;
162
163 /**
164 * Converts a length from current view pixels to native map coordinates.
165 */
166 qreal pixelToLength(qreal pixel) const;
167
168
169 /**
170 * Calculates the bounding box of the map coordinates which can be viewed
171 * using the given view coordinates rect
172 */
173 QRectF calculateViewedRect(QRectF view_rect) const;
174
175 /**
176 * Calculates the bounding box in view coordinates
177 * of the given map coordinates rect
178 */
179 QRectF calculateViewBoundingBox(QRectF map_rect) const;
180
181 /**
182 * Returns a QTransform suitable for QPainter, so objects defined in
183 * map coordinates will be drawn at their view coordinates. Append a
184 * viewport transformation to this to get a complete map-to-viewport transformation
185 * which makes the view center appear at the viewport center.
186 *
187 * Note: The transform is to be combined with the painter's existing transform.
188 */
189 const QTransform& worldTransform() const;
190
191
192 // Panning
193
194 /** Returns the current pan offset (when dragging the map). */
195 QPoint panOffset() const;
196
197 /** Sets the current pan offset while the map is being dragged. */
198 void setPanOffset(const QPoint& offset);
199
200 /**
201 * Finishes panning the map.
202 *
203 * @param offset The final offset, relative to the start of the operation.
204 */
205 void finishPanning(const QPoint& offset);
206
207
208 /** Returns the map this view is defined on. */
209 const Map* getMap() const;
210
211 /** Returns the map this view is defined on. */
212 Map* getMap();
213
214
215 /**
216 * Zooms the maps (in steps), preserving the given cursor position.
217 *
218 * @param num_steps Number of zoom steps to zoom in. Negative numbers zoom out.
219 * @param cursor_pos_view The cursor position in view coordinates, must be
220 * set if preserve_cursor_pos is used.
221 */
222 void zoomSteps(double num_steps, const QPointF& cursor_pos_view);
223
224 /**
225 * Zooms the maps (in steps), preserving the center of the view.
226 *
227 * @param num_steps Number of zoom steps to zoom in. Negative numbers zoom out.
228 */
229 void zoomSteps(double num_steps);
230
231 /**
232 * Returns the final zoom factor for use in transformations.
233 * Depends on the pixel per millimeter of the display.
234 */
235 double calculateFinalZoomFactor() const;
236
237 /** Returns the raw zoom factor, see also calculateFinalZoomFactor(). */
238 double getZoom() const;
239
240 /** Sets the zoom factor relative to the given point.*/
241 void setZoom(double value, const QPointF& center);
242
243 /** Sets the zoom factor. */
244 void setZoom(double value);
245
246
247 /** Returns the view rotation (in radians). */
248 double getRotation() const;
249
250 /** Sets the view rotation (in radians). */
251 void setRotation(double value);
252
253
254 /** Returns the position of the view center. */
255 MapCoord center() const;
256
257 /** Sets the position of the view center. */
258 void setCenter(const MapCoord& pos);
259
260
261 // Map and template visibilities
262
263 /** Returns the effectiv visibility settings of the map drawing.
264 *
265 * Other than getMapVisibility, this will always return an (100 %) opaque,
266 * visible configuration when areAllTemplatesHidden() is true,
267 * and it return an invisible configuration when the map's opacity is
268 * below 0.005.
269 */
270 TemplateVisibility effectiveMapVisibility() const;
271
272 /** Returns the visibility settings of the map drawing. */
273 TemplateVisibility getMapVisibility() const;
274
275 void setMapVisibility(TemplateVisibility vis);
276
277 /**
278 * Checks if the template is visible without creating
279 * a template visibility object if none exists
280 */
281 bool isTemplateVisible(const Template* temp) const;
282
283 /**
284 * Returns the template visibility.
285 *
286 * If the template is unknown, returns default settings.
287 */
288 TemplateVisibility getTemplateVisibility(const Template* temp) const;
289
290 /**
291 * Sets the template visibility, and emits a change signal.
292 */
293 void setTemplateVisibility(Template* temp, TemplateVisibility vis);
294
295
296 /** Enables or disables hiding all templates in this view */
297 void setAllTemplatesHidden(bool value);
298
299 /**
300 * Returns if the "hide all templates" toggle is active.
301 * See also setHideAllTemplates().
302 */
303 bool areAllTemplatesHidden() const;
304
305
306 /** Returns if the map grid is visible. */
307 bool isGridVisible() const;
308
309 /** Sets the map grid visibility. */
310 void setGridVisible(bool visible);
311
312
313 /** Returns if overprinting simulation is enabled. */
314 bool isOverprintingSimulationEnabled() const;
315
316 /** Enables or disables overprinting simulation. */
317 void setOverprintingSimulationEnabled(bool enabled);
318
319
320 /**
321 * Returns true if any of the visible elements is not opaque.
322 */
323 bool hasAlpha() const;
324
325
326 /** Temporarily blocks automatic template loading on visibility changes. */
327 void setTemplateLoadingBlocked(bool blocked);
328
329 /** Returns true when template loading on visibility changes is disabled. */
templateLoadingBlocked()330 bool templateLoadingBlocked() const { return template_loading_blocked; }
331
332
333 signals:
334 /**
335 * Indicates a change of the viewed area of the map.
336 *
337 * @param change The aspects that have changed.
338 */
339 void viewChanged(OpenOrienteering::MapView::ChangeFlags change);
340
341 /**
342 * Indicates a change of the pan offset.
343 */
344 void panOffsetChanged(const QPoint& offset);
345
346 /**
347 * Indicates a particular change of visibility.
348 *
349 * @param feature The map view feature that has changed.
350 * @param active The features current state of activation.
351 * @param temp If a the feature is a template, a pointer to this template.
352 */
353 void visibilityChanged(OpenOrienteering::MapView::VisibilityFeature feature, bool active, const OpenOrienteering::Template* temp = nullptr);
354
355
356 public:
357 // Static
358
359 /** The global zoom in limit for the zoom factor. */
360 static const double zoom_in_limit;
361 /** The global zoom out limit for the zoom factor. */
362 static const double zoom_out_limit;
363
364 protected:
365 /**
366 * Sets the template visibility without emitting signals.
367 */
368 bool setTemplateVisibilityHelper(const Template *temp, TemplateVisibility vis);
369
370 /**
371 * Creates the visibility data when a template is added to the map.
372 */
373 void onTemplateAdded(int pos, Template* temp);
374
375 /**
376 * Removes the visibility data when a template is deleted.
377 */
378 void onTemplateDeleted(int pos, const Template* temp);
379
380 private:
381 Q_DISABLE_COPY(MapView)
382
383 struct TemplateVisibilityEntry : public TemplateVisibility
384 {
385 const Template* temp;
386 TemplateVisibilityEntry() = default;
387 TemplateVisibilityEntry(const TemplateVisibilityEntry&) = default;
388 TemplateVisibilityEntry(TemplateVisibilityEntry&&) = default;
389 TemplateVisibilityEntry& operator=(const TemplateVisibilityEntry&) = default;
390 TemplateVisibilityEntry& operator=(TemplateVisibilityEntry&&) = default;
TemplateVisibilityEntryTemplateVisibilityEntry391 TemplateVisibilityEntry(const Template* temp, TemplateVisibility vis)
392 : TemplateVisibility(vis)
393 , temp(temp)
394 {}
395 };
396 typedef std::vector<TemplateVisibilityEntry> TemplateVisibilityVector;
397
398
399 void updateTransform(); // recalculates the x_to_y matrices
400
401 TemplateVisibilityVector::const_iterator findVisibility(const Template* temp) const;
402
403 TemplateVisibilityVector::iterator findVisibility(const Template* temp);
404
405
406 Map* map;
407
408 double zoom; // factor
409 double rotation; // counterclockwise 0 to 2*PI. This is the viewer rotation, so the map is rotated clockwise
410 MapCoord center_pos;// position of the viewer, positive values move the map to the left; the position is in 1/1000 mm
411 QPoint pan_offset; // the distance the content of the view was dragged with the mouse, in pixels
412
413 QTransform view_to_map;
414 QTransform map_to_view;
415
416 TemplateVisibility map_visibility;
417 TemplateVisibilityVector template_visibilities;
418
419 bool all_templates_hidden;
420 bool grid_visible;
421 bool overprinting_simulation_enabled;
422
423 bool template_loading_blocked;
424 };
425
426
427
428 // ### TemplateVisibility inline code ###
429
430 inline
431 bool operator==(TemplateVisibility lhs, TemplateVisibility rhs)
432 {
433 return lhs.visible == rhs.visible
434 && qFuzzyCompare(1.0+rhs.opacity, 1.0+lhs.opacity);
435 }
436
437 inline
438 bool operator!=(TemplateVisibility lhs, TemplateVisibility rhs)
439 {
440 return !(lhs == rhs);
441 }
442
443
444
445 // ### MapView inline code ###
446
447 inline
viewToMap(const QPointF & point)448 MapCoord MapView::viewToMap(const QPointF& point) const
449 {
450 return MapCoord(view_to_map.map(point));
451 }
452
453 inline
viewToMapF(const QPointF & point)454 MapCoordF MapView::viewToMapF(const QPointF& point) const
455 {
456 return MapCoordF(view_to_map.map(point));
457 }
458
459 inline
worldTransform()460 const QTransform& MapView::worldTransform() const
461 {
462 return map_to_view;
463 }
464
465 inline
getMap()466 Map* MapView::getMap()
467 {
468 return map;
469 }
470
471 inline
getMap()472 const Map* MapView::getMap() const
473 {
474 return map;
475 }
476
477 inline
calculateFinalZoomFactor()478 double MapView::calculateFinalZoomFactor() const
479 {
480 return lengthToPixel(1000.0);
481 }
482
483 inline
getZoom()484 double MapView::getZoom() const
485 {
486 return zoom;
487 }
488
489 inline
getRotation()490 double MapView::getRotation() const
491 {
492 return rotation;
493 }
494
495 inline
center()496 MapCoord MapView::center() const
497 {
498 return center_pos;
499 }
500
501 inline
panOffset()502 QPoint MapView::panOffset() const
503 {
504 return pan_offset;
505 }
506
507 inline
getMapVisibility()508 TemplateVisibility MapView::getMapVisibility() const
509 {
510 return map_visibility;
511 }
512
513 inline
areAllTemplatesHidden()514 bool MapView::areAllTemplatesHidden() const
515 {
516 return all_templates_hidden;
517 }
518
519 inline
isGridVisible()520 bool MapView::isGridVisible() const
521 {
522 return grid_visible;
523 }
524
525 inline
isOverprintingSimulationEnabled()526 bool MapView::isOverprintingSimulationEnabled() const
527 {
528 return overprinting_simulation_enabled;
529 }
530
531
532 } // namespace OpenOrienteering
533
534
535 Q_DECLARE_OPERATORS_FOR_FLAGS(OpenOrienteering::MapView::ChangeFlags)
536
537
538 #endif
539