Skip to content

Commit

Permalink
Merge pull request #9857 from marcel-dancak/tiles_xyz
Browse files Browse the repository at this point in the history
New Processing Algorithm to generate raster XYZ tiles
  • Loading branch information
wonder-sk committed Apr 26, 2019
2 parents ebab2e1 + e6ff7e0 commit 5e4ea73
Show file tree
Hide file tree
Showing 15 changed files with 1,210 additions and 1 deletion.
7 changes: 7 additions & 0 deletions python/plugins/processing/algs/help/qgis.yaml
Expand Up @@ -509,6 +509,13 @@ qgis:sumlinelengths: >
qgis:texttofloat: >
This algorithm modifies the type of a given attribute in a vector layer, converting a text attribute containing numeric strings into a numeric attribute.

qgis:tilesxyz: >
This algorithm generates raster XYZ tiles of map canvas content.

Tile images can be saved as individual images in directory structure, or as single file in MBTiles format.

Tile size is fixed to 256x256.

qgis:topologicalcoloring: >
This algorithm assigns a color index to polygon features in such a way that no adjacent polygons share the same color index, whilst minimizing the number of colors required.

Expand Down
2 changes: 2 additions & 0 deletions python/plugins/processing/algs/qgis/QgisAlgorithmProvider.py
Expand Up @@ -134,6 +134,7 @@
from .StatisticsByCategories import StatisticsByCategories
from .SumLines import SumLines
from .TextToFloat import TextToFloat
from .TilesXYZ import TilesXYZ
from .TinInterpolation import TinInterpolation
from .TopoColors import TopoColor
from .TruncateTable import TruncateTable
Expand Down Expand Up @@ -244,6 +245,7 @@ def getAlgs(self):
StatisticsByCategories(),
SumLines(),
TextToFloat(),
TilesXYZ(),
TinInterpolation(),
TopoColor(),
TruncateTable(),
Expand Down
393 changes: 393 additions & 0 deletions python/plugins/processing/algs/qgis/TilesXYZ.py

Large diffs are not rendered by default.

15 changes: 14 additions & 1 deletion python/plugins/processing/tests/AlgorithmsTestBase.py
Expand Up @@ -86,7 +86,12 @@ def check_algorithm(self, name, defs):
:param defs: A python dict containing a test algorithm definition
"""
self.vector_layer_params = {}
QgsProject.instance().removeAllMapLayers()
QgsProject.instance().clear()

if 'project' in defs:
full_project_path = os.path.join(processingTestDataPath(), defs['project'])
project_read_success = QgsProject.instance().read(full_project_path)
self.assertTrue(project_read_success, 'Failed to load project file: ' + defs['project'])

if 'project_crs' in defs:
QgsProject.instance().setCrs(QgsCoordinateReferenceSystem(defs['project_crs']))
Expand Down Expand Up @@ -212,6 +217,9 @@ def load_result_param(self, param):
basename = 'raster.tif'
filepath = os.path.join(outdir, basename)
return filepath
elif param['type'] == 'directory':
outdir = tempfile.mkdtemp()
return outdir

raise KeyError("Unknown type '{}' specified for parameter".format(param['type']))

Expand Down Expand Up @@ -350,6 +358,11 @@ def check_results(self, results, context, params, expected):
result_filepath = results[id]

self.assertFilesEqual(expected_filepath, result_filepath)
elif 'directory' == expected_result['type']:
expected_dirpath = self.filepath_from_param(expected_result)
result_dirpath = results[id]

self.assertDirectoriesEqual(expected_dirpath, result_dirpath)
elif 'regex' == expected_result['type']:
with open(results[id], 'r') as file:
data = file.read()
Expand Down
19 changes: 19 additions & 0 deletions python/plugins/processing/tests/README.md
Expand Up @@ -194,6 +194,25 @@ OUTPUT:
- 'Feature Count: 6'
```

#### Directories

You can compare the content of an output directory by en expected result reference directory

```yaml
OUTPUT_DIR:
name: expected/tiles_xyz/test_1
type: directory
```

### Algorithm Context

There are few more definitions that can modify context of the algorithm - these can be specified at top level of test:

- `project` - will load a specified QGIS project file before running the algorithm. If not specified, algorithm will run with empty project
- `project_crs` - overrides the default project CRS - e.g. `EPSG:27700`
- `ellipsoid` - overrides the default project ellipsoid used for measurements - e.g. `GRS80`


Running tests locally
------------------
```bash
Expand Down
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 16 additions & 0 deletions python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml
Expand Up @@ -7524,5 +7524,21 @@ tests:
name: expected/join_to_nearest_no_matches.gml
type: vector

- name: Generate XYZ tiles
algorithm: qgis:tilesxyz
project: ../../../../../tests/testdata/xyztiles.qgs
project_crs: EPSG:3857
params:
EXTENT: -12535000,-9883000,3360000,5349000 [EPSG:3857]
ZOOM_MIN: 1
ZOOM_MAX: 3
TILE_FORMAT: 0 # png
OUTPUT_FORMAT: 0 # directory

results:
OUTPUT_DIRECTORY:
type: directory
name: expected/xyztiles


# See ../README.md for a description of the file format
15 changes: 15 additions & 0 deletions python/testing/__init__.py
Expand Up @@ -29,6 +29,7 @@
import sys
import difflib
import functools
import filecmp

from qgis.PyQt.QtCore import QVariant
from qgis.core import QgsApplication, QgsFeatureRequest, NULL
Expand Down Expand Up @@ -196,6 +197,20 @@ def assertFilesEqual(self, filepath_expected, filepath_result):
diff = list(diff)
self.assertEqual(0, len(diff), ''.join(diff))

def assertDirectoriesEqual(self, dirpath_expected, dirpath_result):
""" Checks whether both directories have the same content (recursively) and raises an assertion error if not. """
dc = filecmp.dircmp(dirpath_expected, dirpath_result)
dc.report_full_closure()

def _check_dirs_equal_recursive(dcmp):
self.assertEqual(dcmp.left_only, [])
self.assertEqual(dcmp.right_only, [])
self.assertEqual(dcmp.diff_files, [])
for sub_dcmp in dcmp.subdirs.values():
_check_dirs_equal_recursive(sub_dcmp)

_check_dirs_equal_recursive(dc)

def assertGeometriesEqual(self, geom0, geom1, geom0_id='geometry 1', geom1_id='geometry 2', precision=14, topo_equal_check=False):
self.checkGeometriesEqual(geom0, geom1, geom0_id, geom1_id, use_asserts=True, precision=precision, topo_equal_check=topo_equal_check)

Expand Down

0 comments on commit 5e4ea73

Please sign in to comment.