From 3a39615886c4b5a58e6a35cbd366a7efa4b0328a Mon Sep 17 00:00:00 2001 From: loicgasser Date: Tue, 26 Jan 2021 14:42:31 -0500 Subject: [PATCH] Drop py 2 support and migrate to github actions --- .flake8 | 10 --- .github/workflows/main.yml | 62 +++++++++++++ .gitignore | 1 + .travis.yml | 22 ----- LICENSE => LICENSE.txt | 2 +- README.md | 2 +- dev-requirements.txt | 2 +- doc/source/index.rst | 2 +- quantized_mesh_tile/bbsphere.py | 27 +++--- quantized_mesh_tile/cartesian3d.py | 6 +- quantized_mesh_tile/global_geodetic.py | 23 ++--- .../horizon_occlusion_point.py | 20 ++--- quantized_mesh_tile/llh_ecef.py | 22 ++--- quantized_mesh_tile/terrain.py | 45 +++++----- quantized_mesh_tile/topology.py | 10 +-- quantized_mesh_tile/utils.py | 29 +++--- setup.cfg | 12 +++ setup.py | 19 +++- tests/test_bbsphere.py | 6 +- tests/test_collapse_to_triangle.py | 89 +++++++++---------- tests/test_encode_decode.py | 33 ++++--- tests/test_terrain_tile.py | 5 -- 22 files changed, 236 insertions(+), 213 deletions(-) delete mode 100644 .flake8 create mode 100644 .github/workflows/main.yml delete mode 100644 .travis.yml rename LICENSE => LICENSE.txt (97%) diff --git a/.flake8 b/.flake8 deleted file mode 100644 index 4e999ee..0000000 --- a/.flake8 +++ /dev/null @@ -1,10 +0,0 @@ -[flake8] -exclude = .git,__pycache__,doc,setup.py,venv -max-line-length = 90 -ignore= - E128, - E221, - E241, - E251, - E402, - W504, diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..1fbfa4e --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,62 @@ +name: CI + +on: + push: + branches: [master] + tags: + - "*" + pull_request: + +jobs: + tests: + strategy: + fail-fast: false + matrix: + python-version: [3.5, 3.6, 3.7, 3.8, 3.9] + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + architecture: x64 + + - name: Display Python version + run: python -c "import sys; print(sys.version)" + + - name: Install dependencies + run: | + sudo apt-get update -y + sudo apt-get install -y python-dev libgeos-dev + pip install -U pip setuptools + pip install -r dev-requirements.txt + + - name: Lint + run: flake8 + + - name: Isort + run: isort --check-only --diff . + + - name: Test + run: coverage run --source=quantized_mesh_tile setup.py test + + - name: Coveralls + uses: AndreMiras/coveralls-python-action@develop + with: + github-token: ${{ secrets.github_token }} + flag-name: run-${{ matrix.python-version }} + parallel: true + + coveralls: + needs: tests + runs-on: ubuntu-latest + steps: + - name: Coveralls Finished + uses: AndreMiras/coveralls-python-action@develop + with: + github-token: ${{ secrets.github_token }} + parallel-finished: true diff --git a/.gitignore b/.gitignore index a274a93..a64b28e 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ doc/build/ .idea/ .coverage .vscode +.eggs diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index d884d8b..0000000 --- a/.travis.yml +++ /dev/null @@ -1,22 +0,0 @@ -dist: xenial -language: python -python: - - "2.7" - - "3.6" - - "3.7" -before_install: -- sudo apt-get remove -y libgdal1 -- sudo add-apt-repository -y ppa:ubuntugis/ubuntugis-unstable -- sudo apt-get update -y -- sudo apt-get install -y python-dev libgeos-dev -- pip install -r dev-requirements.txt -install: -- pip install nose -- pip install flake8 -- pip install coveralls -- pip install isort -script: -- flake8 -- isort --recursive --check-only --diff --skip .eggs -- coverage run --source=quantized_mesh_tile setup.py test -after_success: coveralls diff --git a/LICENSE b/LICENSE.txt similarity index 97% rename from LICENSE rename to LICENSE.txt index ab84b7f..c825491 100644 --- a/LICENSE +++ b/LICENSE.txt @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2016 Gasser Loïc +Copyright (c) 2021 Gasser Loïc Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 5abe4ab..79b3dcd 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ quantized-mesh-tile =================== -[![Build Status](https://travis-ci.org/loicgasser/quantized-mesh-tile.svg?branch=master)](https://travis-ci.org/loicgasser/quantized-mesh-tile) +![Build Status](https://github.com/loicgasser/quantized-mesh-tile/workflows/CI/badge.svg?branch=master) [![Coverage Status](https://coveralls.io/repos/github/loicgasser/quantized-mesh-tile/badge.svg?branch=master)](https://coveralls.io/github/loicgasser/quantized-mesh-tile?branch=master) [![Doc Status](https://readthedocs.org/projects/quantized-mesh-tile/badge/?version=latest)](http://quantized-mesh-tile.readthedocs.io/en/latest/?badge=latest) diff --git a/dev-requirements.txt b/dev-requirements.txt index e6fc7be..79eff8e 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,6 +1,6 @@ coveralls flake8 -future +isort mock==1.0.1 nose sphinx diff --git a/doc/source/index.rst b/doc/source/index.rst index a796c33..f237cbc 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -17,7 +17,7 @@ Reference Documentation Requirements ------------ -Quantized mesh tile requires Python >=2.7 (not including Python 3.x) and GEOS >= 3.3. +Quantized mesh tile requires Python >= 3.5 and GEOS >= 3.3. Installation ------------ diff --git a/quantized_mesh_tile/bbsphere.py b/quantized_mesh_tile/bbsphere.py index 3c126b8..3ef575d 100644 --- a/quantized_mesh_tile/bbsphere.py +++ b/quantized_mesh_tile/bbsphere.py @@ -1,12 +1,6 @@ # -*- coding: utf-8 -*- -from __future__ import absolute_import, division - import math -from builtins import map, object - -from past.builtins import xrange -from past.utils import old_div from . import cartesian3d as c3d @@ -15,8 +9,9 @@ class BoundingSphere(object): def __init__(self, *args, **kwargs): MAX = float('infinity') MIN = float('-infinity') - self.center = list(map(float, kwargs.get('center', []))) - self.radius = float(kwargs.get('radius', 0)) + + self.center = kwargs.get('center', []) + self.radius = kwargs.get('radius', 0) self.minPointX = [MAX, MAX, MAX] self.minPointY = [MAX, MAX, MAX] self.minPointZ = [MAX, MAX, MAX] @@ -31,7 +26,7 @@ def fromPoints(self, points): if nbPositions < 2: raise Exception('Your list of points must contain at least 2 points') - for i in xrange(0, nbPositions): + for i in range(0, nbPositions): point = points[i] # Store the points containing the smallest and largest component @@ -86,7 +81,7 @@ def fromPoints(self, points): naiveCenter = c3d.multiplyByScalar(c3d.add(minBoxPt, maxBoxPt), 0.5) naiveRadius = 0.0 - for i in xrange(0, nbPositions): + for i in range(0, nbPositions): currentP = points[i] # Find the furthest point from the naive center to calculate the naive radius. @@ -104,12 +99,12 @@ def fromPoints(self, points): # Calculate center of new Ritter sphere oldToNew = oldCenterToPoint - ritterRadius ritterCenter = [ - old_div((ritterRadius * ritterCenter[0] + oldToNew * currentP[0]), - oldCenterToPoint), - old_div((ritterRadius * ritterCenter[1] + oldToNew * currentP[1]), - oldCenterToPoint), - old_div((ritterRadius * ritterCenter[2] + oldToNew * currentP[2]), - oldCenterToPoint) + (ritterRadius * ritterCenter[0] + + oldToNew * currentP[0]) / oldCenterToPoint, + (ritterRadius * ritterCenter[1] + + oldToNew * currentP[1]) / oldCenterToPoint, + (ritterRadius * ritterCenter[2] + + oldToNew * currentP[2]) / oldCenterToPoint ] # Keep the naive sphere if smaller diff --git a/quantized_mesh_tile/cartesian3d.py b/quantized_mesh_tile/cartesian3d.py index da480f2..c6a0073 100644 --- a/quantized_mesh_tile/cartesian3d.py +++ b/quantized_mesh_tile/cartesian3d.py @@ -1,11 +1,7 @@ # -*- coding: utf-8 -*- -from __future__ import division - import math -from past.utils import old_div - def magnitudeSquared(p): return p[0] ** 2 + p[1] ** 2 + p[2] ** 2 @@ -37,4 +33,4 @@ def multiplyByScalar(p, scalar): def normalize(p): mgn = magnitude(p) - return [old_div(p[0], mgn), old_div(p[1], mgn), old_div(p[2], mgn)] + return [p[0] / mgn, p[1] / mgn, p[2] / mgn] diff --git a/quantized_mesh_tile/global_geodetic.py b/quantized_mesh_tile/global_geodetic.py index 2fc710c..14b79fd 100644 --- a/quantized_mesh_tile/global_geodetic.py +++ b/quantized_mesh_tile/global_geodetic.py @@ -15,12 +15,7 @@ Reference --------- """ -from __future__ import division - import math -from builtins import object, range - -from past.utils import old_div MAXZOOMLEVEL = 32 @@ -46,27 +41,27 @@ class GlobalGeodetic(object): def __init__(self, tmscompatible, tileSize=256): self.tileSize = tileSize if tmscompatible is not None: - self.resFact = old_div(180.0, self.tileSize) + self.resFact = 180.0 / self.tileSize self._numberOfLevelZeroTilesX = 2 self._numberOfLevelZeroTilesY = 1 else: - self.resFact = old_div(360.0, self.tileSize) + self.resFact = 360.0 / self.tileSize self._numberOfLevelZeroTilesX = 1 self._numberOfLevelZeroTilesY = 1 def LonLatToPixels(self, lon, lat, zoom): "Converts lon/lat to pixel coordinates in given zoom of the EPSG:4326 pyramid" - res = old_div(self.resFact, 2 ** zoom) - px = old_div((180 + lon), res) - py = old_div((90 + lat), res) + res = self.resFact / 2 ** zoom + px = (180 + lon) / res + py = (90 + lat) / res return px, py def PixelsToTile(self, px, py): "Returns coordinates of the tile covering region in pixel coordinates" - tx = int(math.ceil(old_div(px, float(self.tileSize))) - 1) if px > 0 else 0 - ty = int(math.ceil(old_div(py, float(self.tileSize))) - 1) if py > 0 else 0 + tx = int(math.ceil(px / float(self.tileSize)) - 1) if px > 0 else 0 + ty = int(math.ceil(py / float(self.tileSize)) - 1) if py > 0 else 0 return tx, ty def LonLatToTile(self, lon, lat, zoom): @@ -78,7 +73,7 @@ def LonLatToTile(self, lon, lat, zoom): def Resolution(self, zoom): "Resolution (arc/pixel) for given zoom level (measured at Equator)" - return old_div(self.resFact, 2 ** zoom) + return self.resFact / 2 ** zoom # return 180 / float( 1 << (8+zoom) ) def ZoomForPixelSize(self, pixelSize): @@ -93,7 +88,7 @@ def ZoomForPixelSize(self, pixelSize): def TileBounds(self, tx, ty, zoom): "Returns bounds of the given tile" - res = old_div(self.resFact, 2 ** zoom) + res = self.resFact / 2 ** zoom return ( tx * self.tileSize * res - 180, ty * self.tileSize * res - 90, diff --git a/quantized_mesh_tile/horizon_occlusion_point.py b/quantized_mesh_tile/horizon_occlusion_point.py index 3743a1f..9fd2e55 100644 --- a/quantized_mesh_tile/horizon_occlusion_point.py +++ b/quantized_mesh_tile/horizon_occlusion_point.py @@ -1,20 +1,16 @@ # -*- coding: utf-8 -*- -from __future__ import absolute_import, division - import math -from builtins import map import numpy as np -from past.utils import old_div from . import cartesian3d as c3d from . import llh_ecef as ecef # Constants taken from http://cesiumjs.org/2013/04/25/Horizon-culling/ -rX = old_div(1.0, ecef.radiusX) -rY = old_div(1.0, ecef.radiusY) -rZ = old_div(1.0, ecef.radiusZ) +rX = 1.0 / ecef.radiusX +rY = 1.0 / ecef.radiusY +rZ = 1.0 / ecef.radiusZ # Functions assumes ellipsoid scaled coordinates @@ -22,16 +18,16 @@ def computeMagnitude(point, sphereCenter): magnitudeSquared = c3d.magnitudeSquared(point) magnitude = math.sqrt(magnitudeSquared) - direction = c3d.multiplyByScalar(point, old_div(1, magnitude)) + direction = c3d.multiplyByScalar(point, 1 / magnitude) magnitudeSquared = max(1.0, magnitudeSquared) magnitude = max(1.0, magnitude) cosAlpha = np.dot(direction, sphereCenter) sinAlpha = c3d.magnitude(np.cross(direction, sphereCenter)) - cosBeta = old_div(1.0, magnitude) + cosBeta = 1.0 / magnitude sinBeta = math.sqrt(magnitudeSquared - 1.0) * cosBeta - return old_div(1.0, (cosAlpha * cosBeta - sinAlpha * sinBeta)) + return 1.0 / (cosAlpha * cosBeta - sinAlpha * sinBeta) # https://cesiumjs.org/2013/05/09/Computing-the-horizon-occlusion-point/ @@ -43,11 +39,11 @@ def fromPoints(points, boundingSphere): # Bring coordinates to ellipsoid scaled coordinates def scaleDown(coord): return [coord[0] * rX, coord[1] * rY, coord[2] * rZ] - scaledPoints = list(map(scaleDown, points)) + scaledPoints = [scaleDown(coord) for coord in points] scaledSphereCenter = scaleDown(boundingSphere.center) def magnitude(coord): return computeMagnitude(coord, scaledSphereCenter) - magnitudes = list(map(magnitude, scaledPoints)) + magnitudes = [magnitude(coord) for coord in scaledPoints] return c3d.multiplyByScalar(scaledSphereCenter, max(magnitudes)) diff --git a/quantized_mesh_tile/llh_ecef.py b/quantized_mesh_tile/llh_ecef.py index db5b307..f0d6ff1 100644 --- a/quantized_mesh_tile/llh_ecef.py +++ b/quantized_mesh_tile/llh_ecef.py @@ -1,11 +1,7 @@ # -*- coding: utf-8 -*- -from __future__ import division - import math -from past.utils import old_div - # Constants taken from http://cesiumjs.org/2013/04/25/Horizon-culling/ radiusX = 6378137.0 radiusY = 6378137.0 @@ -23,12 +19,11 @@ def LLH2ECEF(lon, lat, alt): - lat *= (old_div(math.pi, 180.0)) - lon *= (old_div(math.pi, 180.0)) + lat *= (math.pi / 180.0) + lon *= (math.pi / 180.0) def n(x): - return old_div(wgs84_a, math.sqrt( - 1 - wgs84_e2 * (math.sin(x) ** 2))) + return wgs84_a / math.sqrt(1 - wgs84_e2 * (math.sin(x) ** 2)) x = (n(lat) + alt) * math.cos(lat) * math.cos(lon) y = (n(lat) + alt) * math.cos(lat) * math.sin(lon) @@ -40,7 +35,7 @@ def n(x): def ECEF2LLH(x, y, z): - ep = math.sqrt(old_div((wgs84_a2 - wgs84_b2), wgs84_b2)) + ep = math.sqrt((wgs84_a2 - wgs84_b2) / wgs84_b2) p = math.sqrt(x ** 2 + y ** 2) th = math.atan2(wgs84_a * z, wgs84_b * p) lon = math.atan2(y, x) @@ -48,10 +43,11 @@ def ECEF2LLH(x, y, z): z + ep ** 2 * wgs84_b * math.sin(th) ** 3, p - wgs84_e2 * wgs84_a * math.cos(th) ** 3 ) - N = old_div(wgs84_a, math.sqrt(1 - wgs84_e2 * math.sin(lat) ** 2)) - alt = old_div(p, math.cos(lat)) - N + N = wgs84_a / math.sqrt(1 - wgs84_e2 * math.sin(lat) ** 2) + alt = p / math.cos(lat) - N - lon *= (old_div(180., math.pi)) - lat *= (old_div(180., math.pi)) + r = 180 / math.pi + lon *= r + lat *= r return [lon, lat, alt] diff --git a/quantized_mesh_tile/terrain.py b/quantized_mesh_tile/terrain.py index a19e91b..6df6164 100644 --- a/quantized_mesh_tile/terrain.py +++ b/quantized_mesh_tile/terrain.py @@ -5,18 +5,12 @@ Reference --------- """ -from __future__ import absolute_import, division import gzip import io import os -from builtins import map, object from collections import OrderedDict -from future import standard_library -from past.builtins import xrange -from past.utils import old_div - from . import horizon_occlusion_point as occ from .bbsphere import BoundingSphere from .topology import TerrainTopology @@ -24,8 +18,6 @@ octEncode, packEntry, packIndices, ungzipFileObject, unpackEntry, zigZagDecode, zigZagEncode) -standard_library.install_aliases() - # For a tile of 256px * 256px TILEPXS = 65536 @@ -237,12 +229,16 @@ def __repr__(self): msg += '\nnorthIndicesCount: %s' % len(self.northI) msg += '\nnorthIndices: %s\n' % self.northI # Output coordinates - msg += '\nNumber of triangles: %s' % (old_div(len(self.indices), 3)) + msg += '\nNumber of triangles: %s' % (len(self.indices) / 3) msg += '\nTriangles coordinates in EPSG %s' % self.EPSG msg += '\n%s' % self.getTrianglesCoordinates() return msg + @property + def bounds(self): + return [self._west, self._south, self._east, self._north] + def getContentType(self): """ A method to determine the content type of a tile. @@ -277,7 +273,7 @@ def getTrianglesCoordinates(self): nbTriangles = len(self.indices) if nbTriangles % 3 != 0: raise Exception('Corrupted tile') - for i in xrange(0, nbTriangles - 1, 3): + for i in range(0, nbTriangles - 1, 3): vi1 = self.indices[i] vi2 = self.indices[i + 1] vi3 = self.indices[i + 2] @@ -302,16 +298,16 @@ def _computeVerticesCoordinates(self): if not self._longs: for u in self.u: self._longs.append( - lerp(self._west, self._east, old_div(float(u), self.MAX))) + lerp(self._west, self._east, u / self.MAX)) for v in self.v: self._lats.append( - lerp(self._south, self._north, old_div(float(v), self.MAX))) + lerp(self._south, self._north, v / self.MAX)) for h in self.h: self._heights.append( lerp( self.header['minimumHeight'], self.header['maximumHeight'], - old_div(float(h), self.MAX) + h / self.MAX ) ) @@ -435,7 +431,7 @@ def _iterUnpackAndDecodeLight(f, extensionLength, structType): A private method to iteratively unpack light vector. """ i = 0 - xyCount = old_div(extensionLength, 2) + xyCount = extensionLength / 2 while i != xyCount: yield octDecode( unpackEntry( @@ -535,12 +531,12 @@ def toFile(self, filePath, gzipped=False): def _getWorkingUnitLatitude(self): if not self._workingUnitLatitude: - self._workingUnitLatitude = old_div(self.MAX, (self._north - self._south)) + self._workingUnitLatitude = self.MAX / (self._north - self._south) return self._workingUnitLatitude def _getWorkingUnitLongitude(self): if not self._workingUnitLongitude: - self._workingUnitLongitude = old_div(self.MAX, (self._east - self._west)) + self._workingUnitLongitude = self.MAX / (self._east - self._west) return self._workingUnitLongitude def _getDeltaHeight(self): @@ -564,7 +560,7 @@ def _quantizeHeight(self, height): if deniv == 0: h = 0 else: - workingUnitHeight = old_div(self.MAX, deniv) + workingUnitHeight = self.MAX / deniv h = int(round((height - self.header['minimumHeight']) * workingUnitHeight)) return h @@ -577,7 +573,7 @@ def _dequantizeHeight(self, h): """ return lerp(self.header['minimumHeight'], self.header['maximumHeight'], - old_div(float(h), self.MAX)) + h / self.MAX) def _writeTo(self, f): """ @@ -596,7 +592,7 @@ def _writeTo(self, f): packEntry( TerrainTile.vertexData['uVertexCount'], zigZagEncode(self.u[0])) ) - for i in xrange(0, vertexCount - 1): + for i in range(0, vertexCount - 1): ud = self.u[i + 1] - self.u[i] f.write( packEntry(TerrainTile.vertexData['uVertexCount'], zigZagEncode(ud))) @@ -604,7 +600,7 @@ def _writeTo(self, f): packEntry( TerrainTile.vertexData['uVertexCount'], zigZagEncode(self.v[0])) ) - for i in xrange(0, vertexCount - 1): + for i in range(0, vertexCount - 1): vd = self.v[i + 1] - self.v[i] f.write( packEntry(TerrainTile.vertexData['vVertexCount'], zigZagEncode(vd))) @@ -612,7 +608,7 @@ def _writeTo(self, f): packEntry( TerrainTile.vertexData['uVertexCount'], zigZagEncode(self.h[0])) ) - for i in xrange(0, vertexCount - 1): + for i in range(0, vertexCount - 1): hd = self.h[i + 1] - self.h[i] f.write( packEntry( @@ -624,8 +620,7 @@ def _writeTo(self, f): if vertexCount > TerrainTile.BYTESPLIT: meta = TerrainTile.indexData32 - f.write(packEntry(meta['triangleCount'], - old_div(len(self.indices), 3))) + f.write(packEntry(meta['triangleCount'], len(self.indices) // 3)) ind = encodeIndices(self.indices) packIndices(f, meta['indices'], ind) @@ -659,7 +654,7 @@ def _writeTo(self, f): f.write(packEntry(meta['extensionLength'], 2 * vertexCount)) metaV = TerrainTile.OctEncodedVertexNormals - for i in xrange(0, vertexCount): + for i in range(0, vertexCount): x, y = octEncode(self.vLight[i]) f.write(packEntry(metaV['xy'], x)) f.write(packEntry(metaV['xy'], y)) @@ -679,7 +674,7 @@ def _writeTo(self, f): 'Unexpected number of rows for the watermask: %s' % nbRows ) # From North to South - for i in xrange(0, nbRows): + for i in range(0, nbRows): x = self.watermask[i] if len(x) != 256: raise Exception( diff --git a/quantized_mesh_tile/topology.py b/quantized_mesh_tile/topology.py index 445e1d4..b4f7cf3 100644 --- a/quantized_mesh_tile/topology.py +++ b/quantized_mesh_tile/topology.py @@ -3,14 +3,10 @@ Reference --------- """ -from __future__ import division import math -from builtins import object import numpy as np -from past.utils import old_div - from shapely.geometry.base import BaseGeometry from shapely.geometry.polygon import Polygon from shapely.wkb import loads as load_wkb @@ -107,7 +103,7 @@ def __repr__(self): msg += '\n%s' % len(self.indexData) msg += '\nindexData list:' msg += '\n%s' % self.indexData - msg += '\nNumber of triangles: %s' % (old_div(len(self.indexData), 3)) + msg += '\nNumber of triangles: %s' % (len(self.indexData) / 3) return msg def addGeometries(self, geometries): @@ -239,8 +235,8 @@ def _assureCounterClockWise(self, vertices): http://stackoverflow.com/questions/1709283/\ how-can-i-sort-a-coordinate-list-for-a-rectangle-counterclockwise """ - mlat = old_div(sum(coord[0] for coord in vertices), float(len(vertices))) - mlon = old_div(sum(coord[1] for coord in vertices), float(len(vertices))) + mlat = sum(coord[0] for coord in vertices) / float(len(vertices)) + mlon = sum(coord[1] for coord in vertices) / float(len(vertices)) def algo(coord): return (math.atan2(coord[0] - mlat, coord[1] - mlon) + 2 * math.pi) % ( diff --git a/quantized_mesh_tile/utils.py b/quantized_mesh_tile/utils.py index 161f4cb..5b66798 100644 --- a/quantized_mesh_tile/utils.py +++ b/quantized_mesh_tile/utils.py @@ -1,21 +1,14 @@ # -*- coding: utf-8 -*- -from __future__ import absolute_import, division - import gzip import io import math from struct import calcsize, pack, unpack import numpy as np -from future import standard_library -from past.builtins import xrange -from past.utils import old_div from . import cartesian3d as c3d -standard_library.install_aliases() - EPSILON6 = 0.000001 @@ -94,8 +87,8 @@ def octEncode(vec): raise ValueError('Only normalized vectors are supported') res = [0.0, 0.0] l1Norm = float(abs(vec[0]) + abs(vec[1]) + abs(vec[2])) - res[0] = old_div(vec[0], l1Norm) - res[1] = old_div(vec[1], l1Norm) + res[0] = vec[0] / l1Norm + res[1] = vec[1] / l1Norm if vec[2] < 0.0: x = res[0] @@ -124,9 +117,9 @@ def octDecode(x, y): def centroid(a, b, c): - return [old_div(sum((a[0], b[0], c[0])), 3), - old_div(sum((a[1], b[1], c[1])), 3), - old_div(sum([a[2], b[2], c[2]]), 3)] + return [sum((a[0], b[0], c[0])) / 3, + sum((a[1], b[1], c[1])) / 3, + sum([a[2], b[2], c[2]]) / 3] # Based on the vectors defining the plan @@ -148,7 +141,7 @@ def computeNormals(vertices, faces): areasPerFace = [0.0] * numFaces normalsPerVertex = np.zeros(vertices.shape, dtype=vertices.dtype) - for i in xrange(0, numFaces): + for i in range(0, numFaces): face = faces[i] v0 = vertices[face[0]] v1 = vertices[face[1]] @@ -160,13 +153,13 @@ def computeNormals(vertices, faces): areasPerFace[i] = area normalsPerFace[i] = normal - for i in xrange(0, numFaces): + for i in range(0, numFaces): face = faces[i] weightedNormal = [c * areasPerFace[i] for c in normalsPerFace[i]] for j in face: normalsPerVertex[j] = c3d.add(normalsPerVertex[j], weightedNormal) - for i in xrange(0, numVertices): + for i in range(0, numVertices): normalsPerVertex[i] = c3d.normalize(normalsPerVertex[i]) return normalsPerVertex @@ -192,10 +185,10 @@ def getCoordsIndex(n, i): # Creates all the potential pairs of coords -def createCoordsPairs(l): +def createCoordsPairs(c): coordsPairs = [] - for i in xrange(0, len(l)): - coordsPairs.append([l[i], l[(i + 2) % len(l)]]) + for i in range(0, len(c)): + coordsPairs.append([c[i], c[(i + 2) % len(c)]]) return coordsPairs diff --git a/setup.cfg b/setup.cfg index b88034e..b8627de 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,2 +1,14 @@ [metadata] description-file = README.md +[flake8] +exclude = .git,__pycache__,doc,setup.py,.venv,venv,build,dist +max-line-length = 90 +ignore= + E128, + E221, + E241, + E251, + E402, + W504 +[coverage:run] +relative_files = True \ No newline at end of file diff --git a/setup.py b/setup.py index 3fd6a56..6adc8c6 100644 --- a/setup.py +++ b/setup.py @@ -10,14 +10,29 @@ if '/home/docs/checkouts/readthedocs' in os.getcwd(): requires = [] else: - requires = ['future', 'numpy', 'shapely'] + requires = ['numpy', 'shapely'] setup(name='quantized-mesh-tile', version='0.6.1', description='Quantized-Mesh format reader and writer', - author=u'Loic Gasser', + author='Loic Gasser', author_email='loicgasser4@gmail.com', + classifiers=[ + "Development Status :: Beta", + "Environment :: Plugins", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 3.5", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Intended Audience :: Information Technology", + "License :: OSI Approved :: MIT License", + "Topic :: Scientific/Engineering :: GIS", + ], license='MIT', + keywords='gis tile terrain quantized-mesh', url='https://github.com/loicgasser/quantized-mesh-tile', packages=find_packages(exclude=['tests', 'doc']), zip_safe=False, diff --git a/tests/test_bbsphere.py b/tests/test_bbsphere.py index 2e41127..326f96e 100644 --- a/tests/test_bbsphere.py +++ b/tests/test_bbsphere.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- import unittest -from builtins import map import quantized_mesh_tile.cartesian3d as c3d from quantized_mesh_tile.bbsphere import BoundingSphere @@ -74,10 +73,7 @@ def testBoundingSpherePrecision(self): ter = TerrainTile(west=minx, south=miny, east=maxx, north=maxy) ter.fromFile('tests/data/%s_%s_%s.terrain' % (z, x, y)) - def llh2ecef(x): - return LLH2ECEF(x[0], x[1], x[2]) - coords = ter.getVerticesCoordinates() - coords = list(map(llh2ecef, coords)) + coords = [LLH2ECEF(*coord) for coord in ter.getVerticesCoordinates()] sphere = BoundingSphere() sphere.fromPoints(coords) for coord in coords: diff --git a/tests/test_collapse_to_triangle.py b/tests/test_collapse_to_triangle.py index 452d2a4..6747ca3 100644 --- a/tests/test_collapse_to_triangle.py +++ b/tests/test_collapse_to_triangle.py @@ -1,13 +1,12 @@ # -*- coding: utf-8 -*- import unittest -from builtins import str from quantized_mesh_tile.utils import collapseIntoTriangles -def toString(l): - return [str(i) for i in l] +def toString(t): + return ','.join([str(i) for i in t]) class TestCollapseIntoTriangle(unittest.TestCase): @@ -24,13 +23,13 @@ def testCollapseFourNodes(self): self.assertEqual(len(triangles[0]), 3) self.assertEqual(len(triangles[1]), 3) - self.assertEqual(','.join(toString(triangles[0][0])), '1,1,1') - self.assertEqual(','.join(toString(triangles[0][1])), '2,1,1') - self.assertEqual(','.join(toString(triangles[0][2])), '1,2,1') + self.assertEqual(toString(triangles[0][0]), '1,1,1') + self.assertEqual(toString(triangles[0][1]), '2,1,1') + self.assertEqual(toString(triangles[0][2]), '1,2,1') - self.assertEqual(','.join(toString(triangles[1][0])), '1,1,1') - self.assertEqual(','.join(toString(triangles[1][1])), '2,1,1') - self.assertEqual(','.join(toString(triangles[1][2])), '3,2,2') + self.assertEqual(toString(triangles[1][0]), '1,1,1') + self.assertEqual(toString(triangles[1][1]), '2,1,1') + self.assertEqual(toString(triangles[1][2]), '3,2,2') def testCollapseFiveNodes(self): coords = [ @@ -46,17 +45,17 @@ def testCollapseFiveNodes(self): self.assertEqual(len(triangles[1]), 3) self.assertEqual(len(triangles[2]), 3) - self.assertEqual(','.join(toString(triangles[0][0])), '1,1,1') - self.assertEqual(','.join(toString(triangles[0][1])), '2,1,1') - self.assertEqual(','.join(toString(triangles[0][2])), '1,2,1') + self.assertEqual(toString(triangles[0][0]), '1,1,1') + self.assertEqual(toString(triangles[0][1]), '2,1,1') + self.assertEqual(toString(triangles[0][2]), '1,2,1') - self.assertEqual(','.join(toString(triangles[1][0])), '1,1,1') - self.assertEqual(','.join(toString(triangles[1][1])), '3,2,2') - self.assertEqual(','.join(toString(triangles[1][2])), '2,1,1') + self.assertEqual(toString(triangles[1][0]), '1,1,1') + self.assertEqual(toString(triangles[1][1]), '3,2,2') + self.assertEqual(toString(triangles[1][2]), '2,1,1') - self.assertEqual(','.join(toString(triangles[2][0])), '1,1,1') - self.assertEqual(','.join(toString(triangles[2][1])), '3,2,2') - self.assertEqual(','.join(toString(triangles[2][2])), '2,3,3') + self.assertEqual(toString(triangles[2][0]), '1,1,1') + self.assertEqual(toString(triangles[2][1]), '3,2,2') + self.assertEqual(toString(triangles[2][2]), '2,3,3') def testCollapseSixNodes(self): coords = [ @@ -74,21 +73,21 @@ def testCollapseSixNodes(self): self.assertEqual(len(triangles[2]), 3) self.assertEqual(len(triangles[3]), 3) - self.assertEqual(','.join(toString(triangles[0][0])), '1,1,1') - self.assertEqual(','.join(toString(triangles[0][1])), '2,1,1') - self.assertEqual(','.join(toString(triangles[0][2])), '1,2,1') + self.assertEqual(toString(triangles[0][0]), '1,1,1') + self.assertEqual(toString(triangles[0][1]), '2,1,1') + self.assertEqual(toString(triangles[0][2]), '1,2,1') - self.assertEqual(','.join(toString(triangles[1][0])), '3,2,2') - self.assertEqual(','.join(toString(triangles[1][1])), '5,2,1') - self.assertEqual(','.join(toString(triangles[1][2])), '2,3,3') + self.assertEqual(toString(triangles[1][0]), '3,2,2') + self.assertEqual(toString(triangles[1][1]), '5,2,1') + self.assertEqual(toString(triangles[1][2]), '2,3,3') - self.assertEqual(','.join(toString(triangles[2][0])), '1,1,1') - self.assertEqual(','.join(toString(triangles[2][1])), '3,2,2') - self.assertEqual(','.join(toString(triangles[2][2])), '2,1,1') + self.assertEqual(toString(triangles[2][0]), '1,1,1') + self.assertEqual(toString(triangles[2][1]), '3,2,2') + self.assertEqual(toString(triangles[2][2]), '2,1,1') - self.assertEqual(','.join(toString(triangles[3][0])), '1,1,1') - self.assertEqual(','.join(toString(triangles[3][1])), '3,2,2') - self.assertEqual(','.join(toString(triangles[3][2])), '5,2,1') + self.assertEqual(toString(triangles[3][0]), '1,1,1') + self.assertEqual(toString(triangles[3][1]), '3,2,2') + self.assertEqual(toString(triangles[3][2]), '5,2,1') def testCollapseSevenNodes(self): coords = [ @@ -108,22 +107,22 @@ def testCollapseSevenNodes(self): self.assertEqual(len(triangles[3]), 3) self.assertEqual(len(triangles[4]), 3) - self.assertEqual(','.join(toString(triangles[0][0])), '1,1,1') - self.assertEqual(','.join(toString(triangles[0][1])), '2,1,1') - self.assertEqual(','.join(toString(triangles[0][2])), '1,2,1') + self.assertEqual(toString(triangles[0][0]), '1,1,1') + self.assertEqual(toString(triangles[0][1]), '2,1,1') + self.assertEqual(toString(triangles[0][2]), '1,2,1') - self.assertEqual(','.join(toString(triangles[1][0])), '3,2,2') - self.assertEqual(','.join(toString(triangles[1][1])), '5,2,1') - self.assertEqual(','.join(toString(triangles[1][2])), '2,3,3') + self.assertEqual(toString(triangles[1][0]), '3,2,2') + self.assertEqual(toString(triangles[1][1]), '5,2,1') + self.assertEqual(toString(triangles[1][2]), '2,3,3') - self.assertEqual(','.join(toString(triangles[2][0])), '1,1,1') - self.assertEqual(','.join(toString(triangles[2][1])), '3,2,2') - self.assertEqual(','.join(toString(triangles[2][2])), '2,1,1') + self.assertEqual(toString(triangles[2][0]), '1,1,1') + self.assertEqual(toString(triangles[2][1]), '3,2,2') + self.assertEqual(toString(triangles[2][2]), '2,1,1') - self.assertEqual(','.join(toString(triangles[3][0])), '1,1,1') - self.assertEqual(','.join(toString(triangles[3][1])), '5,2,1') - self.assertEqual(','.join(toString(triangles[3][2])), '3,2,2') + self.assertEqual(toString(triangles[3][0]), '1,1,1') + self.assertEqual(toString(triangles[3][1]), '5,2,1') + self.assertEqual(toString(triangles[3][2]), '3,2,2') - self.assertEqual(','.join(toString(triangles[4][0])), '1,1,1') - self.assertEqual(','.join(toString(triangles[4][1])), '5,2,1') - self.assertEqual(','.join(toString(triangles[4][2])), '6,6,6') + self.assertEqual(toString(triangles[4][0]), '1,1,1') + self.assertEqual(toString(triangles[4][1]), '5,2,1') + self.assertEqual(toString(triangles[4][2]), '6,6,6') diff --git a/tests/test_encode_decode.py b/tests/test_encode_decode.py index 8761bf5..cd4445e 100644 --- a/tests/test_encode_decode.py +++ b/tests/test_encode_decode.py @@ -29,16 +29,7 @@ def tearDown(self): if os.path.exists(self.tmpfile): os.remove(self.tmpfile) - def testEncodeDecode(self): - z = 0 - x = 0 - y = 0 - globalGeodetic = GlobalGeodetic(True) - bounds = globalGeodetic.TileBounds(x, y, z) - ter = encode(geometries, bounds=bounds) - ter.toFile(self.tmpfile) - ter2 = decode(self.tmpfile, bounds) - + def assert_tile(self, ter, ter2): self.assertIsInstance(ter.__repr__(), str) self.assertIsInstance(ter2.__repr__(), str) @@ -76,5 +67,27 @@ def testEncodeDecode(self): for i, v in enumerate(ter.westI): self.assertEqual(v, ter2.westI[i], i) + def testEncodeDecode(self): + z = 0 + x = 0 + y = 0 + globalGeodetic = GlobalGeodetic(True) + bounds = globalGeodetic.TileBounds(x, y, z) + ter = encode(geometries, bounds=bounds) + ter.toFile(self.tmpfile) + ter2 = decode(self.tmpfile, bounds) + self.assert_tile(ter, ter2) + # Partial tile nothing on the east edge self.assertEqual(len(ter.eastI), 0) self.assertEqual(len(ter.eastI), len(ter2.eastI)) + + def testEncodeDecodeNoBounds(self): + ter = encode(geometries) + ter.toFile(self.tmpfile) + bounds = ter.bounds + ter2 = decode(self.tmpfile, bounds) + self.assert_tile(ter, ter2) + # Bounds computed dynamically from partial tile + # east edge now has data + self.assertGreater(len(ter.eastI), 0) + self.assertEqual(len(ter.eastI), len(ter2.eastI)) diff --git a/tests/test_terrain_tile.py b/tests/test_terrain_tile.py index 5e09c0b..19c1a52 100644 --- a/tests/test_terrain_tile.py +++ b/tests/test_terrain_tile.py @@ -3,16 +3,11 @@ import io import os import unittest -from builtins import range - -from future import standard_library from quantized_mesh_tile.global_geodetic import GlobalGeodetic from quantized_mesh_tile.terrain import TerrainTile from quantized_mesh_tile.topology import TerrainTopology -standard_library.install_aliases() - class TestTerrainTile(unittest.TestCase): def setUp(self):