maps4fs 0.7.9__py3-none-any.whl → 0.8.3__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/background.py +292 -0
- maps4fs/generator/component.py +66 -0
- maps4fs/generator/config.py +4 -2
- maps4fs/generator/dem.py +115 -32
- maps4fs/generator/game.py +24 -1
- maps4fs/generator/i3d.py +89 -0
- maps4fs/generator/map.py +43 -27
- maps4fs/generator/path_steps.py +72 -0
- maps4fs/generator/texture.py +86 -29
- maps4fs/generator/tile.py +55 -0
- {maps4fs-0.7.9.dist-info → maps4fs-0.8.3.dist-info}/METADATA +172 -25
- maps4fs-0.8.3.dist-info/RECORD +18 -0
- maps4fs-0.7.9.dist-info/RECORD +0 -14
- {maps4fs-0.7.9.dist-info → maps4fs-0.8.3.dist-info}/LICENSE.md +0 -0
- {maps4fs-0.7.9.dist-info → maps4fs-0.8.3.dist-info}/WHEEL +0 -0
- {maps4fs-0.7.9.dist-info → maps4fs-0.8.3.dist-info}/top_level.txt +0 -0
maps4fs/generator/i3d.py
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
"""This module contains the Config class for map settings and configuration."""
|
2
|
+
|
3
|
+
from __future__ import annotations
|
4
|
+
|
5
|
+
import os
|
6
|
+
from xml.etree import ElementTree as ET
|
7
|
+
|
8
|
+
from maps4fs.generator.component import Component
|
9
|
+
|
10
|
+
DEFAULT_HEIGHT_SCALE = 2000
|
11
|
+
DEFAULT_MAX_LOD_DISTANCE = 10000
|
12
|
+
DEFAULT_MAX_LOD_OCCLUDER_DISTANCE = 10000
|
13
|
+
|
14
|
+
|
15
|
+
# pylint: disable=R0903
|
16
|
+
class I3d(Component):
|
17
|
+
"""Component for map i3d file settings and configuration.
|
18
|
+
|
19
|
+
Args:
|
20
|
+
coordinates (tuple[float, float]): The latitude and longitude of the center of the map.
|
21
|
+
map_height (int): The height of the map in pixels.
|
22
|
+
map_width (int): The width of the map in pixels.
|
23
|
+
map_directory (str): The directory where the map files are stored.
|
24
|
+
logger (Any, optional): The logger to use. Must have at least three basic methods: debug,
|
25
|
+
info, warning. If not provided, default logging will be used.
|
26
|
+
"""
|
27
|
+
|
28
|
+
_map_i3d_path: str | None = None
|
29
|
+
|
30
|
+
def preprocess(self) -> None:
|
31
|
+
"""Gets the path to the map I3D file from the game instance and saves it to the instance
|
32
|
+
attribute. If the game does not support I3D files, the attribute is set to None."""
|
33
|
+
try:
|
34
|
+
self._map_i3d_path = self.game.i3d_file_path(self.map_directory)
|
35
|
+
self.logger.debug("Map I3D path: %s.", self._map_i3d_path)
|
36
|
+
except NotImplementedError:
|
37
|
+
self.logger.info("I3D file processing is not implemented for this game.")
|
38
|
+
self._map_i3d_path = None
|
39
|
+
|
40
|
+
def process(self) -> None:
|
41
|
+
"""Updates the map I3D file with the default settings."""
|
42
|
+
self._update_i3d_file()
|
43
|
+
|
44
|
+
def _update_i3d_file(self) -> None:
|
45
|
+
"""Updates the map I3D file with the default settings."""
|
46
|
+
if not self._map_i3d_path:
|
47
|
+
self.logger.info("I3D is not obtained, skipping the update.")
|
48
|
+
return
|
49
|
+
if not os.path.isfile(self._map_i3d_path):
|
50
|
+
self.logger.warning("I3D file not found: %s.", self._map_i3d_path)
|
51
|
+
return
|
52
|
+
|
53
|
+
tree = ET.parse(self._map_i3d_path)
|
54
|
+
|
55
|
+
self.logger.debug("Map I3D file loaded from: %s.", self._map_i3d_path)
|
56
|
+
|
57
|
+
root = tree.getroot()
|
58
|
+
for map_elem in root.iter("Scene"):
|
59
|
+
for terrain_elem in map_elem.iter("TerrainTransformGroup"):
|
60
|
+
terrain_elem.set("heightScale", str(DEFAULT_HEIGHT_SCALE))
|
61
|
+
self.logger.debug(
|
62
|
+
"heightScale attribute set to %s in TerrainTransformGroup element.",
|
63
|
+
DEFAULT_HEIGHT_SCALE,
|
64
|
+
)
|
65
|
+
terrain_elem.set("maxLODDistance", str(DEFAULT_MAX_LOD_DISTANCE))
|
66
|
+
self.logger.debug(
|
67
|
+
"maxLODDistance attribute set to %s in TerrainTransformGroup element.",
|
68
|
+
DEFAULT_MAX_LOD_DISTANCE,
|
69
|
+
)
|
70
|
+
|
71
|
+
terrain_elem.set("occMaxLODDistance", str(DEFAULT_MAX_LOD_OCCLUDER_DISTANCE))
|
72
|
+
self.logger.debug(
|
73
|
+
"occMaxLODDistance attribute set to %s in TerrainTransformGroup element.",
|
74
|
+
DEFAULT_MAX_LOD_OCCLUDER_DISTANCE,
|
75
|
+
)
|
76
|
+
|
77
|
+
self.logger.debug("TerrainTransformGroup element updated in I3D file.")
|
78
|
+
|
79
|
+
tree.write(self._map_i3d_path)
|
80
|
+
self.logger.debug("Map I3D file saved to: %s.", self._map_i3d_path)
|
81
|
+
|
82
|
+
def previews(self) -> list[str]:
|
83
|
+
"""Returns a list of paths to the preview images (empty list).
|
84
|
+
The component does not generate any preview images so it returns an empty list.
|
85
|
+
|
86
|
+
Returns:
|
87
|
+
list[str]: An empty list.
|
88
|
+
"""
|
89
|
+
return []
|
maps4fs/generator/map.py
CHANGED
@@ -1,11 +1,12 @@
|
|
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
|
+
# from maps4fs.generator.background import Background
|
9
10
|
from maps4fs.generator.component import Component
|
10
11
|
from maps4fs.generator.game import Game
|
11
12
|
from maps4fs.logger import Logger
|
@@ -47,6 +48,7 @@ class Map:
|
|
47
48
|
self.logger.debug("Game was set to %s", game.code)
|
48
49
|
|
49
50
|
self.kwargs = kwargs
|
51
|
+
self.logger.debug("Additional arguments: %s", kwargs)
|
50
52
|
|
51
53
|
os.makedirs(self.map_directory, exist_ok=True)
|
52
54
|
self.logger.debug("Map directory created: %s", self.map_directory)
|
@@ -57,31 +59,45 @@ class Map:
|
|
57
59
|
except Exception as e:
|
58
60
|
raise RuntimeError(f"Can not unpack map template due to error: {e}") from e
|
59
61
|
|
60
|
-
def generate(self) -> None:
|
61
|
-
"""Launch map generation using all components.
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
62
|
+
def generate(self) -> Generator[str, None, None]:
|
63
|
+
"""Launch map generation using all components. Yield component names during the process.
|
64
|
+
|
65
|
+
Yields:
|
66
|
+
Generator[str, None, None]: Component names.
|
67
|
+
"""
|
68
|
+
for game_component in self.game.components:
|
69
|
+
component = game_component(
|
70
|
+
self.game,
|
71
|
+
self.coordinates,
|
72
|
+
self.height,
|
73
|
+
self.width,
|
74
|
+
self.map_directory,
|
75
|
+
self.logger,
|
76
|
+
**self.kwargs,
|
77
|
+
)
|
78
|
+
|
79
|
+
yield component.__class__.__name__
|
80
|
+
|
81
|
+
try:
|
82
|
+
component.process()
|
83
|
+
except Exception as e: # pylint: disable=W0718
|
84
|
+
self.logger.error(
|
85
|
+
"Error processing component %s: %s",
|
86
|
+
component.__class__.__name__,
|
87
|
+
e,
|
88
|
+
)
|
89
|
+
raise e
|
90
|
+
|
91
|
+
try:
|
92
|
+
component.commit_generation_info()
|
93
|
+
except Exception as e: # pylint: disable=W0718
|
94
|
+
self.logger.error(
|
95
|
+
"Error committing generation info for component %s: %s",
|
96
|
+
component.__class__.__name__,
|
97
|
+
e,
|
72
98
|
)
|
73
|
-
|
74
|
-
|
75
|
-
except Exception as e: # pylint: disable=W0718
|
76
|
-
self.logger.error(
|
77
|
-
"Error processing component %s: %s",
|
78
|
-
component.__class__.__name__,
|
79
|
-
e,
|
80
|
-
)
|
81
|
-
raise e
|
82
|
-
self.components.append(component)
|
83
|
-
|
84
|
-
pbar.update(1)
|
99
|
+
raise e
|
100
|
+
self.components.append(component)
|
85
101
|
|
86
102
|
def previews(self) -> list[str]:
|
87
103
|
"""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
|
+
]
|
maps4fs/generator/texture.py
CHANGED
@@ -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
|
@@ -57,6 +57,7 @@ class Texture(Component):
|
|
57
57
|
width: int | None = None,
|
58
58
|
color: tuple[int, int, int] | list[int] | None = None,
|
59
59
|
exclude_weight: bool = False,
|
60
|
+
priority: int | None = None,
|
60
61
|
):
|
61
62
|
self.name = name
|
62
63
|
self.count = count
|
@@ -64,6 +65,7 @@ class Texture(Component):
|
|
64
65
|
self.width = width
|
65
66
|
self.color = color if color else (255, 255, 255)
|
66
67
|
self.exclude_weight = exclude_weight
|
68
|
+
self.priority = priority
|
67
69
|
|
68
70
|
def to_json(self) -> dict[str, str | list[str] | bool]: # type: ignore
|
69
71
|
"""Returns dictionary with layer data.
|
@@ -77,6 +79,7 @@ class Texture(Component):
|
|
77
79
|
"width": self.width,
|
78
80
|
"color": list(self.color),
|
79
81
|
"exclude_weight": self.exclude_weight,
|
82
|
+
"priority": self.priority,
|
80
83
|
}
|
81
84
|
|
82
85
|
data = {k: v for k, v in data.items() if v is not None}
|
@@ -120,16 +123,32 @@ class Texture(Component):
|
|
120
123
|
self.layers = [self.Layer.from_json(layer) for layer in layers_schema]
|
121
124
|
self.logger.info("Loaded %s layers.", len(self.layers))
|
122
125
|
|
126
|
+
base_layer = self.get_base_layer()
|
127
|
+
if base_layer:
|
128
|
+
self.logger.info("Base layer found: %s.", base_layer.name)
|
129
|
+
else:
|
130
|
+
self.logger.warning("No base layer found.")
|
131
|
+
|
123
132
|
self._weights_dir = self.game.weights_dir_path(self.map_directory)
|
124
133
|
self.logger.debug("Weights directory: %s.", self._weights_dir)
|
125
134
|
self.info_save_path = os.path.join(self.map_directory, "generation_info.json")
|
126
135
|
self.logger.debug("Generation info save path: %s.", self.info_save_path)
|
127
136
|
|
137
|
+
def get_base_layer(self) -> Layer | None:
|
138
|
+
"""Returns base layer.
|
139
|
+
|
140
|
+
Returns:
|
141
|
+
Layer | None: Base layer.
|
142
|
+
"""
|
143
|
+
for layer in self.layers:
|
144
|
+
if layer.priority == 0:
|
145
|
+
return layer
|
146
|
+
return None
|
147
|
+
|
128
148
|
def process(self):
|
129
149
|
self._prepare_weights()
|
130
150
|
self._read_parameters()
|
131
151
|
self.draw()
|
132
|
-
self.info_sequence()
|
133
152
|
|
134
153
|
# pylint: disable=W0201
|
135
154
|
def _read_parameters(self) -> None:
|
@@ -156,23 +175,8 @@ class Texture(Component):
|
|
156
175
|
self.width_coef = self.width / self.map_width
|
157
176
|
self.logger.debug("Map coefficients (HxW): %s x %s.", self.height_coef, self.width_coef)
|
158
177
|
|
159
|
-
def info_sequence(self) ->
|
160
|
-
"""
|
161
|
-
|
162
|
-
Info sequence contains following attributes:
|
163
|
-
- coordinates
|
164
|
-
- bbox
|
165
|
-
- map_height
|
166
|
-
- map_width
|
167
|
-
- minimum_x
|
168
|
-
- minimum_y
|
169
|
-
- maximum_x
|
170
|
-
- maximum_y
|
171
|
-
- height
|
172
|
-
- width
|
173
|
-
- height_coef
|
174
|
-
- width_coef
|
175
|
-
"""
|
178
|
+
def info_sequence(self) -> dict[str, Any]:
|
179
|
+
"""Returns the JSON representation of the generation info for textures."""
|
176
180
|
useful_attributes = [
|
177
181
|
"coordinates",
|
178
182
|
"bbox",
|
@@ -187,11 +191,7 @@ class Texture(Component):
|
|
187
191
|
"height_coef",
|
188
192
|
"width_coef",
|
189
193
|
]
|
190
|
-
|
191
|
-
|
192
|
-
with open(self.info_save_path, "w") as f: # pylint: disable=W1514
|
193
|
-
json.dump(info_sequence, f, indent=4)
|
194
|
-
self.logger.info("Generation info saved to %s.", self.info_save_path)
|
194
|
+
return {attr: getattr(self, attr, None) for attr in useful_attributes}
|
195
195
|
|
196
196
|
def _prepare_weights(self):
|
197
197
|
self.logger.debug("Starting preparing weights from %s layers.", len(self.layers))
|
@@ -239,22 +239,79 @@ class Texture(Component):
|
|
239
239
|
"""
|
240
240
|
self._layers = layers
|
241
241
|
|
242
|
+
def layers_by_priority(self) -> list[Layer]:
|
243
|
+
"""Returns list of layers sorted by priority: None priority layers are first,
|
244
|
+
then layers are sorted by priority (descending).
|
245
|
+
|
246
|
+
Returns:
|
247
|
+
list[Layer]: List of layers sorted by priority.
|
248
|
+
"""
|
249
|
+
return sorted(
|
250
|
+
self.layers,
|
251
|
+
key=lambda _layer: (
|
252
|
+
_layer.priority is not None,
|
253
|
+
-_layer.priority if _layer.priority is not None else float("inf"),
|
254
|
+
),
|
255
|
+
)
|
256
|
+
|
242
257
|
# pylint: disable=no-member
|
243
258
|
def draw(self) -> None:
|
244
259
|
"""Iterates over layers and fills them with polygons from OSM data."""
|
245
|
-
|
260
|
+
layers = self.layers_by_priority()
|
261
|
+
|
262
|
+
self.logger.debug(
|
263
|
+
"Sorted layers by priority: %s.", [(layer.name, layer.priority) for layer in layers]
|
264
|
+
)
|
265
|
+
|
266
|
+
cumulative_image = None
|
267
|
+
|
268
|
+
for layer in layers:
|
246
269
|
if not layer.tags:
|
247
270
|
self.logger.debug("Layer %s has no tags, there's nothing to draw.", layer.name)
|
248
271
|
continue
|
272
|
+
if layer.priority == 0:
|
273
|
+
self.logger.debug(
|
274
|
+
"Found base layer %s. Postponing that to be the last layer drawn.", layer.name
|
275
|
+
)
|
276
|
+
continue
|
249
277
|
layer_path = layer.path(self._weights_dir)
|
250
278
|
self.logger.debug("Drawing layer %s.", layer_path)
|
279
|
+
layer_image = cv2.imread(layer_path, cv2.IMREAD_UNCHANGED)
|
280
|
+
|
281
|
+
if cumulative_image is None:
|
282
|
+
self.logger.debug("First layer, creating new cumulative image.")
|
283
|
+
cumulative_image = layer_image
|
284
|
+
|
285
|
+
mask = cv2.bitwise_not(cumulative_image)
|
251
286
|
|
252
|
-
img = cv2.imread(layer_path, cv2.IMREAD_UNCHANGED)
|
253
287
|
for polygon in self.polygons(layer.tags, layer.width): # type: ignore
|
254
|
-
cv2.fillPoly(
|
255
|
-
|
288
|
+
cv2.fillPoly(layer_image, [polygon], color=255) # type: ignore
|
289
|
+
|
290
|
+
output_image = cv2.bitwise_and(layer_image, mask)
|
291
|
+
|
292
|
+
cumulative_image = cv2.bitwise_or(cumulative_image, output_image)
|
293
|
+
|
294
|
+
cv2.imwrite(layer_path, output_image)
|
256
295
|
self.logger.debug("Texture %s saved.", layer_path)
|
257
296
|
|
297
|
+
if cumulative_image is not None:
|
298
|
+
self.draw_base_layer(cumulative_image)
|
299
|
+
|
300
|
+
def draw_base_layer(self, cumulative_image: np.ndarray) -> None:
|
301
|
+
"""Draws base layer and saves it into the png file.
|
302
|
+
Base layer is the last layer to be drawn, it fills the remaining area of the map.
|
303
|
+
|
304
|
+
Args:
|
305
|
+
cumulative_image (np.ndarray): Cumulative image with all layers.
|
306
|
+
"""
|
307
|
+
base_layer = self.get_base_layer()
|
308
|
+
if base_layer is not None:
|
309
|
+
layer_path = base_layer.path(self._weights_dir)
|
310
|
+
self.logger.debug("Drawing base layer %s.", layer_path)
|
311
|
+
img = cv2.bitwise_not(cumulative_image)
|
312
|
+
cv2.imwrite(layer_path, img)
|
313
|
+
self.logger.debug("Base texture %s saved.", layer_path)
|
314
|
+
|
258
315
|
def get_relative_x(self, x: float) -> int:
|
259
316
|
"""Converts UTM X coordinate to relative X coordinate in map image.
|
260
317
|
|
@@ -428,7 +485,7 @@ class Texture(Component):
|
|
428
485
|
merged.shape,
|
429
486
|
merged.dtype,
|
430
487
|
)
|
431
|
-
preview_path = os.path.join(self.
|
488
|
+
preview_path = os.path.join(self.previews_directory, "textures_osm.png")
|
432
489
|
cv2.imwrite(preview_path, merged) # pylint: disable=no-member
|
433
490
|
self.logger.info("Preview saved to %s.", preview_path)
|
434
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
|