diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 0000000..013dd20 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,2 @@ +[report] +show_missing = True diff --git a/.github/workflows/pr-auto-semver.yml b/.github/workflows/pr-auto-semver.yml new file mode 100644 index 0000000..de068da --- /dev/null +++ b/.github/workflows/pr-auto-semver.yml @@ -0,0 +1,13 @@ +name: on-pr + +on: + pull_request: + types: + - opened + - reopened + - synchronize + - edited + +jobs: + pr-edit: + uses: geoadmin/.github/.github/workflows/pr-auto-semver.yml@master diff --git a/.github/workflows/semver.yml b/.github/workflows/semver.yml new file mode 100644 index 0000000..ad0c532 --- /dev/null +++ b/.github/workflows/semver.yml @@ -0,0 +1,11 @@ +name: on-push + +on: + push: + branches: + - master + - develop + +jobs: + release: + uses: geoadmin/.github/.github/workflows/semver-release.yml@master diff --git a/.gitignore b/.gitignore index 976e661..12d7720 100644 --- a/.gitignore +++ b/.gitignore @@ -64,3 +64,6 @@ target/ # Vim temporary files *.swp + +# test report +nose2-junit.xml diff --git a/.isort.cfg b/.isort.cfg new file mode 100644 index 0000000..07ed404 --- /dev/null +++ b/.isort.cfg @@ -0,0 +1,7 @@ +[settings] +known_third_party=pytest +known_django=django +known_flask=flask +known_gatilegrid=gatilegrid +force_single_line=True +sections=FUTURE,STDLIB,THIRDPARTY,DJANGO,FLASK,GATILEGRID,FIRSTPARTY,LOCALFOLDER diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 0000000..6d7bc71 --- /dev/null +++ b/.pylintrc @@ -0,0 +1,29 @@ +[MAIN] +ignore=.venv + +[MESSAGES CONTROL] +disable= + # no convention and refactor rules + C, + R, + # allow some unconventional stuff (assuming the library already works as intended) + unused-variable, + attribute-defined-outside-init, + unused-import, + possibly-used-before-assignment + +generated-members= + # added via mixins + gatilegrid.tilegrids._TileGrid.MINX + gatilegrid.tilegrids._TileGrid.MAXX + gatilegrid.tilegrids._TileGrid.MINY + gatilegrid.tilegrids._TileGrid.MAXY + gatilegrid.tilegrids._TileGrid.RESOLUTIONS + gatilegrid.tilegrids._TileGrid.spatialReference + gatilegrid.tilegrids._TileGrid.tileAddressTemplate + gatilegrid.tilegrids._TileGrid.unit + gatilegrid.tilegrids._TileGrid.metersPerUnit + gatilegrid.tilegrids._TileGrid. + +[REPORTS] +score = no diff --git a/.style.yapf b/.style.yapf new file mode 100644 index 0000000..22a740b --- /dev/null +++ b/.style.yapf @@ -0,0 +1,33 @@ +[style] +based_on_style=google +# Put closing brackets on a separate line, dedented, if the bracketed +# expression can't fit in a single line. Applies to all kinds of brackets, +# including function definitions and calls. For example: +# +# config = { +# 'key1': 'value1', +# 'key2': 'value2', +# } # <--- this bracket is dedented and on a separate line +# +# time_series = self.remote_client.query_entity_counters( +# entity='dev3246.region1', +# key='dns.query_latency_tcp', +# transform=Transformation.AVERAGE(window=timedelta(seconds=60)), +# start_ts=now()-timedelta(days=3), +# end_ts=now(), +# ) # <--- this bracket is dedented and on a separate line +dedent_closing_brackets=True +coalesce_brackets=True + +# This avoid issues with complex dictionary +# see https://github.com/google/yapf/issues/392#issuecomment-407958737 +indent_dictionary_value=True +allow_split_before_dict_value=False + +# Split before arguments, but do not split all sub expressions recursively +# (unless needed). +split_all_top_level_comma_separated_values=True + +# Split lines longer than 100 characters (this only applies to code not to +# comment and docstring) +column_limit=100 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..058e5f5 --- /dev/null +++ b/Makefile @@ -0,0 +1,133 @@ +SHELL = /bin/bash + +.DEFAULT_GOAL := help + + +CURRENT_DIR := $(shell pwd) + +# Test reports configuration +TEST_REPORT_DIR ?= $(CURRENT_DIR)/tests/report +TEST_REPORT_FILE ?= nose2-junit.xml + +# general targets timestamps +TIMESTAMPS = .timestamps +REQUIREMENTS := $(TIMESTAMPS) $(PIP_FILE) $(PIP_FILE_LOCK) + +# Find all python files that are not inside a hidden directory (directory starting with .) +PYTHON_FILES := $(shell find ./* -type d \( -path ./build -o -path ./dist \) -prune -false -o -type f -name "*.py" -print) + +# PIPENV files +PIP_FILE = Pipfile +PIP_FILE_LOCK = Pipfile.lock + +# default configuration +ENV_FILE ?= .env.local + +# Commands +PIPENV_RUN := pipenv run +PYTHON := $(PIPENV_RUN) python3 +PIP := $(PIPENV_RUN) pip3 +YAPF := $(PIPENV_RUN) yapf +ISORT := $(PIPENV_RUN) isort +NOSE := $(PIPENV_RUN) nose2 +PYLINT := $(PIPENV_RUN) pylint + +PACKAGE_VERSION = $(shell awk '/^Version:/ {print $$2}' gatilegrid.egg-info/PKG-INFO) + +# Build targets. Calling setup is all that is needed for the local files to be installed as needed. + +.PHONY: setup +setup: $(REQUIREMENTS) ## Create the python virtual environment with developper tools + pipenv install --dev + +# linting target, calls upon yapf to make sure your code is easier to read and respects some conventions. + +.PHONY: format +format: $(REQUIREMENTS) ## Format the python source code + $(YAPF) -p -i --style .style.yapf $(PYTHON_FILES) + $(ISORT) $(PYTHON_FILES) + + +.PHONY: ci-check-format +ci-check-format: format ## Check if formatting python source code is required + @if [[ -n `git status --porcelain` ]]; then \ + >&2 echo "ERROR: the following files are not formatted correctly:"; \ + >&2 git status --porcelain; \ + exit 1; \ + fi + + +.PHONY: lint +lint: $(REQUIREMENTS) ## Lint the python source code + $(PYLINT) $(PYTHON_FILES) + + +.PHONY: format-lint +format-lint: format lint ## Format and lint the python source code + + +# Test target + +.PHONY: test +test: $(DEV_REQUIREMENTS_TIMESTAMP) ## Run the tests + mkdir -p $(TEST_REPORT_DIR) + $(NOSE) -v -c tests/unittest.cfg --junit-xml-path $(TEST_REPORT_DIR)/$(TEST_REPORT_FILE) -s tests/ + + +# Packaging target + +.PHONY: package +package: $(DEV_REQUIREMENTS_TIMESTAMP) ## Create package + $(PYTHON) -m build + + +.PHONY: publish +publish: publish-check package ## Tag and publish package to PyPI + @echo "Upload package version=$(PACKAGE_VERSION)" + $(PYTHON) -m twine upload dist/* + + +# Clean targets + +.PHONY: clean-venv +clean-venv: ## Clean python venv + pipenv --rm + +.PHONY: clean +clean: clean-venv ## Clean generated files + @# clean python cache files + find . -name __pycache__ -type d -print0 | xargs -I {} -0 rm -rf "{}" + rm -rf $(TEST_REPORT_DIR) + rm -rf $(TIMESTAMPS) + rm -rf dist + rm -rf build + rm -rf *.egg-info + rm -f .coverage + +.PHONY: clean-all +clean-all: clean ## Clean everything + +# Actual builds targets with dependencies + +$(TIMESTAMPS): + mkdir -p $(TIMESTAMPS) + + +$(VENV_TIMESTAMP): + test -d $(VENV) || $(SYSTEM_PYTHON) -m venv $(VENV) && $(PIP) install --upgrade pip setuptools + @touch $(VENV_TIMESTAMP) + + +$(DEV_REQUIREMENTS_TIMESTAMP): $(VENV_TIMESTAMP) $(DEV_REQUIREMENTS) + $(PIP) install -r $(DEV_REQUIREMENTS) + @touch $(DEV_REQUIREMENTS_TIMESTAMP) + + +publish-check: ## Check if publish is allowed + @if [ -n "`git status --porcelain`" ]; then echo "ERROR: Repo is dirty !" >&2; exit 1; fi + +.PHONY: help +help: ## Display this help +# automatically generate the help page based on the documentation after each make target +# from https://gist.github.com/prwhite/8168133 + @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[$$()% a-zA-Z_-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST) diff --git a/Pipfile b/Pipfile new file mode 100644 index 0000000..3cfae54 --- /dev/null +++ b/Pipfile @@ -0,0 +1,13 @@ +[[source]] +url = "https://pypi.python.org/simple" +verify_ssl = true +name = "pypi" + +[dev-packages] +flake8 = "*" +nose2 = { version = "*", extras = ["coverage_plugin"] } +yapf = "*" +pylint = "*" +isort = "*" +setuptools = "*" +build = "*" diff --git a/Pipfile.lock b/Pipfile.lock new file mode 100644 index 0000000..a077e10 --- /dev/null +++ b/Pipfile.lock @@ -0,0 +1,250 @@ +{ + "_meta": { + "hash": { + "sha256": "a2a23c83d187deaae80c01e4a381a82a1678c64ac454d4fa6724e9bfdd762213" + }, + "pipfile-spec": 6, + "requires": {}, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.python.org/simple", + "verify_ssl": true + } + ] + }, + "default": {}, + "develop": { + "astroid": { + "hashes": [ + "sha256:1e5a5011af2920c7c67a53f65d536d65bfa7116feeaf2354d8b94f29573bb0ce", + "sha256:54c760ae8322ece1abd213057c4b5bba7c49818853fc901ef09719a60dbf9dec" + ], + "markers": "python_full_version >= '3.9.0'", + "version": "==3.3.11" + }, + "build": { + "hashes": [ + "sha256:698edd0ea270bde950f53aed21f3a0135672206f3911e0176261a31e0e07b397", + "sha256:7145f0b5061ba90a1500d60bd1b13ca0a8a4cebdd0cc16ed8adf1c0e739f43b4" + ], + "index": "pypi", + "markers": "python_version >= '3.9'", + "version": "==1.3.0" + }, + "coverage": { + "hashes": [ + "sha256:073711de3181b2e204e4870ac83a7c4853115b42e9cd4d145f2231e12d670930", + "sha256:081b98395ced0d9bcf60ada7661a0b75f36b78b9d7e39ea0790bb4ed8da14747", + "sha256:0de434f4fbbe5af4fa7989521c655c8c779afb61c53ab561b64dcee6149e4c65", + "sha256:0e93b1476b79eae849dc3872faeb0bf7948fd9ea34869590bc16a2a00b9c82a7", + "sha256:0f3f56e4cb573755e96a16501a98bf211f100463d70275759e73f3cbc00d4f27", + "sha256:0f7cb359a448e043c576f0da00aa8bfd796a01b06aa610ca453d4dde09cc1034", + "sha256:10356fdd33a7cc06e8051413140bbdc6f972137508a3572e3f59f805cd2832fd", + "sha256:137921f2bac5559334ba66122b753db6dc5d1cf01eb7b64eb412bb0d064ef35b", + "sha256:160c00a5e6b6bdf4e5984b0ef21fc860bc94416c41b7df4d63f536d17c38902e", + "sha256:2195f8e16ba1a44651ca684db2ea2b2d4b5345da12f07d9c22a395202a05b23c", + "sha256:282b1b20f45df57cc508c1e033403f02283adfb67d4c9c35a90281d81e5c52c5", + "sha256:28395ca3f71cd103b8c116333fa9db867f3a3e1ad6a084aa3725ae002b6583bc", + "sha256:2904271c80898663c810a6b067920a61dd8d38341244a3605bd31ab55250dad5", + "sha256:2b38261034fda87be356f2c3f42221fdb4171c3ce7658066ae449241485390d5", + "sha256:2e4c33e6378b9d52d3454bd08847a8651f4ed23ddbb4a0520227bd346382bbc6", + "sha256:388d80e56191bf846c485c14ae2bc8898aa3124d9d35903fef7d907780477634", + "sha256:3e23dd5408fe71a356b41baa82892772a4cefcf758f2ca3383d2aa39e1b7a003", + "sha256:3fb99d0786fe17b228eab663d16bee2288e8724d26a199c29325aac4b0319b9b", + "sha256:441c357d55f4936875636ef2cfb3bee36e466dcf50df9afbd398ce79dba1ebb7", + "sha256:4cec13817a651f8804a86e4f79d815b3b28472c910e099e4d5a0e8a3b6a1d4cb", + "sha256:5aea98383463d6e1fa4e95416d8de66f2d0cb588774ee20ae1b28df826bcb619", + "sha256:5b15a87265e96307482746d86995f4bff282f14b027db75469c446da6127433b", + "sha256:5b2dd6059938063a2c9fee1af729d4f2af28fd1a545e9b7652861f0d752ebcea", + "sha256:5e75e37f23eb144e78940b40395b42f2321951206a4f50e23cfd6e8a198d3ceb", + "sha256:6008a021907be8c4c02f37cdc3ffb258493bdebfeaf9a839f9e71dfdc47b018e", + "sha256:61c950fc33d29c91b9e18540e1aed7d9f6787cc870a3e4032493bbbe641d12fc", + "sha256:628055297f3e2aa181464c3808402887643405573eb3d9de060d81531fa79d32", + "sha256:675824a363cc05781b1527b39dc2587b8984965834a748177ee3c37b64ffeafb", + "sha256:689920ecfd60f992cafca4f5477d55720466ad2c7fa29bb56ac8d44a1ac2b47a", + "sha256:692d70ea725f471a547c305f0d0fc6a73480c62fb0da726370c088ab21aed282", + "sha256:6937347c5d7d069ee776b2bf4e1212f912a9f1f141a429c475e6089462fcecc5", + "sha256:6b3039e2ca459a70c79523d39347d83b73f2f06af5624905eba7ec34d64d80b5", + "sha256:6e31b8155150c57e5ac43ccd289d079eb3f825187d7c66e755a055d2c85794c6", + "sha256:70e7bfbd57126b5554aa482691145f798d7df77489a177a6bef80de78860a356", + "sha256:752a3005a1ded28f2f3a6e8787e24f28d6abe176ca64677bcd8d53d6fe2ec08a", + "sha256:7d79dabc0a56f5af990cc6da9ad1e40766e82773c075f09cc571e2076fef882e", + "sha256:7eb68d356ba0cc158ca535ce1381dbf2037fa8cb5b1ae5ddfc302e7317d04144", + "sha256:80b1695cf7c5ebe7b44bf2521221b9bb8cdf69b1f24231149a7e3eb1ae5fa2fb", + "sha256:851430a9a361c7a8484a36126d1d0ff8d529d97385eacc8dfdc9bfc8c2d2cbe4", + "sha256:856986eadf41f52b214176d894a7de05331117f6035a28ac0016c0f63d887629", + "sha256:86b9b59f2b16e981906e9d6383eb6446d5b46c278460ae2c36487667717eccf1", + "sha256:8953746d371e5695405806c46d705a3cd170b9cc2b9f93953ad838f6c1e58612", + "sha256:8cdbe264f11afd69841bd8c0d83ca10b5b32853263ee62e6ac6a0ab63895f972", + "sha256:8dd5af36092430c2b075cee966719898f2ae87b636cefb85a653f1d0ba5d5393", + "sha256:8e0c38dc289e0508ef68ec95834cb5d2e96fdbe792eaccaa1bccac3966bbadcc", + "sha256:90558c35af64971d65fbd935c32010f9a2f52776103a259f1dee865fe8259352", + "sha256:90cb5b1a4670662719591aa92d0095bb41714970c0b065b02a2610172dbf0af6", + "sha256:92be86fcb125e9bda0da7806afd29a3fd33fdf58fba5d60318399adf40bf37d0", + "sha256:92c4ecf6bf11b2e85fd4d8204814dc26e6a19f0c9d938c207c5cb0eadfcabbe3", + "sha256:95d91d7317cde40a1c249d6b7382750b7e6d86fad9d8eaf4fa3f8f44cf171e80", + "sha256:961834e2f2b863a0e14260a9a273aff07ff7818ab6e66d2addf5628590c628f9", + "sha256:9702b59d582ff1e184945d8b501ffdd08d2cee38d93a2206aa5f1365ce0b8d78", + "sha256:98cede73eb83c31e2118ae8d379c12e3e42736903a8afcca92a7218e1f2903b0", + "sha256:99c4283e2a0e147b9c9cc6bc9c96124de9419d6044837e9799763a0e29a7321a", + "sha256:99e1a305c7765631d74b98bf7dbf54eeea931f975e80f115437d23848ee8c27c", + "sha256:a517feaf3a0a3eca1ee985d8373135cfdedfbba3882a5eab4362bda7c7cf518d", + "sha256:a80f7aef9535442bdcf562e5a0d5a5538ce8abe6bb209cfbf170c462ac2c2a32", + "sha256:ac765b026c9f33044419cbba1da913cfb82cca1b60598ac1c7a5ed6aac4621a0", + "sha256:acf36b8268785aad739443fa2780c16260ee3fa09d12b3a70f772ef100939d80", + "sha256:adec1d980fa07e60b6ef865f9e5410ba760e4e1d26f60f7e5772c73b9a5b0713", + "sha256:b0353b0f0850d49ada66fdd7d0c7cdb0f86b900bb9e367024fd14a60cecc1e27", + "sha256:b37201ce4a458c7a758ecc4efa92fa8ed783c66e0fa3c42ae19fc454a0792153", + "sha256:bf9a19f5012dab774628491659646335b1928cfc931bf8d97b0d5918dd58033c", + "sha256:c61fc91ab80b23f5fddbee342d19662f3d3328173229caded831aa0bd7595460", + "sha256:c68018e4fc4e14b5668f1353b41ccf4bc83ba355f0e1b3836861c6f042d89ac1", + "sha256:c706db3cabb7ceef779de68270150665e710b46d56372455cd741184f3868d8f", + "sha256:c83f6afb480eae0313114297d29d7c295670a41c11b274e6bca0c64540c1ce7b", + "sha256:c8a3ec16e34ef980a46f60dc6ad86ec60f763c3f2fa0db6d261e6e754f72e945", + "sha256:c9a8b7a34a4de3ed987f636f71881cd3b8339f61118b1aa311fbda12741bff0b", + "sha256:cd4b2b0707fc55afa160cd5fc33b27ccbf75ca11d81f4ec9863d5793fc6df56a", + "sha256:d6b9ae13d5d3e8aeca9ca94198aa7b3ebbc5acfada557d724f2a1f03d2c0b0df", + "sha256:d8fd7879082953c156d5b13c74aa6cca37f6a6f4747b39538504c3f9c63d043d", + "sha256:d9369a23186d189b2fc95cc08b8160ba242057e887d766864f7adf3c46b2df21", + "sha256:db4a1d897bbbe7339946ffa2fe60c10cc81c43fab8b062d3fcb84188688174a4", + "sha256:df4ec1f8540b0bcbe26ca7dd0f541847cc8a108b35596f9f91f59f0c060bfdd2", + "sha256:e132b9152749bd33534e5bd8565c7576f135f157b4029b975e15ee184325f528", + "sha256:e3fb1fa01d3598002777dd259c0c2e6d9d5e10e7222976fc8e03992f972a2cba", + "sha256:e41be6f0f19da64af13403e52f2dec38bbc2937af54df8ecef10850ff8d35301", + "sha256:ec98435796d2624d6905820a42f82149ee9fc4f2d45c2c5bc5a44481cc50db62", + "sha256:efeda443000aa23f276f4df973cb82beca682fd800bb119d19e80504ffe53ec2", + "sha256:f2a6a8e06bbda06f78739f40bfb56c45d14eb8249d0f0ea6d4b3d48e1f7c695d", + "sha256:f32ff80e7ef6a5b5b606ea69a36e97b219cd9dc799bcf2963018a4d8f788cfbf", + "sha256:f35ed9d945bece26553d5b4c8630453169672bea0050a564456eb88bdffd927e", + "sha256:f644a3ae5933a552a29dbb9aa2f90c677a875f80ebea028e5a52a4f429044b90", + "sha256:f863c08f4ff6b64fa8045b1e3da480f5374779ef187f07b82e0538c68cb4ff8e", + "sha256:fc53ba868875bfbb66ee447d64d6413c2db91fddcfca57025a0e7ab5b07d5862", + "sha256:ff8a991f70f4c0cf53088abf1e3886edcc87d53004c7bb94e78650b4d3dac3b5", + "sha256:ffea0575345e9ee0144dfe5701aa17f3ba546f8c3bb48db62ae101afb740e7d6" + ], + "markers": "python_version >= '3.9'", + "version": "==7.10.6" + }, + "dill": { + "hashes": [ + "sha256:0633f1d2df477324f53a895b02c901fb961bdbf65a17122586ea7019292cbcf0", + "sha256:44f54bf6412c2c8464c14e8243eb163690a9800dbe2c367330883b19c7561049" + ], + "markers": "python_version >= '3.8'", + "version": "==0.4.0" + }, + "flake8": { + "hashes": [ + "sha256:b9696257b9ce8beb888cdbe31cf885c90d31928fe202be0889a7cdafad32f01e", + "sha256:fe044858146b9fc69b551a4b490d69cf960fcb78ad1edcb84e7fbb1b4a8e3872" + ], + "index": "pypi", + "markers": "python_version >= '3.9'", + "version": "==7.3.0" + }, + "isort": { + "hashes": [ + "sha256:1cb5df28dfbc742e490c5e41bad6da41b805b0a8be7bc93cd0fb2a8a890ac450", + "sha256:2dc5d7f65c9678d94c88dfc29161a320eec67328bc97aad576874cb4be1e9615" + ], + "index": "pypi", + "markers": "python_full_version >= '3.9.0'", + "version": "==6.0.1" + }, + "mccabe": { + "hashes": [ + "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325", + "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e" + ], + "markers": "python_version >= '3.6'", + "version": "==0.7.0" + }, + "nose2": { + "extras": [ + "coverage_plugin" + ], + "hashes": [ + "sha256:36770f519df5becd3cbfe0bee4abbfbf9b9f6b4eb4e03361d282b7efcfc4f0df", + "sha256:564450c0c4f1602dfe171902ceb4726cc56658af7a620ae1826f1ffc86b09a86" + ], + "markers": "python_version >= '3.8'", + "version": "==0.15.1" + }, + "packaging": { + "hashes": [ + "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", + "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f" + ], + "markers": "python_version >= '3.8'", + "version": "==25.0" + }, + "platformdirs": { + "hashes": [ + "sha256:abd01743f24e5287cd7a5db3752faf1a2d65353f38ec26d98e25a6db65958c85", + "sha256:ca753cf4d81dc309bc67b0ea38fd15dc97bc30ce419a7f58d13eb3bf14c4febf" + ], + "markers": "python_version >= '3.9'", + "version": "==4.4.0" + }, + "pycodestyle": { + "hashes": [ + "sha256:c4b5b517d278089ff9d0abdec919cd97262a3367449ea1c8b49b91529167b783", + "sha256:dd6bf7cb4ee77f8e016f9c8e74a35ddd9f67e1d5fd4184d86c3b98e07099f42d" + ], + "markers": "python_version >= '3.9'", + "version": "==2.14.0" + }, + "pyflakes": { + "hashes": [ + "sha256:b24f96fafb7d2ab0ec5075b7350b3d2d2218eab42003821c06344973d3ea2f58", + "sha256:f742a7dbd0d9cb9ea41e9a24a918996e8170c799fa528688d40dd582c8265f4f" + ], + "markers": "python_version >= '3.9'", + "version": "==3.4.0" + }, + "pylint": { + "hashes": [ + "sha256:26698de19941363037e2937d3db9ed94fb3303fdadf7d98847875345a8bb6b05", + "sha256:7ef94aa692a600e82fabdd17102b73fc226758218c97473c7ad67bd4cb905d83" + ], + "index": "pypi", + "markers": "python_full_version >= '3.9.0'", + "version": "==3.3.8" + }, + "pyproject-hooks": { + "hashes": [ + "sha256:1e859bd5c40fae9448642dd871adf459e5e2084186e8d2c2a79a824c970da1f8", + "sha256:9e5c6bfa8dcc30091c74b0cf803c81fdd29d94f01992a7707bc97babb1141913" + ], + "markers": "python_version >= '3.7'", + "version": "==1.2.0" + }, + "setuptools": { + "hashes": [ + "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922", + "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c" + ], + "index": "pypi", + "markers": "python_version >= '3.9'", + "version": "==80.9.0" + }, + "tomlkit": { + "hashes": [ + "sha256:430cf247ee57df2b94ee3fbe588e71d362a941ebb545dec29b53961d61add2a1", + "sha256:c89c649d79ee40629a9fda55f8ace8c6a1b42deb912b2a8fd8d942ddadb606b0" + ], + "markers": "python_version >= '3.8'", + "version": "==0.13.3" + }, + "yapf": { + "hashes": [ + "sha256:00d3aa24bfedff9420b2e0d5d9f5ab6d9d4268e72afbf59bb3fa542781d5218e", + "sha256:224faffbc39c428cb095818cf6ef5511fdab6f7430a10783fdfb292ccf2852ca" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==0.43.0" + } + } +} diff --git a/README.md b/README.md index e5796b5..a804b4a 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,6 @@ -gatilegrid -=========== +# gatilegrid -![Build Status](https://codebuild.eu-central-1.amazonaws.com/badges?uuid=eyJlbmNyeXB0ZWREYXRhIjoiY1JweHpoMGV5cndNTjFRT1JXc0tNK0tyK0NSL3NoVWhXK3BFNU9DZFBEc3M5clN4RzdZMDc1czk5VUJiV1RFSk5YR0lFR1g4dVBES0FTMmpyVTNCcU0wPSIsIml2UGFyYW1ldGVyU3BlYyI6Im5qQWZtZ0lTWDFIelk0cG0iLCJtYXRlcmlhbFNldFNlcmlhbCI6MX0%3D&branch=master) - -## Geoadmin custom tile grid for web mapping applications - -gatilegrid is compatible with python 2.7, 3.5, 3.6, 3.7 and 3.8 +Geoadmin custom tile grid for web mapping applications. ## Installation @@ -13,7 +8,7 @@ gatilegrid is compatible with python 2.7, 3.5, 3.6, 3.7 and 3.8 $ pip install gatilegrid ``` -### Usage +## Usage Several tile grids are supported, namely 21781, 2056, 3857 and 4326. Here is an exemple using 21781. For 4326, an additional parameter is available (`tmsCompatible=True`). @@ -155,65 +150,33 @@ print(maxRow) >>> 1959 ``` -### Tests - -``` -source .venv/bin/activate -python setup.py test +## Local Development -``` - -### Publish a new version of the module - -Edit `$HOME/.pypirc` and add (username and password in keepass): - -``` -[distutils] -index-servers = - pypi - pypitest - -[pypi] -repository=https://upload.pypi.org/legacy/ -username=iwi*** -password= - -[pypitest] -repository=https://test.pypi.org/legacy/ -username=iwi*** -password= -``` +### Setup -Bump version in `setup.py`. - -Build, check and upload the new module to the test repository: - -``` -pip install --upgrade twine wheel setuptools -python setup.py sdist bdist_wheel -twine upload --repository testpypi dist/* +```bash +make setup ``` -Test local install from test repository. +### Tests -``` -pip install -i https://test.pypi.org/simple/ gatilegrid +```bash +make test ``` -If everything is ok, push the new version to the default repository. +### Formatting and Linting +```bash +make format +make lint +#or +make format-lint ``` -twine upload --repository pypi dist/* -``` - -Test the newly created module. -Create a RELEASE in github. +## Release and Publish -#### CONTRIBUTORS: +New release and publish on PyPI is done automatically upon PR merge into master branch. For bug fixes and small new features, PR can be directly open on master. Then the PR title define the version bump as follow: -- [Loic Gasser](https://github.com/loicgasser) -- [Marc Monnerat](https://github.com/procrastinatio) -- [Nadine Piveteau](https://github.com/nadine-piveteau) -- [Marcel Clausen](https://github.com/ltclm) -- [Gilbert Jeinziner](https://github.com/gjn) +- PR title and/or commit message contains #major => major version is bumped +- PR title and/or commit message contains #patch or head branch name starts with bug-|hotfix-|bugfix- => patch version is bumped +- Otherwise by default the minor version is bumped diff --git a/buildspec.yml b/buildspec.yml deleted file mode 100644 index 186c65a..0000000 --- a/buildspec.yml +++ /dev/null @@ -1,58 +0,0 @@ -version: 0.2 - -env: - shell: bash - variables: - SHELL: /bin/bash - AWS_DEFAULT_REGION: eu-central-1 - USER: "aws_code_build" - -phases: - install: - runtime-versions: - python: 3.7 - commands: - - apt-get update -qq - - apt-get install -yqq python3-pip python2.7 python2.7-dev python3.8 python3.9 - - build: - commands: - # For python 2.7 we need to have a virtualenv in order to have pip and to install the packages - - virtualenv -p python2.7 .venv - # each command in buildspec is run in a separate shell, therefore below we put the whole - # python2.7 into one command in order to activate the venv in this shell and run the test - # inside the venv - - | - . .venv/bin/activate - python --version - pip install -qq flake8 future nose coveralls - echo "Runs flake8..." - python -m flake8 --ignore=F401 gatilegrid tests - echo "Runs coverage" - python -m coverage run --source=gatilegrid setup.py test - python -m coverage report -m - deactivate - # Python 3.7 - - python3.7 --version - - python3.7 -m pip install -qq flake8 future nose coveralls - - echo "Runs flake8..." - - python3.7 -m flake8 --ignore=F401 gatilegrid tests - - echo "Runs coverage" - - python3.7 -m coverage run --source=gatilegrid setup.py test - - python3.7 -m coverage report -m - # Python 3.8 - - python3.8 --version - - python3.8 -m pip install -qq flake8 future nose coveralls - - echo "Runs flake8..." - - python3.8 -m flake8 --ignore=F401 gatilegrid tests - - echo "Runs coverage" - - python3.8 -m coverage run --source=gatilegrid setup.py test - - python3.8 -m coverage report -m - # Python 3.9 - - python3.9 --version - - python3.9 -m pip install -qq flake8 future nose coveralls - - echo "Runs flake8..." - - python3.9 -m flake8 --ignore=F401 gatilegrid tests - - echo "Runs coverage" - - python3.9 -m coverage run --source=gatilegrid setup.py test - - python3.9 -m coverage report -m diff --git a/gatilegrid/__init__.py b/gatilegrid/__init__.py index d32861d..2241a7d 100644 --- a/gatilegrid/__init__.py +++ b/gatilegrid/__init__.py @@ -1,6 +1,8 @@ -from .tilegrids import GeoadminTileGridLV03, GeoadminTileGridLV95, \ - GlobalMercatorTileGrid, GlobalGeodeticTileGrid from .grid import Grid +from .tilegrids import GeoadminTileGridLV03 +from .tilegrids import GeoadminTileGridLV95 +from .tilegrids import GlobalGeodeticTileGrid +from .tilegrids import GlobalMercatorTileGrid def getTileGrid(srs): diff --git a/gatilegrid/grid.py b/gatilegrid/grid.py index 0ad47d2..2f9d8c9 100644 --- a/gatilegrid/grid.py +++ b/gatilegrid/grid.py @@ -1,7 +1,4 @@ -# -*- coding: utf-8 -*- - import math -from past.builtins import xrange class Grid: @@ -34,8 +31,8 @@ def __init__(self, extent, resolutionX, resolutionY): self._setExtentAddress() def __iter__(self): - for col in xrange(0, self.nbCellsX): - for row in xrange(0, self.nbCellsY): + for col in range(0, self.nbCellsX): + for row in range(0, self.nbCellsY): cellExtent = self.cellExtent(col, row) yield (cellExtent, col, row) @@ -78,10 +75,7 @@ def cellAddressFromPointCoordinate(self, pointCoordinate): col = max(0, col - 1) if y in (self.MINY, self.MAXY) and row.is_integer(): row = max(0, row - 1) - return [ - int(math.floor(col)), - int(math.floor(row)) - ] + return [int(math.floor(col)), int(math.floor(row))] def getExtentAddress(self, extent): fromCellCoordinate = extent[:2] @@ -90,8 +84,7 @@ def getExtentAddress(self, extent): self.cellAddressFromPointCoordinate(fromCellCoordinate) colTo, rowTo = \ self.cellAddressFromPointCoordinate(toCellCoordinate) - return [min(colFrom, colTo), min(rowFrom, rowTo), - max(colFrom, colTo), max(rowFrom, rowTo)] + return [min(colFrom, colTo), min(rowFrom, rowTo), max(colFrom, colTo), max(rowFrom, rowTo)] def inExtent(self, pointCoordinate): [x, y] = pointCoordinate diff --git a/gatilegrid/tilegrids.py b/gatilegrid/tilegrids.py index 610615d..6596bae 100644 --- a/gatilegrid/tilegrids.py +++ b/gatilegrid/tilegrids.py @@ -1,8 +1,4 @@ -# -*- coding: utf-8 -*- - import math -from past.builtins import xrange - EPSG4326_METERS_PER_UNIT = math.pi * 6378137 / 180 # Standard rendered pixel size as defined by OGC standards @@ -165,13 +161,19 @@ def resolutions(self, tmsCompatible, tileSizePx): self.resFact = 180.0 / tileSizePx # Cesium terrain else: self.resFact = 360.0 / tileSizePx # OpenLayers - self.RESOLUTIONS = [self.resFact / 2 ** z for z in range(0, 25)] + self.RESOLUTIONS = [self.resFact / 2**z for z in range(0, 25)] class _TileGrid(object): - def __init__(self, extent=None, tileSizePx=256.0, originCorner='top-left', - tmsCompatible=None, useSwissExtent=True): + def __init__( + self, + extent=None, + tileSizePx=256.0, + originCorner='top-left', + tmsCompatible=None, + useSwissExtent=True + ): assert originCorner in ('bottom-left', 'top-left') self.originCorner = originCorner @@ -240,10 +242,7 @@ def tileAddress(self, zoom, point): col = max(0, col - 1) if y in (self.MINY, self.MAXY) and row.is_integer(): row = max(0, row - 1) - return [ - int(math.floor(col)), - int(math.floor(row)) - ] + return [int(math.floor(col)), int(math.floor(row))] def intersectsExtent(self, extent): "Determine if an extent intersects this instance extent" @@ -257,10 +256,10 @@ def iterGrid(self, minZoom, maxZoom): assert maxZoom in range(0, len(self.RESOLUTIONS)) assert minZoom <= maxZoom - for zoom in xrange(minZoom, maxZoom + 1): + for zoom in range(minZoom, maxZoom + 1): [minRow, minCol, maxRow, maxCol] = self.getExtentAddress(zoom) - for row in xrange(minRow, maxRow + 1): - for col in xrange(minCol, maxCol + 1): + for row in range(minRow, maxRow + 1): + for col in range(minCol, maxCol + 1): tileBounds = self.tileBounds(zoom, col, row) yield (tileBounds, zoom, col, row) @@ -287,7 +286,7 @@ def totalNumberOfTiles(self, minZoom=None, maxZoom=None): maxZoom = maxZoom + 1 else: maxZoom = len(self.RESOLUTIONS) - for zoom in xrange(minZoom, maxZoom): + for zoom in range(minZoom, maxZoom): nbTiles += self.numberOfTilesAtZoom(zoom) return nbTiles @@ -437,13 +436,9 @@ def ySpan(self): class GeoadminTileGridLV03(_LV03Base, _TileGrid): - def __init__(self, - extent=None, - tileSizePx=256.0, - originCorner='top-left', - useSwissExtent=True): + def __init__(self, extent=None, tileSizePx=256.0, originCorner='top-left', useSwissExtent=True): - super(GeoadminTileGridLV03, self).__init__( + super().__init__( extent=extent, tileSizePx=tileSizePx, originCorner=originCorner, @@ -453,13 +448,9 @@ def __init__(self, class GeoadminTileGridLV95(_LV95Base, _TileGrid): - def __init__(self, - extent=None, - tileSizePx=256.0, - originCorner='top-left', - useSwissExtent=True): + def __init__(self, extent=None, tileSizePx=256.0, originCorner='top-left', useSwissExtent=True): - super(GeoadminTileGridLV95, self).__init__( + super().__init__( extent=extent, tileSizePx=tileSizePx, originCorner=originCorner, @@ -469,13 +460,9 @@ def __init__(self, class GlobalMercatorTileGrid(_MercatorBase, _TileGrid): - def __init__(self, - extent=None, - tileSizePx=256.0, - originCorner='top-left', - useSwissExtent=True): + def __init__(self, extent=None, tileSizePx=256.0, originCorner='top-left', useSwissExtent=True): - super(GlobalMercatorTileGrid, self).__init__( + super().__init__( extent=extent, tileSizePx=tileSizePx, originCorner=originCorner, @@ -485,14 +472,16 @@ def __init__(self, class GlobalGeodeticTileGrid(_GeodeticBase, _TileGrid): - def __init__(self, - extent=None, - tileSizePx=256.0, - originCorner='top-left', - tmsCompatible=True, - useSwissExtent=True): + def __init__( + self, + extent=None, + tileSizePx=256.0, + originCorner='top-left', + tmsCompatible=True, + useSwissExtent=True + ): - super(GlobalGeodeticTileGrid, self).__init__( + super().__init__( extent=extent, tileSizePx=tileSizePx, originCorner=originCorner, diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..62ec009 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,38 @@ +[build-system] +requires = ["setuptools>=77.0.0", "build", "setuptools-git-versioning<2"] +build-backend = "setuptools.build_meta" + +[project] +name = "gatilegrid" +requires-python = ">=3.0" +description = "Popular tile grids and grids API for web mapping applications" +readme = "README.md" +license = "MIT" +authors = [ + {name = "Loic Gasser", email = "loicgasser4@gmail.com"} +] +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Topic :: Scientific/Engineering :: GIS", + "Topic :: Software Development :: Libraries :: Python Modules" +] +keywords = ["gis", "wmts", "grid", "map"] +dynamic = ["version"] + +[project.urls] +Homepage = "https://github.com/geoadmin/lib-gatilegrid" +Documentation = "https://github.com/geoadmin/lib-gatilegrid#readme" +Source = "https://github.com/geoadmin/lib-gatilegrid" +Tracker = "https://github.com/geoadmin/lib-gatilegrid/issues" + +[tool.setuptools.packages.find] +include = ["gatilegrid*"] + +[tool.setuptools-git-versioning] +enabled = true +dev_template = "{tag}+git.{sha}" +dirty_template = "{tag}+git.{sha}.dirty" diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index b88034e..0000000 --- a/setup.cfg +++ /dev/null @@ -1,2 +0,0 @@ -[metadata] -description-file = README.md diff --git a/setup.py b/setup.py deleted file mode 100644 index 15bce1f..0000000 --- a/setup.py +++ /dev/null @@ -1,46 +0,0 @@ -# -*- coding: utf-8 -*- - -# HACK for `nose.collector` to work on python 2.7.3 and earlier -import multiprocessing -import os -from setuptools import setup - - -with open("README.md", "r") as fh: - long_description = fh.read() - - -setup(name='gatilegrid', - version='0.2.0', - description='Popular tile grids and grids API for web mapping applications', - keywords='gis wmts grid map', - author='Loic Gasser', - author_email='loicgasser4@gmail.com', - license='MIT', - url='https://github.com/geoadmin/lib-gatilegrid', - packages=['gatilegrid'], - package_dir={'gatilegrid': 'gatilegrid'}, - include_package_data=True, - classifiers=[ - 'Development Status :: 5 - Production/Stable', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: MIT License', - 'Operating System :: OS Independent', - 'Programming Language :: Python', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', - 'Topic :: Scientific/Engineering :: GIS', - 'Topic :: Software Development :: Libraries :: Python Modules' - ], - zip_safe=False, - long_description=long_description, - long_description_content_type='text/markdown', - test_suite='nose.collector', - install_requires=['future'], - python_requires='>2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4', - ) diff --git a/tests/__init__.py b/tests/__init__.py index 40a96af..e69de29 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1 +0,0 @@ -# -*- coding: utf-8 -*- diff --git a/tests/test_grid.py b/tests/test_grid.py index 7e3c435..b80f1a8 100644 --- a/tests/test_grid.py +++ b/tests/test_grid.py @@ -1,6 +1,5 @@ -# -*- coding: utf-8 -*- - import unittest + from gatilegrid.grid import Grid diff --git a/tests/test_tilegrids.py b/tests/test_tilegrids.py index f67993a..76d1b6d 100644 --- a/tests/test_tilegrids.py +++ b/tests/test_tilegrids.py @@ -1,9 +1,11 @@ -# -*- coding: utf-8 -*- - import math import unittest -from gatilegrid import getTileGrid, GeoadminTileGridLV03, \ - GeoadminTileGridLV95, GlobalMercatorTileGrid, GlobalGeodeticTileGrid + +from gatilegrid import GeoadminTileGridLV03 +from gatilegrid import GeoadminTileGridLV95 +from gatilegrid import GlobalGeodeticTileGrid +from gatilegrid import GlobalMercatorTileGrid +from gatilegrid import getTileGrid class TestGeoadminTileGrid(unittest.TestCase): @@ -31,8 +33,7 @@ def testTileGridWrongExtent(self): GeoadminTileGridLV03(extent=[10.0, 10.0, 20.0, 20.0]) with self.assertRaises(AssertionError): - GeoadminTileGridLV03( - extent=[430000.0, 40000.0, 420000.0, 340000.0]) + GeoadminTileGridLV03(extent=[430000.0, 40000.0, 420000.0, 340000.0]) def testTileGridWrongOrigin(self): with self.assertRaises(AssertionError): @@ -42,8 +43,7 @@ def testTileSize(self): gagrid = GeoadminTileGridLV03() ts = gagrid.tileSize(20) self.assertEqual(ts, 2560.0) - self.assertEqual(gagrid.tileAddressTemplate, - '{zoom}/{tileRow}/{tileCol}') + self.assertEqual(gagrid.tileAddressTemplate, '{zoom}/{tileRow}/{tileCol}') with self.assertRaises(AssertionError): gagrid.tileSize(40) @@ -205,8 +205,12 @@ def testGetScaleLV95(self): def testIterGridWithExtent(self): offset = 20000.0 gagridDefault = GeoadminTileGridLV03() - extent = [gagridDefault.MINX + offset, gagridDefault.MINY + offset, - gagridDefault.MAXX - offset, gagridDefault.MAXY - offset] + extent = [ + gagridDefault.MINX + offset, + gagridDefault.MINY + offset, + gagridDefault.MAXX - offset, + gagridDefault.MAXY - offset + ] gagridExtent = GeoadminTileGridLV03(extent=extent) self.assertGreater(gagridDefault.xSpan, gagridExtent.xSpan) @@ -331,9 +335,9 @@ def testNumberOfTilesMercator(self): self.assertEqual(nb, 16) def testNumberOfTilesGeodetic(self): - grid = GlobalGeodeticTileGrid(originCorner='top-left', - tmsCompatible=False, - useSwissExtent=False) + grid = GlobalGeodeticTileGrid( + originCorner='top-left', tmsCompatible=False, useSwissExtent=False + ) zoom = 0 nb = grid.numberOfTilesAtZoom(zoom) nbx = grid.numberOfXTilesAtZoom(zoom) @@ -362,9 +366,9 @@ def testNumberOfTilesGeodetic(self): self.assertEqual(nb, nbx * nby) self.assertEqual(nb, 32) - grid = GlobalGeodeticTileGrid(originCorner='top-left', - tmsCompatible=True, - useSwissExtent=False) + grid = GlobalGeodeticTileGrid( + originCorner='top-left', tmsCompatible=True, useSwissExtent=False + ) zoom = 0 nb = grid.numberOfTilesAtZoom(zoom) @@ -394,9 +398,9 @@ def testNumberOfTilesGeodetic(self): self.assertEqual(nb, nbx * nby) self.assertEqual(nb, 128) - grid = GlobalGeodeticTileGrid(originCorner='bottom-left', - tmsCompatible=False, - useSwissExtent=False) + grid = GlobalGeodeticTileGrid( + originCorner='bottom-left', tmsCompatible=False, useSwissExtent=False + ) zoom = 0 nb = grid.numberOfTilesAtZoom(zoom) nbx = grid.numberOfXTilesAtZoom(zoom) @@ -415,8 +419,9 @@ def testNumberOfTilesGeodetic(self): self.assertEqual(nb, nbx * nby) self.assertEqual(nb, 8) - grid = GlobalGeodeticTileGrid(originCorner='bottom-left', - tmsCompatible=True, useSwissExtent=False) + grid = GlobalGeodeticTileGrid( + originCorner='bottom-left', tmsCompatible=True, useSwissExtent=False + ) zoom = 0 nb = grid.numberOfTilesAtZoom(zoom) nbx = grid.numberOfXTilesAtZoom(zoom) @@ -474,9 +479,9 @@ def testMercatorGridBoundsAndAddress(self): self.assertEqual(ya, y) def testGeodeticGridBoundsAndAddress(self): - grid = GlobalGeodeticTileGrid(originCorner='top-left', - tmsCompatible=True, - useSwissExtent=False) + grid = GlobalGeodeticTileGrid( + originCorner='top-left', tmsCompatible=True, useSwissExtent=False + ) [z, x, y] = [8, 268, 60] [xmin, ymin, xmax, ymax] = grid.tileBounds(z, x, y) self.assertAlmostEqual(xmin, 8.4375) @@ -491,9 +496,9 @@ def testGeodeticGridBoundsAndAddress(self): self.assertEqual(ya, y) [z, x, y] = [8, 266, 193] - grid = GlobalGeodeticTileGrid(originCorner='bottom-left', - tmsCompatible=True, - useSwissExtent=False) + grid = GlobalGeodeticTileGrid( + originCorner='bottom-left', tmsCompatible=True, useSwissExtent=False + ) [xmin, ymin, xmax, ymax] = grid.tileBounds(z, x, y) self.assertAlmostEqual(xmin, 7.03125) self.assertAlmostEqual(ymin, 45.703125) @@ -508,9 +513,8 @@ def testGeodeticGridBoundsAndAddress(self): def testGetParentTilesGlobalGeodeticTopLeft(self): grid = GlobalGeodeticTileGrid( - originCorner='top-left', - tmsCompatible=False, - useSwissExtent=False) + originCorner='top-left', tmsCompatible=False, useSwissExtent=False + ) nb = grid.numberOfTilesAtZoom(1) self.assertEqual(nb, 2) @@ -537,9 +541,8 @@ def testGetParentTilesGlobalGeodeticTopLeft(self): def testGetParentTilesGlobalGeodeticTopLeftUseSwiss(self): grid = GlobalGeodeticTileGrid( - originCorner='top-left', - tmsCompatible=False, - useSwissExtent=True) + originCorner='top-left', tmsCompatible=False, useSwissExtent=True + ) nb = grid.numberOfTilesAtZoom(1) self.assertEqual(nb, 1) @@ -566,9 +569,8 @@ def testGetParentTilesGlobalGeodeticTopLeftUseSwiss(self): def testGetParentTilesGlobalGeodeticBottomLeft(self): grid = GlobalGeodeticTileGrid( - originCorner='bottom-left', - tmsCompatible=False, - useSwissExtent=False) + originCorner='bottom-left', tmsCompatible=False, useSwissExtent=False + ) nb = grid.numberOfTilesAtZoom(1) self.assertEqual(nb, 2) @@ -611,9 +613,9 @@ def testGetParentTilesLV95(self): # Check that the tile extent intersects the parent tile childExtent = grid.tileBounds(z, x, y) - parentExtent = grid.tileBounds(tileAddresses[0][0], - tileAddresses[0][1], - tileAddresses[0][2]) + parentExtent = grid.tileBounds( + tileAddresses[0][0], tileAddresses[0][1], tileAddresses[0][2] + ) self.assertLessEqual(parentExtent[0], childExtent[2]) self.assertLessEqual(parentExtent[1], childExtent[3]) self.assertGreaterEqual(parentExtent[2], childExtent[0]) @@ -654,9 +656,9 @@ def testGetParentTilesLV03(self): # Check that the tile extent intersects the parent tile childExtent = grid.tileBounds(z, x, y) - parentExtent = grid.tileBounds(tileAddresses[0][0], - tileAddresses[0][1], - tileAddresses[0][2]) + parentExtent = grid.tileBounds( + tileAddresses[0][0], tileAddresses[0][1], tileAddresses[0][2] + ) self.assertLessEqual(parentExtent[0], childExtent[2]) self.assertLessEqual(parentExtent[1], childExtent[3]) self.assertGreaterEqual(parentExtent[2], childExtent[0]) diff --git a/tests/unittest.cfg b/tests/unittest.cfg new file mode 100644 index 0000000..046b53c --- /dev/null +++ b/tests/unittest.cfg @@ -0,0 +1,15 @@ +[unittest] +plugins = nose2.plugins.junitxml + +[junit-xml] +always-on = True + +[log-capture] +always-on = True +# clear the handler to not polute the stdout and stderr with logging messages +clear-handlers = True +log-level = DEBUG + +[coverage] +always-on = True +coverage = gatilegrid