maps4fs 1.6.91__py3-none-any.whl → 1.7.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -188,8 +188,7 @@ class Component:
188
188
  self,
189
189
  coordinates: tuple[float, float] | None = None,
190
190
  distance: int | None = None,
191
- project_utm: bool = False,
192
- ) -> tuple[int, int, int, int]:
191
+ ) -> tuple[float, float, float, float]:
193
192
  """Calculates the bounding box of the map from the coordinates and the height and
194
193
  width of the map.
195
194
  If coordinates and distance are not provided, the instance variables are used.
@@ -199,24 +198,23 @@ class Component:
199
198
  of the map. Defaults to None.
200
199
  distance (int, optional): The distance from the center of the map to the edge of the
201
200
  map in all directions. Defaults to None.
202
- project_utm (bool, optional): Whether to project the bounding box to UTM.
203
201
 
204
202
  Returns:
205
- tuple[int, int, int, int]: The bounding box of the map.
203
+ tuple[float, float, float, float]: The bounding box of the map.
206
204
  """
207
205
  coordinates = coordinates or self.coordinates
208
206
  distance = distance or int(self.map_rotated_size / 2)
209
207
 
210
208
  west, south, east, north = ox.utils_geo.bbox_from_point( # type: ignore
211
- coordinates, dist=distance, project_utm=project_utm
209
+ coordinates,
210
+ dist=distance,
212
211
  )
213
212
 
214
213
  bbox = north, south, east, west
215
214
  self.logger.debug(
216
- "Calculated bounding box for component: %s: %s, project_utm: %s, distance: %s",
215
+ "Calculated bounding box for component: %s: %s, distance: %s",
217
216
  self.__class__.__name__,
218
217
  bbox,
219
- project_utm,
220
218
  distance,
221
219
  )
222
220
  return bbox
@@ -225,7 +223,7 @@ class Component:
225
223
  """Saves the bounding box of the map to the component instance from the coordinates and the
226
224
  height and width of the map.
227
225
  """
228
- self.bbox = self.get_bbox(project_utm=False)
226
+ self.bbox = self.get_bbox()
229
227
  self.logger.debug("Saved bounding box: %s", self.bbox)
230
228
 
231
229
  @property
@@ -544,17 +542,10 @@ class Component:
544
542
  """
545
543
 
546
544
  scaling_factor = 1 / self.map.dem_settings.multiplier
547
- self.logger.debug("Z scaling factor including DEM multiplier: %s", scaling_factor)
548
545
 
549
546
  if self.map.shared_settings.height_scale_multiplier:
550
547
  scaling_factor *= self.map.shared_settings.height_scale_multiplier
551
- self.logger.debug(
552
- "Z scaling factor including height scale multiplier: %s", scaling_factor
553
- )
554
548
  if self.map.shared_settings.mesh_z_scaling_factor:
555
549
  scaling_factor *= 1 / self.map.shared_settings.mesh_z_scaling_factor
556
- self.logger.debug(
557
- "Z scaling factor including mesh z scaling factor: %s", scaling_factor
558
- )
559
550
 
560
551
  return scaling_factor
@@ -183,5 +183,5 @@ class SatelliteSettings(SettingsModel):
183
183
  """
184
184
 
185
185
  download_images: bool = False
186
- satellite_margin: int = 100
186
+ satellite_margin: int = 0
187
187
  zoom_level: int = 14
@@ -336,19 +336,12 @@ class Texture(Component):
336
336
  # pylint: disable=W0201
337
337
  def _read_parameters(self) -> None:
338
338
  """Reads map parameters from OSM data, such as:
339
- - minimum and maximum coordinates in UTM format
339
+ - minimum and maximum coordinates
340
340
  - map dimensions in meters
341
341
  - map coefficients (meters per pixel)
342
342
  """
343
- north, south, east, west = self.get_bbox(project_utm=True)
344
-
345
- # Parameters of the map in UTM format (meters).
346
- self.minimum_x = min(west, east)
347
- self.minimum_y = min(south, north)
348
- self.maximum_x = max(west, east)
349
- self.maximum_y = max(south, north)
350
- self.logger.debug("Map minimum coordinates (XxY): %s x %s.", self.minimum_x, self.minimum_y)
351
- self.logger.debug("Map maximum coordinates (XxY): %s x %s.", self.maximum_x, self.maximum_y)
343
+ bbox = ox.utils_geo.bbox_from_point(self.coordinates, dist=self.map_rotated_size / 2)
344
+ self.minimum_x, self.minimum_y, self.maximum_x, self.maximum_y = bbox
352
345
 
353
346
  def info_sequence(self) -> dict[str, Any]:
354
347
  """Returns the JSON representation of the generation info for textures."""
@@ -576,29 +569,19 @@ class Texture(Component):
576
569
  cv2.imwrite(layer_path, img)
577
570
  self.logger.debug("Base texture %s saved.", layer_path)
578
571
 
579
- def get_relative_x(self, x: float) -> int:
580
- """Converts UTM X coordinate to relative X coordinate in map image.
572
+ def latlon_to_pixel(self, lat: float, lon: float) -> tuple[int, int]:
573
+ """Converts latitude and longitude to pixel coordinates.
581
574
 
582
575
  Arguments:
583
- x (float): UTM X coordinate.
576
+ lat (float): Latitude.
577
+ lon (float): Longitude.
584
578
 
585
579
  Returns:
586
- int: Relative X coordinate in map image.
580
+ tuple[int, int]: Pixel coordinates.
587
581
  """
588
- return int(self.map_rotated_size * (x - self.minimum_x) / (self.maximum_x - self.minimum_x))
589
-
590
- def get_relative_y(self, y: float) -> int:
591
- """Converts UTM Y coordinate to relative Y coordinate in map image.
592
-
593
- Arguments:
594
- y (float): UTM Y coordinate.
595
-
596
- Returns:
597
- int: Relative Y coordinate in map image.
598
- """
599
- return int(
600
- self.map_rotated_size * (1 - (y - self.minimum_y) / (self.maximum_y - self.minimum_y))
601
- )
582
+ x = int((lon - self.minimum_x) / (self.maximum_x - self.minimum_x) * self.map_rotated_size)
583
+ y = int((lat - self.maximum_y) / (self.minimum_y - self.maximum_y) * self.map_rotated_size)
584
+ return x, y
602
585
 
603
586
  def np_to_polygon_points(self, np_array: np.ndarray) -> list[tuple[int, int]]:
604
587
  """Converts numpy array of polygon points to list of tuples.
@@ -623,11 +606,13 @@ class Texture(Component):
623
606
  Returns:
624
607
  np.ndarray: Numpy array of polygon points.
625
608
  """
626
- xs, ys = geometry.exterior.coords.xy
627
- xs = [int(self.get_relative_x(x)) for x in xs.tolist()]
628
- ys = [int(self.get_relative_y(y)) for y in ys.tolist()]
629
- pairs = list(zip(xs, ys))
630
- return np.array(pairs, dtype=np.int32).reshape((-1, 1, 2))
609
+ coords = list(geometry.exterior.coords)
610
+ pts = np.array(
611
+ [self.latlon_to_pixel(coord[1], coord[0]) for coord in coords],
612
+ np.int32,
613
+ )
614
+ pts = pts.reshape((-1, 1, 2))
615
+ return pts
631
616
 
632
617
  def _to_polygon(
633
618
  self, obj: pd.core.series.Series, width: int | None
@@ -664,9 +649,20 @@ class Texture(Component):
664
649
  Returns:
665
650
  shapely.geometry.polygon.Polygon: Polygon geometry.
666
651
  """
667
- polygon = geometry.buffer(width)
652
+ polygon = geometry.buffer(self.meters_to_degrees(width) if width else 0)
668
653
  return polygon
669
654
 
655
+ def meters_to_degrees(self, meters: int) -> float:
656
+ """Converts meters to degrees.
657
+
658
+ Arguments:
659
+ meters (int): Meters.
660
+
661
+ Returns:
662
+ float: Degrees.
663
+ """
664
+ return meters / 111320
665
+
670
666
  def _skip(
671
667
  self, geometry: shapely.geometry.polygon.Polygon, *args, **kwargs
672
668
  ) -> shapely.geometry.polygon.Polygon:
@@ -724,46 +720,43 @@ class Texture(Component):
724
720
  except Exception as e: # pylint: disable=W0718
725
721
  self.logger.debug("Error fetching objects for tags: %s. Error: %s.", tags, e)
726
722
  return
727
- objects_utm = ox.projection.project_gdf(objects, to_latlong=False)
728
- self.logger.debug("Fetched %s elements for tags: %s.", len(objects_utm), tags)
723
+ self.logger.debug("Fetched %s elements for tags: %s.", len(objects), tags)
729
724
 
730
725
  method = self.linestrings_generator if yield_linestrings else self.polygons_generator
731
726
 
732
- yield from method(objects_utm, width, is_fieds)
727
+ yield from method(objects, width, is_fieds)
733
728
 
734
729
  def linestrings_generator(
735
- self, objects_utm: pd.core.frame.DataFrame, *args, **kwargs
730
+ self, objects: pd.core.frame.DataFrame, *args, **kwargs
736
731
  ) -> Generator[list[tuple[int, int]], None, None]:
737
732
  """Generator which yields lists of point coordinates which represent LineStrings from OSM.
738
733
 
739
734
  Arguments:
740
- objects_utm (pd.core.frame.DataFrame): Dataframe with OSM objects in UTM format.
735
+ objects (pd.core.frame.DataFrame): Dataframe with OSM objects.
741
736
 
742
737
  Yields:
743
738
  Generator[list[tuple[int, int]], None, None]: List of point coordinates.
744
739
  """
745
- for _, obj in objects_utm.iterrows():
740
+ for _, obj in objects.iterrows():
746
741
  geometry = obj["geometry"]
747
742
  if isinstance(geometry, shapely.geometry.linestring.LineString):
748
- points = [
749
- (self.get_relative_x(x), self.get_relative_y(y)) for x, y in geometry.coords
750
- ]
743
+ points = [self.latlon_to_pixel(x, y) for y, x in geometry.coords]
751
744
  yield points
752
745
 
753
746
  def polygons_generator(
754
- self, objects_utm: pd.core.frame.DataFrame, width: int | None, is_fieds: bool
747
+ self, objects: pd.core.frame.DataFrame, width: int | None, is_fieds: bool
755
748
  ) -> Generator[np.ndarray, None, None]:
756
749
  """Generator which yields numpy arrays of polygons from OSM data.
757
750
 
758
751
  Arguments:
759
- objects_utm (pd.core.frame.DataFrame): Dataframe with OSM objects in UTM format.
752
+ objects (pd.core.frame.DataFrame): Dataframe with OSM objects.
760
753
  width (int | None): Width of the polygon in meters (only for LineString).
761
754
  is_fieds (bool): Flag to determine if the fields should be padded.
762
755
 
763
756
  Yields:
764
757
  Generator[np.ndarray, None, None]: Numpy array of polygon points.
765
758
  """
766
- for _, obj in objects_utm.iterrows():
759
+ for _, obj in objects.iterrows():
767
760
  try:
768
761
  polygon = self._to_polygon(obj, width)
769
762
  except Exception as e: # pylint: disable=W0703
@@ -773,7 +766,9 @@ class Texture(Component):
773
766
  continue
774
767
 
775
768
  if is_fieds and self.map.texture_settings.fields_padding > 0:
776
- padded_polygon = polygon.buffer(-self.map.texture_settings.fields_padding)
769
+ padded_polygon = polygon.buffer(
770
+ -self.meters_to_degrees(self.map.texture_settings.fields_padding)
771
+ )
777
772
 
778
773
  if not isinstance(padded_polygon, shapely.geometry.polygon.Polygon):
779
774
  self.logger.warning("The padding value is too high, field will not padded.")
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: maps4fs
3
- Version: 1.6.91
3
+ Version: 1.7.1
4
4
  Summary: Generate map templates for Farming Simulator from real places.
5
5
  Author-email: iwatkot <iwatkot@gmail.com>
6
6
  License: MIT License
@@ -69,16 +69,15 @@ Requires-Dist: pydantic
69
69
  </div>
70
70
 
71
71
  🗺️ Supports 2x2, 4x4, 8x8, 16x16 and any custom size maps<br>
72
- 🔄 Support map rotation 🆕<br>
72
+ 🔄 Support map rotation<br>
73
73
  🌐 Supports custom [DTM Providers](#DTM-Providers) 🆕<br>
74
- 🌾 Automatically generates fields 🆕<br>
75
- 🌽 Automatically generates farmlands 🆕<br>
76
- 🌿 Automatically generates decorative foliage 🆕<br>
77
- 🌲 Automatically generates forests 🆕<br>
78
- 🌊 Automatically generates water planes 🆕<br>
74
+ 🌾 Automatically generates fields<br>
75
+ 🌽 Automatically generates farmlands<br>
76
+ 🌿 Automatically generates decorative foliage<br>
77
+ 🌲 Automatically generates forests<br>
78
+ 🌊 Automatically generates water planes<br>
79
79
  📈 Automatically generates splines 🆕<br>
80
80
  🛰️ Automatically downloads high resolution satellite images 🆕<br>
81
- 🏔️ Allows to use multiple DTM providers for elevation models 🆕<br>
82
81
  🌍 Based on real-world data from OpenStreetMap<br>
83
82
  🗺️ Supports [custom OSM maps](/docs/custom_osm.md)<br>
84
83
  🏞️ Generates height map using SRTM dataset<br>
@@ -99,10 +98,14 @@ Requires-Dist: pydantic
99
98
  🌿 Automatically generates decorative foliage.<br><br>
100
99
  <img src="https://github.com/user-attachments/assets/27a5e541-a9f5-4504-b8d2-64aae9fb3e52"><br>
101
100
  🌲 Automatically generates forests.<br><br>
101
+ <img src="https://github.com/user-attachments/assets/891911d7-081d-431e-a677-b4ae96870286"><br>
102
+ 🌲 Allows to select trees for generation.<br><br>
102
103
  <img src="https://github.com/user-attachments/assets/cce7d4e0-cba2-4dd2-b22d-03137fb2e860"><br>
103
104
  🌊 Automatically generates water planes.<br><br>
104
105
  <img src="https://github.com/user-attachments/assets/0b05b511-a595-48e7-a353-8298081314a4"><br>
105
106
  📈 Automatically generates splines.<br><br>
107
+ <img src="https://github.com/user-attachments/assets/0957db9e-7b95-4951-969c-9d1edd9f073b"><br>
108
+ 🖌️ Allows customization of the texture schema.<br><br>
106
109
  <img src="https://github.com/user-attachments/assets/80e5923c-22c7-4dc0-8906-680902511f3a"><br>
107
110
  🗒️ True-to-life blueprints for fast and precise modding.<br><br>
108
111
  <img width="480" src="https://github.com/user-attachments/assets/1a8802d2-6a3b-4bfa-af2b-7c09478e199b"><br>
@@ -259,6 +262,8 @@ Tools are divided into categories, which are listed below.
259
262
  #### For custom schemas
260
263
  - **Tree Schema Editor** - allows you to view all the supported trees models and select the ones you need on your map. After it, you should click the Show updated schema button and copy the JSON schema to the clipboard. Then you can use it in the Expert settings to generate the map with the selected trees.
261
264
 
265
+ - **Texture Schema Editor** - allows you to view all the supported textures and edit their parameters, such as priority, OSM tags and so on. After editing, you should click the Show updated schema button and copy the JSON schema to the clipboard. Then you can use it in the Expert settings to generate the map with the updated textures.
266
+
262
267
  #### For Textures and DEM
263
268
  - **GeoTIFF windowing** - allows you to upload your GeoTIFF file and select the region of interest to extract it from the image. It's useful when you have high-resolution DEM data and want to create a height map using it.
264
269
 
@@ -2,7 +2,7 @@ maps4fs/__init__.py,sha256=LrWSsyWaU28Dzcs7sRycywO_LvM-j34UvtafyBhvdx4,490
2
2
  maps4fs/logger.py,sha256=B-NEYpMjPAAqlV4VpfTi6nbBFnEABVtQOaYe6nMpidg,1489
3
3
  maps4fs/generator/__init__.py,sha256=zZMLEkGzb4z0xql650gOtGSvcgX58DnJ2yN3vC2daRk,43
4
4
  maps4fs/generator/background.py,sha256=tV4UXvtkNN-OSvv6ujp4jFWRU1xGBgEvSakVGZ1H4nc,24877
5
- maps4fs/generator/component.py,sha256=vn_ThQw3OTcloqYuJWC7vghAvIAnwJsybEm7qMwvsZk,21356
5
+ maps4fs/generator/component.py,sha256=pbpGaWy5C0UzxpcJ72HPY2gMol98snDr-bvNZSX4yY0,20823
6
6
  maps4fs/generator/config.py,sha256=0QmK052B8bxyHVhg3jzCORLfOBMMmqVfhhbqXKf6OMk,4383
7
7
  maps4fs/generator/dem.py,sha256=20gx0dzX0LyO6ipvDitst-BwGfcKogFqgQf9Q2qMH5U,10933
8
8
  maps4fs/generator/game.py,sha256=Nf5r2ubV4YVAVHGzJyhbF2GnOC0qV3HlHYIZBCWciHs,7992
@@ -11,8 +11,8 @@ maps4fs/generator/i3d.py,sha256=pUyHKWKcw43xVCf3Y8iabtbQba05LYxMHi8vziGksIA,2484
11
11
  maps4fs/generator/map.py,sha256=a50KQEr1XZKjS_WKXywGwh4OC3gyjY6M8FTc0eNcxpg,10183
12
12
  maps4fs/generator/qgis.py,sha256=Es8hLuqN_KH8lDfnJE6He2rWYbAKJ3RGPn-o87S6CPI,6116
13
13
  maps4fs/generator/satellite.py,sha256=_7RcuNmR1mjxEJWMDsjnzKUIqWxnGUn50XtjB7HmSPg,3661
14
- maps4fs/generator/settings.py,sha256=3ASf3hW1nkGt8_3IOvKIKNUd6XAHYTAA8FquuhpSUlU,5668
15
- maps4fs/generator/texture.py,sha256=gIXCHU1vT3evbkaXAV9gLUrgI1wH3xJLgWAtZgFruj0,34013
14
+ maps4fs/generator/settings.py,sha256=9vbXISQrE-aDY7ATpvZ7LVJMqjfwa3-gNl-huI8XLO0,5666
15
+ maps4fs/generator/texture.py,sha256=tDC9lIx2qn8d09Gu6PW_Lbq7EK7s1N4l25p50v92xl8,33548
16
16
  maps4fs/generator/dtm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
17
  maps4fs/generator/dtm/dtm.py,sha256=nCQKQygARLxaz4HkREQQd0Yb03oKOf1Iav5_VoZsFWI,9819
18
18
  maps4fs/generator/dtm/srtm.py,sha256=2-pX6bWrJX6gr8IM7ueX6mm_PW7_UQ58MtdzDHae2OQ,9030
@@ -20,8 +20,8 @@ maps4fs/generator/dtm/usgs.py,sha256=hwVjoSNTNRU6hwnfwJ2d3rOdtOjadCmx2QESA2REn6s
20
20
  maps4fs/toolbox/__init__.py,sha256=zZMLEkGzb4z0xql650gOtGSvcgX58DnJ2yN3vC2daRk,43
21
21
  maps4fs/toolbox/background.py,sha256=9BXWNqs_n3HgqDiPztWylgYk_QM4YgBpe6_ZNQAWtSc,2154
22
22
  maps4fs/toolbox/dem.py,sha256=z9IPFNmYbjiigb3t02ZenI3Mo8odd19c5MZbjDEovTo,3525
23
- maps4fs-1.6.91.dist-info/LICENSE.md,sha256=pTKD_oUexcn-yccFCTrMeLkZy0ifLRa-VNcDLqLZaIw,10749
24
- maps4fs-1.6.91.dist-info/METADATA,sha256=1yIT5j9FOyB83FGk2lNExtLmEA3w-V1yeKiobQEbn40,39161
25
- maps4fs-1.6.91.dist-info/WHEEL,sha256=A3WOREP4zgxI0fKrHUG8DC8013e3dK3n7a6HDbcEIwE,91
26
- maps4fs-1.6.91.dist-info/top_level.txt,sha256=Ue9DSRlejRQRCaJueB0uLcKrWwsEq9zezfv5dI5mV1M,8
27
- maps4fs-1.6.91.dist-info/RECORD,,
23
+ maps4fs-1.7.1.dist-info/LICENSE.md,sha256=pTKD_oUexcn-yccFCTrMeLkZy0ifLRa-VNcDLqLZaIw,10749
24
+ maps4fs-1.7.1.dist-info/METADATA,sha256=QMIGgmKUnYvtc-Ln_rxPm7WeF8Q9L82Eo_aY2g2Uc8c,39697
25
+ maps4fs-1.7.1.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
26
+ maps4fs-1.7.1.dist-info/top_level.txt,sha256=Ue9DSRlejRQRCaJueB0uLcKrWwsEq9zezfv5dI5mV1M,8
27
+ maps4fs-1.7.1.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.7.0)
2
+ Generator: setuptools (75.8.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5