Skip to content

Commit

Permalink
Merge pull request #645 from ungarj/singlepart_multipart_filtering
Browse files Browse the repository at this point in the history
Singlepart multipart filtering
  • Loading branch information
ungarj authored Sep 11, 2024
2 parents 1e2fe9e + 92dc610 commit 0f461fe
Show file tree
Hide file tree
Showing 6 changed files with 85 additions and 23 deletions.
8 changes: 5 additions & 3 deletions mapchete/formats/default/vector_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -289,9 +289,11 @@ def _read_from_cache(self, validity_check=True, clip_to_crs_bounds=False):
if checked not in self._cache:
self._cache[checked] = list(
read_vector_window(
self._in_memory_features
if self._memory_cache_active
else self.path,
(
self._in_memory_features
if self._memory_cache_active
else self.path
),
self.tile,
validity_check=validity_check,
clip_to_crs_bounds=clip_to_crs_bounds,
Expand Down
20 changes: 14 additions & 6 deletions mapchete/geometry/filter.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Generator, Union
from typing import Generator, Type, Union

from mapchete.errors import GeometryTypeError
from mapchete.geometry.types import (
Expand Down Expand Up @@ -39,23 +39,27 @@ def multipart_to_singleparts(

def is_type(
geometry: Geometry,
target_type: Union[str, Geometry],
target_type: Union[str, Type[Geometry]],
allow_multipart: bool = True,
) -> bool:
"""
Checks whether geometry type is in alignment with target type.
"""
target_type = get_geometry_type(target_type)
if isinstance(geometry, target_type):
return True
elif isinstance(geometry, GeometryCollection):
return False

# SinglePart is MultiPart or MultiPart is SinglePart
if allow_multipart:
return isinstance(geometry, get_multipart_type(target_type))
return False


def filter_by_geometry_type(
geometry: Geometry,
target_type: Union[str, Geometry],
target_type: Union[str, Type[Geometry]],
allow_multipart: bool = True,
):
"""Yields geometries only if they match the target type.
Expand All @@ -64,12 +68,16 @@ def filter_by_geometry_type(
If allow_multipart is set to False, multipart geometries are broken down into their
subgeometries. If set to True, a MultiPoint will be yielded if the
"""
target_type = get_geometry_type(target_type)
if is_type(geometry, target_type=target_type, allow_multipart=allow_multipart):
target_geometry_type = get_geometry_type(target_type)
if is_type(
geometry, target_type=target_geometry_type, allow_multipart=allow_multipart
):
yield geometry

elif isinstance(geometry, MultipartGeometry):
for subgeometry in multipart_to_singleparts(geometry):
yield from filter_by_geometry_type(
subgeometry, target_type=target_type, allow_multipart=allow_multipart
subgeometry,
target_type=target_geometry_type,
allow_multipart=allow_multipart,
)
34 changes: 29 additions & 5 deletions mapchete/geometry/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ class GeoInterface(Protocol):
CoordArrays = Tuple[Iterable[float], Iterable[float]]


def get_multipart_type(geometry: Geometry) -> MultipartGeometry:
def get_multipart_type(geometry_type: Union[Type[Geometry], str]) -> MultipartGeometry:
try:
return {
Point: MultiPoint,
Expand All @@ -62,14 +62,38 @@ def get_multipart_type(geometry: Geometry) -> MultipartGeometry:
MultiPoint: MultiPoint,
MultiLineString: MultiLineString,
MultiPolygon: MultiPolygon,
}[geometry]
}[
get_geometry_type(geometry_type)
] # type: ignore
except KeyError:
raise GeometryTypeError(
f"geometry type {geometry.type} has no corresponding multipart type"
f"geometry type {geometry_type} has no corresponding multipart type"
)


def get_geometry_type(geometry_type: Union[Type[Geometry], str]) -> Type[Geometry]:
def get_singlepart_type(
geometry_type: Union[Type[Geometry], str]
) -> SinglepartGeometry:
try:
return {
Point: Point,
LineString: LineString,
Polygon: Polygon,
MultiPoint: Point,
MultiLineString: LineString,
MultiPolygon: Polygon,
}[
get_geometry_type(geometry_type)
] # type: ignore
except KeyError: # pragma: no cover
raise GeometryTypeError(
f"geometry type {geometry_type} has no corresponding multipart type"
)


def get_geometry_type(
geometry_type: Union[Type[Geometry], str, dict, Geometry]
) -> Geometry:
if isinstance(geometry_type, str):
try:
return {
Expand All @@ -87,5 +111,5 @@ def get_geometry_type(geometry_type: Union[Type[Geometry], str]) -> Type[Geometr
f"geometry type cannot be determined from {geometry_type}"
)
elif issubclass(geometry_type, Geometry):
return geometry_type
return geometry_type # type: ignore
raise GeometryTypeError(f"geometry type cannot be determined from {geometry_type}")
4 changes: 2 additions & 2 deletions mapchete/io/vector.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
segmentize_geometry,
to_shape,
)
from mapchete.geometry.types import get_geometry_type
from mapchete.geometry.types import get_geometry_type, get_singlepart_type
from mapchete.io import copy
from mapchete.path import MPath, fs_from_path
from mapchete.settings import IORetrySettings
Expand Down Expand Up @@ -323,7 +323,7 @@ def _get_reprojected_features(
clipped_geom = original_geom.intersection(dst_bbox)
for checked_geom in filter_by_geometry_type(
clipped_geom,
original_geom.geom_type,
get_singlepart_type(original_geom.geom_type),
):
# reproject each feature to tile CRS
reprojected_geom = reproject_geometry(
Expand Down
40 changes: 34 additions & 6 deletions mapchete/tile.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Mapchtete handling tiles."""

from __future__ import annotations

import logging
Expand All @@ -9,9 +10,11 @@
from affine import Affine
from rasterio.enums import Resampling
from rasterio.features import rasterize, shapes
from rasterio.transform import from_bounds
from rasterio.warp import reproject
from shapely import clip_by_rect
from shapely.geometry import box, shape
from shapely.geometry.base import BaseGeometry
from shapely.ops import unary_union
from tilematrix import Tile, TilePyramid
from tilematrix._conf import ROUND
Expand Down Expand Up @@ -527,21 +530,46 @@ def _count_cells(pyramid, geometry, minzoom, maxzoom):
return int(count)


def snap_geometry_to_tiles(geometry=None, pyramid=None, zoom=None):
def snap_geometry_to_tiles(
geometry: BaseGeometry, pyramid: BufferedTilePyramid, zoom: int
) -> BaseGeometry:
if geometry.is_empty:
return geometry
# calculate everything using an unbuffered pyramid, because otherwise the Affine
# object cannot be calculated
unbuffered_pyramid = BufferedTilePyramid.from_dict(
dict(pyramid.to_dict(), pixelbuffer=0)
)
transform = unbuffered_pyramid.matrix_affine(zoom)

# use subset because otherwise memory usage can crash the program
left, bottom, right, top = geometry.bounds
# clip geometry bounds to pyramid bounds
left = max([left, unbuffered_pyramid.bounds.left])
bottom = max([bottom, unbuffered_pyramid.bounds.bottom])
right = min([right, unbuffered_pyramid.bounds.right])
top = min([top, unbuffered_pyramid.bounds.top])
lb_tile = unbuffered_pyramid.tile_from_xy(left, bottom, zoom, on_edge_use="rt")
rt_tile = unbuffered_pyramid.tile_from_xy(right, top, zoom, on_edge_use="lb")
snapped_left, snapped_south, snapped_east, snapped_north = (
lb_tile.bounds.left,
lb_tile.bounds.bottom,
rt_tile.bounds.right,
rt_tile.bounds.top,
)
width = abs(rt_tile.col - lb_tile.col) + 1
height = abs(rt_tile.row - lb_tile.row) + 1
out_shape = (height, width)
transform = from_bounds(
west=snapped_left,
south=snapped_south,
east=snapped_east,
north=snapped_north,
width=width,
height=height,
)
raster = rasterize(
[(geometry, 1)],
out_shape=(
unbuffered_pyramid.matrix_height(zoom),
unbuffered_pyramid.matrix_width(zoom),
),
out_shape=out_shape,
fill=0,
transform=transform,
dtype=np.uint8,
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ dependencies = [
"geojson-pydantic",
"importlib-metadata",
"importlib-resources",
"numpy>=1.16",
"numpy>=1.16,<=2.0.1",
"oyaml",
"pydantic>=2.3.0",
"pydantic_settings>=2.0.0",
Expand Down

6 comments on commit 0f461fe

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test Coverage

missing coverage
FileStmtsMissCoverMissing
__init__.py300100% 
enums.py310100% 
errors.py170100% 
index.py1790100% 
log.py500100% 
path.py3820100% 
pretty.py160100% 
protocols.py140100% 
registered.py50100% 
settings.py320100% 
stac.py1470100% 
testing.py980100% 
tile.py1710100% 
timer.py370100% 
types.py2160100% 
validate.py630100% 
cli
   __init__.py00100% 
   main.py90100% 
   mpath.py830100% 
   options.py1270100% 
   progress_bar.py370100% 
cli/default
   __init__.py00100% 
   convert.py630100% 
   cp.py330100% 
   create.py360100% 
   execute.py420100% 
   formats.py190100% 
   index.py420100% 
   processes.py180100% 
   rm.py250100% 
   serve.py690100% 
   stac.py600100% 
commands
   __init__.py60100% 
   convert.py780100% 
   cp.py650100% 
   execute.py700100% 
   index.py340100% 
   observer.py90100% 
   parser.py890100% 
   rm.py440100% 
config
   __init__.py40100% 
   base.py3550100% 
   models.py1770100% 
   parse.py770100% 
   process_func.py890100% 
executor
   __init__.py170100% 
   base.py770100% 
   concurrent_futures.py700100% 
   dask.py1050100% 
   future.py820100% 
   sequential.py330100% 
   types.py130100% 
formats
   __init__.py30100% 
   base.py1740100% 
   drivers.py00100% 
   loaders.py430100% 
   protocols.py130100% 
   tools.py1530100% 
formats/default
   __init__.py00100% 
   _fiona_base.py510100% 
   flatgeobuf.py140100% 
   geojson.py120100% 
   gtiff.py1950100% 
   mapchete_input.py160100% 
   png.py660100% 
   png_hillshade.py560100% 
   raster_file.py850100% 
   tile_directory.py1010100% 
   vector_file.py750100% 
geometry
   __init__.py100100% 
   filter.py280100% 
   footprints.py390100% 
   latlon.py300100% 
   repair.py80100% 
   reproject.py690100% 
   segmentize.py230100% 
   shape.py90100% 
   transform.py280100% 
   types.py300100% 
io
   __init__.py70100% 
   _json.py60100% 
   _misc.py780100% 
   _path.py00100% 
   profiles.py60100% 
   vector.py2900100% 
io/raster
   __init__.py80100% 
   array.py1040100% 
   convert.py230100% 
   mosaic.py1020100% 
   open.py140100% 
   read.py1670100% 
   referenced_raster.py950100% 
   write.py1000100% 
processes
   __init__.py170100% 
   clip.py170100% 
   contours.py520100% 
   convert.py380100% 
   hillshade.py490100% 
processes/examples
   __init__.py00100% 
   example_process.py60100% 
processing
   __init__.py30100% 
   base.py2620100% 
   execute.py710100% 
   mp.py260100% 
   tasks.py2890100% 
   types.py450100% 
processing/profilers
   __init__.py50100% 
   memory.py600100% 
   requests.py250100% 
   time.py220100% 
static
   __init__.py00100% 
   process_template.py10100% 
TOTAL69640100% 

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test Coverage

missing coverage
FileStmtsMissCoverMissing
__init__.py300100% 
enums.py310100% 
errors.py170100% 
index.py1790100% 
log.py500100% 
path.py3820100% 
pretty.py160100% 
protocols.py140100% 
registered.py50100% 
settings.py320100% 
stac.py1470100% 
testing.py980100% 
tile.py1710100% 
timer.py370100% 
types.py2160100% 
validate.py630100% 
cli
   __init__.py00100% 
   main.py90100% 
   mpath.py830100% 
   options.py1270100% 
   progress_bar.py370100% 
cli/default
   __init__.py00100% 
   convert.py630100% 
   cp.py330100% 
   create.py360100% 
   execute.py420100% 
   formats.py190100% 
   index.py420100% 
   processes.py180100% 
   rm.py250100% 
   serve.py690100% 
   stac.py600100% 
commands
   __init__.py60100% 
   convert.py780100% 
   cp.py650100% 
   execute.py700100% 
   index.py340100% 
   observer.py90100% 
   parser.py890100% 
   rm.py440100% 
config
   __init__.py40100% 
   base.py3550100% 
   models.py1770100% 
   parse.py770100% 
   process_func.py890100% 
executor
   __init__.py170100% 
   base.py770100% 
   concurrent_futures.py700100% 
   dask.py1050100% 
   future.py820100% 
   sequential.py330100% 
   types.py130100% 
formats
   __init__.py30100% 
   base.py1740100% 
   drivers.py00100% 
   loaders.py430100% 
   protocols.py130100% 
   tools.py1530100% 
formats/default
   __init__.py00100% 
   _fiona_base.py510100% 
   flatgeobuf.py140100% 
   geojson.py120100% 
   gtiff.py1950100% 
   mapchete_input.py160100% 
   png.py660100% 
   png_hillshade.py560100% 
   raster_file.py850100% 
   tile_directory.py1010100% 
   vector_file.py750100% 
geometry
   __init__.py100100% 
   filter.py280100% 
   footprints.py390100% 
   latlon.py300100% 
   repair.py80100% 
   reproject.py690100% 
   segmentize.py230100% 
   shape.py90100% 
   transform.py280100% 
   types.py300100% 
io
   __init__.py70100% 
   _json.py60100% 
   _misc.py780100% 
   _path.py00100% 
   profiles.py60100% 
   vector.py2900100% 
io/raster
   __init__.py80100% 
   array.py1040100% 
   convert.py230100% 
   mosaic.py1020100% 
   open.py140100% 
   read.py1670100% 
   referenced_raster.py950100% 
   write.py1000100% 
processes
   __init__.py170100% 
   clip.py170100% 
   contours.py520100% 
   convert.py380100% 
   hillshade.py490100% 
processes/examples
   __init__.py00100% 
   example_process.py60100% 
processing
   __init__.py30100% 
   base.py2620100% 
   execute.py710100% 
   mp.py260100% 
   tasks.py2890100% 
   types.py450100% 
processing/profilers
   __init__.py50100% 
   memory.py600100% 
   requests.py250100% 
   time.py220100% 
static
   __init__.py00100% 
   process_template.py10100% 
TOTAL69640100% 

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test Coverage

missing coverage
FileStmtsMissCoverMissing
__init__.py300100% 
enums.py310100% 
errors.py170100% 
index.py1790100% 
log.py500100% 
path.py3820100% 
pretty.py160100% 
protocols.py140100% 
registered.py50100% 
settings.py320100% 
stac.py1470100% 
testing.py980100% 
tile.py1710100% 
timer.py370100% 
types.py2160100% 
validate.py630100% 
cli
   __init__.py00100% 
   main.py90100% 
   mpath.py830100% 
   options.py1270100% 
   progress_bar.py370100% 
cli/default
   __init__.py00100% 
   convert.py630100% 
   cp.py330100% 
   create.py360100% 
   execute.py420100% 
   formats.py190100% 
   index.py420100% 
   processes.py180100% 
   rm.py250100% 
   serve.py690100% 
   stac.py600100% 
commands
   __init__.py60100% 
   convert.py780100% 
   cp.py650100% 
   execute.py700100% 
   index.py340100% 
   observer.py90100% 
   parser.py890100% 
   rm.py440100% 
config
   __init__.py40100% 
   base.py3550100% 
   models.py1770100% 
   parse.py770100% 
   process_func.py890100% 
executor
   __init__.py170100% 
   base.py770100% 
   concurrent_futures.py700100% 
   dask.py1050100% 
   future.py820100% 
   sequential.py330100% 
   types.py130100% 
formats
   __init__.py30100% 
   base.py1740100% 
   drivers.py00100% 
   loaders.py430100% 
   protocols.py130100% 
   tools.py1530100% 
formats/default
   __init__.py00100% 
   _fiona_base.py510100% 
   flatgeobuf.py140100% 
   geojson.py120100% 
   gtiff.py1950100% 
   mapchete_input.py160100% 
   png.py660100% 
   png_hillshade.py560100% 
   raster_file.py850100% 
   tile_directory.py1010100% 
   vector_file.py750100% 
geometry
   __init__.py100100% 
   filter.py280100% 
   footprints.py390100% 
   latlon.py300100% 
   repair.py80100% 
   reproject.py690100% 
   segmentize.py230100% 
   shape.py90100% 
   transform.py280100% 
   types.py300100% 
io
   __init__.py70100% 
   _json.py60100% 
   _misc.py780100% 
   _path.py00100% 
   profiles.py60100% 
   vector.py2900100% 
io/raster
   __init__.py80100% 
   array.py1040100% 
   convert.py230100% 
   mosaic.py1020100% 
   open.py140100% 
   read.py1670100% 
   referenced_raster.py950100% 
   write.py1000100% 
processes
   __init__.py170100% 
   clip.py170100% 
   contours.py520100% 
   convert.py380100% 
   hillshade.py490100% 
processes/examples
   __init__.py00100% 
   example_process.py60100% 
processing
   __init__.py30100% 
   base.py2620100% 
   execute.py710100% 
   mp.py260100% 
   tasks.py2890100% 
   types.py450100% 
processing/profilers
   __init__.py50100% 
   memory.py600100% 
   requests.py250100% 
   time.py220100% 
static
   __init__.py00100% 
   process_template.py10100% 
TOTAL69640100% 

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test Coverage

missing coverage
FileStmtsMissCoverMissing
__init__.py300100% 
enums.py310100% 
errors.py170100% 
index.py1790100% 
log.py500100% 
path.py3820100% 
pretty.py160100% 
protocols.py140100% 
registered.py50100% 
settings.py320100% 
stac.py1470100% 
testing.py980100% 
tile.py1710100% 
timer.py370100% 
types.py2160100% 
validate.py630100% 
cli
   __init__.py00100% 
   main.py90100% 
   mpath.py830100% 
   options.py1270100% 
   progress_bar.py370100% 
cli/default
   __init__.py00100% 
   convert.py630100% 
   cp.py330100% 
   create.py360100% 
   execute.py420100% 
   formats.py190100% 
   index.py420100% 
   processes.py180100% 
   rm.py250100% 
   serve.py690100% 
   stac.py600100% 
commands
   __init__.py60100% 
   convert.py780100% 
   cp.py650100% 
   execute.py700100% 
   index.py340100% 
   observer.py90100% 
   parser.py890100% 
   rm.py440100% 
config
   __init__.py40100% 
   base.py3550100% 
   models.py1770100% 
   parse.py770100% 
   process_func.py890100% 
executor
   __init__.py170100% 
   base.py770100% 
   concurrent_futures.py700100% 
   dask.py1050100% 
   future.py820100% 
   sequential.py330100% 
   types.py130100% 
formats
   __init__.py30100% 
   base.py1740100% 
   drivers.py00100% 
   loaders.py430100% 
   protocols.py130100% 
   tools.py1530100% 
formats/default
   __init__.py00100% 
   _fiona_base.py510100% 
   flatgeobuf.py140100% 
   geojson.py120100% 
   gtiff.py1950100% 
   mapchete_input.py160100% 
   png.py660100% 
   png_hillshade.py560100% 
   raster_file.py850100% 
   tile_directory.py1010100% 
   vector_file.py750100% 
geometry
   __init__.py100100% 
   filter.py280100% 
   footprints.py390100% 
   latlon.py300100% 
   repair.py80100% 
   reproject.py690100% 
   segmentize.py230100% 
   shape.py90100% 
   transform.py280100% 
   types.py300100% 
io
   __init__.py70100% 
   _json.py60100% 
   _misc.py780100% 
   _path.py00100% 
   profiles.py60100% 
   vector.py2900100% 
io/raster
   __init__.py80100% 
   array.py1040100% 
   convert.py230100% 
   mosaic.py1020100% 
   open.py140100% 
   read.py1670100% 
   referenced_raster.py950100% 
   write.py1000100% 
processes
   __init__.py170100% 
   clip.py170100% 
   contours.py520100% 
   convert.py380100% 
   hillshade.py490100% 
processes/examples
   __init__.py00100% 
   example_process.py60100% 
processing
   __init__.py30100% 
   base.py2620100% 
   execute.py710100% 
   mp.py260100% 
   tasks.py2890100% 
   types.py450100% 
processing/profilers
   __init__.py50100% 
   memory.py600100% 
   requests.py250100% 
   time.py220100% 
static
   __init__.py00100% 
   process_template.py10100% 
TOTAL69640100% 

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test Coverage

missing coverage
FileStmtsMissCoverMissing
__init__.py300100% 
enums.py310100% 
errors.py170100% 
index.py1790100% 
log.py500100% 
path.py3820100% 
pretty.py160100% 
protocols.py140100% 
registered.py50100% 
settings.py320100% 
stac.py1470100% 
testing.py980100% 
tile.py1710100% 
timer.py370100% 
types.py2160100% 
validate.py630100% 
cli
   __init__.py00100% 
   main.py90100% 
   mpath.py830100% 
   options.py1270100% 
   progress_bar.py370100% 
cli/default
   __init__.py00100% 
   convert.py630100% 
   cp.py330100% 
   create.py360100% 
   execute.py420100% 
   formats.py190100% 
   index.py420100% 
   processes.py180100% 
   rm.py250100% 
   serve.py690100% 
   stac.py600100% 
commands
   __init__.py60100% 
   convert.py780100% 
   cp.py650100% 
   execute.py700100% 
   index.py340100% 
   observer.py90100% 
   parser.py890100% 
   rm.py440100% 
config
   __init__.py40100% 
   base.py3550100% 
   models.py1770100% 
   parse.py770100% 
   process_func.py890100% 
executor
   __init__.py170100% 
   base.py770100% 
   concurrent_futures.py700100% 
   dask.py1050100% 
   future.py820100% 
   sequential.py330100% 
   types.py130100% 
formats
   __init__.py30100% 
   base.py1740100% 
   drivers.py00100% 
   loaders.py430100% 
   protocols.py130100% 
   tools.py1530100% 
formats/default
   __init__.py00100% 
   _fiona_base.py510100% 
   flatgeobuf.py140100% 
   geojson.py120100% 
   gtiff.py1950100% 
   mapchete_input.py160100% 
   png.py660100% 
   png_hillshade.py560100% 
   raster_file.py850100% 
   tile_directory.py1010100% 
   vector_file.py750100% 
geometry
   __init__.py100100% 
   filter.py280100% 
   footprints.py390100% 
   latlon.py300100% 
   repair.py80100% 
   reproject.py690100% 
   segmentize.py230100% 
   shape.py90100% 
   transform.py280100% 
   types.py300100% 
io
   __init__.py70100% 
   _json.py60100% 
   _misc.py780100% 
   _path.py00100% 
   profiles.py60100% 
   vector.py2900100% 
io/raster
   __init__.py80100% 
   array.py1040100% 
   convert.py230100% 
   mosaic.py1020100% 
   open.py140100% 
   read.py1670100% 
   referenced_raster.py950100% 
   write.py1000100% 
processes
   __init__.py170100% 
   clip.py170100% 
   contours.py520100% 
   convert.py380100% 
   hillshade.py490100% 
processes/examples
   __init__.py00100% 
   example_process.py60100% 
processing
   __init__.py30100% 
   base.py2620100% 
   execute.py710100% 
   mp.py260100% 
   tasks.py2890100% 
   types.py450100% 
processing/profilers
   __init__.py50100% 
   memory.py600100% 
   requests.py250100% 
   time.py220100% 
static
   __init__.py00100% 
   process_template.py10100% 
TOTAL69640100% 

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test Coverage

missing coverage
FileStmtsMissCoverMissing
__init__.py300100% 
enums.py310100% 
errors.py170100% 
index.py1790100% 
log.py500100% 
path.py3820100% 
pretty.py160100% 
protocols.py140100% 
registered.py50100% 
settings.py320100% 
stac.py1470100% 
testing.py980100% 
tile.py1710100% 
timer.py370100% 
types.py2160100% 
validate.py630100% 
cli
   __init__.py00100% 
   main.py90100% 
   mpath.py830100% 
   options.py1270100% 
   progress_bar.py370100% 
cli/default
   __init__.py00100% 
   convert.py630100% 
   cp.py330100% 
   create.py360100% 
   execute.py420100% 
   formats.py190100% 
   index.py420100% 
   processes.py180100% 
   rm.py250100% 
   serve.py690100% 
   stac.py600100% 
commands
   __init__.py60100% 
   convert.py780100% 
   cp.py650100% 
   execute.py700100% 
   index.py340100% 
   observer.py90100% 
   parser.py890100% 
   rm.py440100% 
config
   __init__.py40100% 
   base.py3550100% 
   models.py1770100% 
   parse.py770100% 
   process_func.py890100% 
executor
   __init__.py170100% 
   base.py770100% 
   concurrent_futures.py700100% 
   dask.py1050100% 
   future.py820100% 
   sequential.py330100% 
   types.py130100% 
formats
   __init__.py30100% 
   base.py1740100% 
   drivers.py00100% 
   loaders.py430100% 
   protocols.py130100% 
   tools.py1530100% 
formats/default
   __init__.py00100% 
   _fiona_base.py510100% 
   flatgeobuf.py140100% 
   geojson.py120100% 
   gtiff.py1950100% 
   mapchete_input.py160100% 
   png.py660100% 
   png_hillshade.py560100% 
   raster_file.py850100% 
   tile_directory.py1010100% 
   vector_file.py750100% 
geometry
   __init__.py100100% 
   filter.py280100% 
   footprints.py390100% 
   latlon.py300100% 
   repair.py80100% 
   reproject.py690100% 
   segmentize.py230100% 
   shape.py90100% 
   transform.py280100% 
   types.py300100% 
io
   __init__.py70100% 
   _json.py60100% 
   _misc.py780100% 
   _path.py00100% 
   profiles.py60100% 
   vector.py2900100% 
io/raster
   __init__.py80100% 
   array.py1040100% 
   convert.py230100% 
   mosaic.py1020100% 
   open.py140100% 
   read.py1670100% 
   referenced_raster.py950100% 
   write.py1000100% 
processes
   __init__.py170100% 
   clip.py170100% 
   contours.py520100% 
   convert.py380100% 
   hillshade.py490100% 
processes/examples
   __init__.py00100% 
   example_process.py60100% 
processing
   __init__.py30100% 
   base.py2620100% 
   execute.py710100% 
   mp.py260100% 
   tasks.py2890100% 
   types.py450100% 
processing/profilers
   __init__.py50100% 
   memory.py600100% 
   requests.py250100% 
   time.py220100% 
static
   __init__.py00100% 
   process_template.py10100% 
TOTAL69640100% 

Please sign in to comment.