1 /*
2  *    Copyright 2016-2019 Kai Pastor
3  *
4  *    This file is part of OpenOrienteering.
5  *
6  *    OpenOrienteering is free software: you can redistribute it and/or modify
7  *    it under the terms of the GNU General Public License as published by
8  *    the Free Software Foundation, either version 3 of the License, or
9  *    (at your option) any later version.
10  *
11  *    OpenOrienteering is distributed in the hope that it will be useful,
12  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *    GNU General Public License for more details.
15  *
16  *    You should have received a copy of the GNU General Public License
17  *    along with OpenOrienteering.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #ifndef OPENORIENTEERING_OGR_FILE_FORMAT_P_H
21 #define OPENORIENTEERING_OGR_FILE_FORMAT_P_H
22 
23 #include <functional>
24 #include <memory>
25 #include <type_traits>
26 #include <vector>
27 
28 #include <QByteArray>
29 #include <QCoreApplication>
30 #include <QFlags>
31 #include <QHash>
32 #include <QString>
33 #include <QtGlobal>
34 
35 // The GDAL/OGR C API is more stable than the C++ API.
36 #include <gdal.h>
37 #include <ogr_api.h>
38 #include <ogr_srs_api.h>
39 // IWYU pragma: no_include <ogr_core.h>
40 
41 #include "core/map_coord.h"
42 #include "core/symbols/symbol.h"
43 #include "fileformats/file_import_export.h"
44 
45 class QPointF;
46 
47 namespace OpenOrienteering {
48 
49 class AreaSymbol;
50 class Georeferencing;
51 class LatLon;
52 class LineSymbol;
53 class Map;
54 class MapView;
55 class MapColor;
56 class MapPart;
57 class Object;
58 class PathObject;
59 class PointSymbol;
60 class TextSymbol;
61 
62 
63 namespace ogr
64 {
65 
66 	class OGRCoordinateTransformationHDeleter
67 	{
68 	public:
operator()69 		void operator()(OGRCoordinateTransformationH ct) const
70 		{
71 			OCTDestroyCoordinateTransformation(ct);
72 		}
73 	};
74 
75 	/**
76 	 * A convenience class for OGR C API coordinate transformation handles,
77 	 * similar to std::unique_ptr.
78 	 */
79 	using unique_transformation = std::unique_ptr<typename std::remove_pointer<OGRCoordinateTransformationH>::type, OGRCoordinateTransformationHDeleter>;
80 
81 
82 	class OGRDataSourceHDeleter
83 	{
84 	public:
operator()85 		void operator()(OGRDataSourceH data_source) const
86 		{
87 			OGRReleaseDataSource(data_source);
88 		}
89 	};
90 
91 	/** A convenience class for OGR C API datasource handles, similar to std::unique_ptr. */
92 	using unique_datasource = std::unique_ptr<typename std::remove_pointer<OGRDataSourceH>::type, OGRDataSourceHDeleter>;
93 
94 
95 	class OGRFeatureHDeleter
96 	{
97 	public:
operator()98 		void operator()(OGRFeatureH feature) const
99 		{
100 			OGR_F_Destroy(feature);
101 		}
102 	};
103 
104 	/** A convenience class for OGR C API feature handles, similar to std::unique_ptr. */
105 	using unique_feature = std::unique_ptr<typename std::remove_pointer<OGRFeatureH>::type, OGRFeatureHDeleter>;
106 
107 
108 	class OGRFieldDefnHDeleter
109 	{
110 	public:
operator()111 		void operator()(OGRFieldDefnH field_defn) const
112 		{
113 			OGR_Fld_Destroy(field_defn);
114 		}
115 	};
116 
117 	/** A convenience class for OGR C API field definition handles, similar to std::unique_ptr. */
118 	using unique_fielddefn = std::unique_ptr<typename std::remove_pointer<OGRFieldDefnH>::type, OGRFieldDefnHDeleter>;
119 
120 
121 	class OGRGeometryHDeleter
122 	{
123 	public:
operator()124 		void operator()(OGRGeometryH geometry) const
125 		{
126 			OGR_G_DestroyGeometry(geometry);
127 		}
128 	};
129 
130 	/** A convenience class for OGR C API geometry handles, similar to std::unique_ptr. */
131 	using unique_geometry = std::unique_ptr<typename std::remove_pointer<OGRGeometryH>::type, OGRGeometryHDeleter>;
132 
133 
134 	class OGRSpatialReferenceHDeleter
135 	{
136 	public:
operator()137 		void operator()(OGRSpatialReferenceH srs) const
138 		{
139 			OSRDestroySpatialReference(srs);
140 		}
141 	};
142 
143 	/**
144 	 * A convenience class for OGR C API SRS handles, similar to std::unique_ptr.
145 	 */
146 	using unique_srs = std::unique_ptr<typename std::remove_pointer<OGRSpatialReferenceH>::type, OGRSpatialReferenceHDeleter>;
147 
148 
149 	class OGRStyleMgrHDeleter
150 	{
151 	public:
operator()152 		void operator()(OGRStyleMgrH manager) const
153 		{
154 			OGR_SM_Destroy(manager);
155 		}
156 	};
157 
158 	/** A convenience class for OGR C API style manager handles, similar to std::unique_ptr. */
159 	using unique_stylemanager = std::unique_ptr<typename std::remove_pointer<OGRStyleMgrH>::type, OGRStyleMgrHDeleter>;
160 
161 
162 	class OGRStyleTableHDeleter
163 	{
164 	public:
operator()165 		void operator()(OGRStyleTableH table) const
166 		{
167 			OGR_STBL_Destroy(table);
168 		}
169 	};
170 
171 	/** A convenience class for OGR C API style manager handles, similar to std::unique_ptr. */
172 	using unique_styletable = std::unique_ptr<typename std::remove_pointer<OGRStyleTableH>::type, OGRStyleTableHDeleter>;
173 }
174 
175 
176 
177 /**
178  * An Importer for geospatial vector data supported by OGR.
179  *
180  * The option "separate_layers" will cause OGR layers to be imported as distinct
181  * map parts if set to true.
182  */
183 class OgrFileImport : public Importer
184 {
185 	Q_DECLARE_TR_FUNCTIONS(OpenOrienteering::OgrFileImport)
186 
187 public:
188 	/**
189 	 * The unit type indicates the coordinate system the data units refers to.
190 	 */
191 	enum UnitType
192 	{
193 		UnitOnGround,  ///< Data refers to real dimensions. Includes geograghic CS.
194 		UnitOnPaper    ///< Data refers to dimensions in the (printed) map.
195 	};
196 
197 	/**
198 	 * A Pointer to a function which creates a MapCoordF from double coordinates.
199 	 */
200 	using MapCoordConstructor = MapCoord (OgrFileImport::*)(double, double) const;
201 
202 	/**
203 	 * Constructs a new importer.
204 	 */
205 	OgrFileImport(const QString& path, Map *map, MapView *view, UnitType unit_type = UnitOnGround);
206 
207 	~OgrFileImport() override;
208 
209 
210 	bool supportsQIODevice() const noexcept override;
211 
212 
213 	/**
214 	 * Enables the import of georeferencing from the geospatial data.
215 	 *
216 	 * If this import is not enabled, the georeferencing of the Map given to
217 	 * the constructor will be used instead.
218 	 */
219 	void setGeoreferencingImportEnabled(bool enabled);
220 
221 
222 	/**
223 	 * Tests if the file's spatial references can be used with the given georeferencing.
224 	 *
225 	 * This returns true only if all layers' spatial references can be
226 	 * transformed to the spatial reference systems represented by georef.
227 	 * It will always return false for a local or invalid Georeferencing.
228 	 */
229 	static bool checkGeoreferencing(const QString& path, const Georeferencing& georef);
230 
231 	/**
232 	 * Calculates the average geographic coordinates (WGS84) of the file.
233 	 */
234 	static LatLon calcAverageLatLon(const QString& path);
235 
236 
237 protected:
238 	ogr::unique_srs srsFromMap();
239 
240 	/**
241 	 * Tests if the file's spatial references can be used with the given georeferencing.
242 	 *
243 	 * This returns true only if all layers' spatial references can be
244 	 * transformed to the spatial reference systems represented by georef.
245 	 * It will always return false for a local or invalid Georeferencing.
246 	 */
247 	static bool checkGeoreferencing(OGRDataSourceH data_source, const Georeferencing& georef);
248 
249 	bool importImplementation() override;
250 
251 	ogr::unique_srs importGeoreferencing(OGRDataSourceH data_source);
252 
253 	void importStyles(OGRDataSourceH data_source);
254 
255 	void importLayer(MapPart* map_part, OGRLayerH layer);
256 
257 	void importFeature(MapPart* map_part, OGRFeatureDefnH feature_definition, OGRFeatureH feature, OGRGeometryH geometry);
258 
259 	using ObjectList = std::vector<Object*>;
260 
261 	ObjectList importGeometry(OGRFeatureH feature, OGRGeometryH geometry);
262 
263 	ObjectList importGeometryCollection(OGRFeatureH feature, OGRGeometryH geometry);
264 
265 	Object* importPointGeometry(OGRFeatureH feature, OGRGeometryH geometry);
266 
267 	PathObject* importLineStringGeometry(OGRFeatureH feature, OGRGeometryH geometry);
268 
269 	PathObject* importPolygonGeometry(OGRFeatureH feature, OGRGeometryH geometry);
270 
271 
272 	Symbol* getSymbol(Symbol::Type type, const char* raw_style_string);
273 
274 	MapColor* makeColor(OGRStyleToolH tool, const char* color_string);
275 
276 	void applyPenColor(OGRStyleToolH tool, LineSymbol* line_symbol);
277 
278 	void applyBrushColor(OGRStyleToolH tool, AreaSymbol* area_symbol);
279 
280 
281 	MapCoord toMapCoord(double x, double y) const;
282 
283 	/**
284 	 * A MapCoordConstructor which interprets the given coordinates in millimeters on paper.
285 	 */
286 	MapCoord fromDrawing(double x, double y) const;
287 
288 	/**
289 	 * A MapCoordConstructor which interprets the given coordinates as projected.
290 	 */
291 	MapCoord fromProjected(double x, double y) const;
292 
293 
294 	static LatLon calcAverageLatLon(OGRDataSourceH data_source);
295 
296 	static QPointF calcAverageCoords(OGRDataSourceH data_source, OGRDataSourceH srs);
297 
298 
299 private:
300 	Symbol* getSymbolForPointGeometry(const QByteArray& style_string);
301 	LineSymbol* getLineSymbol(const QByteArray& style_string);
302 	AreaSymbol* getAreaSymbol(const QByteArray& style_string);
303 
304 	PointSymbol* getSymbolForOgrSymbol(OGRStyleToolH tool, const QByteArray& style_string);
305 	TextSymbol* getSymbolForLabel(OGRStyleToolH tool, const QByteArray& style_string);
306 	LineSymbol* getSymbolForPen(OGRStyleToolH tool, const QByteArray& style_string);
307 	AreaSymbol* getSymbolForBrush(OGRStyleToolH tool, const QByteArray& style_string);
308 
309 	QHash<QByteArray, Symbol*> point_symbols;
310 	PointSymbol* default_point_symbol;
311 	QHash<QByteArray, Symbol*> text_symbols;
312 	TextSymbol* default_text_symbol;
313 	QHash<QByteArray, Symbol*> line_symbols;
314 	LineSymbol* default_line_symbol;
315 	QHash<QByteArray, Symbol*> area_symbols;
316 	AreaSymbol* default_area_symbol;
317 	QHash<QByteArray, MapColor*> colors;
318 	MapColor* default_pen_color;
319 
320 	MapCoordConstructor to_map_coord;
321 
322 	ogr::unique_srs map_srs;
323 
324 	OGRSpatialReferenceH data_srs;
325 
326 	ogr::unique_transformation data_transform;
327 
328 	ogr::unique_stylemanager manager;
329 
330 	int empty_geometries = 0;
331 	int no_transformation = 0;
332 	int failed_transformation = 0;
333 	int unsupported_geometry_type = 0;
334 	int too_few_coordinates = 0;
335 
336 	UnitType unit_type;
337 
338 	bool georeferencing_import_enabled = true;
339 };
340 
341 
342 
343 // ### inline code ###
344 
345 inline
toMapCoord(double x,double y)346 MapCoord OgrFileImport::toMapCoord(double x, double y) const
347 {
348 	return (this->*to_map_coord)(x, y);
349 }
350 
351 /**
352  * An Exporter to geospatial vector data supported by OGR.
353  *
354  * OGR needs to know the filename. As well, it doesn't use
355  * a QIODevice, but rather uses the filename directly.
356  */
357 class OgrFileExport : public Exporter
358 {
359 	Q_DECLARE_TR_FUNCTIONS(OpenOrienteering::OgrFileExport)
360 
361 public:
362 
363 	/**
364 	 * Quirks for OGR drivers
365 	 *
366 	 * Each quirk shall use a distinct bit so that they may be OR-combined.
367 	 */
368 	enum OgrQuirk
369 	{
370 		GeorefOptional = 0x01,   ///< The driver does not need or use georeferencing.
371 		NeedsWgs84     = 0x02,   ///< The driver needs WGS84 geographic coordinates.
372 		SingleLayer    = 0x04,   ///< The driver supports just a single layer.
373 		UseLayerField  = 0x08,   ///< Write the symbol names to the layer field.
374 	};
375 
376 	/**
377 	 * A type which handles OR-combinations of OGR driver quirks.
378 	 */
379 	Q_DECLARE_FLAGS(OgrQuirks, OgrQuirk)
380 
381 	OgrFileExport(const QString& path, const Map *map, const MapView *view);
382 	~OgrFileExport() override;
383 
384 	bool supportsQIODevice() const noexcept override;
385 
386 	bool exportImplementation() override;
387 
388 protected:
389 	std::vector<const Symbol*> symbolsForExport() const;
390 
391 	void addPointsToLayer(OGRLayerH layer, const std::function<bool (const Object*)>& condition);
392 	void addTextToLayer(OGRLayerH layer, const std::function<bool (const Object*)>& condition);
393 	void addLinesToLayer(OGRLayerH layer, const std::function<bool (const Object*)>& condition);
394 	void addAreasToLayer(OGRLayerH layer, const std::function<bool (const Object*)>& condition);
395 
396 	OGRLayerH createLayer(const char* layer_name, OGRwkbGeometryType type);
397 
symbolId(const Symbol * symbol)398 	static QByteArray symbolId(const Symbol* symbol) { return QByteArray::number(quint64(symbol), 16); }
399 
400 	void populateStyleTable(const std::vector<const Symbol*>& symbols);
401 
402 	void setupGeoreferencing(GDALDriverH po_driver);
403 	void setupQuirks(GDALDriverH po_driver);
404 
405 private:
406 	ogr::unique_datasource po_ds;
407 	ogr::unique_fielddefn o_name_field;
408 	ogr::unique_srs map_srs;
409 	ogr::unique_styletable table;
410 	ogr::unique_transformation transformation;
411 
412 	const char* symbol_field;
413 
414 	OgrQuirks quirks;
415 };
416 
417 
418 }  // namespace OpenOrienteering
419 
420 #endif // OPENORIENTEERING_OGR_FILE_FORMAT_P_H
421