maps4fs 0.7.8__py3-none-any.whl → 0.8.0__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/component.py +1 -0
- maps4fs/generator/dem.py +52 -12
- maps4fs/generator/game.py +23 -1
- maps4fs/generator/i3d.py +77 -0
- maps4fs/generator/map.py +1 -0
- {maps4fs-0.7.8.dist-info → maps4fs-0.8.0.dist-info}/METADATA +1 -1
- maps4fs-0.8.0.dist-info/RECORD +15 -0
- maps4fs-0.7.8.dist-info/RECORD +0 -14
- {maps4fs-0.7.8.dist-info → maps4fs-0.8.0.dist-info}/LICENSE.md +0 -0
- {maps4fs-0.7.8.dist-info → maps4fs-0.8.0.dist-info}/WHEEL +0 -0
- {maps4fs-0.7.8.dist-info → maps4fs-0.8.0.dist-info}/top_level.txt +0 -0
maps4fs/generator/component.py
CHANGED
@@ -15,6 +15,7 @@ class Component:
|
|
15
15
|
"""Base class for all map generation components.
|
16
16
|
|
17
17
|
Args:
|
18
|
+
game (Game): The game instance for which the map is generated.
|
18
19
|
coordinates (tuple[float, float]): The latitude and longitude of the center of the map.
|
19
20
|
map_height (int): The height of the map in pixels.
|
20
21
|
map_width (int): The width of the map in pixels.
|
maps4fs/generator/dem.py
CHANGED
@@ -14,7 +14,7 @@ from maps4fs.generator.component import Component
|
|
14
14
|
|
15
15
|
SRTM = "https://elevation-tiles-prod.s3.amazonaws.com/skadi/{latitude_band}/{tile_name}.hgt.gz"
|
16
16
|
DEFAULT_MULTIPLIER = 1
|
17
|
-
DEFAULT_BLUR_RADIUS =
|
17
|
+
DEFAULT_BLUR_RADIUS = 35
|
18
18
|
|
19
19
|
|
20
20
|
# pylint: disable=R0903
|
@@ -47,6 +47,8 @@ class DEM(Component):
|
|
47
47
|
"DEM value multiplier is %s, blur radius is %s.", self.multiplier, self.blur_radius
|
48
48
|
)
|
49
49
|
|
50
|
+
self.auto_process = self.kwargs.get("auto_process", False)
|
51
|
+
|
50
52
|
# pylint: disable=no-member
|
51
53
|
def process(self) -> None:
|
52
54
|
"""Reads SRTM file, crops it to map size, normalizes and blurs it,
|
@@ -105,15 +107,12 @@ class DEM(Component):
|
|
105
107
|
resampled_data.min(),
|
106
108
|
)
|
107
109
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
resampled_data.
|
114
|
-
resampled_data.min(),
|
115
|
-
resampled_data.max(),
|
116
|
-
)
|
110
|
+
if self.auto_process:
|
111
|
+
self.logger.debug("Auto processing is enabled, will normalize DEM data.")
|
112
|
+
resampled_data = self._normalize_dem(resampled_data)
|
113
|
+
else:
|
114
|
+
self.logger.debug("Auto processing is disabled, DEM data will not be normalized.")
|
115
|
+
resampled_data = resampled_data * self.multiplier
|
117
116
|
|
118
117
|
self.logger.debug(
|
119
118
|
"DEM data was resampled. Shape: %s, dtype: %s. Min: %s, max: %s.",
|
@@ -123,7 +122,9 @@ class DEM(Component):
|
|
123
122
|
resampled_data.max(),
|
124
123
|
)
|
125
124
|
|
126
|
-
resampled_data = cv2.GaussianBlur(
|
125
|
+
resampled_data = cv2.GaussianBlur(
|
126
|
+
resampled_data, (self.blur_radius, self.blur_radius), sigmaX=40, sigmaY=40
|
127
|
+
)
|
127
128
|
self.logger.debug(
|
128
129
|
"Gaussion blur applied to DEM data with kernel size %s.",
|
129
130
|
self.blur_radius,
|
@@ -254,7 +255,7 @@ class DEM(Component):
|
|
254
255
|
|
255
256
|
self.logger.debug("Creating colored preview of DEM data in %s.", colored_dem_path)
|
256
257
|
|
257
|
-
dem_data = cv2.imread(self._dem_path, cv2.
|
258
|
+
dem_data = cv2.imread(self._dem_path, cv2.IMREAD_GRAYSCALE)
|
258
259
|
|
259
260
|
self.logger.debug(
|
260
261
|
"DEM data before normalization. Shape: %s, dtype: %s. Min: %s, max: %s.",
|
@@ -289,3 +290,42 @@ class DEM(Component):
|
|
289
290
|
"""
|
290
291
|
self.logger.debug("Starting DEM previews generation.")
|
291
292
|
return [self.grayscale_preview(), self.colored_preview()]
|
293
|
+
|
294
|
+
def _get_scaling_factor(self, maximum_deviation: int) -> float:
|
295
|
+
ESTIMATED_MAXIMUM_DEVIATION = 1000 # pylint: disable=C0103
|
296
|
+
scaling_factor = maximum_deviation / ESTIMATED_MAXIMUM_DEVIATION
|
297
|
+
return scaling_factor if scaling_factor < 1 else 1
|
298
|
+
|
299
|
+
def _normalize_dem(self, data: np.ndarray) -> np.ndarray:
|
300
|
+
"""Normalize DEM data to 16-bit unsigned integer using max height from settings.
|
301
|
+
Args:
|
302
|
+
data (np.ndarray): DEM data from SRTM file after cropping.
|
303
|
+
Returns:
|
304
|
+
np.ndarray: Normalized DEM data.
|
305
|
+
"""
|
306
|
+
self.logger.debug("Starting DEM data normalization.")
|
307
|
+
# Calculate the difference between the maximum and minimum values in the DEM data.
|
308
|
+
|
309
|
+
max_height = data.max() # 1800
|
310
|
+
min_height = data.min() # 1700
|
311
|
+
max_dev = max_height - min_height # 100
|
312
|
+
self.logger.debug(
|
313
|
+
"Maximum deviation: %s with maximum at %s and minimum at %s.",
|
314
|
+
max_dev,
|
315
|
+
max_height,
|
316
|
+
min_height,
|
317
|
+
)
|
318
|
+
|
319
|
+
scaling_factor = self._get_scaling_factor(max_dev)
|
320
|
+
adjusted_max_height = int(65535 * scaling_factor)
|
321
|
+
self.logger.debug(
|
322
|
+
f"Maximum deviation: {max_dev}. Scaling factor: {scaling_factor}. "
|
323
|
+
f"Adjusted max height: {adjusted_max_height}."
|
324
|
+
)
|
325
|
+
normalized_data = (
|
326
|
+
(data - data.min()) / (data.max() - data.min()) * adjusted_max_height
|
327
|
+
).astype("uint16")
|
328
|
+
self.logger.debug(
|
329
|
+
f"DEM data was normalized to {normalized_data.min()} - {normalized_data.max()}."
|
330
|
+
)
|
331
|
+
return normalized_data
|
maps4fs/generator/game.py
CHANGED
@@ -8,6 +8,7 @@ import os
|
|
8
8
|
|
9
9
|
from maps4fs.generator.config import Config
|
10
10
|
from maps4fs.generator.dem import DEM
|
11
|
+
from maps4fs.generator.i3d import I3d
|
11
12
|
from maps4fs.generator.texture import Texture
|
12
13
|
|
13
14
|
working_directory = os.getcwd()
|
@@ -34,7 +35,7 @@ class Game:
|
|
34
35
|
_map_template_path: str | None = None
|
35
36
|
_texture_schema: str | None = None
|
36
37
|
|
37
|
-
components = [Config, Texture, DEM]
|
38
|
+
components = [Config, Texture, DEM, I3d]
|
38
39
|
|
39
40
|
def __init__(self, map_template_path: str | None = None):
|
40
41
|
if map_template_path:
|
@@ -113,6 +114,16 @@ class Game:
|
|
113
114
|
str: The path to the weights directory."""
|
114
115
|
raise NotImplementedError
|
115
116
|
|
117
|
+
def i3d_file_path(self, map_directory: str) -> str:
|
118
|
+
"""Returns the path to the i3d file.
|
119
|
+
|
120
|
+
Arguments:
|
121
|
+
map_directory (str): The path to the map directory.
|
122
|
+
|
123
|
+
Returns:
|
124
|
+
str: The path to the i3d file."""
|
125
|
+
raise NotImplementedError
|
126
|
+
|
116
127
|
@property
|
117
128
|
def additional_dem_name(self) -> str | None:
|
118
129
|
"""Returns the name of the additional DEM file.
|
@@ -122,6 +133,7 @@ class Game:
|
|
122
133
|
return self._additional_dem_name
|
123
134
|
|
124
135
|
|
136
|
+
# pylint: disable=W0223
|
125
137
|
class FS22(Game):
|
126
138
|
"""Class used to define the game version FS22."""
|
127
139
|
|
@@ -189,3 +201,13 @@ class FS25(Game):
|
|
189
201
|
Returns:
|
190
202
|
str: The path to the weights directory."""
|
191
203
|
return os.path.join(map_directory, "mapUS", "data")
|
204
|
+
|
205
|
+
def i3d_file_path(self, map_directory: str) -> str:
|
206
|
+
"""Returns the path to the i3d file.
|
207
|
+
|
208
|
+
Arguments:
|
209
|
+
map_directory (str): The path to the map directory.
|
210
|
+
|
211
|
+
Returns:
|
212
|
+
str: The path to the i3d file."""
|
213
|
+
return os.path.join(map_directory, "mapUS", "mapUS.i3d")
|
maps4fs/generator/i3d.py
ADDED
@@ -0,0 +1,77 @@
|
|
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
|
+
|
13
|
+
|
14
|
+
# pylint: disable=R0903
|
15
|
+
class I3d(Component):
|
16
|
+
"""Component for map i3d file settings and configuration.
|
17
|
+
|
18
|
+
Args:
|
19
|
+
coordinates (tuple[float, float]): The latitude and longitude of the center of the map.
|
20
|
+
map_height (int): The height of the map in pixels.
|
21
|
+
map_width (int): The width of the map in pixels.
|
22
|
+
map_directory (str): The directory where the map files are stored.
|
23
|
+
logger (Any, optional): The logger to use. Must have at least three basic methods: debug,
|
24
|
+
info, warning. If not provided, default logging will be used.
|
25
|
+
"""
|
26
|
+
|
27
|
+
_map_i3d_path: str | None = None
|
28
|
+
|
29
|
+
def preprocess(self) -> None:
|
30
|
+
try:
|
31
|
+
self._map_i3d_path = self.game.i3d_file_path(self.map_directory)
|
32
|
+
self.logger.debug("Map I3D path: %s.", self._map_i3d_path)
|
33
|
+
except NotImplementedError:
|
34
|
+
self.logger.info("I3D file processing is not implemented for this game.")
|
35
|
+
self._map_i3d_path = None
|
36
|
+
|
37
|
+
def process(self) -> None:
|
38
|
+
self._update_i3d_file()
|
39
|
+
|
40
|
+
def _update_i3d_file(self) -> None:
|
41
|
+
if not self._map_i3d_path:
|
42
|
+
self.logger.info("I3D is not obtained, skipping the update.")
|
43
|
+
return
|
44
|
+
if not os.path.isfile(self._map_i3d_path):
|
45
|
+
self.logger.warning("I3D file not found: %s.", self._map_i3d_path)
|
46
|
+
return
|
47
|
+
|
48
|
+
tree = ET.parse(self._map_i3d_path)
|
49
|
+
|
50
|
+
self.logger.debug("Map I3D file loaded from: %s.", self._map_i3d_path)
|
51
|
+
|
52
|
+
root = tree.getroot()
|
53
|
+
for map_elem in root.iter("Scene"):
|
54
|
+
for terrain_elem in map_elem.iter("TerrainTransformGroup"):
|
55
|
+
terrain_elem.set("heightScale", str(DEFAULT_HEIGHT_SCALE))
|
56
|
+
self.logger.debug(
|
57
|
+
"heightScale attribute set to %s in TerrainTransformGroup element.",
|
58
|
+
DEFAULT_HEIGHT_SCALE,
|
59
|
+
)
|
60
|
+
terrain_elem.set("maxLODDistance", str(DEFAULT_MAX_LOD_DISTANCE))
|
61
|
+
self.logger.debug(
|
62
|
+
"maxLODDistance attribute set to %s in TerrainTransformGroup element.",
|
63
|
+
DEFAULT_MAX_LOD_DISTANCE,
|
64
|
+
)
|
65
|
+
self.logger.debug("TerrainTransformGroup element updated in I3D file.")
|
66
|
+
|
67
|
+
tree.write(self._map_i3d_path)
|
68
|
+
self.logger.debug("Map I3D file saved to: %s.", self._map_i3d_path)
|
69
|
+
|
70
|
+
def previews(self) -> list[str]:
|
71
|
+
"""Returns a list of paths to the preview images (empty list).
|
72
|
+
The component does not generate any preview images so it returns an empty list.
|
73
|
+
|
74
|
+
Returns:
|
75
|
+
list[str]: An empty list.
|
76
|
+
"""
|
77
|
+
return []
|
maps4fs/generator/map.py
CHANGED
@@ -47,6 +47,7 @@ class Map:
|
|
47
47
|
self.logger.debug("Game was set to %s", game.code)
|
48
48
|
|
49
49
|
self.kwargs = kwargs
|
50
|
+
self.logger.debug("Additional arguments: %s", kwargs)
|
50
51
|
|
51
52
|
os.makedirs(self.map_directory, exist_ok=True)
|
52
53
|
self.logger.debug("Map directory created: %s", self.map_directory)
|
@@ -0,0 +1,15 @@
|
|
1
|
+
maps4fs/__init__.py,sha256=da4jmND2Ths9AffnkAKgzLHNkvKFOc_l21gJisPXqWY,155
|
2
|
+
maps4fs/logger.py,sha256=CneeHxQywjNUJXqQrUUSeiDxu95FfrfyK_Si1v0gMZ8,1477
|
3
|
+
maps4fs/generator/__init__.py,sha256=zZMLEkGzb4z0xql650gOtGSvcgX58DnJ2yN3vC2daRk,43
|
4
|
+
maps4fs/generator/component.py,sha256=CL3DNuvKAd44pynLfir_JLnBC0c9Kr233_BbSHQng6Q,3534
|
5
|
+
maps4fs/generator/config.py,sha256=luDYXkKjgOM4-Mft4mHDPP6v-DVIUyWZAMAsCKyvaoY,2108
|
6
|
+
maps4fs/generator/dem.py,sha256=L91hOxoXnn2LtpaH7fVA6Ke2i7JNfFk_KGMsVWHVNps,13169
|
7
|
+
maps4fs/generator/game.py,sha256=mN9VeN4nk-e43oFujIC5GOGAQOYATKrmeIb1kLHuwtI,6773
|
8
|
+
maps4fs/generator/i3d.py,sha256=LW6XUL4uC0rliVG0AEoHb6dk0MP4FGsmZdoFvHKEy4M,2935
|
9
|
+
maps4fs/generator/map.py,sha256=xYbSyGrcEB9CRJhVf8iTRCKLK6BvnewtlKO_Ut--Oik,3858
|
10
|
+
maps4fs/generator/texture.py,sha256=cEXQOLJ5Iq_Nb0C9OmU-9PR-E_5cgM1Q62kG49G3VNg,16019
|
11
|
+
maps4fs-0.8.0.dist-info/LICENSE.md,sha256=-JY0v7p3dwXze61EbYiK7YEJ2aKrjaFZ8y2xYEOrmRY,1068
|
12
|
+
maps4fs-0.8.0.dist-info/METADATA,sha256=ytEA4ZWBi-nbFpikYmCozC9yohqV5MekJTLaooMih2k,11844
|
13
|
+
maps4fs-0.8.0.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
14
|
+
maps4fs-0.8.0.dist-info/top_level.txt,sha256=Ue9DSRlejRQRCaJueB0uLcKrWwsEq9zezfv5dI5mV1M,8
|
15
|
+
maps4fs-0.8.0.dist-info/RECORD,,
|
maps4fs-0.7.8.dist-info/RECORD
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
maps4fs/__init__.py,sha256=da4jmND2Ths9AffnkAKgzLHNkvKFOc_l21gJisPXqWY,155
|
2
|
-
maps4fs/logger.py,sha256=CneeHxQywjNUJXqQrUUSeiDxu95FfrfyK_Si1v0gMZ8,1477
|
3
|
-
maps4fs/generator/__init__.py,sha256=zZMLEkGzb4z0xql650gOtGSvcgX58DnJ2yN3vC2daRk,43
|
4
|
-
maps4fs/generator/component.py,sha256=BU_HNNyKXuEBohQJRTlXmNdSNrjolwX-ZzqqSSuzTrE,3463
|
5
|
-
maps4fs/generator/config.py,sha256=luDYXkKjgOM4-Mft4mHDPP6v-DVIUyWZAMAsCKyvaoY,2108
|
6
|
-
maps4fs/generator/dem.py,sha256=nVDDtDqj_rEOO2oPviBf4cUbqCnYmZbjDqypINzMLrs,11414
|
7
|
-
maps4fs/generator/game.py,sha256=dfbLyu1yqbHkBWmJu1uANCX-84vkdlvqR6hA6JCiIsE,6107
|
8
|
-
maps4fs/generator/map.py,sha256=zLIFAjjz-Y7u_FG-UjYoGiqBreLwuverkYY8Er9L_Qg,3796
|
9
|
-
maps4fs/generator/texture.py,sha256=cEXQOLJ5Iq_Nb0C9OmU-9PR-E_5cgM1Q62kG49G3VNg,16019
|
10
|
-
maps4fs-0.7.8.dist-info/LICENSE.md,sha256=-JY0v7p3dwXze61EbYiK7YEJ2aKrjaFZ8y2xYEOrmRY,1068
|
11
|
-
maps4fs-0.7.8.dist-info/METADATA,sha256=D8NKpG80a8SOHQqnDagfQGCnWhYvdBgo10JivZTjZdA,11844
|
12
|
-
maps4fs-0.7.8.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
13
|
-
maps4fs-0.7.8.dist-info/top_level.txt,sha256=Ue9DSRlejRQRCaJueB0uLcKrWwsEq9zezfv5dI5mV1M,8
|
14
|
-
maps4fs-0.7.8.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|