Skip to content

Commit

Permalink
upgrade versions, sign items (#87)
Browse files Browse the repository at this point in the history
* upgrade versions, sign items

* cleanup, use ruff

* ruff formatting

* adapt notebook
  • Loading branch information
fgebhart authored Oct 27, 2024
1 parent e8933d1 commit 1458b36
Show file tree
Hide file tree
Showing 22 changed files with 1,520 additions and 1,153 deletions.
9 changes: 0 additions & 9 deletions .flake8

This file was deleted.

17 changes: 4 additions & 13 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
repos:

- repo: https://github.com/psf/black
rev: 23.1.0
hooks:
- id: black

- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
hooks:
Expand All @@ -14,12 +9,8 @@ repos:
- id: end-of-file-fixer
types: [python]

- repo: https://github.com/pycqa/flake8
rev: 6.0.0
hooks:
- id: flake8

- repo: https://github.com/pycqa/isort
rev: 5.12.0
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.7.1
hooks:
- id: isort
- id: ruff
- id: ruff-format
41 changes: 31 additions & 10 deletions mapa/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,15 +56,19 @@ def convert_array_to_stl(
log.debug("🔍 reducing image resolution...")
array = reduce_resolution(array, bin_factor=bin_fac)

triangles = compute_all_triangles(array, desired_size, z_offset, z_scale, elevation_scale)
triangles = compute_all_triangles(
array, desired_size, z_offset, z_scale, elevation_scale
)
log.debug("💾 saving data to stl file...")

output_file = save_to_stl_file(triangles, output_file, as_ascii)
log.info(f"🎉 successfully generated STL file: {Path(output_file).absolute()}")
return Path(output_file)


def _get_desired_size(array: np.ndarray, x: float, y: float, ensure_squared: bool) -> ModelSize:
def _get_desired_size(
array: np.ndarray, x: float, y: float, ensure_squared: bool
) -> ModelSize:
if ensure_squared:
return ModelSize(x=x, y=y)
else:
Expand All @@ -82,15 +86,19 @@ def convert_tiff_to_stl(
z_scale: float,
ensure_squared: bool = False,
) -> Path:
output_file = verify_input_and_output_are_valid(input=input_file, output=output_file)
output_file = verify_input_and_output_are_valid(
input=input_file, output=output_file
)

tiff = rio.open(input_file)
elevation_scale = determine_elevation_scale(tiff, model_size)
array = tiff_to_array(tiff)

if ensure_squared:
array = cut_array_to_square(array)
desired_size = _get_desired_size(array=array, x=model_size, y=model_size, ensure_squared=ensure_squared)
desired_size = _get_desired_size(
array=array, x=model_size, y=model_size, ensure_squared=ensure_squared
)

return convert_array_to_stl(
array=array,
Expand All @@ -111,7 +119,9 @@ def _fetch_merge_and_clip_tiffs(
cache_dir: Path,
progress_bar: Union[None, ProgressBar] = None,
) -> Path:
tiffs = fetch_stac_items_for_bbox(bbox_geojson, allow_caching, cache_dir, progress_bar)
tiffs = fetch_stac_items_for_bbox(
bbox_geojson, allow_caching, cache_dir, progress_bar
)
if len(tiffs) > 1:
merged_tiff = merge_tiffs(tiffs, bbox_hash, cache_dir)
else:
Expand All @@ -120,14 +130,19 @@ def _fetch_merge_and_clip_tiffs(


def _get_tiff_for_bbox(
bbox_geojson: dict, allow_caching: bool, cache_dir: Path, progress_bar: Union[None, ProgressBar] = None
bbox_geojson: dict,
allow_caching: bool,
cache_dir: Path,
progress_bar: Union[None, ProgressBar] = None,
) -> Path:
bbox_hash = get_hash_of_geojson(bbox_geojson)
if tiff_for_bbox_is_cached(bbox_hash, cache_dir) and allow_caching:
log.info("🚀 using cached tiff!")
return path_to_clipped_tiff(bbox_hash, cache_dir)
else:
return _fetch_merge_and_clip_tiffs(bbox_geojson, bbox_hash, allow_caching, cache_dir, progress_bar)
return _fetch_merge_and_clip_tiffs(
bbox_geojson, bbox_hash, allow_caching, cache_dir, progress_bar
)


def convert_bbox_to_stl(
Expand Down Expand Up @@ -211,7 +226,9 @@ def convert_bbox_to_stl(
steps = tiles.x * tiles.y * 2 if compress else tiles.x * tiles.y
progress_bar = ProgressBar(progress_bar=progress_bar, steps=steps)

path_to_tiff = _get_tiff_for_bbox(bbox_geometry, allow_caching, Path(cache_dir), progress_bar)
path_to_tiff = _get_tiff_for_bbox(
bbox_geometry, allow_caching, Path(cache_dir), progress_bar
)
tiff = rio.open(path_to_tiff)
elevation_scale = determine_elevation_scale(tiff, model_size)
array = tiff_to_array(tiff)
Expand All @@ -237,12 +254,16 @@ def convert_bbox_to_stl(
z_offset=z_offset,
z_scale=z_scale,
elevation_scale=elevation_scale,
output_file=f"{output_file}_{i+1}.stl" if len(tiled_arrays) > 1 else f"{output_file}.stl",
output_file=f"{output_file}_{i+1}.stl"
if len(tiled_arrays) > 1
else f"{output_file}.stl",
)
)
if progress_bar:
progress_bar.step()
if compress:
return create_zip_archive(files=stl_files, output_file=f"{output_file}.zip", progress_bar=progress_bar)
return create_zip_archive(
files=stl_files, output_file=f"{output_file}.zip", progress_bar=progress_bar
)
else:
return stl_files[0] if len(stl_files) == 1 else stl_files
55 changes: 44 additions & 11 deletions mapa/algorithm.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,12 @@ def _create_raster(array: npt.ArrayLike, max_x: int, max_y: int) -> np.ndarray:
raster[ix][iy] = array[ix][iy]
else:
# z value in raster is average of four neighbors
raster[ix][iy] = (array[ix][iy] + array[ix - 1][iy] + array[ix][iy - 1] + array[ix - 1][iy - 1]) / 4
raster[ix][iy] = (
array[ix][iy]
+ array[ix - 1][iy]
+ array[ix][iy - 1]
+ array[ix - 1][iy - 1]
) / 4
return raster


Expand Down Expand Up @@ -203,7 +208,9 @@ def _compute_triangles_of_3d_surface(
# first vertex
triangles[ix, iy, 2, 0, 0] = (ix + 1) * x_scale
triangles[ix, iy, 2, 0, 1] = (iy + 1) * y_scale
triangles[ix, iy, 2, 0, 2] = (raster[ix + 1, iy + 1]) * z_scale + z_offset
triangles[ix, iy, 2, 0, 2] = (
(raster[ix + 1, iy + 1]) * z_scale + z_offset
)
# second vertex
triangles[ix, iy, 2, 1, 0] = ix * x_scale
triangles[ix, iy, 2, 1, 1] = (iy + 1) * y_scale
Expand All @@ -225,13 +232,21 @@ def _compute_triangles_of_3d_surface(
# third vertex
triangles[ix, iy, 3, 2, 0] = (ix + 1) * x_scale
triangles[ix, iy, 3, 2, 1] = (iy + 1) * y_scale
triangles[ix, iy, 3, 2, 2] = (raster[ix + 1, iy + 1]) * z_scale + z_offset
triangles[ix, iy, 3, 2, 2] = (
(raster[ix + 1, iy + 1]) * z_scale + z_offset
)

return triangles.reshape((max_x * max_y * 4, 3, 3))


def _compute_triangles_of_body_side(
raster: npt.ArrayLike, max_x: int, max_y: int, x_scale: float, y_scale: float, z_scale: float, z_offset: float
raster: npt.ArrayLike,
max_x: int,
max_y: int,
x_scale: float,
y_scale: float,
z_scale: float,
z_offset: float,
) -> np.ndarray:
# loop over raster and build triangles when in first and last col and row
triangles = np.full((max_x * 4 + max_y * 4, 3, 3), -1.0, dtype=np.float64)
Expand Down Expand Up @@ -354,7 +369,9 @@ def _compute_triangles_of_body_side(
return triangles


def _compute_triangles_of_bottom(max_x: int, max_y: int, x_scale: float, y_scale: float) -> np.ndarray:
def _compute_triangles_of_bottom(
max_x: int, max_y: int, x_scale: float, y_scale: float
) -> np.ndarray:
# first row
fr_triangles = np.full((max_x - 1, 3, 3), -1.0, dtype=np.float64)
for i, cnt in enumerate(range(0, max_x - 1)):
Expand Down Expand Up @@ -428,16 +445,22 @@ def _compute_triangles_of_bottom(max_x: int, max_y: int, x_scale: float, y_scale
center_triangles[1, 2, 1] = 1 * y_scale
center_triangles[1, 2, 2] = 0

return np.vstack((fr_triangles, lr_triangles, fc_triangles, lc_triangles, center_triangles))
return np.vstack(
(fr_triangles, lr_triangles, fc_triangles, lc_triangles, center_triangles)
)


def _determine_z_offset(z_offset: Union[None, float], minimum: float, elevation_scale: float) -> float:
def _determine_z_offset(
z_offset: Union[None, float], minimum: float, elevation_scale: float
) -> float:
if z_offset is None:
# using the natural height, i.e. islands will have an z_offset of ~0 and mountains will have a larger z_offset
return minimum * elevation_scale
else:
if z_offset < 0:
log.warning("☝️ Warning: Be careful using negative z_offsets, as it might break your 3D model.")
log.warning(
"☝️ Warning: Be careful using negative z_offsets, as it might break your 3D model."
)
# subtract scaled minimum from z_offset to ensure the input z_offset will remain
return z_offset - minimum * elevation_scale

Expand Down Expand Up @@ -480,14 +503,24 @@ def compute_all_triangles(
z_scale=combined_z_scale,
z_offset=z_offset,
)
bottom_triangles = _compute_triangles_of_bottom(max_x=max_x, max_y=max_y, x_scale=x_scale, y_scale=y_scale)
bottom_triangles = _compute_triangles_of_bottom(
max_x=max_x, max_y=max_y, x_scale=x_scale, y_scale=y_scale
)
return np.vstack((dem_triangles, side_triangles, bottom_triangles))


def reduce_resolution(array: npt.ArrayLike, bin_factor: int) -> np.ndarray:
strided = as_strided(
array,
shape=(array.shape[0] // bin_factor, array.shape[1] // bin_factor, bin_factor, bin_factor),
strides=((array.strides[0] * bin_factor, array.strides[1] * bin_factor) + array.strides),
shape=(
array.shape[0] // bin_factor,
array.shape[1] // bin_factor,
bin_factor,
bin_factor,
),
strides=(
(array.strides[0] * bin_factor, array.strides[1] * bin_factor)
+ array.strides
),
)
return strided.mean(axis=-1).mean(axis=-1)
12 changes: 9 additions & 3 deletions mapa/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
@click.option("--input", help="Path to input TIFF file.")
@click.option("--output", help="Path to output STL file.")
@click.option(
"--as-ascii", is_flag=True, help="Save output STL as ascii file. If not provided, output file will be binary."
"--as-ascii",
is_flag=True,
help="Save output STL as ascii file. If not provided, output file will be binary.",
)
@click.option(
"--model-size",
Expand Down Expand Up @@ -46,7 +48,9 @@
f"Defaults to {conf.DEFAULT_Z_SCALE}."
),
)
@click.option("--demo", is_flag=True, help="Converts a demo tif of Hawaii into a STL file.")
@click.option(
"--demo", is_flag=True, help="Converts a demo tif of Hawaii into a STL file."
)
@click.option(
"--ensure-squared",
is_flag=True,
Expand Down Expand Up @@ -78,7 +82,9 @@ def dem2stl(
max_res = True
z_scale = 2.5

convert_tiff_to_stl(input, as_ascii, model_size, output, max_res, z_offset, z_scale, ensure_squared)
convert_tiff_to_stl(
input, as_ascii, model_size, output, max_res, z_offset, z_scale, ensure_squared
)


@click.command(help="🗺 Draw a bounding box on a map and turn it into a STL file 🗺")
Expand Down
11 changes: 9 additions & 2 deletions mapa/map.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,18 @@


def show_map(center: List[float] = CENTER, zoom: int = ZOOM) -> Tuple[Map, DrawControl]:
m = Map(center=center, zoom=zoom, scroll_wheel_zoom=True, layout=Layout(height="600px"))
m = Map(
center=center, zoom=zoom, scroll_wheel_zoom=True, layout=Layout(height="600px")
)
m.add_control(ScaleControl(position="bottomleft"))
m.add_layer(basemap_to_tiles(basemaps.OpenTopoMap))

dc = DrawControl(rectangle={"shapeOptions": {"color": "#0000FF"}}, polyline={}, polygon={}, circlemarker={})
dc = DrawControl(
rectangle={"shapeOptions": {"color": "#0000FF"}},
polyline={},
polygon={},
circlemarker={},
)

def handle_draw(target, action, geo_json):
print("Rectangle detected, execute next cells to continue!")
Expand Down
19 changes: 10 additions & 9 deletions mapa/mapa.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "4852dbe068704ce2bcbb5d7128dda91f",
"model_id": "3dff886098884676b5d84225aa1868c5",
"version_major": 2,
"version_minor": 0
},
Expand All @@ -47,7 +47,6 @@
"name": "stdout",
"output_type": "stream",
"text": [
"Rectangle detected, execute next cells to continue!\n",
"Rectangle detected, execute next cells to continue!\n"
]
}
Expand All @@ -67,7 +66,7 @@
},
{
"cell_type": "code",
"execution_count": 4,
"execution_count": 7,
"id": "39c8fef3",
"metadata": {
"scrolled": true
Expand All @@ -77,9 +76,11 @@
"name": "stderr",
"output_type": "stream",
"text": [
"INFO:mapa:⏳ converting bounding box to STL file with arguments: {'bbox_geometry': {'type': 'Polygon', 'coordinates': [[[5.457402, 44.910359], [5.457402, 45.228674], [6.80603, 45.228674], [6.80603, 44.910359], [5.457402, 44.910359]]]}, 'as_ascii': False, 'model_size': 150, 'output_file': 'mapa_output', 'max_res': False, 'z_offset': 3.0, 'z_scale': 2.0, 'ensure_squared': True, 'split_area_in_tiles': '1x1', 'compress': False, 'allow_caching': True, 'cache_dir': '/home/fabian/make/mapa/cache/', 'tiles': TileFormat(x=1, y=1)}\n",
"INFO:mapa:🚀 using cached tiff!\n",
"INFO:mapa:🎉 successfully generated STL file: /home/fabian/make/mapa/mapa/mapa_output.stl\n"
"INFO:mapa:⏳ converting bounding box to STL file with arguments: {'bbox_geometry': {'type': 'Polygon', 'coordinates': [[[9.634475, 47.370563], [9.634475, 47.569222], [10.142567, 47.569222], [10.142567, 47.370563], [9.634475, 47.370563]]]}, 'as_ascii': False, 'model_size': 150, 'output_file': 'mapa_output', 'max_res': False, 'z_offset': 3.0, 'z_scale': 2.0, 'ensure_squared': False, 'split_area_in_tiles': '1x1', 'compress': False, 'allow_caching': True, 'cache_dir': '.', 'tiles': TileFormat(x=1, y=1)}\n",
"INFO:mapa.stac:⬇️ fetching 2 stac items...\n",
"INFO:mapa.stac:🏞 1/2 downloading stac item ALPSMLC30_N047E010_DSM\n",
"INFO:mapa.stac:🏞 2/2 downloading stac item ALPSMLC30_N047E009_DSM\n",
"INFO:mapa:🎉 successfully generated STL file: /Users/fabian.gebhart/dev/mapa/mapa/mapa_output.stl\n"
]
}
],
Expand All @@ -94,7 +95,7 @@
" ensure_squared=False,\n",
" split_area_in_tiles=\"1x1\",\n",
" compress=False,\n",
" cache_dir=\"/home/fabian/make/mapa/cache/\"\n",
" cache_dir=\"/some/path/to/dir/\",\n",
")"
]
},
Expand All @@ -113,7 +114,7 @@
},
{
"cell_type": "code",
"execution_count": 4,
"execution_count": 8,
"id": "cd7e9b13",
"metadata": {},
"outputs": [
Expand Down Expand Up @@ -203,7 +204,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.6"
"version": "3.10.14"
},
"vscode": {
"interpreter": {
Expand Down
4 changes: 3 additions & 1 deletion mapa/raster.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
log = logging.getLogger(__name__)


def clip_tiff_to_bbox(input_tiff: Path, bbox_geometry: dict, bbox_hash: str, cache_dir: Path) -> Path:
def clip_tiff_to_bbox(
input_tiff: Path, bbox_geometry: dict, bbox_hash: str, cache_dir: Path
) -> Path:
log.debug("🔪 clipping region of interest...")
data = rio.open(input_tiff)
out_img, out_transform = mask(data, shapes=[bbox_geometry], crop=True)
Expand Down
Loading

0 comments on commit 1458b36

Please sign in to comment.