maps4fs 1.8.11__tar.gz → 1.8.12__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. {maps4fs-1.8.11 → maps4fs-1.8.12}/PKG-INFO +3 -1
  2. {maps4fs-1.8.11 → maps4fs-1.8.12}/README.md +2 -0
  3. {maps4fs-1.8.11 → maps4fs-1.8.12}/maps4fs/generator/background.py +7 -11
  4. maps4fs-1.8.12/maps4fs/generator/component/base/__init__.py +1 -0
  5. {maps4fs-1.8.11/maps4fs/generator → maps4fs-1.8.12/maps4fs/generator/component/base}/component.py +39 -23
  6. maps4fs-1.8.12/maps4fs/generator/component/base/component_xml.py +95 -0
  7. {maps4fs-1.8.11/maps4fs/generator → maps4fs-1.8.12/maps4fs/generator/component}/config.py +15 -30
  8. maps4fs-1.8.12/maps4fs/generator/component/i3d.py +545 -0
  9. {maps4fs-1.8.11 → maps4fs-1.8.12}/maps4fs/generator/dem.py +1 -10
  10. {maps4fs-1.8.11 → maps4fs-1.8.12}/maps4fs/generator/game.py +33 -2
  11. {maps4fs-1.8.11 → maps4fs-1.8.12}/maps4fs/generator/grle.py +10 -16
  12. {maps4fs-1.8.11 → maps4fs-1.8.12}/maps4fs/generator/map.py +41 -1
  13. {maps4fs-1.8.11 → maps4fs-1.8.12}/maps4fs/generator/satellite.py +1 -2
  14. {maps4fs-1.8.11 → maps4fs-1.8.12}/maps4fs/generator/settings.py +11 -0
  15. {maps4fs-1.8.11 → maps4fs-1.8.12}/maps4fs/generator/texture.py +5 -7
  16. maps4fs-1.8.12/maps4fs/toolbox/__init__.py +1 -0
  17. {maps4fs-1.8.11 → maps4fs-1.8.12}/maps4fs/toolbox/background.py +1 -3
  18. {maps4fs-1.8.11 → maps4fs-1.8.12}/maps4fs.egg-info/PKG-INFO +3 -1
  19. {maps4fs-1.8.11 → maps4fs-1.8.12}/maps4fs.egg-info/SOURCES.txt +6 -3
  20. {maps4fs-1.8.11 → maps4fs-1.8.12}/pyproject.toml +1 -1
  21. maps4fs-1.8.11/maps4fs/generator/i3d.py +0 -624
  22. {maps4fs-1.8.11 → maps4fs-1.8.12}/LICENSE.md +0 -0
  23. {maps4fs-1.8.11 → maps4fs-1.8.12}/maps4fs/__init__.py +0 -0
  24. {maps4fs-1.8.11 → maps4fs-1.8.12}/maps4fs/generator/__init__.py +0 -0
  25. {maps4fs-1.8.11/maps4fs/toolbox → maps4fs-1.8.12/maps4fs/generator/component}/__init__.py +0 -0
  26. {maps4fs-1.8.11 → maps4fs-1.8.12}/maps4fs/generator/dtm/__init__.py +0 -0
  27. {maps4fs-1.8.11 → maps4fs-1.8.12}/maps4fs/generator/dtm/base/wcs.py +0 -0
  28. {maps4fs-1.8.11 → maps4fs-1.8.12}/maps4fs/generator/dtm/base/wms.py +0 -0
  29. {maps4fs-1.8.11 → maps4fs-1.8.12}/maps4fs/generator/dtm/bavaria.py +0 -0
  30. {maps4fs-1.8.11 → maps4fs-1.8.12}/maps4fs/generator/dtm/dtm.py +0 -0
  31. {maps4fs-1.8.11 → maps4fs-1.8.12}/maps4fs/generator/dtm/england.py +0 -0
  32. {maps4fs-1.8.11 → maps4fs-1.8.12}/maps4fs/generator/dtm/hessen.py +0 -0
  33. {maps4fs-1.8.11 → maps4fs-1.8.12}/maps4fs/generator/dtm/niedersachsen.py +0 -0
  34. {maps4fs-1.8.11 → maps4fs-1.8.12}/maps4fs/generator/dtm/nrw.py +0 -0
  35. {maps4fs-1.8.11 → maps4fs-1.8.12}/maps4fs/generator/dtm/srtm.py +0 -0
  36. {maps4fs-1.8.11 → maps4fs-1.8.12}/maps4fs/generator/dtm/usgs.py +0 -0
  37. {maps4fs-1.8.11 → maps4fs-1.8.12}/maps4fs/generator/dtm/utils.py +0 -0
  38. {maps4fs-1.8.11 → maps4fs-1.8.12}/maps4fs/generator/qgis.py +0 -0
  39. {maps4fs-1.8.11 → maps4fs-1.8.12}/maps4fs/logger.py +0 -0
  40. {maps4fs-1.8.11 → maps4fs-1.8.12}/maps4fs/toolbox/custom_osm.py +0 -0
  41. {maps4fs-1.8.11 → maps4fs-1.8.12}/maps4fs/toolbox/dem.py +0 -0
  42. {maps4fs-1.8.11 → maps4fs-1.8.12}/maps4fs.egg-info/dependency_links.txt +0 -0
  43. {maps4fs-1.8.11 → maps4fs-1.8.12}/maps4fs.egg-info/requires.txt +0 -0
  44. {maps4fs-1.8.11 → maps4fs-1.8.12}/maps4fs.egg-info/top_level.txt +0 -0
  45. {maps4fs-1.8.11 → maps4fs-1.8.12}/setup.cfg +0 -0
  46. {maps4fs-1.8.11 → maps4fs-1.8.12}/tests/test_generator.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: maps4fs
3
- Version: 1.8.11
3
+ Version: 1.8.12
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
@@ -611,6 +611,8 @@ You can also apply some advanced settings to the map generation process.<br>
611
611
 
612
612
  - Forest density - the density of the forest in meters. The lower the value, the lower the distance between the trees, which makes the forest denser. Note, that low values will lead to enormous number of trees, which may cause the Giants Editor to crash or lead to performance issues. By default, it's set to 10.
613
613
 
614
+ - Trees relative shift - represents the maximum possible shift of the tree from it's original position in percents of the forest density value. The higher the value, the more the trees will be shifted from their original positions. Warning: higher values can lead to overlapping trees.
615
+
614
616
  ### Texture Advanced settings
615
617
 
616
618
  - Dissolve - if enabled, the values from one layer will be splitted between different layers of texture, making it look more natural. Warning: it's a time-consuming process, recommended to enable it, when you generating the final version of the map, not some test versions.
@@ -585,6 +585,8 @@ You can also apply some advanced settings to the map generation process.<br>
585
585
 
586
586
  - Forest density - the density of the forest in meters. The lower the value, the lower the distance between the trees, which makes the forest denser. Note, that low values will lead to enormous number of trees, which may cause the Giants Editor to crash or lead to performance issues. By default, it's set to 10.
587
587
 
588
+ - Trees relative shift - represents the maximum possible shift of the tree from it's original position in percents of the forest density value. The higher the value, the more the trees will be shifted from their original positions. Warning: higher values can lead to overlapping trees.
589
+
588
590
  ### Texture Advanced settings
589
591
 
590
592
  - Dissolve - if enabled, the values from one layer will be splitted between different layers of texture, making it look more natural. Warning: it's a time-consuming process, recommended to enable it, when you generating the final version of the map, not some test versions.
@@ -13,7 +13,7 @@ import numpy as np
13
13
  import trimesh # type: ignore
14
14
  from tqdm import tqdm
15
15
 
16
- from maps4fs.generator.component import Component
16
+ from maps4fs.generator.component.base.component import Component
17
17
  from maps4fs.generator.dem import DEM
18
18
  from maps4fs.generator.texture import Texture
19
19
 
@@ -89,7 +89,7 @@ class Background(Component):
89
89
  Raises:
90
90
  ValueError: If the custom background image does not meet the requirements.
91
91
  """
92
- image = cv2.imread(image_path, cv2.IMREAD_UNCHANGED) # pylint: disable=no-member
92
+ image = cv2.imread(image_path, cv2.IMREAD_UNCHANGED)
93
93
  if image.shape[0] != image.shape[1]:
94
94
  raise ValueError("The custom background image must be a square.")
95
95
 
@@ -207,7 +207,7 @@ class Background(Component):
207
207
  save_path = os.path.join(self.background_directory, f"{filename}.obj")
208
208
  self.logger.debug("Generating obj file in path: %s", save_path)
209
209
 
210
- dem_data = cv2.imread(self.dem.dem_path, cv2.IMREAD_UNCHANGED) # pylint: disable=no-member
210
+ dem_data = cv2.imread(self.dem.dem_path, cv2.IMREAD_UNCHANGED)
211
211
  self.plane_from_np(
212
212
  dem_data,
213
213
  save_path,
@@ -227,7 +227,7 @@ class Background(Component):
227
227
  Returns:
228
228
  str -- The path to the cutout DEM file.
229
229
  """
230
- dem_data = cv2.imread(dem_path, cv2.IMREAD_UNCHANGED) # pylint: disable=no-member
230
+ dem_data = cv2.imread(dem_path, cv2.IMREAD_UNCHANGED)
231
231
 
232
232
  center = (dem_data.shape[0] // 2, dem_data.shape[1] // 2)
233
233
  half_size = self.map_size // 2
@@ -238,7 +238,7 @@ class Background(Component):
238
238
  dem_data = dem_data[x1:x2, y1:y2]
239
239
 
240
240
  if save_path:
241
- cv2.imwrite(save_path, dem_data) # pylint: disable=no-member
241
+ cv2.imwrite(save_path, dem_data)
242
242
  self.logger.debug("Not resized DEM saved: %s", save_path)
243
243
  return save_path
244
244
 
@@ -251,12 +251,11 @@ class Background(Component):
251
251
  except FileNotFoundError:
252
252
  pass
253
253
 
254
- # pylint: disable=no-member
255
254
  resized_dem_data = cv2.resize(
256
255
  dem_data, (output_size, output_size), interpolation=cv2.INTER_LINEAR
257
256
  )
258
257
 
259
- cv2.imwrite(main_dem_path, resized_dem_data) # pylint: disable=no-member
258
+ cv2.imwrite(main_dem_path, resized_dem_data)
260
259
  self.logger.debug("DEM cutout saved: %s", main_dem_path)
261
260
 
262
261
  return main_dem_path
@@ -301,9 +300,7 @@ class Background(Component):
301
300
  playable area is will be cut out.
302
301
  """
303
302
  resize_factor = 1 / self.map.background_settings.resize_factor
304
- dem_data = cv2.resize( # pylint: disable=no-member
305
- dem_data, (0, 0), fx=resize_factor, fy=resize_factor
306
- )
303
+ dem_data = cv2.resize(dem_data, (0, 0), fx=resize_factor, fy=resize_factor)
307
304
  if remove_center:
308
305
  dem_data = self.remove_center(dem_data, resize_factor)
309
306
  self.logger.debug("Center removed from DEM data.")
@@ -410,7 +407,6 @@ class Background(Component):
410
407
 
411
408
  self.stl_preview_path = preview_path # pylint: disable=attribute-defined-outside-init
412
409
 
413
- # pylint: disable=no-member
414
410
  def previews(self) -> list[str]:
415
411
  """Returns the path to the image previews paths and the path to the STL preview file.
416
412
 
@@ -0,0 +1 @@
1
+ # pylint: disable=missing-module-docstring
@@ -7,11 +7,11 @@ import os
7
7
  from copy import deepcopy
8
8
  from typing import TYPE_CHECKING, Any
9
9
 
10
- import cv2 # type: ignore
11
- import osmnx as ox # type: ignore
10
+ import cv2
11
+ import osmnx as ox
12
12
  from pyproj import Transformer
13
- from shapely.affinity import rotate, translate # type: ignore
14
- from shapely.geometry import LineString, Polygon, box # type: ignore
13
+ from shapely.affinity import rotate, translate
14
+ from shapely.geometry import LineString, Polygon, box
15
15
 
16
16
  from maps4fs.generator.qgis import save_scripts
17
17
 
@@ -20,7 +20,6 @@ if TYPE_CHECKING:
20
20
  from maps4fs.generator.map import Map
21
21
 
22
22
 
23
- # pylint: disable=R0801, R0903, R0902, R0904, R0913, R0917
24
23
  class Component:
25
24
  """Base class for all map generation components.
26
25
 
@@ -39,7 +38,7 @@ class Component:
39
38
  def __init__(
40
39
  self,
41
40
  game: Game,
42
- map: Map, # pylint: disable=W0622
41
+ map: Map,
43
42
  coordinates: tuple[float, float],
44
43
  map_size: int,
45
44
  map_rotated_size: int,
@@ -59,7 +58,7 @@ class Component:
59
58
  self.kwargs = kwargs
60
59
 
61
60
  self.logger.debug(
62
- "Component %s initialized. Map size: %s, map rotated size: %s", # type: ignore
61
+ "Component %s initialized. Map size: %s, map rotated size: %s",
63
62
  self.__class__.__name__,
64
63
  self.map_size,
65
64
  self.map_rotated_size,
@@ -90,12 +89,13 @@ class Component:
90
89
  raise NotImplementedError
91
90
 
92
91
  def previews(self) -> list[str]:
93
- """Returns a list of paths to the preview images. Must be implemented in the child class.
92
+ """Returns a list of paths to the preview images. If the component does not generate any
93
+ previews, the method may not be re-implemented in the child class.
94
94
 
95
- Raises:
96
- NotImplementedError: If the method is not implemented in the child class.
95
+ Returns:
96
+ list[str]: A list of paths to the preview images.
97
97
  """
98
- raise NotImplementedError
98
+ return []
99
99
 
100
100
  @property
101
101
  def previews_directory(self) -> str:
@@ -205,7 +205,7 @@ class Component:
205
205
  coordinates = coordinates or self.coordinates
206
206
  distance = distance or int(self.map_rotated_size / 2)
207
207
 
208
- west, south, east, north = ox.utils_geo.bbox_from_point( # type: ignore
208
+ west, south, east, north = ox.utils_geo.bbox_from_point(
209
209
  coordinates,
210
210
  dist=distance,
211
211
  )
@@ -260,11 +260,11 @@ class Component:
260
260
  epsg3857_south, epsg3857_east = transformer.transform(south, east)
261
261
 
262
262
  if add_margin:
263
- MARGIN = 500 # pylint: disable=C0103
264
- epsg3857_north = int(epsg3857_north - MARGIN)
265
- epsg3857_south = int(epsg3857_south + MARGIN)
266
- epsg3857_east = int(epsg3857_east - MARGIN)
267
- epsg3857_west = int(epsg3857_west + MARGIN)
263
+ margin = 500
264
+ epsg3857_north = int(epsg3857_north - margin)
265
+ epsg3857_south = int(epsg3857_south + margin)
266
+ epsg3857_east = int(epsg3857_east - margin)
267
+ epsg3857_west = int(epsg3857_west + margin)
268
268
 
269
269
  return epsg3857_north, epsg3857_south, epsg3857_east, epsg3857_west
270
270
 
@@ -345,7 +345,6 @@ class Component:
345
345
 
346
346
  return cs_x, cs_y
347
347
 
348
- # pylint: disable=R0914
349
348
  def fit_object_into_bounds(
350
349
  self,
351
350
  polygon_points: list[tuple[int, int]] | None = None,
@@ -404,9 +403,7 @@ class Component:
404
403
  fitted_osm_object = osm_object.intersection(bounds)
405
404
  self.logger.debug("Fitted the osm_object into the bounds: %s", bounds)
406
405
  except Exception as e:
407
- raise ValueError( # pylint: disable=W0707
408
- f"Could not fit the osm_object into the bounds: {e}"
409
- )
406
+ raise ValueError(f"Could not fit the osm_object into the bounds: {e}")
410
407
 
411
408
  if not isinstance(fitted_osm_object, object_type):
412
409
  raise ValueError("The fitted osm_object is not valid (probably splitted into parts).")
@@ -438,7 +435,27 @@ class Component:
438
435
  return None
439
436
  return info_layer_path
440
437
 
441
- # pylint: disable=R0913, R0917, R0914
438
+ def get_infolayer_data(self, layer_name: str, layer_key: str) -> Any | None:
439
+ """Reads the JSON file of the requested info layer and returns the value of the requested
440
+ key. If the layer or the key does not exist, None is returned.
441
+
442
+ Arguments:
443
+ layer_name (str): The name of the layer.
444
+ layer_key (str): The key to get the value of.
445
+
446
+ Returns:
447
+ Any | None: The value of the requested key or None if the layer or the key does not
448
+ exist.
449
+ """
450
+ infolayer_path = self.get_infolayer_path(layer_name)
451
+ if not infolayer_path:
452
+ return None
453
+
454
+ with open(infolayer_path, "r", encoding="utf-8") as file:
455
+ data = json.load(file)
456
+
457
+ return data.get(layer_key)
458
+
442
459
  def rotate_image(
443
460
  self,
444
461
  image_path: str,
@@ -460,7 +477,6 @@ class Component:
460
477
  self.logger.warning("Image %s does not exist", image_path)
461
478
  return
462
479
 
463
- # pylint: disable=no-member
464
480
  image = cv2.imread(image_path, cv2.IMREAD_UNCHANGED)
465
481
  if image is None:
466
482
  self.logger.warning("Image %s could not be read", image_path)
@@ -0,0 +1,95 @@
1
+ """Base class for all components that primarily used to work with XML files."""
2
+
3
+ import os
4
+ from xml.etree import ElementTree as ET
5
+
6
+ from maps4fs.generator.component.base.component import Component
7
+
8
+
9
+ class XMLComponent(Component):
10
+ """Base class for all components that primarily used to work with XML files."""
11
+
12
+ xml_path: str | None = None
13
+
14
+ def get_tree(self, xml_path: str | None = None) -> ET.ElementTree:
15
+ """Parses the XML file and returns the tree.
16
+ If the XML path is not provided, the class attribute is used.
17
+
18
+ Arguments:
19
+ xml_path (str, optional): The path to the XML file. Defaults to None.
20
+
21
+ Raises:
22
+ ValueError: If the XML path is not set as a class attribute nor provided as an
23
+ argument.
24
+ FileNotFoundError: If the XML file does not exist
25
+
26
+ Returns:
27
+ ET.ElementTree: The parsed XML tree.
28
+ """
29
+ xml_path = xml_path or self.xml_path
30
+ if not xml_path:
31
+ raise ValueError(
32
+ "XML path was not set as a class attribute nor provided as an argument."
33
+ )
34
+
35
+ if not os.path.isfile(xml_path):
36
+ raise FileNotFoundError(f"XML file {xml_path} does not exist.")
37
+
38
+ return ET.parse(xml_path)
39
+
40
+ def save_tree(self, tree: ET.ElementTree, xml_path: str | None = None) -> None:
41
+ """Saves the XML tree to the file.
42
+ If the XML path is not provided, the class attribute is used.
43
+
44
+ Arguments:
45
+ tree (ET.ElementTree): The XML tree to save.
46
+ xml_path (str, optional): The path to the XML file. Defaults to None.
47
+
48
+ Raises:
49
+ ValueError: If the XML path is not set as a class attribute nor provided as an
50
+ argument.
51
+ """
52
+ xml_path = xml_path or self.xml_path
53
+ if not xml_path:
54
+ raise ValueError(
55
+ "XML path was not set as a class attribute nor provided as an argument."
56
+ )
57
+
58
+ tree.write(xml_path, encoding="utf-8", xml_declaration=True)
59
+
60
+ def get_and_update_element(self, root: ET.Element, path: str, data: dict[str, str]) -> None:
61
+ """Finds the element by the path and updates it with the provided data.
62
+
63
+ Arguments:
64
+ root (ET.Element): The root element of the XML tree.
65
+ path (str): The path to the element.
66
+ data (dict[str, str]): The data to update the element with.
67
+ """
68
+ element = root.find(path)
69
+ if element is not None:
70
+ self.update_element(element, data)
71
+
72
+ def update_element(self, element: ET.Element, data: dict[str, str]) -> None:
73
+ """Updates the element with the provided data.
74
+
75
+ Arguments:
76
+ element (ET.Element): The element to update.
77
+ data (dict[str, str]): The data to update the element with.
78
+ """
79
+ for key, value in data.items():
80
+ element.set(key, value)
81
+
82
+ def create_element(self, element_name: str, data: dict[str, str]) -> ET.Element:
83
+ """Creates an element with the provided data.
84
+
85
+ Arguments:
86
+ element_name (str): The name of the element.
87
+ data (dict[str, str]): The data to set the element attributes to.
88
+
89
+ Returns:
90
+ ET.Element: The created element.
91
+ """
92
+ element = ET.Element(element_name)
93
+ for key, value in data.items():
94
+ element.set(key, value)
95
+ return element
@@ -2,14 +2,11 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- import os
6
- from xml.etree import ElementTree as ET
7
-
8
- from maps4fs.generator.component import Component
5
+ from maps4fs.generator.component.base.component_xml import XMLComponent
9
6
 
10
7
 
11
8
  # pylint: disable=R0903
12
- class Config(Component):
9
+ class Config(XMLComponent):
13
10
  """Component for map settings and configuration.
14
11
 
15
12
  Arguments:
@@ -25,8 +22,7 @@ class Config(Component):
25
22
 
26
23
  def preprocess(self) -> None:
27
24
  """Gets the path to the map XML file and saves it to the instance variable."""
28
- self._map_xml_path = self.game.map_xml_path(self.map_directory)
29
- self.logger.debug("Map XML path: %s.", self._map_xml_path)
25
+ self.xml_path = self.game.map_xml_path(self.map_directory)
30
26
 
31
27
  def process(self) -> None:
32
28
  """Sets the map size in the map.xml file."""
@@ -34,31 +30,20 @@ class Config(Component):
34
30
 
35
31
  def _set_map_size(self) -> None:
36
32
  """Edits map.xml file to set correct map size."""
37
- if not os.path.isfile(self._map_xml_path):
38
- self.logger.warning("Map XML file not found: %s.", self._map_xml_path)
39
- return
40
- tree = ET.parse(self._map_xml_path)
41
- self.logger.debug("Map XML file loaded from: %s.", self._map_xml_path)
33
+ tree = self.get_tree()
34
+ if not tree:
35
+ raise FileNotFoundError(f"Map XML file not found: {self.xml_path}")
36
+
42
37
  root = tree.getroot()
43
- for map_elem in root.iter("map"):
44
- map_elem.set("width", str(self.map_size))
45
- map_elem.set("height", str(self.map_size))
46
- self.logger.debug(
47
- "Map size set to %sx%s in Map XML file.",
48
- self.map_size,
49
- self.map_size,
50
- )
51
- tree.write(self._map_xml_path)
52
- self.logger.debug("Map XML file saved to: %s.", self._map_xml_path)
53
-
54
- def previews(self) -> list[str]:
55
- """Returns a list of paths to the preview images (empty list).
56
- The component does not generate any preview images so it returns an empty list.
38
+ data = {
39
+ "width": str(self.map_size),
40
+ "height": str(self.map_size),
41
+ }
57
42
 
58
- Returns:
59
- list[str]: An empty list.
60
- """
61
- return []
43
+ for element in root.iter("map"):
44
+ self.update_element(element, data)
45
+ break
46
+ self.save_tree(tree)
62
47
 
63
48
  def info_sequence(self) -> dict[str, dict[str, str | float | int]]:
64
49
  """Returns information about the component.