maps4fs 0.8.2__py3-none-any.whl → 0.8.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
maps4fs/generator/map.py CHANGED
@@ -1,10 +1,10 @@
1
1
  """This module contains Map class, which is used to generate map using all components."""
2
2
 
3
+ from __future__ import annotations
4
+
3
5
  import os
4
6
  import shutil
5
- from typing import Any
6
-
7
- from tqdm import tqdm
7
+ from typing import Any, Generator
8
8
 
9
9
  from maps4fs.generator.component import Component
10
10
  from maps4fs.generator.game import Game
@@ -58,31 +58,45 @@ class Map:
58
58
  except Exception as e:
59
59
  raise RuntimeError(f"Can not unpack map template due to error: {e}") from e
60
60
 
61
- def generate(self) -> None:
62
- """Launch map generation using all components."""
63
- with tqdm(total=len(self.game.components), desc="Generating map...") as pbar:
64
- for game_component in self.game.components:
65
- component = game_component(
66
- self.game,
67
- self.coordinates,
68
- self.height,
69
- self.width,
70
- self.map_directory,
71
- self.logger,
72
- **self.kwargs,
61
+ def generate(self) -> Generator[str, None, None]:
62
+ """Launch map generation using all components. Yield component names during the process.
63
+
64
+ Yields:
65
+ Generator[str, None, None]: Component names.
66
+ """
67
+ for game_component in self.game.components:
68
+ component = game_component(
69
+ self.game,
70
+ self.coordinates,
71
+ self.height,
72
+ self.width,
73
+ self.map_directory,
74
+ self.logger,
75
+ **self.kwargs,
76
+ )
77
+
78
+ yield component.__class__.__name__
79
+
80
+ try:
81
+ component.process()
82
+ except Exception as e: # pylint: disable=W0718
83
+ self.logger.error(
84
+ "Error processing component %s: %s",
85
+ component.__class__.__name__,
86
+ e,
87
+ )
88
+ raise e
89
+
90
+ try:
91
+ component.commit_generation_info()
92
+ except Exception as e: # pylint: disable=W0718
93
+ self.logger.error(
94
+ "Error committing generation info for component %s: %s",
95
+ component.__class__.__name__,
96
+ e,
73
97
  )
74
- try:
75
- component.process()
76
- except Exception as e: # pylint: disable=W0718
77
- self.logger.error(
78
- "Error processing component %s: %s",
79
- component.__class__.__name__,
80
- e,
81
- )
82
- raise e
83
- self.components.append(component)
84
-
85
- pbar.update(1)
98
+ raise e
99
+ self.components.append(component)
86
100
 
87
101
  def previews(self) -> list[str]:
88
102
  """Get list of preview images.
@@ -0,0 +1,72 @@
1
+ """This module contains functions and clas for generating path steps."""
2
+
3
+ from typing import NamedTuple
4
+
5
+ from geopy.distance import distance # type: ignore
6
+
7
+ DEFAULT_DISTANCE = 2048
8
+
9
+
10
+ class PathStep(NamedTuple):
11
+ """Represents parameters of one step in the path.
12
+
13
+ Attributes:
14
+ code {str} -- Tile code (N, NE, E, SE, S, SW, W, NW).
15
+ angle {int} -- Angle in degrees (for example 0 for North, 90 for East).
16
+ distance {int} -- Distance in meters from previous step.
17
+ size {tuple[int, int]} -- Size of the tile in pixels (width, height).
18
+ """
19
+
20
+ code: str
21
+ angle: int
22
+ distance: int
23
+ size: tuple[int, int]
24
+
25
+ def get_destination(self, origin: tuple[float, float]) -> tuple[float, float]:
26
+ """Calculate destination coordinates based on origin and step parameters.
27
+
28
+ Arguments:
29
+ origin {tuple[float, float]} -- Origin coordinates (latitude, longitude)
30
+
31
+ Returns:
32
+ tuple[float, float] -- Destination coordinates (latitude, longitude)
33
+ """
34
+ destination = distance(meters=self.distance).destination(origin, self.angle)
35
+ return destination.latitude, destination.longitude
36
+
37
+
38
+ def get_steps(map_height: int, map_width: int) -> list[PathStep]:
39
+ """Return a list of PathStep objects for each tile, which represent a step in the path.
40
+ Moving from the center of the map to North, then clockwise.
41
+
42
+ Arguments:
43
+ map_height {int} -- Height of the map in pixels
44
+ map_width {int} -- Width of the map in pixels
45
+
46
+ Returns:
47
+ list[PathStep] -- List of PathStep objects
48
+ """
49
+ # Move clockwise from N and calculate coordinates and sizes for each tile.
50
+ half_width = int(map_width / 2)
51
+ half_height = int(map_height / 2)
52
+
53
+ half_default_distance = int(DEFAULT_DISTANCE / 2)
54
+
55
+ return [
56
+ PathStep("N", 0, half_height + half_default_distance, (map_width, DEFAULT_DISTANCE)),
57
+ PathStep(
58
+ "NE", 90, half_width + half_default_distance, (DEFAULT_DISTANCE, DEFAULT_DISTANCE)
59
+ ),
60
+ PathStep("E", 180, half_height + half_default_distance, (DEFAULT_DISTANCE, map_height)),
61
+ PathStep(
62
+ "SE", 180, half_height + half_default_distance, (DEFAULT_DISTANCE, DEFAULT_DISTANCE)
63
+ ),
64
+ PathStep("S", 270, half_width + half_default_distance, (map_width, DEFAULT_DISTANCE)),
65
+ PathStep(
66
+ "SW", 270, half_width + half_default_distance, (DEFAULT_DISTANCE, DEFAULT_DISTANCE)
67
+ ),
68
+ PathStep("W", 0, half_height + half_default_distance, (DEFAULT_DISTANCE, map_height)),
69
+ PathStep(
70
+ "NW", 0, half_height + half_default_distance, (DEFAULT_DISTANCE, DEFAULT_DISTANCE)
71
+ ),
72
+ ]
@@ -5,7 +5,7 @@ from __future__ import annotations
5
5
  import json
6
6
  import os
7
7
  import warnings
8
- from typing import Callable, Generator, Optional
8
+ from typing import Any, Callable, Generator, Optional
9
9
 
10
10
  import cv2
11
11
  import numpy as np
@@ -149,7 +149,6 @@ class Texture(Component):
149
149
  self._prepare_weights()
150
150
  self._read_parameters()
151
151
  self.draw()
152
- self.info_sequence()
153
152
 
154
153
  # pylint: disable=W0201
155
154
  def _read_parameters(self) -> None:
@@ -176,23 +175,8 @@ class Texture(Component):
176
175
  self.width_coef = self.width / self.map_width
177
176
  self.logger.debug("Map coefficients (HxW): %s x %s.", self.height_coef, self.width_coef)
178
177
 
179
- def info_sequence(self) -> None:
180
- """Saves generation info to JSON file "generation_info.json".
181
-
182
- Info sequence contains following attributes:
183
- - coordinates
184
- - bbox
185
- - map_height
186
- - map_width
187
- - minimum_x
188
- - minimum_y
189
- - maximum_x
190
- - maximum_y
191
- - height
192
- - width
193
- - height_coef
194
- - width_coef
195
- """
178
+ def info_sequence(self) -> dict[str, Any]:
179
+ """Returns the JSON representation of the generation info for textures."""
196
180
  useful_attributes = [
197
181
  "coordinates",
198
182
  "bbox",
@@ -207,11 +191,7 @@ class Texture(Component):
207
191
  "height_coef",
208
192
  "width_coef",
209
193
  ]
210
- info_sequence = {attr: getattr(self, attr, None) for attr in useful_attributes}
211
-
212
- with open(self.info_save_path, "w") as f: # pylint: disable=W1514
213
- json.dump(info_sequence, f, indent=4)
214
- self.logger.info("Generation info saved to %s.", self.info_save_path)
194
+ return {attr: getattr(self, attr, None) for attr in useful_attributes}
215
195
 
216
196
  def _prepare_weights(self):
217
197
  self.logger.debug("Starting preparing weights from %s layers.", len(self.layers))
@@ -505,7 +485,7 @@ class Texture(Component):
505
485
  merged.shape,
506
486
  merged.dtype,
507
487
  )
508
- preview_path = os.path.join(self.map_directory, "preview_osm.png")
488
+ preview_path = os.path.join(self.previews_directory, "textures_osm.png")
509
489
  cv2.imwrite(preview_path, merged) # pylint: disable=no-member
510
490
  self.logger.info("Preview saved to %s.", preview_path)
511
491
  return preview_path
@@ -0,0 +1,55 @@
1
+ """This module contains the Tile component, which is used to generate a tile of DEM data around
2
+ the map."""
3
+
4
+ import os
5
+
6
+ from maps4fs.generator.dem import DEM
7
+
8
+
9
+ class Tile(DEM):
10
+ """Component for creating a tile of DEM data around the map.
11
+
12
+ Arguments:
13
+ coordinates (tuple[float, float]): The latitude and longitude of the center of the map.
14
+ map_height (int): The height of the map in pixels.
15
+ map_width (int): The width of the map in pixels.
16
+ map_directory (str): The directory where the map files are stored.
17
+ logger (Any, optional): The logger to use. Must have at least three basic methods: debug,
18
+ info, warning. If not provided, default logging will be used.
19
+
20
+ Keyword Arguments:
21
+ tile_code (str): The code of the tile (N, NE, E, SE, S, SW, W, NW).
22
+
23
+ Public Methods:
24
+ get_output_resolution: Return the resolution of the output image.
25
+ process: Launch the component processing.
26
+ make_copy: Override the method to prevent copying the tile.
27
+ """
28
+
29
+ def preprocess(self) -> None:
30
+ """Prepares the component for processing. Reads the tile code from the kwargs and sets the
31
+ DEM path for the tile."""
32
+ super().preprocess()
33
+ self.code = self.kwargs.get("tile_code")
34
+ if not self.code:
35
+ raise ValueError("Tile code was not provided")
36
+
37
+ self.logger.debug(f"Generating tile {self.code}")
38
+
39
+ tiles_directory = os.path.join(self.map_directory, "objects", "tiles")
40
+ os.makedirs(tiles_directory, exist_ok=True)
41
+
42
+ self._dem_path = os.path.join(tiles_directory, f"{self.code}.png")
43
+ self.logger.debug(f"DEM path for tile {self.code} is {self._dem_path}")
44
+
45
+ def get_output_resolution(self) -> tuple[int, int]:
46
+ """Return the resolution of the output image.
47
+
48
+ Returns:
49
+ tuple[int, int]: The width and height of the output image.
50
+ """
51
+ return self.map_width, self.map_height
52
+
53
+ def make_copy(self, *args, **kwargs) -> None:
54
+ """Override the method to prevent copying the tile."""
55
+ pass # pylint: disable=unnecessary-pass