Coverage for src/cstlcore/maps/services.py: 94%

51 statements  

« prev     ^ index     » next       coverage.py v7.9.1, created at 2026-02-19 12:46 +0000

1import math 

2import os 

3import shutil 

4import tempfile 

5import uuid 

6from pathlib import Path 

7 

8from fastapi import UploadFile 

9from PIL import Image 

10 

11from cstlcore.settings import settings 

12 

13 

14def generate_leaflet_tiles( 

15 image_path, output_dir, tile_size=256, min_zoom=0, max_zoom=4 

16): 

17 with Image.open(image_path) as image: 

18 if image.mode not in ("RGBA", "RGB"): 

19 image = image.convert("RGBA") 

20 image_width, image_height = image.size 

21 

22 for zoom in range(min_zoom, max_zoom + 1): 

23 scale = 2**zoom 

24 scaled_width = tile_size * scale 

25 scaled_height = int(image_height * (scaled_width / image_width)) 

26 

27 zoom_image = image.resize( 

28 (scaled_width, scaled_height), Image.Resampling.LANCZOS 

29 ) 

30 

31 pad_width = math.ceil(scaled_width / tile_size) * tile_size 

32 pad_height = math.ceil(scaled_height / tile_size) * tile_size 

33 

34 padded_image = Image.new("RGBA", (pad_width, pad_height), (0, 0, 0, 0)) 

35 padded_image.paste(zoom_image, (0, 0)) 

36 

37 x_tiles = pad_width // tile_size 

38 y_tiles = pad_height // tile_size 

39 

40 for x in range(x_tiles): 

41 for y in range(y_tiles): 

42 left = x * tile_size 

43 upper = y * tile_size 

44 right = left + tile_size 

45 lower = upper + tile_size 

46 

47 tile = padded_image.crop((left, upper, right, lower)) 

48 

49 tile_dir = os.path.join(output_dir, str(zoom), str(x)) 

50 os.makedirs(tile_dir, exist_ok=True) 

51 tile_path = os.path.join(tile_dir, f"{y}.png") 

52 tile.save(tile_path) 

53 

54 

55def verify_and_store_map(uploaded_file: UploadFile, map_id: uuid.UUID): 

56 with tempfile.TemporaryDirectory() as tmpdir: 

57 tmp_path = Path(tmpdir) 

58 uploaded_file_path = tmp_path / f"{uploaded_file.filename}" 

59 uploaded_file_path.parent.mkdir(parents=True, exist_ok=True) 

60 with uploaded_file_path.open("wb") as f: 

61 shutil.copyfileobj(uploaded_file.file, f) 

62 

63 # verify if the uploaded file is an image 

64 try: 

65 with Image.open(uploaded_file_path) as img: 

66 img.verify() # Verify that it is an image 

67 except (IOError, SyntaxError) as e: 

68 raise ValueError(f"Uploaded file is not a valid image: {e}") 

69 

70 # Generate tiles 

71 output_dir = settings.fs.map_directory / f"{map_id}" 

72 output_dir.mkdir(parents=True, exist_ok=True) 

73 generate_leaflet_tiles( 

74 image_path=uploaded_file_path, 

75 output_dir=output_dir, 

76 tile_size=256, 

77 min_zoom=0, 

78 max_zoom=5, 

79 )