maps4fs 0.7.9__py3-none-any.whl → 0.8.1__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.
@@ -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.
@@ -22,13 +22,15 @@ class Config(Component):
22
22
  """
23
23
 
24
24
  def preprocess(self) -> None:
25
+ """Gets the path to the map XML file and saves it to the instance variable."""
25
26
  self._map_xml_path = self.game.map_xml_path(self.map_directory)
26
27
  self.logger.debug("Map XML path: %s.", self._map_xml_path)
27
28
 
28
- def process(self):
29
+ def process(self) -> None:
30
+ """Sets the map size in the map.xml file."""
29
31
  self._set_map_size()
30
32
 
31
- def _set_map_size(self):
33
+ def _set_map_size(self) -> None:
32
34
  """Edits map.xml file to set correct map size."""
33
35
  if not os.path.isfile(self._map_xml_path):
34
36
  self.logger.warning("Map XML file not found: %s.", self._map_xml_path)
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 = 21
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
- resampled_data = resampled_data * self.multiplier
109
- self.logger.debug(
110
- "DEM data multiplied by %s. Shape: %s, dtype: %s. Min: %s, max: %s.",
111
- self.multiplier,
112
- resampled_data.shape,
113
- resampled_data.dtype,
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(resampled_data, (self.blur_radius, self.blur_radius), 0)
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,
@@ -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")
@@ -0,0 +1,81 @@
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
+ """Gets the path to the map I3D file from the game instance and saves it to the instance
31
+ attribute. If the game does not support I3D files, the attribute is set to None."""
32
+ try:
33
+ self._map_i3d_path = self.game.i3d_file_path(self.map_directory)
34
+ self.logger.debug("Map I3D path: %s.", self._map_i3d_path)
35
+ except NotImplementedError:
36
+ self.logger.info("I3D file processing is not implemented for this game.")
37
+ self._map_i3d_path = None
38
+
39
+ def process(self) -> None:
40
+ """Updates the map I3D file with the default settings."""
41
+ self._update_i3d_file()
42
+
43
+ def _update_i3d_file(self) -> None:
44
+ """Updates the map I3D file with the default settings."""
45
+ if not self._map_i3d_path:
46
+ self.logger.info("I3D is not obtained, skipping the update.")
47
+ return
48
+ if not os.path.isfile(self._map_i3d_path):
49
+ self.logger.warning("I3D file not found: %s.", self._map_i3d_path)
50
+ return
51
+
52
+ tree = ET.parse(self._map_i3d_path)
53
+
54
+ self.logger.debug("Map I3D file loaded from: %s.", self._map_i3d_path)
55
+
56
+ root = tree.getroot()
57
+ for map_elem in root.iter("Scene"):
58
+ for terrain_elem in map_elem.iter("TerrainTransformGroup"):
59
+ terrain_elem.set("heightScale", str(DEFAULT_HEIGHT_SCALE))
60
+ self.logger.debug(
61
+ "heightScale attribute set to %s in TerrainTransformGroup element.",
62
+ DEFAULT_HEIGHT_SCALE,
63
+ )
64
+ terrain_elem.set("maxLODDistance", str(DEFAULT_MAX_LOD_DISTANCE))
65
+ self.logger.debug(
66
+ "maxLODDistance attribute set to %s in TerrainTransformGroup element.",
67
+ DEFAULT_MAX_LOD_DISTANCE,
68
+ )
69
+ self.logger.debug("TerrainTransformGroup element updated in I3D file.")
70
+
71
+ tree.write(self._map_i3d_path)
72
+ self.logger.debug("Map I3D file saved to: %s.", self._map_i3d_path)
73
+
74
+ def previews(self) -> list[str]:
75
+ """Returns a list of paths to the preview images (empty list).
76
+ The component does not generate any preview images so it returns an empty list.
77
+
78
+ Returns:
79
+ list[str]: An empty list.
80
+ """
81
+ 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)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: maps4fs
3
- Version: 0.7.9
3
+ Version: 0.8.1
4
4
  Summary: Generate map templates for Farming Simulator from real places.
5
5
  Author-email: iwatkot <iwatkot@gmail.com>
6
6
  License: MIT License
@@ -17,6 +17,7 @@ Requires-Dist: opencv-python
17
17
  Requires-Dist: osmnx<2.0.0
18
18
  Requires-Dist: rasterio
19
19
  Requires-Dist: tqdm
20
+ Requires-Dist: folium
20
21
 
21
22
  <div align="center" markdown>
22
23
  <img src="https://github.com/iwatkot/maps4fs/assets/118521851/ffd7f0a3-e317-4c3f-911f-2c2fb736fbfa">
@@ -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=ln_XGUdiPnc4zzhMVEueZRf-FGljeHtCHUnJ6FRlE4w,2264
6
+ maps4fs/generator/dem.py,sha256=L91hOxoXnn2LtpaH7fVA6Ke2i7JNfFk_KGMsVWHVNps,13169
7
+ maps4fs/generator/game.py,sha256=mN9VeN4nk-e43oFujIC5GOGAQOYATKrmeIb1kLHuwtI,6773
8
+ maps4fs/generator/i3d.py,sha256=0f4f21X8PVRDgR5tS_TjWxALJhb6shbVgYtPqWF5s_A,3256
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.1.dist-info/LICENSE.md,sha256=-JY0v7p3dwXze61EbYiK7YEJ2aKrjaFZ8y2xYEOrmRY,1068
12
+ maps4fs-0.8.1.dist-info/METADATA,sha256=zqGNxRYlkNuFZUCCAiAV_PkbBkpBQxe0sm8AJ5dNH84,11866
13
+ maps4fs-0.8.1.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
14
+ maps4fs-0.8.1.dist-info/top_level.txt,sha256=Ue9DSRlejRQRCaJueB0uLcKrWwsEq9zezfv5dI5mV1M,8
15
+ maps4fs-0.8.1.dist-info/RECORD,,
@@ -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=61SOBf1-Tr0AQb_HeYrnvDbKR4rR_7PfzL7i99ADZEA,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.9.dist-info/LICENSE.md,sha256=-JY0v7p3dwXze61EbYiK7YEJ2aKrjaFZ8y2xYEOrmRY,1068
11
- maps4fs-0.7.9.dist-info/METADATA,sha256=AoopE2APfkWbDK6H25pTqXuObQ_suF7a2kOxbiNGEPg,11844
12
- maps4fs-0.7.9.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
13
- maps4fs-0.7.9.dist-info/top_level.txt,sha256=Ue9DSRlejRQRCaJueB0uLcKrWwsEq9zezfv5dI5mV1M,8
14
- maps4fs-0.7.9.dist-info/RECORD,,