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