Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

upgrade versions, sign items #87

Merged
merged 4 commits into from
Oct 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading