1 /*
2  *    Copyright 2012, 2013 Thomas Schöps
3  *    Copyright 2012-2020 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_TEMPLATE_IMAGE_H
23 #define OPENORIENTEERING_TEMPLATE_IMAGE_H
24 
25 #include <memory>
26 #include <vector>
27 
28 #include <QtGlobal>
29 #include <QByteArray>
30 #include <QColor>
31 #include <QImage>
32 #include <QObject>
33 #include <QPointF>
34 #include <QRectF>
35 #include <QRgb>
36 #include <QString>
37 #include <QTransform>
38 
39 #include "templates/template.h"
40 
41 class QPainter;
42 class QPointF;
43 class QRectF;
44 class QWidget;
45 class QXmlStreamReader;
46 class QXmlStreamWriter;
47 
48 namespace OpenOrienteering {
49 
50 class Georeferencing;
51 class Map;
52 class MapCoordF;
53 
54 
55 /**
56  * Template showing a raster image.
57  * Can be georeferenced or non-georeferenced.
58  */
59 class TemplateImage : public Template
60 {
61 Q_OBJECT
62 public:
63 	/**
64 	 * Information fragment about template image georeferencing.
65 	 *
66 	 * A GeoreferencingOption may carry information about the CRS and/or the
67 	 * pixel-to-world transform for a template image. The crs_spec is empty
68 	 * if the CRS is unknown. The transform's source is empty if the
69 	 * pixel-to-world transformation is unknown.
70 	 */
71 	struct GeoreferencingOption
72 	{
73 		struct Transform
74 		{
75 			QTransform pixel_to_world = {};     ///< The transformation from pixel space to CRS space.
76 			QByteArray source = {};             ///< The source of the pixel-to-world transform.
77 		};
78 
79 		QString crs_spec = {};              ///< The specification of the CRS uses for georeferencing.
80 		Transform transform = {};           ///< The transformation from pixel space to CRS space.
81 	};
82 
83 	/**
84 	 * A collection of alternative and/or complementary georeferencing information fragments.
85 	 */
86 	struct GeoreferencingOptions
87 	{
88 		GeoreferencingOption effective;     ///< The effective option, if CRS and transform are defined.
89 		GeoreferencingOption world_file;    ///< A pixel-to-world transform from a world file.
90 		GeoreferencingOption template_file; ///< CRS and/or pixel-to-world transform from the template file.
91 	};
92 
93 	/**
94 	 * Returns the filename extensions supported by this template class.
95 	 */
96 	static const std::vector<QByteArray>& supportedExtensions();
97 
98 	TemplateImage(const QString& path, Map* map);
99 protected:
100 	TemplateImage(const TemplateImage& proto);
101 public:
102     ~TemplateImage() override;
103 
104 	TemplateImage* duplicate() const override;
105 
getTemplateType()106 	const char* getTemplateType() const override {return "TemplateImage";}
isRasterGraphics()107 	bool isRasterGraphics() const override {return true;}
108 
109 	bool saveTemplateFile() const override;
110 	void saveTypeSpecificTemplateConfiguration(QXmlStreamWriter& xml) const override;
111 	bool loadTypeSpecificTemplateConfiguration(QXmlStreamReader& xml) override;
112 
113 	bool loadTemplateFileImpl(bool configuring) override;
114 	bool postLoadConfiguration(QWidget* dialog_parent, bool& out_center_in_view) override;
115 	void unloadTemplateFileImpl() override;
116 
117     void drawTemplate(QPainter* painter, const QRectF& clip_rect, double scale, bool on_screen, qreal opacity) const override;
118 	QRectF getTemplateExtent() const override;
canBeDrawnOnto()119 	bool canBeDrawnOnto() const override { return drawable; }
120 
121 	/**
122 	 * Calculates the image's center of gravity in template coordinates by
123 	 * iterating over all pixels, leaving out the pixels with background_color.
124 	 */
125 	QPointF calcCenterOfGravity(QRgb background_color);
126 
127 	/** Returns the internal QImage. */
getImage()128 	inline const QImage& getImage() const {return image;}
129 
130 	/**
131 	 * Returns which georeferencing methods are known to be available.
132 	 *
133 	 * (This does not imply that the image is in georeferenced mode.)
134 	 *
135 	 * Invariant: The list is never empty. It always contains at least an entry
136 	 * of type Georeferencing_None. This entry is always the last one.
137 	 */
availableGeoreferencing()138 	const GeoreferencingOptions& availableGeoreferencing() const { return available_georef; }
139 
140 	bool canChangeTemplateGeoreferenced() override;
141 	bool trySetTemplateGeoreferenced(bool value, QWidget* dialog_parent) override;
142 
143 
144 public slots:
145 	void updateGeoreferencing();
146 
147 protected:
148 	/**
149 	 * Collects available georeferencing information.
150 	 *
151 	 * This function is meant to be used from loadTemplateFileImpl() to set the
152 	 * available_georef member. The parameter template_file_option is used to
153 	 * efficiently provide georeferencing information from the template file
154 	 * itself.
155 	 */
156 	GeoreferencingOptions findAvailableGeoreferencing(GeoreferencingOption template_file_option) const;
157 
158 	/**
159 	 * Tests if the available georeferencing options can be used with the current map.
160 	 *
161 	 * To be usable, the effective option must provide a transformation, and
162 	 * if the map's CRS spec is not empty (local georeferencing), the effective
163 	 * option's CRS spec must not be empty.
164 	 *
165 	 * Note that changing the state to georeferenced may still fail for invalid
166 	 * CRS specs.
167 	 */
168 	bool isGeoreferencingUsable() const;
169 
170 	/** Information about an undo step for the paint-on-template functionality. */
171 	struct DrawOnImageUndoStep
172 	{
173 		/** Copy of previous image part */
174 		QImage image;
175 
176 		/** X position of image part origin */
177 		int x;
178 
179 		/** Y position of image part origin */
180 		int y;
181 	};
182 
183 	void drawOntoTemplateImpl(MapCoordF* coords, int num_coords, const QColor& color, qreal width) override;
184 	void drawOntoTemplateUndo(bool redo) override;
185 	void addUndoStep(const DrawOnImageUndoStep& new_step);
186 	void calculateGeoreferencing();
187 	void updatePosFromGeoreferencing();
188 
189 	QImage image;
190 
191 	std::vector< DrawOnImageUndoStep > undo_steps;
192 	/// Current index in undo_steps, where 0 means before the first item.
193 	int undo_index = 0;
194 	/// A flag indicating that this template can be drawn onto.
195 	bool drawable = false;
196 
197 	GeoreferencingOptions available_georef;
198 	std::unique_ptr<Georeferencing> georef;
199 };
200 
201 
202 }  // namespace OpenOrienteering
203 
204 #endif
205