maps4fs 0.7.3__py3-none-any.whl → 0.7.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.
maps4fs/generator/dem.py CHANGED
@@ -41,7 +41,7 @@ class DEM(Component):
41
41
  self.multiplier = self.kwargs.get("multiplier", DEFAULT_MULTIPLIER)
42
42
  self.blur_radius = self.kwargs.get("blur_radius", DEFAULT_BLUR_RADIUS)
43
43
  self.logger.debug(
44
- "DEM multiplier is %s, blur radius is %s.", self.multiplier, self.blur_radius
44
+ "DEM value multiplier is %s, blur radius is %s.", self.multiplier, self.blur_radius
45
45
  )
46
46
 
47
47
  # pylint: disable=no-member
@@ -50,10 +50,10 @@ class DEM(Component):
50
50
  saves to map directory."""
51
51
  north, south, east, west = self.bbox
52
52
 
53
- dem_height = self.map_height * self.game.dem_multipliyer + 1
54
- dem_width = self.map_width * self.game.dem_multipliyer + 1
53
+ dem_height = int((self.map_height / 2) * self.game.dem_multipliyer + 1)
54
+ dem_width = int((self.map_width / 2) * self.game.dem_multipliyer + 1)
55
55
  self.logger.debug(
56
- "DEM multiplier is %s, DEM height is %s, DEM width is %s.",
56
+ "DEM size multiplier is %s, DEM height is %s, DEM width is %s.",
57
57
  self.game.dem_multipliyer,
58
58
  dem_height,
59
59
  dem_width,
@@ -129,6 +129,12 @@ class DEM(Component):
129
129
  cv2.imwrite(self._dem_path, resampled_data)
130
130
  self.logger.debug("DEM data was saved to %s.", self._dem_path)
131
131
 
132
+ if self.game.additional_dem_name is not None:
133
+ dem_directory = os.path.dirname(self._dem_path)
134
+ additional_dem_path = os.path.join(dem_directory, self.game.additional_dem_name)
135
+ shutil.copyfile(self._dem_path, additional_dem_path)
136
+ self.logger.debug("Additional DEM data was copied to %s.", additional_dem_path)
137
+
132
138
  def _tile_info(self, lat: float, lon: float) -> tuple[str, str]:
133
139
  """Returns latitude band and tile name for SRTM tile from coordinates.
134
140
 
@@ -218,6 +224,9 @@ class DEM(Component):
218
224
  str: Path to the preview image.
219
225
  """
220
226
  rgb_dem_path = self._dem_path.replace(".png", "_grayscale.png")
227
+
228
+ self.logger.debug("Creating grayscale preview of DEM data in %s.", rgb_dem_path)
229
+
221
230
  dem_data = cv2.imread(self._dem_path, cv2.IMREAD_GRAYSCALE)
222
231
  dem_data_rgb = cv2.cvtColor(dem_data, cv2.COLOR_GRAY2RGB)
223
232
  cv2.imwrite(rgb_dem_path, dem_data_rgb)
@@ -232,12 +241,17 @@ class DEM(Component):
232
241
  """
233
242
 
234
243
  colored_dem_path = self._dem_path.replace(".png", "_colored.png")
244
+
245
+ self.logger.debug("Creating colored preview of DEM data in %s.", colored_dem_path)
246
+
235
247
  dem_data = cv2.imread(self._dem_path, cv2.IMREAD_GRAYSCALE)
236
248
 
237
- # Normalize the DEM data to the range [0, 255]
238
- # dem_data_normalized = cv2.normalize(dem_data, None, 0, 255, cv2.NORM_MINMAX)
249
+ # Create an empty array with the same shape and type as dem_data
250
+ dem_data_normalized = np.empty_like(dem_data)
239
251
 
240
- dem_data_colored = cv2.applyColorMap(dem_data, cv2.COLORMAP_JET)
252
+ # Normalize the DEM data to the range [0, 255]
253
+ cv2.normalize(dem_data, dem_data_normalized, 0, 255, cv2.NORM_MINMAX)
254
+ dem_data_colored = cv2.applyColorMap(dem_data_normalized, cv2.COLORMAP_JET)
241
255
 
242
256
  cv2.imwrite(colored_dem_path, dem_data_colored)
243
257
  return colored_dem_path
@@ -248,4 +262,5 @@ class DEM(Component):
248
262
  Returns:
249
263
  list[str]: List of preview images.
250
264
  """
265
+ self.logger.debug("Starting DEM previews generation.")
251
266
  return [self.grayscale_preview(), self.colored_preview()]
maps4fs/generator/game.py CHANGED
@@ -30,6 +30,7 @@ class Game:
30
30
 
31
31
  code: str | None = None
32
32
  dem_multipliyer: int = 1
33
+ _additional_dem_name: str | None = None
33
34
  _map_template_path: str | None = None
34
35
  _texture_schema: str | None = None
35
36
 
@@ -102,6 +103,24 @@ class Game:
102
103
  """
103
104
  raise NotImplementedError
104
105
 
106
+ def weights_dir_path(self, map_directory: str) -> str:
107
+ """Returns the path to the weights directory.
108
+
109
+ Arguments:
110
+ map_directory (str): The path to the map directory.
111
+
112
+ Returns:
113
+ str: The path to the weights directory."""
114
+ raise NotImplementedError
115
+
116
+ @property
117
+ def additional_dem_name(self) -> str | None:
118
+ """Returns the name of the additional DEM file.
119
+
120
+ Returns:
121
+ str | None: The name of the additional DEM file."""
122
+ return self._additional_dem_name
123
+
105
124
 
106
125
  class FS22(Game):
107
126
  """Class used to define the game version FS22."""
@@ -120,12 +139,23 @@ class FS22(Game):
120
139
  str: The path to the DEM file."""
121
140
  return os.path.join(map_directory, "maps", "map", "data", "map_dem.png")
122
141
 
142
+ def weights_dir_path(self, map_directory: str) -> str:
143
+ """Returns the path to the weights directory.
144
+
145
+ Arguments:
146
+ map_directory (str): The path to the map directory.
147
+
148
+ Returns:
149
+ str: The path to the weights directory."""
150
+ return os.path.join(map_directory, "maps", "map", "data")
151
+
123
152
 
124
153
  class FS25(Game):
125
154
  """Class used to define the game version FS25."""
126
155
 
127
156
  code = "FS25"
128
157
  dem_multipliyer: int = 2
158
+ _additional_dem_name = "unprocessedHeightMap.png"
129
159
  _map_template_path = os.path.join(working_directory, "data", "fs25-map-template.zip")
130
160
  _texture_schema = os.path.join(working_directory, "data", "fs25-texture-schema.json")
131
161
 
@@ -137,4 +167,25 @@ class FS25(Game):
137
167
 
138
168
  Returns:
139
169
  str: The path to the DEM file."""
140
- return os.path.join(map_directory, "maps", "map", "data", "dem.png")
170
+ return os.path.join(map_directory, "mapUS", "data", "dem.png")
171
+
172
+ def map_xml_path(self, map_directory: str) -> str:
173
+ """Returns the path to the map.xml file.
174
+
175
+ Arguments:
176
+ map_directory (str): The path to the map directory.
177
+
178
+ Returns:
179
+ str: The path to the map.xml file.
180
+ """
181
+ return os.path.join(map_directory, "mapUS", "mapUS.xml")
182
+
183
+ def weights_dir_path(self, map_directory: str) -> str:
184
+ """Returns the path to the weights directory.
185
+
186
+ Arguments:
187
+ map_directory (str): The path to the map directory.
188
+
189
+ Returns:
190
+ str: The path to the weights directory."""
191
+ return os.path.join(map_directory, "mapUS", "data")
maps4fs/generator/map.py CHANGED
@@ -91,7 +91,14 @@ class Map:
91
91
  """
92
92
  previews = []
93
93
  for component in self.components:
94
- previews.extend(component.previews())
94
+ try:
95
+ previews.extend(component.previews())
96
+ except Exception as e: # pylint: disable=W0718
97
+ self.logger.error(
98
+ "Error getting previews for component %s: %s",
99
+ component.__class__.__name__,
100
+ e,
101
+ )
95
102
  return previews
96
103
 
97
104
  def pack(self, archive_name: str) -> str:
@@ -56,12 +56,14 @@ class Texture(Component):
56
56
  tags: dict[str, str | list[str] | bool] | None = None,
57
57
  width: int | None = None,
58
58
  color: tuple[int, int, int] | list[int] | None = None,
59
+ exclude_weight: bool = False,
59
60
  ):
60
61
  self.name = name
61
62
  self.count = count
62
63
  self.tags = tags
63
64
  self.width = width
64
65
  self.color = color if color else (255, 255, 255)
66
+ self.exclude_weight = exclude_weight
65
67
 
66
68
  def to_json(self) -> dict[str, str | list[str] | bool]: # type: ignore
67
69
  """Returns dictionary with layer data.
@@ -74,6 +76,7 @@ class Texture(Component):
74
76
  "tags": self.tags,
75
77
  "width": self.width,
76
78
  "color": list(self.color),
79
+ "exclude_weight": self.exclude_weight,
77
80
  }
78
81
 
79
82
  data = {k: v for k, v in data.items() if v is not None}
@@ -100,9 +103,9 @@ class Texture(Component):
100
103
  Returns:
101
104
  str: Path to the texture.
102
105
  """
103
- if self.name == "waterPuddle":
104
- return os.path.join(weights_directory, "waterPuddle_weight.png")
105
- return os.path.join(weights_directory, f"{self.name}01_weight.png")
106
+ idx = "01" if self.count > 0 else ""
107
+ weight_postfix = "_weight" if not self.exclude_weight else ""
108
+ return os.path.join(weights_directory, f"{self.name}{idx}{weight_postfix}.png")
106
109
 
107
110
  def preprocess(self) -> None:
108
111
  if not os.path.isfile(self.game.texture_schema):
@@ -117,8 +120,10 @@ class Texture(Component):
117
120
  self.layers = [self.Layer.from_json(layer) for layer in layers_schema]
118
121
  self.logger.info("Loaded %s layers.", len(self.layers))
119
122
 
120
- self._weights_dir = os.path.join(self.map_directory, "maps", "map", "data")
123
+ self._weights_dir = self.game.weights_dir_path(self.map_directory)
124
+ self.logger.debug("Weights directory: %s.", self._weights_dir)
121
125
  self.info_save_path = os.path.join(self.map_directory, "generation_info.json")
126
+ self.logger.debug("Generation info save path: %s.", self.info_save_path)
122
127
 
123
128
  def process(self):
124
129
  self._prepare_weights()
@@ -192,25 +197,24 @@ class Texture(Component):
192
197
  self.logger.debug("Starting preparing weights from %s layers.", len(self.layers))
193
198
 
194
199
  for layer in self.layers:
195
- self._generate_weights(layer.name, layer.count)
200
+ self._generate_weights(layer)
196
201
  self.logger.debug("Prepared weights for %s layers.", len(self.layers))
197
202
 
198
- def _generate_weights(self, texture_name: str, layer_numbers: int) -> None:
203
+ def _generate_weights(self, layer: Layer) -> None:
199
204
  """Generates weight files for textures. Each file is a numpy array of zeros and
200
205
  dtype uint8 (0-255).
201
206
 
202
207
  Args:
203
- texture_name (str): Name of the texture.
204
- layer_numbers (int): Number of layers in the texture.
208
+ layer (Layer): Layer with textures and tags.
205
209
  """
206
210
  size = (self.map_height, self.map_width)
207
- postfix = "_weight.png"
208
- if layer_numbers == 0:
209
- filepaths = [os.path.join(self._weights_dir, texture_name + postfix)]
211
+ postfix = "_weight.png" if not layer.exclude_weight else ".png"
212
+ if layer.count == 0:
213
+ filepaths = [os.path.join(self._weights_dir, layer.name + postfix)]
210
214
  else:
211
215
  filepaths = [
212
- os.path.join(self._weights_dir, texture_name + str(i).zfill(2) + postfix)
213
- for i in range(1, layer_numbers + 1)
216
+ os.path.join(self._weights_dir, layer.name + str(i).zfill(2) + postfix)
217
+ for i in range(1, layer.count + 1)
214
218
  ]
215
219
 
216
220
  for filepath in filepaths:
@@ -403,13 +407,16 @@ class Texture(Component):
403
407
  preview_size,
404
408
  )
405
409
 
410
+ active_layers = [layer for layer in self.layers if layer.tags is not None]
411
+ self.logger.debug("Following layers have tag textures: %s.", len(active_layers))
412
+
406
413
  images = [
407
414
  cv2.resize(
408
415
  cv2.imread(layer.path(self._weights_dir), cv2.IMREAD_UNCHANGED), preview_size
409
416
  )
410
- for layer in self.layers
417
+ for layer in active_layers
411
418
  ]
412
- colors = [layer.color for layer in self.layers]
419
+ colors = [layer.color for layer in active_layers]
413
420
  color_images = []
414
421
  for img, color in zip(images, colors):
415
422
  color_img = np.zeros((img.shape[0], img.shape[1], 3), dtype=np.uint8)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: maps4fs
3
- Version: 0.7.3
3
+ Version: 0.7.5
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
@@ -47,9 +47,7 @@ Requires-Dist: tqdm
47
47
  🌍 Based on real-world data from OpenStreetMap<br>
48
48
  🏞️ Generates height using SRTM dataset<br>
49
49
  📦 Provides a ready-to-use map template for the Giants Editor<br>
50
- 🚜 Supports Farming Simulator 22 and 25*<br>
51
-
52
- \* changes in the library are ready, waiting for the Giants to release the Giants Editor v10. Meanwhile the option to generate a map for FS25 is disabled.
50
+ 🚜 Supports Farming Simulator 22 and 25<br>
53
51
 
54
52
  ## Quick Start
55
53
  There are several ways to use the tool. You obviously need the **first one**, but you can choose any of the others depending on your needs.<br>
@@ -172,13 +170,14 @@ The map will be saved in the `map_directory` directory.
172
170
  The project is based on the [OpenStreetMap](https://www.openstreetmap.org/) data. So, refer to [this page](https://wiki.openstreetmap.org/wiki/Map_Features) to understand the list below.
173
171
  - "building": True
174
172
  - "highway": ["motorway", "trunk", "primary"]
175
- - "highway": ["secondary", "tertiary", "road"]
173
+ - "highway": ["secondary", "tertiary", "road", "service"]
176
174
  - "highway": ["unclassified", "residential", "track"]
177
- - "natural": "grassland"
175
+ - "natural": ["grassland", "scrub"]
178
176
  - "landuse": "farmland"
179
177
  - "natural": ["water"]
180
178
  - "waterway": True
181
179
  - "natural": ["wood", "tree_row"]
180
+ - "railway": True
182
181
 
183
182
  The list will be updated as the project develops.
184
183
 
@@ -0,0 +1,14 @@
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=5DyQtAceWfL3ciIkovFx21X1gcBVyhWUmI3tJo-3DKE,10553
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.5.dist-info/LICENSE.md,sha256=-JY0v7p3dwXze61EbYiK7YEJ2aKrjaFZ8y2xYEOrmRY,1068
11
+ maps4fs-0.7.5.dist-info/METADATA,sha256=wJSatoEwVhXxQxUmlfoBJ4LZ5BI83Pt5SBqboyzVCog,11844
12
+ maps4fs-0.7.5.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
13
+ maps4fs-0.7.5.dist-info/top_level.txt,sha256=Ue9DSRlejRQRCaJueB0uLcKrWwsEq9zezfv5dI5mV1M,8
14
+ maps4fs-0.7.5.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.5.0)
2
+ Generator: setuptools (75.6.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -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=EJBXHZ0_G9nXZ-b1PmR9mjIdXjbKlYEUv-4IDGJPmdc,9779
7
- maps4fs/generator/game.py,sha256=IyXjNEC5epJmDdqjsrl4wKL85T1F23km73pUkBiuDWU,4468
8
- maps4fs/generator/map.py,sha256=Oyu45ONmSluidaUErsU6n28pqI-XYx1eVfPT9FurCTE,3522
9
- maps4fs/generator/texture.py,sha256=W2tMkCxyD7qesSuutPJ3N5InDza2Dzi5_yzewSl9e-E,15615
10
- maps4fs-0.7.3.dist-info/LICENSE.md,sha256=-JY0v7p3dwXze61EbYiK7YEJ2aKrjaFZ8y2xYEOrmRY,1068
11
- maps4fs-0.7.3.dist-info/METADATA,sha256=LimQqtHMDbvCq_hUNZg8ZmxHSIk-g8G6DRJ7Etu8F7E,11961
12
- maps4fs-0.7.3.dist-info/WHEEL,sha256=R06PA3UVYHThwHvxuRWMqaGcr-PuniXahwjmQRFMEkY,91
13
- maps4fs-0.7.3.dist-info/top_level.txt,sha256=Ue9DSRlejRQRCaJueB0uLcKrWwsEq9zezfv5dI5mV1M,8
14
- maps4fs-0.7.3.dist-info/RECORD,,