maps4fs 1.9.3__py3-none-any.whl → 1.9.5__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.
@@ -224,6 +224,8 @@ class Background(MeshComponent, ImageComponent):
224
224
  save_path = os.path.join(self.background_directory, f"{filename}.obj")
225
225
  self.logger.debug("Generating obj file in path: %s", save_path)
226
226
 
227
+ self.assets.background_mesh = save_path
228
+
227
229
  dem_data = cv2.imread(self.output_path, cv2.IMREAD_UNCHANGED)
228
230
 
229
231
  if self.map.output_size is not None:
@@ -279,6 +281,8 @@ class Background(MeshComponent, ImageComponent):
279
281
  cv2.imwrite(main_dem_path, resized_dem_data)
280
282
  self.logger.debug("DEM cutout saved: %s", main_dem_path)
281
283
 
284
+ self.assets.dem = main_dem_path
285
+
282
286
  return main_dem_path
283
287
 
284
288
  def plane_from_np(
@@ -321,8 +325,11 @@ class Background(MeshComponent, ImageComponent):
321
325
  self.logger.debug("Obj file saved: %s", save_path)
322
326
 
323
327
  if create_preview:
324
- mesh.apply_scale([0.5, 0.5, 0.5])
325
- self.mesh_to_stl(mesh, save_path=self.stl_preview_path)
328
+ try:
329
+ mesh.apply_scale([0.5, 0.5, 0.5])
330
+ self.mesh_to_stl(mesh, save_path=self.stl_preview_path)
331
+ except Exception as e:
332
+ self.logger.error("Could not create STL preview: %s", e)
326
333
 
327
334
  def update_mesh_info(self, save_path: str, mesh: Trimesh) -> None:
328
335
  """Updates the mesh info with the data from the mesh.
@@ -587,4 +594,6 @@ class Background(MeshComponent, ImageComponent):
587
594
  elevated_water = np.where(mask, background_dem, elevated_water)
588
595
  elevated_save_path = os.path.join(self.water_directory, "elevated_water.obj")
589
596
 
597
+ self.assets.water_mesh = elevated_save_path
598
+
590
599
  self.plane_from_np(elevated_water, elevated_save_path, include_zeros=False)
@@ -20,6 +20,16 @@ if TYPE_CHECKING:
20
20
  from maps4fs.generator.map import Map
21
21
 
22
22
 
23
+ class AttrDict(dict):
24
+ """A dictionary that allows attribute-style access to its keys."""
25
+
26
+ def __getattr__(self, name):
27
+ return self[name]
28
+
29
+ def __setattr__(self, name, value):
30
+ self[name] = value
31
+
32
+
23
33
  class Component:
24
34
  """Base class for all map generation components.
25
35
 
@@ -72,6 +82,8 @@ class Component:
72
82
  self.save_bbox()
73
83
  self.preprocess()
74
84
 
85
+ self.assets = AttrDict()
86
+
75
87
  def preprocess(self) -> None:
76
88
  """Prepares the component for processing. Must be implemented in the child class."""
77
89
  return
@@ -263,6 +263,8 @@ class GRLE(ImageComponent, XMLComponent):
263
263
 
264
264
  cv2.imwrite(info_layer_farmlands_path, image)
265
265
 
266
+ self.assets.farmlands = info_layer_farmlands_path
267
+
266
268
  self.preview_paths["farmlands"] = info_layer_farmlands_path
267
269
 
268
270
  def _add_plants(self) -> None:
@@ -362,6 +364,9 @@ class GRLE(ImageComponent, XMLComponent):
362
364
  # Ensure that order of channels is correct because CV2 uses BGR and we need RGB.
363
365
  density_map_fruits = cv2.cvtColor(density_map_fruits, cv2.COLOR_BGR2RGB)
364
366
  cv2.imwrite(density_map_fruit_path, density_map_fruits)
367
+
368
+ self.assets.plants = density_map_fruit_path
369
+
365
370
  self.logger.debug("Updated density map for fruits saved in %s.", density_map_fruit_path)
366
371
 
367
372
  def create_island_of_plants(self, image: np.ndarray, count: int) -> np.ndarray:
@@ -222,6 +222,8 @@ class I3d(XMLComponent):
222
222
  tree.write(splines_i3d_path) # type: ignore
223
223
  self.logger.debug("Splines I3D file saved to: %s.", splines_i3d_path)
224
224
 
225
+ self.assets.splines = splines_i3d_path
226
+
225
227
  def _add_fields(self) -> None:
226
228
  """Adds fields to the map I3D file."""
227
229
  tree = self.get_tree()
@@ -285,6 +287,8 @@ class I3d(XMLComponent):
285
287
 
286
288
  self.save_tree(tree)
287
289
 
290
+ self.assets.fields = self.xml_path
291
+
288
292
  def _get_field_xml_entry(
289
293
  self, field_id: int, field_ccs: list[tuple[int, int]], node_id: int
290
294
  ) -> tuple[ET.Element, int] | tuple[None, int]:
@@ -559,6 +563,8 @@ class I3d(XMLComponent):
559
563
  scene_node.append(trees_node)
560
564
  self.save_tree(tree)
561
565
 
566
+ self.assets.forests = self.xml_path
567
+
562
568
  @staticmethod
563
569
  def randomize_coordinates(
564
570
  coordinates: tuple[int, int], density: int, shift_percent: int
@@ -59,9 +59,13 @@ class Satellite(ImageComponent):
59
59
  overview_size = (self.map_size + margin) * 2
60
60
  overwiew_path = os.path.join(self.satellite_directory, "satellite_overview.png")
61
61
 
62
+ self.assets.overview = overwiew_path
63
+
62
64
  background_size = self.map_size + (Parameters.BACKGROUND_DISTANCE + margin) * 2
63
65
  background_path = os.path.join(self.satellite_directory, "satellite_background.png")
64
66
 
67
+ self.assets.background = background_path
68
+
65
69
  sizes = [overview_size, background_size]
66
70
  self.image_paths = [overwiew_path, background_path]
67
71
 
@@ -149,6 +149,9 @@ class Texture(ImageComponent):
149
149
  self.dissolve()
150
150
  self.copy_procedural()
151
151
 
152
+ for layer in self.layers:
153
+ self.assets[layer.name] = layer.path(self._weights_dir)
154
+
152
155
  def add_borders(self) -> None:
153
156
  """Iterates over all the layers and picks the one which have the border propety defined.
154
157
  Borders are distance from the edge of the map on each side (top, right, bottom, left).
@@ -504,7 +507,7 @@ class Texture(ImageComponent):
504
507
 
505
508
  # Randomly assign non-zero values to sublayers.
506
509
  for coord in non_zero_coords:
507
- sublayers[np.random.randint(0, layer.count)][coord[0], coord[1]] = 255
510
+ sublayers[np.random.randint(0, layer.count)][coord[0], coord[1]] = 255 # type: ignore
508
511
 
509
512
  # Save the sublayers.
510
513
  for sublayer, sublayer_path in zip(sublayers, layer_paths):
maps4fs/generator/game.py CHANGED
@@ -49,6 +49,14 @@ class Game:
49
49
  if map_template_path:
50
50
  self._map_template_path = map_template_path
51
51
 
52
+ def set_components_by_names(self, component_names: list[str]) -> None:
53
+ """Sets the components used for map generation by their names.
54
+
55
+ Arguments:
56
+ component_names (list[str]): List of component names to be used.
57
+ """
58
+ self.components = [comp for comp in self.components if comp.__name__ in component_names]
59
+
52
60
  def map_xml_path(self, map_directory: str) -> str:
53
61
  """Returns the path to the map.xml file.
54
62
 
maps4fs/generator/map.py CHANGED
@@ -58,7 +58,7 @@ class Map:
58
58
  **kwargs,
59
59
  ):
60
60
  if not logger:
61
- logger = Logger(to_stdout=True, to_file=False)
61
+ logger = Logger()
62
62
  self.logger = logger
63
63
  self.size = size
64
64
 
@@ -145,6 +145,13 @@ class BackgroundSettings(SettingsModel):
145
145
  generate_water (bool): generate obj files for the water.
146
146
  resize_factor (int): resize factor for the background terrain and water.
147
147
  It will be used as 1 / resize_factor of the original size.
148
+ water_blurriness (int): blurriness of the water.
149
+ remove_center (bool): remove the center of the background terrain.
150
+ It will be used to remove the center of the map where the player starts.
151
+ apply_decimation (bool): apply decimation to the background terrain.
152
+ decimation_percent (int): percentage of the decimation.
153
+ decimation_agression (int): agression of the decimation.
154
+ It will be used to control the amount of decimation applied to the background terrain.
148
155
  """
149
156
 
150
157
  generate_background: bool = False
maps4fs/logger.py CHANGED
@@ -3,7 +3,6 @@
3
3
  import logging
4
4
  import os
5
5
  import sys
6
- from datetime import datetime
7
6
  from typing import Literal
8
7
 
9
8
  LOGGER_NAME = "maps4fs"
@@ -12,35 +11,18 @@ os.makedirs(log_directory, exist_ok=True)
12
11
 
13
12
 
14
13
  class Logger(logging.Logger):
15
- """Handles logging to the file and stroudt with timestamps."""
14
+ """Handles logging to stdout with timestamps."""
16
15
 
17
16
  def __init__(
18
17
  self,
19
18
  level: Literal["DEBUG", "INFO", "WARNING", "ERROR"] = "INFO",
20
19
  to_stdout: bool = True,
21
- to_file: bool = True,
22
20
  ):
23
21
  super().__init__(LOGGER_NAME)
24
22
  self.setLevel(level)
25
23
  self.stdout_handler = logging.StreamHandler(sys.stdout)
26
- self.file_handler = logging.FileHandler(
27
- filename=self.log_file(), mode="a", encoding="utf-8"
28
- )
29
24
  formatter = "%(name)s | %(levelname)s | %(asctime)s | %(message)s"
30
25
  self.fmt = formatter
31
26
  self.stdout_handler.setFormatter(logging.Formatter(formatter))
32
- self.file_handler.setFormatter(logging.Formatter(formatter))
33
27
  if to_stdout:
34
28
  self.addHandler(self.stdout_handler)
35
- if to_file:
36
- self.addHandler(self.file_handler)
37
-
38
- def log_file(self) -> str:
39
- """Returns the path to the log file.
40
-
41
- Returns:
42
- str: The path to the log file.
43
- """
44
- today = datetime.now().strftime("%Y-%m-%d")
45
- log_file = os.path.join(log_directory, f"{today}.txt")
46
- return log_file
@@ -1,22 +1,21 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: maps4fs
3
- Version: 1.9.3
3
+ Version: 1.9.5
4
4
  Summary: Generate map templates for Farming Simulator from real places.
5
5
  Author-email: iwatkot <iwatkot@gmail.com>
6
- License: MIT License
6
+ License: Apache License 2.0
7
7
  Project-URL: Homepage, https://github.com/iwatkot/maps4fs
8
8
  Project-URL: Repository, https://github.com/iwatkot/maps4fs
9
9
  Keywords: farmingsimulator,fs,farmingsimulator22,farmingsimulator25,fs22,fs25
10
10
  Classifier: Programming Language :: Python :: 3.11
11
11
  Classifier: Programming Language :: Python :: 3.12
12
- Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: License :: OSI Approved :: Apache Software License
13
13
  Classifier: Operating System :: OS Independent
14
14
  Description-Content-Type: text/markdown
15
15
  License-File: LICENSE.md
16
16
  Requires-Dist: opencv-python
17
17
  Requires-Dist: osmnx>=2.0.0
18
18
  Requires-Dist: rasterio
19
- Requires-Dist: folium
20
19
  Requires-Dist: geopy
21
20
  Requires-Dist: trimesh
22
21
  Requires-Dist: imageio
@@ -29,7 +28,7 @@ Requires-Dist: tqdm
29
28
  Requires-Dist: scipy
30
29
  Dynamic: license-file
31
30
 
32
- ⚠️ Learn more about the upcoming 2.0.0 changes in the [migration guide](docs/migration.md).
31
+ ⚠️ Learn more about the upcoming 2.0 changes in the [migration guide](docs/migration.md).
33
32
  ⚠️ Some components are deprecated and there are major changes in the project structure.
34
33
 
35
34
 
@@ -52,7 +51,7 @@ Dynamic: license-file
52
51
  <a href="docs/step_by_step.md">Create a map in 10 steps</a> •
53
52
  <a href="#How-To-Run">How-To-Run</a><br>
54
53
  <a href="docs/FAQ.md">FAQ</a> •
55
- <a href="docs/map_structure.md">Map Structure</a>
54
+ <a href="docs/map_structure.md">Map Structure</a>
56
55
  <a href="#Schemas-Editor">Schemas Editor</a> •
57
56
  <a href="#Main-Settings">Main Settings</a><br>
58
57
  <a href="#Supported-objects">Supported objects</a> •
@@ -85,7 +84,8 @@ Dynamic: license-file
85
84
  </div>
86
85
 
87
86
  🗺️ Supports 2x2, 4x4, 8x8, 16x16 and any custom size maps<br>
88
- ✂️ Supports map scaling 🆕<br>
87
+ 🔗 Generate maps using an [API](https://github.com/iwatkot/maps4fsapi) 🆕<br>
88
+ ✂️ Supports map scaling<br>
89
89
  🔄 Support map rotation<br>
90
90
  🌐 Supports custom [DTM Providers](#DTM-Providers)<br>
91
91
  🌾 Automatically generates fields<br>
@@ -156,7 +156,7 @@ There are several ways to use the tool. You obviously need the **first one**, bu
156
156
  **Option 2:** Run the Docker version in your browser. Launch the following command in your terminal:
157
157
 
158
158
  ```bash
159
- docker run -d -p 8501:8501 --name maps4fs iwatkot/maps4fs
159
+ docker run -d -p 8501:8501 -p 8000:8000 --name maps4fs iwatkot/maps4fs
160
160
  ```
161
161
 
162
162
  And open [http://localhost:8501](http://localhost:8501) in your browser.<br>
@@ -238,21 +238,13 @@ You can launch the project with minimalistic UI in your browser using Docker. Fo
238
238
  2. Run the following command in your terminal:
239
239
 
240
240
  ```bash
241
- docker run -d -p 8501:8501 --name maps4fs iwatkot/maps4fs
241
+ docker run -d -p 8501:8501 -p 8000:8000 --name maps4fs iwatkot/maps4fs
242
242
  ```
243
243
 
244
244
  3. Open your browser and go to [http://localhost:8501](http://localhost:8501).
245
245
  4. Fill in the required fields and click on the `Generate` button.
246
246
  5. When the map is generated click on the `Download` button to get the map.
247
247
 
248
- ➡️ If you don't need Blender backend, you can use the **lite** version of the Docker image.
249
-
250
- ```bash
251
- docker run -d -p 8501:8501 --name maps4fs iwatkot/maps4fs:*.*.*_lite
252
- ```
253
-
254
- Remember to replace `*.*.*` with the version you want to use, e.g. `iwatkot/maps4fs:1.8.202_lite`. Also, pay attention that some features will not work in the lite version, such as removing the center part of the background mesh.
255
-
256
248
  ### Option 3: Python package or source code
257
249
 
258
250
  🔴 Recommended for developers.
@@ -1,22 +1,22 @@
1
1
  maps4fs/__init__.py,sha256=EGvLVoRpSt4jITswsGbe1ISVmGAZAMQJcBmTwtyuVxI,335
2
- maps4fs/logger.py,sha256=HQrDyj72mUjVYo25aR_-_SxVn2rfFjDCNbj-JKJdSnE,1488
2
+ maps4fs/logger.py,sha256=WDfR14hxqy8b6xtwL6YIu2LGzFO1sbt0LxMgfsDTOkA,865
3
3
  maps4fs/generator/__init__.py,sha256=zZMLEkGzb4z0xql650gOtGSvcgX58DnJ2yN3vC2daRk,43
4
- maps4fs/generator/game.py,sha256=NZaxj5z7WzMiHzAvQyr-TvVjGoHgqGldM6ZsItuYyzA,11292
5
- maps4fs/generator/map.py,sha256=CaRfesYIZa801cKbTEwb-WdUYZcFQ_9dn_j1ij6VmhE,13928
4
+ maps4fs/generator/game.py,sha256=g8lMHuuRRmJLSDsQTAMv8p_-qntYMiZKnAqn7ru96i0,11645
5
+ maps4fs/generator/map.py,sha256=7-nx27M-VwTocPor8BCkkpthYBJYe_B-pe5_Dcsijaw,13899
6
6
  maps4fs/generator/qgis.py,sha256=Es8hLuqN_KH8lDfnJE6He2rWYbAKJ3RGPn-o87S6CPI,6116
7
- maps4fs/generator/settings.py,sha256=E5sfkfJgcjh9QHpa7ILlV_jnrF6USpsOFxkooVnxrbs,6968
7
+ maps4fs/generator/settings.py,sha256=E_M47X2QhbBqgiNraBDQd62lAQlNWHYLq30HQ59_4k4,7490
8
8
  maps4fs/generator/statistics.py,sha256=aynS3zbAtiwnU_YLKHPTiiaKW98_suvQUhy1SGBA6mc,2448
9
9
  maps4fs/generator/component/__init__.py,sha256=s01yVVVi8R2xxNvflu2D6wTd9I_g73AMM2x7vAC7GX4,490
10
- maps4fs/generator/component/background.py,sha256=kt_RaLrj6hZeZaaFNT36jJyp9IofiN9CFsO8EYY7F7A,23756
10
+ maps4fs/generator/component/background.py,sha256=AfbgNUai3tvpMe8lYmfiaM1n51ssunECy4gRglihumU,24032
11
11
  maps4fs/generator/component/config.py,sha256=IP530sapLofFskSnBEB96G0aUSd6Sno0G9ET3ca0ZOQ,3696
12
12
  maps4fs/generator/component/dem.py,sha256=Bvm3ViA6PpR7RXRAHBj5rgYB9PWy55Qj6PhTMv6dJRI,11953
13
- maps4fs/generator/component/grle.py,sha256=zzQsTrSoGE8hIESRIMJMHggVJzohUzCgaFlCBl7k58Q,19500
14
- maps4fs/generator/component/i3d.py,sha256=OvcvRnsLjqo059R3nClJ4PNGaCyh7-1aFyGFmC8Jivo,23777
13
+ maps4fs/generator/component/grle.py,sha256=ao3-4B4QUVl0XgOmYAj9L48KGaZtCZRTB1BqYgdAJIc,19613
14
+ maps4fs/generator/component/i3d.py,sha256=xTiZYQRahYBsVALxmnsBygzTHJLE54ucXvHK8LWCYAs,23914
15
15
  maps4fs/generator/component/layer.py,sha256=-br4gAGcGeBNb3ldch9XFEK0lhXqb1IbArhFB5Owu54,6186
16
- maps4fs/generator/component/satellite.py,sha256=oZBHjP_QY0ik1-Vk7JqMS__zIG8ffw2voeozB7-HUQc,4946
17
- maps4fs/generator/component/texture.py,sha256=tCMuuNcRwgdPkPVmNA7BCbnsTBnwkUXOrdlJJUoYAMU,33273
16
+ maps4fs/generator/component/satellite.py,sha256=47V9aPbv7k-2C-PsoXd33EO92yavFzB6_MRCd7fLP6k,5042
17
+ maps4fs/generator/component/texture.py,sha256=Nc_oOHX3b4vJm8FnNOn3W4EQGFkW0zW0rGzO_0nTJMM,33392
18
18
  maps4fs/generator/component/base/__init__.py,sha256=zZMLEkGzb4z0xql650gOtGSvcgX58DnJ2yN3vC2daRk,43
19
- maps4fs/generator/component/base/component.py,sha256=13ZjIatyxmxfcpE89cqlpQ6IBppJTeUisIEVa344eqM,21833
19
+ maps4fs/generator/component/base/component.py,sha256=HeNDaToKrS6OLeJepKZA7iQzZQDYy-9QRtv1A73Ire0,22090
20
20
  maps4fs/generator/component/base/component_image.py,sha256=2NYJgCU8deHl7O2FYFYk38WKZVJygFoc2gjBXwH6vjM,5970
21
21
  maps4fs/generator/component/base/component_mesh.py,sha256=BsxS5NlUK2hLhksgCwSoMK-00GNAwK2fYGpgb3R4n1w,9396
22
22
  maps4fs/generator/component/base/component_xml.py,sha256=V9pGUvHh6UF6BP0qFARqDq9vquoAgq1zJqhOgBoeS_Y,3983
@@ -54,8 +54,8 @@ maps4fs/toolbox/__init__.py,sha256=zZMLEkGzb4z0xql650gOtGSvcgX58DnJ2yN3vC2daRk,4
54
54
  maps4fs/toolbox/background.py,sha256=RB0pHuRyfnDuqYtO2gUypfPm4G5cYRFdT2W-DG49zy0,2427
55
55
  maps4fs/toolbox/custom_osm.py,sha256=fjVSl9Ztc8_q0DxgNkhM7tE0Y-XpX8xcGGW-Tunp0R8,2518
56
56
  maps4fs/toolbox/dem.py,sha256=mbn3ZqMRhhYmzgssm2CGvg6aa89MUBOJPq6QyE54OLY,4191
57
- maps4fs-1.9.3.dist-info/licenses/LICENSE.md,sha256=pTKD_oUexcn-yccFCTrMeLkZy0ifLRa-VNcDLqLZaIw,10749
58
- maps4fs-1.9.3.dist-info/METADATA,sha256=a4q4ZRFsuBGoiARtZHJurqD06CO4GHmPgFEdIoMKME0,49704
59
- maps4fs-1.9.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
60
- maps4fs-1.9.3.dist-info/top_level.txt,sha256=Ue9DSRlejRQRCaJueB0uLcKrWwsEq9zezfv5dI5mV1M,8
61
- maps4fs-1.9.3.dist-info/RECORD,,
57
+ maps4fs-1.9.5.dist-info/licenses/LICENSE.md,sha256=pTKD_oUexcn-yccFCTrMeLkZy0ifLRa-VNcDLqLZaIw,10749
58
+ maps4fs-1.9.5.dist-info/METADATA,sha256=XD5fgbYz1HyY35sFP_JpZB16jBHRwwO0h8bo8bDgsCM,49396
59
+ maps4fs-1.9.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
60
+ maps4fs-1.9.5.dist-info/top_level.txt,sha256=Ue9DSRlejRQRCaJueB0uLcKrWwsEq9zezfv5dI5mV1M,8
61
+ maps4fs-1.9.5.dist-info/RECORD,,