diff --git a/3rd-party/cove/app/colorseditform.cpp b/3rd-party/cove/app/colorseditform.cpp index 5626916d3..ee2fbe47f 100644 --- a/3rd-party/cove/app/colorseditform.cpp +++ b/3rd-party/cove/app/colorseditform.cpp @@ -78,7 +78,9 @@ class ColorsEditingDelegate : public QItemDelegate QVariant v = model->data(model->index(index.row(), 0, index), Qt::BackgroundRole); QColor c = v.value().color(); - clr = QColorDialog::getRgba(c.rgb()); + auto chosen_color = QColorDialog::getColor(c); + if (chosen_color.isValid()) + clr = chosen_color.rgba(); setModelData(nullptr, model, index); event->accept(); return true; @@ -150,7 +152,7 @@ QVariant ColorsListModel::data(const QModelIndex& index, int role) const .arg(qBlue(colors[index.row()]))); } else if (index.column() == 2 && role == Qt::DisplayRole && - index.row() < comments.size()) + index.row() < int(comments.size())) { return QVariant(comments[index.row()]); } diff --git a/3rd-party/cove/app/mainform.cpp b/3rd-party/cove/app/mainform.cpp index 5189fb5fb..c4d5a6cd7 100644 --- a/3rd-party/cove/app/mainform.cpp +++ b/3rd-party/cove/app/mainform.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005-2019 Libor Pecháček. + * Copyright (c) 2005-2020 Libor Pecháček. * Copyright 2020 Kai Pastor * * This file is part of CoVe @@ -249,15 +249,6 @@ void mainForm::afterLoadImage() } } -//! Simple About dialog. -void mainForm::aboutDialog() -{ - QMessageBox::about(this, tr("About"), - trUtf8("

COntour VEctorizer


" - "Author: Libor Pecháček
" - "License: GPL")); -} - /*! Return color randomly created from image pixels. It takes 5 scanlines and * makes an average color of its pixels. */ QRgb mainForm::getColorFromImage(const QImage& image) @@ -633,7 +624,7 @@ void mainForm::on_classificationOptionsButton_clicked() void mainForm::on_initialColorsButton_clicked() { ColorsEditForm* optionsDialog = new ColorsEditForm(this); - int settingsNColors = settings.getInt("nColors"); + auto settingsNColors = static_cast(settings.getInt("nColors")); std::vector comments; auto colors = settings.getInitColors(comments); auto nColors = colors.size(); @@ -791,7 +782,7 @@ void mainForm::on_bwImageSaveButton_clicked() QString selectedFilter("PNG (*.png)"); QString newfilename = QFileDialog::getSaveFileName( - this, QString::null, QFileInfo(imageFileName).dir().path(), filter, + this, QString(), QFileInfo(imageFileName).dir().path(), filter, &selectedFilter); if (!newfilename.isEmpty()) diff --git a/3rd-party/cove/app/mainform.h b/3rd-party/cove/app/mainform.h index a3ede2909..c7e322745 100644 --- a/3rd-party/cove/app/mainform.h +++ b/3rd-party/cove/app/mainform.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005-2019 Libor Pecháček. + * Copyright (c) 2005-2020 Libor Pecháček. * * This file is part of CoVe * @@ -97,7 +97,6 @@ class mainForm : public QDialog std::vector getSelectedColors(); public slots: - void aboutDialog(); void setInitialColors(bool on); void colorButtonToggled(bool on); diff --git a/3rd-party/cove/libvectorizer/Morphology.cpp b/3rd-party/cove/libvectorizer/Morphology.cpp index 6837eac8a..2068565f2 100644 --- a/3rd-party/cove/libvectorizer/Morphology.cpp +++ b/3rd-party/cove/libvectorizer/Morphology.cpp @@ -128,10 +128,8 @@ bool Morphology::rosenfeld(ProgressObserver* progressObserver) { bool cancel = false; // whether the thinning was canceled unsigned int x, y; // Pixel coordinates - unsigned int pc = 0; // Pass count unsigned int count; // Deleted pixel count unsigned int p, q; // Neighborhood maps of adjacent cells - unsigned int m; // Deletion direction mask thinnedImage = image; thinnedImage.detach(); @@ -144,7 +142,6 @@ bool Morphology::rosenfeld(ProgressObserver* progressObserver) do { // Thin image lines until there are no deletions - pc++; count = 0; for (auto const m : masks) diff --git a/3rd-party/cove/libvectorizer/Vectorizer.cpp b/3rd-party/cove/libvectorizer/Vectorizer.cpp index c8a4bbf2a..f0a230a9a 100644 --- a/3rd-party/cove/libvectorizer/Vectorizer.cpp +++ b/3rd-party/cove/libvectorizer/Vectorizer.cpp @@ -164,6 +164,9 @@ namespace cove { /*! \var PatternStrategy Vectorizer::patternStrategy Selected pattern selection strategy. \sa setPatternStrategy */ +// virtual +Vectorizer::~Vectorizer() = default; + Vectorizer::Vectorizer() : mc(std::make_unique(2.0)) , E(100000) @@ -171,6 +174,7 @@ Vectorizer::Vectorizer() , q(0.5) , minAlpha(1e-6) , p(2) + , quality(0) , learnMethod(KOHONEN_CLASSIC) , colorSpace(COLSPC_RGB) , alphaStrategy(ALPHA_CLASSIC) diff --git a/3rd-party/cove/libvectorizer/Vectorizer.h b/3rd-party/cove/libvectorizer/Vectorizer.h index a67611612..077638d20 100644 --- a/3rd-party/cove/libvectorizer/Vectorizer.h +++ b/3rd-party/cove/libvectorizer/Vectorizer.h @@ -27,9 +27,9 @@ #include #include -#include "MapColor.h" - namespace cove { + +class MapColor; class ProgressObserver; class Vectorizer @@ -81,8 +81,9 @@ class Vectorizer void deleteColorsTable(); public: + virtual ~Vectorizer(); Vectorizer(); - Vectorizer(QImage& im); + explicit Vectorizer(QImage& im); virtual void setClassificationMethod(LearningMethod learnMethod); virtual void setColorSpace(ColorSpace colorSpace); virtual void setP(double p); diff --git a/CMakeLists.txt b/CMakeLists.txt index b9c455cc0..2cda566ca 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,7 +29,7 @@ endif() # Project declaration -project(Mapper VERSION 0.9.3 LANGUAGES CXX C) +project(Mapper VERSION 0.9.4 LANGUAGES CXX C) set(Mapper_COPYRIGHT "(C) 2020 The OpenOrienteering developers") math(EXPR Mapper_VERSION_CODE "${Mapper_VERSION_MAJOR} * 10000 + ${Mapper_VERSION_MINOR} * 100 + ${Mapper_VERSION_PATCH} * 2 + ${CMAKE_SIZEOF_VOID_P} / 4 - 1") diff --git a/ci/build.yml b/ci/build.yml index d00f9205c..4e6c5fa3f 100644 --- a/ci/build.yml +++ b/ci/build.yml @@ -106,8 +106,13 @@ steps: # if [ -f build/default/openorienteering-mapper-ci/Testing/TAG ]; then # Test result exist, so executables are runnable + OLD_PATH="$PATH" + if [ -n "$(MINGW)" ]; then + export PATH=`cygpath -u "$(SUPERBUILD_INSTALL_DIR)"`/$(MINGW)/superbuild/bin:${PATH} + fi build/${TARGET:-default}/openorienteering-mapper-ci/src/gdal/mapper-gdal-info \ | tee output/OpenOrienteering-Mapper-GDAL-info-$(IMAGE_NAME)$(OUTPUT_SUFFIX).md; + export PATH="$OLD_PATH" else # No test results, probably cross-compiling set +x # https://github.com/microsoft/azure-pipelines-tasks/issues/10331 diff --git a/ci/filter-stderr.sed b/ci/filter-stderr.sed index fbec9d665..ffd1a4f4c 100644 --- a/ci/filter-stderr.sed +++ b/ci/filter-stderr.sed @@ -1,12 +1,13 @@ # Cf. https://github.com/Microsoft/azure-pipelines-tasks/blob/master/docs/authoring/commands.md -/^\/.*openorienteering.*[Ww]arning:/ { - s,^\([a-zA-Z]:\)?/.*/build/source/openorienteering-mapper-ci/,, +/:.*[0-9]: [Ww]arning:/ { + /libocad/ b + s,^\([a-zA-Z]:\)?/.*/1/s/,, s,^\([a-zA-Z]:\)?/.*/build/,, s,^,##vso[task.LogIssue type=warning;], s,^\([^;]*\);\]\([^:]*\):\([0-9]*\):\([0-9]*\): *[Ww]arning: *,\1;sourcepath=\2;linenumber=\3;columnnumber=\4;], } /^\/.*[Ee]rror:/ { - s,^\([a-zA-Z]:\)?/.*/build/source/openorienteering-mapper-ci/,, + s,^\([a-zA-Z]:\)?/.*/1/s/,, s,^\([a-zA-Z]:\)?/.*/build/,, s,^,##vso[task.LogIssue type=error;], s,^\([^;]*\);\]\([^:]*\):\([0-9]*\):\([0-9]*\): *[Ee]rror: *,\1;sourcepath=\2;linenumber=\3;columnnumber=\4;], @@ -14,7 +15,7 @@ # Include-what-you-use / should add these lines:$/ { h - s,^\([a-zA-Z]:\)?/.*/build/source/openorienteering-mapper-ci/,, + s,^\([a-zA-Z]:\)?/.*/1/s/,, s,^\([a-zA-Z]:\)?/.*/build/,, s,^,##vso[task.LogIssue type=warning;], s,^\([^;]*\);\]\([^:]*\) should add these lines:,\1;sourcepath=\2;]include-what-you-use reported diagnostics, diff --git a/code-check-wrapper.sh b/code-check-wrapper.sh index 277bb7393..3b4354105 100755 --- a/code-check-wrapper.sh +++ b/code-check-wrapper.sh @@ -62,6 +62,7 @@ for I in \ georeferencing.cpp \ georeferencing_dialog.cpp \ georeferencing_t.cpp \ + icon_engine \ key_button_bar.cpp \ line_symbol.cpp \ main.cpp \ @@ -70,17 +71,22 @@ for I in \ map_editor.cpp \ map_find_feature.cpp \ map_widget.cpp \ + mapper_proxystyle.cpp \ /object.cpp \ object_mover.cpp \ object_query.cpp \ ocd_file_format.cpp \ ocd_t.cpp \ overriding_shortcut.cpp \ + paint_on_template \ point_symbol.cpp \ print_widget.cpp \ renderable.cpp \ renderable_implementation.cpp \ + rotate_map_dialog.cpp \ settings_dialog.cpp \ + stretch_map_dialog.cpp \ + style_t.cpp \ /symbol.cpp \ symbol_replacement.cpp \ symbol_replacement_dialog.cpp \ @@ -94,8 +100,11 @@ for I in \ template_list_widget.cpp \ template_map.cpp \ template_placeholder.cpp \ + template_table_model.cpp \ + template_t.cpp \ template_tool \ template_track.cpp \ + text_object_editor_helper.cpp \ text_brwoser_dialog \ toast.cpp \ track_t.cpp \ diff --git a/codespell.sh b/codespell.sh index 92acbc959..94c4f6fe4 100755 --- a/codespell.sh +++ b/codespell.sh @@ -33,3 +33,5 @@ codespell -q 7 \ -L endwhile \ -L intrest \ "$@" + +git log -n 20 | codespell -q 7 - | sed 's/^/git log: /' diff --git a/doc/licensing/superbuild-licensing.cmake b/doc/licensing/superbuild-licensing.cmake index 140de9be8..cb3e3e008 100644 --- a/doc/licensing/superbuild-licensing.cmake +++ b/doc/licensing/superbuild-licensing.cmake @@ -37,7 +37,7 @@ if(NOT LICENSING_COPYRIGHT_DIR OR NOT LICENSING_COMMON_DIR) endif() -# Based on OpenOrienteering superbuild as of 2020-03-26 +# Based on OpenOrienteering superbuild as of 2020-06-21 list(APPEND third_party_components giflib libjpeg-turbo @@ -47,6 +47,7 @@ list(APPEND third_party_components libpng libtiff libwebp + poppler ) find_package(Qt5Core REQUIRED QUIET) if(NOT ${Qt5Core_VERSION} VERSION_LESS 5.9) @@ -61,9 +62,10 @@ if(NOT APPLE) libsqlite ) endif() -if(WIN32) +if(ANDROID) list(APPEND third_party_components - zlib + freetype + libiconv ) endif() if(CMAKE_ANDROID_STL_TYPE MATCHES "gnustl") @@ -75,14 +77,25 @@ elseif(CMAKE_ANDROID_STL_TYPE MATCHES "c\\+\\+") libc++ ) endif() +if(APPLE) + list(APPEND third_party_components + freetype + ) +endif() if(MINGW) list(APPEND third_party_components gcc-libs - libiconv mingw-w64 winpthreads ) endif() +if(WIN32) + list(APPEND third_party_components + freetype + libiconv + zlib + ) +endif() list(APPEND common_license_names Apache-2.0 diff --git a/doc/manual/CMakeLists.txt b/doc/manual/CMakeLists.txt index 56d72f820..d50c0ced1 100644 --- a/doc/manual/CMakeLists.txt +++ b/doc/manual/CMakeLists.txt @@ -103,7 +103,7 @@ message(STATUS "Conditional manual sections: ${MANUAL_SECTIONS}") set(DOXYFILE_HTML_EXTRA ) if(ANDROID) set(DOXYFILE_HTML_EXTRA " -FILE_PATTERNS = android* toolbar* +FILE_PATTERNS = android* toolbar* touch-mode* USE_MDFILE_AS_MAINPAGE = android-app.md ") endif() diff --git a/doc/manual/pages/android-app.md b/doc/manual/pages/android-app.md index 5f853cd15..f673f64f7 100644 --- a/doc/manual/pages/android-app.md +++ b/doc/manual/pages/android-app.md @@ -4,7 +4,7 @@ authors: - Kai Pastor - Thomas Schoeps keywords: Android -edited: 3 February 2018 +edited: 19 September 2020 redirect_from: - /android/ - '/Android Manual/' @@ -22,149 +22,9 @@ the desktop version, so please read this first for important instructions. [Storing Maps and Templates on Android Devices](android-storage.md){: .subpage} +[Using the Mapper app in "Touch Mode"](touch-mode.md){: .subpage} + {% if doxygen %} -**Note:** The [online version](https://www.openorienteering.org/mapper-manual/android/) of this documentation may contain additions and corrections. +**Note:** The [online version](https://www.openorienteering.org/mapper-manual/android-app/) of this documentation may contain additions and corrections. {% endif %} - -## Using the Mapper app - -On startup, the app shows a list of map files available from the [supported storage locations](android-storage.md). To start editing a map, touch its filename. - -![ ](images/Android_UI_explanation.png) - -The map editor is slightly different from the desktop version. This page focuses on specific features of the mobile version. To find out about the other symbols, see [Toolbars](toolbars.md) in the desktop application's manual. - -#### ![ ](../mapper-images/arrow-thin-upleft.png) Hide the top bar - -Using this button, you may hide the top bar, leaving more screen for the map. A similar button will remain visible for restoring the top bar again. - - -#### ![ ](../mapper-images/compass.png) Compass display toggle - -When this item is activated, a compass is shown in the top-left corner of the screen. The red line indicates the north direction, while the black-white lines show the up direction of your device. If both are aligned, the device is aligned with magnetic north. When the needle is close to the north direction, a green circle appears which quickly shows how well the device is aligned: the larger it is, the better the alignment is. The compass works three-dimensionally, so you do not need to hold the device in a flat pose for it to work. - -Note: the usefulness of the compass depends on the presence of a gyroscope, as mentioned in the device recommendations section. - -Attention: the compass is very sensitive to magnetic materials. Android tries the eliminate the influence of local magnetic fields, but this requires calibration. To do this, move the device around in the shape of an 8 while rotating it and while the compass is active. If the local influence changes, re-calibration is required. - - -#### ![ ](../mapper-images/tool-gps-display.png) Location display toggle - -If the currently active map work has valid georeferencing, this control will be enabled and will show a dot at your current position as obtained from GPS or other positioning services. In addition, in case an accuracy estimate (HDOP) is available, this shows a circle around the dot which indicates the positioning accuracy. The chance of your real position being in the circle is approximately 70%. - -While this is active, your track is automatically recorded into a GPX file in the directory of your map, named " - GPS-.gpx". This file is included as a template in the map. You can simply show it using the template visibility control button to view your track. - -Note that track recording might be interrupted while the screen is off or the app is in the background to save battery. Keep the app active if you want to ensure continuous track recording. - - -#### ![ ](../mapper-images/gps-distance-rings.png) Distance rings toggle - -This button is active if location display is active. When activated, this will display rings in 10 meter and 20 meter distance from the current location which can be used to do rough distance estimates. - - -#### ![ ](../mapper-images/rotate-map.png) Automatic north alignment toggle - -When this toggle is active, the map is automatically rotated to north using the device's internal compass. The update frequency is 1 update per second. This is quite low on purpose to save battery, but you probably still need some replacement batteries when using this mode. While interacting with the map and for another 1.5 seconds after that, the map is not rotated to allow editing without unwanted rotations. If GPS position display is activated at the same time, a line starting from the GPS position and going in the direction of the up direction of your device is displayed which shows your viewing direction (assuming you are holding the device upright). - - ------ - -#### ![ ](../mapper-images/map-parts.png) Map parts selector - -This button opens a popup for changing the active map part. - - -#### ![ ](../mapper-images/tool-touch-cursor.png) Touch cursor toggle - -This symbol activates a helper cursor which allows slow, but precise editing with fingers only. The cursor consists of a circular area and a pointer above the circle. - -![ ](images/touch_cursor.png) - -Touching the map anywhere outside the circle moves the pointer to this position. Touching the circle simulates a real touch at the pointer position above the circle. - - -#### ![ ](../mapper-images/templates.png) Template control - -This shows a list of all opened templates. Touching a template toggles its visibility. - - -#### ![ ](../mapper-images/close.png) Close button - -This closes the active map and returns to the map selection screen. - - -#### ![ ](../mapper-images/three-dots.png) Overflow button - -Depending on the screen size of your device, some of the symbols available not fit onto the screen. They are instead placed in a list which is shown on touching the overflow button. If all symbols fit on the screen, this button is unused. - - ------ - -#### ![ ](../mapper-images/view-zoom-out.png) Zoom out / Zoom to fixed - -A short press will zoom out by one step. A long press will open a menu where you can jump to a fixed zoom (cf. screenshot). - - -#### ![ ](../mapper-images/move-to-gps.png) Move to location - -When positioning services are enabled, this will move the map so that your current location is in the middle of the screen. - - -#### ![ ](../mapper-images/gps-temporary-point.png) Mark temporary position - -This records a single point at your current location to act as a drawing help. Note that this marker is not saved in the map. - - -#### ![ ](../mapper-images/gps-temporary-path.png) Record temporary trace - -This symbol is enabled when the location display is active. It records a temporary trace of the location, which is intended to act as a guideline for drawings. Using the trace directly is rarely useful because of the inaccurateness of location services. -Note that the temporary trace is not saved in the map. - - -#### ![ ](../mapper-images/gps-temporary-clear.png) Clear temporary markers - -This removes the temporary traces and position markers. (They will also be discarded when closing the map file.) - - -#### ![ ](../mapper-images/pencil.png) Paint on template - -By this tool, mapper supports free-hand drawing onto image templates. There is -a small selection of colors for drawing. White is used for corrections to -previous drawings. There is also a dedicated undo/redo feature. - -This tool is an convenient alternative when you want to do only (or mostly) -drafting in the field, but draw the final version of the map at home on a PC. -It does not create new map objects but only alters the template you draw on. -This may help to deal with low processing power or battery runtime on mobile -devices. - -Please note that direct drawing onto base map images is *not* recommended: -First, the eraser tool would also erase the base map. Second, these images -usually are in the lossy JPEG format which will introduce visible artifacts -when saving the file and loading it repeatedly. - - -#### ![ ](../mapper-images/paint-on-template-settings.png) Paint-on-template settings - -This button allows to select an image template for scribbling, -or to add a new one covering the vicinity of your position. - - ------ - -#### ![ ](../mapper-images/draw-freehand.png) Free-hand draw tool - -Allows to draw paths free-handedly. - - -#### ![ ](../mapper-images/draw-point-gps.png) Draw at location tool - -Sets a point object at your current location. Selecting this button first enters an averaging mode. While it is active, the input from the location service is averaged to get a more accurate estimate. Touch the map display anywhere to finish the averaging. - - -#### Symbol selector - -Displays the active symbol, and opens the symbol selection screen on short press. - -A long press of the symbol selector opens a popup menu which allows to read the symbol name and description, and to toggle the visibility and the protection state of the symbol. diff --git a/doc/manual/pages/android-storage.md b/doc/manual/pages/android-storage.md index c9fe50200..36b3b9b6c 100644 --- a/doc/manual/pages/android-storage.md +++ b/doc/manual/pages/android-storage.md @@ -14,7 +14,7 @@ You can choose the following locations for storing data which you want to edit a Files in this area are stored permanently, until you decide to remove them. Unfortunately, there is usually only limited capacity available - this memory is expensive when buying the device. - - Path "Android/data/org.openorienteering.mapper/files" on arbitrary storage volumes such as SD cards (since Mapper 0.6.5). + - Path "Android/data/org.openorienteering.mapper/files" on arbitrary storage volumes such as built-in memory or SD cards (since Mapper 0.6.5). This is the only convenient way to store map data on cheap extra memory cards. However, you need to be aware of the fact that these folders are removed by Android when you uninstall the Mapper app. So please carefully save your changes to a PC. diff --git a/doc/manual/pages/find_objects.md b/doc/manual/pages/find_objects.md index d34d42bbc..33ffb3eae 100644 --- a/doc/manual/pages/find_objects.md +++ b/doc/manual/pages/find_objects.md @@ -33,12 +33,15 @@ The following expressions are supported: | ---------- | ---------------- | | word | having "word" in any textual property | | "some words" | having the phrase "some words" in any textual property | +| NOT expr | not matching expr | | expr1 AND expr2 | matching both expression expr1 and expr2 | | expr1 OR expr2 | matching expression expr1 or expr2 (or both) | | key = value | having a tag with the given key and value | | key ~= word | having a tag with the given key and a value containing "word" | | key != word | having a tag with the given key and a value different from "word", or not having a tag with this key | +| SYMBOL 123 | having the symbol with code number 123 | +NOT has precedence over AND and OR. AND has precedence over OR. You may use parentheses to nest operators in another way. You may use double quotes to deal with keys and values containing white space or the special words and signs. diff --git a/doc/manual/pages/gdal.md b/doc/manual/pages/gdal.md index 0ed9eb74e..0adfa99e2 100644 --- a/doc/manual/pages/gdal.md +++ b/doc/manual/pages/gdal.md @@ -1,19 +1,20 @@ --- title: Geospatial data support with GDAL keywords: GDAL -edited: 21 December 2019 +edited: 20 September 2020 --- OpenOrienteering Mapper uses the [GDAL library](https://gdal.org) -for supporting a broad range of geospatial raster and vector data. +for supporting a broad range of geospatial +raster data, such as GeoTIFF or JPEG2000 files, +vector data, such as Shape, OSM, or DXF files, +and hybrid formats such as PDF. GDAL's vector data support used to be known as OGR, so you will sometimes find this name, too. -Geospatial raster data, such as GeoTIFF or JPEG2000 files, -can be used as [templates](templates.md) starting with Mapper v0.9.2. -Geospatial vector data, such as Shape, OSM, or DXF files, -may be used as templates, too. -But it also possible to open or import vector data as map data. +Geospatial raster data and vector data can be used as templates. +But it also possible to open vector data files as maps +and to import vector data into map files. In addition, the [File menu](file_menu.md) offers the option to export a map in selected geospatial vector data formats. @@ -22,147 +23,229 @@ given to the filename. When using GDAL formats as templates, some filename extensions may be supported by multiple providers (plain image, GDAL raster data, GDAL vector data). -In these cases, a secondary provider from GDAL may be selected by prefixing the -regular extension with "raster." or "vector." as listed below. Please note that some of the raster formats listed below are not used for images but for actual geospatial metrics meant for algorithmic processing. -## GDAL formats offered by OpenOrienteering Mapper +## GDAL drivers offered by OpenOrienteering Mapper The following list is valid for the official Android, macOS and Windows releases -as of v0.9.2. -Linux packages are likely to support additional formats, +as of v0.9.4. +Linux packages are likely to support a slightly different set of formats, depending on the configuration of the distribution's GDAL package. ### Raster import drivers | Driver | Long name | Extensions | |--------|------------|------------| -| AAIGrid | [Arc/Info ASCII Grid](https://gdal.org/frmt_various.html#AAIGrid) | asc | -| ACE2 | [ACE2](https://gdal.org/frmt_various.html#ACE2) | ACE2 | -| ADRG | [ARC Digitized Raster Graphics](https://gdal.org/frmt_various.html#ADRG) | gen | -| BIGGIF | [Graphics Interchange Format (.gif)](https://gdal.org/frmt_gif.html) | raster.gif | -| BLX | [Magellan topo (.blx)](https://gdal.org/frmt_various.html#BLX) | blx | -| BMP | [MS Windows Device Independent Bitmap](https://gdal.org/frmt_bmp.html) | raster.bmp | -| BT | [VTP .bt (Binary Terrain) 1.3 Format](https://gdal.org/frmt_various.html#BT) | bt | -| BYN | [Natural Resources Canada's Geoid](https://gdal.org/frmt_byn.html) | byn err | -| CAD | [AutoCAD Driver](https://gdal.org/drv_cad.html) | raster.dwg | -| CALS | [CALS (Type 1)](https://gdal.org/frmt_cals.html) | .cal .ct1 | -| COASP | DRDC COASP SAR Processor Raster | hdr | -| DTED | [DTED Elevation Raster](https://gdal.org/frmt_various.html#DTED) | dt0 dt1 dt2 | -| E00GRID | [Arc/Info Export E00 GRID](https://gdal.org/frmt_various.html#E00GRID) | raster.e00 | -| ECRGTOC | [ECRG TOC format](https://gdal.org/frmt_various.html#ECRGTOC) | raster.xml | -| EHdr | [ESRI .hdr Labelled](https://gdal.org/frmt_various.html#EHdr) | bil | -| ENVI | [ENVI .hdr Labelled](https://gdal.org/frmt_various.html#ENVI) | | -| ERS | [ERMapper .ers Labelled](https://gdal.org/frmt_ers.html) | ers | -| ESAT | [Envisat Image Format](https://gdal.org/frmt_various.html#Envisat) | n1 | -| FIT | [FIT Image](https://gdal.org/frmt_various.html#) | | -| GFF | [Ground-based SAR Applications Testbed File Format (.gff)](https://gdal.org/frmt_various.html#GFF) | gff | -| GIF | [Graphics Interchange Format (.gif)](https://gdal.org/frmt_gif.html) | raster.gif | -| GPKG | [GeoPackage](https://gdal.org/drv_geopackage.html) | raster.gpkg | -| GRIB | [GRIdded Binary (.grb, .grb2)](https://gdal.org/frmt_grib.html) | grb grb2 grib2 | -| GS7BG | [Golden Software 7 Binary Grid (.grd)](https://gdal.org/frmt_various.html#GS7BG) | grd | -| GSAG | [Golden Software ASCII Grid (.grd)](https://gdal.org/frmt_various.html#GSAG) | grd | -| GSBG | [Golden Software Binary Grid (.grd)](https://gdal.org/frmt_various.html#GSBG) | grd | +| AAIGrid | [Arc/Info ASCII Grid](https://gdal.org/drivers/raster/aaigrid.html) | asc | +| ACE2 | [ACE2](https://gdal.org/drivers/raster/ace2.html) | ACE2 | +| ADRG | [ARC Digitized Raster Graphics](https://gdal.org/drivers/raster/adrg.html) | gen | +| AIG | [Arc/Info Binary Grid](https://gdal.org/drivers/raster/aig.html) | | +| ARG | [Azavea Raster Grid format](https://gdal.org/drivers/raster/arg.html) | | +| AirSAR | [AirSAR Polarimetric Image](https://gdal.org/drivers/raster/airsar.html) | | +| BIGGIF | [Graphics Interchange Format (.gif)](https://gdal.org/drivers/raster/gif.html) | raster.gif | +| BLX | [Magellan topo (.blx)](https://gdal.org/drivers/raster/blx.html) | blx | +| BMP | [MS Windows Device Independent Bitmap](https://gdal.org/drivers/raster/bmp.html) | raster.bmp | +| BSB | [Maptech BSB Nautical Charts](https://gdal.org/drivers/raster/bsb.html) | | +| BT | [VTP .bt (Binary Terrain) 1.3 Format](https://gdal.org/drivers/raster/bt.html) | bt | +| BYN | [Natural Resources Canada's Geoid](https://gdal.org/drivers/raster/byn.html) | byn err | +| CAD | [AutoCAD Driver](https://gdal.org/drv_cad.html) | dwg | +| CALS | [CALS (Type 1)](https://gdal.org/drivers/raster/cals.html) | cal ct1 | +| CEOS | [CEOS Image](https://gdal.org/drivers/raster/ceos.html) | | +| COASP | [DRDC COASP SAR Processor Raster](https://gdal.org/drivers/raster/coasp.html) | hdr | +| COSAR | [COSAR Annotated Binary Matrix (TerraSAR-X)](https://gdal.org/drivers/raster/cosar.html) | | +| CPG | Convair PolGASP | | +| CTG | [USGS LULC Composite Theme Grid](https://gdal.org/drivers/raster/ctg.html) | | +| CTable2 | CTable2 Datum Grid Shift | | +| DAAS | [Airbus DS Intelligence Data As A Service driver](https://gdal.org/drivers/raster/daas.html) | | +| DERIVED | [Derived datasets using VRT pixel functions](https://gdal.org/drivers/raster/derived.html) | | +| DIMAP | [SPOT DIMAP](https://gdal.org/drivers/raster/dimap.html) | | +| DIPEx | DIPEx | | +| DOQ1 | [USGS DOQ (Old Style)](https://gdal.org/drivers/raster/doq1.html) | | +| DOQ2 | [USGS DOQ (New Style)](https://gdal.org/drivers/raster/doq2.html) | | +| DTED | [DTED Elevation Raster](https://gdal.org/drivers/raster/dted.html) | dt0 dt1 dt2 | +| E00GRID | [Arc/Info Export E00 GRID](https://gdal.org/drivers/raster/e00grid.html) | e00 | +| ECRGTOC | [ECRG TOC format](https://gdal.org/drivers/raster/ecrgtoc.html) | xml | +| EEDAI | [Earth Engine Data API Image](https://gdal.org/drivers/raster/eedai.html) | | +| EHdr | [ESRI .hdr Labelled](https://gdal.org/drivers/raster/ehdr.html) | bil | +| EIR | [Erdas Imagine Raw](https://gdal.org/drivers/raster/eir.html) | | +| ELAS | ELAS | | +| ENVI | [ENVI .hdr Labelled](https://gdal.org/drivers/raster/envi.html) | | +| ERS | [ERMapper .ers Labelled](https://gdal.org/drivers/raster/ers.html) | ers | +| ESAT | [Envisat Image Format](https://gdal.org/drivers/raster/esat.html) | n1 | +| FAST | [EOSAT FAST Format](https://gdal.org/drivers/raster/fast.html) | | +| FIT | [FIT Image](https://gdal.org/drivers/raster/fit.html) | | +| FujiBAS | [Fuji BAS Scanner Image](https://gdal.org/drivers/raster/fujibas.html) | | +| GFF | [Ground-based SAR Applications Testbed File Format (.gff)](https://gdal.org/drivers/raster/gff.html) | gff | +| GIF | [Graphics Interchange Format (.gif)](https://gdal.org/drivers/raster/gif.html) | raster.gif | +| GPKG | [GeoPackage](https://gdal.org/drv_geopackage.html) | gpkg | +| GRASSASCIIGrid | [GRASS ASCII Grid](https://gdal.org/drivers/raster/grassasciigrid.html) | | +| GRIB | [GRIdded Binary (.grb, .grb2)](https://gdal.org/drivers/raster/grib.html) | grb grb2 grib2 | +| GS7BG | [Golden Software 7 Binary Grid (.grd)](https://gdal.org/drivers/raster/gs7bg.html) | grd | +| GSAG | [Golden Software ASCII Grid (.grd)](https://gdal.org/drivers/raster/gsag.html) | grd | +| GSBG | [Golden Software Binary Grid (.grd)](https://gdal.org/drivers/raster/gsbg.html) | grd | +| GSC | [GSC Geogrid](https://gdal.org/drivers/raster/gsc.html) | | | GTX | NOAA Vertical Datum .GTX | gtx | -| GTiff | [GeoTIFF](https://gdal.org/frmt_gtiff.html) | tif tiff | -| GXF | [GeoSoft Grid Exchange Format](https://gdal.org/frmt_various.html#GXF) | gxf | -| HF2 | [HF2/HFZ heightfield raster](https://gdal.org/frmt_hf2.html) | hf2 | -| HFA | [Erdas Imagine Images (.img)](https://gdal.org/frmt_hfa.html) | img | -| IGNFHeightASCIIGrid | [IGN France height correction ASCII Grid](https://gdal.org/frmt_various.html#IGNFHeightASCIIGrid) | mnt raster.txt gra | +| GTiff | [GeoTIFF](https://gdal.org/drivers/raster/gtiff.html) | tif tiff | +| GXF | [GeoSoft Grid Exchange Format](https://gdal.org/drivers/raster/gxf.html) | gxf | +| GenBin | [Generic Binary (.hdr Labelled)](https://gdal.org/drivers/raster/genbin.html) | | +| HF2 | [HF2/HFZ heightfield raster](https://gdal.org/drivers/raster/hf2.html) | hf2 | +| HFA | [Erdas Imagine Images (.img)](https://gdal.org/drivers/raster/hfa.html) | img | +| IDA | [Image Data and Analysis](https://gdal.org/drivers/raster/ida.html) | | +| IGNFHeightASCIIGrid | [IGN France height correction ASCII Grid](https://gdal.org/drivers/raster/ignfheightasciigrid.html) | mnt txt gra | | ILWIS | ILWIS Raster Map | mpr mpl | -| IRIS | [IRIS data (.PPI, .CAPPi etc)](https://gdal.org/frmt_various.html#IRIS) | ppi | -| ISIS3 | [USGS Astrogeology ISIS cube (Version 3)](https://gdal.org/frmt_isis3.html) | lbl cub | -| JDEM | [Japanese DEM (.mem)](https://gdal.org/frmt_various.html#JDEM) | mem | -| JP2OpenJPEG | [JPEG-2000 driver based on OpenJPEG library](https://gdal.org/frmt_jp2openjpeg.html) | jp2 j2k | -| JPEG | [JPEG JFIF](https://gdal.org/frmt_jpeg.html) | raster.jpg raster.jpeg | -| KMLSUPEROVERLAY | Kml Super Overlay | raster.kml kmz | +| INGR | [Intergraph Raster](https://gdal.org/drivers/raster/intergraphraster.html) | | +| IRIS | [IRIS data (.PPI, .CAPPi etc)](https://gdal.org/drivers/raster/iris.html) | ppi | +| ISCE | [ISCE raster](https://gdal.org/drivers/raster/isce.html) | | +| ISG | [International Service for the Geoid](https://gdal.org/drivers/raster/isg.html) | isg | +| ISIS2 | [USGS Astrogeology ISIS cube (Version 2)](https://gdal.org/drivers/raster/isis2.html) | | +| ISIS3 | [USGS Astrogeology ISIS cube (Version 3)](https://gdal.org/drivers/raster/isis3.html) | lbl cub | +| JAXAPALSAR | [JAXA PALSAR Product Reader (Level 1.1/1.5)](https://gdal.org/drivers/raster/palsar.html) | | +| JDEM | [Japanese DEM (.mem)](https://gdal.org/drivers/raster/jdem.html) | mem | +| JP2OpenJPEG | [JPEG-2000 driver based on OpenJPEG library](https://gdal.org/drivers/raster/jp2openjpeg.html) | jp2 j2k | +| JPEG | [JPEG JFIF](https://gdal.org/drivers/raster/jpeg.html) | raster.jpg raster.jpeg | +| KMLSUPEROVERLAY | Kml Super Overlay | kml kmz | | KRO | KOLOR Raw | kro | -| LCP | [FARSITE v.4 Landscape File (.lcp)](https://gdal.org/frmt_lcp.html) | lcp | -| Leveller | [Leveller heightfield](https://gdal.org/frmt_leveller.html) | ter | -| MBTiles | [MBTiles](https://gdal.org/frmt_mbtiles.html) | mbtiles | -| MFF | [Vexcel MFF Raster](https://gdal.org/frmt_various.html#MFF) | hdr | -| MRF | [Meta Raster Format](https://gdal.org/frmt_marfa.html) | mrf | -| MSGN | [EUMETSAT Archive native (.nat)](https://gdal.org/frmt_msgn.html) | nat | -| NGSGEOID | [NOAA NGS Geoid Height Grids](https://gdal.org/frmt_ngsgeoid.html) | bin | -| NITF | [National Imagery Transmission Format](https://gdal.org/frmt_nitf.html) | ntf | -| NTv1 | NTv1 Datum Grid Shift | raster.dat | -| NTv2 | NTv2 Datum Grid Shift | gsb | -| NWT_GRC | [Northwood Classified Grid Format .grc/.tab](https://gdal.org/frmt_various.html#northwood_grc) | grc | -| NWT_GRD | [Northwood Numeric Grid Format .grd/.tab](https://gdal.org/frmt_nwtgrd.html) | grd | -| PCIDSK | [PCIDSK Database File](https://gdal.org/frmt_pcidsk.html) | pix | -| PDS4 | [NASA Planetary Data System 4](https://gdal.org/frmt_pds4.html) | raster.xml | -| PNG | [Portable Network Graphics](https://gdal.org/frmt_various.html#PNG) | raster.png | -| PNM | [Portable Pixmap Format (netpbm)](https://gdal.org/frmt_various.html#PNM) | pgm ppm pnm | -| PRF | [Racurs PHOTOMOD PRF](https://gdal.org/frmt_prf.html) | prf | -| R | [R Object Data Store](https://gdal.org/frmt_r.html) | rda | -| RDA | [DigitalGlobe Raster Data Access driver](https://gdal.org/frmt_rda.html) | dgrda | -| RIK | [Swedish Grid RIK (.rik)](https://gdal.org/frmt_various.html#RIK) | rik | -| RMF | [Raster Matrix Format](https://gdal.org/frmt_rmf.html) | rsw | -| RPFTOC | [Raster Product Format TOC format](https://gdal.org/frmt_various.html#RPFTOC) | toc | -| RRASTER | [R Raster](https://gdal.org/frmt_various.html#RRASTER) | grd | -| RST | [Idrisi Raster A.1](https://gdal.org/frmt_Idrisi.html) | rst | -| Rasterlite | [Rasterlite](https://gdal.org/frmt_rasterlite.html) | raster.sqlite | -| SAGA | [SAGA GIS Binary Grid (.sdat, .sg-grd-z)](https://gdal.org/frmt_various.html#SAGA) | sdat sg-grd-z | -| SDTS | [SDTS Raster](https://gdal.org/frmt_various.html#SDTS) | ddf | -| SGI | [SGI Image File Format 1.0](https://gdal.org/frmt_various.html#SGI) | rgb | -| SIGDEM | [Scaled Integer Gridded DEM .sigdem](https://gdal.org/frmt_various.html#SIGDEM) | sigdem | -| SNODAS | [Snow Data Assimilation System](https://gdal.org/frmt_various.html#SNODAS) | hdr | -| SRP | [Standard Raster Product (ASRP/USRP)](https://gdal.org/frmt_various.html#SRP) | img | -| SRTMHGT | [SRTMHGT File Format](https://gdal.org/frmt_various.html#SRTMHGT) | hgt | -| Terragen | [Terragen heightfield](https://gdal.org/frmt_terragen.html) | ter | -| USGSDEM | [USGS Optional ASCII DEM (and CDED)](https://gdal.org/frmt_usgsdem.html) | dem | -| VRT | [Virtual Raster](https://gdal.org/gdal_vrttut.html) | raster.vrt | -| XPM | [X11 PixMap Format](https://gdal.org/frmt_various.html#XPM) | xpm | -| XYZ | [ASCII Gridded XYZ](https://gdal.org/frmt_xyz.html) | xyz | -| ZMap | [ZMap Plus Grid](https://gdal.org/frmt_various.html#ZMap) | raster.dat | +| L1B | [NOAA Polar Orbiter Level 1b Data Set](https://gdal.org/drivers/raster/l1b.html) | | +| LAN | [Erdas .LAN/.GIS](https://gdal.org/drivers/raster/lan.html) | | +| LCP | [FARSITE v.4 Landscape File (.lcp)](https://gdal.org/drivers/raster/lcp.html) | lcp | +| LOSLAS | NADCON .los/.las Datum Grid Shift | | +| Leveller | [Leveller heightfield](https://gdal.org/drivers/raster/leveller.html) | ter | +| MAP | [OziExplorer .MAP](https://gdal.org/drivers/raster/map.html) | | +| MBTiles | [MBTiles](https://gdal.org/drivers/raster/mbtiles.html) | mbtiles | +| MFF | [Vexcel MFF Raster](https://gdal.org/drivers/raster/mff.html) | hdr | +| MFF2 | [Vexcel MFF2 (HKV) Raster](https://gdal.org/drivers/raster/mff2.html) | | +| MRF | [Meta Raster Format](https://gdal.org/drivers/raster/marfa.html) | mrf | +| MSGN | [EUMETSAT Archive native (.nat)](https://gdal.org/drivers/raster/msgn.html) | nat | +| NDF | [NLAPS Data Format](https://gdal.org/drivers/raster/ndf.html) | | +| NGSGEOID | [NOAA NGS Geoid Height Grids](https://gdal.org/drivers/raster/ngsgeoid.html) | bin | +| NGW | [NextGIS Web](https://gdal.org/drv_ngw.html) | | +| NITF | [National Imagery Transmission Format](https://gdal.org/drivers/raster/nitf.html) | ntf | +| NTv1 | NTv1 Datum Grid Shift | dat | +| NTv2 | NTv2 Datum Grid Shift | gsb gvb | +| NWT_GRC | [Northwood Classified Grid Format .grc/.tab](https://gdal.org/drivers/raster/nwtgrd.html#driver-capabilities-nwt-grc) | grc | +| NWT_GRD | [Northwood Numeric Grid Format .grd/.tab](https://gdal.org/drivers/raster/nwtgrd.html) | grd | +| OZI | [OziExplorer Image File](https://gdal.org/drivers/raster/ozi.html) | | +| PAux | [PCI .aux Labelled](https://gdal.org/drivers/raster/paux.html) | | +| PCIDSK | [PCIDSK Database File](https://gdal.org/drivers/raster/pcidsk.html) | pix | +| PDF | [Geospatial PDF](https://gdal.org/drivers/raster/pdf.html) | pdf | +| PDS | [NASA Planetary Data System](https://gdal.org/drivers/raster/pds.html) | | +| PDS4 | [NASA Planetary Data System 4](https://gdal.org/drivers/raster/pds4.html) | xml | +| PLMOSAIC | [Planet Labs Mosaics API](https://gdal.org/drivers/raster/plmosaic.html) | | +| PLSCENES | [Planet Labs Scenes API](https://gdal.org/drv_plscenes.html) | | +| PNG | [Portable Network Graphics](https://gdal.org/drivers/raster/png.html) | raster.png | +| PNM | [Portable Pixmap Format (netpbm)](https://gdal.org/drivers/raster/pnm.html) | pgm ppm pnm | +| PRF | [Racurs PHOTOMOD PRF](https://gdal.org/drivers/raster/prf.html) | prf | +| R | [R Object Data Store](https://gdal.org/drivers/raster/r.html) | rda | +| RDA | [DigitalGlobe Raster Data Access driver](https://gdal.org/drivers/raster/rda.html) | dgrda | +| RIK | [Swedish Grid RIK (.rik)](https://gdal.org/drivers/raster/rik.html) | rik | +| RMF | [Raster Matrix Format](https://gdal.org/drivers/raster/rmf.html) | rsw | +| ROI_PAC | [ROI_PAC raster](https://gdal.org/drivers/raster/roi_pac.html) | | +| RPFTOC | [Raster Product Format TOC format](https://gdal.org/drivers/raster/rpftoc.html) | toc | +| RRASTER | [R Raster](https://gdal.org/drivers/raster/rraster.html) | grd | +| RS2 | [RadarSat 2 XML Product](https://gdal.org/drivers/raster/rs2.html) | | +| RST | [Idrisi Raster A.1](https://gdal.org/drivers/raster/Idrisi.html) | rst | +| Rasterlite | [Rasterlite](https://gdal.org/drivers/raster/rasterlite.html) | sqlite | +| SAFE | [Sentinel-1 SAR SAFE Product](https://gdal.org/drivers/raster/safe.html) | | +| SAGA | [SAGA GIS Binary Grid (.sdat, .sg-grd-z)](https://gdal.org/drivers/raster/sdat.html) | sdat sg-grd-z | +| SAR_CEOS | [CEOS SAR Image](https://gdal.org/drivers/raster/sar_ceos.html) | | +| SDTS | [SDTS Raster](https://gdal.org/drivers/raster/sdts.html) | ddf | +| SENTINEL2 | [Sentinel 2](https://gdal.org/drivers/raster/sentinel2.html) | | +| SGI | [SGI Image File Format 1.0](https://gdal.org/drivers/raster/sgi.html) | rgb | +| SIGDEM | [Scaled Integer Gridded DEM .sigdem](https://gdal.org/drivers/raster/sigdem.html) | sigdem | +| SNODAS | [Snow Data Assimilation System](https://gdal.org/drivers/raster/snodas.html) | hdr | +| SRP | [Standard Raster Product (ASRP/USRP)](https://gdal.org/drivers/raster/srp.html) | img | +| SRTMHGT | [SRTMHGT File Format](https://gdal.org/drivers/raster/srtmhgt.html) | hgt | +| TIL | [EarthWatch .TIL](https://gdal.org/drivers/raster/til.html) | | +| TSX | [TerraSAR-X Product](https://gdal.org/drivers/raster/tsx.html) | | +| Terragen | [Terragen heightfield](https://gdal.org/drivers/raster/terragen.html) | ter | +| USGSDEM | [USGS Optional ASCII DEM (and CDED)](https://gdal.org/drivers/raster/usgsdem.html) | dem | +| VICAR | [MIPL VICAR file](https://gdal.org/drivers/raster/vicar.html) | | +| VRT | [Virtual Raster](https://gdal.org/drivers/raster/vrt.html) | vrt | +| WCS | [OGC Web Coverage Service](https://gdal.org/drivers/raster/wcs.html) | | +| WEBP | [WEBP](https://gdal.org/drivers/raster/webp.html) | webp | +| WMS | [OGC Web Map Service](https://gdal.org/drivers/raster/wms.html) | | +| WMTS | [OGC Web Map Tile Service](https://gdal.org/drivers/raster/wmts.html) | | +| XPM | [X11 PixMap Format](https://gdal.org/drivers/raster/xpm.html) | xpm | +| XYZ | [ASCII Gridded XYZ](https://gdal.org/drivers/raster/xyz.html) | xyz | +| ZMap | [ZMap Plus Grid](https://gdal.org/drivers/raster/zmap.html) | dat | {: .no_spell_check } ### Vector import drivers | Driver | Long name | Extensions | |--------|------------|------------| +| ARCGEN | [Arc/Info Generate](https://gdal.org/drv_arcgen.html) | | +| AVCBin | [Arc/Info Binary Coverage](https://gdal.org/drv_avcbin.html) | | | AVCE00 | [Arc/Info E00 (ASCII) Coverage](https://gdal.org/drv_avce00.html) | e00 | +| AeronavFAA | [Aeronav FAA](https://gdal.org/drv_aeronavfaa.html) | | +| AmigoCloud | [AmigoCloud](https://gdal.org/drv_amigocloud.html) | | | BNA | [Atlas BNA](https://gdal.org/drv_bna.html) | bna | | CAD | [AutoCAD Driver](https://gdal.org/drv_cad.html) | dwg | | CSV | [Comma Separated Value (.csv)](https://gdal.org/drv_csv.html) | csv | +| CSW | [OGC CSW (Catalog Service for the Web)](https://gdal.org/drv_csw.html) | | +| Carto | [Carto](https://gdal.org/drv_carto.html) | | +| Cloudant | [Cloudant / CouchDB](https://gdal.org/drv_cloudant.html) | | +| CouchDB | [CouchDB / GeoCouch](https://gdal.org/drv_couchdb.html) | | | DGN | [Microstation DGN](https://gdal.org/drv_dgn.html) | dgn | | DXF | [AutoCAD DXF](https://gdal.org/drv_dxf.html) | dxf | | EDIGEO | [French EDIGEO exchange format](https://gdal.org/drv_edigeo.html) | thf | -| ESRI Shapefile | [ESRI Shapefile](https://gdal.org/drv_shape.html) | shp dbf | +| EEDA | [Earth Engine Data API](https://gdal.org/drivers/vector/eeda.html) | | +| ESRI Shapefile | [ESRI Shapefile](https://gdal.org/drv_shape.html) | shp dbf shz shp.zip | | ESRIJSON | [ESRIJSON](https://gdal.org/drv_esrijson.html) | json | +| Elasticsearch | [Elastic Search](https://gdal.org/drv_elasticsearch.html) | | +| FlatGeobuf | [FlatGeobuf](https://gdal.org/drivers/vector/flatgeobuf.html) | fgb | | GML | [Geography Markup Language (GML)](https://gdal.org/drv_gml.html) | gml xml | | GPKG | [GeoPackage](https://gdal.org/drv_geopackage.html) | gpkg | +| GPSBabel | [GPSBabel](https://gdal.org/drv_gpsbabel.html) | | | GPSTrackMaker | [GPSTrackMaker](https://gdal.org/drv_gtm.html) | gtm gtz | | GPX | [GPX](https://gdal.org/drv_gpx.html) | gpx | | GeoJSON | [GeoJSON](https://gdal.org/drv_geojson.html) | json geojson | | GeoJSONSeq | [GeoJSON Sequence](https://gdal.org/drv_geojsonseq.html) | geojsonl geojsons | +| GeoRSS | [GeoRSS](https://gdal.org/drv_georss.html) | | | Geoconcept | Geoconcept | gxt txt | +| HTF | [Hydrographic Transfer Vector](https://gdal.org/drv_htf.html) | | | Idrisi | Idrisi Vector (.vct) | vct | | JML | [OpenJUMP JML](https://gdal.org/drv_jml.html) | jml | -| JP2OpenJPEG | [JPEG-2000 driver based on OpenJPEG library](https://gdal.org/frmt_jp2openjpeg.html) | vector.jp2 vector.j2k | +| JP2OpenJPEG | [JPEG-2000 driver based on OpenJPEG library](https://gdal.org/drivers/raster/jp2openjpeg.html) | jp2 j2k | | KML | [Keyhole Markup Language (KML)](https://gdal.org/drv_kml.html) | kml | -| MBTiles | [MBTiles](https://gdal.org/frmt_mbtiles.html) | vector.mbtiles | +| MBTiles | [MBTiles](https://gdal.org/drivers/raster/mbtiles.html) | mbtiles | | MVT | [Mapbox Vector Tiles](https://gdal.org/drv_mvt.html) | mvt mvt.gz pbf | | MapInfo File | [MapInfo File](https://gdal.org/drv_mitab.html) | tab mif mid | +| MapML | [MapML](https://gdal.org/drivers/vector/mapml.html) | | +| NGW | [NextGIS Web](https://gdal.org/drv_ngw.html) | | +| OAPIF | [OGC API - Features](https://gdal.org/drivers/vector/oapif.html) | | | ODS | [Open Document/ LibreOffice / OpenOffice Spreadsheet ](https://gdal.org/drv_ods.html) | ods | | OGR_GMT | [GMT ASCII Vectors (.gmt)](https://gdal.org/drv_gmt.html) | gmt | +| OGR_PDS | [Planetary Data Systems TABLE](https://gdal.org/drv_pds.html) | | +| OGR_SDTS | [SDTS](https://gdal.org/drv_sdts.html) | | | OGR_VRT | [VRT - Virtual Datasource](https://gdal.org/drv_vrt.html) | vrt | | OSM | [OpenStreetMap XML and PBF](https://gdal.org/drv_osm.html) | osm pbf | +| OpenAir | [OpenAir](https://gdal.org/drv_openair.html) | | | OpenFileGDB | [ESRI FileGDB](https://gdal.org/drv_openfilegdb.html) | gdb | -| PCIDSK | [PCIDSK Database File](https://gdal.org/frmt_pcidsk.html) | vector.pix | -| PDS4 | [NASA Planetary Data System 4](https://gdal.org/frmt_pds4.html) | xml | +| PCIDSK | [PCIDSK Database File](https://gdal.org/drivers/raster/pcidsk.html) | pix | +| PDF | [Geospatial PDF](https://gdal.org/drivers/raster/pdf.html) | pdf | +| PDS4 | [NASA Planetary Data System 4](https://gdal.org/drivers/raster/pds4.html) | xml | +| PLSCENES | [Planet Labs Scenes API](https://gdal.org/drv_plscenes.html) | | | REC | EPIInfo .REC | rec | | S57 | [IHO S-57 (ENC)](https://gdal.org/drv_s57.html) | 000 | +| SEGUKOOA | [SEG-P1 / UKOOA P1/90](https://gdal.org/drv_segukooa.html) | | +| SEGY | [SEG-Y](https://gdal.org/drv_segy.html) | | | SQLite | [SQLite / Spatialite](https://gdal.org/drv_sqlite.html) | sqlite db | +| SUA | [Tim Newport-Peace's Special Use Airspace Format](https://gdal.org/drv_sua.html) | | | SVG | [Scalable Vector Graphics](https://gdal.org/drv_svg.html) | svg | | SXF | [Storage and eXchange Format](https://gdal.org/drv_sxf.html) | sxf | +| Selafin | [Selafin](https://gdal.org/drv_selafin.html) | | +| TIGER | [U.S. Census TIGER/Line](https://gdal.org/drv_tiger.html) | | | TopoJSON | [TopoJSON](https://gdal.org/drv_topojson.html) | json topojson | +| UK .NTF | [UK .NTF](https://gdal.org/drv_ntf.html) | | | VDV | [VDV-451/VDV-452/INTREST Data Format](https://gdal.org/drv_vdv.html) | txt x10 | | VFK | [Czech Cadastral Exchange Data Format](https://gdal.org/drv_vfk.html) | vfk | +| VICAR | [MIPL VICAR file](https://gdal.org/drivers/raster/vicar.html) | | | WAsP | [WAsP .map format](https://gdal.org/drv_wasp.html) | map | +| WFS | [OGC WFS (Web Feature Service)](https://gdal.org/drv_wfs.html) | | | XLSX | [MS Office Open XML spreadsheet](https://gdal.org/drv_xlsx.html) | xlsx xlsm | | XPlane | [X-Plane/Flightgear aeronautical data](https://gdal.org/drv_xplane.html) | dat | {: .no_spell_check } @@ -171,31 +254,44 @@ depending on the configuration of the distribution's GDAL package. | Driver | Long name | Extensions | |--------|------------|------------| +| AmigoCloud | [AmigoCloud](https://gdal.org/drv_amigocloud.html) | | | BNA | [Atlas BNA](https://gdal.org/drv_bna.html) | bna | | CSV | [Comma Separated Value (.csv)](https://gdal.org/drv_csv.html) | csv | +| Carto | [Carto](https://gdal.org/drv_carto.html) | | +| Cloudant | [Cloudant / CouchDB](https://gdal.org/drv_cloudant.html) | | +| CouchDB | [CouchDB / GeoCouch](https://gdal.org/drv_couchdb.html) | | | DGN | [Microstation DGN](https://gdal.org/drv_dgn.html) | dgn | | DXF | [AutoCAD DXF](https://gdal.org/drv_dxf.html) | dxf | -| ESRI Shapefile | [ESRI Shapefile](https://gdal.org/drv_shape.html) | shp dbf | +| ESRI Shapefile | [ESRI Shapefile](https://gdal.org/drv_shape.html) | shp dbf shz shp.zip | +| Elasticsearch | [Elastic Search](https://gdal.org/drv_elasticsearch.html) | | +| FlatGeobuf | [FlatGeobuf](https://gdal.org/drivers/vector/flatgeobuf.html) | fgb | | GML | [Geography Markup Language (GML)](https://gdal.org/drv_gml.html) | gml xml | | GPKG | [GeoPackage](https://gdal.org/drv_geopackage.html) | gpkg | +| GPSBabel | [GPSBabel](https://gdal.org/drv_gpsbabel.html) | | | GPSTrackMaker | [GPSTrackMaker](https://gdal.org/drv_gtm.html) | gtm gtz | | GPX | [GPX](https://gdal.org/drv_gpx.html) | gpx | | GeoJSON | [GeoJSON](https://gdal.org/drv_geojson.html) | json geojson | | GeoJSONSeq | [GeoJSON Sequence](https://gdal.org/drv_geojsonseq.html) | geojsonl geojsons | +| GeoRSS | [GeoRSS](https://gdal.org/drv_georss.html) | | | Geoconcept | Geoconcept | gxt txt | | JML | [OpenJUMP JML](https://gdal.org/drv_jml.html) | jml | | KML | [Keyhole Markup Language (KML)](https://gdal.org/drv_kml.html) | kml | -| MBTiles | [MBTiles](https://gdal.org/frmt_mbtiles.html) | mbtiles | +| MBTiles | [MBTiles](https://gdal.org/drivers/raster/mbtiles.html) | mbtiles | | MapInfo File | [MapInfo File](https://gdal.org/drv_mitab.html) | tab mif mid | +| MapML | [MapML](https://gdal.org/drivers/vector/mapml.html) | | +| NGW | [NextGIS Web](https://gdal.org/drv_ngw.html) | | | ODS | [Open Document/ LibreOffice / OpenOffice Spreadsheet ](https://gdal.org/drv_ods.html) | ods | | OGR_GMT | [GMT ASCII Vectors (.gmt)](https://gdal.org/drv_gmt.html) | gmt | -| PCIDSK | [PCIDSK Database File](https://gdal.org/frmt_pcidsk.html) | pix | -| PDF | [Geospatial PDF](https://gdal.org/frmt_pdf.html) | pdf | -| PDS4 | [NASA Planetary Data System 4](https://gdal.org/frmt_pds4.html) | xml | +| PCIDSK | [PCIDSK Database File](https://gdal.org/drivers/raster/pcidsk.html) | pix | +| PDF | [Geospatial PDF](https://gdal.org/drivers/raster/pdf.html) | pdf | +| PDS4 | [NASA Planetary Data System 4](https://gdal.org/drivers/raster/pds4.html) | xml | | PGDUMP | [PostgreSQL SQL dump](https://gdal.org/drv_pgdump.html) | sql | | S57 | [IHO S-57 (ENC)](https://gdal.org/drv_s57.html) | 000 | | SQLite | [SQLite / Spatialite](https://gdal.org/drv_sqlite.html) | sqlite db | +| Selafin | [Selafin](https://gdal.org/drv_selafin.html) | | +| TIGER | [U.S. Census TIGER/Line](https://gdal.org/drv_tiger.html) | | | VDV | [VDV-451/VDV-452/INTREST Data Format](https://gdal.org/drv_vdv.html) | txt x10 | +| VICAR | [MIPL VICAR file](https://gdal.org/drivers/raster/vicar.html) | | | WAsP | [WAsP .map format](https://gdal.org/drv_wasp.html) | map | | XLSX | [MS Office Open XML spreadsheet](https://gdal.org/drv_xlsx.html) | xlsx xlsm | {: .no_spell_check } diff --git a/doc/manual/pages/georeferencing.md b/doc/manual/pages/georeferencing.md index 08ca26d0f..95a3d98b0 100644 --- a/doc/manual/pages/georeferencing.md +++ b/doc/manual/pages/georeferencing.md @@ -20,7 +20,7 @@ Georeferencing of a map is the best way for aligning templates (such as base map Georeferencing properties are set in a dialog which is available from the menu **Map > Georeferencing...**. The dialog is divided in four sections: Map coordinate reference system, Reference point, Map north, and Scale compensation. -![Georeferencing dialog](images/georeferencing.png) +![Georeferencing dialog](images/georeferencing_dialog.png) #### Map coordinate reference system diff --git a/doc/manual/pages/images/georeferencing.png b/doc/manual/pages/images/georeferencing_dialog.png similarity index 100% rename from doc/manual/pages/images/georeferencing.png rename to doc/manual/pages/images/georeferencing_dialog.png diff --git a/doc/manual/pages/images/Android_UI_explanation.png b/doc/manual/pages/images/touch-mode-main-window.png similarity index 100% rename from doc/manual/pages/images/Android_UI_explanation.png rename to doc/manual/pages/images/touch-mode-main-window.png diff --git a/doc/manual/pages/index.md b/doc/manual/pages/index.md index 1c11b2614..4c3139f30 100644 --- a/doc/manual/pages/index.md +++ b/doc/manual/pages/index.md @@ -60,6 +60,9 @@ Adjusting the program to your preferences. [Course design](course_design.md){: .subpage} Using the course design symbol set. +[The Touch Mode User Interface](touch-mode.md){: .subpage} +Using the Mapper app in "Touch Mode" + [The Mapper app for Android](android-app.md){: .subpage} Working with the Android version of Mapper. diff --git a/doc/manual/pages/touch-mode.md b/doc/manual/pages/touch-mode.md new file mode 100644 index 000000000..318b88cc6 --- /dev/null +++ b/doc/manual/pages/touch-mode.md @@ -0,0 +1,163 @@ +--- +title: The Touch Mode User Interface +authors: + - Kai Pastor + - Thomas Schoeps +keywords: Touch Mode, Android +edited: 20 September 2020 +--- + +## Using the Mapper app in "Touch Mode" + +The "Touch Mode" is an alternative user interface which is always active on +Android devices. It can be activated on PCs, too. + +In Touch Mode, the map editor is different from the desktop version. It offers +a fixed set of buttons add the top and at the bottom of the screen. Not all +Mapper features are offered in Touch Mode, but on the other hands some features +such as live GNSS tracking are available only in this mode. + +This page focuses on specific features of the touch mode user interface. +To find out about the other symbols, see the [Toolbars documentation](toolbars.md). + +![ ](images/touch-mode-main-window.png) + + +#### ![ ](../mapper-images/arrow-thin-upleft.png) Hide the top bar + +Using this button, you may hide the top bar, leaving more screen for the map. A similar button will remain visible for restoring the top bar again. + + +#### ![ ](../mapper-images/compass.png) Compass display toggle + +When this item is activated, a compass is shown in the top-left corner of the screen. The red line indicates the north direction, while the black-white lines show the up direction of your device. If both are aligned, the device is aligned with magnetic north. When the needle is close to the north direction, a green circle appears which quickly shows how well the device is aligned: the larger it is, the better the alignment is. The compass works three-dimensionally, so you do not need to hold the device in a flat pose for it to work. + +Note: the usefulness of the compass depends on the presence of a gyroscope, as mentioned in the device recommendations section. + +Attention: the compass is very sensitive to magnetic materials. Android tries the eliminate the influence of local magnetic fields, but this requires calibration. To do this, move the device around in the shape of an 8 while rotating it and while the compass is active. If the local influence changes, re-calibration is required. + + +#### ![ ](../mapper-images/tool-gps-display.png) Location display toggle + +If the currently active map work has valid georeferencing, this control will be enabled and will show a dot at your current position as obtained from Global Navigation Satellite Systems (GNSS, e.g. GPS) or other positioning services. In addition, in case an accuracy estimate (HDOP) is available, this shows a circle around the dot which indicates the positioning accuracy. The chance of your real position being in the circle is approximately 70%. + +While this is active, your track is automatically recorded into a GPX file in the directory of your map, named " - GPS-.gpx". This file is included as a template in the map. You can simply show it using the template visibility control button to view your track. + +Note that track recording might be interrupted while the screen is off or the app is in the background to save battery. Keep the app active if you want to ensure continuous track recording. + + +#### ![ ](../mapper-images/gps-distance-rings.png) Distance rings toggle + +This button is active if location display is active. When activated, this will display rings in 10 meter and 20 meter distance from the current location which can be used to do rough distance estimates. + + +#### ![ ](../mapper-images/rotate-map.png) Automatic north alignment toggle + +When this toggle is active, the map is automatically rotated to north using the device's internal compass. The update frequency is 1 update per second. This is quite low on purpose to save battery, but you probably still need some replacement batteries when using this mode. While interacting with the map and for another 1.5 seconds after that, the map is not rotated to allow editing without unwanted rotations. If GNSS position display is activated at the same time, a line starting from the GNSS position and going in the direction of the up direction of your device is displayed which shows your viewing direction (assuming you are holding the device upright). + + +----- + +#### ![ ](../mapper-images/map-parts.png) Map parts selector + +This button opens a popup for changing the active map part. + + +#### ![ ](../mapper-images/tool-touch-cursor.png) Touch cursor toggle + +This symbol activates a helper cursor which allows slow, but precise editing with fingers only. The cursor consists of a circular area and a pointer above the circle. + +![ ](images/touch_cursor.png) + +Touching the map anywhere outside the circle moves the pointer to this position. Touching the circle simulates a real touch at the pointer position above the circle. + + +#### ![ ](../mapper-images/templates.png) Template control + +This shows a list of all opened templates. Touching a template toggles its visibility. + + +#### ![ ](../mapper-images/close.png) Close button + +This closes the active map and returns to the map selection screen. + + +#### ![ ](../mapper-images/three-dots.png) Overflow button + +Depending on the screen size of your device, some of the symbols available not fit onto the screen. They are instead placed in a list which is shown on touching the overflow button. If all symbols fit on the screen, this button is unused. + + +----- + +#### ![ ](../mapper-images/view-zoom-out.png) Zoom out / Zoom to fixed + +A short press will zoom out by one step. + +A long press will open a menu where you can jump to a fixed zoom (cf. screenshot). + + +#### ![ ](../mapper-images/move-to-gps.png) Move to location + +When positioning services are enabled, this will move the map so that your current location is in the middle of the screen. + +A long press will open a menu where you can enable a mode which will automatically +move the map so that the current location stay in the central region of the screen. + + +#### ![ ](../mapper-images/gps-temporary-point.png) Mark temporary position + +This records a single point at your current location to act as a drawing help. Note that this marker is not saved in the map. + + +#### ![ ](../mapper-images/gps-temporary-path.png) Record temporary trace + +This symbol is enabled when the location display is active. It records a temporary trace of the location, which is intended to act as a guideline for drawings. Using the trace directly is rarely useful because of the inaccurateness of location services. +Note that the temporary trace is not saved in the map. + + +#### ![ ](../mapper-images/gps-temporary-clear.png) Clear temporary markers + +This removes the temporary traces and position markers. (They will also be discarded when closing the map file.) + + +#### ![ ](../mapper-images/pencil.png) Paint on template + +By this tool, mapper supports free-hand drawing onto raster image templates. +There is a small selection of colors for drawing as well as an eraser tool. +By additional buttons, you can choose to created filled areas instead of lines, +to use a dot pattern instead of a solid fill, or to draw only in the background +(transparent regions). There is also a dedicated undo/redo feature. + +This tool is an convenient alternative when you want to do only (or mostly) +drafting in the field, but draw the final version of the map at home on a PC. +It does not create new map objects but only alters the template you draw on. +This may help to deal with low processing power or battery runtime on mobile +devices. + +A short press may activate painting on the template which was used before if +it is still on the screen. Otherwise, or on long press, there is a menu which +indicates available templates, or offers to create a new template. + +Please note that direct drawing onto regular base map images is *not* recommended: +Color and eraser tool would easily destroy important details. +Templates in JPEG format also should not be used: This is a lossy file format which +will introduce visible artifacts when saving the file and loading it repeatedly. + + +----- + +#### ![ ](../mapper-images/draw-freehand.png) Free-hand draw tool + +Allows to draw paths free-handedly. + + +#### ![ ](../mapper-images/draw-point-gps.png) Draw at location tool + +Sets a point object at your current location. Selecting this button first enters an averaging mode. While it is active, the input from the location service is averaged to get a more accurate estimate. Touch the map display anywhere to finish the averaging. + + +#### Symbol selector + +Displays the active symbol, and opens the symbol selection screen on short press. + +A long press of the symbol selector opens a popup menu which allows to read the symbol name and description, and to toggle the visibility and the protection state of the symbol. diff --git a/doc/manual/preprocess-markdown-html.cmake.in b/doc/manual/preprocess-markdown-html.cmake.in index 9115d94ee..2f3b7ba56 100644 --- a/doc/manual/preprocess-markdown-html.cmake.in +++ b/doc/manual/preprocess-markdown-html.cmake.in @@ -84,6 +84,9 @@ foreach(file ${input_files}) if(NOT title) message(FATAL_ERROR "${file}:1: No title in frontmatter") endif() + # Don't duplicate the implicit 'index' label. Using 'mainpage' has the same + # effect: doxygen puts the documentation on the front page (index.html). + string(REGEX REPLACE "^index$" "mainpage" pagename_safe "${pagename_safe}") set(output "${title} {#${pagename_safe}}\n===\n\n${output}") get_yaml_field(subpages ${frontmatter} "subpages") @@ -103,7 +106,7 @@ foreach(file ${input_files}) get_yaml_field(edited ${frontmatter} "edited") if(edited) - set(output "${output}\n\n---\nUpdate on ${edited}") + set(output "${output}\n\n---\nUpdated on ${edited}") else() list(APPEND file_remarks "missing field 'edited'") endif() @@ -147,6 +150,7 @@ foreach(file ${input_files}) # Liquid/Kramdown/Markdown markup string(REGEX REPLACE "\r?\n *[-*] *[^\r\n]*\r?\n{:toc}" "\\\\tableofcontents" output "${output}") + string(REGEX REPLACE "(\\[[^]\"]*)\\\"([^]\"]*)\\\"([^[)\"]*\\){: \\.subpage})" "\\1\\\\\"\\2\\\\\"\\3" output "${output}") string(REGEX REPLACE "\\[([^]]*)\\]\\(([^\)]*).md\\){: \\.subpage}" "\\\\subpage \\2 \"\\1\"" output "${output}") string(REGEX REPLACE "(\\[[^]]*\\]\\([^\)]*)\\.md(\\)|#)" "\\1.html\\2" output "${output}") string(REGEX REPLACE "(href=\"[^\"]*)\\.md(\"|#)" "\\1.html\\2" output "${output}") diff --git a/doc/manual/preprocess-markdown-pdflatex.cmake.in b/doc/manual/preprocess-markdown-pdflatex.cmake.in index b1f076e87..42a724bcf 100644 --- a/doc/manual/preprocess-markdown-pdflatex.cmake.in +++ b/doc/manual/preprocess-markdown-pdflatex.cmake.in @@ -172,6 +172,7 @@ foreach(file ${input_files}) if(subpages_${pagename_safe}) string(REGEX REPLACE "\\[[^]]*\\]\\(([^\)]*).md\\){: \\.n?o?subpage}" "\\1" "subpages_${pagename_safe}" "${subpages_${pagename_safe}}") endif() + string(REGEX REPLACE "(\\[[^]\"]*)\\\"([^]\"]*)\\\"([^[)\"]*\\){: \\.n?o?subpage})" "\\1\\\\\"\\2\\\\\"\\3" output "${output}") string(REGEX REPLACE "\\[([^]]*)\\]\\(([^\)]*)\\.md\\){: \\.n?o?subpage}" "\\\\subpage \\2 \"\\1\"" output "${output}") string(REGEX REPLACE "(\\[[^]]*\\])\\(#([^)]*)\\)" "\\1(${pagename_safe}.md#\\2)" output "${output}") string(REGEX REPLACE "(\\[[^]]*\\])\\(([^\)#]*)\\.md#([^\)]*)\\)" "\\1(#\\2_\\3)" output "${output}") diff --git a/images/scribble-fill-shapes.png b/images/scribble-fill-shapes.png new file mode 100644 index 000000000..c0a57681d Binary files /dev/null and b/images/scribble-fill-shapes.png differ diff --git a/images/svg/scribble-fill-shapes.svg b/images/svg/scribble-fill-shapes.svg new file mode 100644 index 000000000..5badc08e4 --- /dev/null +++ b/images/svg/scribble-fill-shapes.svg @@ -0,0 +1,261 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packaging/android/AndroidManifest.xml.in b/packaging/android/AndroidManifest.xml.in index ab1ec5675..c8b8842a3 100644 --- a/packaging/android/AndroidManifest.xml.in +++ b/packaging/android/AndroidManifest.xml.in @@ -43,12 +43,13 @@ - - + $<$:> + $<$:> + $<$:> @@ -90,7 +91,7 @@ android:exported="false" /> - + $<$:> + + + + + + + + + + + + + + + + + + + diff --git a/test/file_format_t.cpp b/test/file_format_t.cpp index d7e6856b2..4b03b0ff2 100644 --- a/test/file_format_t.cpp +++ b/test/file_format_t.cpp @@ -1,6 +1,6 @@ /* * Copyright 2012, 2013 Thomas Schöps - * Copyright 2012-2019 Kai Pastor + * Copyright 2012-2020 Kai Pastor * * This file is part of OpenOrienteering. * @@ -32,11 +32,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -60,6 +62,7 @@ #include "core/map_printer.h" #include "core/objects/object.h" #include "core/objects/text_object.h" +#include "core/symbols/area_symbol.h" #include "core/symbols/symbol.h" #include "fileformats/file_format.h" #include "fileformats/file_format_registry.h" @@ -128,6 +131,49 @@ namespace QTest namespace { + /** + * Provides QCOMPARE-style symbol property comparison. + * + * This macro reports the symbol, but avoids expensive string operations + * when the properties match. + */ + #define COMPARE_SYMBOL_PROPERTY(a, b, symbol) \ + if ((a) != (b)) \ + { \ + auto const diff = qstrlen(#b) - qstrlen(#a); \ + auto const fill_a = QString().fill(QChar::Space, +diff); \ + auto const fill_b = QString().fill(QChar::Space, -diff); \ + QFAIL(QString::fromLatin1( \ + "Compared values are not the same (%1 %2)\n Actual (%3)%4: %7\n Expected (%5)%6: %8") \ + .arg((symbol).getNumberAsString(), (symbol).getPlainTextName(), \ + QString::fromUtf8(#a), fill_a, \ + QString::fromUtf8(#b), fill_b) \ + .arg(a).arg(b) \ + .toUtf8()); \ + } \ + else \ + { \ + QVERIFY(true); /* for QEXPECT_FAIL etc. */ \ + } + + /** + * Provides QVERIFY-style symbol property verification. + * + * This macro reports the symbol, but avoids expensive string operations + * when the properties match. + */ + #define VERIFY_SYMBOL_PROPERTY(cond, symbol) \ + if (cond) \ + { \ + QVERIFY2(cond, QByteArray((symbol).getNumberAsString().toUtf8() + ' ' \ + + (symbol).getPlainTextName().toUtf8())); \ + } \ + else \ + { \ + QVERIFY(true); /* for QEXPECT_FAIL etc. */ \ + } + + void comparePrinterConfig(const MapPrinterConfig& copy, const MapPrinterConfig& orig) { QCOMPARE(copy.center_print_area, orig.center_print_area); @@ -140,6 +186,14 @@ namespace QCOMPARE(copy.single_page_print_area, orig.single_page_print_area); } + void compareSymbol(const Symbol& actual, const Symbol& expected) + { + COMPARE_SYMBOL_PROPERTY(actual.isHidden(), expected.isHidden(), expected); + COMPARE_SYMBOL_PROPERTY(actual.isProtected(), expected.isProtected(), expected); + VERIFY_SYMBOL_PROPERTY(actual.stateEquals(&expected), expected); + VERIFY_SYMBOL_PROPERTY(actual.equals(&expected, Qt::CaseInsensitive), expected); + } + void compareMaps(const Map& actual, const Map& expected) { // TODO: This does not compare everything - yet ... @@ -186,11 +240,7 @@ namespace } else for (int i = 0; i < actual.getNumSymbols(); ++i) { - if (!actual.getSymbol(i)->equals(expected.getSymbol(i), Qt::CaseSensitive)) - qDebug("%s vs %s", qPrintable(actual.getSymbol(i)->getNumberAsString()), - qPrintable(expected.getSymbol(i)->getNumberAsString())); - QVERIFY(actual.getSymbol(i)->equals(expected.getSymbol(i), Qt::CaseSensitive)); - QVERIFY(actual.getSymbol(i)->stateEquals(expected.getSymbol(i))); + compareSymbol(*actual.getSymbol(i), *expected.getSymbol(i)); } // Parts and objects @@ -256,6 +306,100 @@ namespace } + void fuzzyCompareSymbol(const AreaSymbol& actual, const AreaSymbol& expected) + { + auto pattern_rotable = false; + for (auto i = 0; i < expected.getNumFillPatterns(); ++i) + pattern_rotable |= expected.getFillPattern(i).rotatable(); + for (auto i = 0; i < actual.getNumFillPatterns(); ++i) + COMPARE_SYMBOL_PROPERTY(actual.getFillPattern(i).rotatable(), pattern_rotable, expected); + } + + void fuzzyCompareSymbol(const Symbol& actual, const Symbol& expected, const QByteArray& format_id) + { + COMPARE_SYMBOL_PROPERTY(actual.isHidden(), expected.isHidden(), expected); + COMPARE_SYMBOL_PROPERTY(actual.isProtected(), expected.isProtected(), expected); + + if (format_id == "OCD-legacy") + return; // Unmaintained, no other properties testedd. + + COMPARE_SYMBOL_PROPERTY(actual.isRotatable(), expected.isRotatable(), expected); + + /// \todo Ideally, the dominant color would be preserved. +#ifdef EXPORT_PRESERVES_DOMINANT_COLOR + COMPARE_SYMBOL_PROPERTY(actual.guessDominantColor()->getName(), expected.guessDominantColor()->getName(), expected); +#endif + if (actual.getType() != expected.getType()) + return; // The following tests assume the same type. + + switch (actual.getType()) + { + case Symbol::Area: + fuzzyCompareSymbol(static_cast(actual), static_cast(expected)); + break; + + default: + ; /// \todo Extend fuzzy testing + } + + } + + void fuzzyMatchSymbol(const Map& map, const QByteArray& format_id, const Symbol* expected) + { + Symbol const* actual = nullptr; + for (auto i = 0; !actual && i < map.getNumSymbols(); ++i) + { + auto const* symbol = map.getSymbol(i); + if (symbol->getType() != expected->getType() + && symbol->getType() != Symbol::Combined) + { + continue; + } + + if (format_id.startsWith("OCD")) + { + // In OCD format, export may have incremented second and or first component. + if (symbol->getNumberComponent(0) < expected->getNumberComponent(0) + || symbol->getNumberComponent(0) > expected->getNumberComponent(0) + 1) + { + continue; + } + } + else if (expected->getNumberAsString() != symbol->getNumberAsString()) + { + continue; + } + + if (!expected->getPlainTextName().startsWith(symbol->getPlainTextName())) + continue; + + actual = symbol; + } + if (format_id == "OCD8" || format_id == "OCD-legacy") + { + // Don't elaborate testing for these legacy formats. + switch (expected->getType()) + { + case Symbol::Combined: + // Expected symbol may be entirely dropped. (Its parts live independently.) + if (!actual) + return; + break; + case Symbol::Line: + // Expected symbol may be entirely turned into combined symbol. + if (!actual && format_id == "OCD-legacy") + return; + break; + default: + ; // nothing + } + } + if (actual) + fuzzyCompareSymbol(*actual, *expected, format_id); + else + QFAIL(qPrintable(QString::fromLatin1("Missing symbol: %1 %2").arg(expected->getNumberAsString(), expected->getPlainTextName()))); + } + /** * Compares map features in a way that works for lossy exporters and importers. * @@ -266,7 +410,7 @@ namespace * A lossy importer might skip some elements, but it is fair to require that * it imports everything which is exported by the corresponding exporter. */ - void fuzzyCompareMaps(const Map& actual, const Map& expected) + void fuzzyCompareMaps(const Map& actual, const Map& expected, const QByteArray& format_id) { // Miscellaneous QCOMPARE(actual.getScaleDenominator(), expected.getScaleDenominator()); @@ -301,9 +445,14 @@ namespace QVERIFY2(actual.getNumColors() <= 2 * expected.getNumColors(), qPrintable(test_label.arg(actual.getNumColors()).arg(expected.getNumColors()))); // Symbols - // Combined symbols may be dropped on export + // Combined symbols may be dropped (split) on export. + // Text symbols may be duplicated on export. QVERIFY2(2 * actual.getNumSymbols() >= expected.getNumSymbols(), qPrintable(test_label.arg(actual.getNumSymbols()).arg(expected.getNumSymbols()))); QVERIFY2(actual.getNumSymbols() <= 2 * expected.getNumSymbols(), qPrintable(test_label.arg(actual.getNumSymbols()).arg(expected.getNumSymbols()))); + for (auto i = 0; i < expected.getNumSymbols(); ++i) + { + fuzzyMatchSymbol(actual, format_id, expected.getSymbol(i)); + } // Objects QVERIFY2(actual.getNumObjects() >= expected.getNumObjects(), qPrintable(test_label.arg(actual.getNumObjects()).arg(expected.getNumObjects()))); @@ -654,6 +803,15 @@ void FileFormatTest::saveAndLoad() printer_config.page_format.v_overlap += 4.0; original->setPrinterConfig(printer_config); + // Enforce a unique prefix for symbol names, allowing for reliable + // recognition even when truncated. However, this doesn't help with + // symbols which are duplicated or dropped on export/import. + for (auto i = 0; i < original->getNumSymbols(); ++i) + { + auto* symbol = original->getSymbol(i); + symbol->setName(QLatin1Char('#') + QString::number(i) + QChar::Space + symbol->getPlainTextName()); + } + // Save and load the map auto new_map = saveAndLoadMap(*original, format); QVERIFY2(new_map, "Exception while importing / exporting."); @@ -670,7 +828,7 @@ void FileFormatTest::saveAndLoad() // be independent of information which cannot be exported into this format if (new_map && format->isWritingLossy()) { - fuzzyCompareMaps(*new_map, *original); + fuzzyCompareMaps(*new_map, *original, format_id); original = std::move(new_map); new_map = saveAndLoadMap(*original, format); diff --git a/test/object_query_t.cpp b/test/object_query_t.cpp index c5986575f..d78dcb72b 100644 --- a/test/object_query_t.cpp +++ b/test/object_query_t.cpp @@ -1,6 +1,6 @@ /* * Copyright 2016 Mitchell Krome - * Copyright 2017 Kai Pastor + * Copyright 2017-2020 Kai Pastor * * This file is part of OpenOrienteering. * @@ -29,6 +29,7 @@ #include #include +#include "core/map.h" #include "core/objects/object.h" #include "core/objects/text_object.h" #include "core/objects/object_query.h" @@ -284,6 +285,27 @@ void ObjectQueryTest::testSymbol() operand = clone.symbolOperand(); QVERIFY(operand); QCOMPARE(operand, &symbol_2); + + symbol_query = ObjectQuery(static_cast(nullptr)); + QVERIFY(bool(symbol_query)); + QVERIFY(symbol_query(&object) == false); + object.setSymbol(nullptr, true); + QVERIFY(symbol_query(&object) == true); +} + +void ObjectQueryTest::testNegation() +{ + PointSymbol symbol_1; + PointObject object(&symbol_1); + + // variation of testSymbol + QVERIFY(ObjectQuery::negation({&symbol_1})(&object) == false); + + PointSymbol symbol_2; + QVERIFY(ObjectQuery::negation({&symbol_2})(&object) == true); + + object.setSymbol(&symbol_2, false); + QVERIFY(ObjectQuery::negation({&symbol_2})(&object) == false); } @@ -318,6 +340,34 @@ void ObjectQueryTest::testToString() ObjectQuery::OperatorAnd, {QStringLiteral("A B C"), ObjectQuery::OperatorIsNot, QStringLiteral("3")}); QCOMPARE(q.toString(), QStringLiteral("Layer = \"1\" AND \"A B C\" != \"3\"")); + + q = ObjectQuery(static_cast(nullptr)); + QCOMPARE(q.toString(), QStringLiteral("SYMBOL \"\"")); + + PointSymbol symbol_1; + symbol_1.setNumberComponent(0, 123); + q = ObjectQuery(static_cast(&symbol_1)); + QCOMPARE(q.toString(), QStringLiteral("SYMBOL \"123\"")); + + q = ObjectQuery(ObjectQuery::negation({ObjectQuery::OperatorSearch, QStringLiteral("1")})); + QCOMPARE(q.toString(), QStringLiteral("NOT \"1\"")); + + q = ObjectQuery({ObjectQuery::OperatorSearch, QStringLiteral("1")}, + ObjectQuery::OperatorAnd, + ObjectQuery::negation({ObjectQuery::OperatorSearch, QStringLiteral("2")})); + QCOMPARE(q.toString(), QStringLiteral("\"1\" AND NOT \"2\"")); + + q = ObjectQuery({{ObjectQuery::OperatorSearch, QStringLiteral("1")}, + ObjectQuery::OperatorAnd, + ObjectQuery::negation({ObjectQuery::OperatorSearch, QStringLiteral("2")})}, + ObjectQuery::OperatorOr, + {ObjectQuery::OperatorSearch, QStringLiteral("3")}); + QCOMPARE(q.toString(), QStringLiteral("\"1\" AND NOT \"2\" OR \"3\"")); + + q = ObjectQuery(ObjectQuery::negation(ObjectQuery::negation({ObjectQuery::OperatorSearch, QStringLiteral("1")})), + ObjectQuery::OperatorAnd, + {ObjectQuery::OperatorSearch, QStringLiteral("3")}); + QCOMPARE(q.toString(), QStringLiteral("NOT NOT \"1\" AND \"3\"")); } @@ -386,7 +436,80 @@ void ObjectQueryTest::testParser() q = ObjectQuery(ObjectQuery::OperatorSearch, QStringLiteral("1\"\\x")); QCOMPARE(p.parse(QStringLiteral("\"1\\\"\\\\x\"")), q); + + q = ObjectQuery(static_cast(nullptr)); + QCOMPARE(p.parse(QStringLiteral("SYMBOL \"\"")), q); + + q = ObjectQuery(ObjectQuery::negation({ObjectQuery::OperatorSearch, QStringLiteral("1")})); + QCOMPARE(p.parse(QStringLiteral("NOT \"1\"")), q); + + q = ObjectQuery(ObjectQuery::negation(ObjectQuery::negation({ObjectQuery::OperatorSearch, QStringLiteral("1")}))); + QCOMPARE(p.parse(QStringLiteral("NOT NOT \"1\"")), q); + QCOMPARE(p.parse(QStringLiteral("NOT (NOT \"1\")")), q); + QCOMPARE(p.parse(QStringLiteral("(NOT NOT \"1\")")), q); + QCOMPARE(p.parse(QStringLiteral("(NOT NOT (\"1\"))")), q); + + q = ObjectQuery({ObjectQuery::OperatorSearch, QStringLiteral("1")}, + ObjectQuery::OperatorAnd, + ObjectQuery::negation({ObjectQuery::OperatorSearch, QStringLiteral("2")})); + QCOMPARE(p.parse(QStringLiteral("\"1\" AND NOT \"2\"")), q); + + q = ObjectQuery({{ObjectQuery::OperatorSearch, QStringLiteral("1")}, + ObjectQuery::OperatorAnd, + ObjectQuery::negation({ObjectQuery::OperatorSearch, QStringLiteral("2")})}, + ObjectQuery::OperatorOr, + {ObjectQuery::OperatorSearch, QStringLiteral("3")}); + QCOMPARE(p.parse(QStringLiteral("1 AND NOT 2 OR 3")), q); + QCOMPARE(p.parse(QStringLiteral("1 AND (NOT 2) OR 3")), q); + QCOMPARE(p.parse(QStringLiteral("(1 AND NOT 2) OR 3")), q); + + q = ObjectQuery({ObjectQuery::negation({ObjectQuery::OperatorSearch, QStringLiteral("1")}), + ObjectQuery::OperatorAnd, + {ObjectQuery::OperatorSearch, QStringLiteral("2")}}, + ObjectQuery::OperatorOr, + {ObjectQuery::OperatorSearch, QStringLiteral("3")}); + QCOMPARE(p.parse(QStringLiteral("NOT 1 AND 2 OR 3")), q); + QCOMPARE(p.parse(QStringLiteral("(NOT 1) AND 2 OR 3")), q); + QCOMPARE(p.parse(QStringLiteral("(NOT 1 AND 2) OR 3")), q); + + q = ObjectQuery({{ObjectQuery::OperatorSearch, QStringLiteral("1")}, + ObjectQuery::OperatorAnd, + {ObjectQuery::OperatorSearch, QStringLiteral("2")}}, + ObjectQuery::OperatorOr, + ObjectQuery::negation({ObjectQuery::OperatorSearch, QStringLiteral("3")})); + QCOMPARE(p.parse(QStringLiteral("1 AND 2 OR NOT 3")), q); + QCOMPARE(p.parse(QStringLiteral("1 AND 2 OR (NOT 3)")), q); + QCOMPARE(p.parse(QStringLiteral("(1 AND 2) OR NOT 3")), q); + + q = ObjectQuery(ObjectQuery::negation(ObjectQuery::negation({ObjectQuery::OperatorSearch, QStringLiteral("1")})), + ObjectQuery::OperatorAnd, + {ObjectQuery::OperatorSearch, QStringLiteral("3")}); + QCOMPARE(p.parse(QStringLiteral("NOT NOT \"1\" AND \"3\"")), q); + QCOMPARE(p.parse(QStringLiteral("NOT (NOT \"1\") AND \"3\"")), q); + QCOMPARE(p.parse(QStringLiteral("(NOT NOT \"1\") AND \"3\"")), q); + QCOMPARE(p.parse(QStringLiteral("(NOT (NOT \"1\")) AND \"3\"")), q); + + Map m; + auto* symbol_1 = new PointSymbol(); + symbol_1->setNumberComponent(0, 123); + m.addSymbol(symbol_1, 0); + p.setMap(&m); + + q = ObjectQuery(static_cast(symbol_1)); + QCOMPARE(p.parse(QStringLiteral("SYMBOL 123")), q); + QCOMPARE(p.parse(QStringLiteral("SYMBOL \"123\"")), q); + + q = ObjectQuery(static_cast(nullptr)); + QCOMPARE(p.parse(QStringLiteral("SYMBOL \"\"")), q); +} + + +/* + * We don't need a real GUI window. + */ +namespace { + auto Q_DECL_UNUSED qpa_selected = qputenv("QT_QPA_PLATFORM", "minimal"); // clazy:exclude=non-pod-global-static } -QTEST_APPLESS_MAIN(ObjectQueryTest) +QTEST_MAIN(ObjectQueryTest) diff --git a/test/object_query_t.h b/test/object_query_t.h index 64ba10870..f688ff99e 100644 --- a/test/object_query_t.h +++ b/test/object_query_t.h @@ -1,6 +1,6 @@ /* * Copyright 2016 Mitchell Krome - * Copyright 2017 Kai Pastor + * Copyright 2017-2020 Kai Pastor * * This file is part of OpenOrienteering. * @@ -45,6 +45,7 @@ private slots: void testSearch(); void testObjectText(); void testSymbol(); + void testNegation(); void testToString(); void testParser(); diff --git a/test/path_object_t.cpp b/test/path_object_t.cpp index 94f5b4041..1613a6af1 100644 --- a/test/path_object_t.cpp +++ b/test/path_object_t.cpp @@ -281,8 +281,8 @@ void PathObjectTest::constructorTest() { PathObject expect_from_first_part(red_area.getSymbol(), red_area.getRawCoordinateVector()); expect_from_first_part.deletePart(1); - auto actual = line_from_first_part.parts().front(); - auto expected = expect_from_first_part.parts().front(); + auto const& actual = line_from_first_part.parts().front(); + auto const& expected = expect_from_first_part.parts().front(); QCOMPARE(actual.size(), expected.size()); QCOMPARE(actual.first_index, expected.first_index); QCOMPARE(actual.last_index, expected.last_index); @@ -310,8 +310,8 @@ void PathObjectTest::constructorTest() { PathObject expect_from_last_part(red_area.getSymbol(), red_area.getRawCoordinateVector()); expect_from_last_part.deletePart(0); - auto actual = line_from_last_part.parts().front(); - auto expected = expect_from_last_part.parts().front(); + auto const& actual = line_from_last_part.parts().front(); + auto const& expected = expect_from_last_part.parts().front(); QCOMPARE(actual.size(), expected.size()); QCOMPARE(actual.first_index, expected.first_index); QCOMPARE(actual.last_index, expected.last_index); diff --git a/test/sensors_t.cpp b/test/sensors_t.cpp index 6fb6c9a53..82efc998b 100644 --- a/test/sensors_t.cpp +++ b/test/sensors_t.cpp @@ -38,6 +38,14 @@ using namespace OpenOrienteering; #include #endif +#ifdef MAPPER_USE_FAKE_POSITION_PLUGIN +#include // IWYU pragma: keep +#include // IWYU pragma: keep +#include "sensors/fake_position_plugin.h" +#include "sensors/fake_position_source.h" +Q_IMPORT_PLUGIN(FakePositionPlugin) +#endif + #ifdef MAPPER_USE_NMEA_POSITION_PLUGIN #include // IWYU pragma: keep #include "sensors/nmea_position_plugin.h" @@ -60,6 +68,42 @@ private slots: QDir::addSearchPath(QStringLiteral("testdata"), QDir(QString::fromUtf8(MAPPER_TEST_SOURCE_DIR)).absoluteFilePath(QStringLiteral("data"))); } + +#if defined(MAPPER_USE_FAKE_POSITION_PLUGIN) + void fakePositionSourcePluginTest() + { + auto sources = QGeoPositionInfoSource::availableSources(); + QVERIFY(sources.contains(QStringLiteral("Fake position"))); + } + + void fakePositionSourceSimulatedTest() + { + auto const reference = QGeoCoordinate{10, 20, 100}; + FakePositionSource::setReferencePoint(reference); + + auto* source = QGeoPositionInfoSource::createSource(QStringLiteral("Fake position"), this); + QVERIFY(source); + QCOMPARE(int(source->error()), int(QGeoPositionInfoSource::NoError)); + + QSignalSpy source_spy(source, &QGeoPositionInfoSource::positionUpdated); + QVERIFY(source_spy.isValid()); + + source->startUpdates(); + QCOMPARE(int(source->error()), int(QGeoPositionInfoSource::NoError)); + QVERIFY(source_spy.wait()); + + auto last = source->lastKnownPosition(true); + QVERIFY(last.isValid()); + QVERIFY(qAbs(last.coordinate().latitude() - reference.latitude()) < 0.002); + QVERIFY(qAbs(last.coordinate().longitude() - reference.longitude()) < 0.002); + QVERIFY(qAbs(last.coordinate().altitude() - reference.altitude()) < 20); + + source->stopUpdates(); + delete source; + } +#endif // MAPPER_USE_FAKE_POSITION_PLUGIN + + #if defined(MAPPER_USE_NMEA_POSITION_PLUGIN) void nmeaPositionSourcePluginTest() { diff --git a/test/style_t.cpp b/test/style_t.cpp new file mode 100644 index 000000000..68d055c99 --- /dev/null +++ b/test/style_t.cpp @@ -0,0 +1,122 @@ +/* + * Copyright 2020 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core/map_coord.h" +#include "gui/scaling_icon_engine.h" +#include "gui/widgets/mapper_proxystyle.h" + +using namespace OpenOrienteering; + + + +/** + * @test Tests style customizations. + */ +class StyleTest : public QObject +{ +Q_OBJECT +private slots: + void scalingIconEngineTest(); + void standardIconTest(); +}; + + +/** + * Tests key behaviours of ScalingIconEngine. + */ +void StyleTest::scalingIconEngineTest() +{ + QPixmap pm(4, 4); + pm.fill(Qt::red); + { + // Testing Qt's default behaviour: + QIcon icon(pm); + // Scaling down + QCOMPARE(icon.actualSize(QSize(1,1)), QSize(1,1)); + QCOMPARE(icon.pixmap(QSize(1,1)).size(), QSize(1,1)); + // Not scaling up + QCOMPARE(icon.actualSize(QSize(10,10)), QSize(4,4)); + QCOMPARE(icon.pixmap(QSize(10,10)).size(), QSize(4,4)); + } + { + // ScalingIconEngine: + QIcon icon(new ScalingIconEngine(QString{})); + icon.addPixmap(pm); + // Scaling down + QCOMPARE(icon.actualSize(QSize(1,1)), QSize(1,1)); + QCOMPARE(icon.pixmap(QSize(1,1)).size(), QSize(1,1)); + // Scaling up + QCOMPARE(icon.actualSize(QSize(10,10)), QSize(10,10)); + QCOMPARE(icon.pixmap(QSize(10,10)).size(), QSize(10,10)); + } +} + +/** + * Tests standard icon behaviours of MapperProxyStyle + */ +void StyleTest::standardIconTest() +{ + // CommonStyle doesn't use PNG for QStyle::SP_TitleBarMenuButton, + // so it is not passed through ScalingIconEngine. + auto const standard_icon = QStyle::SP_TitleBarMenuButton; + auto const large = QSize(1000, 1000); + { + // Testing Qt's default behaviour: + auto const size = QCommonStyle().standardIcon(standard_icon).actualSize(large); + QVERIFY(size.width() < large.width()); + QVERIFY(size.height() < large.height()); + } + { + // ScalingIconEngine: + // MapperProxyStyle must ensure use of the scaling icon engine + QCOMPARE(MapperProxyStyle().standardIcon(standard_icon, nullptr, nullptr).actualSize(large), large); + } +} + + +/* + * We select a non-standard QPA because we don't need a real GUI window. + * + * Normally, the "offscreen" plugin would be the correct one. + * However, it bails out with a QFontDatabase error (cf. QTBUG-33674) + */ +namespace { + auto Q_DECL_UNUSED qpa_selected = qputenv("QT_QPA_PLATFORM", "minimal"); // clazy:exclude=non-pod-global-static +} + + +Q_IMPORT_PLUGIN(ScalingIconEnginePlugin) + +QTEST_MAIN(StyleTest) + +#include "style_t.moc" // IWYU pragma: keep diff --git a/test/symbol_set_t.cpp b/test/symbol_set_t.cpp index 186e5affe..ef4541ccb 100644 --- a/test/symbol_set_t.cpp +++ b/test/symbol_set_t.cpp @@ -300,6 +300,38 @@ bool validLineWidth(const LineSymbol& symbol) } +void checkOsmCrtFile(const Map& target, const QDir& symbol_set_dir) +{ + auto const crt_filename = QString::fromLatin1("OSM-%1.crt").arg(target.symbolSetId()); + if (!symbol_set_dir.exists(crt_filename)) + return; + + QFile crt_file {symbol_set_dir.absoluteFilePath(crt_filename)}; + QVERIFY(crt_file.open(QIODevice::ReadOnly)); + QTextStream stream {&crt_file}; + + auto rules = SymbolRuleSet::loadCrt(stream, target); + QCOMPARE(stream.status(), QTextStream::Ok); + + auto test = [](auto const& item) ->bool { + return item.type == SymbolRule::NoAssignment + || !item.symbol + || item.symbol->isHidden() + || !item.query.getOperator(); + }; + auto invalid_rule = std::find_if(begin(rules), end(rules), test); + if (invalid_rule != end(rules)) + { + auto message = QString::fromLatin1("Invalid rule in OSM-%1.crt: %2 %3") + .arg(target.symbolSetId(), + invalid_rule->symbol ? invalid_rule->symbol->getNumberAsString() : QStringLiteral("???"), + invalid_rule->query.toString()) + .toLatin1(); + QFAIL(message.constData()); + } +} + + namespace ISOM_2017_2 { @@ -824,7 +856,11 @@ void SymbolSetTool::processSymbolSet() processTranslation(map, translation_entries); } - if (source_scale != target_scale) + if (source_scale == target_scale) + { + checkOsmCrtFile(map, symbol_set_dir); + } + else { if (name == QStringLiteral("ISOM 2017-2")) { diff --git a/test/template_t.cpp b/test/template_t.cpp index a96508560..eeb8d3b31 100644 --- a/test/template_t.cpp +++ b/test/template_t.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2017-2019 Kai Pastor + * Copyright 2017-2020 Kai Pastor * * This file is part of OpenOrienteering. * @@ -18,28 +18,73 @@ */ +#include +#include +#include +#include + #include #include #include +#include +#include +#include #include +#include +#include #include +#include +#include +#include #include +#include +#include +#include +#include +#include // IWYU pragma: keep #include +#include #include #include "test_config.h" #include "global.h" #include "core/georeferencing.h" +#include "core/latlon.h" #include "core/map.h" +#include "core/map_coord.h" #include "core/map_view.h" #include "fileformats/xml_file_format_p.h" #include "gdal/ogr_template.h" +#include "gdal/gdal_manager.h" #include "templates/template.h" +#include "templates/template_table_model.h" +#include "templates/template_track.h" #include "templates/world_file.h" using namespace OpenOrienteering; +namespace +{ + +QPointF center(const TemplateTrack* temp) +{ + QPicture picture; + QPainter painter(&picture); + temp->drawTracks(&painter, false); + painter.end(); + return QRectF(picture.boundingRect()).center(); +} + +QPointF center(const Template* temp) +{ + if (auto* template_track = qobject_cast(temp)) + return center(template_track); + return temp->templateToMap(temp->getTemplateExtent().center()); +} + +} // namespace + /** * @test Tests template classes. @@ -50,6 +95,8 @@ Q_OBJECT private slots: void initTestCase() { + QCoreApplication::setOrganizationName(QString::fromLatin1("OpenOrienteering.org")); + QCoreApplication::setApplicationName(QString::fromLatin1(metaObject()->className())); Q_INIT_RESOURCE(resources); doStaticInitializations(); // Static map initializations @@ -125,11 +172,24 @@ private slots: auto temp = map.getTemplate(0); QCOMPARE(temp->getTemplateType(), "TemplateImage"); QCOMPARE(temp->getTemplateFilename(), QString::fromLatin1("world-file.png")); - QCOMPARE(temp->getTemplateState(), Template::Loaded); QVERIFY(temp->isTemplateGeoreferenced()); + + QVERIFY(temp->getTemplateState() != Template::Invalid); + QCOMPARE(temp->getTemplateRelativePath(), QStringLiteral("world-file.png")); + QCOMPARE(temp->getTemplateRelativePath(nullptr), QStringLiteral("world-file.png")); + auto base_dir = QDir(QStringLiteral("testdata:templates")); + QCOMPARE(temp->getTemplateRelativePath(&base_dir), QStringLiteral("world-file.png")); + base_dir.cdUp(); + QCOMPARE(temp->getTemplateRelativePath(&base_dir), QStringLiteral("templates/world-file.png")); + + map.loadTemplateFiles(view); + QCOMPARE(temp->getTemplateState(), Template::Loaded); auto rotation_template = 0.01 * qRound(100 * qRadiansToDegrees(temp->getTemplateRotation())); auto rotation_map = 0.01 * qRound(100 * georef.getGrivation()); QCOMPARE(rotation_template, rotation_map); + + // Test boundingRect for TemplateImage + QCOMPARE(temp->boundingRect().center(), center(temp)); } @@ -162,6 +222,12 @@ private slots: QCOMPARE(temp->getTemplateRelativePath(), QStringLiteral("world-file.png")); QCOMPARE(temp->getTemplateState(), Template::Invalid); + QCOMPARE(temp->getTemplateRelativePath(nullptr), QStringLiteral("world-file.png")); + auto base_dir = QDir(QStringLiteral("testdata:templates")); + QCOMPARE(temp->getTemplateRelativePath(&base_dir), QStringLiteral("world-file.png")); + base_dir.cdUp(); + QCOMPARE(temp->getTemplateRelativePath(&base_dir), QStringLiteral("world-file.png")); + QBuffer out_buffer; QVERIFY(out_buffer.open(QIODevice::WriteOnly)); XMLFileExporter exporter{ {}, &map, &view }; @@ -195,15 +261,96 @@ private slots: if (QFile::encodeName(temp->getTemplateFilename()) != temp->getTemplateFilename().toUtf8()) QEXPECT_FAIL("", "Local 8 bit encoding is not UTF-8", Abort); #endif - QCOMPARE(temp->getTemplateState(), Template::Loaded); QVERIFY(temp->isTemplateGeoreferenced()); + + map.loadTemplateFiles(view); + QCOMPARE(temp->getTemplateState(), Template::Loaded); auto rotation_template = 0.01 * qRound(100 * qRadiansToDegrees(temp->getTemplateRotation())); auto rotation_map = 0.01 * qRound(100 * georef.getGrivation()); QCOMPARE(rotation_template, rotation_map); #endif } + void templateTrackTest_data() + { + QTest::addColumn("map_file"); + QTest::addColumn("template_index"); + + QTest::newRow("TemplateTrack georef") << QStringLiteral("testdata:templates/template-track.xmap") << 0; + QTest::newRow("TemplateTrack non-georef") << QStringLiteral("testdata:templates/template-track.xmap") << 1; + QTest::newRow("TemplateTrack OGR georef") << QStringLiteral("testdata:templates/template-track.xmap") << 2; + QTest::newRow("TemplateTrack OGR non-georef") << QStringLiteral("testdata:templates/template-track.xmap") << 3; + } + + void templateTrackTest() + { #ifdef MAPPER_USE_GDAL + GdalManager().setFormatEnabled(GdalManager::GPX, false); +#endif + + QFETCH(QString, map_file); + QFETCH(int, template_index); + + Map map; + MapView view{ &map }; + QVERIFY(map.loadFrom(map_file, &view)); + + QVERIFY(map.getNumTemplates() > template_index); + auto const* temp = map.getTemplate(template_index); + QCOMPARE(temp->getTemplateType(), "TemplateTrack"); + QCOMPARE(temp->getTemplateState(), Template::Unloaded); + QVERIFY(map.getTemplate(template_index)->loadTemplateFile()); + QCOMPARE(temp->getTemplateState(), Template::Loaded); + + auto const expected_center = map.calculateExtent().center(); + if (QLineF(center(temp), expected_center).length() > 0.5) + QCOMPARE(center(temp), expected_center); + else + QVERIFY2(true, "Centers do match"); + } + +#ifdef MAPPER_USE_GDAL + void ogrTemplateTest_data() + { + QTest::addColumn("map_file"); + QTest::addColumn("template_index"); + + QTest::newRow("TemplateTrack georef") << QStringLiteral("testdata:templates/template-track.xmap") << 0; + QTest::newRow("TemplateTrack non-georef") << QStringLiteral("testdata:templates/template-track.xmap") << 1; + QTest::newRow("TemplateTrack OGR georef") << QStringLiteral("testdata:templates/template-track.xmap") << 2; + QTest::newRow("TemplateTrack OGR non-georef") << QStringLiteral("testdata:templates/template-track.xmap") << 3; + QTest::newRow("OgrTemplate basic") << QStringLiteral("testdata:templates/ogr-template.xmap") << 0; + QTest::newRow("OgrTemplate compatibility") << QStringLiteral("testdata:templates/ogr-template.xmap") << 1; + } + + void ogrTemplateTest() + { + GdalManager().setFormatEnabled(GdalManager::GPX, true); + + QFETCH(QString, map_file); + QFETCH(int, template_index); + + Map map; + MapView view{ &map }; + QVERIFY(map.loadFrom(map_file, &view)); + + QVERIFY(map.getNumTemplates() > template_index); + auto const* temp = map.getTemplate(template_index); + QCOMPARE(temp->getTemplateType(), "OgrTemplate"); + QCOMPARE(temp->getTemplateState(), Template::Unloaded); + QVERIFY(map.getTemplate(template_index)->loadTemplateFile()); + QCOMPARE(temp->getTemplateState(), Template::Loaded); + + auto const expected_center = map.calculateExtent().center(); + if (QLineF(center(temp), expected_center).length() > 0.5) + QCOMPARE(center(temp), expected_center); + else + QVERIFY2(true, "Centers do match"); + + // Test boundingRect for OgrTemplate + QCOMPARE(temp->boundingRect().center(), center(temp)); + } + void ogrTemplateGeoreferencingTest() { auto const osm_fileinfo = QFileInfo(QStringLiteral("testdata:templates/map.osm")); @@ -215,6 +362,175 @@ private slots: QCOMPARE(qRound(latlon.longitude()), 8); } #endif + + void templateRotationTest_data() + { + QTest::addColumn("map_file"); + QTest::addColumn("template_index"); + + QTest::newRow("TemplateImage world file") << QStringLiteral("testdata:templates/world-file.xmap") << 0; + QTest::newRow("TemplateTrack georef") << QStringLiteral("testdata:templates/template-track.xmap") << 0; + QTest::newRow("TemplateTrack non-georef") << QStringLiteral("testdata:templates/template-track.xmap") << 1; +#ifdef MAPPER_USE_GDAL + QTest::newRow("GdalImage geotiff") << QStringLiteral("testdata:templates/geotiff.xmap") << 0; + QTest::newRow("OgrTemplate basic") << QStringLiteral("testdata:templates/ogr-template.xmap") << 0; + QTest::newRow("OgrTemplate compatibility") << QStringLiteral("testdata:templates/ogr-template.xmap") << 1; +#endif + } + + void templateRotationTest() + { +#ifdef MAPPER_USE_GDAL + GdalManager().setFormatEnabled(GdalManager::GPX, false); +#endif + + QFETCH(QString, map_file); + QFETCH(int, template_index); + + Map map; + MapView view{ &map }; + QVERIFY(map.loadFrom(map_file, &view)); + + auto const& georef = map.getGeoreferencing(); + QVERIFY(georef.isValid()); + QVERIFY(std::abs(georef.getGrivation()) >= 0.01); + + QVERIFY(map.getNumTemplates() > template_index); + auto const* temp = map.getTemplate(template_index); + QCOMPARE(temp->getTemplateState(), Template::Unloaded); + + // A clone of temp which is loaded before map rotation + auto* loaded_clone = temp->duplicate(); + map.addTemplate(map.getNumTemplates(), std::unique_ptr