maps4fs 2.9.34__py3-none-any.whl → 2.9.36__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.
Potentially problematic release.
This version of maps4fs might be problematic. Click here for more details.
- maps4fs/generator/component/background.py +8 -3
- maps4fs/generator/component/building.py +160 -24
- maps4fs/generator/component/i3d.py +23 -1
- maps4fs/generator/component/texture.py +2 -2
- maps4fs/generator/config.py +25 -2
- maps4fs/generator/map.py +1 -0
- maps4fs/generator/settings.py +18 -1
- {maps4fs-2.9.34.dist-info → maps4fs-2.9.36.dist-info}/METADATA +1 -1
- {maps4fs-2.9.34.dist-info → maps4fs-2.9.36.dist-info}/RECORD +12 -12
- {maps4fs-2.9.34.dist-info → maps4fs-2.9.36.dist-info}/WHEEL +0 -0
- {maps4fs-2.9.34.dist-info → maps4fs-2.9.36.dist-info}/licenses/LICENSE.md +0 -0
- {maps4fs-2.9.34.dist-info → maps4fs-2.9.36.dist-info}/top_level.txt +0 -0
|
@@ -823,6 +823,10 @@ class Background(MeshComponent, ImageComponent):
|
|
|
823
823
|
|
|
824
824
|
if self.map.background_settings.flatten_water:
|
|
825
825
|
try:
|
|
826
|
+
# Check if there are any water pixels (255) in the water resources image.
|
|
827
|
+
if not np.any(water_resources_image == 255):
|
|
828
|
+
self.logger.warning("No water pixels found in water resources image.")
|
|
829
|
+
return
|
|
826
830
|
mask = water_resources_image == 255
|
|
827
831
|
flatten_to = int(np.mean(dem_image[mask]) - subtract_by) # type: ignore
|
|
828
832
|
self.flatten_water_to = flatten_to # type: ignore
|
|
@@ -862,13 +866,14 @@ class Background(MeshComponent, ImageComponent):
|
|
|
862
866
|
"""
|
|
863
867
|
self.logger.debug("Starting line-based water generation...")
|
|
864
868
|
water_polygons = self.get_infolayer_data(Parameters.BACKGROUND, Parameters.WATER)
|
|
865
|
-
self.logger.debug(
|
|
866
|
-
"Found %s water polygons in background info layer.", len(water_polygons) # type: ignore
|
|
867
|
-
)
|
|
868
869
|
if not water_polygons:
|
|
869
870
|
self.logger.warning("No water polygons found in background info layer.")
|
|
870
871
|
return
|
|
871
872
|
|
|
873
|
+
self.logger.debug(
|
|
874
|
+
"Found %s water polygons in background info layer.", len(water_polygons) # type: ignore
|
|
875
|
+
)
|
|
876
|
+
|
|
872
877
|
polygons: list[shapely.Polygon] = []
|
|
873
878
|
for polygon_points in water_polygons:
|
|
874
879
|
if not polygon_points or len(polygon_points) < 2:
|
|
@@ -14,8 +14,9 @@ from maps4fs.generator.settings import Parameters
|
|
|
14
14
|
from maps4fs.generator.utils import get_region_by_coordinates
|
|
15
15
|
|
|
16
16
|
BUILDINGS_STARTING_NODE_ID = 10000
|
|
17
|
-
TOLERANCE_FACTOR = 0.3 # 30% size tolerance
|
|
18
17
|
DEFAULT_HEIGHT = 200
|
|
18
|
+
AUTO_REGION = "auto"
|
|
19
|
+
ALL_REGIONS = "all"
|
|
19
20
|
|
|
20
21
|
|
|
21
22
|
AREA_TYPES = {
|
|
@@ -45,16 +46,25 @@ class BuildingEntry(NamedTuple):
|
|
|
45
46
|
class BuildingEntryCollection:
|
|
46
47
|
"""Collection of building entries with efficient lookup capabilities."""
|
|
47
48
|
|
|
48
|
-
def __init__(
|
|
49
|
+
def __init__(
|
|
50
|
+
self, building_entries: list[BuildingEntry], region: str, ignore_region: bool = False
|
|
51
|
+
):
|
|
49
52
|
"""Initialize the collection with a list of building entries for a specific region.
|
|
50
53
|
|
|
51
54
|
Arguments:
|
|
52
55
|
building_entries (list[BuildingEntry]): List of building entries to manage
|
|
53
56
|
region (str): The region for this collection (filters entries to this region only)
|
|
57
|
+
ignore_region (bool): If True, ignore region filtering and use all entries
|
|
54
58
|
"""
|
|
55
59
|
self.region = region
|
|
56
|
-
|
|
57
|
-
|
|
60
|
+
self.ignore_region = ignore_region
|
|
61
|
+
|
|
62
|
+
# Filter entries based on ignore_region flag
|
|
63
|
+
if ignore_region:
|
|
64
|
+
self.entries = building_entries # Use all entries regardless of region
|
|
65
|
+
else:
|
|
66
|
+
self.entries = [entry for entry in building_entries if region in entry.regions]
|
|
67
|
+
|
|
58
68
|
# Create indices for faster lookup
|
|
59
69
|
self._create_indices()
|
|
60
70
|
|
|
@@ -77,7 +87,7 @@ class BuildingEntryCollection:
|
|
|
77
87
|
tolerance: float = 0.3,
|
|
78
88
|
) -> BuildingEntry | None:
|
|
79
89
|
"""Find the best matching building entry based on criteria.
|
|
80
|
-
|
|
90
|
+
Entries are filtered by region during initialization unless ignore_region is True.
|
|
81
91
|
|
|
82
92
|
Arguments:
|
|
83
93
|
category (str): Required building category
|
|
@@ -88,7 +98,7 @@ class BuildingEntryCollection:
|
|
|
88
98
|
Returns:
|
|
89
99
|
BuildingEntry | None: Best matching entry or None if no suitable match found
|
|
90
100
|
"""
|
|
91
|
-
# Start with buildings of the required category (
|
|
101
|
+
# Start with buildings of the required category (filtered by region unless ignore_region is True)
|
|
92
102
|
candidates = self.by_category.get(category, [])
|
|
93
103
|
if not candidates:
|
|
94
104
|
return None
|
|
@@ -107,6 +117,91 @@ class BuildingEntryCollection:
|
|
|
107
117
|
scored_candidates.sort(key=lambda x: x[0], reverse=True)
|
|
108
118
|
return scored_candidates[0][1]
|
|
109
119
|
|
|
120
|
+
def find_best_match_with_orientation(
|
|
121
|
+
self,
|
|
122
|
+
category: str,
|
|
123
|
+
width: float | None = None,
|
|
124
|
+
depth: float | None = None,
|
|
125
|
+
tolerance: float = 0.3,
|
|
126
|
+
) -> tuple[BuildingEntry | None, bool]:
|
|
127
|
+
"""Find the best matching building entry and determine if rotation is needed.
|
|
128
|
+
|
|
129
|
+
Arguments:
|
|
130
|
+
category (str): Required building category
|
|
131
|
+
width (float | None): Desired width (optional)
|
|
132
|
+
depth (float | None): Desired depth (optional)
|
|
133
|
+
tolerance (float): Size tolerance factor (0.3 = 30% tolerance)
|
|
134
|
+
|
|
135
|
+
Returns:
|
|
136
|
+
tuple[BuildingEntry | None, bool]: Best matching entry and whether it needs 90° rotation
|
|
137
|
+
"""
|
|
138
|
+
# Start with buildings of the required category
|
|
139
|
+
candidates = self.by_category.get(category, [])
|
|
140
|
+
if not candidates:
|
|
141
|
+
return None, False
|
|
142
|
+
|
|
143
|
+
# Score each candidate and track orientation
|
|
144
|
+
scored_candidates = []
|
|
145
|
+
for entry in candidates:
|
|
146
|
+
score, needs_rotation = self._calculate_match_score_with_orientation(
|
|
147
|
+
entry, category, width, depth, tolerance
|
|
148
|
+
)
|
|
149
|
+
if score > 0: # Only consider viable matches
|
|
150
|
+
scored_candidates.append((score, entry, needs_rotation))
|
|
151
|
+
|
|
152
|
+
if not scored_candidates:
|
|
153
|
+
return None, False
|
|
154
|
+
|
|
155
|
+
# Return the highest scoring match with its orientation info
|
|
156
|
+
scored_candidates.sort(key=lambda x: x[0], reverse=True)
|
|
157
|
+
_, best_entry, needs_rotation = scored_candidates[0]
|
|
158
|
+
return best_entry, needs_rotation
|
|
159
|
+
|
|
160
|
+
def _calculate_match_score_with_orientation(
|
|
161
|
+
self,
|
|
162
|
+
entry: BuildingEntry,
|
|
163
|
+
category: str,
|
|
164
|
+
width: float | None,
|
|
165
|
+
depth: float | None,
|
|
166
|
+
tolerance: float,
|
|
167
|
+
) -> tuple[float, bool]:
|
|
168
|
+
"""Calculate a match score and determine orientation for a building entry.
|
|
169
|
+
|
|
170
|
+
Returns:
|
|
171
|
+
tuple[float, bool]: Match score and whether 90° rotation is needed
|
|
172
|
+
"""
|
|
173
|
+
score = 0.0
|
|
174
|
+
|
|
175
|
+
# Category match (required) - base score
|
|
176
|
+
if category in entry.categories:
|
|
177
|
+
score = 100.0
|
|
178
|
+
else:
|
|
179
|
+
return 0.0, False # Category mismatch = no match
|
|
180
|
+
|
|
181
|
+
# Size matching (if dimensions are provided)
|
|
182
|
+
if width is not None and depth is not None:
|
|
183
|
+
# Calculate how well the dimensions match (considering both orientations)
|
|
184
|
+
size_score1 = self._calculate_size_match(
|
|
185
|
+
entry.width, entry.depth, width, depth, tolerance
|
|
186
|
+
)
|
|
187
|
+
size_score2 = self._calculate_size_match(
|
|
188
|
+
entry.width, entry.depth, depth, width, tolerance
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
# Determine which orientation is better
|
|
192
|
+
if size_score1 >= size_score2:
|
|
193
|
+
# Original orientation is better
|
|
194
|
+
if size_score1 > 0:
|
|
195
|
+
score += size_score1 * 80.0
|
|
196
|
+
return score, False
|
|
197
|
+
return 0.0, False
|
|
198
|
+
if size_score2 > 0:
|
|
199
|
+
score += size_score2 * 80.0
|
|
200
|
+
return score, True
|
|
201
|
+
return 0.0, False
|
|
202
|
+
|
|
203
|
+
return score, False
|
|
204
|
+
|
|
110
205
|
def _calculate_match_score(
|
|
111
206
|
self,
|
|
112
207
|
entry: BuildingEntry,
|
|
@@ -116,7 +211,7 @@ class BuildingEntryCollection:
|
|
|
116
211
|
tolerance: float,
|
|
117
212
|
) -> float:
|
|
118
213
|
"""Calculate a match score for a building entry.
|
|
119
|
-
Region is
|
|
214
|
+
Region is matched during initialization unless ignore_region is True.
|
|
120
215
|
|
|
121
216
|
Returns:
|
|
122
217
|
float: Match score (higher is better, 0 means no match)
|
|
@@ -175,11 +270,11 @@ class BuildingEntryCollection:
|
|
|
175
270
|
return (width_ratio + depth_ratio) / 2.0
|
|
176
271
|
|
|
177
272
|
def get_available_categories(self) -> list[str]:
|
|
178
|
-
"""Get list of available building categories for this
|
|
273
|
+
"""Get list of available building categories for this collection."""
|
|
179
274
|
return list(self.by_category.keys())
|
|
180
275
|
|
|
181
276
|
def filter_by_category(self, category: str) -> list[BuildingEntry]:
|
|
182
|
-
"""Get all buildings of a specific category (
|
|
277
|
+
"""Get all buildings of a specific category (filtered by region unless ignore_region is True)."""
|
|
183
278
|
return self.by_category.get(category, [])
|
|
184
279
|
|
|
185
280
|
|
|
@@ -310,17 +405,40 @@ class Building(I3d):
|
|
|
310
405
|
building = BuildingEntry(**building_entry)
|
|
311
406
|
building_entries.append(building)
|
|
312
407
|
|
|
313
|
-
|
|
408
|
+
ignore_region = False
|
|
409
|
+
region = ""
|
|
314
410
|
|
|
315
|
-
self.
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
region
|
|
320
|
-
|
|
411
|
+
if self.map.building_settings.region == AUTO_REGION:
|
|
412
|
+
region = get_region_by_coordinates(self.coordinates)
|
|
413
|
+
elif self.map.building_settings.region == ALL_REGIONS:
|
|
414
|
+
ignore_region = True
|
|
415
|
+
region = "all" # Set a default region name for logging
|
|
416
|
+
else:
|
|
417
|
+
region = self.map.building_settings.region
|
|
418
|
+
|
|
419
|
+
self.buildings_collection = BuildingEntryCollection(building_entries, region, ignore_region)
|
|
420
|
+
|
|
421
|
+
if ignore_region:
|
|
422
|
+
self.logger.info(
|
|
423
|
+
"Buildings collection created with %d buildings ignoring region restrictions.",
|
|
424
|
+
len(self.buildings_collection.entries),
|
|
425
|
+
)
|
|
426
|
+
else:
|
|
427
|
+
self.logger.info(
|
|
428
|
+
"Buildings collection created with %d buildings for region '%s'.",
|
|
429
|
+
len(self.buildings_collection.entries),
|
|
430
|
+
region,
|
|
431
|
+
)
|
|
321
432
|
|
|
322
|
-
# pylint: disable=too-many-return-statements
|
|
323
433
|
def process(self) -> None:
|
|
434
|
+
"""Process and place buildings on the map."""
|
|
435
|
+
try:
|
|
436
|
+
self.add_buildings()
|
|
437
|
+
except Exception as e:
|
|
438
|
+
self.logger.warning("An error occurred during buildings processing: %s", e)
|
|
439
|
+
|
|
440
|
+
# pylint: disable=too-many-return-statements
|
|
441
|
+
def add_buildings(self) -> None:
|
|
324
442
|
"""Process and place buildings on the map based on the buildings map image and schema."""
|
|
325
443
|
if not hasattr(self, "buildings_map_path") or not os.path.isfile(self.buildings_map_path):
|
|
326
444
|
self.logger.warning(
|
|
@@ -376,7 +494,7 @@ class Building(I3d):
|
|
|
376
494
|
file_id_counter = BUILDINGS_STARTING_NODE_ID
|
|
377
495
|
node_id_counter = BUILDINGS_STARTING_NODE_ID + 1000
|
|
378
496
|
|
|
379
|
-
not_resized_dem = self.
|
|
497
|
+
not_resized_dem = self.get_not_resized_dem_with_foundations(allow_fallback=True)
|
|
380
498
|
if not_resized_dem is None:
|
|
381
499
|
self.logger.warning("Not resized DEM not found.")
|
|
382
500
|
return
|
|
@@ -414,17 +532,21 @@ class Building(I3d):
|
|
|
414
532
|
rotation_angle,
|
|
415
533
|
)
|
|
416
534
|
|
|
417
|
-
# 3. Find the best matching building from the collection
|
|
418
|
-
best_match = self.buildings_collection.
|
|
535
|
+
# 3. Find the best matching building from the collection and determine orientation
|
|
536
|
+
best_match, needs_rotation = self.buildings_collection.find_best_match_with_orientation(
|
|
419
537
|
category=category,
|
|
420
538
|
width=width,
|
|
421
539
|
depth=depth,
|
|
422
|
-
tolerance=
|
|
540
|
+
tolerance=self.map.building_settings.tolerance_factor,
|
|
423
541
|
)
|
|
424
542
|
|
|
425
543
|
if best_match:
|
|
426
544
|
self.logger.debug(
|
|
427
|
-
|
|
545
|
+
"Best building match: %s: %d x %d, needs_rotation: %s",
|
|
546
|
+
best_match.name,
|
|
547
|
+
best_match.width,
|
|
548
|
+
best_match.depth,
|
|
549
|
+
needs_rotation,
|
|
428
550
|
)
|
|
429
551
|
|
|
430
552
|
# Get world coordinates
|
|
@@ -470,11 +592,21 @@ class Building(I3d):
|
|
|
470
592
|
else:
|
|
471
593
|
file_id = used_building_files[best_match.file]
|
|
472
594
|
|
|
595
|
+
# Adjust rotation if the building needs to be rotated 90 degrees
|
|
596
|
+
final_rotation = rotation_angle
|
|
597
|
+
if needs_rotation:
|
|
598
|
+
final_rotation = (rotation_angle + 90.0) % 360.0
|
|
599
|
+
self.logger.debug(
|
|
600
|
+
"Building needs 90° rotation: original=%.1f°, final=%.1f°",
|
|
601
|
+
rotation_angle,
|
|
602
|
+
final_rotation,
|
|
603
|
+
)
|
|
604
|
+
|
|
473
605
|
# Create building instance in the buildings group
|
|
474
606
|
building_node = ET.SubElement(buildings_group, "ReferenceNode")
|
|
475
607
|
building_node.set("name", f"{best_match.name}_{node_id_counter}")
|
|
476
608
|
building_node.set("translation", f"{x_center:.3f} {z:.3f} {y_center:.3f}")
|
|
477
|
-
building_node.set("rotation", f"0 {
|
|
609
|
+
building_node.set("rotation", f"0 {final_rotation:.3f} 0")
|
|
478
610
|
# building_node.set(
|
|
479
611
|
# "scale", f"{scale_width:.4f} 1.0 {scale_depth:.4f}"
|
|
480
612
|
# )
|
|
@@ -485,7 +617,11 @@ class Building(I3d):
|
|
|
485
617
|
|
|
486
618
|
else:
|
|
487
619
|
self.logger.debug(
|
|
488
|
-
|
|
620
|
+
"No suitable building found for category '%s' with dimensions %.2fx%.2f",
|
|
621
|
+
category,
|
|
622
|
+
width,
|
|
623
|
+
depth,
|
|
624
|
+
needs_rotation,
|
|
489
625
|
)
|
|
490
626
|
continue
|
|
491
627
|
|
|
@@ -712,7 +712,7 @@ class I3d(XMLComponent, ImageComponent):
|
|
|
712
712
|
else background_component.not_resized_path
|
|
713
713
|
)
|
|
714
714
|
|
|
715
|
-
if not dem_path:
|
|
715
|
+
if not dem_path or not os.path.isfile(dem_path):
|
|
716
716
|
self.logger.warning("Not resized DEM path not found.")
|
|
717
717
|
return None
|
|
718
718
|
|
|
@@ -720,6 +720,28 @@ class I3d(XMLComponent, ImageComponent):
|
|
|
720
720
|
|
|
721
721
|
return not_resized_dem
|
|
722
722
|
|
|
723
|
+
def get_not_resized_dem_with_foundations(
|
|
724
|
+
self, allow_fallback: bool = False
|
|
725
|
+
) -> np.ndarray | None:
|
|
726
|
+
"""Gets the not resized DEM with foundations. If the DEM with foundations is not found
|
|
727
|
+
and allow_fallback is True, the method returns the not resized DEM without foundations.
|
|
728
|
+
|
|
729
|
+
Arguments:
|
|
730
|
+
allow_fallback (bool, optional): Whether to allow fallback to DEM without
|
|
731
|
+
foundations. Defaults to False.
|
|
732
|
+
|
|
733
|
+
Returns:
|
|
734
|
+
np.ndarray | None: The not resized DEM image or None if the image could not be read.
|
|
735
|
+
"""
|
|
736
|
+
dem_with_foundations = self.get_not_resized_dem(with_foundations=True)
|
|
737
|
+
|
|
738
|
+
if dem_with_foundations is not None:
|
|
739
|
+
return dem_with_foundations
|
|
740
|
+
self.logger.warning("Not resized DEM with foundations not found.")
|
|
741
|
+
if allow_fallback:
|
|
742
|
+
return self.get_not_resized_dem(with_foundations=False)
|
|
743
|
+
return None
|
|
744
|
+
|
|
723
745
|
def info_sequence(self) -> dict[str, dict[str, str | float | int]]:
|
|
724
746
|
"""Returns information about the component.
|
|
725
747
|
|
|
@@ -814,7 +814,7 @@ class Texture(ImageComponent):
|
|
|
814
814
|
is_fieds = info_layer == "fields"
|
|
815
815
|
|
|
816
816
|
ox_settings.use_cache = self.map.texture_settings.use_cache
|
|
817
|
-
ox_settings.requests_timeout =
|
|
817
|
+
ox_settings.requests_timeout = 10
|
|
818
818
|
|
|
819
819
|
objects = self.fetch_osm_data(tags)
|
|
820
820
|
if objects is None or objects.empty:
|
|
@@ -845,7 +845,7 @@ class Texture(ImageComponent):
|
|
|
845
845
|
else:
|
|
846
846
|
objects = ox.features_from_bbox(bbox=self.new_bbox, tags=tags)
|
|
847
847
|
except Exception as e:
|
|
848
|
-
self.logger.
|
|
848
|
+
self.logger.warning("Error fetching objects for tags: %s. Error: %s.", tags, e)
|
|
849
849
|
return None
|
|
850
850
|
|
|
851
851
|
return objects
|
maps4fs/generator/config.py
CHANGED
|
@@ -205,8 +205,31 @@ SAT_CACHE_DIR = os.path.join(MFS_CACHE_DIR, "sat")
|
|
|
205
205
|
|
|
206
206
|
osmnx_cache = os.path.join(MFS_CACHE_DIR, "osmnx")
|
|
207
207
|
osmnx_data = os.path.join(MFS_CACHE_DIR, "odata")
|
|
208
|
-
|
|
209
|
-
|
|
208
|
+
|
|
209
|
+
CACHE_DIRS = [DTM_CACHE_DIR, SAT_CACHE_DIR, osmnx_cache, osmnx_data]
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def create_cache_dirs() -> None:
|
|
213
|
+
"""Create cache directories if they do not exist."""
|
|
214
|
+
logger.info("Ensuring cache directories exist...")
|
|
215
|
+
for cache_dir in CACHE_DIRS:
|
|
216
|
+
os.makedirs(cache_dir, exist_ok=True)
|
|
217
|
+
logger.debug("Cache directory ensured: %s", cache_dir)
|
|
218
|
+
logger.info("All cache directories are ready.")
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def clean_cache() -> None:
|
|
222
|
+
"""Clean all cache directories by removing and recreating them."""
|
|
223
|
+
logger.info("Cleaning cache directories...")
|
|
224
|
+
for cache_dir in CACHE_DIRS:
|
|
225
|
+
if os.path.exists(cache_dir):
|
|
226
|
+
shutil.rmtree(cache_dir)
|
|
227
|
+
logger.debug("Removed cache directory: %s", cache_dir)
|
|
228
|
+
create_cache_dirs()
|
|
229
|
+
logger.info("Cache directories cleaned and recreated.")
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
create_cache_dirs()
|
|
210
233
|
|
|
211
234
|
|
|
212
235
|
ox_settings.cache_folder = osmnx_cache
|
maps4fs/generator/map.py
CHANGED
|
@@ -105,6 +105,7 @@ class Map:
|
|
|
105
105
|
self.i3d_settings = generation_settings.i3d_settings
|
|
106
106
|
self.texture_settings = generation_settings.texture_settings
|
|
107
107
|
self.satellite_settings = generation_settings.satellite_settings
|
|
108
|
+
self.building_settings = generation_settings.building_settings
|
|
108
109
|
self.process_settings()
|
|
109
110
|
|
|
110
111
|
self.logger = logger if logger else Logger()
|
maps4fs/generator/settings.py
CHANGED
|
@@ -4,7 +4,7 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import re
|
|
6
6
|
from datetime import datetime
|
|
7
|
-
from typing import TYPE_CHECKING, Any, NamedTuple
|
|
7
|
+
from typing import TYPE_CHECKING, Any, Literal, NamedTuple
|
|
8
8
|
|
|
9
9
|
from pydantic import BaseModel, ConfigDict
|
|
10
10
|
|
|
@@ -279,6 +279,21 @@ class SatelliteSettings(SettingsModel):
|
|
|
279
279
|
zoom_level: int = 16
|
|
280
280
|
|
|
281
281
|
|
|
282
|
+
class BuildingSettings(SettingsModel):
|
|
283
|
+
"""Represents the advanced settings for building component.
|
|
284
|
+
|
|
285
|
+
Attributes:
|
|
286
|
+
generate_buildings (bool): generate buildings on the map.
|
|
287
|
+
region (Literal["auto", "all", "EU", "US"]): region for the buildings.
|
|
288
|
+
tolerance_factor (float): tolerance factor representing allowed dimension difference
|
|
289
|
+
between OSM building footprint and the building model footprint.
|
|
290
|
+
"""
|
|
291
|
+
|
|
292
|
+
generate_buildings: bool = True
|
|
293
|
+
region: Literal["auto", "all", "EU", "US"] = "auto"
|
|
294
|
+
tolerance_factor: float = 0.3
|
|
295
|
+
|
|
296
|
+
|
|
282
297
|
class GenerationSettings(BaseModel):
|
|
283
298
|
"""Represents the settings for the map generation process."""
|
|
284
299
|
|
|
@@ -288,6 +303,7 @@ class GenerationSettings(BaseModel):
|
|
|
288
303
|
i3d_settings: I3DSettings = I3DSettings()
|
|
289
304
|
texture_settings: TextureSettings = TextureSettings()
|
|
290
305
|
satellite_settings: SatelliteSettings = SatelliteSettings()
|
|
306
|
+
building_settings: BuildingSettings = BuildingSettings()
|
|
291
307
|
|
|
292
308
|
def to_json(self) -> dict[str, Any]:
|
|
293
309
|
"""Convert the GenerationSettings instance to JSON format.
|
|
@@ -302,6 +318,7 @@ class GenerationSettings(BaseModel):
|
|
|
302
318
|
"I3DSettings": self.i3d_settings.model_dump(),
|
|
303
319
|
"TextureSettings": self.texture_settings.model_dump(),
|
|
304
320
|
"SatelliteSettings": self.satellite_settings.model_dump(),
|
|
321
|
+
"BuildingSettings": self.building_settings.model_dump(),
|
|
305
322
|
}
|
|
306
323
|
|
|
307
324
|
@classmethod
|
|
@@ -1,31 +1,31 @@
|
|
|
1
1
|
maps4fs/__init__.py,sha256=5ixsCA5vgcIV0OrF9EJBm91Mmc_KfMiDRM-QyifMAvo,386
|
|
2
2
|
maps4fs/logger.py,sha256=aZAa9glzgvH6ySVDLelSPTwHfWZtpGK5YBl-ufNUsPg,801
|
|
3
3
|
maps4fs/generator/__init__.py,sha256=zZMLEkGzb4z0xql650gOtGSvcgX58DnJ2yN3vC2daRk,43
|
|
4
|
-
maps4fs/generator/config.py,sha256=
|
|
4
|
+
maps4fs/generator/config.py,sha256=o_zRT8Nauy1djELsC867yD7GTs1fwz1OtZ2jE4w_7Ts,9797
|
|
5
5
|
maps4fs/generator/game.py,sha256=bflRv0lxJ9-wkRvauh0k0RzIgF7zVWguygqQLcC7U-s,18457
|
|
6
|
-
maps4fs/generator/map.py,sha256=
|
|
6
|
+
maps4fs/generator/map.py,sha256=kkyMETKteFhnWRgmcR8gjdNBQy4roQzcdlFw1nE5chE,16116
|
|
7
7
|
maps4fs/generator/monitor.py,sha256=Yrc7rClpmJK53SRzrOYZNBlwJmb5l6TkW-laFbyBEno,3524
|
|
8
8
|
maps4fs/generator/qgis.py,sha256=Es8hLuqN_KH8lDfnJE6He2rWYbAKJ3RGPn-o87S6CPI,6116
|
|
9
|
-
maps4fs/generator/settings.py,sha256=
|
|
9
|
+
maps4fs/generator/settings.py,sha256=jrrIILNRtIpj7hpLrQqLTIagTY8tdJlLZDEN1M4n3Yc,14116
|
|
10
10
|
maps4fs/generator/statistics.py,sha256=ol0MTiehcCbQFfyYA7cKU-M4_cjiLCktnGbid4GYABU,2641
|
|
11
11
|
maps4fs/generator/utils.py,sha256=qaHmS5I30OhDwd213bbctlplQQlX-qkHugyszXGmh0U,5587
|
|
12
12
|
maps4fs/generator/component/__init__.py,sha256=s01yVVVi8R2xxNvflu2D6wTd9I_g73AMM2x7vAC7GX4,490
|
|
13
|
-
maps4fs/generator/component/background.py,sha256=
|
|
14
|
-
maps4fs/generator/component/building.py,sha256=
|
|
13
|
+
maps4fs/generator/component/background.py,sha256=KYtbnIqflrqiTzsibWh7atDWTeV1GgvLD0ZG463WshM,49704
|
|
14
|
+
maps4fs/generator/component/building.py,sha256=VoLjj6mDT-4kVfwxXP-lD0r4vJVYszyWZtciVFwdkIk,27402
|
|
15
15
|
maps4fs/generator/component/config.py,sha256=tI2RQaGIqBgJIi9KjYfMZZ8AWg_YVUm6KKsBHGV241g,31285
|
|
16
16
|
maps4fs/generator/component/dem.py,sha256=vMVJtU2jAS-2lfB9JsqodZsrUvY1h5xr3Dh5qk6txwk,11895
|
|
17
17
|
maps4fs/generator/component/grle.py,sha256=FAcGmG7yq0icOElRoO4QMsVisZMsNrLhfNSWvGKnOHg,29899
|
|
18
|
-
maps4fs/generator/component/i3d.py,sha256=
|
|
18
|
+
maps4fs/generator/component/i3d.py,sha256=UM1js6bJx9_deftSvij1qjTiDypY8zvyhcXa5oXkfbk,28143
|
|
19
19
|
maps4fs/generator/component/layer.py,sha256=-MHnIXyJ7Xth9wOcjJCX-XkXBIYYv23lRRGbQ0XlHdU,7602
|
|
20
20
|
maps4fs/generator/component/satellite.py,sha256=1bPqd8JqAPqU0tEI9m-iuljMW9hXqlaCIxvq7kdpMY0,5219
|
|
21
|
-
maps4fs/generator/component/texture.py,sha256=
|
|
21
|
+
maps4fs/generator/component/texture.py,sha256=XocNWPH1BHBcvTOJgpx8RU_tB-Mi1Kefp_lgOVIkEzY,38528
|
|
22
22
|
maps4fs/generator/component/base/__init__.py,sha256=zZMLEkGzb4z0xql650gOtGSvcgX58DnJ2yN3vC2daRk,43
|
|
23
23
|
maps4fs/generator/component/base/component.py,sha256=-7H3donrH19f0_rivNyI3fgLsiZkntXfGywEx4tOnM4,23924
|
|
24
24
|
maps4fs/generator/component/base/component_image.py,sha256=GXFkEFARNRkWkDiGSjvU4WX6f_8s6R1t2ZYqZflv1jk,9626
|
|
25
25
|
maps4fs/generator/component/base/component_mesh.py,sha256=2wGe_-wAZVRljMKzzVJ8jdzIETWg7LjxGj8A3inH5eI,25550
|
|
26
26
|
maps4fs/generator/component/base/component_xml.py,sha256=MT-VhU2dEckLFxAgmxg6V3gnv11di_94Qq6atfpOLdc,5342
|
|
27
|
-
maps4fs-2.9.
|
|
28
|
-
maps4fs-2.9.
|
|
29
|
-
maps4fs-2.9.
|
|
30
|
-
maps4fs-2.9.
|
|
31
|
-
maps4fs-2.9.
|
|
27
|
+
maps4fs-2.9.36.dist-info/licenses/LICENSE.md,sha256=Ptw8AkqJ60c4tRts6yuqGP_8B0dxwOGmJsp6YJ8dKqM,34328
|
|
28
|
+
maps4fs-2.9.36.dist-info/METADATA,sha256=-Fi5m_KZ584IkyeFCJnx89a1yFNq0VIPZEe6Wv3lKSQ,10213
|
|
29
|
+
maps4fs-2.9.36.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
30
|
+
maps4fs-2.9.36.dist-info/top_level.txt,sha256=Ue9DSRlejRQRCaJueB0uLcKrWwsEq9zezfv5dI5mV1M,8
|
|
31
|
+
maps4fs-2.9.36.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|