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