maps4fs 1.9.54__py3-none-any.whl → 2.0.1__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.
@@ -308,10 +308,6 @@ class Background(MeshComponent, ImageComponent):
308
308
  dem_data,
309
309
  include_zeros=include_zeros,
310
310
  z_scaling_factor=self.get_z_scaling_factor(ignore_height_scale_multiplier=True),
311
- resize_factor=self.map.background_settings.resize_factor,
312
- apply_decimation=self.map.background_settings.apply_decimation,
313
- decimation_percent=self.map.background_settings.decimation_percent,
314
- decimation_agression=self.map.background_settings.decimation_agression,
315
311
  remove_center=remove_center,
316
312
  remove_size=self.scaled_size,
317
313
  )
@@ -62,10 +62,6 @@ class MeshComponent(Component):
62
62
  image: np.ndarray,
63
63
  include_zeros: bool,
64
64
  z_scaling_factor: float,
65
- resize_factor: int,
66
- apply_decimation: bool,
67
- decimation_percent: int,
68
- decimation_agression: int,
69
65
  remove_center: bool,
70
66
  remove_size: int,
71
67
  ) -> trimesh.Trimesh:
@@ -75,10 +71,6 @@ class MeshComponent(Component):
75
71
  image (np.ndarray): The numpy array to generate the mesh from.
76
72
  include_zeros (bool): Whether to include zero values in the mesh.
77
73
  z_scaling_factor (float): The scaling factor for the Z-axis.
78
- resize_factor (int): The resizing factor.
79
- apply_decimation (bool): Whether to apply decimation to the mesh.
80
- decimation_percent (int): The percent of the decimation.
81
- decimation_agression (int): The agression of the decimation.
82
74
  remove_center (bool): Whether to remove the center from the mesh.
83
75
  remove_size (int): The size of the center to remove.
84
76
 
@@ -88,7 +80,7 @@ class MeshComponent(Component):
88
80
  output_x_size, _ = image.shape
89
81
  image = image.max() - image
90
82
 
91
- image = image[::resize_factor, ::resize_factor]
83
+ image = image[:: Parameters.RESIZE_FACTOR, :: Parameters.RESIZE_FACTOR]
92
84
 
93
85
  rows, cols = image.shape
94
86
  x = np.linspace(0, cols - 1, cols)
@@ -124,22 +116,9 @@ class MeshComponent(Component):
124
116
  mesh = trimesh.Trimesh(vertices=vertices, faces=faces_np)
125
117
  mesh = MeshComponent.rotate_mesh(mesh)
126
118
 
127
- if apply_decimation:
128
- percent = decimation_percent / 100
129
- mesh = mesh.simplify_quadric_decimation(
130
- percent=percent, aggression=decimation_agression
131
- )
132
-
133
- # * Looks like it takes some time but has no effect on the result.
134
- # try:
135
- # if not mesh.is_watertight:
136
- # mesh = MeshComponent.fix_mesh(mesh)
137
- # except Exception:
138
- # pass
139
-
140
119
  mesh = MeshComponent.mesh_to_output_size(
141
120
  mesh,
142
- resize_factor,
121
+ Parameters.RESIZE_FACTOR,
143
122
  z_scaling_factor,
144
123
  output_x_size,
145
124
  skip_resize_to_expected_size=not include_zeros,
@@ -13,8 +13,6 @@ from maps4fs.generator.component.base.component_image import ImageComponent
13
13
  from maps4fs.generator.component.base.component_xml import XMLComponent
14
14
  from maps4fs.generator.settings import Parameters
15
15
 
16
- FARMLAND_ID_LIMIT = 254
17
-
18
16
 
19
17
  def plant_to_pixel_value(plant_name: str) -> int | None:
20
18
  """Returns the pixel value representation of the plant.
@@ -229,7 +227,7 @@ class GRLE(ImageComponent, XMLComponent):
229
227
 
230
228
  farmland_np = self.polygon_points_to_np(fitted_farmland, divide=2)
231
229
 
232
- if farmland_id > FARMLAND_ID_LIMIT:
230
+ if farmland_id > Parameters.FARMLAND_ID_LIMIT:
233
231
  self.logger.warning(
234
232
  "Farmland ID limit reached. Skipping the rest of the farmlands. "
235
233
  "Giants Editor supports maximum 254 farmlands."
@@ -335,7 +333,7 @@ class GRLE(ImageComponent, XMLComponent):
335
333
  grass_image_copy[grass_image != 0] = base_layer_pixel_value
336
334
 
337
335
  # Add islands of plants to the base image.
338
- island_count = int(self.scaled_size * self.map.grle_settings.plants_island_percent // 100)
336
+ island_count = int(self.scaled_size * Parameters.PLANTS_ISLAND_PERCENT // 100)
339
337
  self.logger.debug("Adding %s islands of plants to the base image.", island_count)
340
338
  if self.map.grle_settings.random_plants:
341
339
  grass_image_copy = self.create_island_of_plants(grass_image_copy, island_count)
@@ -387,8 +385,8 @@ class GRLE(ImageComponent, XMLComponent):
387
385
  plant_value = choice(possible_r_values)
388
386
  # Randomly choose the size of the island.
389
387
  island_size = randint(
390
- self.map.grle_settings.plants_island_minimum_size,
391
- self.map.grle_settings.plants_island_maximum_size,
388
+ Parameters.PLANTS_ISLAND_MINIMUM_SIZE,
389
+ Parameters.PLANTS_ISLAND_MAXIMUM_SIZE,
392
390
  )
393
391
  # Randomly choose the position of the island.
394
392
  x = randint(0, image.shape[1] - island_size)
@@ -396,10 +394,10 @@ class GRLE(ImageComponent, XMLComponent):
396
394
 
397
395
  try:
398
396
  polygon_points = self.get_rounded_polygon(
399
- num_vertices=self.map.grle_settings.plants_island_vertex_count,
397
+ num_vertices=Parameters.PLANTS_ISLAND_VERTEX_COUNT,
400
398
  center=(x + island_size // 2, y + island_size // 2),
401
399
  radius=island_size // 2,
402
- rounding_radius=self.map.grle_settings.plants_island_rounding_radius,
400
+ rounding_radius=Parameters.PLANTS_ISLAND_ROUNDING_RADIUS,
403
401
  )
404
402
  if not polygon_points:
405
403
  continue
@@ -120,7 +120,7 @@ class I3d(XMLComponent):
120
120
 
121
121
  roads_polylines = self.get_infolayer_data(Parameters.TEXTURES, Parameters.ROADS_POLYLINES)
122
122
 
123
- if self.map.spline_settings.field_splines:
123
+ if self.map.i3d_settings.field_splines:
124
124
  fields_polygons = self.get_infolayer_data(Parameters.TEXTURES, Parameters.FIELDS)
125
125
  if isinstance(roads_polylines, list) and isinstance(fields_polygons, list):
126
126
  roads_polylines.extend(fields_polygons)
@@ -180,11 +180,11 @@ class I3d(XMLComponent):
180
180
  continue
181
181
 
182
182
  fitted_road = self.interpolate_points(
183
- fitted_road, num_points=self.map.spline_settings.spline_density
183
+ fitted_road, num_points=self.map.i3d_settings.spline_density
184
184
  )
185
185
  fitted_roads = [(fitted_road, "original")]
186
186
 
187
- if self.map.spline_settings.add_reversed_splines:
187
+ if self.map.i3d_settings.add_reversed_splines:
188
188
  reversed_fitted_road = fitted_road[::-1]
189
189
  fitted_roads.append((reversed_fitted_road, "reversed"))
190
190
 
@@ -55,13 +55,12 @@ class Satellite(ImageComponent):
55
55
  self.logger.debug("Satellite images download is disabled.")
56
56
  return
57
57
 
58
- margin = self.map.satellite_settings.satellite_margin
59
- overview_size = (self.map_size + margin) * 2
58
+ overview_size = self.map_size * 2
60
59
  overwiew_path = os.path.join(self.satellite_directory, "satellite_overview.png")
61
60
 
62
61
  self.assets.overview = overwiew_path
63
62
 
64
- background_size = self.map_size + (Parameters.BACKGROUND_DISTANCE + margin) * 2
63
+ background_size = self.map_size + Parameters.BACKGROUND_DISTANCE * 2
65
64
  background_path = os.path.join(self.satellite_directory, "satellite_background.png")
66
65
 
67
66
  self.assets.background = background_path
maps4fs/generator/map.py CHANGED
@@ -6,12 +6,15 @@ import json
6
6
  import os
7
7
  import shutil
8
8
  from typing import Any, Generator
9
+ from xml.etree import ElementTree as ET
9
10
 
11
+ import osmnx as ox
10
12
  from geopy.geocoders import Nominatim
13
+ from osmnx._errors import InsufficientResponseError
11
14
 
12
15
  from maps4fs.generator.component import Background, Component, Layer, Texture
13
16
  from maps4fs.generator.dtm.dtm import DTMProvider, DTMProviderSettings
14
- from maps4fs.generator.game import Game
17
+ from maps4fs.generator.game import FS25, Game
15
18
  from maps4fs.generator.settings import (
16
19
  BackgroundSettings,
17
20
  DEMSettings,
@@ -19,7 +22,6 @@ from maps4fs.generator.settings import (
19
22
  I3DSettings,
20
23
  SatelliteSettings,
21
24
  SharedSettings,
22
- SplineSettings,
23
25
  TextureSettings,
24
26
  )
25
27
  from maps4fs.generator.statistics import send_advanced_settings, send_main_settings
@@ -53,7 +55,6 @@ class Map:
53
55
  grle_settings: GRLESettings = GRLESettings(),
54
56
  i3d_settings: I3DSettings = I3DSettings(),
55
57
  texture_settings: TextureSettings = TextureSettings(),
56
- spline_settings: SplineSettings = SplineSettings(),
57
58
  satellite_settings: SatelliteSettings = SatelliteSettings(),
58
59
  **kwargs,
59
60
  ):
@@ -108,6 +109,21 @@ class Map:
108
109
  self.custom_osm = custom_osm
109
110
  log_entry += f"Custom OSM file: {custom_osm}. "
110
111
 
112
+ if self.custom_osm:
113
+ osm_is_valid = check_osm_file(self.custom_osm)
114
+ if not osm_is_valid:
115
+ self.logger.warning(
116
+ "Custom OSM file %s is not valid. Attempting to fix it.", custom_osm
117
+ )
118
+ fixed, fixed_errors = fix_osm_file(self.custom_osm)
119
+ if not fixed:
120
+ raise ValueError(
121
+ f"Custom OSM file {custom_osm} is not valid and cannot be fixed."
122
+ )
123
+ self.logger.info(
124
+ "Custom OSM file %s fixed. Fixed errors: %d", custom_osm, fixed_errors
125
+ )
126
+
111
127
  # Make a copy of a custom osm file to the map directory, so it will be
112
128
  # included in the output archive.
113
129
  if custom_osm:
@@ -131,8 +147,6 @@ class Map:
131
147
  log_entry += f"I3D settings: {i3d_settings}. "
132
148
  self.texture_settings = texture_settings
133
149
  log_entry += f"Texture settings: {texture_settings}. "
134
- self.spline_settings = spline_settings
135
- log_entry += f"Spline settings: {spline_settings}. "
136
150
  self.satellite_settings = satellite_settings
137
151
 
138
152
  self.logger.info(log_entry)
@@ -145,7 +159,6 @@ class Map:
145
159
  grle_settings,
146
160
  i3d_settings,
147
161
  texture_settings,
148
- spline_settings,
149
162
  satellite_settings,
150
163
  ]
151
164
 
@@ -386,3 +399,62 @@ class Map:
386
399
  self.logger.error("Error getting country name by coordinates: %s", e)
387
400
  return "Unknown"
388
401
  return "Unknown"
402
+
403
+
404
+ def check_osm_file(file_path: str) -> bool:
405
+ """Tries to read the OSM file using OSMnx and returns True if the file is valid,
406
+ False otherwise.
407
+
408
+ Arguments:
409
+ file_path (str): Path to the OSM file.
410
+
411
+ Returns:
412
+ bool: True if the file is valid, False otherwise.
413
+ """
414
+ with open(FS25().texture_schema, encoding="utf-8") as f:
415
+ schema = json.load(f)
416
+
417
+ tags = []
418
+ for element in schema:
419
+ element_tags = element.get("tags")
420
+ if element_tags:
421
+ tags.append(element_tags)
422
+
423
+ for tag in tags:
424
+ try:
425
+ ox.features_from_xml(file_path, tags=tag)
426
+ except InsufficientResponseError:
427
+ continue
428
+ except Exception: # pylint: disable=W0718
429
+ return False
430
+ return True
431
+
432
+
433
+ def fix_osm_file(input_file_path: str, output_file_path: str | None = None) -> tuple[bool, int]:
434
+ """Fixes the OSM file by removing all the <relation> nodes and all the nodes with
435
+ action='delete'.
436
+
437
+ Arguments:
438
+ input_file_path (str): Path to the input OSM file.
439
+ output_file_path (str | None): Path to the output OSM file. If None, the input file
440
+ will be overwritten.
441
+
442
+ Returns:
443
+ tuple[bool, int]: A tuple containing the result of the check_osm_file function
444
+ and the number of fixed errors.
445
+ """
446
+ broken_entries = ["relation", ".//*[@action='delete']"]
447
+
448
+ tree = ET.parse(input_file_path)
449
+ root = tree.getroot()
450
+
451
+ fixed_errors = 0
452
+ for entry in broken_entries:
453
+ for element in root.findall(entry):
454
+ root.remove(element)
455
+ fixed_errors += 1
456
+
457
+ tree.write(output_file_path) # type: ignore
458
+ result = check_osm_file(output_file_path) # type: ignore
459
+
460
+ return result, fixed_errors
@@ -25,6 +25,16 @@ class Parameters:
25
25
  FULL = "FULL"
26
26
  PREVIEW = "PREVIEW"
27
27
 
28
+ RESIZE_FACTOR = 8
29
+
30
+ FARMLAND_ID_LIMIT = 254
31
+
32
+ PLANTS_ISLAND_PERCENT = 100
33
+ PLANTS_ISLAND_MINIMUM_SIZE = 10
34
+ PLANTS_ISLAND_MAXIMUM_SIZE = 200
35
+ PLANTS_ISLAND_VERTEX_COUNT = 30
36
+ PLANTS_ISLAND_ROUNDING_RADIUS = 15
37
+
28
38
 
29
39
  class SharedSettings(BaseModel):
30
40
  """Represents the shared settings for all components."""
@@ -143,25 +153,15 @@ class BackgroundSettings(SettingsModel):
143
153
  Attributes:
144
154
  generate_background (bool): generate obj files for the background terrain.
145
155
  generate_water (bool): generate obj files for the water.
146
- resize_factor (int): resize factor for the background terrain and water.
147
- It will be used as 1 / resize_factor of the original size.
148
156
  water_blurriness (int): blurriness of the water.
149
157
  remove_center (bool): remove the center of the background terrain.
150
158
  It will be used to remove the center of the map where the player starts.
151
- apply_decimation (bool): apply decimation to the background terrain.
152
- decimation_percent (int): percentage of the decimation.
153
- decimation_agression (int): agression of the decimation.
154
- It will be used to control the amount of decimation applied to the background terrain.
155
159
  """
156
160
 
157
161
  generate_background: bool = False
158
162
  generate_water: bool = False
159
163
  water_blurriness: int = 20
160
- resize_factor: int = 8
161
164
  remove_center: bool = True
162
- apply_decimation: bool = False
163
- decimation_percent: int = 25
164
- decimation_agression: int = 3
165
165
 
166
166
 
167
167
  class GRLESettings(SettingsModel):
@@ -169,9 +169,15 @@ class GRLESettings(SettingsModel):
169
169
 
170
170
  Attributes:
171
171
  farmland_margin (int): margin around the farmland.
172
- random_plants (bool): generate random plants on the map or use the default one.
173
- add_farmyards (bool): If True, regions of frarmyards will be added to the map
172
+ add_farmyards (bool): If True, regions of farmyards will be added to the map
174
173
  without corresponding fields.
174
+ base_price (int): base price for the farmland.
175
+ price_scale (int): scale for the price of the farmland.
176
+ add_grass (bool): if True, grass will be added to the map.
177
+ base_grass (tuple | str): base grass to be used on the map.
178
+ random_plants (bool): generate random plants on the map or use the default one.
179
+ fill_empty_farmlands (bool): if True, empty farmlands will be filled with grass.
180
+
175
181
  """
176
182
 
177
183
  farmland_margin: int = 0
@@ -181,11 +187,6 @@ class GRLESettings(SettingsModel):
181
187
  add_grass: bool = True
182
188
  base_grass: tuple | str = ("smallDenseMix", "meadow")
183
189
  random_plants: bool = True
184
- plants_island_minimum_size: int = 10
185
- plants_island_maximum_size: int = 200
186
- plants_island_vertex_count: int = 30
187
- plants_island_rounding_radius: int = 15
188
- plants_island_percent: int = 100
189
190
  fill_empty_farmlands: bool = False
190
191
 
191
192
 
@@ -193,13 +194,23 @@ class I3DSettings(SettingsModel):
193
194
  """Represents the advanced settings for I3D component.
194
195
 
195
196
  Attributes:
197
+ add_trees (bool): add trees to the map.
196
198
  forest_density (int): density of the forest (distance between trees).
199
+ trees_relative_shift (int): relative shift of the trees.
200
+ spline_density (int): the number of extra points that will be added between each two
201
+ existing points.
202
+ add_reversed_splines (bool): if True, reversed splines will be added to the map.
203
+ field_splines (bool): if True, splines will be added to the fields.
197
204
  """
198
205
 
199
206
  add_trees: bool = True
200
207
  forest_density: int = 10
201
208
  trees_relative_shift: int = 20
202
209
 
210
+ spline_density: int = 2
211
+ add_reversed_splines: bool = False
212
+ field_splines: bool = False
213
+
203
214
 
204
215
  class TextureSettings(SettingsModel):
205
216
  """Represents the advanced settings for texture component.
@@ -217,19 +228,6 @@ class TextureSettings(SettingsModel):
217
228
  use_precise_tags: bool = False
218
229
 
219
230
 
220
- class SplineSettings(SettingsModel):
221
- """Represents the advanced settings for spline component.
222
-
223
- Attributes:
224
- spline_density (int): the number of extra points that will be added between each two
225
- existing points.
226
- """
227
-
228
- spline_density: int = 2
229
- add_reversed_splines: bool = False
230
- field_splines: bool = False
231
-
232
-
233
231
  class SatelliteSettings(SettingsModel):
234
232
  """Represents the advanced settings for satellite component.
235
233
 
@@ -239,5 +237,4 @@ class SatelliteSettings(SettingsModel):
239
237
  """
240
238
 
241
239
  download_images: bool = False
242
- satellite_margin: int = 0
243
240
  zoom_level: int = 16
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: maps4fs
3
- Version: 1.9.54
3
+ Version: 2.0.1
4
4
  Summary: Generate map templates for Farming Simulator from real places.
5
5
  Author-email: iwatkot <iwatkot@gmail.com>
6
6
  License: Apache License 2.0
@@ -28,7 +28,7 @@ Requires-Dist: tqdm
28
28
  Requires-Dist: scipy
29
29
  Dynamic: license-file
30
30
 
31
- ⚠️ Learn more about the upcoming 2.0 changes in the [migration guide](docs/migration.md).
31
+ ⚠️ Learn more about the 2.0 changes in the [migration guide](docs/migration.md).
32
32
  ⚠️ Some components are deprecated and there are major changes in the project structure.
33
33
 
34
34
 
@@ -624,16 +624,8 @@ You can also apply some advanced settings to the map generation process.<br>
624
624
 
625
625
  - Water blurriness - used to make the water surface smoother. The higher the value, the more flat surface of the water planes will be. However, too high values can lead to the water planes mesh not matching the terrain.
626
626
 
627
- - Resize factor - the factor by which the background terrain will be resized. It will be used as 1 / resize_factor while generating the models. Which means that the larger the value the more the terrain will be resized. The lowest value is 1, in this case background terrain will not be resized. Note, than low values will lead to long processing and enormous size of the obj files.
628
-
629
627
  - Remove center - if enabled, the playable region (map terrain) will be removed from the background terrain. By default, it's set to True.
630
628
 
631
- - Apply decimation - if enabled, the mesh will be simplified to reduce the number of faces.
632
-
633
- - Decimation percent - the target percentage of decimation. The higher the value, the more simplified the mesh will be. Note, that high values will break the 3D model entirely.
634
-
635
- - Decimation agression - the aggression of the decimation. The higher the value, the more aggressive the
636
- decimation will be, which means the higher it will affect the geometry. It's not recommended to make it higher than the default value, otherwise the background terrain will not match the map terrain.
637
629
 
638
630
  ### GRLE Advanced settings
639
631
 
@@ -651,16 +643,6 @@ You can also apply some advanced settings to the map generation process.<br>
651
643
 
652
644
  - Base grass - you can select which plant will be used as a base grass on the map.
653
645
 
654
- - Plants island minimum size - when random plants are enabled, the generator will add islands of differents plants to the map and choose the random size of those island between the minimum and maximum values. This one is the minimum size of the island in meters.
655
-
656
- - Plants island maximum size - it's the same as above, but for the maximum size of the island in meters.
657
-
658
- - Plants island vertex count - the number of vertices in the island. The higher the value, the more detailed the island will be. Note, that high values will turn the smoothed island into geometric madness.
659
-
660
- - Plants insland rounding radius - used to round the vertices of the island. The higher the value, the more rounded the island will be.
661
-
662
- - Plants island percent - defines the relation between the map size and the number of islands of plants. For example, if set to 100% for map size of 2048 will be added 2048 islands of plants.
663
-
664
646
  - Fill empty farmlands - if enabled, the empty (zero value) pixels of the farmlands image will be replaces with the value of 255.
665
647
 
666
648
  ### I3D Advanced settings
@@ -671,6 +653,10 @@ You can also apply some advanced settings to the map generation process.<br>
671
653
 
672
654
  - 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.
673
655
 
656
+ - Splines density - number of points, which will be added (interpolate) between each pair of existing points. The higher the value, the denser the spline will be. It can smooth the splines, but high values can in opposite make the splines look unnatural.
657
+ - Add reversed splines - if enabled, the tool will add the reversed copies of the splines. It can be useful if you want to have the splines on both directions. By default, it's set to False.
658
+ - Field splines - if enabled, the tool will generate the splines around the fields. It may not work if the field appears on the map only partially, e.g., when the part of the field is outside the map area. By default, it's set to False.
659
+
674
660
  ### Texture Advanced settings
675
661
 
676
662
  - 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.
@@ -683,10 +669,6 @@ You can also apply some advanced settings to the map generation process.<br>
683
669
 
684
670
  - Use precise tags - if enabled, the tool will use the precise tags from the texture schema and will ignore basic tags specified for the texture. In the default schema being used for specific types of forests: broadleaved, needleleaved, mixed, and so on. Note, that if it's enabled and the object does not have the precise tag, it will not be drawn on the map. By default, it's set to False.
685
671
 
686
- ### Splines Advanced settings
687
-
688
- - Splines density - number of points, which will be added (interpolate) between each pair of existing points. The higher the value, the denser the spline will be. It can smooth the splines, but high values can in opposite make the splines look unnatural.
689
- - Add reversed splines - if enabled, the tool will add the reversed copies of the splines. It can be useful if you want to have the splines on both directions. By default, it's set to False.
690
672
 
691
673
  ### Satellite Advanced settings
692
674
 
@@ -2,23 +2,23 @@ maps4fs/__init__.py,sha256=EGvLVoRpSt4jITswsGbe1ISVmGAZAMQJcBmTwtyuVxI,335
2
2
  maps4fs/logger.py,sha256=WDfR14hxqy8b6xtwL6YIu2LGzFO1sbt0LxMgfsDTOkA,865
3
3
  maps4fs/generator/__init__.py,sha256=zZMLEkGzb4z0xql650gOtGSvcgX58DnJ2yN3vC2daRk,43
4
4
  maps4fs/generator/game.py,sha256=g8lMHuuRRmJLSDsQTAMv8p_-qntYMiZKnAqn7ru96i0,11645
5
- maps4fs/generator/map.py,sha256=3q9S4tYWeRaYO_ENLD_80towAEnIVn11_9mXNsqN6eg,13964
5
+ maps4fs/generator/map.py,sha256=XEY2wYWl7ABcJ2i1-05t-4mduuGYhE07nscoA_7va_g,16288
6
6
  maps4fs/generator/qgis.py,sha256=Es8hLuqN_KH8lDfnJE6He2rWYbAKJ3RGPn-o87S6CPI,6116
7
- maps4fs/generator/settings.py,sha256=e_wAJy69QoEmYgXOsoNc1Hp115FNcLQBzrrv-o8Pzd8,7522
7
+ maps4fs/generator/settings.py,sha256=8UbET3X1IbA7D4DX5HesZ7ijXLdzCCp7B3p3mFCZs8s,7429
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=AfbgNUai3tvpMe8lYmfiaM1n51ssunECy4gRglihumU,24032
10
+ maps4fs/generator/component/background.py,sha256=9CyT5MoQWILFwEGZThZnn3l1UEDI-5Sg0-VUV9TT240,23722
11
11
  maps4fs/generator/component/config.py,sha256=IP530sapLofFskSnBEB96G0aUSd6Sno0G9ET3ca0ZOQ,3696
12
12
  maps4fs/generator/component/dem.py,sha256=Bvm3ViA6PpR7RXRAHBj5rgYB9PWy55Qj6PhTMv6dJRI,11953
13
- maps4fs/generator/component/grle.py,sha256=ao3-4B4QUVl0XgOmYAj9L48KGaZtCZRTB1BqYgdAJIc,19613
14
- maps4fs/generator/component/i3d.py,sha256=1NlcK-7TmnF6zYa-Gi6whAwK2z6HPmWert7MLbzJiqI,24471
13
+ maps4fs/generator/component/grle.py,sha256=5ftHSg8FwSkabVlStlfG_erGZSOrilU-RUvCobfT1Yw,19539
14
+ maps4fs/generator/component/i3d.py,sha256=5703ntFt7EQKFpQWQN5ST_bM08akV3rpSwKZDvkFp6w,24462
15
15
  maps4fs/generator/component/layer.py,sha256=-br4gAGcGeBNb3ldch9XFEK0lhXqb1IbArhFB5Owu54,6186
16
- maps4fs/generator/component/satellite.py,sha256=47V9aPbv7k-2C-PsoXd33EO92yavFzB6_MRCd7fLP6k,5042
16
+ maps4fs/generator/component/satellite.py,sha256=OsxoNOCgkUtRzL7Geuqubsf6uoKXAIN8jQvrJ7IFeAI,4958
17
17
  maps4fs/generator/component/texture.py,sha256=Nc_oOHX3b4vJm8FnNOn3W4EQGFkW0zW0rGzO_0nTJMM,33392
18
18
  maps4fs/generator/component/base/__init__.py,sha256=zZMLEkGzb4z0xql650gOtGSvcgX58DnJ2yN3vC2daRk,43
19
19
  maps4fs/generator/component/base/component.py,sha256=HeNDaToKrS6OLeJepKZA7iQzZQDYy-9QRtv1A73Ire0,22090
20
20
  maps4fs/generator/component/base/component_image.py,sha256=2NYJgCU8deHl7O2FYFYk38WKZVJygFoc2gjBXwH6vjM,5970
21
- maps4fs/generator/component/base/component_mesh.py,sha256=BsxS5NlUK2hLhksgCwSoMK-00GNAwK2fYGpgb3R4n1w,9396
21
+ maps4fs/generator/component/base/component_mesh.py,sha256=7CfaEpfj_4P5LfAjFT4L76pTokqf6zmla9__sQNLUpA,8587
22
22
  maps4fs/generator/component/base/component_xml.py,sha256=V9pGUvHh6UF6BP0qFARqDq9vquoAgq1zJqhOgBoeS_Y,3983
23
23
  maps4fs/generator/dtm/__init__.py,sha256=CzK8ZdLU5Iv7DrwK5hIQ41zVsLRFgZO-IuRxf-NCVqg,1516
24
24
  maps4fs/generator/dtm/arctic.py,sha256=LSivLLjtd6TJUaPYvgSYQ4KalMTaY58zFvwivSh45uM,2587
@@ -50,12 +50,8 @@ maps4fs/generator/dtm/usgs_wcs.py,sha256=X8VxdhyH0-EciGE_X-KgrAM6sVLTGssYIhtebOj
50
50
  maps4fs/generator/dtm/utils.py,sha256=I-wUSA_J85Xbt8sZCZAVKHSIcrMj5Ng-0adtPVhVmk0,2315
51
51
  maps4fs/generator/dtm/base/wcs.py,sha256=lQAp_gVz9_XUmtyobJkskiefQpuJH4o1Vwb3CSQ0lQA,2510
52
52
  maps4fs/generator/dtm/base/wms.py,sha256=6Va2UMhg_s0TMOfMhxrPbsiAPiw6-vXBglnaij032I0,2264
53
- maps4fs/toolbox/__init__.py,sha256=zZMLEkGzb4z0xql650gOtGSvcgX58DnJ2yN3vC2daRk,43
54
- maps4fs/toolbox/background.py,sha256=RB0pHuRyfnDuqYtO2gUypfPm4G5cYRFdT2W-DG49zy0,2427
55
- maps4fs/toolbox/custom_osm.py,sha256=fjVSl9Ztc8_q0DxgNkhM7tE0Y-XpX8xcGGW-Tunp0R8,2518
56
- maps4fs/toolbox/dem.py,sha256=mbn3ZqMRhhYmzgssm2CGvg6aa89MUBOJPq6QyE54OLY,4191
57
- maps4fs-1.9.54.dist-info/licenses/LICENSE.md,sha256=pTKD_oUexcn-yccFCTrMeLkZy0ifLRa-VNcDLqLZaIw,10749
58
- maps4fs-1.9.54.dist-info/METADATA,sha256=GjTIUeYYs-VHnduS-CmoBq8jYPvysEREcVZ-r3feHvs,49397
59
- maps4fs-1.9.54.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
60
- maps4fs-1.9.54.dist-info/top_level.txt,sha256=Ue9DSRlejRQRCaJueB0uLcKrWwsEq9zezfv5dI5mV1M,8
61
- maps4fs-1.9.54.dist-info/RECORD,,
53
+ maps4fs-2.0.1.dist-info/licenses/LICENSE.md,sha256=pTKD_oUexcn-yccFCTrMeLkZy0ifLRa-VNcDLqLZaIw,10749
54
+ maps4fs-2.0.1.dist-info/METADATA,sha256=gN6kKoaRpc8RY63LVOEVud4zXG3tDqqd3NFHdUu4lyg,47725
55
+ maps4fs-2.0.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
56
+ maps4fs-2.0.1.dist-info/top_level.txt,sha256=Ue9DSRlejRQRCaJueB0uLcKrWwsEq9zezfv5dI5mV1M,8
57
+ maps4fs-2.0.1.dist-info/RECORD,,
@@ -1 +0,0 @@
1
- # pylint: disable=missing-module-docstring
@@ -1,70 +0,0 @@
1
- """This module contains functions to work with the background terrain of the map."""
2
-
3
- import warnings
4
-
5
- import cv2
6
- import numpy as np
7
- import trimesh # type: ignore
8
-
9
-
10
- # pylint: disable=R0914
11
- def plane_from_np(
12
- dem_data: np.ndarray,
13
- resize_factor: float,
14
- simplify_factor: int,
15
- save_path: str,
16
- ) -> None:
17
- """Generates a 3D obj file based on DEM data.
18
-
19
- Arguments:
20
- dem_data (np.ndarray) -- The DEM data as a numpy array.
21
- resize_factor (float) -- The factor by which the DEM data will be resized. Bigger values
22
- will result in a bigger mesh.
23
- simplify_factor (int) -- The factor by which the mesh will be simplified. Bigger values
24
- will result in a simpler mesh.
25
- save_path (str) -- The path to save the obj file.
26
- """
27
- warnings.warn(
28
- "The 'plane_from_np' function is deprecated and will be removed in maps4fs 2.0. "
29
- "Use 'trimesh' directly instead or maps4fs.generator.component.base.component_mesh."
30
- "MeshComponent.mesh_from_np' instead.",
31
- DeprecationWarning,
32
- stacklevel=2,
33
- )
34
- dem_data = cv2.resize(dem_data, (0, 0), fx=resize_factor, fy=resize_factor)
35
-
36
- # Invert the height values.
37
- dem_data = dem_data.max() - dem_data
38
-
39
- rows, cols = dem_data.shape
40
- x = np.linspace(0, cols - 1, cols)
41
- y = np.linspace(0, rows - 1, rows)
42
- x, y = np.meshgrid(x, y)
43
- z = dem_data
44
-
45
- vertices = np.column_stack([x.ravel(), y.ravel(), z.ravel()])
46
- faces = []
47
-
48
- for i in range(rows - 1):
49
- for j in range(cols - 1):
50
- top_left = i * cols + j
51
- top_right = top_left + 1
52
- bottom_left = top_left + cols
53
- bottom_right = bottom_left + 1
54
-
55
- faces.append([top_left, bottom_left, bottom_right])
56
- faces.append([top_left, bottom_right, top_right])
57
-
58
- faces = np.array(faces) # type: ignore
59
- mesh = trimesh.Trimesh(vertices=vertices, faces=faces)
60
-
61
- # Apply rotation: 180 degrees around Y-axis and Z-axis
62
- rotation_matrix_y = trimesh.transformations.rotation_matrix(np.pi, [0, 1, 0])
63
- rotation_matrix_z = trimesh.transformations.rotation_matrix(np.pi, [0, 0, 1])
64
- mesh.apply_transform(rotation_matrix_y)
65
- mesh.apply_transform(rotation_matrix_z)
66
-
67
- # Simplify the mesh to reduce the number of faces.
68
- mesh = mesh.simplify_quadric_decimation(face_count=len(faces) // simplify_factor)
69
-
70
- mesh.export(save_path)
@@ -1,82 +0,0 @@
1
- """This module contains functions to work with custom OSM files."""
2
-
3
- import json
4
- import warnings
5
- from xml.etree import ElementTree as ET
6
-
7
- import osmnx as ox
8
- from osmnx._errors import InsufficientResponseError
9
-
10
- from maps4fs.generator.game import FS25
11
-
12
-
13
- def check_osm_file(file_path: str) -> bool:
14
- """Tries to read the OSM file using OSMnx and returns True if the file is valid,
15
- False otherwise.
16
-
17
- Arguments:
18
- file_path (str): Path to the OSM file.
19
-
20
- Returns:
21
- bool: True if the file is valid, False otherwise.
22
- """
23
- warnings.warn(
24
- "The 'check_osm_file' function is deprecated and will be removed in maps4fs 2.0. "
25
- "The feature will be directly integrated into the maps4fs generator. "
26
- "And will not be available as a separate function.",
27
- DeprecationWarning,
28
- stacklevel=2,
29
- )
30
- with open(FS25().texture_schema, encoding="utf-8") as f:
31
- schema = json.load(f)
32
-
33
- tags = []
34
- for element in schema:
35
- element_tags = element.get("tags")
36
- if element_tags:
37
- tags.append(element_tags)
38
-
39
- for tag in tags:
40
- try:
41
- ox.features_from_xml(file_path, tags=tag)
42
- except InsufficientResponseError:
43
- continue
44
- except Exception: # pylint: disable=W0718
45
- return False
46
- return True
47
-
48
-
49
- def fix_osm_file(input_file_path: str, output_file_path: str) -> tuple[bool, int]:
50
- """Fixes the OSM file by removing all the <relation> nodes and all the nodes with
51
- action='delete'.
52
-
53
- Arguments:
54
- input_file_path (str): Path to the input OSM file.
55
- output_file_path (str): Path to the output OSM file.
56
-
57
- Returns:
58
- tuple[bool, int]: A tuple containing the result of the check_osm_file function
59
- and the number of fixed errors.
60
- """
61
- warnings.warn(
62
- "The 'fix_osm_file' function is deprecated and will be removed in maps4fs 2.0. "
63
- "The feature will be directly integrated into the maps4fs generator. "
64
- "And will not be available as a separate function.",
65
- DeprecationWarning,
66
- stacklevel=2,
67
- )
68
- broken_entries = ["relation", ".//*[@action='delete']"]
69
-
70
- tree = ET.parse(input_file_path)
71
- root = tree.getroot()
72
-
73
- fixed_errors = 0
74
- for entry in broken_entries:
75
- for element in root.findall(entry):
76
- root.remove(element)
77
- fixed_errors += 1
78
-
79
- tree.write(output_file_path)
80
- result = check_osm_file(output_file_path)
81
-
82
- return result, fixed_errors
maps4fs/toolbox/dem.py DELETED
@@ -1,131 +0,0 @@
1
- """This module contains functions for working with Digital Elevation Models (DEMs)."""
2
-
3
- import os
4
- import warnings
5
-
6
- import rasterio # type: ignore
7
- from pyproj import Transformer
8
- from rasterio.io import DatasetReader # type: ignore
9
- from rasterio.windows import from_bounds # type: ignore
10
-
11
-
12
- def read_geo_tiff(file_path: str) -> DatasetReader:
13
- """Read a GeoTIFF file and return the DatasetReader object.
14
-
15
- Arguments:
16
- file_path (str): The path to the GeoTIFF file.
17
-
18
- Raises:
19
- FileNotFoundError: If the file is not found.
20
- RuntimeError: If there is an error reading the file.
21
-
22
- Returns:
23
- DatasetReader: The DatasetReader object for the GeoTIFF file.
24
- """
25
- warnings.warn(
26
- "The 'read_geo_tiff' function is deprecated and will be removed in maps4fs 2.0. "
27
- "Use 'rasterio.open' directly instead.",
28
- DeprecationWarning,
29
- stacklevel=2,
30
- )
31
- if not os.path.isfile(file_path):
32
- raise FileNotFoundError(f"File not found: {file_path}")
33
-
34
- try:
35
- src = rasterio.open(file_path)
36
- except Exception as e:
37
- raise RuntimeError(f"Error reading file: {file_path}") from e
38
-
39
- if not src.bounds or not src.crs:
40
- raise RuntimeError(
41
- f"Can not read bounds or CRS from file: {file_path}. "
42
- f"Bounds: {src.bounds}, CRS: {src.crs}"
43
- )
44
-
45
- return src
46
-
47
-
48
- def get_geo_tiff_bbox(
49
- src: DatasetReader, dst_crs: str | None = "EPSG:4326"
50
- ) -> tuple[float, float, float, float]:
51
- """Return the bounding box of a GeoTIFF file in the destination CRS.
52
-
53
- Arguments:
54
- src (DatasetReader): The DatasetReader object for the GeoTIFF file.
55
- dst_crs (str, optional): The destination CRS. Defaults to "EPSG:4326".
56
-
57
- Returns:
58
- tuple[float, float, float, float]: The bounding box in the destination CRS
59
- as (north, south, east, west).
60
- """
61
- warnings.warn(
62
- "The 'get_geo_tiff_bbox' function is deprecated and will be removed in maps4fs 2.0. "
63
- "Use 'rasterio' methods directly instead.",
64
- DeprecationWarning,
65
- stacklevel=2,
66
- )
67
- left, bottom, right, top = src.bounds
68
-
69
- transformer = Transformer.from_crs(src.crs, dst_crs, always_xy=True)
70
-
71
- east, north = transformer.transform(left, top)
72
- west, south = transformer.transform(right, bottom)
73
-
74
- return north, south, east, west
75
-
76
-
77
- # pylint: disable=R0914
78
- def extract_roi(file_path: str, bbox: tuple[float, float, float, float]) -> str:
79
- """Extract a region of interest (ROI) from a GeoTIFF file and save it as a new file.
80
-
81
- Arguments:
82
- file_path (str): The path to the GeoTIFF file.
83
- bbox (tuple[float, float, float, float]): The bounding box of the region of interest
84
- as (north, south, east, west).
85
-
86
- Raises:
87
- RuntimeError: If there is no data in the selected region.
88
-
89
- Returns:
90
- str: The path to the new GeoTIFF file containing the extracted ROI.
91
- """
92
- warnings.warn(
93
- "The 'extract_roi' function is deprecated and will be removed in maps4fs 2.0. "
94
- "Use 'rasterio' methods directly instead.",
95
- DeprecationWarning,
96
- stacklevel=2,
97
- )
98
- with rasterio.open(file_path) as src:
99
- transformer = Transformer.from_crs("EPSG:4326", src.crs, always_xy=True)
100
- north, south, east, west = bbox
101
-
102
- left, bottom = transformer.transform(west, south)
103
- right, top = transformer.transform(east, north)
104
-
105
- window = from_bounds(left, bottom, right, top, src.transform)
106
- data = src.read(window=window)
107
-
108
- if not data.size > 0:
109
- raise RuntimeError("No data in the selected region.")
110
-
111
- base_name = os.path.basename(file_path).split(".")[0]
112
- dir_name = os.path.dirname(file_path)
113
-
114
- output_name = f"{base_name}_{north}_{south}_{east}_{west}.tif"
115
-
116
- output_path = os.path.join(dir_name, output_name)
117
-
118
- with rasterio.open(
119
- output_path,
120
- "w",
121
- driver="GTiff",
122
- height=data.shape[1],
123
- width=data.shape[2],
124
- count=data.shape[0],
125
- dtype=data.dtype,
126
- crs=src.crs,
127
- transform=src.window_transform(window),
128
- ) as dst:
129
- dst.write(data)
130
-
131
- return output_path