maps4fs 1.5.7__py3-none-any.whl → 1.5.9__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/__init__.py CHANGED
@@ -1,5 +1,7 @@
1
1
  # pylint: disable=missing-module-docstring
2
- from maps4fs.generator.dtm import DTMProvider
2
+ from maps4fs.generator.dtm.dtm import DTMProvider
3
+ from maps4fs.generator.dtm.srtm import SRTM30Provider
4
+ from maps4fs.generator.dtm.usgs import USGS1mProvider
3
5
  from maps4fs.generator.game import Game
4
6
  from maps4fs.generator.map import Map
5
7
  from maps4fs.generator.settings import (
maps4fs/generator/dem.py CHANGED
@@ -9,7 +9,7 @@ import numpy as np
9
9
  from pympler import asizeof # type: ignore
10
10
 
11
11
  from maps4fs.generator.component import Component
12
- from maps4fs.generator.dtm import DTMProvider
12
+ from maps4fs.generator.dtm.dtm import DTMProvider
13
13
 
14
14
 
15
15
  # pylint: disable=R0903, R0902
File without changes
@@ -4,10 +4,7 @@ and specific settings for downloading and processing the data."""
4
4
 
5
5
  from __future__ import annotations
6
6
 
7
- import gzip
8
- import math
9
7
  import os
10
- import shutil
11
8
  from typing import Type
12
9
 
13
10
  import numpy as np
@@ -263,71 +260,3 @@ class DTMProvider:
263
260
  raise ValueError("No data in the tile.")
264
261
 
265
262
  return data
266
-
267
-
268
- class SRTM30Provider(DTMProvider):
269
- """Provider of Shuttle Radar Topography Mission (SRTM) 30m data."""
270
-
271
- _code = "srtm30"
272
- _name = "SRTM 30 m"
273
- _region = "Global"
274
- _icon = "🌎"
275
- _resolution = 30.0
276
-
277
- _url = "https://elevation-tiles-prod.s3.amazonaws.com/skadi/{latitude_band}/{tile_name}.hgt.gz"
278
-
279
- _author = "[iwatkot](https://github.com/iwatkot)"
280
-
281
- def __init__(self, *args, **kwargs):
282
- super().__init__(*args, **kwargs)
283
- self.hgt_directory = os.path.join(self._tile_directory, "hgt")
284
- self.gz_directory = os.path.join(self._tile_directory, "gz")
285
- os.makedirs(self.hgt_directory, exist_ok=True)
286
- os.makedirs(self.gz_directory, exist_ok=True)
287
-
288
- def get_tile_parameters(self, *args, **kwargs) -> dict[str, str]:
289
- """Returns latitude band and tile name for SRTM tile from coordinates.
290
-
291
- Arguments:
292
- lat (float): Latitude.
293
- lon (float): Longitude.
294
-
295
- Returns:
296
- dict: Tile parameters.
297
- """
298
- lat, lon = args
299
-
300
- tile_latitude = math.floor(lat)
301
- tile_longitude = math.floor(lon)
302
-
303
- latitude_band = f"N{abs(tile_latitude):02d}" if lat >= 0 else f"S{abs(tile_latitude):02d}"
304
- if lon < 0:
305
- tile_name = f"{latitude_band}W{abs(tile_longitude):03d}"
306
- else:
307
- tile_name = f"{latitude_band}E{abs(tile_longitude):03d}"
308
-
309
- self.logger.debug(
310
- "Detected tile name: %s for coordinates: lat %s, lon %s.", tile_name, lat, lon
311
- )
312
- return {"latitude_band": latitude_band, "tile_name": tile_name}
313
-
314
- def get_numpy(self) -> np.ndarray:
315
- """Get numpy array of the tile.
316
-
317
- Returns:
318
- np.ndarray: Numpy array of the tile.
319
- """
320
- tile_parameters = self.get_tile_parameters(*self.coordinates)
321
- tile_name = tile_parameters["tile_name"]
322
- decompressed_tile_path = os.path.join(self.hgt_directory, f"{tile_name}.hgt")
323
-
324
- if not os.path.isfile(decompressed_tile_path):
325
- compressed_tile_path = os.path.join(self.gz_directory, f"{tile_name}.hgt.gz")
326
- if not self.get_or_download_tile(compressed_tile_path, **tile_parameters):
327
- raise FileNotFoundError(f"Tile {tile_name} not found.")
328
-
329
- with gzip.open(compressed_tile_path, "rb") as f_in:
330
- with open(decompressed_tile_path, "wb") as f_out:
331
- shutil.copyfileobj(f_in, f_out)
332
-
333
- return self.extract_roi(decompressed_tile_path)
@@ -0,0 +1,85 @@
1
+ """This module contains provider of Shuttle Radar Topography Mission (SRTM) 30m data."""
2
+
3
+ import gzip
4
+ import math
5
+ import os
6
+ import shutil
7
+
8
+ import numpy as np
9
+
10
+ from maps4fs.generator.dtm.dtm import DTMProvider
11
+
12
+
13
+ class SRTM30Provider(DTMProvider):
14
+ """Provider of Shuttle Radar Topography Mission (SRTM) 30m data."""
15
+
16
+ _code = "srtm30"
17
+ _name = "SRTM 30 m"
18
+ _region = "Global"
19
+ _icon = "🌎"
20
+ _resolution = 30.0
21
+
22
+ _url = "https://elevation-tiles-prod.s3.amazonaws.com/skadi/{latitude_band}/{tile_name}.hgt.gz"
23
+
24
+ _author = "[iwatkot](https://github.com/iwatkot)"
25
+
26
+ _instructions = (
27
+ "ℹ️ Set the Multiplier value in the DEM Settings, when using this DTM provider. "
28
+ "Otherwise, the dem file will contain values in meters exactly as on Earth "
29
+ "and you probably won't see any terrain by eye. "
30
+ "Note that the multiplier value may be big enough to make the terrain visible."
31
+ )
32
+
33
+ def __init__(self, *args, **kwargs):
34
+ super().__init__(*args, **kwargs)
35
+ self.hgt_directory = os.path.join(self._tile_directory, "hgt")
36
+ self.gz_directory = os.path.join(self._tile_directory, "gz")
37
+ os.makedirs(self.hgt_directory, exist_ok=True)
38
+ os.makedirs(self.gz_directory, exist_ok=True)
39
+
40
+ def get_tile_parameters(self, *args, **kwargs) -> dict[str, str]:
41
+ """Returns latitude band and tile name for SRTM tile from coordinates.
42
+
43
+ Arguments:
44
+ lat (float): Latitude.
45
+ lon (float): Longitude.
46
+
47
+ Returns:
48
+ dict: Tile parameters.
49
+ """
50
+ lat, lon = args
51
+
52
+ tile_latitude = math.floor(lat)
53
+ tile_longitude = math.floor(lon)
54
+
55
+ latitude_band = f"N{abs(tile_latitude):02d}" if lat >= 0 else f"S{abs(tile_latitude):02d}"
56
+ if lon < 0:
57
+ tile_name = f"{latitude_band}W{abs(tile_longitude):03d}"
58
+ else:
59
+ tile_name = f"{latitude_band}E{abs(tile_longitude):03d}"
60
+
61
+ self.logger.debug(
62
+ "Detected tile name: %s for coordinates: lat %s, lon %s.", tile_name, lat, lon
63
+ )
64
+ return {"latitude_band": latitude_band, "tile_name": tile_name}
65
+
66
+ def get_numpy(self) -> np.ndarray:
67
+ """Get numpy array of the tile.
68
+
69
+ Returns:
70
+ np.ndarray: Numpy array of the tile.
71
+ """
72
+ tile_parameters = self.get_tile_parameters(*self.coordinates)
73
+ tile_name = tile_parameters["tile_name"]
74
+ decompressed_tile_path = os.path.join(self.hgt_directory, f"{tile_name}.hgt")
75
+
76
+ if not os.path.isfile(decompressed_tile_path):
77
+ compressed_tile_path = os.path.join(self.gz_directory, f"{tile_name}.hgt.gz")
78
+ if not self.get_or_download_tile(compressed_tile_path, **tile_parameters):
79
+ raise FileNotFoundError(f"Tile {tile_name} not found.")
80
+
81
+ with gzip.open(compressed_tile_path, "rb") as f_in:
82
+ with open(decompressed_tile_path, "wb") as f_out:
83
+ shutil.copyfileobj(f_in, f_out)
84
+
85
+ return self.extract_roi(decompressed_tile_path)
@@ -0,0 +1,323 @@
1
+ """This module contains provider of USGS 1m data."""
2
+
3
+ import os
4
+ from datetime import datetime
5
+
6
+ import numpy as np
7
+ import rasterio # type: ignore
8
+ import requests
9
+ from rasterio._warp import Resampling # type: ignore # pylint: disable=E0611
10
+ from rasterio.merge import merge # type: ignore
11
+ from rasterio.warp import calculate_default_transform, reproject # type: ignore
12
+ from rasterio.windows import from_bounds # type: ignore
13
+
14
+ from maps4fs.generator.dtm.dtm import DTMProvider, DTMProviderSettings
15
+
16
+
17
+ class USGS1mProviderSettings(DTMProviderSettings):
18
+ """Settings for the USGS 1m provider."""
19
+
20
+ max_local_elevation: int = 255
21
+
22
+
23
+ # pylint: disable=W0223
24
+ class USGS1mProvider(DTMProvider):
25
+ """Provider of USGS."""
26
+
27
+ _code = "USGS1m"
28
+ _name = "USGS 1m"
29
+ _region = "USA"
30
+ _icon = "🇺🇸"
31
+ _resolution = 1
32
+ _data: np.ndarray | None = None
33
+ _settings = USGS1mProviderSettings
34
+ _author = "[ZenJakey](https://github.com/ZenJakey)"
35
+ _is_community = True
36
+
37
+ _url = (
38
+ "https://tnmaccess.nationalmap.gov/api/v1/products?prodFormats=GeoTIFF,IMG&prodExtents="
39
+ "10000 x 10000 meter&datasets=Digital Elevation Model (DEM) 1 meter&polygon="
40
+ )
41
+
42
+ def __init__(self, *args, **kwargs):
43
+ super().__init__(*args, **kwargs)
44
+ timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
45
+ self.shared_tiff_path = os.path.join(self._tile_directory, "shared")
46
+ os.makedirs(self.shared_tiff_path, exist_ok=True)
47
+ self.output_path = os.path.join(self._tile_directory, f"timestamp_{timestamp}")
48
+ os.makedirs(self.output_path, exist_ok=True)
49
+
50
+ def get_download_urls(self) -> list[str]:
51
+ """Get download URLs of the GeoTIFF files from the USGS API.
52
+
53
+ Returns:
54
+ list: List of download URLs.
55
+ """
56
+ urls = []
57
+ try:
58
+ # Make the GET request
59
+ (north, south, east, west) = self.get_bbox()
60
+ response = requests.get( # pylint: disable=W3101
61
+ self.url # type: ignore
62
+ + f"{west} {south},{east} {south},{east} {north},{west} {north},{west} {south}&="
63
+ )
64
+ self.logger.debug("Getting file locations from USGS...")
65
+
66
+ # Check if the request was successful (HTTP status code 200)
67
+ if response.status_code == 200:
68
+ # Parse the JSON response
69
+ json_data = response.json()
70
+ items = json_data["items"]
71
+ for item in items:
72
+ urls.append(item["downloadURL"])
73
+ self.download_tif_files(urls)
74
+ else:
75
+ self.logger.error("Failed to get data. HTTP Status Code: %s", response.status_code)
76
+ except requests.exceptions.RequestException as e:
77
+ self.logger.error("Failed to get data. Error: %s", e)
78
+ self.logger.debug("Received %s urls", len(urls))
79
+ return urls
80
+
81
+ def download_tif_files(self, urls: list[str]) -> list[str]:
82
+ """Download GeoTIFF files from the given URLs.
83
+
84
+ Arguments:
85
+ urls (list): List of URLs to download GeoTIFF files from.
86
+
87
+ Returns:
88
+ list: List of paths to the downloaded GeoTIFF files.
89
+ """
90
+ tif_files = []
91
+ for url in urls:
92
+ file_name = os.path.basename(url)
93
+ self.logger.debug("Retrieving TIFF: %s", file_name)
94
+ file_path = os.path.join(self.shared_tiff_path, file_name)
95
+ if not os.path.exists(file_path):
96
+ try:
97
+ # Send a GET request to the file URL
98
+ response = requests.get(url, stream=True) # pylint: disable=W3101
99
+ response.raise_for_status() # Raise an error for HTTP status codes 4xx/5xx
100
+
101
+ # Write the content of the response to the file
102
+ with open(file_path, "wb") as file:
103
+ for chunk in response.iter_content(chunk_size=8192): # Download in chunks
104
+ file.write(chunk)
105
+ self.logger.info("File downloaded successfully: %s", file_path)
106
+ except requests.exceptions.RequestException as e:
107
+ self.logger.error("Failed to download file: %s", e)
108
+ else:
109
+ self.logger.debug("File already exists: %s", file_name)
110
+
111
+ tif_files.append(file_path)
112
+ return tif_files
113
+
114
+ def merge_geotiff(self, input_files: list[str], output_file: str) -> None:
115
+ """Merge multiple GeoTIFF files into a single GeoTIFF file.
116
+
117
+ Arguments:
118
+ input_files (list): List of input GeoTIFF files to merge.
119
+ output_file (str): Path to save the merged GeoTIFF file.
120
+ """
121
+ # Open all input GeoTIFF files as datasets
122
+ self.logger.debug("Merging tiff files...")
123
+ datasets = [rasterio.open(file) for file in input_files]
124
+
125
+ # Merge datasets
126
+ mosaic, out_transform = merge(datasets)
127
+
128
+ # Get metadata from the first file and update it for the output
129
+ out_meta = datasets[0].meta.copy()
130
+ out_meta.update(
131
+ {
132
+ "driver": "GTiff",
133
+ "height": mosaic.shape[1],
134
+ "width": mosaic.shape[2],
135
+ "transform": out_transform,
136
+ "count": mosaic.shape[0], # Number of bands
137
+ }
138
+ )
139
+
140
+ # Write merged GeoTIFF to the output file
141
+ with rasterio.open(output_file, "w", **out_meta) as dest:
142
+ dest.write(mosaic)
143
+
144
+ self.logger.debug("GeoTIFF images merged successfully into %s", output_file)
145
+
146
+ def reproject_geotiff(self, input_tiff: str, output_tiff: str, target_crs: str) -> None:
147
+ """Reproject a GeoTIFF file to a new coordinate reference system (CRS).
148
+
149
+ Arguments:
150
+ input_tiff (str): Path to the input GeoTIFF file.
151
+ output_tiff (str): Path to save the reprojected GeoTIFF file.
152
+ target_crs (str): Target CRS (e.g., EPSG:4326 for CRS:84).
153
+ """
154
+ # Open the source GeoTIFF
155
+ self.logger.debug("Reprojecting GeoTIFF to %s CRS...", target_crs)
156
+ with rasterio.open(input_tiff) as src:
157
+ # Get the transform, width, and height of the target CRS
158
+ transform, width, height = calculate_default_transform(
159
+ src.crs, target_crs, src.width, src.height, *src.bounds
160
+ )
161
+
162
+ # Update the metadata for the target GeoTIFF
163
+ kwargs = src.meta.copy()
164
+ kwargs.update(
165
+ {"crs": target_crs, "transform": transform, "width": width, "height": height}
166
+ )
167
+
168
+ # Open the destination GeoTIFF file and reproject
169
+ with rasterio.open(output_tiff, "w", **kwargs) as dst:
170
+ for i in range(1, src.count + 1): # Iterate over all raster bands
171
+ reproject(
172
+ source=rasterio.band(src, i),
173
+ destination=rasterio.band(dst, i),
174
+ src_transform=src.transform,
175
+ src_crs=src.crs,
176
+ dst_transform=transform,
177
+ dst_crs=target_crs,
178
+ resampling=Resampling.nearest, # Choose resampling method
179
+ )
180
+ self.logger.debug("Reprojected GeoTIFF saved to %s", output_tiff)
181
+
182
+ def extract_roi(self, input_tiff: str) -> np.ndarray: # pylint: disable=W0237
183
+ """
184
+ Crop a GeoTIFF based on given geographic bounding box and save to a new file.
185
+
186
+ Arguments:
187
+ input_tiff (str): Path to the input GeoTIFF file.
188
+
189
+ Returns:
190
+ np.ndarray: Numpy array of the cropped GeoTIFF.
191
+ """
192
+ self.logger.debug("Extracting ROI...")
193
+ # Open the input GeoTIFF
194
+ with rasterio.open(input_tiff) as src:
195
+
196
+ # Create a rasterio window from the bounding box
197
+ (north, south, east, west) = self.get_bbox()
198
+ window = from_bounds(west, south, east, north, transform=src.transform)
199
+
200
+ data = src.read(1, window=window)
201
+ self.logger.debug("Extracted ROI")
202
+ return data
203
+
204
+ # pylint: disable=R0914, R0917, R0913
205
+ def convert_geotiff_to_geotiff(
206
+ self,
207
+ input_tiff: str,
208
+ output_tiff: str,
209
+ min_height: float,
210
+ max_height: float,
211
+ target_crs: str,
212
+ ) -> None:
213
+ """
214
+ Convert a GeoTIFF to a scaled GeoTIFF with UInt16 values using a specific coordinate
215
+ system and output size.
216
+
217
+ Arguments:
218
+ input_tiff (str): Path to the input GeoTIFF file.
219
+ output_tiff (str): Path to save the output GeoTIFF file.
220
+ min_height (float): Minimum terrain height (input range).
221
+ max_height (float): Maximum terrain height (input range).
222
+ target_crs (str): Target CRS (e.g., EPSG:4326 for CRS:84).
223
+ """
224
+ # Open the input GeoTIFF file
225
+ self.logger.debug("Converting to uint16")
226
+ with rasterio.open(input_tiff) as src:
227
+ # Ensure the input CRS matches the target CRS (reprojection may be required)
228
+ if str(src.crs) != str(target_crs):
229
+ raise ValueError(
230
+ f"The GeoTIFF CRS is {src.crs}, but the target CRS is {target_crs}. "
231
+ "Reprojection may be required."
232
+ )
233
+
234
+ # Read the data from the first band
235
+ data = src.read(1) # Assuming the input GeoTIFF has only a single band
236
+
237
+ # Identify the input file's NoData value
238
+ input_nodata = src.nodata
239
+ if input_nodata is None:
240
+ input_nodata = -999999.0 # Default fallback if no NoData value is defined
241
+ nodata_value = 0
242
+ # Replace NoData values (e.g., -999999.0) with the new NoData value
243
+ # (e.g., 65535 for UInt16)
244
+ data[data == input_nodata] = nodata_value
245
+
246
+ # Scale the data to the 0–65535 range (UInt16), avoiding NoData areas
247
+ scaled_data = np.clip(
248
+ (data - min_height) * (65535 / (max_height - min_height)), 0, 65535
249
+ ).astype(np.uint16)
250
+ scaled_data[data == nodata_value] = (
251
+ nodata_value # Preserve NoData value in the scaled array
252
+ )
253
+
254
+ # Compute the proper transform to ensure consistency
255
+ # Get the original transform, width, and height
256
+ transform = src.transform
257
+ width = src.width
258
+ height = src.height
259
+ left, bottom, right, top = src.bounds
260
+
261
+ # Adjust the transform matrix to make sure bounds and transform align correctly
262
+ transform = rasterio.transform.from_bounds(left, bottom, right, top, width, height)
263
+
264
+ # Prepare metadata for the output GeoTIFF
265
+ metadata = src.meta.copy()
266
+ metadata.update(
267
+ {
268
+ "dtype": rasterio.uint16, # Update dtype for uint16
269
+ "crs": target_crs, # Update CRS if needed
270
+ "nodata": nodata_value, # Set the new NoData value
271
+ "transform": transform, # Use the updated, consistent transform
272
+ }
273
+ )
274
+
275
+ # Write the scaled data to the output GeoTIFF
276
+ with rasterio.open(output_tiff, "w", **metadata) as dst:
277
+ dst.write(scaled_data, 1) # Write the first band
278
+
279
+ self.logger.debug(
280
+ "GeoTIFF successfully converted and saved to %s, with nodata value: %s.",
281
+ output_tiff,
282
+ nodata_value,
283
+ )
284
+
285
+ def generate_data(self) -> np.ndarray:
286
+ """Generate data from the USGS 1m provider.
287
+
288
+ Returns:
289
+ np.ndarray: Numpy array of the data.
290
+ """
291
+ download_urls = self.get_download_urls()
292
+ all_tif_files = self.download_tif_files(download_urls)
293
+ self.merge_geotiff(all_tif_files, os.path.join(self.output_path, "merged.tif"))
294
+ self.reproject_geotiff(
295
+ os.path.join(self.output_path, "merged.tif"),
296
+ os.path.join(self.output_path, "reprojected.tif"),
297
+ "EPSG:4326",
298
+ )
299
+ self.convert_geotiff_to_geotiff(
300
+ os.path.join(self.output_path, "reprojected.tif"),
301
+ os.path.join(self.output_path, "translated.tif"),
302
+ min_height=0,
303
+ max_height=self.user_settings.max_local_elevation, # type: ignore
304
+ target_crs="EPSG:4326",
305
+ )
306
+ return self.extract_roi(os.path.join(self.output_path, "translated.tif"))
307
+
308
+ def get_numpy(self) -> np.ndarray:
309
+ """Get numpy array of the tile.
310
+
311
+ Returns:
312
+ np.ndarray: Numpy array of the tile.
313
+ """
314
+ if not self.user_settings:
315
+ raise ValueError("user_settings is 'none'")
316
+ if self.user_settings.max_local_elevation <= 0: # type: ignore
317
+ raise ValueError(
318
+ "Entered 'max_local_elevation' value is unable to be used. "
319
+ "Use a value greater than 0."
320
+ )
321
+ if not self._data:
322
+ self._data = self.generate_data()
323
+ return self._data
maps4fs/generator/map.py CHANGED
@@ -8,7 +8,7 @@ import shutil
8
8
  from typing import Any, Generator
9
9
 
10
10
  from maps4fs.generator.component import Component
11
- from maps4fs.generator.dtm import DTMProvider, DTMProviderSettings
11
+ from maps4fs.generator.dtm.dtm import DTMProvider, DTMProviderSettings
12
12
  from maps4fs.generator.game import Game
13
13
  from maps4fs.generator.settings import (
14
14
  BackgroundSettings,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: maps4fs
3
- Version: 1.5.7
3
+ Version: 1.5.9
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
@@ -48,6 +48,7 @@ Requires-Dist: pydantic
48
48
  <a href="#Expert-settings">Expert settings</a> •
49
49
  <a href="#Resources">Resources</a> •
50
50
  <a href="#Bugs-and-feature-requests">Bugs and feature requests</a><br>
51
+ <a href="#DTM-Providers">DTM Providers</a> •
51
52
  <a href="#Special-thanks">Special thanks</a>
52
53
  </p>
53
54
 
@@ -69,6 +70,7 @@ Requires-Dist: pydantic
69
70
 
70
71
  🗺️ Supports 2x2, 4x4, 8x8, 16x16 and any custom size maps<br>
71
72
  🔄 Support map rotation 🆕<br>
73
+ 🌐 Supports custom [DTM Providers](#DTM-Providers) 🆕<br>
72
74
  🌾 Automatically generates fields 🆕<br>
73
75
  🌽 Automatically generates farmlands 🆕<br>
74
76
  🌿 Automatically generates decorative foliage 🆕<br>
@@ -547,6 +549,15 @@ To create a basic map, you only need the Giants Editor. But if you want to creat
547
549
  ➡️ Please, before creating an issue or asking some questions, check the [FAQ](docs/FAQ.md) section.<br>
548
550
  If you find a bug or have an idea for a new feature, please create an issue [here](https://github.com/iwatkot/maps4fs/issues) or contact me directly on [Telegram](https://t.me/iwatkot) or on Discord: `iwatkot`.
549
551
 
552
+ ## DTM Providers
553
+
554
+ The generator supports adding the own DTM providers, please refer to the [DTM Providers](docs/dtm_providers.md) section to learn how to add the custom DTM provider.
555
+
556
+ ### Supported DTM providers
557
+
558
+ - [SRTM 30m](https://dwtkns.com/srtm30m/) - the 30 meters resolution DEM data from the SRTM mission for the whole world.
559
+ - [USGS 1m](https://portal.opentopography.org/raster?opentopoID=OTNED.012021.4269.3) - the 1-meter resolution DEM data from the USGS for the USA. Developed by [ZenJakey](https://github.com/ZenJakey).
560
+
550
561
  ## Special thanks
551
562
 
552
563
  Of course, first of all, thanks to the direct [contributors](https://github.com/iwatkot/maps4fs/graphs/contributors) of the project.
@@ -1,24 +1,27 @@
1
- maps4fs/__init__.py,sha256=EJzbqRrSGltSMUI-dHgONODxKt9YvP_ElwFmXV8M_MA,380
1
+ maps4fs/__init__.py,sha256=WbT36EzJ_74GN0RUUrLIYECdSdtRiZaxKl17KUt7pjA,492
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=moTsEJM-hZgHQQiBjFVTWBKgPMqxup-58EErh4bq_dE,21342
5
5
  maps4fs/generator/component.py,sha256=RtXruvT4Fxfr7_xo9Bi-i3IIWcPd5QQOSpYJ_cNC49o,20408
6
6
  maps4fs/generator/config.py,sha256=0QmK052B8bxyHVhg3jzCORLfOBMMmqVfhhbqXKf6OMk,4383
7
- maps4fs/generator/dem.py,sha256=shyehXYXXog9ZTdi_8Y1WAnmhsL4-YAIZ3EpmGy8qeA,12300
8
- maps4fs/generator/dtm.py,sha256=5_1e-kQcZ7c1Xg3tvuTyumzfTAcUPmDkIyZd5VagyOk,10550
7
+ maps4fs/generator/dem.py,sha256=vGz-gUg_JArqHO7qewdnSR7WiF7ciUzY-OSqOluUDWw,12304
9
8
  maps4fs/generator/game.py,sha256=QHgVnyGYvEnfwGZ84-u-dpbCRr3UeVVqBbrwr5WG8dE,7992
10
9
  maps4fs/generator/grle.py,sha256=u8ZwSs313PIOkH_0B_O2tVTaZ-eYNkc30eKGtBxWzTM,17846
11
10
  maps4fs/generator/i3d.py,sha256=FLVlj0g90IXRuaRARD1HTnufsLpuaa5kHKdiME-LUZY,24329
12
- maps4fs/generator/map.py,sha256=flU0b2TrVYLxj9o3v_YRvNz9YB3s4w6YFSv4Jka5ojM,9283
11
+ maps4fs/generator/map.py,sha256=-iUFGqe11Df-oUrxhcnJzRZ0o6NZKRQ_blTq1h1Wezg,9287
13
12
  maps4fs/generator/qgis.py,sha256=Es8hLuqN_KH8lDfnJE6He2rWYbAKJ3RGPn-o87S6CPI,6116
14
13
  maps4fs/generator/satellite.py,sha256=Qnb6XxmXKnHdHKVMb9mJ3vDGtGkDHCOv_81hrrXdx3k,3660
15
14
  maps4fs/generator/settings.py,sha256=NWuK76ICr8gURQnzePat4JH9w-iACbQEKQebqu51gBE,4470
16
15
  maps4fs/generator/texture.py,sha256=sErusfv1AqQfP-veMrZ921Tz8DnGEhfB4ucggMmKrD4,31231
16
+ maps4fs/generator/dtm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
+ maps4fs/generator/dtm/dtm.py,sha256=THQ3RWVL9ut7A4omS8hEog-oQSSwYV0JcDMe0Iiw4fY,8009
18
+ maps4fs/generator/dtm/srtm.py,sha256=7uEb-Pde_uTG4D311mZ634QYkB5yvV8t2DfNfXuhYGY,3066
19
+ maps4fs/generator/dtm/usgs.py,sha256=U0kDog1UAa1lWiK4Pe3nnhXnnplOS4HZde1yqYqCiDw,13123
17
20
  maps4fs/toolbox/__init__.py,sha256=zZMLEkGzb4z0xql650gOtGSvcgX58DnJ2yN3vC2daRk,43
18
21
  maps4fs/toolbox/background.py,sha256=9BXWNqs_n3HgqDiPztWylgYk_QM4YgBpe6_ZNQAWtSc,2154
19
22
  maps4fs/toolbox/dem.py,sha256=z9IPFNmYbjiigb3t02ZenI3Mo8odd19c5MZbjDEovTo,3525
20
- maps4fs-1.5.7.dist-info/LICENSE.md,sha256=pTKD_oUexcn-yccFCTrMeLkZy0ifLRa-VNcDLqLZaIw,10749
21
- maps4fs-1.5.7.dist-info/METADATA,sha256=pIrqQEpHgaljNNzjl297anHnHS2jMruuNeBKyX9iGME,35585
22
- maps4fs-1.5.7.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
23
- maps4fs-1.5.7.dist-info/top_level.txt,sha256=Ue9DSRlejRQRCaJueB0uLcKrWwsEq9zezfv5dI5mV1M,8
24
- maps4fs-1.5.7.dist-info/RECORD,,
23
+ maps4fs-1.5.9.dist-info/LICENSE.md,sha256=pTKD_oUexcn-yccFCTrMeLkZy0ifLRa-VNcDLqLZaIw,10749
24
+ maps4fs-1.5.9.dist-info/METADATA,sha256=CEhXKo9Y6Nqmh7SL-_BV7XSHtD7eApd8-81Grfd-zp4,36231
25
+ maps4fs-1.5.9.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
26
+ maps4fs-1.5.9.dist-info/top_level.txt,sha256=Ue9DSRlejRQRCaJueB0uLcKrWwsEq9zezfv5dI5mV1M,8
27
+ maps4fs-1.5.9.dist-info/RECORD,,