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 +22 -7
- maps4fs/generator/game.py +52 -1
- maps4fs/generator/map.py +8 -1
- maps4fs/generator/texture.py +22 -15
- {maps4fs-0.7.3.dist-info → maps4fs-0.7.5.dist-info}/METADATA +5 -6
- maps4fs-0.7.5.dist-info/RECORD +14 -0
- {maps4fs-0.7.3.dist-info → maps4fs-0.7.5.dist-info}/WHEEL +1 -1
- maps4fs-0.7.3.dist-info/RECORD +0 -14
- {maps4fs-0.7.3.dist-info → maps4fs-0.7.5.dist-info}/LICENSE.md +0 -0
- {maps4fs-0.7.3.dist-info → maps4fs-0.7.5.dist-info}/top_level.txt +0 -0
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
|
-
#
|
238
|
-
|
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
|
-
|
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, "
|
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
|
-
|
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:
|
maps4fs/generator/texture.py
CHANGED
@@ -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.
|
104
|
-
|
105
|
-
return os.path.join(weights_directory, f"{self.name}
|
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 =
|
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
|
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,
|
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
|
-
|
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
|
209
|
-
filepaths = [os.path.join(self._weights_dir,
|
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,
|
213
|
-
for i in range(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
|
417
|
+
for layer in active_layers
|
411
418
|
]
|
412
|
-
colors = [layer.color for layer in
|
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
|
+
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
|
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,,
|
maps4fs-0.7.3.dist-info/RECORD
DELETED
@@ -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,,
|
File without changes
|
File without changes
|