maps4fs 1.9.53__py3-none-any.whl → 2.0.0__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 +0 -4
- maps4fs/generator/component/base/component_mesh.py +2 -23
- maps4fs/generator/component/grle.py +6 -8
- maps4fs/generator/component/i3d.py +25 -12
- maps4fs/generator/component/satellite.py +2 -3
- maps4fs/generator/map.py +78 -6
- maps4fs/generator/settings.py +28 -30
- {maps4fs-1.9.53.dist-info → maps4fs-2.0.0.dist-info}/METADATA +6 -24
- {maps4fs-1.9.53.dist-info → maps4fs-2.0.0.dist-info}/RECORD +12 -16
- maps4fs/toolbox/__init__.py +0 -1
- maps4fs/toolbox/background.py +0 -70
- maps4fs/toolbox/custom_osm.py +0 -82
- maps4fs/toolbox/dem.py +0 -131
- {maps4fs-1.9.53.dist-info → maps4fs-2.0.0.dist-info}/WHEEL +0 -0
- {maps4fs-1.9.53.dist-info → maps4fs-2.0.0.dist-info}/licenses/LICENSE.md +0 -0
- {maps4fs-1.9.53.dist-info → maps4fs-2.0.0.dist-info}/top_level.txt +0 -0
@@ -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[::
|
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
|
-
|
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 *
|
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
|
-
|
391
|
-
|
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=
|
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=
|
400
|
+
rounding_radius=Parameters.PLANTS_ISLAND_ROUNDING_RADIUS,
|
403
401
|
)
|
404
402
|
if not polygon_points:
|
405
403
|
continue
|
@@ -119,6 +119,12 @@ class I3d(XMLComponent):
|
|
119
119
|
tree = self.get_tree(splines_i3d_path)
|
120
120
|
|
121
121
|
roads_polylines = self.get_infolayer_data(Parameters.TEXTURES, Parameters.ROADS_POLYLINES)
|
122
|
+
|
123
|
+
if self.map.i3d_settings.field_splines:
|
124
|
+
fields_polygons = self.get_infolayer_data(Parameters.TEXTURES, Parameters.FIELDS)
|
125
|
+
if isinstance(roads_polylines, list) and isinstance(fields_polygons, list):
|
126
|
+
roads_polylines.extend(fields_polygons)
|
127
|
+
|
122
128
|
if not roads_polylines:
|
123
129
|
self.logger.warning("Roads polylines data not found in textures info layer.")
|
124
130
|
return
|
@@ -152,8 +158,14 @@ class I3d(XMLComponent):
|
|
152
158
|
|
153
159
|
node_id = SPLINES_NODE_ID_STARTING_VALUE
|
154
160
|
for road_id, road_info in enumerate(roads_polylines, start=1):
|
155
|
-
|
156
|
-
|
161
|
+
if isinstance(road_info, dict):
|
162
|
+
points = road_info.get("points")
|
163
|
+
tags = road_info.get("tags")
|
164
|
+
is_field = False
|
165
|
+
else:
|
166
|
+
points = road_info
|
167
|
+
tags = "field"
|
168
|
+
is_field = True
|
157
169
|
|
158
170
|
try:
|
159
171
|
fitted_road = self.fit_object_into_bounds(
|
@@ -168,11 +180,11 @@ class I3d(XMLComponent):
|
|
168
180
|
continue
|
169
181
|
|
170
182
|
fitted_road = self.interpolate_points(
|
171
|
-
fitted_road, num_points=self.map.
|
183
|
+
fitted_road, num_points=self.map.i3d_settings.spline_density
|
172
184
|
)
|
173
185
|
fitted_roads = [(fitted_road, "original")]
|
174
186
|
|
175
|
-
if self.map.
|
187
|
+
if self.map.i3d_settings.add_reversed_splines:
|
176
188
|
reversed_fitted_road = fitted_road[::-1]
|
177
189
|
fitted_roads.append((reversed_fitted_road, "reversed"))
|
178
190
|
|
@@ -208,15 +220,16 @@ class I3d(XMLComponent):
|
|
208
220
|
|
209
221
|
shapes_node.append(nurbs_curve_node)
|
210
222
|
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
223
|
+
if not is_field:
|
224
|
+
user_attribute_node = self.get_user_attribute_node(
|
225
|
+
node_id,
|
226
|
+
attributes=[
|
227
|
+
("maxSpeedScale", "integer", "1"),
|
228
|
+
("speedLimit", "integer", "100"),
|
229
|
+
],
|
230
|
+
)
|
218
231
|
|
219
|
-
|
232
|
+
user_attributes_node.append(user_attribute_node)
|
220
233
|
node_id += 1
|
221
234
|
|
222
235
|
tree.write(splines_i3d_path) # type: ignore
|
@@ -55,13 +55,12 @@ class Satellite(ImageComponent):
|
|
55
55
|
self.logger.debug("Satellite images download is disabled.")
|
56
56
|
return
|
57
57
|
|
58
|
-
|
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 +
|
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
|
maps4fs/generator/settings.py
CHANGED
@@ -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
|
-
|
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,18 +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
|
-
|
231
|
-
|
232
231
|
class SatelliteSettings(SettingsModel):
|
233
232
|
"""Represents the advanced settings for satellite component.
|
234
233
|
|
@@ -238,5 +237,4 @@ class SatelliteSettings(SettingsModel):
|
|
238
237
|
"""
|
239
238
|
|
240
239
|
download_images: bool = False
|
241
|
-
satellite_margin: int = 0
|
242
240
|
zoom_level: int = 16
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: maps4fs
|
3
|
-
Version:
|
3
|
+
Version: 2.0.0
|
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
|
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=
|
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=
|
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=
|
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=
|
14
|
-
maps4fs/generator/component/i3d.py,sha256=
|
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=
|
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=
|
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/
|
54
|
-
maps4fs/
|
55
|
-
maps4fs/
|
56
|
-
maps4fs/
|
57
|
-
maps4fs-
|
58
|
-
maps4fs-1.9.53.dist-info/METADATA,sha256=8Q5ZfUvd2GzOFcVfhHJouP6vgKs-77Nbk_6mAASL8bE,49397
|
59
|
-
maps4fs-1.9.53.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
60
|
-
maps4fs-1.9.53.dist-info/top_level.txt,sha256=Ue9DSRlejRQRCaJueB0uLcKrWwsEq9zezfv5dI5mV1M,8
|
61
|
-
maps4fs-1.9.53.dist-info/RECORD,,
|
53
|
+
maps4fs-2.0.0.dist-info/licenses/LICENSE.md,sha256=pTKD_oUexcn-yccFCTrMeLkZy0ifLRa-VNcDLqLZaIw,10749
|
54
|
+
maps4fs-2.0.0.dist-info/METADATA,sha256=kaF0PzplnuFhL2_8PLKYGd_mbowqWQi7G0rhvK7T9A8,47725
|
55
|
+
maps4fs-2.0.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
56
|
+
maps4fs-2.0.0.dist-info/top_level.txt,sha256=Ue9DSRlejRQRCaJueB0uLcKrWwsEq9zezfv5dI5mV1M,8
|
57
|
+
maps4fs-2.0.0.dist-info/RECORD,,
|
maps4fs/toolbox/__init__.py
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
# pylint: disable=missing-module-docstring
|
maps4fs/toolbox/background.py
DELETED
@@ -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)
|
maps4fs/toolbox/custom_osm.py
DELETED
@@ -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
|
File without changes
|
File without changes
|
File without changes
|