maps4fs 1.8.1__py3-none-any.whl → 1.8.12__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -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.