maps4fs 1.2.4__tar.gz → 1.4.1__tar.gz

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.
Files changed (29) hide show
  1. {maps4fs-1.2.4 → maps4fs-1.4.1}/PKG-INFO +36 -17
  2. {maps4fs-1.2.4 → maps4fs-1.4.1}/README.md +34 -16
  3. maps4fs-1.4.1/maps4fs/__init__.py +13 -0
  4. {maps4fs-1.2.4 → maps4fs-1.4.1}/maps4fs/generator/background.py +27 -29
  5. {maps4fs-1.2.4 → maps4fs-1.4.1}/maps4fs/generator/component.py +80 -24
  6. {maps4fs-1.2.4 → maps4fs-1.4.1}/maps4fs/generator/config.py +1 -1
  7. {maps4fs-1.2.4 → maps4fs-1.4.1}/maps4fs/generator/dem.py +11 -13
  8. {maps4fs-1.2.4 → maps4fs-1.4.1}/maps4fs/generator/game.py +1 -1
  9. {maps4fs-1.2.4 → maps4fs-1.4.1}/maps4fs/generator/grle.py +13 -14
  10. {maps4fs-1.2.4 → maps4fs-1.4.1}/maps4fs/generator/i3d.py +169 -35
  11. maps4fs-1.4.1/maps4fs/generator/map.py +337 -0
  12. {maps4fs-1.2.4 → maps4fs-1.4.1}/maps4fs/generator/texture.py +94 -32
  13. {maps4fs-1.2.4 → maps4fs-1.4.1}/maps4fs.egg-info/PKG-INFO +36 -17
  14. {maps4fs-1.2.4 → maps4fs-1.4.1}/maps4fs.egg-info/requires.txt +1 -0
  15. {maps4fs-1.2.4 → maps4fs-1.4.1}/pyproject.toml +2 -1
  16. {maps4fs-1.2.4 → maps4fs-1.4.1}/tests/test_generator.py +54 -57
  17. maps4fs-1.2.4/maps4fs/__init__.py +0 -4
  18. maps4fs-1.2.4/maps4fs/generator/map.py +0 -160
  19. {maps4fs-1.2.4 → maps4fs-1.4.1}/LICENSE.md +0 -0
  20. {maps4fs-1.2.4 → maps4fs-1.4.1}/maps4fs/generator/__init__.py +0 -0
  21. {maps4fs-1.2.4 → maps4fs-1.4.1}/maps4fs/generator/qgis.py +0 -0
  22. {maps4fs-1.2.4 → maps4fs-1.4.1}/maps4fs/logger.py +0 -0
  23. {maps4fs-1.2.4 → maps4fs-1.4.1}/maps4fs/toolbox/__init__.py +0 -0
  24. {maps4fs-1.2.4 → maps4fs-1.4.1}/maps4fs/toolbox/background.py +0 -0
  25. {maps4fs-1.2.4 → maps4fs-1.4.1}/maps4fs/toolbox/dem.py +0 -0
  26. {maps4fs-1.2.4 → maps4fs-1.4.1}/maps4fs.egg-info/SOURCES.txt +0 -0
  27. {maps4fs-1.2.4 → maps4fs-1.4.1}/maps4fs.egg-info/dependency_links.txt +0 -0
  28. {maps4fs-1.2.4 → maps4fs-1.4.1}/maps4fs.egg-info/top_level.txt +0 -0
  29. {maps4fs-1.2.4 → maps4fs-1.4.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: maps4fs
3
- Version: 1.2.4
3
+ Version: 1.4.1
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
@@ -22,6 +22,7 @@ Requires-Dist: trimesh
22
22
  Requires-Dist: imageio
23
23
  Requires-Dist: tifffile
24
24
  Requires-Dist: pympler
25
+ Requires-Dist: pydantic
25
26
 
26
27
  <div align="center" markdown>
27
28
  <a href="https://discord.gg/Sj5QKKyE42">
@@ -60,7 +61,8 @@ Requires-Dist: pympler
60
61
  [![Checked with mypy](https://www.mypy-lang.org/static/mypy_badge.svg)](https://mypy-lang.org/)
61
62
  [![Build Status](https://github.com/iwatkot/maps4fs/actions/workflows/checks.yml/badge.svg)](https://github.com/iwatkot/maps4fs/actions)
62
63
  [![Test Coverage](https://api.codeclimate.com/v1/badges/b922fd0a7188d37e61de/test_coverage)](https://codeclimate.com/github/iwatkot/maps4fs/test_coverage)
63
- [![GitHub Repo stars](https://img.shields.io/github/stars/iwatkot/maps4fs)](https://github.com/iwatkot/maps4fs/stargazers)
64
+ [![GitHub Repo stars](https://img.shields.io/github/stars/iwatkot/maps4fs)](https://github.com/iwatkot/maps4fs/stargazers)<br>
65
+ [![Lines of code](https://tokei.rs/b1/github/iwatkot/maps4fs)](https://github.com/iwatkot/maps4fs)
64
66
 
65
67
  </div>
66
68
 
@@ -71,7 +73,9 @@ Requires-Dist: pympler
71
73
  🌿 Automatically generates decorative foliage 🆕<br>
72
74
  🌲 Automatically generates forests 🆕<br>
73
75
  🌊 Automatically generates water planes 🆕<br>
76
+ 📈 Automatically generates splines 🆕<br>
74
77
  🌍 Based on real-world data from OpenStreetMap<br>
78
+ 🗺️ Supports [custom OSM maps](/docs/custom_osm.md)<br>
75
79
  🏞️ Generates height map using SRTM dataset<br>
76
80
  📦 Provides a ready-to-use map template for the Giants Editor<br>
77
81
  🚜 Supports Farming Simulator 22 and 25<br>
@@ -79,7 +83,6 @@ Requires-Dist: pympler
79
83
  📄 Generates scripts to download high-resolution satellite images from [QGIS](https://qgis.org/download/) in one click<br>
80
84
  📕 Detailed [documentation](/docs) and tutorials <br>
81
85
  🧰 Modder Toolbox to help you with various tasks <br>
82
-
83
86
  <p align="center">
84
87
  <img src="https://github.com/user-attachments/assets/cf8f5752-9c69-4018-bead-290f59ba6976"><br>
85
88
  🌎 Detailed terrain based on real-world data.<br><br>
@@ -93,6 +96,8 @@ Requires-Dist: pympler
93
96
  🌲 Automatically generates forests.<br><br>
94
97
  <img src="https://github.com/user-attachments/assets/cce7d4e0-cba2-4dd2-b22d-03137fb2e860"><br>
95
98
  🌊 Automatically generates water planes.<br><br>
99
+ <img src="https://github.com/user-attachments/assets/0b05b511-a595-48e7-a353-8298081314a4"><br>
100
+ 📈 Automatically generates splines.<br><br>
96
101
  <img src="https://github.com/user-attachments/assets/80e5923c-22c7-4dc0-8906-680902511f3a"><br>
97
102
  🗒️ True-to-life blueprints for fast and precise modding.<br><br>
98
103
  <img width="480" src="https://github.com/user-attachments/assets/1a8802d2-6a3b-4bfa-af2b-7c09478e199b"><br>
@@ -110,14 +115,7 @@ Requires-Dist: pympler
110
115
  ## Quick Start
111
116
  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>
112
117
  ### 🚜 For most users
113
- **Option 1:** Open the [maps4fs](https://maps4fs.streamlit.app) on StreamLit and generate a map template in a few clicks.<br>
114
- <i>Note, that StreamLit community hosting has some limitations, such as: <br>
115
- 1. Maximum map size is 4096x4096 meters. <br>
116
- 2. Advanced settings are disabled. <br>
117
- 3. Texture dissolving is disabled (they will look worse). </i><br>
118
-
119
- If you run the application locally, you won't have any of these limitations and will be able to generate maps of any size with any settings you want and nice looking textures.<br>
120
- So, jump to [Docker version](#option-2-docker-version) to launch the tool with one command and get the full experience.<br>
118
+ **Option 1:** Open the [maps4fs](https://maps4fs.xyz) and generate a map template in a few clicks.<br>
121
119
 
122
120
  ![Basic WebUI](https://github.com/user-attachments/assets/52f499cc-f28a-4da3-abef-0e818abe8dbe)
123
121
 
@@ -165,13 +163,13 @@ Don't know where to start? Don't worry, just follow this [step-by-step guide](do
165
163
 
166
164
  ## How-To-Run
167
165
 
168
- ### Option 1: StreamLit
169
- 🟢 Recommended for all users.
166
+ ### Option 1: Public version
167
+ 🟢 Recommended for all users.
170
168
  🛠️ Don't need to install anything.
171
- 🗺️ Supported map sizes: 2x2, 4x4 km.
172
- ⚙️ Advanced settings: disabled.
173
- 🖼️ Texture dissolving: disabled.
174
- Using the [StreamLit](https://maps4fs.streamlit.app) version of the tool is the easiest way to generate a map template. Just open the link and follow the instructions.
169
+ 🗺️ Supported map sizes: 2x2, 4x4, 8x8 km.
170
+ ⚙️ Advanced settings: enabled.
171
+ 🖼️ Texture dissolving: enabled.
172
+ 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.
175
173
  Note: due to CPU and RAM limitations of the hosting, the generation may take some time. If you need faster processing, use the [Docker version](#option-2-docker-version).<br>
176
174
 
177
175
  Using it is easy and doesn't require any guides. Enjoy!
@@ -488,6 +486,10 @@ You can also apply some advanced settings to the map generation process. Note th
488
486
 
489
487
  - Fields padding - this value (in meters) will be applied to each field, making it smaller. It's useful when the fields are too close to each other and you want to make them smaller. By default, it's set to 0.
490
488
 
489
+ - Texture dissolving - if enabled, the values from one layer will be splitted between different layers of texture, making it look more natural. By default, it's set to True. Can be turned of for faster processing.
490
+
491
+ - Skip drains - if enabled, the tool will not generate the drains and ditches on the map. By default, it's set to False. Use this if you don't need the drains on the map.
492
+
491
493
  ### Farmlands Advanced settings
492
494
 
493
495
  - Farmlands margin - this value (in meters) will be applied to each farmland, making it bigger. You can use the value to adjust how much the farmland should be bigger than the actual field. By default, it's set to 3.
@@ -498,6 +500,18 @@ You can also apply some advanced settings to the map generation process. Note th
498
500
 
499
501
  - Random plants - when adding decorative foliage, enabling this option will add different species of plants to the map. If unchecked only basic grass (smallDenseMix) will be added. Defaults to True.
500
502
 
503
+ ### Background terrain Advanced settings
504
+
505
+ - Generate background - if enabled, the obj files for the background terrain will be generated. You can turn it off if you already have those files or don't need them. By default, it's set to True.
506
+
507
+ - Generate water - if enabled, the water planes obj files will be generated. You can turn it off if you already have those files or don't need them. By default, it's set to True.
508
+
509
+ - 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.
510
+
511
+ ## Splines Advanced settings
512
+
513
+ - 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.
514
+
501
515
  ## Resources
502
516
  In this section, you'll find a list of the resources that you need to create a map for the Farming Simulator.<br>
503
517
  To create a basic map, you only need the Giants Editor. But if you want to create a background terrain - the world around the map, so it won't look like it's floating in the void - you also need Blender and the Blender Exporter Plugins. To create realistic textures for the background terrain, the QGIS is required to obtain high-resolution satellite images.<br>
@@ -522,3 +536,8 @@ But also, I want to thank the people who helped me with the project in some way,
522
536
  - [Ka5tis](https://github.com/Ka5tis) - for investigating the issue with a "spiky terrain" and finding a solution - changing the `DisplacementLayer` size to a higher value.
523
537
  - [Kalderone](https://www.youtube.com/@Kalderone_FS22) - for useful feedback, suggestions, expert advice on the map-making process and highlihting some important settings in the Giants Editor.
524
538
  - [OneSunnySunday](https://www.artstation.com/onesunnysunday) - for expert advice on Blender, help in processing background terrain, and compiling detailed tutorials on how to prepare the OBJ files for use in Giants Editor.
539
+ - [BFernaesds](https://github.com/BFernaesds) - for the manual tests of the app.
540
+ - [gamerdesigns](https://github.com/gamerdesigns) - for the manual tests of the app.
541
+ - [Tox3](https://github.com/Tox3) - for the manual tests of the app.
542
+ - [Lucandia](https://github.com/Lucandia) - for the awesome StreamLit [widget to preview STL files](https://github.com/Lucandia/streamlit_stl).
543
+ - [H4rdB4se](https://github.com/H4rdB4se) - for investigating the issue with custom OSM files and finding a proper way to work with the files in JOSM.
@@ -35,7 +35,8 @@
35
35
  [![Checked with mypy](https://www.mypy-lang.org/static/mypy_badge.svg)](https://mypy-lang.org/)
36
36
  [![Build Status](https://github.com/iwatkot/maps4fs/actions/workflows/checks.yml/badge.svg)](https://github.com/iwatkot/maps4fs/actions)
37
37
  [![Test Coverage](https://api.codeclimate.com/v1/badges/b922fd0a7188d37e61de/test_coverage)](https://codeclimate.com/github/iwatkot/maps4fs/test_coverage)
38
- [![GitHub Repo stars](https://img.shields.io/github/stars/iwatkot/maps4fs)](https://github.com/iwatkot/maps4fs/stargazers)
38
+ [![GitHub Repo stars](https://img.shields.io/github/stars/iwatkot/maps4fs)](https://github.com/iwatkot/maps4fs/stargazers)<br>
39
+ [![Lines of code](https://tokei.rs/b1/github/iwatkot/maps4fs)](https://github.com/iwatkot/maps4fs)
39
40
 
40
41
  </div>
41
42
 
@@ -46,7 +47,9 @@
46
47
  🌿 Automatically generates decorative foliage 🆕<br>
47
48
  🌲 Automatically generates forests 🆕<br>
48
49
  🌊 Automatically generates water planes 🆕<br>
50
+ 📈 Automatically generates splines 🆕<br>
49
51
  🌍 Based on real-world data from OpenStreetMap<br>
52
+ 🗺️ Supports [custom OSM maps](/docs/custom_osm.md)<br>
50
53
  🏞️ Generates height map using SRTM dataset<br>
51
54
  📦 Provides a ready-to-use map template for the Giants Editor<br>
52
55
  🚜 Supports Farming Simulator 22 and 25<br>
@@ -54,7 +57,6 @@
54
57
  📄 Generates scripts to download high-resolution satellite images from [QGIS](https://qgis.org/download/) in one click<br>
55
58
  📕 Detailed [documentation](/docs) and tutorials <br>
56
59
  🧰 Modder Toolbox to help you with various tasks <br>
57
-
58
60
  <p align="center">
59
61
  <img src="https://github.com/user-attachments/assets/cf8f5752-9c69-4018-bead-290f59ba6976"><br>
60
62
  🌎 Detailed terrain based on real-world data.<br><br>
@@ -68,6 +70,8 @@
68
70
  🌲 Automatically generates forests.<br><br>
69
71
  <img src="https://github.com/user-attachments/assets/cce7d4e0-cba2-4dd2-b22d-03137fb2e860"><br>
70
72
  🌊 Automatically generates water planes.<br><br>
73
+ <img src="https://github.com/user-attachments/assets/0b05b511-a595-48e7-a353-8298081314a4"><br>
74
+ 📈 Automatically generates splines.<br><br>
71
75
  <img src="https://github.com/user-attachments/assets/80e5923c-22c7-4dc0-8906-680902511f3a"><br>
72
76
  🗒️ True-to-life blueprints for fast and precise modding.<br><br>
73
77
  <img width="480" src="https://github.com/user-attachments/assets/1a8802d2-6a3b-4bfa-af2b-7c09478e199b"><br>
@@ -85,14 +89,7 @@
85
89
  ## Quick Start
86
90
  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>
87
91
  ### 🚜 For most users
88
- **Option 1:** Open the [maps4fs](https://maps4fs.streamlit.app) on StreamLit and generate a map template in a few clicks.<br>
89
- <i>Note, that StreamLit community hosting has some limitations, such as: <br>
90
- 1. Maximum map size is 4096x4096 meters. <br>
91
- 2. Advanced settings are disabled. <br>
92
- 3. Texture dissolving is disabled (they will look worse). </i><br>
93
-
94
- If you run the application locally, you won't have any of these limitations and will be able to generate maps of any size with any settings you want and nice looking textures.<br>
95
- So, jump to [Docker version](#option-2-docker-version) to launch the tool with one command and get the full experience.<br>
92
+ **Option 1:** Open the [maps4fs](https://maps4fs.xyz) and generate a map template in a few clicks.<br>
96
93
 
97
94
  ![Basic WebUI](https://github.com/user-attachments/assets/52f499cc-f28a-4da3-abef-0e818abe8dbe)
98
95
 
@@ -140,13 +137,13 @@ Don't know where to start? Don't worry, just follow this [step-by-step guide](do
140
137
 
141
138
  ## How-To-Run
142
139
 
143
- ### Option 1: StreamLit
144
- 🟢 Recommended for all users.
140
+ ### Option 1: Public version
141
+ 🟢 Recommended for all users.
145
142
  🛠️ Don't need to install anything.
146
- 🗺️ Supported map sizes: 2x2, 4x4 km.
147
- ⚙️ Advanced settings: disabled.
148
- 🖼️ Texture dissolving: disabled.
149
- Using the [StreamLit](https://maps4fs.streamlit.app) version of the tool is the easiest way to generate a map template. Just open the link and follow the instructions.
143
+ 🗺️ Supported map sizes: 2x2, 4x4, 8x8 km.
144
+ ⚙️ Advanced settings: enabled.
145
+ 🖼️ Texture dissolving: enabled.
146
+ 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.
150
147
  Note: due to CPU and RAM limitations of the hosting, the generation may take some time. If you need faster processing, use the [Docker version](#option-2-docker-version).<br>
151
148
 
152
149
  Using it is easy and doesn't require any guides. Enjoy!
@@ -463,6 +460,10 @@ You can also apply some advanced settings to the map generation process. Note th
463
460
 
464
461
  - Fields padding - this value (in meters) will be applied to each field, making it smaller. It's useful when the fields are too close to each other and you want to make them smaller. By default, it's set to 0.
465
462
 
463
+ - Texture dissolving - if enabled, the values from one layer will be splitted between different layers of texture, making it look more natural. By default, it's set to True. Can be turned of for faster processing.
464
+
465
+ - Skip drains - if enabled, the tool will not generate the drains and ditches on the map. By default, it's set to False. Use this if you don't need the drains on the map.
466
+
466
467
  ### Farmlands Advanced settings
467
468
 
468
469
  - Farmlands margin - this value (in meters) will be applied to each farmland, making it bigger. You can use the value to adjust how much the farmland should be bigger than the actual field. By default, it's set to 3.
@@ -473,6 +474,18 @@ You can also apply some advanced settings to the map generation process. Note th
473
474
 
474
475
  - Random plants - when adding decorative foliage, enabling this option will add different species of plants to the map. If unchecked only basic grass (smallDenseMix) will be added. Defaults to True.
475
476
 
477
+ ### Background terrain Advanced settings
478
+
479
+ - Generate background - if enabled, the obj files for the background terrain will be generated. You can turn it off if you already have those files or don't need them. By default, it's set to True.
480
+
481
+ - Generate water - if enabled, the water planes obj files will be generated. You can turn it off if you already have those files or don't need them. By default, it's set to True.
482
+
483
+ - 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.
484
+
485
+ ## Splines Advanced settings
486
+
487
+ - 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.
488
+
476
489
  ## Resources
477
490
  In this section, you'll find a list of the resources that you need to create a map for the Farming Simulator.<br>
478
491
  To create a basic map, you only need the Giants Editor. But if you want to create a background terrain - the world around the map, so it won't look like it's floating in the void - you also need Blender and the Blender Exporter Plugins. To create realistic textures for the background terrain, the QGIS is required to obtain high-resolution satellite images.<br>
@@ -497,3 +510,8 @@ But also, I want to thank the people who helped me with the project in some way,
497
510
  - [Ka5tis](https://github.com/Ka5tis) - for investigating the issue with a "spiky terrain" and finding a solution - changing the `DisplacementLayer` size to a higher value.
498
511
  - [Kalderone](https://www.youtube.com/@Kalderone_FS22) - for useful feedback, suggestions, expert advice on the map-making process and highlihting some important settings in the Giants Editor.
499
512
  - [OneSunnySunday](https://www.artstation.com/onesunnysunday) - for expert advice on Blender, help in processing background terrain, and compiling detailed tutorials on how to prepare the OBJ files for use in Giants Editor.
513
+ - [BFernaesds](https://github.com/BFernaesds) - for the manual tests of the app.
514
+ - [gamerdesigns](https://github.com/gamerdesigns) - for the manual tests of the app.
515
+ - [Tox3](https://github.com/Tox3) - for the manual tests of the app.
516
+ - [Lucandia](https://github.com/Lucandia) - for the awesome StreamLit [widget to preview STL files](https://github.com/Lucandia/streamlit_stl).
517
+ - [H4rdB4se](https://github.com/H4rdB4se) - for investigating the issue with custom OSM files and finding a proper way to work with the files in JOSM.
@@ -0,0 +1,13 @@
1
+ # pylint: disable=missing-module-docstring
2
+ from maps4fs.generator.game import Game
3
+ from maps4fs.generator.map import (
4
+ BackgroundSettings,
5
+ DEMSettings,
6
+ GRLESettings,
7
+ I3DSettings,
8
+ Map,
9
+ SettingsModel,
10
+ SplineSettings,
11
+ TextureSettings,
12
+ )
13
+ from maps4fs.logger import Logger
@@ -13,16 +13,10 @@ import numpy as np
13
13
  import trimesh # type: ignore
14
14
 
15
15
  from maps4fs.generator.component import Component
16
- from maps4fs.generator.dem import (
17
- DEFAULT_BLUR_RADIUS,
18
- DEFAULT_MULTIPLIER,
19
- DEFAULT_PLATEAU,
20
- DEM,
21
- )
16
+ from maps4fs.generator.dem import DEM
22
17
  from maps4fs.generator.texture import Texture
23
18
 
24
19
  DEFAULT_DISTANCE = 2048
25
- RESIZE_FACTOR = 1 / 8
26
20
  FULL_NAME = "FULL"
27
21
  FULL_PREVIEW_NAME = "PREVIEW"
28
22
  ELEMENTS = [FULL_NAME, FULL_PREVIEW_NAME]
@@ -46,8 +40,6 @@ class Background(Component):
46
40
  # pylint: disable=R0801
47
41
  def preprocess(self) -> None:
48
42
  """Registers the DEMs for the background terrain."""
49
- self.light_version = self.kwargs.get("light_version", False)
50
- self.water_depth = self.kwargs.get("water_depth", 0)
51
43
  self.stl_preview_path: str | None = None
52
44
  self.water_resources_path: str | None = None
53
45
 
@@ -65,11 +57,12 @@ class Background(Component):
65
57
  os.makedirs(self.background_directory, exist_ok=True)
66
58
  os.makedirs(self.water_directory, exist_ok=True)
67
59
 
68
- autoprocesses = [self.kwargs.get("auto_process", False), False]
60
+ autoprocesses = [self.map.dem_settings.auto_process, False]
69
61
  self.output_paths = [
70
62
  os.path.join(self.background_directory, f"{name}.png") for name in ELEMENTS
71
63
  ]
72
64
  self.not_substracted_path = os.path.join(self.background_directory, "not_substracted.png")
65
+ self.not_resized_path = os.path.join(self.background_directory, "not_resized.png")
73
66
 
74
67
  dems = []
75
68
 
@@ -83,13 +76,12 @@ class Background(Component):
83
76
  self.rotation,
84
77
  self.map_directory,
85
78
  self.logger,
86
- auto_process=autoprocess,
87
- blur_radius=self.kwargs.get("blur_radius", DEFAULT_BLUR_RADIUS),
88
- multiplier=self.kwargs.get("multiplier", DEFAULT_MULTIPLIER),
89
- plateau=self.kwargs.get("plateau", DEFAULT_PLATEAU),
90
79
  )
91
80
  dem.preprocess()
92
81
  dem.is_preview = self.is_preview(name) # type: ignore
82
+ if dem.is_preview: # type: ignore
83
+ dem.multiplier = 1
84
+ dem.auto_process = autoprocess
93
85
  dem.set_output_resolution((self.rotated_size, self.rotated_size))
94
86
  dem.set_dem_path(output_path)
95
87
  dems.append(dem)
@@ -117,8 +109,9 @@ class Background(Component):
117
109
  dem.process()
118
110
  if not dem.is_preview: # type: ignore
119
111
  shutil.copyfile(dem.dem_path, self.not_substracted_path)
112
+ self.cutout(dem.dem_path, save_path=self.not_resized_path)
120
113
 
121
- if self.water_depth:
114
+ if self.map.dem_settings.water_depth:
122
115
  self.subtraction()
123
116
 
124
117
  for dem in self.dems:
@@ -127,11 +120,10 @@ class Background(Component):
127
120
  if self.game.additional_dem_name is not None:
128
121
  self.make_copy(cutted_dem_path, self.game.additional_dem_name)
129
122
 
130
- if not self.light_version:
123
+ if self.map.background_settings.generate_background:
131
124
  self.generate_obj_files()
125
+ if self.map.background_settings.generate_water:
132
126
  self.generate_water_resources_obj()
133
- else:
134
- self.logger.info("Light version is enabled, obj files will not be generated.")
135
127
 
136
128
  def make_copy(self, dem_path: str, dem_name: str) -> None:
137
129
  """Copies DEM data to additional DEM file.
@@ -145,7 +137,7 @@ class Background(Component):
145
137
  additional_dem_path = os.path.join(dem_directory, dem_name)
146
138
 
147
139
  shutil.copyfile(dem_path, additional_dem_path)
148
- self.logger.info("Additional DEM data was copied to %s.", additional_dem_path)
140
+ self.logger.debug("Additional DEM data was copied to %s.", additional_dem_path)
149
141
 
150
142
  def info_sequence(self) -> dict[str, str | float | int]:
151
143
  """Returns a dictionary with information about the background terrain.
@@ -207,11 +199,12 @@ class Background(Component):
207
199
  self.plane_from_np(dem_data, save_path, is_preview=dem.is_preview) # type: ignore
208
200
 
209
201
  # pylint: disable=too-many-locals
210
- def cutout(self, dem_path: str) -> str:
202
+ def cutout(self, dem_path: str, save_path: str | None = None) -> str:
211
203
  """Cuts out the center of the DEM (the actual map) and saves it as a separate file.
212
204
 
213
205
  Arguments:
214
206
  dem_path (str): The path to the DEM file.
207
+ save_path (str, optional): The path where the cutout DEM file will be saved.
215
208
 
216
209
  Returns:
217
210
  str -- The path to the cutout DEM file.
@@ -226,6 +219,11 @@ class Background(Component):
226
219
  y2 = center[1] + half_size
227
220
  dem_data = dem_data[x1:x2, y1:y2]
228
221
 
222
+ if save_path:
223
+ cv2.imwrite(save_path, dem_data) # pylint: disable=no-member
224
+ self.logger.debug("Not resized DEM saved: %s", save_path)
225
+ return save_path
226
+
229
227
  output_size = self.map_size + 1
230
228
 
231
229
  main_dem_path = self.game.dem_file_path(self.map_directory)
@@ -261,11 +259,12 @@ class Background(Component):
261
259
  is_preview (bool, optional) -- If True, the preview mesh will be generated.
262
260
  include_zeros (bool, optional) -- If True, the mesh will include the zero height values.
263
261
  """
262
+ resize_factor = 1 / self.map.background_settings.resize_factor
264
263
  dem_data = cv2.resize( # pylint: disable=no-member
265
- dem_data, (0, 0), fx=RESIZE_FACTOR, fy=RESIZE_FACTOR
264
+ dem_data, (0, 0), fx=resize_factor, fy=resize_factor
266
265
  )
267
266
  self.logger.debug(
268
- "DEM data resized to shape: %s with factor: %s", dem_data.shape, RESIZE_FACTOR
267
+ "DEM data resized to shape: %s with factor: %s", dem_data.shape, resize_factor
269
268
  )
270
269
 
271
270
  # Invert the height values.
@@ -325,13 +324,13 @@ class Background(Component):
325
324
  self.mesh_to_stl(mesh)
326
325
  else:
327
326
  if not include_zeros:
328
- multiplier = self.kwargs.get("multiplier", DEFAULT_MULTIPLIER)
327
+ multiplier = self.map.dem_settings.multiplier
329
328
  if multiplier != 1:
330
329
  z_scaling_factor = 1 / multiplier
331
330
  else:
332
331
  z_scaling_factor = 1 / 2**5
333
332
  self.logger.debug("Z scaling factor: %s", z_scaling_factor)
334
- mesh.apply_scale([1 / RESIZE_FACTOR, 1 / RESIZE_FACTOR, z_scaling_factor])
333
+ mesh.apply_scale([1 / resize_factor, 1 / resize_factor, z_scaling_factor])
335
334
 
336
335
  mesh.export(save_path)
337
336
  self.logger.debug("Obj file saved: %s", save_path)
@@ -347,7 +346,7 @@ class Background(Component):
347
346
  preview_path = os.path.join(self.previews_directory, "background_dem.stl")
348
347
  mesh.export(preview_path)
349
348
 
350
- self.logger.info("STL file saved: %s", preview_path)
349
+ self.logger.debug("STL file saved: %s", preview_path)
351
350
 
352
351
  self.stl_preview_path = preview_path # pylint: disable=attribute-defined-outside-init
353
352
 
@@ -485,8 +484,7 @@ class Background(Component):
485
484
  rotation=self.rotation,
486
485
  map_directory=self.map_directory,
487
486
  logger=self.logger,
488
- light_version=self.light_version,
489
- custom_schema=background_layers,
487
+ texture_custom_schema=background_layers, # type: ignore
490
488
  )
491
489
 
492
490
  self.background_texture.preprocess()
@@ -509,7 +507,7 @@ class Background(Component):
509
507
 
510
508
  background_save_path = os.path.join(self.water_directory, "water_resources.png")
511
509
  cv2.imwrite(background_save_path, background_image)
512
- self.logger.info("Background texture saved: %s", background_save_path)
510
+ self.logger.debug("Background texture saved: %s", background_save_path)
513
511
  self.water_resources_path = background_save_path # pylint: disable=W0201
514
512
 
515
513
  def subtraction(self) -> None:
@@ -534,7 +532,7 @@ class Background(Component):
534
532
 
535
533
  # Create a mask where water_resources_image is 255 (or not 0)
536
534
  # Subtract water_depth from dem_image where mask is True
537
- dem_image[mask] = dem_image[mask] - self.water_depth
535
+ dem_image[mask] = dem_image[mask] - self.map.dem_settings.water_depth
538
536
 
539
537
  # Save the modified dem_image back to the output path
540
538
  cv2.imwrite(output_path, dem_image)
@@ -7,11 +7,11 @@ import os
7
7
  from copy import deepcopy
8
8
  from typing import TYPE_CHECKING, Any
9
9
 
10
- import cv2
10
+ import cv2 # type: ignore
11
11
  import osmnx as ox # type: ignore
12
12
  from pyproj import Transformer
13
13
  from shapely.affinity import rotate, translate # type: ignore
14
- from shapely.geometry import Polygon, box # type: ignore
14
+ from shapely.geometry import LineString, Polygon, box # type: ignore
15
15
 
16
16
  from maps4fs.generator.qgis import save_scripts
17
17
 
@@ -20,7 +20,7 @@ if TYPE_CHECKING:
20
20
  from maps4fs.generator.map import Map
21
21
 
22
22
 
23
- # pylint: disable=R0801, R0903, R0902, R0904
23
+ # pylint: disable=R0801, R0903, R0902, R0904, R0913, R0917
24
24
  class Component:
25
25
  """Base class for all map generation components.
26
26
 
@@ -46,7 +46,7 @@ class Component:
46
46
  rotation: int,
47
47
  map_directory: str,
48
48
  logger: Any = None,
49
- **kwargs, # pylint: disable=W0613, R0913, R0917
49
+ **kwargs: dict[str, Any],
50
50
  ):
51
51
  self.game = game
52
52
  self.map = map
@@ -58,6 +58,13 @@ class Component:
58
58
  self.logger = logger
59
59
  self.kwargs = kwargs
60
60
 
61
+ self.logger.info(
62
+ "Component %s initialized. Map size: %s, map rotated size: %s", # type: ignore
63
+ self.__class__.__name__,
64
+ self.map_size,
65
+ self.map_rotated_size,
66
+ )
67
+
61
68
  os.makedirs(self.previews_directory, exist_ok=True)
62
69
  os.makedirs(self.scripts_directory, exist_ok=True)
63
70
  os.makedirs(self.info_layers_directory, exist_ok=True)
@@ -331,62 +338,79 @@ class Component:
331
338
  return cs_x, cs_y
332
339
 
333
340
  # pylint: disable=R0914
334
- def fit_polygon_into_bounds(
335
- self, polygon_points: list[tuple[int, int]], margin: int = 0, angle: int = 0
341
+ def fit_object_into_bounds(
342
+ self,
343
+ polygon_points: list[tuple[int, int]] | None = None,
344
+ linestring_points: list[tuple[int, int]] | None = None,
345
+ margin: int = 0,
346
+ angle: int = 0,
336
347
  ) -> list[tuple[int, int]]:
337
348
  """Fits a polygon into the bounds of the map.
338
349
 
339
350
  Arguments:
340
351
  polygon_points (list[tuple[int, int]]): The points of the polygon.
352
+ linestring_points (list[tuple[int, int]]): The points of the linestring.
341
353
  margin (int, optional): The margin to add to the polygon. Defaults to 0.
342
354
  angle (int, optional): The angle to rotate the polygon by. Defaults to 0.
343
355
 
344
356
  Returns:
345
357
  list[tuple[int, int]]: The points of the polygon fitted into the map bounds.
346
358
  """
359
+ if polygon_points is None and linestring_points is None:
360
+ raise ValueError("Either polygon or linestring points must be provided.")
361
+
347
362
  min_x = min_y = 0
348
363
  max_x = max_y = self.map_size
349
364
 
350
- polygon = Polygon(polygon_points)
365
+ object_type = Polygon if polygon_points else LineString
366
+
367
+ # polygon = Polygon(polygon_points)
368
+ osm_object = object_type(polygon_points or linestring_points)
351
369
 
352
370
  if angle:
353
371
  center_x = center_y = self.map_rotated_size // 2
354
372
  self.logger.debug(
355
- "Rotating the polygon by %s degrees with center at %sx%s",
373
+ "Rotating the osm_object by %s degrees with center at %sx%s",
356
374
  angle,
357
375
  center_x,
358
376
  center_y,
359
377
  )
360
- polygon = rotate(polygon, -angle, origin=(center_x, center_y))
378
+ osm_object = rotate(osm_object, -angle, origin=(center_x, center_y))
361
379
  offset = (self.map_size / 2) - (self.map_rotated_size / 2)
362
- self.logger.debug("Translating the polygon by %s", offset)
363
- polygon = translate(polygon, xoff=offset, yoff=offset)
364
- self.logger.debug("Rotated and translated polygon.")
380
+ self.logger.debug("Translating the osm_object by %s", offset)
381
+ osm_object = translate(osm_object, xoff=offset, yoff=offset)
382
+ self.logger.debug("Rotated and translated the osm_object.")
365
383
 
366
- if margin:
367
- polygon = polygon.buffer(margin, join_style="mitre")
368
- if polygon.is_empty:
369
- raise ValueError("The polygon is empty after adding the margin.")
384
+ if margin and object_type is Polygon:
385
+ osm_object = osm_object.buffer(margin, join_style="mitre")
386
+ if osm_object.is_empty:
387
+ raise ValueError("The osm_object is empty after adding the margin.")
370
388
 
371
389
  # Create a bounding box for the map bounds
372
390
  bounds = box(min_x, min_y, max_x, max_y)
373
391
 
374
- # Intersect the polygon with the bounds to fit it within the map
392
+ # Intersect the osm_object with the bounds to fit it within the map
375
393
  try:
376
- fitted_polygon = polygon.intersection(bounds)
377
- self.logger.debug("Fitted the polygon into the bounds: %s", bounds)
394
+ fitted_osm_object = osm_object.intersection(bounds)
395
+ self.logger.debug("Fitted the osm_object into the bounds: %s", bounds)
378
396
  except Exception as e:
379
397
  raise ValueError( # pylint: disable=W0707
380
- f"Could not fit the polygon into the bounds: {e}"
398
+ f"Could not fit the osm_object into the bounds: {e}"
381
399
  )
382
400
 
383
- if not isinstance(fitted_polygon, Polygon):
384
- raise ValueError("The fitted polygon is not a valid polygon.")
401
+ if not isinstance(fitted_osm_object, object_type):
402
+ raise ValueError("The fitted osm_object is not valid (probably splitted into parts).")
385
403
 
386
404
  # Return the fitted polygon points
387
- as_list = list(fitted_polygon.exterior.coords)
405
+ if object_type is Polygon:
406
+ as_list = list(fitted_osm_object.exterior.coords)
407
+ elif object_type is LineString:
408
+ as_list = list(fitted_osm_object.coords)
409
+ else:
410
+ raise ValueError("The object type is not supported.")
411
+
388
412
  if not as_list:
389
- raise ValueError("The fitted polygon has no points.")
413
+ raise ValueError("The fitted osm_object has no points.")
390
414
  return as_list
391
415
 
392
416
  def get_infolayer_path(self, layer_name: str) -> str | None:
@@ -469,3 +493,35 @@ class Component:
469
493
  self.logger.debug("Shape of the cropped image: %s", cropped.shape)
470
494
 
471
495
  cv2.imwrite(output_path, cropped)
496
+
497
+ @staticmethod
498
+ def interpolate_points(
499
+ polyline: list[tuple[int, int]], num_points: int = 4
500
+ ) -> list[tuple[int, int]]:
501
+ """Receives a list of tuples, which represents a polyline. Add additional points
502
+ between the existing points to make the polyline smoother.
503
+
504
+ Arguments:
505
+ polyline (list[tuple[int, int]]): The list of points to interpolate.
506
+ num_points (int): The number of additional points to add between each pair of points.
507
+
508
+ Returns:
509
+ list[tuple[int, int]]: The list of points with additional points.
510
+ """
511
+ if not polyline or num_points < 1:
512
+ return polyline
513
+
514
+ interpolated_polyline = []
515
+ for i in range(len(polyline) - 1):
516
+ p1 = polyline[i]
517
+ p2 = polyline[i + 1]
518
+ interpolated_polyline.append(p1)
519
+ for j in range(1, num_points + 1):
520
+ new_point = (
521
+ p1[0] + (p2[0] - p1[0]) * j / (num_points + 1),
522
+ p1[1] + (p2[1] - p1[1]) * j / (num_points + 1),
523
+ )
524
+ interpolated_polyline.append((int(new_point[0]), int(new_point[1])))
525
+ interpolated_polyline.append(polyline[-1])
526
+
527
+ return interpolated_polyline
@@ -38,7 +38,7 @@ class Config(Component):
38
38
  self.logger.warning("Map XML file not found: %s.", self._map_xml_path)
39
39
  return
40
40
  tree = ET.parse(self._map_xml_path)
41
- self.logger.info("Map XML file loaded from: %s.", self._map_xml_path)
41
+ self.logger.debug("Map XML file loaded from: %s.", self._map_xml_path)
42
42
  root = tree.getroot()
43
43
  for map_elem in root.iter("map"):
44
44
  map_elem.set("width", str(self.map_size))