maps4fs 1.8.1__py3-none-any.whl → 1.8.12__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
maps4fs/generator/i3d.py DELETED
@@ -1,624 +0,0 @@
1
- """This module contains the Config class for map settings and configuration."""
2
-
3
- from __future__ import annotations
4
-
5
- import json
6
- import os
7
- from random import choice, randint, uniform
8
- from typing import Generator
9
- from xml.etree import ElementTree as ET
10
-
11
- import cv2
12
- import numpy as np
13
- from tqdm import tqdm
14
-
15
- from maps4fs.generator.component import Component
16
- from maps4fs.generator.texture import Texture
17
-
18
- DISPLACEMENT_LAYER_SIZE_FOR_BIG_MAPS = 32768
19
- NODE_ID_STARTING_VALUE = 2000
20
- SPLINES_NODE_ID_STARTING_VALUE = 5000
21
- TREE_NODE_ID_STARTING_VALUE = 10000
22
-
23
-
24
- # pylint: disable=R0903
25
- class I3d(Component):
26
- """Component for map i3d file settings and configuration.
27
-
28
- Arguments:
29
- game (Game): The game instance for which the map is generated.
30
- coordinates (tuple[float, float]): The latitude and longitude of the center of the map.
31
- map_size (int): The size of the map in pixels.
32
- map_rotated_size (int): The size of the map in pixels after rotation.
33
- rotation (int): The rotation angle of the map.
34
- map_directory (str): The directory where the map files are stored.
35
- logger (Any, optional): The logger to use. Must have at least three basic methods: debug,
36
- info, warning. If not provided, default logging will be used.
37
- """
38
-
39
- _map_i3d_path: str | None = None
40
-
41
- def preprocess(self) -> None:
42
- """Gets the path to the map I3D file from the game instance and saves it to the instance
43
- attribute. If the game does not support I3D files, the attribute is set to None."""
44
- try:
45
- self._map_i3d_path = self.game.i3d_file_path(self.map_directory)
46
- self.logger.debug("Map I3D path: %s.", self._map_i3d_path)
47
- except NotImplementedError:
48
- self.logger.warning("I3D file processing is not implemented for this game.")
49
- self._map_i3d_path = None
50
-
51
- def process(self) -> None:
52
- """Updates the map I3D file with the default settings."""
53
- self._update_i3d_file()
54
- self._add_fields()
55
- if self.game.code == "FS25":
56
- self._add_forests()
57
- self._add_splines()
58
-
59
- def _get_tree(self) -> ET.ElementTree | None:
60
- """Returns the ElementTree instance of the map I3D file."""
61
- if not self._map_i3d_path:
62
- self.logger.debug("I3D is not obtained, skipping the update.")
63
- return None
64
- if not os.path.isfile(self._map_i3d_path):
65
- self.logger.warning("I3D file not found: %s.", self._map_i3d_path)
66
- return None
67
-
68
- return ET.parse(self._map_i3d_path)
69
-
70
- def _update_i3d_file(self) -> None:
71
- """Updates the map I3D file with the default settings."""
72
-
73
- tree = self._get_tree()
74
- if tree is None:
75
- return
76
-
77
- self.logger.debug("Map I3D file loaded from: %s.", self._map_i3d_path)
78
-
79
- root = tree.getroot()
80
- for map_elem in root.iter("Scene"):
81
- for terrain_elem in map_elem.iter("TerrainTransformGroup"):
82
- if self.map.shared_settings.change_height_scale:
83
- suggested_height_scale = self.map.shared_settings.height_scale_value
84
- if suggested_height_scale is not None and suggested_height_scale > 255:
85
- new_height_scale = int(
86
- self.map.shared_settings.height_scale_value # type: ignore
87
- )
88
- terrain_elem.set("heightScale", str(new_height_scale))
89
- self.logger.info(
90
- "heightScale attribute set to %s in TerrainTransformGroup element.",
91
- new_height_scale,
92
- )
93
-
94
- self.logger.debug("TerrainTransformGroup element updated in I3D file.")
95
- sun_elem = map_elem.find(".//Light[@name='sun']")
96
-
97
- if sun_elem is not None:
98
- self.logger.debug("Sun element found in I3D file.")
99
-
100
- distance = self.map_size // 2
101
-
102
- sun_elem.set("lastShadowMapSplitBboxMin", f"-{distance},-128,-{distance}")
103
- sun_elem.set("lastShadowMapSplitBboxMax", f"{distance},148,{distance}")
104
-
105
- self.logger.debug(
106
- "Sun BBOX updated with half of the map size: %s.",
107
- distance,
108
- )
109
-
110
- if self.map_size > 4096:
111
- displacement_layer = terrain_elem.find(".//DisplacementLayer") # pylint: disable=W0631
112
-
113
- if displacement_layer is not None:
114
- displacement_layer.set("size", str(DISPLACEMENT_LAYER_SIZE_FOR_BIG_MAPS))
115
- self.logger.debug(
116
- "Map size is greater than 4096, DisplacementLayer size set to %s.",
117
- )
118
-
119
- tree.write(self._map_i3d_path) # type: ignore
120
- self.logger.debug("Map I3D file saved to: %s.", self._map_i3d_path)
121
-
122
- def previews(self) -> list[str]:
123
- """Returns a list of paths to the preview images (empty list).
124
- The component does not generate any preview images so it returns an empty list.
125
-
126
- Returns:
127
- list[str]: An empty list.
128
- """
129
- return []
130
-
131
- # pylint: disable=R0914, R0915
132
- def _add_splines(self) -> None:
133
- """Adds splines to the map I3D file."""
134
- splines_i3d_path = os.path.join(self.map_directory, "map", "splines.i3d")
135
- if not os.path.isfile(splines_i3d_path):
136
- self.logger.warning("Splines I3D file not found: %s.", splines_i3d_path)
137
- return
138
-
139
- tree = ET.parse(splines_i3d_path)
140
-
141
- textures_info_layer_path = self.get_infolayer_path("textures")
142
- if not textures_info_layer_path:
143
- return
144
-
145
- with open(textures_info_layer_path, "r", encoding="utf-8") as textures_info_layer_file:
146
- textures_info_layer = json.load(textures_info_layer_file)
147
-
148
- roads_polylines: list[list[tuple[int, int]]] | None = textures_info_layer.get(
149
- "roads_polylines"
150
- )
151
-
152
- if not roads_polylines:
153
- self.logger.warning("Roads polylines data not found in textures info layer.")
154
- return
155
-
156
- self.logger.debug("Found %s roads polylines in textures info layer.", len(roads_polylines))
157
- self.logger.debug("Starging to add roads polylines to the I3D file.")
158
-
159
- root = tree.getroot()
160
- # Find <Shapes> element in the I3D file.
161
- shapes_node = root.find(".//Shapes")
162
- # Find <Scene> element in the I3D file.
163
- scene_node = root.find(".//Scene")
164
-
165
- # Read the not resized DEM to obtain Z values for spline points.
166
- background_component = self.map.get_component("Background")
167
- if not background_component:
168
- self.logger.warning("Background component not found.")
169
- return
170
-
171
- # pylint: disable=no-member
172
- not_resized_dem = cv2.imread(
173
- background_component.not_resized_path, cv2.IMREAD_UNCHANGED # type: ignore
174
- )
175
- self.logger.debug(
176
- "Not resized DEM loaded from: %s. Shape: %s.",
177
- background_component.not_resized_path, # type: ignore
178
- not_resized_dem.shape,
179
- )
180
- dem_x_size, dem_y_size = not_resized_dem.shape
181
-
182
- if shapes_node is not None and scene_node is not None:
183
- node_id = SPLINES_NODE_ID_STARTING_VALUE
184
- user_attributes_node = root.find(".//UserAttributes")
185
- if user_attributes_node is None:
186
- self.logger.warning("UserAttributes node not found in I3D file.")
187
- return
188
-
189
- for road_id, road in enumerate(roads_polylines, start=1):
190
- # Add to scene node
191
- # <Shape name="spline01_CSV" translation="0 0 0" nodeId="11" shapeId="11"/>
192
-
193
- try:
194
- fitted_road = self.fit_object_into_bounds(
195
- linestring_points=road, angle=self.rotation
196
- )
197
- except ValueError as e:
198
- self.logger.debug(
199
- "Road %s could not be fitted into the map bounds with error: %s",
200
- road_id,
201
- e,
202
- )
203
- continue
204
-
205
- self.logger.debug("Road %s has %s points.", road_id, len(fitted_road))
206
- fitted_road = self.interpolate_points(
207
- fitted_road, num_points=self.map.spline_settings.spline_density
208
- )
209
- self.logger.debug(
210
- "Road %s has %s points after interpolation.", road_id, len(fitted_road)
211
- )
212
-
213
- spline_name = f"spline{road_id}"
214
-
215
- shape_node = ET.Element("Shape")
216
- shape_node.set("name", spline_name)
217
- shape_node.set("translation", "0 0 0")
218
- shape_node.set("nodeId", str(node_id))
219
- shape_node.set("shapeId", str(node_id))
220
-
221
- scene_node.append(shape_node)
222
-
223
- road_ccs = [self.top_left_coordinates_to_center(point) for point in fitted_road]
224
-
225
- # Add to shapes node
226
- # <NurbsCurve name="spline01_CSV" shapeId="11" degree="3" form="open">
227
-
228
- nurbs_curve_node = ET.Element("NurbsCurve")
229
- nurbs_curve_node.set("name", spline_name)
230
- nurbs_curve_node.set("shapeId", str(node_id))
231
- nurbs_curve_node.set("degree", "3")
232
- nurbs_curve_node.set("form", "open")
233
-
234
- # Now for each point in the road add the following entry to nurbs_curve_node
235
- # <cv c="-224.548401, 427.297546, -2047.570312" />
236
- # The second coordinate (Z) will be 0 at the moment.
237
-
238
- for point_ccs, point in zip(road_ccs, fitted_road):
239
- cx, cy = point_ccs
240
- x, y = point
241
-
242
- x = int(x)
243
- y = int(y)
244
-
245
- x = max(0, min(x, dem_x_size - 1))
246
- y = max(0, min(y, dem_y_size - 1))
247
-
248
- z = not_resized_dem[y, x]
249
- z *= self.get_z_scaling_factor() # type: ignore
250
-
251
- cv_node = ET.Element("cv")
252
- cv_node.set("c", f"{cx}, {z}, {cy}")
253
-
254
- nurbs_curve_node.append(cv_node)
255
-
256
- shapes_node.append(nurbs_curve_node)
257
-
258
- # Add UserAttributes to the shape node.
259
- # <UserAttribute nodeId="5000">
260
- # <Attribute name="maxSpeedScale" type="integer" value="1"/>
261
- # <Attribute name="speedLimit" type="integer" value="100"/>
262
- # </UserAttribute>
263
-
264
- user_attribute_node = ET.Element("UserAttribute")
265
- user_attribute_node.set("nodeId", str(node_id))
266
-
267
- attributes = [
268
- ("maxSpeedScale", "integer", "1"),
269
- ("speedLimit", "integer", "100"),
270
- ]
271
-
272
- for name, attr_type, value in attributes:
273
- user_attribute_node.append(I3d.create_attribute_node(name, attr_type, value))
274
-
275
- user_attributes_node.append(user_attribute_node) # type: ignore
276
-
277
- node_id += 1
278
-
279
- tree.write(splines_i3d_path) # type: ignore
280
- self.logger.debug("Splines I3D file saved to: %s.", splines_i3d_path)
281
-
282
- # pylint: disable=R0914, R0915
283
- def _add_fields(self) -> None:
284
- """Adds fields to the map I3D file."""
285
- tree = self._get_tree()
286
- if tree is None:
287
- return
288
-
289
- textures_info_layer_path = self.get_infolayer_path("textures")
290
- if not textures_info_layer_path:
291
- return
292
-
293
- border = 0
294
- textures_layer: Texture | None = self.map.get_component("Texture") # type: ignore
295
- if textures_layer:
296
- border = textures_layer.get_layer_by_usage("field").border # type: ignore
297
-
298
- with open(textures_info_layer_path, "r", encoding="utf-8") as textures_info_layer_file:
299
- textures_info_layer = json.load(textures_info_layer_file)
300
-
301
- fields: list[list[tuple[int, int]]] | None = textures_info_layer.get("fields")
302
- if not fields:
303
- self.logger.warning("Fields data not found in textures info layer.")
304
- return
305
-
306
- self.logger.debug("Found %s fields in textures info layer.", len(fields))
307
- self.logger.debug("Starging to add fields to the I3D file.")
308
-
309
- root = tree.getroot()
310
- gameplay_node = root.find(".//TransformGroup[@name='gameplay']")
311
- if gameplay_node is not None:
312
- fields_node = gameplay_node.find(".//TransformGroup[@name='fields']")
313
- user_attributes_node = root.find(".//UserAttributes")
314
-
315
- if fields_node is not None:
316
- node_id = NODE_ID_STARTING_VALUE
317
-
318
- # Not using enumerate because in case of the error, we do not increment
319
- # the field_id. So as a result we do not have a gap in the field IDs.
320
- field_id = 1
321
-
322
- for field in tqdm(fields, desc="Adding fields", unit="field"):
323
- try:
324
- fitted_field = self.fit_object_into_bounds(
325
- polygon_points=field, angle=self.rotation, border=border
326
- )
327
- except ValueError as e:
328
- self.logger.debug(
329
- "Field %s could not be fitted into the map bounds with error: %s",
330
- field_id,
331
- e,
332
- )
333
- continue
334
-
335
- field_ccs = [
336
- self.top_left_coordinates_to_center(point) for point in fitted_field
337
- ]
338
-
339
- try:
340
- cx, cy = self.get_polygon_center(field_ccs)
341
- except Exception as e: # pylint: disable=W0718
342
- self.logger.debug(
343
- "Field %s could not be fitted into the map bounds.", field_id
344
- )
345
- self.logger.debug("Error: %s", e)
346
- continue
347
-
348
- # Creating the main field node.
349
- field_node = ET.Element("TransformGroup")
350
- field_node.set("name", f"field{field_id}")
351
- field_node.set("translation", f"{cx} 0 {cy}")
352
- field_node.set("nodeId", str(node_id))
353
-
354
- # Adding UserAttributes to the field node.
355
- user_attribute_node = self.create_user_attribute_node(node_id)
356
- user_attributes_node.append(user_attribute_node) # type: ignore
357
-
358
- node_id += 1
359
-
360
- # Creating the polygon points node, which contains the points of the field.
361
- polygon_points_node = ET.Element("TransformGroup")
362
- polygon_points_node.set("name", "polygonPoints")
363
- polygon_points_node.set("nodeId", str(node_id))
364
- node_id += 1
365
-
366
- for point_id, point in enumerate(field_ccs, start=1):
367
- rx, ry = self.absolute_to_relative(point, (cx, cy))
368
-
369
- node_id += 1
370
- point_node = ET.Element("TransformGroup")
371
- point_node.set("name", f"point{point_id}")
372
- point_node.set("translation", f"{rx} 0 {ry}")
373
- point_node.set("nodeId", str(node_id))
374
-
375
- polygon_points_node.append(point_node)
376
-
377
- field_node.append(polygon_points_node)
378
-
379
- # Adding the name indicator node to the field node.
380
- name_indicator_node, node_id = self.get_name_indicator_node(node_id, field_id)
381
- field_node.append(name_indicator_node)
382
-
383
- # Adding the teleport indicator node to the field node.
384
- teleport_indicator_node, node_id = self.get_teleport_indicator_node(node_id)
385
- field_node.append(teleport_indicator_node)
386
-
387
- # Adding the field node to the fields node.
388
- fields_node.append(field_node)
389
- self.logger.debug("Field %s added to the I3D file.", field_id)
390
-
391
- node_id += 1
392
- field_id += 1
393
-
394
- tree.write(self._map_i3d_path) # type: ignore
395
- self.logger.debug("Map I3D file saved to: %s.", self._map_i3d_path)
396
-
397
- def get_name_indicator_node(self, node_id: int, field_id: int) -> tuple[ET.Element, int]:
398
- """Creates a name indicator node with given node ID and field ID.
399
-
400
- Arguments:
401
- node_id (int): The node ID of the name indicator node.
402
- field_id (int): The ID of the field.
403
-
404
- Returns:
405
- tuple[ET.Element, int]: The name indicator node and the updated node ID.
406
- """
407
- node_id += 1
408
- name_indicator_node = ET.Element("TransformGroup")
409
- name_indicator_node.set("name", "nameIndicator")
410
- name_indicator_node.set("nodeId", str(node_id))
411
-
412
- node_id += 1
413
- note_node = ET.Element("Note")
414
- note_node.set("name", "Note")
415
- note_node.set("nodeId", str(node_id))
416
- note_node.set("text", f"field{field_id}&#xA;0.00 ha")
417
- note_node.set("color", "4278190080")
418
- note_node.set("fixedSize", "true")
419
-
420
- name_indicator_node.append(note_node)
421
-
422
- return name_indicator_node, node_id
423
-
424
- def get_teleport_indicator_node(self, node_id: int) -> tuple[ET.Element, int]:
425
- """Creates a teleport indicator node with given node ID.
426
-
427
- Arguments:
428
- node_id (int): The node ID of the teleport indicator node.
429
-
430
- Returns:
431
- tuple[ET.Element, int]: The teleport indicator node and the updated node ID.
432
- """
433
- node_id += 1
434
- teleport_indicator_node = ET.Element("TransformGroup")
435
- teleport_indicator_node.set("name", "teleportIndicator")
436
- teleport_indicator_node.set("nodeId", str(node_id))
437
-
438
- return teleport_indicator_node, node_id
439
-
440
- @staticmethod
441
- def create_user_attribute_node(node_id: int) -> ET.Element:
442
- """Creates an XML user attribute node with given node ID.
443
-
444
- Arguments:
445
- node_id (int): The node ID of the user attribute node.
446
-
447
- Returns:
448
- ET.Element: The created user attribute node.
449
- """
450
- user_attribute_node = ET.Element("UserAttribute")
451
- user_attribute_node.set("nodeId", str(node_id))
452
-
453
- attributes = [
454
- ("angle", "integer", "0"),
455
- ("missionAllowed", "boolean", "true"),
456
- ("missionOnlyGrass", "boolean", "false"),
457
- ("nameIndicatorIndex", "string", "1"),
458
- ("polygonIndex", "string", "0"),
459
- ("teleportIndicatorIndex", "string", "2"),
460
- ]
461
-
462
- for name, attr_type, value in attributes:
463
- user_attribute_node.append(I3d.create_attribute_node(name, attr_type, value))
464
-
465
- return user_attribute_node
466
-
467
- @staticmethod
468
- def create_attribute_node(name: str, attr_type: str, value: str) -> ET.Element:
469
- """Creates an XML attribute node with given name, type, and value.
470
-
471
- Arguments:
472
- name (str): The name of the attribute.
473
- attr_type (str): The type of the attribute.
474
- value (str): The value of the attribute.
475
-
476
- Returns:
477
- ET.Element: The created attribute node.
478
- """
479
- attribute_node = ET.Element("Attribute")
480
- attribute_node.set("name", name)
481
- attribute_node.set("type", attr_type)
482
- attribute_node.set("value", value)
483
- return attribute_node
484
-
485
- # pylint: disable=R0911
486
- def _add_forests(self) -> None:
487
- """Adds forests to the map I3D file."""
488
- custom_schema = self.kwargs.get("tree_custom_schema")
489
- if custom_schema:
490
- tree_schema = custom_schema
491
- else:
492
- try:
493
- tree_schema_path = self.game.tree_schema
494
- except ValueError:
495
- self.logger.warning("Tree schema path not set for the Game %s.", self.game.code)
496
- return
497
-
498
- if not os.path.isfile(tree_schema_path):
499
- self.logger.warning("Tree schema file was not found: %s.", tree_schema_path)
500
- return
501
-
502
- try:
503
- with open(tree_schema_path, "r", encoding="utf-8") as tree_schema_file:
504
- tree_schema = json.load(tree_schema_file) # type: ignore
505
- except json.JSONDecodeError as e:
506
- self.logger.warning(
507
- "Could not load tree schema from %s with error: %s", tree_schema_path, e
508
- )
509
- return
510
-
511
- texture_component: Texture | None = self.map.get_component("Texture") # type: ignore
512
- if not texture_component:
513
- self.logger.warning("Texture component not found.")
514
- return
515
-
516
- forest_layer = texture_component.get_layer_by_usage("forest")
517
-
518
- if not forest_layer:
519
- self.logger.warning("Forest layer not found.")
520
- return
521
-
522
- weights_directory = self.game.weights_dir_path(self.map_directory)
523
- forest_image_path = forest_layer.get_preview_or_path(weights_directory)
524
-
525
- if not forest_image_path or not os.path.isfile(forest_image_path):
526
- self.logger.warning("Forest image not found.")
527
- return
528
-
529
- tree = self._get_tree()
530
- if tree is None:
531
- return
532
-
533
- # Find the <Scene> element in the I3D file.
534
- root = tree.getroot()
535
- scene_node = root.find(".//Scene")
536
- if scene_node is None:
537
- self.logger.warning("Scene element not found in I3D file.")
538
- return
539
-
540
- self.logger.debug("Scene element found in I3D file, starting to add forests.")
541
-
542
- node_id = TREE_NODE_ID_STARTING_VALUE
543
-
544
- # Create <TransformGroup name="trees" translation="0 400 0" nodeId="{node_id}"> element.
545
- trees_node = ET.Element("TransformGroup")
546
- trees_node.set("name", "trees")
547
- trees_node.set("translation", "0 400 0")
548
- trees_node.set("nodeId", str(node_id))
549
- node_id += 1
550
-
551
- # pylint: disable=no-member
552
- forest_image = cv2.imread(forest_image_path, cv2.IMREAD_UNCHANGED)
553
-
554
- tree_count = 0
555
- for x, y in self.non_empty_pixels(forest_image, step=self.map.i3d_settings.forest_density):
556
- xcs, ycs = self.top_left_coordinates_to_center((x, y))
557
- node_id += 1
558
-
559
- rotation = randint(-180, 180)
560
- xcs, ycs = self.randomize_coordinates( # type: ignore
561
- (xcs, ycs), self.map.i3d_settings.forest_density
562
- )
563
-
564
- random_tree = choice(tree_schema) # type: ignore
565
- tree_name = random_tree["name"]
566
- tree_id = random_tree["reference_id"]
567
-
568
- reference_node = ET.Element("ReferenceNode")
569
- reference_node.set("name", tree_name) # type: ignore
570
- reference_node.set("translation", f"{xcs} 0 {ycs}")
571
- reference_node.set("rotation", f"0 {rotation} 0")
572
- reference_node.set("referenceId", str(tree_id))
573
- reference_node.set("nodeId", str(node_id))
574
-
575
- trees_node.append(reference_node)
576
- tree_count += 1
577
-
578
- scene_node.append(trees_node)
579
- self.logger.debug("Added %s trees to the I3D file.", tree_count)
580
-
581
- tree.write(self._map_i3d_path) # type: ignore
582
- self.logger.debug("Map I3D file saved to: %s.", self._map_i3d_path)
583
-
584
- @staticmethod
585
- def randomize_coordinates(coordinates: tuple[int, int], density: int) -> tuple[float, float]:
586
- """Randomizes the coordinates of the point with the given density.
587
-
588
- Arguments:
589
- coordinates (tuple[int, int]): The coordinates of the point.
590
- density (int): The density of the randomization.
591
-
592
- Returns:
593
- tuple[float, float]: The randomized coordinates of the point.
594
- """
595
- MAXIMUM_RELATIVE_SHIFT = 0.2 # pylint: disable=C0103
596
- shift_range = density * MAXIMUM_RELATIVE_SHIFT
597
-
598
- x_shift = uniform(-shift_range, shift_range)
599
- y_shift = uniform(-shift_range, shift_range)
600
-
601
- x, y = coordinates
602
- x += x_shift # type: ignore
603
- y += y_shift # type: ignore
604
-
605
- return x, y
606
-
607
- @staticmethod
608
- def non_empty_pixels(
609
- image: np.ndarray, step: int = 1
610
- ) -> Generator[tuple[int, int], None, None]:
611
- """Receives numpy array, which represents single-channeled image of uint8 type.
612
- Yield coordinates of non-empty pixels (pixels with value greater than 0).
613
-
614
- Arguments:
615
- image (np.ndarray): The image to get non-empty pixels from.
616
- step (int, optional): The step to iterate through the image. Defaults to 1.
617
-
618
- Yields:
619
- tuple[int, int]: The coordinates of non-empty pixels.
620
- """
621
- for y, row in enumerate(image[::step]):
622
- for x, value in enumerate(row[::step]):
623
- if value > 0:
624
- yield x * step, y * step
@@ -1,36 +0,0 @@
1
- maps4fs/__init__.py,sha256=_KfKq_ppTfHH6N7yLHTPjYc0QP2AG1HT8BfYTR9SMJE,853
2
- maps4fs/logger.py,sha256=B-NEYpMjPAAqlV4VpfTi6nbBFnEABVtQOaYe6nMpidg,1489
3
- maps4fs/generator/__init__.py,sha256=zZMLEkGzb4z0xql650gOtGSvcgX58DnJ2yN3vC2daRk,43
4
- maps4fs/generator/background.py,sha256=wLiJQroR4IjxpCekVnm030qnrOR9utoKqOVN5n-UwSQ,25023
5
- maps4fs/generator/component.py,sha256=p2bn37VlTLWu_pqH96GCV7LgKzj5Y0d3yATpXGegFDI,20950
6
- maps4fs/generator/config.py,sha256=0QmK052B8bxyHVhg3jzCORLfOBMMmqVfhhbqXKf6OMk,4383
7
- maps4fs/generator/dem.py,sha256=20gx0dzX0LyO6ipvDitst-BwGfcKogFqgQf9Q2qMH5U,10933
8
- maps4fs/generator/game.py,sha256=GmCl_KQ9D-UwKao4HFEb0PRAm829ThtSZfkgzK3Oh2g,8143
9
- maps4fs/generator/grle.py,sha256=wWYzi7tqYy5f1CVbYyk2T92nJJntahtV7wSsVnrEgqM,21181
10
- maps4fs/generator/i3d.py,sha256=o90zZU1dEzOaBzgrt2C6ymlYIsI8i2Oicqafz7TC1mo,25148
11
- maps4fs/generator/map.py,sha256=a50KQEr1XZKjS_WKXywGwh4OC3gyjY6M8FTc0eNcxpg,10183
12
- maps4fs/generator/qgis.py,sha256=Es8hLuqN_KH8lDfnJE6He2rWYbAKJ3RGPn-o87S6CPI,6116
13
- maps4fs/generator/satellite.py,sha256=_7RcuNmR1mjxEJWMDsjnzKUIqWxnGUn50XtjB7HmSPg,3661
14
- maps4fs/generator/settings.py,sha256=9vbXISQrE-aDY7ATpvZ7LVJMqjfwa3-gNl-huI8XLO0,5666
15
- maps4fs/generator/texture.py,sha256=tH7cbX10WL8WbwmMh_7ndIBNZptNgr_CMGbpGFL3HHM,36584
16
- maps4fs/generator/dtm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
- maps4fs/generator/dtm/bavaria.py,sha256=7njrEvSCYAC8ZVyvS-_84iXHhWA0oHKrEqSzxdnZuGs,4293
18
- maps4fs/generator/dtm/dtm.py,sha256=tbYPlpDbFS374ywHrYIJwRflRRzGzi7M5mmcVqwCYBw,22880
19
- maps4fs/generator/dtm/england.py,sha256=YyCYwnNUJuBeeMNUozfKIj_yNjHpGeuH1Mz0NiAJL-U,1122
20
- maps4fs/generator/dtm/hessen.py,sha256=dYu7TUXICaFMI1sc2l1i3ZDdM5fecXkj5jcz5ZuXIOw,964
21
- maps4fs/generator/dtm/niedersachsen.py,sha256=rHScUGrI8JYseBb3xeSN6mdZ7b28YgdlPVCMlluaAMM,1297
22
- maps4fs/generator/dtm/nrw.py,sha256=1zMa10O0NdQbiTwOa7XXGrx3NhdHUqRXI4yBn_Scb2M,958
23
- maps4fs/generator/dtm/srtm.py,sha256=EYGU20a8c8DnIxzoDAP3rrHYSdgXunwgiJ7R6-FIsuE,4448
24
- maps4fs/generator/dtm/usgs.py,sha256=LYtbtecpluAsfS3vhJ2xpVKPbBWerTIJ2uqaM2UmBEw,3228
25
- maps4fs/generator/dtm/utils.py,sha256=I-wUSA_J85Xbt8sZCZAVKHSIcrMj5Ng-0adtPVhVmk0,2315
26
- maps4fs/generator/dtm/base/wcs.py,sha256=3Irnk9eJPsuRXe1GNpnEK4sWlbpPSJIJUT1V2W-qpKo,2278
27
- maps4fs/generator/dtm/base/wms.py,sha256=tevNGOf9VgGYBtc1TTd_oXWHLjU-nLowTIG8QShWDA0,2197
28
- maps4fs/toolbox/__init__.py,sha256=zZMLEkGzb4z0xql650gOtGSvcgX58DnJ2yN3vC2daRk,43
29
- maps4fs/toolbox/background.py,sha256=9BXWNqs_n3HgqDiPztWylgYk_QM4YgBpe6_ZNQAWtSc,2154
30
- maps4fs/toolbox/custom_osm.py,sha256=X6ZlPqiOhNjkmdD_qVroIfdOl9Rb90cDwVSLDVYgx80,1892
31
- maps4fs/toolbox/dem.py,sha256=z9IPFNmYbjiigb3t02ZenI3Mo8odd19c5MZbjDEovTo,3525
32
- maps4fs-1.8.1.dist-info/LICENSE.md,sha256=pTKD_oUexcn-yccFCTrMeLkZy0ifLRa-VNcDLqLZaIw,10749
33
- maps4fs-1.8.1.dist-info/METADATA,sha256=ByakHrv8vLLlLLtBcBKd74GOhIm8XjjbKd7YcH4MbvI,41661
34
- maps4fs-1.8.1.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
35
- maps4fs-1.8.1.dist-info/top_level.txt,sha256=Ue9DSRlejRQRCaJueB0uLcKrWwsEq9zezfv5dI5mV1M,8
36
- maps4fs-1.8.1.dist-info/RECORD,,