maps4fs 1.9.0__py3-none-any.whl → 1.9.2__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/component/background.py +36 -14
- maps4fs/generator/component/base/component.py +14 -6
- maps4fs/generator/component/base/component_image.py +14 -0
- maps4fs/generator/component/config.py +2 -2
- maps4fs/generator/component/dem.py +5 -18
- maps4fs/generator/component/grle.py +3 -3
- maps4fs/generator/component/i3d.py +14 -0
- maps4fs/generator/component/layer.py +1 -1
- maps4fs/generator/component/texture.py +38 -3
- maps4fs/generator/map.py +6 -0
- {maps4fs-1.9.0.dist-info → maps4fs-1.9.2.dist-info}/METADATA +46 -7
- {maps4fs-1.9.0.dist-info → maps4fs-1.9.2.dist-info}/RECORD +15 -15
- {maps4fs-1.9.0.dist-info → maps4fs-1.9.2.dist-info}/WHEEL +1 -1
- {maps4fs-1.9.0.dist-info → maps4fs-1.9.2.dist-info}/licenses/LICENSE.md +0 -0
- {maps4fs-1.9.0.dist-info → maps4fs-1.9.2.dist-info}/top_level.txt +0 -0
@@ -87,15 +87,14 @@ class Background(MeshComponent, ImageComponent):
|
|
87
87
|
|
88
88
|
if not self.map.custom_background_path:
|
89
89
|
self.dem.process()
|
90
|
-
self.validate_np_for_mesh(self.
|
91
|
-
|
92
|
-
shutil.copyfile(self.dem.dem_path, self.not_substracted_path)
|
93
|
-
self.save_map_dem(self.dem.dem_path, save_path=self.not_resized_path)
|
90
|
+
self.validate_np_for_mesh(self.output_path, self.map_size)
|
94
91
|
|
95
92
|
if self.map.dem_settings.water_depth:
|
96
93
|
self.subtraction()
|
97
94
|
|
98
|
-
cutted_dem_path = self.save_map_dem(self.
|
95
|
+
cutted_dem_path = self.save_map_dem(self.output_path)
|
96
|
+
shutil.copyfile(self.output_path, self.not_substracted_path)
|
97
|
+
self.save_map_dem(self.output_path, save_path=self.not_resized_path)
|
99
98
|
if self.game.additional_dem_name is not None:
|
100
99
|
self.make_copy(cutted_dem_path, self.game.additional_dem_name)
|
101
100
|
|
@@ -213,19 +212,28 @@ class Background(MeshComponent, ImageComponent):
|
|
213
212
|
"""Iterates over all dems and generates 3D obj files based on DEM data.
|
214
213
|
If at least one DEM file is missing, the generation will be stopped at all.
|
215
214
|
"""
|
216
|
-
if not os.path.isfile(self.
|
215
|
+
if not os.path.isfile(self.output_path):
|
217
216
|
self.logger.error(
|
218
|
-
"DEM file not found, generation will be stopped: %s", self.
|
217
|
+
"DEM file not found, generation will be stopped: %s", self.output_path
|
219
218
|
)
|
220
219
|
return
|
221
220
|
|
222
|
-
self.logger.debug("DEM file for found: %s", self.
|
221
|
+
self.logger.debug("DEM file for found: %s", self.output_path)
|
223
222
|
|
224
|
-
filename = os.path.splitext(os.path.basename(self.
|
223
|
+
filename = os.path.splitext(os.path.basename(self.output_path))[0]
|
225
224
|
save_path = os.path.join(self.background_directory, f"{filename}.obj")
|
226
225
|
self.logger.debug("Generating obj file in path: %s", save_path)
|
227
226
|
|
228
|
-
dem_data = cv2.imread(self.
|
227
|
+
dem_data = cv2.imread(self.output_path, cv2.IMREAD_UNCHANGED)
|
228
|
+
|
229
|
+
if self.map.output_size is not None:
|
230
|
+
scaled_background_size = int(self.background_size * self.map.size_scale)
|
231
|
+
dem_data = cv2.resize(
|
232
|
+
dem_data,
|
233
|
+
(scaled_background_size, scaled_background_size),
|
234
|
+
interpolation=cv2.INTER_NEAREST,
|
235
|
+
)
|
236
|
+
|
229
237
|
self.plane_from_np(
|
230
238
|
dem_data,
|
231
239
|
save_path,
|
@@ -255,7 +263,7 @@ class Background(MeshComponent, ImageComponent):
|
|
255
263
|
if self.map.dem_settings.add_foundations:
|
256
264
|
dem_data = self.create_foundations(dem_data)
|
257
265
|
|
258
|
-
output_size = self.
|
266
|
+
output_size = self.scaled_size + 1
|
259
267
|
|
260
268
|
main_dem_path = self.game.dem_file_path(self.map_directory)
|
261
269
|
|
@@ -265,7 +273,7 @@ class Background(MeshComponent, ImageComponent):
|
|
265
273
|
pass
|
266
274
|
|
267
275
|
resized_dem_data = cv2.resize(
|
268
|
-
dem_data, (output_size, output_size), interpolation=cv2.
|
276
|
+
dem_data, (output_size, output_size), interpolation=cv2.INTER_NEAREST
|
269
277
|
)
|
270
278
|
|
271
279
|
cv2.imwrite(main_dem_path, resized_dem_data)
|
@@ -301,7 +309,7 @@ class Background(MeshComponent, ImageComponent):
|
|
301
309
|
decimation_percent=self.map.background_settings.decimation_percent,
|
302
310
|
decimation_agression=self.map.background_settings.decimation_agression,
|
303
311
|
remove_center=remove_center,
|
304
|
-
remove_size=self.
|
312
|
+
remove_size=self.scaled_size,
|
305
313
|
)
|
306
314
|
|
307
315
|
try:
|
@@ -348,7 +356,7 @@ class Background(MeshComponent, ImageComponent):
|
|
348
356
|
preview_paths = self.dem_previews(self.game.dem_file_path(self.map_directory))
|
349
357
|
|
350
358
|
background_dem_preview_path = os.path.join(self.previews_directory, "background_dem.png")
|
351
|
-
background_dem_preview_image = cv2.imread(self.
|
359
|
+
background_dem_preview_image = cv2.imread(self.output_path, cv2.IMREAD_UNCHANGED)
|
352
360
|
|
353
361
|
background_dem_preview_image = cv2.resize(
|
354
362
|
background_dem_preview_image, (0, 0), fx=1 / 4, fy=1 / 4
|
@@ -458,6 +466,7 @@ class Background(MeshComponent, ImageComponent):
|
|
458
466
|
map_directory=self.map_directory,
|
459
467
|
logger=self.logger,
|
460
468
|
texture_custom_schema=background_layers, # type: ignore
|
469
|
+
skip_scaling=True, # type: ignore
|
461
470
|
)
|
462
471
|
|
463
472
|
self.background_texture.preprocess()
|
@@ -545,6 +554,19 @@ class Background(MeshComponent, ImageComponent):
|
|
545
554
|
# Single channeled 16 bit DEM image of terrain.
|
546
555
|
background_dem = cv2.imread(self.not_substracted_path, cv2.IMREAD_UNCHANGED)
|
547
556
|
|
557
|
+
if self.map.output_size is not None:
|
558
|
+
scaled_background_size = int(self.background_size * self.map.size_scale)
|
559
|
+
plane_water = cv2.resize(
|
560
|
+
plane_water,
|
561
|
+
(scaled_background_size, scaled_background_size),
|
562
|
+
interpolation=cv2.INTER_NEAREST,
|
563
|
+
)
|
564
|
+
background_dem = cv2.resize(
|
565
|
+
background_dem,
|
566
|
+
(scaled_background_size, scaled_background_size),
|
567
|
+
interpolation=cv2.INTER_NEAREST,
|
568
|
+
)
|
569
|
+
|
548
570
|
if self.map.background_settings.water_blurriness:
|
549
571
|
# Apply Gaussian blur to the background dem.
|
550
572
|
blur_power = self._get_blur_power()
|
@@ -339,8 +339,8 @@ class Component:
|
|
339
339
|
tuple[int, int]: The coordinates in the center system.
|
340
340
|
"""
|
341
341
|
x, y = top_left
|
342
|
-
cs_x = x - self.
|
343
|
-
cs_y = y - self.
|
342
|
+
cs_x = x - self.scaled_size // 2
|
343
|
+
cs_y = y - self.scaled_size // 2
|
344
344
|
|
345
345
|
return cs_x, cs_y
|
346
346
|
|
@@ -368,15 +368,14 @@ class Component:
|
|
368
368
|
raise ValueError("Either polygon or linestring points must be provided.")
|
369
369
|
|
370
370
|
min_x = min_y = 0 + border
|
371
|
-
max_x = max_y = self.
|
371
|
+
max_x = max_y = self.scaled_size - border
|
372
372
|
|
373
373
|
object_type = Polygon if polygon_points else LineString
|
374
374
|
|
375
|
-
# polygon = Polygon(polygon_points)
|
376
375
|
osm_object = object_type(polygon_points or linestring_points)
|
377
376
|
|
378
377
|
if angle:
|
379
|
-
center_x = center_y = self.map_rotated_size // 2
|
378
|
+
center_x = center_y = self.map_rotated_size * self.map.size_scale // 2
|
380
379
|
self.logger.debug(
|
381
380
|
"Rotating the osm_object by %s degrees with center at %sx%s",
|
382
381
|
angle,
|
@@ -384,7 +383,7 @@ class Component:
|
|
384
383
|
center_y,
|
385
384
|
)
|
386
385
|
osm_object = rotate(osm_object, -angle, origin=(center_x, center_y))
|
387
|
-
offset = (self.map_size / 2) - (self.map_rotated_size / 2)
|
386
|
+
offset = int((self.map_size / 2) - (self.map_rotated_size / 2)) * self.map.size_scale
|
388
387
|
self.logger.debug("Translating the osm_object by %s", offset)
|
389
388
|
osm_object = translate(osm_object, xoff=offset, yoff=offset)
|
390
389
|
self.logger.debug("Rotated and translated the osm_object.")
|
@@ -566,3 +565,12 @@ class Component:
|
|
566
565
|
scaling_factor *= 1 / self.map.shared_settings.mesh_z_scaling_factor
|
567
566
|
|
568
567
|
return scaling_factor
|
568
|
+
|
569
|
+
@property
|
570
|
+
def scaled_size(self) -> int:
|
571
|
+
"""Returns the output size of the map if it was set, otherwise returns the map size.
|
572
|
+
|
573
|
+
Returns:
|
574
|
+
int: The output size of the map or the map size.
|
575
|
+
"""
|
576
|
+
return self.map_size if self.map.output_size is None else self.map.output_size
|
@@ -161,3 +161,17 @@ class ImageComponent(Component):
|
|
161
161
|
)
|
162
162
|
|
163
163
|
return blurred_data
|
164
|
+
|
165
|
+
def get_blur_radius(self) -> int:
|
166
|
+
"""Get the blur radius from the DEM settings.
|
167
|
+
|
168
|
+
Returns:
|
169
|
+
int: The blur radius.
|
170
|
+
"""
|
171
|
+
blur_radius = self.map.dem_settings.blur_radius
|
172
|
+
if blur_radius is None or blur_radius <= 0:
|
173
|
+
# We'll disable blur if the radius is 0 or negative.
|
174
|
+
blur_radius = 0
|
175
|
+
elif blur_radius % 2 == 0:
|
176
|
+
blur_radius += 1
|
177
|
+
return blur_radius
|
@@ -40,20 +40,6 @@ class DEM(ImageComponent):
|
|
40
40
|
self.output_resolution = self.get_output_resolution()
|
41
41
|
self.logger.debug("Output resolution for DEM data: %s.", self.output_resolution)
|
42
42
|
|
43
|
-
blur_radius = self.map.dem_settings.blur_radius
|
44
|
-
if blur_radius is None or blur_radius <= 0:
|
45
|
-
# We'll disable blur if the radius is 0 or negative.
|
46
|
-
blur_radius = 0
|
47
|
-
elif blur_radius % 2 == 0:
|
48
|
-
blur_radius += 1
|
49
|
-
self.blur_radius = blur_radius
|
50
|
-
self.multiplier = self.map.dem_settings.multiplier
|
51
|
-
self.logger.debug(
|
52
|
-
"DEM value multiplier is %s, blur radius is %s.",
|
53
|
-
self.multiplier,
|
54
|
-
self.blur_radius,
|
55
|
-
)
|
56
|
-
|
57
43
|
self.dtm_provider: DTMProvider = self.map.dtm_provider( # type: ignore
|
58
44
|
coordinates=self.coordinates,
|
59
45
|
user_settings=self.map.dtm_provider_settings,
|
@@ -200,7 +186,7 @@ class DEM(ImageComponent):
|
|
200
186
|
self.update_info("normalized", resampled_data)
|
201
187
|
|
202
188
|
# 6. Blur DEM data.
|
203
|
-
resampled_data = self.apply_blur(resampled_data, blur_radius=self.
|
189
|
+
resampled_data = self.apply_blur(resampled_data, blur_radius=self.get_blur_radius())
|
204
190
|
|
205
191
|
cv2.imwrite(self._dem_path, resampled_data)
|
206
192
|
self.logger.debug("DEM data was saved to %s.", self._dem_path)
|
@@ -288,13 +274,14 @@ class DEM(ImageComponent):
|
|
288
274
|
Returns:
|
289
275
|
np.ndarray: Multiplied DEM data.
|
290
276
|
"""
|
291
|
-
|
277
|
+
multiplier = self.map.dem_settings.multiplier
|
278
|
+
if not multiplier != 1:
|
292
279
|
return data
|
293
280
|
|
294
|
-
multiplied_data = data *
|
281
|
+
multiplied_data = data * multiplier
|
295
282
|
self.logger.debug(
|
296
283
|
"DEM data was multiplied by %s. Min: %s, max: %s.",
|
297
|
-
|
284
|
+
multiplier,
|
298
285
|
multiplied_data.min(),
|
299
286
|
multiplied_data.max(),
|
300
287
|
)
|
@@ -87,8 +87,8 @@ class GRLE(ImageComponent, XMLComponent):
|
|
87
87
|
self.game.weights_dir_path(self.map_directory), info_layer["name"]
|
88
88
|
)
|
89
89
|
|
90
|
-
height = int(self.
|
91
|
-
width = int(self.
|
90
|
+
height = int(self.scaled_size * info_layer["height_multiplier"])
|
91
|
+
width = int(self.scaled_size * info_layer["width_multiplier"])
|
92
92
|
channels = info_layer["channels"]
|
93
93
|
data_type = info_layer["data_type"]
|
94
94
|
|
@@ -333,7 +333,7 @@ class GRLE(ImageComponent, XMLComponent):
|
|
333
333
|
grass_image_copy[grass_image != 0] = base_layer_pixel_value
|
334
334
|
|
335
335
|
# Add islands of plants to the base image.
|
336
|
-
island_count = int(self.
|
336
|
+
island_count = int(self.scaled_size * self.map.grle_settings.plants_island_percent // 100)
|
337
337
|
self.logger.debug("Adding %s islands of plants to the base image.", island_count)
|
338
338
|
if self.map.grle_settings.random_plants:
|
339
339
|
grass_image_copy = self.create_island_of_plants(grass_image_copy, island_count)
|
@@ -138,6 +138,13 @@ class I3d(XMLComponent):
|
|
138
138
|
self.logger.warning("Not resized DEM not found.")
|
139
139
|
return
|
140
140
|
|
141
|
+
if self.map.output_size is not None:
|
142
|
+
not_resized_dem = cv2.resize(
|
143
|
+
not_resized_dem,
|
144
|
+
(self.map.output_size, self.map.output_size),
|
145
|
+
interpolation=cv2.INTER_NEAREST,
|
146
|
+
)
|
147
|
+
|
141
148
|
user_attributes_node = root.find(".//UserAttributes")
|
142
149
|
if user_attributes_node is None:
|
143
150
|
self.logger.warning("UserAttributes node not found in I3D file.")
|
@@ -510,6 +517,13 @@ class I3d(XMLComponent):
|
|
510
517
|
self.logger.warning("Not resized DEM not found.")
|
511
518
|
return
|
512
519
|
|
520
|
+
if self.map.output_size is not None:
|
521
|
+
not_resized_dem = cv2.resize(
|
522
|
+
not_resized_dem,
|
523
|
+
(self.map.output_size, self.map.output_size),
|
524
|
+
interpolation=cv2.INTER_NEAREST,
|
525
|
+
)
|
526
|
+
|
513
527
|
forest_image = cv2.imread(forest_image_path, cv2.IMREAD_UNCHANGED)
|
514
528
|
for x, y in self.non_empty_pixels(
|
515
529
|
forest_image, step=self.map.i3d_settings.forest_density
|
@@ -152,7 +152,7 @@ class Layer:
|
|
152
152
|
|
153
153
|
# Inconsistent names are the name of textures that are not following the pattern
|
154
154
|
# of texture_name{idx}_weight.png.
|
155
|
-
inconsistent_names = ["
|
155
|
+
inconsistent_names = ["forestRockRoots", "waterPuddle"]
|
156
156
|
|
157
157
|
if self.name in inconsistent_names:
|
158
158
|
return [
|
@@ -140,6 +140,10 @@ class Texture(ImageComponent):
|
|
140
140
|
self._read_parameters()
|
141
141
|
self.draw()
|
142
142
|
self.rotate_textures()
|
143
|
+
|
144
|
+
if not self.kwargs.get("skip_scaling", False):
|
145
|
+
self.scale_textures()
|
146
|
+
|
143
147
|
self.add_borders()
|
144
148
|
if self.map.texture_settings.dissolve and self.game.dissolve:
|
145
149
|
self.dissolve()
|
@@ -181,7 +185,7 @@ class Texture(ImageComponent):
|
|
181
185
|
blockmask_path = os.path.join(self.procedural_dir, "BLOCKMASK.png")
|
182
186
|
if not os.path.isfile(blockmask_path):
|
183
187
|
self.logger.debug("BLOCKMASK.png not found, creating an empty file.")
|
184
|
-
img = np.zeros((self.
|
188
|
+
img = np.zeros((self.scaled_size, self.scaled_size), dtype=np.uint8)
|
185
189
|
cv2.imwrite(blockmask_path, img)
|
186
190
|
|
187
191
|
pg_layers_by_type = defaultdict(list)
|
@@ -200,7 +204,7 @@ class Texture(ImageComponent):
|
|
200
204
|
procedural_save_path = os.path.join(self.procedural_dir, f"{procedural_layer_name}.png")
|
201
205
|
if len(texture_paths) > 1:
|
202
206
|
# If there are more than one texture, merge them.
|
203
|
-
merged_texture = np.zeros((self.
|
207
|
+
merged_texture = np.zeros((self.scaled_size, self.scaled_size), dtype=np.uint8)
|
204
208
|
for texture_path in texture_paths:
|
205
209
|
texture = cv2.imread(texture_path, cv2.IMREAD_UNCHANGED)
|
206
210
|
merged_texture[texture == 255] = 255
|
@@ -239,6 +243,29 @@ class Texture(ImageComponent):
|
|
239
243
|
"Skipping rotation of layer %s because it has no tags.", layer.name
|
240
244
|
)
|
241
245
|
|
246
|
+
def scale_textures(self) -> None:
|
247
|
+
"""Resizes all the textures to the map output size."""
|
248
|
+
if not self.map.output_size:
|
249
|
+
self.logger.debug("No output size defined, skipping scaling.")
|
250
|
+
return
|
251
|
+
|
252
|
+
for layer in tqdm(self.layers, desc="Scaling textures", unit="layer"):
|
253
|
+
layer_paths = layer.paths(self._weights_dir)
|
254
|
+
layer_paths += [layer.path_preview(self._weights_dir)]
|
255
|
+
|
256
|
+
for layer_path in layer_paths:
|
257
|
+
if os.path.isfile(layer_path):
|
258
|
+
self.logger.debug("Scaling layer %s.", layer_path)
|
259
|
+
img = cv2.imread(layer_path, cv2.IMREAD_UNCHANGED)
|
260
|
+
img = cv2.resize(
|
261
|
+
img,
|
262
|
+
(self.map.output_size, self.map.output_size),
|
263
|
+
interpolation=cv2.INTER_NEAREST,
|
264
|
+
)
|
265
|
+
cv2.imwrite(layer_path, img)
|
266
|
+
else:
|
267
|
+
self.logger.debug("Layer %s not found, skipping scaling.", layer_path)
|
268
|
+
|
242
269
|
def _read_parameters(self) -> None:
|
243
270
|
"""Reads map parameters from OSM data, such as:
|
244
271
|
- minimum and maximum coordinates
|
@@ -426,6 +453,11 @@ class Texture(ImageComponent):
|
|
426
453
|
for linestring in self.objects_generator(
|
427
454
|
layer.tags, layer.width, layer.info_layer, yield_linestrings=True
|
428
455
|
):
|
456
|
+
if self.map.size_scale is not None:
|
457
|
+
linestring = [
|
458
|
+
(int(x * self.map.size_scale), int(y * self.map.size_scale))
|
459
|
+
for x, y in linestring
|
460
|
+
]
|
429
461
|
linestring_entry = {
|
430
462
|
"points": linestring,
|
431
463
|
"tags": str(layer.tags),
|
@@ -519,7 +551,10 @@ class Texture(ImageComponent):
|
|
519
551
|
Returns:
|
520
552
|
list[tuple[int, int]]: List of tuples.
|
521
553
|
"""
|
522
|
-
return [
|
554
|
+
return [
|
555
|
+
(int(x * self.map.size_scale), int(y * self.map.size_scale))
|
556
|
+
for x, y in np_array.reshape(-1, 2)
|
557
|
+
]
|
523
558
|
|
524
559
|
def _to_np(self, geometry: Polygon, *args) -> np.ndarray:
|
525
560
|
"""Converts Polygon geometry to numpy array of polygon points.
|
maps4fs/generator/map.py
CHANGED
@@ -69,6 +69,10 @@ class Map:
|
|
69
69
|
|
70
70
|
self.rotation = rotation
|
71
71
|
self.rotated_size = int(size * rotation_multiplier)
|
72
|
+
self.output_size = kwargs.get("output_size", None)
|
73
|
+
self.size_scale = 1.0
|
74
|
+
if self.output_size:
|
75
|
+
self.size_scale = self.output_size / self.size
|
72
76
|
|
73
77
|
self.game = game
|
74
78
|
self.dtm_provider = dtm_provider
|
@@ -96,6 +100,8 @@ class Map:
|
|
96
100
|
log_entry = ""
|
97
101
|
log_entry += f"Map instance created for Game: {game.code}. "
|
98
102
|
log_entry += f"Coordinates: {coordinates}. Size: {size}. Rotation: {rotation}. "
|
103
|
+
if self.output_size:
|
104
|
+
log_entry += f"Output size: {self.output_size}. Scaling: {self.size_scale}. "
|
99
105
|
log_entry += f"DTM provider is {dtm_provider.name()}. "
|
100
106
|
|
101
107
|
self.custom_osm = custom_osm
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: maps4fs
|
3
|
-
Version: 1.9.
|
3
|
+
Version: 1.9.2
|
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
|
@@ -40,8 +40,9 @@ Dynamic: license-file
|
|
40
40
|
<a href="docs/step_by_step.md">Create a map in 10 steps</a> •
|
41
41
|
<a href="#How-To-Run">How-To-Run</a><br>
|
42
42
|
<a href="docs/FAQ.md">FAQ</a> •
|
43
|
-
<a href="docs/map_structure.md">Map Structure</a>
|
44
|
-
<a href="#Modder-Toolbox">Modder Toolbox</a
|
43
|
+
<a href="docs/map_structure.md">Map Structure</a>
|
44
|
+
<a href="#Modder-Toolbox">Modder Toolbox</a> •
|
45
|
+
<a href="#Main-Settings">Main Settings</a><br>
|
45
46
|
<a href="#Supported-objects">Supported objects</a> •
|
46
47
|
<a href="#Generation-info">Generation info</a> •
|
47
48
|
<a href="#Texture-schema">Texture schema</a> •
|
@@ -73,15 +74,16 @@ Dynamic: license-file
|
|
73
74
|
</div>
|
74
75
|
|
75
76
|
🗺️ Supports 2x2, 4x4, 8x8, 16x16 and any custom size maps<br>
|
77
|
+
✂️ Supports map scaling 🆕<br>
|
76
78
|
🔄 Support map rotation<br>
|
77
|
-
🌐 Supports custom [DTM Providers](#DTM-Providers)
|
79
|
+
🌐 Supports custom [DTM Providers](#DTM-Providers)<br>
|
78
80
|
🌾 Automatically generates fields<br>
|
79
81
|
🌽 Automatically generates farmlands<br>
|
80
82
|
🌿 Automatically generates decorative foliage<br>
|
81
83
|
🌲 Automatically generates forests<br>
|
82
84
|
🌊 Automatically generates water planes<br>
|
83
|
-
📈 Automatically generates splines
|
84
|
-
🛰️ Automatically downloads high resolution satellite images
|
85
|
+
📈 Automatically generates splines<br>
|
86
|
+
🛰️ Automatically downloads high resolution satellite images<br>
|
85
87
|
🌍 Based on real-world data from OpenStreetMap<br>
|
86
88
|
🗺️ Supports [custom OSM maps](/docs/custom_osm.md)<br>
|
87
89
|
🏞️ Generates height map using SRTM dataset<br>
|
@@ -199,7 +201,8 @@ Don't know where to start? Don't worry, just follow this [step-by-step guide](do
|
|
199
201
|
|
200
202
|
🟢 Recommended for all users.
|
201
203
|
🛠️ Don't need to install anything.
|
202
|
-
🗺️ Supported map sizes: 2x2, 4x4
|
204
|
+
🗺️ Supported map sizes: 2x2, 4x4.
|
205
|
+
✂️ Map scaling: not supported.
|
203
206
|
⚙️ Advanced settings: enabled.
|
204
207
|
🖼️ Texture dissolving: enabled.
|
205
208
|
Using the public version on [maps4fs.xyz](https://maps4fs.xyz) is the easiest way to generate a map template. Just open the link and follow the instructions.
|
@@ -212,6 +215,7 @@ Using it is easy and doesn't require any guides. Enjoy!
|
|
212
215
|
🟠 Recommended for users who want bigger maps, fast generation, nice-looking textures, and advanced settings.
|
213
216
|
🛠️ Launch with one single command.
|
214
217
|
🗺️ Supported map sizes: 2x2, 4x4, 8x8, 16x16 km and any custom size.
|
218
|
+
✂️ Map scaling: supported.
|
215
219
|
⚙️ Advanced settings: enabled.
|
216
220
|
🖼️ Texture dissolving: enabled.
|
217
221
|
Check out the [Docker FAQ](docs/FAQ_docker.md) if you have any questions.<br>
|
@@ -243,6 +247,7 @@ Remember to replace `*.*.*` with the version you want to use, e.g. `iwatkot/maps
|
|
243
247
|
|
244
248
|
🔴 Recommended for developers.
|
245
249
|
🗺️ Supported map sizes: 2x2, 4x4, 8x8, 16x16 km and any custom size.
|
250
|
+
✂️ Map scaling: supported.
|
246
251
|
⚙️ Advanced settings: enabled.
|
247
252
|
🖼️ Texture dissolving: enabled.
|
248
253
|
You can use the Python package to generate maps. Follow these steps:
|
@@ -347,6 +352,40 @@ Tools are divided into categories, which are listed below.
|
|
347
352
|
|
348
353
|
- **Convert image to obj model** - allows you to convert the image to the obj model. You can use this tool to create the background terrain for your map. It can be extremely useful if you have access to the sources of high-resolution DEM data and want to create the background terrain using it.
|
349
354
|
|
355
|
+
## Main Settings
|
356
|
+
|
357
|
+
### Game Selection
|
358
|
+
The tool supports two games: Farming Simulator 22 and Farming Simulator 25. You can select the game you want to generate the map for in the `Game` dropdown list. The default value is `FS25`, but you can change it to `FS22` if you want to generate a map for Farming Simulator 22.<br>
|
359
|
+
**NOTE:** Support for Farming Simulator 22 is discontinued. The tool will not be updated for this game anymore. Some features, such as forest generation, fields generation not implemented and not planned to be implemented. The tool will be updated only for Farming Simulator 25.<br>
|
360
|
+
|
361
|
+
### Latitude and Longitude
|
362
|
+
These are the coordinates of the center of the map. The coordinates should be in decimal format, e.g. `45.28, 20.23`, any other format will not work.
|
363
|
+
|
364
|
+
### Map Size
|
365
|
+
|
366
|
+
#### Default sizes
|
367
|
+
The tool supports all possible sizes of maps, but some of them only available in the [Docker version](#option-2-docker-version). <br>
|
368
|
+
The sizes are:
|
369
|
+
- 2x2 km
|
370
|
+
- 4x4 km
|
371
|
+
- 8x8 km
|
372
|
+
- 16x16 km
|
373
|
+
|
374
|
+
**NOTE:** 16 km maps probably won't work for FS25 due to the limitations of the game engine. The map will be generated, but you may have issues trying to open it in the Giants Editor.
|
375
|
+
|
376
|
+
#### Custom size
|
377
|
+
You can also specify any custom size of the map. Be aware that Giants Editor supports only square maps, which size is a power of 2 (2048, 4096 and so on). All other sizes will be generated, but if you try to open them in the Giants Editor, it will crash. If you want your map to cover other real-world region, use the [Output size](#output-size) option.
|
378
|
+
|
379
|
+
#### Output size
|
380
|
+
This setting can be useful if you want add some scaling to your map. For example, you can select a region of 3000 meters in real world and set the output size to 2048 meters. In this case, the map will be generated with a size of 2048x2048 meters, but it will contain the region of 3000x3000 meters in real world.
|
381
|
+
|
382
|
+
### DTM Provider
|
383
|
+
DTM Provider is a source of the height map data. will find the list of available providers in the [DTM Providers](#dtm-providers) section. The default provider is `SRTM30Provider` which is available all aroung the globe, but the resolution is not very high. If you want to use a different provider, you can select it in the dropdown list. You will only see the providers that are available for the selected region. It's better to use the provider that has the highest resolution for the selected region.
|
384
|
+
**NOTE:** Some of the providers are community-developed and may not work properly. I do not provide any support for them. If you have any issues with them, please contact the provider's author.
|
385
|
+
|
386
|
+
### Map Rotation
|
387
|
+
You can rotate the map by any angle. The rotation is applied to the map and the height map. The rotation is in degrees, so you can use any value from 0 to 360. The default value is `0`, which means that the map will be generated without rotation.
|
388
|
+
|
350
389
|
## Supported objects
|
351
390
|
|
352
391
|
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.
|
@@ -2,22 +2,22 @@ maps4fs/__init__.py,sha256=EGvLVoRpSt4jITswsGbe1ISVmGAZAMQJcBmTwtyuVxI,335
|
|
2
2
|
maps4fs/logger.py,sha256=HQrDyj72mUjVYo25aR_-_SxVn2rfFjDCNbj-JKJdSnE,1488
|
3
3
|
maps4fs/generator/__init__.py,sha256=zZMLEkGzb4z0xql650gOtGSvcgX58DnJ2yN3vC2daRk,43
|
4
4
|
maps4fs/generator/game.py,sha256=NZaxj5z7WzMiHzAvQyr-TvVjGoHgqGldM6ZsItuYyzA,11292
|
5
|
-
maps4fs/generator/map.py,sha256=
|
5
|
+
maps4fs/generator/map.py,sha256=XYKzhr2SuC3ILty1RYfaWkxUr9VJF3FkpWJ6-dTwzB8,13896
|
6
6
|
maps4fs/generator/qgis.py,sha256=Es8hLuqN_KH8lDfnJE6He2rWYbAKJ3RGPn-o87S6CPI,6116
|
7
7
|
maps4fs/generator/settings.py,sha256=E5sfkfJgcjh9QHpa7ILlV_jnrF6USpsOFxkooVnxrbs,6968
|
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=
|
11
|
-
maps4fs/generator/component/config.py,sha256=
|
12
|
-
maps4fs/generator/component/dem.py,sha256=
|
13
|
-
maps4fs/generator/component/grle.py,sha256=
|
14
|
-
maps4fs/generator/component/i3d.py,sha256=
|
15
|
-
maps4fs/generator/component/layer.py,sha256
|
10
|
+
maps4fs/generator/component/background.py,sha256=kt_RaLrj6hZeZaaFNT36jJyp9IofiN9CFsO8EYY7F7A,23756
|
11
|
+
maps4fs/generator/component/config.py,sha256=dEezqrDJ9ghEvJ1Y2jdL-v7ezgeeTN415Fcr_vBQmqg,3680
|
12
|
+
maps4fs/generator/component/dem.py,sha256=Bvm3ViA6PpR7RXRAHBj5rgYB9PWy55Qj6PhTMv6dJRI,11953
|
13
|
+
maps4fs/generator/component/grle.py,sha256=xwuwkqVDvxQL6r9E5pytIcPH0HsgHI7ZYGmCcyfd8Pc,19484
|
14
|
+
maps4fs/generator/component/i3d.py,sha256=432LJWfOcY4rPXc2tb3okCRJ_DD9wgIHt0u0nqJBhJI,23617
|
15
|
+
maps4fs/generator/component/layer.py,sha256=-br4gAGcGeBNb3ldch9XFEK0lhXqb1IbArhFB5Owu54,6186
|
16
16
|
maps4fs/generator/component/satellite.py,sha256=oZBHjP_QY0ik1-Vk7JqMS__zIG8ffw2voeozB7-HUQc,4946
|
17
|
-
maps4fs/generator/component/texture.py,sha256=
|
17
|
+
maps4fs/generator/component/texture.py,sha256=tCMuuNcRwgdPkPVmNA7BCbnsTBnwkUXOrdlJJUoYAMU,33273
|
18
18
|
maps4fs/generator/component/base/__init__.py,sha256=zZMLEkGzb4z0xql650gOtGSvcgX58DnJ2yN3vC2daRk,43
|
19
|
-
maps4fs/generator/component/base/component.py,sha256=
|
20
|
-
maps4fs/generator/component/base/component_image.py,sha256=
|
19
|
+
maps4fs/generator/component/base/component.py,sha256=13ZjIatyxmxfcpE89cqlpQ6IBppJTeUisIEVa344eqM,21833
|
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=6OO1dKoceO1ACk7-k1oGtnkfNud8ZN3u3ZNjdNMpTqw,3967
|
23
23
|
maps4fs/generator/dtm/__init__.py,sha256=CzK8ZdLU5Iv7DrwK5hIQ41zVsLRFgZO-IuRxf-NCVqg,1516
|
@@ -54,8 +54,8 @@ maps4fs/toolbox/__init__.py,sha256=zZMLEkGzb4z0xql650gOtGSvcgX58DnJ2yN3vC2daRk,4
|
|
54
54
|
maps4fs/toolbox/background.py,sha256=RclEqxEWLbMxuEkkegQP8jybzugwQ1_R3rdfDe0s21U,2104
|
55
55
|
maps4fs/toolbox/custom_osm.py,sha256=X6ZlPqiOhNjkmdD_qVroIfdOl9Rb90cDwVSLDVYgx80,1892
|
56
56
|
maps4fs/toolbox/dem.py,sha256=z9IPFNmYbjiigb3t02ZenI3Mo8odd19c5MZbjDEovTo,3525
|
57
|
-
maps4fs-1.9.
|
58
|
-
maps4fs-1.9.
|
59
|
-
maps4fs-1.9.
|
60
|
-
maps4fs-1.9.
|
61
|
-
maps4fs-1.9.
|
57
|
+
maps4fs-1.9.2.dist-info/licenses/LICENSE.md,sha256=pTKD_oUexcn-yccFCTrMeLkZy0ifLRa-VNcDLqLZaIw,10749
|
58
|
+
maps4fs-1.9.2.dist-info/METADATA,sha256=JbEQlBdtwmVmxgKqAX18yn6PFBwmyQrFGOo4ZJLa8iI,50476
|
59
|
+
maps4fs-1.9.2.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
|
60
|
+
maps4fs-1.9.2.dist-info/top_level.txt,sha256=Ue9DSRlejRQRCaJueB0uLcKrWwsEq9zezfv5dI5mV1M,8
|
61
|
+
maps4fs-1.9.2.dist-info/RECORD,,
|
File without changes
|
File without changes
|