maps4fs 2.1.4__tar.gz → 2.1.6__tar.gz

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.
Files changed (34) hide show
  1. {maps4fs-2.1.4 → maps4fs-2.1.6}/PKG-INFO +1 -1
  2. {maps4fs-2.1.4 → maps4fs-2.1.6}/maps4fs/generator/component/background.py +26 -7
  3. {maps4fs-2.1.4 → maps4fs-2.1.6}/maps4fs/generator/component/base/component_image.py +7 -1
  4. {maps4fs-2.1.4 → maps4fs-2.1.6}/maps4fs/generator/component/base/component_mesh.py +3 -1
  5. {maps4fs-2.1.4 → maps4fs-2.1.6}/maps4fs/generator/component/dem.py +2 -2
  6. {maps4fs-2.1.4 → maps4fs-2.1.6}/maps4fs/generator/component/satellite.py +2 -0
  7. {maps4fs-2.1.4 → maps4fs-2.1.6}/maps4fs/generator/component/texture.py +2 -3
  8. maps4fs-2.1.6/maps4fs/generator/config.py +52 -0
  9. {maps4fs-2.1.4 → maps4fs-2.1.6}/maps4fs/generator/map.py +91 -19
  10. {maps4fs-2.1.4 → maps4fs-2.1.6}/maps4fs/generator/settings.py +105 -1
  11. {maps4fs-2.1.4 → maps4fs-2.1.6}/maps4fs/logger.py +0 -3
  12. {maps4fs-2.1.4 → maps4fs-2.1.6}/maps4fs.egg-info/PKG-INFO +1 -1
  13. {maps4fs-2.1.4 → maps4fs-2.1.6}/maps4fs.egg-info/SOURCES.txt +1 -0
  14. {maps4fs-2.1.4 → maps4fs-2.1.6}/pyproject.toml +1 -1
  15. {maps4fs-2.1.4 → maps4fs-2.1.6}/LICENSE.md +0 -0
  16. {maps4fs-2.1.4 → maps4fs-2.1.6}/README.md +0 -0
  17. {maps4fs-2.1.4 → maps4fs-2.1.6}/maps4fs/__init__.py +0 -0
  18. {maps4fs-2.1.4 → maps4fs-2.1.6}/maps4fs/generator/__init__.py +0 -0
  19. {maps4fs-2.1.4 → maps4fs-2.1.6}/maps4fs/generator/component/__init__.py +0 -0
  20. {maps4fs-2.1.4 → maps4fs-2.1.6}/maps4fs/generator/component/base/__init__.py +0 -0
  21. {maps4fs-2.1.4 → maps4fs-2.1.6}/maps4fs/generator/component/base/component.py +0 -0
  22. {maps4fs-2.1.4 → maps4fs-2.1.6}/maps4fs/generator/component/base/component_xml.py +0 -0
  23. {maps4fs-2.1.4 → maps4fs-2.1.6}/maps4fs/generator/component/config.py +0 -0
  24. {maps4fs-2.1.4 → maps4fs-2.1.6}/maps4fs/generator/component/grle.py +0 -0
  25. {maps4fs-2.1.4 → maps4fs-2.1.6}/maps4fs/generator/component/i3d.py +0 -0
  26. {maps4fs-2.1.4 → maps4fs-2.1.6}/maps4fs/generator/component/layer.py +0 -0
  27. {maps4fs-2.1.4 → maps4fs-2.1.6}/maps4fs/generator/game.py +0 -0
  28. {maps4fs-2.1.4 → maps4fs-2.1.6}/maps4fs/generator/qgis.py +0 -0
  29. {maps4fs-2.1.4 → maps4fs-2.1.6}/maps4fs/generator/statistics.py +0 -0
  30. {maps4fs-2.1.4 → maps4fs-2.1.6}/maps4fs.egg-info/dependency_links.txt +0 -0
  31. {maps4fs-2.1.4 → maps4fs-2.1.6}/maps4fs.egg-info/requires.txt +0 -0
  32. {maps4fs-2.1.4 → maps4fs-2.1.6}/maps4fs.egg-info/top_level.txt +0 -0
  33. {maps4fs-2.1.4 → maps4fs-2.1.6}/setup.cfg +0 -0
  34. {maps4fs-2.1.4 → maps4fs-2.1.6}/tests/test_generator.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: maps4fs
3
- Version: 2.1.4
3
+ Version: 2.1.6
4
4
  Summary: Generate map templates for Farming Simulator from real places.
5
5
  Author-email: iwatkot <iwatkot@gmail.com>
6
6
  License: Apache License 2.0
@@ -69,6 +69,8 @@ class Background(MeshComponent, ImageComponent):
69
69
  )
70
70
  self.not_resized_path: str = os.path.join(self.background_directory, "not_resized.png")
71
71
 
72
+ self.flatten_water_to: int | None = None
73
+
72
74
  self.dem = DEM(
73
75
  self.game,
74
76
  self.map,
@@ -520,11 +522,22 @@ class Background(MeshComponent, ImageComponent):
520
522
  if self.map.shared_settings.mesh_z_scaling_factor is not None
521
523
  else 257
522
524
  )
525
+ flatten_to = None
526
+ subtract_by = int(self.map.dem_settings.water_depth * z_scaling_factor)
527
+
528
+ if self.map.background_settings.flatten_water:
529
+ try:
530
+ mask = water_resources_image == 255
531
+ flatten_to = int(np.mean(dem_image[mask]) - subtract_by) # type: ignore
532
+ self.flatten_water_to = flatten_to # type: ignore
533
+ except Exception as e:
534
+ self.logger.warning("Error occurred while flattening water: %s", e)
523
535
 
524
536
  dem_image = self.subtract_by_mask(
525
537
  dem_image, # type: ignore
526
538
  water_resources_image, # type: ignore
527
- int(self.map.dem_settings.water_depth * z_scaling_factor),
539
+ subtract_by=subtract_by,
540
+ flatten_to=flatten_to,
528
541
  )
529
542
 
530
543
  dem_image = self.blur_edges_by_mask(
@@ -598,7 +611,7 @@ class Background(MeshComponent, ImageComponent):
598
611
  return
599
612
 
600
613
  # Create a mesh from the 3D polygons
601
- mesh = self.mesh_from_3d_polygons(fitted_polygons)
614
+ mesh = self.mesh_from_3d_polygons(fitted_polygons, single_z_value=self.flatten_water_to)
602
615
  if mesh is None:
603
616
  self.logger.warning("No mesh could be created from the water polygons.")
604
617
  return
@@ -611,13 +624,16 @@ class Background(MeshComponent, ImageComponent):
611
624
  mesh.export(line_based_save_path)
612
625
  self.logger.debug("Line-based water mesh saved to %s", line_based_save_path)
613
626
 
614
- def mesh_from_3d_polygons(self, polygons: list[shapely.Polygon]) -> Trimesh | None:
627
+ def mesh_from_3d_polygons(
628
+ self, polygons: list[shapely.Polygon], single_z_value: int | None = None
629
+ ) -> Trimesh | None:
615
630
  """Create a simple mesh from a list of 3D shapely Polygons.
616
631
  Each polygon must be flat (all Z the same or nearly the same for each polygon).
617
632
  Returns a single Trimesh mesh.
618
633
 
619
634
  Arguments:
620
635
  polygons (list[shapely.Polygon]): List of 3D shapely Polygons to create the mesh from.
636
+ single_z_value (int | None): The Z value to use for all vertices in the mesh.
621
637
 
622
638
  Returns:
623
639
  Trimesh: A single Trimesh object containing the mesh created from the polygons.
@@ -649,9 +665,12 @@ class Background(MeshComponent, ImageComponent):
649
665
  dists = np.linalg.norm(exterior_2d - v[:2], axis=1)
650
666
  idx = np.argmin(dists)
651
667
  # z = exterior_coords[idx, 2]
652
- z = self.get_z_coordinate_from_dem(
653
- not_resized_dem, exterior_coords[idx, 0], exterior_coords[idx, 1] # type: ignore
654
- )
668
+ if not single_z_value:
669
+ z = self.get_z_coordinate_from_dem(
670
+ not_resized_dem, exterior_coords[idx, 0], exterior_coords[idx, 1] # type: ignore
671
+ )
672
+ else:
673
+ z = single_z_value
655
674
  vertices_3d.append([v[0], v[1], z])
656
675
  vertices_3d = np.array(vertices_3d) # type: ignore
657
676
 
@@ -814,7 +833,7 @@ class Background(MeshComponent, ImageComponent):
814
833
  full_mask[mask == 255] = 255
815
834
 
816
835
  main_dem_path = self.game.dem_file_path(self.map_directory)
817
- dem_image = self.blur_by_mask(dem_image, full_mask)
836
+ dem_image = self.blur_by_mask(dem_image, full_mask, blur_radius=5)
818
837
  dem_image = self.blur_edges_by_mask(dem_image, full_mask)
819
838
 
820
839
  output_size = dem_image.shape[0] + 1
@@ -67,6 +67,7 @@ class ImageComponent(Component):
67
67
  mask_by: int = 255,
68
68
  erode_kernel: int | None = 3,
69
69
  erode_iter: int | None = 1,
70
+ flatten_to: int | None = None,
70
71
  ) -> np.ndarray:
71
72
  """Subtracts a value from the image where the mask is equal to the mask by value.
72
73
 
@@ -77,6 +78,7 @@ class ImageComponent(Component):
77
78
  mask_by (int, optional): The value to mask by. Defaults to 255.
78
79
  erode_kernel (int, optional): The kernel size for the erosion. Defaults to 3.
79
80
  erode_iter (int, optional): The number of iterations for the erosion. Defaults to 1.
81
+ flatten_to_mean (bool, optional): Whether to flatten the image to the mean value.
80
82
 
81
83
  Returns:
82
84
  np.ndarray: The image with the subtracted value.
@@ -90,6 +92,10 @@ class ImageComponent(Component):
90
92
  ).astype(bool)
91
93
 
92
94
  image[mask] = image[mask] - subtract_by
95
+
96
+ if flatten_to:
97
+ image[mask] = flatten_to
98
+
93
99
  return image
94
100
 
95
101
  @staticmethod
@@ -190,7 +196,7 @@ class ImageComponent(Component):
190
196
  raise ValueError("Data and mask must have the same dimensions.")
191
197
 
192
198
  # Create a blurred version of the data
193
- blurred_data = cv2.GaussianBlur(data, (blur_radius, blur_radius), sigmaX=3)
199
+ blurred_data = cv2.GaussianBlur(data, (blur_radius, blur_radius), sigmaX=10)
194
200
 
195
201
  # Combine the blurred data with the original data using the mask
196
202
  result = np.where(mask == 255, blurred_data, data)
@@ -235,7 +235,9 @@ class MeshComponent(Component):
235
235
  pass
236
236
  cube_mesh = trimesh.creation.box([remove_size, remove_size, z_size * 4])
237
237
 
238
- return trimesh.boolean.difference([mesh_copy, cube_mesh], check_volume=False)
238
+ return trimesh.boolean.difference(
239
+ [mesh_copy, cube_mesh], check_volume=False, engine="blender"
240
+ )
239
241
 
240
242
  @staticmethod
241
243
  def mesh_to_origin(mesh: trimesh.Trimesh) -> trimesh.Trimesh:
@@ -10,6 +10,7 @@ from pydtmdl import DTMProvider
10
10
  # import rasterio # type: ignore
11
11
  from pympler import asizeof # type: ignore
12
12
 
13
+ import maps4fs.generator.config as mfscfg
13
14
  from maps4fs.generator.component.base.component_image import ImageComponent
14
15
 
15
16
 
@@ -30,7 +31,6 @@ class DEM(ImageComponent):
30
31
 
31
32
  def preprocess(self) -> None:
32
33
  self._dem_path = self.game.dem_file_path(self.map_directory)
33
- self.temp_dir = "temp"
34
34
 
35
35
  self.logger.debug("Map size: %s x %s.", self.map_size, self.map_size)
36
36
  self.logger.debug(
@@ -44,7 +44,7 @@ class DEM(ImageComponent):
44
44
  coordinates=self.coordinates,
45
45
  user_settings=self.map.dtm_provider_settings,
46
46
  size=self.map_rotated_size,
47
- directory=self.temp_dir,
47
+ directory=mfscfg.DTM_CACHE_DIR,
48
48
  logger=self.logger,
49
49
  )
50
50
 
@@ -7,6 +7,7 @@ from typing import NamedTuple
7
7
 
8
8
  from pygmdl import save_image
9
9
 
10
+ import maps4fs.generator.config as mfscfg
10
11
  from maps4fs.generator.component.base.component_image import ImageComponent
11
12
  from maps4fs.generator.settings import Parameters
12
13
 
@@ -85,6 +86,7 @@ class Satellite(ImageComponent):
85
86
  zoom=task.zoom,
86
87
  from_center=True,
87
88
  logger=self.logger,
89
+ tiles_dir=mfscfg.SAT_CACHE_DIR,
88
90
  )
89
91
 
90
92
  except Exception as e:
@@ -20,8 +20,7 @@ from tqdm import tqdm
20
20
 
21
21
  from maps4fs.generator.component.base.component_image import ImageComponent
22
22
  from maps4fs.generator.component.layer import Layer
23
-
24
- PREVIEW_MAXIMUM_SIZE = 2048
23
+ from maps4fs.generator.settings import Parameters
25
24
 
26
25
 
27
26
  class Texture(ImageComponent):
@@ -834,7 +833,7 @@ class Texture(ImageComponent):
834
833
  Returns:
835
834
  str: Path to the preview.
836
835
  """
837
- scaling_factor = PREVIEW_MAXIMUM_SIZE / self.map_size
836
+ scaling_factor = Parameters.PREVIEW_MAXIMUM_SIZE / self.map_size
838
837
 
839
838
  preview_size = (
840
839
  int(self.map_size * scaling_factor),
@@ -0,0 +1,52 @@
1
+ """This module contains configuration files for the maps4fs generator."""
2
+
3
+ import os
4
+ import subprocess
5
+
6
+ from osmnx import settings as ox_settings
7
+
8
+ MFS_ROOT_DIR = os.getenv("MFS_ROOT_DIRECTORY", os.path.join(os.getcwd(), "mfsrootdir"))
9
+ MFS_CACHE_DIR = os.path.join(MFS_ROOT_DIR, "cache")
10
+ MFS_DATA_DIR = os.path.join(MFS_ROOT_DIR, "data")
11
+ os.makedirs(MFS_CACHE_DIR, exist_ok=True)
12
+ os.makedirs(MFS_DATA_DIR, exist_ok=True)
13
+
14
+ DTM_CACHE_DIR = os.path.join(MFS_CACHE_DIR, "dtm")
15
+ SAT_CACHE_DIR = os.path.join(MFS_CACHE_DIR, "sat")
16
+
17
+ osmnx_cache = os.path.join(MFS_CACHE_DIR, "osmnx")
18
+ osmnx_data = os.path.join(MFS_CACHE_DIR, "odata")
19
+ os.makedirs(osmnx_cache, exist_ok=True)
20
+ os.makedirs(osmnx_data, exist_ok=True)
21
+
22
+
23
+ ox_settings.cache_folder = osmnx_cache
24
+ ox_settings.data_folder = osmnx_data
25
+
26
+
27
+ def get_package_version(package_name: str) -> str:
28
+ """Get the package version.
29
+
30
+ Arguments:
31
+ package_name (str): The name of the package to check.
32
+
33
+ Returns:
34
+ str: The version of the package, or "unknown" if it cannot be determined.
35
+ """
36
+ try:
37
+ result = subprocess.run(
38
+ [os.sys.executable, "-m", "pip", "show", package_name], # type: ignore
39
+ stdout=subprocess.PIPE,
40
+ stderr=subprocess.PIPE,
41
+ text=True,
42
+ check=True,
43
+ )
44
+ for line in result.stdout.splitlines():
45
+ if line.startswith("Version:"):
46
+ return line.split(":", 1)[1].strip()
47
+ return "unknown"
48
+ except Exception:
49
+ return "unknown"
50
+
51
+
52
+ PACKAGE_VERSION = get_package_version("maps4fs")
@@ -5,6 +5,7 @@ from __future__ import annotations
5
5
  import json
6
6
  import os
7
7
  import shutil
8
+ from datetime import datetime
8
9
  from typing import Any, Generator
9
10
  from xml.etree import ElementTree as ET
10
11
 
@@ -14,13 +15,16 @@ from osmnx._errors import InsufficientResponseError
14
15
  from pydtmdl import DTMProvider
15
16
  from pydtmdl.base.dtm import DTMProviderSettings
16
17
 
18
+ import maps4fs.generator.config as mfscfg
17
19
  from maps4fs.generator.component import Background, Component, Layer, Texture
18
20
  from maps4fs.generator.game import FS25, Game
19
21
  from maps4fs.generator.settings import (
20
22
  BackgroundSettings,
21
23
  DEMSettings,
24
+ GenerationSettings,
22
25
  GRLESettings,
23
26
  I3DSettings,
27
+ MainSettings,
24
28
  SatelliteSettings,
25
29
  SharedSettings,
26
30
  TextureSettings,
@@ -48,7 +52,7 @@ class Map:
48
52
  coordinates: tuple[float, float],
49
53
  size: int,
50
54
  rotation: int,
51
- map_directory: str,
55
+ map_directory: str | None = None,
52
56
  logger: Any = None,
53
57
  custom_osm: str | None = None,
54
58
  dem_settings: DEMSettings = DEMSettings(),
@@ -81,11 +85,13 @@ class Map:
81
85
  self.dtm_provider_settings = dtm_provider_settings
82
86
  self.components: list[Component] = []
83
87
  self.coordinates = coordinates
84
- self.map_directory = map_directory
88
+ self.map_directory = map_directory or self.suggest_map_directory(
89
+ coordinates=coordinates, game_code=game.code # type: ignore
90
+ )
85
91
 
86
- try:
87
- main_settings = {
88
- "game": game.code,
92
+ main_settings = MainSettings.from_json(
93
+ {
94
+ "game": game.code, # type: ignore
89
95
  "latitude": coordinates[0],
90
96
  "longitude": coordinates[1],
91
97
  "country": self.get_country_by_coordinates(),
@@ -95,11 +101,24 @@ class Map:
95
101
  "custom_osm": bool(custom_osm),
96
102
  "is_public": kwargs.get("is_public", False),
97
103
  "api_request": kwargs.get("api_request", False),
104
+ "date": datetime.now().strftime("%Y-%m-%d"),
105
+ "time": datetime.now().strftime("%H:%M:%S"),
106
+ "version": mfscfg.PACKAGE_VERSION,
107
+ "completed": False,
108
+ "error": None,
98
109
  }
99
- send_main_settings(main_settings)
110
+ )
111
+ main_settings_json = main_settings.to_json()
112
+
113
+ try:
114
+ send_main_settings(main_settings_json)
100
115
  except Exception as e:
101
116
  self.logger.error("Error sending main settings: %s", e)
102
117
 
118
+ self.main_settings_path = os.path.join(self.map_directory, "main_settings.json")
119
+ with open(self.main_settings_path, "w", encoding="utf-8") as file:
120
+ json.dump(main_settings_json, file, indent=4)
121
+
103
122
  log_entry = ""
104
123
  log_entry += f"Map instance created for Game: {game.code}. "
105
124
  log_entry += f"Coordinates: {coordinates}. Size: {size}. Rotation: {rotation}. "
@@ -154,19 +173,14 @@ class Map:
154
173
  os.makedirs(self.map_directory, exist_ok=True)
155
174
  self.logger.debug("Map directory created: %s", self.map_directory)
156
175
 
157
- settings = [
158
- dem_settings,
159
- background_settings,
160
- grle_settings,
161
- i3d_settings,
162
- texture_settings,
163
- satellite_settings,
164
- ]
165
-
166
- settings_json = {}
167
-
168
- for setting in settings:
169
- settings_json[setting.__class__.__name__] = setting.model_dump()
176
+ settings_json = GenerationSettings(
177
+ dem_settings=dem_settings,
178
+ background_settings=background_settings,
179
+ grle_settings=grle_settings,
180
+ i3d_settings=i3d_settings,
181
+ texture_settings=texture_settings,
182
+ satellite_settings=satellite_settings,
183
+ ).to_json()
170
184
 
171
185
  try:
172
186
  send_advanced_settings(settings_json)
@@ -205,6 +219,52 @@ class Map:
205
219
  except Exception as e:
206
220
  raise RuntimeError(f"Can not unpack map template due to error: {e}") from e
207
221
 
222
+ self.logger.debug(
223
+ "MFS_DATA_DIR: %s. MFS_CACHE_DIR %s", mfscfg.MFS_DATA_DIR, mfscfg.MFS_CACHE_DIR
224
+ )
225
+
226
+ @staticmethod
227
+ def suggest_map_directory(coordinates: tuple[float, float], game_code: str) -> str:
228
+ """Generate map directory path from coordinates and game code.
229
+
230
+ Returns:
231
+ str: Map directory path.
232
+ """
233
+ return os.path.join(mfscfg.MFS_DATA_DIR, Map.suggest_directory_name(coordinates, game_code))
234
+
235
+ @staticmethod
236
+ def suggest_directory_name(coordinates: tuple[float, float], game_code: str) -> str:
237
+ """Generate directory name from coordinates and game code.
238
+
239
+ Returns:
240
+ str: Directory name.
241
+ """
242
+ lat, lon = coordinates
243
+ latr = Map.coordinate_to_string(lat)
244
+ lonr = Map.coordinate_to_string(lon)
245
+ return f"{Map.get_timestamp()}_{game_code}_{latr}_{lonr}".lower()
246
+
247
+ @staticmethod
248
+ def get_timestamp() -> str:
249
+ """Get current underscore-separated timestamp.
250
+
251
+ Returns:
252
+ str: Current timestamp.
253
+ """
254
+ return datetime.now().strftime("%Y%m%d_%H%M%S")
255
+
256
+ @staticmethod
257
+ def coordinate_to_string(coordinate: float) -> str:
258
+ """Convert coordinate to string with 3 decimal places.
259
+
260
+ Arguments:
261
+ coordinate (float): Coordinate value.
262
+
263
+ Returns:
264
+ str: Coordinate as string.
265
+ """
266
+ return f"{coordinate:.3f}".replace(".", "_")
267
+
208
268
  @property
209
269
  def texture_schema(self) -> list[dict[str, Any]] | None:
210
270
  """Return texture schema (custom if provided, default otherwise).
@@ -230,6 +290,7 @@ class Map:
230
290
  self.size,
231
291
  self.rotation,
232
292
  )
293
+ error_text = None
233
294
 
234
295
  for game_component in self.game.components:
235
296
  component = game_component(
@@ -256,8 +317,19 @@ class Map:
256
317
  component.__class__.__name__,
257
318
  e,
258
319
  )
320
+ error_text = str(e)
259
321
  raise e
260
322
 
323
+ finally:
324
+ with open(self.main_settings_path, "r", encoding="utf-8") as file:
325
+ main_settings_json = json.load(file)
326
+
327
+ main_settings_json["completed"] = True
328
+ main_settings_json["error"] = error_text
329
+
330
+ with open(self.main_settings_path, "w", encoding="utf-8") as file:
331
+ json.dump(main_settings_json, file, indent=4)
332
+
261
333
  try:
262
334
  component.commit_generation_info()
263
335
  except Exception as e:
@@ -3,7 +3,7 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import re
6
- from typing import Any
6
+ from typing import Any, NamedTuple
7
7
 
8
8
  from pydantic import BaseModel, ConfigDict
9
9
 
@@ -164,6 +164,8 @@ class BackgroundSettings(SettingsModel):
164
164
  remove_center (bool): remove the center of the background terrain.
165
165
  It will be used to remove the center of the map where the player starts.
166
166
  flatten_roads (bool): if True, roads will be flattened in the DEM data.
167
+ flatten_water (bool): if True, the bottom of the water resources will be flattened
168
+ to the average height of the water resources.
167
169
  """
168
170
 
169
171
  generate_background: bool = False
@@ -171,6 +173,7 @@ class BackgroundSettings(SettingsModel):
171
173
  water_blurriness: int = 20
172
174
  remove_center: bool = True
173
175
  flatten_roads: bool = False
176
+ flatten_water: bool = False
174
177
 
175
178
 
176
179
  class GRLESettings(SettingsModel):
@@ -249,3 +252,104 @@ class SatelliteSettings(SettingsModel):
249
252
 
250
253
  download_images: bool = False
251
254
  zoom_level: int = 16
255
+
256
+
257
+ class GenerationSettings(BaseModel):
258
+ """Represents the settings for the map generation process."""
259
+
260
+ dem_settings: DEMSettings
261
+ background_settings: BackgroundSettings
262
+ grle_settings: GRLESettings
263
+ i3d_settings: I3DSettings
264
+ texture_settings: TextureSettings
265
+ satellite_settings: SatelliteSettings
266
+
267
+ def to_json(self) -> dict[str, Any]:
268
+ """Convert the GenerationSettings instance to JSON format.
269
+
270
+ Returns:
271
+ dict[str, Any]: JSON representation of the GenerationSettings.
272
+ """
273
+ return {
274
+ "DEMSettings": self.dem_settings.model_dump(),
275
+ "BackgroundSettings": self.background_settings.model_dump(),
276
+ "GRLESettings": self.grle_settings.model_dump(),
277
+ "I3DSettings": self.i3d_settings.model_dump(),
278
+ "TextureSettings": self.texture_settings.model_dump(),
279
+ "SatelliteSettings": self.satellite_settings.model_dump(),
280
+ }
281
+
282
+ @classmethod
283
+ def from_json(cls, data: dict[str, Any]) -> GenerationSettings:
284
+ """Create a GenerationSettings instance from JSON data.
285
+
286
+ Arguments:
287
+ data (dict[str, Any]): JSON data.
288
+
289
+ Returns:
290
+ GenerationSettings: Instance of GenerationSettings.
291
+ """
292
+ return cls(
293
+ dem_settings=DEMSettings(**data["DEMSettings"]),
294
+ background_settings=BackgroundSettings(**data["BackgroundSettings"]),
295
+ grle_settings=GRLESettings(**data["GRLESettings"]),
296
+ i3d_settings=I3DSettings(**data["I3DSettings"]),
297
+ texture_settings=TextureSettings(**data["TextureSettings"]),
298
+ satellite_settings=SatelliteSettings(**data["SatelliteSettings"]),
299
+ )
300
+
301
+
302
+ class MainSettings(NamedTuple):
303
+ """Represents the main settings for the map generation."""
304
+
305
+ game: str
306
+ latitude: float
307
+ longitude: float
308
+ country: str
309
+ size: int
310
+ rotation: int
311
+ dtm_provider: str
312
+ custom_osm: bool
313
+ is_public: bool
314
+ api_request: bool
315
+ date: str
316
+ time: str
317
+ version: str
318
+ completed: bool
319
+ error: str | None = None
320
+
321
+ @classmethod
322
+ def from_json(cls, data: dict[str, str | float | int | bool | None]) -> MainSettings:
323
+ """Create a MainSettings instance from JSON data.
324
+
325
+ Arguments:
326
+ data (dict[str, str | float | int | bool | None]): JSON data.
327
+
328
+ Returns:
329
+ MainSettings: Instance of MainSettings.
330
+ """
331
+ return cls(**data) # type: ignore
332
+
333
+ def to_json(self) -> dict[str, str | float | int | bool | None]:
334
+ """Convert the MainSettings instance to JSON format.
335
+
336
+ Returns:
337
+ dict[str, str | float | int | bool | None]: JSON representation of the MainSettings.
338
+ """
339
+ return {
340
+ "game": self.game,
341
+ "latitude": self.latitude,
342
+ "longitude": self.longitude,
343
+ "country": self.country,
344
+ "size": self.size,
345
+ "rotation": self.rotation,
346
+ "dtm_provider": self.dtm_provider,
347
+ "custom_osm": self.custom_osm,
348
+ "is_public": self.is_public,
349
+ "api_request": self.api_request,
350
+ "date": self.date,
351
+ "time": self.time,
352
+ "version": self.version,
353
+ "completed": self.completed,
354
+ "error": self.error,
355
+ }
@@ -1,13 +1,10 @@
1
1
  """This module contains the Logger class for logging to the file and stdout."""
2
2
 
3
3
  import logging
4
- import os
5
4
  import sys
6
5
  from typing import Literal
7
6
 
8
7
  LOGGER_NAME = "maps4fs"
9
- log_directory = os.path.join(os.getcwd(), "logs")
10
- os.makedirs(log_directory, exist_ok=True)
11
8
 
12
9
 
13
10
  class Logger(logging.Logger):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: maps4fs
3
- Version: 2.1.4
3
+ Version: 2.1.6
4
4
  Summary: Generate map templates for Farming Simulator from real places.
5
5
  Author-email: iwatkot <iwatkot@gmail.com>
6
6
  License: Apache License 2.0
@@ -9,6 +9,7 @@ maps4fs.egg-info/dependency_links.txt
9
9
  maps4fs.egg-info/requires.txt
10
10
  maps4fs.egg-info/top_level.txt
11
11
  maps4fs/generator/__init__.py
12
+ maps4fs/generator/config.py
12
13
  maps4fs/generator/game.py
13
14
  maps4fs/generator/map.py
14
15
  maps4fs/generator/qgis.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "maps4fs"
7
- version = "2.1.4"
7
+ version = "2.1.6"
8
8
  description = "Generate map templates for Farming Simulator from real places."
9
9
  authors = [{name = "iwatkot", email = "iwatkot@gmail.com"}]
10
10
  license = {text = "Apache License 2.0"}
File without changes
File without changes
File without changes
File without changes
File without changes